views.py 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073
  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. 'first_available_prefix': instance.get_first_available_prefix(),
  222. 'show_available': bool(request.GET.get('show_available', 'true') == 'true'),
  223. 'show_assigned': bool(request.GET.get('show_assigned', 'true') == 'true'),
  224. }
  225. class AggregateEditView(generic.ObjectEditView):
  226. queryset = Aggregate.objects.all()
  227. model_form = forms.AggregateForm
  228. class AggregateDeleteView(generic.ObjectDeleteView):
  229. queryset = Aggregate.objects.all()
  230. class AggregateBulkImportView(generic.BulkImportView):
  231. queryset = Aggregate.objects.all()
  232. model_form = forms.AggregateCSVForm
  233. table = tables.AggregateTable
  234. class AggregateBulkEditView(generic.BulkEditView):
  235. queryset = Aggregate.objects.prefetch_related('rir')
  236. filterset = filtersets.AggregateFilterSet
  237. table = tables.AggregateTable
  238. form = forms.AggregateBulkEditForm
  239. class AggregateBulkDeleteView(generic.BulkDeleteView):
  240. queryset = Aggregate.objects.prefetch_related('rir')
  241. filterset = filtersets.AggregateFilterSet
  242. table = tables.AggregateTable
  243. #
  244. # Prefix/VLAN roles
  245. #
  246. class RoleListView(generic.ObjectListView):
  247. queryset = Role.objects.annotate(
  248. prefix_count=count_related(Prefix, 'role'),
  249. iprange_count=count_related(IPRange, 'role'),
  250. vlan_count=count_related(VLAN, 'role')
  251. )
  252. filterset = filtersets.RoleFilterSet
  253. filterset_form = forms.RoleFilterForm
  254. table = tables.RoleTable
  255. class RoleView(generic.ObjectView):
  256. queryset = Role.objects.all()
  257. def get_extra_context(self, request, instance):
  258. prefixes = Prefix.objects.restrict(request.user, 'view').filter(
  259. role=instance
  260. )
  261. prefixes_table = tables.PrefixTable(prefixes, exclude=('role', 'utilization'))
  262. paginate_table(prefixes_table, request)
  263. return {
  264. 'prefixes_table': prefixes_table,
  265. }
  266. class RoleEditView(generic.ObjectEditView):
  267. queryset = Role.objects.all()
  268. model_form = forms.RoleForm
  269. class RoleDeleteView(generic.ObjectDeleteView):
  270. queryset = Role.objects.all()
  271. class RoleBulkImportView(generic.BulkImportView):
  272. queryset = Role.objects.all()
  273. model_form = forms.RoleCSVForm
  274. table = tables.RoleTable
  275. class RoleBulkEditView(generic.BulkEditView):
  276. queryset = Role.objects.all()
  277. filterset = filtersets.RoleFilterSet
  278. table = tables.RoleTable
  279. form = forms.RoleBulkEditForm
  280. class RoleBulkDeleteView(generic.BulkDeleteView):
  281. queryset = Role.objects.all()
  282. table = tables.RoleTable
  283. #
  284. # Prefixes
  285. #
  286. class PrefixListView(generic.ObjectListView):
  287. queryset = Prefix.objects.all()
  288. filterset = filtersets.PrefixFilterSet
  289. filterset_form = forms.PrefixFilterForm
  290. table = tables.PrefixTable
  291. template_name = 'ipam/prefix_list.html'
  292. class PrefixView(generic.ObjectView):
  293. queryset = Prefix.objects.prefetch_related('vrf', 'site__region', 'tenant__group', 'vlan__group', 'role')
  294. def get_extra_context(self, request, instance):
  295. try:
  296. aggregate = Aggregate.objects.restrict(request.user, 'view').get(
  297. prefix__net_contains_or_equals=str(instance.prefix)
  298. )
  299. except Aggregate.DoesNotExist:
  300. aggregate = None
  301. # Parent prefixes table
  302. parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
  303. Q(vrf=instance.vrf) | Q(vrf__isnull=True)
  304. ).filter(
  305. prefix__net_contains=str(instance.prefix)
  306. ).prefetch_related(
  307. 'site', 'role', 'tenant'
  308. )
  309. parent_prefix_table = tables.PrefixTable(
  310. list(parent_prefixes),
  311. exclude=('vrf', 'utilization'),
  312. orderable=False
  313. )
  314. # Duplicate prefixes table
  315. duplicate_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
  316. vrf=instance.vrf, prefix=str(instance.prefix)
  317. ).exclude(
  318. pk=instance.pk
  319. ).prefetch_related(
  320. 'site', 'role'
  321. )
  322. duplicate_prefix_table = tables.PrefixTable(
  323. list(duplicate_prefixes),
  324. exclude=('vrf', 'utilization'),
  325. orderable=False
  326. )
  327. return {
  328. 'aggregate': aggregate,
  329. 'parent_prefix_table': parent_prefix_table,
  330. 'duplicate_prefix_table': duplicate_prefix_table,
  331. }
  332. class PrefixPrefixesView(generic.ObjectChildrenView):
  333. queryset = Prefix.objects.all()
  334. child_model = Prefix
  335. table = tables.PrefixTable
  336. filterset = filtersets.PrefixFilterSet
  337. template_name = 'ipam/prefix/prefixes.html'
  338. def get_children(self, request, parent):
  339. return parent.get_child_prefixes().restrict(request.user, 'view').prefetch_related(
  340. 'site', 'vrf', 'vlan', 'role', 'tenant',
  341. )
  342. def prep_table_data(self, request, queryset, parent):
  343. # Determine whether to show assigned prefixes, available prefixes, or both
  344. show_available = bool(request.GET.get('show_available', 'true') == 'true')
  345. show_assigned = bool(request.GET.get('show_assigned', 'true') == 'true')
  346. return add_requested_prefixes(parent.prefix, queryset, show_available, show_assigned)
  347. def get_extra_context(self, request, instance):
  348. return {
  349. 'bulk_querystring': f"vrf_id={instance.vrf.pk if instance.vrf else '0'}&within={instance.prefix}",
  350. 'active_tab': 'prefixes',
  351. 'first_available_prefix': instance.get_first_available_prefix(),
  352. 'show_available': bool(request.GET.get('show_available', 'true') == 'true'),
  353. 'show_assigned': bool(request.GET.get('show_assigned', 'true') == 'true'),
  354. }
  355. class PrefixIPRangesView(generic.ObjectChildrenView):
  356. queryset = Prefix.objects.all()
  357. child_model = IPRange
  358. table = tables.IPRangeTable
  359. filterset = filtersets.IPRangeFilterSet
  360. template_name = 'ipam/prefix/ip_ranges.html'
  361. def get_children(self, request, parent):
  362. return parent.get_child_ranges().restrict(request.user, 'view').prefetch_related(
  363. 'vrf', 'role', 'tenant',
  364. )
  365. def get_extra_context(self, request, instance):
  366. return {
  367. 'bulk_querystring': f"vrf_id={instance.vrf.pk if instance.vrf else '0'}&parent={instance.prefix}",
  368. 'active_tab': 'ip-ranges',
  369. 'first_available_ip': instance.get_first_available_ip(),
  370. }
  371. class PrefixIPAddressesView(generic.ObjectChildrenView):
  372. queryset = Prefix.objects.all()
  373. child_model = IPAddress
  374. table = tables.IPAddressTable
  375. filterset = filtersets.IPAddressFilterSet
  376. template_name = 'ipam/prefix/ip_addresses.html'
  377. def get_children(self, request, parent):
  378. return parent.get_child_ips().restrict(request.user, 'view').prefetch_related('vrf', 'tenant')
  379. def prep_table_data(self, request, queryset, parent):
  380. show_available = bool(request.GET.get('show_available', 'true') == 'true')
  381. if show_available:
  382. return add_available_ipaddresses(parent.prefix, queryset, parent.is_pool)
  383. return queryset
  384. def get_extra_context(self, request, instance):
  385. return {
  386. 'bulk_querystring': f"vrf_id={instance.vrf.pk if instance.vrf else '0'}&parent={instance.prefix}",
  387. 'active_tab': 'ip-addresses',
  388. 'first_available_ip': instance.get_first_available_ip(),
  389. }
  390. class PrefixEditView(generic.ObjectEditView):
  391. queryset = Prefix.objects.all()
  392. model_form = forms.PrefixForm
  393. class PrefixDeleteView(generic.ObjectDeleteView):
  394. queryset = Prefix.objects.all()
  395. class PrefixBulkImportView(generic.BulkImportView):
  396. queryset = Prefix.objects.all()
  397. model_form = forms.PrefixCSVForm
  398. table = tables.PrefixTable
  399. class PrefixBulkEditView(generic.BulkEditView):
  400. queryset = Prefix.objects.prefetch_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role')
  401. filterset = filtersets.PrefixFilterSet
  402. table = tables.PrefixTable
  403. form = forms.PrefixBulkEditForm
  404. class PrefixBulkDeleteView(generic.BulkDeleteView):
  405. queryset = Prefix.objects.prefetch_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role')
  406. filterset = filtersets.PrefixFilterSet
  407. table = tables.PrefixTable
  408. #
  409. # IP Ranges
  410. #
  411. class IPRangeListView(generic.ObjectListView):
  412. queryset = IPRange.objects.all()
  413. filterset = filtersets.IPRangeFilterSet
  414. filterset_form = forms.IPRangeFilterForm
  415. table = tables.IPRangeTable
  416. class IPRangeView(generic.ObjectView):
  417. queryset = IPRange.objects.all()
  418. class IPRangeIPAddressesView(generic.ObjectChildrenView):
  419. queryset = IPRange.objects.all()
  420. child_model = IPAddress
  421. table = tables.IPAddressTable
  422. filterset = filtersets.IPAddressFilterSet
  423. template_name = 'ipam/iprange/ip_addresses.html'
  424. def get_children(self, request, parent):
  425. return parent.get_child_ips().restrict(request.user, 'view').prefetch_related(
  426. 'vrf', 'role', 'tenant',
  427. )
  428. def get_extra_context(self, request, instance):
  429. return {
  430. 'active_tab': 'ip-addresses',
  431. }
  432. class IPRangeEditView(generic.ObjectEditView):
  433. queryset = IPRange.objects.all()
  434. model_form = forms.IPRangeForm
  435. class IPRangeDeleteView(generic.ObjectDeleteView):
  436. queryset = IPRange.objects.all()
  437. class IPRangeBulkImportView(generic.BulkImportView):
  438. queryset = IPRange.objects.all()
  439. model_form = forms.IPRangeCSVForm
  440. table = tables.IPRangeTable
  441. class IPRangeBulkEditView(generic.BulkEditView):
  442. queryset = IPRange.objects.prefetch_related('vrf', 'tenant')
  443. filterset = filtersets.IPRangeFilterSet
  444. table = tables.IPRangeTable
  445. form = forms.IPRangeBulkEditForm
  446. class IPRangeBulkDeleteView(generic.BulkDeleteView):
  447. queryset = IPRange.objects.prefetch_related('vrf', 'tenant')
  448. filterset = filtersets.IPRangeFilterSet
  449. table = tables.IPRangeTable
  450. #
  451. # IP addresses
  452. #
  453. class IPAddressListView(generic.ObjectListView):
  454. queryset = IPAddress.objects.all()
  455. filterset = filtersets.IPAddressFilterSet
  456. filterset_form = forms.IPAddressFilterForm
  457. table = tables.IPAddressTable
  458. class IPAddressView(generic.ObjectView):
  459. queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant')
  460. def get_extra_context(self, request, instance):
  461. # Parent prefixes table
  462. parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
  463. vrf=instance.vrf,
  464. prefix__net_contains_or_equals=str(instance.address.ip)
  465. ).prefetch_related(
  466. 'site', 'role'
  467. )
  468. parent_prefixes_table = tables.PrefixTable(
  469. list(parent_prefixes),
  470. exclude=('vrf', 'utilization'),
  471. orderable=False
  472. )
  473. # Duplicate IPs table
  474. duplicate_ips = IPAddress.objects.restrict(request.user, 'view').filter(
  475. vrf=instance.vrf,
  476. address=str(instance.address)
  477. ).exclude(
  478. pk=instance.pk
  479. ).prefetch_related(
  480. 'nat_inside'
  481. )
  482. # Exclude anycast IPs if this IP is anycast
  483. if instance.role == IPAddressRoleChoices.ROLE_ANYCAST:
  484. duplicate_ips = duplicate_ips.exclude(role=IPAddressRoleChoices.ROLE_ANYCAST)
  485. # Limit to a maximum of 10 duplicates displayed here
  486. duplicate_ips_table = tables.IPAddressTable(duplicate_ips[:10], orderable=False)
  487. # Related IP table
  488. related_ips = IPAddress.objects.restrict(request.user, 'view').exclude(
  489. address=str(instance.address)
  490. ).filter(
  491. vrf=instance.vrf, address__net_contained_or_equal=str(instance.address)
  492. )
  493. related_ips_table = tables.IPAddressTable(related_ips, orderable=False)
  494. paginate_table(related_ips_table, request)
  495. return {
  496. 'parent_prefixes_table': parent_prefixes_table,
  497. 'duplicate_ips_table': duplicate_ips_table,
  498. 'more_duplicate_ips': duplicate_ips.count() > 10,
  499. 'related_ips_table': related_ips_table,
  500. }
  501. class IPAddressEditView(generic.ObjectEditView):
  502. queryset = IPAddress.objects.all()
  503. model_form = forms.IPAddressForm
  504. template_name = 'ipam/ipaddress_edit.html'
  505. def alter_obj(self, obj, request, url_args, url_kwargs):
  506. if 'interface' in request.GET:
  507. try:
  508. obj.assigned_object = Interface.objects.get(pk=request.GET['interface'])
  509. except (ValueError, Interface.DoesNotExist):
  510. pass
  511. elif 'vminterface' in request.GET:
  512. try:
  513. obj.assigned_object = VMInterface.objects.get(pk=request.GET['vminterface'])
  514. except (ValueError, VMInterface.DoesNotExist):
  515. pass
  516. elif 'fhrpgroup' in request.GET:
  517. try:
  518. obj.assigned_object = FHRPGroup.objects.get(pk=request.GET['fhrpgroup'])
  519. except (ValueError, FHRPGroup.DoesNotExist):
  520. pass
  521. return obj
  522. # TODO: Standardize or remove this view
  523. class IPAddressAssignView(generic.ObjectView):
  524. """
  525. Search for IPAddresses to be assigned to an Interface.
  526. """
  527. queryset = IPAddress.objects.all()
  528. def dispatch(self, request, *args, **kwargs):
  529. # Redirect user if an interface has not been provided
  530. if 'interface' not in request.GET and 'vminterface' not in request.GET:
  531. return redirect('ipam:ipaddress_add')
  532. return super().dispatch(request, *args, **kwargs)
  533. def get(self, request):
  534. form = forms.IPAddressAssignForm()
  535. return render(request, 'ipam/ipaddress_assign.html', {
  536. 'form': form,
  537. 'return_url': request.GET.get('return_url', ''),
  538. })
  539. def post(self, request):
  540. form = forms.IPAddressAssignForm(request.POST)
  541. table = None
  542. if form.is_valid():
  543. addresses = self.queryset.prefetch_related('vrf', 'tenant')
  544. # Limit to 100 results
  545. addresses = filtersets.IPAddressFilterSet(request.POST, addresses).qs[:100]
  546. table = tables.IPAddressAssignTable(addresses)
  547. return render(request, 'ipam/ipaddress_assign.html', {
  548. 'form': form,
  549. 'table': table,
  550. 'return_url': request.GET.get('return_url'),
  551. })
  552. class IPAddressDeleteView(generic.ObjectDeleteView):
  553. queryset = IPAddress.objects.all()
  554. class IPAddressBulkCreateView(generic.BulkCreateView):
  555. queryset = IPAddress.objects.all()
  556. form = forms.IPAddressBulkCreateForm
  557. model_form = forms.IPAddressBulkAddForm
  558. pattern_target = 'address'
  559. template_name = 'ipam/ipaddress_bulk_add.html'
  560. class IPAddressBulkImportView(generic.BulkImportView):
  561. queryset = IPAddress.objects.all()
  562. model_form = forms.IPAddressCSVForm
  563. table = tables.IPAddressTable
  564. class IPAddressBulkEditView(generic.BulkEditView):
  565. queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant')
  566. filterset = filtersets.IPAddressFilterSet
  567. table = tables.IPAddressTable
  568. form = forms.IPAddressBulkEditForm
  569. class IPAddressBulkDeleteView(generic.BulkDeleteView):
  570. queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant')
  571. filterset = filtersets.IPAddressFilterSet
  572. table = tables.IPAddressTable
  573. #
  574. # VLAN groups
  575. #
  576. class VLANGroupListView(generic.ObjectListView):
  577. queryset = VLANGroup.objects.annotate(
  578. vlan_count=count_related(VLAN, 'group')
  579. )
  580. filterset = filtersets.VLANGroupFilterSet
  581. filterset_form = forms.VLANGroupFilterForm
  582. table = tables.VLANGroupTable
  583. class VLANGroupView(generic.ObjectView):
  584. queryset = VLANGroup.objects.all()
  585. def get_extra_context(self, request, instance):
  586. vlans = VLAN.objects.restrict(request.user, 'view').filter(group=instance).prefetch_related(
  587. Prefetch('prefixes', queryset=Prefix.objects.restrict(request.user))
  588. ).order_by('vid')
  589. vlans_count = vlans.count()
  590. vlans = add_available_vlans(vlans, vlan_group=instance)
  591. vlans_table = tables.VLANTable(vlans, exclude=('site', 'group', 'prefixes'))
  592. if request.user.has_perm('ipam.change_vlan') or request.user.has_perm('ipam.delete_vlan'):
  593. vlans_table.columns.show('pk')
  594. paginate_table(vlans_table, request)
  595. # Compile permissions list for rendering the object table
  596. permissions = {
  597. 'add': request.user.has_perm('ipam.add_vlan'),
  598. 'change': request.user.has_perm('ipam.change_vlan'),
  599. 'delete': request.user.has_perm('ipam.delete_vlan'),
  600. }
  601. return {
  602. 'vlans_count': vlans_count,
  603. 'vlans_table': vlans_table,
  604. 'permissions': permissions,
  605. }
  606. class VLANGroupEditView(generic.ObjectEditView):
  607. queryset = VLANGroup.objects.all()
  608. model_form = forms.VLANGroupForm
  609. class VLANGroupDeleteView(generic.ObjectDeleteView):
  610. queryset = VLANGroup.objects.all()
  611. class VLANGroupBulkImportView(generic.BulkImportView):
  612. queryset = VLANGroup.objects.all()
  613. model_form = forms.VLANGroupCSVForm
  614. table = tables.VLANGroupTable
  615. class VLANGroupBulkEditView(generic.BulkEditView):
  616. queryset = VLANGroup.objects.annotate(
  617. vlan_count=count_related(VLAN, 'group')
  618. )
  619. filterset = filtersets.VLANGroupFilterSet
  620. table = tables.VLANGroupTable
  621. form = forms.VLANGroupBulkEditForm
  622. class VLANGroupBulkDeleteView(generic.BulkDeleteView):
  623. queryset = VLANGroup.objects.annotate(
  624. vlan_count=count_related(VLAN, 'group')
  625. )
  626. filterset = filtersets.VLANGroupFilterSet
  627. table = tables.VLANGroupTable
  628. #
  629. # FHRP groups
  630. #
  631. class FHRPGroupListView(generic.ObjectListView):
  632. queryset = FHRPGroup.objects.annotate(
  633. member_count=count_related(FHRPGroupAssignment, 'group')
  634. )
  635. filterset = filtersets.FHRPGroupFilterSet
  636. filterset_form = forms.FHRPGroupFilterForm
  637. table = tables.FHRPGroupTable
  638. class FHRPGroupView(generic.ObjectView):
  639. queryset = FHRPGroup.objects.all()
  640. def get_extra_context(self, request, instance):
  641. # Get assigned IP addresses
  642. ipaddress_table = tables.AssignedIPAddressesTable(
  643. data=instance.ip_addresses.restrict(request.user, 'view').prefetch_related('vrf', 'tenant'),
  644. orderable=False
  645. )
  646. # Get assigned interfaces
  647. members_table = tables.FHRPGroupAssignmentTable(
  648. data=FHRPGroupAssignment.objects.restrict(request.user, 'view').filter(group=instance),
  649. orderable=False
  650. )
  651. members_table.columns.hide('group')
  652. return {
  653. 'ipaddress_table': ipaddress_table,
  654. 'members_table': members_table,
  655. 'member_count': FHRPGroupAssignment.objects.filter(group=instance).count(),
  656. }
  657. class FHRPGroupEditView(generic.ObjectEditView):
  658. queryset = FHRPGroup.objects.all()
  659. model_form = forms.FHRPGroupForm
  660. template_name = 'ipam/fhrpgroup_edit.html'
  661. def get_return_url(self, request, obj=None):
  662. return_url = super().get_return_url(request, obj)
  663. # If we're redirecting the user to the FHRPGroupAssignment creation form,
  664. # initialize the group field with the FHRPGroup we just saved.
  665. if return_url.startswith(reverse('ipam:fhrpgroupassignment_add')):
  666. return_url += f'&group={obj.pk}'
  667. return return_url
  668. class FHRPGroupDeleteView(generic.ObjectDeleteView):
  669. queryset = FHRPGroup.objects.all()
  670. class FHRPGroupBulkImportView(generic.BulkImportView):
  671. queryset = FHRPGroup.objects.all()
  672. model_form = forms.FHRPGroupCSVForm
  673. table = tables.FHRPGroupTable
  674. class FHRPGroupBulkEditView(generic.BulkEditView):
  675. queryset = FHRPGroup.objects.all()
  676. filterset = filtersets.FHRPGroupFilterSet
  677. table = tables.FHRPGroupTable
  678. form = forms.FHRPGroupBulkEditForm
  679. class FHRPGroupBulkDeleteView(generic.BulkDeleteView):
  680. queryset = FHRPGroup.objects.all()
  681. filterset = filtersets.FHRPGroupFilterSet
  682. table = tables.FHRPGroupTable
  683. #
  684. # FHRP group assignments
  685. #
  686. class FHRPGroupAssignmentEditView(generic.ObjectEditView):
  687. queryset = FHRPGroupAssignment.objects.all()
  688. model_form = forms.FHRPGroupAssignmentForm
  689. template_name = 'ipam/fhrpgroupassignment_edit.html'
  690. def alter_obj(self, instance, request, args, kwargs):
  691. if not instance.pk:
  692. # Assign the interface based on URL kwargs
  693. content_type = get_object_or_404(ContentType, pk=request.GET.get('interface_type'))
  694. instance.interface = get_object_or_404(content_type.model_class(), pk=request.GET.get('interface_id'))
  695. return instance
  696. class FHRPGroupAssignmentDeleteView(generic.ObjectDeleteView):
  697. queryset = FHRPGroupAssignment.objects.all()
  698. #
  699. # VLANs
  700. #
  701. class VLANListView(generic.ObjectListView):
  702. queryset = VLAN.objects.all()
  703. filterset = filtersets.VLANFilterSet
  704. filterset_form = forms.VLANFilterForm
  705. table = tables.VLANTable
  706. class VLANView(generic.ObjectView):
  707. queryset = VLAN.objects.prefetch_related('site__region', 'tenant__group', 'role')
  708. def get_extra_context(self, request, instance):
  709. prefixes = Prefix.objects.restrict(request.user, 'view').filter(vlan=instance).prefetch_related(
  710. 'vrf', 'site', 'role'
  711. )
  712. prefix_table = tables.PrefixTable(list(prefixes), exclude=('vlan', 'utilization'), orderable=False)
  713. return {
  714. 'prefix_table': prefix_table,
  715. }
  716. class VLANInterfacesView(generic.ObjectChildrenView):
  717. queryset = VLAN.objects.all()
  718. child_model = Interface
  719. table = tables.VLANDevicesTable
  720. filterset = InterfaceFilterSet
  721. template_name = 'ipam/vlan/interfaces.html'
  722. def get_children(self, request, parent):
  723. return parent.get_interfaces().restrict(request.user, 'view')
  724. def get_extra_context(self, request, instance):
  725. return {
  726. 'active_tab': 'interfaces',
  727. }
  728. class VLANVMInterfacesView(generic.ObjectChildrenView):
  729. queryset = VLAN.objects.all()
  730. child_model = VMInterface
  731. table = tables.VLANVirtualMachinesTable
  732. filterset = VMInterfaceFilterSet
  733. template_name = 'ipam/vlan/vminterfaces.html'
  734. def get_children(self, request, parent):
  735. return parent.get_vminterfaces().restrict(request.user, 'view')
  736. def get_extra_context(self, request, instance):
  737. return {
  738. 'active_tab': 'vminterfaces',
  739. }
  740. class VLANEditView(generic.ObjectEditView):
  741. queryset = VLAN.objects.all()
  742. model_form = forms.VLANForm
  743. template_name = 'ipam/vlan_edit.html'
  744. class VLANDeleteView(generic.ObjectDeleteView):
  745. queryset = VLAN.objects.all()
  746. class VLANBulkImportView(generic.BulkImportView):
  747. queryset = VLAN.objects.all()
  748. model_form = forms.VLANCSVForm
  749. table = tables.VLANTable
  750. class VLANBulkEditView(generic.BulkEditView):
  751. queryset = VLAN.objects.prefetch_related('site', 'group', 'tenant', 'role')
  752. filterset = filtersets.VLANFilterSet
  753. table = tables.VLANTable
  754. form = forms.VLANBulkEditForm
  755. class VLANBulkDeleteView(generic.BulkDeleteView):
  756. queryset = VLAN.objects.prefetch_related('site', 'group', 'tenant', 'role')
  757. filterset = filtersets.VLANFilterSet
  758. table = tables.VLANTable
  759. #
  760. # Services
  761. #
  762. class ServiceListView(generic.ObjectListView):
  763. queryset = Service.objects.all()
  764. filterset = filtersets.ServiceFilterSet
  765. filterset_form = forms.ServiceFilterForm
  766. table = tables.ServiceTable
  767. class ServiceView(generic.ObjectView):
  768. queryset = Service.objects.prefetch_related('ipaddresses')
  769. class ServiceEditView(generic.ObjectEditView):
  770. queryset = Service.objects.prefetch_related('ipaddresses')
  771. model_form = forms.ServiceForm
  772. template_name = 'ipam/service_edit.html'
  773. class ServiceBulkImportView(generic.BulkImportView):
  774. queryset = Service.objects.all()
  775. model_form = forms.ServiceCSVForm
  776. table = tables.ServiceTable
  777. class ServiceDeleteView(generic.ObjectDeleteView):
  778. queryset = Service.objects.all()
  779. class ServiceBulkEditView(generic.BulkEditView):
  780. queryset = Service.objects.prefetch_related('device', 'virtual_machine')
  781. filterset = filtersets.ServiceFilterSet
  782. table = tables.ServiceTable
  783. form = forms.ServiceBulkEditForm
  784. class ServiceBulkDeleteView(generic.BulkDeleteView):
  785. queryset = Service.objects.prefetch_related('device', 'virtual_machine')
  786. filterset = filtersets.ServiceFilterSet
  787. table = tables.ServiceTable