fix: SCALE 25.04 compatibility — integer types and alias uniqueness (#264)
TrueNAS SCALE 25.04 introduced two breaking changes in its iSCSI API:
1. Unique alias constraint: POST /iscsi/target with alias collides on retry.
Remove alias field entirely — names are already unique per-VM.
2. Pydantic v2 strict integer validation: rejects JSON strings ("7") where
integers are expected. Perl scalars become dual-vars (string+int flags)
after log interpolation or hash-key lookups, causing encode_json to emit
quoted values. Fix: int() at all integer-ID pass sites (portal, target,
extent, lun) and omit null initiator/auth instead of sending explicit null.
Confirmed: alloc_image works on SCALE 25.04, SCALE 24.10, and CORE 13.0-U6.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7a66e59dc7
commit
96957c037f
|
|
@ -277,19 +277,25 @@ sub _portal_groups_for_new_target {
|
|||
return _clean_groups(\@gs) if @gs;
|
||||
}
|
||||
|
||||
# No existing targets — use first matching portal, no initiator restriction
|
||||
# No existing targets — use first matching portal, no initiator restriction.
|
||||
# Force int(): hash keys are always Perl strings; encode_json must see an IV
|
||||
# to emit "portal":1 rather than "portal":"1" (rejected by Pydantic v2).
|
||||
my ($pid) = keys %our_portal_ids;
|
||||
return [ { portal => $pid, initiator => undef, authmethod => 'NONE', auth => undef } ];
|
||||
return [ { portal => int($pid), authmethod => 'NONE' } ];
|
||||
}
|
||||
|
||||
sub _clean_groups {
|
||||
my ($gs) = @_;
|
||||
return [ map { {
|
||||
portal => $_->{portal},
|
||||
initiator => $_->{initiator},
|
||||
authmethod => $_->{authmethod} // 'NONE',
|
||||
auth => $_->{auth},
|
||||
} } @$gs ];
|
||||
return [ map {
|
||||
my %g = (
|
||||
portal => int($_->{portal}),
|
||||
authmethod => $_->{authmethod} // 'NONE',
|
||||
);
|
||||
# Omit null integer fields — Pydantic v2 (SCALE 25.04+) is strict about types
|
||||
$g{initiator} = int($_->{initiator}) if defined $_->{initiator};
|
||||
$g{auth} = int($_->{auth}) if defined $_->{auth};
|
||||
\%g
|
||||
} @$gs ];
|
||||
}
|
||||
|
||||
# Finds or creates the per-VM iSCSI target for $vmid.
|
||||
|
|
@ -314,7 +320,6 @@ sub _resolve_vm_target {
|
|||
my $groups = _portal_groups_for_new_target($scfg);
|
||||
my $new = _api($scfg, 'POST', '/iscsi/target', {
|
||||
name => $target_name,
|
||||
alias => "Proxmox VM $vmid",
|
||||
mode => 'ISCSI',
|
||||
groups => $groups,
|
||||
});
|
||||
|
|
@ -323,7 +328,9 @@ sub _resolve_vm_target {
|
|||
_log('info', "Created per-VM target: $iqn (id=$t_id)");
|
||||
}
|
||||
|
||||
$state->{$host}{vm_targets}{$vmid} = { id => $t_id, iqn => $iqn };
|
||||
# int() after _log interpolation: string-interpolating $t_id sets its PV flag,
|
||||
# turning it into a dual-var that encode_json encodes as a string.
|
||||
$state->{$host}{vm_targets}{$vmid} = { id => int($t_id), iqn => $iqn };
|
||||
return $state->{$host}{vm_targets}{$vmid};
|
||||
}
|
||||
|
||||
|
|
@ -353,8 +360,8 @@ sub _next_lun_id {
|
|||
my $tes = _api($scfg, 'GET', "/iscsi/targetextent?target=$target_id") // [];
|
||||
my %used = map { $_->{lunid} => 1 } @$tes;
|
||||
my $lun = 0;
|
||||
$lun++ while $used{$lun};
|
||||
return $lun;
|
||||
$lun++ while $used{$lun}; # hash-key lookup stringifies $lun; int() restores IV type
|
||||
return int($lun);
|
||||
}
|
||||
|
||||
# Returns { extent_id, targetextent_id, lun_id, target_id } for a volname,
|
||||
|
|
@ -449,10 +456,13 @@ sub alloc_image {
|
|||
my $target = _resolve_vm_target($scfg, $vmid);
|
||||
my $lun_id = _next_lun_id($scfg, $target->{id});
|
||||
|
||||
# int() forces fresh IV scalars — decode_json IDs can become dual-vars
|
||||
# (string+int) after log interpolation or hash-key lookup, which causes
|
||||
# encode_json to emit "7" instead of 7, rejected by Pydantic v2.
|
||||
_api($scfg, 'POST', '/iscsi/targetextent', {
|
||||
target => $target->{id},
|
||||
extent => $extent->{id},
|
||||
lunid => $lun_id,
|
||||
target => int($target->{id}),
|
||||
extent => int($extent->{id}),
|
||||
lunid => int($lun_id),
|
||||
});
|
||||
|
||||
_reload_iscsi($scfg);
|
||||
|
|
|
|||
Loading…
Reference in New Issue