Coverage for dcim/views.py: 84%

2819 statements  

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

1from django.conf import settings 

2from django.contrib import messages 

3from django.contrib.contenttypes.models import ContentType 

4from django.core.exceptions import ValidationError 

5from django.core.paginator import EmptyPage, PageNotAnInteger 

6from django.db import router, transaction 

7from django.db.models import Func, IntegerField, Prefetch 

8from django.forms import ModelMultipleChoiceField, MultipleHiddenInput, modelformset_factory 

9from django.shortcuts import get_object_or_404, redirect, render 

10from django.urls import reverse 

11from django.utils.html import escape 

12from django.utils.safestring import mark_safe 

13from django.utils.translation import gettext_lazy as _ 

14from django.utils.translation import ngettext 

15from django.views.generic import View 

16 

17from circuits.models import Circuit, CircuitTermination 

18from extras.ui.panels import CustomFieldsPanel, ImageAttachmentsPanel, TagsPanel 

19from extras.views import ObjectConfigContextView, ObjectRenderConfigView 

20from ipam.models import ASN, VLAN, IPAddress, Prefix, VLANGroup 

21from ipam.tables import VLANTranslationRuleTable 

22from ipam.ui.panels import FHRPGroupAssignmentsPanel 

23from netbox.object_actions import * 

24from netbox.ui import actions, layout 

25from netbox.ui.breadcrumbs import Breadcrumb, filtered_list_url, object_view_url 

26from netbox.ui.panels import ( 

27 CommentsPanel, 

28 ContextTablePanel, 

29 JSONPanel, 

30 NestedGroupObjectPanel, 

31 ObjectsTablePanel, 

32 OrganizationalObjectPanel, 

33 RelatedObjectsPanel, 

34 TemplatePanel, 

35) 

36from netbox.views import generic 

37from utilities.forms import ConfirmationForm 

38from utilities.paginator import EnhancedPaginator, get_paginate_count 

39from utilities.permissions import get_permission_for_model 

40from utilities.query import count_related 

41from utilities.query_functions import CollateAsChar 

42from utilities.request import safe_for_redirect 

43from utilities.views import ( 

44 ConditionalLoginRequiredMixin, 

45 GetRelatedModelsMixin, 

46 GetReturnURLMixin, 

47 ObjectPermissionRequiredMixin, 

48 ViewTab, 

49 register_model_view, 

50) 

51from virtualization.filtersets import VirtualMachineFilterSet 

52from virtualization.forms import VirtualMachineFilterForm 

53from virtualization.models import Cluster, VirtualMachine 

54from virtualization.tables import VirtualMachineTable 

55from wireless.models import WirelessLAN 

56 

57from . import filtersets, forms, tables 

58from .choices import DeviceFaceChoices, InterfaceModeChoices 

59from .models import * 

60from .models.device_components import PortMapping 

61from .object_actions import BulkAddComponents, BulkDisconnect 

62from .ui import panels 

63 

64CABLE_TERMINATION_TYPES = { 

65 'dcim.consoleport': ConsolePort, 

66 'dcim.consoleserverport': ConsoleServerPort, 

67 'dcim.powerport': PowerPort, 

68 'dcim.poweroutlet': PowerOutlet, 

69 'dcim.interface': Interface, 

70 'dcim.frontport': FrontPort, 

71 'dcim.rearport': RearPort, 

72 'dcim.powerfeed': PowerFeed, 

73 'circuits.circuittermination': CircuitTermination, 

74} 

75 

76 

77class DeviceComponentsView(generic.ObjectChildrenView): 

78 queryset = Device.objects.all() 

79 

80 def get_children(self, request, parent): 

81 return self.child_model.objects.restrict(request.user, 'view').filter(device=parent) 

82 

83 

84class DeviceTypeComponentsView(generic.ObjectChildrenView): 

85 actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete) 

86 queryset = DeviceType.objects.all() 

87 viewname = None # Used for return_url resolution 

88 

89 def get_children(self, request, parent): 

90 return self.child_model.objects.restrict(request.user, 'view').filter(device_type=parent) 

91 

92 def get_extra_context(self, request, instance): 

93 return { 

94 'return_url': reverse(self.viewname, kwargs={'pk': instance.pk}), 

95 } 

96 

97 

98class ModuleTypeComponentsView(generic.ObjectChildrenView): 

99 queryset = ModuleType.objects.all() 

100 actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete) 

101 viewname = None # Used for return_url resolution 

102 

103 def get_children(self, request, parent): 

104 return self.child_model.objects.restrict(request.user, 'view').filter(module_type=parent) 

105 

106 def get_extra_context(self, request, instance): 

107 return { 

108 'return_url': reverse(self.viewname, kwargs={'pk': instance.pk}), 

109 } 

110 

111 

112class BulkDisconnectView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View): 

113 """ 

114 An extendable view for disconnection console/power/interface components in bulk. 

115 """ 

116 queryset = None 

117 template_name = 'dcim/bulk_disconnect.html' 

118 

119 def __init__(self, *args, **kwargs): 

120 super().__init__(*args, **kwargs) 

121 

122 # Create a new Form class from ConfirmationForm 

123 class _Form(ConfirmationForm): 

124 pk = ModelMultipleChoiceField( 

125 queryset=self.queryset, 

126 widget=MultipleHiddenInput() 

127 ) 

128 

129 self.form = _Form 

130 

131 def get_required_permission(self): 

132 return get_permission_for_model(self.queryset.model, 'change') 

133 

134 def post(self, request): 

135 

136 selected_objects = [] 

137 return_url = self.get_return_url(request) 

138 

139 if '_confirm' in request.POST: 

140 form = self.form(request.POST) 

141 

142 if form.is_valid(): 

143 

144 with transaction.atomic(using=router.db_for_write(Cable)): 

145 count = 0 

146 cable_ids = set() 

147 for obj in self.queryset.filter(pk__in=form.cleaned_data['pk']): 

148 if obj.cable: 

149 cable_ids.add(obj.cable.pk) 

150 count += 1 

151 for cable in Cable.objects.filter(pk__in=cable_ids): 

152 cable.delete() 

153 

154 messages.success(request, _("Disconnected {count} {type}").format( 

155 count=count, 

156 type=self.queryset.model._meta.verbose_name_plural 

157 )) 

158 

159 return redirect(return_url) 

160 

161 else: 

162 form = self.form(initial={'pk': request.POST.getlist('pk')}) 

163 selected_objects = self.queryset.filter(pk__in=form.initial['pk']) 

164 

165 return render(request, self.template_name, { 

166 'form': form, 

167 'obj_type_plural': self.queryset.model._meta.verbose_name_plural, 

168 'selected_objects': selected_objects, 

169 'return_url': return_url, 

170 }) 

171 

172 

173class PathTraceView(generic.ObjectView): 

174 """ 

175 Trace a cable path beginning from the given path endpoint (origin). 

176 """ 

177 additional_permissions = ['dcim.view_cable'] 

178 template_name = 'dcim/cable_trace.html' 

179 

180 def dispatch(self, request, *args, **kwargs): 

181 model = kwargs.pop('model') 

182 self.queryset = model.objects.all() 

183 

184 return super().dispatch(request, *args, **kwargs) 

185 

186 def get_extra_context(self, request, instance): 

187 related_paths = [] 

188 

189 # If tracing a PathEndpoint, locate the CablePath (if one exists) by its origin 

190 if isinstance(instance, PathEndpoint): 

191 path = instance._path 

192 

193 # Otherwise, find all CablePaths which traverse the specified object 

194 else: 

195 related_paths = CablePath.objects.filter(_nodes__contains=instance) 

196 # Check for specification of a particular path (when tracing pass-through ports) 

197 try: 

198 path_id = int(request.GET.get('cablepath_id')) 

199 except TypeError: 

200 path_id = None 

201 if path_id in list(related_paths.values_list('pk', flat=True)): 

202 path = CablePath.objects.get(pk=path_id) 

203 else: 

204 path = related_paths.first() 

205 

206 # No paths found 

207 if path is None: 

208 return { 

209 'path': None 

210 } 

211 

212 # Get the total length of the cable and whether the length is definitive (fully defined) 

213 total_length, is_definitive = path.get_total_length() if path else (None, False) 

214 

215 # Determine the path to the SVG trace image. The `-trace` API action (and the SVG renderer, 

216 # which calls origin.trace()) exist only for PathEndpoint origins. Other valid origins such as 

217 # CircuitTermination have no such action, so omit the SVG for them. 

218 origin_model = path.origin_type.model_class() 

219 if issubclass(origin_model, PathEndpoint): 

220 api_viewname = f"{path.origin_type.app_label}-api:{path.origin_type.model}-trace" 

221 svg_url = f"{reverse(api_viewname, kwargs={'pk': path.origins[0].pk})}?render=svg" 

222 else: 

223 svg_url = None 

224 

225 return { 

226 'path': path, 

227 'related_paths': related_paths, 

228 'total_length': total_length, 

229 'is_definitive': is_definitive, 

230 'svg_url': svg_url, 

231 } 

232 

233 

234# 

235# Regions 

236# 

237 

238@register_model_view(Region, 'list', path='', detail=False) 

239class RegionListView(generic.ObjectListView): 

240 queryset = Region.objects.add_related_count( 

241 Region.objects.all(), 

242 Site, 

243 'region', 

244 'site_count', 

245 cumulative=True 

246 ) 

247 filterset = filtersets.RegionFilterSet 

248 filterset_form = forms.RegionFilterForm 

249 table = tables.RegionTable 

250 

251 

252@register_model_view(Region) 

253class RegionView(GetRelatedModelsMixin, generic.ObjectView): 

254 queryset = Region.objects.all() 

255 layout = layout.SimpleLayout( 

256 breadcrumbs=[ 

257 Breadcrumb( 

258 lambda o: o.get_ancestors(), 

259 url=filtered_list_url('dcim:region_list', 'parent_id'), 

260 ), 

261 ], 

262 left_panels=[ 

263 NestedGroupObjectPanel(), 

264 TagsPanel(), 

265 CustomFieldsPanel(), 

266 CommentsPanel(), 

267 ], 

268 right_panels=[ 

269 RelatedObjectsPanel(), 

270 ], 

271 bottom_panels=[ 

272 ObjectsTablePanel( 

273 model='dcim.Region', 

274 title=_('Child Regions'), 

275 filters={'parent_id': lambda ctx: ctx['object'].pk}, 

276 exclude_columns=['parent'], 

277 actions=[ 

278 actions.AddObject('dcim.Region', url_params={'parent': lambda ctx: ctx['object'].pk}), 

279 ], 

280 ), 

281 ] 

282 ) 

283 

284 def get_extra_context(self, request, instance): 

285 regions = instance.get_descendants(include_self=True) 

286 

287 return { 

288 'related_models': self.get_related_models( 

289 request, 

290 regions, 

291 omit=(Cluster, CircuitTermination, Prefix, WirelessLAN), 

292 extra=( 

293 (Location.objects.restrict(request.user, 'view').filter(site__region__in=regions), 'region_id'), 

294 (Rack.objects.restrict(request.user, 'view').filter(site__region__in=regions), 'region_id'), 

295 ( 

296 Circuit.objects.restrict(request.user, 'view').filter( 

297 terminations___region=instance 

298 ).distinct(), 

299 'region_id' 

300 ), 

301 ( 

302 VLANGroup.objects.restrict(request.user, 'view').filter( 

303 scope_type=ContentType.objects.get_for_model(Region), 

304 scope_id__in=regions 

305 ).distinct(), 

306 'region' 

307 ), 

308 

309 # Handle these relations manually to avoid erroneous filter name resolution 

310 ( 

311 CircuitTermination.objects.restrict(request.user, 'view').filter(_region__in=regions), 

312 'region_id' 

313 ), 

314 (Cluster.objects.restrict(request.user, 'view').filter(_region__in=regions), 'region_id'), 

315 (Prefix.objects.restrict(request.user, 'view').filter(_region__in=regions), 'region_id'), 

316 (WirelessLAN.objects.restrict(request.user, 'view').filter(_region__in=regions), 'region_id'), 

317 ), 

318 ), 

319 } 

320 

321 

322@register_model_view(Region, 'add', detail=False) 

323@register_model_view(Region, 'edit') 

324class RegionEditView(generic.ObjectEditView): 

325 queryset = Region.objects.all() 

326 form = forms.RegionForm 

327 

328 

329@register_model_view(Region, 'delete') 

330class RegionDeleteView(generic.ObjectDeleteView): 

331 queryset = Region.objects.all() 

332 

333 

334@register_model_view(Region, 'bulk_import', path='import', detail=False) 

335class RegionBulkImportView(generic.BulkImportView): 

336 queryset = Region.objects.all() 

337 model_form = forms.RegionImportForm 

338 

339 

340@register_model_view(Region, 'bulk_edit', path='edit', detail=False) 

341class RegionBulkEditView(generic.BulkEditView): 

342 queryset = Region.objects.add_related_count( 

343 Region.objects.all(), 

344 Site, 

345 'region', 

346 'site_count', 

347 cumulative=True 

348 ) 

349 filterset = filtersets.RegionFilterSet 

350 table = tables.RegionTable 

351 form = forms.RegionBulkEditForm 

352 

353 

354@register_model_view(Region, 'bulk_rename', path='rename', detail=False) 

355class RegionBulkRenameView(generic.BulkRenameView): 

356 queryset = Region.objects.all() 

357 filterset = filtersets.RegionFilterSet 

358 

359 

360@register_model_view(Region, 'bulk_delete', path='delete', detail=False) 

361class RegionBulkDeleteView(generic.BulkDeleteView): 

362 queryset = Region.objects.add_related_count( 

363 Region.objects.all(), 

364 Site, 

365 'region', 

366 'site_count', 

367 cumulative=True 

368 ) 

369 filterset = filtersets.RegionFilterSet 

370 table = tables.RegionTable 

371 

372 

373# 

374# Site groups 

375# 

376 

377@register_model_view(SiteGroup, 'list', path='', detail=False) 

378class SiteGroupListView(generic.ObjectListView): 

379 queryset = SiteGroup.objects.add_related_count( 

380 SiteGroup.objects.all(), 

381 Site, 

382 'group', 

383 'site_count', 

384 cumulative=True 

385 ) 

386 filterset = filtersets.SiteGroupFilterSet 

387 filterset_form = forms.SiteGroupFilterForm 

388 table = tables.SiteGroupTable 

389 

390 

391@register_model_view(SiteGroup) 

392class SiteGroupView(GetRelatedModelsMixin, generic.ObjectView): 

393 queryset = SiteGroup.objects.all() 

394 layout = layout.SimpleLayout( 

395 breadcrumbs=[ 

396 Breadcrumb( 

397 lambda o: o.get_ancestors(), 

398 url=filtered_list_url('dcim:sitegroup_list', 'parent_id'), 

399 ), 

400 ], 

401 left_panels=[ 

402 NestedGroupObjectPanel(), 

403 TagsPanel(), 

404 CustomFieldsPanel(), 

405 CommentsPanel(), 

406 ], 

407 right_panels=[ 

408 RelatedObjectsPanel(), 

409 ], 

410 bottom_panels=[ 

411 ObjectsTablePanel( 

412 model='dcim.SiteGroup', 

413 title=_('Child Groups'), 

414 filters={'parent_id': lambda ctx: ctx['object'].pk}, 

415 exclude_columns=['parent'], 

416 actions=[ 

417 actions.AddObject('dcim.SiteGroup', url_params={'parent': lambda ctx: ctx['object'].pk}), 

418 ], 

419 ), 

420 ] 

421 ) 

422 

423 def get_extra_context(self, request, instance): 

424 groups = instance.get_descendants(include_self=True) 

425 

426 return { 

427 'related_models': self.get_related_models( 

428 request, 

429 groups, 

430 omit=(Cluster, CircuitTermination, Prefix, WirelessLAN), 

431 extra=( 

432 (Location.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'), 

433 (Rack.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'), 

434 (Device.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'), 

435 (VLAN.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'), 

436 ( 

437 ASN.objects.restrict(request.user, 'view').filter( 

438 sites__group__in=groups 

439 ).distinct(), 

440 'site_group_id'), 

441 ( 

442 VirtualMachine.objects.restrict(request.user, 'view').filter( 

443 site__group__in=groups), 

444 'site_group_id' 

445 ), 

446 ( 

447 VLANGroup.objects.restrict(request.user, 'view').filter( 

448 scope_type=ContentType.objects.get_for_model(SiteGroup), 

449 scope_id__in=groups 

450 ).distinct(), 

451 'site_group' 

452 ), 

453 ( 

454 Circuit.objects.restrict(request.user, 'view').filter( 

455 terminations___site_group=instance 

456 ).distinct(), 

457 'site_group_id' 

458 ), 

459 

460 # Handle these relations manually to avoid erroneous filter name resolution 

461 ( 

462 CircuitTermination.objects.restrict(request.user, 'view').filter(_site_group__in=groups), 

463 'site_group_id' 

464 ), 

465 ( 

466 Cluster.objects.restrict(request.user, 'view').filter(_site_group__in=groups), 

467 'site_group_id' 

468 ), 

469 ( 

470 Prefix.objects.restrict(request.user, 'view').filter(_site_group__in=groups), 

471 'site_group_id' 

472 ), 

473 ( 

474 WirelessLAN.objects.restrict(request.user, 'view').filter(_site_group__in=groups), 

475 'site_group_id' 

476 ), 

477 ), 

478 ), 

479 } 

480 

481 

482@register_model_view(SiteGroup, 'add', detail=False) 

483@register_model_view(SiteGroup, 'edit') 

484class SiteGroupEditView(generic.ObjectEditView): 

485 queryset = SiteGroup.objects.all() 

486 form = forms.SiteGroupForm 

487 

488 

489@register_model_view(SiteGroup, 'delete') 

490class SiteGroupDeleteView(generic.ObjectDeleteView): 

491 queryset = SiteGroup.objects.all() 

492 

493 

494@register_model_view(SiteGroup, 'bulk_import', path='import', detail=False) 

495class SiteGroupBulkImportView(generic.BulkImportView): 

496 queryset = SiteGroup.objects.all() 

497 model_form = forms.SiteGroupImportForm 

498 

499 

500@register_model_view(SiteGroup, 'bulk_edit', path='edit', detail=False) 

501class SiteGroupBulkEditView(generic.BulkEditView): 

502 queryset = SiteGroup.objects.add_related_count( 

503 SiteGroup.objects.all(), 

504 Site, 

505 'group', 

506 'site_count', 

507 cumulative=True 

508 ) 

509 filterset = filtersets.SiteGroupFilterSet 

510 table = tables.SiteGroupTable 

511 form = forms.SiteGroupBulkEditForm 

512 

513 

514@register_model_view(SiteGroup, 'bulk_rename', path='rename', detail=False) 

515class SiteGroupBulkRenameView(generic.BulkRenameView): 

516 queryset = SiteGroup.objects.all() 

517 filterset = filtersets.SiteGroupFilterSet 

518 

519 

520@register_model_view(SiteGroup, 'bulk_delete', path='delete', detail=False) 

521class SiteGroupBulkDeleteView(generic.BulkDeleteView): 

522 queryset = SiteGroup.objects.add_related_count( 

523 SiteGroup.objects.all(), 

524 Site, 

525 'group', 

526 'site_count', 

527 cumulative=True 

528 ) 

529 filterset = filtersets.SiteGroupFilterSet 

530 table = tables.SiteGroupTable 

531 

532 

533# 

534# Sites 

535# 

536 

537@register_model_view(Site, 'list', path='', detail=False) 

538class SiteListView(generic.ObjectListView): 

539 queryset = Site.objects.annotate( 

540 device_count=count_related(Device, 'site'), 

541 asn_count=count_related(ASN, 'sites') 

542 ) 

543 filterset = filtersets.SiteFilterSet 

544 filterset_form = forms.SiteFilterForm 

545 table = tables.SiteTable 

546 

547 

548@register_model_view(Site) 

549class SiteView(GetRelatedModelsMixin, generic.ObjectView): 

550 queryset = Site.objects.prefetch_related('tenant__group') 

551 layout = layout.SimpleLayout( 

552 left_panels=[ 

553 panels.SitePanel(), 

554 CustomFieldsPanel(), 

555 TagsPanel(), 

556 CommentsPanel(), 

557 ], 

558 right_panels=[ 

559 RelatedObjectsPanel(), 

560 ImageAttachmentsPanel(), 

561 ], 

562 bottom_panels=[ 

563 ObjectsTablePanel( 

564 model='dcim.Location', 

565 filters={'site_id': lambda ctx: ctx['object'].pk}, 

566 exclude_columns=['site'], 

567 actions=[ 

568 actions.AddObject('dcim.Location', url_params={'site': lambda ctx: ctx['object'].pk}), 

569 ], 

570 ), 

571 ObjectsTablePanel( 

572 model='dcim.Device', 

573 title=_('Non-Racked Devices'), 

574 filters={ 

575 'site_id': lambda ctx: ctx['object'].pk, 

576 'rack_id': settings.FILTERS_NULL_CHOICE_VALUE, 

577 'parent_bay_id': settings.FILTERS_NULL_CHOICE_VALUE, 

578 }, 

579 exclude_columns=['site'], 

580 actions=[ 

581 actions.AddObject('dcim.Device', url_params={'site': lambda ctx: ctx['object'].pk}), 

582 ], 

583 ), 

584 ] 

585 ) 

586 

587 def get_extra_context(self, request, instance): 

588 return { 

589 'related_models': self.get_related_models( 

590 request, 

591 instance, 

592 omit=(CableTermination, CircuitTermination, Cluster, Prefix, VLAN, WirelessLAN), 

593 extra=( 

594 (VLANGroup.objects.restrict(request.user, 'view').filter( 

595 scope_type=ContentType.objects.get_for_model(Site), 

596 scope_id=instance.pk 

597 ), 'site'), 

598 (VLAN.objects.restrict(request.user, 'view').get_related_to_sites([instance]), 'related_to_site'), 

599 (ASN.objects.restrict(request.user, 'view').filter(sites=instance), 'site_id'), 

600 ( 

601 Circuit.objects.restrict(request.user, 'view').filter(terminations___site=instance).distinct(), 

602 'site_id' 

603 ), 

604 

605 # Handle these relations manually to avoid erroneous filter name resolution 

606 (Cluster.objects.restrict(request.user, 'view').filter(_site=instance), 'site_id'), 

607 (Prefix.objects.restrict(request.user, 'view').filter(_site=instance), 'site_id'), 

608 (WirelessLAN.objects.restrict(request.user, 'view').filter(_site=instance), 'site_id'), 

609 (CircuitTermination.objects.restrict(request.user, 'view').filter(_site=instance), 'site_id'), 

610 ), 

611 ), 

612 } 

613 

614 

615@register_model_view(Site, 'add', detail=False) 

616@register_model_view(Site, 'edit') 

617class SiteEditView(generic.ObjectEditView): 

618 queryset = Site.objects.all() 

619 form = forms.SiteForm 

620 

621 

622@register_model_view(Site, 'delete') 

623class SiteDeleteView(generic.ObjectDeleteView): 

624 queryset = Site.objects.all() 

625 

626 

627@register_model_view(Site, 'bulk_import', path='import', detail=False) 

628class SiteBulkImportView(generic.BulkImportView): 

629 queryset = Site.objects.all() 

630 model_form = forms.SiteImportForm 

631 

632 

633@register_model_view(Site, 'bulk_edit', path='edit', detail=False) 

634class SiteBulkEditView(generic.BulkEditView): 

635 queryset = Site.objects.all() 

636 filterset = filtersets.SiteFilterSet 

637 table = tables.SiteTable 

638 form = forms.SiteBulkEditForm 

639 

640 

641@register_model_view(Site, 'bulk_rename', path='rename', detail=False) 

642class SiteBulkRenameView(generic.BulkRenameView): 

643 queryset = Site.objects.all() 

644 filterset = filtersets.SiteFilterSet 

645 

646 

647@register_model_view(Site, 'bulk_delete', path='delete', detail=False) 

648class SiteBulkDeleteView(generic.BulkDeleteView): 

649 queryset = Site.objects.all() 

650 filterset = filtersets.SiteFilterSet 

651 table = tables.SiteTable 

652 

653 

654# 

655# Locations 

656# 

657 

658@register_model_view(Location, 'list', path='', detail=False) 

659class LocationListView(generic.ObjectListView): 

660 queryset = Location.objects.add_related_count( 

661 Location.objects.add_related_count( 

662 Location.objects.add_related_count( 

663 Location.objects.all(), 

664 Device, 

665 'location', 

666 'device_count', 

667 cumulative=True 

668 ), 

669 Rack, 

670 'location', 

671 'rack_count', 

672 cumulative=True 

673 ), 

674 VLANGroup, 

675 'location', 

676 'vlangroup_count', 

677 cumulative=True 

678 ) 

679 filterset = filtersets.LocationFilterSet 

680 filterset_form = forms.LocationFilterForm 

681 table = tables.LocationTable 

682 

683 

684@register_model_view(Location) 

685class LocationView(GetRelatedModelsMixin, generic.ObjectView): 

686 template_name = 'generic/object.html' 

687 queryset = Location.objects.all() 

688 layout = layout.SimpleLayout( 

689 breadcrumbs=[ 

690 Breadcrumb(lambda o: o.get_ancestors()), 

691 ], 

692 left_panels=[ 

693 panels.LocationPanel(), 

694 TagsPanel(), 

695 CustomFieldsPanel(), 

696 CommentsPanel(), 

697 ], 

698 right_panels=[ 

699 RelatedObjectsPanel(), 

700 ImageAttachmentsPanel(), 

701 ], 

702 bottom_panels=[ 

703 ObjectsTablePanel( 

704 model='dcim.Location', 

705 title=_('Child Locations'), 

706 filters={'parent_id': lambda ctx: ctx['object'].pk}, 

707 exclude_columns=['parent'], 

708 actions=[ 

709 actions.AddObject( 

710 'dcim.Location', 

711 url_params={ 

712 'site': lambda ctx: ctx['object'].site_id, 

713 'parent': lambda ctx: ctx['object'].pk, 

714 } 

715 ), 

716 ], 

717 ), 

718 ObjectsTablePanel( 

719 model='dcim.Device', 

720 title=_('Non-Racked Devices'), 

721 filters={ 

722 'location_id': lambda ctx: ctx['object'].pk, 

723 'rack_id': settings.FILTERS_NULL_CHOICE_VALUE, 

724 'parent_bay_id': settings.FILTERS_NULL_CHOICE_VALUE, 

725 }, 

726 exclude_columns=['location'], 

727 actions=[ 

728 actions.AddObject( 

729 'dcim.Device', 

730 url_params={ 

731 'site': lambda ctx: ctx['object'].site_id, 

732 'parent': lambda ctx: ctx['object'].pk, 

733 } 

734 ), 

735 ], 

736 ), 

737 ] 

738 ) 

739 

740 def get_extra_context(self, request, instance): 

741 locations = instance.get_descendants(include_self=True) 

742 location_content_type = ContentType.objects.get_for_model(instance) 

743 return { 

744 'related_models': self.get_related_models( 

745 request, 

746 locations, 

747 omit=[CableTermination, CircuitTermination, Cluster, Prefix, WirelessLAN], 

748 extra=( 

749 ( 

750 Circuit.objects.restrict(request.user, 'view').filter( 

751 terminations___location=instance 

752 ).distinct(), 

753 'location_id' 

754 ), 

755 

756 # Handle these relations manually to avoid erroneous filter name resolution 

757 ( 

758 CircuitTermination.objects.restrict(request.user, 'view').filter(_location=instance), 

759 'location_id' 

760 ), 

761 (Cluster.objects.restrict(request.user, 'view').filter(_location=instance), 'location_id'), 

762 (Prefix.objects.restrict(request.user, 'view').filter(_location=instance), 'location_id'), 

763 (WirelessLAN.objects.restrict(request.user, 'view').filter(_location=instance), 'location_id'), 

764 (VLANGroup.objects.restrict(request.user, 'view').filter( 

765 scope_type_id=location_content_type.id, scope_id=instance.id), 'location'), 

766 ), 

767 ), 

768 } 

769 

770 

771@register_model_view(Location, 'add', detail=False) 

772@register_model_view(Location, 'edit') 

773class LocationEditView(generic.ObjectEditView): 

774 queryset = Location.objects.all() 

775 form = forms.LocationForm 

776 

777 

778@register_model_view(Location, 'delete') 

779class LocationDeleteView(generic.ObjectDeleteView): 

780 queryset = Location.objects.all() 

781 

782 

783@register_model_view(Location, 'bulk_import', path='import', detail=False) 

784class LocationBulkImportView(generic.BulkImportView): 

785 queryset = Location.objects.all() 

786 model_form = forms.LocationImportForm 

787 

788 

789@register_model_view(Location, 'bulk_edit', path='edit', detail=False) 

790class LocationBulkEditView(generic.BulkEditView): 

791 queryset = Location.objects.add_related_count( 

792 Location.objects.all(), 

793 Rack, 

794 'location', 

795 'rack_count', 

796 cumulative=True 

797 ).prefetch_related('site') 

798 filterset = filtersets.LocationFilterSet 

799 table = tables.LocationTable 

800 form = forms.LocationBulkEditForm 

801 

802 

803@register_model_view(Location, 'bulk_rename', path='rename', detail=False) 

804class LocationBulkRenameView(generic.BulkRenameView): 

805 queryset = Location.objects.all() 

806 filterset = filtersets.LocationFilterSet 

807 

808 

809@register_model_view(Location, 'bulk_delete', path='delete', detail=False) 

810class LocationBulkDeleteView(generic.BulkDeleteView): 

811 queryset = Location.objects.add_related_count( 

812 Location.objects.all(), 

813 Rack, 

814 'location', 

815 'rack_count', 

816 cumulative=True 

817 ).prefetch_related('site') 

818 filterset = filtersets.LocationFilterSet 

819 table = tables.LocationTable 

820 

821 

822# 

823# Rack groups 

824# 

825 

826 

827@register_model_view(RackGroup, 'list', path='', detail=False) 

828class RackGroupListView(generic.ObjectListView): 

829 queryset = RackGroup.objects.annotate( 

830 rack_count=count_related(Rack, 'group') 

831 ) 

832 filterset = filtersets.RackGroupFilterSet 

833 filterset_form = forms.RackGroupFilterForm 

834 table = tables.RackGroupTable 

835 

836 

837@register_model_view(RackGroup) 

838class RackGroupView(GetRelatedModelsMixin, generic.ObjectView): 

839 queryset = RackGroup.objects.all() 

840 layout = layout.SimpleLayout( 

841 left_panels=[ 

842 OrganizationalObjectPanel(), 

843 TagsPanel(), 

844 ], 

845 right_panels=[ 

846 RelatedObjectsPanel(), 

847 CustomFieldsPanel(), 

848 CommentsPanel(), 

849 ], 

850 ) 

851 

852 def get_extra_context(self, request, instance): 

853 return { 

854 'related_models': self.get_related_models(request, instance), 

855 } 

856 

857 

858@register_model_view(RackGroup, 'add', detail=False) 

859@register_model_view(RackGroup, 'edit') 

860class RackGroupEditView(generic.ObjectEditView): 

861 queryset = RackGroup.objects.all() 

862 form = forms.RackGroupForm 

863 

864 

865@register_model_view(RackGroup, 'delete') 

866class RackGroupDeleteView(generic.ObjectDeleteView): 

867 queryset = RackGroup.objects.all() 

868 

869 

870@register_model_view(RackGroup, 'bulk_import', path='import', detail=False) 

871class RackGroupBulkImportView(generic.BulkImportView): 

872 queryset = RackGroup.objects.all() 

873 model_form = forms.RackGroupImportForm 

874 

875 

876@register_model_view(RackGroup, 'bulk_edit', path='edit', detail=False) 

877class RackGroupBulkEditView(generic.BulkEditView): 

878 queryset = RackGroup.objects.annotate( 

879 rack_count=count_related(Rack, 'group') 

880 ) 

881 filterset = filtersets.RackGroupFilterSet 

882 table = tables.RackGroupTable 

883 form = forms.RackGroupBulkEditForm 

884 

885 

886@register_model_view(RackGroup, 'bulk_rename', path='rename', detail=False) 

887class RackGroupBulkRenameView(generic.BulkRenameView): 

888 queryset = RackGroup.objects.all() 

889 filterset = filtersets.RackGroupFilterSet 

890 

891 

892@register_model_view(RackGroup, 'bulk_delete', path='delete', detail=False) 

893class RackGroupBulkDeleteView(generic.BulkDeleteView): 

894 queryset = RackGroup.objects.annotate( 

895 rack_count=count_related(Rack, 'group') 

896 ) 

897 filterset = filtersets.RackGroupFilterSet 

898 table = tables.RackGroupTable 

899 

900 

901# 

902# Rack roles 

903# 

904 

905@register_model_view(RackRole, 'list', path='', detail=False) 

906class RackRoleListView(generic.ObjectListView): 

907 queryset = RackRole.objects.annotate( 

908 rack_count=count_related(Rack, 'role') 

909 ) 

910 filterset = filtersets.RackRoleFilterSet 

911 filterset_form = forms.RackRoleFilterForm 

912 table = tables.RackRoleTable 

913 

914 

915@register_model_view(RackRole) 

916class RackRoleView(GetRelatedModelsMixin, generic.ObjectView): 

917 queryset = RackRole.objects.all() 

918 layout = layout.SimpleLayout( 

919 left_panels=[ 

920 panels.RackRolePanel(), 

921 TagsPanel(), 

922 ], 

923 right_panels=[ 

924 RelatedObjectsPanel(), 

925 CustomFieldsPanel(), 

926 CommentsPanel(), 

927 ], 

928 ) 

929 

930 def get_extra_context(self, request, instance): 

931 return { 

932 'related_models': self.get_related_models(request, instance), 

933 } 

934 

935 

936@register_model_view(RackRole, 'add', detail=False) 

937@register_model_view(RackRole, 'edit') 

938class RackRoleEditView(generic.ObjectEditView): 

939 queryset = RackRole.objects.all() 

940 form = forms.RackRoleForm 

941 

942 

943@register_model_view(RackRole, 'delete') 

944class RackRoleDeleteView(generic.ObjectDeleteView): 

945 queryset = RackRole.objects.all() 

946 

947 

948@register_model_view(RackRole, 'bulk_import', path='import', detail=False) 

949class RackRoleBulkImportView(generic.BulkImportView): 

950 queryset = RackRole.objects.all() 

951 model_form = forms.RackRoleImportForm 

952 

953 

954@register_model_view(RackRole, 'bulk_edit', path='edit', detail=False) 

955class RackRoleBulkEditView(generic.BulkEditView): 

956 queryset = RackRole.objects.annotate( 

957 rack_count=count_related(Rack, 'role') 

958 ) 

959 filterset = filtersets.RackRoleFilterSet 

960 table = tables.RackRoleTable 

961 form = forms.RackRoleBulkEditForm 

962 

963 

964@register_model_view(RackRole, 'bulk_rename', path='rename', detail=False) 

965class RackRoleBulkRenameView(generic.BulkRenameView): 

966 queryset = RackRole.objects.all() 

967 filterset = filtersets.RackRoleFilterSet 

968 

969 

970@register_model_view(RackRole, 'bulk_delete', path='delete', detail=False) 

971class RackRoleBulkDeleteView(generic.BulkDeleteView): 

972 queryset = RackRole.objects.annotate( 

973 rack_count=count_related(Rack, 'role') 

974 ) 

975 filterset = filtersets.RackRoleFilterSet 

976 table = tables.RackRoleTable 

977 

978 

979# 

980# RackTypes 

981# 

982 

983@register_model_view(RackType, 'list', path='', detail=False) 

984class RackTypeListView(generic.ObjectListView): 

985 queryset = RackType.objects.all() 

986 filterset = filtersets.RackTypeFilterSet 

987 filterset_form = forms.RackTypeFilterForm 

988 table = tables.RackTypeTable 

989 

990 

991@register_model_view(RackType) 

992class RackTypeView(GetRelatedModelsMixin, generic.ObjectView): 

993 template_name = 'generic/object.html' 

994 queryset = RackType.objects.all() 

995 layout = layout.SimpleLayout( 

996 left_panels=[ 

997 panels.RackTypePanel(), 

998 panels.RackDimensionsPanel(title=_('Dimensions')), 

999 TagsPanel(), 

1000 CommentsPanel(), 

1001 ], 

1002 right_panels=[ 

1003 panels.RackNumberingPanel(title=_('Numbering')), 

1004 panels.RackWeightPanel(title=_('Weight'), exclude=['total_weight']), 

1005 CustomFieldsPanel(), 

1006 RelatedObjectsPanel(), 

1007 ImageAttachmentsPanel(), 

1008 ], 

1009 ) 

1010 

1011 def get_extra_context(self, request, instance): 

1012 return { 

1013 'related_models': self.get_related_models(request, instance), 

1014 } 

1015 

1016 

1017@register_model_view(RackType, 'add', detail=False) 

1018@register_model_view(RackType, 'edit') 

1019class RackTypeEditView(generic.ObjectEditView): 

1020 queryset = RackType.objects.all() 

1021 form = forms.RackTypeForm 

1022 

1023 

1024@register_model_view(RackType, 'delete') 

1025class RackTypeDeleteView(generic.ObjectDeleteView): 

1026 queryset = RackType.objects.all() 

1027 

1028 

1029@register_model_view(RackType, 'bulk_import', path='import', detail=False) 

1030class RackTypeBulkImportView(generic.BulkImportView): 

1031 queryset = RackType.objects.all() 

1032 model_form = forms.RackTypeImportForm 

1033 

1034 

1035@register_model_view(RackType, 'bulk_edit', path='edit', detail=False) 

1036class RackTypeBulkEditView(generic.BulkEditView): 

1037 queryset = RackType.objects.all() 

1038 filterset = filtersets.RackTypeFilterSet 

1039 table = tables.RackTypeTable 

1040 form = forms.RackTypeBulkEditForm 

1041 

1042 

1043@register_model_view(RackType, 'bulk_rename', path='rename', detail=False) 

1044class RackTypeBulkRenameView(generic.BulkRenameView): 

1045 queryset = RackType.objects.all() 

1046 field_name = 'model' 

1047 filterset = filtersets.RackTypeFilterSet 

1048 

1049 

1050@register_model_view(RackType, 'bulk_delete', path='delete', detail=False) 

1051class RackTypeBulkDeleteView(generic.BulkDeleteView): 

1052 queryset = RackType.objects.all() 

1053 filterset = filtersets.RackTypeFilterSet 

1054 table = tables.RackTypeTable 

1055 

1056 

1057# 

1058# Racks 

1059# 

1060 

1061@register_model_view(Rack, 'list', path='', detail=False) 

1062class RackListView(generic.ObjectListView): 

1063 queryset = Rack.objects.annotate( 

1064 device_count=count_related(Device, 'rack') 

1065 ) 

1066 filterset = filtersets.RackFilterSet 

1067 filterset_form = forms.RackFilterForm 

1068 table = tables.RackTable 

1069 template_name = 'dcim/rack_list.html' 

1070 

1071 

1072class RackElevationListView(generic.ObjectListView): 

1073 """ 

1074 Display a set of rack elevations side-by-side. 

1075 """ 

1076 queryset = Rack.objects.prefetch_related('role') 

1077 

1078 def get(self, request): 

1079 

1080 racks = filtersets.RackFilterSet(request.GET, self.queryset).qs 

1081 total_count = racks.count() 

1082 

1083 # Ordering 

1084 ORDERING_CHOICES = { 

1085 'name': 'Name (A-Z)', 

1086 '-name': 'Name (Z-A)', 

1087 'facility_id': 'Facility ID (A-Z)', 

1088 '-facility_id': 'Facility ID (Z-A)', 

1089 } 

1090 sort = request.GET.get('sort', 'name') 

1091 if sort not in ORDERING_CHOICES: 

1092 sort = 'name' 

1093 racks = racks.order_by(sort) 

1094 

1095 # Pagination 

1096 per_page = get_paginate_count(request) 

1097 page_number = request.GET.get('page', 1) 

1098 paginator = EnhancedPaginator(racks, per_page) 

1099 try: 

1100 page = paginator.page(page_number) 

1101 except PageNotAnInteger: 

1102 page = paginator.page(1) 

1103 except EmptyPage: 

1104 page = paginator.page(paginator.num_pages) 

1105 

1106 # Determine rack face 

1107 rack_face = request.GET.get('face', DeviceFaceChoices.FACE_FRONT) 

1108 if rack_face not in DeviceFaceChoices.values(): 

1109 rack_face = DeviceFaceChoices.FACE_FRONT 

1110 

1111 return render(request, 'dcim/rack_elevation_list.html', { 

1112 'paginator': paginator, 

1113 'page': page, 

1114 'total_count': total_count, 

1115 'sort': sort, 

1116 'sort_display_name': ORDERING_CHOICES[sort], 

1117 'sort_choices': ORDERING_CHOICES, 

1118 'rack_face': rack_face, 

1119 'filter_form': forms.RackElevationFilterForm(request.GET), 

1120 'model': self.queryset.model, 

1121 }) 

1122 

1123 

1124@register_model_view(Rack) 

1125class RackView(GetRelatedModelsMixin, generic.ObjectView): 

1126 queryset = Rack.objects.prefetch_related('site__region', 'tenant__group', 'location', 'role') 

1127 layout = layout.SimpleLayout( 

1128 breadcrumbs=[ 

1129 Breadcrumb('site', url=filtered_list_url('dcim:rack_list', 'site_id')), 

1130 Breadcrumb( 

1131 lambda o: o.location.get_ancestors() if o.location else [], 

1132 url=filtered_list_url('dcim:rack_list', 'location_id'), 

1133 ), 

1134 Breadcrumb('location', url=filtered_list_url('dcim:rack_list', 'location_id')), 

1135 ], 

1136 left_panels=[ 

1137 panels.RackPanel(), 

1138 panels.RackDimensionsPanel(title=_('Dimensions')), 

1139 panels.RackNumberingPanel(title=_('Numbering')), 

1140 panels.RackWeightPanel(title=_('Weight')), 

1141 CustomFieldsPanel(), 

1142 TagsPanel(), 

1143 CommentsPanel(), 

1144 ImageAttachmentsPanel(), 

1145 ], 

1146 right_panels=[ 

1147 TemplatePanel('dcim/panels/rack_elevations.html'), 

1148 RelatedObjectsPanel(), 

1149 ], 

1150 ) 

1151 

1152 def get_extra_context(self, request, instance): 

1153 peer_racks = Rack.objects.restrict(request.user, 'view').filter(site=instance.site) 

1154 

1155 if instance.location: 

1156 peer_racks = peer_racks.filter(location=instance.location) 

1157 else: 

1158 peer_racks = peer_racks.filter(location__isnull=True) 

1159 next_rack = peer_racks.filter(name__gt=instance.name).first() 

1160 prev_rack = peer_racks.filter(name__lt=instance.name).reverse().first() 

1161 

1162 # Determine any additional parameters to pass when embedding the rack elevations 

1163 svg_extra = '&'.join([ 

1164 f'highlight=id:{pk}' for pk in request.GET.getlist('device') 

1165 ]) 

1166 

1167 return { 

1168 'related_models': self.get_related_models( 

1169 request, 

1170 instance, 

1171 omit=(CableTermination,), 

1172 extra=( 

1173 ( 

1174 VLANGroup.objects.restrict(request.user, 'view').filter( 

1175 scope_type=ContentType.objects.get_for_model(Rack), 

1176 scope_id=instance.pk 

1177 ), 'rack'), 

1178 ), 

1179 ), 

1180 'next_rack': next_rack, 

1181 'prev_rack': prev_rack, 

1182 'svg_extra': svg_extra, 

1183 'peer_racks': peer_racks, 

1184 } 

1185 

1186 

1187@register_model_view(Rack, 'reservations') 

1188class RackRackReservationsView(generic.ObjectChildrenView): 

1189 queryset = Rack.objects.all() 

1190 child_model = RackReservation 

1191 table = tables.RackReservationTable 

1192 filterset = filtersets.RackReservationFilterSet 

1193 filterset_form = forms.RackReservationFilterForm 

1194 template_name = 'dcim/rack/reservations.html' 

1195 tab = ViewTab( 

1196 label=_('Reservations'), 

1197 badge=lambda obj: obj.reservations.count(), 

1198 permission='dcim.view_rackreservation', 

1199 weight=510 

1200 ) 

1201 

1202 def get_children(self, request, parent): 

1203 return parent.reservations.restrict(request.user, 'view') 

1204 

1205 

1206@register_model_view(Rack, 'nonracked_devices', 'nonracked-devices') 

1207class RackNonRackedView(generic.ObjectChildrenView): 

1208 queryset = Rack.objects.all() 

1209 child_model = Device 

1210 table = tables.DeviceTable 

1211 filterset = filtersets.DeviceFilterSet 

1212 filterset_form = forms.DeviceFilterForm 

1213 template_name = 'dcim/rack/non_racked_devices.html' 

1214 tab = ViewTab( 

1215 label=_('Non-Racked Devices'), 

1216 badge=lambda obj: obj.devices.filter(rack=obj, position__isnull=True, parent_bay__isnull=True).count(), 

1217 weight=500, 

1218 permission='dcim.view_device', 

1219 ) 

1220 

1221 def get_children(self, request, parent): 

1222 return parent.devices.restrict(request.user, 'view').filter( 

1223 rack=parent, position__isnull=True, parent_bay__isnull=True 

1224 ) 

1225 

1226 

1227@register_model_view(Rack, 'add', detail=False) 

1228@register_model_view(Rack, 'edit') 

1229class RackEditView(generic.ObjectEditView): 

1230 queryset = Rack.objects.all() 

1231 form = forms.RackForm 

1232 

1233 

1234@register_model_view(Rack, 'delete') 

1235class RackDeleteView(generic.ObjectDeleteView): 

1236 queryset = Rack.objects.all() 

1237 

1238 

1239@register_model_view(Rack, 'bulk_import', path='import', detail=False) 

1240class RackBulkImportView(generic.BulkImportView): 

1241 queryset = Rack.objects.all() 

1242 model_form = forms.RackImportForm 

1243 

1244 

1245@register_model_view(Rack, 'bulk_edit', path='edit', detail=False) 

1246class RackBulkEditView(generic.BulkEditView): 

1247 queryset = Rack.objects.all() 

1248 filterset = filtersets.RackFilterSet 

1249 table = tables.RackTable 

1250 form = forms.RackBulkEditForm 

1251 

1252 

1253@register_model_view(Rack, 'bulk_rename', path='rename', detail=False) 

1254class RackBulkRenameView(generic.BulkRenameView): 

1255 queryset = Rack.objects.all() 

1256 filterset = filtersets.RackFilterSet 

1257 

1258 

1259@register_model_view(Rack, 'bulk_delete', path='delete', detail=False) 

1260class RackBulkDeleteView(generic.BulkDeleteView): 

1261 queryset = Rack.objects.all() 

1262 filterset = filtersets.RackFilterSet 

1263 table = tables.RackTable 

1264 

1265 

1266# 

1267# Rack reservations 

1268# 

1269 

1270@register_model_view(RackReservation, 'list', path='', detail=False) 

1271class RackReservationListView(generic.ObjectListView): 

1272 queryset = RackReservation.objects.annotate( 

1273 unit_count=Func('units', function='CARDINALITY', output_field=IntegerField()) 

1274 ) 

1275 filterset = filtersets.RackReservationFilterSet 

1276 filterset_form = forms.RackReservationFilterForm 

1277 table = tables.RackReservationTable 

1278 actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete) 

1279 

1280 

1281@register_model_view(RackReservation) 

1282class RackReservationView(generic.ObjectView): 

1283 template_name = 'generic/object.html' 

1284 queryset = RackReservation.objects.annotate( 

1285 unit_count=Func('units', function='CARDINALITY', output_field=IntegerField()) 

1286 ) 

1287 layout = layout.SimpleLayout( 

1288 breadcrumbs=[ 

1289 Breadcrumb('rack', url=filtered_list_url('dcim:rackreservation_list', 'rack_id')), 

1290 Breadcrumb(label=lambda o: f"{_('Units')} {o.unit_list}"), 

1291 ], 

1292 left_panels=[ 

1293 panels.RackPanel(accessor='object.rack', only=['region', 'site', 'location', 'group', 'name']), 

1294 panels.RackReservationPanel(title=_('Reservation')), 

1295 CustomFieldsPanel(), 

1296 TagsPanel(), 

1297 CommentsPanel(), 

1298 ], 

1299 right_panels=[ 

1300 TemplatePanel(template_name='dcim/panels/rack_reservation_elevations.html'), 

1301 RelatedObjectsPanel(), 

1302 ], 

1303 ) 

1304 

1305 

1306@register_model_view(RackReservation, 'add', detail=False) 

1307@register_model_view(RackReservation, 'edit') 

1308class RackReservationEditView(generic.ObjectEditView): 

1309 queryset = RackReservation.objects.all() 

1310 form = forms.RackReservationForm 

1311 

1312 def alter_object(self, obj, request, args, kwargs): 

1313 if not obj.pk: 

1314 if 'rack' in request.GET: 

1315 obj.rack = get_object_or_404(Rack, pk=request.GET.get('rack')) 

1316 obj.user = request.user 

1317 return obj 

1318 

1319 

1320@register_model_view(RackReservation, 'delete') 

1321class RackReservationDeleteView(generic.ObjectDeleteView): 

1322 queryset = RackReservation.objects.all() 

1323 

1324 

1325@register_model_view(RackReservation, 'bulk_import', path='import', detail=False) 

1326class RackReservationImportView(generic.BulkImportView): 

1327 queryset = RackReservation.objects.all() 

1328 model_form = forms.RackReservationImportForm 

1329 

1330 def save_object(self, object_form, request): 

1331 """ 

1332 Assign the currently authenticated user to the RackReservation. 

1333 """ 

1334 instance = object_form.save(commit=False) 

1335 instance.user = request.user 

1336 instance.save() 

1337 

1338 return instance 

1339 

1340 

1341@register_model_view(RackReservation, 'bulk_edit', path='edit', detail=False) 

1342class RackReservationBulkEditView(generic.BulkEditView): 

1343 queryset = RackReservation.objects.annotate( 

1344 unit_count=Func('units', function='CARDINALITY', output_field=IntegerField()) 

1345 ) 

1346 filterset = filtersets.RackReservationFilterSet 

1347 table = tables.RackReservationTable 

1348 form = forms.RackReservationBulkEditForm 

1349 

1350 

1351@register_model_view(RackReservation, 'bulk_delete', path='delete', detail=False) 

1352class RackReservationBulkDeleteView(generic.BulkDeleteView): 

1353 queryset = RackReservation.objects.annotate( 

1354 unit_count=Func('units', function='CARDINALITY', output_field=IntegerField()) 

1355 ) 

1356 filterset = filtersets.RackReservationFilterSet 

1357 table = tables.RackReservationTable 

1358 

1359 

1360# 

1361# Manufacturers 

1362# 

1363 

1364@register_model_view(Manufacturer, 'list', path='', detail=False) 

1365class ManufacturerListView(generic.ObjectListView): 

1366 queryset = Manufacturer.objects.annotate( 

1367 racktype_count=count_related(RackType, 'manufacturer'), 

1368 devicetype_count=count_related(DeviceType, 'manufacturer'), 

1369 moduletype_count=count_related(ModuleType, 'manufacturer'), 

1370 inventoryitem_count=count_related(InventoryItem, 'manufacturer'), 

1371 platform_count=count_related(Platform, 'manufacturer') 

1372 ) 

1373 filterset = filtersets.ManufacturerFilterSet 

1374 filterset_form = forms.ManufacturerFilterForm 

1375 table = tables.ManufacturerTable 

1376 

1377 

1378@register_model_view(Manufacturer) 

1379class ManufacturerView(GetRelatedModelsMixin, generic.ObjectView): 

1380 queryset = Manufacturer.objects.all() 

1381 layout = layout.SimpleLayout( 

1382 left_panels=[OrganizationalObjectPanel(), TagsPanel()], 

1383 right_panels=[RelatedObjectsPanel(), CustomFieldsPanel(), CommentsPanel()], 

1384 ) 

1385 

1386 def get_extra_context(self, request, instance): 

1387 return { 

1388 'related_models': self.get_related_models(request, instance, [InventoryItemTemplate]), 

1389 } 

1390 

1391 

1392@register_model_view(Manufacturer, 'add', detail=False) 

1393@register_model_view(Manufacturer, 'edit') 

1394class ManufacturerEditView(generic.ObjectEditView): 

1395 queryset = Manufacturer.objects.all() 

1396 form = forms.ManufacturerForm 

1397 

1398 

1399@register_model_view(Manufacturer, 'delete') 

1400class ManufacturerDeleteView(generic.ObjectDeleteView): 

1401 queryset = Manufacturer.objects.all() 

1402 

1403 

1404@register_model_view(Manufacturer, 'bulk_import', path='import', detail=False) 

1405class ManufacturerBulkImportView(generic.BulkImportView): 

1406 queryset = Manufacturer.objects.all() 

1407 model_form = forms.ManufacturerImportForm 

1408 

1409 

1410@register_model_view(Manufacturer, 'bulk_edit', path='edit', detail=False) 

1411class ManufacturerBulkEditView(generic.BulkEditView): 

1412 queryset = Manufacturer.objects.annotate( 

1413 devicetype_count=count_related(DeviceType, 'manufacturer'), 

1414 moduletype_count=count_related(ModuleType, 'manufacturer'), 

1415 inventoryitem_count=count_related(InventoryItem, 'manufacturer'), 

1416 platform_count=count_related(Platform, 'manufacturer') 

1417 ) 

1418 filterset = filtersets.ManufacturerFilterSet 

1419 table = tables.ManufacturerTable 

1420 form = forms.ManufacturerBulkEditForm 

1421 

1422 

1423@register_model_view(Manufacturer, 'bulk_rename', path='rename', detail=False) 

1424class ManufacturerBulkRenameView(generic.BulkRenameView): 

1425 queryset = Manufacturer.objects.all() 

1426 filterset = filtersets.ManufacturerFilterSet 

1427 

1428 

1429@register_model_view(Manufacturer, 'bulk_delete', path='delete', detail=False) 

1430class ManufacturerBulkDeleteView(generic.BulkDeleteView): 

1431 queryset = Manufacturer.objects.annotate( 

1432 devicetype_count=count_related(DeviceType, 'manufacturer'), 

1433 moduletype_count=count_related(ModuleType, 'manufacturer'), 

1434 inventoryitem_count=count_related(InventoryItem, 'manufacturer'), 

1435 platform_count=count_related(Platform, 'manufacturer') 

1436 ) 

1437 filterset = filtersets.ManufacturerFilterSet 

1438 table = tables.ManufacturerTable 

1439 

1440 

1441# 

1442# Device types 

1443# 

1444 

1445@register_model_view(DeviceType, 'list', path='', detail=False) 

1446class DeviceTypeListView(generic.ObjectListView): 

1447 queryset = DeviceType.objects.all() 

1448 filterset = filtersets.DeviceTypeFilterSet 

1449 filterset_form = forms.DeviceTypeFilterForm 

1450 table = tables.DeviceTypeTable 

1451 

1452 def export_yaml(self): 

1453 # Avoid one module_bay_types query per module bay template across the export. 

1454 self.queryset = self.queryset.prefetch_related('modulebaytemplates__module_bay_types') 

1455 return super().export_yaml() 

1456 

1457 

1458@register_model_view(DeviceType) 

1459class DeviceTypeView(GetRelatedModelsMixin, generic.ObjectView): 

1460 queryset = DeviceType.objects.all() 

1461 layout = layout.SimpleLayout( 

1462 breadcrumbs=[ 

1463 Breadcrumb('manufacturer', url=filtered_list_url('dcim:devicetype_list', 'manufacturer_id')), 

1464 ], 

1465 left_panels=[ 

1466 panels.DeviceTypePanel(), 

1467 TagsPanel(), 

1468 ], 

1469 right_panels=[ 

1470 RelatedObjectsPanel(), 

1471 CustomFieldsPanel(), 

1472 CommentsPanel(), 

1473 ImageAttachmentsPanel(), 

1474 ], 

1475 ) 

1476 

1477 def get_extra_context(self, request, instance): 

1478 return { 

1479 'related_models': self.get_related_models(request, instance, omit=[ 

1480 ConsolePortTemplate, ConsoleServerPortTemplate, CoolingIntakeTemplate, CoolingOutflowTemplate, 

1481 DeviceBayTemplate, FrontPortTemplate, InventoryItemTemplate, InterfaceTemplate, ModuleBayTemplate, 

1482 PowerOutletTemplate, PowerPortTemplate, RearPortTemplate, 

1483 ]), 

1484 } 

1485 

1486 

1487@register_model_view(DeviceType, 'add', detail=False) 

1488@register_model_view(DeviceType, 'edit') 

1489class DeviceTypeEditView(generic.ObjectEditView): 

1490 queryset = DeviceType.objects.all() 

1491 form = forms.DeviceTypeForm 

1492 

1493 

1494@register_model_view(DeviceType, 'delete') 

1495class DeviceTypeDeleteView(generic.ObjectDeleteView): 

1496 queryset = DeviceType.objects.all() 

1497 

1498 

1499@register_model_view(DeviceType, 'consoleports', path='console-ports') 

1500class DeviceTypeConsolePortsView(DeviceTypeComponentsView): 

1501 child_model = ConsolePortTemplate 

1502 table = tables.ConsolePortTemplateTable 

1503 filterset = filtersets.ConsolePortTemplateFilterSet 

1504 viewname = 'dcim:devicetype_consoleports' 

1505 tab = ViewTab( 

1506 label=_('Console Ports'), 

1507 badge=lambda obj: obj.console_port_template_count, 

1508 permission='dcim.view_consoleporttemplate', 

1509 weight=550, 

1510 hide_if_empty=True 

1511 ) 

1512 

1513 

1514@register_model_view(DeviceType, 'consoleserverports', path='console-server-ports') 

1515class DeviceTypeConsoleServerPortsView(DeviceTypeComponentsView): 

1516 child_model = ConsoleServerPortTemplate 

1517 table = tables.ConsoleServerPortTemplateTable 

1518 filterset = filtersets.ConsoleServerPortTemplateFilterSet 

1519 viewname = 'dcim:devicetype_consoleserverports' 

1520 tab = ViewTab( 

1521 label=_('Console Server Ports'), 

1522 badge=lambda obj: obj.console_server_port_template_count, 

1523 permission='dcim.view_consoleserverporttemplate', 

1524 weight=560, 

1525 hide_if_empty=True 

1526 ) 

1527 

1528 

1529@register_model_view(DeviceType, 'powerports', path='power-ports') 

1530class DeviceTypePowerPortsView(DeviceTypeComponentsView): 

1531 child_model = PowerPortTemplate 

1532 table = tables.PowerPortTemplateTable 

1533 filterset = filtersets.PowerPortTemplateFilterSet 

1534 viewname = 'dcim:devicetype_powerports' 

1535 tab = ViewTab( 

1536 label=_('Power Ports'), 

1537 badge=lambda obj: obj.power_port_template_count, 

1538 permission='dcim.view_powerporttemplate', 

1539 weight=570, 

1540 hide_if_empty=True 

1541 ) 

1542 

1543 

1544@register_model_view(DeviceType, 'poweroutlets', path='power-outlets') 

1545class DeviceTypePowerOutletsView(DeviceTypeComponentsView): 

1546 child_model = PowerOutletTemplate 

1547 table = tables.PowerOutletTemplateTable 

1548 filterset = filtersets.PowerOutletTemplateFilterSet 

1549 viewname = 'dcim:devicetype_poweroutlets' 

1550 tab = ViewTab( 

1551 label=_('Power Outlets'), 

1552 badge=lambda obj: obj.power_outlet_template_count, 

1553 permission='dcim.view_poweroutlettemplate', 

1554 weight=580, 

1555 hide_if_empty=True 

1556 ) 

1557 

1558 

1559@register_model_view(DeviceType, 'coolingintakes', path='cooling-intakes') 

1560class DeviceTypeCoolingIntakesView(DeviceTypeComponentsView): 

1561 child_model = CoolingIntakeTemplate 

1562 table = tables.CoolingIntakeTemplateTable 

1563 filterset = filtersets.CoolingIntakeTemplateFilterSet 

1564 viewname = 'dcim:devicetype_coolingintakes' 

1565 tab = ViewTab( 

1566 label=_('Cooling Intakes'), 

1567 badge=lambda obj: obj.cooling_intake_template_count, 

1568 permission='dcim.view_coolingintaketemplate', 

1569 weight=590, 

1570 hide_if_empty=True 

1571 ) 

1572 

1573 

1574@register_model_view(DeviceType, 'coolingoutflows', path='cooling-outflows') 

1575class DeviceTypeCoolingOutflowsView(DeviceTypeComponentsView): 

1576 child_model = CoolingOutflowTemplate 

1577 table = tables.CoolingOutflowTemplateTable 

1578 filterset = filtersets.CoolingOutflowTemplateFilterSet 

1579 viewname = 'dcim:devicetype_coolingoutflows' 

1580 tab = ViewTab( 

1581 label=_('Cooling Outflows'), 

1582 badge=lambda obj: obj.cooling_outflow_template_count, 

1583 permission='dcim.view_coolingoutflowtemplate', 

1584 weight=600, 

1585 hide_if_empty=True 

1586 ) 

1587 

1588 

1589@register_model_view(DeviceType, 'interfaces') 

1590class DeviceTypeInterfacesView(DeviceTypeComponentsView): 

1591 child_model = InterfaceTemplate 

1592 table = tables.InterfaceTemplateTable 

1593 filterset = filtersets.InterfaceTemplateFilterSet 

1594 viewname = 'dcim:devicetype_interfaces' 

1595 tab = ViewTab( 

1596 label=_('Interfaces'), 

1597 badge=lambda obj: obj.interface_template_count, 

1598 permission='dcim.view_interfacetemplate', 

1599 weight=520, 

1600 hide_if_empty=True 

1601 ) 

1602 

1603 

1604@register_model_view(DeviceType, 'frontports', path='front-ports') 

1605class DeviceTypeFrontPortsView(DeviceTypeComponentsView): 

1606 child_model = FrontPortTemplate 

1607 table = tables.FrontPortTemplateTable 

1608 filterset = filtersets.FrontPortTemplateFilterSet 

1609 viewname = 'dcim:devicetype_frontports' 

1610 tab = ViewTab( 

1611 label=_('Front Ports'), 

1612 badge=lambda obj: obj.front_port_template_count, 

1613 permission='dcim.view_frontporttemplate', 

1614 weight=530, 

1615 hide_if_empty=True 

1616 ) 

1617 

1618 

1619@register_model_view(DeviceType, 'rearports', path='rear-ports') 

1620class DeviceTypeRearPortsView(DeviceTypeComponentsView): 

1621 child_model = RearPortTemplate 

1622 table = tables.RearPortTemplateTable 

1623 filterset = filtersets.RearPortTemplateFilterSet 

1624 viewname = 'dcim:devicetype_rearports' 

1625 tab = ViewTab( 

1626 label=_('Rear Ports'), 

1627 badge=lambda obj: obj.rear_port_template_count, 

1628 permission='dcim.view_rearporttemplate', 

1629 weight=540, 

1630 hide_if_empty=True 

1631 ) 

1632 

1633 

1634@register_model_view(DeviceType, 'modulebays', path='module-bays') 

1635class DeviceTypeModuleBaysView(DeviceTypeComponentsView): 

1636 child_model = ModuleBayTemplate 

1637 table = tables.ModuleBayTemplateTable 

1638 filterset = filtersets.ModuleBayTemplateFilterSet 

1639 viewname = 'dcim:devicetype_modulebays' 

1640 tab = ViewTab( 

1641 label=_('Module Bays'), 

1642 badge=lambda obj: obj.module_bay_template_count, 

1643 permission='dcim.view_modulebaytemplate', 

1644 weight=510, 

1645 hide_if_empty=True 

1646 ) 

1647 

1648 

1649@register_model_view(DeviceType, 'devicebays', path='device-bays') 

1650class DeviceTypeDeviceBaysView(DeviceTypeComponentsView): 

1651 child_model = DeviceBayTemplate 

1652 table = tables.DeviceBayTemplateTable 

1653 filterset = filtersets.DeviceBayTemplateFilterSet 

1654 viewname = 'dcim:devicetype_devicebays' 

1655 tab = ViewTab( 

1656 label=_('Device Bays'), 

1657 badge=lambda obj: obj.device_bay_template_count, 

1658 permission='dcim.view_devicebaytemplate', 

1659 weight=500, 

1660 hide_if_empty=True 

1661 ) 

1662 

1663 

1664@register_model_view(DeviceType, 'inventoryitems', path='inventory-items') 

1665class DeviceTypeInventoryItemsView(DeviceTypeComponentsView): 

1666 child_model = InventoryItemTemplate 

1667 table = tables.InventoryItemTemplateTable 

1668 filterset = filtersets.InventoryItemTemplateFilterSet 

1669 viewname = 'dcim:devicetype_inventoryitems' 

1670 tab = ViewTab( 

1671 label=_('Inventory Items'), 

1672 badge=lambda obj: obj.inventory_item_template_count, 

1673 permission='dcim.view_inventoryitemtemplate', 

1674 weight=610, 

1675 hide_if_empty=True 

1676 ) 

1677 

1678 

1679@register_model_view(DeviceType, 'bulk_import', path='import', detail=False) 

1680class DeviceTypeImportView(generic.BulkImportView): 

1681 additional_permissions = [ 

1682 'dcim.add_devicetype', 

1683 'dcim.add_consoleporttemplate', 

1684 'dcim.add_consoleserverporttemplate', 

1685 'dcim.add_powerporttemplate', 

1686 'dcim.add_poweroutlettemplate', 

1687 'dcim.add_coolingintaketemplate', 

1688 'dcim.add_coolingoutflowtemplate', 

1689 'dcim.add_interfacetemplate', 

1690 'dcim.add_frontporttemplate', 

1691 'dcim.add_rearporttemplate', 

1692 'dcim.add_modulebaytemplate', 

1693 'dcim.add_devicebaytemplate', 

1694 'dcim.add_inventoryitemtemplate', 

1695 ] 

1696 queryset = DeviceType.objects.all() 

1697 model_form = forms.DeviceTypeImportForm 

1698 related_object_forms = { 

1699 'console-ports': forms.ConsolePortTemplateImportForm, 

1700 'console-server-ports': forms.ConsoleServerPortTemplateImportForm, 

1701 'power-ports': forms.PowerPortTemplateImportForm, 

1702 'power-outlets': forms.PowerOutletTemplateImportForm, 

1703 'cooling-intakes': forms.CoolingIntakeTemplateImportForm, 

1704 'cooling-outflows': forms.CoolingOutflowTemplateImportForm, 

1705 'interfaces': forms.InterfaceTemplateImportForm, 

1706 'rear-ports': forms.RearPortTemplateImportForm, 

1707 'front-ports': forms.FrontPortTemplateImportForm, 

1708 'port-mappings': forms.PortTemplateMappingImportForm, 

1709 'module-bays': forms.ModuleBayTemplateImportForm, 

1710 'device-bays': forms.DeviceBayTemplateImportForm, 

1711 'inventory-items': forms.InventoryItemTemplateImportForm, 

1712 } 

1713 

1714 def prep_related_object_data(self, parent, data): 

1715 data.update({'device_type': parent}) 

1716 return data 

1717 

1718 

1719@register_model_view(DeviceType, 'bulk_edit', path='edit', detail=False) 

1720class DeviceTypeBulkEditView(generic.BulkEditView): 

1721 queryset = DeviceType.objects.all() 

1722 filterset = filtersets.DeviceTypeFilterSet 

1723 table = tables.DeviceTypeTable 

1724 form = forms.DeviceTypeBulkEditForm 

1725 

1726 

1727@register_model_view(DeviceType, 'bulk_rename', path='rename', detail=False) 

1728class DeviceTypeBulkRenameView(generic.BulkRenameView): 

1729 queryset = DeviceType.objects.all() 

1730 field_name = 'model' 

1731 filterset = filtersets.DeviceTypeFilterSet 

1732 

1733 

1734@register_model_view(DeviceType, 'bulk_delete', path='delete', detail=False) 

1735class DeviceTypeBulkDeleteView(generic.BulkDeleteView): 

1736 queryset = DeviceType.objects.all() 

1737 filterset = filtersets.DeviceTypeFilterSet 

1738 table = tables.DeviceTypeTable 

1739 

1740 

1741# 

1742# Module bay types 

1743# 

1744 

1745@register_model_view(ModuleBayType, 'list', path='', detail=False) 

1746class ModuleBayTypeListView(generic.ObjectListView): 

1747 queryset = ModuleBayType.objects.all() 

1748 filterset = filtersets.ModuleBayTypeFilterSet 

1749 filterset_form = forms.ModuleBayTypeFilterForm 

1750 table = tables.ModuleBayTypeTable 

1751 

1752 

1753@register_model_view(ModuleBayType) 

1754class ModuleBayTypeView(generic.ObjectView): 

1755 template_name = 'generic/object.html' 

1756 queryset = ModuleBayType.objects.all() 

1757 layout = layout.SimpleLayout( 

1758 left_panels=[ 

1759 panels.ModuleBayTypePanel(), 

1760 TagsPanel(), 

1761 ], 

1762 right_panels=[ 

1763 CustomFieldsPanel(), 

1764 CommentsPanel(), 

1765 ], 

1766 bottom_panels=[ 

1767 ObjectsTablePanel( 

1768 model='dcim.ModuleType', 

1769 title=_('Compatible Module Types'), 

1770 filters={'module_bay_type_id': lambda ctx: ctx['object'].pk}, 

1771 ), 

1772 ObjectsTablePanel( 

1773 model='dcim.ModuleBay', 

1774 title=_('Module Bays'), 

1775 filters={'module_bay_type_id': lambda ctx: ctx['object'].pk}, 

1776 ), 

1777 ], 

1778 ) 

1779 

1780 

1781@register_model_view(ModuleBayType, 'add', detail=False) 

1782@register_model_view(ModuleBayType, 'edit') 

1783class ModuleBayTypeEditView(generic.ObjectEditView): 

1784 queryset = ModuleBayType.objects.all() 

1785 form = forms.ModuleBayTypeForm 

1786 

1787 

1788@register_model_view(ModuleBayType, 'delete') 

1789class ModuleBayTypeDeleteView(generic.ObjectDeleteView): 

1790 queryset = ModuleBayType.objects.all() 

1791 

1792 

1793@register_model_view(ModuleBayType, 'bulk_import', detail=False) 

1794class ModuleBayTypeBulkImportView(generic.BulkImportView): 

1795 queryset = ModuleBayType.objects.all() 

1796 model_form = forms.ModuleBayTypeImportForm 

1797 

1798 

1799@register_model_view(ModuleBayType, 'bulk_edit', path='edit', detail=False) 

1800class ModuleBayTypeBulkEditView(generic.BulkEditView): 

1801 queryset = ModuleBayType.objects.all() 

1802 filterset = filtersets.ModuleBayTypeFilterSet 

1803 table = tables.ModuleBayTypeTable 

1804 form = forms.ModuleBayTypeBulkEditForm 

1805 

1806 

1807@register_model_view(ModuleBayType, 'bulk_delete', path='delete', detail=False) 

1808class ModuleBayTypeBulkDeleteView(generic.BulkDeleteView): 

1809 queryset = ModuleBayType.objects.all() 

1810 filterset = filtersets.ModuleBayTypeFilterSet 

1811 table = tables.ModuleBayTypeTable 

1812 

1813 

1814# 

1815# Module type profiles 

1816# 

1817 

1818@register_model_view(ModuleTypeProfile, 'list', path='', detail=False) 

1819class ModuleTypeProfileListView(generic.ObjectListView): 

1820 queryset = ModuleTypeProfile.objects.annotate( 

1821 instance_count=count_related(ModuleType, 'profile') 

1822 ) 

1823 filterset = filtersets.ModuleTypeProfileFilterSet 

1824 filterset_form = forms.ModuleTypeProfileFilterForm 

1825 table = tables.ModuleTypeProfileTable 

1826 

1827 

1828@register_model_view(ModuleTypeProfile) 

1829class ModuleTypeProfileView(generic.ObjectView): 

1830 template_name = 'generic/object.html' 

1831 queryset = ModuleTypeProfile.objects.all() 

1832 layout = layout.SimpleLayout( 

1833 left_panels=[ 

1834 panels.ModuleTypeProfilePanel(), 

1835 TagsPanel(), 

1836 CommentsPanel(), 

1837 ], 

1838 right_panels=[ 

1839 JSONPanel(field_name='schema', title=_('Schema')), 

1840 CustomFieldsPanel(), 

1841 ], 

1842 bottom_panels=[ 

1843 ObjectsTablePanel( 

1844 model='dcim.ModuleType', 

1845 title=_('Module Types'), 

1846 filters={ 

1847 'profile_id': lambda ctx: ctx['object'].pk, 

1848 }, 

1849 exclude_columns=['profile'], 

1850 actions=[ 

1851 actions.AddObject( 

1852 'dcim.ModuleType', 

1853 url_params={ 

1854 'profile': lambda ctx: ctx['object'].pk, 

1855 } 

1856 ), 

1857 ], 

1858 ), 

1859 ] 

1860 ) 

1861 

1862 

1863@register_model_view(ModuleTypeProfile, 'add', detail=False) 

1864@register_model_view(ModuleTypeProfile, 'edit') 

1865class ModuleTypeProfileEditView(generic.ObjectEditView): 

1866 queryset = ModuleTypeProfile.objects.all() 

1867 form = forms.ModuleTypeProfileForm 

1868 

1869 

1870@register_model_view(ModuleTypeProfile, 'delete') 

1871class ModuleTypeProfileDeleteView(generic.ObjectDeleteView): 

1872 queryset = ModuleTypeProfile.objects.all() 

1873 

1874 

1875@register_model_view(ModuleTypeProfile, 'bulk_import', detail=False) 

1876class ModuleTypeProfileBulkImportView(generic.BulkImportView): 

1877 queryset = ModuleTypeProfile.objects.all() 

1878 model_form = forms.ModuleTypeProfileImportForm 

1879 

1880 

1881@register_model_view(ModuleTypeProfile, 'bulk_edit', path='edit', detail=False) 

1882class ModuleTypeProfileBulkEditView(generic.BulkEditView): 

1883 queryset = ModuleTypeProfile.objects.annotate( 

1884 instance_count=count_related(Module, 'module_type') 

1885 ) 

1886 filterset = filtersets.ModuleTypeProfileFilterSet 

1887 table = tables.ModuleTypeProfileTable 

1888 form = forms.ModuleTypeProfileBulkEditForm 

1889 

1890 

1891@register_model_view(ModuleTypeProfile, 'bulk_rename', path='rename', detail=False) 

1892class ModuleTypeProfileBulkRenameView(generic.BulkRenameView): 

1893 queryset = ModuleTypeProfile.objects.all() 

1894 filterset = filtersets.ModuleTypeProfileFilterSet 

1895 

1896 

1897@register_model_view(ModuleTypeProfile, 'bulk_delete', path='delete', detail=False) 

1898class ModuleTypeProfileBulkDeleteView(generic.BulkDeleteView): 

1899 queryset = ModuleTypeProfile.objects.annotate( 

1900 instance_count=count_related(Module, 'module_type') 

1901 ) 

1902 filterset = filtersets.ModuleTypeProfileFilterSet 

1903 table = tables.ModuleTypeProfileTable 

1904 

1905 

1906# 

1907# Module types 

1908# 

1909 

1910@register_model_view(ModuleType, 'list', path='', detail=False) 

1911class ModuleTypeListView(generic.ObjectListView): 

1912 queryset = ModuleType.objects.all() 

1913 filterset = filtersets.ModuleTypeFilterSet 

1914 filterset_form = forms.ModuleTypeFilterForm 

1915 table = tables.ModuleTypeTable 

1916 

1917 def export_yaml(self): 

1918 # Avoid one module_bay_types query per module type across the export. (Unlike 

1919 # DeviceType.to_yaml(), ModuleType.to_yaml() doesn't export module bay templates at 

1920 # all, so there's nothing to prefetch alongside it.) 

1921 self.queryset = self.queryset.prefetch_related('module_bay_types') 

1922 return super().export_yaml() 

1923 

1924 

1925@register_model_view(ModuleType) 

1926class ModuleTypeView(GetRelatedModelsMixin, generic.ObjectView): 

1927 queryset = ModuleType.objects.all() 

1928 layout = layout.SimpleLayout( 

1929 breadcrumbs=[ 

1930 Breadcrumb('manufacturer', url=filtered_list_url('dcim:moduletype_list', 'manufacturer_id')), 

1931 ], 

1932 left_panels=[ 

1933 panels.ModuleTypePanel(), 

1934 TagsPanel(), 

1935 CommentsPanel(), 

1936 ], 

1937 right_panels=[ 

1938 TemplatePanel( 

1939 title=_('Attributes'), 

1940 template_name='dcim/panels/module_type_attributes.html', 

1941 ), 

1942 RelatedObjectsPanel(), 

1943 CustomFieldsPanel(), 

1944 ImageAttachmentsPanel(), 

1945 ], 

1946 ) 

1947 

1948 def get_extra_context(self, request, instance): 

1949 return { 

1950 'related_models': self.get_related_models(request, instance, omit=[ 

1951 ConsolePortTemplate, ConsoleServerPortTemplate, CoolingIntakeTemplate, CoolingOutflowTemplate, 

1952 DeviceBayTemplate, FrontPortTemplate, InventoryItemTemplate, InterfaceTemplate, ModuleBayTemplate, 

1953 PowerOutletTemplate, PowerPortTemplate, RearPortTemplate, 

1954 ]), 

1955 } 

1956 

1957 

1958@register_model_view(ModuleType, 'add', detail=False) 

1959@register_model_view(ModuleType, 'edit') 

1960class ModuleTypeEditView(generic.ObjectEditView): 

1961 queryset = ModuleType.objects.all() 

1962 form = forms.ModuleTypeForm 

1963 

1964 def post(self, request, *args, **kwargs): 

1965 response = super().post(request, *args, **kwargs) 

1966 # A successful save always redirects (302) or returns an HTMX redirect header. 

1967 # Quick-add creates new objects (no pk in kwargs), so it is excluded by the pk guard below. 

1968 saved = ( 

1969 getattr(response, 'status_code', None) == 302 or 

1970 (hasattr(response, 'headers') and 'HX-Location' in response.headers) 

1971 ) 

1972 if saved and kwargs.get('pk'): 

1973 try: 

1974 module_type = ModuleType.objects.prefetch_related('module_bay_types').get(pk=kwargs['pk']) 

1975 count = module_type.get_incompatible_modules().count() 

1976 if count: 

1977 messages.warning( 

1978 request, 

1979 ngettext( 

1980 '%(count)d installed module of this type is now incompatible with its module bay ' 

1981 'due to conflicting bay type constraints.', 

1982 '%(count)d installed modules of this type are now incompatible with their module bays ' 

1983 'due to conflicting bay type constraints.', 

1984 count, 

1985 ) % {'count': count} 

1986 ) 

1987 except ModuleType.DoesNotExist: 

1988 pass 

1989 return response 

1990 

1991 

1992@register_model_view(ModuleType, 'delete') 

1993class ModuleTypeDeleteView(generic.ObjectDeleteView): 

1994 queryset = ModuleType.objects.all() 

1995 

1996 

1997@register_model_view(ModuleType, 'consoleports', path='console-ports') 

1998class ModuleTypeConsolePortsView(ModuleTypeComponentsView): 

1999 child_model = ConsolePortTemplate 

2000 table = tables.ConsolePortTemplateTable 

2001 filterset = filtersets.ConsolePortTemplateFilterSet 

2002 viewname = 'dcim:moduletype_consoleports' 

2003 tab = ViewTab( 

2004 label=_('Console Ports'), 

2005 badge=lambda obj: obj.console_port_template_count, 

2006 permission='dcim.view_consoleporttemplate', 

2007 weight=530, 

2008 hide_if_empty=True 

2009 ) 

2010 

2011 

2012@register_model_view(ModuleType, 'consoleserverports', path='console-server-ports') 

2013class ModuleTypeConsoleServerPortsView(ModuleTypeComponentsView): 

2014 child_model = ConsoleServerPortTemplate 

2015 table = tables.ConsoleServerPortTemplateTable 

2016 filterset = filtersets.ConsoleServerPortTemplateFilterSet 

2017 viewname = 'dcim:moduletype_consoleserverports' 

2018 tab = ViewTab( 

2019 label=_('Console Server Ports'), 

2020 badge=lambda obj: obj.console_server_port_template_count, 

2021 permission='dcim.view_consoleserverporttemplate', 

2022 weight=540, 

2023 hide_if_empty=True 

2024 ) 

2025 

2026 

2027@register_model_view(ModuleType, 'powerports', path='power-ports') 

2028class ModuleTypePowerPortsView(ModuleTypeComponentsView): 

2029 child_model = PowerPortTemplate 

2030 table = tables.PowerPortTemplateTable 

2031 filterset = filtersets.PowerPortTemplateFilterSet 

2032 viewname = 'dcim:moduletype_powerports' 

2033 tab = ViewTab( 

2034 label=_('Power Ports'), 

2035 badge=lambda obj: obj.power_port_template_count, 

2036 permission='dcim.view_powerporttemplate', 

2037 weight=550, 

2038 hide_if_empty=True 

2039 ) 

2040 

2041 

2042@register_model_view(ModuleType, 'poweroutlets', path='power-outlets') 

2043class ModuleTypePowerOutletsView(ModuleTypeComponentsView): 

2044 child_model = PowerOutletTemplate 

2045 table = tables.PowerOutletTemplateTable 

2046 filterset = filtersets.PowerOutletTemplateFilterSet 

2047 viewname = 'dcim:moduletype_poweroutlets' 

2048 tab = ViewTab( 

2049 label=_('Power Outlets'), 

2050 badge=lambda obj: obj.power_outlet_template_count, 

2051 permission='dcim.view_poweroutlettemplate', 

2052 weight=560, 

2053 hide_if_empty=True 

2054 ) 

2055 

2056 

2057@register_model_view(ModuleType, 'coolingintakes', path='cooling-intakes') 

2058class ModuleTypeCoolingIntakesView(ModuleTypeComponentsView): 

2059 child_model = CoolingIntakeTemplate 

2060 table = tables.CoolingIntakeTemplateTable 

2061 filterset = filtersets.CoolingIntakeTemplateFilterSet 

2062 viewname = 'dcim:moduletype_coolingintakes' 

2063 tab = ViewTab( 

2064 label=_('Cooling Intakes'), 

2065 badge=lambda obj: obj.cooling_intake_template_count, 

2066 permission='dcim.view_coolingintaketemplate', 

2067 weight=570, 

2068 hide_if_empty=True 

2069 ) 

2070 

2071 

2072@register_model_view(ModuleType, 'coolingoutflows', path='cooling-outflows') 

2073class ModuleTypeCoolingOutflowsView(ModuleTypeComponentsView): 

2074 child_model = CoolingOutflowTemplate 

2075 table = tables.CoolingOutflowTemplateTable 

2076 filterset = filtersets.CoolingOutflowTemplateFilterSet 

2077 viewname = 'dcim:moduletype_coolingoutflows' 

2078 tab = ViewTab( 

2079 label=_('Cooling Outflows'), 

2080 badge=lambda obj: obj.cooling_outflow_template_count, 

2081 permission='dcim.view_coolingoutflowtemplate', 

2082 weight=580, 

2083 hide_if_empty=True 

2084 ) 

2085 

2086 

2087@register_model_view(ModuleType, 'interfaces') 

2088class ModuleTypeInterfacesView(ModuleTypeComponentsView): 

2089 child_model = InterfaceTemplate 

2090 table = tables.InterfaceTemplateTable 

2091 filterset = filtersets.InterfaceTemplateFilterSet 

2092 viewname = 'dcim:moduletype_interfaces' 

2093 tab = ViewTab( 

2094 label=_('Interfaces'), 

2095 badge=lambda obj: obj.interface_template_count, 

2096 permission='dcim.view_interfacetemplate', 

2097 weight=500, 

2098 hide_if_empty=True 

2099 ) 

2100 

2101 

2102@register_model_view(ModuleType, 'frontports', path='front-ports') 

2103class ModuleTypeFrontPortsView(ModuleTypeComponentsView): 

2104 child_model = FrontPortTemplate 

2105 table = tables.FrontPortTemplateTable 

2106 filterset = filtersets.FrontPortTemplateFilterSet 

2107 viewname = 'dcim:moduletype_frontports' 

2108 tab = ViewTab( 

2109 label=_('Front Ports'), 

2110 badge=lambda obj: obj.front_port_template_count, 

2111 permission='dcim.view_frontporttemplate', 

2112 weight=510, 

2113 hide_if_empty=True 

2114 ) 

2115 

2116 

2117@register_model_view(ModuleType, 'rearports', path='rear-ports') 

2118class ModuleTypeRearPortsView(ModuleTypeComponentsView): 

2119 child_model = RearPortTemplate 

2120 table = tables.RearPortTemplateTable 

2121 filterset = filtersets.RearPortTemplateFilterSet 

2122 viewname = 'dcim:moduletype_rearports' 

2123 tab = ViewTab( 

2124 label=_('Rear Ports'), 

2125 badge=lambda obj: obj.rear_port_template_count, 

2126 permission='dcim.view_rearporttemplate', 

2127 weight=520, 

2128 hide_if_empty=True 

2129 ) 

2130 

2131 

2132@register_model_view(ModuleType, 'modulebays', path='module-bays') 

2133class ModuleTypeModuleBaysView(ModuleTypeComponentsView): 

2134 child_model = ModuleBayTemplate 

2135 table = tables.ModuleBayTemplateTable 

2136 filterset = filtersets.ModuleBayTemplateFilterSet 

2137 viewname = 'dcim:moduletype_modulebays' 

2138 tab = ViewTab( 

2139 label=_('Module Bays'), 

2140 badge=lambda obj: obj.module_bay_template_count, 

2141 permission='dcim.view_modulebaytemplate', 

2142 weight=590, 

2143 hide_if_empty=True 

2144 ) 

2145 

2146 

2147@register_model_view(ModuleType, 'bulk_import', path='import', detail=False) 

2148class ModuleTypeImportView(generic.BulkImportView): 

2149 additional_permissions = [ 

2150 'dcim.add_moduletype', 

2151 'dcim.add_consoleporttemplate', 

2152 'dcim.add_consoleserverporttemplate', 

2153 'dcim.add_powerporttemplate', 

2154 'dcim.add_poweroutlettemplate', 

2155 'dcim.add_coolingintaketemplate', 

2156 'dcim.add_coolingoutflowtemplate', 

2157 'dcim.add_interfacetemplate', 

2158 'dcim.add_frontporttemplate', 

2159 'dcim.add_rearporttemplate', 

2160 'dcim.add_modulebaytemplate', 

2161 ] 

2162 queryset = ModuleType.objects.all() 

2163 model_form = forms.ModuleTypeImportForm 

2164 related_object_forms = { 

2165 'console-ports': forms.ConsolePortTemplateImportForm, 

2166 'console-server-ports': forms.ConsoleServerPortTemplateImportForm, 

2167 'power-ports': forms.PowerPortTemplateImportForm, 

2168 'power-outlets': forms.PowerOutletTemplateImportForm, 

2169 'cooling-intakes': forms.CoolingIntakeTemplateImportForm, 

2170 'cooling-outflows': forms.CoolingOutflowTemplateImportForm, 

2171 'interfaces': forms.InterfaceTemplateImportForm, 

2172 'rear-ports': forms.RearPortTemplateImportForm, 

2173 'front-ports': forms.FrontPortTemplateImportForm, 

2174 'port-mappings': forms.PortTemplateMappingImportForm, 

2175 'module-bays': forms.ModuleBayTemplateImportForm, 

2176 } 

2177 

2178 def prep_related_object_data(self, parent, data): 

2179 data.update({'module_type': parent}) 

2180 return data 

2181 

2182 

2183@register_model_view(ModuleType, 'bulk_edit', path='edit', detail=False) 

2184class ModuleTypeBulkEditView(generic.BulkEditView): 

2185 queryset = ModuleType.objects.annotate( 

2186 instance_count=count_related(Module, 'module_type') 

2187 ) 

2188 filterset = filtersets.ModuleTypeFilterSet 

2189 table = tables.ModuleTypeTable 

2190 form = forms.ModuleTypeBulkEditForm 

2191 

2192 def post_save_operations(self, form, obj): 

2193 super().post_save_operations(form, obj) 

2194 add = form.cleaned_data.get('add_module_bay_types') 

2195 remove = form.cleaned_data.get('remove_module_bay_types') 

2196 if add: 

2197 obj.module_bay_types.add(*add) 

2198 if remove: 

2199 obj.module_bay_types.remove(*remove) 

2200 if add or remove: 

2201 # Counts current incompatibilities, not just newly-introduced ones; may over-warn 

2202 # if pre-existing incompatibilities exist, but safe to under-warn on. 

2203 self._incompatible_count += obj.get_incompatible_modules().count() 

2204 

2205 def post(self, request, **kwargs): 

2206 self._incompatible_count = 0 

2207 response = super().post(request, **kwargs) 

2208 if self._incompatible_count and getattr(response, 'status_code', None) == 302: 

2209 messages.warning( 

2210 request, 

2211 ngettext( 

2212 '%(count)d installed module is now incompatible with its module bay ' 

2213 'due to conflicting bay type constraints.', 

2214 '%(count)d installed modules are now incompatible with their module bays ' 

2215 'due to conflicting bay type constraints.', 

2216 self._incompatible_count, 

2217 ) % {'count': self._incompatible_count} 

2218 ) 

2219 return response 

2220 

2221 

2222@register_model_view(ModuleType, 'bulk_rename', path='rename', detail=False) 

2223class ModuleTypeBulkRenameView(generic.BulkRenameView): 

2224 queryset = ModuleType.objects.all() 

2225 filterset = filtersets.ModuleTypeFilterSet 

2226 field_name = 'model' 

2227 

2228 

2229@register_model_view(ModuleType, 'bulk_delete', path='delete', detail=False) 

2230class ModuleTypeBulkDeleteView(generic.BulkDeleteView): 

2231 queryset = ModuleType.objects.annotate( 

2232 instance_count=count_related(Module, 'module_type') 

2233 ) 

2234 filterset = filtersets.ModuleTypeFilterSet 

2235 table = tables.ModuleTypeTable 

2236 

2237 

2238# 

2239# Console port templates 

2240# 

2241 

2242@register_model_view(ConsolePortTemplate, 'add', detail=False) 

2243class ConsolePortTemplateCreateView(generic.ComponentCreateView): 

2244 queryset = ConsolePortTemplate.objects.all() 

2245 form = forms.ConsolePortTemplateCreateForm 

2246 model_form = forms.ConsolePortTemplateForm 

2247 

2248 

2249@register_model_view(ConsolePortTemplate, 'edit') 

2250class ConsolePortTemplateEditView(generic.ObjectEditView): 

2251 queryset = ConsolePortTemplate.objects.all() 

2252 form = forms.ConsolePortTemplateForm 

2253 

2254 

2255@register_model_view(ConsolePortTemplate, 'delete') 

2256class ConsolePortTemplateDeleteView(generic.ObjectDeleteView): 

2257 queryset = ConsolePortTemplate.objects.all() 

2258 

2259 

2260@register_model_view(ConsolePortTemplate, 'bulk_edit', path='edit', detail=False) 

2261class ConsolePortTemplateBulkEditView(generic.BulkEditView): 

2262 queryset = ConsolePortTemplate.objects.all() 

2263 table = tables.ConsolePortTemplateTable 

2264 form = forms.ConsolePortTemplateBulkEditForm 

2265 

2266 

2267@register_model_view(ConsolePortTemplate, 'bulk_rename', path='rename', detail=False) 

2268class ConsolePortTemplateBulkRenameView(generic.BulkRenameView): 

2269 queryset = ConsolePortTemplate.objects.all() 

2270 rename_fields = ('name', 'label') 

2271 

2272 

2273@register_model_view(ConsolePortTemplate, 'bulk_delete', path='delete', detail=False) 

2274class ConsolePortTemplateBulkDeleteView(generic.BulkDeleteView): 

2275 queryset = ConsolePortTemplate.objects.all() 

2276 table = tables.ConsolePortTemplateTable 

2277 

2278 

2279# 

2280# Console server port templates 

2281# 

2282 

2283@register_model_view(ConsoleServerPortTemplate, 'add', detail=False) 

2284class ConsoleServerPortTemplateCreateView(generic.ComponentCreateView): 

2285 queryset = ConsoleServerPortTemplate.objects.all() 

2286 form = forms.ConsoleServerPortTemplateCreateForm 

2287 model_form = forms.ConsoleServerPortTemplateForm 

2288 

2289 

2290@register_model_view(ConsoleServerPortTemplate, 'edit') 

2291class ConsoleServerPortTemplateEditView(generic.ObjectEditView): 

2292 queryset = ConsoleServerPortTemplate.objects.all() 

2293 form = forms.ConsoleServerPortTemplateForm 

2294 

2295 

2296@register_model_view(ConsoleServerPortTemplate, 'delete') 

2297class ConsoleServerPortTemplateDeleteView(generic.ObjectDeleteView): 

2298 queryset = ConsoleServerPortTemplate.objects.all() 

2299 

2300 

2301@register_model_view(ConsoleServerPortTemplate, 'bulk_edit', path='edit', detail=False) 

2302class ConsoleServerPortTemplateBulkEditView(generic.BulkEditView): 

2303 queryset = ConsoleServerPortTemplate.objects.all() 

2304 table = tables.ConsoleServerPortTemplateTable 

2305 form = forms.ConsoleServerPortTemplateBulkEditForm 

2306 

2307 

2308@register_model_view(ConsoleServerPortTemplate, 'bulk_rename', detail=False) 

2309class ConsoleServerPortTemplateBulkRenameView(generic.BulkRenameView): 

2310 queryset = ConsoleServerPortTemplate.objects.all() 

2311 rename_fields = ('name', 'label') 

2312 

2313 

2314@register_model_view(ConsoleServerPortTemplate, 'bulk_delete', path='delete', detail=False) 

2315class ConsoleServerPortTemplateBulkDeleteView(generic.BulkDeleteView): 

2316 queryset = ConsoleServerPortTemplate.objects.all() 

2317 table = tables.ConsoleServerPortTemplateTable 

2318 

2319 

2320# 

2321# Power port templates 

2322# 

2323 

2324@register_model_view(PowerPortTemplate, 'add', detail=False) 

2325class PowerPortTemplateCreateView(generic.ComponentCreateView): 

2326 queryset = PowerPortTemplate.objects.all() 

2327 form = forms.PowerPortTemplateCreateForm 

2328 model_form = forms.PowerPortTemplateForm 

2329 

2330 

2331@register_model_view(PowerPortTemplate, 'edit') 

2332class PowerPortTemplateEditView(generic.ObjectEditView): 

2333 queryset = PowerPortTemplate.objects.all() 

2334 form = forms.PowerPortTemplateForm 

2335 

2336 

2337@register_model_view(PowerPortTemplate, 'delete') 

2338class PowerPortTemplateDeleteView(generic.ObjectDeleteView): 

2339 queryset = PowerPortTemplate.objects.all() 

2340 

2341 

2342@register_model_view(PowerPortTemplate, 'bulk_edit', path='edit', detail=False) 

2343class PowerPortTemplateBulkEditView(generic.BulkEditView): 

2344 queryset = PowerPortTemplate.objects.all() 

2345 table = tables.PowerPortTemplateTable 

2346 form = forms.PowerPortTemplateBulkEditForm 

2347 

2348 

2349@register_model_view(PowerPortTemplate, 'bulk_rename', path='rename', detail=False) 

2350class PowerPortTemplateBulkRenameView(generic.BulkRenameView): 

2351 queryset = PowerPortTemplate.objects.all() 

2352 rename_fields = ('name', 'label') 

2353 

2354 

2355@register_model_view(PowerPortTemplate, 'bulk_delete', path='delete', detail=False) 

2356class PowerPortTemplateBulkDeleteView(generic.BulkDeleteView): 

2357 queryset = PowerPortTemplate.objects.all() 

2358 table = tables.PowerPortTemplateTable 

2359 

2360 

2361# 

2362# Power outlet templates 

2363# 

2364 

2365@register_model_view(PowerOutletTemplate, 'add', detail=False) 

2366class PowerOutletTemplateCreateView(generic.ComponentCreateView): 

2367 queryset = PowerOutletTemplate.objects.all() 

2368 form = forms.PowerOutletTemplateCreateForm 

2369 model_form = forms.PowerOutletTemplateForm 

2370 

2371 

2372@register_model_view(PowerOutletTemplate, 'edit') 

2373class PowerOutletTemplateEditView(generic.ObjectEditView): 

2374 queryset = PowerOutletTemplate.objects.all() 

2375 form = forms.PowerOutletTemplateForm 

2376 

2377 

2378@register_model_view(PowerOutletTemplate, 'delete') 

2379class PowerOutletTemplateDeleteView(generic.ObjectDeleteView): 

2380 queryset = PowerOutletTemplate.objects.all() 

2381 

2382 

2383@register_model_view(PowerOutletTemplate, 'bulk_edit', path='edit', detail=False) 

2384class PowerOutletTemplateBulkEditView(generic.BulkEditView): 

2385 queryset = PowerOutletTemplate.objects.all() 

2386 table = tables.PowerOutletTemplateTable 

2387 form = forms.PowerOutletTemplateBulkEditForm 

2388 

2389 

2390@register_model_view(PowerOutletTemplate, 'bulk_rename', path='rename', detail=False) 

2391class PowerOutletTemplateBulkRenameView(generic.BulkRenameView): 

2392 queryset = PowerOutletTemplate.objects.all() 

2393 rename_fields = ('name', 'label') 

2394 

2395 

2396@register_model_view(PowerOutletTemplate, 'bulk_delete', path='delete', detail=False) 

2397class PowerOutletTemplateBulkDeleteView(generic.BulkDeleteView): 

2398 queryset = PowerOutletTemplate.objects.all() 

2399 table = tables.PowerOutletTemplateTable 

2400 

2401 

2402# 

2403# Cooling port templates 

2404# 

2405 

2406@register_model_view(CoolingIntakeTemplate, 'add', detail=False) 

2407class CoolingIntakeTemplateCreateView(generic.ComponentCreateView): 

2408 queryset = CoolingIntakeTemplate.objects.all() 

2409 form = forms.CoolingIntakeTemplateCreateForm 

2410 model_form = forms.CoolingIntakeTemplateForm 

2411 

2412 

2413@register_model_view(CoolingIntakeTemplate, 'edit') 

2414class CoolingIntakeTemplateEditView(generic.ObjectEditView): 

2415 queryset = CoolingIntakeTemplate.objects.all() 

2416 form = forms.CoolingIntakeTemplateForm 

2417 

2418 

2419@register_model_view(CoolingIntakeTemplate, 'delete') 

2420class CoolingIntakeTemplateDeleteView(generic.ObjectDeleteView): 

2421 queryset = CoolingIntakeTemplate.objects.all() 

2422 

2423 

2424@register_model_view(CoolingIntakeTemplate, 'bulk_edit', path='edit', detail=False) 

2425class CoolingIntakeTemplateBulkEditView(generic.BulkEditView): 

2426 queryset = CoolingIntakeTemplate.objects.all() 

2427 table = tables.CoolingIntakeTemplateTable 

2428 form = forms.CoolingIntakeTemplateBulkEditForm 

2429 

2430 

2431@register_model_view(CoolingIntakeTemplate, 'bulk_rename', path='rename', detail=False) 

2432class CoolingIntakeTemplateBulkRenameView(generic.BulkRenameView): 

2433 queryset = CoolingIntakeTemplate.objects.all() 

2434 rename_fields = ('name', 'label') 

2435 

2436 

2437@register_model_view(CoolingIntakeTemplate, 'bulk_delete', path='delete', detail=False) 

2438class CoolingIntakeTemplateBulkDeleteView(generic.BulkDeleteView): 

2439 queryset = CoolingIntakeTemplate.objects.all() 

2440 table = tables.CoolingIntakeTemplateTable 

2441 

2442 

2443# 

2444# Cooling outlet templates 

2445# 

2446 

2447@register_model_view(CoolingOutflowTemplate, 'add', detail=False) 

2448class CoolingOutflowTemplateCreateView(generic.ComponentCreateView): 

2449 queryset = CoolingOutflowTemplate.objects.all() 

2450 form = forms.CoolingOutflowTemplateCreateForm 

2451 model_form = forms.CoolingOutflowTemplateForm 

2452 

2453 

2454@register_model_view(CoolingOutflowTemplate, 'edit') 

2455class CoolingOutflowTemplateEditView(generic.ObjectEditView): 

2456 queryset = CoolingOutflowTemplate.objects.all() 

2457 form = forms.CoolingOutflowTemplateForm 

2458 

2459 

2460@register_model_view(CoolingOutflowTemplate, 'delete') 

2461class CoolingOutflowTemplateDeleteView(generic.ObjectDeleteView): 

2462 queryset = CoolingOutflowTemplate.objects.all() 

2463 

2464 

2465@register_model_view(CoolingOutflowTemplate, 'bulk_edit', path='edit', detail=False) 

2466class CoolingOutflowTemplateBulkEditView(generic.BulkEditView): 

2467 queryset = CoolingOutflowTemplate.objects.all() 

2468 table = tables.CoolingOutflowTemplateTable 

2469 form = forms.CoolingOutflowTemplateBulkEditForm 

2470 

2471 

2472@register_model_view(CoolingOutflowTemplate, 'bulk_rename', path='rename', detail=False) 

2473class CoolingOutflowTemplateBulkRenameView(generic.BulkRenameView): 

2474 queryset = CoolingOutflowTemplate.objects.all() 

2475 rename_fields = ('name', 'label') 

2476 

2477 

2478@register_model_view(CoolingOutflowTemplate, 'bulk_delete', path='delete', detail=False) 

2479class CoolingOutflowTemplateBulkDeleteView(generic.BulkDeleteView): 

2480 queryset = CoolingOutflowTemplate.objects.all() 

2481 table = tables.CoolingOutflowTemplateTable 

2482 

2483 

2484# 

2485# Interface templates 

2486# 

2487 

2488@register_model_view(InterfaceTemplate, 'add', detail=False) 

2489class InterfaceTemplateCreateView(generic.ComponentCreateView): 

2490 queryset = InterfaceTemplate.objects.all() 

2491 form = forms.InterfaceTemplateCreateForm 

2492 model_form = forms.InterfaceTemplateForm 

2493 

2494 

2495@register_model_view(InterfaceTemplate, 'edit') 

2496class InterfaceTemplateEditView(generic.ObjectEditView): 

2497 queryset = InterfaceTemplate.objects.all() 

2498 form = forms.InterfaceTemplateForm 

2499 

2500 

2501@register_model_view(InterfaceTemplate, 'delete') 

2502class InterfaceTemplateDeleteView(generic.ObjectDeleteView): 

2503 queryset = InterfaceTemplate.objects.all() 

2504 

2505 

2506@register_model_view(InterfaceTemplate, 'bulk_edit', path='edit', detail=False) 

2507class InterfaceTemplateBulkEditView(generic.BulkEditView): 

2508 queryset = InterfaceTemplate.objects.all() 

2509 table = tables.InterfaceTemplateTable 

2510 form = forms.InterfaceTemplateBulkEditForm 

2511 

2512 

2513@register_model_view(InterfaceTemplate, 'bulk_rename', path='rename', detail=False) 

2514class InterfaceTemplateBulkRenameView(generic.BulkRenameView): 

2515 queryset = InterfaceTemplate.objects.all() 

2516 rename_fields = ('name', 'label') 

2517 

2518 

2519@register_model_view(InterfaceTemplate, 'bulk_delete', path='delete', detail=False) 

2520class InterfaceTemplateBulkDeleteView(generic.BulkDeleteView): 

2521 queryset = InterfaceTemplate.objects.all() 

2522 table = tables.InterfaceTemplateTable 

2523 

2524 

2525# 

2526# Front port templates 

2527# 

2528 

2529@register_model_view(FrontPortTemplate, 'add', detail=False) 

2530class FrontPortTemplateCreateView(generic.ComponentCreateView): 

2531 queryset = FrontPortTemplate.objects.all() 

2532 form = forms.FrontPortTemplateCreateForm 

2533 model_form = forms.FrontPortTemplateForm 

2534 

2535 

2536@register_model_view(FrontPortTemplate, 'edit') 

2537class FrontPortTemplateEditView(generic.ObjectEditView): 

2538 queryset = FrontPortTemplate.objects.all() 

2539 form = forms.FrontPortTemplateForm 

2540 

2541 

2542@register_model_view(FrontPortTemplate, 'delete') 

2543class FrontPortTemplateDeleteView(generic.ObjectDeleteView): 

2544 queryset = FrontPortTemplate.objects.all() 

2545 

2546 

2547@register_model_view(FrontPortTemplate, 'bulk_edit', path='edit', detail=False) 

2548class FrontPortTemplateBulkEditView(generic.BulkEditView): 

2549 queryset = FrontPortTemplate.objects.all() 

2550 table = tables.FrontPortTemplateTable 

2551 form = forms.FrontPortTemplateBulkEditForm 

2552 

2553 

2554@register_model_view(FrontPortTemplate, 'bulk_rename', path='rename', detail=False) 

2555class FrontPortTemplateBulkRenameView(generic.BulkRenameView): 

2556 queryset = FrontPortTemplate.objects.all() 

2557 rename_fields = ('name', 'label') 

2558 

2559 

2560@register_model_view(FrontPortTemplate, 'bulk_delete', path='delete', detail=False) 

2561class FrontPortTemplateBulkDeleteView(generic.BulkDeleteView): 

2562 queryset = FrontPortTemplate.objects.all() 

2563 table = tables.FrontPortTemplateTable 

2564 

2565 

2566# 

2567# Rear port templates 

2568# 

2569 

2570@register_model_view(RearPortTemplate, 'add', detail=False) 

2571class RearPortTemplateCreateView(generic.ComponentCreateView): 

2572 queryset = RearPortTemplate.objects.all() 

2573 form = forms.RearPortTemplateCreateForm 

2574 model_form = forms.RearPortTemplateForm 

2575 

2576 

2577@register_model_view(RearPortTemplate, 'edit') 

2578class RearPortTemplateEditView(generic.ObjectEditView): 

2579 queryset = RearPortTemplate.objects.all() 

2580 form = forms.RearPortTemplateForm 

2581 

2582 

2583@register_model_view(RearPortTemplate, 'delete') 

2584class RearPortTemplateDeleteView(generic.ObjectDeleteView): 

2585 queryset = RearPortTemplate.objects.all() 

2586 

2587 

2588@register_model_view(RearPortTemplate, 'bulk_edit', path='edit', detail=False) 

2589class RearPortTemplateBulkEditView(generic.BulkEditView): 

2590 queryset = RearPortTemplate.objects.all() 

2591 table = tables.RearPortTemplateTable 

2592 form = forms.RearPortTemplateBulkEditForm 

2593 

2594 

2595@register_model_view(RearPortTemplate, 'bulk_rename', path='rename', detail=False) 

2596class RearPortTemplateBulkRenameView(generic.BulkRenameView): 

2597 queryset = RearPortTemplate.objects.all() 

2598 rename_fields = ('name', 'label') 

2599 

2600 

2601@register_model_view(RearPortTemplate, 'bulk_delete', path='delete', detail=False) 

2602class RearPortTemplateBulkDeleteView(generic.BulkDeleteView): 

2603 queryset = RearPortTemplate.objects.all() 

2604 table = tables.RearPortTemplateTable 

2605 

2606 

2607# 

2608# Module bay templates 

2609# 

2610 

2611@register_model_view(ModuleBayTemplate, 'add', detail=False) 

2612class ModuleBayTemplateCreateView(generic.ComponentCreateView): 

2613 queryset = ModuleBayTemplate.objects.all() 

2614 form = forms.ModuleBayTemplateCreateForm 

2615 model_form = forms.ModuleBayTemplateForm 

2616 

2617 

2618@register_model_view(ModuleBayTemplate, 'edit') 

2619class ModuleBayTemplateEditView(generic.ObjectEditView): 

2620 queryset = ModuleBayTemplate.objects.all() 

2621 form = forms.ModuleBayTemplateForm 

2622 

2623 

2624@register_model_view(ModuleBayTemplate, 'delete') 

2625class ModuleBayTemplateDeleteView(generic.ObjectDeleteView): 

2626 queryset = ModuleBayTemplate.objects.all() 

2627 

2628 

2629@register_model_view(ModuleBayTemplate, 'bulk_edit', path='edit', detail=False) 

2630class ModuleBayTemplateBulkEditView(generic.BulkEditView): 

2631 queryset = ModuleBayTemplate.objects.all() 

2632 table = tables.ModuleBayTemplateTable 

2633 form = forms.ModuleBayTemplateBulkEditForm 

2634 

2635 def post_save_operations(self, form, obj): 

2636 # Unlike the ModuleType and ModuleBay editors, no compatibility warning: a template 

2637 # has no installed module to invalidate. 

2638 super().post_save_operations(form, obj) 

2639 add = form.cleaned_data.get('add_module_bay_types') 

2640 remove = form.cleaned_data.get('remove_module_bay_types') 

2641 if add: 

2642 obj.module_bay_types.add(*add) 

2643 if remove: 

2644 obj.module_bay_types.remove(*remove) 

2645 

2646 

2647@register_model_view(ModuleBayTemplate, 'bulk_rename', path='rename', detail=False) 

2648class ModuleBayTemplateBulkRenameView(generic.BulkRenameView): 

2649 queryset = ModuleBayTemplate.objects.all() 

2650 rename_fields = ('name', 'label') 

2651 

2652 

2653@register_model_view(ModuleBayTemplate, 'bulk_delete', path='delete', detail=False) 

2654class ModuleBayTemplateBulkDeleteView(generic.BulkDeleteView): 

2655 queryset = ModuleBayTemplate.objects.all() 

2656 table = tables.ModuleBayTemplateTable 

2657 

2658 

2659# 

2660# Device bay templates 

2661# 

2662 

2663@register_model_view(DeviceBayTemplate, 'add', detail=False) 

2664class DeviceBayTemplateCreateView(generic.ComponentCreateView): 

2665 queryset = DeviceBayTemplate.objects.all() 

2666 form = forms.DeviceBayTemplateCreateForm 

2667 model_form = forms.DeviceBayTemplateForm 

2668 

2669 

2670@register_model_view(DeviceBayTemplate, 'edit') 

2671class DeviceBayTemplateEditView(generic.ObjectEditView): 

2672 queryset = DeviceBayTemplate.objects.all() 

2673 form = forms.DeviceBayTemplateForm 

2674 

2675 

2676@register_model_view(DeviceBayTemplate, 'delete') 

2677class DeviceBayTemplateDeleteView(generic.ObjectDeleteView): 

2678 queryset = DeviceBayTemplate.objects.all() 

2679 

2680 

2681@register_model_view(DeviceBayTemplate, 'bulk_edit', path='edit', detail=False) 

2682class DeviceBayTemplateBulkEditView(generic.BulkEditView): 

2683 queryset = DeviceBayTemplate.objects.all() 

2684 table = tables.DeviceBayTemplateTable 

2685 form = forms.DeviceBayTemplateBulkEditForm 

2686 

2687 

2688@register_model_view(DeviceBayTemplate, 'bulk_rename', path='rename', detail=False) 

2689class DeviceBayTemplateBulkRenameView(generic.BulkRenameView): 

2690 queryset = DeviceBayTemplate.objects.all() 

2691 rename_fields = ('name', 'label') 

2692 

2693 

2694@register_model_view(DeviceBayTemplate, 'bulk_delete', path='delete', detail=False) 

2695class DeviceBayTemplateBulkDeleteView(generic.BulkDeleteView): 

2696 queryset = DeviceBayTemplate.objects.all() 

2697 table = tables.DeviceBayTemplateTable 

2698 

2699 

2700# 

2701# Inventory item templates 

2702# 

2703 

2704@register_model_view(InventoryItemTemplate, 'add', detail=False) 

2705class InventoryItemTemplateCreateView(generic.ComponentCreateView): 

2706 queryset = InventoryItemTemplate.objects.all() 

2707 form = forms.InventoryItemTemplateCreateForm 

2708 model_form = forms.InventoryItemTemplateForm 

2709 

2710 def alter_object(self, instance, request): 

2711 # Set component (if any) 

2712 component_type = request.GET.get('component_type') 

2713 component_id = request.GET.get('component_id') 

2714 

2715 if component_type and component_id: 

2716 content_type = get_object_or_404(ContentType, pk=component_type) 

2717 instance.component = get_object_or_404(content_type.model_class(), pk=component_id) 

2718 

2719 return instance 

2720 

2721 

2722@register_model_view(InventoryItemTemplate, 'edit') 

2723class InventoryItemTemplateEditView(generic.ObjectEditView): 

2724 queryset = InventoryItemTemplate.objects.all() 

2725 form = forms.InventoryItemTemplateForm 

2726 

2727 

2728@register_model_view(InventoryItemTemplate, 'delete') 

2729class InventoryItemTemplateDeleteView(generic.ObjectDeleteView): 

2730 queryset = InventoryItemTemplate.objects.all() 

2731 

2732 

2733@register_model_view(InventoryItemTemplate, 'bulk_edit', path='edit', detail=False) 

2734class InventoryItemTemplateBulkEditView(generic.BulkEditView): 

2735 queryset = InventoryItemTemplate.objects.all() 

2736 table = tables.InventoryItemTemplateTable 

2737 form = forms.InventoryItemTemplateBulkEditForm 

2738 

2739 

2740@register_model_view(InventoryItemTemplate, 'bulk_rename', path='rename', detail=False) 

2741class InventoryItemTemplateBulkRenameView(generic.BulkRenameView): 

2742 queryset = InventoryItemTemplate.objects.all() 

2743 rename_fields = ('name', 'label') 

2744 

2745 

2746@register_model_view(InventoryItemTemplate, 'bulk_delete', path='delete', detail=False) 

2747class InventoryItemTemplateBulkDeleteView(generic.BulkDeleteView): 

2748 queryset = InventoryItemTemplate.objects.all() 

2749 table = tables.InventoryItemTemplateTable 

2750 

2751 

2752# 

2753# Device roles 

2754# 

2755 

2756@register_model_view(DeviceRole, 'list', path='', detail=False) 

2757class DeviceRoleListView(generic.ObjectListView): 

2758 queryset = DeviceRole.objects.add_related_count( 

2759 DeviceRole.objects.add_related_count( 

2760 DeviceRole.objects.all(), 

2761 VirtualMachine, 

2762 'role', 

2763 'vm_count', 

2764 cumulative=True 

2765 ), 

2766 Device, 

2767 'role', 

2768 'device_count', 

2769 cumulative=True 

2770 ) 

2771 filterset = filtersets.DeviceRoleFilterSet 

2772 filterset_form = forms.DeviceRoleFilterForm 

2773 table = tables.DeviceRoleTable 

2774 

2775 

2776@register_model_view(DeviceRole) 

2777class DeviceRoleView(GetRelatedModelsMixin, generic.ObjectView): 

2778 queryset = DeviceRole.objects.all() 

2779 layout = layout.SimpleLayout( 

2780 breadcrumbs=[ 

2781 Breadcrumb( 

2782 lambda o: o.get_ancestors(), 

2783 url=filtered_list_url('dcim:devicerole_list', 'parent_id'), 

2784 ), 

2785 ], 

2786 left_panels=[ 

2787 panels.DeviceRolePanel(), 

2788 TagsPanel(), 

2789 ], 

2790 right_panels=[ 

2791 RelatedObjectsPanel(), 

2792 CustomFieldsPanel(), 

2793 CommentsPanel(), 

2794 ], 

2795 bottom_panels=[ 

2796 ObjectsTablePanel( 

2797 model='dcim.DeviceRole', 

2798 title=_('Child Device Roles'), 

2799 filters={'parent_id': lambda ctx: ctx['object'].pk}, 

2800 exclude_columns=['parent'], 

2801 actions=[ 

2802 actions.AddObject('dcim.DeviceRole', url_params={'parent': lambda ctx: ctx['object'].pk}), 

2803 ], 

2804 ), 

2805 ] 

2806 ) 

2807 

2808 def get_extra_context(self, request, instance): 

2809 return { 

2810 'related_models': self.get_related_models(request, instance), 

2811 } 

2812 

2813 

2814@register_model_view(DeviceRole, 'add', detail=False) 

2815@register_model_view(DeviceRole, 'edit') 

2816class DeviceRoleEditView(generic.ObjectEditView): 

2817 queryset = DeviceRole.objects.all() 

2818 form = forms.DeviceRoleForm 

2819 

2820 

2821@register_model_view(DeviceRole, 'delete') 

2822class DeviceRoleDeleteView(generic.ObjectDeleteView): 

2823 queryset = DeviceRole.objects.all() 

2824 

2825 

2826@register_model_view(DeviceRole, 'bulk_import', path='import', detail=False) 

2827class DeviceRoleBulkImportView(generic.BulkImportView): 

2828 queryset = DeviceRole.objects.all() 

2829 model_form = forms.DeviceRoleImportForm 

2830 

2831 

2832@register_model_view(DeviceRole, 'bulk_edit', path='edit', detail=False) 

2833class DeviceRoleBulkEditView(generic.BulkEditView): 

2834 queryset = DeviceRole.objects.annotate( 

2835 device_count=count_related(Device, 'role'), 

2836 vm_count=count_related(VirtualMachine, 'role') 

2837 ) 

2838 filterset = filtersets.DeviceRoleFilterSet 

2839 table = tables.DeviceRoleTable 

2840 form = forms.DeviceRoleBulkEditForm 

2841 

2842 

2843@register_model_view(DeviceRole, 'bulk_rename', path='rename', detail=False) 

2844class DeviceRoleBulkRenameView(generic.BulkRenameView): 

2845 queryset = DeviceRole.objects.all() 

2846 filterset = filtersets.DeviceRoleFilterSet 

2847 

2848 

2849@register_model_view(DeviceRole, 'bulk_delete', path='delete', detail=False) 

2850class DeviceRoleBulkDeleteView(generic.BulkDeleteView): 

2851 queryset = DeviceRole.objects.annotate( 

2852 device_count=count_related(Device, 'role'), 

2853 vm_count=count_related(VirtualMachine, 'role') 

2854 ) 

2855 filterset = filtersets.DeviceRoleFilterSet 

2856 table = tables.DeviceRoleTable 

2857 

2858 

2859# 

2860# Platforms 

2861# 

2862 

2863@register_model_view(Platform, 'list', path='', detail=False) 

2864class PlatformListView(generic.ObjectListView): 

2865 queryset = Platform.objects.add_related_count( 

2866 Platform.objects.add_related_count( 

2867 Platform.objects.all(), 

2868 VirtualMachine, 

2869 'platform', 

2870 'vm_count', 

2871 cumulative=True 

2872 ), 

2873 Device, 

2874 'platform', 

2875 'device_count', 

2876 cumulative=True 

2877 ) 

2878 table = tables.PlatformTable 

2879 filterset = filtersets.PlatformFilterSet 

2880 filterset_form = forms.PlatformFilterForm 

2881 

2882 

2883@register_model_view(Platform) 

2884class PlatformView(GetRelatedModelsMixin, generic.ObjectView): 

2885 queryset = Platform.objects.all() 

2886 layout = layout.SimpleLayout( 

2887 breadcrumbs=[ 

2888 Breadcrumb('manufacturer', url=filtered_list_url('dcim:platform_list', 'manufacturer_id')), 

2889 Breadcrumb( 

2890 lambda o: o.get_ancestors(), 

2891 url=filtered_list_url('dcim:platform_list', 'parent_id'), 

2892 ), 

2893 ], 

2894 left_panels=[ 

2895 panels.PlatformPanel(), 

2896 TagsPanel(), 

2897 ], 

2898 right_panels=[ 

2899 RelatedObjectsPanel(), 

2900 CustomFieldsPanel(), 

2901 CommentsPanel(), 

2902 ], 

2903 bottom_panels=[ 

2904 ObjectsTablePanel( 

2905 model='dcim.Platform', 

2906 title=_('Child Platforms'), 

2907 filters={'parent_id': lambda ctx: ctx['object'].pk}, 

2908 exclude_columns=['parent'], 

2909 actions=[ 

2910 actions.AddObject('dcim.Platform', url_params={'parent': lambda ctx: ctx['object'].pk}), 

2911 ], 

2912 ), 

2913 ] 

2914 ) 

2915 

2916 def get_extra_context(self, request, instance): 

2917 return { 

2918 'related_models': self.get_related_models(request, instance), 

2919 } 

2920 

2921 

2922@register_model_view(Platform, 'add', detail=False) 

2923@register_model_view(Platform, 'edit') 

2924class PlatformEditView(generic.ObjectEditView): 

2925 queryset = Platform.objects.all() 

2926 form = forms.PlatformForm 

2927 

2928 

2929@register_model_view(Platform, 'delete') 

2930class PlatformDeleteView(generic.ObjectDeleteView): 

2931 queryset = Platform.objects.all() 

2932 

2933 

2934@register_model_view(Platform, 'bulk_import', path='import', detail=False) 

2935class PlatformBulkImportView(generic.BulkImportView): 

2936 queryset = Platform.objects.all() 

2937 model_form = forms.PlatformImportForm 

2938 

2939 

2940@register_model_view(Platform, 'bulk_edit', path='edit', detail=False) 

2941class PlatformBulkEditView(generic.BulkEditView): 

2942 queryset = Platform.objects.all() 

2943 filterset = filtersets.PlatformFilterSet 

2944 table = tables.PlatformTable 

2945 form = forms.PlatformBulkEditForm 

2946 

2947 

2948@register_model_view(Platform, 'bulk_rename', path='rename', detail=False) 

2949class PlatformBulkRenameView(generic.BulkRenameView): 

2950 queryset = Platform.objects.all() 

2951 filterset = filtersets.PlatformFilterSet 

2952 

2953 

2954@register_model_view(Platform, 'bulk_delete', path='delete', detail=False) 

2955class PlatformBulkDeleteView(generic.BulkDeleteView): 

2956 queryset = Platform.objects.all() 

2957 filterset = filtersets.PlatformFilterSet 

2958 table = tables.PlatformTable 

2959 

2960 

2961# 

2962# Devices 

2963# 

2964 

2965@register_model_view(Device, 'list', path='', detail=False) 

2966class DeviceListView(generic.ObjectListView): 

2967 queryset = Device.objects.select_related('virtual_chassis') 

2968 filterset = filtersets.DeviceFilterSet 

2969 filterset_form = forms.DeviceFilterForm 

2970 table = tables.DeviceTable 

2971 actions = (AddObject, BulkImport, BulkExport, BulkAddComponents, BulkEdit, BulkRename, BulkDelete) 

2972 

2973 

2974@register_model_view(Device) 

2975class DeviceView(generic.ObjectView): 

2976 queryset = Device.objects.all() 

2977 layout = layout.SimpleLayout( 

2978 left_panels=[ 

2979 panels.DevicePanel(), 

2980 panels.VirtualChassisMembersPanel(), 

2981 CustomFieldsPanel(), 

2982 TagsPanel(), 

2983 CommentsPanel(), 

2984 ObjectsTablePanel( 

2985 model='dcim.VirtualDeviceContext', 

2986 filters={'device_id': lambda ctx: ctx['object'].pk}, 

2987 exclude_columns=['device'], 

2988 actions=[ 

2989 actions.AddObject('dcim.VirtualDeviceContext', url_params={'device': lambda ctx: ctx['object'].pk}), 

2990 ], 

2991 ), 

2992 ], 

2993 right_panels=[ 

2994 panels.DeviceManagementPanel(), 

2995 panels.PowerUtilizationPanel(), 

2996 ObjectsTablePanel( 

2997 model='ipam.Service', 

2998 title=_('Application Services'), 

2999 filters={'device_id': lambda ctx: ctx['object'].pk}, 

3000 exclude_columns=['parent'], 

3001 actions=[ 

3002 actions.AddObject( 

3003 'ipam.Service', 

3004 url_params={ 

3005 'parent_content_type': lambda ctx: ContentType.objects.get_for_model(ctx['object']).pk, 

3006 'parent_object_id': lambda ctx: ctx['object'].pk 

3007 } 

3008 ), 

3009 ], 

3010 ), 

3011 ImageAttachmentsPanel(), 

3012 panels.DeviceDeviceTypePanel(), 

3013 panels.DeviceDimensionsPanel(), 

3014 TemplatePanel('dcim/panels/device_rack_elevations.html'), 

3015 ], 

3016 ) 

3017 

3018 def get_extra_context(self, request, instance): 

3019 # VirtualChassis members 

3020 if instance.virtual_chassis is not None: 

3021 vc_members = Device.objects.restrict(request.user, 'view').filter( 

3022 virtual_chassis=instance.virtual_chassis 

3023 ).order_by('vc_position') 

3024 else: 

3025 vc_members = [] 

3026 

3027 return { 

3028 'virtual_chassis': instance.virtual_chassis, 

3029 'vc_members': vc_members, 

3030 'svg_extra': f'highlight=id:{instance.pk}', 

3031 } 

3032 

3033 

3034@register_model_view(Device, 'add', detail=False) 

3035@register_model_view(Device, 'edit') 

3036class DeviceEditView(generic.ObjectEditView): 

3037 queryset = Device.objects.all() 

3038 form = forms.DeviceForm 

3039 template_name = 'dcim/device_edit.html' 

3040 

3041 

3042@register_model_view(Device, 'delete') 

3043class DeviceDeleteView(generic.ObjectDeleteView): 

3044 queryset = Device.objects.all() 

3045 

3046 

3047@register_model_view(Device, 'consoleports', path='console-ports') 

3048class DeviceConsolePortsView(DeviceComponentsView): 

3049 child_model = ConsolePort 

3050 table = tables.DeviceConsolePortTable 

3051 filterset = filtersets.ConsolePortFilterSet 

3052 filterset_form = forms.ConsolePortFilterForm 

3053 actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete) 

3054 tab = ViewTab( 

3055 label=_('Console Ports'), 

3056 badge=lambda obj: obj.console_port_count, 

3057 permission='dcim.view_consoleport', 

3058 weight=550, 

3059 hide_if_empty=True 

3060 ) 

3061 

3062 

3063@register_model_view(Device, 'consoleserverports', path='console-server-ports') 

3064class DeviceConsoleServerPortsView(DeviceComponentsView): 

3065 child_model = ConsoleServerPort 

3066 table = tables.DeviceConsoleServerPortTable 

3067 filterset = filtersets.ConsoleServerPortFilterSet 

3068 filterset_form = forms.ConsoleServerPortFilterForm 

3069 actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete) 

3070 tab = ViewTab( 

3071 label=_('Console Server Ports'), 

3072 badge=lambda obj: obj.console_server_port_count, 

3073 permission='dcim.view_consoleserverport', 

3074 weight=560, 

3075 hide_if_empty=True 

3076 ) 

3077 

3078 

3079@register_model_view(Device, 'powerports', path='power-ports') 

3080class DevicePowerPortsView(DeviceComponentsView): 

3081 child_model = PowerPort 

3082 table = tables.DevicePowerPortTable 

3083 filterset = filtersets.PowerPortFilterSet 

3084 filterset_form = forms.PowerPortFilterForm 

3085 actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete) 

3086 tab = ViewTab( 

3087 label=_('Power Ports'), 

3088 badge=lambda obj: obj.power_port_count, 

3089 permission='dcim.view_powerport', 

3090 weight=570, 

3091 hide_if_empty=True 

3092 ) 

3093 

3094 

3095@register_model_view(Device, 'poweroutlets', path='power-outlets') 

3096class DevicePowerOutletsView(DeviceComponentsView): 

3097 child_model = PowerOutlet 

3098 table = tables.DevicePowerOutletTable 

3099 filterset = filtersets.PowerOutletFilterSet 

3100 filterset_form = forms.PowerOutletFilterForm 

3101 actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete) 

3102 tab = ViewTab( 

3103 label=_('Power Outlets'), 

3104 badge=lambda obj: obj.power_outlet_count, 

3105 permission='dcim.view_poweroutlet', 

3106 weight=580, 

3107 hide_if_empty=True 

3108 ) 

3109 

3110 

3111@register_model_view(Device, 'coolingintakes', path='cooling-intakes') 

3112class DeviceCoolingIntakesView(DeviceComponentsView): 

3113 child_model = CoolingIntake 

3114 table = tables.DeviceCoolingIntakeTable 

3115 filterset = filtersets.CoolingIntakeFilterSet 

3116 filterset_form = forms.CoolingIntakeFilterForm 

3117 actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete) 

3118 tab = ViewTab( 

3119 label=_('Cooling Intakes'), 

3120 badge=lambda obj: obj.cooling_intake_count, 

3121 permission='dcim.view_coolingintake', 

3122 weight=590, 

3123 hide_if_empty=True 

3124 ) 

3125 

3126 

3127@register_model_view(Device, 'coolingoutflows', path='cooling-outflows') 

3128class DeviceCoolingOutflowsView(DeviceComponentsView): 

3129 child_model = CoolingOutflow 

3130 table = tables.DeviceCoolingOutflowTable 

3131 filterset = filtersets.CoolingOutflowFilterSet 

3132 filterset_form = forms.CoolingOutflowFilterForm 

3133 actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete) 

3134 tab = ViewTab( 

3135 label=_('Cooling Outflows'), 

3136 badge=lambda obj: obj.cooling_outflow_count, 

3137 permission='dcim.view_coolingoutflow', 

3138 weight=600, 

3139 hide_if_empty=True 

3140 ) 

3141 

3142 

3143@register_model_view(Device, 'interfaces') 

3144class DeviceInterfacesView(DeviceComponentsView): 

3145 child_model = Interface 

3146 table = tables.DeviceInterfaceTable 

3147 filterset = filtersets.InterfaceFilterSet 

3148 filterset_form = forms.InterfaceFilterForm 

3149 actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete) 

3150 template_name = 'dcim/device/interfaces.html' 

3151 tab = ViewTab( 

3152 label=_('Interfaces'), 

3153 badge=lambda obj: obj.vc_interfaces().count(), 

3154 permission='dcim.view_interface', 

3155 weight=520, 

3156 hide_if_empty=True 

3157 ) 

3158 

3159 def get_children(self, request, parent): 

3160 return parent.vc_interfaces().restrict(request.user, 'view').prefetch_related( 

3161 Prefetch('ip_addresses', queryset=IPAddress.objects.restrict(request.user)), 

3162 Prefetch('member_interfaces', queryset=Interface.objects.restrict(request.user)) 

3163 ) 

3164 

3165 

3166@register_model_view(Device, 'frontports', path='front-ports') 

3167class DeviceFrontPortsView(DeviceComponentsView): 

3168 child_model = FrontPort 

3169 table = tables.DeviceFrontPortTable 

3170 filterset = filtersets.FrontPortFilterSet 

3171 filterset_form = forms.FrontPortFilterForm 

3172 actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete) 

3173 tab = ViewTab( 

3174 label=_('Front Ports'), 

3175 badge=lambda obj: obj.front_port_count, 

3176 permission='dcim.view_frontport', 

3177 weight=530, 

3178 hide_if_empty=True 

3179 ) 

3180 

3181 

3182@register_model_view(Device, 'rearports', path='rear-ports') 

3183class DeviceRearPortsView(DeviceComponentsView): 

3184 child_model = RearPort 

3185 table = tables.DeviceRearPortTable 

3186 filterset = filtersets.RearPortFilterSet 

3187 filterset_form = forms.RearPortFilterForm 

3188 actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDisconnect, BulkDelete) 

3189 tab = ViewTab( 

3190 label=_('Rear Ports'), 

3191 badge=lambda obj: obj.rear_port_count, 

3192 permission='dcim.view_rearport', 

3193 weight=540, 

3194 hide_if_empty=True 

3195 ) 

3196 

3197 

3198@register_model_view(Device, 'modulebays', path='module-bays') 

3199class DeviceModuleBaysView(DeviceComponentsView): 

3200 child_model = ModuleBay 

3201 table = tables.DeviceModuleBayTable 

3202 filterset = filtersets.ModuleBayFilterSet 

3203 filterset_form = forms.ModuleBayFilterForm 

3204 actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete) 

3205 tab = ViewTab( 

3206 label=_('Module Bays'), 

3207 badge=lambda obj: obj.module_bay_count, 

3208 permission='dcim.view_modulebay', 

3209 weight=510, 

3210 hide_if_empty=True 

3211 ) 

3212 

3213 

3214@register_model_view(Device, 'devicebays', path='device-bays') 

3215class DeviceDeviceBaysView(DeviceComponentsView): 

3216 child_model = DeviceBay 

3217 table = tables.DeviceDeviceBayTable 

3218 filterset = filtersets.DeviceBayFilterSet 

3219 filterset_form = forms.DeviceBayFilterForm 

3220 actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete) 

3221 tab = ViewTab( 

3222 label=_('Device Bays'), 

3223 badge=lambda obj: obj.device_bay_count, 

3224 permission='dcim.view_devicebay', 

3225 weight=500, 

3226 hide_if_empty=True 

3227 ) 

3228 

3229 

3230@register_model_view(Device, 'inventory') 

3231class DeviceInventoryView(DeviceComponentsView): 

3232 child_model = InventoryItem 

3233 table = tables.DeviceInventoryItemTable 

3234 filterset = filtersets.InventoryItemFilterSet 

3235 filterset_form = forms.InventoryItemFilterForm 

3236 actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete) 

3237 tab = ViewTab( 

3238 label=_('Inventory Items'), 

3239 badge=lambda obj: obj.inventory_item_count, 

3240 permission='dcim.view_inventoryitem', 

3241 weight=610, 

3242 hide_if_empty=True 

3243 ) 

3244 

3245 def get_children(self, request, parent): 

3246 # DeviceInventoryItemTable indents rows by record.level; under MPTT, 

3247 # TreeManager forced tree-flatten (tree_id, lft) ordering so descendants 

3248 # were contiguous with their parent. InventoryItem.Meta.ordering is a 

3249 # flat sort suited to the global list; for the indented device tab, 

3250 # order by ltree path so the rendered hierarchy is correct. 

3251 return super().get_children(request, parent).order_by('path') 

3252 

3253 

3254@register_model_view(Device, 'configcontext', path='config-context') 

3255class DeviceConfigContextView(ObjectConfigContextView): 

3256 queryset = Device.objects.all() 

3257 base_template = 'dcim/device/base.html' 

3258 tab = ViewTab( 

3259 label=_('Config Context'), 

3260 weight=2000 

3261 ) 

3262 

3263 

3264@register_model_view(Device, 'render-config') 

3265class DeviceRenderConfigView(ObjectRenderConfigView): 

3266 queryset = Device.objects.all() 

3267 base_template = 'dcim/device/base.html' 

3268 additional_permissions = ['dcim.render_config_device'] 

3269 tab = ViewTab( 

3270 label=_('Render Config'), 

3271 weight=2100, 

3272 ) 

3273 

3274 

3275@register_model_view(Device, 'virtual-machines') 

3276class DeviceVirtualMachinesView(generic.ObjectChildrenView): 

3277 queryset = Device.objects.all() 

3278 child_model = VirtualMachine 

3279 table = VirtualMachineTable 

3280 filterset = VirtualMachineFilterSet 

3281 filterset_form = VirtualMachineFilterForm 

3282 tab = ViewTab( 

3283 label=_('Virtual Machines'), 

3284 badge=lambda obj: VirtualMachine.objects.filter(cluster=obj.cluster, device=obj).count(), 

3285 weight=2200, 

3286 hide_if_empty=True, 

3287 permission='virtualization.view_virtualmachine' 

3288 ) 

3289 

3290 def get_children(self, request, parent): 

3291 return self.child_model.objects.restrict(request.user, 'view').filter(cluster=parent.cluster, device=parent) 

3292 

3293 

3294@register_model_view(Device, 'bulk_import', path='import', detail=False) 

3295class DeviceBulkImportView(generic.BulkImportView): 

3296 queryset = Device.objects.all() 

3297 model_form = forms.DeviceImportForm 

3298 

3299 def save_object(self, object_form, request): 

3300 parent_bay = getattr(object_form.instance, 'parent_bay', None) 

3301 obj = object_form.save() 

3302 

3303 # For child devices, save the reverse relation to the parent device bay 

3304 if parent_bay: 

3305 device_bay = parent_bay 

3306 device_bay.snapshot() 

3307 device_bay.installed_device = obj 

3308 device_bay.save() 

3309 

3310 return obj 

3311 

3312 

3313@register_model_view(Device, 'bulk_edit', path='edit', detail=False) 

3314class DeviceBulkEditView(generic.BulkEditView): 

3315 queryset = Device.objects.prefetch_related('device_type__manufacturer') 

3316 filterset = filtersets.DeviceFilterSet 

3317 table = tables.DeviceTable 

3318 form = forms.DeviceBulkEditForm 

3319 

3320 

3321@register_model_view(Device, 'bulk_rename', path='rename', detail=False) 

3322class DeviceBulkRenameView(generic.BulkRenameView): 

3323 queryset = Device.objects.all() 

3324 filterset = filtersets.DeviceFilterSet 

3325 table = tables.DeviceTable 

3326 

3327 

3328@register_model_view(Device, 'bulk_delete', path='delete', detail=False) 

3329class DeviceBulkDeleteView(generic.BulkDeleteView): 

3330 queryset = Device.objects.prefetch_related('device_type__manufacturer') 

3331 filterset = filtersets.DeviceFilterSet 

3332 table = tables.DeviceTable 

3333 

3334 

3335# 

3336# Modules 

3337# 

3338 

3339@register_model_view(Module, 'list', path='', detail=False) 

3340class ModuleListView(generic.ObjectListView): 

3341 queryset = Module.objects.prefetch_related('module_type__manufacturer') 

3342 filterset = filtersets.ModuleFilterSet 

3343 filterset_form = forms.ModuleFilterForm 

3344 table = tables.ModuleTable 

3345 actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete) 

3346 

3347 

3348@register_model_view(Module) 

3349class ModuleView(GetRelatedModelsMixin, generic.ObjectView): 

3350 queryset = Module.objects.prefetch_related( 

3351 'module_bay__module_bay_types', 

3352 'module_type__module_bay_types', 

3353 ) 

3354 layout = layout.SimpleLayout( 

3355 breadcrumbs=[ 

3356 Breadcrumb('module_type', url=filtered_list_url('dcim:module_list', 'module_type_id')), 

3357 ], 

3358 left_panels=[ 

3359 panels.BayTypeIncompatibilityPanel(), 

3360 panels.ModulePanel(), 

3361 TagsPanel(), 

3362 CommentsPanel(), 

3363 ], 

3364 right_panels=[ 

3365 TemplatePanel( 

3366 title=_('Module Type'), 

3367 template_name='dcim/panels/module_type.html', 

3368 ), 

3369 RelatedObjectsPanel(), 

3370 CustomFieldsPanel(), 

3371 ], 

3372 ) 

3373 

3374 def get_extra_context(self, request, instance): 

3375 return { 

3376 'related_models': self.get_related_models(request, instance), 

3377 } 

3378 

3379 

3380@register_model_view(Module, 'add', detail=False) 

3381@register_model_view(Module, 'edit') 

3382class ModuleEditView(generic.ObjectEditView): 

3383 queryset = Module.objects.all() 

3384 form = forms.ModuleForm 

3385 

3386 

3387@register_model_view(Module, 'delete') 

3388class ModuleDeleteView(generic.ObjectDeleteView): 

3389 queryset = Module.objects.all() 

3390 

3391 

3392@register_model_view(Module, 'bulk_import', path='import', detail=False) 

3393class ModuleBulkImportView(generic.BulkImportView): 

3394 queryset = Module.objects.all() 

3395 model_form = forms.ModuleImportForm 

3396 

3397 

3398@register_model_view(Module, 'bulk_edit', path='edit', detail=False) 

3399class ModuleBulkEditView(generic.BulkEditView): 

3400 queryset = Module.objects.prefetch_related('module_type__manufacturer') 

3401 filterset = filtersets.ModuleFilterSet 

3402 table = tables.ModuleTable 

3403 form = forms.ModuleBulkEditForm 

3404 

3405 

3406@register_model_view(Module, 'bulk_delete', path='delete', detail=False) 

3407class ModuleBulkDeleteView(generic.BulkDeleteView): 

3408 queryset = Module.objects.prefetch_related('module_type__manufacturer') 

3409 filterset = filtersets.ModuleFilterSet 

3410 table = tables.ModuleTable 

3411 

3412 

3413# 

3414# Console ports 

3415# 

3416 

3417@register_model_view(ConsolePort, 'list', path='', detail=False) 

3418class ConsolePortListView(generic.ObjectListView): 

3419 queryset = ConsolePort.objects.all() 

3420 filterset = filtersets.ConsolePortFilterSet 

3421 filterset_form = forms.ConsolePortFilterForm 

3422 table = tables.ConsolePortTable 

3423 

3424 

3425@register_model_view(ConsolePort) 

3426class ConsolePortView(generic.ObjectView): 

3427 template_name = 'generic/object.html' 

3428 queryset = ConsolePort.objects.all() 

3429 layout = layout.SimpleLayout( 

3430 breadcrumbs=[ 

3431 Breadcrumb('device', url=object_view_url('dcim:device_consoleports')), 

3432 ], 

3433 left_panels=[ 

3434 panels.ConsolePortPanel(), 

3435 CustomFieldsPanel(), 

3436 TagsPanel(), 

3437 ], 

3438 right_panels=[ 

3439 panels.ConnectionPanel( 

3440 trace_url_name='dcim:consoleport_trace', 

3441 connect_options=[ 

3442 { 

3443 'a_type': 'dcim.consoleport', 

3444 'b_type': 'dcim.consoleserverport', 

3445 'label': _('Console Server Port'), 

3446 }, 

3447 {'a_type': 'dcim.consoleport', 'b_type': 'dcim.frontport', 'label': _('Front Port')}, 

3448 {'a_type': 'dcim.consoleport', 'b_type': 'dcim.rearport', 'label': _('Rear Port')}, 

3449 ], 

3450 ), 

3451 panels.InventoryItemsPanel(), 

3452 ], 

3453 ) 

3454 

3455 

3456@register_model_view(ConsolePort, 'add', detail=False) 

3457class ConsolePortCreateView(generic.ComponentCreateView): 

3458 queryset = ConsolePort.objects.all() 

3459 form = forms.ConsolePortCreateForm 

3460 model_form = forms.ConsolePortForm 

3461 

3462 

3463@register_model_view(ConsolePort, 'edit') 

3464class ConsolePortEditView(generic.ObjectEditView): 

3465 queryset = ConsolePort.objects.all() 

3466 form = forms.ConsolePortForm 

3467 

3468 

3469@register_model_view(ConsolePort, 'delete') 

3470class ConsolePortDeleteView(generic.ObjectDeleteView): 

3471 queryset = ConsolePort.objects.all() 

3472 

3473 

3474@register_model_view(ConsolePort, 'bulk_import', path='import', detail=False) 

3475class ConsolePortBulkImportView(generic.BulkImportView): 

3476 queryset = ConsolePort.objects.all() 

3477 model_form = forms.ConsolePortImportForm 

3478 

3479 

3480@register_model_view(ConsolePort, 'bulk_edit', path='edit', detail=False) 

3481class ConsolePortBulkEditView(generic.BulkEditView): 

3482 queryset = ConsolePort.objects.all() 

3483 filterset = filtersets.ConsolePortFilterSet 

3484 table = tables.ConsolePortTable 

3485 form = forms.ConsolePortBulkEditForm 

3486 

3487 

3488@register_model_view(ConsolePort, 'bulk_rename', path='rename', detail=False) 

3489class ConsolePortBulkRenameView(generic.BulkRenameView): 

3490 queryset = ConsolePort.objects.all() 

3491 filterset = filtersets.ConsolePortFilterSet 

3492 rename_fields = ('name', 'label') 

3493 

3494 

3495@register_model_view(ConsolePort, 'bulk_disconnect', path='disconnect', detail=False) 

3496class ConsolePortBulkDisconnectView(BulkDisconnectView): 

3497 queryset = ConsolePort.objects.all() 

3498 

3499 

3500@register_model_view(ConsolePort, 'bulk_delete', path='delete', detail=False) 

3501class ConsolePortBulkDeleteView(generic.BulkDeleteView): 

3502 queryset = ConsolePort.objects.all() 

3503 filterset = filtersets.ConsolePortFilterSet 

3504 table = tables.ConsolePortTable 

3505 

3506 

3507# Trace view 

3508register_model_view(ConsolePort, 'trace', kwargs={'model': ConsolePort})(PathTraceView) 

3509 

3510 

3511# 

3512# Console server ports 

3513# 

3514 

3515@register_model_view(ConsoleServerPort, 'list', path='', detail=False) 

3516class ConsoleServerPortListView(generic.ObjectListView): 

3517 queryset = ConsoleServerPort.objects.all() 

3518 filterset = filtersets.ConsoleServerPortFilterSet 

3519 filterset_form = forms.ConsoleServerPortFilterForm 

3520 table = tables.ConsoleServerPortTable 

3521 

3522 

3523@register_model_view(ConsoleServerPort) 

3524class ConsoleServerPortView(generic.ObjectView): 

3525 template_name = 'generic/object.html' 

3526 queryset = ConsoleServerPort.objects.all() 

3527 layout = layout.SimpleLayout( 

3528 breadcrumbs=[ 

3529 Breadcrumb('device', url=object_view_url('dcim:device_consoleserverports')), 

3530 ], 

3531 left_panels=[ 

3532 panels.ConsoleServerPortPanel(), 

3533 CustomFieldsPanel(), 

3534 TagsPanel(), 

3535 ], 

3536 right_panels=[ 

3537 panels.ConnectionPanel( 

3538 trace_url_name='dcim:consoleserverport_trace', 

3539 connect_options=[ 

3540 {'a_type': 'dcim.consoleserverport', 'b_type': 'dcim.consoleport', 'label': _('Console Port')}, 

3541 {'a_type': 'dcim.consoleserverport', 'b_type': 'dcim.frontport', 'label': _('Front Port')}, 

3542 {'a_type': 'dcim.consoleserverport', 'b_type': 'dcim.rearport', 'label': _('Rear Port')}, 

3543 ], 

3544 ), 

3545 panels.InventoryItemsPanel(), 

3546 ], 

3547 ) 

3548 

3549 

3550@register_model_view(ConsoleServerPort, 'add', detail=False) 

3551class ConsoleServerPortCreateView(generic.ComponentCreateView): 

3552 queryset = ConsoleServerPort.objects.all() 

3553 form = forms.ConsoleServerPortCreateForm 

3554 model_form = forms.ConsoleServerPortForm 

3555 

3556 

3557@register_model_view(ConsoleServerPort, 'edit') 

3558class ConsoleServerPortEditView(generic.ObjectEditView): 

3559 queryset = ConsoleServerPort.objects.all() 

3560 form = forms.ConsoleServerPortForm 

3561 

3562 

3563@register_model_view(ConsoleServerPort, 'delete') 

3564class ConsoleServerPortDeleteView(generic.ObjectDeleteView): 

3565 queryset = ConsoleServerPort.objects.all() 

3566 

3567 

3568@register_model_view(ConsoleServerPort, 'bulk_import', path='import', detail=False) 

3569class ConsoleServerPortBulkImportView(generic.BulkImportView): 

3570 queryset = ConsoleServerPort.objects.all() 

3571 model_form = forms.ConsoleServerPortImportForm 

3572 

3573 

3574@register_model_view(ConsoleServerPort, 'bulk_edit', path='edit', detail=False) 

3575class ConsoleServerPortBulkEditView(generic.BulkEditView): 

3576 queryset = ConsoleServerPort.objects.all() 

3577 filterset = filtersets.ConsoleServerPortFilterSet 

3578 table = tables.ConsoleServerPortTable 

3579 form = forms.ConsoleServerPortBulkEditForm 

3580 

3581 

3582@register_model_view(ConsoleServerPort, 'bulk_rename', path='rename', detail=False) 

3583class ConsoleServerPortBulkRenameView(generic.BulkRenameView): 

3584 queryset = ConsoleServerPort.objects.all() 

3585 filterset = filtersets.ConsoleServerPortFilterSet 

3586 rename_fields = ('name', 'label') 

3587 

3588 

3589@register_model_view(ConsoleServerPort, 'bulk_disconnect', path='disconnect', detail=False) 

3590class ConsoleServerPortBulkDisconnectView(BulkDisconnectView): 

3591 queryset = ConsoleServerPort.objects.all() 

3592 

3593 

3594@register_model_view(ConsoleServerPort, 'bulk_delete', path='delete', detail=False) 

3595class ConsoleServerPortBulkDeleteView(generic.BulkDeleteView): 

3596 queryset = ConsoleServerPort.objects.all() 

3597 filterset = filtersets.ConsoleServerPortFilterSet 

3598 table = tables.ConsoleServerPortTable 

3599 

3600 

3601# Trace view 

3602register_model_view(ConsoleServerPort, 'trace', kwargs={'model': ConsoleServerPort})(PathTraceView) 

3603 

3604 

3605# 

3606# Power ports 

3607# 

3608 

3609@register_model_view(PowerPort, 'list', path='', detail=False) 

3610class PowerPortListView(generic.ObjectListView): 

3611 queryset = PowerPort.objects.all() 

3612 filterset = filtersets.PowerPortFilterSet 

3613 filterset_form = forms.PowerPortFilterForm 

3614 table = tables.PowerPortTable 

3615 

3616 

3617@register_model_view(PowerPort) 

3618class PowerPortView(generic.ObjectView): 

3619 template_name = 'generic/object.html' 

3620 queryset = PowerPort.objects.all() 

3621 layout = layout.SimpleLayout( 

3622 breadcrumbs=[ 

3623 Breadcrumb('device', url=object_view_url('dcim:device_powerports')), 

3624 ], 

3625 left_panels=[ 

3626 panels.PowerPortPanel(), 

3627 CustomFieldsPanel(), 

3628 TagsPanel(), 

3629 ], 

3630 right_panels=[ 

3631 panels.ConnectionPanel( 

3632 trace_url_name='dcim:powerport_trace', 

3633 connect_options=[ 

3634 {'a_type': 'dcim.powerport', 'b_type': 'dcim.poweroutlet', 'label': _('Power Outlet')}, 

3635 {'a_type': 'dcim.powerport', 'b_type': 'dcim.powerfeed', 'label': _('Power Feed')}, 

3636 ], 

3637 ), 

3638 panels.InventoryItemsPanel(), 

3639 ], 

3640 ) 

3641 

3642 

3643@register_model_view(PowerPort, 'add', detail=False) 

3644class PowerPortCreateView(generic.ComponentCreateView): 

3645 queryset = PowerPort.objects.all() 

3646 form = forms.PowerPortCreateForm 

3647 model_form = forms.PowerPortForm 

3648 

3649 

3650@register_model_view(PowerPort, 'edit') 

3651class PowerPortEditView(generic.ObjectEditView): 

3652 queryset = PowerPort.objects.all() 

3653 form = forms.PowerPortForm 

3654 

3655 

3656@register_model_view(PowerPort, 'delete') 

3657class PowerPortDeleteView(generic.ObjectDeleteView): 

3658 queryset = PowerPort.objects.all() 

3659 

3660 

3661@register_model_view(PowerPort, 'bulk_import', path='import', detail=False) 

3662class PowerPortBulkImportView(generic.BulkImportView): 

3663 queryset = PowerPort.objects.all() 

3664 model_form = forms.PowerPortImportForm 

3665 

3666 

3667@register_model_view(PowerPort, 'bulk_edit', path='edit', detail=False) 

3668class PowerPortBulkEditView(generic.BulkEditView): 

3669 queryset = PowerPort.objects.all() 

3670 filterset = filtersets.PowerPortFilterSet 

3671 table = tables.PowerPortTable 

3672 form = forms.PowerPortBulkEditForm 

3673 

3674 

3675@register_model_view(PowerPort, 'bulk_rename', path='rename', detail=False) 

3676class PowerPortBulkRenameView(generic.BulkRenameView): 

3677 queryset = PowerPort.objects.all() 

3678 filterset = filtersets.PowerPortFilterSet 

3679 rename_fields = ('name', 'label') 

3680 

3681 

3682@register_model_view(PowerPort, 'bulk_disconnect', path='disconnect', detail=False) 

3683class PowerPortBulkDisconnectView(BulkDisconnectView): 

3684 queryset = PowerPort.objects.all() 

3685 

3686 

3687@register_model_view(PowerPort, 'bulk_delete', path='delete', detail=False) 

3688class PowerPortBulkDeleteView(generic.BulkDeleteView): 

3689 queryset = PowerPort.objects.all() 

3690 filterset = filtersets.PowerPortFilterSet 

3691 table = tables.PowerPortTable 

3692 

3693 

3694# Trace view 

3695register_model_view(PowerPort, 'trace', kwargs={'model': PowerPort})(PathTraceView) 

3696 

3697 

3698# 

3699# Power outlets 

3700# 

3701 

3702@register_model_view(PowerOutlet, 'list', path='', detail=False) 

3703class PowerOutletListView(generic.ObjectListView): 

3704 queryset = PowerOutlet.objects.all() 

3705 filterset = filtersets.PowerOutletFilterSet 

3706 filterset_form = forms.PowerOutletFilterForm 

3707 table = tables.PowerOutletTable 

3708 

3709 

3710@register_model_view(PowerOutlet) 

3711class PowerOutletView(generic.ObjectView): 

3712 template_name = 'generic/object.html' 

3713 queryset = PowerOutlet.objects.all() 

3714 layout = layout.SimpleLayout( 

3715 breadcrumbs=[ 

3716 Breadcrumb('device', url=object_view_url('dcim:device_poweroutlets')), 

3717 ], 

3718 left_panels=[ 

3719 panels.PowerOutletPanel(), 

3720 CustomFieldsPanel(), 

3721 TagsPanel(), 

3722 ], 

3723 right_panels=[ 

3724 panels.ConnectionPanel( 

3725 trace_url_name='dcim:poweroutlet_trace', 

3726 connect_options=[ 

3727 {'a_type': 'dcim.poweroutlet', 'b_type': 'dcim.powerport', 'label': _('Power Port')}, 

3728 ], 

3729 ), 

3730 panels.InventoryItemsPanel(), 

3731 ], 

3732 ) 

3733 

3734 

3735@register_model_view(PowerOutlet, 'add', detail=False) 

3736class PowerOutletCreateView(generic.ComponentCreateView): 

3737 queryset = PowerOutlet.objects.all() 

3738 form = forms.PowerOutletCreateForm 

3739 model_form = forms.PowerOutletForm 

3740 

3741 

3742@register_model_view(PowerOutlet, 'edit') 

3743class PowerOutletEditView(generic.ObjectEditView): 

3744 queryset = PowerOutlet.objects.all() 

3745 form = forms.PowerOutletForm 

3746 

3747 

3748@register_model_view(PowerOutlet, 'delete') 

3749class PowerOutletDeleteView(generic.ObjectDeleteView): 

3750 queryset = PowerOutlet.objects.all() 

3751 

3752 

3753@register_model_view(PowerOutlet, 'bulk_import', path='import', detail=False) 

3754class PowerOutletBulkImportView(generic.BulkImportView): 

3755 queryset = PowerOutlet.objects.all() 

3756 model_form = forms.PowerOutletImportForm 

3757 

3758 

3759@register_model_view(PowerOutlet, 'bulk_edit', path='edit', detail=False) 

3760class PowerOutletBulkEditView(generic.BulkEditView): 

3761 queryset = PowerOutlet.objects.all() 

3762 filterset = filtersets.PowerOutletFilterSet 

3763 table = tables.PowerOutletTable 

3764 form = forms.PowerOutletBulkEditForm 

3765 

3766 

3767@register_model_view(PowerOutlet, 'bulk_rename', path='rename', detail=False) 

3768class PowerOutletBulkRenameView(generic.BulkRenameView): 

3769 queryset = PowerOutlet.objects.all() 

3770 filterset = filtersets.PowerOutletFilterSet 

3771 rename_fields = ('name', 'label') 

3772 

3773 

3774@register_model_view(PowerOutlet, 'bulk_disconnect', path='disconnect', detail=False) 

3775class PowerOutletBulkDisconnectView(BulkDisconnectView): 

3776 queryset = PowerOutlet.objects.all() 

3777 

3778 

3779@register_model_view(PowerOutlet, 'bulk_delete', path='delete', detail=False) 

3780class PowerOutletBulkDeleteView(generic.BulkDeleteView): 

3781 queryset = PowerOutlet.objects.all() 

3782 filterset = filtersets.PowerOutletFilterSet 

3783 table = tables.PowerOutletTable 

3784 

3785 

3786# Trace view 

3787register_model_view(PowerOutlet, 'trace', kwargs={'model': PowerOutlet})(PathTraceView) 

3788 

3789 

3790# 

3791# Cooling ports 

3792# 

3793 

3794@register_model_view(CoolingIntake, 'list', path='', detail=False) 

3795class CoolingIntakeListView(generic.ObjectListView): 

3796 queryset = CoolingIntake.objects.all() 

3797 filterset = filtersets.CoolingIntakeFilterSet 

3798 filterset_form = forms.CoolingIntakeFilterForm 

3799 table = tables.CoolingIntakeTable 

3800 

3801 

3802@register_model_view(CoolingIntake) 

3803class CoolingIntakeView(generic.ObjectView): 

3804 queryset = CoolingIntake.objects.all() 

3805 template_name = 'generic/object.html' 

3806 layout = layout.SimpleLayout( 

3807 breadcrumbs=[ 

3808 Breadcrumb('device', url=object_view_url('dcim:device_coolingintakes')), 

3809 ], 

3810 left_panels=[ 

3811 panels.CoolingIntakePanel(), 

3812 CustomFieldsPanel(), 

3813 TagsPanel(), 

3814 ], 

3815 right_panels=[ 

3816 panels.InventoryItemsPanel(), 

3817 ], 

3818 ) 

3819 

3820 

3821@register_model_view(CoolingIntake, 'add', detail=False) 

3822class CoolingIntakeCreateView(generic.ComponentCreateView): 

3823 queryset = CoolingIntake.objects.all() 

3824 form = forms.CoolingIntakeCreateForm 

3825 model_form = forms.CoolingIntakeForm 

3826 

3827 

3828@register_model_view(CoolingIntake, 'edit') 

3829class CoolingIntakeEditView(generic.ObjectEditView): 

3830 queryset = CoolingIntake.objects.all() 

3831 form = forms.CoolingIntakeForm 

3832 

3833 

3834@register_model_view(CoolingIntake, 'delete') 

3835class CoolingIntakeDeleteView(generic.ObjectDeleteView): 

3836 queryset = CoolingIntake.objects.all() 

3837 

3838 

3839@register_model_view(CoolingIntake, 'bulk_import', path='import', detail=False) 

3840class CoolingIntakeBulkImportView(generic.BulkImportView): 

3841 queryset = CoolingIntake.objects.all() 

3842 model_form = forms.CoolingIntakeImportForm 

3843 

3844 

3845@register_model_view(CoolingIntake, 'bulk_edit', path='edit', detail=False) 

3846class CoolingIntakeBulkEditView(generic.BulkEditView): 

3847 queryset = CoolingIntake.objects.all() 

3848 filterset = filtersets.CoolingIntakeFilterSet 

3849 table = tables.CoolingIntakeTable 

3850 form = forms.CoolingIntakeBulkEditForm 

3851 

3852 

3853@register_model_view(CoolingIntake, 'bulk_rename', path='rename', detail=False) 

3854class CoolingIntakeBulkRenameView(generic.BulkRenameView): 

3855 queryset = CoolingIntake.objects.all() 

3856 filterset = filtersets.CoolingIntakeFilterSet 

3857 rename_fields = ('name', 'label') 

3858 

3859 

3860@register_model_view(CoolingIntake, 'bulk_delete', path='delete', detail=False) 

3861class CoolingIntakeBulkDeleteView(generic.BulkDeleteView): 

3862 queryset = CoolingIntake.objects.all() 

3863 filterset = filtersets.CoolingIntakeFilterSet 

3864 table = tables.CoolingIntakeTable 

3865 

3866 

3867# 

3868# Cooling outlets 

3869# 

3870 

3871@register_model_view(CoolingOutflow, 'list', path='', detail=False) 

3872class CoolingOutflowListView(generic.ObjectListView): 

3873 queryset = CoolingOutflow.objects.all() 

3874 filterset = filtersets.CoolingOutflowFilterSet 

3875 filterset_form = forms.CoolingOutflowFilterForm 

3876 table = tables.CoolingOutflowTable 

3877 

3878 

3879@register_model_view(CoolingOutflow) 

3880class CoolingOutflowView(generic.ObjectView): 

3881 queryset = CoolingOutflow.objects.all() 

3882 template_name = 'generic/object.html' 

3883 layout = layout.SimpleLayout( 

3884 breadcrumbs=[ 

3885 Breadcrumb('device', url=object_view_url('dcim:device_coolingoutflows')), 

3886 ], 

3887 left_panels=[ 

3888 panels.CoolingOutflowPanel(), 

3889 CustomFieldsPanel(), 

3890 TagsPanel(), 

3891 ], 

3892 right_panels=[ 

3893 panels.InventoryItemsPanel(), 

3894 ], 

3895 ) 

3896 

3897 

3898@register_model_view(CoolingOutflow, 'add', detail=False) 

3899class CoolingOutflowCreateView(generic.ComponentCreateView): 

3900 queryset = CoolingOutflow.objects.all() 

3901 form = forms.CoolingOutflowCreateForm 

3902 model_form = forms.CoolingOutflowForm 

3903 

3904 

3905@register_model_view(CoolingOutflow, 'edit') 

3906class CoolingOutflowEditView(generic.ObjectEditView): 

3907 queryset = CoolingOutflow.objects.all() 

3908 form = forms.CoolingOutflowForm 

3909 

3910 

3911@register_model_view(CoolingOutflow, 'delete') 

3912class CoolingOutflowDeleteView(generic.ObjectDeleteView): 

3913 queryset = CoolingOutflow.objects.all() 

3914 

3915 

3916@register_model_view(CoolingOutflow, 'bulk_import', path='import', detail=False) 

3917class CoolingOutflowBulkImportView(generic.BulkImportView): 

3918 queryset = CoolingOutflow.objects.all() 

3919 model_form = forms.CoolingOutflowImportForm 

3920 

3921 

3922@register_model_view(CoolingOutflow, 'bulk_edit', path='edit', detail=False) 

3923class CoolingOutflowBulkEditView(generic.BulkEditView): 

3924 queryset = CoolingOutflow.objects.all() 

3925 filterset = filtersets.CoolingOutflowFilterSet 

3926 table = tables.CoolingOutflowTable 

3927 form = forms.CoolingOutflowBulkEditForm 

3928 

3929 

3930@register_model_view(CoolingOutflow, 'bulk_rename', path='rename', detail=False) 

3931class CoolingOutflowBulkRenameView(generic.BulkRenameView): 

3932 queryset = CoolingOutflow.objects.all() 

3933 filterset = filtersets.CoolingOutflowFilterSet 

3934 rename_fields = ('name', 'label') 

3935 

3936 

3937@register_model_view(CoolingOutflow, 'bulk_delete', path='delete', detail=False) 

3938class CoolingOutflowBulkDeleteView(generic.BulkDeleteView): 

3939 queryset = CoolingOutflow.objects.all() 

3940 filterset = filtersets.CoolingOutflowFilterSet 

3941 table = tables.CoolingOutflowTable 

3942 

3943 

3944# 

3945# Interfaces 

3946# 

3947 

3948@register_model_view(Interface, 'list', path='', detail=False) 

3949class InterfaceListView(generic.ObjectListView): 

3950 queryset = Interface.objects.all() 

3951 filterset = filtersets.InterfaceFilterSet 

3952 filterset_form = forms.InterfaceFilterForm 

3953 table = tables.InterfaceTable 

3954 

3955 

3956@register_model_view(Interface) 

3957class InterfaceView(generic.ObjectView): 

3958 queryset = Interface.objects.all() 

3959 layout = layout.SimpleLayout( 

3960 breadcrumbs=[ 

3961 Breadcrumb('device', url=object_view_url('dcim:device_interfaces')), 

3962 ], 

3963 left_panels=[ 

3964 panels.InterfacePanel(), 

3965 panels.RelatedInterfacesPanel(), 

3966 CustomFieldsPanel(), 

3967 TagsPanel(), 

3968 ], 

3969 right_panels=[ 

3970 ContextTablePanel('vdc_table', title=_('Virtual Device Contexts')), 

3971 panels.InterfaceAddressingPanel(), 

3972 panels.VirtualCircuitPanel(), 

3973 panels.InterfaceConnectionPanel(), 

3974 panels.InterfaceWirelessPanel(), 

3975 panels.WirelessLANsPanel(), 

3976 FHRPGroupAssignmentsPanel(), 

3977 panels.InventoryItemsPanel(), 

3978 ], 

3979 bottom_panels=[ 

3980 ObjectsTablePanel( 

3981 model='ipam.IPAddress', 

3982 filters={'interface_id': lambda ctx: ctx['object'].pk}, 

3983 title=_('IP Addresses'), 

3984 exclude_columns=['assigned', 'assigned_object', 'assigned_object_parent'], 

3985 ), 

3986 ObjectsTablePanel( 

3987 model='dcim.MACAddress', 

3988 filters={'interface_id': lambda ctx: ctx['object'].pk}, 

3989 title=_('MAC Addresses'), 

3990 exclude_columns=['assigned_object', 'assigned_object_parent'], 

3991 actions=[ 

3992 actions.AddObject( 

3993 'dcim.MACAddress', url_params={'interface': lambda ctx: ctx['object'].pk} 

3994 ), 

3995 ], 

3996 ), 

3997 ObjectsTablePanel( 

3998 model='ipam.VLAN', 

3999 filters={'interface_id': lambda ctx: ctx['object'].pk}, 

4000 title=_('VLANs'), 

4001 ), 

4002 ContextTablePanel('lag_interfaces_table', title=_('LAG Members')), 

4003 ContextTablePanel('vlan_translation_table', title=_('VLAN Translation')), 

4004 ContextTablePanel('bridge_interfaces_table', title=_('Bridged Interfaces')), 

4005 ContextTablePanel('child_interfaces_table', title=_('Child Interfaces')), 

4006 ], 

4007 ) 

4008 

4009 def get_extra_context(self, request, instance): 

4010 # Get assigned VDCs 

4011 vdc_table = tables.VirtualDeviceContextTable( 

4012 data=instance.vdcs.restrict(request.user, 'view').prefetch_related('device'), 

4013 exclude=( 

4014 'tenant', 'tenant_group', 'primary_ip', 'primary_ip4', 'primary_ip6', 'oob_ip', 'comments', 'tags', 

4015 'created', 'last_updated', 'actions', 

4016 ), 

4017 orderable=False 

4018 ) 

4019 vdc_table.configure(request) 

4020 

4021 # Get bridge interfaces 

4022 bridge_interfaces_table = tables.InterfaceTable( 

4023 Interface.objects.restrict(request.user, 'view').filter(bridge=instance), 

4024 exclude=('device', 'parent'), 

4025 orderable=False 

4026 ) 

4027 bridge_interfaces_table.configure(request) 

4028 

4029 # Get child interfaces 

4030 child_interfaces_table = tables.InterfaceTable( 

4031 Interface.objects.restrict(request.user, 'view').filter(parent=instance), 

4032 exclude=('device', 'parent'), 

4033 orderable=False 

4034 ) 

4035 child_interfaces_table.configure(request) 

4036 

4037 # Get LAG members (only for LAG interfaces) 

4038 lag_interfaces_table = None 

4039 if instance.is_lag: 

4040 lag_interfaces_table = tables.InterfaceLAGMemberTable( 

4041 Interface.objects.restrict(request.user, 'view').filter(lag=instance), 

4042 orderable=False 

4043 ) 

4044 lag_interfaces_table.configure(request) 

4045 

4046 # Get VLAN translation rules 

4047 vlan_translation_table = None 

4048 if instance.vlan_translation_policy: 

4049 vlan_translation_table = VLANTranslationRuleTable( 

4050 data=instance.vlan_translation_policy.rules.all(), 

4051 orderable=False 

4052 ) 

4053 vlan_translation_table.configure(request) 

4054 

4055 return { 

4056 'vdc_table': vdc_table, 

4057 'bridge_interfaces_table': bridge_interfaces_table, 

4058 'child_interfaces_table': child_interfaces_table, 

4059 'lag_interfaces_table': lag_interfaces_table, 

4060 'vlan_translation_table': vlan_translation_table, 

4061 } 

4062 

4063 

4064@register_model_view(Interface, 'add', detail=False) 

4065class InterfaceCreateView(generic.ComponentCreateView): 

4066 queryset = Interface.objects.all() 

4067 form = forms.InterfaceCreateForm 

4068 model_form = forms.InterfaceForm 

4069 

4070 

4071@register_model_view(Interface, 'edit') 

4072class InterfaceEditView(generic.ObjectEditView): 

4073 queryset = Interface.objects.all() 

4074 form = forms.InterfaceForm 

4075 

4076 

4077@register_model_view(Interface, 'delete') 

4078class InterfaceDeleteView(generic.ObjectDeleteView): 

4079 queryset = Interface.objects.all() 

4080 

4081 

4082@register_model_view(Interface, 'bulk_import', path='import', detail=False) 

4083class InterfaceBulkImportView(generic.BulkImportView): 

4084 queryset = Interface.objects.all() 

4085 model_form = forms.InterfaceImportForm 

4086 

4087 

4088@register_model_view(Interface, 'bulk_edit', path='edit', detail=False) 

4089class InterfaceBulkEditView(generic.BulkEditView): 

4090 queryset = Interface.objects.all() 

4091 filterset = filtersets.InterfaceFilterSet 

4092 table = tables.InterfaceTable 

4093 form = forms.InterfaceBulkEditForm 

4094 

4095 def post_save_operations(self, form, obj): 

4096 super().post_save_operations(form, obj) 

4097 

4098 # Add/remove tagged VLANs 

4099 if obj.mode == InterfaceModeChoices.MODE_TAGGED: 

4100 if form.cleaned_data.get('add_tagged_vlans', None): 

4101 obj.tagged_vlans.add(*form.cleaned_data['add_tagged_vlans']) 

4102 if form.cleaned_data.get('remove_tagged_vlans', None): 

4103 obj.tagged_vlans.remove(*form.cleaned_data['remove_tagged_vlans']) 

4104 

4105 

4106@register_model_view(Interface, 'bulk_rename', path='rename', detail=False) 

4107class InterfaceBulkRenameView(generic.BulkRenameView): 

4108 queryset = Interface.objects.all() 

4109 filterset = filtersets.InterfaceFilterSet 

4110 rename_fields = ('name', 'label') 

4111 

4112 

4113@register_model_view(Interface, 'bulk_disconnect', path='disconnect', detail=False) 

4114class InterfaceBulkDisconnectView(BulkDisconnectView): 

4115 queryset = Interface.objects.all() 

4116 

4117 

4118@register_model_view(Interface, 'bulk_delete', path='delete', detail=False) 

4119class InterfaceBulkDeleteView(generic.BulkDeleteView): 

4120 # Ensure child interfaces are deleted prior to their parents 

4121 queryset = Interface.objects.order_by('device', 'parent', CollateAsChar('_name')) 

4122 filterset = filtersets.InterfaceFilterSet 

4123 table = tables.InterfaceTable 

4124 

4125 

4126# Trace view 

4127register_model_view(Interface, 'trace', kwargs={'model': Interface})(PathTraceView) 

4128 

4129 

4130# 

4131# Front ports 

4132# 

4133 

4134@register_model_view(FrontPort, 'list', path='', detail=False) 

4135class FrontPortListView(generic.ObjectListView): 

4136 queryset = FrontPort.objects.all() 

4137 filterset = filtersets.FrontPortFilterSet 

4138 filterset_form = forms.FrontPortFilterForm 

4139 table = tables.FrontPortTable 

4140 

4141 

4142@register_model_view(FrontPort) 

4143class FrontPortView(generic.ObjectView): 

4144 template_name = 'generic/object.html' 

4145 queryset = FrontPort.objects.all() 

4146 layout = layout.SimpleLayout( 

4147 breadcrumbs=[ 

4148 Breadcrumb('device', url=object_view_url('dcim:device_frontports')), 

4149 ], 

4150 left_panels=[ 

4151 panels.FrontPortPanel(), 

4152 CustomFieldsPanel(), 

4153 TagsPanel(), 

4154 panels.InventoryItemsPanel(), 

4155 ], 

4156 right_panels=[ 

4157 panels.ConnectionPanel( 

4158 trace_url_name='dcim:frontport_trace', 

4159 show_endpoints=False, 

4160 connect_options=[ 

4161 {'a_type': 'dcim.frontport', 'b_type': 'dcim.interface', 'label': _('Interface')}, 

4162 {'a_type': 'dcim.frontport', 'b_type': 'dcim.consoleserverport', 'label': _('Console Server Port')}, 

4163 {'a_type': 'dcim.frontport', 'b_type': 'dcim.consoleport', 'label': _('Console Port')}, 

4164 {'a_type': 'dcim.frontport', 'b_type': 'dcim.frontport', 'label': _('Front Port')}, 

4165 {'a_type': 'dcim.frontport', 'b_type': 'dcim.rearport', 'label': _('Rear Port')}, 

4166 { 

4167 'a_type': 'dcim.frontport', 

4168 'b_type': 'circuits.circuittermination', 

4169 'label': _('Circuit Termination'), 

4170 }, 

4171 ], 

4172 ), 

4173 TemplatePanel('dcim/panels/front_port_mappings.html'), 

4174 ], 

4175 ) 

4176 

4177 def get_extra_context(self, request, instance): 

4178 return { 

4179 'rear_port_mappings': PortMapping.objects.filter(front_port=instance).prefetch_related('rear_port'), 

4180 } 

4181 

4182 

4183@register_model_view(FrontPort, 'add', detail=False) 

4184class FrontPortCreateView(generic.ComponentCreateView): 

4185 queryset = FrontPort.objects.all() 

4186 form = forms.FrontPortCreateForm 

4187 model_form = forms.FrontPortForm 

4188 

4189 

4190@register_model_view(FrontPort, 'edit') 

4191class FrontPortEditView(generic.ObjectEditView): 

4192 queryset = FrontPort.objects.all() 

4193 form = forms.FrontPortForm 

4194 

4195 

4196@register_model_view(FrontPort, 'delete') 

4197class FrontPortDeleteView(generic.ObjectDeleteView): 

4198 queryset = FrontPort.objects.all() 

4199 

4200 

4201@register_model_view(FrontPort, 'bulk_import', path='import', detail=False) 

4202class FrontPortBulkImportView(generic.BulkImportView): 

4203 queryset = FrontPort.objects.all() 

4204 model_form = forms.FrontPortImportForm 

4205 

4206 

4207@register_model_view(FrontPort, 'bulk_edit', path='edit', detail=False) 

4208class FrontPortBulkEditView(generic.BulkEditView): 

4209 queryset = FrontPort.objects.all() 

4210 filterset = filtersets.FrontPortFilterSet 

4211 table = tables.FrontPortTable 

4212 form = forms.FrontPortBulkEditForm 

4213 

4214 

4215@register_model_view(FrontPort, 'bulk_rename', path='rename', detail=False) 

4216class FrontPortBulkRenameView(generic.BulkRenameView): 

4217 queryset = FrontPort.objects.all() 

4218 filterset = filtersets.FrontPortFilterSet 

4219 rename_fields = ('name', 'label') 

4220 

4221 

4222@register_model_view(FrontPort, 'bulk_disconnect', path='disconnect', detail=False) 

4223class FrontPortBulkDisconnectView(BulkDisconnectView): 

4224 queryset = FrontPort.objects.all() 

4225 

4226 

4227@register_model_view(FrontPort, 'bulk_delete', path='delete', detail=False) 

4228class FrontPortBulkDeleteView(generic.BulkDeleteView): 

4229 queryset = FrontPort.objects.all() 

4230 filterset = filtersets.FrontPortFilterSet 

4231 table = tables.FrontPortTable 

4232 

4233 

4234# Trace view 

4235register_model_view(FrontPort, 'trace', kwargs={'model': FrontPort})(PathTraceView) 

4236 

4237 

4238# 

4239# Rear ports 

4240# 

4241 

4242@register_model_view(RearPort, 'list', path='', detail=False) 

4243class RearPortListView(generic.ObjectListView): 

4244 queryset = RearPort.objects.all() 

4245 filterset = filtersets.RearPortFilterSet 

4246 filterset_form = forms.RearPortFilterForm 

4247 table = tables.RearPortTable 

4248 

4249 

4250@register_model_view(RearPort) 

4251class RearPortView(generic.ObjectView): 

4252 template_name = 'generic/object.html' 

4253 queryset = RearPort.objects.all() 

4254 layout = layout.SimpleLayout( 

4255 breadcrumbs=[ 

4256 Breadcrumb('device', url=object_view_url('dcim:device_rearports')), 

4257 ], 

4258 left_panels=[ 

4259 panels.RearPortPanel(), 

4260 CustomFieldsPanel(), 

4261 TagsPanel(), 

4262 panels.InventoryItemsPanel(), 

4263 ], 

4264 right_panels=[ 

4265 panels.ConnectionPanel( 

4266 trace_url_name='dcim:rearport_trace', 

4267 show_endpoints=False, 

4268 connect_options=[ 

4269 {'a_type': 'dcim.rearport', 'b_type': 'dcim.interface', 'label': _('Interface')}, 

4270 {'a_type': 'dcim.rearport', 'b_type': 'dcim.frontport', 'label': _('Front Port')}, 

4271 {'a_type': 'dcim.rearport', 'b_type': 'dcim.rearport', 'label': _('Rear Port')}, 

4272 { 

4273 'a_type': 'dcim.rearport', 

4274 'b_type': 'circuits.circuittermination', 

4275 'label': _('Circuit Termination'), 

4276 }, 

4277 ], 

4278 ), 

4279 TemplatePanel('dcim/panels/rear_port_mappings.html'), 

4280 ], 

4281 ) 

4282 

4283 def get_extra_context(self, request, instance): 

4284 return { 

4285 'front_port_mappings': PortMapping.objects.filter(rear_port=instance).prefetch_related('front_port'), 

4286 } 

4287 

4288 

4289@register_model_view(RearPort, 'add', detail=False) 

4290class RearPortCreateView(generic.ComponentCreateView): 

4291 queryset = RearPort.objects.all() 

4292 form = forms.RearPortCreateForm 

4293 model_form = forms.RearPortForm 

4294 

4295 

4296@register_model_view(RearPort, 'edit') 

4297class RearPortEditView(generic.ObjectEditView): 

4298 queryset = RearPort.objects.all() 

4299 form = forms.RearPortForm 

4300 

4301 

4302@register_model_view(RearPort, 'delete') 

4303class RearPortDeleteView(generic.ObjectDeleteView): 

4304 queryset = RearPort.objects.all() 

4305 

4306 

4307@register_model_view(RearPort, 'bulk_import', path='import', detail=False) 

4308class RearPortBulkImportView(generic.BulkImportView): 

4309 queryset = RearPort.objects.all() 

4310 model_form = forms.RearPortImportForm 

4311 

4312 

4313@register_model_view(RearPort, 'bulk_edit', path='edit', detail=False) 

4314class RearPortBulkEditView(generic.BulkEditView): 

4315 queryset = RearPort.objects.all() 

4316 filterset = filtersets.RearPortFilterSet 

4317 table = tables.RearPortTable 

4318 form = forms.RearPortBulkEditForm 

4319 

4320 

4321@register_model_view(RearPort, 'bulk_rename', path='rename', detail=False) 

4322class RearPortBulkRenameView(generic.BulkRenameView): 

4323 queryset = RearPort.objects.all() 

4324 rename_fields = ('name', 'label') 

4325 

4326 

4327@register_model_view(RearPort, 'bulk_disconnect', path='disconnect', detail=False) 

4328class RearPortBulkDisconnectView(BulkDisconnectView): 

4329 queryset = RearPort.objects.all() 

4330 filterset = filtersets.RearPortFilterSet 

4331 

4332 

4333@register_model_view(RearPort, 'bulk_delete', path='delete', detail=False) 

4334class RearPortBulkDeleteView(generic.BulkDeleteView): 

4335 queryset = RearPort.objects.all() 

4336 filterset = filtersets.RearPortFilterSet 

4337 table = tables.RearPortTable 

4338 

4339 

4340# Trace view 

4341register_model_view(RearPort, 'trace', kwargs={'model': RearPort})(PathTraceView) 

4342 

4343 

4344# 

4345# Module bays 

4346# 

4347 

4348@register_model_view(ModuleBay, 'list', path='', detail=False) 

4349class ModuleBayListView(generic.ObjectListView): 

4350 queryset = ModuleBay.objects.select_related('installed_module__module_type') 

4351 filterset = filtersets.ModuleBayFilterSet 

4352 filterset_form = forms.ModuleBayFilterForm 

4353 table = tables.ModuleBayTable 

4354 

4355 

4356@register_model_view(ModuleBay) 

4357class ModuleBayView(generic.ObjectView): 

4358 template_name = 'generic/object.html' 

4359 queryset = ModuleBay.objects.prefetch_related( 

4360 'module_bay_types', 

4361 'installed_module__module_type__module_bay_types', 

4362 ) 

4363 layout = layout.SimpleLayout( 

4364 breadcrumbs=[ 

4365 Breadcrumb('device', url=object_view_url('dcim:device_modulebays')), 

4366 ], 

4367 left_panels=[ 

4368 panels.BayTypeIncompatibilityPanel(), 

4369 panels.ModuleBayPanel(), 

4370 TagsPanel(), 

4371 ], 

4372 right_panels=[ 

4373 CustomFieldsPanel(), 

4374 panels.InstalledModulePanel(), 

4375 ], 

4376 ) 

4377 

4378 

4379@register_model_view(ModuleBay, 'add', detail=False) 

4380class ModuleBayCreateView(generic.ComponentCreateView): 

4381 queryset = ModuleBay.objects.all() 

4382 form = forms.ModuleBayCreateForm 

4383 model_form = forms.ModuleBayForm 

4384 

4385 

4386@register_model_view(ModuleBay, 'edit') 

4387class ModuleBayEditView(generic.ObjectEditView): 

4388 queryset = ModuleBay.objects.all() 

4389 form = forms.ModuleBayForm 

4390 

4391 def post(self, request, *args, **kwargs): 

4392 response = super().post(request, *args, **kwargs) 

4393 # A successful save always redirects (302) or returns an HTMX redirect header. 

4394 # Quick-add creates new objects (no pk in kwargs), so it is excluded by the pk guard below. 

4395 saved = ( 

4396 getattr(response, 'status_code', None) == 302 or 

4397 (hasattr(response, 'headers') and 'HX-Location' in response.headers) 

4398 ) 

4399 if saved and kwargs.get('pk'): 

4400 try: 

4401 bay = ModuleBay.objects.prefetch_related( 

4402 'module_bay_types', 'installed_module__module_type__module_bay_types' 

4403 ).get(pk=kwargs['pk']) 

4404 if not bay.is_module_compatible: 

4405 messages.warning( 

4406 request, 

4407 _('The module currently installed in this bay is incompatible with the new bay type ' 

4408 'constraints. Consider removing or replacing it.') 

4409 ) 

4410 except ModuleBay.DoesNotExist: 

4411 pass 

4412 return response 

4413 

4414 

4415@register_model_view(ModuleBay, 'delete') 

4416class ModuleBayDeleteView(generic.ObjectDeleteView): 

4417 queryset = ModuleBay.objects.all() 

4418 

4419 

4420@register_model_view(ModuleBay, 'bulk_import', path='import', detail=False) 

4421class ModuleBayBulkImportView(generic.BulkImportView): 

4422 queryset = ModuleBay.objects.all() 

4423 model_form = forms.ModuleBayImportForm 

4424 

4425 

4426@register_model_view(ModuleBay, 'bulk_edit', path='edit', detail=False) 

4427class ModuleBayBulkEditView(generic.BulkEditView): 

4428 queryset = ModuleBay.objects.all() 

4429 filterset = filtersets.ModuleBayFilterSet 

4430 table = tables.ModuleBayTable 

4431 form = forms.ModuleBayBulkEditForm 

4432 

4433 def post_save_operations(self, form, obj): 

4434 super().post_save_operations(form, obj) 

4435 add = form.cleaned_data.get('add_module_bay_types') 

4436 remove = form.cleaned_data.get('remove_module_bay_types') 

4437 if add: 

4438 obj.module_bay_types.add(*add) 

4439 if remove: 

4440 obj.module_bay_types.remove(*remove) 

4441 if add or remove: 

4442 # Counts current incompatibilities, not just newly-introduced ones; may over-warn 

4443 # if pre-existing incompatibilities exist, but safe to under-warn on. 

4444 self._incompatible_count += int(not obj.is_module_compatible) 

4445 

4446 def post(self, request, **kwargs): 

4447 self._incompatible_count = 0 

4448 response = super().post(request, **kwargs) 

4449 if self._incompatible_count and getattr(response, 'status_code', None) == 302: 

4450 messages.warning( 

4451 request, 

4452 ngettext( 

4453 '%(count)d module bay now has an incompatible module installed ' 

4454 'due to conflicting bay type constraints.', 

4455 '%(count)d module bays now have incompatible modules installed ' 

4456 'due to conflicting bay type constraints.', 

4457 self._incompatible_count, 

4458 ) % {'count': self._incompatible_count} 

4459 ) 

4460 return response 

4461 

4462 

4463@register_model_view(ModuleBay, 'bulk_rename', path='rename', detail=False) 

4464class ModuleBayBulkRenameView(generic.BulkRenameView): 

4465 queryset = ModuleBay.objects.all() 

4466 filterset = filtersets.ModuleBayFilterSet 

4467 rename_fields = ('name', 'label') 

4468 

4469 

4470@register_model_view(ModuleBay, 'bulk_delete', path='delete', detail=False) 

4471class ModuleBayBulkDeleteView(generic.BulkDeleteView): 

4472 queryset = ModuleBay.objects.all() 

4473 filterset = filtersets.ModuleBayFilterSet 

4474 table = tables.ModuleBayTable 

4475 

4476 

4477# 

4478# Device bays 

4479# 

4480 

4481@register_model_view(DeviceBay, 'list', path='', detail=False) 

4482class DeviceBayListView(generic.ObjectListView): 

4483 queryset = DeviceBay.objects.all() 

4484 filterset = filtersets.DeviceBayFilterSet 

4485 filterset_form = forms.DeviceBayFilterForm 

4486 table = tables.DeviceBayTable 

4487 

4488 

4489@register_model_view(DeviceBay) 

4490class DeviceBayView(generic.ObjectView): 

4491 template_name = 'generic/object.html' 

4492 queryset = DeviceBay.objects.all() 

4493 layout = layout.SimpleLayout( 

4494 breadcrumbs=[ 

4495 Breadcrumb('device', url=object_view_url('dcim:device_devicebays')), 

4496 ], 

4497 left_panels=[ 

4498 panels.DeviceBayPanel(), 

4499 CustomFieldsPanel(), 

4500 TagsPanel(), 

4501 ], 

4502 right_panels=[ 

4503 panels.InstalledDevicePanel(), 

4504 ], 

4505 ) 

4506 

4507 

4508@register_model_view(DeviceBay, 'add', detail=False) 

4509class DeviceBayCreateView(generic.ComponentCreateView): 

4510 queryset = DeviceBay.objects.all() 

4511 form = forms.DeviceBayCreateForm 

4512 model_form = forms.DeviceBayForm 

4513 

4514 

4515@register_model_view(DeviceBay, 'edit') 

4516class DeviceBayEditView(generic.ObjectEditView): 

4517 queryset = DeviceBay.objects.all() 

4518 form = forms.DeviceBayForm 

4519 

4520 

4521@register_model_view(DeviceBay, 'delete') 

4522class DeviceBayDeleteView(generic.ObjectDeleteView): 

4523 queryset = DeviceBay.objects.all() 

4524 

4525 

4526@register_model_view(DeviceBay, 'populate') 

4527class DeviceBayPopulateView(generic.ObjectEditView): 

4528 queryset = DeviceBay.objects.all() 

4529 

4530 def get(self, request, pk): 

4531 device_bay = get_object_or_404(self.queryset, pk=pk) 

4532 form = forms.PopulateDeviceBayForm(device_bay) 

4533 

4534 return render(request, 'dcim/devicebay_populate.html', { 

4535 'device_bay': device_bay, 

4536 'form': form, 

4537 'return_url': self.get_return_url(request, device_bay), 

4538 }) 

4539 

4540 def post(self, request, pk): 

4541 device_bay = get_object_or_404(self.queryset, pk=pk) 

4542 form = forms.PopulateDeviceBayForm(device_bay, request.POST) 

4543 

4544 if form.is_valid(): 

4545 device_bay.snapshot() 

4546 device_bay.installed_device = form.cleaned_data['installed_device'] 

4547 device_bay.save() 

4548 messages.success( 

4549 request, 

4550 _("Installed device {device} in bay {device_bay}.").format( 

4551 device=device_bay.installed_device, 

4552 device_bay=device_bay 

4553 ) 

4554 ) 

4555 return_url = self.get_return_url(request) 

4556 

4557 return redirect(return_url) 

4558 

4559 return render(request, 'dcim/devicebay_populate.html', { 

4560 'device_bay': device_bay, 

4561 'form': form, 

4562 'return_url': self.get_return_url(request, device_bay), 

4563 }) 

4564 

4565 

4566@register_model_view(DeviceBay, 'depopulate') 

4567class DeviceBayDepopulateView(generic.ObjectEditView): 

4568 queryset = DeviceBay.objects.all() 

4569 

4570 def get(self, request, pk): 

4571 device_bay = get_object_or_404(self.queryset, pk=pk) 

4572 form = ConfirmationForm() 

4573 

4574 return render(request, 'dcim/devicebay_depopulate.html', { 

4575 'device_bay': device_bay, 

4576 'form': form, 

4577 'return_url': self.get_return_url(request, device_bay), 

4578 }) 

4579 

4580 def post(self, request, pk): 

4581 device_bay = get_object_or_404(self.queryset, pk=pk) 

4582 form = ConfirmationForm(request.POST) 

4583 

4584 if form.is_valid(): 

4585 device_bay.snapshot() 

4586 removed_device = device_bay.installed_device 

4587 device_bay.installed_device = None 

4588 device_bay.save() 

4589 messages.success( 

4590 request, 

4591 _("Removed device {device} from bay {device_bay}.").format( 

4592 device=removed_device, 

4593 device_bay=device_bay 

4594 ) 

4595 ) 

4596 return_url = self.get_return_url(request, device_bay.device) 

4597 

4598 return redirect(return_url) 

4599 

4600 return render(request, 'dcim/devicebay_depopulate.html', { 

4601 'device_bay': device_bay, 

4602 'form': form, 

4603 'return_url': self.get_return_url(request, device_bay), 

4604 }) 

4605 

4606 

4607@register_model_view(DeviceBay, 'bulk_import', path='import', detail=False) 

4608class DeviceBayBulkImportView(generic.BulkImportView): 

4609 queryset = DeviceBay.objects.all() 

4610 model_form = forms.DeviceBayImportForm 

4611 

4612 

4613@register_model_view(DeviceBay, 'bulk_edit', path='edit', detail=False) 

4614class DeviceBayBulkEditView(generic.BulkEditView): 

4615 queryset = DeviceBay.objects.all() 

4616 filterset = filtersets.DeviceBayFilterSet 

4617 table = tables.DeviceBayTable 

4618 form = forms.DeviceBayBulkEditForm 

4619 

4620 

4621@register_model_view(DeviceBay, 'bulk_rename', path='rename', detail=False) 

4622class DeviceBayBulkRenameView(generic.BulkRenameView): 

4623 queryset = DeviceBay.objects.all() 

4624 filterset = filtersets.DeviceBayFilterSet 

4625 rename_fields = ('name', 'label') 

4626 

4627 

4628@register_model_view(DeviceBay, 'bulk_delete', path='delete', detail=False) 

4629class DeviceBayBulkDeleteView(generic.BulkDeleteView): 

4630 queryset = DeviceBay.objects.all() 

4631 filterset = filtersets.DeviceBayFilterSet 

4632 table = tables.DeviceBayTable 

4633 

4634 

4635# 

4636# Inventory items 

4637# 

4638 

4639@register_model_view(InventoryItem, 'list', path='', detail=False) 

4640class InventoryItemListView(generic.ObjectListView): 

4641 queryset = InventoryItem.objects.all() 

4642 filterset = filtersets.InventoryItemFilterSet 

4643 filterset_form = forms.InventoryItemFilterForm 

4644 table = tables.InventoryItemTable 

4645 

4646 

4647@register_model_view(InventoryItem) 

4648class InventoryItemView(generic.ObjectView): 

4649 template_name = 'generic/object.html' 

4650 queryset = InventoryItem.objects.all() 

4651 layout = layout.SimpleLayout( 

4652 breadcrumbs=[ 

4653 Breadcrumb('device', url=object_view_url('dcim:device_inventory')), 

4654 ], 

4655 left_panels=[ 

4656 panels.InventoryItemPanel(), 

4657 CustomFieldsPanel(), 

4658 TagsPanel(), 

4659 ], 

4660 ) 

4661 

4662 

4663@register_model_view(InventoryItem, 'edit') 

4664class InventoryItemEditView(generic.ObjectEditView): 

4665 queryset = InventoryItem.objects.all() 

4666 form = forms.InventoryItemForm 

4667 

4668 

4669@register_model_view(InventoryItem, 'add', detail=False) 

4670class InventoryItemCreateView(generic.ComponentCreateView): 

4671 queryset = InventoryItem.objects.all() 

4672 form = forms.InventoryItemCreateForm 

4673 model_form = forms.InventoryItemForm 

4674 

4675 

4676@register_model_view(InventoryItem, 'delete') 

4677class InventoryItemDeleteView(generic.ObjectDeleteView): 

4678 queryset = InventoryItem.objects.all() 

4679 

4680 

4681@register_model_view(InventoryItem, 'bulk_import', path='import', detail=False) 

4682class InventoryItemBulkImportView(generic.BulkImportView): 

4683 queryset = InventoryItem.objects.all() 

4684 model_form = forms.InventoryItemImportForm 

4685 

4686 

4687@register_model_view(InventoryItem, 'bulk_edit', path='edit', detail=False) 

4688class InventoryItemBulkEditView(generic.BulkEditView): 

4689 queryset = InventoryItem.objects.all() 

4690 filterset = filtersets.InventoryItemFilterSet 

4691 table = tables.InventoryItemTable 

4692 form = forms.InventoryItemBulkEditForm 

4693 

4694 

4695@register_model_view(InventoryItem, 'bulk_rename', path='rename', detail=False) 

4696class InventoryItemBulkRenameView(generic.BulkRenameView): 

4697 queryset = InventoryItem.objects.all() 

4698 filterset = filtersets.InventoryItemFilterSet 

4699 rename_fields = ('name', 'label') 

4700 

4701 

4702@register_model_view(InventoryItem, 'bulk_delete', path='delete', detail=False) 

4703class InventoryItemBulkDeleteView(generic.BulkDeleteView): 

4704 queryset = InventoryItem.objects.all() 

4705 filterset = filtersets.InventoryItemFilterSet 

4706 table = tables.InventoryItemTable 

4707 

4708 

4709@register_model_view(InventoryItem, 'children') 

4710class InventoryItemChildrenView(generic.ObjectChildrenView): 

4711 queryset = InventoryItem.objects.all() 

4712 child_model = InventoryItem 

4713 table = tables.InventoryItemTable 

4714 filterset = filtersets.InventoryItemFilterSet 

4715 filterset_form = forms.InventoryItemFilterForm 

4716 tab = ViewTab( 

4717 label=_('Children'), 

4718 badge=lambda obj: obj.child_items.count(), 

4719 permission='dcim.view_inventoryitem', 

4720 hide_if_empty=True, 

4721 weight=5000 

4722 ) 

4723 

4724 def get_children(self, request, parent): 

4725 return parent.child_items.restrict(request.user, 'view') 

4726 

4727 

4728# 

4729# Inventory item roles 

4730# 

4731 

4732@register_model_view(InventoryItemRole, 'list', path='', detail=False) 

4733class InventoryItemRoleListView(generic.ObjectListView): 

4734 queryset = InventoryItemRole.objects.annotate( 

4735 inventoryitem_count=count_related(InventoryItem, 'role'), 

4736 ) 

4737 filterset = filtersets.InventoryItemRoleFilterSet 

4738 filterset_form = forms.InventoryItemRoleFilterForm 

4739 table = tables.InventoryItemRoleTable 

4740 

4741 

4742@register_model_view(InventoryItemRole) 

4743class InventoryItemRoleView(GetRelatedModelsMixin, generic.ObjectView): 

4744 queryset = InventoryItemRole.objects.all() 

4745 template_name = 'generic/object.html' 

4746 layout = layout.SimpleLayout( 

4747 left_panels=[ 

4748 panels.InventoryItemRolePanel(), 

4749 TagsPanel(), 

4750 ], 

4751 right_panels=[ 

4752 RelatedObjectsPanel(), 

4753 CustomFieldsPanel(), 

4754 CommentsPanel(), 

4755 ], 

4756 ) 

4757 

4758 def get_extra_context(self, request, instance): 

4759 return { 

4760 'related_models': self.get_related_models(request, instance), 

4761 } 

4762 

4763 

4764@register_model_view(InventoryItemRole, 'add', detail=False) 

4765@register_model_view(InventoryItemRole, 'edit') 

4766class InventoryItemRoleEditView(generic.ObjectEditView): 

4767 queryset = InventoryItemRole.objects.all() 

4768 form = forms.InventoryItemRoleForm 

4769 

4770 

4771@register_model_view(InventoryItemRole, 'delete') 

4772class InventoryItemRoleDeleteView(generic.ObjectDeleteView): 

4773 queryset = InventoryItemRole.objects.all() 

4774 

4775 

4776@register_model_view(InventoryItemRole, 'bulk_import', path='import', detail=False) 

4777class InventoryItemRoleBulkImportView(generic.BulkImportView): 

4778 queryset = InventoryItemRole.objects.all() 

4779 model_form = forms.InventoryItemRoleImportForm 

4780 

4781 

4782@register_model_view(InventoryItemRole, 'bulk_edit', path='edit', detail=False) 

4783class InventoryItemRoleBulkEditView(generic.BulkEditView): 

4784 queryset = InventoryItemRole.objects.annotate( 

4785 inventoryitem_count=count_related(InventoryItem, 'role'), 

4786 ) 

4787 filterset = filtersets.InventoryItemRoleFilterSet 

4788 table = tables.InventoryItemRoleTable 

4789 form = forms.InventoryItemRoleBulkEditForm 

4790 

4791 

4792@register_model_view(InventoryItemRole, 'bulk_rename', path='rename', detail=False) 

4793class InventoryItemRoleBulkRenameView(generic.BulkRenameView): 

4794 queryset = InventoryItemRole.objects.all() 

4795 filterset = filtersets.InventoryItemRoleFilterSet 

4796 

4797 

4798@register_model_view(InventoryItemRole, 'bulk_delete', path='delete', detail=False) 

4799class InventoryItemRoleBulkDeleteView(generic.BulkDeleteView): 

4800 queryset = InventoryItemRole.objects.annotate( 

4801 inventoryitem_count=count_related(InventoryItem, 'role'), 

4802 ) 

4803 filterset = filtersets.InventoryItemRoleFilterSet 

4804 table = tables.InventoryItemRoleTable 

4805 

4806 

4807# 

4808# Bulk Device component creation 

4809# 

4810 

4811class DeviceBulkAddConsolePortView(generic.BulkComponentCreateView): 

4812 parent_model = Device 

4813 parent_field = 'device' 

4814 form = forms.ConsolePortBulkCreateForm 

4815 queryset = ConsolePort.objects.all() 

4816 model_form = forms.ConsolePortForm 

4817 filterset = filtersets.DeviceFilterSet 

4818 table = tables.DeviceTable 

4819 default_return_url = 'dcim:device_list' 

4820 

4821 

4822class DeviceBulkAddConsoleServerPortView(generic.BulkComponentCreateView): 

4823 parent_model = Device 

4824 parent_field = 'device' 

4825 form = forms.ConsoleServerPortBulkCreateForm 

4826 queryset = ConsoleServerPort.objects.all() 

4827 model_form = forms.ConsoleServerPortForm 

4828 filterset = filtersets.DeviceFilterSet 

4829 table = tables.DeviceTable 

4830 default_return_url = 'dcim:device_list' 

4831 

4832 

4833class DeviceBulkAddPowerPortView(generic.BulkComponentCreateView): 

4834 parent_model = Device 

4835 parent_field = 'device' 

4836 form = forms.PowerPortBulkCreateForm 

4837 queryset = PowerPort.objects.all() 

4838 model_form = forms.PowerPortForm 

4839 filterset = filtersets.DeviceFilterSet 

4840 table = tables.DeviceTable 

4841 default_return_url = 'dcim:device_list' 

4842 

4843 

4844class DeviceBulkAddPowerOutletView(generic.BulkComponentCreateView): 

4845 parent_model = Device 

4846 parent_field = 'device' 

4847 form = forms.PowerOutletBulkCreateForm 

4848 queryset = PowerOutlet.objects.all() 

4849 model_form = forms.PowerOutletForm 

4850 filterset = filtersets.DeviceFilterSet 

4851 table = tables.DeviceTable 

4852 default_return_url = 'dcim:device_list' 

4853 

4854 

4855class DeviceBulkAddCoolingIntakeView(generic.BulkComponentCreateView): 

4856 parent_model = Device 

4857 parent_field = 'device' 

4858 form = forms.CoolingIntakeBulkCreateForm 

4859 queryset = CoolingIntake.objects.all() 

4860 model_form = forms.CoolingIntakeForm 

4861 filterset = filtersets.DeviceFilterSet 

4862 table = tables.DeviceTable 

4863 default_return_url = 'dcim:device_list' 

4864 

4865 

4866class DeviceBulkAddCoolingOutflowView(generic.BulkComponentCreateView): 

4867 parent_model = Device 

4868 parent_field = 'device' 

4869 form = forms.CoolingOutflowBulkCreateForm 

4870 queryset = CoolingOutflow.objects.all() 

4871 model_form = forms.CoolingOutflowForm 

4872 filterset = filtersets.DeviceFilterSet 

4873 table = tables.DeviceTable 

4874 default_return_url = 'dcim:device_list' 

4875 

4876 

4877class DeviceBulkAddInterfaceView(generic.BulkComponentCreateView): 

4878 parent_model = Device 

4879 parent_field = 'device' 

4880 form = forms.InterfaceBulkCreateForm 

4881 queryset = Interface.objects.all() 

4882 model_form = forms.InterfaceForm 

4883 filterset = filtersets.DeviceFilterSet 

4884 table = tables.DeviceTable 

4885 default_return_url = 'dcim:device_list' 

4886 

4887 

4888class DeviceBulkAddRearPortView(generic.BulkComponentCreateView): 

4889 parent_model = Device 

4890 parent_field = 'device' 

4891 form = forms.RearPortBulkCreateForm 

4892 queryset = RearPort.objects.all() 

4893 model_form = forms.RearPortForm 

4894 filterset = filtersets.DeviceFilterSet 

4895 table = tables.DeviceTable 

4896 default_return_url = 'dcim:device_list' 

4897 

4898 

4899class DeviceBulkAddModuleBayView(generic.BulkComponentCreateView): 

4900 parent_model = Device 

4901 parent_field = 'device' 

4902 form = forms.ModuleBayBulkCreateForm 

4903 queryset = ModuleBay.objects.all() 

4904 model_form = forms.ModuleBayForm 

4905 filterset = filtersets.DeviceFilterSet 

4906 table = tables.DeviceTable 

4907 default_return_url = 'dcim:device_list' 

4908 

4909 

4910class DeviceBulkAddDeviceBayView(generic.BulkComponentCreateView): 

4911 parent_model = Device 

4912 parent_field = 'device' 

4913 form = forms.DeviceBayBulkCreateForm 

4914 queryset = DeviceBay.objects.all() 

4915 model_form = forms.DeviceBayForm 

4916 filterset = filtersets.DeviceFilterSet 

4917 table = tables.DeviceTable 

4918 default_return_url = 'dcim:device_list' 

4919 

4920 

4921class DeviceBulkAddInventoryItemView(generic.BulkComponentCreateView): 

4922 parent_model = Device 

4923 parent_field = 'device' 

4924 form = forms.InventoryItemBulkCreateForm 

4925 queryset = InventoryItem.objects.all() 

4926 model_form = forms.InventoryItemForm 

4927 filterset = filtersets.DeviceFilterSet 

4928 table = tables.DeviceTable 

4929 default_return_url = 'dcim:device_list' 

4930 

4931 

4932# 

4933# Cable bundles 

4934# 

4935 

4936@register_model_view(CableBundle, 'list', path='', detail=False) 

4937class CableBundleListView(generic.ObjectListView): 

4938 queryset = CableBundle.objects.annotate( 

4939 cable_count=count_related(Cable, 'bundle') 

4940 ) 

4941 filterset = filtersets.CableBundleFilterSet 

4942 filterset_form = forms.CableBundleFilterForm 

4943 table = tables.CableBundleTable 

4944 

4945 

4946@register_model_view(CableBundle) 

4947class CableBundleView(generic.ObjectView): 

4948 queryset = CableBundle.objects.all() 

4949 

4950 def get_extra_context(self, request, instance): 

4951 cables_table = tables.CableTable( 

4952 instance.cables.all().prefetch_related( 

4953 'terminations__termination', 'terminations___device', 'terminations___rack', 'terminations___location', 

4954 'terminations___site', 

4955 ), 

4956 orderable=False, 

4957 ) 

4958 cables_table.configure(request) 

4959 

4960 return { 

4961 'cables_table': cables_table, 

4962 } 

4963 

4964 

4965@register_model_view(CableBundle, 'add', detail=False) 

4966@register_model_view(CableBundle, 'edit') 

4967class CableBundleEditView(generic.ObjectEditView): 

4968 queryset = CableBundle.objects.all() 

4969 form = forms.CableBundleForm 

4970 

4971 

4972@register_model_view(CableBundle, 'delete') 

4973class CableBundleDeleteView(generic.ObjectDeleteView): 

4974 queryset = CableBundle.objects.all() 

4975 

4976 

4977@register_model_view(CableBundle, 'bulk_import', path='import', detail=False) 

4978class CableBundleBulkImportView(generic.BulkImportView): 

4979 queryset = CableBundle.objects.all() 

4980 model_form = forms.CableBundleImportForm 

4981 

4982 

4983@register_model_view(CableBundle, 'bulk_edit', path='edit', detail=False) 

4984class CableBundleBulkEditView(generic.BulkEditView): 

4985 queryset = CableBundle.objects.all() 

4986 filterset = filtersets.CableBundleFilterSet 

4987 table = tables.CableBundleTable 

4988 form = forms.CableBundleBulkEditForm 

4989 

4990 

4991@register_model_view(CableBundle, 'bulk_delete', path='delete', detail=False) 

4992class CableBundleBulkDeleteView(generic.BulkDeleteView): 

4993 queryset = CableBundle.objects.all() 

4994 filterset = filtersets.CableBundleFilterSet 

4995 table = tables.CableBundleTable 

4996 

4997 

4998# 

4999# Cables 

5000# 

5001 

5002@register_model_view(Cable, 'list', path='', detail=False) 

5003class CableListView(generic.ObjectListView): 

5004 queryset = Cable.objects.prefetch_related( 

5005 'terminations__termination', 'terminations___device', 'terminations___rack', 'terminations___location', 

5006 'terminations___site', 

5007 ) 

5008 filterset = filtersets.CableFilterSet 

5009 filterset_form = forms.CableFilterForm 

5010 table = tables.CableTable 

5011 

5012 

5013@register_model_view(Cable) 

5014class CableView(generic.ObjectView): 

5015 queryset = Cable.objects.all() 

5016 template_name = 'generic/object.html' 

5017 layout = layout.SimpleLayout( 

5018 left_panels=[ 

5019 panels.CablePanel(), 

5020 CustomFieldsPanel(), 

5021 TagsPanel(), 

5022 CommentsPanel(), 

5023 ], 

5024 right_panels=[ 

5025 TemplatePanel( 

5026 title=_('Termination A'), 

5027 template_name='dcim/panels/cable_termination_a.html', 

5028 ), 

5029 TemplatePanel( 

5030 title=_('Termination B'), 

5031 template_name='dcim/panels/cable_termination_b.html', 

5032 ), 

5033 ], 

5034 ) 

5035 

5036 

5037@register_model_view(Cable, 'add', detail=False) 

5038@register_model_view(Cable, 'edit') 

5039class CableEditView(generic.ObjectEditView): 

5040 queryset = Cable.objects.all() 

5041 template_name = 'dcim/cable_edit.html' 

5042 htmx_template_name = 'dcim/htmx/cable_edit.html' 

5043 

5044 def alter_object(self, obj, request, url_args, url_kwargs): 

5045 """ 

5046 Hack into alter_object() to set the form class when editing an existing Cable, since ObjectEditView 

5047 doesn't currently provide a hook for dynamic class resolution. 

5048 """ 

5049 a_terminations_type = CABLE_TERMINATION_TYPES.get( 

5050 request.POST.get('a_terminations_type') or request.GET.get('a_terminations_type') 

5051 ) 

5052 b_terminations_type = CABLE_TERMINATION_TYPES.get( 

5053 request.POST.get('b_terminations_type') or request.GET.get('b_terminations_type') 

5054 ) 

5055 

5056 if obj.pk: 

5057 if not a_terminations_type and (termination_a := obj.terminations.filter(cable_end='A').first()): 

5058 a_terminations_type = termination_a.termination._meta.model 

5059 if not b_terminations_type and (termination_b := obj.terminations.filter(cable_end='B').first()): 

5060 b_terminations_type = termination_b.termination._meta.model 

5061 

5062 self.form = forms.get_cable_form(a_terminations_type, b_terminations_type) 

5063 

5064 return super().alter_object(obj, request, url_args, url_kwargs) 

5065 

5066 

5067@register_model_view(Cable, 'delete') 

5068class CableDeleteView(generic.ObjectDeleteView): 

5069 queryset = Cable.objects.all() 

5070 

5071 

5072@register_model_view(Cable, 'bulk_import', path='import', detail=False) 

5073class CableBulkImportView(generic.BulkImportView): 

5074 queryset = Cable.objects.all() 

5075 model_form = forms.CableImportForm 

5076 

5077 

5078@register_model_view(Cable, 'bulk_edit', path='edit', detail=False) 

5079class CableBulkEditView(generic.BulkEditView): 

5080 queryset = Cable.objects.prefetch_related( 

5081 'terminations__termination', 'terminations___device', 'terminations___rack', 'terminations___location', 

5082 'terminations___site', 

5083 ) 

5084 filterset = filtersets.CableFilterSet 

5085 table = tables.CableTable 

5086 form = forms.CableBulkEditForm 

5087 

5088 

5089@register_model_view(Cable, 'bulk_rename', path='rename', detail=False) 

5090class CableBulkRenameView(generic.BulkRenameView): 

5091 queryset = Cable.objects.all() 

5092 field_name = 'label' 

5093 filterset = filtersets.CableFilterSet 

5094 

5095 

5096@register_model_view(Cable, 'bulk_delete', path='delete', detail=False) 

5097class CableBulkDeleteView(generic.BulkDeleteView): 

5098 queryset = Cable.objects.prefetch_related( 

5099 'terminations__termination', 'terminations___device', 'terminations___rack', 'terminations___location', 

5100 'terminations___site', 

5101 ) 

5102 filterset = filtersets.CableFilterSet 

5103 table = tables.CableTable 

5104 

5105 

5106# 

5107# Connections 

5108# 

5109 

5110class ConsoleConnectionsListView(generic.ObjectListView): 

5111 queryset = ConsolePort.objects.filter(_path__is_complete=True) 

5112 filterset = filtersets.ConsoleConnectionFilterSet 

5113 filterset_form = forms.ConsoleConnectionFilterForm 

5114 table = tables.ConsoleConnectionTable 

5115 template_name = 'dcim/connections_list.html' 

5116 actions = (BulkExport,) 

5117 

5118 def get_extra_context(self, request): 

5119 return { 

5120 'title': 'Console Connections' 

5121 } 

5122 

5123 

5124class PowerConnectionsListView(generic.ObjectListView): 

5125 queryset = PowerPort.objects.filter(_path__is_complete=True) 

5126 filterset = filtersets.PowerConnectionFilterSet 

5127 filterset_form = forms.PowerConnectionFilterForm 

5128 table = tables.PowerConnectionTable 

5129 template_name = 'dcim/connections_list.html' 

5130 actions = (BulkExport,) 

5131 

5132 def get_extra_context(self, request): 

5133 return { 

5134 'title': 'Power Connections' 

5135 } 

5136 

5137 

5138class InterfaceConnectionsListView(generic.ObjectListView): 

5139 queryset = Interface.objects.filter(_path__is_complete=True) 

5140 filterset = filtersets.InterfaceConnectionFilterSet 

5141 filterset_form = forms.InterfaceConnectionFilterForm 

5142 table = tables.InterfaceConnectionTable 

5143 template_name = 'dcim/connections_list.html' 

5144 actions = (BulkExport,) 

5145 

5146 def get_extra_context(self, request): 

5147 return { 

5148 'title': 'Interface Connections' 

5149 } 

5150 

5151 

5152# 

5153# Virtual chassis 

5154# 

5155 

5156@register_model_view(VirtualChassis, 'list', path='', detail=False) 

5157class VirtualChassisListView(generic.ObjectListView): 

5158 queryset = VirtualChassis.objects.all() 

5159 table = tables.VirtualChassisTable 

5160 filterset = filtersets.VirtualChassisFilterSet 

5161 filterset_form = forms.VirtualChassisFilterForm 

5162 

5163 

5164@register_model_view(VirtualChassis) 

5165class VirtualChassisView(generic.ObjectView): 

5166 queryset = VirtualChassis.objects.all() 

5167 template_name = 'generic/object.html' 

5168 layout = layout.SimpleLayout( 

5169 left_panels=[ 

5170 panels.VirtualChassisPanel(), 

5171 TagsPanel(), 

5172 CustomFieldsPanel(), 

5173 ], 

5174 right_panels=[ 

5175 panels.VirtualChassisMembersPanel(), 

5176 CommentsPanel(), 

5177 ], 

5178 ) 

5179 

5180 def get_extra_context(self, request, instance): 

5181 vc_members = Device.objects.restrict(request.user).filter(virtual_chassis=instance).order_by('vc_position') 

5182 return { 

5183 'virtual_chassis': instance, 

5184 'vc_members': vc_members, 

5185 } 

5186 

5187 

5188@register_model_view(VirtualChassis, 'add', detail=False) 

5189class VirtualChassisCreateView(generic.ObjectEditView): 

5190 queryset = VirtualChassis.objects.all() 

5191 form = forms.VirtualChassisCreateForm 

5192 

5193 

5194@register_model_view(VirtualChassis, 'edit') 

5195class VirtualChassisEditView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View): 

5196 queryset = VirtualChassis.objects.all() 

5197 

5198 def get_required_permission(self): 

5199 return 'dcim.change_virtualchassis' 

5200 

5201 def get(self, request, pk): 

5202 

5203 virtual_chassis = get_object_or_404(self.queryset, pk=pk) 

5204 VCMemberFormSet = modelformset_factory( 

5205 model=Device, 

5206 form=forms.DeviceVCMembershipForm, 

5207 formset=forms.BaseVCMemberFormSet, 

5208 extra=0 

5209 ) 

5210 members_queryset = virtual_chassis.members.prefetch_related('rack').order_by('vc_position') 

5211 

5212 vc_form = forms.VirtualChassisForm(instance=virtual_chassis) 

5213 vc_form.fields['master'].queryset = members_queryset 

5214 formset = VCMemberFormSet(queryset=members_queryset) 

5215 

5216 return render(request, 'dcim/virtualchassis_edit.html', { 

5217 'object': virtual_chassis, 

5218 'vc_form': vc_form, 

5219 'formset': formset, 

5220 'return_url': self.get_return_url(request, virtual_chassis), 

5221 }) 

5222 

5223 def post(self, request, pk): 

5224 

5225 virtual_chassis = get_object_or_404(self.queryset, pk=pk) 

5226 virtual_chassis.snapshot() 

5227 VCMemberFormSet = modelformset_factory( 

5228 model=Device, 

5229 form=forms.DeviceVCMembershipForm, 

5230 formset=forms.BaseVCMemberFormSet, 

5231 extra=0 

5232 ) 

5233 members_queryset = virtual_chassis.members.prefetch_related('rack').order_by('vc_position') 

5234 

5235 vc_form = forms.VirtualChassisForm(request.POST, instance=virtual_chassis) 

5236 vc_form.fields['master'].queryset = members_queryset 

5237 formset = VCMemberFormSet(request.POST, queryset=members_queryset) 

5238 

5239 if vc_form.is_valid() and formset.is_valid(): 

5240 virtual_chassis._changelog_message = vc_form.cleaned_data.pop('changelog_message', '') 

5241 

5242 with transaction.atomic(using=router.db_for_write(Device)): 

5243 

5244 # Save the VirtualChassis 

5245 vc_form.save() 

5246 

5247 # Nullify the vc_position of each member first to allow reordering without raising an IntegrityError on 

5248 # duplicate positions. Then save each member instance. 

5249 members = formset.save(commit=False) 

5250 devices = Device.objects.filter(pk__in=[m.pk for m in members]) 

5251 for device in devices: 

5252 device.snapshot() 

5253 device.vc_position = None 

5254 device.save() 

5255 for member in members: 

5256 member.save() 

5257 

5258 return redirect(virtual_chassis.get_absolute_url()) 

5259 

5260 return render(request, 'dcim/virtualchassis_edit.html', { 

5261 'vc_form': vc_form, 

5262 'formset': formset, 

5263 'return_url': self.get_return_url(request, virtual_chassis), 

5264 }) 

5265 

5266 

5267@register_model_view(VirtualChassis, 'delete') 

5268class VirtualChassisDeleteView(generic.ObjectDeleteView): 

5269 queryset = VirtualChassis.objects.all() 

5270 

5271 

5272@register_model_view(VirtualChassis, 'add_member', path='add-member') 

5273class VirtualChassisAddMemberView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View): 

5274 queryset = VirtualChassis.objects.all() 

5275 

5276 def get_required_permission(self): 

5277 return 'dcim.change_virtualchassis' 

5278 

5279 def get(self, request, pk): 

5280 virtual_chassis = get_object_or_404(self.queryset, pk=pk) 

5281 initial_data = {k: request.GET[k] for k in request.GET} 

5282 member_select_form = forms.VCMemberSelectForm(initial=initial_data) 

5283 membership_form = forms.DeviceVCMembershipForm(initial=initial_data) 

5284 

5285 return render(request, 'dcim/virtualchassis_add_member.html', { 

5286 'virtual_chassis': virtual_chassis, 

5287 'member_select_form': member_select_form, 

5288 'membership_form': membership_form, 

5289 'return_url': self.get_return_url(request, virtual_chassis), 

5290 }) 

5291 

5292 def post(self, request, pk): 

5293 virtual_chassis = get_object_or_404(self.queryset, pk=pk) 

5294 member_select_form = forms.VCMemberSelectForm(request.POST) 

5295 

5296 if member_select_form.is_valid(): 

5297 device = member_select_form.cleaned_data['device'] 

5298 device.snapshot() 

5299 device.virtual_chassis = virtual_chassis 

5300 data = { 

5301 'vc_position': request.POST['vc_position'], 

5302 'vc_priority': request.POST['vc_priority'], 

5303 } 

5304 membership_form = forms.DeviceVCMembershipForm(data=data, validate_vc_position=True, instance=device) 

5305 

5306 if membership_form.is_valid(): 

5307 membership_form.save() 

5308 messages.success(request, mark_safe( 

5309 _('Added member <a href="{url}">{device}</a>').format( 

5310 url=device.get_absolute_url(), device=escape(device) 

5311 ) 

5312 )) 

5313 

5314 if '_addanother' in request.POST and safe_for_redirect(request.get_full_path()): 

5315 return redirect(request.get_full_path()) 

5316 return redirect(self.get_return_url(request, device)) 

5317 

5318 else: 

5319 membership_form = forms.DeviceVCMembershipForm(data=request.POST) 

5320 

5321 return render(request, 'dcim/virtualchassis_add_member.html', { 

5322 'virtual_chassis': virtual_chassis, 

5323 'member_select_form': member_select_form, 

5324 'membership_form': membership_form, 

5325 'return_url': self.get_return_url(request, virtual_chassis), 

5326 }) 

5327 

5328 

5329class VirtualChassisRemoveMemberView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View): 

5330 queryset = Device.objects.all() 

5331 

5332 def get_required_permission(self): 

5333 return 'dcim.change_device' 

5334 

5335 def get(self, request, pk): 

5336 device = get_object_or_404(self.queryset, pk=pk, virtual_chassis__isnull=False) 

5337 form = ConfirmationForm(initial=request.GET) 

5338 

5339 return render(request, 'dcim/virtualchassis_remove_member.html', { 

5340 'device': device, 

5341 'form': form, 

5342 'return_url': self.get_return_url(request, device), 

5343 }) 

5344 

5345 def post(self, request, pk): 

5346 device = get_object_or_404(self.queryset, pk=pk, virtual_chassis__isnull=False) 

5347 form = ConfirmationForm(request.POST) 

5348 

5349 # Protect master device from being removed 

5350 virtual_chassis = VirtualChassis.objects.filter(master=device).first() 

5351 if virtual_chassis is not None: 

5352 messages.error( 

5353 request, 

5354 _('Unable to remove master device {device} from the virtual chassis.').format(device=device) 

5355 ) 

5356 return redirect(device.get_absolute_url()) 

5357 

5358 if form.is_valid(): 

5359 device.snapshot() 

5360 device.virtual_chassis = None 

5361 device.vc_position = None 

5362 device.vc_priority = None 

5363 device.save() 

5364 

5365 msg = _('Removed {device} from virtual chassis {chassis}').format( 

5366 device=device, 

5367 chassis=device.virtual_chassis 

5368 ) 

5369 messages.success(request, msg) 

5370 

5371 return redirect(self.get_return_url(request, device)) 

5372 

5373 return render(request, 'dcim/virtualchassis_remove_member.html', { 

5374 'device': device, 

5375 'form': form, 

5376 'return_url': self.get_return_url(request, device), 

5377 }) 

5378 

5379 

5380@register_model_view(VirtualChassis, 'bulk_import', path='import', detail=False) 

5381class VirtualChassisBulkImportView(generic.BulkImportView): 

5382 queryset = VirtualChassis.objects.all() 

5383 model_form = forms.VirtualChassisImportForm 

5384 

5385 

5386@register_model_view(VirtualChassis, 'bulk_edit', path='edit', detail=False) 

5387class VirtualChassisBulkEditView(generic.BulkEditView): 

5388 queryset = VirtualChassis.objects.all() 

5389 filterset = filtersets.VirtualChassisFilterSet 

5390 table = tables.VirtualChassisTable 

5391 form = forms.VirtualChassisBulkEditForm 

5392 

5393 

5394@register_model_view(VirtualChassis, 'bulk_rename', path='rename', detail=False) 

5395class VirtualChassisBulkRenameView(generic.BulkRenameView): 

5396 queryset = VirtualChassis.objects.all() 

5397 filterset = filtersets.VirtualChassisFilterSet 

5398 

5399 

5400@register_model_view(VirtualChassis, 'bulk_delete', path='delete', detail=False) 

5401class VirtualChassisBulkDeleteView(generic.BulkDeleteView): 

5402 queryset = VirtualChassis.objects.all() 

5403 filterset = filtersets.VirtualChassisFilterSet 

5404 table = tables.VirtualChassisTable 

5405 

5406 

5407# 

5408# Power panels 

5409# 

5410 

5411@register_model_view(PowerPanel, 'list', path='', detail=False) 

5412class PowerPanelListView(generic.ObjectListView): 

5413 queryset = PowerPanel.objects.annotate( 

5414 powerfeed_count=count_related(PowerFeed, 'power_panel') 

5415 ) 

5416 filterset = filtersets.PowerPanelFilterSet 

5417 filterset_form = forms.PowerPanelFilterForm 

5418 table = tables.PowerPanelTable 

5419 

5420 

5421@register_model_view(PowerPanel) 

5422class PowerPanelView(GetRelatedModelsMixin, generic.ObjectView): 

5423 template_name = 'generic/object.html' 

5424 queryset = PowerPanel.objects.all() 

5425 layout = layout.SimpleLayout( 

5426 breadcrumbs=[ 

5427 Breadcrumb('site', url=filtered_list_url('dcim:powerpanel_list', 'site_id')), 

5428 Breadcrumb( 

5429 lambda o: o.location.get_ancestors() if o.location else [], 

5430 url=filtered_list_url('dcim:powerpanel_list', 'location_id'), 

5431 ), 

5432 Breadcrumb('location', url=filtered_list_url('dcim:powerpanel_list', 'location_id')), 

5433 ], 

5434 left_panels=[ 

5435 panels.PowerPanelPanel(), 

5436 TagsPanel(), 

5437 CommentsPanel(), 

5438 ], 

5439 right_panels=[ 

5440 RelatedObjectsPanel(), 

5441 CustomFieldsPanel(), 

5442 ImageAttachmentsPanel(), 

5443 ], 

5444 bottom_panels=[ 

5445 ObjectsTablePanel( 

5446 model='dcim.PowerFeed', 

5447 filters={'power_panel_id': lambda ctx: ctx['object'].pk}, 

5448 actions=[ 

5449 actions.AddObject('dcim.PowerFeed', url_params={'power_panel': lambda ctx: ctx['object'].pk}), 

5450 ], 

5451 ), 

5452 ], 

5453 ) 

5454 

5455 def get_extra_context(self, request, instance): 

5456 return { 

5457 'related_models': self.get_related_models(request, instance), 

5458 } 

5459 

5460 

5461@register_model_view(PowerPanel, 'add', detail=False) 

5462@register_model_view(PowerPanel, 'edit') 

5463class PowerPanelEditView(generic.ObjectEditView): 

5464 queryset = PowerPanel.objects.all() 

5465 form = forms.PowerPanelForm 

5466 

5467 

5468@register_model_view(PowerPanel, 'delete') 

5469class PowerPanelDeleteView(generic.ObjectDeleteView): 

5470 queryset = PowerPanel.objects.all() 

5471 

5472 

5473@register_model_view(PowerPanel, 'bulk_import', path='import', detail=False) 

5474class PowerPanelBulkImportView(generic.BulkImportView): 

5475 queryset = PowerPanel.objects.all() 

5476 model_form = forms.PowerPanelImportForm 

5477 

5478 

5479@register_model_view(PowerPanel, 'bulk_edit', path='edit', detail=False) 

5480class PowerPanelBulkEditView(generic.BulkEditView): 

5481 queryset = PowerPanel.objects.all() 

5482 filterset = filtersets.PowerPanelFilterSet 

5483 table = tables.PowerPanelTable 

5484 form = forms.PowerPanelBulkEditForm 

5485 

5486 

5487@register_model_view(PowerPanel, 'bulk_rename', path='rename', detail=False) 

5488class PowerPanelBulkRenameView(generic.BulkRenameView): 

5489 queryset = PowerPanel.objects.all() 

5490 filterset = filtersets.PowerPanelFilterSet 

5491 

5492 

5493@register_model_view(PowerPanel, 'bulk_delete', path='delete', detail=False) 

5494class PowerPanelBulkDeleteView(generic.BulkDeleteView): 

5495 queryset = PowerPanel.objects.annotate( 

5496 powerfeed_count=count_related(PowerFeed, 'power_panel') 

5497 ) 

5498 filterset = filtersets.PowerPanelFilterSet 

5499 table = tables.PowerPanelTable 

5500 

5501 

5502# 

5503# Power feeds 

5504# 

5505 

5506@register_model_view(PowerFeed, 'list', path='', detail=False) 

5507class PowerFeedListView(generic.ObjectListView): 

5508 queryset = PowerFeed.objects.all() 

5509 filterset = filtersets.PowerFeedFilterSet 

5510 filterset_form = forms.PowerFeedFilterForm 

5511 table = tables.PowerFeedTable 

5512 

5513 

5514@register_model_view(PowerFeed) 

5515class PowerFeedView(generic.ObjectView): 

5516 template_name = 'generic/object.html' 

5517 queryset = PowerFeed.objects.all() 

5518 layout = layout.SimpleLayout( 

5519 breadcrumbs=[ 

5520 Breadcrumb('power_panel.site', url=filtered_list_url('dcim:powerfeed_list', 'site_id')), 

5521 Breadcrumb('power_panel', url=filtered_list_url('dcim:powerfeed_list', 'power_panel_id')), 

5522 Breadcrumb('rack', url=filtered_list_url('dcim:powerfeed_list', 'rack_id')), 

5523 ], 

5524 left_panels=[ 

5525 panels.PowerFeedPanel(), 

5526 panels.PowerFeedElectricalPanel(), 

5527 CustomFieldsPanel(), 

5528 TagsPanel(), 

5529 ], 

5530 right_panels=[ 

5531 panels.ConnectionPanel( 

5532 trace_url_name='dcim:powerfeed_trace', 

5533 connect_options=[ 

5534 {'a_type': 'dcim.powerfeed', 'b_type': 'dcim.powerport', 'label': _('Power Port')}, 

5535 ], 

5536 ), 

5537 CommentsPanel(), 

5538 ], 

5539 ) 

5540 

5541 

5542@register_model_view(PowerFeed, 'add', detail=False) 

5543@register_model_view(PowerFeed, 'edit') 

5544class PowerFeedEditView(generic.ObjectEditView): 

5545 queryset = PowerFeed.objects.all() 

5546 form = forms.PowerFeedForm 

5547 

5548 

5549@register_model_view(PowerFeed, 'delete') 

5550class PowerFeedDeleteView(generic.ObjectDeleteView): 

5551 queryset = PowerFeed.objects.all() 

5552 

5553 

5554@register_model_view(PowerFeed, 'bulk_import', path='import', detail=False) 

5555class PowerFeedBulkImportView(generic.BulkImportView): 

5556 queryset = PowerFeed.objects.all() 

5557 model_form = forms.PowerFeedImportForm 

5558 

5559 

5560@register_model_view(PowerFeed, 'bulk_edit', path='edit', detail=False) 

5561class PowerFeedBulkEditView(generic.BulkEditView): 

5562 queryset = PowerFeed.objects.all() 

5563 filterset = filtersets.PowerFeedFilterSet 

5564 table = tables.PowerFeedTable 

5565 form = forms.PowerFeedBulkEditForm 

5566 

5567 

5568@register_model_view(PowerFeed, 'bulk_rename', path='rename', detail=False) 

5569class PowerFeedBulkRenameView(generic.BulkRenameView): 

5570 queryset = PowerFeed.objects.all() 

5571 filterset = filtersets.PowerFeedFilterSet 

5572 

5573 

5574@register_model_view(PowerFeed, 'bulk_disconnect', path='disconnect', detail=False) 

5575class PowerFeedBulkDisconnectView(BulkDisconnectView): 

5576 queryset = PowerFeed.objects.all() 

5577 

5578 

5579@register_model_view(PowerFeed, 'bulk_delete', path='delete', detail=False) 

5580class PowerFeedBulkDeleteView(generic.BulkDeleteView): 

5581 queryset = PowerFeed.objects.all() 

5582 filterset = filtersets.PowerFeedFilterSet 

5583 table = tables.PowerFeedTable 

5584 

5585 

5586# Trace view 

5587register_model_view(PowerFeed, 'trace', kwargs={'model': PowerFeed})(PathTraceView) 

5588 

5589 

5590# 

5591# Cooling sources 

5592# 

5593 

5594@register_model_view(CoolingSource, 'list', path='', detail=False) 

5595class CoolingSourceListView(generic.ObjectListView): 

5596 queryset = CoolingSource.objects.annotate( 

5597 coolingfeed_count=count_related(CoolingFeed, 'cooling_source') 

5598 ) 

5599 filterset = filtersets.CoolingSourceFilterSet 

5600 filterset_form = forms.CoolingSourceFilterForm 

5601 table = tables.CoolingSourceTable 

5602 

5603 

5604@register_model_view(CoolingSource) 

5605class CoolingSourceView(GetRelatedModelsMixin, generic.ObjectView): 

5606 queryset = CoolingSource.objects.all() 

5607 template_name = 'generic/object.html' 

5608 layout = layout.SimpleLayout( 

5609 breadcrumbs=[ 

5610 Breadcrumb('site', url=filtered_list_url('dcim:coolingsource_list', 'site_id')), 

5611 Breadcrumb( 

5612 lambda o: o.location.get_ancestors() if o.location else [], 

5613 url=filtered_list_url('dcim:coolingsource_list', 'location_id'), 

5614 ), 

5615 Breadcrumb('location', url=filtered_list_url('dcim:coolingsource_list', 'location_id')), 

5616 ], 

5617 left_panels=[ 

5618 panels.CoolingSourcePanel(), 

5619 TagsPanel(), 

5620 CommentsPanel(), 

5621 ], 

5622 right_panels=[ 

5623 RelatedObjectsPanel(), 

5624 CustomFieldsPanel(), 

5625 ImageAttachmentsPanel(), 

5626 ], 

5627 bottom_panels=[ 

5628 ObjectsTablePanel( 

5629 model='dcim.CoolingFeed', 

5630 filters={'cooling_source_id': lambda ctx: ctx['object'].pk}, 

5631 actions=[ 

5632 actions.AddObject('dcim.CoolingFeed', url_params={'cooling_source': lambda ctx: ctx['object'].pk}), 

5633 ], 

5634 ), 

5635 ], 

5636 ) 

5637 

5638 def get_extra_context(self, request, instance): 

5639 return { 

5640 'related_models': self.get_related_models(request, instance), 

5641 } 

5642 

5643 

5644@register_model_view(CoolingSource, 'add', detail=False) 

5645@register_model_view(CoolingSource, 'edit') 

5646class CoolingSourceEditView(generic.ObjectEditView): 

5647 queryset = CoolingSource.objects.all() 

5648 form = forms.CoolingSourceForm 

5649 

5650 

5651@register_model_view(CoolingSource, 'delete') 

5652class CoolingSourceDeleteView(generic.ObjectDeleteView): 

5653 queryset = CoolingSource.objects.all() 

5654 

5655 

5656@register_model_view(CoolingSource, 'bulk_import', path='import', detail=False) 

5657class CoolingSourceBulkImportView(generic.BulkImportView): 

5658 queryset = CoolingSource.objects.all() 

5659 model_form = forms.CoolingSourceImportForm 

5660 

5661 

5662@register_model_view(CoolingSource, 'bulk_edit', path='edit', detail=False) 

5663class CoolingSourceBulkEditView(generic.BulkEditView): 

5664 queryset = CoolingSource.objects.all() 

5665 filterset = filtersets.CoolingSourceFilterSet 

5666 table = tables.CoolingSourceTable 

5667 form = forms.CoolingSourceBulkEditForm 

5668 

5669 

5670@register_model_view(CoolingSource, 'bulk_rename', path='rename', detail=False) 

5671class CoolingSourceBulkRenameView(generic.BulkRenameView): 

5672 queryset = CoolingSource.objects.all() 

5673 filterset = filtersets.CoolingSourceFilterSet 

5674 

5675 

5676@register_model_view(CoolingSource, 'bulk_delete', path='delete', detail=False) 

5677class CoolingSourceBulkDeleteView(generic.BulkDeleteView): 

5678 queryset = CoolingSource.objects.annotate( 

5679 coolingfeed_count=count_related(CoolingFeed, 'cooling_source') 

5680 ) 

5681 filterset = filtersets.CoolingSourceFilterSet 

5682 table = tables.CoolingSourceTable 

5683 

5684 

5685# 

5686# Cooling feeds 

5687# 

5688 

5689@register_model_view(CoolingFeed, 'list', path='', detail=False) 

5690class CoolingFeedListView(generic.ObjectListView): 

5691 queryset = CoolingFeed.objects.all() 

5692 filterset = filtersets.CoolingFeedFilterSet 

5693 filterset_form = forms.CoolingFeedFilterForm 

5694 table = tables.CoolingFeedTable 

5695 

5696 

5697@register_model_view(CoolingFeed) 

5698class CoolingFeedView(generic.ObjectView): 

5699 queryset = CoolingFeed.objects.all() 

5700 template_name = 'generic/object.html' 

5701 layout = layout.SimpleLayout( 

5702 breadcrumbs=[ 

5703 Breadcrumb('cooling_source.site', url=filtered_list_url('dcim:coolingfeed_list', 'site_id')), 

5704 Breadcrumb('cooling_source', url=filtered_list_url('dcim:coolingfeed_list', 'cooling_source_id')), 

5705 Breadcrumb('rack', url=filtered_list_url('dcim:coolingfeed_list', 'rack_id')), 

5706 ], 

5707 left_panels=[ 

5708 panels.CoolingFeedPanel(), 

5709 panels.CoolingFeedCharacteristicsPanel(), 

5710 CustomFieldsPanel(), 

5711 TagsPanel(), 

5712 ], 

5713 right_panels=[ 

5714 CommentsPanel(), 

5715 ], 

5716 ) 

5717 

5718 

5719@register_model_view(CoolingFeed, 'add', detail=False) 

5720@register_model_view(CoolingFeed, 'edit') 

5721class CoolingFeedEditView(generic.ObjectEditView): 

5722 queryset = CoolingFeed.objects.all() 

5723 form = forms.CoolingFeedForm 

5724 

5725 

5726@register_model_view(CoolingFeed, 'delete') 

5727class CoolingFeedDeleteView(generic.ObjectDeleteView): 

5728 queryset = CoolingFeed.objects.all() 

5729 

5730 

5731@register_model_view(CoolingFeed, 'bulk_import', path='import', detail=False) 

5732class CoolingFeedBulkImportView(generic.BulkImportView): 

5733 queryset = CoolingFeed.objects.all() 

5734 model_form = forms.CoolingFeedImportForm 

5735 

5736 

5737@register_model_view(CoolingFeed, 'bulk_edit', path='edit', detail=False) 

5738class CoolingFeedBulkEditView(generic.BulkEditView): 

5739 queryset = CoolingFeed.objects.all() 

5740 filterset = filtersets.CoolingFeedFilterSet 

5741 table = tables.CoolingFeedTable 

5742 form = forms.CoolingFeedBulkEditForm 

5743 

5744 

5745@register_model_view(CoolingFeed, 'bulk_rename', path='rename', detail=False) 

5746class CoolingFeedBulkRenameView(generic.BulkRenameView): 

5747 queryset = CoolingFeed.objects.all() 

5748 filterset = filtersets.CoolingFeedFilterSet 

5749 

5750 

5751@register_model_view(CoolingFeed, 'bulk_delete', path='delete', detail=False) 

5752class CoolingFeedBulkDeleteView(generic.BulkDeleteView): 

5753 queryset = CoolingFeed.objects.all() 

5754 filterset = filtersets.CoolingFeedFilterSet 

5755 table = tables.CoolingFeedTable 

5756 

5757 

5758# 

5759# Virtual device contexts 

5760# 

5761 

5762@register_model_view(VirtualDeviceContext, 'list', path='', detail=False) 

5763class VirtualDeviceContextListView(generic.ObjectListView): 

5764 queryset = VirtualDeviceContext.objects.annotate( 

5765 interface_count=count_related(Interface, 'vdcs'), 

5766 ) 

5767 filterset = filtersets.VirtualDeviceContextFilterSet 

5768 filterset_form = forms.VirtualDeviceContextFilterForm 

5769 table = tables.VirtualDeviceContextTable 

5770 actions = (AddObject, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete) 

5771 

5772 

5773@register_model_view(VirtualDeviceContext) 

5774class VirtualDeviceContextView(GetRelatedModelsMixin, generic.ObjectView): 

5775 queryset = VirtualDeviceContext.objects.all() 

5776 template_name = 'generic/object.html' 

5777 layout = layout.SimpleLayout( 

5778 left_panels=[ 

5779 panels.VirtualDeviceContextPanel(), 

5780 TagsPanel(), 

5781 ], 

5782 right_panels=[ 

5783 RelatedObjectsPanel(), 

5784 CommentsPanel(), 

5785 CustomFieldsPanel(), 

5786 ], 

5787 bottom_panels=[ 

5788 ObjectsTablePanel( 

5789 model='dcim.Interface', 

5790 filters={'vdc_id': lambda ctx: ctx['object'].pk}, 

5791 ), 

5792 ], 

5793 ) 

5794 

5795 def get_extra_context(self, request, instance): 

5796 return { 

5797 'related_models': self.get_related_models( 

5798 request, 

5799 instance, 

5800 extra=( 

5801 (Interface.objects.restrict(request.user, 'view').filter(vdcs__in=[instance]), 'vdc_id'), 

5802 ), 

5803 ), 

5804 } 

5805 

5806 

5807@register_model_view(VirtualDeviceContext, 'add', detail=False) 

5808@register_model_view(VirtualDeviceContext, 'edit') 

5809class VirtualDeviceContextEditView(generic.ObjectEditView): 

5810 queryset = VirtualDeviceContext.objects.all() 

5811 form = forms.VirtualDeviceContextForm 

5812 

5813 

5814@register_model_view(VirtualDeviceContext, 'delete') 

5815class VirtualDeviceContextDeleteView(generic.ObjectDeleteView): 

5816 queryset = VirtualDeviceContext.objects.all() 

5817 

5818 

5819@register_model_view(VirtualDeviceContext, 'bulk_import', path='import', detail=False) 

5820class VirtualDeviceContextBulkImportView(generic.BulkImportView): 

5821 queryset = VirtualDeviceContext.objects.all() 

5822 model_form = forms.VirtualDeviceContextImportForm 

5823 

5824 

5825@register_model_view(VirtualDeviceContext, 'bulk_edit', path='edit', detail=False) 

5826class VirtualDeviceContextBulkEditView(generic.BulkEditView): 

5827 queryset = VirtualDeviceContext.objects.all() 

5828 filterset = filtersets.VirtualDeviceContextFilterSet 

5829 table = tables.VirtualDeviceContextTable 

5830 form = forms.VirtualDeviceContextBulkEditForm 

5831 

5832 

5833@register_model_view(VirtualDeviceContext, 'bulk_rename', path='rename', detail=False) 

5834class VirtualDeviceContextBulkRenameView(generic.BulkRenameView): 

5835 queryset = VirtualDeviceContext.objects.all() 

5836 filterset = filtersets.VirtualDeviceContextFilterSet 

5837 

5838 

5839@register_model_view(VirtualDeviceContext, 'bulk_delete', path='delete', detail=False) 

5840class VirtualDeviceContextBulkDeleteView(generic.BulkDeleteView): 

5841 queryset = VirtualDeviceContext.objects.all() 

5842 filterset = filtersets.VirtualDeviceContextFilterSet 

5843 table = tables.VirtualDeviceContextTable 

5844 

5845 

5846# 

5847# MAC addresses 

5848# 

5849 

5850@register_model_view(MACAddress, 'list', path='', detail=False) 

5851class MACAddressListView(generic.ObjectListView): 

5852 queryset = MACAddress.objects.all() 

5853 filterset = filtersets.MACAddressFilterSet 

5854 filterset_form = forms.MACAddressFilterForm 

5855 table = tables.MACAddressTable 

5856 actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete) 

5857 

5858 

5859@register_model_view(MACAddress) 

5860class MACAddressView(generic.ObjectView): 

5861 queryset = MACAddress.objects.all() 

5862 template_name = 'generic/object.html' 

5863 layout = layout.SimpleLayout( 

5864 left_panels=[ 

5865 panels.MACAddressPanel(), 

5866 TagsPanel(), 

5867 CustomFieldsPanel(), 

5868 ], 

5869 right_panels=[ 

5870 CommentsPanel(), 

5871 ], 

5872 ) 

5873 

5874 

5875@register_model_view(MACAddress, 'add', detail=False) 

5876@register_model_view(MACAddress, 'edit') 

5877class MACAddressEditView(generic.ObjectEditView): 

5878 queryset = MACAddress.objects.all() 

5879 form = forms.MACAddressForm 

5880 

5881 

5882@register_model_view(MACAddress, 'delete') 

5883class MACAddressDeleteView(generic.ObjectDeleteView): 

5884 queryset = MACAddress.objects.all() 

5885 

5886 

5887@register_model_view(MACAddress, 'set_primary') 

5888class MACAddressSetPrimaryView(ConditionalLoginRequiredMixin, GetReturnURLMixin, View): 

5889 queryset = MACAddress.objects.all() 

5890 

5891 def get(self, request, pk): 

5892 # Degrade a direct GET (bookmark, prefetch) to the MAC's detail page rather than a 405, 

5893 # matching DataSourceSyncView. 

5894 mac = get_object_or_404(self.queryset.restrict(request.user, 'view'), pk=pk) 

5895 return redirect(mac.get_absolute_url()) 

5896 

5897 def post(self, request, pk): 

5898 mac = get_object_or_404(self.queryset.restrict(request.user, 'view'), pk=pk) 

5899 assigned_object = mac.assigned_object 

5900 

5901 if assigned_object is None: 

5902 messages.error(request, _('This MAC address is not assigned to an interface.')) 

5903 return redirect(mac.get_absolute_url()) 

5904 

5905 # Re-fetch the interface through its change-restricted queryset so object-level permissions 

5906 # are enforced, not just the model-level change permission. 

5907 model = assigned_object._meta.model 

5908 interface = model.objects.restrict(request.user, 'change').filter(pk=assigned_object.pk).first() 

5909 if interface is None: 

5910 messages.error( 

5911 request, 

5912 _('You do not have permission to modify {object}.').format(object=assigned_object) 

5913 ) 

5914 return redirect(mac.get_absolute_url()) 

5915 

5916 try: 

5917 interface.set_primary_mac_address(mac) 

5918 except ValidationError as e: 

5919 messages.error(request, ', '.join(e.messages)) 

5920 return redirect(mac.get_absolute_url()) 

5921 

5922 messages.success( 

5923 request, 

5924 _('Set {mac} as primary MAC address for {interface}.').format(mac=mac, interface=interface) 

5925 ) 

5926 return redirect(self.get_return_url(request, interface)) 

5927 

5928 

5929@register_model_view(MACAddress, 'bulk_import', path='import', detail=False) 

5930class MACAddressBulkImportView(generic.BulkImportView): 

5931 queryset = MACAddress.objects.all() 

5932 model_form = forms.MACAddressImportForm 

5933 

5934 

5935@register_model_view(MACAddress, 'bulk_edit', path='edit', detail=False) 

5936class MACAddressBulkEditView(generic.BulkEditView): 

5937 queryset = MACAddress.objects.all() 

5938 filterset = filtersets.MACAddressFilterSet 

5939 table = tables.MACAddressTable 

5940 form = forms.MACAddressBulkEditForm 

5941 

5942 

5943@register_model_view(MACAddress, 'bulk_delete', path='delete', detail=False) 

5944class MACAddressBulkDeleteView(generic.BulkDeleteView): 

5945 queryset = MACAddress.objects.all() 

5946 filterset = filtersets.MACAddressFilterSet 

5947 table = tables.MACAddressTable