nautobot 3.0.0rc1__py3-none-any.whl → 3.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of nautobot might be problematic. Click here for more details.
- nautobot/apps/forms.py +8 -0
- nautobot/apps/templatetags.py +231 -0
- nautobot/apps/testing.py +11 -1
- nautobot/apps/ui.py +21 -1
- nautobot/apps/utils.py +26 -1
- nautobot/core/celery/__init__.py +46 -1
- nautobot/core/cli/bootstrap_v3_to_v5.py +185 -44
- nautobot/core/cli/bootstrap_v3_to_v5_changes.yaml +314 -0
- nautobot/core/graphql/generators.py +2 -2
- nautobot/core/jobs/bulk_actions.py +12 -6
- nautobot/core/jobs/cleanup.py +13 -1
- nautobot/core/settings.py +13 -0
- nautobot/core/settings.yaml +22 -0
- nautobot/core/settings_funcs.py +11 -1
- nautobot/core/tables.py +19 -1
- nautobot/core/templates/components/panel/body_wrapper_generic_table.html +1 -1
- nautobot/core/templates/components/panel/header_extra_content_table.html +9 -3
- nautobot/core/templates/generic/object_create.html +1 -1
- nautobot/core/templates/inc/header.html +9 -10
- nautobot/core/templates/login.html +16 -1
- nautobot/core/templates/nautobot_config.py.j2 +14 -1
- nautobot/core/templates/redoc_ui.html +3 -0
- nautobot/core/templatetags/helpers.py +3 -3
- nautobot/core/testing/views.py +3 -1
- nautobot/core/tests/test_graphql.py +13 -0
- nautobot/core/tests/test_jobs.py +118 -0
- nautobot/core/tests/test_views.py +24 -0
- nautobot/core/ui/bulk_buttons.py +2 -3
- nautobot/core/utils/lookup.py +2 -3
- nautobot/core/utils/permissions.py +1 -1
- nautobot/core/views/generic.py +1 -0
- nautobot/core/views/mixins.py +37 -10
- nautobot/core/views/renderers.py +1 -0
- nautobot/core/views/utils.py +3 -3
- nautobot/data_validation/views.py +1 -9
- nautobot/dcim/forms.py +9 -9
- nautobot/dcim/models/devices.py +3 -3
- nautobot/dcim/tables/power.py +3 -0
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +1 -1
- nautobot/dcim/views.py +30 -44
- nautobot/extras/api/views.py +14 -3
- nautobot/extras/choices.py +3 -0
- nautobot/extras/jobs.py +48 -2
- nautobot/extras/migrations/0132_approval_workflow_seed_data.py +127 -0
- nautobot/extras/models/approvals.py +11 -1
- nautobot/extras/models/models.py +19 -0
- nautobot/extras/models/relationships.py +3 -1
- nautobot/extras/tables.py +35 -18
- nautobot/extras/templates/extras/approval_workflow/approve.html +9 -2
- nautobot/extras/templates/extras/approval_workflow/deny.html +9 -3
- nautobot/extras/templates/extras/approvalworkflowdefinition_update.html +1 -1
- nautobot/extras/templates/extras/customfield_update.html +1 -1
- nautobot/extras/templates/extras/dynamicgroup_update.html +2 -2
- nautobot/extras/templates/extras/inc/approval_buttons_column.html +10 -2
- nautobot/extras/templates/extras/inc/job_tiles.html +2 -2
- nautobot/extras/templates/extras/inc/jobresult.html +1 -1
- nautobot/extras/templates/extras/metadatatype_create.html +1 -1
- nautobot/extras/templates/extras/object_approvalworkflow.html +2 -3
- nautobot/extras/templates/extras/secretsgroup_update.html +1 -1
- nautobot/extras/tests/test_api.py +57 -3
- nautobot/extras/tests/test_customfields_filters.py +84 -4
- nautobot/extras/tests/test_views.py +323 -6
- nautobot/extras/views.py +114 -39
- nautobot/ipam/constants.py +2 -2
- nautobot/ipam/tables.py +7 -6
- nautobot/load_balancers/constants.py +6 -0
- nautobot/load_balancers/migrations/0001_initial.py +14 -3
- nautobot/load_balancers/models.py +5 -4
- nautobot/load_balancers/tables.py +5 -0
- nautobot/project-static/dist/css/nautobot.css +1 -1
- nautobot/project-static/dist/css/nautobot.css.map +1 -1
- nautobot/project-static/dist/js/graphql-libraries.js +1 -1
- nautobot/project-static/dist/js/graphql-libraries.js.map +1 -1
- nautobot/project-static/dist/js/libraries.js +1 -1
- nautobot/project-static/dist/js/libraries.js.LICENSE.txt +38 -2
- nautobot/project-static/dist/js/libraries.js.map +1 -1
- nautobot/project-static/dist/js/nautobot-graphiql.js +1 -1
- nautobot/project-static/dist/js/nautobot-graphiql.js.map +1 -1
- nautobot/project-static/dist/js/nautobot.js +1 -1
- nautobot/project-static/dist/js/nautobot.js.map +1 -1
- nautobot/project-static/img/dark-theme.png +0 -0
- nautobot/project-static/img/light-theme.png +0 -0
- nautobot/project-static/img/system-theme.png +0 -0
- nautobot/project-static/js/forms.js +1 -85
- nautobot/tenancy/tables.py +3 -2
- nautobot/tenancy/views.py +3 -2
- nautobot/ui/package-lock.json +553 -569
- nautobot/ui/package.json +10 -10
- nautobot/ui/src/js/checkbox.js +132 -0
- nautobot/ui/src/js/nautobot.js +6 -0
- nautobot/ui/src/js/select2.js +69 -73
- nautobot/ui/src/js/theme.js +129 -39
- nautobot/ui/src/scss/nautobot.scss +11 -1
- nautobot/vpn/templates/vpn/vpnprofile_create.html +2 -2
- nautobot/wireless/filters.py +15 -1
- nautobot/wireless/tables.py +18 -14
- nautobot/wireless/templates/wireless/wirelessnetwork_create.html +1 -1
- {nautobot-3.0.0rc1.dist-info → nautobot-3.0.1.dist-info}/METADATA +2 -2
- {nautobot-3.0.0rc1.dist-info → nautobot-3.0.1.dist-info}/RECORD +103 -98
- {nautobot-3.0.0rc1.dist-info → nautobot-3.0.1.dist-info}/LICENSE.txt +0 -0
- {nautobot-3.0.0rc1.dist-info → nautobot-3.0.1.dist-info}/NOTICE +0 -0
- {nautobot-3.0.0rc1.dist-info → nautobot-3.0.1.dist-info}/WHEEL +0 -0
- {nautobot-3.0.0rc1.dist-info → nautobot-3.0.1.dist-info}/entry_points.txt +0 -0
nautobot/extras/views.py
CHANGED
|
@@ -325,7 +325,7 @@ class ApprovalWorkflowUIViewSet(
|
|
|
325
325
|
table_title="Responses",
|
|
326
326
|
table_class=tables.RelatedApprovalWorkflowStageResponseTable,
|
|
327
327
|
table_filter="approval_workflow_stage__approval_workflow",
|
|
328
|
-
section=SectionChoices.
|
|
328
|
+
section=SectionChoices.FULL_WIDTH,
|
|
329
329
|
exclude_columns=["approval_workflow"],
|
|
330
330
|
add_button_route=None,
|
|
331
331
|
enable_related_link=False,
|
|
@@ -412,7 +412,7 @@ class ApprovalWorkflowStageUIViewSet(
|
|
|
412
412
|
weight=200,
|
|
413
413
|
table_class=tables.ApprovalWorkflowStageResponseTable,
|
|
414
414
|
table_filter="approval_workflow_stage",
|
|
415
|
-
section=SectionChoices.
|
|
415
|
+
section=SectionChoices.FULL_WIDTH,
|
|
416
416
|
exclude_columns=["approval_workflow_stage"],
|
|
417
417
|
table_title="Responses",
|
|
418
418
|
enable_related_link=False,
|
|
@@ -420,27 +420,33 @@ class ApprovalWorkflowStageUIViewSet(
|
|
|
420
420
|
],
|
|
421
421
|
)
|
|
422
422
|
|
|
423
|
-
@action(
|
|
423
|
+
@action(
|
|
424
|
+
detail=True,
|
|
425
|
+
url_path="approve",
|
|
426
|
+
methods=["get", "post"],
|
|
427
|
+
custom_view_base_action="change",
|
|
428
|
+
custom_view_additional_permissions=["extras.view_approvalworkflowstage"],
|
|
429
|
+
)
|
|
424
430
|
def approve(self, request, *args, **kwargs):
|
|
425
431
|
"""
|
|
426
432
|
Approve the approval workflow stage response.
|
|
427
433
|
"""
|
|
428
434
|
instance = self.get_object()
|
|
429
435
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
)
|
|
435
|
-
|
|
436
|
-
approval_workflow_stage_response = ApprovalWorkflowStageResponse.objects.create(
|
|
437
|
-
approval_workflow_stage=instance,
|
|
438
|
-
user=request.user,
|
|
439
|
-
)
|
|
436
|
+
if not (
|
|
437
|
+
request.user.is_superuser
|
|
438
|
+
or instance.approval_workflow_stage_definition.approver_group.user_set.filter(id=request.user.id).exists()
|
|
439
|
+
):
|
|
440
|
+
messages.error(request, "You are not permitted to approve this workflow stage.")
|
|
441
|
+
return redirect(self.get_return_url(request, instance))
|
|
440
442
|
|
|
441
443
|
if request.method == "GET":
|
|
442
|
-
|
|
443
|
-
|
|
444
|
+
if existing_response := ApprovalWorkflowStageResponse.objects.filter(
|
|
445
|
+
approval_workflow_stage=instance, user=request.user
|
|
446
|
+
).first():
|
|
447
|
+
form = ApprovalForm(initial={"comments": existing_response.comments})
|
|
448
|
+
else:
|
|
449
|
+
form = ApprovalForm()
|
|
444
450
|
|
|
445
451
|
object_under_review = instance.approval_workflow.object_under_review
|
|
446
452
|
template_name = getattr(object_under_review, "get_approval_template", lambda: None)()
|
|
@@ -451,15 +457,19 @@ class ApprovalWorkflowStageUIViewSet(
|
|
|
451
457
|
request,
|
|
452
458
|
template_name,
|
|
453
459
|
{
|
|
454
|
-
"obj":
|
|
455
|
-
"object_under_review":
|
|
460
|
+
"obj": instance,
|
|
461
|
+
"object_under_review": instance.approval_workflow.object_under_review,
|
|
456
462
|
"form": form,
|
|
457
463
|
"obj_type": ApprovalWorkflowStage._meta.verbose_name,
|
|
458
|
-
"return_url": self.get_return_url(request,
|
|
464
|
+
"return_url": self.get_return_url(request, instance),
|
|
459
465
|
"card_class": "success",
|
|
460
466
|
"button_class": "success",
|
|
461
467
|
},
|
|
462
468
|
)
|
|
469
|
+
|
|
470
|
+
approval_workflow_stage_response, _ = ApprovalWorkflowStageResponse.objects.get_or_create(
|
|
471
|
+
approval_workflow_stage=instance, user=request.user
|
|
472
|
+
)
|
|
463
473
|
approval_workflow_stage_response.comments = request.data.get("comments")
|
|
464
474
|
approval_workflow_stage_response.state = ApprovalWorkflowStateChoices.APPROVED
|
|
465
475
|
approval_workflow_stage_response.save()
|
|
@@ -467,40 +477,49 @@ class ApprovalWorkflowStageUIViewSet(
|
|
|
467
477
|
messages.success(request, f"You approved {instance}.")
|
|
468
478
|
return redirect(self.get_return_url(request))
|
|
469
479
|
|
|
470
|
-
@action(
|
|
480
|
+
@action(
|
|
481
|
+
detail=True,
|
|
482
|
+
url_path="deny",
|
|
483
|
+
methods=["get", "post"],
|
|
484
|
+
custom_view_base_action="change",
|
|
485
|
+
custom_view_additional_permissions=["extras.view_approvalworkflowstage"],
|
|
486
|
+
)
|
|
471
487
|
def deny(self, request, *args, **kwargs):
|
|
472
488
|
"""
|
|
473
489
|
Deny the approval workflow stage response.
|
|
474
490
|
"""
|
|
475
491
|
instance = self.get_object()
|
|
476
492
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
)
|
|
482
|
-
|
|
483
|
-
approval_workflow_stage_response = ApprovalWorkflowStageResponse.objects.create(
|
|
484
|
-
approval_workflow_stage=instance,
|
|
485
|
-
user=request.user,
|
|
486
|
-
state=ApprovalWorkflowStateChoices.PENDING,
|
|
487
|
-
)
|
|
493
|
+
if not (
|
|
494
|
+
request.user.is_superuser
|
|
495
|
+
or instance.approval_workflow_stage_definition.approver_group.user_set.filter(id=request.user.id).exists()
|
|
496
|
+
):
|
|
497
|
+
messages.error(request, "You are not permitted to deny this workflow stage.")
|
|
498
|
+
return redirect(self.get_return_url(request, instance))
|
|
488
499
|
|
|
489
500
|
if request.method == "GET":
|
|
490
|
-
|
|
491
|
-
|
|
501
|
+
if existing_response := ApprovalWorkflowStageResponse.objects.filter(
|
|
502
|
+
approval_workflow_stage=instance, user=request.user
|
|
503
|
+
).first():
|
|
504
|
+
form = ApprovalForm(initial={"comments": existing_response.comments})
|
|
505
|
+
else:
|
|
506
|
+
form = ApprovalForm()
|
|
492
507
|
|
|
493
508
|
return render(
|
|
494
509
|
request,
|
|
495
510
|
"extras/approval_workflow/deny.html",
|
|
496
511
|
{
|
|
497
|
-
"obj":
|
|
498
|
-
"object_under_review":
|
|
512
|
+
"obj": instance,
|
|
513
|
+
"object_under_review": instance.approval_workflow.object_under_review,
|
|
499
514
|
"form": form,
|
|
500
515
|
"obj_type": ApprovalWorkflowStage._meta.verbose_name,
|
|
501
|
-
"return_url": self.get_return_url(request,
|
|
516
|
+
"return_url": self.get_return_url(request, instance),
|
|
502
517
|
},
|
|
503
518
|
)
|
|
519
|
+
|
|
520
|
+
approval_workflow_stage_response, _ = ApprovalWorkflowStageResponse.objects.get_or_create(
|
|
521
|
+
approval_workflow_stage=instance, user=request.user
|
|
522
|
+
)
|
|
504
523
|
approval_workflow_stage_response.comments = request.data.get("comments")
|
|
505
524
|
approval_workflow_stage_response.state = ApprovalWorkflowStateChoices.DENIED
|
|
506
525
|
approval_workflow_stage_response.save()
|
|
@@ -508,6 +527,61 @@ class ApprovalWorkflowStageUIViewSet(
|
|
|
508
527
|
messages.success(request, f"You denied {instance}.")
|
|
509
528
|
return redirect(self.get_return_url(request))
|
|
510
529
|
|
|
530
|
+
@action(
|
|
531
|
+
detail=True,
|
|
532
|
+
url_path="comment",
|
|
533
|
+
methods=["get", "post"],
|
|
534
|
+
custom_view_base_action="change",
|
|
535
|
+
custom_view_additional_permissions=["extras.view_approvalworkflowstage"],
|
|
536
|
+
)
|
|
537
|
+
def comment(self, request, *args, **kwargs):
|
|
538
|
+
"""
|
|
539
|
+
Comment the approval workflow stage response.
|
|
540
|
+
"""
|
|
541
|
+
instance = self.get_object()
|
|
542
|
+
|
|
543
|
+
if not instance.is_not_done_stage:
|
|
544
|
+
messages.error(
|
|
545
|
+
request, f"This stage is in {instance.state} state. Can't comment on an approved or denied stage."
|
|
546
|
+
)
|
|
547
|
+
return redirect(self.get_return_url(request, instance))
|
|
548
|
+
|
|
549
|
+
# We don't enforce approver-group/superuser check here, anyone can comment, not just an approver.
|
|
550
|
+
|
|
551
|
+
if request.method == "GET":
|
|
552
|
+
if existing_response := ApprovalWorkflowStageResponse.objects.filter(
|
|
553
|
+
approval_workflow_stage=instance, user=request.user
|
|
554
|
+
).first():
|
|
555
|
+
form = ApprovalForm(initial={"comments": existing_response.comments})
|
|
556
|
+
else:
|
|
557
|
+
form = ApprovalForm()
|
|
558
|
+
|
|
559
|
+
template_name = "extras/approval_workflow/comment.html"
|
|
560
|
+
|
|
561
|
+
return render(
|
|
562
|
+
request,
|
|
563
|
+
template_name,
|
|
564
|
+
{
|
|
565
|
+
"obj": instance,
|
|
566
|
+
"object_under_review": instance.approval_workflow.object_under_review,
|
|
567
|
+
"form": form,
|
|
568
|
+
"obj_type": ApprovalWorkflowStage._meta.verbose_name,
|
|
569
|
+
"return_url": self.get_return_url(request, instance),
|
|
570
|
+
},
|
|
571
|
+
)
|
|
572
|
+
|
|
573
|
+
approval_workflow_stage_response, _ = ApprovalWorkflowStageResponse.objects.get_or_create(
|
|
574
|
+
approval_workflow_stage=instance, user=request.user
|
|
575
|
+
)
|
|
576
|
+
approval_workflow_stage_response.comments = request.data.get("comments")
|
|
577
|
+
# we don't want to change a state if is approved, denied or canceled
|
|
578
|
+
if approval_workflow_stage_response.state == ApprovalWorkflowStateChoices.PENDING:
|
|
579
|
+
approval_workflow_stage_response.state = ApprovalWorkflowStateChoices.COMMENT
|
|
580
|
+
approval_workflow_stage_response.save()
|
|
581
|
+
instance.refresh_from_db()
|
|
582
|
+
messages.success(request, f"You commented {instance}.")
|
|
583
|
+
return redirect(self.get_return_url(request))
|
|
584
|
+
|
|
511
585
|
|
|
512
586
|
class ApprovalWorkflowStageResponseUIViewSet(
|
|
513
587
|
ObjectBulkDestroyViewMixin,
|
|
@@ -659,6 +733,9 @@ class ObjectApprovalWorkflowView(generic.GenericView):
|
|
|
659
733
|
"default_time_zone": get_current_timezone(),
|
|
660
734
|
"stage_table": stage_table,
|
|
661
735
|
"response_table": response_table,
|
|
736
|
+
"view_titles": self.get_view_titles(model=obj, view_type=""),
|
|
737
|
+
"breadcrumbs": self.get_breadcrumbs(model=obj, view_type=""),
|
|
738
|
+
"detail": True,
|
|
662
739
|
**common_detail_view_context(request, obj),
|
|
663
740
|
},
|
|
664
741
|
)
|
|
@@ -1516,8 +1593,6 @@ class ObjectDynamicGroupsView(generic.GenericView):
|
|
|
1516
1593
|
"""
|
|
1517
1594
|
|
|
1518
1595
|
base_template: Optional[str] = None
|
|
1519
|
-
breadcrumbs = Breadcrumbs()
|
|
1520
|
-
view_titles = Titles()
|
|
1521
1596
|
|
|
1522
1597
|
def get(self, request, model, **kwargs):
|
|
1523
1598
|
# Handle QuerySet restriction of parent object if needed
|
|
@@ -1551,8 +1626,8 @@ class ObjectDynamicGroupsView(generic.GenericView):
|
|
|
1551
1626
|
"table": dynamicgroups_table,
|
|
1552
1627
|
"base_template": base_template,
|
|
1553
1628
|
"active_tab": "dynamic-groups",
|
|
1554
|
-
"
|
|
1555
|
-
"
|
|
1629
|
+
"view_titles": self.get_view_titles(model=obj, view_type=""),
|
|
1630
|
+
"breadcrumbs": self.get_breadcrumbs(model=obj, view_type=""),
|
|
1556
1631
|
"detail": True,
|
|
1557
1632
|
},
|
|
1558
1633
|
)
|
nautobot/ipam/constants.py
CHANGED
|
@@ -22,8 +22,8 @@ VRF_RD_MAX_LENGTH = 21
|
|
|
22
22
|
# Prefixes
|
|
23
23
|
#
|
|
24
24
|
|
|
25
|
-
PREFIX_LENGTH_MIN =
|
|
26
|
-
PREFIX_LENGTH_MAX =
|
|
25
|
+
PREFIX_LENGTH_MIN = 0
|
|
26
|
+
PREFIX_LENGTH_MAX = 128 # IPv6
|
|
27
27
|
PREFIX_ALLOWED_PARENT_TYPES = {
|
|
28
28
|
PrefixTypeChoices.TYPE_CONTAINER: PrefixTypeChoices.TYPE_CONTAINER,
|
|
29
29
|
PrefixTypeChoices.TYPE_NETWORK: PrefixTypeChoices.TYPE_CONTAINER,
|
nautobot/ipam/tables.py
CHANGED
|
@@ -207,10 +207,11 @@ class NamespaceTable(BaseTable):
|
|
|
207
207
|
name = tables.LinkColumn()
|
|
208
208
|
tenant = TenantColumn()
|
|
209
209
|
tags = TagColumn(url_name="ipam:namespace_list")
|
|
210
|
+
actions = ButtonsColumn(Namespace)
|
|
210
211
|
|
|
211
212
|
class Meta(BaseTable.Meta):
|
|
212
213
|
model = Namespace
|
|
213
|
-
fields = ("pk", "name", "description", "tenant", "location")
|
|
214
|
+
fields = ("pk", "name", "description", "tenant", "location", "actions")
|
|
214
215
|
|
|
215
216
|
|
|
216
217
|
#
|
|
@@ -247,11 +248,11 @@ class VRFTable(StatusTableMixin, BaseTable):
|
|
|
247
248
|
class VRFDeviceAssignmentTable(BaseTable):
|
|
248
249
|
"""Table for displaying VRF Device Assignments with RD."""
|
|
249
250
|
|
|
250
|
-
vrf = tables.Column(verbose_name="VRF", linkify=lambda record: record.vrf.get_absolute_url(), accessor="
|
|
251
|
+
vrf = tables.Column(verbose_name="VRF", linkify=lambda record: record.vrf.get_absolute_url(), accessor="vrf__name")
|
|
251
252
|
namespace = tables.Column(
|
|
252
253
|
verbose_name="Namespace",
|
|
253
254
|
linkify=lambda record: record.vrf.namespace.get_absolute_url(),
|
|
254
|
-
accessor="
|
|
255
|
+
accessor="vrf__namespace__name",
|
|
255
256
|
)
|
|
256
257
|
related_object_type = tables.TemplateColumn(
|
|
257
258
|
template_code="""
|
|
@@ -287,10 +288,10 @@ class VRFDeviceAssignmentTable(BaseTable):
|
|
|
287
288
|
class VRFPrefixAssignmentTable(BaseTable):
|
|
288
289
|
"""Table for displaying VRF Prefix Assignments."""
|
|
289
290
|
|
|
290
|
-
vrf = tables.Column(verbose_name="VRF", linkify=lambda record: record.vrf.get_absolute_url(), accessor="
|
|
291
|
+
vrf = tables.Column(verbose_name="VRF", linkify=lambda record: record.vrf.get_absolute_url(), accessor="vrf__name")
|
|
291
292
|
prefix = tables.Column(linkify=True)
|
|
292
|
-
rd = tables.Column(accessor="
|
|
293
|
-
tenant = TenantColumn(accessor="
|
|
293
|
+
rd = tables.Column(accessor="vrf__rd", verbose_name="RD")
|
|
294
|
+
tenant = TenantColumn(accessor="vrf__tenant")
|
|
294
295
|
|
|
295
296
|
class Meta(BaseTable.Meta):
|
|
296
297
|
model = VRFPrefixAssignment
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import uuid
|
|
4
4
|
|
|
5
5
|
import django.core.serializers.json
|
|
6
|
+
import django.core.validators
|
|
6
7
|
from django.db import migrations, models
|
|
7
8
|
import django.db.models.deletion
|
|
8
9
|
|
|
@@ -86,7 +87,12 @@ class Migration(migrations.Migration):
|
|
|
86
87
|
("interval", models.PositiveIntegerField(blank=True, null=True)),
|
|
87
88
|
("retry", models.PositiveIntegerField(blank=True, null=True)),
|
|
88
89
|
("timeout", models.PositiveIntegerField(blank=True, null=True)),
|
|
89
|
-
(
|
|
90
|
+
(
|
|
91
|
+
"port",
|
|
92
|
+
models.PositiveIntegerField(
|
|
93
|
+
blank=True, null=True, validators=[django.core.validators.MaxValueValidator(65535)]
|
|
94
|
+
),
|
|
95
|
+
),
|
|
90
96
|
("health_check_type", models.CharField(blank=True, max_length=255)),
|
|
91
97
|
("tags", nautobot.core.models.fields.TagsField(through="extras.TaggedItem", to="extras.Tag")),
|
|
92
98
|
(
|
|
@@ -175,7 +181,7 @@ class Migration(migrations.Migration):
|
|
|
175
181
|
models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder),
|
|
176
182
|
),
|
|
177
183
|
("label", models.CharField(blank=True, max_length=255)),
|
|
178
|
-
("port", models.PositiveIntegerField()),
|
|
184
|
+
("port", models.PositiveIntegerField(validators=[django.core.validators.MaxValueValidator(65535)])),
|
|
179
185
|
("ssl_offload", models.BooleanField(default=False)),
|
|
180
186
|
],
|
|
181
187
|
options={
|
|
@@ -203,7 +209,12 @@ class Migration(migrations.Migration):
|
|
|
203
209
|
models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder),
|
|
204
210
|
),
|
|
205
211
|
("name", models.CharField(max_length=255)),
|
|
206
|
-
(
|
|
212
|
+
(
|
|
213
|
+
"port",
|
|
214
|
+
models.PositiveIntegerField(
|
|
215
|
+
blank=True, null=True, validators=[django.core.validators.MaxValueValidator(65535)]
|
|
216
|
+
),
|
|
217
|
+
),
|
|
207
218
|
("protocol", models.CharField(blank=True, max_length=255)),
|
|
208
219
|
("source_nat_type", models.CharField(blank=True, max_length=255)),
|
|
209
220
|
("load_balancer_type", models.CharField(blank=True, max_length=255)),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Models for Load Balancer Models."""
|
|
2
2
|
|
|
3
|
+
from django.core.validators import MaxValueValidator
|
|
3
4
|
from django.db import models
|
|
4
5
|
|
|
5
6
|
from nautobot.core.constants import CHARFIELD_MAX_LENGTH
|
|
@@ -7,7 +8,7 @@ from nautobot.core.models import BaseModel
|
|
|
7
8
|
from nautobot.core.models.generics import PrimaryModel
|
|
8
9
|
from nautobot.extras.models import StatusField
|
|
9
10
|
from nautobot.extras.utils import extras_features
|
|
10
|
-
from nautobot.load_balancers import choices
|
|
11
|
+
from nautobot.load_balancers import choices, constants
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
@extras_features("custom_links", "custom_validators", "export_templates", "graphql", "webhooks")
|
|
@@ -18,7 +19,7 @@ class VirtualServer(PrimaryModel): # pylint: disable=too-many-ancestors
|
|
|
18
19
|
to="ipam.IPAddress", on_delete=models.PROTECT, related_name="virtual_servers", verbose_name="VIP"
|
|
19
20
|
)
|
|
20
21
|
name = models.CharField(max_length=CHARFIELD_MAX_LENGTH)
|
|
21
|
-
port = models.PositiveIntegerField(blank=True, null=True)
|
|
22
|
+
port = models.PositiveIntegerField(blank=True, null=True, validators=[MaxValueValidator(constants.PORT_VALUE_MAX)])
|
|
22
23
|
protocol = models.CharField(max_length=CHARFIELD_MAX_LENGTH, blank=True, choices=choices.ProtocolChoices)
|
|
23
24
|
source_nat_pool = models.ForeignKey(
|
|
24
25
|
to="ipam.Prefix",
|
|
@@ -200,7 +201,7 @@ class LoadBalancerPoolMember(PrimaryModel): # pylint: disable=too-many-ancestor
|
|
|
200
201
|
related_name="load_balancer_pool_members",
|
|
201
202
|
on_delete=models.PROTECT,
|
|
202
203
|
)
|
|
203
|
-
port = models.PositiveIntegerField()
|
|
204
|
+
port = models.PositiveIntegerField(validators=[MaxValueValidator(constants.PORT_VALUE_MAX)])
|
|
204
205
|
ssl_offload = models.BooleanField(
|
|
205
206
|
default=False,
|
|
206
207
|
verbose_name="SSL Offload",
|
|
@@ -262,7 +263,7 @@ class HealthCheckMonitor(PrimaryModel): # pylint: disable=too-many-ancestors
|
|
|
262
263
|
interval = models.PositiveIntegerField(blank=True, null=True)
|
|
263
264
|
retry = models.PositiveIntegerField(blank=True, null=True, help_text="Number of retries before marking as down")
|
|
264
265
|
timeout = models.PositiveIntegerField(blank=True, null=True)
|
|
265
|
-
port = models.PositiveIntegerField(blank=True, null=True)
|
|
266
|
+
port = models.PositiveIntegerField(blank=True, null=True, validators=[MaxValueValidator(constants.PORT_VALUE_MAX)])
|
|
266
267
|
health_check_type = models.CharField(
|
|
267
268
|
max_length=CHARFIELD_MAX_LENGTH,
|
|
268
269
|
choices=choices.HealthCheckTypeChoices,
|
|
@@ -64,6 +64,7 @@ class VirtualServerTable(BaseTable):
|
|
|
64
64
|
"ssl_offload",
|
|
65
65
|
"certificate_profiles_count",
|
|
66
66
|
"tags",
|
|
67
|
+
"actions",
|
|
67
68
|
)
|
|
68
69
|
|
|
69
70
|
default_columns = (
|
|
@@ -108,6 +109,7 @@ class LoadBalancerPoolTable(BaseTable):
|
|
|
108
109
|
"load_balancer_pool_member_count",
|
|
109
110
|
"health_check_monitor",
|
|
110
111
|
"tenant",
|
|
112
|
+
"actions",
|
|
111
113
|
)
|
|
112
114
|
default_columns = (
|
|
113
115
|
"pk",
|
|
@@ -153,6 +155,7 @@ class LoadBalancerPoolMemberTable(StatusTableMixin, BaseTable):
|
|
|
153
155
|
"health_check_monitor",
|
|
154
156
|
"certificate_profiles_count",
|
|
155
157
|
"tenant",
|
|
158
|
+
"actions",
|
|
156
159
|
)
|
|
157
160
|
default_columns = (
|
|
158
161
|
"pk",
|
|
@@ -206,6 +209,7 @@ class HealthCheckMonitorTable(BaseTable):
|
|
|
206
209
|
"load_balancer_pool_count",
|
|
207
210
|
"load_balancer_pool_member_count",
|
|
208
211
|
"tenant",
|
|
212
|
+
"actions",
|
|
209
213
|
)
|
|
210
214
|
default_columns = (
|
|
211
215
|
"pk",
|
|
@@ -243,6 +247,7 @@ class CertificateProfileTable(BaseTable):
|
|
|
243
247
|
"expiration_date",
|
|
244
248
|
"cipher",
|
|
245
249
|
"tenant",
|
|
250
|
+
"actions",
|
|
246
251
|
)
|
|
247
252
|
default_columns = (
|
|
248
253
|
"pk",
|