bulk_edit.py 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790
  1. from django import forms
  2. from django.conf import settings
  3. from django.utils.translation import gettext_lazy 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.choices import VLANQinQRoleChoices
  10. from ipam.models import ASN, VLAN, VLANGroup, VRF
  11. from netbox.choices import *
  12. from netbox.forms import NetBoxModelBulkEditForm
  13. from tenancy.models import Tenant
  14. from users.models import User
  15. from utilities.forms import BulkEditForm, add_blank_choice, form_from_model
  16. from utilities.forms.fields import ColorField, CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField
  17. from utilities.forms.rendering import FieldSet, InlineFields, TabbedGroups
  18. from utilities.forms.widgets import BulkEditNullBooleanSelect, NumberWithOptions
  19. from virtualization.models import Cluster
  20. from wireless.choices import WirelessRoleChoices
  21. from wireless.models import WirelessLAN, WirelessLANGroup
  22. __all__ = (
  23. 'CableBulkEditForm',
  24. 'ConsolePortBulkEditForm',
  25. 'ConsolePortTemplateBulkEditForm',
  26. 'ConsoleServerPortBulkEditForm',
  27. 'ConsoleServerPortTemplateBulkEditForm',
  28. 'DeviceBayBulkEditForm',
  29. 'DeviceBayTemplateBulkEditForm',
  30. 'DeviceBulkEditForm',
  31. 'DeviceRoleBulkEditForm',
  32. 'DeviceTypeBulkEditForm',
  33. 'FrontPortBulkEditForm',
  34. 'FrontPortTemplateBulkEditForm',
  35. 'InterfaceBulkEditForm',
  36. 'InterfaceTemplateBulkEditForm',
  37. 'InventoryItemBulkEditForm',
  38. 'InventoryItemRoleBulkEditForm',
  39. 'InventoryItemTemplateBulkEditForm',
  40. 'LocationBulkEditForm',
  41. 'MACAddressBulkEditForm',
  42. 'ManufacturerBulkEditForm',
  43. 'ModuleBulkEditForm',
  44. 'ModuleBayBulkEditForm',
  45. 'ModuleBayTemplateBulkEditForm',
  46. 'ModuleTypeBulkEditForm',
  47. 'PlatformBulkEditForm',
  48. 'PowerFeedBulkEditForm',
  49. 'PowerOutletBulkEditForm',
  50. 'PowerOutletTemplateBulkEditForm',
  51. 'PowerPanelBulkEditForm',
  52. 'PowerPortBulkEditForm',
  53. 'PowerPortTemplateBulkEditForm',
  54. 'RackBulkEditForm',
  55. 'RackReservationBulkEditForm',
  56. 'RackRoleBulkEditForm',
  57. 'RackTypeBulkEditForm',
  58. 'RearPortBulkEditForm',
  59. 'RearPortTemplateBulkEditForm',
  60. 'RegionBulkEditForm',
  61. 'SiteBulkEditForm',
  62. 'SiteGroupBulkEditForm',
  63. 'VirtualChassisBulkEditForm',
  64. 'VirtualDeviceContextBulkEditForm'
  65. )
  66. class RegionBulkEditForm(NetBoxModelBulkEditForm):
  67. parent = DynamicModelChoiceField(
  68. label=_('Parent'),
  69. queryset=Region.objects.all(),
  70. required=False
  71. )
  72. description = forms.CharField(
  73. label=_('Description'),
  74. max_length=200,
  75. required=False
  76. )
  77. model = Region
  78. fieldsets = (
  79. FieldSet('parent', 'description'),
  80. )
  81. nullable_fields = ('parent', 'description')
  82. class SiteGroupBulkEditForm(NetBoxModelBulkEditForm):
  83. parent = DynamicModelChoiceField(
  84. label=_('Parent'),
  85. queryset=SiteGroup.objects.all(),
  86. required=False
  87. )
  88. description = forms.CharField(
  89. label=_('Description'),
  90. max_length=200,
  91. required=False
  92. )
  93. model = SiteGroup
  94. fieldsets = (
  95. FieldSet('parent', 'description'),
  96. )
  97. nullable_fields = ('parent', 'description')
  98. class SiteBulkEditForm(NetBoxModelBulkEditForm):
  99. status = forms.ChoiceField(
  100. label=_('Status'),
  101. choices=add_blank_choice(SiteStatusChoices),
  102. required=False,
  103. initial=''
  104. )
  105. region = DynamicModelChoiceField(
  106. label=_('Region'),
  107. queryset=Region.objects.all(),
  108. required=False
  109. )
  110. group = DynamicModelChoiceField(
  111. label=_('Group'),
  112. queryset=SiteGroup.objects.all(),
  113. required=False
  114. )
  115. tenant = DynamicModelChoiceField(
  116. label=_('Tenant'),
  117. queryset=Tenant.objects.all(),
  118. required=False
  119. )
  120. asns = DynamicModelMultipleChoiceField(
  121. queryset=ASN.objects.all(),
  122. label=_('ASNs'),
  123. required=False
  124. )
  125. contact_name = forms.CharField(
  126. label=_('Contact name'),
  127. max_length=50,
  128. required=False
  129. )
  130. contact_phone = forms.CharField(
  131. label=_('Contact phone'),
  132. max_length=20,
  133. required=False
  134. )
  135. contact_email = forms.EmailField(
  136. required=False,
  137. label=_('Contact E-mail')
  138. )
  139. time_zone = TimeZoneFormField(
  140. label=_('Time zone'),
  141. choices=add_blank_choice(TimeZoneFormField().choices),
  142. required=False
  143. )
  144. description = forms.CharField(
  145. label=_('Description'),
  146. max_length=200,
  147. required=False
  148. )
  149. comments = CommentField()
  150. model = Site
  151. fieldsets = (
  152. FieldSet('status', 'region', 'group', 'tenant', 'asns', 'time_zone', 'description'),
  153. )
  154. nullable_fields = (
  155. 'region', 'group', 'tenant', 'asns', 'time_zone', 'description', 'comments',
  156. )
  157. class LocationBulkEditForm(NetBoxModelBulkEditForm):
  158. site = DynamicModelChoiceField(
  159. label=_('Site'),
  160. queryset=Site.objects.all(),
  161. required=False
  162. )
  163. parent = DynamicModelChoiceField(
  164. label=_('Parent'),
  165. queryset=Location.objects.all(),
  166. required=False,
  167. query_params={
  168. 'site_id': '$site'
  169. }
  170. )
  171. status = forms.ChoiceField(
  172. label=_('Status'),
  173. choices=add_blank_choice(LocationStatusChoices),
  174. required=False,
  175. initial=''
  176. )
  177. tenant = DynamicModelChoiceField(
  178. label=_('Tenant'),
  179. queryset=Tenant.objects.all(),
  180. required=False
  181. )
  182. description = forms.CharField(
  183. label=_('Description'),
  184. max_length=200,
  185. required=False
  186. )
  187. model = Location
  188. fieldsets = (
  189. FieldSet('site', 'parent', 'status', 'tenant', 'description'),
  190. )
  191. nullable_fields = ('parent', 'tenant', 'description')
  192. class RackRoleBulkEditForm(NetBoxModelBulkEditForm):
  193. color = ColorField(
  194. label=_('Color'),
  195. required=False
  196. )
  197. description = forms.CharField(
  198. label=_('Description'),
  199. max_length=200,
  200. required=False
  201. )
  202. model = RackRole
  203. fieldsets = (
  204. FieldSet('color', 'description'),
  205. )
  206. nullable_fields = ('color', 'description')
  207. class RackTypeBulkEditForm(NetBoxModelBulkEditForm):
  208. manufacturer = DynamicModelChoiceField(
  209. label=_('Manufacturer'),
  210. queryset=Manufacturer.objects.all(),
  211. required=False
  212. )
  213. form_factor = forms.ChoiceField(
  214. label=_('Form factor'),
  215. choices=add_blank_choice(RackFormFactorChoices),
  216. required=False
  217. )
  218. width = forms.ChoiceField(
  219. label=_('Width'),
  220. choices=add_blank_choice(RackWidthChoices),
  221. required=False
  222. )
  223. u_height = forms.IntegerField(
  224. required=False,
  225. label=_('Height (U)')
  226. )
  227. starting_unit = forms.IntegerField(
  228. required=False,
  229. min_value=1
  230. )
  231. desc_units = forms.NullBooleanField(
  232. required=False,
  233. widget=BulkEditNullBooleanSelect,
  234. label=_('Descending units')
  235. )
  236. outer_width = forms.IntegerField(
  237. label=_('Outer width'),
  238. required=False,
  239. min_value=1
  240. )
  241. outer_depth = forms.IntegerField(
  242. label=_('Outer depth'),
  243. required=False,
  244. min_value=1
  245. )
  246. outer_unit = forms.ChoiceField(
  247. label=_('Outer unit'),
  248. choices=add_blank_choice(RackDimensionUnitChoices),
  249. required=False
  250. )
  251. mounting_depth = forms.IntegerField(
  252. label=_('Mounting depth'),
  253. required=False,
  254. min_value=1
  255. )
  256. weight = forms.DecimalField(
  257. label=_('Weight'),
  258. min_value=0,
  259. required=False
  260. )
  261. max_weight = forms.IntegerField(
  262. label=_('Max weight'),
  263. min_value=0,
  264. required=False
  265. )
  266. weight_unit = forms.ChoiceField(
  267. label=_('Weight unit'),
  268. choices=add_blank_choice(WeightUnitChoices),
  269. required=False,
  270. initial=''
  271. )
  272. description = forms.CharField(
  273. label=_('Description'),
  274. max_length=200,
  275. required=False
  276. )
  277. comments = CommentField()
  278. model = RackType
  279. fieldsets = (
  280. FieldSet('manufacturer', 'description', 'form_factor', 'width', 'u_height', name=_('Rack Type')),
  281. FieldSet(
  282. InlineFields('outer_width', 'outer_depth', 'outer_unit', label=_('Outer Dimensions')),
  283. InlineFields('weight', 'max_weight', 'weight_unit', label=_('Weight')),
  284. 'mounting_depth',
  285. name=_('Dimensions')
  286. ),
  287. FieldSet('starting_unit', 'desc_units', name=_('Numbering')),
  288. )
  289. nullable_fields = (
  290. 'outer_width', 'outer_depth', 'outer_unit', 'weight',
  291. 'max_weight', 'weight_unit', 'description', 'comments',
  292. )
  293. class RackBulkEditForm(NetBoxModelBulkEditForm):
  294. region = DynamicModelChoiceField(
  295. label=_('Region'),
  296. queryset=Region.objects.all(),
  297. required=False,
  298. initial_params={
  299. 'sites': '$site'
  300. }
  301. )
  302. site_group = DynamicModelChoiceField(
  303. label=_('Site group'),
  304. queryset=SiteGroup.objects.all(),
  305. required=False,
  306. initial_params={
  307. 'sites': '$site'
  308. }
  309. )
  310. site = DynamicModelChoiceField(
  311. label=_('Site'),
  312. queryset=Site.objects.all(),
  313. required=False,
  314. query_params={
  315. 'region_id': '$region',
  316. 'group_id': '$site_group',
  317. }
  318. )
  319. location = DynamicModelChoiceField(
  320. label=_('Location'),
  321. queryset=Location.objects.all(),
  322. required=False,
  323. query_params={
  324. 'site_id': '$site'
  325. }
  326. )
  327. tenant = DynamicModelChoiceField(
  328. label=_('Tenant'),
  329. queryset=Tenant.objects.all(),
  330. required=False
  331. )
  332. status = forms.ChoiceField(
  333. label=_('Status'),
  334. choices=add_blank_choice(RackStatusChoices),
  335. required=False,
  336. initial=''
  337. )
  338. role = DynamicModelChoiceField(
  339. label=_('Role'),
  340. queryset=RackRole.objects.all(),
  341. required=False
  342. )
  343. serial = forms.CharField(
  344. max_length=50,
  345. required=False,
  346. label=_('Serial Number')
  347. )
  348. asset_tag = forms.CharField(
  349. label=_('Asset tag'),
  350. max_length=50,
  351. required=False
  352. )
  353. form_factor = forms.ChoiceField(
  354. label=_('Form factor'),
  355. choices=add_blank_choice(RackFormFactorChoices),
  356. required=False
  357. )
  358. width = forms.ChoiceField(
  359. label=_('Width'),
  360. choices=add_blank_choice(RackWidthChoices),
  361. required=False
  362. )
  363. u_height = forms.IntegerField(
  364. required=False,
  365. label=_('Height (U)')
  366. )
  367. desc_units = forms.NullBooleanField(
  368. required=False,
  369. widget=BulkEditNullBooleanSelect,
  370. label=_('Descending units')
  371. )
  372. outer_width = forms.IntegerField(
  373. label=_('Outer width'),
  374. required=False,
  375. min_value=1
  376. )
  377. outer_depth = forms.IntegerField(
  378. label=_('Outer depth'),
  379. required=False,
  380. min_value=1
  381. )
  382. outer_unit = forms.ChoiceField(
  383. label=_('Outer unit'),
  384. choices=add_blank_choice(RackDimensionUnitChoices),
  385. required=False
  386. )
  387. mounting_depth = forms.IntegerField(
  388. label=_('Mounting depth'),
  389. required=False,
  390. min_value=1
  391. )
  392. airflow = forms.ChoiceField(
  393. label=_('Airflow'),
  394. choices=add_blank_choice(RackAirflowChoices),
  395. required=False
  396. )
  397. weight = forms.DecimalField(
  398. label=_('Weight'),
  399. min_value=0,
  400. required=False
  401. )
  402. max_weight = forms.IntegerField(
  403. label=_('Max weight'),
  404. min_value=0,
  405. required=False
  406. )
  407. weight_unit = forms.ChoiceField(
  408. label=_('Weight unit'),
  409. choices=add_blank_choice(WeightUnitChoices),
  410. required=False,
  411. initial=''
  412. )
  413. description = forms.CharField(
  414. label=_('Description'),
  415. max_length=200,
  416. required=False
  417. )
  418. comments = CommentField()
  419. model = Rack
  420. fieldsets = (
  421. FieldSet('status', 'role', 'tenant', 'serial', 'asset_tag', 'description', name=_('Rack')),
  422. FieldSet('region', 'site_group', 'site', 'location', name=_('Location')),
  423. FieldSet(
  424. 'form_factor', 'width', 'u_height', 'desc_units', 'airflow', 'outer_width', 'outer_depth', 'outer_unit',
  425. 'mounting_depth', name=_('Hardware')
  426. ),
  427. FieldSet('weight', 'max_weight', 'weight_unit', name=_('Weight')),
  428. )
  429. nullable_fields = (
  430. 'location', 'tenant', 'role', 'serial', 'asset_tag', 'outer_width', 'outer_depth', 'outer_unit', 'weight',
  431. 'max_weight', 'weight_unit', 'description', 'comments',
  432. )
  433. class RackReservationBulkEditForm(NetBoxModelBulkEditForm):
  434. user = forms.ModelChoiceField(
  435. label=_('User'),
  436. queryset=User.objects.order_by('username'),
  437. required=False
  438. )
  439. tenant = DynamicModelChoiceField(
  440. label=_('Tenant'),
  441. queryset=Tenant.objects.all(),
  442. required=False
  443. )
  444. description = forms.CharField(
  445. label=_('Description'),
  446. max_length=200,
  447. required=False
  448. )
  449. comments = CommentField()
  450. model = RackReservation
  451. fieldsets = (
  452. FieldSet('user', 'tenant', 'description'),
  453. )
  454. nullable_fields = ('comments',)
  455. class ManufacturerBulkEditForm(NetBoxModelBulkEditForm):
  456. description = forms.CharField(
  457. label=_('Description'),
  458. max_length=200,
  459. required=False
  460. )
  461. model = Manufacturer
  462. fieldsets = (
  463. FieldSet('description'),
  464. )
  465. nullable_fields = ('description',)
  466. class DeviceTypeBulkEditForm(NetBoxModelBulkEditForm):
  467. manufacturer = DynamicModelChoiceField(
  468. label=_('Manufacturer'),
  469. queryset=Manufacturer.objects.all(),
  470. required=False
  471. )
  472. default_platform = DynamicModelChoiceField(
  473. label=_('Default platform'),
  474. queryset=Platform.objects.all(),
  475. required=False
  476. )
  477. part_number = forms.CharField(
  478. label=_('Part number'),
  479. required=False
  480. )
  481. u_height = forms.IntegerField(
  482. label=_('U height'),
  483. min_value=0,
  484. required=False
  485. )
  486. is_full_depth = forms.NullBooleanField(
  487. required=False,
  488. widget=BulkEditNullBooleanSelect(),
  489. label=_('Is full depth')
  490. )
  491. exclude_from_utilization = forms.NullBooleanField(
  492. required=False,
  493. widget=BulkEditNullBooleanSelect(),
  494. label=_('Exclude from utilization')
  495. )
  496. airflow = forms.ChoiceField(
  497. label=_('Airflow'),
  498. choices=add_blank_choice(DeviceAirflowChoices),
  499. required=False
  500. )
  501. weight = forms.DecimalField(
  502. label=_('Weight'),
  503. min_value=0,
  504. required=False
  505. )
  506. weight_unit = forms.ChoiceField(
  507. label=_('Weight unit'),
  508. choices=add_blank_choice(WeightUnitChoices),
  509. required=False,
  510. initial=''
  511. )
  512. description = forms.CharField(
  513. label=_('Description'),
  514. max_length=200,
  515. required=False
  516. )
  517. comments = CommentField()
  518. model = DeviceType
  519. fieldsets = (
  520. FieldSet(
  521. 'manufacturer', 'default_platform', 'part_number', 'u_height', 'exclude_from_utilization', 'is_full_depth',
  522. 'airflow', 'description', name=_('Device Type')
  523. ),
  524. FieldSet('weight', 'weight_unit', name=_('Weight')),
  525. )
  526. nullable_fields = ('part_number', 'airflow', 'weight', 'weight_unit', 'description', 'comments')
  527. class ModuleTypeBulkEditForm(NetBoxModelBulkEditForm):
  528. manufacturer = DynamicModelChoiceField(
  529. label=_('Manufacturer'),
  530. queryset=Manufacturer.objects.all(),
  531. required=False
  532. )
  533. part_number = forms.CharField(
  534. label=_('Part number'),
  535. required=False
  536. )
  537. airflow = forms.ChoiceField(
  538. label=_('Airflow'),
  539. choices=add_blank_choice(ModuleAirflowChoices),
  540. required=False
  541. )
  542. weight = forms.DecimalField(
  543. label=_('Weight'),
  544. min_value=0,
  545. required=False
  546. )
  547. weight_unit = forms.ChoiceField(
  548. label=_('Weight unit'),
  549. choices=add_blank_choice(WeightUnitChoices),
  550. required=False,
  551. initial=''
  552. )
  553. description = forms.CharField(
  554. label=_('Description'),
  555. max_length=200,
  556. required=False
  557. )
  558. comments = CommentField()
  559. model = ModuleType
  560. fieldsets = (
  561. FieldSet('manufacturer', 'part_number', 'description', name=_('Module Type')),
  562. FieldSet(
  563. 'airflow',
  564. InlineFields('weight', 'max_weight', 'weight_unit', label=_('Weight')),
  565. name=_('Chassis')
  566. ),
  567. )
  568. nullable_fields = ('part_number', 'weight', 'weight_unit', 'description', 'comments')
  569. class DeviceRoleBulkEditForm(NetBoxModelBulkEditForm):
  570. color = ColorField(
  571. label=_('Color'),
  572. required=False
  573. )
  574. vm_role = forms.NullBooleanField(
  575. required=False,
  576. widget=BulkEditNullBooleanSelect,
  577. label=_('VM role')
  578. )
  579. config_template = DynamicModelChoiceField(
  580. label=_('Config template'),
  581. queryset=ConfigTemplate.objects.all(),
  582. required=False
  583. )
  584. description = forms.CharField(
  585. label=_('Description'),
  586. max_length=200,
  587. required=False
  588. )
  589. model = DeviceRole
  590. fieldsets = (
  591. FieldSet('color', 'vm_role', 'config_template', 'description'),
  592. )
  593. nullable_fields = ('color', 'config_template', 'description')
  594. class PlatformBulkEditForm(NetBoxModelBulkEditForm):
  595. manufacturer = DynamicModelChoiceField(
  596. label=_('Manufacturer'),
  597. queryset=Manufacturer.objects.all(),
  598. required=False
  599. )
  600. config_template = DynamicModelChoiceField(
  601. label=_('Config template'),
  602. queryset=ConfigTemplate.objects.all(),
  603. required=False
  604. )
  605. description = forms.CharField(
  606. label=_('Description'),
  607. max_length=200,
  608. required=False
  609. )
  610. model = Platform
  611. fieldsets = (
  612. FieldSet('manufacturer', 'config_template', 'description'),
  613. )
  614. nullable_fields = ('manufacturer', 'config_template', 'description')
  615. class DeviceBulkEditForm(NetBoxModelBulkEditForm):
  616. manufacturer = DynamicModelChoiceField(
  617. label=_('Manufacturer'),
  618. queryset=Manufacturer.objects.all(),
  619. required=False
  620. )
  621. device_type = DynamicModelChoiceField(
  622. label=_('Device type'),
  623. queryset=DeviceType.objects.all(),
  624. required=False,
  625. context={
  626. 'parent': 'manufacturer',
  627. },
  628. query_params={
  629. 'manufacturer_id': '$manufacturer'
  630. }
  631. )
  632. role = DynamicModelChoiceField(
  633. label=_('Device role'),
  634. queryset=DeviceRole.objects.all(),
  635. required=False
  636. )
  637. site = DynamicModelChoiceField(
  638. label=_('Site'),
  639. queryset=Site.objects.all(),
  640. required=False
  641. )
  642. location = DynamicModelChoiceField(
  643. label=_('Location'),
  644. queryset=Location.objects.all(),
  645. required=False,
  646. query_params={
  647. 'site_id': '$site'
  648. }
  649. )
  650. tenant = DynamicModelChoiceField(
  651. label=_('Tenant'),
  652. queryset=Tenant.objects.all(),
  653. required=False
  654. )
  655. platform = DynamicModelChoiceField(
  656. label=_('Platform'),
  657. queryset=Platform.objects.all(),
  658. required=False
  659. )
  660. status = forms.ChoiceField(
  661. label=_('Status'),
  662. choices=add_blank_choice(DeviceStatusChoices),
  663. required=False
  664. )
  665. airflow = forms.ChoiceField(
  666. label=_('Airflow'),
  667. choices=add_blank_choice(DeviceAirflowChoices),
  668. required=False
  669. )
  670. serial = forms.CharField(
  671. max_length=50,
  672. required=False,
  673. label=_('Serial Number')
  674. )
  675. description = forms.CharField(
  676. label=_('Description'),
  677. max_length=200,
  678. required=False
  679. )
  680. config_template = DynamicModelChoiceField(
  681. label=_('Config template'),
  682. queryset=ConfigTemplate.objects.all(),
  683. required=False
  684. )
  685. cluster = DynamicModelChoiceField(
  686. label=_('Cluster'),
  687. queryset=Cluster.objects.all(),
  688. required=False,
  689. query_params={
  690. 'site_id': ['$site', 'null']
  691. },
  692. )
  693. comments = CommentField()
  694. model = Device
  695. fieldsets = (
  696. FieldSet('role', 'status', 'tenant', 'platform', 'description', name=_('Device')),
  697. FieldSet('site', 'location', name=_('Location')),
  698. FieldSet('manufacturer', 'device_type', 'airflow', 'serial', name=_('Hardware')),
  699. FieldSet('config_template', name=_('Configuration')),
  700. FieldSet('cluster', name=_('Virtualization')),
  701. )
  702. nullable_fields = (
  703. 'location', 'tenant', 'platform', 'serial', 'airflow', 'description', 'cluster', 'comments',
  704. )
  705. class ModuleBulkEditForm(NetBoxModelBulkEditForm):
  706. manufacturer = DynamicModelChoiceField(
  707. label=_('Manufacturer'),
  708. queryset=Manufacturer.objects.all(),
  709. required=False
  710. )
  711. module_type = DynamicModelChoiceField(
  712. label=_('Module type'),
  713. queryset=ModuleType.objects.all(),
  714. required=False,
  715. query_params={
  716. 'manufacturer_id': '$manufacturer'
  717. },
  718. context={
  719. 'parent': 'manufacturer',
  720. }
  721. )
  722. status = forms.ChoiceField(
  723. label=_('Status'),
  724. choices=add_blank_choice(ModuleStatusChoices),
  725. required=False,
  726. initial=''
  727. )
  728. serial = forms.CharField(
  729. max_length=50,
  730. required=False,
  731. label=_('Serial Number')
  732. )
  733. description = forms.CharField(
  734. label=_('Description'),
  735. max_length=200,
  736. required=False
  737. )
  738. comments = CommentField()
  739. model = Module
  740. fieldsets = (
  741. FieldSet('manufacturer', 'module_type', 'status', 'serial', 'description'),
  742. )
  743. nullable_fields = ('serial', 'description', 'comments')
  744. class CableBulkEditForm(NetBoxModelBulkEditForm):
  745. type = forms.ChoiceField(
  746. label=_('Type'),
  747. choices=add_blank_choice(CableTypeChoices),
  748. required=False,
  749. initial=''
  750. )
  751. status = forms.ChoiceField(
  752. label=_('Status'),
  753. choices=add_blank_choice(LinkStatusChoices),
  754. required=False,
  755. initial=''
  756. )
  757. tenant = DynamicModelChoiceField(
  758. label=_('Tenant'),
  759. queryset=Tenant.objects.all(),
  760. required=False
  761. )
  762. label = forms.CharField(
  763. label=_('Label'),
  764. max_length=100,
  765. required=False
  766. )
  767. color = ColorField(
  768. label=_('Color'),
  769. required=False
  770. )
  771. length = forms.DecimalField(
  772. label=_('Length'),
  773. min_value=0,
  774. required=False
  775. )
  776. length_unit = forms.ChoiceField(
  777. label=_('Length unit'),
  778. choices=add_blank_choice(CableLengthUnitChoices),
  779. required=False,
  780. initial=''
  781. )
  782. description = forms.CharField(
  783. label=_('Description'),
  784. max_length=200,
  785. required=False
  786. )
  787. comments = CommentField()
  788. model = Cable
  789. fieldsets = (
  790. FieldSet('type', 'status', 'tenant', 'label', 'description'),
  791. FieldSet('color', 'length', 'length_unit', name=_('Attributes')),
  792. )
  793. nullable_fields = (
  794. 'type', 'status', 'tenant', 'label', 'color', 'length', 'description', 'comments',
  795. )
  796. class VirtualChassisBulkEditForm(NetBoxModelBulkEditForm):
  797. domain = forms.CharField(
  798. label=_('Domain'),
  799. max_length=30,
  800. required=False
  801. )
  802. description = forms.CharField(
  803. label=_('Description'),
  804. max_length=200,
  805. required=False
  806. )
  807. comments = CommentField()
  808. model = VirtualChassis
  809. fieldsets = (
  810. FieldSet('domain', 'description'),
  811. )
  812. nullable_fields = ('domain', 'description', 'comments')
  813. class PowerPanelBulkEditForm(NetBoxModelBulkEditForm):
  814. region = DynamicModelChoiceField(
  815. label=_('Region'),
  816. queryset=Region.objects.all(),
  817. required=False,
  818. initial_params={
  819. 'sites': '$site'
  820. }
  821. )
  822. site_group = DynamicModelChoiceField(
  823. label=_('Site group'),
  824. queryset=SiteGroup.objects.all(),
  825. required=False,
  826. initial_params={
  827. 'sites': '$site'
  828. }
  829. )
  830. site = DynamicModelChoiceField(
  831. label=_('Site'),
  832. queryset=Site.objects.all(),
  833. required=False,
  834. query_params={
  835. 'region_id': '$region',
  836. 'group_id': '$site_group',
  837. }
  838. )
  839. location = DynamicModelChoiceField(
  840. label=_('Location'),
  841. queryset=Location.objects.all(),
  842. required=False,
  843. query_params={
  844. 'site_id': '$site'
  845. }
  846. )
  847. description = forms.CharField(
  848. label=_('Description'),
  849. max_length=200,
  850. required=False
  851. )
  852. comments = CommentField()
  853. model = PowerPanel
  854. fieldsets = (
  855. FieldSet('region', 'site_group', 'site', 'location', 'description'),
  856. )
  857. nullable_fields = ('location', 'description', 'comments')
  858. class PowerFeedBulkEditForm(NetBoxModelBulkEditForm):
  859. power_panel = DynamicModelChoiceField(
  860. label=_('Power panel'),
  861. queryset=PowerPanel.objects.all(),
  862. required=False
  863. )
  864. rack = DynamicModelChoiceField(
  865. label=_('Rack'),
  866. queryset=Rack.objects.all(),
  867. required=False,
  868. )
  869. status = forms.ChoiceField(
  870. label=_('Status'),
  871. choices=add_blank_choice(PowerFeedStatusChoices),
  872. required=False,
  873. initial=''
  874. )
  875. type = forms.ChoiceField(
  876. label=_('Type'),
  877. choices=add_blank_choice(PowerFeedTypeChoices),
  878. required=False,
  879. initial=''
  880. )
  881. supply = forms.ChoiceField(
  882. label=_('Supply'),
  883. choices=add_blank_choice(PowerFeedSupplyChoices),
  884. required=False,
  885. initial=''
  886. )
  887. phase = forms.ChoiceField(
  888. label=_('Phase'),
  889. choices=add_blank_choice(PowerFeedPhaseChoices),
  890. required=False,
  891. initial=''
  892. )
  893. voltage = forms.IntegerField(
  894. label=_('Voltage'),
  895. required=False
  896. )
  897. amperage = forms.IntegerField(
  898. label=_('Amperage'),
  899. required=False
  900. )
  901. max_utilization = forms.IntegerField(
  902. label=_('Max utilization'),
  903. required=False
  904. )
  905. mark_connected = forms.NullBooleanField(
  906. label=_('Mark connected'),
  907. required=False,
  908. widget=BulkEditNullBooleanSelect
  909. )
  910. tenant = DynamicModelChoiceField(
  911. queryset=Tenant.objects.all(),
  912. required=False
  913. )
  914. description = forms.CharField(
  915. label=_('Description'),
  916. max_length=200,
  917. required=False
  918. )
  919. comments = CommentField()
  920. model = PowerFeed
  921. fieldsets = (
  922. FieldSet('power_panel', 'rack', 'status', 'type', 'mark_connected', 'description', 'tenant'),
  923. FieldSet('supply', 'phase', 'voltage', 'amperage', 'max_utilization', name=_('Power'))
  924. )
  925. nullable_fields = ('location', 'tenant', 'description', 'comments')
  926. #
  927. # Device component templates
  928. #
  929. class ConsolePortTemplateBulkEditForm(BulkEditForm):
  930. pk = forms.ModelMultipleChoiceField(
  931. queryset=ConsolePortTemplate.objects.all(),
  932. widget=forms.MultipleHiddenInput()
  933. )
  934. label = forms.CharField(
  935. label=_('Label'),
  936. max_length=64,
  937. required=False
  938. )
  939. type = forms.ChoiceField(
  940. label=_('Type'),
  941. choices=add_blank_choice(ConsolePortTypeChoices),
  942. required=False
  943. )
  944. nullable_fields = ('label', 'type', 'description')
  945. class ConsoleServerPortTemplateBulkEditForm(BulkEditForm):
  946. pk = forms.ModelMultipleChoiceField(
  947. queryset=ConsoleServerPortTemplate.objects.all(),
  948. widget=forms.MultipleHiddenInput()
  949. )
  950. label = forms.CharField(
  951. label=_('Label'),
  952. max_length=64,
  953. required=False
  954. )
  955. type = forms.ChoiceField(
  956. label=_('Type'),
  957. choices=add_blank_choice(ConsolePortTypeChoices),
  958. required=False
  959. )
  960. description = forms.CharField(
  961. label=_('Description'),
  962. required=False
  963. )
  964. nullable_fields = ('label', 'type', 'description')
  965. class PowerPortTemplateBulkEditForm(BulkEditForm):
  966. pk = forms.ModelMultipleChoiceField(
  967. queryset=PowerPortTemplate.objects.all(),
  968. widget=forms.MultipleHiddenInput()
  969. )
  970. label = forms.CharField(
  971. label=_('Label'),
  972. max_length=64,
  973. required=False
  974. )
  975. type = forms.ChoiceField(
  976. label=_('Type'),
  977. choices=add_blank_choice(PowerPortTypeChoices),
  978. required=False
  979. )
  980. maximum_draw = forms.IntegerField(
  981. label=_('Maximum draw'),
  982. min_value=1,
  983. required=False,
  984. help_text=_("Maximum power draw (watts)")
  985. )
  986. allocated_draw = forms.IntegerField(
  987. label=_('Allocated draw'),
  988. min_value=1,
  989. required=False,
  990. help_text=_("Allocated power draw (watts)")
  991. )
  992. description = forms.CharField(
  993. label=_('Description'),
  994. required=False
  995. )
  996. nullable_fields = ('label', 'type', 'maximum_draw', 'allocated_draw', 'description')
  997. class PowerOutletTemplateBulkEditForm(BulkEditForm):
  998. pk = forms.ModelMultipleChoiceField(
  999. queryset=PowerOutletTemplate.objects.all(),
  1000. widget=forms.MultipleHiddenInput()
  1001. )
  1002. device_type = forms.ModelChoiceField(
  1003. label=_('Device type'),
  1004. queryset=DeviceType.objects.all(),
  1005. required=False,
  1006. disabled=True,
  1007. widget=forms.HiddenInput()
  1008. )
  1009. label = forms.CharField(
  1010. label=_('Label'),
  1011. max_length=64,
  1012. required=False
  1013. )
  1014. type = forms.ChoiceField(
  1015. label=_('Type'),
  1016. choices=add_blank_choice(PowerOutletTypeChoices),
  1017. required=False
  1018. )
  1019. power_port = forms.ModelChoiceField(
  1020. label=_('Power port'),
  1021. queryset=PowerPortTemplate.objects.all(),
  1022. required=False
  1023. )
  1024. feed_leg = forms.ChoiceField(
  1025. label=_('Feed leg'),
  1026. choices=add_blank_choice(PowerOutletFeedLegChoices),
  1027. required=False
  1028. )
  1029. description = forms.CharField(
  1030. label=_('Description'),
  1031. required=False
  1032. )
  1033. nullable_fields = ('label', 'type', 'power_port', 'feed_leg', 'description')
  1034. def __init__(self, *args, **kwargs):
  1035. super().__init__(*args, **kwargs)
  1036. # Limit power_port queryset to PowerPortTemplates which belong to the parent DeviceType
  1037. if 'device_type' in self.initial:
  1038. device_type = DeviceType.objects.filter(pk=self.initial['device_type']).first()
  1039. self.fields['power_port'].queryset = PowerPortTemplate.objects.filter(device_type=device_type)
  1040. else:
  1041. self.fields['power_port'].choices = ()
  1042. self.fields['power_port'].widget.attrs['disabled'] = True
  1043. class InterfaceTemplateBulkEditForm(BulkEditForm):
  1044. pk = forms.ModelMultipleChoiceField(
  1045. queryset=InterfaceTemplate.objects.all(),
  1046. widget=forms.MultipleHiddenInput()
  1047. )
  1048. label = forms.CharField(
  1049. label=_('Label'),
  1050. max_length=64,
  1051. required=False
  1052. )
  1053. type = forms.ChoiceField(
  1054. label=_('Type'),
  1055. choices=add_blank_choice(InterfaceTypeChoices),
  1056. required=False
  1057. )
  1058. enabled = forms.NullBooleanField(
  1059. label=_('Enabled'),
  1060. required=False,
  1061. widget=BulkEditNullBooleanSelect
  1062. )
  1063. mgmt_only = forms.NullBooleanField(
  1064. required=False,
  1065. widget=BulkEditNullBooleanSelect,
  1066. label=_('Management only')
  1067. )
  1068. description = forms.CharField(
  1069. label=_('Description'),
  1070. required=False
  1071. )
  1072. poe_mode = forms.ChoiceField(
  1073. choices=add_blank_choice(InterfacePoEModeChoices),
  1074. required=False,
  1075. initial='',
  1076. label=_('PoE mode')
  1077. )
  1078. poe_type = forms.ChoiceField(
  1079. choices=add_blank_choice(InterfacePoETypeChoices),
  1080. required=False,
  1081. initial='',
  1082. label=_('PoE type')
  1083. )
  1084. rf_role = forms.ChoiceField(
  1085. choices=add_blank_choice(WirelessRoleChoices),
  1086. required=False,
  1087. initial='',
  1088. label=_('Wireless role')
  1089. )
  1090. nullable_fields = ('label', 'description', 'poe_mode', 'poe_type', 'rf_role')
  1091. class FrontPortTemplateBulkEditForm(BulkEditForm):
  1092. pk = forms.ModelMultipleChoiceField(
  1093. queryset=FrontPortTemplate.objects.all(),
  1094. widget=forms.MultipleHiddenInput()
  1095. )
  1096. label = forms.CharField(
  1097. label=_('Label'),
  1098. max_length=64,
  1099. required=False
  1100. )
  1101. type = forms.ChoiceField(
  1102. label=_('Type'),
  1103. choices=add_blank_choice(PortTypeChoices),
  1104. required=False
  1105. )
  1106. color = ColorField(
  1107. label=_('Color'),
  1108. required=False
  1109. )
  1110. description = forms.CharField(
  1111. label=_('Description'),
  1112. required=False
  1113. )
  1114. nullable_fields = ('description',)
  1115. class RearPortTemplateBulkEditForm(BulkEditForm):
  1116. pk = forms.ModelMultipleChoiceField(
  1117. queryset=RearPortTemplate.objects.all(),
  1118. widget=forms.MultipleHiddenInput()
  1119. )
  1120. label = forms.CharField(
  1121. label=_('Label'),
  1122. max_length=64,
  1123. required=False
  1124. )
  1125. type = forms.ChoiceField(
  1126. label=_('Type'),
  1127. choices=add_blank_choice(PortTypeChoices),
  1128. required=False
  1129. )
  1130. color = ColorField(
  1131. label=_('Color'),
  1132. required=False
  1133. )
  1134. description = forms.CharField(
  1135. label=_('Description'),
  1136. required=False
  1137. )
  1138. nullable_fields = ('description',)
  1139. class ModuleBayTemplateBulkEditForm(BulkEditForm):
  1140. pk = forms.ModelMultipleChoiceField(
  1141. queryset=ModuleBayTemplate.objects.all(),
  1142. widget=forms.MultipleHiddenInput()
  1143. )
  1144. label = forms.CharField(
  1145. label=_('Label'),
  1146. max_length=64,
  1147. required=False
  1148. )
  1149. description = forms.CharField(
  1150. label=_('Description'),
  1151. required=False
  1152. )
  1153. nullable_fields = ('label', 'position', 'description')
  1154. class DeviceBayTemplateBulkEditForm(BulkEditForm):
  1155. pk = forms.ModelMultipleChoiceField(
  1156. queryset=DeviceBayTemplate.objects.all(),
  1157. widget=forms.MultipleHiddenInput()
  1158. )
  1159. label = forms.CharField(
  1160. label=_('Label'),
  1161. max_length=64,
  1162. required=False
  1163. )
  1164. description = forms.CharField(
  1165. label=_('Description'),
  1166. required=False
  1167. )
  1168. nullable_fields = ('label', 'description')
  1169. class InventoryItemTemplateBulkEditForm(BulkEditForm):
  1170. pk = forms.ModelMultipleChoiceField(
  1171. queryset=InventoryItemTemplate.objects.all(),
  1172. widget=forms.MultipleHiddenInput()
  1173. )
  1174. label = forms.CharField(
  1175. label=_('Label'),
  1176. max_length=64,
  1177. required=False
  1178. )
  1179. description = forms.CharField(
  1180. label=_('Description'),
  1181. required=False
  1182. )
  1183. role = DynamicModelChoiceField(
  1184. label=_('Role'),
  1185. queryset=InventoryItemRole.objects.all(),
  1186. required=False
  1187. )
  1188. manufacturer = DynamicModelChoiceField(
  1189. label=_('Manufacturer'),
  1190. queryset=Manufacturer.objects.all(),
  1191. required=False
  1192. )
  1193. nullable_fields = ('label', 'role', 'manufacturer', 'part_id', 'description')
  1194. #
  1195. # Device components
  1196. #
  1197. class ComponentBulkEditForm(NetBoxModelBulkEditForm):
  1198. device = forms.ModelChoiceField(
  1199. label=_('Device'),
  1200. queryset=Device.objects.all(),
  1201. required=False,
  1202. disabled=True,
  1203. widget=forms.HiddenInput()
  1204. )
  1205. module = forms.ModelChoiceField(
  1206. label=_('Module'),
  1207. queryset=Module.objects.all(),
  1208. required=False
  1209. )
  1210. def __init__(self, *args, initial=None, **kwargs):
  1211. try:
  1212. self.device_id = int(initial.get('device'))
  1213. except (TypeError, ValueError):
  1214. self.device_id = None
  1215. super().__init__(*args, initial=initial, **kwargs)
  1216. # Limit module queryset to Modules which belong to the parent Device
  1217. if self.device_id:
  1218. device = Device.objects.filter(pk=self.device_id).first()
  1219. self.fields['module'].queryset = Module.objects.filter(device=device)
  1220. else:
  1221. self.fields['module'].choices = ()
  1222. self.fields['module'].widget.attrs['disabled'] = True
  1223. class ConsolePortBulkEditForm(
  1224. ComponentBulkEditForm,
  1225. form_from_model(ConsolePort, ['label', 'type', 'speed', 'mark_connected', 'description'])
  1226. ):
  1227. mark_connected = forms.NullBooleanField(
  1228. label=_('Mark connected'),
  1229. required=False,
  1230. widget=BulkEditNullBooleanSelect
  1231. )
  1232. model = ConsolePort
  1233. fieldsets = (
  1234. FieldSet('module', 'type', 'label', 'speed', 'description', 'mark_connected'),
  1235. )
  1236. nullable_fields = ('module', 'label', 'description')
  1237. class ConsoleServerPortBulkEditForm(
  1238. ComponentBulkEditForm,
  1239. form_from_model(ConsoleServerPort, ['label', 'type', 'speed', 'mark_connected', 'description'])
  1240. ):
  1241. mark_connected = forms.NullBooleanField(
  1242. label=_('Mark connected'),
  1243. required=False,
  1244. widget=BulkEditNullBooleanSelect
  1245. )
  1246. model = ConsoleServerPort
  1247. fieldsets = (
  1248. FieldSet('module', 'type', 'label', 'speed', 'description', 'mark_connected'),
  1249. )
  1250. nullable_fields = ('module', 'label', 'description')
  1251. class PowerPortBulkEditForm(
  1252. ComponentBulkEditForm,
  1253. form_from_model(PowerPort, ['label', 'type', 'maximum_draw', 'allocated_draw', 'mark_connected', 'description'])
  1254. ):
  1255. mark_connected = forms.NullBooleanField(
  1256. label=_('Mark connected'),
  1257. required=False,
  1258. widget=BulkEditNullBooleanSelect
  1259. )
  1260. model = PowerPort
  1261. fieldsets = (
  1262. FieldSet('module', 'type', 'label', 'description', 'mark_connected'),
  1263. FieldSet('maximum_draw', 'allocated_draw', name=_('Power')),
  1264. )
  1265. nullable_fields = ('module', 'label', 'description', 'maximum_draw', 'allocated_draw')
  1266. class PowerOutletBulkEditForm(
  1267. ComponentBulkEditForm,
  1268. form_from_model(PowerOutlet, ['label', 'type', 'color', 'feed_leg', 'power_port', 'mark_connected', 'description'])
  1269. ):
  1270. mark_connected = forms.NullBooleanField(
  1271. label=_('Mark connected'),
  1272. required=False,
  1273. widget=BulkEditNullBooleanSelect
  1274. )
  1275. model = PowerOutlet
  1276. fieldsets = (
  1277. FieldSet('module', 'type', 'label', 'description', 'mark_connected', 'color'),
  1278. FieldSet('feed_leg', 'power_port', name=_('Power')),
  1279. )
  1280. nullable_fields = ('module', 'label', 'type', 'feed_leg', 'power_port', 'description')
  1281. def __init__(self, *args, **kwargs):
  1282. super().__init__(*args, **kwargs)
  1283. # Limit power_port queryset to PowerPorts which belong to the parent Device
  1284. if self.device_id:
  1285. device = Device.objects.filter(pk=self.device_id).first()
  1286. self.fields['power_port'].queryset = PowerPort.objects.filter(device=device)
  1287. else:
  1288. self.fields['power_port'].choices = ()
  1289. self.fields['power_port'].widget.attrs['disabled'] = True
  1290. class InterfaceBulkEditForm(
  1291. ComponentBulkEditForm,
  1292. form_from_model(Interface, [
  1293. 'label', 'type', 'parent', 'bridge', 'lag', 'speed', 'duplex', 'wwn', 'mtu', 'mgmt_only', 'mark_connected',
  1294. 'description', 'mode', 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power',
  1295. 'wireless_lans'
  1296. ])
  1297. ):
  1298. enabled = forms.NullBooleanField(
  1299. label=_('Enabled'),
  1300. required=False,
  1301. widget=BulkEditNullBooleanSelect
  1302. )
  1303. parent = DynamicModelChoiceField(
  1304. label=_('Parent'),
  1305. queryset=Interface.objects.all(),
  1306. required=False,
  1307. query_params={
  1308. 'virtual_chassis_member_id': '$device',
  1309. }
  1310. )
  1311. bridge = DynamicModelChoiceField(
  1312. label=_('Bridge'),
  1313. queryset=Interface.objects.all(),
  1314. required=False,
  1315. query_params={
  1316. 'virtual_chassis_member_id': '$device',
  1317. }
  1318. )
  1319. lag = DynamicModelChoiceField(
  1320. queryset=Interface.objects.all(),
  1321. required=False,
  1322. query_params={
  1323. 'type': 'lag',
  1324. 'virtual_chassis_member_id': '$device',
  1325. },
  1326. label=_('LAG')
  1327. )
  1328. vdcs = DynamicModelMultipleChoiceField(
  1329. queryset=VirtualDeviceContext.objects.all(),
  1330. required=False,
  1331. label=_('Virtual device contexts'),
  1332. query_params={
  1333. 'device_id': '$device',
  1334. }
  1335. )
  1336. speed = forms.IntegerField(
  1337. label=_('Speed'),
  1338. required=False,
  1339. widget=NumberWithOptions(
  1340. options=InterfaceSpeedChoices
  1341. )
  1342. )
  1343. mgmt_only = forms.NullBooleanField(
  1344. required=False,
  1345. widget=BulkEditNullBooleanSelect,
  1346. label=_('Management only')
  1347. )
  1348. poe_mode = forms.ChoiceField(
  1349. choices=add_blank_choice(InterfacePoEModeChoices),
  1350. required=False,
  1351. initial='',
  1352. label=_('PoE mode')
  1353. )
  1354. poe_type = forms.ChoiceField(
  1355. choices=add_blank_choice(InterfacePoETypeChoices),
  1356. required=False,
  1357. initial='',
  1358. label=_('PoE type')
  1359. )
  1360. mark_connected = forms.NullBooleanField(
  1361. label=_('Mark connected'),
  1362. required=False,
  1363. widget=BulkEditNullBooleanSelect
  1364. )
  1365. mode = forms.ChoiceField(
  1366. label=_('Mode'),
  1367. choices=add_blank_choice(InterfaceModeChoices),
  1368. required=False,
  1369. initial=''
  1370. )
  1371. vlan_group = DynamicModelChoiceField(
  1372. queryset=VLANGroup.objects.all(),
  1373. required=False,
  1374. label=_('VLAN group')
  1375. )
  1376. untagged_vlan = DynamicModelChoiceField(
  1377. queryset=VLAN.objects.all(),
  1378. required=False,
  1379. query_params={
  1380. 'group_id': '$vlan_group',
  1381. 'available_on_device': '$device',
  1382. },
  1383. label=_('Untagged VLAN')
  1384. )
  1385. tagged_vlans = DynamicModelMultipleChoiceField(
  1386. queryset=VLAN.objects.all(),
  1387. required=False,
  1388. query_params={
  1389. 'group_id': '$vlan_group',
  1390. 'available_on_device': '$device',
  1391. },
  1392. label=_('Tagged VLANs')
  1393. )
  1394. add_tagged_vlans = DynamicModelMultipleChoiceField(
  1395. label=_('Add tagged VLANs'),
  1396. queryset=VLAN.objects.all(),
  1397. required=False,
  1398. query_params={
  1399. 'group_id': '$vlan_group',
  1400. 'available_on_device': '$device',
  1401. },
  1402. )
  1403. remove_tagged_vlans = DynamicModelMultipleChoiceField(
  1404. label=_('Remove tagged VLANs'),
  1405. queryset=VLAN.objects.all(),
  1406. required=False,
  1407. query_params={
  1408. 'group_id': '$vlan_group',
  1409. 'available_on_device': '$device',
  1410. }
  1411. )
  1412. qinq_svlan = DynamicModelChoiceField(
  1413. queryset=VLAN.objects.all(),
  1414. required=False,
  1415. label=_('Q-in-Q Service VLAN'),
  1416. query_params={
  1417. 'group_id': '$vlan_group',
  1418. 'available_on_device': '$device',
  1419. 'qinq_role': VLANQinQRoleChoices.ROLE_SERVICE,
  1420. }
  1421. )
  1422. vrf = DynamicModelChoiceField(
  1423. queryset=VRF.objects.all(),
  1424. required=False,
  1425. label=_('VRF')
  1426. )
  1427. wireless_lan_group = DynamicModelChoiceField(
  1428. queryset=WirelessLANGroup.objects.all(),
  1429. required=False,
  1430. label=_('Wireless LAN group')
  1431. )
  1432. wireless_lans = DynamicModelMultipleChoiceField(
  1433. queryset=WirelessLAN.objects.all(),
  1434. required=False,
  1435. label=_('Wireless LANs'),
  1436. query_params={
  1437. 'group_id': '$wireless_lan_group',
  1438. }
  1439. )
  1440. model = Interface
  1441. fieldsets = (
  1442. FieldSet('module', 'type', 'label', 'speed', 'duplex', 'description'),
  1443. FieldSet('vrf', 'wwn', name=_('Addressing')),
  1444. FieldSet('vdcs', 'mtu', 'tx_power', 'enabled', 'mgmt_only', 'mark_connected', name=_('Operation')),
  1445. FieldSet('poe_mode', 'poe_type', name=_('PoE')),
  1446. FieldSet('parent', 'bridge', 'lag', name=_('Related Interfaces')),
  1447. FieldSet('mode', 'vlan_group', 'untagged_vlan', 'qinq_svlan', name=_('802.1Q Switching')),
  1448. FieldSet(
  1449. TabbedGroups(
  1450. FieldSet('tagged_vlans', name=_('Assignment')),
  1451. FieldSet('add_tagged_vlans', 'remove_tagged_vlans', name=_('Add/Remove')),
  1452. ),
  1453. ),
  1454. FieldSet(
  1455. 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'wireless_lan_group', 'wireless_lans',
  1456. name=_('Wireless')
  1457. ),
  1458. )
  1459. nullable_fields = (
  1460. 'module', 'label', 'parent', 'bridge', 'lag', 'speed', 'duplex', 'wwn', 'vdcs', 'mtu', 'description',
  1461. 'poe_mode', 'poe_type', 'mode', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power',
  1462. 'untagged_vlan', 'tagged_vlans', 'qinq_svlan', 'vrf', 'wireless_lans'
  1463. )
  1464. def __init__(self, *args, **kwargs):
  1465. super().__init__(*args, **kwargs)
  1466. if not self.device_id:
  1467. # See #4523
  1468. if 'pk' in self.initial:
  1469. site = None
  1470. interfaces = Interface.objects.filter(pk__in=self.initial['pk']).prefetch_related('device__site')
  1471. # Check interface sites. First interface should set site, further interfaces will either continue the
  1472. # loop or reset back to no site and break the loop.
  1473. for interface in interfaces:
  1474. if site is None:
  1475. site = interface.device.site
  1476. elif interface.device.site is not site:
  1477. site = None
  1478. break
  1479. if site is not None:
  1480. # Query for VLANs assigned to the same site and VLANs with no site assigned (null).
  1481. self.fields['untagged_vlan'].widget.add_query_param(
  1482. 'site_id', [site.pk, settings.FILTERS_NULL_CHOICE_VALUE]
  1483. )
  1484. self.fields['tagged_vlans'].widget.add_query_param(
  1485. 'site_id', [site.pk, settings.FILTERS_NULL_CHOICE_VALUE]
  1486. )
  1487. self.fields['add_tagged_vlans'].widget.add_query_param(
  1488. 'site_id', [site.pk, settings.FILTERS_NULL_CHOICE_VALUE]
  1489. )
  1490. self.fields['remove_tagged_vlans'].widget.add_query_param(
  1491. 'site_id', [site.pk, settings.FILTERS_NULL_CHOICE_VALUE]
  1492. )
  1493. self.fields['parent'].choices = ()
  1494. self.fields['parent'].widget.attrs['disabled'] = True
  1495. self.fields['bridge'].choices = ()
  1496. self.fields['bridge'].widget.attrs['disabled'] = True
  1497. self.fields['lag'].choices = ()
  1498. self.fields['lag'].widget.attrs['disabled'] = True
  1499. def clean(self):
  1500. super().clean()
  1501. if not self.cleaned_data['mode']:
  1502. if self.cleaned_data['untagged_vlan']:
  1503. raise forms.ValidationError({'untagged_vlan': _("Interface mode must be specified to assign VLANs")})
  1504. elif self.cleaned_data['tagged_vlans']:
  1505. raise forms.ValidationError({'tagged_vlans': _("Interface mode must be specified to assign VLANs")})
  1506. # Untagged interfaces cannot be assigned tagged VLANs
  1507. elif self.cleaned_data['mode'] == InterfaceModeChoices.MODE_ACCESS and self.cleaned_data['tagged_vlans']:
  1508. raise forms.ValidationError({
  1509. 'mode': _("An access interface cannot have tagged VLANs assigned.")
  1510. })
  1511. # Remove all tagged VLAN assignments from "tagged all" interfaces
  1512. elif self.cleaned_data['mode'] == InterfaceModeChoices.MODE_TAGGED_ALL:
  1513. self.cleaned_data['tagged_vlans'] = []
  1514. class FrontPortBulkEditForm(
  1515. ComponentBulkEditForm,
  1516. form_from_model(FrontPort, ['label', 'type', 'color', 'mark_connected', 'description'])
  1517. ):
  1518. mark_connected = forms.NullBooleanField(
  1519. label=_('Mark connected'),
  1520. required=False,
  1521. widget=BulkEditNullBooleanSelect
  1522. )
  1523. model = FrontPort
  1524. fieldsets = (
  1525. FieldSet('module', 'type', 'label', 'color', 'description', 'mark_connected'),
  1526. )
  1527. nullable_fields = ('module', 'label', 'description', 'color')
  1528. class RearPortBulkEditForm(
  1529. ComponentBulkEditForm,
  1530. form_from_model(RearPort, ['label', 'type', 'color', 'mark_connected', 'description'])
  1531. ):
  1532. mark_connected = forms.NullBooleanField(
  1533. label=_('Mark connected'),
  1534. required=False,
  1535. widget=BulkEditNullBooleanSelect
  1536. )
  1537. model = RearPort
  1538. fieldsets = (
  1539. FieldSet('module', 'type', 'label', 'color', 'description', 'mark_connected'),
  1540. )
  1541. nullable_fields = ('module', 'label', 'description', 'color')
  1542. class ModuleBayBulkEditForm(
  1543. form_from_model(ModuleBay, ['label', 'position', 'description']),
  1544. NetBoxModelBulkEditForm
  1545. ):
  1546. model = ModuleBay
  1547. fieldsets = (
  1548. FieldSet('label', 'position', 'description'),
  1549. )
  1550. nullable_fields = ('label', 'position', 'description')
  1551. class DeviceBayBulkEditForm(
  1552. form_from_model(DeviceBay, ['label', 'description']),
  1553. NetBoxModelBulkEditForm
  1554. ):
  1555. model = DeviceBay
  1556. fieldsets = (
  1557. FieldSet('label', 'description'),
  1558. )
  1559. nullable_fields = ('label', 'description')
  1560. class InventoryItemBulkEditForm(
  1561. form_from_model(InventoryItem, ['label', 'role', 'manufacturer', 'part_id', 'description']),
  1562. NetBoxModelBulkEditForm
  1563. ):
  1564. device = DynamicModelChoiceField(
  1565. label=_('Device'),
  1566. queryset=Device.objects.all(),
  1567. required=False
  1568. )
  1569. role = DynamicModelChoiceField(
  1570. label=_('Role'),
  1571. queryset=InventoryItemRole.objects.all(),
  1572. required=False
  1573. )
  1574. manufacturer = DynamicModelChoiceField(
  1575. label=_('Manufacturer'),
  1576. queryset=Manufacturer.objects.all(),
  1577. required=False
  1578. )
  1579. status = forms.ChoiceField(
  1580. label=_('Status'),
  1581. choices=add_blank_choice(InventoryItemStatusChoices),
  1582. required=False,
  1583. initial=''
  1584. )
  1585. model = InventoryItem
  1586. fieldsets = (
  1587. FieldSet('device', 'label', 'role', 'manufacturer', 'part_id', 'status', 'description'),
  1588. )
  1589. nullable_fields = ('label', 'role', 'manufacturer', 'part_id', 'description')
  1590. #
  1591. # Device component roles
  1592. #
  1593. class InventoryItemRoleBulkEditForm(NetBoxModelBulkEditForm):
  1594. color = ColorField(
  1595. label=_('Color'),
  1596. required=False
  1597. )
  1598. description = forms.CharField(
  1599. label=_('Description'),
  1600. max_length=200,
  1601. required=False
  1602. )
  1603. model = InventoryItemRole
  1604. fieldsets = (
  1605. FieldSet('color', 'description'),
  1606. )
  1607. nullable_fields = ('color', 'description')
  1608. class VirtualDeviceContextBulkEditForm(NetBoxModelBulkEditForm):
  1609. device = DynamicModelChoiceField(
  1610. label=_('Device'),
  1611. queryset=Device.objects.all(),
  1612. required=False
  1613. )
  1614. status = forms.ChoiceField(
  1615. label=_('Status'),
  1616. required=False,
  1617. choices=add_blank_choice(VirtualDeviceContextStatusChoices)
  1618. )
  1619. tenant = DynamicModelChoiceField(
  1620. label=_('Tenant'),
  1621. queryset=Tenant.objects.all(),
  1622. required=False
  1623. )
  1624. model = VirtualDeviceContext
  1625. fieldsets = (
  1626. FieldSet('device', 'status', 'tenant'),
  1627. )
  1628. nullable_fields = ('device', 'tenant', )
  1629. #
  1630. # Addressing
  1631. #
  1632. class MACAddressBulkEditForm(NetBoxModelBulkEditForm):
  1633. description = forms.CharField(
  1634. label=_('Description'),
  1635. max_length=200,
  1636. required=False
  1637. )
  1638. comments = CommentField()
  1639. model = MACAddress
  1640. fieldsets = (
  1641. FieldSet('description'),
  1642. )
  1643. nullable_fields = ('description', 'comments')