ipfabric_netbox 4.3.1b1__py3-none-any.whl → 4.3.1b3__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 +4 -2
- ipfabric_netbox/choices.py +56 -20
- ipfabric_netbox/filtersets.py +6 -2
- ipfabric_netbox/forms.py +24 -19
- ipfabric_netbox/graphql/enums.py +10 -3
- ipfabric_netbox/graphql/filters.py +4 -3
- ipfabric_netbox/jobs.py +6 -5
- ipfabric_netbox/models.py +46 -45
- ipfabric_netbox/navigation.py +10 -9
- ipfabric_netbox/tables.py +12 -10
- ipfabric_netbox/templates/ipfabric_netbox/inc/clone_form.html +7 -4
- ipfabric_netbox/templates/ipfabric_netbox/inc/diff.html +10 -8
- ipfabric_netbox/templates/ipfabric_netbox/inc/json.html +4 -2
- ipfabric_netbox/templates/ipfabric_netbox/inc/logs_pending.html +4 -3
- ipfabric_netbox/templates/ipfabric_netbox/inc/merge_form.html +7 -5
- ipfabric_netbox/templates/ipfabric_netbox/inc/site_topology_button.html +8 -6
- ipfabric_netbox/templates/ipfabric_netbox/inc/site_topology_modal.html +8 -4
- ipfabric_netbox/templates/ipfabric_netbox/inc/transform_map_field_map.html +2 -1
- ipfabric_netbox/templates/ipfabric_netbox/inc/transform_map_relationship_map.html +2 -1
- ipfabric_netbox/templates/ipfabric_netbox/ipfabric_table.html +4 -4
- ipfabric_netbox/templates/ipfabric_netbox/ipfabricingestion.html +12 -11
- ipfabric_netbox/templates/ipfabric_netbox/ipfabricsnapshot.html +15 -14
- ipfabric_netbox/templates/ipfabric_netbox/ipfabricsource.html +13 -12
- ipfabric_netbox/templates/ipfabric_netbox/ipfabricsync.html +16 -15
- ipfabric_netbox/templates/ipfabric_netbox/ipfabrictransformmap.html +7 -6
- ipfabric_netbox/templates/ipfabric_netbox/ipfabrictransformmap_list.html +2 -1
- ipfabric_netbox/templates/ipfabric_netbox/ipfabrictransformmap_restore.html +1 -1
- ipfabric_netbox/templates/ipfabric_netbox/ipfabrictransformmapgroup.html +4 -3
- ipfabric_netbox/templates/ipfabric_netbox/partials/ingestion_progress.html +5 -4
- ipfabric_netbox/templates/ipfabric_netbox/partials/ingestion_statistics.html +5 -4
- ipfabric_netbox/templates/ipfabric_netbox/partials/job_logs.html +10 -9
- ipfabric_netbox/tests/api/test_api.py +6 -5
- ipfabric_netbox/tests/test_forms.py +16 -15
- ipfabric_netbox/tests/test_views.py +24 -23
- ipfabric_netbox/utilities/ipfutils.py +2 -2
- ipfabric_netbox/views.py +8 -7
- {ipfabric_netbox-4.3.1b1.dist-info → ipfabric_netbox-4.3.1b3.dist-info}/METADATA +1 -1
- {ipfabric_netbox-4.3.1b1.dist-info → ipfabric_netbox-4.3.1b3.dist-info}/RECORD +40 -40
- {ipfabric_netbox-4.3.1b1.dist-info → ipfabric_netbox-4.3.1b3.dist-info}/WHEEL +0 -0
ipfabric_netbox/models.py
CHANGED
|
@@ -7,7 +7,6 @@ from copy import deepcopy
|
|
|
7
7
|
from uuid import uuid4
|
|
8
8
|
|
|
9
9
|
import httpx
|
|
10
|
-
from core.choices import DataSourceStatusChoices
|
|
11
10
|
from core.choices import JobStatusChoices
|
|
12
11
|
from core.exceptions import SyncError
|
|
13
12
|
from core.models import Job
|
|
@@ -46,7 +45,9 @@ from utilities.request import NetBoxFakeRequest
|
|
|
46
45
|
|
|
47
46
|
from .choices import IPFabricRawDataTypeChoices
|
|
48
47
|
from .choices import IPFabricSnapshotStatusModelChoices
|
|
48
|
+
from .choices import IPFabricSourceStatusChoices
|
|
49
49
|
from .choices import IPFabricSourceTypeChoices
|
|
50
|
+
from .choices import IPFabricSyncStatusChoices
|
|
50
51
|
from .choices import IPFabricTransformMapSourceModelChoices
|
|
51
52
|
from .choices import required_transform_map_contenttypes
|
|
52
53
|
from .signals import clear_other_primary_ip
|
|
@@ -104,8 +105,8 @@ class IPFabricTransformMapGroup(NetBoxModel):
|
|
|
104
105
|
|
|
105
106
|
class Meta:
|
|
106
107
|
ordering = ("pk",)
|
|
107
|
-
verbose_name = "IP Fabric Transform Map Group"
|
|
108
|
-
verbose_name_plural = "IP Fabric Transform Map Groups"
|
|
108
|
+
verbose_name = _("IP Fabric Transform Map Group")
|
|
109
|
+
verbose_name_plural = _("IP Fabric Transform Map Groups")
|
|
109
110
|
|
|
110
111
|
def __str__(self):
|
|
111
112
|
return self.name
|
|
@@ -124,7 +125,7 @@ class IPFabricTransformMap(NetBoxModel):
|
|
|
124
125
|
target_model = models.ForeignKey(
|
|
125
126
|
to=ContentType,
|
|
126
127
|
related_name="+",
|
|
127
|
-
verbose_name="Target Model",
|
|
128
|
+
verbose_name=_("Target Model"),
|
|
128
129
|
limit_choices_to=IPFabricSupportedSyncModels,
|
|
129
130
|
help_text=_("The object(s) to which transform map target applies."),
|
|
130
131
|
on_delete=models.PROTECT,
|
|
@@ -141,8 +142,8 @@ class IPFabricTransformMap(NetBoxModel):
|
|
|
141
142
|
|
|
142
143
|
class Meta:
|
|
143
144
|
ordering = ("pk",)
|
|
144
|
-
verbose_name = "IP Fabric Transform Map"
|
|
145
|
-
verbose_name_plural = "IP Fabric Transform Maps"
|
|
145
|
+
verbose_name = _("IP Fabric Transform Map")
|
|
146
|
+
verbose_name_plural = _("IP Fabric Transform Maps")
|
|
146
147
|
|
|
147
148
|
def __str__(self):
|
|
148
149
|
if self.source_model and self.target_model:
|
|
@@ -336,7 +337,7 @@ class IPFabricRelationshipField(models.Model):
|
|
|
336
337
|
ContentType,
|
|
337
338
|
related_name="ipfabric_transform_fields",
|
|
338
339
|
limit_choices_to=IPFabricRelationshipFieldSourceModels,
|
|
339
|
-
verbose_name="Source Model",
|
|
340
|
+
verbose_name=_("Source Model"),
|
|
340
341
|
on_delete=models.PROTECT,
|
|
341
342
|
blank=False,
|
|
342
343
|
null=False,
|
|
@@ -355,8 +356,8 @@ class IPFabricRelationshipField(models.Model):
|
|
|
355
356
|
|
|
356
357
|
class Meta:
|
|
357
358
|
ordering = ("transform_map",)
|
|
358
|
-
verbose_name = "IP Fabric Relationship Field"
|
|
359
|
-
verbose_name_plural = "IP Fabric Relationship Fields"
|
|
359
|
+
verbose_name = _("IP Fabric Relationship Field")
|
|
360
|
+
verbose_name_plural = _("IP Fabric Relationship Fields")
|
|
360
361
|
|
|
361
362
|
@property
|
|
362
363
|
def docs_url(self):
|
|
@@ -385,8 +386,8 @@ class IPFabricTransformField(models.Model):
|
|
|
385
386
|
|
|
386
387
|
class Meta:
|
|
387
388
|
ordering = ("transform_map",)
|
|
388
|
-
verbose_name = "IP Fabric Transform Field"
|
|
389
|
-
verbose_name_plural = "IP Fabric Transform Fields"
|
|
389
|
+
verbose_name = _("IP Fabric Transform Field")
|
|
390
|
+
verbose_name_plural = _("IP Fabric Transform Fields")
|
|
390
391
|
|
|
391
392
|
@property
|
|
392
393
|
def docs_url(self):
|
|
@@ -418,7 +419,7 @@ class IPFabricClient:
|
|
|
418
419
|
self.handle_sync_failure("Error", e)
|
|
419
420
|
|
|
420
421
|
def handle_sync_failure(self, failure_type, exception, message=None):
|
|
421
|
-
self.status =
|
|
422
|
+
self.status = IPFabricSyncStatusChoices.FAILED
|
|
422
423
|
|
|
423
424
|
if message:
|
|
424
425
|
self.logger.log_failure(
|
|
@@ -439,8 +440,8 @@ class IPFabricSource(IPFabricClient, JobsMixin, PrimaryModel):
|
|
|
439
440
|
url = models.CharField(max_length=200, verbose_name=_("URL"))
|
|
440
441
|
status = models.CharField(
|
|
441
442
|
max_length=50,
|
|
442
|
-
choices=
|
|
443
|
-
default=
|
|
443
|
+
choices=IPFabricSourceStatusChoices,
|
|
444
|
+
default=IPFabricSourceStatusChoices.NEW,
|
|
444
445
|
editable=False,
|
|
445
446
|
)
|
|
446
447
|
parameters = models.JSONField(blank=True, null=True)
|
|
@@ -448,8 +449,8 @@ class IPFabricSource(IPFabricClient, JobsMixin, PrimaryModel):
|
|
|
448
449
|
|
|
449
450
|
class Meta:
|
|
450
451
|
ordering = ("name",)
|
|
451
|
-
verbose_name = "IP Fabric Source"
|
|
452
|
-
verbose_name_plural = "IP Fabric Sources"
|
|
452
|
+
verbose_name = _("IP Fabric Source")
|
|
453
|
+
verbose_name_plural = _("IP Fabric Sources")
|
|
453
454
|
|
|
454
455
|
def __str__(self):
|
|
455
456
|
return f"{self.name}"
|
|
@@ -460,8 +461,8 @@ class IPFabricSource(IPFabricClient, JobsMixin, PrimaryModel):
|
|
|
460
461
|
@property
|
|
461
462
|
def ready_for_sync(self):
|
|
462
463
|
return self.status not in (
|
|
463
|
-
|
|
464
|
-
|
|
464
|
+
IPFabricSourceStatusChoices.QUEUED,
|
|
465
|
+
IPFabricSourceStatusChoices.SYNCING,
|
|
465
466
|
)
|
|
466
467
|
|
|
467
468
|
@property
|
|
@@ -476,7 +477,7 @@ class IPFabricSource(IPFabricClient, JobsMixin, PrimaryModel):
|
|
|
476
477
|
|
|
477
478
|
def enqueue_sync_job(self, request):
|
|
478
479
|
# Set the status to "syncing"
|
|
479
|
-
self.status =
|
|
480
|
+
self.status = IPFabricSourceStatusChoices.QUEUED
|
|
480
481
|
IPFabricSource.objects.filter(pk=self.pk).update(status=self.status)
|
|
481
482
|
|
|
482
483
|
# Enqueue a sync job
|
|
@@ -489,7 +490,7 @@ class IPFabricSource(IPFabricClient, JobsMixin, PrimaryModel):
|
|
|
489
490
|
|
|
490
491
|
def sync(self, job):
|
|
491
492
|
self.logger = SyncLogging(job=job.pk)
|
|
492
|
-
if self.status ==
|
|
493
|
+
if self.status == IPFabricSourceStatusChoices.SYNCING:
|
|
493
494
|
self.logger.log_failure(
|
|
494
495
|
"Cannot initiate sync; syncing already in progress.", obj=self
|
|
495
496
|
)
|
|
@@ -497,7 +498,7 @@ class IPFabricSource(IPFabricClient, JobsMixin, PrimaryModel):
|
|
|
497
498
|
|
|
498
499
|
pre_sync.send(sender=self.__class__, instance=self)
|
|
499
500
|
|
|
500
|
-
self.status =
|
|
501
|
+
self.status = IPFabricSourceStatusChoices.SYNCING
|
|
501
502
|
IPFabricSource.objects.filter(pk=self.pk).update(status=self.status)
|
|
502
503
|
|
|
503
504
|
# Begin Sync
|
|
@@ -542,7 +543,7 @@ class IPFabricSource(IPFabricClient, JobsMixin, PrimaryModel):
|
|
|
542
543
|
f"Created/Updated Snapshot {snapshot.name} ({snapshot.snapshot_id})",
|
|
543
544
|
obj=snapshot, # noqa E225
|
|
544
545
|
)
|
|
545
|
-
self.status =
|
|
546
|
+
self.status = IPFabricSourceStatusChoices.COMPLETED
|
|
546
547
|
self.logger.log_success(f"Completed syncing snapshots from {self.name}")
|
|
547
548
|
logger.debug(f"Completed syncing snapshots from {self.url}")
|
|
548
549
|
except Exception as e:
|
|
@@ -587,8 +588,8 @@ class IPFabricSnapshot(TagsMixin, ChangeLoggedModel):
|
|
|
587
588
|
|
|
588
589
|
class Meta:
|
|
589
590
|
ordering = ("source", "-date")
|
|
590
|
-
verbose_name = "IP Fabric Snapshot"
|
|
591
|
-
verbose_name_plural = "IP Fabric Snapshots"
|
|
591
|
+
verbose_name = _("IP Fabric Snapshot")
|
|
592
|
+
verbose_name_plural = _("IP Fabric Snapshots")
|
|
592
593
|
|
|
593
594
|
def __str__(self):
|
|
594
595
|
return f"{self.name} - {self.snapshot_id}"
|
|
@@ -621,8 +622,8 @@ class IPFabricSync(IPFabricClient, JobsMixin, TagsMixin, ChangeLoggedModel):
|
|
|
621
622
|
)
|
|
622
623
|
status = models.CharField(
|
|
623
624
|
max_length=50,
|
|
624
|
-
choices=
|
|
625
|
-
default=
|
|
625
|
+
choices=IPFabricSyncStatusChoices,
|
|
626
|
+
default=IPFabricSyncStatusChoices.NEW,
|
|
626
627
|
editable=False,
|
|
627
628
|
)
|
|
628
629
|
parameters = models.JSONField(blank=True, null=True)
|
|
@@ -646,7 +647,7 @@ class IPFabricSync(IPFabricClient, JobsMixin, TagsMixin, ChangeLoggedModel):
|
|
|
646
647
|
|
|
647
648
|
class Meta:
|
|
648
649
|
ordering = ["pk"]
|
|
649
|
-
verbose_name = "IP Fabric Sync"
|
|
650
|
+
verbose_name = _("IP Fabric Sync")
|
|
650
651
|
|
|
651
652
|
def __str__(self):
|
|
652
653
|
return f"{self.name}"
|
|
@@ -668,7 +669,7 @@ class IPFabricSync(IPFabricClient, JobsMixin, TagsMixin, ChangeLoggedModel):
|
|
|
668
669
|
return reverse("plugins:ipfabric_netbox:ipfabricsync", args=[self.pk])
|
|
669
670
|
|
|
670
671
|
def get_status_color(self):
|
|
671
|
-
return
|
|
672
|
+
return IPFabricSyncStatusChoices.colors.get(self.status)
|
|
672
673
|
|
|
673
674
|
def save(self, *args, **kwargs):
|
|
674
675
|
super().save(*args, **kwargs)
|
|
@@ -677,7 +678,7 @@ class IPFabricSync(IPFabricClient, JobsMixin, TagsMixin, ChangeLoggedModel):
|
|
|
677
678
|
|
|
678
679
|
@property
|
|
679
680
|
def ready_for_sync(self):
|
|
680
|
-
if self.status not in (
|
|
681
|
+
if self.status not in (IPFabricSyncStatusChoices.SYNCING,):
|
|
681
682
|
if self.snapshot_data.source.type == "remote":
|
|
682
683
|
if self.snapshot_data.ipf_data.count() > 0:
|
|
683
684
|
return True
|
|
@@ -723,7 +724,7 @@ class IPFabricSync(IPFabricClient, JobsMixin, TagsMixin, ChangeLoggedModel):
|
|
|
723
724
|
|
|
724
725
|
def enqueue_sync_job(self, adhoc=False, user=None) -> Job | None:
|
|
725
726
|
def set_syncing_status():
|
|
726
|
-
self.status =
|
|
727
|
+
self.status = IPFabricSyncStatusChoices.QUEUED
|
|
727
728
|
IPFabricSync.objects.filter(pk=self.pk).update(status=self.status)
|
|
728
729
|
|
|
729
730
|
def sync_snapshots():
|
|
@@ -794,12 +795,12 @@ class IPFabricSync(IPFabricClient, JobsMixin, TagsMixin, ChangeLoggedModel):
|
|
|
794
795
|
f"Combination of these transform map groups failed validation. Missing maps: {missing}."
|
|
795
796
|
)
|
|
796
797
|
|
|
797
|
-
if self.status ==
|
|
798
|
+
if self.status == IPFabricSyncStatusChoices.SYNCING:
|
|
798
799
|
raise SyncError("Cannot initiate sync; ingestion already in progress.")
|
|
799
800
|
|
|
800
801
|
pre_sync.send(sender=self.__class__, instance=self)
|
|
801
802
|
|
|
802
|
-
self.status =
|
|
803
|
+
self.status = IPFabricSyncStatusChoices.SYNCING
|
|
803
804
|
IPFabricSync.objects.filter(pk=self.pk).update(status=self.status)
|
|
804
805
|
|
|
805
806
|
# Begin Sync
|
|
@@ -875,11 +876,11 @@ class IPFabricSync(IPFabricClient, JobsMixin, TagsMixin, ChangeLoggedModel):
|
|
|
875
876
|
current_request.set(token.old_value)
|
|
876
877
|
active_branch.set(current_branch)
|
|
877
878
|
|
|
878
|
-
if self.status !=
|
|
879
|
-
self.status =
|
|
879
|
+
if self.status != IPFabricSyncStatusChoices.FAILED:
|
|
880
|
+
self.status = IPFabricSyncStatusChoices.READY_TO_MERGE
|
|
880
881
|
|
|
881
882
|
except Exception as e:
|
|
882
|
-
self.status =
|
|
883
|
+
self.status = IPFabricSyncStatusChoices.FAILED
|
|
883
884
|
self.logger.log_failure(f"Ingestion Failed: `{e}`", obj=ingestion)
|
|
884
885
|
self.logger.log_failure(
|
|
885
886
|
f"Stack Trace: `{traceback.format_exc()}`", obj=ingestion
|
|
@@ -893,7 +894,7 @@ class IPFabricSync(IPFabricClient, JobsMixin, TagsMixin, ChangeLoggedModel):
|
|
|
893
894
|
|
|
894
895
|
self.last_synced = timezone.now()
|
|
895
896
|
|
|
896
|
-
if self.auto_merge and self.status ==
|
|
897
|
+
if self.auto_merge and self.status == IPFabricSyncStatusChoices.READY_TO_MERGE:
|
|
897
898
|
self.logger.log_info("Auto Merging Ingestion", obj=ingestion)
|
|
898
899
|
logger.info("Auto Merging Ingestion")
|
|
899
900
|
try:
|
|
@@ -927,8 +928,8 @@ class IPFabricIngestion(JobsMixin, models.Model):
|
|
|
927
928
|
|
|
928
929
|
class Meta:
|
|
929
930
|
ordering = ("pk",)
|
|
930
|
-
verbose_name = "IP Fabric Ingestion"
|
|
931
|
-
verbose_name_plural = "IP Fabric Ingestions"
|
|
931
|
+
verbose_name = _("IP Fabric Ingestion")
|
|
932
|
+
verbose_name_plural = _("IP Fabric Ingestions")
|
|
932
933
|
|
|
933
934
|
def __str__(self):
|
|
934
935
|
return self.name
|
|
@@ -947,7 +948,7 @@ class IPFabricIngestion(JobsMixin, models.Model):
|
|
|
947
948
|
|
|
948
949
|
def enqueue_merge_job(self, user, remove_branch=False):
|
|
949
950
|
# Set the status to "queued"
|
|
950
|
-
self.status =
|
|
951
|
+
self.status = IPFabricSyncStatusChoices.QUEUED
|
|
951
952
|
IPFabricSync.objects.filter(ipfabricingestion=self.pk).update(
|
|
952
953
|
status=self.status
|
|
953
954
|
)
|
|
@@ -988,12 +989,12 @@ class IPFabricIngestion(JobsMixin, models.Model):
|
|
|
988
989
|
|
|
989
990
|
def sync_merge(self):
|
|
990
991
|
ipfabricsync = self.sync
|
|
991
|
-
if ipfabricsync.status ==
|
|
992
|
+
if ipfabricsync.status == IPFabricSyncStatusChoices.MERGING:
|
|
992
993
|
raise SyncError("Cannot initiate merge; merge already in progress.")
|
|
993
994
|
|
|
994
995
|
pre_sync.send(sender=self.__class__, instance=self)
|
|
995
996
|
|
|
996
|
-
ipfabricsync.status =
|
|
997
|
+
ipfabricsync.status = IPFabricSyncStatusChoices.MERGING
|
|
997
998
|
IPFabricSync.objects.filter(ipfabricingestion=self.pk).update(
|
|
998
999
|
status=self.sync.status
|
|
999
1000
|
)
|
|
@@ -1010,9 +1011,9 @@ class IPFabricIngestion(JobsMixin, models.Model):
|
|
|
1010
1011
|
assign_virtualchassis_master, sender=VirtualChassis
|
|
1011
1012
|
)
|
|
1012
1013
|
signals.pre_save.disconnect(clear_other_primary_ip, sender=Device)
|
|
1013
|
-
ipfabricsync.status =
|
|
1014
|
+
ipfabricsync.status = IPFabricSyncStatusChoices.COMPLETED
|
|
1014
1015
|
except Exception as e:
|
|
1015
|
-
ipfabricsync.status =
|
|
1016
|
+
ipfabricsync.status = IPFabricSyncStatusChoices.FAILED
|
|
1016
1017
|
logger.debug(f"Merging {self.name} Failed: `{e}`")
|
|
1017
1018
|
|
|
1018
1019
|
logger.debug(f"Completed merge {self.name}")
|
|
@@ -1039,8 +1040,8 @@ class IPFabricIngestionIssue(models.Model):
|
|
|
1039
1040
|
|
|
1040
1041
|
class Meta:
|
|
1041
1042
|
ordering = ["timestamp"]
|
|
1042
|
-
verbose_name = "IP Fabric Ingestion Issue"
|
|
1043
|
-
verbose_name_plural = "IP Fabric Ingestion Issues"
|
|
1043
|
+
verbose_name = _("IP Fabric Ingestion Issue")
|
|
1044
|
+
verbose_name_plural = _("IP Fabric Ingestion Issues")
|
|
1044
1045
|
|
|
1045
1046
|
def __str__(self):
|
|
1046
1047
|
return f"[{self.timestamp}] {self.message}"
|
ipfabric_netbox/navigation.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from django.utils.translation import gettext as _
|
|
1
2
|
from netbox.plugins import PluginMenu
|
|
2
3
|
from netbox.plugins import PluginMenuButton
|
|
3
4
|
from netbox.plugins import PluginMenuItem
|
|
@@ -6,7 +7,7 @@ from netbox.plugins import PluginMenuItem
|
|
|
6
7
|
sync_buttons = [
|
|
7
8
|
PluginMenuButton(
|
|
8
9
|
link="plugins:ipfabric_netbox:ipfabricsync_add",
|
|
9
|
-
title="Add",
|
|
10
|
+
title=_("Add"),
|
|
10
11
|
icon_class="mdi mdi-plus-thick",
|
|
11
12
|
permissions=["ipfabric_netbox.add_ipfabricsync"],
|
|
12
13
|
)
|
|
@@ -15,7 +16,7 @@ sync_buttons = [
|
|
|
15
16
|
source_buttons = [
|
|
16
17
|
PluginMenuButton(
|
|
17
18
|
link="plugins:ipfabric_netbox:ipfabricsource_add",
|
|
18
|
-
title="Add",
|
|
19
|
+
title=_("Add"),
|
|
19
20
|
icon_class="mdi mdi-plus-thick",
|
|
20
21
|
permissions=["ipfabric_netbox.add_ipfabricsource"],
|
|
21
22
|
)
|
|
@@ -23,33 +24,33 @@ source_buttons = [
|
|
|
23
24
|
|
|
24
25
|
source = PluginMenuItem(
|
|
25
26
|
link="plugins:ipfabric_netbox:ipfabricsource_list",
|
|
26
|
-
link_text="Sources",
|
|
27
|
+
link_text=_("Sources"),
|
|
27
28
|
buttons=source_buttons,
|
|
28
29
|
permissions=["ipfabric_netbox.view_ipfabricsource"],
|
|
29
30
|
)
|
|
30
31
|
|
|
31
32
|
snapshot = PluginMenuItem(
|
|
32
33
|
link="plugins:ipfabric_netbox:ipfabricsnapshot_list",
|
|
33
|
-
link_text="Snapshots",
|
|
34
|
+
link_text=_("Snapshots"),
|
|
34
35
|
permissions=["ipfabric_netbox.view_ipfabricsnapshot"],
|
|
35
36
|
)
|
|
36
37
|
|
|
37
38
|
|
|
38
39
|
ingestion = PluginMenuItem(
|
|
39
40
|
link="plugins:ipfabric_netbox:ipfabricsync_list",
|
|
40
|
-
link_text="Syncs",
|
|
41
|
+
link_text=_("Syncs"),
|
|
41
42
|
buttons=sync_buttons,
|
|
42
43
|
permissions=["ipfabric_netbox.view_ipfabricsync"],
|
|
43
44
|
)
|
|
44
45
|
|
|
45
46
|
tmg = PluginMenuItem(
|
|
46
47
|
link="plugins:ipfabric_netbox:ipfabrictransformmapgroup_list",
|
|
47
|
-
link_text="Transform Map Groups",
|
|
48
|
+
link_text=_("Transform Map Groups"),
|
|
48
49
|
permissions=["ipfabric_netbox.view_ipfabrictransformmapgroup"],
|
|
49
50
|
buttons=[
|
|
50
51
|
PluginMenuButton(
|
|
51
52
|
link="plugins:ipfabric_netbox:ipfabrictransformmapgroup_add",
|
|
52
|
-
title="Add",
|
|
53
|
+
title=_("Add"),
|
|
53
54
|
icon_class="mdi mdi-plus-thick",
|
|
54
55
|
permissions=["ipfabric_netbox.add_ipfabrictransformmapgroup"],
|
|
55
56
|
)
|
|
@@ -58,12 +59,12 @@ tmg = PluginMenuItem(
|
|
|
58
59
|
|
|
59
60
|
tm = PluginMenuItem(
|
|
60
61
|
link="plugins:ipfabric_netbox:ipfabrictransformmap_list",
|
|
61
|
-
link_text="Transform Maps",
|
|
62
|
+
link_text=_("Transform Maps"),
|
|
62
63
|
permissions=["ipfabric_netbox.view_ipfabrictransformmap"],
|
|
63
64
|
buttons=[
|
|
64
65
|
PluginMenuButton(
|
|
65
66
|
link="plugins:ipfabric_netbox:ipfabrictransformmap_add",
|
|
66
|
-
title="Add",
|
|
67
|
+
title=_("Add"),
|
|
67
68
|
icon_class="mdi mdi-plus-thick",
|
|
68
69
|
permissions=["ipfabric_netbox.add_ipfabrictransformmap"],
|
|
69
70
|
)
|
ipfabric_netbox/tables.py
CHANGED
|
@@ -68,7 +68,7 @@ class IPFabricTransformMapGroupTable(NetBoxTable):
|
|
|
68
68
|
maps_count = columns.LinkedCountColumn(
|
|
69
69
|
viewname="plugins:ipfabric_netbox:ipfabrictransformmap_list",
|
|
70
70
|
url_params={"group_id": "pk"},
|
|
71
|
-
verbose_name="Transform Maps",
|
|
71
|
+
verbose_name=_("Transform Maps"),
|
|
72
72
|
)
|
|
73
73
|
|
|
74
74
|
class Meta(NetBoxTable.Meta):
|
|
@@ -90,9 +90,11 @@ class IPFabricTransformMapTable(NetBoxTable):
|
|
|
90
90
|
|
|
91
91
|
class IPFabricIngestionTable(NetBoxTable):
|
|
92
92
|
name = tables.Column(linkify=True)
|
|
93
|
-
sync = tables.Column(verbose_name="IP Fabric Sync", linkify=True)
|
|
93
|
+
sync = tables.Column(verbose_name=_("IP Fabric Sync"), linkify=True)
|
|
94
94
|
branch = tables.Column(linkify=True)
|
|
95
|
-
changes = tables.Column(
|
|
95
|
+
changes = tables.Column(
|
|
96
|
+
accessor="staged_changes", verbose_name=_("Number of Changes")
|
|
97
|
+
)
|
|
96
98
|
actions = columns.ActionsColumn(actions=("delete",))
|
|
97
99
|
|
|
98
100
|
class Meta(NetBoxTable.Meta):
|
|
@@ -126,7 +128,7 @@ class IPFabricSnapshotTable(NetBoxTable):
|
|
|
126
128
|
class IPFabricSourceTable(NetBoxTable):
|
|
127
129
|
name = tables.Column(linkify=True)
|
|
128
130
|
status = columns.ChoiceFieldColumn()
|
|
129
|
-
snapshot_count = tables.Column(verbose_name="Snapshots")
|
|
131
|
+
snapshot_count = tables.Column(verbose_name=_("Snapshots"))
|
|
130
132
|
tags = columns.TagColumn(url_name="core:datasource_list")
|
|
131
133
|
|
|
132
134
|
class Meta(NetBoxTable.Meta):
|
|
@@ -148,13 +150,13 @@ class IPFabricSyncTable(NetBoxTable):
|
|
|
148
150
|
name = tables.Column(linkify=True)
|
|
149
151
|
status = columns.ChoiceFieldColumn()
|
|
150
152
|
snapshot_name = tables.Column(
|
|
151
|
-
verbose_name="Snapshot Name",
|
|
153
|
+
verbose_name=_("Snapshot Name"),
|
|
152
154
|
accessor="snapshot_data",
|
|
153
155
|
linkify=True,
|
|
154
156
|
)
|
|
155
157
|
last_ingestion = tables.Column(
|
|
156
158
|
accessor="last_ingestion",
|
|
157
|
-
verbose_name="Last Ingestion",
|
|
159
|
+
verbose_name=_("Last Ingestion"),
|
|
158
160
|
linkify=True,
|
|
159
161
|
)
|
|
160
162
|
|
|
@@ -186,9 +188,9 @@ class IPFabricIngestionChangesTable(NetBoxTable):
|
|
|
186
188
|
id = tables.Column(verbose_name=_("ID"))
|
|
187
189
|
pk = None
|
|
188
190
|
object_type = tables.Column(
|
|
189
|
-
accessor="object_type.model", verbose_name="Object Type"
|
|
191
|
+
accessor="object_type.model", verbose_name=_("Object Type")
|
|
190
192
|
)
|
|
191
|
-
object = tables.Column(verbose_name="Object")
|
|
193
|
+
object = tables.Column(verbose_name=_("Object"))
|
|
192
194
|
actions = columns.TemplateColumn(template_code=DIFF_BUTTON)
|
|
193
195
|
|
|
194
196
|
def render_object(self, value, record):
|
|
@@ -225,8 +227,8 @@ class IPFabricIngestionChangesTable(NetBoxTable):
|
|
|
225
227
|
|
|
226
228
|
class IPFabricIngestionIssuesTable(NetBoxTable):
|
|
227
229
|
id = tables.Column(verbose_name=_("ID"))
|
|
228
|
-
exception = tables.Column(verbose_name="Exception Type")
|
|
229
|
-
message = tables.Column(verbose_name="Error Message")
|
|
230
|
+
exception = tables.Column(verbose_name=_("Exception Type"))
|
|
231
|
+
message = tables.Column(verbose_name=_("Error Message"))
|
|
230
232
|
actions = None
|
|
231
233
|
|
|
232
234
|
class Meta(NetBoxTable.Meta):
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
{% load form_helpers %}
|
|
2
|
+
{% load i18n %}
|
|
2
3
|
|
|
3
4
|
<form action="{{ form_url }}" method="post">
|
|
4
5
|
{% csrf_token %}
|
|
5
6
|
|
|
6
7
|
<div class="modal-header">
|
|
7
|
-
<h5 class="modal-title">Confirm Clone</h5>
|
|
8
|
+
<h5 class="modal-title">{% trans "Confirm Clone" %}</h5>
|
|
8
9
|
</div>
|
|
9
10
|
<div class="modal-body">
|
|
11
|
+
{% blocktrans trimmed with count=selected_objects|length %}
|
|
10
12
|
<p>Are you sure you want to <strong class="text-danger">clone</strong> {{ object_type }} <strong>{{ object }}</strong>?</p>
|
|
13
|
+
{% endblocktrans %}
|
|
11
14
|
{% if form.non_field_errors %}
|
|
12
15
|
<div class="form-text text-danger">
|
|
13
16
|
{{ form.non_field_errors }}
|
|
@@ -17,11 +20,11 @@
|
|
|
17
20
|
</div>
|
|
18
21
|
<div class="modal-footer">
|
|
19
22
|
{% if return_url %}
|
|
20
|
-
<a href="{{ return_url }}" class="btn btn-outline-secondary">Cancel</a>
|
|
23
|
+
<a href="{{ return_url }}" class="btn btn-outline-secondary">{% trans "Cancel" %}</a>
|
|
21
24
|
{% else %}
|
|
22
|
-
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
25
|
+
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">{% trans "Cancel" %}</button>
|
|
23
26
|
{% endif %}
|
|
24
|
-
<button hx-post="{% url 'plugins:ipfabric_netbox:ipfabrictransformmap_clone' pk=pk %}" hx-target="#htmx-modal-content" type="submit" class="btn btn-danger">Clone</button>
|
|
27
|
+
<button hx-post="{% url 'plugins:ipfabric_netbox:ipfabrictransformmap_clone' pk=pk %}" hx-target="#htmx-modal-content" type="submit" class="btn btn-danger">{% trans "Clone" %}</button>
|
|
25
28
|
</div>
|
|
26
29
|
</div>
|
|
27
30
|
</form>
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
+
{% load i18n %}
|
|
1
2
|
<div class="modal-content" id="htmx-modal-content">
|
|
2
3
|
<div class="modal-header">
|
|
3
|
-
<h5 class="modal-title">
|
|
4
|
+
<h5 class="modal-title">
|
|
5
|
+
{% blocktrans %}Change Diff {{ change }}{% endblocktrans %}</h5>
|
|
4
6
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
5
7
|
</div>
|
|
6
8
|
<div class="modal-body">
|
|
7
9
|
<div class="row mb-3">
|
|
8
10
|
<div class="col-6">
|
|
9
11
|
<div class="card">
|
|
10
|
-
<h5 class="card-header"
|
|
12
|
+
<h5 class="card-header">%{ trans "Pre-Change Data" %}</h5>
|
|
11
13
|
<div class="card-body">
|
|
12
14
|
{% if prechange_data %}
|
|
13
15
|
<pre class="change-data">{% for k, v in prechange_data.items %}{% spaceless %}
|
|
@@ -15,14 +17,14 @@
|
|
|
15
17
|
{% endspaceless %}{% endfor %}
|
|
16
18
|
</pre>
|
|
17
19
|
{% else %}
|
|
18
|
-
<span class="text-muted"
|
|
20
|
+
<span class="text-muted">%{ trans "None" %}</span>
|
|
19
21
|
{% endif %}
|
|
20
22
|
</div>
|
|
21
23
|
</div>
|
|
22
24
|
</div>
|
|
23
25
|
<div class="col-6">
|
|
24
26
|
<div class="card">
|
|
25
|
-
<h5 class="card-header"
|
|
27
|
+
<h5 class="card-header">%{ trans "Post-Change Data" %}</h5>
|
|
26
28
|
<div class="card-body">
|
|
27
29
|
{% if postchange_data %}
|
|
28
30
|
<pre class="change-data">{% for k, v in postchange_data.items %}{% spaceless %}
|
|
@@ -36,7 +38,7 @@
|
|
|
36
38
|
<div class="col">
|
|
37
39
|
<div class="card">
|
|
38
40
|
<div class="card-header">
|
|
39
|
-
<h5 class="d-inline"
|
|
41
|
+
<h5 class="d-inline">%{ trans "Difference" %}</h5>
|
|
40
42
|
<!-- <div class="btn-group btn-group-sm float-end noprint">
|
|
41
43
|
<a {% if prev_change %}href="{% url 'extras:objectchange' pk=prev_change.pk %}"{% else %}disabled{% endif %} class="btn btn-outline-secondary">
|
|
42
44
|
<i class="mdi mdi-chevron-left" aria-hidden="true"></i> Previous
|
|
@@ -50,11 +52,11 @@
|
|
|
50
52
|
{% if diff_added == diff_removed %}
|
|
51
53
|
<span class="text-muted" style="margin-left: 10px;">
|
|
52
54
|
{% if object.action == 'create' %}
|
|
53
|
-
Object Created
|
|
55
|
+
%{ trans "Object Created" %}
|
|
54
56
|
{% elif object.action == 'delete' %}
|
|
55
|
-
Object Deleted
|
|
57
|
+
%{ trans "Object Deleted" %}
|
|
56
58
|
{% else %}
|
|
57
|
-
No Changes
|
|
59
|
+
%{ trans "No Changes" %}
|
|
58
60
|
{% endif %}
|
|
59
61
|
</span>
|
|
60
62
|
{% else %}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
{% load i18n %}
|
|
2
|
+
|
|
1
3
|
<div class="modal-header">
|
|
2
|
-
<h5 class="modal-title">Raw Data {{ object.pk }}</h5>
|
|
4
|
+
<h5 class="modal-title">{% blocktrans %}Raw Data {{ object.pk }}{% endblocktrans %}</h5>
|
|
3
5
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
4
6
|
</div>
|
|
5
7
|
<div class="modal-body">
|
|
6
8
|
<div class="row mb-3">
|
|
7
9
|
<div class="col-12">
|
|
8
10
|
<div class="card">
|
|
9
|
-
<h5 class="card-header">JSON Output</h5>
|
|
11
|
+
<h5 class="card-header">{% trans "JSON Output" %}</h5>
|
|
10
12
|
<div class="card-body">
|
|
11
13
|
<pre>{{ object.data | json }}</pre>
|
|
12
14
|
</div>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
{% load i18n %}
|
|
1
2
|
{# Indicates that a job result is still pending; used for HTMX requests #}
|
|
2
3
|
<div class="spinner-border float-start me-1" id="spinner" role="status">
|
|
3
|
-
<span class="visually-hidden">Loading
|
|
4
|
+
<span class="visually-hidden">{% trans "Loading..." %}</span>
|
|
4
5
|
</div>
|
|
5
|
-
<h3>Logs pending
|
|
6
|
-
<small class="text-muted">Last updated {% now "H:i:s" %}</small>
|
|
6
|
+
<h3>{% trans "Logs pending..." %}</h3>
|
|
7
|
+
<small class="text-muted">{% trans "Last updated" %} {% now "H:i:s" %}</small>
|
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
{% load form_helpers %}
|
|
2
|
-
|
|
2
|
+
{% load i18n %}
|
|
3
3
|
<form action="{{ form_url }}" method="post">
|
|
4
4
|
{% csrf_token %}
|
|
5
5
|
|
|
6
6
|
<div class="modal-header">
|
|
7
|
-
<h5 class="modal-title">Confirm Merge</h5>
|
|
7
|
+
<h5 class="modal-title">{% trans "Confirm Merge" %}</h5>
|
|
8
8
|
</div>
|
|
9
9
|
<div class="modal-body">
|
|
10
|
+
{% blocktrans %}
|
|
10
11
|
<p>Are you sure you want to <strong class="text-danger">merge</strong> {{ object_type }} <strong>{{ object }}</strong>?</p>
|
|
12
|
+
{% endblocktrans %}
|
|
11
13
|
{% render_form form %}
|
|
12
14
|
</div>
|
|
13
15
|
<div class="modal-footer">
|
|
14
16
|
{% if return_url %}
|
|
15
|
-
<a href="{{ return_url }}" class="btn btn-outline-secondary">Cancel</a>
|
|
17
|
+
<a href="{{ return_url }}" class="btn btn-outline-secondary">{% trans "Cancel" %}</a>
|
|
16
18
|
{% else %}
|
|
17
|
-
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
19
|
+
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">{% trans "Cancel" %}</button>
|
|
18
20
|
{% endif %}
|
|
19
|
-
<button type="submit" class="btn btn-danger">Merge</button>
|
|
21
|
+
<button type="submit" class="btn btn-danger">{% trans "Merge" %}</button>
|
|
20
22
|
</div>
|
|
21
23
|
</div>
|
|
22
24
|
</form>
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
{% load i18n %}
|
|
2
|
+
|
|
1
3
|
<div class="btn-group" role="group" aria-label="Button group with nested dropdown">
|
|
2
4
|
<div class="btn-group" role="group">
|
|
3
5
|
<button id="btnGroupDrop1" type="button" class="btn btn-sm btn-success dropdown-toggle" data-bs-toggle="dropdown"
|
|
4
6
|
aria-expanded="false">
|
|
5
|
-
IP Fabric Topology
|
|
7
|
+
{% trans "IP Fabric Topology" %}
|
|
6
8
|
</button>
|
|
7
9
|
<ul class="dropdown-menu" aria-labelledby="btnGroupDrop1">
|
|
8
10
|
{% if source is not None %}
|
|
@@ -10,19 +12,19 @@
|
|
|
10
12
|
hx-get="{% url 'plugins:ipfabric_netbox:ipfabricsource_topology' pk=source.pk site=object.pk %}?snapshot=$last&source={{ source.id }}"
|
|
11
13
|
hx-target="#topology-modal-content" data-bs-toggle="modal" data-bs-target="#topology-modal"
|
|
12
14
|
class="dropdown-item">
|
|
13
|
-
Last Snapshot
|
|
15
|
+
{% trans "Last Snapshot" %}
|
|
14
16
|
</a>
|
|
15
17
|
</li>
|
|
16
18
|
<li><a href="#"
|
|
17
19
|
hx-get="{% url 'plugins:ipfabric_netbox:ipfabricsource_topology' pk=source.pk site=object.pk %}?snapshot=$prev&source={{ source.id }}"
|
|
18
20
|
hx-target="#topology-modal-content" data-bs-toggle="modal" data-bs-target="#topology-modal"
|
|
19
21
|
class="dropdown-item">
|
|
20
|
-
Previous Snapshot
|
|
22
|
+
{% trans "Previous Snapshot" %}
|
|
21
23
|
</a>
|
|
22
24
|
</li>
|
|
23
25
|
{% else %}
|
|
24
26
|
<li>
|
|
25
|
-
<span class="dropdown-item text-danger">No IP Fabric Source Found for this Site</span>
|
|
27
|
+
<span class="dropdown-item text-danger">{% trans "No IP Fabric Source Found for this Site" %}</span>
|
|
26
28
|
</li>
|
|
27
29
|
{% endif %}
|
|
28
30
|
</ul>
|
|
@@ -33,13 +35,13 @@
|
|
|
33
35
|
<div class="modal-dialog modal-xl">
|
|
34
36
|
<div class="modal-content">
|
|
35
37
|
<div class="modal-header">
|
|
36
|
-
<h5 class="modal-title">{{ object.name }} Topology</h5>
|
|
38
|
+
<h5 class="modal-title">{{ object.name }} {% trans "Topology" %}</h5>
|
|
37
39
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
38
40
|
</div>
|
|
39
41
|
<div id="topology-modal-content">
|
|
40
42
|
<div class="modal-body">
|
|
41
43
|
<div class="d-flex align-items-center">
|
|
42
|
-
<strong role="status">Loading
|
|
44
|
+
<strong role="status">{% trans "Loading..." %}</strong>
|
|
43
45
|
<div class="spinner-border ms-auto" aria-hidden="true"></div>
|
|
44
46
|
</div>
|
|
45
47
|
</div>
|