views.py 86 KB

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