ipfabric_netbox 4.3.2b9__py3-none-any.whl → 4.3.2b10__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 ipfabric_netbox might be problematic. Click here for more details.
- ipfabric_netbox/__init__.py +2 -2
- ipfabric_netbox/api/serializers.py +112 -7
- ipfabric_netbox/api/urls.py +6 -0
- ipfabric_netbox/api/views.py +23 -0
- ipfabric_netbox/choices.py +72 -40
- ipfabric_netbox/data/endpoint.json +47 -0
- ipfabric_netbox/data/filters.json +51 -0
- ipfabric_netbox/data/transform_map.json +188 -174
- ipfabric_netbox/exceptions.py +7 -5
- ipfabric_netbox/filtersets.py +310 -41
- ipfabric_netbox/forms.py +324 -79
- ipfabric_netbox/graphql/__init__.py +6 -0
- ipfabric_netbox/graphql/enums.py +5 -5
- ipfabric_netbox/graphql/filters.py +56 -4
- ipfabric_netbox/graphql/schema.py +28 -0
- ipfabric_netbox/graphql/types.py +61 -1
- ipfabric_netbox/jobs.py +5 -1
- ipfabric_netbox/migrations/0022_prepare_for_filters.py +182 -0
- ipfabric_netbox/migrations/0023_populate_filters_data.py +279 -0
- ipfabric_netbox/migrations/0024_finish_filters.py +29 -0
- ipfabric_netbox/models.py +384 -12
- ipfabric_netbox/navigation.py +98 -24
- ipfabric_netbox/tables.py +194 -9
- ipfabric_netbox/templates/ipfabric_netbox/htmx_list.html +5 -0
- ipfabric_netbox/templates/ipfabric_netbox/inc/combined_expressions.html +59 -0
- ipfabric_netbox/templates/ipfabric_netbox/inc/combined_expressions_content.html +39 -0
- ipfabric_netbox/templates/ipfabric_netbox/inc/endpoint_filters_with_selector.html +54 -0
- ipfabric_netbox/templates/ipfabric_netbox/ipfabricendpoint.html +39 -0
- ipfabric_netbox/templates/ipfabric_netbox/ipfabricfilter.html +51 -0
- ipfabric_netbox/templates/ipfabric_netbox/ipfabricfilterexpression.html +39 -0
- ipfabric_netbox/templates/ipfabric_netbox/ipfabricfilterexpression_edit.html +150 -0
- ipfabric_netbox/templates/ipfabric_netbox/ipfabricsync.html +1 -1
- ipfabric_netbox/templates/ipfabric_netbox/ipfabrictransformmap.html +16 -2
- ipfabric_netbox/templatetags/ipfabric_netbox_helpers.py +65 -0
- ipfabric_netbox/tests/api/test_api.py +333 -13
- ipfabric_netbox/tests/test_filtersets.py +2592 -0
- ipfabric_netbox/tests/test_forms.py +1256 -74
- ipfabric_netbox/tests/test_models.py +242 -34
- ipfabric_netbox/tests/test_views.py +2030 -25
- ipfabric_netbox/urls.py +35 -0
- ipfabric_netbox/utilities/endpoint.py +30 -0
- ipfabric_netbox/utilities/filters.py +88 -0
- ipfabric_netbox/utilities/ipfutils.py +254 -316
- ipfabric_netbox/utilities/logging.py +7 -7
- ipfabric_netbox/utilities/transform_map.py +126 -0
- ipfabric_netbox/views.py +719 -5
- {ipfabric_netbox-4.3.2b9.dist-info → ipfabric_netbox-4.3.2b10.dist-info}/METADATA +3 -2
- {ipfabric_netbox-4.3.2b9.dist-info → ipfabric_netbox-4.3.2b10.dist-info}/RECORD +49 -33
- {ipfabric_netbox-4.3.2b9.dist-info → ipfabric_netbox-4.3.2b10.dist-info}/WHEEL +1 -1
|
@@ -3,9 +3,13 @@ from django.utils import timezone
|
|
|
3
3
|
from rest_framework import status
|
|
4
4
|
from utilities.testing import APIViewTestCases
|
|
5
5
|
|
|
6
|
+
from ipfabric_netbox.choices import IPFabricFilterTypeChoices
|
|
6
7
|
from ipfabric_netbox.choices import IPFabricSourceStatusChoices
|
|
7
8
|
from ipfabric_netbox.choices import IPFabricSyncStatusChoices
|
|
8
9
|
from ipfabric_netbox.models import IPFabricData
|
|
10
|
+
from ipfabric_netbox.models import IPFabricEndpoint
|
|
11
|
+
from ipfabric_netbox.models import IPFabricFilter
|
|
12
|
+
from ipfabric_netbox.models import IPFabricFilterExpression
|
|
9
13
|
from ipfabric_netbox.models import IPFabricIngestion
|
|
10
14
|
from ipfabric_netbox.models import IPFabricIngestionIssue
|
|
11
15
|
from ipfabric_netbox.models import IPFabricRelationshipField
|
|
@@ -25,6 +29,7 @@ class IPFabricTransformMapGroupTest(APIViewTestCases.APIViewTestCase):
|
|
|
25
29
|
graphql_base_name = "ipfabric_transform_map_group"
|
|
26
30
|
brief_fields = [
|
|
27
31
|
"description",
|
|
32
|
+
"display",
|
|
28
33
|
"id",
|
|
29
34
|
"name",
|
|
30
35
|
]
|
|
@@ -56,10 +61,11 @@ class IPFabricTransformMapTest(APIViewTestCases.APIViewTestCase):
|
|
|
56
61
|
model = IPFabricTransformMap
|
|
57
62
|
graphql_base_name = "ipfabric_transform_map"
|
|
58
63
|
brief_fields = [
|
|
64
|
+
"display",
|
|
59
65
|
"group",
|
|
60
66
|
"id",
|
|
61
67
|
"name",
|
|
62
|
-
"
|
|
68
|
+
"source_endpoint",
|
|
63
69
|
"target_model",
|
|
64
70
|
]
|
|
65
71
|
|
|
@@ -77,20 +83,32 @@ class IPFabricTransformMapTest(APIViewTestCases.APIViewTestCase):
|
|
|
77
83
|
IPFabricTransformMapGroup.objects.create(name="Group C"),
|
|
78
84
|
)
|
|
79
85
|
|
|
86
|
+
# Get existing endpoints created by migrations
|
|
87
|
+
endpoints = {
|
|
88
|
+
"site": IPFabricEndpoint.objects.get(endpoint="/inventory/sites/overview"),
|
|
89
|
+
"device": IPFabricEndpoint.objects.get(endpoint="/inventory/devices"),
|
|
90
|
+
"ipaddress": IPFabricEndpoint.objects.get(
|
|
91
|
+
endpoint="/technology/addressing/managed-ip/ipv4"
|
|
92
|
+
),
|
|
93
|
+
"vrf": IPFabricEndpoint.objects.get(
|
|
94
|
+
endpoint="/technology/routing/vrf/detail"
|
|
95
|
+
),
|
|
96
|
+
}
|
|
97
|
+
|
|
80
98
|
IPFabricTransformMap.objects.create(
|
|
81
99
|
name="TransformMap D",
|
|
82
|
-
|
|
100
|
+
source_endpoint=endpoints["site"],
|
|
83
101
|
target_model=ContentType.objects.get(app_label="dcim", model="site"),
|
|
84
102
|
)
|
|
85
103
|
IPFabricTransformMap.objects.create(
|
|
86
104
|
name="TransformMap E",
|
|
87
|
-
|
|
105
|
+
source_endpoint=endpoints["site"],
|
|
88
106
|
target_model=ContentType.objects.get(app_label="dcim", model="site"),
|
|
89
107
|
group=groups[0],
|
|
90
108
|
)
|
|
91
109
|
IPFabricTransformMap.objects.create(
|
|
92
110
|
name="TransformMap F",
|
|
93
|
-
|
|
111
|
+
source_endpoint=endpoints["ipaddress"],
|
|
94
112
|
target_model=ContentType.objects.get(app_label="ipam", model="ipaddress"),
|
|
95
113
|
group=groups[0],
|
|
96
114
|
)
|
|
@@ -98,19 +116,19 @@ class IPFabricTransformMapTest(APIViewTestCases.APIViewTestCase):
|
|
|
98
116
|
cls.create_data = [
|
|
99
117
|
{
|
|
100
118
|
"name": "Transform Map A",
|
|
101
|
-
"
|
|
119
|
+
"source_endpoint": endpoints["site"].pk,
|
|
102
120
|
"target_model": "dcim.site",
|
|
103
121
|
"group": groups[1].pk,
|
|
104
122
|
},
|
|
105
123
|
{
|
|
106
124
|
"name": "Transform Map B",
|
|
107
|
-
"
|
|
125
|
+
"source_endpoint": endpoints["device"].pk,
|
|
108
126
|
"target_model": "dcim.device",
|
|
109
127
|
"group": groups[1].pk,
|
|
110
128
|
},
|
|
111
129
|
{
|
|
112
130
|
"name": "Transform Map C",
|
|
113
|
-
"
|
|
131
|
+
"source_endpoint": endpoints["vrf"].pk,
|
|
114
132
|
"target_model": "ipam.vrf",
|
|
115
133
|
"group": groups[1].pk,
|
|
116
134
|
},
|
|
@@ -126,6 +144,7 @@ class IPFabricTransformFieldTest(APIViewTestCases.APIViewTestCase):
|
|
|
126
144
|
# in this case brief fields are the same, but they are needed fot the test
|
|
127
145
|
brief_fields = [
|
|
128
146
|
"coalesce",
|
|
147
|
+
"display",
|
|
129
148
|
"id",
|
|
130
149
|
"source_field",
|
|
131
150
|
"target_field",
|
|
@@ -148,23 +167,32 @@ class IPFabricTransformFieldTest(APIViewTestCases.APIViewTestCase):
|
|
|
148
167
|
IPFabricTransformMapGroup.objects.create(name="Field Test Group C"),
|
|
149
168
|
)
|
|
150
169
|
|
|
170
|
+
# Get existing endpoints created by migrations
|
|
171
|
+
endpoints = {
|
|
172
|
+
"site": IPFabricEndpoint.objects.get(endpoint="/inventory/sites/overview"),
|
|
173
|
+
"device": IPFabricEndpoint.objects.get(endpoint="/inventory/devices"),
|
|
174
|
+
"ipaddress": IPFabricEndpoint.objects.get(
|
|
175
|
+
endpoint="/technology/addressing/managed-ip/ipv4"
|
|
176
|
+
),
|
|
177
|
+
}
|
|
178
|
+
|
|
151
179
|
# Create transform maps for the fields to reference
|
|
152
180
|
transform_maps = [
|
|
153
181
|
IPFabricTransformMap.objects.create(
|
|
154
182
|
name="Field Map A",
|
|
155
|
-
|
|
183
|
+
source_endpoint=endpoints["site"],
|
|
156
184
|
target_model=ContentType.objects.get(app_label="dcim", model="site"),
|
|
157
185
|
group=groups[0],
|
|
158
186
|
),
|
|
159
187
|
IPFabricTransformMap.objects.create(
|
|
160
188
|
name="Field Map B",
|
|
161
|
-
|
|
189
|
+
source_endpoint=endpoints["device"],
|
|
162
190
|
target_model=ContentType.objects.get(app_label="dcim", model="device"),
|
|
163
191
|
group=groups[0],
|
|
164
192
|
),
|
|
165
193
|
IPFabricTransformMap.objects.create(
|
|
166
194
|
name="Field Map C",
|
|
167
|
-
|
|
195
|
+
source_endpoint=endpoints["ipaddress"],
|
|
168
196
|
target_model=ContentType.objects.get(
|
|
169
197
|
app_label="ipam", model="ipaddress"
|
|
170
198
|
),
|
|
@@ -229,6 +257,7 @@ class IPFabricRelationshipFieldTest(APIViewTestCases.APIViewTestCase):
|
|
|
229
257
|
# in this case brief fields are the same, but they are needed fot the test
|
|
230
258
|
brief_fields = [
|
|
231
259
|
"coalesce",
|
|
260
|
+
"display",
|
|
232
261
|
"id",
|
|
233
262
|
"source_model",
|
|
234
263
|
"target_field",
|
|
@@ -251,23 +280,32 @@ class IPFabricRelationshipFieldTest(APIViewTestCases.APIViewTestCase):
|
|
|
251
280
|
IPFabricTransformMapGroup.objects.create(name="Relationship Test Group C"),
|
|
252
281
|
)
|
|
253
282
|
|
|
283
|
+
# Get existing endpoints created by migrations
|
|
284
|
+
endpoints = {
|
|
285
|
+
"site": IPFabricEndpoint.objects.get(endpoint="/inventory/sites/overview"),
|
|
286
|
+
"device": IPFabricEndpoint.objects.get(endpoint="/inventory/devices"),
|
|
287
|
+
"ipaddress": IPFabricEndpoint.objects.get(
|
|
288
|
+
endpoint="/technology/addressing/managed-ip/ipv4"
|
|
289
|
+
),
|
|
290
|
+
}
|
|
291
|
+
|
|
254
292
|
# Create transform maps for the relationship fields to reference
|
|
255
293
|
transform_maps = [
|
|
256
294
|
IPFabricTransformMap.objects.create(
|
|
257
295
|
name="Relationship Map A",
|
|
258
|
-
|
|
296
|
+
source_endpoint=endpoints["site"],
|
|
259
297
|
target_model=ContentType.objects.get(app_label="dcim", model="site"),
|
|
260
298
|
group=groups[0],
|
|
261
299
|
),
|
|
262
300
|
IPFabricTransformMap.objects.create(
|
|
263
301
|
name="Relationship Map B",
|
|
264
|
-
|
|
302
|
+
source_endpoint=endpoints["device"],
|
|
265
303
|
target_model=ContentType.objects.get(app_label="dcim", model="device"),
|
|
266
304
|
group=groups[0],
|
|
267
305
|
),
|
|
268
306
|
IPFabricTransformMap.objects.create(
|
|
269
307
|
name="Relationship Map C",
|
|
270
|
-
|
|
308
|
+
source_endpoint=endpoints["ipaddress"],
|
|
271
309
|
target_model=ContentType.objects.get(
|
|
272
310
|
app_label="ipam", model="ipaddress"
|
|
273
311
|
),
|
|
@@ -547,6 +585,30 @@ class IPFabricSnapshotTest(
|
|
|
547
585
|
labels = [i["name"].lower() for i in body["results"]]
|
|
548
586
|
self.assertTrue(all("site" in name for name in labels))
|
|
549
587
|
|
|
588
|
+
def test_sites_action_with_no_data(self):
|
|
589
|
+
"""Test sites endpoint when snapshot.data is None."""
|
|
590
|
+
self.add_permissions("ipfabric_netbox.view_ipfabricsnapshot")
|
|
591
|
+
|
|
592
|
+
# Create a snapshot with data=None
|
|
593
|
+
source = IPFabricSource.objects.first()
|
|
594
|
+
snapshot_no_data = IPFabricSnapshot.objects.create(
|
|
595
|
+
name="Snapshot No Data",
|
|
596
|
+
source=source,
|
|
597
|
+
snapshot_id="snap-no-data",
|
|
598
|
+
status="unloaded",
|
|
599
|
+
data=None,
|
|
600
|
+
date=timezone.now(),
|
|
601
|
+
last_updated=timezone.now(),
|
|
602
|
+
)
|
|
603
|
+
|
|
604
|
+
# Call sites endpoint on snapshot with no data
|
|
605
|
+
url = f"{BASE}/snapshot/{snapshot_no_data.pk}/sites/"
|
|
606
|
+
resp = self.client.get(url, **self.header)
|
|
607
|
+
self.assertHttpStatus(resp, status.HTTP_200_OK)
|
|
608
|
+
body = resp.json()
|
|
609
|
+
# Should return empty list when data is None
|
|
610
|
+
self.assertEqual(body, [])
|
|
611
|
+
|
|
550
612
|
def test_raw_patch_and_delete(self):
|
|
551
613
|
self.add_permissions(
|
|
552
614
|
"ipfabric_netbox.view_ipfabricsnapshot",
|
|
@@ -585,6 +647,7 @@ class IPFabricSyncTest(APIViewTestCases.APIViewTestCase):
|
|
|
585
647
|
graphql_base_name = "ipfabric_sync"
|
|
586
648
|
brief_fields = [
|
|
587
649
|
"auto_merge",
|
|
650
|
+
"display",
|
|
588
651
|
"id",
|
|
589
652
|
"last_synced",
|
|
590
653
|
"name",
|
|
@@ -595,16 +658,19 @@ class IPFabricSyncTest(APIViewTestCases.APIViewTestCase):
|
|
|
595
658
|
{
|
|
596
659
|
"name": "Test Sync A",
|
|
597
660
|
"parameters": {"site": True, "device": False},
|
|
661
|
+
"filters": [],
|
|
598
662
|
},
|
|
599
663
|
{
|
|
600
664
|
"name": "Test Sync B",
|
|
601
665
|
"parameters": {"ipaddress": True, "prefix": True},
|
|
602
666
|
"auto_merge": True,
|
|
667
|
+
"filters": [],
|
|
603
668
|
},
|
|
604
669
|
{
|
|
605
670
|
"name": "Test Sync C",
|
|
606
671
|
"parameters": {"device": True, "interface": True},
|
|
607
672
|
"interval": 60,
|
|
673
|
+
"filters": [],
|
|
608
674
|
},
|
|
609
675
|
]
|
|
610
676
|
bulk_update_data = {
|
|
@@ -775,6 +841,7 @@ class IPFabricIngestionTest(
|
|
|
775
841
|
graphql_base_name = "ipfabric_ingestion"
|
|
776
842
|
brief_fields = [
|
|
777
843
|
"branch",
|
|
844
|
+
"display",
|
|
778
845
|
"id",
|
|
779
846
|
"name",
|
|
780
847
|
"sync",
|
|
@@ -874,6 +941,7 @@ class IPFabricIngestionIssueTest(
|
|
|
874
941
|
model = IPFabricIngestionIssue
|
|
875
942
|
graphql_base_name = "ipfabric_ingestion_issue"
|
|
876
943
|
brief_fields = [
|
|
944
|
+
"display",
|
|
877
945
|
"exception",
|
|
878
946
|
"id",
|
|
879
947
|
"ingestion",
|
|
@@ -1005,3 +1073,255 @@ class IPFabricIngestionIssueTest(
|
|
|
1005
1073
|
defaults='{"dns_name": ""}',
|
|
1006
1074
|
exception="IntegrityError: IP address 192.168.1.1/24 already exists",
|
|
1007
1075
|
)
|
|
1076
|
+
|
|
1077
|
+
|
|
1078
|
+
class IPFabricEndpointTest(
|
|
1079
|
+
APIViewTestCases.GetObjectViewTestCase,
|
|
1080
|
+
APIViewTestCases.ListObjectsViewTestCase,
|
|
1081
|
+
APIViewTestCases.GraphQLTestCase,
|
|
1082
|
+
):
|
|
1083
|
+
model = IPFabricEndpoint
|
|
1084
|
+
brief_fields = [
|
|
1085
|
+
"display",
|
|
1086
|
+
"endpoint",
|
|
1087
|
+
"id",
|
|
1088
|
+
"name",
|
|
1089
|
+
]
|
|
1090
|
+
graphql_base_name = "ipfabric_endpoint"
|
|
1091
|
+
|
|
1092
|
+
def _get_list_url(self):
|
|
1093
|
+
return f"{BASE}/endpoint/"
|
|
1094
|
+
|
|
1095
|
+
def _get_detail_url(self, instance):
|
|
1096
|
+
return f"{BASE}/endpoint/{instance.pk}/"
|
|
1097
|
+
|
|
1098
|
+
@classmethod
|
|
1099
|
+
def setUpTestData(cls):
|
|
1100
|
+
# Note: IPFabricEndpoint is read-only in the API (NetBoxReadOnlyModelViewSet)
|
|
1101
|
+
# The endpoints are created by migrations and should not be modified via API
|
|
1102
|
+
# We rely on the existing endpoints created by migrations for these tests
|
|
1103
|
+
pass
|
|
1104
|
+
|
|
1105
|
+
|
|
1106
|
+
class IPFabricFilterTest(
|
|
1107
|
+
APIViewTestCases.GetObjectViewTestCase,
|
|
1108
|
+
APIViewTestCases.ListObjectsViewTestCase,
|
|
1109
|
+
APIViewTestCases.GraphQLTestCase,
|
|
1110
|
+
):
|
|
1111
|
+
model = IPFabricFilter
|
|
1112
|
+
brief_fields = [
|
|
1113
|
+
"display",
|
|
1114
|
+
"endpoints",
|
|
1115
|
+
"expressions",
|
|
1116
|
+
"filter_type",
|
|
1117
|
+
"id",
|
|
1118
|
+
"name",
|
|
1119
|
+
"syncs",
|
|
1120
|
+
]
|
|
1121
|
+
graphql_base_name = "ip_fabric_filter"
|
|
1122
|
+
|
|
1123
|
+
def _get_list_url(self):
|
|
1124
|
+
return f"{BASE}/filter/"
|
|
1125
|
+
|
|
1126
|
+
def _get_detail_url(self, instance):
|
|
1127
|
+
return f"{BASE}/filter/{instance.pk}/"
|
|
1128
|
+
|
|
1129
|
+
@classmethod
|
|
1130
|
+
def setUpTestData(cls):
|
|
1131
|
+
# Get existing endpoints created by migrations
|
|
1132
|
+
endpoints = {
|
|
1133
|
+
"site": IPFabricEndpoint.objects.get(endpoint="/inventory/sites/overview"),
|
|
1134
|
+
"device": IPFabricEndpoint.objects.get(endpoint="/inventory/devices"),
|
|
1135
|
+
"ipaddress": IPFabricEndpoint.objects.get(
|
|
1136
|
+
endpoint="/technology/addressing/managed-ip/ipv4"
|
|
1137
|
+
),
|
|
1138
|
+
"vrf": IPFabricEndpoint.objects.get(
|
|
1139
|
+
endpoint="/technology/routing/vrf/detail"
|
|
1140
|
+
),
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
# Create sources for snapshots
|
|
1144
|
+
sources = (
|
|
1145
|
+
IPFabricSource.objects.create(
|
|
1146
|
+
name="Filter Test Source A",
|
|
1147
|
+
url="https://filter-a.local",
|
|
1148
|
+
parameters={"auth": "token", "verify": True},
|
|
1149
|
+
last_synced=timezone.now(),
|
|
1150
|
+
),
|
|
1151
|
+
IPFabricSource.objects.create(
|
|
1152
|
+
name="Filter Test Source B",
|
|
1153
|
+
url="https://filter-b.local",
|
|
1154
|
+
parameters={"auth": "token", "verify": False},
|
|
1155
|
+
last_synced=timezone.now(),
|
|
1156
|
+
),
|
|
1157
|
+
)
|
|
1158
|
+
|
|
1159
|
+
# Create snapshots for syncs
|
|
1160
|
+
snapshots = (
|
|
1161
|
+
IPFabricSnapshot.objects.create(
|
|
1162
|
+
name="Filter Test Snapshot A",
|
|
1163
|
+
source=sources[0],
|
|
1164
|
+
snapshot_id="filter-snap-a",
|
|
1165
|
+
status="loaded",
|
|
1166
|
+
data={"sites": ["FilterSiteA"]},
|
|
1167
|
+
date=timezone.now(),
|
|
1168
|
+
last_updated=timezone.now(),
|
|
1169
|
+
),
|
|
1170
|
+
IPFabricSnapshot.objects.create(
|
|
1171
|
+
name="Filter Test Snapshot B",
|
|
1172
|
+
source=sources[1],
|
|
1173
|
+
snapshot_id="filter-snap-b",
|
|
1174
|
+
status="loaded",
|
|
1175
|
+
data={"devices": ["FilterDevice1"]},
|
|
1176
|
+
date=timezone.now(),
|
|
1177
|
+
last_updated=timezone.now(),
|
|
1178
|
+
),
|
|
1179
|
+
)
|
|
1180
|
+
|
|
1181
|
+
# Create syncs to associate with filters
|
|
1182
|
+
syncs = (
|
|
1183
|
+
IPFabricSync.objects.create(
|
|
1184
|
+
name="Filter Test Sync A",
|
|
1185
|
+
snapshot_data=snapshots[0],
|
|
1186
|
+
parameters={"site": True, "device": False},
|
|
1187
|
+
),
|
|
1188
|
+
IPFabricSync.objects.create(
|
|
1189
|
+
name="Filter Test Sync B",
|
|
1190
|
+
snapshot_data=snapshots[1],
|
|
1191
|
+
parameters={"device": True, "interface": True},
|
|
1192
|
+
),
|
|
1193
|
+
)
|
|
1194
|
+
|
|
1195
|
+
# Create existing filters for testing
|
|
1196
|
+
IPFabricFilter.objects.create(
|
|
1197
|
+
name="Filter D",
|
|
1198
|
+
description="Sites and devices filter",
|
|
1199
|
+
filter_type=IPFabricFilterTypeChoices.AND,
|
|
1200
|
+
)
|
|
1201
|
+
IPFabricFilter.objects.create(
|
|
1202
|
+
name="Filter E",
|
|
1203
|
+
description="Device and VRF filter",
|
|
1204
|
+
filter_type=IPFabricFilterTypeChoices.OR,
|
|
1205
|
+
)
|
|
1206
|
+
IPFabricFilter.objects.create(
|
|
1207
|
+
name="Filter F",
|
|
1208
|
+
description="IP address filter",
|
|
1209
|
+
filter_type=IPFabricFilterTypeChoices.AND,
|
|
1210
|
+
)
|
|
1211
|
+
|
|
1212
|
+
cls.create_data = [
|
|
1213
|
+
{
|
|
1214
|
+
"name": "Filter A",
|
|
1215
|
+
"description": "Test filter A",
|
|
1216
|
+
"filter_type": IPFabricFilterTypeChoices.AND,
|
|
1217
|
+
"endpoints": [endpoints["site"].pk, endpoints["device"].pk],
|
|
1218
|
+
"syncs": [syncs[0].pk],
|
|
1219
|
+
},
|
|
1220
|
+
{
|
|
1221
|
+
"name": "Filter B",
|
|
1222
|
+
"description": "Test filter B",
|
|
1223
|
+
"filter_type": IPFabricFilterTypeChoices.OR,
|
|
1224
|
+
"endpoints": [endpoints["device"].pk],
|
|
1225
|
+
"syncs": [syncs[1].pk],
|
|
1226
|
+
},
|
|
1227
|
+
{
|
|
1228
|
+
"name": "Filter C",
|
|
1229
|
+
"description": "Test filter C",
|
|
1230
|
+
"filter_type": IPFabricFilterTypeChoices.AND,
|
|
1231
|
+
"endpoints": [endpoints["ipaddress"].pk, endpoints["vrf"].pk],
|
|
1232
|
+
"syncs": [],
|
|
1233
|
+
},
|
|
1234
|
+
]
|
|
1235
|
+
cls.bulk_update_data = {
|
|
1236
|
+
"description": "Updated filter description",
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
|
|
1240
|
+
class IPFabricFilterExpressionTest(
|
|
1241
|
+
APIViewTestCases.GetObjectViewTestCase,
|
|
1242
|
+
APIViewTestCases.ListObjectsViewTestCase,
|
|
1243
|
+
APIViewTestCases.GraphQLTestCase,
|
|
1244
|
+
):
|
|
1245
|
+
model = IPFabricFilterExpression
|
|
1246
|
+
brief_fields = [
|
|
1247
|
+
"display",
|
|
1248
|
+
"expression",
|
|
1249
|
+
"filters",
|
|
1250
|
+
"id",
|
|
1251
|
+
"name",
|
|
1252
|
+
]
|
|
1253
|
+
graphql_base_name = "ip_fabric_filter_expression"
|
|
1254
|
+
|
|
1255
|
+
def _get_list_url(self):
|
|
1256
|
+
return f"{BASE}/filter-expression/"
|
|
1257
|
+
|
|
1258
|
+
def _get_detail_url(self, instance):
|
|
1259
|
+
return f"{BASE}/filter-expression/{instance.pk}/"
|
|
1260
|
+
|
|
1261
|
+
@classmethod
|
|
1262
|
+
def setUpTestData(cls):
|
|
1263
|
+
# Create filters to associate with expressions
|
|
1264
|
+
filters = (
|
|
1265
|
+
IPFabricFilter.objects.create(
|
|
1266
|
+
name="Expression Test Filter A",
|
|
1267
|
+
description="First expression test filter",
|
|
1268
|
+
filter_type=IPFabricFilterTypeChoices.AND,
|
|
1269
|
+
),
|
|
1270
|
+
IPFabricFilter.objects.create(
|
|
1271
|
+
name="Expression Test Filter B",
|
|
1272
|
+
description="Second expression test filter",
|
|
1273
|
+
filter_type=IPFabricFilterTypeChoices.OR,
|
|
1274
|
+
),
|
|
1275
|
+
IPFabricFilter.objects.create(
|
|
1276
|
+
name="Expression Test Filter C",
|
|
1277
|
+
description="Third expression test filter",
|
|
1278
|
+
filter_type=IPFabricFilterTypeChoices.AND,
|
|
1279
|
+
),
|
|
1280
|
+
)
|
|
1281
|
+
|
|
1282
|
+
# Create existing filter expressions for testing
|
|
1283
|
+
IPFabricFilterExpression.objects.create(
|
|
1284
|
+
name="Expression D",
|
|
1285
|
+
description="Sites expression",
|
|
1286
|
+
expression=[{"siteName": ["eq", "Site1"]}],
|
|
1287
|
+
)
|
|
1288
|
+
IPFabricFilterExpression.objects.create(
|
|
1289
|
+
name="Expression E",
|
|
1290
|
+
description="Devices expression",
|
|
1291
|
+
expression=[{"hostname": ["like", "router%"]}],
|
|
1292
|
+
)
|
|
1293
|
+
IPFabricFilterExpression.objects.create(
|
|
1294
|
+
name="Expression F",
|
|
1295
|
+
description="Complex expression",
|
|
1296
|
+
expression=[
|
|
1297
|
+
{"siteName": ["eq", "Site1"]},
|
|
1298
|
+
{"hostname": ["like", "switch%"]},
|
|
1299
|
+
],
|
|
1300
|
+
)
|
|
1301
|
+
|
|
1302
|
+
cls.create_data = [
|
|
1303
|
+
{
|
|
1304
|
+
"name": "Expression A",
|
|
1305
|
+
"description": "Test expression A",
|
|
1306
|
+
"expression": [{"siteName": ["eq", "TestSite"]}],
|
|
1307
|
+
"filters": [filters[0].pk],
|
|
1308
|
+
},
|
|
1309
|
+
{
|
|
1310
|
+
"name": "Expression B",
|
|
1311
|
+
"description": "Test expression B",
|
|
1312
|
+
"expression": [{"hostname": ["like", "test-router%"]}],
|
|
1313
|
+
"filters": [filters[1].pk, filters[2].pk],
|
|
1314
|
+
},
|
|
1315
|
+
{
|
|
1316
|
+
"name": "Expression C",
|
|
1317
|
+
"description": "Test expression C",
|
|
1318
|
+
"expression": [
|
|
1319
|
+
{"siteName": ["eq", "TestSite2"]},
|
|
1320
|
+
{"vendor": ["eq", "Cisco"]},
|
|
1321
|
+
],
|
|
1322
|
+
"filters": [filters[0].pk],
|
|
1323
|
+
},
|
|
1324
|
+
]
|
|
1325
|
+
cls.bulk_update_data = {
|
|
1326
|
+
"description": "Updated expression description",
|
|
1327
|
+
}
|