Coverage for ipam/views.py: 79%

982 statements  

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

1import django_filters 

2from django.contrib.contenttypes.models import ContentType 

3from django.db.models import Prefetch 

4from django.db.models.expressions import RawSQL 

5from django.shortcuts import get_object_or_404, redirect, render 

6from django.urls import reverse 

7from django.utils.translation import gettext_lazy as _ 

8from django_filters.constants import EMPTY_VALUES 

9 

10from circuits.models import Provider 

11from dcim.filtersets import InterfaceFilterSet 

12from dcim.forms import InterfaceFilterForm 

13from dcim.models import Device, Interface, Site 

14from extras.ui.panels import CustomFieldsPanel, TagsPanel 

15from netbox.object_actions import AddObject, BulkDelete, BulkEdit, BulkExport, BulkImport 

16from netbox.ui import actions, layout 

17from netbox.ui.breadcrumbs import Breadcrumb, filtered_list_url 

18from netbox.ui.panels import ( 

19 CommentsPanel, 

20 ContextTablePanel, 

21 ObjectsTablePanel, 

22 PluginContentPanel, 

23 RelatedObjectsPanel, 

24 TemplatePanel, 

25) 

26from netbox.views import generic 

27from utilities.query import count_related 

28from utilities.tables import get_table_ordering 

29from utilities.views import GetRelatedModelsMixin, GetReturnURLMixin, ViewTab, register_model_view 

30from virtualization.filtersets import VMInterfaceFilterSet 

31from virtualization.forms import VMInterfaceFilterForm 

32from virtualization.models import VirtualMachine, VMInterface 

33 

34from . import filtersets, forms, tables 

35from .choices import PrefixStatusChoices 

36from .constants import * 

37from .models import * 

38from .ui import panels 

39from .utils import add_available_vlans, add_requested_prefixes, annotate_ip_space 

40 

41# 

42# VRFs 

43# 

44 

45 

46@register_model_view(VRF, 'list', path='', detail=False) 

47class VRFListView(generic.ObjectListView): 

48 queryset = VRF.objects.all() 

49 filterset = filtersets.VRFFilterSet 

50 filterset_form = forms.VRFFilterForm 

51 table = tables.VRFTable 

52 

53 

54@register_model_view(VRF) 

55class VRFView(GetRelatedModelsMixin, generic.ObjectView): 

56 queryset = VRF.objects.all() 

57 template_name = 'generic/object.html' 

58 layout = layout.Layout( 

59 layout.Row( 

60 layout.Column( 

61 panels.VRFPanel(), 

62 TagsPanel(), 

63 PluginContentPanel('left_page'), 

64 ), 

65 layout.Column( 

66 RelatedObjectsPanel(), 

67 CustomFieldsPanel(), 

68 CommentsPanel(), 

69 PluginContentPanel('right_page'), 

70 ), 

71 ), 

72 layout.Row( 

73 layout.Column( 

74 ContextTablePanel('import_targets_table', title=_('Import route targets')), 

75 ), 

76 layout.Column( 

77 ContextTablePanel('export_targets_table', title=_('Export route targets')), 

78 ), 

79 ), 

80 layout.Row( 

81 layout.Column( 

82 PluginContentPanel('full_width_page'), 

83 ), 

84 ), 

85 ) 

86 

87 def get_extra_context(self, request, instance): 

88 import_targets_table = tables.RouteTargetTable( 

89 instance.import_targets.all(), 

90 orderable=False 

91 ) 

92 import_targets_table.configure(request) 

93 

94 export_targets_table = tables.RouteTargetTable( 

95 instance.export_targets.all(), 

96 orderable=False 

97 ) 

98 export_targets_table.configure(request) 

99 

100 related_models = self.get_related_models( 

101 request, 

102 instance, 

103 omit=(Interface, VMInterface), 

104 extra=( 

105 ( 

106 Interface.objects.restrict(request.user, 'view').filter(vrf=instance), 

107 'vrf_id', 

108 _('Device Interfaces') 

109 ), 

110 ( 

111 VMInterface.objects.restrict(request.user, 'view').filter(vrf=instance), 

112 'vrf_id', 

113 _('VM Interfaces') 

114 ), 

115 ), 

116 ) 

117 

118 return { 

119 'related_models': related_models, 

120 'import_targets_table': import_targets_table, 

121 'export_targets_table': export_targets_table, 

122 } 

123 

124 

125@register_model_view(VRF, 'add', detail=False) 

126@register_model_view(VRF, 'edit') 

127class VRFEditView(generic.ObjectEditView): 

128 queryset = VRF.objects.all() 

129 form = forms.VRFForm 

130 

131 

132@register_model_view(VRF, 'delete') 

133class VRFDeleteView(generic.ObjectDeleteView): 

134 queryset = VRF.objects.all() 

135 

136 

137@register_model_view(VRF, 'bulk_import', path='import', detail=False) 

138class VRFBulkImportView(generic.BulkImportView): 

139 queryset = VRF.objects.all() 

140 model_form = forms.VRFImportForm 

141 

142 

143@register_model_view(VRF, 'bulk_edit', path='edit', detail=False) 

144class VRFBulkEditView(generic.BulkEditView): 

145 queryset = VRF.objects.all() 

146 filterset = filtersets.VRFFilterSet 

147 table = tables.VRFTable 

148 form = forms.VRFBulkEditForm 

149 

150 

151@register_model_view(VRF, 'bulk_rename', path='rename', detail=False) 

152class VRFBulkRenameView(generic.BulkRenameView): 

153 queryset = VRF.objects.all() 

154 filterset = filtersets.VRFFilterSet 

155 

156 

157@register_model_view(VRF, 'bulk_delete', path='delete', detail=False) 

158class VRFBulkDeleteView(generic.BulkDeleteView): 

159 queryset = VRF.objects.all() 

160 filterset = filtersets.VRFFilterSet 

161 table = tables.VRFTable 

162 

163 

164# 

165# Route targets 

166# 

167 

168@register_model_view(RouteTarget, 'list', path='', detail=False) 

169class RouteTargetListView(generic.ObjectListView): 

170 queryset = RouteTarget.objects.all() 

171 filterset = filtersets.RouteTargetFilterSet 

172 filterset_form = forms.RouteTargetFilterForm 

173 table = tables.RouteTargetTable 

174 

175 

176@register_model_view(RouteTarget) 

177class RouteTargetView(generic.ObjectView): 

178 queryset = RouteTarget.objects.all() 

179 template_name = 'generic/object.html' 

180 layout = layout.Layout( 

181 layout.Row( 

182 layout.Column( 

183 panels.RouteTargetPanel(), 

184 TagsPanel(), 

185 PluginContentPanel('left_page'), 

186 ), 

187 layout.Column( 

188 CustomFieldsPanel(), 

189 CommentsPanel(), 

190 PluginContentPanel('right_page'), 

191 ), 

192 ), 

193 layout.Row( 

194 layout.Column( 

195 ObjectsTablePanel( 

196 'ipam.vrf', 

197 filters={'import_target_id': lambda ctx: ctx['object'].pk}, 

198 title=_('Importing VRFs'), 

199 ), 

200 ), 

201 layout.Column( 

202 ObjectsTablePanel( 

203 'ipam.vrf', 

204 filters={'export_target_id': lambda ctx: ctx['object'].pk}, 

205 title=_('Exporting VRFs'), 

206 ), 

207 ), 

208 ), 

209 layout.Row( 

210 layout.Column( 

211 ObjectsTablePanel( 

212 'vpn.l2vpn', 

213 filters={'import_target_id': lambda ctx: ctx['object'].pk}, 

214 title=_('Importing L2VPNs'), 

215 ), 

216 ), 

217 layout.Column( 

218 ObjectsTablePanel( 

219 'vpn.l2vpn', 

220 filters={'export_target_id': lambda ctx: ctx['object'].pk}, 

221 title=_('Exporting L2VPNs'), 

222 ), 

223 ), 

224 ), 

225 layout.Row( 

226 layout.Column( 

227 PluginContentPanel('full_width_page'), 

228 ), 

229 ), 

230 ) 

231 

232 

233@register_model_view(RouteTarget, 'add', detail=False) 

234@register_model_view(RouteTarget, 'edit') 

235class RouteTargetEditView(generic.ObjectEditView): 

236 queryset = RouteTarget.objects.all() 

237 form = forms.RouteTargetForm 

238 

239 

240@register_model_view(RouteTarget, 'delete') 

241class RouteTargetDeleteView(generic.ObjectDeleteView): 

242 queryset = RouteTarget.objects.all() 

243 

244 

245@register_model_view(RouteTarget, 'bulk_import', path='import', detail=False) 

246class RouteTargetBulkImportView(generic.BulkImportView): 

247 queryset = RouteTarget.objects.all() 

248 model_form = forms.RouteTargetImportForm 

249 

250 

251@register_model_view(RouteTarget, 'bulk_edit', path='edit', detail=False) 

252class RouteTargetBulkEditView(generic.BulkEditView): 

253 queryset = RouteTarget.objects.all() 

254 filterset = filtersets.RouteTargetFilterSet 

255 table = tables.RouteTargetTable 

256 form = forms.RouteTargetBulkEditForm 

257 

258 

259@register_model_view(RouteTarget, 'bulk_rename', path='rename', detail=False) 

260class RouteTargetBulkRenameView(generic.BulkRenameView): 

261 queryset = RouteTarget.objects.all() 

262 filterset = filtersets.RouteTargetFilterSet 

263 

264 

265@register_model_view(RouteTarget, 'bulk_delete', path='delete', detail=False) 

266class RouteTargetBulkDeleteView(generic.BulkDeleteView): 

267 queryset = RouteTarget.objects.all() 

268 filterset = filtersets.RouteTargetFilterSet 

269 table = tables.RouteTargetTable 

270 

271 

272# 

273# RIRs 

274# 

275 

276@register_model_view(RIR, 'list', path='', detail=False) 

277class RIRListView(generic.ObjectListView): 

278 queryset = RIR.objects.annotate( 

279 aggregate_count=count_related(Aggregate, 'rir') 

280 ) 

281 filterset = filtersets.RIRFilterSet 

282 filterset_form = forms.RIRFilterForm 

283 table = tables.RIRTable 

284 

285 

286@register_model_view(RIR) 

287class RIRView(GetRelatedModelsMixin, generic.ObjectView): 

288 queryset = RIR.objects.all() 

289 layout = layout.SimpleLayout( 

290 left_panels=[ 

291 panels.RIRPanel(), 

292 TagsPanel(), 

293 ], 

294 right_panels=[ 

295 RelatedObjectsPanel(), 

296 CommentsPanel(), 

297 CustomFieldsPanel(), 

298 ], 

299 ) 

300 

301 def get_extra_context(self, request, instance): 

302 return { 

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

304 } 

305 

306 

307@register_model_view(RIR, 'add', detail=False) 

308@register_model_view(RIR, 'edit') 

309class RIREditView(generic.ObjectEditView): 

310 queryset = RIR.objects.all() 

311 form = forms.RIRForm 

312 

313 

314@register_model_view(RIR, 'delete') 

315class RIRDeleteView(generic.ObjectDeleteView): 

316 queryset = RIR.objects.all() 

317 

318 

319@register_model_view(RIR, 'bulk_import', path='import', detail=False) 

320class RIRBulkImportView(generic.BulkImportView): 

321 queryset = RIR.objects.all() 

322 model_form = forms.RIRImportForm 

323 

324 

325@register_model_view(RIR, 'bulk_edit', path='edit', detail=False) 

326class RIRBulkEditView(generic.BulkEditView): 

327 queryset = RIR.objects.annotate( 

328 aggregate_count=count_related(Aggregate, 'rir') 

329 ) 

330 filterset = filtersets.RIRFilterSet 

331 table = tables.RIRTable 

332 form = forms.RIRBulkEditForm 

333 

334 

335@register_model_view(RIR, 'bulk_rename', path='rename', detail=False) 

336class RIRBulkRenameView(generic.BulkRenameView): 

337 queryset = RIR.objects.all() 

338 filterset = filtersets.RIRFilterSet 

339 

340 

341@register_model_view(RIR, 'bulk_delete', path='delete', detail=False) 

342class RIRBulkDeleteView(generic.BulkDeleteView): 

343 queryset = RIR.objects.annotate( 

344 aggregate_count=count_related(Aggregate, 'rir') 

345 ) 

346 filterset = filtersets.RIRFilterSet 

347 table = tables.RIRTable 

348 

349 

350# 

351# ASN ranges 

352# 

353 

354@register_model_view(ASNRange, 'list', path='', detail=False) 

355class ASNRangeListView(generic.ObjectListView): 

356 queryset = ASNRange.objects.annotate_asn_counts() 

357 filterset = filtersets.ASNRangeFilterSet 

358 filterset_form = forms.ASNRangeFilterForm 

359 table = tables.ASNRangeTable 

360 

361 

362@register_model_view(ASNRange) 

363class ASNRangeView(generic.ObjectView): 

364 template_name = 'generic/object.html' 

365 queryset = ASNRange.objects.all() 

366 layout = layout.SimpleLayout( 

367 breadcrumbs=[ 

368 Breadcrumb('rir', url=filtered_list_url('ipam:asnrange_list', 'rir_id')), 

369 ], 

370 left_panels=[ 

371 panels.ASNRangePanel(), 

372 TagsPanel(), 

373 ], 

374 right_panels=[ 

375 CommentsPanel(), 

376 CustomFieldsPanel(), 

377 ], 

378 ) 

379 

380 

381@register_model_view(ASNRange, 'asns') 

382class ASNRangeASNsView(generic.ObjectChildrenView): 

383 queryset = ASNRange.objects.all() 

384 child_model = ASN 

385 table = tables.ASNTable 

386 filterset = filtersets.ASNFilterSet 

387 filterset_form = forms.ASNFilterForm 

388 tab = ViewTab( 

389 label=_('ASNs'), 

390 badge=lambda x: x.get_child_asns().count(), 

391 permission='ipam.view_asn', 

392 weight=500 

393 ) 

394 

395 def get_children(self, request, parent): 

396 return parent.get_child_asns().restrict(request.user, 'view').annotate( 

397 site_count=count_related(Site, 'asns'), 

398 provider_count=count_related(Provider, 'asns') 

399 ) 

400 

401 

402@register_model_view(ASNRange, 'add', detail=False) 

403@register_model_view(ASNRange, 'edit') 

404class ASNRangeEditView(generic.ObjectEditView): 

405 queryset = ASNRange.objects.all() 

406 form = forms.ASNRangeForm 

407 

408 

409@register_model_view(ASNRange, 'delete') 

410class ASNRangeDeleteView(generic.ObjectDeleteView): 

411 queryset = ASNRange.objects.all() 

412 

413 

414@register_model_view(ASNRange, 'bulk_import', path='import', detail=False) 

415class ASNRangeBulkImportView(generic.BulkImportView): 

416 queryset = ASNRange.objects.all() 

417 model_form = forms.ASNRangeImportForm 

418 

419 

420@register_model_view(ASNRange, 'bulk_edit', path='edit', detail=False) 

421class ASNRangeBulkEditView(generic.BulkEditView): 

422 queryset = ASNRange.objects.annotate_asn_counts() 

423 filterset = filtersets.ASNRangeFilterSet 

424 table = tables.ASNRangeTable 

425 form = forms.ASNRangeBulkEditForm 

426 

427 

428@register_model_view(ASNRange, 'bulk_rename', path='rename', detail=False) 

429class ASNRangeBulkRenameView(generic.BulkRenameView): 

430 queryset = ASNRange.objects.all() 

431 filterset = filtersets.ASNRangeFilterSet 

432 

433 

434@register_model_view(ASNRange, 'bulk_delete', path='delete', detail=False) 

435class ASNRangeBulkDeleteView(generic.BulkDeleteView): 

436 queryset = ASNRange.objects.annotate_asn_counts() 

437 filterset = filtersets.ASNRangeFilterSet 

438 table = tables.ASNRangeTable 

439 

440 

441# 

442# ASNs 

443# 

444 

445@register_model_view(ASN, 'list', path='', detail=False) 

446class ASNListView(generic.ObjectListView): 

447 queryset = ASN.objects.annotate( 

448 site_count=count_related(Site, 'asns'), 

449 provider_count=count_related(Provider, 'asns') 

450 ) 

451 filterset = filtersets.ASNFilterSet 

452 filterset_form = forms.ASNFilterForm 

453 table = tables.ASNTable 

454 

455 

456@register_model_view(ASN) 

457class ASNView(GetRelatedModelsMixin, generic.ObjectView): 

458 template_name = 'generic/object.html' 

459 queryset = ASN.objects.all() 

460 layout = layout.SimpleLayout( 

461 breadcrumbs=[ 

462 Breadcrumb('rir', url=filtered_list_url('ipam:asn_list', 'rir_id')), 

463 ], 

464 left_panels=[ 

465 panels.ASNPanel(), 

466 TagsPanel(), 

467 ], 

468 right_panels=[ 

469 RelatedObjectsPanel(), 

470 CustomFieldsPanel(), 

471 CommentsPanel(), 

472 ], 

473 ) 

474 

475 def get_extra_context(self, request, instance): 

476 return { 

477 'related_models': self.get_related_models( 

478 request, 

479 instance, 

480 extra=( 

481 (Site.objects.restrict(request.user, 'view').filter(asns__in=[instance]), 'asn_id'), 

482 (Provider.objects.restrict(request.user, 'view').filter(asns__in=[instance]), 'asn_id'), 

483 ), 

484 ), 

485 } 

486 

487 

488@register_model_view(ASN, 'add', detail=False) 

489@register_model_view(ASN, 'edit') 

490class ASNEditView(generic.ObjectEditView): 

491 queryset = ASN.objects.all() 

492 form = forms.ASNForm 

493 

494 

495@register_model_view(ASN, 'delete') 

496class ASNDeleteView(generic.ObjectDeleteView): 

497 queryset = ASN.objects.all() 

498 

499 

500@register_model_view(ASN, 'bulk_import', path='import', detail=False) 

501class ASNBulkImportView(generic.BulkImportView): 

502 queryset = ASN.objects.all() 

503 model_form = forms.ASNImportForm 

504 

505 

506@register_model_view(ASN, 'bulk_edit', path='edit', detail=False) 

507class ASNBulkEditView(generic.BulkEditView): 

508 queryset = ASN.objects.annotate( 

509 site_count=count_related(Site, 'asns') 

510 ) 

511 filterset = filtersets.ASNFilterSet 

512 table = tables.ASNTable 

513 form = forms.ASNBulkEditForm 

514 

515 

516@register_model_view(ASN, 'bulk_rename', path='rename', detail=False) 

517class ASNBulkRenameView(generic.BulkRenameView): 

518 queryset = ASN.objects.all() 

519 filterset = filtersets.ASNFilterSet 

520 

521 

522@register_model_view(ASN, 'bulk_delete', path='delete', detail=False) 

523class ASNBulkDeleteView(generic.BulkDeleteView): 

524 queryset = ASN.objects.annotate( 

525 site_count=count_related(Site, 'asns') 

526 ) 

527 filterset = filtersets.ASNFilterSet 

528 table = tables.ASNTable 

529 

530 

531# 

532# Aggregates 

533# 

534 

535@register_model_view(Aggregate, 'list', path='', detail=False) 

536class AggregateListView(generic.ObjectListView): 

537 queryset = Aggregate.objects.annotate( 

538 child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ()) 

539 ) 

540 filterset = filtersets.AggregateFilterSet 

541 filterset_form = forms.AggregateFilterForm 

542 table = tables.AggregateTable 

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

544 

545 

546@register_model_view(Aggregate) 

547class AggregateView(generic.ObjectView): 

548 template_name = 'generic/object.html' 

549 queryset = Aggregate.objects.all() 

550 layout = layout.SimpleLayout( 

551 breadcrumbs=[ 

552 Breadcrumb('rir', url=filtered_list_url('ipam:aggregate_list', 'rir_id')), 

553 ], 

554 left_panels=[ 

555 panels.AggregatePanel(), 

556 ], 

557 right_panels=[ 

558 CustomFieldsPanel(), 

559 TagsPanel(), 

560 CommentsPanel(), 

561 ], 

562 ) 

563 

564 

565class ChildAvailabilityMixin: 

566 """ 

567 Mixin for ObjectChildrenView subclasses that render synthetic "available" rows 

568 (available IP space, prefixes, or VLANs) and must suppress them when the request 

569 activates a child object filter, so objects excluded by the filter are not 

570 misrepresented as available space. 

571 """ 

572 

573 def _get_detection_filterset(self, request): 

574 """ 

575 Return the bound FilterSet used to evaluate the request, or None if the view declares no 

576 filterset. ObjectChildrenView.get() has already built one and validated its form while 

577 resolving the child queryset, so reuse it rather than paying for a second FilterSet: 

578 get_filters() regenerates every dynamic lookup variant and NetBoxModelFilterSet.__init__ 

579 queries the custom fields for the model. Build one only for direct calls where get() has 

580 not run. 

581 """ 

582 if self.filterset_instance is not None: 

583 return self.filterset_instance 

584 if self.filterset is None: 

585 return None 

586 

587 return self.filterset(request.GET, request=request) 

588 

589 def _has_active_child_filters(self, request): 

590 """ 

591 Return True if the request supplies a valid, non-empty value for any filter declared by 

592 the view's filterset. Saved filters are expanded during filterset instantiation and 

593 dynamic custom field filters are registered on the instance, so both are detected. 

594 Permission constraints and parent scoping never appear in the request, so they cannot 

595 affect the result. The result is memoized because a single request evaluates it from 

596 both prep_table_data and get_extra_context. 

597 """ 

598 if hasattr(self, '_active_child_filters'): 

599 return self._active_child_filters 

600 

601 self._active_child_filters = False 

602 

603 # An empty request cannot activate a filter, so skip validating a form for nothing. 

604 if not request.GET: 

605 return False 

606 

607 filterset = self._get_detection_filterset(request) 

608 if filterset is None: 

609 return False 

610 

611 # A non-empty cleaned value means the request activated a declared filter. Emptiness 

612 # follows each filter's own semantics, so absent multi-value fields stay inactive. 

613 # This is a no-op for a reused FilterSet: .qs validated the form to build the queryset. 

614 filterset.form.is_valid() 

615 for name, value in filterset.form.cleaned_data.items(): 

616 if isinstance(filterset.filters[name], django_filters.MultipleChoiceFilter): 

617 if value: # mirrors MultipleChoiceFilter.filter() 

618 self._active_child_filters = True 

619 break 

620 elif value not in EMPTY_VALUES: # mirrors Filter.filter() 

621 self._active_child_filters = True 

622 break 

623 

624 return self._active_child_filters 

625 

626 

627@register_model_view(Aggregate, 'prefixes') 

628class AggregatePrefixesView(ChildAvailabilityMixin, generic.ObjectChildrenView): 

629 queryset = Aggregate.objects.all() 

630 child_model = Prefix 

631 table = tables.PrefixTable 

632 filterset = filtersets.PrefixFilterSet 

633 filterset_form = forms.PrefixFilterForm 

634 template_name = 'ipam/aggregate/prefixes.html' 

635 tab = ViewTab( 

636 label=_('Prefixes'), 

637 badge=lambda x: x.get_child_prefixes().count(), 

638 permission='ipam.view_prefix', 

639 weight=500 

640 ) 

641 

642 def get_children(self, request, parent): 

643 return Prefix.objects.restrict(request.user, 'view').filter( 

644 prefix__net_contained_or_equal=str(parent.prefix) 

645 ).prefetch_related('scope', 'role', 'tenant', 'tenant__group', 'vlan') 

646 

647 def prep_table_data(self, request, queryset, parent): 

648 # Determine whether to show assigned prefixes, available prefixes, or both 

649 show_available = bool(request.GET.get('show_available', 'true') == 'true') 

650 show_assigned = bool(request.GET.get('show_assigned', 'true') == 'true') 

651 

652 if show_available and self._has_active_child_filters(request): 

653 show_available = False 

654 

655 return add_requested_prefixes(parent.prefix, queryset, show_available, show_assigned) 

656 

657 def get_extra_context(self, request, instance): 

658 show_available = ( 

659 bool(request.GET.get('show_available', 'true') == 'true') and 

660 not self._has_active_child_filters(request) 

661 ) 

662 

663 return { 

664 'bulk_querystring': f'within={instance.prefix}', 

665 'first_available_prefix': instance.get_first_available_prefix(), 

666 'show_available': show_available, 

667 'show_assigned': bool(request.GET.get('show_assigned', 'true') == 'true'), 

668 } 

669 

670 

671@register_model_view(Aggregate, 'add', detail=False) 

672@register_model_view(Aggregate, 'edit') 

673class AggregateEditView(generic.ObjectEditView): 

674 queryset = Aggregate.objects.all() 

675 form = forms.AggregateForm 

676 

677 

678@register_model_view(Aggregate, 'delete') 

679class AggregateDeleteView(generic.ObjectDeleteView): 

680 queryset = Aggregate.objects.all() 

681 

682 

683@register_model_view(Aggregate, 'bulk_import', path='import', detail=False) 

684class AggregateBulkImportView(generic.BulkImportView): 

685 queryset = Aggregate.objects.all() 

686 model_form = forms.AggregateImportForm 

687 

688 

689@register_model_view(Aggregate, 'bulk_edit', path='edit', detail=False) 

690class AggregateBulkEditView(generic.BulkEditView): 

691 queryset = Aggregate.objects.annotate( 

692 child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ()) 

693 ) 

694 filterset = filtersets.AggregateFilterSet 

695 table = tables.AggregateTable 

696 form = forms.AggregateBulkEditForm 

697 

698 

699@register_model_view(Aggregate, 'bulk_delete', path='delete', detail=False) 

700class AggregateBulkDeleteView(generic.BulkDeleteView): 

701 queryset = Aggregate.objects.annotate( 

702 child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ()) 

703 ) 

704 filterset = filtersets.AggregateFilterSet 

705 table = tables.AggregateTable 

706 

707 

708# 

709# Prefix/VLAN roles 

710# 

711 

712@register_model_view(Role, 'list', path='', detail=False) 

713class RoleListView(generic.ObjectListView): 

714 queryset = Role.objects.annotate( 

715 prefix_count=count_related(Prefix, 'role'), 

716 iprange_count=count_related(IPRange, 'role'), 

717 vlan_count=count_related(VLAN, 'role'), 

718 asn_count=count_related(ASN, 'role') 

719 ) 

720 filterset = filtersets.RoleFilterSet 

721 filterset_form = forms.RoleFilterForm 

722 table = tables.RoleTable 

723 

724 

725@register_model_view(Role) 

726class RoleView(GetRelatedModelsMixin, generic.ObjectView): 

727 queryset = Role.objects.all() 

728 layout = layout.SimpleLayout( 

729 left_panels=[ 

730 panels.RolePanel(), 

731 TagsPanel(), 

732 ], 

733 right_panels=[ 

734 RelatedObjectsPanel(), 

735 CommentsPanel(), 

736 CustomFieldsPanel(), 

737 ], 

738 ) 

739 

740 def get_extra_context(self, request, instance): 

741 return { 

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

743 } 

744 

745 

746@register_model_view(Role, 'add', detail=False) 

747@register_model_view(Role, 'edit') 

748class RoleEditView(generic.ObjectEditView): 

749 queryset = Role.objects.all() 

750 form = forms.RoleForm 

751 

752 

753@register_model_view(Role, 'delete') 

754class RoleDeleteView(generic.ObjectDeleteView): 

755 queryset = Role.objects.all() 

756 

757 

758@register_model_view(Role, 'bulk_import', path='import', detail=False) 

759class RoleBulkImportView(generic.BulkImportView): 

760 queryset = Role.objects.all() 

761 model_form = forms.RoleImportForm 

762 

763 

764@register_model_view(Role, 'bulk_edit', path='edit', detail=False) 

765class RoleBulkEditView(generic.BulkEditView): 

766 queryset = Role.objects.all() 

767 filterset = filtersets.RoleFilterSet 

768 table = tables.RoleTable 

769 form = forms.RoleBulkEditForm 

770 

771 

772@register_model_view(Role, 'bulk_rename', path='rename', detail=False) 

773class RoleBulkRenameView(generic.BulkRenameView): 

774 queryset = Role.objects.all() 

775 filterset = filtersets.RoleFilterSet 

776 

777 

778@register_model_view(Role, 'bulk_delete', path='delete', detail=False) 

779class RoleBulkDeleteView(generic.BulkDeleteView): 

780 queryset = Role.objects.all() 

781 filterset = filtersets.RoleFilterSet 

782 table = tables.RoleTable 

783 

784 

785# 

786# Prefixes 

787# 

788 

789@register_model_view(Prefix, 'list', path='', detail=False) 

790class PrefixListView(generic.ObjectListView): 

791 queryset = Prefix.objects.all() 

792 filterset = filtersets.PrefixFilterSet 

793 filterset_form = forms.PrefixFilterForm 

794 table = tables.PrefixTable 

795 template_name = 'ipam/prefix_list.html' 

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

797 

798 

799@register_model_view(Prefix) 

800class PrefixView(generic.ObjectView): 

801 queryset = Prefix.objects.all() 

802 layout = layout.SimpleLayout( 

803 breadcrumbs=[ 

804 Breadcrumb('vrf', url=filtered_list_url('ipam:prefix_list', 'vrf_id')), 

805 ], 

806 left_panels=[ 

807 panels.PrefixPanel(), 

808 ], 

809 right_panels=[ 

810 TemplatePanel('ipam/panels/prefix_addressing.html'), 

811 CustomFieldsPanel(), 

812 TagsPanel(), 

813 CommentsPanel(), 

814 ], 

815 bottom_panels=[ 

816 ContextTablePanel('duplicate_prefix_table', title=_('Duplicate prefixes')), 

817 ContextTablePanel('parent_prefix_table', title=_('Parent prefixes')), 

818 ], 

819 ) 

820 

821 def get_extra_context(self, request, instance): 

822 # Parent prefixes table 

823 parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter( 

824 Q(vrf=instance.vrf) | Q(vrf__isnull=True, status=PrefixStatusChoices.STATUS_CONTAINER) 

825 ).filter( 

826 prefix__net_contains=str(instance.prefix) 

827 ).prefetch_related( 

828 'scope', 'role', 'tenant', 'vlan', 

829 ) 

830 parent_prefix_table = tables.PrefixTable( 

831 list(parent_prefixes), 

832 exclude=('vrf', 'utilization'), 

833 orderable=False 

834 ) 

835 parent_prefix_table.configure(request) 

836 

837 # Duplicate prefixes table 

838 duplicate_prefixes = Prefix.objects.restrict(request.user, 'view').filter( 

839 vrf=instance.vrf, prefix=str(instance.prefix) 

840 ).exclude( 

841 pk=instance.pk 

842 ).prefetch_related( 

843 'scope', 'role', 'tenant', 'vlan', 

844 ) 

845 duplicate_prefix_table = tables.PrefixTable( 

846 list(duplicate_prefixes), 

847 exclude=('vrf', 'utilization'), 

848 orderable=False 

849 ) 

850 duplicate_prefix_table.configure(request) 

851 

852 context = { 

853 'parent_prefix_table': parent_prefix_table, 

854 } 

855 if duplicate_prefixes.exists(): 

856 context['duplicate_prefix_table'] = duplicate_prefix_table 

857 return context 

858 

859 

860@register_model_view(Prefix, 'prefixes') 

861class PrefixPrefixesView(ChildAvailabilityMixin, generic.ObjectChildrenView): 

862 queryset = Prefix.objects.all() 

863 child_model = Prefix 

864 table = tables.PrefixTable 

865 filterset = filtersets.PrefixFilterSet 

866 filterset_form = forms.PrefixFilterForm 

867 template_name = 'ipam/prefix/prefixes.html' 

868 tab = ViewTab( 

869 label=_('Child Prefixes'), 

870 badge=lambda x: x.get_child_prefixes().count(), 

871 permission='ipam.view_prefix', 

872 weight=500 

873 ) 

874 

875 def get_children(self, request, parent): 

876 return parent.get_child_prefixes().restrict(request.user, 'view').prefetch_related( 

877 'scope', 'vrf', 'vlan', 'role', 'tenant', 'tenant__group' 

878 ) 

879 

880 def prep_table_data(self, request, queryset, parent): 

881 # Determine whether to show assigned prefixes, available prefixes, or both 

882 show_available = bool(request.GET.get('show_available', 'true') == 'true') 

883 show_assigned = bool(request.GET.get('show_assigned', 'true') == 'true') 

884 

885 if show_available and self._has_active_child_filters(request): 

886 show_available = False 

887 

888 return add_requested_prefixes(parent.prefix, queryset, show_available, show_assigned) 

889 

890 def get_extra_context(self, request, instance): 

891 show_available = ( 

892 bool(request.GET.get('show_available', 'true') == 'true') and 

893 not self._has_active_child_filters(request) 

894 ) 

895 

896 return { 

897 'bulk_querystring': f"vrf_id={instance.vrf.pk if instance.vrf else '0'}&within={instance.prefix}", 

898 'first_available_prefix': instance.get_first_available_prefix(), 

899 'show_available': show_available, 

900 'show_assigned': bool(request.GET.get('show_assigned', 'true') == 'true'), 

901 } 

902 

903 

904@register_model_view(Prefix, 'ipranges', path='ip-ranges') 

905class PrefixIPRangesView(generic.ObjectChildrenView): 

906 queryset = Prefix.objects.all() 

907 child_model = IPRange 

908 table = tables.IPRangeTable 

909 filterset = filtersets.IPRangeFilterSet 

910 filterset_form = forms.IPRangeFilterForm 

911 template_name = 'ipam/prefix/ip_ranges.html' 

912 tab = ViewTab( 

913 label=_('Child Ranges'), 

914 badge=lambda x: x.get_child_ranges().count(), 

915 permission='ipam.view_iprange', 

916 weight=600 

917 ) 

918 

919 def get_children(self, request, parent): 

920 return parent.get_child_ranges().restrict(request.user, 'view').prefetch_related( 

921 'tenant__group', 

922 ) 

923 

924 def get_extra_context(self, request, instance): 

925 return { 

926 'bulk_querystring': f"vrf_id={instance.vrf.pk if instance.vrf else '0'}&parent={instance.prefix}", 

927 'first_available_ip': instance.get_first_available_ip(), 

928 } 

929 

930 

931@register_model_view(Prefix, 'ipaddresses', path='ip-addresses') 

932class PrefixIPAddressesView(ChildAvailabilityMixin, generic.ObjectChildrenView): 

933 queryset = Prefix.objects.all() 

934 child_model = IPAddress 

935 table = tables.AnnotatedIPAddressTable 

936 filterset = filtersets.IPAddressFilterSet 

937 filterset_form = forms.IPAddressFilterForm 

938 template_name = 'ipam/prefix/ip_addresses.html' 

939 tab = ViewTab( 

940 label=_('IP Addresses'), 

941 badge=lambda x: x.get_child_ips().count(), 

942 permission='ipam.view_ipaddress', 

943 weight=700 

944 ) 

945 

946 def get_children(self, request, parent): 

947 return parent.get_child_ips().restrict(request.user, 'view').prefetch_related('vrf', 'tenant', 'tenant__group') 

948 

949 def prep_table_data(self, request, queryset, parent): 

950 # Ordering is checked first: it reads request.GET directly, so a sorted request never 

951 # builds the detection filterset. 

952 if not get_table_ordering(request, self.table) and not self._has_active_child_filters(request): 

953 ip_ranges = parent.get_child_ranges(mark_populated=True).restrict(request.user, 'view') 

954 return annotate_ip_space(parent, ip_addresses=queryset, ip_ranges=ip_ranges) 

955 

956 return super().prep_table_data(request, queryset, parent) 

957 

958 def get_extra_context(self, request, instance): 

959 return { 

960 'bulk_querystring': f"vrf_id={instance.vrf.pk if instance.vrf else '0'}&parent={instance.prefix}", 

961 'first_available_ip': instance.get_first_available_ip(), 

962 } 

963 

964 

965@register_model_view(Prefix, 'add', detail=False) 

966@register_model_view(Prefix, 'edit') 

967class PrefixEditView(generic.ObjectEditView): 

968 queryset = Prefix.objects.all() 

969 form = forms.PrefixForm 

970 template_name = 'ipam/prefix_edit.html' 

971 

972 

973@register_model_view(Prefix, 'delete') 

974class PrefixDeleteView(generic.ObjectDeleteView): 

975 queryset = Prefix.objects.all() 

976 

977 

978@register_model_view(Prefix, 'bulk_add', path='bulk-add', detail=False) 

979class PrefixBulkCreateView(generic.BulkCreateView): 

980 queryset = Prefix.objects.all() 

981 form = forms.IPNetworkBulkCreateForm 

982 model_form = forms.PrefixBulkAddForm 

983 pattern_target = 'prefix' 

984 template_name = 'ipam/prefix_bulk_add.html' 

985 

986 

987@register_model_view(Prefix, 'bulk_import', path='import', detail=False) 

988class PrefixBulkImportView(generic.BulkImportView): 

989 queryset = Prefix.objects.all() 

990 model_form = forms.PrefixImportForm 

991 

992 

993@register_model_view(Prefix, 'bulk_edit', path='edit', detail=False) 

994class PrefixBulkEditView(generic.BulkEditView): 

995 queryset = Prefix.objects.prefetch_related('vrf__tenant') 

996 filterset = filtersets.PrefixFilterSet 

997 table = tables.PrefixTable 

998 form = forms.PrefixBulkEditForm 

999 

1000 

1001@register_model_view(Prefix, 'bulk_delete', path='delete', detail=False) 

1002class PrefixBulkDeleteView(generic.BulkDeleteView): 

1003 queryset = Prefix.objects.prefetch_related('vrf__tenant') 

1004 filterset = filtersets.PrefixFilterSet 

1005 table = tables.PrefixTable 

1006 

1007 

1008# 

1009# IP Ranges 

1010# 

1011 

1012@register_model_view(IPRange, 'list', path='', detail=False) 

1013class IPRangeListView(generic.ObjectListView): 

1014 queryset = IPRange.objects.all() 

1015 filterset = filtersets.IPRangeFilterSet 

1016 filterset_form = forms.IPRangeFilterForm 

1017 table = tables.IPRangeTable 

1018 

1019 

1020@register_model_view(IPRange) 

1021class IPRangeView(generic.ObjectView): 

1022 template_name = 'generic/object.html' 

1023 queryset = IPRange.objects.all() 

1024 layout = layout.SimpleLayout( 

1025 breadcrumbs=[ 

1026 Breadcrumb('vrf', url=filtered_list_url('ipam:iprange_list', 'vrf_id')), 

1027 ], 

1028 left_panels=[ 

1029 panels.IPRangePanel(), 

1030 ], 

1031 right_panels=[ 

1032 TagsPanel(), 

1033 CustomFieldsPanel(), 

1034 CommentsPanel(), 

1035 ], 

1036 bottom_panels=[ 

1037 ContextTablePanel('parent_prefixes_table', title=_('Parent prefixes')), 

1038 ], 

1039 ) 

1040 

1041 def get_extra_context(self, request, instance): 

1042 

1043 # Parent prefixes table 

1044 parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter( 

1045 Q(prefix__net_contains_or_equals=str(instance.start_address.ip)), 

1046 Q(prefix__net_contains_or_equals=str(instance.end_address.ip)), 

1047 vrf=instance.vrf 

1048 ).prefetch_related( 

1049 'scope', 'role', 'tenant', 'vlan', 'role' 

1050 ) 

1051 parent_prefixes_table = tables.PrefixTable( 

1052 list(parent_prefixes), 

1053 exclude=('vrf', 'utilization'), 

1054 orderable=False 

1055 ) 

1056 parent_prefixes_table.configure(request) 

1057 

1058 return { 

1059 'parent_prefixes_table': parent_prefixes_table, 

1060 } 

1061 

1062 

1063@register_model_view(IPRange, 'ipaddresses', path='ip-addresses') 

1064class IPRangeIPAddressesView(generic.ObjectChildrenView): 

1065 queryset = IPRange.objects.all() 

1066 child_model = IPAddress 

1067 table = tables.IPAddressTable 

1068 filterset = filtersets.IPAddressFilterSet 

1069 filterset_form = forms.IPRangeFilterForm 

1070 template_name = 'ipam/iprange/ip_addresses.html' 

1071 tab = ViewTab( 

1072 label=_('IP Addresses'), 

1073 badge=lambda x: x.get_child_ips().count(), 

1074 permission='ipam.view_ipaddress', 

1075 weight=500 

1076 ) 

1077 

1078 def get_children(self, request, parent): 

1079 return parent.get_child_ips().restrict(request.user, 'view') 

1080 

1081 

1082@register_model_view(IPRange, 'add', detail=False) 

1083@register_model_view(IPRange, 'edit') 

1084class IPRangeEditView(generic.ObjectEditView): 

1085 queryset = IPRange.objects.all() 

1086 form = forms.IPRangeForm 

1087 

1088 

1089@register_model_view(IPRange, 'delete') 

1090class IPRangeDeleteView(generic.ObjectDeleteView): 

1091 queryset = IPRange.objects.all() 

1092 

1093 

1094@register_model_view(IPRange, 'bulk_import', path='import', detail=False) 

1095class IPRangeBulkImportView(generic.BulkImportView): 

1096 queryset = IPRange.objects.all() 

1097 model_form = forms.IPRangeImportForm 

1098 

1099 

1100@register_model_view(IPRange, 'bulk_edit', path='edit', detail=False) 

1101class IPRangeBulkEditView(generic.BulkEditView): 

1102 queryset = IPRange.objects.all() 

1103 filterset = filtersets.IPRangeFilterSet 

1104 table = tables.IPRangeTable 

1105 form = forms.IPRangeBulkEditForm 

1106 

1107 

1108@register_model_view(IPRange, 'bulk_rename', path='rename', detail=False) 

1109class IPRangeBulkRenameView(generic.BulkRenameView): 

1110 queryset = IPRange.objects.all() 

1111 filterset = filtersets.IPRangeFilterSet 

1112 

1113 

1114@register_model_view(IPRange, 'bulk_delete', path='delete', detail=False) 

1115class IPRangeBulkDeleteView(generic.BulkDeleteView): 

1116 queryset = IPRange.objects.all() 

1117 filterset = filtersets.IPRangeFilterSet 

1118 table = tables.IPRangeTable 

1119 

1120 

1121# 

1122# IP addresses 

1123# 

1124 

1125@register_model_view(IPAddress, 'list', path='', detail=False) 

1126class IPAddressListView(generic.ObjectListView): 

1127 queryset = IPAddress.objects.all() 

1128 filterset = filtersets.IPAddressFilterSet 

1129 filterset_form = forms.IPAddressFilterForm 

1130 table = tables.IPAddressTable 

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

1132 

1133 

1134@register_model_view(IPAddress) 

1135class IPAddressView(generic.ObjectView): 

1136 template_name = 'generic/object.html' 

1137 queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant') 

1138 layout = layout.SimpleLayout( 

1139 breadcrumbs=[ 

1140 Breadcrumb('vrf', url=filtered_list_url('ipam:ipaddress_list', 'vrf_id')), 

1141 ], 

1142 left_panels=[ 

1143 panels.IPAddressPanel(), 

1144 TagsPanel(), 

1145 CustomFieldsPanel(), 

1146 CommentsPanel(), 

1147 ], 

1148 right_panels=[ 

1149 ContextTablePanel('parent_prefixes_table', title=_('Parent prefixes')), 

1150 ContextTablePanel('duplicate_ips_table', title=_('Duplicate IPs')), 

1151 ObjectsTablePanel( 

1152 'ipam.service', 

1153 filters={'ip_address_id': lambda ctx: ctx['object'].pk}, 

1154 title=_('Application services'), 

1155 ), 

1156 ], 

1157 ) 

1158 

1159 def get_extra_context(self, request, instance): 

1160 # Parent prefixes table 

1161 parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter( 

1162 vrf=instance.vrf, 

1163 prefix__net_contains_or_equals=str(instance.address.ip) 

1164 ).prefetch_related( 

1165 'scope', 'role' 

1166 ) 

1167 parent_prefixes_table = tables.PrefixTable( 

1168 list(parent_prefixes), 

1169 exclude=('vrf', 'utilization'), 

1170 orderable=False 

1171 ) 

1172 parent_prefixes_table.configure(request) 

1173 

1174 # Duplicate IPs table 

1175 duplicate_ips = IPAddress.objects.restrict(request.user, 'view').filter( 

1176 vrf=instance.vrf, 

1177 address=str(instance.address) 

1178 ).exclude( 

1179 pk=instance.pk 

1180 ).prefetch_related( 

1181 'nat_inside' 

1182 ) 

1183 # Exclude anycast IPs if this IP is anycast 

1184 if instance.role == IPAddressRoleChoices.ROLE_ANYCAST: 

1185 duplicate_ips = duplicate_ips.exclude(role=IPAddressRoleChoices.ROLE_ANYCAST) 

1186 # Limit to a maximum of 10 duplicates displayed here 

1187 duplicate_ips_table = tables.IPAddressTable(duplicate_ips[:10], orderable=False) 

1188 duplicate_ips_table.configure(request) 

1189 

1190 context = { 

1191 'parent_prefixes_table': parent_prefixes_table, 

1192 } 

1193 if duplicate_ips.exists(): 

1194 context['duplicate_ips_table'] = duplicate_ips_table 

1195 return context 

1196 

1197 

1198@register_model_view(IPAddress, 'add', detail=False) 

1199@register_model_view(IPAddress, 'edit') 

1200class IPAddressEditView(generic.ObjectEditView): 

1201 queryset = IPAddress.objects.all() 

1202 form = forms.IPAddressForm 

1203 template_name = 'ipam/ipaddress_edit.html' 

1204 

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

1206 

1207 if 'interface' in request.GET: 

1208 try: 

1209 obj.assigned_object = Interface.objects.get(pk=request.GET['interface']) 

1210 except (ValueError, Interface.DoesNotExist): 

1211 pass 

1212 

1213 elif 'vminterface' in request.GET: 

1214 try: 

1215 obj.assigned_object = VMInterface.objects.get(pk=request.GET['vminterface']) 

1216 except (ValueError, VMInterface.DoesNotExist): 

1217 pass 

1218 

1219 elif 'fhrpgroup' in request.GET: 

1220 try: 

1221 obj.assigned_object = FHRPGroup.objects.get(pk=request.GET['fhrpgroup']) 

1222 except (ValueError, FHRPGroup.DoesNotExist): 

1223 pass 

1224 

1225 return obj 

1226 

1227 def get_extra_addanother_params(self, request): 

1228 if 'interface' in request.GET: 

1229 return {'interface': request.GET['interface']} 

1230 if 'vminterface' in request.GET: 

1231 return {'vminterface': request.GET['vminterface']} 

1232 return {} 

1233 

1234 

1235# TODO: Standardize or remove this view 

1236@register_model_view(IPAddress, 'assign', path='assign', detail=False) 

1237class IPAddressAssignView(GetReturnURLMixin, generic.ObjectView): 

1238 """ 

1239 Search for IPAddresses to be assigned to an Interface. 

1240 """ 

1241 queryset = IPAddress.objects.all() 

1242 

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

1244 

1245 # Redirect user if an interface has not been provided 

1246 if 'interface' not in request.GET and 'vminterface' not in request.GET: 

1247 return redirect('ipam:ipaddress_add') 

1248 

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

1250 

1251 def get(self, request): 

1252 form = forms.IPAddressAssignForm() 

1253 

1254 return render(request, 'ipam/ipaddress_assign.html', { 

1255 'form': form, 

1256 'return_url': self.get_return_url(request), 

1257 }) 

1258 

1259 def post(self, request): 

1260 form = forms.IPAddressAssignForm(request.POST) 

1261 table = None 

1262 

1263 if form.is_valid(): 

1264 addresses = self.queryset.prefetch_related('vrf', 'tenant') 

1265 # Limit to 100 results 

1266 addresses = filtersets.IPAddressFilterSet(request.POST, addresses).qs[:100] 

1267 table = tables.IPAddressAssignTable(addresses) 

1268 table.configure(request) 

1269 

1270 return render(request, 'ipam/ipaddress_assign.html', { 

1271 'form': form, 

1272 'table': table, 

1273 'return_url': self.get_return_url(request), 

1274 }) 

1275 

1276 

1277@register_model_view(IPAddress, 'delete') 

1278class IPAddressDeleteView(generic.ObjectDeleteView): 

1279 queryset = IPAddress.objects.all() 

1280 

1281 

1282@register_model_view(IPAddress, 'bulk_add', path='bulk-add', detail=False) 

1283class IPAddressBulkCreateView(generic.BulkCreateView): 

1284 queryset = IPAddress.objects.all() 

1285 form = forms.IPNetworkBulkCreateForm 

1286 model_form = forms.IPAddressBulkAddForm 

1287 pattern_target = 'address' 

1288 template_name = 'ipam/ipaddress_bulk_add.html' 

1289 

1290 

1291@register_model_view(IPAddress, 'bulk_import', path='import', detail=False) 

1292class IPAddressBulkImportView(generic.BulkImportView): 

1293 queryset = IPAddress.objects.all() 

1294 model_form = forms.IPAddressImportForm 

1295 

1296 

1297@register_model_view(IPAddress, 'bulk_edit', path='edit', detail=False) 

1298class IPAddressBulkEditView(generic.BulkEditView): 

1299 queryset = IPAddress.objects.prefetch_related('vrf__tenant') 

1300 filterset = filtersets.IPAddressFilterSet 

1301 table = tables.IPAddressTable 

1302 form = forms.IPAddressBulkEditForm 

1303 

1304 

1305@register_model_view(IPAddress, 'bulk_delete', path='delete', detail=False) 

1306class IPAddressBulkDeleteView(generic.BulkDeleteView): 

1307 queryset = IPAddress.objects.prefetch_related('vrf__tenant') 

1308 filterset = filtersets.IPAddressFilterSet 

1309 table = tables.IPAddressTable 

1310 

1311 

1312@register_model_view(IPAddress, 'related_ips', path='related-ip-addresses') 

1313class IPAddressRelatedIPsView(generic.ObjectChildrenView): 

1314 queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant') 

1315 child_model = IPAddress 

1316 table = tables.IPAddressTable 

1317 filterset = filtersets.IPAddressFilterSet 

1318 filterset_form = forms.IPAddressFilterForm 

1319 tab = ViewTab( 

1320 label=_('Related IPs'), 

1321 badge=lambda x: x.get_related_ips().count(), 

1322 weight=500, 

1323 hide_if_empty=True, 

1324 ) 

1325 

1326 def get_children(self, request, parent): 

1327 return parent.get_related_ips().restrict(request.user, 'view') 

1328 

1329 

1330# 

1331# VLAN groups 

1332# 

1333 

1334@register_model_view(VLANGroup, 'list', path='', detail=False) 

1335class VLANGroupListView(generic.ObjectListView): 

1336 queryset = VLANGroup.objects.annotate_utilization() 

1337 filterset = filtersets.VLANGroupFilterSet 

1338 filterset_form = forms.VLANGroupFilterForm 

1339 table = tables.VLANGroupTable 

1340 

1341 

1342@register_model_view(VLANGroup) 

1343class VLANGroupView(GetRelatedModelsMixin, generic.ObjectView): 

1344 queryset = VLANGroup.objects.annotate_utilization() 

1345 layout = layout.SimpleLayout( 

1346 breadcrumbs=[ 

1347 Breadcrumb('scope'), 

1348 ], 

1349 left_panels=[ 

1350 panels.VLANGroupPanel(), 

1351 TagsPanel(), 

1352 ], 

1353 right_panels=[ 

1354 RelatedObjectsPanel(), 

1355 CommentsPanel(), 

1356 CustomFieldsPanel(), 

1357 ], 

1358 ) 

1359 

1360 def get_extra_context(self, request, instance): 

1361 return { 

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

1363 } 

1364 

1365 

1366@register_model_view(VLANGroup, 'add', detail=False) 

1367@register_model_view(VLANGroup, 'edit') 

1368class VLANGroupEditView(generic.ObjectEditView): 

1369 queryset = VLANGroup.objects.all() 

1370 form = forms.VLANGroupForm 

1371 

1372 

1373@register_model_view(VLANGroup, 'delete') 

1374class VLANGroupDeleteView(generic.ObjectDeleteView): 

1375 queryset = VLANGroup.objects.all() 

1376 

1377 

1378@register_model_view(VLANGroup, 'bulk_import', path='import', detail=False) 

1379class VLANGroupBulkImportView(generic.BulkImportView): 

1380 queryset = VLANGroup.objects.all() 

1381 model_form = forms.VLANGroupImportForm 

1382 

1383 

1384@register_model_view(VLANGroup, 'bulk_edit', path='edit', detail=False) 

1385class VLANGroupBulkEditView(generic.BulkEditView): 

1386 queryset = VLANGroup.objects.annotate_utilization().prefetch_related('tags') 

1387 filterset = filtersets.VLANGroupFilterSet 

1388 table = tables.VLANGroupTable 

1389 form = forms.VLANGroupBulkEditForm 

1390 

1391 

1392@register_model_view(VLANGroup, 'bulk_rename', path='rename', detail=False) 

1393class VLANGroupBulkRenameView(generic.BulkRenameView): 

1394 queryset = VLANGroup.objects.all() 

1395 filterset = filtersets.VLANGroupFilterSet 

1396 

1397 

1398@register_model_view(VLANGroup, 'bulk_delete', path='delete', detail=False) 

1399class VLANGroupBulkDeleteView(generic.BulkDeleteView): 

1400 queryset = VLANGroup.objects.annotate_utilization().prefetch_related('tags') 

1401 filterset = filtersets.VLANGroupFilterSet 

1402 table = tables.VLANGroupTable 

1403 

1404 

1405@register_model_view(VLANGroup, 'vlans') 

1406class VLANGroupVLANsView(ChildAvailabilityMixin, generic.ObjectChildrenView): 

1407 queryset = VLANGroup.objects.all() 

1408 child_model = VLAN 

1409 table = tables.VLANTable 

1410 filterset = filtersets.VLANFilterSet 

1411 filterset_form = forms.VLANFilterForm 

1412 tab = ViewTab( 

1413 label=_('VLANs'), 

1414 badge=lambda x: x.get_child_vlans().count(), 

1415 permission='ipam.view_vlan', 

1416 weight=500 

1417 ) 

1418 

1419 def get_children(self, request, parent): 

1420 return parent.get_child_vlans().restrict(request.user, 'view').prefetch_related( 

1421 Prefetch('prefixes', queryset=Prefix.objects.restrict(request.user)), 

1422 'tenant', 'site', 'role', 

1423 ) 

1424 

1425 def prep_table_data(self, request, queryset, parent): 

1426 # Skip synthetic available rows under active filters: filtered-out VLANs would otherwise look available. 

1427 if not get_table_ordering(request, self.table) and not self._has_active_child_filters(request): 

1428 return add_available_vlans(queryset, parent) 

1429 

1430 return super().prep_table_data(request, queryset, parent) 

1431 

1432 

1433# 

1434# VLAN Translation Policies 

1435# 

1436 

1437@register_model_view(VLANTranslationPolicy, 'list', path='', detail=False) 

1438class VLANTranslationPolicyListView(generic.ObjectListView): 

1439 queryset = VLANTranslationPolicy.objects.annotate( 

1440 rule_count=count_related(VLANTranslationRule, 'policy'), 

1441 ) 

1442 filterset = filtersets.VLANTranslationPolicyFilterSet 

1443 filterset_form = forms.VLANTranslationPolicyFilterForm 

1444 table = tables.VLANTranslationPolicyTable 

1445 

1446 

1447@register_model_view(VLANTranslationPolicy) 

1448class VLANTranslationPolicyView(generic.ObjectView): 

1449 queryset = VLANTranslationPolicy.objects.all() 

1450 template_name = 'generic/object.html' 

1451 layout = layout.SimpleLayout( 

1452 left_panels=[ 

1453 panels.VLANTranslationPolicyPanel(), 

1454 ], 

1455 right_panels=[ 

1456 TagsPanel(), 

1457 CustomFieldsPanel(), 

1458 CommentsPanel(), 

1459 ], 

1460 bottom_panels=[ 

1461 ObjectsTablePanel( 

1462 'ipam.vlantranslationrule', 

1463 filters={'policy_id': lambda ctx: ctx['object'].pk}, 

1464 title=_('VLAN translation rules'), 

1465 exclude_columns=['policy'], 

1466 actions=[ 

1467 actions.AddObject( 

1468 'ipam.vlantranslationrule', 

1469 url_params={'policy': lambda ctx: ctx['object'].pk}, 

1470 label=_('Add Rule'), 

1471 ), 

1472 ], 

1473 ), 

1474 ], 

1475 ) 

1476 

1477 

1478@register_model_view(VLANTranslationPolicy, 'add', detail=False) 

1479@register_model_view(VLANTranslationPolicy, 'edit') 

1480class VLANTranslationPolicyEditView(generic.ObjectEditView): 

1481 queryset = VLANTranslationPolicy.objects.all() 

1482 form = forms.VLANTranslationPolicyForm 

1483 

1484 

1485@register_model_view(VLANTranslationPolicy, 'delete') 

1486class VLANTranslationPolicyDeleteView(generic.ObjectDeleteView): 

1487 queryset = VLANTranslationPolicy.objects.all() 

1488 

1489 

1490@register_model_view(VLANTranslationPolicy, 'bulk_import', path='import', detail=False) 

1491class VLANTranslationPolicyBulkImportView(generic.BulkImportView): 

1492 queryset = VLANTranslationPolicy.objects.all() 

1493 model_form = forms.VLANTranslationPolicyImportForm 

1494 

1495 

1496@register_model_view(VLANTranslationPolicy, 'bulk_edit', path='edit', detail=False) 

1497class VLANTranslationPolicyBulkEditView(generic.BulkEditView): 

1498 queryset = VLANTranslationPolicy.objects.all() 

1499 filterset = filtersets.VLANTranslationPolicyFilterSet 

1500 table = tables.VLANTranslationPolicyTable 

1501 form = forms.VLANTranslationPolicyBulkEditForm 

1502 

1503 

1504@register_model_view(VLANTranslationPolicy, 'bulk_rename', path='rename', detail=False) 

1505class VLANTranslationPolicyBulkRenameView(generic.BulkRenameView): 

1506 queryset = VLANTranslationPolicy.objects.all() 

1507 filterset = filtersets.VLANTranslationPolicyFilterSet 

1508 

1509 

1510@register_model_view(VLANTranslationPolicy, 'bulk_delete', path='delete', detail=False) 

1511class VLANTranslationPolicyBulkDeleteView(generic.BulkDeleteView): 

1512 queryset = VLANTranslationPolicy.objects.all() 

1513 filterset = filtersets.VLANTranslationPolicyFilterSet 

1514 table = tables.VLANTranslationPolicyTable 

1515 

1516 

1517# 

1518# VLAN Translation Rules 

1519# 

1520 

1521@register_model_view(VLANTranslationRule, 'list', path='', detail=False) 

1522class VLANTranslationRuleListView(generic.ObjectListView): 

1523 queryset = VLANTranslationRule.objects.all() 

1524 filterset = filtersets.VLANTranslationRuleFilterSet 

1525 filterset_form = forms.VLANTranslationRuleFilterForm 

1526 table = tables.VLANTranslationRuleTable 

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

1528 

1529 

1530@register_model_view(VLANTranslationRule) 

1531class VLANTranslationRuleView(generic.ObjectView): 

1532 queryset = VLANTranslationRule.objects.all() 

1533 template_name = 'generic/object.html' 

1534 layout = layout.SimpleLayout( 

1535 left_panels=[ 

1536 panels.VLANTranslationRulePanel(), 

1537 ], 

1538 right_panels=[ 

1539 TagsPanel(), 

1540 CustomFieldsPanel(), 

1541 ], 

1542 ) 

1543 

1544 

1545@register_model_view(VLANTranslationRule, 'add', detail=False) 

1546@register_model_view(VLANTranslationRule, 'edit') 

1547class VLANTranslationRuleEditView(generic.ObjectEditView): 

1548 queryset = VLANTranslationRule.objects.all() 

1549 form = forms.VLANTranslationRuleForm 

1550 

1551 

1552@register_model_view(VLANTranslationRule, 'delete') 

1553class VLANTranslationRuleDeleteView(generic.ObjectDeleteView): 

1554 queryset = VLANTranslationRule.objects.all() 

1555 

1556 

1557@register_model_view(VLANTranslationRule, 'bulk_import', path='import', detail=False) 

1558class VLANTranslationRuleBulkImportView(generic.BulkImportView): 

1559 queryset = VLANTranslationRule.objects.all() 

1560 model_form = forms.VLANTranslationRuleImportForm 

1561 

1562 

1563@register_model_view(VLANTranslationRule, 'bulk_edit', path='edit', detail=False) 

1564class VLANTranslationRuleBulkEditView(generic.BulkEditView): 

1565 queryset = VLANTranslationRule.objects.all() 

1566 filterset = filtersets.VLANTranslationRuleFilterSet 

1567 table = tables.VLANTranslationRuleTable 

1568 form = forms.VLANTranslationRuleBulkEditForm 

1569 

1570 

1571@register_model_view(VLANTranslationRule, 'bulk_delete', path='delete', detail=False) 

1572class VLANTranslationRuleBulkDeleteView(generic.BulkDeleteView): 

1573 queryset = VLANTranslationRule.objects.all() 

1574 filterset = filtersets.VLANTranslationRuleFilterSet 

1575 table = tables.VLANTranslationRuleTable 

1576 

1577 

1578# 

1579# FHRP groups 

1580# 

1581 

1582@register_model_view(FHRPGroup, 'list', path='', detail=False) 

1583class FHRPGroupListView(generic.ObjectListView): 

1584 queryset = FHRPGroup.objects.annotate( 

1585 member_count=count_related(FHRPGroupAssignment, 'group') 

1586 ) 

1587 filterset = filtersets.FHRPGroupFilterSet 

1588 filterset_form = forms.FHRPGroupFilterForm 

1589 table = tables.FHRPGroupTable 

1590 

1591 

1592@register_model_view(FHRPGroup) 

1593class FHRPGroupView(GetRelatedModelsMixin, generic.ObjectView): 

1594 queryset = FHRPGroup.objects.annotate( 

1595 member_count=count_related(FHRPGroupAssignment, 'group') 

1596 ) 

1597 layout = layout.SimpleLayout( 

1598 left_panels=[ 

1599 panels.FHRPGroupPanel(), 

1600 TagsPanel(), 

1601 CommentsPanel(), 

1602 ], 

1603 right_panels=[ 

1604 panels.FHRPGroupAuthPanel(), 

1605 RelatedObjectsPanel(), 

1606 CustomFieldsPanel(), 

1607 ], 

1608 bottom_panels=[ 

1609 ObjectsTablePanel( 

1610 'ipam.ipaddress', 

1611 filters={'fhrpgroup_id': lambda ctx: ctx['object'].pk}, 

1612 title=_('Virtual IP addresses'), 

1613 actions=[ 

1614 actions.AddObject( 

1615 'ipam.ipaddress', 

1616 url_params={'fhrpgroup': lambda ctx: ctx['object'].pk}, 

1617 label=_('Add IP Address'), 

1618 ), 

1619 ], 

1620 ), 

1621 ContextTablePanel('members_table', title=_('Members')), 

1622 ], 

1623 ) 

1624 

1625 def get_extra_context(self, request, instance): 

1626 # Get assigned interfaces 

1627 members_table = tables.FHRPGroupAssignmentTable( 

1628 data=FHRPGroupAssignment.objects.restrict(request.user, 'view').filter(group=instance), 

1629 orderable=False 

1630 ) 

1631 members_table.configure(request) 

1632 members_table.columns.hide('group') 

1633 

1634 return { 

1635 'related_models': self.get_related_models( 

1636 request, instance, 

1637 extra=( 

1638 ( 

1639 Service.objects.restrict(request.user, 'view').filter( 

1640 parent_object_type=ContentType.objects.get_for_model(FHRPGroup), 

1641 parent_object_id=instance.id, 

1642 ), 

1643 'fhrpgroup_id' 

1644 ), 

1645 ), 

1646 ), 

1647 'members_table': members_table, 

1648 } 

1649 

1650 

1651@register_model_view(FHRPGroup, 'add', detail=False) 

1652@register_model_view(FHRPGroup, 'edit') 

1653class FHRPGroupEditView(generic.ObjectEditView): 

1654 queryset = FHRPGroup.objects.all() 

1655 form = forms.FHRPGroupForm 

1656 

1657 def get_return_url(self, request, obj=None): 

1658 return_url = super().get_return_url(request, obj) 

1659 

1660 # If we're redirecting the user to the FHRPGroupAssignment creation form, 

1661 # initialize the group field with the FHRPGroup we just saved. 

1662 if return_url.startswith(reverse('ipam:fhrpgroupassignment_add')): 

1663 return_url += f'&group={obj.pk}' 

1664 

1665 return return_url 

1666 

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

1668 # Workaround to solve #10719. Capture the current user on the FHRPGroup instance so that 

1669 # we can evaluate permissions during the creation of a new IPAddress within the form. 

1670 obj._user = request.user 

1671 return obj 

1672 

1673 

1674@register_model_view(FHRPGroup, 'delete') 

1675class FHRPGroupDeleteView(generic.ObjectDeleteView): 

1676 queryset = FHRPGroup.objects.all() 

1677 

1678 

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

1680class FHRPGroupBulkImportView(generic.BulkImportView): 

1681 queryset = FHRPGroup.objects.all() 

1682 model_form = forms.FHRPGroupImportForm 

1683 

1684 

1685@register_model_view(FHRPGroup, 'bulk_edit', path='edit', detail=False) 

1686class FHRPGroupBulkEditView(generic.BulkEditView): 

1687 queryset = FHRPGroup.objects.all() 

1688 filterset = filtersets.FHRPGroupFilterSet 

1689 table = tables.FHRPGroupTable 

1690 form = forms.FHRPGroupBulkEditForm 

1691 

1692 

1693@register_model_view(FHRPGroup, 'bulk_rename', path='rename', detail=False) 

1694class FHRPGroupBulkRenameView(generic.BulkRenameView): 

1695 queryset = FHRPGroup.objects.all() 

1696 filterset = filtersets.FHRPGroupFilterSet 

1697 

1698 

1699@register_model_view(FHRPGroup, 'bulk_delete', path='delete', detail=False) 

1700class FHRPGroupBulkDeleteView(generic.BulkDeleteView): 

1701 queryset = FHRPGroup.objects.all() 

1702 filterset = filtersets.FHRPGroupFilterSet 

1703 table = tables.FHRPGroupTable 

1704 

1705 

1706# 

1707# FHRP group assignments 

1708# 

1709 

1710@register_model_view(FHRPGroupAssignment, 'add', detail=False) 

1711@register_model_view(FHRPGroupAssignment, 'edit') 

1712class FHRPGroupAssignmentEditView(generic.ObjectEditView): 

1713 queryset = FHRPGroupAssignment.objects.all() 

1714 form = forms.FHRPGroupAssignmentForm 

1715 

1716 def alter_object(self, instance, request, args, kwargs): 

1717 if not instance.pk: 

1718 # Assign the interface based on URL kwargs 

1719 content_type = get_object_or_404(ContentType, pk=request.GET.get('interface_type')) 

1720 instance.interface = get_object_or_404(content_type.model_class(), pk=request.GET.get('interface_id')) 

1721 return instance 

1722 

1723 def get_extra_addanother_params(self, request): 

1724 return { 

1725 'interface_type': request.GET.get('interface_type'), 

1726 'interface_id': request.GET.get('interface_id'), 

1727 } 

1728 

1729 

1730@register_model_view(FHRPGroupAssignment, 'delete') 

1731class FHRPGroupAssignmentDeleteView(generic.ObjectDeleteView): 

1732 queryset = FHRPGroupAssignment.objects.all() 

1733 

1734 

1735# 

1736# VLANs 

1737# 

1738 

1739@register_model_view(VLAN, 'list', path='', detail=False) 

1740class VLANListView(generic.ObjectListView): 

1741 queryset = VLAN.objects.all() 

1742 filterset = filtersets.VLANFilterSet 

1743 filterset_form = forms.VLANFilterForm 

1744 table = tables.VLANTable 

1745 

1746 

1747@register_model_view(VLAN) 

1748class VLANView(generic.ObjectView): 

1749 queryset = VLAN.objects.all() 

1750 template_name = 'generic/object.html' 

1751 layout = layout.SimpleLayout( 

1752 breadcrumbs=[ 

1753 Breadcrumb('site', url=filtered_list_url('ipam:vlan_list', 'site_id')), 

1754 Breadcrumb('group', url=filtered_list_url('ipam:vlan_list', 'group_id')), 

1755 ], 

1756 left_panels=[ 

1757 panels.VLANPanel(), 

1758 ], 

1759 right_panels=[ 

1760 CustomFieldsPanel(), 

1761 TagsPanel(), 

1762 CommentsPanel(), 

1763 ], 

1764 bottom_panels=[ 

1765 ObjectsTablePanel( 

1766 'ipam.prefix', 

1767 filters={'vlan_id': lambda ctx: ctx['object'].pk}, 

1768 title=_('Prefixes'), 

1769 exclude_columns=['vlan'], 

1770 actions=[ 

1771 actions.AddObject( 

1772 'ipam.prefix', 

1773 url_params={ 

1774 'tenant': lambda ctx: ctx['object'].tenant_id, 

1775 'scope_content_type': lambda ctx: ( 

1776 ContentType.objects.get_for_model(Site).pk if ctx['object'].site_id else None 

1777 ), 

1778 'scope_object_id': lambda ctx: ctx['object'].site_id, 

1779 'vlan': lambda ctx: ctx['object'].pk, 

1780 }, 

1781 label=_('Add a Prefix'), 

1782 ), 

1783 ], 

1784 ), 

1785 panels.VLANCustomerVLANsPanel(), 

1786 ], 

1787 ) 

1788 

1789 

1790@register_model_view(VLAN, 'interfaces') 

1791class VLANInterfacesView(generic.ObjectChildrenView): 

1792 queryset = VLAN.objects.all() 

1793 child_model = Interface 

1794 table = tables.VLANDevicesTable 

1795 filterset = InterfaceFilterSet 

1796 filterset_form = InterfaceFilterForm 

1797 tab = ViewTab( 

1798 label=_('Device Interfaces'), 

1799 badge=lambda x: x.get_interfaces().count(), 

1800 permission='dcim.view_interface', 

1801 weight=500 

1802 ) 

1803 

1804 def get_children(self, request, parent): 

1805 return parent.get_interfaces().restrict(request.user, 'view') 

1806 

1807 

1808@register_model_view(VLAN, 'vminterfaces', path='vm-interfaces') 

1809class VLANVMInterfacesView(generic.ObjectChildrenView): 

1810 queryset = VLAN.objects.all() 

1811 child_model = VMInterface 

1812 table = tables.VLANVirtualMachinesTable 

1813 filterset = VMInterfaceFilterSet 

1814 filterset_form = VMInterfaceFilterForm 

1815 tab = ViewTab( 

1816 label=_('VM Interfaces'), 

1817 badge=lambda x: x.get_vminterfaces().count(), 

1818 permission='virtualization.view_vminterface', 

1819 weight=510 

1820 ) 

1821 

1822 def get_children(self, request, parent): 

1823 return parent.get_vminterfaces().restrict(request.user, 'view') 

1824 

1825 

1826@register_model_view(VLAN, 'add', detail=False) 

1827@register_model_view(VLAN, 'edit') 

1828class VLANEditView(generic.ObjectEditView): 

1829 queryset = VLAN.objects.all() 

1830 form = forms.VLANForm 

1831 template_name = 'ipam/vlan_edit.html' 

1832 

1833 

1834@register_model_view(VLAN, 'delete') 

1835class VLANDeleteView(generic.ObjectDeleteView): 

1836 queryset = VLAN.objects.all() 

1837 

1838 

1839@register_model_view(VLAN, 'bulk_add', path='bulk-add', detail=False) 

1840class VLANBulkCreateView(generic.BulkCreateView): 

1841 queryset = VLAN.objects.all() 

1842 form = forms.VLANIDBulkCreateForm 

1843 model_form = forms.VLANBulkAddForm 

1844 pattern_target = 'vid' 

1845 pattern_template_fields = ('name',) 

1846 template_name = 'ipam/vlan_bulk_add.html' 

1847 

1848 

1849@register_model_view(VLAN, 'bulk_import', path='import', detail=False) 

1850class VLANBulkImportView(generic.BulkImportView): 

1851 queryset = VLAN.objects.all() 

1852 model_form = forms.VLANImportForm 

1853 

1854 

1855@register_model_view(VLAN, 'bulk_edit', path='edit', detail=False) 

1856class VLANBulkEditView(generic.BulkEditView): 

1857 queryset = VLAN.objects.all() 

1858 filterset = filtersets.VLANFilterSet 

1859 table = tables.VLANTable 

1860 form = forms.VLANBulkEditForm 

1861 

1862 

1863@register_model_view(VLAN, 'bulk_rename', path='rename', detail=False) 

1864class VLANBulkRenameView(generic.BulkRenameView): 

1865 queryset = VLAN.objects.all() 

1866 filterset = filtersets.VLANFilterSet 

1867 

1868 

1869@register_model_view(VLAN, 'bulk_delete', path='delete', detail=False) 

1870class VLANBulkDeleteView(generic.BulkDeleteView): 

1871 queryset = VLAN.objects.all() 

1872 filterset = filtersets.VLANFilterSet 

1873 table = tables.VLANTable 

1874 

1875 

1876# 

1877# Service templates 

1878# 

1879 

1880@register_model_view(ServiceTemplate, 'list', path='', detail=False) 

1881class ServiceTemplateListView(generic.ObjectListView): 

1882 queryset = ServiceTemplate.objects.all() 

1883 filterset = filtersets.ServiceTemplateFilterSet 

1884 filterset_form = forms.ServiceTemplateFilterForm 

1885 table = tables.ServiceTemplateTable 

1886 

1887 

1888@register_model_view(ServiceTemplate) 

1889class ServiceTemplateView(generic.ObjectView): 

1890 queryset = ServiceTemplate.objects.all() 

1891 template_name = 'generic/object.html' 

1892 layout = layout.SimpleLayout( 

1893 left_panels=[ 

1894 panels.ServiceTemplatePanel(), 

1895 ], 

1896 right_panels=[ 

1897 CustomFieldsPanel(), 

1898 TagsPanel(), 

1899 CommentsPanel(), 

1900 ], 

1901 ) 

1902 

1903 

1904@register_model_view(ServiceTemplate, 'add', detail=False) 

1905@register_model_view(ServiceTemplate, 'edit') 

1906class ServiceTemplateEditView(generic.ObjectEditView): 

1907 queryset = ServiceTemplate.objects.all() 

1908 form = forms.ServiceTemplateForm 

1909 

1910 

1911@register_model_view(ServiceTemplate, 'delete') 

1912class ServiceTemplateDeleteView(generic.ObjectDeleteView): 

1913 queryset = ServiceTemplate.objects.all() 

1914 

1915 

1916@register_model_view(ServiceTemplate, 'bulk_import', path='import', detail=False) 

1917class ServiceTemplateBulkImportView(generic.BulkImportView): 

1918 queryset = ServiceTemplate.objects.all() 

1919 model_form = forms.ServiceTemplateImportForm 

1920 

1921 

1922class ServicePortMappingsBulkEditMixin: 

1923 """ 

1924 Fold the ``add_port_mappings`` / ``remove_port_mappings`` bulk-edit deltas into each object before it 

1925 is validated and saved, keeping the model unaware of the bulk-edit form's fields. 

1926 """ 

1927 def pre_save_operations(self, form, obj): 

1928 super().pre_save_operations(form, obj) 

1929 add = form.cleaned_data.get('add_port_mappings') 

1930 remove = form.cleaned_data.get('remove_port_mappings') 

1931 if add: 

1932 obj._add_port_mappings(add) 

1933 if remove: 

1934 obj._remove_port_mappings(remove) 

1935 

1936 

1937@register_model_view(ServiceTemplate, 'bulk_edit', path='edit', detail=False) 

1938class ServiceTemplateBulkEditView(ServicePortMappingsBulkEditMixin, generic.BulkEditView): 

1939 queryset = ServiceTemplate.objects.all() 

1940 filterset = filtersets.ServiceTemplateFilterSet 

1941 table = tables.ServiceTemplateTable 

1942 form = forms.ServiceTemplateBulkEditForm 

1943 

1944 

1945@register_model_view(ServiceTemplate, 'bulk_rename', path='rename', detail=False) 

1946class ServiceTemplateBulkRenameView(generic.BulkRenameView): 

1947 queryset = ServiceTemplate.objects.all() 

1948 filterset = filtersets.ServiceTemplateFilterSet 

1949 

1950 

1951@register_model_view(ServiceTemplate, 'bulk_delete', path='delete', detail=False) 

1952class ServiceTemplateBulkDeleteView(generic.BulkDeleteView): 

1953 queryset = ServiceTemplate.objects.all() 

1954 filterset = filtersets.ServiceTemplateFilterSet 

1955 table = tables.ServiceTemplateTable 

1956 

1957 

1958# 

1959# Services 

1960# 

1961 

1962@register_model_view(Service, 'list', path='', detail=False) 

1963class ServiceListView(generic.ObjectListView): 

1964 queryset = Service.objects.prefetch_related('parent') 

1965 filterset = filtersets.ServiceFilterSet 

1966 filterset_form = forms.ServiceFilterForm 

1967 table = tables.ServiceTable 

1968 

1969 

1970@register_model_view(Service) 

1971class ServiceView(generic.ObjectView): 

1972 queryset = Service.objects.all() 

1973 layout = layout.SimpleLayout( 

1974 left_panels=[ 

1975 panels.ServicePanel(), 

1976 ], 

1977 right_panels=[ 

1978 CustomFieldsPanel(), 

1979 TagsPanel(), 

1980 CommentsPanel(), 

1981 ], 

1982 ) 

1983 

1984 def get_extra_context(self, request, instance): 

1985 context = {} 

1986 match instance.parent: 

1987 case Device(): 

1988 context['breadcrumb_queryparam'] = 'device_id' 

1989 case VirtualMachine(): 

1990 context['breadcrumb_queryparam'] = 'virtual_machine_id' 

1991 case FHRPGroup(): 

1992 context['breadcrumb_queryparam'] = 'fhrpgroup_id' 

1993 

1994 return context 

1995 

1996 

1997@register_model_view(Service, 'add', detail=False) 

1998class ServiceCreateView(generic.ObjectEditView): 

1999 queryset = Service.objects.all() 

2000 form = forms.ServiceCreateForm 

2001 

2002 

2003@register_model_view(Service, 'edit') 

2004class ServiceEditView(generic.ObjectEditView): 

2005 queryset = Service.objects.all() 

2006 form = forms.ServiceForm 

2007 

2008 

2009@register_model_view(Service, 'delete') 

2010class ServiceDeleteView(generic.ObjectDeleteView): 

2011 queryset = Service.objects.all() 

2012 

2013 

2014@register_model_view(Service, 'bulk_import', path='import', detail=False) 

2015class ServiceBulkImportView(generic.BulkImportView): 

2016 queryset = Service.objects.all() 

2017 model_form = forms.ServiceImportForm 

2018 

2019 

2020@register_model_view(Service, 'bulk_edit', path='edit', detail=False) 

2021class ServiceBulkEditView(ServicePortMappingsBulkEditMixin, generic.BulkEditView): 

2022 queryset = Service.objects.prefetch_related('parent') 

2023 filterset = filtersets.ServiceFilterSet 

2024 table = tables.ServiceTable 

2025 form = forms.ServiceBulkEditForm 

2026 

2027 

2028@register_model_view(Service, 'bulk_rename', path='rename', detail=False) 

2029class ServiceBulkRenameView(generic.BulkRenameView): 

2030 queryset = Service.objects.all() 

2031 filterset = filtersets.ServiceFilterSet 

2032 

2033 

2034@register_model_view(Service, 'bulk_delete', path='delete', detail=False) 

2035class ServiceBulkDeleteView(generic.BulkDeleteView): 

2036 queryset = Service.objects.prefetch_related('parent') 

2037 filterset = filtersets.ServiceFilterSet 

2038 table = tables.ServiceTable