views.py 91 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908
  1. import logging
  2. from copy import deepcopy
  3. from collections import OrderedDict
  4. from django.contrib import messages
  5. from django.contrib.contenttypes.models import ContentType
  6. from django.core.exceptions import ObjectDoesNotExist
  7. from django.core.paginator import EmptyPage, PageNotAnInteger
  8. from django.db import transaction
  9. from django.db.models import F, Prefetch
  10. from django.forms import ModelMultipleChoiceField, MultipleHiddenInput, modelformset_factory
  11. from django.shortcuts import get_object_or_404, redirect, render
  12. from django.utils.html import escape
  13. from django.utils.safestring import mark_safe
  14. from django.views.generic import View
  15. from circuits.models import Circuit
  16. from extras.views import ObjectChangeLogView, ObjectConfigContextView, ObjectJournalView
  17. from ipam.models import IPAddress, Prefix, Service, VLAN
  18. from ipam.tables import InterfaceIPAddressTable, InterfaceVLANTable
  19. from netbox.views import generic
  20. from utilities.forms import ConfirmationForm
  21. from utilities.paginator import EnhancedPaginator, get_paginate_count
  22. from utilities.permissions import get_permission_for_model
  23. from utilities.tables import paginate_table
  24. from utilities.utils import csv_format, count_related
  25. from utilities.views import GetReturnURLMixin, ObjectPermissionRequiredMixin
  26. from virtualization.models import VirtualMachine
  27. from . import filtersets, forms, tables
  28. from .choices import DeviceFaceChoices
  29. from .constants import NONCONNECTABLE_IFACE_TYPES
  30. from .models import (
  31. Cable, CablePath, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
  32. DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
  33. InventoryItem, Manufacturer, PathEndpoint, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel,
  34. PowerPort, PowerPortTemplate, Rack, Location, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site,
  35. SiteGroup, VirtualChassis,
  36. )
  37. class BulkDisconnectView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
  38. """
  39. An extendable view for disconnection console/power/interface components in bulk.
  40. """
  41. queryset = None
  42. template_name = 'dcim/bulk_disconnect.html'
  43. def __init__(self, *args, **kwargs):
  44. super().__init__(*args, **kwargs)
  45. # Create a new Form class from ConfirmationForm
  46. class _Form(ConfirmationForm):
  47. pk = ModelMultipleChoiceField(
  48. queryset=self.queryset,
  49. widget=MultipleHiddenInput()
  50. )
  51. self.form = _Form
  52. def get_required_permission(self):
  53. return get_permission_for_model(self.queryset.model, 'change')
  54. def post(self, request):
  55. selected_objects = []
  56. return_url = self.get_return_url(request)
  57. if '_confirm' in request.POST:
  58. form = self.form(request.POST)
  59. if form.is_valid():
  60. with transaction.atomic():
  61. count = 0
  62. for obj in self.queryset.filter(pk__in=form.cleaned_data['pk']):
  63. if obj.cable is None:
  64. continue
  65. obj.cable.delete()
  66. count += 1
  67. messages.success(request, "Disconnected {} {}".format(
  68. count, self.queryset.model._meta.verbose_name_plural
  69. ))
  70. return redirect(return_url)
  71. else:
  72. form = self.form(initial={'pk': request.POST.getlist('pk')})
  73. selected_objects = self.queryset.filter(pk__in=form.initial['pk'])
  74. return render(request, self.template_name, {
  75. 'form': form,
  76. 'obj_type_plural': self.queryset.model._meta.verbose_name_plural,
  77. 'selected_objects': selected_objects,
  78. 'return_url': return_url,
  79. })
  80. #
  81. # Regions
  82. #
  83. class RegionListView(generic.ObjectListView):
  84. queryset = Region.objects.add_related_count(
  85. Region.objects.all(),
  86. Site,
  87. 'region',
  88. 'site_count',
  89. cumulative=True
  90. )
  91. filterset = filtersets.RegionFilterSet
  92. filterset_form = forms.RegionFilterForm
  93. table = tables.RegionTable
  94. class RegionView(generic.ObjectView):
  95. queryset = Region.objects.all()
  96. def get_extra_context(self, request, instance):
  97. child_regions = Region.objects.add_related_count(
  98. Region.objects.all(),
  99. Site,
  100. 'region',
  101. 'site_count',
  102. cumulative=True
  103. ).restrict(request.user, 'view').filter(
  104. parent__in=instance.get_descendants(include_self=True)
  105. )
  106. child_regions_table = tables.RegionTable(child_regions)
  107. sites = Site.objects.restrict(request.user, 'view').filter(
  108. region=instance
  109. )
  110. sites_table = tables.SiteTable(sites)
  111. sites_table.columns.hide('region')
  112. paginate_table(sites_table, request)
  113. return {
  114. 'child_regions_table': child_regions_table,
  115. 'sites_table': sites_table,
  116. }
  117. class RegionEditView(generic.ObjectEditView):
  118. queryset = Region.objects.all()
  119. model_form = forms.RegionForm
  120. class RegionDeleteView(generic.ObjectDeleteView):
  121. queryset = Region.objects.all()
  122. class RegionBulkImportView(generic.BulkImportView):
  123. queryset = Region.objects.all()
  124. model_form = forms.RegionCSVForm
  125. table = tables.RegionTable
  126. class RegionBulkEditView(generic.BulkEditView):
  127. queryset = Region.objects.add_related_count(
  128. Region.objects.all(),
  129. Site,
  130. 'region',
  131. 'site_count',
  132. cumulative=True
  133. )
  134. filterset = filtersets.RegionFilterSet
  135. table = tables.RegionTable
  136. form = forms.RegionBulkEditForm
  137. class RegionBulkDeleteView(generic.BulkDeleteView):
  138. queryset = Region.objects.add_related_count(
  139. Region.objects.all(),
  140. Site,
  141. 'region',
  142. 'site_count',
  143. cumulative=True
  144. )
  145. filterset = filtersets.RegionFilterSet
  146. table = tables.RegionTable
  147. #
  148. # Site groups
  149. #
  150. class SiteGroupListView(generic.ObjectListView):
  151. queryset = SiteGroup.objects.add_related_count(
  152. SiteGroup.objects.all(),
  153. Site,
  154. 'group',
  155. 'site_count',
  156. cumulative=True
  157. )
  158. filterset = filtersets.SiteGroupFilterSet
  159. filterset_form = forms.SiteGroupFilterForm
  160. table = tables.SiteGroupTable
  161. class SiteGroupView(generic.ObjectView):
  162. queryset = SiteGroup.objects.all()
  163. def get_extra_context(self, request, instance):
  164. child_groups = SiteGroup.objects.add_related_count(
  165. SiteGroup.objects.all(),
  166. Site,
  167. 'group',
  168. 'site_count',
  169. cumulative=True
  170. ).restrict(request.user, 'view').filter(
  171. parent__in=instance.get_descendants(include_self=True)
  172. )
  173. child_groups_table = tables.SiteGroupTable(child_groups)
  174. sites = Site.objects.restrict(request.user, 'view').filter(
  175. group=instance
  176. )
  177. sites_table = tables.SiteTable(sites)
  178. sites_table.columns.hide('group')
  179. paginate_table(sites_table, request)
  180. return {
  181. 'child_groups_table': child_groups_table,
  182. 'sites_table': sites_table,
  183. }
  184. class SiteGroupEditView(generic.ObjectEditView):
  185. queryset = SiteGroup.objects.all()
  186. model_form = forms.SiteGroupForm
  187. class SiteGroupDeleteView(generic.ObjectDeleteView):
  188. queryset = SiteGroup.objects.all()
  189. class SiteGroupBulkImportView(generic.BulkImportView):
  190. queryset = SiteGroup.objects.all()
  191. model_form = forms.SiteGroupCSVForm
  192. table = tables.SiteGroupTable
  193. class SiteGroupBulkEditView(generic.BulkEditView):
  194. queryset = SiteGroup.objects.add_related_count(
  195. SiteGroup.objects.all(),
  196. Site,
  197. 'group',
  198. 'site_count',
  199. cumulative=True
  200. )
  201. filterset = filtersets.SiteGroupFilterSet
  202. table = tables.SiteGroupTable
  203. form = forms.SiteGroupBulkEditForm
  204. class SiteGroupBulkDeleteView(generic.BulkDeleteView):
  205. queryset = SiteGroup.objects.add_related_count(
  206. SiteGroup.objects.all(),
  207. Site,
  208. 'group',
  209. 'site_count',
  210. cumulative=True
  211. )
  212. filterset = filtersets.SiteGroupFilterSet
  213. table = tables.SiteGroupTable
  214. #
  215. # Sites
  216. #
  217. class SiteListView(generic.ObjectListView):
  218. queryset = Site.objects.all()
  219. filterset = filtersets.SiteFilterSet
  220. filterset_form = forms.SiteFilterForm
  221. table = tables.SiteTable
  222. class SiteView(generic.ObjectView):
  223. queryset = Site.objects.prefetch_related('region', 'tenant__group')
  224. def get_extra_context(self, request, instance):
  225. stats = {
  226. 'rack_count': Rack.objects.restrict(request.user, 'view').filter(site=instance).count(),
  227. 'device_count': Device.objects.restrict(request.user, 'view').filter(site=instance).count(),
  228. 'prefix_count': Prefix.objects.restrict(request.user, 'view').filter(site=instance).count(),
  229. 'vlan_count': VLAN.objects.restrict(request.user, 'view').filter(site=instance).count(),
  230. 'circuit_count': Circuit.objects.restrict(request.user, 'view').filter(terminations__site=instance).count(),
  231. 'vm_count': VirtualMachine.objects.restrict(request.user, 'view').filter(cluster__site=instance).count(),
  232. }
  233. locations = Location.objects.add_related_count(
  234. Location.objects.all(),
  235. Rack,
  236. 'location',
  237. 'rack_count',
  238. cumulative=True
  239. )
  240. locations = Location.objects.add_related_count(
  241. locations,
  242. Device,
  243. 'location',
  244. 'device_count',
  245. cumulative=True
  246. ).restrict(request.user, 'view').filter(site=instance)
  247. return {
  248. 'stats': stats,
  249. 'locations': locations,
  250. }
  251. class SiteEditView(generic.ObjectEditView):
  252. queryset = Site.objects.all()
  253. model_form = forms.SiteForm
  254. class SiteDeleteView(generic.ObjectDeleteView):
  255. queryset = Site.objects.all()
  256. class SiteBulkImportView(generic.BulkImportView):
  257. queryset = Site.objects.all()
  258. model_form = forms.SiteCSVForm
  259. table = tables.SiteTable
  260. class SiteBulkEditView(generic.BulkEditView):
  261. queryset = Site.objects.prefetch_related('region', 'tenant')
  262. filterset = filtersets.SiteFilterSet
  263. table = tables.SiteTable
  264. form = forms.SiteBulkEditForm
  265. class SiteBulkDeleteView(generic.BulkDeleteView):
  266. queryset = Site.objects.prefetch_related('region', 'tenant')
  267. filterset = filtersets.SiteFilterSet
  268. table = tables.SiteTable
  269. #
  270. # Locations
  271. #
  272. class LocationListView(generic.ObjectListView):
  273. queryset = Location.objects.add_related_count(
  274. Location.objects.add_related_count(
  275. Location.objects.all(),
  276. Device,
  277. 'location',
  278. 'device_count',
  279. cumulative=True
  280. ),
  281. Rack,
  282. 'location',
  283. 'rack_count',
  284. cumulative=True
  285. )
  286. filterset = filtersets.LocationFilterSet
  287. filterset_form = forms.LocationFilterForm
  288. table = tables.LocationTable
  289. class LocationView(generic.ObjectView):
  290. queryset = Location.objects.all()
  291. def get_extra_context(self, request, instance):
  292. location_ids = instance.get_descendants(include_self=True).values_list('pk', flat=True)
  293. rack_count = Rack.objects.filter(location__in=location_ids).count()
  294. device_count = Device.objects.filter(location__in=location_ids).count()
  295. child_locations = Location.objects.add_related_count(
  296. Location.objects.add_related_count(
  297. Location.objects.all(),
  298. Device,
  299. 'location',
  300. 'device_count',
  301. cumulative=True
  302. ),
  303. Rack,
  304. 'location',
  305. 'rack_count',
  306. cumulative=True
  307. ).filter(pk__in=location_ids).exclude(pk=instance.pk)
  308. child_locations_table = tables.LocationTable(child_locations)
  309. paginate_table(child_locations_table, request)
  310. return {
  311. 'rack_count': rack_count,
  312. 'device_count': device_count,
  313. 'child_locations_table': child_locations_table,
  314. }
  315. class LocationEditView(generic.ObjectEditView):
  316. queryset = Location.objects.all()
  317. model_form = forms.LocationForm
  318. class LocationDeleteView(generic.ObjectDeleteView):
  319. queryset = Location.objects.all()
  320. class LocationBulkImportView(generic.BulkImportView):
  321. queryset = Location.objects.all()
  322. model_form = forms.LocationCSVForm
  323. table = tables.LocationTable
  324. class LocationBulkEditView(generic.BulkEditView):
  325. queryset = Location.objects.add_related_count(
  326. Location.objects.all(),
  327. Rack,
  328. 'location',
  329. 'rack_count',
  330. cumulative=True
  331. ).prefetch_related('site')
  332. filterset = filtersets.LocationFilterSet
  333. table = tables.LocationTable
  334. form = forms.LocationBulkEditForm
  335. class LocationBulkDeleteView(generic.BulkDeleteView):
  336. queryset = Location.objects.add_related_count(
  337. Location.objects.all(),
  338. Rack,
  339. 'location',
  340. 'rack_count',
  341. cumulative=True
  342. ).prefetch_related('site')
  343. filterset = filtersets.LocationFilterSet
  344. table = tables.LocationTable
  345. #
  346. # Rack roles
  347. #
  348. class RackRoleListView(generic.ObjectListView):
  349. queryset = RackRole.objects.annotate(
  350. rack_count=count_related(Rack, 'role')
  351. )
  352. table = tables.RackRoleTable
  353. class RackRoleView(generic.ObjectView):
  354. queryset = RackRole.objects.all()
  355. def get_extra_context(self, request, instance):
  356. racks = Rack.objects.restrict(request.user, 'view').filter(
  357. role=instance
  358. )
  359. racks_table = tables.RackTable(racks)
  360. racks_table.columns.hide('role')
  361. paginate_table(racks_table, request)
  362. return {
  363. 'racks_table': racks_table,
  364. }
  365. class RackRoleEditView(generic.ObjectEditView):
  366. queryset = RackRole.objects.all()
  367. model_form = forms.RackRoleForm
  368. class RackRoleDeleteView(generic.ObjectDeleteView):
  369. queryset = RackRole.objects.all()
  370. class RackRoleBulkImportView(generic.BulkImportView):
  371. queryset = RackRole.objects.all()
  372. model_form = forms.RackRoleCSVForm
  373. table = tables.RackRoleTable
  374. class RackRoleBulkEditView(generic.BulkEditView):
  375. queryset = RackRole.objects.annotate(
  376. rack_count=count_related(Rack, 'role')
  377. )
  378. filterset = filtersets.RackRoleFilterSet
  379. table = tables.RackRoleTable
  380. form = forms.RackRoleBulkEditForm
  381. class RackRoleBulkDeleteView(generic.BulkDeleteView):
  382. queryset = RackRole.objects.annotate(
  383. rack_count=count_related(Rack, 'role')
  384. )
  385. table = tables.RackRoleTable
  386. #
  387. # Racks
  388. #
  389. class RackListView(generic.ObjectListView):
  390. queryset = Rack.objects.prefetch_related(
  391. 'site', 'location', 'tenant', 'role', 'devices__device_type'
  392. ).annotate(
  393. device_count=count_related(Device, 'rack')
  394. )
  395. filterset = filtersets.RackFilterSet
  396. filterset_form = forms.RackFilterForm
  397. table = tables.RackDetailTable
  398. class RackElevationListView(generic.ObjectListView):
  399. """
  400. Display a set of rack elevations side-by-side.
  401. """
  402. queryset = Rack.objects.prefetch_related('role')
  403. def get(self, request):
  404. racks = filtersets.RackFilterSet(request.GET, self.queryset).qs
  405. total_count = racks.count()
  406. # Determine ordering
  407. reverse = bool(request.GET.get('reverse', False))
  408. if reverse:
  409. racks = racks.reverse()
  410. # Pagination
  411. per_page = get_paginate_count(request)
  412. page_number = request.GET.get('page', 1)
  413. paginator = EnhancedPaginator(racks, per_page)
  414. try:
  415. page = paginator.page(page_number)
  416. except PageNotAnInteger:
  417. page = paginator.page(1)
  418. except EmptyPage:
  419. page = paginator.page(paginator.num_pages)
  420. # Determine rack face
  421. rack_face = request.GET.get('face', DeviceFaceChoices.FACE_FRONT)
  422. if rack_face not in DeviceFaceChoices.values():
  423. rack_face = DeviceFaceChoices.FACE_FRONT
  424. return render(request, 'dcim/rack_elevation_list.html', {
  425. 'paginator': paginator,
  426. 'page': page,
  427. 'total_count': total_count,
  428. 'reverse': reverse,
  429. 'rack_face': rack_face,
  430. 'filter_form': forms.RackElevationFilterForm(request.GET),
  431. })
  432. class RackView(generic.ObjectView):
  433. queryset = Rack.objects.prefetch_related('site__region', 'tenant__group', 'location', 'role')
  434. def get_extra_context(self, request, instance):
  435. # Get 0U devices located within the rack
  436. nonracked_devices = Device.objects.filter(
  437. rack=instance,
  438. position__isnull=True,
  439. parent_bay__isnull=True
  440. ).prefetch_related('device_type__manufacturer')
  441. peer_racks = Rack.objects.restrict(request.user, 'view').filter(site=instance.site)
  442. if instance.location:
  443. peer_racks = peer_racks.filter(location=instance.location)
  444. else:
  445. peer_racks = peer_racks.filter(location__isnull=True)
  446. next_rack = peer_racks.filter(name__gt=instance.name).order_by('name').first()
  447. prev_rack = peer_racks.filter(name__lt=instance.name).order_by('-name').first()
  448. reservations = RackReservation.objects.restrict(request.user, 'view').filter(rack=instance)
  449. power_feeds = PowerFeed.objects.restrict(request.user, 'view').filter(rack=instance).prefetch_related(
  450. 'power_panel'
  451. )
  452. device_count = Device.objects.restrict(request.user, 'view').filter(rack=instance).count()
  453. return {
  454. 'device_count': device_count,
  455. 'reservations': reservations,
  456. 'power_feeds': power_feeds,
  457. 'nonracked_devices': nonracked_devices,
  458. 'next_rack': next_rack,
  459. 'prev_rack': prev_rack,
  460. }
  461. class RackEditView(generic.ObjectEditView):
  462. queryset = Rack.objects.all()
  463. model_form = forms.RackForm
  464. template_name = 'dcim/rack_edit.html'
  465. class RackDeleteView(generic.ObjectDeleteView):
  466. queryset = Rack.objects.all()
  467. class RackBulkImportView(generic.BulkImportView):
  468. queryset = Rack.objects.all()
  469. model_form = forms.RackCSVForm
  470. table = tables.RackTable
  471. class RackBulkEditView(generic.BulkEditView):
  472. queryset = Rack.objects.prefetch_related('site', 'location', 'tenant', 'role')
  473. filterset = filtersets.RackFilterSet
  474. table = tables.RackTable
  475. form = forms.RackBulkEditForm
  476. class RackBulkDeleteView(generic.BulkDeleteView):
  477. queryset = Rack.objects.prefetch_related('site', 'location', 'tenant', 'role')
  478. filterset = filtersets.RackFilterSet
  479. table = tables.RackTable
  480. #
  481. # Rack reservations
  482. #
  483. class RackReservationListView(generic.ObjectListView):
  484. queryset = RackReservation.objects.all()
  485. filterset = filtersets.RackReservationFilterSet
  486. filterset_form = forms.RackReservationFilterForm
  487. table = tables.RackReservationTable
  488. class RackReservationView(generic.ObjectView):
  489. queryset = RackReservation.objects.prefetch_related('rack')
  490. class RackReservationEditView(generic.ObjectEditView):
  491. queryset = RackReservation.objects.all()
  492. model_form = forms.RackReservationForm
  493. def alter_obj(self, obj, request, args, kwargs):
  494. if not obj.pk:
  495. if 'rack' in request.GET:
  496. obj.rack = get_object_or_404(Rack, pk=request.GET.get('rack'))
  497. obj.user = request.user
  498. return obj
  499. class RackReservationDeleteView(generic.ObjectDeleteView):
  500. queryset = RackReservation.objects.all()
  501. class RackReservationImportView(generic.BulkImportView):
  502. queryset = RackReservation.objects.all()
  503. model_form = forms.RackReservationCSVForm
  504. table = tables.RackReservationTable
  505. def _save_obj(self, obj_form, request):
  506. """
  507. Assign the currently authenticated user to the RackReservation.
  508. """
  509. instance = obj_form.save(commit=False)
  510. instance.user = request.user
  511. instance.save()
  512. return instance
  513. class RackReservationBulkEditView(generic.BulkEditView):
  514. queryset = RackReservation.objects.prefetch_related('rack', 'user')
  515. filterset = filtersets.RackReservationFilterSet
  516. table = tables.RackReservationTable
  517. form = forms.RackReservationBulkEditForm
  518. class RackReservationBulkDeleteView(generic.BulkDeleteView):
  519. queryset = RackReservation.objects.prefetch_related('rack', 'user')
  520. filterset = filtersets.RackReservationFilterSet
  521. table = tables.RackReservationTable
  522. #
  523. # Manufacturers
  524. #
  525. class ManufacturerListView(generic.ObjectListView):
  526. queryset = Manufacturer.objects.annotate(
  527. devicetype_count=count_related(DeviceType, 'manufacturer'),
  528. inventoryitem_count=count_related(InventoryItem, 'manufacturer'),
  529. platform_count=count_related(Platform, 'manufacturer')
  530. )
  531. table = tables.ManufacturerTable
  532. class ManufacturerView(generic.ObjectView):
  533. queryset = Manufacturer.objects.all()
  534. def get_extra_context(self, request, instance):
  535. devicetypes = DeviceType.objects.restrict(request.user, 'view').filter(
  536. manufacturer=instance
  537. ).annotate(
  538. instance_count=count_related(Device, 'device_type')
  539. )
  540. devicetypes_table = tables.DeviceTypeTable(devicetypes)
  541. devicetypes_table.columns.hide('manufacturer')
  542. paginate_table(devicetypes_table, request)
  543. return {
  544. 'devicetypes_table': devicetypes_table,
  545. }
  546. class ManufacturerEditView(generic.ObjectEditView):
  547. queryset = Manufacturer.objects.all()
  548. model_form = forms.ManufacturerForm
  549. class ManufacturerDeleteView(generic.ObjectDeleteView):
  550. queryset = Manufacturer.objects.all()
  551. class ManufacturerBulkImportView(generic.BulkImportView):
  552. queryset = Manufacturer.objects.all()
  553. model_form = forms.ManufacturerCSVForm
  554. table = tables.ManufacturerTable
  555. class ManufacturerBulkEditView(generic.BulkEditView):
  556. queryset = Manufacturer.objects.annotate(
  557. devicetype_count=count_related(DeviceType, 'manufacturer')
  558. )
  559. filterset = filtersets.ManufacturerFilterSet
  560. table = tables.ManufacturerTable
  561. form = forms.ManufacturerBulkEditForm
  562. class ManufacturerBulkDeleteView(generic.BulkDeleteView):
  563. queryset = Manufacturer.objects.annotate(
  564. devicetype_count=count_related(DeviceType, 'manufacturer')
  565. )
  566. table = tables.ManufacturerTable
  567. #
  568. # Device types
  569. #
  570. class DeviceTypeListView(generic.ObjectListView):
  571. queryset = DeviceType.objects.prefetch_related('manufacturer').annotate(
  572. instance_count=count_related(Device, 'device_type')
  573. )
  574. filterset = filtersets.DeviceTypeFilterSet
  575. filterset_form = forms.DeviceTypeFilterForm
  576. table = tables.DeviceTypeTable
  577. class DeviceTypeView(generic.ObjectView):
  578. queryset = DeviceType.objects.prefetch_related('manufacturer')
  579. def get_extra_context(self, request, instance):
  580. instance_count = Device.objects.restrict(request.user).filter(device_type=instance).count()
  581. # Component tables
  582. consoleport_table = tables.ConsolePortTemplateTable(
  583. ConsolePortTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
  584. orderable=False
  585. )
  586. consoleserverport_table = tables.ConsoleServerPortTemplateTable(
  587. ConsoleServerPortTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
  588. orderable=False
  589. )
  590. powerport_table = tables.PowerPortTemplateTable(
  591. PowerPortTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
  592. orderable=False
  593. )
  594. poweroutlet_table = tables.PowerOutletTemplateTable(
  595. PowerOutletTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
  596. orderable=False
  597. )
  598. interface_table = tables.InterfaceTemplateTable(
  599. list(InterfaceTemplate.objects.restrict(request.user, 'view').filter(device_type=instance)),
  600. orderable=False
  601. )
  602. front_port_table = tables.FrontPortTemplateTable(
  603. FrontPortTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
  604. orderable=False
  605. )
  606. rear_port_table = tables.RearPortTemplateTable(
  607. RearPortTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
  608. orderable=False
  609. )
  610. devicebay_table = tables.DeviceBayTemplateTable(
  611. DeviceBayTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
  612. orderable=False
  613. )
  614. if request.user.has_perm('dcim.change_devicetype'):
  615. consoleport_table.columns.show('pk')
  616. consoleserverport_table.columns.show('pk')
  617. powerport_table.columns.show('pk')
  618. poweroutlet_table.columns.show('pk')
  619. interface_table.columns.show('pk')
  620. front_port_table.columns.show('pk')
  621. rear_port_table.columns.show('pk')
  622. devicebay_table.columns.show('pk')
  623. return {
  624. 'instance_count': instance_count,
  625. 'consoleport_table': consoleport_table,
  626. 'consoleserverport_table': consoleserverport_table,
  627. 'powerport_table': powerport_table,
  628. 'poweroutlet_table': poweroutlet_table,
  629. 'interface_table': interface_table,
  630. 'front_port_table': front_port_table,
  631. 'rear_port_table': rear_port_table,
  632. 'devicebay_table': devicebay_table,
  633. }
  634. class DeviceTypeEditView(generic.ObjectEditView):
  635. queryset = DeviceType.objects.all()
  636. model_form = forms.DeviceTypeForm
  637. class DeviceTypeDeleteView(generic.ObjectDeleteView):
  638. queryset = DeviceType.objects.all()
  639. class DeviceTypeImportView(generic.ObjectImportView):
  640. additional_permissions = [
  641. 'dcim.add_devicetype',
  642. 'dcim.add_consoleporttemplate',
  643. 'dcim.add_consoleserverporttemplate',
  644. 'dcim.add_powerporttemplate',
  645. 'dcim.add_poweroutlettemplate',
  646. 'dcim.add_interfacetemplate',
  647. 'dcim.add_frontporttemplate',
  648. 'dcim.add_rearporttemplate',
  649. 'dcim.add_devicebaytemplate',
  650. ]
  651. queryset = DeviceType.objects.all()
  652. model_form = forms.DeviceTypeImportForm
  653. related_object_forms = OrderedDict((
  654. ('console-ports', forms.ConsolePortTemplateImportForm),
  655. ('console-server-ports', forms.ConsoleServerPortTemplateImportForm),
  656. ('power-ports', forms.PowerPortTemplateImportForm),
  657. ('power-outlets', forms.PowerOutletTemplateImportForm),
  658. ('interfaces', forms.InterfaceTemplateImportForm),
  659. ('rear-ports', forms.RearPortTemplateImportForm),
  660. ('front-ports', forms.FrontPortTemplateImportForm),
  661. ('device-bays', forms.DeviceBayTemplateImportForm),
  662. ))
  663. class DeviceTypeBulkEditView(generic.BulkEditView):
  664. queryset = DeviceType.objects.prefetch_related('manufacturer').annotate(
  665. instance_count=count_related(Device, 'device_type')
  666. )
  667. filterset = filtersets.DeviceTypeFilterSet
  668. table = tables.DeviceTypeTable
  669. form = forms.DeviceTypeBulkEditForm
  670. class DeviceTypeBulkDeleteView(generic.BulkDeleteView):
  671. queryset = DeviceType.objects.prefetch_related('manufacturer').annotate(
  672. instance_count=count_related(Device, 'device_type')
  673. )
  674. filterset = filtersets.DeviceTypeFilterSet
  675. table = tables.DeviceTypeTable
  676. #
  677. # Console port templates
  678. #
  679. class ConsolePortTemplateCreateView(generic.ComponentCreateView):
  680. queryset = ConsolePortTemplate.objects.all()
  681. form = forms.ConsolePortTemplateCreateForm
  682. model_form = forms.ConsolePortTemplateForm
  683. template_name = 'dcim/device_component_add.html'
  684. class ConsolePortTemplateEditView(generic.ObjectEditView):
  685. queryset = ConsolePortTemplate.objects.all()
  686. model_form = forms.ConsolePortTemplateForm
  687. class ConsolePortTemplateDeleteView(generic.ObjectDeleteView):
  688. queryset = ConsolePortTemplate.objects.all()
  689. class ConsolePortTemplateBulkEditView(generic.BulkEditView):
  690. queryset = ConsolePortTemplate.objects.all()
  691. table = tables.ConsolePortTemplateTable
  692. form = forms.ConsolePortTemplateBulkEditForm
  693. class ConsolePortTemplateBulkRenameView(generic.BulkRenameView):
  694. queryset = ConsolePortTemplate.objects.all()
  695. class ConsolePortTemplateBulkDeleteView(generic.BulkDeleteView):
  696. queryset = ConsolePortTemplate.objects.all()
  697. table = tables.ConsolePortTemplateTable
  698. #
  699. # Console server port templates
  700. #
  701. class ConsoleServerPortTemplateCreateView(generic.ComponentCreateView):
  702. queryset = ConsoleServerPortTemplate.objects.all()
  703. form = forms.ConsoleServerPortTemplateCreateForm
  704. model_form = forms.ConsoleServerPortTemplateForm
  705. template_name = 'dcim/device_component_add.html'
  706. class ConsoleServerPortTemplateEditView(generic.ObjectEditView):
  707. queryset = ConsoleServerPortTemplate.objects.all()
  708. model_form = forms.ConsoleServerPortTemplateForm
  709. class ConsoleServerPortTemplateDeleteView(generic.ObjectDeleteView):
  710. queryset = ConsoleServerPortTemplate.objects.all()
  711. class ConsoleServerPortTemplateBulkEditView(generic.BulkEditView):
  712. queryset = ConsoleServerPortTemplate.objects.all()
  713. table = tables.ConsoleServerPortTemplateTable
  714. form = forms.ConsoleServerPortTemplateBulkEditForm
  715. class ConsoleServerPortTemplateBulkRenameView(generic.BulkRenameView):
  716. queryset = ConsoleServerPortTemplate.objects.all()
  717. class ConsoleServerPortTemplateBulkDeleteView(generic.BulkDeleteView):
  718. queryset = ConsoleServerPortTemplate.objects.all()
  719. table = tables.ConsoleServerPortTemplateTable
  720. #
  721. # Power port templates
  722. #
  723. class PowerPortTemplateCreateView(generic.ComponentCreateView):
  724. queryset = PowerPortTemplate.objects.all()
  725. form = forms.PowerPortTemplateCreateForm
  726. model_form = forms.PowerPortTemplateForm
  727. template_name = 'dcim/device_component_add.html'
  728. class PowerPortTemplateEditView(generic.ObjectEditView):
  729. queryset = PowerPortTemplate.objects.all()
  730. model_form = forms.PowerPortTemplateForm
  731. class PowerPortTemplateDeleteView(generic.ObjectDeleteView):
  732. queryset = PowerPortTemplate.objects.all()
  733. class PowerPortTemplateBulkEditView(generic.BulkEditView):
  734. queryset = PowerPortTemplate.objects.all()
  735. table = tables.PowerPortTemplateTable
  736. form = forms.PowerPortTemplateBulkEditForm
  737. class PowerPortTemplateBulkRenameView(generic.BulkRenameView):
  738. queryset = PowerPortTemplate.objects.all()
  739. class PowerPortTemplateBulkDeleteView(generic.BulkDeleteView):
  740. queryset = PowerPortTemplate.objects.all()
  741. table = tables.PowerPortTemplateTable
  742. #
  743. # Power outlet templates
  744. #
  745. class PowerOutletTemplateCreateView(generic.ComponentCreateView):
  746. queryset = PowerOutletTemplate.objects.all()
  747. form = forms.PowerOutletTemplateCreateForm
  748. model_form = forms.PowerOutletTemplateForm
  749. template_name = 'dcim/device_component_add.html'
  750. class PowerOutletTemplateEditView(generic.ObjectEditView):
  751. queryset = PowerOutletTemplate.objects.all()
  752. model_form = forms.PowerOutletTemplateForm
  753. class PowerOutletTemplateDeleteView(generic.ObjectDeleteView):
  754. queryset = PowerOutletTemplate.objects.all()
  755. class PowerOutletTemplateBulkEditView(generic.BulkEditView):
  756. queryset = PowerOutletTemplate.objects.all()
  757. table = tables.PowerOutletTemplateTable
  758. form = forms.PowerOutletTemplateBulkEditForm
  759. class PowerOutletTemplateBulkRenameView(generic.BulkRenameView):
  760. queryset = PowerOutletTemplate.objects.all()
  761. class PowerOutletTemplateBulkDeleteView(generic.BulkDeleteView):
  762. queryset = PowerOutletTemplate.objects.all()
  763. table = tables.PowerOutletTemplateTable
  764. #
  765. # Interface templates
  766. #
  767. class InterfaceTemplateCreateView(generic.ComponentCreateView):
  768. queryset = InterfaceTemplate.objects.all()
  769. form = forms.InterfaceTemplateCreateForm
  770. model_form = forms.InterfaceTemplateForm
  771. template_name = 'dcim/device_component_add.html'
  772. class InterfaceTemplateEditView(generic.ObjectEditView):
  773. queryset = InterfaceTemplate.objects.all()
  774. model_form = forms.InterfaceTemplateForm
  775. class InterfaceTemplateDeleteView(generic.ObjectDeleteView):
  776. queryset = InterfaceTemplate.objects.all()
  777. class InterfaceTemplateBulkEditView(generic.BulkEditView):
  778. queryset = InterfaceTemplate.objects.all()
  779. table = tables.InterfaceTemplateTable
  780. form = forms.InterfaceTemplateBulkEditForm
  781. class InterfaceTemplateBulkRenameView(generic.BulkRenameView):
  782. queryset = InterfaceTemplate.objects.all()
  783. class InterfaceTemplateBulkDeleteView(generic.BulkDeleteView):
  784. queryset = InterfaceTemplate.objects.all()
  785. table = tables.InterfaceTemplateTable
  786. #
  787. # Front port templates
  788. #
  789. class FrontPortTemplateCreateView(generic.ComponentCreateView):
  790. queryset = FrontPortTemplate.objects.all()
  791. form = forms.FrontPortTemplateCreateForm
  792. model_form = forms.FrontPortTemplateForm
  793. template_name = 'dcim/device_component_add.html'
  794. class FrontPortTemplateEditView(generic.ObjectEditView):
  795. queryset = FrontPortTemplate.objects.all()
  796. model_form = forms.FrontPortTemplateForm
  797. class FrontPortTemplateDeleteView(generic.ObjectDeleteView):
  798. queryset = FrontPortTemplate.objects.all()
  799. class FrontPortTemplateBulkEditView(generic.BulkEditView):
  800. queryset = FrontPortTemplate.objects.all()
  801. table = tables.FrontPortTemplateTable
  802. form = forms.FrontPortTemplateBulkEditForm
  803. class FrontPortTemplateBulkRenameView(generic.BulkRenameView):
  804. queryset = FrontPortTemplate.objects.all()
  805. class FrontPortTemplateBulkDeleteView(generic.BulkDeleteView):
  806. queryset = FrontPortTemplate.objects.all()
  807. table = tables.FrontPortTemplateTable
  808. #
  809. # Rear port templates
  810. #
  811. class RearPortTemplateCreateView(generic.ComponentCreateView):
  812. queryset = RearPortTemplate.objects.all()
  813. form = forms.RearPortTemplateCreateForm
  814. model_form = forms.RearPortTemplateForm
  815. template_name = 'dcim/device_component_add.html'
  816. class RearPortTemplateEditView(generic.ObjectEditView):
  817. queryset = RearPortTemplate.objects.all()
  818. model_form = forms.RearPortTemplateForm
  819. class RearPortTemplateDeleteView(generic.ObjectDeleteView):
  820. queryset = RearPortTemplate.objects.all()
  821. class RearPortTemplateBulkEditView(generic.BulkEditView):
  822. queryset = RearPortTemplate.objects.all()
  823. table = tables.RearPortTemplateTable
  824. form = forms.RearPortTemplateBulkEditForm
  825. class RearPortTemplateBulkRenameView(generic.BulkRenameView):
  826. queryset = RearPortTemplate.objects.all()
  827. class RearPortTemplateBulkDeleteView(generic.BulkDeleteView):
  828. queryset = RearPortTemplate.objects.all()
  829. table = tables.RearPortTemplateTable
  830. #
  831. # Device bay templates
  832. #
  833. class DeviceBayTemplateCreateView(generic.ComponentCreateView):
  834. queryset = DeviceBayTemplate.objects.all()
  835. form = forms.DeviceBayTemplateCreateForm
  836. model_form = forms.DeviceBayTemplateForm
  837. template_name = 'dcim/device_component_add.html'
  838. class DeviceBayTemplateEditView(generic.ObjectEditView):
  839. queryset = DeviceBayTemplate.objects.all()
  840. model_form = forms.DeviceBayTemplateForm
  841. class DeviceBayTemplateDeleteView(generic.ObjectDeleteView):
  842. queryset = DeviceBayTemplate.objects.all()
  843. class DeviceBayTemplateBulkEditView(generic.BulkEditView):
  844. queryset = DeviceBayTemplate.objects.all()
  845. table = tables.DeviceBayTemplateTable
  846. form = forms.DeviceBayTemplateBulkEditForm
  847. class DeviceBayTemplateBulkRenameView(generic.BulkRenameView):
  848. queryset = DeviceBayTemplate.objects.all()
  849. class DeviceBayTemplateBulkDeleteView(generic.BulkDeleteView):
  850. queryset = DeviceBayTemplate.objects.all()
  851. table = tables.DeviceBayTemplateTable
  852. #
  853. # Device roles
  854. #
  855. class DeviceRoleListView(generic.ObjectListView):
  856. queryset = DeviceRole.objects.annotate(
  857. device_count=count_related(Device, 'device_role'),
  858. vm_count=count_related(VirtualMachine, 'role')
  859. )
  860. table = tables.DeviceRoleTable
  861. class DeviceRoleView(generic.ObjectView):
  862. queryset = DeviceRole.objects.all()
  863. def get_extra_context(self, request, instance):
  864. devices = Device.objects.restrict(request.user, 'view').filter(
  865. device_role=instance
  866. )
  867. devices_table = tables.DeviceTable(devices)
  868. devices_table.columns.hide('device_role')
  869. paginate_table(devices_table, request)
  870. return {
  871. 'devices_table': devices_table,
  872. 'device_count': Device.objects.filter(device_role=instance).count(),
  873. 'virtualmachine_count': VirtualMachine.objects.filter(role=instance).count(),
  874. }
  875. class DeviceRoleEditView(generic.ObjectEditView):
  876. queryset = DeviceRole.objects.all()
  877. model_form = forms.DeviceRoleForm
  878. class DeviceRoleDeleteView(generic.ObjectDeleteView):
  879. queryset = DeviceRole.objects.all()
  880. class DeviceRoleBulkImportView(generic.BulkImportView):
  881. queryset = DeviceRole.objects.all()
  882. model_form = forms.DeviceRoleCSVForm
  883. table = tables.DeviceRoleTable
  884. class DeviceRoleBulkEditView(generic.BulkEditView):
  885. queryset = DeviceRole.objects.annotate(
  886. device_count=count_related(Device, 'device_role'),
  887. vm_count=count_related(VirtualMachine, 'role')
  888. )
  889. filterset = filtersets.DeviceRoleFilterSet
  890. table = tables.DeviceRoleTable
  891. form = forms.DeviceRoleBulkEditForm
  892. class DeviceRoleBulkDeleteView(generic.BulkDeleteView):
  893. queryset = DeviceRole.objects.annotate(
  894. device_count=count_related(Device, 'device_role'),
  895. vm_count=count_related(VirtualMachine, 'role')
  896. )
  897. table = tables.DeviceRoleTable
  898. #
  899. # Platforms
  900. #
  901. class PlatformListView(generic.ObjectListView):
  902. queryset = Platform.objects.annotate(
  903. device_count=count_related(Device, 'platform'),
  904. vm_count=count_related(VirtualMachine, 'platform')
  905. )
  906. table = tables.PlatformTable
  907. class PlatformView(generic.ObjectView):
  908. queryset = Platform.objects.all()
  909. def get_extra_context(self, request, instance):
  910. devices = Device.objects.restrict(request.user, 'view').filter(
  911. platform=instance
  912. )
  913. devices_table = tables.DeviceTable(devices)
  914. devices_table.columns.hide('platform')
  915. paginate_table(devices_table, request)
  916. return {
  917. 'devices_table': devices_table,
  918. }
  919. class PlatformEditView(generic.ObjectEditView):
  920. queryset = Platform.objects.all()
  921. model_form = forms.PlatformForm
  922. class PlatformDeleteView(generic.ObjectDeleteView):
  923. queryset = Platform.objects.all()
  924. class PlatformBulkImportView(generic.BulkImportView):
  925. queryset = Platform.objects.all()
  926. model_form = forms.PlatformCSVForm
  927. table = tables.PlatformTable
  928. class PlatformBulkEditView(generic.BulkEditView):
  929. queryset = Platform.objects.all()
  930. filterset = filtersets.PlatformFilterSet
  931. table = tables.PlatformTable
  932. form = forms.PlatformBulkEditForm
  933. class PlatformBulkDeleteView(generic.BulkDeleteView):
  934. queryset = Platform.objects.all()
  935. table = tables.PlatformTable
  936. #
  937. # Devices
  938. #
  939. class DeviceListView(generic.ObjectListView):
  940. queryset = Device.objects.all()
  941. filterset = filtersets.DeviceFilterSet
  942. filterset_form = forms.DeviceFilterForm
  943. table = tables.DeviceTable
  944. template_name = 'dcim/device_list.html'
  945. class DeviceView(generic.ObjectView):
  946. queryset = Device.objects.prefetch_related(
  947. 'site__region', 'location', 'rack', 'tenant__group', 'device_role', 'platform', 'primary_ip4', 'primary_ip6'
  948. )
  949. def get_extra_context(self, request, instance):
  950. # VirtualChassis members
  951. if instance.virtual_chassis is not None:
  952. vc_members = Device.objects.restrict(request.user, 'view').filter(
  953. virtual_chassis=instance.virtual_chassis
  954. ).order_by('vc_position')
  955. else:
  956. vc_members = []
  957. # Services
  958. services = Service.objects.restrict(request.user, 'view').filter(device=instance)
  959. # Find up to ten devices in the same site with the same functional role for quick reference.
  960. related_devices = Device.objects.restrict(request.user, 'view').filter(
  961. site=instance.site, device_role=instance.device_role
  962. ).exclude(
  963. pk=instance.pk
  964. ).prefetch_related(
  965. 'rack', 'device_type__manufacturer'
  966. )[:10]
  967. return {
  968. 'services': services,
  969. 'vc_members': vc_members,
  970. 'related_devices': related_devices,
  971. 'active_tab': 'device',
  972. }
  973. class DeviceConsolePortsView(generic.ObjectView):
  974. queryset = Device.objects.all()
  975. template_name = 'dcim/device/consoleports.html'
  976. def get_extra_context(self, request, instance):
  977. consoleports = ConsolePort.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related(
  978. 'cable', '_path__destination',
  979. )
  980. consoleport_table = tables.DeviceConsolePortTable(
  981. data=consoleports,
  982. user=request.user
  983. )
  984. if request.user.has_perm('dcim.change_consoleport') or request.user.has_perm('dcim.delete_consoleport'):
  985. consoleport_table.columns.show('pk')
  986. paginate_table(consoleport_table, request)
  987. return {
  988. 'consoleport_table': consoleport_table,
  989. 'active_tab': 'console-ports',
  990. }
  991. class DeviceConsoleServerPortsView(generic.ObjectView):
  992. queryset = Device.objects.all()
  993. template_name = 'dcim/device/consoleserverports.html'
  994. def get_extra_context(self, request, instance):
  995. consoleserverports = ConsoleServerPort.objects.restrict(request.user, 'view').filter(
  996. device=instance
  997. ).prefetch_related(
  998. 'cable', '_path__destination',
  999. )
  1000. consoleserverport_table = tables.DeviceConsoleServerPortTable(
  1001. data=consoleserverports,
  1002. user=request.user
  1003. )
  1004. if request.user.has_perm('dcim.change_consoleserverport') or \
  1005. request.user.has_perm('dcim.delete_consoleserverport'):
  1006. consoleserverport_table.columns.show('pk')
  1007. paginate_table(consoleserverport_table, request)
  1008. return {
  1009. 'consoleserverport_table': consoleserverport_table,
  1010. 'active_tab': 'console-server-ports',
  1011. }
  1012. class DevicePowerPortsView(generic.ObjectView):
  1013. queryset = Device.objects.all()
  1014. template_name = 'dcim/device/powerports.html'
  1015. def get_extra_context(self, request, instance):
  1016. powerports = PowerPort.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related(
  1017. 'cable', '_path__destination',
  1018. )
  1019. powerport_table = tables.DevicePowerPortTable(
  1020. data=powerports,
  1021. user=request.user
  1022. )
  1023. if request.user.has_perm('dcim.change_powerport') or request.user.has_perm('dcim.delete_powerport'):
  1024. powerport_table.columns.show('pk')
  1025. paginate_table(powerport_table, request)
  1026. return {
  1027. 'powerport_table': powerport_table,
  1028. 'active_tab': 'power-ports',
  1029. }
  1030. class DevicePowerOutletsView(generic.ObjectView):
  1031. queryset = Device.objects.all()
  1032. template_name = 'dcim/device/poweroutlets.html'
  1033. def get_extra_context(self, request, instance):
  1034. poweroutlets = PowerOutlet.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related(
  1035. 'cable', 'power_port', '_path__destination',
  1036. )
  1037. poweroutlet_table = tables.DevicePowerOutletTable(
  1038. data=poweroutlets,
  1039. user=request.user
  1040. )
  1041. if request.user.has_perm('dcim.change_poweroutlet') or request.user.has_perm('dcim.delete_poweroutlet'):
  1042. poweroutlet_table.columns.show('pk')
  1043. paginate_table(poweroutlet_table, request)
  1044. return {
  1045. 'poweroutlet_table': poweroutlet_table,
  1046. 'active_tab': 'power-outlets',
  1047. }
  1048. class DeviceInterfacesView(generic.ObjectView):
  1049. queryset = Device.objects.all()
  1050. template_name = 'dcim/device/interfaces.html'
  1051. def get_extra_context(self, request, instance):
  1052. interfaces = instance.vc_interfaces().restrict(request.user, 'view').prefetch_related(
  1053. Prefetch('ip_addresses', queryset=IPAddress.objects.restrict(request.user)),
  1054. Prefetch('member_interfaces', queryset=Interface.objects.restrict(request.user)),
  1055. 'lag', 'cable', '_path__destination', 'tags',
  1056. )
  1057. interface_table = tables.DeviceInterfaceTable(
  1058. data=interfaces,
  1059. user=request.user
  1060. )
  1061. if request.user.has_perm('dcim.change_interface') or request.user.has_perm('dcim.delete_interface'):
  1062. interface_table.columns.show('pk')
  1063. paginate_table(interface_table, request)
  1064. return {
  1065. 'interface_table': interface_table,
  1066. 'active_tab': 'interfaces',
  1067. }
  1068. class DeviceFrontPortsView(generic.ObjectView):
  1069. queryset = Device.objects.all()
  1070. template_name = 'dcim/device/frontports.html'
  1071. def get_extra_context(self, request, instance):
  1072. frontports = FrontPort.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related(
  1073. 'rear_port', 'cable',
  1074. )
  1075. frontport_table = tables.DeviceFrontPortTable(
  1076. data=frontports,
  1077. user=request.user
  1078. )
  1079. if request.user.has_perm('dcim.change_frontport') or request.user.has_perm('dcim.delete_frontport'):
  1080. frontport_table.columns.show('pk')
  1081. paginate_table(frontport_table, request)
  1082. return {
  1083. 'frontport_table': frontport_table,
  1084. 'active_tab': 'front-ports',
  1085. }
  1086. class DeviceRearPortsView(generic.ObjectView):
  1087. queryset = Device.objects.all()
  1088. template_name = 'dcim/device/rearports.html'
  1089. def get_extra_context(self, request, instance):
  1090. rearports = RearPort.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related('cable')
  1091. rearport_table = tables.DeviceRearPortTable(
  1092. data=rearports,
  1093. user=request.user
  1094. )
  1095. if request.user.has_perm('dcim.change_rearport') or request.user.has_perm('dcim.delete_rearport'):
  1096. rearport_table.columns.show('pk')
  1097. paginate_table(rearport_table, request)
  1098. return {
  1099. 'rearport_table': rearport_table,
  1100. 'active_tab': 'rear-ports',
  1101. }
  1102. class DeviceDeviceBaysView(generic.ObjectView):
  1103. queryset = Device.objects.all()
  1104. template_name = 'dcim/device/devicebays.html'
  1105. def get_extra_context(self, request, instance):
  1106. devicebays = DeviceBay.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related(
  1107. 'installed_device__device_type__manufacturer',
  1108. )
  1109. devicebay_table = tables.DeviceDeviceBayTable(
  1110. data=devicebays,
  1111. user=request.user
  1112. )
  1113. if request.user.has_perm('dcim.change_devicebay') or request.user.has_perm('dcim.delete_devicebay'):
  1114. devicebay_table.columns.show('pk')
  1115. paginate_table(devicebay_table, request)
  1116. return {
  1117. 'devicebay_table': devicebay_table,
  1118. 'active_tab': 'device-bays',
  1119. }
  1120. class DeviceInventoryView(generic.ObjectView):
  1121. queryset = Device.objects.all()
  1122. template_name = 'dcim/device/inventory.html'
  1123. def get_extra_context(self, request, instance):
  1124. inventoryitems = InventoryItem.objects.restrict(request.user, 'view').filter(
  1125. device=instance
  1126. ).prefetch_related('manufacturer')
  1127. inventoryitem_table = tables.DeviceInventoryItemTable(
  1128. data=inventoryitems,
  1129. user=request.user
  1130. )
  1131. if request.user.has_perm('dcim.change_inventoryitem') or request.user.has_perm('dcim.delete_inventoryitem'):
  1132. inventoryitem_table.columns.show('pk')
  1133. paginate_table(inventoryitem_table, request)
  1134. return {
  1135. 'inventoryitem_table': inventoryitem_table,
  1136. 'active_tab': 'inventory',
  1137. }
  1138. class DeviceStatusView(generic.ObjectView):
  1139. additional_permissions = ['dcim.napalm_read_device']
  1140. queryset = Device.objects.all()
  1141. template_name = 'dcim/device/status.html'
  1142. def get_extra_context(self, request, instance):
  1143. return {
  1144. 'active_tab': 'status',
  1145. }
  1146. class DeviceLLDPNeighborsView(generic.ObjectView):
  1147. additional_permissions = ['dcim.napalm_read_device']
  1148. queryset = Device.objects.all()
  1149. template_name = 'dcim/device/lldp_neighbors.html'
  1150. def get_extra_context(self, request, instance):
  1151. interfaces = instance.vc_interfaces().restrict(request.user, 'view').prefetch_related(
  1152. '_path__destination'
  1153. ).exclude(
  1154. type__in=NONCONNECTABLE_IFACE_TYPES
  1155. )
  1156. return {
  1157. 'interfaces': interfaces,
  1158. 'active_tab': 'lldp-neighbors',
  1159. }
  1160. class DeviceConfigView(generic.ObjectView):
  1161. additional_permissions = ['dcim.napalm_read_device']
  1162. queryset = Device.objects.all()
  1163. template_name = 'dcim/device/config.html'
  1164. def get_extra_context(self, request, instance):
  1165. return {
  1166. 'active_tab': 'config',
  1167. }
  1168. class DeviceConfigContextView(ObjectConfigContextView):
  1169. queryset = Device.objects.annotate_config_context_data()
  1170. base_template = 'dcim/device/base.html'
  1171. class DeviceChangeLogView(ObjectChangeLogView):
  1172. base_template = 'dcim/device/base.html'
  1173. class DeviceJournalView(ObjectJournalView):
  1174. base_template = 'dcim/device/base.html'
  1175. class DeviceEditView(generic.ObjectEditView):
  1176. queryset = Device.objects.all()
  1177. model_form = forms.DeviceForm
  1178. template_name = 'dcim/device_edit.html'
  1179. class DeviceDeleteView(generic.ObjectDeleteView):
  1180. queryset = Device.objects.all()
  1181. class DeviceBulkImportView(generic.BulkImportView):
  1182. queryset = Device.objects.all()
  1183. model_form = forms.DeviceCSVForm
  1184. table = tables.DeviceImportTable
  1185. template_name = 'dcim/device_import.html'
  1186. class ChildDeviceBulkImportView(generic.BulkImportView):
  1187. queryset = Device.objects.all()
  1188. model_form = forms.ChildDeviceCSVForm
  1189. table = tables.DeviceImportTable
  1190. template_name = 'dcim/device_import_child.html'
  1191. def _save_obj(self, obj_form, request):
  1192. obj = obj_form.save()
  1193. # Save the reverse relation to the parent device bay
  1194. device_bay = obj.parent_bay
  1195. device_bay.installed_device = obj
  1196. device_bay.save()
  1197. return obj
  1198. class DeviceBulkEditView(generic.BulkEditView):
  1199. queryset = Device.objects.prefetch_related('tenant', 'site', 'rack', 'device_role', 'device_type__manufacturer')
  1200. filterset = filtersets.DeviceFilterSet
  1201. table = tables.DeviceTable
  1202. form = forms.DeviceBulkEditForm
  1203. class DeviceBulkDeleteView(generic.BulkDeleteView):
  1204. queryset = Device.objects.prefetch_related('tenant', 'site', 'rack', 'device_role', 'device_type__manufacturer')
  1205. filterset = filtersets.DeviceFilterSet
  1206. table = tables.DeviceTable
  1207. #
  1208. # Console ports
  1209. #
  1210. class ConsolePortListView(generic.ObjectListView):
  1211. queryset = ConsolePort.objects.all()
  1212. filterset = filtersets.ConsolePortFilterSet
  1213. filterset_form = forms.ConsolePortFilterForm
  1214. table = tables.ConsolePortTable
  1215. action_buttons = ('import', 'export')
  1216. class ConsolePortView(generic.ObjectView):
  1217. queryset = ConsolePort.objects.all()
  1218. class ConsolePortCreateView(generic.ComponentCreateView):
  1219. queryset = ConsolePort.objects.all()
  1220. form = forms.ConsolePortCreateForm
  1221. model_form = forms.ConsolePortForm
  1222. template_name = 'dcim/device_component_add.html'
  1223. class ConsolePortEditView(generic.ObjectEditView):
  1224. queryset = ConsolePort.objects.all()
  1225. model_form = forms.ConsolePortForm
  1226. template_name = 'dcim/device_component_edit.html'
  1227. class ConsolePortDeleteView(generic.ObjectDeleteView):
  1228. queryset = ConsolePort.objects.all()
  1229. class ConsolePortBulkImportView(generic.BulkImportView):
  1230. queryset = ConsolePort.objects.all()
  1231. model_form = forms.ConsolePortCSVForm
  1232. table = tables.ConsolePortTable
  1233. class ConsolePortBulkEditView(generic.BulkEditView):
  1234. queryset = ConsolePort.objects.all()
  1235. filterset = filtersets.ConsolePortFilterSet
  1236. table = tables.ConsolePortTable
  1237. form = forms.ConsolePortBulkEditForm
  1238. class ConsolePortBulkRenameView(generic.BulkRenameView):
  1239. queryset = ConsolePort.objects.all()
  1240. class ConsolePortBulkDisconnectView(BulkDisconnectView):
  1241. queryset = ConsolePort.objects.all()
  1242. class ConsolePortBulkDeleteView(generic.BulkDeleteView):
  1243. queryset = ConsolePort.objects.all()
  1244. filterset = filtersets.ConsolePortFilterSet
  1245. table = tables.ConsolePortTable
  1246. #
  1247. # Console server ports
  1248. #
  1249. class ConsoleServerPortListView(generic.ObjectListView):
  1250. queryset = ConsoleServerPort.objects.all()
  1251. filterset = filtersets.ConsoleServerPortFilterSet
  1252. filterset_form = forms.ConsoleServerPortFilterForm
  1253. table = tables.ConsoleServerPortTable
  1254. action_buttons = ('import', 'export')
  1255. class ConsoleServerPortView(generic.ObjectView):
  1256. queryset = ConsoleServerPort.objects.all()
  1257. class ConsoleServerPortCreateView(generic.ComponentCreateView):
  1258. queryset = ConsoleServerPort.objects.all()
  1259. form = forms.ConsoleServerPortCreateForm
  1260. model_form = forms.ConsoleServerPortForm
  1261. template_name = 'dcim/device_component_add.html'
  1262. class ConsoleServerPortEditView(generic.ObjectEditView):
  1263. queryset = ConsoleServerPort.objects.all()
  1264. model_form = forms.ConsoleServerPortForm
  1265. template_name = 'dcim/device_component_edit.html'
  1266. class ConsoleServerPortDeleteView(generic.ObjectDeleteView):
  1267. queryset = ConsoleServerPort.objects.all()
  1268. class ConsoleServerPortBulkImportView(generic.BulkImportView):
  1269. queryset = ConsoleServerPort.objects.all()
  1270. model_form = forms.ConsoleServerPortCSVForm
  1271. table = tables.ConsoleServerPortTable
  1272. class ConsoleServerPortBulkEditView(generic.BulkEditView):
  1273. queryset = ConsoleServerPort.objects.all()
  1274. filterset = filtersets.ConsoleServerPortFilterSet
  1275. table = tables.ConsoleServerPortTable
  1276. form = forms.ConsoleServerPortBulkEditForm
  1277. class ConsoleServerPortBulkRenameView(generic.BulkRenameView):
  1278. queryset = ConsoleServerPort.objects.all()
  1279. class ConsoleServerPortBulkDisconnectView(BulkDisconnectView):
  1280. queryset = ConsoleServerPort.objects.all()
  1281. class ConsoleServerPortBulkDeleteView(generic.BulkDeleteView):
  1282. queryset = ConsoleServerPort.objects.all()
  1283. filterset = filtersets.ConsoleServerPortFilterSet
  1284. table = tables.ConsoleServerPortTable
  1285. #
  1286. # Power ports
  1287. #
  1288. class PowerPortListView(generic.ObjectListView):
  1289. queryset = PowerPort.objects.all()
  1290. filterset = filtersets.PowerPortFilterSet
  1291. filterset_form = forms.PowerPortFilterForm
  1292. table = tables.PowerPortTable
  1293. action_buttons = ('import', 'export')
  1294. class PowerPortView(generic.ObjectView):
  1295. queryset = PowerPort.objects.all()
  1296. class PowerPortCreateView(generic.ComponentCreateView):
  1297. queryset = PowerPort.objects.all()
  1298. form = forms.PowerPortCreateForm
  1299. model_form = forms.PowerPortForm
  1300. template_name = 'dcim/device_component_add.html'
  1301. class PowerPortEditView(generic.ObjectEditView):
  1302. queryset = PowerPort.objects.all()
  1303. model_form = forms.PowerPortForm
  1304. template_name = 'dcim/device_component_edit.html'
  1305. class PowerPortDeleteView(generic.ObjectDeleteView):
  1306. queryset = PowerPort.objects.all()
  1307. class PowerPortBulkImportView(generic.BulkImportView):
  1308. queryset = PowerPort.objects.all()
  1309. model_form = forms.PowerPortCSVForm
  1310. table = tables.PowerPortTable
  1311. class PowerPortBulkEditView(generic.BulkEditView):
  1312. queryset = PowerPort.objects.all()
  1313. filterset = filtersets.PowerPortFilterSet
  1314. table = tables.PowerPortTable
  1315. form = forms.PowerPortBulkEditForm
  1316. class PowerPortBulkRenameView(generic.BulkRenameView):
  1317. queryset = PowerPort.objects.all()
  1318. class PowerPortBulkDisconnectView(BulkDisconnectView):
  1319. queryset = PowerPort.objects.all()
  1320. class PowerPortBulkDeleteView(generic.BulkDeleteView):
  1321. queryset = PowerPort.objects.all()
  1322. filterset = filtersets.PowerPortFilterSet
  1323. table = tables.PowerPortTable
  1324. #
  1325. # Power outlets
  1326. #
  1327. class PowerOutletListView(generic.ObjectListView):
  1328. queryset = PowerOutlet.objects.all()
  1329. filterset = filtersets.PowerOutletFilterSet
  1330. filterset_form = forms.PowerOutletFilterForm
  1331. table = tables.PowerOutletTable
  1332. action_buttons = ('import', 'export')
  1333. class PowerOutletView(generic.ObjectView):
  1334. queryset = PowerOutlet.objects.all()
  1335. class PowerOutletCreateView(generic.ComponentCreateView):
  1336. queryset = PowerOutlet.objects.all()
  1337. form = forms.PowerOutletCreateForm
  1338. model_form = forms.PowerOutletForm
  1339. template_name = 'dcim/device_component_add.html'
  1340. class PowerOutletEditView(generic.ObjectEditView):
  1341. queryset = PowerOutlet.objects.all()
  1342. model_form = forms.PowerOutletForm
  1343. template_name = 'dcim/device_component_edit.html'
  1344. class PowerOutletDeleteView(generic.ObjectDeleteView):
  1345. queryset = PowerOutlet.objects.all()
  1346. class PowerOutletBulkImportView(generic.BulkImportView):
  1347. queryset = PowerOutlet.objects.all()
  1348. model_form = forms.PowerOutletCSVForm
  1349. table = tables.PowerOutletTable
  1350. class PowerOutletBulkEditView(generic.BulkEditView):
  1351. queryset = PowerOutlet.objects.all()
  1352. filterset = filtersets.PowerOutletFilterSet
  1353. table = tables.PowerOutletTable
  1354. form = forms.PowerOutletBulkEditForm
  1355. class PowerOutletBulkRenameView(generic.BulkRenameView):
  1356. queryset = PowerOutlet.objects.all()
  1357. class PowerOutletBulkDisconnectView(BulkDisconnectView):
  1358. queryset = PowerOutlet.objects.all()
  1359. class PowerOutletBulkDeleteView(generic.BulkDeleteView):
  1360. queryset = PowerOutlet.objects.all()
  1361. filterset = filtersets.PowerOutletFilterSet
  1362. table = tables.PowerOutletTable
  1363. #
  1364. # Interfaces
  1365. #
  1366. class InterfaceListView(generic.ObjectListView):
  1367. queryset = Interface.objects.all()
  1368. filterset = filtersets.InterfaceFilterSet
  1369. filterset_form = forms.InterfaceFilterForm
  1370. table = tables.InterfaceTable
  1371. action_buttons = ('import', 'export')
  1372. class InterfaceView(generic.ObjectView):
  1373. queryset = Interface.objects.all()
  1374. def get_extra_context(self, request, instance):
  1375. # Get assigned IP addresses
  1376. ipaddress_table = InterfaceIPAddressTable(
  1377. data=instance.ip_addresses.restrict(request.user, 'view').prefetch_related('vrf', 'tenant'),
  1378. orderable=False
  1379. )
  1380. # Get child interfaces
  1381. child_interfaces = Interface.objects.restrict(request.user, 'view').filter(parent=instance)
  1382. child_interfaces_tables = tables.InterfaceTable(
  1383. child_interfaces,
  1384. orderable=False
  1385. )
  1386. child_interfaces_tables.columns.hide('device')
  1387. # Get assigned VLANs and annotate whether each is tagged or untagged
  1388. vlans = []
  1389. if instance.untagged_vlan is not None:
  1390. vlans.append(instance.untagged_vlan)
  1391. vlans[0].tagged = False
  1392. for vlan in instance.tagged_vlans.restrict(request.user).prefetch_related('site', 'group', 'tenant', 'role'):
  1393. vlan.tagged = True
  1394. vlans.append(vlan)
  1395. vlan_table = InterfaceVLANTable(
  1396. interface=instance,
  1397. data=vlans,
  1398. orderable=False
  1399. )
  1400. return {
  1401. 'ipaddress_table': ipaddress_table,
  1402. 'child_interfaces_table': child_interfaces_tables,
  1403. 'vlan_table': vlan_table,
  1404. }
  1405. class InterfaceCreateView(generic.ComponentCreateView):
  1406. queryset = Interface.objects.all()
  1407. form = forms.InterfaceCreateForm
  1408. model_form = forms.InterfaceForm
  1409. template_name = 'dcim/device_component_add.html'
  1410. def post(self, request):
  1411. """
  1412. Override inherited post() method to handle request to assign newly created
  1413. interface objects (first object) to an IP Address object.
  1414. """
  1415. logger = logging.getLogger('netbox.dcim.views.InterfaceCreateView')
  1416. form = self.form(request.POST, initial=request.GET)
  1417. new_objs = self.validate_form(request, form)
  1418. if form.is_valid() and not form.errors:
  1419. if '_addanother' in request.POST:
  1420. return redirect(request.get_full_path())
  1421. elif new_objs is not None and '_assignip' in request.POST and len(new_objs) >= 1 and request.user.has_perm('ipam.add_ipaddress'):
  1422. first_obj = new_objs[0].pk
  1423. return redirect(f'/ipam/ip-addresses/add/?interface={first_obj}&return_url={self.get_return_url(request)}')
  1424. else:
  1425. return redirect(self.get_return_url(request))
  1426. return render(request, self.template_name, {
  1427. 'component_type': self.queryset.model._meta.verbose_name,
  1428. 'form': form,
  1429. 'return_url': self.get_return_url(request),
  1430. })
  1431. class InterfaceEditView(generic.ObjectEditView):
  1432. queryset = Interface.objects.all()
  1433. model_form = forms.InterfaceForm
  1434. template_name = 'dcim/interface_edit.html'
  1435. class InterfaceDeleteView(generic.ObjectDeleteView):
  1436. queryset = Interface.objects.all()
  1437. class InterfaceBulkImportView(generic.BulkImportView):
  1438. queryset = Interface.objects.all()
  1439. model_form = forms.InterfaceCSVForm
  1440. table = tables.InterfaceTable
  1441. class InterfaceBulkEditView(generic.BulkEditView):
  1442. queryset = Interface.objects.all()
  1443. filterset = filtersets.InterfaceFilterSet
  1444. table = tables.InterfaceTable
  1445. form = forms.InterfaceBulkEditForm
  1446. class InterfaceBulkRenameView(generic.BulkRenameView):
  1447. queryset = Interface.objects.all()
  1448. class InterfaceBulkDisconnectView(BulkDisconnectView):
  1449. queryset = Interface.objects.all()
  1450. class InterfaceBulkDeleteView(generic.BulkDeleteView):
  1451. queryset = Interface.objects.all()
  1452. filterset = filtersets.InterfaceFilterSet
  1453. table = tables.InterfaceTable
  1454. #
  1455. # Front ports
  1456. #
  1457. class FrontPortListView(generic.ObjectListView):
  1458. queryset = FrontPort.objects.all()
  1459. filterset = filtersets.FrontPortFilterSet
  1460. filterset_form = forms.FrontPortFilterForm
  1461. table = tables.FrontPortTable
  1462. action_buttons = ('import', 'export')
  1463. class FrontPortView(generic.ObjectView):
  1464. queryset = FrontPort.objects.all()
  1465. class FrontPortCreateView(generic.ComponentCreateView):
  1466. queryset = FrontPort.objects.all()
  1467. form = forms.FrontPortCreateForm
  1468. model_form = forms.FrontPortForm
  1469. template_name = 'dcim/device_component_add.html'
  1470. class FrontPortEditView(generic.ObjectEditView):
  1471. queryset = FrontPort.objects.all()
  1472. model_form = forms.FrontPortForm
  1473. template_name = 'dcim/device_component_edit.html'
  1474. class FrontPortDeleteView(generic.ObjectDeleteView):
  1475. queryset = FrontPort.objects.all()
  1476. class FrontPortBulkImportView(generic.BulkImportView):
  1477. queryset = FrontPort.objects.all()
  1478. model_form = forms.FrontPortCSVForm
  1479. table = tables.FrontPortTable
  1480. class FrontPortBulkEditView(generic.BulkEditView):
  1481. queryset = FrontPort.objects.all()
  1482. filterset = filtersets.FrontPortFilterSet
  1483. table = tables.FrontPortTable
  1484. form = forms.FrontPortBulkEditForm
  1485. class FrontPortBulkRenameView(generic.BulkRenameView):
  1486. queryset = FrontPort.objects.all()
  1487. class FrontPortBulkDisconnectView(BulkDisconnectView):
  1488. queryset = FrontPort.objects.all()
  1489. class FrontPortBulkDeleteView(generic.BulkDeleteView):
  1490. queryset = FrontPort.objects.all()
  1491. filterset = filtersets.FrontPortFilterSet
  1492. table = tables.FrontPortTable
  1493. #
  1494. # Rear ports
  1495. #
  1496. class RearPortListView(generic.ObjectListView):
  1497. queryset = RearPort.objects.all()
  1498. filterset = filtersets.RearPortFilterSet
  1499. filterset_form = forms.RearPortFilterForm
  1500. table = tables.RearPortTable
  1501. action_buttons = ('import', 'export')
  1502. class RearPortView(generic.ObjectView):
  1503. queryset = RearPort.objects.all()
  1504. class RearPortCreateView(generic.ComponentCreateView):
  1505. queryset = RearPort.objects.all()
  1506. form = forms.RearPortCreateForm
  1507. model_form = forms.RearPortForm
  1508. template_name = 'dcim/device_component_add.html'
  1509. class RearPortEditView(generic.ObjectEditView):
  1510. queryset = RearPort.objects.all()
  1511. model_form = forms.RearPortForm
  1512. template_name = 'dcim/device_component_edit.html'
  1513. class RearPortDeleteView(generic.ObjectDeleteView):
  1514. queryset = RearPort.objects.all()
  1515. class RearPortBulkImportView(generic.BulkImportView):
  1516. queryset = RearPort.objects.all()
  1517. model_form = forms.RearPortCSVForm
  1518. table = tables.RearPortTable
  1519. class RearPortBulkEditView(generic.BulkEditView):
  1520. queryset = RearPort.objects.all()
  1521. filterset = filtersets.RearPortFilterSet
  1522. table = tables.RearPortTable
  1523. form = forms.RearPortBulkEditForm
  1524. class RearPortBulkRenameView(generic.BulkRenameView):
  1525. queryset = RearPort.objects.all()
  1526. class RearPortBulkDisconnectView(BulkDisconnectView):
  1527. queryset = RearPort.objects.all()
  1528. class RearPortBulkDeleteView(generic.BulkDeleteView):
  1529. queryset = RearPort.objects.all()
  1530. filterset = filtersets.RearPortFilterSet
  1531. table = tables.RearPortTable
  1532. #
  1533. # Device bays
  1534. #
  1535. class DeviceBayListView(generic.ObjectListView):
  1536. queryset = DeviceBay.objects.all()
  1537. filterset = filtersets.DeviceBayFilterSet
  1538. filterset_form = forms.DeviceBayFilterForm
  1539. table = tables.DeviceBayTable
  1540. action_buttons = ('import', 'export')
  1541. class DeviceBayView(generic.ObjectView):
  1542. queryset = DeviceBay.objects.all()
  1543. class DeviceBayCreateView(generic.ComponentCreateView):
  1544. queryset = DeviceBay.objects.all()
  1545. form = forms.DeviceBayCreateForm
  1546. model_form = forms.DeviceBayForm
  1547. template_name = 'dcim/device_component_add.html'
  1548. class DeviceBayEditView(generic.ObjectEditView):
  1549. queryset = DeviceBay.objects.all()
  1550. model_form = forms.DeviceBayForm
  1551. template_name = 'dcim/device_component_edit.html'
  1552. class DeviceBayDeleteView(generic.ObjectDeleteView):
  1553. queryset = DeviceBay.objects.all()
  1554. class DeviceBayPopulateView(generic.ObjectEditView):
  1555. queryset = DeviceBay.objects.all()
  1556. def get(self, request, pk):
  1557. device_bay = get_object_or_404(self.queryset, pk=pk)
  1558. form = forms.PopulateDeviceBayForm(device_bay)
  1559. return render(request, 'dcim/devicebay_populate.html', {
  1560. 'device_bay': device_bay,
  1561. 'form': form,
  1562. 'return_url': self.get_return_url(request, device_bay),
  1563. })
  1564. def post(self, request, pk):
  1565. device_bay = get_object_or_404(self.queryset, pk=pk)
  1566. form = forms.PopulateDeviceBayForm(device_bay, request.POST)
  1567. if form.is_valid():
  1568. device_bay.installed_device = form.cleaned_data['installed_device']
  1569. device_bay.save()
  1570. messages.success(request, "Added {} to {}.".format(device_bay.installed_device, device_bay))
  1571. return redirect('dcim:device', pk=device_bay.device.pk)
  1572. return render(request, 'dcim/devicebay_populate.html', {
  1573. 'device_bay': device_bay,
  1574. 'form': form,
  1575. 'return_url': self.get_return_url(request, device_bay),
  1576. })
  1577. class DeviceBayDepopulateView(generic.ObjectEditView):
  1578. queryset = DeviceBay.objects.all()
  1579. def get(self, request, pk):
  1580. device_bay = get_object_or_404(self.queryset, pk=pk)
  1581. form = ConfirmationForm()
  1582. return render(request, 'dcim/devicebay_depopulate.html', {
  1583. 'device_bay': device_bay,
  1584. 'form': form,
  1585. 'return_url': self.get_return_url(request, device_bay),
  1586. })
  1587. def post(self, request, pk):
  1588. device_bay = get_object_or_404(self.queryset, pk=pk)
  1589. form = ConfirmationForm(request.POST)
  1590. if form.is_valid():
  1591. removed_device = device_bay.installed_device
  1592. device_bay.installed_device = None
  1593. device_bay.save()
  1594. messages.success(request, "{} has been removed from {}.".format(removed_device, device_bay))
  1595. return redirect('dcim:device', pk=device_bay.device.pk)
  1596. return render(request, 'dcim/devicebay_depopulate.html', {
  1597. 'device_bay': device_bay,
  1598. 'form': form,
  1599. 'return_url': self.get_return_url(request, device_bay),
  1600. })
  1601. class DeviceBayBulkImportView(generic.BulkImportView):
  1602. queryset = DeviceBay.objects.all()
  1603. model_form = forms.DeviceBayCSVForm
  1604. table = tables.DeviceBayTable
  1605. class DeviceBayBulkEditView(generic.BulkEditView):
  1606. queryset = DeviceBay.objects.all()
  1607. filterset = filtersets.DeviceBayFilterSet
  1608. table = tables.DeviceBayTable
  1609. form = forms.DeviceBayBulkEditForm
  1610. class DeviceBayBulkRenameView(generic.BulkRenameView):
  1611. queryset = DeviceBay.objects.all()
  1612. class DeviceBayBulkDeleteView(generic.BulkDeleteView):
  1613. queryset = DeviceBay.objects.all()
  1614. filterset = filtersets.DeviceBayFilterSet
  1615. table = tables.DeviceBayTable
  1616. #
  1617. # Inventory items
  1618. #
  1619. class InventoryItemListView(generic.ObjectListView):
  1620. queryset = InventoryItem.objects.all()
  1621. filterset = filtersets.InventoryItemFilterSet
  1622. filterset_form = forms.InventoryItemFilterForm
  1623. table = tables.InventoryItemTable
  1624. action_buttons = ('import', 'export')
  1625. class InventoryItemView(generic.ObjectView):
  1626. queryset = InventoryItem.objects.all()
  1627. class InventoryItemEditView(generic.ObjectEditView):
  1628. queryset = InventoryItem.objects.all()
  1629. model_form = forms.InventoryItemForm
  1630. class InventoryItemCreateView(generic.ComponentCreateView):
  1631. queryset = InventoryItem.objects.all()
  1632. form = forms.InventoryItemCreateForm
  1633. model_form = forms.InventoryItemForm
  1634. template_name = 'dcim/device_component_add.html'
  1635. class InventoryItemDeleteView(generic.ObjectDeleteView):
  1636. queryset = InventoryItem.objects.all()
  1637. class InventoryItemBulkImportView(generic.BulkImportView):
  1638. queryset = InventoryItem.objects.all()
  1639. model_form = forms.InventoryItemCSVForm
  1640. table = tables.InventoryItemTable
  1641. class InventoryItemBulkEditView(generic.BulkEditView):
  1642. queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer')
  1643. filterset = filtersets.InventoryItemFilterSet
  1644. table = tables.InventoryItemTable
  1645. form = forms.InventoryItemBulkEditForm
  1646. class InventoryItemBulkRenameView(generic.BulkRenameView):
  1647. queryset = InventoryItem.objects.all()
  1648. class InventoryItemBulkDeleteView(generic.BulkDeleteView):
  1649. queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer')
  1650. table = tables.InventoryItemTable
  1651. template_name = 'dcim/inventoryitem_bulk_delete.html'
  1652. #
  1653. # Bulk Device component creation
  1654. #
  1655. class DeviceBulkAddConsolePortView(generic.BulkComponentCreateView):
  1656. parent_model = Device
  1657. parent_field = 'device'
  1658. form = forms.ConsolePortBulkCreateForm
  1659. queryset = ConsolePort.objects.all()
  1660. model_form = forms.ConsolePortForm
  1661. filterset = filtersets.DeviceFilterSet
  1662. table = tables.DeviceTable
  1663. default_return_url = 'dcim:device_list'
  1664. class DeviceBulkAddConsoleServerPortView(generic.BulkComponentCreateView):
  1665. parent_model = Device
  1666. parent_field = 'device'
  1667. form = forms.ConsoleServerPortBulkCreateForm
  1668. queryset = ConsoleServerPort.objects.all()
  1669. model_form = forms.ConsoleServerPortForm
  1670. filterset = filtersets.DeviceFilterSet
  1671. table = tables.DeviceTable
  1672. default_return_url = 'dcim:device_list'
  1673. class DeviceBulkAddPowerPortView(generic.BulkComponentCreateView):
  1674. parent_model = Device
  1675. parent_field = 'device'
  1676. form = forms.PowerPortBulkCreateForm
  1677. queryset = PowerPort.objects.all()
  1678. model_form = forms.PowerPortForm
  1679. filterset = filtersets.DeviceFilterSet
  1680. table = tables.DeviceTable
  1681. default_return_url = 'dcim:device_list'
  1682. class DeviceBulkAddPowerOutletView(generic.BulkComponentCreateView):
  1683. parent_model = Device
  1684. parent_field = 'device'
  1685. form = forms.PowerOutletBulkCreateForm
  1686. queryset = PowerOutlet.objects.all()
  1687. model_form = forms.PowerOutletForm
  1688. filterset = filtersets.DeviceFilterSet
  1689. table = tables.DeviceTable
  1690. default_return_url = 'dcim:device_list'
  1691. class DeviceBulkAddInterfaceView(generic.BulkComponentCreateView):
  1692. parent_model = Device
  1693. parent_field = 'device'
  1694. form = forms.InterfaceBulkCreateForm
  1695. queryset = Interface.objects.all()
  1696. model_form = forms.InterfaceForm
  1697. filterset = filtersets.DeviceFilterSet
  1698. table = tables.DeviceTable
  1699. default_return_url = 'dcim:device_list'
  1700. # class DeviceBulkAddFrontPortView(generic.BulkComponentCreateView):
  1701. # parent_model = Device
  1702. # parent_field = 'device'
  1703. # form = forms.FrontPortBulkCreateForm
  1704. # queryset = FrontPort.objects.all()
  1705. # model_form = forms.FrontPortForm
  1706. # filterset = filtersets.DeviceFilterSet
  1707. # table = tables.DeviceTable
  1708. # default_return_url = 'dcim:device_list'
  1709. class DeviceBulkAddRearPortView(generic.BulkComponentCreateView):
  1710. parent_model = Device
  1711. parent_field = 'device'
  1712. form = forms.RearPortBulkCreateForm
  1713. queryset = RearPort.objects.all()
  1714. model_form = forms.RearPortForm
  1715. filterset = filtersets.DeviceFilterSet
  1716. table = tables.DeviceTable
  1717. default_return_url = 'dcim:device_list'
  1718. class DeviceBulkAddDeviceBayView(generic.BulkComponentCreateView):
  1719. parent_model = Device
  1720. parent_field = 'device'
  1721. form = forms.DeviceBayBulkCreateForm
  1722. queryset = DeviceBay.objects.all()
  1723. model_form = forms.DeviceBayForm
  1724. filterset = filtersets.DeviceFilterSet
  1725. table = tables.DeviceTable
  1726. default_return_url = 'dcim:device_list'
  1727. class DeviceBulkAddInventoryItemView(generic.BulkComponentCreateView):
  1728. parent_model = Device
  1729. parent_field = 'device'
  1730. form = forms.InventoryItemBulkCreateForm
  1731. queryset = InventoryItem.objects.all()
  1732. model_form = forms.InventoryItemForm
  1733. filterset = filtersets.DeviceFilterSet
  1734. table = tables.DeviceTable
  1735. default_return_url = 'dcim:device_list'
  1736. #
  1737. # Cables
  1738. #
  1739. class CableListView(generic.ObjectListView):
  1740. queryset = Cable.objects.all()
  1741. filterset = filtersets.CableFilterSet
  1742. filterset_form = forms.CableFilterForm
  1743. table = tables.CableTable
  1744. action_buttons = ('import', 'export')
  1745. class CableView(generic.ObjectView):
  1746. queryset = Cable.objects.all()
  1747. class PathTraceView(generic.ObjectView):
  1748. """
  1749. Trace a cable path beginning from the given path endpoint (origin).
  1750. """
  1751. additional_permissions = ['dcim.view_cable']
  1752. template_name = 'dcim/cable_trace.html'
  1753. def dispatch(self, request, *args, **kwargs):
  1754. model = kwargs.pop('model')
  1755. self.queryset = model.objects.all()
  1756. return super().dispatch(request, *args, **kwargs)
  1757. def get_extra_context(self, request, instance):
  1758. related_paths = []
  1759. # If tracing a PathEndpoint, locate the CablePath (if one exists) by its origin
  1760. if isinstance(instance, PathEndpoint):
  1761. path = instance._path
  1762. # Otherwise, find all CablePaths which traverse the specified object
  1763. else:
  1764. related_paths = CablePath.objects.filter(path__contains=instance).prefetch_related('origin')
  1765. # Check for specification of a particular path (when tracing pass-through ports)
  1766. try:
  1767. path_id = int(request.GET.get('cablepath_id'))
  1768. except TypeError:
  1769. path_id = None
  1770. if path_id in list(related_paths.values_list('pk', flat=True)):
  1771. path = CablePath.objects.get(pk=path_id)
  1772. else:
  1773. path = related_paths.first()
  1774. # Get the total length of the cable and whether the length is definitive (fully defined)
  1775. total_length, is_definitive = path.get_total_length() if path else (None, False)
  1776. return {
  1777. 'path': path,
  1778. 'related_paths': related_paths,
  1779. 'total_length': total_length,
  1780. 'is_definitive': is_definitive
  1781. }
  1782. class CableCreateView(generic.ObjectEditView):
  1783. queryset = Cable.objects.all()
  1784. template_name = 'dcim/cable_connect.html'
  1785. def dispatch(self, request, *args, **kwargs):
  1786. # Set the model_form class based on the type of component being connected
  1787. self.model_form = {
  1788. 'console-port': forms.ConnectCableToConsolePortForm,
  1789. 'console-server-port': forms.ConnectCableToConsoleServerPortForm,
  1790. 'power-port': forms.ConnectCableToPowerPortForm,
  1791. 'power-outlet': forms.ConnectCableToPowerOutletForm,
  1792. 'interface': forms.ConnectCableToInterfaceForm,
  1793. 'front-port': forms.ConnectCableToFrontPortForm,
  1794. 'rear-port': forms.ConnectCableToRearPortForm,
  1795. 'power-feed': forms.ConnectCableToPowerFeedForm,
  1796. 'circuit-termination': forms.ConnectCableToCircuitTerminationForm,
  1797. }[kwargs.get('termination_b_type')]
  1798. return super().dispatch(request, *args, **kwargs)
  1799. def alter_obj(self, obj, request, url_args, url_kwargs):
  1800. termination_a_type = url_kwargs.get('termination_a_type')
  1801. termination_a_id = url_kwargs.get('termination_a_id')
  1802. termination_b_type_name = url_kwargs.get('termination_b_type')
  1803. self.termination_b_type = ContentType.objects.get(model=termination_b_type_name.replace('-', ''))
  1804. # Initialize Cable termination attributes
  1805. obj.termination_a = termination_a_type.objects.get(pk=termination_a_id)
  1806. obj.termination_b_type = self.termination_b_type
  1807. return obj
  1808. def get(self, request, *args, **kwargs):
  1809. obj = self.alter_obj(self.get_object(kwargs), request, args, kwargs)
  1810. # Parse initial data manually to avoid setting field values as lists
  1811. initial_data = {k: request.GET[k] for k in request.GET}
  1812. # Set initial site and rack based on side A termination (if not already set)
  1813. termination_a_site = getattr(obj.termination_a.parent_object, 'site', None)
  1814. if termination_a_site and 'termination_b_region' not in initial_data:
  1815. initial_data['termination_b_region'] = termination_a_site.region
  1816. if termination_a_site and 'termination_b_site_group' not in initial_data:
  1817. initial_data['termination_b_site_group'] = termination_a_site.group
  1818. if 'termination_b_site' not in initial_data:
  1819. initial_data['termination_b_site'] = termination_a_site
  1820. if 'termination_b_rack' not in initial_data:
  1821. initial_data['termination_b_rack'] = getattr(obj.termination_a.parent_object, 'rack', None)
  1822. form = self.model_form(instance=obj, initial=initial_data)
  1823. return render(request, self.template_name, {
  1824. 'obj': obj,
  1825. 'obj_type': Cable._meta.verbose_name,
  1826. 'termination_b_type': self.termination_b_type.name,
  1827. 'form': form,
  1828. 'return_url': self.get_return_url(request, obj),
  1829. })
  1830. class CableEditView(generic.ObjectEditView):
  1831. queryset = Cable.objects.all()
  1832. model_form = forms.CableForm
  1833. template_name = 'dcim/cable_edit.html'
  1834. class CableDeleteView(generic.ObjectDeleteView):
  1835. queryset = Cable.objects.all()
  1836. class CableBulkImportView(generic.BulkImportView):
  1837. queryset = Cable.objects.all()
  1838. model_form = forms.CableCSVForm
  1839. table = tables.CableTable
  1840. class CableBulkEditView(generic.BulkEditView):
  1841. queryset = Cable.objects.prefetch_related('termination_a', 'termination_b')
  1842. filterset = filtersets.CableFilterSet
  1843. table = tables.CableTable
  1844. form = forms.CableBulkEditForm
  1845. class CableBulkDeleteView(generic.BulkDeleteView):
  1846. queryset = Cable.objects.prefetch_related('termination_a', 'termination_b')
  1847. filterset = filtersets.CableFilterSet
  1848. table = tables.CableTable
  1849. #
  1850. # Connections
  1851. #
  1852. class ConsoleConnectionsListView(generic.ObjectListView):
  1853. queryset = ConsolePort.objects.filter(_path__isnull=False).order_by('device')
  1854. filterset = filtersets.ConsoleConnectionFilterSet
  1855. filterset_form = forms.ConsoleConnectionFilterForm
  1856. table = tables.ConsoleConnectionTable
  1857. template_name = 'dcim/connections_list.html'
  1858. def extra_context(self):
  1859. return {
  1860. 'title': 'Console Connections'
  1861. }
  1862. class PowerConnectionsListView(generic.ObjectListView):
  1863. queryset = PowerPort.objects.filter(_path__isnull=False).order_by('device')
  1864. filterset = filtersets.PowerConnectionFilterSet
  1865. filterset_form = forms.PowerConnectionFilterForm
  1866. table = tables.PowerConnectionTable
  1867. template_name = 'dcim/connections_list.html'
  1868. def extra_context(self):
  1869. return {
  1870. 'title': 'Power Connections'
  1871. }
  1872. class InterfaceConnectionsListView(generic.ObjectListView):
  1873. queryset = Interface.objects.filter(
  1874. # Avoid duplicate connections by only selecting the lower PK in a connected pair
  1875. _path__isnull=False,
  1876. pk__lt=F('_path__destination_id')
  1877. ).order_by('device')
  1878. filterset = filtersets.InterfaceConnectionFilterSet
  1879. filterset_form = forms.InterfaceConnectionFilterForm
  1880. table = tables.InterfaceConnectionTable
  1881. template_name = 'dcim/connections_list.html'
  1882. def extra_context(self):
  1883. return {
  1884. 'title': 'Interface Connections'
  1885. }
  1886. #
  1887. # Virtual chassis
  1888. #
  1889. class VirtualChassisListView(generic.ObjectListView):
  1890. queryset = VirtualChassis.objects.prefetch_related('master').annotate(
  1891. member_count=count_related(Device, 'virtual_chassis')
  1892. )
  1893. table = tables.VirtualChassisTable
  1894. filterset = filtersets.VirtualChassisFilterSet
  1895. filterset_form = forms.VirtualChassisFilterForm
  1896. class VirtualChassisView(generic.ObjectView):
  1897. queryset = VirtualChassis.objects.all()
  1898. def get_extra_context(self, request, instance):
  1899. members = Device.objects.restrict(request.user).filter(virtual_chassis=instance)
  1900. return {
  1901. 'members': members,
  1902. }
  1903. class VirtualChassisCreateView(generic.ObjectEditView):
  1904. queryset = VirtualChassis.objects.all()
  1905. model_form = forms.VirtualChassisCreateForm
  1906. template_name = 'dcim/virtualchassis_add.html'
  1907. class VirtualChassisEditView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View):
  1908. queryset = VirtualChassis.objects.all()
  1909. def get_required_permission(self):
  1910. return 'dcim.change_virtualchassis'
  1911. def get(self, request, pk):
  1912. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  1913. VCMemberFormSet = modelformset_factory(
  1914. model=Device,
  1915. form=forms.DeviceVCMembershipForm,
  1916. formset=forms.BaseVCMemberFormSet,
  1917. extra=0
  1918. )
  1919. members_queryset = virtual_chassis.members.prefetch_related('rack').order_by('vc_position')
  1920. vc_form = forms.VirtualChassisForm(instance=virtual_chassis)
  1921. vc_form.fields['master'].queryset = members_queryset
  1922. formset = VCMemberFormSet(queryset=members_queryset)
  1923. return render(request, 'dcim/virtualchassis_edit.html', {
  1924. 'vc_form': vc_form,
  1925. 'formset': formset,
  1926. 'return_url': self.get_return_url(request, virtual_chassis),
  1927. })
  1928. def post(self, request, pk):
  1929. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  1930. VCMemberFormSet = modelformset_factory(
  1931. model=Device,
  1932. form=forms.DeviceVCMembershipForm,
  1933. formset=forms.BaseVCMemberFormSet,
  1934. extra=0
  1935. )
  1936. members_queryset = virtual_chassis.members.prefetch_related('rack').order_by('vc_position')
  1937. vc_form = forms.VirtualChassisForm(request.POST, instance=virtual_chassis)
  1938. vc_form.fields['master'].queryset = members_queryset
  1939. formset = VCMemberFormSet(request.POST, queryset=members_queryset)
  1940. if vc_form.is_valid() and formset.is_valid():
  1941. with transaction.atomic():
  1942. # Save the VirtualChassis
  1943. vc_form.save()
  1944. # Nullify the vc_position of each member first to allow reordering without raising an IntegrityError on
  1945. # duplicate positions. Then save each member instance.
  1946. members = formset.save(commit=False)
  1947. devices = Device.objects.filter(pk__in=[m.pk for m in members])
  1948. for device in devices:
  1949. device.vc_position = None
  1950. device.save()
  1951. for member in members:
  1952. member.save()
  1953. return redirect(virtual_chassis.get_absolute_url())
  1954. return render(request, 'dcim/virtualchassis_edit.html', {
  1955. 'vc_form': vc_form,
  1956. 'formset': formset,
  1957. 'return_url': self.get_return_url(request, virtual_chassis),
  1958. })
  1959. class VirtualChassisDeleteView(generic.ObjectDeleteView):
  1960. queryset = VirtualChassis.objects.all()
  1961. class VirtualChassisAddMemberView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View):
  1962. queryset = VirtualChassis.objects.all()
  1963. def get_required_permission(self):
  1964. return 'dcim.change_virtualchassis'
  1965. def get(self, request, pk):
  1966. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  1967. initial_data = {k: request.GET[k] for k in request.GET}
  1968. member_select_form = forms.VCMemberSelectForm(initial=initial_data)
  1969. membership_form = forms.DeviceVCMembershipForm(initial=initial_data)
  1970. return render(request, 'dcim/virtualchassis_add_member.html', {
  1971. 'virtual_chassis': virtual_chassis,
  1972. 'member_select_form': member_select_form,
  1973. 'membership_form': membership_form,
  1974. 'return_url': self.get_return_url(request, virtual_chassis),
  1975. })
  1976. def post(self, request, pk):
  1977. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  1978. member_select_form = forms.VCMemberSelectForm(request.POST)
  1979. if member_select_form.is_valid():
  1980. device = member_select_form.cleaned_data['device']
  1981. device.virtual_chassis = virtual_chassis
  1982. data = {k: request.POST[k] for k in ['vc_position', 'vc_priority']}
  1983. membership_form = forms.DeviceVCMembershipForm(data=data, validate_vc_position=True, instance=device)
  1984. if membership_form.is_valid():
  1985. membership_form.save()
  1986. msg = 'Added member <a href="{}">{}</a>'.format(device.get_absolute_url(), escape(device))
  1987. messages.success(request, mark_safe(msg))
  1988. if '_addanother' in request.POST:
  1989. return redirect(request.get_full_path())
  1990. return redirect(self.get_return_url(request, device))
  1991. else:
  1992. membership_form = forms.DeviceVCMembershipForm(data=request.POST)
  1993. return render(request, 'dcim/virtualchassis_add_member.html', {
  1994. 'virtual_chassis': virtual_chassis,
  1995. 'member_select_form': member_select_form,
  1996. 'membership_form': membership_form,
  1997. 'return_url': self.get_return_url(request, virtual_chassis),
  1998. })
  1999. class VirtualChassisRemoveMemberView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View):
  2000. queryset = Device.objects.all()
  2001. def get_required_permission(self):
  2002. return 'dcim.change_device'
  2003. def get(self, request, pk):
  2004. device = get_object_or_404(self.queryset, pk=pk, virtual_chassis__isnull=False)
  2005. form = ConfirmationForm(initial=request.GET)
  2006. return render(request, 'dcim/virtualchassis_remove_member.html', {
  2007. 'device': device,
  2008. 'form': form,
  2009. 'return_url': self.get_return_url(request, device),
  2010. })
  2011. def post(self, request, pk):
  2012. device = get_object_or_404(self.queryset, pk=pk, virtual_chassis__isnull=False)
  2013. form = ConfirmationForm(request.POST)
  2014. # Protect master device from being removed
  2015. virtual_chassis = VirtualChassis.objects.filter(master=device).first()
  2016. if virtual_chassis is not None:
  2017. msg = 'Unable to remove master device {} from the virtual chassis.'.format(escape(device))
  2018. messages.error(request, mark_safe(msg))
  2019. return redirect(device.get_absolute_url())
  2020. if form.is_valid():
  2021. devices = Device.objects.filter(pk=device.pk)
  2022. for device in devices:
  2023. device.virtual_chassis = None
  2024. device.vc_position = None
  2025. device.vc_priority = None
  2026. device.save()
  2027. msg = 'Removed {} from virtual chassis {}'.format(device, device.virtual_chassis)
  2028. messages.success(request, msg)
  2029. return redirect(self.get_return_url(request, device))
  2030. return render(request, 'dcim/virtualchassis_remove_member.html', {
  2031. 'device': device,
  2032. 'form': form,
  2033. 'return_url': self.get_return_url(request, device),
  2034. })
  2035. class VirtualChassisBulkImportView(generic.BulkImportView):
  2036. queryset = VirtualChassis.objects.all()
  2037. model_form = forms.VirtualChassisCSVForm
  2038. table = tables.VirtualChassisTable
  2039. class VirtualChassisBulkEditView(generic.BulkEditView):
  2040. queryset = VirtualChassis.objects.all()
  2041. filterset = filtersets.VirtualChassisFilterSet
  2042. table = tables.VirtualChassisTable
  2043. form = forms.VirtualChassisBulkEditForm
  2044. class VirtualChassisBulkDeleteView(generic.BulkDeleteView):
  2045. queryset = VirtualChassis.objects.all()
  2046. filterset = filtersets.VirtualChassisFilterSet
  2047. table = tables.VirtualChassisTable
  2048. #
  2049. # Power panels
  2050. #
  2051. class PowerPanelListView(generic.ObjectListView):
  2052. queryset = PowerPanel.objects.prefetch_related(
  2053. 'site', 'location'
  2054. ).annotate(
  2055. powerfeed_count=count_related(PowerFeed, 'power_panel')
  2056. )
  2057. filterset = filtersets.PowerPanelFilterSet
  2058. filterset_form = forms.PowerPanelFilterForm
  2059. table = tables.PowerPanelTable
  2060. class PowerPanelView(generic.ObjectView):
  2061. queryset = PowerPanel.objects.prefetch_related('site', 'location')
  2062. def get_extra_context(self, request, instance):
  2063. power_feeds = PowerFeed.objects.restrict(request.user).filter(power_panel=instance).prefetch_related('rack')
  2064. powerfeed_table = tables.PowerFeedTable(
  2065. data=power_feeds,
  2066. orderable=False
  2067. )
  2068. if request.user.has_perm('dcim.delete_cable'):
  2069. powerfeed_table.columns.show('pk')
  2070. powerfeed_table.exclude = ['power_panel']
  2071. return {
  2072. 'powerfeed_table': powerfeed_table,
  2073. }
  2074. class PowerPanelEditView(generic.ObjectEditView):
  2075. queryset = PowerPanel.objects.all()
  2076. model_form = forms.PowerPanelForm
  2077. class PowerPanelDeleteView(generic.ObjectDeleteView):
  2078. queryset = PowerPanel.objects.all()
  2079. class PowerPanelBulkImportView(generic.BulkImportView):
  2080. queryset = PowerPanel.objects.all()
  2081. model_form = forms.PowerPanelCSVForm
  2082. table = tables.PowerPanelTable
  2083. class PowerPanelBulkEditView(generic.BulkEditView):
  2084. queryset = PowerPanel.objects.prefetch_related('site', 'location')
  2085. filterset = filtersets.PowerPanelFilterSet
  2086. table = tables.PowerPanelTable
  2087. form = forms.PowerPanelBulkEditForm
  2088. class PowerPanelBulkDeleteView(generic.BulkDeleteView):
  2089. queryset = PowerPanel.objects.prefetch_related(
  2090. 'site', 'location'
  2091. ).annotate(
  2092. powerfeed_count=count_related(PowerFeed, 'power_panel')
  2093. )
  2094. filterset = filtersets.PowerPanelFilterSet
  2095. table = tables.PowerPanelTable
  2096. #
  2097. # Power feeds
  2098. #
  2099. class PowerFeedListView(generic.ObjectListView):
  2100. queryset = PowerFeed.objects.all()
  2101. filterset = filtersets.PowerFeedFilterSet
  2102. filterset_form = forms.PowerFeedFilterForm
  2103. table = tables.PowerFeedTable
  2104. class PowerFeedView(generic.ObjectView):
  2105. queryset = PowerFeed.objects.prefetch_related('power_panel', 'rack')
  2106. class PowerFeedEditView(generic.ObjectEditView):
  2107. queryset = PowerFeed.objects.all()
  2108. model_form = forms.PowerFeedForm
  2109. class PowerFeedDeleteView(generic.ObjectDeleteView):
  2110. queryset = PowerFeed.objects.all()
  2111. class PowerFeedBulkImportView(generic.BulkImportView):
  2112. queryset = PowerFeed.objects.all()
  2113. model_form = forms.PowerFeedCSVForm
  2114. table = tables.PowerFeedTable
  2115. class PowerFeedBulkEditView(generic.BulkEditView):
  2116. queryset = PowerFeed.objects.prefetch_related('power_panel', 'rack')
  2117. filterset = filtersets.PowerFeedFilterSet
  2118. table = tables.PowerFeedTable
  2119. form = forms.PowerFeedBulkEditForm
  2120. class PowerFeedBulkDisconnectView(BulkDisconnectView):
  2121. queryset = PowerFeed.objects.all()
  2122. class PowerFeedBulkDeleteView(generic.BulkDeleteView):
  2123. queryset = PowerFeed.objects.prefetch_related('power_panel', 'rack')
  2124. filterset = filtersets.PowerFeedFilterSet
  2125. table = tables.PowerFeedTable