2
0

views.py 112 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628
  1. from django.contrib import messages
  2. from django.contrib.contenttypes.models import ContentType
  3. from django.core.paginator import EmptyPage, PageNotAnInteger
  4. from django.db import transaction
  5. from django.db.models import Prefetch
  6. from django.forms import ModelMultipleChoiceField, MultipleHiddenInput, modelformset_factory
  7. from django.shortcuts import get_object_or_404, redirect, render
  8. from django.urls import reverse
  9. from django.utils.html import escape
  10. from django.utils.safestring import mark_safe
  11. from django.utils.translation import gettext as _
  12. from django.views.generic import View
  13. from circuits.models import Circuit, CircuitTermination
  14. from extras.views import ObjectConfigContextView
  15. from ipam.models import ASN, IPAddress, Prefix, Service, VLAN, VLANGroup
  16. from ipam.tables import AssignedIPAddressesTable, 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.utils import count_related
  22. from utilities.views import GetReturnURLMixin, ObjectPermissionRequiredMixin, ViewTab, register_model_view
  23. from virtualization.models import VirtualMachine
  24. from . import filtersets, forms, tables
  25. from .choices import DeviceFaceChoices
  26. from .constants import NONCONNECTABLE_IFACE_TYPES
  27. from .models import *
  28. CABLE_TERMINATION_TYPES = {
  29. 'dcim.consoleport': ConsolePort,
  30. 'dcim.consoleserverport': ConsoleServerPort,
  31. 'dcim.powerport': PowerPort,
  32. 'dcim.poweroutlet': PowerOutlet,
  33. 'dcim.interface': Interface,
  34. 'dcim.frontport': FrontPort,
  35. 'dcim.rearport': RearPort,
  36. 'dcim.powerfeed': PowerFeed,
  37. 'circuits.circuittermination': CircuitTermination,
  38. }
  39. class DeviceComponentsView(generic.ObjectChildrenView):
  40. queryset = Device.objects.all()
  41. def get_children(self, request, parent):
  42. return self.child_model.objects.restrict(request.user, 'view').filter(device=parent)
  43. class DeviceTypeComponentsView(DeviceComponentsView):
  44. queryset = DeviceType.objects.all()
  45. template_name = 'dcim/devicetype/component_templates.html'
  46. viewname = None # Used for return_url resolution
  47. def get_children(self, request, parent):
  48. return self.child_model.objects.restrict(request.user, 'view').filter(device_type=parent)
  49. def get_extra_context(self, request, instance):
  50. return {
  51. 'return_url': reverse(self.viewname, kwargs={'pk': instance.pk}),
  52. }
  53. class ModuleTypeComponentsView(DeviceComponentsView):
  54. queryset = ModuleType.objects.all()
  55. template_name = 'dcim/moduletype/component_templates.html'
  56. viewname = None # Used for return_url resolution
  57. def get_children(self, request, parent):
  58. return self.child_model.objects.restrict(request.user, 'view').filter(module_type=parent)
  59. def get_extra_context(self, request, instance):
  60. return {
  61. 'return_url': reverse(self.viewname, kwargs={'pk': instance.pk}),
  62. }
  63. class BulkDisconnectView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
  64. """
  65. An extendable view for disconnection console/power/interface components in bulk.
  66. """
  67. queryset = None
  68. template_name = 'dcim/bulk_disconnect.html'
  69. def __init__(self, *args, **kwargs):
  70. super().__init__(*args, **kwargs)
  71. # Create a new Form class from ConfirmationForm
  72. class _Form(ConfirmationForm):
  73. pk = ModelMultipleChoiceField(
  74. queryset=self.queryset,
  75. widget=MultipleHiddenInput()
  76. )
  77. self.form = _Form
  78. def get_required_permission(self):
  79. return get_permission_for_model(self.queryset.model, 'change')
  80. def post(self, request):
  81. selected_objects = []
  82. return_url = self.get_return_url(request)
  83. if '_confirm' in request.POST:
  84. form = self.form(request.POST)
  85. if form.is_valid():
  86. with transaction.atomic():
  87. count = 0
  88. for obj in self.queryset.filter(pk__in=form.cleaned_data['pk']):
  89. if obj.cable is None:
  90. continue
  91. obj.cable.delete()
  92. count += 1
  93. messages.success(request, "Disconnected {} {}".format(
  94. count, self.queryset.model._meta.verbose_name_plural
  95. ))
  96. return redirect(return_url)
  97. else:
  98. form = self.form(initial={'pk': request.POST.getlist('pk')})
  99. selected_objects = self.queryset.filter(pk__in=form.initial['pk'])
  100. return render(request, self.template_name, {
  101. 'form': form,
  102. 'obj_type_plural': self.queryset.model._meta.verbose_name_plural,
  103. 'selected_objects': selected_objects,
  104. 'return_url': return_url,
  105. })
  106. class PathTraceView(generic.ObjectView):
  107. """
  108. Trace a cable path beginning from the given path endpoint (origin).
  109. """
  110. additional_permissions = ['dcim.view_cable']
  111. template_name = 'dcim/cable_trace.html'
  112. def dispatch(self, request, *args, **kwargs):
  113. model = kwargs.pop('model')
  114. self.queryset = model.objects.all()
  115. return super().dispatch(request, *args, **kwargs)
  116. def get_extra_context(self, request, instance):
  117. related_paths = []
  118. # If tracing a PathEndpoint, locate the CablePath (if one exists) by its origin
  119. if isinstance(instance, PathEndpoint):
  120. path = instance._path
  121. # Otherwise, find all CablePaths which traverse the specified object
  122. else:
  123. related_paths = CablePath.objects.filter(_nodes__contains=instance)
  124. # Check for specification of a particular path (when tracing pass-through ports)
  125. try:
  126. path_id = int(request.GET.get('cablepath_id'))
  127. except TypeError:
  128. path_id = None
  129. if path_id in list(related_paths.values_list('pk', flat=True)):
  130. path = CablePath.objects.get(pk=path_id)
  131. else:
  132. path = related_paths.first()
  133. # No paths found
  134. if path is None:
  135. return {
  136. 'path': None
  137. }
  138. # Get the total length of the cable and whether the length is definitive (fully defined)
  139. total_length, is_definitive = path.get_total_length() if path else (None, False)
  140. # Determine the path to the SVG trace image
  141. api_viewname = f"{path.origin_type.app_label}-api:{path.origin_type.model}-trace"
  142. svg_url = f"{reverse(api_viewname, kwargs={'pk': path.origins[0].pk})}?render=svg"
  143. return {
  144. 'path': path,
  145. 'related_paths': related_paths,
  146. 'total_length': total_length,
  147. 'is_definitive': is_definitive,
  148. 'svg_url': svg_url,
  149. }
  150. #
  151. # Regions
  152. #
  153. class RegionListView(generic.ObjectListView):
  154. queryset = Region.objects.add_related_count(
  155. Region.objects.all(),
  156. Site,
  157. 'region',
  158. 'site_count',
  159. cumulative=True
  160. )
  161. filterset = filtersets.RegionFilterSet
  162. filterset_form = forms.RegionFilterForm
  163. table = tables.RegionTable
  164. @register_model_view(Region)
  165. class RegionView(generic.ObjectView):
  166. queryset = Region.objects.all()
  167. def get_extra_context(self, request, instance):
  168. child_regions = Region.objects.add_related_count(
  169. Region.objects.all(),
  170. Site,
  171. 'region',
  172. 'site_count',
  173. cumulative=True
  174. ).restrict(request.user, 'view').filter(
  175. parent__in=instance.get_descendants(include_self=True)
  176. )
  177. child_regions_table = tables.RegionTable(child_regions)
  178. child_regions_table.columns.hide('actions')
  179. sites = Site.objects.restrict(request.user, 'view').filter(
  180. region=instance
  181. )
  182. sites_table = tables.SiteTable(sites, user=request.user, exclude=('region',))
  183. sites_table.configure(request)
  184. return {
  185. 'child_regions_table': child_regions_table,
  186. 'sites_table': sites_table,
  187. }
  188. @register_model_view(Region, 'edit')
  189. class RegionEditView(generic.ObjectEditView):
  190. queryset = Region.objects.all()
  191. form = forms.RegionForm
  192. @register_model_view(Region, 'delete')
  193. class RegionDeleteView(generic.ObjectDeleteView):
  194. queryset = Region.objects.all()
  195. class RegionBulkImportView(generic.BulkImportView):
  196. queryset = Region.objects.all()
  197. model_form = forms.RegionCSVForm
  198. table = tables.RegionTable
  199. class RegionBulkEditView(generic.BulkEditView):
  200. queryset = Region.objects.add_related_count(
  201. Region.objects.all(),
  202. Site,
  203. 'region',
  204. 'site_count',
  205. cumulative=True
  206. )
  207. filterset = filtersets.RegionFilterSet
  208. table = tables.RegionTable
  209. form = forms.RegionBulkEditForm
  210. class RegionBulkDeleteView(generic.BulkDeleteView):
  211. queryset = Region.objects.add_related_count(
  212. Region.objects.all(),
  213. Site,
  214. 'region',
  215. 'site_count',
  216. cumulative=True
  217. )
  218. filterset = filtersets.RegionFilterSet
  219. table = tables.RegionTable
  220. #
  221. # Site groups
  222. #
  223. class SiteGroupListView(generic.ObjectListView):
  224. queryset = SiteGroup.objects.add_related_count(
  225. SiteGroup.objects.all(),
  226. Site,
  227. 'group',
  228. 'site_count',
  229. cumulative=True
  230. )
  231. filterset = filtersets.SiteGroupFilterSet
  232. filterset_form = forms.SiteGroupFilterForm
  233. table = tables.SiteGroupTable
  234. @register_model_view(SiteGroup)
  235. class SiteGroupView(generic.ObjectView):
  236. queryset = SiteGroup.objects.all()
  237. def get_extra_context(self, request, instance):
  238. child_groups = SiteGroup.objects.add_related_count(
  239. SiteGroup.objects.all(),
  240. Site,
  241. 'group',
  242. 'site_count',
  243. cumulative=True
  244. ).restrict(request.user, 'view').filter(
  245. parent__in=instance.get_descendants(include_self=True)
  246. )
  247. child_groups_table = tables.SiteGroupTable(child_groups)
  248. child_groups_table.columns.hide('actions')
  249. sites = Site.objects.restrict(request.user, 'view').filter(
  250. group=instance
  251. )
  252. sites_table = tables.SiteTable(sites, user=request.user, exclude=('group',))
  253. sites_table.configure(request)
  254. return {
  255. 'child_groups_table': child_groups_table,
  256. 'sites_table': sites_table,
  257. }
  258. @register_model_view(SiteGroup, 'edit')
  259. class SiteGroupEditView(generic.ObjectEditView):
  260. queryset = SiteGroup.objects.all()
  261. form = forms.SiteGroupForm
  262. @register_model_view(SiteGroup, 'delete')
  263. class SiteGroupDeleteView(generic.ObjectDeleteView):
  264. queryset = SiteGroup.objects.all()
  265. class SiteGroupBulkImportView(generic.BulkImportView):
  266. queryset = SiteGroup.objects.all()
  267. model_form = forms.SiteGroupCSVForm
  268. table = tables.SiteGroupTable
  269. class SiteGroupBulkEditView(generic.BulkEditView):
  270. queryset = SiteGroup.objects.add_related_count(
  271. SiteGroup.objects.all(),
  272. Site,
  273. 'group',
  274. 'site_count',
  275. cumulative=True
  276. )
  277. filterset = filtersets.SiteGroupFilterSet
  278. table = tables.SiteGroupTable
  279. form = forms.SiteGroupBulkEditForm
  280. class SiteGroupBulkDeleteView(generic.BulkDeleteView):
  281. queryset = SiteGroup.objects.add_related_count(
  282. SiteGroup.objects.all(),
  283. Site,
  284. 'group',
  285. 'site_count',
  286. cumulative=True
  287. )
  288. filterset = filtersets.SiteGroupFilterSet
  289. table = tables.SiteGroupTable
  290. #
  291. # Sites
  292. #
  293. class SiteListView(generic.ObjectListView):
  294. queryset = Site.objects.all()
  295. filterset = filtersets.SiteFilterSet
  296. filterset_form = forms.SiteFilterForm
  297. table = tables.SiteTable
  298. @register_model_view(Site)
  299. class SiteView(generic.ObjectView):
  300. queryset = Site.objects.prefetch_related('tenant__group')
  301. def get_extra_context(self, request, instance):
  302. stats = {
  303. 'location_count': Location.objects.restrict(request.user, 'view').filter(site=instance).count(),
  304. 'rack_count': Rack.objects.restrict(request.user, 'view').filter(site=instance).count(),
  305. 'device_count': Device.objects.restrict(request.user, 'view').filter(site=instance).count(),
  306. 'prefix_count': Prefix.objects.restrict(request.user, 'view').filter(site=instance).count(),
  307. 'vlangroup_count': VLANGroup.objects.restrict(request.user, 'view').filter(
  308. scope_type=ContentType.objects.get_for_model(Site),
  309. scope_id=instance.pk
  310. ).count(),
  311. 'vlan_count': VLAN.objects.restrict(request.user, 'view').filter(site=instance).count(),
  312. 'circuit_count': Circuit.objects.restrict(request.user, 'view').filter(terminations__site=instance).count(),
  313. 'vm_count': VirtualMachine.objects.restrict(request.user, 'view').filter(cluster__site=instance).count(),
  314. }
  315. locations = Location.objects.add_related_count(
  316. Location.objects.all(),
  317. Rack,
  318. 'location',
  319. 'rack_count',
  320. cumulative=True
  321. )
  322. locations = Location.objects.add_related_count(
  323. locations,
  324. Device,
  325. 'location',
  326. 'device_count',
  327. cumulative=True
  328. ).restrict(request.user, 'view').filter(site=instance)
  329. nonracked_devices = Device.objects.filter(
  330. site=instance,
  331. rack__isnull=True,
  332. parent_bay__isnull=True
  333. ).prefetch_related('device_type__manufacturer', 'parent_bay', 'device_role')
  334. asns = ASN.objects.restrict(request.user, 'view').filter(sites=instance)
  335. asn_count = asns.count()
  336. stats.update({'asn_count': asn_count})
  337. return {
  338. 'stats': stats,
  339. 'locations': locations,
  340. 'asns': asns,
  341. 'nonracked_devices': nonracked_devices.order_by('-pk')[:10],
  342. 'total_nonracked_devices_count': nonracked_devices.count(),
  343. }
  344. @register_model_view(Site, 'edit')
  345. class SiteEditView(generic.ObjectEditView):
  346. queryset = Site.objects.all()
  347. form = forms.SiteForm
  348. @register_model_view(Site, 'delete')
  349. class SiteDeleteView(generic.ObjectDeleteView):
  350. queryset = Site.objects.all()
  351. class SiteBulkImportView(generic.BulkImportView):
  352. queryset = Site.objects.all()
  353. model_form = forms.SiteCSVForm
  354. table = tables.SiteTable
  355. class SiteBulkEditView(generic.BulkEditView):
  356. queryset = Site.objects.all()
  357. filterset = filtersets.SiteFilterSet
  358. table = tables.SiteTable
  359. form = forms.SiteBulkEditForm
  360. class SiteBulkDeleteView(generic.BulkDeleteView):
  361. queryset = Site.objects.all()
  362. filterset = filtersets.SiteFilterSet
  363. table = tables.SiteTable
  364. #
  365. # Locations
  366. #
  367. class LocationListView(generic.ObjectListView):
  368. queryset = Location.objects.add_related_count(
  369. Location.objects.add_related_count(
  370. Location.objects.all(),
  371. Device,
  372. 'location',
  373. 'device_count',
  374. cumulative=True
  375. ),
  376. Rack,
  377. 'location',
  378. 'rack_count',
  379. cumulative=True
  380. )
  381. filterset = filtersets.LocationFilterSet
  382. filterset_form = forms.LocationFilterForm
  383. table = tables.LocationTable
  384. @register_model_view(Location)
  385. class LocationView(generic.ObjectView):
  386. queryset = Location.objects.all()
  387. def get_extra_context(self, request, instance):
  388. location_ids = instance.get_descendants(include_self=True).values_list('pk', flat=True)
  389. rack_count = Rack.objects.filter(location__in=location_ids).count()
  390. device_count = Device.objects.filter(location__in=location_ids).count()
  391. child_locations = Location.objects.add_related_count(
  392. Location.objects.add_related_count(
  393. Location.objects.all(),
  394. Device,
  395. 'location',
  396. 'device_count',
  397. cumulative=True
  398. ),
  399. Rack,
  400. 'location',
  401. 'rack_count',
  402. cumulative=True
  403. ).filter(pk__in=location_ids).exclude(pk=instance.pk)
  404. child_locations_table = tables.LocationTable(child_locations, user=request.user)
  405. child_locations_table.configure(request)
  406. nonracked_devices = Device.objects.filter(
  407. location=instance,
  408. rack__isnull=True,
  409. parent_bay__isnull=True
  410. ).prefetch_related('device_type__manufacturer', 'parent_bay', 'device_role')
  411. return {
  412. 'rack_count': rack_count,
  413. 'device_count': device_count,
  414. 'child_locations_table': child_locations_table,
  415. 'nonracked_devices': nonracked_devices.order_by('-pk')[:10],
  416. 'total_nonracked_devices_count': nonracked_devices.count(),
  417. }
  418. @register_model_view(Location, 'edit')
  419. class LocationEditView(generic.ObjectEditView):
  420. queryset = Location.objects.all()
  421. form = forms.LocationForm
  422. @register_model_view(Location, 'delete')
  423. class LocationDeleteView(generic.ObjectDeleteView):
  424. queryset = Location.objects.all()
  425. class LocationBulkImportView(generic.BulkImportView):
  426. queryset = Location.objects.all()
  427. model_form = forms.LocationCSVForm
  428. table = tables.LocationTable
  429. class LocationBulkEditView(generic.BulkEditView):
  430. queryset = Location.objects.add_related_count(
  431. Location.objects.all(),
  432. Rack,
  433. 'location',
  434. 'rack_count',
  435. cumulative=True
  436. ).prefetch_related('site')
  437. filterset = filtersets.LocationFilterSet
  438. table = tables.LocationTable
  439. form = forms.LocationBulkEditForm
  440. class LocationBulkDeleteView(generic.BulkDeleteView):
  441. queryset = Location.objects.add_related_count(
  442. Location.objects.all(),
  443. Rack,
  444. 'location',
  445. 'rack_count',
  446. cumulative=True
  447. ).prefetch_related('site')
  448. filterset = filtersets.LocationFilterSet
  449. table = tables.LocationTable
  450. #
  451. # Rack roles
  452. #
  453. class RackRoleListView(generic.ObjectListView):
  454. queryset = RackRole.objects.annotate(
  455. rack_count=count_related(Rack, 'role')
  456. )
  457. filterset = filtersets.RackRoleFilterSet
  458. filterset_form = forms.RackRoleFilterForm
  459. table = tables.RackRoleTable
  460. @register_model_view(RackRole)
  461. class RackRoleView(generic.ObjectView):
  462. queryset = RackRole.objects.all()
  463. def get_extra_context(self, request, instance):
  464. racks = Rack.objects.restrict(request.user, 'view').filter(role=instance).annotate(
  465. device_count=count_related(Device, 'rack')
  466. )
  467. racks_table = tables.RackTable(racks, user=request.user, exclude=(
  468. 'role', 'get_utilization', 'get_power_utilization',
  469. ))
  470. racks_table.configure(request)
  471. return {
  472. 'racks_table': racks_table,
  473. }
  474. @register_model_view(RackRole, 'edit')
  475. class RackRoleEditView(generic.ObjectEditView):
  476. queryset = RackRole.objects.all()
  477. form = forms.RackRoleForm
  478. @register_model_view(RackRole, 'delete')
  479. class RackRoleDeleteView(generic.ObjectDeleteView):
  480. queryset = RackRole.objects.all()
  481. class RackRoleBulkImportView(generic.BulkImportView):
  482. queryset = RackRole.objects.all()
  483. model_form = forms.RackRoleCSVForm
  484. table = tables.RackRoleTable
  485. class RackRoleBulkEditView(generic.BulkEditView):
  486. queryset = RackRole.objects.annotate(
  487. rack_count=count_related(Rack, 'role')
  488. )
  489. filterset = filtersets.RackRoleFilterSet
  490. table = tables.RackRoleTable
  491. form = forms.RackRoleBulkEditForm
  492. class RackRoleBulkDeleteView(generic.BulkDeleteView):
  493. queryset = RackRole.objects.annotate(
  494. rack_count=count_related(Rack, 'role')
  495. )
  496. table = tables.RackRoleTable
  497. #
  498. # Racks
  499. #
  500. class RackListView(generic.ObjectListView):
  501. queryset = Rack.objects.annotate(
  502. device_count=count_related(Device, 'rack')
  503. )
  504. filterset = filtersets.RackFilterSet
  505. filterset_form = forms.RackFilterForm
  506. table = tables.RackTable
  507. class RackElevationListView(generic.ObjectListView):
  508. """
  509. Display a set of rack elevations side-by-side.
  510. """
  511. queryset = Rack.objects.prefetch_related('role')
  512. def get(self, request):
  513. racks = filtersets.RackFilterSet(request.GET, self.queryset).qs
  514. total_count = racks.count()
  515. ORDERING_CHOICES = {
  516. 'name': 'Name (A-Z)',
  517. '-name': 'Name (Z-A)',
  518. 'facility_id': 'Facility ID (A-Z)',
  519. '-facility_id': 'Facility ID (Z-A)',
  520. }
  521. sort = request.GET.get('sort', "name")
  522. if sort not in ORDERING_CHOICES:
  523. sort = 'name'
  524. racks = racks.order_by(sort)
  525. # Pagination
  526. per_page = get_paginate_count(request)
  527. page_number = request.GET.get('page', 1)
  528. paginator = EnhancedPaginator(racks, per_page)
  529. try:
  530. page = paginator.page(page_number)
  531. except PageNotAnInteger:
  532. page = paginator.page(1)
  533. except EmptyPage:
  534. page = paginator.page(paginator.num_pages)
  535. # Determine rack face
  536. rack_face = request.GET.get('face', DeviceFaceChoices.FACE_FRONT)
  537. if rack_face not in DeviceFaceChoices.values():
  538. rack_face = DeviceFaceChoices.FACE_FRONT
  539. return render(request, 'dcim/rack_elevation_list.html', {
  540. 'paginator': paginator,
  541. 'page': page,
  542. 'total_count': total_count,
  543. 'sort': sort,
  544. 'sort_display_name': ORDERING_CHOICES[sort],
  545. 'sort_choices': ORDERING_CHOICES,
  546. 'rack_face': rack_face,
  547. 'filter_form': forms.RackElevationFilterForm(request.GET),
  548. })
  549. @register_model_view(Rack)
  550. class RackView(generic.ObjectView):
  551. queryset = Rack.objects.prefetch_related('site__region', 'tenant__group', 'location', 'role')
  552. def get_extra_context(self, request, instance):
  553. # Get 0U devices located within the rack
  554. nonracked_devices = Device.objects.filter(
  555. rack=instance,
  556. position__isnull=True,
  557. parent_bay__isnull=True
  558. ).prefetch_related('device_type__manufacturer', 'parent_bay', 'device_role')
  559. peer_racks = Rack.objects.restrict(request.user, 'view').filter(site=instance.site)
  560. if instance.location:
  561. peer_racks = peer_racks.filter(location=instance.location)
  562. else:
  563. peer_racks = peer_racks.filter(location__isnull=True)
  564. next_rack = peer_racks.filter(_name__gt=instance._name).first()
  565. prev_rack = peer_racks.filter(_name__lt=instance._name).reverse().first()
  566. reservations = RackReservation.objects.restrict(request.user, 'view').filter(rack=instance)
  567. power_feeds = PowerFeed.objects.restrict(request.user, 'view').filter(rack=instance).prefetch_related(
  568. 'power_panel'
  569. )
  570. device_count = Device.objects.restrict(request.user, 'view').filter(rack=instance).count()
  571. # Determine any additional parameters to pass when embedding the rack elevations
  572. svg_extra = '&'.join([
  573. f'highlight=id:{pk}' for pk in request.GET.getlist('device')
  574. ])
  575. return {
  576. 'device_count': device_count,
  577. 'reservations': reservations,
  578. 'power_feeds': power_feeds,
  579. 'nonracked_devices': nonracked_devices,
  580. 'next_rack': next_rack,
  581. 'prev_rack': prev_rack,
  582. 'svg_extra': svg_extra,
  583. }
  584. @register_model_view(Rack, 'edit')
  585. class RackEditView(generic.ObjectEditView):
  586. queryset = Rack.objects.all()
  587. form = forms.RackForm
  588. template_name = 'dcim/rack_edit.html'
  589. @register_model_view(Rack, 'delete')
  590. class RackDeleteView(generic.ObjectDeleteView):
  591. queryset = Rack.objects.all()
  592. class RackBulkImportView(generic.BulkImportView):
  593. queryset = Rack.objects.all()
  594. model_form = forms.RackCSVForm
  595. table = tables.RackTable
  596. class RackBulkEditView(generic.BulkEditView):
  597. queryset = Rack.objects.all()
  598. filterset = filtersets.RackFilterSet
  599. table = tables.RackTable
  600. form = forms.RackBulkEditForm
  601. class RackBulkDeleteView(generic.BulkDeleteView):
  602. queryset = Rack.objects.all()
  603. filterset = filtersets.RackFilterSet
  604. table = tables.RackTable
  605. #
  606. # Rack reservations
  607. #
  608. class RackReservationListView(generic.ObjectListView):
  609. queryset = RackReservation.objects.all()
  610. filterset = filtersets.RackReservationFilterSet
  611. filterset_form = forms.RackReservationFilterForm
  612. table = tables.RackReservationTable
  613. @register_model_view(RackReservation)
  614. class RackReservationView(generic.ObjectView):
  615. queryset = RackReservation.objects.all()
  616. @register_model_view(RackReservation, 'edit')
  617. class RackReservationEditView(generic.ObjectEditView):
  618. queryset = RackReservation.objects.all()
  619. form = forms.RackReservationForm
  620. def alter_object(self, obj, request, args, kwargs):
  621. if not obj.pk:
  622. if 'rack' in request.GET:
  623. obj.rack = get_object_or_404(Rack, pk=request.GET.get('rack'))
  624. obj.user = request.user
  625. return obj
  626. @register_model_view(RackReservation, 'delete')
  627. class RackReservationDeleteView(generic.ObjectDeleteView):
  628. queryset = RackReservation.objects.all()
  629. class RackReservationImportView(generic.BulkImportView):
  630. queryset = RackReservation.objects.all()
  631. model_form = forms.RackReservationCSVForm
  632. table = tables.RackReservationTable
  633. def save_object(self, obj_form, request):
  634. """
  635. Assign the currently authenticated user to the RackReservation.
  636. """
  637. instance = obj_form.save(commit=False)
  638. instance.user = request.user
  639. instance.save()
  640. return instance
  641. class RackReservationBulkEditView(generic.BulkEditView):
  642. queryset = RackReservation.objects.all()
  643. filterset = filtersets.RackReservationFilterSet
  644. table = tables.RackReservationTable
  645. form = forms.RackReservationBulkEditForm
  646. class RackReservationBulkDeleteView(generic.BulkDeleteView):
  647. queryset = RackReservation.objects.all()
  648. filterset = filtersets.RackReservationFilterSet
  649. table = tables.RackReservationTable
  650. #
  651. # Manufacturers
  652. #
  653. class ManufacturerListView(generic.ObjectListView):
  654. queryset = Manufacturer.objects.annotate(
  655. devicetype_count=count_related(DeviceType, 'manufacturer'),
  656. inventoryitem_count=count_related(InventoryItem, 'manufacturer'),
  657. platform_count=count_related(Platform, 'manufacturer')
  658. )
  659. filterset = filtersets.ManufacturerFilterSet
  660. filterset_form = forms.ManufacturerFilterForm
  661. table = tables.ManufacturerTable
  662. @register_model_view(Manufacturer)
  663. class ManufacturerView(generic.ObjectView):
  664. queryset = Manufacturer.objects.all()
  665. def get_extra_context(self, request, instance):
  666. device_types = DeviceType.objects.restrict(request.user, 'view').filter(
  667. manufacturer=instance
  668. ).annotate(
  669. instance_count=count_related(Device, 'device_type')
  670. )
  671. module_types = ModuleType.objects.restrict(request.user, 'view').filter(
  672. manufacturer=instance
  673. )
  674. inventory_items = InventoryItem.objects.restrict(request.user, 'view').filter(
  675. manufacturer=instance
  676. )
  677. devicetypes_table = tables.DeviceTypeTable(device_types, user=request.user, exclude=('manufacturer',))
  678. devicetypes_table.configure(request)
  679. return {
  680. 'devicetypes_table': devicetypes_table,
  681. 'inventory_item_count': inventory_items.count(),
  682. 'module_type_count': module_types.count(),
  683. }
  684. @register_model_view(Manufacturer, 'edit')
  685. class ManufacturerEditView(generic.ObjectEditView):
  686. queryset = Manufacturer.objects.all()
  687. form = forms.ManufacturerForm
  688. @register_model_view(Manufacturer, 'delete')
  689. class ManufacturerDeleteView(generic.ObjectDeleteView):
  690. queryset = Manufacturer.objects.all()
  691. class ManufacturerBulkImportView(generic.BulkImportView):
  692. queryset = Manufacturer.objects.all()
  693. model_form = forms.ManufacturerCSVForm
  694. table = tables.ManufacturerTable
  695. class ManufacturerBulkEditView(generic.BulkEditView):
  696. queryset = Manufacturer.objects.annotate(
  697. devicetype_count=count_related(DeviceType, 'manufacturer')
  698. )
  699. filterset = filtersets.ManufacturerFilterSet
  700. table = tables.ManufacturerTable
  701. form = forms.ManufacturerBulkEditForm
  702. class ManufacturerBulkDeleteView(generic.BulkDeleteView):
  703. queryset = Manufacturer.objects.annotate(
  704. devicetype_count=count_related(DeviceType, 'manufacturer')
  705. )
  706. table = tables.ManufacturerTable
  707. #
  708. # Device types
  709. #
  710. class DeviceTypeListView(generic.ObjectListView):
  711. queryset = DeviceType.objects.annotate(
  712. instance_count=count_related(Device, 'device_type')
  713. )
  714. filterset = filtersets.DeviceTypeFilterSet
  715. filterset_form = forms.DeviceTypeFilterForm
  716. table = tables.DeviceTypeTable
  717. @register_model_view(DeviceType)
  718. class DeviceTypeView(generic.ObjectView):
  719. queryset = DeviceType.objects.all()
  720. def get_extra_context(self, request, instance):
  721. instance_count = Device.objects.restrict(request.user).filter(device_type=instance).count()
  722. return {
  723. 'instance_count': instance_count,
  724. }
  725. @register_model_view(DeviceType, 'edit')
  726. class DeviceTypeEditView(generic.ObjectEditView):
  727. queryset = DeviceType.objects.all()
  728. form = forms.DeviceTypeForm
  729. @register_model_view(DeviceType, 'delete')
  730. class DeviceTypeDeleteView(generic.ObjectDeleteView):
  731. queryset = DeviceType.objects.all()
  732. @register_model_view(DeviceType, 'consoleports', path='console-ports')
  733. class DeviceTypeConsolePortsView(DeviceTypeComponentsView):
  734. child_model = ConsolePortTemplate
  735. table = tables.ConsolePortTemplateTable
  736. filterset = filtersets.ConsolePortTemplateFilterSet
  737. viewname = 'dcim:devicetype_consoleports'
  738. tab = ViewTab(
  739. label=_('Console Ports'),
  740. badge=lambda obj: obj.consoleporttemplates.count(),
  741. permission='dcim.view_consoleporttemplate',
  742. hide_if_empty=True
  743. )
  744. @register_model_view(DeviceType, 'consoleserverports', path='console-server-ports')
  745. class DeviceTypeConsoleServerPortsView(DeviceTypeComponentsView):
  746. child_model = ConsoleServerPortTemplate
  747. table = tables.ConsoleServerPortTemplateTable
  748. filterset = filtersets.ConsoleServerPortTemplateFilterSet
  749. viewname = 'dcim:devicetype_consoleserverports'
  750. tab = ViewTab(
  751. label=_('Console Server Ports'),
  752. badge=lambda obj: obj.consoleserverporttemplates.count(),
  753. permission='dcim.view_consoleserverporttemplate',
  754. hide_if_empty=True
  755. )
  756. @register_model_view(DeviceType, 'powerports', path='power-ports')
  757. class DeviceTypePowerPortsView(DeviceTypeComponentsView):
  758. child_model = PowerPortTemplate
  759. table = tables.PowerPortTemplateTable
  760. filterset = filtersets.PowerPortTemplateFilterSet
  761. viewname = 'dcim:devicetype_powerports'
  762. tab = ViewTab(
  763. label=_('Power Ports'),
  764. badge=lambda obj: obj.powerporttemplates.count(),
  765. permission='dcim.view_powerporttemplate',
  766. hide_if_empty=True
  767. )
  768. @register_model_view(DeviceType, 'poweroutlets', path='power-outlets')
  769. class DeviceTypePowerOutletsView(DeviceTypeComponentsView):
  770. child_model = PowerOutletTemplate
  771. table = tables.PowerOutletTemplateTable
  772. filterset = filtersets.PowerOutletTemplateFilterSet
  773. viewname = 'dcim:devicetype_poweroutlets'
  774. tab = ViewTab(
  775. label=_('Power Outlets'),
  776. badge=lambda obj: obj.poweroutlettemplates.count(),
  777. permission='dcim.view_poweroutlettemplate',
  778. hide_if_empty=True
  779. )
  780. @register_model_view(DeviceType, 'interfaces')
  781. class DeviceTypeInterfacesView(DeviceTypeComponentsView):
  782. child_model = InterfaceTemplate
  783. table = tables.InterfaceTemplateTable
  784. filterset = filtersets.InterfaceTemplateFilterSet
  785. viewname = 'dcim:devicetype_interfaces'
  786. tab = ViewTab(
  787. label=_('Interfaces'),
  788. badge=lambda obj: obj.interfacetemplates.count(),
  789. permission='dcim.view_interfacetemplate',
  790. hide_if_empty=True
  791. )
  792. @register_model_view(DeviceType, 'frontports', path='front-ports')
  793. class DeviceTypeFrontPortsView(DeviceTypeComponentsView):
  794. child_model = FrontPortTemplate
  795. table = tables.FrontPortTemplateTable
  796. filterset = filtersets.FrontPortTemplateFilterSet
  797. viewname = 'dcim:devicetype_frontports'
  798. tab = ViewTab(
  799. label=_('Front Ports'),
  800. badge=lambda obj: obj.frontporttemplates.count(),
  801. permission='dcim.view_frontporttemplate',
  802. hide_if_empty=True
  803. )
  804. @register_model_view(DeviceType, 'rearports', path='rear-ports')
  805. class DeviceTypeRearPortsView(DeviceTypeComponentsView):
  806. child_model = RearPortTemplate
  807. table = tables.RearPortTemplateTable
  808. filterset = filtersets.RearPortTemplateFilterSet
  809. viewname = 'dcim:devicetype_rearports'
  810. tab = ViewTab(
  811. label=_('Rear Ports'),
  812. badge=lambda obj: obj.rearporttemplates.count(),
  813. permission='dcim.view_rearporttemplate',
  814. hide_if_empty=True
  815. )
  816. @register_model_view(DeviceType, 'modulebays', path='module-bays')
  817. class DeviceTypeModuleBaysView(DeviceTypeComponentsView):
  818. child_model = ModuleBayTemplate
  819. table = tables.ModuleBayTemplateTable
  820. filterset = filtersets.ModuleBayTemplateFilterSet
  821. viewname = 'dcim:devicetype_modulebays'
  822. tab = ViewTab(
  823. label=_('Module Bays'),
  824. badge=lambda obj: obj.modulebaytemplates.count(),
  825. permission='dcim.view_modulebaytemplate',
  826. hide_if_empty=True
  827. )
  828. @register_model_view(DeviceType, 'devicebays', path='device-bays')
  829. class DeviceTypeDeviceBaysView(DeviceTypeComponentsView):
  830. child_model = DeviceBayTemplate
  831. table = tables.DeviceBayTemplateTable
  832. filterset = filtersets.DeviceBayTemplateFilterSet
  833. viewname = 'dcim:devicetype_devicebays'
  834. tab = ViewTab(
  835. label=_('Device Bays'),
  836. badge=lambda obj: obj.devicebaytemplates.count(),
  837. permission='dcim.view_devicebaytemplate',
  838. hide_if_empty=True
  839. )
  840. @register_model_view(DeviceType, 'inventoryitems', path='inventory-items')
  841. class DeviceTypeInventoryItemsView(DeviceTypeComponentsView):
  842. child_model = InventoryItemTemplate
  843. table = tables.InventoryItemTemplateTable
  844. filterset = filtersets.InventoryItemTemplateFilterSet
  845. viewname = 'dcim:devicetype_inventoryitems'
  846. tab = ViewTab(
  847. label=_('Inventory Items'),
  848. badge=lambda obj: obj.inventoryitemtemplates.count(),
  849. permission='dcim.view_invenotryitemtemplate',
  850. hide_if_empty=True
  851. )
  852. class DeviceTypeImportView(generic.BulkImportView):
  853. additional_permissions = [
  854. 'dcim.add_devicetype',
  855. 'dcim.add_consoleporttemplate',
  856. 'dcim.add_consoleserverporttemplate',
  857. 'dcim.add_powerporttemplate',
  858. 'dcim.add_poweroutlettemplate',
  859. 'dcim.add_interfacetemplate',
  860. 'dcim.add_frontporttemplate',
  861. 'dcim.add_rearporttemplate',
  862. 'dcim.add_modulebaytemplate',
  863. 'dcim.add_devicebaytemplate',
  864. 'dcim.add_inventoryitemtemplate',
  865. ]
  866. queryset = DeviceType.objects.all()
  867. model_form = forms.DeviceTypeImportForm
  868. table = tables.DeviceTypeTable
  869. related_object_forms = {
  870. 'console-ports': forms.ConsolePortTemplateImportForm,
  871. 'console-server-ports': forms.ConsoleServerPortTemplateImportForm,
  872. 'power-ports': forms.PowerPortTemplateImportForm,
  873. 'power-outlets': forms.PowerOutletTemplateImportForm,
  874. 'interfaces': forms.InterfaceTemplateImportForm,
  875. 'rear-ports': forms.RearPortTemplateImportForm,
  876. 'front-ports': forms.FrontPortTemplateImportForm,
  877. 'module-bays': forms.ModuleBayTemplateImportForm,
  878. 'device-bays': forms.DeviceBayTemplateImportForm,
  879. 'inventory-items': forms.InventoryItemTemplateImportForm,
  880. }
  881. def prep_related_object_data(self, parent, data):
  882. data.update({'device_type': parent})
  883. return data
  884. class DeviceTypeBulkEditView(generic.BulkEditView):
  885. queryset = DeviceType.objects.annotate(
  886. instance_count=count_related(Device, 'device_type')
  887. )
  888. filterset = filtersets.DeviceTypeFilterSet
  889. table = tables.DeviceTypeTable
  890. form = forms.DeviceTypeBulkEditForm
  891. class DeviceTypeBulkDeleteView(generic.BulkDeleteView):
  892. queryset = DeviceType.objects.annotate(
  893. instance_count=count_related(Device, 'device_type')
  894. )
  895. filterset = filtersets.DeviceTypeFilterSet
  896. table = tables.DeviceTypeTable
  897. #
  898. # Module types
  899. #
  900. class ModuleTypeListView(generic.ObjectListView):
  901. queryset = ModuleType.objects.annotate(
  902. instance_count=count_related(Module, 'module_type')
  903. )
  904. filterset = filtersets.ModuleTypeFilterSet
  905. filterset_form = forms.ModuleTypeFilterForm
  906. table = tables.ModuleTypeTable
  907. @register_model_view(ModuleType)
  908. class ModuleTypeView(generic.ObjectView):
  909. queryset = ModuleType.objects.all()
  910. def get_extra_context(self, request, instance):
  911. instance_count = Module.objects.restrict(request.user).filter(module_type=instance).count()
  912. return {
  913. 'instance_count': instance_count,
  914. }
  915. @register_model_view(ModuleType, 'edit')
  916. class ModuleTypeEditView(generic.ObjectEditView):
  917. queryset = ModuleType.objects.all()
  918. form = forms.ModuleTypeForm
  919. @register_model_view(ModuleType, 'delete')
  920. class ModuleTypeDeleteView(generic.ObjectDeleteView):
  921. queryset = ModuleType.objects.all()
  922. @register_model_view(ModuleType, 'consoleports', path='console-ports')
  923. class ModuleTypeConsolePortsView(ModuleTypeComponentsView):
  924. child_model = ConsolePortTemplate
  925. table = tables.ConsolePortTemplateTable
  926. filterset = filtersets.ConsolePortTemplateFilterSet
  927. viewname = 'dcim:moduletype_consoleports'
  928. tab = ViewTab(
  929. label=_('Console Ports'),
  930. badge=lambda obj: obj.consoleporttemplates.count(),
  931. permission='dcim.view_consoleporttemplate',
  932. hide_if_empty=True
  933. )
  934. @register_model_view(ModuleType, 'consoleserverports', path='console-server-ports')
  935. class ModuleTypeConsoleServerPortsView(ModuleTypeComponentsView):
  936. child_model = ConsoleServerPortTemplate
  937. table = tables.ConsoleServerPortTemplateTable
  938. filterset = filtersets.ConsoleServerPortTemplateFilterSet
  939. viewname = 'dcim:moduletype_consoleserverports'
  940. tab = ViewTab(
  941. label=_('Console Server Ports'),
  942. badge=lambda obj: obj.consoleserverporttemplates.count(),
  943. permission='dcim.view_consoleserverporttemplate',
  944. hide_if_empty=True
  945. )
  946. @register_model_view(ModuleType, 'powerports', path='power-ports')
  947. class ModuleTypePowerPortsView(ModuleTypeComponentsView):
  948. child_model = PowerPortTemplate
  949. table = tables.PowerPortTemplateTable
  950. filterset = filtersets.PowerPortTemplateFilterSet
  951. viewname = 'dcim:moduletype_powerports'
  952. tab = ViewTab(
  953. label=_('Power Ports'),
  954. badge=lambda obj: obj.powerporttemplates.count(),
  955. permission='dcim.view_powerporttemplate',
  956. hide_if_empty=True
  957. )
  958. @register_model_view(ModuleType, 'poweroutlets', path='power-outlets')
  959. class ModuleTypePowerOutletsView(ModuleTypeComponentsView):
  960. child_model = PowerOutletTemplate
  961. table = tables.PowerOutletTemplateTable
  962. filterset = filtersets.PowerOutletTemplateFilterSet
  963. viewname = 'dcim:moduletype_poweroutlets'
  964. tab = ViewTab(
  965. label=_('Power Outlets'),
  966. badge=lambda obj: obj.poweroutlettemplates.count(),
  967. permission='dcim.view_poweroutlettemplate',
  968. hide_if_empty=True
  969. )
  970. @register_model_view(ModuleType, 'interfaces')
  971. class ModuleTypeInterfacesView(ModuleTypeComponentsView):
  972. child_model = InterfaceTemplate
  973. table = tables.InterfaceTemplateTable
  974. filterset = filtersets.InterfaceTemplateFilterSet
  975. viewname = 'dcim:moduletype_interfaces'
  976. tab = ViewTab(
  977. label=_('Interfaces'),
  978. badge=lambda obj: obj.interfacetemplates.count(),
  979. permission='dcim.view_interfacetemplate',
  980. hide_if_empty=True
  981. )
  982. @register_model_view(ModuleType, 'frontports', path='front-ports')
  983. class ModuleTypeFrontPortsView(ModuleTypeComponentsView):
  984. child_model = FrontPortTemplate
  985. table = tables.FrontPortTemplateTable
  986. filterset = filtersets.FrontPortTemplateFilterSet
  987. viewname = 'dcim:moduletype_frontports'
  988. tab = ViewTab(
  989. label=_('Front Ports'),
  990. badge=lambda obj: obj.frontporttemplates.count(),
  991. permission='dcim.view_frontporttemplate',
  992. hide_if_empty=True
  993. )
  994. @register_model_view(ModuleType, 'rearports', path='rear-ports')
  995. class ModuleTypeRearPortsView(ModuleTypeComponentsView):
  996. child_model = RearPortTemplate
  997. table = tables.RearPortTemplateTable
  998. filterset = filtersets.RearPortTemplateFilterSet
  999. viewname = 'dcim:moduletype_rearports'
  1000. tab = ViewTab(
  1001. label=_('Rear Ports'),
  1002. badge=lambda obj: obj.rearporttemplates.count(),
  1003. permission='dcim.view_rearporttemplate',
  1004. hide_if_empty=True
  1005. )
  1006. class ModuleTypeImportView(generic.BulkImportView):
  1007. additional_permissions = [
  1008. 'dcim.add_moduletype',
  1009. 'dcim.add_consoleporttemplate',
  1010. 'dcim.add_consoleserverporttemplate',
  1011. 'dcim.add_powerporttemplate',
  1012. 'dcim.add_poweroutlettemplate',
  1013. 'dcim.add_interfacetemplate',
  1014. 'dcim.add_frontporttemplate',
  1015. 'dcim.add_rearporttemplate',
  1016. ]
  1017. queryset = ModuleType.objects.all()
  1018. model_form = forms.ModuleTypeImportForm
  1019. table = tables.ModuleTypeTable
  1020. related_object_forms = {
  1021. 'console-ports': forms.ConsolePortTemplateImportForm,
  1022. 'console-server-ports': forms.ConsoleServerPortTemplateImportForm,
  1023. 'power-ports': forms.PowerPortTemplateImportForm,
  1024. 'power-outlets': forms.PowerOutletTemplateImportForm,
  1025. 'interfaces': forms.InterfaceTemplateImportForm,
  1026. 'rear-ports': forms.RearPortTemplateImportForm,
  1027. 'front-ports': forms.FrontPortTemplateImportForm,
  1028. }
  1029. def prep_related_object_data(self, parent, data):
  1030. data.update({'module_type': parent})
  1031. return data
  1032. class ModuleTypeBulkEditView(generic.BulkEditView):
  1033. queryset = ModuleType.objects.annotate(
  1034. instance_count=count_related(Module, 'module_type')
  1035. )
  1036. filterset = filtersets.ModuleTypeFilterSet
  1037. table = tables.ModuleTypeTable
  1038. form = forms.ModuleTypeBulkEditForm
  1039. class ModuleTypeBulkDeleteView(generic.BulkDeleteView):
  1040. queryset = ModuleType.objects.annotate(
  1041. instance_count=count_related(Module, 'module_type')
  1042. )
  1043. filterset = filtersets.ModuleTypeFilterSet
  1044. table = tables.ModuleTypeTable
  1045. #
  1046. # Console port templates
  1047. #
  1048. class ConsolePortTemplateCreateView(generic.ComponentCreateView):
  1049. queryset = ConsolePortTemplate.objects.all()
  1050. form = forms.ConsolePortTemplateCreateForm
  1051. model_form = forms.ConsolePortTemplateForm
  1052. @register_model_view(ConsolePortTemplate, 'edit')
  1053. class ConsolePortTemplateEditView(generic.ObjectEditView):
  1054. queryset = ConsolePortTemplate.objects.all()
  1055. form = forms.ConsolePortTemplateForm
  1056. @register_model_view(ConsolePortTemplate, 'delete')
  1057. class ConsolePortTemplateDeleteView(generic.ObjectDeleteView):
  1058. queryset = ConsolePortTemplate.objects.all()
  1059. class ConsolePortTemplateBulkEditView(generic.BulkEditView):
  1060. queryset = ConsolePortTemplate.objects.all()
  1061. table = tables.ConsolePortTemplateTable
  1062. form = forms.ConsolePortTemplateBulkEditForm
  1063. class ConsolePortTemplateBulkRenameView(generic.BulkRenameView):
  1064. queryset = ConsolePortTemplate.objects.all()
  1065. class ConsolePortTemplateBulkDeleteView(generic.BulkDeleteView):
  1066. queryset = ConsolePortTemplate.objects.all()
  1067. table = tables.ConsolePortTemplateTable
  1068. #
  1069. # Console server port templates
  1070. #
  1071. class ConsoleServerPortTemplateCreateView(generic.ComponentCreateView):
  1072. queryset = ConsoleServerPortTemplate.objects.all()
  1073. form = forms.ConsoleServerPortTemplateCreateForm
  1074. model_form = forms.ConsoleServerPortTemplateForm
  1075. @register_model_view(ConsoleServerPortTemplate, 'edit')
  1076. class ConsoleServerPortTemplateEditView(generic.ObjectEditView):
  1077. queryset = ConsoleServerPortTemplate.objects.all()
  1078. form = forms.ConsoleServerPortTemplateForm
  1079. @register_model_view(ConsoleServerPortTemplate, 'delete')
  1080. class ConsoleServerPortTemplateDeleteView(generic.ObjectDeleteView):
  1081. queryset = ConsoleServerPortTemplate.objects.all()
  1082. class ConsoleServerPortTemplateBulkEditView(generic.BulkEditView):
  1083. queryset = ConsoleServerPortTemplate.objects.all()
  1084. table = tables.ConsoleServerPortTemplateTable
  1085. form = forms.ConsoleServerPortTemplateBulkEditForm
  1086. class ConsoleServerPortTemplateBulkRenameView(generic.BulkRenameView):
  1087. queryset = ConsoleServerPortTemplate.objects.all()
  1088. class ConsoleServerPortTemplateBulkDeleteView(generic.BulkDeleteView):
  1089. queryset = ConsoleServerPortTemplate.objects.all()
  1090. table = tables.ConsoleServerPortTemplateTable
  1091. #
  1092. # Power port templates
  1093. #
  1094. class PowerPortTemplateCreateView(generic.ComponentCreateView):
  1095. queryset = PowerPortTemplate.objects.all()
  1096. form = forms.PowerPortTemplateCreateForm
  1097. model_form = forms.PowerPortTemplateForm
  1098. @register_model_view(PowerPortTemplate, 'edit')
  1099. class PowerPortTemplateEditView(generic.ObjectEditView):
  1100. queryset = PowerPortTemplate.objects.all()
  1101. form = forms.PowerPortTemplateForm
  1102. @register_model_view(PowerPortTemplate, 'delete')
  1103. class PowerPortTemplateDeleteView(generic.ObjectDeleteView):
  1104. queryset = PowerPortTemplate.objects.all()
  1105. class PowerPortTemplateBulkEditView(generic.BulkEditView):
  1106. queryset = PowerPortTemplate.objects.all()
  1107. table = tables.PowerPortTemplateTable
  1108. form = forms.PowerPortTemplateBulkEditForm
  1109. class PowerPortTemplateBulkRenameView(generic.BulkRenameView):
  1110. queryset = PowerPortTemplate.objects.all()
  1111. class PowerPortTemplateBulkDeleteView(generic.BulkDeleteView):
  1112. queryset = PowerPortTemplate.objects.all()
  1113. table = tables.PowerPortTemplateTable
  1114. #
  1115. # Power outlet templates
  1116. #
  1117. class PowerOutletTemplateCreateView(generic.ComponentCreateView):
  1118. queryset = PowerOutletTemplate.objects.all()
  1119. form = forms.PowerOutletTemplateCreateForm
  1120. model_form = forms.PowerOutletTemplateForm
  1121. @register_model_view(PowerOutletTemplate, 'edit')
  1122. class PowerOutletTemplateEditView(generic.ObjectEditView):
  1123. queryset = PowerOutletTemplate.objects.all()
  1124. form = forms.PowerOutletTemplateForm
  1125. @register_model_view(PowerOutletTemplate, 'delete')
  1126. class PowerOutletTemplateDeleteView(generic.ObjectDeleteView):
  1127. queryset = PowerOutletTemplate.objects.all()
  1128. class PowerOutletTemplateBulkEditView(generic.BulkEditView):
  1129. queryset = PowerOutletTemplate.objects.all()
  1130. table = tables.PowerOutletTemplateTable
  1131. form = forms.PowerOutletTemplateBulkEditForm
  1132. class PowerOutletTemplateBulkRenameView(generic.BulkRenameView):
  1133. queryset = PowerOutletTemplate.objects.all()
  1134. class PowerOutletTemplateBulkDeleteView(generic.BulkDeleteView):
  1135. queryset = PowerOutletTemplate.objects.all()
  1136. table = tables.PowerOutletTemplateTable
  1137. #
  1138. # Interface templates
  1139. #
  1140. class InterfaceTemplateCreateView(generic.ComponentCreateView):
  1141. queryset = InterfaceTemplate.objects.all()
  1142. form = forms.InterfaceTemplateCreateForm
  1143. model_form = forms.InterfaceTemplateForm
  1144. @register_model_view(InterfaceTemplate, 'edit')
  1145. class InterfaceTemplateEditView(generic.ObjectEditView):
  1146. queryset = InterfaceTemplate.objects.all()
  1147. form = forms.InterfaceTemplateForm
  1148. @register_model_view(InterfaceTemplate, 'delete')
  1149. class InterfaceTemplateDeleteView(generic.ObjectDeleteView):
  1150. queryset = InterfaceTemplate.objects.all()
  1151. class InterfaceTemplateBulkEditView(generic.BulkEditView):
  1152. queryset = InterfaceTemplate.objects.all()
  1153. table = tables.InterfaceTemplateTable
  1154. form = forms.InterfaceTemplateBulkEditForm
  1155. class InterfaceTemplateBulkRenameView(generic.BulkRenameView):
  1156. queryset = InterfaceTemplate.objects.all()
  1157. class InterfaceTemplateBulkDeleteView(generic.BulkDeleteView):
  1158. queryset = InterfaceTemplate.objects.all()
  1159. table = tables.InterfaceTemplateTable
  1160. #
  1161. # Front port templates
  1162. #
  1163. class FrontPortTemplateCreateView(generic.ComponentCreateView):
  1164. queryset = FrontPortTemplate.objects.all()
  1165. form = forms.FrontPortTemplateCreateForm
  1166. model_form = forms.FrontPortTemplateForm
  1167. @register_model_view(FrontPortTemplate, 'edit')
  1168. class FrontPortTemplateEditView(generic.ObjectEditView):
  1169. queryset = FrontPortTemplate.objects.all()
  1170. form = forms.FrontPortTemplateForm
  1171. @register_model_view(FrontPortTemplate, 'delete')
  1172. class FrontPortTemplateDeleteView(generic.ObjectDeleteView):
  1173. queryset = FrontPortTemplate.objects.all()
  1174. class FrontPortTemplateBulkEditView(generic.BulkEditView):
  1175. queryset = FrontPortTemplate.objects.all()
  1176. table = tables.FrontPortTemplateTable
  1177. form = forms.FrontPortTemplateBulkEditForm
  1178. class FrontPortTemplateBulkRenameView(generic.BulkRenameView):
  1179. queryset = FrontPortTemplate.objects.all()
  1180. class FrontPortTemplateBulkDeleteView(generic.BulkDeleteView):
  1181. queryset = FrontPortTemplate.objects.all()
  1182. table = tables.FrontPortTemplateTable
  1183. #
  1184. # Rear port templates
  1185. #
  1186. class RearPortTemplateCreateView(generic.ComponentCreateView):
  1187. queryset = RearPortTemplate.objects.all()
  1188. form = forms.RearPortTemplateCreateForm
  1189. model_form = forms.RearPortTemplateForm
  1190. @register_model_view(RearPortTemplate, 'edit')
  1191. class RearPortTemplateEditView(generic.ObjectEditView):
  1192. queryset = RearPortTemplate.objects.all()
  1193. form = forms.RearPortTemplateForm
  1194. @register_model_view(RearPortTemplate, 'delete')
  1195. class RearPortTemplateDeleteView(generic.ObjectDeleteView):
  1196. queryset = RearPortTemplate.objects.all()
  1197. class RearPortTemplateBulkEditView(generic.BulkEditView):
  1198. queryset = RearPortTemplate.objects.all()
  1199. table = tables.RearPortTemplateTable
  1200. form = forms.RearPortTemplateBulkEditForm
  1201. class RearPortTemplateBulkRenameView(generic.BulkRenameView):
  1202. queryset = RearPortTemplate.objects.all()
  1203. class RearPortTemplateBulkDeleteView(generic.BulkDeleteView):
  1204. queryset = RearPortTemplate.objects.all()
  1205. table = tables.RearPortTemplateTable
  1206. #
  1207. # Module bay templates
  1208. #
  1209. class ModuleBayTemplateCreateView(generic.ComponentCreateView):
  1210. queryset = ModuleBayTemplate.objects.all()
  1211. form = forms.ModuleBayTemplateCreateForm
  1212. model_form = forms.ModuleBayTemplateForm
  1213. @register_model_view(ModuleBayTemplate, 'edit')
  1214. class ModuleBayTemplateEditView(generic.ObjectEditView):
  1215. queryset = ModuleBayTemplate.objects.all()
  1216. form = forms.ModuleBayTemplateForm
  1217. @register_model_view(ModuleBayTemplate, 'delete')
  1218. class ModuleBayTemplateDeleteView(generic.ObjectDeleteView):
  1219. queryset = ModuleBayTemplate.objects.all()
  1220. class ModuleBayTemplateBulkEditView(generic.BulkEditView):
  1221. queryset = ModuleBayTemplate.objects.all()
  1222. table = tables.ModuleBayTemplateTable
  1223. form = forms.ModuleBayTemplateBulkEditForm
  1224. class ModuleBayTemplateBulkRenameView(generic.BulkRenameView):
  1225. queryset = ModuleBayTemplate.objects.all()
  1226. class ModuleBayTemplateBulkDeleteView(generic.BulkDeleteView):
  1227. queryset = ModuleBayTemplate.objects.all()
  1228. table = tables.ModuleBayTemplateTable
  1229. #
  1230. # Device bay templates
  1231. #
  1232. class DeviceBayTemplateCreateView(generic.ComponentCreateView):
  1233. queryset = DeviceBayTemplate.objects.all()
  1234. form = forms.DeviceBayTemplateCreateForm
  1235. model_form = forms.DeviceBayTemplateForm
  1236. @register_model_view(DeviceBayTemplate, 'edit')
  1237. class DeviceBayTemplateEditView(generic.ObjectEditView):
  1238. queryset = DeviceBayTemplate.objects.all()
  1239. form = forms.DeviceBayTemplateForm
  1240. @register_model_view(DeviceBayTemplate, 'delete')
  1241. class DeviceBayTemplateDeleteView(generic.ObjectDeleteView):
  1242. queryset = DeviceBayTemplate.objects.all()
  1243. class DeviceBayTemplateBulkEditView(generic.BulkEditView):
  1244. queryset = DeviceBayTemplate.objects.all()
  1245. table = tables.DeviceBayTemplateTable
  1246. form = forms.DeviceBayTemplateBulkEditForm
  1247. class DeviceBayTemplateBulkRenameView(generic.BulkRenameView):
  1248. queryset = DeviceBayTemplate.objects.all()
  1249. class DeviceBayTemplateBulkDeleteView(generic.BulkDeleteView):
  1250. queryset = DeviceBayTemplate.objects.all()
  1251. table = tables.DeviceBayTemplateTable
  1252. #
  1253. # Inventory item templates
  1254. #
  1255. class InventoryItemTemplateCreateView(generic.ComponentCreateView):
  1256. queryset = InventoryItemTemplate.objects.all()
  1257. form = forms.InventoryItemTemplateCreateForm
  1258. model_form = forms.InventoryItemTemplateForm
  1259. def alter_object(self, instance, request):
  1260. # Set component (if any)
  1261. component_type = request.GET.get('component_type')
  1262. component_id = request.GET.get('component_id')
  1263. if component_type and component_id:
  1264. content_type = get_object_or_404(ContentType, pk=component_type)
  1265. instance.component = get_object_or_404(content_type.model_class(), pk=component_id)
  1266. return instance
  1267. @register_model_view(InventoryItemTemplate, 'edit')
  1268. class InventoryItemTemplateEditView(generic.ObjectEditView):
  1269. queryset = InventoryItemTemplate.objects.all()
  1270. form = forms.InventoryItemTemplateForm
  1271. @register_model_view(InventoryItemTemplate, 'delete')
  1272. class InventoryItemTemplateDeleteView(generic.ObjectDeleteView):
  1273. queryset = InventoryItemTemplate.objects.all()
  1274. class InventoryItemTemplateBulkEditView(generic.BulkEditView):
  1275. queryset = InventoryItemTemplate.objects.all()
  1276. table = tables.InventoryItemTemplateTable
  1277. form = forms.InventoryItemTemplateBulkEditForm
  1278. class InventoryItemTemplateBulkRenameView(generic.BulkRenameView):
  1279. queryset = InventoryItemTemplate.objects.all()
  1280. class InventoryItemTemplateBulkDeleteView(generic.BulkDeleteView):
  1281. queryset = InventoryItemTemplate.objects.all()
  1282. table = tables.InventoryItemTemplateTable
  1283. #
  1284. # Device roles
  1285. #
  1286. class DeviceRoleListView(generic.ObjectListView):
  1287. queryset = DeviceRole.objects.annotate(
  1288. device_count=count_related(Device, 'device_role'),
  1289. vm_count=count_related(VirtualMachine, 'role')
  1290. )
  1291. filterset = filtersets.DeviceRoleFilterSet
  1292. filterset_form = forms.DeviceRoleFilterForm
  1293. table = tables.DeviceRoleTable
  1294. @register_model_view(DeviceRole)
  1295. class DeviceRoleView(generic.ObjectView):
  1296. queryset = DeviceRole.objects.all()
  1297. def get_extra_context(self, request, instance):
  1298. devices = Device.objects.restrict(request.user, 'view').filter(
  1299. device_role=instance
  1300. )
  1301. devices_table = tables.DeviceTable(devices, user=request.user, exclude=('device_role',))
  1302. devices_table.configure(request)
  1303. return {
  1304. 'devices_table': devices_table,
  1305. 'device_count': Device.objects.filter(device_role=instance).count(),
  1306. 'virtualmachine_count': VirtualMachine.objects.filter(role=instance).count(),
  1307. }
  1308. @register_model_view(DeviceRole, 'edit')
  1309. class DeviceRoleEditView(generic.ObjectEditView):
  1310. queryset = DeviceRole.objects.all()
  1311. form = forms.DeviceRoleForm
  1312. @register_model_view(DeviceRole, 'delete')
  1313. class DeviceRoleDeleteView(generic.ObjectDeleteView):
  1314. queryset = DeviceRole.objects.all()
  1315. class DeviceRoleBulkImportView(generic.BulkImportView):
  1316. queryset = DeviceRole.objects.all()
  1317. model_form = forms.DeviceRoleCSVForm
  1318. table = tables.DeviceRoleTable
  1319. class DeviceRoleBulkEditView(generic.BulkEditView):
  1320. queryset = DeviceRole.objects.annotate(
  1321. device_count=count_related(Device, 'device_role'),
  1322. vm_count=count_related(VirtualMachine, 'role')
  1323. )
  1324. filterset = filtersets.DeviceRoleFilterSet
  1325. table = tables.DeviceRoleTable
  1326. form = forms.DeviceRoleBulkEditForm
  1327. class DeviceRoleBulkDeleteView(generic.BulkDeleteView):
  1328. queryset = DeviceRole.objects.annotate(
  1329. device_count=count_related(Device, 'device_role'),
  1330. vm_count=count_related(VirtualMachine, 'role')
  1331. )
  1332. table = tables.DeviceRoleTable
  1333. #
  1334. # Platforms
  1335. #
  1336. class PlatformListView(generic.ObjectListView):
  1337. queryset = Platform.objects.annotate(
  1338. device_count=count_related(Device, 'platform'),
  1339. vm_count=count_related(VirtualMachine, 'platform')
  1340. )
  1341. table = tables.PlatformTable
  1342. filterset = filtersets.PlatformFilterSet
  1343. filterset_form = forms.PlatformFilterForm
  1344. @register_model_view(Platform)
  1345. class PlatformView(generic.ObjectView):
  1346. queryset = Platform.objects.all()
  1347. def get_extra_context(self, request, instance):
  1348. devices = Device.objects.restrict(request.user, 'view').filter(
  1349. platform=instance
  1350. )
  1351. devices_table = tables.DeviceTable(devices, user=request.user, exclude=('platform',))
  1352. devices_table.configure(request)
  1353. return {
  1354. 'devices_table': devices_table,
  1355. 'virtualmachine_count': VirtualMachine.objects.filter(platform=instance).count()
  1356. }
  1357. @register_model_view(Platform, 'edit')
  1358. class PlatformEditView(generic.ObjectEditView):
  1359. queryset = Platform.objects.all()
  1360. form = forms.PlatformForm
  1361. @register_model_view(Platform, 'delete')
  1362. class PlatformDeleteView(generic.ObjectDeleteView):
  1363. queryset = Platform.objects.all()
  1364. class PlatformBulkImportView(generic.BulkImportView):
  1365. queryset = Platform.objects.all()
  1366. model_form = forms.PlatformCSVForm
  1367. table = tables.PlatformTable
  1368. class PlatformBulkEditView(generic.BulkEditView):
  1369. queryset = Platform.objects.all()
  1370. filterset = filtersets.PlatformFilterSet
  1371. table = tables.PlatformTable
  1372. form = forms.PlatformBulkEditForm
  1373. class PlatformBulkDeleteView(generic.BulkDeleteView):
  1374. queryset = Platform.objects.all()
  1375. table = tables.PlatformTable
  1376. #
  1377. # Devices
  1378. #
  1379. class DeviceListView(generic.ObjectListView):
  1380. queryset = Device.objects.all()
  1381. filterset = filtersets.DeviceFilterSet
  1382. filterset_form = forms.DeviceFilterForm
  1383. table = tables.DeviceTable
  1384. template_name = 'dcim/device_list.html'
  1385. @register_model_view(Device)
  1386. class DeviceView(generic.ObjectView):
  1387. queryset = Device.objects.all()
  1388. def get_extra_context(self, request, instance):
  1389. # VirtualChassis members
  1390. if instance.virtual_chassis is not None:
  1391. vc_members = Device.objects.restrict(request.user, 'view').filter(
  1392. virtual_chassis=instance.virtual_chassis
  1393. ).order_by('vc_position')
  1394. else:
  1395. vc_members = []
  1396. services = Service.objects.restrict(request.user, 'view').filter(device=instance)
  1397. vdcs = VirtualDeviceContext.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related(
  1398. 'tenant'
  1399. )
  1400. return {
  1401. 'services': services,
  1402. 'vdcs': vdcs,
  1403. 'vc_members': vc_members,
  1404. 'svg_extra': f'highlight=id:{instance.pk}'
  1405. }
  1406. @register_model_view(Device, 'edit')
  1407. class DeviceEditView(generic.ObjectEditView):
  1408. queryset = Device.objects.all()
  1409. form = forms.DeviceForm
  1410. template_name = 'dcim/device_edit.html'
  1411. @register_model_view(Device, 'delete')
  1412. class DeviceDeleteView(generic.ObjectDeleteView):
  1413. queryset = Device.objects.all()
  1414. @register_model_view(Device, 'consoleports', path='console-ports')
  1415. class DeviceConsolePortsView(DeviceComponentsView):
  1416. child_model = ConsolePort
  1417. table = tables.DeviceConsolePortTable
  1418. filterset = filtersets.ConsolePortFilterSet
  1419. template_name = 'dcim/device/consoleports.html',
  1420. tab = ViewTab(
  1421. label=_('Console Ports'),
  1422. badge=lambda obj: obj.consoleports.count(),
  1423. permission='dcim.view_consoleport',
  1424. hide_if_empty=True
  1425. )
  1426. @register_model_view(Device, 'consoleserverports', path='console-server-ports')
  1427. class DeviceConsoleServerPortsView(DeviceComponentsView):
  1428. child_model = ConsoleServerPort
  1429. table = tables.DeviceConsoleServerPortTable
  1430. filterset = filtersets.ConsoleServerPortFilterSet
  1431. template_name = 'dcim/device/consoleserverports.html'
  1432. tab = ViewTab(
  1433. label=_('Console Server Ports'),
  1434. badge=lambda obj: obj.consoleserverports.count(),
  1435. permission='dcim.view_consoleserverport',
  1436. hide_if_empty=True
  1437. )
  1438. @register_model_view(Device, 'powerports', path='power-ports')
  1439. class DevicePowerPortsView(DeviceComponentsView):
  1440. child_model = PowerPort
  1441. table = tables.DevicePowerPortTable
  1442. filterset = filtersets.PowerPortFilterSet
  1443. template_name = 'dcim/device/powerports.html'
  1444. tab = ViewTab(
  1445. label=_('Power Ports'),
  1446. badge=lambda obj: obj.powerports.count(),
  1447. permission='dcim.view_powerport',
  1448. hide_if_empty=True
  1449. )
  1450. @register_model_view(Device, 'poweroutlets', path='power-outlets')
  1451. class DevicePowerOutletsView(DeviceComponentsView):
  1452. child_model = PowerOutlet
  1453. table = tables.DevicePowerOutletTable
  1454. filterset = filtersets.PowerOutletFilterSet
  1455. template_name = 'dcim/device/poweroutlets.html'
  1456. tab = ViewTab(
  1457. label=_('Power Outlets'),
  1458. badge=lambda obj: obj.poweroutlets.count(),
  1459. permission='dcim.view_poweroutlet',
  1460. hide_if_empty=True
  1461. )
  1462. @register_model_view(Device, 'interfaces')
  1463. class DeviceInterfacesView(DeviceComponentsView):
  1464. child_model = Interface
  1465. table = tables.DeviceInterfaceTable
  1466. filterset = filtersets.InterfaceFilterSet
  1467. template_name = 'dcim/device/interfaces.html'
  1468. tab = ViewTab(
  1469. label=_('Interfaces'),
  1470. badge=lambda obj: obj.interfaces.count(),
  1471. permission='dcim.view_interface',
  1472. hide_if_empty=True
  1473. )
  1474. def get_children(self, request, parent):
  1475. return parent.vc_interfaces().restrict(request.user, 'view').prefetch_related(
  1476. Prefetch('ip_addresses', queryset=IPAddress.objects.restrict(request.user)),
  1477. Prefetch('member_interfaces', queryset=Interface.objects.restrict(request.user))
  1478. )
  1479. @register_model_view(Device, 'frontports', path='front-ports')
  1480. class DeviceFrontPortsView(DeviceComponentsView):
  1481. child_model = FrontPort
  1482. table = tables.DeviceFrontPortTable
  1483. filterset = filtersets.FrontPortFilterSet
  1484. template_name = 'dcim/device/frontports.html'
  1485. tab = ViewTab(
  1486. label=_('Front Ports'),
  1487. badge=lambda obj: obj.frontports.count(),
  1488. permission='dcim.view_frontport',
  1489. hide_if_empty=True
  1490. )
  1491. @register_model_view(Device, 'rearports', path='rear-ports')
  1492. class DeviceRearPortsView(DeviceComponentsView):
  1493. child_model = RearPort
  1494. table = tables.DeviceRearPortTable
  1495. filterset = filtersets.RearPortFilterSet
  1496. template_name = 'dcim/device/rearports.html'
  1497. tab = ViewTab(
  1498. label=_('Rear Ports'),
  1499. badge=lambda obj: obj.rearports.count(),
  1500. permission='dcim.view_rearport',
  1501. hide_if_empty=True
  1502. )
  1503. @register_model_view(Device, 'modulebays', path='module-bays')
  1504. class DeviceModuleBaysView(DeviceComponentsView):
  1505. child_model = ModuleBay
  1506. table = tables.DeviceModuleBayTable
  1507. filterset = filtersets.ModuleBayFilterSet
  1508. template_name = 'dcim/device/modulebays.html'
  1509. tab = ViewTab(
  1510. label=_('Module Bays'),
  1511. badge=lambda obj: obj.modulebays.count(),
  1512. permission='dcim.view_modulebay',
  1513. hide_if_empty=True
  1514. )
  1515. @register_model_view(Device, 'devicebays', path='device-bays')
  1516. class DeviceDeviceBaysView(DeviceComponentsView):
  1517. child_model = DeviceBay
  1518. table = tables.DeviceDeviceBayTable
  1519. filterset = filtersets.DeviceBayFilterSet
  1520. template_name = 'dcim/device/devicebays.html'
  1521. tab = ViewTab(
  1522. label=_('Device Bays'),
  1523. badge=lambda obj: obj.devicebays.count(),
  1524. permission='dcim.view_devicebay',
  1525. hide_if_empty=True
  1526. )
  1527. @register_model_view(Device, 'inventory')
  1528. class DeviceInventoryView(DeviceComponentsView):
  1529. child_model = InventoryItem
  1530. table = tables.DeviceInventoryItemTable
  1531. filterset = filtersets.InventoryItemFilterSet
  1532. template_name = 'dcim/device/inventory.html'
  1533. tab = ViewTab(
  1534. label=_('Inventory Items'),
  1535. badge=lambda obj: obj.inventoryitems.count(),
  1536. permission='dcim.view_inventoryitem',
  1537. hide_if_empty=True
  1538. )
  1539. @register_model_view(Device, 'configcontext', path='config-context')
  1540. class DeviceConfigContextView(ObjectConfigContextView):
  1541. queryset = Device.objects.annotate_config_context_data()
  1542. base_template = 'dcim/device/base.html'
  1543. tab = ViewTab(
  1544. label=_('Config Context'),
  1545. permission='extras.view_configcontext'
  1546. )
  1547. class DeviceBulkImportView(generic.BulkImportView):
  1548. queryset = Device.objects.all()
  1549. model_form = forms.DeviceCSVForm
  1550. table = tables.DeviceImportTable
  1551. template_name = 'dcim/device_import.html'
  1552. class ChildDeviceBulkImportView(generic.BulkImportView):
  1553. queryset = Device.objects.all()
  1554. model_form = forms.ChildDeviceCSVForm
  1555. table = tables.DeviceImportTable
  1556. template_name = 'dcim/device_import_child.html'
  1557. def save_object(self, obj_form, request):
  1558. obj = obj_form.save()
  1559. # Save the reverse relation to the parent device bay
  1560. device_bay = obj.parent_bay
  1561. device_bay.installed_device = obj
  1562. device_bay.save()
  1563. return obj
  1564. class DeviceBulkEditView(generic.BulkEditView):
  1565. queryset = Device.objects.prefetch_related('device_type__manufacturer')
  1566. filterset = filtersets.DeviceFilterSet
  1567. table = tables.DeviceTable
  1568. form = forms.DeviceBulkEditForm
  1569. class DeviceBulkDeleteView(generic.BulkDeleteView):
  1570. queryset = Device.objects.prefetch_related('device_type__manufacturer')
  1571. filterset = filtersets.DeviceFilterSet
  1572. table = tables.DeviceTable
  1573. class DeviceBulkRenameView(generic.BulkRenameView):
  1574. queryset = Device.objects.all()
  1575. filterset = filtersets.DeviceFilterSet
  1576. table = tables.DeviceTable
  1577. #
  1578. # Device NAPALM views
  1579. #
  1580. class NAPALMViewTab(ViewTab):
  1581. def render(self, instance):
  1582. # Display NAPALM tabs only for devices which meet certain requirements
  1583. if not (
  1584. instance.status == 'active' and
  1585. instance.primary_ip and
  1586. instance.platform.napalm_driver
  1587. ):
  1588. return None
  1589. return super().render(instance)
  1590. @register_model_view(Device, 'status')
  1591. class DeviceStatusView(generic.ObjectView):
  1592. additional_permissions = ['dcim.napalm_read_device']
  1593. queryset = Device.objects.all()
  1594. template_name = 'dcim/device/status.html'
  1595. tab = NAPALMViewTab(
  1596. label=_('Status'),
  1597. permission='dcim.napalm_read_device',
  1598. )
  1599. @register_model_view(Device, 'lldp_neighbors', path='lldp-neighbors')
  1600. class DeviceLLDPNeighborsView(generic.ObjectView):
  1601. additional_permissions = ['dcim.napalm_read_device']
  1602. queryset = Device.objects.all()
  1603. template_name = 'dcim/device/lldp_neighbors.html'
  1604. tab = NAPALMViewTab(
  1605. label=_('LLDP Neighbors'),
  1606. permission='dcim.napalm_read_device',
  1607. )
  1608. def get_extra_context(self, request, instance):
  1609. interfaces = instance.vc_interfaces().restrict(request.user, 'view').prefetch_related(
  1610. '_path'
  1611. ).exclude(
  1612. type__in=NONCONNECTABLE_IFACE_TYPES
  1613. )
  1614. return {
  1615. 'interfaces': interfaces,
  1616. }
  1617. @register_model_view(Device, 'config')
  1618. class DeviceConfigView(generic.ObjectView):
  1619. additional_permissions = ['dcim.napalm_read_device']
  1620. queryset = Device.objects.all()
  1621. template_name = 'dcim/device/config.html'
  1622. tab = NAPALMViewTab(
  1623. label=_('Config'),
  1624. permission='dcim.napalm_read_device',
  1625. )
  1626. #
  1627. # Modules
  1628. #
  1629. class ModuleListView(generic.ObjectListView):
  1630. queryset = Module.objects.prefetch_related('module_type__manufacturer')
  1631. filterset = filtersets.ModuleFilterSet
  1632. filterset_form = forms.ModuleFilterForm
  1633. table = tables.ModuleTable
  1634. @register_model_view(Module)
  1635. class ModuleView(generic.ObjectView):
  1636. queryset = Module.objects.all()
  1637. @register_model_view(Module, 'edit')
  1638. class ModuleEditView(generic.ObjectEditView):
  1639. queryset = Module.objects.all()
  1640. form = forms.ModuleForm
  1641. @register_model_view(Module, 'delete')
  1642. class ModuleDeleteView(generic.ObjectDeleteView):
  1643. queryset = Module.objects.all()
  1644. class ModuleBulkImportView(generic.BulkImportView):
  1645. queryset = Module.objects.all()
  1646. model_form = forms.ModuleCSVForm
  1647. table = tables.ModuleTable
  1648. class ModuleBulkEditView(generic.BulkEditView):
  1649. queryset = Module.objects.prefetch_related('module_type__manufacturer')
  1650. filterset = filtersets.ModuleFilterSet
  1651. table = tables.ModuleTable
  1652. form = forms.ModuleBulkEditForm
  1653. class ModuleBulkDeleteView(generic.BulkDeleteView):
  1654. queryset = Module.objects.prefetch_related('module_type__manufacturer')
  1655. filterset = filtersets.ModuleFilterSet
  1656. table = tables.ModuleTable
  1657. #
  1658. # Console ports
  1659. #
  1660. class ConsolePortListView(generic.ObjectListView):
  1661. queryset = ConsolePort.objects.all()
  1662. filterset = filtersets.ConsolePortFilterSet
  1663. filterset_form = forms.ConsolePortFilterForm
  1664. table = tables.ConsolePortTable
  1665. actions = ('import', 'export', 'bulk_edit', 'bulk_delete')
  1666. @register_model_view(ConsolePort)
  1667. class ConsolePortView(generic.ObjectView):
  1668. queryset = ConsolePort.objects.all()
  1669. class ConsolePortCreateView(generic.ComponentCreateView):
  1670. queryset = ConsolePort.objects.all()
  1671. form = forms.ConsolePortCreateForm
  1672. model_form = forms.ConsolePortForm
  1673. @register_model_view(ConsolePort, 'edit')
  1674. class ConsolePortEditView(generic.ObjectEditView):
  1675. queryset = ConsolePort.objects.all()
  1676. form = forms.ConsolePortForm
  1677. @register_model_view(ConsolePort, 'delete')
  1678. class ConsolePortDeleteView(generic.ObjectDeleteView):
  1679. queryset = ConsolePort.objects.all()
  1680. class ConsolePortBulkImportView(generic.BulkImportView):
  1681. queryset = ConsolePort.objects.all()
  1682. model_form = forms.ConsolePortCSVForm
  1683. table = tables.ConsolePortTable
  1684. class ConsolePortBulkEditView(generic.BulkEditView):
  1685. queryset = ConsolePort.objects.all()
  1686. filterset = filtersets.ConsolePortFilterSet
  1687. table = tables.ConsolePortTable
  1688. form = forms.ConsolePortBulkEditForm
  1689. class ConsolePortBulkRenameView(generic.BulkRenameView):
  1690. queryset = ConsolePort.objects.all()
  1691. class ConsolePortBulkDisconnectView(BulkDisconnectView):
  1692. queryset = ConsolePort.objects.all()
  1693. class ConsolePortBulkDeleteView(generic.BulkDeleteView):
  1694. queryset = ConsolePort.objects.all()
  1695. filterset = filtersets.ConsolePortFilterSet
  1696. table = tables.ConsolePortTable
  1697. # Trace view
  1698. register_model_view(ConsolePort, 'trace', kwargs={'model': ConsolePort})(PathTraceView)
  1699. #
  1700. # Console server ports
  1701. #
  1702. class ConsoleServerPortListView(generic.ObjectListView):
  1703. queryset = ConsoleServerPort.objects.all()
  1704. filterset = filtersets.ConsoleServerPortFilterSet
  1705. filterset_form = forms.ConsoleServerPortFilterForm
  1706. table = tables.ConsoleServerPortTable
  1707. actions = ('import', 'export', 'bulk_edit', 'bulk_delete')
  1708. @register_model_view(ConsoleServerPort)
  1709. class ConsoleServerPortView(generic.ObjectView):
  1710. queryset = ConsoleServerPort.objects.all()
  1711. class ConsoleServerPortCreateView(generic.ComponentCreateView):
  1712. queryset = ConsoleServerPort.objects.all()
  1713. form = forms.ConsoleServerPortCreateForm
  1714. model_form = forms.ConsoleServerPortForm
  1715. @register_model_view(ConsoleServerPort, 'edit')
  1716. class ConsoleServerPortEditView(generic.ObjectEditView):
  1717. queryset = ConsoleServerPort.objects.all()
  1718. form = forms.ConsoleServerPortForm
  1719. @register_model_view(ConsoleServerPort, 'delete')
  1720. class ConsoleServerPortDeleteView(generic.ObjectDeleteView):
  1721. queryset = ConsoleServerPort.objects.all()
  1722. class ConsoleServerPortBulkImportView(generic.BulkImportView):
  1723. queryset = ConsoleServerPort.objects.all()
  1724. model_form = forms.ConsoleServerPortCSVForm
  1725. table = tables.ConsoleServerPortTable
  1726. class ConsoleServerPortBulkEditView(generic.BulkEditView):
  1727. queryset = ConsoleServerPort.objects.all()
  1728. filterset = filtersets.ConsoleServerPortFilterSet
  1729. table = tables.ConsoleServerPortTable
  1730. form = forms.ConsoleServerPortBulkEditForm
  1731. class ConsoleServerPortBulkRenameView(generic.BulkRenameView):
  1732. queryset = ConsoleServerPort.objects.all()
  1733. class ConsoleServerPortBulkDisconnectView(BulkDisconnectView):
  1734. queryset = ConsoleServerPort.objects.all()
  1735. class ConsoleServerPortBulkDeleteView(generic.BulkDeleteView):
  1736. queryset = ConsoleServerPort.objects.all()
  1737. filterset = filtersets.ConsoleServerPortFilterSet
  1738. table = tables.ConsoleServerPortTable
  1739. # Trace view
  1740. register_model_view(ConsoleServerPort, 'trace', kwargs={'model': ConsoleServerPort})(PathTraceView)
  1741. #
  1742. # Power ports
  1743. #
  1744. class PowerPortListView(generic.ObjectListView):
  1745. queryset = PowerPort.objects.all()
  1746. filterset = filtersets.PowerPortFilterSet
  1747. filterset_form = forms.PowerPortFilterForm
  1748. table = tables.PowerPortTable
  1749. actions = ('import', 'export', 'bulk_edit', 'bulk_delete')
  1750. @register_model_view(PowerPort)
  1751. class PowerPortView(generic.ObjectView):
  1752. queryset = PowerPort.objects.all()
  1753. class PowerPortCreateView(generic.ComponentCreateView):
  1754. queryset = PowerPort.objects.all()
  1755. form = forms.PowerPortCreateForm
  1756. model_form = forms.PowerPortForm
  1757. @register_model_view(PowerPort, 'edit')
  1758. class PowerPortEditView(generic.ObjectEditView):
  1759. queryset = PowerPort.objects.all()
  1760. form = forms.PowerPortForm
  1761. @register_model_view(PowerPort, 'delete')
  1762. class PowerPortDeleteView(generic.ObjectDeleteView):
  1763. queryset = PowerPort.objects.all()
  1764. class PowerPortBulkImportView(generic.BulkImportView):
  1765. queryset = PowerPort.objects.all()
  1766. model_form = forms.PowerPortCSVForm
  1767. table = tables.PowerPortTable
  1768. class PowerPortBulkEditView(generic.BulkEditView):
  1769. queryset = PowerPort.objects.all()
  1770. filterset = filtersets.PowerPortFilterSet
  1771. table = tables.PowerPortTable
  1772. form = forms.PowerPortBulkEditForm
  1773. class PowerPortBulkRenameView(generic.BulkRenameView):
  1774. queryset = PowerPort.objects.all()
  1775. class PowerPortBulkDisconnectView(BulkDisconnectView):
  1776. queryset = PowerPort.objects.all()
  1777. class PowerPortBulkDeleteView(generic.BulkDeleteView):
  1778. queryset = PowerPort.objects.all()
  1779. filterset = filtersets.PowerPortFilterSet
  1780. table = tables.PowerPortTable
  1781. # Trace view
  1782. register_model_view(PowerPort, 'trace', kwargs={'model': PowerPort})(PathTraceView)
  1783. #
  1784. # Power outlets
  1785. #
  1786. class PowerOutletListView(generic.ObjectListView):
  1787. queryset = PowerOutlet.objects.all()
  1788. filterset = filtersets.PowerOutletFilterSet
  1789. filterset_form = forms.PowerOutletFilterForm
  1790. table = tables.PowerOutletTable
  1791. actions = ('import', 'export', 'bulk_edit', 'bulk_delete')
  1792. @register_model_view(PowerOutlet)
  1793. class PowerOutletView(generic.ObjectView):
  1794. queryset = PowerOutlet.objects.all()
  1795. class PowerOutletCreateView(generic.ComponentCreateView):
  1796. queryset = PowerOutlet.objects.all()
  1797. form = forms.PowerOutletCreateForm
  1798. model_form = forms.PowerOutletForm
  1799. @register_model_view(PowerOutlet, 'edit')
  1800. class PowerOutletEditView(generic.ObjectEditView):
  1801. queryset = PowerOutlet.objects.all()
  1802. form = forms.PowerOutletForm
  1803. @register_model_view(PowerOutlet, 'delete')
  1804. class PowerOutletDeleteView(generic.ObjectDeleteView):
  1805. queryset = PowerOutlet.objects.all()
  1806. class PowerOutletBulkImportView(generic.BulkImportView):
  1807. queryset = PowerOutlet.objects.all()
  1808. model_form = forms.PowerOutletCSVForm
  1809. table = tables.PowerOutletTable
  1810. class PowerOutletBulkEditView(generic.BulkEditView):
  1811. queryset = PowerOutlet.objects.all()
  1812. filterset = filtersets.PowerOutletFilterSet
  1813. table = tables.PowerOutletTable
  1814. form = forms.PowerOutletBulkEditForm
  1815. class PowerOutletBulkRenameView(generic.BulkRenameView):
  1816. queryset = PowerOutlet.objects.all()
  1817. class PowerOutletBulkDisconnectView(BulkDisconnectView):
  1818. queryset = PowerOutlet.objects.all()
  1819. class PowerOutletBulkDeleteView(generic.BulkDeleteView):
  1820. queryset = PowerOutlet.objects.all()
  1821. filterset = filtersets.PowerOutletFilterSet
  1822. table = tables.PowerOutletTable
  1823. # Trace view
  1824. register_model_view(PowerOutlet, 'trace', kwargs={'model': PowerOutlet})(PathTraceView)
  1825. #
  1826. # Interfaces
  1827. #
  1828. class InterfaceListView(generic.ObjectListView):
  1829. queryset = Interface.objects.all()
  1830. filterset = filtersets.InterfaceFilterSet
  1831. filterset_form = forms.InterfaceFilterForm
  1832. table = tables.InterfaceTable
  1833. actions = ('import', 'export', 'bulk_edit', 'bulk_delete')
  1834. @register_model_view(Interface)
  1835. class InterfaceView(generic.ObjectView):
  1836. queryset = Interface.objects.all()
  1837. def get_extra_context(self, request, instance):
  1838. # Get assigned VDC's
  1839. vdc_table = tables.VirtualDeviceContextTable(
  1840. data=instance.vdcs.restrict(request.user, 'view').prefetch_related('device'),
  1841. exclude=('tenant', 'tenant_group', 'primary_ip', 'primary_ip4', 'primary_ip6', 'comments', 'tags',
  1842. 'created', 'last_updated', 'actions', ),
  1843. orderable=False
  1844. )
  1845. # Get assigned IP addresses
  1846. ipaddress_table = AssignedIPAddressesTable(
  1847. data=instance.ip_addresses.restrict(request.user, 'view').prefetch_related('vrf', 'tenant'),
  1848. orderable=False
  1849. )
  1850. # Get bridge interfaces
  1851. bridge_interfaces = Interface.objects.restrict(request.user, 'view').filter(bridge=instance)
  1852. bridge_interfaces_tables = tables.InterfaceTable(
  1853. bridge_interfaces,
  1854. exclude=('device', 'parent'),
  1855. orderable=False
  1856. )
  1857. # Get child interfaces
  1858. child_interfaces = Interface.objects.restrict(request.user, 'view').filter(parent=instance)
  1859. child_interfaces_tables = tables.InterfaceTable(
  1860. child_interfaces,
  1861. exclude=('device', 'parent'),
  1862. orderable=False
  1863. )
  1864. # Get assigned VLANs and annotate whether each is tagged or untagged
  1865. vlans = []
  1866. if instance.untagged_vlan is not None:
  1867. vlans.append(instance.untagged_vlan)
  1868. vlans[0].tagged = False
  1869. for vlan in instance.tagged_vlans.restrict(request.user).prefetch_related('site', 'group', 'tenant', 'role'):
  1870. vlan.tagged = True
  1871. vlans.append(vlan)
  1872. vlan_table = InterfaceVLANTable(
  1873. interface=instance,
  1874. data=vlans,
  1875. orderable=False
  1876. )
  1877. return {
  1878. 'vdc_table': vdc_table,
  1879. 'ipaddress_table': ipaddress_table,
  1880. 'bridge_interfaces_table': bridge_interfaces_tables,
  1881. 'child_interfaces_table': child_interfaces_tables,
  1882. 'vlan_table': vlan_table,
  1883. }
  1884. class InterfaceCreateView(generic.ComponentCreateView):
  1885. queryset = Interface.objects.all()
  1886. form = forms.InterfaceCreateForm
  1887. model_form = forms.InterfaceForm
  1888. @register_model_view(Interface, 'edit')
  1889. class InterfaceEditView(generic.ObjectEditView):
  1890. queryset = Interface.objects.all()
  1891. form = forms.InterfaceForm
  1892. @register_model_view(Interface, 'delete')
  1893. class InterfaceDeleteView(generic.ObjectDeleteView):
  1894. queryset = Interface.objects.all()
  1895. class InterfaceBulkImportView(generic.BulkImportView):
  1896. queryset = Interface.objects.all()
  1897. model_form = forms.InterfaceCSVForm
  1898. table = tables.InterfaceTable
  1899. class InterfaceBulkEditView(generic.BulkEditView):
  1900. queryset = Interface.objects.all()
  1901. filterset = filtersets.InterfaceFilterSet
  1902. table = tables.InterfaceTable
  1903. form = forms.InterfaceBulkEditForm
  1904. class InterfaceBulkRenameView(generic.BulkRenameView):
  1905. queryset = Interface.objects.all()
  1906. class InterfaceBulkDisconnectView(BulkDisconnectView):
  1907. queryset = Interface.objects.all()
  1908. class InterfaceBulkDeleteView(generic.BulkDeleteView):
  1909. queryset = Interface.objects.all()
  1910. filterset = filtersets.InterfaceFilterSet
  1911. table = tables.InterfaceTable
  1912. # Trace view
  1913. register_model_view(Interface, 'trace', kwargs={'model': Interface})(PathTraceView)
  1914. #
  1915. # Front ports
  1916. #
  1917. class FrontPortListView(generic.ObjectListView):
  1918. queryset = FrontPort.objects.all()
  1919. filterset = filtersets.FrontPortFilterSet
  1920. filterset_form = forms.FrontPortFilterForm
  1921. table = tables.FrontPortTable
  1922. actions = ('import', 'export', 'bulk_edit', 'bulk_delete')
  1923. @register_model_view(FrontPort)
  1924. class FrontPortView(generic.ObjectView):
  1925. queryset = FrontPort.objects.all()
  1926. class FrontPortCreateView(generic.ComponentCreateView):
  1927. queryset = FrontPort.objects.all()
  1928. form = forms.FrontPortCreateForm
  1929. model_form = forms.FrontPortForm
  1930. @register_model_view(FrontPort, 'edit')
  1931. class FrontPortEditView(generic.ObjectEditView):
  1932. queryset = FrontPort.objects.all()
  1933. form = forms.FrontPortForm
  1934. @register_model_view(FrontPort, 'delete')
  1935. class FrontPortDeleteView(generic.ObjectDeleteView):
  1936. queryset = FrontPort.objects.all()
  1937. class FrontPortBulkImportView(generic.BulkImportView):
  1938. queryset = FrontPort.objects.all()
  1939. model_form = forms.FrontPortCSVForm
  1940. table = tables.FrontPortTable
  1941. class FrontPortBulkEditView(generic.BulkEditView):
  1942. queryset = FrontPort.objects.all()
  1943. filterset = filtersets.FrontPortFilterSet
  1944. table = tables.FrontPortTable
  1945. form = forms.FrontPortBulkEditForm
  1946. class FrontPortBulkRenameView(generic.BulkRenameView):
  1947. queryset = FrontPort.objects.all()
  1948. class FrontPortBulkDisconnectView(BulkDisconnectView):
  1949. queryset = FrontPort.objects.all()
  1950. class FrontPortBulkDeleteView(generic.BulkDeleteView):
  1951. queryset = FrontPort.objects.all()
  1952. filterset = filtersets.FrontPortFilterSet
  1953. table = tables.FrontPortTable
  1954. # Trace view
  1955. register_model_view(FrontPort, 'trace', kwargs={'model': FrontPort})(PathTraceView)
  1956. #
  1957. # Rear ports
  1958. #
  1959. class RearPortListView(generic.ObjectListView):
  1960. queryset = RearPort.objects.all()
  1961. filterset = filtersets.RearPortFilterSet
  1962. filterset_form = forms.RearPortFilterForm
  1963. table = tables.RearPortTable
  1964. actions = ('import', 'export', 'bulk_edit', 'bulk_delete')
  1965. @register_model_view(RearPort)
  1966. class RearPortView(generic.ObjectView):
  1967. queryset = RearPort.objects.all()
  1968. class RearPortCreateView(generic.ComponentCreateView):
  1969. queryset = RearPort.objects.all()
  1970. form = forms.RearPortCreateForm
  1971. model_form = forms.RearPortForm
  1972. @register_model_view(RearPort, 'edit')
  1973. class RearPortEditView(generic.ObjectEditView):
  1974. queryset = RearPort.objects.all()
  1975. form = forms.RearPortForm
  1976. @register_model_view(RearPort, 'delete')
  1977. class RearPortDeleteView(generic.ObjectDeleteView):
  1978. queryset = RearPort.objects.all()
  1979. class RearPortBulkImportView(generic.BulkImportView):
  1980. queryset = RearPort.objects.all()
  1981. model_form = forms.RearPortCSVForm
  1982. table = tables.RearPortTable
  1983. class RearPortBulkEditView(generic.BulkEditView):
  1984. queryset = RearPort.objects.all()
  1985. filterset = filtersets.RearPortFilterSet
  1986. table = tables.RearPortTable
  1987. form = forms.RearPortBulkEditForm
  1988. class RearPortBulkRenameView(generic.BulkRenameView):
  1989. queryset = RearPort.objects.all()
  1990. class RearPortBulkDisconnectView(BulkDisconnectView):
  1991. queryset = RearPort.objects.all()
  1992. class RearPortBulkDeleteView(generic.BulkDeleteView):
  1993. queryset = RearPort.objects.all()
  1994. filterset = filtersets.RearPortFilterSet
  1995. table = tables.RearPortTable
  1996. # Trace view
  1997. register_model_view(RearPort, 'trace', kwargs={'model': RearPort})(PathTraceView)
  1998. #
  1999. # Module bays
  2000. #
  2001. class ModuleBayListView(generic.ObjectListView):
  2002. queryset = ModuleBay.objects.select_related('installed_module__module_type')
  2003. filterset = filtersets.ModuleBayFilterSet
  2004. filterset_form = forms.ModuleBayFilterForm
  2005. table = tables.ModuleBayTable
  2006. actions = ('import', 'export', 'bulk_edit', 'bulk_delete')
  2007. @register_model_view(ModuleBay)
  2008. class ModuleBayView(generic.ObjectView):
  2009. queryset = ModuleBay.objects.all()
  2010. class ModuleBayCreateView(generic.ComponentCreateView):
  2011. queryset = ModuleBay.objects.all()
  2012. form = forms.ModuleBayCreateForm
  2013. model_form = forms.ModuleBayForm
  2014. @register_model_view(ModuleBay, 'edit')
  2015. class ModuleBayEditView(generic.ObjectEditView):
  2016. queryset = ModuleBay.objects.all()
  2017. form = forms.ModuleBayForm
  2018. @register_model_view(ModuleBay, 'delete')
  2019. class ModuleBayDeleteView(generic.ObjectDeleteView):
  2020. queryset = ModuleBay.objects.all()
  2021. class ModuleBayBulkImportView(generic.BulkImportView):
  2022. queryset = ModuleBay.objects.all()
  2023. model_form = forms.ModuleBayCSVForm
  2024. table = tables.ModuleBayTable
  2025. class ModuleBayBulkEditView(generic.BulkEditView):
  2026. queryset = ModuleBay.objects.all()
  2027. filterset = filtersets.ModuleBayFilterSet
  2028. table = tables.ModuleBayTable
  2029. form = forms.ModuleBayBulkEditForm
  2030. class ModuleBayBulkRenameView(generic.BulkRenameView):
  2031. queryset = ModuleBay.objects.all()
  2032. class ModuleBayBulkDeleteView(generic.BulkDeleteView):
  2033. queryset = ModuleBay.objects.all()
  2034. filterset = filtersets.ModuleBayFilterSet
  2035. table = tables.ModuleBayTable
  2036. #
  2037. # Device bays
  2038. #
  2039. class DeviceBayListView(generic.ObjectListView):
  2040. queryset = DeviceBay.objects.all()
  2041. filterset = filtersets.DeviceBayFilterSet
  2042. filterset_form = forms.DeviceBayFilterForm
  2043. table = tables.DeviceBayTable
  2044. actions = ('import', 'export', 'bulk_edit', 'bulk_delete')
  2045. @register_model_view(DeviceBay)
  2046. class DeviceBayView(generic.ObjectView):
  2047. queryset = DeviceBay.objects.all()
  2048. class DeviceBayCreateView(generic.ComponentCreateView):
  2049. queryset = DeviceBay.objects.all()
  2050. form = forms.DeviceBayCreateForm
  2051. model_form = forms.DeviceBayForm
  2052. @register_model_view(DeviceBay, 'edit')
  2053. class DeviceBayEditView(generic.ObjectEditView):
  2054. queryset = DeviceBay.objects.all()
  2055. form = forms.DeviceBayForm
  2056. @register_model_view(DeviceBay, 'delete')
  2057. class DeviceBayDeleteView(generic.ObjectDeleteView):
  2058. queryset = DeviceBay.objects.all()
  2059. @register_model_view(DeviceBay, 'populate')
  2060. class DeviceBayPopulateView(generic.ObjectEditView):
  2061. queryset = DeviceBay.objects.all()
  2062. def get(self, request, pk):
  2063. device_bay = get_object_or_404(self.queryset, pk=pk)
  2064. form = forms.PopulateDeviceBayForm(device_bay)
  2065. return render(request, 'dcim/devicebay_populate.html', {
  2066. 'device_bay': device_bay,
  2067. 'form': form,
  2068. 'return_url': self.get_return_url(request, device_bay),
  2069. })
  2070. def post(self, request, pk):
  2071. device_bay = get_object_or_404(self.queryset, pk=pk)
  2072. form = forms.PopulateDeviceBayForm(device_bay, request.POST)
  2073. if form.is_valid():
  2074. device_bay.installed_device = form.cleaned_data['installed_device']
  2075. device_bay.save()
  2076. messages.success(request, "Added {} to {}.".format(device_bay.installed_device, device_bay))
  2077. return_url = self.get_return_url(request)
  2078. return redirect(return_url)
  2079. return render(request, 'dcim/devicebay_populate.html', {
  2080. 'device_bay': device_bay,
  2081. 'form': form,
  2082. 'return_url': self.get_return_url(request, device_bay),
  2083. })
  2084. @register_model_view(DeviceBay, 'depopulate')
  2085. class DeviceBayDepopulateView(generic.ObjectEditView):
  2086. queryset = DeviceBay.objects.all()
  2087. def get(self, request, pk):
  2088. device_bay = get_object_or_404(self.queryset, pk=pk)
  2089. form = ConfirmationForm()
  2090. return render(request, 'dcim/devicebay_depopulate.html', {
  2091. 'device_bay': device_bay,
  2092. 'form': form,
  2093. 'return_url': self.get_return_url(request, device_bay),
  2094. })
  2095. def post(self, request, pk):
  2096. device_bay = get_object_or_404(self.queryset, pk=pk)
  2097. form = ConfirmationForm(request.POST)
  2098. if form.is_valid():
  2099. removed_device = device_bay.installed_device
  2100. device_bay.installed_device = None
  2101. device_bay.save()
  2102. messages.success(request, f"{removed_device} has been removed from {device_bay}.")
  2103. return_url = self.get_return_url(request, device_bay.device)
  2104. return redirect(return_url)
  2105. return render(request, 'dcim/devicebay_depopulate.html', {
  2106. 'device_bay': device_bay,
  2107. 'form': form,
  2108. 'return_url': self.get_return_url(request, device_bay),
  2109. })
  2110. class DeviceBayBulkImportView(generic.BulkImportView):
  2111. queryset = DeviceBay.objects.all()
  2112. model_form = forms.DeviceBayCSVForm
  2113. table = tables.DeviceBayTable
  2114. class DeviceBayBulkEditView(generic.BulkEditView):
  2115. queryset = DeviceBay.objects.all()
  2116. filterset = filtersets.DeviceBayFilterSet
  2117. table = tables.DeviceBayTable
  2118. form = forms.DeviceBayBulkEditForm
  2119. class DeviceBayBulkRenameView(generic.BulkRenameView):
  2120. queryset = DeviceBay.objects.all()
  2121. class DeviceBayBulkDeleteView(generic.BulkDeleteView):
  2122. queryset = DeviceBay.objects.all()
  2123. filterset = filtersets.DeviceBayFilterSet
  2124. table = tables.DeviceBayTable
  2125. #
  2126. # Inventory items
  2127. #
  2128. class InventoryItemListView(generic.ObjectListView):
  2129. queryset = InventoryItem.objects.all()
  2130. filterset = filtersets.InventoryItemFilterSet
  2131. filterset_form = forms.InventoryItemFilterForm
  2132. table = tables.InventoryItemTable
  2133. actions = ('import', 'export', 'bulk_edit', 'bulk_delete')
  2134. @register_model_view(InventoryItem)
  2135. class InventoryItemView(generic.ObjectView):
  2136. queryset = InventoryItem.objects.all()
  2137. @register_model_view(InventoryItem, 'edit')
  2138. class InventoryItemEditView(generic.ObjectEditView):
  2139. queryset = InventoryItem.objects.all()
  2140. form = forms.InventoryItemForm
  2141. class InventoryItemCreateView(generic.ComponentCreateView):
  2142. queryset = InventoryItem.objects.all()
  2143. form = forms.InventoryItemCreateForm
  2144. model_form = forms.InventoryItemForm
  2145. def alter_object(self, instance, request):
  2146. # Set component (if any)
  2147. component_type = request.GET.get('component_type')
  2148. component_id = request.GET.get('component_id')
  2149. if component_type and component_id:
  2150. content_type = get_object_or_404(ContentType, pk=component_type)
  2151. instance.component = get_object_or_404(content_type.model_class(), pk=component_id)
  2152. return instance
  2153. @register_model_view(InventoryItem, 'delete')
  2154. class InventoryItemDeleteView(generic.ObjectDeleteView):
  2155. queryset = InventoryItem.objects.all()
  2156. class InventoryItemBulkImportView(generic.BulkImportView):
  2157. queryset = InventoryItem.objects.all()
  2158. model_form = forms.InventoryItemCSVForm
  2159. table = tables.InventoryItemTable
  2160. class InventoryItemBulkEditView(generic.BulkEditView):
  2161. queryset = InventoryItem.objects.all()
  2162. filterset = filtersets.InventoryItemFilterSet
  2163. table = tables.InventoryItemTable
  2164. form = forms.InventoryItemBulkEditForm
  2165. class InventoryItemBulkRenameView(generic.BulkRenameView):
  2166. queryset = InventoryItem.objects.all()
  2167. class InventoryItemBulkDeleteView(generic.BulkDeleteView):
  2168. queryset = InventoryItem.objects.all()
  2169. table = tables.InventoryItemTable
  2170. template_name = 'dcim/inventoryitem_bulk_delete.html'
  2171. #
  2172. # Inventory item roles
  2173. #
  2174. class InventoryItemRoleListView(generic.ObjectListView):
  2175. queryset = InventoryItemRole.objects.annotate(
  2176. inventoryitem_count=count_related(InventoryItem, 'role'),
  2177. )
  2178. filterset = filtersets.InventoryItemRoleFilterSet
  2179. filterset_form = forms.InventoryItemRoleFilterForm
  2180. table = tables.InventoryItemRoleTable
  2181. @register_model_view(InventoryItemRole)
  2182. class InventoryItemRoleView(generic.ObjectView):
  2183. queryset = InventoryItemRole.objects.all()
  2184. def get_extra_context(self, request, instance):
  2185. return {
  2186. 'inventoryitem_count': InventoryItem.objects.filter(role=instance).count(),
  2187. }
  2188. @register_model_view(InventoryItemRole, 'edit')
  2189. class InventoryItemRoleEditView(generic.ObjectEditView):
  2190. queryset = InventoryItemRole.objects.all()
  2191. form = forms.InventoryItemRoleForm
  2192. @register_model_view(InventoryItemRole, 'delete')
  2193. class InventoryItemRoleDeleteView(generic.ObjectDeleteView):
  2194. queryset = InventoryItemRole.objects.all()
  2195. class InventoryItemRoleBulkImportView(generic.BulkImportView):
  2196. queryset = InventoryItemRole.objects.all()
  2197. model_form = forms.InventoryItemRoleCSVForm
  2198. table = tables.InventoryItemRoleTable
  2199. class InventoryItemRoleBulkEditView(generic.BulkEditView):
  2200. queryset = InventoryItemRole.objects.annotate(
  2201. inventoryitem_count=count_related(InventoryItem, 'role'),
  2202. )
  2203. filterset = filtersets.InventoryItemRoleFilterSet
  2204. table = tables.InventoryItemRoleTable
  2205. form = forms.InventoryItemRoleBulkEditForm
  2206. class InventoryItemRoleBulkDeleteView(generic.BulkDeleteView):
  2207. queryset = InventoryItemRole.objects.annotate(
  2208. inventoryitem_count=count_related(InventoryItem, 'role'),
  2209. )
  2210. table = tables.InventoryItemRoleTable
  2211. #
  2212. # Bulk Device component creation
  2213. #
  2214. class DeviceBulkAddConsolePortView(generic.BulkComponentCreateView):
  2215. parent_model = Device
  2216. parent_field = 'device'
  2217. form = forms.ConsolePortBulkCreateForm
  2218. queryset = ConsolePort.objects.all()
  2219. model_form = forms.ConsolePortForm
  2220. filterset = filtersets.DeviceFilterSet
  2221. table = tables.DeviceTable
  2222. default_return_url = 'dcim:device_list'
  2223. class DeviceBulkAddConsoleServerPortView(generic.BulkComponentCreateView):
  2224. parent_model = Device
  2225. parent_field = 'device'
  2226. form = forms.ConsoleServerPortBulkCreateForm
  2227. queryset = ConsoleServerPort.objects.all()
  2228. model_form = forms.ConsoleServerPortForm
  2229. filterset = filtersets.DeviceFilterSet
  2230. table = tables.DeviceTable
  2231. default_return_url = 'dcim:device_list'
  2232. class DeviceBulkAddPowerPortView(generic.BulkComponentCreateView):
  2233. parent_model = Device
  2234. parent_field = 'device'
  2235. form = forms.PowerPortBulkCreateForm
  2236. queryset = PowerPort.objects.all()
  2237. model_form = forms.PowerPortForm
  2238. filterset = filtersets.DeviceFilterSet
  2239. table = tables.DeviceTable
  2240. default_return_url = 'dcim:device_list'
  2241. class DeviceBulkAddPowerOutletView(generic.BulkComponentCreateView):
  2242. parent_model = Device
  2243. parent_field = 'device'
  2244. form = forms.PowerOutletBulkCreateForm
  2245. queryset = PowerOutlet.objects.all()
  2246. model_form = forms.PowerOutletForm
  2247. filterset = filtersets.DeviceFilterSet
  2248. table = tables.DeviceTable
  2249. default_return_url = 'dcim:device_list'
  2250. class DeviceBulkAddInterfaceView(generic.BulkComponentCreateView):
  2251. parent_model = Device
  2252. parent_field = 'device'
  2253. form = forms.InterfaceBulkCreateForm
  2254. queryset = Interface.objects.all()
  2255. model_form = forms.InterfaceForm
  2256. filterset = filtersets.DeviceFilterSet
  2257. table = tables.DeviceTable
  2258. default_return_url = 'dcim:device_list'
  2259. # class DeviceBulkAddFrontPortView(generic.BulkComponentCreateView):
  2260. # parent_model = Device
  2261. # parent_field = 'device'
  2262. # form = forms.FrontPortBulkCreateForm
  2263. # queryset = FrontPort.objects.all()
  2264. # model_form = forms.FrontPortForm
  2265. # filterset = filtersets.DeviceFilterSet
  2266. # table = tables.DeviceTable
  2267. # default_return_url = 'dcim:device_list'
  2268. class DeviceBulkAddRearPortView(generic.BulkComponentCreateView):
  2269. parent_model = Device
  2270. parent_field = 'device'
  2271. form = forms.RearPortBulkCreateForm
  2272. queryset = RearPort.objects.all()
  2273. model_form = forms.RearPortForm
  2274. filterset = filtersets.DeviceFilterSet
  2275. table = tables.DeviceTable
  2276. default_return_url = 'dcim:device_list'
  2277. class DeviceBulkAddModuleBayView(generic.BulkComponentCreateView):
  2278. parent_model = Device
  2279. parent_field = 'device'
  2280. form = forms.ModuleBayBulkCreateForm
  2281. queryset = ModuleBay.objects.all()
  2282. model_form = forms.ModuleBayForm
  2283. filterset = filtersets.DeviceFilterSet
  2284. table = tables.DeviceTable
  2285. default_return_url = 'dcim:device_list'
  2286. class DeviceBulkAddDeviceBayView(generic.BulkComponentCreateView):
  2287. parent_model = Device
  2288. parent_field = 'device'
  2289. form = forms.DeviceBayBulkCreateForm
  2290. queryset = DeviceBay.objects.all()
  2291. model_form = forms.DeviceBayForm
  2292. filterset = filtersets.DeviceFilterSet
  2293. table = tables.DeviceTable
  2294. default_return_url = 'dcim:device_list'
  2295. class DeviceBulkAddInventoryItemView(generic.BulkComponentCreateView):
  2296. parent_model = Device
  2297. parent_field = 'device'
  2298. form = forms.InventoryItemBulkCreateForm
  2299. queryset = InventoryItem.objects.all()
  2300. model_form = forms.InventoryItemForm
  2301. filterset = filtersets.DeviceFilterSet
  2302. table = tables.DeviceTable
  2303. default_return_url = 'dcim:device_list'
  2304. #
  2305. # Cables
  2306. #
  2307. class CableListView(generic.ObjectListView):
  2308. queryset = Cable.objects.prefetch_related(
  2309. 'terminations__termination', 'terminations___device', 'terminations___rack', 'terminations___location',
  2310. 'terminations___site',
  2311. )
  2312. filterset = filtersets.CableFilterSet
  2313. filterset_form = forms.CableFilterForm
  2314. table = tables.CableTable
  2315. actions = ('import', 'export', 'bulk_edit', 'bulk_delete')
  2316. @register_model_view(Cable)
  2317. class CableView(generic.ObjectView):
  2318. queryset = Cable.objects.all()
  2319. @register_model_view(Cable, 'edit')
  2320. class CableEditView(generic.ObjectEditView):
  2321. queryset = Cable.objects.all()
  2322. template_name = 'dcim/cable_edit.html'
  2323. def dispatch(self, request, *args, **kwargs):
  2324. # If creating a new Cable, initialize the form class using URL query params
  2325. if 'pk' not in kwargs:
  2326. self.form = forms.get_cable_form(
  2327. a_type=CABLE_TERMINATION_TYPES.get(request.GET.get('a_terminations_type')),
  2328. b_type=CABLE_TERMINATION_TYPES.get(request.GET.get('b_terminations_type'))
  2329. )
  2330. return super().dispatch(request, *args, **kwargs)
  2331. def get_object(self, **kwargs):
  2332. """
  2333. Hack into get_object() to set the form class when editing an existing Cable, since ObjectEditView
  2334. doesn't currently provide a hook for dynamic class resolution.
  2335. """
  2336. obj = super().get_object(**kwargs)
  2337. if obj.pk:
  2338. # TODO: Optimize this logic
  2339. termination_a = obj.terminations.filter(cable_end='A').first()
  2340. a_type = termination_a.termination._meta.model if termination_a else None
  2341. termination_b = obj.terminations.filter(cable_end='B').first()
  2342. b_type = termination_b.termination._meta.model if termination_b else None
  2343. self.form = forms.get_cable_form(a_type, b_type)
  2344. return obj
  2345. @register_model_view(Cable, 'delete')
  2346. class CableDeleteView(generic.ObjectDeleteView):
  2347. queryset = Cable.objects.all()
  2348. class CableBulkImportView(generic.BulkImportView):
  2349. queryset = Cable.objects.all()
  2350. model_form = forms.CableCSVForm
  2351. table = tables.CableTable
  2352. class CableBulkEditView(generic.BulkEditView):
  2353. queryset = Cable.objects.prefetch_related(
  2354. 'terminations__termination', 'terminations___device', 'terminations___rack', 'terminations___location',
  2355. 'terminations___site',
  2356. )
  2357. filterset = filtersets.CableFilterSet
  2358. table = tables.CableTable
  2359. form = forms.CableBulkEditForm
  2360. class CableBulkDeleteView(generic.BulkDeleteView):
  2361. queryset = Cable.objects.prefetch_related(
  2362. 'terminations__termination', 'terminations___device', 'terminations___rack', 'terminations___location',
  2363. 'terminations___site',
  2364. )
  2365. filterset = filtersets.CableFilterSet
  2366. table = tables.CableTable
  2367. #
  2368. # Connections
  2369. #
  2370. class ConsoleConnectionsListView(generic.ObjectListView):
  2371. queryset = ConsolePort.objects.filter(_path__is_complete=True)
  2372. filterset = filtersets.ConsoleConnectionFilterSet
  2373. filterset_form = forms.ConsoleConnectionFilterForm
  2374. table = tables.ConsoleConnectionTable
  2375. template_name = 'dcim/connections_list.html'
  2376. actions = ('export',)
  2377. def get_extra_context(self, request):
  2378. return {
  2379. 'title': 'Console Connections'
  2380. }
  2381. class PowerConnectionsListView(generic.ObjectListView):
  2382. queryset = PowerPort.objects.filter(_path__is_complete=True)
  2383. filterset = filtersets.PowerConnectionFilterSet
  2384. filterset_form = forms.PowerConnectionFilterForm
  2385. table = tables.PowerConnectionTable
  2386. template_name = 'dcim/connections_list.html'
  2387. actions = ('export',)
  2388. def get_extra_context(self, request):
  2389. return {
  2390. 'title': 'Power Connections'
  2391. }
  2392. class InterfaceConnectionsListView(generic.ObjectListView):
  2393. queryset = Interface.objects.filter(_path__is_complete=True)
  2394. filterset = filtersets.InterfaceConnectionFilterSet
  2395. filterset_form = forms.InterfaceConnectionFilterForm
  2396. table = tables.InterfaceConnectionTable
  2397. template_name = 'dcim/connections_list.html'
  2398. actions = ('export',)
  2399. def get_extra_context(self, request):
  2400. return {
  2401. 'title': 'Interface Connections'
  2402. }
  2403. #
  2404. # Virtual chassis
  2405. #
  2406. class VirtualChassisListView(generic.ObjectListView):
  2407. queryset = VirtualChassis.objects.annotate(
  2408. member_count=count_related(Device, 'virtual_chassis')
  2409. )
  2410. table = tables.VirtualChassisTable
  2411. filterset = filtersets.VirtualChassisFilterSet
  2412. filterset_form = forms.VirtualChassisFilterForm
  2413. @register_model_view(VirtualChassis)
  2414. class VirtualChassisView(generic.ObjectView):
  2415. queryset = VirtualChassis.objects.all()
  2416. def get_extra_context(self, request, instance):
  2417. members = Device.objects.restrict(request.user).filter(virtual_chassis=instance)
  2418. return {
  2419. 'members': members,
  2420. }
  2421. class VirtualChassisCreateView(generic.ObjectEditView):
  2422. queryset = VirtualChassis.objects.all()
  2423. form = forms.VirtualChassisCreateForm
  2424. template_name = 'dcim/virtualchassis_add.html'
  2425. @register_model_view(VirtualChassis, 'edit')
  2426. class VirtualChassisEditView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View):
  2427. queryset = VirtualChassis.objects.all()
  2428. def get_required_permission(self):
  2429. return 'dcim.change_virtualchassis'
  2430. def get(self, request, pk):
  2431. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  2432. VCMemberFormSet = modelformset_factory(
  2433. model=Device,
  2434. form=forms.DeviceVCMembershipForm,
  2435. formset=forms.BaseVCMemberFormSet,
  2436. extra=0
  2437. )
  2438. members_queryset = virtual_chassis.members.prefetch_related('rack').order_by('vc_position')
  2439. vc_form = forms.VirtualChassisForm(instance=virtual_chassis)
  2440. vc_form.fields['master'].queryset = members_queryset
  2441. formset = VCMemberFormSet(queryset=members_queryset)
  2442. return render(request, 'dcim/virtualchassis_edit.html', {
  2443. 'vc_form': vc_form,
  2444. 'formset': formset,
  2445. 'return_url': self.get_return_url(request, virtual_chassis),
  2446. })
  2447. def post(self, request, pk):
  2448. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  2449. VCMemberFormSet = modelformset_factory(
  2450. model=Device,
  2451. form=forms.DeviceVCMembershipForm,
  2452. formset=forms.BaseVCMemberFormSet,
  2453. extra=0
  2454. )
  2455. members_queryset = virtual_chassis.members.prefetch_related('rack').order_by('vc_position')
  2456. vc_form = forms.VirtualChassisForm(request.POST, instance=virtual_chassis)
  2457. vc_form.fields['master'].queryset = members_queryset
  2458. formset = VCMemberFormSet(request.POST, queryset=members_queryset)
  2459. if vc_form.is_valid() and formset.is_valid():
  2460. with transaction.atomic():
  2461. # Save the VirtualChassis
  2462. vc_form.save()
  2463. # Nullify the vc_position of each member first to allow reordering without raising an IntegrityError on
  2464. # duplicate positions. Then save each member instance.
  2465. members = formset.save(commit=False)
  2466. devices = Device.objects.filter(pk__in=[m.pk for m in members])
  2467. for device in devices:
  2468. device.vc_position = None
  2469. device.save()
  2470. for member in members:
  2471. member.save()
  2472. return redirect(virtual_chassis.get_absolute_url())
  2473. return render(request, 'dcim/virtualchassis_edit.html', {
  2474. 'vc_form': vc_form,
  2475. 'formset': formset,
  2476. 'return_url': self.get_return_url(request, virtual_chassis),
  2477. })
  2478. @register_model_view(VirtualChassis, 'delete')
  2479. class VirtualChassisDeleteView(generic.ObjectDeleteView):
  2480. queryset = VirtualChassis.objects.all()
  2481. @register_model_view(VirtualChassis, 'add_member', path='add-member')
  2482. class VirtualChassisAddMemberView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View):
  2483. queryset = VirtualChassis.objects.all()
  2484. def get_required_permission(self):
  2485. return 'dcim.change_virtualchassis'
  2486. def get(self, request, pk):
  2487. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  2488. initial_data = {k: request.GET[k] for k in request.GET}
  2489. member_select_form = forms.VCMemberSelectForm(initial=initial_data)
  2490. membership_form = forms.DeviceVCMembershipForm(initial=initial_data)
  2491. return render(request, 'dcim/virtualchassis_add_member.html', {
  2492. 'virtual_chassis': virtual_chassis,
  2493. 'member_select_form': member_select_form,
  2494. 'membership_form': membership_form,
  2495. 'return_url': self.get_return_url(request, virtual_chassis),
  2496. })
  2497. def post(self, request, pk):
  2498. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  2499. member_select_form = forms.VCMemberSelectForm(request.POST)
  2500. if member_select_form.is_valid():
  2501. device = member_select_form.cleaned_data['device']
  2502. device.virtual_chassis = virtual_chassis
  2503. data = {k: request.POST[k] for k in ['vc_position', 'vc_priority']}
  2504. membership_form = forms.DeviceVCMembershipForm(data=data, validate_vc_position=True, instance=device)
  2505. if membership_form.is_valid():
  2506. membership_form.save()
  2507. msg = f'Added member <a href="{device.get_absolute_url()}">{escape(device)}</a>'
  2508. messages.success(request, mark_safe(msg))
  2509. if '_addanother' in request.POST:
  2510. return redirect(request.get_full_path())
  2511. return redirect(self.get_return_url(request, device))
  2512. else:
  2513. membership_form = forms.DeviceVCMembershipForm(data=request.POST)
  2514. return render(request, 'dcim/virtualchassis_add_member.html', {
  2515. 'virtual_chassis': virtual_chassis,
  2516. 'member_select_form': member_select_form,
  2517. 'membership_form': membership_form,
  2518. 'return_url': self.get_return_url(request, virtual_chassis),
  2519. })
  2520. class VirtualChassisRemoveMemberView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View):
  2521. queryset = Device.objects.all()
  2522. def get_required_permission(self):
  2523. return 'dcim.change_device'
  2524. def get(self, request, pk):
  2525. device = get_object_or_404(self.queryset, pk=pk, virtual_chassis__isnull=False)
  2526. form = ConfirmationForm(initial=request.GET)
  2527. return render(request, 'dcim/virtualchassis_remove_member.html', {
  2528. 'device': device,
  2529. 'form': form,
  2530. 'return_url': self.get_return_url(request, device),
  2531. })
  2532. def post(self, request, pk):
  2533. device = get_object_or_404(self.queryset, pk=pk, virtual_chassis__isnull=False)
  2534. form = ConfirmationForm(request.POST)
  2535. # Protect master device from being removed
  2536. virtual_chassis = VirtualChassis.objects.filter(master=device).first()
  2537. if virtual_chassis is not None:
  2538. messages.error(request, f'Unable to remove master device {device} from the virtual chassis.')
  2539. return redirect(device.get_absolute_url())
  2540. if form.is_valid():
  2541. devices = Device.objects.filter(pk=device.pk)
  2542. for device in devices:
  2543. device.virtual_chassis = None
  2544. device.vc_position = None
  2545. device.vc_priority = None
  2546. device.save()
  2547. msg = 'Removed {} from virtual chassis {}'.format(device, device.virtual_chassis)
  2548. messages.success(request, msg)
  2549. return redirect(self.get_return_url(request, device))
  2550. return render(request, 'dcim/virtualchassis_remove_member.html', {
  2551. 'device': device,
  2552. 'form': form,
  2553. 'return_url': self.get_return_url(request, device),
  2554. })
  2555. class VirtualChassisBulkImportView(generic.BulkImportView):
  2556. queryset = VirtualChassis.objects.all()
  2557. model_form = forms.VirtualChassisCSVForm
  2558. table = tables.VirtualChassisTable
  2559. class VirtualChassisBulkEditView(generic.BulkEditView):
  2560. queryset = VirtualChassis.objects.all()
  2561. filterset = filtersets.VirtualChassisFilterSet
  2562. table = tables.VirtualChassisTable
  2563. form = forms.VirtualChassisBulkEditForm
  2564. class VirtualChassisBulkDeleteView(generic.BulkDeleteView):
  2565. queryset = VirtualChassis.objects.all()
  2566. filterset = filtersets.VirtualChassisFilterSet
  2567. table = tables.VirtualChassisTable
  2568. #
  2569. # Power panels
  2570. #
  2571. class PowerPanelListView(generic.ObjectListView):
  2572. queryset = PowerPanel.objects.annotate(
  2573. powerfeed_count=count_related(PowerFeed, 'power_panel')
  2574. )
  2575. filterset = filtersets.PowerPanelFilterSet
  2576. filterset_form = forms.PowerPanelFilterForm
  2577. table = tables.PowerPanelTable
  2578. @register_model_view(PowerPanel)
  2579. class PowerPanelView(generic.ObjectView):
  2580. queryset = PowerPanel.objects.all()
  2581. def get_extra_context(self, request, instance):
  2582. power_feeds = PowerFeed.objects.restrict(request.user).filter(power_panel=instance)
  2583. powerfeed_table = tables.PowerFeedTable(
  2584. data=power_feeds,
  2585. orderable=False
  2586. )
  2587. if request.user.has_perm('dcim.delete_cable'):
  2588. powerfeed_table.columns.show('pk')
  2589. powerfeed_table.exclude = ['power_panel']
  2590. return {
  2591. 'powerfeed_table': powerfeed_table,
  2592. }
  2593. @register_model_view(PowerPanel, 'edit')
  2594. class PowerPanelEditView(generic.ObjectEditView):
  2595. queryset = PowerPanel.objects.all()
  2596. form = forms.PowerPanelForm
  2597. @register_model_view(PowerPanel, 'delete')
  2598. class PowerPanelDeleteView(generic.ObjectDeleteView):
  2599. queryset = PowerPanel.objects.all()
  2600. class PowerPanelBulkImportView(generic.BulkImportView):
  2601. queryset = PowerPanel.objects.all()
  2602. model_form = forms.PowerPanelCSVForm
  2603. table = tables.PowerPanelTable
  2604. class PowerPanelBulkEditView(generic.BulkEditView):
  2605. queryset = PowerPanel.objects.all()
  2606. filterset = filtersets.PowerPanelFilterSet
  2607. table = tables.PowerPanelTable
  2608. form = forms.PowerPanelBulkEditForm
  2609. class PowerPanelBulkDeleteView(generic.BulkDeleteView):
  2610. queryset = PowerPanel.objects.annotate(
  2611. powerfeed_count=count_related(PowerFeed, 'power_panel')
  2612. )
  2613. filterset = filtersets.PowerPanelFilterSet
  2614. table = tables.PowerPanelTable
  2615. #
  2616. # Power feeds
  2617. #
  2618. class PowerFeedListView(generic.ObjectListView):
  2619. queryset = PowerFeed.objects.all()
  2620. filterset = filtersets.PowerFeedFilterSet
  2621. filterset_form = forms.PowerFeedFilterForm
  2622. table = tables.PowerFeedTable
  2623. @register_model_view(PowerFeed)
  2624. class PowerFeedView(generic.ObjectView):
  2625. queryset = PowerFeed.objects.all()
  2626. @register_model_view(PowerFeed, 'edit')
  2627. class PowerFeedEditView(generic.ObjectEditView):
  2628. queryset = PowerFeed.objects.all()
  2629. form = forms.PowerFeedForm
  2630. @register_model_view(PowerFeed, 'delete')
  2631. class PowerFeedDeleteView(generic.ObjectDeleteView):
  2632. queryset = PowerFeed.objects.all()
  2633. class PowerFeedBulkImportView(generic.BulkImportView):
  2634. queryset = PowerFeed.objects.all()
  2635. model_form = forms.PowerFeedCSVForm
  2636. table = tables.PowerFeedTable
  2637. class PowerFeedBulkEditView(generic.BulkEditView):
  2638. queryset = PowerFeed.objects.all()
  2639. filterset = filtersets.PowerFeedFilterSet
  2640. table = tables.PowerFeedTable
  2641. form = forms.PowerFeedBulkEditForm
  2642. class PowerFeedBulkDisconnectView(BulkDisconnectView):
  2643. queryset = PowerFeed.objects.all()
  2644. class PowerFeedBulkDeleteView(generic.BulkDeleteView):
  2645. queryset = PowerFeed.objects.all()
  2646. filterset = filtersets.PowerFeedFilterSet
  2647. table = tables.PowerFeedTable
  2648. # Trace view
  2649. register_model_view(PowerFeed, 'trace', kwargs={'model': PowerFeed})(PathTraceView)
  2650. # VDC
  2651. class VirtualDeviceContextListView(generic.ObjectListView):
  2652. queryset = VirtualDeviceContext.objects.all()
  2653. filterset = filtersets.VirtualDeviceContextFilterSet
  2654. filterset_form = forms.VirtualDeviceContextFilterForm
  2655. table = tables.VirtualDeviceContextTable
  2656. @register_model_view(VirtualDeviceContext)
  2657. class VirtualDeviceContextView(generic.ObjectView):
  2658. queryset = VirtualDeviceContext.objects.all()
  2659. def get_extra_context(self, request, instance):
  2660. interfaces_table = tables.InterfaceTable(instance.interfaces, user=request.user)
  2661. interfaces_table.configure(request)
  2662. return {
  2663. 'interfaces_table': interfaces_table,
  2664. 'interface_count': instance.interfaces.count(),
  2665. }
  2666. @register_model_view(VirtualDeviceContext, 'edit')
  2667. class VirtualDeviceContextEditView(generic.ObjectEditView):
  2668. queryset = VirtualDeviceContext.objects.all()
  2669. form = forms.VirtualDeviceContextForm
  2670. @register_model_view(VirtualDeviceContext, 'delete')
  2671. class VirtualDeviceContextDeleteView(generic.ObjectDeleteView):
  2672. queryset = VirtualDeviceContext.objects.all()
  2673. class VirtualDeviceContextBulkImportView(generic.BulkImportView):
  2674. queryset = VirtualDeviceContext.objects.all()
  2675. model_form = forms.VirtualDeviceContextCSVForm
  2676. table = tables.VirtualDeviceContextTable
  2677. class VirtualDeviceContextBulkEditView(generic.BulkEditView):
  2678. queryset = VirtualDeviceContext.objects.all()
  2679. filterset = filtersets.VirtualDeviceContextFilterSet
  2680. table = tables.VirtualDeviceContextTable
  2681. form = forms.VirtualDeviceContextBulkEditForm
  2682. class VirtualDeviceContextBulkDeleteView(generic.BulkDeleteView):
  2683. queryset = VirtualDeviceContext.objects.all()
  2684. filterset = filtersets.VirtualDeviceContextFilterSet
  2685. table = tables.VirtualDeviceContextTable