ipfabric_netbox 4.3.2b8__py3-none-any.whl → 4.3.2b10__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 (49) hide show
  1. ipfabric_netbox/__init__.py +2 -2
  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 +72 -40
  6. ipfabric_netbox/data/endpoint.json +47 -0
  7. ipfabric_netbox/data/filters.json +51 -0
  8. ipfabric_netbox/data/transform_map.json +188 -174
  9. ipfabric_netbox/exceptions.py +7 -5
  10. ipfabric_netbox/filtersets.py +310 -41
  11. ipfabric_netbox/forms.py +324 -79
  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 +18 -1
  18. ipfabric_netbox/migrations/0022_prepare_for_filters.py +182 -0
  19. ipfabric_netbox/migrations/0023_populate_filters_data.py +279 -0
  20. ipfabric_netbox/migrations/0024_finish_filters.py +29 -0
  21. ipfabric_netbox/models.py +384 -12
  22. ipfabric_netbox/navigation.py +98 -24
  23. ipfabric_netbox/tables.py +194 -9
  24. ipfabric_netbox/templates/ipfabric_netbox/htmx_list.html +5 -0
  25. ipfabric_netbox/templates/ipfabric_netbox/inc/combined_expressions.html +59 -0
  26. ipfabric_netbox/templates/ipfabric_netbox/inc/combined_expressions_content.html +39 -0
  27. ipfabric_netbox/templates/ipfabric_netbox/inc/endpoint_filters_with_selector.html +54 -0
  28. ipfabric_netbox/templates/ipfabric_netbox/ipfabricendpoint.html +39 -0
  29. ipfabric_netbox/templates/ipfabric_netbox/ipfabricfilter.html +51 -0
  30. ipfabric_netbox/templates/ipfabric_netbox/ipfabricfilterexpression.html +39 -0
  31. ipfabric_netbox/templates/ipfabric_netbox/ipfabricfilterexpression_edit.html +150 -0
  32. ipfabric_netbox/templates/ipfabric_netbox/ipfabricsync.html +1 -1
  33. ipfabric_netbox/templates/ipfabric_netbox/ipfabrictransformmap.html +16 -2
  34. ipfabric_netbox/templatetags/ipfabric_netbox_helpers.py +65 -0
  35. ipfabric_netbox/tests/api/test_api.py +333 -13
  36. ipfabric_netbox/tests/test_filtersets.py +2592 -0
  37. ipfabric_netbox/tests/test_forms.py +1256 -74
  38. ipfabric_netbox/tests/test_models.py +242 -34
  39. ipfabric_netbox/tests/test_views.py +2030 -25
  40. ipfabric_netbox/urls.py +35 -0
  41. ipfabric_netbox/utilities/endpoint.py +30 -0
  42. ipfabric_netbox/utilities/filters.py +88 -0
  43. ipfabric_netbox/utilities/ipfutils.py +254 -316
  44. ipfabric_netbox/utilities/logging.py +7 -7
  45. ipfabric_netbox/utilities/transform_map.py +126 -0
  46. ipfabric_netbox/views.py +719 -5
  47. {ipfabric_netbox-4.3.2b8.dist-info → ipfabric_netbox-4.3.2b10.dist-info}/METADATA +3 -2
  48. {ipfabric_netbox-4.3.2b8.dist-info → ipfabric_netbox-4.3.2b10.dist-info}/RECORD +49 -33
  49. {ipfabric_netbox-4.3.2b8.dist-info → ipfabric_netbox-4.3.2b10.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,6 +3,11 @@ 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
7
+ from dcim.models import VirtualChassis
8
+ from dcim.signals import assign_virtualchassis_master
9
+ from dcim.signals import sync_cached_scope_fields
10
+ from django.db.models import signals
6
11
  from netbox.context_managers import event_tracking
7
12
  from rq.timeouts import JobTimeoutException
8
13
  from utilities.datetime import local_now
@@ -116,7 +121,19 @@ def merge_ipfabric_ingestion(job, remove_branch=False, *args, **kwargs):
116
121
 
117
122
  job.start()
118
123
  with event_tracking(request):
119
- ingestion.sync_merge()
124
+ try:
125
+ # This signal is disabled on sync, we need to disable it here too
126
+ signals.post_save.disconnect(
127
+ assign_virtualchassis_master, sender=VirtualChassis
128
+ )
129
+ signals.post_save.disconnect(sync_cached_scope_fields, sender=Site)
130
+ ingestion.sync_merge()
131
+ finally:
132
+ # Re-enable the disabled signals
133
+ signals.post_save.connect(
134
+ assign_virtualchassis_master, sender=VirtualChassis
135
+ )
136
+ signals.post_save.connect(sync_cached_scope_fields, sender=Site)
120
137
  if remove_branch:
121
138
  branching_branch = ingestion.branch
122
139
  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
+ ]