Coverage for dcim/ui/panels.py: 92%

434 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-10 18:35 +0000

1from django.contrib.contenttypes.models import ContentType 

2from django.utils.translation import gettext_lazy as _ 

3 

4from netbox.ui import actions, attrs, panels 

5 

6 

7class BayTypeIncompatibilityPanel(panels.Panel): 

8 """ 

9 Renders a warning banner when a Module is incompatibly installed (its type's bay type set and 

10 the bay's bay type set are both non-empty and share no common members). 

11 Silently omitted when the installation is compatible or unconstrained. 

12 """ 

13 template_name = 'dcim/panels/bay_type_incompatibility.html' 

14 

15 def should_render(self, context): 

16 from dcim.models import Module, ModuleBay 

17 obj = context.get('object') 

18 if isinstance(obj, Module): 

19 return not obj.is_bay_compatible 

20 if isinstance(obj, ModuleBay): 

21 return not obj.is_module_compatible 

22 return False 

23 

24 def get_context(self, context): 

25 from dcim.models import Module 

26 ctx = super().get_context(context) 

27 ctx['is_module_view'] = isinstance(context.get('object'), Module) 

28 return ctx 

29 

30 

31class SitePanel(panels.ObjectAttributesPanel): 

32 region = attrs.NestedObjectAttr('region', linkify=True) 

33 group = attrs.NestedObjectAttr('group', linkify=True) 

34 name = attrs.TextAttr('name') 

35 status = attrs.ChoiceAttr('status') 

36 tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group') 

37 facility = attrs.TextAttr('facility') 

38 description = attrs.TextAttr('description') 

39 timezone = attrs.TimezoneAttr('time_zone') 

40 physical_address = attrs.AddressAttr('physical_address', map_url=True) 

41 shipping_address = attrs.AddressAttr('shipping_address', map_url=True) 

42 gps_coordinates = attrs.GPSCoordinatesAttr() 

43 

44 

45class LocationPanel(panels.NestedGroupObjectPanel): 

46 site = attrs.RelatedObjectAttr('site', linkify=True, grouped_by='group') 

47 status = attrs.ChoiceAttr('status') 

48 tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group') 

49 facility = attrs.TextAttr('facility') 

50 

51 

52class RackDimensionsPanel(panels.ObjectAttributesPanel): 

53 form_factor = attrs.ChoiceAttr('form_factor') 

54 width = attrs.ChoiceAttr('width') 

55 height = attrs.TextAttr('u_height', format_string='{}U', label=_('Height')) 

56 outer_width = attrs.NumericAttr('outer_width', unit_accessor='get_outer_unit_display') 

57 outer_height = attrs.NumericAttr('outer_height', unit_accessor='get_outer_unit_display') 

58 outer_depth = attrs.NumericAttr('outer_depth', unit_accessor='get_outer_unit_display') 

59 mounting_depth = attrs.TextAttr('mounting_depth', format_string=_('{0} millimeters')) 

60 

61 

62class RackNumberingPanel(panels.ObjectAttributesPanel): 

63 starting_unit = attrs.TextAttr('starting_unit') 

64 desc_units = attrs.BooleanAttr('desc_units', label=_('Descending units')) 

65 

66 

67class RackPanel(panels.ObjectAttributesPanel): 

68 region = attrs.NestedObjectAttr('site.region', linkify=True) 

69 site = attrs.RelatedObjectAttr('site', linkify=True, grouped_by='group') 

70 location = attrs.NestedObjectAttr('location', linkify=True) 

71 group = attrs.RelatedObjectAttr('group', linkify=True, label=_('Rack group')) 

72 name = attrs.TextAttr('name') 

73 facility_id = attrs.TextAttr('facility_id', label=_('Facility ID')) 

74 tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group') 

75 status = attrs.ChoiceAttr('status') 

76 rack_type = attrs.RelatedObjectAttr('rack_type', linkify=True, grouped_by='manufacturer') 

77 role = attrs.RelatedObjectAttr('role', linkify=True, colored=True) 

78 description = attrs.TextAttr('description') 

79 serial = attrs.TextAttr('serial', label=_('Serial number'), style='font-monospace', copy_button=True) 

80 asset_tag = attrs.TextAttr('asset_tag', style='font-monospace', copy_button=True) 

81 airflow = attrs.ChoiceAttr('airflow') 

82 cooling_capability = attrs.ChoiceAttr('cooling_capability') 

83 cooling_capacity = attrs.TextAttr('cooling_capacity', format_string=_('{} kW')) 

84 space_utilization = attrs.UtilizationAttr('get_utilization') 

85 power_utilization = attrs.UtilizationAttr('get_power_utilization') 

86 

87 

88class RackWeightPanel(panels.ObjectAttributesPanel): 

89 weight = attrs.WeightAttr('weight') 

90 max_weight = attrs.WeightAttr('max_weight', label=_('Maximum weight')) 

91 total_weight = attrs.TemplatedAttr('total_weight', template_name='dcim/attrs/total_weight.html') 

92 

93 

94class RackRolePanel(panels.OrganizationalObjectPanel): 

95 color = attrs.ColorAttr('color') 

96 

97 

98class RackReservationPanel(panels.ObjectAttributesPanel): 

99 units = attrs.TextAttr('unit_list') 

100 unit_count = attrs.TextAttr('unit_count', label=_("Total U's")) 

101 status = attrs.ChoiceAttr('status') 

102 tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group') 

103 user = attrs.RelatedObjectAttr('user', linkify=True) 

104 description = attrs.TextAttr('description') 

105 

106 

107class RackTypePanel(panels.ObjectAttributesPanel): 

108 manufacturer = attrs.RelatedObjectAttr('manufacturer', linkify=True) 

109 model = attrs.TextAttr('model') 

110 description = attrs.TextAttr('description') 

111 cooling_capability = attrs.ChoiceAttr('cooling_capability') 

112 cooling_capacity = attrs.TextAttr('cooling_capacity', format_string=_('{} kW')) 

113 

114 

115class DevicePanel(panels.ObjectAttributesPanel): 

116 region = attrs.NestedObjectAttr('site.region', linkify=True) 

117 site = attrs.RelatedObjectAttr('site', linkify=True, grouped_by='group') 

118 location = attrs.NestedObjectAttr('location', linkify=True) 

119 rack = attrs.TemplatedAttr('rack', template_name='dcim/device/attrs/rack.html') 

120 virtual_chassis = attrs.RelatedObjectAttr('virtual_chassis', linkify=True) 

121 parent_device = attrs.TemplatedAttr('parent_bay', template_name='dcim/device/attrs/parent_device.html') 

122 gps_coordinates = attrs.GPSCoordinatesAttr() 

123 tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group') 

124 description = attrs.TextAttr('description') 

125 cooling_method = attrs.ChoiceAttr('cooling_method') 

126 airflow = attrs.ChoiceAttr('airflow') 

127 serial = attrs.TextAttr('serial', label=_('Serial number'), style='font-monospace', copy_button=True) 

128 asset_tag = attrs.TextAttr('asset_tag', style='font-monospace', copy_button=True) 

129 config_template = attrs.RelatedObjectAttr('config_template', linkify=True) 

130 

131 

132class DeviceManagementPanel(panels.ObjectAttributesPanel): 

133 title = _('Management') 

134 

135 status = attrs.ChoiceAttr('status') 

136 role = attrs.NestedObjectAttr('role', linkify=True, max_depth=3, colored=True) 

137 platform = attrs.NestedObjectAttr('platform', linkify=True, max_depth=3) 

138 primary_ip4 = attrs.TemplatedAttr( 

139 'primary_ip4', 

140 label=_('Primary IPv4'), 

141 template_name='dcim/device/attrs/ipaddress.html', 

142 ) 

143 primary_ip6 = attrs.TemplatedAttr( 

144 'primary_ip6', 

145 label=_('Primary IPv6'), 

146 template_name='dcim/device/attrs/ipaddress.html', 

147 ) 

148 oob_ip = attrs.TemplatedAttr( 

149 'oob_ip', 

150 label=_('Out-of-band IP'), 

151 template_name='dcim/device/attrs/ipaddress.html', 

152 ) 

153 cluster = attrs.RelatedObjectAttr('cluster', linkify=True) 

154 

155 

156class DeviceDeviceTypePanel(panels.ObjectAttributesPanel): 

157 title = _('Device Type') 

158 

159 manufacturer = attrs.RelatedObjectAttr('device_type.manufacturer', linkify=True) 

160 model = attrs.RelatedObjectAttr('device_type', linkify=True) 

161 height = attrs.TemplatedAttr('device_type.u_height', template_name='dcim/devicetype/attrs/height.html') 

162 front_image = attrs.ImageAttr('device_type.front_image') 

163 rear_image = attrs.ImageAttr('device_type.rear_image') 

164 

165 

166class DeviceDimensionsPanel(panels.ObjectAttributesPanel): 

167 title = _('Dimensions') 

168 

169 total_weight = attrs.TemplatedAttr('total_weight', template_name='dcim/attrs/total_weight.html') 

170 

171 

172class DeviceRolePanel(panels.NestedGroupObjectPanel): 

173 color = attrs.ColorAttr('color') 

174 vm_role = attrs.BooleanAttr('vm_role', label=_('VM role')) 

175 config_template = attrs.RelatedObjectAttr('config_template', linkify=True) 

176 

177 

178class DeviceTypePanel(panels.ObjectAttributesPanel): 

179 manufacturer = attrs.RelatedObjectAttr('manufacturer', linkify=True) 

180 model = attrs.TextAttr('model') 

181 part_number = attrs.TextAttr('part_number') 

182 default_platform = attrs.NestedObjectAttr('default_platform', linkify=True, max_depth=3) 

183 description = attrs.TextAttr('description') 

184 height = attrs.TemplatedAttr('u_height', template_name='dcim/devicetype/attrs/height.html') 

185 exclude_from_utilization = attrs.BooleanAttr('exclude_from_utilization') 

186 full_depth = attrs.BooleanAttr('is_full_depth') 

187 weight = attrs.WeightAttr('weight') 

188 subdevice_role = attrs.ChoiceAttr('subdevice_role', label=_('Parent/child')) 

189 cooling_method = attrs.ChoiceAttr('cooling_method') 

190 airflow = attrs.ChoiceAttr('airflow') 

191 end_of_life = attrs.DateTimeAttr('end_of_life', spec='date') 

192 front_image = attrs.ImageAttr('front_image') 

193 rear_image = attrs.ImageAttr('rear_image') 

194 

195 

196class ModulePanel(panels.ObjectAttributesPanel): 

197 device = attrs.RelatedObjectAttr('device', linkify=True) 

198 device_type = attrs.RelatedObjectAttr('device.device_type', linkify=True, grouped_by='manufacturer') 

199 module_bay = attrs.NestedObjectAttr('module_bay', linkify=True) 

200 status = attrs.ChoiceAttr('status') 

201 description = attrs.TextAttr('description') 

202 serial = attrs.TextAttr('serial', label=_('Serial number'), style='font-monospace', copy_button=True) 

203 asset_tag = attrs.TextAttr('asset_tag', style='font-monospace', copy_button=True) 

204 

205 

206class ModuleBayTypePanel(panels.ObjectAttributesPanel): 

207 manufacturer = attrs.RelatedObjectAttr('manufacturer', linkify=True) 

208 name = attrs.TextAttr('name') 

209 color = attrs.ColorAttr('color') 

210 description = attrs.TextAttr('description') 

211 

212 

213class ModuleTypeProfilePanel(panels.ObjectAttributesPanel): 

214 name = attrs.TextAttr('name') 

215 description = attrs.TextAttr('description') 

216 

217 

218class ModuleTypePanel(panels.ObjectAttributesPanel): 

219 profile = attrs.RelatedObjectAttr('profile', linkify=True) 

220 manufacturer = attrs.RelatedObjectAttr('manufacturer', linkify=True) 

221 model = attrs.TextAttr('model', label=_('Model name')) 

222 part_number = attrs.TextAttr('part_number') 

223 description = attrs.TextAttr('description') 

224 cooling_method = attrs.ChoiceAttr('cooling_method') 

225 airflow = attrs.ChoiceAttr('airflow') 

226 weight = attrs.WeightAttr('weight') 

227 end_of_life = attrs.DateTimeAttr('end_of_life', spec='date') 

228 module_bay_types = attrs.RelatedObjectListAttr( 

229 'module_bay_types', label=_('Bay Type Compatibility'), linkify=True 

230 ) 

231 

232 

233class PlatformPanel(panels.NestedGroupObjectPanel): 

234 manufacturer = attrs.RelatedObjectAttr('manufacturer', linkify=True) 

235 config_template = attrs.RelatedObjectAttr('config_template', linkify=True) 

236 

237 

238class ConsolePortPanel(panels.ObjectAttributesPanel): 

239 device = attrs.RelatedObjectAttr('device', linkify=True) 

240 module = attrs.RelatedObjectAttr('module', linkify=True) 

241 name = attrs.TextAttr('name') 

242 label = attrs.TextAttr('label') 

243 type = attrs.ChoiceAttr('type') 

244 speed = attrs.ChoiceAttr('speed') 

245 description = attrs.TextAttr('description') 

246 

247 

248class ConsoleServerPortPanel(panels.ObjectAttributesPanel): 

249 device = attrs.RelatedObjectAttr('device', linkify=True) 

250 module = attrs.RelatedObjectAttr('module', linkify=True) 

251 name = attrs.TextAttr('name') 

252 label = attrs.TextAttr('label') 

253 type = attrs.ChoiceAttr('type') 

254 speed = attrs.ChoiceAttr('speed') 

255 description = attrs.TextAttr('description') 

256 

257 

258class PowerPortPanel(panels.ObjectAttributesPanel): 

259 device = attrs.RelatedObjectAttr('device', linkify=True) 

260 module = attrs.RelatedObjectAttr('module', linkify=True) 

261 name = attrs.TextAttr('name') 

262 label = attrs.TextAttr('label') 

263 type = attrs.ChoiceAttr('type') 

264 description = attrs.TextAttr('description') 

265 maximum_draw = attrs.TextAttr('maximum_draw', format_string='{}W') 

266 allocated_draw = attrs.TextAttr('allocated_draw', format_string='{}W') 

267 

268 

269class PowerOutletPanel(panels.ObjectAttributesPanel): 

270 device = attrs.RelatedObjectAttr('device', linkify=True) 

271 module = attrs.RelatedObjectAttr('module', linkify=True) 

272 name = attrs.TextAttr('name') 

273 label = attrs.TextAttr('label') 

274 type = attrs.ChoiceAttr('type') 

275 status = attrs.ChoiceAttr('status') 

276 description = attrs.TextAttr('description') 

277 color = attrs.ColorAttr('color') 

278 power_port = attrs.RelatedObjectAttr('power_port', linkify=True) 

279 feed_leg = attrs.ChoiceAttr('feed_leg') 

280 

281 

282class CoolingIntakePanel(panels.ObjectAttributesPanel): 

283 device = attrs.RelatedObjectAttr('device', linkify=True) 

284 module = attrs.RelatedObjectAttr('module', linkify=True) 

285 name = attrs.TextAttr('name') 

286 label = attrs.TextAttr('label') 

287 type = attrs.ChoiceAttr('type') 

288 diameter = attrs.DiameterAttr('diameter') 

289 max_flow = attrs.FlowRateAttr('max_flow') 

290 cooling_outflow = attrs.RelatedObjectAttr('cooling_outflow', linkify=True) 

291 description = attrs.TextAttr('description') 

292 

293 

294class CoolingOutflowPanel(panels.ObjectAttributesPanel): 

295 device = attrs.RelatedObjectAttr('device', linkify=True) 

296 module = attrs.RelatedObjectAttr('module', linkify=True) 

297 name = attrs.TextAttr('name') 

298 label = attrs.TextAttr('label') 

299 type = attrs.ChoiceAttr('type') 

300 diameter = attrs.DiameterAttr('diameter') 

301 cooling_intake = attrs.RelatedObjectAttr('cooling_intake', linkify=True) 

302 description = attrs.TextAttr('description') 

303 

304 

305class FrontPortPanel(panels.ObjectAttributesPanel): 

306 device = attrs.RelatedObjectAttr('device', linkify=True) 

307 module = attrs.RelatedObjectAttr('module', linkify=True) 

308 name = attrs.TextAttr('name') 

309 label = attrs.TextAttr('label') 

310 type = attrs.ChoiceAttr('type') 

311 color = attrs.ColorAttr('color') 

312 positions = attrs.NumericAttr('positions') 

313 description = attrs.TextAttr('description') 

314 

315 

316class RearPortPanel(panels.ObjectAttributesPanel): 

317 device = attrs.RelatedObjectAttr('device', linkify=True) 

318 module = attrs.RelatedObjectAttr('module', linkify=True) 

319 name = attrs.TextAttr('name') 

320 label = attrs.TextAttr('label') 

321 type = attrs.ChoiceAttr('type') 

322 color = attrs.ColorAttr('color') 

323 positions = attrs.NumericAttr('positions') 

324 description = attrs.TextAttr('description') 

325 

326 

327class ModuleBayPanel(panels.ObjectAttributesPanel): 

328 device = attrs.RelatedObjectAttr('device', linkify=True) 

329 module = attrs.RelatedObjectAttr('module', linkify=True) 

330 name = attrs.TextAttr('name') 

331 label = attrs.TextAttr('label') 

332 position = attrs.TextAttr('position') 

333 description = attrs.TextAttr('description') 

334 module_bay_types = attrs.RelatedObjectListAttr('module_bay_types', label=_('Bay Type Compatibility'), linkify=True) 

335 

336 

337class InstalledModulePanel(panels.ObjectAttributesPanel): 

338 title = _('Installed Module') 

339 module = attrs.RelatedObjectAttr('installed_module', linkify=True) 

340 manufacturer = attrs.RelatedObjectAttr('installed_module.module_type.manufacturer', linkify=True) 

341 module_type = attrs.RelatedObjectAttr('installed_module.module_type', linkify=True) 

342 serial = attrs.TextAttr('installed_module.serial', label=_('Serial number'), style='font-monospace') 

343 asset_tag = attrs.TextAttr('installed_module.asset_tag', style='font-monospace') 

344 

345 

346class DeviceBayPanel(panels.ObjectAttributesPanel): 

347 device = attrs.RelatedObjectAttr('device', linkify=True) 

348 name = attrs.TextAttr('name') 

349 label = attrs.TextAttr('label') 

350 description = attrs.TextAttr('description') 

351 

352 

353class InstalledDevicePanel(panels.ObjectAttributesPanel): 

354 title = _('Installed Device') 

355 device = attrs.RelatedObjectAttr('installed_device', linkify=True) 

356 device_type = attrs.RelatedObjectAttr('installed_device.device_type') 

357 

358 

359class InventoryItemPanel(panels.ObjectAttributesPanel): 

360 device = attrs.RelatedObjectAttr('device', linkify=True) 

361 parent = attrs.RelatedObjectAttr('parent', linkify=True, label=_('Parent item')) 

362 name = attrs.TextAttr('name') 

363 label = attrs.TextAttr('label') 

364 status = attrs.ChoiceAttr('status') 

365 role = attrs.RelatedObjectAttr('role', linkify=True, colored=True) 

366 component = attrs.GenericForeignKeyAttr('component', linkify=True) 

367 manufacturer = attrs.RelatedObjectAttr('manufacturer', linkify=True) 

368 part_id = attrs.TextAttr('part_id', label=_('Part ID')) 

369 serial = attrs.TextAttr('serial') 

370 asset_tag = attrs.TextAttr('asset_tag') 

371 description = attrs.TextAttr('description') 

372 

373 

374class InventoryItemRolePanel(panels.OrganizationalObjectPanel): 

375 color = attrs.ColorAttr('color') 

376 

377 

378class CablePanel(panels.ObjectAttributesPanel): 

379 type = attrs.ChoiceAttr('type') 

380 status = attrs.ChoiceAttr('status') 

381 profile = attrs.ChoiceAttr('profile') 

382 tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group') 

383 bundle = attrs.RelatedObjectAttr('bundle', linkify=True) 

384 label = attrs.TextAttr('label') 

385 description = attrs.TextAttr('description') 

386 color = attrs.ColorAttr('color') 

387 length = attrs.NumericAttr('length', unit_accessor='get_length_unit_display') 

388 

389 

390class VirtualChassisPanel(panels.ObjectAttributesPanel): 

391 domain = attrs.TextAttr('domain') 

392 master = attrs.RelatedObjectAttr('master', linkify=True) 

393 description = attrs.TextAttr('description') 

394 

395 

396class PowerPanelPanel(panels.ObjectAttributesPanel): 

397 site = attrs.RelatedObjectAttr('site', linkify=True) 

398 location = attrs.NestedObjectAttr('location', linkify=True) 

399 description = attrs.TextAttr('description') 

400 

401 

402class PowerFeedPanel(panels.ObjectAttributesPanel): 

403 power_panel = attrs.RelatedObjectAttr('power_panel', linkify=True) 

404 rack = attrs.RelatedObjectAttr('rack', linkify=True) 

405 type = attrs.ChoiceAttr('type') 

406 status = attrs.ChoiceAttr('status') 

407 description = attrs.TextAttr('description') 

408 tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group') 

409 connected_device = attrs.TemplatedAttr( 

410 'connected_endpoints', 

411 label=_('Connected device'), 

412 template_name='dcim/powerfeed/attrs/connected_device.html', 

413 ) 

414 utilization = attrs.TemplatedAttr( 

415 'connected_endpoints', 

416 label=_('Utilization (allocated)'), 

417 template_name='dcim/powerfeed/attrs/utilization.html', 

418 ) 

419 

420 

421class PowerFeedElectricalPanel(panels.ObjectAttributesPanel): 

422 title = _('Electrical Characteristics') 

423 

424 supply = attrs.ChoiceAttr('supply') 

425 voltage = attrs.TextAttr('voltage', format_string='{}V') 

426 amperage = attrs.TextAttr('amperage', format_string='{}A') 

427 phase = attrs.ChoiceAttr('phase') 

428 max_utilization = attrs.TextAttr('max_utilization', format_string='{}%') 

429 

430 

431class CoolingSourcePanel(panels.ObjectAttributesPanel): 

432 site = attrs.RelatedObjectAttr('site', linkify=True) 

433 location = attrs.NestedObjectAttr('location', linkify=True) 

434 type = attrs.ChoiceAttr('type') 

435 status = attrs.ChoiceAttr('status') 

436 fluid_type = attrs.ChoiceAttr('fluid_type') 

437 cooling_capacity = attrs.TextAttr('cooling_capacity', format_string=_('{} kW')) 

438 description = attrs.TextAttr('description') 

439 

440 

441class CoolingFeedPanel(panels.ObjectAttributesPanel): 

442 cooling_source = attrs.RelatedObjectAttr('cooling_source', linkify=True) 

443 rack = attrs.RelatedObjectAttr('rack', linkify=True) 

444 status = attrs.ChoiceAttr('status') 

445 description = attrs.TextAttr('description') 

446 tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group') 

447 

448 

449class CoolingFeedCharacteristicsPanel(panels.ObjectAttributesPanel): 

450 title = _('Cooling Characteristics') 

451 

452 cooling_capacity = attrs.TextAttr('cooling_capacity', format_string=_('{} kW')) 

453 max_flow = attrs.FlowRateAttr('max_flow') 

454 

455 

456class VirtualDeviceContextPanel(panels.ObjectAttributesPanel): 

457 name = attrs.TextAttr('name') 

458 device = attrs.RelatedObjectAttr('device', linkify=True) 

459 identifier = attrs.TextAttr('identifier') 

460 status = attrs.ChoiceAttr('status') 

461 primary_ip4 = attrs.TemplatedAttr( 

462 'primary_ip4', 

463 label=_('Primary IPv4'), 

464 template_name='dcim/device/attrs/ipaddress.html', 

465 ) 

466 primary_ip6 = attrs.TemplatedAttr( 

467 'primary_ip6', 

468 label=_('Primary IPv6'), 

469 template_name='dcim/device/attrs/ipaddress.html', 

470 ) 

471 tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group') 

472 

473 

474class MACAddressPanel(panels.ObjectAttributesPanel): 

475 mac_address = attrs.TextAttr('mac_address', label=_('MAC address'), style='font-monospace', copy_button=True) 

476 description = attrs.TextAttr('description') 

477 assignment = attrs.RelatedObjectAttr('assigned_object', linkify=True, grouped_by='parent_object') 

478 is_primary = attrs.BooleanAttr('is_primary', label=_('Primary for interface')) 

479 

480 

481class ConnectionPanel(panels.ObjectPanel): 

482 """ 

483 A panel which displays connection information for a cabled object. 

484 """ 

485 template_name = 'dcim/panels/connection.html' 

486 title = _('Connection') 

487 

488 def __init__(self, trace_url_name, connect_options=None, show_endpoints=True, **kwargs): 

489 super().__init__(**kwargs) 

490 self.trace_url_name = trace_url_name 

491 self.connect_options = connect_options or [] 

492 self.show_endpoints = show_endpoints 

493 

494 def get_context(self, context): 

495 return { 

496 **super().get_context(context), 

497 'trace_url_name': self.trace_url_name, 

498 'connect_options': self.connect_options, 

499 'show_endpoints': self.show_endpoints, 

500 } 

501 

502 

503class InventoryItemsPanel(panels.ObjectPanel): 

504 """ 

505 A panel which displays inventory items associated with a component. 

506 """ 

507 template_name = 'dcim/panels/component_inventory_items.html' 

508 title = _('Inventory Items') 

509 actions = [ 

510 actions.AddObject( 

511 'dcim.inventoryitem', 

512 url_params={ 

513 'component_type': lambda ctx: ContentType.objects.get_for_model(ctx['object']).pk, 

514 'component_id': lambda ctx: ctx['object'].pk, 

515 }, 

516 ), 

517 ] 

518 

519 

520class VirtualChassisMembersPanel(panels.ObjectPanel): 

521 """ 

522 A panel which lists all members of a virtual chassis. 

523 """ 

524 

525 template_name = 'dcim/panels/virtual_chassis_members.html' 

526 title = _('Virtual Chassis Members') 

527 actions = [ 

528 actions.AddObject( 

529 'dcim.device', 

530 url_params={ 

531 'site': lambda ctx: ( 

532 ctx['virtual_chassis'].master.site_id 

533 if ctx['virtual_chassis'] and ctx['virtual_chassis'].master_id 

534 else '' 

535 ), 

536 'rack': lambda ctx: ( 

537 ctx['virtual_chassis'].master.rack_id 

538 if ctx['virtual_chassis'] and ctx['virtual_chassis'].master_id 

539 else '' 

540 ), 

541 }, 

542 ), 

543 ] 

544 

545 def get_context(self, context): 

546 return { 

547 **super().get_context(context), 

548 'virtual_chassis': context.get('virtual_chassis'), 

549 'vc_members': context.get('vc_members'), 

550 } 

551 

552 def should_render(self, context): 

553 return bool(context.get('vc_members')) 

554 

555 

556class PowerUtilizationPanel(panels.ObjectPanel): 

557 """ 

558 A panel which displays the power utilization statistics for a device. 

559 """ 

560 template_name = 'dcim/panels/power_utilization.html' 

561 title = _('Power Utilization') 

562 

563 def get_context(self, context): 

564 return { 

565 **super().get_context(context), 

566 'vc_members': context.get('vc_members'), 

567 } 

568 

569 def should_render(self, context): 

570 obj = context['object'] 

571 return obj.powerports.exists() and obj.poweroutlets.exists() 

572 

573 

574class InterfacePanel(panels.ObjectAttributesPanel): 

575 device = attrs.RelatedObjectAttr('device', linkify=True) 

576 module = attrs.RelatedObjectAttr('module', linkify=True) 

577 name = attrs.TextAttr('name') 

578 label = attrs.TextAttr('label') 

579 type = attrs.ChoiceAttr('type') 

580 channels = attrs.NumericAttr('channels') 

581 speed = attrs.TemplatedAttr('speed', template_name='dcim/interface/attrs/speed.html', label=_('Speed')) 

582 duplex = attrs.ChoiceAttr('duplex') 

583 mtu = attrs.NumericAttr('mtu', label=_('MTU')) 

584 enabled = attrs.BooleanAttr('enabled') 

585 mgmt_only = attrs.BooleanAttr('mgmt_only', label=_('Management only')) 

586 description = attrs.TextAttr('description') 

587 poe_mode = attrs.ChoiceAttr('poe_mode', label=_('PoE mode')) 

588 poe_type = attrs.ChoiceAttr('poe_type', label=_('PoE type')) 

589 mode = attrs.ChoiceAttr('mode', label=_('802.1Q mode')) 

590 qinq_svlan = attrs.RelatedObjectAttr('qinq_svlan', linkify=True, label=_('Q-in-Q SVLAN')) 

591 untagged_vlan = attrs.RelatedObjectAttr('untagged_vlan', linkify=True, label=_('Untagged VLAN')) 

592 tx_power = attrs.TextAttr('tx_power', label=_('Transmit power'), format_string='{} dBm') 

593 tunnel = attrs.RelatedObjectAttr('tunnel_termination.tunnel', linkify=True, label=_('Tunnel')) 

594 l2vpn = attrs.RelatedObjectAttr('l2vpn_termination.l2vpn', linkify=True, label=_('L2VPN')) 

595 

596 

597class RelatedInterfacesPanel(panels.ObjectAttributesPanel): 

598 title = _('Related Interfaces') 

599 

600 parent = attrs.RelatedObjectAttr('parent', linkify=True) 

601 channel_id = attrs.NumericAttr('channel_id', label=_('Channel ID')) 

602 bridge = attrs.RelatedObjectAttr('bridge', linkify=True) 

603 lag = attrs.RelatedObjectAttr('lag', linkify=True, label=_('LAG')) 

604 

605 

606class InterfaceAddressingPanel(panels.ObjectAttributesPanel): 

607 title = _('Addressing') 

608 

609 mac_address = attrs.TemplatedAttr( 

610 'primary_mac_address', 

611 template_name='dcim/interface/attrs/mac_address.html', 

612 label=_('MAC address'), 

613 ) 

614 wwn = attrs.TextAttr('wwn', style='font-monospace', label=_('WWN')) 

615 vrf = attrs.RelatedObjectAttr('vrf', linkify=True, label=_('VRF')) 

616 vlan_translation = attrs.RelatedObjectAttr('vlan_translation_policy', linkify=True, label=_('VLAN translation')) 

617 

618 

619class InterfaceConnectionPanel(panels.ObjectPanel): 

620 """ 

621 A connection panel for interfaces, which handles cable, wireless link, and virtual circuit cases. 

622 """ 

623 template_name = 'dcim/panels/interface_connection.html' 

624 title = _('Connection') 

625 

626 def should_render(self, context): 

627 obj = context.get('object') 

628 return False if (obj is None or obj.is_virtual) else True 

629 

630 

631class VirtualCircuitPanel(panels.ObjectPanel): 

632 """ 

633 A panel which displays virtual circuit information for a virtual interface. 

634 """ 

635 template_name = 'dcim/panels/interface_virtual_circuit.html' 

636 title = _('Virtual Circuit') 

637 

638 def should_render(self, context): 

639 obj = context.get('object') 

640 if not obj or not obj.is_virtual or not hasattr(obj, 'virtual_circuit_termination'): 

641 return False 

642 return True 

643 

644 

645class InterfaceWirelessPanel(panels.ObjectPanel): 

646 """ 

647 A panel which displays wireless RF attributes for an interface, comparing local and peer values. 

648 """ 

649 template_name = 'dcim/panels/interface_wireless.html' 

650 title = _('Wireless') 

651 

652 def should_render(self, context): 

653 obj = context.get('object') 

654 return False if (obj is None or not obj.is_wireless) else True 

655 

656 

657class WirelessLANsPanel(panels.ObjectPanel): 

658 """ 

659 A panel which lists the wireless LANs associated with an interface. 

660 """ 

661 template_name = 'dcim/panels/interface_wireless_lans.html' 

662 title = _('Wireless LANs') 

663 

664 def should_render(self, context): 

665 obj = context.get('object') 

666 return False if (obj is None or not obj.is_wireless) else True