views.py 86 KB

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