ipfabric_netbox 4.2.0b7__py3-none-any.whl → 4.2.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.

Potentially problematic release.


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

@@ -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.2.0b7"
9
+ version = "4.2.0b9"
10
10
  base_url = "ipfabric"
11
11
  min_version = "4.2.4"
12
12
 
@@ -183,7 +183,6 @@ class IPFabricSyncSerializer(NestedGroupModelSerializer):
183
183
  "id",
184
184
  "name",
185
185
  "snapshot_data",
186
- "type",
187
186
  "status",
188
187
  "parameters",
189
188
  "auto_merge",
@@ -83,9 +83,7 @@ class IPFabricSnapshotViewSet(NetBoxReadOnlyModelViewSet):
83
83
  with transaction.atomic():
84
84
  IPFabricData.objects.bulk_create(
85
85
  [
86
- IPFabricData(
87
- snapshot_data=snapshot, data=item["data"], type=item["type"]
88
- )
86
+ IPFabricData(snapshot_data=snapshot, data=item["data"])
89
87
  for item in request.data["data"]
90
88
  ],
91
89
  batch_size=5000,
@@ -198,18 +198,6 @@ class IPFabricSnapshotStatusModelChoices(ChoiceSet):
198
198
  ]
199
199
 
200
200
 
201
- class IPFabricSyncTypeChoices(ChoiceSet):
202
- ALL = "all"
203
- DCIM = "dcim"
204
- IPAM = "ipam"
205
-
206
- CHOICES = (
207
- (ALL, _("All"), "gray"),
208
- (DCIM, _("DCIM"), "blue"),
209
- (IPAM, _("IPAM"), "blue"),
210
- )
211
-
212
-
213
201
  class IPFabricSourceTypeChoices(ChoiceSet):
214
202
  LOCAL = "local"
215
203
  REMOTE = "remote"
@@ -74,14 +74,12 @@ class IPFabricDataFilterSet(BaseFilterSet):
74
74
 
75
75
  class Meta:
76
76
  model = IPFabricData
77
- fields = ["snapshot_data", "type"]
77
+ fields = ["snapshot_data"]
78
78
 
79
79
  def search(self, queryset, name, value):
80
80
  if not value.strip():
81
81
  return queryset
82
- return queryset.filter(
83
- Q(snapshot_data__icontains=value) | Q(type__icontains=value)
84
- )
82
+ return queryset.filter(Q(snapshot_data__icontains=value))
85
83
 
86
84
 
87
85
  class IPFabricSnapshotFilterSet(ChangeLoggedModelFilterSet):
@@ -245,7 +243,6 @@ class IPFabricSyncFilterSet(ChangeLoggedModelFilterSet):
245
243
  "name",
246
244
  "snapshot_data",
247
245
  "snapshot_data_id",
248
- "type",
249
246
  "status",
250
247
  "auto_merge",
251
248
  "last_synced",
ipfabric_netbox/forms.py CHANGED
@@ -46,22 +46,6 @@ exclude_fields = [
46
46
  "status",
47
47
  ]
48
48
 
49
-
50
- class IPFSiteChoiceField(forms.MultipleChoiceField):
51
- def valid_value(self, value):
52
- """Check to see if the provided value is a valid choice."""
53
- text_value = str(value)
54
- for k, v in self.choices:
55
- if isinstance(v, (list, tuple)):
56
- for k2, v2 in v:
57
- if value == k2 or text_value == str(k2):
58
- return True
59
- else:
60
- if value == k or text_value == str(k):
61
- return True
62
- return False
63
-
64
-
65
49
  dcim_parameters = {
66
50
  "site": forms.BooleanField(required=False, label=_("Sites"), initial=True),
67
51
  "manufacturer": forms.BooleanField(
@@ -78,43 +62,45 @@ dcim_parameters = {
78
62
  "virtualchassis": forms.BooleanField(
79
63
  required=False, label=_("Virtual Chassis"), initial=True
80
64
  ),
81
- "interface": forms.BooleanField(required=False, label=_("Interfaces")),
82
- "macaddress": forms.BooleanField(required=False, label=_("MAC Addresses")),
83
- "inventoryitem": forms.BooleanField(required=False, label=_("Part Numbers")),
65
+ "interface": forms.BooleanField(
66
+ required=False, label=_("Interfaces"), initial=False
67
+ ),
68
+ "macaddress": forms.BooleanField(
69
+ required=False, label=_("MAC Addresses"), initial=False
70
+ ),
71
+ "inventoryitem": forms.BooleanField(
72
+ required=False, label=_("Part Numbers"), initial=False
73
+ ),
84
74
  }
85
75
  ipam_parameters = {
86
- "vlan": forms.BooleanField(required=False, label=_("VLANs")),
87
- "vrf": forms.BooleanField(required=False, label=_("VRFs")),
88
- "prefix": forms.BooleanField(required=False, label=_("Prefixes")),
89
- "ipaddress": forms.BooleanField(required=False, label=_("IP Addresses")),
76
+ "vlan": forms.BooleanField(required=False, label=_("VLANs"), initial=False),
77
+ "vrf": forms.BooleanField(required=False, label=_("VRFs"), initial=False),
78
+ "prefix": forms.BooleanField(required=False, label=_("Prefixes"), initial=False),
79
+ "ipaddress": forms.BooleanField(
80
+ required=False, label=_("IP Addresses"), initial=False
81
+ ),
90
82
  }
91
83
  sync_parameters = {"dcim": dcim_parameters, "ipam": ipam_parameters}
92
84
 
93
85
 
94
- def source_column_choices(model):
86
+ def source_column_choices(model: str) -> list[tuple[str, str]]:
95
87
  columns = transform_field_source_columns.get(model, None)
96
88
  if columns:
97
89
  choices = [(f, f) for f in transform_field_source_columns.get(model)]
98
90
  else:
99
- choices = []
91
+ # This should never happen, but better be safe than sorry
92
+ choices = [] # pragma: no cover
100
93
  return choices
101
94
 
102
95
 
103
- def add_all_sites(choices):
104
- """
105
- Add a blank choice to the beginning of a choices list.
106
- """
107
- return ((None, "All Sites"),) + tuple(choices)
108
-
109
-
110
- def str_to_list(str):
111
- if not isinstance(str, list):
112
- return [str]
96
+ def str_to_list(_str: str | list) -> list[str]:
97
+ if not isinstance(_str, list):
98
+ return [_str]
113
99
  else:
114
- return str
100
+ return _str
115
101
 
116
102
 
117
- def list_to_choices(choices):
103
+ def list_to_choices(choices: list[str]) -> tuple[tuple[str, str], ...]:
118
104
  new_choices = ()
119
105
  for choice in choices:
120
106
  new_choices = new_choices + ((choice, choice),)
@@ -176,7 +162,7 @@ class IPFabricRelationshipFieldForm(NetBoxModelForm):
176
162
  )
177
163
  self.fields["target_field"].widget.initial = self.instance.target_field
178
164
  else:
179
- if kwargs["initial"].get("transform_map", None):
165
+ if kwargs.get("initial", {}).get("transform_map", None):
180
166
  transform_map_id = kwargs["initial"]["transform_map"]
181
167
  transform_map = IPFabricTransformMap.objects.get(
182
168
  pk=transform_map_id
@@ -258,7 +244,7 @@ class IPFabricTransformFieldForm(NetBoxModelForm):
258
244
  source_column_choices(source_fields)
259
245
  )
260
246
  else:
261
- if kwargs["initial"].get("transform_map", None):
247
+ if kwargs.get("initial", {}).get("transform_map", None):
262
248
  transform_map_id = kwargs["initial"]["transform_map"]
263
249
  transform_map = IPFabricTransformMap.objects.get(
264
250
  pk=transform_map_id
@@ -375,22 +361,22 @@ class IPFabricSourceForm(NetBoxModelForm):
375
361
  "type": HTMXSelect(),
376
362
  }
377
363
 
378
- @property
379
- def fieldsets(self):
380
- fieldsets = [
381
- FieldSet("name", "type", "url", name=_("Source")),
382
- FieldSet("timeout", name=_("Parameters")),
383
- ]
384
-
385
- if self.source_type == "local":
386
- fieldsets[1] = FieldSet("auth", "verify", "timeout", name=_("Parameters"))
387
-
388
- return fieldsets
389
-
390
364
  def __init__(self, *args, **kwargs):
391
365
  super().__init__(*args, **kwargs)
392
366
  self.source_type = get_field_value(self, "type")
393
367
 
368
+ # Set fieldsets dynamically based on source_type
369
+ if self.source_type == "local":
370
+ self.fieldsets = [
371
+ FieldSet("name", "type", "url", name=_("Source")),
372
+ FieldSet("auth", "verify", "timeout", name=_("Parameters")),
373
+ ]
374
+ else:
375
+ self.fieldsets = [
376
+ FieldSet("name", "type", "url", name=_("Source")),
377
+ FieldSet("timeout", name=_("Parameters")),
378
+ ]
379
+
394
380
  self.fields["timeout"] = forms.IntegerField(
395
381
  required=False,
396
382
  label=_("Timeout"),
@@ -451,6 +437,9 @@ class OrderedModelMultipleChoiceField(forms.ModelMultipleChoiceField):
451
437
 
452
438
  def clean(self, value):
453
439
  qs = super().clean(value)
440
+ # Handle None or empty values
441
+ if not value:
442
+ return qs
454
443
  clauses = " ".join(
455
444
  ["WHEN id=%s THEN %s" % (pk, i) for i, pk in enumerate(value)]
456
445
  )
@@ -488,6 +477,7 @@ class IPFabricSyncForm(NetBoxModelForm):
488
477
  sites = forms.MultipleChoiceField(
489
478
  required=False,
490
479
  label=_("Sites"),
480
+ help_text=_("Defaults to all sites if none selected."),
491
481
  widget=APISelectMultiple(
492
482
  api_url="/api/plugins/ipfabric/snapshot/{{snapshot_data}}/sites/",
493
483
  ),
@@ -527,42 +517,11 @@ class IPFabricSyncForm(NetBoxModelForm):
527
517
  "auto_merge",
528
518
  "update_custom_fields",
529
519
  "sites",
530
- "type",
531
520
  "tags",
532
521
  "scheduled",
533
522
  "interval",
534
523
  )
535
- widgets = {
536
- "source": HTMXSelect(),
537
- "type": HTMXSelect(),
538
- }
539
-
540
- @property
541
- def fieldsets(self):
542
- fieldsets = [
543
- FieldSet("name", "source", "groups", name=_("IP Fabric Source")),
544
- ]
545
- if self.source_type == "local":
546
- fieldsets.append(
547
- FieldSet("snapshot_data", "sites", name=_("Snapshot Information")),
548
- )
549
- else:
550
- fieldsets.append(
551
- FieldSet("snapshot_data", name=_("Snapshot Information")),
552
- )
553
- fieldsets.append(FieldSet("type", name=_("Ingestion Type")))
554
- if self.backend_fields:
555
- for k, v in self.backend_fields.items():
556
- fieldsets.append(FieldSet(*v, name=f"{k.upper()} Parameters"))
557
- fieldsets.append(
558
- FieldSet("scheduled", "interval", name=_("Ingestion Execution Parameters"))
559
- )
560
- fieldsets.append(
561
- FieldSet("auto_merge", "update_custom_fields", name=_("Extras"))
562
- )
563
- fieldsets.append(FieldSet("tags", name=_("Tags")))
564
-
565
- return fieldsets
524
+ widgets = {"source": HTMXSelect()}
566
525
 
567
526
  def __init__(self, *args, **kwargs):
568
527
  super().__init__(*args, **kwargs)
@@ -594,20 +553,13 @@ class IPFabricSyncForm(NetBoxModelForm):
594
553
  self.initial["sites"] = self.instance.parameters.get("sites", [])
595
554
  self.initial["groups"] = self.instance.parameters.get("groups", [])
596
555
 
597
- backend_type = get_field_value(self, "type")
598
- backend = {}
599
- if backend_type == "all":
600
- backend = sync_parameters
601
- else:
602
- backend[backend_type] = sync_parameters.get(backend_type)
603
-
604
556
  now = local_now().strftime("%Y-%m-%d %H:%M:%S")
605
557
  self.fields["scheduled"].help_text += f" (current time: <strong>{now}</strong>)"
606
558
 
607
559
  # Add backend-specific form fields
608
560
  self.backend_fields = {}
609
561
 
610
- for k, v in backend.items():
562
+ for k, v in sync_parameters.items():
611
563
  self.backend_fields[k] = []
612
564
  for name, form_field in v.items():
613
565
  field_name = f"ipf_{name}"
@@ -616,17 +568,52 @@ class IPFabricSyncForm(NetBoxModelForm):
616
568
  if self.instance and self.instance.parameters:
617
569
  self.fields[field_name].initial = self.instance.parameters.get(name)
618
570
 
571
+ # Set fieldsets dynamically based and backend_fields
572
+ fieldsets = [
573
+ FieldSet("name", "source", "groups", name=_("IP Fabric Source")),
574
+ ]
575
+ if self.source_type == "local":
576
+ fieldsets.append(
577
+ FieldSet("snapshot_data", "sites", name=_("Snapshot Information")),
578
+ )
579
+ else:
580
+ fieldsets.append(
581
+ FieldSet("snapshot_data", name=_("Snapshot Information")),
582
+ )
583
+ for k, v in self.backend_fields.items():
584
+ fieldsets.append(FieldSet(*v, name=f"{k.upper()} Parameters"))
585
+ fieldsets.append(
586
+ FieldSet("scheduled", "interval", name=_("Ingestion Execution Parameters"))
587
+ )
588
+ fieldsets.append(
589
+ FieldSet("auto_merge", "update_custom_fields", name=_("Extras"))
590
+ )
591
+ fieldsets.append(FieldSet("tags", name=_("Tags")))
592
+
593
+ self.fieldsets = fieldsets
594
+
619
595
  def clean(self):
620
596
  super().clean()
621
- snapshot = self.cleaned_data["snapshot_data"]
622
597
 
623
598
  sites = self.data.get("sites")
624
- choices = list_to_choices(str_to_list(sites))
625
- self.fields["sites"].choices = choices
626
-
627
- if sites:
628
- if not any(y in x for x in snapshot.sites for y in sites):
629
- raise ValidationError({"sites": f"{sites} not part of the snapshot."})
599
+ self.fields["sites"].choices = list_to_choices(str_to_list(sites))
600
+ if sites and "snapshot_data" in self.cleaned_data:
601
+ snapshot = self.cleaned_data["snapshot_data"]
602
+ # Check if all sites are valid - fail if any site is not found in snapshot.sites
603
+ if not all(
604
+ any(site in snapshot_site for snapshot_site in snapshot.sites)
605
+ for site in sites
606
+ ):
607
+ invalid_sites = [
608
+ site
609
+ for site in sites
610
+ if not any(
611
+ site in snapshot_site for snapshot_site in snapshot.sites
612
+ )
613
+ ]
614
+ raise ValidationError(
615
+ {"sites": f"Sites {invalid_sites} not part of the snapshot."}
616
+ )
630
617
 
631
618
  scheduled_time = self.cleaned_data.get("scheduled")
632
619
  if scheduled_time and scheduled_time < local_now():
@@ -674,50 +661,6 @@ class IPFabricSyncForm(NetBoxModelForm):
674
661
  return object
675
662
 
676
663
 
677
- # class SyncForm(forms.Form):
678
- # def __init__(self, *args, **kwargs):
679
- # self.snapshots = kwargs.pop("snapshot_choices", None)
680
- # self.sites = kwargs.pop("site_choices", None)
681
- # super(SyncForm, self).__init__(*args, **kwargs)
682
- # if self.snapshots:
683
- # snapshot_choices = [
684
- # (snapshot_id, snapshot_name)
685
- # for snapshot_name, snapshot_id in self.snapshots.values()
686
- # ]
687
- # self.fields["snapshot"] = forms.ChoiceField(
688
- # label="Snapshot",
689
- # required=True,
690
- # choices=snapshot_choices,
691
- # help_text="IPFabric snapshot to sync from. Defaults to $last",
692
- # widget=forms.Select(
693
- # attrs={
694
- # "hx-get": reverse("plugins:ipfabric_netbox:ipfabricsync_add"),
695
- # "hx-trigger": "change",
696
- # "hx-target": "#modules",
697
- # "class": "form-control",
698
- # }
699
- # ),
700
- # )
701
- # if self.sites:
702
- # site_choices = [(site, site) for site in self.sites]
703
- # self.fields["site"] = forms.ChoiceField(
704
- # label="Site",
705
- # required=False,
706
- # choices=add_blank_choice(site_choices),
707
- # help_text="Sites available within snapshot",
708
- # widget=forms.Select(attrs={"class": "form-control"}),
709
- # )
710
- # else:
711
- # self.fields["site"] = forms.ChoiceField(
712
- # label="Site",
713
- # required=False,
714
- # choices=add_blank_choice([]),
715
- # help_text="Sites available within snapshot",
716
- # widget=forms.Select(
717
- # attrs={"class": "form-control", "disabled": "disabled"}
718
- # ),
719
- # )
720
-
721
664
  tableChoices = [
722
665
  ("eol_details", "Inventory - EOL_DETAILS"),
723
666
  ("fans", "Inventory - FANS"),
@@ -6,7 +6,6 @@ from netbox_branching.choices import BranchStatusChoices
6
6
  from ipfabric_netbox.choices import IPFabricRawDataTypeChoices
7
7
  from ipfabric_netbox.choices import IPFabricSnapshotStatusModelChoices
8
8
  from ipfabric_netbox.choices import IPFabricSourceTypeChoices
9
- from ipfabric_netbox.choices import IPFabricSyncTypeChoices
10
9
  from ipfabric_netbox.choices import IPFabricTransformMapSourceModelChoices
11
10
 
12
11
  __all__ = (
@@ -14,7 +13,6 @@ __all__ = (
14
13
  "IPFabricTransformMapSourceModelEnum",
15
14
  "IPFabricSourceTypeEnum",
16
15
  "IPFabricSnapshotStatusModelEnum",
17
- "IPFabricSyncTypeEnum",
18
16
  "IPFabricRawDataTypeEnum",
19
17
  "BranchStatusEnum",
20
18
  "JobStatusEnum",
@@ -30,7 +28,6 @@ IPFabricSourceTypeEnum = strawberry.enum(
30
28
  IPFabricSnapshotStatusModelEnum = strawberry.enum(
31
29
  IPFabricSnapshotStatusModelChoices.as_enum(prefix="type")
32
30
  )
33
- IPFabricSyncTypeEnum = strawberry.enum(IPFabricSyncTypeChoices.as_enum(prefix="type"))
34
31
  IPFabricRawDataTypeEnum = strawberry.enum(
35
32
  IPFabricRawDataTypeChoices.as_enum(prefix="type")
36
33
  )