bulk_edit.py 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210
  1. from django import forms
  2. from django.utils.translation import gettext as _
  3. from django.contrib.auth.models import User
  4. from timezone_field import TimeZoneFormField
  5. from dcim.choices import *
  6. from dcim.constants import *
  7. from dcim.models import *
  8. from extras.forms import AddRemoveTagsForm, CustomFieldModelBulkEditForm
  9. from ipam.models import VLAN, ASN
  10. from tenancy.models import Tenant
  11. from utilities.forms import (
  12. add_blank_choice, BulkEditForm, BulkEditNullBooleanSelect, ColorField, CommentField, DynamicModelChoiceField,
  13. DynamicModelMultipleChoiceField, form_from_model, SmallTextarea, StaticSelect,
  14. )
  15. __all__ = (
  16. 'CableBulkEditForm',
  17. 'ConsolePortBulkEditForm',
  18. 'ConsolePortTemplateBulkEditForm',
  19. 'ConsoleServerPortBulkEditForm',
  20. 'ConsoleServerPortTemplateBulkEditForm',
  21. 'DeviceBayBulkEditForm',
  22. 'DeviceBayTemplateBulkEditForm',
  23. 'DeviceBulkEditForm',
  24. 'DeviceRoleBulkEditForm',
  25. 'DeviceTypeBulkEditForm',
  26. 'FrontPortBulkEditForm',
  27. 'FrontPortTemplateBulkEditForm',
  28. 'InterfaceBulkEditForm',
  29. 'InterfaceTemplateBulkEditForm',
  30. 'InventoryItemBulkEditForm',
  31. 'InventoryItemRoleBulkEditForm',
  32. 'LocationBulkEditForm',
  33. 'ManufacturerBulkEditForm',
  34. 'ModuleBulkEditForm',
  35. 'ModuleBayBulkEditForm',
  36. 'ModuleBayTemplateBulkEditForm',
  37. 'ModuleTypeBulkEditForm',
  38. 'PlatformBulkEditForm',
  39. 'PowerFeedBulkEditForm',
  40. 'PowerOutletBulkEditForm',
  41. 'PowerOutletTemplateBulkEditForm',
  42. 'PowerPanelBulkEditForm',
  43. 'PowerPortBulkEditForm',
  44. 'PowerPortTemplateBulkEditForm',
  45. 'RackBulkEditForm',
  46. 'RackReservationBulkEditForm',
  47. 'RackRoleBulkEditForm',
  48. 'RearPortBulkEditForm',
  49. 'RearPortTemplateBulkEditForm',
  50. 'RegionBulkEditForm',
  51. 'SiteBulkEditForm',
  52. 'SiteGroupBulkEditForm',
  53. 'VirtualChassisBulkEditForm',
  54. )
  55. class RegionBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
  56. pk = forms.ModelMultipleChoiceField(
  57. queryset=Region.objects.all(),
  58. widget=forms.MultipleHiddenInput
  59. )
  60. parent = DynamicModelChoiceField(
  61. queryset=Region.objects.all(),
  62. required=False
  63. )
  64. description = forms.CharField(
  65. max_length=200,
  66. required=False
  67. )
  68. class Meta:
  69. nullable_fields = ['parent', 'description']
  70. class SiteGroupBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
  71. pk = forms.ModelMultipleChoiceField(
  72. queryset=SiteGroup.objects.all(),
  73. widget=forms.MultipleHiddenInput
  74. )
  75. parent = DynamicModelChoiceField(
  76. queryset=SiteGroup.objects.all(),
  77. required=False
  78. )
  79. description = forms.CharField(
  80. max_length=200,
  81. required=False
  82. )
  83. class Meta:
  84. nullable_fields = ['parent', 'description']
  85. class SiteBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
  86. pk = forms.ModelMultipleChoiceField(
  87. queryset=Site.objects.all(),
  88. widget=forms.MultipleHiddenInput
  89. )
  90. status = forms.ChoiceField(
  91. choices=add_blank_choice(SiteStatusChoices),
  92. required=False,
  93. initial='',
  94. widget=StaticSelect()
  95. )
  96. region = DynamicModelChoiceField(
  97. queryset=Region.objects.all(),
  98. required=False
  99. )
  100. group = DynamicModelChoiceField(
  101. queryset=SiteGroup.objects.all(),
  102. required=False
  103. )
  104. tenant = DynamicModelChoiceField(
  105. queryset=Tenant.objects.all(),
  106. required=False
  107. )
  108. asns = DynamicModelMultipleChoiceField(
  109. queryset=ASN.objects.all(),
  110. label=_('ASNs'),
  111. required=False
  112. )
  113. description = forms.CharField(
  114. max_length=100,
  115. required=False
  116. )
  117. time_zone = TimeZoneFormField(
  118. choices=add_blank_choice(TimeZoneFormField().choices),
  119. required=False,
  120. widget=StaticSelect()
  121. )
  122. class Meta:
  123. nullable_fields = [
  124. 'region', 'group', 'tenant', 'asns', 'description', 'time_zone',
  125. ]
  126. class LocationBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
  127. pk = forms.ModelMultipleChoiceField(
  128. queryset=Location.objects.all(),
  129. widget=forms.MultipleHiddenInput
  130. )
  131. site = DynamicModelChoiceField(
  132. queryset=Site.objects.all(),
  133. required=False
  134. )
  135. parent = DynamicModelChoiceField(
  136. queryset=Location.objects.all(),
  137. required=False,
  138. query_params={
  139. 'site_id': '$site'
  140. }
  141. )
  142. tenant = DynamicModelChoiceField(
  143. queryset=Tenant.objects.all(),
  144. required=False
  145. )
  146. description = forms.CharField(
  147. max_length=200,
  148. required=False
  149. )
  150. class Meta:
  151. nullable_fields = ['parent', 'tenant', 'description']
  152. class RackRoleBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
  153. pk = forms.ModelMultipleChoiceField(
  154. queryset=RackRole.objects.all(),
  155. widget=forms.MultipleHiddenInput
  156. )
  157. color = ColorField(
  158. required=False
  159. )
  160. description = forms.CharField(
  161. max_length=200,
  162. required=False
  163. )
  164. class Meta:
  165. nullable_fields = ['color', 'description']
  166. class RackBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
  167. pk = forms.ModelMultipleChoiceField(
  168. queryset=Rack.objects.all(),
  169. widget=forms.MultipleHiddenInput
  170. )
  171. region = DynamicModelChoiceField(
  172. queryset=Region.objects.all(),
  173. required=False,
  174. initial_params={
  175. 'sites': '$site'
  176. }
  177. )
  178. site_group = DynamicModelChoiceField(
  179. queryset=SiteGroup.objects.all(),
  180. required=False,
  181. initial_params={
  182. 'sites': '$site'
  183. }
  184. )
  185. site = DynamicModelChoiceField(
  186. queryset=Site.objects.all(),
  187. required=False,
  188. query_params={
  189. 'region_id': '$region',
  190. 'group_id': '$site_group',
  191. }
  192. )
  193. location = DynamicModelChoiceField(
  194. queryset=Location.objects.all(),
  195. required=False,
  196. query_params={
  197. 'site_id': '$site'
  198. }
  199. )
  200. tenant = DynamicModelChoiceField(
  201. queryset=Tenant.objects.all(),
  202. required=False
  203. )
  204. status = forms.ChoiceField(
  205. choices=add_blank_choice(RackStatusChoices),
  206. required=False,
  207. initial='',
  208. widget=StaticSelect()
  209. )
  210. role = DynamicModelChoiceField(
  211. queryset=RackRole.objects.all(),
  212. required=False
  213. )
  214. serial = forms.CharField(
  215. max_length=50,
  216. required=False,
  217. label='Serial Number'
  218. )
  219. asset_tag = forms.CharField(
  220. max_length=50,
  221. required=False
  222. )
  223. type = forms.ChoiceField(
  224. choices=add_blank_choice(RackTypeChoices),
  225. required=False,
  226. widget=StaticSelect()
  227. )
  228. width = forms.ChoiceField(
  229. choices=add_blank_choice(RackWidthChoices),
  230. required=False,
  231. widget=StaticSelect()
  232. )
  233. u_height = forms.IntegerField(
  234. required=False,
  235. label='Height (U)'
  236. )
  237. desc_units = forms.NullBooleanField(
  238. required=False,
  239. widget=BulkEditNullBooleanSelect,
  240. label='Descending units'
  241. )
  242. outer_width = forms.IntegerField(
  243. required=False,
  244. min_value=1
  245. )
  246. outer_depth = forms.IntegerField(
  247. required=False,
  248. min_value=1
  249. )
  250. outer_unit = forms.ChoiceField(
  251. choices=add_blank_choice(RackDimensionUnitChoices),
  252. required=False,
  253. widget=StaticSelect()
  254. )
  255. comments = CommentField(
  256. widget=SmallTextarea,
  257. label='Comments'
  258. )
  259. class Meta:
  260. nullable_fields = [
  261. 'location', 'tenant', 'role', 'serial', 'asset_tag', 'outer_width', 'outer_depth', 'outer_unit', 'comments',
  262. ]
  263. class RackReservationBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
  264. pk = forms.ModelMultipleChoiceField(
  265. queryset=RackReservation.objects.all(),
  266. widget=forms.MultipleHiddenInput()
  267. )
  268. user = forms.ModelChoiceField(
  269. queryset=User.objects.order_by(
  270. 'username'
  271. ),
  272. required=False,
  273. widget=StaticSelect()
  274. )
  275. tenant = DynamicModelChoiceField(
  276. queryset=Tenant.objects.all(),
  277. required=False
  278. )
  279. description = forms.CharField(
  280. max_length=100,
  281. required=False
  282. )
  283. class Meta:
  284. nullable_fields = []
  285. class ManufacturerBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
  286. pk = forms.ModelMultipleChoiceField(
  287. queryset=Manufacturer.objects.all(),
  288. widget=forms.MultipleHiddenInput
  289. )
  290. description = forms.CharField(
  291. max_length=200,
  292. required=False
  293. )
  294. class Meta:
  295. nullable_fields = ['description']
  296. class DeviceTypeBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
  297. pk = forms.ModelMultipleChoiceField(
  298. queryset=DeviceType.objects.all(),
  299. widget=forms.MultipleHiddenInput()
  300. )
  301. manufacturer = DynamicModelChoiceField(
  302. queryset=Manufacturer.objects.all(),
  303. required=False
  304. )
  305. part_number = forms.CharField(
  306. required=False
  307. )
  308. u_height = forms.IntegerField(
  309. min_value=1,
  310. required=False
  311. )
  312. is_full_depth = forms.NullBooleanField(
  313. required=False,
  314. widget=BulkEditNullBooleanSelect(),
  315. label='Is full depth'
  316. )
  317. airflow = forms.ChoiceField(
  318. choices=add_blank_choice(DeviceAirflowChoices),
  319. required=False,
  320. widget=StaticSelect()
  321. )
  322. class Meta:
  323. nullable_fields = ['part_number', 'airflow']
  324. class ModuleTypeBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
  325. pk = forms.ModelMultipleChoiceField(
  326. queryset=ModuleType.objects.all(),
  327. widget=forms.MultipleHiddenInput()
  328. )
  329. manufacturer = DynamicModelChoiceField(
  330. queryset=Manufacturer.objects.all(),
  331. required=False
  332. )
  333. part_number = forms.CharField(
  334. required=False
  335. )
  336. class Meta:
  337. nullable_fields = ['part_number']
  338. class DeviceRoleBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
  339. pk = forms.ModelMultipleChoiceField(
  340. queryset=DeviceRole.objects.all(),
  341. widget=forms.MultipleHiddenInput
  342. )
  343. color = ColorField(
  344. required=False
  345. )
  346. vm_role = forms.NullBooleanField(
  347. required=False,
  348. widget=BulkEditNullBooleanSelect,
  349. label='VM role'
  350. )
  351. description = forms.CharField(
  352. max_length=200,
  353. required=False
  354. )
  355. class Meta:
  356. nullable_fields = ['color', 'description']
  357. class PlatformBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
  358. pk = forms.ModelMultipleChoiceField(
  359. queryset=Platform.objects.all(),
  360. widget=forms.MultipleHiddenInput
  361. )
  362. manufacturer = DynamicModelChoiceField(
  363. queryset=Manufacturer.objects.all(),
  364. required=False
  365. )
  366. napalm_driver = forms.CharField(
  367. max_length=50,
  368. required=False
  369. )
  370. # TODO: Bulk edit support for napalm_args
  371. description = forms.CharField(
  372. max_length=200,
  373. required=False
  374. )
  375. class Meta:
  376. nullable_fields = ['manufacturer', 'napalm_driver', 'description']
  377. class DeviceBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
  378. pk = forms.ModelMultipleChoiceField(
  379. queryset=Device.objects.all(),
  380. widget=forms.MultipleHiddenInput()
  381. )
  382. manufacturer = DynamicModelChoiceField(
  383. queryset=Manufacturer.objects.all(),
  384. required=False
  385. )
  386. device_type = DynamicModelChoiceField(
  387. queryset=DeviceType.objects.all(),
  388. required=False,
  389. query_params={
  390. 'manufacturer_id': '$manufacturer'
  391. }
  392. )
  393. device_role = DynamicModelChoiceField(
  394. queryset=DeviceRole.objects.all(),
  395. required=False
  396. )
  397. site = DynamicModelChoiceField(
  398. queryset=Site.objects.all(),
  399. required=False
  400. )
  401. location = DynamicModelChoiceField(
  402. queryset=Location.objects.all(),
  403. required=False,
  404. query_params={
  405. 'site_id': '$site'
  406. }
  407. )
  408. tenant = DynamicModelChoiceField(
  409. queryset=Tenant.objects.all(),
  410. required=False
  411. )
  412. platform = DynamicModelChoiceField(
  413. queryset=Platform.objects.all(),
  414. required=False
  415. )
  416. status = forms.ChoiceField(
  417. choices=add_blank_choice(DeviceStatusChoices),
  418. required=False,
  419. widget=StaticSelect()
  420. )
  421. airflow = forms.ChoiceField(
  422. choices=add_blank_choice(DeviceAirflowChoices),
  423. required=False,
  424. widget=StaticSelect()
  425. )
  426. serial = forms.CharField(
  427. max_length=50,
  428. required=False,
  429. label='Serial Number'
  430. )
  431. class Meta:
  432. nullable_fields = [
  433. 'tenant', 'platform', 'serial', 'airflow',
  434. ]
  435. class ModuleBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
  436. pk = forms.ModelMultipleChoiceField(
  437. queryset=Module.objects.all(),
  438. widget=forms.MultipleHiddenInput()
  439. )
  440. manufacturer = DynamicModelChoiceField(
  441. queryset=Manufacturer.objects.all(),
  442. required=False
  443. )
  444. module_type = DynamicModelChoiceField(
  445. queryset=ModuleType.objects.all(),
  446. required=False,
  447. query_params={
  448. 'manufacturer_id': '$manufacturer'
  449. }
  450. )
  451. serial = forms.CharField(
  452. max_length=50,
  453. required=False,
  454. label='Serial Number'
  455. )
  456. class Meta:
  457. nullable_fields = ['serial']
  458. class CableBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
  459. pk = forms.ModelMultipleChoiceField(
  460. queryset=Cable.objects.all(),
  461. widget=forms.MultipleHiddenInput
  462. )
  463. type = forms.ChoiceField(
  464. choices=add_blank_choice(CableTypeChoices),
  465. required=False,
  466. initial='',
  467. widget=StaticSelect()
  468. )
  469. status = forms.ChoiceField(
  470. choices=add_blank_choice(LinkStatusChoices),
  471. required=False,
  472. widget=StaticSelect(),
  473. initial=''
  474. )
  475. tenant = DynamicModelChoiceField(
  476. queryset=Tenant.objects.all(),
  477. required=False
  478. )
  479. label = forms.CharField(
  480. max_length=100,
  481. required=False
  482. )
  483. color = ColorField(
  484. required=False
  485. )
  486. length = forms.DecimalField(
  487. min_value=0,
  488. required=False
  489. )
  490. length_unit = forms.ChoiceField(
  491. choices=add_blank_choice(CableLengthUnitChoices),
  492. required=False,
  493. initial='',
  494. widget=StaticSelect()
  495. )
  496. class Meta:
  497. nullable_fields = [
  498. 'type', 'status', 'tenant', 'label', 'color', 'length',
  499. ]
  500. def clean(self):
  501. super().clean()
  502. # Validate length/unit
  503. length = self.cleaned_data.get('length')
  504. length_unit = self.cleaned_data.get('length_unit')
  505. if length and not length_unit:
  506. raise forms.ValidationError({
  507. 'length_unit': "Must specify a unit when setting length"
  508. })
  509. class VirtualChassisBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
  510. pk = forms.ModelMultipleChoiceField(
  511. queryset=VirtualChassis.objects.all(),
  512. widget=forms.MultipleHiddenInput()
  513. )
  514. domain = forms.CharField(
  515. max_length=30,
  516. required=False
  517. )
  518. class Meta:
  519. nullable_fields = ['domain']
  520. class PowerPanelBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
  521. pk = forms.ModelMultipleChoiceField(
  522. queryset=PowerPanel.objects.all(),
  523. widget=forms.MultipleHiddenInput
  524. )
  525. region = DynamicModelChoiceField(
  526. queryset=Region.objects.all(),
  527. required=False,
  528. initial_params={
  529. 'sites': '$site'
  530. }
  531. )
  532. site_group = DynamicModelChoiceField(
  533. queryset=SiteGroup.objects.all(),
  534. required=False,
  535. initial_params={
  536. 'sites': '$site'
  537. }
  538. )
  539. site = DynamicModelChoiceField(
  540. queryset=Site.objects.all(),
  541. required=False,
  542. query_params={
  543. 'region_id': '$region',
  544. 'group_id': '$site_group',
  545. }
  546. )
  547. location = DynamicModelChoiceField(
  548. queryset=Location.objects.all(),
  549. required=False,
  550. query_params={
  551. 'site_id': '$site'
  552. }
  553. )
  554. class Meta:
  555. nullable_fields = ['location']
  556. class PowerFeedBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
  557. pk = forms.ModelMultipleChoiceField(
  558. queryset=PowerFeed.objects.all(),
  559. widget=forms.MultipleHiddenInput
  560. )
  561. power_panel = DynamicModelChoiceField(
  562. queryset=PowerPanel.objects.all(),
  563. required=False
  564. )
  565. rack = DynamicModelChoiceField(
  566. queryset=Rack.objects.all(),
  567. required=False,
  568. )
  569. status = forms.ChoiceField(
  570. choices=add_blank_choice(PowerFeedStatusChoices),
  571. required=False,
  572. initial='',
  573. widget=StaticSelect()
  574. )
  575. type = forms.ChoiceField(
  576. choices=add_blank_choice(PowerFeedTypeChoices),
  577. required=False,
  578. initial='',
  579. widget=StaticSelect()
  580. )
  581. supply = forms.ChoiceField(
  582. choices=add_blank_choice(PowerFeedSupplyChoices),
  583. required=False,
  584. initial='',
  585. widget=StaticSelect()
  586. )
  587. phase = forms.ChoiceField(
  588. choices=add_blank_choice(PowerFeedPhaseChoices),
  589. required=False,
  590. initial='',
  591. widget=StaticSelect()
  592. )
  593. voltage = forms.IntegerField(
  594. required=False
  595. )
  596. amperage = forms.IntegerField(
  597. required=False
  598. )
  599. max_utilization = forms.IntegerField(
  600. required=False
  601. )
  602. mark_connected = forms.NullBooleanField(
  603. required=False,
  604. widget=BulkEditNullBooleanSelect
  605. )
  606. comments = CommentField(
  607. widget=SmallTextarea,
  608. label='Comments'
  609. )
  610. class Meta:
  611. nullable_fields = [
  612. 'location', 'comments',
  613. ]
  614. #
  615. # Device component templates
  616. #
  617. class ConsolePortTemplateBulkEditForm(BulkEditForm):
  618. pk = forms.ModelMultipleChoiceField(
  619. queryset=ConsolePortTemplate.objects.all(),
  620. widget=forms.MultipleHiddenInput()
  621. )
  622. label = forms.CharField(
  623. max_length=64,
  624. required=False
  625. )
  626. type = forms.ChoiceField(
  627. choices=add_blank_choice(ConsolePortTypeChoices),
  628. required=False,
  629. widget=StaticSelect()
  630. )
  631. class Meta:
  632. nullable_fields = ('label', 'type', 'description')
  633. class ConsoleServerPortTemplateBulkEditForm(BulkEditForm):
  634. pk = forms.ModelMultipleChoiceField(
  635. queryset=ConsoleServerPortTemplate.objects.all(),
  636. widget=forms.MultipleHiddenInput()
  637. )
  638. label = forms.CharField(
  639. max_length=64,
  640. required=False
  641. )
  642. type = forms.ChoiceField(
  643. choices=add_blank_choice(ConsolePortTypeChoices),
  644. required=False,
  645. widget=StaticSelect()
  646. )
  647. description = forms.CharField(
  648. required=False
  649. )
  650. class Meta:
  651. nullable_fields = ('label', 'type', 'description')
  652. class PowerPortTemplateBulkEditForm(BulkEditForm):
  653. pk = forms.ModelMultipleChoiceField(
  654. queryset=PowerPortTemplate.objects.all(),
  655. widget=forms.MultipleHiddenInput()
  656. )
  657. label = forms.CharField(
  658. max_length=64,
  659. required=False
  660. )
  661. type = forms.ChoiceField(
  662. choices=add_blank_choice(PowerPortTypeChoices),
  663. required=False,
  664. widget=StaticSelect()
  665. )
  666. maximum_draw = forms.IntegerField(
  667. min_value=1,
  668. required=False,
  669. help_text="Maximum power draw (watts)"
  670. )
  671. allocated_draw = forms.IntegerField(
  672. min_value=1,
  673. required=False,
  674. help_text="Allocated power draw (watts)"
  675. )
  676. description = forms.CharField(
  677. required=False
  678. )
  679. class Meta:
  680. nullable_fields = ('label', 'type', 'maximum_draw', 'allocated_draw', 'description')
  681. class PowerOutletTemplateBulkEditForm(BulkEditForm):
  682. pk = forms.ModelMultipleChoiceField(
  683. queryset=PowerOutletTemplate.objects.all(),
  684. widget=forms.MultipleHiddenInput()
  685. )
  686. device_type = forms.ModelChoiceField(
  687. queryset=DeviceType.objects.all(),
  688. required=False,
  689. disabled=True,
  690. widget=forms.HiddenInput()
  691. )
  692. label = forms.CharField(
  693. max_length=64,
  694. required=False
  695. )
  696. type = forms.ChoiceField(
  697. choices=add_blank_choice(PowerOutletTypeChoices),
  698. required=False,
  699. widget=StaticSelect()
  700. )
  701. power_port = forms.ModelChoiceField(
  702. queryset=PowerPortTemplate.objects.all(),
  703. required=False
  704. )
  705. feed_leg = forms.ChoiceField(
  706. choices=add_blank_choice(PowerOutletFeedLegChoices),
  707. required=False,
  708. widget=StaticSelect()
  709. )
  710. description = forms.CharField(
  711. required=False
  712. )
  713. class Meta:
  714. nullable_fields = ('label', 'type', 'power_port', 'feed_leg', 'description')
  715. def __init__(self, *args, **kwargs):
  716. super().__init__(*args, **kwargs)
  717. # Limit power_port queryset to PowerPortTemplates which belong to the parent DeviceType
  718. if 'device_type' in self.initial:
  719. device_type = DeviceType.objects.filter(pk=self.initial['device_type']).first()
  720. self.fields['power_port'].queryset = PowerPortTemplate.objects.filter(device_type=device_type)
  721. else:
  722. self.fields['power_port'].choices = ()
  723. self.fields['power_port'].widget.attrs['disabled'] = True
  724. class InterfaceTemplateBulkEditForm(BulkEditForm):
  725. pk = forms.ModelMultipleChoiceField(
  726. queryset=InterfaceTemplate.objects.all(),
  727. widget=forms.MultipleHiddenInput()
  728. )
  729. label = forms.CharField(
  730. max_length=64,
  731. required=False
  732. )
  733. type = forms.ChoiceField(
  734. choices=add_blank_choice(InterfaceTypeChoices),
  735. required=False,
  736. widget=StaticSelect()
  737. )
  738. mgmt_only = forms.NullBooleanField(
  739. required=False,
  740. widget=BulkEditNullBooleanSelect,
  741. label='Management only'
  742. )
  743. description = forms.CharField(
  744. required=False
  745. )
  746. class Meta:
  747. nullable_fields = ('label', 'description')
  748. class FrontPortTemplateBulkEditForm(BulkEditForm):
  749. pk = forms.ModelMultipleChoiceField(
  750. queryset=FrontPortTemplate.objects.all(),
  751. widget=forms.MultipleHiddenInput()
  752. )
  753. label = forms.CharField(
  754. max_length=64,
  755. required=False
  756. )
  757. type = forms.ChoiceField(
  758. choices=add_blank_choice(PortTypeChoices),
  759. required=False,
  760. widget=StaticSelect()
  761. )
  762. color = ColorField(
  763. required=False
  764. )
  765. description = forms.CharField(
  766. required=False
  767. )
  768. class Meta:
  769. nullable_fields = ('description',)
  770. class RearPortTemplateBulkEditForm(BulkEditForm):
  771. pk = forms.ModelMultipleChoiceField(
  772. queryset=RearPortTemplate.objects.all(),
  773. widget=forms.MultipleHiddenInput()
  774. )
  775. label = forms.CharField(
  776. max_length=64,
  777. required=False
  778. )
  779. type = forms.ChoiceField(
  780. choices=add_blank_choice(PortTypeChoices),
  781. required=False,
  782. widget=StaticSelect()
  783. )
  784. color = ColorField(
  785. required=False
  786. )
  787. description = forms.CharField(
  788. required=False
  789. )
  790. class Meta:
  791. nullable_fields = ('description',)
  792. class ModuleBayTemplateBulkEditForm(BulkEditForm):
  793. pk = forms.ModelMultipleChoiceField(
  794. queryset=ModuleBayTemplate.objects.all(),
  795. widget=forms.MultipleHiddenInput()
  796. )
  797. label = forms.CharField(
  798. max_length=64,
  799. required=False
  800. )
  801. description = forms.CharField(
  802. required=False
  803. )
  804. class Meta:
  805. nullable_fields = ('label', 'position', 'description')
  806. class DeviceBayTemplateBulkEditForm(BulkEditForm):
  807. pk = forms.ModelMultipleChoiceField(
  808. queryset=DeviceBayTemplate.objects.all(),
  809. widget=forms.MultipleHiddenInput()
  810. )
  811. label = forms.CharField(
  812. max_length=64,
  813. required=False
  814. )
  815. description = forms.CharField(
  816. required=False
  817. )
  818. class Meta:
  819. nullable_fields = ('label', 'description')
  820. #
  821. # Device components
  822. #
  823. class ConsolePortBulkEditForm(
  824. form_from_model(ConsolePort, ['label', 'type', 'speed', 'mark_connected', 'description']),
  825. AddRemoveTagsForm,
  826. CustomFieldModelBulkEditForm
  827. ):
  828. pk = forms.ModelMultipleChoiceField(
  829. queryset=ConsolePort.objects.all(),
  830. widget=forms.MultipleHiddenInput()
  831. )
  832. mark_connected = forms.NullBooleanField(
  833. required=False,
  834. widget=BulkEditNullBooleanSelect
  835. )
  836. class Meta:
  837. nullable_fields = ['label', 'description']
  838. class ConsoleServerPortBulkEditForm(
  839. form_from_model(ConsoleServerPort, ['label', 'type', 'speed', 'mark_connected', 'description']),
  840. AddRemoveTagsForm,
  841. CustomFieldModelBulkEditForm
  842. ):
  843. pk = forms.ModelMultipleChoiceField(
  844. queryset=ConsoleServerPort.objects.all(),
  845. widget=forms.MultipleHiddenInput()
  846. )
  847. mark_connected = forms.NullBooleanField(
  848. required=False,
  849. widget=BulkEditNullBooleanSelect
  850. )
  851. class Meta:
  852. nullable_fields = ['label', 'description']
  853. class PowerPortBulkEditForm(
  854. form_from_model(PowerPort, ['label', 'type', 'maximum_draw', 'allocated_draw', 'mark_connected', 'description']),
  855. AddRemoveTagsForm,
  856. CustomFieldModelBulkEditForm
  857. ):
  858. pk = forms.ModelMultipleChoiceField(
  859. queryset=PowerPort.objects.all(),
  860. widget=forms.MultipleHiddenInput()
  861. )
  862. mark_connected = forms.NullBooleanField(
  863. required=False,
  864. widget=BulkEditNullBooleanSelect
  865. )
  866. class Meta:
  867. nullable_fields = ['label', 'description']
  868. class PowerOutletBulkEditForm(
  869. form_from_model(PowerOutlet, ['label', 'type', 'feed_leg', 'power_port', 'mark_connected', 'description']),
  870. AddRemoveTagsForm,
  871. CustomFieldModelBulkEditForm
  872. ):
  873. pk = forms.ModelMultipleChoiceField(
  874. queryset=PowerOutlet.objects.all(),
  875. widget=forms.MultipleHiddenInput()
  876. )
  877. device = forms.ModelChoiceField(
  878. queryset=Device.objects.all(),
  879. required=False,
  880. disabled=True,
  881. widget=forms.HiddenInput()
  882. )
  883. mark_connected = forms.NullBooleanField(
  884. required=False,
  885. widget=BulkEditNullBooleanSelect
  886. )
  887. class Meta:
  888. nullable_fields = ['label', 'type', 'feed_leg', 'power_port', 'description']
  889. def __init__(self, *args, **kwargs):
  890. super().__init__(*args, **kwargs)
  891. # Limit power_port queryset to PowerPorts which belong to the parent Device
  892. if 'device' in self.initial:
  893. device = Device.objects.filter(pk=self.initial['device']).first()
  894. self.fields['power_port'].queryset = PowerPort.objects.filter(device=device)
  895. else:
  896. self.fields['power_port'].choices = ()
  897. self.fields['power_port'].widget.attrs['disabled'] = True
  898. class InterfaceBulkEditForm(
  899. form_from_model(Interface, [
  900. 'label', 'type', 'parent', 'bridge', 'lag', 'mac_address', 'wwn', 'mtu', 'mgmt_only', 'mark_connected',
  901. 'description', 'mode', 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power',
  902. ]),
  903. AddRemoveTagsForm,
  904. CustomFieldModelBulkEditForm
  905. ):
  906. pk = forms.ModelMultipleChoiceField(
  907. queryset=Interface.objects.all(),
  908. widget=forms.MultipleHiddenInput()
  909. )
  910. device = forms.ModelChoiceField(
  911. queryset=Device.objects.all(),
  912. required=False,
  913. disabled=True,
  914. widget=forms.HiddenInput()
  915. )
  916. enabled = forms.NullBooleanField(
  917. required=False,
  918. widget=BulkEditNullBooleanSelect
  919. )
  920. parent = DynamicModelChoiceField(
  921. queryset=Interface.objects.all(),
  922. required=False
  923. )
  924. bridge = DynamicModelChoiceField(
  925. queryset=Interface.objects.all(),
  926. required=False
  927. )
  928. lag = DynamicModelChoiceField(
  929. queryset=Interface.objects.all(),
  930. required=False,
  931. query_params={
  932. 'type': 'lag',
  933. }
  934. )
  935. mgmt_only = forms.NullBooleanField(
  936. required=False,
  937. widget=BulkEditNullBooleanSelect,
  938. label='Management only'
  939. )
  940. mark_connected = forms.NullBooleanField(
  941. required=False,
  942. widget=BulkEditNullBooleanSelect
  943. )
  944. untagged_vlan = DynamicModelChoiceField(
  945. queryset=VLAN.objects.all(),
  946. required=False
  947. )
  948. tagged_vlans = DynamicModelMultipleChoiceField(
  949. queryset=VLAN.objects.all(),
  950. required=False
  951. )
  952. class Meta:
  953. nullable_fields = [
  954. 'label', 'parent', 'bridge', 'lag', 'mac_address', 'wwn', 'mtu', 'description', 'mode', 'rf_channel',
  955. 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'untagged_vlan', 'tagged_vlans',
  956. ]
  957. def __init__(self, *args, **kwargs):
  958. super().__init__(*args, **kwargs)
  959. if 'device' in self.initial:
  960. device = Device.objects.filter(pk=self.initial['device']).first()
  961. # Restrict parent/bridge/LAG interface assignment by device
  962. self.fields['parent'].widget.add_query_param('device_id', device.pk)
  963. self.fields['bridge'].widget.add_query_param('device_id', device.pk)
  964. self.fields['lag'].widget.add_query_param('device_id', device.pk)
  965. # Limit VLAN choices by device
  966. self.fields['untagged_vlan'].widget.add_query_param('available_on_device', device.pk)
  967. self.fields['tagged_vlans'].widget.add_query_param('available_on_device', device.pk)
  968. else:
  969. # See #4523
  970. if 'pk' in self.initial:
  971. site = None
  972. interfaces = Interface.objects.filter(pk__in=self.initial['pk']).prefetch_related('device__site')
  973. # Check interface sites. First interface should set site, further interfaces will either continue the
  974. # loop or reset back to no site and break the loop.
  975. for interface in interfaces:
  976. if site is None:
  977. site = interface.device.site
  978. elif interface.device.site is not site:
  979. site = None
  980. break
  981. if site is not None:
  982. self.fields['untagged_vlan'].widget.add_query_param('site_id', site.pk)
  983. self.fields['tagged_vlans'].widget.add_query_param('site_id', site.pk)
  984. self.fields['parent'].choices = ()
  985. self.fields['parent'].widget.attrs['disabled'] = True
  986. self.fields['bridge'].choices = ()
  987. self.fields['bridge'].widget.attrs['disabled'] = True
  988. self.fields['lag'].choices = ()
  989. self.fields['lag'].widget.attrs['disabled'] = True
  990. def clean(self):
  991. super().clean()
  992. # Untagged interfaces cannot be assigned tagged VLANs
  993. if self.cleaned_data['mode'] == InterfaceModeChoices.MODE_ACCESS and self.cleaned_data['tagged_vlans']:
  994. raise forms.ValidationError({
  995. 'mode': "An access interface cannot have tagged VLANs assigned."
  996. })
  997. # Remove all tagged VLAN assignments from "tagged all" interfaces
  998. elif self.cleaned_data['mode'] == InterfaceModeChoices.MODE_TAGGED_ALL:
  999. self.cleaned_data['tagged_vlans'] = []
  1000. class FrontPortBulkEditForm(
  1001. form_from_model(FrontPort, ['label', 'type', 'color', 'mark_connected', 'description']),
  1002. AddRemoveTagsForm,
  1003. CustomFieldModelBulkEditForm
  1004. ):
  1005. pk = forms.ModelMultipleChoiceField(
  1006. queryset=FrontPort.objects.all(),
  1007. widget=forms.MultipleHiddenInput()
  1008. )
  1009. class Meta:
  1010. nullable_fields = ['label', 'description']
  1011. class RearPortBulkEditForm(
  1012. form_from_model(RearPort, ['label', 'type', 'color', 'mark_connected', 'description']),
  1013. AddRemoveTagsForm,
  1014. CustomFieldModelBulkEditForm
  1015. ):
  1016. pk = forms.ModelMultipleChoiceField(
  1017. queryset=RearPort.objects.all(),
  1018. widget=forms.MultipleHiddenInput()
  1019. )
  1020. class Meta:
  1021. nullable_fields = ['label', 'description']
  1022. class ModuleBayBulkEditForm(
  1023. form_from_model(DeviceBay, ['label', 'description']),
  1024. AddRemoveTagsForm,
  1025. CustomFieldModelBulkEditForm
  1026. ):
  1027. pk = forms.ModelMultipleChoiceField(
  1028. queryset=ModuleBay.objects.all(),
  1029. widget=forms.MultipleHiddenInput()
  1030. )
  1031. class Meta:
  1032. nullable_fields = ['label', 'position', 'description']
  1033. class DeviceBayBulkEditForm(
  1034. form_from_model(DeviceBay, ['label', 'description']),
  1035. AddRemoveTagsForm,
  1036. CustomFieldModelBulkEditForm
  1037. ):
  1038. pk = forms.ModelMultipleChoiceField(
  1039. queryset=DeviceBay.objects.all(),
  1040. widget=forms.MultipleHiddenInput()
  1041. )
  1042. class Meta:
  1043. nullable_fields = ['label', 'description']
  1044. class InventoryItemBulkEditForm(
  1045. form_from_model(InventoryItem, ['label', 'manufacturer', 'part_id', 'description']),
  1046. AddRemoveTagsForm,
  1047. CustomFieldModelBulkEditForm
  1048. ):
  1049. pk = forms.ModelMultipleChoiceField(
  1050. queryset=InventoryItem.objects.all(),
  1051. widget=forms.MultipleHiddenInput()
  1052. )
  1053. manufacturer = DynamicModelChoiceField(
  1054. queryset=Manufacturer.objects.all(),
  1055. required=False
  1056. )
  1057. class Meta:
  1058. nullable_fields = ['label', 'manufacturer', 'part_id', 'description']
  1059. #
  1060. # Device component roles
  1061. #
  1062. class InventoryItemRoleBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
  1063. pk = forms.ModelMultipleChoiceField(
  1064. queryset=InventoryItemRole.objects.all(),
  1065. widget=forms.MultipleHiddenInput
  1066. )
  1067. color = ColorField(
  1068. required=False
  1069. )
  1070. description = forms.CharField(
  1071. max_length=200,
  1072. required=False
  1073. )
  1074. class Meta:
  1075. nullable_fields = ['color', 'description']