bulk_edit.py 40 KB

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