ipfabric_netbox 4.3.2b10__py3-none-any.whl → 4.3.2b11__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/choices.py +2 -0
- ipfabric_netbox/data/endpoint.json +5 -0
- ipfabric_netbox/data/filters.json +1 -1
- ipfabric_netbox/data/transform_map.json +3 -3
- ipfabric_netbox/forms.py +10 -5
- ipfabric_netbox/jobs.py +10 -3
- ipfabric_netbox/migrations/0023_populate_filters_data.py +24 -0
- ipfabric_netbox/migrations/0025_add_vss_chassis_endpoint.py +166 -0
- ipfabric_netbox/models.py +72 -29
- ipfabric_netbox/templatetags/ipfabric_netbox_helpers.py +7 -4
- ipfabric_netbox/tests/test_forms.py +93 -0
- ipfabric_netbox/tests/test_views.py +1 -1
- ipfabric_netbox/utilities/endpoint.py +53 -0
- ipfabric_netbox/utilities/ipfutils.py +252 -174
- ipfabric_netbox/utilities/transform_map.py +18 -5
- {ipfabric_netbox-4.3.2b10.dist-info → ipfabric_netbox-4.3.2b11.dist-info}/METADATA +3 -4
- {ipfabric_netbox-4.3.2b10.dist-info → ipfabric_netbox-4.3.2b11.dist-info}/RECORD +19 -18
- {ipfabric_netbox-4.3.2b10.dist-info → ipfabric_netbox-4.3.2b11.dist-info}/WHEEL +1 -1
|
@@ -1843,6 +1843,99 @@ class IPFabricSyncFormTestCase(TestCase):
|
|
|
1843
1843
|
fieldset_names2 = [fs.name for fs in fieldsets2]
|
|
1844
1844
|
self.assertEqual(fieldset_names1, fieldset_names2)
|
|
1845
1845
|
|
|
1846
|
+
def test_save_method_with_filters(self):
|
|
1847
|
+
"""Test that filters many-to-many relationship is properly saved"""
|
|
1848
|
+
# Create some filter objects
|
|
1849
|
+
endpoint = IPFabricEndpoint.objects.create(
|
|
1850
|
+
name="Test Endpoint",
|
|
1851
|
+
endpoint="inventory/devices",
|
|
1852
|
+
)
|
|
1853
|
+
filter1 = IPFabricFilter.objects.create(
|
|
1854
|
+
name="Filter 1",
|
|
1855
|
+
filter_type="and",
|
|
1856
|
+
)
|
|
1857
|
+
filter1.endpoints.add(endpoint)
|
|
1858
|
+
|
|
1859
|
+
filter2 = IPFabricFilter.objects.create(
|
|
1860
|
+
name="Filter 2",
|
|
1861
|
+
filter_type="or",
|
|
1862
|
+
)
|
|
1863
|
+
filter2.endpoints.add(endpoint)
|
|
1864
|
+
|
|
1865
|
+
# Create a sync with filters
|
|
1866
|
+
form = IPFabricSyncForm(
|
|
1867
|
+
data={
|
|
1868
|
+
"name": "Test Sync With Filters",
|
|
1869
|
+
"source": self.source.pk,
|
|
1870
|
+
"snapshot_data": self.snapshot.pk,
|
|
1871
|
+
"filters": [filter1.pk, filter2.pk],
|
|
1872
|
+
}
|
|
1873
|
+
)
|
|
1874
|
+
|
|
1875
|
+
self.assertTrue(form.is_valid(), form.errors)
|
|
1876
|
+
sync_instance = form.save()
|
|
1877
|
+
|
|
1878
|
+
# Verify filters were saved correctly
|
|
1879
|
+
self.assertEqual(sync_instance.filters.count(), 2)
|
|
1880
|
+
self.assertIn(filter1, sync_instance.filters.all())
|
|
1881
|
+
self.assertIn(filter2, sync_instance.filters.all())
|
|
1882
|
+
|
|
1883
|
+
def test_update_method_with_filters(self):
|
|
1884
|
+
"""Test that filters can be updated on existing sync"""
|
|
1885
|
+
# Create some filter objects
|
|
1886
|
+
endpoint = IPFabricEndpoint.objects.create(
|
|
1887
|
+
name="Test Endpoint Update",
|
|
1888
|
+
endpoint="inventory/devices",
|
|
1889
|
+
)
|
|
1890
|
+
filter1 = IPFabricFilter.objects.create(
|
|
1891
|
+
name="Filter Update 1",
|
|
1892
|
+
filter_type="and",
|
|
1893
|
+
)
|
|
1894
|
+
filter1.endpoints.add(endpoint)
|
|
1895
|
+
|
|
1896
|
+
filter2 = IPFabricFilter.objects.create(
|
|
1897
|
+
name="Filter Update 2",
|
|
1898
|
+
filter_type="or",
|
|
1899
|
+
)
|
|
1900
|
+
filter2.endpoints.add(endpoint)
|
|
1901
|
+
|
|
1902
|
+
filter3 = IPFabricFilter.objects.create(
|
|
1903
|
+
name="Filter Update 3",
|
|
1904
|
+
filter_type="and",
|
|
1905
|
+
)
|
|
1906
|
+
filter3.endpoints.add(endpoint)
|
|
1907
|
+
|
|
1908
|
+
# Create a sync with initial filters
|
|
1909
|
+
sync_instance = IPFabricSync.objects.create(
|
|
1910
|
+
name="Existing Sync With Filters",
|
|
1911
|
+
snapshot_data=self.snapshot,
|
|
1912
|
+
parameters={},
|
|
1913
|
+
)
|
|
1914
|
+
sync_instance.filters.set([filter1, filter2])
|
|
1915
|
+
|
|
1916
|
+
# Verify initial state
|
|
1917
|
+
self.assertEqual(sync_instance.filters.count(), 2)
|
|
1918
|
+
|
|
1919
|
+
# Update the sync with different filters
|
|
1920
|
+
form = IPFabricSyncForm(
|
|
1921
|
+
instance=sync_instance,
|
|
1922
|
+
data={
|
|
1923
|
+
"name": "Existing Sync With Filters",
|
|
1924
|
+
"source": self.source.pk,
|
|
1925
|
+
"snapshot_data": self.snapshot.pk,
|
|
1926
|
+
"filters": [filter2.pk, filter3.pk], # Changed filters
|
|
1927
|
+
},
|
|
1928
|
+
)
|
|
1929
|
+
|
|
1930
|
+
self.assertTrue(form.is_valid(), form.errors)
|
|
1931
|
+
updated_instance = form.save()
|
|
1932
|
+
|
|
1933
|
+
# Verify filters were updated correctly
|
|
1934
|
+
self.assertEqual(updated_instance.filters.count(), 2)
|
|
1935
|
+
self.assertNotIn(filter1, updated_instance.filters.all())
|
|
1936
|
+
self.assertIn(filter2, updated_instance.filters.all())
|
|
1937
|
+
self.assertIn(filter3, updated_instance.filters.all())
|
|
1938
|
+
|
|
1846
1939
|
|
|
1847
1940
|
class IPFabricFilterFormTestCase(TestCase):
|
|
1848
1941
|
"""Test cases for IPFabricFilterForm"""
|
|
@@ -1692,7 +1692,7 @@ class IPFabricTransformMapTestCase(
|
|
|
1692
1692
|
self.assertHttpStatus(response, 200)
|
|
1693
1693
|
self.assertContains(
|
|
1694
1694
|
response,
|
|
1695
|
-
"A transform map with group 'Test Group'
|
|
1695
|
+
"A transform map with group 'Test Group', target model 'DCIM | site', and source endpoint '/inventory/sites/overview' already exists.",
|
|
1696
1696
|
)
|
|
1697
1697
|
self.assertIn("X-Debug-HTMX-Partial", response)
|
|
1698
1698
|
|
|
@@ -28,3 +28,56 @@ def get_endpoint_data() -> dict:
|
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
# endregion Endpoint Creation
|
|
31
|
+
|
|
32
|
+
# region Endpoint Updating
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class EndpointRecord:
|
|
36
|
+
"""Record for endpoint creation/deletion in migrations."""
|
|
37
|
+
|
|
38
|
+
def __init__(self, name: str, description: str, endpoint: str):
|
|
39
|
+
self.name = name
|
|
40
|
+
self.description = description
|
|
41
|
+
self.endpoint = endpoint
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def do_endpoint_change(
|
|
45
|
+
apps, schema_editor, endpoints: tuple[EndpointRecord, ...], forward: bool = True
|
|
46
|
+
):
|
|
47
|
+
"""
|
|
48
|
+
Apply endpoint changes, `forward` determines direction.
|
|
49
|
+
|
|
50
|
+
Forward: Creates endpoints if they don't exist (idempotent using get_or_create).
|
|
51
|
+
Reverse: Deletes endpoints by endpoint path.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
apps: Django apps registry (historical models)
|
|
55
|
+
schema_editor: Database schema editor
|
|
56
|
+
endpoints: Tuple of EndpointRecord objects to process
|
|
57
|
+
forward: True for forward migration (create), False for reverse (delete)
|
|
58
|
+
"""
|
|
59
|
+
IPFabricEndpoint = apps.get_model("ipfabric_netbox", "IPFabricEndpoint")
|
|
60
|
+
db_alias = schema_editor.connection.alias
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
for endpoint_record in endpoints:
|
|
64
|
+
if forward:
|
|
65
|
+
# Forward: Create endpoint if it doesn't exist (idempotent)
|
|
66
|
+
IPFabricEndpoint.objects.using(db_alias).get_or_create(
|
|
67
|
+
endpoint=endpoint_record.endpoint,
|
|
68
|
+
defaults={
|
|
69
|
+
"name": endpoint_record.name,
|
|
70
|
+
"description": endpoint_record.description,
|
|
71
|
+
},
|
|
72
|
+
)
|
|
73
|
+
else:
|
|
74
|
+
# Reverse: Delete endpoint by endpoint path
|
|
75
|
+
IPFabricEndpoint.objects.using(db_alias).filter(
|
|
76
|
+
endpoint=endpoint_record.endpoint
|
|
77
|
+
).delete()
|
|
78
|
+
|
|
79
|
+
except Exception as e:
|
|
80
|
+
print(f"Error applying endpoint changes: {e}")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# endregion
|