views.py 83 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669
  1. from collections import OrderedDict
  2. from django.contrib import messages
  3. from django.contrib.contenttypes.models import ContentType
  4. from django.core.paginator import EmptyPage, PageNotAnInteger
  5. from django.db import transaction
  6. from django.db.models import F, Prefetch
  7. from django.forms import ModelMultipleChoiceField, MultipleHiddenInput, modelformset_factory
  8. from django.shortcuts import get_object_or_404, redirect, render
  9. from django.utils.html import escape
  10. from django.utils.safestring import mark_safe
  11. from django.views.generic import View
  12. from circuits.models import Circuit
  13. from extras.views import ObjectChangeLogView, ObjectConfigContextView
  14. from ipam.models import IPAddress, Prefix, Service, VLAN
  15. from ipam.tables import InterfaceIPAddressTable, InterfaceVLANTable
  16. from netbox.views import generic
  17. from secrets.models import Secret
  18. from utilities.forms import ConfirmationForm
  19. from utilities.paginator import EnhancedPaginator, get_paginate_count
  20. from utilities.permissions import get_permission_for_model
  21. from utilities.utils import csv_format, count_related
  22. from utilities.views import GetReturnURLMixin, ObjectPermissionRequiredMixin
  23. from virtualization.models import VirtualMachine
  24. from . import filters, forms, tables
  25. from .choices import DeviceFaceChoices
  26. from .constants import NONCONNECTABLE_IFACE_TYPES
  27. from .models import (
  28. Cable, CablePath, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
  29. DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
  30. InventoryItem, Manufacturer, PathEndpoint, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel,
  31. PowerPort, PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site,
  32. VirtualChassis,
  33. )
  34. class BulkDisconnectView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
  35. """
  36. An extendable view for disconnection console/power/interface components in bulk.
  37. """
  38. queryset = None
  39. template_name = 'dcim/bulk_disconnect.html'
  40. def __init__(self, *args, **kwargs):
  41. super().__init__(*args, **kwargs)
  42. # Create a new Form class from ConfirmationForm
  43. class _Form(ConfirmationForm):
  44. pk = ModelMultipleChoiceField(
  45. queryset=self.queryset,
  46. widget=MultipleHiddenInput()
  47. )
  48. self.form = _Form
  49. def get_required_permission(self):
  50. return get_permission_for_model(self.queryset.model, 'change')
  51. def post(self, request):
  52. selected_objects = []
  53. return_url = self.get_return_url(request)
  54. if '_confirm' in request.POST:
  55. form = self.form(request.POST)
  56. if form.is_valid():
  57. with transaction.atomic():
  58. count = 0
  59. for obj in self.queryset.filter(pk__in=form.cleaned_data['pk']):
  60. if obj.cable is None:
  61. continue
  62. obj.cable.delete()
  63. count += 1
  64. messages.success(request, "Disconnected {} {}".format(
  65. count, self.queryset.model._meta.verbose_name_plural
  66. ))
  67. return redirect(return_url)
  68. else:
  69. form = self.form(initial={'pk': request.POST.getlist('pk')})
  70. selected_objects = self.queryset.filter(pk__in=form.initial['pk'])
  71. return render(request, self.template_name, {
  72. 'form': form,
  73. 'obj_type_plural': self.queryset.model._meta.verbose_name_plural,
  74. 'selected_objects': selected_objects,
  75. 'return_url': return_url,
  76. })
  77. #
  78. # Regions
  79. #
  80. class RegionListView(generic.ObjectListView):
  81. queryset = Region.objects.add_related_count(
  82. Region.objects.all(),
  83. Site,
  84. 'region',
  85. 'site_count',
  86. cumulative=True
  87. )
  88. filterset = filters.RegionFilterSet
  89. filterset_form = forms.RegionFilterForm
  90. table = tables.RegionTable
  91. class RegionEditView(generic.ObjectEditView):
  92. queryset = Region.objects.all()
  93. model_form = forms.RegionForm
  94. class RegionDeleteView(generic.ObjectDeleteView):
  95. queryset = Region.objects.all()
  96. class RegionBulkImportView(generic.BulkImportView):
  97. queryset = Region.objects.all()
  98. model_form = forms.RegionCSVForm
  99. table = tables.RegionTable
  100. class RegionBulkDeleteView(generic.BulkDeleteView):
  101. queryset = Region.objects.add_related_count(
  102. Region.objects.all(),
  103. Site,
  104. 'region',
  105. 'site_count',
  106. cumulative=True
  107. )
  108. filterset = filters.RegionFilterSet
  109. table = tables.RegionTable
  110. #
  111. # Sites
  112. #
  113. class SiteListView(generic.ObjectListView):
  114. queryset = Site.objects.all()
  115. filterset = filters.SiteFilterSet
  116. filterset_form = forms.SiteFilterForm
  117. table = tables.SiteTable
  118. class SiteView(generic.ObjectView):
  119. queryset = Site.objects.prefetch_related('region', 'tenant__group')
  120. def get_extra_context(self, request, instance):
  121. stats = {
  122. 'rack_count': Rack.objects.restrict(request.user, 'view').filter(site=instance).count(),
  123. 'device_count': Device.objects.restrict(request.user, 'view').filter(site=instance).count(),
  124. 'prefix_count': Prefix.objects.restrict(request.user, 'view').filter(site=instance).count(),
  125. 'vlan_count': VLAN.objects.restrict(request.user, 'view').filter(site=instance).count(),
  126. 'circuit_count': Circuit.objects.restrict(request.user, 'view').filter(terminations__site=instance).count(),
  127. 'vm_count': VirtualMachine.objects.restrict(request.user, 'view').filter(cluster__site=instance).count(),
  128. }
  129. rack_groups = RackGroup.objects.add_related_count(
  130. RackGroup.objects.all(),
  131. Rack,
  132. 'group',
  133. 'rack_count',
  134. cumulative=True
  135. ).restrict(request.user, 'view').filter(site=instance)
  136. return {
  137. 'stats': stats,
  138. 'rack_groups': rack_groups,
  139. }
  140. class SiteEditView(generic.ObjectEditView):
  141. queryset = Site.objects.all()
  142. model_form = forms.SiteForm
  143. template_name = 'dcim/site_edit.html'
  144. class SiteDeleteView(generic.ObjectDeleteView):
  145. queryset = Site.objects.all()
  146. class SiteBulkImportView(generic.BulkImportView):
  147. queryset = Site.objects.all()
  148. model_form = forms.SiteCSVForm
  149. table = tables.SiteTable
  150. class SiteBulkEditView(generic.BulkEditView):
  151. queryset = Site.objects.prefetch_related('region', 'tenant')
  152. filterset = filters.SiteFilterSet
  153. table = tables.SiteTable
  154. form = forms.SiteBulkEditForm
  155. class SiteBulkDeleteView(generic.BulkDeleteView):
  156. queryset = Site.objects.prefetch_related('region', 'tenant')
  157. filterset = filters.SiteFilterSet
  158. table = tables.SiteTable
  159. #
  160. # Rack groups
  161. #
  162. class RackGroupListView(generic.ObjectListView):
  163. queryset = RackGroup.objects.add_related_count(
  164. RackGroup.objects.all(),
  165. Rack,
  166. 'group',
  167. 'rack_count',
  168. cumulative=True
  169. )
  170. filterset = filters.RackGroupFilterSet
  171. filterset_form = forms.RackGroupFilterForm
  172. table = tables.RackGroupTable
  173. class RackGroupEditView(generic.ObjectEditView):
  174. queryset = RackGroup.objects.all()
  175. model_form = forms.RackGroupForm
  176. class RackGroupDeleteView(generic.ObjectDeleteView):
  177. queryset = RackGroup.objects.all()
  178. class RackGroupBulkImportView(generic.BulkImportView):
  179. queryset = RackGroup.objects.all()
  180. model_form = forms.RackGroupCSVForm
  181. table = tables.RackGroupTable
  182. class RackGroupBulkDeleteView(generic.BulkDeleteView):
  183. queryset = RackGroup.objects.add_related_count(
  184. RackGroup.objects.all(),
  185. Rack,
  186. 'group',
  187. 'rack_count',
  188. cumulative=True
  189. ).prefetch_related('site')
  190. filterset = filters.RackGroupFilterSet
  191. table = tables.RackGroupTable
  192. #
  193. # Rack roles
  194. #
  195. class RackRoleListView(generic.ObjectListView):
  196. queryset = RackRole.objects.annotate(
  197. rack_count=count_related(Rack, 'role')
  198. )
  199. table = tables.RackRoleTable
  200. class RackRoleEditView(generic.ObjectEditView):
  201. queryset = RackRole.objects.all()
  202. model_form = forms.RackRoleForm
  203. class RackRoleDeleteView(generic.ObjectDeleteView):
  204. queryset = RackRole.objects.all()
  205. class RackRoleBulkImportView(generic.BulkImportView):
  206. queryset = RackRole.objects.all()
  207. model_form = forms.RackRoleCSVForm
  208. table = tables.RackRoleTable
  209. class RackRoleBulkDeleteView(generic.BulkDeleteView):
  210. queryset = RackRole.objects.annotate(
  211. rack_count=count_related(Rack, 'role')
  212. )
  213. table = tables.RackRoleTable
  214. #
  215. # Racks
  216. #
  217. class RackListView(generic.ObjectListView):
  218. queryset = Rack.objects.prefetch_related(
  219. 'site', 'group', 'tenant', 'role', 'devices__device_type'
  220. ).annotate(
  221. device_count=count_related(Device, 'rack')
  222. )
  223. filterset = filters.RackFilterSet
  224. filterset_form = forms.RackFilterForm
  225. table = tables.RackDetailTable
  226. class RackElevationListView(generic.ObjectListView):
  227. """
  228. Display a set of rack elevations side-by-side.
  229. """
  230. queryset = Rack.objects.prefetch_related('role')
  231. def get(self, request):
  232. racks = filters.RackFilterSet(request.GET, self.queryset).qs
  233. total_count = racks.count()
  234. # Determine ordering
  235. reverse = bool(request.GET.get('reverse', False))
  236. if reverse:
  237. racks = racks.reverse()
  238. # Pagination
  239. per_page = get_paginate_count(request)
  240. page_number = request.GET.get('page', 1)
  241. paginator = EnhancedPaginator(racks, per_page)
  242. try:
  243. page = paginator.page(page_number)
  244. except PageNotAnInteger:
  245. page = paginator.page(1)
  246. except EmptyPage:
  247. page = paginator.page(paginator.num_pages)
  248. # Determine rack face
  249. rack_face = request.GET.get('face', DeviceFaceChoices.FACE_FRONT)
  250. if rack_face not in DeviceFaceChoices.values():
  251. rack_face = DeviceFaceChoices.FACE_FRONT
  252. return render(request, 'dcim/rack_elevation_list.html', {
  253. 'paginator': paginator,
  254. 'page': page,
  255. 'total_count': total_count,
  256. 'reverse': reverse,
  257. 'rack_face': rack_face,
  258. 'filter_form': forms.RackElevationFilterForm(request.GET),
  259. })
  260. class RackView(generic.ObjectView):
  261. queryset = Rack.objects.prefetch_related('site__region', 'tenant__group', 'group', 'role')
  262. def get_extra_context(self, request, instance):
  263. # Get 0U and child devices located within the rack
  264. nonracked_devices = Device.objects.filter(
  265. rack=instance,
  266. position__isnull=True
  267. ).prefetch_related('device_type__manufacturer')
  268. peer_racks = Rack.objects.restrict(request.user, 'view').filter(site=instance.site)
  269. if instance.group:
  270. peer_racks = peer_racks.filter(group=instance.group)
  271. else:
  272. peer_racks = peer_racks.filter(group__isnull=True)
  273. next_rack = peer_racks.filter(name__gt=instance.name).order_by('name').first()
  274. prev_rack = peer_racks.filter(name__lt=instance.name).order_by('-name').first()
  275. reservations = RackReservation.objects.restrict(request.user, 'view').filter(rack=instance)
  276. power_feeds = PowerFeed.objects.restrict(request.user, 'view').filter(rack=instance).prefetch_related(
  277. 'power_panel'
  278. )
  279. device_count = Device.objects.restrict(request.user, 'view').filter(rack=instance).count()
  280. return {
  281. 'device_count': device_count,
  282. 'reservations': reservations,
  283. 'power_feeds': power_feeds,
  284. 'nonracked_devices': nonracked_devices,
  285. 'next_rack': next_rack,
  286. 'prev_rack': prev_rack,
  287. }
  288. class RackEditView(generic.ObjectEditView):
  289. queryset = Rack.objects.all()
  290. model_form = forms.RackForm
  291. template_name = 'dcim/rack_edit.html'
  292. class RackDeleteView(generic.ObjectDeleteView):
  293. queryset = Rack.objects.all()
  294. class RackBulkImportView(generic.BulkImportView):
  295. queryset = Rack.objects.all()
  296. model_form = forms.RackCSVForm
  297. table = tables.RackTable
  298. class RackBulkEditView(generic.BulkEditView):
  299. queryset = Rack.objects.prefetch_related('site', 'group', 'tenant', 'role')
  300. filterset = filters.RackFilterSet
  301. table = tables.RackTable
  302. form = forms.RackBulkEditForm
  303. class RackBulkDeleteView(generic.BulkDeleteView):
  304. queryset = Rack.objects.prefetch_related('site', 'group', 'tenant', 'role')
  305. filterset = filters.RackFilterSet
  306. table = tables.RackTable
  307. #
  308. # Rack reservations
  309. #
  310. class RackReservationListView(generic.ObjectListView):
  311. queryset = RackReservation.objects.all()
  312. filterset = filters.RackReservationFilterSet
  313. filterset_form = forms.RackReservationFilterForm
  314. table = tables.RackReservationTable
  315. class RackReservationView(generic.ObjectView):
  316. queryset = RackReservation.objects.prefetch_related('rack')
  317. class RackReservationEditView(generic.ObjectEditView):
  318. queryset = RackReservation.objects.all()
  319. model_form = forms.RackReservationForm
  320. template_name = 'dcim/rackreservation_edit.html'
  321. def alter_obj(self, obj, request, args, kwargs):
  322. if not obj.pk:
  323. if 'rack' in request.GET:
  324. obj.rack = get_object_or_404(Rack, pk=request.GET.get('rack'))
  325. obj.user = request.user
  326. return obj
  327. class RackReservationDeleteView(generic.ObjectDeleteView):
  328. queryset = RackReservation.objects.all()
  329. class RackReservationImportView(generic.BulkImportView):
  330. queryset = RackReservation.objects.all()
  331. model_form = forms.RackReservationCSVForm
  332. table = tables.RackReservationTable
  333. def _save_obj(self, obj_form, request):
  334. """
  335. Assign the currently authenticated user to the RackReservation.
  336. """
  337. instance = obj_form.save(commit=False)
  338. instance.user = request.user
  339. instance.save()
  340. return instance
  341. class RackReservationBulkEditView(generic.BulkEditView):
  342. queryset = RackReservation.objects.prefetch_related('rack', 'user')
  343. filterset = filters.RackReservationFilterSet
  344. table = tables.RackReservationTable
  345. form = forms.RackReservationBulkEditForm
  346. class RackReservationBulkDeleteView(generic.BulkDeleteView):
  347. queryset = RackReservation.objects.prefetch_related('rack', 'user')
  348. filterset = filters.RackReservationFilterSet
  349. table = tables.RackReservationTable
  350. #
  351. # Manufacturers
  352. #
  353. class ManufacturerListView(generic.ObjectListView):
  354. queryset = Manufacturer.objects.annotate(
  355. devicetype_count=count_related(DeviceType, 'manufacturer'),
  356. inventoryitem_count=count_related(InventoryItem, 'manufacturer'),
  357. platform_count=count_related(Platform, 'manufacturer')
  358. )
  359. table = tables.ManufacturerTable
  360. class ManufacturerEditView(generic.ObjectEditView):
  361. queryset = Manufacturer.objects.all()
  362. model_form = forms.ManufacturerForm
  363. class ManufacturerDeleteView(generic.ObjectDeleteView):
  364. queryset = Manufacturer.objects.all()
  365. class ManufacturerBulkImportView(generic.BulkImportView):
  366. queryset = Manufacturer.objects.all()
  367. model_form = forms.ManufacturerCSVForm
  368. table = tables.ManufacturerTable
  369. class ManufacturerBulkDeleteView(generic.BulkDeleteView):
  370. queryset = Manufacturer.objects.annotate(
  371. devicetype_count=count_related(DeviceType, 'manufacturer')
  372. )
  373. table = tables.ManufacturerTable
  374. #
  375. # Device types
  376. #
  377. class DeviceTypeListView(generic.ObjectListView):
  378. queryset = DeviceType.objects.prefetch_related('manufacturer').annotate(
  379. instance_count=count_related(Device, 'device_type')
  380. )
  381. filterset = filters.DeviceTypeFilterSet
  382. filterset_form = forms.DeviceTypeFilterForm
  383. table = tables.DeviceTypeTable
  384. class DeviceTypeView(generic.ObjectView):
  385. queryset = DeviceType.objects.prefetch_related('manufacturer')
  386. def get_extra_context(self, request, instance):
  387. instance_count = Device.objects.restrict(request.user).filter(device_type=instance).count()
  388. # Component tables
  389. consoleport_table = tables.ConsolePortTemplateTable(
  390. ConsolePortTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
  391. orderable=False
  392. )
  393. consoleserverport_table = tables.ConsoleServerPortTemplateTable(
  394. ConsoleServerPortTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
  395. orderable=False
  396. )
  397. powerport_table = tables.PowerPortTemplateTable(
  398. PowerPortTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
  399. orderable=False
  400. )
  401. poweroutlet_table = tables.PowerOutletTemplateTable(
  402. PowerOutletTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
  403. orderable=False
  404. )
  405. interface_table = tables.InterfaceTemplateTable(
  406. list(InterfaceTemplate.objects.restrict(request.user, 'view').filter(device_type=instance)),
  407. orderable=False
  408. )
  409. front_port_table = tables.FrontPortTemplateTable(
  410. FrontPortTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
  411. orderable=False
  412. )
  413. rear_port_table = tables.RearPortTemplateTable(
  414. RearPortTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
  415. orderable=False
  416. )
  417. devicebay_table = tables.DeviceBayTemplateTable(
  418. DeviceBayTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
  419. orderable=False
  420. )
  421. if request.user.has_perm('dcim.change_devicetype'):
  422. consoleport_table.columns.show('pk')
  423. consoleserverport_table.columns.show('pk')
  424. powerport_table.columns.show('pk')
  425. poweroutlet_table.columns.show('pk')
  426. interface_table.columns.show('pk')
  427. front_port_table.columns.show('pk')
  428. rear_port_table.columns.show('pk')
  429. devicebay_table.columns.show('pk')
  430. return {
  431. 'instance_count': instance_count,
  432. 'consoleport_table': consoleport_table,
  433. 'consoleserverport_table': consoleserverport_table,
  434. 'powerport_table': powerport_table,
  435. 'poweroutlet_table': poweroutlet_table,
  436. 'interface_table': interface_table,
  437. 'front_port_table': front_port_table,
  438. 'rear_port_table': rear_port_table,
  439. 'devicebay_table': devicebay_table,
  440. }
  441. class DeviceTypeEditView(generic.ObjectEditView):
  442. queryset = DeviceType.objects.all()
  443. model_form = forms.DeviceTypeForm
  444. template_name = 'dcim/devicetype_edit.html'
  445. class DeviceTypeDeleteView(generic.ObjectDeleteView):
  446. queryset = DeviceType.objects.all()
  447. class DeviceTypeImportView(generic.ObjectImportView):
  448. additional_permissions = [
  449. 'dcim.add_devicetype',
  450. 'dcim.add_consoleporttemplate',
  451. 'dcim.add_consoleserverporttemplate',
  452. 'dcim.add_powerporttemplate',
  453. 'dcim.add_poweroutlettemplate',
  454. 'dcim.add_interfacetemplate',
  455. 'dcim.add_frontporttemplate',
  456. 'dcim.add_rearporttemplate',
  457. 'dcim.add_devicebaytemplate',
  458. ]
  459. queryset = DeviceType.objects.all()
  460. model_form = forms.DeviceTypeImportForm
  461. related_object_forms = OrderedDict((
  462. ('console-ports', forms.ConsolePortTemplateImportForm),
  463. ('console-server-ports', forms.ConsoleServerPortTemplateImportForm),
  464. ('power-ports', forms.PowerPortTemplateImportForm),
  465. ('power-outlets', forms.PowerOutletTemplateImportForm),
  466. ('interfaces', forms.InterfaceTemplateImportForm),
  467. ('rear-ports', forms.RearPortTemplateImportForm),
  468. ('front-ports', forms.FrontPortTemplateImportForm),
  469. ('device-bays', forms.DeviceBayTemplateImportForm),
  470. ))
  471. class DeviceTypeBulkEditView(generic.BulkEditView):
  472. queryset = DeviceType.objects.prefetch_related('manufacturer').annotate(
  473. instance_count=count_related(Device, 'device_type')
  474. )
  475. filterset = filters.DeviceTypeFilterSet
  476. table = tables.DeviceTypeTable
  477. form = forms.DeviceTypeBulkEditForm
  478. class DeviceTypeBulkDeleteView(generic.BulkDeleteView):
  479. queryset = DeviceType.objects.prefetch_related('manufacturer').annotate(
  480. instance_count=count_related(Device, 'device_type')
  481. )
  482. filterset = filters.DeviceTypeFilterSet
  483. table = tables.DeviceTypeTable
  484. #
  485. # Console port templates
  486. #
  487. class ConsolePortTemplateCreateView(generic.ComponentCreateView):
  488. queryset = ConsolePortTemplate.objects.all()
  489. form = forms.ConsolePortTemplateCreateForm
  490. model_form = forms.ConsolePortTemplateForm
  491. template_name = 'dcim/device_component_add.html'
  492. class ConsolePortTemplateEditView(generic.ObjectEditView):
  493. queryset = ConsolePortTemplate.objects.all()
  494. model_form = forms.ConsolePortTemplateForm
  495. class ConsolePortTemplateDeleteView(generic.ObjectDeleteView):
  496. queryset = ConsolePortTemplate.objects.all()
  497. class ConsolePortTemplateBulkEditView(generic.BulkEditView):
  498. queryset = ConsolePortTemplate.objects.all()
  499. table = tables.ConsolePortTemplateTable
  500. form = forms.ConsolePortTemplateBulkEditForm
  501. class ConsolePortTemplateBulkRenameView(generic.BulkRenameView):
  502. queryset = ConsolePortTemplate.objects.all()
  503. class ConsolePortTemplateBulkDeleteView(generic.BulkDeleteView):
  504. queryset = ConsolePortTemplate.objects.all()
  505. table = tables.ConsolePortTemplateTable
  506. #
  507. # Console server port templates
  508. #
  509. class ConsoleServerPortTemplateCreateView(generic.ComponentCreateView):
  510. queryset = ConsoleServerPortTemplate.objects.all()
  511. form = forms.ConsoleServerPortTemplateCreateForm
  512. model_form = forms.ConsoleServerPortTemplateForm
  513. template_name = 'dcim/device_component_add.html'
  514. class ConsoleServerPortTemplateEditView(generic.ObjectEditView):
  515. queryset = ConsoleServerPortTemplate.objects.all()
  516. model_form = forms.ConsoleServerPortTemplateForm
  517. class ConsoleServerPortTemplateDeleteView(generic.ObjectDeleteView):
  518. queryset = ConsoleServerPortTemplate.objects.all()
  519. class ConsoleServerPortTemplateBulkEditView(generic.BulkEditView):
  520. queryset = ConsoleServerPortTemplate.objects.all()
  521. table = tables.ConsoleServerPortTemplateTable
  522. form = forms.ConsoleServerPortTemplateBulkEditForm
  523. class ConsoleServerPortTemplateBulkRenameView(generic.BulkRenameView):
  524. queryset = ConsoleServerPortTemplate.objects.all()
  525. class ConsoleServerPortTemplateBulkDeleteView(generic.BulkDeleteView):
  526. queryset = ConsoleServerPortTemplate.objects.all()
  527. table = tables.ConsoleServerPortTemplateTable
  528. #
  529. # Power port templates
  530. #
  531. class PowerPortTemplateCreateView(generic.ComponentCreateView):
  532. queryset = PowerPortTemplate.objects.all()
  533. form = forms.PowerPortTemplateCreateForm
  534. model_form = forms.PowerPortTemplateForm
  535. template_name = 'dcim/device_component_add.html'
  536. class PowerPortTemplateEditView(generic.ObjectEditView):
  537. queryset = PowerPortTemplate.objects.all()
  538. model_form = forms.PowerPortTemplateForm
  539. class PowerPortTemplateDeleteView(generic.ObjectDeleteView):
  540. queryset = PowerPortTemplate.objects.all()
  541. class PowerPortTemplateBulkEditView(generic.BulkEditView):
  542. queryset = PowerPortTemplate.objects.all()
  543. table = tables.PowerPortTemplateTable
  544. form = forms.PowerPortTemplateBulkEditForm
  545. class PowerPortTemplateBulkRenameView(generic.BulkRenameView):
  546. queryset = PowerPortTemplate.objects.all()
  547. class PowerPortTemplateBulkDeleteView(generic.BulkDeleteView):
  548. queryset = PowerPortTemplate.objects.all()
  549. table = tables.PowerPortTemplateTable
  550. #
  551. # Power outlet templates
  552. #
  553. class PowerOutletTemplateCreateView(generic.ComponentCreateView):
  554. queryset = PowerOutletTemplate.objects.all()
  555. form = forms.PowerOutletTemplateCreateForm
  556. model_form = forms.PowerOutletTemplateForm
  557. template_name = 'dcim/device_component_add.html'
  558. class PowerOutletTemplateEditView(generic.ObjectEditView):
  559. queryset = PowerOutletTemplate.objects.all()
  560. model_form = forms.PowerOutletTemplateForm
  561. class PowerOutletTemplateDeleteView(generic.ObjectDeleteView):
  562. queryset = PowerOutletTemplate.objects.all()
  563. class PowerOutletTemplateBulkEditView(generic.BulkEditView):
  564. queryset = PowerOutletTemplate.objects.all()
  565. table = tables.PowerOutletTemplateTable
  566. form = forms.PowerOutletTemplateBulkEditForm
  567. class PowerOutletTemplateBulkRenameView(generic.BulkRenameView):
  568. queryset = PowerOutletTemplate.objects.all()
  569. class PowerOutletTemplateBulkDeleteView(generic.BulkDeleteView):
  570. queryset = PowerOutletTemplate.objects.all()
  571. table = tables.PowerOutletTemplateTable
  572. #
  573. # Interface templates
  574. #
  575. class InterfaceTemplateCreateView(generic.ComponentCreateView):
  576. queryset = InterfaceTemplate.objects.all()
  577. form = forms.InterfaceTemplateCreateForm
  578. model_form = forms.InterfaceTemplateForm
  579. template_name = 'dcim/device_component_add.html'
  580. class InterfaceTemplateEditView(generic.ObjectEditView):
  581. queryset = InterfaceTemplate.objects.all()
  582. model_form = forms.InterfaceTemplateForm
  583. class InterfaceTemplateDeleteView(generic.ObjectDeleteView):
  584. queryset = InterfaceTemplate.objects.all()
  585. class InterfaceTemplateBulkEditView(generic.BulkEditView):
  586. queryset = InterfaceTemplate.objects.all()
  587. table = tables.InterfaceTemplateTable
  588. form = forms.InterfaceTemplateBulkEditForm
  589. class InterfaceTemplateBulkRenameView(generic.BulkRenameView):
  590. queryset = InterfaceTemplate.objects.all()
  591. class InterfaceTemplateBulkDeleteView(generic.BulkDeleteView):
  592. queryset = InterfaceTemplate.objects.all()
  593. table = tables.InterfaceTemplateTable
  594. #
  595. # Front port templates
  596. #
  597. class FrontPortTemplateCreateView(generic.ComponentCreateView):
  598. queryset = FrontPortTemplate.objects.all()
  599. form = forms.FrontPortTemplateCreateForm
  600. model_form = forms.FrontPortTemplateForm
  601. template_name = 'dcim/device_component_add.html'
  602. class FrontPortTemplateEditView(generic.ObjectEditView):
  603. queryset = FrontPortTemplate.objects.all()
  604. model_form = forms.FrontPortTemplateForm
  605. class FrontPortTemplateDeleteView(generic.ObjectDeleteView):
  606. queryset = FrontPortTemplate.objects.all()
  607. class FrontPortTemplateBulkEditView(generic.BulkEditView):
  608. queryset = FrontPortTemplate.objects.all()
  609. table = tables.FrontPortTemplateTable
  610. form = forms.FrontPortTemplateBulkEditForm
  611. class FrontPortTemplateBulkRenameView(generic.BulkRenameView):
  612. queryset = FrontPortTemplate.objects.all()
  613. class FrontPortTemplateBulkDeleteView(generic.BulkDeleteView):
  614. queryset = FrontPortTemplate.objects.all()
  615. table = tables.FrontPortTemplateTable
  616. #
  617. # Rear port templates
  618. #
  619. class RearPortTemplateCreateView(generic.ComponentCreateView):
  620. queryset = RearPortTemplate.objects.all()
  621. form = forms.RearPortTemplateCreateForm
  622. model_form = forms.RearPortTemplateForm
  623. template_name = 'dcim/device_component_add.html'
  624. class RearPortTemplateEditView(generic.ObjectEditView):
  625. queryset = RearPortTemplate.objects.all()
  626. model_form = forms.RearPortTemplateForm
  627. class RearPortTemplateDeleteView(generic.ObjectDeleteView):
  628. queryset = RearPortTemplate.objects.all()
  629. class RearPortTemplateBulkEditView(generic.BulkEditView):
  630. queryset = RearPortTemplate.objects.all()
  631. table = tables.RearPortTemplateTable
  632. form = forms.RearPortTemplateBulkEditForm
  633. class RearPortTemplateBulkRenameView(generic.BulkRenameView):
  634. queryset = RearPortTemplate.objects.all()
  635. class RearPortTemplateBulkDeleteView(generic.BulkDeleteView):
  636. queryset = RearPortTemplate.objects.all()
  637. table = tables.RearPortTemplateTable
  638. #
  639. # Device bay templates
  640. #
  641. class DeviceBayTemplateCreateView(generic.ComponentCreateView):
  642. queryset = DeviceBayTemplate.objects.all()
  643. form = forms.DeviceBayTemplateCreateForm
  644. model_form = forms.DeviceBayTemplateForm
  645. template_name = 'dcim/device_component_add.html'
  646. class DeviceBayTemplateEditView(generic.ObjectEditView):
  647. queryset = DeviceBayTemplate.objects.all()
  648. model_form = forms.DeviceBayTemplateForm
  649. class DeviceBayTemplateDeleteView(generic.ObjectDeleteView):
  650. queryset = DeviceBayTemplate.objects.all()
  651. class DeviceBayTemplateBulkEditView(generic.BulkEditView):
  652. queryset = DeviceBayTemplate.objects.all()
  653. table = tables.DeviceBayTemplateTable
  654. form = forms.DeviceBayTemplateBulkEditForm
  655. class DeviceBayTemplateBulkRenameView(generic.BulkRenameView):
  656. queryset = DeviceBayTemplate.objects.all()
  657. class DeviceBayTemplateBulkDeleteView(generic.BulkDeleteView):
  658. queryset = DeviceBayTemplate.objects.all()
  659. table = tables.DeviceBayTemplateTable
  660. #
  661. # Device roles
  662. #
  663. class DeviceRoleListView(generic.ObjectListView):
  664. queryset = DeviceRole.objects.annotate(
  665. device_count=count_related(Device, 'device_role'),
  666. vm_count=count_related(VirtualMachine, 'role')
  667. )
  668. table = tables.DeviceRoleTable
  669. class DeviceRoleEditView(generic.ObjectEditView):
  670. queryset = DeviceRole.objects.all()
  671. model_form = forms.DeviceRoleForm
  672. class DeviceRoleDeleteView(generic.ObjectDeleteView):
  673. queryset = DeviceRole.objects.all()
  674. class DeviceRoleBulkImportView(generic.BulkImportView):
  675. queryset = DeviceRole.objects.all()
  676. model_form = forms.DeviceRoleCSVForm
  677. table = tables.DeviceRoleTable
  678. class DeviceRoleBulkDeleteView(generic.BulkDeleteView):
  679. queryset = DeviceRole.objects.all()
  680. table = tables.DeviceRoleTable
  681. #
  682. # Platforms
  683. #
  684. class PlatformListView(generic.ObjectListView):
  685. queryset = Platform.objects.annotate(
  686. device_count=count_related(Device, 'platform'),
  687. vm_count=count_related(VirtualMachine, 'platform')
  688. )
  689. table = tables.PlatformTable
  690. class PlatformEditView(generic.ObjectEditView):
  691. queryset = Platform.objects.all()
  692. model_form = forms.PlatformForm
  693. class PlatformDeleteView(generic.ObjectDeleteView):
  694. queryset = Platform.objects.all()
  695. class PlatformBulkImportView(generic.BulkImportView):
  696. queryset = Platform.objects.all()
  697. model_form = forms.PlatformCSVForm
  698. table = tables.PlatformTable
  699. class PlatformBulkDeleteView(generic.BulkDeleteView):
  700. queryset = Platform.objects.all()
  701. table = tables.PlatformTable
  702. #
  703. # Devices
  704. #
  705. class DeviceListView(generic.ObjectListView):
  706. queryset = Device.objects.all()
  707. filterset = filters.DeviceFilterSet
  708. filterset_form = forms.DeviceFilterForm
  709. table = tables.DeviceTable
  710. template_name = 'dcim/device_list.html'
  711. class DeviceView(generic.ObjectView):
  712. queryset = Device.objects.prefetch_related(
  713. 'site__region', 'rack__group', 'tenant__group', 'device_role', 'platform', 'primary_ip4', 'primary_ip6'
  714. )
  715. def get_extra_context(self, request, instance):
  716. # VirtualChassis members
  717. if instance.virtual_chassis is not None:
  718. vc_members = Device.objects.restrict(request.user, 'view').filter(
  719. virtual_chassis=instance.virtual_chassis
  720. ).order_by('vc_position')
  721. else:
  722. vc_members = []
  723. # Services
  724. services = Service.objects.restrict(request.user, 'view').filter(device=instance)
  725. # Secrets
  726. secrets = Secret.objects.restrict(request.user, 'view').filter(device=instance)
  727. # Find up to ten devices in the same site with the same functional role for quick reference.
  728. related_devices = Device.objects.restrict(request.user, 'view').filter(
  729. site=instance.site, device_role=instance.device_role
  730. ).exclude(
  731. pk=instance.pk
  732. ).prefetch_related(
  733. 'rack', 'device_type__manufacturer'
  734. )[:10]
  735. return {
  736. 'services': services,
  737. 'secrets': secrets,
  738. 'vc_members': vc_members,
  739. 'related_devices': related_devices,
  740. 'active_tab': 'device',
  741. }
  742. class DeviceConsolePortsView(generic.ObjectView):
  743. queryset = Device.objects.all()
  744. template_name = 'dcim/device/consoleports.html'
  745. def get_extra_context(self, request, instance):
  746. consoleports = ConsolePort.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related(
  747. 'cable', '_path__destination',
  748. )
  749. consoleport_table = tables.DeviceConsolePortTable(
  750. data=consoleports,
  751. user=request.user,
  752. orderable=False
  753. )
  754. if request.user.has_perm('dcim.change_consoleport') or request.user.has_perm('dcim.delete_consoleport'):
  755. consoleport_table.columns.show('pk')
  756. return {
  757. 'consoleport_table': consoleport_table,
  758. 'active_tab': 'console-ports',
  759. }
  760. class DeviceConsoleServerPortsView(generic.ObjectView):
  761. queryset = Device.objects.all()
  762. template_name = 'dcim/device/consoleserverports.html'
  763. def get_extra_context(self, request, instance):
  764. consoleserverports = ConsoleServerPort.objects.restrict(request.user, 'view').filter(
  765. device=instance
  766. ).prefetch_related(
  767. 'cable', '_path__destination',
  768. )
  769. consoleserverport_table = tables.DeviceConsoleServerPortTable(
  770. data=consoleserverports,
  771. user=request.user,
  772. orderable=False
  773. )
  774. if request.user.has_perm('dcim.change_consoleserverport') or \
  775. request.user.has_perm('dcim.delete_consoleserverport'):
  776. consoleserverport_table.columns.show('pk')
  777. return {
  778. 'consoleserverport_table': consoleserverport_table,
  779. 'active_tab': 'console-server-ports',
  780. }
  781. class DevicePowerPortsView(generic.ObjectView):
  782. queryset = Device.objects.all()
  783. template_name = 'dcim/device/powerports.html'
  784. def get_extra_context(self, request, instance):
  785. powerports = PowerPort.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related(
  786. 'cable', '_path__destination',
  787. )
  788. powerport_table = tables.DevicePowerPortTable(
  789. data=powerports,
  790. user=request.user,
  791. orderable=False
  792. )
  793. if request.user.has_perm('dcim.change_powerport') or request.user.has_perm('dcim.delete_powerport'):
  794. powerport_table.columns.show('pk')
  795. return {
  796. 'powerport_table': powerport_table,
  797. 'active_tab': 'power-ports',
  798. }
  799. class DevicePowerOutletsView(generic.ObjectView):
  800. queryset = Device.objects.all()
  801. template_name = 'dcim/device/poweroutlets.html'
  802. def get_extra_context(self, request, instance):
  803. poweroutlets = PowerOutlet.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related(
  804. 'cable', 'power_port', '_path__destination',
  805. )
  806. poweroutlet_table = tables.DevicePowerOutletTable(
  807. data=poweroutlets,
  808. user=request.user,
  809. orderable=False
  810. )
  811. if request.user.has_perm('dcim.change_poweroutlet') or request.user.has_perm('dcim.delete_poweroutlet'):
  812. poweroutlet_table.columns.show('pk')
  813. return {
  814. 'poweroutlet_table': poweroutlet_table,
  815. 'active_tab': 'power-outlets',
  816. }
  817. class DeviceInterfacesView(generic.ObjectView):
  818. queryset = Device.objects.all()
  819. template_name = 'dcim/device/interfaces.html'
  820. def get_extra_context(self, request, instance):
  821. interfaces = instance.vc_interfaces.restrict(request.user, 'view').prefetch_related(
  822. Prefetch('ip_addresses', queryset=IPAddress.objects.restrict(request.user)),
  823. Prefetch('member_interfaces', queryset=Interface.objects.restrict(request.user)),
  824. 'lag', 'cable', '_path__destination', 'tags',
  825. )
  826. interface_table = tables.DeviceInterfaceTable(
  827. data=interfaces,
  828. user=request.user,
  829. orderable=False
  830. )
  831. if request.user.has_perm('dcim.change_interface') or request.user.has_perm('dcim.delete_interface'):
  832. interface_table.columns.show('pk')
  833. return {
  834. 'interface_table': interface_table,
  835. 'active_tab': 'interfaces',
  836. }
  837. class DeviceFrontPortsView(generic.ObjectView):
  838. queryset = Device.objects.all()
  839. template_name = 'dcim/device/frontports.html'
  840. def get_extra_context(self, request, instance):
  841. frontports = FrontPort.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related(
  842. 'rear_port', 'cable',
  843. )
  844. frontport_table = tables.DeviceFrontPortTable(
  845. data=frontports,
  846. user=request.user,
  847. orderable=False
  848. )
  849. if request.user.has_perm('dcim.change_frontport') or request.user.has_perm('dcim.delete_frontport'):
  850. frontport_table.columns.show('pk')
  851. return {
  852. 'frontport_table': frontport_table,
  853. 'active_tab': 'front-ports',
  854. }
  855. class DeviceRearPortsView(generic.ObjectView):
  856. queryset = Device.objects.all()
  857. template_name = 'dcim/device/rearports.html'
  858. def get_extra_context(self, request, instance):
  859. rearports = RearPort.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related('cable')
  860. rearport_table = tables.DeviceRearPortTable(
  861. data=rearports,
  862. user=request.user,
  863. orderable=False
  864. )
  865. if request.user.has_perm('dcim.change_rearport') or request.user.has_perm('dcim.delete_rearport'):
  866. rearport_table.columns.show('pk')
  867. return {
  868. 'rearport_table': rearport_table,
  869. 'active_tab': 'rear-ports',
  870. }
  871. class DeviceDeviceBaysView(generic.ObjectView):
  872. queryset = Device.objects.all()
  873. template_name = 'dcim/device/devicebays.html'
  874. def get_extra_context(self, request, instance):
  875. devicebays = DeviceBay.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related(
  876. 'installed_device__device_type__manufacturer',
  877. )
  878. devicebay_table = tables.DeviceDeviceBayTable(
  879. data=devicebays,
  880. user=request.user,
  881. orderable=False
  882. )
  883. if request.user.has_perm('dcim.change_devicebay') or request.user.has_perm('dcim.delete_devicebay'):
  884. devicebay_table.columns.show('pk')
  885. return {
  886. 'devicebay_table': devicebay_table,
  887. 'active_tab': 'device-bays',
  888. }
  889. class DeviceInventoryView(generic.ObjectView):
  890. queryset = Device.objects.all()
  891. template_name = 'dcim/device/inventory.html'
  892. def get_extra_context(self, request, instance):
  893. inventoryitems = InventoryItem.objects.restrict(request.user, 'view').filter(
  894. device=instance
  895. ).prefetch_related('manufacturer')
  896. inventoryitem_table = tables.DeviceInventoryItemTable(
  897. data=inventoryitems,
  898. user=request.user,
  899. orderable=False
  900. )
  901. if request.user.has_perm('dcim.change_inventoryitem') or request.user.has_perm('dcim.delete_inventoryitem'):
  902. inventoryitem_table.columns.show('pk')
  903. return {
  904. 'inventoryitem_table': inventoryitem_table,
  905. 'active_tab': 'inventory',
  906. }
  907. class DeviceStatusView(generic.ObjectView):
  908. additional_permissions = ['dcim.napalm_read_device']
  909. queryset = Device.objects.all()
  910. template_name = 'dcim/device/status.html'
  911. def get_extra_context(self, request, instance):
  912. return {
  913. 'active_tab': 'status',
  914. }
  915. class DeviceLLDPNeighborsView(generic.ObjectView):
  916. additional_permissions = ['dcim.napalm_read_device']
  917. queryset = Device.objects.all()
  918. template_name = 'dcim/device/lldp_neighbors.html'
  919. def get_extra_context(self, request, instance):
  920. interfaces = instance.vc_interfaces.restrict(request.user, 'view').prefetch_related(
  921. '_path__destination'
  922. ).exclude(
  923. type__in=NONCONNECTABLE_IFACE_TYPES
  924. )
  925. return {
  926. 'interfaces': interfaces,
  927. 'active_tab': 'lldp-neighbors',
  928. }
  929. class DeviceConfigView(generic.ObjectView):
  930. additional_permissions = ['dcim.napalm_read_device']
  931. queryset = Device.objects.all()
  932. template_name = 'dcim/device/config.html'
  933. def get_extra_context(self, request, instance):
  934. return {
  935. 'active_tab': 'config',
  936. }
  937. class DeviceConfigContextView(ObjectConfigContextView):
  938. queryset = Device.objects.annotate_config_context_data()
  939. base_template = 'dcim/device/base.html'
  940. class DeviceChangeLogView(ObjectChangeLogView):
  941. base_template = 'dcim/device/base.html'
  942. class DeviceEditView(generic.ObjectEditView):
  943. queryset = Device.objects.all()
  944. model_form = forms.DeviceForm
  945. template_name = 'dcim/device_edit.html'
  946. class DeviceDeleteView(generic.ObjectDeleteView):
  947. queryset = Device.objects.all()
  948. class DeviceBulkImportView(generic.BulkImportView):
  949. queryset = Device.objects.all()
  950. model_form = forms.DeviceCSVForm
  951. table = tables.DeviceImportTable
  952. template_name = 'dcim/device_import.html'
  953. class ChildDeviceBulkImportView(generic.BulkImportView):
  954. queryset = Device.objects.all()
  955. model_form = forms.ChildDeviceCSVForm
  956. table = tables.DeviceImportTable
  957. template_name = 'dcim/device_import_child.html'
  958. def _save_obj(self, obj_form, request):
  959. obj = obj_form.save()
  960. # Save the reverse relation to the parent device bay
  961. device_bay = obj.parent_bay
  962. device_bay.installed_device = obj
  963. device_bay.save()
  964. return obj
  965. class DeviceBulkEditView(generic.BulkEditView):
  966. queryset = Device.objects.prefetch_related('tenant', 'site', 'rack', 'device_role', 'device_type__manufacturer')
  967. filterset = filters.DeviceFilterSet
  968. table = tables.DeviceTable
  969. form = forms.DeviceBulkEditForm
  970. class DeviceBulkDeleteView(generic.BulkDeleteView):
  971. queryset = Device.objects.prefetch_related('tenant', 'site', 'rack', 'device_role', 'device_type__manufacturer')
  972. filterset = filters.DeviceFilterSet
  973. table = tables.DeviceTable
  974. #
  975. # Console ports
  976. #
  977. class ConsolePortListView(generic.ObjectListView):
  978. queryset = ConsolePort.objects.all()
  979. filterset = filters.ConsolePortFilterSet
  980. filterset_form = forms.ConsolePortFilterForm
  981. table = tables.ConsolePortTable
  982. action_buttons = ('import', 'export')
  983. class ConsolePortView(generic.ObjectView):
  984. queryset = ConsolePort.objects.all()
  985. def get_extra_context(self, request, instance):
  986. return {
  987. 'breadcrumb_url': 'dcim:device_consoleports'
  988. }
  989. class ConsolePortCreateView(generic.ComponentCreateView):
  990. queryset = ConsolePort.objects.all()
  991. form = forms.ConsolePortCreateForm
  992. model_form = forms.ConsolePortForm
  993. template_name = 'dcim/device_component_add.html'
  994. class ConsolePortEditView(generic.ObjectEditView):
  995. queryset = ConsolePort.objects.all()
  996. model_form = forms.ConsolePortForm
  997. template_name = 'dcim/device_component_edit.html'
  998. class ConsolePortDeleteView(generic.ObjectDeleteView):
  999. queryset = ConsolePort.objects.all()
  1000. class ConsolePortBulkImportView(generic.BulkImportView):
  1001. queryset = ConsolePort.objects.all()
  1002. model_form = forms.ConsolePortCSVForm
  1003. table = tables.ConsolePortTable
  1004. class ConsolePortBulkEditView(generic.BulkEditView):
  1005. queryset = ConsolePort.objects.all()
  1006. filterset = filters.ConsolePortFilterSet
  1007. table = tables.ConsolePortTable
  1008. form = forms.ConsolePortBulkEditForm
  1009. class ConsolePortBulkRenameView(generic.BulkRenameView):
  1010. queryset = ConsolePort.objects.all()
  1011. class ConsolePortBulkDisconnectView(BulkDisconnectView):
  1012. queryset = ConsolePort.objects.all()
  1013. class ConsolePortBulkDeleteView(generic.BulkDeleteView):
  1014. queryset = ConsolePort.objects.all()
  1015. filterset = filters.ConsolePortFilterSet
  1016. table = tables.ConsolePortTable
  1017. #
  1018. # Console server ports
  1019. #
  1020. class ConsoleServerPortListView(generic.ObjectListView):
  1021. queryset = ConsoleServerPort.objects.all()
  1022. filterset = filters.ConsoleServerPortFilterSet
  1023. filterset_form = forms.ConsoleServerPortFilterForm
  1024. table = tables.ConsoleServerPortTable
  1025. action_buttons = ('import', 'export')
  1026. class ConsoleServerPortView(generic.ObjectView):
  1027. queryset = ConsoleServerPort.objects.all()
  1028. def get_extra_context(self, request, instance):
  1029. return {
  1030. 'breadcrumb_url': 'dcim:device_consoleserverports'
  1031. }
  1032. class ConsoleServerPortCreateView(generic.ComponentCreateView):
  1033. queryset = ConsoleServerPort.objects.all()
  1034. form = forms.ConsoleServerPortCreateForm
  1035. model_form = forms.ConsoleServerPortForm
  1036. template_name = 'dcim/device_component_add.html'
  1037. class ConsoleServerPortEditView(generic.ObjectEditView):
  1038. queryset = ConsoleServerPort.objects.all()
  1039. model_form = forms.ConsoleServerPortForm
  1040. template_name = 'dcim/device_component_edit.html'
  1041. class ConsoleServerPortDeleteView(generic.ObjectDeleteView):
  1042. queryset = ConsoleServerPort.objects.all()
  1043. class ConsoleServerPortBulkImportView(generic.BulkImportView):
  1044. queryset = ConsoleServerPort.objects.all()
  1045. model_form = forms.ConsoleServerPortCSVForm
  1046. table = tables.ConsoleServerPortTable
  1047. class ConsoleServerPortBulkEditView(generic.BulkEditView):
  1048. queryset = ConsoleServerPort.objects.all()
  1049. filterset = filters.ConsoleServerPortFilterSet
  1050. table = tables.ConsoleServerPortTable
  1051. form = forms.ConsoleServerPortBulkEditForm
  1052. class ConsoleServerPortBulkRenameView(generic.BulkRenameView):
  1053. queryset = ConsoleServerPort.objects.all()
  1054. class ConsoleServerPortBulkDisconnectView(BulkDisconnectView):
  1055. queryset = ConsoleServerPort.objects.all()
  1056. class ConsoleServerPortBulkDeleteView(generic.BulkDeleteView):
  1057. queryset = ConsoleServerPort.objects.all()
  1058. filterset = filters.ConsoleServerPortFilterSet
  1059. table = tables.ConsoleServerPortTable
  1060. #
  1061. # Power ports
  1062. #
  1063. class PowerPortListView(generic.ObjectListView):
  1064. queryset = PowerPort.objects.all()
  1065. filterset = filters.PowerPortFilterSet
  1066. filterset_form = forms.PowerPortFilterForm
  1067. table = tables.PowerPortTable
  1068. action_buttons = ('import', 'export')
  1069. class PowerPortView(generic.ObjectView):
  1070. queryset = PowerPort.objects.all()
  1071. def get_extra_context(self, request, instance):
  1072. return {
  1073. 'breadcrumb_url': 'dcim:device_powerports'
  1074. }
  1075. class PowerPortCreateView(generic.ComponentCreateView):
  1076. queryset = PowerPort.objects.all()
  1077. form = forms.PowerPortCreateForm
  1078. model_form = forms.PowerPortForm
  1079. template_name = 'dcim/device_component_add.html'
  1080. class PowerPortEditView(generic.ObjectEditView):
  1081. queryset = PowerPort.objects.all()
  1082. model_form = forms.PowerPortForm
  1083. template_name = 'dcim/device_component_edit.html'
  1084. class PowerPortDeleteView(generic.ObjectDeleteView):
  1085. queryset = PowerPort.objects.all()
  1086. class PowerPortBulkImportView(generic.BulkImportView):
  1087. queryset = PowerPort.objects.all()
  1088. model_form = forms.PowerPortCSVForm
  1089. table = tables.PowerPortTable
  1090. class PowerPortBulkEditView(generic.BulkEditView):
  1091. queryset = PowerPort.objects.all()
  1092. filterset = filters.PowerPortFilterSet
  1093. table = tables.PowerPortTable
  1094. form = forms.PowerPortBulkEditForm
  1095. class PowerPortBulkRenameView(generic.BulkRenameView):
  1096. queryset = PowerPort.objects.all()
  1097. class PowerPortBulkDisconnectView(BulkDisconnectView):
  1098. queryset = PowerPort.objects.all()
  1099. class PowerPortBulkDeleteView(generic.BulkDeleteView):
  1100. queryset = PowerPort.objects.all()
  1101. filterset = filters.PowerPortFilterSet
  1102. table = tables.PowerPortTable
  1103. #
  1104. # Power outlets
  1105. #
  1106. class PowerOutletListView(generic.ObjectListView):
  1107. queryset = PowerOutlet.objects.all()
  1108. filterset = filters.PowerOutletFilterSet
  1109. filterset_form = forms.PowerOutletFilterForm
  1110. table = tables.PowerOutletTable
  1111. action_buttons = ('import', 'export')
  1112. class PowerOutletView(generic.ObjectView):
  1113. queryset = PowerOutlet.objects.all()
  1114. def get_extra_context(self, request, instance):
  1115. return {
  1116. 'breadcrumb_url': 'dcim:device_poweroutlets'
  1117. }
  1118. class PowerOutletCreateView(generic.ComponentCreateView):
  1119. queryset = PowerOutlet.objects.all()
  1120. form = forms.PowerOutletCreateForm
  1121. model_form = forms.PowerOutletForm
  1122. template_name = 'dcim/device_component_add.html'
  1123. class PowerOutletEditView(generic.ObjectEditView):
  1124. queryset = PowerOutlet.objects.all()
  1125. model_form = forms.PowerOutletForm
  1126. template_name = 'dcim/device_component_edit.html'
  1127. class PowerOutletDeleteView(generic.ObjectDeleteView):
  1128. queryset = PowerOutlet.objects.all()
  1129. class PowerOutletBulkImportView(generic.BulkImportView):
  1130. queryset = PowerOutlet.objects.all()
  1131. model_form = forms.PowerOutletCSVForm
  1132. table = tables.PowerOutletTable
  1133. class PowerOutletBulkEditView(generic.BulkEditView):
  1134. queryset = PowerOutlet.objects.all()
  1135. filterset = filters.PowerOutletFilterSet
  1136. table = tables.PowerOutletTable
  1137. form = forms.PowerOutletBulkEditForm
  1138. class PowerOutletBulkRenameView(generic.BulkRenameView):
  1139. queryset = PowerOutlet.objects.all()
  1140. class PowerOutletBulkDisconnectView(BulkDisconnectView):
  1141. queryset = PowerOutlet.objects.all()
  1142. class PowerOutletBulkDeleteView(generic.BulkDeleteView):
  1143. queryset = PowerOutlet.objects.all()
  1144. filterset = filters.PowerOutletFilterSet
  1145. table = tables.PowerOutletTable
  1146. #
  1147. # Interfaces
  1148. #
  1149. class InterfaceListView(generic.ObjectListView):
  1150. queryset = Interface.objects.all()
  1151. filterset = filters.InterfaceFilterSet
  1152. filterset_form = forms.InterfaceFilterForm
  1153. table = tables.InterfaceTable
  1154. action_buttons = ('import', 'export')
  1155. class InterfaceView(generic.ObjectView):
  1156. queryset = Interface.objects.all()
  1157. def get_extra_context(self, request, instance):
  1158. # Get assigned IP addresses
  1159. ipaddress_table = InterfaceIPAddressTable(
  1160. data=instance.ip_addresses.restrict(request.user, 'view').prefetch_related('vrf', 'tenant'),
  1161. orderable=False
  1162. )
  1163. # Get assigned VLANs and annotate whether each is tagged or untagged
  1164. vlans = []
  1165. if instance.untagged_vlan is not None:
  1166. vlans.append(instance.untagged_vlan)
  1167. vlans[0].tagged = False
  1168. for vlan in instance.tagged_vlans.restrict(request.user).prefetch_related('site', 'group', 'tenant', 'role'):
  1169. vlan.tagged = True
  1170. vlans.append(vlan)
  1171. vlan_table = InterfaceVLANTable(
  1172. interface=instance,
  1173. data=vlans,
  1174. orderable=False
  1175. )
  1176. return {
  1177. 'ipaddress_table': ipaddress_table,
  1178. 'vlan_table': vlan_table,
  1179. 'breadcrumb_url': 'dcim:device_interfaces'
  1180. }
  1181. class InterfaceCreateView(generic.ComponentCreateView):
  1182. queryset = Interface.objects.all()
  1183. form = forms.InterfaceCreateForm
  1184. model_form = forms.InterfaceForm
  1185. template_name = 'dcim/device_component_add.html'
  1186. class InterfaceEditView(generic.ObjectEditView):
  1187. queryset = Interface.objects.all()
  1188. model_form = forms.InterfaceForm
  1189. template_name = 'dcim/interface_edit.html'
  1190. class InterfaceDeleteView(generic.ObjectDeleteView):
  1191. queryset = Interface.objects.all()
  1192. class InterfaceBulkImportView(generic.BulkImportView):
  1193. queryset = Interface.objects.all()
  1194. model_form = forms.InterfaceCSVForm
  1195. table = tables.InterfaceTable
  1196. class InterfaceBulkEditView(generic.BulkEditView):
  1197. queryset = Interface.objects.all()
  1198. filterset = filters.InterfaceFilterSet
  1199. table = tables.InterfaceTable
  1200. form = forms.InterfaceBulkEditForm
  1201. class InterfaceBulkRenameView(generic.BulkRenameView):
  1202. queryset = Interface.objects.all()
  1203. class InterfaceBulkDisconnectView(BulkDisconnectView):
  1204. queryset = Interface.objects.all()
  1205. class InterfaceBulkDeleteView(generic.BulkDeleteView):
  1206. queryset = Interface.objects.all()
  1207. filterset = filters.InterfaceFilterSet
  1208. table = tables.InterfaceTable
  1209. #
  1210. # Front ports
  1211. #
  1212. class FrontPortListView(generic.ObjectListView):
  1213. queryset = FrontPort.objects.all()
  1214. filterset = filters.FrontPortFilterSet
  1215. filterset_form = forms.FrontPortFilterForm
  1216. table = tables.FrontPortTable
  1217. action_buttons = ('import', 'export')
  1218. class FrontPortView(generic.ObjectView):
  1219. queryset = FrontPort.objects.all()
  1220. def get_extra_context(self, request, instance):
  1221. return {
  1222. 'breadcrumb_url': 'dcim:device_frontports'
  1223. }
  1224. class FrontPortCreateView(generic.ComponentCreateView):
  1225. queryset = FrontPort.objects.all()
  1226. form = forms.FrontPortCreateForm
  1227. model_form = forms.FrontPortForm
  1228. template_name = 'dcim/device_component_add.html'
  1229. class FrontPortEditView(generic.ObjectEditView):
  1230. queryset = FrontPort.objects.all()
  1231. model_form = forms.FrontPortForm
  1232. template_name = 'dcim/device_component_edit.html'
  1233. class FrontPortDeleteView(generic.ObjectDeleteView):
  1234. queryset = FrontPort.objects.all()
  1235. class FrontPortBulkImportView(generic.BulkImportView):
  1236. queryset = FrontPort.objects.all()
  1237. model_form = forms.FrontPortCSVForm
  1238. table = tables.FrontPortTable
  1239. class FrontPortBulkEditView(generic.BulkEditView):
  1240. queryset = FrontPort.objects.all()
  1241. filterset = filters.FrontPortFilterSet
  1242. table = tables.FrontPortTable
  1243. form = forms.FrontPortBulkEditForm
  1244. class FrontPortBulkRenameView(generic.BulkRenameView):
  1245. queryset = FrontPort.objects.all()
  1246. class FrontPortBulkDisconnectView(BulkDisconnectView):
  1247. queryset = FrontPort.objects.all()
  1248. class FrontPortBulkDeleteView(generic.BulkDeleteView):
  1249. queryset = FrontPort.objects.all()
  1250. filterset = filters.FrontPortFilterSet
  1251. table = tables.FrontPortTable
  1252. #
  1253. # Rear ports
  1254. #
  1255. class RearPortListView(generic.ObjectListView):
  1256. queryset = RearPort.objects.all()
  1257. filterset = filters.RearPortFilterSet
  1258. filterset_form = forms.RearPortFilterForm
  1259. table = tables.RearPortTable
  1260. action_buttons = ('import', 'export')
  1261. class RearPortView(generic.ObjectView):
  1262. queryset = RearPort.objects.all()
  1263. def get_extra_context(self, request, instance):
  1264. return {
  1265. 'breadcrumb_url': 'dcim:device_rearports'
  1266. }
  1267. class RearPortCreateView(generic.ComponentCreateView):
  1268. queryset = RearPort.objects.all()
  1269. form = forms.RearPortCreateForm
  1270. model_form = forms.RearPortForm
  1271. template_name = 'dcim/device_component_add.html'
  1272. class RearPortEditView(generic.ObjectEditView):
  1273. queryset = RearPort.objects.all()
  1274. model_form = forms.RearPortForm
  1275. template_name = 'dcim/device_component_edit.html'
  1276. class RearPortDeleteView(generic.ObjectDeleteView):
  1277. queryset = RearPort.objects.all()
  1278. class RearPortBulkImportView(generic.BulkImportView):
  1279. queryset = RearPort.objects.all()
  1280. model_form = forms.RearPortCSVForm
  1281. table = tables.RearPortTable
  1282. class RearPortBulkEditView(generic.BulkEditView):
  1283. queryset = RearPort.objects.all()
  1284. filterset = filters.RearPortFilterSet
  1285. table = tables.RearPortTable
  1286. form = forms.RearPortBulkEditForm
  1287. class RearPortBulkRenameView(generic.BulkRenameView):
  1288. queryset = RearPort.objects.all()
  1289. class RearPortBulkDisconnectView(BulkDisconnectView):
  1290. queryset = RearPort.objects.all()
  1291. class RearPortBulkDeleteView(generic.BulkDeleteView):
  1292. queryset = RearPort.objects.all()
  1293. filterset = filters.RearPortFilterSet
  1294. table = tables.RearPortTable
  1295. #
  1296. # Device bays
  1297. #
  1298. class DeviceBayListView(generic.ObjectListView):
  1299. queryset = DeviceBay.objects.all()
  1300. filterset = filters.DeviceBayFilterSet
  1301. filterset_form = forms.DeviceBayFilterForm
  1302. table = tables.DeviceBayTable
  1303. action_buttons = ('import', 'export')
  1304. class DeviceBayView(generic.ObjectView):
  1305. queryset = DeviceBay.objects.all()
  1306. def get_extra_context(self, request, instance):
  1307. return {
  1308. 'breadcrumb_url': 'dcim:device_devicebays'
  1309. }
  1310. class DeviceBayCreateView(generic.ComponentCreateView):
  1311. queryset = DeviceBay.objects.all()
  1312. form = forms.DeviceBayCreateForm
  1313. model_form = forms.DeviceBayForm
  1314. template_name = 'dcim/device_component_add.html'
  1315. class DeviceBayEditView(generic.ObjectEditView):
  1316. queryset = DeviceBay.objects.all()
  1317. model_form = forms.DeviceBayForm
  1318. template_name = 'dcim/device_component_edit.html'
  1319. class DeviceBayDeleteView(generic.ObjectDeleteView):
  1320. queryset = DeviceBay.objects.all()
  1321. class DeviceBayPopulateView(generic.ObjectEditView):
  1322. queryset = DeviceBay.objects.all()
  1323. def get(self, request, pk):
  1324. device_bay = get_object_or_404(self.queryset, pk=pk)
  1325. form = forms.PopulateDeviceBayForm(device_bay)
  1326. return render(request, 'dcim/devicebay_populate.html', {
  1327. 'device_bay': device_bay,
  1328. 'form': form,
  1329. 'return_url': self.get_return_url(request, device_bay),
  1330. })
  1331. def post(self, request, pk):
  1332. device_bay = get_object_or_404(self.queryset, pk=pk)
  1333. form = forms.PopulateDeviceBayForm(device_bay, request.POST)
  1334. if form.is_valid():
  1335. device_bay.installed_device = form.cleaned_data['installed_device']
  1336. device_bay.save()
  1337. messages.success(request, "Added {} to {}.".format(device_bay.installed_device, device_bay))
  1338. return redirect('dcim:device', pk=device_bay.device.pk)
  1339. return render(request, 'dcim/devicebay_populate.html', {
  1340. 'device_bay': device_bay,
  1341. 'form': form,
  1342. 'return_url': self.get_return_url(request, device_bay),
  1343. })
  1344. class DeviceBayDepopulateView(generic.ObjectEditView):
  1345. queryset = DeviceBay.objects.all()
  1346. def get(self, request, pk):
  1347. device_bay = get_object_or_404(self.queryset, pk=pk)
  1348. form = ConfirmationForm()
  1349. return render(request, 'dcim/devicebay_depopulate.html', {
  1350. 'device_bay': device_bay,
  1351. 'form': form,
  1352. 'return_url': self.get_return_url(request, device_bay),
  1353. })
  1354. def post(self, request, pk):
  1355. device_bay = get_object_or_404(self.queryset, pk=pk)
  1356. form = ConfirmationForm(request.POST)
  1357. if form.is_valid():
  1358. removed_device = device_bay.installed_device
  1359. device_bay.installed_device = None
  1360. device_bay.save()
  1361. messages.success(request, "{} has been removed from {}.".format(removed_device, device_bay))
  1362. return redirect('dcim:device', pk=device_bay.device.pk)
  1363. return render(request, 'dcim/devicebay_depopulate.html', {
  1364. 'device_bay': device_bay,
  1365. 'form': form,
  1366. 'return_url': self.get_return_url(request, device_bay),
  1367. })
  1368. class DeviceBayBulkImportView(generic.BulkImportView):
  1369. queryset = DeviceBay.objects.all()
  1370. model_form = forms.DeviceBayCSVForm
  1371. table = tables.DeviceBayTable
  1372. class DeviceBayBulkEditView(generic.BulkEditView):
  1373. queryset = DeviceBay.objects.all()
  1374. filterset = filters.DeviceBayFilterSet
  1375. table = tables.DeviceBayTable
  1376. form = forms.DeviceBayBulkEditForm
  1377. class DeviceBayBulkRenameView(generic.BulkRenameView):
  1378. queryset = DeviceBay.objects.all()
  1379. class DeviceBayBulkDeleteView(generic.BulkDeleteView):
  1380. queryset = DeviceBay.objects.all()
  1381. filterset = filters.DeviceBayFilterSet
  1382. table = tables.DeviceBayTable
  1383. #
  1384. # Inventory items
  1385. #
  1386. class InventoryItemListView(generic.ObjectListView):
  1387. queryset = InventoryItem.objects.all()
  1388. filterset = filters.InventoryItemFilterSet
  1389. filterset_form = forms.InventoryItemFilterForm
  1390. table = tables.InventoryItemTable
  1391. action_buttons = ('import', 'export')
  1392. class InventoryItemView(generic.ObjectView):
  1393. queryset = InventoryItem.objects.all()
  1394. def get_extra_context(self, request, instance):
  1395. return {
  1396. 'breadcrumb_url': 'dcim:device_inventory'
  1397. }
  1398. class InventoryItemEditView(generic.ObjectEditView):
  1399. queryset = InventoryItem.objects.all()
  1400. model_form = forms.InventoryItemForm
  1401. class InventoryItemCreateView(generic.ComponentCreateView):
  1402. queryset = InventoryItem.objects.all()
  1403. form = forms.InventoryItemCreateForm
  1404. model_form = forms.InventoryItemForm
  1405. template_name = 'dcim/device_component_add.html'
  1406. class InventoryItemDeleteView(generic.ObjectDeleteView):
  1407. queryset = InventoryItem.objects.all()
  1408. class InventoryItemBulkImportView(generic.BulkImportView):
  1409. queryset = InventoryItem.objects.all()
  1410. model_form = forms.InventoryItemCSVForm
  1411. table = tables.InventoryItemTable
  1412. class InventoryItemBulkEditView(generic.BulkEditView):
  1413. queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer')
  1414. filterset = filters.InventoryItemFilterSet
  1415. table = tables.InventoryItemTable
  1416. form = forms.InventoryItemBulkEditForm
  1417. class InventoryItemBulkRenameView(generic.BulkRenameView):
  1418. queryset = InventoryItem.objects.all()
  1419. class InventoryItemBulkDeleteView(generic.BulkDeleteView):
  1420. queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer')
  1421. table = tables.InventoryItemTable
  1422. template_name = 'dcim/inventoryitem_bulk_delete.html'
  1423. #
  1424. # Bulk Device component creation
  1425. #
  1426. class DeviceBulkAddConsolePortView(generic.BulkComponentCreateView):
  1427. parent_model = Device
  1428. parent_field = 'device'
  1429. form = forms.ConsolePortBulkCreateForm
  1430. queryset = ConsolePort.objects.all()
  1431. model_form = forms.ConsolePortForm
  1432. filterset = filters.DeviceFilterSet
  1433. table = tables.DeviceTable
  1434. default_return_url = 'dcim:device_list'
  1435. class DeviceBulkAddConsoleServerPortView(generic.BulkComponentCreateView):
  1436. parent_model = Device
  1437. parent_field = 'device'
  1438. form = forms.ConsoleServerPortBulkCreateForm
  1439. queryset = ConsoleServerPort.objects.all()
  1440. model_form = forms.ConsoleServerPortForm
  1441. filterset = filters.DeviceFilterSet
  1442. table = tables.DeviceTable
  1443. default_return_url = 'dcim:device_list'
  1444. class DeviceBulkAddPowerPortView(generic.BulkComponentCreateView):
  1445. parent_model = Device
  1446. parent_field = 'device'
  1447. form = forms.PowerPortBulkCreateForm
  1448. queryset = PowerPort.objects.all()
  1449. model_form = forms.PowerPortForm
  1450. filterset = filters.DeviceFilterSet
  1451. table = tables.DeviceTable
  1452. default_return_url = 'dcim:device_list'
  1453. class DeviceBulkAddPowerOutletView(generic.BulkComponentCreateView):
  1454. parent_model = Device
  1455. parent_field = 'device'
  1456. form = forms.PowerOutletBulkCreateForm
  1457. queryset = PowerOutlet.objects.all()
  1458. model_form = forms.PowerOutletForm
  1459. filterset = filters.DeviceFilterSet
  1460. table = tables.DeviceTable
  1461. default_return_url = 'dcim:device_list'
  1462. class DeviceBulkAddInterfaceView(generic.BulkComponentCreateView):
  1463. parent_model = Device
  1464. parent_field = 'device'
  1465. form = forms.InterfaceBulkCreateForm
  1466. queryset = Interface.objects.all()
  1467. model_form = forms.InterfaceForm
  1468. filterset = filters.DeviceFilterSet
  1469. table = tables.DeviceTable
  1470. default_return_url = 'dcim:device_list'
  1471. # class DeviceBulkAddFrontPortView(generic.BulkComponentCreateView):
  1472. # parent_model = Device
  1473. # parent_field = 'device'
  1474. # form = forms.FrontPortBulkCreateForm
  1475. # queryset = FrontPort.objects.all()
  1476. # model_form = forms.FrontPortForm
  1477. # filterset = filters.DeviceFilterSet
  1478. # table = tables.DeviceTable
  1479. # default_return_url = 'dcim:device_list'
  1480. class DeviceBulkAddRearPortView(generic.BulkComponentCreateView):
  1481. parent_model = Device
  1482. parent_field = 'device'
  1483. form = forms.RearPortBulkCreateForm
  1484. queryset = RearPort.objects.all()
  1485. model_form = forms.RearPortForm
  1486. filterset = filters.DeviceFilterSet
  1487. table = tables.DeviceTable
  1488. default_return_url = 'dcim:device_list'
  1489. class DeviceBulkAddDeviceBayView(generic.BulkComponentCreateView):
  1490. parent_model = Device
  1491. parent_field = 'device'
  1492. form = forms.DeviceBayBulkCreateForm
  1493. queryset = DeviceBay.objects.all()
  1494. model_form = forms.DeviceBayForm
  1495. filterset = filters.DeviceFilterSet
  1496. table = tables.DeviceTable
  1497. default_return_url = 'dcim:device_list'
  1498. class DeviceBulkAddInventoryItemView(generic.BulkComponentCreateView):
  1499. parent_model = Device
  1500. parent_field = 'device'
  1501. form = forms.InventoryItemBulkCreateForm
  1502. queryset = InventoryItem.objects.all()
  1503. model_form = forms.InventoryItemForm
  1504. filterset = filters.DeviceFilterSet
  1505. table = tables.DeviceTable
  1506. default_return_url = 'dcim:device_list'
  1507. #
  1508. # Cables
  1509. #
  1510. class CableListView(generic.ObjectListView):
  1511. queryset = Cable.objects.all()
  1512. filterset = filters.CableFilterSet
  1513. filterset_form = forms.CableFilterForm
  1514. table = tables.CableTable
  1515. action_buttons = ('import', 'export')
  1516. class CableView(generic.ObjectView):
  1517. queryset = Cable.objects.all()
  1518. class PathTraceView(generic.ObjectView):
  1519. """
  1520. Trace a cable path beginning from the given path endpoint (origin).
  1521. """
  1522. additional_permissions = ['dcim.view_cable']
  1523. template_name = 'dcim/cable_trace.html'
  1524. def dispatch(self, request, *args, **kwargs):
  1525. model = kwargs.pop('model')
  1526. self.queryset = model.objects.all()
  1527. return super().dispatch(request, *args, **kwargs)
  1528. def get_extra_context(self, request, instance):
  1529. related_paths = []
  1530. # If tracing a PathEndpoint, locate the CablePath (if one exists) by its origin
  1531. if isinstance(instance, PathEndpoint):
  1532. path = instance._path
  1533. # Otherwise, find all CablePaths which traverse the specified object
  1534. else:
  1535. related_paths = CablePath.objects.filter(path__contains=instance).prefetch_related('origin')
  1536. # Check for specification of a particular path (when tracing pass-through ports)
  1537. try:
  1538. path_id = int(request.GET.get('cablepath_id'))
  1539. except TypeError:
  1540. path_id = None
  1541. if path_id in list(related_paths.values_list('pk', flat=True)):
  1542. path = CablePath.objects.get(pk=path_id)
  1543. else:
  1544. path = related_paths.first()
  1545. # Get the total length of the cable and whether the length is definitive (fully defined)
  1546. total_length, is_definitive = path.get_total_length if path else (None, False)
  1547. return {
  1548. 'path': path,
  1549. 'related_paths': related_paths,
  1550. 'total_length': total_length,
  1551. 'is_definitive': is_definitive
  1552. }
  1553. class CableCreateView(generic.ObjectEditView):
  1554. queryset = Cable.objects.all()
  1555. template_name = 'dcim/cable_connect.html'
  1556. def dispatch(self, request, *args, **kwargs):
  1557. # Set the model_form class based on the type of component being connected
  1558. self.model_form = {
  1559. 'console-port': forms.ConnectCableToConsolePortForm,
  1560. 'console-server-port': forms.ConnectCableToConsoleServerPortForm,
  1561. 'power-port': forms.ConnectCableToPowerPortForm,
  1562. 'power-outlet': forms.ConnectCableToPowerOutletForm,
  1563. 'interface': forms.ConnectCableToInterfaceForm,
  1564. 'front-port': forms.ConnectCableToFrontPortForm,
  1565. 'rear-port': forms.ConnectCableToRearPortForm,
  1566. 'power-feed': forms.ConnectCableToPowerFeedForm,
  1567. 'circuit-termination': forms.ConnectCableToCircuitTerminationForm,
  1568. }[kwargs.get('termination_b_type')]
  1569. return super().dispatch(request, *args, **kwargs)
  1570. def alter_obj(self, obj, request, url_args, url_kwargs):
  1571. termination_a_type = url_kwargs.get('termination_a_type')
  1572. termination_a_id = url_kwargs.get('termination_a_id')
  1573. termination_b_type_name = url_kwargs.get('termination_b_type')
  1574. self.termination_b_type = ContentType.objects.get(model=termination_b_type_name.replace('-', ''))
  1575. # Initialize Cable termination attributes
  1576. obj.termination_a = termination_a_type.objects.get(pk=termination_a_id)
  1577. obj.termination_b_type = self.termination_b_type
  1578. return obj
  1579. def get(self, request, *args, **kwargs):
  1580. obj = self.alter_obj(self.get_object(kwargs), request, args, kwargs)
  1581. # Parse initial data manually to avoid setting field values as lists
  1582. initial_data = {k: request.GET[k] for k in request.GET}
  1583. # Set initial site and rack based on side A termination (if not already set)
  1584. termination_a_site = getattr(obj.termination_a.parent, 'site', None)
  1585. if termination_a_site and 'termination_b_region' not in initial_data:
  1586. initial_data['termination_b_region'] = termination_a_site.region
  1587. if 'termination_b_site' not in initial_data:
  1588. initial_data['termination_b_site'] = termination_a_site
  1589. if 'termination_b_rack' not in initial_data:
  1590. initial_data['termination_b_rack'] = getattr(obj.termination_a.parent, 'rack', None)
  1591. form = self.model_form(instance=obj, initial=initial_data)
  1592. return render(request, self.template_name, {
  1593. 'obj': obj,
  1594. 'obj_type': Cable._meta.verbose_name,
  1595. 'termination_b_type': self.termination_b_type.name,
  1596. 'form': form,
  1597. 'return_url': self.get_return_url(request, obj),
  1598. })
  1599. class CableEditView(generic.ObjectEditView):
  1600. queryset = Cable.objects.all()
  1601. model_form = forms.CableForm
  1602. template_name = 'dcim/cable_edit.html'
  1603. class CableDeleteView(generic.ObjectDeleteView):
  1604. queryset = Cable.objects.all()
  1605. class CableBulkImportView(generic.BulkImportView):
  1606. queryset = Cable.objects.all()
  1607. model_form = forms.CableCSVForm
  1608. table = tables.CableTable
  1609. class CableBulkEditView(generic.BulkEditView):
  1610. queryset = Cable.objects.prefetch_related('termination_a', 'termination_b')
  1611. filterset = filters.CableFilterSet
  1612. table = tables.CableTable
  1613. form = forms.CableBulkEditForm
  1614. class CableBulkDeleteView(generic.BulkDeleteView):
  1615. queryset = Cable.objects.prefetch_related('termination_a', 'termination_b')
  1616. filterset = filters.CableFilterSet
  1617. table = tables.CableTable
  1618. #
  1619. # Connections
  1620. #
  1621. class ConsoleConnectionsListView(generic.ObjectListView):
  1622. queryset = ConsolePort.objects.filter(_path__isnull=False).order_by('device')
  1623. filterset = filters.ConsoleConnectionFilterSet
  1624. filterset_form = forms.ConsoleConnectionFilterForm
  1625. table = tables.ConsoleConnectionTable
  1626. template_name = 'dcim/connections_list.html'
  1627. def queryset_to_csv(self):
  1628. csv_data = [
  1629. # Headers
  1630. ','.join(['console_server', 'port', 'device', 'console_port', 'reachable'])
  1631. ]
  1632. for obj in self.queryset:
  1633. csv = csv_format([
  1634. obj._path.destination.device.identifier if obj._path.destination else None,
  1635. obj._path.destination.name if obj._path.destination else None,
  1636. obj.device.identifier,
  1637. obj.name,
  1638. obj._path.is_active
  1639. ])
  1640. csv_data.append(csv)
  1641. return '\n'.join(csv_data)
  1642. def extra_context(self):
  1643. return {
  1644. 'title': 'Console Connections'
  1645. }
  1646. class PowerConnectionsListView(generic.ObjectListView):
  1647. queryset = PowerPort.objects.filter(_path__isnull=False).order_by('device')
  1648. filterset = filters.PowerConnectionFilterSet
  1649. filterset_form = forms.PowerConnectionFilterForm
  1650. table = tables.PowerConnectionTable
  1651. template_name = 'dcim/connections_list.html'
  1652. def queryset_to_csv(self):
  1653. csv_data = [
  1654. # Headers
  1655. ','.join(['pdu', 'outlet', 'device', 'power_port', 'reachable'])
  1656. ]
  1657. for obj in self.queryset:
  1658. csv = csv_format([
  1659. obj._path.destination.device.identifier if obj._path.destination else None,
  1660. obj._path.destination.name if obj._path.destination else None,
  1661. obj.device.identifier,
  1662. obj.name,
  1663. obj._path.is_active
  1664. ])
  1665. csv_data.append(csv)
  1666. return '\n'.join(csv_data)
  1667. def extra_context(self):
  1668. return {
  1669. 'title': 'Power Connections'
  1670. }
  1671. class InterfaceConnectionsListView(generic.ObjectListView):
  1672. queryset = Interface.objects.filter(
  1673. # Avoid duplicate connections by only selecting the lower PK in a connected pair
  1674. _path__isnull=False,
  1675. pk__lt=F('_path__destination_id')
  1676. ).order_by('device')
  1677. filterset = filters.InterfaceConnectionFilterSet
  1678. filterset_form = forms.InterfaceConnectionFilterForm
  1679. table = tables.InterfaceConnectionTable
  1680. template_name = 'dcim/connections_list.html'
  1681. def queryset_to_csv(self):
  1682. csv_data = [
  1683. # Headers
  1684. ','.join([
  1685. 'device_a', 'interface_a', 'device_b', 'interface_b', 'reachable'
  1686. ])
  1687. ]
  1688. for obj in self.queryset:
  1689. csv = csv_format([
  1690. obj._path.destination.device.identifier if obj._path.destination else None,
  1691. obj._path.destination.name if obj._path.destination else None,
  1692. obj.device.identifier,
  1693. obj.name,
  1694. obj._path.is_active
  1695. ])
  1696. csv_data.append(csv)
  1697. return '\n'.join(csv_data)
  1698. def extra_context(self):
  1699. return {
  1700. 'title': 'Interface Connections'
  1701. }
  1702. #
  1703. # Virtual chassis
  1704. #
  1705. class VirtualChassisListView(generic.ObjectListView):
  1706. queryset = VirtualChassis.objects.prefetch_related('master').annotate(
  1707. member_count=count_related(Device, 'virtual_chassis')
  1708. )
  1709. table = tables.VirtualChassisTable
  1710. filterset = filters.VirtualChassisFilterSet
  1711. filterset_form = forms.VirtualChassisFilterForm
  1712. class VirtualChassisView(generic.ObjectView):
  1713. queryset = VirtualChassis.objects.all()
  1714. def get_extra_context(self, request, instance):
  1715. members = Device.objects.restrict(request.user).filter(virtual_chassis=instance)
  1716. return {
  1717. 'members': members,
  1718. }
  1719. class VirtualChassisCreateView(generic.ObjectEditView):
  1720. queryset = VirtualChassis.objects.all()
  1721. model_form = forms.VirtualChassisCreateForm
  1722. template_name = 'dcim/virtualchassis_add.html'
  1723. class VirtualChassisEditView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View):
  1724. queryset = VirtualChassis.objects.all()
  1725. def get_required_permission(self):
  1726. return 'dcim.change_virtualchassis'
  1727. def get(self, request, pk):
  1728. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  1729. VCMemberFormSet = modelformset_factory(
  1730. model=Device,
  1731. form=forms.DeviceVCMembershipForm,
  1732. formset=forms.BaseVCMemberFormSet,
  1733. extra=0
  1734. )
  1735. members_queryset = virtual_chassis.members.prefetch_related('rack').order_by('vc_position')
  1736. vc_form = forms.VirtualChassisForm(instance=virtual_chassis)
  1737. vc_form.fields['master'].queryset = members_queryset
  1738. formset = VCMemberFormSet(queryset=members_queryset)
  1739. return render(request, 'dcim/virtualchassis_edit.html', {
  1740. 'vc_form': vc_form,
  1741. 'formset': formset,
  1742. 'return_url': self.get_return_url(request, virtual_chassis),
  1743. })
  1744. def post(self, request, pk):
  1745. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  1746. VCMemberFormSet = modelformset_factory(
  1747. model=Device,
  1748. form=forms.DeviceVCMembershipForm,
  1749. formset=forms.BaseVCMemberFormSet,
  1750. extra=0
  1751. )
  1752. members_queryset = virtual_chassis.members.prefetch_related('rack').order_by('vc_position')
  1753. vc_form = forms.VirtualChassisForm(request.POST, instance=virtual_chassis)
  1754. vc_form.fields['master'].queryset = members_queryset
  1755. formset = VCMemberFormSet(request.POST, queryset=members_queryset)
  1756. if vc_form.is_valid() and formset.is_valid():
  1757. with transaction.atomic():
  1758. # Save the VirtualChassis
  1759. vc_form.save()
  1760. # Nullify the vc_position of each member first to allow reordering without raising an IntegrityError on
  1761. # duplicate positions. Then save each member instance.
  1762. members = formset.save(commit=False)
  1763. devices = Device.objects.filter(pk__in=[m.pk for m in members])
  1764. for device in devices:
  1765. device.vc_position = None
  1766. device.save()
  1767. for member in members:
  1768. member.save()
  1769. return redirect(virtual_chassis.get_absolute_url())
  1770. return render(request, 'dcim/virtualchassis_edit.html', {
  1771. 'vc_form': vc_form,
  1772. 'formset': formset,
  1773. 'return_url': self.get_return_url(request, virtual_chassis),
  1774. })
  1775. class VirtualChassisDeleteView(generic.ObjectDeleteView):
  1776. queryset = VirtualChassis.objects.all()
  1777. class VirtualChassisAddMemberView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View):
  1778. queryset = VirtualChassis.objects.all()
  1779. def get_required_permission(self):
  1780. return 'dcim.change_virtualchassis'
  1781. def get(self, request, pk):
  1782. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  1783. initial_data = {k: request.GET[k] for k in request.GET}
  1784. member_select_form = forms.VCMemberSelectForm(initial=initial_data)
  1785. membership_form = forms.DeviceVCMembershipForm(initial=initial_data)
  1786. return render(request, 'dcim/virtualchassis_add_member.html', {
  1787. 'virtual_chassis': virtual_chassis,
  1788. 'member_select_form': member_select_form,
  1789. 'membership_form': membership_form,
  1790. 'return_url': self.get_return_url(request, virtual_chassis),
  1791. })
  1792. def post(self, request, pk):
  1793. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  1794. member_select_form = forms.VCMemberSelectForm(request.POST)
  1795. if member_select_form.is_valid():
  1796. device = member_select_form.cleaned_data['device']
  1797. device.virtual_chassis = virtual_chassis
  1798. data = {k: request.POST[k] for k in ['vc_position', 'vc_priority']}
  1799. membership_form = forms.DeviceVCMembershipForm(data=data, validate_vc_position=True, instance=device)
  1800. if membership_form.is_valid():
  1801. membership_form.save()
  1802. msg = 'Added member <a href="{}">{}</a>'.format(device.get_absolute_url(), escape(device))
  1803. messages.success(request, mark_safe(msg))
  1804. if '_addanother' in request.POST:
  1805. return redirect(request.get_full_path())
  1806. return redirect(self.get_return_url(request, device))
  1807. else:
  1808. membership_form = forms.DeviceVCMembershipForm(data=request.POST)
  1809. return render(request, 'dcim/virtualchassis_add_member.html', {
  1810. 'virtual_chassis': virtual_chassis,
  1811. 'member_select_form': member_select_form,
  1812. 'membership_form': membership_form,
  1813. 'return_url': self.get_return_url(request, virtual_chassis),
  1814. })
  1815. class VirtualChassisRemoveMemberView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View):
  1816. queryset = Device.objects.all()
  1817. def get_required_permission(self):
  1818. return 'dcim.change_device'
  1819. def get(self, request, pk):
  1820. device = get_object_or_404(self.queryset, pk=pk, virtual_chassis__isnull=False)
  1821. form = ConfirmationForm(initial=request.GET)
  1822. return render(request, 'dcim/virtualchassis_remove_member.html', {
  1823. 'device': device,
  1824. 'form': form,
  1825. 'return_url': self.get_return_url(request, device),
  1826. })
  1827. def post(self, request, pk):
  1828. device = get_object_or_404(self.queryset, pk=pk, virtual_chassis__isnull=False)
  1829. form = ConfirmationForm(request.POST)
  1830. # Protect master device from being removed
  1831. virtual_chassis = VirtualChassis.objects.filter(master=device).first()
  1832. if virtual_chassis is not None:
  1833. msg = 'Unable to remove master device {} from the virtual chassis.'.format(escape(device))
  1834. messages.error(request, mark_safe(msg))
  1835. return redirect(device.get_absolute_url())
  1836. if form.is_valid():
  1837. devices = Device.objects.filter(pk=device.pk)
  1838. for device in devices:
  1839. device.virtual_chassis = None
  1840. device.vc_position = None
  1841. device.vc_priority = None
  1842. device.save()
  1843. msg = 'Removed {} from virtual chassis {}'.format(device, device.virtual_chassis)
  1844. messages.success(request, msg)
  1845. return redirect(self.get_return_url(request, device))
  1846. return render(request, 'dcim/virtualchassis_remove_member.html', {
  1847. 'device': device,
  1848. 'form': form,
  1849. 'return_url': self.get_return_url(request, device),
  1850. })
  1851. class VirtualChassisBulkImportView(generic.BulkImportView):
  1852. queryset = VirtualChassis.objects.all()
  1853. model_form = forms.VirtualChassisCSVForm
  1854. table = tables.VirtualChassisTable
  1855. class VirtualChassisBulkEditView(generic.BulkEditView):
  1856. queryset = VirtualChassis.objects.all()
  1857. filterset = filters.VirtualChassisFilterSet
  1858. table = tables.VirtualChassisTable
  1859. form = forms.VirtualChassisBulkEditForm
  1860. class VirtualChassisBulkDeleteView(generic.BulkDeleteView):
  1861. queryset = VirtualChassis.objects.all()
  1862. filterset = filters.VirtualChassisFilterSet
  1863. table = tables.VirtualChassisTable
  1864. #
  1865. # Power panels
  1866. #
  1867. class PowerPanelListView(generic.ObjectListView):
  1868. queryset = PowerPanel.objects.prefetch_related(
  1869. 'site', 'rack_group'
  1870. ).annotate(
  1871. powerfeed_count=count_related(PowerFeed, 'power_panel')
  1872. )
  1873. filterset = filters.PowerPanelFilterSet
  1874. filterset_form = forms.PowerPanelFilterForm
  1875. table = tables.PowerPanelTable
  1876. class PowerPanelView(generic.ObjectView):
  1877. queryset = PowerPanel.objects.prefetch_related('site', 'rack_group')
  1878. def get_extra_context(self, request, instance):
  1879. power_feeds = PowerFeed.objects.restrict(request.user).filter(power_panel=instance).prefetch_related('rack')
  1880. powerfeed_table = tables.PowerFeedTable(
  1881. data=power_feeds,
  1882. orderable=False
  1883. )
  1884. powerfeed_table.exclude = ['power_panel']
  1885. return {
  1886. 'powerfeed_table': powerfeed_table,
  1887. }
  1888. class PowerPanelEditView(generic.ObjectEditView):
  1889. queryset = PowerPanel.objects.all()
  1890. model_form = forms.PowerPanelForm
  1891. template_name = 'dcim/powerpanel_edit.html'
  1892. class PowerPanelDeleteView(generic.ObjectDeleteView):
  1893. queryset = PowerPanel.objects.all()
  1894. class PowerPanelBulkImportView(generic.BulkImportView):
  1895. queryset = PowerPanel.objects.all()
  1896. model_form = forms.PowerPanelCSVForm
  1897. table = tables.PowerPanelTable
  1898. class PowerPanelBulkEditView(generic.BulkEditView):
  1899. queryset = PowerPanel.objects.prefetch_related('site', 'rack_group')
  1900. filterset = filters.PowerPanelFilterSet
  1901. table = tables.PowerPanelTable
  1902. form = forms.PowerPanelBulkEditForm
  1903. class PowerPanelBulkDeleteView(generic.BulkDeleteView):
  1904. queryset = PowerPanel.objects.prefetch_related(
  1905. 'site', 'rack_group'
  1906. ).annotate(
  1907. powerfeed_count=count_related(PowerFeed, 'power_panel')
  1908. )
  1909. filterset = filters.PowerPanelFilterSet
  1910. table = tables.PowerPanelTable
  1911. #
  1912. # Power feeds
  1913. #
  1914. class PowerFeedListView(generic.ObjectListView):
  1915. queryset = PowerFeed.objects.all()
  1916. filterset = filters.PowerFeedFilterSet
  1917. filterset_form = forms.PowerFeedFilterForm
  1918. table = tables.PowerFeedTable
  1919. class PowerFeedView(generic.ObjectView):
  1920. queryset = PowerFeed.objects.prefetch_related('power_panel', 'rack')
  1921. class PowerFeedEditView(generic.ObjectEditView):
  1922. queryset = PowerFeed.objects.all()
  1923. model_form = forms.PowerFeedForm
  1924. template_name = 'dcim/powerfeed_edit.html'
  1925. class PowerFeedDeleteView(generic.ObjectDeleteView):
  1926. queryset = PowerFeed.objects.all()
  1927. class PowerFeedBulkImportView(generic.BulkImportView):
  1928. queryset = PowerFeed.objects.all()
  1929. model_form = forms.PowerFeedCSVForm
  1930. table = tables.PowerFeedTable
  1931. class PowerFeedBulkEditView(generic.BulkEditView):
  1932. queryset = PowerFeed.objects.prefetch_related('power_panel', 'rack')
  1933. filterset = filters.PowerFeedFilterSet
  1934. table = tables.PowerFeedTable
  1935. form = forms.PowerFeedBulkEditForm
  1936. class PowerFeedBulkDeleteView(generic.BulkDeleteView):
  1937. queryset = PowerFeed.objects.prefetch_related('power_panel', 'rack')
  1938. filterset = filters.PowerFeedFilterSet
  1939. table = tables.PowerFeedTable