views.py 85 KB

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