views.py 86 KB

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