diff --git a/xCAT-server/lib/xcat/plugins/anaconda.pm b/xCAT-server/lib/xcat/plugins/anaconda.pm index 06a32cb6b..9107f26ed 100644 --- a/xCAT-server/lib/xcat/plugins/anaconda.pm +++ b/xCAT-server/lib/xcat/plugins/anaconda.pm @@ -84,6 +84,42 @@ sub _centos_linux_distname } +sub _driver_disk_marker_path +{ + my $initrd = shift; + + return unless defined($initrd) && length($initrd); + return "$initrd.xcat-driver-disk"; +} + + +sub _set_driver_disk_marker +{ + my ($initrd, $present) = @_; + my $marker = _driver_disk_marker_path($initrd); + + return $present ? 0 : 1 unless defined($marker); + unless ($present) { + return 1 unless -e $marker; + return unlink($marker) ? 1 : 0; + } + + open(my $marker_fh, '>', $marker) or return 0; + return close($marker_fh) ? 1 : 0; +} + + +sub _driver_disk_kernel_arg +{ + my ($kversion, $initrd) = @_; + my $marker = _driver_disk_marker_path($initrd); + + return '' unless defined($marker) && -f $initrd && -f $marker; + return '' if xCAT::Utils->version_cmp($kversion, "7.0") < 0; + return " inst.dd=/dd.img"; +} + + sub handled_commands { return { @@ -1375,7 +1411,7 @@ sub mkinstall unless ($noupdateinitrd) { copy($kernpath, "$tftppath"); copy($initrdpath, "$tftppath/initrd.img"); - &insert_dd($callback, $os, $arch, "$tftppath/initrd.img", "$tftppath/vmlinuz", $driverupdatesrc, $netdrivers, $osupdir, $ignorekernelchk); + insert_dd($callback, $os, $arch, "$tftppath/initrd.img", "$tftppath/vmlinuz", $driverupdatesrc, $netdrivers, $osupdir, $ignorekernelchk); } } xCAT::MsgUtils->trace($verbose_on_off, "d", "anaconda->mkinstall: copy initrd.img and vmlinuz to $tftppath"); @@ -1536,7 +1572,9 @@ sub mkinstall } - #TODO: dd= for driver disks + $kcmdline .= _driver_disk_kernel_arg( + $kversion, "$tftppath/initrd.img" + ); 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"); @@ -2579,6 +2617,13 @@ sub insert_dd { my $osupdirlist = shift; my $ignorekernelchk = shift; + unless (_set_driver_disk_marker($img, 0)) { + my $rsp; + push @{ $rsp->{data} }, "Handle the driver update disk failed. Could not clear the initrd driver disk marker."; + xCAT::MsgUtils->message("E", $rsp, $callback); + return (); + } + my $install_dir = xCAT::TableUtils->getInstallDir(); my $cmd; @@ -3363,12 +3408,19 @@ EOMS if ( (&using_dracut($os)) && @dd_list) { #new style, skip the fanagling, copy over the dds and append them... mkpath("$dd_dir/dd"); if (scalar(@dd_list) == 1) { #only one, just append it.. - copy($dd_list[0], "$dd_dir/dd/dd.img"); + unless (copy($dd_list[0], "$dd_dir/dd/dd.img") && -s "$dd_dir/dd/dd.img") { + my $rsp; + push @{ $rsp->{data} }, "Handle the driver update disk failed. Could not copy the driver disk."; + xCAT::MsgUtils->message("E", $rsp, $callback); + rmtree $dd_dir; + return (); + } } elsif (scalar(@dd_list) > 1) { unless (-x "/usr/bin/createrepo" and -x "/usr/bin/mkisofs") { my $rsp; push @{ $rsp->{data} }, "Merging multiple driver disks requires createrepo and mkisofs utilities"; xCAT::MsgUtils->message("E", $rsp, $callback); + rmtree $dd_dir; return (); } mkpath("$dd_dir/newddimg"); @@ -3382,28 +3434,78 @@ EOMS $repodir =~ s/\/repodata\z//; xCAT::Utils->runcmd("createrepo $repodir", -1); } - chdir("$dd_dir/newddimg"); + unless (chdir("$dd_dir/newddimg")) { + my $rsp; + push @{ $rsp->{data} }, "Handle the driver update disk failed. Could not enter the merged driver disk directory."; + xCAT::MsgUtils->message("E", $rsp, $callback); + rmtree $dd_dir; + return (); + } xCAT::Utils->runcmd("mkisofs -J -R -o $dd_dir/dd/dd.img .", -1); + if ($::RUNCMD_RC != 0 || !-s "$dd_dir/dd/dd.img") { + my $rsp; + push @{ $rsp->{data} }, "Handle the driver update disk failed. Could not merge the driver disks."; + xCAT::MsgUtils->message("E", $rsp, $callback); + chdir("/"); + rmtree $dd_dir; + return (); + } } else { #there should be no else... die "This should never occur"; } - chdir($dd_dir . "/dd"); + unless (chdir($dd_dir . "/dd")) { + my $rsp; + push @{ $rsp->{data} }, "Handle the driver update disk failed. Could not enter the driver disk directory."; + xCAT::MsgUtils->message("E", $rsp, $callback); + chdir("/"); + rmtree $dd_dir; + return (); + } $cmd = "find .|cpio -H newc -o|gzip -9 -c - > ../dd.gz"; xCAT::Utils->runcmd($cmd, -1); - unless (-f "../dd.gz") { - die "Error attempting to archive driver disk"; + if ($::RUNCMD_RC != 0 || !-s "../dd.gz") { + my $rsp; + push @{ $rsp->{data} }, "Handle the driver update disk failed. Could not archive the driver disk."; + xCAT::MsgUtils->message("E", $rsp, $callback); + chdir("/"); + rmtree $dd_dir; + return (); } my $ddhdl; my $inithdl; - open($inithdl, ">>", $img); - open($ddhdl, "<", "../dd.gz"); + unless (open($ddhdl, "<", "../dd.gz")) { + my $rsp; + push @{ $rsp->{data} }, "Handle the driver update disk failed. Could not append the driver disk to the initrd."; + xCAT::MsgUtils->message("E", $rsp, $callback); + chdir("/"); + rmtree $dd_dir; + return (); + } + unless (open($inithdl, ">>", $img)) { + close($ddhdl); + my $rsp; + push @{ $rsp->{data} }, "Handle the driver update disk failed. Could not append the driver disk to the initrd."; + xCAT::MsgUtils->message("E", $rsp, $callback); + chdir("/"); + rmtree $dd_dir; + return (); + } binmode($ddhdl); binmode($inithdl); { local $/ = \32768; while (my $block = <$ddhdl>) { print $inithdl $block; } } + close($ddhdl); + unless (close($inithdl) && _set_driver_disk_marker($img, 1)) { + my $rsp; + push @{ $rsp->{data} }, "Handle the driver update disk failed. Could not record the driver disk in the initrd."; + xCAT::MsgUtils->message("E", $rsp, $callback); + chdir("/"); + rmtree $dd_dir; + return (); + } chdir("/"); push @inserted_dd, @dd_list; } diff --git a/xCAT-test/unit/anaconda_driver_disk_arg.t b/xCAT-test/unit/anaconda_driver_disk_arg.t new file mode 100644 index 000000000..4fea393ab --- /dev/null +++ b/xCAT-test/unit/anaconda_driver_disk_arg.t @@ -0,0 +1,113 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use Cwd qw(getcwd); +use File::Slurper qw(write_text); +use File::Spec; +use File::Temp qw(tempdir); +use FindBin; +use lib "$FindBin::Bin/../lib"; +use lib "$FindBin::Bin/../../perl-xCAT"; +use lib "$FindBin::Bin/../../xCAT-server/lib/perl"; +use Test::More; + +use XCAT::Test::File qw(repo_path); + +my $plugin = repo_path('xCAT-server/lib/xcat/plugins/anaconda.pm'); +plan skip_all => 'anaconda.pm not found' unless -r $plugin; +$ENV{XCATROOT} ||= repo_path('xCAT-server'); +require $plugin; + +my $tempdir = tempdir(CLEANUP => 1); +my $initrd = File::Spec->catfile($tempdir, 'initrd.img'); +my $driver_disk = File::Spec->catfile($tempdir, 'vendor-dd.img'); +write_text($initrd, "base initrd\n"); +write_text($driver_disk, "driver disk\n"); +my $base_size = -s $initrd; + +sub insert_driver_disk { + my ($source, $fail_archive) = @_; + my $cwd = getcwd(); + my @inserted; + my @messages; + { + no warnings qw(redefine once); + my $real_runcmd = \&xCAT::Utils::runcmd; + local *xCAT::TableUtils::getInstallDir = sub { return $tempdir; }; + local *xCAT::MsgUtils::message = sub { + my $rsp = $_[2]; + push @messages, @{ $rsp->{data} || [] }; + return; + }; + local *xCAT::Utils::runcmd = sub { + my ($class, $command, @arguments) = @_; + if ($fail_archive && $command =~ /cpio -H newc -o/) { + $::RUNCMD_RC = 1; + return; + } + return $real_runcmd->($class, $command, @arguments); + }; + @inserted = xCAT_plugin::anaconda::insert_dd( + sub { return; }, + 'rhels9.6', 'x86_64', $initrd, undef, + $source, '', '', 0, + ); + } + chdir($cwd) or die "Unable to restore $cwd: $!"; + return ( \@inserted, \@messages ); +} + +my ($inserted) = insert_driver_disk("dud:$driver_disk"); +is_deeply($inserted, [$driver_disk], 'insert_dd reports the driver disk it appended'); +cmp_ok(-s $initrd, '>', $base_size, 'the driver disk archive is appended to the initrd'); + +my $marker = xCAT_plugin::anaconda::_driver_disk_marker_path($initrd); +ok(-f $marker, 'successful injection records the driver disk beside the initrd'); + +is( + xCAT_plugin::anaconda::_driver_disk_kernel_arg('6.10', $initrd), + '', + 'anaconda before 7 continues to auto-load the embedded driver disk', +); +is( + xCAT_plugin::anaconda::_driver_disk_kernel_arg('9.6', $initrd), + ' inst.dd=/dd.img', + 'anaconda 7 and newer is given the inst.dd argument it reads', +); +write_text($initrd, "reused initrd\n"); +is( + xCAT_plugin::anaconda::_driver_disk_kernel_arg('9.6', $initrd), + ' inst.dd=/dd.img', + 'a later nodeset --noupdateinitrd run reuses the persistent marker', +); + +write_text($initrd, "fresh initrd\n"); +my ($without_disk) = insert_driver_disk(undef); +is_deeply($without_disk, [], 'an image without a driver disk reports no insertion'); +ok(!-e $marker, 'rebuilding without a driver disk clears a stale marker'); +is( + xCAT_plugin::anaconda::_driver_disk_kernel_arg('9.6', $initrd), + '', + 'an image without an injected driver disk receives no kernel argument', +); + +my $missing_disk = File::Spec->catfile($tempdir, 'missing-dd.img'); +my ($failed, $copy_messages) = insert_driver_disk("dud:$missing_disk"); +is_deeply($failed, [], 'a driver disk that cannot be copied is not reported as inserted'); +like(join("\n", @{$copy_messages}), qr/Could not copy the driver disk/, 'the failed copy takes the expected error path'); +ok(!-e $marker, 'failed injection leaves no driver disk marker'); + +($inserted) = insert_driver_disk("dud:$driver_disk"); +ok(-f $marker, 'the marker exists before an archive failure is exercised'); +my ($archive_failed, $archive_messages) = insert_driver_disk("dud:$driver_disk", 1); +is_deeply($archive_failed, [], 'a driver disk that cannot be archived is not reported as inserted'); +like(join("\n", @{$archive_messages}), qr/Could not archive the driver disk/, 'the failed archive takes the expected error path'); +ok(!-e $marker, 'a failed driver disk archive leaves no marker'); + +ok( + xCAT_plugin::anaconda::_set_driver_disk_marker(undef, 0), + 'clearing a marker for an unavailable initrd is a no-op', +); + +done_testing();