ipfabric_netbox 4.2.2b2__py3-none-any.whl → 4.2.2b4__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/forms.py +84 -21
- ipfabric_netbox/migrations/0001_initial_squashed_0013_switch_to_branching_plugin.py +37 -17
- ipfabric_netbox/migrations/0019_alter_ipfabrictransformmap_options_and_more.py +19 -0
- ipfabric_netbox/models.py +30 -6
- ipfabric_netbox/template_content.py +9 -6
- ipfabric_netbox/templates/ipfabric_netbox/inc/site_topology_button.html +0 -2
- ipfabric_netbox/templates/ipfabric_netbox/ipfabric_table.html +1 -1
- ipfabric_netbox/templates/ipfabric_netbox/ipfabricsync.html +2 -1
- ipfabric_netbox/templates/ipfabric_netbox/ipfabrictransformmap_restore.html +1 -1
- ipfabric_netbox/tests/api/test_api.py +1 -1
- ipfabric_netbox/tests/test_forms.py +111 -18
- ipfabric_netbox/tests/test_views.py +2166 -0
- ipfabric_netbox/urls.py +15 -14
- ipfabric_netbox/utilities/transform_map.py +12 -10
- ipfabric_netbox/views.py +153 -139
- {ipfabric_netbox-4.2.2b2.dist-info → ipfabric_netbox-4.2.2b4.dist-info}/METADATA +5 -3
- {ipfabric_netbox-4.2.2b2.dist-info → ipfabric_netbox-4.2.2b4.dist-info}/RECORD +19 -17
- {ipfabric_netbox-4.2.2b2.dist-info → ipfabric_netbox-4.2.2b4.dist-info}/WHEEL +1 -1
ipfabric_netbox/urls.py
CHANGED
|
@@ -15,18 +15,13 @@ urlpatterns = (
|
|
|
15
15
|
views.IPFabricSourceBulkDeleteView.as_view(),
|
|
16
16
|
name="ipfabricsource_bulk_delete",
|
|
17
17
|
),
|
|
18
|
-
path(
|
|
19
|
-
"source/<int:pk>/delete/",
|
|
20
|
-
views.IPFabricSourceDeleteView.as_view(),
|
|
21
|
-
name="ipfabricsource_delete",
|
|
22
|
-
),
|
|
23
18
|
path(
|
|
24
19
|
"source/<int:pk>/", include(get_model_urls("ipfabric_netbox", "ipfabricsource"))
|
|
25
20
|
),
|
|
26
21
|
path(
|
|
27
|
-
"source
|
|
28
|
-
views.
|
|
29
|
-
name="
|
|
22
|
+
"source/<int:pk>/delete/",
|
|
23
|
+
views.IPFabricSourceDeleteView.as_view(),
|
|
24
|
+
name="ipfabricsource_delete",
|
|
30
25
|
),
|
|
31
26
|
# Snapshot
|
|
32
27
|
path(
|
|
@@ -48,20 +43,26 @@ urlpatterns = (
|
|
|
48
43
|
views.IPFabricSnapshotDeleteView.as_view(),
|
|
49
44
|
name="ipfabricsnapshot_delete",
|
|
50
45
|
),
|
|
51
|
-
|
|
52
|
-
"data/<int:pk>/delete",
|
|
53
|
-
views.IPFabricSnapshotDataDeleteView.as_view(),
|
|
54
|
-
name="ipfabricdata_delete",
|
|
55
|
-
),
|
|
56
|
-
path("data/<int:pk>/", include(get_model_urls("ipfabric_netbox", "ipfabricdata"))),
|
|
46
|
+
# Snapshot Data
|
|
57
47
|
path(
|
|
58
48
|
"data/delete",
|
|
59
49
|
views.IPFabricSnapshotDataBulkDeleteView.as_view(),
|
|
60
50
|
name="ipfabricdata_bulk_delete",
|
|
61
51
|
),
|
|
52
|
+
path("data/<int:pk>/", include(get_model_urls("ipfabric_netbox", "ipfabricdata"))),
|
|
53
|
+
path(
|
|
54
|
+
"data/<int:pk>/delete",
|
|
55
|
+
views.IPFabricSnapshotDataDeleteView.as_view(),
|
|
56
|
+
name="ipfabricdata_delete",
|
|
57
|
+
),
|
|
62
58
|
# Sync
|
|
63
59
|
path("sync/", views.IPFabricSyncListView.as_view(), name="ipfabricsync_list"),
|
|
64
60
|
path("sync/add/", views.IPFabricSyncEditView.as_view(), name="ipfabricsync_add"),
|
|
61
|
+
path(
|
|
62
|
+
"sync/delete/",
|
|
63
|
+
views.IPFabricSyncBulkDeleteView.as_view(),
|
|
64
|
+
name="ipfabricsync_bulk_delete",
|
|
65
|
+
),
|
|
65
66
|
path("sync/<int:pk>/", include(get_model_urls("ipfabric_netbox", "ipfabricsync"))),
|
|
66
67
|
path(
|
|
67
68
|
"sync/<int:pk>/delete/",
|
|
@@ -8,10 +8,10 @@ from django.apps import apps as django_apps
|
|
|
8
8
|
# see https://docs.djangoproject.com/en/5.1/topics/migrations/#historical-models
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
def build_fields(data, apps):
|
|
11
|
+
def build_fields(data, apps, db_alias):
|
|
12
12
|
ContentType = apps.get_model("contenttypes", "ContentType")
|
|
13
13
|
if "target_model" in data:
|
|
14
|
-
ct = ContentType.objects.get_for_model(
|
|
14
|
+
ct = ContentType.objects.db_manager(db_alias).get_for_model(
|
|
15
15
|
apps.get_model(
|
|
16
16
|
data["target_model"]["app_label"],
|
|
17
17
|
data["target_model"]["model"],
|
|
@@ -19,7 +19,7 @@ def build_fields(data, apps):
|
|
|
19
19
|
)
|
|
20
20
|
data["target_model"] = ct
|
|
21
21
|
elif "source_model" in data:
|
|
22
|
-
ct = ContentType.objects.get_for_model(
|
|
22
|
+
ct = ContentType.objects.db_manager(db_alias).get_for_model(
|
|
23
23
|
apps.get_model(
|
|
24
24
|
data["source_model"]["app_label"],
|
|
25
25
|
data["source_model"]["model"],
|
|
@@ -29,7 +29,7 @@ def build_fields(data, apps):
|
|
|
29
29
|
return data
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
def build_transform_maps(data, apps: django_apps = None):
|
|
32
|
+
def build_transform_maps(data, apps: django_apps = None, db_alias: str = "default"):
|
|
33
33
|
apps = apps or django_apps
|
|
34
34
|
IPFabricTransformMap = apps.get_model("ipfabric_netbox", "IPFabricTransformMap")
|
|
35
35
|
IPFabricTransformField = apps.get_model("ipfabric_netbox", "IPFabricTransformField")
|
|
@@ -37,14 +37,16 @@ def build_transform_maps(data, apps: django_apps = None):
|
|
|
37
37
|
"ipfabric_netbox", "IPFabricRelationshipField"
|
|
38
38
|
)
|
|
39
39
|
for tm in data:
|
|
40
|
-
field_data = build_fields(tm["data"], apps)
|
|
41
|
-
tm_obj = IPFabricTransformMap.objects.create(**field_data)
|
|
40
|
+
field_data = build_fields(tm["data"], apps, db_alias)
|
|
41
|
+
tm_obj = IPFabricTransformMap.objects.using(db_alias).create(**field_data)
|
|
42
42
|
for fm in tm["field_maps"]:
|
|
43
|
-
field_data = build_fields(fm, apps)
|
|
44
|
-
IPFabricTransformField.objects.create(
|
|
43
|
+
field_data = build_fields(fm, apps, db_alias)
|
|
44
|
+
IPFabricTransformField.objects.using(db_alias).create(
|
|
45
|
+
transform_map=tm_obj, **field_data
|
|
46
|
+
)
|
|
45
47
|
for rm in tm["relationship_maps"]:
|
|
46
|
-
relationship_data = build_fields(rm, apps)
|
|
47
|
-
IPFabricRelationshipField.objects.create(
|
|
48
|
+
relationship_data = build_fields(rm, apps, db_alias)
|
|
49
|
+
IPFabricRelationshipField.objects.using(db_alias).create(
|
|
48
50
|
transform_map=tm_obj, **relationship_data
|
|
49
51
|
)
|
|
50
52
|
|
ipfabric_netbox/views.py
CHANGED
|
@@ -170,7 +170,7 @@ class IPFabricTransformMapRestoreView(generic.ObjectListView):
|
|
|
170
170
|
table = IPFabricTransformMapTable
|
|
171
171
|
|
|
172
172
|
def get_required_permission(self):
|
|
173
|
-
return "ipfabric_netbox.
|
|
173
|
+
return "ipfabric_netbox.restore_ipfabrictransformmap"
|
|
174
174
|
|
|
175
175
|
def get(self, request):
|
|
176
176
|
if request.htmx:
|
|
@@ -197,6 +197,7 @@ class IPFabricTransformMapRestoreView(generic.ObjectListView):
|
|
|
197
197
|
"dependent_objects": dependent_objects,
|
|
198
198
|
},
|
|
199
199
|
)
|
|
200
|
+
return redirect(reverse("plugins:ipfabric_netbox:ipfabrictransformmap_list"))
|
|
200
201
|
|
|
201
202
|
def post(self, request):
|
|
202
203
|
IPFabricTransformMap.objects.filter(group__isnull=True).delete()
|
|
@@ -218,7 +219,7 @@ class IPFabricTransformMapCloneView(BaseObjectView):
|
|
|
218
219
|
form = IPFabricTransformMapCloneForm
|
|
219
220
|
|
|
220
221
|
def get_required_permission(self):
|
|
221
|
-
return "ipfabric_netbox.
|
|
222
|
+
return "ipfabric_netbox.clone_ipfabrictransformmap"
|
|
222
223
|
|
|
223
224
|
def get(self, request, pk):
|
|
224
225
|
obj = get_object_or_404(self.queryset, pk=pk)
|
|
@@ -252,36 +253,40 @@ class IPFabricTransformMapCloneView(BaseObjectView):
|
|
|
252
253
|
relationships = IPFabricRelationshipField.objects.filter(
|
|
253
254
|
transform_map=obj
|
|
254
255
|
)
|
|
255
|
-
# Clone the transform map
|
|
256
|
-
new_map =
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
256
|
+
# Clone the transform map - create a proper copy using Django model copying
|
|
257
|
+
new_map = IPFabricTransformMap(
|
|
258
|
+
name=form.cleaned_data["name"],
|
|
259
|
+
source_model=obj.source_model,
|
|
260
|
+
target_model=obj.target_model,
|
|
261
|
+
group=form.cleaned_data["group"],
|
|
262
|
+
)
|
|
261
263
|
new_map.full_clean()
|
|
262
264
|
new_map.save()
|
|
263
|
-
new_map.refresh_from_db()
|
|
264
265
|
|
|
265
266
|
# Clone related transform fields
|
|
266
267
|
if form.cleaned_data["clone_fields"]:
|
|
267
268
|
for field in fields:
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
269
|
+
IPFabricTransformField.objects.create(
|
|
270
|
+
transform_map=new_map,
|
|
271
|
+
source_field=field.source_field,
|
|
272
|
+
target_field=field.target_field,
|
|
273
|
+
coalesce=field.coalesce,
|
|
274
|
+
template=field.template,
|
|
275
|
+
)
|
|
273
276
|
|
|
274
277
|
# Clone related relationship fields
|
|
275
278
|
if form.cleaned_data["clone_relationships"]:
|
|
276
279
|
for rel in relationships:
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
280
|
+
IPFabricRelationshipField.objects.create(
|
|
281
|
+
transform_map=new_map,
|
|
282
|
+
source_model=rel.source_model,
|
|
283
|
+
target_field=rel.target_field,
|
|
284
|
+
coalesce=rel.coalesce,
|
|
285
|
+
template=rel.template,
|
|
286
|
+
)
|
|
282
287
|
|
|
283
288
|
return_url = reverse(
|
|
284
|
-
"plugins:ipfabric_netbox:ipfabrictransformmap", args=[
|
|
289
|
+
"plugins:ipfabric_netbox:ipfabrictransformmap", args=[new_map.pk]
|
|
285
290
|
)
|
|
286
291
|
if request.htmx:
|
|
287
292
|
response = HttpResponse()
|
|
@@ -289,7 +294,7 @@ class IPFabricTransformMapCloneView(BaseObjectView):
|
|
|
289
294
|
return response
|
|
290
295
|
return redirect(return_url)
|
|
291
296
|
except ValidationError as err:
|
|
292
|
-
if not err.error_dict:
|
|
297
|
+
if not hasattr(err, "error_dict") or not err.error_dict:
|
|
293
298
|
form.add_error(None, err)
|
|
294
299
|
else:
|
|
295
300
|
# This serves to show errors in the form directly
|
|
@@ -299,6 +304,8 @@ class IPFabricTransformMapCloneView(BaseObjectView):
|
|
|
299
304
|
else:
|
|
300
305
|
form.add_error(None, error)
|
|
301
306
|
if request.htmx:
|
|
307
|
+
viewname = get_viewname(self.queryset.model, action="clone")
|
|
308
|
+
form_url = reverse(viewname, kwargs={"pk": obj.pk})
|
|
302
309
|
response = render(
|
|
303
310
|
request,
|
|
304
311
|
"ipfabric_netbox/inc/clone_form.html",
|
|
@@ -306,6 +313,7 @@ class IPFabricTransformMapCloneView(BaseObjectView):
|
|
|
306
313
|
"form": form,
|
|
307
314
|
"object": obj,
|
|
308
315
|
"pk": pk,
|
|
316
|
+
"form_url": form_url,
|
|
309
317
|
},
|
|
310
318
|
)
|
|
311
319
|
response["X-Debug-HTMX-Partial"] = "true"
|
|
@@ -316,6 +324,7 @@ class IPFabricTransformMapCloneView(BaseObjectView):
|
|
|
316
324
|
{
|
|
317
325
|
"form": form,
|
|
318
326
|
"object": obj,
|
|
327
|
+
"pk": pk,
|
|
319
328
|
},
|
|
320
329
|
)
|
|
321
330
|
|
|
@@ -429,15 +438,13 @@ class IPFabricSnapshotDataBulkDeleteView(generic.BulkDeleteView):
|
|
|
429
438
|
path="json",
|
|
430
439
|
kwargs={},
|
|
431
440
|
)
|
|
432
|
-
class IPFabricSnapshotDataJSONView(
|
|
441
|
+
class IPFabricSnapshotDataJSONView(generic.ObjectView):
|
|
442
|
+
queryset = IPFabricData.objects.all()
|
|
433
443
|
template_name = "ipfabric_netbox/inc/json.html"
|
|
434
444
|
|
|
435
445
|
def get(self, request, **kwargs):
|
|
436
|
-
|
|
437
|
-
# change_id = kwargs.get("change_pk", None)
|
|
438
|
-
|
|
446
|
+
data = get_object_or_404(IPFabricData, pk=kwargs.get("pk"))
|
|
439
447
|
if request.htmx:
|
|
440
|
-
data = get_object_or_404(IPFabricData, pk=kwargs.get("pk"))
|
|
441
448
|
return render(
|
|
442
449
|
request,
|
|
443
450
|
self.template_name,
|
|
@@ -445,19 +452,13 @@ class IPFabricSnapshotDataJSONView(LoginRequiredMixin, View):
|
|
|
445
452
|
"object": data,
|
|
446
453
|
},
|
|
447
454
|
)
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
# "postchange_data": postchange_data,
|
|
456
|
-
# "diff_added": diff_added,
|
|
457
|
-
# "diff_removed": diff_removed,
|
|
458
|
-
# "size": "lg",
|
|
459
|
-
# },
|
|
460
|
-
# )
|
|
455
|
+
return render(
|
|
456
|
+
request,
|
|
457
|
+
self.template_name,
|
|
458
|
+
{
|
|
459
|
+
"object": data,
|
|
460
|
+
},
|
|
461
|
+
)
|
|
461
462
|
|
|
462
463
|
|
|
463
464
|
# Source
|
|
@@ -498,7 +499,7 @@ class IPFabricSourceSyncView(BaseObjectView):
|
|
|
498
499
|
queryset = IPFabricSource.objects.all()
|
|
499
500
|
|
|
500
501
|
def get_required_permission(self):
|
|
501
|
-
return "ipfabric_netbox.
|
|
502
|
+
return "ipfabric_netbox.sync_ipfabricsource"
|
|
502
503
|
|
|
503
504
|
def get(self, request, pk):
|
|
504
505
|
ipfabricsource = get_object_or_404(self.queryset, pk=pk)
|
|
@@ -543,13 +544,13 @@ class IPFabricSyncEditView(generic.ObjectEditView):
|
|
|
543
544
|
@register_model_view(IPFabricSync)
|
|
544
545
|
class IPFabricSyncView(generic.ObjectView):
|
|
545
546
|
queryset = IPFabricSync.objects.all()
|
|
546
|
-
actions = ("edit",)
|
|
547
547
|
|
|
548
548
|
def get(self, request, **kwargs):
|
|
549
|
-
|
|
550
|
-
last_ingestion = instance.ipfabricingestion_set.last()
|
|
551
|
-
|
|
549
|
+
# Handle HTMX requests separately
|
|
552
550
|
if request.htmx:
|
|
551
|
+
instance = self.get_object(**kwargs)
|
|
552
|
+
last_ingestion = instance.ipfabricingestion_set.last()
|
|
553
|
+
|
|
553
554
|
response = render(
|
|
554
555
|
request,
|
|
555
556
|
"ipfabric_netbox/partials/sync_last_ingestion.html",
|
|
@@ -564,15 +565,8 @@ class IPFabricSyncView(generic.ObjectView):
|
|
|
564
565
|
response["HX-Refresh"] = "true"
|
|
565
566
|
return response
|
|
566
567
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
self.get_template_name(),
|
|
570
|
-
{
|
|
571
|
-
"object": instance,
|
|
572
|
-
"tab": self.tab,
|
|
573
|
-
**self.get_extra_context(request, instance),
|
|
574
|
-
},
|
|
575
|
-
)
|
|
568
|
+
# For regular requests, use the parent method which includes actions
|
|
569
|
+
return super().get(request, **kwargs)
|
|
576
570
|
|
|
577
571
|
def get_extra_context(self, request, instance):
|
|
578
572
|
if request.GET.get("format") in ["json", "yaml"]:
|
|
@@ -594,7 +588,7 @@ class IPFabricStartSyncView(BaseObjectView):
|
|
|
594
588
|
queryset = IPFabricSync.objects.all()
|
|
595
589
|
|
|
596
590
|
def get_required_permission(self):
|
|
597
|
-
return "ipfabric_netbox.
|
|
591
|
+
return "ipfabric_netbox.start_ipfabricsync"
|
|
598
592
|
|
|
599
593
|
def get(self, request, pk):
|
|
600
594
|
ipfabric = get_object_or_404(self.queryset, pk=pk)
|
|
@@ -617,7 +611,7 @@ class IPFabricSyncDeleteView(generic.ObjectDeleteView):
|
|
|
617
611
|
class IPFabricSyncBulkDeleteView(generic.BulkDeleteView):
|
|
618
612
|
queryset = IPFabricSync.objects.all()
|
|
619
613
|
filterset = IPFabricSnapshotFilterSet
|
|
620
|
-
table =
|
|
614
|
+
table = IPFabricSyncTable
|
|
621
615
|
|
|
622
616
|
|
|
623
617
|
@register_model_view(IPFabricSync, "transformmaps")
|
|
@@ -640,6 +634,26 @@ class IPFabricTransformMapTabView(generic.ObjectChildrenView):
|
|
|
640
634
|
)
|
|
641
635
|
|
|
642
636
|
|
|
637
|
+
@register_model_view(IPFabricSync, "ingestion")
|
|
638
|
+
class IPFabricIngestionTabView(generic.ObjectChildrenView):
|
|
639
|
+
queryset = IPFabricSync.objects.all()
|
|
640
|
+
child_model = IPFabricIngestion
|
|
641
|
+
table = IPFabricIngestionTable
|
|
642
|
+
filterset = IPFabricIngestionFilterSet
|
|
643
|
+
tab = ViewTab(
|
|
644
|
+
label="Ingestions",
|
|
645
|
+
badge=lambda obj: IPFabricIngestion.objects.filter(sync=obj).count(),
|
|
646
|
+
permission="ipfabric_netbox.view_ipfabricingestion",
|
|
647
|
+
)
|
|
648
|
+
|
|
649
|
+
def get_children(self, request, parent):
|
|
650
|
+
return self.child_model.objects.filter(sync=parent).annotate(
|
|
651
|
+
description=models.F("branch__description"),
|
|
652
|
+
user=models.F("sync__user__username"),
|
|
653
|
+
staged_changes=models.Count(models.F("branch__changediff")),
|
|
654
|
+
)
|
|
655
|
+
|
|
656
|
+
|
|
643
657
|
# Ingestion
|
|
644
658
|
class IPFabricIngestionListView(generic.ObjectListView):
|
|
645
659
|
queryset = IPFabricIngestion.objects.annotate(
|
|
@@ -691,13 +705,12 @@ class IPFabricIngestionLogView(LoginRequiredMixin, View):
|
|
|
691
705
|
|
|
692
706
|
def get(self, request, **kwargs):
|
|
693
707
|
ingestion_id = kwargs.get("pk")
|
|
708
|
+
ingestion = annotate_statistics(IPFabricIngestion.objects).get(pk=ingestion_id)
|
|
709
|
+
data = ingestion.get_statistics()
|
|
710
|
+
data["object"] = ingestion
|
|
711
|
+
data["job"] = ingestion.jobs.first()
|
|
712
|
+
|
|
694
713
|
if request.htmx:
|
|
695
|
-
ingestion = annotate_statistics(IPFabricIngestion.objects).get(
|
|
696
|
-
pk=ingestion_id
|
|
697
|
-
)
|
|
698
|
-
data = ingestion.get_statistics()
|
|
699
|
-
data["object"] = ingestion
|
|
700
|
-
data["job"] = ingestion.jobs.first()
|
|
701
714
|
response = render(
|
|
702
715
|
request,
|
|
703
716
|
self.template_name,
|
|
@@ -706,7 +719,7 @@ class IPFabricIngestionLogView(LoginRequiredMixin, View):
|
|
|
706
719
|
if ingestion.job.completed:
|
|
707
720
|
response["HX-Refresh"] = "true"
|
|
708
721
|
return response
|
|
709
|
-
return render(request, self.template_name)
|
|
722
|
+
return render(request, self.template_name, data)
|
|
710
723
|
|
|
711
724
|
|
|
712
725
|
@register_model_view(IPFabricIngestion)
|
|
@@ -729,7 +742,7 @@ class IPFabricIngestionMergeView(BaseObjectView):
|
|
|
729
742
|
form = IPFabricIngestionMergeForm
|
|
730
743
|
|
|
731
744
|
def get_required_permission(self):
|
|
732
|
-
return "ipfabric_netbox.
|
|
745
|
+
return "ipfabric_netbox.merge_ipfabricingestion"
|
|
733
746
|
|
|
734
747
|
def get(self, request, pk):
|
|
735
748
|
obj = get_object_or_404(self.queryset, pk=pk)
|
|
@@ -763,7 +776,16 @@ class IPFabricIngestionMergeView(BaseObjectView):
|
|
|
763
776
|
)
|
|
764
777
|
messages.success(request, f"Queued job #{job.pk} to sync {ingestion}")
|
|
765
778
|
return redirect(ingestion.get_absolute_url())
|
|
766
|
-
|
|
779
|
+
|
|
780
|
+
# Handle invalid form - add form errors to messages and redirect back
|
|
781
|
+
for field, errors in form.errors.items():
|
|
782
|
+
for error in errors:
|
|
783
|
+
messages.error(request, f"{field}: {error}")
|
|
784
|
+
if form.non_field_errors():
|
|
785
|
+
for error in form.non_field_errors():
|
|
786
|
+
messages.error(request, error)
|
|
787
|
+
|
|
788
|
+
return redirect(ingestion.get_absolute_url())
|
|
767
789
|
|
|
768
790
|
|
|
769
791
|
@register_model_view(
|
|
@@ -775,8 +797,10 @@ class IPFabricIngestionMergeView(BaseObjectView):
|
|
|
775
797
|
class IPFabricIngestionChangesDiffView(LoginRequiredMixin, View):
|
|
776
798
|
template_name = "ipfabric_netbox/inc/diff.html"
|
|
777
799
|
|
|
778
|
-
def get(self, request,
|
|
779
|
-
|
|
800
|
+
def get(self, request, **kwargs):
|
|
801
|
+
change_id = kwargs.get("change_pk", None)
|
|
802
|
+
|
|
803
|
+
if not request.htmx or not change_id:
|
|
780
804
|
return render(
|
|
781
805
|
request,
|
|
782
806
|
self.template_name,
|
|
@@ -790,13 +814,6 @@ class IPFabricIngestionChangesDiffView(LoginRequiredMixin, View):
|
|
|
790
814
|
},
|
|
791
815
|
)
|
|
792
816
|
|
|
793
|
-
change_id = kwargs.get("change_pk", None)
|
|
794
|
-
|
|
795
|
-
if not request.htmx:
|
|
796
|
-
return _return_empty()
|
|
797
|
-
if not change_id:
|
|
798
|
-
return _return_empty()
|
|
799
|
-
|
|
800
817
|
change = ChangeDiff.objects.get(pk=change_id)
|
|
801
818
|
if change.original and change.modified:
|
|
802
819
|
diff_added = shallow_compare_dict(
|
|
@@ -866,34 +883,15 @@ class IPFabricIngestionDeleteView(generic.ObjectDeleteView):
|
|
|
866
883
|
queryset = IPFabricIngestion.objects.all()
|
|
867
884
|
|
|
868
885
|
|
|
869
|
-
@register_model_view(IPFabricSync, "ingestion")
|
|
870
|
-
class IPFabricIngestionTabView(generic.ObjectChildrenView):
|
|
871
|
-
queryset = IPFabricSync.objects.all()
|
|
872
|
-
child_model = IPFabricIngestion
|
|
873
|
-
table = IPFabricIngestionTable
|
|
874
|
-
filterset = IPFabricIngestionFilterSet
|
|
875
|
-
template_name = "generic/object_children.html"
|
|
876
|
-
tab = ViewTab(
|
|
877
|
-
label="Ingestions",
|
|
878
|
-
badge=lambda obj: IPFabricIngestion.objects.filter(sync=obj).count(),
|
|
879
|
-
permission="ipfabric_netbox.view_ipfabricingestion",
|
|
880
|
-
)
|
|
881
|
-
|
|
882
|
-
def get_children(self, request, parent):
|
|
883
|
-
return self.child_model.objects.filter(sync=parent).annotate(
|
|
884
|
-
description=models.F("branch__description"),
|
|
885
|
-
user=models.F("sync__user__username"),
|
|
886
|
-
staged_changes=models.Count(models.F("branch__changediff")),
|
|
887
|
-
)
|
|
888
|
-
|
|
889
|
-
|
|
890
886
|
@register_model_view(Device, "ipfabric")
|
|
891
|
-
class IPFabricTable(
|
|
887
|
+
class IPFabricTable(generic.ObjectView):
|
|
892
888
|
template_name = "ipfabric_netbox/ipfabric_table.html"
|
|
893
889
|
tab = ViewTab("IP Fabric", permission="ipfabric_netbox.view_devicetable")
|
|
890
|
+
queryset = Device.objects.all()
|
|
894
891
|
|
|
895
|
-
def
|
|
896
|
-
|
|
892
|
+
def get_extra_context(self, request, instance):
|
|
893
|
+
"""Process form and prepare table data for the template."""
|
|
894
|
+
device = instance
|
|
897
895
|
form = (
|
|
898
896
|
IPFabricTableForm(request.GET)
|
|
899
897
|
if "table" in request.GET
|
|
@@ -901,43 +899,48 @@ class IPFabricTable(View):
|
|
|
901
899
|
)
|
|
902
900
|
restrict_form_fields(form, request.user)
|
|
903
901
|
data = None
|
|
902
|
+
source = None
|
|
904
903
|
|
|
905
904
|
if form.is_valid():
|
|
906
|
-
|
|
905
|
+
table_name = form.cleaned_data["table"]
|
|
907
906
|
test = {
|
|
908
907
|
"True": True,
|
|
909
908
|
"False": False,
|
|
910
909
|
}
|
|
911
910
|
cache_enable = test.get(form.cleaned_data["cache_enable"])
|
|
912
|
-
|
|
911
|
+
source = form.cleaned_data.get("source")
|
|
913
912
|
|
|
914
913
|
if not form.cleaned_data["snapshot_data"]:
|
|
915
914
|
snapshot_id = "$last"
|
|
916
|
-
source =
|
|
917
|
-
|
|
915
|
+
source = (
|
|
916
|
+
source
|
|
917
|
+
or IPFabricSource.objects.filter(
|
|
918
|
+
pk=device.custom_field_data.get("ipfabric_source")
|
|
919
|
+
).first()
|
|
920
|
+
or IPFabricSource.get_for_site(device.site).first()
|
|
918
921
|
)
|
|
919
|
-
|
|
920
922
|
else:
|
|
921
923
|
snapshot_id = form.cleaned_data["snapshot_data"].snapshot_id
|
|
922
|
-
source = form.cleaned_data["snapshot_data"].source
|
|
923
|
-
|
|
924
|
-
source
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
f"ipfabric_{
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
924
|
+
source = source or form.cleaned_data["snapshot_data"].source
|
|
925
|
+
|
|
926
|
+
if source is not None:
|
|
927
|
+
source.parameters["snapshot_id"] = snapshot_id
|
|
928
|
+
source.parameters["base_url"] = source.url
|
|
929
|
+
|
|
930
|
+
cache_key = f"ipfabric_{table_name}_{device.serial}_{source.parameters['snapshot_id']}"
|
|
931
|
+
if cache_enable:
|
|
932
|
+
data = cache.get(cache_key)
|
|
933
|
+
|
|
934
|
+
if not data:
|
|
935
|
+
try:
|
|
936
|
+
ipf = IPFabric(parameters=source.parameters)
|
|
937
|
+
raw_data, columns = ipf.get_table_data(
|
|
938
|
+
table=table_name, device=device
|
|
939
|
+
)
|
|
940
|
+
data = {"data": raw_data, "columns": columns}
|
|
941
|
+
cache.set(cache_key, data, 60 * 60 * 24)
|
|
942
|
+
except Exception as e:
|
|
943
|
+
messages.error(request, e)
|
|
941
944
|
|
|
942
945
|
if not data:
|
|
943
946
|
data = {"data": [], "columns": []}
|
|
@@ -952,31 +955,34 @@ class IPFabricTable(View):
|
|
|
952
955
|
},
|
|
953
956
|
).configure(table)
|
|
954
957
|
|
|
958
|
+
if not source:
|
|
959
|
+
if source_id := device.custom_field_data.get("ipfabric_source"):
|
|
960
|
+
source = IPFabricSource.objects.filter(pk=source_id).first()
|
|
961
|
+
else:
|
|
962
|
+
source = IPFabricSource.get_for_site(device.site).first()
|
|
963
|
+
|
|
964
|
+
return {
|
|
965
|
+
"source": source,
|
|
966
|
+
"form": form,
|
|
967
|
+
"table": table,
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
def get(self, request, **kwargs):
|
|
971
|
+
"""Handle GET requests, with special handling for HTMX table updates."""
|
|
972
|
+
# For HTMX requests, we only need to return the table HTML
|
|
955
973
|
if request.htmx:
|
|
974
|
+
device = get_object_or_404(Device, pk=kwargs.get("pk"))
|
|
975
|
+
context = self.get_extra_context(request, device)
|
|
956
976
|
return render(
|
|
957
977
|
request,
|
|
958
978
|
"htmx/table.html",
|
|
959
979
|
{
|
|
960
|
-
"table": table,
|
|
980
|
+
"table": context["table"],
|
|
961
981
|
},
|
|
962
982
|
)
|
|
963
983
|
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
if source_id := device.custom_field_data["ipfabric_source"]:
|
|
967
|
-
source = IPFabricSource.objects.get(pk=source_id)
|
|
968
|
-
|
|
969
|
-
return render(
|
|
970
|
-
request,
|
|
971
|
-
self.template_name,
|
|
972
|
-
{
|
|
973
|
-
"object": device,
|
|
974
|
-
"source": source,
|
|
975
|
-
"tab": self.tab,
|
|
976
|
-
"form": form,
|
|
977
|
-
"table": table,
|
|
978
|
-
},
|
|
979
|
-
)
|
|
984
|
+
# For regular requests, use the parent's get() method which will call get_extra_context()
|
|
985
|
+
return super().get(request, **kwargs)
|
|
980
986
|
|
|
981
987
|
|
|
982
988
|
@register_model_view(
|
|
@@ -1047,4 +1053,12 @@ class IPFabricSourceTopology(LoginRequiredMixin, View):
|
|
|
1047
1053
|
"error": error,
|
|
1048
1054
|
},
|
|
1049
1055
|
)
|
|
1050
|
-
return
|
|
1056
|
+
return render(
|
|
1057
|
+
request,
|
|
1058
|
+
self.template_name,
|
|
1059
|
+
{
|
|
1060
|
+
"site": site,
|
|
1061
|
+
"size": "xl",
|
|
1062
|
+
"time": timezone.now(),
|
|
1063
|
+
},
|
|
1064
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ipfabric_netbox
|
|
3
|
-
Version: 4.2.
|
|
3
|
+
Version: 4.2.2b4
|
|
4
4
|
Summary: NetBox plugin to sync IP Fabric data into NetBox
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: netbox,ipfabric,plugin,sync
|
|
@@ -24,7 +24,7 @@ Requires-Dist: ipfabric (>=6.6.4) ; extra != "ipfabric_6_10" and extra != "ipfab
|
|
|
24
24
|
Requires-Dist: ipfabric (>=7.0.0,<7.1.0) ; extra != "ipfabric_6_10" and extra == "ipfabric_7_0" and extra != "ipfabric_7_2" and extra != "ipfabric_7_3"
|
|
25
25
|
Requires-Dist: ipfabric (>=7.2.0,<7.3.0) ; extra != "ipfabric_6_10" and extra != "ipfabric_7_0" and extra == "ipfabric_7_2" and extra != "ipfabric_7_3"
|
|
26
26
|
Requires-Dist: ipfabric (>=7.3.0,<7.4.0) ; extra != "ipfabric_6_10" and extra != "ipfabric_7_0" and extra != "ipfabric_7_2" and extra == "ipfabric_7_3"
|
|
27
|
-
Requires-Dist: netboxlabs-netbox-branching (
|
|
27
|
+
Requires-Dist: netboxlabs-netbox-branching (==0.7.0)
|
|
28
28
|
Requires-Dist: netutils
|
|
29
29
|
Project-URL: Bug Tracker, https://gitlab.com/ip-fabric/integrations/ipfabric-netbox-sync/-/issues
|
|
30
30
|
Project-URL: Homepage, https://gitlab.com/ip-fabric/integrations/ipfabric-netbox-sync
|
|
@@ -65,7 +65,9 @@ These are the required NetBox versions for corresponding plugin version. Any oth
|
|
|
65
65
|
|
|
66
66
|
| Netbox Version | Plugin Version |
|
|
67
67
|
|----------------|----------------|
|
|
68
|
-
| 4.
|
|
68
|
+
| 4.4.0 and up | 4.3.0 and up |
|
|
69
|
+
| 4.3.0 - 4.3.7 | 4.2.2 |
|
|
70
|
+
| 4.3.0 - 4.3.6 | 4.0.0 - 4.2.1 |
|
|
69
71
|
| 4.2.4 - 4.2.9 | 3.2.2 - 3.2.4 |
|
|
70
72
|
| 4.2.0 - 4.2.3 | 3.2.0 |
|
|
71
73
|
| 4.1.5 - 4.1.11 | 3.1.1 - 3.1.3 |
|