views.py 151 KB

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