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:
@@ -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") {
|
||||
|
||||
Reference in New Issue
Block a user