views.py 78 KB

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