views.py 32 KB

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