ipfabric_netbox 3.2.4__py3-none-any.whl → 3.2.4b3__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/nested_serializers.py +0 -20
- ipfabric_netbox/api/serializers.py +7 -13
- ipfabric_netbox/api/urls.py +2 -2
- ipfabric_netbox/api/views.py +5 -5
- ipfabric_netbox/data/transform_map.json +21 -35
- ipfabric_netbox/filtersets.py +15 -13
- ipfabric_netbox/forms.py +14 -42
- ipfabric_netbox/jobs.py +12 -7
- ipfabric_netbox/migrations/0001_initial.py +1 -1
- ipfabric_netbox/migrations/0001_initial_squashed_0013_switch_to_branching_plugin.py +503 -0
- ipfabric_netbox/migrations/0007_prepare_custom_fields.py +3 -3
- ipfabric_netbox/migrations/0011_update_part_number_DCIM_inventory_item_template.py +57 -0
- ipfabric_netbox/migrations/0012_remove_status_field.py +18 -0
- ipfabric_netbox/migrations/0013_switch_to_branching_plugin.py +270 -0
- ipfabric_netbox/models.py +120 -91
- ipfabric_netbox/navigation.py +1 -1
- ipfabric_netbox/signals.py +9 -2
- ipfabric_netbox/tables.py +40 -46
- ipfabric_netbox/templates/ipfabric_netbox/{ipfabricbranch.html → ipfabricingestion.html} +14 -9
- ipfabric_netbox/templates/ipfabric_netbox/ipfabricsource.html +3 -3
- ipfabric_netbox/templates/ipfabric_netbox/ipfabricsync.html +3 -3
- ipfabric_netbox/templates/ipfabric_netbox/ipfabricsync_list.html +71 -0
- ipfabric_netbox/templates/ipfabric_netbox/ipfabrictransformmap.html +0 -4
- ipfabric_netbox/templates/ipfabric_netbox/partials/ingestion_all.html +10 -0
- ipfabric_netbox/templates/ipfabric_netbox/partials/{branch_progress.html → ingestion_progress.html} +1 -1
- ipfabric_netbox/templates/ipfabric_netbox/partials/sync_last_ingestion.html +1 -0
- ipfabric_netbox/urls.py +13 -3
- ipfabric_netbox/utilities/ipfutils.py +52 -19
- ipfabric_netbox/views.py +162 -155
- {ipfabric_netbox-3.2.4.dist-info → ipfabric_netbox-3.2.4b3.dist-info}/METADATA +12 -4
- {ipfabric_netbox-3.2.4.dist-info → ipfabric_netbox-3.2.4b3.dist-info}/RECORD +34 -31
- {ipfabric_netbox-3.2.4.dist-info → ipfabric_netbox-3.2.4b3.dist-info}/WHEEL +1 -1
- ipfabric_netbox/templates/ipfabric_netbox/inc/sync_delete.html +0 -19
- ipfabric_netbox/templates/ipfabric_netbox/partials/branch_all.html +0 -10
- ipfabric_netbox/templates/ipfabric_netbox/partials/sync_last_branch.html +0 -1
- ipfabric_netbox/templates/ipfabric_netbox/sync_list.html +0 -126
- /ipfabric_netbox/templates/ipfabric_netbox/partials/{branch_status.html → ingestion_status.html} +0 -0
ipfabric_netbox/views.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
from core.choices import ObjectChangeActionChoices
|
|
1
2
|
from dcim.models import Device
|
|
2
3
|
from dcim.models import Site
|
|
3
4
|
from django.contrib import messages
|
|
4
5
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
5
6
|
from django.core.cache import cache
|
|
7
|
+
from django.core.exceptions import ValidationError
|
|
6
8
|
from django.db import models
|
|
7
9
|
from django.shortcuts import get_object_or_404
|
|
8
10
|
from django.shortcuts import redirect
|
|
@@ -11,28 +13,27 @@ from django.urls import reverse
|
|
|
11
13
|
from django.utils import timezone
|
|
12
14
|
from django.views.generic import View
|
|
13
15
|
from django_tables2 import RequestConfig
|
|
14
|
-
from extras.choices import ChangeActionChoices
|
|
15
16
|
from ipfabric.diagrams import Network
|
|
16
17
|
from ipfabric.diagrams import NetworkSettings
|
|
17
|
-
from netbox.staging import StagedChange
|
|
18
18
|
from netbox.views import generic
|
|
19
19
|
from netbox.views.generic.base import BaseObjectView
|
|
20
|
+
from netbox_branching.models import ChangeDiff
|
|
20
21
|
from utilities.data import shallow_compare_dict
|
|
21
22
|
from utilities.forms import ConfirmationForm
|
|
22
23
|
from utilities.paginator import EnhancedPaginator
|
|
23
24
|
from utilities.paginator import get_paginate_count
|
|
24
25
|
from utilities.query import count_related
|
|
25
|
-
from utilities.serialization import serialize_object
|
|
26
26
|
from utilities.views import get_viewname
|
|
27
27
|
from utilities.views import register_model_view
|
|
28
28
|
from utilities.views import ViewTab
|
|
29
29
|
|
|
30
|
-
from .filtersets import IPFabricBranchFilterSet
|
|
31
30
|
from .filtersets import IPFabricDataFilterSet
|
|
31
|
+
from .filtersets import IPFabricIngestionChangeFilterSet
|
|
32
|
+
from .filtersets import IPFabricIngestionFilterSet
|
|
32
33
|
from .filtersets import IPFabricSnapshotFilterSet
|
|
33
34
|
from .filtersets import IPFabricSourceFilterSet
|
|
34
|
-
from .
|
|
35
|
-
from .forms import
|
|
35
|
+
from .forms import IPFabricIngestionFilterForm
|
|
36
|
+
from .forms import IPFabricIngestionMergeForm
|
|
36
37
|
from .forms import IPFabricRelationshipFieldForm
|
|
37
38
|
from .forms import IPFabricSnapshotFilterForm
|
|
38
39
|
from .forms import IPFabricSourceFilterForm
|
|
@@ -41,23 +42,23 @@ from .forms import IPFabricSyncForm
|
|
|
41
42
|
from .forms import IPFabricTableForm
|
|
42
43
|
from .forms import IPFabricTransformFieldForm
|
|
43
44
|
from .forms import IPFabricTransformMapForm
|
|
44
|
-
from .models import IPFabricBranch
|
|
45
45
|
from .models import IPFabricData
|
|
46
|
+
from .models import IPFabricIngestion
|
|
46
47
|
from .models import IPFabricRelationshipField
|
|
47
48
|
from .models import IPFabricSnapshot
|
|
48
49
|
from .models import IPFabricSource
|
|
49
50
|
from .models import IPFabricSync
|
|
50
51
|
from .models import IPFabricTransformField
|
|
51
52
|
from .models import IPFabricTransformMap
|
|
52
|
-
from .tables import BranchTable
|
|
53
53
|
from .tables import DeviceIPFTable
|
|
54
54
|
from .tables import IPFabricDataTable
|
|
55
|
+
from .tables import IPFabricIngestionChangesTable
|
|
56
|
+
from .tables import IPFabricIngestionTable
|
|
55
57
|
from .tables import IPFabricRelationshipFieldTable
|
|
56
58
|
from .tables import IPFabricSnapshotTable
|
|
57
59
|
from .tables import IPFabricSourceTable
|
|
58
60
|
from .tables import IPFabricTransformFieldTable
|
|
59
61
|
from .tables import IPFabricTransformMapTable
|
|
60
|
-
from .tables import StagedChangesTable
|
|
61
62
|
from .utilities.ipfutils import IPFabric
|
|
62
63
|
from .utilities.transform_map import build_transform_maps
|
|
63
64
|
from .utilities.transform_map import get_transform_map
|
|
@@ -361,14 +362,12 @@ class IPFabricSourceBulkDeleteView(generic.BulkDeleteView):
|
|
|
361
362
|
|
|
362
363
|
|
|
363
364
|
# Sync
|
|
364
|
-
|
|
365
|
-
|
|
366
365
|
class IPFabricSyncListView(View):
|
|
367
366
|
def get(self, request):
|
|
368
367
|
syncs = IPFabricSync.objects.prefetch_related("snapshot_data")
|
|
369
368
|
return render(
|
|
370
369
|
request,
|
|
371
|
-
"ipfabric_netbox/
|
|
370
|
+
"ipfabric_netbox/ipfabricsync_list.html",
|
|
372
371
|
{"model": IPFabricSync, "syncs": syncs},
|
|
373
372
|
)
|
|
374
373
|
|
|
@@ -390,19 +389,19 @@ class IPFabricSyncView(generic.ObjectView):
|
|
|
390
389
|
|
|
391
390
|
def get(self, request, **kwargs):
|
|
392
391
|
instance = self.get_object(**kwargs)
|
|
393
|
-
|
|
392
|
+
last_ingestion = instance.ipfabricingestion_set.last()
|
|
394
393
|
|
|
395
394
|
if request.htmx:
|
|
396
395
|
response = render(
|
|
397
396
|
request,
|
|
398
|
-
"ipfabric_netbox/partials/
|
|
399
|
-
{"
|
|
397
|
+
"ipfabric_netbox/partials/sync_last_ingestion.html",
|
|
398
|
+
{"last_ingestion": last_ingestion},
|
|
400
399
|
)
|
|
401
400
|
|
|
402
401
|
if instance.status not in ["queued", "syncing"]:
|
|
403
402
|
messages.success(
|
|
404
403
|
request,
|
|
405
|
-
f"Ingestion ({instance.name}) {instance.status}.
|
|
404
|
+
f"Ingestion ({instance.name}) {instance.status}. Ingestion {last_ingestion.name} {last_ingestion.job.status}.",
|
|
406
405
|
)
|
|
407
406
|
response["HX-Refresh"] = "true"
|
|
408
407
|
return response
|
|
@@ -418,8 +417,6 @@ class IPFabricSyncView(generic.ObjectView):
|
|
|
418
417
|
)
|
|
419
418
|
|
|
420
419
|
def get_extra_context(self, request, instance):
|
|
421
|
-
last_branch = instance.ipfabricbranch_set.last()
|
|
422
|
-
|
|
423
420
|
if request.GET.get("format") in ["json", "yaml"]:
|
|
424
421
|
format = request.GET.get("format")
|
|
425
422
|
if request.user.is_authenticated:
|
|
@@ -429,51 +426,17 @@ class IPFabricSyncView(generic.ObjectView):
|
|
|
429
426
|
else:
|
|
430
427
|
format = "json"
|
|
431
428
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
return {"format": format, "last_branch": last_branch}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
@register_model_view(IPFabricSync, "delete")
|
|
438
|
-
class IPFabricSyncDeleteView(generic.ObjectDeleteView):
|
|
439
|
-
queryset = IPFabricSync.objects.all()
|
|
440
|
-
default_return_url = "plugins:ipfabric_netbox:ipfabricsync_list"
|
|
441
|
-
|
|
442
|
-
def get(self, request, pk):
|
|
443
|
-
obj = get_object_or_404(self.queryset, pk=pk)
|
|
444
|
-
|
|
445
|
-
if request.htmx:
|
|
446
|
-
viewname = get_viewname(self.queryset.model, action="delete")
|
|
447
|
-
form_url = reverse(viewname, kwargs={"pk": obj.pk})
|
|
448
|
-
form = ConfirmationForm(initial=request.GET)
|
|
449
|
-
return render(
|
|
450
|
-
request,
|
|
451
|
-
"ipfabric_netbox/inc/sync_delete.html",
|
|
452
|
-
{
|
|
453
|
-
"object": obj,
|
|
454
|
-
"object_type": self.queryset.model._meta.verbose_name,
|
|
455
|
-
"form": form,
|
|
456
|
-
"form_url": form_url,
|
|
457
|
-
**self.get_extra_context(request, obj),
|
|
458
|
-
},
|
|
459
|
-
)
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
class IPFabricSyncBulkDeleteView(generic.BulkDeleteView):
|
|
463
|
-
queryset = IPFabricSync.objects.all()
|
|
464
|
-
filterset = IPFabricSnapshotFilterSet
|
|
465
|
-
table = IPFabricSnapshotTable
|
|
466
|
-
|
|
429
|
+
last_ingestion = instance.ipfabricingestion_set.last()
|
|
467
430
|
|
|
468
|
-
|
|
431
|
+
return {"format": format, "last_ingestion": last_ingestion}
|
|
469
432
|
|
|
470
433
|
|
|
471
434
|
@register_model_view(IPFabricSync, "sync")
|
|
472
|
-
class
|
|
435
|
+
class IPFabricStartSyncView(BaseObjectView):
|
|
473
436
|
queryset = IPFabricSync.objects.all()
|
|
474
437
|
|
|
475
438
|
def get_required_permission(self):
|
|
476
|
-
return "ipfabric_netbox.
|
|
439
|
+
return "ipfabric_netbox.start_sync"
|
|
477
440
|
|
|
478
441
|
def get(self, request, pk):
|
|
479
442
|
ipfabric = get_object_or_404(self.queryset, pk=pk)
|
|
@@ -487,61 +450,84 @@ class IPFabricIngestSyncView(BaseObjectView):
|
|
|
487
450
|
return redirect(ipfabric.get_absolute_url())
|
|
488
451
|
|
|
489
452
|
|
|
490
|
-
|
|
453
|
+
@register_model_view(IPFabricSync, "delete")
|
|
454
|
+
class IPFabricSyncDeleteView(generic.ObjectDeleteView):
|
|
455
|
+
queryset = IPFabricSync.objects.all()
|
|
456
|
+
default_return_url = "plugins:ipfabric_netbox:ipfabricsync_list"
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
class IPFabricSyncBulkDeleteView(generic.BulkDeleteView):
|
|
460
|
+
queryset = IPFabricSync.objects.all()
|
|
461
|
+
filterset = IPFabricSnapshotFilterSet
|
|
462
|
+
table = IPFabricSnapshotTable
|
|
491
463
|
|
|
492
464
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
465
|
+
# Ingestion
|
|
466
|
+
class IPFabricIngestionListView(generic.ObjectListView):
|
|
467
|
+
queryset = IPFabricIngestion.objects.annotate(
|
|
468
|
+
description=models.F("branch__description"),
|
|
469
|
+
user=models.F("sync__user__username"),
|
|
470
|
+
staged_changes=models.Count(models.F("branch__changediff")),
|
|
471
|
+
)
|
|
472
|
+
filterset = IPFabricIngestionFilterSet
|
|
473
|
+
filterset_form = IPFabricIngestionFilterForm
|
|
474
|
+
table = IPFabricIngestionTable
|
|
498
475
|
|
|
499
476
|
|
|
500
477
|
@register_model_view(
|
|
501
|
-
|
|
478
|
+
IPFabricIngestion,
|
|
502
479
|
name="logs",
|
|
503
480
|
path="logs",
|
|
504
481
|
)
|
|
505
|
-
class
|
|
506
|
-
template_name = "ipfabric_netbox/partials/
|
|
482
|
+
class IPFabricIngestionLogView(LoginRequiredMixin, View):
|
|
483
|
+
template_name = "ipfabric_netbox/partials/ingestion_all.html"
|
|
507
484
|
|
|
508
485
|
def get(self, request, **kwargs):
|
|
509
|
-
|
|
486
|
+
ingestion_id = kwargs.get("pk")
|
|
510
487
|
if request.htmx:
|
|
511
|
-
|
|
512
|
-
data =
|
|
513
|
-
data["object"] =
|
|
514
|
-
data["job"] =
|
|
488
|
+
ingestion = IPFabricIngestion.objects.get(pk=ingestion_id)
|
|
489
|
+
data = ingestion.get_statistics()
|
|
490
|
+
data["object"] = ingestion
|
|
491
|
+
data["job"] = ingestion.jobs.first()
|
|
515
492
|
response = render(
|
|
516
493
|
request,
|
|
517
494
|
self.template_name,
|
|
518
495
|
data,
|
|
519
496
|
)
|
|
520
|
-
if
|
|
497
|
+
if ingestion.job.completed:
|
|
521
498
|
response["HX-Refresh"] = "true"
|
|
522
499
|
return response
|
|
523
500
|
else:
|
|
524
501
|
return response
|
|
525
502
|
|
|
526
503
|
|
|
527
|
-
@register_model_view(
|
|
528
|
-
class
|
|
529
|
-
queryset =
|
|
504
|
+
@register_model_view(IPFabricIngestion)
|
|
505
|
+
class IPFabricIngestionView(generic.ObjectView):
|
|
506
|
+
queryset = IPFabricIngestion.objects.annotate(
|
|
530
507
|
num_created=models.Count(
|
|
531
|
-
"
|
|
532
|
-
filter=models.Q(
|
|
533
|
-
|
|
508
|
+
"branch__changediff",
|
|
509
|
+
filter=models.Q(
|
|
510
|
+
branch__changediff__action=ObjectChangeActionChoices.ACTION_CREATE
|
|
511
|
+
)
|
|
512
|
+
& ~models.Q(branch__changediff__object_type__model="objectchange"),
|
|
534
513
|
),
|
|
535
514
|
num_updated=models.Count(
|
|
536
|
-
"
|
|
537
|
-
filter=models.Q(
|
|
538
|
-
|
|
515
|
+
"branch__changediff",
|
|
516
|
+
filter=models.Q(
|
|
517
|
+
branch__changediff__action=ObjectChangeActionChoices.ACTION_UPDATE
|
|
518
|
+
)
|
|
519
|
+
& ~models.Q(branch__changediff__object_type__model="objectchange"),
|
|
539
520
|
),
|
|
540
521
|
num_deleted=models.Count(
|
|
541
|
-
"
|
|
542
|
-
filter=models.Q(
|
|
543
|
-
|
|
522
|
+
"branch__changediff",
|
|
523
|
+
filter=models.Q(
|
|
524
|
+
branch__changediff__action=ObjectChangeActionChoices.ACTION_DELETE
|
|
525
|
+
)
|
|
526
|
+
& ~models.Q(branch__changediff__object_type__model="objectchange"),
|
|
544
527
|
),
|
|
528
|
+
description=models.F("branch__description"),
|
|
529
|
+
user=models.F("sync__user__username"),
|
|
530
|
+
staged_changes=models.Count(models.F("branch__changediff")),
|
|
545
531
|
)
|
|
546
532
|
|
|
547
533
|
def get_extra_context(self, request, instance):
|
|
@@ -549,13 +535,18 @@ class IPFabricBranchView(generic.ObjectView):
|
|
|
549
535
|
return data
|
|
550
536
|
|
|
551
537
|
|
|
552
|
-
@register_model_view(
|
|
553
|
-
class
|
|
554
|
-
queryset =
|
|
538
|
+
@register_model_view(IPFabricIngestion, "merge")
|
|
539
|
+
class IPFabricIngestionMergeView(BaseObjectView):
|
|
540
|
+
queryset = IPFabricIngestion.objects.annotate(
|
|
541
|
+
description=models.F("branch__description"),
|
|
542
|
+
user=models.F("sync__user__username"),
|
|
543
|
+
staged_changes=models.Count(models.F("branch__changediff")),
|
|
544
|
+
)
|
|
555
545
|
template_name = "ipfabric_netbox/inc/merge_form.html"
|
|
546
|
+
form = IPFabricIngestionMergeForm
|
|
556
547
|
|
|
557
548
|
def get_required_permission(self):
|
|
558
|
-
return "ipfabric_netbox.
|
|
549
|
+
return "ipfabric_netbox.merge_ingestion"
|
|
559
550
|
|
|
560
551
|
def get(self, request, pk):
|
|
561
552
|
obj = get_object_or_404(self.queryset, pk=pk)
|
|
@@ -563,7 +554,7 @@ class IPFabricBranchMergeView(BaseObjectView):
|
|
|
563
554
|
if request.htmx:
|
|
564
555
|
viewname = get_viewname(self.queryset.model, action="merge")
|
|
565
556
|
form_url = reverse(viewname, kwargs={"pk": obj.pk})
|
|
566
|
-
form =
|
|
557
|
+
form = self.form(initial=request.GET)
|
|
567
558
|
return render(
|
|
568
559
|
request,
|
|
569
560
|
"ipfabric_netbox/inc/merge_form.html",
|
|
@@ -579,103 +570,119 @@ class IPFabricBranchMergeView(BaseObjectView):
|
|
|
579
570
|
return redirect(obj.get_absolute_url())
|
|
580
571
|
|
|
581
572
|
def post(self, request, pk):
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
573
|
+
ingestion = get_object_or_404(self.queryset, pk=pk)
|
|
574
|
+
form = self.form(request.POST)
|
|
575
|
+
if form.is_valid():
|
|
576
|
+
job = ingestion.enqueue_merge_job(
|
|
577
|
+
user=request.user, remove_branch=form.cleaned_data["remove_branch"]
|
|
578
|
+
)
|
|
579
|
+
messages.success(request, f"Queued job #{job.pk} to sync {ingestion}")
|
|
580
|
+
return redirect(ingestion.get_absolute_url())
|
|
581
|
+
raise ValidationError("Form is not valid.")
|
|
587
582
|
|
|
588
583
|
|
|
589
584
|
@register_model_view(
|
|
590
|
-
|
|
585
|
+
IPFabricIngestion,
|
|
591
586
|
name="change_diff",
|
|
592
587
|
path="change/<int:change_pk>",
|
|
593
|
-
kwargs={"model":
|
|
588
|
+
kwargs={"model": IPFabricIngestion},
|
|
594
589
|
)
|
|
595
|
-
class
|
|
590
|
+
class IPFabricIngestionChangesDiffView(LoginRequiredMixin, View):
|
|
596
591
|
template_name = "ipfabric_netbox/inc/diff.html"
|
|
597
592
|
|
|
598
593
|
def get(self, request, model, **kwargs):
|
|
594
|
+
def _return_empty():
|
|
595
|
+
return render(
|
|
596
|
+
request,
|
|
597
|
+
self.template_name,
|
|
598
|
+
{
|
|
599
|
+
"change": None,
|
|
600
|
+
"prechange_data": None,
|
|
601
|
+
"postchange_data": None,
|
|
602
|
+
"diff_added": None,
|
|
603
|
+
"diff_removed": None,
|
|
604
|
+
"size": "lg",
|
|
605
|
+
},
|
|
606
|
+
)
|
|
607
|
+
|
|
599
608
|
change_id = kwargs.get("change_pk", None)
|
|
600
609
|
|
|
601
|
-
if request.htmx:
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
if prechange_data
|
|
622
|
-
else {}
|
|
623
|
-
)
|
|
624
|
-
else:
|
|
625
|
-
diff_added = None
|
|
626
|
-
diff_removed = None
|
|
610
|
+
if not request.htmx:
|
|
611
|
+
return _return_empty()
|
|
612
|
+
if not change_id:
|
|
613
|
+
return _return_empty()
|
|
614
|
+
|
|
615
|
+
change = ChangeDiff.objects.get(pk=change_id)
|
|
616
|
+
if change.original and change.modified:
|
|
617
|
+
diff_added = shallow_compare_dict(
|
|
618
|
+
change.original or dict(),
|
|
619
|
+
change.modified or dict(),
|
|
620
|
+
exclude=["last_updated"],
|
|
621
|
+
)
|
|
622
|
+
diff_removed = (
|
|
623
|
+
{x: change.original.get(x) for x in diff_added}
|
|
624
|
+
if change.modified
|
|
625
|
+
else {}
|
|
626
|
+
)
|
|
627
|
+
else:
|
|
628
|
+
diff_added = None
|
|
629
|
+
diff_removed = None
|
|
627
630
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
631
|
+
return render(
|
|
632
|
+
request,
|
|
633
|
+
self.template_name,
|
|
634
|
+
{
|
|
635
|
+
"change": change,
|
|
636
|
+
"prechange_data": change.original,
|
|
637
|
+
"postchange_data": change.modified,
|
|
638
|
+
"diff_added": diff_added,
|
|
639
|
+
"diff_removed": diff_removed,
|
|
640
|
+
"size": "lg",
|
|
641
|
+
},
|
|
642
|
+
)
|
|
640
643
|
|
|
641
644
|
|
|
642
|
-
@register_model_view(
|
|
643
|
-
class
|
|
644
|
-
queryset =
|
|
645
|
-
child_model =
|
|
646
|
-
table =
|
|
647
|
-
filterset =
|
|
645
|
+
@register_model_view(IPFabricIngestion, "change")
|
|
646
|
+
class IPFabricIngestionChangesView(generic.ObjectChildrenView):
|
|
647
|
+
queryset = IPFabricIngestion.objects.all()
|
|
648
|
+
child_model = ChangeDiff
|
|
649
|
+
table = IPFabricIngestionChangesTable
|
|
650
|
+
filterset = IPFabricIngestionChangeFilterSet
|
|
648
651
|
template_name = "generic/object_children.html"
|
|
649
652
|
tab = ViewTab(
|
|
650
653
|
label="Changes",
|
|
651
|
-
badge=lambda obj:
|
|
652
|
-
permission="ipfabric_netbox.
|
|
654
|
+
badge=lambda obj: ChangeDiff.objects.filter(branch=obj.branch).count(),
|
|
655
|
+
permission="ipfabric_netbox.view_ipfabricingestion",
|
|
653
656
|
)
|
|
654
657
|
|
|
655
658
|
def get_children(self, request, parent):
|
|
656
|
-
return self.child_model.objects.filter(branch=parent)
|
|
659
|
+
return self.child_model.objects.filter(branch=parent.branch)
|
|
657
660
|
|
|
658
661
|
|
|
659
|
-
@register_model_view(
|
|
660
|
-
class
|
|
661
|
-
queryset =
|
|
662
|
+
@register_model_view(IPFabricIngestion, "delete")
|
|
663
|
+
class IPFabricIngestionDeleteView(generic.ObjectDeleteView):
|
|
664
|
+
queryset = IPFabricIngestion.objects.all()
|
|
662
665
|
|
|
663
666
|
|
|
664
|
-
@register_model_view(IPFabricSync, "
|
|
665
|
-
class
|
|
667
|
+
@register_model_view(IPFabricSync, "ingestion")
|
|
668
|
+
class IPFabricIngestionTabView(generic.ObjectChildrenView):
|
|
666
669
|
queryset = IPFabricSync.objects.all()
|
|
667
|
-
child_model =
|
|
668
|
-
table =
|
|
669
|
-
filterset =
|
|
670
|
+
child_model = IPFabricIngestion
|
|
671
|
+
table = IPFabricIngestionTable
|
|
672
|
+
filterset = IPFabricIngestionFilterSet
|
|
670
673
|
template_name = "generic/object_children.html"
|
|
671
674
|
tab = ViewTab(
|
|
672
|
-
label="
|
|
673
|
-
badge=lambda obj:
|
|
674
|
-
permission="ipfabric_netbox.
|
|
675
|
+
label="Ingestions",
|
|
676
|
+
badge=lambda obj: IPFabricIngestion.objects.filter(sync=obj).count(),
|
|
677
|
+
permission="ipfabric_netbox.view_ipfabricingestion",
|
|
675
678
|
)
|
|
676
679
|
|
|
677
680
|
def get_children(self, request, parent):
|
|
678
|
-
return self.child_model.objects.filter(sync=parent)
|
|
681
|
+
return self.child_model.objects.filter(sync=parent).annotate(
|
|
682
|
+
description=models.F("branch__description"),
|
|
683
|
+
user=models.F("sync__user__username"),
|
|
684
|
+
staged_changes=models.Count(models.F("branch__changediff")),
|
|
685
|
+
)
|
|
679
686
|
|
|
680
687
|
|
|
681
688
|
@register_model_view(Device, "ipfabric")
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: ipfabric_netbox
|
|
3
|
-
Version: 3.2.
|
|
3
|
+
Version: 3.2.4b3
|
|
4
4
|
Summary: NetBox plugin to sync IP Fabric data into NetBox
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: netbox,ipfabric,plugin,sync
|
|
7
7
|
Author: Solution Architecture
|
|
8
8
|
Author-email: solution.architecture@ipfabric.io
|
|
9
9
|
Requires-Python: >=3.10,<4.0
|
|
10
|
-
Classifier: Programming Language :: Python :: 3
|
|
11
10
|
Classifier: License :: OSI Approved :: MIT License
|
|
12
11
|
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
17
|
Provides-Extra: ipfabric-6-10
|
|
14
18
|
Provides-Extra: ipfabric-6-6
|
|
15
19
|
Provides-Extra: ipfabric-6-7
|
|
@@ -23,8 +27,11 @@ Requires-Dist: ipfabric (>=6.7.0,<6.8.0) ; extra != "ipfabric_6_6" and extra ==
|
|
|
23
27
|
Requires-Dist: ipfabric (>=6.8.0,<6.9.0) ; extra != "ipfabric_6_6" and extra != "ipfabric_6_7" and extra == "ipfabric_6_8" and extra != "ipfabric_6_9" and extra != "ipfabric_6_10" and extra != "ipfabric_7_0"
|
|
24
28
|
Requires-Dist: ipfabric (>=6.9.0,<6.10.0) ; extra != "ipfabric_6_6" and extra != "ipfabric_6_7" and extra != "ipfabric_6_8" and extra == "ipfabric_6_9" and extra != "ipfabric_6_10" and extra != "ipfabric_7_0"
|
|
25
29
|
Requires-Dist: ipfabric (>=7.0.0,<7.1.0) ; extra != "ipfabric_6_6" and extra != "ipfabric_6_7" and extra != "ipfabric_6_8" and extra != "ipfabric_6_9" and extra != "ipfabric_6_10" and extra == "ipfabric_7_0"
|
|
30
|
+
Requires-Dist: netboxlabs-netbox-branching (>=0.5.5,<0.6.0)
|
|
26
31
|
Requires-Dist: netutils
|
|
27
32
|
Project-URL: Bug Tracker, https://gitlab.com/ip-fabric/integrations/ipfabric-netbox-sync/-/issues
|
|
33
|
+
Project-URL: Homepage, https://gitlab.com/ip-fabric/integrations/ipfabric-netbox-sync
|
|
34
|
+
Project-URL: Repository, https://gitlab.com/ip-fabric/integrations/ipfabric-netbox-sync
|
|
28
35
|
Description-Content-Type: text/markdown
|
|
29
36
|
|
|
30
37
|
# IP Fabric Netbox Plugin
|
|
@@ -49,7 +56,7 @@ or autoboss account may cause irreversible, detrimental changes to the product a
|
|
|
49
56
|
|
|
50
57
|
This plugin allows the integration and data synchronization between IP Fabric and NetBox.
|
|
51
58
|
|
|
52
|
-
The plugin uses IP Fabric collect network data utilizing the [IP Fabric Python SDK](https://gitlab.com/ip-fabric/integrations/python-ipfabric). This plugin relies on helpful features in NetBox like [
|
|
59
|
+
The plugin uses IP Fabric collect network data utilizing the [IP Fabric Python SDK](https://gitlab.com/ip-fabric/integrations/python-ipfabric). This plugin relies on helpful features in NetBox like [Branches](https://docs.netboxlabs.com/netbox-extensions/branching/) and [Background Tasks](https://netboxlabs.com/docs/netbox/en/stable/plugins/development/background-tasks/) to make the job of bringing in data to NetBox easier.
|
|
53
60
|
|
|
54
61
|
- Multiple IP Fabric Sources
|
|
55
62
|
- Transform Maps
|
|
@@ -61,7 +68,8 @@ These are the required NetBox versions for corresponding plugin version. Any oth
|
|
|
61
68
|
|
|
62
69
|
| Netbox Version | Plugin Version |
|
|
63
70
|
|----------------|----------------|
|
|
64
|
-
| 4.
|
|
71
|
+
| 4.3.0 and up | 4.0.0 and up |
|
|
72
|
+
| 4.2.4 - 4.2.9 | 3.2.2 - 3.2.4 |
|
|
65
73
|
| 4.2.0 - 4.2.3 | 3.2.0 |
|
|
66
74
|
| 4.1.5 - 4.1.11 | 3.1.1 - 3.1.3 |
|
|
67
75
|
| 4.1.0 - 4.1.4 | 3.1.0 |
|