views.py 34 KB

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