views.py 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132
  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 circuits.models import Provider
  7. from circuits.tables import ProviderTable
  8. from dcim.filtersets import InterfaceFilterSet
  9. from dcim.models import Interface, Site
  10. from dcim.tables import SiteTable
  11. from netbox.views import generic
  12. from utilities.utils import count_related
  13. from virtualization.filtersets import VMInterfaceFilterSet
  14. from virtualization.models import VMInterface
  15. from . import filtersets, forms, tables
  16. from .constants import *
  17. from .models import *
  18. from .models import ASN
  19. from .utils import add_requested_prefixes, add_available_ipaddresses, add_available_vlans
  20. #
  21. # VRFs
  22. #
  23. class VRFListView(generic.ObjectListView):
  24. queryset = VRF.objects.all()
  25. filterset = filtersets.VRFFilterSet
  26. filterset_form = forms.VRFFilterForm
  27. table = tables.VRFTable
  28. class VRFView(generic.ObjectView):
  29. queryset = VRF.objects.all()
  30. def get_extra_context(self, request, instance):
  31. prefix_count = Prefix.objects.restrict(request.user, 'view').filter(vrf=instance).count()
  32. ipaddress_count = IPAddress.objects.restrict(request.user, 'view').filter(vrf=instance).count()
  33. import_targets_table = tables.RouteTargetTable(
  34. instance.import_targets.prefetch_related('tenant'),
  35. orderable=False
  36. )
  37. export_targets_table = tables.RouteTargetTable(
  38. instance.export_targets.prefetch_related('tenant'),
  39. orderable=False
  40. )
  41. return {
  42. 'prefix_count': prefix_count,
  43. 'ipaddress_count': ipaddress_count,
  44. 'import_targets_table': import_targets_table,
  45. 'export_targets_table': export_targets_table,
  46. }
  47. class VRFEditView(generic.ObjectEditView):
  48. queryset = VRF.objects.all()
  49. form = forms.VRFForm
  50. class VRFDeleteView(generic.ObjectDeleteView):
  51. queryset = VRF.objects.all()
  52. class VRFBulkImportView(generic.BulkImportView):
  53. queryset = VRF.objects.all()
  54. model_form = forms.VRFCSVForm
  55. table = tables.VRFTable
  56. class VRFBulkEditView(generic.BulkEditView):
  57. queryset = VRF.objects.prefetch_related('tenant')
  58. filterset = filtersets.VRFFilterSet
  59. table = tables.VRFTable
  60. form = forms.VRFBulkEditForm
  61. class VRFBulkDeleteView(generic.BulkDeleteView):
  62. queryset = VRF.objects.prefetch_related('tenant')
  63. filterset = filtersets.VRFFilterSet
  64. table = tables.VRFTable
  65. #
  66. # Route targets
  67. #
  68. class RouteTargetListView(generic.ObjectListView):
  69. queryset = RouteTarget.objects.all()
  70. filterset = filtersets.RouteTargetFilterSet
  71. filterset_form = forms.RouteTargetFilterForm
  72. table = tables.RouteTargetTable
  73. class RouteTargetView(generic.ObjectView):
  74. queryset = RouteTarget.objects.all()
  75. def get_extra_context(self, request, instance):
  76. importing_vrfs_table = tables.VRFTable(
  77. instance.importing_vrfs.prefetch_related('tenant'),
  78. orderable=False
  79. )
  80. exporting_vrfs_table = tables.VRFTable(
  81. instance.exporting_vrfs.prefetch_related('tenant'),
  82. orderable=False
  83. )
  84. return {
  85. 'importing_vrfs_table': importing_vrfs_table,
  86. 'exporting_vrfs_table': exporting_vrfs_table,
  87. }
  88. class RouteTargetEditView(generic.ObjectEditView):
  89. queryset = RouteTarget.objects.all()
  90. form = forms.RouteTargetForm
  91. class RouteTargetDeleteView(generic.ObjectDeleteView):
  92. queryset = RouteTarget.objects.all()
  93. class RouteTargetBulkImportView(generic.BulkImportView):
  94. queryset = RouteTarget.objects.all()
  95. model_form = forms.RouteTargetCSVForm
  96. table = tables.RouteTargetTable
  97. class RouteTargetBulkEditView(generic.BulkEditView):
  98. queryset = RouteTarget.objects.prefetch_related('tenant')
  99. filterset = filtersets.RouteTargetFilterSet
  100. table = tables.RouteTargetTable
  101. form = forms.RouteTargetBulkEditForm
  102. class RouteTargetBulkDeleteView(generic.BulkDeleteView):
  103. queryset = RouteTarget.objects.prefetch_related('tenant')
  104. filterset = filtersets.RouteTargetFilterSet
  105. table = tables.RouteTargetTable
  106. #
  107. # RIRs
  108. #
  109. class RIRListView(generic.ObjectListView):
  110. queryset = RIR.objects.annotate(
  111. aggregate_count=count_related(Aggregate, 'rir')
  112. )
  113. filterset = filtersets.RIRFilterSet
  114. filterset_form = forms.RIRFilterForm
  115. table = tables.RIRTable
  116. class RIRView(generic.ObjectView):
  117. queryset = RIR.objects.all()
  118. def get_extra_context(self, request, instance):
  119. aggregates = Aggregate.objects.restrict(request.user, 'view').filter(rir=instance).annotate(
  120. child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
  121. )
  122. aggregates_table = tables.AggregateTable(aggregates, exclude=('rir', 'utilization'))
  123. aggregates_table.configure(request)
  124. return {
  125. 'aggregates_table': aggregates_table,
  126. }
  127. class RIREditView(generic.ObjectEditView):
  128. queryset = RIR.objects.all()
  129. form = forms.RIRForm
  130. class RIRDeleteView(generic.ObjectDeleteView):
  131. queryset = RIR.objects.all()
  132. class RIRBulkImportView(generic.BulkImportView):
  133. queryset = RIR.objects.all()
  134. model_form = forms.RIRCSVForm
  135. table = tables.RIRTable
  136. class RIRBulkEditView(generic.BulkEditView):
  137. queryset = RIR.objects.annotate(
  138. aggregate_count=count_related(Aggregate, 'rir')
  139. )
  140. filterset = filtersets.RIRFilterSet
  141. table = tables.RIRTable
  142. form = forms.RIRBulkEditForm
  143. class RIRBulkDeleteView(generic.BulkDeleteView):
  144. queryset = RIR.objects.annotate(
  145. aggregate_count=count_related(Aggregate, 'rir')
  146. )
  147. filterset = filtersets.RIRFilterSet
  148. table = tables.RIRTable
  149. #
  150. # ASNs
  151. #
  152. class ASNListView(generic.ObjectListView):
  153. queryset = ASN.objects.annotate(
  154. site_count=count_related(Site, 'asns'),
  155. provider_count=count_related(Provider, 'asns')
  156. )
  157. filterset = filtersets.ASNFilterSet
  158. filterset_form = forms.ASNFilterForm
  159. table = tables.ASNTable
  160. class ASNView(generic.ObjectView):
  161. queryset = ASN.objects.all()
  162. def get_extra_context(self, request, instance):
  163. # Gather assigned Sites
  164. sites = instance.sites.restrict(request.user, 'view')
  165. sites_table = SiteTable(sites)
  166. sites_table.configure(request)
  167. # Gather assigned Providers
  168. providers = instance.providers.restrict(request.user, 'view')
  169. providers_table = ProviderTable(providers)
  170. providers_table.configure(request)
  171. return {
  172. 'sites_table': sites_table,
  173. 'sites_count': sites.count(),
  174. 'providers_table': providers_table,
  175. 'providers_count': providers.count(),
  176. }
  177. class ASNEditView(generic.ObjectEditView):
  178. queryset = ASN.objects.all()
  179. form = forms.ASNForm
  180. class ASNDeleteView(generic.ObjectDeleteView):
  181. queryset = ASN.objects.all()
  182. class ASNBulkImportView(generic.BulkImportView):
  183. queryset = ASN.objects.all()
  184. model_form = forms.ASNCSVForm
  185. table = tables.ASNTable
  186. class ASNBulkEditView(generic.BulkEditView):
  187. queryset = ASN.objects.annotate(
  188. site_count=count_related(Site, 'asns')
  189. )
  190. filterset = filtersets.ASNFilterSet
  191. table = tables.ASNTable
  192. form = forms.ASNBulkEditForm
  193. class ASNBulkDeleteView(generic.BulkDeleteView):
  194. queryset = ASN.objects.annotate(
  195. site_count=count_related(Site, 'asns')
  196. )
  197. filterset = filtersets.ASNFilterSet
  198. table = tables.ASNTable
  199. #
  200. # Aggregates
  201. #
  202. class AggregateListView(generic.ObjectListView):
  203. queryset = Aggregate.objects.annotate(
  204. child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
  205. )
  206. filterset = filtersets.AggregateFilterSet
  207. filterset_form = forms.AggregateFilterForm
  208. table = tables.AggregateTable
  209. class AggregateView(generic.ObjectView):
  210. queryset = Aggregate.objects.all()
  211. class AggregatePrefixesView(generic.ObjectChildrenView):
  212. queryset = Aggregate.objects.all()
  213. child_model = Prefix
  214. table = tables.PrefixTable
  215. filterset = filtersets.PrefixFilterSet
  216. template_name = 'ipam/aggregate/prefixes.html'
  217. def get_children(self, request, parent):
  218. return Prefix.objects.restrict(request.user, 'view').filter(
  219. prefix__net_contained_or_equal=str(parent.prefix)
  220. ).prefetch_related('site', 'role', 'tenant', 'vlan')
  221. def prep_table_data(self, request, queryset, parent):
  222. # Determine whether to show assigned prefixes, available prefixes, or both
  223. show_available = bool(request.GET.get('show_available', 'true') == 'true')
  224. show_assigned = bool(request.GET.get('show_assigned', 'true') == 'true')
  225. return add_requested_prefixes(parent.prefix, queryset, show_available, show_assigned)
  226. def get_extra_context(self, request, instance):
  227. return {
  228. 'bulk_querystring': f'within={instance.prefix}',
  229. 'active_tab': 'prefixes',
  230. 'first_available_prefix': instance.get_first_available_prefix(),
  231. 'show_available': bool(request.GET.get('show_available', 'true') == 'true'),
  232. 'show_assigned': bool(request.GET.get('show_assigned', 'true') == 'true'),
  233. }
  234. class AggregateEditView(generic.ObjectEditView):
  235. queryset = Aggregate.objects.all()
  236. form = forms.AggregateForm
  237. class AggregateDeleteView(generic.ObjectDeleteView):
  238. queryset = Aggregate.objects.all()
  239. class AggregateBulkImportView(generic.BulkImportView):
  240. queryset = Aggregate.objects.all()
  241. model_form = forms.AggregateCSVForm
  242. table = tables.AggregateTable
  243. class AggregateBulkEditView(generic.BulkEditView):
  244. queryset = Aggregate.objects.prefetch_related('rir')
  245. filterset = filtersets.AggregateFilterSet
  246. table = tables.AggregateTable
  247. form = forms.AggregateBulkEditForm
  248. class AggregateBulkDeleteView(generic.BulkDeleteView):
  249. queryset = Aggregate.objects.prefetch_related('rir')
  250. filterset = filtersets.AggregateFilterSet
  251. table = tables.AggregateTable
  252. #
  253. # Prefix/VLAN roles
  254. #
  255. class RoleListView(generic.ObjectListView):
  256. queryset = Role.objects.annotate(
  257. prefix_count=count_related(Prefix, 'role'),
  258. iprange_count=count_related(IPRange, 'role'),
  259. vlan_count=count_related(VLAN, 'role')
  260. )
  261. filterset = filtersets.RoleFilterSet
  262. filterset_form = forms.RoleFilterForm
  263. table = tables.RoleTable
  264. class RoleView(generic.ObjectView):
  265. queryset = Role.objects.all()
  266. def get_extra_context(self, request, instance):
  267. prefixes = Prefix.objects.restrict(request.user, 'view').filter(
  268. role=instance
  269. )
  270. prefixes_table = tables.PrefixTable(prefixes, exclude=('role', 'utilization'))
  271. prefixes_table.configure(request)
  272. return {
  273. 'prefixes_table': prefixes_table,
  274. }
  275. class RoleEditView(generic.ObjectEditView):
  276. queryset = Role.objects.all()
  277. form = forms.RoleForm
  278. class RoleDeleteView(generic.ObjectDeleteView):
  279. queryset = Role.objects.all()
  280. class RoleBulkImportView(generic.BulkImportView):
  281. queryset = Role.objects.all()
  282. model_form = forms.RoleCSVForm
  283. table = tables.RoleTable
  284. class RoleBulkEditView(generic.BulkEditView):
  285. queryset = Role.objects.all()
  286. filterset = filtersets.RoleFilterSet
  287. table = tables.RoleTable
  288. form = forms.RoleBulkEditForm
  289. class RoleBulkDeleteView(generic.BulkDeleteView):
  290. queryset = Role.objects.all()
  291. table = tables.RoleTable
  292. #
  293. # Prefixes
  294. #
  295. class PrefixListView(generic.ObjectListView):
  296. queryset = Prefix.objects.all()
  297. filterset = filtersets.PrefixFilterSet
  298. filterset_form = forms.PrefixFilterForm
  299. table = tables.PrefixTable
  300. template_name = 'ipam/prefix_list.html'
  301. class PrefixView(generic.ObjectView):
  302. queryset = Prefix.objects.prefetch_related('vrf', 'site__region', 'tenant__group', 'vlan__group', 'role')
  303. def get_extra_context(self, request, instance):
  304. try:
  305. aggregate = Aggregate.objects.restrict(request.user, 'view').get(
  306. prefix__net_contains_or_equals=str(instance.prefix)
  307. )
  308. except Aggregate.DoesNotExist:
  309. aggregate = None
  310. # Parent prefixes table
  311. parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
  312. Q(vrf=instance.vrf) | Q(vrf__isnull=True)
  313. ).filter(
  314. prefix__net_contains=str(instance.prefix)
  315. ).prefetch_related(
  316. 'site', 'role', 'tenant'
  317. )
  318. parent_prefix_table = tables.PrefixTable(
  319. list(parent_prefixes),
  320. exclude=('vrf', 'utilization'),
  321. orderable=False
  322. )
  323. # Duplicate prefixes table
  324. duplicate_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
  325. vrf=instance.vrf, prefix=str(instance.prefix)
  326. ).exclude(
  327. pk=instance.pk
  328. ).prefetch_related(
  329. 'site', 'role'
  330. )
  331. duplicate_prefix_table = tables.PrefixTable(
  332. list(duplicate_prefixes),
  333. exclude=('vrf', 'utilization'),
  334. orderable=False
  335. )
  336. return {
  337. 'aggregate': aggregate,
  338. 'parent_prefix_table': parent_prefix_table,
  339. 'duplicate_prefix_table': duplicate_prefix_table,
  340. }
  341. class PrefixPrefixesView(generic.ObjectChildrenView):
  342. queryset = Prefix.objects.all()
  343. child_model = Prefix
  344. table = tables.PrefixTable
  345. filterset = filtersets.PrefixFilterSet
  346. template_name = 'ipam/prefix/prefixes.html'
  347. def get_children(self, request, parent):
  348. return parent.get_child_prefixes().restrict(request.user, 'view').prefetch_related(
  349. 'site', 'vrf', 'vlan', 'role', 'tenant',
  350. )
  351. def prep_table_data(self, request, queryset, parent):
  352. # Determine whether to show assigned prefixes, available prefixes, or both
  353. show_available = bool(request.GET.get('show_available', 'true') == 'true')
  354. show_assigned = bool(request.GET.get('show_assigned', 'true') == 'true')
  355. return add_requested_prefixes(parent.prefix, queryset, show_available, show_assigned)
  356. def get_extra_context(self, request, instance):
  357. return {
  358. 'bulk_querystring': f"vrf_id={instance.vrf.pk if instance.vrf else '0'}&within={instance.prefix}",
  359. 'active_tab': 'prefixes',
  360. 'first_available_prefix': instance.get_first_available_prefix(),
  361. 'show_available': bool(request.GET.get('show_available', 'true') == 'true'),
  362. 'show_assigned': bool(request.GET.get('show_assigned', 'true') == 'true'),
  363. }
  364. class PrefixIPRangesView(generic.ObjectChildrenView):
  365. queryset = Prefix.objects.all()
  366. child_model = IPRange
  367. table = tables.IPRangeTable
  368. filterset = filtersets.IPRangeFilterSet
  369. template_name = 'ipam/prefix/ip_ranges.html'
  370. def get_children(self, request, parent):
  371. return parent.get_child_ranges().restrict(request.user, 'view').prefetch_related(
  372. 'vrf', 'role', 'tenant',
  373. )
  374. def get_extra_context(self, request, instance):
  375. return {
  376. 'bulk_querystring': f"vrf_id={instance.vrf.pk if instance.vrf else '0'}&parent={instance.prefix}",
  377. 'active_tab': 'ip-ranges',
  378. 'first_available_ip': instance.get_first_available_ip(),
  379. }
  380. class PrefixIPAddressesView(generic.ObjectChildrenView):
  381. queryset = Prefix.objects.all()
  382. child_model = IPAddress
  383. table = tables.IPAddressTable
  384. filterset = filtersets.IPAddressFilterSet
  385. template_name = 'ipam/prefix/ip_addresses.html'
  386. def get_children(self, request, parent):
  387. return parent.get_child_ips().restrict(request.user, 'view').prefetch_related('vrf', 'tenant')
  388. def prep_table_data(self, request, queryset, parent):
  389. show_available = bool(request.GET.get('show_available', 'true') == 'true')
  390. if show_available:
  391. return add_available_ipaddresses(parent.prefix, queryset, parent.is_pool)
  392. return queryset
  393. def get_extra_context(self, request, instance):
  394. return {
  395. 'bulk_querystring': f"vrf_id={instance.vrf.pk if instance.vrf else '0'}&parent={instance.prefix}",
  396. 'active_tab': 'ip-addresses',
  397. 'first_available_ip': instance.get_first_available_ip(),
  398. }
  399. class PrefixEditView(generic.ObjectEditView):
  400. queryset = Prefix.objects.all()
  401. form = forms.PrefixForm
  402. class PrefixDeleteView(generic.ObjectDeleteView):
  403. queryset = Prefix.objects.all()
  404. class PrefixBulkImportView(generic.BulkImportView):
  405. queryset = Prefix.objects.all()
  406. model_form = forms.PrefixCSVForm
  407. table = tables.PrefixTable
  408. class PrefixBulkEditView(generic.BulkEditView):
  409. queryset = Prefix.objects.prefetch_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role')
  410. filterset = filtersets.PrefixFilterSet
  411. table = tables.PrefixTable
  412. form = forms.PrefixBulkEditForm
  413. class PrefixBulkDeleteView(generic.BulkDeleteView):
  414. queryset = Prefix.objects.prefetch_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role')
  415. filterset = filtersets.PrefixFilterSet
  416. table = tables.PrefixTable
  417. #
  418. # IP Ranges
  419. #
  420. class IPRangeListView(generic.ObjectListView):
  421. queryset = IPRange.objects.all()
  422. filterset = filtersets.IPRangeFilterSet
  423. filterset_form = forms.IPRangeFilterForm
  424. table = tables.IPRangeTable
  425. class IPRangeView(generic.ObjectView):
  426. queryset = IPRange.objects.all()
  427. class IPRangeIPAddressesView(generic.ObjectChildrenView):
  428. queryset = IPRange.objects.all()
  429. child_model = IPAddress
  430. table = tables.IPAddressTable
  431. filterset = filtersets.IPAddressFilterSet
  432. template_name = 'ipam/iprange/ip_addresses.html'
  433. def get_children(self, request, parent):
  434. return parent.get_child_ips().restrict(request.user, 'view').prefetch_related(
  435. 'vrf', 'role', 'tenant',
  436. )
  437. def get_extra_context(self, request, instance):
  438. return {
  439. 'active_tab': 'ip-addresses',
  440. }
  441. class IPRangeEditView(generic.ObjectEditView):
  442. queryset = IPRange.objects.all()
  443. form = forms.IPRangeForm
  444. class IPRangeDeleteView(generic.ObjectDeleteView):
  445. queryset = IPRange.objects.all()
  446. class IPRangeBulkImportView(generic.BulkImportView):
  447. queryset = IPRange.objects.all()
  448. model_form = forms.IPRangeCSVForm
  449. table = tables.IPRangeTable
  450. class IPRangeBulkEditView(generic.BulkEditView):
  451. queryset = IPRange.objects.prefetch_related('vrf', 'tenant')
  452. filterset = filtersets.IPRangeFilterSet
  453. table = tables.IPRangeTable
  454. form = forms.IPRangeBulkEditForm
  455. class IPRangeBulkDeleteView(generic.BulkDeleteView):
  456. queryset = IPRange.objects.prefetch_related('vrf', 'tenant')
  457. filterset = filtersets.IPRangeFilterSet
  458. table = tables.IPRangeTable
  459. #
  460. # IP addresses
  461. #
  462. class IPAddressListView(generic.ObjectListView):
  463. queryset = IPAddress.objects.all()
  464. filterset = filtersets.IPAddressFilterSet
  465. filterset_form = forms.IPAddressFilterForm
  466. table = tables.IPAddressTable
  467. class IPAddressView(generic.ObjectView):
  468. queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant')
  469. def get_extra_context(self, request, instance):
  470. # Parent prefixes table
  471. parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
  472. vrf=instance.vrf,
  473. prefix__net_contains_or_equals=str(instance.address.ip)
  474. ).prefetch_related(
  475. 'site', 'role'
  476. )
  477. parent_prefixes_table = tables.PrefixTable(
  478. list(parent_prefixes),
  479. exclude=('vrf', 'utilization'),
  480. orderable=False
  481. )
  482. # Duplicate IPs table
  483. duplicate_ips = IPAddress.objects.restrict(request.user, 'view').filter(
  484. vrf=instance.vrf,
  485. address=str(instance.address)
  486. ).exclude(
  487. pk=instance.pk
  488. ).prefetch_related(
  489. 'nat_inside'
  490. )
  491. # Exclude anycast IPs if this IP is anycast
  492. if instance.role == IPAddressRoleChoices.ROLE_ANYCAST:
  493. duplicate_ips = duplicate_ips.exclude(role=IPAddressRoleChoices.ROLE_ANYCAST)
  494. # Limit to a maximum of 10 duplicates displayed here
  495. duplicate_ips_table = tables.IPAddressTable(duplicate_ips[:10], orderable=False)
  496. # Related IP table
  497. related_ips = IPAddress.objects.restrict(request.user, 'view').exclude(
  498. address=str(instance.address)
  499. ).filter(
  500. vrf=instance.vrf, address__net_contained_or_equal=str(instance.address)
  501. )
  502. related_ips_table = tables.IPAddressTable(related_ips, orderable=False)
  503. related_ips_table.configure(request)
  504. return {
  505. 'parent_prefixes_table': parent_prefixes_table,
  506. 'duplicate_ips_table': duplicate_ips_table,
  507. 'more_duplicate_ips': duplicate_ips.count() > 10,
  508. 'related_ips_table': related_ips_table,
  509. }
  510. class IPAddressEditView(generic.ObjectEditView):
  511. queryset = IPAddress.objects.all()
  512. form = forms.IPAddressForm
  513. template_name = 'ipam/ipaddress_edit.html'
  514. def alter_object(self, obj, request, url_args, url_kwargs):
  515. if 'interface' in request.GET:
  516. try:
  517. obj.assigned_object = Interface.objects.get(pk=request.GET['interface'])
  518. except (ValueError, Interface.DoesNotExist):
  519. pass
  520. elif 'vminterface' in request.GET:
  521. try:
  522. obj.assigned_object = VMInterface.objects.get(pk=request.GET['vminterface'])
  523. except (ValueError, VMInterface.DoesNotExist):
  524. pass
  525. elif 'fhrpgroup' in request.GET:
  526. try:
  527. obj.assigned_object = FHRPGroup.objects.get(pk=request.GET['fhrpgroup'])
  528. except (ValueError, FHRPGroup.DoesNotExist):
  529. pass
  530. return obj
  531. # TODO: Standardize or remove this view
  532. class IPAddressAssignView(generic.ObjectView):
  533. """
  534. Search for IPAddresses to be assigned to an Interface.
  535. """
  536. queryset = IPAddress.objects.all()
  537. def dispatch(self, request, *args, **kwargs):
  538. # Redirect user if an interface has not been provided
  539. if 'interface' not in request.GET and 'vminterface' not in request.GET:
  540. return redirect('ipam:ipaddress_add')
  541. return super().dispatch(request, *args, **kwargs)
  542. def get(self, request):
  543. form = forms.IPAddressAssignForm()
  544. return render(request, 'ipam/ipaddress_assign.html', {
  545. 'form': form,
  546. 'return_url': request.GET.get('return_url', ''),
  547. })
  548. def post(self, request):
  549. form = forms.IPAddressAssignForm(request.POST)
  550. table = None
  551. if form.is_valid():
  552. addresses = self.queryset.prefetch_related('vrf', 'tenant')
  553. # Limit to 100 results
  554. addresses = filtersets.IPAddressFilterSet(request.POST, addresses).qs[:100]
  555. table = tables.IPAddressAssignTable(addresses)
  556. return render(request, 'ipam/ipaddress_assign.html', {
  557. 'form': form,
  558. 'table': table,
  559. 'return_url': request.GET.get('return_url'),
  560. })
  561. class IPAddressDeleteView(generic.ObjectDeleteView):
  562. queryset = IPAddress.objects.all()
  563. class IPAddressBulkCreateView(generic.BulkCreateView):
  564. queryset = IPAddress.objects.all()
  565. form = forms.IPAddressBulkCreateForm
  566. model_form = forms.IPAddressBulkAddForm
  567. pattern_target = 'address'
  568. template_name = 'ipam/ipaddress_bulk_add.html'
  569. class IPAddressBulkImportView(generic.BulkImportView):
  570. queryset = IPAddress.objects.all()
  571. model_form = forms.IPAddressCSVForm
  572. table = tables.IPAddressTable
  573. class IPAddressBulkEditView(generic.BulkEditView):
  574. queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant')
  575. filterset = filtersets.IPAddressFilterSet
  576. table = tables.IPAddressTable
  577. form = forms.IPAddressBulkEditForm
  578. class IPAddressBulkDeleteView(generic.BulkDeleteView):
  579. queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant')
  580. filterset = filtersets.IPAddressFilterSet
  581. table = tables.IPAddressTable
  582. #
  583. # VLAN groups
  584. #
  585. class VLANGroupListView(generic.ObjectListView):
  586. queryset = VLANGroup.objects.annotate(
  587. vlan_count=count_related(VLAN, 'group')
  588. )
  589. filterset = filtersets.VLANGroupFilterSet
  590. filterset_form = forms.VLANGroupFilterForm
  591. table = tables.VLANGroupTable
  592. class VLANGroupView(generic.ObjectView):
  593. queryset = VLANGroup.objects.all()
  594. def get_extra_context(self, request, instance):
  595. vlans = VLAN.objects.restrict(request.user, 'view').filter(group=instance).prefetch_related(
  596. Prefetch('prefixes', queryset=Prefix.objects.restrict(request.user))
  597. ).order_by('vid')
  598. vlans_count = vlans.count()
  599. vlans = add_available_vlans(vlans, vlan_group=instance)
  600. vlans_table = tables.VLANTable(vlans, exclude=('group',))
  601. if request.user.has_perm('ipam.change_vlan') or request.user.has_perm('ipam.delete_vlan'):
  602. vlans_table.columns.show('pk')
  603. vlans_table.configure(request)
  604. # Compile permissions list for rendering the object table
  605. permissions = {
  606. 'add': request.user.has_perm('ipam.add_vlan'),
  607. 'change': request.user.has_perm('ipam.change_vlan'),
  608. 'delete': request.user.has_perm('ipam.delete_vlan'),
  609. }
  610. return {
  611. 'vlans_count': vlans_count,
  612. 'vlans_table': vlans_table,
  613. 'permissions': permissions,
  614. }
  615. class VLANGroupEditView(generic.ObjectEditView):
  616. queryset = VLANGroup.objects.all()
  617. form = forms.VLANGroupForm
  618. class VLANGroupDeleteView(generic.ObjectDeleteView):
  619. queryset = VLANGroup.objects.all()
  620. class VLANGroupBulkImportView(generic.BulkImportView):
  621. queryset = VLANGroup.objects.all()
  622. model_form = forms.VLANGroupCSVForm
  623. table = tables.VLANGroupTable
  624. class VLANGroupBulkEditView(generic.BulkEditView):
  625. queryset = VLANGroup.objects.annotate(
  626. vlan_count=count_related(VLAN, 'group')
  627. )
  628. filterset = filtersets.VLANGroupFilterSet
  629. table = tables.VLANGroupTable
  630. form = forms.VLANGroupBulkEditForm
  631. class VLANGroupBulkDeleteView(generic.BulkDeleteView):
  632. queryset = VLANGroup.objects.annotate(
  633. vlan_count=count_related(VLAN, 'group')
  634. )
  635. filterset = filtersets.VLANGroupFilterSet
  636. table = tables.VLANGroupTable
  637. #
  638. # FHRP groups
  639. #
  640. class FHRPGroupListView(generic.ObjectListView):
  641. queryset = FHRPGroup.objects.annotate(
  642. member_count=count_related(FHRPGroupAssignment, 'group')
  643. )
  644. filterset = filtersets.FHRPGroupFilterSet
  645. filterset_form = forms.FHRPGroupFilterForm
  646. table = tables.FHRPGroupTable
  647. class FHRPGroupView(generic.ObjectView):
  648. queryset = FHRPGroup.objects.all()
  649. def get_extra_context(self, request, instance):
  650. # Get assigned IP addresses
  651. ipaddress_table = tables.AssignedIPAddressesTable(
  652. data=instance.ip_addresses.restrict(request.user, 'view').prefetch_related('vrf', 'tenant'),
  653. orderable=False
  654. )
  655. # Get assigned interfaces
  656. members_table = tables.FHRPGroupAssignmentTable(
  657. data=FHRPGroupAssignment.objects.restrict(request.user, 'view').filter(group=instance),
  658. orderable=False
  659. )
  660. members_table.columns.hide('group')
  661. return {
  662. 'ipaddress_table': ipaddress_table,
  663. 'members_table': members_table,
  664. 'member_count': FHRPGroupAssignment.objects.filter(group=instance).count(),
  665. }
  666. class FHRPGroupEditView(generic.ObjectEditView):
  667. queryset = FHRPGroup.objects.all()
  668. form = forms.FHRPGroupForm
  669. template_name = 'ipam/fhrpgroup_edit.html'
  670. def get_return_url(self, request, obj=None):
  671. return_url = super().get_return_url(request, obj)
  672. # If we're redirecting the user to the FHRPGroupAssignment creation form,
  673. # initialize the group field with the FHRPGroup we just saved.
  674. if return_url.startswith(reverse('ipam:fhrpgroupassignment_add')):
  675. return_url += f'&group={obj.pk}'
  676. return return_url
  677. class FHRPGroupDeleteView(generic.ObjectDeleteView):
  678. queryset = FHRPGroup.objects.all()
  679. class FHRPGroupBulkImportView(generic.BulkImportView):
  680. queryset = FHRPGroup.objects.all()
  681. model_form = forms.FHRPGroupCSVForm
  682. table = tables.FHRPGroupTable
  683. class FHRPGroupBulkEditView(generic.BulkEditView):
  684. queryset = FHRPGroup.objects.all()
  685. filterset = filtersets.FHRPGroupFilterSet
  686. table = tables.FHRPGroupTable
  687. form = forms.FHRPGroupBulkEditForm
  688. class FHRPGroupBulkDeleteView(generic.BulkDeleteView):
  689. queryset = FHRPGroup.objects.all()
  690. filterset = filtersets.FHRPGroupFilterSet
  691. table = tables.FHRPGroupTable
  692. #
  693. # FHRP group assignments
  694. #
  695. class FHRPGroupAssignmentEditView(generic.ObjectEditView):
  696. queryset = FHRPGroupAssignment.objects.all()
  697. form = forms.FHRPGroupAssignmentForm
  698. template_name = 'ipam/fhrpgroupassignment_edit.html'
  699. def alter_object(self, instance, request, args, kwargs):
  700. if not instance.pk:
  701. # Assign the interface based on URL kwargs
  702. content_type = get_object_or_404(ContentType, pk=request.GET.get('interface_type'))
  703. instance.interface = get_object_or_404(content_type.model_class(), pk=request.GET.get('interface_id'))
  704. return instance
  705. class FHRPGroupAssignmentDeleteView(generic.ObjectDeleteView):
  706. queryset = FHRPGroupAssignment.objects.all()
  707. #
  708. # VLANs
  709. #
  710. class VLANListView(generic.ObjectListView):
  711. queryset = VLAN.objects.all()
  712. filterset = filtersets.VLANFilterSet
  713. filterset_form = forms.VLANFilterForm
  714. table = tables.VLANTable
  715. class VLANView(generic.ObjectView):
  716. queryset = VLAN.objects.prefetch_related('site__region', 'tenant__group', 'role')
  717. def get_extra_context(self, request, instance):
  718. prefixes = Prefix.objects.restrict(request.user, 'view').filter(vlan=instance).prefetch_related(
  719. 'vrf', 'site', 'role'
  720. )
  721. prefix_table = tables.PrefixTable(list(prefixes), exclude=('vlan', 'utilization'), orderable=False)
  722. return {
  723. 'prefix_table': prefix_table,
  724. }
  725. class VLANInterfacesView(generic.ObjectChildrenView):
  726. queryset = VLAN.objects.all()
  727. child_model = Interface
  728. table = tables.VLANDevicesTable
  729. filterset = InterfaceFilterSet
  730. template_name = 'ipam/vlan/interfaces.html'
  731. def get_children(self, request, parent):
  732. return parent.get_interfaces().restrict(request.user, 'view')
  733. def get_extra_context(self, request, instance):
  734. return {
  735. 'active_tab': 'interfaces',
  736. }
  737. class VLANVMInterfacesView(generic.ObjectChildrenView):
  738. queryset = VLAN.objects.all()
  739. child_model = VMInterface
  740. table = tables.VLANVirtualMachinesTable
  741. filterset = VMInterfaceFilterSet
  742. template_name = 'ipam/vlan/vminterfaces.html'
  743. def get_children(self, request, parent):
  744. return parent.get_vminterfaces().restrict(request.user, 'view')
  745. def get_extra_context(self, request, instance):
  746. return {
  747. 'active_tab': 'vminterfaces',
  748. }
  749. class VLANEditView(generic.ObjectEditView):
  750. queryset = VLAN.objects.all()
  751. form = forms.VLANForm
  752. template_name = 'ipam/vlan_edit.html'
  753. class VLANDeleteView(generic.ObjectDeleteView):
  754. queryset = VLAN.objects.all()
  755. class VLANBulkImportView(generic.BulkImportView):
  756. queryset = VLAN.objects.all()
  757. model_form = forms.VLANCSVForm
  758. table = tables.VLANTable
  759. class VLANBulkEditView(generic.BulkEditView):
  760. queryset = VLAN.objects.prefetch_related('site', 'group', 'tenant', 'role')
  761. filterset = filtersets.VLANFilterSet
  762. table = tables.VLANTable
  763. form = forms.VLANBulkEditForm
  764. class VLANBulkDeleteView(generic.BulkDeleteView):
  765. queryset = VLAN.objects.prefetch_related('site', 'group', 'tenant', 'role')
  766. filterset = filtersets.VLANFilterSet
  767. table = tables.VLANTable
  768. #
  769. # Service templates
  770. #
  771. class ServiceTemplateListView(generic.ObjectListView):
  772. queryset = ServiceTemplate.objects.all()
  773. filterset = filtersets.ServiceTemplateFilterSet
  774. filterset_form = forms.ServiceTemplateFilterForm
  775. table = tables.ServiceTemplateTable
  776. class ServiceTemplateView(generic.ObjectView):
  777. queryset = ServiceTemplate.objects.all()
  778. class ServiceTemplateEditView(generic.ObjectEditView):
  779. queryset = ServiceTemplate.objects.all()
  780. form = forms.ServiceTemplateForm
  781. class ServiceTemplateDeleteView(generic.ObjectDeleteView):
  782. queryset = ServiceTemplate.objects.all()
  783. class ServiceTemplateBulkImportView(generic.BulkImportView):
  784. queryset = ServiceTemplate.objects.all()
  785. model_form = forms.ServiceTemplateCSVForm
  786. table = tables.ServiceTemplateTable
  787. class ServiceTemplateBulkEditView(generic.BulkEditView):
  788. queryset = ServiceTemplate.objects.all()
  789. filterset = filtersets.ServiceTemplateFilterSet
  790. table = tables.ServiceTemplateTable
  791. form = forms.ServiceTemplateBulkEditForm
  792. class ServiceTemplateBulkDeleteView(generic.BulkDeleteView):
  793. queryset = ServiceTemplate.objects.all()
  794. filterset = filtersets.ServiceTemplateFilterSet
  795. table = tables.ServiceTemplateTable
  796. #
  797. # Services
  798. #
  799. class ServiceListView(generic.ObjectListView):
  800. queryset = Service.objects.all()
  801. filterset = filtersets.ServiceFilterSet
  802. filterset_form = forms.ServiceFilterForm
  803. table = tables.ServiceTable
  804. class ServiceView(generic.ObjectView):
  805. queryset = Service.objects.prefetch_related('ipaddresses')
  806. class ServiceCreateView(generic.ObjectEditView):
  807. queryset = Service.objects.all()
  808. form = forms.ServiceCreateForm
  809. template_name = 'ipam/service_create.html'
  810. class ServiceEditView(generic.ObjectEditView):
  811. queryset = Service.objects.prefetch_related('ipaddresses')
  812. form = forms.ServiceForm
  813. template_name = 'ipam/service_edit.html'
  814. class ServiceDeleteView(generic.ObjectDeleteView):
  815. queryset = Service.objects.all()
  816. class ServiceBulkImportView(generic.BulkImportView):
  817. queryset = Service.objects.all()
  818. model_form = forms.ServiceCSVForm
  819. table = tables.ServiceTable
  820. class ServiceBulkEditView(generic.BulkEditView):
  821. queryset = Service.objects.prefetch_related('device', 'virtual_machine')
  822. filterset = filtersets.ServiceFilterSet
  823. table = tables.ServiceTable
  824. form = forms.ServiceBulkEditForm
  825. class ServiceBulkDeleteView(generic.BulkDeleteView):
  826. queryset = Service.objects.prefetch_related('device', 'virtual_machine')
  827. filterset = filtersets.ServiceFilterSet
  828. table = tables.ServiceTable