views.py 87 KB

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