Add iSCSI migration support (SCALE archive source)
archive.py: - Add iscsi_config.json to _CANDIDATES and _KEYWORDS - parse_archive() now extracts portals, initiators, targets, extents, targetextents, and global_config into archive["iscsi"] migrate.py: - Add payload builders for all five iSCSI object types (extents, initiators, portals, targets, target-extents) - Add migrate_iscsi() which creates objects in dependency order (extents+initiators first, then portals, then targets, then target-extent associations) and tracks old→new ID mappings at each step so downstream references are correctly remapped - Conflict detection: extents/targets by name, portals by IP set, initiators by comment, target-extents by target+LUN combination - Skipped objects still populate the ID map so dependent objects can remap their references correctly summary.py: - Add per-sub-type found/created/skipped/failed counters for iSCSI - iSCSI rows appear in the report only when iSCSI data was processed cli.py: - Add _prompt_iscsi_portals() — shows source IPs per portal and prompts for destination IPs in-place; supports MPIO (space-separated) - Wizard scope menu gains option 3 (iSCSI); portal prompt fires automatically after archive parse when iSCSI portals are present - run() wires in migrate_iscsi() - argparse --migrate now accepts "iscsi" as a valid choice Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -21,12 +21,43 @@ class Summary:
|
||||
nfs_skipped: int = 0
|
||||
nfs_failed: int = 0
|
||||
|
||||
iscsi_extents_found: int = 0
|
||||
iscsi_extents_created: int = 0
|
||||
iscsi_extents_skipped: int = 0
|
||||
iscsi_extents_failed: int = 0
|
||||
|
||||
iscsi_initiators_found: int = 0
|
||||
iscsi_initiators_created: int = 0
|
||||
iscsi_initiators_skipped: int = 0
|
||||
iscsi_initiators_failed: int = 0
|
||||
|
||||
iscsi_portals_found: int = 0
|
||||
iscsi_portals_created: int = 0
|
||||
iscsi_portals_skipped: int = 0
|
||||
iscsi_portals_failed: int = 0
|
||||
|
||||
iscsi_targets_found: int = 0
|
||||
iscsi_targets_created: int = 0
|
||||
iscsi_targets_skipped: int = 0
|
||||
iscsi_targets_failed: int = 0
|
||||
|
||||
iscsi_targetextents_found: int = 0
|
||||
iscsi_targetextents_created: int = 0
|
||||
iscsi_targetextents_skipped: int = 0
|
||||
iscsi_targetextents_failed: int = 0
|
||||
|
||||
errors: list[str] = field(default_factory=list)
|
||||
|
||||
# Populated during dry-run dataset safety checks
|
||||
paths_to_create: list[str] = field(default_factory=list)
|
||||
missing_datasets: list[str] = field(default_factory=list)
|
||||
|
||||
@property
|
||||
def _has_iscsi(self) -> bool:
|
||||
return (self.iscsi_extents_found + self.iscsi_initiators_found +
|
||||
self.iscsi_portals_found + self.iscsi_targets_found +
|
||||
self.iscsi_targetextents_found) > 0
|
||||
|
||||
def report(self) -> str:
|
||||
w = 60
|
||||
|
||||
@@ -34,6 +65,14 @@ class Summary:
|
||||
s = f"{label}={n}"
|
||||
return color_fn(s) if n > 0 else _dim(s)
|
||||
|
||||
def _iscsi_val(found, created, skipped, failed) -> str:
|
||||
return (
|
||||
f"{_dim('found=' + str(found))} "
|
||||
f"{_stat('created', created, _bold_green)} "
|
||||
f"{_stat('skipped', skipped, _yellow)} "
|
||||
f"{_stat('failed', failed, _bold_red)}"
|
||||
)
|
||||
|
||||
smb_val = (
|
||||
f"{_dim('found=' + str(self.smb_found))} "
|
||||
f"{_stat('created', self.smb_created, _bold_green)} "
|
||||
@@ -67,11 +106,30 @@ class Summary:
|
||||
f"{tl}{hr}{tr}",
|
||||
title_row,
|
||||
f"{ml}{hr}{mr}",
|
||||
row("SMB shares : ", smb_val),
|
||||
row("NFS shares : ", nfs_val),
|
||||
f"{bl}{hr}{br}",
|
||||
row("SMB shares : ", smb_val),
|
||||
row("NFS shares : ", nfs_val),
|
||||
]
|
||||
|
||||
if self._has_iscsi:
|
||||
lines.append(f"{ml}{hr}{mr}")
|
||||
lines.append(row("iSCSI extents : ", _iscsi_val(
|
||||
self.iscsi_extents_found, self.iscsi_extents_created,
|
||||
self.iscsi_extents_skipped, self.iscsi_extents_failed)))
|
||||
lines.append(row("iSCSI initiators: ", _iscsi_val(
|
||||
self.iscsi_initiators_found, self.iscsi_initiators_created,
|
||||
self.iscsi_initiators_skipped, self.iscsi_initiators_failed)))
|
||||
lines.append(row("iSCSI portals : ", _iscsi_val(
|
||||
self.iscsi_portals_found, self.iscsi_portals_created,
|
||||
self.iscsi_portals_skipped, self.iscsi_portals_failed)))
|
||||
lines.append(row("iSCSI targets : ", _iscsi_val(
|
||||
self.iscsi_targets_found, self.iscsi_targets_created,
|
||||
self.iscsi_targets_skipped, self.iscsi_targets_failed)))
|
||||
lines.append(row("iSCSI tgt↔ext : ", _iscsi_val(
|
||||
self.iscsi_targetextents_found, self.iscsi_targetextents_created,
|
||||
self.iscsi_targetextents_skipped, self.iscsi_targetextents_failed)))
|
||||
|
||||
lines.append(f"{bl}{hr}{br}")
|
||||
|
||||
if self.errors:
|
||||
lines.append(f"\n {_bold_red(str(len(self.errors)) + ' error(s):')} ")
|
||||
for e in self.errors:
|
||||
|
||||
Reference in New Issue
Block a user