views.py 154 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567
  1. from django.conf import settings
  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 router, transaction
  6. from django.db.models import Prefetch
  7. from django.forms import ModelMultipleChoiceField, MultipleHiddenInput, modelformset_factory
  8. from django.shortcuts import get_object_or_404, redirect, render
  9. from django.urls import reverse
  10. from django.utils.html import escape
  11. from django.utils.safestring import mark_safe
  12. from django.utils.translation import gettext_lazy as _
  13. from django.views.generic import View
  14. from circuits.models import Circuit, CircuitTermination
  15. from extras.ui.panels import CustomFieldsPanel, ImageAttachmentsPanel, TagsPanel
  16. from extras.views import ObjectConfigContextView, ObjectRenderConfigView
  17. from ipam.models import ASN, VLAN, IPAddress, Prefix, VLANGroup
  18. from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable
  19. from netbox.object_actions import *
  20. from netbox.ui import actions, layout
  21. from netbox.ui.panels import (
  22. CommentsPanel,
  23. JSONPanel,
  24. NestedGroupObjectPanel,
  25. ObjectsTablePanel,
  26. OrganizationalObjectPanel,
  27. Panel,
  28. RelatedObjectsPanel,
  29. TemplatePanel,
  30. )
  31. from netbox.views import generic
  32. from utilities.forms import ConfirmationForm
  33. from utilities.paginator import EnhancedPaginator, get_paginate_count
  34. from utilities.permissions import get_permission_for_model
  35. from utilities.query import count_related
  36. from utilities.query_functions import CollateAsChar
  37. from utilities.request import safe_for_redirect
  38. from utilities.views import (
  39. GetRelatedModelsMixin,
  40. GetReturnURLMixin,
  41. ObjectPermissionRequiredMixin,
  42. ViewTab,
  43. register_model_view,
  44. )
  45. from virtualization.filtersets import VirtualMachineFilterSet
  46. from virtualization.forms import VirtualMachineFilterForm
  47. from virtualization.models import Cluster, VirtualMachine
  48. from virtualization.tables import VirtualMachineTable
  49. from wireless.models import WirelessLAN
  50. from . import filtersets, forms, tables
  51. from .choices import DeviceFaceChoices, InterfaceModeChoices
  52. from .models import *
  53. from .models.device_components import PortMapping
  54. from .object_actions import BulkAddComponents, BulkDisconnect
  55. from .ui import panels
  56. CABLE_TERMINATION_TYPES = {
  57. 'dcim.consoleport': ConsolePort,
  58. 'dcim.consoleserverport': ConsoleServerPort,
  59. 'dcim.powerport': PowerPort,
  60. 'dcim.poweroutlet': PowerOutlet,
  61. 'dcim.interface': Interface,
  62. 'dcim.frontport': FrontPort,
  63. 'dcim.rearport': RearPort,
  64. 'dcim.powerfeed': PowerFeed,
  65. 'circuits.circuittermination': CircuitTermination,
  66. }
  67. class DeviceComponentsView(generic.ObjectChildrenView):
  68. queryset = Device.objects.all()
  69. def get_children(self, request, parent):
  70. return self.child_model.objects.restrict(request.user, 'view').filter(device=parent)
  71. class DeviceTypeComponentsView(generic.ObjectChildrenView):
  72. actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete)
  73. queryset = DeviceType.objects.all()
  74. viewname = None # Used for return_url resolution
  75. def get_children(self, request, parent):
  76. return self.child_model.objects.restrict(request.user, 'view').filter(device_type=parent)
  77. def get_extra_context(self, request, instance):
  78. return {
  79. 'return_url': reverse(self.viewname, kwargs={'pk': instance.pk}),
  80. }
  81. class ModuleTypeComponentsView(generic.ObjectChildrenView):
  82. queryset = ModuleType.objects.all()
  83. actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete)
  84. viewname = None # Used for return_url resolution
  85. def get_children(self, request, parent):
  86. return self.child_model.objects.restrict(request.user, 'view').filter(module_type=parent)
  87. def get_extra_context(self, request, instance):
  88. return {
  89. 'return_url': reverse(self.viewname, kwargs={'pk': instance.pk}),
  90. }
  91. class BulkDisconnectView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
  92. """
  93. An extendable view for disconnection console/power/interface components in bulk.
  94. """
  95. queryset = None
  96. template_name = 'dcim/bulk_disconnect.html'
  97. def __init__(self, *args, **kwargs):
  98. super().__init__(*args, **kwargs)
  99. # Create a new Form class from ConfirmationForm
  100. class _Form(ConfirmationForm):
  101. pk = ModelMultipleChoiceField(
  102. queryset=self.queryset,
  103. widget=MultipleHiddenInput()
  104. )
  105. self.form = _Form
  106. def get_required_permission(self):
  107. return get_permission_for_model(self.queryset.model, 'change')
  108. def post(self, request):
  109. selected_objects = []
  110. return_url = self.get_return_url(request)
  111. if '_confirm' in request.POST:
  112. form = self.form(request.POST)
  113. if form.is_valid():
  114. with transaction.atomic(using=router.db_for_write(Cable)):
  115. count = 0
  116. cable_ids = set()
  117. for obj in self.queryset.filter(pk__in=form.cleaned_data['pk']):
  118. if obj.cable:
  119. cable_ids.add(obj.cable.pk)
  120. count += 1
  121. for cable in Cable.objects.filter(pk__in=cable_ids):
  122. cable.delete()
  123. messages.success(request, _("Disconnected {count} {type}").format(
  124. count=count,
  125. type=self.queryset.model._meta.verbose_name_plural
  126. ))
  127. return redirect(return_url)
  128. else:
  129. form = self.form(initial={'pk': request.POST.getlist('pk')})
  130. selected_objects = self.queryset.filter(pk__in=form.initial['pk'])
  131. return render(request, self.template_name, {
  132. 'form': form,
  133. 'obj_type_plural': self.queryset.model._meta.verbose_name_plural,
  134. 'selected_objects': selected_objects,
  135. 'return_url': return_url,
  136. })
  137. class PathTraceView(generic.ObjectView):
  138. """
  139. Trace a cable path beginning from the given path endpoint (origin).
  140. """
  141. additional_permissions = ['dcim.view_cable']
  142. template_name = 'dcim/cable_trace.html'
  143. def dispatch(self, request, *args, **kwargs):
  144. model = kwargs.pop('model')
  145. self.queryset = model.objects.all()
  146. return super().dispatch(request, *args, **kwargs)
  147. def get_extra_context(self, request, instance):
  148. related_paths = []
  149. # If tracing a PathEndpoint, locate the CablePath (if one exists) by its origin
  150. if isinstance(instance, PathEndpoint):
  151. path = instance._path
  152. # Otherwise, find all CablePaths which traverse the specified object
  153. else:
  154. related_paths = CablePath.objects.filter(_nodes__contains=instance)
  155. # Check for specification of a particular path (when tracing pass-through ports)
  156. try:
  157. path_id = int(request.GET.get('cablepath_id'))
  158. except TypeError:
  159. path_id = None
  160. if path_id in list(related_paths.values_list('pk', flat=True)):
  161. path = CablePath.objects.get(pk=path_id)
  162. else:
  163. path = related_paths.first()
  164. # No paths found
  165. if path is None:
  166. return {
  167. 'path': None
  168. }
  169. # Get the total length of the cable and whether the length is definitive (fully defined)
  170. total_length, is_definitive = path.get_total_length() if path else (None, False)
  171. # Determine the path to the SVG trace image
  172. api_viewname = f"{path.origin_type.app_label}-api:{path.origin_type.model}-trace"
  173. svg_url = f"{reverse(api_viewname, kwargs={'pk': path.origins[0].pk})}?render=svg"
  174. return {
  175. 'path': path,
  176. 'related_paths': related_paths,
  177. 'total_length': total_length,
  178. 'is_definitive': is_definitive,
  179. 'svg_url': svg_url,
  180. }
  181. #
  182. # Regions
  183. #
  184. @register_model_view(Region, 'list', path='', detail=False)
  185. class RegionListView(generic.ObjectListView):
  186. queryset = Region.objects.add_related_count(
  187. Region.objects.all(),
  188. Site,
  189. 'region',
  190. 'site_count',
  191. cumulative=True
  192. )
  193. filterset = filtersets.RegionFilterSet
  194. filterset_form = forms.RegionFilterForm
  195. table = tables.RegionTable
  196. @register_model_view(Region)
  197. class RegionView(GetRelatedModelsMixin, generic.ObjectView):
  198. queryset = Region.objects.all()
  199. layout = layout.SimpleLayout(
  200. left_panels=[
  201. NestedGroupObjectPanel(),
  202. TagsPanel(),
  203. CustomFieldsPanel(),
  204. CommentsPanel(),
  205. ],
  206. right_panels=[
  207. RelatedObjectsPanel(),
  208. ],
  209. bottom_panels=[
  210. ObjectsTablePanel(
  211. model='dcim.Region',
  212. title=_('Child Regions'),
  213. filters={'parent_id': lambda ctx: ctx['object'].pk},
  214. actions=[
  215. actions.AddObject('dcim.Region', url_params={'parent': lambda ctx: ctx['object'].pk}),
  216. ],
  217. ),
  218. ]
  219. )
  220. def get_extra_context(self, request, instance):
  221. regions = instance.get_descendants(include_self=True)
  222. return {
  223. 'related_models': self.get_related_models(
  224. request,
  225. regions,
  226. omit=(Cluster, CircuitTermination, Prefix, WirelessLAN),
  227. extra=(
  228. (Location.objects.restrict(request.user, 'view').filter(site__region__in=regions), 'region_id'),
  229. (Rack.objects.restrict(request.user, 'view').filter(site__region__in=regions), 'region_id'),
  230. (
  231. Circuit.objects.restrict(request.user, 'view').filter(
  232. terminations___region=instance
  233. ).distinct(),
  234. 'region_id'
  235. ),
  236. (
  237. VLANGroup.objects.restrict(request.user, 'view').filter(
  238. scope_type=ContentType.objects.get_for_model(Region),
  239. scope_id__in=regions
  240. ).distinct(),
  241. 'region'
  242. ),
  243. # Handle these relations manually to avoid erroneous filter name resolution
  244. (
  245. CircuitTermination.objects.restrict(request.user, 'view').filter(_region__in=regions),
  246. 'region_id'
  247. ),
  248. (Cluster.objects.restrict(request.user, 'view').filter(_region__in=regions), 'region_id'),
  249. (Prefix.objects.restrict(request.user, 'view').filter(_region__in=regions), 'region_id'),
  250. (WirelessLAN.objects.restrict(request.user, 'view').filter(_region__in=regions), 'region_id'),
  251. ),
  252. ),
  253. }
  254. @register_model_view(Region, 'add', detail=False)
  255. @register_model_view(Region, 'edit')
  256. class RegionEditView(generic.ObjectEditView):
  257. queryset = Region.objects.all()
  258. form = forms.RegionForm
  259. @register_model_view(Region, 'delete')
  260. class RegionDeleteView(generic.ObjectDeleteView):
  261. queryset = Region.objects.all()
  262. @register_model_view(Region, 'bulk_import', path='import', detail=False)
  263. class RegionBulkImportView(generic.BulkImportView):
  264. queryset = Region.objects.all()
  265. model_form = forms.RegionImportForm
  266. @register_model_view(Region, 'bulk_edit', path='edit', detail=False)
  267. class RegionBulkEditView(generic.BulkEditView):
  268. queryset = Region.objects.add_related_count(
  269. Region.objects.all(),
  270. Site,
  271. 'region',
  272. 'site_count',
  273. cumulative=True
  274. )
  275. filterset = filtersets.RegionFilterSet
  276. table = tables.RegionTable
  277. form = forms.RegionBulkEditForm
  278. @register_model_view(Region, 'bulk_rename', path='rename', detail=False)
  279. class RegionBulkRenameView(generic.BulkRenameView):
  280. queryset = Region.objects.all()
  281. filterset = filtersets.RegionFilterSet
  282. @register_model_view(Region, 'bulk_delete', path='delete', detail=False)
  283. class RegionBulkDeleteView(generic.BulkDeleteView):
  284. queryset = Region.objects.add_related_count(
  285. Region.objects.all(),
  286. Site,
  287. 'region',
  288. 'site_count',
  289. cumulative=True
  290. )
  291. filterset = filtersets.RegionFilterSet
  292. table = tables.RegionTable
  293. #
  294. # Site groups
  295. #
  296. @register_model_view(SiteGroup, 'list', path='', detail=False)
  297. class SiteGroupListView(generic.ObjectListView):
  298. queryset = SiteGroup.objects.add_related_count(
  299. SiteGroup.objects.all(),
  300. Site,
  301. 'group',
  302. 'site_count',
  303. cumulative=True
  304. )
  305. filterset = filtersets.SiteGroupFilterSet
  306. filterset_form = forms.SiteGroupFilterForm
  307. table = tables.SiteGroupTable
  308. @register_model_view(SiteGroup)
  309. class SiteGroupView(GetRelatedModelsMixin, generic.ObjectView):
  310. queryset = SiteGroup.objects.all()
  311. layout = layout.SimpleLayout(
  312. left_panels=[
  313. NestedGroupObjectPanel(),
  314. TagsPanel(),
  315. CustomFieldsPanel(),
  316. CommentsPanel(),
  317. ],
  318. right_panels=[
  319. RelatedObjectsPanel(),
  320. ],
  321. bottom_panels=[
  322. ObjectsTablePanel(
  323. model='dcim.SiteGroup',
  324. title=_('Child Groups'),
  325. filters={'parent_id': lambda ctx: ctx['object'].pk},
  326. actions=[
  327. actions.AddObject('dcim.SiteGroup', url_params={'parent': lambda ctx: ctx['object'].pk}),
  328. ],
  329. ),
  330. ]
  331. )
  332. def get_extra_context(self, request, instance):
  333. groups = instance.get_descendants(include_self=True)
  334. return {
  335. 'related_models': self.get_related_models(
  336. request,
  337. groups,
  338. omit=(Cluster, CircuitTermination, Prefix, WirelessLAN),
  339. extra=(
  340. (Location.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'),
  341. (Rack.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'),
  342. (Device.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'),
  343. (VLAN.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'),
  344. (
  345. ASN.objects.restrict(request.user, 'view').filter(
  346. sites__group__in=groups
  347. ).distinct(),
  348. 'site_group_id'),
  349. (
  350. VirtualMachine.objects.restrict(request.user, 'view').filter(
  351. site__group__in=groups),
  352. 'site_group_id'
  353. ),
  354. (
  355. VLANGroup.objects.restrict(request.user, 'view').filter(
  356. scope_type=ContentType.objects.get_for_model(SiteGroup),
  357. scope_id__in=groups
  358. ).distinct(),
  359. 'site_group'
  360. ),
  361. (
  362. Circuit.objects.restrict(request.user, 'view').filter(
  363. terminations___site_group=instance
  364. ).distinct(),
  365. 'site_group_id'
  366. ),
  367. # Handle these relations manually to avoid erroneous filter name resolution
  368. (
  369. CircuitTermination.objects.restrict(request.user, 'view').filter(_site_group__in=groups),
  370. 'site_group_id'
  371. ),
  372. (
  373. Cluster.objects.restrict(request.user, 'view').filter(_site_group__in=groups),
  374. 'site_group_id'
  375. ),
  376. (
  377. Prefix.objects.restrict(request.user, 'view').filter(_site_group__in=groups),
  378. 'site_group_id'
  379. ),
  380. (
  381. WirelessLAN.objects.restrict(request.user, 'view').filter(_site_group__in=groups),
  382. 'site_group_id'
  383. ),
  384. ),
  385. ),
  386. }
  387. @register_model_view(SiteGroup, 'add', detail=False)
  388. @register_model_view(SiteGroup, 'edit')
  389. class SiteGroupEditView(generic.ObjectEditView):
  390. queryset = SiteGroup.objects.all()
  391. form = forms.SiteGroupForm
  392. @register_model_view(SiteGroup, 'delete')
  393. class SiteGroupDeleteView(generic.ObjectDeleteView):
  394. queryset = SiteGroup.objects.all()
  395. @register_model_view(SiteGroup, 'bulk_import', path='import', detail=False)
  396. class SiteGroupBulkImportView(generic.BulkImportView):
  397. queryset = SiteGroup.objects.all()
  398. model_form = forms.SiteGroupImportForm
  399. @register_model_view(SiteGroup, 'bulk_edit', path='edit', detail=False)
  400. class SiteGroupBulkEditView(generic.BulkEditView):
  401. queryset = SiteGroup.objects.add_related_count(
  402. SiteGroup.objects.all(),
  403. Site,
  404. 'group',
  405. 'site_count',
  406. cumulative=True
  407. )
  408. filterset = filtersets.SiteGroupFilterSet
  409. table = tables.SiteGroupTable
  410. form = forms.SiteGroupBulkEditForm
  411. @register_model_view(SiteGroup, 'bulk_rename', path='rename', detail=False)
  412. class SiteGroupBulkRenameView(generic.BulkRenameView):
  413. queryset = SiteGroup.objects.all()
  414. filterset = filtersets.SiteGroupFilterSet
  415. @register_model_view(SiteGroup, 'bulk_delete', path='delete', detail=False)
  416. class SiteGroupBulkDeleteView(generic.BulkDeleteView):
  417. queryset = SiteGroup.objects.add_related_count(
  418. SiteGroup.objects.all(),
  419. Site,
  420. 'group',
  421. 'site_count',
  422. cumulative=True
  423. )
  424. filterset = filtersets.SiteGroupFilterSet
  425. table = tables.SiteGroupTable
  426. #
  427. # Sites
  428. #
  429. @register_model_view(Site, 'list', path='', detail=False)
  430. class SiteListView(generic.ObjectListView):
  431. queryset = Site.objects.annotate(
  432. device_count=count_related(Device, 'site'),
  433. asn_count=count_related(ASN, 'sites')
  434. )
  435. filterset = filtersets.SiteFilterSet
  436. filterset_form = forms.SiteFilterForm
  437. table = tables.SiteTable
  438. @register_model_view(Site)
  439. class SiteView(GetRelatedModelsMixin, generic.ObjectView):
  440. queryset = Site.objects.prefetch_related('tenant__group')
  441. layout = layout.SimpleLayout(
  442. left_panels=[
  443. panels.SitePanel(),
  444. CustomFieldsPanel(),
  445. TagsPanel(),
  446. CommentsPanel(),
  447. ],
  448. right_panels=[
  449. RelatedObjectsPanel(),
  450. ImageAttachmentsPanel(),
  451. ],
  452. bottom_panels=[
  453. ObjectsTablePanel(
  454. model='dcim.Location',
  455. filters={'site_id': lambda ctx: ctx['object'].pk},
  456. actions=[
  457. actions.AddObject('dcim.Location', url_params={'site': lambda ctx: ctx['object'].pk}),
  458. ],
  459. ),
  460. ObjectsTablePanel(
  461. model='dcim.Device',
  462. title=_('Non-Racked Devices'),
  463. filters={
  464. 'site_id': lambda ctx: ctx['object'].pk,
  465. 'rack_id': settings.FILTERS_NULL_CHOICE_VALUE,
  466. 'parent_bay_id': settings.FILTERS_NULL_CHOICE_VALUE,
  467. },
  468. actions=[
  469. actions.AddObject('dcim.Device', url_params={'site': lambda ctx: ctx['object'].pk}),
  470. ],
  471. ),
  472. ]
  473. )
  474. def get_extra_context(self, request, instance):
  475. return {
  476. 'related_models': self.get_related_models(
  477. request,
  478. instance,
  479. omit=(CableTermination, CircuitTermination, Cluster, Prefix, WirelessLAN),
  480. extra=(
  481. (VLANGroup.objects.restrict(request.user, 'view').filter(
  482. scope_type=ContentType.objects.get_for_model(Site),
  483. scope_id=instance.pk
  484. ), 'site'),
  485. (ASN.objects.restrict(request.user, 'view').filter(sites=instance), 'site_id'),
  486. (
  487. Circuit.objects.restrict(request.user, 'view').filter(terminations___site=instance).distinct(),
  488. 'site_id'
  489. ),
  490. # Handle these relations manually to avoid erroneous filter name resolution
  491. (Cluster.objects.restrict(request.user, 'view').filter(_site=instance), 'site_id'),
  492. (Prefix.objects.restrict(request.user, 'view').filter(_site=instance), 'site_id'),
  493. (WirelessLAN.objects.restrict(request.user, 'view').filter(_site=instance), 'site_id'),
  494. (CircuitTermination.objects.restrict(request.user, 'view').filter(_site=instance), 'site_id'),
  495. ),
  496. ),
  497. }
  498. @register_model_view(Site, 'add', detail=False)
  499. @register_model_view(Site, 'edit')
  500. class SiteEditView(generic.ObjectEditView):
  501. queryset = Site.objects.all()
  502. form = forms.SiteForm
  503. @register_model_view(Site, 'delete')
  504. class SiteDeleteView(generic.ObjectDeleteView):
  505. queryset = Site.objects.all()
  506. @register_model_view(Site, 'bulk_import', path='import', detail=False)
  507. class SiteBulkImportView(generic.BulkImportView):
  508. queryset = Site.objects.all()
  509. model_form = forms.SiteImportForm
  510. @register_model_view(Site, 'bulk_edit', path='edit', detail=False)
  511. class SiteBulkEditView(generic.BulkEditView):
  512. queryset = Site.objects.all()
  513. filterset = filtersets.SiteFilterSet
  514. table = tables.SiteTable
  515. form = forms.SiteBulkEditForm
  516. @register_model_view(Site, 'bulk_rename', path='rename', detail=False)
  517. class SiteBulkRenameView(generic.BulkRenameView):
  518. queryset = Site.objects.all()
  519. filterset = filtersets.SiteFilterSet
  520. @register_model_view(Site, 'bulk_delete', path='delete', detail=False)
  521. class SiteBulkDeleteView(generic.BulkDeleteView):
  522. queryset = Site.objects.all()
  523. filterset = filtersets.SiteFilterSet
  524. table = tables.SiteTable
  525. #
  526. # Locations
  527. #
  528. @register_model_view(Location, 'list', path='', detail=False)
  529. class LocationListView(generic.ObjectListView):
  530. queryset = Location.objects.add_related_count(
  531. Location.objects.add_related_count(
  532. Location.objects.add_related_count(
  533. Location.objects.all(),
  534. Device,
  535. 'location',
  536. 'device_count',
  537. cumulative=True
  538. ),
  539. Rack,
  540. 'location',
  541. 'rack_count',
  542. cumulative=True
  543. ),
  544. VLANGroup,
  545. 'location',
  546. 'vlangroup_count',
  547. cumulative=True
  548. )
  549. filterset = filtersets.LocationFilterSet
  550. filterset_form = forms.LocationFilterForm
  551. table = tables.LocationTable
  552. @register_model_view(Location)
  553. class LocationView(GetRelatedModelsMixin, generic.ObjectView):
  554. queryset = Location.objects.all()
  555. layout = layout.SimpleLayout(
  556. left_panels=[
  557. panels.LocationPanel(),
  558. TagsPanel(),
  559. CustomFieldsPanel(),
  560. CommentsPanel(),
  561. ],
  562. right_panels=[
  563. RelatedObjectsPanel(),
  564. ImageAttachmentsPanel(),
  565. ],
  566. bottom_panels=[
  567. ObjectsTablePanel(
  568. model='dcim.Location',
  569. title=_('Child Locations'),
  570. filters={'parent_id': lambda ctx: ctx['object'].pk},
  571. actions=[
  572. actions.AddObject(
  573. 'dcim.Location',
  574. url_params={
  575. 'site': lambda ctx: ctx['object'].site_id,
  576. 'parent': lambda ctx: ctx['object'].pk,
  577. }
  578. ),
  579. ],
  580. ),
  581. ObjectsTablePanel(
  582. model='dcim.Device',
  583. title=_('Non-Racked Devices'),
  584. filters={
  585. 'location_id': lambda ctx: ctx['object'].pk,
  586. 'rack_id': settings.FILTERS_NULL_CHOICE_VALUE,
  587. 'parent_bay_id': settings.FILTERS_NULL_CHOICE_VALUE,
  588. },
  589. actions=[
  590. actions.AddObject(
  591. 'dcim.Device',
  592. url_params={
  593. 'site': lambda ctx: ctx['object'].site_id,
  594. 'parent': lambda ctx: ctx['object'].pk,
  595. }
  596. ),
  597. ],
  598. ),
  599. ]
  600. )
  601. def get_extra_context(self, request, instance):
  602. locations = instance.get_descendants(include_self=True)
  603. location_content_type = ContentType.objects.get_for_model(instance)
  604. return {
  605. 'related_models': self.get_related_models(
  606. request,
  607. locations,
  608. omit=[CableTermination, CircuitTermination, Cluster, Prefix, WirelessLAN],
  609. extra=(
  610. (
  611. Circuit.objects.restrict(request.user, 'view').filter(
  612. terminations___location=instance
  613. ).distinct(),
  614. 'location_id'
  615. ),
  616. # Handle these relations manually to avoid erroneous filter name resolution
  617. (
  618. CircuitTermination.objects.restrict(request.user, 'view').filter(_location=instance),
  619. 'location_id'
  620. ),
  621. (Cluster.objects.restrict(request.user, 'view').filter(_location=instance), 'location_id'),
  622. (Prefix.objects.restrict(request.user, 'view').filter(_location=instance), 'location_id'),
  623. (WirelessLAN.objects.restrict(request.user, 'view').filter(_location=instance), 'location_id'),
  624. (VLANGroup.objects.restrict(request.user, 'view').filter(
  625. scope_type_id=location_content_type.id, scope_id=instance.id), 'location'),
  626. ),
  627. ),
  628. }
  629. @register_model_view(Location, 'add', detail=False)
  630. @register_model_view(Location, 'edit')
  631. class LocationEditView(generic.ObjectEditView):
  632. queryset = Location.objects.all()
  633. form = forms.LocationForm
  634. @register_model_view(Location, 'delete')
  635. class LocationDeleteView(generic.ObjectDeleteView):
  636. queryset = Location.objects.all()
  637. @register_model_view(Location, 'bulk_import', path='import', detail=False)
  638. class LocationBulkImportView(generic.BulkImportView):
  639. queryset = Location.objects.all()
  640. model_form = forms.LocationImportForm
  641. @register_model_view(Location, 'bulk_edit', path='edit', detail=False)
  642. class LocationBulkEditView(generic.BulkEditView):
  643. queryset = Location.objects.add_related_count(
  644. Location.objects.all(),
  645. Rack,
  646. 'location',
  647. 'rack_count',
  648. cumulative=True
  649. ).prefetch_related('site')
  650. filterset = filtersets.LocationFilterSet
  651. table = tables.LocationTable
  652. form = forms.LocationBulkEditForm
  653. @register_model_view(Location, 'bulk_rename', path='rename', detail=False)
  654. class LocationBulkRenameView(generic.BulkRenameView):
  655. queryset = Location.objects.all()
  656. filterset = filtersets.LocationFilterSet
  657. @register_model_view(Location, 'bulk_delete', path='delete', detail=False)
  658. class LocationBulkDeleteView(generic.BulkDeleteView):
  659. queryset = Location.objects.add_related_count(
  660. Location.objects.all(),
  661. Rack,
  662. 'location',
  663. 'rack_count',
  664. cumulative=True
  665. ).prefetch_related('site')
  666. filterset = filtersets.LocationFilterSet
  667. table = tables.LocationTable
  668. #
  669. # Rack roles
  670. #
  671. @register_model_view(RackRole, 'list', path='', detail=False)
  672. class RackRoleListView(generic.ObjectListView):
  673. queryset = RackRole.objects.annotate(
  674. rack_count=count_related(Rack, 'role')
  675. )
  676. filterset = filtersets.RackRoleFilterSet
  677. filterset_form = forms.RackRoleFilterForm
  678. table = tables.RackRoleTable
  679. @register_model_view(RackRole)
  680. class RackRoleView(GetRelatedModelsMixin, generic.ObjectView):
  681. queryset = RackRole.objects.all()
  682. layout = layout.SimpleLayout(
  683. left_panels=[
  684. panels.RackRolePanel(),
  685. TagsPanel(),
  686. ],
  687. right_panels=[
  688. RelatedObjectsPanel(),
  689. CustomFieldsPanel(),
  690. CommentsPanel(),
  691. ],
  692. )
  693. def get_extra_context(self, request, instance):
  694. return {
  695. 'related_models': self.get_related_models(request, instance),
  696. }
  697. @register_model_view(RackRole, 'add', detail=False)
  698. @register_model_view(RackRole, 'edit')
  699. class RackRoleEditView(generic.ObjectEditView):
  700. queryset = RackRole.objects.all()
  701. form = forms.RackRoleForm
  702. @register_model_view(RackRole, 'delete')
  703. class RackRoleDeleteView(generic.ObjectDeleteView):
  704. queryset = RackRole.objects.all()
  705. @register_model_view(RackRole, 'bulk_import', path='import', detail=False)
  706. class RackRoleBulkImportView(generic.BulkImportView):
  707. queryset = RackRole.objects.all()
  708. model_form = forms.RackRoleImportForm
  709. @register_model_view(RackRole, 'bulk_edit', path='edit', detail=False)
  710. class RackRoleBulkEditView(generic.BulkEditView):
  711. queryset = RackRole.objects.annotate(
  712. rack_count=count_related(Rack, 'role')
  713. )
  714. filterset = filtersets.RackRoleFilterSet
  715. table = tables.RackRoleTable
  716. form = forms.RackRoleBulkEditForm
  717. @register_model_view(RackRole, 'bulk_rename', path='rename', detail=False)
  718. class RackRoleBulkRenameView(generic.BulkRenameView):
  719. queryset = RackRole.objects.all()
  720. filterset = filtersets.RackRoleFilterSet
  721. @register_model_view(RackRole, 'bulk_delete', path='delete', detail=False)
  722. class RackRoleBulkDeleteView(generic.BulkDeleteView):
  723. queryset = RackRole.objects.annotate(
  724. rack_count=count_related(Rack, 'role')
  725. )
  726. filterset = filtersets.RackRoleFilterSet
  727. table = tables.RackRoleTable
  728. #
  729. # RackTypes
  730. #
  731. @register_model_view(RackType, 'list', path='', detail=False)
  732. class RackTypeListView(generic.ObjectListView):
  733. queryset = RackType.objects.all()
  734. filterset = filtersets.RackTypeFilterSet
  735. filterset_form = forms.RackTypeFilterForm
  736. table = tables.RackTypeTable
  737. @register_model_view(RackType)
  738. class RackTypeView(GetRelatedModelsMixin, generic.ObjectView):
  739. template_name = 'generic/object.html'
  740. queryset = RackType.objects.all()
  741. layout = layout.SimpleLayout(
  742. left_panels=[
  743. panels.RackTypePanel(),
  744. panels.RackDimensionsPanel(title=_('Dimensions')),
  745. TagsPanel(),
  746. CommentsPanel(),
  747. ],
  748. right_panels=[
  749. panels.RackNumberingPanel(title=_('Numbering')),
  750. panels.RackWeightPanel(title=_('Weight'), exclude=['total_weight']),
  751. CustomFieldsPanel(),
  752. RelatedObjectsPanel(),
  753. ImageAttachmentsPanel(),
  754. ],
  755. )
  756. def get_extra_context(self, request, instance):
  757. return {
  758. 'related_models': self.get_related_models(request, instance),
  759. }
  760. @register_model_view(RackType, 'add', detail=False)
  761. @register_model_view(RackType, 'edit')
  762. class RackTypeEditView(generic.ObjectEditView):
  763. queryset = RackType.objects.all()
  764. form = forms.RackTypeForm
  765. @register_model_view(RackType, 'delete')
  766. class RackTypeDeleteView(generic.ObjectDeleteView):
  767. queryset = RackType.objects.all()
  768. @register_model_view(RackType, 'bulk_import', path='import', detail=False)
  769. class RackTypeBulkImportView(generic.BulkImportView):
  770. queryset = RackType.objects.all()
  771. model_form = forms.RackTypeImportForm
  772. @register_model_view(RackType, 'bulk_edit', path='edit', detail=False)
  773. class RackTypeBulkEditView(generic.BulkEditView):
  774. queryset = RackType.objects.all()
  775. filterset = filtersets.RackTypeFilterSet
  776. table = tables.RackTypeTable
  777. form = forms.RackTypeBulkEditForm
  778. @register_model_view(RackType, 'bulk_rename', path='rename', detail=False)
  779. class RackTypeBulkRenameView(generic.BulkRenameView):
  780. queryset = RackType.objects.all()
  781. field_name = 'model'
  782. filterset = filtersets.RackTypeFilterSet
  783. @register_model_view(RackType, 'bulk_delete', path='delete', detail=False)
  784. class RackTypeBulkDeleteView(generic.BulkDeleteView):
  785. queryset = RackType.objects.all()
  786. filterset = filtersets.RackTypeFilterSet
  787. table = tables.RackTypeTable
  788. #
  789. # Racks
  790. #
  791. @register_model_view(Rack, 'list', path='', detail=False)
  792. class RackListView(generic.ObjectListView):
  793. queryset = Rack.objects.annotate(
  794. device_count=count_related(Device, 'rack')
  795. )
  796. filterset = filtersets.RackFilterSet
  797. filterset_form = forms.RackFilterForm
  798. table = tables.RackTable
  799. template_name = 'dcim/rack_list.html'
  800. class RackElevationListView(generic.ObjectListView):
  801. """
  802. Display a set of rack elevations side-by-side.
  803. """
  804. queryset = Rack.objects.prefetch_related('role')
  805. def get(self, request):
  806. racks = filtersets.RackFilterSet(request.GET, self.queryset).qs
  807. total_count = racks.count()
  808. # Ordering
  809. ORDERING_CHOICES = {
  810. 'name': 'Name (A-Z)',
  811. '-name': 'Name (Z-A)',
  812. 'facility_id': 'Facility ID (A-Z)',
  813. '-facility_id': 'Facility ID (Z-A)',
  814. }
  815. sort = request.GET.get('sort', 'name')
  816. if sort not in ORDERING_CHOICES:
  817. sort = 'name'
  818. racks = racks.order_by(sort)
  819. # Pagination
  820. per_page = get_paginate_count(request)
  821. page_number = request.GET.get('page', 1)
  822. paginator = EnhancedPaginator(racks, per_page)
  823. try:
  824. page = paginator.page(page_number)
  825. except PageNotAnInteger:
  826. page = paginator.page(1)
  827. except EmptyPage:
  828. page = paginator.page(paginator.num_pages)
  829. # Determine rack face
  830. rack_face = request.GET.get('face', DeviceFaceChoices.FACE_FRONT)
  831. if rack_face not in DeviceFaceChoices.values():
  832. rack_face = DeviceFaceChoices.FACE_FRONT
  833. return render(request, 'dcim/rack_elevation_list.html', {
  834. 'paginator': paginator,
  835. 'page': page,
  836. 'total_count': total_count,
  837. 'sort': sort,
  838. 'sort_display_name': ORDERING_CHOICES[sort],
  839. 'sort_choices': ORDERING_CHOICES,
  840. 'rack_face': rack_face,
  841. 'filter_form': forms.RackElevationFilterForm(request.GET),
  842. 'model': self.queryset.model,
  843. })
  844. @register_model_view(Rack)
  845. class RackView(GetRelatedModelsMixin, generic.ObjectView):
  846. queryset = Rack.objects.prefetch_related('site__region', 'tenant__group', 'location', 'role')
  847. layout = layout.SimpleLayout(
  848. left_panels=[
  849. panels.RackPanel(),
  850. panels.RackDimensionsPanel(title=_('Dimensions')),
  851. panels.RackNumberingPanel(title=_('Numbering')),
  852. panels.RackWeightPanel(title=_('Weight')),
  853. CustomFieldsPanel(),
  854. TagsPanel(),
  855. CommentsPanel(),
  856. ImageAttachmentsPanel(),
  857. ],
  858. right_panels=[
  859. TemplatePanel('dcim/panels/rack_elevations.html'),
  860. RelatedObjectsPanel(),
  861. ],
  862. )
  863. def get_extra_context(self, request, instance):
  864. peer_racks = Rack.objects.restrict(request.user, 'view').filter(site=instance.site)
  865. if instance.location:
  866. peer_racks = peer_racks.filter(location=instance.location)
  867. else:
  868. peer_racks = peer_racks.filter(location__isnull=True)
  869. next_rack = peer_racks.filter(name__gt=instance.name).first()
  870. prev_rack = peer_racks.filter(name__lt=instance.name).reverse().first()
  871. # Determine any additional parameters to pass when embedding the rack elevations
  872. svg_extra = '&'.join([
  873. f'highlight=id:{pk}' for pk in request.GET.getlist('device')
  874. ])
  875. return {
  876. 'related_models': self.get_related_models(
  877. request,
  878. instance,
  879. omit=(CableTermination,),
  880. extra=(
  881. (
  882. VLANGroup.objects.restrict(request.user, 'view').filter(
  883. scope_type=ContentType.objects.get_for_model(Rack),
  884. scope_id=instance.pk
  885. ), 'rack'),
  886. ),
  887. ),
  888. 'next_rack': next_rack,
  889. 'prev_rack': prev_rack,
  890. 'svg_extra': svg_extra,
  891. 'peer_racks': peer_racks,
  892. }
  893. @register_model_view(Rack, 'reservations')
  894. class RackRackReservationsView(generic.ObjectChildrenView):
  895. queryset = Rack.objects.all()
  896. child_model = RackReservation
  897. table = tables.RackReservationTable
  898. filterset = filtersets.RackReservationFilterSet
  899. filterset_form = forms.RackReservationFilterForm
  900. template_name = 'dcim/rack/reservations.html'
  901. tab = ViewTab(
  902. label=_('Reservations'),
  903. badge=lambda obj: obj.reservations.count(),
  904. permission='dcim.view_rackreservation',
  905. weight=510
  906. )
  907. def get_children(self, request, parent):
  908. return parent.reservations.restrict(request.user, 'view')
  909. @register_model_view(Rack, 'nonracked_devices', 'nonracked-devices')
  910. class RackNonRackedView(generic.ObjectChildrenView):
  911. queryset = Rack.objects.all()
  912. child_model = Device
  913. table = tables.DeviceTable
  914. filterset = filtersets.DeviceFilterSet
  915. filterset_form = forms.DeviceFilterForm
  916. template_name = 'dcim/rack/non_racked_devices.html'
  917. tab = ViewTab(
  918. label=_('Non-Racked Devices'),
  919. badge=lambda obj: obj.devices.filter(rack=obj, position__isnull=True, parent_bay__isnull=True).count(),
  920. weight=500,
  921. permission='dcim.view_device',
  922. )
  923. def get_children(self, request, parent):
  924. return parent.devices.restrict(request.user, 'view').filter(
  925. rack=parent, position__isnull=True, parent_bay__isnull=True
  926. )
  927. @register_model_view(Rack, 'add', detail=False)
  928. @register_model_view(Rack, 'edit')
  929. class RackEditView(generic.ObjectEditView):
  930. queryset = Rack.objects.all()
  931. form = forms.RackForm
  932. @register_model_view(Rack, 'delete')
  933. class RackDeleteView(generic.ObjectDeleteView):
  934. queryset = Rack.objects.all()
  935. @register_model_view(Rack, 'bulk_import', path='import', detail=False)
  936. class RackBulkImportView(generic.BulkImportView):
  937. queryset = Rack.objects.all()
  938. model_form = forms.RackImportForm
  939. @register_model_view(Rack, 'bulk_edit', path='edit', detail=False)
  940. class RackBulkEditView(generic.BulkEditView):
  941. queryset = Rack.objects.all()
  942. filterset = filtersets.RackFilterSet
  943. table = tables.RackTable
  944. form = forms.RackBulkEditForm
  945. @register_model_view(Rack, 'bulk_rename', path='rename', detail=False)
  946. class RackBulkRenameView(generic.BulkRenameView):
  947. queryset = Rack.objects.all()
  948. filterset = filtersets.RackFilterSet
  949. @register_model_view(Rack, 'bulk_delete', path='delete', detail=False)
  950. class RackBulkDeleteView(generic.BulkDeleteView):
  951. queryset = Rack.objects.all()
  952. filterset = filtersets.RackFilterSet
  953. table = tables.RackTable
  954. #
  955. # Rack reservations
  956. #
  957. @register_model_view(RackReservation, 'list', path='', detail=False)
  958. class RackReservationListView(generic.ObjectListView):
  959. queryset = RackReservation.objects.all()
  960. filterset = filtersets.RackReservationFilterSet
  961. filterset_form = forms.RackReservationFilterForm
  962. table = tables.RackReservationTable
  963. actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete)
  964. @register_model_view(RackReservation)
  965. class RackReservationView(generic.ObjectView):
  966. queryset = RackReservation.objects.all()
  967. layout = layout.SimpleLayout(
  968. left_panels=[
  969. panels.RackPanel(accessor='object.rack', only=['region', 'site', 'location', 'name']),
  970. panels.RackReservationPanel(title=_('Reservation')),
  971. CustomFieldsPanel(),
  972. TagsPanel(),
  973. CommentsPanel(),
  974. ],
  975. right_panels=[
  976. TemplatePanel(template_name='dcim/panels/rack_reservation_elevations.html'),
  977. RelatedObjectsPanel(),
  978. ],
  979. )
  980. @register_model_view(RackReservation, 'add', detail=False)
  981. @register_model_view(RackReservation, 'edit')
  982. class RackReservationEditView(generic.ObjectEditView):
  983. queryset = RackReservation.objects.all()
  984. form = forms.RackReservationForm
  985. def alter_object(self, obj, request, args, kwargs):
  986. if not obj.pk:
  987. if 'rack' in request.GET:
  988. obj.rack = get_object_or_404(Rack, pk=request.GET.get('rack'))
  989. obj.user = request.user
  990. return obj
  991. @register_model_view(RackReservation, 'delete')
  992. class RackReservationDeleteView(generic.ObjectDeleteView):
  993. queryset = RackReservation.objects.all()
  994. @register_model_view(RackReservation, 'bulk_import', path='import', detail=False)
  995. class RackReservationImportView(generic.BulkImportView):
  996. queryset = RackReservation.objects.all()
  997. model_form = forms.RackReservationImportForm
  998. def save_object(self, object_form, request):
  999. """
  1000. Assign the currently authenticated user to the RackReservation.
  1001. """
  1002. instance = object_form.save(commit=False)
  1003. instance.user = request.user
  1004. instance.save()
  1005. return instance
  1006. @register_model_view(RackReservation, 'bulk_edit', path='edit', detail=False)
  1007. class RackReservationBulkEditView(generic.BulkEditView):
  1008. queryset = RackReservation.objects.all()
  1009. filterset = filtersets.RackReservationFilterSet
  1010. table = tables.RackReservationTable
  1011. form = forms.RackReservationBulkEditForm
  1012. @register_model_view(RackReservation, 'bulk_delete', path='delete', detail=False)
  1013. class RackReservationBulkDeleteView(generic.BulkDeleteView):
  1014. queryset = RackReservation.objects.all()
  1015. filterset = filtersets.RackReservationFilterSet
  1016. table = tables.RackReservationTable
  1017. #
  1018. # Manufacturers
  1019. #
  1020. @register_model_view(Manufacturer, 'list', path='', detail=False)
  1021. class ManufacturerListView(generic.ObjectListView):
  1022. queryset = Manufacturer.objects.annotate(
  1023. racktype_count=count_related(RackType, 'manufacturer'),
  1024. devicetype_count=count_related(DeviceType, 'manufacturer'),
  1025. moduletype_count=count_related(ModuleType, 'manufacturer'),
  1026. inventoryitem_count=count_related(InventoryItem, 'manufacturer'),
  1027. platform_count=count_related(Platform, 'manufacturer')
  1028. )
  1029. filterset = filtersets.ManufacturerFilterSet
  1030. filterset_form = forms.ManufacturerFilterForm
  1031. table = tables.ManufacturerTable
  1032. @register_model_view(Manufacturer)
  1033. class ManufacturerView(GetRelatedModelsMixin, generic.ObjectView):
  1034. queryset = Manufacturer.objects.all()
  1035. layout = layout.SimpleLayout(
  1036. left_panels=[OrganizationalObjectPanel(), TagsPanel()],
  1037. right_panels=[RelatedObjectsPanel(), CustomFieldsPanel(), CommentsPanel()],
  1038. )
  1039. def get_extra_context(self, request, instance):
  1040. return {
  1041. 'related_models': self.get_related_models(request, instance, [InventoryItemTemplate]),
  1042. }
  1043. @register_model_view(Manufacturer, 'add', detail=False)
  1044. @register_model_view(Manufacturer, 'edit')
  1045. class ManufacturerEditView(generic.ObjectEditView):
  1046. queryset = Manufacturer.objects.all()
  1047. form = forms.ManufacturerForm
  1048. @register_model_view(Manufacturer, 'delete')
  1049. class ManufacturerDeleteView(generic.ObjectDeleteView):
  1050. queryset = Manufacturer.objects.all()
  1051. @register_model_view(Manufacturer, 'bulk_import', path='import', detail=False)
  1052. class ManufacturerBulkImportView(generic.BulkImportView):
  1053. queryset = Manufacturer.objects.all()
  1054. model_form = forms.ManufacturerImportForm
  1055. @register_model_view(Manufacturer, 'bulk_edit', path='edit', detail=False)
  1056. class ManufacturerBulkEditView(generic.BulkEditView):
  1057. queryset = Manufacturer.objects.annotate(
  1058. devicetype_count=count_related(DeviceType, 'manufacturer'),
  1059. moduletype_count=count_related(ModuleType, 'manufacturer'),
  1060. inventoryitem_count=count_related(InventoryItem, 'manufacturer'),
  1061. platform_count=count_related(Platform, 'manufacturer')
  1062. )
  1063. filterset = filtersets.ManufacturerFilterSet
  1064. table = tables.ManufacturerTable
  1065. form = forms.ManufacturerBulkEditForm
  1066. @register_model_view(Manufacturer, 'bulk_rename', path='rename', detail=False)
  1067. class ManufacturerBulkRenameView(generic.BulkRenameView):
  1068. queryset = Manufacturer.objects.all()
  1069. filterset = filtersets.ManufacturerFilterSet
  1070. @register_model_view(Manufacturer, 'bulk_delete', path='delete', detail=False)
  1071. class ManufacturerBulkDeleteView(generic.BulkDeleteView):
  1072. queryset = Manufacturer.objects.annotate(
  1073. devicetype_count=count_related(DeviceType, 'manufacturer'),
  1074. moduletype_count=count_related(ModuleType, 'manufacturer'),
  1075. inventoryitem_count=count_related(InventoryItem, 'manufacturer'),
  1076. platform_count=count_related(Platform, 'manufacturer')
  1077. )
  1078. filterset = filtersets.ManufacturerFilterSet
  1079. table = tables.ManufacturerTable
  1080. #
  1081. # Device types
  1082. #
  1083. @register_model_view(DeviceType, 'list', path='', detail=False)
  1084. class DeviceTypeListView(generic.ObjectListView):
  1085. queryset = DeviceType.objects.all()
  1086. filterset = filtersets.DeviceTypeFilterSet
  1087. filterset_form = forms.DeviceTypeFilterForm
  1088. table = tables.DeviceTypeTable
  1089. @register_model_view(DeviceType)
  1090. class DeviceTypeView(GetRelatedModelsMixin, generic.ObjectView):
  1091. queryset = DeviceType.objects.all()
  1092. layout = layout.SimpleLayout(
  1093. left_panels=[
  1094. panels.DeviceTypePanel(),
  1095. TagsPanel(),
  1096. ],
  1097. right_panels=[
  1098. RelatedObjectsPanel(),
  1099. CustomFieldsPanel(),
  1100. CommentsPanel(),
  1101. ImageAttachmentsPanel(),
  1102. ],
  1103. )
  1104. def get_extra_context(self, request, instance):
  1105. return {
  1106. 'related_models': self.get_related_models(request, instance, omit=[
  1107. ConsolePortTemplate, ConsoleServerPortTemplate, DeviceBayTemplate, FrontPortTemplate,
  1108. InventoryItemTemplate, InterfaceTemplate, ModuleBayTemplate, PowerOutletTemplate, PowerPortTemplate,
  1109. RearPortTemplate,
  1110. ]),
  1111. }
  1112. @register_model_view(DeviceType, 'add', detail=False)
  1113. @register_model_view(DeviceType, 'edit')
  1114. class DeviceTypeEditView(generic.ObjectEditView):
  1115. queryset = DeviceType.objects.all()
  1116. form = forms.DeviceTypeForm
  1117. @register_model_view(DeviceType, 'delete')
  1118. class DeviceTypeDeleteView(generic.ObjectDeleteView):
  1119. queryset = DeviceType.objects.all()
  1120. @register_model_view(DeviceType, 'consoleports', path='console-ports')
  1121. class DeviceTypeConsolePortsView(DeviceTypeComponentsView):
  1122. child_model = ConsolePortTemplate
  1123. table = tables.ConsolePortTemplateTable
  1124. filterset = filtersets.ConsolePortTemplateFilterSet
  1125. viewname = 'dcim:devicetype_consoleports'
  1126. tab = ViewTab(
  1127. label=_('Console Ports'),
  1128. badge=lambda obj: obj.console_port_template_count,
  1129. permission='dcim.view_consoleporttemplate',
  1130. weight=550,
  1131. hide_if_empty=True
  1132. )
  1133. @register_model_view(DeviceType, 'consoleserverports', path='console-server-ports')
  1134. class DeviceTypeConsoleServerPortsView(DeviceTypeComponentsView):
  1135. child_model = ConsoleServerPortTemplate
  1136. table = tables.ConsoleServerPortTemplateTable
  1137. filterset = filtersets.ConsoleServerPortTemplateFilterSet
  1138. viewname = 'dcim:devicetype_consoleserverports'
  1139. tab = ViewTab(
  1140. label=_('Console Server Ports'),
  1141. badge=lambda obj: obj.console_server_port_template_count,
  1142. permission='dcim.view_consoleserverporttemplate',
  1143. weight=560,
  1144. hide_if_empty=True
  1145. )
  1146. @register_model_view(DeviceType, 'powerports', path='power-ports')
  1147. class DeviceTypePowerPortsView(DeviceTypeComponentsView):
  1148. child_model = PowerPortTemplate
  1149. table = tables.PowerPortTemplateTable
  1150. filterset = filtersets.PowerPortTemplateFilterSet
  1151. viewname = 'dcim:devicetype_powerports'
  1152. tab = ViewTab(
  1153. label=_('Power Ports'),
  1154. badge=lambda obj: obj.power_port_template_count,
  1155. permission='dcim.view_powerporttemplate',
  1156. weight=570,
  1157. hide_if_empty=True
  1158. )
  1159. @register_model_view(DeviceType, 'poweroutlets', path='power-outlets')
  1160. class DeviceTypePowerOutletsView(DeviceTypeComponentsView):
  1161. child_model = PowerOutletTemplate
  1162. table = tables.PowerOutletTemplateTable
  1163. filterset = filtersets.PowerOutletTemplateFilterSet
  1164. viewname = 'dcim:devicetype_poweroutlets'
  1165. tab = ViewTab(
  1166. label=_('Power Outlets'),
  1167. badge=lambda obj: obj.power_outlet_template_count,
  1168. permission='dcim.view_poweroutlettemplate',
  1169. weight=580,
  1170. hide_if_empty=True
  1171. )
  1172. @register_model_view(DeviceType, 'interfaces')
  1173. class DeviceTypeInterfacesView(DeviceTypeComponentsView):
  1174. child_model = InterfaceTemplate
  1175. table = tables.InterfaceTemplateTable
  1176. filterset = filtersets.InterfaceTemplateFilterSet
  1177. viewname = 'dcim:devicetype_interfaces'
  1178. tab = ViewTab(
  1179. label=_('Interfaces'),
  1180. badge=lambda obj: obj.interface_template_count,
  1181. permission='dcim.view_interfacetemplate',
  1182. weight=520,
  1183. hide_if_empty=True
  1184. )
  1185. @register_model_view(DeviceType, 'frontports', path='front-ports')
  1186. class DeviceTypeFrontPortsView(DeviceTypeComponentsView):
  1187. child_model = FrontPortTemplate
  1188. table = tables.FrontPortTemplateTable
  1189. filterset = filtersets.FrontPortTemplateFilterSet
  1190. viewname = 'dcim:devicetype_frontports'
  1191. tab = ViewTab(
  1192. label=_('Front Ports'),
  1193. badge=lambda obj: obj.front_port_template_count,
  1194. permission='dcim.view_frontporttemplate',
  1195. weight=530,
  1196. hide_if_empty=True
  1197. )
  1198. @register_model_view(DeviceType, 'rearports', path='rear-ports')
  1199. class DeviceTypeRearPortsView(DeviceTypeComponentsView):
  1200. child_model = RearPortTemplate
  1201. table = tables.RearPortTemplateTable
  1202. filterset = filtersets.RearPortTemplateFilterSet
  1203. viewname = 'dcim:devicetype_rearports'
  1204. tab = ViewTab(
  1205. label=_('Rear Ports'),
  1206. badge=lambda obj: obj.rear_port_template_count,
  1207. permission='dcim.view_rearporttemplate',
  1208. weight=540,
  1209. hide_if_empty=True
  1210. )
  1211. @register_model_view(DeviceType, 'modulebays', path='module-bays')
  1212. class DeviceTypeModuleBaysView(DeviceTypeComponentsView):
  1213. child_model = ModuleBayTemplate
  1214. table = tables.ModuleBayTemplateTable
  1215. filterset = filtersets.ModuleBayTemplateFilterSet
  1216. viewname = 'dcim:devicetype_modulebays'
  1217. tab = ViewTab(
  1218. label=_('Module Bays'),
  1219. badge=lambda obj: obj.module_bay_template_count,
  1220. permission='dcim.view_modulebaytemplate',
  1221. weight=510,
  1222. hide_if_empty=True
  1223. )
  1224. @register_model_view(DeviceType, 'devicebays', path='device-bays')
  1225. class DeviceTypeDeviceBaysView(DeviceTypeComponentsView):
  1226. child_model = DeviceBayTemplate
  1227. table = tables.DeviceBayTemplateTable
  1228. filterset = filtersets.DeviceBayTemplateFilterSet
  1229. viewname = 'dcim:devicetype_devicebays'
  1230. tab = ViewTab(
  1231. label=_('Device Bays'),
  1232. badge=lambda obj: obj.device_bay_template_count,
  1233. permission='dcim.view_devicebaytemplate',
  1234. weight=500,
  1235. hide_if_empty=True
  1236. )
  1237. @register_model_view(DeviceType, 'inventoryitems', path='inventory-items')
  1238. class DeviceTypeInventoryItemsView(DeviceTypeComponentsView):
  1239. child_model = InventoryItemTemplate
  1240. table = tables.InventoryItemTemplateTable
  1241. filterset = filtersets.InventoryItemTemplateFilterSet
  1242. viewname = 'dcim:devicetype_inventoryitems'
  1243. tab = ViewTab(
  1244. label=_('Inventory Items'),
  1245. badge=lambda obj: obj.inventory_item_template_count,
  1246. permission='dcim.view_inventoryitemtemplate',
  1247. weight=590,
  1248. hide_if_empty=True
  1249. )
  1250. @register_model_view(DeviceType, 'bulk_import', path='import', detail=False)
  1251. class DeviceTypeImportView(generic.BulkImportView):
  1252. additional_permissions = [
  1253. 'dcim.add_devicetype',
  1254. 'dcim.add_consoleporttemplate',
  1255. 'dcim.add_consoleserverporttemplate',
  1256. 'dcim.add_powerporttemplate',
  1257. 'dcim.add_poweroutlettemplate',
  1258. 'dcim.add_interfacetemplate',
  1259. 'dcim.add_frontporttemplate',
  1260. 'dcim.add_rearporttemplate',
  1261. 'dcim.add_modulebaytemplate',
  1262. 'dcim.add_devicebaytemplate',
  1263. 'dcim.add_inventoryitemtemplate',
  1264. ]
  1265. queryset = DeviceType.objects.all()
  1266. model_form = forms.DeviceTypeImportForm
  1267. related_object_forms = {
  1268. 'console-ports': forms.ConsolePortTemplateImportForm,
  1269. 'console-server-ports': forms.ConsoleServerPortTemplateImportForm,
  1270. 'power-ports': forms.PowerPortTemplateImportForm,
  1271. 'power-outlets': forms.PowerOutletTemplateImportForm,
  1272. 'interfaces': forms.InterfaceTemplateImportForm,
  1273. 'rear-ports': forms.RearPortTemplateImportForm,
  1274. 'front-ports': forms.FrontPortTemplateImportForm,
  1275. 'port-mappings': forms.PortTemplateMappingImportForm,
  1276. 'module-bays': forms.ModuleBayTemplateImportForm,
  1277. 'device-bays': forms.DeviceBayTemplateImportForm,
  1278. 'inventory-items': forms.InventoryItemTemplateImportForm,
  1279. }
  1280. def prep_related_object_data(self, parent, data):
  1281. data.update({'device_type': parent})
  1282. return data
  1283. @register_model_view(DeviceType, 'bulk_edit', path='edit', detail=False)
  1284. class DeviceTypeBulkEditView(generic.BulkEditView):
  1285. queryset = DeviceType.objects.all()
  1286. filterset = filtersets.DeviceTypeFilterSet
  1287. table = tables.DeviceTypeTable
  1288. form = forms.DeviceTypeBulkEditForm
  1289. @register_model_view(DeviceType, 'bulk_rename', path='rename', detail=False)
  1290. class DeviceTypeBulkRenameView(generic.BulkRenameView):
  1291. queryset = DeviceType.objects.all()
  1292. field_name = 'model'
  1293. filterset = filtersets.DeviceTypeFilterSet
  1294. @register_model_view(DeviceType, 'bulk_delete', path='delete', detail=False)
  1295. class DeviceTypeBulkDeleteView(generic.BulkDeleteView):
  1296. queryset = DeviceType.objects.all()
  1297. filterset = filtersets.DeviceTypeFilterSet
  1298. table = tables.DeviceTypeTable
  1299. #
  1300. # Module type profiles
  1301. #
  1302. @register_model_view(ModuleTypeProfile, 'list', path='', detail=False)
  1303. class ModuleTypeProfileListView(generic.ObjectListView):
  1304. queryset = ModuleTypeProfile.objects.annotate(
  1305. instance_count=count_related(ModuleType, 'profile')
  1306. )
  1307. filterset = filtersets.ModuleTypeProfileFilterSet
  1308. filterset_form = forms.ModuleTypeProfileFilterForm
  1309. table = tables.ModuleTypeProfileTable
  1310. @register_model_view(ModuleTypeProfile)
  1311. class ModuleTypeProfileView(GetRelatedModelsMixin, generic.ObjectView):
  1312. template_name = 'generic/object.html'
  1313. queryset = ModuleTypeProfile.objects.all()
  1314. layout = layout.SimpleLayout(
  1315. left_panels=[
  1316. panels.ModuleTypeProfilePanel(),
  1317. TagsPanel(),
  1318. CommentsPanel(),
  1319. ],
  1320. right_panels=[
  1321. JSONPanel(field_name='schema', title=_('Schema')),
  1322. CustomFieldsPanel(),
  1323. ],
  1324. bottom_panels=[
  1325. ObjectsTablePanel(
  1326. model='dcim.ModuleType',
  1327. title=_('Module Types'),
  1328. filters={
  1329. 'profile_id': lambda ctx: ctx['object'].pk,
  1330. },
  1331. actions=[
  1332. actions.AddObject(
  1333. 'dcim.ModuleType',
  1334. url_params={
  1335. 'profile': lambda ctx: ctx['object'].pk,
  1336. }
  1337. ),
  1338. ],
  1339. ),
  1340. ]
  1341. )
  1342. @register_model_view(ModuleTypeProfile, 'add', detail=False)
  1343. @register_model_view(ModuleTypeProfile, 'edit')
  1344. class ModuleTypeProfileEditView(generic.ObjectEditView):
  1345. queryset = ModuleTypeProfile.objects.all()
  1346. form = forms.ModuleTypeProfileForm
  1347. @register_model_view(ModuleTypeProfile, 'delete')
  1348. class ModuleTypeProfileDeleteView(generic.ObjectDeleteView):
  1349. queryset = ModuleTypeProfile.objects.all()
  1350. @register_model_view(ModuleTypeProfile, 'bulk_import', detail=False)
  1351. class ModuleTypeProfileBulkImportView(generic.BulkImportView):
  1352. queryset = ModuleTypeProfile.objects.all()
  1353. model_form = forms.ModuleTypeProfileImportForm
  1354. @register_model_view(ModuleTypeProfile, 'bulk_edit', path='edit', detail=False)
  1355. class ModuleTypeProfileBulkEditView(generic.BulkEditView):
  1356. queryset = ModuleTypeProfile.objects.annotate(
  1357. instance_count=count_related(Module, 'module_type')
  1358. )
  1359. filterset = filtersets.ModuleTypeProfileFilterSet
  1360. table = tables.ModuleTypeProfileTable
  1361. form = forms.ModuleTypeProfileBulkEditForm
  1362. @register_model_view(ModuleTypeProfile, 'bulk_rename', path='rename', detail=False)
  1363. class ModuleTypeProfileBulkRenameView(generic.BulkRenameView):
  1364. queryset = ModuleTypeProfile.objects.all()
  1365. filterset = filtersets.ModuleTypeProfileFilterSet
  1366. @register_model_view(ModuleTypeProfile, 'bulk_delete', path='delete', detail=False)
  1367. class ModuleTypeProfileBulkDeleteView(generic.BulkDeleteView):
  1368. queryset = ModuleTypeProfile.objects.annotate(
  1369. instance_count=count_related(Module, 'module_type')
  1370. )
  1371. filterset = filtersets.ModuleTypeProfileFilterSet
  1372. table = tables.ModuleTypeProfileTable
  1373. #
  1374. # Module types
  1375. #
  1376. @register_model_view(ModuleType, 'list', path='', detail=False)
  1377. class ModuleTypeListView(generic.ObjectListView):
  1378. queryset = ModuleType.objects.all()
  1379. filterset = filtersets.ModuleTypeFilterSet
  1380. filterset_form = forms.ModuleTypeFilterForm
  1381. table = tables.ModuleTypeTable
  1382. @register_model_view(ModuleType)
  1383. class ModuleTypeView(GetRelatedModelsMixin, generic.ObjectView):
  1384. queryset = ModuleType.objects.all()
  1385. layout = layout.SimpleLayout(
  1386. left_panels=[
  1387. panels.ModuleTypePanel(),
  1388. TagsPanel(),
  1389. CommentsPanel(),
  1390. ],
  1391. right_panels=[
  1392. Panel(
  1393. title=_('Attributes'),
  1394. template_name='dcim/panels/module_type_attributes.html',
  1395. ),
  1396. RelatedObjectsPanel(),
  1397. CustomFieldsPanel(),
  1398. ImageAttachmentsPanel(),
  1399. ],
  1400. )
  1401. def get_extra_context(self, request, instance):
  1402. return {
  1403. 'related_models': self.get_related_models(request, instance, omit=[
  1404. ConsolePortTemplate, ConsoleServerPortTemplate, DeviceBayTemplate, FrontPortTemplate,
  1405. InventoryItemTemplate, InterfaceTemplate, ModuleBayTemplate, PowerOutletTemplate, PowerPortTemplate,
  1406. RearPortTemplate,
  1407. ]),
  1408. }
  1409. @register_model_view(ModuleType, 'add', detail=False)
  1410. @register_model_view(ModuleType, 'edit')
  1411. class ModuleTypeEditView(generic.ObjectEditView):
  1412. queryset = ModuleType.objects.all()
  1413. form = forms.ModuleTypeForm
  1414. @register_model_view(ModuleType, 'delete')
  1415. class ModuleTypeDeleteView(generic.ObjectDeleteView):
  1416. queryset = ModuleType.objects.all()
  1417. @register_model_view(ModuleType, 'consoleports', path='console-ports')
  1418. class ModuleTypeConsolePortsView(ModuleTypeComponentsView):
  1419. child_model = ConsolePortTemplate
  1420. table = tables.ConsolePortTemplateTable
  1421. filterset = filtersets.ConsolePortTemplateFilterSet
  1422. viewname = 'dcim:moduletype_consoleports'
  1423. tab = ViewTab(
  1424. label=_('Console Ports'),
  1425. badge=lambda obj: obj.consoleporttemplates.count(),
  1426. permission='dcim.view_consoleporttemplate',
  1427. weight=530,
  1428. hide_if_empty=True
  1429. )
  1430. @register_model_view(ModuleType, 'consoleserverports', path='console-server-ports')
  1431. class ModuleTypeConsoleServerPortsView(ModuleTypeComponentsView):
  1432. child_model = ConsoleServerPortTemplate
  1433. table = tables.ConsoleServerPortTemplateTable
  1434. filterset = filtersets.ConsoleServerPortTemplateFilterSet
  1435. viewname = 'dcim:moduletype_consoleserverports'
  1436. tab = ViewTab(
  1437. label=_('Console Server Ports'),
  1438. badge=lambda obj: obj.consoleserverporttemplates.count(),
  1439. permission='dcim.view_consoleserverporttemplate',
  1440. weight=540,
  1441. hide_if_empty=True
  1442. )
  1443. @register_model_view(ModuleType, 'powerports', path='power-ports')
  1444. class ModuleTypePowerPortsView(ModuleTypeComponentsView):
  1445. child_model = PowerPortTemplate
  1446. table = tables.PowerPortTemplateTable
  1447. filterset = filtersets.PowerPortTemplateFilterSet
  1448. viewname = 'dcim:moduletype_powerports'
  1449. tab = ViewTab(
  1450. label=_('Power Ports'),
  1451. badge=lambda obj: obj.powerporttemplates.count(),
  1452. permission='dcim.view_powerporttemplate',
  1453. weight=550,
  1454. hide_if_empty=True
  1455. )
  1456. @register_model_view(ModuleType, 'poweroutlets', path='power-outlets')
  1457. class ModuleTypePowerOutletsView(ModuleTypeComponentsView):
  1458. child_model = PowerOutletTemplate
  1459. table = tables.PowerOutletTemplateTable
  1460. filterset = filtersets.PowerOutletTemplateFilterSet
  1461. viewname = 'dcim:moduletype_poweroutlets'
  1462. tab = ViewTab(
  1463. label=_('Power Outlets'),
  1464. badge=lambda obj: obj.poweroutlettemplates.count(),
  1465. permission='dcim.view_poweroutlettemplate',
  1466. weight=560,
  1467. hide_if_empty=True
  1468. )
  1469. @register_model_view(ModuleType, 'interfaces')
  1470. class ModuleTypeInterfacesView(ModuleTypeComponentsView):
  1471. child_model = InterfaceTemplate
  1472. table = tables.InterfaceTemplateTable
  1473. filterset = filtersets.InterfaceTemplateFilterSet
  1474. viewname = 'dcim:moduletype_interfaces'
  1475. tab = ViewTab(
  1476. label=_('Interfaces'),
  1477. badge=lambda obj: obj.interfacetemplates.count(),
  1478. permission='dcim.view_interfacetemplate',
  1479. weight=500,
  1480. hide_if_empty=True
  1481. )
  1482. @register_model_view(ModuleType, 'frontports', path='front-ports')
  1483. class ModuleTypeFrontPortsView(ModuleTypeComponentsView):
  1484. child_model = FrontPortTemplate
  1485. table = tables.FrontPortTemplateTable
  1486. filterset = filtersets.FrontPortTemplateFilterSet
  1487. viewname = 'dcim:moduletype_frontports'
  1488. tab = ViewTab(
  1489. label=_('Front Ports'),
  1490. badge=lambda obj: obj.frontporttemplates.count(),
  1491. permission='dcim.view_frontporttemplate',
  1492. weight=510,
  1493. hide_if_empty=True
  1494. )
  1495. @register_model_view(ModuleType, 'rearports', path='rear-ports')
  1496. class ModuleTypeRearPortsView(ModuleTypeComponentsView):
  1497. child_model = RearPortTemplate
  1498. table = tables.RearPortTemplateTable
  1499. filterset = filtersets.RearPortTemplateFilterSet
  1500. viewname = 'dcim:moduletype_rearports'
  1501. tab = ViewTab(
  1502. label=_('Rear Ports'),
  1503. badge=lambda obj: obj.rearporttemplates.count(),
  1504. permission='dcim.view_rearporttemplate',
  1505. weight=520,
  1506. hide_if_empty=True
  1507. )
  1508. @register_model_view(ModuleType, 'modulebays', path='module-bays')
  1509. class ModuleTypeModuleBaysView(ModuleTypeComponentsView):
  1510. child_model = ModuleBayTemplate
  1511. table = tables.ModuleBayTemplateTable
  1512. filterset = filtersets.ModuleBayTemplateFilterSet
  1513. viewname = 'dcim:moduletype_modulebays'
  1514. tab = ViewTab(
  1515. label=_('Module Bays'),
  1516. badge=lambda obj: obj.modulebaytemplates.count(),
  1517. permission='dcim.view_modulebaytemplate',
  1518. weight=570,
  1519. hide_if_empty=True
  1520. )
  1521. @register_model_view(ModuleType, 'bulk_import', path='import', detail=False)
  1522. class ModuleTypeImportView(generic.BulkImportView):
  1523. additional_permissions = [
  1524. 'dcim.add_moduletype',
  1525. 'dcim.add_consoleporttemplate',
  1526. 'dcim.add_consoleserverporttemplate',
  1527. 'dcim.add_powerporttemplate',
  1528. 'dcim.add_poweroutlettemplate',
  1529. 'dcim.add_interfacetemplate',
  1530. 'dcim.add_frontporttemplate',
  1531. 'dcim.add_rearporttemplate',
  1532. 'dcim.add_modulebaytemplate',
  1533. ]
  1534. queryset = ModuleType.objects.all()
  1535. model_form = forms.ModuleTypeImportForm
  1536. related_object_forms = {
  1537. 'console-ports': forms.ConsolePortTemplateImportForm,
  1538. 'console-server-ports': forms.ConsoleServerPortTemplateImportForm,
  1539. 'power-ports': forms.PowerPortTemplateImportForm,
  1540. 'power-outlets': forms.PowerOutletTemplateImportForm,
  1541. 'interfaces': forms.InterfaceTemplateImportForm,
  1542. 'rear-ports': forms.RearPortTemplateImportForm,
  1543. 'front-ports': forms.FrontPortTemplateImportForm,
  1544. 'port-mappings': forms.PortTemplateMappingImportForm,
  1545. 'module-bays': forms.ModuleBayTemplateImportForm,
  1546. }
  1547. def prep_related_object_data(self, parent, data):
  1548. data.update({'module_type': parent})
  1549. return data
  1550. @register_model_view(ModuleType, 'bulk_edit', path='edit', detail=False)
  1551. class ModuleTypeBulkEditView(generic.BulkEditView):
  1552. queryset = ModuleType.objects.annotate(
  1553. instance_count=count_related(Module, 'module_type')
  1554. )
  1555. filterset = filtersets.ModuleTypeFilterSet
  1556. table = tables.ModuleTypeTable
  1557. form = forms.ModuleTypeBulkEditForm
  1558. @register_model_view(ModuleType, 'bulk_rename', path='rename', detail=False)
  1559. class ModuleTypeBulkRenameView(generic.BulkRenameView):
  1560. queryset = ModuleType.objects.all()
  1561. filterset = filtersets.ModuleTypeFilterSet
  1562. field_name = 'model'
  1563. @register_model_view(ModuleType, 'bulk_delete', path='delete', detail=False)
  1564. class ModuleTypeBulkDeleteView(generic.BulkDeleteView):
  1565. queryset = ModuleType.objects.annotate(
  1566. instance_count=count_related(Module, 'module_type')
  1567. )
  1568. filterset = filtersets.ModuleTypeFilterSet
  1569. table = tables.ModuleTypeTable
  1570. #
  1571. # Console port templates
  1572. #
  1573. @register_model_view(ConsolePortTemplate, 'add', detail=False)
  1574. class ConsolePortTemplateCreateView(generic.ComponentCreateView):
  1575. queryset = ConsolePortTemplate.objects.all()
  1576. form = forms.ConsolePortTemplateCreateForm
  1577. model_form = forms.ConsolePortTemplateForm
  1578. @register_model_view(ConsolePortTemplate, 'edit')
  1579. class ConsolePortTemplateEditView(generic.ObjectEditView):
  1580. queryset = ConsolePortTemplate.objects.all()
  1581. form = forms.ConsolePortTemplateForm
  1582. @register_model_view(ConsolePortTemplate, 'delete')
  1583. class ConsolePortTemplateDeleteView(generic.ObjectDeleteView):
  1584. queryset = ConsolePortTemplate.objects.all()
  1585. @register_model_view(ConsolePortTemplate, 'bulk_edit', path='edit', detail=False)
  1586. class ConsolePortTemplateBulkEditView(generic.BulkEditView):
  1587. queryset = ConsolePortTemplate.objects.all()
  1588. table = tables.ConsolePortTemplateTable
  1589. form = forms.ConsolePortTemplateBulkEditForm
  1590. @register_model_view(ConsolePortTemplate, 'bulk_rename', path='rename', detail=False)
  1591. class ConsolePortTemplateBulkRenameView(generic.BulkRenameView):
  1592. queryset = ConsolePortTemplate.objects.all()
  1593. @register_model_view(ConsolePortTemplate, 'bulk_delete', path='delete', detail=False)
  1594. class ConsolePortTemplateBulkDeleteView(generic.BulkDeleteView):
  1595. queryset = ConsolePortTemplate.objects.all()
  1596. table = tables.ConsolePortTemplateTable
  1597. #
  1598. # Console server port templates
  1599. #
  1600. @register_model_view(ConsoleServerPortTemplate, 'add', detail=False)
  1601. class ConsoleServerPortTemplateCreateView(generic.ComponentCreateView):
  1602. queryset = ConsoleServerPortTemplate.objects.all()
  1603. form = forms.ConsoleServerPortTemplateCreateForm
  1604. model_form = forms.ConsoleServerPortTemplateForm
  1605. @register_model_view(ConsoleServerPortTemplate, 'edit')
  1606. class ConsoleServerPortTemplateEditView(generic.ObjectEditView):
  1607. queryset = ConsoleServerPortTemplate.objects.all()
  1608. form = forms.ConsoleServerPortTemplateForm
  1609. @register_model_view(ConsoleServerPortTemplate, 'delete')
  1610. class ConsoleServerPortTemplateDeleteView(generic.ObjectDeleteView):
  1611. queryset = ConsoleServerPortTemplate.objects.all()
  1612. @register_model_view(ConsoleServerPortTemplate, 'bulk_edit', path='edit', detail=False)
  1613. class ConsoleServerPortTemplateBulkEditView(generic.BulkEditView):
  1614. queryset = ConsoleServerPortTemplate.objects.all()
  1615. table = tables.ConsoleServerPortTemplateTable
  1616. form = forms.ConsoleServerPortTemplateBulkEditForm
  1617. @register_model_view(ConsoleServerPortTemplate, 'bulk_rename', detail=False)
  1618. class ConsoleServerPortTemplateBulkRenameView(generic.BulkRenameView):
  1619. queryset = ConsoleServerPortTemplate.objects.all()
  1620. @register_model_view(ConsoleServerPortTemplate, 'bulk_delete', path='delete', detail=False)
  1621. class ConsoleServerPortTemplateBulkDeleteView(generic.BulkDeleteView):
  1622. queryset = ConsoleServerPortTemplate.objects.all()
  1623. table = tables.ConsoleServerPortTemplateTable
  1624. #
  1625. # Power port templates
  1626. #
  1627. @register_model_view(PowerPortTemplate, 'add', detail=False)
  1628. class PowerPortTemplateCreateView(generic.ComponentCreateView):
  1629. queryset = PowerPortTemplate.objects.all()
  1630. form = forms.PowerPortTemplateCreateForm
  1631. model_form = forms.PowerPortTemplateForm
  1632. @register_model_view(PowerPortTemplate, 'edit')
  1633. class PowerPortTemplateEditView(generic.ObjectEditView):
  1634. queryset = PowerPortTemplate.objects.all()
  1635. form = forms.PowerPortTemplateForm
  1636. @register_model_view(PowerPortTemplate, 'delete')
  1637. class PowerPortTemplateDeleteView(generic.ObjectDeleteView):
  1638. queryset = PowerPortTemplate.objects.all()
  1639. @register_model_view(PowerPortTemplate, 'bulk_edit', path='edit', detail=False)
  1640. class PowerPortTemplateBulkEditView(generic.BulkEditView):
  1641. queryset = PowerPortTemplate.objects.all()
  1642. table = tables.PowerPortTemplateTable
  1643. form = forms.PowerPortTemplateBulkEditForm
  1644. @register_model_view(PowerPortTemplate, 'bulk_rename', path='rename', detail=False)
  1645. class PowerPortTemplateBulkRenameView(generic.BulkRenameView):
  1646. queryset = PowerPortTemplate.objects.all()
  1647. @register_model_view(PowerPortTemplate, 'bulk_delete', path='delete', detail=False)
  1648. class PowerPortTemplateBulkDeleteView(generic.BulkDeleteView):
  1649. queryset = PowerPortTemplate.objects.all()
  1650. table = tables.PowerPortTemplateTable
  1651. #
  1652. # Power outlet templates
  1653. #
  1654. @register_model_view(PowerOutletTemplate, 'add', detail=False)
  1655. class PowerOutletTemplateCreateView(generic.ComponentCreateView):
  1656. queryset = PowerOutletTemplate.objects.all()
  1657. form = forms.PowerOutletTemplateCreateForm
  1658. model_form = forms.PowerOutletTemplateForm
  1659. @register_model_view(PowerOutletTemplate, 'edit')
  1660. class PowerOutletTemplateEditView(generic.ObjectEditView):
  1661. queryset = PowerOutletTemplate.objects.all()
  1662. form = forms.PowerOutletTemplateForm
  1663. @register_model_view(PowerOutletTemplate, 'delete')
  1664. class PowerOutletTemplateDeleteView(generic.ObjectDeleteView):
  1665. queryset = PowerOutletTemplate.objects.all()
  1666. @register_model_view(PowerOutletTemplate, 'bulk_edit', path='edit', detail=False)
  1667. class PowerOutletTemplateBulkEditView(generic.BulkEditView):
  1668. queryset = PowerOutletTemplate.objects.all()
  1669. table = tables.PowerOutletTemplateTable
  1670. form = forms.PowerOutletTemplateBulkEditForm
  1671. @register_model_view(PowerOutletTemplate, 'bulk_rename', path='rename', detail=False)
  1672. class PowerOutletTemplateBulkRenameView(generic.BulkRenameView):
  1673. queryset = PowerOutletTemplate.objects.all()
  1674. @register_model_view(PowerOutletTemplate, 'bulk_delete', path='delete', detail=False)
  1675. class PowerOutletTemplateBulkDeleteView(generic.BulkDeleteView):
  1676. queryset = PowerOutletTemplate.objects.all()
  1677. table = tables.PowerOutletTemplateTable
  1678. #
  1679. # Interface templates
  1680. #
  1681. @register_model_view(InterfaceTemplate, 'add', detail=False)
  1682. class InterfaceTemplateCreateView(generic.ComponentCreateView):
  1683. queryset = InterfaceTemplate.objects.all()
  1684. form = forms.InterfaceTemplateCreateForm
  1685. model_form = forms.InterfaceTemplateForm
  1686. @register_model_view(InterfaceTemplate, 'edit')
  1687. class InterfaceTemplateEditView(generic.ObjectEditView):
  1688. queryset = InterfaceTemplate.objects.all()
  1689. form = forms.InterfaceTemplateForm
  1690. @register_model_view(InterfaceTemplate, 'delete')
  1691. class InterfaceTemplateDeleteView(generic.ObjectDeleteView):
  1692. queryset = InterfaceTemplate.objects.all()
  1693. @register_model_view(InterfaceTemplate, 'bulk_edit', path='edit', detail=False)
  1694. class InterfaceTemplateBulkEditView(generic.BulkEditView):
  1695. queryset = InterfaceTemplate.objects.all()
  1696. table = tables.InterfaceTemplateTable
  1697. form = forms.InterfaceTemplateBulkEditForm
  1698. @register_model_view(InterfaceTemplate, 'bulk_rename', path='rename', detail=False)
  1699. class InterfaceTemplateBulkRenameView(generic.BulkRenameView):
  1700. queryset = InterfaceTemplate.objects.all()
  1701. @register_model_view(InterfaceTemplate, 'bulk_delete', path='delete', detail=False)
  1702. class InterfaceTemplateBulkDeleteView(generic.BulkDeleteView):
  1703. queryset = InterfaceTemplate.objects.all()
  1704. table = tables.InterfaceTemplateTable
  1705. #
  1706. # Front port templates
  1707. #
  1708. @register_model_view(FrontPortTemplate, 'add', detail=False)
  1709. class FrontPortTemplateCreateView(generic.ComponentCreateView):
  1710. queryset = FrontPortTemplate.objects.all()
  1711. form = forms.FrontPortTemplateCreateForm
  1712. model_form = forms.FrontPortTemplateForm
  1713. @register_model_view(FrontPortTemplate, 'edit')
  1714. class FrontPortTemplateEditView(generic.ObjectEditView):
  1715. queryset = FrontPortTemplate.objects.all()
  1716. form = forms.FrontPortTemplateForm
  1717. @register_model_view(FrontPortTemplate, 'delete')
  1718. class FrontPortTemplateDeleteView(generic.ObjectDeleteView):
  1719. queryset = FrontPortTemplate.objects.all()
  1720. @register_model_view(FrontPortTemplate, 'bulk_edit', path='edit', detail=False)
  1721. class FrontPortTemplateBulkEditView(generic.BulkEditView):
  1722. queryset = FrontPortTemplate.objects.all()
  1723. table = tables.FrontPortTemplateTable
  1724. form = forms.FrontPortTemplateBulkEditForm
  1725. @register_model_view(FrontPortTemplate, 'bulk_rename', path='rename', detail=False)
  1726. class FrontPortTemplateBulkRenameView(generic.BulkRenameView):
  1727. queryset = FrontPortTemplate.objects.all()
  1728. @register_model_view(FrontPortTemplate, 'bulk_delete', path='delete', detail=False)
  1729. class FrontPortTemplateBulkDeleteView(generic.BulkDeleteView):
  1730. queryset = FrontPortTemplate.objects.all()
  1731. table = tables.FrontPortTemplateTable
  1732. #
  1733. # Rear port templates
  1734. #
  1735. @register_model_view(RearPortTemplate, 'add', detail=False)
  1736. class RearPortTemplateCreateView(generic.ComponentCreateView):
  1737. queryset = RearPortTemplate.objects.all()
  1738. form = forms.RearPortTemplateCreateForm
  1739. model_form = forms.RearPortTemplateForm
  1740. @register_model_view(RearPortTemplate, 'edit')
  1741. class RearPortTemplateEditView(generic.ObjectEditView):
  1742. queryset = RearPortTemplate.objects.all()
  1743. form = forms.RearPortTemplateForm
  1744. @register_model_view(RearPortTemplate, 'delete')
  1745. class RearPortTemplateDeleteView(generic.ObjectDeleteView):
  1746. queryset = RearPortTemplate.objects.all()
  1747. @register_model_view(RearPortTemplate, 'bulk_edit', path='edit', detail=False)
  1748. class RearPortTemplateBulkEditView(generic.BulkEditView):
  1749. queryset = RearPortTemplate.objects.all()
  1750. table = tables.RearPortTemplateTable
  1751. form = forms.RearPortTemplateBulkEditForm
  1752. @register_model_view(RearPortTemplate, 'bulk_rename', path='rename', detail=False)
  1753. class RearPortTemplateBulkRenameView(generic.BulkRenameView):
  1754. queryset = RearPortTemplate.objects.all()
  1755. @register_model_view(RearPortTemplate, 'bulk_delete', path='delete', detail=False)
  1756. class RearPortTemplateBulkDeleteView(generic.BulkDeleteView):
  1757. queryset = RearPortTemplate.objects.all()
  1758. table = tables.RearPortTemplateTable
  1759. #
  1760. # Module bay templates
  1761. #
  1762. @register_model_view(ModuleBayTemplate, 'add', detail=False)
  1763. class ModuleBayTemplateCreateView(generic.ComponentCreateView):
  1764. queryset = ModuleBayTemplate.objects.all()
  1765. form = forms.ModuleBayTemplateCreateForm
  1766. model_form = forms.ModuleBayTemplateForm
  1767. @register_model_view(ModuleBayTemplate, 'edit')
  1768. class ModuleBayTemplateEditView(generic.ObjectEditView):
  1769. queryset = ModuleBayTemplate.objects.all()
  1770. form = forms.ModuleBayTemplateForm
  1771. @register_model_view(ModuleBayTemplate, 'delete')
  1772. class ModuleBayTemplateDeleteView(generic.ObjectDeleteView):
  1773. queryset = ModuleBayTemplate.objects.all()
  1774. @register_model_view(ModuleBayTemplate, 'bulk_edit', path='edit', detail=False)
  1775. class ModuleBayTemplateBulkEditView(generic.BulkEditView):
  1776. queryset = ModuleBayTemplate.objects.all()
  1777. table = tables.ModuleBayTemplateTable
  1778. form = forms.ModuleBayTemplateBulkEditForm
  1779. @register_model_view(ModuleBayTemplate, 'bulk_rename', path='rename', detail=False)
  1780. class ModuleBayTemplateBulkRenameView(generic.BulkRenameView):
  1781. queryset = ModuleBayTemplate.objects.all()
  1782. @register_model_view(ModuleBayTemplate, 'bulk_delete', path='delete', detail=False)
  1783. class ModuleBayTemplateBulkDeleteView(generic.BulkDeleteView):
  1784. queryset = ModuleBayTemplate.objects.all()
  1785. table = tables.ModuleBayTemplateTable
  1786. #
  1787. # Device bay templates
  1788. #
  1789. @register_model_view(DeviceBayTemplate, 'add', detail=False)
  1790. class DeviceBayTemplateCreateView(generic.ComponentCreateView):
  1791. queryset = DeviceBayTemplate.objects.all()
  1792. form = forms.DeviceBayTemplateCreateForm
  1793. model_form = forms.DeviceBayTemplateForm
  1794. @register_model_view(DeviceBayTemplate, 'edit')
  1795. class DeviceBayTemplateEditView(generic.ObjectEditView):
  1796. queryset = DeviceBayTemplate.objects.all()
  1797. form = forms.DeviceBayTemplateForm
  1798. @register_model_view(DeviceBayTemplate, 'delete')
  1799. class DeviceBayTemplateDeleteView(generic.ObjectDeleteView):
  1800. queryset = DeviceBayTemplate.objects.all()
  1801. @register_model_view(DeviceBayTemplate, 'bulk_edit', path='edit', detail=False)
  1802. class DeviceBayTemplateBulkEditView(generic.BulkEditView):
  1803. queryset = DeviceBayTemplate.objects.all()
  1804. table = tables.DeviceBayTemplateTable
  1805. form = forms.DeviceBayTemplateBulkEditForm
  1806. @register_model_view(DeviceBayTemplate, 'bulk_rename', path='rename', detail=False)
  1807. class DeviceBayTemplateBulkRenameView(generic.BulkRenameView):
  1808. queryset = DeviceBayTemplate.objects.all()
  1809. @register_model_view(DeviceBayTemplate, 'bulk_delete', path='delete', detail=False)
  1810. class DeviceBayTemplateBulkDeleteView(generic.BulkDeleteView):
  1811. queryset = DeviceBayTemplate.objects.all()
  1812. table = tables.DeviceBayTemplateTable
  1813. #
  1814. # Inventory item templates
  1815. #
  1816. @register_model_view(InventoryItemTemplate, 'add', detail=False)
  1817. class InventoryItemTemplateCreateView(generic.ComponentCreateView):
  1818. queryset = InventoryItemTemplate.objects.all()
  1819. form = forms.InventoryItemTemplateCreateForm
  1820. model_form = forms.InventoryItemTemplateForm
  1821. def alter_object(self, instance, request):
  1822. # Set component (if any)
  1823. component_type = request.GET.get('component_type')
  1824. component_id = request.GET.get('component_id')
  1825. if component_type and component_id:
  1826. content_type = get_object_or_404(ContentType, pk=component_type)
  1827. instance.component = get_object_or_404(content_type.model_class(), pk=component_id)
  1828. return instance
  1829. @register_model_view(InventoryItemTemplate, 'edit')
  1830. class InventoryItemTemplateEditView(generic.ObjectEditView):
  1831. queryset = InventoryItemTemplate.objects.all()
  1832. form = forms.InventoryItemTemplateForm
  1833. @register_model_view(InventoryItemTemplate, 'delete')
  1834. class InventoryItemTemplateDeleteView(generic.ObjectDeleteView):
  1835. queryset = InventoryItemTemplate.objects.all()
  1836. @register_model_view(InventoryItemTemplate, 'bulk_edit', path='edit', detail=False)
  1837. class InventoryItemTemplateBulkEditView(generic.BulkEditView):
  1838. queryset = InventoryItemTemplate.objects.all()
  1839. table = tables.InventoryItemTemplateTable
  1840. form = forms.InventoryItemTemplateBulkEditForm
  1841. @register_model_view(InventoryItemTemplate, 'bulk_rename', path='rename', detail=False)
  1842. class InventoryItemTemplateBulkRenameView(generic.BulkRenameView):
  1843. queryset = InventoryItemTemplate.objects.all()
  1844. @register_model_view(InventoryItemTemplate, 'bulk_delete', path='delete', detail=False)
  1845. class InventoryItemTemplateBulkDeleteView(generic.BulkDeleteView):
  1846. queryset = InventoryItemTemplate.objects.all()
  1847. table = tables.InventoryItemTemplateTable
  1848. #
  1849. # Device roles
  1850. #
  1851. @register_model_view(DeviceRole, 'list', path='', detail=False)
  1852. class DeviceRoleListView(generic.ObjectListView):
  1853. queryset = DeviceRole.objects.add_related_count(
  1854. DeviceRole.objects.add_related_count(
  1855. DeviceRole.objects.all(),
  1856. VirtualMachine,
  1857. 'role',
  1858. 'vm_count',
  1859. cumulative=True
  1860. ),
  1861. Device,
  1862. 'role',
  1863. 'device_count',
  1864. cumulative=True
  1865. )
  1866. filterset = filtersets.DeviceRoleFilterSet
  1867. filterset_form = forms.DeviceRoleFilterForm
  1868. table = tables.DeviceRoleTable
  1869. @register_model_view(DeviceRole)
  1870. class DeviceRoleView(GetRelatedModelsMixin, generic.ObjectView):
  1871. queryset = DeviceRole.objects.all()
  1872. layout = layout.SimpleLayout(
  1873. left_panels=[
  1874. panels.DeviceRolePanel(),
  1875. TagsPanel(),
  1876. ],
  1877. right_panels=[
  1878. RelatedObjectsPanel(),
  1879. CustomFieldsPanel(),
  1880. CommentsPanel(),
  1881. ],
  1882. bottom_panels=[
  1883. ObjectsTablePanel(
  1884. model='dcim.DeviceRole',
  1885. title=_('Child Device Roles'),
  1886. filters={'parent_id': lambda ctx: ctx['object'].pk},
  1887. actions=[
  1888. actions.AddObject('dcim.DeviceRole', url_params={'parent': lambda ctx: ctx['object'].pk}),
  1889. ],
  1890. ),
  1891. ]
  1892. )
  1893. def get_extra_context(self, request, instance):
  1894. return {
  1895. 'related_models': self.get_related_models(request, instance),
  1896. }
  1897. @register_model_view(DeviceRole, 'add', detail=False)
  1898. @register_model_view(DeviceRole, 'edit')
  1899. class DeviceRoleEditView(generic.ObjectEditView):
  1900. queryset = DeviceRole.objects.all()
  1901. form = forms.DeviceRoleForm
  1902. @register_model_view(DeviceRole, 'delete')
  1903. class DeviceRoleDeleteView(generic.ObjectDeleteView):
  1904. queryset = DeviceRole.objects.all()
  1905. @register_model_view(DeviceRole, 'bulk_import', path='import', detail=False)
  1906. class DeviceRoleBulkImportView(generic.BulkImportView):
  1907. queryset = DeviceRole.objects.all()
  1908. model_form = forms.DeviceRoleImportForm
  1909. @register_model_view(DeviceRole, 'bulk_edit', path='edit', detail=False)
  1910. class DeviceRoleBulkEditView(generic.BulkEditView):
  1911. queryset = DeviceRole.objects.annotate(
  1912. device_count=count_related(Device, 'role'),
  1913. vm_count=count_related(VirtualMachine, 'role')
  1914. )
  1915. filterset = filtersets.DeviceRoleFilterSet
  1916. table = tables.DeviceRoleTable
  1917. form = forms.DeviceRoleBulkEditForm
  1918. @register_model_view(DeviceRole, 'bulk_rename', path='rename', detail=False)
  1919. class DeviceRoleBulkRenameView(generic.BulkRenameView):
  1920. queryset = DeviceRole.objects.all()
  1921. filterset = filtersets.DeviceRoleFilterSet
  1922. @register_model_view(DeviceRole, 'bulk_delete', path='delete', detail=False)
  1923. class DeviceRoleBulkDeleteView(generic.BulkDeleteView):
  1924. queryset = DeviceRole.objects.annotate(
  1925. device_count=count_related(Device, 'role'),
  1926. vm_count=count_related(VirtualMachine, 'role')
  1927. )
  1928. filterset = filtersets.DeviceRoleFilterSet
  1929. table = tables.DeviceRoleTable
  1930. #
  1931. # Platforms
  1932. #
  1933. @register_model_view(Platform, 'list', path='', detail=False)
  1934. class PlatformListView(generic.ObjectListView):
  1935. queryset = Platform.objects.add_related_count(
  1936. Platform.objects.add_related_count(
  1937. Platform.objects.all(),
  1938. VirtualMachine,
  1939. 'platform',
  1940. 'vm_count',
  1941. cumulative=True
  1942. ),
  1943. Device,
  1944. 'platform',
  1945. 'device_count',
  1946. cumulative=True
  1947. )
  1948. table = tables.PlatformTable
  1949. filterset = filtersets.PlatformFilterSet
  1950. filterset_form = forms.PlatformFilterForm
  1951. @register_model_view(Platform)
  1952. class PlatformView(GetRelatedModelsMixin, generic.ObjectView):
  1953. queryset = Platform.objects.all()
  1954. layout = layout.SimpleLayout(
  1955. left_panels=[
  1956. panels.PlatformPanel(),
  1957. TagsPanel(),
  1958. ],
  1959. right_panels=[
  1960. RelatedObjectsPanel(),
  1961. CustomFieldsPanel(),
  1962. CommentsPanel(),
  1963. ],
  1964. bottom_panels=[
  1965. ObjectsTablePanel(
  1966. model='dcim.Platform',
  1967. title=_('Child Platforms'),
  1968. filters={'parent_id': lambda ctx: ctx['object'].pk},
  1969. actions=[
  1970. actions.AddObject('dcim.Platform', url_params={'parent': lambda ctx: ctx['object'].pk}),
  1971. ],
  1972. ),
  1973. ]
  1974. )
  1975. def get_extra_context(self, request, instance):
  1976. return {
  1977. 'related_models': self.get_related_models(request, instance),
  1978. }
  1979. @register_model_view(Platform, 'add', detail=False)
  1980. @register_model_view(Platform, 'edit')
  1981. class PlatformEditView(generic.ObjectEditView):
  1982. queryset = Platform.objects.all()
  1983. form = forms.PlatformForm
  1984. @register_model_view(Platform, 'delete')
  1985. class PlatformDeleteView(generic.ObjectDeleteView):
  1986. queryset = Platform.objects.all()
  1987. @register_model_view(Platform, 'bulk_import', path='import', detail=False)
  1988. class PlatformBulkImportView(generic.BulkImportView):
  1989. queryset = Platform.objects.all()
  1990. model_form = forms.PlatformImportForm
  1991. @register_model_view(Platform, 'bulk_edit', path='edit', detail=False)
  1992. class PlatformBulkEditView(generic.BulkEditView):
  1993. queryset = Platform.objects.all()
  1994. filterset = filtersets.PlatformFilterSet
  1995. table = tables.PlatformTable
  1996. form = forms.PlatformBulkEditForm
  1997. @register_model_view(Platform, 'bulk_rename', path='rename', detail=False)
  1998. class PlatformBulkRenameView(generic.BulkRenameView):
  1999. queryset = Platform.objects.all()
  2000. filterset = filtersets.PlatformFilterSet
  2001. @register_model_view(Platform, 'bulk_delete', path='delete', detail=False)
  2002. class PlatformBulkDeleteView(generic.BulkDeleteView):
  2003. queryset = Platform.objects.all()
  2004. filterset = filtersets.PlatformFilterSet
  2005. table = tables.PlatformTable
  2006. #
  2007. # Devices
  2008. #
  2009. @register_model_view(Device, 'list', path='', detail=False)
  2010. class DeviceListView(generic.ObjectListView):
  2011. queryset = Device.objects.select_related('virtual_chassis')
  2012. filterset = filtersets.DeviceFilterSet
  2013. filterset_form = forms.DeviceFilterForm
  2014. table = tables.DeviceTable
  2015. actions = (AddObject, BulkImport, BulkExport, BulkAddComponents, BulkEdit, BulkRename, BulkDelete)
  2016. @register_model_view(Device)
  2017. class DeviceView(generic.ObjectView):
  2018. queryset = Device.objects.all()
  2019. layout = layout.SimpleLayout(
  2020. left_panels=[
  2021. panels.DevicePanel(),
  2022. panels.VirtualChassisMembersPanel(),
  2023. CustomFieldsPanel(),
  2024. TagsPanel(),
  2025. CommentsPanel(),
  2026. ObjectsTablePanel(
  2027. model='dcim.VirtualDeviceContext',
  2028. filters={'device_id': lambda ctx: ctx['object'].pk},
  2029. actions=[
  2030. actions.AddObject('dcim.VirtualDeviceContext', url_params={'device': lambda ctx: ctx['object'].pk}),
  2031. ],
  2032. ),
  2033. ],
  2034. right_panels=[
  2035. panels.DeviceManagementPanel(),
  2036. panels.PowerUtilizationPanel(),
  2037. ObjectsTablePanel(
  2038. model='ipam.Service',
  2039. title=_('Application Services'),
  2040. filters={'device_id': lambda ctx: ctx['object'].pk},
  2041. actions=[
  2042. actions.AddObject(
  2043. 'ipam.Service',
  2044. url_params={
  2045. 'parent_object_type': lambda ctx: ContentType.objects.get_for_model(ctx['object']).pk,
  2046. 'parent': lambda ctx: ctx['object'].pk
  2047. }
  2048. ),
  2049. ],
  2050. ),
  2051. ImageAttachmentsPanel(),
  2052. panels.DeviceDeviceTypePanel(),
  2053. panels.DeviceDimensionsPanel(),
  2054. TemplatePanel('dcim/panels/device_rack_elevations.html'),
  2055. ],
  2056. )
  2057. def get_extra_context(self, request, instance):
  2058. # VirtualChassis members
  2059. if instance.virtual_chassis is not None:
  2060. vc_members = Device.objects.restrict(request.user, 'view').filter(
  2061. virtual_chassis=instance.virtual_chassis
  2062. ).order_by('vc_position')
  2063. else:
  2064. vc_members = []
  2065. return {
  2066. 'vc_members': vc_members,
  2067. 'svg_extra': f'highlight=id:{instance.pk}',
  2068. }
  2069. @register_model_view(Device, 'add', detail=False)
  2070. @register_model_view(Device, 'edit')
  2071. class DeviceEditView(generic.ObjectEditView):
  2072. queryset = Device.objects.all()
  2073. form = forms.DeviceForm
  2074. template_name = 'dcim/device_edit.html'
  2075. @register_model_view(Device, 'delete')
  2076. class DeviceDeleteView(generic.ObjectDeleteView):
  2077. queryset = Device.objects.all()
  2078. @register_model_view(Device, 'consoleports', path='console-ports')
  2079. class DeviceConsolePortsView(DeviceComponentsView):
  2080. child_model = ConsolePort
  2081. table = tables.DeviceConsolePortTable
  2082. filterset = filtersets.ConsolePortFilterSet
  2083. filterset_form = forms.ConsolePortFilterForm
  2084. actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete)
  2085. tab = ViewTab(
  2086. label=_('Console Ports'),
  2087. badge=lambda obj: obj.console_port_count,
  2088. permission='dcim.view_consoleport',
  2089. weight=550,
  2090. hide_if_empty=True
  2091. )
  2092. @register_model_view(Device, 'consoleserverports', path='console-server-ports')
  2093. class DeviceConsoleServerPortsView(DeviceComponentsView):
  2094. child_model = ConsoleServerPort
  2095. table = tables.DeviceConsoleServerPortTable
  2096. filterset = filtersets.ConsoleServerPortFilterSet
  2097. filterset_form = forms.ConsoleServerPortFilterForm
  2098. actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete)
  2099. tab = ViewTab(
  2100. label=_('Console Server Ports'),
  2101. badge=lambda obj: obj.console_server_port_count,
  2102. permission='dcim.view_consoleserverport',
  2103. weight=560,
  2104. hide_if_empty=True
  2105. )
  2106. @register_model_view(Device, 'powerports', path='power-ports')
  2107. class DevicePowerPortsView(DeviceComponentsView):
  2108. child_model = PowerPort
  2109. table = tables.DevicePowerPortTable
  2110. filterset = filtersets.PowerPortFilterSet
  2111. filterset_form = forms.PowerPortFilterForm
  2112. actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete)
  2113. tab = ViewTab(
  2114. label=_('Power Ports'),
  2115. badge=lambda obj: obj.power_port_count,
  2116. permission='dcim.view_powerport',
  2117. weight=570,
  2118. hide_if_empty=True
  2119. )
  2120. @register_model_view(Device, 'poweroutlets', path='power-outlets')
  2121. class DevicePowerOutletsView(DeviceComponentsView):
  2122. child_model = PowerOutlet
  2123. table = tables.DevicePowerOutletTable
  2124. filterset = filtersets.PowerOutletFilterSet
  2125. filterset_form = forms.PowerOutletFilterForm
  2126. actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete)
  2127. tab = ViewTab(
  2128. label=_('Power Outlets'),
  2129. badge=lambda obj: obj.power_outlet_count,
  2130. permission='dcim.view_poweroutlet',
  2131. weight=580,
  2132. hide_if_empty=True
  2133. )
  2134. @register_model_view(Device, 'interfaces')
  2135. class DeviceInterfacesView(DeviceComponentsView):
  2136. child_model = Interface
  2137. table = tables.DeviceInterfaceTable
  2138. filterset = filtersets.InterfaceFilterSet
  2139. filterset_form = forms.InterfaceFilterForm
  2140. actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete)
  2141. template_name = 'dcim/device/interfaces.html'
  2142. tab = ViewTab(
  2143. label=_('Interfaces'),
  2144. badge=lambda obj: obj.vc_interfaces().count(),
  2145. permission='dcim.view_interface',
  2146. weight=520,
  2147. hide_if_empty=True
  2148. )
  2149. def get_children(self, request, parent):
  2150. return parent.vc_interfaces().restrict(request.user, 'view').prefetch_related(
  2151. Prefetch('ip_addresses', queryset=IPAddress.objects.restrict(request.user)),
  2152. Prefetch('member_interfaces', queryset=Interface.objects.restrict(request.user))
  2153. )
  2154. @register_model_view(Device, 'frontports', path='front-ports')
  2155. class DeviceFrontPortsView(DeviceComponentsView):
  2156. child_model = FrontPort
  2157. table = tables.DeviceFrontPortTable
  2158. filterset = filtersets.FrontPortFilterSet
  2159. filterset_form = forms.FrontPortFilterForm
  2160. actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete)
  2161. tab = ViewTab(
  2162. label=_('Front Ports'),
  2163. badge=lambda obj: obj.front_port_count,
  2164. permission='dcim.view_frontport',
  2165. weight=530,
  2166. hide_if_empty=True
  2167. )
  2168. @register_model_view(Device, 'rearports', path='rear-ports')
  2169. class DeviceRearPortsView(DeviceComponentsView):
  2170. child_model = RearPort
  2171. table = tables.DeviceRearPortTable
  2172. filterset = filtersets.RearPortFilterSet
  2173. filterset_form = forms.RearPortFilterForm
  2174. actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete)
  2175. tab = ViewTab(
  2176. label=_('Rear Ports'),
  2177. badge=lambda obj: obj.rear_port_count,
  2178. permission='dcim.view_rearport',
  2179. weight=540,
  2180. hide_if_empty=True
  2181. )
  2182. @register_model_view(Device, 'modulebays', path='module-bays')
  2183. class DeviceModuleBaysView(DeviceComponentsView):
  2184. child_model = ModuleBay
  2185. table = tables.DeviceModuleBayTable
  2186. filterset = filtersets.ModuleBayFilterSet
  2187. filterset_form = forms.ModuleBayFilterForm
  2188. actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete)
  2189. tab = ViewTab(
  2190. label=_('Module Bays'),
  2191. badge=lambda obj: obj.module_bay_count,
  2192. permission='dcim.view_modulebay',
  2193. weight=510,
  2194. hide_if_empty=True
  2195. )
  2196. @register_model_view(Device, 'devicebays', path='device-bays')
  2197. class DeviceDeviceBaysView(DeviceComponentsView):
  2198. child_model = DeviceBay
  2199. table = tables.DeviceDeviceBayTable
  2200. filterset = filtersets.DeviceBayFilterSet
  2201. filterset_form = forms.DeviceBayFilterForm
  2202. actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete)
  2203. tab = ViewTab(
  2204. label=_('Device Bays'),
  2205. badge=lambda obj: obj.device_bay_count,
  2206. permission='dcim.view_devicebay',
  2207. weight=500,
  2208. hide_if_empty=True
  2209. )
  2210. @register_model_view(Device, 'inventory')
  2211. class DeviceInventoryView(DeviceComponentsView):
  2212. child_model = InventoryItem
  2213. table = tables.DeviceInventoryItemTable
  2214. filterset = filtersets.InventoryItemFilterSet
  2215. filterset_form = forms.InventoryItemFilterForm
  2216. actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete)
  2217. tab = ViewTab(
  2218. label=_('Inventory Items'),
  2219. badge=lambda obj: obj.inventory_item_count,
  2220. permission='dcim.view_inventoryitem',
  2221. weight=590,
  2222. hide_if_empty=True
  2223. )
  2224. @register_model_view(Device, 'configcontext', path='config-context')
  2225. class DeviceConfigContextView(ObjectConfigContextView):
  2226. queryset = Device.objects.annotate_config_context_data()
  2227. base_template = 'dcim/device/base.html'
  2228. tab = ViewTab(
  2229. label=_('Config Context'),
  2230. weight=2000
  2231. )
  2232. @register_model_view(Device, 'render-config')
  2233. class DeviceRenderConfigView(ObjectRenderConfigView):
  2234. queryset = Device.objects.all()
  2235. base_template = 'dcim/device/base.html'
  2236. additional_permissions = ['dcim.render_config_device']
  2237. tab = ViewTab(
  2238. label=_('Render Config'),
  2239. weight=2100,
  2240. )
  2241. @register_model_view(Device, 'virtual-machines')
  2242. class DeviceVirtualMachinesView(generic.ObjectChildrenView):
  2243. queryset = Device.objects.all()
  2244. child_model = VirtualMachine
  2245. table = VirtualMachineTable
  2246. filterset = VirtualMachineFilterSet
  2247. filterset_form = VirtualMachineFilterForm
  2248. tab = ViewTab(
  2249. label=_('Virtual Machines'),
  2250. badge=lambda obj: VirtualMachine.objects.filter(cluster=obj.cluster, device=obj).count(),
  2251. weight=2200,
  2252. hide_if_empty=True,
  2253. permission='virtualization.view_virtualmachine'
  2254. )
  2255. def get_children(self, request, parent):
  2256. return self.child_model.objects.restrict(request.user, 'view').filter(cluster=parent.cluster, device=parent)
  2257. @register_model_view(Device, 'bulk_import', path='import', detail=False)
  2258. class DeviceBulkImportView(generic.BulkImportView):
  2259. queryset = Device.objects.all()
  2260. model_form = forms.DeviceImportForm
  2261. def save_object(self, object_form, request):
  2262. parent_bay = getattr(object_form.instance, 'parent_bay', None)
  2263. obj = object_form.save()
  2264. # For child devices, save the reverse relation to the parent device bay
  2265. if parent_bay:
  2266. device_bay = parent_bay
  2267. device_bay.snapshot()
  2268. device_bay.installed_device = obj
  2269. device_bay.save()
  2270. return obj
  2271. @register_model_view(Device, 'bulk_edit', path='edit', detail=False)
  2272. class DeviceBulkEditView(generic.BulkEditView):
  2273. queryset = Device.objects.prefetch_related('device_type__manufacturer')
  2274. filterset = filtersets.DeviceFilterSet
  2275. table = tables.DeviceTable
  2276. form = forms.DeviceBulkEditForm
  2277. @register_model_view(Device, 'bulk_rename', path='rename', detail=False)
  2278. class DeviceBulkRenameView(generic.BulkRenameView):
  2279. queryset = Device.objects.all()
  2280. filterset = filtersets.DeviceFilterSet
  2281. table = tables.DeviceTable
  2282. @register_model_view(Device, 'bulk_delete', path='delete', detail=False)
  2283. class DeviceBulkDeleteView(generic.BulkDeleteView):
  2284. queryset = Device.objects.prefetch_related('device_type__manufacturer')
  2285. filterset = filtersets.DeviceFilterSet
  2286. table = tables.DeviceTable
  2287. #
  2288. # Modules
  2289. #
  2290. @register_model_view(Module, 'list', path='', detail=False)
  2291. class ModuleListView(generic.ObjectListView):
  2292. queryset = Module.objects.prefetch_related('module_type__manufacturer')
  2293. filterset = filtersets.ModuleFilterSet
  2294. filterset_form = forms.ModuleFilterForm
  2295. table = tables.ModuleTable
  2296. actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete)
  2297. @register_model_view(Module)
  2298. class ModuleView(GetRelatedModelsMixin, generic.ObjectView):
  2299. queryset = Module.objects.all()
  2300. layout = layout.SimpleLayout(
  2301. left_panels=[
  2302. panels.ModulePanel(),
  2303. TagsPanel(),
  2304. CommentsPanel(),
  2305. ],
  2306. right_panels=[
  2307. Panel(
  2308. title=_('Module Type'),
  2309. template_name='dcim/panels/module_type.html',
  2310. ),
  2311. RelatedObjectsPanel(),
  2312. CustomFieldsPanel(),
  2313. ],
  2314. )
  2315. def get_extra_context(self, request, instance):
  2316. return {
  2317. 'related_models': self.get_related_models(request, instance),
  2318. }
  2319. @register_model_view(Module, 'add', detail=False)
  2320. @register_model_view(Module, 'edit')
  2321. class ModuleEditView(generic.ObjectEditView):
  2322. queryset = Module.objects.all()
  2323. form = forms.ModuleForm
  2324. @register_model_view(Module, 'delete')
  2325. class ModuleDeleteView(generic.ObjectDeleteView):
  2326. queryset = Module.objects.all()
  2327. @register_model_view(Module, 'bulk_import', path='import', detail=False)
  2328. class ModuleBulkImportView(generic.BulkImportView):
  2329. queryset = Module.objects.all()
  2330. model_form = forms.ModuleImportForm
  2331. @register_model_view(Module, 'bulk_edit', path='edit', detail=False)
  2332. class ModuleBulkEditView(generic.BulkEditView):
  2333. queryset = Module.objects.prefetch_related('module_type__manufacturer')
  2334. filterset = filtersets.ModuleFilterSet
  2335. table = tables.ModuleTable
  2336. form = forms.ModuleBulkEditForm
  2337. @register_model_view(Module, 'bulk_delete', path='delete', detail=False)
  2338. class ModuleBulkDeleteView(generic.BulkDeleteView):
  2339. queryset = Module.objects.prefetch_related('module_type__manufacturer')
  2340. filterset = filtersets.ModuleFilterSet
  2341. table = tables.ModuleTable
  2342. #
  2343. # Console ports
  2344. #
  2345. @register_model_view(ConsolePort, 'list', path='', detail=False)
  2346. class ConsolePortListView(generic.ObjectListView):
  2347. queryset = ConsolePort.objects.all()
  2348. filterset = filtersets.ConsolePortFilterSet
  2349. filterset_form = forms.ConsolePortFilterForm
  2350. table = tables.ConsolePortTable
  2351. @register_model_view(ConsolePort)
  2352. class ConsolePortView(generic.ObjectView):
  2353. queryset = ConsolePort.objects.all()
  2354. @register_model_view(ConsolePort, 'add', detail=False)
  2355. class ConsolePortCreateView(generic.ComponentCreateView):
  2356. queryset = ConsolePort.objects.all()
  2357. form = forms.ConsolePortCreateForm
  2358. model_form = forms.ConsolePortForm
  2359. @register_model_view(ConsolePort, 'edit')
  2360. class ConsolePortEditView(generic.ObjectEditView):
  2361. queryset = ConsolePort.objects.all()
  2362. form = forms.ConsolePortForm
  2363. @register_model_view(ConsolePort, 'delete')
  2364. class ConsolePortDeleteView(generic.ObjectDeleteView):
  2365. queryset = ConsolePort.objects.all()
  2366. @register_model_view(ConsolePort, 'bulk_import', path='import', detail=False)
  2367. class ConsolePortBulkImportView(generic.BulkImportView):
  2368. queryset = ConsolePort.objects.all()
  2369. model_form = forms.ConsolePortImportForm
  2370. @register_model_view(ConsolePort, 'bulk_edit', path='edit', detail=False)
  2371. class ConsolePortBulkEditView(generic.BulkEditView):
  2372. queryset = ConsolePort.objects.all()
  2373. filterset = filtersets.ConsolePortFilterSet
  2374. table = tables.ConsolePortTable
  2375. form = forms.ConsolePortBulkEditForm
  2376. @register_model_view(ConsolePort, 'bulk_rename', path='rename', detail=False)
  2377. class ConsolePortBulkRenameView(generic.BulkRenameView):
  2378. queryset = ConsolePort.objects.all()
  2379. filterset = filtersets.ConsolePortFilterSet
  2380. @register_model_view(ConsolePort, 'bulk_disconnect', path='disconnect', detail=False)
  2381. class ConsolePortBulkDisconnectView(BulkDisconnectView):
  2382. queryset = ConsolePort.objects.all()
  2383. @register_model_view(ConsolePort, 'bulk_delete', path='delete', detail=False)
  2384. class ConsolePortBulkDeleteView(generic.BulkDeleteView):
  2385. queryset = ConsolePort.objects.all()
  2386. filterset = filtersets.ConsolePortFilterSet
  2387. table = tables.ConsolePortTable
  2388. # Trace view
  2389. register_model_view(ConsolePort, 'trace', kwargs={'model': ConsolePort})(PathTraceView)
  2390. #
  2391. # Console server ports
  2392. #
  2393. @register_model_view(ConsoleServerPort, 'list', path='', detail=False)
  2394. class ConsoleServerPortListView(generic.ObjectListView):
  2395. queryset = ConsoleServerPort.objects.all()
  2396. filterset = filtersets.ConsoleServerPortFilterSet
  2397. filterset_form = forms.ConsoleServerPortFilterForm
  2398. table = tables.ConsoleServerPortTable
  2399. @register_model_view(ConsoleServerPort)
  2400. class ConsoleServerPortView(generic.ObjectView):
  2401. queryset = ConsoleServerPort.objects.all()
  2402. @register_model_view(ConsoleServerPort, 'add', detail=False)
  2403. class ConsoleServerPortCreateView(generic.ComponentCreateView):
  2404. queryset = ConsoleServerPort.objects.all()
  2405. form = forms.ConsoleServerPortCreateForm
  2406. model_form = forms.ConsoleServerPortForm
  2407. @register_model_view(ConsoleServerPort, 'edit')
  2408. class ConsoleServerPortEditView(generic.ObjectEditView):
  2409. queryset = ConsoleServerPort.objects.all()
  2410. form = forms.ConsoleServerPortForm
  2411. @register_model_view(ConsoleServerPort, 'delete')
  2412. class ConsoleServerPortDeleteView(generic.ObjectDeleteView):
  2413. queryset = ConsoleServerPort.objects.all()
  2414. @register_model_view(ConsoleServerPort, 'bulk_import', path='import', detail=False)
  2415. class ConsoleServerPortBulkImportView(generic.BulkImportView):
  2416. queryset = ConsoleServerPort.objects.all()
  2417. model_form = forms.ConsoleServerPortImportForm
  2418. @register_model_view(ConsoleServerPort, 'bulk_edit', path='edit', detail=False)
  2419. class ConsoleServerPortBulkEditView(generic.BulkEditView):
  2420. queryset = ConsoleServerPort.objects.all()
  2421. filterset = filtersets.ConsoleServerPortFilterSet
  2422. table = tables.ConsoleServerPortTable
  2423. form = forms.ConsoleServerPortBulkEditForm
  2424. @register_model_view(ConsoleServerPort, 'bulk_rename', path='rename', detail=False)
  2425. class ConsoleServerPortBulkRenameView(generic.BulkRenameView):
  2426. queryset = ConsoleServerPort.objects.all()
  2427. filterset = filtersets.ConsoleServerPortFilterSet
  2428. @register_model_view(ConsoleServerPort, 'bulk_disconnect', path='disconnect', detail=False)
  2429. class ConsoleServerPortBulkDisconnectView(BulkDisconnectView):
  2430. queryset = ConsoleServerPort.objects.all()
  2431. @register_model_view(ConsoleServerPort, 'bulk_delete', path='delete', detail=False)
  2432. class ConsoleServerPortBulkDeleteView(generic.BulkDeleteView):
  2433. queryset = ConsoleServerPort.objects.all()
  2434. filterset = filtersets.ConsoleServerPortFilterSet
  2435. table = tables.ConsoleServerPortTable
  2436. # Trace view
  2437. register_model_view(ConsoleServerPort, 'trace', kwargs={'model': ConsoleServerPort})(PathTraceView)
  2438. #
  2439. # Power ports
  2440. #
  2441. @register_model_view(PowerPort, 'list', path='', detail=False)
  2442. class PowerPortListView(generic.ObjectListView):
  2443. queryset = PowerPort.objects.all()
  2444. filterset = filtersets.PowerPortFilterSet
  2445. filterset_form = forms.PowerPortFilterForm
  2446. table = tables.PowerPortTable
  2447. @register_model_view(PowerPort)
  2448. class PowerPortView(generic.ObjectView):
  2449. queryset = PowerPort.objects.all()
  2450. @register_model_view(PowerPort, 'add', detail=False)
  2451. class PowerPortCreateView(generic.ComponentCreateView):
  2452. queryset = PowerPort.objects.all()
  2453. form = forms.PowerPortCreateForm
  2454. model_form = forms.PowerPortForm
  2455. @register_model_view(PowerPort, 'edit')
  2456. class PowerPortEditView(generic.ObjectEditView):
  2457. queryset = PowerPort.objects.all()
  2458. form = forms.PowerPortForm
  2459. @register_model_view(PowerPort, 'delete')
  2460. class PowerPortDeleteView(generic.ObjectDeleteView):
  2461. queryset = PowerPort.objects.all()
  2462. @register_model_view(PowerPort, 'bulk_import', path='import', detail=False)
  2463. class PowerPortBulkImportView(generic.BulkImportView):
  2464. queryset = PowerPort.objects.all()
  2465. model_form = forms.PowerPortImportForm
  2466. @register_model_view(PowerPort, 'bulk_edit', path='edit', detail=False)
  2467. class PowerPortBulkEditView(generic.BulkEditView):
  2468. queryset = PowerPort.objects.all()
  2469. filterset = filtersets.PowerPortFilterSet
  2470. table = tables.PowerPortTable
  2471. form = forms.PowerPortBulkEditForm
  2472. @register_model_view(PowerPort, 'bulk_rename', path='rename', detail=False)
  2473. class PowerPortBulkRenameView(generic.BulkRenameView):
  2474. queryset = PowerPort.objects.all()
  2475. filterset = filtersets.PowerPortFilterSet
  2476. @register_model_view(PowerPort, 'bulk_disconnect', path='disconnect', detail=False)
  2477. class PowerPortBulkDisconnectView(BulkDisconnectView):
  2478. queryset = PowerPort.objects.all()
  2479. @register_model_view(PowerPort, 'bulk_delete', path='delete', detail=False)
  2480. class PowerPortBulkDeleteView(generic.BulkDeleteView):
  2481. queryset = PowerPort.objects.all()
  2482. filterset = filtersets.PowerPortFilterSet
  2483. table = tables.PowerPortTable
  2484. # Trace view
  2485. register_model_view(PowerPort, 'trace', kwargs={'model': PowerPort})(PathTraceView)
  2486. #
  2487. # Power outlets
  2488. #
  2489. @register_model_view(PowerOutlet, 'list', path='', detail=False)
  2490. class PowerOutletListView(generic.ObjectListView):
  2491. queryset = PowerOutlet.objects.all()
  2492. filterset = filtersets.PowerOutletFilterSet
  2493. filterset_form = forms.PowerOutletFilterForm
  2494. table = tables.PowerOutletTable
  2495. @register_model_view(PowerOutlet)
  2496. class PowerOutletView(generic.ObjectView):
  2497. queryset = PowerOutlet.objects.all()
  2498. @register_model_view(PowerOutlet, 'add', detail=False)
  2499. class PowerOutletCreateView(generic.ComponentCreateView):
  2500. queryset = PowerOutlet.objects.all()
  2501. form = forms.PowerOutletCreateForm
  2502. model_form = forms.PowerOutletForm
  2503. @register_model_view(PowerOutlet, 'edit')
  2504. class PowerOutletEditView(generic.ObjectEditView):
  2505. queryset = PowerOutlet.objects.all()
  2506. form = forms.PowerOutletForm
  2507. @register_model_view(PowerOutlet, 'delete')
  2508. class PowerOutletDeleteView(generic.ObjectDeleteView):
  2509. queryset = PowerOutlet.objects.all()
  2510. @register_model_view(PowerOutlet, 'bulk_import', path='import', detail=False)
  2511. class PowerOutletBulkImportView(generic.BulkImportView):
  2512. queryset = PowerOutlet.objects.all()
  2513. model_form = forms.PowerOutletImportForm
  2514. @register_model_view(PowerOutlet, 'bulk_edit', path='edit', detail=False)
  2515. class PowerOutletBulkEditView(generic.BulkEditView):
  2516. queryset = PowerOutlet.objects.all()
  2517. filterset = filtersets.PowerOutletFilterSet
  2518. table = tables.PowerOutletTable
  2519. form = forms.PowerOutletBulkEditForm
  2520. @register_model_view(PowerOutlet, 'bulk_rename', path='rename', detail=False)
  2521. class PowerOutletBulkRenameView(generic.BulkRenameView):
  2522. queryset = PowerOutlet.objects.all()
  2523. filterset = filtersets.PowerOutletFilterSet
  2524. @register_model_view(PowerOutlet, 'bulk_disconnect', path='disconnect', detail=False)
  2525. class PowerOutletBulkDisconnectView(BulkDisconnectView):
  2526. queryset = PowerOutlet.objects.all()
  2527. @register_model_view(PowerOutlet, 'bulk_delete', path='delete', detail=False)
  2528. class PowerOutletBulkDeleteView(generic.BulkDeleteView):
  2529. queryset = PowerOutlet.objects.all()
  2530. filterset = filtersets.PowerOutletFilterSet
  2531. table = tables.PowerOutletTable
  2532. # Trace view
  2533. register_model_view(PowerOutlet, 'trace', kwargs={'model': PowerOutlet})(PathTraceView)
  2534. #
  2535. # Interfaces
  2536. #
  2537. @register_model_view(Interface, 'list', path='', detail=False)
  2538. class InterfaceListView(generic.ObjectListView):
  2539. queryset = Interface.objects.all()
  2540. filterset = filtersets.InterfaceFilterSet
  2541. filterset_form = forms.InterfaceFilterForm
  2542. table = tables.InterfaceTable
  2543. @register_model_view(Interface)
  2544. class InterfaceView(generic.ObjectView):
  2545. queryset = Interface.objects.all()
  2546. def get_extra_context(self, request, instance):
  2547. # Get assigned VDCs
  2548. vdc_table = tables.VirtualDeviceContextTable(
  2549. data=instance.vdcs.restrict(request.user, 'view').prefetch_related('device'),
  2550. exclude=(
  2551. 'tenant', 'tenant_group', 'primary_ip', 'primary_ip4', 'primary_ip6', 'oob_ip', 'comments', 'tags',
  2552. 'created', 'last_updated', 'actions',
  2553. ),
  2554. orderable=False
  2555. )
  2556. vdc_table.configure(request)
  2557. # Get bridge interfaces
  2558. bridge_interfaces = Interface.objects.restrict(request.user, 'view').filter(bridge=instance)
  2559. bridge_interfaces_table = tables.InterfaceTable(
  2560. bridge_interfaces,
  2561. exclude=('device', 'parent'),
  2562. orderable=False
  2563. )
  2564. bridge_interfaces_table.configure(request)
  2565. # Get child interfaces
  2566. child_interfaces = Interface.objects.restrict(request.user, 'view').filter(parent=instance)
  2567. child_interfaces_table = tables.InterfaceTable(
  2568. child_interfaces,
  2569. exclude=('device', 'parent'),
  2570. orderable=False
  2571. )
  2572. child_interfaces_table.configure(request)
  2573. # Get LAG interfaces
  2574. lag_interfaces = Interface.objects.restrict(request.user, 'view').filter(lag=instance)
  2575. lag_interfaces_table = tables.InterfaceLAGMemberTable(
  2576. lag_interfaces,
  2577. orderable=False
  2578. )
  2579. lag_interfaces_table.configure(request)
  2580. # Get assigned VLANs and annotate whether each is tagged or untagged
  2581. vlans = []
  2582. if instance.untagged_vlan is not None:
  2583. vlans.append(instance.untagged_vlan)
  2584. vlans[0].tagged = False
  2585. for vlan in instance.tagged_vlans.restrict(request.user).prefetch_related('site', 'group', 'tenant', 'role'):
  2586. vlan.tagged = True
  2587. vlans.append(vlan)
  2588. vlan_table = InterfaceVLANTable(
  2589. interface=instance,
  2590. data=vlans,
  2591. orderable=False
  2592. )
  2593. vlan_table.configure(request)
  2594. # Get VLAN translation rules
  2595. vlan_translation_table = None
  2596. if instance.vlan_translation_policy:
  2597. vlan_translation_table = VLANTranslationRuleTable(
  2598. data=instance.vlan_translation_policy.rules.all(),
  2599. orderable=False
  2600. )
  2601. vlan_translation_table.configure(request)
  2602. return {
  2603. 'vdc_table': vdc_table,
  2604. 'bridge_interfaces': bridge_interfaces,
  2605. 'bridge_interfaces_table': bridge_interfaces_table,
  2606. 'child_interfaces_table': child_interfaces_table,
  2607. 'lag_interfaces_table': lag_interfaces_table,
  2608. 'vlan_table': vlan_table,
  2609. 'vlan_translation_table': vlan_translation_table,
  2610. }
  2611. @register_model_view(Interface, 'add', detail=False)
  2612. class InterfaceCreateView(generic.ComponentCreateView):
  2613. queryset = Interface.objects.all()
  2614. form = forms.InterfaceCreateForm
  2615. model_form = forms.InterfaceForm
  2616. @register_model_view(Interface, 'edit')
  2617. class InterfaceEditView(generic.ObjectEditView):
  2618. queryset = Interface.objects.all()
  2619. form = forms.InterfaceForm
  2620. @register_model_view(Interface, 'delete')
  2621. class InterfaceDeleteView(generic.ObjectDeleteView):
  2622. queryset = Interface.objects.all()
  2623. @register_model_view(Interface, 'bulk_import', path='import', detail=False)
  2624. class InterfaceBulkImportView(generic.BulkImportView):
  2625. queryset = Interface.objects.all()
  2626. model_form = forms.InterfaceImportForm
  2627. @register_model_view(Interface, 'bulk_edit', path='edit', detail=False)
  2628. class InterfaceBulkEditView(generic.BulkEditView):
  2629. queryset = Interface.objects.all()
  2630. filterset = filtersets.InterfaceFilterSet
  2631. table = tables.InterfaceTable
  2632. form = forms.InterfaceBulkEditForm
  2633. def post_save_operations(self, form, obj):
  2634. super().post_save_operations(form, obj)
  2635. # Add/remove tagged VLANs
  2636. if obj.mode == InterfaceModeChoices.MODE_TAGGED:
  2637. if form.cleaned_data.get('add_tagged_vlans', None):
  2638. obj.tagged_vlans.add(*form.cleaned_data['add_tagged_vlans'])
  2639. if form.cleaned_data.get('remove_tagged_vlans', None):
  2640. obj.tagged_vlans.remove(*form.cleaned_data['remove_tagged_vlans'])
  2641. @register_model_view(Interface, 'bulk_rename', path='rename', detail=False)
  2642. class InterfaceBulkRenameView(generic.BulkRenameView):
  2643. queryset = Interface.objects.all()
  2644. filterset = filtersets.InterfaceFilterSet
  2645. @register_model_view(Interface, 'bulk_disconnect', path='disconnect', detail=False)
  2646. class InterfaceBulkDisconnectView(BulkDisconnectView):
  2647. queryset = Interface.objects.all()
  2648. @register_model_view(Interface, 'bulk_delete', path='delete', detail=False)
  2649. class InterfaceBulkDeleteView(generic.BulkDeleteView):
  2650. # Ensure child interfaces are deleted prior to their parents
  2651. queryset = Interface.objects.order_by('device', 'parent', CollateAsChar('_name'))
  2652. filterset = filtersets.InterfaceFilterSet
  2653. table = tables.InterfaceTable
  2654. # Trace view
  2655. register_model_view(Interface, 'trace', kwargs={'model': Interface})(PathTraceView)
  2656. #
  2657. # Front ports
  2658. #
  2659. @register_model_view(FrontPort, 'list', path='', detail=False)
  2660. class FrontPortListView(generic.ObjectListView):
  2661. queryset = FrontPort.objects.all()
  2662. filterset = filtersets.FrontPortFilterSet
  2663. filterset_form = forms.FrontPortFilterForm
  2664. table = tables.FrontPortTable
  2665. @register_model_view(FrontPort)
  2666. class FrontPortView(generic.ObjectView):
  2667. queryset = FrontPort.objects.all()
  2668. def get_extra_context(self, request, instance):
  2669. return {
  2670. 'rear_port_mappings': PortMapping.objects.filter(front_port=instance).prefetch_related('rear_port'),
  2671. }
  2672. @register_model_view(FrontPort, 'add', detail=False)
  2673. class FrontPortCreateView(generic.ComponentCreateView):
  2674. queryset = FrontPort.objects.all()
  2675. form = forms.FrontPortCreateForm
  2676. model_form = forms.FrontPortForm
  2677. @register_model_view(FrontPort, 'edit')
  2678. class FrontPortEditView(generic.ObjectEditView):
  2679. queryset = FrontPort.objects.all()
  2680. form = forms.FrontPortForm
  2681. @register_model_view(FrontPort, 'delete')
  2682. class FrontPortDeleteView(generic.ObjectDeleteView):
  2683. queryset = FrontPort.objects.all()
  2684. @register_model_view(FrontPort, 'bulk_import', path='import', detail=False)
  2685. class FrontPortBulkImportView(generic.BulkImportView):
  2686. queryset = FrontPort.objects.all()
  2687. model_form = forms.FrontPortImportForm
  2688. @register_model_view(FrontPort, 'bulk_edit', path='edit', detail=False)
  2689. class FrontPortBulkEditView(generic.BulkEditView):
  2690. queryset = FrontPort.objects.all()
  2691. filterset = filtersets.FrontPortFilterSet
  2692. table = tables.FrontPortTable
  2693. form = forms.FrontPortBulkEditForm
  2694. @register_model_view(FrontPort, 'bulk_rename', path='rename', detail=False)
  2695. class FrontPortBulkRenameView(generic.BulkRenameView):
  2696. queryset = FrontPort.objects.all()
  2697. filterset = filtersets.FrontPortFilterSet
  2698. @register_model_view(FrontPort, 'bulk_disconnect', path='disconnect', detail=False)
  2699. class FrontPortBulkDisconnectView(BulkDisconnectView):
  2700. queryset = FrontPort.objects.all()
  2701. @register_model_view(FrontPort, 'bulk_delete', path='delete', detail=False)
  2702. class FrontPortBulkDeleteView(generic.BulkDeleteView):
  2703. queryset = FrontPort.objects.all()
  2704. filterset = filtersets.FrontPortFilterSet
  2705. table = tables.FrontPortTable
  2706. # Trace view
  2707. register_model_view(FrontPort, 'trace', kwargs={'model': FrontPort})(PathTraceView)
  2708. #
  2709. # Rear ports
  2710. #
  2711. @register_model_view(RearPort, 'list', path='', detail=False)
  2712. class RearPortListView(generic.ObjectListView):
  2713. queryset = RearPort.objects.all()
  2714. filterset = filtersets.RearPortFilterSet
  2715. filterset_form = forms.RearPortFilterForm
  2716. table = tables.RearPortTable
  2717. @register_model_view(RearPort)
  2718. class RearPortView(generic.ObjectView):
  2719. queryset = RearPort.objects.all()
  2720. def get_extra_context(self, request, instance):
  2721. return {
  2722. 'front_port_mappings': PortMapping.objects.filter(rear_port=instance).prefetch_related('front_port'),
  2723. }
  2724. @register_model_view(RearPort, 'add', detail=False)
  2725. class RearPortCreateView(generic.ComponentCreateView):
  2726. queryset = RearPort.objects.all()
  2727. form = forms.RearPortCreateForm
  2728. model_form = forms.RearPortForm
  2729. @register_model_view(RearPort, 'edit')
  2730. class RearPortEditView(generic.ObjectEditView):
  2731. queryset = RearPort.objects.all()
  2732. form = forms.RearPortForm
  2733. @register_model_view(RearPort, 'delete')
  2734. class RearPortDeleteView(generic.ObjectDeleteView):
  2735. queryset = RearPort.objects.all()
  2736. @register_model_view(RearPort, 'bulk_import', path='import', detail=False)
  2737. class RearPortBulkImportView(generic.BulkImportView):
  2738. queryset = RearPort.objects.all()
  2739. model_form = forms.RearPortImportForm
  2740. @register_model_view(RearPort, 'bulk_edit', path='edit', detail=False)
  2741. class RearPortBulkEditView(generic.BulkEditView):
  2742. queryset = RearPort.objects.all()
  2743. filterset = filtersets.RearPortFilterSet
  2744. table = tables.RearPortTable
  2745. form = forms.RearPortBulkEditForm
  2746. @register_model_view(RearPort, 'bulk_rename', path='rename', detail=False)
  2747. class RearPortBulkRenameView(generic.BulkRenameView):
  2748. queryset = RearPort.objects.all()
  2749. @register_model_view(RearPort, 'bulk_disconnect', path='disconnect', detail=False)
  2750. class RearPortBulkDisconnectView(BulkDisconnectView):
  2751. queryset = RearPort.objects.all()
  2752. filterset = filtersets.RearPortFilterSet
  2753. @register_model_view(RearPort, 'bulk_delete', path='delete', detail=False)
  2754. class RearPortBulkDeleteView(generic.BulkDeleteView):
  2755. queryset = RearPort.objects.all()
  2756. filterset = filtersets.RearPortFilterSet
  2757. table = tables.RearPortTable
  2758. # Trace view
  2759. register_model_view(RearPort, 'trace', kwargs={'model': RearPort})(PathTraceView)
  2760. #
  2761. # Module bays
  2762. #
  2763. @register_model_view(ModuleBay, 'list', path='', detail=False)
  2764. class ModuleBayListView(generic.ObjectListView):
  2765. queryset = ModuleBay.objects.select_related('installed_module__module_type')
  2766. filterset = filtersets.ModuleBayFilterSet
  2767. filterset_form = forms.ModuleBayFilterForm
  2768. table = tables.ModuleBayTable
  2769. @register_model_view(ModuleBay)
  2770. class ModuleBayView(generic.ObjectView):
  2771. queryset = ModuleBay.objects.all()
  2772. @register_model_view(ModuleBay, 'add', detail=False)
  2773. class ModuleBayCreateView(generic.ComponentCreateView):
  2774. queryset = ModuleBay.objects.all()
  2775. form = forms.ModuleBayCreateForm
  2776. model_form = forms.ModuleBayForm
  2777. @register_model_view(ModuleBay, 'edit')
  2778. class ModuleBayEditView(generic.ObjectEditView):
  2779. queryset = ModuleBay.objects.all()
  2780. form = forms.ModuleBayForm
  2781. @register_model_view(ModuleBay, 'delete')
  2782. class ModuleBayDeleteView(generic.ObjectDeleteView):
  2783. queryset = ModuleBay.objects.all()
  2784. @register_model_view(ModuleBay, 'bulk_import', path='import', detail=False)
  2785. class ModuleBayBulkImportView(generic.BulkImportView):
  2786. queryset = ModuleBay.objects.all()
  2787. model_form = forms.ModuleBayImportForm
  2788. @register_model_view(ModuleBay, 'bulk_edit', path='edit', detail=False)
  2789. class ModuleBayBulkEditView(generic.BulkEditView):
  2790. queryset = ModuleBay.objects.all()
  2791. filterset = filtersets.ModuleBayFilterSet
  2792. table = tables.ModuleBayTable
  2793. form = forms.ModuleBayBulkEditForm
  2794. @register_model_view(ModuleBay, 'bulk_rename', path='rename', detail=False)
  2795. class ModuleBayBulkRenameView(generic.BulkRenameView):
  2796. queryset = ModuleBay.objects.all()
  2797. filterset = filtersets.ModuleBayFilterSet
  2798. @register_model_view(ModuleBay, 'bulk_delete', path='delete', detail=False)
  2799. class ModuleBayBulkDeleteView(generic.BulkDeleteView):
  2800. queryset = ModuleBay.objects.all()
  2801. filterset = filtersets.ModuleBayFilterSet
  2802. table = tables.ModuleBayTable
  2803. #
  2804. # Device bays
  2805. #
  2806. @register_model_view(DeviceBay, 'list', path='', detail=False)
  2807. class DeviceBayListView(generic.ObjectListView):
  2808. queryset = DeviceBay.objects.all()
  2809. filterset = filtersets.DeviceBayFilterSet
  2810. filterset_form = forms.DeviceBayFilterForm
  2811. table = tables.DeviceBayTable
  2812. @register_model_view(DeviceBay)
  2813. class DeviceBayView(generic.ObjectView):
  2814. queryset = DeviceBay.objects.all()
  2815. @register_model_view(DeviceBay, 'add', detail=False)
  2816. class DeviceBayCreateView(generic.ComponentCreateView):
  2817. queryset = DeviceBay.objects.all()
  2818. form = forms.DeviceBayCreateForm
  2819. model_form = forms.DeviceBayForm
  2820. @register_model_view(DeviceBay, 'edit')
  2821. class DeviceBayEditView(generic.ObjectEditView):
  2822. queryset = DeviceBay.objects.all()
  2823. form = forms.DeviceBayForm
  2824. @register_model_view(DeviceBay, 'delete')
  2825. class DeviceBayDeleteView(generic.ObjectDeleteView):
  2826. queryset = DeviceBay.objects.all()
  2827. @register_model_view(DeviceBay, 'populate')
  2828. class DeviceBayPopulateView(generic.ObjectEditView):
  2829. queryset = DeviceBay.objects.all()
  2830. def get(self, request, pk):
  2831. device_bay = get_object_or_404(self.queryset, pk=pk)
  2832. form = forms.PopulateDeviceBayForm(device_bay)
  2833. return render(request, 'dcim/devicebay_populate.html', {
  2834. 'device_bay': device_bay,
  2835. 'form': form,
  2836. 'return_url': self.get_return_url(request, device_bay),
  2837. })
  2838. def post(self, request, pk):
  2839. device_bay = get_object_or_404(self.queryset, pk=pk)
  2840. form = forms.PopulateDeviceBayForm(device_bay, request.POST)
  2841. if form.is_valid():
  2842. device_bay.snapshot()
  2843. device_bay.installed_device = form.cleaned_data['installed_device']
  2844. device_bay.save()
  2845. messages.success(
  2846. request,
  2847. _("Installed device {device} in bay {device_bay}.").format(
  2848. device=device_bay.installed_device,
  2849. device_bay=device_bay
  2850. )
  2851. )
  2852. return_url = self.get_return_url(request)
  2853. return redirect(return_url)
  2854. return render(request, 'dcim/devicebay_populate.html', {
  2855. 'device_bay': device_bay,
  2856. 'form': form,
  2857. 'return_url': self.get_return_url(request, device_bay),
  2858. })
  2859. @register_model_view(DeviceBay, 'depopulate')
  2860. class DeviceBayDepopulateView(generic.ObjectEditView):
  2861. queryset = DeviceBay.objects.all()
  2862. def get(self, request, pk):
  2863. device_bay = get_object_or_404(self.queryset, pk=pk)
  2864. form = ConfirmationForm()
  2865. return render(request, 'dcim/devicebay_depopulate.html', {
  2866. 'device_bay': device_bay,
  2867. 'form': form,
  2868. 'return_url': self.get_return_url(request, device_bay),
  2869. })
  2870. def post(self, request, pk):
  2871. device_bay = get_object_or_404(self.queryset, pk=pk)
  2872. form = ConfirmationForm(request.POST)
  2873. if form.is_valid():
  2874. device_bay.snapshot()
  2875. removed_device = device_bay.installed_device
  2876. device_bay.installed_device = None
  2877. device_bay.save()
  2878. messages.success(
  2879. request,
  2880. _("Removed device {device} from bay {device_bay}.").format(
  2881. device=removed_device,
  2882. device_bay=device_bay
  2883. )
  2884. )
  2885. return_url = self.get_return_url(request, device_bay.device)
  2886. return redirect(return_url)
  2887. return render(request, 'dcim/devicebay_depopulate.html', {
  2888. 'device_bay': device_bay,
  2889. 'form': form,
  2890. 'return_url': self.get_return_url(request, device_bay),
  2891. })
  2892. @register_model_view(DeviceBay, 'bulk_import', path='import', detail=False)
  2893. class DeviceBayBulkImportView(generic.BulkImportView):
  2894. queryset = DeviceBay.objects.all()
  2895. model_form = forms.DeviceBayImportForm
  2896. @register_model_view(DeviceBay, 'bulk_edit', path='edit', detail=False)
  2897. class DeviceBayBulkEditView(generic.BulkEditView):
  2898. queryset = DeviceBay.objects.all()
  2899. filterset = filtersets.DeviceBayFilterSet
  2900. table = tables.DeviceBayTable
  2901. form = forms.DeviceBayBulkEditForm
  2902. @register_model_view(DeviceBay, 'bulk_rename', path='rename', detail=False)
  2903. class DeviceBayBulkRenameView(generic.BulkRenameView):
  2904. queryset = DeviceBay.objects.all()
  2905. filterset = filtersets.DeviceBayFilterSet
  2906. @register_model_view(DeviceBay, 'bulk_delete', path='delete', detail=False)
  2907. class DeviceBayBulkDeleteView(generic.BulkDeleteView):
  2908. queryset = DeviceBay.objects.all()
  2909. filterset = filtersets.DeviceBayFilterSet
  2910. table = tables.DeviceBayTable
  2911. #
  2912. # Inventory items
  2913. #
  2914. @register_model_view(InventoryItem, 'list', path='', detail=False)
  2915. class InventoryItemListView(generic.ObjectListView):
  2916. queryset = InventoryItem.objects.all()
  2917. filterset = filtersets.InventoryItemFilterSet
  2918. filterset_form = forms.InventoryItemFilterForm
  2919. table = tables.InventoryItemTable
  2920. @register_model_view(InventoryItem)
  2921. class InventoryItemView(generic.ObjectView):
  2922. queryset = InventoryItem.objects.all()
  2923. @register_model_view(InventoryItem, 'edit')
  2924. class InventoryItemEditView(generic.ObjectEditView):
  2925. queryset = InventoryItem.objects.all()
  2926. form = forms.InventoryItemForm
  2927. @register_model_view(InventoryItem, 'add', detail=False)
  2928. class InventoryItemCreateView(generic.ComponentCreateView):
  2929. queryset = InventoryItem.objects.all()
  2930. form = forms.InventoryItemCreateForm
  2931. model_form = forms.InventoryItemForm
  2932. @register_model_view(InventoryItem, 'delete')
  2933. class InventoryItemDeleteView(generic.ObjectDeleteView):
  2934. queryset = InventoryItem.objects.all()
  2935. @register_model_view(InventoryItem, 'bulk_import', path='import', detail=False)
  2936. class InventoryItemBulkImportView(generic.BulkImportView):
  2937. queryset = InventoryItem.objects.all()
  2938. model_form = forms.InventoryItemImportForm
  2939. @register_model_view(InventoryItem, 'bulk_edit', path='edit', detail=False)
  2940. class InventoryItemBulkEditView(generic.BulkEditView):
  2941. queryset = InventoryItem.objects.all()
  2942. filterset = filtersets.InventoryItemFilterSet
  2943. table = tables.InventoryItemTable
  2944. form = forms.InventoryItemBulkEditForm
  2945. @register_model_view(InventoryItem, 'bulk_rename', path='rename', detail=False)
  2946. class InventoryItemBulkRenameView(generic.BulkRenameView):
  2947. queryset = InventoryItem.objects.all()
  2948. filterset = filtersets.InventoryItemFilterSet
  2949. @register_model_view(InventoryItem, 'bulk_delete', path='delete', detail=False)
  2950. class InventoryItemBulkDeleteView(generic.BulkDeleteView):
  2951. queryset = InventoryItem.objects.all()
  2952. filterset = filtersets.InventoryItemFilterSet
  2953. table = tables.InventoryItemTable
  2954. @register_model_view(InventoryItem, 'children')
  2955. class InventoryItemChildrenView(generic.ObjectChildrenView):
  2956. queryset = InventoryItem.objects.all()
  2957. child_model = InventoryItem
  2958. table = tables.InventoryItemTable
  2959. filterset = filtersets.InventoryItemFilterSet
  2960. filterset_form = forms.InventoryItemFilterForm
  2961. tab = ViewTab(
  2962. label=_('Children'),
  2963. badge=lambda obj: obj.child_items.count(),
  2964. permission='dcim.view_inventoryitem',
  2965. hide_if_empty=True,
  2966. weight=5000
  2967. )
  2968. def get_children(self, request, parent):
  2969. return parent.child_items.restrict(request.user, 'view')
  2970. #
  2971. # Inventory item roles
  2972. #
  2973. @register_model_view(InventoryItemRole, 'list', path='', detail=False)
  2974. class InventoryItemRoleListView(generic.ObjectListView):
  2975. queryset = InventoryItemRole.objects.annotate(
  2976. inventoryitem_count=count_related(InventoryItem, 'role'),
  2977. )
  2978. filterset = filtersets.InventoryItemRoleFilterSet
  2979. filterset_form = forms.InventoryItemRoleFilterForm
  2980. table = tables.InventoryItemRoleTable
  2981. @register_model_view(InventoryItemRole)
  2982. class InventoryItemRoleView(generic.ObjectView):
  2983. queryset = InventoryItemRole.objects.all()
  2984. def get_extra_context(self, request, instance):
  2985. return {
  2986. 'inventoryitem_count': InventoryItem.objects.filter(role=instance).count(),
  2987. }
  2988. @register_model_view(InventoryItemRole, 'add', detail=False)
  2989. @register_model_view(InventoryItemRole, 'edit')
  2990. class InventoryItemRoleEditView(generic.ObjectEditView):
  2991. queryset = InventoryItemRole.objects.all()
  2992. form = forms.InventoryItemRoleForm
  2993. @register_model_view(InventoryItemRole, 'delete')
  2994. class InventoryItemRoleDeleteView(generic.ObjectDeleteView):
  2995. queryset = InventoryItemRole.objects.all()
  2996. @register_model_view(InventoryItemRole, 'bulk_import', path='import', detail=False)
  2997. class InventoryItemRoleBulkImportView(generic.BulkImportView):
  2998. queryset = InventoryItemRole.objects.all()
  2999. model_form = forms.InventoryItemRoleImportForm
  3000. @register_model_view(InventoryItemRole, 'bulk_edit', path='edit', detail=False)
  3001. class InventoryItemRoleBulkEditView(generic.BulkEditView):
  3002. queryset = InventoryItemRole.objects.annotate(
  3003. inventoryitem_count=count_related(InventoryItem, 'role'),
  3004. )
  3005. filterset = filtersets.InventoryItemRoleFilterSet
  3006. table = tables.InventoryItemRoleTable
  3007. form = forms.InventoryItemRoleBulkEditForm
  3008. @register_model_view(InventoryItemRole, 'bulk_rename', path='rename', detail=False)
  3009. class InventoryItemRoleBulkRenameView(generic.BulkRenameView):
  3010. queryset = InventoryItemRole.objects.all()
  3011. filterset = filtersets.InventoryItemRoleFilterSet
  3012. @register_model_view(InventoryItemRole, 'bulk_delete', path='delete', detail=False)
  3013. class InventoryItemRoleBulkDeleteView(generic.BulkDeleteView):
  3014. queryset = InventoryItemRole.objects.annotate(
  3015. inventoryitem_count=count_related(InventoryItem, 'role'),
  3016. )
  3017. filterset = filtersets.InventoryItemRoleFilterSet
  3018. table = tables.InventoryItemRoleTable
  3019. #
  3020. # Bulk Device component creation
  3021. #
  3022. class DeviceBulkAddConsolePortView(generic.BulkComponentCreateView):
  3023. parent_model = Device
  3024. parent_field = 'device'
  3025. form = forms.ConsolePortBulkCreateForm
  3026. queryset = ConsolePort.objects.all()
  3027. model_form = forms.ConsolePortForm
  3028. filterset = filtersets.DeviceFilterSet
  3029. table = tables.DeviceTable
  3030. default_return_url = 'dcim:device_list'
  3031. class DeviceBulkAddConsoleServerPortView(generic.BulkComponentCreateView):
  3032. parent_model = Device
  3033. parent_field = 'device'
  3034. form = forms.ConsoleServerPortBulkCreateForm
  3035. queryset = ConsoleServerPort.objects.all()
  3036. model_form = forms.ConsoleServerPortForm
  3037. filterset = filtersets.DeviceFilterSet
  3038. table = tables.DeviceTable
  3039. default_return_url = 'dcim:device_list'
  3040. class DeviceBulkAddPowerPortView(generic.BulkComponentCreateView):
  3041. parent_model = Device
  3042. parent_field = 'device'
  3043. form = forms.PowerPortBulkCreateForm
  3044. queryset = PowerPort.objects.all()
  3045. model_form = forms.PowerPortForm
  3046. filterset = filtersets.DeviceFilterSet
  3047. table = tables.DeviceTable
  3048. default_return_url = 'dcim:device_list'
  3049. class DeviceBulkAddPowerOutletView(generic.BulkComponentCreateView):
  3050. parent_model = Device
  3051. parent_field = 'device'
  3052. form = forms.PowerOutletBulkCreateForm
  3053. queryset = PowerOutlet.objects.all()
  3054. model_form = forms.PowerOutletForm
  3055. filterset = filtersets.DeviceFilterSet
  3056. table = tables.DeviceTable
  3057. default_return_url = 'dcim:device_list'
  3058. class DeviceBulkAddInterfaceView(generic.BulkComponentCreateView):
  3059. parent_model = Device
  3060. parent_field = 'device'
  3061. form = forms.InterfaceBulkCreateForm
  3062. queryset = Interface.objects.all()
  3063. model_form = forms.InterfaceForm
  3064. filterset = filtersets.DeviceFilterSet
  3065. table = tables.DeviceTable
  3066. default_return_url = 'dcim:device_list'
  3067. class DeviceBulkAddRearPortView(generic.BulkComponentCreateView):
  3068. parent_model = Device
  3069. parent_field = 'device'
  3070. form = forms.RearPortBulkCreateForm
  3071. queryset = RearPort.objects.all()
  3072. model_form = forms.RearPortForm
  3073. filterset = filtersets.DeviceFilterSet
  3074. table = tables.DeviceTable
  3075. default_return_url = 'dcim:device_list'
  3076. class DeviceBulkAddModuleBayView(generic.BulkComponentCreateView):
  3077. parent_model = Device
  3078. parent_field = 'device'
  3079. form = forms.ModuleBayBulkCreateForm
  3080. queryset = ModuleBay.objects.all()
  3081. model_form = forms.ModuleBayForm
  3082. filterset = filtersets.DeviceFilterSet
  3083. table = tables.DeviceTable
  3084. default_return_url = 'dcim:device_list'
  3085. class DeviceBulkAddDeviceBayView(generic.BulkComponentCreateView):
  3086. parent_model = Device
  3087. parent_field = 'device'
  3088. form = forms.DeviceBayBulkCreateForm
  3089. queryset = DeviceBay.objects.all()
  3090. model_form = forms.DeviceBayForm
  3091. filterset = filtersets.DeviceFilterSet
  3092. table = tables.DeviceTable
  3093. default_return_url = 'dcim:device_list'
  3094. class DeviceBulkAddInventoryItemView(generic.BulkComponentCreateView):
  3095. parent_model = Device
  3096. parent_field = 'device'
  3097. form = forms.InventoryItemBulkCreateForm
  3098. queryset = InventoryItem.objects.all()
  3099. model_form = forms.InventoryItemForm
  3100. filterset = filtersets.DeviceFilterSet
  3101. table = tables.DeviceTable
  3102. default_return_url = 'dcim:device_list'
  3103. #
  3104. # Cables
  3105. #
  3106. @register_model_view(Cable, 'list', path='', detail=False)
  3107. class CableListView(generic.ObjectListView):
  3108. queryset = Cable.objects.prefetch_related(
  3109. 'terminations__termination', 'terminations___device', 'terminations___rack', 'terminations___location',
  3110. 'terminations___site',
  3111. )
  3112. filterset = filtersets.CableFilterSet
  3113. filterset_form = forms.CableFilterForm
  3114. table = tables.CableTable
  3115. @register_model_view(Cable)
  3116. class CableView(generic.ObjectView):
  3117. queryset = Cable.objects.all()
  3118. @register_model_view(Cable, 'add', detail=False)
  3119. @register_model_view(Cable, 'edit')
  3120. class CableEditView(generic.ObjectEditView):
  3121. queryset = Cable.objects.all()
  3122. template_name = 'dcim/cable_edit.html'
  3123. htmx_template_name = 'dcim/htmx/cable_edit.html'
  3124. def alter_object(self, obj, request, url_args, url_kwargs):
  3125. """
  3126. Hack into alter_object() to set the form class when editing an existing Cable, since ObjectEditView
  3127. doesn't currently provide a hook for dynamic class resolution.
  3128. """
  3129. a_terminations_type = CABLE_TERMINATION_TYPES.get(
  3130. request.POST.get('a_terminations_type') or request.GET.get('a_terminations_type')
  3131. )
  3132. b_terminations_type = CABLE_TERMINATION_TYPES.get(
  3133. request.POST.get('b_terminations_type') or request.GET.get('b_terminations_type')
  3134. )
  3135. if obj.pk:
  3136. if not a_terminations_type and (termination_a := obj.terminations.filter(cable_end='A').first()):
  3137. a_terminations_type = termination_a.termination._meta.model
  3138. if not b_terminations_type and (termination_b := obj.terminations.filter(cable_end='B').first()):
  3139. b_terminations_type = termination_b.termination._meta.model
  3140. self.form = forms.get_cable_form(a_terminations_type, b_terminations_type)
  3141. return super().alter_object(obj, request, url_args, url_kwargs)
  3142. @register_model_view(Cable, 'delete')
  3143. class CableDeleteView(generic.ObjectDeleteView):
  3144. queryset = Cable.objects.all()
  3145. @register_model_view(Cable, 'bulk_import', path='import', detail=False)
  3146. class CableBulkImportView(generic.BulkImportView):
  3147. queryset = Cable.objects.all()
  3148. model_form = forms.CableImportForm
  3149. @register_model_view(Cable, 'bulk_edit', path='edit', detail=False)
  3150. class CableBulkEditView(generic.BulkEditView):
  3151. queryset = Cable.objects.prefetch_related(
  3152. 'terminations__termination', 'terminations___device', 'terminations___rack', 'terminations___location',
  3153. 'terminations___site',
  3154. )
  3155. filterset = filtersets.CableFilterSet
  3156. table = tables.CableTable
  3157. form = forms.CableBulkEditForm
  3158. @register_model_view(Cable, 'bulk_rename', path='rename', detail=False)
  3159. class CableBulkRenameView(generic.BulkRenameView):
  3160. queryset = Cable.objects.all()
  3161. field_name = 'label'
  3162. filterset = filtersets.CableFilterSet
  3163. @register_model_view(Cable, 'bulk_delete', path='delete', detail=False)
  3164. class CableBulkDeleteView(generic.BulkDeleteView):
  3165. queryset = Cable.objects.prefetch_related(
  3166. 'terminations__termination', 'terminations___device', 'terminations___rack', 'terminations___location',
  3167. 'terminations___site',
  3168. )
  3169. filterset = filtersets.CableFilterSet
  3170. table = tables.CableTable
  3171. #
  3172. # Connections
  3173. #
  3174. class ConsoleConnectionsListView(generic.ObjectListView):
  3175. queryset = ConsolePort.objects.filter(_path__is_complete=True)
  3176. filterset = filtersets.ConsoleConnectionFilterSet
  3177. filterset_form = forms.ConsoleConnectionFilterForm
  3178. table = tables.ConsoleConnectionTable
  3179. template_name = 'dcim/connections_list.html'
  3180. actions = (BulkExport,)
  3181. def get_extra_context(self, request):
  3182. return {
  3183. 'title': 'Console Connections'
  3184. }
  3185. class PowerConnectionsListView(generic.ObjectListView):
  3186. queryset = PowerPort.objects.filter(_path__is_complete=True)
  3187. filterset = filtersets.PowerConnectionFilterSet
  3188. filterset_form = forms.PowerConnectionFilterForm
  3189. table = tables.PowerConnectionTable
  3190. template_name = 'dcim/connections_list.html'
  3191. actions = (BulkExport,)
  3192. def get_extra_context(self, request):
  3193. return {
  3194. 'title': 'Power Connections'
  3195. }
  3196. class InterfaceConnectionsListView(generic.ObjectListView):
  3197. queryset = Interface.objects.filter(_path__is_complete=True)
  3198. filterset = filtersets.InterfaceConnectionFilterSet
  3199. filterset_form = forms.InterfaceConnectionFilterForm
  3200. table = tables.InterfaceConnectionTable
  3201. template_name = 'dcim/connections_list.html'
  3202. actions = (BulkExport,)
  3203. def get_extra_context(self, request):
  3204. return {
  3205. 'title': 'Interface Connections'
  3206. }
  3207. #
  3208. # Virtual chassis
  3209. #
  3210. @register_model_view(VirtualChassis, 'list', path='', detail=False)
  3211. class VirtualChassisListView(generic.ObjectListView):
  3212. queryset = VirtualChassis.objects.all()
  3213. table = tables.VirtualChassisTable
  3214. filterset = filtersets.VirtualChassisFilterSet
  3215. filterset_form = forms.VirtualChassisFilterForm
  3216. @register_model_view(VirtualChassis)
  3217. class VirtualChassisView(generic.ObjectView):
  3218. queryset = VirtualChassis.objects.all()
  3219. def get_extra_context(self, request, instance):
  3220. members = Device.objects.restrict(request.user).filter(virtual_chassis=instance)
  3221. return {
  3222. 'members': members,
  3223. }
  3224. @register_model_view(VirtualChassis, 'add', detail=False)
  3225. class VirtualChassisCreateView(generic.ObjectEditView):
  3226. queryset = VirtualChassis.objects.all()
  3227. form = forms.VirtualChassisCreateForm
  3228. @register_model_view(VirtualChassis, 'edit')
  3229. class VirtualChassisEditView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View):
  3230. queryset = VirtualChassis.objects.all()
  3231. def get_required_permission(self):
  3232. return 'dcim.change_virtualchassis'
  3233. def get(self, request, pk):
  3234. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  3235. VCMemberFormSet = modelformset_factory(
  3236. model=Device,
  3237. form=forms.DeviceVCMembershipForm,
  3238. formset=forms.BaseVCMemberFormSet,
  3239. extra=0
  3240. )
  3241. members_queryset = virtual_chassis.members.prefetch_related('rack').order_by('vc_position')
  3242. vc_form = forms.VirtualChassisForm(instance=virtual_chassis)
  3243. vc_form.fields['master'].queryset = members_queryset
  3244. formset = VCMemberFormSet(queryset=members_queryset)
  3245. return render(request, 'dcim/virtualchassis_edit.html', {
  3246. 'object': virtual_chassis,
  3247. 'vc_form': vc_form,
  3248. 'formset': formset,
  3249. 'return_url': self.get_return_url(request, virtual_chassis),
  3250. })
  3251. def post(self, request, pk):
  3252. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  3253. virtual_chassis.snapshot()
  3254. VCMemberFormSet = modelformset_factory(
  3255. model=Device,
  3256. form=forms.DeviceVCMembershipForm,
  3257. formset=forms.BaseVCMemberFormSet,
  3258. extra=0
  3259. )
  3260. members_queryset = virtual_chassis.members.prefetch_related('rack').order_by('vc_position')
  3261. vc_form = forms.VirtualChassisForm(request.POST, instance=virtual_chassis)
  3262. vc_form.fields['master'].queryset = members_queryset
  3263. formset = VCMemberFormSet(request.POST, queryset=members_queryset)
  3264. if vc_form.is_valid() and formset.is_valid():
  3265. virtual_chassis._changelog_message = vc_form.cleaned_data.pop('changelog_message', '')
  3266. with transaction.atomic(using=router.db_for_write(Device)):
  3267. # Save the VirtualChassis
  3268. vc_form.save()
  3269. # Nullify the vc_position of each member first to allow reordering without raising an IntegrityError on
  3270. # duplicate positions. Then save each member instance.
  3271. members = formset.save(commit=False)
  3272. devices = Device.objects.filter(pk__in=[m.pk for m in members])
  3273. for device in devices:
  3274. device.snapshot()
  3275. device.vc_position = None
  3276. device.save()
  3277. for member in members:
  3278. member.save()
  3279. return redirect(virtual_chassis.get_absolute_url())
  3280. return render(request, 'dcim/virtualchassis_edit.html', {
  3281. 'vc_form': vc_form,
  3282. 'formset': formset,
  3283. 'return_url': self.get_return_url(request, virtual_chassis),
  3284. })
  3285. @register_model_view(VirtualChassis, 'delete')
  3286. class VirtualChassisDeleteView(generic.ObjectDeleteView):
  3287. queryset = VirtualChassis.objects.all()
  3288. @register_model_view(VirtualChassis, 'add_member', path='add-member')
  3289. class VirtualChassisAddMemberView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View):
  3290. queryset = VirtualChassis.objects.all()
  3291. def get_required_permission(self):
  3292. return 'dcim.change_virtualchassis'
  3293. def get(self, request, pk):
  3294. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  3295. initial_data = {k: request.GET[k] for k in request.GET}
  3296. member_select_form = forms.VCMemberSelectForm(initial=initial_data)
  3297. membership_form = forms.DeviceVCMembershipForm(initial=initial_data)
  3298. return render(request, 'dcim/virtualchassis_add_member.html', {
  3299. 'virtual_chassis': virtual_chassis,
  3300. 'member_select_form': member_select_form,
  3301. 'membership_form': membership_form,
  3302. 'return_url': self.get_return_url(request, virtual_chassis),
  3303. })
  3304. def post(self, request, pk):
  3305. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  3306. member_select_form = forms.VCMemberSelectForm(request.POST)
  3307. if member_select_form.is_valid():
  3308. device = member_select_form.cleaned_data['device']
  3309. device.snapshot()
  3310. device.virtual_chassis = virtual_chassis
  3311. data = {
  3312. 'vc_position': request.POST['vc_position'],
  3313. 'vc_priority': request.POST['vc_priority'],
  3314. }
  3315. membership_form = forms.DeviceVCMembershipForm(data=data, validate_vc_position=True, instance=device)
  3316. if membership_form.is_valid():
  3317. membership_form.save()
  3318. messages.success(request, mark_safe(
  3319. _('Added member <a href="{url}">{device}</a>').format(
  3320. url=device.get_absolute_url(), device=escape(device)
  3321. )
  3322. ))
  3323. if '_addanother' in request.POST and safe_for_redirect(request.get_full_path()):
  3324. return redirect(request.get_full_path())
  3325. return redirect(self.get_return_url(request, device))
  3326. else:
  3327. membership_form = forms.DeviceVCMembershipForm(data=request.POST)
  3328. return render(request, 'dcim/virtualchassis_add_member.html', {
  3329. 'virtual_chassis': virtual_chassis,
  3330. 'member_select_form': member_select_form,
  3331. 'membership_form': membership_form,
  3332. 'return_url': self.get_return_url(request, virtual_chassis),
  3333. })
  3334. class VirtualChassisRemoveMemberView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View):
  3335. queryset = Device.objects.all()
  3336. def get_required_permission(self):
  3337. return 'dcim.change_device'
  3338. def get(self, request, pk):
  3339. device = get_object_or_404(self.queryset, pk=pk, virtual_chassis__isnull=False)
  3340. form = ConfirmationForm(initial=request.GET)
  3341. return render(request, 'dcim/virtualchassis_remove_member.html', {
  3342. 'device': device,
  3343. 'form': form,
  3344. 'return_url': self.get_return_url(request, device),
  3345. })
  3346. def post(self, request, pk):
  3347. device = get_object_or_404(self.queryset, pk=pk, virtual_chassis__isnull=False)
  3348. form = ConfirmationForm(request.POST)
  3349. # Protect master device from being removed
  3350. virtual_chassis = VirtualChassis.objects.filter(master=device).first()
  3351. if virtual_chassis is not None:
  3352. messages.error(
  3353. request,
  3354. _('Unable to remove master device {device} from the virtual chassis.').format(device=device)
  3355. )
  3356. return redirect(device.get_absolute_url())
  3357. if form.is_valid():
  3358. device.snapshot()
  3359. device.virtual_chassis = None
  3360. device.vc_position = None
  3361. device.vc_priority = None
  3362. device.save()
  3363. msg = _('Removed {device} from virtual chassis {chassis}').format(
  3364. device=device,
  3365. chassis=device.virtual_chassis
  3366. )
  3367. messages.success(request, msg)
  3368. return redirect(self.get_return_url(request, device))
  3369. return render(request, 'dcim/virtualchassis_remove_member.html', {
  3370. 'device': device,
  3371. 'form': form,
  3372. 'return_url': self.get_return_url(request, device),
  3373. })
  3374. @register_model_view(VirtualChassis, 'bulk_import', path='import', detail=False)
  3375. class VirtualChassisBulkImportView(generic.BulkImportView):
  3376. queryset = VirtualChassis.objects.all()
  3377. model_form = forms.VirtualChassisImportForm
  3378. @register_model_view(VirtualChassis, 'bulk_edit', path='edit', detail=False)
  3379. class VirtualChassisBulkEditView(generic.BulkEditView):
  3380. queryset = VirtualChassis.objects.all()
  3381. filterset = filtersets.VirtualChassisFilterSet
  3382. table = tables.VirtualChassisTable
  3383. form = forms.VirtualChassisBulkEditForm
  3384. @register_model_view(VirtualChassis, 'bulk_rename', path='rename', detail=False)
  3385. class VirtualChassisBulkRenameView(generic.BulkRenameView):
  3386. queryset = VirtualChassis.objects.all()
  3387. filterset = filtersets.VirtualChassisFilterSet
  3388. @register_model_view(VirtualChassis, 'bulk_delete', path='delete', detail=False)
  3389. class VirtualChassisBulkDeleteView(generic.BulkDeleteView):
  3390. queryset = VirtualChassis.objects.all()
  3391. filterset = filtersets.VirtualChassisFilterSet
  3392. table = tables.VirtualChassisTable
  3393. #
  3394. # Power panels
  3395. #
  3396. @register_model_view(PowerPanel, 'list', path='', detail=False)
  3397. class PowerPanelListView(generic.ObjectListView):
  3398. queryset = PowerPanel.objects.annotate(
  3399. powerfeed_count=count_related(PowerFeed, 'power_panel')
  3400. )
  3401. filterset = filtersets.PowerPanelFilterSet
  3402. filterset_form = forms.PowerPanelFilterForm
  3403. table = tables.PowerPanelTable
  3404. @register_model_view(PowerPanel)
  3405. class PowerPanelView(GetRelatedModelsMixin, generic.ObjectView):
  3406. queryset = PowerPanel.objects.all()
  3407. def get_extra_context(self, request, instance):
  3408. return {
  3409. 'related_models': self.get_related_models(request, instance),
  3410. }
  3411. @register_model_view(PowerPanel, 'add', detail=False)
  3412. @register_model_view(PowerPanel, 'edit')
  3413. class PowerPanelEditView(generic.ObjectEditView):
  3414. queryset = PowerPanel.objects.all()
  3415. form = forms.PowerPanelForm
  3416. @register_model_view(PowerPanel, 'delete')
  3417. class PowerPanelDeleteView(generic.ObjectDeleteView):
  3418. queryset = PowerPanel.objects.all()
  3419. @register_model_view(PowerPanel, 'bulk_import', path='import', detail=False)
  3420. class PowerPanelBulkImportView(generic.BulkImportView):
  3421. queryset = PowerPanel.objects.all()
  3422. model_form = forms.PowerPanelImportForm
  3423. @register_model_view(PowerPanel, 'bulk_edit', path='edit', detail=False)
  3424. class PowerPanelBulkEditView(generic.BulkEditView):
  3425. queryset = PowerPanel.objects.all()
  3426. filterset = filtersets.PowerPanelFilterSet
  3427. table = tables.PowerPanelTable
  3428. form = forms.PowerPanelBulkEditForm
  3429. @register_model_view(PowerPanel, 'bulk_rename', path='rename', detail=False)
  3430. class PowerPanelBulkRenameView(generic.BulkRenameView):
  3431. queryset = PowerPanel.objects.all()
  3432. filterset = filtersets.PowerPanelFilterSet
  3433. @register_model_view(PowerPanel, 'bulk_delete', path='delete', detail=False)
  3434. class PowerPanelBulkDeleteView(generic.BulkDeleteView):
  3435. queryset = PowerPanel.objects.annotate(
  3436. powerfeed_count=count_related(PowerFeed, 'power_panel')
  3437. )
  3438. filterset = filtersets.PowerPanelFilterSet
  3439. table = tables.PowerPanelTable
  3440. #
  3441. # Power feeds
  3442. #
  3443. @register_model_view(PowerFeed, 'list', path='', detail=False)
  3444. class PowerFeedListView(generic.ObjectListView):
  3445. queryset = PowerFeed.objects.all()
  3446. filterset = filtersets.PowerFeedFilterSet
  3447. filterset_form = forms.PowerFeedFilterForm
  3448. table = tables.PowerFeedTable
  3449. @register_model_view(PowerFeed)
  3450. class PowerFeedView(generic.ObjectView):
  3451. queryset = PowerFeed.objects.all()
  3452. @register_model_view(PowerFeed, 'add', detail=False)
  3453. @register_model_view(PowerFeed, 'edit')
  3454. class PowerFeedEditView(generic.ObjectEditView):
  3455. queryset = PowerFeed.objects.all()
  3456. form = forms.PowerFeedForm
  3457. @register_model_view(PowerFeed, 'delete')
  3458. class PowerFeedDeleteView(generic.ObjectDeleteView):
  3459. queryset = PowerFeed.objects.all()
  3460. @register_model_view(PowerFeed, 'bulk_import', path='import', detail=False)
  3461. class PowerFeedBulkImportView(generic.BulkImportView):
  3462. queryset = PowerFeed.objects.all()
  3463. model_form = forms.PowerFeedImportForm
  3464. @register_model_view(PowerFeed, 'bulk_edit', path='edit', detail=False)
  3465. class PowerFeedBulkEditView(generic.BulkEditView):
  3466. queryset = PowerFeed.objects.all()
  3467. filterset = filtersets.PowerFeedFilterSet
  3468. table = tables.PowerFeedTable
  3469. form = forms.PowerFeedBulkEditForm
  3470. @register_model_view(PowerFeed, 'bulk_rename', path='rename', detail=False)
  3471. class PowerFeedBulkRenameView(generic.BulkRenameView):
  3472. queryset = PowerFeed.objects.all()
  3473. filterset = filtersets.PowerFeedFilterSet
  3474. @register_model_view(PowerFeed, 'bulk_disconnect', path='disconnect', detail=False)
  3475. class PowerFeedBulkDisconnectView(BulkDisconnectView):
  3476. queryset = PowerFeed.objects.all()
  3477. @register_model_view(PowerFeed, 'bulk_delete', path='delete', detail=False)
  3478. class PowerFeedBulkDeleteView(generic.BulkDeleteView):
  3479. queryset = PowerFeed.objects.all()
  3480. filterset = filtersets.PowerFeedFilterSet
  3481. table = tables.PowerFeedTable
  3482. # Trace view
  3483. register_model_view(PowerFeed, 'trace', kwargs={'model': PowerFeed})(PathTraceView)
  3484. #
  3485. # Virtual device contexts
  3486. #
  3487. @register_model_view(VirtualDeviceContext, 'list', path='', detail=False)
  3488. class VirtualDeviceContextListView(generic.ObjectListView):
  3489. queryset = VirtualDeviceContext.objects.annotate(
  3490. interface_count=count_related(Interface, 'vdcs'),
  3491. )
  3492. filterset = filtersets.VirtualDeviceContextFilterSet
  3493. filterset_form = forms.VirtualDeviceContextFilterForm
  3494. table = tables.VirtualDeviceContextTable
  3495. actions = (AddObject, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete)
  3496. @register_model_view(VirtualDeviceContext)
  3497. class VirtualDeviceContextView(GetRelatedModelsMixin, generic.ObjectView):
  3498. queryset = VirtualDeviceContext.objects.all()
  3499. def get_extra_context(self, request, instance):
  3500. return {
  3501. 'related_models': self.get_related_models(
  3502. request,
  3503. instance,
  3504. extra=(
  3505. (Interface.objects.restrict(request.user, 'view').filter(vdcs__in=[instance]), 'vdc_id'),
  3506. ),
  3507. ),
  3508. }
  3509. @register_model_view(VirtualDeviceContext, 'add', detail=False)
  3510. @register_model_view(VirtualDeviceContext, 'edit')
  3511. class VirtualDeviceContextEditView(generic.ObjectEditView):
  3512. queryset = VirtualDeviceContext.objects.all()
  3513. form = forms.VirtualDeviceContextForm
  3514. @register_model_view(VirtualDeviceContext, 'delete')
  3515. class VirtualDeviceContextDeleteView(generic.ObjectDeleteView):
  3516. queryset = VirtualDeviceContext.objects.all()
  3517. @register_model_view(VirtualDeviceContext, 'bulk_import', path='import', detail=False)
  3518. class VirtualDeviceContextBulkImportView(generic.BulkImportView):
  3519. queryset = VirtualDeviceContext.objects.all()
  3520. model_form = forms.VirtualDeviceContextImportForm
  3521. @register_model_view(VirtualDeviceContext, 'bulk_edit', path='edit', detail=False)
  3522. class VirtualDeviceContextBulkEditView(generic.BulkEditView):
  3523. queryset = VirtualDeviceContext.objects.all()
  3524. filterset = filtersets.VirtualDeviceContextFilterSet
  3525. table = tables.VirtualDeviceContextTable
  3526. form = forms.VirtualDeviceContextBulkEditForm
  3527. @register_model_view(VirtualDeviceContext, 'bulk_rename', path='rename', detail=False)
  3528. class VirtualDeviceContextBulkRenameView(generic.BulkRenameView):
  3529. queryset = VirtualDeviceContext.objects.all()
  3530. filterset = filtersets.VirtualDeviceContextFilterSet
  3531. @register_model_view(VirtualDeviceContext, 'bulk_delete', path='delete', detail=False)
  3532. class VirtualDeviceContextBulkDeleteView(generic.BulkDeleteView):
  3533. queryset = VirtualDeviceContext.objects.all()
  3534. filterset = filtersets.VirtualDeviceContextFilterSet
  3535. table = tables.VirtualDeviceContextTable
  3536. #
  3537. # MAC addresses
  3538. #
  3539. @register_model_view(MACAddress, 'list', path='', detail=False)
  3540. class MACAddressListView(generic.ObjectListView):
  3541. queryset = MACAddress.objects.all()
  3542. filterset = filtersets.MACAddressFilterSet
  3543. filterset_form = forms.MACAddressFilterForm
  3544. table = tables.MACAddressTable
  3545. actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete)
  3546. @register_model_view(MACAddress)
  3547. class MACAddressView(generic.ObjectView):
  3548. queryset = MACAddress.objects.all()
  3549. @register_model_view(MACAddress, 'add', detail=False)
  3550. @register_model_view(MACAddress, 'edit')
  3551. class MACAddressEditView(generic.ObjectEditView):
  3552. queryset = MACAddress.objects.all()
  3553. form = forms.MACAddressForm
  3554. @register_model_view(MACAddress, 'delete')
  3555. class MACAddressDeleteView(generic.ObjectDeleteView):
  3556. queryset = MACAddress.objects.all()
  3557. @register_model_view(MACAddress, 'bulk_import', path='import', detail=False)
  3558. class MACAddressBulkImportView(generic.BulkImportView):
  3559. queryset = MACAddress.objects.all()
  3560. model_form = forms.MACAddressImportForm
  3561. @register_model_view(MACAddress, 'bulk_edit', path='edit', detail=False)
  3562. class MACAddressBulkEditView(generic.BulkEditView):
  3563. queryset = MACAddress.objects.all()
  3564. filterset = filtersets.MACAddressFilterSet
  3565. table = tables.MACAddressTable
  3566. form = forms.MACAddressBulkEditForm
  3567. @register_model_view(MACAddress, 'bulk_delete', path='delete', detail=False)
  3568. class MACAddressBulkDeleteView(generic.BulkDeleteView):
  3569. queryset = MACAddress.objects.all()
  3570. filterset = filtersets.MACAddressFilterSet
  3571. table = tables.MACAddressTable