views.py 39 KB

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