bulk_edit.py 40 KB

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