views.py 77 KB

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