ipfabric_netbox 4.3.0b7__tar.gz → 4.3.0b8__tar.gz

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 (88) hide show
  1. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/PKG-INFO +1 -1
  2. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/__init__.py +1 -1
  3. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/jobs.py +35 -13
  4. ipfabric_netbox-4.3.0b8/ipfabric_netbox/migrations/0020_clean_scheduled_jobs.py +70 -0
  5. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/models.py +46 -21
  6. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/tests/test_views.py +1 -1
  7. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/pyproject.toml +1 -1
  8. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/README.md +0 -0
  9. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/api/__init__.py +0 -0
  10. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/api/serializers.py +0 -0
  11. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/api/urls.py +0 -0
  12. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/api/views.py +0 -0
  13. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/choices.py +0 -0
  14. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/data/transform_map.json +0 -0
  15. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/exceptions.py +0 -0
  16. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/filtersets.py +0 -0
  17. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/forms.py +0 -0
  18. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/graphql/__init__.py +0 -0
  19. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/graphql/enums.py +0 -0
  20. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/graphql/filters.py +0 -0
  21. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/graphql/schema.py +0 -0
  22. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/graphql/types.py +0 -0
  23. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/migrations/0001_initial.py +0 -0
  24. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/migrations/0001_initial_squashed_0013_switch_to_branching_plugin.py +0 -0
  25. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/migrations/0002_ipfabricsnapshot_status.py +0 -0
  26. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/migrations/0003_ipfabricsource_type_and_more.py +0 -0
  27. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/migrations/0004_ipfabricsync_auto_merge.py +0 -0
  28. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/migrations/0005_alter_ipfabricrelationshipfield_source_model_and_more.py +0 -0
  29. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/migrations/0006_alter_ipfabrictransformmap_target_model.py +0 -0
  30. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/migrations/0007_prepare_custom_fields.py +0 -0
  31. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/migrations/0008_prepare_transform_maps.py +0 -0
  32. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/migrations/0009_transformmap_changes_for_netbox_v4_2.py +0 -0
  33. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/migrations/0010_remove_uuid_from_get_or_create.py +0 -0
  34. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/migrations/0011_update_part_number_DCIM_inventory_item_template.py +0 -0
  35. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/migrations/0012_remove_status_field.py +0 -0
  36. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/migrations/0013_switch_to_branching_plugin.py +0 -0
  37. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/migrations/0014_ipfabrictransformmapgroup_ipfabrictransformmap_group.py +0 -0
  38. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/migrations/0015_ipfabricingestionissue.py +0 -0
  39. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/migrations/0016_tags_and_changelog_for_snapshots.py +0 -0
  40. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/migrations/0017_ipfabricsync_update_custom_fields.py +0 -0
  41. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/migrations/0018_remove_type_field.py +0 -0
  42. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/migrations/0019_alter_ipfabrictransformmap_options_and_more.py +0 -0
  43. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/migrations/__init__.py +0 -0
  44. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/navigation.py +0 -0
  45. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/signals.py +0 -0
  46. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/tables.py +0 -0
  47. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/template_content.py +0 -0
  48. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/ipfabric_netbox/inc/clone_form.html +0 -0
  49. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/ipfabric_netbox/inc/diff.html +0 -0
  50. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/ipfabric_netbox/inc/json.html +0 -0
  51. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/ipfabric_netbox/inc/logs_pending.html +0 -0
  52. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/ipfabric_netbox/inc/merge_form.html +0 -0
  53. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/ipfabric_netbox/inc/site_topology_button.html +0 -0
  54. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/ipfabric_netbox/inc/site_topology_modal.html +0 -0
  55. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/ipfabric_netbox/inc/snapshotdata.html +0 -0
  56. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/ipfabric_netbox/inc/transform_map_field_map.html +0 -0
  57. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/ipfabric_netbox/inc/transform_map_relationship_map.html +0 -0
  58. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/ipfabric_netbox/ipfabric_table.html +0 -0
  59. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/ipfabric_netbox/ipfabricingestion.html +0 -0
  60. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/ipfabric_netbox/ipfabricsnapshot.html +0 -0
  61. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/ipfabric_netbox/ipfabricsource.html +0 -0
  62. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/ipfabric_netbox/ipfabricsync.html +0 -0
  63. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/ipfabric_netbox/ipfabrictransformmap.html +0 -0
  64. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/ipfabric_netbox/ipfabrictransformmap_list.html +0 -0
  65. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/ipfabric_netbox/ipfabrictransformmap_restore.html +0 -0
  66. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/ipfabric_netbox/ipfabrictransformmapgroup.html +0 -0
  67. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/ipfabric_netbox/partials/ingestion_all.html +0 -0
  68. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/ipfabric_netbox/partials/ingestion_progress.html +0 -0
  69. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/ipfabric_netbox/partials/ingestion_statistics.html +0 -0
  70. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/ipfabric_netbox/partials/ingestion_status.html +0 -0
  71. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/ipfabric_netbox/partials/job_logs.html +0 -0
  72. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/ipfabric_netbox/partials/object_tabs.html +0 -0
  73. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/ipfabric_netbox/partials/sync_last_ingestion.html +0 -0
  74. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templates/static/ipfabric_netbox/css/rack.css +0 -0
  75. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templatetags/__init__.py +0 -0
  76. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/templatetags/ipfabric_netbox_helpers.py +0 -0
  77. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/tests/__init__.py +0 -0
  78. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/tests/api/__init__.py +0 -0
  79. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/tests/api/test_api.py +0 -0
  80. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/tests/test_forms.py +0 -0
  81. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/tests/test_models.py +0 -0
  82. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/urls.py +0 -0
  83. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/utilities/__init__.py +0 -0
  84. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/utilities/ipfutils.py +0 -0
  85. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/utilities/logging.py +0 -0
  86. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/utilities/nbutils.py +0 -0
  87. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/utilities/transform_map.py +0 -0
  88. {ipfabric_netbox-4.3.0b7 → ipfabric_netbox-4.3.0b8}/ipfabric_netbox/views.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ipfabric_netbox
3
- Version: 4.3.0b7
3
+ Version: 4.3.0b8
4
4
  Summary: NetBox plugin to sync IP Fabric data into NetBox
5
5
  License: MIT
6
6
  Keywords: netbox,ipfabric,plugin,sync
@@ -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.0b8"
10
10
  base_url = "ipfabric"
11
11
  min_version = "4.4.0"
12
12
 
@@ -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
+ ]
@@ -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,
@@ -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"}
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "ipfabric_netbox"
3
- version = "4.3.0b7"
3
+ version = "4.3.0b8"
4
4
  description = "NetBox plugin to sync IP Fabric data into NetBox"
5
5
  authors = ["Solution Architecture <solution.architecture@ipfabric.io>"]
6
6
  license = "MIT"