views.py 113 KB

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