views.py 78 KB

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