views.py 61 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895
  1. from django.contrib.contenttypes.models import ContentType
  2. from django.db.models import Prefetch
  3. from django.db.models.expressions import RawSQL
  4. from django.shortcuts import get_object_or_404, redirect, render
  5. from django.urls import reverse
  6. from django.utils.translation import gettext_lazy as _
  7. from circuits.models import Provider
  8. from dcim.filtersets import InterfaceFilterSet
  9. from dcim.forms import InterfaceFilterForm
  10. from dcim.models import Device, Interface, Site
  11. from extras.ui.panels import CustomFieldsPanel, TagsPanel
  12. from netbox.object_actions import AddObject, BulkDelete, BulkEdit, BulkExport, BulkImport
  13. from netbox.ui import actions, layout
  14. from netbox.ui.panels import (
  15. CommentsPanel,
  16. ContextTablePanel,
  17. ObjectsTablePanel,
  18. PluginContentPanel,
  19. RelatedObjectsPanel,
  20. TemplatePanel,
  21. )
  22. from netbox.views import generic
  23. from utilities.query import count_related
  24. from utilities.tables import get_table_ordering
  25. from utilities.views import GetRelatedModelsMixin, ViewTab, register_model_view
  26. from virtualization.filtersets import VMInterfaceFilterSet
  27. from virtualization.forms import VMInterfaceFilterForm
  28. from virtualization.models import VirtualMachine, VMInterface
  29. from . import filtersets, forms, tables
  30. from .choices import PrefixStatusChoices
  31. from .constants import *
  32. from .models import *
  33. from .ui import panels
  34. from .utils import add_available_vlans, add_requested_prefixes, annotate_ip_space
  35. #
  36. # VRFs
  37. #
  38. @register_model_view(VRF, 'list', path='', detail=False)
  39. class VRFListView(generic.ObjectListView):
  40. queryset = VRF.objects.all()
  41. filterset = filtersets.VRFFilterSet
  42. filterset_form = forms.VRFFilterForm
  43. table = tables.VRFTable
  44. @register_model_view(VRF)
  45. class VRFView(GetRelatedModelsMixin, generic.ObjectView):
  46. queryset = VRF.objects.all()
  47. template_name = 'generic/object.html'
  48. layout = layout.Layout(
  49. layout.Row(
  50. layout.Column(
  51. panels.VRFPanel(),
  52. TagsPanel(),
  53. PluginContentPanel('left_page'),
  54. ),
  55. layout.Column(
  56. RelatedObjectsPanel(),
  57. CustomFieldsPanel(),
  58. CommentsPanel(),
  59. PluginContentPanel('right_page'),
  60. ),
  61. ),
  62. layout.Row(
  63. layout.Column(
  64. ContextTablePanel('import_targets_table', title=_('Import route targets')),
  65. ),
  66. layout.Column(
  67. ContextTablePanel('export_targets_table', title=_('Export route targets')),
  68. ),
  69. ),
  70. layout.Row(
  71. layout.Column(
  72. PluginContentPanel('full_width_page'),
  73. ),
  74. ),
  75. )
  76. def get_extra_context(self, request, instance):
  77. import_targets_table = tables.RouteTargetTable(
  78. instance.import_targets.all(),
  79. orderable=False
  80. )
  81. import_targets_table.configure(request)
  82. export_targets_table = tables.RouteTargetTable(
  83. instance.export_targets.all(),
  84. orderable=False
  85. )
  86. export_targets_table.configure(request)
  87. related_models = self.get_related_models(
  88. request,
  89. instance,
  90. omit=(Interface, VMInterface),
  91. extra=(
  92. (
  93. Interface.objects.restrict(request.user, 'view').filter(vrf=instance),
  94. 'vrf_id',
  95. _('Device Interfaces')
  96. ),
  97. (
  98. VMInterface.objects.restrict(request.user, 'view').filter(vrf=instance),
  99. 'vrf_id',
  100. _('VM Interfaces')
  101. ),
  102. ),
  103. )
  104. return {
  105. 'related_models': related_models,
  106. 'import_targets_table': import_targets_table,
  107. 'export_targets_table': export_targets_table,
  108. }
  109. @register_model_view(VRF, 'add', detail=False)
  110. @register_model_view(VRF, 'edit')
  111. class VRFEditView(generic.ObjectEditView):
  112. queryset = VRF.objects.all()
  113. form = forms.VRFForm
  114. @register_model_view(VRF, 'delete')
  115. class VRFDeleteView(generic.ObjectDeleteView):
  116. queryset = VRF.objects.all()
  117. @register_model_view(VRF, 'bulk_import', path='import', detail=False)
  118. class VRFBulkImportView(generic.BulkImportView):
  119. queryset = VRF.objects.all()
  120. model_form = forms.VRFImportForm
  121. @register_model_view(VRF, 'bulk_edit', path='edit', detail=False)
  122. class VRFBulkEditView(generic.BulkEditView):
  123. queryset = VRF.objects.all()
  124. filterset = filtersets.VRFFilterSet
  125. table = tables.VRFTable
  126. form = forms.VRFBulkEditForm
  127. @register_model_view(VRF, 'bulk_rename', path='rename', detail=False)
  128. class VRFBulkRenameView(generic.BulkRenameView):
  129. queryset = VRF.objects.all()
  130. filterset = filtersets.VRFFilterSet
  131. @register_model_view(VRF, 'bulk_delete', path='delete', detail=False)
  132. class VRFBulkDeleteView(generic.BulkDeleteView):
  133. queryset = VRF.objects.all()
  134. filterset = filtersets.VRFFilterSet
  135. table = tables.VRFTable
  136. #
  137. # Route targets
  138. #
  139. @register_model_view(RouteTarget, 'list', path='', detail=False)
  140. class RouteTargetListView(generic.ObjectListView):
  141. queryset = RouteTarget.objects.all()
  142. filterset = filtersets.RouteTargetFilterSet
  143. filterset_form = forms.RouteTargetFilterForm
  144. table = tables.RouteTargetTable
  145. @register_model_view(RouteTarget)
  146. class RouteTargetView(generic.ObjectView):
  147. queryset = RouteTarget.objects.all()
  148. template_name = 'generic/object.html'
  149. layout = layout.Layout(
  150. layout.Row(
  151. layout.Column(
  152. panels.RouteTargetPanel(),
  153. TagsPanel(),
  154. PluginContentPanel('left_page'),
  155. ),
  156. layout.Column(
  157. CustomFieldsPanel(),
  158. CommentsPanel(),
  159. PluginContentPanel('right_page'),
  160. ),
  161. ),
  162. layout.Row(
  163. layout.Column(
  164. ObjectsTablePanel(
  165. 'ipam.vrf',
  166. filters={'import_target_id': lambda ctx: ctx['object'].pk},
  167. title=_('Importing VRFs'),
  168. ),
  169. ),
  170. layout.Column(
  171. ObjectsTablePanel(
  172. 'ipam.vrf',
  173. filters={'export_target_id': lambda ctx: ctx['object'].pk},
  174. title=_('Exporting VRFs'),
  175. ),
  176. ),
  177. ),
  178. layout.Row(
  179. layout.Column(
  180. ObjectsTablePanel(
  181. 'vpn.l2vpn',
  182. filters={'import_target_id': lambda ctx: ctx['object'].pk},
  183. title=_('Importing L2VPNs'),
  184. ),
  185. ),
  186. layout.Column(
  187. ObjectsTablePanel(
  188. 'vpn.l2vpn',
  189. filters={'export_target_id': lambda ctx: ctx['object'].pk},
  190. title=_('Exporting L2VPNs'),
  191. ),
  192. ),
  193. ),
  194. layout.Row(
  195. layout.Column(
  196. PluginContentPanel('full_width_page'),
  197. ),
  198. ),
  199. )
  200. @register_model_view(RouteTarget, 'add', detail=False)
  201. @register_model_view(RouteTarget, 'edit')
  202. class RouteTargetEditView(generic.ObjectEditView):
  203. queryset = RouteTarget.objects.all()
  204. form = forms.RouteTargetForm
  205. @register_model_view(RouteTarget, 'delete')
  206. class RouteTargetDeleteView(generic.ObjectDeleteView):
  207. queryset = RouteTarget.objects.all()
  208. @register_model_view(RouteTarget, 'bulk_import', path='import', detail=False)
  209. class RouteTargetBulkImportView(generic.BulkImportView):
  210. queryset = RouteTarget.objects.all()
  211. model_form = forms.RouteTargetImportForm
  212. @register_model_view(RouteTarget, 'bulk_edit', path='edit', detail=False)
  213. class RouteTargetBulkEditView(generic.BulkEditView):
  214. queryset = RouteTarget.objects.all()
  215. filterset = filtersets.RouteTargetFilterSet
  216. table = tables.RouteTargetTable
  217. form = forms.RouteTargetBulkEditForm
  218. @register_model_view(RouteTarget, 'bulk_rename', path='rename', detail=False)
  219. class RouteTargetBulkRenameView(generic.BulkRenameView):
  220. queryset = RouteTarget.objects.all()
  221. filterset = filtersets.RouteTargetFilterSet
  222. @register_model_view(RouteTarget, 'bulk_delete', path='delete', detail=False)
  223. class RouteTargetBulkDeleteView(generic.BulkDeleteView):
  224. queryset = RouteTarget.objects.all()
  225. filterset = filtersets.RouteTargetFilterSet
  226. table = tables.RouteTargetTable
  227. #
  228. # RIRs
  229. #
  230. @register_model_view(RIR, 'list', path='', detail=False)
  231. class RIRListView(generic.ObjectListView):
  232. queryset = RIR.objects.annotate(
  233. aggregate_count=count_related(Aggregate, 'rir')
  234. )
  235. filterset = filtersets.RIRFilterSet
  236. filterset_form = forms.RIRFilterForm
  237. table = tables.RIRTable
  238. @register_model_view(RIR)
  239. class RIRView(GetRelatedModelsMixin, generic.ObjectView):
  240. queryset = RIR.objects.all()
  241. layout = layout.SimpleLayout(
  242. left_panels=[
  243. panels.RIRPanel(),
  244. TagsPanel(),
  245. ],
  246. right_panels=[
  247. RelatedObjectsPanel(),
  248. CommentsPanel(),
  249. CustomFieldsPanel(),
  250. ],
  251. )
  252. def get_extra_context(self, request, instance):
  253. return {
  254. 'related_models': self.get_related_models(request, instance),
  255. }
  256. @register_model_view(RIR, 'add', detail=False)
  257. @register_model_view(RIR, 'edit')
  258. class RIREditView(generic.ObjectEditView):
  259. queryset = RIR.objects.all()
  260. form = forms.RIRForm
  261. @register_model_view(RIR, 'delete')
  262. class RIRDeleteView(generic.ObjectDeleteView):
  263. queryset = RIR.objects.all()
  264. @register_model_view(RIR, 'bulk_import', path='import', detail=False)
  265. class RIRBulkImportView(generic.BulkImportView):
  266. queryset = RIR.objects.all()
  267. model_form = forms.RIRImportForm
  268. @register_model_view(RIR, 'bulk_edit', path='edit', detail=False)
  269. class RIRBulkEditView(generic.BulkEditView):
  270. queryset = RIR.objects.annotate(
  271. aggregate_count=count_related(Aggregate, 'rir')
  272. )
  273. filterset = filtersets.RIRFilterSet
  274. table = tables.RIRTable
  275. form = forms.RIRBulkEditForm
  276. @register_model_view(RIR, 'bulk_rename', path='rename', detail=False)
  277. class RIRBulkRenameView(generic.BulkRenameView):
  278. queryset = RIR.objects.all()
  279. filterset = filtersets.RIRFilterSet
  280. @register_model_view(RIR, 'bulk_delete', path='delete', detail=False)
  281. class RIRBulkDeleteView(generic.BulkDeleteView):
  282. queryset = RIR.objects.annotate(
  283. aggregate_count=count_related(Aggregate, 'rir')
  284. )
  285. filterset = filtersets.RIRFilterSet
  286. table = tables.RIRTable
  287. #
  288. # ASN ranges
  289. #
  290. @register_model_view(ASNRange, 'list', path='', detail=False)
  291. class ASNRangeListView(generic.ObjectListView):
  292. queryset = ASNRange.objects.annotate_asn_counts()
  293. filterset = filtersets.ASNRangeFilterSet
  294. filterset_form = forms.ASNRangeFilterForm
  295. table = tables.ASNRangeTable
  296. @register_model_view(ASNRange)
  297. class ASNRangeView(generic.ObjectView):
  298. queryset = ASNRange.objects.all()
  299. layout = layout.SimpleLayout(
  300. left_panels=[
  301. panels.ASNRangePanel(),
  302. TagsPanel(),
  303. ],
  304. right_panels=[
  305. CommentsPanel(),
  306. CustomFieldsPanel(),
  307. ],
  308. )
  309. @register_model_view(ASNRange, 'asns')
  310. class ASNRangeASNsView(generic.ObjectChildrenView):
  311. queryset = ASNRange.objects.all()
  312. child_model = ASN
  313. table = tables.ASNTable
  314. filterset = filtersets.ASNFilterSet
  315. filterset_form = forms.ASNFilterForm
  316. tab = ViewTab(
  317. label=_('ASNs'),
  318. badge=lambda x: x.get_child_asns().count(),
  319. permission='ipam.view_asn',
  320. weight=500
  321. )
  322. def get_children(self, request, parent):
  323. return parent.get_child_asns().restrict(request.user, 'view').annotate(
  324. site_count=count_related(Site, 'asns'),
  325. provider_count=count_related(Provider, 'asns')
  326. )
  327. @register_model_view(ASNRange, 'add', detail=False)
  328. @register_model_view(ASNRange, 'edit')
  329. class ASNRangeEditView(generic.ObjectEditView):
  330. queryset = ASNRange.objects.all()
  331. form = forms.ASNRangeForm
  332. @register_model_view(ASNRange, 'delete')
  333. class ASNRangeDeleteView(generic.ObjectDeleteView):
  334. queryset = ASNRange.objects.all()
  335. @register_model_view(ASNRange, 'bulk_import', path='import', detail=False)
  336. class ASNRangeBulkImportView(generic.BulkImportView):
  337. queryset = ASNRange.objects.all()
  338. model_form = forms.ASNRangeImportForm
  339. @register_model_view(ASNRange, 'bulk_edit', path='edit', detail=False)
  340. class ASNRangeBulkEditView(generic.BulkEditView):
  341. queryset = ASNRange.objects.annotate_asn_counts()
  342. filterset = filtersets.ASNRangeFilterSet
  343. table = tables.ASNRangeTable
  344. form = forms.ASNRangeBulkEditForm
  345. @register_model_view(ASNRange, 'bulk_rename', path='rename', detail=False)
  346. class ASNRangeBulkRenameView(generic.BulkRenameView):
  347. queryset = ASNRange.objects.all()
  348. filterset = filtersets.ASNRangeFilterSet
  349. @register_model_view(ASNRange, 'bulk_delete', path='delete', detail=False)
  350. class ASNRangeBulkDeleteView(generic.BulkDeleteView):
  351. queryset = ASNRange.objects.annotate_asn_counts()
  352. filterset = filtersets.ASNRangeFilterSet
  353. table = tables.ASNRangeTable
  354. #
  355. # ASNs
  356. #
  357. @register_model_view(ASN, 'list', path='', detail=False)
  358. class ASNListView(generic.ObjectListView):
  359. queryset = ASN.objects.annotate(
  360. site_count=count_related(Site, 'asns'),
  361. provider_count=count_related(Provider, 'asns')
  362. )
  363. filterset = filtersets.ASNFilterSet
  364. filterset_form = forms.ASNFilterForm
  365. table = tables.ASNTable
  366. @register_model_view(ASN)
  367. class ASNView(GetRelatedModelsMixin, generic.ObjectView):
  368. queryset = ASN.objects.all()
  369. layout = layout.SimpleLayout(
  370. left_panels=[
  371. panels.ASNPanel(),
  372. TagsPanel(),
  373. ],
  374. right_panels=[
  375. RelatedObjectsPanel(),
  376. CustomFieldsPanel(),
  377. CommentsPanel(),
  378. ],
  379. )
  380. def get_extra_context(self, request, instance):
  381. return {
  382. 'related_models': self.get_related_models(
  383. request,
  384. instance,
  385. extra=(
  386. (Site.objects.restrict(request.user, 'view').filter(asns__in=[instance]), 'asn_id'),
  387. (Provider.objects.restrict(request.user, 'view').filter(asns__in=[instance]), 'asn_id'),
  388. ),
  389. ),
  390. }
  391. @register_model_view(ASN, 'add', detail=False)
  392. @register_model_view(ASN, 'edit')
  393. class ASNEditView(generic.ObjectEditView):
  394. queryset = ASN.objects.all()
  395. form = forms.ASNForm
  396. @register_model_view(ASN, 'delete')
  397. class ASNDeleteView(generic.ObjectDeleteView):
  398. queryset = ASN.objects.all()
  399. @register_model_view(ASN, 'bulk_import', path='import', detail=False)
  400. class ASNBulkImportView(generic.BulkImportView):
  401. queryset = ASN.objects.all()
  402. model_form = forms.ASNImportForm
  403. @register_model_view(ASN, 'bulk_edit', path='edit', detail=False)
  404. class ASNBulkEditView(generic.BulkEditView):
  405. queryset = ASN.objects.annotate(
  406. site_count=count_related(Site, 'asns')
  407. )
  408. filterset = filtersets.ASNFilterSet
  409. table = tables.ASNTable
  410. form = forms.ASNBulkEditForm
  411. @register_model_view(ASN, 'bulk_rename', path='rename', detail=False)
  412. class ASNBulkRenameView(generic.BulkRenameView):
  413. queryset = ASN.objects.all()
  414. filterset = filtersets.ASNFilterSet
  415. @register_model_view(ASN, 'bulk_delete', path='delete', detail=False)
  416. class ASNBulkDeleteView(generic.BulkDeleteView):
  417. queryset = ASN.objects.annotate(
  418. site_count=count_related(Site, 'asns')
  419. )
  420. filterset = filtersets.ASNFilterSet
  421. table = tables.ASNTable
  422. #
  423. # Aggregates
  424. #
  425. @register_model_view(Aggregate, 'list', path='', detail=False)
  426. class AggregateListView(generic.ObjectListView):
  427. queryset = Aggregate.objects.annotate(
  428. child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
  429. )
  430. filterset = filtersets.AggregateFilterSet
  431. filterset_form = forms.AggregateFilterForm
  432. table = tables.AggregateTable
  433. actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete)
  434. @register_model_view(Aggregate)
  435. class AggregateView(generic.ObjectView):
  436. queryset = Aggregate.objects.all()
  437. layout = layout.SimpleLayout(
  438. left_panels=[
  439. panels.AggregatePanel(),
  440. ],
  441. right_panels=[
  442. CustomFieldsPanel(),
  443. TagsPanel(),
  444. CommentsPanel(),
  445. ],
  446. )
  447. @register_model_view(Aggregate, 'prefixes')
  448. class AggregatePrefixesView(generic.ObjectChildrenView):
  449. queryset = Aggregate.objects.all()
  450. child_model = Prefix
  451. table = tables.PrefixTable
  452. filterset = filtersets.PrefixFilterSet
  453. filterset_form = forms.PrefixFilterForm
  454. template_name = 'ipam/aggregate/prefixes.html'
  455. tab = ViewTab(
  456. label=_('Prefixes'),
  457. badge=lambda x: x.get_child_prefixes().count(),
  458. permission='ipam.view_prefix',
  459. weight=500
  460. )
  461. def get_children(self, request, parent):
  462. return Prefix.objects.restrict(request.user, 'view').filter(
  463. prefix__net_contained_or_equal=str(parent.prefix)
  464. ).prefetch_related('scope', 'role', 'tenant', 'tenant__group', 'vlan')
  465. def prep_table_data(self, request, queryset, parent):
  466. # Determine whether to show assigned prefixes, available prefixes, or both
  467. show_available = bool(request.GET.get('show_available', 'true') == 'true')
  468. show_assigned = bool(request.GET.get('show_assigned', 'true') == 'true')
  469. return add_requested_prefixes(parent.prefix, queryset, show_available, show_assigned)
  470. def get_extra_context(self, request, instance):
  471. return {
  472. 'bulk_querystring': f'within={instance.prefix}',
  473. 'first_available_prefix': instance.get_first_available_prefix(),
  474. 'show_available': bool(request.GET.get('show_available', 'true') == 'true'),
  475. 'show_assigned': bool(request.GET.get('show_assigned', 'true') == 'true'),
  476. }
  477. @register_model_view(Aggregate, 'add', detail=False)
  478. @register_model_view(Aggregate, 'edit')
  479. class AggregateEditView(generic.ObjectEditView):
  480. queryset = Aggregate.objects.all()
  481. form = forms.AggregateForm
  482. @register_model_view(Aggregate, 'delete')
  483. class AggregateDeleteView(generic.ObjectDeleteView):
  484. queryset = Aggregate.objects.all()
  485. @register_model_view(Aggregate, 'bulk_import', path='import', detail=False)
  486. class AggregateBulkImportView(generic.BulkImportView):
  487. queryset = Aggregate.objects.all()
  488. model_form = forms.AggregateImportForm
  489. @register_model_view(Aggregate, 'bulk_edit', path='edit', detail=False)
  490. class AggregateBulkEditView(generic.BulkEditView):
  491. queryset = Aggregate.objects.annotate(
  492. child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
  493. )
  494. filterset = filtersets.AggregateFilterSet
  495. table = tables.AggregateTable
  496. form = forms.AggregateBulkEditForm
  497. @register_model_view(Aggregate, 'bulk_delete', path='delete', detail=False)
  498. class AggregateBulkDeleteView(generic.BulkDeleteView):
  499. queryset = Aggregate.objects.annotate(
  500. child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
  501. )
  502. filterset = filtersets.AggregateFilterSet
  503. table = tables.AggregateTable
  504. #
  505. # Prefix/VLAN roles
  506. #
  507. @register_model_view(Role, 'list', path='', detail=False)
  508. class RoleListView(generic.ObjectListView):
  509. queryset = Role.objects.annotate(
  510. prefix_count=count_related(Prefix, 'role'),
  511. iprange_count=count_related(IPRange, 'role'),
  512. vlan_count=count_related(VLAN, 'role'),
  513. asn_count=count_related(ASN, 'role')
  514. )
  515. filterset = filtersets.RoleFilterSet
  516. filterset_form = forms.RoleFilterForm
  517. table = tables.RoleTable
  518. @register_model_view(Role)
  519. class RoleView(GetRelatedModelsMixin, generic.ObjectView):
  520. queryset = Role.objects.all()
  521. layout = layout.SimpleLayout(
  522. left_panels=[
  523. panels.RolePanel(),
  524. TagsPanel(),
  525. ],
  526. right_panels=[
  527. RelatedObjectsPanel(),
  528. CommentsPanel(),
  529. CustomFieldsPanel(),
  530. ],
  531. )
  532. def get_extra_context(self, request, instance):
  533. return {
  534. 'related_models': self.get_related_models(request, instance),
  535. }
  536. @register_model_view(Role, 'add', detail=False)
  537. @register_model_view(Role, 'edit')
  538. class RoleEditView(generic.ObjectEditView):
  539. queryset = Role.objects.all()
  540. form = forms.RoleForm
  541. @register_model_view(Role, 'delete')
  542. class RoleDeleteView(generic.ObjectDeleteView):
  543. queryset = Role.objects.all()
  544. @register_model_view(Role, 'bulk_import', path='import', detail=False)
  545. class RoleBulkImportView(generic.BulkImportView):
  546. queryset = Role.objects.all()
  547. model_form = forms.RoleImportForm
  548. @register_model_view(Role, 'bulk_edit', path='edit', detail=False)
  549. class RoleBulkEditView(generic.BulkEditView):
  550. queryset = Role.objects.all()
  551. filterset = filtersets.RoleFilterSet
  552. table = tables.RoleTable
  553. form = forms.RoleBulkEditForm
  554. @register_model_view(Role, 'bulk_rename', path='rename', detail=False)
  555. class RoleBulkRenameView(generic.BulkRenameView):
  556. queryset = Role.objects.all()
  557. filterset = filtersets.RoleFilterSet
  558. @register_model_view(Role, 'bulk_delete', path='delete', detail=False)
  559. class RoleBulkDeleteView(generic.BulkDeleteView):
  560. queryset = Role.objects.all()
  561. filterset = filtersets.RoleFilterSet
  562. table = tables.RoleTable
  563. #
  564. # Prefixes
  565. #
  566. @register_model_view(Prefix, 'list', path='', detail=False)
  567. class PrefixListView(generic.ObjectListView):
  568. queryset = Prefix.objects.all()
  569. filterset = filtersets.PrefixFilterSet
  570. filterset_form = forms.PrefixFilterForm
  571. table = tables.PrefixTable
  572. template_name = 'ipam/prefix_list.html'
  573. actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete)
  574. @register_model_view(Prefix)
  575. class PrefixView(generic.ObjectView):
  576. queryset = Prefix.objects.all()
  577. layout = layout.SimpleLayout(
  578. left_panels=[
  579. panels.PrefixPanel(),
  580. ],
  581. right_panels=[
  582. TemplatePanel('ipam/panels/prefix_addressing.html'),
  583. CustomFieldsPanel(),
  584. TagsPanel(),
  585. CommentsPanel(),
  586. ],
  587. bottom_panels=[
  588. ContextTablePanel('duplicate_prefix_table', title=_('Duplicate prefixes')),
  589. ContextTablePanel('parent_prefix_table', title=_('Parent prefixes')),
  590. ],
  591. )
  592. def get_extra_context(self, request, instance):
  593. # Parent prefixes table
  594. parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
  595. Q(vrf=instance.vrf) | Q(vrf__isnull=True, status=PrefixStatusChoices.STATUS_CONTAINER)
  596. ).filter(
  597. prefix__net_contains=str(instance.prefix)
  598. ).prefetch_related(
  599. 'scope', 'role', 'tenant', 'vlan',
  600. )
  601. parent_prefix_table = tables.PrefixTable(
  602. list(parent_prefixes),
  603. exclude=('vrf', 'utilization'),
  604. orderable=False
  605. )
  606. parent_prefix_table.configure(request)
  607. # Duplicate prefixes table
  608. duplicate_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
  609. vrf=instance.vrf, prefix=str(instance.prefix)
  610. ).exclude(
  611. pk=instance.pk
  612. ).prefetch_related(
  613. 'scope', 'role', 'tenant', 'vlan',
  614. )
  615. duplicate_prefix_table = tables.PrefixTable(
  616. list(duplicate_prefixes),
  617. exclude=('vrf', 'utilization'),
  618. orderable=False
  619. )
  620. duplicate_prefix_table.configure(request)
  621. context = {
  622. 'parent_prefix_table': parent_prefix_table,
  623. }
  624. if duplicate_prefixes.exists():
  625. context['duplicate_prefix_table'] = duplicate_prefix_table
  626. return context
  627. @register_model_view(Prefix, 'prefixes')
  628. class PrefixPrefixesView(generic.ObjectChildrenView):
  629. queryset = Prefix.objects.all()
  630. child_model = Prefix
  631. table = tables.PrefixTable
  632. filterset = filtersets.PrefixFilterSet
  633. filterset_form = forms.PrefixFilterForm
  634. template_name = 'ipam/prefix/prefixes.html'
  635. tab = ViewTab(
  636. label=_('Child Prefixes'),
  637. badge=lambda x: x.get_child_prefixes().count(),
  638. permission='ipam.view_prefix',
  639. weight=500
  640. )
  641. def get_children(self, request, parent):
  642. return parent.get_child_prefixes().restrict(request.user, 'view').prefetch_related(
  643. 'scope', 'vrf', 'vlan', 'role', 'tenant', 'tenant__group'
  644. )
  645. def prep_table_data(self, request, queryset, parent):
  646. # Determine whether to show assigned prefixes, available prefixes, or both
  647. show_available = bool(request.GET.get('show_available', 'true') == 'true')
  648. show_assigned = bool(request.GET.get('show_assigned', 'true') == 'true')
  649. return add_requested_prefixes(parent.prefix, queryset, show_available, show_assigned)
  650. def get_extra_context(self, request, instance):
  651. return {
  652. 'bulk_querystring': f"vrf_id={instance.vrf.pk if instance.vrf else '0'}&within={instance.prefix}",
  653. 'first_available_prefix': instance.get_first_available_prefix(),
  654. 'show_available': bool(request.GET.get('show_available', 'true') == 'true'),
  655. 'show_assigned': bool(request.GET.get('show_assigned', 'true') == 'true'),
  656. }
  657. @register_model_view(Prefix, 'ipranges', path='ip-ranges')
  658. class PrefixIPRangesView(generic.ObjectChildrenView):
  659. queryset = Prefix.objects.all()
  660. child_model = IPRange
  661. table = tables.IPRangeTable
  662. filterset = filtersets.IPRangeFilterSet
  663. filterset_form = forms.IPRangeFilterForm
  664. template_name = 'ipam/prefix/ip_ranges.html'
  665. tab = ViewTab(
  666. label=_('Child Ranges'),
  667. badge=lambda x: x.get_child_ranges().count(),
  668. permission='ipam.view_iprange',
  669. weight=600
  670. )
  671. def get_children(self, request, parent):
  672. return parent.get_child_ranges().restrict(request.user, 'view').prefetch_related(
  673. 'tenant__group',
  674. )
  675. def get_extra_context(self, request, instance):
  676. return {
  677. 'bulk_querystring': f"vrf_id={instance.vrf.pk if instance.vrf else '0'}&parent={instance.prefix}",
  678. 'first_available_ip': instance.get_first_available_ip(),
  679. }
  680. @register_model_view(Prefix, 'ipaddresses', path='ip-addresses')
  681. class PrefixIPAddressesView(generic.ObjectChildrenView):
  682. queryset = Prefix.objects.all()
  683. child_model = IPAddress
  684. table = tables.AnnotatedIPAddressTable
  685. filterset = filtersets.IPAddressFilterSet
  686. filterset_form = forms.IPAddressFilterForm
  687. template_name = 'ipam/prefix/ip_addresses.html'
  688. tab = ViewTab(
  689. label=_('IP Addresses'),
  690. badge=lambda x: x.get_child_ips().count(),
  691. permission='ipam.view_ipaddress',
  692. weight=700
  693. )
  694. def get_children(self, request, parent):
  695. return parent.get_child_ips().restrict(request.user, 'view').prefetch_related('vrf', 'tenant', 'tenant__group')
  696. def prep_table_data(self, request, queryset, parent):
  697. if not request.GET.get('q') and not get_table_ordering(request, self.table):
  698. return annotate_ip_space(parent)
  699. return queryset
  700. def get_extra_context(self, request, instance):
  701. return {
  702. 'bulk_querystring': f"vrf_id={instance.vrf.pk if instance.vrf else '0'}&parent={instance.prefix}",
  703. 'first_available_ip': instance.get_first_available_ip(),
  704. }
  705. @register_model_view(Prefix, 'add', detail=False)
  706. @register_model_view(Prefix, 'edit')
  707. class PrefixEditView(generic.ObjectEditView):
  708. queryset = Prefix.objects.all()
  709. form = forms.PrefixForm
  710. template_name = 'ipam/prefix_edit.html'
  711. @register_model_view(Prefix, 'delete')
  712. class PrefixDeleteView(generic.ObjectDeleteView):
  713. queryset = Prefix.objects.all()
  714. @register_model_view(Prefix, 'bulk_add', path='bulk-add', detail=False)
  715. class PrefixBulkCreateView(generic.BulkCreateView):
  716. queryset = Prefix.objects.all()
  717. form = forms.IPNetworkBulkCreateForm
  718. model_form = forms.PrefixBulkAddForm
  719. pattern_target = 'prefix'
  720. template_name = 'ipam/prefix_bulk_add.html'
  721. @register_model_view(Prefix, 'bulk_import', path='import', detail=False)
  722. class PrefixBulkImportView(generic.BulkImportView):
  723. queryset = Prefix.objects.all()
  724. model_form = forms.PrefixImportForm
  725. @register_model_view(Prefix, 'bulk_edit', path='edit', detail=False)
  726. class PrefixBulkEditView(generic.BulkEditView):
  727. queryset = Prefix.objects.prefetch_related('vrf__tenant')
  728. filterset = filtersets.PrefixFilterSet
  729. table = tables.PrefixTable
  730. form = forms.PrefixBulkEditForm
  731. @register_model_view(Prefix, 'bulk_delete', path='delete', detail=False)
  732. class PrefixBulkDeleteView(generic.BulkDeleteView):
  733. queryset = Prefix.objects.prefetch_related('vrf__tenant')
  734. filterset = filtersets.PrefixFilterSet
  735. table = tables.PrefixTable
  736. #
  737. # IP Ranges
  738. #
  739. @register_model_view(IPRange, 'list', path='', detail=False)
  740. class IPRangeListView(generic.ObjectListView):
  741. queryset = IPRange.objects.all()
  742. filterset = filtersets.IPRangeFilterSet
  743. filterset_form = forms.IPRangeFilterForm
  744. table = tables.IPRangeTable
  745. @register_model_view(IPRange)
  746. class IPRangeView(generic.ObjectView):
  747. queryset = IPRange.objects.all()
  748. layout = layout.SimpleLayout(
  749. left_panels=[
  750. panels.IPRangePanel(),
  751. ],
  752. right_panels=[
  753. TagsPanel(),
  754. CustomFieldsPanel(),
  755. CommentsPanel(),
  756. ],
  757. bottom_panels=[
  758. ContextTablePanel('parent_prefixes_table', title=_('Parent prefixes')),
  759. ],
  760. )
  761. def get_extra_context(self, request, instance):
  762. # Parent prefixes table
  763. parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
  764. Q(prefix__net_contains_or_equals=str(instance.start_address.ip)),
  765. Q(prefix__net_contains_or_equals=str(instance.end_address.ip)),
  766. vrf=instance.vrf
  767. ).prefetch_related(
  768. 'scope', 'role', 'tenant', 'vlan', 'role'
  769. )
  770. parent_prefixes_table = tables.PrefixTable(
  771. list(parent_prefixes),
  772. exclude=('vrf', 'utilization'),
  773. orderable=False
  774. )
  775. parent_prefixes_table.configure(request)
  776. return {
  777. 'parent_prefixes_table': parent_prefixes_table,
  778. }
  779. @register_model_view(IPRange, 'ipaddresses', path='ip-addresses')
  780. class IPRangeIPAddressesView(generic.ObjectChildrenView):
  781. queryset = IPRange.objects.all()
  782. child_model = IPAddress
  783. table = tables.IPAddressTable
  784. filterset = filtersets.IPAddressFilterSet
  785. filterset_form = forms.IPRangeFilterForm
  786. template_name = 'ipam/iprange/ip_addresses.html'
  787. tab = ViewTab(
  788. label=_('IP Addresses'),
  789. badge=lambda x: x.get_child_ips().count(),
  790. permission='ipam.view_ipaddress',
  791. weight=500
  792. )
  793. def get_children(self, request, parent):
  794. return parent.get_child_ips().restrict(request.user, 'view')
  795. @register_model_view(IPRange, 'add', detail=False)
  796. @register_model_view(IPRange, 'edit')
  797. class IPRangeEditView(generic.ObjectEditView):
  798. queryset = IPRange.objects.all()
  799. form = forms.IPRangeForm
  800. @register_model_view(IPRange, 'delete')
  801. class IPRangeDeleteView(generic.ObjectDeleteView):
  802. queryset = IPRange.objects.all()
  803. @register_model_view(IPRange, 'bulk_import', path='import', detail=False)
  804. class IPRangeBulkImportView(generic.BulkImportView):
  805. queryset = IPRange.objects.all()
  806. model_form = forms.IPRangeImportForm
  807. @register_model_view(IPRange, 'bulk_edit', path='edit', detail=False)
  808. class IPRangeBulkEditView(generic.BulkEditView):
  809. queryset = IPRange.objects.all()
  810. filterset = filtersets.IPRangeFilterSet
  811. table = tables.IPRangeTable
  812. form = forms.IPRangeBulkEditForm
  813. @register_model_view(IPRange, 'bulk_rename', path='rename', detail=False)
  814. class IPRangeBulkRenameView(generic.BulkRenameView):
  815. queryset = IPRange.objects.all()
  816. filterset = filtersets.IPRangeFilterSet
  817. @register_model_view(IPRange, 'bulk_delete', path='delete', detail=False)
  818. class IPRangeBulkDeleteView(generic.BulkDeleteView):
  819. queryset = IPRange.objects.all()
  820. filterset = filtersets.IPRangeFilterSet
  821. table = tables.IPRangeTable
  822. #
  823. # IP addresses
  824. #
  825. @register_model_view(IPAddress, 'list', path='', detail=False)
  826. class IPAddressListView(generic.ObjectListView):
  827. queryset = IPAddress.objects.all()
  828. filterset = filtersets.IPAddressFilterSet
  829. filterset_form = forms.IPAddressFilterForm
  830. table = tables.IPAddressTable
  831. actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete)
  832. @register_model_view(IPAddress)
  833. class IPAddressView(generic.ObjectView):
  834. queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant')
  835. layout = layout.SimpleLayout(
  836. left_panels=[
  837. panels.IPAddressPanel(),
  838. TagsPanel(),
  839. CustomFieldsPanel(),
  840. CommentsPanel(),
  841. ],
  842. right_panels=[
  843. ContextTablePanel('parent_prefixes_table', title=_('Parent prefixes')),
  844. ContextTablePanel('duplicate_ips_table', title=_('Duplicate IPs')),
  845. ObjectsTablePanel(
  846. 'ipam.service',
  847. filters={'ip_address_id': lambda ctx: ctx['object'].pk},
  848. title=_('Application services'),
  849. ),
  850. ],
  851. )
  852. def get_extra_context(self, request, instance):
  853. # Parent prefixes table
  854. parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
  855. vrf=instance.vrf,
  856. prefix__net_contains_or_equals=str(instance.address.ip)
  857. ).prefetch_related(
  858. 'scope', 'role'
  859. )
  860. parent_prefixes_table = tables.PrefixTable(
  861. list(parent_prefixes),
  862. exclude=('vrf', 'utilization'),
  863. orderable=False
  864. )
  865. parent_prefixes_table.configure(request)
  866. # Duplicate IPs table
  867. duplicate_ips = IPAddress.objects.restrict(request.user, 'view').filter(
  868. vrf=instance.vrf,
  869. address=str(instance.address)
  870. ).exclude(
  871. pk=instance.pk
  872. ).prefetch_related(
  873. 'nat_inside'
  874. )
  875. # Exclude anycast IPs if this IP is anycast
  876. if instance.role == IPAddressRoleChoices.ROLE_ANYCAST:
  877. duplicate_ips = duplicate_ips.exclude(role=IPAddressRoleChoices.ROLE_ANYCAST)
  878. # Limit to a maximum of 10 duplicates displayed here
  879. duplicate_ips_table = tables.IPAddressTable(duplicate_ips[:10], orderable=False)
  880. duplicate_ips_table.configure(request)
  881. context = {
  882. 'parent_prefixes_table': parent_prefixes_table,
  883. }
  884. if duplicate_ips.exists():
  885. context['duplicate_ips_table'] = duplicate_ips_table
  886. return context
  887. @register_model_view(IPAddress, 'add', detail=False)
  888. @register_model_view(IPAddress, 'edit')
  889. class IPAddressEditView(generic.ObjectEditView):
  890. queryset = IPAddress.objects.all()
  891. form = forms.IPAddressForm
  892. template_name = 'ipam/ipaddress_edit.html'
  893. def alter_object(self, obj, request, url_args, url_kwargs):
  894. if 'interface' in request.GET:
  895. try:
  896. obj.assigned_object = Interface.objects.get(pk=request.GET['interface'])
  897. except (ValueError, Interface.DoesNotExist):
  898. pass
  899. elif 'vminterface' in request.GET:
  900. try:
  901. obj.assigned_object = VMInterface.objects.get(pk=request.GET['vminterface'])
  902. except (ValueError, VMInterface.DoesNotExist):
  903. pass
  904. elif 'fhrpgroup' in request.GET:
  905. try:
  906. obj.assigned_object = FHRPGroup.objects.get(pk=request.GET['fhrpgroup'])
  907. except (ValueError, FHRPGroup.DoesNotExist):
  908. pass
  909. return obj
  910. def get_extra_addanother_params(self, request):
  911. if 'interface' in request.GET:
  912. return {'interface': request.GET['interface']}
  913. if 'vminterface' in request.GET:
  914. return {'vminterface': request.GET['vminterface']}
  915. return {}
  916. # TODO: Standardize or remove this view
  917. @register_model_view(IPAddress, 'assign', path='assign', detail=False)
  918. class IPAddressAssignView(generic.ObjectView):
  919. """
  920. Search for IPAddresses to be assigned to an Interface.
  921. """
  922. queryset = IPAddress.objects.all()
  923. def dispatch(self, request, *args, **kwargs):
  924. # Redirect user if an interface has not been provided
  925. if 'interface' not in request.GET and 'vminterface' not in request.GET:
  926. return redirect('ipam:ipaddress_add')
  927. return super().dispatch(request, *args, **kwargs)
  928. def get(self, request):
  929. form = forms.IPAddressAssignForm()
  930. return render(request, 'ipam/ipaddress_assign.html', {
  931. 'form': form,
  932. 'return_url': request.GET.get('return_url', ''),
  933. })
  934. def post(self, request):
  935. form = forms.IPAddressAssignForm(request.POST)
  936. table = None
  937. if form.is_valid():
  938. addresses = self.queryset.prefetch_related('vrf', 'tenant')
  939. # Limit to 100 results
  940. addresses = filtersets.IPAddressFilterSet(request.POST, addresses).qs[:100]
  941. table = tables.IPAddressAssignTable(addresses)
  942. table.configure(request)
  943. return render(request, 'ipam/ipaddress_assign.html', {
  944. 'form': form,
  945. 'table': table,
  946. 'return_url': request.GET.get('return_url'),
  947. })
  948. @register_model_view(IPAddress, 'delete')
  949. class IPAddressDeleteView(generic.ObjectDeleteView):
  950. queryset = IPAddress.objects.all()
  951. @register_model_view(IPAddress, 'bulk_add', path='bulk-add', detail=False)
  952. class IPAddressBulkCreateView(generic.BulkCreateView):
  953. queryset = IPAddress.objects.all()
  954. form = forms.IPNetworkBulkCreateForm
  955. model_form = forms.IPAddressBulkAddForm
  956. pattern_target = 'address'
  957. template_name = 'ipam/ipaddress_bulk_add.html'
  958. @register_model_view(IPAddress, 'bulk_import', path='import', detail=False)
  959. class IPAddressBulkImportView(generic.BulkImportView):
  960. queryset = IPAddress.objects.all()
  961. model_form = forms.IPAddressImportForm
  962. @register_model_view(IPAddress, 'bulk_edit', path='edit', detail=False)
  963. class IPAddressBulkEditView(generic.BulkEditView):
  964. queryset = IPAddress.objects.prefetch_related('vrf__tenant')
  965. filterset = filtersets.IPAddressFilterSet
  966. table = tables.IPAddressTable
  967. form = forms.IPAddressBulkEditForm
  968. @register_model_view(IPAddress, 'bulk_delete', path='delete', detail=False)
  969. class IPAddressBulkDeleteView(generic.BulkDeleteView):
  970. queryset = IPAddress.objects.prefetch_related('vrf__tenant')
  971. filterset = filtersets.IPAddressFilterSet
  972. table = tables.IPAddressTable
  973. @register_model_view(IPAddress, 'related_ips', path='related-ip-addresses')
  974. class IPAddressRelatedIPsView(generic.ObjectChildrenView):
  975. queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant')
  976. child_model = IPAddress
  977. table = tables.IPAddressTable
  978. filterset = filtersets.IPAddressFilterSet
  979. filterset_form = forms.IPAddressFilterForm
  980. tab = ViewTab(
  981. label=_('Related IPs'),
  982. badge=lambda x: x.get_related_ips().count(),
  983. weight=500,
  984. hide_if_empty=True,
  985. )
  986. def get_children(self, request, parent):
  987. return parent.get_related_ips().restrict(request.user, 'view')
  988. #
  989. # VLAN groups
  990. #
  991. @register_model_view(VLANGroup, 'list', path='', detail=False)
  992. class VLANGroupListView(generic.ObjectListView):
  993. queryset = VLANGroup.objects.annotate_utilization()
  994. filterset = filtersets.VLANGroupFilterSet
  995. filterset_form = forms.VLANGroupFilterForm
  996. table = tables.VLANGroupTable
  997. @register_model_view(VLANGroup)
  998. class VLANGroupView(GetRelatedModelsMixin, generic.ObjectView):
  999. queryset = VLANGroup.objects.annotate_utilization()
  1000. layout = layout.SimpleLayout(
  1001. left_panels=[
  1002. panels.VLANGroupPanel(),
  1003. TagsPanel(),
  1004. ],
  1005. right_panels=[
  1006. RelatedObjectsPanel(),
  1007. CommentsPanel(),
  1008. CustomFieldsPanel(),
  1009. ],
  1010. )
  1011. def get_extra_context(self, request, instance):
  1012. return {
  1013. 'related_models': self.get_related_models(request, instance),
  1014. }
  1015. @register_model_view(VLANGroup, 'add', detail=False)
  1016. @register_model_view(VLANGroup, 'edit')
  1017. class VLANGroupEditView(generic.ObjectEditView):
  1018. queryset = VLANGroup.objects.all()
  1019. form = forms.VLANGroupForm
  1020. @register_model_view(VLANGroup, 'delete')
  1021. class VLANGroupDeleteView(generic.ObjectDeleteView):
  1022. queryset = VLANGroup.objects.all()
  1023. @register_model_view(VLANGroup, 'bulk_import', path='import', detail=False)
  1024. class VLANGroupBulkImportView(generic.BulkImportView):
  1025. queryset = VLANGroup.objects.all()
  1026. model_form = forms.VLANGroupImportForm
  1027. @register_model_view(VLANGroup, 'bulk_edit', path='edit', detail=False)
  1028. class VLANGroupBulkEditView(generic.BulkEditView):
  1029. queryset = VLANGroup.objects.annotate_utilization().prefetch_related('tags')
  1030. filterset = filtersets.VLANGroupFilterSet
  1031. table = tables.VLANGroupTable
  1032. form = forms.VLANGroupBulkEditForm
  1033. @register_model_view(VLANGroup, 'bulk_rename', path='rename', detail=False)
  1034. class VLANGroupBulkRenameView(generic.BulkRenameView):
  1035. queryset = VLANGroup.objects.all()
  1036. filterset = filtersets.VLANGroupFilterSet
  1037. @register_model_view(VLANGroup, 'bulk_delete', path='delete', detail=False)
  1038. class VLANGroupBulkDeleteView(generic.BulkDeleteView):
  1039. queryset = VLANGroup.objects.annotate_utilization().prefetch_related('tags')
  1040. filterset = filtersets.VLANGroupFilterSet
  1041. table = tables.VLANGroupTable
  1042. @register_model_view(VLANGroup, 'vlans')
  1043. class VLANGroupVLANsView(generic.ObjectChildrenView):
  1044. queryset = VLANGroup.objects.all()
  1045. child_model = VLAN
  1046. table = tables.VLANTable
  1047. filterset = filtersets.VLANFilterSet
  1048. filterset_form = forms.VLANFilterForm
  1049. tab = ViewTab(
  1050. label=_('VLANs'),
  1051. badge=lambda x: x.get_child_vlans().count(),
  1052. permission='ipam.view_vlan',
  1053. weight=500
  1054. )
  1055. def get_children(self, request, parent):
  1056. return parent.get_child_vlans().restrict(request.user, 'view').prefetch_related(
  1057. Prefetch('prefixes', queryset=Prefix.objects.restrict(request.user)),
  1058. 'tenant', 'site', 'role',
  1059. )
  1060. def prep_table_data(self, request, queryset, parent):
  1061. if not get_table_ordering(request, self.table):
  1062. return add_available_vlans(queryset, parent)
  1063. return queryset
  1064. #
  1065. # VLAN Translation Policies
  1066. #
  1067. @register_model_view(VLANTranslationPolicy, 'list', path='', detail=False)
  1068. class VLANTranslationPolicyListView(generic.ObjectListView):
  1069. queryset = VLANTranslationPolicy.objects.annotate(
  1070. rule_count=count_related(VLANTranslationRule, 'policy'),
  1071. )
  1072. filterset = filtersets.VLANTranslationPolicyFilterSet
  1073. filterset_form = forms.VLANTranslationPolicyFilterForm
  1074. table = tables.VLANTranslationPolicyTable
  1075. @register_model_view(VLANTranslationPolicy)
  1076. class VLANTranslationPolicyView(generic.ObjectView):
  1077. queryset = VLANTranslationPolicy.objects.all()
  1078. template_name = 'generic/object.html'
  1079. layout = layout.SimpleLayout(
  1080. left_panels=[
  1081. panels.VLANTranslationPolicyPanel(),
  1082. ],
  1083. right_panels=[
  1084. TagsPanel(),
  1085. CustomFieldsPanel(),
  1086. CommentsPanel(),
  1087. ],
  1088. bottom_panels=[
  1089. ObjectsTablePanel(
  1090. 'ipam.vlantranslationrule',
  1091. filters={'policy_id': lambda ctx: ctx['object'].pk},
  1092. title=_('VLAN translation rules'),
  1093. exclude_columns=['policy'],
  1094. actions=[
  1095. actions.AddObject(
  1096. 'ipam.vlantranslationrule',
  1097. url_params={'policy': lambda ctx: ctx['object'].pk},
  1098. label=_('Add Rule'),
  1099. ),
  1100. ],
  1101. ),
  1102. ],
  1103. )
  1104. @register_model_view(VLANTranslationPolicy, 'add', detail=False)
  1105. @register_model_view(VLANTranslationPolicy, 'edit')
  1106. class VLANTranslationPolicyEditView(generic.ObjectEditView):
  1107. queryset = VLANTranslationPolicy.objects.all()
  1108. form = forms.VLANTranslationPolicyForm
  1109. @register_model_view(VLANTranslationPolicy, 'delete')
  1110. class VLANTranslationPolicyDeleteView(generic.ObjectDeleteView):
  1111. queryset = VLANTranslationPolicy.objects.all()
  1112. @register_model_view(VLANTranslationPolicy, 'bulk_import', path='import', detail=False)
  1113. class VLANTranslationPolicyBulkImportView(generic.BulkImportView):
  1114. queryset = VLANTranslationPolicy.objects.all()
  1115. model_form = forms.VLANTranslationPolicyImportForm
  1116. @register_model_view(VLANTranslationPolicy, 'bulk_edit', path='edit', detail=False)
  1117. class VLANTranslationPolicyBulkEditView(generic.BulkEditView):
  1118. queryset = VLANTranslationPolicy.objects.all()
  1119. filterset = filtersets.VLANTranslationPolicyFilterSet
  1120. table = tables.VLANTranslationPolicyTable
  1121. form = forms.VLANTranslationPolicyBulkEditForm
  1122. @register_model_view(VLANTranslationPolicy, 'bulk_rename', path='rename', detail=False)
  1123. class VLANTranslationPolicyBulkRenameView(generic.BulkRenameView):
  1124. queryset = VLANTranslationPolicy.objects.all()
  1125. filterset = filtersets.VLANTranslationPolicyFilterSet
  1126. @register_model_view(VLANTranslationPolicy, 'bulk_delete', path='delete', detail=False)
  1127. class VLANTranslationPolicyBulkDeleteView(generic.BulkDeleteView):
  1128. queryset = VLANTranslationPolicy.objects.all()
  1129. filterset = filtersets.VLANTranslationPolicyFilterSet
  1130. table = tables.VLANTranslationPolicyTable
  1131. #
  1132. # VLAN Translation Rules
  1133. #
  1134. @register_model_view(VLANTranslationRule, 'list', path='', detail=False)
  1135. class VLANTranslationRuleListView(generic.ObjectListView):
  1136. queryset = VLANTranslationRule.objects.all()
  1137. filterset = filtersets.VLANTranslationRuleFilterSet
  1138. filterset_form = forms.VLANTranslationRuleFilterForm
  1139. table = tables.VLANTranslationRuleTable
  1140. actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete)
  1141. @register_model_view(VLANTranslationRule)
  1142. class VLANTranslationRuleView(generic.ObjectView):
  1143. queryset = VLANTranslationRule.objects.all()
  1144. template_name = 'generic/object.html'
  1145. layout = layout.SimpleLayout(
  1146. left_panels=[
  1147. panels.VLANTranslationRulePanel(),
  1148. ],
  1149. right_panels=[
  1150. TagsPanel(),
  1151. CustomFieldsPanel(),
  1152. ],
  1153. )
  1154. @register_model_view(VLANTranslationRule, 'add', detail=False)
  1155. @register_model_view(VLANTranslationRule, 'edit')
  1156. class VLANTranslationRuleEditView(generic.ObjectEditView):
  1157. queryset = VLANTranslationRule.objects.all()
  1158. form = forms.VLANTranslationRuleForm
  1159. @register_model_view(VLANTranslationRule, 'delete')
  1160. class VLANTranslationRuleDeleteView(generic.ObjectDeleteView):
  1161. queryset = VLANTranslationRule.objects.all()
  1162. @register_model_view(VLANTranslationRule, 'bulk_import', path='import', detail=False)
  1163. class VLANTranslationRuleBulkImportView(generic.BulkImportView):
  1164. queryset = VLANTranslationRule.objects.all()
  1165. model_form = forms.VLANTranslationRuleImportForm
  1166. @register_model_view(VLANTranslationRule, 'bulk_edit', path='edit', detail=False)
  1167. class VLANTranslationRuleBulkEditView(generic.BulkEditView):
  1168. queryset = VLANTranslationRule.objects.all()
  1169. filterset = filtersets.VLANTranslationRuleFilterSet
  1170. table = tables.VLANTranslationRuleTable
  1171. form = forms.VLANTranslationRuleBulkEditForm
  1172. @register_model_view(VLANTranslationRule, 'bulk_delete', path='delete', detail=False)
  1173. class VLANTranslationRuleBulkDeleteView(generic.BulkDeleteView):
  1174. queryset = VLANTranslationRule.objects.all()
  1175. filterset = filtersets.VLANTranslationRuleFilterSet
  1176. table = tables.VLANTranslationRuleTable
  1177. #
  1178. # FHRP groups
  1179. #
  1180. @register_model_view(FHRPGroup, 'list', path='', detail=False)
  1181. class FHRPGroupListView(generic.ObjectListView):
  1182. queryset = FHRPGroup.objects.annotate(
  1183. member_count=count_related(FHRPGroupAssignment, 'group')
  1184. )
  1185. filterset = filtersets.FHRPGroupFilterSet
  1186. filterset_form = forms.FHRPGroupFilterForm
  1187. table = tables.FHRPGroupTable
  1188. @register_model_view(FHRPGroup)
  1189. class FHRPGroupView(GetRelatedModelsMixin, generic.ObjectView):
  1190. queryset = FHRPGroup.objects.annotate(
  1191. member_count=count_related(FHRPGroupAssignment, 'group')
  1192. )
  1193. layout = layout.SimpleLayout(
  1194. left_panels=[
  1195. panels.FHRPGroupPanel(),
  1196. TagsPanel(),
  1197. CommentsPanel(),
  1198. ],
  1199. right_panels=[
  1200. panels.FHRPGroupAuthPanel(),
  1201. RelatedObjectsPanel(),
  1202. CustomFieldsPanel(),
  1203. ],
  1204. bottom_panels=[
  1205. ObjectsTablePanel(
  1206. 'ipam.ipaddress',
  1207. filters={'fhrpgroup_id': lambda ctx: ctx['object'].pk},
  1208. title=_('Virtual IP addresses'),
  1209. actions=[
  1210. actions.AddObject(
  1211. 'ipam.ipaddress',
  1212. url_params={'fhrpgroup': lambda ctx: ctx['object'].pk},
  1213. label=_('Add IP Address'),
  1214. ),
  1215. ],
  1216. ),
  1217. ContextTablePanel('members_table', title=_('Members')),
  1218. ],
  1219. )
  1220. def get_extra_context(self, request, instance):
  1221. # Get assigned interfaces
  1222. members_table = tables.FHRPGroupAssignmentTable(
  1223. data=FHRPGroupAssignment.objects.restrict(request.user, 'view').filter(group=instance),
  1224. orderable=False
  1225. )
  1226. members_table.configure(request)
  1227. members_table.columns.hide('group')
  1228. return {
  1229. 'related_models': self.get_related_models(
  1230. request, instance,
  1231. extra=(
  1232. (
  1233. Service.objects.restrict(request.user, 'view').filter(
  1234. parent_object_type=ContentType.objects.get_for_model(FHRPGroup),
  1235. parent_object_id=instance.id,
  1236. ),
  1237. 'fhrpgroup_id'
  1238. ),
  1239. ),
  1240. ),
  1241. 'members_table': members_table,
  1242. }
  1243. @register_model_view(FHRPGroup, 'add', detail=False)
  1244. @register_model_view(FHRPGroup, 'edit')
  1245. class FHRPGroupEditView(generic.ObjectEditView):
  1246. queryset = FHRPGroup.objects.all()
  1247. form = forms.FHRPGroupForm
  1248. def get_return_url(self, request, obj=None):
  1249. return_url = super().get_return_url(request, obj)
  1250. # If we're redirecting the user to the FHRPGroupAssignment creation form,
  1251. # initialize the group field with the FHRPGroup we just saved.
  1252. if return_url.startswith(reverse('ipam:fhrpgroupassignment_add')):
  1253. return_url += f'&group={obj.pk}'
  1254. return return_url
  1255. def alter_object(self, obj, request, url_args, url_kwargs):
  1256. # Workaround to solve #10719. Capture the current user on the FHRPGroup instance so that
  1257. # we can evaluate permissions during the creation of a new IPAddress within the form.
  1258. obj._user = request.user
  1259. return obj
  1260. @register_model_view(FHRPGroup, 'delete')
  1261. class FHRPGroupDeleteView(generic.ObjectDeleteView):
  1262. queryset = FHRPGroup.objects.all()
  1263. @register_model_view(FHRPGroup, 'bulk_import', path='import', detail=False)
  1264. class FHRPGroupBulkImportView(generic.BulkImportView):
  1265. queryset = FHRPGroup.objects.all()
  1266. model_form = forms.FHRPGroupImportForm
  1267. @register_model_view(FHRPGroup, 'bulk_edit', path='edit', detail=False)
  1268. class FHRPGroupBulkEditView(generic.BulkEditView):
  1269. queryset = FHRPGroup.objects.all()
  1270. filterset = filtersets.FHRPGroupFilterSet
  1271. table = tables.FHRPGroupTable
  1272. form = forms.FHRPGroupBulkEditForm
  1273. @register_model_view(FHRPGroup, 'bulk_rename', path='rename', detail=False)
  1274. class FHRPGroupBulkRenameView(generic.BulkRenameView):
  1275. queryset = FHRPGroup.objects.all()
  1276. filterset = filtersets.FHRPGroupFilterSet
  1277. @register_model_view(FHRPGroup, 'bulk_delete', path='delete', detail=False)
  1278. class FHRPGroupBulkDeleteView(generic.BulkDeleteView):
  1279. queryset = FHRPGroup.objects.all()
  1280. filterset = filtersets.FHRPGroupFilterSet
  1281. table = tables.FHRPGroupTable
  1282. #
  1283. # FHRP group assignments
  1284. #
  1285. @register_model_view(FHRPGroupAssignment, 'add', detail=False)
  1286. @register_model_view(FHRPGroupAssignment, 'edit')
  1287. class FHRPGroupAssignmentEditView(generic.ObjectEditView):
  1288. queryset = FHRPGroupAssignment.objects.all()
  1289. form = forms.FHRPGroupAssignmentForm
  1290. def alter_object(self, instance, request, args, kwargs):
  1291. if not instance.pk:
  1292. # Assign the interface based on URL kwargs
  1293. content_type = get_object_or_404(ContentType, pk=request.GET.get('interface_type'))
  1294. instance.interface = get_object_or_404(content_type.model_class(), pk=request.GET.get('interface_id'))
  1295. return instance
  1296. def get_extra_addanother_params(self, request):
  1297. return {
  1298. 'interface_type': request.GET.get('interface_type'),
  1299. 'interface_id': request.GET.get('interface_id'),
  1300. }
  1301. @register_model_view(FHRPGroupAssignment, 'delete')
  1302. class FHRPGroupAssignmentDeleteView(generic.ObjectDeleteView):
  1303. queryset = FHRPGroupAssignment.objects.all()
  1304. #
  1305. # VLANs
  1306. #
  1307. @register_model_view(VLAN, 'list', path='', detail=False)
  1308. class VLANListView(generic.ObjectListView):
  1309. queryset = VLAN.objects.all()
  1310. filterset = filtersets.VLANFilterSet
  1311. filterset_form = forms.VLANFilterForm
  1312. table = tables.VLANTable
  1313. @register_model_view(VLAN)
  1314. class VLANView(generic.ObjectView):
  1315. queryset = VLAN.objects.all()
  1316. layout = layout.SimpleLayout(
  1317. left_panels=[
  1318. panels.VLANPanel(),
  1319. ],
  1320. right_panels=[
  1321. CustomFieldsPanel(),
  1322. TagsPanel(),
  1323. CommentsPanel(),
  1324. ],
  1325. bottom_panels=[
  1326. ObjectsTablePanel(
  1327. 'ipam.prefix',
  1328. filters={'vlan_id': lambda ctx: ctx['object'].pk},
  1329. title=_('Prefixes'),
  1330. exclude_columns=['vlan'],
  1331. actions=[
  1332. actions.AddObject(
  1333. 'ipam.prefix',
  1334. url_params={
  1335. 'tenant': lambda ctx: ctx['object'].tenant_id,
  1336. 'scope_type': lambda ctx: (
  1337. ContentType.objects.get_for_model(Site).pk if ctx['object'].site_id else None
  1338. ),
  1339. 'scope': lambda ctx: ctx['object'].site_id,
  1340. 'vlan': lambda ctx: ctx['object'].pk,
  1341. },
  1342. label=_('Add a Prefix'),
  1343. ),
  1344. ],
  1345. ),
  1346. panels.VLANCustomerVLANsPanel(),
  1347. ],
  1348. )
  1349. @register_model_view(VLAN, 'interfaces')
  1350. class VLANInterfacesView(generic.ObjectChildrenView):
  1351. queryset = VLAN.objects.all()
  1352. child_model = Interface
  1353. table = tables.VLANDevicesTable
  1354. filterset = InterfaceFilterSet
  1355. filterset_form = InterfaceFilterForm
  1356. tab = ViewTab(
  1357. label=_('Device Interfaces'),
  1358. badge=lambda x: x.get_interfaces().count(),
  1359. permission='dcim.view_interface',
  1360. weight=500
  1361. )
  1362. def get_children(self, request, parent):
  1363. return parent.get_interfaces().restrict(request.user, 'view')
  1364. @register_model_view(VLAN, 'vminterfaces', path='vm-interfaces')
  1365. class VLANVMInterfacesView(generic.ObjectChildrenView):
  1366. queryset = VLAN.objects.all()
  1367. child_model = VMInterface
  1368. table = tables.VLANVirtualMachinesTable
  1369. filterset = VMInterfaceFilterSet
  1370. filterset_form = VMInterfaceFilterForm
  1371. tab = ViewTab(
  1372. label=_('VM Interfaces'),
  1373. badge=lambda x: x.get_vminterfaces().count(),
  1374. permission='virtualization.view_vminterface',
  1375. weight=510
  1376. )
  1377. def get_children(self, request, parent):
  1378. return parent.get_vminterfaces().restrict(request.user, 'view')
  1379. @register_model_view(VLAN, 'add', detail=False)
  1380. @register_model_view(VLAN, 'edit')
  1381. class VLANEditView(generic.ObjectEditView):
  1382. queryset = VLAN.objects.all()
  1383. form = forms.VLANForm
  1384. template_name = 'ipam/vlan_edit.html'
  1385. @register_model_view(VLAN, 'delete')
  1386. class VLANDeleteView(generic.ObjectDeleteView):
  1387. queryset = VLAN.objects.all()
  1388. @register_model_view(VLAN, 'bulk_import', path='import', detail=False)
  1389. class VLANBulkImportView(generic.BulkImportView):
  1390. queryset = VLAN.objects.all()
  1391. model_form = forms.VLANImportForm
  1392. @register_model_view(VLAN, 'bulk_edit', path='edit', detail=False)
  1393. class VLANBulkEditView(generic.BulkEditView):
  1394. queryset = VLAN.objects.all()
  1395. filterset = filtersets.VLANFilterSet
  1396. table = tables.VLANTable
  1397. form = forms.VLANBulkEditForm
  1398. @register_model_view(VLAN, 'bulk_rename', path='rename', detail=False)
  1399. class VLANBulkRenameView(generic.BulkRenameView):
  1400. queryset = VLAN.objects.all()
  1401. filterset = filtersets.VLANFilterSet
  1402. @register_model_view(VLAN, 'bulk_delete', path='delete', detail=False)
  1403. class VLANBulkDeleteView(generic.BulkDeleteView):
  1404. queryset = VLAN.objects.all()
  1405. filterset = filtersets.VLANFilterSet
  1406. table = tables.VLANTable
  1407. #
  1408. # Service templates
  1409. #
  1410. @register_model_view(ServiceTemplate, 'list', path='', detail=False)
  1411. class ServiceTemplateListView(generic.ObjectListView):
  1412. queryset = ServiceTemplate.objects.all()
  1413. filterset = filtersets.ServiceTemplateFilterSet
  1414. filterset_form = forms.ServiceTemplateFilterForm
  1415. table = tables.ServiceTemplateTable
  1416. @register_model_view(ServiceTemplate)
  1417. class ServiceTemplateView(generic.ObjectView):
  1418. queryset = ServiceTemplate.objects.all()
  1419. template_name = 'generic/object.html'
  1420. layout = layout.SimpleLayout(
  1421. left_panels=[
  1422. panels.ServiceTemplatePanel(),
  1423. ],
  1424. right_panels=[
  1425. CustomFieldsPanel(),
  1426. TagsPanel(),
  1427. CommentsPanel(),
  1428. ],
  1429. )
  1430. @register_model_view(ServiceTemplate, 'add', detail=False)
  1431. @register_model_view(ServiceTemplate, 'edit')
  1432. class ServiceTemplateEditView(generic.ObjectEditView):
  1433. queryset = ServiceTemplate.objects.all()
  1434. form = forms.ServiceTemplateForm
  1435. @register_model_view(ServiceTemplate, 'delete')
  1436. class ServiceTemplateDeleteView(generic.ObjectDeleteView):
  1437. queryset = ServiceTemplate.objects.all()
  1438. @register_model_view(ServiceTemplate, 'bulk_import', path='import', detail=False)
  1439. class ServiceTemplateBulkImportView(generic.BulkImportView):
  1440. queryset = ServiceTemplate.objects.all()
  1441. model_form = forms.ServiceTemplateImportForm
  1442. @register_model_view(ServiceTemplate, 'bulk_edit', path='edit', detail=False)
  1443. class ServiceTemplateBulkEditView(generic.BulkEditView):
  1444. queryset = ServiceTemplate.objects.all()
  1445. filterset = filtersets.ServiceTemplateFilterSet
  1446. table = tables.ServiceTemplateTable
  1447. form = forms.ServiceTemplateBulkEditForm
  1448. @register_model_view(ServiceTemplate, 'bulk_rename', path='rename', detail=False)
  1449. class ServiceTemplateBulkRenameView(generic.BulkRenameView):
  1450. queryset = ServiceTemplate.objects.all()
  1451. filterset = filtersets.ServiceTemplateFilterSet
  1452. @register_model_view(ServiceTemplate, 'bulk_delete', path='delete', detail=False)
  1453. class ServiceTemplateBulkDeleteView(generic.BulkDeleteView):
  1454. queryset = ServiceTemplate.objects.all()
  1455. filterset = filtersets.ServiceTemplateFilterSet
  1456. table = tables.ServiceTemplateTable
  1457. #
  1458. # Services
  1459. #
  1460. @register_model_view(Service, 'list', path='', detail=False)
  1461. class ServiceListView(generic.ObjectListView):
  1462. queryset = Service.objects.prefetch_related('parent')
  1463. filterset = filtersets.ServiceFilterSet
  1464. filterset_form = forms.ServiceFilterForm
  1465. table = tables.ServiceTable
  1466. @register_model_view(Service)
  1467. class ServiceView(generic.ObjectView):
  1468. queryset = Service.objects.all()
  1469. layout = layout.SimpleLayout(
  1470. left_panels=[
  1471. panels.ServicePanel(),
  1472. ],
  1473. right_panels=[
  1474. CustomFieldsPanel(),
  1475. TagsPanel(),
  1476. CommentsPanel(),
  1477. ],
  1478. )
  1479. def get_extra_context(self, request, instance):
  1480. context = {}
  1481. match instance.parent:
  1482. case Device():
  1483. context['breadcrumb_queryparam'] = 'device_id'
  1484. case VirtualMachine():
  1485. context['breadcrumb_queryparam'] = 'virtual_machine_id'
  1486. case FHRPGroup():
  1487. context['breadcrumb_queryparam'] = 'fhrpgroup_id'
  1488. return context
  1489. @register_model_view(Service, 'add', detail=False)
  1490. class ServiceCreateView(generic.ObjectEditView):
  1491. queryset = Service.objects.all()
  1492. form = forms.ServiceCreateForm
  1493. @register_model_view(Service, 'edit')
  1494. class ServiceEditView(generic.ObjectEditView):
  1495. queryset = Service.objects.all()
  1496. form = forms.ServiceForm
  1497. @register_model_view(Service, 'delete')
  1498. class ServiceDeleteView(generic.ObjectDeleteView):
  1499. queryset = Service.objects.all()
  1500. @register_model_view(Service, 'bulk_import', path='import', detail=False)
  1501. class ServiceBulkImportView(generic.BulkImportView):
  1502. queryset = Service.objects.all()
  1503. model_form = forms.ServiceImportForm
  1504. @register_model_view(Service, 'bulk_edit', path='edit', detail=False)
  1505. class ServiceBulkEditView(generic.BulkEditView):
  1506. queryset = Service.objects.prefetch_related('parent')
  1507. filterset = filtersets.ServiceFilterSet
  1508. table = tables.ServiceTable
  1509. form = forms.ServiceBulkEditForm
  1510. @register_model_view(Service, 'bulk_rename', path='rename', detail=False)
  1511. class ServiceBulkRenameView(generic.BulkRenameView):
  1512. queryset = Service.objects.all()
  1513. filterset = filtersets.ServiceFilterSet
  1514. @register_model_view(Service, 'bulk_delete', path='delete', detail=False)
  1515. class ServiceBulkDeleteView(generic.BulkDeleteView):
  1516. queryset = Service.objects.prefetch_related('parent')
  1517. filterset = filtersets.ServiceFilterSet
  1518. table = tables.ServiceTable