ipfabric_netbox 3.2.3__py3-none-any.whl → 3.2.4b2__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.

Files changed (39) hide show
  1. ipfabric_netbox/__init__.py +1 -1
  2. ipfabric_netbox/api/nested_serializers.py +0 -20
  3. ipfabric_netbox/api/serializers.py +7 -13
  4. ipfabric_netbox/api/urls.py +2 -2
  5. ipfabric_netbox/api/views.py +5 -5
  6. ipfabric_netbox/data/transform_map.json +21 -35
  7. ipfabric_netbox/filtersets.py +15 -13
  8. ipfabric_netbox/forms.py +14 -42
  9. ipfabric_netbox/jobs.py +12 -7
  10. ipfabric_netbox/migrations/0001_initial.py +1 -1
  11. ipfabric_netbox/migrations/0001_initial_squashed_0013_switch_to_branching_plugin.py +503 -0
  12. ipfabric_netbox/migrations/0007_prepare_custom_fields.py +3 -3
  13. ipfabric_netbox/migrations/0010_remove_uuid_from_get_or_create.py +20 -11
  14. ipfabric_netbox/migrations/0011_update_part_number_DCIM_inventory_item_template.py +57 -0
  15. ipfabric_netbox/migrations/0012_remove_status_field.py +18 -0
  16. ipfabric_netbox/migrations/0013_switch_to_branching_plugin.py +270 -0
  17. ipfabric_netbox/models.py +120 -91
  18. ipfabric_netbox/navigation.py +1 -1
  19. ipfabric_netbox/signals.py +9 -2
  20. ipfabric_netbox/tables.py +40 -46
  21. ipfabric_netbox/templates/ipfabric_netbox/{ipfabricbranch.html → ipfabricingestion.html} +14 -9
  22. ipfabric_netbox/templates/ipfabric_netbox/ipfabricsource.html +3 -3
  23. ipfabric_netbox/templates/ipfabric_netbox/ipfabricsync.html +3 -3
  24. ipfabric_netbox/templates/ipfabric_netbox/ipfabricsync_list.html +71 -0
  25. ipfabric_netbox/templates/ipfabric_netbox/ipfabrictransformmap.html +0 -4
  26. ipfabric_netbox/templates/ipfabric_netbox/partials/ingestion_all.html +10 -0
  27. ipfabric_netbox/templates/ipfabric_netbox/partials/{branch_progress.html → ingestion_progress.html} +1 -1
  28. ipfabric_netbox/templates/ipfabric_netbox/partials/sync_last_ingestion.html +1 -0
  29. ipfabric_netbox/urls.py +13 -3
  30. ipfabric_netbox/utilities/ipfutils.py +52 -19
  31. ipfabric_netbox/views.py +162 -155
  32. {ipfabric_netbox-3.2.3.dist-info → ipfabric_netbox-3.2.4b2.dist-info}/METADATA +12 -4
  33. {ipfabric_netbox-3.2.3.dist-info → ipfabric_netbox-3.2.4b2.dist-info}/RECORD +35 -32
  34. {ipfabric_netbox-3.2.3.dist-info → ipfabric_netbox-3.2.4b2.dist-info}/WHEEL +1 -1
  35. ipfabric_netbox/templates/ipfabric_netbox/inc/sync_delete.html +0 -19
  36. ipfabric_netbox/templates/ipfabric_netbox/partials/branch_all.html +0 -10
  37. ipfabric_netbox/templates/ipfabric_netbox/partials/sync_last_branch.html +0 -1
  38. ipfabric_netbox/templates/ipfabric_netbox/sync_list.html +0 -126
  39. /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 .filtersets import IPFabricStagedChangeFilterSet
35
- from .forms import IPFabricBranchFilterForm
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/sync_list.html",
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
- last_branch = instance.ipfabricbranch_set.last()
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/sync_last_branch.html",
399
- {"last_branch": last_branch},
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}. Branch {last_branch.name} {last_branch.job.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
- last_branch = instance.ipfabricbranch_set.last()
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
- # Ingestion
431
+ return {"format": format, "last_ingestion": last_ingestion}
469
432
 
470
433
 
471
434
  @register_model_view(IPFabricSync, "sync")
472
- class IPFabricIngestSyncView(BaseObjectView):
435
+ class IPFabricStartSyncView(BaseObjectView):
473
436
  queryset = IPFabricSync.objects.all()
474
437
 
475
438
  def get_required_permission(self):
476
- return "ipfabric_netbox.sync_ingest"
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
- # Branch
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
- class IPFabricBranchListView(generic.ObjectListView):
494
- queryset = IPFabricBranch.objects.all()
495
- filterset = IPFabricBranchFilterSet
496
- filterset_form = IPFabricBranchFilterForm
497
- table = BranchTable
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
- IPFabricBranch,
478
+ IPFabricIngestion,
502
479
  name="logs",
503
480
  path="logs",
504
481
  )
505
- class IPFabricBranchLogView(LoginRequiredMixin, View):
506
- template_name = "ipfabric_netbox/partials/branch_all.html"
482
+ class IPFabricIngestionLogView(LoginRequiredMixin, View):
483
+ template_name = "ipfabric_netbox/partials/ingestion_all.html"
507
484
 
508
485
  def get(self, request, **kwargs):
509
- branch_id = kwargs.get("pk")
486
+ ingestion_id = kwargs.get("pk")
510
487
  if request.htmx:
511
- branch = IPFabricBranch.objects.get(pk=branch_id)
512
- data = branch.get_statistics()
513
- data["object"] = branch
514
- data["job"] = branch.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 branch.job.completed:
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(IPFabricBranch)
528
- class IPFabricBranchView(generic.ObjectView):
529
- queryset = IPFabricBranch.objects.annotate(
504
+ @register_model_view(IPFabricIngestion)
505
+ class IPFabricIngestionView(generic.ObjectView):
506
+ queryset = IPFabricIngestion.objects.annotate(
530
507
  num_created=models.Count(
531
- "staged_changes",
532
- filter=models.Q(staged_changes__action=ChangeActionChoices.ACTION_CREATE)
533
- & ~models.Q(staged_changes__object_type__model="objectchange"),
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
- "staged_changes",
537
- filter=models.Q(staged_changes__action=ChangeActionChoices.ACTION_UPDATE)
538
- & ~models.Q(staged_changes__object_type__model="objectchange"),
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
- "staged_changes",
542
- filter=models.Q(staged_changes__action=ChangeActionChoices.ACTION_DELETE)
543
- & ~models.Q(staged_changes__object_type__model="objectchange"),
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(IPFabricBranch, "merge")
553
- class IPFabricBranchMergeView(BaseObjectView):
554
- queryset = IPFabricBranch.objects.all()
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.merge_branch"
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 = ConfirmationForm(initial=request.GET)
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
- ipfabric = get_object_or_404(self.queryset, pk=pk)
583
- job = ipfabric.enqueue_merge_job(user=request.user)
584
-
585
- messages.success(request, f"Queued job #{job.pk} to sync {ipfabric}")
586
- return redirect(ipfabric.get_absolute_url())
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
- IPFabricBranch,
585
+ IPFabricIngestion,
591
586
  name="change_diff",
592
587
  path="change/<int:change_pk>",
593
- kwargs={"model": IPFabricBranch},
588
+ kwargs={"model": IPFabricIngestion},
594
589
  )
595
- class IPFabricBranchChangesDiffView(LoginRequiredMixin, View):
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
- if change_id:
603
- change = StagedChange.objects.get(pk=change_id)
604
- if hasattr(change.object, "pk"):
605
- prechange_data = serialize_object(change.object, resolve_tags=False)
606
- prechange_data = dict(sorted(prechange_data.items()))
607
- else:
608
- prechange_data = None
609
-
610
- if hasattr(change, "data"):
611
- postchange_data = dict(sorted(change.data.items()))
612
-
613
- if prechange_data and postchange_data:
614
- diff_added = shallow_compare_dict(
615
- prechange_data or dict(),
616
- postchange_data or dict(),
617
- exclude=["last_updated"],
618
- )
619
- diff_removed = (
620
- {x: prechange_data.get(x) for x in diff_added}
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
- return render(
629
- request,
630
- self.template_name,
631
- {
632
- "change": change,
633
- "prechange_data": prechange_data,
634
- "postchange_data": postchange_data,
635
- "diff_added": diff_added,
636
- "diff_removed": diff_removed,
637
- "size": "lg",
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(IPFabricBranch, "change")
643
- class IPFabricBranchChangesView(generic.ObjectChildrenView):
644
- queryset = IPFabricBranch.objects.all()
645
- child_model = StagedChange
646
- table = StagedChangesTable
647
- filterset = IPFabricStagedChangeFilterSet
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: StagedChange.objects.filter(branch=obj).count(),
652
- permission="ipfabric_netbox.view_ipfabricbranch",
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(IPFabricBranch, "delete")
660
- class IPFabricBranchDeleteView(generic.ObjectDeleteView):
661
- queryset = IPFabricBranch.objects.all()
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, "branch")
665
- class IPFabricBranchTabView(generic.ObjectChildrenView):
667
+ @register_model_view(IPFabricSync, "ingestion")
668
+ class IPFabricIngestionTabView(generic.ObjectChildrenView):
666
669
  queryset = IPFabricSync.objects.all()
667
- child_model = IPFabricBranch
668
- table = BranchTable
669
- filterset = IPFabricBranchFilterSet
670
+ child_model = IPFabricIngestion
671
+ table = IPFabricIngestionTable
672
+ filterset = IPFabricIngestionFilterSet
670
673
  template_name = "generic/object_children.html"
671
674
  tab = ViewTab(
672
- label="Branches",
673
- badge=lambda obj: IPFabricBranch.objects.filter(sync=obj).count(),
674
- permission="ipfabric_netbox.view_ipfabricbranch",
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
3
+ Version: 3.2.4b2
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 [Staged Changes](https://netboxlabs.com/docs/netbox/en/stable/plugins/development/staged-changes/) 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.
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.2.4 and up | 3.2.2 and up |
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 |