views.py 91 KB

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