ipfabric_netbox 4.3.0b6__py3-none-any.whl → 4.3.0b7__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 +1 -1
- ipfabric_netbox/api/serializers.py +5 -0
- ipfabric_netbox/api/views.py +47 -0
- ipfabric_netbox/forms.py +1 -5
- ipfabric_netbox/models.py +5 -0
- ipfabric_netbox/templates/ipfabric_netbox/ipfabricsync.html +1 -1
- ipfabric_netbox/tests/api/test_api.py +149 -22
- ipfabric_netbox/tests/test_views.py +4 -1
- ipfabric_netbox/views.py +10 -2
- {ipfabric_netbox-4.3.0b6.dist-info → ipfabric_netbox-4.3.0b7.dist-info}/METADATA +1 -1
- {ipfabric_netbox-4.3.0b6.dist-info → ipfabric_netbox-4.3.0b7.dist-info}/RECORD +12 -12
- {ipfabric_netbox-4.3.0b6.dist-info → ipfabric_netbox-4.3.0b7.dist-info}/WHEEL +0 -0
ipfabric_netbox/__init__.py
CHANGED
|
@@ -176,6 +176,7 @@ class IPFabricSnapshotSerializer(NestedGroupModelSerializer):
|
|
|
176
176
|
|
|
177
177
|
class IPFabricSyncSerializer(NestedGroupModelSerializer):
|
|
178
178
|
snapshot_data = IPFabricSnapshotSerializer(nested=True)
|
|
179
|
+
parameters = serializers.JSONField()
|
|
179
180
|
|
|
180
181
|
class Meta:
|
|
181
182
|
model = IPFabricSync
|
|
@@ -244,3 +245,7 @@ class IPFabricIngestionIssueSerializer(NestedGroupModelSerializer):
|
|
|
244
245
|
"message",
|
|
245
246
|
"model",
|
|
246
247
|
)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
class EmptySerializer(serializers.Serializer):
|
|
251
|
+
pass
|
ipfabric_netbox/api/views.py
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
|
+
from core.api.serializers_.jobs import JobSerializer
|
|
2
|
+
from django.core.exceptions import PermissionDenied
|
|
1
3
|
from django.db import transaction
|
|
4
|
+
from django.http import HttpResponseBadRequest
|
|
5
|
+
from drf_spectacular.utils import extend_schema
|
|
2
6
|
from netbox.api.viewsets import NetBoxModelViewSet
|
|
3
7
|
from netbox.api.viewsets import NetBoxReadOnlyModelViewSet
|
|
4
8
|
from rest_framework.decorators import action
|
|
5
9
|
from rest_framework.response import Response
|
|
6
10
|
from utilities.query import count_related
|
|
7
11
|
|
|
12
|
+
from .serializers import EmptySerializer
|
|
8
13
|
from .serializers import IPFabricIngestionIssueSerializer
|
|
9
14
|
from .serializers import IPFabricIngestionSerializer
|
|
10
15
|
from .serializers import IPFabricRelationshipFieldSerializer
|
|
@@ -56,6 +61,27 @@ class IPFabricSyncViewSet(NetBoxModelViewSet):
|
|
|
56
61
|
queryset = IPFabricSync.objects.all()
|
|
57
62
|
serializer_class = IPFabricSyncSerializer
|
|
58
63
|
|
|
64
|
+
@extend_schema(
|
|
65
|
+
methods=["post"],
|
|
66
|
+
request=EmptySerializer(),
|
|
67
|
+
responses={201: JobSerializer()},
|
|
68
|
+
)
|
|
69
|
+
@action(detail=True, methods=["post"])
|
|
70
|
+
def sync(self, request, pk):
|
|
71
|
+
if not request.user.has_perm("ipfabric_netbox.sync_ipfabricsync"):
|
|
72
|
+
raise PermissionDenied(
|
|
73
|
+
"This user does not have permission to sync IPFabricSync."
|
|
74
|
+
)
|
|
75
|
+
sync = self.get_object()
|
|
76
|
+
if not sync.ready_for_sync:
|
|
77
|
+
return HttpResponseBadRequest(
|
|
78
|
+
f"Sync '{sync.name}' is not ready to be synced."
|
|
79
|
+
)
|
|
80
|
+
job = sync.enqueue_sync_job(user=request.user, adhoc=True)
|
|
81
|
+
return Response(
|
|
82
|
+
JobSerializer(job, context={"request": request}).data, status=201
|
|
83
|
+
)
|
|
84
|
+
|
|
59
85
|
|
|
60
86
|
class IPFabricIngestionViewSet(NetBoxReadOnlyModelViewSet):
|
|
61
87
|
queryset = IPFabricIngestion.objects.all()
|
|
@@ -122,3 +148,24 @@ class IPFabricSourceViewSet(NetBoxModelViewSet):
|
|
|
122
148
|
)
|
|
123
149
|
serializer_class = IPFabricSourceSerializer
|
|
124
150
|
filterset_class = IPFabricSourceFilterSet
|
|
151
|
+
|
|
152
|
+
@extend_schema(
|
|
153
|
+
methods=["post"],
|
|
154
|
+
request=EmptySerializer(),
|
|
155
|
+
responses={201: JobSerializer()},
|
|
156
|
+
)
|
|
157
|
+
@action(detail=True, methods=["post"])
|
|
158
|
+
def sync(self, request, pk):
|
|
159
|
+
if not request.user.has_perm("ipfabric_netbox.sync_ipfabricsource"):
|
|
160
|
+
raise PermissionDenied(
|
|
161
|
+
"This user does not have permission to sync IPFabricSource."
|
|
162
|
+
)
|
|
163
|
+
source = self.get_object()
|
|
164
|
+
if not source.ready_for_sync:
|
|
165
|
+
return HttpResponseBadRequest(
|
|
166
|
+
f"Source '{source.name}' is not ready to be synced."
|
|
167
|
+
)
|
|
168
|
+
job = source.enqueue_sync_job(request=request)
|
|
169
|
+
return Response(
|
|
170
|
+
JobSerializer(job, context={"request": request}).data, status=201
|
|
171
|
+
)
|
ipfabric_netbox/forms.py
CHANGED
|
@@ -802,11 +802,7 @@ class IPFabricSyncForm(NetBoxModelForm):
|
|
|
802
802
|
]
|
|
803
803
|
self.instance.parameters = parameters
|
|
804
804
|
self.instance.status = DataSourceStatusChoices.NEW
|
|
805
|
-
|
|
806
|
-
object = super().save(*args, **kwargs)
|
|
807
|
-
if object.scheduled:
|
|
808
|
-
object.enqueue_sync_job()
|
|
809
|
-
return object
|
|
805
|
+
return super().save(*args, **kwargs)
|
|
810
806
|
|
|
811
807
|
|
|
812
808
|
class IPFabricSyncBulkEditForm(NetBoxModelBulkEditForm):
|
ipfabric_netbox/models.py
CHANGED
|
@@ -669,6 +669,11 @@ class IPFabricSync(IPFabricClient, JobsMixin, TagsMixin, ChangeLoggedModel):
|
|
|
669
669
|
def get_status_color(self):
|
|
670
670
|
return DataSourceStatusChoices.colors.get(self.status)
|
|
671
671
|
|
|
672
|
+
def save(self, *args, **kwargs):
|
|
673
|
+
super().save(*args, **kwargs)
|
|
674
|
+
if self.scheduled:
|
|
675
|
+
self.enqueue_sync_job()
|
|
676
|
+
|
|
672
677
|
@property
|
|
673
678
|
def ready_for_sync(self):
|
|
674
679
|
if self.status not in (DataSourceStatusChoices.SYNCING,):
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
</tr>
|
|
59
59
|
<tr>
|
|
60
60
|
<th scope="row">Schedule</th>
|
|
61
|
-
<td><div>{{ object.scheduled }}</div></td>
|
|
61
|
+
<td><div>{% if scheduled_job %}<a href="{% url 'core:job' pk=scheduled_job.pk %}">{{ object.scheduled }}</a>{% else %} {{ object.scheduled | placeholder }} {% endif %}</div></td>
|
|
62
62
|
</tr>
|
|
63
63
|
<tr>
|
|
64
64
|
<th scope="row">Interval</th>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from core.choices import DataSourceStatusChoices
|
|
1
2
|
from django.contrib.contenttypes.models import ContentType
|
|
2
3
|
from django.utils import timezone
|
|
3
4
|
from rest_framework import status
|
|
@@ -15,7 +16,7 @@ from ipfabric_netbox.models import IPFabricTransformMap
|
|
|
15
16
|
from ipfabric_netbox.models import IPFabricTransformMapGroup
|
|
16
17
|
|
|
17
18
|
|
|
18
|
-
BASE = "/api/plugins/ipfabric
|
|
19
|
+
BASE = "/api/plugins/ipfabric"
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
class IPFabricTransformMapGroupTest(APIViewTestCases.APIViewTestCase):
|
|
@@ -36,10 +37,10 @@ class IPFabricTransformMapGroupTest(APIViewTestCases.APIViewTestCase):
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
def _get_list_url(self):
|
|
39
|
-
return f"{BASE}transform-map-group/"
|
|
40
|
+
return f"{BASE}/transform-map-group/"
|
|
40
41
|
|
|
41
42
|
def _get_detail_url(self, instance):
|
|
42
|
-
return f"{BASE}transform-map-group/{instance.pk}/"
|
|
43
|
+
return f"{BASE}/transform-map-group/{instance.pk}/"
|
|
43
44
|
|
|
44
45
|
@classmethod
|
|
45
46
|
def setUpTestData(cls):
|
|
@@ -62,10 +63,10 @@ class IPFabricTransformMapTest(APIViewTestCases.APIViewTestCase):
|
|
|
62
63
|
]
|
|
63
64
|
|
|
64
65
|
def _get_list_url(self):
|
|
65
|
-
return f"{BASE}transform-map/"
|
|
66
|
+
return f"{BASE}/transform-map/"
|
|
66
67
|
|
|
67
68
|
def _get_detail_url(self, instance):
|
|
68
|
-
return f"{BASE}transform-map/{instance.pk}/"
|
|
69
|
+
return f"{BASE}/transform-map/{instance.pk}/"
|
|
69
70
|
|
|
70
71
|
@classmethod
|
|
71
72
|
def setUpTestData(cls):
|
|
@@ -132,10 +133,10 @@ class IPFabricTransformFieldTest(APIViewTestCases.APIViewTestCase):
|
|
|
132
133
|
]
|
|
133
134
|
|
|
134
135
|
def _get_list_url(self):
|
|
135
|
-
return f"{BASE}transform-field/"
|
|
136
|
+
return f"{BASE}/transform-field/"
|
|
136
137
|
|
|
137
138
|
def _get_detail_url(self, instance):
|
|
138
|
-
return f"{BASE}transform-field/{instance.pk}/"
|
|
139
|
+
return f"{BASE}/transform-field/{instance.pk}/"
|
|
139
140
|
|
|
140
141
|
@classmethod
|
|
141
142
|
def setUpTestData(cls):
|
|
@@ -235,10 +236,10 @@ class IPFabricRelationshipFieldTest(APIViewTestCases.APIViewTestCase):
|
|
|
235
236
|
]
|
|
236
237
|
|
|
237
238
|
def _get_list_url(self):
|
|
238
|
-
return f"{BASE}relationship-field/"
|
|
239
|
+
return f"{BASE}/relationship-field/"
|
|
239
240
|
|
|
240
241
|
def _get_detail_url(self, instance):
|
|
241
|
-
return f"{BASE}relationship-field/{instance.pk}/"
|
|
242
|
+
return f"{BASE}/relationship-field/{instance.pk}/"
|
|
242
243
|
|
|
243
244
|
@classmethod
|
|
244
245
|
def setUpTestData(cls):
|
|
@@ -340,10 +341,10 @@ class IPFabricSourceTest(APIViewTestCases.APIViewTestCase):
|
|
|
340
341
|
graphql_base_name = "ipfabric_source"
|
|
341
342
|
|
|
342
343
|
def _get_list_url(self):
|
|
343
|
-
return f"{BASE}source/"
|
|
344
|
+
return f"{BASE}/source/"
|
|
344
345
|
|
|
345
346
|
def _get_detail_url(self, instance):
|
|
346
|
-
return f"{BASE}source/{instance.pk}/"
|
|
347
|
+
return f"{BASE}/source/{instance.pk}/"
|
|
347
348
|
|
|
348
349
|
@classmethod
|
|
349
350
|
def setUpTestData(cls):
|
|
@@ -387,6 +388,66 @@ class IPFabricSourceTest(APIViewTestCases.APIViewTestCase):
|
|
|
387
388
|
},
|
|
388
389
|
]
|
|
389
390
|
|
|
391
|
+
def test_sync_action_success(self):
|
|
392
|
+
"""Test successful sync action with proper permissions and ready source."""
|
|
393
|
+
self.add_permissions(
|
|
394
|
+
"ipfabric_netbox.add_ipfabricsource",
|
|
395
|
+
"ipfabric_netbox.sync_ipfabricsource",
|
|
396
|
+
)
|
|
397
|
+
# Get the first source from setUpTestData
|
|
398
|
+
source = IPFabricSource.objects.first()
|
|
399
|
+
# Set status to make ready_for_sync return True
|
|
400
|
+
source.status = DataSourceStatusChoices.COMPLETED
|
|
401
|
+
source.save()
|
|
402
|
+
|
|
403
|
+
with self.settings(CELERY_TASK_ALWAYS_EAGER=True):
|
|
404
|
+
# Create a mock job object to simulate enqueue_sync_job response
|
|
405
|
+
from unittest.mock import Mock, patch
|
|
406
|
+
|
|
407
|
+
mock_job = Mock()
|
|
408
|
+
mock_job.id = "test-job-123"
|
|
409
|
+
mock_job.status = "queued"
|
|
410
|
+
|
|
411
|
+
with patch.object(source, "enqueue_sync_job", return_value=mock_job):
|
|
412
|
+
url = f"{BASE}/source/{source.pk}/sync/"
|
|
413
|
+
response = self.client.post(url, **self.header)
|
|
414
|
+
|
|
415
|
+
self.assertHttpStatus(response, status.HTTP_201_CREATED)
|
|
416
|
+
self.assertIn("id", response.data)
|
|
417
|
+
|
|
418
|
+
def test_sync_action_permission_denied(self):
|
|
419
|
+
"""Test sync action without proper permissions."""
|
|
420
|
+
# Note: Not adding sync_ipfabricsource permission
|
|
421
|
+
self.add_permissions(
|
|
422
|
+
"ipfabric_netbox.add_ipfabricsource",
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
source = IPFabricSource.objects.first()
|
|
426
|
+
url = f"{BASE}/source/{source.pk}/sync/"
|
|
427
|
+
response = self.client.post(url, **self.header)
|
|
428
|
+
|
|
429
|
+
self.assertHttpStatus(response, status.HTTP_403_FORBIDDEN)
|
|
430
|
+
|
|
431
|
+
def test_sync_action_not_ready(self):
|
|
432
|
+
"""Test sync action when source is not ready for sync."""
|
|
433
|
+
self.add_permissions(
|
|
434
|
+
"ipfabric_netbox.add_ipfabricsource",
|
|
435
|
+
"ipfabric_netbox.sync_ipfabricsource",
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
source = IPFabricSource.objects.first()
|
|
439
|
+
# Set status to make ready_for_sync return False
|
|
440
|
+
source.status = DataSourceStatusChoices.SYNCING
|
|
441
|
+
source.save()
|
|
442
|
+
|
|
443
|
+
url = f"{BASE}/source/{source.pk}/sync/"
|
|
444
|
+
response = self.client.post(url, **self.header)
|
|
445
|
+
|
|
446
|
+
self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
|
|
447
|
+
self.assertIn(
|
|
448
|
+
f"Source '{source.name}' is not ready to be synced.", str(response.content)
|
|
449
|
+
)
|
|
450
|
+
|
|
390
451
|
|
|
391
452
|
class IPFabricSnapshotTest(
|
|
392
453
|
APIViewTestCases.GetObjectViewTestCase,
|
|
@@ -407,10 +468,10 @@ class IPFabricSnapshotTest(
|
|
|
407
468
|
]
|
|
408
469
|
|
|
409
470
|
def _get_list_url(self):
|
|
410
|
-
return f"{BASE}snapshot/"
|
|
471
|
+
return f"{BASE}/snapshot/"
|
|
411
472
|
|
|
412
473
|
def _get_detail_url(self, instance):
|
|
413
|
-
return f"{BASE}snapshot/{instance.pk}/"
|
|
474
|
+
return f"{BASE}/snapshot/{instance.pk}/"
|
|
414
475
|
|
|
415
476
|
@classmethod
|
|
416
477
|
def setUpTestData(cls):
|
|
@@ -468,7 +529,7 @@ class IPFabricSnapshotTest(
|
|
|
468
529
|
def test_sites_action_lists_all_and_filters(self):
|
|
469
530
|
self.add_permissions("ipfabric_netbox.view_ipfabricsnapshot")
|
|
470
531
|
# list all
|
|
471
|
-
url = f"{BASE}snapshot/{self.snapshots[0].pk}/sites/"
|
|
532
|
+
url = f"{BASE}/snapshot/{self.snapshots[0].pk}/sites/"
|
|
472
533
|
resp = self.client.get(url, **self.header)
|
|
473
534
|
self.assertHttpStatus(resp, status.HTTP_200_OK)
|
|
474
535
|
body = resp.json()
|
|
@@ -477,7 +538,7 @@ class IPFabricSnapshotTest(
|
|
|
477
538
|
labels = [i["name"] for i in body["results"]]
|
|
478
539
|
self.assertEqual(labels, self.snapshots[0].data["sites"])
|
|
479
540
|
# filter
|
|
480
|
-
url = f"{BASE}snapshot/{self.snapshots[0].pk}/sites/?q=site"
|
|
541
|
+
url = f"{BASE}/snapshot/{self.snapshots[0].pk}/sites/?q=site"
|
|
481
542
|
resp = self.client.get(url, **self.header)
|
|
482
543
|
self.assertHttpStatus(resp, status.HTTP_200_OK)
|
|
483
544
|
body = resp.json()
|
|
@@ -496,7 +557,7 @@ class IPFabricSnapshotTest(
|
|
|
496
557
|
IPFabricData.objects.filter(snapshot_data=self.snapshots[0]).count(), 0
|
|
497
558
|
)
|
|
498
559
|
# PATCH raw
|
|
499
|
-
url = f"{BASE}snapshot/{self.snapshots[0].pk}/raw/"
|
|
560
|
+
url = f"{BASE}/snapshot/{self.snapshots[0].pk}/raw/"
|
|
500
561
|
payload = {
|
|
501
562
|
"data": [
|
|
502
563
|
{"data": {"example": 1}, "type": "device"},
|
|
@@ -550,10 +611,10 @@ class IPFabricSyncTest(APIViewTestCases.APIViewTestCase):
|
|
|
550
611
|
}
|
|
551
612
|
|
|
552
613
|
def _get_list_url(self):
|
|
553
|
-
return f"{BASE}sync/"
|
|
614
|
+
return f"{BASE}/sync/"
|
|
554
615
|
|
|
555
616
|
def _get_detail_url(self, instance):
|
|
556
|
-
return f"{BASE}sync/{instance.pk}/"
|
|
617
|
+
return f"{BASE}/sync/{instance.pk}/"
|
|
557
618
|
|
|
558
619
|
@classmethod
|
|
559
620
|
def setUpTestData(cls):
|
|
@@ -637,6 +698,72 @@ class IPFabricSyncTest(APIViewTestCases.APIViewTestCase):
|
|
|
637
698
|
cls.create_data[1]["parameters"] = {"ipaddress": True, "prefix": True}
|
|
638
699
|
cls.create_data[2]["parameters"] = {"device": True, "interface": True}
|
|
639
700
|
|
|
701
|
+
def test_sync_action_success(self):
|
|
702
|
+
"""Test successful sync action with proper permissions and ready sync."""
|
|
703
|
+
self.add_permissions(
|
|
704
|
+
"ipfabric_netbox.add_ipfabricsync",
|
|
705
|
+
"ipfabric_netbox.sync_ipfabricsync",
|
|
706
|
+
)
|
|
707
|
+
# Get the first sync from setUpTestData
|
|
708
|
+
sync = IPFabricSync.objects.first()
|
|
709
|
+
# Set status and ensure snapshot has data to make ready_for_sync return True
|
|
710
|
+
sync.status = DataSourceStatusChoices.COMPLETED
|
|
711
|
+
sync.save()
|
|
712
|
+
|
|
713
|
+
# Ensure the snapshot has data
|
|
714
|
+
sync.snapshot_data.source.type = (
|
|
715
|
+
"local" # For local type, ready_for_sync checks are simpler
|
|
716
|
+
)
|
|
717
|
+
sync.snapshot_data.source.save()
|
|
718
|
+
|
|
719
|
+
with self.settings(CELERY_TASK_ALWAYS_EAGER=True):
|
|
720
|
+
# Create a mock job object to simulate enqueue_sync_job response
|
|
721
|
+
from unittest.mock import Mock, patch
|
|
722
|
+
|
|
723
|
+
mock_job = Mock()
|
|
724
|
+
mock_job.id = "test-sync-job-456"
|
|
725
|
+
mock_job.status = "queued"
|
|
726
|
+
|
|
727
|
+
with patch.object(sync, "enqueue_sync_job", return_value=mock_job):
|
|
728
|
+
url = f"{BASE}/sync/{sync.pk}/sync/"
|
|
729
|
+
response = self.client.post(url, **self.header)
|
|
730
|
+
|
|
731
|
+
self.assertHttpStatus(response, status.HTTP_201_CREATED)
|
|
732
|
+
self.assertIn("id", response.data)
|
|
733
|
+
|
|
734
|
+
def test_sync_action_permission_denied(self):
|
|
735
|
+
"""Test sync action without proper permissions."""
|
|
736
|
+
# Note: Not adding sync_ipfabricsource permission
|
|
737
|
+
self.add_permissions(
|
|
738
|
+
"ipfabric_netbox.add_ipfabricsync",
|
|
739
|
+
)
|
|
740
|
+
|
|
741
|
+
sync = IPFabricSync.objects.first()
|
|
742
|
+
url = f"{BASE}/sync/{sync.pk}/sync/"
|
|
743
|
+
response = self.client.post(url, **self.header)
|
|
744
|
+
|
|
745
|
+
self.assertHttpStatus(response, status.HTTP_403_FORBIDDEN)
|
|
746
|
+
|
|
747
|
+
def test_sync_action_not_ready(self):
|
|
748
|
+
"""Test sync action when sync is not ready for sync."""
|
|
749
|
+
self.add_permissions(
|
|
750
|
+
"ipfabric_netbox.add_ipfabricsync",
|
|
751
|
+
"ipfabric_netbox.sync_ipfabricsync",
|
|
752
|
+
)
|
|
753
|
+
|
|
754
|
+
sync = IPFabricSync.objects.first()
|
|
755
|
+
# Set status to make ready_for_sync return False
|
|
756
|
+
sync.status = DataSourceStatusChoices.SYNCING
|
|
757
|
+
sync.save()
|
|
758
|
+
|
|
759
|
+
url = f"{BASE}/sync/{sync.pk}/sync/"
|
|
760
|
+
response = self.client.post(url, **self.header)
|
|
761
|
+
|
|
762
|
+
self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
|
|
763
|
+
self.assertIn(
|
|
764
|
+
f"Sync '{sync.name}' is not ready to be synced.", str(response.content)
|
|
765
|
+
)
|
|
766
|
+
|
|
640
767
|
|
|
641
768
|
class IPFabricIngestionTest(
|
|
642
769
|
APIViewTestCases.GetObjectViewTestCase,
|
|
@@ -653,10 +780,10 @@ class IPFabricIngestionTest(
|
|
|
653
780
|
]
|
|
654
781
|
|
|
655
782
|
def _get_list_url(self):
|
|
656
|
-
return f"{BASE}ingestion/"
|
|
783
|
+
return f"{BASE}/ingestion/"
|
|
657
784
|
|
|
658
785
|
def _get_detail_url(self, instance):
|
|
659
|
-
return f"{BASE}ingestion/{instance.pk}/"
|
|
786
|
+
return f"{BASE}/ingestion/{instance.pk}/"
|
|
660
787
|
|
|
661
788
|
@classmethod
|
|
662
789
|
def setUpTestData(cls):
|
|
@@ -754,10 +881,10 @@ class IPFabricIngestionIssueTest(
|
|
|
754
881
|
]
|
|
755
882
|
|
|
756
883
|
def _get_list_url(self):
|
|
757
|
-
return f"{BASE}ingestion-issues/"
|
|
884
|
+
return f"{BASE}/ingestion-issues/"
|
|
758
885
|
|
|
759
886
|
def _get_detail_url(self, instance):
|
|
760
|
-
return f"{BASE}ingestion-issues/{instance.pk}/"
|
|
887
|
+
return f"{BASE}/ingestion-issues/{instance.pk}/"
|
|
761
888
|
|
|
762
889
|
@classmethod
|
|
763
890
|
def setUpTestData(cls):
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import random
|
|
2
|
+
from datetime import timedelta
|
|
2
3
|
from unittest.mock import patch
|
|
3
4
|
from uuid import uuid4
|
|
4
5
|
|
|
@@ -611,7 +612,7 @@ class IPFabricSyncTestCase(
|
|
|
611
612
|
# ViewTestCases.BulkImportObjectsViewTestCase,
|
|
612
613
|
):
|
|
613
614
|
model = IPFabricSync
|
|
614
|
-
user_permissions = ("ipfabric_netbox.
|
|
615
|
+
user_permissions = ("ipfabric_netbox.sync_ipfabricsync",)
|
|
615
616
|
|
|
616
617
|
@classmethod
|
|
617
618
|
def setUpTestData(cls):
|
|
@@ -656,6 +657,8 @@ class IPFabricSyncTestCase(
|
|
|
656
657
|
status=DataSourceStatusChoices.NEW,
|
|
657
658
|
parameters=get_parameters(),
|
|
658
659
|
last_synced=timezone.now(),
|
|
660
|
+
scheduled=timezone.now() + timedelta(hours=6),
|
|
661
|
+
interval=123456,
|
|
659
662
|
),
|
|
660
663
|
IPFabricSync(
|
|
661
664
|
name="Sync Job 2",
|
ipfabric_netbox/views.py
CHANGED
|
@@ -782,7 +782,15 @@ class IPFabricSyncView(generic.ObjectView):
|
|
|
782
782
|
|
|
783
783
|
last_ingestion = instance.ipfabricingestion_set.last()
|
|
784
784
|
|
|
785
|
-
|
|
785
|
+
scheduled_job = None
|
|
786
|
+
if instance.scheduled:
|
|
787
|
+
scheduled_job = instance.jobs.filter(scheduled=instance.scheduled).last()
|
|
788
|
+
|
|
789
|
+
return {
|
|
790
|
+
"format": format,
|
|
791
|
+
"last_ingestion": last_ingestion,
|
|
792
|
+
"scheduled_job": scheduled_job,
|
|
793
|
+
}
|
|
786
794
|
|
|
787
795
|
|
|
788
796
|
@register_model_view(IPFabricSync, "sync")
|
|
@@ -790,7 +798,7 @@ class IPFabricStartSyncView(BaseObjectView):
|
|
|
790
798
|
queryset = IPFabricSync.objects.all()
|
|
791
799
|
|
|
792
800
|
def get_required_permission(self):
|
|
793
|
-
return "ipfabric_netbox.
|
|
801
|
+
return "ipfabric_netbox.sync_ipfabricsync"
|
|
794
802
|
|
|
795
803
|
def get(self, request, pk):
|
|
796
804
|
ipfabric = get_object_or_404(self.queryset, pk=pk)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
ipfabric_netbox/__init__.py,sha256
|
|
1
|
+
ipfabric_netbox/__init__.py,sha256=p6HeR0kRnjcO4U5wSyEk81T3wJrEExUzfnzzvurzjl4,674
|
|
2
2
|
ipfabric_netbox/api/__init__.py,sha256=XRclTGWVR0ZhAAwgYul5Wm_loug5_hUjEumbLQEwKYM,47
|
|
3
|
-
ipfabric_netbox/api/serializers.py,sha256=
|
|
3
|
+
ipfabric_netbox/api/serializers.py,sha256=lr_PWG0tqAxaKtkIIm8Gx2B-tn9yENQIfKY9cvu8Cco,6581
|
|
4
4
|
ipfabric_netbox/api/urls.py,sha256=1fXXVTxNY5E64Nfz6b7zXD9bZI3FcefuxAWKMe0w_QU,1240
|
|
5
|
-
ipfabric_netbox/api/views.py,sha256=
|
|
5
|
+
ipfabric_netbox/api/views.py,sha256=qOBTIzPtOBY75tTjirsTBbiRXrQQid478Tp15-WKbmQ,6859
|
|
6
6
|
ipfabric_netbox/choices.py,sha256=gXRV_c04OnQl_kJTsAH1k88u8PWbiVXvcYwufZPKMdM,5035
|
|
7
7
|
ipfabric_netbox/data/transform_map.json,sha256=4PsucgMHcLW3SPoKEptQCd0gA5tCF4hjrR4bGQFCWy8,21744
|
|
8
8
|
ipfabric_netbox/exceptions.py,sha256=DT4dpbakvqoROtBR_F0LzvQCMNWpGhufFcUbZTx0OLY,2655
|
|
9
9
|
ipfabric_netbox/filtersets.py,sha256=vaWlxf8DTwduv_aQ35kJxwyzmM1XvE781GjUj2z4QGQ,7845
|
|
10
|
-
ipfabric_netbox/forms.py,sha256=
|
|
10
|
+
ipfabric_netbox/forms.py,sha256=WI1OypOG4Zqry6jiH991ZBVBMnfwzUDhNJAZCUQNaUg,49932
|
|
11
11
|
ipfabric_netbox/graphql/__init__.py,sha256=-a5w_VY7pc-RVt8MvThkTzeAqCC3xCan4Ue6iMefmjI,754
|
|
12
12
|
ipfabric_netbox/graphql/enums.py,sha256=QFhwiwUKJekxQfsOGk_-70_WnkzrKEP_zIBMrin0S0Q,1343
|
|
13
13
|
ipfabric_netbox/graphql/filters.py,sha256=B8xy9r9a18vWfV6a6tHXAN1FUcoxI6MOrbsdNmzusNI,12991
|
|
@@ -35,7 +35,7 @@ ipfabric_netbox/migrations/0017_ipfabricsync_update_custom_fields.py,sha256=IVbA
|
|
|
35
35
|
ipfabric_netbox/migrations/0018_remove_type_field.py,sha256=ffxW6IS3BLCbvM5M9DbDb_x6spMmRxnV1iq8IuXxMGw,385
|
|
36
36
|
ipfabric_netbox/migrations/0019_alter_ipfabrictransformmap_options_and_more.py,sha256=ieDVedt9KpJBicAiC3kdZXzHeos12N0L9EdRXKmIVgY,501
|
|
37
37
|
ipfabric_netbox/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
-
ipfabric_netbox/models.py,sha256=
|
|
38
|
+
ipfabric_netbox/models.py,sha256=P2oLmXDgDswrPDMtvQcuy_inl53-RBcghoV7Uu2I3q4,37095
|
|
39
39
|
ipfabric_netbox/navigation.py,sha256=2dEJ_wKHb52Tl0FOV1TH3JbxRe8YZ56ewrTsBFGKpCg,2210
|
|
40
40
|
ipfabric_netbox/signals.py,sha256=cGa5PVD2i24pGXiVNfbu6ruIDqPVdwKQHTSWe9Ura84,1838
|
|
41
41
|
ipfabric_netbox/tables.py,sha256=BpPmL6-KClX16Ol_PIADyOx9NtDR9oauXH5iO4GeSoI,9008
|
|
@@ -54,7 +54,7 @@ ipfabric_netbox/templates/ipfabric_netbox/ipfabric_table.html,sha256=TsF34lK2CyD
|
|
|
54
54
|
ipfabric_netbox/templates/ipfabric_netbox/ipfabricingestion.html,sha256=fm_X2FLnoTS6s6AL3WmU6p3puDojROSkPG0jA4EBQeM,4435
|
|
55
55
|
ipfabric_netbox/templates/ipfabric_netbox/ipfabricsnapshot.html,sha256=hj8ORs_4mM_xTjmw3McHN-da5seC8nbbkzobn0f1TSc,3482
|
|
56
56
|
ipfabric_netbox/templates/ipfabric_netbox/ipfabricsource.html,sha256=DQOA2TA7f1nI5YpxXthS1VzjIU1kMZus37l6bYSCauE,3869
|
|
57
|
-
ipfabric_netbox/templates/ipfabric_netbox/ipfabricsync.html,sha256=
|
|
57
|
+
ipfabric_netbox/templates/ipfabric_netbox/ipfabricsync.html,sha256=dt8HYuHCzIN4otLS9QK3e1aES14isFI-1jyp8jrENXU,4616
|
|
58
58
|
ipfabric_netbox/templates/ipfabric_netbox/ipfabrictransformmap.html,sha256=qFo_Ku5oksx5co4HVtVq0xAVFI6CLWs-iBrwYzGsEGA,1460
|
|
59
59
|
ipfabric_netbox/templates/ipfabric_netbox/ipfabrictransformmap_list.html,sha256=p8zqn0-B6mawSUM3zQrus6dsKUM5SRBTO0X94pLboX8,452
|
|
60
60
|
ipfabric_netbox/templates/ipfabric_netbox/ipfabrictransformmap_restore.html,sha256=TV7gAZWtSd-c7mzOen_nv7Z8MZr2Vw8vkHP4zW9au4w,2580
|
|
@@ -71,17 +71,17 @@ ipfabric_netbox/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
|
|
|
71
71
|
ipfabric_netbox/templatetags/ipfabric_netbox_helpers.py,sha256=STw4pAd2qG7hgf-O6UNTwsO5VqEa_gxf5wLv50BWL4Q,417
|
|
72
72
|
ipfabric_netbox/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
73
73
|
ipfabric_netbox/tests/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
74
|
-
ipfabric_netbox/tests/api/test_api.py,sha256
|
|
74
|
+
ipfabric_netbox/tests/api/test_api.py,sha256=9o9k4kfnT3Zz9Hg0qnI0_XASt6pi5BCZQ3RhBjsb_iE,35215
|
|
75
75
|
ipfabric_netbox/tests/test_forms.py,sha256=C8giV6E3PbMB9_864C12ebvfQ3Vlvdn39VIQQSP6GV8,61566
|
|
76
76
|
ipfabric_netbox/tests/test_models.py,sha256=FFrIT5xxv_yvujKpxGjRJPNPBDF2Pqi8zbY0vxuJeQs,16043
|
|
77
|
-
ipfabric_netbox/tests/test_views.py,sha256=
|
|
77
|
+
ipfabric_netbox/tests/test_views.py,sha256=6R4Ri8Y54Eg-9u5nmrWLNOp0794Y-JugcBAJGk22GEc,87771
|
|
78
78
|
ipfabric_netbox/urls.py,sha256=qF2BzZEDnPRd3opFaRfiMdaarYKFfv69iMaAbU2rsBU,2702
|
|
79
79
|
ipfabric_netbox/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
80
80
|
ipfabric_netbox/utilities/ipfutils.py,sha256=wFmL5oriuF-is1ZlrIcLmoeYUY5ih-CA9weRQrx5AiA,31885
|
|
81
81
|
ipfabric_netbox/utilities/logging.py,sha256=GYknjocMN6LQ2873_az3y0RKm29TCXaWviUIIneH-x0,3445
|
|
82
82
|
ipfabric_netbox/utilities/nbutils.py,sha256=kFBEiJOGvr_49hJWCS2duXojx2-A9kVk0Xp_vj0ohfs,2641
|
|
83
83
|
ipfabric_netbox/utilities/transform_map.py,sha256=GpM_7Mm6FE0qV2qbyj4YfDn0l-JkeeEHQOZkNVSSHk4,2391
|
|
84
|
-
ipfabric_netbox/views.py,sha256=
|
|
85
|
-
ipfabric_netbox-4.3.
|
|
86
|
-
ipfabric_netbox-4.3.
|
|
87
|
-
ipfabric_netbox-4.3.
|
|
84
|
+
ipfabric_netbox/views.py,sha256=n3tTDs1xOAY5xtcMmhM6VrnpEr-2IzqeliFNgthQf1U,44746
|
|
85
|
+
ipfabric_netbox-4.3.0b7.dist-info/METADATA,sha256=zK1X88R7MzrYCMgp9AGN-V0DWgjSJUJwuuS-NcEYtKo,4754
|
|
86
|
+
ipfabric_netbox-4.3.0b7.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
87
|
+
ipfabric_netbox-4.3.0b7.dist-info/RECORD,,
|
|
File without changes
|