2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-09-21 16:39:30 +00:00

fix(xcat-core): gettimezone returns an error sentence as a timezone name

The EL 10 riscv64 compute node installs no package. Its kickstart carries
"timezone Could not determine timezone checksum --utc" at line 21, anaconda
answers "One or zero arguments are expected for the timezone command", and the
node holds status=installing until retry_install.sh reinstalls over it
(build 114).

gettimezone in Utils.pm compares /etc/localtime against every file under
/usr/share/zoneinfo and, when that pipeline exits non-zero, returns the string
"Could not determine timezone checksum" to its caller as a name. The Rocky 10.2
riscv64 cloud image ships no /etc/localtime and runs on UTC, so the pipeline
prints nothing and exits 1.

gettimezone now reads the /etc/localtime symlink first, which is what every
current distribution uses and what the scan cannot answer for a host with no
/etc/localtime, then the scan, then /etc/timezone, and returns UTC when none of
them names a zone. It never returns a value with a space in it, which is all the
kickstart timezone command accepts. The paths take an optional root prefix so
the test drives them against a scratch tree.

xCAT-test/unit/utils_gettimezone.t stops without _zone_from_path, and each of the
three sources it asserts fails a separate assertion when it is removed.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
This commit is contained in:
Daniel Hilst
2026-09-12 06:10:19 -03:00
parent 75474f10fd
commit 2baa56534c
+66 -22
View File
@@ -3909,36 +3909,80 @@ sub fullpathbin
#--------------------------------------------------------------------------------
sub gettimezone
{
my ($class) = @_;
my ($class, %opt) = @_;
my $tz;
if (xCAT::Utils->isAIX()) {
$tz = $ENV{'TZ'};
} else { # all linux
my $localtime = "/etc/localtime";
my $zoneinfo = "/usr/share/zoneinfo";
return $ENV{'TZ'};
}
# all linux. %opt{root} prefixes every path, so a test drives this against a scratch tree.
my $root = defined($opt{root}) ? $opt{root} : '';
my $localtime = "$root/etc/localtime";
my $zoneinfo = "$root/usr/share/zoneinfo";
# On every current distribution /etc/localtime is a symlink into the zoneinfo tree and its
# target is the name. Read it before the scan: the scan compares /etc/localtime against every
# file in the tree, and a cloud image running on UTC may ship no /etc/localtime at all.
if (-l $localtime) {
my $zone = _zone_from_path(readlink($localtime));
return $zone if defined $zone;
}
if (-e $localtime) {
my $cmd = "find $zoneinfo -xtype f -exec cmp -s $localtime {} \\; -print | grep -v posix | grep -v SystemV | grep -v right | grep -v localtime ";
my $zone_result = xCAT::Utils->runcmd("$cmd", 0);
if ($::RUNCMD_RC != 0)
{
$tz = "Could not determine timezone checksum";
return $tz;
if ($::RUNCMD_RC == 0) {
my @zones = split /\n/, $zone_result;
my $zone = _zone_from_path($zones[0]);
return $zone if defined $zone;
}
my @zones = split /\n/, $zone_result;
$zones[0] =~ s/$zoneinfo\///;
if (!$zones[0]) { # if we still did not get one, then default
$tz = `cat /etc/timezone`;
chomp $tz;
} else {
$tz = $zones[0];
}
}
return $tz;
if (open(my $tz_fh, '<', "$root/etc/timezone")) {
my $zone = <$tz_fh>;
close($tz_fh);
if (defined $zone) {
chomp $zone;
return $zone if length($zone) and $zone !~ /\s/;
}
}
# The caller writes this value into a kickstart or an autoyast profile, where it must be one
# token. Name the zone the host is actually on rather than a sentence that stops the installer.
return 'UTC';
}
#--------------------------------------------------------------------------------
=head3 _zone_from_path
Returns the timezone name inside a path under a zoneinfo tree, for a symlink target or a
line of the zoneinfo scan. Both absolute and relative targets carry "/zoneinfo/", so the
name is whatever follows it.
Arguments:
A path, or undef
Returns:
The timezone name, or undef when the path names no zone
Globals:
none
Error:
None
Example:
my $zone = _zone_from_path("../usr/share/zoneinfo/America/Sao_Paulo");
Comments:
none
=cut
#--------------------------------------------------------------------------------
sub _zone_from_path
{
my ($path) = @_;
return undef unless defined($path) and length($path);
return undef unless $path =~ m{(?:^|/)zoneinfo/(.+)\z};
my $zone = $1;
$zone =~ s{^posix/}{};
return undef if $zone eq '' or $zone eq 'localtime' or $zone =~ /\s/;
return $zone;
}
#--------------------------------------------------------------------------------