views.py 82 KB

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