views.py 153 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531
  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.Region', 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. def get_extra_context(self, request, instance):
  1955. return {
  1956. 'related_models': self.get_related_models(request, instance),
  1957. }
  1958. @register_model_view(Platform, 'add', detail=False)
  1959. @register_model_view(Platform, 'edit')
  1960. class PlatformEditView(generic.ObjectEditView):
  1961. queryset = Platform.objects.all()
  1962. form = forms.PlatformForm
  1963. @register_model_view(Platform, 'delete')
  1964. class PlatformDeleteView(generic.ObjectDeleteView):
  1965. queryset = Platform.objects.all()
  1966. @register_model_view(Platform, 'bulk_import', path='import', detail=False)
  1967. class PlatformBulkImportView(generic.BulkImportView):
  1968. queryset = Platform.objects.all()
  1969. model_form = forms.PlatformImportForm
  1970. @register_model_view(Platform, 'bulk_edit', path='edit', detail=False)
  1971. class PlatformBulkEditView(generic.BulkEditView):
  1972. queryset = Platform.objects.all()
  1973. filterset = filtersets.PlatformFilterSet
  1974. table = tables.PlatformTable
  1975. form = forms.PlatformBulkEditForm
  1976. @register_model_view(Platform, 'bulk_rename', path='rename', detail=False)
  1977. class PlatformBulkRenameView(generic.BulkRenameView):
  1978. queryset = Platform.objects.all()
  1979. filterset = filtersets.PlatformFilterSet
  1980. @register_model_view(Platform, 'bulk_delete', path='delete', detail=False)
  1981. class PlatformBulkDeleteView(generic.BulkDeleteView):
  1982. queryset = Platform.objects.all()
  1983. filterset = filtersets.PlatformFilterSet
  1984. table = tables.PlatformTable
  1985. #
  1986. # Devices
  1987. #
  1988. @register_model_view(Device, 'list', path='', detail=False)
  1989. class DeviceListView(generic.ObjectListView):
  1990. queryset = Device.objects.select_related('virtual_chassis')
  1991. filterset = filtersets.DeviceFilterSet
  1992. filterset_form = forms.DeviceFilterForm
  1993. table = tables.DeviceTable
  1994. actions = (AddObject, BulkImport, BulkExport, BulkAddComponents, BulkEdit, BulkRename, BulkDelete)
  1995. @register_model_view(Device)
  1996. class DeviceView(generic.ObjectView):
  1997. queryset = Device.objects.all()
  1998. layout = layout.SimpleLayout(
  1999. left_panels=[
  2000. panels.DevicePanel(),
  2001. panels.VirtualChassisMembersPanel(),
  2002. CustomFieldsPanel(),
  2003. TagsPanel(),
  2004. CommentsPanel(),
  2005. ObjectsTablePanel(
  2006. model='dcim.VirtualDeviceContext',
  2007. filters={'device_id': lambda ctx: ctx['object'].pk},
  2008. actions=[
  2009. actions.AddObject('dcim.VirtualDeviceContext', url_params={'device': lambda ctx: ctx['object'].pk}),
  2010. ],
  2011. ),
  2012. ],
  2013. right_panels=[
  2014. panels.DeviceManagementPanel(),
  2015. panels.PowerUtilizationPanel(),
  2016. ObjectsTablePanel(
  2017. model='ipam.Service',
  2018. title=_('Application Services'),
  2019. filters={'device_id': lambda ctx: ctx['object'].pk},
  2020. actions=[
  2021. actions.AddObject(
  2022. 'ipam.Service',
  2023. url_params={
  2024. 'parent_object_type': lambda ctx: ContentType.objects.get_for_model(ctx['object']).pk,
  2025. 'parent': lambda ctx: ctx['object'].pk
  2026. }
  2027. ),
  2028. ],
  2029. ),
  2030. ImageAttachmentsPanel(),
  2031. panels.DeviceDeviceTypePanel(),
  2032. panels.DeviceDimensionsPanel(),
  2033. TemplatePanel('dcim/panels/device_rack_elevations.html'),
  2034. ],
  2035. )
  2036. def get_extra_context(self, request, instance):
  2037. # VirtualChassis members
  2038. if instance.virtual_chassis is not None:
  2039. vc_members = Device.objects.restrict(request.user, 'view').filter(
  2040. virtual_chassis=instance.virtual_chassis
  2041. ).order_by('vc_position')
  2042. else:
  2043. vc_members = []
  2044. return {
  2045. 'vc_members': vc_members,
  2046. 'svg_extra': f'highlight=id:{instance.pk}',
  2047. }
  2048. @register_model_view(Device, 'add', detail=False)
  2049. @register_model_view(Device, 'edit')
  2050. class DeviceEditView(generic.ObjectEditView):
  2051. queryset = Device.objects.all()
  2052. form = forms.DeviceForm
  2053. template_name = 'dcim/device_edit.html'
  2054. @register_model_view(Device, 'delete')
  2055. class DeviceDeleteView(generic.ObjectDeleteView):
  2056. queryset = Device.objects.all()
  2057. @register_model_view(Device, 'consoleports', path='console-ports')
  2058. class DeviceConsolePortsView(DeviceComponentsView):
  2059. child_model = ConsolePort
  2060. table = tables.DeviceConsolePortTable
  2061. filterset = filtersets.ConsolePortFilterSet
  2062. filterset_form = forms.ConsolePortFilterForm
  2063. actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete)
  2064. tab = ViewTab(
  2065. label=_('Console Ports'),
  2066. badge=lambda obj: obj.console_port_count,
  2067. permission='dcim.view_consoleport',
  2068. weight=550,
  2069. hide_if_empty=True
  2070. )
  2071. @register_model_view(Device, 'consoleserverports', path='console-server-ports')
  2072. class DeviceConsoleServerPortsView(DeviceComponentsView):
  2073. child_model = ConsoleServerPort
  2074. table = tables.DeviceConsoleServerPortTable
  2075. filterset = filtersets.ConsoleServerPortFilterSet
  2076. filterset_form = forms.ConsoleServerPortFilterForm
  2077. actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete)
  2078. tab = ViewTab(
  2079. label=_('Console Server Ports'),
  2080. badge=lambda obj: obj.console_server_port_count,
  2081. permission='dcim.view_consoleserverport',
  2082. weight=560,
  2083. hide_if_empty=True
  2084. )
  2085. @register_model_view(Device, 'powerports', path='power-ports')
  2086. class DevicePowerPortsView(DeviceComponentsView):
  2087. child_model = PowerPort
  2088. table = tables.DevicePowerPortTable
  2089. filterset = filtersets.PowerPortFilterSet
  2090. filterset_form = forms.PowerPortFilterForm
  2091. actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete)
  2092. tab = ViewTab(
  2093. label=_('Power Ports'),
  2094. badge=lambda obj: obj.power_port_count,
  2095. permission='dcim.view_powerport',
  2096. weight=570,
  2097. hide_if_empty=True
  2098. )
  2099. @register_model_view(Device, 'poweroutlets', path='power-outlets')
  2100. class DevicePowerOutletsView(DeviceComponentsView):
  2101. child_model = PowerOutlet
  2102. table = tables.DevicePowerOutletTable
  2103. filterset = filtersets.PowerOutletFilterSet
  2104. filterset_form = forms.PowerOutletFilterForm
  2105. actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete)
  2106. tab = ViewTab(
  2107. label=_('Power Outlets'),
  2108. badge=lambda obj: obj.power_outlet_count,
  2109. permission='dcim.view_poweroutlet',
  2110. weight=580,
  2111. hide_if_empty=True
  2112. )
  2113. @register_model_view(Device, 'interfaces')
  2114. class DeviceInterfacesView(DeviceComponentsView):
  2115. child_model = Interface
  2116. table = tables.DeviceInterfaceTable
  2117. filterset = filtersets.InterfaceFilterSet
  2118. filterset_form = forms.InterfaceFilterForm
  2119. actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete)
  2120. template_name = 'dcim/device/interfaces.html'
  2121. tab = ViewTab(
  2122. label=_('Interfaces'),
  2123. badge=lambda obj: obj.vc_interfaces().count(),
  2124. permission='dcim.view_interface',
  2125. weight=520,
  2126. hide_if_empty=True
  2127. )
  2128. def get_children(self, request, parent):
  2129. return parent.vc_interfaces().restrict(request.user, 'view').prefetch_related(
  2130. Prefetch('ip_addresses', queryset=IPAddress.objects.restrict(request.user)),
  2131. Prefetch('member_interfaces', queryset=Interface.objects.restrict(request.user))
  2132. )
  2133. @register_model_view(Device, 'frontports', path='front-ports')
  2134. class DeviceFrontPortsView(DeviceComponentsView):
  2135. child_model = FrontPort
  2136. table = tables.DeviceFrontPortTable
  2137. filterset = filtersets.FrontPortFilterSet
  2138. filterset_form = forms.FrontPortFilterForm
  2139. actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete)
  2140. tab = ViewTab(
  2141. label=_('Front Ports'),
  2142. badge=lambda obj: obj.front_port_count,
  2143. permission='dcim.view_frontport',
  2144. weight=530,
  2145. hide_if_empty=True
  2146. )
  2147. @register_model_view(Device, 'rearports', path='rear-ports')
  2148. class DeviceRearPortsView(DeviceComponentsView):
  2149. child_model = RearPort
  2150. table = tables.DeviceRearPortTable
  2151. filterset = filtersets.RearPortFilterSet
  2152. filterset_form = forms.RearPortFilterForm
  2153. actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete)
  2154. tab = ViewTab(
  2155. label=_('Rear Ports'),
  2156. badge=lambda obj: obj.rear_port_count,
  2157. permission='dcim.view_rearport',
  2158. weight=540,
  2159. hide_if_empty=True
  2160. )
  2161. @register_model_view(Device, 'modulebays', path='module-bays')
  2162. class DeviceModuleBaysView(DeviceComponentsView):
  2163. child_model = ModuleBay
  2164. table = tables.DeviceModuleBayTable
  2165. filterset = filtersets.ModuleBayFilterSet
  2166. filterset_form = forms.ModuleBayFilterForm
  2167. actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete)
  2168. tab = ViewTab(
  2169. label=_('Module Bays'),
  2170. badge=lambda obj: obj.module_bay_count,
  2171. permission='dcim.view_modulebay',
  2172. weight=510,
  2173. hide_if_empty=True
  2174. )
  2175. @register_model_view(Device, 'devicebays', path='device-bays')
  2176. class DeviceDeviceBaysView(DeviceComponentsView):
  2177. child_model = DeviceBay
  2178. table = tables.DeviceDeviceBayTable
  2179. filterset = filtersets.DeviceBayFilterSet
  2180. filterset_form = forms.DeviceBayFilterForm
  2181. actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete)
  2182. tab = ViewTab(
  2183. label=_('Device Bays'),
  2184. badge=lambda obj: obj.device_bay_count,
  2185. permission='dcim.view_devicebay',
  2186. weight=500,
  2187. hide_if_empty=True
  2188. )
  2189. @register_model_view(Device, 'inventory')
  2190. class DeviceInventoryView(DeviceComponentsView):
  2191. child_model = InventoryItem
  2192. table = tables.DeviceInventoryItemTable
  2193. filterset = filtersets.InventoryItemFilterSet
  2194. filterset_form = forms.InventoryItemFilterForm
  2195. actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete)
  2196. tab = ViewTab(
  2197. label=_('Inventory Items'),
  2198. badge=lambda obj: obj.inventory_item_count,
  2199. permission='dcim.view_inventoryitem',
  2200. weight=590,
  2201. hide_if_empty=True
  2202. )
  2203. @register_model_view(Device, 'configcontext', path='config-context')
  2204. class DeviceConfigContextView(ObjectConfigContextView):
  2205. queryset = Device.objects.annotate_config_context_data()
  2206. base_template = 'dcim/device/base.html'
  2207. tab = ViewTab(
  2208. label=_('Config Context'),
  2209. weight=2000
  2210. )
  2211. @register_model_view(Device, 'render-config')
  2212. class DeviceRenderConfigView(ObjectRenderConfigView):
  2213. queryset = Device.objects.all()
  2214. base_template = 'dcim/device/base.html'
  2215. additional_permissions = ['dcim.render_config_device']
  2216. tab = ViewTab(
  2217. label=_('Render Config'),
  2218. weight=2100,
  2219. )
  2220. @register_model_view(Device, 'virtual-machines')
  2221. class DeviceVirtualMachinesView(generic.ObjectChildrenView):
  2222. queryset = Device.objects.all()
  2223. child_model = VirtualMachine
  2224. table = VirtualMachineTable
  2225. filterset = VirtualMachineFilterSet
  2226. filterset_form = VirtualMachineFilterForm
  2227. tab = ViewTab(
  2228. label=_('Virtual Machines'),
  2229. badge=lambda obj: VirtualMachine.objects.filter(cluster=obj.cluster, device=obj).count(),
  2230. weight=2200,
  2231. hide_if_empty=True,
  2232. permission='virtualization.view_virtualmachine'
  2233. )
  2234. def get_children(self, request, parent):
  2235. return self.child_model.objects.restrict(request.user, 'view').filter(cluster=parent.cluster, device=parent)
  2236. @register_model_view(Device, 'bulk_import', path='import', detail=False)
  2237. class DeviceBulkImportView(generic.BulkImportView):
  2238. queryset = Device.objects.all()
  2239. model_form = forms.DeviceImportForm
  2240. def save_object(self, object_form, request):
  2241. parent_bay = getattr(object_form.instance, 'parent_bay', None)
  2242. obj = object_form.save()
  2243. # For child devices, save the reverse relation to the parent device bay
  2244. if parent_bay:
  2245. device_bay = parent_bay
  2246. device_bay.snapshot()
  2247. device_bay.installed_device = obj
  2248. device_bay.save()
  2249. return obj
  2250. @register_model_view(Device, 'bulk_edit', path='edit', detail=False)
  2251. class DeviceBulkEditView(generic.BulkEditView):
  2252. queryset = Device.objects.prefetch_related('device_type__manufacturer')
  2253. filterset = filtersets.DeviceFilterSet
  2254. table = tables.DeviceTable
  2255. form = forms.DeviceBulkEditForm
  2256. @register_model_view(Device, 'bulk_rename', path='rename', detail=False)
  2257. class DeviceBulkRenameView(generic.BulkRenameView):
  2258. queryset = Device.objects.all()
  2259. filterset = filtersets.DeviceFilterSet
  2260. table = tables.DeviceTable
  2261. @register_model_view(Device, 'bulk_delete', path='delete', detail=False)
  2262. class DeviceBulkDeleteView(generic.BulkDeleteView):
  2263. queryset = Device.objects.prefetch_related('device_type__manufacturer')
  2264. filterset = filtersets.DeviceFilterSet
  2265. table = tables.DeviceTable
  2266. #
  2267. # Modules
  2268. #
  2269. @register_model_view(Module, 'list', path='', detail=False)
  2270. class ModuleListView(generic.ObjectListView):
  2271. queryset = Module.objects.prefetch_related('module_type__manufacturer')
  2272. filterset = filtersets.ModuleFilterSet
  2273. filterset_form = forms.ModuleFilterForm
  2274. table = tables.ModuleTable
  2275. actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete)
  2276. @register_model_view(Module)
  2277. class ModuleView(GetRelatedModelsMixin, generic.ObjectView):
  2278. queryset = Module.objects.all()
  2279. def get_extra_context(self, request, instance):
  2280. return {
  2281. 'related_models': self.get_related_models(request, instance),
  2282. }
  2283. @register_model_view(Module, 'add', detail=False)
  2284. @register_model_view(Module, 'edit')
  2285. class ModuleEditView(generic.ObjectEditView):
  2286. queryset = Module.objects.all()
  2287. form = forms.ModuleForm
  2288. @register_model_view(Module, 'delete')
  2289. class ModuleDeleteView(generic.ObjectDeleteView):
  2290. queryset = Module.objects.all()
  2291. @register_model_view(Module, 'bulk_import', path='import', detail=False)
  2292. class ModuleBulkImportView(generic.BulkImportView):
  2293. queryset = Module.objects.all()
  2294. model_form = forms.ModuleImportForm
  2295. @register_model_view(Module, 'bulk_edit', path='edit', detail=False)
  2296. class ModuleBulkEditView(generic.BulkEditView):
  2297. queryset = Module.objects.prefetch_related('module_type__manufacturer')
  2298. filterset = filtersets.ModuleFilterSet
  2299. table = tables.ModuleTable
  2300. form = forms.ModuleBulkEditForm
  2301. @register_model_view(Module, 'bulk_delete', path='delete', detail=False)
  2302. class ModuleBulkDeleteView(generic.BulkDeleteView):
  2303. queryset = Module.objects.prefetch_related('module_type__manufacturer')
  2304. filterset = filtersets.ModuleFilterSet
  2305. table = tables.ModuleTable
  2306. #
  2307. # Console ports
  2308. #
  2309. @register_model_view(ConsolePort, 'list', path='', detail=False)
  2310. class ConsolePortListView(generic.ObjectListView):
  2311. queryset = ConsolePort.objects.all()
  2312. filterset = filtersets.ConsolePortFilterSet
  2313. filterset_form = forms.ConsolePortFilterForm
  2314. table = tables.ConsolePortTable
  2315. @register_model_view(ConsolePort)
  2316. class ConsolePortView(generic.ObjectView):
  2317. queryset = ConsolePort.objects.all()
  2318. @register_model_view(ConsolePort, 'add', detail=False)
  2319. class ConsolePortCreateView(generic.ComponentCreateView):
  2320. queryset = ConsolePort.objects.all()
  2321. form = forms.ConsolePortCreateForm
  2322. model_form = forms.ConsolePortForm
  2323. @register_model_view(ConsolePort, 'edit')
  2324. class ConsolePortEditView(generic.ObjectEditView):
  2325. queryset = ConsolePort.objects.all()
  2326. form = forms.ConsolePortForm
  2327. @register_model_view(ConsolePort, 'delete')
  2328. class ConsolePortDeleteView(generic.ObjectDeleteView):
  2329. queryset = ConsolePort.objects.all()
  2330. @register_model_view(ConsolePort, 'bulk_import', path='import', detail=False)
  2331. class ConsolePortBulkImportView(generic.BulkImportView):
  2332. queryset = ConsolePort.objects.all()
  2333. model_form = forms.ConsolePortImportForm
  2334. @register_model_view(ConsolePort, 'bulk_edit', path='edit', detail=False)
  2335. class ConsolePortBulkEditView(generic.BulkEditView):
  2336. queryset = ConsolePort.objects.all()
  2337. filterset = filtersets.ConsolePortFilterSet
  2338. table = tables.ConsolePortTable
  2339. form = forms.ConsolePortBulkEditForm
  2340. @register_model_view(ConsolePort, 'bulk_rename', path='rename', detail=False)
  2341. class ConsolePortBulkRenameView(generic.BulkRenameView):
  2342. queryset = ConsolePort.objects.all()
  2343. filterset = filtersets.ConsolePortFilterSet
  2344. @register_model_view(ConsolePort, 'bulk_disconnect', path='disconnect', detail=False)
  2345. class ConsolePortBulkDisconnectView(BulkDisconnectView):
  2346. queryset = ConsolePort.objects.all()
  2347. @register_model_view(ConsolePort, 'bulk_delete', path='delete', detail=False)
  2348. class ConsolePortBulkDeleteView(generic.BulkDeleteView):
  2349. queryset = ConsolePort.objects.all()
  2350. filterset = filtersets.ConsolePortFilterSet
  2351. table = tables.ConsolePortTable
  2352. # Trace view
  2353. register_model_view(ConsolePort, 'trace', kwargs={'model': ConsolePort})(PathTraceView)
  2354. #
  2355. # Console server ports
  2356. #
  2357. @register_model_view(ConsoleServerPort, 'list', path='', detail=False)
  2358. class ConsoleServerPortListView(generic.ObjectListView):
  2359. queryset = ConsoleServerPort.objects.all()
  2360. filterset = filtersets.ConsoleServerPortFilterSet
  2361. filterset_form = forms.ConsoleServerPortFilterForm
  2362. table = tables.ConsoleServerPortTable
  2363. @register_model_view(ConsoleServerPort)
  2364. class ConsoleServerPortView(generic.ObjectView):
  2365. queryset = ConsoleServerPort.objects.all()
  2366. @register_model_view(ConsoleServerPort, 'add', detail=False)
  2367. class ConsoleServerPortCreateView(generic.ComponentCreateView):
  2368. queryset = ConsoleServerPort.objects.all()
  2369. form = forms.ConsoleServerPortCreateForm
  2370. model_form = forms.ConsoleServerPortForm
  2371. @register_model_view(ConsoleServerPort, 'edit')
  2372. class ConsoleServerPortEditView(generic.ObjectEditView):
  2373. queryset = ConsoleServerPort.objects.all()
  2374. form = forms.ConsoleServerPortForm
  2375. @register_model_view(ConsoleServerPort, 'delete')
  2376. class ConsoleServerPortDeleteView(generic.ObjectDeleteView):
  2377. queryset = ConsoleServerPort.objects.all()
  2378. @register_model_view(ConsoleServerPort, 'bulk_import', path='import', detail=False)
  2379. class ConsoleServerPortBulkImportView(generic.BulkImportView):
  2380. queryset = ConsoleServerPort.objects.all()
  2381. model_form = forms.ConsoleServerPortImportForm
  2382. @register_model_view(ConsoleServerPort, 'bulk_edit', path='edit', detail=False)
  2383. class ConsoleServerPortBulkEditView(generic.BulkEditView):
  2384. queryset = ConsoleServerPort.objects.all()
  2385. filterset = filtersets.ConsoleServerPortFilterSet
  2386. table = tables.ConsoleServerPortTable
  2387. form = forms.ConsoleServerPortBulkEditForm
  2388. @register_model_view(ConsoleServerPort, 'bulk_rename', path='rename', detail=False)
  2389. class ConsoleServerPortBulkRenameView(generic.BulkRenameView):
  2390. queryset = ConsoleServerPort.objects.all()
  2391. filterset = filtersets.ConsoleServerPortFilterSet
  2392. @register_model_view(ConsoleServerPort, 'bulk_disconnect', path='disconnect', detail=False)
  2393. class ConsoleServerPortBulkDisconnectView(BulkDisconnectView):
  2394. queryset = ConsoleServerPort.objects.all()
  2395. @register_model_view(ConsoleServerPort, 'bulk_delete', path='delete', detail=False)
  2396. class ConsoleServerPortBulkDeleteView(generic.BulkDeleteView):
  2397. queryset = ConsoleServerPort.objects.all()
  2398. filterset = filtersets.ConsoleServerPortFilterSet
  2399. table = tables.ConsoleServerPortTable
  2400. # Trace view
  2401. register_model_view(ConsoleServerPort, 'trace', kwargs={'model': ConsoleServerPort})(PathTraceView)
  2402. #
  2403. # Power ports
  2404. #
  2405. @register_model_view(PowerPort, 'list', path='', detail=False)
  2406. class PowerPortListView(generic.ObjectListView):
  2407. queryset = PowerPort.objects.all()
  2408. filterset = filtersets.PowerPortFilterSet
  2409. filterset_form = forms.PowerPortFilterForm
  2410. table = tables.PowerPortTable
  2411. @register_model_view(PowerPort)
  2412. class PowerPortView(generic.ObjectView):
  2413. queryset = PowerPort.objects.all()
  2414. @register_model_view(PowerPort, 'add', detail=False)
  2415. class PowerPortCreateView(generic.ComponentCreateView):
  2416. queryset = PowerPort.objects.all()
  2417. form = forms.PowerPortCreateForm
  2418. model_form = forms.PowerPortForm
  2419. @register_model_view(PowerPort, 'edit')
  2420. class PowerPortEditView(generic.ObjectEditView):
  2421. queryset = PowerPort.objects.all()
  2422. form = forms.PowerPortForm
  2423. @register_model_view(PowerPort, 'delete')
  2424. class PowerPortDeleteView(generic.ObjectDeleteView):
  2425. queryset = PowerPort.objects.all()
  2426. @register_model_view(PowerPort, 'bulk_import', path='import', detail=False)
  2427. class PowerPortBulkImportView(generic.BulkImportView):
  2428. queryset = PowerPort.objects.all()
  2429. model_form = forms.PowerPortImportForm
  2430. @register_model_view(PowerPort, 'bulk_edit', path='edit', detail=False)
  2431. class PowerPortBulkEditView(generic.BulkEditView):
  2432. queryset = PowerPort.objects.all()
  2433. filterset = filtersets.PowerPortFilterSet
  2434. table = tables.PowerPortTable
  2435. form = forms.PowerPortBulkEditForm
  2436. @register_model_view(PowerPort, 'bulk_rename', path='rename', detail=False)
  2437. class PowerPortBulkRenameView(generic.BulkRenameView):
  2438. queryset = PowerPort.objects.all()
  2439. filterset = filtersets.PowerPortFilterSet
  2440. @register_model_view(PowerPort, 'bulk_disconnect', path='disconnect', detail=False)
  2441. class PowerPortBulkDisconnectView(BulkDisconnectView):
  2442. queryset = PowerPort.objects.all()
  2443. @register_model_view(PowerPort, 'bulk_delete', path='delete', detail=False)
  2444. class PowerPortBulkDeleteView(generic.BulkDeleteView):
  2445. queryset = PowerPort.objects.all()
  2446. filterset = filtersets.PowerPortFilterSet
  2447. table = tables.PowerPortTable
  2448. # Trace view
  2449. register_model_view(PowerPort, 'trace', kwargs={'model': PowerPort})(PathTraceView)
  2450. #
  2451. # Power outlets
  2452. #
  2453. @register_model_view(PowerOutlet, 'list', path='', detail=False)
  2454. class PowerOutletListView(generic.ObjectListView):
  2455. queryset = PowerOutlet.objects.all()
  2456. filterset = filtersets.PowerOutletFilterSet
  2457. filterset_form = forms.PowerOutletFilterForm
  2458. table = tables.PowerOutletTable
  2459. @register_model_view(PowerOutlet)
  2460. class PowerOutletView(generic.ObjectView):
  2461. queryset = PowerOutlet.objects.all()
  2462. @register_model_view(PowerOutlet, 'add', detail=False)
  2463. class PowerOutletCreateView(generic.ComponentCreateView):
  2464. queryset = PowerOutlet.objects.all()
  2465. form = forms.PowerOutletCreateForm
  2466. model_form = forms.PowerOutletForm
  2467. @register_model_view(PowerOutlet, 'edit')
  2468. class PowerOutletEditView(generic.ObjectEditView):
  2469. queryset = PowerOutlet.objects.all()
  2470. form = forms.PowerOutletForm
  2471. @register_model_view(PowerOutlet, 'delete')
  2472. class PowerOutletDeleteView(generic.ObjectDeleteView):
  2473. queryset = PowerOutlet.objects.all()
  2474. @register_model_view(PowerOutlet, 'bulk_import', path='import', detail=False)
  2475. class PowerOutletBulkImportView(generic.BulkImportView):
  2476. queryset = PowerOutlet.objects.all()
  2477. model_form = forms.PowerOutletImportForm
  2478. @register_model_view(PowerOutlet, 'bulk_edit', path='edit', detail=False)
  2479. class PowerOutletBulkEditView(generic.BulkEditView):
  2480. queryset = PowerOutlet.objects.all()
  2481. filterset = filtersets.PowerOutletFilterSet
  2482. table = tables.PowerOutletTable
  2483. form = forms.PowerOutletBulkEditForm
  2484. @register_model_view(PowerOutlet, 'bulk_rename', path='rename', detail=False)
  2485. class PowerOutletBulkRenameView(generic.BulkRenameView):
  2486. queryset = PowerOutlet.objects.all()
  2487. filterset = filtersets.PowerOutletFilterSet
  2488. @register_model_view(PowerOutlet, 'bulk_disconnect', path='disconnect', detail=False)
  2489. class PowerOutletBulkDisconnectView(BulkDisconnectView):
  2490. queryset = PowerOutlet.objects.all()
  2491. @register_model_view(PowerOutlet, 'bulk_delete', path='delete', detail=False)
  2492. class PowerOutletBulkDeleteView(generic.BulkDeleteView):
  2493. queryset = PowerOutlet.objects.all()
  2494. filterset = filtersets.PowerOutletFilterSet
  2495. table = tables.PowerOutletTable
  2496. # Trace view
  2497. register_model_view(PowerOutlet, 'trace', kwargs={'model': PowerOutlet})(PathTraceView)
  2498. #
  2499. # Interfaces
  2500. #
  2501. @register_model_view(Interface, 'list', path='', detail=False)
  2502. class InterfaceListView(generic.ObjectListView):
  2503. queryset = Interface.objects.all()
  2504. filterset = filtersets.InterfaceFilterSet
  2505. filterset_form = forms.InterfaceFilterForm
  2506. table = tables.InterfaceTable
  2507. @register_model_view(Interface)
  2508. class InterfaceView(generic.ObjectView):
  2509. queryset = Interface.objects.all()
  2510. def get_extra_context(self, request, instance):
  2511. # Get assigned VDCs
  2512. vdc_table = tables.VirtualDeviceContextTable(
  2513. data=instance.vdcs.restrict(request.user, 'view').prefetch_related('device'),
  2514. exclude=(
  2515. 'tenant', 'tenant_group', 'primary_ip', 'primary_ip4', 'primary_ip6', 'oob_ip', 'comments', 'tags',
  2516. 'created', 'last_updated', 'actions',
  2517. ),
  2518. orderable=False
  2519. )
  2520. vdc_table.configure(request)
  2521. # Get bridge interfaces
  2522. bridge_interfaces = Interface.objects.restrict(request.user, 'view').filter(bridge=instance)
  2523. bridge_interfaces_table = tables.InterfaceTable(
  2524. bridge_interfaces,
  2525. exclude=('device', 'parent'),
  2526. orderable=False
  2527. )
  2528. bridge_interfaces_table.configure(request)
  2529. # Get child interfaces
  2530. child_interfaces = Interface.objects.restrict(request.user, 'view').filter(parent=instance)
  2531. child_interfaces_table = tables.InterfaceTable(
  2532. child_interfaces,
  2533. exclude=('device', 'parent'),
  2534. orderable=False
  2535. )
  2536. child_interfaces_table.configure(request)
  2537. # Get LAG interfaces
  2538. lag_interfaces = Interface.objects.restrict(request.user, 'view').filter(lag=instance)
  2539. lag_interfaces_table = tables.InterfaceLAGMemberTable(
  2540. lag_interfaces,
  2541. orderable=False
  2542. )
  2543. lag_interfaces_table.configure(request)
  2544. # Get assigned VLANs and annotate whether each is tagged or untagged
  2545. vlans = []
  2546. if instance.untagged_vlan is not None:
  2547. vlans.append(instance.untagged_vlan)
  2548. vlans[0].tagged = False
  2549. for vlan in instance.tagged_vlans.restrict(request.user).prefetch_related('site', 'group', 'tenant', 'role'):
  2550. vlan.tagged = True
  2551. vlans.append(vlan)
  2552. vlan_table = InterfaceVLANTable(
  2553. interface=instance,
  2554. data=vlans,
  2555. orderable=False
  2556. )
  2557. vlan_table.configure(request)
  2558. # Get VLAN translation rules
  2559. vlan_translation_table = None
  2560. if instance.vlan_translation_policy:
  2561. vlan_translation_table = VLANTranslationRuleTable(
  2562. data=instance.vlan_translation_policy.rules.all(),
  2563. orderable=False
  2564. )
  2565. vlan_translation_table.configure(request)
  2566. return {
  2567. 'vdc_table': vdc_table,
  2568. 'bridge_interfaces': bridge_interfaces,
  2569. 'bridge_interfaces_table': bridge_interfaces_table,
  2570. 'child_interfaces_table': child_interfaces_table,
  2571. 'lag_interfaces_table': lag_interfaces_table,
  2572. 'vlan_table': vlan_table,
  2573. 'vlan_translation_table': vlan_translation_table,
  2574. }
  2575. @register_model_view(Interface, 'add', detail=False)
  2576. class InterfaceCreateView(generic.ComponentCreateView):
  2577. queryset = Interface.objects.all()
  2578. form = forms.InterfaceCreateForm
  2579. model_form = forms.InterfaceForm
  2580. @register_model_view(Interface, 'edit')
  2581. class InterfaceEditView(generic.ObjectEditView):
  2582. queryset = Interface.objects.all()
  2583. form = forms.InterfaceForm
  2584. @register_model_view(Interface, 'delete')
  2585. class InterfaceDeleteView(generic.ObjectDeleteView):
  2586. queryset = Interface.objects.all()
  2587. @register_model_view(Interface, 'bulk_import', path='import', detail=False)
  2588. class InterfaceBulkImportView(generic.BulkImportView):
  2589. queryset = Interface.objects.all()
  2590. model_form = forms.InterfaceImportForm
  2591. @register_model_view(Interface, 'bulk_edit', path='edit', detail=False)
  2592. class InterfaceBulkEditView(generic.BulkEditView):
  2593. queryset = Interface.objects.all()
  2594. filterset = filtersets.InterfaceFilterSet
  2595. table = tables.InterfaceTable
  2596. form = forms.InterfaceBulkEditForm
  2597. def post_save_operations(self, form, obj):
  2598. super().post_save_operations(form, obj)
  2599. # Add/remove tagged VLANs
  2600. if obj.mode == InterfaceModeChoices.MODE_TAGGED:
  2601. if form.cleaned_data.get('add_tagged_vlans', None):
  2602. obj.tagged_vlans.add(*form.cleaned_data['add_tagged_vlans'])
  2603. if form.cleaned_data.get('remove_tagged_vlans', None):
  2604. obj.tagged_vlans.remove(*form.cleaned_data['remove_tagged_vlans'])
  2605. @register_model_view(Interface, 'bulk_rename', path='rename', detail=False)
  2606. class InterfaceBulkRenameView(generic.BulkRenameView):
  2607. queryset = Interface.objects.all()
  2608. filterset = filtersets.InterfaceFilterSet
  2609. @register_model_view(Interface, 'bulk_disconnect', path='disconnect', detail=False)
  2610. class InterfaceBulkDisconnectView(BulkDisconnectView):
  2611. queryset = Interface.objects.all()
  2612. @register_model_view(Interface, 'bulk_delete', path='delete', detail=False)
  2613. class InterfaceBulkDeleteView(generic.BulkDeleteView):
  2614. # Ensure child interfaces are deleted prior to their parents
  2615. queryset = Interface.objects.order_by('device', 'parent', CollateAsChar('_name'))
  2616. filterset = filtersets.InterfaceFilterSet
  2617. table = tables.InterfaceTable
  2618. # Trace view
  2619. register_model_view(Interface, 'trace', kwargs={'model': Interface})(PathTraceView)
  2620. #
  2621. # Front ports
  2622. #
  2623. @register_model_view(FrontPort, 'list', path='', detail=False)
  2624. class FrontPortListView(generic.ObjectListView):
  2625. queryset = FrontPort.objects.all()
  2626. filterset = filtersets.FrontPortFilterSet
  2627. filterset_form = forms.FrontPortFilterForm
  2628. table = tables.FrontPortTable
  2629. @register_model_view(FrontPort)
  2630. class FrontPortView(generic.ObjectView):
  2631. queryset = FrontPort.objects.all()
  2632. def get_extra_context(self, request, instance):
  2633. return {
  2634. 'rear_port_mappings': PortMapping.objects.filter(front_port=instance).prefetch_related('rear_port'),
  2635. }
  2636. @register_model_view(FrontPort, 'add', detail=False)
  2637. class FrontPortCreateView(generic.ComponentCreateView):
  2638. queryset = FrontPort.objects.all()
  2639. form = forms.FrontPortCreateForm
  2640. model_form = forms.FrontPortForm
  2641. @register_model_view(FrontPort, 'edit')
  2642. class FrontPortEditView(generic.ObjectEditView):
  2643. queryset = FrontPort.objects.all()
  2644. form = forms.FrontPortForm
  2645. @register_model_view(FrontPort, 'delete')
  2646. class FrontPortDeleteView(generic.ObjectDeleteView):
  2647. queryset = FrontPort.objects.all()
  2648. @register_model_view(FrontPort, 'bulk_import', path='import', detail=False)
  2649. class FrontPortBulkImportView(generic.BulkImportView):
  2650. queryset = FrontPort.objects.all()
  2651. model_form = forms.FrontPortImportForm
  2652. @register_model_view(FrontPort, 'bulk_edit', path='edit', detail=False)
  2653. class FrontPortBulkEditView(generic.BulkEditView):
  2654. queryset = FrontPort.objects.all()
  2655. filterset = filtersets.FrontPortFilterSet
  2656. table = tables.FrontPortTable
  2657. form = forms.FrontPortBulkEditForm
  2658. @register_model_view(FrontPort, 'bulk_rename', path='rename', detail=False)
  2659. class FrontPortBulkRenameView(generic.BulkRenameView):
  2660. queryset = FrontPort.objects.all()
  2661. filterset = filtersets.FrontPortFilterSet
  2662. @register_model_view(FrontPort, 'bulk_disconnect', path='disconnect', detail=False)
  2663. class FrontPortBulkDisconnectView(BulkDisconnectView):
  2664. queryset = FrontPort.objects.all()
  2665. @register_model_view(FrontPort, 'bulk_delete', path='delete', detail=False)
  2666. class FrontPortBulkDeleteView(generic.BulkDeleteView):
  2667. queryset = FrontPort.objects.all()
  2668. filterset = filtersets.FrontPortFilterSet
  2669. table = tables.FrontPortTable
  2670. # Trace view
  2671. register_model_view(FrontPort, 'trace', kwargs={'model': FrontPort})(PathTraceView)
  2672. #
  2673. # Rear ports
  2674. #
  2675. @register_model_view(RearPort, 'list', path='', detail=False)
  2676. class RearPortListView(generic.ObjectListView):
  2677. queryset = RearPort.objects.all()
  2678. filterset = filtersets.RearPortFilterSet
  2679. filterset_form = forms.RearPortFilterForm
  2680. table = tables.RearPortTable
  2681. @register_model_view(RearPort)
  2682. class RearPortView(generic.ObjectView):
  2683. queryset = RearPort.objects.all()
  2684. def get_extra_context(self, request, instance):
  2685. return {
  2686. 'front_port_mappings': PortMapping.objects.filter(rear_port=instance).prefetch_related('front_port'),
  2687. }
  2688. @register_model_view(RearPort, 'add', detail=False)
  2689. class RearPortCreateView(generic.ComponentCreateView):
  2690. queryset = RearPort.objects.all()
  2691. form = forms.RearPortCreateForm
  2692. model_form = forms.RearPortForm
  2693. @register_model_view(RearPort, 'edit')
  2694. class RearPortEditView(generic.ObjectEditView):
  2695. queryset = RearPort.objects.all()
  2696. form = forms.RearPortForm
  2697. @register_model_view(RearPort, 'delete')
  2698. class RearPortDeleteView(generic.ObjectDeleteView):
  2699. queryset = RearPort.objects.all()
  2700. @register_model_view(RearPort, 'bulk_import', path='import', detail=False)
  2701. class RearPortBulkImportView(generic.BulkImportView):
  2702. queryset = RearPort.objects.all()
  2703. model_form = forms.RearPortImportForm
  2704. @register_model_view(RearPort, 'bulk_edit', path='edit', detail=False)
  2705. class RearPortBulkEditView(generic.BulkEditView):
  2706. queryset = RearPort.objects.all()
  2707. filterset = filtersets.RearPortFilterSet
  2708. table = tables.RearPortTable
  2709. form = forms.RearPortBulkEditForm
  2710. @register_model_view(RearPort, 'bulk_rename', path='rename', detail=False)
  2711. class RearPortBulkRenameView(generic.BulkRenameView):
  2712. queryset = RearPort.objects.all()
  2713. @register_model_view(RearPort, 'bulk_disconnect', path='disconnect', detail=False)
  2714. class RearPortBulkDisconnectView(BulkDisconnectView):
  2715. queryset = RearPort.objects.all()
  2716. filterset = filtersets.RearPortFilterSet
  2717. @register_model_view(RearPort, 'bulk_delete', path='delete', detail=False)
  2718. class RearPortBulkDeleteView(generic.BulkDeleteView):
  2719. queryset = RearPort.objects.all()
  2720. filterset = filtersets.RearPortFilterSet
  2721. table = tables.RearPortTable
  2722. # Trace view
  2723. register_model_view(RearPort, 'trace', kwargs={'model': RearPort})(PathTraceView)
  2724. #
  2725. # Module bays
  2726. #
  2727. @register_model_view(ModuleBay, 'list', path='', detail=False)
  2728. class ModuleBayListView(generic.ObjectListView):
  2729. queryset = ModuleBay.objects.select_related('installed_module__module_type')
  2730. filterset = filtersets.ModuleBayFilterSet
  2731. filterset_form = forms.ModuleBayFilterForm
  2732. table = tables.ModuleBayTable
  2733. @register_model_view(ModuleBay)
  2734. class ModuleBayView(generic.ObjectView):
  2735. queryset = ModuleBay.objects.all()
  2736. @register_model_view(ModuleBay, 'add', detail=False)
  2737. class ModuleBayCreateView(generic.ComponentCreateView):
  2738. queryset = ModuleBay.objects.all()
  2739. form = forms.ModuleBayCreateForm
  2740. model_form = forms.ModuleBayForm
  2741. @register_model_view(ModuleBay, 'edit')
  2742. class ModuleBayEditView(generic.ObjectEditView):
  2743. queryset = ModuleBay.objects.all()
  2744. form = forms.ModuleBayForm
  2745. @register_model_view(ModuleBay, 'delete')
  2746. class ModuleBayDeleteView(generic.ObjectDeleteView):
  2747. queryset = ModuleBay.objects.all()
  2748. @register_model_view(ModuleBay, 'bulk_import', path='import', detail=False)
  2749. class ModuleBayBulkImportView(generic.BulkImportView):
  2750. queryset = ModuleBay.objects.all()
  2751. model_form = forms.ModuleBayImportForm
  2752. @register_model_view(ModuleBay, 'bulk_edit', path='edit', detail=False)
  2753. class ModuleBayBulkEditView(generic.BulkEditView):
  2754. queryset = ModuleBay.objects.all()
  2755. filterset = filtersets.ModuleBayFilterSet
  2756. table = tables.ModuleBayTable
  2757. form = forms.ModuleBayBulkEditForm
  2758. @register_model_view(ModuleBay, 'bulk_rename', path='rename', detail=False)
  2759. class ModuleBayBulkRenameView(generic.BulkRenameView):
  2760. queryset = ModuleBay.objects.all()
  2761. filterset = filtersets.ModuleBayFilterSet
  2762. @register_model_view(ModuleBay, 'bulk_delete', path='delete', detail=False)
  2763. class ModuleBayBulkDeleteView(generic.BulkDeleteView):
  2764. queryset = ModuleBay.objects.all()
  2765. filterset = filtersets.ModuleBayFilterSet
  2766. table = tables.ModuleBayTable
  2767. #
  2768. # Device bays
  2769. #
  2770. @register_model_view(DeviceBay, 'list', path='', detail=False)
  2771. class DeviceBayListView(generic.ObjectListView):
  2772. queryset = DeviceBay.objects.all()
  2773. filterset = filtersets.DeviceBayFilterSet
  2774. filterset_form = forms.DeviceBayFilterForm
  2775. table = tables.DeviceBayTable
  2776. @register_model_view(DeviceBay)
  2777. class DeviceBayView(generic.ObjectView):
  2778. queryset = DeviceBay.objects.all()
  2779. @register_model_view(DeviceBay, 'add', detail=False)
  2780. class DeviceBayCreateView(generic.ComponentCreateView):
  2781. queryset = DeviceBay.objects.all()
  2782. form = forms.DeviceBayCreateForm
  2783. model_form = forms.DeviceBayForm
  2784. @register_model_view(DeviceBay, 'edit')
  2785. class DeviceBayEditView(generic.ObjectEditView):
  2786. queryset = DeviceBay.objects.all()
  2787. form = forms.DeviceBayForm
  2788. @register_model_view(DeviceBay, 'delete')
  2789. class DeviceBayDeleteView(generic.ObjectDeleteView):
  2790. queryset = DeviceBay.objects.all()
  2791. @register_model_view(DeviceBay, 'populate')
  2792. class DeviceBayPopulateView(generic.ObjectEditView):
  2793. queryset = DeviceBay.objects.all()
  2794. def get(self, request, pk):
  2795. device_bay = get_object_or_404(self.queryset, pk=pk)
  2796. form = forms.PopulateDeviceBayForm(device_bay)
  2797. return render(request, 'dcim/devicebay_populate.html', {
  2798. 'device_bay': device_bay,
  2799. 'form': form,
  2800. 'return_url': self.get_return_url(request, device_bay),
  2801. })
  2802. def post(self, request, pk):
  2803. device_bay = get_object_or_404(self.queryset, pk=pk)
  2804. form = forms.PopulateDeviceBayForm(device_bay, request.POST)
  2805. if form.is_valid():
  2806. device_bay.snapshot()
  2807. device_bay.installed_device = form.cleaned_data['installed_device']
  2808. device_bay.save()
  2809. messages.success(
  2810. request,
  2811. _("Installed device {device} in bay {device_bay}.").format(
  2812. device=device_bay.installed_device,
  2813. device_bay=device_bay
  2814. )
  2815. )
  2816. return_url = self.get_return_url(request)
  2817. return redirect(return_url)
  2818. return render(request, 'dcim/devicebay_populate.html', {
  2819. 'device_bay': device_bay,
  2820. 'form': form,
  2821. 'return_url': self.get_return_url(request, device_bay),
  2822. })
  2823. @register_model_view(DeviceBay, 'depopulate')
  2824. class DeviceBayDepopulateView(generic.ObjectEditView):
  2825. queryset = DeviceBay.objects.all()
  2826. def get(self, request, pk):
  2827. device_bay = get_object_or_404(self.queryset, pk=pk)
  2828. form = ConfirmationForm()
  2829. return render(request, 'dcim/devicebay_depopulate.html', {
  2830. 'device_bay': device_bay,
  2831. 'form': form,
  2832. 'return_url': self.get_return_url(request, device_bay),
  2833. })
  2834. def post(self, request, pk):
  2835. device_bay = get_object_or_404(self.queryset, pk=pk)
  2836. form = ConfirmationForm(request.POST)
  2837. if form.is_valid():
  2838. device_bay.snapshot()
  2839. removed_device = device_bay.installed_device
  2840. device_bay.installed_device = None
  2841. device_bay.save()
  2842. messages.success(
  2843. request,
  2844. _("Removed device {device} from bay {device_bay}.").format(
  2845. device=removed_device,
  2846. device_bay=device_bay
  2847. )
  2848. )
  2849. return_url = self.get_return_url(request, device_bay.device)
  2850. return redirect(return_url)
  2851. return render(request, 'dcim/devicebay_depopulate.html', {
  2852. 'device_bay': device_bay,
  2853. 'form': form,
  2854. 'return_url': self.get_return_url(request, device_bay),
  2855. })
  2856. @register_model_view(DeviceBay, 'bulk_import', path='import', detail=False)
  2857. class DeviceBayBulkImportView(generic.BulkImportView):
  2858. queryset = DeviceBay.objects.all()
  2859. model_form = forms.DeviceBayImportForm
  2860. @register_model_view(DeviceBay, 'bulk_edit', path='edit', detail=False)
  2861. class DeviceBayBulkEditView(generic.BulkEditView):
  2862. queryset = DeviceBay.objects.all()
  2863. filterset = filtersets.DeviceBayFilterSet
  2864. table = tables.DeviceBayTable
  2865. form = forms.DeviceBayBulkEditForm
  2866. @register_model_view(DeviceBay, 'bulk_rename', path='rename', detail=False)
  2867. class DeviceBayBulkRenameView(generic.BulkRenameView):
  2868. queryset = DeviceBay.objects.all()
  2869. filterset = filtersets.DeviceBayFilterSet
  2870. @register_model_view(DeviceBay, 'bulk_delete', path='delete', detail=False)
  2871. class DeviceBayBulkDeleteView(generic.BulkDeleteView):
  2872. queryset = DeviceBay.objects.all()
  2873. filterset = filtersets.DeviceBayFilterSet
  2874. table = tables.DeviceBayTable
  2875. #
  2876. # Inventory items
  2877. #
  2878. @register_model_view(InventoryItem, 'list', path='', detail=False)
  2879. class InventoryItemListView(generic.ObjectListView):
  2880. queryset = InventoryItem.objects.all()
  2881. filterset = filtersets.InventoryItemFilterSet
  2882. filterset_form = forms.InventoryItemFilterForm
  2883. table = tables.InventoryItemTable
  2884. @register_model_view(InventoryItem)
  2885. class InventoryItemView(generic.ObjectView):
  2886. queryset = InventoryItem.objects.all()
  2887. @register_model_view(InventoryItem, 'edit')
  2888. class InventoryItemEditView(generic.ObjectEditView):
  2889. queryset = InventoryItem.objects.all()
  2890. form = forms.InventoryItemForm
  2891. @register_model_view(InventoryItem, 'add', detail=False)
  2892. class InventoryItemCreateView(generic.ComponentCreateView):
  2893. queryset = InventoryItem.objects.all()
  2894. form = forms.InventoryItemCreateForm
  2895. model_form = forms.InventoryItemForm
  2896. @register_model_view(InventoryItem, 'delete')
  2897. class InventoryItemDeleteView(generic.ObjectDeleteView):
  2898. queryset = InventoryItem.objects.all()
  2899. @register_model_view(InventoryItem, 'bulk_import', path='import', detail=False)
  2900. class InventoryItemBulkImportView(generic.BulkImportView):
  2901. queryset = InventoryItem.objects.all()
  2902. model_form = forms.InventoryItemImportForm
  2903. @register_model_view(InventoryItem, 'bulk_edit', path='edit', detail=False)
  2904. class InventoryItemBulkEditView(generic.BulkEditView):
  2905. queryset = InventoryItem.objects.all()
  2906. filterset = filtersets.InventoryItemFilterSet
  2907. table = tables.InventoryItemTable
  2908. form = forms.InventoryItemBulkEditForm
  2909. @register_model_view(InventoryItem, 'bulk_rename', path='rename', detail=False)
  2910. class InventoryItemBulkRenameView(generic.BulkRenameView):
  2911. queryset = InventoryItem.objects.all()
  2912. filterset = filtersets.InventoryItemFilterSet
  2913. @register_model_view(InventoryItem, 'bulk_delete', path='delete', detail=False)
  2914. class InventoryItemBulkDeleteView(generic.BulkDeleteView):
  2915. queryset = InventoryItem.objects.all()
  2916. filterset = filtersets.InventoryItemFilterSet
  2917. table = tables.InventoryItemTable
  2918. @register_model_view(InventoryItem, 'children')
  2919. class InventoryItemChildrenView(generic.ObjectChildrenView):
  2920. queryset = InventoryItem.objects.all()
  2921. child_model = InventoryItem
  2922. table = tables.InventoryItemTable
  2923. filterset = filtersets.InventoryItemFilterSet
  2924. filterset_form = forms.InventoryItemFilterForm
  2925. tab = ViewTab(
  2926. label=_('Children'),
  2927. badge=lambda obj: obj.child_items.count(),
  2928. permission='dcim.view_inventoryitem',
  2929. hide_if_empty=True,
  2930. weight=5000
  2931. )
  2932. def get_children(self, request, parent):
  2933. return parent.child_items.restrict(request.user, 'view')
  2934. #
  2935. # Inventory item roles
  2936. #
  2937. @register_model_view(InventoryItemRole, 'list', path='', detail=False)
  2938. class InventoryItemRoleListView(generic.ObjectListView):
  2939. queryset = InventoryItemRole.objects.annotate(
  2940. inventoryitem_count=count_related(InventoryItem, 'role'),
  2941. )
  2942. filterset = filtersets.InventoryItemRoleFilterSet
  2943. filterset_form = forms.InventoryItemRoleFilterForm
  2944. table = tables.InventoryItemRoleTable
  2945. @register_model_view(InventoryItemRole)
  2946. class InventoryItemRoleView(generic.ObjectView):
  2947. queryset = InventoryItemRole.objects.all()
  2948. def get_extra_context(self, request, instance):
  2949. return {
  2950. 'inventoryitem_count': InventoryItem.objects.filter(role=instance).count(),
  2951. }
  2952. @register_model_view(InventoryItemRole, 'add', detail=False)
  2953. @register_model_view(InventoryItemRole, 'edit')
  2954. class InventoryItemRoleEditView(generic.ObjectEditView):
  2955. queryset = InventoryItemRole.objects.all()
  2956. form = forms.InventoryItemRoleForm
  2957. @register_model_view(InventoryItemRole, 'delete')
  2958. class InventoryItemRoleDeleteView(generic.ObjectDeleteView):
  2959. queryset = InventoryItemRole.objects.all()
  2960. @register_model_view(InventoryItemRole, 'bulk_import', path='import', detail=False)
  2961. class InventoryItemRoleBulkImportView(generic.BulkImportView):
  2962. queryset = InventoryItemRole.objects.all()
  2963. model_form = forms.InventoryItemRoleImportForm
  2964. @register_model_view(InventoryItemRole, 'bulk_edit', path='edit', detail=False)
  2965. class InventoryItemRoleBulkEditView(generic.BulkEditView):
  2966. queryset = InventoryItemRole.objects.annotate(
  2967. inventoryitem_count=count_related(InventoryItem, 'role'),
  2968. )
  2969. filterset = filtersets.InventoryItemRoleFilterSet
  2970. table = tables.InventoryItemRoleTable
  2971. form = forms.InventoryItemRoleBulkEditForm
  2972. @register_model_view(InventoryItemRole, 'bulk_rename', path='rename', detail=False)
  2973. class InventoryItemRoleBulkRenameView(generic.BulkRenameView):
  2974. queryset = InventoryItemRole.objects.all()
  2975. filterset = filtersets.InventoryItemRoleFilterSet
  2976. @register_model_view(InventoryItemRole, 'bulk_delete', path='delete', detail=False)
  2977. class InventoryItemRoleBulkDeleteView(generic.BulkDeleteView):
  2978. queryset = InventoryItemRole.objects.annotate(
  2979. inventoryitem_count=count_related(InventoryItem, 'role'),
  2980. )
  2981. filterset = filtersets.InventoryItemRoleFilterSet
  2982. table = tables.InventoryItemRoleTable
  2983. #
  2984. # Bulk Device component creation
  2985. #
  2986. class DeviceBulkAddConsolePortView(generic.BulkComponentCreateView):
  2987. parent_model = Device
  2988. parent_field = 'device'
  2989. form = forms.ConsolePortBulkCreateForm
  2990. queryset = ConsolePort.objects.all()
  2991. model_form = forms.ConsolePortForm
  2992. filterset = filtersets.DeviceFilterSet
  2993. table = tables.DeviceTable
  2994. default_return_url = 'dcim:device_list'
  2995. class DeviceBulkAddConsoleServerPortView(generic.BulkComponentCreateView):
  2996. parent_model = Device
  2997. parent_field = 'device'
  2998. form = forms.ConsoleServerPortBulkCreateForm
  2999. queryset = ConsoleServerPort.objects.all()
  3000. model_form = forms.ConsoleServerPortForm
  3001. filterset = filtersets.DeviceFilterSet
  3002. table = tables.DeviceTable
  3003. default_return_url = 'dcim:device_list'
  3004. class DeviceBulkAddPowerPortView(generic.BulkComponentCreateView):
  3005. parent_model = Device
  3006. parent_field = 'device'
  3007. form = forms.PowerPortBulkCreateForm
  3008. queryset = PowerPort.objects.all()
  3009. model_form = forms.PowerPortForm
  3010. filterset = filtersets.DeviceFilterSet
  3011. table = tables.DeviceTable
  3012. default_return_url = 'dcim:device_list'
  3013. class DeviceBulkAddPowerOutletView(generic.BulkComponentCreateView):
  3014. parent_model = Device
  3015. parent_field = 'device'
  3016. form = forms.PowerOutletBulkCreateForm
  3017. queryset = PowerOutlet.objects.all()
  3018. model_form = forms.PowerOutletForm
  3019. filterset = filtersets.DeviceFilterSet
  3020. table = tables.DeviceTable
  3021. default_return_url = 'dcim:device_list'
  3022. class DeviceBulkAddInterfaceView(generic.BulkComponentCreateView):
  3023. parent_model = Device
  3024. parent_field = 'device'
  3025. form = forms.InterfaceBulkCreateForm
  3026. queryset = Interface.objects.all()
  3027. model_form = forms.InterfaceForm
  3028. filterset = filtersets.DeviceFilterSet
  3029. table = tables.DeviceTable
  3030. default_return_url = 'dcim:device_list'
  3031. class DeviceBulkAddRearPortView(generic.BulkComponentCreateView):
  3032. parent_model = Device
  3033. parent_field = 'device'
  3034. form = forms.RearPortBulkCreateForm
  3035. queryset = RearPort.objects.all()
  3036. model_form = forms.RearPortForm
  3037. filterset = filtersets.DeviceFilterSet
  3038. table = tables.DeviceTable
  3039. default_return_url = 'dcim:device_list'
  3040. class DeviceBulkAddModuleBayView(generic.BulkComponentCreateView):
  3041. parent_model = Device
  3042. parent_field = 'device'
  3043. form = forms.ModuleBayBulkCreateForm
  3044. queryset = ModuleBay.objects.all()
  3045. model_form = forms.ModuleBayForm
  3046. filterset = filtersets.DeviceFilterSet
  3047. table = tables.DeviceTable
  3048. default_return_url = 'dcim:device_list'
  3049. class DeviceBulkAddDeviceBayView(generic.BulkComponentCreateView):
  3050. parent_model = Device
  3051. parent_field = 'device'
  3052. form = forms.DeviceBayBulkCreateForm
  3053. queryset = DeviceBay.objects.all()
  3054. model_form = forms.DeviceBayForm
  3055. filterset = filtersets.DeviceFilterSet
  3056. table = tables.DeviceTable
  3057. default_return_url = 'dcim:device_list'
  3058. class DeviceBulkAddInventoryItemView(generic.BulkComponentCreateView):
  3059. parent_model = Device
  3060. parent_field = 'device'
  3061. form = forms.InventoryItemBulkCreateForm
  3062. queryset = InventoryItem.objects.all()
  3063. model_form = forms.InventoryItemForm
  3064. filterset = filtersets.DeviceFilterSet
  3065. table = tables.DeviceTable
  3066. default_return_url = 'dcim:device_list'
  3067. #
  3068. # Cables
  3069. #
  3070. @register_model_view(Cable, 'list', path='', detail=False)
  3071. class CableListView(generic.ObjectListView):
  3072. queryset = Cable.objects.prefetch_related(
  3073. 'terminations__termination', 'terminations___device', 'terminations___rack', 'terminations___location',
  3074. 'terminations___site',
  3075. )
  3076. filterset = filtersets.CableFilterSet
  3077. filterset_form = forms.CableFilterForm
  3078. table = tables.CableTable
  3079. @register_model_view(Cable)
  3080. class CableView(generic.ObjectView):
  3081. queryset = Cable.objects.all()
  3082. @register_model_view(Cable, 'add', detail=False)
  3083. @register_model_view(Cable, 'edit')
  3084. class CableEditView(generic.ObjectEditView):
  3085. queryset = Cable.objects.all()
  3086. template_name = 'dcim/cable_edit.html'
  3087. htmx_template_name = 'dcim/htmx/cable_edit.html'
  3088. def alter_object(self, obj, request, url_args, url_kwargs):
  3089. """
  3090. Hack into alter_object() to set the form class when editing an existing Cable, since ObjectEditView
  3091. doesn't currently provide a hook for dynamic class resolution.
  3092. """
  3093. a_terminations_type = CABLE_TERMINATION_TYPES.get(
  3094. request.POST.get('a_terminations_type') or request.GET.get('a_terminations_type')
  3095. )
  3096. b_terminations_type = CABLE_TERMINATION_TYPES.get(
  3097. request.POST.get('b_terminations_type') or request.GET.get('b_terminations_type')
  3098. )
  3099. if obj.pk:
  3100. if not a_terminations_type and (termination_a := obj.terminations.filter(cable_end='A').first()):
  3101. a_terminations_type = termination_a.termination._meta.model
  3102. if not b_terminations_type and (termination_b := obj.terminations.filter(cable_end='B').first()):
  3103. b_terminations_type = termination_b.termination._meta.model
  3104. self.form = forms.get_cable_form(a_terminations_type, b_terminations_type)
  3105. return super().alter_object(obj, request, url_args, url_kwargs)
  3106. @register_model_view(Cable, 'delete')
  3107. class CableDeleteView(generic.ObjectDeleteView):
  3108. queryset = Cable.objects.all()
  3109. @register_model_view(Cable, 'bulk_import', path='import', detail=False)
  3110. class CableBulkImportView(generic.BulkImportView):
  3111. queryset = Cable.objects.all()
  3112. model_form = forms.CableImportForm
  3113. @register_model_view(Cable, 'bulk_edit', path='edit', detail=False)
  3114. class CableBulkEditView(generic.BulkEditView):
  3115. queryset = Cable.objects.prefetch_related(
  3116. 'terminations__termination', 'terminations___device', 'terminations___rack', 'terminations___location',
  3117. 'terminations___site',
  3118. )
  3119. filterset = filtersets.CableFilterSet
  3120. table = tables.CableTable
  3121. form = forms.CableBulkEditForm
  3122. @register_model_view(Cable, 'bulk_rename', path='rename', detail=False)
  3123. class CableBulkRenameView(generic.BulkRenameView):
  3124. queryset = Cable.objects.all()
  3125. field_name = 'label'
  3126. filterset = filtersets.CableFilterSet
  3127. @register_model_view(Cable, 'bulk_delete', path='delete', detail=False)
  3128. class CableBulkDeleteView(generic.BulkDeleteView):
  3129. queryset = Cable.objects.prefetch_related(
  3130. 'terminations__termination', 'terminations___device', 'terminations___rack', 'terminations___location',
  3131. 'terminations___site',
  3132. )
  3133. filterset = filtersets.CableFilterSet
  3134. table = tables.CableTable
  3135. #
  3136. # Connections
  3137. #
  3138. class ConsoleConnectionsListView(generic.ObjectListView):
  3139. queryset = ConsolePort.objects.filter(_path__is_complete=True)
  3140. filterset = filtersets.ConsoleConnectionFilterSet
  3141. filterset_form = forms.ConsoleConnectionFilterForm
  3142. table = tables.ConsoleConnectionTable
  3143. template_name = 'dcim/connections_list.html'
  3144. actions = (BulkExport,)
  3145. def get_extra_context(self, request):
  3146. return {
  3147. 'title': 'Console Connections'
  3148. }
  3149. class PowerConnectionsListView(generic.ObjectListView):
  3150. queryset = PowerPort.objects.filter(_path__is_complete=True)
  3151. filterset = filtersets.PowerConnectionFilterSet
  3152. filterset_form = forms.PowerConnectionFilterForm
  3153. table = tables.PowerConnectionTable
  3154. template_name = 'dcim/connections_list.html'
  3155. actions = (BulkExport,)
  3156. def get_extra_context(self, request):
  3157. return {
  3158. 'title': 'Power Connections'
  3159. }
  3160. class InterfaceConnectionsListView(generic.ObjectListView):
  3161. queryset = Interface.objects.filter(_path__is_complete=True)
  3162. filterset = filtersets.InterfaceConnectionFilterSet
  3163. filterset_form = forms.InterfaceConnectionFilterForm
  3164. table = tables.InterfaceConnectionTable
  3165. template_name = 'dcim/connections_list.html'
  3166. actions = (BulkExport,)
  3167. def get_extra_context(self, request):
  3168. return {
  3169. 'title': 'Interface Connections'
  3170. }
  3171. #
  3172. # Virtual chassis
  3173. #
  3174. @register_model_view(VirtualChassis, 'list', path='', detail=False)
  3175. class VirtualChassisListView(generic.ObjectListView):
  3176. queryset = VirtualChassis.objects.all()
  3177. table = tables.VirtualChassisTable
  3178. filterset = filtersets.VirtualChassisFilterSet
  3179. filterset_form = forms.VirtualChassisFilterForm
  3180. @register_model_view(VirtualChassis)
  3181. class VirtualChassisView(generic.ObjectView):
  3182. queryset = VirtualChassis.objects.all()
  3183. def get_extra_context(self, request, instance):
  3184. members = Device.objects.restrict(request.user).filter(virtual_chassis=instance)
  3185. return {
  3186. 'members': members,
  3187. }
  3188. @register_model_view(VirtualChassis, 'add', detail=False)
  3189. class VirtualChassisCreateView(generic.ObjectEditView):
  3190. queryset = VirtualChassis.objects.all()
  3191. form = forms.VirtualChassisCreateForm
  3192. @register_model_view(VirtualChassis, 'edit')
  3193. class VirtualChassisEditView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View):
  3194. queryset = VirtualChassis.objects.all()
  3195. def get_required_permission(self):
  3196. return 'dcim.change_virtualchassis'
  3197. def get(self, request, pk):
  3198. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  3199. VCMemberFormSet = modelformset_factory(
  3200. model=Device,
  3201. form=forms.DeviceVCMembershipForm,
  3202. formset=forms.BaseVCMemberFormSet,
  3203. extra=0
  3204. )
  3205. members_queryset = virtual_chassis.members.prefetch_related('rack').order_by('vc_position')
  3206. vc_form = forms.VirtualChassisForm(instance=virtual_chassis)
  3207. vc_form.fields['master'].queryset = members_queryset
  3208. formset = VCMemberFormSet(queryset=members_queryset)
  3209. return render(request, 'dcim/virtualchassis_edit.html', {
  3210. 'object': virtual_chassis,
  3211. 'vc_form': vc_form,
  3212. 'formset': formset,
  3213. 'return_url': self.get_return_url(request, virtual_chassis),
  3214. })
  3215. def post(self, request, pk):
  3216. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  3217. virtual_chassis.snapshot()
  3218. VCMemberFormSet = modelformset_factory(
  3219. model=Device,
  3220. form=forms.DeviceVCMembershipForm,
  3221. formset=forms.BaseVCMemberFormSet,
  3222. extra=0
  3223. )
  3224. members_queryset = virtual_chassis.members.prefetch_related('rack').order_by('vc_position')
  3225. vc_form = forms.VirtualChassisForm(request.POST, instance=virtual_chassis)
  3226. vc_form.fields['master'].queryset = members_queryset
  3227. formset = VCMemberFormSet(request.POST, queryset=members_queryset)
  3228. if vc_form.is_valid() and formset.is_valid():
  3229. virtual_chassis._changelog_message = vc_form.cleaned_data.pop('changelog_message', '')
  3230. with transaction.atomic(using=router.db_for_write(Device)):
  3231. # Save the VirtualChassis
  3232. vc_form.save()
  3233. # Nullify the vc_position of each member first to allow reordering without raising an IntegrityError on
  3234. # duplicate positions. Then save each member instance.
  3235. members = formset.save(commit=False)
  3236. devices = Device.objects.filter(pk__in=[m.pk for m in members])
  3237. for device in devices:
  3238. device.snapshot()
  3239. device.vc_position = None
  3240. device.save()
  3241. for member in members:
  3242. member.save()
  3243. return redirect(virtual_chassis.get_absolute_url())
  3244. return render(request, 'dcim/virtualchassis_edit.html', {
  3245. 'vc_form': vc_form,
  3246. 'formset': formset,
  3247. 'return_url': self.get_return_url(request, virtual_chassis),
  3248. })
  3249. @register_model_view(VirtualChassis, 'delete')
  3250. class VirtualChassisDeleteView(generic.ObjectDeleteView):
  3251. queryset = VirtualChassis.objects.all()
  3252. @register_model_view(VirtualChassis, 'add_member', path='add-member')
  3253. class VirtualChassisAddMemberView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View):
  3254. queryset = VirtualChassis.objects.all()
  3255. def get_required_permission(self):
  3256. return 'dcim.change_virtualchassis'
  3257. def get(self, request, pk):
  3258. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  3259. initial_data = {k: request.GET[k] for k in request.GET}
  3260. member_select_form = forms.VCMemberSelectForm(initial=initial_data)
  3261. membership_form = forms.DeviceVCMembershipForm(initial=initial_data)
  3262. return render(request, 'dcim/virtualchassis_add_member.html', {
  3263. 'virtual_chassis': virtual_chassis,
  3264. 'member_select_form': member_select_form,
  3265. 'membership_form': membership_form,
  3266. 'return_url': self.get_return_url(request, virtual_chassis),
  3267. })
  3268. def post(self, request, pk):
  3269. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  3270. member_select_form = forms.VCMemberSelectForm(request.POST)
  3271. if member_select_form.is_valid():
  3272. device = member_select_form.cleaned_data['device']
  3273. device.snapshot()
  3274. device.virtual_chassis = virtual_chassis
  3275. data = {
  3276. 'vc_position': request.POST['vc_position'],
  3277. 'vc_priority': request.POST['vc_priority'],
  3278. }
  3279. membership_form = forms.DeviceVCMembershipForm(data=data, validate_vc_position=True, instance=device)
  3280. if membership_form.is_valid():
  3281. membership_form.save()
  3282. messages.success(request, mark_safe(
  3283. _('Added member <a href="{url}">{device}</a>').format(
  3284. url=device.get_absolute_url(), device=escape(device)
  3285. )
  3286. ))
  3287. if '_addanother' in request.POST and safe_for_redirect(request.get_full_path()):
  3288. return redirect(request.get_full_path())
  3289. return redirect(self.get_return_url(request, device))
  3290. else:
  3291. membership_form = forms.DeviceVCMembershipForm(data=request.POST)
  3292. return render(request, 'dcim/virtualchassis_add_member.html', {
  3293. 'virtual_chassis': virtual_chassis,
  3294. 'member_select_form': member_select_form,
  3295. 'membership_form': membership_form,
  3296. 'return_url': self.get_return_url(request, virtual_chassis),
  3297. })
  3298. class VirtualChassisRemoveMemberView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View):
  3299. queryset = Device.objects.all()
  3300. def get_required_permission(self):
  3301. return 'dcim.change_device'
  3302. def get(self, request, pk):
  3303. device = get_object_or_404(self.queryset, pk=pk, virtual_chassis__isnull=False)
  3304. form = ConfirmationForm(initial=request.GET)
  3305. return render(request, 'dcim/virtualchassis_remove_member.html', {
  3306. 'device': device,
  3307. 'form': form,
  3308. 'return_url': self.get_return_url(request, device),
  3309. })
  3310. def post(self, request, pk):
  3311. device = get_object_or_404(self.queryset, pk=pk, virtual_chassis__isnull=False)
  3312. form = ConfirmationForm(request.POST)
  3313. # Protect master device from being removed
  3314. virtual_chassis = VirtualChassis.objects.filter(master=device).first()
  3315. if virtual_chassis is not None:
  3316. messages.error(
  3317. request,
  3318. _('Unable to remove master device {device} from the virtual chassis.').format(device=device)
  3319. )
  3320. return redirect(device.get_absolute_url())
  3321. if form.is_valid():
  3322. device.snapshot()
  3323. device.virtual_chassis = None
  3324. device.vc_position = None
  3325. device.vc_priority = None
  3326. device.save()
  3327. msg = _('Removed {device} from virtual chassis {chassis}').format(
  3328. device=device,
  3329. chassis=device.virtual_chassis
  3330. )
  3331. messages.success(request, msg)
  3332. return redirect(self.get_return_url(request, device))
  3333. return render(request, 'dcim/virtualchassis_remove_member.html', {
  3334. 'device': device,
  3335. 'form': form,
  3336. 'return_url': self.get_return_url(request, device),
  3337. })
  3338. @register_model_view(VirtualChassis, 'bulk_import', path='import', detail=False)
  3339. class VirtualChassisBulkImportView(generic.BulkImportView):
  3340. queryset = VirtualChassis.objects.all()
  3341. model_form = forms.VirtualChassisImportForm
  3342. @register_model_view(VirtualChassis, 'bulk_edit', path='edit', detail=False)
  3343. class VirtualChassisBulkEditView(generic.BulkEditView):
  3344. queryset = VirtualChassis.objects.all()
  3345. filterset = filtersets.VirtualChassisFilterSet
  3346. table = tables.VirtualChassisTable
  3347. form = forms.VirtualChassisBulkEditForm
  3348. @register_model_view(VirtualChassis, 'bulk_rename', path='rename', detail=False)
  3349. class VirtualChassisBulkRenameView(generic.BulkRenameView):
  3350. queryset = VirtualChassis.objects.all()
  3351. filterset = filtersets.VirtualChassisFilterSet
  3352. @register_model_view(VirtualChassis, 'bulk_delete', path='delete', detail=False)
  3353. class VirtualChassisBulkDeleteView(generic.BulkDeleteView):
  3354. queryset = VirtualChassis.objects.all()
  3355. filterset = filtersets.VirtualChassisFilterSet
  3356. table = tables.VirtualChassisTable
  3357. #
  3358. # Power panels
  3359. #
  3360. @register_model_view(PowerPanel, 'list', path='', detail=False)
  3361. class PowerPanelListView(generic.ObjectListView):
  3362. queryset = PowerPanel.objects.annotate(
  3363. powerfeed_count=count_related(PowerFeed, 'power_panel')
  3364. )
  3365. filterset = filtersets.PowerPanelFilterSet
  3366. filterset_form = forms.PowerPanelFilterForm
  3367. table = tables.PowerPanelTable
  3368. @register_model_view(PowerPanel)
  3369. class PowerPanelView(GetRelatedModelsMixin, generic.ObjectView):
  3370. queryset = PowerPanel.objects.all()
  3371. def get_extra_context(self, request, instance):
  3372. return {
  3373. 'related_models': self.get_related_models(request, instance),
  3374. }
  3375. @register_model_view(PowerPanel, 'add', detail=False)
  3376. @register_model_view(PowerPanel, 'edit')
  3377. class PowerPanelEditView(generic.ObjectEditView):
  3378. queryset = PowerPanel.objects.all()
  3379. form = forms.PowerPanelForm
  3380. @register_model_view(PowerPanel, 'delete')
  3381. class PowerPanelDeleteView(generic.ObjectDeleteView):
  3382. queryset = PowerPanel.objects.all()
  3383. @register_model_view(PowerPanel, 'bulk_import', path='import', detail=False)
  3384. class PowerPanelBulkImportView(generic.BulkImportView):
  3385. queryset = PowerPanel.objects.all()
  3386. model_form = forms.PowerPanelImportForm
  3387. @register_model_view(PowerPanel, 'bulk_edit', path='edit', detail=False)
  3388. class PowerPanelBulkEditView(generic.BulkEditView):
  3389. queryset = PowerPanel.objects.all()
  3390. filterset = filtersets.PowerPanelFilterSet
  3391. table = tables.PowerPanelTable
  3392. form = forms.PowerPanelBulkEditForm
  3393. @register_model_view(PowerPanel, 'bulk_rename', path='rename', detail=False)
  3394. class PowerPanelBulkRenameView(generic.BulkRenameView):
  3395. queryset = PowerPanel.objects.all()
  3396. filterset = filtersets.PowerPanelFilterSet
  3397. @register_model_view(PowerPanel, 'bulk_delete', path='delete', detail=False)
  3398. class PowerPanelBulkDeleteView(generic.BulkDeleteView):
  3399. queryset = PowerPanel.objects.annotate(
  3400. powerfeed_count=count_related(PowerFeed, 'power_panel')
  3401. )
  3402. filterset = filtersets.PowerPanelFilterSet
  3403. table = tables.PowerPanelTable
  3404. #
  3405. # Power feeds
  3406. #
  3407. @register_model_view(PowerFeed, 'list', path='', detail=False)
  3408. class PowerFeedListView(generic.ObjectListView):
  3409. queryset = PowerFeed.objects.all()
  3410. filterset = filtersets.PowerFeedFilterSet
  3411. filterset_form = forms.PowerFeedFilterForm
  3412. table = tables.PowerFeedTable
  3413. @register_model_view(PowerFeed)
  3414. class PowerFeedView(generic.ObjectView):
  3415. queryset = PowerFeed.objects.all()
  3416. @register_model_view(PowerFeed, 'add', detail=False)
  3417. @register_model_view(PowerFeed, 'edit')
  3418. class PowerFeedEditView(generic.ObjectEditView):
  3419. queryset = PowerFeed.objects.all()
  3420. form = forms.PowerFeedForm
  3421. @register_model_view(PowerFeed, 'delete')
  3422. class PowerFeedDeleteView(generic.ObjectDeleteView):
  3423. queryset = PowerFeed.objects.all()
  3424. @register_model_view(PowerFeed, 'bulk_import', path='import', detail=False)
  3425. class PowerFeedBulkImportView(generic.BulkImportView):
  3426. queryset = PowerFeed.objects.all()
  3427. model_form = forms.PowerFeedImportForm
  3428. @register_model_view(PowerFeed, 'bulk_edit', path='edit', detail=False)
  3429. class PowerFeedBulkEditView(generic.BulkEditView):
  3430. queryset = PowerFeed.objects.all()
  3431. filterset = filtersets.PowerFeedFilterSet
  3432. table = tables.PowerFeedTable
  3433. form = forms.PowerFeedBulkEditForm
  3434. @register_model_view(PowerFeed, 'bulk_rename', path='rename', detail=False)
  3435. class PowerFeedBulkRenameView(generic.BulkRenameView):
  3436. queryset = PowerFeed.objects.all()
  3437. filterset = filtersets.PowerFeedFilterSet
  3438. @register_model_view(PowerFeed, 'bulk_disconnect', path='disconnect', detail=False)
  3439. class PowerFeedBulkDisconnectView(BulkDisconnectView):
  3440. queryset = PowerFeed.objects.all()
  3441. @register_model_view(PowerFeed, 'bulk_delete', path='delete', detail=False)
  3442. class PowerFeedBulkDeleteView(generic.BulkDeleteView):
  3443. queryset = PowerFeed.objects.all()
  3444. filterset = filtersets.PowerFeedFilterSet
  3445. table = tables.PowerFeedTable
  3446. # Trace view
  3447. register_model_view(PowerFeed, 'trace', kwargs={'model': PowerFeed})(PathTraceView)
  3448. #
  3449. # Virtual device contexts
  3450. #
  3451. @register_model_view(VirtualDeviceContext, 'list', path='', detail=False)
  3452. class VirtualDeviceContextListView(generic.ObjectListView):
  3453. queryset = VirtualDeviceContext.objects.annotate(
  3454. interface_count=count_related(Interface, 'vdcs'),
  3455. )
  3456. filterset = filtersets.VirtualDeviceContextFilterSet
  3457. filterset_form = forms.VirtualDeviceContextFilterForm
  3458. table = tables.VirtualDeviceContextTable
  3459. actions = (AddObject, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete)
  3460. @register_model_view(VirtualDeviceContext)
  3461. class VirtualDeviceContextView(GetRelatedModelsMixin, generic.ObjectView):
  3462. queryset = VirtualDeviceContext.objects.all()
  3463. def get_extra_context(self, request, instance):
  3464. return {
  3465. 'related_models': self.get_related_models(
  3466. request,
  3467. instance,
  3468. extra=(
  3469. (Interface.objects.restrict(request.user, 'view').filter(vdcs__in=[instance]), 'vdc_id'),
  3470. ),
  3471. ),
  3472. }
  3473. @register_model_view(VirtualDeviceContext, 'add', detail=False)
  3474. @register_model_view(VirtualDeviceContext, 'edit')
  3475. class VirtualDeviceContextEditView(generic.ObjectEditView):
  3476. queryset = VirtualDeviceContext.objects.all()
  3477. form = forms.VirtualDeviceContextForm
  3478. @register_model_view(VirtualDeviceContext, 'delete')
  3479. class VirtualDeviceContextDeleteView(generic.ObjectDeleteView):
  3480. queryset = VirtualDeviceContext.objects.all()
  3481. @register_model_view(VirtualDeviceContext, 'bulk_import', path='import', detail=False)
  3482. class VirtualDeviceContextBulkImportView(generic.BulkImportView):
  3483. queryset = VirtualDeviceContext.objects.all()
  3484. model_form = forms.VirtualDeviceContextImportForm
  3485. @register_model_view(VirtualDeviceContext, 'bulk_edit', path='edit', detail=False)
  3486. class VirtualDeviceContextBulkEditView(generic.BulkEditView):
  3487. queryset = VirtualDeviceContext.objects.all()
  3488. filterset = filtersets.VirtualDeviceContextFilterSet
  3489. table = tables.VirtualDeviceContextTable
  3490. form = forms.VirtualDeviceContextBulkEditForm
  3491. @register_model_view(VirtualDeviceContext, 'bulk_rename', path='rename', detail=False)
  3492. class VirtualDeviceContextBulkRenameView(generic.BulkRenameView):
  3493. queryset = VirtualDeviceContext.objects.all()
  3494. filterset = filtersets.VirtualDeviceContextFilterSet
  3495. @register_model_view(VirtualDeviceContext, 'bulk_delete', path='delete', detail=False)
  3496. class VirtualDeviceContextBulkDeleteView(generic.BulkDeleteView):
  3497. queryset = VirtualDeviceContext.objects.all()
  3498. filterset = filtersets.VirtualDeviceContextFilterSet
  3499. table = tables.VirtualDeviceContextTable
  3500. #
  3501. # MAC addresses
  3502. #
  3503. @register_model_view(MACAddress, 'list', path='', detail=False)
  3504. class MACAddressListView(generic.ObjectListView):
  3505. queryset = MACAddress.objects.all()
  3506. filterset = filtersets.MACAddressFilterSet
  3507. filterset_form = forms.MACAddressFilterForm
  3508. table = tables.MACAddressTable
  3509. actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete)
  3510. @register_model_view(MACAddress)
  3511. class MACAddressView(generic.ObjectView):
  3512. queryset = MACAddress.objects.all()
  3513. @register_model_view(MACAddress, 'add', detail=False)
  3514. @register_model_view(MACAddress, 'edit')
  3515. class MACAddressEditView(generic.ObjectEditView):
  3516. queryset = MACAddress.objects.all()
  3517. form = forms.MACAddressForm
  3518. @register_model_view(MACAddress, 'delete')
  3519. class MACAddressDeleteView(generic.ObjectDeleteView):
  3520. queryset = MACAddress.objects.all()
  3521. @register_model_view(MACAddress, 'bulk_import', path='import', detail=False)
  3522. class MACAddressBulkImportView(generic.BulkImportView):
  3523. queryset = MACAddress.objects.all()
  3524. model_form = forms.MACAddressImportForm
  3525. @register_model_view(MACAddress, 'bulk_edit', path='edit', detail=False)
  3526. class MACAddressBulkEditView(generic.BulkEditView):
  3527. queryset = MACAddress.objects.all()
  3528. filterset = filtersets.MACAddressFilterSet
  3529. table = tables.MACAddressTable
  3530. form = forms.MACAddressBulkEditForm
  3531. @register_model_view(MACAddress, 'bulk_delete', path='delete', detail=False)
  3532. class MACAddressBulkDeleteView(generic.BulkDeleteView):
  3533. queryset = MACAddress.objects.all()
  3534. filterset = filtersets.MACAddressFilterSet
  3535. table = tables.MACAddressTable