views.py 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482
  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 ipam.tables import VLANTranslationRuleTable
  12. from netbox.views import generic
  13. from utilities.query import count_related
  14. from utilities.tables import get_table_ordering
  15. from utilities.views import GetRelatedModelsMixin, ViewTab, register_model_view
  16. from virtualization.filtersets import VMInterfaceFilterSet
  17. from virtualization.forms import VMInterfaceFilterForm
  18. from virtualization.models import VirtualMachine, VMInterface
  19. from . import filtersets, forms, tables
  20. from .choices import PrefixStatusChoices
  21. from .constants import *
  22. from .models import *
  23. from .utils import add_requested_prefixes, add_available_vlans, annotate_ip_space
  24. #
  25. # VRFs
  26. #
  27. @register_model_view(VRF, 'list', path='', detail=False)
  28. class VRFListView(generic.ObjectListView):
  29. queryset = VRF.objects.all()
  30. filterset = filtersets.VRFFilterSet
  31. filterset_form = forms.VRFFilterForm
  32. table = tables.VRFTable
  33. @register_model_view(VRF)
  34. class VRFView(GetRelatedModelsMixin, generic.ObjectView):
  35. queryset = VRF.objects.all()
  36. def get_extra_context(self, request, instance):
  37. import_targets_table = tables.RouteTargetTable(
  38. instance.import_targets.all(),
  39. orderable=False
  40. )
  41. export_targets_table = tables.RouteTargetTable(
  42. instance.export_targets.all(),
  43. orderable=False
  44. )
  45. return {
  46. 'related_models': self.get_related_models(request, instance, omit=[Interface, VMInterface]),
  47. 'import_targets_table': import_targets_table,
  48. 'export_targets_table': export_targets_table,
  49. }
  50. @register_model_view(VRF, 'add', detail=False)
  51. @register_model_view(VRF, 'edit')
  52. class VRFEditView(generic.ObjectEditView):
  53. queryset = VRF.objects.all()
  54. form = forms.VRFForm
  55. @register_model_view(VRF, 'delete')
  56. class VRFDeleteView(generic.ObjectDeleteView):
  57. queryset = VRF.objects.all()
  58. @register_model_view(VRF, 'bulk_import', path='import', detail=False)
  59. class VRFBulkImportView(generic.BulkImportView):
  60. queryset = VRF.objects.all()
  61. model_form = forms.VRFImportForm
  62. @register_model_view(VRF, 'bulk_edit', path='edit', detail=False)
  63. class VRFBulkEditView(generic.BulkEditView):
  64. queryset = VRF.objects.all()
  65. filterset = filtersets.VRFFilterSet
  66. table = tables.VRFTable
  67. form = forms.VRFBulkEditForm
  68. @register_model_view(VRF, 'bulk_delete', path='delete', detail=False)
  69. class VRFBulkDeleteView(generic.BulkDeleteView):
  70. queryset = VRF.objects.all()
  71. filterset = filtersets.VRFFilterSet
  72. table = tables.VRFTable
  73. #
  74. # Route targets
  75. #
  76. @register_model_view(RouteTarget, 'list', path='', detail=False)
  77. class RouteTargetListView(generic.ObjectListView):
  78. queryset = RouteTarget.objects.all()
  79. filterset = filtersets.RouteTargetFilterSet
  80. filterset_form = forms.RouteTargetFilterForm
  81. table = tables.RouteTargetTable
  82. @register_model_view(RouteTarget)
  83. class RouteTargetView(generic.ObjectView):
  84. queryset = RouteTarget.objects.all()
  85. @register_model_view(RouteTarget, 'add', detail=False)
  86. @register_model_view(RouteTarget, 'edit')
  87. class RouteTargetEditView(generic.ObjectEditView):
  88. queryset = RouteTarget.objects.all()
  89. form = forms.RouteTargetForm
  90. @register_model_view(RouteTarget, 'delete')
  91. class RouteTargetDeleteView(generic.ObjectDeleteView):
  92. queryset = RouteTarget.objects.all()
  93. @register_model_view(RouteTarget, 'bulk_import', path='import', detail=False)
  94. class RouteTargetBulkImportView(generic.BulkImportView):
  95. queryset = RouteTarget.objects.all()
  96. model_form = forms.RouteTargetImportForm
  97. @register_model_view(RouteTarget, 'bulk_edit', path='edit', detail=False)
  98. class RouteTargetBulkEditView(generic.BulkEditView):
  99. queryset = RouteTarget.objects.all()
  100. filterset = filtersets.RouteTargetFilterSet
  101. table = tables.RouteTargetTable
  102. form = forms.RouteTargetBulkEditForm
  103. @register_model_view(RouteTarget, 'bulk_delete', path='delete', detail=False)
  104. class RouteTargetBulkDeleteView(generic.BulkDeleteView):
  105. queryset = RouteTarget.objects.all()
  106. filterset = filtersets.RouteTargetFilterSet
  107. table = tables.RouteTargetTable
  108. #
  109. # RIRs
  110. #
  111. @register_model_view(RIR, 'list', path='', detail=False)
  112. class RIRListView(generic.ObjectListView):
  113. queryset = RIR.objects.annotate(
  114. aggregate_count=count_related(Aggregate, 'rir')
  115. )
  116. filterset = filtersets.RIRFilterSet
  117. filterset_form = forms.RIRFilterForm
  118. table = tables.RIRTable
  119. @register_model_view(RIR)
  120. class RIRView(GetRelatedModelsMixin, generic.ObjectView):
  121. queryset = RIR.objects.all()
  122. def get_extra_context(self, request, instance):
  123. return {
  124. 'related_models': self.get_related_models(request, instance),
  125. }
  126. @register_model_view(RIR, 'add', detail=False)
  127. @register_model_view(RIR, 'edit')
  128. class RIREditView(generic.ObjectEditView):
  129. queryset = RIR.objects.all()
  130. form = forms.RIRForm
  131. @register_model_view(RIR, 'delete')
  132. class RIRDeleteView(generic.ObjectDeleteView):
  133. queryset = RIR.objects.all()
  134. @register_model_view(RIR, 'bulk_import', path='import', detail=False)
  135. class RIRBulkImportView(generic.BulkImportView):
  136. queryset = RIR.objects.all()
  137. model_form = forms.RIRImportForm
  138. @register_model_view(RIR, 'bulk_edit', path='edit', detail=False)
  139. class RIRBulkEditView(generic.BulkEditView):
  140. queryset = RIR.objects.annotate(
  141. aggregate_count=count_related(Aggregate, 'rir')
  142. )
  143. filterset = filtersets.RIRFilterSet
  144. table = tables.RIRTable
  145. form = forms.RIRBulkEditForm
  146. @register_model_view(RIR, 'bulk_delete', path='delete', detail=False)
  147. class RIRBulkDeleteView(generic.BulkDeleteView):
  148. queryset = RIR.objects.annotate(
  149. aggregate_count=count_related(Aggregate, 'rir')
  150. )
  151. filterset = filtersets.RIRFilterSet
  152. table = tables.RIRTable
  153. #
  154. # ASN ranges
  155. #
  156. @register_model_view(ASNRange, 'list', path='', detail=False)
  157. class ASNRangeListView(generic.ObjectListView):
  158. queryset = ASNRange.objects.annotate_asn_counts()
  159. filterset = filtersets.ASNRangeFilterSet
  160. filterset_form = forms.ASNRangeFilterForm
  161. table = tables.ASNRangeTable
  162. @register_model_view(ASNRange)
  163. class ASNRangeView(generic.ObjectView):
  164. queryset = ASNRange.objects.all()
  165. @register_model_view(ASNRange, 'asns')
  166. class ASNRangeASNsView(generic.ObjectChildrenView):
  167. queryset = ASNRange.objects.all()
  168. child_model = ASN
  169. table = tables.ASNTable
  170. filterset = filtersets.ASNFilterSet
  171. filterset_form = forms.ASNFilterForm
  172. tab = ViewTab(
  173. label=_('ASNs'),
  174. badge=lambda x: x.get_child_asns().count(),
  175. permission='ipam.view_asn',
  176. weight=500
  177. )
  178. def get_children(self, request, parent):
  179. return parent.get_child_asns().restrict(request.user, 'view').annotate(
  180. site_count=count_related(Site, 'asns'),
  181. provider_count=count_related(Provider, 'asns')
  182. )
  183. @register_model_view(ASNRange, 'add', detail=False)
  184. @register_model_view(ASNRange, 'edit')
  185. class ASNRangeEditView(generic.ObjectEditView):
  186. queryset = ASNRange.objects.all()
  187. form = forms.ASNRangeForm
  188. @register_model_view(ASNRange, 'delete')
  189. class ASNRangeDeleteView(generic.ObjectDeleteView):
  190. queryset = ASNRange.objects.all()
  191. @register_model_view(ASNRange, 'bulk_import', path='import', detail=False)
  192. class ASNRangeBulkImportView(generic.BulkImportView):
  193. queryset = ASNRange.objects.all()
  194. model_form = forms.ASNRangeImportForm
  195. @register_model_view(ASNRange, 'bulk_edit', path='edit', detail=False)
  196. class ASNRangeBulkEditView(generic.BulkEditView):
  197. queryset = ASNRange.objects.annotate_asn_counts()
  198. filterset = filtersets.ASNRangeFilterSet
  199. table = tables.ASNRangeTable
  200. form = forms.ASNRangeBulkEditForm
  201. @register_model_view(ASNRange, 'bulk_delete', path='delete', detail=False)
  202. class ASNRangeBulkDeleteView(generic.BulkDeleteView):
  203. queryset = ASNRange.objects.annotate_asn_counts()
  204. filterset = filtersets.ASNRangeFilterSet
  205. table = tables.ASNRangeTable
  206. #
  207. # ASNs
  208. #
  209. @register_model_view(ASN, 'list', path='', detail=False)
  210. class ASNListView(generic.ObjectListView):
  211. queryset = ASN.objects.annotate(
  212. site_count=count_related(Site, 'asns'),
  213. provider_count=count_related(Provider, 'asns')
  214. )
  215. filterset = filtersets.ASNFilterSet
  216. filterset_form = forms.ASNFilterForm
  217. table = tables.ASNTable
  218. @register_model_view(ASN)
  219. class ASNView(GetRelatedModelsMixin, generic.ObjectView):
  220. queryset = ASN.objects.all()
  221. def get_extra_context(self, request, instance):
  222. return {
  223. 'related_models': self.get_related_models(
  224. request,
  225. instance,
  226. extra=(
  227. (Site.objects.restrict(request.user, 'view').filter(asns__in=[instance]), 'asn_id'),
  228. (Provider.objects.restrict(request.user, 'view').filter(asns__in=[instance]), 'asn_id'),
  229. ),
  230. ),
  231. }
  232. @register_model_view(ASN, 'add', detail=False)
  233. @register_model_view(ASN, 'edit')
  234. class ASNEditView(generic.ObjectEditView):
  235. queryset = ASN.objects.all()
  236. form = forms.ASNForm
  237. @register_model_view(ASN, 'delete')
  238. class ASNDeleteView(generic.ObjectDeleteView):
  239. queryset = ASN.objects.all()
  240. @register_model_view(ASN, 'bulk_import', path='import', detail=False)
  241. class ASNBulkImportView(generic.BulkImportView):
  242. queryset = ASN.objects.all()
  243. model_form = forms.ASNImportForm
  244. @register_model_view(ASN, 'bulk_edit', path='edit', detail=False)
  245. class ASNBulkEditView(generic.BulkEditView):
  246. queryset = ASN.objects.annotate(
  247. site_count=count_related(Site, 'asns')
  248. )
  249. filterset = filtersets.ASNFilterSet
  250. table = tables.ASNTable
  251. form = forms.ASNBulkEditForm
  252. @register_model_view(ASN, 'bulk_delete', path='delete', detail=False)
  253. class ASNBulkDeleteView(generic.BulkDeleteView):
  254. queryset = ASN.objects.annotate(
  255. site_count=count_related(Site, 'asns')
  256. )
  257. filterset = filtersets.ASNFilterSet
  258. table = tables.ASNTable
  259. #
  260. # Aggregates
  261. #
  262. @register_model_view(Aggregate, 'list', path='', detail=False)
  263. class AggregateListView(generic.ObjectListView):
  264. queryset = Aggregate.objects.annotate(
  265. child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
  266. )
  267. filterset = filtersets.AggregateFilterSet
  268. filterset_form = forms.AggregateFilterForm
  269. table = tables.AggregateTable
  270. @register_model_view(Aggregate)
  271. class AggregateView(generic.ObjectView):
  272. queryset = Aggregate.objects.all()
  273. @register_model_view(Aggregate, 'prefixes')
  274. class AggregatePrefixesView(generic.ObjectChildrenView):
  275. queryset = Aggregate.objects.all()
  276. child_model = Prefix
  277. table = tables.PrefixTable
  278. filterset = filtersets.PrefixFilterSet
  279. filterset_form = forms.PrefixFilterForm
  280. template_name = 'ipam/aggregate/prefixes.html'
  281. tab = ViewTab(
  282. label=_('Prefixes'),
  283. badge=lambda x: x.get_child_prefixes().count(),
  284. permission='ipam.view_prefix',
  285. weight=500
  286. )
  287. def get_children(self, request, parent):
  288. return Prefix.objects.restrict(request.user, 'view').filter(
  289. prefix__net_contained_or_equal=str(parent.prefix)
  290. ).prefetch_related('scope', 'role', 'tenant', 'tenant__group', 'vlan')
  291. def prep_table_data(self, request, queryset, parent):
  292. # Determine whether to show assigned prefixes, available prefixes, or both
  293. show_available = bool(request.GET.get('show_available', 'true') == 'true')
  294. show_assigned = bool(request.GET.get('show_assigned', 'true') == 'true')
  295. return add_requested_prefixes(parent.prefix, queryset, show_available, show_assigned)
  296. def get_extra_context(self, request, instance):
  297. return {
  298. 'bulk_querystring': f'within={instance.prefix}',
  299. 'first_available_prefix': instance.get_first_available_prefix(),
  300. 'show_available': bool(request.GET.get('show_available', 'true') == 'true'),
  301. 'show_assigned': bool(request.GET.get('show_assigned', 'true') == 'true'),
  302. }
  303. @register_model_view(Aggregate, 'add', detail=False)
  304. @register_model_view(Aggregate, 'edit')
  305. class AggregateEditView(generic.ObjectEditView):
  306. queryset = Aggregate.objects.all()
  307. form = forms.AggregateForm
  308. @register_model_view(Aggregate, 'delete')
  309. class AggregateDeleteView(generic.ObjectDeleteView):
  310. queryset = Aggregate.objects.all()
  311. @register_model_view(Aggregate, 'bulk_import', path='import', detail=False)
  312. class AggregateBulkImportView(generic.BulkImportView):
  313. queryset = Aggregate.objects.all()
  314. model_form = forms.AggregateImportForm
  315. @register_model_view(Aggregate, 'bulk_edit', path='edit', detail=False)
  316. class AggregateBulkEditView(generic.BulkEditView):
  317. queryset = Aggregate.objects.annotate(
  318. child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
  319. )
  320. filterset = filtersets.AggregateFilterSet
  321. table = tables.AggregateTable
  322. form = forms.AggregateBulkEditForm
  323. @register_model_view(Aggregate, 'bulk_delete', path='delete', detail=False)
  324. class AggregateBulkDeleteView(generic.BulkDeleteView):
  325. queryset = Aggregate.objects.annotate(
  326. child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
  327. )
  328. filterset = filtersets.AggregateFilterSet
  329. table = tables.AggregateTable
  330. #
  331. # Prefix/VLAN roles
  332. #
  333. @register_model_view(Role, 'list', path='', detail=False)
  334. class RoleListView(generic.ObjectListView):
  335. queryset = Role.objects.annotate(
  336. prefix_count=count_related(Prefix, 'role'),
  337. iprange_count=count_related(IPRange, 'role'),
  338. vlan_count=count_related(VLAN, 'role')
  339. )
  340. filterset = filtersets.RoleFilterSet
  341. filterset_form = forms.RoleFilterForm
  342. table = tables.RoleTable
  343. @register_model_view(Role)
  344. class RoleView(GetRelatedModelsMixin, generic.ObjectView):
  345. queryset = Role.objects.all()
  346. def get_extra_context(self, request, instance):
  347. return {
  348. 'related_models': self.get_related_models(request, instance),
  349. }
  350. @register_model_view(Role, 'add', detail=False)
  351. @register_model_view(Role, 'edit')
  352. class RoleEditView(generic.ObjectEditView):
  353. queryset = Role.objects.all()
  354. form = forms.RoleForm
  355. @register_model_view(Role, 'delete')
  356. class RoleDeleteView(generic.ObjectDeleteView):
  357. queryset = Role.objects.all()
  358. @register_model_view(Role, 'bulk_import', path='import', detail=False)
  359. class RoleBulkImportView(generic.BulkImportView):
  360. queryset = Role.objects.all()
  361. model_form = forms.RoleImportForm
  362. @register_model_view(Role, 'bulk_edit', path='edit', detail=False)
  363. class RoleBulkEditView(generic.BulkEditView):
  364. queryset = Role.objects.all()
  365. filterset = filtersets.RoleFilterSet
  366. table = tables.RoleTable
  367. form = forms.RoleBulkEditForm
  368. @register_model_view(Role, 'bulk_delete', path='delete', detail=False)
  369. class RoleBulkDeleteView(generic.BulkDeleteView):
  370. queryset = Role.objects.all()
  371. filterset = filtersets.RoleFilterSet
  372. table = tables.RoleTable
  373. #
  374. # Prefixes
  375. #
  376. @register_model_view(Prefix, 'list', path='', detail=False)
  377. class PrefixListView(generic.ObjectListView):
  378. queryset = Prefix.objects.all()
  379. filterset = filtersets.PrefixFilterSet
  380. filterset_form = forms.PrefixFilterForm
  381. table = tables.PrefixTable
  382. template_name = 'ipam/prefix_list.html'
  383. @register_model_view(Prefix)
  384. class PrefixView(generic.ObjectView):
  385. queryset = Prefix.objects.all()
  386. def get_extra_context(self, request, instance):
  387. try:
  388. aggregate = Aggregate.objects.restrict(request.user, 'view').get(
  389. prefix__net_contains_or_equals=str(instance.prefix)
  390. )
  391. except Aggregate.DoesNotExist:
  392. aggregate = None
  393. # Parent prefixes table
  394. parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
  395. Q(vrf=instance.vrf) | Q(vrf__isnull=True, status=PrefixStatusChoices.STATUS_CONTAINER)
  396. ).filter(
  397. prefix__net_contains=str(instance.prefix)
  398. ).prefetch_related(
  399. 'scope', 'role', 'tenant', 'vlan',
  400. )
  401. parent_prefix_table = tables.PrefixTable(
  402. list(parent_prefixes),
  403. exclude=('vrf', 'utilization'),
  404. orderable=False
  405. )
  406. # Duplicate prefixes table
  407. duplicate_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
  408. vrf=instance.vrf, prefix=str(instance.prefix)
  409. ).exclude(
  410. pk=instance.pk
  411. ).prefetch_related(
  412. 'scope', 'role', 'tenant', 'vlan',
  413. )
  414. duplicate_prefix_table = tables.PrefixTable(
  415. list(duplicate_prefixes),
  416. exclude=('vrf', 'utilization'),
  417. orderable=False
  418. )
  419. return {
  420. 'aggregate': aggregate,
  421. 'parent_prefix_table': parent_prefix_table,
  422. 'duplicate_prefix_table': duplicate_prefix_table,
  423. }
  424. @register_model_view(Prefix, 'prefixes')
  425. class PrefixPrefixesView(generic.ObjectChildrenView):
  426. queryset = Prefix.objects.all()
  427. child_model = Prefix
  428. table = tables.PrefixTable
  429. filterset = filtersets.PrefixFilterSet
  430. filterset_form = forms.PrefixFilterForm
  431. template_name = 'ipam/prefix/prefixes.html'
  432. tab = ViewTab(
  433. label=_('Child Prefixes'),
  434. badge=lambda x: x.get_child_prefixes().count(),
  435. permission='ipam.view_prefix',
  436. weight=500
  437. )
  438. def get_children(self, request, parent):
  439. return parent.get_child_prefixes().restrict(request.user, 'view').prefetch_related(
  440. 'scope', 'vrf', 'vlan', 'role', 'tenant', 'tenant__group'
  441. )
  442. def prep_table_data(self, request, queryset, parent):
  443. # Determine whether to show assigned prefixes, available prefixes, or both
  444. show_available = bool(request.GET.get('show_available', 'true') == 'true')
  445. show_assigned = bool(request.GET.get('show_assigned', 'true') == 'true')
  446. return add_requested_prefixes(parent.prefix, queryset, show_available, show_assigned)
  447. def get_extra_context(self, request, instance):
  448. return {
  449. 'bulk_querystring': f"vrf_id={instance.vrf.pk if instance.vrf else '0'}&within={instance.prefix}",
  450. 'first_available_prefix': instance.get_first_available_prefix(),
  451. 'show_available': bool(request.GET.get('show_available', 'true') == 'true'),
  452. 'show_assigned': bool(request.GET.get('show_assigned', 'true') == 'true'),
  453. }
  454. @register_model_view(Prefix, 'ipranges', path='ip-ranges')
  455. class PrefixIPRangesView(generic.ObjectChildrenView):
  456. queryset = Prefix.objects.all()
  457. child_model = IPRange
  458. table = tables.IPRangeTable
  459. filterset = filtersets.IPRangeFilterSet
  460. filterset_form = forms.IPRangeFilterForm
  461. template_name = 'ipam/prefix/ip_ranges.html'
  462. tab = ViewTab(
  463. label=_('Child Ranges'),
  464. badge=lambda x: x.get_child_ranges().count(),
  465. permission='ipam.view_iprange',
  466. weight=600
  467. )
  468. def get_children(self, request, parent):
  469. return parent.get_child_ranges().restrict(request.user, 'view').prefetch_related(
  470. 'tenant__group',
  471. )
  472. def get_extra_context(self, request, instance):
  473. return {
  474. 'bulk_querystring': f"vrf_id={instance.vrf.pk if instance.vrf else '0'}&parent={instance.prefix}",
  475. 'first_available_ip': instance.get_first_available_ip(),
  476. }
  477. @register_model_view(Prefix, 'ipaddresses', path='ip-addresses')
  478. class PrefixIPAddressesView(generic.ObjectChildrenView):
  479. queryset = Prefix.objects.all()
  480. child_model = IPAddress
  481. table = tables.AnnotatedIPAddressTable
  482. filterset = filtersets.IPAddressFilterSet
  483. filterset_form = forms.IPAddressFilterForm
  484. template_name = 'ipam/prefix/ip_addresses.html'
  485. tab = ViewTab(
  486. label=_('IP Addresses'),
  487. badge=lambda x: x.ip_addresses.count(),
  488. permission='ipam.view_ipaddress',
  489. weight=700
  490. )
  491. def get_children(self, request, parent):
  492. return parent.ip_addresses.restrict(request.user, 'view').prefetch_related('vrf', 'tenant', 'tenant__group')
  493. def prep_table_data(self, request, queryset, parent):
  494. if not request.GET.get('q') and not get_table_ordering(request, self.table):
  495. return annotate_ip_space(parent)
  496. return queryset
  497. def get_extra_context(self, request, instance):
  498. return {
  499. 'bulk_querystring': f"vrf_id={instance.vrf.pk if instance.vrf else '0'}&parent={instance.prefix}",
  500. 'first_available_ip': instance.get_first_available_ip(),
  501. }
  502. @register_model_view(Prefix, 'add', detail=False)
  503. @register_model_view(Prefix, 'edit')
  504. class PrefixEditView(generic.ObjectEditView):
  505. queryset = Prefix.objects.all()
  506. form = forms.PrefixForm
  507. @register_model_view(Prefix, 'delete')
  508. class PrefixDeleteView(generic.ObjectDeleteView):
  509. queryset = Prefix.objects.all()
  510. @register_model_view(Prefix, 'bulk_import', path='import', detail=False)
  511. class PrefixBulkImportView(generic.BulkImportView):
  512. queryset = Prefix.objects.all()
  513. model_form = forms.PrefixImportForm
  514. @register_model_view(Prefix, 'bulk_edit', path='edit', detail=False)
  515. class PrefixBulkEditView(generic.BulkEditView):
  516. queryset = Prefix.objects.prefetch_related('vrf__tenant')
  517. filterset = filtersets.PrefixFilterSet
  518. table = tables.PrefixTable
  519. form = forms.PrefixBulkEditForm
  520. @register_model_view(Prefix, 'bulk_delete', path='delete', detail=False)
  521. class PrefixBulkDeleteView(generic.BulkDeleteView):
  522. queryset = Prefix.objects.prefetch_related('vrf__tenant')
  523. filterset = filtersets.PrefixFilterSet
  524. table = tables.PrefixTable
  525. #
  526. # IP Ranges
  527. #
  528. @register_model_view(IPRange, 'list', path='', detail=False)
  529. class IPRangeListView(generic.ObjectListView):
  530. queryset = IPRange.objects.all()
  531. filterset = filtersets.IPRangeFilterSet
  532. filterset_form = forms.IPRangeFilterForm
  533. table = tables.IPRangeTable
  534. @register_model_view(IPRange)
  535. class IPRangeView(generic.ObjectView):
  536. queryset = IPRange.objects.all()
  537. def get_extra_context(self, request, instance):
  538. # Parent prefixes table
  539. parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
  540. Q(prefix__net_contains_or_equals=str(instance.start_address.ip)),
  541. Q(prefix__net_contains_or_equals=str(instance.end_address.ip)),
  542. vrf=instance.vrf
  543. ).prefetch_related(
  544. 'scope', 'role', 'tenant', 'vlan', 'role'
  545. )
  546. parent_prefixes_table = tables.PrefixTable(
  547. list(parent_prefixes),
  548. exclude=('vrf', 'utilization'),
  549. orderable=False
  550. )
  551. return {
  552. 'parent_prefixes_table': parent_prefixes_table,
  553. }
  554. @register_model_view(IPRange, 'ipaddresses', path='ip-addresses')
  555. class IPRangeIPAddressesView(generic.ObjectChildrenView):
  556. queryset = IPRange.objects.all()
  557. child_model = IPAddress
  558. table = tables.IPAddressTable
  559. filterset = filtersets.IPAddressFilterSet
  560. filterset_form = forms.IPRangeFilterForm
  561. template_name = 'ipam/iprange/ip_addresses.html'
  562. tab = ViewTab(
  563. label=_('IP Addresses'),
  564. badge=lambda x: x.get_child_ips().count(),
  565. permission='ipam.view_ipaddress',
  566. weight=500
  567. )
  568. def get_children(self, request, parent):
  569. return parent.get_child_ips().restrict(request.user, 'view')
  570. @register_model_view(IPRange, 'add', detail=False)
  571. @register_model_view(IPRange, 'edit')
  572. class IPRangeEditView(generic.ObjectEditView):
  573. queryset = IPRange.objects.all()
  574. form = forms.IPRangeForm
  575. @register_model_view(IPRange, 'delete')
  576. class IPRangeDeleteView(generic.ObjectDeleteView):
  577. queryset = IPRange.objects.all()
  578. @register_model_view(IPRange, 'bulk_import', path='import', detail=False)
  579. class IPRangeBulkImportView(generic.BulkImportView):
  580. queryset = IPRange.objects.all()
  581. model_form = forms.IPRangeImportForm
  582. @register_model_view(IPRange, 'bulk_edit', path='edit', detail=False)
  583. class IPRangeBulkEditView(generic.BulkEditView):
  584. queryset = IPRange.objects.all()
  585. filterset = filtersets.IPRangeFilterSet
  586. table = tables.IPRangeTable
  587. form = forms.IPRangeBulkEditForm
  588. @register_model_view(IPRange, 'bulk_delete', path='delete', detail=False)
  589. class IPRangeBulkDeleteView(generic.BulkDeleteView):
  590. queryset = IPRange.objects.all()
  591. filterset = filtersets.IPRangeFilterSet
  592. table = tables.IPRangeTable
  593. #
  594. # IP addresses
  595. #
  596. @register_model_view(IPAddress, 'list', path='', detail=False)
  597. class IPAddressListView(generic.ObjectListView):
  598. queryset = IPAddress.objects.all()
  599. filterset = filtersets.IPAddressFilterSet
  600. filterset_form = forms.IPAddressFilterForm
  601. table = tables.IPAddressTable
  602. @register_model_view(IPAddress)
  603. class IPAddressView(generic.ObjectView):
  604. queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant')
  605. def get_extra_context(self, request, instance):
  606. # Parent prefixes table
  607. parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
  608. vrf=instance.vrf,
  609. prefix__net_contains_or_equals=str(instance.address.ip)
  610. ).prefetch_related(
  611. 'scope', 'role'
  612. )
  613. parent_prefixes_table = tables.PrefixTable(
  614. list(parent_prefixes),
  615. exclude=('vrf', 'utilization'),
  616. orderable=False
  617. )
  618. # Duplicate IPs table
  619. duplicate_ips = IPAddress.objects.restrict(request.user, 'view').filter(
  620. vrf=instance.vrf,
  621. address=str(instance.address)
  622. ).exclude(
  623. pk=instance.pk
  624. ).prefetch_related(
  625. 'nat_inside'
  626. )
  627. # Exclude anycast IPs if this IP is anycast
  628. if instance.role == IPAddressRoleChoices.ROLE_ANYCAST:
  629. duplicate_ips = duplicate_ips.exclude(role=IPAddressRoleChoices.ROLE_ANYCAST)
  630. # Limit to a maximum of 10 duplicates displayed here
  631. duplicate_ips_table = tables.IPAddressTable(duplicate_ips[:10], orderable=False)
  632. return {
  633. 'parent_prefixes_table': parent_prefixes_table,
  634. 'duplicate_ips_table': duplicate_ips_table,
  635. }
  636. @register_model_view(IPAddress, 'add', detail=False)
  637. @register_model_view(IPAddress, 'edit')
  638. class IPAddressEditView(generic.ObjectEditView):
  639. queryset = IPAddress.objects.all()
  640. form = forms.IPAddressForm
  641. template_name = 'ipam/ipaddress_edit.html'
  642. def alter_object(self, obj, request, url_args, url_kwargs):
  643. if 'interface' in request.GET:
  644. try:
  645. obj.assigned_object = Interface.objects.get(pk=request.GET['interface'])
  646. except (ValueError, Interface.DoesNotExist):
  647. pass
  648. elif 'vminterface' in request.GET:
  649. try:
  650. obj.assigned_object = VMInterface.objects.get(pk=request.GET['vminterface'])
  651. except (ValueError, VMInterface.DoesNotExist):
  652. pass
  653. elif 'fhrpgroup' in request.GET:
  654. try:
  655. obj.assigned_object = FHRPGroup.objects.get(pk=request.GET['fhrpgroup'])
  656. except (ValueError, FHRPGroup.DoesNotExist):
  657. pass
  658. return obj
  659. def get_extra_addanother_params(self, request):
  660. if 'interface' in request.GET:
  661. return {'interface': request.GET['interface']}
  662. elif 'vminterface' in request.GET:
  663. return {'vminterface': request.GET['vminterface']}
  664. return {}
  665. # TODO: Standardize or remove this view
  666. @register_model_view(IPAddress, 'assign', path='assign', detail=False)
  667. class IPAddressAssignView(generic.ObjectView):
  668. """
  669. Search for IPAddresses to be assigned to an Interface.
  670. """
  671. queryset = IPAddress.objects.all()
  672. def dispatch(self, request, *args, **kwargs):
  673. # Redirect user if an interface has not been provided
  674. if 'interface' not in request.GET and 'vminterface' not in request.GET:
  675. return redirect('ipam:ipaddress_add')
  676. return super().dispatch(request, *args, **kwargs)
  677. def get(self, request):
  678. form = forms.IPAddressAssignForm()
  679. return render(request, 'ipam/ipaddress_assign.html', {
  680. 'form': form,
  681. 'return_url': request.GET.get('return_url', ''),
  682. })
  683. def post(self, request):
  684. form = forms.IPAddressAssignForm(request.POST)
  685. table = None
  686. if form.is_valid():
  687. addresses = self.queryset.prefetch_related('vrf', 'tenant')
  688. # Limit to 100 results
  689. addresses = filtersets.IPAddressFilterSet(request.POST, addresses).qs[:100]
  690. table = tables.IPAddressAssignTable(addresses)
  691. return render(request, 'ipam/ipaddress_assign.html', {
  692. 'form': form,
  693. 'table': table,
  694. 'return_url': request.GET.get('return_url'),
  695. })
  696. @register_model_view(IPAddress, 'delete')
  697. class IPAddressDeleteView(generic.ObjectDeleteView):
  698. queryset = IPAddress.objects.all()
  699. @register_model_view(IPAddress, 'bulk_add', path='bulk-add', detail=False)
  700. class IPAddressBulkCreateView(generic.BulkCreateView):
  701. queryset = IPAddress.objects.all()
  702. form = forms.IPAddressBulkCreateForm
  703. model_form = forms.IPAddressBulkAddForm
  704. pattern_target = 'address'
  705. template_name = 'ipam/ipaddress_bulk_add.html'
  706. @register_model_view(IPAddress, 'bulk_import', path='import', detail=False)
  707. class IPAddressBulkImportView(generic.BulkImportView):
  708. queryset = IPAddress.objects.all()
  709. model_form = forms.IPAddressImportForm
  710. @register_model_view(IPAddress, 'bulk_edit', path='edit', detail=False)
  711. class IPAddressBulkEditView(generic.BulkEditView):
  712. queryset = IPAddress.objects.prefetch_related('vrf__tenant')
  713. filterset = filtersets.IPAddressFilterSet
  714. table = tables.IPAddressTable
  715. form = forms.IPAddressBulkEditForm
  716. @register_model_view(IPAddress, 'bulk_delete', path='delete', detail=False)
  717. class IPAddressBulkDeleteView(generic.BulkDeleteView):
  718. queryset = IPAddress.objects.prefetch_related('vrf__tenant')
  719. filterset = filtersets.IPAddressFilterSet
  720. table = tables.IPAddressTable
  721. @register_model_view(IPAddress, 'related_ips', path='related-ip-addresses')
  722. class IPAddressRelatedIPsView(generic.ObjectChildrenView):
  723. queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant')
  724. child_model = IPAddress
  725. table = tables.IPAddressTable
  726. filterset = filtersets.IPAddressFilterSet
  727. filterset_form = forms.IPAddressFilterForm
  728. tab = ViewTab(
  729. label=_('Related IPs'),
  730. badge=lambda x: x.get_related_ips().count(),
  731. weight=500,
  732. hide_if_empty=True,
  733. )
  734. def get_children(self, request, parent):
  735. return parent.get_related_ips().restrict(request.user, 'view')
  736. #
  737. # VLAN groups
  738. #
  739. @register_model_view(VLANGroup, 'list', path='', detail=False)
  740. class VLANGroupListView(generic.ObjectListView):
  741. queryset = VLANGroup.objects.annotate_utilization()
  742. filterset = filtersets.VLANGroupFilterSet
  743. filterset_form = forms.VLANGroupFilterForm
  744. table = tables.VLANGroupTable
  745. @register_model_view(VLANGroup)
  746. class VLANGroupView(GetRelatedModelsMixin, generic.ObjectView):
  747. queryset = VLANGroup.objects.annotate_utilization()
  748. def get_extra_context(self, request, instance):
  749. return {
  750. 'related_models': self.get_related_models(request, instance),
  751. }
  752. @register_model_view(VLANGroup, 'add', detail=False)
  753. @register_model_view(VLANGroup, 'edit')
  754. class VLANGroupEditView(generic.ObjectEditView):
  755. queryset = VLANGroup.objects.all()
  756. form = forms.VLANGroupForm
  757. @register_model_view(VLANGroup, 'delete')
  758. class VLANGroupDeleteView(generic.ObjectDeleteView):
  759. queryset = VLANGroup.objects.all()
  760. @register_model_view(VLANGroup, 'bulk_import', path='import', detail=False)
  761. class VLANGroupBulkImportView(generic.BulkImportView):
  762. queryset = VLANGroup.objects.all()
  763. model_form = forms.VLANGroupImportForm
  764. @register_model_view(VLANGroup, 'bulk_edit', path='edit', detail=False)
  765. class VLANGroupBulkEditView(generic.BulkEditView):
  766. queryset = VLANGroup.objects.annotate_utilization().prefetch_related('tags')
  767. filterset = filtersets.VLANGroupFilterSet
  768. table = tables.VLANGroupTable
  769. form = forms.VLANGroupBulkEditForm
  770. @register_model_view(VLANGroup, 'bulk_delete', path='delete', detail=False)
  771. class VLANGroupBulkDeleteView(generic.BulkDeleteView):
  772. queryset = VLANGroup.objects.annotate_utilization().prefetch_related('tags')
  773. filterset = filtersets.VLANGroupFilterSet
  774. table = tables.VLANGroupTable
  775. @register_model_view(VLANGroup, 'vlans')
  776. class VLANGroupVLANsView(generic.ObjectChildrenView):
  777. queryset = VLANGroup.objects.all()
  778. child_model = VLAN
  779. table = tables.VLANTable
  780. filterset = filtersets.VLANFilterSet
  781. filterset_form = forms.VLANFilterForm
  782. tab = ViewTab(
  783. label=_('VLANs'),
  784. badge=lambda x: x.get_child_vlans().count(),
  785. permission='ipam.view_vlan',
  786. weight=500
  787. )
  788. def get_children(self, request, parent):
  789. return parent.get_child_vlans().restrict(request.user, 'view').prefetch_related(
  790. Prefetch('prefixes', queryset=Prefix.objects.restrict(request.user)),
  791. 'tenant', 'site', 'role',
  792. )
  793. def prep_table_data(self, request, queryset, parent):
  794. if not get_table_ordering(request, self.table):
  795. return add_available_vlans(queryset, parent)
  796. return queryset
  797. #
  798. # VLAN Translation Policies
  799. #
  800. @register_model_view(VLANTranslationPolicy, 'list', path='', detail=False)
  801. class VLANTranslationPolicyListView(generic.ObjectListView):
  802. queryset = VLANTranslationPolicy.objects.annotate(
  803. rule_count=count_related(VLANTranslationRule, 'policy'),
  804. )
  805. filterset = filtersets.VLANTranslationPolicyFilterSet
  806. filterset_form = forms.VLANTranslationPolicyFilterForm
  807. table = tables.VLANTranslationPolicyTable
  808. @register_model_view(VLANTranslationPolicy)
  809. class VLANTranslationPolicyView(GetRelatedModelsMixin, generic.ObjectView):
  810. queryset = VLANTranslationPolicy.objects.all()
  811. def get_extra_context(self, request, instance):
  812. vlan_translation_table = VLANTranslationRuleTable(
  813. data=instance.rules.all(),
  814. orderable=False
  815. )
  816. return {
  817. 'vlan_translation_table': vlan_translation_table,
  818. }
  819. @register_model_view(VLANTranslationPolicy, 'add', detail=False)
  820. @register_model_view(VLANTranslationPolicy, 'edit')
  821. class VLANTranslationPolicyEditView(generic.ObjectEditView):
  822. queryset = VLANTranslationPolicy.objects.all()
  823. form = forms.VLANTranslationPolicyForm
  824. @register_model_view(VLANTranslationPolicy, 'delete')
  825. class VLANTranslationPolicyDeleteView(generic.ObjectDeleteView):
  826. queryset = VLANTranslationPolicy.objects.all()
  827. @register_model_view(VLANTranslationPolicy, 'bulk_import', path='import', detail=False)
  828. class VLANTranslationPolicyBulkImportView(generic.BulkImportView):
  829. queryset = VLANTranslationPolicy.objects.all()
  830. model_form = forms.VLANTranslationPolicyImportForm
  831. @register_model_view(VLANTranslationPolicy, 'bulk_edit', path='edit', detail=False)
  832. class VLANTranslationPolicyBulkEditView(generic.BulkEditView):
  833. queryset = VLANTranslationPolicy.objects.all()
  834. filterset = filtersets.VLANTranslationPolicyFilterSet
  835. table = tables.VLANTranslationPolicyTable
  836. form = forms.VLANTranslationPolicyBulkEditForm
  837. @register_model_view(VLANTranslationPolicy, 'bulk_delete', path='delete', detail=False)
  838. class VLANTranslationPolicyBulkDeleteView(generic.BulkDeleteView):
  839. queryset = VLANTranslationPolicy.objects.all()
  840. filterset = filtersets.VLANTranslationPolicyFilterSet
  841. table = tables.VLANTranslationPolicyTable
  842. #
  843. # VLAN Translation Rules
  844. #
  845. @register_model_view(VLANTranslationRule, 'list', path='', detail=False)
  846. class VLANTranslationRuleListView(generic.ObjectListView):
  847. queryset = VLANTranslationRule.objects.all()
  848. filterset = filtersets.VLANTranslationRuleFilterSet
  849. filterset_form = forms.VLANTranslationRuleFilterForm
  850. table = tables.VLANTranslationRuleTable
  851. @register_model_view(VLANTranslationRule)
  852. class VLANTranslationRuleView(GetRelatedModelsMixin, generic.ObjectView):
  853. queryset = VLANTranslationRule.objects.all()
  854. def get_extra_context(self, request, instance):
  855. return {
  856. 'related_models': self.get_related_models(request, instance),
  857. }
  858. @register_model_view(VLANTranslationRule, 'add', detail=False)
  859. @register_model_view(VLANTranslationRule, 'edit')
  860. class VLANTranslationRuleEditView(generic.ObjectEditView):
  861. queryset = VLANTranslationRule.objects.all()
  862. form = forms.VLANTranslationRuleForm
  863. @register_model_view(VLANTranslationRule, 'delete')
  864. class VLANTranslationRuleDeleteView(generic.ObjectDeleteView):
  865. queryset = VLANTranslationRule.objects.all()
  866. @register_model_view(VLANTranslationRule, 'bulk_import', path='import', detail=False)
  867. class VLANTranslationRuleBulkImportView(generic.BulkImportView):
  868. queryset = VLANTranslationRule.objects.all()
  869. model_form = forms.VLANTranslationRuleImportForm
  870. @register_model_view(VLANTranslationRule, 'bulk_edit', path='edit', detail=False)
  871. class VLANTranslationRuleBulkEditView(generic.BulkEditView):
  872. queryset = VLANTranslationRule.objects.all()
  873. filterset = filtersets.VLANTranslationRuleFilterSet
  874. table = tables.VLANTranslationRuleTable
  875. form = forms.VLANTranslationRuleBulkEditForm
  876. @register_model_view(VLANTranslationRule, 'bulk_delete', path='delete', detail=False)
  877. class VLANTranslationRuleBulkDeleteView(generic.BulkDeleteView):
  878. queryset = VLANTranslationRule.objects.all()
  879. filterset = filtersets.VLANTranslationRuleFilterSet
  880. table = tables.VLANTranslationRuleTable
  881. #
  882. # FHRP groups
  883. #
  884. @register_model_view(FHRPGroup, 'list', path='', detail=False)
  885. class FHRPGroupListView(generic.ObjectListView):
  886. queryset = FHRPGroup.objects.annotate(
  887. member_count=count_related(FHRPGroupAssignment, 'group')
  888. )
  889. filterset = filtersets.FHRPGroupFilterSet
  890. filterset_form = forms.FHRPGroupFilterForm
  891. table = tables.FHRPGroupTable
  892. @register_model_view(FHRPGroup)
  893. class FHRPGroupView(GetRelatedModelsMixin, generic.ObjectView):
  894. queryset = FHRPGroup.objects.all()
  895. def get_extra_context(self, request, instance):
  896. # Get assigned interfaces
  897. members_table = tables.FHRPGroupAssignmentTable(
  898. data=FHRPGroupAssignment.objects.restrict(request.user, 'view').filter(group=instance),
  899. orderable=False
  900. )
  901. members_table.columns.hide('group')
  902. return {
  903. 'related_models': self.get_related_models(
  904. request, instance,
  905. extra=(
  906. (
  907. Service.objects.restrict(request.user, 'view').filter(
  908. parent_object_type=ContentType.objects.get_for_model(FHRPGroup),
  909. parent_object_id=instance.id,
  910. ),
  911. 'fhrpgroup_id'
  912. ),
  913. ),
  914. ),
  915. 'members_table': members_table,
  916. 'member_count': FHRPGroupAssignment.objects.filter(group=instance).count(),
  917. }
  918. @register_model_view(FHRPGroup, 'add', detail=False)
  919. @register_model_view(FHRPGroup, 'edit')
  920. class FHRPGroupEditView(generic.ObjectEditView):
  921. queryset = FHRPGroup.objects.all()
  922. form = forms.FHRPGroupForm
  923. def get_return_url(self, request, obj=None):
  924. return_url = super().get_return_url(request, obj)
  925. # If we're redirecting the user to the FHRPGroupAssignment creation form,
  926. # initialize the group field with the FHRPGroup we just saved.
  927. if return_url.startswith(reverse('ipam:fhrpgroupassignment_add')):
  928. return_url += f'&group={obj.pk}'
  929. return return_url
  930. def alter_object(self, obj, request, url_args, url_kwargs):
  931. # Workaround to solve #10719. Capture the current user on the FHRPGroup instance so that
  932. # we can evaluate permissions during the creation of a new IPAddress within the form.
  933. obj._user = request.user
  934. return obj
  935. @register_model_view(FHRPGroup, 'delete')
  936. class FHRPGroupDeleteView(generic.ObjectDeleteView):
  937. queryset = FHRPGroup.objects.all()
  938. @register_model_view(FHRPGroup, 'bulk_import', path='import', detail=False)
  939. class FHRPGroupBulkImportView(generic.BulkImportView):
  940. queryset = FHRPGroup.objects.all()
  941. model_form = forms.FHRPGroupImportForm
  942. @register_model_view(FHRPGroup, 'bulk_edit', path='edit', detail=False)
  943. class FHRPGroupBulkEditView(generic.BulkEditView):
  944. queryset = FHRPGroup.objects.all()
  945. filterset = filtersets.FHRPGroupFilterSet
  946. table = tables.FHRPGroupTable
  947. form = forms.FHRPGroupBulkEditForm
  948. @register_model_view(FHRPGroup, 'bulk_delete', path='delete', detail=False)
  949. class FHRPGroupBulkDeleteView(generic.BulkDeleteView):
  950. queryset = FHRPGroup.objects.all()
  951. filterset = filtersets.FHRPGroupFilterSet
  952. table = tables.FHRPGroupTable
  953. #
  954. # FHRP group assignments
  955. #
  956. @register_model_view(FHRPGroupAssignment, 'add', detail=False)
  957. @register_model_view(FHRPGroupAssignment, 'edit')
  958. class FHRPGroupAssignmentEditView(generic.ObjectEditView):
  959. queryset = FHRPGroupAssignment.objects.all()
  960. form = forms.FHRPGroupAssignmentForm
  961. def alter_object(self, instance, request, args, kwargs):
  962. if not instance.pk:
  963. # Assign the interface based on URL kwargs
  964. content_type = get_object_or_404(ContentType, pk=request.GET.get('interface_type'))
  965. instance.interface = get_object_or_404(content_type.model_class(), pk=request.GET.get('interface_id'))
  966. return instance
  967. def get_extra_addanother_params(self, request):
  968. return {
  969. 'interface_type': request.GET.get('interface_type'),
  970. 'interface_id': request.GET.get('interface_id'),
  971. }
  972. @register_model_view(FHRPGroupAssignment, 'delete')
  973. class FHRPGroupAssignmentDeleteView(generic.ObjectDeleteView):
  974. queryset = FHRPGroupAssignment.objects.all()
  975. #
  976. # VLANs
  977. #
  978. @register_model_view(VLAN, 'list', path='', detail=False)
  979. class VLANListView(generic.ObjectListView):
  980. queryset = VLAN.objects.all()
  981. filterset = filtersets.VLANFilterSet
  982. filterset_form = forms.VLANFilterForm
  983. table = tables.VLANTable
  984. @register_model_view(VLAN)
  985. class VLANView(generic.ObjectView):
  986. queryset = VLAN.objects.all()
  987. def get_extra_context(self, request, instance):
  988. prefixes = Prefix.objects.restrict(request.user, 'view').filter(vlan=instance).prefetch_related(
  989. 'vrf', 'scope', 'role', 'tenant'
  990. )
  991. prefix_table = tables.PrefixTable(list(prefixes), exclude=('vlan', 'utilization'), orderable=False)
  992. return {
  993. 'prefix_table': prefix_table,
  994. }
  995. @register_model_view(VLAN, 'interfaces')
  996. class VLANInterfacesView(generic.ObjectChildrenView):
  997. queryset = VLAN.objects.all()
  998. child_model = Interface
  999. table = tables.VLANDevicesTable
  1000. filterset = InterfaceFilterSet
  1001. filterset_form = InterfaceFilterForm
  1002. tab = ViewTab(
  1003. label=_('Device Interfaces'),
  1004. badge=lambda x: x.get_interfaces().count(),
  1005. permission='dcim.view_interface',
  1006. weight=500
  1007. )
  1008. def get_children(self, request, parent):
  1009. return parent.get_interfaces().restrict(request.user, 'view')
  1010. @register_model_view(VLAN, 'vminterfaces', path='vm-interfaces')
  1011. class VLANVMInterfacesView(generic.ObjectChildrenView):
  1012. queryset = VLAN.objects.all()
  1013. child_model = VMInterface
  1014. table = tables.VLANVirtualMachinesTable
  1015. filterset = VMInterfaceFilterSet
  1016. filterset_form = VMInterfaceFilterForm
  1017. tab = ViewTab(
  1018. label=_('VM Interfaces'),
  1019. badge=lambda x: x.get_vminterfaces().count(),
  1020. permission='virtualization.view_vminterface',
  1021. weight=510
  1022. )
  1023. def get_children(self, request, parent):
  1024. return parent.get_vminterfaces().restrict(request.user, 'view')
  1025. @register_model_view(VLAN, 'add', detail=False)
  1026. @register_model_view(VLAN, 'edit')
  1027. class VLANEditView(generic.ObjectEditView):
  1028. queryset = VLAN.objects.all()
  1029. form = forms.VLANForm
  1030. template_name = 'ipam/vlan_edit.html'
  1031. @register_model_view(VLAN, 'delete')
  1032. class VLANDeleteView(generic.ObjectDeleteView):
  1033. queryset = VLAN.objects.all()
  1034. @register_model_view(VLAN, 'bulk_import', path='import', detail=False)
  1035. class VLANBulkImportView(generic.BulkImportView):
  1036. queryset = VLAN.objects.all()
  1037. model_form = forms.VLANImportForm
  1038. @register_model_view(VLAN, 'bulk_edit', path='edit', detail=False)
  1039. class VLANBulkEditView(generic.BulkEditView):
  1040. queryset = VLAN.objects.all()
  1041. filterset = filtersets.VLANFilterSet
  1042. table = tables.VLANTable
  1043. form = forms.VLANBulkEditForm
  1044. @register_model_view(VLAN, 'bulk_delete', path='delete', detail=False)
  1045. class VLANBulkDeleteView(generic.BulkDeleteView):
  1046. queryset = VLAN.objects.all()
  1047. filterset = filtersets.VLANFilterSet
  1048. table = tables.VLANTable
  1049. #
  1050. # Service templates
  1051. #
  1052. @register_model_view(ServiceTemplate, 'list', path='', detail=False)
  1053. class ServiceTemplateListView(generic.ObjectListView):
  1054. queryset = ServiceTemplate.objects.all()
  1055. filterset = filtersets.ServiceTemplateFilterSet
  1056. filterset_form = forms.ServiceTemplateFilterForm
  1057. table = tables.ServiceTemplateTable
  1058. @register_model_view(ServiceTemplate)
  1059. class ServiceTemplateView(generic.ObjectView):
  1060. queryset = ServiceTemplate.objects.all()
  1061. @register_model_view(ServiceTemplate, 'add', detail=False)
  1062. @register_model_view(ServiceTemplate, 'edit')
  1063. class ServiceTemplateEditView(generic.ObjectEditView):
  1064. queryset = ServiceTemplate.objects.all()
  1065. form = forms.ServiceTemplateForm
  1066. @register_model_view(ServiceTemplate, 'delete')
  1067. class ServiceTemplateDeleteView(generic.ObjectDeleteView):
  1068. queryset = ServiceTemplate.objects.all()
  1069. @register_model_view(ServiceTemplate, 'bulk_import', path='import', detail=False)
  1070. class ServiceTemplateBulkImportView(generic.BulkImportView):
  1071. queryset = ServiceTemplate.objects.all()
  1072. model_form = forms.ServiceTemplateImportForm
  1073. @register_model_view(ServiceTemplate, 'bulk_edit', path='edit', detail=False)
  1074. class ServiceTemplateBulkEditView(generic.BulkEditView):
  1075. queryset = ServiceTemplate.objects.all()
  1076. filterset = filtersets.ServiceTemplateFilterSet
  1077. table = tables.ServiceTemplateTable
  1078. form = forms.ServiceTemplateBulkEditForm
  1079. @register_model_view(ServiceTemplate, 'bulk_delete', path='delete', detail=False)
  1080. class ServiceTemplateBulkDeleteView(generic.BulkDeleteView):
  1081. queryset = ServiceTemplate.objects.all()
  1082. filterset = filtersets.ServiceTemplateFilterSet
  1083. table = tables.ServiceTemplateTable
  1084. #
  1085. # Services
  1086. #
  1087. @register_model_view(Service, 'list', path='', detail=False)
  1088. class ServiceListView(generic.ObjectListView):
  1089. queryset = Service.objects.prefetch_related('parent')
  1090. filterset = filtersets.ServiceFilterSet
  1091. filterset_form = forms.ServiceFilterForm
  1092. table = tables.ServiceTable
  1093. @register_model_view(Service)
  1094. class ServiceView(generic.ObjectView):
  1095. queryset = Service.objects.all()
  1096. def get_extra_context(self, request, instance):
  1097. context = {}
  1098. match instance.parent:
  1099. case Device():
  1100. context['breadcrumb_queryparam'] = 'device_id'
  1101. case VirtualMachine():
  1102. context['breadcrumb_queryparam'] = 'virtual_machine_id'
  1103. case FHRPGroup():
  1104. context['breadcrumb_queryparam'] = 'fhrpgroup_id'
  1105. return context
  1106. @register_model_view(Service, 'add', detail=False)
  1107. class ServiceCreateView(generic.ObjectEditView):
  1108. queryset = Service.objects.all()
  1109. form = forms.ServiceCreateForm
  1110. @register_model_view(Service, 'edit')
  1111. class ServiceEditView(generic.ObjectEditView):
  1112. queryset = Service.objects.all()
  1113. form = forms.ServiceForm
  1114. @register_model_view(Service, 'delete')
  1115. class ServiceDeleteView(generic.ObjectDeleteView):
  1116. queryset = Service.objects.all()
  1117. @register_model_view(Service, 'bulk_import', path='import', detail=False)
  1118. class ServiceBulkImportView(generic.BulkImportView):
  1119. queryset = Service.objects.all()
  1120. model_form = forms.ServiceImportForm
  1121. @register_model_view(Service, 'bulk_edit', path='edit', detail=False)
  1122. class ServiceBulkEditView(generic.BulkEditView):
  1123. queryset = Service.objects.prefetch_related('parent')
  1124. filterset = filtersets.ServiceFilterSet
  1125. table = tables.ServiceTable
  1126. form = forms.ServiceBulkEditForm
  1127. @register_model_view(Service, 'bulk_delete', path='delete', detail=False)
  1128. class ServiceBulkDeleteView(generic.BulkDeleteView):
  1129. queryset = Service.objects.prefetch_related('parent')
  1130. filterset = filtersets.ServiceFilterSet
  1131. table = tables.ServiceTable