2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-09-30 06:51:17 +00:00

feat(mknb): install exported Genesis images

This commit is contained in:
Vinícius Ferrão
2026-08-10 20:36:39 -03:00
parent 875a6ef40d
commit 26b4aec340
+152 -1
View File
@@ -35,6 +35,144 @@ sub _select_network_addresses {
return (\%legacy, \%selected);
}
sub _prebuilt_genesis_requested {
my ($directory) = @_;
return -e "$directory/initramfs.cpio.gz"
|| -l "$directory/initramfs.cpio.gz"
|| -e "$directory/SHA256SUMS"
|| -l "$directory/SHA256SUMS";
}
sub _read_prebuilt_genesis_checksums {
my ($directory) = @_;
my $checksum_file = "$directory/SHA256SUMS";
return (undef, "Missing Genesis checksum file: $checksum_file")
unless -f $checksum_file && !-l $checksum_file;
open(my $checksum_fh, '<:raw', $checksum_file)
or return (undef, "Unable to read Genesis checksum file: $checksum_file");
my %expected;
while (my $line = <$checksum_fh>) {
chomp($line);
unless ($line =~ /^([0-9a-f]{64}) ([A-Za-z0-9][A-Za-z0-9._-]*)$/) {
close($checksum_fh);
return (undef, "Invalid Genesis checksum entry: $line");
}
my ($digest, $name) = ($1, $2);
if (exists($expected{$name})) {
close($checksum_fh);
return (undef, "Duplicate Genesis checksum entry: $name");
}
$expected{$name} = $digest;
}
close($checksum_fh);
return (\%expected, undef);
}
sub _sha256_file {
my ($path) = @_;
return (undef, "Digest::SHA is required to verify Genesis artifacts")
unless eval { require Digest::SHA; 1 };
open(my $artifact_fh, '<:raw', $path)
or return (undef, "Unable to read Genesis artifact: $path");
my $digest = Digest::SHA->new(256)->addfile($artifact_fh)->hexdigest;
close($artifact_fh);
return ($digest, undef);
}
sub _install_prebuilt_genesis {
my ($source, $tftpdir, $arch) = @_;
return (undef, "Invalid Genesis export directory: $source")
unless -d $source && !-l $source;
my ($expected, $checksum_error) =
_read_prebuilt_genesis_checksums($source);
return (undef, $checksum_error) if $checksum_error;
my $destination_dir = "$tftpdir/xcat";
eval { mkpath($destination_dir) unless -d $destination_dir; };
return (undef, "Unable to create Genesis destination: $destination_dir")
unless -d $destination_dir;
my $suffix = xCAT::Utils::genpassword(24);
my @artifacts = (
[ 'kernel', "$destination_dir/genesis.kernel.$arch" ],
[ 'initramfs.cpio.gz', "$destination_dir/genesis.fs.$arch.gz" ],
);
my @staged;
foreach my $artifact (@artifacts) {
my ($name, $destination) = @{$artifact};
my $source_path = "$source/$name";
unless (-f $source_path && !-l $source_path) {
unlink(@staged);
return (undef, "Missing Genesis artifact: $source_path");
}
unless (exists($expected->{$name})) {
unlink(@staged);
return (undef, "Missing Genesis checksum entry: $name");
}
my $temporary = "$destination.$suffix.new";
unless (copy($source_path, $temporary) && chmod(0644, $temporary)) {
unlink(@staged, $temporary);
return (undef, "Unable to stage Genesis artifact: $source_path");
}
push(@staged, $temporary);
my ($digest, $digest_error) = _sha256_file($temporary);
if ($digest_error || $digest ne $expected->{$name}) {
unlink(@staged);
return (undef, $digest_error || "Genesis checksum mismatch: $source_path");
}
}
my %backups;
foreach my $artifact (@artifacts) {
my $destination = $artifact->[1];
next unless -e $destination || -l $destination;
unless (-f $destination && !-l $destination) {
unlink(@staged, values(%backups));
return (undef, "Invalid Genesis destination: $destination");
}
my $backup = "$destination.$suffix.old";
unless (copy($destination, $backup)) {
unlink(@staged, values(%backups));
return (undef, "Unable to preserve Genesis artifact: $destination");
}
$backups{$destination} = $backup;
}
my @installed;
foreach my $index (0 .. $#artifacts) {
my $destination = $artifacts[$index]->[1];
unless (rename($staged[$index], $destination)) {
my @rollback_errors;
foreach my $installed (reverse(@installed)) {
if ($backups{$installed}) {
push(@rollback_errors, $installed)
unless rename($backups{$installed}, $installed);
} else {
push(@rollback_errors, $installed) unless unlink($installed);
}
}
unlink(@staged[$index .. $#staged], values(%backups));
my $error = "Unable to install Genesis artifact: $destination";
$error .= "; unable to restore: " . join(', ', @rollback_errors)
if @rollback_errors;
return (undef, $error);
}
push(@installed, $destination);
}
unlink(values(%backups));
return ("$destination_dir/genesis.fs.$arch.gz", undef);
}
sub process_request {
my $request = shift;
my $callback = shift;
@@ -133,7 +271,8 @@ sub process_request {
$request->{arg}->[0] = $arch;
}
unless (-d "$::XCATROOT/share/xcat/netboot/$arch" or -d "$::XCATROOT/share/xcat/netboot/genesis/$arch") {
my $genesis_dir = "$::XCATROOT/share/xcat/netboot/genesis/$arch";
unless (-d "$::XCATROOT/share/xcat/netboot/$arch" or -d $genesis_dir) {
$callback->({ error => "Unable to find directory $::XCATROOT/share/xcat/netboot/$arch or $::XCATROOT/share/xcat/netboot/genesis/$arch", errorcode => [1] });
return;
}
@@ -144,6 +283,18 @@ sub process_request {
} elsif ($configfileonly) {
goto CREAT_CONF_FILE;
}
if (_prebuilt_genesis_requested($genesis_dir)) {
$callback->({ data => ["Installing exported Genesis image for $arch"] });
my ($installed_initrd, $install_error) =
_install_prebuilt_genesis($genesis_dir, $tftpdir, $arch);
if ($install_error) {
$callback->({ error => [$install_error], errorcode => [1] });
return;
}
$initrd_file = $installed_initrd;
$invisibletouch = 1;
goto CREAT_CONF_FILE;
}
# Grab all the standard ssh public keys we can
my @ssh_pub_keys = ();
if (-r "/root/.ssh/id_rsa.pub") {