2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-10-21 23:28:19 +00:00

feat(nodeset): tell nodes the root image checksum setting

Nodes could not know whether an image turned the root image checksum on.
An initial ramdisk built before the check booted the image unchecked,
and nothing reported it. An archive packed before the check had no
checksum for a new initial ramdisk to find.

When the image or the site turns the checksum on, nodeset adds
rootimgchecksum=yes to the kernel command line of the node. This applies
to stateless and statelite ramdisk nodes. genimage writes
<initrd>.rootimgcheck next to an initial ramdisk that can check. The
marker holds the SHA-256 checksum of the initial ramdisk, so a replaced
ramdisk does not match it. imgexport and imgimport carry the marker with
the ramdisk.

When the checksum is on, nodeset warns once for each image whose initial
ramdisk has no matching marker. Its nodes may boot the image unchecked.
nodeset judges the initial ramdisk in TFTP after it copies the boot
files, because nodes boot that copy, also with --noupdateinitrd. A copy
that fails is now an error for the node, instead of a boot entry for the
previous boot files.

genimage and nodeset write a checksum for each archive of the image
directory that has none, also when the check is off. Nodes with older
boot entries still check. genimage does it before it copies a new
initial ramdisk to TFTP, and nodeset before it copies the boot files of
a node. Both hold the publication lock while they write. When they
cannot write the checksum, they keep the boot files that nodes use now
and report how to recover. With the check off, nodeset reports it as a
warning instead, and still sets up the node. nodeset marks the boot
files of an image as copied only after this step succeeds. When the
first node of an image fails it, the next node copies them.

The change covers the Red Hat, SUSE and Ubuntu nodeset plugins, which
share one helper in xCAT::SvrUtils.
This commit is contained in:
Vinícius Ferrão
2026-10-13 15:29:22 -03:00
parent fd0468a72f
commit f8469e2074
9 changed files with 292 additions and 49 deletions
+128
View File
@@ -2640,6 +2640,106 @@ sub rootimg_checksum_enabled {
return !(defined($value) && $value =~ /^\s*(?:no|0)\s*$/ix);
}
#--------------------------------------------------------------------------------------------------------
#write_initrd_check_marker:
#description: record that an initial ramdisk checks the compressed rootimage against its checksum.
# The marker holds the SHA-256 of the initial ramdisk, so a replaced ramdisk does not match it.
#argument:
# $initrd: the path of the initial ramdisk
#return:
# on success: nothing
# on fail: the error message
#--------------------------------------------------------------------------------------------------------
sub write_initrd_check_marker {
my (undef, $initrd) = @_;
my $digest = eval { Digest::SHA->new(256)->addfile($initrd)->hexdigest }
or return "Cannot compute the SHA-256 checksum of $initrd: $@";
return _write_published_file("$initrd.rootimgcheck", "sha256=$digest\n");
}
#--------------------------------------------------------------------------------------------------------
#initrd_checks_rootimg:
#description: tell whether genimage built an initial ramdisk that checks the compressed rootimage
#arguments:
# $initrd: the path of the initial ramdisk, or of a copy of it
# $marker: the marker that genimage wrote, by default <initrd>.rootimgcheck
#return:
# true when the marker holds the SHA-256 of the initial ramdisk
#--------------------------------------------------------------------------------------------------------
sub initrd_checks_rootimg {
my (undef, $initrd, $marker_path) = @_;
open(my $marker, '<', $marker_path // "$initrd.rootimgcheck") or return 0;
my $line = <$marker> // '';
close($marker);
my ($recorded) = $line =~ /^sha256=([0-9a-f]{64})$/x or return 0;
my $digest = eval { Digest::SHA->new(256)->addfile($initrd)->hexdigest } or return 0;
return $digest eq $recorded;
}
#--------------------------------------------------------------------------------------------------------
#prepare_rootimg_checksum:
#description: write the missing checksums of an image before nodeset copies its boot files. With the check
# turned off, a checksum that cannot be written is a warning instead of an error.
#arguments (by name):
# value: the rootimgchecksum attribute of the image, or undef
# rootimgdir: the image directory
# imagename: the image name, for the messages
# warned: a hash of the image directories that already got a warning
# callback: the request callback
#return:
# nothing, or the error that keeps nodeset from setting up the node
#--------------------------------------------------------------------------------------------------------
sub prepare_rootimg_checksum {
my ($class, %args) = @_;
my $rootimgdir = $args{rootimgdir};
my $imagename = $args{imagename} || $rootimgdir;
# A node whose boot entry nodeset wrote while the check was on still checks, so every archive gets a checksum.
my $error = $class->publish_missing_rootimg_checksums($rootimgdir) or return;
$error =~ s/\.$//x;
if (!$class->rootimg_checksum_enabled($args{value})) {
$args{callback}->({ warning => [ "$error. Nodes of $imagename whose boot entries still ask for the check "
. "download the root image again until nodeset runs for them." ] }) unless $args{warned}{$rootimgdir}++;
return;
}
return "The root image of $imagename has no checksum that its initial ramdisk can check: $error. "
. "Run packimage for the image, then nodeset.";
}
#--------------------------------------------------------------------------------------------------------
#rootimg_checksum_boot:
#description: decide how a node checks the compressed rootimage that it downloads at boot, and warn once
# for each image whose initial ramdisk has no matching marker
#arguments (by name):
# value: the rootimgchecksum attribute of the image, or undef
# rootimgdir: the image directory
# initrd: the initial ramdisk that nodes boot, as nodeset left it in TFTP
# imagename: the image name, for the messages
# warned: a hash of the image directories that already got a warning
# callback: the request callback
#return:
# the kernel argument that asks the node to check, or an empty string when the check is off
#--------------------------------------------------------------------------------------------------------
my %initrd_checks;
sub rootimg_checksum_boot {
my ($class, %args) = @_;
return '' unless $class->rootimg_checksum_enabled($args{value});
my ($rootimgdir, $initrd) = @args{qw(rootimgdir initrd)};
my $imagename = $args{imagename} || $rootimgdir;
# The copy in TFTP checks only when it matches the marker that genimage wrote in the image directory.
my $marker = "$rootimgdir/" . basename($initrd) . '.rootimgcheck';
# nodeset runs this for every node, so each version of the initial ramdisk and its marker is hashed once.
my @version = map { (stat($_))[ 0, 1, 7, 9 ] } ($initrd, $marker);
my $key = join(':', $initrd, map { $_ // '' } @version);
$initrd_checks{$key} //= $class->initrd_checks_rootimg($initrd, $marker) ? 1 : 0;
$args{callback}->({ warning => [ "Cannot tell whether the initial ramdisk $initrd checks the root image checksum, so "
. "nodes may boot the root image of $imagename unchecked. Run genimage for the image and then nodeset, or "
. "set its rootimgchecksum attribute to no." ] }) unless $initrd_checks{$key} || $args{warned}{$rootimgdir}++;
return 'rootimgchecksum=yes ';
}
# Returns the content of a file, or undef when it cannot be read.
sub _read_published_file {
my ($path) = @_;
@@ -2770,6 +2870,34 @@ sub _restore_rootimg {
return;
}
#--------------------------------------------------------------------------------------------------------
#publish_missing_rootimg_checksums:
#description: write the checksum of each published compressed rootimage of an image directory that has
# none. An initial ramdisk that checks the rootimage refuses an image packed before the check.
#argument:
# $dir: the image directory
#return:
# on success: nothing
# on fail: the error message
#--------------------------------------------------------------------------------------------------------
sub publish_missing_rootimg_checksums {
my ($class, $dir) = @_;
my @formats = qw(rootimg.gz rootimg.cpio.gz rootimg.cpio.xz rootimg.tar.gz rootimg.tar.xz rootimg.sfs rootimg-statelite.gz);
my $missing = sub { grep { -f "$dir/$_" && !-e "$dir/$_.sha256" } @formats };
my @missing = $missing->();
return if !@missing;
# The lock keeps packimage from replacing an archive while this checksum is computed.
my ($lock, $error) = _acquire_publish_lock($dir);
return $error unless $lock;
foreach my $file ($missing->()) {
$error = $class->write_rootimg_checksum("$dir/$file");
last if $error;
}
_release_publish_lock($lock);
return $error;
}
# The image lock is per host, and mkdir is atomic for every host, also on NFS without locks.
sub _acquire_publish_lock {
my ($dir) = @_;
+41 -15
View File
@@ -394,6 +394,7 @@ sub mknetboot
}
my %donetftp = ();
my %rootimg_warned;
my $ostab = xCAT::Table->new('nodetype');
my %oents = %{ $ostab->getNodesAttribs(\@nodes, [qw(os arch profile provmethod)]) };
my $restab = xCAT::Table->new('noderes');
@@ -426,6 +427,7 @@ sub mknetboot
my $nfsrootopts;
my $tftpdir;
my $cfgpart;
my $rootimgchecksum;
my $imagename; # set it if running of 'nodeset osimage=xxx'
if ($reshash->{$node}->[0] and $reshash->{$node}->[0]->{tftpdir}) {
@@ -457,10 +459,11 @@ sub mknetboot
if (!$linuximagetab) {
$linuximagetab = xCAT::Table->new('linuximage', -create => 1);
}
(my $ref1) = $linuximagetab->getAttribs({ imagename => $imagename }, 'rootimgdir', 'nodebootif', 'dump', 'crashkernelsize', 'partitionfile');
(my $ref1) = $linuximagetab->getAttribs({ imagename => $imagename }, 'rootimgdir', 'nodebootif', 'dump', 'crashkernelsize', 'partitionfile', 'rootimgchecksum');
if (($ref1) && ($ref1->{'rootimgdir'})) {
$img_hash{$imagename}->{rootimgdir} = $ref1->{'rootimgdir'};
}
$img_hash{$imagename}->{rootimgchecksum} = $ref1->{'rootimgchecksum'} if $ref1;
if (($ref1) && ($ref1->{'nodebootif'})) {
$img_hash{$imagename}->{nodebootif} = $ref1->{'nodebootif'};
}
@@ -517,6 +520,7 @@ sub mknetboot
$crashkernelsize = $ph->{crashkernelsize};
$dump = $ph->{dump};
$cfgpart = $ph->{'cfgpart'};
$rootimgchecksum = $ph->{rootimgchecksum};
}
else {
my @ents = xCAT::TableUtils->get_site_attribute("disablenodesetwarning");
@@ -563,10 +567,11 @@ sub mknetboot
$linuximagetab = xCAT::Table->new('linuximage');
}
if ($linuximagetab) {
(my $ref1) = $linuximagetab->getAttribs({ imagename => $imgname }, 'dump', 'crashkernelsize', 'partitionfile');
(my $ref1) = $linuximagetab->getAttribs({ imagename => $imgname }, 'dump', 'crashkernelsize', 'partitionfile', 'rootimgchecksum');
if ($ref1 and $ref1->{'dump'}) {
$dump = $ref1->{'dump'};
}
$rootimgchecksum = $ref1->{'rootimgchecksum'} if $ref1;
if ($ref1 and $ref1->{'crashkernelsize'}) {
$crashkernelsize = $ref1->{'crashkernelsize'};
}
@@ -664,42 +669,53 @@ sub mknetboot
#mkpath "/install/postscripts/";
# 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/
my $copykey;
if ($imagename) {
$tftppath = "$tftpdir/xcat/osimage/$imagename";
$rtftppath = "xcat/osimage/$imagename";
unless ($donetftp{$imagename}) {
$docopy = 1;
$donetftp{$imagename} = 1;
}
$copykey = $imagename;
} 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;
$copykey = join($;, $osver, $arch, $profile);
}
my $docopy = !$donetftp{$copykey};
# This runs before the boot files are copied, so a failure keeps the boot files that the node uses now.
if (!$statelite || $rootfstype eq "ramdisk") {
my $error = xCAT::SvrUtils->prepare_rootimg_checksum(value => $rootimgchecksum, rootimgdir => $rootimgdir,
imagename => $imagename, warned => \%rootimg_warned, callback => $callback);
if ($error) {
xCAT::MsgUtils->report_node_error($callback, $node, $error);
next;
}
}
my $copied = 1;
if ($docopy && !$noupdateinitrd) {
mkpath("$tftppath");
if (-f "$rootimgdir/hypervisor") {
copy("$rootimgdir/hypervisor", "$tftppath");
$copied &&= copy("$rootimgdir/hypervisor", "$tftppath");
$xenstyle = 1;
}
copy("$rootimgdir/kernel", "$tftppath");
$copied &&= copy("$rootimgdir/kernel", "$tftppath");
if ($statelite) {
if ($rootfstype eq "ramdisk") {
copy("$rootimgdir/initrd-stateless.gz", "$tftppath");
$copied &&= copy("$rootimgdir/initrd-stateless.gz", "$tftppath");
} else {
copy("$rootimgdir/initrd-statelite.gz", "$tftppath");
$copied &&= copy("$rootimgdir/initrd-statelite.gz", "$tftppath");
}
} else {
copy("$rootimgdir/initrd-stateless.gz", "$tftppath");
$copied &&= copy("$rootimgdir/initrd-stateless.gz", "$tftppath");
}
}
unless ($copied) {
xCAT::MsgUtils->report_node_error($callback, $node, qq{Copying to $tftppath failed: $!});
next;
}
$donetftp{$copykey} = 1;
if ($statelite) {
my $initrdloc = "$tftppath";
@@ -719,6 +735,13 @@ sub mknetboot
}
}
# Nodes boot the initial ramdisk that is in TFTP now, so that copy decides the check.
my $checksum_arg = '';
if (!$statelite || $rootfstype eq "ramdisk") {
$checksum_arg = xCAT::SvrUtils->rootimg_checksum_boot(value => $rootimgchecksum, rootimgdir => $rootimgdir,
initrd => "$tftppath/initrd-stateless.gz", imagename => $imagename, warned => \%rootimg_warned, callback => $callback);
}
$ent = $reshash->{$node}->[0]; #$restab->getNodeAttribs($node, ['primarynic']);
my $sent = $hmhash->{$node}->[0];
@@ -815,6 +838,7 @@ sub mknetboot
} else {
$kcmdline = "imgurl=$httpmethod://$imgsrvip:$httpport/$rootimgdir/rootimg-statelite.gz STATEMNT=";
}
$kcmdline = $checksum_arg . $kcmdline;
}
# add support for subVars in the value of "statemnt"
@@ -872,6 +896,7 @@ sub mknetboot
} else {
$kcmdline = "imgurl=$httpmethod://$imgsrvip:$httpport/$rootimgdir/$compressedrootimg ";
}
$kcmdline .= $checksum_arg;
$kcmdline .= "XCAT=$xcatmasterip:$xcatdport ";
$kcmdline .= "NODE=$node ";
@@ -3673,4 +3698,5 @@ EOMS
return @inserted_dd;
}
1;
+41 -15
View File
@@ -1448,6 +1448,7 @@ sub mknetboot
}
my %donetftp = ();
my %rootimg_warned;
my %oents = %{ $ostab->getNodesAttribs(\@nodes, [qw(os arch profile provmethod)]) };
my $restab = xCAT::Table->new('noderes');
my $hmtab = xCAT::Table->new('nodehm');
@@ -1479,6 +1480,7 @@ sub mknetboot
my $rootfstype;
my $nfsrootopts;
my $tftpdir;
my $rootimgchecksum;
my $imagename; # set it if running of 'nodeset osimage=xxx'
if ($reshash->{$node}->[0] and $reshash->{$node}->[0]->{tftpdir}) {
@@ -1505,10 +1507,11 @@ sub mknetboot
if (!$linuximagetab) {
$linuximagetab = xCAT::Table->new('linuximage', -create => 1);
}
(my $ref1) = $linuximagetab->getAttribs({ imagename => $imagename }, 'rootimgdir');
(my $ref1) = $linuximagetab->getAttribs({ imagename => $imagename }, 'rootimgdir', 'rootimgchecksum');
if (($ref1) && ($ref1->{'rootimgdir'})) {
$img_hash{$imagename}->{rootimgdir} = $ref1->{'rootimgdir'};
}
$img_hash{$imagename}->{rootimgchecksum} = $ref1->{'rootimgchecksum'} if $ref1;
if (($ref1) && ($ref1->{'nodebootif'})) {
$img_hash{$imagename}->{nodebootif} = $ref1->{'nodebootif'};
}
@@ -1541,6 +1544,7 @@ sub mknetboot
$nodebootif = $ph->{nodebootif};
$crashkernelsize = $ph->{crashkernelsize};
$dump = $ph->{dump};
$rootimgchecksum = $ph->{rootimgchecksum};
}
else {
# This is deprecated mode to define node's provmethod, not supported now.
@@ -1582,10 +1586,11 @@ sub mknetboot
$linuximagetab = xCAT::Table->new('linuximage');
}
if ($linuximagetab) {
(my $ref1) = $linuximagetab->getAttribs({ imagename => $imgname }, 'dump', 'crashkernelsize');
(my $ref1) = $linuximagetab->getAttribs({ imagename => $imgname }, 'dump', 'crashkernelsize', 'rootimgchecksum');
if ($ref1 and $ref1->{'dump'}) {
$dump = $ref1->{'dump'};
}
$rootimgchecksum = $ref1->{'rootimgchecksum'} if $ref1;
if ($ref1 and $ref1->{'crashkernelsize'}) {
$crashkernelsize = $ref1->{'crashkernelsize'};
}
@@ -1664,42 +1669,53 @@ sub mknetboot
#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/
my $copykey;
if ($imagename) {
$tftppath = "$tftpdir/xcat/osimage/$imagename";
$rtftppath = "xcat/osimage/$imagename";
unless ($donetftp{$imagename}) {
$docopy = 1;
$donetftp{$imagename} = 1;
}
$copykey = $imagename;
} 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;
$copykey = join($;, $osver, $arch, $profile);
}
my $docopy = !$donetftp{$copykey};
# This runs before the boot files are copied, so a failure keeps the boot files that the node uses now.
if (!$statelite || $rootfstype eq "ramdisk") {
my $error = xCAT::SvrUtils->prepare_rootimg_checksum(value => $rootimgchecksum, rootimgdir => $rootimgdir,
imagename => $imagename, warned => \%rootimg_warned, callback => $callback);
if ($error) {
xCAT::MsgUtils->report_node_error($callback, $node, $error);
next;
}
}
my $copied = 1;
if ($docopy) {
mkpath("$tftppath");
if (-f "$rootimgdir/hypervisor") {
copy("$rootimgdir/hypervisor", "$tftppath");
$copied &&= copy("$rootimgdir/hypervisor", "$tftppath");
$xenstyle = 1;
}
copy("$rootimgdir/kernel", "$tftppath");
$copied &&= copy("$rootimgdir/kernel", "$tftppath");
if ($statelite) {
if ($rootfstype eq "ramdisk") {
copy("$rootimgdir/initrd-stateless.gz", "$tftppath");
$copied &&= copy("$rootimgdir/initrd-stateless.gz", "$tftppath");
} else {
copy("$rootimgdir/initrd-statelite.gz", "$tftppath");
$copied &&= copy("$rootimgdir/initrd-statelite.gz", "$tftppath");
}
} else {
copy("$rootimgdir/initrd-stateless.gz", "$tftppath");
$copied &&= copy("$rootimgdir/initrd-stateless.gz", "$tftppath");
}
}
unless ($copied) {
xCAT::MsgUtils->report_node_error($callback, $node, qq{Copying to $tftppath failed: $!});
next;
}
$donetftp{$copykey} = 1;
if ($statelite) {
my $initrdloc = "$tftppath";
@@ -1719,6 +1735,13 @@ sub mknetboot
next;
}
}
# Nodes boot the initial ramdisk that is in TFTP now, so that copy decides the check.
my $checksum_arg = '';
if (!$statelite || $rootfstype eq "ramdisk") {
$checksum_arg = xCAT::SvrUtils->rootimg_checksum_boot(value => $rootimgchecksum, rootimgdir => $rootimgdir,
initrd => "$tftppath/initrd-stateless.gz", imagename => $imagename, warned => \%rootimg_warned, callback => $callback);
}
my $ent = $reshash->{$node}->[0]; #$restab->getNodeAttribs($node, ['primarynic']);
my $sent = $hmhash->{$node}->[0];
@@ -1785,6 +1808,7 @@ sub mknetboot
}
} else {
$kcmdline = "imgurl=http://$imgsrv:$httpport/$rootimgdir/rootimg-statelite.gz STATEMNT=";
$kcmdline = $checksum_arg . $kcmdline;
}
@@ -1841,6 +1865,7 @@ sub mknetboot
{
$kcmdline =
"imgurl=http://$imgsrv:$httpport/$rootimgdir/$compressedrootimg ";
$kcmdline .= $checksum_arg;
$kcmdline .= "XCAT=$xcatmaster:$xcatdport ";
}
@@ -2110,4 +2135,5 @@ sub subVars()
return $fdir;
}
1;
+6
View File
@@ -451,6 +451,12 @@ sub process_request {
$installdir = $siteents[0];
}
# The new initial ramdisk also checks for nodes with older boot entries, so an archive packed before the check gets a checksum.
if (my $error = xCAT::SvrUtils->publish_missing_rootimg_checksums($rootimg_dir)) {
$error =~ s/\.$//x;
$callback->({ error => ["$error. The new initial ramdisk is not copied to $tftppath. Run packimage $imagename, then nodeset."], errorcode => [1] });
return 1;
}
unless (-d $tftppath) {
mkpath $tftppath;
}
+6 -3
View File
@@ -637,6 +637,9 @@ sub get_files {
$errors++;
} else {
$attrs->{ramdisk} = $ramdisk;
# nodeset reads this marker to tell whether the ramdisk checks the root image checksum.
$attrs->{ramdiskcheck} = "$ramdisk.rootimgcheck" if -r "$ramdisk.rootimgcheck";
}
unless ($kernel) {
@@ -917,7 +920,7 @@ sub make_bundle {
# these are the only files we copy in. (unless you have extras)
for my $a ("kernel", "ramdisk", "rootimg") {
for my $a ("kernel", "ramdisk", "ramdiskcheck", "rootimg") {
my $filenames = $attribs->{$a};
if ($filenames) {
my @file_array = split(',', $filenames);
@@ -1333,7 +1336,7 @@ sub change_profile {
$data->{linuximage}->{rootimgdir} = "$installdir/netboot/" . $data->{osimage}->{osvers} . "/" . $data->{osimage}->{osarch} . "/$new_profile";
}
for my $a ("kernel", "ramdisk", "rootimg", "rootimgtree", "litefile") {
for my $a ("kernel", "ramdisk", "ramdiskcheck", "rootimg", "rootimgtree", "litefile") {
if ($data->{$a}) {
my $fn = basename($data->{$a});
$data->{$a} = $data->{linuximage}->{rootimgdir} . "/$fn";
@@ -1946,7 +1949,7 @@ sub make_files {
# };
# The root image goes first, so a failed publication leaves the kernel and ramdisk that match the published image.
for my $a ("rootimg", "kernel", "ramdisk", "rootimgtree", "litefile") {
for my $a ("rootimg", "kernel", "ramdisk", "ramdiskcheck", "rootimgtree", "litefile") {
my $filenames = $data->{$a};
if ($filenames) {
my @file_array = split(',', $filenames);
+39 -13
View File
@@ -150,6 +150,7 @@ sub mknetboot
my $reshash = $restab->getNodesAttribs(\@nodes, [ 'primarynic', 'tftpserver', 'tftpdir', 'xcatmaster', 'nfsserver', 'nfsdir', 'installnic' ]);
my %donetftp = ();
my %rootimg_warned;
foreach my $node (@nodes)
{
@@ -164,6 +165,7 @@ sub mknetboot
my $rootfstype;
my $nfsrootopts;
my $cfgpart;
my $rootimgchecksum;
my $imagename; # set it if running of 'nodeset osimage=xxx'
my $ent = $ntents->{$node}->[0];
@@ -184,10 +186,11 @@ sub mknetboot
if (!$linuximagetab) {
$linuximagetab = xCAT::Table->new('linuximage', -create => 1);
}
(my $ref1) = $linuximagetab->getAttribs({ imagename => $imagename }, 'rootimgdir', 'nodebootif', 'dump', 'crashkernelsize', 'partitionfile');
(my $ref1) = $linuximagetab->getAttribs({ imagename => $imagename }, 'rootimgdir', 'nodebootif', 'dump', 'crashkernelsize', 'partitionfile', 'rootimgchecksum');
if (($ref1) && ($ref1->{'rootimgdir'})) {
$img_hash{$imagename}->{rootimgdir} = $ref1->{'rootimgdir'};
}
$img_hash{$imagename}->{rootimgchecksum} = $ref1->{'rootimgchecksum'} if $ref1;
if (($ref1) && ($ref1->{'nodebootif'})) {
$img_hash{$imagename}->{nodebootif} = $ref1->{'nodebootif'};
}
@@ -237,6 +240,7 @@ sub mknetboot
$dump = $ph->{dump};
$crashkernelsize = $ph->{crashkernelsize};
$cfgpart = $ph->{'cfgpart'};
$rootimgchecksum = $ph->{rootimgchecksum};
$rootimgdir = $ph->{rootimgdir};
unless ($rootimgdir) {
@@ -287,10 +291,11 @@ sub mknetboot
$linuximagetab = xCAT::Table->new('linuximage');
}
if ($linuximagetab) {
(my $ref1) = $linuximagetab->getAttribs({ imagename => $imgname }, 'dump', 'crashkernelsize', 'partitionfile');
(my $ref1) = $linuximagetab->getAttribs({ imagename => $imgname }, 'dump', 'crashkernelsize', 'partitionfile', 'rootimgchecksum');
if ($ref1 && $ref1->{'dump'}) {
$dump = $ref1->{'dump'};
}
$rootimgchecksum = $ref1->{'rootimgchecksum'} if $ref1;
if ($ref1 and $ref1->{'crashkernelsize'}) {
$crashkernelsize = $ref1->{'crashkernelsize'};
}
@@ -408,34 +413,45 @@ sub mknetboot
# 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/
my $copykey;
if ($imagename) {
$tftppath = "$tftpdir/xcat/osimage/$imagename";
$rtftppath = "xcat/osimage/$imagename";
unless ($donetftp{$imagename}) {
$docopy = 1;
$donetftp{$imagename} = 1;
}
$copykey = $imagename;
} else {
$tftppath = "/$tftpdir/xcat/netboot/$osver/$arch/$profile/";
$rtftppath = "xcat/netboot/$osver/$arch/$profile/";
unless ($donetftp{ $osver, $arch, $profile, $tftpdir }) {
$docopy = 1;
$donetftp{ $osver, $arch, $profile, $tftpdir } = 1;
$copykey = join($;, $osver, $arch, $profile, $tftpdir);
}
my $docopy = !$donetftp{$copykey};
# This runs before the boot files are copied, so a failure keeps the boot files that the node uses now.
if (!$statelite || $rootfstype eq "ramdisk") {
my $error = xCAT::SvrUtils->prepare_rootimg_checksum(value => $rootimgchecksum, rootimgdir => $rootimgdir,
imagename => $imagename, warned => \%rootimg_warned, callback => $callback);
if ($error) {
xCAT::MsgUtils->report_node_error($callback, $node, $error);
next;
}
}
my $copied = 1;
if ($docopy && !$noupdateinitrd) {
mkpath("$tftppath");
copy("$rootimgdir/kernel", "$tftppath");
$copied &&= copy("$rootimgdir/kernel", "$tftppath");
if ($statelite) {
copy("$rootimgdir/initrd-statelite.gz", "$tftppath");
$copied &&= copy("$rootimgdir/initrd-statelite.gz", "$tftppath");
} else {
copy("$rootimgdir/initrd-stateless.gz", "$tftppath");
$copied &&= copy("$rootimgdir/initrd-stateless.gz", "$tftppath");
}
}
unless ($copied) {
xCAT::MsgUtils->report_node_error($callback, $node, qq{Copying to $tftppath failed: $!});
next;
}
$donetftp{$copykey} = 1;
if ($statelite) {
unless (-r "$tftppath/kernel" and -r "$tftppath/initrd-statelite.gz") {
@@ -450,6 +466,13 @@ sub mknetboot
}
}
# Nodes boot the initial ramdisk that is in TFTP now, so that copy decides the check.
my $checksum_arg = '';
if (!$statelite || $rootfstype eq "ramdisk") {
$checksum_arg = xCAT::SvrUtils->rootimg_checksum_boot(value => $rootimgchecksum, rootimgdir => $rootimgdir,
initrd => "$tftppath/initrd-stateless.gz", imagename => $imagename, warned => \%rootimg_warned, callback => $callback);
}
# TODO: move the table operations out of the foreach loop
my $hmtab = xCAT::Table->new('nodehm');
my $sent =
@@ -509,6 +532,7 @@ sub mknetboot
}
} else {
$kcmdline = "imgurl=$httpmethod://$imgsrv:$httpport/$rootimgdir/rootimg-statelite.gz STATEMNT=";
$kcmdline = $checksum_arg . $kcmdline;
}
# add support for subVars in the value of "statemnt"
@@ -562,6 +586,7 @@ sub mknetboot
{
$kcmdline =
"imgurl=$httpmethod://$imgsrv:$httpport/$rootimgdir/$compressedrootimg ";
$kcmdline .= $checksum_arg;
}
$kcmdline .= "XCAT=$xcatmaster:$xcatdport ";
$kcmdline .= " XCATHTTPPORT=$httpport ";
@@ -2788,4 +2813,5 @@ sub insert_dd () {
# return "";
#}
1;
+11 -1
View File
@@ -1304,11 +1304,21 @@ sub mkinitrd_dracut {
"$rootimg_dir/tmp/initrd.$$.gz", $mode);
} or xdie($@);
print "the initial ramdisk for $mode is generated successfully.\n";
mark_rootimg_check($mode);
return;
}
print "the initial ramdisk for $mode is generated successfully.\n";
print("Move $rootimg_dir/tmp/initrd.$$.gz -> $destdir/initrd-$mode.gz");
move("$rootimg_dir/tmp/initrd.$$.gz", "$destdir/initrd-$mode.gz");
move("$rootimg_dir/tmp/initrd.$$.gz", "$destdir/initrd-$mode.gz") and mark_rootimg_check($mode);
}
# nodeset warns unless this marker says that the stateless initial ramdisk checks the root image checksum.
sub mark_rootimg_check {
my ($mode) = @_;
return unless $mode eq 'stateless' && $dracutdir =~ /^dracut_(?:033|047|105)$/x;
require xCAT::SvrUtils;
my $error = xCAT::SvrUtils->write_initrd_check_marker("$destdir/initrd-$mode.gz");
print "Warning: $error\n" if $error;
}
sub mkinitrd {
+10 -1
View File
@@ -1178,10 +1178,19 @@ sub mkinitrd_dracut {
!system("chroot $rootimg_dir dracut $additional_options -f /tmp/initrd.$$.gz $kernelver")
or die("Error: failed to generate the initial ramdisk for $mode.\n");
print "the initial ramdisk for $mode is generated successfully.\n";
move("$rootimg_dir/tmp/initrd.$$.gz", "$destdir/initrd-$mode.gz");
move("$rootimg_dir/tmp/initrd.$$.gz", "$destdir/initrd-$mode.gz") and mark_rootimg_check($mode);
}
# nodeset warns unless this marker says that the stateless initial ramdisk checks the root image checksum.
sub mark_rootimg_check {
my ($mode) = @_;
return unless $mode eq 'stateless' && $dracutdir eq 'dracut_033';
require xCAT::SvrUtils;
my $error = xCAT::SvrUtils->write_initrd_check_marker("$destdir/initrd-$mode.gz");
print "Warning: $error\n" if $error;
}
sub mkinitrd {
my ($mode) = @_; # statelite or stateless
+10 -1
View File
@@ -1060,7 +1060,16 @@ sub mkinitrd_dracut {
!system("chroot $rootimg_dir dracut -f /tmp/initrd.$$.gz $kernelver")
or die("Error: failed to generate the initial ramdisk for $mode.\n");
print "the initial ramdisk for $mode is generated successfully.\n";
move("$rootimg_dir/tmp/initrd.$$.gz", "$destdir/initrd-$mode.gz");
move("$rootimg_dir/tmp/initrd.$$.gz", "$destdir/initrd-$mode.gz") and mark_rootimg_check($mode);
}
# nodeset warns unless this marker says that the stateless initial ramdisk checks the root image checksum.
sub mark_rootimg_check {
my ($mode) = @_;
return unless $mode eq 'stateless' && $dracutdir eq 'dracut';
require xCAT::SvrUtils;
my $error = xCAT::SvrUtils->write_initrd_check_marker("$destdir/initrd-$mode.gz");
print "Warning: $error\n" if $error;
}
sub mkinitrd {