mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-08-28 01:26:41 +00:00
c390570e95
The genesis image goes into a file whose name ends with .lzma. The plugin writes that file only when /usr/bin/lzma is there, and it falls back to gzip when it is not. Red Hat ships no lzma binary. On AlmaLinux 9 and on AlmaLinux 10 that test fails, the plugin falls back to gzip, and it gives no message that says why. The image is larger on each run of mknb. Debian and Ubuntu ship lzma as a second name for xz, so those systems still get the smaller image. Ask xz for the same container when lzma is absent. The command "xz --format=lzma" writes the same bytes as "lzma", so the file keeps its name, its container and its size. Keep the gzip fallback for a system that has neither program. Recovered from the lenovobuild branch, which asked xz for the xz container. That container is not the lzma container, and the name of the file says lzma.
713 lines
28 KiB
Perl
713 lines
28 KiB
Perl
package xCAT_plugin::mknb;
|
|
use strict;
|
|
use Digest::SHA ();
|
|
use File::Temp qw(tempdir);
|
|
use xCAT::Utils;
|
|
use xCAT::TableUtils;
|
|
use xCAT::NodeRange;
|
|
use File::Path;
|
|
use File::Copy;
|
|
|
|
my $GENESIS_EXPORT_MANIFEST = 'xcat-genesis.manifest';
|
|
|
|
sub handled_commands {
|
|
return {
|
|
mknb => 'mknb',
|
|
};
|
|
}
|
|
|
|
sub _select_network_addresses {
|
|
my ($network_addresses, $preferred_addresses) = @_;
|
|
my %preferred = map { $_ => 1 } grep { defined($_) } @{$preferred_addresses};
|
|
my %legacy;
|
|
my %selected;
|
|
|
|
foreach my $network (keys %{$network_addresses}) {
|
|
my $addresses = $network_addresses->{$network};
|
|
next unless @{$addresses};
|
|
$legacy{$network} = $addresses->[-1];
|
|
$selected{$network} = $addresses->[0];
|
|
foreach my $address (@{$addresses}) {
|
|
if (defined($address) && $preferred{$address}) {
|
|
$selected{$network} = $address;
|
|
last;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (\%legacy, \%selected);
|
|
}
|
|
|
|
sub _genesis_export_manifest_present {
|
|
my ($directory) = @_;
|
|
my $manifest = "$directory/$GENESIS_EXPORT_MANIFEST";
|
|
return -e $manifest || -l $manifest;
|
|
}
|
|
|
|
sub _prebuilt_genesis_requested {
|
|
my ($directory) = @_;
|
|
foreach my $name (
|
|
$GENESIS_EXPORT_MANIFEST,
|
|
'kernel',
|
|
'initramfs.cpio.gz',
|
|
'SHA256SUMS'
|
|
)
|
|
{
|
|
my $path = "$directory/$name";
|
|
return 0 unless -e $path || -l $path;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
sub _validate_prebuilt_genesis_manifest {
|
|
my ($directory, $arch) = @_;
|
|
my $manifest = "$directory/$GENESIS_EXPORT_MANIFEST";
|
|
return "Missing Genesis export manifest: $manifest"
|
|
unless -f $manifest && !-l $manifest;
|
|
|
|
open(my $manifest_fh, '<:raw', $manifest)
|
|
or return "Unable to read Genesis export manifest: $manifest";
|
|
|
|
my %expected = (
|
|
format => 'xcat-genesis',
|
|
version => '1',
|
|
architecture => $arch,
|
|
);
|
|
my %values;
|
|
while (my $line = <$manifest_fh>) {
|
|
chomp($line);
|
|
unless ($line =~ /^([a-z][a-z0-9_-]*)=([A-Za-z0-9][A-Za-z0-9._+-]*)$/) {
|
|
close($manifest_fh);
|
|
return "Invalid Genesis export manifest entry: $line";
|
|
}
|
|
my ($name, $value) = ($1, $2);
|
|
unless (exists($expected{$name})) {
|
|
close($manifest_fh);
|
|
return "Unknown Genesis export manifest entry: $name";
|
|
}
|
|
if (exists($values{$name})) {
|
|
close($manifest_fh);
|
|
return "Duplicate Genesis export manifest entry: $name";
|
|
}
|
|
$values{$name} = $value;
|
|
}
|
|
close($manifest_fh);
|
|
|
|
foreach my $name (qw(format version architecture)) {
|
|
return "Missing Genesis export manifest entry: $name"
|
|
unless exists($values{$name});
|
|
return "Unsupported Genesis export $name: $values{$name}"
|
|
unless $values{$name} eq $expected{$name};
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
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) = @_;
|
|
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 $manifest_error =
|
|
_validate_prebuilt_genesis_manifest($source, $arch);
|
|
return (undef, $manifest_error) if $manifest_error;
|
|
|
|
my ($expected, $checksum_error) =
|
|
_read_prebuilt_genesis_checksums($source);
|
|
return (undef, $checksum_error) if $checksum_error;
|
|
|
|
unless (exists($expected->{$GENESIS_EXPORT_MANIFEST})) {
|
|
return (undef,
|
|
"Missing Genesis checksum entry: $GENESIS_EXPORT_MANIFEST");
|
|
}
|
|
my ($manifest_digest, $manifest_digest_error) =
|
|
_sha256_file("$source/$GENESIS_EXPORT_MANIFEST");
|
|
return (undef, $manifest_digest_error) if $manifest_digest_error;
|
|
unless ($manifest_digest eq $expected->{$GENESIS_EXPORT_MANIFEST}) {
|
|
return (undef,
|
|
"Genesis checksum mismatch: $source/$GENESIS_EXPORT_MANIFEST");
|
|
}
|
|
|
|
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 genesis_lzma_command {
|
|
my ($have_lzma, $have_xz) = @_;
|
|
return 'lzma -C crc32 -9' if $have_lzma;
|
|
return 'xz --format=lzma -C crc32 -9' if $have_xz;
|
|
return;
|
|
}
|
|
|
|
sub process_request {
|
|
my $request = shift;
|
|
my $callback = shift;
|
|
my $serialport;
|
|
my $serialspeed;
|
|
my $serialflow;
|
|
my %nobootnicips = ();
|
|
my $initrd_file = undef;
|
|
my $invisibletouch = 0;
|
|
my $xcatdport = 3001;
|
|
my @entries = xCAT::TableUtils->get_site_attribute("defserialport");
|
|
my $t_entry = $entries[0];
|
|
if (defined($t_entry)) {
|
|
$serialport = $t_entry;
|
|
}
|
|
|
|
@entries = xCAT::TableUtils->get_site_attribute("defserialspeed");
|
|
$t_entry = $entries[0];
|
|
if (defined($t_entry)) {
|
|
$serialspeed = $t_entry;
|
|
}
|
|
|
|
@entries = xCAT::TableUtils->get_site_attribute("defserialflow");
|
|
$t_entry = $entries[0];
|
|
if (defined($t_entry)) {
|
|
$serialflow = $t_entry;
|
|
}
|
|
|
|
@entries = xCAT::TableUtils->get_site_attribute("xcatdport");
|
|
$t_entry = $entries[0];
|
|
if (defined($t_entry)) {
|
|
$xcatdport = $t_entry;
|
|
}
|
|
|
|
my $httpport="80";
|
|
my @hports=xCAT::TableUtils->get_site_attribute("httpport");
|
|
if ($hports[0]){
|
|
$httpport=$hports[0];
|
|
}
|
|
my $portsuffix = ( $httpport eq "80" ) ? "" : ":$httpport";
|
|
|
|
@entries = xCAT::TableUtils->get_site_attribute("dhcpinterfaces");
|
|
$t_entry = $entries[0];
|
|
if (defined($t_entry)) {
|
|
my %nobootnics = ();
|
|
foreach my $dhcpif (split /;/, $t_entry) {
|
|
if ($dhcpif =~ /\|/) {
|
|
my $isself = 0;
|
|
(my $ngroup, $dhcpif) = split /\|/, $dhcpif;
|
|
foreach my $host (noderange($ngroup)) {
|
|
unless(xCAT::NetworkUtils->thishostisnot($host)) {
|
|
$isself = 1;
|
|
}
|
|
}
|
|
unless(xCAT::NetworkUtils->thishostisnot($ngroup)) {
|
|
$isself = 1;
|
|
}
|
|
unless ($isself) {
|
|
next;
|
|
}
|
|
}
|
|
foreach (split /[,\s]+/, $dhcpif) {
|
|
my ($nicname, $flag) = split /:/;
|
|
if ($flag and $flag =~ /noboot/i) {
|
|
$nobootnics{$nicname} = 1;
|
|
}
|
|
}
|
|
}
|
|
my $nicips = xCAT::NetworkUtils->get_nic_ip();
|
|
foreach (keys %$nicips) {
|
|
# To support tagged vlan, create entries in the hash for the
|
|
# interface name removing the physical interface ending:
|
|
# 'enP1p12s0f0.2@enP1p12s0f0' => 'enP1p12s0f0.2'
|
|
if ($_ =~ "@") {
|
|
my $newkey = $_;
|
|
$newkey =~ s/\@.*//g;
|
|
$$nicips{$newkey} = ${nicips}->{$_};
|
|
}
|
|
}
|
|
|
|
foreach (keys %nobootnics) {
|
|
if (defined($nicips->{$_})) {
|
|
$nobootnicips{$nicips->{$_}} = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
my $tftpdir = xCAT::TableUtils->getTftpDir();
|
|
my $arch = $request->{arg}->[0];
|
|
if (!$arch) {
|
|
$callback->({ error => "Need to specify architecture (x86, x86_64 or ppc64)" }, { errorcode => [1] });
|
|
return;
|
|
} elsif ($arch eq "ppc64le" or $arch eq "ppc64el") {
|
|
$callback->({ data => "The arch:$arch is not supported, using \"ppc64\" instead" });
|
|
$arch = 'ppc64';
|
|
$request->{arg}->[0] = $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;
|
|
}
|
|
my $configfileonly = $request->{arg}->[1];
|
|
if ($configfileonly and $configfileonly ne "-c" and $configfileonly ne "--configfileonly") {
|
|
$callback->({ error => "The option $configfileonly is not supported", errorcode => [1] });
|
|
return;
|
|
} 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;
|
|
}
|
|
if (_genesis_export_manifest_present($genesis_dir)) {
|
|
$callback->({
|
|
error => ["Incomplete Genesis export: $genesis_dir"],
|
|
errorcode => [1],
|
|
});
|
|
return;
|
|
}
|
|
# Grab all the standard ssh public keys we can
|
|
my @ssh_pub_keys = ();
|
|
if (-r "/root/.ssh/id_rsa.pub") {
|
|
push(@ssh_pub_keys, 'id_rsa.pub');
|
|
}
|
|
if (-r "/root/.ssh/id_ed25519.pub") {
|
|
push(@ssh_pub_keys, 'id_ed25519.pub');
|
|
}
|
|
if (-r "/root/.ssh/id_ecdsa.pub") {
|
|
push(@ssh_pub_keys, 'id_ecdsa.pub');
|
|
}
|
|
if (scalar @ssh_pub_keys == 0) {
|
|
# We have no public keys.
|
|
# See if we have any private keys we can extract pubkeys from
|
|
if (-r "/root/.ssh/id_rsa") {
|
|
$callback->({ data => ["Extracting rsa ssh public key from private key"] });
|
|
my $rc = system('ssh-keygen -y -f /root/.ssh/id_rsa > /root/.ssh/id_rsa.pub');
|
|
if ($rc) {
|
|
$callback->({ error => ["Failure executing ssh-keygen for root when extracting rsa ssh public key from private key"], errorcode => [1] });
|
|
} else {
|
|
push(@ssh_pub_keys, 'id_rsa.pub');
|
|
}
|
|
} elsif (-r "/root/.ssh/id_ed25519") {
|
|
$callback->({ data => ["Extracting ed25519 ssh public key from private key"] });
|
|
my $rc = system('ssh-keygen -y -f /root/.ssh/id_ed25519 > /root/.ssh/id_ed25519.pub');
|
|
if ($rc) {
|
|
$callback->({ error => ["Failure executing ssh-keygen for root when extracting ed25519 ssh public key from private key"], errorcode => [1] });
|
|
} else {
|
|
push(@ssh_pub_keys, 'id_ed25519.pub');
|
|
}
|
|
} elsif (-r "/root/.ssh/id_ecdsa") {
|
|
$callback->({ data => ["Extracting ecdsa ssh public key from private key"] });
|
|
my $rc = system('ssh-keygen -y -f /root/.ssh/id_ecdsa > /root/.ssh/id_ecdsa.pub');
|
|
if ($rc) {
|
|
$callback->({ error => ["Failure executing ssh-keygen for root when extracting ecdsa ssh public key from private key"], errorcode => [1] });
|
|
} else {
|
|
push(@ssh_pub_keys, 'id_ecdsa.pub');
|
|
}
|
|
}
|
|
}
|
|
if (scalar @ssh_pub_keys == 0) {
|
|
# Looks like we didn't have any private keys either, so generate one
|
|
$callback->({ data => ["Generating rsa ssh private key for root"] });
|
|
my $rc = system('ssh-keygen -t rsa -q -b 2048 -N "" -f /root/.ssh/id_rsa');
|
|
if ($rc) {
|
|
$callback->({ error => ["Failure executing ssh-keygen for root when generating rsa ssh private key"], errorcode => [1] });
|
|
} else {
|
|
push(@ssh_pub_keys, 'id_rsa.pub');
|
|
}
|
|
}
|
|
my $tempdir = tempdir("mknb.$$.XXXXXX", TMPDIR => 1);
|
|
unless ($tempdir) {
|
|
$callback->({ error => ["Failed to create a temporary directory"], errorcode => [1] });
|
|
return;
|
|
}
|
|
unless (-e "$tftpdir/xcat") {
|
|
mkpath("$tftpdir/xcat");
|
|
}
|
|
my $rc;
|
|
if (-e "$::XCATROOT/share/xcat/netboot/genesis/$arch") {
|
|
$rc = system("shopt -s dotglob; GLOBIGNORE=\".:..\" cp -a $::XCATROOT/share/xcat/netboot/genesis/$arch/fs/* $tempdir");
|
|
$rc = system("cp -a $::XCATROOT/share/xcat/netboot/genesis/$arch/kernel $tftpdir/xcat/genesis.kernel.$arch");
|
|
$invisibletouch = 1;
|
|
} else {
|
|
$rc = system("cp -a $::XCATROOT/share/xcat/netboot/$arch/nbroot/* $tempdir");
|
|
}
|
|
if ($rc) {
|
|
system("rm -rf $tempdir");
|
|
if ($invisibletouch) {
|
|
$callback->({ error => ["Failed to copy $::XCATROOT/share/xcat/netboot/genesis/$arch/fs contents"], errorcode => [1] });
|
|
} else {
|
|
$callback->({ error => ["Failed to copy $::XCATROOT/share/xcat/netboot/$arch/nbroot/ contents"], errorcode => [1] });
|
|
}
|
|
return;
|
|
}
|
|
my $sshdir;
|
|
if ($invisibletouch) {
|
|
$sshdir = "/.ssh";
|
|
} else {
|
|
$sshdir = "/root/.ssh";
|
|
}
|
|
mkpath($tempdir . "$sshdir");
|
|
chmod(0700, $tempdir . "$sshdir");
|
|
open(my $authkeys_fh, '>:raw', "$tempdir$sshdir/authorized_keys");
|
|
foreach my $keyfile (@ssh_pub_keys) {
|
|
open(my $pubkey_fh, '<:raw', "/root/.ssh/$keyfile");
|
|
while(my $line = <$pubkey_fh>) {
|
|
print($authkeys_fh $line);
|
|
}
|
|
close($pubkey_fh);
|
|
}
|
|
close($authkeys_fh);
|
|
chmod(0600, "$tempdir$sshdir/authorized_keys");
|
|
if (not $invisibletouch and -r "/etc/xcat/hostkeys/ssh_host_rsa_key") {
|
|
copy("/etc/xcat/hostkeys/ssh_host_rsa_key", "$tempdir/etc/ssh_host_rsa_key");
|
|
copy("/etc/xcat/hostkeys/ssh_host_dsa_key", "$tempdir/etc/ssh_host_dsa_key");
|
|
chmod(0600, <$tempdir/etc/ssh_*>);
|
|
}
|
|
unless ($invisibletouch or -r "$tempdir/etc/ssh_host_rsa_key") {
|
|
system("ssh-keygen -t rsa -f $tempdir/etc/ssh_host_rsa_key -C '' -N ''");
|
|
system("ssh-keygen -t dsa -f $tempdir/etc/ssh_host_dsa_key -C '' -N ''");
|
|
}
|
|
my $lzma_exit_value = 1;
|
|
if ($invisibletouch) {
|
|
my $done = 0;
|
|
# Build each image under a unique suffix and atomically rename it into
|
|
# place, so concurrent mknb runs sharing $tftpdir cannot read or clobber
|
|
# a half-written genesis.fs.
|
|
my $suffix = xCAT::Utils::genpassword(24);
|
|
my $lzma_command = genesis_lzma_command(-x "/usr/bin/lzma", -x "/usr/bin/xz");
|
|
if ($lzma_command) { #let's reclaim some of that size...
|
|
$callback->({ data => ["Creating genesis.fs.$arch.lzma in $tftpdir/xcat"] });
|
|
system("cd $tempdir; find . | cpio -o -H newc | $lzma_command > $tftpdir/xcat/genesis.fs.$arch.lzma.$suffix");
|
|
$lzma_exit_value = $? >> 8;
|
|
if ($lzma_exit_value) {
|
|
$callback->({ data => ["Creating genesis.fs.$arch.lzma in $tftpdir/xcat failed, falling back to gzip"] });
|
|
unlink("$tftpdir/xcat/genesis.fs.$arch.lzma.$suffix");
|
|
} else {
|
|
move("$tftpdir/xcat/genesis.fs.$arch.lzma.$suffix", "$tftpdir/xcat/genesis.fs.$arch.lzma");
|
|
$done = 1;
|
|
$initrd_file = "$tftpdir/xcat/genesis.fs.$arch.lzma";
|
|
}
|
|
}
|
|
|
|
if (not $done) {
|
|
$callback->({ data => ["Creating genesis.fs.$arch.gz in $tftpdir/xcat"] });
|
|
system("cd $tempdir; find . | cpio -o -H newc | gzip -9 > $tftpdir/xcat/genesis.fs.$arch.gz.$suffix");
|
|
move("$tftpdir/xcat/genesis.fs.$arch.gz.$suffix", "$tftpdir/xcat/genesis.fs.$arch.gz");
|
|
$initrd_file = "$tftpdir/xcat/genesis.fs.$arch.gz";
|
|
}
|
|
} else {
|
|
$callback->({ data => ["Creating nbfs.$arch.gz in $tftpdir/xcat"] });
|
|
system("cd $tempdir; find . | cpio -o -H newc | gzip -9 > $tftpdir/xcat/nbfs.$arch.gz");
|
|
$initrd_file = "$tftpdir/xcat/nbfs.$arch.gz";
|
|
}
|
|
system("rm -rf $tempdir");
|
|
unless ($initrd_file) {
|
|
$callback->({ data => ["Creating filesystem file in $tftpdir/xcat failed"] });
|
|
return;
|
|
}
|
|
|
|
CREAT_CONF_FILE:
|
|
if ($configfileonly) {
|
|
unless (-e "$tftpdir/xcat/genesis.kernel.$arch") {
|
|
$callback->({ error => ["No kernel file found in $tftpdir/xcat, pls run \"mknb $arch\" instead."], errorcode => [1] });
|
|
return;
|
|
}
|
|
if (-e "$tftpdir/xcat/genesis.fs.$arch.lzma") {
|
|
$initrd_file = "$tftpdir/xcat/genesis.fs.$arch.lzma";
|
|
$invisibletouch = 1;
|
|
} elsif (-e "$tftpdir/xcat/genesis.fs.$arch.gz") {
|
|
$initrd_file = "$tftpdir/xcat/genesis.fs.$arch.gz";
|
|
$invisibletouch = 1;
|
|
} elsif (-e "$tftpdir/xcat/nbfs.$arch.gz") {
|
|
$initrd_file = "$tftpdir/xcat/nbfs.$arch.gz";
|
|
} else {
|
|
$callback->({ error => ["No filesystem file found in $tftpdir/xcat, pls run \"mknb $arch\" instead."], errorcode => [1] });
|
|
return;
|
|
}
|
|
}
|
|
my $hexnet_addresses = xCAT::NetworkUtils->my_hexnets('all');
|
|
my $normnet_addresses = xCAT::NetworkUtils->my_nets('all');
|
|
my @masters = xCAT::TableUtils->get_site_attribute("master");
|
|
my @master_addresses;
|
|
if ($masters[0]) {
|
|
@master_addresses = xCAT::NetworkUtils->getipaddr(
|
|
$masters[0], OnlyV4 => 1, GetAllAddresses => 1
|
|
);
|
|
}
|
|
my ($hexnets, $xcatdhexnets) = _select_network_addresses(
|
|
$hexnet_addresses, \@master_addresses
|
|
);
|
|
my ($normnets, $xcatdnormnets) = _select_network_addresses(
|
|
$normnet_addresses, \@master_addresses
|
|
);
|
|
my $consolecmdline;
|
|
if (defined($serialport) and $serialspeed) {
|
|
if ($arch =~ /ppc/) {
|
|
$consolecmdline = "console=tty0 console=hvc$serialport,$serialspeed";
|
|
} else {
|
|
$consolecmdline = "console=tty0 console=ttyS$serialport,$serialspeed";
|
|
}
|
|
if ($serialflow =~ /cts/ or $serialflow =~ /hard/) {
|
|
$consolecmdline .= "n8r";
|
|
}
|
|
}
|
|
my $cfgfile;
|
|
if ($arch =~ /x86/) {
|
|
mkpath("$tftpdir/xcat/xnba/nets");
|
|
chmod(0755, "$tftpdir/xcat/xnba");
|
|
chmod(0755, "$tftpdir/xcat/xnba/nets");
|
|
mkpath("$tftpdir/pxelinux.cfg");
|
|
chmod(0755, "$tftpdir/pxelinux.cfg");
|
|
if (-r "/usr/lib/syslinux/pxelinux.0") {
|
|
copy("/usr/lib/syslinux/pxelinux.0", "$tftpdir/pxelinux.0");
|
|
} elsif (-r "/usr/share/syslinux/pxelinux.0") {
|
|
copy("/usr/share/syslinux/pxelinux.0", "$tftpdir/pxelinux.0");
|
|
} elsif ("/usr/lib/PXELINUX/pxelinux.0") {
|
|
copy("/usr/lib/PXELINUX/pxelinux.0", "$tftpdir/pxelinux.0");
|
|
} else {
|
|
copy("/opt/xcat/share/xcat/netboot/syslinux/pxelinux.0", "$tftpdir/pxelinux.0");
|
|
}
|
|
if (-r "$tftpdir/pxelinux.0") {
|
|
chmod(0644, "$tftpdir/pxelinux.0");
|
|
}
|
|
} elsif ($arch =~ /ppc/) {
|
|
mkpath("$tftpdir/pxelinux.cfg/p/");
|
|
}
|
|
my $dopxe = 0;
|
|
foreach (keys %{$normnets}) {
|
|
my $net = $_;
|
|
my $nicip = $normnets->{$net};
|
|
my $xcatd_address = defined($xcatdnormnets->{$net}) ? $xcatdnormnets->{$net} : $nicip;
|
|
$net =~ s/\//_/;
|
|
if (defined($nobootnicips{$nicip})) {
|
|
if ($arch =~ /ppc/ and -r "$tftpdir/pxelinux.cfg/p/$net") {
|
|
unlink("$tftpdir/pxelinux.cfg/p/$net");
|
|
}
|
|
next;
|
|
}
|
|
$dopxe = 0;
|
|
if ($arch =~ /x86/) { #only do pxe if just x86 or x86_64 and no x86
|
|
if ($arch =~ /x86_64/ and not $invisibletouch) {
|
|
if (-r "$tftpdir/xcat/xnba/nets/$net") {
|
|
my $cfg;
|
|
my @contents;
|
|
open($cfg, "<", "$tftpdir/xcat/xnba/nets/$net");
|
|
@contents = <$cfg>;
|
|
close($cfg);
|
|
if (grep (/x86_64/, @contents)) {
|
|
$dopxe = 1;
|
|
}
|
|
} else {
|
|
$dopxe = 1;
|
|
}
|
|
} else {
|
|
$dopxe = 1;
|
|
}
|
|
}
|
|
if ($dopxe) {
|
|
my $cfg;
|
|
open($cfg, ">", "$tftpdir/xcat/xnba/nets/$net");
|
|
print $cfg "#!gpxe\n";
|
|
if ($invisibletouch) {
|
|
print $cfg 'imgfetch -n kernel http://${next-server}'.$portsuffix.'/tftpboot/xcat/genesis.kernel.' . "$arch xcatd=" . $xcatd_address . ":$xcatdport $consolecmdline BOOTIF=01-" . '${netX/machyp}' . "\n";
|
|
print $cfg 'imgfetch -n nbfs http://${next-server}'.$portsuffix . "$initrd_file\n";
|
|
} else {
|
|
print $cfg 'imgfetch -n kernel http://${next-server}'.$portsuffix.'/tftpboot/xcat/nbk.' . "$arch xcatd=" . $xcatd_address . ":$xcatdport $consolecmdline\n";
|
|
print $cfg 'imgfetch -n nbfs http://${next-server}'.$portsuffix . "$initrd_file\n";
|
|
}
|
|
print $cfg "imgload kernel\n";
|
|
print $cfg "imgexec kernel\n";
|
|
close($cfg);
|
|
if ($invisibletouch and $arch =~ /x86_64/) { #UEFI time
|
|
open($cfg, ">", "$tftpdir/xcat/xnba/nets/$net.elilo");
|
|
print $cfg "default=\"xCAT Genesis (" . $normnets->{$_} . ")\"\n";
|
|
print $cfg " delay=5\n";
|
|
print $cfg ' image=/tftpboot/xcat/genesis.kernel.' . "$arch\n";
|
|
print $cfg " label=\"xCAT Genesis (" . $normnets->{$_} . ")\"\n";
|
|
print $cfg " initrd=$initrd_file\n";
|
|
print $cfg " append=\"xcatd=" . $xcatd_address . ":$xcatdport destiny=discover $consolecmdline BOOTIF=%B\"\n";
|
|
close($cfg);
|
|
open($cfg, ">", "$tftpdir/xcat/xnba/nets/$net.uefi");
|
|
print $cfg "#!gpxe\n";
|
|
print $cfg 'imgfetch -n kernel http://${next-server}'.$portsuffix.'/tftpboot/xcat/genesis.kernel.' . "$arch\nimgload kernel\n";
|
|
print $cfg "imgargs kernel xcatd=" . $xcatd_address . ":$xcatdport $consolecmdline BOOTIF=01-" . '${netX/mac:hexhyp}' . " destiny=discover initrd=initrd\n";
|
|
print $cfg 'imgfetch -n initrd http://${next-server}'.$portsuffix . "$initrd_file\nimgexec kernel\n";
|
|
close($cfg);
|
|
}
|
|
} elsif ($arch =~ /ppc/) {
|
|
open($cfgfile, ">", "$tftpdir/pxelinux.cfg/p/$net");
|
|
print $cfgfile "default \"xCAT Genesis (" . $normnets->{$_} . ")\"\n";
|
|
print $cfgfile " delay=10\n";
|
|
print $cfgfile " label \"xCAT Genesis (" . $normnets->{$_} . ")\"\n";
|
|
print $cfgfile " kernel http://" . $xcatd_address . "$portsuffix/$tftpdir/xcat/genesis.kernel.$arch\n";
|
|
print $cfgfile " initrd http://" . $xcatd_address . "$portsuffix/$initrd_file\n";
|
|
print $cfgfile ' append "xcatd=' . $xcatd_address . ":$xcatdport $consolecmdline\"\n";
|
|
close($cfgfile);
|
|
}
|
|
}
|
|
$dopxe = 0;
|
|
foreach (keys %{$hexnets}) {
|
|
my $xcatd_address = defined($xcatdhexnets->{$_}) ? $xcatdhexnets->{$_} : $hexnets->{$_};
|
|
$dopxe = 0;
|
|
if ($arch =~ /x86/) { #only do pxe if just x86 or x86_64 and no x86
|
|
if ($arch =~ /x86_64/) {
|
|
if (-r "$tftpdir/pxelinux.cfg/" . uc($_)) {
|
|
my $pcfg;
|
|
open($pcfg, "<", "$tftpdir/pxelinux.cfg/" . uc($_));
|
|
my @pcfgcontents = <$pcfg>;
|
|
close($pcfg);
|
|
if (grep (/x86_64/, @pcfgcontents)) {
|
|
$dopxe = 1;
|
|
}
|
|
} else {
|
|
$dopxe = 1;
|
|
}
|
|
} else {
|
|
$dopxe = 1;
|
|
}
|
|
}
|
|
if ($dopxe) {
|
|
my $tftp_initrd = $initrd_file;
|
|
$tftp_initrd =~ s{^\Q$tftpdir\E/?}{};
|
|
my $kernel_file = $invisibletouch ? "genesis.kernel.$arch" : "nbk.$arch";
|
|
open($cfgfile, ">", "$tftpdir/pxelinux.cfg/" . uc($_));
|
|
print $cfgfile "DEFAULT xCAT\n";
|
|
print $cfgfile " LABEL xCAT\n";
|
|
print $cfgfile " KERNEL xcat/$kernel_file\n";
|
|
print $cfgfile " APPEND initrd=$tftp_initrd xcatd=" . $xcatd_address . ":$xcatdport $consolecmdline\n";
|
|
close($cfgfile);
|
|
} elsif ($arch =~ /ppc/) {
|
|
open($cfgfile, ">", "$tftpdir/etc/" . lc($_));
|
|
print $cfgfile "default \"xCAT Genesis (" . $normnets->{$_} . ")\"\n";
|
|
print $cfgfile " delay=10\n";
|
|
print $cfgfile " label \"xCAT Genesis (" . $normnets->{$_} . ")\"\n";
|
|
print $cfgfile " kernel http://" . $xcatd_address . "$portsuffix/$tftpdir/xcat/genesis.kernel.$arch\n";
|
|
print $cfgfile " initrd http://" . $xcatd_address . "$portsuffix/$initrd_file\n";
|
|
print $cfgfile ' append "xcatd=' . $xcatd_address . ":$xcatdport $consolecmdline\"\n";
|
|
close($cfgfile);
|
|
}
|
|
}
|
|
if ($configfileonly) {
|
|
$callback->({ data => ["Write netboot config file done"] });
|
|
}
|
|
}
|
|
|
|
1;
|