mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-08-28 17:46:40 +00:00
705 lines
28 KiB
Perl
705 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 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);
|
|
if (-x "/usr/bin/lzma") { #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 -C crc32 -9 > $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;
|