views.py 133 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061
  1. from django.contrib import messages
  2. from django.contrib.contenttypes.models import ContentType
  3. from django.core.paginator import EmptyPage, PageNotAnInteger
  4. from django.db import transaction
  5. from django.db.models import Prefetch
  6. from django.forms import ModelMultipleChoiceField, MultipleHiddenInput, modelformset_factory
  7. from django.shortcuts import get_object_or_404, redirect, render
  8. from django.urls import reverse
  9. from django.utils.html import escape
  10. from django.utils.safestring import mark_safe
  11. from django.utils.translation import gettext_lazy as _
  12. from django.views.generic import View
  13. from circuits.models import Circuit, CircuitTermination
  14. from extras.views import ObjectConfigContextView, ObjectRenderConfigView
  15. from ipam.models import ASN, IPAddress, Prefix, VLANGroup
  16. from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable
  17. from netbox.constants import DEFAULT_ACTION_PERMISSIONS
  18. from netbox.views import generic
  19. from tenancy.views import ObjectContactsView
  20. from utilities.forms import ConfirmationForm
  21. from utilities.paginator import EnhancedPaginator, get_paginate_count
  22. from utilities.permissions import get_permission_for_model
  23. from utilities.query import count_related
  24. from utilities.query_functions import CollateAsChar
  25. from utilities.views import (
  26. GetRelatedModelsMixin, GetReturnURLMixin, ObjectPermissionRequiredMixin, ViewTab, register_model_view
  27. )
  28. from virtualization.filtersets import VirtualMachineFilterSet
  29. from virtualization.forms import VirtualMachineFilterForm
  30. from virtualization.models import Cluster, VirtualMachine
  31. from virtualization.tables import VirtualMachineTable
  32. from wireless.models import WirelessLAN
  33. from . import filtersets, forms, tables
  34. from .choices import DeviceFaceChoices, InterfaceModeChoices
  35. from .models import *
  36. CABLE_TERMINATION_TYPES = {
  37. 'dcim.consoleport': ConsolePort,
  38. 'dcim.consoleserverport': ConsoleServerPort,
  39. 'dcim.powerport': PowerPort,
  40. 'dcim.poweroutlet': PowerOutlet,
  41. 'dcim.interface': Interface,
  42. 'dcim.frontport': FrontPort,
  43. 'dcim.rearport': RearPort,
  44. 'dcim.powerfeed': PowerFeed,
  45. 'circuits.circuittermination': CircuitTermination,
  46. }
  47. class DeviceComponentsView(generic.ObjectChildrenView):
  48. actions = {
  49. **DEFAULT_ACTION_PERMISSIONS,
  50. 'bulk_rename': {'change'},
  51. 'bulk_disconnect': {'change'},
  52. }
  53. queryset = Device.objects.all()
  54. def get_children(self, request, parent):
  55. return self.child_model.objects.restrict(request.user, 'view').filter(device=parent)
  56. class DeviceTypeComponentsView(generic.ObjectChildrenView):
  57. actions = {
  58. **DEFAULT_ACTION_PERMISSIONS,
  59. 'bulk_rename': {'change'},
  60. }
  61. queryset = DeviceType.objects.all()
  62. template_name = 'dcim/devicetype/component_templates.html'
  63. viewname = None # Used for return_url resolution
  64. def get_children(self, request, parent):
  65. return self.child_model.objects.restrict(request.user, 'view').filter(device_type=parent)
  66. def get_extra_context(self, request, instance):
  67. return {
  68. 'return_url': reverse(self.viewname, kwargs={'pk': instance.pk}),
  69. }
  70. class ModuleTypeComponentsView(DeviceComponentsView):
  71. queryset = ModuleType.objects.all()
  72. template_name = 'dcim/moduletype/component_templates.html'
  73. viewname = None # Used for return_url resolution
  74. def get_children(self, request, parent):
  75. return self.child_model.objects.restrict(request.user, 'view').filter(module_type=parent)
  76. def get_extra_context(self, request, instance):
  77. return {
  78. 'return_url': reverse(self.viewname, kwargs={'pk': instance.pk}),
  79. }
  80. class BulkDisconnectView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
  81. """
  82. An extendable view for disconnection console/power/interface components in bulk.
  83. """
  84. queryset = None
  85. template_name = 'dcim/bulk_disconnect.html'
  86. def __init__(self, *args, **kwargs):
  87. super().__init__(*args, **kwargs)
  88. # Create a new Form class from ConfirmationForm
  89. class _Form(ConfirmationForm):
  90. pk = ModelMultipleChoiceField(
  91. queryset=self.queryset,
  92. widget=MultipleHiddenInput()
  93. )
  94. self.form = _Form
  95. def get_required_permission(self):
  96. return get_permission_for_model(self.queryset.model, 'change')
  97. def post(self, request):
  98. selected_objects = []
  99. return_url = self.get_return_url(request)
  100. if '_confirm' in request.POST:
  101. form = self.form(request.POST)
  102. if form.is_valid():
  103. with transaction.atomic():
  104. count = 0
  105. cable_ids = set()
  106. for obj in self.queryset.filter(pk__in=form.cleaned_data['pk']):
  107. if obj.cable:
  108. cable_ids.add(obj.cable.pk)
  109. count += 1
  110. for cable in Cable.objects.filter(pk__in=cable_ids):
  111. cable.delete()
  112. messages.success(request, _("Disconnected {count} {type}").format(
  113. count=count,
  114. type=self.queryset.model._meta.verbose_name_plural
  115. ))
  116. return redirect(return_url)
  117. else:
  118. form = self.form(initial={'pk': request.POST.getlist('pk')})
  119. selected_objects = self.queryset.filter(pk__in=form.initial['pk'])
  120. return render(request, self.template_name, {
  121. 'form': form,
  122. 'obj_type_plural': self.queryset.model._meta.verbose_name_plural,
  123. 'selected_objects': selected_objects,
  124. 'return_url': return_url,
  125. })
  126. class PathTraceView(generic.ObjectView):
  127. """
  128. Trace a cable path beginning from the given path endpoint (origin).
  129. """
  130. additional_permissions = ['dcim.view_cable']
  131. template_name = 'dcim/cable_trace.html'
  132. def dispatch(self, request, *args, **kwargs):
  133. model = kwargs.pop('model')
  134. self.queryset = model.objects.all()
  135. return super().dispatch(request, *args, **kwargs)
  136. def get_extra_context(self, request, instance):
  137. related_paths = []
  138. # If tracing a PathEndpoint, locate the CablePath (if one exists) by its origin
  139. if isinstance(instance, PathEndpoint):
  140. path = instance._path
  141. # Otherwise, find all CablePaths which traverse the specified object
  142. else:
  143. related_paths = CablePath.objects.filter(_nodes__contains=instance)
  144. # Check for specification of a particular path (when tracing pass-through ports)
  145. try:
  146. path_id = int(request.GET.get('cablepath_id'))
  147. except TypeError:
  148. path_id = None
  149. if path_id in list(related_paths.values_list('pk', flat=True)):
  150. path = CablePath.objects.get(pk=path_id)
  151. else:
  152. path = related_paths.first()
  153. # No paths found
  154. if path is None:
  155. return {
  156. 'path': None
  157. }
  158. # Get the total length of the cable and whether the length is definitive (fully defined)
  159. total_length, is_definitive = path.get_total_length() if path else (None, False)
  160. # Determine the path to the SVG trace image
  161. api_viewname = f"{path.origin_type.app_label}-api:{path.origin_type.model}-trace"
  162. svg_url = f"{reverse(api_viewname, kwargs={'pk': path.origins[0].pk})}?render=svg"
  163. return {
  164. 'path': path,
  165. 'related_paths': related_paths,
  166. 'total_length': total_length,
  167. 'is_definitive': is_definitive,
  168. 'svg_url': svg_url,
  169. }
  170. #
  171. # Regions
  172. #
  173. @register_model_view(Region, 'list', path='', detail=False)
  174. class RegionListView(generic.ObjectListView):
  175. queryset = Region.objects.add_related_count(
  176. Region.objects.all(),
  177. Site,
  178. 'region',
  179. 'site_count',
  180. cumulative=True
  181. )
  182. filterset = filtersets.RegionFilterSet
  183. filterset_form = forms.RegionFilterForm
  184. table = tables.RegionTable
  185. @register_model_view(Region)
  186. class RegionView(GetRelatedModelsMixin, generic.ObjectView):
  187. queryset = Region.objects.all()
  188. def get_extra_context(self, request, instance):
  189. regions = instance.get_descendants(include_self=True)
  190. return {
  191. 'related_models': self.get_related_models(
  192. request,
  193. regions,
  194. omit=(Cluster, Prefix, WirelessLAN),
  195. extra=(
  196. (Location.objects.restrict(request.user, 'view').filter(site__region__in=regions), 'region_id'),
  197. (Rack.objects.restrict(request.user, 'view').filter(site__region__in=regions), 'region_id'),
  198. (
  199. Circuit.objects.restrict(request.user, 'view').filter(
  200. terminations___region=instance
  201. ).distinct(),
  202. 'region_id'
  203. ),
  204. # Handle these relations manually to avoid erroneous filter name resolution
  205. (Cluster.objects.restrict(request.user, 'view').filter(_region__in=regions), 'region_id'),
  206. (Prefix.objects.restrict(request.user, 'view').filter(_region__in=regions), 'region_id'),
  207. (WirelessLAN.objects.restrict(request.user, 'view').filter(_region__in=regions), 'region_id'),
  208. ),
  209. ),
  210. }
  211. @register_model_view(Region, 'add', detail=False)
  212. @register_model_view(Region, 'edit')
  213. class RegionEditView(generic.ObjectEditView):
  214. queryset = Region.objects.all()
  215. form = forms.RegionForm
  216. @register_model_view(Region, 'delete')
  217. class RegionDeleteView(generic.ObjectDeleteView):
  218. queryset = Region.objects.all()
  219. @register_model_view(Region, 'bulk_import', detail=False)
  220. class RegionBulkImportView(generic.BulkImportView):
  221. queryset = Region.objects.all()
  222. model_form = forms.RegionImportForm
  223. @register_model_view(Region, 'bulk_edit', path='edit', detail=False)
  224. class RegionBulkEditView(generic.BulkEditView):
  225. queryset = Region.objects.add_related_count(
  226. Region.objects.all(),
  227. Site,
  228. 'region',
  229. 'site_count',
  230. cumulative=True
  231. )
  232. filterset = filtersets.RegionFilterSet
  233. table = tables.RegionTable
  234. form = forms.RegionBulkEditForm
  235. @register_model_view(Region, 'bulk_delete', path='delete', detail=False)
  236. class RegionBulkDeleteView(generic.BulkDeleteView):
  237. queryset = Region.objects.add_related_count(
  238. Region.objects.all(),
  239. Site,
  240. 'region',
  241. 'site_count',
  242. cumulative=True
  243. )
  244. filterset = filtersets.RegionFilterSet
  245. table = tables.RegionTable
  246. @register_model_view(Region, 'contacts')
  247. class RegionContactsView(ObjectContactsView):
  248. queryset = Region.objects.all()
  249. #
  250. # Site groups
  251. #
  252. @register_model_view(SiteGroup, 'list', path='', detail=False)
  253. class SiteGroupListView(generic.ObjectListView):
  254. queryset = SiteGroup.objects.add_related_count(
  255. SiteGroup.objects.all(),
  256. Site,
  257. 'group',
  258. 'site_count',
  259. cumulative=True
  260. )
  261. filterset = filtersets.SiteGroupFilterSet
  262. filterset_form = forms.SiteGroupFilterForm
  263. table = tables.SiteGroupTable
  264. @register_model_view(SiteGroup)
  265. class SiteGroupView(GetRelatedModelsMixin, generic.ObjectView):
  266. queryset = SiteGroup.objects.all()
  267. def get_extra_context(self, request, instance):
  268. groups = instance.get_descendants(include_self=True)
  269. return {
  270. 'related_models': self.get_related_models(
  271. request,
  272. groups,
  273. omit=(Cluster, Prefix, WirelessLAN),
  274. extra=(
  275. (Location.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'),
  276. (Rack.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'),
  277. (
  278. Circuit.objects.restrict(request.user, 'view').filter(
  279. terminations___site_group=instance
  280. ).distinct(),
  281. 'site_group_id'
  282. ),
  283. # Handle these relations manually to avoid erroneous filter name resolution
  284. (
  285. Cluster.objects.restrict(request.user, 'view').filter(_site_group__in=groups),
  286. 'site_group_id'
  287. ),
  288. (
  289. Prefix.objects.restrict(request.user, 'view').filter(_site_group__in=groups),
  290. 'site_group_id'
  291. ),
  292. (
  293. WirelessLAN.objects.restrict(request.user, 'view').filter(_site_group__in=groups),
  294. 'site_group_id'
  295. ),
  296. ),
  297. ),
  298. }
  299. @register_model_view(SiteGroup, 'add', detail=False)
  300. @register_model_view(SiteGroup, 'edit')
  301. class SiteGroupEditView(generic.ObjectEditView):
  302. queryset = SiteGroup.objects.all()
  303. form = forms.SiteGroupForm
  304. @register_model_view(SiteGroup, 'delete')
  305. class SiteGroupDeleteView(generic.ObjectDeleteView):
  306. queryset = SiteGroup.objects.all()
  307. @register_model_view(SiteGroup, 'bulk_import', detail=False)
  308. class SiteGroupBulkImportView(generic.BulkImportView):
  309. queryset = SiteGroup.objects.all()
  310. model_form = forms.SiteGroupImportForm
  311. @register_model_view(SiteGroup, 'bulk_edit', path='edit', detail=False)
  312. class SiteGroupBulkEditView(generic.BulkEditView):
  313. queryset = SiteGroup.objects.add_related_count(
  314. SiteGroup.objects.all(),
  315. Site,
  316. 'group',
  317. 'site_count',
  318. cumulative=True
  319. )
  320. filterset = filtersets.SiteGroupFilterSet
  321. table = tables.SiteGroupTable
  322. form = forms.SiteGroupBulkEditForm
  323. @register_model_view(SiteGroup, 'bulk_delete', path='delete', detail=False)
  324. class SiteGroupBulkDeleteView(generic.BulkDeleteView):
  325. queryset = SiteGroup.objects.add_related_count(
  326. SiteGroup.objects.all(),
  327. Site,
  328. 'group',
  329. 'site_count',
  330. cumulative=True
  331. )
  332. filterset = filtersets.SiteGroupFilterSet
  333. table = tables.SiteGroupTable
  334. @register_model_view(SiteGroup, 'contacts')
  335. class SiteGroupContactsView(ObjectContactsView):
  336. queryset = SiteGroup.objects.all()
  337. #
  338. # Sites
  339. #
  340. @register_model_view(Site, 'list', path='', detail=False)
  341. class SiteListView(generic.ObjectListView):
  342. queryset = Site.objects.annotate(
  343. device_count=count_related(Device, 'site'),
  344. asn_count=count_related(ASN, 'sites')
  345. )
  346. filterset = filtersets.SiteFilterSet
  347. filterset_form = forms.SiteFilterForm
  348. table = tables.SiteTable
  349. @register_model_view(Site)
  350. class SiteView(GetRelatedModelsMixin, generic.ObjectView):
  351. queryset = Site.objects.prefetch_related('tenant__group')
  352. def get_extra_context(self, request, instance):
  353. return {
  354. 'related_models': self.get_related_models(
  355. request,
  356. instance,
  357. omit=(CableTermination, CircuitTermination, Cluster, Prefix, WirelessLAN),
  358. extra=(
  359. (VLANGroup.objects.restrict(request.user, 'view').filter(
  360. scope_type=ContentType.objects.get_for_model(Site),
  361. scope_id=instance.pk
  362. ), 'site'),
  363. (ASN.objects.restrict(request.user, 'view').filter(sites=instance), 'site_id'),
  364. (
  365. Circuit.objects.restrict(request.user, 'view').filter(terminations___site=instance).distinct(),
  366. 'site_id'
  367. ),
  368. # Handle these relations manually to avoid erroneous filter name resolution
  369. (Cluster.objects.restrict(request.user, 'view').filter(_site=instance), 'site_id'),
  370. (Prefix.objects.restrict(request.user, 'view').filter(_site=instance), 'site_id'),
  371. (WirelessLAN.objects.restrict(request.user, 'view').filter(_site=instance), 'site_id'),
  372. ),
  373. ),
  374. }
  375. @register_model_view(Site, 'add', detail=False)
  376. @register_model_view(Site, 'edit')
  377. class SiteEditView(generic.ObjectEditView):
  378. queryset = Site.objects.all()
  379. form = forms.SiteForm
  380. @register_model_view(Site, 'delete')
  381. class SiteDeleteView(generic.ObjectDeleteView):
  382. queryset = Site.objects.all()
  383. @register_model_view(Site, 'bulk_import', detail=False)
  384. class SiteBulkImportView(generic.BulkImportView):
  385. queryset = Site.objects.all()
  386. model_form = forms.SiteImportForm
  387. @register_model_view(Site, 'bulk_edit', path='edit', detail=False)
  388. class SiteBulkEditView(generic.BulkEditView):
  389. queryset = Site.objects.all()
  390. filterset = filtersets.SiteFilterSet
  391. table = tables.SiteTable
  392. form = forms.SiteBulkEditForm
  393. @register_model_view(Site, 'bulk_delete', path='delete', detail=False)
  394. class SiteBulkDeleteView(generic.BulkDeleteView):
  395. queryset = Site.objects.all()
  396. filterset = filtersets.SiteFilterSet
  397. table = tables.SiteTable
  398. @register_model_view(Site, 'contacts')
  399. class SiteContactsView(ObjectContactsView):
  400. queryset = Site.objects.all()
  401. #
  402. # Locations
  403. #
  404. @register_model_view(Location, 'list', path='', detail=False)
  405. class LocationListView(generic.ObjectListView):
  406. queryset = Location.objects.add_related_count(
  407. Location.objects.add_related_count(
  408. Location.objects.all(),
  409. Device,
  410. 'location',
  411. 'device_count',
  412. cumulative=True
  413. ),
  414. Rack,
  415. 'location',
  416. 'rack_count',
  417. cumulative=True
  418. )
  419. filterset = filtersets.LocationFilterSet
  420. filterset_form = forms.LocationFilterForm
  421. table = tables.LocationTable
  422. @register_model_view(Location)
  423. class LocationView(GetRelatedModelsMixin, generic.ObjectView):
  424. queryset = Location.objects.all()
  425. def get_extra_context(self, request, instance):
  426. locations = instance.get_descendants(include_self=True)
  427. return {
  428. 'related_models': self.get_related_models(
  429. request,
  430. locations,
  431. omit=[CableTermination, Cluster, Prefix, WirelessLAN],
  432. extra=(
  433. (
  434. Circuit.objects.restrict(request.user, 'view').filter(
  435. terminations___location=instance
  436. ).distinct(),
  437. 'location_id'
  438. ),
  439. # Handle these relations manually to avoid erroneous filter name resolution
  440. (Cluster.objects.restrict(request.user, 'view').filter(_location=instance), 'location_id'),
  441. (Prefix.objects.restrict(request.user, 'view').filter(_location=instance), 'location_id'),
  442. (WirelessLAN.objects.restrict(request.user, 'view').filter(_location=instance), 'location_id'),
  443. ),
  444. ),
  445. }
  446. @register_model_view(Location, 'add', detail=False)
  447. @register_model_view(Location, 'edit')
  448. class LocationEditView(generic.ObjectEditView):
  449. queryset = Location.objects.all()
  450. form = forms.LocationForm
  451. @register_model_view(Location, 'delete')
  452. class LocationDeleteView(generic.ObjectDeleteView):
  453. queryset = Location.objects.all()
  454. @register_model_view(Location, 'bulk_import', detail=False)
  455. class LocationBulkImportView(generic.BulkImportView):
  456. queryset = Location.objects.all()
  457. model_form = forms.LocationImportForm
  458. @register_model_view(Location, 'bulk_edit', path='edit', detail=False)
  459. class LocationBulkEditView(generic.BulkEditView):
  460. queryset = Location.objects.add_related_count(
  461. Location.objects.all(),
  462. Rack,
  463. 'location',
  464. 'rack_count',
  465. cumulative=True
  466. ).prefetch_related('site')
  467. filterset = filtersets.LocationFilterSet
  468. table = tables.LocationTable
  469. form = forms.LocationBulkEditForm
  470. @register_model_view(Location, 'bulk_delete', path='delete', detail=False)
  471. class LocationBulkDeleteView(generic.BulkDeleteView):
  472. queryset = Location.objects.add_related_count(
  473. Location.objects.all(),
  474. Rack,
  475. 'location',
  476. 'rack_count',
  477. cumulative=True
  478. ).prefetch_related('site')
  479. filterset = filtersets.LocationFilterSet
  480. table = tables.LocationTable
  481. @register_model_view(Location, 'contacts')
  482. class LocationContactsView(ObjectContactsView):
  483. queryset = Location.objects.all()
  484. #
  485. # Rack roles
  486. #
  487. @register_model_view(RackRole, 'list', path='', detail=False)
  488. class RackRoleListView(generic.ObjectListView):
  489. queryset = RackRole.objects.annotate(
  490. rack_count=count_related(Rack, 'role')
  491. )
  492. filterset = filtersets.RackRoleFilterSet
  493. filterset_form = forms.RackRoleFilterForm
  494. table = tables.RackRoleTable
  495. @register_model_view(RackRole)
  496. class RackRoleView(GetRelatedModelsMixin, generic.ObjectView):
  497. queryset = RackRole.objects.all()
  498. def get_extra_context(self, request, instance):
  499. return {
  500. 'related_models': self.get_related_models(request, instance),
  501. }
  502. @register_model_view(RackRole, 'add', detail=False)
  503. @register_model_view(RackRole, 'edit')
  504. class RackRoleEditView(generic.ObjectEditView):
  505. queryset = RackRole.objects.all()
  506. form = forms.RackRoleForm
  507. @register_model_view(RackRole, 'delete')
  508. class RackRoleDeleteView(generic.ObjectDeleteView):
  509. queryset = RackRole.objects.all()
  510. @register_model_view(RackRole, 'bulk_import', detail=False)
  511. class RackRoleBulkImportView(generic.BulkImportView):
  512. queryset = RackRole.objects.all()
  513. model_form = forms.RackRoleImportForm
  514. @register_model_view(RackRole, 'bulk_edit', path='edit', detail=False)
  515. class RackRoleBulkEditView(generic.BulkEditView):
  516. queryset = RackRole.objects.annotate(
  517. rack_count=count_related(Rack, 'role')
  518. )
  519. filterset = filtersets.RackRoleFilterSet
  520. table = tables.RackRoleTable
  521. form = forms.RackRoleBulkEditForm
  522. @register_model_view(RackRole, 'bulk_delete', path='delete', detail=False)
  523. class RackRoleBulkDeleteView(generic.BulkDeleteView):
  524. queryset = RackRole.objects.annotate(
  525. rack_count=count_related(Rack, 'role')
  526. )
  527. filterset = filtersets.RackRoleFilterSet
  528. table = tables.RackRoleTable
  529. #
  530. # RackTypes
  531. #
  532. @register_model_view(RackType, 'list', path='', detail=False)
  533. class RackTypeListView(generic.ObjectListView):
  534. queryset = RackType.objects.annotate(
  535. instance_count=count_related(Rack, 'rack_type')
  536. )
  537. filterset = filtersets.RackTypeFilterSet
  538. filterset_form = forms.RackTypeFilterForm
  539. table = tables.RackTypeTable
  540. @register_model_view(RackType)
  541. class RackTypeView(GetRelatedModelsMixin, generic.ObjectView):
  542. queryset = RackType.objects.all()
  543. def get_extra_context(self, request, instance):
  544. return {
  545. 'related_models': self.get_related_models(request, instance),
  546. }
  547. @register_model_view(RackType, 'add', detail=False)
  548. @register_model_view(RackType, 'edit')
  549. class RackTypeEditView(generic.ObjectEditView):
  550. queryset = RackType.objects.all()
  551. form = forms.RackTypeForm
  552. @register_model_view(RackType, 'delete')
  553. class RackTypeDeleteView(generic.ObjectDeleteView):
  554. queryset = RackType.objects.all()
  555. @register_model_view(RackType, 'bulk_import', detail=False)
  556. class RackTypeBulkImportView(generic.BulkImportView):
  557. queryset = RackType.objects.all()
  558. model_form = forms.RackTypeImportForm
  559. @register_model_view(RackType, 'bulk_edit', path='edit', detail=False)
  560. class RackTypeBulkEditView(generic.BulkEditView):
  561. queryset = RackType.objects.all()
  562. filterset = filtersets.RackTypeFilterSet
  563. table = tables.RackTypeTable
  564. form = forms.RackTypeBulkEditForm
  565. @register_model_view(RackType, 'bulk_delete', path='delete', detail=False)
  566. class RackTypeBulkDeleteView(generic.BulkDeleteView):
  567. queryset = RackType.objects.all()
  568. filterset = filtersets.RackTypeFilterSet
  569. table = tables.RackTypeTable
  570. #
  571. # Racks
  572. #
  573. @register_model_view(Rack, 'list', path='', detail=False)
  574. class RackListView(generic.ObjectListView):
  575. queryset = Rack.objects.annotate(
  576. device_count=count_related(Device, 'rack')
  577. )
  578. filterset = filtersets.RackFilterSet
  579. filterset_form = forms.RackFilterForm
  580. table = tables.RackTable
  581. template_name = 'dcim/rack_list.html'
  582. class RackElevationListView(generic.ObjectListView):
  583. """
  584. Display a set of rack elevations side-by-side.
  585. """
  586. queryset = Rack.objects.prefetch_related('role')
  587. def get(self, request):
  588. racks = filtersets.RackFilterSet(request.GET, self.queryset).qs
  589. total_count = racks.count()
  590. # Ordering
  591. ORDERING_CHOICES = {
  592. 'name': 'Name (A-Z)',
  593. '-name': 'Name (Z-A)',
  594. 'facility_id': 'Facility ID (A-Z)',
  595. '-facility_id': 'Facility ID (Z-A)',
  596. }
  597. sort = request.GET.get('sort', 'name')
  598. if sort not in ORDERING_CHOICES:
  599. sort = 'name'
  600. racks = racks.order_by(sort)
  601. # Pagination
  602. per_page = get_paginate_count(request)
  603. page_number = request.GET.get('page', 1)
  604. paginator = EnhancedPaginator(racks, per_page)
  605. try:
  606. page = paginator.page(page_number)
  607. except PageNotAnInteger:
  608. page = paginator.page(1)
  609. except EmptyPage:
  610. page = paginator.page(paginator.num_pages)
  611. # Determine rack face
  612. rack_face = request.GET.get('face', DeviceFaceChoices.FACE_FRONT)
  613. if rack_face not in DeviceFaceChoices.values():
  614. rack_face = DeviceFaceChoices.FACE_FRONT
  615. return render(request, 'dcim/rack_elevation_list.html', {
  616. 'paginator': paginator,
  617. 'page': page,
  618. 'total_count': total_count,
  619. 'sort': sort,
  620. 'sort_display_name': ORDERING_CHOICES[sort],
  621. 'sort_choices': ORDERING_CHOICES,
  622. 'rack_face': rack_face,
  623. 'filter_form': forms.RackElevationFilterForm(request.GET),
  624. 'model': self.queryset.model,
  625. })
  626. @register_model_view(Rack)
  627. class RackView(GetRelatedModelsMixin, generic.ObjectView):
  628. queryset = Rack.objects.prefetch_related('site__region', 'tenant__group', 'location', 'role')
  629. def get_extra_context(self, request, instance):
  630. peer_racks = Rack.objects.restrict(request.user, 'view').filter(site=instance.site)
  631. if instance.location:
  632. peer_racks = peer_racks.filter(location=instance.location)
  633. else:
  634. peer_racks = peer_racks.filter(location__isnull=True)
  635. next_rack = peer_racks.filter(name__gt=instance.name).first()
  636. prev_rack = peer_racks.filter(name__lt=instance.name).reverse().first()
  637. # Determine any additional parameters to pass when embedding the rack elevations
  638. svg_extra = '&'.join([
  639. f'highlight=id:{pk}' for pk in request.GET.getlist('device')
  640. ])
  641. return {
  642. 'related_models': self.get_related_models(request, instance, [CableTermination]),
  643. 'next_rack': next_rack,
  644. 'prev_rack': prev_rack,
  645. 'svg_extra': svg_extra,
  646. 'peer_racks': peer_racks,
  647. }
  648. @register_model_view(Rack, 'reservations')
  649. class RackRackReservationsView(generic.ObjectChildrenView):
  650. queryset = Rack.objects.all()
  651. child_model = RackReservation
  652. table = tables.RackReservationTable
  653. filterset = filtersets.RackReservationFilterSet
  654. filterset_form = forms.RackReservationFilterForm
  655. template_name = 'dcim/rack/reservations.html'
  656. tab = ViewTab(
  657. label=_('Reservations'),
  658. badge=lambda obj: obj.reservations.count(),
  659. permission='dcim.view_rackreservation',
  660. weight=510
  661. )
  662. def get_children(self, request, parent):
  663. return parent.reservations.restrict(request.user, 'view')
  664. @register_model_view(Rack, 'nonracked_devices', 'nonracked-devices')
  665. class RackNonRackedView(generic.ObjectChildrenView):
  666. queryset = Rack.objects.all()
  667. child_model = Device
  668. table = tables.DeviceTable
  669. filterset = filtersets.DeviceFilterSet
  670. filterset_form = forms.DeviceFilterForm
  671. template_name = 'dcim/rack/non_racked_devices.html'
  672. tab = ViewTab(
  673. label=_('Non-Racked Devices'),
  674. badge=lambda obj: obj.devices.filter(rack=obj, position__isnull=True, parent_bay__isnull=True).count(),
  675. weight=500,
  676. permission='dcim.view_device',
  677. )
  678. def get_children(self, request, parent):
  679. return parent.devices.restrict(request.user, 'view').filter(
  680. rack=parent, position__isnull=True, parent_bay__isnull=True
  681. )
  682. @register_model_view(Rack, 'add', detail=False)
  683. @register_model_view(Rack, 'edit')
  684. class RackEditView(generic.ObjectEditView):
  685. queryset = Rack.objects.all()
  686. form = forms.RackForm
  687. @register_model_view(Rack, 'delete')
  688. class RackDeleteView(generic.ObjectDeleteView):
  689. queryset = Rack.objects.all()
  690. @register_model_view(Rack, 'bulk_import', detail=False)
  691. class RackBulkImportView(generic.BulkImportView):
  692. queryset = Rack.objects.all()
  693. model_form = forms.RackImportForm
  694. @register_model_view(Rack, 'bulk_edit', path='edit', detail=False)
  695. class RackBulkEditView(generic.BulkEditView):
  696. queryset = Rack.objects.all()
  697. filterset = filtersets.RackFilterSet
  698. table = tables.RackTable
  699. form = forms.RackBulkEditForm
  700. @register_model_view(Rack, 'bulk_delete', path='delete', detail=False)
  701. class RackBulkDeleteView(generic.BulkDeleteView):
  702. queryset = Rack.objects.all()
  703. filterset = filtersets.RackFilterSet
  704. table = tables.RackTable
  705. @register_model_view(Rack, 'contacts')
  706. class RackContactsView(ObjectContactsView):
  707. queryset = Rack.objects.all()
  708. #
  709. # Rack reservations
  710. #
  711. @register_model_view(RackReservation, 'list', path='', detail=False)
  712. class RackReservationListView(generic.ObjectListView):
  713. queryset = RackReservation.objects.all()
  714. filterset = filtersets.RackReservationFilterSet
  715. filterset_form = forms.RackReservationFilterForm
  716. table = tables.RackReservationTable
  717. @register_model_view(RackReservation)
  718. class RackReservationView(generic.ObjectView):
  719. queryset = RackReservation.objects.all()
  720. @register_model_view(RackReservation, 'add', detail=False)
  721. @register_model_view(RackReservation, 'edit')
  722. class RackReservationEditView(generic.ObjectEditView):
  723. queryset = RackReservation.objects.all()
  724. form = forms.RackReservationForm
  725. def alter_object(self, obj, request, args, kwargs):
  726. if not obj.pk:
  727. if 'rack' in request.GET:
  728. obj.rack = get_object_or_404(Rack, pk=request.GET.get('rack'))
  729. obj.user = request.user
  730. return obj
  731. @register_model_view(RackReservation, 'delete')
  732. class RackReservationDeleteView(generic.ObjectDeleteView):
  733. queryset = RackReservation.objects.all()
  734. @register_model_view(RackReservation, 'bulk_import', detail=False)
  735. class RackReservationImportView(generic.BulkImportView):
  736. queryset = RackReservation.objects.all()
  737. model_form = forms.RackReservationImportForm
  738. def save_object(self, object_form, request):
  739. """
  740. Assign the currently authenticated user to the RackReservation.
  741. """
  742. instance = object_form.save(commit=False)
  743. instance.user = request.user
  744. instance.save()
  745. return instance
  746. @register_model_view(RackReservation, 'bulk_edit', path='edit', detail=False)
  747. class RackReservationBulkEditView(generic.BulkEditView):
  748. queryset = RackReservation.objects.all()
  749. filterset = filtersets.RackReservationFilterSet
  750. table = tables.RackReservationTable
  751. form = forms.RackReservationBulkEditForm
  752. @register_model_view(RackReservation, 'bulk_delete', path='delete', detail=False)
  753. class RackReservationBulkDeleteView(generic.BulkDeleteView):
  754. queryset = RackReservation.objects.all()
  755. filterset = filtersets.RackReservationFilterSet
  756. table = tables.RackReservationTable
  757. #
  758. # Manufacturers
  759. #
  760. @register_model_view(Manufacturer, 'list', path='', detail=False)
  761. class ManufacturerListView(generic.ObjectListView):
  762. queryset = Manufacturer.objects.annotate(
  763. racktype_count=count_related(RackType, 'manufacturer'),
  764. devicetype_count=count_related(DeviceType, 'manufacturer'),
  765. moduletype_count=count_related(ModuleType, 'manufacturer'),
  766. inventoryitem_count=count_related(InventoryItem, 'manufacturer'),
  767. platform_count=count_related(Platform, 'manufacturer')
  768. )
  769. filterset = filtersets.ManufacturerFilterSet
  770. filterset_form = forms.ManufacturerFilterForm
  771. table = tables.ManufacturerTable
  772. @register_model_view(Manufacturer)
  773. class ManufacturerView(GetRelatedModelsMixin, generic.ObjectView):
  774. queryset = Manufacturer.objects.all()
  775. def get_extra_context(self, request, instance):
  776. return {
  777. 'related_models': self.get_related_models(request, instance, [InventoryItemTemplate]),
  778. }
  779. @register_model_view(Manufacturer, 'add', detail=False)
  780. @register_model_view(Manufacturer, 'edit')
  781. class ManufacturerEditView(generic.ObjectEditView):
  782. queryset = Manufacturer.objects.all()
  783. form = forms.ManufacturerForm
  784. @register_model_view(Manufacturer, 'delete')
  785. class ManufacturerDeleteView(generic.ObjectDeleteView):
  786. queryset = Manufacturer.objects.all()
  787. @register_model_view(Manufacturer, 'bulk_import', detail=False)
  788. class ManufacturerBulkImportView(generic.BulkImportView):
  789. queryset = Manufacturer.objects.all()
  790. model_form = forms.ManufacturerImportForm
  791. @register_model_view(Manufacturer, 'bulk_edit', path='edit', detail=False)
  792. class ManufacturerBulkEditView(generic.BulkEditView):
  793. queryset = Manufacturer.objects.annotate(
  794. devicetype_count=count_related(DeviceType, 'manufacturer'),
  795. moduletype_count=count_related(ModuleType, 'manufacturer'),
  796. inventoryitem_count=count_related(InventoryItem, 'manufacturer'),
  797. platform_count=count_related(Platform, 'manufacturer')
  798. )
  799. filterset = filtersets.ManufacturerFilterSet
  800. table = tables.ManufacturerTable
  801. form = forms.ManufacturerBulkEditForm
  802. @register_model_view(Manufacturer, 'bulk_delete', path='delete', detail=False)
  803. class ManufacturerBulkDeleteView(generic.BulkDeleteView):
  804. queryset = Manufacturer.objects.annotate(
  805. devicetype_count=count_related(DeviceType, 'manufacturer'),
  806. moduletype_count=count_related(ModuleType, 'manufacturer'),
  807. inventoryitem_count=count_related(InventoryItem, 'manufacturer'),
  808. platform_count=count_related(Platform, 'manufacturer')
  809. )
  810. filterset = filtersets.ManufacturerFilterSet
  811. table = tables.ManufacturerTable
  812. @register_model_view(Manufacturer, 'contacts')
  813. class ManufacturerContactsView(ObjectContactsView):
  814. queryset = Manufacturer.objects.all()
  815. #
  816. # Device types
  817. #
  818. @register_model_view(DeviceType, 'list', path='', detail=False)
  819. class DeviceTypeListView(generic.ObjectListView):
  820. queryset = DeviceType.objects.annotate(
  821. instance_count=count_related(Device, 'device_type')
  822. )
  823. filterset = filtersets.DeviceTypeFilterSet
  824. filterset_form = forms.DeviceTypeFilterForm
  825. table = tables.DeviceTypeTable
  826. @register_model_view(DeviceType)
  827. class DeviceTypeView(GetRelatedModelsMixin, generic.ObjectView):
  828. queryset = DeviceType.objects.all()
  829. def get_extra_context(self, request, instance):
  830. return {
  831. 'related_models': self.get_related_models(request, instance, omit=[
  832. ConsolePortTemplate, ConsoleServerPortTemplate, DeviceBayTemplate, FrontPortTemplate,
  833. InventoryItemTemplate, InterfaceTemplate, ModuleBayTemplate, PowerOutletTemplate, PowerPortTemplate,
  834. RearPortTemplate,
  835. ]),
  836. }
  837. @register_model_view(DeviceType, 'add', detail=False)
  838. @register_model_view(DeviceType, 'edit')
  839. class DeviceTypeEditView(generic.ObjectEditView):
  840. queryset = DeviceType.objects.all()
  841. form = forms.DeviceTypeForm
  842. @register_model_view(DeviceType, 'delete')
  843. class DeviceTypeDeleteView(generic.ObjectDeleteView):
  844. queryset = DeviceType.objects.all()
  845. @register_model_view(DeviceType, 'consoleports', path='console-ports')
  846. class DeviceTypeConsolePortsView(DeviceTypeComponentsView):
  847. child_model = ConsolePortTemplate
  848. table = tables.ConsolePortTemplateTable
  849. filterset = filtersets.ConsolePortTemplateFilterSet
  850. viewname = 'dcim:devicetype_consoleports'
  851. tab = ViewTab(
  852. label=_('Console Ports'),
  853. badge=lambda obj: obj.console_port_template_count,
  854. permission='dcim.view_consoleporttemplate',
  855. weight=550,
  856. hide_if_empty=True
  857. )
  858. @register_model_view(DeviceType, 'consoleserverports', path='console-server-ports')
  859. class DeviceTypeConsoleServerPortsView(DeviceTypeComponentsView):
  860. child_model = ConsoleServerPortTemplate
  861. table = tables.ConsoleServerPortTemplateTable
  862. filterset = filtersets.ConsoleServerPortTemplateFilterSet
  863. viewname = 'dcim:devicetype_consoleserverports'
  864. tab = ViewTab(
  865. label=_('Console Server Ports'),
  866. badge=lambda obj: obj.console_server_port_template_count,
  867. permission='dcim.view_consoleserverporttemplate',
  868. weight=560,
  869. hide_if_empty=True
  870. )
  871. @register_model_view(DeviceType, 'powerports', path='power-ports')
  872. class DeviceTypePowerPortsView(DeviceTypeComponentsView):
  873. child_model = PowerPortTemplate
  874. table = tables.PowerPortTemplateTable
  875. filterset = filtersets.PowerPortTemplateFilterSet
  876. viewname = 'dcim:devicetype_powerports'
  877. tab = ViewTab(
  878. label=_('Power Ports'),
  879. badge=lambda obj: obj.power_port_template_count,
  880. permission='dcim.view_powerporttemplate',
  881. weight=570,
  882. hide_if_empty=True
  883. )
  884. @register_model_view(DeviceType, 'poweroutlets', path='power-outlets')
  885. class DeviceTypePowerOutletsView(DeviceTypeComponentsView):
  886. child_model = PowerOutletTemplate
  887. table = tables.PowerOutletTemplateTable
  888. filterset = filtersets.PowerOutletTemplateFilterSet
  889. viewname = 'dcim:devicetype_poweroutlets'
  890. tab = ViewTab(
  891. label=_('Power Outlets'),
  892. badge=lambda obj: obj.power_outlet_template_count,
  893. permission='dcim.view_poweroutlettemplate',
  894. weight=580,
  895. hide_if_empty=True
  896. )
  897. @register_model_view(DeviceType, 'interfaces')
  898. class DeviceTypeInterfacesView(DeviceTypeComponentsView):
  899. child_model = InterfaceTemplate
  900. table = tables.InterfaceTemplateTable
  901. filterset = filtersets.InterfaceTemplateFilterSet
  902. viewname = 'dcim:devicetype_interfaces'
  903. tab = ViewTab(
  904. label=_('Interfaces'),
  905. badge=lambda obj: obj.interface_template_count,
  906. permission='dcim.view_interfacetemplate',
  907. weight=520,
  908. hide_if_empty=True
  909. )
  910. @register_model_view(DeviceType, 'frontports', path='front-ports')
  911. class DeviceTypeFrontPortsView(DeviceTypeComponentsView):
  912. child_model = FrontPortTemplate
  913. table = tables.FrontPortTemplateTable
  914. filterset = filtersets.FrontPortTemplateFilterSet
  915. viewname = 'dcim:devicetype_frontports'
  916. tab = ViewTab(
  917. label=_('Front Ports'),
  918. badge=lambda obj: obj.front_port_template_count,
  919. permission='dcim.view_frontporttemplate',
  920. weight=530,
  921. hide_if_empty=True
  922. )
  923. @register_model_view(DeviceType, 'rearports', path='rear-ports')
  924. class DeviceTypeRearPortsView(DeviceTypeComponentsView):
  925. child_model = RearPortTemplate
  926. table = tables.RearPortTemplateTable
  927. filterset = filtersets.RearPortTemplateFilterSet
  928. viewname = 'dcim:devicetype_rearports'
  929. tab = ViewTab(
  930. label=_('Rear Ports'),
  931. badge=lambda obj: obj.rear_port_template_count,
  932. permission='dcim.view_rearporttemplate',
  933. weight=540,
  934. hide_if_empty=True
  935. )
  936. @register_model_view(DeviceType, 'modulebays', path='module-bays')
  937. class DeviceTypeModuleBaysView(DeviceTypeComponentsView):
  938. child_model = ModuleBayTemplate
  939. table = tables.ModuleBayTemplateTable
  940. filterset = filtersets.ModuleBayTemplateFilterSet
  941. viewname = 'dcim:devicetype_modulebays'
  942. tab = ViewTab(
  943. label=_('Module Bays'),
  944. badge=lambda obj: obj.module_bay_template_count,
  945. permission='dcim.view_modulebaytemplate',
  946. weight=510,
  947. hide_if_empty=True
  948. )
  949. @register_model_view(DeviceType, 'devicebays', path='device-bays')
  950. class DeviceTypeDeviceBaysView(DeviceTypeComponentsView):
  951. child_model = DeviceBayTemplate
  952. table = tables.DeviceBayTemplateTable
  953. filterset = filtersets.DeviceBayTemplateFilterSet
  954. viewname = 'dcim:devicetype_devicebays'
  955. tab = ViewTab(
  956. label=_('Device Bays'),
  957. badge=lambda obj: obj.device_bay_template_count,
  958. permission='dcim.view_devicebaytemplate',
  959. weight=500,
  960. hide_if_empty=True
  961. )
  962. @register_model_view(DeviceType, 'inventoryitems', path='inventory-items')
  963. class DeviceTypeInventoryItemsView(DeviceTypeComponentsView):
  964. child_model = InventoryItemTemplate
  965. table = tables.InventoryItemTemplateTable
  966. filterset = filtersets.InventoryItemTemplateFilterSet
  967. viewname = 'dcim:devicetype_inventoryitems'
  968. tab = ViewTab(
  969. label=_('Inventory Items'),
  970. badge=lambda obj: obj.inventory_item_template_count,
  971. permission='dcim.view_inventoryitemtemplate',
  972. weight=590,
  973. hide_if_empty=True
  974. )
  975. @register_model_view(DeviceType, 'bulk_import', detail=False)
  976. class DeviceTypeImportView(generic.BulkImportView):
  977. additional_permissions = [
  978. 'dcim.add_devicetype',
  979. 'dcim.add_consoleporttemplate',
  980. 'dcim.add_consoleserverporttemplate',
  981. 'dcim.add_powerporttemplate',
  982. 'dcim.add_poweroutlettemplate',
  983. 'dcim.add_interfacetemplate',
  984. 'dcim.add_frontporttemplate',
  985. 'dcim.add_rearporttemplate',
  986. 'dcim.add_modulebaytemplate',
  987. 'dcim.add_devicebaytemplate',
  988. 'dcim.add_inventoryitemtemplate',
  989. ]
  990. queryset = DeviceType.objects.all()
  991. model_form = forms.DeviceTypeImportForm
  992. related_object_forms = {
  993. 'console-ports': forms.ConsolePortTemplateImportForm,
  994. 'console-server-ports': forms.ConsoleServerPortTemplateImportForm,
  995. 'power-ports': forms.PowerPortTemplateImportForm,
  996. 'power-outlets': forms.PowerOutletTemplateImportForm,
  997. 'interfaces': forms.InterfaceTemplateImportForm,
  998. 'rear-ports': forms.RearPortTemplateImportForm,
  999. 'front-ports': forms.FrontPortTemplateImportForm,
  1000. 'module-bays': forms.ModuleBayTemplateImportForm,
  1001. 'device-bays': forms.DeviceBayTemplateImportForm,
  1002. 'inventory-items': forms.InventoryItemTemplateImportForm,
  1003. }
  1004. def prep_related_object_data(self, parent, data):
  1005. data.update({'device_type': parent})
  1006. return data
  1007. @register_model_view(DeviceType, 'bulk_edit', path='edit', detail=False)
  1008. class DeviceTypeBulkEditView(generic.BulkEditView):
  1009. queryset = DeviceType.objects.annotate(
  1010. instance_count=count_related(Device, 'device_type')
  1011. )
  1012. filterset = filtersets.DeviceTypeFilterSet
  1013. table = tables.DeviceTypeTable
  1014. form = forms.DeviceTypeBulkEditForm
  1015. @register_model_view(DeviceType, 'bulk_delete', path='delete', detail=False)
  1016. class DeviceTypeBulkDeleteView(generic.BulkDeleteView):
  1017. queryset = DeviceType.objects.annotate(
  1018. instance_count=count_related(Device, 'device_type')
  1019. )
  1020. filterset = filtersets.DeviceTypeFilterSet
  1021. table = tables.DeviceTypeTable
  1022. #
  1023. # Module types
  1024. #
  1025. @register_model_view(ModuleType, 'list', path='', detail=False)
  1026. class ModuleTypeListView(generic.ObjectListView):
  1027. queryset = ModuleType.objects.annotate(
  1028. instance_count=count_related(Module, 'module_type')
  1029. )
  1030. filterset = filtersets.ModuleTypeFilterSet
  1031. filterset_form = forms.ModuleTypeFilterForm
  1032. table = tables.ModuleTypeTable
  1033. @register_model_view(ModuleType)
  1034. class ModuleTypeView(GetRelatedModelsMixin, generic.ObjectView):
  1035. queryset = ModuleType.objects.all()
  1036. def get_extra_context(self, request, instance):
  1037. return {
  1038. 'related_models': self.get_related_models(request, instance, omit=[
  1039. ConsolePortTemplate, ConsoleServerPortTemplate, DeviceBayTemplate, FrontPortTemplate,
  1040. InventoryItemTemplate, InterfaceTemplate, ModuleBayTemplate, PowerOutletTemplate, PowerPortTemplate,
  1041. RearPortTemplate,
  1042. ]),
  1043. }
  1044. @register_model_view(ModuleType, 'add', detail=False)
  1045. @register_model_view(ModuleType, 'edit')
  1046. class ModuleTypeEditView(generic.ObjectEditView):
  1047. queryset = ModuleType.objects.all()
  1048. form = forms.ModuleTypeForm
  1049. @register_model_view(ModuleType, 'delete')
  1050. class ModuleTypeDeleteView(generic.ObjectDeleteView):
  1051. queryset = ModuleType.objects.all()
  1052. @register_model_view(ModuleType, 'consoleports', path='console-ports')
  1053. class ModuleTypeConsolePortsView(ModuleTypeComponentsView):
  1054. child_model = ConsolePortTemplate
  1055. table = tables.ConsolePortTemplateTable
  1056. filterset = filtersets.ConsolePortTemplateFilterSet
  1057. viewname = 'dcim:moduletype_consoleports'
  1058. tab = ViewTab(
  1059. label=_('Console Ports'),
  1060. badge=lambda obj: obj.consoleporttemplates.count(),
  1061. permission='dcim.view_consoleporttemplate',
  1062. weight=530,
  1063. hide_if_empty=True
  1064. )
  1065. @register_model_view(ModuleType, 'consoleserverports', path='console-server-ports')
  1066. class ModuleTypeConsoleServerPortsView(ModuleTypeComponentsView):
  1067. child_model = ConsoleServerPortTemplate
  1068. table = tables.ConsoleServerPortTemplateTable
  1069. filterset = filtersets.ConsoleServerPortTemplateFilterSet
  1070. viewname = 'dcim:moduletype_consoleserverports'
  1071. tab = ViewTab(
  1072. label=_('Console Server Ports'),
  1073. badge=lambda obj: obj.consoleserverporttemplates.count(),
  1074. permission='dcim.view_consoleserverporttemplate',
  1075. weight=540,
  1076. hide_if_empty=True
  1077. )
  1078. @register_model_view(ModuleType, 'powerports', path='power-ports')
  1079. class ModuleTypePowerPortsView(ModuleTypeComponentsView):
  1080. child_model = PowerPortTemplate
  1081. table = tables.PowerPortTemplateTable
  1082. filterset = filtersets.PowerPortTemplateFilterSet
  1083. viewname = 'dcim:moduletype_powerports'
  1084. tab = ViewTab(
  1085. label=_('Power Ports'),
  1086. badge=lambda obj: obj.powerporttemplates.count(),
  1087. permission='dcim.view_powerporttemplate',
  1088. weight=550,
  1089. hide_if_empty=True
  1090. )
  1091. @register_model_view(ModuleType, 'poweroutlets', path='power-outlets')
  1092. class ModuleTypePowerOutletsView(ModuleTypeComponentsView):
  1093. child_model = PowerOutletTemplate
  1094. table = tables.PowerOutletTemplateTable
  1095. filterset = filtersets.PowerOutletTemplateFilterSet
  1096. viewname = 'dcim:moduletype_poweroutlets'
  1097. tab = ViewTab(
  1098. label=_('Power Outlets'),
  1099. badge=lambda obj: obj.poweroutlettemplates.count(),
  1100. permission='dcim.view_poweroutlettemplate',
  1101. weight=560,
  1102. hide_if_empty=True
  1103. )
  1104. @register_model_view(ModuleType, 'interfaces')
  1105. class ModuleTypeInterfacesView(ModuleTypeComponentsView):
  1106. child_model = InterfaceTemplate
  1107. table = tables.InterfaceTemplateTable
  1108. filterset = filtersets.InterfaceTemplateFilterSet
  1109. viewname = 'dcim:moduletype_interfaces'
  1110. tab = ViewTab(
  1111. label=_('Interfaces'),
  1112. badge=lambda obj: obj.interfacetemplates.count(),
  1113. permission='dcim.view_interfacetemplate',
  1114. weight=500,
  1115. hide_if_empty=True
  1116. )
  1117. @register_model_view(ModuleType, 'frontports', path='front-ports')
  1118. class ModuleTypeFrontPortsView(ModuleTypeComponentsView):
  1119. child_model = FrontPortTemplate
  1120. table = tables.FrontPortTemplateTable
  1121. filterset = filtersets.FrontPortTemplateFilterSet
  1122. viewname = 'dcim:moduletype_frontports'
  1123. tab = ViewTab(
  1124. label=_('Front Ports'),
  1125. badge=lambda obj: obj.frontporttemplates.count(),
  1126. permission='dcim.view_frontporttemplate',
  1127. weight=510,
  1128. hide_if_empty=True
  1129. )
  1130. @register_model_view(ModuleType, 'rearports', path='rear-ports')
  1131. class ModuleTypeRearPortsView(ModuleTypeComponentsView):
  1132. child_model = RearPortTemplate
  1133. table = tables.RearPortTemplateTable
  1134. filterset = filtersets.RearPortTemplateFilterSet
  1135. viewname = 'dcim:moduletype_rearports'
  1136. tab = ViewTab(
  1137. label=_('Rear Ports'),
  1138. badge=lambda obj: obj.rearporttemplates.count(),
  1139. permission='dcim.view_rearporttemplate',
  1140. weight=520,
  1141. hide_if_empty=True
  1142. )
  1143. @register_model_view(ModuleType, 'modulebays', path='module-bays')
  1144. class ModuleTypeModuleBaysView(ModuleTypeComponentsView):
  1145. child_model = ModuleBayTemplate
  1146. table = tables.ModuleBayTemplateTable
  1147. filterset = filtersets.ModuleBayTemplateFilterSet
  1148. viewname = 'dcim:moduletype_modulebays'
  1149. tab = ViewTab(
  1150. label=_('Module Bays'),
  1151. badge=lambda obj: obj.modulebaytemplates.count(),
  1152. permission='dcim.view_modulebaytemplate',
  1153. weight=570,
  1154. hide_if_empty=True
  1155. )
  1156. @register_model_view(ModuleType, 'bulk_import', detail=False)
  1157. class ModuleTypeImportView(generic.BulkImportView):
  1158. additional_permissions = [
  1159. 'dcim.add_moduletype',
  1160. 'dcim.add_consoleporttemplate',
  1161. 'dcim.add_consoleserverporttemplate',
  1162. 'dcim.add_powerporttemplate',
  1163. 'dcim.add_poweroutlettemplate',
  1164. 'dcim.add_interfacetemplate',
  1165. 'dcim.add_frontporttemplate',
  1166. 'dcim.add_rearporttemplate',
  1167. ]
  1168. queryset = ModuleType.objects.all()
  1169. model_form = forms.ModuleTypeImportForm
  1170. related_object_forms = {
  1171. 'console-ports': forms.ConsolePortTemplateImportForm,
  1172. 'console-server-ports': forms.ConsoleServerPortTemplateImportForm,
  1173. 'power-ports': forms.PowerPortTemplateImportForm,
  1174. 'power-outlets': forms.PowerOutletTemplateImportForm,
  1175. 'interfaces': forms.InterfaceTemplateImportForm,
  1176. 'rear-ports': forms.RearPortTemplateImportForm,
  1177. 'front-ports': forms.FrontPortTemplateImportForm,
  1178. }
  1179. def prep_related_object_data(self, parent, data):
  1180. data.update({'module_type': parent})
  1181. return data
  1182. @register_model_view(ModuleType, 'bulk_edit', path='edit', detail=False)
  1183. class ModuleTypeBulkEditView(generic.BulkEditView):
  1184. queryset = ModuleType.objects.annotate(
  1185. instance_count=count_related(Module, 'module_type')
  1186. )
  1187. filterset = filtersets.ModuleTypeFilterSet
  1188. table = tables.ModuleTypeTable
  1189. form = forms.ModuleTypeBulkEditForm
  1190. @register_model_view(ModuleType, 'bulk_delete', path='delete', detail=False)
  1191. class ModuleTypeBulkDeleteView(generic.BulkDeleteView):
  1192. queryset = ModuleType.objects.annotate(
  1193. instance_count=count_related(Module, 'module_type')
  1194. )
  1195. filterset = filtersets.ModuleTypeFilterSet
  1196. table = tables.ModuleTypeTable
  1197. #
  1198. # Console port templates
  1199. #
  1200. @register_model_view(ConsolePortTemplate, 'add', detail=False)
  1201. class ConsolePortTemplateCreateView(generic.ComponentCreateView):
  1202. queryset = ConsolePortTemplate.objects.all()
  1203. form = forms.ConsolePortTemplateCreateForm
  1204. model_form = forms.ConsolePortTemplateForm
  1205. @register_model_view(ConsolePortTemplate, 'edit')
  1206. class ConsolePortTemplateEditView(generic.ObjectEditView):
  1207. queryset = ConsolePortTemplate.objects.all()
  1208. form = forms.ConsolePortTemplateForm
  1209. @register_model_view(ConsolePortTemplate, 'delete')
  1210. class ConsolePortTemplateDeleteView(generic.ObjectDeleteView):
  1211. queryset = ConsolePortTemplate.objects.all()
  1212. @register_model_view(ConsolePortTemplate, 'bulk_edit', path='edit', detail=False)
  1213. class ConsolePortTemplateBulkEditView(generic.BulkEditView):
  1214. queryset = ConsolePortTemplate.objects.all()
  1215. table = tables.ConsolePortTemplateTable
  1216. form = forms.ConsolePortTemplateBulkEditForm
  1217. @register_model_view(ConsolePortTemplate, 'bulk_rename', path='rename', detail=False)
  1218. class ConsolePortTemplateBulkRenameView(generic.BulkRenameView):
  1219. queryset = ConsolePortTemplate.objects.all()
  1220. @register_model_view(ConsolePortTemplate, 'bulk_delete', path='delete', detail=False)
  1221. class ConsolePortTemplateBulkDeleteView(generic.BulkDeleteView):
  1222. queryset = ConsolePortTemplate.objects.all()
  1223. table = tables.ConsolePortTemplateTable
  1224. #
  1225. # Console server port templates
  1226. #
  1227. @register_model_view(ConsoleServerPortTemplate, 'add', detail=False)
  1228. class ConsoleServerPortTemplateCreateView(generic.ComponentCreateView):
  1229. queryset = ConsoleServerPortTemplate.objects.all()
  1230. form = forms.ConsoleServerPortTemplateCreateForm
  1231. model_form = forms.ConsoleServerPortTemplateForm
  1232. @register_model_view(ConsoleServerPortTemplate, 'edit')
  1233. class ConsoleServerPortTemplateEditView(generic.ObjectEditView):
  1234. queryset = ConsoleServerPortTemplate.objects.all()
  1235. form = forms.ConsoleServerPortTemplateForm
  1236. @register_model_view(ConsoleServerPortTemplate, 'delete')
  1237. class ConsoleServerPortTemplateDeleteView(generic.ObjectDeleteView):
  1238. queryset = ConsoleServerPortTemplate.objects.all()
  1239. @register_model_view(ConsoleServerPortTemplate, 'bulk_edit', path='edit', detail=False)
  1240. class ConsoleServerPortTemplateBulkEditView(generic.BulkEditView):
  1241. queryset = ConsoleServerPortTemplate.objects.all()
  1242. table = tables.ConsoleServerPortTemplateTable
  1243. form = forms.ConsoleServerPortTemplateBulkEditForm
  1244. @register_model_view(ConsoleServerPortTemplate, 'bulk_rename', detail=False)
  1245. class ConsoleServerPortTemplateBulkRenameView(generic.BulkRenameView):
  1246. queryset = ConsoleServerPortTemplate.objects.all()
  1247. @register_model_view(ConsoleServerPortTemplate, 'bulk_delete', path='delete', detail=False)
  1248. class ConsoleServerPortTemplateBulkDeleteView(generic.BulkDeleteView):
  1249. queryset = ConsoleServerPortTemplate.objects.all()
  1250. table = tables.ConsoleServerPortTemplateTable
  1251. #
  1252. # Power port templates
  1253. #
  1254. @register_model_view(PowerPortTemplate, 'add', detail=False)
  1255. class PowerPortTemplateCreateView(generic.ComponentCreateView):
  1256. queryset = PowerPortTemplate.objects.all()
  1257. form = forms.PowerPortTemplateCreateForm
  1258. model_form = forms.PowerPortTemplateForm
  1259. @register_model_view(PowerPortTemplate, 'edit')
  1260. class PowerPortTemplateEditView(generic.ObjectEditView):
  1261. queryset = PowerPortTemplate.objects.all()
  1262. form = forms.PowerPortTemplateForm
  1263. @register_model_view(PowerPortTemplate, 'delete')
  1264. class PowerPortTemplateDeleteView(generic.ObjectDeleteView):
  1265. queryset = PowerPortTemplate.objects.all()
  1266. @register_model_view(PowerPortTemplate, 'bulk_edit', path='edit', detail=False)
  1267. class PowerPortTemplateBulkEditView(generic.BulkEditView):
  1268. queryset = PowerPortTemplate.objects.all()
  1269. table = tables.PowerPortTemplateTable
  1270. form = forms.PowerPortTemplateBulkEditForm
  1271. @register_model_view(PowerPortTemplate, 'bulk_rename', path='rename', detail=False)
  1272. class PowerPortTemplateBulkRenameView(generic.BulkRenameView):
  1273. queryset = PowerPortTemplate.objects.all()
  1274. @register_model_view(PowerPortTemplate, 'bulk_delete', path='delete', detail=False)
  1275. class PowerPortTemplateBulkDeleteView(generic.BulkDeleteView):
  1276. queryset = PowerPortTemplate.objects.all()
  1277. table = tables.PowerPortTemplateTable
  1278. #
  1279. # Power outlet templates
  1280. #
  1281. @register_model_view(PowerOutletTemplate, 'add', detail=False)
  1282. class PowerOutletTemplateCreateView(generic.ComponentCreateView):
  1283. queryset = PowerOutletTemplate.objects.all()
  1284. form = forms.PowerOutletTemplateCreateForm
  1285. model_form = forms.PowerOutletTemplateForm
  1286. @register_model_view(PowerOutletTemplate, 'edit')
  1287. class PowerOutletTemplateEditView(generic.ObjectEditView):
  1288. queryset = PowerOutletTemplate.objects.all()
  1289. form = forms.PowerOutletTemplateForm
  1290. @register_model_view(PowerOutletTemplate, 'delete')
  1291. class PowerOutletTemplateDeleteView(generic.ObjectDeleteView):
  1292. queryset = PowerOutletTemplate.objects.all()
  1293. @register_model_view(PowerOutletTemplate, 'bulk_edit', path='edit', detail=False)
  1294. class PowerOutletTemplateBulkEditView(generic.BulkEditView):
  1295. queryset = PowerOutletTemplate.objects.all()
  1296. table = tables.PowerOutletTemplateTable
  1297. form = forms.PowerOutletTemplateBulkEditForm
  1298. @register_model_view(PowerOutletTemplate, 'bulk_rename', path='rename', detail=False)
  1299. class PowerOutletTemplateBulkRenameView(generic.BulkRenameView):
  1300. queryset = PowerOutletTemplate.objects.all()
  1301. @register_model_view(PowerOutletTemplate, 'bulk_delete', path='delete', detail=False)
  1302. class PowerOutletTemplateBulkDeleteView(generic.BulkDeleteView):
  1303. queryset = PowerOutletTemplate.objects.all()
  1304. table = tables.PowerOutletTemplateTable
  1305. #
  1306. # Interface templates
  1307. #
  1308. @register_model_view(InterfaceTemplate, 'add', detail=False)
  1309. class InterfaceTemplateCreateView(generic.ComponentCreateView):
  1310. queryset = InterfaceTemplate.objects.all()
  1311. form = forms.InterfaceTemplateCreateForm
  1312. model_form = forms.InterfaceTemplateForm
  1313. @register_model_view(InterfaceTemplate, 'edit')
  1314. class InterfaceTemplateEditView(generic.ObjectEditView):
  1315. queryset = InterfaceTemplate.objects.all()
  1316. form = forms.InterfaceTemplateForm
  1317. @register_model_view(InterfaceTemplate, 'delete')
  1318. class InterfaceTemplateDeleteView(generic.ObjectDeleteView):
  1319. queryset = InterfaceTemplate.objects.all()
  1320. @register_model_view(InterfaceTemplate, 'bulk_edit', path='edit', detail=False)
  1321. class InterfaceTemplateBulkEditView(generic.BulkEditView):
  1322. queryset = InterfaceTemplate.objects.all()
  1323. table = tables.InterfaceTemplateTable
  1324. form = forms.InterfaceTemplateBulkEditForm
  1325. @register_model_view(InterfaceTemplate, 'bulk_rename', path='rename', detail=False)
  1326. class InterfaceTemplateBulkRenameView(generic.BulkRenameView):
  1327. queryset = InterfaceTemplate.objects.all()
  1328. @register_model_view(InterfaceTemplate, 'bulk_delete', path='delete', detail=False)
  1329. class InterfaceTemplateBulkDeleteView(generic.BulkDeleteView):
  1330. queryset = InterfaceTemplate.objects.all()
  1331. table = tables.InterfaceTemplateTable
  1332. #
  1333. # Front port templates
  1334. #
  1335. @register_model_view(FrontPortTemplate, 'add', detail=False)
  1336. class FrontPortTemplateCreateView(generic.ComponentCreateView):
  1337. queryset = FrontPortTemplate.objects.all()
  1338. form = forms.FrontPortTemplateCreateForm
  1339. model_form = forms.FrontPortTemplateForm
  1340. @register_model_view(FrontPortTemplate, 'edit')
  1341. class FrontPortTemplateEditView(generic.ObjectEditView):
  1342. queryset = FrontPortTemplate.objects.all()
  1343. form = forms.FrontPortTemplateForm
  1344. @register_model_view(FrontPortTemplate, 'delete')
  1345. class FrontPortTemplateDeleteView(generic.ObjectDeleteView):
  1346. queryset = FrontPortTemplate.objects.all()
  1347. @register_model_view(FrontPortTemplate, 'bulk_edit', path='edit', detail=False)
  1348. class FrontPortTemplateBulkEditView(generic.BulkEditView):
  1349. queryset = FrontPortTemplate.objects.all()
  1350. table = tables.FrontPortTemplateTable
  1351. form = forms.FrontPortTemplateBulkEditForm
  1352. @register_model_view(FrontPortTemplate, 'bulk_rename', path='rename', detail=False)
  1353. class FrontPortTemplateBulkRenameView(generic.BulkRenameView):
  1354. queryset = FrontPortTemplate.objects.all()
  1355. @register_model_view(FrontPortTemplate, 'bulk_delete', path='delete', detail=False)
  1356. class FrontPortTemplateBulkDeleteView(generic.BulkDeleteView):
  1357. queryset = FrontPortTemplate.objects.all()
  1358. table = tables.FrontPortTemplateTable
  1359. #
  1360. # Rear port templates
  1361. #
  1362. @register_model_view(RearPortTemplate, 'add', detail=False)
  1363. class RearPortTemplateCreateView(generic.ComponentCreateView):
  1364. queryset = RearPortTemplate.objects.all()
  1365. form = forms.RearPortTemplateCreateForm
  1366. model_form = forms.RearPortTemplateForm
  1367. @register_model_view(RearPortTemplate, 'edit')
  1368. class RearPortTemplateEditView(generic.ObjectEditView):
  1369. queryset = RearPortTemplate.objects.all()
  1370. form = forms.RearPortTemplateForm
  1371. @register_model_view(RearPortTemplate, 'delete')
  1372. class RearPortTemplateDeleteView(generic.ObjectDeleteView):
  1373. queryset = RearPortTemplate.objects.all()
  1374. @register_model_view(RearPortTemplate, 'bulk_edit', path='edit', detail=False)
  1375. class RearPortTemplateBulkEditView(generic.BulkEditView):
  1376. queryset = RearPortTemplate.objects.all()
  1377. table = tables.RearPortTemplateTable
  1378. form = forms.RearPortTemplateBulkEditForm
  1379. @register_model_view(RearPortTemplate, 'bulk_rename', path='rename', detail=False)
  1380. class RearPortTemplateBulkRenameView(generic.BulkRenameView):
  1381. queryset = RearPortTemplate.objects.all()
  1382. @register_model_view(RearPortTemplate, 'bulk_delete', path='delete', detail=False)
  1383. class RearPortTemplateBulkDeleteView(generic.BulkDeleteView):
  1384. queryset = RearPortTemplate.objects.all()
  1385. table = tables.RearPortTemplateTable
  1386. #
  1387. # Module bay templates
  1388. #
  1389. @register_model_view(ModuleBayTemplate, 'add', detail=False)
  1390. class ModuleBayTemplateCreateView(generic.ComponentCreateView):
  1391. queryset = ModuleBayTemplate.objects.all()
  1392. form = forms.ModuleBayTemplateCreateForm
  1393. model_form = forms.ModuleBayTemplateForm
  1394. @register_model_view(ModuleBayTemplate, 'edit')
  1395. class ModuleBayTemplateEditView(generic.ObjectEditView):
  1396. queryset = ModuleBayTemplate.objects.all()
  1397. form = forms.ModuleBayTemplateForm
  1398. @register_model_view(ModuleBayTemplate, 'delete')
  1399. class ModuleBayTemplateDeleteView(generic.ObjectDeleteView):
  1400. queryset = ModuleBayTemplate.objects.all()
  1401. @register_model_view(ModuleBayTemplate, 'bulk_edit', path='edit', detail=False)
  1402. class ModuleBayTemplateBulkEditView(generic.BulkEditView):
  1403. queryset = ModuleBayTemplate.objects.all()
  1404. table = tables.ModuleBayTemplateTable
  1405. form = forms.ModuleBayTemplateBulkEditForm
  1406. @register_model_view(ModuleBayTemplate, 'bulk_rename', path='rename', detail=False)
  1407. class ModuleBayTemplateBulkRenameView(generic.BulkRenameView):
  1408. queryset = ModuleBayTemplate.objects.all()
  1409. @register_model_view(ModuleBayTemplate, 'bulk_delete', path='delete', detail=False)
  1410. class ModuleBayTemplateBulkDeleteView(generic.BulkDeleteView):
  1411. queryset = ModuleBayTemplate.objects.all()
  1412. table = tables.ModuleBayTemplateTable
  1413. #
  1414. # Device bay templates
  1415. #
  1416. @register_model_view(DeviceBayTemplate, 'add', detail=False)
  1417. class DeviceBayTemplateCreateView(generic.ComponentCreateView):
  1418. queryset = DeviceBayTemplate.objects.all()
  1419. form = forms.DeviceBayTemplateCreateForm
  1420. model_form = forms.DeviceBayTemplateForm
  1421. @register_model_view(DeviceBayTemplate, 'edit')
  1422. class DeviceBayTemplateEditView(generic.ObjectEditView):
  1423. queryset = DeviceBayTemplate.objects.all()
  1424. form = forms.DeviceBayTemplateForm
  1425. @register_model_view(DeviceBayTemplate, 'delete')
  1426. class DeviceBayTemplateDeleteView(generic.ObjectDeleteView):
  1427. queryset = DeviceBayTemplate.objects.all()
  1428. @register_model_view(DeviceBayTemplate, 'bulk_edit', path='edit', detail=False)
  1429. class DeviceBayTemplateBulkEditView(generic.BulkEditView):
  1430. queryset = DeviceBayTemplate.objects.all()
  1431. table = tables.DeviceBayTemplateTable
  1432. form = forms.DeviceBayTemplateBulkEditForm
  1433. @register_model_view(DeviceBayTemplate, 'bulk_rename', path='rename', detail=False)
  1434. class DeviceBayTemplateBulkRenameView(generic.BulkRenameView):
  1435. queryset = DeviceBayTemplate.objects.all()
  1436. @register_model_view(DeviceBayTemplate, 'bulk_delete', path='delete', detail=False)
  1437. class DeviceBayTemplateBulkDeleteView(generic.BulkDeleteView):
  1438. queryset = DeviceBayTemplate.objects.all()
  1439. table = tables.DeviceBayTemplateTable
  1440. #
  1441. # Inventory item templates
  1442. #
  1443. @register_model_view(InventoryItemTemplate, 'add', detail=False)
  1444. class InventoryItemTemplateCreateView(generic.ComponentCreateView):
  1445. queryset = InventoryItemTemplate.objects.all()
  1446. form = forms.InventoryItemTemplateCreateForm
  1447. model_form = forms.InventoryItemTemplateForm
  1448. def alter_object(self, instance, request):
  1449. # Set component (if any)
  1450. component_type = request.GET.get('component_type')
  1451. component_id = request.GET.get('component_id')
  1452. if component_type and component_id:
  1453. content_type = get_object_or_404(ContentType, pk=component_type)
  1454. instance.component = get_object_or_404(content_type.model_class(), pk=component_id)
  1455. return instance
  1456. @register_model_view(InventoryItemTemplate, 'edit')
  1457. class InventoryItemTemplateEditView(generic.ObjectEditView):
  1458. queryset = InventoryItemTemplate.objects.all()
  1459. form = forms.InventoryItemTemplateForm
  1460. @register_model_view(InventoryItemTemplate, 'delete')
  1461. class InventoryItemTemplateDeleteView(generic.ObjectDeleteView):
  1462. queryset = InventoryItemTemplate.objects.all()
  1463. @register_model_view(InventoryItemTemplate, 'bulk_edit', path='edit', detail=False)
  1464. class InventoryItemTemplateBulkEditView(generic.BulkEditView):
  1465. queryset = InventoryItemTemplate.objects.all()
  1466. table = tables.InventoryItemTemplateTable
  1467. form = forms.InventoryItemTemplateBulkEditForm
  1468. @register_model_view(InventoryItemTemplate, 'bulk_rename', path='rename', detail=False)
  1469. class InventoryItemTemplateBulkRenameView(generic.BulkRenameView):
  1470. queryset = InventoryItemTemplate.objects.all()
  1471. @register_model_view(InventoryItemTemplate, 'bulk_delete', path='delete', detail=False)
  1472. class InventoryItemTemplateBulkDeleteView(generic.BulkDeleteView):
  1473. queryset = InventoryItemTemplate.objects.all()
  1474. table = tables.InventoryItemTemplateTable
  1475. #
  1476. # Device roles
  1477. #
  1478. @register_model_view(DeviceRole, 'list', path='', detail=False)
  1479. class DeviceRoleListView(generic.ObjectListView):
  1480. queryset = DeviceRole.objects.annotate(
  1481. device_count=count_related(Device, 'role'),
  1482. vm_count=count_related(VirtualMachine, 'role')
  1483. )
  1484. filterset = filtersets.DeviceRoleFilterSet
  1485. filterset_form = forms.DeviceRoleFilterForm
  1486. table = tables.DeviceRoleTable
  1487. @register_model_view(DeviceRole)
  1488. class DeviceRoleView(GetRelatedModelsMixin, generic.ObjectView):
  1489. queryset = DeviceRole.objects.all()
  1490. def get_extra_context(self, request, instance):
  1491. return {
  1492. 'related_models': self.get_related_models(request, instance),
  1493. }
  1494. @register_model_view(DeviceRole, 'add', detail=False)
  1495. @register_model_view(DeviceRole, 'edit')
  1496. class DeviceRoleEditView(generic.ObjectEditView):
  1497. queryset = DeviceRole.objects.all()
  1498. form = forms.DeviceRoleForm
  1499. @register_model_view(DeviceRole, 'delete')
  1500. class DeviceRoleDeleteView(generic.ObjectDeleteView):
  1501. queryset = DeviceRole.objects.all()
  1502. @register_model_view(DeviceRole, 'bulk_import', detail=False)
  1503. class DeviceRoleBulkImportView(generic.BulkImportView):
  1504. queryset = DeviceRole.objects.all()
  1505. model_form = forms.DeviceRoleImportForm
  1506. @register_model_view(DeviceRole, 'bulk_edit', path='edit', detail=False)
  1507. class DeviceRoleBulkEditView(generic.BulkEditView):
  1508. queryset = DeviceRole.objects.annotate(
  1509. device_count=count_related(Device, 'role'),
  1510. vm_count=count_related(VirtualMachine, 'role')
  1511. )
  1512. filterset = filtersets.DeviceRoleFilterSet
  1513. table = tables.DeviceRoleTable
  1514. form = forms.DeviceRoleBulkEditForm
  1515. @register_model_view(DeviceRole, 'bulk_delete', path='delete', detail=False)
  1516. class DeviceRoleBulkDeleteView(generic.BulkDeleteView):
  1517. queryset = DeviceRole.objects.annotate(
  1518. device_count=count_related(Device, 'role'),
  1519. vm_count=count_related(VirtualMachine, 'role')
  1520. )
  1521. filterset = filtersets.DeviceRoleFilterSet
  1522. table = tables.DeviceRoleTable
  1523. #
  1524. # Platforms
  1525. #
  1526. @register_model_view(Platform, 'list', path='', detail=False)
  1527. class PlatformListView(generic.ObjectListView):
  1528. queryset = Platform.objects.annotate(
  1529. device_count=count_related(Device, 'platform'),
  1530. vm_count=count_related(VirtualMachine, 'platform')
  1531. )
  1532. table = tables.PlatformTable
  1533. filterset = filtersets.PlatformFilterSet
  1534. filterset_form = forms.PlatformFilterForm
  1535. @register_model_view(Platform)
  1536. class PlatformView(GetRelatedModelsMixin, generic.ObjectView):
  1537. queryset = Platform.objects.all()
  1538. def get_extra_context(self, request, instance):
  1539. return {
  1540. 'related_models': self.get_related_models(request, instance),
  1541. }
  1542. @register_model_view(Platform, 'add', detail=False)
  1543. @register_model_view(Platform, 'edit')
  1544. class PlatformEditView(generic.ObjectEditView):
  1545. queryset = Platform.objects.all()
  1546. form = forms.PlatformForm
  1547. @register_model_view(Platform, 'delete')
  1548. class PlatformDeleteView(generic.ObjectDeleteView):
  1549. queryset = Platform.objects.all()
  1550. @register_model_view(Platform, 'bulk_import', detail=False)
  1551. class PlatformBulkImportView(generic.BulkImportView):
  1552. queryset = Platform.objects.all()
  1553. model_form = forms.PlatformImportForm
  1554. @register_model_view(Platform, 'bulk_edit', path='edit', detail=False)
  1555. class PlatformBulkEditView(generic.BulkEditView):
  1556. queryset = Platform.objects.all()
  1557. filterset = filtersets.PlatformFilterSet
  1558. table = tables.PlatformTable
  1559. form = forms.PlatformBulkEditForm
  1560. @register_model_view(Platform, 'bulk_delete', path='delete', detail=False)
  1561. class PlatformBulkDeleteView(generic.BulkDeleteView):
  1562. queryset = Platform.objects.all()
  1563. filterset = filtersets.PlatformFilterSet
  1564. table = tables.PlatformTable
  1565. #
  1566. # Devices
  1567. #
  1568. @register_model_view(Device, 'list', path='', detail=False)
  1569. class DeviceListView(generic.ObjectListView):
  1570. queryset = Device.objects.all()
  1571. filterset = filtersets.DeviceFilterSet
  1572. filterset_form = forms.DeviceFilterForm
  1573. table = tables.DeviceTable
  1574. template_name = 'dcim/device_list.html'
  1575. @register_model_view(Device)
  1576. class DeviceView(generic.ObjectView):
  1577. queryset = Device.objects.all()
  1578. def get_extra_context(self, request, instance):
  1579. # VirtualChassis members
  1580. if instance.virtual_chassis is not None:
  1581. vc_members = Device.objects.restrict(request.user, 'view').filter(
  1582. virtual_chassis=instance.virtual_chassis
  1583. ).order_by('vc_position')
  1584. else:
  1585. vc_members = []
  1586. return {
  1587. 'vc_members': vc_members,
  1588. 'svg_extra': f'highlight=id:{instance.pk}'
  1589. }
  1590. @register_model_view(Device, 'add', detail=False)
  1591. @register_model_view(Device, 'edit')
  1592. class DeviceEditView(generic.ObjectEditView):
  1593. queryset = Device.objects.all()
  1594. form = forms.DeviceForm
  1595. template_name = 'dcim/device_edit.html'
  1596. @register_model_view(Device, 'delete')
  1597. class DeviceDeleteView(generic.ObjectDeleteView):
  1598. queryset = Device.objects.all()
  1599. @register_model_view(Device, 'consoleports', path='console-ports')
  1600. class DeviceConsolePortsView(DeviceComponentsView):
  1601. child_model = ConsolePort
  1602. table = tables.DeviceConsolePortTable
  1603. filterset = filtersets.ConsolePortFilterSet
  1604. filterset_form = forms.ConsolePortFilterForm
  1605. template_name = 'dcim/device/consoleports.html',
  1606. tab = ViewTab(
  1607. label=_('Console Ports'),
  1608. badge=lambda obj: obj.console_port_count,
  1609. permission='dcim.view_consoleport',
  1610. weight=550,
  1611. hide_if_empty=True
  1612. )
  1613. @register_model_view(Device, 'consoleserverports', path='console-server-ports')
  1614. class DeviceConsoleServerPortsView(DeviceComponentsView):
  1615. child_model = ConsoleServerPort
  1616. table = tables.DeviceConsoleServerPortTable
  1617. filterset = filtersets.ConsoleServerPortFilterSet
  1618. filterset_form = forms.ConsoleServerPortFilterForm
  1619. template_name = 'dcim/device/consoleserverports.html'
  1620. tab = ViewTab(
  1621. label=_('Console Server Ports'),
  1622. badge=lambda obj: obj.console_server_port_count,
  1623. permission='dcim.view_consoleserverport',
  1624. weight=560,
  1625. hide_if_empty=True
  1626. )
  1627. @register_model_view(Device, 'powerports', path='power-ports')
  1628. class DevicePowerPortsView(DeviceComponentsView):
  1629. child_model = PowerPort
  1630. table = tables.DevicePowerPortTable
  1631. filterset = filtersets.PowerPortFilterSet
  1632. filterset_form = forms.PowerPortFilterForm
  1633. template_name = 'dcim/device/powerports.html'
  1634. tab = ViewTab(
  1635. label=_('Power Ports'),
  1636. badge=lambda obj: obj.power_port_count,
  1637. permission='dcim.view_powerport',
  1638. weight=570,
  1639. hide_if_empty=True
  1640. )
  1641. @register_model_view(Device, 'poweroutlets', path='power-outlets')
  1642. class DevicePowerOutletsView(DeviceComponentsView):
  1643. child_model = PowerOutlet
  1644. table = tables.DevicePowerOutletTable
  1645. filterset = filtersets.PowerOutletFilterSet
  1646. filterset_form = forms.PowerOutletFilterForm
  1647. template_name = 'dcim/device/poweroutlets.html'
  1648. tab = ViewTab(
  1649. label=_('Power Outlets'),
  1650. badge=lambda obj: obj.power_outlet_count,
  1651. permission='dcim.view_poweroutlet',
  1652. weight=580,
  1653. hide_if_empty=True
  1654. )
  1655. @register_model_view(Device, 'interfaces')
  1656. class DeviceInterfacesView(DeviceComponentsView):
  1657. child_model = Interface
  1658. table = tables.DeviceInterfaceTable
  1659. filterset = filtersets.InterfaceFilterSet
  1660. filterset_form = forms.InterfaceFilterForm
  1661. template_name = 'dcim/device/interfaces.html'
  1662. tab = ViewTab(
  1663. label=_('Interfaces'),
  1664. badge=lambda obj: obj.vc_interfaces().count(),
  1665. permission='dcim.view_interface',
  1666. weight=520,
  1667. hide_if_empty=True
  1668. )
  1669. def get_children(self, request, parent):
  1670. return parent.vc_interfaces().restrict(request.user, 'view').prefetch_related(
  1671. Prefetch('ip_addresses', queryset=IPAddress.objects.restrict(request.user)),
  1672. Prefetch('member_interfaces', queryset=Interface.objects.restrict(request.user))
  1673. )
  1674. @register_model_view(Device, 'frontports', path='front-ports')
  1675. class DeviceFrontPortsView(DeviceComponentsView):
  1676. child_model = FrontPort
  1677. table = tables.DeviceFrontPortTable
  1678. filterset = filtersets.FrontPortFilterSet
  1679. filterset_form = forms.FrontPortFilterForm
  1680. template_name = 'dcim/device/frontports.html'
  1681. tab = ViewTab(
  1682. label=_('Front Ports'),
  1683. badge=lambda obj: obj.front_port_count,
  1684. permission='dcim.view_frontport',
  1685. weight=530,
  1686. hide_if_empty=True
  1687. )
  1688. @register_model_view(Device, 'rearports', path='rear-ports')
  1689. class DeviceRearPortsView(DeviceComponentsView):
  1690. child_model = RearPort
  1691. table = tables.DeviceRearPortTable
  1692. filterset = filtersets.RearPortFilterSet
  1693. filterset_form = forms.RearPortFilterForm
  1694. template_name = 'dcim/device/rearports.html'
  1695. tab = ViewTab(
  1696. label=_('Rear Ports'),
  1697. badge=lambda obj: obj.rear_port_count,
  1698. permission='dcim.view_rearport',
  1699. weight=540,
  1700. hide_if_empty=True
  1701. )
  1702. @register_model_view(Device, 'modulebays', path='module-bays')
  1703. class DeviceModuleBaysView(DeviceComponentsView):
  1704. child_model = ModuleBay
  1705. table = tables.DeviceModuleBayTable
  1706. filterset = filtersets.ModuleBayFilterSet
  1707. filterset_form = forms.ModuleBayFilterForm
  1708. template_name = 'dcim/device/modulebays.html'
  1709. actions = {
  1710. **DEFAULT_ACTION_PERMISSIONS,
  1711. 'bulk_rename': {'change'},
  1712. }
  1713. tab = ViewTab(
  1714. label=_('Module Bays'),
  1715. badge=lambda obj: obj.module_bay_count,
  1716. permission='dcim.view_modulebay',
  1717. weight=510,
  1718. hide_if_empty=True
  1719. )
  1720. @register_model_view(Device, 'devicebays', path='device-bays')
  1721. class DeviceDeviceBaysView(DeviceComponentsView):
  1722. child_model = DeviceBay
  1723. table = tables.DeviceDeviceBayTable
  1724. filterset = filtersets.DeviceBayFilterSet
  1725. filterset_form = forms.DeviceBayFilterForm
  1726. template_name = 'dcim/device/devicebays.html'
  1727. actions = {
  1728. **DEFAULT_ACTION_PERMISSIONS,
  1729. 'bulk_rename': {'change'},
  1730. }
  1731. tab = ViewTab(
  1732. label=_('Device Bays'),
  1733. badge=lambda obj: obj.device_bay_count,
  1734. permission='dcim.view_devicebay',
  1735. weight=500,
  1736. hide_if_empty=True
  1737. )
  1738. @register_model_view(Device, 'inventory')
  1739. class DeviceInventoryView(DeviceComponentsView):
  1740. child_model = InventoryItem
  1741. table = tables.DeviceInventoryItemTable
  1742. filterset = filtersets.InventoryItemFilterSet
  1743. filterset_form = forms.InventoryItemFilterForm
  1744. template_name = 'dcim/device/inventory.html'
  1745. actions = {
  1746. **DEFAULT_ACTION_PERMISSIONS,
  1747. 'bulk_rename': {'change'},
  1748. }
  1749. tab = ViewTab(
  1750. label=_('Inventory Items'),
  1751. badge=lambda obj: obj.inventory_item_count,
  1752. permission='dcim.view_inventoryitem',
  1753. weight=590,
  1754. hide_if_empty=True
  1755. )
  1756. @register_model_view(Device, 'configcontext', path='config-context')
  1757. class DeviceConfigContextView(ObjectConfigContextView):
  1758. queryset = Device.objects.annotate_config_context_data()
  1759. base_template = 'dcim/device/base.html'
  1760. tab = ViewTab(
  1761. label=_('Config Context'),
  1762. weight=2000
  1763. )
  1764. @register_model_view(Device, 'render-config')
  1765. class DeviceRenderConfigView(ObjectRenderConfigView):
  1766. queryset = Device.objects.all()
  1767. base_template = 'dcim/device/base.html'
  1768. tab = ViewTab(
  1769. label=_('Render Config'),
  1770. weight=2100,
  1771. )
  1772. @register_model_view(Device, 'virtual-machines')
  1773. class DeviceVirtualMachinesView(generic.ObjectChildrenView):
  1774. queryset = Device.objects.all()
  1775. child_model = VirtualMachine
  1776. table = VirtualMachineTable
  1777. filterset = VirtualMachineFilterSet
  1778. filterset_form = VirtualMachineFilterForm
  1779. tab = ViewTab(
  1780. label=_('Virtual Machines'),
  1781. badge=lambda obj: VirtualMachine.objects.filter(cluster=obj.cluster, device=obj).count(),
  1782. weight=2200,
  1783. hide_if_empty=True,
  1784. permission='virtualization.view_virtualmachine'
  1785. )
  1786. def get_children(self, request, parent):
  1787. return self.child_model.objects.restrict(request.user, 'view').filter(cluster=parent.cluster, device=parent)
  1788. @register_model_view(Device, 'bulk_import', detail=False)
  1789. class DeviceBulkImportView(generic.BulkImportView):
  1790. queryset = Device.objects.all()
  1791. model_form = forms.DeviceImportForm
  1792. def save_object(self, object_form, request):
  1793. obj = object_form.save()
  1794. # For child devices, save the reverse relation to the parent device bay
  1795. if getattr(obj, 'parent_bay', None):
  1796. device_bay = obj.parent_bay
  1797. device_bay.installed_device = obj
  1798. device_bay.save()
  1799. return obj
  1800. @register_model_view(Device, 'bulk_edit', path='edit', detail=False)
  1801. class DeviceBulkEditView(generic.BulkEditView):
  1802. queryset = Device.objects.prefetch_related('device_type__manufacturer')
  1803. filterset = filtersets.DeviceFilterSet
  1804. table = tables.DeviceTable
  1805. form = forms.DeviceBulkEditForm
  1806. @register_model_view(Device, 'bulk_delete', path='delete', detail=False)
  1807. class DeviceBulkDeleteView(generic.BulkDeleteView):
  1808. queryset = Device.objects.prefetch_related('device_type__manufacturer')
  1809. filterset = filtersets.DeviceFilterSet
  1810. table = tables.DeviceTable
  1811. @register_model_view(Device, 'bulk_rename', path='rename', detail=False)
  1812. class DeviceBulkRenameView(generic.BulkRenameView):
  1813. queryset = Device.objects.all()
  1814. filterset = filtersets.DeviceFilterSet
  1815. table = tables.DeviceTable
  1816. @register_model_view(Device, 'contacts')
  1817. class DeviceContactsView(ObjectContactsView):
  1818. queryset = Device.objects.all()
  1819. #
  1820. # Modules
  1821. #
  1822. @register_model_view(Module, 'list', path='', detail=False)
  1823. class ModuleListView(generic.ObjectListView):
  1824. queryset = Module.objects.prefetch_related('module_type__manufacturer')
  1825. filterset = filtersets.ModuleFilterSet
  1826. filterset_form = forms.ModuleFilterForm
  1827. table = tables.ModuleTable
  1828. @register_model_view(Module)
  1829. class ModuleView(GetRelatedModelsMixin, generic.ObjectView):
  1830. queryset = Module.objects.all()
  1831. def get_extra_context(self, request, instance):
  1832. return {
  1833. 'related_models': self.get_related_models(request, instance),
  1834. }
  1835. @register_model_view(Module, 'add', detail=False)
  1836. @register_model_view(Module, 'edit')
  1837. class ModuleEditView(generic.ObjectEditView):
  1838. queryset = Module.objects.all()
  1839. form = forms.ModuleForm
  1840. @register_model_view(Module, 'delete')
  1841. class ModuleDeleteView(generic.ObjectDeleteView):
  1842. queryset = Module.objects.all()
  1843. @register_model_view(Module, 'bulk_import', detail=False)
  1844. class ModuleBulkImportView(generic.BulkImportView):
  1845. queryset = Module.objects.all()
  1846. model_form = forms.ModuleImportForm
  1847. @register_model_view(Module, 'bulk_edit', path='edit', detail=False)
  1848. class ModuleBulkEditView(generic.BulkEditView):
  1849. queryset = Module.objects.prefetch_related('module_type__manufacturer')
  1850. filterset = filtersets.ModuleFilterSet
  1851. table = tables.ModuleTable
  1852. form = forms.ModuleBulkEditForm
  1853. @register_model_view(Module, 'bulk_delete', path='delete', detail=False)
  1854. class ModuleBulkDeleteView(generic.BulkDeleteView):
  1855. queryset = Module.objects.prefetch_related('module_type__manufacturer')
  1856. filterset = filtersets.ModuleFilterSet
  1857. table = tables.ModuleTable
  1858. #
  1859. # Console ports
  1860. #
  1861. @register_model_view(ConsolePort, 'list', path='', detail=False)
  1862. class ConsolePortListView(generic.ObjectListView):
  1863. queryset = ConsolePort.objects.all()
  1864. filterset = filtersets.ConsolePortFilterSet
  1865. filterset_form = forms.ConsolePortFilterForm
  1866. table = tables.ConsolePortTable
  1867. template_name = 'dcim/component_list.html'
  1868. actions = {
  1869. **DEFAULT_ACTION_PERMISSIONS,
  1870. 'bulk_rename': {'change'},
  1871. }
  1872. @register_model_view(ConsolePort)
  1873. class ConsolePortView(generic.ObjectView):
  1874. queryset = ConsolePort.objects.all()
  1875. @register_model_view(ConsolePort, 'add', detail=False)
  1876. class ConsolePortCreateView(generic.ComponentCreateView):
  1877. queryset = ConsolePort.objects.all()
  1878. form = forms.ConsolePortCreateForm
  1879. model_form = forms.ConsolePortForm
  1880. @register_model_view(ConsolePort, 'edit')
  1881. class ConsolePortEditView(generic.ObjectEditView):
  1882. queryset = ConsolePort.objects.all()
  1883. form = forms.ConsolePortForm
  1884. @register_model_view(ConsolePort, 'delete')
  1885. class ConsolePortDeleteView(generic.ObjectDeleteView):
  1886. queryset = ConsolePort.objects.all()
  1887. @register_model_view(ConsolePort, 'bulk_import', detail=False)
  1888. class ConsolePortBulkImportView(generic.BulkImportView):
  1889. queryset = ConsolePort.objects.all()
  1890. model_form = forms.ConsolePortImportForm
  1891. @register_model_view(ConsolePort, 'bulk_edit', path='edit', detail=False)
  1892. class ConsolePortBulkEditView(generic.BulkEditView):
  1893. queryset = ConsolePort.objects.all()
  1894. filterset = filtersets.ConsolePortFilterSet
  1895. table = tables.ConsolePortTable
  1896. form = forms.ConsolePortBulkEditForm
  1897. @register_model_view(ConsolePort, 'bulk_rename', path='rename', detail=False)
  1898. class ConsolePortBulkRenameView(generic.BulkRenameView):
  1899. queryset = ConsolePort.objects.all()
  1900. @register_model_view(ConsolePort, 'bulk_disconnect', path='disconnect', detail=False)
  1901. class ConsolePortBulkDisconnectView(BulkDisconnectView):
  1902. queryset = ConsolePort.objects.all()
  1903. @register_model_view(ConsolePort, 'bulk_delete', path='delete', detail=False)
  1904. class ConsolePortBulkDeleteView(generic.BulkDeleteView):
  1905. queryset = ConsolePort.objects.all()
  1906. filterset = filtersets.ConsolePortFilterSet
  1907. table = tables.ConsolePortTable
  1908. # Trace view
  1909. register_model_view(ConsolePort, 'trace', kwargs={'model': ConsolePort})(PathTraceView)
  1910. #
  1911. # Console server ports
  1912. #
  1913. @register_model_view(ConsoleServerPort, 'list', path='', detail=False)
  1914. class ConsoleServerPortListView(generic.ObjectListView):
  1915. queryset = ConsoleServerPort.objects.all()
  1916. filterset = filtersets.ConsoleServerPortFilterSet
  1917. filterset_form = forms.ConsoleServerPortFilterForm
  1918. table = tables.ConsoleServerPortTable
  1919. template_name = 'dcim/component_list.html'
  1920. actions = {
  1921. **DEFAULT_ACTION_PERMISSIONS,
  1922. 'bulk_rename': {'change'},
  1923. }
  1924. @register_model_view(ConsoleServerPort)
  1925. class ConsoleServerPortView(generic.ObjectView):
  1926. queryset = ConsoleServerPort.objects.all()
  1927. @register_model_view(ConsoleServerPort, 'add', detail=False)
  1928. class ConsoleServerPortCreateView(generic.ComponentCreateView):
  1929. queryset = ConsoleServerPort.objects.all()
  1930. form = forms.ConsoleServerPortCreateForm
  1931. model_form = forms.ConsoleServerPortForm
  1932. @register_model_view(ConsoleServerPort, 'edit')
  1933. class ConsoleServerPortEditView(generic.ObjectEditView):
  1934. queryset = ConsoleServerPort.objects.all()
  1935. form = forms.ConsoleServerPortForm
  1936. @register_model_view(ConsoleServerPort, 'delete')
  1937. class ConsoleServerPortDeleteView(generic.ObjectDeleteView):
  1938. queryset = ConsoleServerPort.objects.all()
  1939. @register_model_view(ConsoleServerPort, 'bulk_import', detail=False)
  1940. class ConsoleServerPortBulkImportView(generic.BulkImportView):
  1941. queryset = ConsoleServerPort.objects.all()
  1942. model_form = forms.ConsoleServerPortImportForm
  1943. @register_model_view(ConsoleServerPort, 'bulk_edit', path='edit', detail=False)
  1944. class ConsoleServerPortBulkEditView(generic.BulkEditView):
  1945. queryset = ConsoleServerPort.objects.all()
  1946. filterset = filtersets.ConsoleServerPortFilterSet
  1947. table = tables.ConsoleServerPortTable
  1948. form = forms.ConsoleServerPortBulkEditForm
  1949. @register_model_view(ConsoleServerPort, 'bulk_rename', path='rename', detail=False)
  1950. class ConsoleServerPortBulkRenameView(generic.BulkRenameView):
  1951. queryset = ConsoleServerPort.objects.all()
  1952. @register_model_view(ConsoleServerPort, 'bulk_disconnect', path='disconnect', detail=False)
  1953. class ConsoleServerPortBulkDisconnectView(BulkDisconnectView):
  1954. queryset = ConsoleServerPort.objects.all()
  1955. @register_model_view(ConsoleServerPort, 'bulk_delete', path='delete', detail=False)
  1956. class ConsoleServerPortBulkDeleteView(generic.BulkDeleteView):
  1957. queryset = ConsoleServerPort.objects.all()
  1958. filterset = filtersets.ConsoleServerPortFilterSet
  1959. table = tables.ConsoleServerPortTable
  1960. # Trace view
  1961. register_model_view(ConsoleServerPort, 'trace', kwargs={'model': ConsoleServerPort})(PathTraceView)
  1962. #
  1963. # Power ports
  1964. #
  1965. @register_model_view(PowerPort, 'list', path='', detail=False)
  1966. class PowerPortListView(generic.ObjectListView):
  1967. queryset = PowerPort.objects.all()
  1968. filterset = filtersets.PowerPortFilterSet
  1969. filterset_form = forms.PowerPortFilterForm
  1970. table = tables.PowerPortTable
  1971. template_name = 'dcim/component_list.html'
  1972. actions = {
  1973. **DEFAULT_ACTION_PERMISSIONS,
  1974. 'bulk_rename': {'change'},
  1975. }
  1976. @register_model_view(PowerPort)
  1977. class PowerPortView(generic.ObjectView):
  1978. queryset = PowerPort.objects.all()
  1979. @register_model_view(PowerPort, 'add', detail=False)
  1980. class PowerPortCreateView(generic.ComponentCreateView):
  1981. queryset = PowerPort.objects.all()
  1982. form = forms.PowerPortCreateForm
  1983. model_form = forms.PowerPortForm
  1984. @register_model_view(PowerPort, 'edit')
  1985. class PowerPortEditView(generic.ObjectEditView):
  1986. queryset = PowerPort.objects.all()
  1987. form = forms.PowerPortForm
  1988. @register_model_view(PowerPort, 'delete')
  1989. class PowerPortDeleteView(generic.ObjectDeleteView):
  1990. queryset = PowerPort.objects.all()
  1991. @register_model_view(PowerPort, 'bulk_import', detail=False)
  1992. class PowerPortBulkImportView(generic.BulkImportView):
  1993. queryset = PowerPort.objects.all()
  1994. model_form = forms.PowerPortImportForm
  1995. @register_model_view(PowerPort, 'bulk_edit', path='edit', detail=False)
  1996. class PowerPortBulkEditView(generic.BulkEditView):
  1997. queryset = PowerPort.objects.all()
  1998. filterset = filtersets.PowerPortFilterSet
  1999. table = tables.PowerPortTable
  2000. form = forms.PowerPortBulkEditForm
  2001. @register_model_view(PowerPort, 'bulk_rename', path='rename', detail=False)
  2002. class PowerPortBulkRenameView(generic.BulkRenameView):
  2003. queryset = PowerPort.objects.all()
  2004. @register_model_view(PowerPort, 'bulk_disconnect', path='disconnect', detail=False)
  2005. class PowerPortBulkDisconnectView(BulkDisconnectView):
  2006. queryset = PowerPort.objects.all()
  2007. @register_model_view(PowerPort, 'bulk_delete', path='delete', detail=False)
  2008. class PowerPortBulkDeleteView(generic.BulkDeleteView):
  2009. queryset = PowerPort.objects.all()
  2010. filterset = filtersets.PowerPortFilterSet
  2011. table = tables.PowerPortTable
  2012. # Trace view
  2013. register_model_view(PowerPort, 'trace', kwargs={'model': PowerPort})(PathTraceView)
  2014. #
  2015. # Power outlets
  2016. #
  2017. @register_model_view(PowerOutlet, 'list', path='', detail=False)
  2018. class PowerOutletListView(generic.ObjectListView):
  2019. queryset = PowerOutlet.objects.all()
  2020. filterset = filtersets.PowerOutletFilterSet
  2021. filterset_form = forms.PowerOutletFilterForm
  2022. table = tables.PowerOutletTable
  2023. template_name = 'dcim/component_list.html'
  2024. actions = {
  2025. **DEFAULT_ACTION_PERMISSIONS,
  2026. 'bulk_rename': {'change'},
  2027. }
  2028. @register_model_view(PowerOutlet)
  2029. class PowerOutletView(generic.ObjectView):
  2030. queryset = PowerOutlet.objects.all()
  2031. @register_model_view(PowerOutlet, 'add', detail=False)
  2032. class PowerOutletCreateView(generic.ComponentCreateView):
  2033. queryset = PowerOutlet.objects.all()
  2034. form = forms.PowerOutletCreateForm
  2035. model_form = forms.PowerOutletForm
  2036. @register_model_view(PowerOutlet, 'edit')
  2037. class PowerOutletEditView(generic.ObjectEditView):
  2038. queryset = PowerOutlet.objects.all()
  2039. form = forms.PowerOutletForm
  2040. @register_model_view(PowerOutlet, 'delete')
  2041. class PowerOutletDeleteView(generic.ObjectDeleteView):
  2042. queryset = PowerOutlet.objects.all()
  2043. @register_model_view(PowerOutlet, 'bulk_import', detail=False)
  2044. class PowerOutletBulkImportView(generic.BulkImportView):
  2045. queryset = PowerOutlet.objects.all()
  2046. model_form = forms.PowerOutletImportForm
  2047. @register_model_view(PowerOutlet, 'bulk_edit', path='edit', detail=False)
  2048. class PowerOutletBulkEditView(generic.BulkEditView):
  2049. queryset = PowerOutlet.objects.all()
  2050. filterset = filtersets.PowerOutletFilterSet
  2051. table = tables.PowerOutletTable
  2052. form = forms.PowerOutletBulkEditForm
  2053. @register_model_view(PowerOutlet, 'bulk_rename', path='rename', detail=False)
  2054. class PowerOutletBulkRenameView(generic.BulkRenameView):
  2055. queryset = PowerOutlet.objects.all()
  2056. @register_model_view(PowerOutlet, 'bulk_disconnect', path='disconnect', detail=False)
  2057. class PowerOutletBulkDisconnectView(BulkDisconnectView):
  2058. queryset = PowerOutlet.objects.all()
  2059. @register_model_view(PowerOutlet, 'bulk_delete', path='delete', detail=False)
  2060. class PowerOutletBulkDeleteView(generic.BulkDeleteView):
  2061. queryset = PowerOutlet.objects.all()
  2062. filterset = filtersets.PowerOutletFilterSet
  2063. table = tables.PowerOutletTable
  2064. # Trace view
  2065. register_model_view(PowerOutlet, 'trace', kwargs={'model': PowerOutlet})(PathTraceView)
  2066. #
  2067. # Interfaces
  2068. #
  2069. @register_model_view(Interface, 'list', path='', detail=False)
  2070. class InterfaceListView(generic.ObjectListView):
  2071. queryset = Interface.objects.all()
  2072. filterset = filtersets.InterfaceFilterSet
  2073. filterset_form = forms.InterfaceFilterForm
  2074. table = tables.InterfaceTable
  2075. template_name = 'dcim/component_list.html'
  2076. actions = {
  2077. **DEFAULT_ACTION_PERMISSIONS,
  2078. 'bulk_rename': {'change'},
  2079. }
  2080. @register_model_view(Interface)
  2081. class InterfaceView(generic.ObjectView):
  2082. queryset = Interface.objects.all()
  2083. def get_extra_context(self, request, instance):
  2084. # Get assigned VDCs
  2085. vdc_table = tables.VirtualDeviceContextTable(
  2086. data=instance.vdcs.restrict(request.user, 'view').prefetch_related('device'),
  2087. exclude=(
  2088. 'tenant', 'tenant_group', 'primary_ip', 'primary_ip4', 'primary_ip6', 'oob_ip', 'comments', 'tags',
  2089. 'created', 'last_updated', 'actions',
  2090. ),
  2091. orderable=False
  2092. )
  2093. # Get bridge interfaces
  2094. bridge_interfaces = Interface.objects.restrict(request.user, 'view').filter(bridge=instance)
  2095. bridge_interfaces_table = tables.InterfaceTable(
  2096. bridge_interfaces,
  2097. exclude=('device', 'parent'),
  2098. orderable=False
  2099. )
  2100. # Get child interfaces
  2101. child_interfaces = Interface.objects.restrict(request.user, 'view').filter(parent=instance)
  2102. child_interfaces_table = tables.InterfaceTable(
  2103. child_interfaces,
  2104. exclude=('device', 'parent'),
  2105. orderable=False
  2106. )
  2107. # Get assigned VLANs and annotate whether each is tagged or untagged
  2108. vlans = []
  2109. if instance.untagged_vlan is not None:
  2110. vlans.append(instance.untagged_vlan)
  2111. vlans[0].tagged = False
  2112. for vlan in instance.tagged_vlans.restrict(request.user).prefetch_related('site', 'group', 'tenant', 'role'):
  2113. vlan.tagged = True
  2114. vlans.append(vlan)
  2115. vlan_table = InterfaceVLANTable(
  2116. interface=instance,
  2117. data=vlans,
  2118. orderable=False
  2119. )
  2120. # Get VLAN translation rules
  2121. vlan_translation_table = None
  2122. if instance.vlan_translation_policy:
  2123. vlan_translation_table = VLANTranslationRuleTable(
  2124. data=instance.vlan_translation_policy.rules.all(),
  2125. orderable=False
  2126. )
  2127. return {
  2128. 'vdc_table': vdc_table,
  2129. 'bridge_interfaces_table': bridge_interfaces_table,
  2130. 'child_interfaces_table': child_interfaces_table,
  2131. 'vlan_table': vlan_table,
  2132. 'vlan_translation_table': vlan_translation_table,
  2133. }
  2134. @register_model_view(Interface, 'add', detail=False)
  2135. class InterfaceCreateView(generic.ComponentCreateView):
  2136. queryset = Interface.objects.all()
  2137. form = forms.InterfaceCreateForm
  2138. model_form = forms.InterfaceForm
  2139. @register_model_view(Interface, 'edit')
  2140. class InterfaceEditView(generic.ObjectEditView):
  2141. queryset = Interface.objects.all()
  2142. form = forms.InterfaceForm
  2143. @register_model_view(Interface, 'delete')
  2144. class InterfaceDeleteView(generic.ObjectDeleteView):
  2145. queryset = Interface.objects.all()
  2146. @register_model_view(Interface, 'bulk_import', detail=False)
  2147. class InterfaceBulkImportView(generic.BulkImportView):
  2148. queryset = Interface.objects.all()
  2149. model_form = forms.InterfaceImportForm
  2150. @register_model_view(Interface, 'bulk_edit', path='edit', detail=False)
  2151. class InterfaceBulkEditView(generic.BulkEditView):
  2152. queryset = Interface.objects.all()
  2153. filterset = filtersets.InterfaceFilterSet
  2154. table = tables.InterfaceTable
  2155. form = forms.InterfaceBulkEditForm
  2156. def post_save_operations(self, form, obj):
  2157. super().post_save_operations(form, obj)
  2158. # Add/remove tagged VLANs
  2159. if obj.mode == InterfaceModeChoices.MODE_TAGGED:
  2160. if form.cleaned_data.get('add_tagged_vlans', None):
  2161. obj.tagged_vlans.add(*form.cleaned_data['add_tagged_vlans'])
  2162. if form.cleaned_data.get('remove_tagged_vlans', None):
  2163. obj.tagged_vlans.remove(*form.cleaned_data['remove_tagged_vlans'])
  2164. @register_model_view(Interface, 'bulk_rename', path='rename', detail=False)
  2165. class InterfaceBulkRenameView(generic.BulkRenameView):
  2166. queryset = Interface.objects.all()
  2167. @register_model_view(Interface, 'bulk_disconnect', path='disconnect', detail=False)
  2168. class InterfaceBulkDisconnectView(BulkDisconnectView):
  2169. queryset = Interface.objects.all()
  2170. @register_model_view(Interface, 'bulk_delete', path='delete', detail=False)
  2171. class InterfaceBulkDeleteView(generic.BulkDeleteView):
  2172. # Ensure child interfaces are deleted prior to their parents
  2173. queryset = Interface.objects.order_by('device', 'parent', CollateAsChar('_name'))
  2174. filterset = filtersets.InterfaceFilterSet
  2175. table = tables.InterfaceTable
  2176. # Trace view
  2177. register_model_view(Interface, 'trace', kwargs={'model': Interface})(PathTraceView)
  2178. #
  2179. # Front ports
  2180. #
  2181. @register_model_view(FrontPort, 'list', path='', detail=False)
  2182. class FrontPortListView(generic.ObjectListView):
  2183. queryset = FrontPort.objects.all()
  2184. filterset = filtersets.FrontPortFilterSet
  2185. filterset_form = forms.FrontPortFilterForm
  2186. table = tables.FrontPortTable
  2187. template_name = 'dcim/component_list.html'
  2188. actions = {
  2189. **DEFAULT_ACTION_PERMISSIONS,
  2190. 'bulk_rename': {'change'},
  2191. }
  2192. @register_model_view(FrontPort)
  2193. class FrontPortView(generic.ObjectView):
  2194. queryset = FrontPort.objects.all()
  2195. @register_model_view(FrontPort, 'add', detail=False)
  2196. class FrontPortCreateView(generic.ComponentCreateView):
  2197. queryset = FrontPort.objects.all()
  2198. form = forms.FrontPortCreateForm
  2199. model_form = forms.FrontPortForm
  2200. @register_model_view(FrontPort, 'edit')
  2201. class FrontPortEditView(generic.ObjectEditView):
  2202. queryset = FrontPort.objects.all()
  2203. form = forms.FrontPortForm
  2204. @register_model_view(FrontPort, 'delete')
  2205. class FrontPortDeleteView(generic.ObjectDeleteView):
  2206. queryset = FrontPort.objects.all()
  2207. @register_model_view(FrontPort, 'bulk_import', detail=False)
  2208. class FrontPortBulkImportView(generic.BulkImportView):
  2209. queryset = FrontPort.objects.all()
  2210. model_form = forms.FrontPortImportForm
  2211. @register_model_view(FrontPort, 'bulk_edit', path='edit', detail=False)
  2212. class FrontPortBulkEditView(generic.BulkEditView):
  2213. queryset = FrontPort.objects.all()
  2214. filterset = filtersets.FrontPortFilterSet
  2215. table = tables.FrontPortTable
  2216. form = forms.FrontPortBulkEditForm
  2217. @register_model_view(FrontPort, 'bulk_rename', path='rename', detail=False)
  2218. class FrontPortBulkRenameView(generic.BulkRenameView):
  2219. queryset = FrontPort.objects.all()
  2220. @register_model_view(FrontPort, 'bulk_disconnect', path='disconnect', detail=False)
  2221. class FrontPortBulkDisconnectView(BulkDisconnectView):
  2222. queryset = FrontPort.objects.all()
  2223. @register_model_view(FrontPort, 'bulk_delete', path='delete', detail=False)
  2224. class FrontPortBulkDeleteView(generic.BulkDeleteView):
  2225. queryset = FrontPort.objects.all()
  2226. filterset = filtersets.FrontPortFilterSet
  2227. table = tables.FrontPortTable
  2228. # Trace view
  2229. register_model_view(FrontPort, 'trace', kwargs={'model': FrontPort})(PathTraceView)
  2230. #
  2231. # Rear ports
  2232. #
  2233. @register_model_view(RearPort, 'list', path='', detail=False)
  2234. class RearPortListView(generic.ObjectListView):
  2235. queryset = RearPort.objects.all()
  2236. filterset = filtersets.RearPortFilterSet
  2237. filterset_form = forms.RearPortFilterForm
  2238. table = tables.RearPortTable
  2239. template_name = 'dcim/component_list.html'
  2240. actions = {
  2241. **DEFAULT_ACTION_PERMISSIONS,
  2242. 'bulk_rename': {'change'},
  2243. }
  2244. @register_model_view(RearPort)
  2245. class RearPortView(generic.ObjectView):
  2246. queryset = RearPort.objects.all()
  2247. @register_model_view(RearPort, 'add', detail=False)
  2248. class RearPortCreateView(generic.ComponentCreateView):
  2249. queryset = RearPort.objects.all()
  2250. form = forms.RearPortCreateForm
  2251. model_form = forms.RearPortForm
  2252. @register_model_view(RearPort, 'edit')
  2253. class RearPortEditView(generic.ObjectEditView):
  2254. queryset = RearPort.objects.all()
  2255. form = forms.RearPortForm
  2256. @register_model_view(RearPort, 'delete')
  2257. class RearPortDeleteView(generic.ObjectDeleteView):
  2258. queryset = RearPort.objects.all()
  2259. @register_model_view(RearPort, 'bulk_import', detail=False)
  2260. class RearPortBulkImportView(generic.BulkImportView):
  2261. queryset = RearPort.objects.all()
  2262. model_form = forms.RearPortImportForm
  2263. @register_model_view(RearPort, 'bulk_edit', path='edit', detail=False)
  2264. class RearPortBulkEditView(generic.BulkEditView):
  2265. queryset = RearPort.objects.all()
  2266. filterset = filtersets.RearPortFilterSet
  2267. table = tables.RearPortTable
  2268. form = forms.RearPortBulkEditForm
  2269. @register_model_view(RearPort, 'bulk_rename', path='rename', detail=False)
  2270. class RearPortBulkRenameView(generic.BulkRenameView):
  2271. queryset = RearPort.objects.all()
  2272. @register_model_view(RearPort, 'bulk_disconnect', path='disconnect', detail=False)
  2273. class RearPortBulkDisconnectView(BulkDisconnectView):
  2274. queryset = RearPort.objects.all()
  2275. @register_model_view(RearPort, 'bulk_delete', path='delete', detail=False)
  2276. class RearPortBulkDeleteView(generic.BulkDeleteView):
  2277. queryset = RearPort.objects.all()
  2278. filterset = filtersets.RearPortFilterSet
  2279. table = tables.RearPortTable
  2280. # Trace view
  2281. register_model_view(RearPort, 'trace', kwargs={'model': RearPort})(PathTraceView)
  2282. #
  2283. # Module bays
  2284. #
  2285. @register_model_view(ModuleBay, 'list', path='', detail=False)
  2286. class ModuleBayListView(generic.ObjectListView):
  2287. queryset = ModuleBay.objects.select_related('installed_module__module_type')
  2288. filterset = filtersets.ModuleBayFilterSet
  2289. filterset_form = forms.ModuleBayFilterForm
  2290. table = tables.ModuleBayTable
  2291. template_name = 'dcim/component_list.html'
  2292. actions = {
  2293. **DEFAULT_ACTION_PERMISSIONS,
  2294. 'bulk_rename': {'change'},
  2295. }
  2296. @register_model_view(ModuleBay)
  2297. class ModuleBayView(generic.ObjectView):
  2298. queryset = ModuleBay.objects.all()
  2299. @register_model_view(ModuleBay, 'add', detail=False)
  2300. class ModuleBayCreateView(generic.ComponentCreateView):
  2301. queryset = ModuleBay.objects.all()
  2302. form = forms.ModuleBayCreateForm
  2303. model_form = forms.ModuleBayForm
  2304. @register_model_view(ModuleBay, 'edit')
  2305. class ModuleBayEditView(generic.ObjectEditView):
  2306. queryset = ModuleBay.objects.all()
  2307. form = forms.ModuleBayForm
  2308. @register_model_view(ModuleBay, 'delete')
  2309. class ModuleBayDeleteView(generic.ObjectDeleteView):
  2310. queryset = ModuleBay.objects.all()
  2311. @register_model_view(ModuleBay, 'bulk_import', detail=False)
  2312. class ModuleBayBulkImportView(generic.BulkImportView):
  2313. queryset = ModuleBay.objects.all()
  2314. model_form = forms.ModuleBayImportForm
  2315. @register_model_view(ModuleBay, 'bulk_edit', path='edit', detail=False)
  2316. class ModuleBayBulkEditView(generic.BulkEditView):
  2317. queryset = ModuleBay.objects.all()
  2318. filterset = filtersets.ModuleBayFilterSet
  2319. table = tables.ModuleBayTable
  2320. form = forms.ModuleBayBulkEditForm
  2321. @register_model_view(ModuleBay, 'bulk_rename', path='rename', detail=False)
  2322. class ModuleBayBulkRenameView(generic.BulkRenameView):
  2323. queryset = ModuleBay.objects.all()
  2324. @register_model_view(ModuleBay, 'bulk_delete', path='delete', detail=False)
  2325. class ModuleBayBulkDeleteView(generic.BulkDeleteView):
  2326. queryset = ModuleBay.objects.all()
  2327. filterset = filtersets.ModuleBayFilterSet
  2328. table = tables.ModuleBayTable
  2329. #
  2330. # Device bays
  2331. #
  2332. @register_model_view(DeviceBay, 'list', path='', detail=False)
  2333. class DeviceBayListView(generic.ObjectListView):
  2334. queryset = DeviceBay.objects.all()
  2335. filterset = filtersets.DeviceBayFilterSet
  2336. filterset_form = forms.DeviceBayFilterForm
  2337. table = tables.DeviceBayTable
  2338. template_name = 'dcim/component_list.html'
  2339. actions = {
  2340. **DEFAULT_ACTION_PERMISSIONS,
  2341. 'bulk_rename': {'change'},
  2342. }
  2343. @register_model_view(DeviceBay)
  2344. class DeviceBayView(generic.ObjectView):
  2345. queryset = DeviceBay.objects.all()
  2346. @register_model_view(DeviceBay, 'add', detail=False)
  2347. class DeviceBayCreateView(generic.ComponentCreateView):
  2348. queryset = DeviceBay.objects.all()
  2349. form = forms.DeviceBayCreateForm
  2350. model_form = forms.DeviceBayForm
  2351. @register_model_view(DeviceBay, 'edit')
  2352. class DeviceBayEditView(generic.ObjectEditView):
  2353. queryset = DeviceBay.objects.all()
  2354. form = forms.DeviceBayForm
  2355. @register_model_view(DeviceBay, 'delete')
  2356. class DeviceBayDeleteView(generic.ObjectDeleteView):
  2357. queryset = DeviceBay.objects.all()
  2358. @register_model_view(DeviceBay, 'populate')
  2359. class DeviceBayPopulateView(generic.ObjectEditView):
  2360. queryset = DeviceBay.objects.all()
  2361. def get(self, request, pk):
  2362. device_bay = get_object_or_404(self.queryset, pk=pk)
  2363. form = forms.PopulateDeviceBayForm(device_bay)
  2364. return render(request, 'dcim/devicebay_populate.html', {
  2365. 'device_bay': device_bay,
  2366. 'form': form,
  2367. 'return_url': self.get_return_url(request, device_bay),
  2368. })
  2369. def post(self, request, pk):
  2370. device_bay = get_object_or_404(self.queryset, pk=pk)
  2371. form = forms.PopulateDeviceBayForm(device_bay, request.POST)
  2372. if form.is_valid():
  2373. device_bay.snapshot()
  2374. device_bay.installed_device = form.cleaned_data['installed_device']
  2375. device_bay.save()
  2376. messages.success(
  2377. request,
  2378. _("Installed device {device} in bay {device_bay}.").format(
  2379. device=device_bay.installed_device,
  2380. device_bay=device_bay
  2381. )
  2382. )
  2383. return_url = self.get_return_url(request)
  2384. return redirect(return_url)
  2385. return render(request, 'dcim/devicebay_populate.html', {
  2386. 'device_bay': device_bay,
  2387. 'form': form,
  2388. 'return_url': self.get_return_url(request, device_bay),
  2389. })
  2390. @register_model_view(DeviceBay, 'depopulate')
  2391. class DeviceBayDepopulateView(generic.ObjectEditView):
  2392. queryset = DeviceBay.objects.all()
  2393. def get(self, request, pk):
  2394. device_bay = get_object_or_404(self.queryset, pk=pk)
  2395. form = ConfirmationForm()
  2396. return render(request, 'dcim/devicebay_depopulate.html', {
  2397. 'device_bay': device_bay,
  2398. 'form': form,
  2399. 'return_url': self.get_return_url(request, device_bay),
  2400. })
  2401. def post(self, request, pk):
  2402. device_bay = get_object_or_404(self.queryset, pk=pk)
  2403. form = ConfirmationForm(request.POST)
  2404. if form.is_valid():
  2405. device_bay.snapshot()
  2406. removed_device = device_bay.installed_device
  2407. device_bay.installed_device = None
  2408. device_bay.save()
  2409. messages.success(
  2410. request,
  2411. _("Removed device {device} from bay {device_bay}.").format(
  2412. device=removed_device,
  2413. device_bay=device_bay
  2414. )
  2415. )
  2416. return_url = self.get_return_url(request, device_bay.device)
  2417. return redirect(return_url)
  2418. return render(request, 'dcim/devicebay_depopulate.html', {
  2419. 'device_bay': device_bay,
  2420. 'form': form,
  2421. 'return_url': self.get_return_url(request, device_bay),
  2422. })
  2423. @register_model_view(DeviceBay, 'bulk_import', detail=False)
  2424. class DeviceBayBulkImportView(generic.BulkImportView):
  2425. queryset = DeviceBay.objects.all()
  2426. model_form = forms.DeviceBayImportForm
  2427. @register_model_view(DeviceBay, 'bulk_edit', path='edit', detail=False)
  2428. class DeviceBayBulkEditView(generic.BulkEditView):
  2429. queryset = DeviceBay.objects.all()
  2430. filterset = filtersets.DeviceBayFilterSet
  2431. table = tables.DeviceBayTable
  2432. form = forms.DeviceBayBulkEditForm
  2433. @register_model_view(DeviceBay, 'bulk_rename', path='rename', detail=False)
  2434. class DeviceBayBulkRenameView(generic.BulkRenameView):
  2435. queryset = DeviceBay.objects.all()
  2436. @register_model_view(DeviceBay, 'bulk_delete', path='delete', detail=False)
  2437. class DeviceBayBulkDeleteView(generic.BulkDeleteView):
  2438. queryset = DeviceBay.objects.all()
  2439. filterset = filtersets.DeviceBayFilterSet
  2440. table = tables.DeviceBayTable
  2441. #
  2442. # Inventory items
  2443. #
  2444. @register_model_view(InventoryItem, 'list', path='', detail=False)
  2445. class InventoryItemListView(generic.ObjectListView):
  2446. queryset = InventoryItem.objects.all()
  2447. filterset = filtersets.InventoryItemFilterSet
  2448. filterset_form = forms.InventoryItemFilterForm
  2449. table = tables.InventoryItemTable
  2450. template_name = 'dcim/component_list.html'
  2451. actions = {
  2452. **DEFAULT_ACTION_PERMISSIONS,
  2453. 'bulk_rename': {'change'},
  2454. }
  2455. @register_model_view(InventoryItem)
  2456. class InventoryItemView(generic.ObjectView):
  2457. queryset = InventoryItem.objects.all()
  2458. @register_model_view(InventoryItem, 'edit')
  2459. class InventoryItemEditView(generic.ObjectEditView):
  2460. queryset = InventoryItem.objects.all()
  2461. form = forms.InventoryItemForm
  2462. @register_model_view(InventoryItem, 'add', detail=False)
  2463. class InventoryItemCreateView(generic.ComponentCreateView):
  2464. queryset = InventoryItem.objects.all()
  2465. form = forms.InventoryItemCreateForm
  2466. model_form = forms.InventoryItemForm
  2467. @register_model_view(InventoryItem, 'delete')
  2468. class InventoryItemDeleteView(generic.ObjectDeleteView):
  2469. queryset = InventoryItem.objects.all()
  2470. @register_model_view(InventoryItem, 'bulk_import', detail=False)
  2471. class InventoryItemBulkImportView(generic.BulkImportView):
  2472. queryset = InventoryItem.objects.all()
  2473. model_form = forms.InventoryItemImportForm
  2474. @register_model_view(InventoryItem, 'bulk_edit', path='edit', detail=False)
  2475. class InventoryItemBulkEditView(generic.BulkEditView):
  2476. queryset = InventoryItem.objects.all()
  2477. filterset = filtersets.InventoryItemFilterSet
  2478. table = tables.InventoryItemTable
  2479. form = forms.InventoryItemBulkEditForm
  2480. @register_model_view(InventoryItem, 'bulk_rename', path='rename', detail=False)
  2481. class InventoryItemBulkRenameView(generic.BulkRenameView):
  2482. queryset = InventoryItem.objects.all()
  2483. @register_model_view(InventoryItem, 'bulk_delete', path='delete', detail=False)
  2484. class InventoryItemBulkDeleteView(generic.BulkDeleteView):
  2485. queryset = InventoryItem.objects.all()
  2486. filterset = filtersets.InventoryItemFilterSet
  2487. table = tables.InventoryItemTable
  2488. @register_model_view(InventoryItem, 'children')
  2489. class InventoryItemChildrenView(generic.ObjectChildrenView):
  2490. queryset = InventoryItem.objects.all()
  2491. child_model = InventoryItem
  2492. table = tables.InventoryItemTable
  2493. filterset = filtersets.InventoryItemFilterSet
  2494. filterset_form = forms.InventoryItemFilterForm
  2495. tab = ViewTab(
  2496. label=_('Children'),
  2497. badge=lambda obj: obj.child_items.count(),
  2498. permission='dcim.view_inventoryitem',
  2499. hide_if_empty=True,
  2500. weight=5000
  2501. )
  2502. def get_children(self, request, parent):
  2503. return parent.child_items.restrict(request.user, 'view')
  2504. #
  2505. # Inventory item roles
  2506. #
  2507. @register_model_view(InventoryItemRole, 'list', path='', detail=False)
  2508. class InventoryItemRoleListView(generic.ObjectListView):
  2509. queryset = InventoryItemRole.objects.annotate(
  2510. inventoryitem_count=count_related(InventoryItem, 'role'),
  2511. )
  2512. filterset = filtersets.InventoryItemRoleFilterSet
  2513. filterset_form = forms.InventoryItemRoleFilterForm
  2514. table = tables.InventoryItemRoleTable
  2515. @register_model_view(InventoryItemRole)
  2516. class InventoryItemRoleView(generic.ObjectView):
  2517. queryset = InventoryItemRole.objects.all()
  2518. def get_extra_context(self, request, instance):
  2519. return {
  2520. 'inventoryitem_count': InventoryItem.objects.filter(role=instance).count(),
  2521. }
  2522. @register_model_view(InventoryItemRole, 'add', detail=False)
  2523. @register_model_view(InventoryItemRole, 'edit')
  2524. class InventoryItemRoleEditView(generic.ObjectEditView):
  2525. queryset = InventoryItemRole.objects.all()
  2526. form = forms.InventoryItemRoleForm
  2527. @register_model_view(InventoryItemRole, 'delete')
  2528. class InventoryItemRoleDeleteView(generic.ObjectDeleteView):
  2529. queryset = InventoryItemRole.objects.all()
  2530. @register_model_view(InventoryItemRole, 'bulk_import', detail=False)
  2531. class InventoryItemRoleBulkImportView(generic.BulkImportView):
  2532. queryset = InventoryItemRole.objects.all()
  2533. model_form = forms.InventoryItemRoleImportForm
  2534. @register_model_view(InventoryItemRole, 'bulk_edit', path='edit', detail=False)
  2535. class InventoryItemRoleBulkEditView(generic.BulkEditView):
  2536. queryset = InventoryItemRole.objects.annotate(
  2537. inventoryitem_count=count_related(InventoryItem, 'role'),
  2538. )
  2539. filterset = filtersets.InventoryItemRoleFilterSet
  2540. table = tables.InventoryItemRoleTable
  2541. form = forms.InventoryItemRoleBulkEditForm
  2542. @register_model_view(InventoryItemRole, 'bulk_delete', path='delete', detail=False)
  2543. class InventoryItemRoleBulkDeleteView(generic.BulkDeleteView):
  2544. queryset = InventoryItemRole.objects.annotate(
  2545. inventoryitem_count=count_related(InventoryItem, 'role'),
  2546. )
  2547. filterset = filtersets.InventoryItemRoleFilterSet
  2548. table = tables.InventoryItemRoleTable
  2549. #
  2550. # Bulk Device component creation
  2551. #
  2552. class DeviceBulkAddConsolePortView(generic.BulkComponentCreateView):
  2553. parent_model = Device
  2554. parent_field = 'device'
  2555. form = forms.ConsolePortBulkCreateForm
  2556. queryset = ConsolePort.objects.all()
  2557. model_form = forms.ConsolePortForm
  2558. filterset = filtersets.DeviceFilterSet
  2559. table = tables.DeviceTable
  2560. default_return_url = 'dcim:device_list'
  2561. class DeviceBulkAddConsoleServerPortView(generic.BulkComponentCreateView):
  2562. parent_model = Device
  2563. parent_field = 'device'
  2564. form = forms.ConsoleServerPortBulkCreateForm
  2565. queryset = ConsoleServerPort.objects.all()
  2566. model_form = forms.ConsoleServerPortForm
  2567. filterset = filtersets.DeviceFilterSet
  2568. table = tables.DeviceTable
  2569. default_return_url = 'dcim:device_list'
  2570. class DeviceBulkAddPowerPortView(generic.BulkComponentCreateView):
  2571. parent_model = Device
  2572. parent_field = 'device'
  2573. form = forms.PowerPortBulkCreateForm
  2574. queryset = PowerPort.objects.all()
  2575. model_form = forms.PowerPortForm
  2576. filterset = filtersets.DeviceFilterSet
  2577. table = tables.DeviceTable
  2578. default_return_url = 'dcim:device_list'
  2579. class DeviceBulkAddPowerOutletView(generic.BulkComponentCreateView):
  2580. parent_model = Device
  2581. parent_field = 'device'
  2582. form = forms.PowerOutletBulkCreateForm
  2583. queryset = PowerOutlet.objects.all()
  2584. model_form = forms.PowerOutletForm
  2585. filterset = filtersets.DeviceFilterSet
  2586. table = tables.DeviceTable
  2587. default_return_url = 'dcim:device_list'
  2588. class DeviceBulkAddInterfaceView(generic.BulkComponentCreateView):
  2589. parent_model = Device
  2590. parent_field = 'device'
  2591. form = forms.InterfaceBulkCreateForm
  2592. queryset = Interface.objects.all()
  2593. model_form = forms.InterfaceForm
  2594. filterset = filtersets.DeviceFilterSet
  2595. table = tables.DeviceTable
  2596. default_return_url = 'dcim:device_list'
  2597. class DeviceBulkAddRearPortView(generic.BulkComponentCreateView):
  2598. parent_model = Device
  2599. parent_field = 'device'
  2600. form = forms.RearPortBulkCreateForm
  2601. queryset = RearPort.objects.all()
  2602. model_form = forms.RearPortForm
  2603. filterset = filtersets.DeviceFilterSet
  2604. table = tables.DeviceTable
  2605. default_return_url = 'dcim:device_list'
  2606. class DeviceBulkAddModuleBayView(generic.BulkComponentCreateView):
  2607. parent_model = Device
  2608. parent_field = 'device'
  2609. form = forms.ModuleBayBulkCreateForm
  2610. queryset = ModuleBay.objects.all()
  2611. model_form = forms.ModuleBayForm
  2612. filterset = filtersets.DeviceFilterSet
  2613. table = tables.DeviceTable
  2614. default_return_url = 'dcim:device_list'
  2615. class DeviceBulkAddDeviceBayView(generic.BulkComponentCreateView):
  2616. parent_model = Device
  2617. parent_field = 'device'
  2618. form = forms.DeviceBayBulkCreateForm
  2619. queryset = DeviceBay.objects.all()
  2620. model_form = forms.DeviceBayForm
  2621. filterset = filtersets.DeviceFilterSet
  2622. table = tables.DeviceTable
  2623. default_return_url = 'dcim:device_list'
  2624. class DeviceBulkAddInventoryItemView(generic.BulkComponentCreateView):
  2625. parent_model = Device
  2626. parent_field = 'device'
  2627. form = forms.InventoryItemBulkCreateForm
  2628. queryset = InventoryItem.objects.all()
  2629. model_form = forms.InventoryItemForm
  2630. filterset = filtersets.DeviceFilterSet
  2631. table = tables.DeviceTable
  2632. default_return_url = 'dcim:device_list'
  2633. #
  2634. # Cables
  2635. #
  2636. @register_model_view(Cable, 'list', path='', detail=False)
  2637. class CableListView(generic.ObjectListView):
  2638. queryset = Cable.objects.prefetch_related(
  2639. 'terminations__termination', 'terminations___device', 'terminations___rack', 'terminations___location',
  2640. 'terminations___site',
  2641. )
  2642. filterset = filtersets.CableFilterSet
  2643. filterset_form = forms.CableFilterForm
  2644. table = tables.CableTable
  2645. @register_model_view(Cable)
  2646. class CableView(generic.ObjectView):
  2647. queryset = Cable.objects.all()
  2648. @register_model_view(Cable, 'add', detail=False)
  2649. @register_model_view(Cable, 'edit')
  2650. class CableEditView(generic.ObjectEditView):
  2651. queryset = Cable.objects.all()
  2652. template_name = 'dcim/cable_edit.html'
  2653. htmx_template_name = 'dcim/htmx/cable_edit.html'
  2654. def alter_object(self, obj, request, url_args, url_kwargs):
  2655. """
  2656. Hack into alter_object() to set the form class when editing an existing Cable, since ObjectEditView
  2657. doesn't currently provide a hook for dynamic class resolution.
  2658. """
  2659. a_terminations_type = CABLE_TERMINATION_TYPES.get(
  2660. request.POST.get('a_terminations_type') or request.GET.get('a_terminations_type')
  2661. )
  2662. b_terminations_type = CABLE_TERMINATION_TYPES.get(
  2663. request.POST.get('b_terminations_type') or request.GET.get('b_terminations_type')
  2664. )
  2665. if obj.pk:
  2666. if not a_terminations_type and (termination_a := obj.terminations.filter(cable_end='A').first()):
  2667. a_terminations_type = termination_a.termination._meta.model
  2668. if not b_terminations_type and (termination_b := obj.terminations.filter(cable_end='B').first()):
  2669. b_terminations_type = termination_b.termination._meta.model
  2670. self.form = forms.get_cable_form(a_terminations_type, b_terminations_type)
  2671. return super().alter_object(obj, request, url_args, url_kwargs)
  2672. def get_extra_addanother_params(self, request):
  2673. params = {
  2674. 'a_terminations_type': request.GET.get('a_terminations_type'),
  2675. 'b_terminations_type': request.GET.get('b_terminations_type')
  2676. }
  2677. for key in request.POST:
  2678. if 'device' in key or 'power_panel' in key or 'circuit' in key:
  2679. params.update({key: request.POST.get(key)})
  2680. return params
  2681. @register_model_view(Cable, 'delete')
  2682. class CableDeleteView(generic.ObjectDeleteView):
  2683. queryset = Cable.objects.all()
  2684. @register_model_view(Cable, 'bulk_import', detail=False)
  2685. class CableBulkImportView(generic.BulkImportView):
  2686. queryset = Cable.objects.all()
  2687. model_form = forms.CableImportForm
  2688. @register_model_view(Cable, 'bulk_edit', path='edit', detail=False)
  2689. class CableBulkEditView(generic.BulkEditView):
  2690. queryset = Cable.objects.prefetch_related(
  2691. 'terminations__termination', 'terminations___device', 'terminations___rack', 'terminations___location',
  2692. 'terminations___site',
  2693. )
  2694. filterset = filtersets.CableFilterSet
  2695. table = tables.CableTable
  2696. form = forms.CableBulkEditForm
  2697. @register_model_view(Cable, 'bulk_delete', path='delete', detail=False)
  2698. class CableBulkDeleteView(generic.BulkDeleteView):
  2699. queryset = Cable.objects.prefetch_related(
  2700. 'terminations__termination', 'terminations___device', 'terminations___rack', 'terminations___location',
  2701. 'terminations___site',
  2702. )
  2703. filterset = filtersets.CableFilterSet
  2704. table = tables.CableTable
  2705. #
  2706. # Connections
  2707. #
  2708. class ConsoleConnectionsListView(generic.ObjectListView):
  2709. queryset = ConsolePort.objects.filter(_path__is_complete=True)
  2710. filterset = filtersets.ConsoleConnectionFilterSet
  2711. filterset_form = forms.ConsoleConnectionFilterForm
  2712. table = tables.ConsoleConnectionTable
  2713. template_name = 'dcim/connections_list.html'
  2714. actions = {
  2715. 'export': {'view'},
  2716. }
  2717. def get_extra_context(self, request):
  2718. return {
  2719. 'title': 'Console Connections'
  2720. }
  2721. class PowerConnectionsListView(generic.ObjectListView):
  2722. queryset = PowerPort.objects.filter(_path__is_complete=True)
  2723. filterset = filtersets.PowerConnectionFilterSet
  2724. filterset_form = forms.PowerConnectionFilterForm
  2725. table = tables.PowerConnectionTable
  2726. template_name = 'dcim/connections_list.html'
  2727. actions = {
  2728. 'export': {'view'},
  2729. }
  2730. def get_extra_context(self, request):
  2731. return {
  2732. 'title': 'Power Connections'
  2733. }
  2734. class InterfaceConnectionsListView(generic.ObjectListView):
  2735. queryset = Interface.objects.filter(_path__is_complete=True)
  2736. filterset = filtersets.InterfaceConnectionFilterSet
  2737. filterset_form = forms.InterfaceConnectionFilterForm
  2738. table = tables.InterfaceConnectionTable
  2739. template_name = 'dcim/connections_list.html'
  2740. actions = {
  2741. 'export': {'view'},
  2742. }
  2743. def get_extra_context(self, request):
  2744. return {
  2745. 'title': 'Interface Connections'
  2746. }
  2747. #
  2748. # Virtual chassis
  2749. #
  2750. @register_model_view(VirtualChassis, 'list', path='', detail=False)
  2751. class VirtualChassisListView(generic.ObjectListView):
  2752. queryset = VirtualChassis.objects.all()
  2753. table = tables.VirtualChassisTable
  2754. filterset = filtersets.VirtualChassisFilterSet
  2755. filterset_form = forms.VirtualChassisFilterForm
  2756. @register_model_view(VirtualChassis)
  2757. class VirtualChassisView(generic.ObjectView):
  2758. queryset = VirtualChassis.objects.all()
  2759. def get_extra_context(self, request, instance):
  2760. members = Device.objects.restrict(request.user).filter(virtual_chassis=instance)
  2761. return {
  2762. 'members': members,
  2763. }
  2764. @register_model_view(VirtualChassis, 'add', detail=False)
  2765. class VirtualChassisCreateView(generic.ObjectEditView):
  2766. queryset = VirtualChassis.objects.all()
  2767. form = forms.VirtualChassisCreateForm
  2768. template_name = 'dcim/virtualchassis_add.html'
  2769. @register_model_view(VirtualChassis, 'edit')
  2770. class VirtualChassisEditView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View):
  2771. queryset = VirtualChassis.objects.all()
  2772. def get_required_permission(self):
  2773. return 'dcim.change_virtualchassis'
  2774. def get(self, request, pk):
  2775. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  2776. VCMemberFormSet = modelformset_factory(
  2777. model=Device,
  2778. form=forms.DeviceVCMembershipForm,
  2779. formset=forms.BaseVCMemberFormSet,
  2780. extra=0
  2781. )
  2782. members_queryset = virtual_chassis.members.prefetch_related('rack').order_by('vc_position')
  2783. vc_form = forms.VirtualChassisForm(instance=virtual_chassis)
  2784. vc_form.fields['master'].queryset = members_queryset
  2785. formset = VCMemberFormSet(queryset=members_queryset)
  2786. return render(request, 'dcim/virtualchassis_edit.html', {
  2787. 'object': virtual_chassis,
  2788. 'vc_form': vc_form,
  2789. 'formset': formset,
  2790. 'return_url': self.get_return_url(request, virtual_chassis),
  2791. })
  2792. def post(self, request, pk):
  2793. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  2794. VCMemberFormSet = modelformset_factory(
  2795. model=Device,
  2796. form=forms.DeviceVCMembershipForm,
  2797. formset=forms.BaseVCMemberFormSet,
  2798. extra=0
  2799. )
  2800. members_queryset = virtual_chassis.members.prefetch_related('rack').order_by('vc_position')
  2801. vc_form = forms.VirtualChassisForm(request.POST, instance=virtual_chassis)
  2802. vc_form.fields['master'].queryset = members_queryset
  2803. formset = VCMemberFormSet(request.POST, queryset=members_queryset)
  2804. if vc_form.is_valid() and formset.is_valid():
  2805. with transaction.atomic():
  2806. # Save the VirtualChassis
  2807. vc_form.save()
  2808. # Nullify the vc_position of each member first to allow reordering without raising an IntegrityError on
  2809. # duplicate positions. Then save each member instance.
  2810. members = formset.save(commit=False)
  2811. devices = Device.objects.filter(pk__in=[m.pk for m in members])
  2812. for device in devices:
  2813. device.vc_position = None
  2814. device.save()
  2815. for member in members:
  2816. member.save()
  2817. return redirect(virtual_chassis.get_absolute_url())
  2818. return render(request, 'dcim/virtualchassis_edit.html', {
  2819. 'vc_form': vc_form,
  2820. 'formset': formset,
  2821. 'return_url': self.get_return_url(request, virtual_chassis),
  2822. })
  2823. @register_model_view(VirtualChassis, 'delete')
  2824. class VirtualChassisDeleteView(generic.ObjectDeleteView):
  2825. queryset = VirtualChassis.objects.all()
  2826. @register_model_view(VirtualChassis, 'add_member', path='add-member')
  2827. class VirtualChassisAddMemberView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View):
  2828. queryset = VirtualChassis.objects.all()
  2829. def get_required_permission(self):
  2830. return 'dcim.change_virtualchassis'
  2831. def get(self, request, pk):
  2832. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  2833. initial_data = {k: request.GET[k] for k in request.GET}
  2834. member_select_form = forms.VCMemberSelectForm(initial=initial_data)
  2835. membership_form = forms.DeviceVCMembershipForm(initial=initial_data)
  2836. return render(request, 'dcim/virtualchassis_add_member.html', {
  2837. 'virtual_chassis': virtual_chassis,
  2838. 'member_select_form': member_select_form,
  2839. 'membership_form': membership_form,
  2840. 'return_url': self.get_return_url(request, virtual_chassis),
  2841. })
  2842. def post(self, request, pk):
  2843. virtual_chassis = get_object_or_404(self.queryset, pk=pk)
  2844. member_select_form = forms.VCMemberSelectForm(request.POST)
  2845. if member_select_form.is_valid():
  2846. device = member_select_form.cleaned_data['device']
  2847. device.virtual_chassis = virtual_chassis
  2848. data = {k: request.POST[k] for k in ['vc_position', 'vc_priority']}
  2849. membership_form = forms.DeviceVCMembershipForm(data=data, validate_vc_position=True, instance=device)
  2850. if membership_form.is_valid():
  2851. membership_form.save()
  2852. messages.success(request, mark_safe(
  2853. _('Added member <a href="{url}">{device}</a>').format(
  2854. url=device.get_absolute_url(), device=escape(device)
  2855. )
  2856. ))
  2857. if '_addanother' in request.POST:
  2858. return redirect(request.get_full_path())
  2859. return redirect(self.get_return_url(request, device))
  2860. else:
  2861. membership_form = forms.DeviceVCMembershipForm(data=request.POST)
  2862. return render(request, 'dcim/virtualchassis_add_member.html', {
  2863. 'virtual_chassis': virtual_chassis,
  2864. 'member_select_form': member_select_form,
  2865. 'membership_form': membership_form,
  2866. 'return_url': self.get_return_url(request, virtual_chassis),
  2867. })
  2868. class VirtualChassisRemoveMemberView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View):
  2869. queryset = Device.objects.all()
  2870. def get_required_permission(self):
  2871. return 'dcim.change_device'
  2872. def get(self, request, pk):
  2873. device = get_object_or_404(self.queryset, pk=pk, virtual_chassis__isnull=False)
  2874. form = ConfirmationForm(initial=request.GET)
  2875. return render(request, 'dcim/virtualchassis_remove_member.html', {
  2876. 'device': device,
  2877. 'form': form,
  2878. 'return_url': self.get_return_url(request, device),
  2879. })
  2880. def post(self, request, pk):
  2881. device = get_object_or_404(self.queryset, pk=pk, virtual_chassis__isnull=False)
  2882. form = ConfirmationForm(request.POST)
  2883. # Protect master device from being removed
  2884. virtual_chassis = VirtualChassis.objects.filter(master=device).first()
  2885. if virtual_chassis is not None:
  2886. messages.error(
  2887. request,
  2888. _('Unable to remove master device {device} from the virtual chassis.').format(device=device)
  2889. )
  2890. return redirect(device.get_absolute_url())
  2891. if form.is_valid():
  2892. devices = Device.objects.filter(pk=device.pk)
  2893. for device in devices:
  2894. device.virtual_chassis = None
  2895. device.vc_position = None
  2896. device.vc_priority = None
  2897. device.save()
  2898. msg = _('Removed {device} from virtual chassis {chassis}').format(
  2899. device=device,
  2900. chassis=device.virtual_chassis
  2901. )
  2902. messages.success(request, msg)
  2903. return redirect(self.get_return_url(request, device))
  2904. return render(request, 'dcim/virtualchassis_remove_member.html', {
  2905. 'device': device,
  2906. 'form': form,
  2907. 'return_url': self.get_return_url(request, device),
  2908. })
  2909. @register_model_view(VirtualChassis, 'bulk_import', detail=False)
  2910. class VirtualChassisBulkImportView(generic.BulkImportView):
  2911. queryset = VirtualChassis.objects.all()
  2912. model_form = forms.VirtualChassisImportForm
  2913. @register_model_view(VirtualChassis, 'bulk_edit', path='edit', detail=False)
  2914. class VirtualChassisBulkEditView(generic.BulkEditView):
  2915. queryset = VirtualChassis.objects.all()
  2916. filterset = filtersets.VirtualChassisFilterSet
  2917. table = tables.VirtualChassisTable
  2918. form = forms.VirtualChassisBulkEditForm
  2919. @register_model_view(VirtualChassis, 'bulk_delete', path='delete', detail=False)
  2920. class VirtualChassisBulkDeleteView(generic.BulkDeleteView):
  2921. queryset = VirtualChassis.objects.all()
  2922. filterset = filtersets.VirtualChassisFilterSet
  2923. table = tables.VirtualChassisTable
  2924. #
  2925. # Power panels
  2926. #
  2927. @register_model_view(PowerPanel, 'list', path='', detail=False)
  2928. class PowerPanelListView(generic.ObjectListView):
  2929. queryset = PowerPanel.objects.annotate(
  2930. powerfeed_count=count_related(PowerFeed, 'power_panel')
  2931. )
  2932. filterset = filtersets.PowerPanelFilterSet
  2933. filterset_form = forms.PowerPanelFilterForm
  2934. table = tables.PowerPanelTable
  2935. @register_model_view(PowerPanel)
  2936. class PowerPanelView(GetRelatedModelsMixin, generic.ObjectView):
  2937. queryset = PowerPanel.objects.all()
  2938. def get_extra_context(self, request, instance):
  2939. return {
  2940. 'related_models': self.get_related_models(request, instance),
  2941. }
  2942. @register_model_view(PowerPanel, 'add', detail=False)
  2943. @register_model_view(PowerPanel, 'edit')
  2944. class PowerPanelEditView(generic.ObjectEditView):
  2945. queryset = PowerPanel.objects.all()
  2946. form = forms.PowerPanelForm
  2947. @register_model_view(PowerPanel, 'delete')
  2948. class PowerPanelDeleteView(generic.ObjectDeleteView):
  2949. queryset = PowerPanel.objects.all()
  2950. @register_model_view(PowerPanel, 'bulk_import', detail=False)
  2951. class PowerPanelBulkImportView(generic.BulkImportView):
  2952. queryset = PowerPanel.objects.all()
  2953. model_form = forms.PowerPanelImportForm
  2954. @register_model_view(PowerPanel, 'bulk_edit', path='edit', detail=False)
  2955. class PowerPanelBulkEditView(generic.BulkEditView):
  2956. queryset = PowerPanel.objects.all()
  2957. filterset = filtersets.PowerPanelFilterSet
  2958. table = tables.PowerPanelTable
  2959. form = forms.PowerPanelBulkEditForm
  2960. @register_model_view(PowerPanel, 'bulk_delete', path='delete', detail=False)
  2961. class PowerPanelBulkDeleteView(generic.BulkDeleteView):
  2962. queryset = PowerPanel.objects.annotate(
  2963. powerfeed_count=count_related(PowerFeed, 'power_panel')
  2964. )
  2965. filterset = filtersets.PowerPanelFilterSet
  2966. table = tables.PowerPanelTable
  2967. @register_model_view(PowerPanel, 'contacts')
  2968. class PowerPanelContactsView(ObjectContactsView):
  2969. queryset = PowerPanel.objects.all()
  2970. #
  2971. # Power feeds
  2972. #
  2973. @register_model_view(PowerFeed, 'list', path='', detail=False)
  2974. class PowerFeedListView(generic.ObjectListView):
  2975. queryset = PowerFeed.objects.all()
  2976. filterset = filtersets.PowerFeedFilterSet
  2977. filterset_form = forms.PowerFeedFilterForm
  2978. table = tables.PowerFeedTable
  2979. @register_model_view(PowerFeed)
  2980. class PowerFeedView(generic.ObjectView):
  2981. queryset = PowerFeed.objects.all()
  2982. @register_model_view(PowerFeed, 'add', detail=False)
  2983. @register_model_view(PowerFeed, 'edit')
  2984. class PowerFeedEditView(generic.ObjectEditView):
  2985. queryset = PowerFeed.objects.all()
  2986. form = forms.PowerFeedForm
  2987. @register_model_view(PowerFeed, 'delete')
  2988. class PowerFeedDeleteView(generic.ObjectDeleteView):
  2989. queryset = PowerFeed.objects.all()
  2990. @register_model_view(PowerFeed, 'bulk_import', detail=False)
  2991. class PowerFeedBulkImportView(generic.BulkImportView):
  2992. queryset = PowerFeed.objects.all()
  2993. model_form = forms.PowerFeedImportForm
  2994. @register_model_view(PowerFeed, 'bulk_edit', path='edit', detail=False)
  2995. class PowerFeedBulkEditView(generic.BulkEditView):
  2996. queryset = PowerFeed.objects.all()
  2997. filterset = filtersets.PowerFeedFilterSet
  2998. table = tables.PowerFeedTable
  2999. form = forms.PowerFeedBulkEditForm
  3000. @register_model_view(PowerFeed, 'bulk_disconnect', path='disconnect', detail=False)
  3001. class PowerFeedBulkDisconnectView(BulkDisconnectView):
  3002. queryset = PowerFeed.objects.all()
  3003. @register_model_view(PowerFeed, 'bulk_delete', path='delete', detail=False)
  3004. class PowerFeedBulkDeleteView(generic.BulkDeleteView):
  3005. queryset = PowerFeed.objects.all()
  3006. filterset = filtersets.PowerFeedFilterSet
  3007. table = tables.PowerFeedTable
  3008. # Trace view
  3009. register_model_view(PowerFeed, 'trace', kwargs={'model': PowerFeed})(PathTraceView)
  3010. #
  3011. # Virtual device contexts
  3012. #
  3013. @register_model_view(VirtualDeviceContext, 'list', path='', detail=False)
  3014. class VirtualDeviceContextListView(generic.ObjectListView):
  3015. queryset = VirtualDeviceContext.objects.annotate(
  3016. interface_count=count_related(Interface, 'vdcs'),
  3017. )
  3018. filterset = filtersets.VirtualDeviceContextFilterSet
  3019. filterset_form = forms.VirtualDeviceContextFilterForm
  3020. table = tables.VirtualDeviceContextTable
  3021. @register_model_view(VirtualDeviceContext)
  3022. class VirtualDeviceContextView(GetRelatedModelsMixin, generic.ObjectView):
  3023. queryset = VirtualDeviceContext.objects.all()
  3024. def get_extra_context(self, request, instance):
  3025. return {
  3026. 'related_models': self.get_related_models(
  3027. request,
  3028. instance,
  3029. extra=(
  3030. (Interface.objects.restrict(request.user, 'view').filter(vdcs__in=[instance]), 'vdc_id'),
  3031. ),
  3032. ),
  3033. }
  3034. @register_model_view(VirtualDeviceContext, 'add', detail=False)
  3035. @register_model_view(VirtualDeviceContext, 'edit')
  3036. class VirtualDeviceContextEditView(generic.ObjectEditView):
  3037. queryset = VirtualDeviceContext.objects.all()
  3038. form = forms.VirtualDeviceContextForm
  3039. @register_model_view(VirtualDeviceContext, 'delete')
  3040. class VirtualDeviceContextDeleteView(generic.ObjectDeleteView):
  3041. queryset = VirtualDeviceContext.objects.all()
  3042. @register_model_view(VirtualDeviceContext, 'bulk_import', detail=False)
  3043. class VirtualDeviceContextBulkImportView(generic.BulkImportView):
  3044. queryset = VirtualDeviceContext.objects.all()
  3045. model_form = forms.VirtualDeviceContextImportForm
  3046. @register_model_view(VirtualDeviceContext, 'bulk_edit', path='edit', detail=False)
  3047. class VirtualDeviceContextBulkEditView(generic.BulkEditView):
  3048. queryset = VirtualDeviceContext.objects.all()
  3049. filterset = filtersets.VirtualDeviceContextFilterSet
  3050. table = tables.VirtualDeviceContextTable
  3051. form = forms.VirtualDeviceContextBulkEditForm
  3052. @register_model_view(VirtualDeviceContext, 'bulk_delete', path='delete', detail=False)
  3053. class VirtualDeviceContextBulkDeleteView(generic.BulkDeleteView):
  3054. queryset = VirtualDeviceContext.objects.all()
  3055. filterset = filtersets.VirtualDeviceContextFilterSet
  3056. table = tables.VirtualDeviceContextTable
  3057. #
  3058. # MAC addresses
  3059. #
  3060. @register_model_view(MACAddress, 'list', path='', detail=False)
  3061. class MACAddressListView(generic.ObjectListView):
  3062. queryset = MACAddress.objects.all()
  3063. filterset = filtersets.MACAddressFilterSet
  3064. filterset_form = forms.MACAddressFilterForm
  3065. table = tables.MACAddressTable
  3066. @register_model_view(MACAddress)
  3067. class MACAddressView(generic.ObjectView):
  3068. queryset = MACAddress.objects.all()
  3069. @register_model_view(MACAddress, 'add', detail=False)
  3070. @register_model_view(MACAddress, 'edit')
  3071. class MACAddressEditView(generic.ObjectEditView):
  3072. queryset = MACAddress.objects.all()
  3073. form = forms.MACAddressForm
  3074. @register_model_view(MACAddress, 'delete')
  3075. class MACAddressDeleteView(generic.ObjectDeleteView):
  3076. queryset = MACAddress.objects.all()
  3077. @register_model_view(MACAddress, 'bulk_import', detail=False)
  3078. class MACAddressBulkImportView(generic.BulkImportView):
  3079. queryset = MACAddress.objects.all()
  3080. model_form = forms.MACAddressImportForm
  3081. @register_model_view(MACAddress, 'bulk_edit', path='edit', detail=False)
  3082. class MACAddressBulkEditView(generic.BulkEditView):
  3083. queryset = MACAddress.objects.all()
  3084. filterset = filtersets.MACAddressFilterSet
  3085. table = tables.MACAddressTable
  3086. form = forms.MACAddressBulkEditForm
  3087. @register_model_view(MACAddress, 'bulk_delete', path='delete', detail=False)
  3088. class MACAddressBulkDeleteView(generic.BulkDeleteView):
  3089. queryset = MACAddress.objects.all()
  3090. filterset = filtersets.MACAddressFilterSet
  3091. table = tables.MACAddressTable