views.py 60 KB

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