views.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875
  1. import netaddr
  2. from django.conf import settings
  3. from django.db.models import Count, Prefetch
  4. from django.db.models.expressions import RawSQL
  5. from django.shortcuts import get_object_or_404, redirect, render
  6. from django_tables2 import RequestConfig
  7. from dcim.models import Device, Interface
  8. from utilities.paginator import EnhancedPaginator
  9. from utilities.utils import get_subquery
  10. from utilities.views import (
  11. BulkCreateView, BulkDeleteView, BulkEditView, BulkImportView, ObjectView, ObjectDeleteView, ObjectEditView,
  12. ObjectListView,
  13. )
  14. from virtualization.models import VirtualMachine, VMInterface
  15. from . import filters, forms, tables
  16. from .choices import *
  17. from .constants import *
  18. from .models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
  19. from .utils import add_available_ipaddresses, add_available_prefixes, add_available_vlans
  20. #
  21. # VRFs
  22. #
  23. class VRFListView(ObjectListView):
  24. queryset = VRF.objects.prefetch_related('tenant')
  25. filterset = filters.VRFFilterSet
  26. filterset_form = forms.VRFFilterForm
  27. table = tables.VRFTable
  28. class VRFView(ObjectView):
  29. queryset = VRF.objects.all()
  30. def get(self, request, pk):
  31. vrf = get_object_or_404(self.queryset, pk=pk)
  32. prefix_count = Prefix.objects.restrict(request.user, 'view').filter(vrf=vrf).count()
  33. return render(request, 'ipam/vrf.html', {
  34. 'vrf': vrf,
  35. 'prefix_count': prefix_count,
  36. })
  37. class VRFEditView(ObjectEditView):
  38. queryset = VRF.objects.all()
  39. model_form = forms.VRFForm
  40. template_name = 'ipam/vrf_edit.html'
  41. class VRFDeleteView(ObjectDeleteView):
  42. queryset = VRF.objects.all()
  43. class VRFBulkImportView(BulkImportView):
  44. queryset = VRF.objects.all()
  45. model_form = forms.VRFCSVForm
  46. table = tables.VRFTable
  47. class VRFBulkEditView(BulkEditView):
  48. queryset = VRF.objects.prefetch_related('tenant')
  49. filterset = filters.VRFFilterSet
  50. table = tables.VRFTable
  51. form = forms.VRFBulkEditForm
  52. class VRFBulkDeleteView(BulkDeleteView):
  53. queryset = VRF.objects.prefetch_related('tenant')
  54. filterset = filters.VRFFilterSet
  55. table = tables.VRFTable
  56. #
  57. # RIRs
  58. #
  59. class RIRListView(ObjectListView):
  60. queryset = RIR.objects.annotate(aggregate_count=Count('aggregates')).order_by(*RIR._meta.ordering)
  61. filterset = filters.RIRFilterSet
  62. filterset_form = forms.RIRFilterForm
  63. table = tables.RIRDetailTable
  64. template_name = 'ipam/rir_list.html'
  65. def alter_queryset(self, request):
  66. if request.GET.get('family') == '6':
  67. family = 6
  68. denominator = 2 ** 64 # Count /64s for IPv6 rather than individual IPs
  69. else:
  70. family = 4
  71. denominator = 1
  72. rirs = []
  73. for rir in self.queryset:
  74. stats = {
  75. 'total': 0,
  76. 'active': 0,
  77. 'reserved': 0,
  78. 'deprecated': 0,
  79. 'available': 0,
  80. }
  81. aggregate_list = Aggregate.objects.restrict(request.user).filter(prefix__family=family, rir=rir)
  82. for aggregate in aggregate_list:
  83. queryset = Prefix.objects.restrict(request.user).filter(
  84. prefix__net_contained_or_equal=str(aggregate.prefix)
  85. )
  86. # Find all consumed space for each prefix status (we ignore containers for this purpose).
  87. active_prefixes = netaddr.cidr_merge(
  88. [p.prefix for p in queryset.filter(status=PrefixStatusChoices.STATUS_ACTIVE)]
  89. )
  90. reserved_prefixes = netaddr.cidr_merge(
  91. [p.prefix for p in queryset.filter(status=PrefixStatusChoices.STATUS_RESERVED)]
  92. )
  93. deprecated_prefixes = netaddr.cidr_merge(
  94. [p.prefix for p in queryset.filter(status=PrefixStatusChoices.STATUS_DEPRECATED)]
  95. )
  96. # Find all available prefixes by subtracting each of the existing prefix sets from the aggregate prefix.
  97. available_prefixes = (
  98. netaddr.IPSet([aggregate.prefix]) -
  99. netaddr.IPSet(active_prefixes) -
  100. netaddr.IPSet(reserved_prefixes) -
  101. netaddr.IPSet(deprecated_prefixes)
  102. )
  103. # Add the size of each metric to the RIR total.
  104. stats['total'] += int(aggregate.prefix.size / denominator)
  105. stats['active'] += int(netaddr.IPSet(active_prefixes).size / denominator)
  106. stats['reserved'] += int(netaddr.IPSet(reserved_prefixes).size / denominator)
  107. stats['deprecated'] += int(netaddr.IPSet(deprecated_prefixes).size / denominator)
  108. stats['available'] += int(available_prefixes.size / denominator)
  109. # Calculate the percentage of total space for each prefix status.
  110. total = float(stats['total'])
  111. stats['percentages'] = {
  112. 'active': float('{:.2f}'.format(stats['active'] / total * 100)) if total else 0,
  113. 'reserved': float('{:.2f}'.format(stats['reserved'] / total * 100)) if total else 0,
  114. 'deprecated': float('{:.2f}'.format(stats['deprecated'] / total * 100)) if total else 0,
  115. }
  116. stats['percentages']['available'] = (
  117. 100 -
  118. stats['percentages']['active'] -
  119. stats['percentages']['reserved'] -
  120. stats['percentages']['deprecated']
  121. )
  122. rir.stats = stats
  123. rirs.append(rir)
  124. return rirs
  125. class RIREditView(ObjectEditView):
  126. queryset = RIR.objects.all()
  127. model_form = forms.RIRForm
  128. class RIRDeleteView(ObjectDeleteView):
  129. queryset = RIR.objects.all()
  130. class RIRBulkImportView(BulkImportView):
  131. queryset = RIR.objects.all()
  132. model_form = forms.RIRCSVForm
  133. table = tables.RIRTable
  134. class RIRBulkDeleteView(BulkDeleteView):
  135. queryset = RIR.objects.annotate(aggregate_count=Count('aggregates')).order_by(*RIR._meta.ordering)
  136. filterset = filters.RIRFilterSet
  137. table = tables.RIRTable
  138. #
  139. # Aggregates
  140. #
  141. class AggregateListView(ObjectListView):
  142. queryset = Aggregate.objects.prefetch_related('rir').annotate(
  143. child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
  144. ).order_by(*Aggregate._meta.ordering)
  145. filterset = filters.AggregateFilterSet
  146. filterset_form = forms.AggregateFilterForm
  147. table = tables.AggregateDetailTable
  148. template_name = 'ipam/aggregate_list.html'
  149. def extra_context(self):
  150. ipv4_total = 0
  151. ipv6_total = 0
  152. for aggregate in self.queryset:
  153. if aggregate.prefix.version == 6:
  154. # Report equivalent /64s for IPv6 to keep things sane
  155. ipv6_total += int(aggregate.prefix.size / 2 ** 64)
  156. else:
  157. ipv4_total += aggregate.prefix.size
  158. return {
  159. 'ipv4_total': ipv4_total,
  160. 'ipv6_total': ipv6_total,
  161. }
  162. class AggregateView(ObjectView):
  163. queryset = Aggregate.objects.all()
  164. def get(self, request, pk):
  165. aggregate = get_object_or_404(self.queryset, pk=pk)
  166. # Find all child prefixes contained by this aggregate
  167. child_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
  168. prefix__net_contained_or_equal=str(aggregate.prefix)
  169. ).prefetch_related(
  170. 'site', 'role'
  171. ).annotate_depth(
  172. limit=0
  173. )
  174. # Add available prefixes to the table if requested
  175. if request.GET.get('show_available', 'true') == 'true':
  176. child_prefixes = add_available_prefixes(aggregate.prefix, child_prefixes)
  177. prefix_table = tables.PrefixDetailTable(child_prefixes)
  178. if request.user.has_perm('ipam.change_prefix') or request.user.has_perm('ipam.delete_prefix'):
  179. prefix_table.columns.show('pk')
  180. paginate = {
  181. 'paginator_class': EnhancedPaginator,
  182. 'per_page': request.GET.get('per_page', settings.PAGINATE_COUNT)
  183. }
  184. RequestConfig(request, paginate).configure(prefix_table)
  185. # Compile permissions list for rendering the object table
  186. permissions = {
  187. 'add': request.user.has_perm('ipam.add_prefix'),
  188. 'change': request.user.has_perm('ipam.change_prefix'),
  189. 'delete': request.user.has_perm('ipam.delete_prefix'),
  190. }
  191. return render(request, 'ipam/aggregate.html', {
  192. 'aggregate': aggregate,
  193. 'prefix_table': prefix_table,
  194. 'permissions': permissions,
  195. 'show_available': request.GET.get('show_available', 'true') == 'true',
  196. })
  197. class AggregateEditView(ObjectEditView):
  198. queryset = Aggregate.objects.all()
  199. model_form = forms.AggregateForm
  200. template_name = 'ipam/aggregate_edit.html'
  201. class AggregateDeleteView(ObjectDeleteView):
  202. queryset = Aggregate.objects.all()
  203. class AggregateBulkImportView(BulkImportView):
  204. queryset = Aggregate.objects.all()
  205. model_form = forms.AggregateCSVForm
  206. table = tables.AggregateTable
  207. class AggregateBulkEditView(BulkEditView):
  208. queryset = Aggregate.objects.prefetch_related('rir')
  209. filterset = filters.AggregateFilterSet
  210. table = tables.AggregateTable
  211. form = forms.AggregateBulkEditForm
  212. class AggregateBulkDeleteView(BulkDeleteView):
  213. queryset = Aggregate.objects.prefetch_related('rir')
  214. filterset = filters.AggregateFilterSet
  215. table = tables.AggregateTable
  216. #
  217. # Prefix/VLAN roles
  218. #
  219. class RoleListView(ObjectListView):
  220. queryset = Role.objects.annotate(
  221. prefix_count=get_subquery(Prefix, 'role'),
  222. vlan_count=get_subquery(VLAN, 'role')
  223. )
  224. table = tables.RoleTable
  225. class RoleEditView(ObjectEditView):
  226. queryset = Role.objects.all()
  227. model_form = forms.RoleForm
  228. class RoleDeleteView(ObjectDeleteView):
  229. queryset = Role.objects.all()
  230. class RoleBulkImportView(BulkImportView):
  231. queryset = Role.objects.all()
  232. model_form = forms.RoleCSVForm
  233. table = tables.RoleTable
  234. class RoleBulkDeleteView(BulkDeleteView):
  235. queryset = Role.objects.all()
  236. table = tables.RoleTable
  237. #
  238. # Prefixes
  239. #
  240. class PrefixListView(ObjectListView):
  241. queryset = Prefix.objects.prefetch_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role')
  242. filterset = filters.PrefixFilterSet
  243. filterset_form = forms.PrefixFilterForm
  244. table = tables.PrefixDetailTable
  245. template_name = 'ipam/prefix_list.html'
  246. def alter_queryset(self, request):
  247. # Show only top-level prefixes by default (unless searching)
  248. limit = None if request.GET.get('expand') or request.GET.get('q') else 0
  249. return self.queryset.annotate_depth(limit=limit)
  250. class PrefixView(ObjectView):
  251. queryset = Prefix.objects.prefetch_related('vrf', 'site__region', 'tenant__group', 'vlan__group', 'role')
  252. def get(self, request, pk):
  253. prefix = get_object_or_404(self.queryset, pk=pk)
  254. try:
  255. aggregate = Aggregate.objects.restrict(request.user, 'view').get(
  256. prefix__net_contains_or_equals=str(prefix.prefix)
  257. )
  258. except Aggregate.DoesNotExist:
  259. aggregate = None
  260. # Parent prefixes table
  261. parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
  262. Q(vrf=prefix.vrf) | Q(vrf__isnull=True)
  263. ).filter(
  264. prefix__net_contains=str(prefix.prefix)
  265. ).prefetch_related(
  266. 'site', 'role'
  267. ).annotate_depth()
  268. parent_prefix_table = tables.PrefixTable(list(parent_prefixes), orderable=False)
  269. parent_prefix_table.exclude = ('vrf',)
  270. # Duplicate prefixes table
  271. duplicate_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
  272. vrf=prefix.vrf, prefix=str(prefix.prefix)
  273. ).exclude(
  274. pk=prefix.pk
  275. ).prefetch_related(
  276. 'site', 'role'
  277. )
  278. duplicate_prefix_table = tables.PrefixTable(list(duplicate_prefixes), orderable=False)
  279. duplicate_prefix_table.exclude = ('vrf',)
  280. return render(request, 'ipam/prefix.html', {
  281. 'prefix': prefix,
  282. 'aggregate': aggregate,
  283. 'parent_prefix_table': parent_prefix_table,
  284. 'duplicate_prefix_table': duplicate_prefix_table,
  285. })
  286. class PrefixPrefixesView(ObjectView):
  287. queryset = Prefix.objects.all()
  288. def get(self, request, pk):
  289. prefix = get_object_or_404(self.queryset, pk=pk)
  290. # Child prefixes table
  291. child_prefixes = prefix.get_child_prefixes().restrict(request.user, 'view').prefetch_related(
  292. 'site', 'vlan', 'role',
  293. ).annotate_depth(limit=0)
  294. # Add available prefixes to the table if requested
  295. if child_prefixes and request.GET.get('show_available', 'true') == 'true':
  296. child_prefixes = add_available_prefixes(prefix.prefix, child_prefixes)
  297. prefix_table = tables.PrefixDetailTable(child_prefixes)
  298. if request.user.has_perm('ipam.change_prefix') or request.user.has_perm('ipam.delete_prefix'):
  299. prefix_table.columns.show('pk')
  300. paginate = {
  301. 'paginator_class': EnhancedPaginator,
  302. 'per_page': request.GET.get('per_page', settings.PAGINATE_COUNT)
  303. }
  304. RequestConfig(request, paginate).configure(prefix_table)
  305. # Compile permissions list for rendering the object table
  306. permissions = {
  307. 'add': request.user.has_perm('ipam.add_prefix'),
  308. 'change': request.user.has_perm('ipam.change_prefix'),
  309. 'delete': request.user.has_perm('ipam.delete_prefix'),
  310. }
  311. return render(request, 'ipam/prefix_prefixes.html', {
  312. 'prefix': prefix,
  313. 'first_available_prefix': prefix.get_first_available_prefix(),
  314. 'prefix_table': prefix_table,
  315. 'permissions': permissions,
  316. 'bulk_querystring': 'vrf_id={}&within={}'.format(prefix.vrf.pk if prefix.vrf else '0', prefix.prefix),
  317. 'active_tab': 'prefixes',
  318. 'show_available': request.GET.get('show_available', 'true') == 'true',
  319. })
  320. class PrefixIPAddressesView(ObjectView):
  321. queryset = Prefix.objects.all()
  322. def get(self, request, pk):
  323. prefix = get_object_or_404(self.queryset, pk=pk)
  324. # Find all IPAddresses belonging to this Prefix
  325. ipaddresses = prefix.get_child_ips().restrict(request.user, 'view').prefetch_related(
  326. 'vrf', 'primary_ip4_for', 'primary_ip6_for'
  327. )
  328. # Add available IP addresses to the table if requested
  329. if request.GET.get('show_available', 'true') == 'true':
  330. ipaddresses = add_available_ipaddresses(prefix.prefix, ipaddresses, prefix.is_pool)
  331. ip_table = tables.IPAddressTable(ipaddresses)
  332. if request.user.has_perm('ipam.change_ipaddress') or request.user.has_perm('ipam.delete_ipaddress'):
  333. ip_table.columns.show('pk')
  334. paginate = {
  335. 'paginator_class': EnhancedPaginator,
  336. 'per_page': request.GET.get('per_page', settings.PAGINATE_COUNT)
  337. }
  338. RequestConfig(request, paginate).configure(ip_table)
  339. # Compile permissions list for rendering the object table
  340. permissions = {
  341. 'add': request.user.has_perm('ipam.add_ipaddress'),
  342. 'change': request.user.has_perm('ipam.change_ipaddress'),
  343. 'delete': request.user.has_perm('ipam.delete_ipaddress'),
  344. }
  345. return render(request, 'ipam/prefix_ipaddresses.html', {
  346. 'prefix': prefix,
  347. 'first_available_ip': prefix.get_first_available_ip(),
  348. 'ip_table': ip_table,
  349. 'permissions': permissions,
  350. 'bulk_querystring': 'vrf_id={}&parent={}'.format(prefix.vrf.pk if prefix.vrf else '0', prefix.prefix),
  351. 'active_tab': 'ip-addresses',
  352. 'show_available': request.GET.get('show_available', 'true') == 'true',
  353. })
  354. class PrefixEditView(ObjectEditView):
  355. queryset = Prefix.objects.all()
  356. model_form = forms.PrefixForm
  357. template_name = 'ipam/prefix_edit.html'
  358. class PrefixDeleteView(ObjectDeleteView):
  359. queryset = Prefix.objects.all()
  360. template_name = 'ipam/prefix_delete.html'
  361. class PrefixBulkImportView(BulkImportView):
  362. queryset = Prefix.objects.all()
  363. model_form = forms.PrefixCSVForm
  364. table = tables.PrefixTable
  365. class PrefixBulkEditView(BulkEditView):
  366. queryset = Prefix.objects.prefetch_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role')
  367. filterset = filters.PrefixFilterSet
  368. table = tables.PrefixTable
  369. form = forms.PrefixBulkEditForm
  370. class PrefixBulkDeleteView(BulkDeleteView):
  371. queryset = Prefix.objects.prefetch_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role')
  372. filterset = filters.PrefixFilterSet
  373. table = tables.PrefixTable
  374. #
  375. # IP addresses
  376. #
  377. class IPAddressListView(ObjectListView):
  378. queryset = IPAddress.objects.prefetch_related(
  379. 'vrf__tenant', 'tenant', 'nat_inside'
  380. )
  381. filterset = filters.IPAddressFilterSet
  382. filterset_form = forms.IPAddressFilterForm
  383. table = tables.IPAddressDetailTable
  384. class IPAddressView(ObjectView):
  385. queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant')
  386. def get(self, request, pk):
  387. ipaddress = get_object_or_404(self.queryset, pk=pk)
  388. # Parent prefixes table
  389. parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
  390. vrf=ipaddress.vrf, prefix__net_contains=str(ipaddress.address.ip)
  391. ).prefetch_related(
  392. 'site', 'role'
  393. )
  394. parent_prefixes_table = tables.PrefixTable(list(parent_prefixes), orderable=False)
  395. parent_prefixes_table.exclude = ('vrf',)
  396. # Duplicate IPs table
  397. duplicate_ips = IPAddress.objects.restrict(request.user, 'view').filter(
  398. vrf=ipaddress.vrf, address=str(ipaddress.address)
  399. ).exclude(
  400. pk=ipaddress.pk
  401. ).prefetch_related(
  402. 'nat_inside'
  403. )
  404. # Exclude anycast IPs if this IP is anycast
  405. if ipaddress.role == IPAddressRoleChoices.ROLE_ANYCAST:
  406. duplicate_ips = duplicate_ips.exclude(role=IPAddressRoleChoices.ROLE_ANYCAST)
  407. duplicate_ips_table = tables.IPAddressTable(list(duplicate_ips), orderable=False)
  408. # Related IP table
  409. related_ips = IPAddress.objects.restrict(request.user, 'view').exclude(
  410. address=str(ipaddress.address)
  411. ).filter(
  412. vrf=ipaddress.vrf, address__net_contained_or_equal=str(ipaddress.address)
  413. )
  414. related_ips_table = tables.IPAddressTable(related_ips, orderable=False)
  415. paginate = {
  416. 'paginator_class': EnhancedPaginator,
  417. 'per_page': request.GET.get('per_page', settings.PAGINATE_COUNT)
  418. }
  419. RequestConfig(request, paginate).configure(related_ips_table)
  420. return render(request, 'ipam/ipaddress.html', {
  421. 'ipaddress': ipaddress,
  422. 'parent_prefixes_table': parent_prefixes_table,
  423. 'duplicate_ips_table': duplicate_ips_table,
  424. 'related_ips_table': related_ips_table,
  425. })
  426. class IPAddressEditView(ObjectEditView):
  427. queryset = IPAddress.objects.all()
  428. model_form = forms.IPAddressForm
  429. template_name = 'ipam/ipaddress_edit.html'
  430. def alter_obj(self, obj, request, url_args, url_kwargs):
  431. if 'interface' in request.GET:
  432. try:
  433. obj.assigned_object = Interface.objects.get(pk=request.GET['interface'])
  434. except (ValueError, Interface.DoesNotExist):
  435. pass
  436. elif 'vminterface' in request.GET:
  437. try:
  438. obj.assigned_object = VMInterface.objects.get(pk=request.GET['vminterface'])
  439. except (ValueError, VMInterface.DoesNotExist):
  440. pass
  441. return obj
  442. class IPAddressAssignView(ObjectView):
  443. """
  444. Search for IPAddresses to be assigned to an Interface.
  445. """
  446. queryset = IPAddress.objects.all()
  447. def dispatch(self, request, *args, **kwargs):
  448. # Redirect user if an interface has not been provided
  449. if 'interface' not in request.GET:
  450. return redirect('ipam:ipaddress_add')
  451. return super().dispatch(request, *args, **kwargs)
  452. def get(self, request):
  453. form = forms.IPAddressAssignForm()
  454. return render(request, 'ipam/ipaddress_assign.html', {
  455. 'form': form,
  456. 'return_url': request.GET.get('return_url', ''),
  457. })
  458. def post(self, request):
  459. form = forms.IPAddressAssignForm(request.POST)
  460. table = None
  461. if form.is_valid():
  462. addresses = self.queryset.prefetch_related('vrf', 'tenant')
  463. # Limit to 100 results
  464. addresses = filters.IPAddressFilterSet(request.POST, addresses).qs[:100]
  465. table = tables.IPAddressAssignTable(addresses)
  466. return render(request, 'ipam/ipaddress_assign.html', {
  467. 'form': form,
  468. 'table': table,
  469. 'return_url': request.GET.get('return_url', ''),
  470. })
  471. class IPAddressDeleteView(ObjectDeleteView):
  472. queryset = IPAddress.objects.all()
  473. class IPAddressBulkCreateView(BulkCreateView):
  474. queryset = IPAddress.objects.all()
  475. form = forms.IPAddressBulkCreateForm
  476. model_form = forms.IPAddressBulkAddForm
  477. pattern_target = 'address'
  478. template_name = 'ipam/ipaddress_bulk_add.html'
  479. class IPAddressBulkImportView(BulkImportView):
  480. queryset = IPAddress.objects.all()
  481. model_form = forms.IPAddressCSVForm
  482. table = tables.IPAddressTable
  483. class IPAddressBulkEditView(BulkEditView):
  484. queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant')
  485. filterset = filters.IPAddressFilterSet
  486. table = tables.IPAddressTable
  487. form = forms.IPAddressBulkEditForm
  488. class IPAddressBulkDeleteView(BulkDeleteView):
  489. queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant')
  490. filterset = filters.IPAddressFilterSet
  491. table = tables.IPAddressTable
  492. #
  493. # VLAN groups
  494. #
  495. class VLANGroupListView(ObjectListView):
  496. queryset = VLANGroup.objects.prefetch_related('site').annotate(
  497. vlan_count=Count('vlans')
  498. ).order_by(*VLANGroup._meta.ordering)
  499. filterset = filters.VLANGroupFilterSet
  500. filterset_form = forms.VLANGroupFilterForm
  501. table = tables.VLANGroupTable
  502. class VLANGroupEditView(ObjectEditView):
  503. queryset = VLANGroup.objects.all()
  504. model_form = forms.VLANGroupForm
  505. class VLANGroupDeleteView(ObjectDeleteView):
  506. queryset = VLANGroup.objects.all()
  507. class VLANGroupBulkImportView(BulkImportView):
  508. queryset = VLANGroup.objects.all()
  509. model_form = forms.VLANGroupCSVForm
  510. table = tables.VLANGroupTable
  511. class VLANGroupBulkDeleteView(BulkDeleteView):
  512. queryset = VLANGroup.objects.prefetch_related('site').annotate(
  513. vlan_count=Count('vlans')
  514. ).order_by(*VLANGroup._meta.ordering)
  515. filterset = filters.VLANGroupFilterSet
  516. table = tables.VLANGroupTable
  517. class VLANGroupVLANsView(ObjectView):
  518. queryset = VLANGroup.objects.all()
  519. def get(self, request, pk):
  520. vlan_group = get_object_or_404(self.queryset, pk=pk)
  521. vlans = VLAN.objects.restrict(request.user, 'view').filter(group_id=pk).prefetch_related(
  522. Prefetch('prefixes', queryset=Prefix.objects.restrict(request.user))
  523. )
  524. vlans = add_available_vlans(vlan_group, vlans)
  525. vlan_table = tables.VLANDetailTable(vlans)
  526. if request.user.has_perm('ipam.change_vlan') or request.user.has_perm('ipam.delete_vlan'):
  527. vlan_table.columns.show('pk')
  528. vlan_table.columns.hide('site')
  529. vlan_table.columns.hide('group')
  530. paginate = {
  531. 'paginator_class': EnhancedPaginator,
  532. 'per_page': request.GET.get('per_page', settings.PAGINATE_COUNT)
  533. }
  534. RequestConfig(request, paginate).configure(vlan_table)
  535. # Compile permissions list for rendering the object table
  536. permissions = {
  537. 'add': request.user.has_perm('ipam.add_vlan'),
  538. 'change': request.user.has_perm('ipam.change_vlan'),
  539. 'delete': request.user.has_perm('ipam.delete_vlan'),
  540. }
  541. return render(request, 'ipam/vlangroup_vlans.html', {
  542. 'vlan_group': vlan_group,
  543. 'first_available_vlan': vlan_group.get_next_available_vid(),
  544. 'vlan_table': vlan_table,
  545. 'permissions': permissions,
  546. })
  547. #
  548. # VLANs
  549. #
  550. class VLANListView(ObjectListView):
  551. queryset = VLAN.objects.prefetch_related(
  552. 'site', 'group', 'tenant', 'role', 'prefixes'
  553. )
  554. filterset = filters.VLANFilterSet
  555. filterset_form = forms.VLANFilterForm
  556. table = tables.VLANDetailTable
  557. class VLANView(ObjectView):
  558. queryset = VLAN.objects.prefetch_related('site__region', 'tenant__group', 'role')
  559. def get(self, request, pk):
  560. vlan = get_object_or_404(self.queryset, pk=pk)
  561. prefixes = Prefix.objects.restrict(request.user, 'view').filter(vlan=vlan).prefetch_related(
  562. 'vrf', 'site', 'role'
  563. )
  564. prefix_table = tables.PrefixTable(list(prefixes), orderable=False)
  565. prefix_table.exclude = ('vlan',)
  566. return render(request, 'ipam/vlan.html', {
  567. 'vlan': vlan,
  568. 'prefix_table': prefix_table,
  569. })
  570. class VLANMembersView(ObjectView):
  571. queryset = VLAN.objects.all()
  572. def get(self, request, pk):
  573. vlan = get_object_or_404(self.queryset, pk=pk)
  574. members = vlan.get_members().restrict(request.user, 'view').prefetch_related('device', 'virtual_machine')
  575. members_table = tables.VLANMemberTable(members)
  576. paginate = {
  577. 'paginator_class': EnhancedPaginator,
  578. 'per_page': request.GET.get('per_page', settings.PAGINATE_COUNT)
  579. }
  580. RequestConfig(request, paginate).configure(members_table)
  581. return render(request, 'ipam/vlan_members.html', {
  582. 'vlan': vlan,
  583. 'members_table': members_table,
  584. 'active_tab': 'members',
  585. })
  586. class VLANEditView(ObjectEditView):
  587. queryset = VLAN.objects.all()
  588. model_form = forms.VLANForm
  589. template_name = 'ipam/vlan_edit.html'
  590. class VLANDeleteView(ObjectDeleteView):
  591. queryset = VLAN.objects.all()
  592. class VLANBulkImportView(BulkImportView):
  593. queryset = VLAN.objects.all()
  594. model_form = forms.VLANCSVForm
  595. table = tables.VLANTable
  596. class VLANBulkEditView(BulkEditView):
  597. queryset = VLAN.objects.prefetch_related('site', 'group', 'tenant', 'role')
  598. filterset = filters.VLANFilterSet
  599. table = tables.VLANTable
  600. form = forms.VLANBulkEditForm
  601. class VLANBulkDeleteView(BulkDeleteView):
  602. queryset = VLAN.objects.prefetch_related('site', 'group', 'tenant', 'role')
  603. filterset = filters.VLANFilterSet
  604. table = tables.VLANTable
  605. #
  606. # Services
  607. #
  608. class ServiceListView(ObjectListView):
  609. queryset = Service.objects.prefetch_related('device', 'virtual_machine')
  610. filterset = filters.ServiceFilterSet
  611. filterset_form = forms.ServiceFilterForm
  612. table = tables.ServiceTable
  613. action_buttons = ('export',)
  614. class ServiceView(ObjectView):
  615. queryset = Service.objects.prefetch_related('ipaddresses')
  616. def get(self, request, pk):
  617. service = get_object_or_404(self.queryset, pk=pk)
  618. return render(request, 'ipam/service.html', {
  619. 'service': service,
  620. })
  621. class ServiceEditView(ObjectEditView):
  622. queryset = Service.objects.prefetch_related('ipaddresses')
  623. model_form = forms.ServiceForm
  624. template_name = 'ipam/service_edit.html'
  625. def alter_obj(self, obj, request, url_args, url_kwargs):
  626. if 'device' in url_kwargs:
  627. obj.device = get_object_or_404(
  628. Device.objects.restrict(request.user),
  629. pk=url_kwargs['device']
  630. )
  631. elif 'virtualmachine' in url_kwargs:
  632. obj.virtual_machine = get_object_or_404(
  633. VirtualMachine.objects.restrict(request.user),
  634. pk=url_kwargs['virtualmachine']
  635. )
  636. return obj
  637. def get_return_url(self, request, service):
  638. return service.parent.get_absolute_url()
  639. class ServiceBulkImportView(BulkImportView):
  640. queryset = Service.objects.all()
  641. model_form = forms.ServiceCSVForm
  642. table = tables.ServiceTable
  643. class ServiceDeleteView(ObjectDeleteView):
  644. queryset = Service.objects.all()
  645. class ServiceBulkEditView(BulkEditView):
  646. queryset = Service.objects.prefetch_related('device', 'virtual_machine')
  647. filterset = filters.ServiceFilterSet
  648. table = tables.ServiceTable
  649. form = forms.ServiceBulkEditForm
  650. class ServiceBulkDeleteView(BulkDeleteView):
  651. queryset = Service.objects.prefetch_related('device', 'virtual_machine')
  652. filterset = filters.ServiceFilterSet
  653. table = tables.ServiceTable