views.py 112 KB

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