views.py 81 KB

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