views.py 32 KB

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