ipfabric_netbox 4.3.2b9__py3-none-any.whl → 4.3.2b11__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 (50) hide show
  1. ipfabric_netbox/__init__.py +1 -1
  2. ipfabric_netbox/api/serializers.py +112 -7
  3. ipfabric_netbox/api/urls.py +6 -0
  4. ipfabric_netbox/api/views.py +23 -0
  5. ipfabric_netbox/choices.py +74 -40
  6. ipfabric_netbox/data/endpoint.json +52 -0
  7. ipfabric_netbox/data/filters.json +51 -0
  8. ipfabric_netbox/data/transform_map.json +190 -176
  9. ipfabric_netbox/exceptions.py +7 -5
  10. ipfabric_netbox/filtersets.py +310 -41
  11. ipfabric_netbox/forms.py +330 -80
  12. ipfabric_netbox/graphql/__init__.py +6 -0
  13. ipfabric_netbox/graphql/enums.py +5 -5
  14. ipfabric_netbox/graphql/filters.py +56 -4
  15. ipfabric_netbox/graphql/schema.py +28 -0
  16. ipfabric_netbox/graphql/types.py +61 -1
  17. ipfabric_netbox/jobs.py +12 -1
  18. ipfabric_netbox/migrations/0022_prepare_for_filters.py +182 -0
  19. ipfabric_netbox/migrations/0023_populate_filters_data.py +303 -0
  20. ipfabric_netbox/migrations/0024_finish_filters.py +29 -0
  21. ipfabric_netbox/migrations/0025_add_vss_chassis_endpoint.py +166 -0
  22. ipfabric_netbox/models.py +432 -17
  23. ipfabric_netbox/navigation.py +98 -24
  24. ipfabric_netbox/tables.py +194 -9
  25. ipfabric_netbox/templates/ipfabric_netbox/htmx_list.html +5 -0
  26. ipfabric_netbox/templates/ipfabric_netbox/inc/combined_expressions.html +59 -0
  27. ipfabric_netbox/templates/ipfabric_netbox/inc/combined_expressions_content.html +39 -0
  28. ipfabric_netbox/templates/ipfabric_netbox/inc/endpoint_filters_with_selector.html +54 -0
  29. ipfabric_netbox/templates/ipfabric_netbox/ipfabricendpoint.html +39 -0
  30. ipfabric_netbox/templates/ipfabric_netbox/ipfabricfilter.html +51 -0
  31. ipfabric_netbox/templates/ipfabric_netbox/ipfabricfilterexpression.html +39 -0
  32. ipfabric_netbox/templates/ipfabric_netbox/ipfabricfilterexpression_edit.html +150 -0
  33. ipfabric_netbox/templates/ipfabric_netbox/ipfabricsync.html +1 -1
  34. ipfabric_netbox/templates/ipfabric_netbox/ipfabrictransformmap.html +16 -2
  35. ipfabric_netbox/templatetags/ipfabric_netbox_helpers.py +68 -0
  36. ipfabric_netbox/tests/api/test_api.py +333 -13
  37. ipfabric_netbox/tests/test_filtersets.py +2592 -0
  38. ipfabric_netbox/tests/test_forms.py +1349 -74
  39. ipfabric_netbox/tests/test_models.py +242 -34
  40. ipfabric_netbox/tests/test_views.py +2031 -26
  41. ipfabric_netbox/urls.py +35 -0
  42. ipfabric_netbox/utilities/endpoint.py +83 -0
  43. ipfabric_netbox/utilities/filters.py +88 -0
  44. ipfabric_netbox/utilities/ipfutils.py +393 -377
  45. ipfabric_netbox/utilities/logging.py +7 -7
  46. ipfabric_netbox/utilities/transform_map.py +144 -5
  47. ipfabric_netbox/views.py +719 -5
  48. {ipfabric_netbox-4.3.2b9.dist-info → ipfabric_netbox-4.3.2b11.dist-info}/METADATA +2 -2
  49. {ipfabric_netbox-4.3.2b9.dist-info → ipfabric_netbox-4.3.2b11.dist-info}/RECORD +50 -33
  50. {ipfabric_netbox-4.3.2b9.dist-info → ipfabric_netbox-4.3.2b11.dist-info}/WHEEL +1 -1
@@ -21,12 +21,12 @@ from users.graphql.filters import UserFilter
21
21
 
22
22
  from ipfabric_netbox import models
23
23
  from ipfabric_netbox.graphql.enums import BranchStatusEnum
24
+ from ipfabric_netbox.graphql.enums import IPFabricFilterTypeEnum
24
25
  from ipfabric_netbox.graphql.enums import IPFabricRawDataTypeEnum
25
26
  from ipfabric_netbox.graphql.enums import IPFabricSnapshotStatusModelEnum
26
27
  from ipfabric_netbox.graphql.enums import IPFabricSourceStatusEnum
27
28
  from ipfabric_netbox.graphql.enums import IPFabricSourceTypeEnum
28
29
  from ipfabric_netbox.graphql.enums import IPFabricSyncStatusEnum
29
- from ipfabric_netbox.graphql.enums import IPFabricTransformMapSourceModelEnum
30
30
  from ipfabric_netbox.graphql.enums import JobStatusEnum
31
31
 
32
32
  __all__ = (
@@ -42,9 +42,19 @@ __all__ = (
42
42
  "IPFabricDataFilter",
43
43
  "BranchFilter",
44
44
  "JobFilter",
45
+ "IPFabricFilterFilter",
46
+ "IPFabricFilterExpressionFilter",
45
47
  )
46
48
 
47
49
 
50
+ @strawberry_django.filter(models.IPFabricEndpoint, lookups=True)
51
+ class IPFabricEndpointFilter(NetBoxModelFilterMixin):
52
+ id: ID | None = strawberry_django.filter_field()
53
+ name: FilterLookup[str] | None = strawberry_django.filter_field()
54
+ description: FilterLookup[str] | None = strawberry_django.filter_field()
55
+ endpoint: FilterLookup[str] | None = strawberry_django.filter_field()
56
+
57
+
48
58
  @strawberry_django.filter(models.IPFabricTransformMapGroup, lookups=True)
49
59
  class IPFabricTransformMapGroupFilter(NetBoxModelFilterMixin):
50
60
  id: ID | None = strawberry_django.filter_field()
@@ -56,10 +66,10 @@ class IPFabricTransformMapGroupFilter(NetBoxModelFilterMixin):
56
66
  class IPFabricTransformMapFilter(NetBoxModelFilterMixin):
57
67
  id: ID | None = strawberry_django.filter_field()
58
68
  name: FilterLookup[str] | None = strawberry_django.filter_field()
59
- source_model: (
69
+ source_endpoint: (
60
70
  Annotated[
61
- "IPFabricTransformMapSourceModelEnum",
62
- strawberry.lazy("ipfabric_netbox.graphql.enums"),
71
+ "IPFabricEndpointFilter",
72
+ strawberry.lazy("ipfabric_netbox.graphql.filters"),
63
73
  ]
64
74
  | None
65
75
  ) = strawberry_django.filter_field()
@@ -259,6 +269,48 @@ class IPFabricDataFilter(BaseFilterMixin):
259
269
  ) = strawberry_django.filter_field()
260
270
 
261
271
 
272
+ @strawberry_django.filter(models.IPFabricFilter, lookups=True)
273
+ class IPFabricFilterFilter(NetBoxModelFilterMixin):
274
+ id: ID | None = strawberry_django.filter_field()
275
+ name: FilterLookup[str] | None = strawberry_django.filter_field()
276
+ description: FilterLookup[str] | None = strawberry_django.filter_field()
277
+ endpoints: (
278
+ Annotated[
279
+ "IPFabricEndpointFilter",
280
+ strawberry.lazy("ipfabric_netbox.graphql.filters"),
281
+ ]
282
+ | None
283
+ ) = strawberry_django.filter_field()
284
+ filter_type: (
285
+ Annotated[
286
+ "IPFabricFilterTypeEnum", strawberry.lazy("ipfabric_netbox.graphql.enums")
287
+ ]
288
+ | None
289
+ ) = strawberry_django.filter_field()
290
+ syncs: (
291
+ Annotated[
292
+ "IPFabricSyncFilter", strawberry.lazy("ipfabric_netbox.graphql.filters")
293
+ ]
294
+ | None
295
+ ) = strawberry_django.filter_field()
296
+
297
+
298
+ @strawberry_django.filter(models.IPFabricFilterExpression, lookups=True)
299
+ class IPFabricFilterExpressionFilter(NetBoxModelFilterMixin):
300
+ id: ID | None = strawberry_django.filter_field()
301
+ name: FilterLookup[str] | None = strawberry_django.filter_field()
302
+ description: FilterLookup[str] | None = strawberry_django.filter_field()
303
+ expression: (
304
+ Annotated["JSONFilter", strawberry.lazy("netbox.graphql.filter_lookups")] | None
305
+ ) = strawberry_django.filter_field()
306
+ filters: (
307
+ Annotated[
308
+ "IPFabricFilterFilter", strawberry.lazy("ipfabric_netbox.graphql.filters")
309
+ ]
310
+ | None
311
+ ) = strawberry_django.filter_field()
312
+
313
+
262
314
  # These filters are not defined in the libs, so need to define them here
263
315
  @strawberry_django.filter(Branch, lookups=True)
264
316
  class BranchFilter(PrimaryModelFilterMixin):
@@ -2,6 +2,9 @@ import strawberry
2
2
  import strawberry_django
3
3
 
4
4
  from .types import IPFabricDataType
5
+ from .types import IPFabricEndpointType
6
+ from .types import IPFabricFilterExpressionType
7
+ from .types import IPFabricFilterType
5
8
  from .types import IPFabricIngestionIssueType
6
9
  from .types import IPFabricIngestionType
7
10
  from .types import IPFabricRelationshipFieldType
@@ -24,6 +27,9 @@ __all__ = (
24
27
  "IPFabricIngestionQuery",
25
28
  "IPFabricIngestionIssueQuery",
26
29
  "IPFabricDataQuery",
30
+ "IPFabricEndpointQuery",
31
+ "IPFabricFilterQuery",
32
+ "IPFabricFilterExpressionQuery",
27
33
  )
28
34
 
29
35
 
@@ -99,3 +105,25 @@ class IPFabricIngestionIssueQuery:
99
105
  class IPFabricDataQuery:
100
106
  ipfabric_data: IPFabricDataType = strawberry_django.field()
101
107
  ipfabric_data_list: list[IPFabricDataType] = strawberry_django.field()
108
+
109
+
110
+ @strawberry.type(name="Query")
111
+ class IPFabricEndpointQuery:
112
+ ipfabric_endpoint: IPFabricEndpointType = strawberry_django.field()
113
+ ipfabric_endpoint_list: list[IPFabricEndpointType] = strawberry_django.field()
114
+
115
+
116
+ @strawberry.type(name="Query")
117
+ class IPFabricFilterQuery:
118
+ ip_fabric_filter: IPFabricFilterType = strawberry_django.field()
119
+ ip_fabric_filter_list: list[IPFabricFilterType] = strawberry_django.field()
120
+
121
+
122
+ @strawberry.type(name="Query")
123
+ class IPFabricFilterExpressionQuery:
124
+ ip_fabric_filter_expression: IPFabricFilterExpressionType = (
125
+ strawberry_django.field()
126
+ )
127
+ ip_fabric_filter_expression_list: list[
128
+ IPFabricFilterExpressionType
129
+ ] = strawberry_django.field()
@@ -15,6 +15,8 @@ from users.graphql.types import UserType
15
15
 
16
16
  from .filters import BranchFilter
17
17
  from .filters import IPFabricDataFilter
18
+ from .filters import IPFabricFilterExpressionFilter
19
+ from .filters import IPFabricFilterFilter
18
20
  from .filters import IPFabricIngestionFilter
19
21
  from .filters import IPFabricIngestionIssueFilter
20
22
  from .filters import IPFabricRelationshipFieldFilter
@@ -29,6 +31,7 @@ from ipfabric_netbox import models
29
31
 
30
32
 
31
33
  __all__ = (
34
+ "IPFabricEndpointType",
32
35
  "IPFabricTransformMapGroupType",
33
36
  "IPFabricTransformMapType",
34
37
  "IPFabricTransformFieldType",
@@ -39,9 +42,21 @@ __all__ = (
39
42
  "IPFabricIngestionType",
40
43
  "IPFabricIngestionIssueType",
41
44
  "IPFabricDataType",
45
+ "IPFabricFilterType",
46
+ "IPFabricFilterExpressionType",
42
47
  )
43
48
 
44
49
 
50
+ @strawberry_django.type(
51
+ models.IPFabricEndpoint,
52
+ fields="__all__",
53
+ )
54
+ class IPFabricEndpointType(NetBoxObjectType):
55
+ name: str
56
+ description: str | None
57
+ endpoint: str
58
+
59
+
45
60
  @strawberry_django.type(
46
61
  models.IPFabricTransformMapGroup,
47
62
  fields="__all__",
@@ -57,7 +72,13 @@ class IPFabricTransformMapGroupType(NetBoxObjectType):
57
72
  )
58
73
  class IPFabricTransformMapType(NetBoxObjectType):
59
74
  name: str
60
- source_model: str
75
+ source_endpoint: (
76
+ Annotated[
77
+ "IPFabricEndpointType",
78
+ strawberry.lazy("ipfabric_netbox.graphql.types"),
79
+ ]
80
+ | None
81
+ )
61
82
  target_model: (
62
83
  Annotated[
63
84
  "ContentTypeType",
@@ -72,6 +93,12 @@ class IPFabricTransformMapType(NetBoxObjectType):
72
93
  ]
73
94
  | None
74
95
  )
96
+ parents: list[
97
+ Annotated[
98
+ "IPFabricTransformMapType",
99
+ strawberry.lazy("ipfabric_netbox.graphql.types"),
100
+ ]
101
+ ]
75
102
 
76
103
 
77
104
  @strawberry_django.type(
@@ -214,3 +241,36 @@ class IPFabricDataType(BaseObjectType):
214
241
  snapshot_data: Annotated[
215
242
  "IPFabricSnapshotType", strawberry.lazy("ipfabric_netbox.graphql.types")
216
243
  ]
244
+
245
+
246
+ @strawberry_django.type(
247
+ models.IPFabricFilter, fields="__all__", filters=IPFabricFilterFilter
248
+ )
249
+ class IPFabricFilterType(NetBoxObjectType):
250
+ name: str
251
+ description: str | None
252
+ endpoints: list[
253
+ Annotated[
254
+ "IPFabricEndpointType", strawberry.lazy("ipfabric_netbox.graphql.types")
255
+ ]
256
+ ]
257
+ filter_type: str
258
+ syncs: list[
259
+ Annotated["IPFabricSyncType", strawberry.lazy("ipfabric_netbox.graphql.types")]
260
+ ]
261
+
262
+
263
+ @strawberry_django.type(
264
+ models.IPFabricFilterExpression,
265
+ fields="__all__",
266
+ filters=IPFabricFilterExpressionFilter,
267
+ )
268
+ class IPFabricFilterExpressionType(NetBoxObjectType):
269
+ name: str
270
+ description: str | None
271
+ expression: JSON
272
+ filters: list[
273
+ Annotated[
274
+ "IPFabricFilterType", strawberry.lazy("ipfabric_netbox.graphql.types")
275
+ ]
276
+ ]
ipfabric_netbox/jobs.py CHANGED
@@ -3,8 +3,15 @@ from datetime import timedelta
3
3
 
4
4
  from core.choices import JobStatusChoices
5
5
  from core.exceptions import SyncError
6
+ from dcim.models import Site
6
7
  from dcim.models import VirtualChassis
7
8
  from dcim.signals import assign_virtualchassis_master
9
+
10
+ try:
11
+ # Got added in NetBox 4.4.9
12
+ from dcim.signals import sync_cached_scope_fields
13
+ except ImportError:
14
+ sync_cached_scope_fields = None
8
15
  from django.db.models import signals
9
16
  from netbox.context_managers import event_tracking
10
17
  from rq.timeouts import JobTimeoutException
@@ -124,12 +131,16 @@ def merge_ipfabric_ingestion(job, remove_branch=False, *args, **kwargs):
124
131
  signals.post_save.disconnect(
125
132
  assign_virtualchassis_master, sender=VirtualChassis
126
133
  )
134
+ if sync_cached_scope_fields is not None:
135
+ signals.post_save.disconnect(sync_cached_scope_fields, sender=Site)
127
136
  ingestion.sync_merge()
128
137
  finally:
129
- # Re-enable the disabled signal
138
+ # Re-enable the disabled signals
130
139
  signals.post_save.connect(
131
140
  assign_virtualchassis_master, sender=VirtualChassis
132
141
  )
142
+ if sync_cached_scope_fields is not None:
143
+ signals.post_save.connect(sync_cached_scope_fields, sender=Site)
133
144
  if remove_branch:
134
145
  branching_branch = ingestion.branch
135
146
  ingestion.branch = None
@@ -0,0 +1,182 @@
1
+ # Generated by Django 5.2.5 on 2025-12-02 12:06
2
+ import django.db.models.deletion
3
+ import netbox.models.deletion
4
+ import taggit.managers
5
+ import utilities.json
6
+ from django.db import migrations
7
+ from django.db import models
8
+
9
+
10
+ class Migration(migrations.Migration):
11
+ dependencies = [
12
+ ("extras", "0132_configcontextprofile"),
13
+ ("ipfabric_netbox", "0021_update_transform_maps"),
14
+ ]
15
+
16
+ operations = [
17
+ migrations.CreateModel(
18
+ name="IPFabricEndpoint",
19
+ fields=[
20
+ (
21
+ "id",
22
+ models.BigAutoField(
23
+ auto_created=True, primary_key=True, serialize=False
24
+ ),
25
+ ),
26
+ ("created", models.DateTimeField(auto_now_add=True, null=True)),
27
+ ("last_updated", models.DateTimeField(auto_now=True, null=True)),
28
+ (
29
+ "custom_field_data",
30
+ models.JSONField(
31
+ blank=True,
32
+ default=dict,
33
+ encoder=utilities.json.CustomFieldJSONEncoder,
34
+ ),
35
+ ),
36
+ ("name", models.CharField(max_length=100, unique=True)),
37
+ ("description", models.TextField(blank=True, null=True)),
38
+ ("endpoint", models.CharField(max_length=200, unique=True)),
39
+ (
40
+ "tags",
41
+ taggit.managers.TaggableManager(
42
+ through="extras.TaggedItem", to="extras.Tag"
43
+ ),
44
+ ),
45
+ ],
46
+ options={
47
+ "verbose_name": "IP Fabric Endpoint",
48
+ "verbose_name_plural": "IP Fabric Endpoints",
49
+ "ordering": ("pk",),
50
+ },
51
+ bases=(netbox.models.deletion.DeleteMixin, models.Model),
52
+ ),
53
+ migrations.AddField(
54
+ model_name="ipfabrictransformmap",
55
+ name="source_endpoint",
56
+ field=models.ForeignKey(
57
+ null=True,
58
+ on_delete=django.db.models.deletion.PROTECT,
59
+ related_name="transform_maps",
60
+ to="ipfabric_netbox.ipfabricendpoint",
61
+ ),
62
+ ),
63
+ migrations.AddField(
64
+ model_name="ipfabrictransformmap",
65
+ name="parents",
66
+ field=models.ManyToManyField(
67
+ blank=True,
68
+ help_text="Parent transform maps, for hierarchical organization during sync.",
69
+ related_name="children",
70
+ symmetrical=False,
71
+ to="ipfabric_netbox.ipfabrictransformmap",
72
+ ),
73
+ ),
74
+ migrations.CreateModel(
75
+ name="IPFabricFilter",
76
+ fields=[
77
+ (
78
+ "id",
79
+ models.BigAutoField(
80
+ auto_created=True, primary_key=True, serialize=False
81
+ ),
82
+ ),
83
+ ("created", models.DateTimeField(auto_now_add=True, null=True)),
84
+ ("last_updated", models.DateTimeField(auto_now=True, null=True)),
85
+ (
86
+ "custom_field_data",
87
+ models.JSONField(
88
+ blank=True,
89
+ default=dict,
90
+ encoder=utilities.json.CustomFieldJSONEncoder,
91
+ ),
92
+ ),
93
+ ("name", models.CharField(max_length=100, unique=True)),
94
+ ("description", models.TextField(blank=True, null=True)),
95
+ (
96
+ "endpoints",
97
+ models.ManyToManyField(
98
+ blank=True,
99
+ default=None,
100
+ related_name="filters",
101
+ to="ipfabric_netbox.ipfabricendpoint",
102
+ ),
103
+ ),
104
+ ("filter_type", models.CharField(max_length=10)),
105
+ (
106
+ "syncs",
107
+ models.ManyToManyField(
108
+ blank=True,
109
+ default=None,
110
+ related_name="filters",
111
+ to="ipfabric_netbox.ipfabricsync",
112
+ ),
113
+ ),
114
+ (
115
+ "tags",
116
+ taggit.managers.TaggableManager(
117
+ through="extras.TaggedItem", to="extras.Tag"
118
+ ),
119
+ ),
120
+ ],
121
+ options={
122
+ "verbose_name": "IP Fabric Filter",
123
+ "verbose_name_plural": "IP Fabric Filters",
124
+ "ordering": ("pk",),
125
+ },
126
+ bases=(netbox.models.deletion.DeleteMixin, models.Model),
127
+ ),
128
+ migrations.CreateModel(
129
+ name="IPFabricFilterExpression",
130
+ fields=[
131
+ (
132
+ "id",
133
+ models.BigAutoField(
134
+ auto_created=True, primary_key=True, serialize=False
135
+ ),
136
+ ),
137
+ ("created", models.DateTimeField(auto_now_add=True, null=True)),
138
+ ("last_updated", models.DateTimeField(auto_now=True, null=True)),
139
+ (
140
+ "custom_field_data",
141
+ models.JSONField(
142
+ blank=True,
143
+ default=dict,
144
+ encoder=utilities.json.CustomFieldJSONEncoder,
145
+ ),
146
+ ),
147
+ ("name", models.CharField(max_length=100, unique=True)),
148
+ ("description", models.TextField(blank=True, null=True)),
149
+ ("expression", models.JSONField(default=list)),
150
+ (
151
+ "filters",
152
+ models.ManyToManyField(
153
+ related_name="expressions", to="ipfabric_netbox.ipfabricfilter"
154
+ ),
155
+ ),
156
+ (
157
+ "tags",
158
+ taggit.managers.TaggableManager(
159
+ through="extras.TaggedItem", to="extras.Tag"
160
+ ),
161
+ ),
162
+ ],
163
+ options={
164
+ "verbose_name": "IP Fabric Filter Expression",
165
+ "verbose_name_plural": "IP Fabric Filter Expressions",
166
+ "ordering": ("pk",),
167
+ },
168
+ bases=(netbox.models.deletion.DeleteMixin, models.Model),
169
+ ),
170
+ # Make source_model nullable to allow reverse data migration
171
+ migrations.AlterField(
172
+ model_name="ipfabrictransformmap",
173
+ name="source_model",
174
+ field=models.CharField(max_length=50, null=True),
175
+ ),
176
+ # Extend size of name to allow for correction tags
177
+ migrations.AlterField(
178
+ model_name="ipfabrictransformmap",
179
+ name="name",
180
+ field=models.CharField(max_length=200),
181
+ ),
182
+ ]