views.py 74 KB

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