views.py 91 KB

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