bulk_edit.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  1. from django import forms
  2. from django.contrib.contenttypes.models import ContentType
  3. from django.utils.translation import gettext_lazy as _
  4. from dcim.models import Location, Rack, Region, Site, SiteGroup
  5. from ipam.choices import *
  6. from ipam.constants import *
  7. from ipam.models import *
  8. from ipam.models import ASN
  9. from netbox.forms import NetBoxModelBulkEditForm
  10. from tenancy.models import Tenant
  11. from utilities.forms import add_blank_choice
  12. from utilities.forms.fields import (
  13. CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, NumericArrayField,
  14. )
  15. from utilities.forms.widgets import BulkEditNullBooleanSelect
  16. from virtualization.models import Cluster, ClusterGroup
  17. __all__ = (
  18. 'AggregateBulkEditForm',
  19. 'ASNBulkEditForm',
  20. 'ASNRangeBulkEditForm',
  21. 'FHRPGroupBulkEditForm',
  22. 'IPAddressBulkEditForm',
  23. 'IPRangeBulkEditForm',
  24. 'L2VPNBulkEditForm',
  25. 'L2VPNTerminationBulkEditForm',
  26. 'PrefixBulkEditForm',
  27. 'RIRBulkEditForm',
  28. 'RoleBulkEditForm',
  29. 'RouteTargetBulkEditForm',
  30. 'ServiceBulkEditForm',
  31. 'ServiceTemplateBulkEditForm',
  32. 'VLANBulkEditForm',
  33. 'VLANGroupBulkEditForm',
  34. 'VRFBulkEditForm',
  35. )
  36. class VRFBulkEditForm(NetBoxModelBulkEditForm):
  37. tenant = DynamicModelChoiceField(
  38. label=_('Tenant'),
  39. queryset=Tenant.objects.all(),
  40. required=False
  41. )
  42. enforce_unique = forms.NullBooleanField(
  43. required=False,
  44. widget=BulkEditNullBooleanSelect(),
  45. label=_('Enforce unique space')
  46. )
  47. description = forms.CharField(
  48. label=_('Description'),
  49. max_length=200,
  50. required=False
  51. )
  52. comments = CommentField()
  53. model = VRF
  54. fieldsets = (
  55. (None, ('tenant', 'enforce_unique', 'description')),
  56. )
  57. nullable_fields = ('tenant', 'description', 'comments')
  58. class RouteTargetBulkEditForm(NetBoxModelBulkEditForm):
  59. tenant = DynamicModelChoiceField(
  60. label=_('Tenant'),
  61. queryset=Tenant.objects.all(),
  62. required=False
  63. )
  64. description = forms.CharField(
  65. label=_('Description'),
  66. max_length=200,
  67. required=False
  68. )
  69. comments = CommentField()
  70. model = RouteTarget
  71. fieldsets = (
  72. (None, ('tenant', 'description')),
  73. )
  74. nullable_fields = ('tenant', 'description', 'comments')
  75. class RIRBulkEditForm(NetBoxModelBulkEditForm):
  76. is_private = forms.NullBooleanField(
  77. label=_('Is private'),
  78. required=False,
  79. widget=BulkEditNullBooleanSelect
  80. )
  81. description = forms.CharField(
  82. label=_('Description'),
  83. max_length=200,
  84. required=False
  85. )
  86. model = RIR
  87. fieldsets = (
  88. (None, ('is_private', 'description')),
  89. )
  90. nullable_fields = ('is_private', 'description')
  91. class ASNRangeBulkEditForm(NetBoxModelBulkEditForm):
  92. rir = DynamicModelChoiceField(
  93. queryset=RIR.objects.all(),
  94. required=False,
  95. label=_('RIR')
  96. )
  97. tenant = DynamicModelChoiceField(
  98. label=_('Tenant'),
  99. queryset=Tenant.objects.all(),
  100. required=False
  101. )
  102. description = forms.CharField(
  103. label=_('Description'),
  104. max_length=200,
  105. required=False
  106. )
  107. model = ASNRange
  108. fieldsets = (
  109. (None, ('rir', 'tenant', 'description')),
  110. )
  111. nullable_fields = ('description',)
  112. class ASNBulkEditForm(NetBoxModelBulkEditForm):
  113. sites = DynamicModelMultipleChoiceField(
  114. label=_('Sites'),
  115. queryset=Site.objects.all(),
  116. required=False
  117. )
  118. rir = DynamicModelChoiceField(
  119. queryset=RIR.objects.all(),
  120. required=False,
  121. label=_('RIR')
  122. )
  123. tenant = DynamicModelChoiceField(
  124. label=_('Tenant'),
  125. queryset=Tenant.objects.all(),
  126. required=False
  127. )
  128. description = forms.CharField(
  129. label=_('Description'),
  130. max_length=200,
  131. required=False
  132. )
  133. comments = CommentField()
  134. model = ASN
  135. fieldsets = (
  136. (None, ('sites', 'rir', 'tenant', 'description')),
  137. )
  138. nullable_fields = ('tenant', 'description', 'comments')
  139. class AggregateBulkEditForm(NetBoxModelBulkEditForm):
  140. rir = DynamicModelChoiceField(
  141. queryset=RIR.objects.all(),
  142. required=False,
  143. label=_('RIR')
  144. )
  145. tenant = DynamicModelChoiceField(
  146. label=_('Tenant'),
  147. queryset=Tenant.objects.all(),
  148. required=False
  149. )
  150. date_added = forms.DateField(
  151. label=_('Date added'),
  152. required=False
  153. )
  154. description = forms.CharField(
  155. label=_('Description'),
  156. max_length=200,
  157. required=False
  158. )
  159. comments = CommentField()
  160. model = Aggregate
  161. fieldsets = (
  162. (None, ('rir', 'tenant', 'date_added', 'description')),
  163. )
  164. nullable_fields = ('date_added', 'description', 'comments')
  165. class RoleBulkEditForm(NetBoxModelBulkEditForm):
  166. weight = forms.IntegerField(
  167. label=_('Weight'),
  168. required=False
  169. )
  170. description = forms.CharField(
  171. label=_('Description'),
  172. max_length=200,
  173. required=False
  174. )
  175. model = Role
  176. fieldsets = (
  177. (None, ('weight', 'description')),
  178. )
  179. nullable_fields = ('description',)
  180. class PrefixBulkEditForm(NetBoxModelBulkEditForm):
  181. region = DynamicModelChoiceField(
  182. label=_('Region'),
  183. queryset=Region.objects.all(),
  184. required=False
  185. )
  186. site_group = DynamicModelChoiceField(
  187. label=_('Site group'),
  188. queryset=SiteGroup.objects.all(),
  189. required=False
  190. )
  191. site = DynamicModelChoiceField(
  192. label=_('Site'),
  193. queryset=Site.objects.all(),
  194. required=False,
  195. query_params={
  196. 'region_id': '$region',
  197. 'group_id': '$site_group',
  198. }
  199. )
  200. vrf = DynamicModelChoiceField(
  201. queryset=VRF.objects.all(),
  202. required=False,
  203. label=_('VRF')
  204. )
  205. prefix_length = forms.IntegerField(
  206. label=_('Prefix length'),
  207. min_value=PREFIX_LENGTH_MIN,
  208. max_value=PREFIX_LENGTH_MAX,
  209. required=False
  210. )
  211. tenant = DynamicModelChoiceField(
  212. label=_('Tenant'),
  213. queryset=Tenant.objects.all(),
  214. required=False
  215. )
  216. status = forms.ChoiceField(
  217. label=_('Status'),
  218. choices=add_blank_choice(PrefixStatusChoices),
  219. required=False
  220. )
  221. role = DynamicModelChoiceField(
  222. label=_('Role'),
  223. queryset=Role.objects.all(),
  224. required=False
  225. )
  226. is_pool = forms.NullBooleanField(
  227. required=False,
  228. widget=BulkEditNullBooleanSelect(),
  229. label=_('Is a pool')
  230. )
  231. mark_utilized = forms.NullBooleanField(
  232. required=False,
  233. widget=BulkEditNullBooleanSelect(),
  234. label=_('Treat as 100% utilized')
  235. )
  236. description = forms.CharField(
  237. label=_('Description'),
  238. max_length=200,
  239. required=False
  240. )
  241. comments = CommentField()
  242. model = Prefix
  243. fieldsets = (
  244. (None, ('tenant', 'status', 'role', 'description')),
  245. (_('Site'), ('region', 'site_group', 'site')),
  246. (_('Addressing'), ('vrf', 'prefix_length', 'is_pool', 'mark_utilized')),
  247. )
  248. nullable_fields = (
  249. 'site', 'vrf', 'tenant', 'role', 'description', 'comments',
  250. )
  251. class IPRangeBulkEditForm(NetBoxModelBulkEditForm):
  252. vrf = DynamicModelChoiceField(
  253. queryset=VRF.objects.all(),
  254. required=False,
  255. label=_('VRF')
  256. )
  257. tenant = DynamicModelChoiceField(
  258. label=_('Tenant'),
  259. queryset=Tenant.objects.all(),
  260. required=False
  261. )
  262. status = forms.ChoiceField(
  263. label=_('Status'),
  264. choices=add_blank_choice(IPRangeStatusChoices),
  265. required=False
  266. )
  267. role = DynamicModelChoiceField(
  268. label=_('Role'),
  269. queryset=Role.objects.all(),
  270. required=False
  271. )
  272. mark_utilized = forms.NullBooleanField(
  273. required=False,
  274. widget=BulkEditNullBooleanSelect(),
  275. label=_('Treat as 100% utilized')
  276. )
  277. description = forms.CharField(
  278. label=_('Description'),
  279. max_length=200,
  280. required=False
  281. )
  282. comments = CommentField()
  283. model = IPRange
  284. fieldsets = (
  285. (None, ('status', 'role', 'vrf', 'tenant', 'mark_utilized', 'description')),
  286. )
  287. nullable_fields = (
  288. 'vrf', 'tenant', 'role', 'description', 'comments',
  289. )
  290. class IPAddressBulkEditForm(NetBoxModelBulkEditForm):
  291. vrf = DynamicModelChoiceField(
  292. queryset=VRF.objects.all(),
  293. required=False,
  294. label=_('VRF')
  295. )
  296. mask_length = forms.IntegerField(
  297. label=_('Mask length'),
  298. min_value=IPADDRESS_MASK_LENGTH_MIN,
  299. max_value=IPADDRESS_MASK_LENGTH_MAX,
  300. required=False
  301. )
  302. tenant = DynamicModelChoiceField(
  303. label=_('Tenant'),
  304. queryset=Tenant.objects.all(),
  305. required=False
  306. )
  307. status = forms.ChoiceField(
  308. label=_('Status'),
  309. choices=add_blank_choice(IPAddressStatusChoices),
  310. required=False
  311. )
  312. role = forms.ChoiceField(
  313. label=_('Role'),
  314. choices=add_blank_choice(IPAddressRoleChoices),
  315. required=False
  316. )
  317. dns_name = forms.CharField(
  318. max_length=255,
  319. required=False,
  320. label=_('DNS name')
  321. )
  322. description = forms.CharField(
  323. label=_('Description'),
  324. max_length=200,
  325. required=False
  326. )
  327. comments = CommentField()
  328. model = IPAddress
  329. fieldsets = (
  330. (None, ('status', 'role', 'tenant', 'description')),
  331. (_('Addressing'), ('vrf', 'mask_length', 'dns_name')),
  332. )
  333. nullable_fields = (
  334. 'vrf', 'role', 'tenant', 'dns_name', 'description', 'comments',
  335. )
  336. class FHRPGroupBulkEditForm(NetBoxModelBulkEditForm):
  337. protocol = forms.ChoiceField(
  338. label=_('Protocol'),
  339. choices=add_blank_choice(FHRPGroupProtocolChoices),
  340. required=False
  341. )
  342. group_id = forms.IntegerField(
  343. min_value=0,
  344. required=False,
  345. label=_('Group ID')
  346. )
  347. auth_type = forms.ChoiceField(
  348. choices=add_blank_choice(FHRPGroupAuthTypeChoices),
  349. required=False,
  350. label=_('Authentication type')
  351. )
  352. auth_key = forms.CharField(
  353. max_length=255,
  354. required=False,
  355. label=_('Authentication key')
  356. )
  357. name = forms.CharField(
  358. label=_('Name'),
  359. max_length=100,
  360. required=False
  361. )
  362. description = forms.CharField(
  363. label=_('Description'),
  364. max_length=200,
  365. required=False
  366. )
  367. comments = CommentField()
  368. model = FHRPGroup
  369. fieldsets = (
  370. (None, ('protocol', 'group_id', 'name', 'description')),
  371. (_('Authentication'), ('auth_type', 'auth_key')),
  372. )
  373. nullable_fields = ('auth_type', 'auth_key', 'name', 'description', 'comments')
  374. class VLANGroupBulkEditForm(NetBoxModelBulkEditForm):
  375. min_vid = forms.IntegerField(
  376. min_value=VLAN_VID_MIN,
  377. max_value=VLAN_VID_MAX,
  378. required=False,
  379. label=_('Minimum child VLAN VID')
  380. )
  381. max_vid = forms.IntegerField(
  382. min_value=VLAN_VID_MIN,
  383. max_value=VLAN_VID_MAX,
  384. required=False,
  385. label=_('Maximum child VLAN VID')
  386. )
  387. description = forms.CharField(
  388. label=_('Description'),
  389. max_length=200,
  390. required=False
  391. )
  392. scope_type = ContentTypeChoiceField(
  393. label=_('Scope type'),
  394. queryset=ContentType.objects.filter(model__in=VLANGROUP_SCOPE_TYPES),
  395. required=False
  396. )
  397. scope_id = forms.IntegerField(
  398. required=False,
  399. widget=forms.HiddenInput()
  400. )
  401. region = DynamicModelChoiceField(
  402. label=_('Region'),
  403. queryset=Region.objects.all(),
  404. required=False
  405. )
  406. sitegroup = DynamicModelChoiceField(
  407. queryset=SiteGroup.objects.all(),
  408. required=False,
  409. label=_('Site group')
  410. )
  411. site = DynamicModelChoiceField(
  412. label=_('Site'),
  413. queryset=Site.objects.all(),
  414. required=False,
  415. query_params={
  416. 'region_id': '$region',
  417. 'group_id': '$sitegroup',
  418. }
  419. )
  420. location = DynamicModelChoiceField(
  421. label=_('Location'),
  422. queryset=Location.objects.all(),
  423. required=False,
  424. query_params={
  425. 'site_id': '$site',
  426. }
  427. )
  428. rack = DynamicModelChoiceField(
  429. label=_('Rack'),
  430. queryset=Rack.objects.all(),
  431. required=False,
  432. query_params={
  433. 'site_id': '$site',
  434. 'location_id': '$location',
  435. }
  436. )
  437. clustergroup = DynamicModelChoiceField(
  438. queryset=ClusterGroup.objects.all(),
  439. required=False,
  440. label=_('Cluster group')
  441. )
  442. cluster = DynamicModelChoiceField(
  443. label=_('Cluster'),
  444. queryset=Cluster.objects.all(),
  445. required=False,
  446. query_params={
  447. 'group_id': '$clustergroup',
  448. }
  449. )
  450. model = VLANGroup
  451. fieldsets = (
  452. (None, ('site', 'min_vid', 'max_vid', 'description')),
  453. (_('Scope'), ('scope_type', 'region', 'sitegroup', 'site', 'location', 'rack', 'clustergroup', 'cluster')),
  454. )
  455. nullable_fields = ('description',)
  456. def clean(self):
  457. super().clean()
  458. # Assign scope based on scope_type
  459. if self.cleaned_data.get('scope_type'):
  460. scope_field = self.cleaned_data['scope_type'].model
  461. if scope_obj := self.cleaned_data.get(scope_field):
  462. self.cleaned_data['scope_id'] = scope_obj.pk
  463. self.changed_data.append('scope_id')
  464. else:
  465. self.cleaned_data.pop('scope_type')
  466. self.changed_data.remove('scope_type')
  467. class VLANBulkEditForm(NetBoxModelBulkEditForm):
  468. region = DynamicModelChoiceField(
  469. label=_('Region'),
  470. queryset=Region.objects.all(),
  471. required=False
  472. )
  473. site_group = DynamicModelChoiceField(
  474. label=_('Site group'),
  475. queryset=SiteGroup.objects.all(),
  476. required=False
  477. )
  478. site = DynamicModelChoiceField(
  479. label=_('Site'),
  480. queryset=Site.objects.all(),
  481. required=False,
  482. query_params={
  483. 'region_id': '$region',
  484. 'group_id': '$site_group',
  485. }
  486. )
  487. group = DynamicModelChoiceField(
  488. label=_('Group'),
  489. queryset=VLANGroup.objects.all(),
  490. required=False,
  491. query_params={
  492. 'site_id': '$site'
  493. }
  494. )
  495. tenant = DynamicModelChoiceField(
  496. label=_('Tenant'),
  497. queryset=Tenant.objects.all(),
  498. required=False
  499. )
  500. status = forms.ChoiceField(
  501. label=_('Status'),
  502. choices=add_blank_choice(VLANStatusChoices),
  503. required=False
  504. )
  505. role = DynamicModelChoiceField(
  506. label=_('Role'),
  507. queryset=Role.objects.all(),
  508. required=False
  509. )
  510. description = forms.CharField(
  511. label=_('Description'),
  512. max_length=200,
  513. required=False
  514. )
  515. comments = CommentField()
  516. model = VLAN
  517. fieldsets = (
  518. (None, ('status', 'role', 'tenant', 'description')),
  519. (_('Site & Group'), ('region', 'site_group', 'site', 'group')),
  520. )
  521. nullable_fields = (
  522. 'site', 'group', 'tenant', 'role', 'description', 'comments',
  523. )
  524. class ServiceTemplateBulkEditForm(NetBoxModelBulkEditForm):
  525. protocol = forms.ChoiceField(
  526. label=_('Protocol'),
  527. choices=add_blank_choice(ServiceProtocolChoices),
  528. required=False
  529. )
  530. ports = NumericArrayField(
  531. label=_('Ports'),
  532. base_field=forms.IntegerField(
  533. min_value=SERVICE_PORT_MIN,
  534. max_value=SERVICE_PORT_MAX
  535. ),
  536. required=False
  537. )
  538. description = forms.CharField(
  539. label=_('Description'),
  540. max_length=200,
  541. required=False
  542. )
  543. comments = CommentField()
  544. model = ServiceTemplate
  545. fieldsets = (
  546. (None, ('protocol', 'ports', 'description')),
  547. )
  548. nullable_fields = ('description', 'comments')
  549. class ServiceBulkEditForm(ServiceTemplateBulkEditForm):
  550. model = Service
  551. class L2VPNBulkEditForm(NetBoxModelBulkEditForm):
  552. type = forms.ChoiceField(
  553. label=_('Type'),
  554. choices=add_blank_choice(L2VPNTypeChoices),
  555. required=False
  556. )
  557. tenant = DynamicModelChoiceField(
  558. label=_('Tenant'),
  559. queryset=Tenant.objects.all(),
  560. required=False
  561. )
  562. description = forms.CharField(
  563. label=_('Description'),
  564. max_length=200,
  565. required=False
  566. )
  567. comments = CommentField()
  568. model = L2VPN
  569. fieldsets = (
  570. (None, ('type', 'tenant', 'description')),
  571. )
  572. nullable_fields = ('tenant', 'description', 'comments')
  573. class L2VPNTerminationBulkEditForm(NetBoxModelBulkEditForm):
  574. model = L2VPN