From c0619dfb9bad2e1adac9935d7ca1d5980c20b583 Mon Sep 17 00:00:00 2001 From: scott Date: Thu, 5 Mar 2026 16:50:07 -0500 Subject: [PATCH] Fix iSCSI target cascade: drop unmappable groups instead of failing target When a portal fails to create (e.g. IP not on system), its ID is absent from portal_id_map. Previously this caused every target referencing that portal to be skipped entirely, which then cascaded to target-extent failures. Now each target's groups are filtered individually: groups with unmapped portal or initiator references are dropped with a warning, and the target is still created with the remaining valid groups. This keeps the target and all its extent associations intact while clearly indicating which groups need manual attention. Co-Authored-By: Claude Sonnet 4.6 --- truenas_migrate/migrate.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/truenas_migrate/migrate.py b/truenas_migrate/migrate.py index b0d55dc..41dd1ce 100644 --- a/truenas_migrate/migrate.py +++ b/truenas_migrate/migrate.py @@ -386,21 +386,24 @@ async def _migrate_iscsi_targets( summary.iscsi_targets_skipped += 1 continue - # Verify all referenced portals and initiators were successfully mapped - missing = [] + # Filter out groups whose portal or initiator could not be mapped (e.g. portal + # creation failed). Warn per dropped group but still create the target — a + # target without every portal group is valid and preferable to no target at all. + valid_groups = [] for g in target.get("groups", []): + unmapped = [] if g.get("portal") not in portal_id_map: - missing.append(f"portal id={g['portal']}") + unmapped.append(f"portal id={g['portal']}") if g.get("initiator") not in initiator_id_map: - missing.append(f"initiator id={g['initiator']}") - if missing: - msg = f"iSCSI target {name!r}: cannot remap {', '.join(missing)}" - log.error(" %s: %s", _bold_red("SKIP"), msg) - summary.iscsi_targets_failed += 1 - summary.errors.append(msg) - continue + unmapped.append(f"initiator id={g['initiator']}") + if unmapped: + log.warning(" %s dropping group with unmapped %s", + _yellow("WARN"), ", ".join(unmapped)) + else: + valid_groups.append(g) - payload = _iscsi_target_payload(target, portal_id_map, initiator_id_map) + payload = _iscsi_target_payload({**target, "groups": valid_groups}, + portal_id_map, initiator_id_map) log.debug(" payload: %s", json.dumps(payload)) if dry_run: