views.py 110 KB

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