nautobot 2.3.14__py3-none-any.whl → 2.3.15b1__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/core/testing/mixins.py +7 -2
- nautobot/ipam/api/views.py +17 -15
- nautobot/ipam/tests/test_api.py +334 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +9 -2
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +270 -270
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- {nautobot-2.3.14.dist-info → nautobot-2.3.15b1.dist-info}/METADATA +1 -1
- {nautobot-2.3.14.dist-info → nautobot-2.3.15b1.dist-info}/RECORD +13 -13
- {nautobot-2.3.14.dist-info → nautobot-2.3.15b1.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.3.14.dist-info → nautobot-2.3.15b1.dist-info}/NOTICE +0 -0
- {nautobot-2.3.14.dist-info → nautobot-2.3.15b1.dist-info}/WHEEL +0 -0
- {nautobot-2.3.14.dist-info → nautobot-2.3.15b1.dist-info}/entry_points.txt +0 -0
nautobot/core/testing/mixins.py
CHANGED
|
@@ -144,13 +144,18 @@ class NautobotTestCaseMixin:
|
|
|
144
144
|
# Permissions management
|
|
145
145
|
#
|
|
146
146
|
|
|
147
|
-
def add_permissions(self, *names):
|
|
147
|
+
def add_permissions(self, *names, **kwargs):
|
|
148
148
|
"""
|
|
149
149
|
Assign a set of permissions to the test user. Accepts permission names in the form <app>.<action>_<model>.
|
|
150
|
+
Additional keyword arguments will be passed to the ObjectPermission constructor to allow creating more detailed permissions.
|
|
151
|
+
|
|
152
|
+
Examples:
|
|
153
|
+
>>> add_permissions("ipam.add_vlangroup", "ipam.view_vlangroup")
|
|
154
|
+
>>> add_permissions("ipam.add_vlangroup", "ipam.view_vlangroup", constraints={"pk": "uuid-1234"})
|
|
150
155
|
"""
|
|
151
156
|
for name in names:
|
|
152
157
|
ct, action = permissions.resolve_permission_ct(name)
|
|
153
|
-
obj_perm = users_models.ObjectPermission(name=name, actions=[action])
|
|
158
|
+
obj_perm = users_models.ObjectPermission(name=name, actions=[action], **kwargs)
|
|
154
159
|
obj_perm.save()
|
|
155
160
|
obj_perm.users.add(self.user)
|
|
156
161
|
obj_perm.object_types.add(ct)
|
nautobot/ipam/api/views.py
CHANGED
|
@@ -227,7 +227,8 @@ class PrefixViewSet(NautobotModelViewSet):
|
|
|
227
227
|
|
|
228
228
|
# Create the new Prefix(es)
|
|
229
229
|
serializer.is_valid(raise_exception=True)
|
|
230
|
-
|
|
230
|
+
self.perform_create(serializer)
|
|
231
|
+
|
|
231
232
|
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
|
232
233
|
|
|
233
234
|
else:
|
|
@@ -318,7 +319,8 @@ class PrefixViewSet(NautobotModelViewSet):
|
|
|
318
319
|
|
|
319
320
|
# Create the new IP address(es)
|
|
320
321
|
serializer.is_valid(raise_exception=True)
|
|
321
|
-
|
|
322
|
+
self.perform_create(serializer)
|
|
323
|
+
|
|
322
324
|
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
|
323
325
|
|
|
324
326
|
# Determine the maximum number of IPs to return
|
|
@@ -396,21 +398,20 @@ class VLANGroupViewSet(NautobotModelViewSet):
|
|
|
396
398
|
serializer_class = serializers.VLANGroupSerializer
|
|
397
399
|
filterset_class = filters.VLANGroupFilterSet
|
|
398
400
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
super().restrict_queryset(request, *args, **kwargs)
|
|
401
|
+
@staticmethod
|
|
402
|
+
def vlan_group_queryset():
|
|
403
|
+
return (
|
|
404
|
+
VLANGroup.objects.select_related("location")
|
|
405
|
+
.prefetch_related("tags")
|
|
406
|
+
.annotate(vlan_count=count_related(VLAN, "vlan_group"))
|
|
407
|
+
)
|
|
407
408
|
|
|
408
409
|
class AvailableVLANPermissions(TokenPermissions):
|
|
409
|
-
"""As nautobot.core.api.authentication.TokenPermissions, but enforcing add_vlan permission."""
|
|
410
|
+
"""As nautobot.core.api.authentication.TokenPermissions, but enforcing `add_vlan` and `view_vlan` permission."""
|
|
410
411
|
|
|
411
412
|
perms_map = {
|
|
412
|
-
"GET": ["ipam.view_vlangroup"],
|
|
413
|
-
"POST": ["ipam.view_vlangroup", "ipam.add_vlan"],
|
|
413
|
+
"GET": ["ipam.view_vlangroup", "ipam.view_vlan"],
|
|
414
|
+
"POST": ["ipam.view_vlangroup", "ipam.view_vlan", "ipam.add_vlan"],
|
|
414
415
|
}
|
|
415
416
|
|
|
416
417
|
@extend_schema(methods=["get"], responses={200: ListSerializer(child=IntegerField())})
|
|
@@ -426,6 +427,7 @@ class VLANGroupViewSet(NautobotModelViewSet):
|
|
|
426
427
|
methods=["get", "post"],
|
|
427
428
|
permission_classes=[AvailableVLANPermissions],
|
|
428
429
|
filterset_class=None,
|
|
430
|
+
queryset=VLAN.objects.all(),
|
|
429
431
|
)
|
|
430
432
|
def available_vlans(self, request, pk=None):
|
|
431
433
|
"""
|
|
@@ -433,7 +435,7 @@ class VLANGroupViewSet(NautobotModelViewSet):
|
|
|
433
435
|
By default, the number of VIDs returned will be equivalent to PAGINATE_COUNT.
|
|
434
436
|
An arbitrary limit (up to MAX_PAGE_SIZE, if set) may be passed, however results will not be paginated.
|
|
435
437
|
"""
|
|
436
|
-
vlan_group = get_object_or_404(self.
|
|
438
|
+
vlan_group = get_object_or_404(self.vlan_group_queryset().restrict(user=request.user), pk=pk)
|
|
437
439
|
|
|
438
440
|
if request.method == "POST":
|
|
439
441
|
with cache.lock(
|
|
@@ -509,7 +511,7 @@ class VLANGroupViewSet(NautobotModelViewSet):
|
|
|
509
511
|
|
|
510
512
|
# Create the new VLANs
|
|
511
513
|
serializer.is_valid(raise_exception=True)
|
|
512
|
-
|
|
514
|
+
self.perform_create(serializer)
|
|
513
515
|
|
|
514
516
|
data = serializer.data
|
|
515
517
|
|
nautobot/ipam/tests/test_api.py
CHANGED
|
@@ -571,6 +571,126 @@ class PrefixTest(APIViewTestCases.APIViewTestCase):
|
|
|
571
571
|
self.assertIn("prefixcf", response.data[0]["custom_fields"])
|
|
572
572
|
self.assertEqual("value 1", response.data[0]["custom_fields"]["prefixcf"])
|
|
573
573
|
|
|
574
|
+
def test_create_available_prefixes_with_permissions_constraint(self):
|
|
575
|
+
# Prepare prefix and permissions
|
|
576
|
+
prefix = Prefix.objects.create(
|
|
577
|
+
prefix="10.2.3.0/24",
|
|
578
|
+
type=choices.PrefixTypeChoices.TYPE_POOL,
|
|
579
|
+
namespace=self.namespace,
|
|
580
|
+
status=self.status,
|
|
581
|
+
description="This is the Prefix created for whole network.",
|
|
582
|
+
)
|
|
583
|
+
url = reverse("ipam-api:prefix-available-prefixes", kwargs={"pk": prefix.pk})
|
|
584
|
+
self.add_permissions("ipam.view_prefix")
|
|
585
|
+
self.add_permissions(
|
|
586
|
+
"ipam.add_prefix", constraints={"description__startswith": "This is the Prefix created for"}
|
|
587
|
+
)
|
|
588
|
+
|
|
589
|
+
# Test invalid request
|
|
590
|
+
data = {
|
|
591
|
+
"prefix_length": 26,
|
|
592
|
+
"status": self.status.pk,
|
|
593
|
+
}
|
|
594
|
+
invalid_data_list = [
|
|
595
|
+
data,
|
|
596
|
+
{**data, "description": ""},
|
|
597
|
+
{**data, "description": "Some description"},
|
|
598
|
+
{**data, "description": "Some description. This is the IP created for"},
|
|
599
|
+
]
|
|
600
|
+
|
|
601
|
+
for invalid_data in invalid_data_list:
|
|
602
|
+
with self.subTest(case=invalid_data):
|
|
603
|
+
response = self.client.post(url, invalid_data, format="json", **self.header)
|
|
604
|
+
self.assertHttpStatus(response, status.HTTP_403_FORBIDDEN)
|
|
605
|
+
self.assertIn("detail", response.data)
|
|
606
|
+
self.assertEqual(response.data["detail"], "You do not have permission to perform this action.")
|
|
607
|
+
|
|
608
|
+
# Verify that no prefixes were created (the entire prefix is still available)
|
|
609
|
+
response = self.client.get(url, **self.header)
|
|
610
|
+
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
611
|
+
self.assertEqual(len(response.data), 1)
|
|
612
|
+
self.assertEqual(response.data[0]["prefix"], prefix.cidr_str)
|
|
613
|
+
|
|
614
|
+
# Test valid request
|
|
615
|
+
valid_data = {
|
|
616
|
+
"prefix_length": 26,
|
|
617
|
+
"status": self.status.pk,
|
|
618
|
+
"description": "This is the Prefix created for my local network",
|
|
619
|
+
}
|
|
620
|
+
response = self.client.post(url, valid_data, format="json", **self.header)
|
|
621
|
+
self.assertHttpStatus(response, status.HTTP_201_CREATED)
|
|
622
|
+
self.assertEqual(response.data["prefix"], "10.2.3.0/26")
|
|
623
|
+
|
|
624
|
+
# Verify that prefix is created
|
|
625
|
+
response = self.client.get(url, **self.header)
|
|
626
|
+
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
627
|
+
self.assertEqual(len(response.data), 2)
|
|
628
|
+
self.assertEqual(response.data[0]["prefix"], "10.2.3.64/26")
|
|
629
|
+
self.assertEqual(response.data[1]["prefix"], "10.2.3.128/25")
|
|
630
|
+
|
|
631
|
+
def test_create_multiple_available_prefixes_with_permissions_constraint(self):
|
|
632
|
+
# Prepare prefix and permissions
|
|
633
|
+
prefix = Prefix.objects.create(
|
|
634
|
+
prefix="10.2.3.0/24",
|
|
635
|
+
type=choices.PrefixTypeChoices.TYPE_POOL,
|
|
636
|
+
namespace=self.namespace,
|
|
637
|
+
status=self.status,
|
|
638
|
+
description="This is the Prefix created for whole network.",
|
|
639
|
+
)
|
|
640
|
+
url = reverse("ipam-api:prefix-available-prefixes", kwargs={"pk": prefix.pk})
|
|
641
|
+
self.add_permissions("ipam.view_prefix")
|
|
642
|
+
self.add_permissions(
|
|
643
|
+
"ipam.add_prefix", constraints={"description__startswith": "This is the Prefix created for"}
|
|
644
|
+
)
|
|
645
|
+
|
|
646
|
+
# Test invalid request
|
|
647
|
+
data = [
|
|
648
|
+
{
|
|
649
|
+
"prefix_length": 26,
|
|
650
|
+
"status": self.status.pk,
|
|
651
|
+
"description": "This is the Prefix created for my local network",
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
"prefix_length": 26,
|
|
655
|
+
"status": self.status.pk,
|
|
656
|
+
},
|
|
657
|
+
]
|
|
658
|
+
response = self.client.post(url, data, format="json", **self.header)
|
|
659
|
+
self.assertHttpStatus(response, status.HTTP_403_FORBIDDEN)
|
|
660
|
+
self.assertIn("detail", response.data)
|
|
661
|
+
self.assertEqual(response.data["detail"], "You do not have permission to perform this action.")
|
|
662
|
+
|
|
663
|
+
# Verify that no prefixes were created (the entire prefix is still available)
|
|
664
|
+
response = self.client.get(url, **self.header)
|
|
665
|
+
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
666
|
+
self.assertEqual(len(response.data), 1)
|
|
667
|
+
self.assertEqual(response.data[0]["prefix"], prefix.cidr_str)
|
|
668
|
+
|
|
669
|
+
# Test valid request
|
|
670
|
+
data = [
|
|
671
|
+
{
|
|
672
|
+
"prefix_length": 26,
|
|
673
|
+
"status": self.status.pk,
|
|
674
|
+
"description": "This is the Prefix created for my local network",
|
|
675
|
+
},
|
|
676
|
+
{
|
|
677
|
+
"prefix_length": 26,
|
|
678
|
+
"status": self.status.pk,
|
|
679
|
+
"description": "This is the Prefix created for my guest house network",
|
|
680
|
+
},
|
|
681
|
+
]
|
|
682
|
+
response = self.client.post(url, data, format="json", **self.header)
|
|
683
|
+
self.assertHttpStatus(response, status.HTTP_201_CREATED)
|
|
684
|
+
self.assertEqual(len(response.data), 2)
|
|
685
|
+
self.assertEqual(response.data[0]["prefix"], "10.2.3.0/26")
|
|
686
|
+
self.assertEqual(response.data[1]["prefix"], "10.2.3.64/26")
|
|
687
|
+
|
|
688
|
+
# Verify that prefixes were created
|
|
689
|
+
response = self.client.get(url, **self.header)
|
|
690
|
+
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
691
|
+
self.assertEqual(len(response.data), 1)
|
|
692
|
+
self.assertEqual(response.data[0]["prefix"], "10.2.3.128/25")
|
|
693
|
+
|
|
574
694
|
def test_list_available_ips(self):
|
|
575
695
|
"""
|
|
576
696
|
Test retrieval of all available IP addresses within a parent prefix.
|
|
@@ -731,6 +851,116 @@ class PrefixTest(APIViewTestCases.APIViewTestCase):
|
|
|
731
851
|
self.assertIn("ipcf", response.data[0]["custom_fields"])
|
|
732
852
|
self.assertEqual("1", response.data[0]["custom_fields"]["ipcf"])
|
|
733
853
|
|
|
854
|
+
def test_create_available_ips_with_permissions_constraint(self):
|
|
855
|
+
# Prepare prefix and permissions
|
|
856
|
+
prefix = Prefix.objects.create(
|
|
857
|
+
prefix="192.168.0.0/30",
|
|
858
|
+
type=choices.PrefixTypeChoices.TYPE_NETWORK,
|
|
859
|
+
namespace=self.namespace,
|
|
860
|
+
status=self.status,
|
|
861
|
+
description="This is the Prefix created for whole network.",
|
|
862
|
+
)
|
|
863
|
+
url = reverse("ipam-api:prefix-available-ips", kwargs={"pk": prefix.pk})
|
|
864
|
+
self.add_permissions("ipam.view_prefix", "ipam.view_ipaddress")
|
|
865
|
+
self.add_permissions(
|
|
866
|
+
"ipam.add_ipaddress", constraints={"description__startswith": "This is the IP created for"}
|
|
867
|
+
)
|
|
868
|
+
|
|
869
|
+
# Test invalid request
|
|
870
|
+
data = {
|
|
871
|
+
"status": self.status.pk,
|
|
872
|
+
}
|
|
873
|
+
invalid_data_list = [
|
|
874
|
+
data,
|
|
875
|
+
{**data, "description": ""},
|
|
876
|
+
{**data, "description": "Some description"},
|
|
877
|
+
{**data, "description": "Some description. This is the IP created for"},
|
|
878
|
+
]
|
|
879
|
+
|
|
880
|
+
for invalid_data in invalid_data_list:
|
|
881
|
+
with self.subTest(case=invalid_data):
|
|
882
|
+
response = self.client.post(url, invalid_data, format="json", **self.header)
|
|
883
|
+
self.assertHttpStatus(response, status.HTTP_403_FORBIDDEN)
|
|
884
|
+
self.assertIn("detail", response.data)
|
|
885
|
+
self.assertEqual(response.data["detail"], "You do not have permission to perform this action.")
|
|
886
|
+
|
|
887
|
+
# Verify that no IPs were created (the entire prefix pool is still available)
|
|
888
|
+
response = self.client.get(url, **self.header)
|
|
889
|
+
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
890
|
+
self.assertEqual(len(response.data), 2)
|
|
891
|
+
|
|
892
|
+
# Test valid request
|
|
893
|
+
valid_data = {
|
|
894
|
+
"status": self.status.pk,
|
|
895
|
+
"description": "This is the IP created for my private laptop",
|
|
896
|
+
}
|
|
897
|
+
response = self.client.post(url, valid_data, format="json", **self.header)
|
|
898
|
+
self.assertHttpStatus(response, status.HTTP_201_CREATED)
|
|
899
|
+
self.assertEqual(response.data["address"], "192.168.0.1/30")
|
|
900
|
+
|
|
901
|
+
# Verify that IP is created
|
|
902
|
+
response = self.client.get(url, **self.header)
|
|
903
|
+
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
904
|
+
self.assertEqual(len(response.data), 1)
|
|
905
|
+
self.assertEqual(response.data[0]["address"], "192.168.0.2/30")
|
|
906
|
+
|
|
907
|
+
def test_create_multiple_available_ips_with_permissions_constraint(self):
|
|
908
|
+
# Prepare prefix and permissions
|
|
909
|
+
prefix = Prefix.objects.create(
|
|
910
|
+
prefix="192.168.0.0/30",
|
|
911
|
+
type=choices.PrefixTypeChoices.TYPE_NETWORK,
|
|
912
|
+
namespace=self.namespace,
|
|
913
|
+
status=self.status,
|
|
914
|
+
description="This is a Prefix created for whole network.",
|
|
915
|
+
)
|
|
916
|
+
url = reverse("ipam-api:prefix-available-ips", kwargs={"pk": prefix.pk})
|
|
917
|
+
self.add_permissions("ipam.view_prefix", "ipam.view_ipaddress")
|
|
918
|
+
self.add_permissions(
|
|
919
|
+
"ipam.add_ipaddress", constraints={"description__startswith": "This is the IP created for"}
|
|
920
|
+
)
|
|
921
|
+
|
|
922
|
+
# Test invalid request
|
|
923
|
+
data = [
|
|
924
|
+
{
|
|
925
|
+
"status": self.status.pk,
|
|
926
|
+
},
|
|
927
|
+
{
|
|
928
|
+
"status": self.status.pk,
|
|
929
|
+
"description": "This is an IP created for my private laptop",
|
|
930
|
+
},
|
|
931
|
+
]
|
|
932
|
+
response = self.client.post(url, data, format="json", **self.header)
|
|
933
|
+
self.assertHttpStatus(response, status.HTTP_403_FORBIDDEN)
|
|
934
|
+
self.assertIn("detail", response.data)
|
|
935
|
+
self.assertEqual(response.data["detail"], "You do not have permission to perform this action.")
|
|
936
|
+
|
|
937
|
+
# Verify that no IPs were created (the entire prefix pool is still available)
|
|
938
|
+
response = self.client.get(url, **self.header)
|
|
939
|
+
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
940
|
+
self.assertEqual(len(response.data), 2)
|
|
941
|
+
|
|
942
|
+
# Test valid request
|
|
943
|
+
valid_data = [
|
|
944
|
+
{
|
|
945
|
+
"status": self.status.pk,
|
|
946
|
+
"description": "This is the IP created for my private laptop",
|
|
947
|
+
},
|
|
948
|
+
{
|
|
949
|
+
"status": self.status.pk,
|
|
950
|
+
"description": "This is the IP created for my gaming laptop",
|
|
951
|
+
},
|
|
952
|
+
]
|
|
953
|
+
response = self.client.post(url, valid_data, format="json", **self.header)
|
|
954
|
+
self.assertHttpStatus(response, status.HTTP_201_CREATED)
|
|
955
|
+
self.assertEqual(len(response.data), 2)
|
|
956
|
+
self.assertEqual(response.data[0]["address"], "192.168.0.1/30")
|
|
957
|
+
self.assertEqual(response.data[1]["address"], "192.168.0.2/30")
|
|
958
|
+
|
|
959
|
+
# Verify that IPs are created
|
|
960
|
+
response = self.client.get(url, **self.header)
|
|
961
|
+
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
962
|
+
self.assertEqual(len(response.data), 0)
|
|
963
|
+
|
|
734
964
|
|
|
735
965
|
class PrefixLocationAssignmentTest(APIViewTestCases.APIViewTestCase):
|
|
736
966
|
model = PrefixLocationAssignment
|
|
@@ -1089,7 +1319,7 @@ class VLANGroupTest(APIViewTestCases.APIViewTestCase):
|
|
|
1089
1319
|
Test retrieval of all available VLAN IDs within a VLANGroup.
|
|
1090
1320
|
"""
|
|
1091
1321
|
url = reverse("ipam-api:vlangroup-available-vlans", kwargs={"pk": self.vlan_group.pk})
|
|
1092
|
-
self.add_permissions("ipam.view_vlangroup")
|
|
1322
|
+
self.add_permissions("ipam.view_vlangroup", "ipam.view_vlan")
|
|
1093
1323
|
|
|
1094
1324
|
# Retrieve all available VLAN IDs
|
|
1095
1325
|
response = self.client.get(url, **self.header)
|
|
@@ -1106,6 +1336,7 @@ class VLANGroupTest(APIViewTestCases.APIViewTestCase):
|
|
|
1106
1336
|
url = reverse("ipam-api:vlangroup-available-vlans", kwargs={"pk": self.vlan_group.pk})
|
|
1107
1337
|
self.add_permissions(
|
|
1108
1338
|
"ipam.view_vlangroup",
|
|
1339
|
+
"ipam.view_vlan",
|
|
1109
1340
|
"ipam.add_vlan",
|
|
1110
1341
|
)
|
|
1111
1342
|
|
|
@@ -1147,6 +1378,7 @@ class VLANGroupTest(APIViewTestCases.APIViewTestCase):
|
|
|
1147
1378
|
url = reverse("ipam-api:vlangroup-available-vlans", kwargs={"pk": self.vlan_group.pk})
|
|
1148
1379
|
self.add_permissions(
|
|
1149
1380
|
"ipam.view_vlangroup",
|
|
1381
|
+
"ipam.view_vlan",
|
|
1150
1382
|
"ipam.add_vlan",
|
|
1151
1383
|
)
|
|
1152
1384
|
|
|
@@ -1193,6 +1425,7 @@ class VLANGroupTest(APIViewTestCases.APIViewTestCase):
|
|
|
1193
1425
|
url = reverse("ipam-api:vlangroup-available-vlans", kwargs={"pk": self.vlan_group.pk})
|
|
1194
1426
|
self.add_permissions(
|
|
1195
1427
|
"ipam.view_vlangroup",
|
|
1428
|
+
"ipam.view_vlan",
|
|
1196
1429
|
"ipam.add_vlan",
|
|
1197
1430
|
)
|
|
1198
1431
|
|
|
@@ -1222,6 +1455,7 @@ class VLANGroupTest(APIViewTestCases.APIViewTestCase):
|
|
|
1222
1455
|
url = reverse("ipam-api:vlangroup-available-vlans", kwargs={"pk": self.vlan_group.pk})
|
|
1223
1456
|
self.add_permissions(
|
|
1224
1457
|
"ipam.view_vlangroup",
|
|
1458
|
+
"ipam.view_vlan",
|
|
1225
1459
|
"ipam.add_vlan",
|
|
1226
1460
|
)
|
|
1227
1461
|
|
|
@@ -1249,6 +1483,105 @@ class VLANGroupTest(APIViewTestCases.APIViewTestCase):
|
|
|
1249
1483
|
response.data["detail"],
|
|
1250
1484
|
)
|
|
1251
1485
|
|
|
1486
|
+
def test_create_available_vlans_with_permissions_constraint(self):
|
|
1487
|
+
url = reverse("ipam-api:vlangroup-available-vlans", kwargs={"pk": self.vlan_group.pk})
|
|
1488
|
+
self.add_permissions(
|
|
1489
|
+
"ipam.view_vlangroup",
|
|
1490
|
+
"ipam.view_vlan",
|
|
1491
|
+
)
|
|
1492
|
+
self.add_permissions("ipam.add_vlan", constraints={"description__startswith": "This is the VLAN created for"})
|
|
1493
|
+
|
|
1494
|
+
data = {"name": "VLAN_6", "status": self.default_status.pk, "vid": 6}
|
|
1495
|
+
invalid_data_list = [
|
|
1496
|
+
data,
|
|
1497
|
+
{**data, "description": ""},
|
|
1498
|
+
{**data, "description": "Some description"},
|
|
1499
|
+
{**data, "description": "Some description. This is the VLAN created for"},
|
|
1500
|
+
]
|
|
1501
|
+
|
|
1502
|
+
# Test invalid request
|
|
1503
|
+
for invalid_data in invalid_data_list:
|
|
1504
|
+
with self.subTest(case=invalid_data):
|
|
1505
|
+
response = self.client.post(url, data, format="json", **self.header)
|
|
1506
|
+
self.assertHttpStatus(response, status.HTTP_403_FORBIDDEN)
|
|
1507
|
+
self.assertIn("detail", response.data)
|
|
1508
|
+
self.assertEqual(response.data["detail"], "You do not have permission to perform this action.")
|
|
1509
|
+
|
|
1510
|
+
# Verify that no VLANs were created (number of VLANs is the same as on the beginning of the test)
|
|
1511
|
+
response = self.client.get(url, **self.header)
|
|
1512
|
+
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
1513
|
+
self.assertEqual(len(response.data["results"]), len(self.unused_vids))
|
|
1514
|
+
|
|
1515
|
+
# Test valid request
|
|
1516
|
+
valid_data = {
|
|
1517
|
+
"name": "VLAN_6",
|
|
1518
|
+
"status": self.default_status.pk,
|
|
1519
|
+
"vid": 6,
|
|
1520
|
+
"description": "This is the VLAN created for home automation.",
|
|
1521
|
+
}
|
|
1522
|
+
response = self.client.post(url, valid_data, format="json", **self.header)
|
|
1523
|
+
self.assertHttpStatus(response, status.HTTP_201_CREATED)
|
|
1524
|
+
self.assertEqual(response.data["results"]["name"], valid_data["name"])
|
|
1525
|
+
|
|
1526
|
+
# Verify that VLAN is created
|
|
1527
|
+
response = self.client.get(url, **self.header)
|
|
1528
|
+
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
1529
|
+
self.assertEqual(
|
|
1530
|
+
len(response.data["results"]), len(self.unused_vids) - 1
|
|
1531
|
+
) # initial unsued vids minus one created
|
|
1532
|
+
|
|
1533
|
+
def test_create_multiple_available_vlans_with_permissions_constraint(self):
|
|
1534
|
+
url = reverse("ipam-api:vlangroup-available-vlans", kwargs={"pk": self.vlan_group.pk})
|
|
1535
|
+
self.add_permissions(
|
|
1536
|
+
"ipam.view_vlangroup",
|
|
1537
|
+
"ipam.view_vlan",
|
|
1538
|
+
)
|
|
1539
|
+
self.add_permissions("ipam.add_vlan", constraints={"description__startswith": "This is the VLAN created for"})
|
|
1540
|
+
|
|
1541
|
+
# Test invalid request
|
|
1542
|
+
data = [
|
|
1543
|
+
{"name": "VLAN_6", "status": self.default_status.pk},
|
|
1544
|
+
{"name": "VLAN_7", "status": self.default_status.pk},
|
|
1545
|
+
{"name": "VLAN_8", "status": self.default_status.pk},
|
|
1546
|
+
]
|
|
1547
|
+
response = self.client.post(url, data, format="json", **self.header)
|
|
1548
|
+
self.assertHttpStatus(response, status.HTTP_403_FORBIDDEN)
|
|
1549
|
+
self.assertIn("detail", response.data)
|
|
1550
|
+
self.assertEqual(response.data["detail"], "You do not have permission to perform this action.")
|
|
1551
|
+
|
|
1552
|
+
# Verify that no VLANs were created (number of VLANs is the same as on the beginning of the test)
|
|
1553
|
+
response = self.client.get(url, **self.header)
|
|
1554
|
+
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
1555
|
+
self.assertEqual(len(response.data["results"]), len(self.unused_vids))
|
|
1556
|
+
|
|
1557
|
+
# Test valid request
|
|
1558
|
+
valid_data = [
|
|
1559
|
+
{
|
|
1560
|
+
"name": "VLAN_6",
|
|
1561
|
+
"status": self.default_status.pk,
|
|
1562
|
+
"description": "This is the VLAN created for home automation.",
|
|
1563
|
+
},
|
|
1564
|
+
{
|
|
1565
|
+
"name": "VLAN_7",
|
|
1566
|
+
"status": self.default_status.pk,
|
|
1567
|
+
"description": "This is the VLAN created for IP cameras.",
|
|
1568
|
+
},
|
|
1569
|
+
{"name": "VLAN_8", "status": self.default_status.pk, "description": "This is the VLAN created for guests."},
|
|
1570
|
+
]
|
|
1571
|
+
response = self.client.post(url, valid_data, format="json", **self.header)
|
|
1572
|
+
self.assertHttpStatus(response, status.HTTP_201_CREATED)
|
|
1573
|
+
self.assertHttpStatus(response, status.HTTP_201_CREATED)
|
|
1574
|
+
self.assertEqual(len(response.data["results"]), 3)
|
|
1575
|
+
for i, vlan_data in enumerate(data):
|
|
1576
|
+
self.assertEqual(response.data["results"][i]["name"], vlan_data["name"])
|
|
1577
|
+
|
|
1578
|
+
# Verify that VLANs are created
|
|
1579
|
+
response = self.client.get(url, **self.header)
|
|
1580
|
+
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
1581
|
+
self.assertEqual(
|
|
1582
|
+
len(response.data["results"]), len(self.unused_vids) - 3
|
|
1583
|
+
) # initial unsued vids minus three created
|
|
1584
|
+
|
|
1252
1585
|
|
|
1253
1586
|
class VLANTest(APIViewTestCases.APIViewTestCase):
|
|
1254
1587
|
model = VLAN
|
|
@@ -13624,14 +13624,21 @@ This expects a field named <code>devices</code> on the model and a filter named
|
|
|
13624
13624
|
|
|
13625
13625
|
|
|
13626
13626
|
<h3 id="nautobot.apps.testing.NautobotTestCaseMixin.add_permissions" class="doc doc-heading">
|
|
13627
|
-
<code class="highlight language-python"><span class="n">add_permissions</span><span class="p">(</span><span class="o">*</span><span class="n">names</span><span class="p">)</span></code>
|
|
13627
|
+
<code class="highlight language-python"><span class="n">add_permissions</span><span class="p">(</span><span class="o">*</span><span class="n">names</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span></code>
|
|
13628
13628
|
|
|
13629
13629
|
<a href="#nautobot.apps.testing.NautobotTestCaseMixin.add_permissions" class="headerlink" title="Permanent link">¶</a></h3>
|
|
13630
13630
|
|
|
13631
13631
|
|
|
13632
13632
|
<div class="doc doc-contents ">
|
|
13633
13633
|
|
|
13634
|
-
<p>Assign a set of permissions to the test user. Accepts permission names in the form <app>.<action>_<model
|
|
13634
|
+
<p>Assign a set of permissions to the test user. Accepts permission names in the form <app>.<action>_<model>.
|
|
13635
|
+
Additional keyword arguments will be passed to the ObjectPermission constructor to allow creating more detailed permissions.</p>
|
|
13636
|
+
|
|
13637
|
+
|
|
13638
|
+
<p><span class="doc-section-title">Examples:</span></p>
|
|
13639
|
+
<div class="highlight"><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="gp">>>> </span><span class="n">add_permissions</span><span class="p">(</span><span class="s2">"ipam.add_vlangroup"</span><span class="p">,</span> <span class="s2">"ipam.view_vlangroup"</span><span class="p">)</span>
|
|
13640
|
+
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="gp">>>> </span><span class="n">add_permissions</span><span class="p">(</span><span class="s2">"ipam.add_vlangroup"</span><span class="p">,</span> <span class="s2">"ipam.view_vlangroup"</span><span class="p">,</span> <span class="n">constraints</span><span class="o">=</span><span class="p">{</span><span class="s2">"pk"</span><span class="p">:</span> <span class="s2">"uuid-1234"</span><span class="p">})</span>
|
|
13641
|
+
</code></pre></div>
|
|
13635
13642
|
|
|
13636
13643
|
</div>
|
|
13637
13644
|
|