ipfabric_netbox 3.2.4__py3-none-any.whl → 3.2.4b3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ipfabric_netbox might be problematic. Click here for more details.

Files changed (38) hide show
  1. ipfabric_netbox/__init__.py +1 -1
  2. ipfabric_netbox/api/nested_serializers.py +0 -20
  3. ipfabric_netbox/api/serializers.py +7 -13
  4. ipfabric_netbox/api/urls.py +2 -2
  5. ipfabric_netbox/api/views.py +5 -5
  6. ipfabric_netbox/data/transform_map.json +21 -35
  7. ipfabric_netbox/filtersets.py +15 -13
  8. ipfabric_netbox/forms.py +14 -42
  9. ipfabric_netbox/jobs.py +12 -7
  10. ipfabric_netbox/migrations/0001_initial.py +1 -1
  11. ipfabric_netbox/migrations/0001_initial_squashed_0013_switch_to_branching_plugin.py +503 -0
  12. ipfabric_netbox/migrations/0007_prepare_custom_fields.py +3 -3
  13. ipfabric_netbox/migrations/0011_update_part_number_DCIM_inventory_item_template.py +57 -0
  14. ipfabric_netbox/migrations/0012_remove_status_field.py +18 -0
  15. ipfabric_netbox/migrations/0013_switch_to_branching_plugin.py +270 -0
  16. ipfabric_netbox/models.py +120 -91
  17. ipfabric_netbox/navigation.py +1 -1
  18. ipfabric_netbox/signals.py +9 -2
  19. ipfabric_netbox/tables.py +40 -46
  20. ipfabric_netbox/templates/ipfabric_netbox/{ipfabricbranch.html → ipfabricingestion.html} +14 -9
  21. ipfabric_netbox/templates/ipfabric_netbox/ipfabricsource.html +3 -3
  22. ipfabric_netbox/templates/ipfabric_netbox/ipfabricsync.html +3 -3
  23. ipfabric_netbox/templates/ipfabric_netbox/ipfabricsync_list.html +71 -0
  24. ipfabric_netbox/templates/ipfabric_netbox/ipfabrictransformmap.html +0 -4
  25. ipfabric_netbox/templates/ipfabric_netbox/partials/ingestion_all.html +10 -0
  26. ipfabric_netbox/templates/ipfabric_netbox/partials/{branch_progress.html → ingestion_progress.html} +1 -1
  27. ipfabric_netbox/templates/ipfabric_netbox/partials/sync_last_ingestion.html +1 -0
  28. ipfabric_netbox/urls.py +13 -3
  29. ipfabric_netbox/utilities/ipfutils.py +52 -19
  30. ipfabric_netbox/views.py +162 -155
  31. {ipfabric_netbox-3.2.4.dist-info → ipfabric_netbox-3.2.4b3.dist-info}/METADATA +12 -4
  32. {ipfabric_netbox-3.2.4.dist-info → ipfabric_netbox-3.2.4b3.dist-info}/RECORD +34 -31
  33. {ipfabric_netbox-3.2.4.dist-info → ipfabric_netbox-3.2.4b3.dist-info}/WHEEL +1 -1
  34. ipfabric_netbox/templates/ipfabric_netbox/inc/sync_delete.html +0 -19
  35. ipfabric_netbox/templates/ipfabric_netbox/partials/branch_all.html +0 -10
  36. ipfabric_netbox/templates/ipfabric_netbox/partials/sync_last_branch.html +0 -1
  37. ipfabric_netbox/templates/ipfabric_netbox/sync_list.html +0 -126
  38. /ipfabric_netbox/templates/ipfabric_netbox/partials/{branch_status.html → ingestion_status.html} +0 -0
@@ -5,7 +5,7 @@ class NetboxIPFabricConfig(PluginConfig):
5
5
  name = "ipfabric_netbox"
6
6
  verbose_name = "NetBox IP Fabric SoT Plugin"
7
7
  description = "Sync IP Fabric into NetBox"
8
- version = "3.2.4"
8
+ version = "3.2.4b2"
9
9
  base_url = "ipfabric"
10
10
  min_version = "4.2.4"
11
11
 
@@ -4,9 +4,7 @@ from netbox.api.fields import ContentTypeField
4
4
  from netbox.api.serializers import NetBoxModelSerializer
5
5
  from netbox.api.serializers import WritableNestedSerializer
6
6
  from rest_framework import serializers
7
- from users.api.serializers_.nested import NestedUserSerializer
8
7
 
9
- from ipfabric_netbox.models import IPFabricBranch
10
8
  from ipfabric_netbox.models import IPFabricSnapshot
11
9
  from ipfabric_netbox.models import IPFabricSource
12
10
  from ipfabric_netbox.models import IPFabricSync
@@ -18,7 +16,6 @@ __all__ = (
18
16
  "NestedIPFabricSourceSerializer",
19
17
  "NestedIPFabricSnapshotSerializer",
20
18
  "NestedIPFabricTransformMapSerializer",
21
- "NestedIPFabricBranchSerializer",
22
19
  "NestedIPFabricSyncSerializer",
23
20
  )
24
21
 
@@ -79,21 +76,4 @@ class NestedIPFabricTransformMapSerializer(NetBoxModelSerializer):
79
76
  "id",
80
77
  "source_model",
81
78
  "target_model",
82
- "status",
83
- ]
84
-
85
-
86
- class NestedIPFabricBranchSerializer(NetBoxModelSerializer):
87
- user = NestedUserSerializer(read_only=True)
88
- sync = NestedIPFabricSyncSerializer(read_only=True)
89
-
90
- class Meta:
91
- model = IPFabricBranch
92
- fields = [
93
- "id",
94
- "name",
95
- "display",
96
- "sync",
97
- "description",
98
- "user",
99
79
  ]
@@ -2,14 +2,13 @@ from core.choices import DataSourceStatusChoices
2
2
  from netbox.api.fields import ChoiceField
3
3
  from netbox.api.fields import ContentTypeField
4
4
  from netbox.api.serializers import NetBoxModelSerializer
5
+ from netbox_branching.api.serializers import BranchSerializer
5
6
  from rest_framework import serializers
6
- from users.api.serializers_.nested import NestedUserSerializer
7
7
 
8
- from .nested_serializers import NestedIPFabricBranchSerializer # noqa: F401
9
8
  from .nested_serializers import NestedIPFabricSnapshotSerializer
10
9
  from .nested_serializers import NestedIPFabricSourceSerializer
11
10
  from .nested_serializers import NestedIPFabricTransformMapSerializer
12
- from ipfabric_netbox.models import IPFabricBranch
11
+ from ipfabric_netbox.models import IPFabricIngestion
13
12
  from ipfabric_netbox.models import IPFabricRelationshipField
14
13
  from ipfabric_netbox.models import IPFabricSnapshot
15
14
  from ipfabric_netbox.models import IPFabricSource
@@ -23,7 +22,7 @@ __all__ = (
23
22
  "IPFabricRelationshipFieldSerializer",
24
23
  "IPFabricTransformFieldSerializer",
25
24
  "IPFabricTransformMapSerializer",
26
- "IPFabricBranchSerializer",
25
+ "IPFabricIngestionSerializer",
27
26
  "IPFabricSourceSerializer",
28
27
  )
29
28
 
@@ -99,7 +98,6 @@ class IPFabricTransformMapSerializer(NetBoxModelSerializer):
99
98
  "id",
100
99
  "source_model",
101
100
  "target_model",
102
- "status",
103
101
  "created",
104
102
  "last_updated",
105
103
  ]
@@ -120,21 +118,17 @@ class IPFabricTransformFieldSerializer(NetBoxModelSerializer):
120
118
  ]
121
119
 
122
120
 
123
- class IPFabricBranchSerializer(NetBoxModelSerializer):
124
- user = NestedUserSerializer(read_only=True)
121
+ class IPFabricIngestionSerializer(NetBoxModelSerializer):
122
+ branch = BranchSerializer(read_only=True)
125
123
  sync = IPFabricSyncSerializer(read_only=True)
126
124
 
127
125
  class Meta:
128
- model = IPFabricBranch
126
+ model = IPFabricIngestion
129
127
  fields = [
130
128
  "id",
131
129
  "name",
132
- "display",
130
+ "branch",
133
131
  "sync",
134
- "description",
135
- "user",
136
- "created",
137
- "last_updated",
138
132
  ]
139
133
 
140
134
 
@@ -1,7 +1,7 @@
1
1
  # api/urls.py
2
2
  from netbox.api.routers import NetBoxRouter
3
3
 
4
- from ipfabric_netbox.api.views import IPFabricBranchViewSet
4
+ from ipfabric_netbox.api.views import IPFabricIngestionViewSet
5
5
  from ipfabric_netbox.api.views import IPFabricRelationshipFieldiewSet
6
6
  from ipfabric_netbox.api.views import IPFabricSnapshotViewSet
7
7
  from ipfabric_netbox.api.views import IPFabricSourceViewSet
@@ -15,7 +15,7 @@ router.register("source", IPFabricSourceViewSet)
15
15
  router.register("snapshot", IPFabricSnapshotViewSet)
16
16
  router.register("transform-map", IPFabricTransformMapViewSet)
17
17
  router.register("sync", IPFabricSyncViewSet)
18
- router.register("branch", IPFabricBranchViewSet)
18
+ router.register("ingestion", IPFabricIngestionViewSet)
19
19
  router.register("transform-field", IPFabricTransformFieldiewSet)
20
20
  router.register("relationship-field", IPFabricRelationshipFieldiewSet)
21
21
  urlpatterns = router.urls
@@ -5,7 +5,7 @@ from rest_framework.decorators import action
5
5
  from rest_framework.response import Response
6
6
  from utilities.query import count_related
7
7
 
8
- from .serializers import IPFabricBranchSerializer
8
+ from .serializers import IPFabricIngestionSerializer
9
9
  from .serializers import IPFabricRelationshipFieldSerializer
10
10
  from .serializers import IPFabricSnapshotSerializer
11
11
  from .serializers import IPFabricSourceSerializer
@@ -15,8 +15,8 @@ from .serializers import IPFabricTransformMapSerializer
15
15
  from ipfabric_netbox.filtersets import IPFabricSnapshotFilterSet
16
16
  from ipfabric_netbox.filtersets import IPFabricSourceFilterSet
17
17
  from ipfabric_netbox.filtersets import IPFabricTransformFieldFilterSet
18
- from ipfabric_netbox.models import IPFabricBranch
19
18
  from ipfabric_netbox.models import IPFabricData
19
+ from ipfabric_netbox.models import IPFabricIngestion
20
20
  from ipfabric_netbox.models import IPFabricRelationshipField
21
21
  from ipfabric_netbox.models import IPFabricSnapshot
22
22
  from ipfabric_netbox.models import IPFabricSource
@@ -47,9 +47,9 @@ class IPFabricSyncViewSet(NetBoxReadOnlyModelViewSet):
47
47
  serializer_class = IPFabricSyncSerializer
48
48
 
49
49
 
50
- class IPFabricBranchViewSet(NetBoxReadOnlyModelViewSet):
51
- queryset = IPFabricBranch.objects.all()
52
- serializer_class = IPFabricBranchSerializer
50
+ class IPFabricIngestionViewSet(NetBoxReadOnlyModelViewSet):
51
+ queryset = IPFabricIngestion.objects.all()
52
+ serializer_class = IPFabricIngestionSerializer
53
53
 
54
54
 
55
55
  class IPFabricSnapshotViewSet(NetBoxModelViewSet):
@@ -9,8 +9,7 @@
9
9
  "target_model": {
10
10
  "app_label": "ipam",
11
11
  "model": "ipaddress"
12
- },
13
- "status": "active"
12
+ }
14
13
  },
15
14
  "field_maps": [
16
15
  {
@@ -57,8 +56,7 @@
57
56
  "target_model": {
58
57
  "app_label": "dcim",
59
58
  "model": "platform"
60
- },
61
- "status": ""
59
+ }
62
60
  },
63
61
  "field_maps": [
64
62
  {
@@ -96,8 +94,7 @@
96
94
  "target_model": {
97
95
  "app_label": "dcim",
98
96
  "model": "site"
99
- },
100
- "status": "active"
97
+ }
101
98
  },
102
99
  "field_maps": [
103
100
  {
@@ -125,8 +122,7 @@
125
122
  "target_model": {
126
123
  "app_label": "dcim",
127
124
  "model": "manufacturer"
128
- },
129
- "status": ""
125
+ }
130
126
  },
131
127
  "field_maps": [
132
128
  {
@@ -139,7 +135,7 @@
139
135
  "source_field": "vendor",
140
136
  "target_field": "name",
141
137
  "coalesce": false,
142
- "template": null
138
+ "template": ""
143
139
  }
144
140
  ],
145
141
  "relationship_maps": []
@@ -154,8 +150,7 @@
154
150
  "target_model": {
155
151
  "app_label": "dcim",
156
152
  "model": "device"
157
- },
158
- "status": "active"
153
+ }
159
154
  },
160
155
  "field_maps": [
161
156
  {
@@ -212,7 +207,7 @@
212
207
  },
213
208
  "target_field": "device_type",
214
209
  "coalesce": false,
215
- "template": "{% if object.model != none %}{% set SLUG = object.model | string | slugify %}{% else %}{% set SLUG = object.vendor | slugify ~ \"-\" ~ object.family | slugify ~ \"-\" ~ object.platform %}{% endif %}{{ dcim.DeviceType.objects.get(slug=SLUG).pk }}"
210
+ "template": "{% if object.model %}{% set SLUG = object.model | string | slugify %}{% else %}{% set SLUG = object.vendor | slugify ~ \"-\" ~ object.family | slugify ~ \"-\" ~ object.platform | slugify %}{% endif %}{{ dcim.DeviceType.objects.get(slug=SLUG).pk }}"
216
211
  },
217
212
  {
218
213
  "source_model": {
@@ -235,8 +230,7 @@
235
230
  "target_model": {
236
231
  "app_label": "dcim",
237
232
  "model": "devicerole"
238
- },
239
- "status": ""
233
+ }
240
234
  },
241
235
  "field_maps": [
242
236
  {
@@ -270,21 +264,20 @@
270
264
  "target_model": {
271
265
  "app_label": "dcim",
272
266
  "model": "devicetype"
273
- },
274
- "status": ""
267
+ }
275
268
  },
276
269
  "field_maps": [
277
270
  {
278
271
  "source_field": "model",
279
272
  "target_field": "slug",
280
273
  "coalesce": true,
281
- "template": "{% if object.model != none %}{{ object.model | string | slugify }}{% else %}{{ object.vendor | slugify }}-{{ object.family | slugify}}-{{ object.platform }}{% endif %}"
274
+ "template": "{% if object.model %}{{ object.model | string | slugify }}{% else %}{{ object.vendor | slugify }}-{{ object.family | slugify}}-{{ object.platform | slugify }}{% endif %}"
282
275
  },
283
276
  {
284
277
  "source_field": "model",
285
278
  "target_field": "model",
286
279
  "coalesce": false,
287
- "template": "{% if object.model != none %}{{ object.model | string }}{% else %}{{ object.vendor }} - {{ object.family }} - {{ object.platform }}{% endif %}"
280
+ "template": "{% if object.model %}{{ object.model | string }}{% else %}{{ object.vendor }} - {{ object.family }} - {{ object.platform }}{% endif %}"
288
281
  }
289
282
  ],
290
283
  "relationship_maps": [
@@ -309,8 +302,7 @@
309
302
  "target_model": {
310
303
  "app_label": "dcim",
311
304
  "model": "interface"
312
- },
313
- "status": ""
305
+ }
314
306
  },
315
307
  "field_maps": [
316
308
  {
@@ -353,13 +345,13 @@
353
345
  "source_field": "speedValue",
354
346
  "target_field": "speed",
355
347
  "coalesce": false,
356
- "template": "{% if not object.speedValue %}None{% else %}{{ object.speedValue|int // 1000 }}{% endif %}"
348
+ "template": "{% if not object.speedValue or object.speedValue==\"unknown\" %}None{% else %}{{ object.speedValue|int // 1000 }}{% endif %}"
357
349
  },
358
350
  {
359
351
  "source_field": "duplex",
360
352
  "target_field": "duplex",
361
353
  "coalesce": false,
362
- "template": null
354
+ "template": "{% if not object.duplex or object.duplex==\"unknown\" %}None{% else %}{{ object.duplex }}{% endif %}"
363
355
  }
364
356
  ],
365
357
  "relationship_maps": [
@@ -384,8 +376,7 @@
384
376
  "target_model": {
385
377
  "app_label": "dcim",
386
378
  "model": "macaddress"
387
- },
388
- "status": ""
379
+ }
389
380
  },
390
381
  "field_maps": [
391
382
  {
@@ -423,8 +414,7 @@
423
414
  "target_model": {
424
415
  "app_label": "dcim",
425
416
  "model": "inventoryitem"
426
- },
427
- "status": ""
417
+ }
428
418
  },
429
419
  "field_maps": [
430
420
  {
@@ -443,7 +433,7 @@
443
433
  "source_field": "name",
444
434
  "target_field": "name",
445
435
  "coalesce": false,
446
- "template": "{% if object.name is not none %}{{ object.name }}{% elif object.dscr is not none %}{{ object.dscr}}{% else %}Default Name{% endif %}"
436
+ "template": "{% if object.name is not none %}{{ object.name | string | truncate(64, True) }}{% elif object.dscr is not none %}{{ object.dscr | string | truncate(64, True) }}{% else %}Default Name{% endif %}"
447
437
  }
448
438
  ],
449
439
  "relationship_maps": [
@@ -477,8 +467,7 @@
477
467
  "target_model": {
478
468
  "app_label": "ipam",
479
469
  "model": "vlan"
480
- },
481
- "status": "active"
470
+ }
482
471
  },
483
472
  "field_maps": [
484
473
  {
@@ -522,8 +511,7 @@
522
511
  "target_model": {
523
512
  "app_label": "ipam",
524
513
  "model": "vrf"
525
- },
526
- "status": ""
514
+ }
527
515
  },
528
516
  "field_maps": [
529
517
  {
@@ -551,8 +539,7 @@
551
539
  "target_model": {
552
540
  "app_label": "ipam",
553
541
  "model": "prefix"
554
- },
555
- "status": "active"
542
+ }
556
543
  },
557
544
  "field_maps": [
558
545
  {
@@ -599,8 +586,7 @@
599
586
  "target_model": {
600
587
  "app_label": "dcim",
601
588
  "model": "virtualchassis"
602
- },
603
- "status": ""
589
+ }
604
590
  },
605
591
  "field_maps": [
606
592
  {
@@ -1,34 +1,36 @@
1
1
  import django_filters
2
2
  from core.choices import DataSourceStatusChoices
3
+ from core.choices import ObjectChangeActionChoices
3
4
  from django.db.models import Q
4
5
  from django.utils.translation import gettext as _
5
- from extras.choices import ChangeActionChoices
6
6
  from netbox.filtersets import BaseFilterSet
7
7
  from netbox.filtersets import ChangeLoggedModelFilterSet
8
8
  from netbox.filtersets import NetBoxModelFilterSet
9
- from netbox.staging import StagedChange
9
+ from netbox_branching.models import ChangeDiff
10
10
 
11
- from .models import IPFabricBranch
12
11
  from .models import IPFabricData
12
+ from .models import IPFabricIngestion
13
13
  from .models import IPFabricSnapshot
14
14
  from .models import IPFabricSource
15
15
  from .models import IPFabricSync
16
16
  from .models import IPFabricTransformMap
17
17
 
18
18
 
19
- class IPFabricStagedChangeFilterSet(BaseFilterSet):
19
+ class IPFabricIngestionChangeFilterSet(BaseFilterSet):
20
20
  q = django_filters.CharFilter(method="search")
21
- action = django_filters.MultipleChoiceFilter(choices=ChangeActionChoices)
21
+ action = django_filters.MultipleChoiceFilter(choices=ObjectChangeActionChoices)
22
22
 
23
23
  class Meta:
24
- model = StagedChange
24
+ model = ChangeDiff
25
25
  fields = ["branch", "action", "object_type"]
26
26
 
27
27
  def search(self, queryset, name, value):
28
28
  if not value.strip():
29
29
  return queryset
30
30
  return queryset.filter(
31
- Q(data__values__contains=value)
31
+ Q(current__icontains=value)
32
+ | Q(modified__icontains=value)
33
+ | Q(original__icontains=value)
32
34
  | Q(action__icontains=value)
33
35
  | Q(object_type__model__icontains=value)
34
36
  )
@@ -94,7 +96,7 @@ class IPFabricSourceFilterSet(NetBoxModelFilterSet):
94
96
  )
95
97
 
96
98
 
97
- class IPFabricBranchFilterSet(BaseFilterSet):
99
+ class IPFabricIngestionFilterSet(BaseFilterSet):
98
100
  q = django_filters.CharFilter(method="search")
99
101
  sync_id = django_filters.ModelMultipleChoiceFilter(
100
102
  queryset=IPFabricSync.objects.all(),
@@ -103,19 +105,19 @@ class IPFabricBranchFilterSet(BaseFilterSet):
103
105
  sync = django_filters.ModelMultipleChoiceFilter(
104
106
  field_name="sync__name",
105
107
  queryset=IPFabricSync.objects.all(),
106
- to_field_name="name",
108
+ to_field_name="branch__name",
107
109
  label=_("Sync (name)"),
108
110
  )
109
111
 
110
112
  class Meta:
111
- model = IPFabricBranch
112
- fields = ("id", "name", "sync")
113
+ model = IPFabricIngestion
114
+ fields = ("id", "branch", "sync")
113
115
 
114
- def search(self, queryset, name, value):
116
+ def search(self, queryset, branch, value):
115
117
  if not value.strip():
116
118
  return queryset
117
119
  return queryset.filter(
118
- Q(name__icontains=value) | Q(sync__name__icontains=value)
120
+ Q(branch__name__icontains=value) | Q(sync__name__icontains=value)
119
121
  )
120
122
 
121
123
 
ipfabric_netbox/forms.py CHANGED
@@ -3,7 +3,6 @@ import copy
3
3
  from core.choices import DataSourceStatusChoices
4
4
  from core.choices import JobIntervalChoices
5
5
  from django import forms
6
- from django.contrib.contenttypes.models import ContentType
7
6
  from django.core.exceptions import ValidationError
8
7
  from django.utils import timezone
9
8
  from django.utils.translation import gettext_lazy as _
@@ -12,6 +11,7 @@ from netbox.forms import NetBoxModelForm
12
11
  from netbox.forms.mixins import SavedFiltersMixin
13
12
  from utilities.datetime import local_now
14
13
  from utilities.forms import add_blank_choice
14
+ from utilities.forms import ConfirmationForm
15
15
  from utilities.forms import FilterForm
16
16
  from utilities.forms import get_field_value
17
17
  from utilities.forms.fields import CommentField
@@ -25,7 +25,7 @@ from utilities.forms.widgets import NumberWithOptions
25
25
 
26
26
  from .choices import IPFabricSnapshotStatusModelChoices
27
27
  from .choices import transform_field_source_columns
28
- from .models import IPFabricBranch
28
+ from .models import IPFabricIngestion
29
29
  from .models import IPFabricRelationshipField
30
30
  from .models import IPFabricSnapshot
31
31
  from .models import IPFabricSource
@@ -275,50 +275,13 @@ class IPFabricTransformFieldForm(NetBoxModelForm):
275
275
 
276
276
 
277
277
  class IPFabricTransformMapForm(forms.ModelForm):
278
- status = forms.CharField(
279
- required=False,
280
- label=_("Status"),
281
- widget=forms.Select(),
282
- )
283
-
284
278
  class Meta:
285
279
  model = IPFabricTransformMap
286
- fields = ("name", "source_model", "target_model", "status")
280
+ fields = ("name", "source_model", "target_model")
287
281
  widgets = {
288
282
  "target_model": HTMXSelect(hx_url="/plugins/ipfabric/transform-map/add"),
289
283
  }
290
284
 
291
- def __init__(self, *args, **kwargs):
292
- super().__init__(*args, **kwargs)
293
- if not self.data:
294
- if kwargs["initial"].get("target_model"):
295
- target_model = ContentType.objects.get(
296
- pk=kwargs["initial"].get("target_model")
297
- )
298
- try:
299
- status = target_model.model_class()._meta.get_field("status")
300
- self.fields["status"].widget.choices = add_blank_choice(
301
- status.choices
302
- )
303
- self.fields["status"].widget.initial = get_field_value(
304
- self, "status"
305
- )
306
- except Exception as e: # noqa: F841
307
- self.fields["status"].widget.attrs["disabled"] = "disabled"
308
- else:
309
- if self.instance and self.instance.pk is not None:
310
- transform_map = self.instance.target_model.model_class()
311
- try:
312
- status = transform_map._meta.get_field("status")
313
- self.fields["status"].widget.choices = add_blank_choice(
314
- status.choices
315
- )
316
- self.fields["status"].widget.initial = get_field_value(
317
- self, "status"
318
- )
319
- except Exception as e: # noqa: F841
320
- self.fields["status"].widget.attrs["disabled"] = "disabled"
321
-
322
285
 
323
286
  class IPFabricSnapshotFilterForm(NetBoxModelFilterSetForm):
324
287
  model = IPFabricSnapshot
@@ -343,17 +306,26 @@ class IPFabricSourceFilterForm(NetBoxModelFilterSetForm):
343
306
  status = forms.MultipleChoiceField(choices=DataSourceStatusChoices, required=False)
344
307
 
345
308
 
346
- class IPFabricBranchFilterForm(SavedFiltersMixin, FilterForm):
309
+ class IPFabricIngestionFilterForm(SavedFiltersMixin, FilterForm):
347
310
  fieldsets = (
348
311
  FieldSet("q", "filter_id"),
349
312
  FieldSet("sync_id", name=_("Source")),
350
313
  )
351
- model = IPFabricBranch
314
+ model = IPFabricIngestion
352
315
  sync_id = DynamicModelMultipleChoiceField(
353
316
  queryset=IPFabricSync.objects.all(), required=False, label=_("Sync")
354
317
  )
355
318
 
356
319
 
320
+ class IPFabricIngestionMergeForm(ConfirmationForm):
321
+ remove_branch = forms.BooleanField(
322
+ initial=True,
323
+ required=False,
324
+ label=_("Remove branch"),
325
+ help_text=_("Leave unchecked to keep Branch for possible revert."),
326
+ )
327
+
328
+
357
329
  class IPFabricSourceForm(NetBoxModelForm):
358
330
  comments = CommentField()
359
331
 
ipfabric_netbox/jobs.py CHANGED
@@ -10,7 +10,7 @@ from rq.timeouts import JobTimeoutException
10
10
  from utilities.datetime import local_now
11
11
  from utilities.request import NetBoxFakeRequest
12
12
 
13
- from .models import IPFabricBranch
13
+ from .models import IPFabricIngestion
14
14
  from .models import IPFabricSource
15
15
  from .models import IPFabricSync
16
16
 
@@ -64,16 +64,16 @@ def sync_ipfabric(job, *args, **kwargs):
64
64
  )
65
65
 
66
66
 
67
- def merge_ipfabric_branch(job, *args, **kwargs):
68
- branch = IPFabricBranch.objects.get(pk=job.object_id)
67
+ def merge_ipfabric_ingestion(job, remove_branch=False, *args, **kwargs):
68
+ ingestion = IPFabricIngestion.objects.get(pk=job.object_id)
69
69
  try:
70
70
  request = NetBoxFakeRequest(
71
71
  {
72
72
  "META": {},
73
- "POST": branch.sync.parameters,
73
+ "POST": ingestion.sync.parameters,
74
74
  "GET": {},
75
75
  "FILES": {},
76
- "user": branch.user,
76
+ "user": ingestion.sync.user,
77
77
  "path": "",
78
78
  "id": job.job_id,
79
79
  }
@@ -81,12 +81,17 @@ def merge_ipfabric_branch(job, *args, **kwargs):
81
81
 
82
82
  job.start()
83
83
  with event_tracking(request):
84
- branch.sync_merge()
84
+ ingestion.sync_merge()
85
+ if remove_branch:
86
+ branching_branch = ingestion.branch
87
+ ingestion.branch = None
88
+ ingestion.save()
89
+ branching_branch.delete()
85
90
  job.terminate()
86
91
  except Exception as e:
87
92
  print(e)
88
93
  job.terminate(status=JobStatusChoices.STATUS_ERRORED)
89
- IPFabricSync.objects.filter(pk=branch.sync.pk).update(
94
+ IPFabricSync.objects.filter(pk=ingestion.sync.pk).update(
90
95
  status=DataSourceStatusChoices.FAILED
91
96
  )
92
97
  if type(e) in (SyncError, JobTimeoutException):
@@ -62,7 +62,7 @@ class Migration(migrations.Migration):
62
62
  ),
63
63
  ("name", models.CharField(max_length=100, unique=True)),
64
64
  ("source_model", models.CharField(max_length=50)),
65
- ("status", models.CharField(max_length=50)),
65
+ ("status", models.CharField(max_length=50, null=True)),
66
66
  (
67
67
  "tags",
68
68
  taggit.managers.TaggableManager(