views.py 90 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903
  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. inventory_items = InventoryItem.objects.restrict(request.user, 'view').filter(
  540. manufacturer=instance
  541. )
  542. devicetypes_table = tables.DeviceTypeTable(devicetypes)
  543. devicetypes_table.columns.hide('manufacturer')
  544. paginate_table(devicetypes_table, request)
  545. return {
  546. 'devicetypes_table': devicetypes_table,
  547. 'inventory_item_count': inventory_items.count(),
  548. }
  549. class ManufacturerEditView(generic.ObjectEditView):
  550. queryset = Manufacturer.objects.all()
  551. model_form = forms.ManufacturerForm
  552. class ManufacturerDeleteView(generic.ObjectDeleteView):
  553. queryset = Manufacturer.objects.all()
  554. class ManufacturerBulkImportView(generic.BulkImportView):
  555. queryset = Manufacturer.objects.all()
  556. model_form = forms.ManufacturerCSVForm
  557. table = tables.ManufacturerTable
  558. class ManufacturerBulkEditView(generic.BulkEditView):
  559. queryset = Manufacturer.objects.annotate(
  560. devicetype_count=count_related(DeviceType, 'manufacturer')
  561. )
  562. filterset = filtersets.ManufacturerFilterSet
  563. table = tables.ManufacturerTable
  564. form = forms.ManufacturerBulkEditForm
  565. class ManufacturerBulkDeleteView(generic.BulkDeleteView):
  566. queryset = Manufacturer.objects.annotate(
  567. devicetype_count=count_related(DeviceType, 'manufacturer')
  568. )
  569. table = tables.ManufacturerTable
  570. #
  571. # Device types
  572. #
  573. class DeviceTypeListView(generic.ObjectListView):
  574. queryset = DeviceType.objects.prefetch_related('manufacturer').annotate(
  575. instance_count=count_related(Device, 'device_type')
  576. )
  577. filterset = filtersets.DeviceTypeFilterSet
  578. filterset_form = forms.DeviceTypeFilterForm
  579. table = tables.DeviceTypeTable
  580. class DeviceTypeView(generic.ObjectView):
  581. queryset = DeviceType.objects.prefetch_related('manufacturer')
  582. def get_extra_context(self, request, instance):
  583. instance_count = Device.objects.restrict(request.user).filter(device_type=instance).count()
  584. # Component tables
  585. consoleport_table = tables.ConsolePortTemplateTable(
  586. ConsolePortTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
  587. orderable=False
  588. )
  589. consoleserverport_table = tables.ConsoleServerPortTemplateTable(
  590. ConsoleServerPortTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
  591. orderable=False
  592. )
  593. powerport_table = tables.PowerPortTemplateTable(
  594. PowerPortTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
  595. orderable=False
  596. )
  597. poweroutlet_table = tables.PowerOutletTemplateTable(
  598. PowerOutletTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
  599. orderable=False
  600. )
  601. interface_table = tables.InterfaceTemplateTable(
  602. list(InterfaceTemplate.objects.restrict(request.user, 'view').filter(device_type=instance)),
  603. orderable=False
  604. )
  605. front_port_table = tables.FrontPortTemplateTable(
  606. FrontPortTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
  607. orderable=False
  608. )
  609. rear_port_table = tables.RearPortTemplateTable(
  610. RearPortTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
  611. orderable=False
  612. )
  613. devicebay_table = tables.DeviceBayTemplateTable(
  614. DeviceBayTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
  615. orderable=False
  616. )
  617. if request.user.has_perm('dcim.change_devicetype'):
  618. consoleport_table.columns.show('pk')
  619. consoleserverport_table.columns.show('pk')
  620. powerport_table.columns.show('pk')
  621. poweroutlet_table.columns.show('pk')
  622. interface_table.columns.show('pk')
  623. front_port_table.columns.show('pk')
  624. rear_port_table.columns.show('pk')
  625. devicebay_table.columns.show('pk')
  626. return {
  627. 'instance_count': instance_count,
  628. 'consoleport_table': consoleport_table,
  629. 'consoleserverport_table': consoleserverport_table,
  630. 'powerport_table': powerport_table,
  631. 'poweroutlet_table': poweroutlet_table,
  632. 'interface_table': interface_table,
  633. 'front_port_table': front_port_table,
  634. 'rear_port_table': rear_port_table,
  635. 'devicebay_table': devicebay_table,
  636. }
  637. class DeviceTypeEditView(generic.ObjectEditView):
  638. queryset = DeviceType.objects.all()
  639. model_form = forms.DeviceTypeForm
  640. class DeviceTypeDeleteView(generic.ObjectDeleteView):
  641. queryset = DeviceType.objects.all()
  642. class DeviceTypeImportView(generic.ObjectImportView):
  643. additional_permissions = [
  644. 'dcim.add_devicetype',
  645. 'dcim.add_consoleporttemplate',
  646. 'dcim.add_consoleserverporttemplate',
  647. 'dcim.add_powerporttemplate',
  648. 'dcim.add_poweroutlettemplate',
  649. 'dcim.add_interfacetemplate',
  650. 'dcim.add_frontporttemplate',
  651. 'dcim.add_rearporttemplate',
  652. 'dcim.add_devicebaytemplate',
  653. ]
  654. queryset = DeviceType.objects.all()
  655. model_form = forms.DeviceTypeImportForm
  656. related_object_forms = OrderedDict((
  657. ('console-ports', forms.ConsolePortTemplateImportForm),
  658. ('console-server-ports', forms.ConsoleServerPortTemplateImportForm),
  659. ('power-ports', forms.PowerPortTemplateImportForm),
  660. ('power-outlets', forms.PowerOutletTemplateImportForm),
  661. ('interfaces', forms.InterfaceTemplateImportForm),
  662. ('rear-ports', forms.RearPortTemplateImportForm),
  663. ('front-ports', forms.FrontPortTemplateImportForm),
  664. ('device-bays', forms.DeviceBayTemplateImportForm),
  665. ))
  666. class DeviceTypeBulkEditView(generic.BulkEditView):
  667. queryset = DeviceType.objects.prefetch_related('manufacturer').annotate(
  668. instance_count=count_related(Device, 'device_type')
  669. )
  670. filterset = filtersets.DeviceTypeFilterSet
  671. table = tables.DeviceTypeTable
  672. form = forms.DeviceTypeBulkEditForm
  673. class DeviceTypeBulkDeleteView(generic.BulkDeleteView):
  674. queryset = DeviceType.objects.prefetch_related('manufacturer').annotate(
  675. instance_count=count_related(Device, 'device_type')
  676. )
  677. filterset = filtersets.DeviceTypeFilterSet
  678. table = tables.DeviceTypeTable
  679. #
  680. # Console port templates
  681. #
  682. class ConsolePortTemplateCreateView(generic.ComponentCreateView):
  683. queryset = ConsolePortTemplate.objects.all()
  684. form = forms.ConsolePortTemplateCreateForm
  685. model_form = forms.ConsolePortTemplateForm
  686. class ConsolePortTemplateEditView(generic.ObjectEditView):
  687. queryset = ConsolePortTemplate.objects.all()
  688. model_form = forms.ConsolePortTemplateForm
  689. class ConsolePortTemplateDeleteView(generic.ObjectDeleteView):
  690. queryset = ConsolePortTemplate.objects.all()
  691. class ConsolePortTemplateBulkEditView(generic.BulkEditView):
  692. queryset = ConsolePortTemplate.objects.all()
  693. table = tables.ConsolePortTemplateTable
  694. form = forms.ConsolePortTemplateBulkEditForm
  695. class ConsolePortTemplateBulkRenameView(generic.BulkRenameView):
  696. queryset = ConsolePortTemplate.objects.all()
  697. class ConsolePortTemplateBulkDeleteView(generic.BulkDeleteView):
  698. queryset = ConsolePortTemplate.objects.all()
  699. table = tables.ConsolePortTemplateTable
  700. #
  701. # Console server port templates
  702. #
  703. class ConsoleServerPortTemplateCreateView(generic.ComponentCreateView):
  704. queryset = ConsoleServerPortTemplate.objects.all()
  705. form = forms.ConsoleServerPortTemplateCreateForm
  706. model_form = forms.ConsoleServerPortTemplateForm
  707. class ConsoleServerPortTemplateEditView(generic.ObjectEditView):
  708. queryset = ConsoleServerPortTemplate.objects.all()
  709. model_form = forms.ConsoleServerPortTemplateForm
  710. class ConsoleServerPortTemplateDeleteView(generic.ObjectDeleteView):
  711. queryset = ConsoleServerPortTemplate.objects.all()
  712. class ConsoleServerPortTemplateBulkEditView(generic.BulkEditView):
  713. queryset = ConsoleServerPortTemplate.objects.all()
  714. table = tables.ConsoleServerPortTemplateTable
  715. form = forms.ConsoleServerPortTemplateBulkEditForm
  716. class ConsoleServerPortTemplateBulkRenameView(generic.BulkRenameView):
  717. queryset = ConsoleServerPortTemplate.objects.all()
  718. class ConsoleServerPortTemplateBulkDeleteView(generic.BulkDeleteView):
  719. queryset = ConsoleServerPortTemplate.objects.all()
  720. table = tables.ConsoleServerPortTemplateTable
  721. #
  722. # Power port templates
  723. #
  724. class PowerPortTemplateCreateView(generic.ComponentCreateView):
  725. queryset = PowerPortTemplate.objects.all()
  726. form = forms.PowerPortTemplateCreateForm
  727. model_form = forms.PowerPortTemplateForm
  728. class PowerPortTemplateEditView(generic.ObjectEditView):
  729. queryset = PowerPortTemplate.objects.all()
  730. model_form = forms.PowerPortTemplateForm
  731. class PowerPortTemplateDeleteView(generic.ObjectDeleteView):
  732. queryset = PowerPortTemplate.objects.all()
  733. class PowerPortTemplateBulkEditView(generic.BulkEditView):
  734. queryset = PowerPortTemplate.objects.all()
  735. table = tables.PowerPortTemplateTable
  736. form = forms.PowerPortTemplateBulkEditForm
  737. class PowerPortTemplateBulkRenameView(generic.BulkRenameView):
  738. queryset = PowerPortTemplate.objects.all()
  739. class PowerPortTemplateBulkDeleteView(generic.BulkDeleteView):
  740. queryset = PowerPortTemplate.objects.all()
  741. table = tables.PowerPortTemplateTable
  742. #
  743. # Power outlet templates
  744. #
  745. class PowerOutletTemplateCreateView(generic.ComponentCreateView):
  746. queryset = PowerOutletTemplate.objects.all()
  747. form = forms.PowerOutletTemplateCreateForm
  748. model_form = forms.PowerOutletTemplateForm
  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. class InterfaceTemplateEditView(generic.ObjectEditView):
  771. queryset = InterfaceTemplate.objects.all()
  772. model_form = forms.InterfaceTemplateForm
  773. class InterfaceTemplateDeleteView(generic.ObjectDeleteView):
  774. queryset = InterfaceTemplate.objects.all()
  775. class InterfaceTemplateBulkEditView(generic.BulkEditView):
  776. queryset = InterfaceTemplate.objects.all()
  777. table = tables.InterfaceTemplateTable
  778. form = forms.InterfaceTemplateBulkEditForm
  779. class InterfaceTemplateBulkRenameView(generic.BulkRenameView):
  780. queryset = InterfaceTemplate.objects.all()
  781. class InterfaceTemplateBulkDeleteView(generic.BulkDeleteView):
  782. queryset = InterfaceTemplate.objects.all()
  783. table = tables.InterfaceTemplateTable
  784. #
  785. # Front port templates
  786. #
  787. class FrontPortTemplateCreateView(generic.ComponentCreateView):
  788. queryset = FrontPortTemplate.objects.all()
  789. form = forms.FrontPortTemplateCreateForm
  790. model_form = forms.FrontPortTemplateForm
  791. class FrontPortTemplateEditView(generic.ObjectEditView):
  792. queryset = FrontPortTemplate.objects.all()
  793. model_form = forms.FrontPortTemplateForm
  794. class FrontPortTemplateDeleteView(generic.ObjectDeleteView):
  795. queryset = FrontPortTemplate.objects.all()
  796. class FrontPortTemplateBulkEditView(generic.BulkEditView):
  797. queryset = FrontPortTemplate.objects.all()
  798. table = tables.FrontPortTemplateTable
  799. form = forms.FrontPortTemplateBulkEditForm
  800. class FrontPortTemplateBulkRenameView(generic.BulkRenameView):
  801. queryset = FrontPortTemplate.objects.all()
  802. class FrontPortTemplateBulkDeleteView(generic.BulkDeleteView):
  803. queryset = FrontPortTemplate.objects.all()
  804. table = tables.FrontPortTemplateTable
  805. #
  806. # Rear port templates
  807. #
  808. class RearPortTemplateCreateView(generic.ComponentCreateView):
  809. queryset = RearPortTemplate.objects.all()
  810. form = forms.RearPortTemplateCreateForm
  811. model_form = forms.RearPortTemplateForm
  812. class RearPortTemplateEditView(generic.ObjectEditView):
  813. queryset = RearPortTemplate.objects.all()
  814. model_form = forms.RearPortTemplateForm
  815. class RearPortTemplateDeleteView(generic.ObjectDeleteView):
  816. queryset = RearPortTemplate.objects.all()
  817. class RearPortTemplateBulkEditView(generic.BulkEditView):
  818. queryset = RearPortTemplate.objects.all()
  819. table = tables.RearPortTemplateTable
  820. form = forms.RearPortTemplateBulkEditForm
  821. class RearPortTemplateBulkRenameView(generic.BulkRenameView):
  822. queryset = RearPortTemplate.objects.all()
  823. class RearPortTemplateBulkDeleteView(generic.BulkDeleteView):
  824. queryset = RearPortTemplate.objects.all()
  825. table = tables.RearPortTemplateTable
  826. #
  827. # Device bay templates
  828. #
  829. class DeviceBayTemplateCreateView(generic.ComponentCreateView):
  830. queryset = DeviceBayTemplate.objects.all()
  831. form = forms.DeviceBayTemplateCreateForm
  832. model_form = forms.DeviceBayTemplateForm
  833. class DeviceBayTemplateEditView(generic.ObjectEditView):
  834. queryset = DeviceBayTemplate.objects.all()
  835. model_form = forms.DeviceBayTemplateForm
  836. class DeviceBayTemplateDeleteView(generic.ObjectDeleteView):
  837. queryset = DeviceBayTemplate.objects.all()
  838. class DeviceBayTemplateBulkEditView(generic.BulkEditView):
  839. queryset = DeviceBayTemplate.objects.all()
  840. table = tables.DeviceBayTemplateTable
  841. form = forms.DeviceBayTemplateBulkEditForm
  842. class DeviceBayTemplateBulkRenameView(generic.BulkRenameView):
  843. queryset = DeviceBayTemplate.objects.all()
  844. class DeviceBayTemplateBulkDeleteView(generic.BulkDeleteView):
  845. queryset = DeviceBayTemplate.objects.all()
  846. table = tables.DeviceBayTemplateTable
  847. #
  848. # Device roles
  849. #
  850. class DeviceRoleListView(generic.ObjectListView):
  851. queryset = DeviceRole.objects.annotate(
  852. device_count=count_related(Device, 'device_role'),
  853. vm_count=count_related(VirtualMachine, 'role')
  854. )
  855. table = tables.DeviceRoleTable
  856. class DeviceRoleView(generic.ObjectView):
  857. queryset = DeviceRole.objects.all()
  858. def get_extra_context(self, request, instance):
  859. devices = Device.objects.restrict(request.user, 'view').filter(
  860. device_role=instance
  861. )
  862. devices_table = tables.DeviceTable(devices)
  863. devices_table.columns.hide('device_role')
  864. paginate_table(devices_table, request)
  865. return {
  866. 'devices_table': devices_table,
  867. 'device_count': Device.objects.filter(device_role=instance).count(),
  868. 'virtualmachine_count': VirtualMachine.objects.filter(role=instance).count(),
  869. }
  870. class DeviceRoleEditView(generic.ObjectEditView):
  871. queryset = DeviceRole.objects.all()
  872. model_form = forms.DeviceRoleForm
  873. class DeviceRoleDeleteView(generic.ObjectDeleteView):
  874. queryset = DeviceRole.objects.all()
  875. class DeviceRoleBulkImportView(generic.BulkImportView):
  876. queryset = DeviceRole.objects.all()
  877. model_form = forms.DeviceRoleCSVForm
  878. table = tables.DeviceRoleTable
  879. class DeviceRoleBulkEditView(generic.BulkEditView):
  880. queryset = DeviceRole.objects.annotate(
  881. device_count=count_related(Device, 'device_role'),
  882. vm_count=count_related(VirtualMachine, 'role')
  883. )
  884. filterset = filtersets.DeviceRoleFilterSet
  885. table = tables.DeviceRoleTable
  886. form = forms.DeviceRoleBulkEditForm
  887. class DeviceRoleBulkDeleteView(generic.BulkDeleteView):
  888. queryset = DeviceRole.objects.annotate(
  889. device_count=count_related(Device, 'device_role'),
  890. vm_count=count_related(VirtualMachine, 'role')
  891. )
  892. table = tables.DeviceRoleTable
  893. #
  894. # Platforms
  895. #
  896. class PlatformListView(generic.ObjectListView):
  897. queryset = Platform.objects.annotate(
  898. device_count=count_related(Device, 'platform'),
  899. vm_count=count_related(VirtualMachine, 'platform')
  900. )
  901. table = tables.PlatformTable
  902. filterset = filtersets.PlatformFilterSet
  903. filterset_form = forms.PlatformFilterForm
  904. class PlatformView(generic.ObjectView):
  905. queryset = Platform.objects.all()
  906. def get_extra_context(self, request, instance):
  907. devices = Device.objects.restrict(request.user, 'view').filter(
  908. platform=instance
  909. )
  910. devices_table = tables.DeviceTable(devices)
  911. devices_table.columns.hide('platform')
  912. paginate_table(devices_table, request)
  913. return {
  914. 'devices_table': devices_table,
  915. }
  916. class PlatformEditView(generic.ObjectEditView):
  917. queryset = Platform.objects.all()
  918. model_form = forms.PlatformForm
  919. class PlatformDeleteView(generic.ObjectDeleteView):
  920. queryset = Platform.objects.all()
  921. class PlatformBulkImportView(generic.BulkImportView):
  922. queryset = Platform.objects.all()
  923. model_form = forms.PlatformCSVForm
  924. table = tables.PlatformTable
  925. class PlatformBulkEditView(generic.BulkEditView):
  926. queryset = Platform.objects.all()
  927. filterset = filtersets.PlatformFilterSet
  928. table = tables.PlatformTable
  929. form = forms.PlatformBulkEditForm
  930. class PlatformBulkDeleteView(generic.BulkDeleteView):
  931. queryset = Platform.objects.all()
  932. table = tables.PlatformTable
  933. #
  934. # Devices
  935. #
  936. class DeviceListView(generic.ObjectListView):
  937. queryset = Device.objects.all()
  938. filterset = filtersets.DeviceFilterSet
  939. filterset_form = forms.DeviceFilterForm
  940. table = tables.DeviceTable
  941. template_name = 'dcim/device_list.html'
  942. class DeviceView(generic.ObjectView):
  943. queryset = Device.objects.prefetch_related(
  944. 'site__region', 'location', 'rack', 'tenant__group', 'device_role', 'platform', 'primary_ip4', 'primary_ip6'
  945. )
  946. def get_extra_context(self, request, instance):
  947. # VirtualChassis members
  948. if instance.virtual_chassis is not None:
  949. vc_members = Device.objects.restrict(request.user, 'view').filter(
  950. virtual_chassis=instance.virtual_chassis
  951. ).order_by('vc_position')
  952. else:
  953. vc_members = []
  954. # Services
  955. services = Service.objects.restrict(request.user, 'view').filter(device=instance)
  956. # Find up to ten devices in the same site with the same functional role for quick reference.
  957. related_devices = Device.objects.restrict(request.user, 'view').filter(
  958. site=instance.site, device_role=instance.device_role
  959. ).exclude(
  960. pk=instance.pk
  961. ).prefetch_related(
  962. 'rack', 'device_type__manufacturer'
  963. )[:10]
  964. return {
  965. 'services': services,
  966. 'vc_members': vc_members,
  967. 'related_devices': related_devices,
  968. 'active_tab': 'device',
  969. }
  970. class DeviceConsolePortsView(generic.ObjectView):
  971. queryset = Device.objects.all()
  972. template_name = 'dcim/device/consoleports.html'
  973. def get_extra_context(self, request, instance):
  974. consoleports = ConsolePort.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related(
  975. 'cable', '_path__destination',
  976. )
  977. consoleport_table = tables.DeviceConsolePortTable(
  978. data=consoleports,
  979. user=request.user
  980. )
  981. if request.user.has_perm('dcim.change_consoleport') or request.user.has_perm('dcim.delete_consoleport'):
  982. consoleport_table.columns.show('pk')
  983. paginate_table(consoleport_table, request)
  984. return {
  985. 'consoleport_table': consoleport_table,
  986. 'active_tab': 'console-ports',
  987. }
  988. class DeviceConsoleServerPortsView(generic.ObjectView):
  989. queryset = Device.objects.all()
  990. template_name = 'dcim/device/consoleserverports.html'
  991. def get_extra_context(self, request, instance):
  992. consoleserverports = ConsoleServerPort.objects.restrict(request.user, 'view').filter(
  993. device=instance
  994. ).prefetch_related(
  995. 'cable', '_path__destination',
  996. )
  997. consoleserverport_table = tables.DeviceConsoleServerPortTable(
  998. data=consoleserverports,
  999. user=request.user
  1000. )
  1001. if request.user.has_perm('dcim.change_consoleserverport') or \
  1002. request.user.has_perm('dcim.delete_consoleserverport'):
  1003. consoleserverport_table.columns.show('pk')
  1004. paginate_table(consoleserverport_table, request)
  1005. return {
  1006. 'consoleserverport_table': consoleserverport_table,
  1007. 'active_tab': 'console-server-ports',
  1008. }
  1009. class DevicePowerPortsView(generic.ObjectView):
  1010. queryset = Device.objects.all()
  1011. template_name = 'dcim/device/powerports.html'
  1012. def get_extra_context(self, request, instance):
  1013. powerports = PowerPort.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related(
  1014. 'cable', '_path__destination',
  1015. )
  1016. powerport_table = tables.DevicePowerPortTable(
  1017. data=powerports,
  1018. user=request.user
  1019. )
  1020. if request.user.has_perm('dcim.change_powerport') or request.user.has_perm('dcim.delete_powerport'):
  1021. powerport_table.columns.show('pk')
  1022. paginate_table(powerport_table, request)
  1023. return {
  1024. 'powerport_table': powerport_table,
  1025. 'active_tab': 'power-ports',
  1026. }
  1027. class DevicePowerOutletsView(generic.ObjectView):
  1028. queryset = Device.objects.all()
  1029. template_name = 'dcim/device/poweroutlets.html'
  1030. def get_extra_context(self, request, instance):
  1031. poweroutlets = PowerOutlet.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related(
  1032. 'cable', 'power_port', '_path__destination',
  1033. )
  1034. poweroutlet_table = tables.DevicePowerOutletTable(
  1035. data=poweroutlets,
  1036. user=request.user
  1037. )
  1038. if request.user.has_perm('dcim.change_poweroutlet') or request.user.has_perm('dcim.delete_poweroutlet'):
  1039. poweroutlet_table.columns.show('pk')
  1040. paginate_table(poweroutlet_table, request)
  1041. return {
  1042. 'poweroutlet_table': poweroutlet_table,
  1043. 'active_tab': 'power-outlets',
  1044. }
  1045. class DeviceInterfacesView(generic.ObjectView):
  1046. queryset = Device.objects.all()
  1047. template_name = 'dcim/device/interfaces.html'
  1048. def get_extra_context(self, request, instance):
  1049. interfaces = instance.vc_interfaces().restrict(request.user, 'view').prefetch_related(
  1050. Prefetch('ip_addresses', queryset=IPAddress.objects.restrict(request.user)),
  1051. Prefetch('member_interfaces', queryset=Interface.objects.restrict(request.user)),
  1052. 'lag', 'cable', '_path__destination', 'tags',
  1053. )
  1054. interface_table = tables.DeviceInterfaceTable(
  1055. data=interfaces,
  1056. user=request.user
  1057. )
  1058. if request.user.has_perm('dcim.change_interface') or request.user.has_perm('dcim.delete_interface'):
  1059. interface_table.columns.show('pk')
  1060. paginate_table(interface_table, request)
  1061. return {
  1062. 'interface_table': interface_table,
  1063. 'active_tab': 'interfaces',
  1064. }
  1065. class DeviceFrontPortsView(generic.ObjectView):
  1066. queryset = Device.objects.all()
  1067. template_name = 'dcim/device/frontports.html'
  1068. def get_extra_context(self, request, instance):
  1069. frontports = FrontPort.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related(
  1070. 'rear_port', 'cable',
  1071. )
  1072. frontport_table = tables.DeviceFrontPortTable(
  1073. data=frontports,
  1074. user=request.user
  1075. )
  1076. if request.user.has_perm('dcim.change_frontport') or request.user.has_perm('dcim.delete_frontport'):
  1077. frontport_table.columns.show('pk')
  1078. paginate_table(frontport_table, request)
  1079. return {
  1080. 'frontport_table': frontport_table,
  1081. 'active_tab': 'front-ports',
  1082. }
  1083. class DeviceRearPortsView(generic.ObjectView):
  1084. queryset = Device.objects.all()
  1085. template_name = 'dcim/device/rearports.html'
  1086. def get_extra_context(self, request, instance):
  1087. rearports = RearPort.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related('cable')
  1088. rearport_table = tables.DeviceRearPortTable(
  1089. data=rearports,
  1090. user=request.user
  1091. )
  1092. if request.user.has_perm('dcim.change_rearport') or request.user.has_perm('dcim.delete_rearport'):
  1093. rearport_table.columns.show('pk')
  1094. paginate_table(rearport_table, request)
  1095. return {
  1096. 'rearport_table': rearport_table,
  1097. 'active_tab': 'rear-ports',
  1098. }
  1099. class DeviceDeviceBaysView(generic.ObjectView):
  1100. queryset = Device.objects.all()
  1101. template_name = 'dcim/device/devicebays.html'
  1102. def get_extra_context(self, request, instance):
  1103. devicebays = DeviceBay.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related(
  1104. 'installed_device__device_type__manufacturer',
  1105. )
  1106. devicebay_table = tables.DeviceDeviceBayTable(
  1107. data=devicebays,
  1108. user=request.user
  1109. )
  1110. if request.user.has_perm('dcim.change_devicebay') or request.user.has_perm('dcim.delete_devicebay'):
  1111. devicebay_table.columns.show('pk')
  1112. paginate_table(devicebay_table, request)
  1113. return {
  1114. 'devicebay_table': devicebay_table,
  1115. 'active_tab': 'device-bays',
  1116. }
  1117. class DeviceInventoryView(generic.ObjectView):
  1118. queryset = Device.objects.all()
  1119. template_name = 'dcim/device/inventory.html'
  1120. def get_extra_context(self, request, instance):
  1121. inventoryitems = InventoryItem.objects.restrict(request.user, 'view').filter(
  1122. device=instance
  1123. ).prefetch_related('manufacturer')
  1124. inventoryitem_table = tables.DeviceInventoryItemTable(
  1125. data=inventoryitems,
  1126. user=request.user
  1127. )
  1128. if request.user.has_perm('dcim.change_inventoryitem') or request.user.has_perm('dcim.delete_inventoryitem'):
  1129. inventoryitem_table.columns.show('pk')
  1130. paginate_table(inventoryitem_table, request)
  1131. return {
  1132. 'inventoryitem_table': inventoryitem_table,
  1133. 'active_tab': 'inventory',
  1134. }
  1135. class DeviceStatusView(generic.ObjectView):
  1136. additional_permissions = ['dcim.napalm_read_device']
  1137. queryset = Device.objects.all()
  1138. template_name = 'dcim/device/status.html'
  1139. def get_extra_context(self, request, instance):
  1140. return {
  1141. 'active_tab': 'status',
  1142. }
  1143. class DeviceLLDPNeighborsView(generic.ObjectView):
  1144. additional_permissions = ['dcim.napalm_read_device']
  1145. queryset = Device.objects.all()
  1146. template_name = 'dcim/device/lldp_neighbors.html'
  1147. def get_extra_context(self, request, instance):
  1148. interfaces = instance.vc_interfaces().restrict(request.user, 'view').prefetch_related(
  1149. '_path__destination'
  1150. ).exclude(
  1151. type__in=NONCONNECTABLE_IFACE_TYPES
  1152. )
  1153. return {
  1154. 'interfaces': interfaces,
  1155. 'active_tab': 'lldp-neighbors',
  1156. }
  1157. class DeviceConfigView(generic.ObjectView):
  1158. additional_permissions = ['dcim.napalm_read_device']
  1159. queryset = Device.objects.all()
  1160. template_name = 'dcim/device/config.html'
  1161. def get_extra_context(self, request, instance):
  1162. return {
  1163. 'active_tab': 'config',
  1164. }
  1165. class DeviceConfigContextView(ObjectConfigContextView):
  1166. queryset = Device.objects.annotate_config_context_data()
  1167. base_template = 'dcim/device/base.html'
  1168. class DeviceChangeLogView(ObjectChangeLogView):
  1169. base_template = 'dcim/device/base.html'
  1170. class DeviceJournalView(ObjectJournalView):
  1171. base_template = 'dcim/device/base.html'
  1172. class DeviceEditView(generic.ObjectEditView):
  1173. queryset = Device.objects.all()
  1174. model_form = forms.DeviceForm
  1175. template_name = 'dcim/device_edit.html'
  1176. class DeviceDeleteView(generic.ObjectDeleteView):
  1177. queryset = Device.objects.all()
  1178. class DeviceBulkImportView(generic.BulkImportView):
  1179. queryset = Device.objects.all()
  1180. model_form = forms.DeviceCSVForm
  1181. table = tables.DeviceImportTable
  1182. template_name = 'dcim/device_import.html'
  1183. class ChildDeviceBulkImportView(generic.BulkImportView):
  1184. queryset = Device.objects.all()
  1185. model_form = forms.ChildDeviceCSVForm
  1186. table = tables.DeviceImportTable
  1187. template_name = 'dcim/device_import_child.html'
  1188. def _save_obj(self, obj_form, request):
  1189. obj = obj_form.save()
  1190. # Save the reverse relation to the parent device bay
  1191. device_bay = obj.parent_bay
  1192. device_bay.installed_device = obj
  1193. device_bay.save()
  1194. return obj
  1195. class DeviceBulkEditView(generic.BulkEditView):
  1196. queryset = Device.objects.prefetch_related('tenant', 'site', 'rack', 'device_role', 'device_type__manufacturer')
  1197. filterset = filtersets.DeviceFilterSet
  1198. table = tables.DeviceTable
  1199. form = forms.DeviceBulkEditForm
  1200. class DeviceBulkDeleteView(generic.BulkDeleteView):
  1201. queryset = Device.objects.prefetch_related('tenant', 'site', 'rack', 'device_role', 'device_type__manufacturer')
  1202. filterset = filtersets.DeviceFilterSet
  1203. table = tables.DeviceTable
  1204. #
  1205. # Console ports
  1206. #
  1207. class ConsolePortListView(generic.ObjectListView):
  1208. queryset = ConsolePort.objects.all()
  1209. filterset = filtersets.ConsolePortFilterSet
  1210. filterset_form = forms.ConsolePortFilterForm
  1211. table = tables.ConsolePortTable
  1212. action_buttons = ('import', 'export')
  1213. class ConsolePortView(generic.ObjectView):
  1214. queryset = ConsolePort.objects.all()
  1215. class ConsolePortCreateView(generic.ComponentCreateView):
  1216. queryset = ConsolePort.objects.all()
  1217. form = forms.ConsolePortCreateForm
  1218. model_form = forms.ConsolePortForm
  1219. class ConsolePortEditView(generic.ObjectEditView):
  1220. queryset = ConsolePort.objects.all()
  1221. model_form = forms.ConsolePortForm
  1222. template_name = 'dcim/device_component_edit.html'
  1223. class ConsolePortDeleteView(generic.ObjectDeleteView):
  1224. queryset = ConsolePort.objects.all()
  1225. class ConsolePortBulkImportView(generic.BulkImportView):
  1226. queryset = ConsolePort.objects.all()
  1227. model_form = forms.ConsolePortCSVForm
  1228. table = tables.ConsolePortTable
  1229. class ConsolePortBulkEditView(generic.BulkEditView):
  1230. queryset = ConsolePort.objects.all()
  1231. filterset = filtersets.ConsolePortFilterSet
  1232. table = tables.ConsolePortTable
  1233. form = forms.ConsolePortBulkEditForm
  1234. class ConsolePortBulkRenameView(generic.BulkRenameView):
  1235. queryset = ConsolePort.objects.all()
  1236. class ConsolePortBulkDisconnectView(BulkDisconnectView):
  1237. queryset = ConsolePort.objects.all()
  1238. class ConsolePortBulkDeleteView(generic.BulkDeleteView):
  1239. queryset = ConsolePort.objects.all()
  1240. filterset = filtersets.ConsolePortFilterSet
  1241. table = tables.ConsolePortTable
  1242. #
  1243. # Console server ports
  1244. #
  1245. class ConsoleServerPortListView(generic.ObjectListView):
  1246. queryset = ConsoleServerPort.objects.all()
  1247. filterset = filtersets.ConsoleServerPortFilterSet
  1248. filterset_form = forms.ConsoleServerPortFilterForm
  1249. table = tables.ConsoleServerPortTable
  1250. action_buttons = ('import', 'export')
  1251. class ConsoleServerPortView(generic.ObjectView):
  1252. queryset = ConsoleServerPort.objects.all()
  1253. class ConsoleServerPortCreateView(generic.ComponentCreateView):
  1254. queryset = ConsoleServerPort.objects.all()
  1255. form = forms.ConsoleServerPortCreateForm
  1256. model_form = forms.ConsoleServerPortForm
  1257. class ConsoleServerPortEditView(generic.ObjectEditView):
  1258. queryset = ConsoleServerPort.objects.all()
  1259. model_form = forms.ConsoleServerPortForm
  1260. template_name = 'dcim/device_component_edit.html'
  1261. class ConsoleServerPortDeleteView(generic.ObjectDeleteView):
  1262. queryset = ConsoleServerPort.objects.all()
  1263. class ConsoleServerPortBulkImportView(generic.BulkImportView):
  1264. queryset = ConsoleServerPort.objects.all()
  1265. model_form = forms.ConsoleServerPortCSVForm
  1266. table = tables.ConsoleServerPortTable
  1267. class ConsoleServerPortBulkEditView(generic.BulkEditView):
  1268. queryset = ConsoleServerPort.objects.all()
  1269. filterset = filtersets.ConsoleServerPortFilterSet
  1270. table = tables.ConsoleServerPortTable
  1271. form = forms.ConsoleServerPortBulkEditForm
  1272. class ConsoleServerPortBulkRenameView(generic.BulkRenameView):
  1273. queryset = ConsoleServerPort.objects.all()
  1274. class ConsoleServerPortBulkDisconnectView(BulkDisconnectView):
  1275. queryset = ConsoleServerPort.objects.all()
  1276. class ConsoleServerPortBulkDeleteView(generic.BulkDeleteView):
  1277. queryset = ConsoleServerPort.objects.all()
  1278. filterset = filtersets.ConsoleServerPortFilterSet
  1279. table = tables.ConsoleServerPortTable
  1280. #
  1281. # Power ports
  1282. #
  1283. class PowerPortListView(generic.ObjectListView):
  1284. queryset = PowerPort.objects.all()
  1285. filterset = filtersets.PowerPortFilterSet
  1286. filterset_form = forms.PowerPortFilterForm
  1287. table = tables.PowerPortTable
  1288. action_buttons = ('import', 'export')
  1289. class PowerPortView(generic.ObjectView):
  1290. queryset = PowerPort.objects.all()
  1291. class PowerPortCreateView(generic.ComponentCreateView):
  1292. queryset = PowerPort.objects.all()
  1293. form = forms.PowerPortCreateForm
  1294. model_form = forms.PowerPortForm
  1295. class PowerPortEditView(generic.ObjectEditView):
  1296. queryset = PowerPort.objects.all()
  1297. model_form = forms.PowerPortForm
  1298. template_name = 'dcim/device_component_edit.html'
  1299. class PowerPortDeleteView(generic.ObjectDeleteView):
  1300. queryset = PowerPort.objects.all()
  1301. class PowerPortBulkImportView(generic.BulkImportView):
  1302. queryset = PowerPort.objects.all()
  1303. model_form = forms.PowerPortCSVForm
  1304. table = tables.PowerPortTable
  1305. class PowerPortBulkEditView(generic.BulkEditView):
  1306. queryset = PowerPort.objects.all()
  1307. filterset = filtersets.PowerPortFilterSet
  1308. table = tables.PowerPortTable
  1309. form = forms.PowerPortBulkEditForm
  1310. class PowerPortBulkRenameView(generic.BulkRenameView):
  1311. queryset = PowerPort.objects.all()
  1312. class PowerPortBulkDisconnectView(BulkDisconnectView):
  1313. queryset = PowerPort.objects.all()
  1314. class PowerPortBulkDeleteView(generic.BulkDeleteView):
  1315. queryset = PowerPort.objects.all()
  1316. filterset = filtersets.PowerPortFilterSet
  1317. table = tables.PowerPortTable
  1318. #
  1319. # Power outlets
  1320. #
  1321. class PowerOutletListView(generic.ObjectListView):
  1322. queryset = PowerOutlet.objects.all()
  1323. filterset = filtersets.PowerOutletFilterSet
  1324. filterset_form = forms.PowerOutletFilterForm
  1325. table = tables.PowerOutletTable
  1326. action_buttons = ('import', 'export')
  1327. class PowerOutletView(generic.ObjectView):
  1328. queryset = PowerOutlet.objects.all()
  1329. class PowerOutletCreateView(generic.ComponentCreateView):
  1330. queryset = PowerOutlet.objects.all()
  1331. form = forms.PowerOutletCreateForm
  1332. model_form = forms.PowerOutletForm
  1333. class PowerOutletEditView(generic.ObjectEditView):
  1334. queryset = PowerOutlet.objects.all()
  1335. model_form = forms.PowerOutletForm
  1336. template_name = 'dcim/device_component_edit.html'
  1337. class PowerOutletDeleteView(generic.ObjectDeleteView):
  1338. queryset = PowerOutlet.objects.all()
  1339. class PowerOutletBulkImportView(generic.BulkImportView):
  1340. queryset = PowerOutlet.objects.all()
  1341. model_form = forms.PowerOutletCSVForm
  1342. table = tables.PowerOutletTable
  1343. class PowerOutletBulkEditView(generic.BulkEditView):
  1344. queryset = PowerOutlet.objects.all()
  1345. filterset = filtersets.PowerOutletFilterSet
  1346. table = tables.PowerOutletTable
  1347. form = forms.PowerOutletBulkEditForm
  1348. class PowerOutletBulkRenameView(generic.BulkRenameView):
  1349. queryset = PowerOutlet.objects.all()
  1350. class PowerOutletBulkDisconnectView(BulkDisconnectView):
  1351. queryset = PowerOutlet.objects.all()
  1352. class PowerOutletBulkDeleteView(generic.BulkDeleteView):
  1353. queryset = PowerOutlet.objects.all()
  1354. filterset = filtersets.PowerOutletFilterSet
  1355. table = tables.PowerOutletTable
  1356. #
  1357. # Interfaces
  1358. #
  1359. class InterfaceListView(generic.ObjectListView):
  1360. queryset = Interface.objects.all()
  1361. filterset = filtersets.InterfaceFilterSet
  1362. filterset_form = forms.InterfaceFilterForm
  1363. table = tables.InterfaceTable
  1364. action_buttons = ('import', 'export')
  1365. class InterfaceView(generic.ObjectView):
  1366. queryset = Interface.objects.all()
  1367. def get_extra_context(self, request, instance):
  1368. # Get assigned IP addresses
  1369. ipaddress_table = InterfaceIPAddressTable(
  1370. data=instance.ip_addresses.restrict(request.user, 'view').prefetch_related('vrf', 'tenant'),
  1371. orderable=False
  1372. )
  1373. # Get child interfaces
  1374. child_interfaces = Interface.objects.restrict(request.user, 'view').filter(parent=instance)
  1375. child_interfaces_tables = tables.InterfaceTable(
  1376. child_interfaces,
  1377. orderable=False
  1378. )
  1379. child_interfaces_tables.columns.hide('device')
  1380. # Get assigned VLANs and annotate whether each is tagged or untagged
  1381. vlans = []
  1382. if instance.untagged_vlan is not None:
  1383. vlans.append(instance.untagged_vlan)
  1384. vlans[0].tagged = False
  1385. for vlan in instance.tagged_vlans.restrict(request.user).prefetch_related('site', 'group', 'tenant', 'role'):
  1386. vlan.tagged = True
  1387. vlans.append(vlan)
  1388. vlan_table = InterfaceVLANTable(
  1389. interface=instance,
  1390. data=vlans,
  1391. orderable=False
  1392. )
  1393. return {
  1394. 'ipaddress_table': ipaddress_table,
  1395. 'child_interfaces_table': child_interfaces_tables,
  1396. 'vlan_table': vlan_table,
  1397. }
  1398. class InterfaceCreateView(generic.ComponentCreateView):
  1399. queryset = Interface.objects.all()
  1400. form = forms.InterfaceCreateForm
  1401. model_form = forms.InterfaceForm
  1402. template_name = 'dcim/interface_create.html'
  1403. def post(self, request):
  1404. """
  1405. Override inherited post() method to handle request to assign newly created
  1406. interface objects (first object) to an IP Address object.
  1407. """
  1408. form = self.form(request.POST, initial=request.GET)
  1409. new_objs = self.validate_form(request, form)
  1410. if form.is_valid() and not form.errors:
  1411. if '_addanother' in request.POST:
  1412. return redirect(request.get_full_path())
  1413. elif new_objs is not None and '_assignip' in request.POST and len(new_objs) >= 1 and \
  1414. request.user.has_perm('ipam.add_ipaddress'):
  1415. first_obj = new_objs[0].pk
  1416. return redirect(
  1417. f'/ipam/ip-addresses/add/?interface={first_obj}&return_url={self.get_return_url(request)}'
  1418. )
  1419. else:
  1420. return redirect(self.get_return_url(request))
  1421. return render(request, self.template_name, {
  1422. 'obj_type': self.queryset.model._meta.verbose_name,
  1423. 'form': form,
  1424. 'return_url': self.get_return_url(request),
  1425. })
  1426. class InterfaceEditView(generic.ObjectEditView):
  1427. queryset = Interface.objects.all()
  1428. model_form = forms.InterfaceForm
  1429. template_name = 'dcim/interface_edit.html'
  1430. class InterfaceDeleteView(generic.ObjectDeleteView):
  1431. queryset = Interface.objects.all()
  1432. class InterfaceBulkImportView(generic.BulkImportView):
  1433. queryset = Interface.objects.all()
  1434. model_form = forms.InterfaceCSVForm
  1435. table = tables.InterfaceTable
  1436. class InterfaceBulkEditView(generic.BulkEditView):
  1437. queryset = Interface.objects.all()
  1438. filterset = filtersets.InterfaceFilterSet
  1439. table = tables.InterfaceTable
  1440. form = forms.InterfaceBulkEditForm
  1441. class InterfaceBulkRenameView(generic.BulkRenameView):
  1442. queryset = Interface.objects.all()
  1443. class InterfaceBulkDisconnectView(BulkDisconnectView):
  1444. queryset = Interface.objects.all()
  1445. class InterfaceBulkDeleteView(generic.BulkDeleteView):
  1446. queryset = Interface.objects.all()
  1447. filterset = filtersets.InterfaceFilterSet
  1448. table = tables.InterfaceTable
  1449. #
  1450. # Front ports
  1451. #
  1452. class FrontPortListView(generic.ObjectListView):
  1453. queryset = FrontPort.objects.all()
  1454. filterset = filtersets.FrontPortFilterSet
  1455. filterset_form = forms.FrontPortFilterForm
  1456. table = tables.FrontPortTable
  1457. action_buttons = ('import', 'export')
  1458. class FrontPortView(generic.ObjectView):
  1459. queryset = FrontPort.objects.all()
  1460. class FrontPortCreateView(generic.ComponentCreateView):
  1461. queryset = FrontPort.objects.all()
  1462. form = forms.FrontPortCreateForm
  1463. model_form = forms.FrontPortForm
  1464. class FrontPortEditView(generic.ObjectEditView):
  1465. queryset = FrontPort.objects.all()
  1466. model_form = forms.FrontPortForm
  1467. template_name = 'dcim/device_component_edit.html'
  1468. class FrontPortDeleteView(generic.ObjectDeleteView):
  1469. queryset = FrontPort.objects.all()
  1470. class FrontPortBulkImportView(generic.BulkImportView):
  1471. queryset = FrontPort.objects.all()
  1472. model_form = forms.FrontPortCSVForm
  1473. table = tables.FrontPortTable
  1474. class FrontPortBulkEditView(generic.BulkEditView):
  1475. queryset = FrontPort.objects.all()
  1476. filterset = filtersets.FrontPortFilterSet
  1477. table = tables.FrontPortTable
  1478. form = forms.FrontPortBulkEditForm
  1479. class FrontPortBulkRenameView(generic.BulkRenameView):
  1480. queryset = FrontPort.objects.all()
  1481. class FrontPortBulkDisconnectView(BulkDisconnectView):
  1482. queryset = FrontPort.objects.all()
  1483. class FrontPortBulkDeleteView(generic.BulkDeleteView):
  1484. queryset = FrontPort.objects.all()
  1485. filterset = filtersets.FrontPortFilterSet
  1486. table = tables.FrontPortTable
  1487. #
  1488. # Rear ports
  1489. #
  1490. class RearPortListView(generic.ObjectListView):
  1491. queryset = RearPort.objects.all()
  1492. filterset = filtersets.RearPortFilterSet
  1493. filterset_form = forms.RearPortFilterForm
  1494. table = tables.RearPortTable
  1495. action_buttons = ('import', 'export')
  1496. class RearPortView(generic.ObjectView):
  1497. queryset = RearPort.objects.all()
  1498. class RearPortCreateView(generic.ComponentCreateView):
  1499. queryset = RearPort.objects.all()
  1500. form = forms.RearPortCreateForm
  1501. model_form = forms.RearPortForm
  1502. class RearPortEditView(generic.ObjectEditView):
  1503. queryset = RearPort.objects.all()
  1504. model_form = forms.RearPortForm
  1505. template_name = 'dcim/device_component_edit.html'
  1506. class RearPortDeleteView(generic.ObjectDeleteView):
  1507. queryset = RearPort.objects.all()
  1508. class RearPortBulkImportView(generic.BulkImportView):
  1509. queryset = RearPort.objects.all()
  1510. model_form = forms.RearPortCSVForm
  1511. table = tables.RearPortTable
  1512. class RearPortBulkEditView(generic.BulkEditView):
  1513. queryset = RearPort.objects.all()
  1514. filterset = filtersets.RearPortFilterSet
  1515. table = tables.RearPortTable
  1516. form = forms.RearPortBulkEditForm
  1517. class RearPortBulkRenameView(generic.BulkRenameView):
  1518. queryset = RearPort.objects.all()
  1519. class RearPortBulkDisconnectView(BulkDisconnectView):
  1520. queryset = RearPort.objects.all()
  1521. class RearPortBulkDeleteView(generic.BulkDeleteView):
  1522. queryset = RearPort.objects.all()
  1523. filterset = filtersets.RearPortFilterSet
  1524. table = tables.RearPortTable
  1525. #
  1526. # Device bays
  1527. #
  1528. class DeviceBayListView(generic.ObjectListView):
  1529. queryset = DeviceBay.objects.all()
  1530. filterset = filtersets.DeviceBayFilterSet
  1531. filterset_form = forms.DeviceBayFilterForm
  1532. table = tables.DeviceBayTable
  1533. action_buttons = ('import', 'export')
  1534. class DeviceBayView(generic.ObjectView):
  1535. queryset = DeviceBay.objects.all()
  1536. class DeviceBayCreateView(generic.ComponentCreateView):
  1537. queryset = DeviceBay.objects.all()
  1538. form = forms.DeviceBayCreateForm
  1539. model_form = forms.DeviceBayForm
  1540. class DeviceBayEditView(generic.ObjectEditView):
  1541. queryset = DeviceBay.objects.all()
  1542. model_form = forms.DeviceBayForm
  1543. template_name = 'dcim/device_component_edit.html'
  1544. class DeviceBayDeleteView(generic.ObjectDeleteView):
  1545. queryset = DeviceBay.objects.all()
  1546. class DeviceBayPopulateView(generic.ObjectEditView):
  1547. queryset = DeviceBay.objects.all()
  1548. def get(self, request, pk):
  1549. device_bay = get_object_or_404(self.queryset, pk=pk)
  1550. form = forms.PopulateDeviceBayForm(device_bay)
  1551. return render(request, 'dcim/devicebay_populate.html', {
  1552. 'device_bay': device_bay,
  1553. 'form': form,
  1554. 'return_url': self.get_return_url(request, device_bay),
  1555. })
  1556. def post(self, request, pk):
  1557. device_bay = get_object_or_404(self.queryset, pk=pk)
  1558. form = forms.PopulateDeviceBayForm(device_bay, request.POST)
  1559. if form.is_valid():
  1560. device_bay.installed_device = form.cleaned_data['installed_device']
  1561. device_bay.save()
  1562. messages.success(request, "Added {} to {}.".format(device_bay.installed_device, device_bay))
  1563. return redirect('dcim:device', pk=device_bay.device.pk)
  1564. return render(request, 'dcim/devicebay_populate.html', {
  1565. 'device_bay': device_bay,
  1566. 'form': form,
  1567. 'return_url': self.get_return_url(request, device_bay),
  1568. })
  1569. class DeviceBayDepopulateView(generic.ObjectEditView):
  1570. queryset = DeviceBay.objects.all()
  1571. def get(self, request, pk):
  1572. device_bay = get_object_or_404(self.queryset, pk=pk)
  1573. form = ConfirmationForm()
  1574. return render(request, 'dcim/devicebay_depopulate.html', {
  1575. 'device_bay': device_bay,
  1576. 'form': form,
  1577. 'return_url': self.get_return_url(request, device_bay),
  1578. })
  1579. def post(self, request, pk):
  1580. device_bay = get_object_or_404(self.queryset, pk=pk)
  1581. form = ConfirmationForm(request.POST)
  1582. if form.is_valid():
  1583. removed_device = device_bay.installed_device
  1584. device_bay.installed_device = None
  1585. device_bay.save()
  1586. messages.success(request, "{} has been removed from {}.".format(removed_device, device_bay))
  1587. return redirect('dcim:device', pk=device_bay.device.pk)
  1588. return render(request, 'dcim/devicebay_depopulate.html', {
  1589. 'device_bay': device_bay,
  1590. 'form': form,
  1591. 'return_url': self.get_return_url(request, device_bay),
  1592. })
  1593. class DeviceBayBulkImportView(generic.BulkImportView):
  1594. queryset = DeviceBay.objects.all()
  1595. model_form = forms.DeviceBayCSVForm
  1596. table = tables.DeviceBayTable
  1597. class DeviceBayBulkEditView(generic.BulkEditView):
  1598. queryset = DeviceBay.objects.all()
  1599. filterset = filtersets.DeviceBayFilterSet
  1600. table = tables.DeviceBayTable
  1601. form = forms.DeviceBayBulkEditForm
  1602. class DeviceBayBulkRenameView(generic.BulkRenameView):
  1603. queryset = DeviceBay.objects.all()
  1604. class DeviceBayBulkDeleteView(generic.BulkDeleteView):
  1605. queryset = DeviceBay.objects.all()
  1606. filterset = filtersets.DeviceBayFilterSet
  1607. table = tables.DeviceBayTable
  1608. #
  1609. # Inventory items
  1610. #
  1611. class InventoryItemListView(generic.ObjectListView):
  1612. queryset = InventoryItem.objects.all()
  1613. filterset = filtersets.InventoryItemFilterSet
  1614. filterset_form = forms.InventoryItemFilterForm
  1615. table = tables.InventoryItemTable
  1616. action_buttons = ('import', 'export')
  1617. class InventoryItemView(generic.ObjectView):
  1618. queryset = InventoryItem.objects.all()
  1619. class InventoryItemEditView(generic.ObjectEditView):
  1620. queryset = InventoryItem.objects.all()
  1621. model_form = forms.InventoryItemForm
  1622. class InventoryItemCreateView(generic.ComponentCreateView):
  1623. queryset = InventoryItem.objects.all()
  1624. form = forms.InventoryItemCreateForm
  1625. model_form = forms.InventoryItemForm
  1626. class InventoryItemDeleteView(generic.ObjectDeleteView):
  1627. queryset = InventoryItem.objects.all()
  1628. class InventoryItemBulkImportView(generic.BulkImportView):
  1629. queryset = InventoryItem.objects.all()
  1630. model_form = forms.InventoryItemCSVForm
  1631. table = tables.InventoryItemTable
  1632. class InventoryItemBulkEditView(generic.BulkEditView):
  1633. queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer')
  1634. filterset = filtersets.InventoryItemFilterSet
  1635. table = tables.InventoryItemTable
  1636. form = forms.InventoryItemBulkEditForm
  1637. class InventoryItemBulkRenameView(generic.BulkRenameView):
  1638. queryset = InventoryItem.objects.all()
  1639. class InventoryItemBulkDeleteView(generic.BulkDeleteView):
  1640. queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer')
  1641. table = tables.InventoryItemTable
  1642. template_name = 'dcim/inventoryitem_bulk_delete.html'
  1643. #
  1644. # Bulk Device component creation
  1645. #
  1646. class DeviceBulkAddConsolePortView(generic.BulkComponentCreateView):
  1647. parent_model = Device
  1648. parent_field = 'device'
  1649. form = forms.ConsolePortBulkCreateForm
  1650. queryset = ConsolePort.objects.all()
  1651. model_form = forms.ConsolePortForm
  1652. filterset = filtersets.DeviceFilterSet
  1653. table = tables.DeviceTable
  1654. default_return_url = 'dcim:device_list'
  1655. class DeviceBulkAddConsoleServerPortView(generic.BulkComponentCreateView):
  1656. parent_model = Device
  1657. parent_field = 'device'
  1658. form = forms.ConsoleServerPortBulkCreateForm
  1659. queryset = ConsoleServerPort.objects.all()
  1660. model_form = forms.ConsoleServerPortForm
  1661. filterset = filtersets.DeviceFilterSet
  1662. table = tables.DeviceTable
  1663. default_return_url = 'dcim:device_list'
  1664. class DeviceBulkAddPowerPortView(generic.BulkComponentCreateView):
  1665. parent_model = Device
  1666. parent_field = 'device'
  1667. form = forms.PowerPortBulkCreateForm
  1668. queryset = PowerPort.objects.all()
  1669. model_form = forms.PowerPortForm
  1670. filterset = filtersets.DeviceFilterSet
  1671. table = tables.DeviceTable
  1672. default_return_url = 'dcim:device_list'
  1673. class DeviceBulkAddPowerOutletView(generic.BulkComponentCreateView):
  1674. parent_model = Device
  1675. parent_field = 'device'
  1676. form = forms.PowerOutletBulkCreateForm
  1677. queryset = PowerOutlet.objects.all()
  1678. model_form = forms.PowerOutletForm
  1679. filterset = filtersets.DeviceFilterSet
  1680. table = tables.DeviceTable
  1681. default_return_url = 'dcim:device_list'
  1682. class DeviceBulkAddInterfaceView(generic.BulkComponentCreateView):
  1683. parent_model = Device
  1684. parent_field = 'device'
  1685. form = forms.InterfaceBulkCreateForm
  1686. queryset = Interface.objects.all()
  1687. model_form = forms.InterfaceForm
  1688. filterset = filtersets.DeviceFilterSet
  1689. table = tables.DeviceTable
  1690. default_return_url = 'dcim:device_list'
  1691. # class DeviceBulkAddFrontPortView(generic.BulkComponentCreateView):
  1692. # parent_model = Device
  1693. # parent_field = 'device'
  1694. # form = forms.FrontPortBulkCreateForm
  1695. # queryset = FrontPort.objects.all()
  1696. # model_form = forms.FrontPortForm
  1697. # filterset = filtersets.DeviceFilterSet
  1698. # table = tables.DeviceTable
  1699. # default_return_url = 'dcim:device_list'
  1700. class DeviceBulkAddRearPortView(generic.BulkComponentCreateView):
  1701. parent_model = Device
  1702. parent_field = 'device'
  1703. form = forms.RearPortBulkCreateForm
  1704. queryset = RearPort.objects.all()
  1705. model_form = forms.RearPortForm
  1706. filterset = filtersets.DeviceFilterSet
  1707. table = tables.DeviceTable
  1708. default_return_url = 'dcim:device_list'
  1709. class DeviceBulkAddDeviceBayView(generic.BulkComponentCreateView):
  1710. parent_model = Device
  1711. parent_field = 'device'
  1712. form = forms.DeviceBayBulkCreateForm
  1713. queryset = DeviceBay.objects.all()
  1714. model_form = forms.DeviceBayForm
  1715. filterset = filtersets.DeviceFilterSet
  1716. table = tables.DeviceTable
  1717. default_return_url = 'dcim:device_list'
  1718. class DeviceBulkAddInventoryItemView(generic.BulkComponentCreateView):
  1719. parent_model = Device
  1720. parent_field = 'device'
  1721. form = forms.InventoryItemBulkCreateForm
  1722. queryset = InventoryItem.objects.all()
  1723. model_form = forms.InventoryItemForm
  1724. filterset = filtersets.DeviceFilterSet
  1725. table = tables.DeviceTable
  1726. default_return_url = 'dcim:device_list'
  1727. #
  1728. # Cables
  1729. #
  1730. class CableListView(generic.ObjectListView):
  1731. queryset = Cable.objects.all()
  1732. filterset = filtersets.CableFilterSet
  1733. filterset_form = forms.CableFilterForm
  1734. table = tables.CableTable
  1735. action_buttons = ('import', 'export')
  1736. class CableView(generic.ObjectView):
  1737. queryset = Cable.objects.all()
  1738. class PathTraceView(generic.ObjectView):
  1739. """
  1740. Trace a cable path beginning from the given path endpoint (origin).
  1741. """
  1742. additional_permissions = ['dcim.view_cable']
  1743. template_name = 'dcim/cable_trace.html'
  1744. def dispatch(self, request, *args, **kwargs):
  1745. model = kwargs.pop('model')
  1746. self.queryset = model.objects.all()
  1747. return super().dispatch(request, *args, **kwargs)
  1748. def get_extra_context(self, request, instance):
  1749. related_paths = []
  1750. # If tracing a PathEndpoint, locate the CablePath (if one exists) by its origin
  1751. if isinstance(instance, PathEndpoint):
  1752. path = instance._path
  1753. # Otherwise, find all CablePaths which traverse the specified object
  1754. else:
  1755. related_paths = CablePath.objects.filter(path__contains=instance).prefetch_related('origin')
  1756. # Check for specification of a particular path (when tracing pass-through ports)
  1757. try:
  1758. path_id = int(request.GET.get('cablepath_id'))
  1759. except TypeError:
  1760. path_id = None
  1761. if path_id in list(related_paths.values_list('pk', flat=True)):
  1762. path = CablePath.objects.get(pk=path_id)
  1763. else:
  1764. path = related_paths.first()
  1765. # Get the total length of the cable and whether the length is definitive (fully defined)
  1766. total_length, is_definitive = path.get_total_length() if path else (None, False)
  1767. # Determine the path to the SVG trace image
  1768. api_viewname = f"{path.origin._meta.app_label}-api:{path.origin._meta.model_name}-trace"
  1769. svg_url = f"{reverse(api_viewname, kwargs={'pk': path.origin.pk})}?render=svg"
  1770. return {
  1771. 'path': path,
  1772. 'related_paths': related_paths,
  1773. 'total_length': total_length,
  1774. 'is_definitive': is_definitive,
  1775. 'svg_url': svg_url,
  1776. }
  1777. class CableCreateView(generic.ObjectEditView):
  1778. queryset = Cable.objects.all()
  1779. template_name = 'dcim/cable_connect.html'
  1780. def dispatch(self, request, *args, **kwargs):
  1781. # Set the model_form class based on the type of component being connected
  1782. self.model_form = {
  1783. 'console-port': forms.ConnectCableToConsolePortForm,
  1784. 'console-server-port': forms.ConnectCableToConsoleServerPortForm,
  1785. 'power-port': forms.ConnectCableToPowerPortForm,
  1786. 'power-outlet': forms.ConnectCableToPowerOutletForm,
  1787. 'interface': forms.ConnectCableToInterfaceForm,
  1788. 'front-port': forms.ConnectCableToFrontPortForm,
  1789. 'rear-port': forms.ConnectCableToRearPortForm,
  1790. 'power-feed': forms.ConnectCableToPowerFeedForm,
  1791. 'circuit-termination': forms.ConnectCableToCircuitTerminationForm,
  1792. }[kwargs.get('termination_b_type')]
  1793. return super().dispatch(request, *args, **kwargs)
  1794. def alter_obj(self, obj, request, url_args, url_kwargs):
  1795. termination_a_type = url_kwargs.get('termination_a_type')
  1796. termination_a_id = url_kwargs.get('termination_a_id')
  1797. termination_b_type_name = url_kwargs.get('termination_b_type')
  1798. self.termination_b_type = ContentType.objects.get(model=termination_b_type_name.replace('-', ''))
  1799. # Initialize Cable termination attributes
  1800. obj.termination_a = termination_a_type.objects.get(pk=termination_a_id)
  1801. obj.termination_b_type = self.termination_b_type
  1802. return obj
  1803. def get(self, request, *args, **kwargs):
  1804. obj = self.alter_obj(self.get_object(kwargs), request, args, kwargs)
  1805. # Parse initial data manually to avoid setting field values as lists
  1806. initial_data = {k: request.GET[k] for k in request.GET}
  1807. # Set initial site and rack based on side A termination (if not already set)
  1808. termination_a_site = getattr(obj.termination_a.parent_object, 'site', None)
  1809. if termination_a_site and 'termination_b_region' not in initial_data:
  1810. initial_data['termination_b_region'] = termination_a_site.region
  1811. if termination_a_site and 'termination_b_site_group' not in initial_data:
  1812. initial_data['termination_b_site_group'] = termination_a_site.group
  1813. if 'termination_b_site' not in initial_data:
  1814. initial_data['termination_b_site'] = termination_a_site
  1815. if 'termination_b_rack' not in initial_data:
  1816. initial_data['termination_b_rack'] = getattr(obj.termination_a.parent_object, 'rack', None)
  1817. form = self.model_form(instance=obj, initial=initial_data)
  1818. return render(request, self.template_name, {
  1819. 'obj': obj,
  1820. 'obj_type': Cable._meta.verbose_name,
  1821. 'termination_b_type': self.termination_b_type.name,
  1822. 'form': form,
  1823. 'return_url': self.get_return_url(request, obj),
  1824. })
  1825. class CableEditView(generic.ObjectEditView):
  1826. queryset = Cable.objects.all()
  1827. model_form = forms.CableForm
  1828. template_name = 'dcim/cable_edit.html'
  1829. class CableDeleteView(generic.ObjectDeleteView):
  1830. queryset = Cable.objects.all()
  1831. class CableBulkImportView(generic.BulkImportView):
  1832. queryset = Cable.objects.all()
  1833. model_form = forms.CableCSVForm
  1834. table = tables.CableTable
  1835. class CableBulkEditView(generic.BulkEditView):
  1836. queryset = Cable.objects.prefetch_related('termination_a', 'termination_b')
  1837. filterset = filtersets.CableFilterSet
  1838. table = tables.CableTable
  1839. form = forms.CableBulkEditForm
  1840. class CableBulkDeleteView(generic.BulkDeleteView):
  1841. queryset = Cable.objects.prefetch_related('termination_a', 'termination_b')
  1842. filterset = filtersets.CableFilterSet
  1843. table = tables.CableTable
  1844. #
  1845. # Connections
  1846. #
  1847. class ConsoleConnectionsListView(generic.ObjectListView):
  1848. queryset = ConsolePort.objects.filter(_path__isnull=False).order_by('device')
  1849. filterset = filtersets.ConsoleConnectionFilterSet
  1850. filterset_form = forms.ConsoleConnectionFilterForm
  1851. table = tables.ConsoleConnectionTable
  1852. template_name = 'dcim/connections_list.html'
  1853. action_buttons = ('export',)
  1854. def extra_context(self):
  1855. return {
  1856. 'title': 'Console Connections'
  1857. }
  1858. class PowerConnectionsListView(generic.ObjectListView):
  1859. queryset = PowerPort.objects.filter(_path__isnull=False).order_by('device')
  1860. filterset = filtersets.PowerConnectionFilterSet
  1861. filterset_form = forms.PowerConnectionFilterForm
  1862. table = tables.PowerConnectionTable
  1863. template_name = 'dcim/connections_list.html'
  1864. action_buttons = ('export',)
  1865. def extra_context(self):
  1866. return {
  1867. 'title': 'Power Connections'
  1868. }
  1869. class InterfaceConnectionsListView(generic.ObjectListView):
  1870. queryset = Interface.objects.filter(_path__isnull=False).order_by('device')
  1871. filterset = filtersets.InterfaceConnectionFilterSet
  1872. filterset_form = forms.InterfaceConnectionFilterForm
  1873. table = tables.InterfaceConnectionTable
  1874. template_name = 'dcim/connections_list.html'
  1875. action_buttons = ('export',)
  1876. def extra_context(self):
  1877. return {
  1878. 'title': 'Interface Connections'
  1879. }
  1880. #
  1881. # Virtual chassis
  1882. #
  1883. class VirtualChassisListView(generic.ObjectListView):
  1884. queryset = VirtualChassis.objects.prefetch_related('master').annotate(
  1885. member_count=count_related(Device, 'virtual_chassis')
  1886. )
  1887. table = tables.VirtualChassisTable
  1888. filterset = filtersets.VirtualChassisFilterSet
  1889. filterset_form = forms.VirtualChassisFilterForm
  1890. class VirtualChassisView(generic.ObjectView):
  1891. queryset = VirtualChassis.objects.all()
  1892. def get_extra_context(self, request, instance):
  1893. members = Device.objects.restrict(request.user).filter(virtual_chassis=instance)
  1894. return {
  1895. 'members': members,
  1896. }
  1897. class VirtualChassisCreateView(generic.ObjectEditView):
  1898. queryset = VirtualChassis.objects.all()
  1899. model_form = forms.VirtualChassisCreateForm
  1900. template_name = 'dcim/virtualchassis_add.html'
  1901. class VirtualChassisEditView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View):
  1902. queryset = VirtualChassis.objects.all()
  1903. def get_required_permission(self):
  1904. return 'dcim.change_virtualchassis'
  1905. def get(self, request, pk):
  1906. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  1907. VCMemberFormSet = modelformset_factory(
  1908. model=Device,
  1909. form=forms.DeviceVCMembershipForm,
  1910. formset=forms.BaseVCMemberFormSet,
  1911. extra=0
  1912. )
  1913. members_queryset = virtual_chassis.members.prefetch_related('rack').order_by('vc_position')
  1914. vc_form = forms.VirtualChassisForm(instance=virtual_chassis)
  1915. vc_form.fields['master'].queryset = members_queryset
  1916. formset = VCMemberFormSet(queryset=members_queryset)
  1917. return render(request, 'dcim/virtualchassis_edit.html', {
  1918. 'vc_form': vc_form,
  1919. 'formset': formset,
  1920. 'return_url': self.get_return_url(request, virtual_chassis),
  1921. })
  1922. def post(self, request, pk):
  1923. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  1924. VCMemberFormSet = modelformset_factory(
  1925. model=Device,
  1926. form=forms.DeviceVCMembershipForm,
  1927. formset=forms.BaseVCMemberFormSet,
  1928. extra=0
  1929. )
  1930. members_queryset = virtual_chassis.members.prefetch_related('rack').order_by('vc_position')
  1931. vc_form = forms.VirtualChassisForm(request.POST, instance=virtual_chassis)
  1932. vc_form.fields['master'].queryset = members_queryset
  1933. formset = VCMemberFormSet(request.POST, queryset=members_queryset)
  1934. if vc_form.is_valid() and formset.is_valid():
  1935. with transaction.atomic():
  1936. # Save the VirtualChassis
  1937. vc_form.save()
  1938. # Nullify the vc_position of each member first to allow reordering without raising an IntegrityError on
  1939. # duplicate positions. Then save each member instance.
  1940. members = formset.save(commit=False)
  1941. devices = Device.objects.filter(pk__in=[m.pk for m in members])
  1942. for device in devices:
  1943. device.vc_position = None
  1944. device.save()
  1945. for member in members:
  1946. member.save()
  1947. return redirect(virtual_chassis.get_absolute_url())
  1948. return render(request, 'dcim/virtualchassis_edit.html', {
  1949. 'vc_form': vc_form,
  1950. 'formset': formset,
  1951. 'return_url': self.get_return_url(request, virtual_chassis),
  1952. })
  1953. class VirtualChassisDeleteView(generic.ObjectDeleteView):
  1954. queryset = VirtualChassis.objects.all()
  1955. class VirtualChassisAddMemberView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View):
  1956. queryset = VirtualChassis.objects.all()
  1957. def get_required_permission(self):
  1958. return 'dcim.change_virtualchassis'
  1959. def get(self, request, pk):
  1960. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  1961. initial_data = {k: request.GET[k] for k in request.GET}
  1962. member_select_form = forms.VCMemberSelectForm(initial=initial_data)
  1963. membership_form = forms.DeviceVCMembershipForm(initial=initial_data)
  1964. return render(request, 'dcim/virtualchassis_add_member.html', {
  1965. 'virtual_chassis': virtual_chassis,
  1966. 'member_select_form': member_select_form,
  1967. 'membership_form': membership_form,
  1968. 'return_url': self.get_return_url(request, virtual_chassis),
  1969. })
  1970. def post(self, request, pk):
  1971. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  1972. member_select_form = forms.VCMemberSelectForm(request.POST)
  1973. if member_select_form.is_valid():
  1974. device = member_select_form.cleaned_data['device']
  1975. device.virtual_chassis = virtual_chassis
  1976. data = {k: request.POST[k] for k in ['vc_position', 'vc_priority']}
  1977. membership_form = forms.DeviceVCMembershipForm(data=data, validate_vc_position=True, instance=device)
  1978. if membership_form.is_valid():
  1979. membership_form.save()
  1980. msg = 'Added member <a href="{}">{}</a>'.format(device.get_absolute_url(), escape(device))
  1981. messages.success(request, mark_safe(msg))
  1982. if '_addanother' in request.POST:
  1983. return redirect(request.get_full_path())
  1984. return redirect(self.get_return_url(request, device))
  1985. else:
  1986. membership_form = forms.DeviceVCMembershipForm(data=request.POST)
  1987. return render(request, 'dcim/virtualchassis_add_member.html', {
  1988. 'virtual_chassis': virtual_chassis,
  1989. 'member_select_form': member_select_form,
  1990. 'membership_form': membership_form,
  1991. 'return_url': self.get_return_url(request, virtual_chassis),
  1992. })
  1993. class VirtualChassisRemoveMemberView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View):
  1994. queryset = Device.objects.all()
  1995. def get_required_permission(self):
  1996. return 'dcim.change_device'
  1997. def get(self, request, pk):
  1998. device = get_object_or_404(self.queryset, pk=pk, virtual_chassis__isnull=False)
  1999. form = ConfirmationForm(initial=request.GET)
  2000. return render(request, 'dcim/virtualchassis_remove_member.html', {
  2001. 'device': device,
  2002. 'form': form,
  2003. 'return_url': self.get_return_url(request, device),
  2004. })
  2005. def post(self, request, pk):
  2006. device = get_object_or_404(self.queryset, pk=pk, virtual_chassis__isnull=False)
  2007. form = ConfirmationForm(request.POST)
  2008. # Protect master device from being removed
  2009. virtual_chassis = VirtualChassis.objects.filter(master=device).first()
  2010. if virtual_chassis is not None:
  2011. msg = 'Unable to remove master device {} from the virtual chassis.'.format(escape(device))
  2012. messages.error(request, mark_safe(msg))
  2013. return redirect(device.get_absolute_url())
  2014. if form.is_valid():
  2015. devices = Device.objects.filter(pk=device.pk)
  2016. for device in devices:
  2017. device.virtual_chassis = None
  2018. device.vc_position = None
  2019. device.vc_priority = None
  2020. device.save()
  2021. msg = 'Removed {} from virtual chassis {}'.format(device, device.virtual_chassis)
  2022. messages.success(request, msg)
  2023. return redirect(self.get_return_url(request, device))
  2024. return render(request, 'dcim/virtualchassis_remove_member.html', {
  2025. 'device': device,
  2026. 'form': form,
  2027. 'return_url': self.get_return_url(request, device),
  2028. })
  2029. class VirtualChassisBulkImportView(generic.BulkImportView):
  2030. queryset = VirtualChassis.objects.all()
  2031. model_form = forms.VirtualChassisCSVForm
  2032. table = tables.VirtualChassisTable
  2033. class VirtualChassisBulkEditView(generic.BulkEditView):
  2034. queryset = VirtualChassis.objects.all()
  2035. filterset = filtersets.VirtualChassisFilterSet
  2036. table = tables.VirtualChassisTable
  2037. form = forms.VirtualChassisBulkEditForm
  2038. class VirtualChassisBulkDeleteView(generic.BulkDeleteView):
  2039. queryset = VirtualChassis.objects.all()
  2040. filterset = filtersets.VirtualChassisFilterSet
  2041. table = tables.VirtualChassisTable
  2042. #
  2043. # Power panels
  2044. #
  2045. class PowerPanelListView(generic.ObjectListView):
  2046. queryset = PowerPanel.objects.prefetch_related(
  2047. 'site', 'location'
  2048. ).annotate(
  2049. powerfeed_count=count_related(PowerFeed, 'power_panel')
  2050. )
  2051. filterset = filtersets.PowerPanelFilterSet
  2052. filterset_form = forms.PowerPanelFilterForm
  2053. table = tables.PowerPanelTable
  2054. class PowerPanelView(generic.ObjectView):
  2055. queryset = PowerPanel.objects.prefetch_related('site', 'location')
  2056. def get_extra_context(self, request, instance):
  2057. power_feeds = PowerFeed.objects.restrict(request.user).filter(power_panel=instance).prefetch_related('rack')
  2058. powerfeed_table = tables.PowerFeedTable(
  2059. data=power_feeds,
  2060. orderable=False
  2061. )
  2062. if request.user.has_perm('dcim.delete_cable'):
  2063. powerfeed_table.columns.show('pk')
  2064. powerfeed_table.exclude = ['power_panel']
  2065. return {
  2066. 'powerfeed_table': powerfeed_table,
  2067. }
  2068. class PowerPanelEditView(generic.ObjectEditView):
  2069. queryset = PowerPanel.objects.all()
  2070. model_form = forms.PowerPanelForm
  2071. class PowerPanelDeleteView(generic.ObjectDeleteView):
  2072. queryset = PowerPanel.objects.all()
  2073. class PowerPanelBulkImportView(generic.BulkImportView):
  2074. queryset = PowerPanel.objects.all()
  2075. model_form = forms.PowerPanelCSVForm
  2076. table = tables.PowerPanelTable
  2077. class PowerPanelBulkEditView(generic.BulkEditView):
  2078. queryset = PowerPanel.objects.prefetch_related('site', 'location')
  2079. filterset = filtersets.PowerPanelFilterSet
  2080. table = tables.PowerPanelTable
  2081. form = forms.PowerPanelBulkEditForm
  2082. class PowerPanelBulkDeleteView(generic.BulkDeleteView):
  2083. queryset = PowerPanel.objects.prefetch_related(
  2084. 'site', 'location'
  2085. ).annotate(
  2086. powerfeed_count=count_related(PowerFeed, 'power_panel')
  2087. )
  2088. filterset = filtersets.PowerPanelFilterSet
  2089. table = tables.PowerPanelTable
  2090. #
  2091. # Power feeds
  2092. #
  2093. class PowerFeedListView(generic.ObjectListView):
  2094. queryset = PowerFeed.objects.all()
  2095. filterset = filtersets.PowerFeedFilterSet
  2096. filterset_form = forms.PowerFeedFilterForm
  2097. table = tables.PowerFeedTable
  2098. class PowerFeedView(generic.ObjectView):
  2099. queryset = PowerFeed.objects.prefetch_related('power_panel', 'rack')
  2100. class PowerFeedEditView(generic.ObjectEditView):
  2101. queryset = PowerFeed.objects.all()
  2102. model_form = forms.PowerFeedForm
  2103. class PowerFeedDeleteView(generic.ObjectDeleteView):
  2104. queryset = PowerFeed.objects.all()
  2105. class PowerFeedBulkImportView(generic.BulkImportView):
  2106. queryset = PowerFeed.objects.all()
  2107. model_form = forms.PowerFeedCSVForm
  2108. table = tables.PowerFeedTable
  2109. class PowerFeedBulkEditView(generic.BulkEditView):
  2110. queryset = PowerFeed.objects.prefetch_related('power_panel', 'rack')
  2111. filterset = filtersets.PowerFeedFilterSet
  2112. table = tables.PowerFeedTable
  2113. form = forms.PowerFeedBulkEditForm
  2114. class PowerFeedBulkDisconnectView(BulkDisconnectView):
  2115. queryset = PowerFeed.objects.all()
  2116. class PowerFeedBulkDeleteView(generic.BulkDeleteView):
  2117. queryset = PowerFeed.objects.prefetch_related('power_panel', 'rack')
  2118. filterset = filtersets.PowerFeedFilterSet
  2119. table = tables.PowerFeedTable