views.py 113 KB

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