ipfabric_netbox 4.3.0b7__py3-none-any.whl → 4.3.0b9__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.
@@ -6,7 +6,7 @@ class NetboxIPFabricConfig(PluginConfig):
6
6
  name = "ipfabric_netbox"
7
7
  verbose_name = "NetBox IP Fabric SoT Plugin"
8
8
  description = "Sync IP Fabric into NetBox"
9
- version = "4.3.0b7"
9
+ version = "4.3.0b9"
10
10
  base_url = "ipfabric"
11
11
  min_version = "4.4.0"
12
12
 
ipfabric_netbox/jobs.py CHANGED
@@ -4,7 +4,6 @@ from datetime import timedelta
4
4
  from core.choices import DataSourceStatusChoices
5
5
  from core.choices import JobStatusChoices
6
6
  from core.exceptions import SyncError
7
- from core.models import Job
8
7
  from netbox.context_managers import event_tracking
9
8
  from rq.timeouts import JobTimeoutException
10
9
  from utilities.datetime import local_now
@@ -36,15 +35,15 @@ def sync_ipfabricsource(job, *args, **kwargs):
36
35
 
37
36
 
38
37
  def sync_ipfabric(job, *args, **kwargs):
39
- obj = IPFabricSync.objects.get(pk=job.object_id)
38
+ sync = IPFabricSync.objects.get(pk=job.object_id)
40
39
 
41
40
  try:
42
41
  job.start()
43
- obj.sync(job=job)
42
+ sync.sync(job=job)
44
43
  job.terminate()
45
44
  except Exception as e:
46
45
  job.terminate(status=JobStatusChoices.STATUS_ERRORED)
47
- IPFabricSync.objects.filter(pk=obj.pk).update(
46
+ IPFabricSync.objects.filter(pk=sync.pk).update(
48
47
  status=DataSourceStatusChoices.FAILED
49
48
  )
50
49
  if type(e) in (SyncError, JobTimeoutException):
@@ -52,15 +51,38 @@ def sync_ipfabric(job, *args, **kwargs):
52
51
  else:
53
52
  raise e
54
53
  finally:
55
- if obj.interval and not kwargs.get("adhoc"):
56
- new_scheduled_time = local_now() + timedelta(minutes=obj.interval)
57
- job = Job.enqueue(
58
- sync_ipfabric,
59
- name=f"{obj.name} - (scheduled)",
60
- instance=obj,
61
- user=obj.user,
62
- schedule_at=new_scheduled_time,
63
- interval=obj.interval,
54
+ if sync.interval and not kwargs.get("adhoc"):
55
+ new_scheduled_time = local_now() + timedelta(minutes=sync.interval)
56
+ # We want to create new Job only if scheduled time was before this Job started
57
+ # The current sync might have been changed while this job was running
58
+ sync.refresh_from_db()
59
+ if not sync.scheduled or (sync.scheduled and sync.scheduled > job.started):
60
+ logger.info(
61
+ f"Not scheduling a new job for IPFabricSync {sync.pk} as the scheduled time was changed while the job was running."
62
+ )
63
+ return
64
+ # Update the sync object with the new scheduled time
65
+ # This also triggers the creation of a new Job
66
+ # Running in fake request context to ensure user is set for changelog
67
+ request = NetBoxFakeRequest(
68
+ {
69
+ "META": {},
70
+ "POST": sync.parameters,
71
+ "GET": {},
72
+ "FILES": {},
73
+ "user": sync.user,
74
+ "path": "",
75
+ "id": job.job_id,
76
+ }
77
+ )
78
+
79
+ with event_tracking(request):
80
+ sync.scheduled = new_scheduled_time
81
+ sync.status = DataSourceStatusChoices.QUEUED
82
+ sync.full_clean()
83
+ sync.save()
84
+ logger.info(
85
+ f"Scheduled next sync for IPFabricSync {sync.pk} at {new_scheduled_time}."
64
86
  )
65
87
 
66
88
 
@@ -0,0 +1,70 @@
1
+ from datetime import timedelta
2
+ from typing import TYPE_CHECKING
3
+
4
+ from django.db import migrations
5
+ from utilities.datetime import local_now
6
+
7
+ if TYPE_CHECKING:
8
+ from django.apps import apps as apps_type
9
+ from django.db.backends.base.schema import BaseDatabaseSchemaEditor
10
+
11
+
12
+ def clean_scheduled_jobs(
13
+ apps: "apps_type", schema_editor: "BaseDatabaseSchemaEditor"
14
+ ) -> None:
15
+ Job = apps.get_model("core", "Job")
16
+ IPFabricSync = apps.get_model("ipfabric_netbox", "IPFabricSync")
17
+ ObjectType = apps.get_model("core", "ObjectType")
18
+
19
+ for sync in IPFabricSync.objects.all():
20
+ try:
21
+ scheduled_jobs = Job.objects.filter(
22
+ object_id=sync.id,
23
+ object_type=ObjectType.objects.get_for_model(sync),
24
+ scheduled__isnull=False,
25
+ ).order_by("scheduled")
26
+ if not scheduled_jobs.exists():
27
+ continue
28
+ if not sync.scheduled:
29
+ # Delete all scheduled jobs if the sync is not scheduled
30
+ scheduled_jobs.delete()
31
+ continue
32
+ if scheduled_jobs.count() == 1:
33
+ # Only one scheduled job exists, let's update scheduled time on the sync object
34
+ # This does not create a new job since sync is a Faked object in migration
35
+ sync.scheduled = scheduled_jobs.first().scheduled
36
+ sync.full_clean()
37
+ sync.save()
38
+ continue
39
+ # More than one scheduled job exists
40
+ # Find the one that is closest to scheduled + N * interval
41
+ interval = timedelta(minutes=sync.interval)
42
+ elapsed = local_now() - sync.scheduled
43
+ intervals_passed = (elapsed // interval) + 1
44
+ closest_future_scheduled = sync.scheduled + intervals_passed * interval
45
+ closest_job = min(
46
+ scheduled_jobs,
47
+ key=lambda job: abs(job.scheduled - closest_future_scheduled),
48
+ )
49
+ for job in scheduled_jobs:
50
+ if job != closest_job:
51
+ job.delete()
52
+ sync.scheduled = closest_job.scheduled
53
+ sync.full_clean()
54
+ sync.save()
55
+ except Exception as err:
56
+ # Always be safe inside a migration
57
+ print(f"Error cleaning scheduled jobs for IPFabricSync {sync.id}: {err}")
58
+
59
+
60
+ class Migration(migrations.Migration):
61
+ dependencies = [
62
+ (
63
+ "ipfabric_netbox",
64
+ "0019_alter_ipfabrictransformmap_options_and_more",
65
+ ),
66
+ ]
67
+
68
+ operations = [
69
+ migrations.RunPython(clean_scheduled_jobs, migrations.RunPython.noop),
70
+ ]
ipfabric_netbox/models.py CHANGED
@@ -8,6 +8,7 @@ from uuid import uuid4
8
8
 
9
9
  import httpx
10
10
  from core.choices import DataSourceStatusChoices
11
+ from core.choices import JobStatusChoices
11
12
  from core.exceptions import SyncError
12
13
  from core.models import Job
13
14
  from core.models import ObjectType
@@ -710,32 +711,56 @@ class IPFabricSync(IPFabricClient, JobsMixin, TagsMixin, ChangeLoggedModel):
710
711
  pk__in=[tm.pk for tm in maps_by_target.values()]
711
712
  )
712
713
 
713
- def enqueue_sync_job(self, adhoc=False, user=None):
714
- # Set the status to "syncing"
715
- self.status = DataSourceStatusChoices.QUEUED
716
- IPFabricSync.objects.filter(pk=self.pk).update(status=self.status)
717
-
718
- Job.enqueue(
719
- import_string("ipfabric_netbox.jobs.sync_ipfabricsource"),
720
- name=f"{self.name} Snapshot Sync (Pre Ingestion)",
721
- instance=self.snapshot_data.source,
722
- user=self.user,
723
- )
714
+ def delete_scheduled_jobs(self) -> None:
715
+ Job.objects.filter(
716
+ object_type=ObjectType.objects.get_for_model(self),
717
+ object_id=self.pk,
718
+ status__in=[
719
+ JobStatusChoices.STATUS_PENDING,
720
+ JobStatusChoices.STATUS_SCHEDULED,
721
+ ],
722
+ ).delete()
723
+
724
+ def enqueue_sync_job(self, adhoc=False, user=None) -> Job | None:
725
+ def set_syncing_status():
726
+ self.status = DataSourceStatusChoices.QUEUED
727
+ IPFabricSync.objects.filter(pk=self.pk).update(status=self.status)
728
+
729
+ def sync_snapshots():
730
+ Job.enqueue(
731
+ import_string("ipfabric_netbox.jobs.sync_ipfabricsource"),
732
+ name=f"{self.name} Snapshot Sync (Pre Ingestion)",
733
+ instance=self.snapshot_data.source,
734
+ user=self.user,
735
+ )
724
736
 
725
737
  # Enqueue a sync job
726
738
  if not user:
727
739
  user = self.user
728
740
 
729
- if not adhoc and self.scheduled:
730
- job = Job.enqueue(
731
- import_string("ipfabric_netbox.jobs.sync_ipfabric"),
732
- name=f"{self.name} - (scheduled)",
733
- instance=self,
734
- user=self.user,
735
- schedule_at=self.scheduled,
736
- interval=self.interval,
737
- )
738
- elif adhoc:
741
+ if not adhoc:
742
+ if self.scheduled:
743
+ # We want to schedule a recurring Job
744
+ # We need to replace the old scheduled Job to make sure it has current context
745
+ self.delete_scheduled_jobs()
746
+ set_syncing_status()
747
+ sync_snapshots()
748
+ job = Job.enqueue(
749
+ import_string("ipfabric_netbox.jobs.sync_ipfabric"),
750
+ name=f"{self.name} - (scheduled)",
751
+ instance=self,
752
+ user=self.user,
753
+ schedule_at=self.scheduled,
754
+ interval=self.interval,
755
+ )
756
+ else:
757
+ # There should be no scheduled Job anymore, clean it up
758
+ self.delete_scheduled_jobs()
759
+ job = None
760
+ else:
761
+ # Start adhoc job immediately
762
+ set_syncing_status()
763
+ sync_snapshots()
739
764
  job = Job.enqueue(
740
765
  import_string("ipfabric_netbox.jobs.sync_ipfabric"),
741
766
  instance=self,
@@ -872,7 +897,7 @@ class IPFabricSync(IPFabricClient, JobsMixin, TagsMixin, ChangeLoggedModel):
872
897
  self.logger.log_info("Auto Merging Ingestion", obj=ingestion)
873
898
  logger.info("Auto Merging Ingestion")
874
899
  try:
875
- ingestion.enqueue_merge_job(user=user)
900
+ ingestion.enqueue_merge_job(user=user, remove_branch=True)
876
901
  self.logger.log_info("Auto Merge Job Enqueued", obj=ingestion)
877
902
  logger.info("Auto Merge Job Enqueued")
878
903
  except NameError:
@@ -937,6 +962,9 @@ class IPFabricIngestion(JobsMixin, models.Model):
937
962
  )
938
963
 
939
964
  def get_logs(self):
965
+ if not self.job:
966
+ # The Job is deleted by manual action
967
+ return {}
940
968
  if self.job.data:
941
969
  job_results = self.job.data
942
970
  else:
@@ -15,7 +15,7 @@
15
15
  {% custom_links object %}
16
16
  </div>
17
17
  {% if perms.core.sync_datasource %}
18
- {% if object.sync.ready_for_sync %}
18
+ {% if object.sync.ready_for_sync and object.branch and object.branch.status != "merged" %}
19
19
  <a href="#"
20
20
  hx-get="{% url 'plugins:ipfabric_netbox:ipfabricingestion_merge' pk=object.pk %}"
21
21
  hx-target="#htmx-modal-content"
@@ -69,7 +69,7 @@
69
69
  </tr>
70
70
  <tr>
71
71
  <th scope="row">Job Object</th>
72
- <td><a href="{% url 'core:job' pk=object.job.pk %}">{{ object.job }}</a></td>
72
+ <td>{% if object.job %}<a href="{% url 'core:job' pk=object.job.pk %}">{{ object.job }}</a>{% else %}{{ ""|placeholder }}{% endif %}</td>
73
73
  </tr>
74
74
  <tr>
75
75
  <th scope="row">Ingestion Status</th>
@@ -96,14 +96,14 @@
96
96
  {% include 'ipfabric_netbox/partials/ingestion_statistics.html' with object=object %}
97
97
  </div>
98
98
  <div id="ingestion_progress" hx-swap-oob="true">
99
- {% include 'ipfabric_netbox/partials/ingestion_progress.html' %}
99
+ {% include 'ipfabric_netbox/partials/ingestion_progress.html' with job=object.job %}
100
100
  </div>
101
101
  <!-- {% include 'inc/panels/related_objects.html' %} -->
102
102
  {% include 'inc/panels/custom_fields.html' %}
103
103
  {% plugin_right_page object %}
104
104
  </div>
105
105
  <div class="row mb-3">
106
- <div class="col col-md-12" {% if not object.job.completed %} hx-get="{% url 'plugins:ipfabric_netbox:ipfabricingestion_logs' pk=object.pk %}?type=info"
106
+ <div class="col col-md-12" {% if object.job and not object.job.completed %} hx-get="{% url 'plugins:ipfabric_netbox:ipfabricingestion_logs' pk=object.pk %}?type=info"
107
107
  hx-trigger="every 5s" hx-target="#ingestion_logs" hx-select="#ingestion_logs" hx-select-oob="#ingestion_status:innerHTML,#ingestion_progress:innerHTML,#ingestion_statistics:innerHTML,#object_tabs:innerHTML"
108
108
  hx-swap="innerHTML" {% endif %}>
109
109
  <div id="ingestion_logs">{% include 'ipfabric_netbox/partials/job_logs.html' with job=object.job %}</div>
@@ -8,7 +8,7 @@
8
8
  {% include 'ipfabric_netbox/partials/ingestion_status.html' with object=object %}
9
9
  </div>
10
10
  <div id="ingestion_progress" hx-swap-oob="true">
11
- {% include 'ipfabric_netbox/partials/ingestion_progress.html' %}
11
+ {% include 'ipfabric_netbox/partials/ingestion_progress.html' with job=job %}
12
12
  </div>
13
13
  <div id="ingestion_logs">
14
14
  {% include 'ipfabric_netbox/partials/job_logs.html' with job_results=job_results job=job %}
@@ -2,7 +2,9 @@
2
2
  <div class="card">
3
3
  <h5 class="card-header">Progress</h5>
4
4
  <div class="card-body">
5
- {% if statistics %}
5
+ {% if not job %}
6
+ Cannot show progress, no sync Job exists anymore.
7
+ {% elif statistics %}
6
8
  <table class="table table-hover attr-table">
7
9
  {% for model, util in statistics.items %}
8
10
  <tr>
@@ -2,20 +2,20 @@
2
2
  {% load helpers %}
3
3
 
4
4
  <p>
5
- {% if job.started %}
6
- Started: <strong>{{ job.started|isodatetime }}</strong>
7
- {% elif job.scheduled %}
8
- Scheduled for: <strong>{{ job.scheduled|isodatetime }}</strong> ({{ job.scheduled|naturaltime }})
9
- {% else %}
10
- Created: <strong>{{ job.created|isodatetime }}</strong>
11
- {% endif %}
12
- {% if job.completed %}
13
- Duration: <strong>{{ job.duration }}</strong>
14
- {% endif %}
5
+ {% if job.started %}
6
+ Started: <strong>{{ job.started|isodatetime }}</strong>
7
+ {% elif job.scheduled %}
8
+ Scheduled for: <strong>{{ job.scheduled|isodatetime }}</strong> ({{ job.scheduled|naturaltime }})
9
+ {% elif job %}
10
+ Created: <strong>{{ job.created|isodatetime }}</strong>
11
+ {% endif %}
12
+ {% if job.completed %}
13
+ Duration: <strong>{{ job.duration }}</strong>
14
+ {% endif %}
15
15
  <span id="pending-result-label">{% badge job.get_status_display job.get_status_color %}</span>
16
16
  </p>
17
- {% if not job.completed %}
18
- {% include 'ipfabric_netbox/inc/logs_pending.html' %}
17
+ {% if job and not job.completed %}
18
+ {% include 'ipfabric_netbox/inc/logs_pending.html' %}
19
19
  {% endif %}
20
20
  <div class="card">
21
21
  <h5 class="card-header">Sync Results</h5>
@@ -719,7 +719,7 @@ class IPFabricSyncTestCase(
719
719
 
720
720
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
721
721
  def test_get_htmx_request(self):
722
- instance = self._get_queryset().first()
722
+ instance = self._get_queryset().last()
723
723
  # Try GET with HTMX
724
724
  response = self.client.get(
725
725
  instance.get_absolute_url(), **{"HTTP_HX-Request": "true"}
@@ -1628,7 +1628,7 @@ class IPFabricIngestionTestCase(
1628
1628
  self.assertHttpStatus(response, 200)
1629
1629
 
1630
1630
  # Verify the response contains expected elements
1631
- self.assertContains(response, "Logs pending...")
1631
+ self.assertContains(response, "Ingestion progress pending...")
1632
1632
 
1633
1633
  @override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
1634
1634
  def test_get_logs_htmx(self):
ipfabric_netbox/views.py CHANGED
@@ -938,7 +938,7 @@ class IPFabricIngestionLogView(LoginRequiredMixin, View):
938
938
  ingestion = annotate_statistics(IPFabricIngestion.objects).get(pk=ingestion_id)
939
939
  data = ingestion.get_statistics()
940
940
  data["object"] = ingestion
941
- data["job"] = ingestion.jobs.first()
941
+ data["job"] = ingestion.job
942
942
 
943
943
  if request.htmx:
944
944
  response = render(
@@ -946,7 +946,7 @@ class IPFabricIngestionLogView(LoginRequiredMixin, View):
946
946
  self.template_name,
947
947
  data,
948
948
  )
949
- if ingestion.job.completed:
949
+ if not ingestion.job or ingestion.job.completed:
950
950
  response["HX-Refresh"] = "true"
951
951
  return response
952
952
  return render(request, self.template_name, data)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ipfabric_netbox
3
- Version: 4.3.0b7
3
+ Version: 4.3.0b9
4
4
  Summary: NetBox plugin to sync IP Fabric data into NetBox
5
5
  License: MIT
6
6
  Keywords: netbox,ipfabric,plugin,sync
@@ -1,4 +1,4 @@
1
- ipfabric_netbox/__init__.py,sha256=p6HeR0kRnjcO4U5wSyEk81T3wJrEExUzfnzzvurzjl4,674
1
+ ipfabric_netbox/__init__.py,sha256=CgoIoRyEpMEu6R1YM-wn9Gjk3vZF8zSn7jbrjS4DDUY,674
2
2
  ipfabric_netbox/api/__init__.py,sha256=XRclTGWVR0ZhAAwgYul5Wm_loug5_hUjEumbLQEwKYM,47
3
3
  ipfabric_netbox/api/serializers.py,sha256=lr_PWG0tqAxaKtkIIm8Gx2B-tn9yENQIfKY9cvu8Cco,6581
4
4
  ipfabric_netbox/api/urls.py,sha256=1fXXVTxNY5E64Nfz6b7zXD9bZI3FcefuxAWKMe0w_QU,1240
@@ -13,7 +13,7 @@ ipfabric_netbox/graphql/enums.py,sha256=QFhwiwUKJekxQfsOGk_-70_WnkzrKEP_zIBMrin0
13
13
  ipfabric_netbox/graphql/filters.py,sha256=B8xy9r9a18vWfV6a6tHXAN1FUcoxI6MOrbsdNmzusNI,12991
14
14
  ipfabric_netbox/graphql/schema.py,sha256=5UVHA1hHRvho5eLuuS-HLXTVTbxpUUx68ovG03gumGE,3209
15
15
  ipfabric_netbox/graphql/types.py,sha256=8RxdxiA-WnoaWSzh-tUJCuZBYGmd6QjfJiJcLirRMKY,5961
16
- ipfabric_netbox/jobs.py,sha256=KrTUeCuFUIU7vKCUS3RiBYCBG7g7GzhGagM_qFMGQJ4,3089
16
+ ipfabric_netbox/jobs.py,sha256=rFQuuc_6WVZlJ2EGDDj2ml14L4JeFneSSTiKA8N1SUg,4189
17
17
  ipfabric_netbox/migrations/0001_initial.py,sha256=VphxkWL6QzWq2tcrdXlog718xQtiEGizKwS830z_fOs,13824
18
18
  ipfabric_netbox/migrations/0001_initial_squashed_0013_switch_to_branching_plugin.py,sha256=OvofuA8ImeJmjrbtCrZPcRxAUWx2Ww4DUXLBZYsy6qE,21381
19
19
  ipfabric_netbox/migrations/0002_ipfabricsnapshot_status.py,sha256=xQpouHjOutyj6riN2B592njzSvz_icpkUbo5W7nWLYw,431
@@ -34,8 +34,9 @@ ipfabric_netbox/migrations/0016_tags_and_changelog_for_snapshots.py,sha256=XqftT
34
34
  ipfabric_netbox/migrations/0017_ipfabricsync_update_custom_fields.py,sha256=IVbAL2WdigYT40sXN0A8K3HweJ_O4QqyzjB06TbkG5E,447
35
35
  ipfabric_netbox/migrations/0018_remove_type_field.py,sha256=ffxW6IS3BLCbvM5M9DbDb_x6spMmRxnV1iq8IuXxMGw,385
36
36
  ipfabric_netbox/migrations/0019_alter_ipfabrictransformmap_options_and_more.py,sha256=ieDVedt9KpJBicAiC3kdZXzHeos12N0L9EdRXKmIVgY,501
37
+ ipfabric_netbox/migrations/0020_clean_scheduled_jobs.py,sha256=zjCVKnCWTKYYkpVRwHjqRIRR2j6ALSKXYMfraRjNu7Y,2652
37
38
  ipfabric_netbox/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
- ipfabric_netbox/models.py,sha256=P2oLmXDgDswrPDMtvQcuy_inl53-RBcghoV7Uu2I3q4,37095
39
+ ipfabric_netbox/models.py,sha256=g6EnrCp714aDig7Rh3_WRe1CHIwpSMV1YLWJexUJL2U,38222
39
40
  ipfabric_netbox/navigation.py,sha256=2dEJ_wKHb52Tl0FOV1TH3JbxRe8YZ56ewrTsBFGKpCg,2210
40
41
  ipfabric_netbox/signals.py,sha256=cGa5PVD2i24pGXiVNfbu6ruIDqPVdwKQHTSWe9Ura84,1838
41
42
  ipfabric_netbox/tables.py,sha256=BpPmL6-KClX16Ol_PIADyOx9NtDR9oauXH5iO4GeSoI,9008
@@ -51,7 +52,7 @@ ipfabric_netbox/templates/ipfabric_netbox/inc/snapshotdata.html,sha256=1ItOCPjjp
51
52
  ipfabric_netbox/templates/ipfabric_netbox/inc/transform_map_field_map.html,sha256=mRU-rBweVFvaPFHbVYPw7vcYyXiVaXCOkeHm7xWdKPA,500
52
53
  ipfabric_netbox/templates/ipfabric_netbox/inc/transform_map_relationship_map.html,sha256=qyuG_EXZMGUscA3sv_6WGSrf_bR7JTRlKyMYf6EYyo4,504
53
54
  ipfabric_netbox/templates/ipfabric_netbox/ipfabric_table.html,sha256=TsF34lK2CyDsMxlFTk--2lF_0AxYM614eKmkgYAbJ-k,1629
54
- ipfabric_netbox/templates/ipfabric_netbox/ipfabricingestion.html,sha256=fm_X2FLnoTS6s6AL3WmU6p3puDojROSkPG0jA4EBQeM,4435
55
+ ipfabric_netbox/templates/ipfabric_netbox/ipfabricingestion.html,sha256=ymqTB9JEk0AFncK5kz8yStHWCD_IMNKlbgyqu_qon7g,4585
55
56
  ipfabric_netbox/templates/ipfabric_netbox/ipfabricsnapshot.html,sha256=hj8ORs_4mM_xTjmw3McHN-da5seC8nbbkzobn0f1TSc,3482
56
57
  ipfabric_netbox/templates/ipfabric_netbox/ipfabricsource.html,sha256=DQOA2TA7f1nI5YpxXthS1VzjIU1kMZus37l6bYSCauE,3869
57
58
  ipfabric_netbox/templates/ipfabric_netbox/ipfabricsync.html,sha256=dt8HYuHCzIN4otLS9QK3e1aES14isFI-1jyp8jrENXU,4616
@@ -59,11 +60,11 @@ ipfabric_netbox/templates/ipfabric_netbox/ipfabrictransformmap.html,sha256=qFo_K
59
60
  ipfabric_netbox/templates/ipfabric_netbox/ipfabrictransformmap_list.html,sha256=p8zqn0-B6mawSUM3zQrus6dsKUM5SRBTO0X94pLboX8,452
60
61
  ipfabric_netbox/templates/ipfabric_netbox/ipfabrictransformmap_restore.html,sha256=TV7gAZWtSd-c7mzOen_nv7Z8MZr2Vw8vkHP4zW9au4w,2580
61
62
  ipfabric_netbox/templates/ipfabric_netbox/ipfabrictransformmapgroup.html,sha256=H3uvjA4PdJq5uX2kizdHV1pAxwcPpyfc9IbJi1ZK5-k,975
62
- ipfabric_netbox/templates/ipfabric_netbox/partials/ingestion_all.html,sha256=W9W31m8b7mGrsfu0f_hE1TcudDqSJ0G2Bp9JE4UTIIE,662
63
- ipfabric_netbox/templates/ipfabric_netbox/partials/ingestion_progress.html,sha256=NellDFy1qzgVbCtkOZbiSi3ufau6FOLIQPUoNiU6Bg4,595
63
+ ipfabric_netbox/templates/ipfabric_netbox/partials/ingestion_all.html,sha256=ZstgG3MJbd8WAOcGUKEpt4CSjIInhF14wgVgqSc1oAs,675
64
+ ipfabric_netbox/templates/ipfabric_netbox/partials/ingestion_progress.html,sha256=tb9JAc9GjPd05F1RQdRrfrDwSQMWlcbbQOrQCXedQWs,680
64
65
  ipfabric_netbox/templates/ipfabric_netbox/partials/ingestion_statistics.html,sha256=_KtbxZQlIa_141OQSIAvSsMMfZRkNj27kJqrYHuLt7Y,867
65
66
  ipfabric_netbox/templates/ipfabric_netbox/partials/ingestion_status.html,sha256=qYXe-V-kQn1INqCHLSSpdrzXJ_IIt84AgCgAGwqpNbs,178
66
- ipfabric_netbox/templates/ipfabric_netbox/partials/job_logs.html,sha256=PMZxxrRVzn_JI8k5XCNV5BQnoFj9zYePN6afP6C_J2E,1622
67
+ ipfabric_netbox/templates/ipfabric_netbox/partials/job_logs.html,sha256=THiEs8wYMvEn8y0XGhxX7rj3w466CkDhaEzc4CxgGLY,1694
67
68
  ipfabric_netbox/templates/ipfabric_netbox/partials/object_tabs.html,sha256=3xbGy38Y1RH13bVPlcyj5L6J_FM-mOW1y0fx54hj1xE,364
68
69
  ipfabric_netbox/templates/ipfabric_netbox/partials/sync_last_ingestion.html,sha256=3v6tC88xE5cic9l9lQw7Msawoq-AW2oo2odd-osGv50,179
69
70
  ipfabric_netbox/templates/static/ipfabric_netbox/css/rack.css,sha256=z1H-RmmsqF2pGdhn5J64TMFiccy62xZUHHlCRd8fJrQ,118
@@ -74,14 +75,14 @@ ipfabric_netbox/tests/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
74
75
  ipfabric_netbox/tests/api/test_api.py,sha256=9o9k4kfnT3Zz9Hg0qnI0_XASt6pi5BCZQ3RhBjsb_iE,35215
75
76
  ipfabric_netbox/tests/test_forms.py,sha256=C8giV6E3PbMB9_864C12ebvfQ3Vlvdn39VIQQSP6GV8,61566
76
77
  ipfabric_netbox/tests/test_models.py,sha256=FFrIT5xxv_yvujKpxGjRJPNPBDF2Pqi8zbY0vxuJeQs,16043
77
- ipfabric_netbox/tests/test_views.py,sha256=6R4Ri8Y54Eg-9u5nmrWLNOp0794Y-JugcBAJGk22GEc,87771
78
+ ipfabric_netbox/tests/test_views.py,sha256=OhPoRrXA8lEzjTktjbm3I8LkByBHDXA45z9gOEspho4,87784
78
79
  ipfabric_netbox/urls.py,sha256=qF2BzZEDnPRd3opFaRfiMdaarYKFfv69iMaAbU2rsBU,2702
79
80
  ipfabric_netbox/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
80
81
  ipfabric_netbox/utilities/ipfutils.py,sha256=wFmL5oriuF-is1ZlrIcLmoeYUY5ih-CA9weRQrx5AiA,31885
81
82
  ipfabric_netbox/utilities/logging.py,sha256=GYknjocMN6LQ2873_az3y0RKm29TCXaWviUIIneH-x0,3445
82
83
  ipfabric_netbox/utilities/nbutils.py,sha256=kFBEiJOGvr_49hJWCS2duXojx2-A9kVk0Xp_vj0ohfs,2641
83
84
  ipfabric_netbox/utilities/transform_map.py,sha256=GpM_7Mm6FE0qV2qbyj4YfDn0l-JkeeEHQOZkNVSSHk4,2391
84
- ipfabric_netbox/views.py,sha256=n3tTDs1xOAY5xtcMmhM6VrnpEr-2IzqeliFNgthQf1U,44746
85
- ipfabric_netbox-4.3.0b7.dist-info/METADATA,sha256=zK1X88R7MzrYCMgp9AGN-V0DWgjSJUJwuuS-NcEYtKo,4754
86
- ipfabric_netbox-4.3.0b7.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
87
- ipfabric_netbox-4.3.0b7.dist-info/RECORD,,
85
+ ipfabric_netbox/views.py,sha256=JcXvoLo8JQ0PUyvUwi1tZQgrxw9okKBlocsIUKoGAbo,44758
86
+ ipfabric_netbox-4.3.0b9.dist-info/METADATA,sha256=HdBydWNVKafz4hTNweGmrZ5bcDsVQLGAsd1rZM-abi0,4754
87
+ ipfabric_netbox-4.3.0b9.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
88
+ ipfabric_netbox-4.3.0b9.dist-info/RECORD,,