views.py 151 KB

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