Fix dry-run iSCSI ID map cascades; add pre-migration existence check
During dry run, "would create" iSCSI objects now populate id_map with a source-ID placeholder so downstream objects (targets, target-extents) can remap references without cascading failures. Adds query_existing_iscsi() and clear_iscsi_config() to migrate.py, and _prompt_clear_existing_iscsi() to the wizard: if the destination already has iSCSI config, the user is shown a summary and offered Keep/Remove before the dry run begins. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -239,6 +239,7 @@ async def _migrate_iscsi_extents(
|
||||
_cyan("[DRY RUN]"), _bold_cyan(repr(name)),
|
||||
ext.get("disk") or ext.get("path"))
|
||||
summary.iscsi_extents_created += 1
|
||||
id_map[ext["id"]] = ext["id"] # placeholder — enables downstream dry-run remapping
|
||||
if ext.get("type") == "DISK" and ext.get("disk"):
|
||||
summary.zvols_to_check.append(ext["disk"].removeprefix("zvol/"))
|
||||
continue
|
||||
@@ -288,6 +289,7 @@ async def _migrate_iscsi_initiators(
|
||||
log.info(" %s would create initiator group %s",
|
||||
_cyan("[DRY RUN]"), _bold_cyan(repr(comment)))
|
||||
summary.iscsi_initiators_created += 1
|
||||
id_map[init["id"]] = init["id"] # placeholder — enables downstream dry-run remapping
|
||||
continue
|
||||
|
||||
try:
|
||||
@@ -341,6 +343,7 @@ async def _migrate_iscsi_portals(
|
||||
log.info(" %s would create portal %s → %s",
|
||||
_cyan("[DRY RUN]"), _bold_cyan(repr(comment)), ips)
|
||||
summary.iscsi_portals_created += 1
|
||||
id_map[portal["id"]] = portal["id"] # placeholder — enables downstream dry-run remapping
|
||||
continue
|
||||
|
||||
try:
|
||||
@@ -404,6 +407,7 @@ async def _migrate_iscsi_targets(
|
||||
log.info(" %s would create target %s",
|
||||
_cyan("[DRY RUN]"), _bold_cyan(repr(name)))
|
||||
summary.iscsi_targets_created += 1
|
||||
id_map[target["id"]] = target["id"] # placeholder — enables downstream dry-run remapping
|
||||
continue
|
||||
|
||||
try:
|
||||
@@ -480,6 +484,56 @@ async def _migrate_iscsi_targetextents(
|
||||
f"iSCSI target-extent (target={dest_tid}, lun={lunid}): {exc}")
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# iSCSI pre-migration utilities
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
async def query_existing_iscsi(client: TrueNASClient) -> dict:
|
||||
"""
|
||||
Query all iSCSI object counts from the destination.
|
||||
Returns a dict with keys: extents, initiators, portals, targets, targetextents
|
||||
Each value is a list of objects (may be empty).
|
||||
"""
|
||||
result = {}
|
||||
for key, method in [
|
||||
("extents", "iscsi.extent.query"),
|
||||
("initiators", "iscsi.initiator.query"),
|
||||
("portals", "iscsi.portal.query"),
|
||||
("targets", "iscsi.target.query"),
|
||||
("targetextents", "iscsi.targetextent.query"),
|
||||
]:
|
||||
try:
|
||||
result[key] = await client.call(method) or []
|
||||
except RuntimeError:
|
||||
result[key] = []
|
||||
return result
|
||||
|
||||
|
||||
async def clear_iscsi_config(client: TrueNASClient) -> None:
|
||||
"""
|
||||
Delete all iSCSI configuration from the destination in safe dependency order:
|
||||
target-extents → targets → portals → initiators → extents.
|
||||
"""
|
||||
for method_query, method_delete, label in [
|
||||
("iscsi.targetextent.query", "iscsi.targetextent.delete", "target-extent"),
|
||||
("iscsi.target.query", "iscsi.target.delete", "target"),
|
||||
("iscsi.portal.query", "iscsi.portal.delete", "portal"),
|
||||
("iscsi.initiator.query", "iscsi.initiator.delete", "initiator"),
|
||||
("iscsi.extent.query", "iscsi.extent.delete", "extent"),
|
||||
]:
|
||||
try:
|
||||
objects = await client.call(method_query) or []
|
||||
except RuntimeError as exc:
|
||||
log.warning(" Could not query iSCSI %ss: %s", label, exc)
|
||||
continue
|
||||
for obj in objects:
|
||||
try:
|
||||
await client.call(method_delete, [obj["id"]])
|
||||
log.info(" Deleted iSCSI %s id=%s", label, obj["id"])
|
||||
except RuntimeError as exc:
|
||||
log.warning(" Failed to delete iSCSI %s id=%s: %s", label, obj["id"], exc)
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Public iSCSI entry point
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user