Skip to content

Commit 51274f2

Browse files
authored
Added Post-import Hook (#205)
* add: Post Import record hook, rename related for clarity - Renamed `SourceAdapter.post_import` => `post_load` - Renamed `SourceModelWrapper.post_import` => `post_process_references` - Renamed `PreImport` => `PreImportRecord` * fix: Linters * fix: Tests
1 parent c5438f8 commit 51274f2

File tree

9 files changed

+56
-37
lines changed

9 files changed

+56
-37
lines changed

changes/205.added

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added post-import hook.

nautobot_netbox_importer/diffsync/adapters/netbox.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ def load(self) -> None:
9292
fix_power_feed_locations(self)
9393
if self.options.unrack_zero_uheight_devices:
9494
unrack_zero_uheight_devices(self)
95-
self.post_import()
95+
96+
self.post_load()
9697

9798
def import_to_nautobot(self) -> None:
9899
"""Import a NetBox export file into Nautobot."""

nautobot_netbox_importer/diffsync/models/custom_fields.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
EMPTY_VALUES,
99
DiffSyncBaseModel,
1010
ImporterPass,
11-
PreImportResult,
11+
PreImportRecordResult,
1212
SourceAdapter,
1313
SourceField,
1414
fields,
@@ -95,14 +95,14 @@ def setup(adapter: SourceAdapter) -> None:
9595
"""Map NetBox custom fields to Nautobot."""
9696
choice_sets = {}
9797

98-
def create_choice_set(source: RecordData, importer_pass: ImporterPass) -> PreImportResult:
98+
def create_choice_set(source: RecordData, importer_pass: ImporterPass) -> PreImportRecordResult:
9999
if importer_pass == ImporterPass.DEFINE_STRUCTURE:
100100
choice_sets[source.get("id")] = [
101101
*_convert_choices(source.get("base_choices")),
102102
*_convert_choices(source.get("extra_choices")),
103103
]
104104

105-
return PreImportResult.USE_RECORD
105+
return PreImportRecordResult.USE_RECORD
106106

107107
def define_choice_set(field: SourceField) -> None:
108108
def choices_importer(source: RecordData, target: DiffSyncBaseModel) -> None:
@@ -144,7 +144,7 @@ def create_choices(choices: list, custom_field_uid: Uid) -> None:
144144
# Defined in NetBox but not in Nautobot
145145
adapter.configure_model(
146146
"extras.CustomFieldChoiceSet",
147-
pre_import=create_choice_set,
147+
pre_import_record=create_choice_set,
148148
)
149149

150150
adapter.configure_model(

nautobot_netbox_importer/diffsync/models/dcim.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@
44
from uuid import UUID
55

66
from nautobot_netbox_importer.base import RecordData
7-
from nautobot_netbox_importer.generator import DiffSyncBaseModel, PreImportResult, SourceAdapter, SourceField, fields
7+
from nautobot_netbox_importer.generator import (
8+
DiffSyncBaseModel,
9+
PreImportRecordResult,
10+
SourceAdapter,
11+
SourceField,
12+
fields,
13+
)
814

915
from .locations import define_location
1016

@@ -39,13 +45,13 @@ def units_importer(source: RecordData, target: DiffSyncBaseModel) -> None:
3945
field.set_importer(units_importer)
4046

4147

42-
def _pre_import_cable_termination(source: RecordData, _) -> PreImportResult:
48+
def _pre_import_cable_termination(source: RecordData, _) -> PreImportRecordResult:
4349
cable_end = source.pop("cable_end").lower()
4450
source["id"] = source.pop("cable")
4551
source[f"termination_{cable_end}_type"] = source.pop("termination_type")
4652
source[f"termination_{cable_end}_id"] = source.pop("termination_id")
4753

48-
return PreImportResult.USE_RECORD
54+
return PreImportRecordResult.USE_RECORD
4955

5056

5157
def setup(adapter: SourceAdapter) -> None:
@@ -69,7 +75,7 @@ def setup(adapter: SourceAdapter) -> None:
6975
adapter.configure_model(
7076
"dcim.cabletermination",
7177
extend_content_type="dcim.cable",
72-
pre_import=_pre_import_cable_termination,
78+
pre_import_record=_pre_import_cable_termination,
7379
)
7480
adapter.configure_model(
7581
"dcim.interface",

nautobot_netbox_importer/diffsync/models/object_change.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
"""NetBox to Nautobot Object Change Model Mapping."""
22

33
from nautobot_netbox_importer.base import RecordData
4-
from nautobot_netbox_importer.generator import ImporterPass, PreImportResult, SourceAdapter, fields
4+
from nautobot_netbox_importer.generator import ImporterPass, PreImportRecordResult, SourceAdapter, fields
55

66

77
def setup(adapter: SourceAdapter) -> None:
88
"""Map NetBox object change to Nautobot."""
99

10-
def skip_disabled_object_types(source: RecordData, importer_pass: ImporterPass) -> PreImportResult:
10+
def skip_disabled_object_types(source: RecordData, importer_pass: ImporterPass) -> PreImportRecordResult:
1111
"""Disabled object types are not in Nautobot and should be skipped."""
1212
if importer_pass != ImporterPass.IMPORT_DATA:
13-
return PreImportResult.USE_RECORD
13+
return PreImportRecordResult.USE_RECORD
1414
object_type = source.get("changed_object_type", None)
1515
wrapper = adapter.get_or_create_wrapper(object_type)
16-
return PreImportResult.SKIP_RECORD if wrapper.disable_reason else PreImportResult.USE_RECORD
16+
return PreImportRecordResult.SKIP_RECORD if wrapper.disable_reason else PreImportRecordResult.USE_RECORD
1717

1818
adapter.configure_model(
1919
"extras.ObjectChange",
20-
pre_import=skip_disabled_object_types,
20+
pre_import_record=skip_disabled_object_types,
2121
disable_related_reference=True,
2222
fields={
2323
"postchange_data": "object_data",

nautobot_netbox_importer/generator/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
DiffSyncBaseModel,
88
ImporterPass,
99
InvalidChoiceValueIssue,
10-
PreImportResult,
10+
PreImportRecordResult,
1111
SourceAdapter,
1212
SourceContentType,
1313
SourceDataGenerator,
@@ -26,7 +26,7 @@
2626
"InternalFieldType",
2727
"InvalidChoiceValueIssue",
2828
"NautobotAdapter",
29-
"PreImportResult",
29+
"PreImportRecordResult",
3030
"SourceAdapter",
3131
"SourceContentType",
3232
"SourceDataGenerator",

nautobot_netbox_importer/generator/fields.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
ImporterPass,
1919
InternalFieldType,
2020
InvalidChoiceValueIssue,
21-
PreImportResult,
21+
PreImportRecordResult,
2222
RecordData,
2323
SourceAdapter,
2424
SourceContentType,
@@ -116,7 +116,7 @@ def role(
116116
e.g., RackRole with `name = "Network"` and DeviceRole with `name = "Network"` to avoid duplicates.
117117
"""
118118

119-
def cache_roles(source: RecordData, importer_pass: ImporterPass) -> PreImportResult:
119+
def cache_roles(source: RecordData, importer_pass: ImporterPass) -> PreImportRecordResult:
120120
if importer_pass == ImporterPass.DEFINE_STRUCTURE:
121121
name = source.get("name", "").capitalize()
122122
if not name:
@@ -126,12 +126,12 @@ def cache_roles(source: RecordData, importer_pass: ImporterPass) -> PreImportRes
126126
if not uid:
127127
_ROLE_NAME_TO_UID_CACHE[name] = nautobot_uid
128128

129-
return PreImportResult.USE_RECORD
129+
return PreImportRecordResult.USE_RECORD
130130

131131
role_wrapper = adapter.configure_model(
132132
source_content_type,
133133
nautobot_content_type="extras.role",
134-
pre_import=cache_roles,
134+
pre_import_record=cache_roles,
135135
identifiers=("name",),
136136
fields={
137137
# Include color to allow setting the default Nautobot value, import fails without it.

nautobot_netbox_importer/generator/source.py

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ class ImporterPass(Enum):
131131
IMPORT_DATA = 2
132132

133133

134-
class PreImportResult(Enum):
134+
class PreImportRecordResult(Enum):
135135
"""Pre Import Response."""
136136

137137
SKIP_RECORD = False
@@ -149,7 +149,8 @@ class SourceFieldSource(Enum):
149149
IDENTIFIER = auto() # Fields used as identifiers
150150

151151

152-
PreImport = Callable[[RecordData, ImporterPass], PreImportResult]
152+
PreImportRecord = Callable[[RecordData, ImporterPass], PreImportRecordResult]
153+
PostImportRecord = Callable[[RecordData, DiffSyncBaseModel], None]
153154
SourceDataGenerator = Callable[[], Iterable[SourceRecord]]
154155
SourceFieldImporter = Callable[[RecordData, DiffSyncBaseModel], None]
155156
GetPkFromData = Callable[[RecordData], Uid]
@@ -205,7 +206,8 @@ def configure_model(
205206
default_reference: Optional[RecordData] = None,
206207
flags: Optional[DiffSyncModelFlags] = None,
207208
nautobot_flags: Optional[DiffSyncModelFlags] = None,
208-
pre_import: Optional[PreImport] = None,
209+
pre_import_record: Optional[PreImportRecord] = None,
210+
post_import_record: Optional[PostImportRecord] = None,
209211
disable_related_reference: Optional[bool] = None,
210212
forward_references: Optional[ForwardReferences] = None,
211213
fill_dummy_data: Optional[FillDummyData] = None,
@@ -257,8 +259,10 @@ def configure_model(
257259
wrapper.flags = flags
258260
if nautobot_flags is not None:
259261
wrapper.nautobot.flags = nautobot_flags
260-
if pre_import:
261-
wrapper.pre_import = pre_import
262+
if pre_import_record:
263+
wrapper.pre_import_record = pre_import_record
264+
if post_import_record:
265+
wrapper.post_import_record = post_import_record
262266
if disable_related_reference is not None:
263267
wrapper.disable_related_reference = disable_related_reference
264268
if forward_references:
@@ -349,7 +353,7 @@ def get_nautobot_content_type_uid(self, content_type: ContentTypeValue) -> int:
349353
def load(self) -> None:
350354
"""Load data from the source."""
351355
self.import_data()
352-
self.post_import()
356+
self.post_load()
353357

354358
def import_data(self) -> None:
355359
"""Import data from the source."""
@@ -379,9 +383,9 @@ def import_data(self) -> None:
379383
for content_type, data in get_source_data():
380384
self.wrappers[content_type].second_pass(data)
381385

382-
def post_import(self) -> None:
386+
def post_load(self) -> None:
383387
"""Post import processing."""
384-
while any(wrapper.post_import() for wrapper in self.wrappers.values()):
388+
while any(wrapper.post_process_references() for wrapper in self.wrappers.values()):
385389
pass
386390

387391
for nautobot_wrapper in self.get_imported_nautobot_wrappers():
@@ -458,7 +462,8 @@ def __init__(self, adapter: SourceAdapter, content_type: ContentTypeStr, nautobo
458462

459463
# Source fields defintions
460464
self.fields: OrderedDict[FieldName, SourceField] = OrderedDict()
461-
self.pre_import: Optional[PreImport] = None
465+
self.pre_import_record: Optional[PreImportRecord] = None
466+
self.post_import_record: Optional[PostImportRecord] = None
462467

463468
if self.disable_reason:
464469
self.adapter.logger.debug("Created disabled %s", self)
@@ -504,8 +509,8 @@ def cache_record_uids(self, source: RecordData, nautobot_uid: Optional[Uid] = No
504509

505510
def first_pass(self, data: RecordData) -> None:
506511
"""Firts pass of data import."""
507-
if self.pre_import:
508-
if self.pre_import(data, ImporterPass.DEFINE_STRUCTURE) != PreImportResult.USE_RECORD:
512+
if self.pre_import_record:
513+
if self.pre_import_record(data, ImporterPass.DEFINE_STRUCTURE) != PreImportRecordResult.USE_RECORD:
509514
self.stats.first_pass_skipped += 1
510515
return
511516

@@ -522,14 +527,17 @@ def second_pass(self, data: RecordData) -> None:
522527
if self.disable_reason:
523528
return
524529

525-
if self.pre_import:
526-
if self.pre_import(data, ImporterPass.IMPORT_DATA) != PreImportResult.USE_RECORD:
530+
if self.pre_import_record:
531+
if self.pre_import_record(data, ImporterPass.IMPORT_DATA) != PreImportRecordResult.USE_RECORD:
527532
self.stats.second_pass_skipped += 1
528533
return
529534

530535
self.stats.second_pass_used += 1
531536

532-
self.import_record(data)
537+
target = self.import_record(data)
538+
539+
if self.post_import_record:
540+
self.post_import_record(data, target)
533541

534542
def get_summary(self, content_type_id) -> SourceModelSummary:
535543
"""Get a summary of the model."""
@@ -544,7 +552,8 @@ def get_summary(self, content_type_id) -> SourceModelSummary:
544552
identifiers=self.identifiers,
545553
disable_related_reference=self.disable_related_reference,
546554
forward_references=self.forward_references and self.forward_references.__name__ or None,
547-
pre_import=self.pre_import and self.pre_import.__name__ or None,
555+
pre_import=self.pre_import_record and self.pre_import_record.__name__ or None,
556+
post_import=self.post_import_record and self.post_import_record.__name__ or None,
548557
fields=sorted(fields, key=lambda field: field.name),
549558
flags=str(self.flags),
550559
default_reference_uid=serialize_to_summary(self.default_reference_uid),
@@ -817,7 +826,7 @@ def set_default_reference(self, data: RecordData) -> None:
817826
"""Set the default reference to this model."""
818827
self.default_reference_uid = self.cache_record(data)
819828

820-
def post_import(self) -> bool:
829+
def post_process_references(self) -> bool:
821830
"""Post import processing.
822831
823832
Assigns referenced content_types to referencing instances.

nautobot_netbox_importer/summary.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ class SourceModelSummary(NamedTuple):
110110
disable_related_reference: Whether related references are disabled
111111
forward_references: Configuration for forward references handling
112112
pre_import: Pre-import processing function/method name
113+
post_import: Post-import processing function/method name
113114
fields: List of field summaries for this model
114115
flags: Feature flags applied to this model
115116
default_reference_uid: Default UID used for reference when actual reference is missing
@@ -124,11 +125,12 @@ class SourceModelSummary(NamedTuple):
124125
identifiers: Optional[List[FieldName]]
125126
disable_related_reference: bool
126127
forward_references: Optional[str]
127-
pre_import: Optional[str]
128128
fields: List[FieldSummary]
129129
flags: str
130130
default_reference_uid: Optional[Uid]
131131
stats: SourceModelStats
132+
pre_import: Optional[str] = None
133+
post_import: Optional[str] = None
132134

133135

134136
class NautobotModelSummary(NamedTuple):

0 commit comments

Comments
 (0)