mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-09-04 20:17:55 +00:00
00ef09add2
The tests added with this fix matched regexes against the text of debian.pm, Postage.pm and compute.subiquity.tmpl. A source match cannot tell whether the code it found ever runs: moving the Subiquity command line into its own routine leaves every one of those assertions passing, and reformatting a line fails them while the behaviour is untouched. One of them pinned Perl syntax outright, qr/\$kcmdline\s*\.=\s*" ---";/, and another matched the text of a substitution. Run the code instead. Two decisions were lifted out of the routines that had grown around them, so a test can call them: subiquity_kcmdline() in debian.pm builds the installer command line from its inputs, and defer_syncfiles_to_postboot() in Postage.pm returns the adjusted postscript lists. Both are pure and carry the reasoning that used to sit inline. The callers keep their behaviour exactly. The template's two shell fragments are extracted and executed: the boot flip runs against a stand-in for xcatd on the install-monitor port, and the resolv.conf step runs with a getent that answers as the case requires. Every assertion now fails when the behaviour it describes is removed, which is what the source matches only appeared to do: boot=casper dropped from the command line 1 assertion fails toram dropped 1 assertion fails nfsroot given the host name instead of the IP 2 assertions fail the deferral made a no-op 7 assertions fail the wrong token sent to xcatd 1 assertion fails the failed-flip log line removed 3 assertions fail the retry loop reduced to one attempt 2 assertions fail resolv.conf given the host name 3 assertions fail the resolution fallback removed 2 assertions fail ubuntu_subiquity_bootparams.t is removed. Its three matches are covered by execution in debian_subiquity_netboot.t, and it demonstrated the problem: it still passed after the command line moved into another routine. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
1936 lines
70 KiB
Perl
1936 lines
70 KiB
Perl
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
|
|
package xCAT_plugin::debian;
|
|
|
|
BEGIN
|
|
{
|
|
$::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
|
|
}
|
|
use lib "$::XCATROOT/lib/perl";
|
|
use Storable qw(dclone);
|
|
use Sys::Syslog;
|
|
use File::Temp qw/tempdir/;
|
|
use xCAT::Table;
|
|
use xCAT::Utils;
|
|
use xCAT::SvrUtils;
|
|
use xCAT::MsgUtils;
|
|
use Data::Dumper;
|
|
use Getopt::Long;
|
|
Getopt::Long::Configure("bundling");
|
|
Getopt::Long::Configure("pass_through");
|
|
use File::Path;
|
|
use File::Copy;
|
|
|
|
use Socket;
|
|
|
|
#use strict;
|
|
my $useflowcontrol = "0";
|
|
my @cpiopid;
|
|
|
|
##############################################################################
|
|
#
|
|
# Author:
|
|
#
|
|
# Arif Ali (OCF plc) <mail@arif-ali.co.uk>
|
|
#
|
|
# Notes:
|
|
#
|
|
# This will not work with Ubuntu Desktop Edition, as all the packages are in
|
|
# a compressed image, and not readily available for creating images. So will
|
|
# only support Server ISOs.
|
|
#
|
|
#
|
|
#
|
|
# ChangeLog:
|
|
#
|
|
# 13 Aug 2010 - Initial release
|
|
# - Implementation of only copycd
|
|
# - Tested with 9.10 desktop and server ISOs
|
|
# 06 Oct 2010 - Added copycd support for Ubuntu 10.04 Server (LTS releases)
|
|
# - Added support for mkinstall, install successfull for 10.04
|
|
# -> used function from anaconda.pm
|
|
# -> Need to cleanup so that it has no references to rhel
|
|
# 07 Oct 2010 - Added preprocess_request (direct copy from anaconda.pm)
|
|
#
|
|
##############################################################################
|
|
|
|
sub handled_commands
|
|
{
|
|
return {
|
|
copycd => "debian",
|
|
mknetboot => "nodetype:os=(ubuntu.*)|(debian.*)",
|
|
mkinstall => "nodetype:os=(ubuntu.*)|(debian.*)",
|
|
mkstatelite => "nodetype:os=(ubuntu.*)|(debian.*)",
|
|
};
|
|
}
|
|
|
|
sub preprocess_request
|
|
{
|
|
my $req = shift;
|
|
my $callback = shift;
|
|
return [$req]; #calls are only made from pre-farmed out scenarios
|
|
if ($req->{command}->[0] eq 'copycd')
|
|
{ #don't farm out copycd
|
|
return [$req];
|
|
}
|
|
|
|
my $stab = xCAT::Table->new('site');
|
|
my $sent;
|
|
($sent) = $stab->getAttribs({ key => 'sharedtftp' }, 'value');
|
|
unless ($sent
|
|
and defined($sent->{value})
|
|
and ($sent->{value} eq "no" or $sent->{value} eq "NO" or $sent->{value} eq "0"))
|
|
{
|
|
|
|
#unless requesting no sharedtftp, don't make hierarchical call
|
|
return [$req];
|
|
}
|
|
|
|
my %localnodehash;
|
|
my %dispatchhash;
|
|
my $nrtab = xCAT::Table->new('noderes');
|
|
my $nrents = $nrtab->getNodesAttribs($req->{node}, [qw(tftpserver servicenode)]);
|
|
foreach my $node (@{ $req->{node} })
|
|
{
|
|
my $nodeserver;
|
|
my $tent = $nrents->{$node}->[0]; #$nrtab->getNodeAttribs($node, ['tftpserver']);
|
|
if ($tent) { $nodeserver = $tent->{tftpserver} }
|
|
unless ($tent and $tent->{tftpserver})
|
|
{
|
|
$tent = $nrents->{$node}->[0]; #$nrtab->getNodeAttribs($node, ['servicenode']);
|
|
if ($tent) { $nodeserver = $tent->{servicenode} }
|
|
}
|
|
if ($nodeserver)
|
|
{
|
|
$dispatchhash{$nodeserver}->{$node} = 1;
|
|
}
|
|
else
|
|
{
|
|
$localnodehash{$node} = 1;
|
|
}
|
|
}
|
|
my @requests;
|
|
my $reqc = {%$req};
|
|
$reqc->{node} = [ keys %localnodehash ];
|
|
if (scalar(@{ $reqc->{node} })) { push @requests, $reqc }
|
|
|
|
foreach my $dtarg (keys %dispatchhash)
|
|
{ #iterate dispatch targets
|
|
my $reqcopy = {%$req}; #deep copy
|
|
$reqcopy->{'_xcatdest'} = $dtarg;
|
|
$reqcopy->{node} = [ keys %{ $dispatchhash{$dtarg} } ];
|
|
push @requests, $reqcopy;
|
|
}
|
|
return \@requests;
|
|
}
|
|
|
|
sub process_request
|
|
{
|
|
my $request = shift;
|
|
my $callback = shift;
|
|
my $doreq = shift;
|
|
my $distname = undef;
|
|
my $arch = undef;
|
|
my $path = undef;
|
|
if ($::XCATSITEVALS{"useflowcontrol"}) { $useflowcontrol = $::XCATSITEVALS{"useflowcontrol"}; }
|
|
if ($request->{command}->[0] eq 'copycd')
|
|
{
|
|
return copycd($request, $callback, $doreq);
|
|
}
|
|
elsif ($request->{command}->[0] eq 'mkinstall')
|
|
{
|
|
return mkinstall($request, $callback, $doreq);
|
|
}
|
|
elsif ($request->{command}->[0] eq 'mknetboot' or
|
|
$request->{command}->[0] eq 'mkstatelite')
|
|
{
|
|
return mknetboot($request, $callback, $doreq);
|
|
}
|
|
}
|
|
|
|
# Check whether the dracut is supported by this os
|
|
sub using_dracut
|
|
{
|
|
my $os = shift;
|
|
if ($os =~ /ubuntu(\d+)/) {
|
|
if ($1 >= 12.04) {
|
|
return 1;
|
|
}
|
|
} elsif ($os =~ /debian(\d+)/) {
|
|
if ($1 >= 6.0) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
# Check whether the new subiquity installer is used by this os
|
|
sub using_subiquity
|
|
{
|
|
my $os = shift;
|
|
my $tmplfile = shift;
|
|
if ($os =~ /ubuntu(\d+\.\d+)/) {
|
|
if (($1 >= 20.04) and ($tmplfile =~ /subiquity/)) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
sub is_ubuntu_live_media
|
|
{
|
|
my $media_path = shift;
|
|
|
|
return 0 unless ($media_path && -d "$media_path/casper");
|
|
return 1 if (-r "$media_path/casper/install-sources.yaml");
|
|
|
|
my @squashfs_images = glob("$media_path/casper/*.squashfs");
|
|
return scalar(@squashfs_images) ? 1 : 0;
|
|
}
|
|
|
|
sub warn_ubuntu_live_media_pkg_source
|
|
{
|
|
my $callback = shift;
|
|
my $media_path = shift;
|
|
|
|
return unless is_ubuntu_live_media($media_path);
|
|
|
|
$callback->({
|
|
warning => [
|
|
"Ubuntu live media was copied successfully, but this media is not a complete Ubuntu apt package mirror. Install osimages can use it, but netboot/statelite image generation with genimage may require additional package sources through linuximage.pkgdir or linuximage.otherpkgdir, such as a local mirror or an explicitly configured HTTP/HTTPS Ubuntu apt repository."
|
|
]
|
|
});
|
|
}
|
|
|
|
sub copyAndAddCustomizations {
|
|
my $source = shift;
|
|
my $dest = shift;
|
|
|
|
#first, it's simple, we copy...
|
|
copy($source, $dest);
|
|
|
|
#next, we apply xCAT customizations to enhance debian installer..
|
|
chdir("$::XCATROOT/share/xcat/install/debian/initoverlay");
|
|
system("find . |cpio -o -H newc | gzip -c - -9 >> $dest");
|
|
}
|
|
|
|
sub copycd
|
|
{
|
|
my $request = shift;
|
|
my $callback = shift;
|
|
my $doreq = shift;
|
|
my $distname = "";
|
|
my $detdistname = "";
|
|
my $installroot;
|
|
my $arch;
|
|
my $path;
|
|
$installroot = "/install";
|
|
my $sitetab = xCAT::Table->new('site');
|
|
|
|
if ($sitetab)
|
|
{
|
|
(my $ref) = $sitetab->getAttribs({ key => 'installdir' }, 'value');
|
|
|
|
#print Dumper($ref);
|
|
if ($ref and $ref->{value})
|
|
{
|
|
$installroot = $ref->{value};
|
|
}
|
|
}
|
|
|
|
@ARGV = @{ $request->{arg} };
|
|
GetOptions(
|
|
'n=s' => \$distname,
|
|
'a=s' => \$arch,
|
|
'p=s' => \$copypath,
|
|
'm=s' => \$path,
|
|
'i' => \$inspection,
|
|
'o' => \$noosimage,
|
|
'w' => \$nonoverwrite,
|
|
);
|
|
unless ($path)
|
|
{
|
|
|
|
#this plugin needs $path...
|
|
return;
|
|
}
|
|
if ($distname
|
|
and $distname !~ /^debian/i
|
|
and $distname !~ /^ubuntu/i)
|
|
{
|
|
|
|
#If they say to call it something unidentifiable, give up?
|
|
return;
|
|
}
|
|
|
|
unless (-r $path . "/.disk/info")
|
|
{
|
|
#xCAT::MsgUtils->message("S","The CD doesn't look like a Debian CD, exiting...");
|
|
return;
|
|
}
|
|
|
|
if ($copypath || $nonoverwrite)
|
|
{
|
|
$callback->({ info => ["copycds on Ubuntu/Debian does not support -p or -w option."] });
|
|
return;
|
|
}
|
|
|
|
my $dinfo;
|
|
open($dinfo, $path . "/.disk/info");
|
|
my $line = <$dinfo>;
|
|
chomp($line);
|
|
my @line2 = split(/ /, $line);
|
|
close($dinfo);
|
|
|
|
my $isnetinst = 0;
|
|
my $legacyUB20 = ""; # Is this a Ubuntu20.04.1 legacy ISO
|
|
my $prod = $line2[0]; # The product should be the first word
|
|
my $ver = $line2[1]; # The version should be the second word
|
|
my $darch = $line2[6]; # The architecture should be the seventh word
|
|
|
|
# Check to see if $darch is defined
|
|
unless ($darch)
|
|
{
|
|
return;
|
|
}
|
|
|
|
my $discno = '';
|
|
if ($prod eq "Debian")
|
|
{
|
|
# Debian specific, the arch and version are in different places
|
|
$darch = $line2[6];
|
|
$ver = $line2[2];
|
|
$discno = $line2[8];
|
|
|
|
# For the purpose of copying the netinst cd before the main one
|
|
# So that we have the netboot images
|
|
$isnetinst = 1 if ($line2[7] eq "NETINST");
|
|
|
|
if (!$distname) {
|
|
$distname = "debian" . $ver;
|
|
}
|
|
$detdistname = "debian" . $ver;
|
|
}
|
|
elsif ($prod eq "Ubuntu" or $prod eq "Ubuntu-Server")
|
|
{
|
|
# to cover for LTS releases
|
|
$darch = $line2[7] if ($line2[2] eq "LTS");
|
|
|
|
if (!$distname) {
|
|
$distname = "ubuntu" . $ver;
|
|
}
|
|
$detdistname = "ubuntu" . $ver;
|
|
$discno = `cat $path/README.diskdefines | grep 'DISKNUM ' | awk '{print \$3}'`;
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
# So that I can use amd64 below
|
|
my $debarch = $darch;
|
|
|
|
if ($darch and $darch =~ /i.86/)
|
|
{
|
|
$darch = "x86";
|
|
}
|
|
elsif ($darch and $darch =~ /ppc64el/)
|
|
{
|
|
$darch = "ppc64el";
|
|
}
|
|
elsif ($darch and ($darch =~ /ppc/ or $darch =~ /powerpc/))
|
|
{
|
|
$darch = "ppc64";
|
|
}
|
|
elsif ($darch and $darch =~ /amd64/)
|
|
{
|
|
$darch = "x86_64";
|
|
}
|
|
|
|
if ($darch)
|
|
{
|
|
unless ($arch)
|
|
{
|
|
$arch = $darch;
|
|
}
|
|
if ($arch and ($arch ne $darch) and ($arch ne $debarch))
|
|
{
|
|
$callback->(
|
|
{
|
|
error =>
|
|
["Requested Debian architecture $arch, but media is $darch"],
|
|
errorcode => [1]
|
|
}
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
if ((-r $path . "/install/vmlinuz") and ($distname =~ /ubuntu20.04/))
|
|
{
|
|
# Presence of /install/vmlinuz file on Ubuntu20.04 iso indicates
|
|
# a legacy (non subiquity installer) image
|
|
$legacyUB20 = "(legacy)";
|
|
}
|
|
if ($inspection) {
|
|
$callback->(
|
|
{
|
|
info => "DISTNAME:$distname" . "$legacyUB20" ." \n" . "ARCH:$debarch\n" . "DISCNO:$discno\n"
|
|
}
|
|
);
|
|
return;
|
|
}
|
|
%{$request} = (); #clear request we've got it.
|
|
|
|
$callback->(
|
|
{ data => "Copying media to $installroot/$distname/$arch" });
|
|
my $omask = umask 0022;
|
|
mkpath("$installroot/$distname/$arch");
|
|
mkpath("$installroot/$distname/$arch/install/netboot") if ($isnetinst);
|
|
umask $omask;
|
|
my $rc;
|
|
$SIG{INT} = $SIG{TERM} = sub {
|
|
foreach (@cpiopid) {
|
|
kill 2, $_;
|
|
}
|
|
if ($path) {
|
|
chdir("/");
|
|
system("umount $path");
|
|
}
|
|
};
|
|
my $kid;
|
|
chdir $path;
|
|
my $numFiles = `find . -print | wc -l`;
|
|
my $child = open($kid, "|-");
|
|
unless (defined $child) {
|
|
$callback->({ error => "Media copy operation fork failure" });
|
|
return;
|
|
}
|
|
if ($child) {
|
|
push @cpiopid, $child;
|
|
my @finddata = `find .`;
|
|
for (@finddata) {
|
|
print $kid $_;
|
|
}
|
|
close($kid);
|
|
$rc = $?;
|
|
} else {
|
|
my $c = "nice -n 20 cpio -vdump $installroot/$distname/$arch";
|
|
my $k2 = open(PIPE, "$c 2>&1 |") ||
|
|
$callback->({ error => "Media copy operation fork failure" });
|
|
push @cpiopid, $k2;
|
|
my $copied = 0;
|
|
my ($percent, $fout);
|
|
while (<PIPE>) {
|
|
next if /^cpio:/;
|
|
$percent = $copied / $numFiles;
|
|
$fout = sprintf "%0.2f%%", $percent * 100;
|
|
$callback->({ sinfo => "$fout" });
|
|
++$copied;
|
|
}
|
|
exit;
|
|
}
|
|
|
|
# system(
|
|
# "cd $path; find . | nice -n 20 cpio -dump $installroot/$distname/$arch/"
|
|
# );
|
|
chmod 0755, "$installroot/$distname/$arch";
|
|
|
|
# Need to do this otherwise there will be warning about corrupt Packages file
|
|
# when installing a system
|
|
|
|
# Grabs the distribution codename
|
|
my @line = split(" ", `ls -lh $installroot/$distname/$arch/dists/ | grep dr`);
|
|
my $dist = $line[ @line - 1 ];
|
|
|
|
# Ensure the uncompressed Packages file exists. The ISO ships only
|
|
# Packages.gz; an empty Packages file causes apt hash-sum failures
|
|
# during autoinstall because apt reads the zero-length file instead
|
|
# of falling back to the compressed variant.
|
|
my $pkgdir = "$installroot/$distname/$arch/dists/$dist/restricted/binary-$debarch";
|
|
if (-d $pkgdir) {
|
|
my $pkgfile = "$pkgdir/Packages";
|
|
my $pkggz = "$pkgfile.gz";
|
|
if (-f $pkggz && (! -f $pkgfile || -z $pkgfile)) {
|
|
if (open(my $in, '-|', 'gzip', '-dc', $pkggz)) {
|
|
if (open(my $out, '>', $pkgfile)) {
|
|
copy($in, $out);
|
|
close($out);
|
|
}
|
|
close($in);
|
|
}
|
|
} elsif (! -f $pkgfile) {
|
|
open(my $fh, '>', $pkgfile);
|
|
close($fh) if $fh;
|
|
}
|
|
}
|
|
|
|
# removes the links unstable and testing, otherwise the repository does not work for debian
|
|
system("rm -f $installroot/$distname/$arch/dists/unstable");
|
|
system("rm -f $installroot/$distname/$arch/dists/testing");
|
|
|
|
# copies the netboot files for debian
|
|
if ($isnetinst)
|
|
{
|
|
system("cp install.*/initrd.gz $installroot/$distname/$arch/install/netboot/.");
|
|
system("cp install.*/vmlinuz $installroot/$distname/$arch/install/netboot/.");
|
|
}
|
|
|
|
if ($rc != 0)
|
|
{
|
|
$callback->({ error => "Media copy operation failed, status $rc" });
|
|
}
|
|
else
|
|
{
|
|
my $osdistroname = $distname . "-" . $arch;
|
|
my $temppath = "$installroot/$distname/$arch";
|
|
my @ret = xCAT::SvrUtils->update_osdistro_table($distname, $arch, $temppath, $osdistroname);
|
|
if ($ret[0] != 0) {
|
|
$callback->({ data => "Error when updating the osdistro tables: " . $ret[1] });
|
|
}
|
|
|
|
if (($prod eq "Ubuntu") || ($prod eq "Ubuntu-Server")) {
|
|
warn_ubuntu_live_media_pkg_source($callback, $temppath);
|
|
}
|
|
|
|
$callback->({ data => "Media copy operation successful" });
|
|
unless ($noosimage) {
|
|
my @ret = xCAT::SvrUtils->update_tables_with_templates($distname, $arch, $temppath, $osdistroname, $legacyUB20);
|
|
if ($ret[0] != 0) {
|
|
$callback->({ data => "Error when updating the osimage tables: " . $ret[1] });
|
|
}
|
|
my @ret = xCAT::SvrUtils->update_tables_with_diskless_image($distname, $arch, undef, "netboot", $temppath, $osdistroname);
|
|
if ($ret[0] != 0) {
|
|
$callback->({ data => "Error when updating the osimage tables for stateless: " . $ret[1] });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
=head3 subiquity_kcmdline
|
|
|
|
Build the kernel command line for a Subiquity (Ubuntu live installer) diskful install.
|
|
|
|
boot=casper is required: without it casper never processes netboot=nfs, it scans the local
|
|
disks, finds no live media and panics "Unable to find a medium containing a live file
|
|
system" (initramfs emergency shell -> PXE loop).
|
|
|
|
nfsroot MUST be a literal IP: casper mounts the live filesystem with klibc's nfsmount, which
|
|
cannot resolve hostnames ("nfsmount: can't parse IP address '<host>'"). The ds= URL is
|
|
fetched later by cloud-init in the booted live system where normal DNS works, so it keeps
|
|
the install server's name.
|
|
|
|
'toram' makes casper copy the live squashfs into RAM and UNMOUNT the NFS source (casper
|
|
scripts/casper: copy_to_ram then umount of the copy source), so the installer runs from RAM
|
|
with NO network root. This is what lets the node reboot at all: with the NFS root still
|
|
mounted, a process doing I/O to it during systemd-shutdown (lvm, netplan, udev) blocks in
|
|
uninterruptible D state -- it cannot be SIGKILLed, so systemd-shutdown waits forever
|
|
("Waiting for process: <pid> (lvm)") and the node never power-cycles into the disk it just
|
|
installed. casper has no cmdline knob for NFS mount options -- it parses only nfsroot=,
|
|
taking the whole value as the path, so appending ,soft breaks the mount; toram is casper's
|
|
supported way to avoid the network root. The 24.04 layers total ~1.5G, well within the CN's
|
|
RAM.
|
|
|
|
Arguments:
|
|
$base the command line built so far
|
|
$nfsip the install server as a literal IP, for casper's klibc nfsmount
|
|
$pkgdir the install media path exported over NFS
|
|
$instserver the install server name, for the cloud-init seed URL
|
|
$httpport the xCAT HTTP port
|
|
$node the node being installed
|
|
Returns:
|
|
the completed command line
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------------------------------
|
|
sub subiquity_kcmdline {
|
|
my ($base, $nfsip, $pkgdir, $instserver, $httpport, $node) = @_;
|
|
|
|
my $kcmdline = $base;
|
|
$kcmdline .= " autoinstall ip=dhcp boot=casper netboot=nfs nfsroot=${nfsip}:${pkgdir} toram";
|
|
$kcmdline .= " ds=nocloud-net;s=http://${instserver}:${httpport}/install/autoinst/${node}/";
|
|
$kcmdline .= " ---";
|
|
|
|
return $kcmdline;
|
|
}
|
|
|
|
sub mkinstall {
|
|
xCAT::MsgUtils->message("S", "Doing debian mkinstall");
|
|
my $request = shift;
|
|
my $callback = shift;
|
|
my $doreq = shift;
|
|
my @nodes = @{ $request->{node} };
|
|
my $bootparams = ${$request->{bootparams}};
|
|
my $sitetab = xCAT::Table->new('site');
|
|
my $linuximagetab;
|
|
my $osimagetab;
|
|
my %img_hash = ();
|
|
|
|
#>>>>>>>used for trace log start>>>>>>>
|
|
my @args = ();
|
|
my %opt;
|
|
if (ref($request->{arg})) {
|
|
@args = @{ $request->{arg} };
|
|
} else {
|
|
@args = ($request->{arg});
|
|
}
|
|
@ARGV = @args;
|
|
GetOptions('V' => \$opt{V});
|
|
my $verbose_on_off = 0;
|
|
if ($opt{V}) { $verbose_on_off = 1; }
|
|
|
|
#>>>>>>>used for trace log end>>>>>>>
|
|
|
|
my $installroot;
|
|
$installroot = "/install";
|
|
if ($sitetab)
|
|
{
|
|
(my $ref) = $sitetab->getAttribs({ key => 'installdir' }, 'value');
|
|
if ($ref and $ref->{value})
|
|
{
|
|
$installroot = $ref->{value};
|
|
}
|
|
}
|
|
|
|
xCAT::MsgUtils->trace($verbose_on_off, "d", "debian->mkinstall: installroot=$installroot");
|
|
|
|
# Check whether the default getinstdisk script exist, if so, copy it into /install/autoinst/
|
|
if (-r "$::XCATROOT/share/xcat/install/scripts/getinstdisk") {
|
|
if (!(-e "$installroot/autoinst")) {
|
|
mkdir("$installroot/autoinst");
|
|
}
|
|
copy("$::XCATROOT/share/xcat/install/scripts/getinstdisk", "$installroot/autoinst/getinstdisk");
|
|
}
|
|
|
|
my $node;
|
|
my $ostab = xCAT::Table->new('nodetype');
|
|
my %donetftp;
|
|
my $restab = xCAT::Table->new('noderes');
|
|
my $hmtab = xCAT::Table->new('nodehm');
|
|
my $mactab = xCAT::Table->new('mac');
|
|
my %osents = %{ $ostab->getNodesAttribs(\@nodes, [ 'profile', 'os', 'arch', 'provmethod' ]) };
|
|
my %rents =
|
|
%{ $restab->getNodesAttribs(\@nodes,
|
|
[ 'xcatmaster', 'nfsserver', 'primarynic', 'installnic', 'tftpserver' ]) };
|
|
my %hents =
|
|
%{ $hmtab->getNodesAttribs(\@nodes,
|
|
[ 'serialport', 'serialspeed', 'serialflow' ]) };
|
|
my %macents = %{ $mactab->getNodesAttribs(\@nodes, ['mac']) };
|
|
|
|
require xCAT::Template;
|
|
|
|
|
|
foreach $node (@nodes)
|
|
{
|
|
my $os;
|
|
my $arch;
|
|
my $darch;
|
|
my $profile;
|
|
my $tmplfile;
|
|
my $partitionfile;
|
|
my $pkgdir;
|
|
my $pkgdirval;
|
|
my @mirrors;
|
|
my $pkglistfile;
|
|
my $imagename; # set it if running of 'nodeset osimage=xxx'
|
|
my $platform;
|
|
my %tmpl_hash;
|
|
|
|
my $ient = $rents{$node}->[0];
|
|
if ($ient and $ient->{xcatmaster}) {
|
|
$tmpl_hash{"xcatmaster"} = $ient->{xcatmaster};
|
|
}
|
|
if ($ient and $ient->{tftpserver}) {
|
|
$tmpl_hash{"tftpserver"} = $ient->{tftpserver};
|
|
}
|
|
|
|
my $osinst;
|
|
my $ent = $osents{$node}->[0]; #$ostab->getNodeAttribs($node, ['profile', 'os', 'arch']);
|
|
if ($ent and $ent->{provmethod} and ($ent->{provmethod} ne 'install') and ($ent->{provmethod} ne 'netboot') and ($ent->{provmethod} ne 'statelite')) {
|
|
$imagename = $ent->{provmethod};
|
|
if (!exists($img_hash{$imagename})) {
|
|
if (!$osimagetab) {
|
|
$osimagetab = xCAT::Table->new('osimage', -create => 1);
|
|
}
|
|
(my $ref) = $osimagetab->getAttribs({ imagename => $imagename }, 'osvers', 'osarch', 'profile', 'provmethod');
|
|
if ($ref) {
|
|
$img_hash{$imagename}->{osver} = $ref->{'osvers'};
|
|
$img_hash{$imagename}->{osarch} = $ref->{'osarch'};
|
|
$img_hash{$imagename}->{profile} = $ref->{'profile'};
|
|
$img_hash{$imagename}->{provmethod} = $ref->{'provmethod'};
|
|
if (!$linuximagetab) {
|
|
$linuximagetab = xCAT::Table->new('linuximage', -create => 1);
|
|
}
|
|
(my $ref1) = $linuximagetab->getAttribs({ imagename => $imagename }, 'template', 'pkgdir', 'pkglist', 'partitionfile');
|
|
if ($ref1) {
|
|
if ($ref1->{'template'}) {
|
|
$img_hash{$imagename}->{template} = $ref1->{'template'};
|
|
}
|
|
if ($ref1->{'pkgdir'}) {
|
|
$img_hash{$imagename}->{pkgdir} = $ref1->{'pkgdir'};
|
|
}
|
|
if ($ref1->{'pkglist'}) {
|
|
$img_hash{$imagename}->{pkglist} = $ref1->{'pkglist'};
|
|
}
|
|
if ($ref1->{'partitionfile'}) {
|
|
$img_hash{$imagename}->{partitionfile} = $ref1->{'partitionfile'};
|
|
}
|
|
}
|
|
|
|
# if the install template wasn't found, then lets look for it in the default locations.
|
|
unless ($img_hash{$imagename}->{template}) {
|
|
my $pltfrm = getplatform($ref->{'osvers'});
|
|
my $tmplfile = xCAT::SvrUtils::get_tmpl_file_name("$installroot/custom/install/$pltfrm",
|
|
$ref->{'profile'}, $ref->{'osvers'}, $ref->{'osarch'}, $ref->{'osvers'});
|
|
if (using_subiquity($os,$tmplfile)) {
|
|
|
|
# in this context we use the genos parameter to search for the subiquity template
|
|
$tmplfile = xCAT::SvrUtils::get_tmpl_file_name("$installroot/custom/install/$pltfrm",
|
|
$ref->{'profile'}, $ref->{'osvers'}, $ref->{'osarch'}, $ref->{'osvers'}, "subiquity");
|
|
}
|
|
if (!$tmplfile) {
|
|
$tmplfile = xCAT::SvrUtils::get_tmpl_file_name("$::XCATROOT/share/xcat/install/$pltfrm",
|
|
$ref->{'profile'}, $ref->{'osvers'}, $ref->{'osarch'}, $ref->{'osvers'});
|
|
}
|
|
|
|
# if we managed to find it, put it in the hash:
|
|
if ($tmplfile) {
|
|
$img_hash{$imagename}->{template} = $tmplfile;
|
|
}
|
|
}
|
|
|
|
#if the install pkglist wasn't found, then lets look for it in the default locations
|
|
unless ($img_hash{$imagename}->{pkglist}) {
|
|
my $pltfrm = getplatform($ref->{'osvers'});
|
|
my $pkglistfile = xCAT::SvrUtils::get_pkglist_file_name("$installroot/custom/install/$pltfrm",
|
|
$ref->{'profile'}, $ref->{'osvers'}, $ref->{'osarch'}, $ref->{'osvers'});
|
|
if (!$pkglistfile) {
|
|
$pkglistfile = xCAT::SvrUtils::get_pkglist_file_name("$::XCATROOT/share/xcat/install/$pltfrm",
|
|
$ref->{'profile'}, $ref->{'osvers'}, $ref->{'osarch'}, $ref->{'osvers'});
|
|
}
|
|
|
|
# if we managed to find it, put it in the hash:
|
|
if ($pkglistfile) {
|
|
$img_hash{$imagename}->{pkglist} = $pkglistfile;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
xCAT::MsgUtils->report_node_error($callback, $node, "The OS image '$imagename' for node does not exist");
|
|
next;
|
|
}
|
|
}
|
|
|
|
my $ph = $img_hash{$imagename};
|
|
$os = $ph->{osver};
|
|
$arch = $ph->{osarch};
|
|
$profile = $ph->{profile};
|
|
$partitionfile = $ph->{partitionfile};
|
|
$platform = xCAT_plugin::debian::getplatform($os);
|
|
|
|
$tmplfile = $ph->{template};
|
|
$pkgdirval = $ph->{pkgdir};
|
|
my @pkgdirlist = split(/,/, $pkgdirval);
|
|
foreach (@pkgdirlist) {
|
|
if ($_ =~ /^http|ssh/) {
|
|
push @mirrors, $_;
|
|
} else {
|
|
|
|
# If multiple pkgdirs are provided, The first path in the value of osimage.pkgdir
|
|
# must be the OS base pkg dir path, so use the first path as pkgdir
|
|
if (!$pkgdir) {
|
|
$pkgdir = $_;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!$pkgdir) {
|
|
$pkgdir = "$installroot/$os/$arch";
|
|
}
|
|
$pkglistfile = $ph->{pkglist};
|
|
|
|
xCAT::MsgUtils->trace($verbose_on_off, "d", "debian->mkinstall: imagename=$imagename pkgdir=$pkgdir pkglistfile=$pkglistfile tmplfile=$tmplfile");
|
|
}
|
|
else {
|
|
# This is deprecated mode to define node's provmethod, not supported now.
|
|
xCAT::MsgUtils->report_node_error($callback, $node, "OS image name must be specified in nodetype.provmethod");
|
|
next;
|
|
|
|
$os = $ent->{os};
|
|
$arch = $ent->{arch};
|
|
$profile = $ent->{profile};
|
|
$platform = xCAT_plugin::debian::getplatform($os);
|
|
my $genos = $os;
|
|
$genos =~ s/\..*//;
|
|
|
|
$tmplfile = xCAT::SvrUtils::get_tmpl_file_name("$installroot/custom/install/$platform", $profile, $os, $arch, $genos);
|
|
if (!$tmplfile) {
|
|
$tmplfile = xCAT::SvrUtils::get_tmpl_file_name("$::XCATROOT/share/xcat/install/$platform", $profile, $os, $arch, $genos);
|
|
}
|
|
|
|
$pkglistfile = xCAT::SvrUtils::get_pkglist_file_name("$installroot/custom/install/$platform", $profile, $os, $arch, $genos);
|
|
if (!$pkglistfile) {
|
|
$pkglistfile = xCAT::SvrUtils::get_pkglist_file_name("$::XCATROOT/share/xcat/install/$platform", $profile, $os, $arch, $genos);
|
|
}
|
|
|
|
$pkgdir = "$installroot/$os/$arch";
|
|
xCAT::MsgUtils->trace($verbose_on_off, "d", "debian->mkinstall: pkgdir=$pkgdir pkglistfile=$pkglistfile tmplfile=$tmplfile");
|
|
}
|
|
|
|
if ($arch eq "x86_64") {
|
|
$darch = "amd64";
|
|
}
|
|
elsif ($arch eq "x86") {
|
|
$darch = "i386";
|
|
}
|
|
else {
|
|
if ($arch ne "ppc64le" and $arch ne "ppc64el") {
|
|
xCAT::MsgUtils->message("S", "debian.pm: Unknown arch ($arch)");
|
|
}
|
|
$darch = $arch;
|
|
}
|
|
|
|
my @missingparms;
|
|
unless ($os) {
|
|
if ($imagename) {
|
|
push @missingparms, "osimage.osvers";
|
|
}
|
|
else {
|
|
push @missingparms, "nodetype.os";
|
|
}
|
|
}
|
|
|
|
unless ($arch) {
|
|
if ($imagename) {
|
|
push @missingparms, "osimage.osarch";
|
|
}
|
|
else {
|
|
push @missingparms, "nodetype.arch";
|
|
}
|
|
}
|
|
unless ($profile) {
|
|
if ($imagename) {
|
|
push @missingparms, "osimage.profile";
|
|
}
|
|
else {
|
|
push @missingparms, "nodetype.profile";
|
|
}
|
|
}
|
|
|
|
unless ($os and $arch and $profile) {
|
|
xCAT::MsgUtils->report_node_error($callback, $node, "Missing " . join(',', @missingparms) . " for $node");
|
|
next; # No profile
|
|
}
|
|
|
|
unless (-r "$tmplfile") {
|
|
xCAT::MsgUtils->report_node_error($callback, $node, "No $platform preseed template exists for " . $profile);
|
|
next;
|
|
}
|
|
|
|
#Call the Template class to do substitution to produce a preseed file in the autoinst dir
|
|
my $tmperr;
|
|
my $preerr;
|
|
my $posterr;
|
|
if ($imagename) {
|
|
$tmperr = "Unable to find template file: $tmplfile";
|
|
} else {
|
|
$tmperr = "Unable to find template in $installroot/custom/install/$platform or $::XCATROOT/share/xcat/install/$platform (for $profile/$os/$arch combination)";
|
|
}
|
|
if (-r "$tmplfile") {
|
|
my $autoinstfile = "$installroot/autoinst/" . $node;
|
|
|
|
# handle nocloud-net datasource for ubuntu 20.04+
|
|
if (using_subiquity($os,$tmplfile)) {
|
|
|
|
# clean up existing files to make way for the new directory
|
|
if (-f $autoinstfile) {
|
|
unlink($autoinstfile);
|
|
}
|
|
mkpath($autoinstfile);
|
|
|
|
# create empty meta-data and vendor-data files
|
|
open(my $fh, ">", $autoinstfile . "/meta-data");
|
|
close($fh);
|
|
open($fh, ">", $autoinstfile . "/vendor-data");
|
|
print $fh "{}\n";
|
|
close($fh);
|
|
|
|
# point the template output at the /user-data file
|
|
$autoinstfile = "$autoinstfile/user-data";
|
|
}
|
|
$tmperr =
|
|
xCAT::Template->subvars($tmplfile,
|
|
$autoinstfile,
|
|
$node,
|
|
$pkglistfile,
|
|
$pkgdir,
|
|
$platform,
|
|
$partitionfile,
|
|
\%tmpl_hash
|
|
);
|
|
}
|
|
|
|
# maybe Debian will decide to use subiquity at some point?
|
|
my $prescript = "$::XCATROOT/share/xcat/install/scripts/pre.$platform";
|
|
if (using_subiquity($os,$tmplfile)) {
|
|
$prescript = $prescript . ".subiquity";
|
|
}
|
|
my $postscript = "$::XCATROOT/share/xcat/install/scripts/post.$platform";
|
|
|
|
# for powerkvm VM ubuntu LE#
|
|
if ($arch =~ /ppc64/i and $platform eq "ubuntu") {
|
|
$prescript = "$::XCATROOT/share/xcat/install/scripts/pre.$platform.ppc64";
|
|
}
|
|
|
|
|
|
if (-r "$prescript") {
|
|
$preerr = xCAT::Template->subvars($prescript,
|
|
"$installroot/autoinst/" . $node . ".pre",
|
|
$node,
|
|
"",
|
|
"",
|
|
"",
|
|
$partitionfile,
|
|
\%tmpl_hash
|
|
);
|
|
}
|
|
|
|
if (-r "$postscript") {
|
|
$posterr = xCAT::Template->subvars($postscript,
|
|
"$installroot/autoinst/" . $node . ".post",
|
|
$node,
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
\%tmpl_hash
|
|
);
|
|
}
|
|
|
|
my $errtmp;
|
|
|
|
if ($errtmp = $tmperr or $errtmp = $preerr or $errtmp = $posterr) {
|
|
xCAT::MsgUtils->report_node_error($callback, $node, $errtmp);
|
|
next;
|
|
}
|
|
|
|
if ($arch =~ /ppc64/i and !(-e "$pkgdir/install/netboot/initrd.gz") and
|
|
!(-e "$pkgdir/install/netboot/ubuntu-installer/$darch/initrd.gz")) {
|
|
xCAT::MsgUtils->report_node_error($callback, $node,
|
|
"The network boot initrd.gz is not found in $pkgdir/install/netboot. This is provided by Ubuntu, please download and retry."
|
|
);
|
|
next;
|
|
}
|
|
my $tftpdir = "/tftpboot";
|
|
|
|
# create the node-specific post scripts
|
|
#mkpath "$installroot/postscripts/";
|
|
my $kernpath;
|
|
my $initrdpath;
|
|
my $maxmem;
|
|
|
|
if (
|
|
(
|
|
($arch =~ /x86/ and
|
|
(
|
|
(-r "$pkgdir/install/hwe-netboot/ubuntu-installer/$darch/linux"
|
|
and $kernpath = "$pkgdir/install/hwe-netboot/ubuntu-installer/$darch/linux"
|
|
and -r "$pkgdir/install/hwe-netboot/ubuntu-installer/$darch/initrd.gz"
|
|
and $initrdpath = "$pkgdir/install/hwe-netboot/ubuntu-installer/$darch/initrd.gz"
|
|
) or
|
|
(-r "$pkgdir/install/netboot/ubuntu-installer/$darch/linux"
|
|
and $kernpath = "$pkgdir/install/netboot/ubuntu-installer/$darch/linux"
|
|
and -r "$pkgdir/install/netboot/ubuntu-installer/$darch/initrd.gz"
|
|
and $initrdpath = "$pkgdir/install/netboot/ubuntu-installer/$darch/initrd.gz"
|
|
) or
|
|
(-r "$pkgdir/install/netboot/vmlinuz"
|
|
and $kernpath = "$pkgdir/install/netboot/vmlinuz"
|
|
and -r "$pkgdir/install/netboot/initrd.gz"
|
|
and $initrdpath = "$pkgdir/install/netboot/initrd.gz"
|
|
) or
|
|
(-r "$pkgdir/casper/hwe-vmlinuz"
|
|
and $kernpath = "$pkgdir/casper/hwe-vmlinuz"
|
|
and -r "$pkgdir/casper/hwe-initrd"
|
|
and $initrdpath = "$pkgdir/casper/hwe-initrd"
|
|
) or
|
|
(-r "$pkgdir/casper/vmlinuz"
|
|
and $kernpath = "$pkgdir/casper/vmlinuz"
|
|
and -r "$pkgdir/casper/initrd"
|
|
and $initrdpath = "$pkgdir/casper/initrd"
|
|
)
|
|
)
|
|
) or (
|
|
$arch =~ /ppc64/i and (
|
|
(-r "$pkgdir/install/netboot/ubuntu-installer/$darch/vmlinux"
|
|
and $kernpath = "$pkgdir/install/netboot/ubuntu-installer/$darch/vmlinux"
|
|
and -r "$pkgdir/install/netboot/ubuntu-installer/$darch/initrd.gz"
|
|
and $initrdpath = "$pkgdir/install/netboot/ubuntu-installer/$darch/initrd.gz"
|
|
) or
|
|
(-r "$pkgdir/install/vmlinux"
|
|
and $kernpath = "$pkgdir/install/vmlinux"
|
|
and -r "$pkgdir/install/netboot/initrd.gz"
|
|
and $initrdpath = "$pkgdir/install/netboot/initrd.gz"
|
|
)
|
|
)
|
|
)
|
|
)
|
|
) {
|
|
#TODO: driver slipstream, targetted for network.
|
|
|
|
# Copy the install resource to /tftpboot and check to only copy once
|
|
my $docopy = 0;
|
|
my $tftppath;
|
|
my $rtftppath; # the relative tftp path without /tftpboot/
|
|
if ($imagename) {
|
|
$tftppath = "$tftpdir/xcat/osimage/$imagename";
|
|
$rtftppath = "xcat/osimage/$imagename";
|
|
unless ($donetftp{$imagename}) {
|
|
$docopy = 1;
|
|
$donetftp{$imagename} = 1;
|
|
}
|
|
} else {
|
|
$tftppath = "/$tftpdir/xcat/$os/$arch/$profile";
|
|
$rtftppath = "xcat/$os/$arch/$profile";
|
|
unless ($donetftp{"$os|$arch"}) {
|
|
$docopy = 1;
|
|
$donetftp{"$os|$arch"} = 1;
|
|
}
|
|
}
|
|
|
|
if ($docopy) {
|
|
mkpath("$tftppath");
|
|
copy($kernpath, "$tftppath/vmlinuz");
|
|
# we don't want to customise the subiquity initrd
|
|
if (using_subiquity($os,$tmplfile)) {
|
|
copy($initrdpath, "$tftppath/initrd.img");
|
|
} else {
|
|
copyAndAddCustomizations($initrdpath, "$tftppath/initrd.img");
|
|
}
|
|
}
|
|
|
|
# We have a shot...
|
|
my $ent = $rents{$node}->[0];
|
|
my $sent = $hents{$node}->[0];
|
|
my $macent = $macents{$node}->[0];
|
|
my $instserver;
|
|
if ($ent and $ent->{xcatmaster}) {
|
|
$instserver = $ent->{xcatmaster};
|
|
}
|
|
else {
|
|
$instserver = '!myipfn!';
|
|
}
|
|
|
|
my $httpport="80";
|
|
my @hports=xCAT::TableUtils->get_site_attribute("httpport");
|
|
if ($hports[0]){
|
|
$httpport=$hports[0];
|
|
}
|
|
|
|
if ($ent and $ent->{nfsserver}) {
|
|
$instserver = $ent->{nfsserver};
|
|
}
|
|
|
|
my $kcmdline = "nofb utf8 auto xcatd=" . $instserver;
|
|
|
|
if (using_subiquity($os,$tmplfile)) {
|
|
my $nfsip = xCAT::NetworkUtils->getipaddr($instserver) || $instserver;
|
|
$kcmdline = subiquity_kcmdline($kcmdline, $nfsip, $pkgdir, $instserver, $httpport, $node);
|
|
} else {
|
|
$kcmdline .= " url=http://${instserver}:$httpport/install/autoinst/$node";
|
|
$kcmdline .= " mirror/http/hostname=${instserver}:$httpport";
|
|
|
|
# default answers as much as possible, we don't want any interactiveness :)
|
|
$kcmdline .= " priority=critical";
|
|
|
|
# parse Mac table to get one mac address in case there are multiples.
|
|
my $net_params = xCAT::NetworkUtils->gen_net_boot_params($ent->{installnic}, $ent->{primarynic}, $mac);
|
|
if (exists($net_params->{nicname})) {
|
|
$kcmdline .= " netcfg/choose_interface=" . $net_params->{nicname};
|
|
} elsif (exists($net_params->{mac})) {
|
|
$kcmdline .= " netcfg/choose_interface=" . $net_params->{mac};
|
|
}
|
|
|
|
#from 12.10, the live install changed, so add the live-installer
|
|
if (-r "$pkgdir/install/filesystem.squashfs") {
|
|
$kcmdline .= " live-installer/net-image=http://${instserver}:$httpport${pkgdir}/install/filesystem.squashfs";
|
|
}
|
|
}
|
|
|
|
if ($maxmem) {
|
|
$kcmdline .= " mem=$maxmem";
|
|
}
|
|
|
|
# parse Mac table to get one mac address in case there are multiples.
|
|
my $mac;
|
|
if ($macent->{mac}) {
|
|
$mac = xCAT::Utils->parseMacTabEntry($macent->{mac}, $node);
|
|
}
|
|
|
|
#TODO: dd=<url> for driver disks
|
|
if (defined($sent->{serialport})) {
|
|
unless ($sent->{serialspeed}) {
|
|
xCAT::MsgUtils->report_node_error($callback, $node, "serialport defined, but no serialspeed for this node in nodehm table");
|
|
next;
|
|
}
|
|
if ($arch =~ /ppc64/i) {
|
|
$kcmdline .= " console=tty0 console=hvc" . $sent->{serialport} . "," . $sent->{serialspeed};
|
|
} else {
|
|
$kcmdline .= " console=tty0 console=ttyS" . $sent->{serialport} . "," . $sent->{serialspeed};
|
|
}
|
|
if ($sent->{serialflow} =~ /(hard|cts|ctsrts)/) {
|
|
$kcmdline .= "n8r";
|
|
}
|
|
} else {
|
|
$callback->({ warning => ["rcons may not work since no serialport is specified for $node"], });
|
|
}
|
|
|
|
# need to add these in, otherwise aptitude will ask questions
|
|
$kcmdline .= " locale=en_US";
|
|
|
|
#$kcmdline .= " netcfg/wireless_wep= netcfg/get_hostname= netcfg/get_domain=";
|
|
|
|
# Automatically detect all HDD
|
|
# $kcmdline .= " all-generic-ide irqpoll";
|
|
|
|
# by default do text based install
|
|
# $kcmdline .= " DEBIAN_FRONTEND=text";
|
|
|
|
# Maybe useful for debugging purposes
|
|
#
|
|
# $kcmdline .= " BOOT_DEBUG=3";
|
|
# $kcmdline .= " DEBCONF_DEBUG=5";
|
|
|
|
# I don't need the timeout for ubuntu, but for debian there is a problem with getting dhcp in a timely manner
|
|
# safer way to set hostname, avoid problems with nameservers
|
|
$kcmdline .= " hostname=" . $node;
|
|
|
|
xCAT::MsgUtils->trace($verbose_on_off, "d", "debian->mkinstall: kcmdline=$kcmdline kernal=$rtftppath/vmlinuz initrd=$rtftppath/initrd.img");
|
|
$bootparams->{$node}->[0]->{kernel} = "$rtftppath/vmlinuz";
|
|
$bootparams->{$node}->[0]->{initrd} = "$rtftppath/initrd.img";
|
|
$bootparams->{$node}->[0]->{kcmdline} = $kcmdline;
|
|
}
|
|
else {
|
|
xCAT::MsgUtils->report_node_error($callback, $node, "Install image not found in $installroot/$os/$arch");
|
|
next;
|
|
}
|
|
}# end foreach node
|
|
}
|
|
|
|
sub mknetboot
|
|
{
|
|
my $xenstyle = 0;
|
|
my $req = shift;
|
|
my $callback = shift;
|
|
my $doreq = shift;
|
|
my $statelite = 0;
|
|
if ($req->{command}->[0] =~ 'mkstatelite') {
|
|
$statelite = "true";
|
|
}
|
|
my $bootparams = ${$req->{bootparams}};
|
|
my $tftpdir = "/tftpboot";
|
|
my $nodes = @{ $req->{node} };
|
|
my @args = @{ $req->{arg} };
|
|
my @nodes = @{ $req->{node} };
|
|
my $ostab = xCAT::Table->new('nodetype');
|
|
my $sitetab = xCAT::Table->new('site');
|
|
my $linuximagetab;
|
|
my $osimagetab;
|
|
my %img_hash = ();
|
|
my $installroot;
|
|
$installroot = "/install";
|
|
my $xcatdport = "3001";
|
|
my $xcatiport = "3002";
|
|
my $nodestatus = "y";
|
|
my $httpport="80";
|
|
my @myself = xCAT::NetworkUtils->determinehostname();
|
|
my $myname = $myself[ (scalar @myself) - 1 ];
|
|
|
|
if ($sitetab)
|
|
{
|
|
(my $ref) = $sitetab->getAttribs({ key => 'installdir' }, 'value');
|
|
if ($ref and $ref->{value})
|
|
{
|
|
$installroot = $ref->{value};
|
|
}
|
|
($ref) = $sitetab->getAttribs({ key => 'xcatdport' }, 'value');
|
|
if ($ref and $ref->{value})
|
|
{
|
|
$xcatdport = $ref->{value};
|
|
}
|
|
($ref) = $sitetab->getAttribs({ key => 'xcatiport' }, 'value');
|
|
if ($ref and $ref->{value})
|
|
{
|
|
$xcatiport = $ref->{value};
|
|
}
|
|
($ref) = $sitetab->getAttribs({ key => 'tftpdir' }, 'value');
|
|
if ($ref and $ref->{value})
|
|
{
|
|
$globaltftpdir = $ref->{value};
|
|
}
|
|
($ref) = $sitetab->getAttribs({ key => 'nodestatus' }, 'value');
|
|
if ($ref and $ref->{value})
|
|
{
|
|
$nodestatus = $ref->{value};
|
|
}
|
|
($ref) = $sitetab->getAttribs({ key => 'httpport' }, 'value');
|
|
if ($ref and $ref->{value})
|
|
{
|
|
$httpport = $ref->{value};
|
|
}
|
|
|
|
}
|
|
my %donetftp = ();
|
|
my %oents = %{ $ostab->getNodesAttribs(\@nodes, [qw(os arch profile provmethod)]) };
|
|
my $restab = xCAT::Table->new('noderes');
|
|
my $hmtab = xCAT::Table->new('nodehm');
|
|
my $mactab = xCAT::Table->new('mac');
|
|
|
|
my $machash = $mactab->getNodesAttribs(\@nodes, [ 'interface', 'mac' ]);
|
|
|
|
my $reshash = $restab->getNodesAttribs(\@nodes, [ 'primarynic', 'tftpserver', 'xcatmaster', 'nfsserver', 'nfsdir', 'installnic' ]);
|
|
my $hmhash =
|
|
$hmtab->getNodesAttribs(\@nodes,
|
|
[ 'serialport', 'serialspeed', 'serialflow' ]);
|
|
my $statetab;
|
|
my $stateHash;
|
|
if ($statelite) {
|
|
$statetab = xCAT::Table->new('statelite', -create => 1);
|
|
$stateHash = $statetab->getNodesAttribs(\@nodes, ['statemnt']);
|
|
}
|
|
|
|
foreach my $node (@nodes)
|
|
{
|
|
my $osver;
|
|
my $arch;
|
|
my $profile;
|
|
my $platform;
|
|
my $rootimgdir;
|
|
my $nodebootif; # nodebootif will be used if noderes.installnic is not set
|
|
my $dump; # for kdump, its format is "nfs://<nfs_server_ip>/<kdump_path>"
|
|
my $crashkernelsize;
|
|
my $rootfstype;
|
|
my $nfsrootopts;
|
|
my $tftpdir;
|
|
my $imagename; # set it if running of 'nodeset osimage=xxx'
|
|
|
|
if ($reshash->{$node}->[0] and $reshash->{$node}->[0]->{tftpdir}) {
|
|
$tftpdir = $reshash->{$node}->[0]->{tftpdir};
|
|
} else {
|
|
$tftpdir = $globaltftpdir;
|
|
}
|
|
|
|
my $ent = $oents{$node}->[0]; #ostab->getNodeAttribs($node, ['os', 'arch', 'profile']);
|
|
if ($ent and $ent->{provmethod} and ($ent->{provmethod} ne 'install') and ($ent->{provmethod} ne 'netboot') and ($ent->{provmethod} ne 'statelite')) {
|
|
$imagename = $ent->{provmethod};
|
|
if (!exists($img_hash{$imagename})) {
|
|
if (!$osimagetab) {
|
|
$osimagetab = xCAT::Table->new('osimage', -create => 1);
|
|
}
|
|
(my $ref) = $osimagetab->getAttribs({ imagename => $imagename }, 'osvers', 'osarch', 'profile', 'provmethod', 'rootfstype', 'nfsrootopts');
|
|
if ($ref) {
|
|
$img_hash{$imagename}->{osver} = $ref->{'osvers'};
|
|
$img_hash{$imagename}->{osarch} = $ref->{'osarch'};
|
|
$img_hash{$imagename}->{profile} = $ref->{'profile'};
|
|
$img_hash{$imagename}->{provmethod} = $ref->{'provmethod'};
|
|
$img_hash{$imagename}->{rootfstype} = $ref->{'rootfstype'};
|
|
$img_hash{$imagename}->{nfsrootopts} = $ref->{'nfsrootopts'};
|
|
if (!$linuximagetab) {
|
|
$linuximagetab = xCAT::Table->new('linuximage', -create => 1);
|
|
}
|
|
(my $ref1) = $linuximagetab->getAttribs({ imagename => $imagename }, 'rootimgdir');
|
|
if (($ref1) && ($ref1->{'rootimgdir'})) {
|
|
$img_hash{$imagename}->{rootimgdir} = $ref1->{'rootimgdir'};
|
|
}
|
|
if (($ref1) && ($ref1->{'nodebootif'})) {
|
|
$img_hash{$imagename}->{nodebootif} = $ref1->{'nodebootif'};
|
|
}
|
|
if ($ref1) {
|
|
if ($ref1->{'dump'}) {
|
|
$img_hash{$imagename}->{dump} = $ref1->{'dump'};
|
|
}
|
|
}
|
|
if (($ref1) && ($ref1->{'crashkernelsize'})) {
|
|
$img_hash{$imagename}->{crashkernelsize} = $ref1->{'crashkernelsize'};
|
|
}
|
|
} else {
|
|
xCAT::MsgUtils->report_node_error($callback, $node, "The OS image '$imagename' for node does not exist");
|
|
next;
|
|
}
|
|
}
|
|
my $ph = $img_hash{$imagename};
|
|
$osver = $ph->{osver};
|
|
$arch = $ph->{osarch};
|
|
$profile = $ph->{profile};
|
|
|
|
$rootfstype = $ph->{rootfstype};
|
|
$nfsrootopts = $ph->{nfsrootopts};
|
|
|
|
$rootimgdir = $ph->{rootimgdir};
|
|
unless ($rootimgdir) {
|
|
$rootimgdir = "$installroot/netboot/$osver/$arch/$profile";
|
|
}
|
|
|
|
$nodebootif = $ph->{nodebootif};
|
|
$crashkernelsize = $ph->{crashkernelsize};
|
|
$dump = $ph->{dump};
|
|
}
|
|
else {
|
|
# This is deprecated mode to define node's provmethod, not supported now.
|
|
xCAT::MsgUtils->report_node_error($callback, $node, "OS image name must be specified in nodetype.provmethod");
|
|
next;
|
|
|
|
$osver = $ent->{os};
|
|
$arch = $ent->{arch};
|
|
$profile = $ent->{profile};
|
|
$rootimgdir = "$installroot/netboot/$osver/$arch/$profile";
|
|
|
|
$rootfstype = "nfs"; # TODO: try to get it from the option or table
|
|
my $imgname;
|
|
if ($statelite) {
|
|
$imgname = "$osver-$arch-statelite-$profile";
|
|
} else {
|
|
$imgname = "$osver-$arch-netboot-$profile";
|
|
}
|
|
|
|
|
|
if (!$osimagetab) {
|
|
$osimagetab = xCAT::Table->new('osimage');
|
|
}
|
|
|
|
if ($osimagetab) {
|
|
my ($ref1) = $osimagetab->getAttribs({ imagename => $imgname }, 'rootfstype', 'nfsrootopts');
|
|
if ($ref1) {
|
|
$rootfstype = $ref1->{'rootfstype'} if $ref1->{'rootfstype'};
|
|
$nfsrootopts = $ref1->{'nfsrootopts'};
|
|
}
|
|
} else {
|
|
xCAT::MsgUtils->report_node_error($callback, $node,
|
|
qq{Cannot find the linux image called "$osver-$arch-$imgname-$profile", maybe you need to use the "nodeset <nr> osimage=<osimage name>" command to set the boot state}
|
|
);
|
|
next;
|
|
}
|
|
|
|
if (!$linuximagetab) {
|
|
$linuximagetab = xCAT::Table->new('linuximage');
|
|
}
|
|
if ($linuximagetab) {
|
|
(my $ref1) = $linuximagetab->getAttribs({ imagename => $imgname }, 'dump', 'crashkernelsize');
|
|
if ($ref1 and $ref1->{'dump'}) {
|
|
$dump = $ref1->{'dump'};
|
|
}
|
|
if ($ref1 and $ref1->{'crashkernelsize'}) {
|
|
$crashkernelsize = $ref1->{'crashkernelsize'};
|
|
}
|
|
} else {
|
|
xCAT::MsgUtils->report_node_error($callback, $node,
|
|
qq{Cannot find the linux image called "$osver-$arch-$imgname-$profile", maybe you need to use the "nodeset <nr> osimage=<osimage name>" command to set the boot state}
|
|
);
|
|
next;
|
|
}
|
|
}
|
|
|
|
#print"osvr=$osver, arch=$arch, profile=$profile, imgdir=$rootimgdir\n";
|
|
unless ($osver and $arch and $profile)
|
|
{
|
|
xCAT::MsgUtils->report_node_error($callback, $node, "Insufficient nodetype entry or osimage entry for $node");
|
|
next;
|
|
}
|
|
|
|
$platform = xCAT_plugin::debian::getplatform($osver);
|
|
my $compressedrootimg=xCAT::SvrUtils->searchcompressedrootimg("$rootimgdir");
|
|
|
|
# statelite images are not packed.
|
|
if ($statelite) {
|
|
unless (-r "$rootimgdir/kernel") {
|
|
xCAT::MsgUtils->report_node_error($callback, $node,
|
|
qq{Did you run "genimage" before running "liteimg"? kernel cannot be found at $rootimgdir/kernel on $myname}
|
|
);
|
|
next;
|
|
}
|
|
if (!-r "$rootimgdir/initrd-statelite.gz") {
|
|
if (!-r "$rootimgdir/initrd.gz") {
|
|
xCAT::MsgUtils->report_node_error($callback, $node,
|
|
qq{Did you run "genimage" before running "liteimg"? initrd.gz or initrd-statelite.gz cannot be found}
|
|
);
|
|
next;
|
|
}
|
|
else {
|
|
copy("$rootimgdir/initrd.gz", "$rootimgdir/initrd-statelite.gz");
|
|
}
|
|
}
|
|
if ($rootfstype eq "ramdisk" and !-r "$rootimgdir/rootimg-statelite.gz") {
|
|
xCAT::MsgUtils->report_node_error($callback, $node,
|
|
qq{No packed image for platform $osver, architecture $arch and profile $profile, please run "liteimg" to create it.}
|
|
);
|
|
next;
|
|
}
|
|
} else {
|
|
unless (-r "$rootimgdir/kernel") {
|
|
xCAT::MsgUtils->report_node_error($callback, $node,
|
|
qq{Did you run "genimage" before running "packimage"? kernel cannot be found at $rootimgdir/kernel on $myname}
|
|
);
|
|
next;
|
|
}
|
|
if (!-r "$rootimgdir/initrd-stateless.gz") {
|
|
if (!-r "$rootimgdir/initrd.gz") {
|
|
xCAT::MsgUtils->report_node_error($callback, $node,
|
|
qq{Did you run "genimage" before running "packimg"? initrd.gz or initrd-statelite.gz cannot be found}
|
|
);
|
|
next;
|
|
next;
|
|
}
|
|
else {
|
|
copy("$rootimgdir/initrd.gz", "$rootimgdir/initrd-stateless.gz");
|
|
}
|
|
}
|
|
unless (-f -r "$rootimgdir/$compressedrootimg") {
|
|
xCAT::MsgUtils->report_node_error($callback, $node,
|
|
"No packed image for platform $osver, architecture $arch, and profile $profile, please run packimage (e.g. packimage -o $osver -p $profile -a $arch)"
|
|
);
|
|
next;
|
|
}
|
|
}
|
|
|
|
# create the node-specific post scripts
|
|
#mkpath "/install/postscripts/";
|
|
#xCAT::Postage->writescript($node,"/install/postscripts/".$node, "netboot", $callback);
|
|
|
|
# Copy the boot resource to /tftpboot and check to only copy once
|
|
my $docopy = 0;
|
|
my $tftppath;
|
|
my $rtftppath; # the relative tftp path without /tftpboot/
|
|
if ($imagename) {
|
|
$tftppath = "$tftpdir/xcat/osimage/$imagename";
|
|
$rtftppath = "xcat/osimage/$imagename";
|
|
unless ($donetftp{$imagename}) {
|
|
$docopy = 1;
|
|
$donetftp{$imagename} = 1;
|
|
}
|
|
} else {
|
|
$tftppath = "/$tftpdir/xcat/netboot/$osver/$arch/$profile/";
|
|
$rtftppath = "xcat/netboot/$osver/$arch/$profile/";
|
|
unless ($donetftp{ $osver, $arch, $profile }) {
|
|
$docopy = 1;
|
|
$donetftp{ $osver, $arch, $profile } = 1;
|
|
}
|
|
}
|
|
|
|
if ($docopy) {
|
|
mkpath("$tftppath");
|
|
if (-f "$rootimgdir/hypervisor") {
|
|
copy("$rootimgdir/hypervisor", "$tftppath");
|
|
$xenstyle = 1;
|
|
}
|
|
copy("$rootimgdir/kernel", "$tftppath");
|
|
if ($statelite) {
|
|
if ($rootfstype eq "ramdisk") {
|
|
copy("$rootimgdir/initrd-stateless.gz", "$tftppath");
|
|
} else {
|
|
copy("$rootimgdir/initrd-statelite.gz", "$tftppath");
|
|
}
|
|
} else {
|
|
copy("$rootimgdir/initrd-stateless.gz", "$tftppath");
|
|
}
|
|
}
|
|
|
|
if ($statelite) {
|
|
my $initrdloc = "$tftppath";
|
|
if ($rootfstype eq "ramdisk") {
|
|
$initrdloc .= "/initrd-stateless.gz";
|
|
} else {
|
|
$initrdloc .= "/initrd-statelite.gz";
|
|
}
|
|
unless (-r "$tftppath/kernel" and -r $initrdloc) {
|
|
xCAT::MsgUtils->report_node_error($callback, $node, qq{Copying to $tftppath failed.});
|
|
next;
|
|
}
|
|
} else {
|
|
|
|
unless (-r "$tftppath/kernel" and -r "$tftppath/initrd-stateless.gz") {
|
|
xCAT::MsgUtils->report_node_error($callback, $node, qq{Copying to $tftppath failed.});
|
|
next;
|
|
}
|
|
}
|
|
my $ent = $reshash->{$node}->[0]; #$restab->getNodeAttribs($node, ['primarynic']);
|
|
my $sent = $hmhash->{$node}->[0];
|
|
|
|
# $hmtab->getNodeAttribs($node,
|
|
# ['serialport', 'serialspeed', 'serialflow']);
|
|
|
|
# determine image server, if tftpserver use it, else use xcatmaster
|
|
# last resort use self
|
|
my $imgsrv;
|
|
my $ient;
|
|
my $xcatmaster;
|
|
|
|
$ient = $reshash->{$node}->[0]; #$restab->getNodeAttribs($node, ['tftpserver']);
|
|
if ($ient and $ient->{xcatmaster}) {
|
|
$xcatmaster = $ient->{xcatmaster};
|
|
} else {
|
|
$xcatmaster = '!myipfn!'; #allow service nodes to dynamically nominate themselves as a good contact point, this is of limited use in the event that xcat is not the dhcp/tftp server
|
|
}
|
|
|
|
if ($ient and $ient->{nfsserver} and $ient->{nfsserver} ne '<xcatmaster>') {
|
|
$imgsrv = $ient->{nfsserver};
|
|
}elsif ($ient and $ient->{tftpserver} and $ient->{tftpserver} ne '<xcatmaster>') {
|
|
$imgsrv = $ient->{tftpserver};
|
|
} else {
|
|
$imgsrv = $xcatmaster;
|
|
}
|
|
unless ($imgsrv) {
|
|
xCAT::MsgUtils->report_node_error($callback, $node, "Unable to determine or reasonably guess the image server for $node");
|
|
next;
|
|
}
|
|
|
|
my $kcmdline;
|
|
if ($statelite) {
|
|
if (rootfstype ne "ramdisk") {
|
|
|
|
# get entry for nfs root if it exists:
|
|
# have to get nfssvr and nfsdir from noderes table
|
|
my $nfssrv = $imgsrv;
|
|
my $nfsdir = $rootimgdir;
|
|
if ($ient->{nfsdir} ne '') {
|
|
$nfsdir = $ient->{nfsdir} . "/netboot/$osver/$arch/$profile";
|
|
|
|
#this code sez, "if nfsdir starts with //, then
|
|
#use a absolute path, i.e. do not append xCATisms"
|
|
#this is required for some statelite envs.
|
|
#still open for debate.
|
|
|
|
if ($ient->{nfsdir} =~ m!^//!) {
|
|
$nfsdir = $ient->{nfsdir};
|
|
$nfsdir =~ s!^/!!;
|
|
}
|
|
}
|
|
|
|
# special case for redhat6, fedora12/13/14
|
|
if (&using_dracut($osver)) {
|
|
my ($nfsroot, $error) = xCAT::SvrUtils->build_statelite_nfsroot_parameter($nfssrv, "$nfsdir/rootimg", $nfsrootopts);
|
|
if ($error) {
|
|
xCAT::MsgUtils->report_node_error($callback, $node, $error);
|
|
next;
|
|
}
|
|
$kcmdline = "$nfsroot STATEMNT=";
|
|
} else {
|
|
$kcmdline = "NFSROOT=$nfssrv:$nfsdir STATEMNT=";
|
|
}
|
|
} else {
|
|
$kcmdline = "imgurl=http://$imgsrv:$httpport/$rootimgdir/rootimg-statelite.gz STATEMNT=";
|
|
}
|
|
|
|
|
|
# add support for subVars in the value of "statemnt"
|
|
my $statemnt = "";
|
|
if (exists($stateHash->{$node})) {
|
|
$statemnt = $stateHash->{$node}->[0]->{statemnt};
|
|
if (grep /\$/, $statemnt) {
|
|
my ($server, $dir) = split(/:/, $statemnt);
|
|
|
|
#if server is blank, then its the directory
|
|
unless ($dir) {
|
|
$dir = $server;
|
|
$server = '';
|
|
}
|
|
if (grep /\$|#CMD/, $dir) {
|
|
$dir = xCAT::SvrUtils->subVars($dir, $node, 'dir', $callback);
|
|
$dir = ~s/\/\//\//g;
|
|
}
|
|
if ($server) {
|
|
$server = xCAT::SvrUtils->subVars($server, $node, 'server', $callback);
|
|
}
|
|
$statemnt = $server . ":" . $dir;
|
|
}
|
|
}
|
|
$kcmdline .= $statemnt . " ";
|
|
$kcmdline .=
|
|
"XCAT=$xcatmaster:$xcatdport ";
|
|
$kcmdline .=
|
|
"NODE=$node ";
|
|
|
|
# add flow control setting
|
|
$kcmdline .= "FC=$useflowcontrol ";
|
|
|
|
# BEGIN service node
|
|
my $isSV = xCAT::Utils->isServiceNode();
|
|
my $res = xCAT::Utils->runcmd("hostname", 0);
|
|
my $sip = inet_ntoa(inet_aton($res)); # this is the IP of service node
|
|
if ($isSV and (($xcatmaster eq $sip) or ($xcatmaster eq $res))) {
|
|
|
|
# if the NFS directory in litetree is on the service node,
|
|
# and it is not exported, then it will be mounted automatically
|
|
xCAT::SvrUtils->setupNFSTree($node, $sip, $callback);
|
|
|
|
# then, export the statemnt directory if it is on the service node
|
|
if ($statemnt) {
|
|
xCAT::SvrUtils->setupStatemnt($sip, $statemnt, $callback);
|
|
}
|
|
}
|
|
|
|
# END service node
|
|
}
|
|
else
|
|
{
|
|
$kcmdline =
|
|
"imgurl=http://$imgsrv:$httpport/$rootimgdir/$compressedrootimg ";
|
|
$kcmdline .= "XCAT=$xcatmaster:$xcatdport ";
|
|
}
|
|
|
|
|
|
# if site.nodestatus='n', add "nonodestatus" to kcmdline to inform the node not to update nodestatus during provision
|
|
if (($nodestatus eq "n") or ($nodestatus eq "N") or ($nodestatus eq "0")) {
|
|
$kcmdline .= " nonodestatus ";
|
|
}
|
|
|
|
|
|
$kcmdline .=" XCATHTTPPORT=$httpport ";
|
|
if (($::XCATSITEVALS{xcatdebugmode} eq "1") or ($::XCATSITEVALS{xcatdebugmode} eq "2")) {
|
|
|
|
my ($host, $ipaddr) = xCAT::NetworkUtils->gethostnameandip($xcatmaster);
|
|
if ($ipaddr) {
|
|
$kcmdline .= " LOGSERVER=$ipaddr ";
|
|
}
|
|
else {
|
|
$kcmdline .= " LOGSERVER=$xcatmaster ";
|
|
}
|
|
|
|
$kcmdline .= " xcatdebugmode=$::XCATSITEVALS{xcatdebugmode} ";
|
|
}
|
|
|
|
|
|
# add one parameter: ifname=<eth0>:<mac address>
|
|
# which is used for dracut
|
|
# the redhat5.x os will ignore it
|
|
|
|
my $installnic = undef;
|
|
my $primarynic = undef;
|
|
my $mac = undef;
|
|
|
|
if ($reshash->{$node}->[0] and $reshash->{$node}->[0]->{installnic}) {
|
|
$installnic = $reshash->{$node}->[0]->{installnic};
|
|
}
|
|
if ($reshash->{$node}->[0] and $reshash->{$node}->[0]->{primarynic}) {
|
|
$primarynic = $reshash->{$node}->[0]->{primarynic};
|
|
}
|
|
|
|
#else { #no, we autodetect and don't presume anything
|
|
# $kcmdline .="eth0:";
|
|
# print "eth0 is used as the default booting network devices...\n";
|
|
#}
|
|
# append the mac address
|
|
my $mac;
|
|
if ($machash->{$node}->[0] && $machash->{$node}->[0]->{'mac'}) {
|
|
|
|
# TODO: currently, only "mac" attribute with classic style is used, the "|" delimited string of "macaddress!hostname" format is not used
|
|
$mac = xCAT::Utils->parseMacTabEntry($machash->{$node}->[0]->{'mac'}, $node);
|
|
}
|
|
my $net_params = xCAT::NetworkUtils->gen_net_boot_params($installnic, $primarynic, $mac, $nodebootif);
|
|
if (defined($net_params->{ifname})) {
|
|
$kcmdline .= "$net_params->{ifname} ";
|
|
}
|
|
if (defined($net_params->{netdev})) {
|
|
$kcmdline .= "$net_params->{netdev} ";
|
|
} elsif (defined($net_params->{BOOTIF}) && ($net_params->{setmac} || $arch =~ /ppc/)) {
|
|
$kcmdline .= "$net_params->{BOOTIF} ";
|
|
}
|
|
|
|
my %client_nethash = xCAT::DBobjUtils->getNetwkInfo([$node]);
|
|
if ($client_nethash{$node}{mgtifname} =~ /hf/)
|
|
{
|
|
$kcmdline .= "rdloaddriver=hf_if ";
|
|
}
|
|
|
|
|
|
if (defined $sent->{serialport}) {
|
|
|
|
#my $sent = $hmtab->getNodeAttribs($node,['serialspeed','serialflow']);
|
|
unless ($sent->{serialspeed}) {
|
|
xCAT::MsgUtils->report_node_error($callback, $node,"serialport defined, but no serialspeed for $node in nodehm table");
|
|
next;
|
|
}
|
|
if ($arch =~ /ppc64/i) {
|
|
$kcmdline .=
|
|
"console=tty0 console=hvc" . $sent->{serialport} . "," . $sent->{serialspeed};
|
|
} else {
|
|
$kcmdline .=
|
|
"console=tty0 console=ttyS" . $sent->{serialport} . "," . $sent->{serialspeed};
|
|
}
|
|
if ($sent->{serialflow} =~ /(hard|tcs|ctsrts)/)
|
|
{
|
|
$kcmdline .= "n8r";
|
|
}
|
|
} else {
|
|
$callback->(
|
|
{
|
|
warning => ["rcons may not work since no serialport is specified for $node"],
|
|
}
|
|
);
|
|
}
|
|
|
|
# add the addkcmdline attribute to the end
|
|
# of the command, if it exists
|
|
#my $addkcmd = $addkcmdhash->{$node}->[0];
|
|
# add the extra addkcmd command info, if in the table
|
|
#if ($addkcmd->{'addkcmdline'}) {
|
|
# $kcmdline .= " ";
|
|
# $kcmdline .= $addkcmd->{'addkcmdline'};
|
|
|
|
#}
|
|
|
|
my $kernstr = "$rtftppath/kernel";
|
|
if ($xenstyle) {
|
|
$kernstr .= "!$rtftppath/hypervisor";
|
|
}
|
|
my $initrdstr = "$rtftppath/initrd-stateless.gz";
|
|
$initrdstr = "$rtftppath/initrd-statelite.gz" if ($statelite);
|
|
|
|
# special case for the dracut-enabled OSes
|
|
if (&using_dracut($osver)) {
|
|
if ($statelite and $rootfstype eq "ramdisk") {
|
|
$initrdstr = "$rtftppath/initrd-stateless.gz";
|
|
}
|
|
}
|
|
|
|
if ($statelite)
|
|
{
|
|
my $statelitetb = xCAT::Table->new('statelite');
|
|
my $mntopts = $statelitetb->getNodeAttribs($node, ['mntopts']);
|
|
|
|
my $mntoptions = $mntopts->{'mntopts'};
|
|
unless (defined($mntoptions))
|
|
{
|
|
$kcmdline .= " MNTOPTS=";
|
|
}
|
|
else
|
|
{
|
|
$kcmdline .= " MNTOPTS=$mntoptions";
|
|
}
|
|
}
|
|
$bootparams->{$node}->[0]->{kernel} = $kernstr;
|
|
$bootparams->{$node}->[0]->{initrd} = $initrdstr;
|
|
$bootparams->{$node}->[0]->{kcmdline} = $kcmdline;
|
|
}
|
|
|
|
#my $rc = xCAT::TableUtils->create_postscripts_tar();
|
|
#if ( $rc != 0 ) {
|
|
# xCAT::MsgUtils->message( "S", "Error creating postscripts tar file." );
|
|
#}
|
|
}
|
|
|
|
sub getplatform {
|
|
my $os = shift;
|
|
my $platform;
|
|
if ($os =~ /debian.*/) {
|
|
$platform = "debian";
|
|
}
|
|
elsif ($os =~ /ubuntu.*/) {
|
|
$platform = "ubuntu";
|
|
}
|
|
return $platform;
|
|
}
|
|
|
|
# sub subVars
|
|
# copied from litetreee.pm
|
|
# TODO: need to move the function to xCAT::Utils?
|
|
|
|
# some directories will have xCAT database values, like:
|
|
# $nodetype.os. If that is the case we need to open up
|
|
# the database and look at them. We need to make sure
|
|
# we do this sparingly... We don't like tons of hits
|
|
# to the database.
|
|
|
|
sub subVars()
|
|
{
|
|
my $dir = shift;
|
|
my $node = shift;
|
|
my $type = shift;
|
|
my $callback = shift;
|
|
|
|
# parse all the dollar signs...
|
|
# if its a directory then it has a / in it, so you have to parse it.
|
|
# if its a server, it won't have one so don't worry about it.
|
|
my @arr = split("/", $dir);
|
|
my $fdir = "";
|
|
foreach my $p (@arr) {
|
|
|
|
# have to make this geric so $ can be in the midle of the name: asdf$foobar.sitadsf
|
|
if ($p =~ /\$/) {
|
|
my $pre;
|
|
my $suf;
|
|
my @fParts;
|
|
if ($p =~ /([^\$]*)([^# ]*)(.*)/) {
|
|
$pre = $1;
|
|
$p = $2;
|
|
$suf = $3;
|
|
}
|
|
|
|
# have to sub here:
|
|
# get rid of the $ sign.
|
|
foreach my $part (split('\$', $p)) {
|
|
if ($part eq '') { next; }
|
|
|
|
#$callback->({error=>["part is $part"],errorcode=>[1]});
|
|
# check if p is just the node name:
|
|
if ($part eq 'node') {
|
|
|
|
# it is so, just return the node.
|
|
#$fdir .= "/$pre$node$suf";
|
|
push @fParts, $node;
|
|
} else {
|
|
|
|
# ask the xCAT DB what the attribute is.
|
|
my ($table, $col) = split('\.', $part);
|
|
unless ($col) { $col = 'UNDEFINED' }
|
|
my $tab = xCAT::Table->new($table);
|
|
unless ($tab) {
|
|
$callback->({ error => ["$table does not exist"], errorcode => [1] });
|
|
return;
|
|
}
|
|
my $ent;
|
|
my $val;
|
|
if ($table eq 'site') {
|
|
$val = $tab->getAttribs({ key => "$col" }, 'value');
|
|
$val = $val->{'value'};
|
|
} else {
|
|
$ent = $tab->getNodeAttribs($node, [$col]);
|
|
$val = $ent->{$col};
|
|
}
|
|
unless ($val) {
|
|
|
|
# couldn't find the value!!
|
|
$val = "UNDEFINED"
|
|
}
|
|
push @fParts, $val;
|
|
}
|
|
}
|
|
my $val = join('.', @fParts);
|
|
if ($type eq 'dir') {
|
|
$fdir .= "/$pre$val$suf";
|
|
} else {
|
|
$fdir .= $pre . $val . $suf;
|
|
}
|
|
} else {
|
|
|
|
# no substitution here
|
|
$fdir .= "/$p";
|
|
}
|
|
}
|
|
|
|
# now that we've processed variables, process commands
|
|
# this isn't quite rock solid. You can't name directories with #'s in them.
|
|
if ($fdir =~ /#CMD=/) {
|
|
my $dir;
|
|
foreach my $p (split(/#/, $fdir)) {
|
|
if ($p =~ /CMD=/) {
|
|
$p =~ s/CMD=//;
|
|
my $cmd = $p;
|
|
|
|
#$callback->({info=>[$p]});
|
|
$p = `$p 2>&1`;
|
|
chomp($p);
|
|
|
|
#$callback->({info=>[$p]});
|
|
unless ($p) {
|
|
$p = "#CMD=$p did not return output#";
|
|
}
|
|
}
|
|
$dir .= $p;
|
|
}
|
|
$fdir = $dir;
|
|
}
|
|
|
|
return $fdir;
|
|
}
|
|
|
|
sub setupNFSTree {
|
|
my $node = shift;
|
|
my $sip = shift;
|
|
my $callback = shift;
|
|
|
|
my $cmd = "litetree $node";
|
|
my @uris = xCAT::Utils->runcmd($cmd, 0);
|
|
|
|
foreach my $uri (@uris) {
|
|
|
|
# parse the result
|
|
# the result looks like "nodename: nfsserver:directory";
|
|
$uri =~ m/\Q$node\E:\s+(.+):(.+)$/;
|
|
my $nfsserver = $1;
|
|
my $nfsdirectory = $2;
|
|
|
|
if ($nfsserver eq $sip) { # on the service node
|
|
unless (-d $nfsdirectory) {
|
|
if (-e $nfsdirectory) {
|
|
unlink $nfsdirectory;
|
|
}
|
|
mkpath $nfsdirectory;
|
|
}
|
|
|
|
|
|
$cmd = "showmount -e $nfsserver";
|
|
my @entries = xCAT::Utils->runcmd($cmd, 0);
|
|
shift @entries;
|
|
if (grep /\Q$nfsdirectory\E/, @entries) {
|
|
$callback->({ data => ["$nfsdirectory has been exported already!"] });
|
|
} else {
|
|
$cmd = "/usr/sbin/exportfs :$nfsdirectory -o rw,no_root_squash,sync,no_subtree_check,insecure";
|
|
xCAT::Utils->runcmd($cmd, 0);
|
|
$callback->({ data => ["now $nfsdirectory is exported!"] });
|
|
}
|
|
|
|
if (xCAT::SvrUtils->ensure_nfs_export_option($nfsdirectory, 'insecure')) {
|
|
xCAT::Utils->runcmd("/usr/sbin/exportfs -r", 0);
|
|
$callback->({ data => ["added insecure to existing $nfsdirectory export"] });
|
|
} elsif (!xCAT::SvrUtils->nfs_export_exists($nfsdirectory)) {
|
|
$cmd = qq{echo "$nfsdirectory *(rw,no_root_squash,sync,no_subtree_check,insecure)" >> /etc/exports};
|
|
xCAT::Utils->runcmd($cmd, 0);
|
|
$callback->({ data => ["$nfsdirectory is added to /etc/exports with default option"] });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sub setupStatemnt {
|
|
my $sip = shift;
|
|
my $statemnt = shift;
|
|
my $callback = shift;
|
|
|
|
$statemnt =~ m/^(.+):(.+)$/;
|
|
my $nfsserver = $1;
|
|
my $nfsdirectory = $2;
|
|
|
|
if ($sip eq inet_ntoa(inet_aton($nfsserver))) {
|
|
unless (-d $nfsdirectory) {
|
|
if (-e $nfsdirectory) {
|
|
unlink $nfsdirectory;
|
|
}
|
|
mkpath $nfsdirectory;
|
|
}
|
|
|
|
my $cmd = "showmount -e $nfsserver";
|
|
my @entries = xCAT::Utils->runcmd($cmd, 0);
|
|
shift @entries;
|
|
if (grep /\Q$nfsdirectory\E/, @entries) {
|
|
$callback->({ data => ["$nfsdirectory has been exported already!"] });
|
|
} else {
|
|
$cmd = "/usr/sbin/exportfs :$nfsdirectory -o rw,no_root_squash,sync,no_subtree_check,insecure";
|
|
xCAT::Utils->runcmd($cmd, 0);
|
|
$callback->({ data => ["now $nfsdirectory is exported!"] });
|
|
}
|
|
|
|
if (xCAT::SvrUtils->ensure_nfs_export_option($nfsdirectory, 'insecure')) {
|
|
xCAT::Utils->runcmd("/usr/sbin/exportfs -r", 0);
|
|
$callback->({ data => ["added insecure to existing $nfsdirectory export"] });
|
|
} elsif (!xCAT::SvrUtils->nfs_export_exists($nfsdirectory)) {
|
|
xCAT::Utils->runcmd(qq{echo "$nfsdirectory *(rw,no_root_squash,sync,no_subtree_check,insecure)" >> /etc/exports}, 0);
|
|
$callback->({ data => ["$nfsdirectory is added into /etc/exports with default options"] });
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
1;
|