views.py 29 KB

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