ipfabric_netbox 4.2.0b8__py3-none-any.whl → 4.2.1__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/__init__.py +1 -0
- ipfabric_netbox/api/nested_serializers.py +78 -0
- ipfabric_netbox/api/serializers.py +90 -147
- ipfabric_netbox/api/urls.py +4 -4
- ipfabric_netbox/api/views.py +19 -18
- ipfabric_netbox/choices.py +12 -0
- ipfabric_netbox/filtersets.py +4 -67
- ipfabric_netbox/forms.py +13 -13
- ipfabric_netbox/models.py +10 -10
- ipfabric_netbox/tables.py +9 -30
- ipfabric_netbox/template_content.py +20 -3
- ipfabric_netbox/templates/ipfabric_netbox/inc/site_topology_button.html +10 -3
- ipfabric_netbox/templates/ipfabric_netbox/ipfabricsync_list.html +71 -0
- ipfabric_netbox/tests/test_models.py +11 -47
- ipfabric_netbox/utilities/ipfutils.py +23 -43
- ipfabric_netbox/views.py +8 -6
- {ipfabric_netbox-4.2.0b8.dist-info → ipfabric_netbox-4.2.1.dist-info}/METADATA +6 -6
- {ipfabric_netbox-4.2.0b8.dist-info → ipfabric_netbox-4.2.1.dist-info}/RECORD +20 -28
- ipfabric_netbox/graphql/__init__.py +0 -23
- ipfabric_netbox/graphql/enums.py +0 -35
- ipfabric_netbox/graphql/filters.py +0 -317
- ipfabric_netbox/graphql/schema.py +0 -101
- ipfabric_netbox/graphql/types.py +0 -216
- ipfabric_netbox/migrations/0016_tags_and_changelog_for_snapshots.py +0 -31
- ipfabric_netbox/migrations/0017_ipfabricsync_update_custom_fields.py +0 -17
- ipfabric_netbox/migrations/0018_remove_type_field.py +0 -17
- ipfabric_netbox/tests/api/__init__.py +0 -0
- ipfabric_netbox/tests/api/test_api.py +0 -879
- {ipfabric_netbox-4.2.0b8.dist-info → ipfabric_netbox-4.2.1.dist-info}/WHEEL +0 -0
ipfabric_netbox/forms.py
CHANGED
|
@@ -488,18 +488,11 @@ class IPFabricSyncForm(NetBoxModelForm):
|
|
|
488
488
|
sites = forms.MultipleChoiceField(
|
|
489
489
|
required=False,
|
|
490
490
|
label=_("Sites"),
|
|
491
|
-
help_text=_("Defaults to all sites if none selected."),
|
|
492
491
|
widget=APISelectMultiple(
|
|
493
492
|
api_url="/api/plugins/ipfabric/snapshot/{{snapshot_data}}/sites/",
|
|
494
493
|
),
|
|
495
494
|
)
|
|
496
495
|
|
|
497
|
-
update_custom_fields = forms.BooleanField(
|
|
498
|
-
required=False,
|
|
499
|
-
label=_("Custom Fields Updating"),
|
|
500
|
-
help_text=_("Update object custom fields where applicable."),
|
|
501
|
-
)
|
|
502
|
-
|
|
503
496
|
scheduled = forms.DateTimeField(
|
|
504
497
|
required=False,
|
|
505
498
|
widget=DateTimePicker(),
|
|
@@ -526,13 +519,16 @@ class IPFabricSyncForm(NetBoxModelForm):
|
|
|
526
519
|
"source",
|
|
527
520
|
"snapshot_data",
|
|
528
521
|
"auto_merge",
|
|
529
|
-
"update_custom_fields",
|
|
530
522
|
"sites",
|
|
523
|
+
"type",
|
|
531
524
|
"tags",
|
|
532
525
|
"scheduled",
|
|
533
526
|
"interval",
|
|
534
527
|
)
|
|
535
|
-
widgets = {
|
|
528
|
+
widgets = {
|
|
529
|
+
"source": HTMXSelect(),
|
|
530
|
+
"type": HTMXSelect(),
|
|
531
|
+
}
|
|
536
532
|
|
|
537
533
|
@property
|
|
538
534
|
def fieldsets(self):
|
|
@@ -547,15 +543,14 @@ class IPFabricSyncForm(NetBoxModelForm):
|
|
|
547
543
|
fieldsets.append(
|
|
548
544
|
FieldSet("snapshot_data", name=_("Snapshot Information")),
|
|
549
545
|
)
|
|
546
|
+
fieldsets.append(FieldSet("type", name=_("Ingestion Type")))
|
|
550
547
|
if self.backend_fields:
|
|
551
548
|
for k, v in self.backend_fields.items():
|
|
552
549
|
fieldsets.append(FieldSet(*v, name=f"{k.upper()} Parameters"))
|
|
553
550
|
fieldsets.append(
|
|
554
551
|
FieldSet("scheduled", "interval", name=_("Ingestion Execution Parameters"))
|
|
555
552
|
)
|
|
556
|
-
fieldsets.append(
|
|
557
|
-
FieldSet("auto_merge", "update_custom_fields", name=_("Extras"))
|
|
558
|
-
)
|
|
553
|
+
fieldsets.append(FieldSet("auto_merge", name=_("Extras")))
|
|
559
554
|
fieldsets.append(FieldSet("tags", name=_("Tags")))
|
|
560
555
|
|
|
561
556
|
return fieldsets
|
|
@@ -590,7 +585,12 @@ class IPFabricSyncForm(NetBoxModelForm):
|
|
|
590
585
|
self.initial["sites"] = self.instance.parameters.get("sites", [])
|
|
591
586
|
self.initial["groups"] = self.instance.parameters.get("groups", [])
|
|
592
587
|
|
|
593
|
-
|
|
588
|
+
backend_type = get_field_value(self, "type")
|
|
589
|
+
backend = {}
|
|
590
|
+
if backend_type == "all":
|
|
591
|
+
backend = sync_parameters
|
|
592
|
+
else:
|
|
593
|
+
backend[backend_type] = sync_parameters.get(backend_type)
|
|
594
594
|
|
|
595
595
|
now = local_now().strftime("%Y-%m-%d %H:%M:%S")
|
|
596
596
|
self.fields["scheduled"].help_text += f" (current time: <strong>{now}</strong>)"
|
ipfabric_netbox/models.py
CHANGED
|
@@ -44,6 +44,7 @@ from utilities.request import NetBoxFakeRequest
|
|
|
44
44
|
from .choices import IPFabricRawDataTypeChoices
|
|
45
45
|
from .choices import IPFabricSnapshotStatusModelChoices
|
|
46
46
|
from .choices import IPFabricSourceTypeChoices
|
|
47
|
+
from .choices import IPFabricSyncTypeChoices
|
|
47
48
|
from .choices import IPFabricTransformMapSourceModelChoices
|
|
48
49
|
from .choices import required_transform_map_contenttypes
|
|
49
50
|
from .signals import clear_other_primary_ip
|
|
@@ -544,7 +545,9 @@ class IPFabricSource(IPFabricClient, JobsMixin, PrimaryModel):
|
|
|
544
545
|
# post_sync.send(sender=self.__class__, instance=self)
|
|
545
546
|
|
|
546
547
|
|
|
547
|
-
class IPFabricSnapshot(
|
|
548
|
+
class IPFabricSnapshot(models.Model):
|
|
549
|
+
created = models.DateTimeField(auto_now_add=True)
|
|
550
|
+
last_updated = models.DateTimeField(editable=False)
|
|
548
551
|
source = models.ForeignKey(
|
|
549
552
|
to=IPFabricSource,
|
|
550
553
|
on_delete=models.CASCADE,
|
|
@@ -597,6 +600,11 @@ class IPFabricSync(IPFabricClient, JobsMixin, TagsMixin, ChangeLoggedModel):
|
|
|
597
600
|
on_delete=models.CASCADE,
|
|
598
601
|
related_name="snapshots",
|
|
599
602
|
)
|
|
603
|
+
type = models.CharField(
|
|
604
|
+
max_length=50,
|
|
605
|
+
choices=IPFabricSyncTypeChoices,
|
|
606
|
+
default=IPFabricSyncTypeChoices.DCIM,
|
|
607
|
+
)
|
|
600
608
|
status = models.CharField(
|
|
601
609
|
max_length=50,
|
|
602
610
|
choices=DataSourceStatusChoices,
|
|
@@ -605,7 +613,6 @@ class IPFabricSync(IPFabricClient, JobsMixin, TagsMixin, ChangeLoggedModel):
|
|
|
605
613
|
)
|
|
606
614
|
parameters = models.JSONField(blank=True, null=True)
|
|
607
615
|
auto_merge = models.BooleanField(default=False)
|
|
608
|
-
update_custom_fields = models.BooleanField(default=True)
|
|
609
616
|
last_synced = models.DateTimeField(blank=True, null=True, editable=False)
|
|
610
617
|
scheduled = models.DateTimeField(null=True, blank=True)
|
|
611
618
|
interval = models.PositiveIntegerField(
|
|
@@ -661,10 +668,6 @@ class IPFabricSync(IPFabricClient, JobsMixin, TagsMixin, ChangeLoggedModel):
|
|
|
661
668
|
else:
|
|
662
669
|
return False
|
|
663
670
|
|
|
664
|
-
@property
|
|
665
|
-
def last_ingestion(self):
|
|
666
|
-
return self.ipfabricingestion_set.last()
|
|
667
|
-
|
|
668
671
|
@staticmethod
|
|
669
672
|
def get_transform_maps(group_ids=None):
|
|
670
673
|
"""
|
|
@@ -886,10 +889,7 @@ class IPFabricIngestion(JobsMixin, models.Model):
|
|
|
886
889
|
def name(self):
|
|
887
890
|
if self.branch:
|
|
888
891
|
return self.branch.name
|
|
889
|
-
|
|
890
|
-
return f"{self.sync.name} (Ingestion {self.pk})"
|
|
891
|
-
except IPFabricIngestion.sync.RelatedObjectDoesNotExist:
|
|
892
|
-
return f"Ingestion {self.pk} (No Sync)"
|
|
892
|
+
return f"{self.sync.name} (Ingestion {self.pk})"
|
|
893
893
|
|
|
894
894
|
def get_absolute_url(self):
|
|
895
895
|
return reverse("plugins:ipfabric_netbox:ipfabricingestion", args=[self.pk])
|
ipfabric_netbox/tables.py
CHANGED
|
@@ -142,41 +142,20 @@ class IPFabricSourceTable(NetBoxTable):
|
|
|
142
142
|
default_columns = ("pk", "name", "status", "description", "snapshot_count")
|
|
143
143
|
|
|
144
144
|
|
|
145
|
-
class
|
|
146
|
-
|
|
145
|
+
class SyncTable(NetBoxTable):
|
|
146
|
+
actions = None
|
|
147
147
|
status = columns.ChoiceFieldColumn()
|
|
148
148
|
snapshot_name = tables.Column(
|
|
149
|
-
verbose_name="Snapshot Name",
|
|
150
|
-
accessor="snapshot_data",
|
|
151
|
-
linkify=True,
|
|
152
|
-
)
|
|
153
|
-
last_ingestion = tables.Column(
|
|
154
|
-
accessor="last_ingestion",
|
|
155
|
-
verbose_name="Last Ingestion",
|
|
156
|
-
linkify=True,
|
|
149
|
+
verbose_name="Snapshot Name", accessor="snapshot_data"
|
|
157
150
|
)
|
|
158
151
|
|
|
159
|
-
def
|
|
160
|
-
return
|
|
161
|
-
|
|
162
|
-
def render_snapshot_name(self, value: IPFabricSnapshot):
|
|
163
|
-
return getattr(value, "name", "---") if value else "---"
|
|
152
|
+
def render_snapshot_name(self, value):
|
|
153
|
+
return value.get("name", "---")
|
|
164
154
|
|
|
165
155
|
class Meta(NetBoxTable.Meta):
|
|
166
156
|
model = IPFabricSync
|
|
167
|
-
fields = (
|
|
168
|
-
|
|
169
|
-
"id",
|
|
170
|
-
"interval",
|
|
171
|
-
"last_synced",
|
|
172
|
-
"last_ingestion",
|
|
173
|
-
"name",
|
|
174
|
-
"scheduled",
|
|
175
|
-
"status",
|
|
176
|
-
"snapshot_name",
|
|
177
|
-
"user",
|
|
178
|
-
)
|
|
179
|
-
default_columns = ("name", "status", "last_ingestion", "snapshot_name")
|
|
157
|
+
fields = ("id", "status", "snapshot_name")
|
|
158
|
+
default_columns = ("id", "status", "snapshot_name")
|
|
180
159
|
|
|
181
160
|
|
|
182
161
|
class IPFabricIngestionChangesTable(NetBoxTable):
|
|
@@ -262,5 +241,5 @@ class IPFabricDataTable(NetBoxTable):
|
|
|
262
241
|
|
|
263
242
|
class Meta(NetBoxTable.Meta):
|
|
264
243
|
model = IPFabricData
|
|
265
|
-
fields = ("snapshot_data", "JSON")
|
|
266
|
-
default_columns = ("snapshot_data", "JSON")
|
|
244
|
+
fields = ("snapshot_data", "type", "JSON")
|
|
245
|
+
default_columns = ("snapshot_data", "type", "JSON")
|
|
@@ -1,13 +1,30 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
1
3
|
from netbox.plugins import PluginTemplateExtension
|
|
2
4
|
|
|
5
|
+
from ipfabric_netbox.models import IPFabricSnapshot
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger("ipfabric_netbox.template_content")
|
|
8
|
+
|
|
3
9
|
|
|
4
10
|
class SiteTopologyButtons(PluginTemplateExtension):
|
|
5
11
|
model = "dcim.site"
|
|
6
12
|
|
|
7
13
|
def buttons(self):
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
14
|
+
try:
|
|
15
|
+
site = self.context.get("object")
|
|
16
|
+
source = None
|
|
17
|
+
for snapshot in IPFabricSnapshot.objects.all():
|
|
18
|
+
# `Site.name` is unique in DB, so we can use it to match against IPF snapshots
|
|
19
|
+
if site.name in snapshot.sites:
|
|
20
|
+
source = snapshot.source
|
|
21
|
+
return self.render(
|
|
22
|
+
"ipfabric_netbox/inc/site_topology_button.html",
|
|
23
|
+
extra_context={"source": source},
|
|
24
|
+
)
|
|
25
|
+
except Exception as e:
|
|
26
|
+
logger.error(f"Could not render topology button: {e}.")
|
|
27
|
+
return "render error"
|
|
11
28
|
|
|
12
29
|
|
|
13
30
|
template_extensions = [SiteTopologyButtons]
|
|
@@ -6,19 +6,26 @@
|
|
|
6
6
|
IP Fabric Topology
|
|
7
7
|
</button>
|
|
8
8
|
<ul class="dropdown-menu" aria-labelledby="btnGroupDrop1">
|
|
9
|
+
{% if source is not None %}
|
|
9
10
|
<li><a href="#"
|
|
10
|
-
hx-get="{% url 'plugins:ipfabric_netbox:ipfabricsource_topology' pk=
|
|
11
|
+
hx-get="{% url 'plugins:ipfabric_netbox:ipfabricsource_topology' pk=source.pk site=object.pk %}?snapshot=$last&source={{ source.id }}"
|
|
11
12
|
hx-target="#topology-modal-content" data-bs-toggle="modal" data-bs-target="#topology-modal"
|
|
12
13
|
class="dropdown-item">
|
|
13
14
|
Last Snapshot
|
|
14
15
|
</a>
|
|
15
16
|
</li>
|
|
16
17
|
<li><a href="#"
|
|
17
|
-
hx-get="{% url 'plugins:ipfabric_netbox:ipfabricsource_topology' pk=
|
|
18
|
+
hx-get="{% url 'plugins:ipfabric_netbox:ipfabricsource_topology' pk=source.pk site=object.pk %}?snapshot=$prev&source={{ source.id }}"
|
|
18
19
|
hx-target="#topology-modal-content" data-bs-toggle="modal" data-bs-target="#topology-modal"
|
|
19
20
|
class="dropdown-item">
|
|
20
21
|
Previous Snapshot
|
|
21
|
-
</a
|
|
22
|
+
</a>
|
|
23
|
+
</li>
|
|
24
|
+
{% else %}
|
|
25
|
+
<li>
|
|
26
|
+
<span class="dropdown-item text-danger">No IP Fabric Source Found for this Site</span>
|
|
27
|
+
</li>
|
|
28
|
+
{% endif %}
|
|
22
29
|
</ul>
|
|
23
30
|
</div>
|
|
24
31
|
</div>
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{% extends 'base/layout.html' %}
|
|
2
|
+
{% load buttons %}
|
|
3
|
+
{% load helpers %}
|
|
4
|
+
{% load perms %}
|
|
5
|
+
|
|
6
|
+
{% block title %}Ingestion{% endblock %}
|
|
7
|
+
|
|
8
|
+
{% block tabs %}
|
|
9
|
+
<ul class="nav nav-tabs px-3">
|
|
10
|
+
<li class="nav-item" role="presentation">
|
|
11
|
+
<a class="nav-link active" role="tab">Ingestion</a>
|
|
12
|
+
</li>
|
|
13
|
+
</ul>
|
|
14
|
+
{% endblock tabs %}
|
|
15
|
+
|
|
16
|
+
{% block controls %}
|
|
17
|
+
<div class="controls">
|
|
18
|
+
<div class="control-group">
|
|
19
|
+
{% block extra_controls %}{% endblock %}
|
|
20
|
+
{% add_button model %}
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
{% endblock controls %}
|
|
24
|
+
|
|
25
|
+
{% block content %}
|
|
26
|
+
<div class="tab-content">
|
|
27
|
+
{% for sync in syncs %}
|
|
28
|
+
<div class="card">
|
|
29
|
+
<h5 class="card-header d-flex justify-content-between" id="module{{ module.pk }}">
|
|
30
|
+
<div>
|
|
31
|
+
<i class="mdi mdi-cloud-sync"></i><i class="mdi mdi-sync"></i> <a href="{% url 'plugins:ipfabric_netbox:ipfabricsync' pk=sync.pk %}">{{ sync.name}}</a>
|
|
32
|
+
</div>
|
|
33
|
+
{% if perms.ipfabric_netbox.delete_ipfabricsync %}
|
|
34
|
+
<a href="{% url 'plugins:ipfabric_netbox:ipfabricsync_delete' pk=sync.pk %}" class="btn btn-danger btn-sm">
|
|
35
|
+
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
|
|
36
|
+
</a>
|
|
37
|
+
{% endif %}
|
|
38
|
+
|
|
39
|
+
</h5>
|
|
40
|
+
<div class="card-body">
|
|
41
|
+
{% include 'inc/sync_warning.html' with object=module %}
|
|
42
|
+
<table class="table table-hover table-headings reports">
|
|
43
|
+
<thead>
|
|
44
|
+
<tr>
|
|
45
|
+
<th width="200">Source</th>
|
|
46
|
+
<th width="400">Snapshot</th>
|
|
47
|
+
<th>Status</th>
|
|
48
|
+
<th>Last Run</th>
|
|
49
|
+
</tr>
|
|
50
|
+
</thead>
|
|
51
|
+
<tbody>
|
|
52
|
+
<tr>
|
|
53
|
+
<td><a href="{% url 'plugins:ipfabric_netbox:ipfabricsource' pk=sync.snapshot_data.source.pk %}">{{ sync.snapshot_data.source.name }}</a></td>
|
|
54
|
+
<td><a href="{% url 'plugins:ipfabric_netbox:ipfabricsnapshot' pk=sync.snapshot_data.pk %}">{{ sync.snapshot_data.name}}</a></td>
|
|
55
|
+
<td>{% badge sync.get_status_display last_job.get_status_color %}</td>
|
|
56
|
+
<td>{{sync.last_synced}}</td>
|
|
57
|
+
</tr>
|
|
58
|
+
</tbody>
|
|
59
|
+
</table>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
{% empty %}
|
|
63
|
+
<div class="alert alert-info" role="alert">
|
|
64
|
+
<h4 class="alert-heading">Sync Jobs Settings Found</h4>
|
|
65
|
+
{% if perms.extras.add_reportmodule %}
|
|
66
|
+
Get started by <a href="{% url 'plugins:ipfabric_netbox:ipfabricsync_add' %}">creating a sync</a> from an IP Fabric source.
|
|
67
|
+
{% endif %}
|
|
68
|
+
</div>
|
|
69
|
+
{% endfor %}
|
|
70
|
+
</div>
|
|
71
|
+
{% endblock content %}
|
|
@@ -8,7 +8,6 @@ from django.test import TestCase
|
|
|
8
8
|
from django.utils import timezone
|
|
9
9
|
|
|
10
10
|
from ipfabric_netbox.choices import IPFabricSnapshotStatusModelChoices
|
|
11
|
-
from ipfabric_netbox.models import IPFabricIngestion
|
|
12
11
|
from ipfabric_netbox.models import IPFabricSnapshot
|
|
13
12
|
from ipfabric_netbox.models import IPFabricSource
|
|
14
13
|
from ipfabric_netbox.models import IPFabricSync
|
|
@@ -119,9 +118,9 @@ class IPFabricTransformMapModelTestCase(TestCase):
|
|
|
119
118
|
)
|
|
120
119
|
sync = IPFabricSync.objects.create(
|
|
121
120
|
name="ingest",
|
|
121
|
+
type="dcim",
|
|
122
122
|
status="new",
|
|
123
123
|
snapshot_data=snapshot,
|
|
124
|
-
update_custom_fields=True,
|
|
125
124
|
parameters={
|
|
126
125
|
"vrf": False,
|
|
127
126
|
"site": True,
|
|
@@ -139,8 +138,6 @@ class IPFabricTransformMapModelTestCase(TestCase):
|
|
|
139
138
|
},
|
|
140
139
|
)
|
|
141
140
|
|
|
142
|
-
ingestion = IPFabricIngestion.objects.create(sync=sync)
|
|
143
|
-
|
|
144
141
|
runner = IPFabricSyncRunner(
|
|
145
142
|
settings={
|
|
146
143
|
"site": True,
|
|
@@ -155,11 +152,7 @@ class IPFabricTransformMapModelTestCase(TestCase):
|
|
|
155
152
|
"snapshot_id": "12dd8c61-129c-431a-b98b-4c9211571f89",
|
|
156
153
|
},
|
|
157
154
|
sync=sync,
|
|
158
|
-
ingestion=ingestion,
|
|
159
155
|
)
|
|
160
|
-
# Need to monkeypatch since we are not in active Branch (different schema)
|
|
161
|
-
# Using default schema "default" here since we are in "test_netbox" DB
|
|
162
|
-
runner.get_db_connection_name = lambda: "default"
|
|
163
156
|
|
|
164
157
|
site_data = {
|
|
165
158
|
"siteName": "MPLS",
|
|
@@ -173,9 +166,7 @@ class IPFabricTransformMapModelTestCase(TestCase):
|
|
|
173
166
|
"networksCount": 6,
|
|
174
167
|
}
|
|
175
168
|
|
|
176
|
-
self.site = runner.
|
|
177
|
-
item=site_data, app_label="dcim", model="site", cf=sync.update_custom_fields
|
|
178
|
-
)
|
|
169
|
+
self.site = runner.get_model_or_update("dcim", "site", site_data)
|
|
179
170
|
|
|
180
171
|
device_data = {
|
|
181
172
|
"id": "961251111",
|
|
@@ -213,36 +204,14 @@ class IPFabricTransformMapModelTestCase(TestCase):
|
|
|
213
204
|
"slug": None,
|
|
214
205
|
}
|
|
215
206
|
|
|
216
|
-
self.mf_obj = runner.
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
)
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
app_label="dcim",
|
|
225
|
-
model="devicetype",
|
|
226
|
-
cf=sync.update_custom_fields,
|
|
227
|
-
)
|
|
228
|
-
self.platform = runner.sync_item(
|
|
229
|
-
item=device_data,
|
|
230
|
-
app_label="dcim",
|
|
231
|
-
model="platform",
|
|
232
|
-
cf=sync.update_custom_fields,
|
|
233
|
-
)
|
|
234
|
-
self.role = runner.sync_item(
|
|
235
|
-
item=device_data,
|
|
236
|
-
app_label="dcim",
|
|
237
|
-
model="devicerole",
|
|
238
|
-
cf=sync.update_custom_fields,
|
|
239
|
-
)
|
|
240
|
-
self.device_object = runner.sync_item(
|
|
241
|
-
item=device_data,
|
|
242
|
-
app_label="dcim",
|
|
243
|
-
model="device",
|
|
244
|
-
cf=sync.update_custom_fields,
|
|
245
|
-
)
|
|
207
|
+
self.mf_obj = runner.get_model_or_update("dcim", "manufacturer", device_data)
|
|
208
|
+
self.dt_obj = runner.get_model_or_update("dcim", "devicetype", device_data)
|
|
209
|
+
|
|
210
|
+
self.platform = runner.get_model_or_update("dcim", "platform", device_data)
|
|
211
|
+
|
|
212
|
+
self.role = runner.get_model_or_update("dcim", "devicerole", device_data)
|
|
213
|
+
|
|
214
|
+
self.device_object = runner.get_model_or_update("dcim", "device", device_data)
|
|
246
215
|
|
|
247
216
|
def test_transform_map(self):
|
|
248
217
|
site_transform_map = IPFabricTransformMap.objects.get(name="Site Transform Map")
|
|
@@ -414,10 +383,5 @@ class IPFabricTransformMapModelTestCase(TestCase):
|
|
|
414
383
|
)
|
|
415
384
|
transform_field.template = "{{ object.hostname }} - test"
|
|
416
385
|
transform_field.save()
|
|
417
|
-
device_object = runner.
|
|
418
|
-
item=device_data,
|
|
419
|
-
app_label="dcim",
|
|
420
|
-
model="device",
|
|
421
|
-
cf=sync.update_custom_fields,
|
|
422
|
-
)
|
|
386
|
+
device_object = runner.get_model_or_update("dcim", "device", device_data)
|
|
423
387
|
self.assertEqual(device_object.name, "L21PE152 - test")
|
|
@@ -589,35 +589,6 @@ class IPFabricSyncRunner(object):
|
|
|
589
589
|
|
|
590
590
|
return instance
|
|
591
591
|
|
|
592
|
-
def sync_item(
|
|
593
|
-
self,
|
|
594
|
-
item,
|
|
595
|
-
app_label: str,
|
|
596
|
-
model: str,
|
|
597
|
-
cf: bool = False,
|
|
598
|
-
ingestion: "IPFabricIngestion" = None,
|
|
599
|
-
) -> ModelTypeVar | None:
|
|
600
|
-
"""Sync a single item to NetBox."""
|
|
601
|
-
synced_object = self.sync_model(
|
|
602
|
-
app_label=app_label,
|
|
603
|
-
model=model,
|
|
604
|
-
data=item,
|
|
605
|
-
sync=self.settings.get(model),
|
|
606
|
-
)
|
|
607
|
-
if synced_object is None:
|
|
608
|
-
return None
|
|
609
|
-
|
|
610
|
-
if cf:
|
|
611
|
-
synced_object.snapshot()
|
|
612
|
-
synced_object.custom_field_data[
|
|
613
|
-
"ipfabric_source"
|
|
614
|
-
] = self.sync.snapshot_data.source.pk
|
|
615
|
-
if ingestion:
|
|
616
|
-
synced_object.custom_field_data["ipfabric_ingestion"] = ingestion.pk
|
|
617
|
-
synced_object.save()
|
|
618
|
-
|
|
619
|
-
return synced_object
|
|
620
|
-
|
|
621
592
|
def sync_items(
|
|
622
593
|
self,
|
|
623
594
|
items,
|
|
@@ -634,7 +605,23 @@ class IPFabricSyncRunner(object):
|
|
|
634
605
|
return
|
|
635
606
|
|
|
636
607
|
for item in items:
|
|
637
|
-
self.
|
|
608
|
+
synced_object = self.sync_model(
|
|
609
|
+
app_label=app_label,
|
|
610
|
+
model=model,
|
|
611
|
+
data=item,
|
|
612
|
+
sync=self.settings.get(model),
|
|
613
|
+
)
|
|
614
|
+
if synced_object is None:
|
|
615
|
+
continue
|
|
616
|
+
|
|
617
|
+
if cf:
|
|
618
|
+
synced_object.snapshot()
|
|
619
|
+
synced_object.custom_field_data[
|
|
620
|
+
"ipfabric_source"
|
|
621
|
+
] = self.sync.snapshot_data.source.pk
|
|
622
|
+
if ingestion:
|
|
623
|
+
synced_object.custom_field_data["ipfabric_ingestion"] = ingestion.pk
|
|
624
|
+
synced_object.save()
|
|
638
625
|
|
|
639
626
|
@handle_errors
|
|
640
627
|
def sync_devices(
|
|
@@ -697,14 +684,11 @@ class IPFabricSyncRunner(object):
|
|
|
697
684
|
|
|
698
685
|
if device_object and self.settings.get("device"):
|
|
699
686
|
device_object.snapshot()
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
device_object.custom_field_data[
|
|
706
|
-
"ipfabric_ingestion"
|
|
707
|
-
] = ingestion.pk
|
|
687
|
+
device_object.custom_field_data[
|
|
688
|
+
"ipfabric_source"
|
|
689
|
+
] = self.sync.snapshot_data.source.pk
|
|
690
|
+
if ingestion:
|
|
691
|
+
device_object.custom_field_data["ipfabric_ingestion"] = ingestion.pk
|
|
708
692
|
device_object.save()
|
|
709
693
|
|
|
710
694
|
self.logger.increment_statistics(model="device")
|
|
@@ -845,11 +829,7 @@ class IPFabricSyncRunner(object):
|
|
|
845
829
|
) = self.collect_data()
|
|
846
830
|
|
|
847
831
|
self.sync_items(
|
|
848
|
-
app_label="dcim",
|
|
849
|
-
model="site",
|
|
850
|
-
items=sites,
|
|
851
|
-
cf=self.sync.update_custom_fields,
|
|
852
|
-
ingestion=ingestion,
|
|
832
|
+
app_label="dcim", model="site", items=sites, cf=True, ingestion=ingestion
|
|
853
833
|
)
|
|
854
834
|
self.sync_devices(
|
|
855
835
|
ingestion,
|
ipfabric_netbox/views.py
CHANGED
|
@@ -37,7 +37,6 @@ from .filtersets import IPFabricIngestionFilterSet
|
|
|
37
37
|
from .filtersets import IPFabricIngestionIssueFilterSet
|
|
38
38
|
from .filtersets import IPFabricSnapshotFilterSet
|
|
39
39
|
from .filtersets import IPFabricSourceFilterSet
|
|
40
|
-
from .filtersets import IPFabricSyncFilterSet
|
|
41
40
|
from .filtersets import IPFabricTransformMapFilterSet
|
|
42
41
|
from .filtersets import IPFabricTransformMapGroupFilterSet
|
|
43
42
|
from .forms import IPFabricIngestionFilterForm
|
|
@@ -70,7 +69,6 @@ from .tables import IPFabricIngestionTable
|
|
|
70
69
|
from .tables import IPFabricRelationshipFieldTable
|
|
71
70
|
from .tables import IPFabricSnapshotTable
|
|
72
71
|
from .tables import IPFabricSourceTable
|
|
73
|
-
from .tables import IPFabricSyncTable
|
|
74
72
|
from .tables import IPFabricTransformFieldTable
|
|
75
73
|
from .tables import IPFabricTransformMapGroupTable
|
|
76
74
|
from .tables import IPFabricTransformMapTable
|
|
@@ -530,10 +528,14 @@ class IPFabricSourceBulkDeleteView(generic.BulkDeleteView):
|
|
|
530
528
|
|
|
531
529
|
|
|
532
530
|
# Sync
|
|
533
|
-
class IPFabricSyncListView(
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
531
|
+
class IPFabricSyncListView(View):
|
|
532
|
+
def get(self, request):
|
|
533
|
+
syncs = IPFabricSync.objects.prefetch_related("snapshot_data")
|
|
534
|
+
return render(
|
|
535
|
+
request,
|
|
536
|
+
"ipfabric_netbox/ipfabricsync_list.html",
|
|
537
|
+
{"model": IPFabricSync, "syncs": syncs},
|
|
538
|
+
)
|
|
537
539
|
|
|
538
540
|
|
|
539
541
|
@register_model_view(IPFabricSync, "edit")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: ipfabric_netbox
|
|
3
|
-
Version: 4.2.
|
|
3
|
+
Version: 4.2.1
|
|
4
4
|
Summary: NetBox plugin to sync IP Fabric data into NetBox
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: netbox,ipfabric,plugin,sync
|
|
@@ -18,11 +18,11 @@ Provides-Extra: ipfabric-6-10
|
|
|
18
18
|
Provides-Extra: ipfabric-7-0
|
|
19
19
|
Provides-Extra: ipfabric-7-2
|
|
20
20
|
Provides-Extra: ipfabric-7-3
|
|
21
|
-
Requires-Dist: ipfabric (>=6.10.0,<6.11.0) ; extra == "ipfabric_6_10" and extra != "ipfabric_7_0" and extra != "ipfabric_7_2"
|
|
22
|
-
Requires-Dist: ipfabric (>=6.6.4) ; extra != "ipfabric_6_10" and extra != "ipfabric_7_0" and extra != "ipfabric_7_2"
|
|
23
|
-
Requires-Dist: ipfabric (>=7.0.0,<7.1.0) ; extra != "ipfabric_6_10" and extra == "ipfabric_7_0" and extra != "ipfabric_7_2"
|
|
24
|
-
Requires-Dist: ipfabric (>=7.2.0,<7.3.0) ; extra
|
|
25
|
-
Requires-Dist: ipfabric (>=7.3.0,<7.4.0) ; extra
|
|
21
|
+
Requires-Dist: ipfabric (>=6.10.0,<6.11.0) ; extra == "ipfabric_6_10" and extra != "ipfabric_7_0" and extra != "ipfabric_7_2"
|
|
22
|
+
Requires-Dist: ipfabric (>=6.6.4) ; extra != "ipfabric_6_10" and extra != "ipfabric_7_0" and extra != "ipfabric_7_2"
|
|
23
|
+
Requires-Dist: ipfabric (>=7.0.0,<7.1.0) ; extra != "ipfabric_6_10" and extra == "ipfabric_7_0" and extra != "ipfabric_7_2"
|
|
24
|
+
Requires-Dist: ipfabric (>=7.2.0,<7.3.0) ; extra == "ipfabric-6-10" or extra == "ipfabric-7-0" or extra == "ipfabric-7-2" or extra == "ipfabric-7-3"
|
|
25
|
+
Requires-Dist: ipfabric (>=7.3.0,<7.4.0) ; extra == "ipfabric-6-10" or extra == "ipfabric-7-0" or extra == "ipfabric-7-2" or extra == "ipfabric-7-3"
|
|
26
26
|
Requires-Dist: netboxlabs-netbox-branching (>=0.5.5,<0.6.0)
|
|
27
27
|
Requires-Dist: netutils
|
|
28
28
|
Project-URL: Bug Tracker, https://gitlab.com/ip-fabric/integrations/ipfabric-netbox-sync/-/issues
|