Coverage for virtualization/views.py: 91%

415 statements  

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

1from django.contrib import messages 

2from django.contrib.contenttypes.models import ContentType 

3from django.db import router, transaction 

4from django.db.models import Prefetch, Sum 

5from django.shortcuts import get_object_or_404, redirect, render 

6from django.urls import reverse 

7from django.utils.translation import gettext_lazy as _ 

8 

9from dcim.filtersets import DeviceFilterSet 

10from dcim.forms import DeviceFilterForm 

11from dcim.models import Device 

12from dcim.tables import DeviceTable 

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

14from extras.views import ObjectConfigContextView, ObjectRenderConfigView 

15from ipam.models import IPAddress, VLANGroup 

16from ipam.tables import VLANTranslationRuleTable 

17from ipam.ui.panels import FHRPGroupAssignmentsPanel 

18from netbox.object_actions import ( 

19 AddObject, 

20 BulkDelete, 

21 BulkEdit, 

22 BulkExport, 

23 BulkImport, 

24 BulkRename, 

25 DeleteObject, 

26 EditObject, 

27) 

28from netbox.ui import actions, layout 

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

30from netbox.ui.panels import ( 

31 CommentsPanel, 

32 ContextTablePanel, 

33 ObjectsTablePanel, 

34 OrganizationalObjectPanel, 

35 RelatedObjectsPanel, 

36 TemplatePanel, 

37) 

38from netbox.views import generic 

39from utilities.query import count_related 

40from utilities.query_functions import CollateAsChar 

41from utilities.views import GetRelatedModelsMixin, ViewTab, register_model_view 

42 

43from . import filtersets, forms, tables 

44from .models import * 

45from .object_actions import BulkAddComponents 

46from .ui import panels 

47 

48# 

49# Cluster types 

50# 

51 

52 

53@register_model_view(ClusterType, 'list', path='', detail=False) 

54class ClusterTypeListView(generic.ObjectListView): 

55 queryset = ClusterType.objects.annotate( 

56 cluster_count=count_related(Cluster, 'type') 

57 ) 

58 filterset = filtersets.ClusterTypeFilterSet 

59 filterset_form = forms.ClusterTypeFilterForm 

60 table = tables.ClusterTypeTable 

61 

62 

63@register_model_view(ClusterType) 

64class ClusterTypeView(GetRelatedModelsMixin, generic.ObjectView): 

65 queryset = ClusterType.objects.all() 

66 layout = layout.SimpleLayout( 

67 left_panels=[ 

68 OrganizationalObjectPanel(), 

69 TagsPanel(), 

70 ], 

71 right_panels=[ 

72 RelatedObjectsPanel(), 

73 CustomFieldsPanel(), 

74 CommentsPanel(), 

75 ], 

76 ) 

77 

78 def get_extra_context(self, request, instance): 

79 return { 

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

81 } 

82 

83 

84@register_model_view(ClusterType, 'add', detail=False) 

85@register_model_view(ClusterType, 'edit') 

86class ClusterTypeEditView(generic.ObjectEditView): 

87 queryset = ClusterType.objects.all() 

88 form = forms.ClusterTypeForm 

89 

90 

91@register_model_view(ClusterType, 'delete') 

92class ClusterTypeDeleteView(generic.ObjectDeleteView): 

93 queryset = ClusterType.objects.all() 

94 

95 

96@register_model_view(ClusterType, 'bulk_import', path='import', detail=False) 

97class ClusterTypeBulkImportView(generic.BulkImportView): 

98 queryset = ClusterType.objects.all() 

99 model_form = forms.ClusterTypeImportForm 

100 

101 

102@register_model_view(ClusterType, 'bulk_edit', path='edit', detail=False) 

103class ClusterTypeBulkEditView(generic.BulkEditView): 

104 queryset = ClusterType.objects.annotate( 

105 cluster_count=count_related(Cluster, 'type') 

106 ) 

107 filterset = filtersets.ClusterTypeFilterSet 

108 table = tables.ClusterTypeTable 

109 form = forms.ClusterTypeBulkEditForm 

110 

111 

112@register_model_view(ClusterType, 'bulk_rename', path='rename', detail=False) 

113class ClusterTypeBulkRenameView(generic.BulkRenameView): 

114 queryset = ClusterType.objects.all() 

115 filterset = filtersets.ClusterTypeFilterSet 

116 

117 

118@register_model_view(ClusterType, 'bulk_delete', path='delete', detail=False) 

119class ClusterTypeBulkDeleteView(generic.BulkDeleteView): 

120 queryset = ClusterType.objects.annotate( 

121 cluster_count=count_related(Cluster, 'type') 

122 ) 

123 filterset = filtersets.ClusterTypeFilterSet 

124 table = tables.ClusterTypeTable 

125 

126 

127# 

128# Cluster groups 

129# 

130 

131@register_model_view(ClusterGroup, 'list', path='', detail=False) 

132class ClusterGroupListView(generic.ObjectListView): 

133 queryset = ClusterGroup.objects.annotate( 

134 cluster_count=count_related(Cluster, 'group') 

135 ) 

136 filterset = filtersets.ClusterGroupFilterSet 

137 filterset_form = forms.ClusterGroupFilterForm 

138 table = tables.ClusterGroupTable 

139 

140 

141@register_model_view(ClusterGroup) 

142class ClusterGroupView(GetRelatedModelsMixin, generic.ObjectView): 

143 queryset = ClusterGroup.objects.all() 

144 layout = layout.SimpleLayout( 

145 left_panels=[ 

146 OrganizationalObjectPanel(), 

147 TagsPanel(), 

148 ], 

149 right_panels=[ 

150 RelatedObjectsPanel(), 

151 CustomFieldsPanel(), 

152 CommentsPanel(), 

153 ], 

154 ) 

155 

156 def get_extra_context(self, request, instance): 

157 return { 

158 'related_models': self.get_related_models( 

159 request, 

160 instance, 

161 extra=( 

162 ( 

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

164 scope_type=ContentType.objects.get_for_model(ClusterGroup), 

165 scope_id=instance.pk 

166 ), 'cluster_group'), 

167 ), 

168 ), 

169 } 

170 

171 

172@register_model_view(ClusterGroup, 'add', detail=False) 

173@register_model_view(ClusterGroup, 'edit') 

174class ClusterGroupEditView(generic.ObjectEditView): 

175 queryset = ClusterGroup.objects.all() 

176 form = forms.ClusterGroupForm 

177 

178 

179@register_model_view(ClusterGroup, 'delete') 

180class ClusterGroupDeleteView(generic.ObjectDeleteView): 

181 queryset = ClusterGroup.objects.all() 

182 

183 

184@register_model_view(ClusterGroup, 'bulk_import', path='import', detail=False) 

185class ClusterGroupBulkImportView(generic.BulkImportView): 

186 queryset = ClusterGroup.objects.annotate( 

187 cluster_count=count_related(Cluster, 'group') 

188 ) 

189 model_form = forms.ClusterGroupImportForm 

190 

191 

192@register_model_view(ClusterGroup, 'bulk_edit', path='edit', detail=False) 

193class ClusterGroupBulkEditView(generic.BulkEditView): 

194 queryset = ClusterGroup.objects.annotate( 

195 cluster_count=count_related(Cluster, 'group') 

196 ) 

197 filterset = filtersets.ClusterGroupFilterSet 

198 table = tables.ClusterGroupTable 

199 form = forms.ClusterGroupBulkEditForm 

200 

201 

202@register_model_view(ClusterGroup, 'bulk_rename', path='rename', detail=False) 

203class ClusterGroupBulkRenameView(generic.BulkRenameView): 

204 queryset = ClusterGroup.objects.all() 

205 filterset = filtersets.ClusterGroupFilterSet 

206 

207 

208@register_model_view(ClusterGroup, 'bulk_delete', path='delete', detail=False) 

209class ClusterGroupBulkDeleteView(generic.BulkDeleteView): 

210 queryset = ClusterGroup.objects.annotate( 

211 cluster_count=count_related(Cluster, 'group') 

212 ) 

213 filterset = filtersets.ClusterGroupFilterSet 

214 table = tables.ClusterGroupTable 

215 

216 

217# 

218# Clusters 

219# 

220 

221@register_model_view(Cluster, 'list', path='', detail=False) 

222class ClusterListView(generic.ObjectListView): 

223 permission_required = 'virtualization.view_cluster' 

224 queryset = Cluster.objects.annotate( 

225 device_count=count_related(Device, 'cluster'), 

226 vm_count=count_related(VirtualMachine, 'cluster') 

227 ) 

228 table = tables.ClusterTable 

229 filterset = filtersets.ClusterFilterSet 

230 filterset_form = forms.ClusterFilterForm 

231 

232 

233@register_model_view(Cluster) 

234class ClusterView(GetRelatedModelsMixin, generic.ObjectView): 

235 queryset = Cluster.objects.all() 

236 layout = layout.SimpleLayout( 

237 breadcrumbs=[ 

238 Breadcrumb('type', url=filtered_list_url('virtualization:cluster_list', 'type_id')), 

239 Breadcrumb('group', url=filtered_list_url('virtualization:cluster_list', 'group_id')), 

240 ], 

241 left_panels=[ 

242 panels.ClusterPanel(), 

243 CommentsPanel(), 

244 ], 

245 right_panels=[ 

246 TemplatePanel('virtualization/panels/cluster_resources.html'), 

247 RelatedObjectsPanel(), 

248 CustomFieldsPanel(), 

249 TagsPanel(), 

250 ], 

251 ) 

252 

253 def get_extra_context(self, request, instance): 

254 return { 

255 **instance.virtual_machines.aggregate( 

256 vcpus_sum=Sum('vcpus'), 

257 memory_sum=Sum('memory'), 

258 disk_sum=Sum('disk') 

259 ), 

260 'related_models': self.get_related_models( 

261 request, 

262 instance, 

263 omit=(), 

264 extra=( 

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

266 scope_type=ContentType.objects.get_for_model(Cluster), 

267 scope_id=instance.pk 

268 ), 'cluster'), 

269 ) 

270 ), 

271 } 

272 

273 

274@register_model_view(Cluster, 'virtualmachines', path='virtual-machines') 

275class ClusterVirtualMachinesView(generic.ObjectChildrenView): 

276 queryset = Cluster.objects.all() 

277 child_model = VirtualMachine 

278 table = tables.VirtualMachineTable 

279 filterset = filtersets.VirtualMachineFilterSet 

280 filterset_form = forms.VirtualMachineFilterForm 

281 actions = (EditObject, DeleteObject, BulkEdit) 

282 tab = ViewTab( 

283 label=_('Virtual Machines'), 

284 badge=lambda obj: obj.virtual_machines.count(), 

285 permission='virtualization.view_virtualmachine', 

286 weight=500 

287 ) 

288 

289 def get_children(self, request, parent): 

290 return VirtualMachine.objects.restrict(request.user, 'view').filter(cluster=parent) 

291 

292 

293@register_model_view(Cluster, 'devices') 

294class ClusterDevicesView(generic.ObjectChildrenView): 

295 queryset = Cluster.objects.all() 

296 child_model = Device 

297 table = DeviceTable 

298 filterset = DeviceFilterSet 

299 filterset_form = DeviceFilterForm 

300 actions = (EditObject, DeleteObject, BulkEdit) 

301 tab = ViewTab( 

302 label=_('Devices'), 

303 badge=lambda obj: obj.devices.count(), 

304 permission='virtualization.view_virtualmachine', 

305 weight=600 

306 ) 

307 

308 def get_children(self, request, parent): 

309 return Device.objects.restrict(request.user, 'view').filter(cluster=parent) 

310 

311 

312@register_model_view(Cluster, 'add', detail=False) 

313@register_model_view(Cluster, 'edit') 

314class ClusterEditView(generic.ObjectEditView): 

315 queryset = Cluster.objects.all() 

316 form = forms.ClusterForm 

317 

318 

319@register_model_view(Cluster, 'delete') 

320class ClusterDeleteView(generic.ObjectDeleteView): 

321 queryset = Cluster.objects.all() 

322 

323 

324@register_model_view(Cluster, 'bulk_import', path='import', detail=False) 

325class ClusterBulkImportView(generic.BulkImportView): 

326 queryset = Cluster.objects.all() 

327 model_form = forms.ClusterImportForm 

328 

329 

330@register_model_view(Cluster, 'bulk_edit', path='edit', detail=False) 

331class ClusterBulkEditView(generic.BulkEditView): 

332 queryset = Cluster.objects.all() 

333 filterset = filtersets.ClusterFilterSet 

334 table = tables.ClusterTable 

335 form = forms.ClusterBulkEditForm 

336 

337 

338@register_model_view(Cluster, 'bulk_rename', path='rename', detail=False) 

339class ClusterBulkRenameView(generic.BulkRenameView): 

340 queryset = Cluster.objects.all() 

341 filterset = filtersets.ClusterFilterSet 

342 

343 

344@register_model_view(Cluster, 'bulk_delete', path='delete', detail=False) 

345class ClusterBulkDeleteView(generic.BulkDeleteView): 

346 queryset = Cluster.objects.all() 

347 filterset = filtersets.ClusterFilterSet 

348 table = tables.ClusterTable 

349 

350 

351@register_model_view(Cluster, 'add_devices', path='devices/add') 

352class ClusterAddDevicesView(generic.ObjectEditView): 

353 queryset = Cluster.objects.all() 

354 form = forms.ClusterAddDevicesForm 

355 template_name = 'virtualization/cluster_add_devices.html' 

356 

357 def get(self, request, pk): 

358 cluster = get_object_or_404(self.queryset, pk=pk) 

359 form = self.form(cluster, initial=request.GET) 

360 

361 return render(request, self.template_name, { 

362 'cluster': cluster, 

363 'form': form, 

364 'return_url': reverse('virtualization:cluster', kwargs={'pk': pk}), 

365 }) 

366 

367 def post(self, request, pk): 

368 cluster = get_object_or_404(self.queryset, pk=pk) 

369 form = self.form(cluster, request.POST) 

370 

371 if form.is_valid(): 

372 

373 device_pks = form.cleaned_data['devices'] 

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

375 

376 # Assign the selected Devices to the Cluster 

377 for device in Device.objects.filter(pk__in=device_pks): 

378 device.snapshot() 

379 device.cluster = cluster 

380 device.save() 

381 

382 messages.success(request, _("Added {count} devices to cluster {cluster}").format( 

383 count=len(device_pks), 

384 cluster=cluster 

385 )) 

386 return redirect(cluster.get_absolute_url()) 

387 

388 return render(request, self.template_name, { 

389 'cluster': cluster, 

390 'form': form, 

391 'return_url': cluster.get_absolute_url(), 

392 }) 

393 

394 

395# 

396# Virtual machine types 

397# 

398 

399 

400@register_model_view(VirtualMachineType, 'list', path='', detail=False) 

401class VirtualMachineTypeListView(generic.ObjectListView): 

402 queryset = VirtualMachineType.objects.all() 

403 filterset = filtersets.VirtualMachineTypeFilterSet 

404 filterset_form = forms.VirtualMachineTypeFilterForm 

405 table = tables.VirtualMachineTypeTable 

406 

407 

408@register_model_view(VirtualMachineType) 

409class VirtualMachineTypeView(GetRelatedModelsMixin, generic.ObjectView): 

410 queryset = VirtualMachineType.objects.all() 

411 layout = layout.SimpleLayout( 

412 left_panels=[ 

413 panels.VirtualMachineTypePanel(), 

414 TagsPanel(), 

415 CommentsPanel(), 

416 ], 

417 right_panels=[ 

418 RelatedObjectsPanel(), 

419 CustomFieldsPanel(), 

420 ImageAttachmentsPanel(), 

421 ], 

422 ) 

423 

424 def get_extra_context(self, request, instance): 

425 return { 

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

427 } 

428 

429 

430@register_model_view(VirtualMachineType, 'add', detail=False) 

431@register_model_view(VirtualMachineType, 'edit') 

432class VirtualMachineTypeEditView(generic.ObjectEditView): 

433 queryset = VirtualMachineType.objects.all() 

434 form = forms.VirtualMachineTypeForm 

435 

436 

437@register_model_view(VirtualMachineType, 'delete') 

438class VirtualMachineTypeDeleteView(generic.ObjectDeleteView): 

439 queryset = VirtualMachineType.objects.all() 

440 

441 

442@register_model_view(VirtualMachineType, 'bulk_import', path='import', detail=False) 

443class VirtualMachineTypeBulkImportView(generic.BulkImportView): 

444 queryset = VirtualMachineType.objects.all() 

445 model_form = forms.VirtualMachineTypeImportForm 

446 

447 

448@register_model_view(VirtualMachineType, 'bulk_edit', path='edit', detail=False) 

449class VirtualMachineTypeBulkEditView(generic.BulkEditView): 

450 queryset = VirtualMachineType.objects.all() 

451 filterset = filtersets.VirtualMachineTypeFilterSet 

452 table = tables.VirtualMachineTypeTable 

453 form = forms.VirtualMachineTypeBulkEditForm 

454 

455 

456@register_model_view(VirtualMachineType, 'bulk_rename', path='rename', detail=False) 

457class VirtualMachineTypeBulkRenameView(generic.BulkRenameView): 

458 queryset = VirtualMachineType.objects.all() 

459 filterset = filtersets.VirtualMachineTypeFilterSet 

460 

461 

462@register_model_view(VirtualMachineType, 'bulk_delete', path='delete', detail=False) 

463class VirtualMachineTypeBulkDeleteView(generic.BulkDeleteView): 

464 queryset = VirtualMachineType.objects.all() 

465 filterset = filtersets.VirtualMachineTypeFilterSet 

466 table = tables.VirtualMachineTypeTable 

467 

468 

469# 

470# Virtual machines 

471# 

472 

473 

474@register_model_view(VirtualMachine, 'list', path='', detail=False) 

475class VirtualMachineListView(generic.ObjectListView): 

476 queryset = VirtualMachine.objects.prefetch_related('primary_ip4', 'primary_ip6') 

477 filterset = filtersets.VirtualMachineFilterSet 

478 filterset_form = forms.VirtualMachineFilterForm 

479 table = tables.VirtualMachineTable 

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

481 

482 

483@register_model_view(VirtualMachine) 

484class VirtualMachineView(generic.ObjectView): 

485 queryset = VirtualMachine.objects.all() 

486 layout = layout.SimpleLayout( 

487 left_panels=[ 

488 panels.VirtualMachinePanel(), 

489 CustomFieldsPanel(), 

490 TagsPanel(), 

491 CommentsPanel(), 

492 ], 

493 right_panels=[ 

494 panels.VirtualMachinePlacementPanel(), 

495 TemplatePanel('virtualization/panels/virtual_machine_resources.html'), 

496 ObjectsTablePanel( 

497 model='ipam.Service', 

498 title=_('Application Services'), 

499 filters={'virtual_machine_id': lambda ctx: ctx['object'].pk}, 

500 exclude_columns=['parent'], 

501 actions=[ 

502 actions.AddObject( 

503 'ipam.Service', 

504 url_params={ 

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

506 'parent_object_id': lambda ctx: ctx['object'].pk, 

507 }, 

508 ), 

509 ], 

510 ), 

511 ImageAttachmentsPanel(), 

512 ], 

513 bottom_panels=[ 

514 ObjectsTablePanel( 

515 model='virtualization.VirtualDisk', 

516 filters={'virtual_machine_id': lambda ctx: ctx['object'].pk}, 

517 exclude_columns=['virtual_machine'], 

518 actions=[ 

519 actions.AddObject( 

520 'virtualization.VirtualDisk', url_params={'virtual_machine': lambda ctx: ctx['object'].pk} 

521 ), 

522 ], 

523 ), 

524 ], 

525 ) 

526 

527 

528@register_model_view(VirtualMachine, 'interfaces') 

529class VirtualMachineInterfacesView(generic.ObjectChildrenView): 

530 queryset = VirtualMachine.objects.all() 

531 child_model = VMInterface 

532 table = tables.VirtualMachineVMInterfaceTable 

533 filterset = filtersets.VMInterfaceFilterSet 

534 filterset_form = forms.VMInterfaceFilterForm 

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

536 tab = ViewTab( 

537 label=_('Interfaces'), 

538 badge=lambda obj: obj.interface_count, 

539 permission='virtualization.view_vminterface', 

540 weight=500 

541 ) 

542 

543 def get_children(self, request, parent): 

544 return parent.interfaces.restrict(request.user, 'view').prefetch_related( 

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

546 'tags', 

547 ) 

548 

549 

550@register_model_view(VirtualMachine, 'disks') 

551class VirtualMachineVirtualDisksView(generic.ObjectChildrenView): 

552 queryset = VirtualMachine.objects.all() 

553 child_model = VirtualDisk 

554 table = tables.VirtualMachineVirtualDiskTable 

555 filterset = filtersets.VirtualDiskFilterSet 

556 filterset_form = forms.VirtualDiskFilterForm 

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

558 tab = ViewTab( 

559 label=_('Virtual Disks'), 

560 badge=lambda obj: obj.virtual_disk_count, 

561 permission='virtualization.view_virtualdisk', 

562 weight=500 

563 ) 

564 

565 def get_children(self, request, parent): 

566 return parent.virtualdisks.restrict(request.user, 'view').prefetch_related('tags') 

567 

568 

569@register_model_view(VirtualMachine, 'configcontext', path='config-context') 

570class VirtualMachineConfigContextView(ObjectConfigContextView): 

571 queryset = VirtualMachine.objects.all() 

572 base_template = 'virtualization/virtualmachine.html' 

573 tab = ViewTab( 

574 label=_('Config Context'), 

575 weight=2000 

576 ) 

577 

578 

579@register_model_view(VirtualMachine, 'render-config') 

580class VirtualMachineRenderConfigView(ObjectRenderConfigView): 

581 queryset = VirtualMachine.objects.all() 

582 base_template = 'virtualization/virtualmachine/base.html' 

583 additional_permissions = ['virtualization.render_config_virtualmachine'] 

584 tab = ViewTab( 

585 label=_('Render Config'), 

586 weight=2100, 

587 ) 

588 

589 

590@register_model_view(VirtualMachine, 'add', detail=False) 

591@register_model_view(VirtualMachine, 'edit') 

592class VirtualMachineEditView(generic.ObjectEditView): 

593 queryset = VirtualMachine.objects.all() 

594 form = forms.VirtualMachineForm 

595 

596 

597@register_model_view(VirtualMachine, 'delete') 

598class VirtualMachineDeleteView(generic.ObjectDeleteView): 

599 queryset = VirtualMachine.objects.all() 

600 

601 

602@register_model_view(VirtualMachine, 'bulk_import', path='import', detail=False) 

603class VirtualMachineBulkImportView(generic.BulkImportView): 

604 queryset = VirtualMachine.objects.all() 

605 model_form = forms.VirtualMachineImportForm 

606 

607 

608@register_model_view(VirtualMachine, 'bulk_edit', path='edit', detail=False) 

609class VirtualMachineBulkEditView(generic.BulkEditView): 

610 queryset = VirtualMachine.objects.prefetch_related('primary_ip4', 'primary_ip6') 

611 filterset = filtersets.VirtualMachineFilterSet 

612 table = tables.VirtualMachineTable 

613 form = forms.VirtualMachineBulkEditForm 

614 

615 

616@register_model_view(VirtualMachine, 'bulk_rename', path='rename', detail=False) 

617class VirtualMachineBulkRenameView(generic.BulkRenameView): 

618 queryset = VirtualMachine.objects.all() 

619 filterset = filtersets.VirtualMachineFilterSet 

620 

621 

622@register_model_view(VirtualMachine, 'bulk_delete', path='delete', detail=False) 

623class VirtualMachineBulkDeleteView(generic.BulkDeleteView): 

624 queryset = VirtualMachine.objects.prefetch_related('primary_ip4', 'primary_ip6') 

625 filterset = filtersets.VirtualMachineFilterSet 

626 table = tables.VirtualMachineTable 

627 

628 

629# 

630# VM interfaces 

631# 

632 

633 

634@register_model_view(VMInterface, 'list', path='', detail=False) 

635class VMInterfaceListView(generic.ObjectListView): 

636 queryset = VMInterface.objects.all() 

637 filterset = filtersets.VMInterfaceFilterSet 

638 filterset_form = forms.VMInterfaceFilterForm 

639 table = tables.VMInterfaceTable 

640 

641 

642@register_model_view(VMInterface) 

643class VMInterfaceView(generic.ObjectView): 

644 template_name = 'generic/object.html' 

645 queryset = VMInterface.objects.all() 

646 layout = layout.SimpleLayout( 

647 breadcrumbs=[ 

648 Breadcrumb( 

649 'virtual_machine', 

650 url=object_view_url('virtualization:virtualmachine_interfaces'), 

651 ), 

652 ], 

653 left_panels=[ 

654 panels.VMInterfacePanel(), 

655 TagsPanel(), 

656 ], 

657 right_panels=[ 

658 CustomFieldsPanel(), 

659 panels.VMInterfaceAddressingPanel(), 

660 FHRPGroupAssignmentsPanel(), 

661 ], 

662 bottom_panels=[ 

663 ObjectsTablePanel( 

664 model='ipam.IPaddress', 

665 filters={'vminterface_id': lambda ctx: ctx['object'].pk}, 

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

667 actions=[ 

668 actions.AddObject( 

669 'ipam.IPaddress', 

670 url_params={ 

671 'virtual_machine': lambda ctx: ctx['object'].virtual_machine.pk, 

672 'vminterface': lambda ctx: ctx['object'].pk, 

673 }, 

674 ), 

675 ], 

676 ), 

677 ObjectsTablePanel( 

678 model='dcim.MACAddress', 

679 filters={'vminterface_id': lambda ctx: ctx['object'].pk}, 

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

681 actions=[ 

682 actions.AddObject( 

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

684 ), 

685 ], 

686 ), 

687 ObjectsTablePanel( 

688 model='ipam.VLAN', 

689 title=_('Assigned VLANs'), 

690 filters={'vminterface_id': lambda ctx: ctx['object'].pk}, 

691 ), 

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

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

694 ], 

695 ) 

696 

697 def get_extra_context(self, request, instance): 

698 

699 # Get child interfaces 

700 child_interfaces = VMInterface.objects.restrict(request.user, 'view').filter(parent=instance) 

701 child_interfaces_tables = tables.VMInterfaceTable( 

702 child_interfaces, 

703 exclude=('virtual_machine',), 

704 orderable=False 

705 ) 

706 child_interfaces_tables.configure(request) 

707 

708 # Get VLAN translation rules 

709 vlan_translation_table = None 

710 if instance.vlan_translation_policy: 

711 vlan_translation_table = VLANTranslationRuleTable( 

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

713 orderable=False 

714 ) 

715 vlan_translation_table.configure(request) 

716 

717 return { 

718 'child_interfaces_table': child_interfaces_tables, 

719 'vlan_translation_table': vlan_translation_table, 

720 } 

721 

722 

723@register_model_view(VMInterface, 'add', detail=False) 

724class VMInterfaceCreateView(generic.ComponentCreateView): 

725 queryset = VMInterface.objects.all() 

726 form = forms.VMInterfaceCreateForm 

727 model_form = forms.VMInterfaceForm 

728 

729 

730@register_model_view(VMInterface, 'edit') 

731class VMInterfaceEditView(generic.ObjectEditView): 

732 queryset = VMInterface.objects.all() 

733 form = forms.VMInterfaceForm 

734 

735 

736@register_model_view(VMInterface, 'delete') 

737class VMInterfaceDeleteView(generic.ObjectDeleteView): 

738 queryset = VMInterface.objects.all() 

739 

740 

741@register_model_view(VMInterface, 'bulk_import', path='import', detail=False) 

742class VMInterfaceBulkImportView(generic.BulkImportView): 

743 queryset = VMInterface.objects.all() 

744 model_form = forms.VMInterfaceImportForm 

745 

746 

747@register_model_view(VMInterface, 'bulk_edit', path='edit', detail=False) 

748class VMInterfaceBulkEditView(generic.BulkEditView): 

749 queryset = VMInterface.objects.all() 

750 filterset = filtersets.VMInterfaceFilterSet 

751 table = tables.VMInterfaceTable 

752 form = forms.VMInterfaceBulkEditForm 

753 

754 

755@register_model_view(VMInterface, 'bulk_rename', path='rename', detail=False) 

756class VMInterfaceBulkRenameView(generic.BulkRenameView): 

757 queryset = VMInterface.objects.all() 

758 filterset = filtersets.VMInterfaceFilterSet 

759 

760 

761@register_model_view(VMInterface, 'bulk_delete', path='delete', detail=False) 

762class VMInterfaceBulkDeleteView(generic.BulkDeleteView): 

763 # Ensure child interfaces are deleted prior to their parents 

764 queryset = VMInterface.objects.order_by('virtual_machine', 'parent', CollateAsChar('_name')) 

765 filterset = filtersets.VMInterfaceFilterSet 

766 table = tables.VMInterfaceTable 

767 

768 

769# 

770# Virtual disks 

771# 

772 

773@register_model_view(VirtualDisk, 'list', path='', detail=False) 

774class VirtualDiskListView(generic.ObjectListView): 

775 queryset = VirtualDisk.objects.all() 

776 filterset = filtersets.VirtualDiskFilterSet 

777 filterset_form = forms.VirtualDiskFilterForm 

778 table = tables.VirtualDiskTable 

779 

780 

781@register_model_view(VirtualDisk) 

782class VirtualDiskView(generic.ObjectView): 

783 template_name = 'generic/object.html' 

784 queryset = VirtualDisk.objects.all() 

785 layout = layout.SimpleLayout( 

786 breadcrumbs=[ 

787 Breadcrumb( 

788 'virtual_machine', 

789 url=object_view_url('virtualization:virtualmachine_disks'), 

790 ), 

791 ], 

792 left_panels=[ 

793 panels.VirtualDiskPanel(), 

794 TagsPanel(), 

795 ], 

796 right_panels=[ 

797 CustomFieldsPanel(), 

798 ], 

799 ) 

800 

801 

802@register_model_view(VirtualDisk, 'add', detail=False) 

803class VirtualDiskCreateView(generic.ComponentCreateView): 

804 queryset = VirtualDisk.objects.all() 

805 form = forms.VirtualDiskCreateForm 

806 model_form = forms.VirtualDiskForm 

807 

808 

809@register_model_view(VirtualDisk, 'edit') 

810class VirtualDiskEditView(generic.ObjectEditView): 

811 queryset = VirtualDisk.objects.all() 

812 form = forms.VirtualDiskForm 

813 

814 

815@register_model_view(VirtualDisk, 'delete') 

816class VirtualDiskDeleteView(generic.ObjectDeleteView): 

817 queryset = VirtualDisk.objects.all() 

818 

819 

820@register_model_view(VirtualDisk, 'bulk_import', path='import', detail=False) 

821class VirtualDiskBulkImportView(generic.BulkImportView): 

822 queryset = VirtualDisk.objects.all() 

823 model_form = forms.VirtualDiskImportForm 

824 

825 

826@register_model_view(VirtualDisk, 'bulk_edit', path='edit', detail=False) 

827class VirtualDiskBulkEditView(generic.BulkEditView): 

828 queryset = VirtualDisk.objects.all() 

829 filterset = filtersets.VirtualDiskFilterSet 

830 table = tables.VirtualDiskTable 

831 form = forms.VirtualDiskBulkEditForm 

832 

833 

834@register_model_view(VirtualDisk, 'bulk_rename', path='rename', detail=False) 

835class VirtualDiskBulkRenameView(generic.BulkRenameView): 

836 queryset = VirtualDisk.objects.all() 

837 filterset = filtersets.VirtualDiskFilterSet 

838 

839 

840@register_model_view(VirtualDisk, 'bulk_delete', path='delete', detail=False) 

841class VirtualDiskBulkDeleteView(generic.BulkDeleteView): 

842 queryset = VirtualDisk.objects.all() 

843 filterset = filtersets.VirtualDiskFilterSet 

844 table = tables.VirtualDiskTable 

845 

846 

847# 

848# Bulk Device component creation 

849# 

850 

851class VirtualMachineBulkAddInterfaceView(generic.BulkComponentCreateView): 

852 parent_model = VirtualMachine 

853 parent_field = 'virtual_machine' 

854 form = forms.VMInterfaceBulkCreateForm 

855 queryset = VMInterface.objects.all() 

856 model_form = forms.VMInterfaceForm 

857 filterset = filtersets.VirtualMachineFilterSet 

858 table = tables.VirtualMachineTable 

859 default_return_url = 'virtualization:virtualmachine_list' 

860 

861 def get_required_permission(self): 

862 return 'virtualization.add_vminterface' 

863 

864 

865class VirtualMachineBulkAddVirtualDiskView(generic.BulkComponentCreateView): 

866 parent_model = VirtualMachine 

867 parent_field = 'virtual_machine' 

868 form = forms.VirtualDiskBulkCreateForm 

869 queryset = VirtualDisk.objects.all() 

870 model_form = forms.VirtualDiskForm 

871 filterset = filtersets.VirtualMachineFilterSet 

872 table = tables.VirtualMachineTable 

873 default_return_url = 'virtualization:virtualmachine_list' 

874 

875 def get_required_permission(self): 

876 return 'virtualization.add_virtualdisk'