views.py 48 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484
  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_lazy as _
  7. from circuits.models import Provider
  8. from dcim.filtersets import InterfaceFilterSet
  9. from dcim.forms import InterfaceFilterForm
  10. from dcim.models import Interface, Site
  11. from ipam.tables import VLANTranslationRuleTable
  12. from netbox.views import generic
  13. from tenancy.views import ObjectContactsView
  14. from utilities.query import count_related
  15. from utilities.tables import get_table_ordering
  16. from utilities.views import GetRelatedModelsMixin, ViewTab, register_model_view
  17. from virtualization.filtersets import VMInterfaceFilterSet
  18. from virtualization.forms import VMInterfaceFilterForm
  19. from virtualization.models import VMInterface
  20. from . import filtersets, forms, tables
  21. from .choices import PrefixStatusChoices
  22. from .constants import *
  23. from .models import *
  24. from .utils import add_requested_prefixes, add_available_ipaddresses, add_available_vlans
  25. #
  26. # VRFs
  27. #
  28. @register_model_view(VRF, 'list', path='', detail=False)
  29. class VRFListView(generic.ObjectListView):
  30. queryset = VRF.objects.all()
  31. filterset = filtersets.VRFFilterSet
  32. filterset_form = forms.VRFFilterForm
  33. table = tables.VRFTable
  34. @register_model_view(VRF)
  35. class VRFView(GetRelatedModelsMixin, generic.ObjectView):
  36. queryset = VRF.objects.all()
  37. def get_extra_context(self, request, instance):
  38. import_targets_table = tables.RouteTargetTable(
  39. instance.import_targets.all(),
  40. orderable=False
  41. )
  42. export_targets_table = tables.RouteTargetTable(
  43. instance.export_targets.all(),
  44. orderable=False
  45. )
  46. return {
  47. 'related_models': self.get_related_models(request, instance, omit=[Interface, VMInterface]),
  48. 'import_targets_table': import_targets_table,
  49. 'export_targets_table': export_targets_table,
  50. }
  51. @register_model_view(VRF, 'add', detail=False)
  52. @register_model_view(VRF, 'edit')
  53. class VRFEditView(generic.ObjectEditView):
  54. queryset = VRF.objects.all()
  55. form = forms.VRFForm
  56. @register_model_view(VRF, 'delete')
  57. class VRFDeleteView(generic.ObjectDeleteView):
  58. queryset = VRF.objects.all()
  59. @register_model_view(VRF, 'bulk_import', detail=False)
  60. class VRFBulkImportView(generic.BulkImportView):
  61. queryset = VRF.objects.all()
  62. model_form = forms.VRFImportForm
  63. @register_model_view(VRF, 'bulk_edit', path='edit', detail=False)
  64. class VRFBulkEditView(generic.BulkEditView):
  65. queryset = VRF.objects.all()
  66. filterset = filtersets.VRFFilterSet
  67. table = tables.VRFTable
  68. form = forms.VRFBulkEditForm
  69. @register_model_view(VRF, 'bulk_delete', path='delete', detail=False)
  70. class VRFBulkDeleteView(generic.BulkDeleteView):
  71. queryset = VRF.objects.all()
  72. filterset = filtersets.VRFFilterSet
  73. table = tables.VRFTable
  74. #
  75. # Route targets
  76. #
  77. @register_model_view(RouteTarget, 'list', path='', detail=False)
  78. class RouteTargetListView(generic.ObjectListView):
  79. queryset = RouteTarget.objects.all()
  80. filterset = filtersets.RouteTargetFilterSet
  81. filterset_form = forms.RouteTargetFilterForm
  82. table = tables.RouteTargetTable
  83. @register_model_view(RouteTarget)
  84. class RouteTargetView(generic.ObjectView):
  85. queryset = RouteTarget.objects.all()
  86. @register_model_view(RouteTarget, 'add', detail=False)
  87. @register_model_view(RouteTarget, 'edit')
  88. class RouteTargetEditView(generic.ObjectEditView):
  89. queryset = RouteTarget.objects.all()
  90. form = forms.RouteTargetForm
  91. @register_model_view(RouteTarget, 'delete')
  92. class RouteTargetDeleteView(generic.ObjectDeleteView):
  93. queryset = RouteTarget.objects.all()
  94. @register_model_view(RouteTarget, 'bulk_import', detail=False)
  95. class RouteTargetBulkImportView(generic.BulkImportView):
  96. queryset = RouteTarget.objects.all()
  97. model_form = forms.RouteTargetImportForm
  98. @register_model_view(RouteTarget, 'bulk_edit', path='edit', detail=False)
  99. class RouteTargetBulkEditView(generic.BulkEditView):
  100. queryset = RouteTarget.objects.all()
  101. filterset = filtersets.RouteTargetFilterSet
  102. table = tables.RouteTargetTable
  103. form = forms.RouteTargetBulkEditForm
  104. @register_model_view(RouteTarget, 'bulk_delete', path='delete', detail=False)
  105. class RouteTargetBulkDeleteView(generic.BulkDeleteView):
  106. queryset = RouteTarget.objects.all()
  107. filterset = filtersets.RouteTargetFilterSet
  108. table = tables.RouteTargetTable
  109. #
  110. # RIRs
  111. #
  112. @register_model_view(RIR, 'list', path='', detail=False)
  113. class RIRListView(generic.ObjectListView):
  114. queryset = RIR.objects.annotate(
  115. aggregate_count=count_related(Aggregate, 'rir')
  116. )
  117. filterset = filtersets.RIRFilterSet
  118. filterset_form = forms.RIRFilterForm
  119. table = tables.RIRTable
  120. @register_model_view(RIR)
  121. class RIRView(GetRelatedModelsMixin, generic.ObjectView):
  122. queryset = RIR.objects.all()
  123. def get_extra_context(self, request, instance):
  124. return {
  125. 'related_models': self.get_related_models(request, instance),
  126. }
  127. @register_model_view(RIR, 'add', detail=False)
  128. @register_model_view(RIR, 'edit')
  129. class RIREditView(generic.ObjectEditView):
  130. queryset = RIR.objects.all()
  131. form = forms.RIRForm
  132. @register_model_view(RIR, 'delete')
  133. class RIRDeleteView(generic.ObjectDeleteView):
  134. queryset = RIR.objects.all()
  135. @register_model_view(RIR, 'bulk_import', detail=False)
  136. class RIRBulkImportView(generic.BulkImportView):
  137. queryset = RIR.objects.all()
  138. model_form = forms.RIRImportForm
  139. @register_model_view(RIR, 'bulk_edit', path='edit', detail=False)
  140. class RIRBulkEditView(generic.BulkEditView):
  141. queryset = RIR.objects.annotate(
  142. aggregate_count=count_related(Aggregate, 'rir')
  143. )
  144. filterset = filtersets.RIRFilterSet
  145. table = tables.RIRTable
  146. form = forms.RIRBulkEditForm
  147. @register_model_view(RIR, 'bulk_delete', path='delete', detail=False)
  148. class RIRBulkDeleteView(generic.BulkDeleteView):
  149. queryset = RIR.objects.annotate(
  150. aggregate_count=count_related(Aggregate, 'rir')
  151. )
  152. filterset = filtersets.RIRFilterSet
  153. table = tables.RIRTable
  154. #
  155. # ASN ranges
  156. #
  157. @register_model_view(ASNRange, 'list', path='', detail=False)
  158. class ASNRangeListView(generic.ObjectListView):
  159. queryset = ASNRange.objects.annotate_asn_counts()
  160. filterset = filtersets.ASNRangeFilterSet
  161. filterset_form = forms.ASNRangeFilterForm
  162. table = tables.ASNRangeTable
  163. @register_model_view(ASNRange)
  164. class ASNRangeView(generic.ObjectView):
  165. queryset = ASNRange.objects.all()
  166. @register_model_view(ASNRange, 'asns')
  167. class ASNRangeASNsView(generic.ObjectChildrenView):
  168. queryset = ASNRange.objects.all()
  169. child_model = ASN
  170. table = tables.ASNTable
  171. filterset = filtersets.ASNFilterSet
  172. filterset_form = forms.ASNFilterForm
  173. tab = ViewTab(
  174. label=_('ASNs'),
  175. badge=lambda x: x.get_child_asns().count(),
  176. permission='ipam.view_asn',
  177. weight=500
  178. )
  179. def get_children(self, request, parent):
  180. return parent.get_child_asns().restrict(request.user, 'view').annotate(
  181. site_count=count_related(Site, 'asns'),
  182. provider_count=count_related(Provider, 'asns')
  183. )
  184. @register_model_view(ASNRange, 'add', detail=False)
  185. @register_model_view(ASNRange, 'edit')
  186. class ASNRangeEditView(generic.ObjectEditView):
  187. queryset = ASNRange.objects.all()
  188. form = forms.ASNRangeForm
  189. @register_model_view(ASNRange, 'delete')
  190. class ASNRangeDeleteView(generic.ObjectDeleteView):
  191. queryset = ASNRange.objects.all()
  192. @register_model_view(ASNRange, 'bulk_import', detail=False)
  193. class ASNRangeBulkImportView(generic.BulkImportView):
  194. queryset = ASNRange.objects.all()
  195. model_form = forms.ASNRangeImportForm
  196. @register_model_view(ASNRange, 'bulk_edit', path='edit', detail=False)
  197. class ASNRangeBulkEditView(generic.BulkEditView):
  198. queryset = ASNRange.objects.annotate_asn_counts()
  199. filterset = filtersets.ASNRangeFilterSet
  200. table = tables.ASNRangeTable
  201. form = forms.ASNRangeBulkEditForm
  202. @register_model_view(ASNRange, 'bulk_delete', path='delete', detail=False)
  203. class ASNRangeBulkDeleteView(generic.BulkDeleteView):
  204. queryset = ASNRange.objects.annotate_asn_counts()
  205. filterset = filtersets.ASNRangeFilterSet
  206. table = tables.ASNRangeTable
  207. #
  208. # ASNs
  209. #
  210. @register_model_view(ASN, 'list', path='', detail=False)
  211. class ASNListView(generic.ObjectListView):
  212. queryset = ASN.objects.annotate(
  213. site_count=count_related(Site, 'asns'),
  214. provider_count=count_related(Provider, 'asns')
  215. )
  216. filterset = filtersets.ASNFilterSet
  217. filterset_form = forms.ASNFilterForm
  218. table = tables.ASNTable
  219. @register_model_view(ASN)
  220. class ASNView(GetRelatedModelsMixin, generic.ObjectView):
  221. queryset = ASN.objects.all()
  222. def get_extra_context(self, request, instance):
  223. return {
  224. 'related_models': self.get_related_models(
  225. request,
  226. instance,
  227. extra=(
  228. (Site.objects.restrict(request.user, 'view').filter(asns__in=[instance]), 'asn_id'),
  229. (Provider.objects.restrict(request.user, 'view').filter(asns__in=[instance]), 'asn_id'),
  230. ),
  231. ),
  232. }
  233. @register_model_view(ASN, 'add', detail=False)
  234. @register_model_view(ASN, 'edit')
  235. class ASNEditView(generic.ObjectEditView):
  236. queryset = ASN.objects.all()
  237. form = forms.ASNForm
  238. @register_model_view(ASN, 'delete')
  239. class ASNDeleteView(generic.ObjectDeleteView):
  240. queryset = ASN.objects.all()
  241. @register_model_view(ASN, 'bulk_import', detail=False)
  242. class ASNBulkImportView(generic.BulkImportView):
  243. queryset = ASN.objects.all()
  244. model_form = forms.ASNImportForm
  245. @register_model_view(ASN, 'bulk_edit', path='edit', detail=False)
  246. class ASNBulkEditView(generic.BulkEditView):
  247. queryset = ASN.objects.annotate(
  248. site_count=count_related(Site, 'asns')
  249. )
  250. filterset = filtersets.ASNFilterSet
  251. table = tables.ASNTable
  252. form = forms.ASNBulkEditForm
  253. @register_model_view(ASN, 'bulk_delete', path='delete', detail=False)
  254. class ASNBulkDeleteView(generic.BulkDeleteView):
  255. queryset = ASN.objects.annotate(
  256. site_count=count_related(Site, 'asns')
  257. )
  258. filterset = filtersets.ASNFilterSet
  259. table = tables.ASNTable
  260. #
  261. # Aggregates
  262. #
  263. @register_model_view(Aggregate, 'list', path='', detail=False)
  264. class AggregateListView(generic.ObjectListView):
  265. queryset = Aggregate.objects.annotate(
  266. child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
  267. )
  268. filterset = filtersets.AggregateFilterSet
  269. filterset_form = forms.AggregateFilterForm
  270. table = tables.AggregateTable
  271. @register_model_view(Aggregate)
  272. class AggregateView(generic.ObjectView):
  273. queryset = Aggregate.objects.all()
  274. @register_model_view(Aggregate, 'prefixes')
  275. class AggregatePrefixesView(generic.ObjectChildrenView):
  276. queryset = Aggregate.objects.all()
  277. child_model = Prefix
  278. table = tables.PrefixTable
  279. filterset = filtersets.PrefixFilterSet
  280. filterset_form = forms.PrefixFilterForm
  281. template_name = 'ipam/aggregate/prefixes.html'
  282. tab = ViewTab(
  283. label=_('Prefixes'),
  284. badge=lambda x: x.get_child_prefixes().count(),
  285. permission='ipam.view_prefix',
  286. weight=500
  287. )
  288. def get_children(self, request, parent):
  289. return Prefix.objects.restrict(request.user, 'view').filter(
  290. prefix__net_contained_or_equal=str(parent.prefix)
  291. ).prefetch_related('scope', 'role', 'tenant', 'tenant__group', 'vlan')
  292. def prep_table_data(self, request, queryset, parent):
  293. # Determine whether to show assigned prefixes, available prefixes, or both
  294. show_available = bool(request.GET.get('show_available', 'true') == 'true')
  295. show_assigned = bool(request.GET.get('show_assigned', 'true') == 'true')
  296. return add_requested_prefixes(parent.prefix, queryset, show_available, show_assigned)
  297. def get_extra_context(self, request, instance):
  298. return {
  299. 'bulk_querystring': f'within={instance.prefix}',
  300. 'first_available_prefix': instance.get_first_available_prefix(),
  301. 'show_available': bool(request.GET.get('show_available', 'true') == 'true'),
  302. 'show_assigned': bool(request.GET.get('show_assigned', 'true') == 'true'),
  303. }
  304. @register_model_view(Aggregate, 'add', detail=False)
  305. @register_model_view(Aggregate, 'edit')
  306. class AggregateEditView(generic.ObjectEditView):
  307. queryset = Aggregate.objects.all()
  308. form = forms.AggregateForm
  309. @register_model_view(Aggregate, 'delete')
  310. class AggregateDeleteView(generic.ObjectDeleteView):
  311. queryset = Aggregate.objects.all()
  312. @register_model_view(Aggregate, 'bulk_import', detail=False)
  313. class AggregateBulkImportView(generic.BulkImportView):
  314. queryset = Aggregate.objects.all()
  315. model_form = forms.AggregateImportForm
  316. @register_model_view(Aggregate, 'bulk_edit', path='edit', detail=False)
  317. class AggregateBulkEditView(generic.BulkEditView):
  318. queryset = Aggregate.objects.annotate(
  319. child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
  320. )
  321. filterset = filtersets.AggregateFilterSet
  322. table = tables.AggregateTable
  323. form = forms.AggregateBulkEditForm
  324. @register_model_view(Aggregate, 'bulk_delete', path='delete', detail=False)
  325. class AggregateBulkDeleteView(generic.BulkDeleteView):
  326. queryset = Aggregate.objects.annotate(
  327. child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
  328. )
  329. filterset = filtersets.AggregateFilterSet
  330. table = tables.AggregateTable
  331. @register_model_view(Aggregate, 'contacts')
  332. class AggregateContactsView(ObjectContactsView):
  333. queryset = Aggregate.objects.all()
  334. #
  335. # Prefix/VLAN roles
  336. #
  337. @register_model_view(Role, 'list', path='', detail=False)
  338. class RoleListView(generic.ObjectListView):
  339. queryset = Role.objects.annotate(
  340. prefix_count=count_related(Prefix, 'role'),
  341. iprange_count=count_related(IPRange, 'role'),
  342. vlan_count=count_related(VLAN, 'role')
  343. )
  344. filterset = filtersets.RoleFilterSet
  345. filterset_form = forms.RoleFilterForm
  346. table = tables.RoleTable
  347. @register_model_view(Role)
  348. class RoleView(GetRelatedModelsMixin, generic.ObjectView):
  349. queryset = Role.objects.all()
  350. def get_extra_context(self, request, instance):
  351. return {
  352. 'related_models': self.get_related_models(request, instance),
  353. }
  354. @register_model_view(Role, 'add', detail=False)
  355. @register_model_view(Role, 'edit')
  356. class RoleEditView(generic.ObjectEditView):
  357. queryset = Role.objects.all()
  358. form = forms.RoleForm
  359. @register_model_view(Role, 'delete')
  360. class RoleDeleteView(generic.ObjectDeleteView):
  361. queryset = Role.objects.all()
  362. @register_model_view(Role, 'bulk_import', detail=False)
  363. class RoleBulkImportView(generic.BulkImportView):
  364. queryset = Role.objects.all()
  365. model_form = forms.RoleImportForm
  366. @register_model_view(Role, 'bulk_edit', path='edit', detail=False)
  367. class RoleBulkEditView(generic.BulkEditView):
  368. queryset = Role.objects.all()
  369. filterset = filtersets.RoleFilterSet
  370. table = tables.RoleTable
  371. form = forms.RoleBulkEditForm
  372. @register_model_view(Role, 'bulk_delete', path='delete', detail=False)
  373. class RoleBulkDeleteView(generic.BulkDeleteView):
  374. queryset = Role.objects.all()
  375. filterset = filtersets.RoleFilterSet
  376. table = tables.RoleTable
  377. #
  378. # Prefixes
  379. #
  380. @register_model_view(Prefix, 'list', path='', detail=False)
  381. class PrefixListView(generic.ObjectListView):
  382. queryset = Prefix.objects.all()
  383. filterset = filtersets.PrefixFilterSet
  384. filterset_form = forms.PrefixFilterForm
  385. table = tables.PrefixTable
  386. template_name = 'ipam/prefix_list.html'
  387. @register_model_view(Prefix)
  388. class PrefixView(generic.ObjectView):
  389. queryset = Prefix.objects.all()
  390. def get_extra_context(self, request, instance):
  391. try:
  392. aggregate = Aggregate.objects.restrict(request.user, 'view').get(
  393. prefix__net_contains_or_equals=str(instance.prefix)
  394. )
  395. except Aggregate.DoesNotExist:
  396. aggregate = None
  397. # Parent prefixes table
  398. parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
  399. Q(vrf=instance.vrf) | Q(vrf__isnull=True, status=PrefixStatusChoices.STATUS_CONTAINER)
  400. ).filter(
  401. prefix__net_contains=str(instance.prefix)
  402. ).prefetch_related(
  403. 'scope', 'role', 'tenant', 'vlan',
  404. )
  405. parent_prefix_table = tables.PrefixTable(
  406. list(parent_prefixes),
  407. exclude=('vrf', 'utilization'),
  408. orderable=False
  409. )
  410. # Duplicate prefixes table
  411. duplicate_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
  412. vrf=instance.vrf, prefix=str(instance.prefix)
  413. ).exclude(
  414. pk=instance.pk
  415. ).prefetch_related(
  416. 'scope', 'role', 'tenant', 'vlan',
  417. )
  418. duplicate_prefix_table = tables.PrefixTable(
  419. list(duplicate_prefixes),
  420. exclude=('vrf', 'utilization'),
  421. orderable=False
  422. )
  423. return {
  424. 'aggregate': aggregate,
  425. 'parent_prefix_table': parent_prefix_table,
  426. 'duplicate_prefix_table': duplicate_prefix_table,
  427. }
  428. @register_model_view(Prefix, 'prefixes')
  429. class PrefixPrefixesView(generic.ObjectChildrenView):
  430. queryset = Prefix.objects.all()
  431. child_model = Prefix
  432. table = tables.PrefixTable
  433. filterset = filtersets.PrefixFilterSet
  434. filterset_form = forms.PrefixFilterForm
  435. template_name = 'ipam/prefix/prefixes.html'
  436. tab = ViewTab(
  437. label=_('Child Prefixes'),
  438. badge=lambda x: x.get_child_prefixes().count(),
  439. permission='ipam.view_prefix',
  440. weight=500
  441. )
  442. def get_children(self, request, parent):
  443. return parent.get_child_prefixes().restrict(request.user, 'view').prefetch_related(
  444. 'scope', 'vrf', 'vlan', 'role', 'tenant', 'tenant__group'
  445. )
  446. def prep_table_data(self, request, queryset, parent):
  447. # Determine whether to show assigned prefixes, available prefixes, or both
  448. show_available = bool(request.GET.get('show_available', 'true') == 'true')
  449. show_assigned = bool(request.GET.get('show_assigned', 'true') == 'true')
  450. return add_requested_prefixes(parent.prefix, queryset, show_available, show_assigned)
  451. def get_extra_context(self, request, instance):
  452. return {
  453. 'bulk_querystring': f"vrf_id={instance.vrf.pk if instance.vrf else '0'}&within={instance.prefix}",
  454. 'first_available_prefix': instance.get_first_available_prefix(),
  455. 'show_available': bool(request.GET.get('show_available', 'true') == 'true'),
  456. 'show_assigned': bool(request.GET.get('show_assigned', 'true') == 'true'),
  457. }
  458. @register_model_view(Prefix, 'ipranges', path='ip-ranges')
  459. class PrefixIPRangesView(generic.ObjectChildrenView):
  460. queryset = Prefix.objects.all()
  461. child_model = IPRange
  462. table = tables.IPRangeTable
  463. filterset = filtersets.IPRangeFilterSet
  464. filterset_form = forms.IPRangeFilterForm
  465. template_name = 'ipam/prefix/ip_ranges.html'
  466. tab = ViewTab(
  467. label=_('Child Ranges'),
  468. badge=lambda x: x.get_child_ranges().count(),
  469. permission='ipam.view_iprange',
  470. weight=600
  471. )
  472. def get_children(self, request, parent):
  473. return parent.get_child_ranges().restrict(request.user, 'view').prefetch_related(
  474. 'tenant__group',
  475. )
  476. def get_extra_context(self, request, instance):
  477. return {
  478. 'bulk_querystring': f"vrf_id={instance.vrf.pk if instance.vrf else '0'}&parent={instance.prefix}",
  479. 'first_available_ip': instance.get_first_available_ip(),
  480. }
  481. @register_model_view(Prefix, 'ipaddresses', path='ip-addresses')
  482. class PrefixIPAddressesView(generic.ObjectChildrenView):
  483. queryset = Prefix.objects.all()
  484. child_model = IPAddress
  485. table = tables.IPAddressTable
  486. filterset = filtersets.IPAddressFilterSet
  487. filterset_form = forms.IPAddressFilterForm
  488. template_name = 'ipam/prefix/ip_addresses.html'
  489. tab = ViewTab(
  490. label=_('IP Addresses'),
  491. badge=lambda x: x.get_child_ips().count(),
  492. permission='ipam.view_ipaddress',
  493. weight=700
  494. )
  495. def get_children(self, request, parent):
  496. return parent.get_child_ips().restrict(request.user, 'view').prefetch_related('vrf', 'tenant', 'tenant__group')
  497. def prep_table_data(self, request, queryset, parent):
  498. if not request.GET.get('q') and not get_table_ordering(request, self.table):
  499. return add_available_ipaddresses(parent.prefix, queryset, parent.is_pool)
  500. return queryset
  501. def get_extra_context(self, request, instance):
  502. return {
  503. 'bulk_querystring': f"vrf_id={instance.vrf.pk if instance.vrf else '0'}&parent={instance.prefix}",
  504. 'first_available_ip': instance.get_first_available_ip(),
  505. }
  506. @register_model_view(Prefix, 'add', detail=False)
  507. @register_model_view(Prefix, 'edit')
  508. class PrefixEditView(generic.ObjectEditView):
  509. queryset = Prefix.objects.all()
  510. form = forms.PrefixForm
  511. @register_model_view(Prefix, 'delete')
  512. class PrefixDeleteView(generic.ObjectDeleteView):
  513. queryset = Prefix.objects.all()
  514. @register_model_view(Prefix, 'bulk_import', detail=False)
  515. class PrefixBulkImportView(generic.BulkImportView):
  516. queryset = Prefix.objects.all()
  517. model_form = forms.PrefixImportForm
  518. @register_model_view(Prefix, 'bulk_edit', path='edit', detail=False)
  519. class PrefixBulkEditView(generic.BulkEditView):
  520. queryset = Prefix.objects.prefetch_related('vrf__tenant')
  521. filterset = filtersets.PrefixFilterSet
  522. table = tables.PrefixTable
  523. form = forms.PrefixBulkEditForm
  524. @register_model_view(Prefix, 'bulk_delete', path='delete', detail=False)
  525. class PrefixBulkDeleteView(generic.BulkDeleteView):
  526. queryset = Prefix.objects.prefetch_related('vrf__tenant')
  527. filterset = filtersets.PrefixFilterSet
  528. table = tables.PrefixTable
  529. @register_model_view(Prefix, 'contacts')
  530. class PrefixContactsView(ObjectContactsView):
  531. queryset = Prefix.objects.all()
  532. #
  533. # IP Ranges
  534. #
  535. @register_model_view(IPRange, 'list', path='', detail=False)
  536. class IPRangeListView(generic.ObjectListView):
  537. queryset = IPRange.objects.all()
  538. filterset = filtersets.IPRangeFilterSet
  539. filterset_form = forms.IPRangeFilterForm
  540. table = tables.IPRangeTable
  541. @register_model_view(IPRange)
  542. class IPRangeView(generic.ObjectView):
  543. queryset = IPRange.objects.all()
  544. def get_extra_context(self, request, instance):
  545. # Parent prefixes table
  546. parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
  547. Q(prefix__net_contains_or_equals=str(instance.start_address.ip)),
  548. Q(prefix__net_contains_or_equals=str(instance.end_address.ip)),
  549. vrf=instance.vrf
  550. ).prefetch_related(
  551. 'scope', 'role', 'tenant', 'vlan', 'role'
  552. )
  553. parent_prefixes_table = tables.PrefixTable(
  554. list(parent_prefixes),
  555. exclude=('vrf', 'utilization'),
  556. orderable=False
  557. )
  558. return {
  559. 'parent_prefixes_table': parent_prefixes_table,
  560. }
  561. @register_model_view(IPRange, 'ipaddresses', path='ip-addresses')
  562. class IPRangeIPAddressesView(generic.ObjectChildrenView):
  563. queryset = IPRange.objects.all()
  564. child_model = IPAddress
  565. table = tables.IPAddressTable
  566. filterset = filtersets.IPAddressFilterSet
  567. filterset_form = forms.IPRangeFilterForm
  568. template_name = 'ipam/iprange/ip_addresses.html'
  569. tab = ViewTab(
  570. label=_('IP Addresses'),
  571. badge=lambda x: x.get_child_ips().count(),
  572. permission='ipam.view_ipaddress',
  573. weight=500
  574. )
  575. def get_children(self, request, parent):
  576. return parent.get_child_ips().restrict(request.user, 'view')
  577. @register_model_view(IPRange, 'add', detail=False)
  578. @register_model_view(IPRange, 'edit')
  579. class IPRangeEditView(generic.ObjectEditView):
  580. queryset = IPRange.objects.all()
  581. form = forms.IPRangeForm
  582. @register_model_view(IPRange, 'delete')
  583. class IPRangeDeleteView(generic.ObjectDeleteView):
  584. queryset = IPRange.objects.all()
  585. @register_model_view(IPRange, 'bulk_import', detail=False)
  586. class IPRangeBulkImportView(generic.BulkImportView):
  587. queryset = IPRange.objects.all()
  588. model_form = forms.IPRangeImportForm
  589. @register_model_view(IPRange, 'bulk_edit', path='edit', detail=False)
  590. class IPRangeBulkEditView(generic.BulkEditView):
  591. queryset = IPRange.objects.all()
  592. filterset = filtersets.IPRangeFilterSet
  593. table = tables.IPRangeTable
  594. form = forms.IPRangeBulkEditForm
  595. @register_model_view(IPRange, 'bulk_delete', path='delete', detail=False)
  596. class IPRangeBulkDeleteView(generic.BulkDeleteView):
  597. queryset = IPRange.objects.all()
  598. filterset = filtersets.IPRangeFilterSet
  599. table = tables.IPRangeTable
  600. @register_model_view(IPRange, 'contacts')
  601. class IPRangeContactsView(ObjectContactsView):
  602. queryset = IPRange.objects.all()
  603. #
  604. # IP addresses
  605. #
  606. @register_model_view(IPAddress, 'list', path='', detail=False)
  607. class IPAddressListView(generic.ObjectListView):
  608. queryset = IPAddress.objects.all()
  609. filterset = filtersets.IPAddressFilterSet
  610. filterset_form = forms.IPAddressFilterForm
  611. table = tables.IPAddressTable
  612. @register_model_view(IPAddress)
  613. class IPAddressView(generic.ObjectView):
  614. queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant')
  615. def get_extra_context(self, request, instance):
  616. # Parent prefixes table
  617. parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
  618. vrf=instance.vrf,
  619. prefix__net_contains_or_equals=str(instance.address.ip)
  620. ).prefetch_related(
  621. 'scope', 'role'
  622. )
  623. parent_prefixes_table = tables.PrefixTable(
  624. list(parent_prefixes),
  625. exclude=('vrf', 'utilization'),
  626. orderable=False
  627. )
  628. # Duplicate IPs table
  629. duplicate_ips = IPAddress.objects.restrict(request.user, 'view').filter(
  630. vrf=instance.vrf,
  631. address=str(instance.address)
  632. ).exclude(
  633. pk=instance.pk
  634. ).prefetch_related(
  635. 'nat_inside'
  636. )
  637. # Exclude anycast IPs if this IP is anycast
  638. if instance.role == IPAddressRoleChoices.ROLE_ANYCAST:
  639. duplicate_ips = duplicate_ips.exclude(role=IPAddressRoleChoices.ROLE_ANYCAST)
  640. # Limit to a maximum of 10 duplicates displayed here
  641. duplicate_ips_table = tables.IPAddressTable(duplicate_ips[:10], orderable=False)
  642. return {
  643. 'parent_prefixes_table': parent_prefixes_table,
  644. 'duplicate_ips_table': duplicate_ips_table,
  645. }
  646. @register_model_view(IPAddress, 'add', detail=False)
  647. @register_model_view(IPAddress, 'edit')
  648. class IPAddressEditView(generic.ObjectEditView):
  649. queryset = IPAddress.objects.all()
  650. form = forms.IPAddressForm
  651. template_name = 'ipam/ipaddress_edit.html'
  652. def alter_object(self, obj, request, url_args, url_kwargs):
  653. if 'interface' in request.GET:
  654. try:
  655. obj.assigned_object = Interface.objects.get(pk=request.GET['interface'])
  656. except (ValueError, Interface.DoesNotExist):
  657. pass
  658. elif 'vminterface' in request.GET:
  659. try:
  660. obj.assigned_object = VMInterface.objects.get(pk=request.GET['vminterface'])
  661. except (ValueError, VMInterface.DoesNotExist):
  662. pass
  663. elif 'fhrpgroup' in request.GET:
  664. try:
  665. obj.assigned_object = FHRPGroup.objects.get(pk=request.GET['fhrpgroup'])
  666. except (ValueError, FHRPGroup.DoesNotExist):
  667. pass
  668. return obj
  669. def get_extra_addanother_params(self, request):
  670. if 'interface' in request.GET:
  671. return {'interface': request.GET['interface']}
  672. elif 'vminterface' in request.GET:
  673. return {'vminterface': request.GET['vminterface']}
  674. return {}
  675. # TODO: Standardize or remove this view
  676. @register_model_view(IPAddress, 'assign', path='assign', detail=False)
  677. class IPAddressAssignView(generic.ObjectView):
  678. """
  679. Search for IPAddresses to be assigned to an Interface.
  680. """
  681. queryset = IPAddress.objects.all()
  682. def dispatch(self, request, *args, **kwargs):
  683. # Redirect user if an interface has not been provided
  684. if 'interface' not in request.GET and 'vminterface' not in request.GET:
  685. return redirect('ipam:ipaddress_add')
  686. return super().dispatch(request, *args, **kwargs)
  687. def get(self, request):
  688. form = forms.IPAddressAssignForm()
  689. return render(request, 'ipam/ipaddress_assign.html', {
  690. 'form': form,
  691. 'return_url': request.GET.get('return_url', ''),
  692. })
  693. def post(self, request):
  694. form = forms.IPAddressAssignForm(request.POST)
  695. table = None
  696. if form.is_valid():
  697. addresses = self.queryset.prefetch_related('vrf', 'tenant')
  698. # Limit to 100 results
  699. addresses = filtersets.IPAddressFilterSet(request.POST, addresses).qs[:100]
  700. table = tables.IPAddressAssignTable(addresses)
  701. return render(request, 'ipam/ipaddress_assign.html', {
  702. 'form': form,
  703. 'table': table,
  704. 'return_url': request.GET.get('return_url'),
  705. })
  706. @register_model_view(IPAddress, 'delete')
  707. class IPAddressDeleteView(generic.ObjectDeleteView):
  708. queryset = IPAddress.objects.all()
  709. @register_model_view(IPAddress, 'bulk_add', path='bulk-add', detail=False)
  710. class IPAddressBulkCreateView(generic.BulkCreateView):
  711. queryset = IPAddress.objects.all()
  712. form = forms.IPAddressBulkCreateForm
  713. model_form = forms.IPAddressBulkAddForm
  714. pattern_target = 'address'
  715. template_name = 'ipam/ipaddress_bulk_add.html'
  716. @register_model_view(IPAddress, 'bulk_import', detail=False)
  717. class IPAddressBulkImportView(generic.BulkImportView):
  718. queryset = IPAddress.objects.all()
  719. model_form = forms.IPAddressImportForm
  720. @register_model_view(IPAddress, 'bulk_edit', path='edit', detail=False)
  721. class IPAddressBulkEditView(generic.BulkEditView):
  722. queryset = IPAddress.objects.prefetch_related('vrf__tenant')
  723. filterset = filtersets.IPAddressFilterSet
  724. table = tables.IPAddressTable
  725. form = forms.IPAddressBulkEditForm
  726. @register_model_view(IPAddress, 'bulk_delete', path='delete', detail=False)
  727. class IPAddressBulkDeleteView(generic.BulkDeleteView):
  728. queryset = IPAddress.objects.prefetch_related('vrf__tenant')
  729. filterset = filtersets.IPAddressFilterSet
  730. table = tables.IPAddressTable
  731. @register_model_view(IPAddress, 'related_ips', path='related-ip-addresses')
  732. class IPAddressRelatedIPsView(generic.ObjectChildrenView):
  733. queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant')
  734. child_model = IPAddress
  735. table = tables.IPAddressTable
  736. filterset = filtersets.IPAddressFilterSet
  737. filterset_form = forms.IPAddressFilterForm
  738. tab = ViewTab(
  739. label=_('Related IPs'),
  740. badge=lambda x: x.get_related_ips().count(),
  741. weight=500,
  742. hide_if_empty=True,
  743. )
  744. def get_children(self, request, parent):
  745. return parent.get_related_ips().restrict(request.user, 'view')
  746. @register_model_view(IPAddress, 'contacts')
  747. class IPAddressContactsView(ObjectContactsView):
  748. queryset = IPAddress.objects.all()
  749. #
  750. # VLAN groups
  751. #
  752. @register_model_view(VLANGroup, 'list', path='', detail=False)
  753. class VLANGroupListView(generic.ObjectListView):
  754. queryset = VLANGroup.objects.annotate_utilization()
  755. filterset = filtersets.VLANGroupFilterSet
  756. filterset_form = forms.VLANGroupFilterForm
  757. table = tables.VLANGroupTable
  758. @register_model_view(VLANGroup)
  759. class VLANGroupView(GetRelatedModelsMixin, generic.ObjectView):
  760. queryset = VLANGroup.objects.annotate_utilization()
  761. def get_extra_context(self, request, instance):
  762. return {
  763. 'related_models': self.get_related_models(request, instance),
  764. }
  765. @register_model_view(VLANGroup, 'add', detail=False)
  766. @register_model_view(VLANGroup, 'edit')
  767. class VLANGroupEditView(generic.ObjectEditView):
  768. queryset = VLANGroup.objects.all()
  769. form = forms.VLANGroupForm
  770. @register_model_view(VLANGroup, 'delete')
  771. class VLANGroupDeleteView(generic.ObjectDeleteView):
  772. queryset = VLANGroup.objects.all()
  773. @register_model_view(VLANGroup, 'bulk_import', detail=False)
  774. class VLANGroupBulkImportView(generic.BulkImportView):
  775. queryset = VLANGroup.objects.all()
  776. model_form = forms.VLANGroupImportForm
  777. @register_model_view(VLANGroup, 'bulk_edit', path='edit', detail=False)
  778. class VLANGroupBulkEditView(generic.BulkEditView):
  779. queryset = VLANGroup.objects.annotate_utilization().prefetch_related('tags')
  780. filterset = filtersets.VLANGroupFilterSet
  781. table = tables.VLANGroupTable
  782. form = forms.VLANGroupBulkEditForm
  783. @register_model_view(VLANGroup, 'bulk_delete', path='delete', detail=False)
  784. class VLANGroupBulkDeleteView(generic.BulkDeleteView):
  785. queryset = VLANGroup.objects.annotate_utilization().prefetch_related('tags')
  786. filterset = filtersets.VLANGroupFilterSet
  787. table = tables.VLANGroupTable
  788. @register_model_view(VLANGroup, 'vlans')
  789. class VLANGroupVLANsView(generic.ObjectChildrenView):
  790. queryset = VLANGroup.objects.all()
  791. child_model = VLAN
  792. table = tables.VLANTable
  793. filterset = filtersets.VLANFilterSet
  794. filterset_form = forms.VLANFilterForm
  795. tab = ViewTab(
  796. label=_('VLANs'),
  797. badge=lambda x: x.get_child_vlans().count(),
  798. permission='ipam.view_vlan',
  799. weight=500
  800. )
  801. def get_children(self, request, parent):
  802. return parent.get_child_vlans().restrict(request.user, 'view').prefetch_related(
  803. Prefetch('prefixes', queryset=Prefix.objects.restrict(request.user)),
  804. 'tenant', 'site', 'role',
  805. )
  806. def prep_table_data(self, request, queryset, parent):
  807. if not get_table_ordering(request, self.table):
  808. return add_available_vlans(queryset, parent)
  809. return queryset
  810. #
  811. # VLAN Translation Policies
  812. #
  813. @register_model_view(VLANTranslationPolicy, 'list', path='', detail=False)
  814. class VLANTranslationPolicyListView(generic.ObjectListView):
  815. queryset = VLANTranslationPolicy.objects.annotate(
  816. rule_count=count_related(VLANTranslationRule, 'policy'),
  817. )
  818. filterset = filtersets.VLANTranslationPolicyFilterSet
  819. filterset_form = forms.VLANTranslationPolicyFilterForm
  820. table = tables.VLANTranslationPolicyTable
  821. @register_model_view(VLANTranslationPolicy)
  822. class VLANTranslationPolicyView(GetRelatedModelsMixin, generic.ObjectView):
  823. queryset = VLANTranslationPolicy.objects.all()
  824. def get_extra_context(self, request, instance):
  825. vlan_translation_table = VLANTranslationRuleTable(
  826. data=instance.rules.all(),
  827. orderable=False
  828. )
  829. return {
  830. 'vlan_translation_table': vlan_translation_table,
  831. }
  832. @register_model_view(VLANTranslationPolicy, 'add', detail=False)
  833. @register_model_view(VLANTranslationPolicy, 'edit')
  834. class VLANTranslationPolicyEditView(generic.ObjectEditView):
  835. queryset = VLANTranslationPolicy.objects.all()
  836. form = forms.VLANTranslationPolicyForm
  837. @register_model_view(VLANTranslationPolicy, 'delete')
  838. class VLANTranslationPolicyDeleteView(generic.ObjectDeleteView):
  839. queryset = VLANTranslationPolicy.objects.all()
  840. @register_model_view(VLANTranslationPolicy, 'bulk_import', detail=False)
  841. class VLANTranslationPolicyBulkImportView(generic.BulkImportView):
  842. queryset = VLANTranslationPolicy.objects.all()
  843. model_form = forms.VLANTranslationPolicyImportForm
  844. @register_model_view(VLANTranslationPolicy, 'bulk_edit', path='edit', detail=False)
  845. class VLANTranslationPolicyBulkEditView(generic.BulkEditView):
  846. queryset = VLANTranslationPolicy.objects.all()
  847. filterset = filtersets.VLANTranslationPolicyFilterSet
  848. table = tables.VLANTranslationPolicyTable
  849. form = forms.VLANTranslationPolicyBulkEditForm
  850. @register_model_view(VLANTranslationPolicy, 'bulk_delete', path='delete', detail=False)
  851. class VLANTranslationPolicyBulkDeleteView(generic.BulkDeleteView):
  852. queryset = VLANTranslationPolicy.objects.all()
  853. filterset = filtersets.VLANTranslationPolicyFilterSet
  854. table = tables.VLANTranslationPolicyTable
  855. #
  856. # VLAN Translation Rules
  857. #
  858. @register_model_view(VLANTranslationRule, 'list', path='', detail=False)
  859. class VLANTranslationRuleListView(generic.ObjectListView):
  860. queryset = VLANTranslationRule.objects.all()
  861. filterset = filtersets.VLANTranslationRuleFilterSet
  862. filterset_form = forms.VLANTranslationRuleFilterForm
  863. table = tables.VLANTranslationRuleTable
  864. @register_model_view(VLANTranslationRule)
  865. class VLANTranslationRuleView(GetRelatedModelsMixin, generic.ObjectView):
  866. queryset = VLANTranslationRule.objects.all()
  867. def get_extra_context(self, request, instance):
  868. return {
  869. 'related_models': self.get_related_models(request, instance),
  870. }
  871. @register_model_view(VLANTranslationRule, 'add', detail=False)
  872. @register_model_view(VLANTranslationRule, 'edit')
  873. class VLANTranslationRuleEditView(generic.ObjectEditView):
  874. queryset = VLANTranslationRule.objects.all()
  875. form = forms.VLANTranslationRuleForm
  876. @register_model_view(VLANTranslationRule, 'delete')
  877. class VLANTranslationRuleDeleteView(generic.ObjectDeleteView):
  878. queryset = VLANTranslationRule.objects.all()
  879. @register_model_view(VLANTranslationRule, 'bulk_import', detail=False)
  880. class VLANTranslationRuleBulkImportView(generic.BulkImportView):
  881. queryset = VLANTranslationRule.objects.all()
  882. model_form = forms.VLANTranslationRuleImportForm
  883. @register_model_view(VLANTranslationRule, 'bulk_edit', path='edit', detail=False)
  884. class VLANTranslationRuleBulkEditView(generic.BulkEditView):
  885. queryset = VLANTranslationRule.objects.all()
  886. filterset = filtersets.VLANTranslationRuleFilterSet
  887. table = tables.VLANTranslationRuleTable
  888. form = forms.VLANTranslationRuleBulkEditForm
  889. @register_model_view(VLANTranslationRule, 'bulk_delete', path='delete', detail=False)
  890. class VLANTranslationRuleBulkDeleteView(generic.BulkDeleteView):
  891. queryset = VLANTranslationRule.objects.all()
  892. filterset = filtersets.VLANTranslationRuleFilterSet
  893. table = tables.VLANTranslationRuleTable
  894. #
  895. # FHRP groups
  896. #
  897. @register_model_view(FHRPGroup, 'list', path='', detail=False)
  898. class FHRPGroupListView(generic.ObjectListView):
  899. queryset = FHRPGroup.objects.annotate(
  900. member_count=count_related(FHRPGroupAssignment, 'group')
  901. )
  902. filterset = filtersets.FHRPGroupFilterSet
  903. filterset_form = forms.FHRPGroupFilterForm
  904. table = tables.FHRPGroupTable
  905. @register_model_view(FHRPGroup)
  906. class FHRPGroupView(generic.ObjectView):
  907. queryset = FHRPGroup.objects.all()
  908. def get_extra_context(self, request, instance):
  909. # Get assigned interfaces
  910. members_table = tables.FHRPGroupAssignmentTable(
  911. data=FHRPGroupAssignment.objects.restrict(request.user, 'view').filter(group=instance),
  912. orderable=False
  913. )
  914. members_table.columns.hide('group')
  915. return {
  916. 'members_table': members_table,
  917. 'member_count': FHRPGroupAssignment.objects.filter(group=instance).count(),
  918. }
  919. @register_model_view(FHRPGroup, 'add', detail=False)
  920. @register_model_view(FHRPGroup, 'edit')
  921. class FHRPGroupEditView(generic.ObjectEditView):
  922. queryset = FHRPGroup.objects.all()
  923. form = forms.FHRPGroupForm
  924. def get_return_url(self, request, obj=None):
  925. return_url = super().get_return_url(request, obj)
  926. # If we're redirecting the user to the FHRPGroupAssignment creation form,
  927. # initialize the group field with the FHRPGroup we just saved.
  928. if return_url.startswith(reverse('ipam:fhrpgroupassignment_add')):
  929. return_url += f'&group={obj.pk}'
  930. return return_url
  931. def alter_object(self, obj, request, url_args, url_kwargs):
  932. # Workaround to solve #10719. Capture the current user on the FHRPGroup instance so that
  933. # we can evaluate permissions during the creation of a new IPAddress within the form.
  934. obj._user = request.user
  935. return obj
  936. @register_model_view(FHRPGroup, 'delete')
  937. class FHRPGroupDeleteView(generic.ObjectDeleteView):
  938. queryset = FHRPGroup.objects.all()
  939. @register_model_view(FHRPGroup, 'bulk_import', detail=False)
  940. class FHRPGroupBulkImportView(generic.BulkImportView):
  941. queryset = FHRPGroup.objects.all()
  942. model_form = forms.FHRPGroupImportForm
  943. @register_model_view(FHRPGroup, 'bulk_edit', path='edit', detail=False)
  944. class FHRPGroupBulkEditView(generic.BulkEditView):
  945. queryset = FHRPGroup.objects.all()
  946. filterset = filtersets.FHRPGroupFilterSet
  947. table = tables.FHRPGroupTable
  948. form = forms.FHRPGroupBulkEditForm
  949. @register_model_view(FHRPGroup, 'bulk_delete', path='delete', detail=False)
  950. class FHRPGroupBulkDeleteView(generic.BulkDeleteView):
  951. queryset = FHRPGroup.objects.all()
  952. filterset = filtersets.FHRPGroupFilterSet
  953. table = tables.FHRPGroupTable
  954. #
  955. # FHRP group assignments
  956. #
  957. @register_model_view(FHRPGroupAssignment, 'add', detail=False)
  958. @register_model_view(FHRPGroupAssignment, 'edit')
  959. class FHRPGroupAssignmentEditView(generic.ObjectEditView):
  960. queryset = FHRPGroupAssignment.objects.all()
  961. form = forms.FHRPGroupAssignmentForm
  962. def alter_object(self, instance, request, args, kwargs):
  963. if not instance.pk:
  964. # Assign the interface based on URL kwargs
  965. content_type = get_object_or_404(ContentType, pk=request.GET.get('interface_type'))
  966. instance.interface = get_object_or_404(content_type.model_class(), pk=request.GET.get('interface_id'))
  967. return instance
  968. def get_extra_addanother_params(self, request):
  969. return {
  970. 'interface_type': request.GET.get('interface_type'),
  971. 'interface_id': request.GET.get('interface_id'),
  972. }
  973. @register_model_view(FHRPGroupAssignment, 'delete')
  974. class FHRPGroupAssignmentDeleteView(generic.ObjectDeleteView):
  975. queryset = FHRPGroupAssignment.objects.all()
  976. #
  977. # VLANs
  978. #
  979. @register_model_view(VLAN, 'list', path='', detail=False)
  980. class VLANListView(generic.ObjectListView):
  981. queryset = VLAN.objects.all()
  982. filterset = filtersets.VLANFilterSet
  983. filterset_form = forms.VLANFilterForm
  984. table = tables.VLANTable
  985. @register_model_view(VLAN)
  986. class VLANView(generic.ObjectView):
  987. queryset = VLAN.objects.all()
  988. def get_extra_context(self, request, instance):
  989. prefixes = Prefix.objects.restrict(request.user, 'view').filter(vlan=instance).prefetch_related(
  990. 'vrf', 'scope', 'role', 'tenant'
  991. )
  992. prefix_table = tables.PrefixTable(list(prefixes), exclude=('vlan', 'utilization'), orderable=False)
  993. return {
  994. 'prefix_table': prefix_table,
  995. }
  996. @register_model_view(VLAN, 'interfaces')
  997. class VLANInterfacesView(generic.ObjectChildrenView):
  998. queryset = VLAN.objects.all()
  999. child_model = Interface
  1000. table = tables.VLANDevicesTable
  1001. filterset = InterfaceFilterSet
  1002. filterset_form = InterfaceFilterForm
  1003. tab = ViewTab(
  1004. label=_('Device Interfaces'),
  1005. badge=lambda x: x.get_interfaces().count(),
  1006. permission='dcim.view_interface',
  1007. weight=500
  1008. )
  1009. def get_children(self, request, parent):
  1010. return parent.get_interfaces().restrict(request.user, 'view')
  1011. @register_model_view(VLAN, 'vminterfaces', path='vm-interfaces')
  1012. class VLANVMInterfacesView(generic.ObjectChildrenView):
  1013. queryset = VLAN.objects.all()
  1014. child_model = VMInterface
  1015. table = tables.VLANVirtualMachinesTable
  1016. filterset = VMInterfaceFilterSet
  1017. filterset_form = VMInterfaceFilterForm
  1018. tab = ViewTab(
  1019. label=_('VM Interfaces'),
  1020. badge=lambda x: x.get_vminterfaces().count(),
  1021. permission='virtualization.view_vminterface',
  1022. weight=510
  1023. )
  1024. def get_children(self, request, parent):
  1025. return parent.get_vminterfaces().restrict(request.user, 'view')
  1026. @register_model_view(VLAN, 'add', detail=False)
  1027. @register_model_view(VLAN, 'edit')
  1028. class VLANEditView(generic.ObjectEditView):
  1029. queryset = VLAN.objects.all()
  1030. form = forms.VLANForm
  1031. template_name = 'ipam/vlan_edit.html'
  1032. @register_model_view(VLAN, 'delete')
  1033. class VLANDeleteView(generic.ObjectDeleteView):
  1034. queryset = VLAN.objects.all()
  1035. @register_model_view(VLAN, 'bulk_import', detail=False)
  1036. class VLANBulkImportView(generic.BulkImportView):
  1037. queryset = VLAN.objects.all()
  1038. model_form = forms.VLANImportForm
  1039. @register_model_view(VLAN, 'bulk_edit', path='edit', detail=False)
  1040. class VLANBulkEditView(generic.BulkEditView):
  1041. queryset = VLAN.objects.all()
  1042. filterset = filtersets.VLANFilterSet
  1043. table = tables.VLANTable
  1044. form = forms.VLANBulkEditForm
  1045. @register_model_view(VLAN, 'bulk_delete', path='delete', detail=False)
  1046. class VLANBulkDeleteView(generic.BulkDeleteView):
  1047. queryset = VLAN.objects.all()
  1048. filterset = filtersets.VLANFilterSet
  1049. table = tables.VLANTable
  1050. #
  1051. # Service templates
  1052. #
  1053. @register_model_view(ServiceTemplate, 'list', path='', detail=False)
  1054. class ServiceTemplateListView(generic.ObjectListView):
  1055. queryset = ServiceTemplate.objects.all()
  1056. filterset = filtersets.ServiceTemplateFilterSet
  1057. filterset_form = forms.ServiceTemplateFilterForm
  1058. table = tables.ServiceTemplateTable
  1059. @register_model_view(ServiceTemplate)
  1060. class ServiceTemplateView(generic.ObjectView):
  1061. queryset = ServiceTemplate.objects.all()
  1062. @register_model_view(ServiceTemplate, 'add', detail=False)
  1063. @register_model_view(ServiceTemplate, 'edit')
  1064. class ServiceTemplateEditView(generic.ObjectEditView):
  1065. queryset = ServiceTemplate.objects.all()
  1066. form = forms.ServiceTemplateForm
  1067. @register_model_view(ServiceTemplate, 'delete')
  1068. class ServiceTemplateDeleteView(generic.ObjectDeleteView):
  1069. queryset = ServiceTemplate.objects.all()
  1070. @register_model_view(ServiceTemplate, 'bulk_import', detail=False)
  1071. class ServiceTemplateBulkImportView(generic.BulkImportView):
  1072. queryset = ServiceTemplate.objects.all()
  1073. model_form = forms.ServiceTemplateImportForm
  1074. @register_model_view(ServiceTemplate, 'bulk_edit', path='edit', detail=False)
  1075. class ServiceTemplateBulkEditView(generic.BulkEditView):
  1076. queryset = ServiceTemplate.objects.all()
  1077. filterset = filtersets.ServiceTemplateFilterSet
  1078. table = tables.ServiceTemplateTable
  1079. form = forms.ServiceTemplateBulkEditForm
  1080. @register_model_view(ServiceTemplate, 'bulk_delete', path='delete', detail=False)
  1081. class ServiceTemplateBulkDeleteView(generic.BulkDeleteView):
  1082. queryset = ServiceTemplate.objects.all()
  1083. filterset = filtersets.ServiceTemplateFilterSet
  1084. table = tables.ServiceTemplateTable
  1085. #
  1086. # Services
  1087. #
  1088. @register_model_view(Service, 'list', path='', detail=False)
  1089. class ServiceListView(generic.ObjectListView):
  1090. queryset = Service.objects.prefetch_related('device', 'virtual_machine')
  1091. filterset = filtersets.ServiceFilterSet
  1092. filterset_form = forms.ServiceFilterForm
  1093. table = tables.ServiceTable
  1094. @register_model_view(Service)
  1095. class ServiceView(generic.ObjectView):
  1096. queryset = Service.objects.all()
  1097. @register_model_view(Service, 'add', detail=False)
  1098. class ServiceCreateView(generic.ObjectEditView):
  1099. queryset = Service.objects.all()
  1100. form = forms.ServiceCreateForm
  1101. @register_model_view(Service, 'edit')
  1102. class ServiceEditView(generic.ObjectEditView):
  1103. queryset = Service.objects.all()
  1104. form = forms.ServiceForm
  1105. @register_model_view(Service, 'delete')
  1106. class ServiceDeleteView(generic.ObjectDeleteView):
  1107. queryset = Service.objects.all()
  1108. @register_model_view(Service, 'bulk_import', detail=False)
  1109. class ServiceBulkImportView(generic.BulkImportView):
  1110. queryset = Service.objects.all()
  1111. model_form = forms.ServiceImportForm
  1112. @register_model_view(Service, 'bulk_edit', path='edit', detail=False)
  1113. class ServiceBulkEditView(generic.BulkEditView):
  1114. queryset = Service.objects.prefetch_related('device', 'virtual_machine')
  1115. filterset = filtersets.ServiceFilterSet
  1116. table = tables.ServiceTable
  1117. form = forms.ServiceBulkEditForm
  1118. @register_model_view(Service, 'bulk_delete', path='delete', detail=False)
  1119. class ServiceBulkDeleteView(generic.BulkDeleteView):
  1120. queryset = Service.objects.prefetch_related('device', 'virtual_machine')
  1121. filterset = filtersets.ServiceFilterSet
  1122. table = tables.ServiceTable
  1123. @register_model_view(Service, 'contacts')
  1124. class ServiceContactsView(ObjectContactsView):
  1125. queryset = Service.objects.all()