mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-05-05 16:49:08 +00:00
fix: improve Ubuntu LTS provisioning support
This commit is contained in:
@@ -351,6 +351,62 @@ backend scope described above.
|
||||
``ALLOC_FAIL_NO_POOLS``; DHCP/TFTP/GRUB handoff passed and the node
|
||||
reached Genesis with the temporary EL9 ppc64le payload workaround.
|
||||
|
||||
Ubuntu LTS KVM Validation Snapshot
|
||||
----------------------------------
|
||||
|
||||
As of April 29, 2026, the Ubuntu LTS KVM validation for the Ubuntu
|
||||
provisioning restoration work has the following result:
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 14 10 10 12 12 42
|
||||
|
||||
* - Platform
|
||||
- Arch
|
||||
- Backend
|
||||
- BIOS
|
||||
- UEFI
|
||||
- Notes
|
||||
* - Ubuntu 18.04 LTS
|
||||
- ``x86_64``
|
||||
- ``ISC``
|
||||
- Pass
|
||||
- Pass
|
||||
- Stateless and stateful compute boots passed against an Ubuntu 18.04
|
||||
headnode. The 18.04 debian-installer initrd did not include
|
||||
``virtio_blk`` in this KVM environment, so stateful VMs used
|
||||
``virtio-scsi`` disks. The legacy preseed disables installer
|
||||
update/security services so stateful installs and ``apt-get update``
|
||||
use only the local xCAT install media. Persistent interface naming used
|
||||
``R::net.ifnames=0 R::biosdevname=0``.
|
||||
* - Ubuntu 20.04 LTS
|
||||
- ``x86_64``
|
||||
- ``ISC``
|
||||
- Pass
|
||||
- Pass
|
||||
- Stateless and stateful compute boots passed. Stateful validation used a
|
||||
manual static reservation workaround for the known forced-ISC OMAPI
|
||||
issue.
|
||||
* - Ubuntu 22.04 LTS
|
||||
- ``x86_64``
|
||||
- ``Kea``
|
||||
- Pass
|
||||
- Pass
|
||||
- Stateless and stateful compute boots passed. Kea 2.0.2 configuration
|
||||
validation with ``kea-dhcp4 -t`` passed on the Ubuntu 22.04 headnode.
|
||||
``makedns -n`` starts ``bind9`` successfully, and the DHCP section of
|
||||
``xcatprobe xcatmn`` passed on consecutive runs.
|
||||
* - Ubuntu 24.04 LTS
|
||||
- ``x86_64``
|
||||
- ``Kea``
|
||||
- Pass
|
||||
- Pass
|
||||
- Stateless and stateful compute boots passed. Kea 2.4.1 configuration
|
||||
validation with ``kea-dhcp4 -t`` passed on the Ubuntu 24.04 headnode.
|
||||
``makedns -n`` starts ``bind9`` successfully, and the DHCP section of
|
||||
``xcatprobe xcatmn`` passed on consecutive runs. UEFI validation used
|
||||
OVMF Secure Boot disabled.
|
||||
|
||||
Skipped Rows
|
||||
------------
|
||||
|
||||
|
||||
@@ -290,7 +290,7 @@ Unit tests:
|
||||
* host reservation formatting
|
||||
* subnet and pool mapping
|
||||
* Kea reservation policy flags for subnet reservations and out-of-pool-only
|
||||
overrides
|
||||
overrides, plus DHCPv4 ``match-client-id`` behavior
|
||||
* backend selection and override behavior
|
||||
|
||||
Configuration validation tests:
|
||||
@@ -319,6 +319,8 @@ Integration matrix:
|
||||
* EL9 plus ISC
|
||||
* EL9 plus forced Kea
|
||||
* EL10 plus Kea
|
||||
* Ubuntu 18.04 plus ISC
|
||||
* Ubuntu 20.04 plus ISC
|
||||
* Ubuntu 22.04 plus Kea
|
||||
* Ubuntu 24.04 plus Kea
|
||||
|
||||
@@ -353,7 +355,8 @@ ordinary containers.
|
||||
Open test infrastructure details to confirm:
|
||||
|
||||
* SSH access method and user for ``rome01.local.versatushpc.com.br``
|
||||
* available base images for EL9, EL10, Ubuntu 22.04, and Ubuntu 24.04
|
||||
* available base images for EL9, EL10, Ubuntu 18.04, Ubuntu 20.04, Ubuntu
|
||||
22.04, and Ubuntu 24.04
|
||||
* libvirt network names and whether isolated DHCP test networks are already
|
||||
available
|
||||
* whether nested or privileged test guests can run DHCP client and PXE tests
|
||||
|
||||
@@ -63,10 +63,11 @@ sub render_dhcp4_config {
|
||||
type => 'memfile',
|
||||
name => '/var/lib/kea/kea-leases4.csv',
|
||||
},
|
||||
'valid-lifetime' => _integer( _first_defined( $intent->{'valid-lifetime'}, $intent->{valid_lifetime}, 43200 ) ),
|
||||
'reservations-in-subnet' => _json_bool( _first_defined( $intent->{'reservations-in-subnet'}, $intent->{reservations_in_subnet}, 1 ) ),
|
||||
'reservations-out-of-pool' => _json_bool( _first_defined( $intent->{'reservations-out-of-pool'}, $intent->{reservations_out_of_pool}, 1 ) ),
|
||||
subnet4 => [ map { $self->_render_subnet4($_) } @{ _first_defined( $intent->{subnet4}, $intent->{subnets}, [] ) } ],
|
||||
'valid-lifetime' => _integer( _first_defined( $intent->{'valid-lifetime'}, $intent->{valid_lifetime}, 43200 ) ),
|
||||
'reservations-in-subnet' => _json_bool( _first_defined( $intent->{'reservations-in-subnet'}, $intent->{reservations_in_subnet}, 1 ) ),
|
||||
'reservations-out-of-pool' => _json_bool( _first_defined( $intent->{'reservations-out-of-pool'}, $intent->{reservations_out_of_pool}, 1 ) ),
|
||||
'match-client-id' => _json_bool( _first_defined( $intent->{'match-client-id'}, $intent->{match_client_id}, 0 ) ),
|
||||
subnet4 => [ map { $self->_render_subnet4($_) } @{ _first_defined( $intent->{subnet4}, $intent->{subnets}, [] ) } ],
|
||||
);
|
||||
|
||||
$dhcp4{'control-socket'} = $intent->{'control-socket'} if $intent->{'control-socket'};
|
||||
|
||||
@@ -158,6 +158,66 @@ sub get_os {
|
||||
return $os;
|
||||
}
|
||||
|
||||
sub _netplan_get {
|
||||
my $key = shift;
|
||||
open(my $fh, '-|', 'netplan', 'get', $key) or return undef;
|
||||
my $val = <$fh>;
|
||||
close $fh;
|
||||
return undef if $?;
|
||||
chomp $val if defined $val;
|
||||
return $val;
|
||||
}
|
||||
|
||||
sub _command_available {
|
||||
my $cmd = shift;
|
||||
for my $dir (split /:/, $ENV{PATH} || '') {
|
||||
next unless $dir;
|
||||
return 1 if -x "$dir/$cmd";
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub _netplan_has_static_ip {
|
||||
my ($nic, $ip) = @_;
|
||||
|
||||
return 0 unless _command_available('netplan');
|
||||
|
||||
(my $escaped_nic = $nic) =~ s/\./\\./g;
|
||||
for my $devtype (qw(ethernets bonds bridges vlans)) {
|
||||
my $dev_obj = _netplan_get("$devtype.$escaped_nic");
|
||||
next unless defined $dev_obj;
|
||||
next if ($dev_obj eq 'null' || $dev_obj eq '');
|
||||
|
||||
my $addresses = _netplan_get("$devtype.$escaped_nic.addresses");
|
||||
next unless defined $addresses;
|
||||
next if ($addresses eq 'null' || $addresses eq '');
|
||||
next unless $addresses =~ /(?:^|[\s\[,])\Q$ip\E(?:\/\d+)?(?:$|[\s\],])/m;
|
||||
|
||||
my $dhcp_val = _netplan_get("$devtype.$escaped_nic.dhcp4");
|
||||
return 0 if defined $dhcp_val && $dhcp_val =~ /true/i;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub dhcp_query_reply_mac {
|
||||
my ($reply, $node, $ip) = @_;
|
||||
|
||||
return unless defined $reply && defined $node && defined $ip;
|
||||
return $1 if $reply =~ /\Q$node\E:\s+ip-address\s*=\s*\Q$ip\E,?\s+hardware-address\s*=\s*([0-9a-f:]+)/i;
|
||||
return;
|
||||
}
|
||||
|
||||
sub dhcp_query_reply_matches {
|
||||
my ($reply, $node, $ip, $mac) = @_;
|
||||
|
||||
return 0 unless defined $mac;
|
||||
my $reply_mac = dhcp_query_reply_mac($reply, $node, $ip);
|
||||
return 0 unless defined $reply_mac;
|
||||
return lc($reply_mac) eq lc($mac);
|
||||
}
|
||||
|
||||
#------------------------------------------
|
||||
|
||||
=head3
|
||||
@@ -206,8 +266,9 @@ sub is_static_ip {
|
||||
my $output2 = `cat /etc/sysconfig/network/ifcfg-$nic 2>&1 |grep -i BOOTPROTO`;
|
||||
$rst = 1 if (($output1 =~ /$ip/) && ($output2 =~ /static/i));
|
||||
} elsif ($os =~ /ubuntu/) {
|
||||
my $output = `cat /etc/network/interfaces 2>&1|grep -E "iface\\s+$nic"`;
|
||||
$rst = 1 if ($output =~ /static/i);
|
||||
return 1 if _netplan_has_static_ip($nic, $ip);
|
||||
my $output = `cat /etc/network/interfaces 2>&1`;
|
||||
$rst = 1 if (($output =~ /iface\s+\Q$nic\E\s+inet\s+static/i) && ($output =~ /\baddress\s+\Q$ip\E(?:\/\d+)?\b/i));
|
||||
}
|
||||
return $rst;
|
||||
}
|
||||
|
||||
@@ -1093,10 +1093,9 @@ sub check_dhcp_service {
|
||||
my $snip = xCAT::NetworkUtils->getipaddr($sntmp);
|
||||
my $snmac = `lsdef $sntmp -i mac -c 2>&1| awk -F'=' '{print \$2}'`;
|
||||
chomp($snmac);
|
||||
my $tmpmac;
|
||||
if ($tmp =~ /$sntmp: ip-address = $snip, hardware-address = (.+)/) {
|
||||
$tmpmac = $1;
|
||||
if ($tmpmac !~ $snmac) {
|
||||
my $tmpmac = probe_utils::dhcp_query_reply_mac($tmp, $sntmp, $snip);
|
||||
if (defined($tmpmac)) {
|
||||
if (lc($tmpmac) ne lc($snmac)) {
|
||||
push @$error_ref, "DHCP server reply is wrong";
|
||||
returncmdoutput($tmp, $error_ref) if ($verbose);
|
||||
$rc = 1;
|
||||
@@ -1108,6 +1107,10 @@ sub check_dhcp_service {
|
||||
}
|
||||
} else {
|
||||
|
||||
# Clean up any leftover test node from a previous failed run
|
||||
`makedhcp -d xcatmntest 2>&1`;
|
||||
`rmdef xcatmntest 2>&1`;
|
||||
|
||||
my $tmp = `chdef xcatmntest groups=all ip=$serverip mac=aa:aa:aa:aa:aa:aa 2>&1`;
|
||||
if ($?) {
|
||||
push @$error_ref, "Node simulation by 'chdef' has failed";
|
||||
@@ -1121,7 +1124,7 @@ sub check_dhcp_service {
|
||||
`cp /etc/hosts /etc/hosts.bak.probe > /dev/null 2>&1`;
|
||||
|
||||
open HOSTFILE, ">> /etc/hosts";
|
||||
print HOSTFILE "$serverip xcatmntest xcatmntest.$domain";
|
||||
print HOSTFILE "$serverip xcatmntest xcatmntest.$domain\n";
|
||||
close HOSTFILE;
|
||||
|
||||
probe_utils->send_msg($outputtarget, "d", "To do 'makedhcp xcatmntest'") if ($verbose);
|
||||
@@ -1130,40 +1133,45 @@ sub check_dhcp_service {
|
||||
push @$error_ref, "makedhcp xcatmntest failed";
|
||||
returncmdoutput($tmp, $error_ref) if ($verbose);
|
||||
$rc = 1;
|
||||
`makedhcp -d xcatmntest 2>&1`;
|
||||
`rmdef xcatmntest 2>&1`;
|
||||
if (-f "/etc/hosts.bak.probe") {
|
||||
unlink "/etc/hosts";
|
||||
move("/etc/hosts.bak.probe", "/etc/hosts");
|
||||
}
|
||||
last;
|
||||
}
|
||||
|
||||
# Allow DHCP server time to commit the OMAPI-added entry
|
||||
sleep 1;
|
||||
|
||||
$tmp = `makedhcp -q xcatmntest 2>&1`;
|
||||
if ($?) {
|
||||
push @$error_ref, "makedhcp -q xcatmntest failed";
|
||||
returncmdoutput($tmp, $error_ref) if ($verbose);
|
||||
$rc = 1;
|
||||
`makedhcp -d xcatmntest 2>&1 && rmdef xcatmntest 2>&1`;
|
||||
last;
|
||||
}
|
||||
chomp($tmp);
|
||||
if (length($tmp) == 0) {
|
||||
# makedhcp -q did not return anything, and RC was 0
|
||||
chomp($tmp) if defined($tmp);
|
||||
if (!$rc && length($tmp) == 0) {
|
||||
push @$error_ref, "DHCP server returned no data for 'makedhcp -q xcatmntest'";
|
||||
returncmdoutput($tmp, $error_ref) if ($verbose);
|
||||
$rc = 1;
|
||||
last;
|
||||
}
|
||||
if ($tmp !~ /xcatmntest: ip-address = $serverip, hardware-address = aa:aa:aa:aa:aa:aa/) {
|
||||
if (!$rc && !probe_utils::dhcp_query_reply_matches($tmp, 'xcatmntest', $serverip, 'aa:aa:aa:aa:aa:aa')) {
|
||||
push @$error_ref, "DHCP server reply does not match expected IP and MAC";
|
||||
returncmdoutput($tmp, $error_ref) if ($verbose);
|
||||
$rc = 1;
|
||||
`makedhcp -d xcatmntest 2>&1 && rmdef xcatmntest 2>&1`;
|
||||
last;
|
||||
}
|
||||
|
||||
# Always clean up the test node regardless of success or failure
|
||||
push @$error_ref, "Start clearing simulation information for dhcp test" if ($verbose);
|
||||
$tmp = `makedhcp -d xcatmntest 2>&1 && rmdef xcatmntest 2>&1`;
|
||||
$tmp = `makedhcp -d xcatmntest 2>&1 && rmdef xcatmntest 2>&1`;
|
||||
returncmdoutput($tmp, $error_ref) if ($verbose);
|
||||
|
||||
unlink "/etc/hosts";
|
||||
move("/etc/hosts.bak.probe", "/etc/hosts");
|
||||
if (-f "/etc/hosts.bak.probe") {
|
||||
unlink "/etc/hosts";
|
||||
move("/etc/hosts.bak.probe", "/etc/hosts");
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($rc) {
|
||||
@@ -1492,5 +1500,3 @@ exit $rst;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1590,28 +1590,38 @@ sub setupNFSTree {
|
||||
shift @entries;
|
||||
if (grep /\Q$nfsdirectory\E/, @entries) {
|
||||
$callback->({ data => ["$nfsdirectory has been exported already!"] });
|
||||
|
||||
# nothing to do
|
||||
} else {
|
||||
$cmd = "/usr/sbin/exportfs :$nfsdirectory";
|
||||
$cmd = "/usr/sbin/exportfs :$nfsdirectory -o rw,no_root_squash,sync,no_subtree_check,insecure";
|
||||
xCAT::Utils->runcmd($cmd, 0);
|
||||
|
||||
# exportfs can export this directory immediately
|
||||
$callback->({ data => ["now $nfsdirectory is exported!"] });
|
||||
$cmd = "cat /etc/exports";
|
||||
@entries = xCAT::Utils->runcmd($cmd, 0);
|
||||
unless (my $entry = grep /\Q$nfsdirectory\E/, @entries) {
|
||||
}
|
||||
|
||||
#if there's no entry in /etc/exports, one with default options will be added
|
||||
$cmd = qq{echo "$nfsdirectory *(rw,no_root_squash,sync,no_subtree_check)" >> /etc/exports};
|
||||
xCAT::Utils->runcmd($cmd, 0);
|
||||
$callback->({ data => ["$nfsdirectory is added to /etc/exports with default option"] });
|
||||
}
|
||||
if (ensure_nfs_export_option($nfsdirectory, 'insecure')) {
|
||||
xCAT::Utils->runcmd("/usr/sbin/exportfs -r", 0);
|
||||
$callback->({ data => ["added insecure to existing $nfsdirectory export"] });
|
||||
} elsif (!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 nfs_export_exists {
|
||||
my ( $dir, %opts ) = _nfs_method_args(@_);
|
||||
my @files = _nfs_export_files(%opts);
|
||||
for my $file (@files) {
|
||||
open(my $fh, '<', $file) or next;
|
||||
while (<$fh>) {
|
||||
next if /^\s*#/;
|
||||
return 1 if /^\s*\Q$dir\E\s/;
|
||||
}
|
||||
close $fh;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub setupStatemnt {
|
||||
my $sip = shift;
|
||||
if (($sip) && ($sip =~ /xCAT::SvrUtils/))
|
||||
@@ -1639,26 +1649,120 @@ sub setupStatemnt {
|
||||
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";
|
||||
$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!"] });
|
||||
}
|
||||
|
||||
# add the directory into /etc/exports if not exist
|
||||
$cmd = "cat /etc/exports";
|
||||
@entries = xCAT::Utils->runcmd($cmd, 0);
|
||||
if (my $entry = grep /\Q$nfsdirectory\E/, @entries) {
|
||||
unless ($entry =~ m/rw/) {
|
||||
$callback->({ data => ["The $nfsdirectory should be with rw option in /etc/exports"] });
|
||||
}
|
||||
} else {
|
||||
xCAT::Utils->runcmd(qq{echo "$nfsdirectory *(rw,no_root_squash,sync,no_subtree_check)" >>/etc/exports}, 0);
|
||||
$callback->({ data => ["$nfsdirectory is added into /etc/exports with default options"] });
|
||||
}
|
||||
if (ensure_nfs_export_option($nfsdirectory, 'insecure')) {
|
||||
xCAT::Utils->runcmd("/usr/sbin/exportfs -r", 0);
|
||||
$callback->({ data => ["added insecure to existing $nfsdirectory export"] });
|
||||
} elsif (!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"] });
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sub ensure_nfs_export_option {
|
||||
my ( $dir, @args ) = _nfs_method_args(@_);
|
||||
local @_ = @args;
|
||||
my $option = shift || 'insecure';
|
||||
my %opts = @_;
|
||||
my @files = _nfs_export_files(%opts);
|
||||
my $changed = 0;
|
||||
for my $file (@files) {
|
||||
open(my $fh, '<', $file) or next;
|
||||
my @raw = <$fh>;
|
||||
close $fh;
|
||||
|
||||
my @logical;
|
||||
my $buf = '';
|
||||
for my $r (@raw) {
|
||||
chomp $r;
|
||||
if ($r =~ s/\s*\\$//) {
|
||||
$buf .= "$r ";
|
||||
next;
|
||||
}
|
||||
$buf .= $r;
|
||||
push @logical, $buf;
|
||||
$buf = '';
|
||||
}
|
||||
push @logical, $buf if $buf ne '';
|
||||
|
||||
my $file_changed = 0;
|
||||
for my $line (@logical) {
|
||||
next if $line =~ /^\s*#/;
|
||||
next unless $line =~ /^\s*\Q$dir\E\s/;
|
||||
my $line_changed = 0;
|
||||
my $comment = '';
|
||||
$comment = " $1" if $line =~ s/\s+(#.*)$//;
|
||||
my $tail = $line;
|
||||
$tail =~ s/^\s*\Q$dir\E\s+//;
|
||||
my @tokens;
|
||||
while ($tail =~ /(\S+(?:\([^)]*\))?)/g) {
|
||||
push @tokens, $1;
|
||||
}
|
||||
my @out;
|
||||
for my $tok (@tokens) {
|
||||
if ($tok =~ /^-(.+)/) {
|
||||
my $opts = $1;
|
||||
unless (_option_list_has($opts, $option)) {
|
||||
$tok = "-$opts,$option";
|
||||
$line_changed = 1;
|
||||
}
|
||||
} elsif ($tok =~ /^([^(]+)\(([^)]*)\)$/) {
|
||||
my ($client, $opts) = ($1, $2);
|
||||
unless (_option_list_has($opts, $option)) {
|
||||
$tok = "$client($opts,$option)";
|
||||
$line_changed = 1;
|
||||
}
|
||||
} else {
|
||||
$tok = "$tok($option)";
|
||||
$line_changed = 1;
|
||||
}
|
||||
push @out, $tok;
|
||||
}
|
||||
if ($line_changed) {
|
||||
$line = "$dir " . join(" ", @out) . "$comment";
|
||||
$file_changed = 1;
|
||||
}
|
||||
}
|
||||
if ($file_changed) {
|
||||
open(my $wfh, '>', $file) or next;
|
||||
print $wfh map { "$_\n" } @logical;
|
||||
close $wfh;
|
||||
$changed = 1;
|
||||
}
|
||||
}
|
||||
return $changed;
|
||||
}
|
||||
|
||||
sub _nfs_method_args {
|
||||
my @args = @_;
|
||||
shift @args if @args && (ref($args[0]) || $args[0] eq __PACKAGE__);
|
||||
return @args;
|
||||
}
|
||||
|
||||
sub _nfs_export_files {
|
||||
my %opts = @_;
|
||||
return @{ $opts{files} } if ref($opts{files}) eq 'ARRAY';
|
||||
|
||||
my @files = ('/etc/exports');
|
||||
push @files, glob('/etc/exports.d/*.exports') if -d '/etc/exports.d';
|
||||
return @files;
|
||||
}
|
||||
|
||||
sub _option_list_has {
|
||||
my ($list, $option) = @_;
|
||||
foreach my $item (split /,/, $list) {
|
||||
$item =~ s/^\s+|\s+$//g;
|
||||
return 1 if $item eq $option;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------------------------
|
||||
# Common method to send info back to the client
|
||||
# The last two args are optional, though $allerrornodes will unlikely be there without $node
|
||||
|
||||
@@ -1132,17 +1132,23 @@ sub mirrorspec {
|
||||
if (!$pkgdir) {
|
||||
$pkgdir = $_;
|
||||
} else {
|
||||
my $osuurl = "http://" . $masternode.':'.$ENV{httpport} . $_ . " ./";
|
||||
my $httpport = $ENV{HTTPPORT} || $ENV{httpport} || '80';
|
||||
my $osuurl = "http://" . $masternode . ':' . $httpport . $_ . " ./";
|
||||
push @mirrors, $osuurl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($pkgdir) {
|
||||
my $httpport = $ENV{HTTPPORT} || $ENV{httpport} || '80';
|
||||
my $security_host = $masternode;
|
||||
$security_host .= ':' . $httpport if $httpport ne '80';
|
||||
$line .= "
|
||||
d-i mirror/country string manual\n
|
||||
d-i mirror/protocol string http\n
|
||||
d-i mirror/http/directory string $pkgdir\n
|
||||
d-i apt-setup/security_host string $security_host\n
|
||||
d-i apt-setup/security_path string $pkgdir\n
|
||||
d-i mirror/http/proxy string\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ use strict;
|
||||
use xCAT::Table;
|
||||
|
||||
use xCAT::Utils;
|
||||
use xCAT::SvrUtils;
|
||||
use xCAT::TableUtils;
|
||||
use xCAT::NetworkUtils;
|
||||
use xCAT::DHCP::Backend;
|
||||
@@ -398,15 +399,11 @@ sub setupInstallloc
|
||||
# add /install to /etc/exports - if needed
|
||||
#
|
||||
|
||||
my $cmd = "/bin/cat /etc/exports | grep '$installdir'";
|
||||
my $outref = xCAT::Utils->runcmd("$cmd", -1);
|
||||
my $changed_exports;
|
||||
if ($::RUNCMD_RC != 0)
|
||||
if (!xCAT::SvrUtils->nfs_export_exists($installdir))
|
||||
{
|
||||
|
||||
# ok - then add this entry
|
||||
my $cmd =
|
||||
"/bin/echo '$installdir *(rw,no_root_squash,sync,no_subtree_check)' >> /etc/exports";
|
||||
"/bin/echo '$installdir *(rw,no_root_squash,sync,no_subtree_check,insecure)' >> /etc/exports";
|
||||
my $outref = xCAT::Utils->runcmd("$cmd", 0);
|
||||
if ($::RUNCMD_RC != 0)
|
||||
{
|
||||
@@ -418,11 +415,16 @@ sub setupInstallloc
|
||||
$changed_exports++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (xCAT::SvrUtils->ensure_nfs_export_option($installdir, 'insecure'))
|
||||
{
|
||||
$changed_exports++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($changed_exports)
|
||||
{
|
||||
|
||||
# restart nfs
|
||||
&setup_NFS($nodename);
|
||||
|
||||
my $cmd = "/usr/sbin/exportfs -a";
|
||||
@@ -431,7 +433,6 @@ sub setupInstallloc
|
||||
{
|
||||
xCAT::MsgUtils->message('S', "Error with $cmd.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,18 +27,34 @@ my $ddns_key_path = "/etc/xcat/ddns.key";
|
||||
|
||||
# Net::DNS >= 1.36 removed support for sign_tsig($keyname, $secret) and now
|
||||
# expects a keyfile. Keep the keyfile in sync with the xcat_key secret.
|
||||
sub ddns_tsig_algorithm {
|
||||
my ($ctx) = @_;
|
||||
|
||||
# Older Net::DNS releases used by Ubuntu 22.04 cannot sign updates with the
|
||||
# keyfile API, so keep the generated xCAT key on the legacy algorithm that
|
||||
# both old and new Net::DNS can use.
|
||||
return "hmac-md5" if (Net::DNS->VERSION < 1.36);
|
||||
return $ctx->{tsig_algorithm} || "hmac-md5";
|
||||
}
|
||||
|
||||
sub ddns_key_contents {
|
||||
my ($ctx) = @_;
|
||||
|
||||
my $algorithm = ddns_tsig_algorithm($ctx);
|
||||
return
|
||||
"key \"xcat_key\" {\n"
|
||||
. "\talgorithm $algorithm;\n"
|
||||
. "\tsecret \"" . $ctx->{privkey} . "\";\n"
|
||||
. "};\n\n";
|
||||
}
|
||||
|
||||
sub ensure_ddns_key_file {
|
||||
my ($ctx) = @_;
|
||||
|
||||
return unless (Net::DNS->VERSION >= 1.36);
|
||||
return unless ($ctx && $ctx->{privkey});
|
||||
|
||||
my $algorithm = $ctx->{tsig_algorithm} || "hmac-sha256";
|
||||
my $contents =
|
||||
"key \"xcat_key\" {\n"
|
||||
. "\talgorithm $algorithm;\n"
|
||||
. "\tsecret \"" . $ctx->{privkey} . "\";\n"
|
||||
. "};\n\n";
|
||||
my $contents = ddns_key_contents($ctx);
|
||||
|
||||
if (open(my $existing_fh, "<", $ddns_key_path)) {
|
||||
local $/;
|
||||
@@ -1278,18 +1294,22 @@ sub update_namedconf {
|
||||
$gotkey = 1;
|
||||
my $algorithmnow;
|
||||
if ($ctx->{privkey}) {
|
||||
|
||||
#for now, assume the field is correct
|
||||
#push @newnamed,"key xcat_key {\n","\talgorithm hmac-md5;\n","\tsecret \"".$ctx->{privkey}."\";\n","};\n\n";
|
||||
push @newnamed, $line;
|
||||
my @keyblock = ($line);
|
||||
do {
|
||||
$i++;
|
||||
$line = $currnamed[$i];
|
||||
if ($line =~ /^\s*algorithm\s+([^;\s]+)\s*;/) {
|
||||
$algorithmnow = $1;
|
||||
}
|
||||
push @newnamed, $line;
|
||||
push @keyblock, $line;
|
||||
} while ($line !~ /^\};/);
|
||||
if ($algorithmnow && Net::DNS->VERSION < 1.36 && lc($algorithmnow) ne "hmac-md5") {
|
||||
$ctx->{tsig_algorithm} = "hmac-md5";
|
||||
push @newnamed, ddns_key_contents($ctx);
|
||||
$ctx->{restartneeded} = 1;
|
||||
} else {
|
||||
push @newnamed, @keyblock;
|
||||
}
|
||||
} else {
|
||||
push @newnamed, $line;
|
||||
while ($line !~ /^\};/) { #skip the old file zone
|
||||
@@ -1406,8 +1426,8 @@ sub update_namedconf {
|
||||
$ctx->{privkey} = encode_base64(genpassword(32));
|
||||
chomp($ctx->{privkey});
|
||||
}
|
||||
$ctx->{tsig_algorithm} ||= "hmac-sha256";
|
||||
my $contents = "key \"xcat_key\" {\n" . "\talgorithm " . $ctx->{tsig_algorithm} . ";\n" . "\tsecret \"" . $ctx->{privkey} . "\";\n" . "};\n\n";
|
||||
$ctx->{tsig_algorithm} ||= ddns_tsig_algorithm($ctx);
|
||||
my $contents = ddns_key_contents($ctx);
|
||||
push @newnamed, $contents;
|
||||
ensure_ddns_key_file($ctx);
|
||||
$ctx->{restartneeded} = 1;
|
||||
|
||||
@@ -418,8 +418,27 @@ sub copycd
|
||||
my @line = split(" ", `ls -lh $installroot/$distname/$arch/dists/ | grep dr`);
|
||||
my $dist = $line[ @line - 1 ];
|
||||
|
||||
# touches the Packages file so that deb packaging works
|
||||
system("touch $installroot/$distname/$arch/dists/$dist/restricted/binary-$debarch/Packages");
|
||||
# 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");
|
||||
@@ -753,9 +772,12 @@ sub mkinstall {
|
||||
}
|
||||
mkpath($autoinstfile);
|
||||
|
||||
# create empty meta-data file
|
||||
# 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";
|
||||
@@ -937,6 +959,7 @@ sub mkinstall {
|
||||
if (using_subiquity($os,$tmplfile)) {
|
||||
$kcmdline .= " autoinstall ip=dhcp netboot=nfs nfsroot=${instserver}:${pkgdir}";
|
||||
$kcmdline .= " ds=nocloud-net;s=http://${instserver}:${httpport}/install/autoinst/${node}/";
|
||||
$kcmdline .= " ---";
|
||||
} else {
|
||||
$kcmdline .= " url=http://${instserver}:$httpport/install/autoinst/$node";
|
||||
$kcmdline .= " mirror/http/hostname=${instserver}:$httpport";
|
||||
@@ -1766,20 +1789,18 @@ sub setupNFSTree {
|
||||
if (grep /\Q$nfsdirectory\E/, @entries) {
|
||||
$callback->({ data => ["$nfsdirectory has been exported already!"] });
|
||||
} else {
|
||||
$cmd = "/usr/sbin/exportfs :$nfsdirectory";
|
||||
$cmd = "/usr/sbin/exportfs :$nfsdirectory -o rw,no_root_squash,sync,no_subtree_check,insecure";
|
||||
xCAT::Utils->runcmd($cmd, 0);
|
||||
|
||||
# exportfs can export this directory immediately
|
||||
$callback->({ data => ["now $nfsdirectory is exported!"] });
|
||||
$cmd = "cat /etc/exports";
|
||||
@entries = xCAT::Utils->runcmd($cmd, 0);
|
||||
unless (my $entry = grep /\Q$nfsdirectory\E/, @entries) {
|
||||
}
|
||||
|
||||
# if no entry in /etc/exports, one entry with default options will be added
|
||||
$cmd = qq{echo "$nfsdirectory *(rw,no_root_squash,sync,no_subtree_check)" >> /etc/exports};
|
||||
xCAT::Utils->runcmd($cmd, 0);
|
||||
$callback->({ data => ["$nfsdirectory is added to /etc/exports with default option"] });
|
||||
}
|
||||
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"] });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1808,21 +1829,17 @@ sub setupStatemnt {
|
||||
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";
|
||||
$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!"] });
|
||||
}
|
||||
|
||||
# add the directory into /etc/exports if not exist
|
||||
$cmd = "cat /etc/exports";
|
||||
@entries = xCAT::Utils->runcmd($cmd, 0);
|
||||
if (my $entry = grep /\Q$nfsdirectory\E/, @entries) {
|
||||
unless ($entry =~ m/rw/) {
|
||||
$callback->({ data => ["The $nfsdirectory should be with rw option in /etc/exports"] });
|
||||
}
|
||||
} else {
|
||||
xCAT::Utils->runcmd(qq{echo "$nfsdirectory *(rw,no_root_squash,sync,no_subtree_check)" >> /etc/exports}, 0);
|
||||
$callback->({ data => ["$nfsdirectory is added into /etc/exports with default options"] });
|
||||
}
|
||||
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"] });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,10 @@ use lib "$::XCATROOT/lib/perl";
|
||||
|
||||
use strict;
|
||||
use IPC::Open2;
|
||||
use IPC::Open3;
|
||||
use IO::Select;
|
||||
use Symbol qw/gensym/;
|
||||
use POSIX qw/WNOHANG/;
|
||||
use xCAT::Table;
|
||||
|
||||
#use Data::Dumper;
|
||||
@@ -149,6 +153,97 @@ sub handled_commands
|
||||
return { makedhcp => "dhcp", };
|
||||
}
|
||||
|
||||
######################################################
|
||||
# Run omshell to query a host and parse the output.
|
||||
# Uses a heredoc pipe instead of open2 to avoid deadlock
|
||||
# when omshell produces output before all input is consumed.
|
||||
######################################################
|
||||
sub _omshell_query_host
|
||||
{
|
||||
my $node = shift;
|
||||
my $omapiuser = shift;
|
||||
my $omapikey = shift;
|
||||
my $port = shift;
|
||||
|
||||
my $port_cmd = defined($port) ? "port $port\n" : "";
|
||||
my $omcmds = "${port_cmd}key $omapiuser \"$omapikey\"\nconnect\nnew host\nset name = \"$node\"\nopen\nclose\n";
|
||||
|
||||
my @output = _run_omshell($omcmds);
|
||||
|
||||
return _parse_omshell_host_output($node, @output);
|
||||
}
|
||||
|
||||
sub _run_omshell
|
||||
{
|
||||
my $omcmds = shift;
|
||||
|
||||
my ($in, $out);
|
||||
my $err = gensym;
|
||||
my $pid = eval { open3($in, $out, $err, '/usr/bin/omshell') };
|
||||
return () if $@ || !$pid;
|
||||
|
||||
print $in $omcmds;
|
||||
close($in);
|
||||
|
||||
my @output;
|
||||
my $selector = IO::Select->new($out, $err);
|
||||
my $deadline = time + 10;
|
||||
while ($selector->count) {
|
||||
my $remaining = $deadline - time;
|
||||
if ($remaining <= 0) {
|
||||
kill 'TERM', $pid;
|
||||
last;
|
||||
}
|
||||
|
||||
foreach my $fh ($selector->can_read($remaining)) {
|
||||
my $line = <$fh>;
|
||||
if (defined $line) {
|
||||
push @output, $line if fileno($fh) == fileno($out);
|
||||
} else {
|
||||
$selector->remove($fh);
|
||||
close($fh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (1 .. 10) {
|
||||
last if waitpid($pid, WNOHANG) == $pid;
|
||||
select(undef, undef, undef, 0.1);
|
||||
}
|
||||
if (waitpid($pid, WNOHANG) == 0) {
|
||||
kill 'KILL', $pid;
|
||||
waitpid($pid, 0);
|
||||
}
|
||||
|
||||
return @output;
|
||||
}
|
||||
|
||||
sub _parse_omshell_host_output
|
||||
{
|
||||
my ($node, @output) = @_;
|
||||
|
||||
my ($nname, $ipaddr, $hwaddr);
|
||||
foreach my $line (@output) {
|
||||
chomp $line;
|
||||
if ($line =~ /^\s*name\s*=\s*"?(.*?)"?\s*$/) {
|
||||
$nname = $1 if $1 eq $node;
|
||||
} elsif ($line =~ /^\s*hardware-address\s*=/) {
|
||||
$hwaddr = $line;
|
||||
} elsif ($line =~ /^\s*ip-address\s*=\s*(.+?)\s*$/) {
|
||||
my $ip = $1;
|
||||
if ($ip =~ /^(?:[[:xdigit:]]{1,2}:){3}[[:xdigit:]]{1,2}$/) {
|
||||
my @parts = split(/\:/, $ip);
|
||||
$ipaddr = "ip-address = " . join(".", map { hex($_) } @parts);
|
||||
} else {
|
||||
$ipaddr = "ip-address = $ip";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$nname = $node if !$nname && $ipaddr;
|
||||
return ($nname, $ipaddr, $hwaddr);
|
||||
}
|
||||
|
||||
######################################################
|
||||
# List nodes in DHCP for both IPv4 and IPv6
|
||||
######################################################
|
||||
@@ -156,154 +251,41 @@ sub listnode
|
||||
{
|
||||
my $node = shift;
|
||||
my $callback = shift;
|
||||
my $lines;
|
||||
my $ipaddr = "";
|
||||
my $hwaddr;
|
||||
my $nname;
|
||||
my $rsp;
|
||||
my ($OMOUT, $OMIN, $OMOUT6, $OMIN6);
|
||||
|
||||
my $usingipv6;
|
||||
my $omapiuser;
|
||||
my $omapikey;
|
||||
|
||||
# Collect the omapi user and key from the passwd table
|
||||
my $pwtab = xCAT::Table->new("passwd");
|
||||
my @pws = $pwtab->getAllAttribs('key', 'username', 'password', 'cryptmethod', 'authdomain', 'comments', 'disable');
|
||||
foreach (@pws) {
|
||||
|
||||
# Look for the opapi entry in the passwd table
|
||||
if ($_->{key} =~ "omapi") { #omapi key
|
||||
# save username and password for omapi connection
|
||||
if ($_->{key} =~ "omapi") {
|
||||
$omapiuser = $_->{username};
|
||||
$omapikey = $_->{password};
|
||||
}
|
||||
}
|
||||
|
||||
# Look through the networks table for networks with IPv6 format for address
|
||||
my $usingipv6;
|
||||
my $nettab = xCAT::Table->new("networks");
|
||||
my @vnets = $nettab->getAllAttribs('net', 'mgtifname', 'mask', 'dynamicrange', 'nameservers', 'ddnsdomain', 'domain');
|
||||
foreach (@vnets) {
|
||||
if ($_->{net} =~ /:/) { #IPv6 detected
|
||||
if ($_->{net} =~ /:/) {
|
||||
$usingipv6 = 1;
|
||||
}
|
||||
}
|
||||
|
||||
# open ipv4 omshell file handles - $OMOUT will contain the response
|
||||
open2($OMOUT, $OMIN, "/usr/bin/omshell ");
|
||||
|
||||
# setup omapi for the connection and check for the node requested
|
||||
print $OMIN "key "
|
||||
. $omapiuser . " \""
|
||||
. $omapikey . "\"\n";
|
||||
print $OMIN "connect\n";
|
||||
print $OMIN "new host\n";
|
||||
|
||||
# specify which node we are looking up
|
||||
print $OMIN "set name = \"$node\"\n";
|
||||
print $OMIN "open\n";
|
||||
|
||||
# the close will put the data into $OMOUT
|
||||
print $OMIN "close\n";
|
||||
close($OMIN);
|
||||
my $name = 0;
|
||||
|
||||
# Process the output
|
||||
while (<$OMOUT>) { # now read the output of sort(1)
|
||||
chomp $_;
|
||||
|
||||
# if this line contains the node name
|
||||
if ($_ =~ $node) {
|
||||
|
||||
# save the name returned
|
||||
if ($name) {
|
||||
$nname = $_;
|
||||
$nname =~ s/name = //;
|
||||
$nname =~ s/"//g;
|
||||
}
|
||||
$name = 1;
|
||||
}
|
||||
|
||||
# if this line is the hardware-address line
|
||||
if ($_ =~ 'hardware-address') {
|
||||
|
||||
# save the hardware address as it is with the hardware-address label
|
||||
$hwaddr = $_;
|
||||
}
|
||||
|
||||
# if this line is the ip-address line
|
||||
elsif ($_ =~ 'ip-address') {
|
||||
|
||||
# convert the hex IP address to a dotted decimal address for readability
|
||||
my ($ipname, $ip) = split /= /, $_;
|
||||
chomp($ip);
|
||||
my ($p1, $p2, $p3, $p4) = split(/\:/, $ip);
|
||||
my $dp1 = hex($p1);
|
||||
my $dp2 = hex($p2);
|
||||
my $dp3 = hex($p3);
|
||||
my $dp4 = hex($p4);
|
||||
$ipaddr = "ip-address = $dp1.$dp2.$dp3.$dp4";
|
||||
}
|
||||
}
|
||||
|
||||
# if we collected the ip address then print out the information for this node
|
||||
my ($nname, $ipaddr, $hwaddr) = _omshell_query_host($node, $omapiuser, $omapikey, undef);
|
||||
if ($ipaddr) {
|
||||
push @{ $rsp->{data} }, "$nname: $ipaddr, $hwaddr";
|
||||
xCAT::MsgUtils->message("I", $rsp, $callback);
|
||||
}
|
||||
close($OMOUT);
|
||||
|
||||
# if using IPv6 addresses check using omshell IPv6 port
|
||||
if ($usingipv6) {
|
||||
open2($OMOUT6, $OMIN6, "/usr/bin/omshell ");
|
||||
print $OMOUT6 "port 7912\n";
|
||||
print $OMOUT6 "connect\n";
|
||||
print $OMIN6 "key "
|
||||
. $omapiuser . " \""
|
||||
. $omapikey . "\"\n";
|
||||
print $OMIN6 "connect\n";
|
||||
print $OMIN6 "new host\n";
|
||||
|
||||
# check for the node specified
|
||||
print $OMIN6 "set name = \"$node\"\n";
|
||||
print $OMIN6 "open\n";
|
||||
print $OMIN6 "close\n";
|
||||
close($OMIN6);
|
||||
$name = 0;
|
||||
$ipaddr = "";
|
||||
while (<$OMOUT6>) { # now read the output
|
||||
chomp $_;
|
||||
if ($_ =~ $node) {
|
||||
|
||||
# save the name
|
||||
if ($name) {
|
||||
$nname = $_;
|
||||
$nname =~ s/name = //;
|
||||
$nname =~ s/"//g;
|
||||
}
|
||||
$name = 1;
|
||||
}
|
||||
if ($_ =~ 'hardware-address') {
|
||||
|
||||
# save the hardware-address
|
||||
$hwaddr = $_;
|
||||
}
|
||||
elsif ($_ =~ 'ip-address') {
|
||||
|
||||
#save the ip address
|
||||
my ($ipname, $ipaddr) = split /= /, $_;
|
||||
chomp($ipaddr);
|
||||
}
|
||||
}
|
||||
|
||||
# print the information if the ip address is found
|
||||
if ($ipaddr) {
|
||||
push @{ $rsp->{data} }, "$nname: $ipaddr, $hwaddr";
|
||||
my ($nname6, $ipaddr6, $hwaddr6) = _omshell_query_host($node, $omapiuser, $omapikey, 7912);
|
||||
if ($ipaddr6) {
|
||||
push @{ $rsp->{data} }, "$nname6: $ipaddr6, $hwaddr6";
|
||||
xCAT::MsgUtils->message("I", $rsp, $callback);
|
||||
}
|
||||
|
||||
# close the IPv6 output file handle
|
||||
close($OMOUT6);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -265,6 +265,7 @@ sub setstate {
|
||||
print $pcfg "exit\n";
|
||||
}
|
||||
close($pcfg);
|
||||
_write_uefi_exit_script($bootloader_root, $node, $cref->{currstate});
|
||||
} elsif ($kern and $kern->{kernel}) {
|
||||
if ($kern->{kernel} =~ /!/) { #TODO: deprecate this, do stateless Xen like stateless ESXi
|
||||
my $hypervisor;
|
||||
@@ -360,10 +361,21 @@ sub setstate {
|
||||
} else { #TODO: actually, should possibly default to xCAT image?
|
||||
print $pcfg "LOCALBOOT 0\n";
|
||||
close($pcfg);
|
||||
_write_uefi_exit_script($bootloader_root, $node, $cref->{currstate});
|
||||
}
|
||||
return (0, "");
|
||||
}
|
||||
|
||||
sub _write_uefi_exit_script {
|
||||
my ($bootloader_root, $node, $state) = @_;
|
||||
|
||||
open(my $ucfg, '>', "$bootloader_root/$node.uefi");
|
||||
print $ucfg "#!gpxe\n";
|
||||
print $ucfg "#$state\n" if $state;
|
||||
print $ucfg "exit\n";
|
||||
close($ucfg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
my $errored = 0;
|
||||
|
||||
@@ -23,6 +23,7 @@ BEGIN
|
||||
use lib "$::XCATROOT/lib/perl";
|
||||
use strict;
|
||||
use xCAT::Utils;
|
||||
use xCAT::SvrUtils;
|
||||
use xCAT::DHCP::Backend;
|
||||
use xCAT::NetworkUtils;
|
||||
use Getopt::Long;
|
||||
@@ -1881,52 +1882,30 @@ sub setupLinuxexports
|
||||
# add tftpboot to /etc/exports - if needed
|
||||
#
|
||||
|
||||
my $cmd = "/bin/cat /etc/exports | grep '$::TFTPDIR'";
|
||||
my $outref = xCAT::Utils->runcmd("$cmd", -1);
|
||||
if ($::RUNCMD_RC != 0)
|
||||
{
|
||||
|
||||
# ok - then add this entry
|
||||
#SECURITY: this has potential for sharing private host/user keys
|
||||
my $cmd =
|
||||
"/bin/echo '$::TFTPDIR *(rw,no_root_squash,sync,no_subtree_check)' >> /etc/exports";
|
||||
|
||||
my $outref = xCAT::Utils->runcmd("$cmd", 0);
|
||||
if ($::RUNCMD_RC != 0)
|
||||
foreach my $exportdir ($::TFTPDIR, $::INSTALLDIR) {
|
||||
if (!xCAT::SvrUtils->nfs_export_exists($exportdir))
|
||||
{
|
||||
xCAT::MsgUtils->message('E',
|
||||
"Could not update the /etc/exports file.");
|
||||
my $cmd =
|
||||
"/bin/echo '$exportdir *(rw,no_root_squash,sync,no_subtree_check,insecure)' >> /etc/exports";
|
||||
xCAT::Utils->runcmd("$cmd", 0);
|
||||
if ($::RUNCMD_RC != 0)
|
||||
{
|
||||
xCAT::MsgUtils->message('E',
|
||||
"Could not update the /etc/exports file.");
|
||||
}
|
||||
else
|
||||
{
|
||||
verbose("Added $exportdir to the /etc/exports file.");
|
||||
$changed_exports++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
verbose("Added $::TFTPDIR to the /etc/exports file.");
|
||||
$changed_exports++;
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# add /install to /etc/exports - if needed
|
||||
#
|
||||
|
||||
$cmd = "/bin/cat /etc/exports | grep '$::INSTALLDIR'";
|
||||
$outref = xCAT::Utils->runcmd("$cmd", -1);
|
||||
if ($::RUNCMD_RC != 0)
|
||||
{
|
||||
|
||||
# ok - then add this entry
|
||||
#SECURITY: this has potential for sharing private host/user keys
|
||||
my $cmd =
|
||||
"/bin/echo '$::INSTALLDIR *(rw,no_root_squash,sync,no_subtree_check)' >> /etc/exports";
|
||||
my $outref = xCAT::Utils->runcmd("$cmd", 0);
|
||||
if ($::RUNCMD_RC != 0)
|
||||
{
|
||||
xCAT::MsgUtils->message('E',
|
||||
"Could not update the /etc/exports file.");
|
||||
}
|
||||
else
|
||||
{
|
||||
verbose("Added $::INSTALLDIR to the /etc/exports file.");
|
||||
$changed_exports++;
|
||||
if (xCAT::SvrUtils->ensure_nfs_export_option($exportdir, 'insecure'))
|
||||
{
|
||||
verbose("Added insecure to existing $exportdir export.");
|
||||
$changed_exports++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1973,8 +1952,8 @@ sub setupLinuxexports
|
||||
{
|
||||
verbose("NFS has been enabled.");
|
||||
}
|
||||
$cmd = "/usr/sbin/exportfs -a";
|
||||
$outref = xCAT::Utils->runcmd("$cmd", 0);
|
||||
my $cmd = "/usr/sbin/exportfs -a";
|
||||
my $outref = xCAT::Utils->runcmd("$cmd", 0);
|
||||
if ($::RUNCMD_RC != 0)
|
||||
{
|
||||
xCAT::MsgUtils->message('E', "Error with $cmd.");
|
||||
|
||||
@@ -205,9 +205,9 @@ base64decode()
|
||||
# must be the last partition on the disk - this forces us to put swap before
|
||||
# the main root partition, and forces us to have a fixed size swap partition
|
||||
#
|
||||
# Also, the autoinstall.yaml file that this needs to be appended to is a
|
||||
# modified cloud-init.conf file, with the top level elided - hence the
|
||||
# storage block below needs to start with no indentation.
|
||||
# Subiquity re-serializes /autoinstall.yaml after the first load,
|
||||
# stripping the autoinstall: wrapper. The storage block is appended
|
||||
# to this unwrapped file and must start at column 0.
|
||||
logger -t $log_label -p "info" "Generate partition file..."
|
||||
if [ -e /tmp/xcat.install_disk ]; then
|
||||
INSTALL_DISK=$(cat /tmp/xcat.install_disk)
|
||||
@@ -220,42 +220,113 @@ fi
|
||||
if [ -d /sys/firmware/efi ]; then
|
||||
cat <<EOF >/tmp/partitionfile
|
||||
storage:
|
||||
version: 1
|
||||
config:
|
||||
- {id: disk-detected, ptable: gpt, path: $INSTALL_DISK, wipe: superblock,
|
||||
preserve: false, name: '', grub_device: true, type: disk}
|
||||
- {id: efi-part, device: disk-detected, size: 512MB, flag: boot,
|
||||
type: partition, preserve: false}
|
||||
- {id: swap-part, device: disk-detected, size: 2GB, flag: swap,
|
||||
type: partition, preserve: false}
|
||||
- {id: root-part, device: disk-detected, size: -1, type: partition,
|
||||
preserve: false}
|
||||
- {id: efi-part-fs, volume: efi-part, type: format, fstype: fat, label: efi}
|
||||
- {id: swap-part-fs, volume: swap-part, type: format, fstype: swap}
|
||||
- {id: root-part-fs, volume: root-part, type: format, fstype: ext4,
|
||||
label: root}
|
||||
- {id: efi-part-mount, device: efi-part-fs, type: mount, path: /boot/efi}
|
||||
- {id: swap-part-mount, device: swap-part-fs, type: mount, path: none}
|
||||
- {id: root-part-mount, device: root-part-fs, type: mount, path: /}
|
||||
- id: disk-detected
|
||||
type: disk
|
||||
ptable: gpt
|
||||
path: $INSTALL_DISK
|
||||
wipe: superblock-recursive
|
||||
preserve: false
|
||||
- id: efi-part
|
||||
type: partition
|
||||
device: disk-detected
|
||||
size: 512M
|
||||
flag: boot
|
||||
number: 1
|
||||
preserve: false
|
||||
grub_device: true
|
||||
- id: swap-part
|
||||
type: partition
|
||||
device: disk-detected
|
||||
size: 2G
|
||||
number: 2
|
||||
preserve: false
|
||||
- id: root-part
|
||||
type: partition
|
||||
device: disk-detected
|
||||
size: -1
|
||||
wipe: superblock
|
||||
number: 3
|
||||
preserve: false
|
||||
- id: efi-part-fs
|
||||
type: format
|
||||
fstype: fat32
|
||||
volume: efi-part
|
||||
preserve: false
|
||||
- id: swap-part-fs
|
||||
type: format
|
||||
fstype: swap
|
||||
volume: swap-part
|
||||
preserve: false
|
||||
- id: root-part-fs
|
||||
type: format
|
||||
fstype: ext4
|
||||
volume: root-part
|
||||
preserve: false
|
||||
- id: efi-part-mount
|
||||
type: mount
|
||||
path: /boot/efi
|
||||
device: efi-part-fs
|
||||
- id: swap-part-mount
|
||||
type: mount
|
||||
path: none
|
||||
device: swap-part-fs
|
||||
- id: root-part-mount
|
||||
type: mount
|
||||
path: /
|
||||
device: root-part-fs
|
||||
EOF
|
||||
else
|
||||
cat <<EOF >/tmp/partitionfile
|
||||
storage:
|
||||
version: 1
|
||||
config:
|
||||
- {id: disk-detected, ptable: msdos, path: $INSTALL_DISK, wipe: superblock,
|
||||
preserve: false, name: '', grub_device: true, type: disk}
|
||||
- {id: boot-part, device: disk-detected, size: 512MB, flag: boot,
|
||||
type: partition, preserve: false}
|
||||
- {id: swap-part, device: disk-detected, size: 2GB, flag: swap,
|
||||
type: partition, preserve: false}
|
||||
- {id: root-part, device: disk-detected, size: -1, type: partition,
|
||||
preserve: false}
|
||||
- {id: boot-part-fs, volume: boot-part, type: format, fstype: ext4, label: boot}
|
||||
- {id: swap-part-fs, volume: swap-part, type: format, fstype: swap}
|
||||
- {id: root-part-fs, volume: root-part, type: format, fstype: ext4,
|
||||
label: root}
|
||||
- {id: boot-part-mount, device: boot-part-fs, type: mount, path: /boot}
|
||||
- {id: swap-part-mount, device: swap-part-fs, type: mount, path: none}
|
||||
- {id: root-part-mount, device: root-part-fs, type: mount, path: /}
|
||||
- id: disk-detected
|
||||
type: disk
|
||||
ptable: gpt
|
||||
path: $INSTALL_DISK
|
||||
wipe: superblock-recursive
|
||||
preserve: false
|
||||
grub_device: true
|
||||
- id: bios-grub
|
||||
type: partition
|
||||
device: disk-detected
|
||||
size: 1M
|
||||
flag: bios_grub
|
||||
number: 1
|
||||
preserve: false
|
||||
- id: swap-part
|
||||
type: partition
|
||||
device: disk-detected
|
||||
size: 2G
|
||||
number: 2
|
||||
preserve: false
|
||||
- id: root-part
|
||||
type: partition
|
||||
device: disk-detected
|
||||
size: -1
|
||||
wipe: superblock
|
||||
number: 3
|
||||
preserve: false
|
||||
- id: swap-part-fs
|
||||
type: format
|
||||
fstype: swap
|
||||
volume: swap-part
|
||||
preserve: false
|
||||
- id: root-part-fs
|
||||
type: format
|
||||
fstype: ext4
|
||||
volume: root-part
|
||||
preserve: false
|
||||
- id: swap-part-mount
|
||||
type: mount
|
||||
path: none
|
||||
device: swap-part-fs
|
||||
- id: root-part-mount
|
||||
type: mount
|
||||
path: /
|
||||
device: root-part-fs
|
||||
EOF
|
||||
fi
|
||||
|
||||
@@ -265,6 +336,5 @@ fi
|
||||
exit 0
|
||||
|
||||
if [ "$XCATDEBUGMODE" = "1" ] || [ "$XCATDEBUGMODE" = "2" ]; then
|
||||
then
|
||||
set +x
|
||||
fi
|
||||
|
||||
@@ -13,65 +13,81 @@ autoinstall:
|
||||
authorized-keys: []
|
||||
install-server: true
|
||||
apt:
|
||||
primary:
|
||||
- arches: [amd64, i386]
|
||||
uri: https://mirror.pnl.gov/ubuntu/
|
||||
fallback: offline-install
|
||||
geoip: false
|
||||
disable_suites:
|
||||
- updates
|
||||
- backports
|
||||
- security
|
||||
kernel:
|
||||
package: linux-generic
|
||||
user-data:
|
||||
hostname: #HOSTNAME#
|
||||
disable_root: false
|
||||
package_update: true
|
||||
package_upgrade: true
|
||||
package_update: false
|
||||
package_upgrade: false
|
||||
timezone: #TABLE:site:key=timezone:value#
|
||||
chpasswd:
|
||||
list:
|
||||
- root:#CRYPT:passwd:key=system,username=root:password#
|
||||
list: |
|
||||
root:#CRYPT:passwd:key=system,username=root:password#
|
||||
expire: false
|
||||
packages:
|
||||
- bash
|
||||
- nfs-common
|
||||
- openssl
|
||||
- isc-dhcp-client
|
||||
- libc-bin
|
||||
- openssh-server
|
||||
- openssh-client
|
||||
- wget
|
||||
- vim
|
||||
- rsync
|
||||
- busybox-static
|
||||
- gawk
|
||||
- dnsutils
|
||||
- chrony
|
||||
- gpg
|
||||
early-commands:
|
||||
- '{
|
||||
echo "Running install disk detection...";
|
||||
wget http://#XCATVAR:XCATMASTER##COLONHTTPPORT#/install/autoinst/getinstdisk;
|
||||
chmod u+x ./getinstdisk;
|
||||
./getinstdisk;
|
||||
echo "Running early_command Installation script...";
|
||||
wget http://#XCATVAR:XCATMASTER##COLONHTTPPORT#/install/autoinst/#HOSTNAME#.pre;
|
||||
chmod u+x #HOSTNAME#.pre;
|
||||
./#HOSTNAME#.pre;
|
||||
echo "Updating storage config...";
|
||||
cat /autoinstall.yaml |head -n -1 > /tmp/autoinstall.yaml;
|
||||
cat /tmp/partitionfile >> /tmp/autoinstall.yaml;
|
||||
cat /tmp/autoinstall.yaml >/autoinstall.yaml;
|
||||
echo "Disabling systemd-resolved...";
|
||||
rm -f /etc/resolv.conf;
|
||||
echo "nameserver #TABLE:noderes:$NODE:xcatmaster#" >/etc/resolv.conf;
|
||||
echo "domain #TABLE:site:key=domain:value#" >>/etc/resolv.conf;
|
||||
} >>/tmp/pre-install.log'
|
||||
- |
|
||||
exec >/tmp/pre-install.log 2>&1
|
||||
set -eux
|
||||
echo "=== xCAT early-commands ==="
|
||||
echo "=== network ==="
|
||||
ip addr show
|
||||
ip route show
|
||||
echo "=== disk detection ==="
|
||||
wget -T 30 -O /tmp/getinstdisk http://#XCATVAR:XCATMASTER##COLONHTTPPORT#/install/autoinst/getinstdisk
|
||||
test -s /tmp/getinstdisk
|
||||
chmod u+x /tmp/getinstdisk
|
||||
/tmp/getinstdisk
|
||||
echo "=== pre-install script ==="
|
||||
wget -T 30 -O /tmp/pre.sh http://#XCATVAR:XCATMASTER##COLONHTTPPORT#/install/autoinst/#HOSTNAME#.pre
|
||||
test -s /tmp/pre.sh
|
||||
chmod u+x /tmp/pre.sh
|
||||
/tmp/pre.sh
|
||||
echo "=== storage injection ==="
|
||||
test -s /tmp/partitionfile
|
||||
sed -i '/^\.\.\.$/d' /autoinstall.yaml
|
||||
cat /tmp/partitionfile >> /autoinstall.yaml
|
||||
echo "=== DNS setup ==="
|
||||
rm -f /etc/resolv.conf
|
||||
echo "nameserver #TABLE:noderes:$NODE:xcatmaster#" >/etc/resolv.conf
|
||||
echo "domain #TABLE:site:key=domain:value#" >>/etc/resolv.conf
|
||||
echo "=== early-commands complete ==="
|
||||
late-commands:
|
||||
- mkdir -p /target/var/log/xcat/
|
||||
- '{
|
||||
cat /tmp/pre-install.log >> /target/var/log/xcat/xcat.log;
|
||||
installnic="#TABLE:noderes:$NODE:installnic#";
|
||||
installmac="#TABLE:mac:$NODE:mac#";
|
||||
installmac="$(printf ''%s'' "${installmac}" | tr ''A-F'' ''a-f'')";
|
||||
mkdir -p /target/etc/netplan;
|
||||
printf ''%s\n'' "network:" " version: 2" " ethernets:" " xcat-install:" " match:" " macaddress: \"${installmac}\"" " set-name: ${installnic}" " dhcp4: true" >/target/etc/netplan/00-xcat-install.yaml;
|
||||
chmod 600 /target/etc/netplan/00-xcat-install.yaml;
|
||||
printf ''%s\n'' ''#HOSTNAME#'' >/target/etc/hostname;
|
||||
if grep -q ''^127\.0\.1\.1'' /target/etc/hosts; then
|
||||
sed -i ''s/^127\.0\.1\.1.*/127.0.1.1 #HOSTNAME#/'' /target/etc/hosts;
|
||||
else
|
||||
printf ''%s\n'' ''127.0.1.1 #HOSTNAME#'' >>/target/etc/hosts;
|
||||
fi;
|
||||
mkdir -p /target/etc/cloud;
|
||||
touch /target/etc/cloud/cloud-init.disabled;
|
||||
echo "Updating kernel command line...";
|
||||
echo "GRUB_CMDLINE_LINUX=\"#TABLE:bootparams:$NODE:kcmdline#\"" >>/target/etc/default/grub;
|
||||
printf ''%s\n'' ''GRUB_CMDLINE_LINUX="#TABLEBLANKOKAY:bootparams:$NODE:kcmdline#"'' >>/target/etc/default/grub;
|
||||
curtin in-target --target /target update-grub2;
|
||||
echo "Running late_command Installation script...";
|
||||
wget http://#XCATVAR:XCATMASTER##COLONHTTPPORT#/install/autoinst/#HOSTNAME#.post;
|
||||
wget -T 30 http://#XCATVAR:XCATMASTER##COLONHTTPPORT#/install/autoinst/#HOSTNAME#.post;
|
||||
chmod u+x #HOSTNAME#.post;
|
||||
cp ./#HOSTNAME#.post /target/root/post.script;
|
||||
curtin in-target --target /target /root/post.script;
|
||||
} >>/target/var/log/xcat/xcat.log 2>&1'
|
||||
error-commands:
|
||||
- tar -c --transform='s/^/#HOSTNAME#-logs\//' /var/crash /var/logs/installer |nc -l 8080
|
||||
- tar -c --transform='s/^/#HOSTNAME#-logs\//' /var/crash /var/log/installer /tmp/pre-install.log /autoinstall.yaml 2>/dev/null |nc -l 8080
|
||||
|
||||
@@ -87,6 +87,8 @@ d-i apt-setup/multiverse boolean false
|
||||
d-i apt-setup/universe boolean false
|
||||
d-i apt-setup/backports boolean false
|
||||
d-i apt-setup/updates boolean false
|
||||
# Keep legacy d-i installs on the local mirror/content only.
|
||||
d-i apt-setup/services-select multiselect
|
||||
|
||||
|
||||
### Boot loader installation
|
||||
@@ -143,8 +145,10 @@ d-i preseed/late_command string \
|
||||
mount -o bind /dev/pts /target/dev/pts -t devpts; \
|
||||
mount -o bind /sys /target/sys; \
|
||||
chroot /target /root/post.script; \
|
||||
if [ -f /target/etc/apt/sources.list ]; then \
|
||||
sed -i -e '/^[[:space:]]*deb[[:space:]].*[[:space:]][^[:space:]]*-\(security\|updates\|backports\)[[:space:]]/s/^/# /' -e '/^[[:space:]]*deb-src[[:space:]].*[[:space:]][^[:space:]]*-\(security\|updates\|backports\)[[:space:]]/s/^/# /' /target/etc/apt/sources.list; \
|
||||
fi; \
|
||||
if [ -f /target/etc/network/interfaces ]; then \
|
||||
cp /target/etc/network/interfaces /etc/network/interfaces; \
|
||||
fi; \
|
||||
} >>/target/var/log/xcat/xcat.log 2>&1
|
||||
|
||||
|
||||
@@ -40,7 +40,10 @@ then
|
||||
sed -i 's/^X11Forwarding .*$/X11Forwarding yes/' $ROOTDIR/etc/ssh/sshd_config
|
||||
sed -i 's/^KeyRegenerationInterval .*$/KeyRegenerationInterval 0/' $ROOTDIR/etc/ssh/sshd_config
|
||||
sed -i 's/\(.*MaxStartups.*\)/#\1/' $ROOTDIR/etc/ssh/sshd_config
|
||||
echo "MaxStartups 1024" >>$ROOTDIR/etc/ssh/sshd_config
|
||||
# Keep the unauthenticated connection limit below systemd's default
|
||||
# 1024 soft open-file limit. Ubuntu 22.04 OpenSSH can otherwise spin
|
||||
# in ppoll() before sending the SSH banner.
|
||||
echo "MaxStartups 100:30:200" >>$ROOTDIR/etc/ssh/sshd_config
|
||||
fi
|
||||
|
||||
if [ -r $ROOTDIR/etc/ssh/sshd_config ]
|
||||
@@ -77,4 +80,3 @@ chmod 600 $ROOTDIR/root/.ssh/config
|
||||
#chroot $ROOTDIR ssh-keygen -y -f /root/.ssh/id_rsa >/root/.ssh/id_rsa.pub
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -16,3 +16,5 @@ gzip
|
||||
xz-utils
|
||||
cpio
|
||||
chrony
|
||||
dracut
|
||||
dracut-network
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
bash
|
||||
nfs-common
|
||||
openssl
|
||||
isc-dhcp-client
|
||||
libc-bin
|
||||
linux-image-generic
|
||||
openssh-server
|
||||
openssh-client
|
||||
wget
|
||||
rsync
|
||||
busybox-static
|
||||
gawk
|
||||
bind9-dnsutils
|
||||
tar
|
||||
gzip
|
||||
xz-utils
|
||||
cpio
|
||||
chrony
|
||||
iproute2
|
||||
dracut
|
||||
dracut-network
|
||||
@@ -0,0 +1,21 @@
|
||||
bash
|
||||
nfs-common
|
||||
openssl
|
||||
isc-dhcp-client
|
||||
libc-bin
|
||||
linux-image-generic
|
||||
openssh-server
|
||||
openssh-client
|
||||
wget
|
||||
rsync
|
||||
busybox-static
|
||||
gawk
|
||||
bind9-dnsutils
|
||||
tar
|
||||
gzip
|
||||
xz-utils
|
||||
cpio
|
||||
chrony
|
||||
iproute2
|
||||
dracut
|
||||
dracut-network
|
||||
@@ -1,7 +1,11 @@
|
||||
#!/bin/sh
|
||||
echo $drivers
|
||||
dracut_install wget tar cpio gzip dash modprobe touch echo cut wc xz
|
||||
dracut_install grep ifconfig hostname awk egrep grep dirname expr
|
||||
dracut_install grep hostname awk egrep dirname expr ip sed tr cat
|
||||
dracut_install mount.nfs
|
||||
inst "$moddir/xcatroot" "/sbin/xcatroot"
|
||||
inst_hook cmdline 10 "$moddir/xcat-cmdline.sh"
|
||||
# install netroot dispatcher if available (dracut 051+ with network-legacy)
|
||||
if [ -f "$moddir/../40network/netroot.sh" ]; then
|
||||
inst "$moddir/../40network/netroot.sh" "/sbin/netroot"
|
||||
fi
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
echo $drivers
|
||||
dracut_install wget cpio gzip dash modprobe wc touch echo cut
|
||||
dracut_install grep ifconfig hostname awk egrep grep dirname expr
|
||||
dracut_install wget cpio gzip dash modprobe wc touch echo cut
|
||||
dracut_install grep hostname awk egrep dirname expr
|
||||
inst_hook pre-pivot 5 "$moddir/xcat-prepivot.sh"
|
||||
|
||||
14
xCAT-server/share/xcat/netboot/ubuntu/dracut/module-setup.sh
Normal file
14
xCAT-server/share/xcat/netboot/ubuntu/dracut/module-setup.sh
Normal file
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
check() {
|
||||
return 0
|
||||
}
|
||||
|
||||
depends() {
|
||||
echo network
|
||||
return 0
|
||||
}
|
||||
|
||||
install() {
|
||||
. "$moddir/install"
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
root=1
|
||||
rootok=1
|
||||
netroot=xcat
|
||||
echo '[ -e $NEWROOT/proc ]' > /initqueue-finished/xcatroot.sh
|
||||
for HDIR in "" "/lib/dracut/hooks"; do
|
||||
[ -d "${HDIR}/initqueue-finished" ] || continue
|
||||
echo '[ -e $NEWROOT/proc ]' > ${HDIR}/initqueue-finished/xcatroot.sh
|
||||
done
|
||||
|
||||
@@ -83,7 +83,10 @@ if [ -d "$NEWROOT/etc/sysconfig" -a ! -e "$NEWROOT/etc/sysconfig/selinux" ]; the
|
||||
echo "SELINUX=disabled" >> "$NEWROOT/etc/sysconfig/selinux"
|
||||
fi
|
||||
|
||||
# inject new exit_if_exists
|
||||
echo 'settle_exit_if_exists="--exit-if-exists=/dev/root"; rm "$job"' > /initqueue/xcat.sh
|
||||
# force udevsettle to break
|
||||
> /initqueue/work
|
||||
# signal dracut to proceed with switch_root
|
||||
[ -e /dev/root ] || ln -s null /dev/root
|
||||
for HDIR in "" "/lib/dracut/hooks"; do
|
||||
[ -d "${HDIR}/initqueue" ] || continue
|
||||
echo 'settle_exit_if_exists="--exit-if-exists=/dev/root"; rm -f -- "$job"' > ${HDIR}/initqueue/xcat.sh
|
||||
> ${HDIR}/initqueue/work
|
||||
done
|
||||
|
||||
@@ -181,9 +181,14 @@ cd /
|
||||
|
||||
if [ -z $STATEMNT ]; then
|
||||
for lf in /tmp/dhclient.*.lease; do
|
||||
[ -e "$lf" ] || continue
|
||||
netif=${lf#*.}
|
||||
netif=${netif%.*}
|
||||
cp $lf "$NEWROOT/var/lib/dhclient/dhclient-$netif.leases"
|
||||
if [ -d "$NEWROOT/var/lib/dhclient" ]; then
|
||||
cp $lf "$NEWROOT/var/lib/dhclient/dhclient-$netif.leases"
|
||||
elif [ -d "$NEWROOT/var/lib/dhcp" ]; then
|
||||
cp $lf "$NEWROOT/var/lib/dhcp/dhclient.$netif.leases"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ! -z "$ifname" ]; then
|
||||
@@ -191,31 +196,58 @@ if [ -z $STATEMNT ]; then
|
||||
ETHX=${ifname%:$MACX*}
|
||||
elif [ ! -z "$netdev" ]; then
|
||||
ETHX=$netdev
|
||||
MACX=`ip link show $netdev | grep ether | awk '{print $2}'`
|
||||
if [ -e "/sys/class/net/$netdev/address" ]; then
|
||||
MACX=$(cat /sys/class/net/$netdev/address)
|
||||
else
|
||||
MACX=$(ip link show $netdev 2>/dev/null | grep ether | awk '{print $2}')
|
||||
fi
|
||||
elif [ ! -z "$BOOTIF" ]; then
|
||||
MACX=$BOOTIF
|
||||
#ETHX=`ifconfig |grep -i $BOOTIF | awk '{print $1}'`
|
||||
ETHX=` ip -oneline link show |grep -i $BOOTIF|awk -F ':' '{print $2}'|grep -o "[^ ]\+\( \+[^ ]\+\)*"`
|
||||
MACX=$(echo $BOOTIF | sed 's/^01-//;s/-/:/g' | tr 'A-Z' 'a-z')
|
||||
for devpath in /sys/class/net/*; do
|
||||
[ -e "$devpath/address" ] || continue
|
||||
if [ "$(cat $devpath/address)" = "$MACX" ]; then
|
||||
ETHX=${devpath##*/}
|
||||
break
|
||||
fi
|
||||
done
|
||||
[ -z "$ETHX" ] && ETHX=$(ip -oneline link show | grep -i "$MACX" | awk -F ':' '{print $2}' | grep -o "[^ ]\+\( \+[^ ]\+\)*")
|
||||
fi
|
||||
|
||||
if [ ! -z "$MACX" ] && [ ! -z "$ETHX" ]; then
|
||||
if [ ! -e $NEWROOT/etc/sysconfig/network-scripts/ifcfg-$ETHX ]; then
|
||||
touch $NEWROOT/etc/sysconfig/network-scripts/ifcfg-$ETHX
|
||||
if [ -x $NEWROOT/usr/sbin/netplan ]; then
|
||||
mkdir -p $NEWROOT/etc/netplan
|
||||
cat > $NEWROOT/etc/netplan/90-xcat.yaml <<XCATNETPLAN
|
||||
network:
|
||||
version: 2
|
||||
ethernets:
|
||||
$ETHX:
|
||||
dhcp4: true
|
||||
match:
|
||||
macaddress: "$MACX"
|
||||
XCATNETPLAN
|
||||
elif [ -d $NEWROOT/etc/sysconfig/network-scripts ]; then
|
||||
echo "DEVICE=$ETHX" > $NEWROOT/etc/sysconfig/network-scripts/ifcfg-$ETHX
|
||||
echo "BOOTPROTO=dhcp" >> $NEWROOT/etc/sysconfig/network-scripts/ifcfg-$ETHX
|
||||
echo "HWADDR=$MACX" >> $NEWROOT/etc/sysconfig/network-scripts/ifcfg-$ETHX
|
||||
echo "ONBOOT=yes" >> $NEWROOT/etc/sysconfig/network-scripts/ifcfg-$ETHX
|
||||
elif [ -d $NEWROOT/etc/network ]; then
|
||||
echo "auto $ETHX" >> $NEWROOT/etc/network/interfaces
|
||||
echo "iface $ETHX inet dhcp" >> $NEWROOT/etc/network/interfaces
|
||||
fi
|
||||
echo "DEVICE=$ETHX" > $NEWROOT/etc/sysconfig/network-scripts/ifcfg-$ETHX
|
||||
echo "BOOTPROTO=dhcp" >> $NEWROOT/etc/sysconfig/network-scripts/ifcfg-$ETHX
|
||||
echo "HWADDR=$MACX" >> $NEWROOT/etc/sysconfig/network-scripts/ifcfg-$ETHX
|
||||
echo "ONBOOT=yes" >> $NEWROOT/etc/sysconfig/network-scripts/ifcfg-$ETHX
|
||||
fi
|
||||
fi
|
||||
|
||||
cp /etc/resolv.conf "$NEWROOT/etc/"
|
||||
|
||||
|
||||
if [ -d "$NEWROOT/etc/sysconfig" -a ! -e "$NEWROOT/etc/sysconfig/selinux" ]; then
|
||||
echo "SELINUX=disabled" >> "$NEWROOT/etc/sysconfig/selinux"
|
||||
fi
|
||||
|
||||
# inject new exit_if_exists
|
||||
echo 'settle_exit_if_exists="--exit-if-exists=/dev/root"; rm "$job"' > /initqueue/xcat.sh
|
||||
# force udevsettle to break
|
||||
> /initqueue/work
|
||||
# signal dracut to proceed with switch_root
|
||||
[ -e /dev/root ] || ln -s null /dev/root
|
||||
for HDIR in "" "/lib/dracut/hooks"; do
|
||||
[ -d "${HDIR}/initqueue" ] || continue
|
||||
echo 'settle_exit_if_exists="--exit-if-exists=/dev/root"; rm -f -- "$job"' > ${HDIR}/initqueue/xcat.sh
|
||||
> ${HDIR}/initqueue/work
|
||||
done
|
||||
|
||||
@@ -357,7 +357,7 @@ unless ($onlyinitrd) {
|
||||
open($aptconfig, ">", "$rootimg_dir/etc/apt/sources.list");
|
||||
|
||||
if ($srcdir) {
|
||||
print $aptconfig "deb http://$masternode:$httpport$srcdir $dist main\n";
|
||||
print $aptconfig "deb http://$masternode:$httpport$srcdir $dist main restricted universe\n";
|
||||
}
|
||||
|
||||
foreach (@pkgdir_internet) {
|
||||
@@ -369,9 +369,19 @@ unless ($onlyinitrd) {
|
||||
|
||||
close($aptconfig);
|
||||
|
||||
# Prevent package maintainer scripts from starting services while the
|
||||
# image root is mounted as a chroot.
|
||||
fake_policy_rc_d();
|
||||
|
||||
$rc = system("$aptgetcmd");
|
||||
if ($rc) {
|
||||
xdie "Failed to update package metadata\n";
|
||||
}
|
||||
|
||||
# run apt-get upgrade to update any installed debs
|
||||
my $aptgetcmd_update = $aptgetcmd . "&&" . $aptgetcmdby . " upgrade ";
|
||||
$rc = system("$aptgetcmd_update");
|
||||
if (!$noupdate) {
|
||||
$rc = system("$aptgetcmdby upgrade");
|
||||
}
|
||||
|
||||
# Start to install pkgs in pkglist
|
||||
unless ($imagename) {
|
||||
@@ -463,6 +473,9 @@ unless ($onlyinitrd) {
|
||||
}
|
||||
my $aptgetcmd_install = $aptgetcmdby . " install --no-install-recommends " . $kernelimage;
|
||||
$rc = system("$aptgetcmd_install");
|
||||
if ($rc) {
|
||||
xdie "Failed to install kernel package $kernelimage\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -713,7 +726,9 @@ my @moddeps = <$moddeps>;
|
||||
my @checkdeps = @ndrivers;
|
||||
while (scalar @checkdeps) {
|
||||
my $driver = pop @checkdeps;
|
||||
my @lines = grep /\/$driver:/, @moddeps;
|
||||
my $driver_match = quotemeta($driver);
|
||||
$driver_match =~ s/\\\.ko$/\\.ko(?:\\.(?:gz|xz|zst))?/;
|
||||
my @lines = grep /\/$driver_match:/, @moddeps;
|
||||
foreach (@lines) {
|
||||
chomp;
|
||||
s/.*://;
|
||||
@@ -722,6 +737,7 @@ while (scalar @checkdeps) {
|
||||
my $dep;
|
||||
foreach $dep (@deps) {
|
||||
$dep =~ s/.*\///;
|
||||
$dep =~ s/\.(?:gz|xz|zst)$//;
|
||||
unless (grep { $_ eq $dep } @ndrivers) { #only add if not added
|
||||
unshift(@checkdeps, $dep); #recursively check dependencies
|
||||
unshift(@ndrivers, $dep);
|
||||
@@ -731,18 +747,12 @@ while (scalar @checkdeps) {
|
||||
}
|
||||
}
|
||||
close($moddeps);
|
||||
if (-d "$rootimg_dir/usr/share/dracut") {
|
||||
if (-d "$rootimg_dir/usr/share/dracut" or -d "$rootimg_dir/usr/lib/dracut") {
|
||||
$dracutmode = 1;
|
||||
|
||||
# get dracut version
|
||||
my $dracutver = `chroot $rootimg_dir dpkg-query -W dracut | awk '{print \$2}'| aek -F- {print \$1}'`;
|
||||
if ($dracutver =~ /^\d\d\d$/) {
|
||||
if ($dracutver >= "009") {
|
||||
$dracutdir = "dracut_009";
|
||||
} else {
|
||||
$dracutdir = "dracut"; # The default directory
|
||||
}
|
||||
}
|
||||
my $dracutver = `chroot $rootimg_dir dpkg-query -W dracut 2>/dev/null | awk '{print \$2}'`;
|
||||
chomp($dracutver);
|
||||
$dracutdir = "dracut";
|
||||
print "Enter the dracut mode. Dracut version: $dracutver. Dracut directory: $dracutdir.\n";
|
||||
}
|
||||
|
||||
@@ -896,7 +906,7 @@ sub getlibs {
|
||||
foreach (@libs) {
|
||||
# vdso is a shared library that's embedded in the kernel
|
||||
# automatically loaded whenever a new process is exec-ed
|
||||
if ( $_ =~ /linux-vdso64/ ) {
|
||||
if ( $_ =~ /linux-vdso/ ) {
|
||||
next;
|
||||
}
|
||||
if (/statically linked/ or /not a dynamic executable/) {
|
||||
@@ -922,12 +932,22 @@ sub getlibs {
|
||||
|
||||
sub mkinitrd_dracut {
|
||||
my ($mode) = @_; # the mode is for statelite or stateless
|
||||
my $dracutmpath = "$rootimg_dir/usr/share/dracut/modules.d/97xcat";
|
||||
my $dracutmpath;
|
||||
if (-d "$rootimg_dir/usr/lib/dracut/modules.d") {
|
||||
$dracutmpath = "$rootimg_dir/usr/lib/dracut/modules.d/97xcat";
|
||||
} else {
|
||||
$dracutmpath = "$rootimg_dir/usr/share/dracut/modules.d/97xcat";
|
||||
}
|
||||
mkpath($dracutmpath);
|
||||
|
||||
my $perm = (stat("$fullpath/$dracutdir/check"))[2];
|
||||
cp("$fullpath/$dracutdir/check", $dracutmpath);
|
||||
chmod($perm & 07777, "$dracutmpath/check");
|
||||
if (-f "$fullpath/$dracutdir/module-setup.sh") {
|
||||
$perm = (stat("$fullpath/$dracutdir/module-setup.sh"))[2];
|
||||
cp("$fullpath/$dracutdir/module-setup.sh", $dracutmpath);
|
||||
chmod($perm & 07777, "$dracutmpath/module-setup.sh");
|
||||
}
|
||||
|
||||
foreach (@ndrivers) { s/\.ko$//; }
|
||||
|
||||
@@ -945,9 +965,13 @@ sub mkinitrd_dracut {
|
||||
$perm = (stat("$fullpath/$dracutdir/xcat-prepivot.sh"))[2];
|
||||
chmod($perm & 07777, "$dracutmpath/xcat-prepivot.sh");
|
||||
|
||||
# update etc/dracut.conf
|
||||
# update etc/dracut.conf - use network-legacy if 40network requires wicked
|
||||
my $netmod = "network";
|
||||
if (-d "$dracutmpath/../35network-legacy" && ! -x "$rootimg_dir/usr/sbin/wicked") {
|
||||
$netmod = "network-legacy";
|
||||
}
|
||||
open($DRACUTCONF, '>', "$rootimg_dir/etc/dracut.conf");
|
||||
print $DRACUTCONF qq{dracutmodules+=" xcat nfs base network kernel-modules "\n};
|
||||
print $DRACUTCONF qq{dracutmodules+=" xcat nfs base $netmod kernel-modules "\n};
|
||||
print $DRACUTCONF qq{add_drivers+=" $add_drivers "\n};
|
||||
print $DRACUTCONF qq{filesystems+=" nfs "\n};
|
||||
close $DRACUTCONF;
|
||||
@@ -975,9 +999,13 @@ sub mkinitrd_dracut {
|
||||
$perm = (stat("$fullpath/$dracutdir/installkernel"))[2];
|
||||
chmod($perm & 07777, "$dracutmpath/installkernel");
|
||||
|
||||
# update etc/dracut.conf
|
||||
# update etc/dracut.conf - use network-legacy if 40network requires wicked
|
||||
my $netmod = "network";
|
||||
if (-d "$dracutmpath/../35network-legacy" && ! -x "$rootimg_dir/usr/sbin/wicked") {
|
||||
$netmod = "network-legacy";
|
||||
}
|
||||
open($DRACUTCONF, '>', "$rootimg_dir/etc/dracut.conf");
|
||||
print $DRACUTCONF qq{dracutmodules+=" xcat nfs base network kernel-modules "\n};
|
||||
print $DRACUTCONF qq{dracutmodules+=" xcat nfs base $netmod kernel-modules "\n};
|
||||
print $DRACUTCONF qq{add_drivers+=" $add_drivers "\n};
|
||||
close $DRACUTCONF;
|
||||
} else {
|
||||
@@ -1152,7 +1180,7 @@ EOS1
|
||||
#print $inifile "mknod /dev/hvc7 c 229 7\n";
|
||||
|
||||
foreach (@ndrivers) {
|
||||
print $inifile "insmod /lib/$_\n";
|
||||
print $inifile "[ -f /lib/$_ ] && insmod /lib/$_\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -1188,13 +1216,24 @@ PRINIC=$prinic
|
||||
NODESTATUS='y'
|
||||
XCATIPORT="3002"
|
||||
|
||||
iface_exists() {
|
||||
[ -n "\$1" ] && [ -d "/sys/class/net/\$1" ]
|
||||
}
|
||||
|
||||
for i in `cat /proc/cmdline`; do
|
||||
KEY=`echo \$i |awk -F= '{print \$1}'`
|
||||
if [ "\$KEY" == 'netdev' ]; then
|
||||
NETDEV=`echo \$i |awk -F= '{print \$2}'`
|
||||
elif [ "\$KEY" == 'BOOTIF' ]; then
|
||||
VALUE=`echo \$i |awk -F= '{print \$2}'|sed -e s/^01-// -e s/-/:/g`
|
||||
BOOTIF=`ifconfig -a|grep -i "hwaddr \$VALUE"|awk '{print \$1}'`
|
||||
VALUE=`echo \$i |awk -F= '{print \$2}'|sed -e s/^01-// -e s/-/:/g|tr 'A-Z' 'a-z'`
|
||||
for devpath in /sys/class/net/*; do
|
||||
[ -e "\$devpath/address" ] || continue
|
||||
if [ "\`cat \$devpath/address\`" = "\$VALUE" ]; then
|
||||
BOOTIF=\${devpath##*/}
|
||||
break
|
||||
fi
|
||||
done
|
||||
[ -z "\$BOOTIF" ] && BOOTIF=`ifconfig -a|grep -i "hwaddr \$VALUE"|awk '{print \$1}'`
|
||||
elif [ "\$KEY" == 'XCAT' ]; then
|
||||
VALUE=`echo \$i |awk -F= '{print \$2}'`
|
||||
# format: XCAT=xcatmaster:3001
|
||||
@@ -1215,16 +1254,23 @@ for i in `cat /proc/cmdline`; do
|
||||
done
|
||||
|
||||
if [ -z "\$IFACE" ]; then
|
||||
if [ ! -z "\$NETDEV" ]; then
|
||||
IFACE=\$NETDEV
|
||||
elif [ ! -z "\$BOOTIF" ]; then
|
||||
if iface_exists "\$BOOTIF"; then
|
||||
IFACE=\$BOOTIF
|
||||
elif [ ! -z "\$PRINIC" ]; then
|
||||
elif iface_exists "\$NETDEV"; then
|
||||
IFACE=\$NETDEV
|
||||
elif iface_exists "\$PRINIC"; then
|
||||
IFACE=\$PRINIC
|
||||
else
|
||||
echo "\${RED}Couldn't find the proper booting device, switch to shell...\${RESET}"
|
||||
shell
|
||||
exit
|
||||
for devpath in /sys/class/net/*; do
|
||||
IFACE=\${devpath##*/}
|
||||
[ "\$IFACE" != "lo" ] && break
|
||||
IFACE=
|
||||
done
|
||||
if [ -z "\$IFACE" ]; then
|
||||
echo "\${RED}Couldn't find the proper booting device, switch to shell...\${RESET}"
|
||||
shell
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -1666,9 +1712,10 @@ EOMS
|
||||
}
|
||||
|
||||
# add extra commands for initrd
|
||||
foreach ("usr/bin/dig", "bin/busybox", "bin/bash", "sbin/mount.nfs", "usr/bin/rsync", "sbin/insmod", "sbin/udevadm", "sbin/modprobe", "sbin/blkid", "sbin/depmod", "usr/bin/wget", "usr/bin/xz", "bin/gzip", "bin/tar") {
|
||||
getlibs($_);
|
||||
push @filestoadd, $_;
|
||||
foreach my $dest ("usr/bin/dig", "bin/busybox", "bin/bash", "sbin/mount.nfs", "usr/bin/rsync", "sbin/insmod", "sbin/udevadm", "sbin/modprobe", "sbin/blkid", "sbin/depmod", "usr/bin/wget", "usr/bin/xz", "bin/gzip", "bin/tar") {
|
||||
my $src = find_rootimg_file($dest);
|
||||
getlibs($src);
|
||||
push @filestoadd, [ $src, $dest ];
|
||||
}
|
||||
|
||||
# Workaround for Ubuntu 18.04 busybox bug
|
||||
@@ -1691,16 +1738,16 @@ EOMS
|
||||
|
||||
|
||||
if ($arch =~ /x86_64/) {
|
||||
push @filestoadd, "lib64/libnss_dns.so.2";
|
||||
#the path of libnss_dns.so.2 for ubuntu 14.4+
|
||||
push @filestoadd, "lib/x86_64-linux-gnu/libnss_dns.so.2";
|
||||
push @filestoadd, "lib64/libresolv.so.2";
|
||||
foreach ("lib64/libnss_dns.so.2", "lib/x86_64-linux-gnu/libnss_dns.so.2", "lib64/libresolv.so.2") {
|
||||
push @filestoadd, $_ if (-e "$rootimg_dir/$_");
|
||||
}
|
||||
} elsif ($arch =~ /ppc64el/) {
|
||||
push @filestoadd, "lib/powerpc64le-linux-gnu/libnss_files.so.2";
|
||||
push @filestoadd, "lib/powerpc64le-linux-gnu/libnss_dns.so.2";
|
||||
foreach ("lib/powerpc64le-linux-gnu/libnss_files.so.2", "lib/powerpc64le-linux-gnu/libnss_dns.so.2") {
|
||||
push @filestoadd, $_ if (-e "$rootimg_dir/$_");
|
||||
}
|
||||
|
||||
} else {
|
||||
push @filestoadd, "lib/libnss_dns.so.2";
|
||||
push @filestoadd, "lib/libnss_dns.so.2" if (-e "$rootimg_dir/lib/libnss_dns.so.2");
|
||||
}
|
||||
push @filestoadd, keys %libhash;
|
||||
|
||||
@@ -1716,9 +1763,7 @@ EOMS
|
||||
} elsif (-f "$pathtofiles/" . $_->[0]) {
|
||||
$srcpath = "$pathtofiles/" . $_->[0];
|
||||
}
|
||||
mkpath(dirname("/tmp/xcatinitrd.$$/" . $_->[1]));
|
||||
copy($srcpath, "/tmp/xcatinitrd.$$/" . $_->[1]);
|
||||
chmod 0755, "/tmp/xcatinitrd.$$/" . $_->[1];
|
||||
copy_initrd_file($srcpath, "/tmp/xcatinitrd.$$/" . $_->[1]);
|
||||
} else {
|
||||
|
||||
#print "$_\n";
|
||||
@@ -1728,9 +1773,7 @@ EOMS
|
||||
} elsif (-f "$pathtofiles/$_") {
|
||||
$srcpath = "$pathtofiles/$_";
|
||||
}
|
||||
mkpath(dirname("/tmp/xcatinitrd.$$/$_"));
|
||||
copy("$srcpath", "/tmp/xcatinitrd.$$/$_");
|
||||
chmod 0755, "/tmp/xcatinitrd.$$/" . $_;
|
||||
copy_initrd_file($srcpath, "/tmp/xcatinitrd.$$/$_");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1743,7 +1786,7 @@ EOMS
|
||||
system("cp $rootimg_dir/lib/modules/$kernelver/modules.order /tmp/xcatinitrd.$$/lib/modules/$kernelver/modules.order");
|
||||
}
|
||||
|
||||
system("chroot /tmp/xcatinitrd.$$/ depmod $kernelver");
|
||||
system("chroot /tmp/xcatinitrd.$$/ /sbin/depmod $kernelver");
|
||||
|
||||
# Copy udev and network scripts into initrd for s390x, which also works for other platforms
|
||||
# udev
|
||||
@@ -1789,7 +1832,9 @@ sub isaptdir {
|
||||
|
||||
sub isnetdriver {
|
||||
foreach (@ndrivers) {
|
||||
if ($File::Find::name =~ /\/$_/) {
|
||||
my $driver_match = quotemeta($_);
|
||||
$driver_match =~ s/\\\.ko$/\\.ko(?:\\.(?:gz|xz|zst))?/;
|
||||
if ($File::Find::name =~ /\/$driver_match$/) {
|
||||
my $filetoadd = $File::Find::name;
|
||||
$filetoadd =~ s!$rootimg_dir/!!;
|
||||
push @filestoadd, [ $filetoadd, "lib/$_" ];
|
||||
@@ -1797,6 +1842,41 @@ sub isnetdriver {
|
||||
}
|
||||
}
|
||||
|
||||
sub find_rootimg_file {
|
||||
my ($file) = @_;
|
||||
|
||||
return $file if (-e "$rootimg_dir/$file");
|
||||
|
||||
my $base = basename($file);
|
||||
foreach my $candidate ("usr/bin/$base", "bin/$base", "usr/sbin/$base", "sbin/$base") {
|
||||
return $candidate if (-e "$rootimg_dir/$candidate");
|
||||
}
|
||||
|
||||
xdie "Failed to find $file in $rootimg_dir\n";
|
||||
}
|
||||
|
||||
sub copy_initrd_file {
|
||||
my ( $src, $dest ) = @_;
|
||||
|
||||
mkpath(dirname($dest));
|
||||
if ( $src =~ /\.ko\.(gz|xz|zst)$/ ) {
|
||||
my %decompressor = (
|
||||
gz => [ 'gzip', '-dc' ],
|
||||
xz => [ 'xz', '-dc' ],
|
||||
zst => [ 'zstd', '-dc' ],
|
||||
);
|
||||
my @cmd = ( @{ $decompressor{$1} }, $src );
|
||||
open( my $in, '-|', @cmd ) || xdie "Failed to decompress $src\n";
|
||||
open( my $out, '>', $dest ) || xdie "Failed to open $dest\n";
|
||||
copy( $in, $out ) || xdie "Failed to copy $src to $dest\n";
|
||||
close($out);
|
||||
close($in) || xdie "Failed to decompress $src\n";
|
||||
} else {
|
||||
copy( $src, $dest ) || xdie "Failed to copy $src to $dest\n";
|
||||
}
|
||||
chmod 0755, $dest;
|
||||
}
|
||||
|
||||
sub postscripts {
|
||||
generic_post();
|
||||
|
||||
@@ -2187,5 +2267,3 @@ sub usage {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
42
xCAT-test/unit/copycds_packages_integrity.t
Normal file
42
xCAT-test/unit/copycds_packages_integrity.t
Normal file
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
use Test::More;
|
||||
use File::Find;
|
||||
|
||||
# This test verifies that copycds does not leave empty Packages files
|
||||
# alongside valid Packages.gz files. An empty Packages file causes
|
||||
# apt's cdrom handler to fail with a hash mismatch during autoinstall.
|
||||
|
||||
my $installdir = $ENV{INSTALLDIR} || '/install';
|
||||
|
||||
plan skip_all => "$installdir not found" unless -d $installdir;
|
||||
|
||||
my @empty_packages;
|
||||
my @ok_packages;
|
||||
|
||||
find(sub {
|
||||
return unless $_ eq 'Packages';
|
||||
return unless -f $File::Find::name;
|
||||
|
||||
my $gz = "$File::Find::name.gz";
|
||||
if (-s $File::Find::name == 0 && -f $gz && -s $gz > 0) {
|
||||
push @empty_packages, $File::Find::name;
|
||||
} elsif (-s $File::Find::name > 0) {
|
||||
push @ok_packages, $File::Find::name;
|
||||
}
|
||||
}, "$installdir");
|
||||
|
||||
if (@empty_packages || @ok_packages) {
|
||||
is(scalar @empty_packages, 0,
|
||||
'no empty Packages files alongside valid Packages.gz')
|
||||
or diag("Empty Packages files found:\n " . join("\n ", @empty_packages));
|
||||
|
||||
cmp_ok(scalar @ok_packages, '>', 0,
|
||||
'at least one non-empty Packages file exists')
|
||||
if @ok_packages;
|
||||
} else {
|
||||
plan skip_all => "no apt repo trees found under $installdir";
|
||||
}
|
||||
|
||||
done_testing();
|
||||
@@ -49,6 +49,12 @@ is(
|
||||
'Ubuntu 22.04 defaults to Kea'
|
||||
);
|
||||
|
||||
is(
|
||||
xCAT::DHCP::Backend->default_backend( platform => '', os => 'ubuntu22.04.5', os_name => 'ubuntu', version => '22.04.5' ),
|
||||
'kea',
|
||||
'Ubuntu 22.04 point releases default to Kea'
|
||||
);
|
||||
|
||||
is(
|
||||
xCAT::DHCP::Backend->default_backend( platform => '', os => 'ubuntu20.04', os_name => 'ubuntu', version => '20.04' ),
|
||||
'isc',
|
||||
|
||||
@@ -76,7 +76,8 @@ is_deeply( $config->{Dhcp4}{'interfaces-config'}{interfaces}, ['eth0'], 'interfa
|
||||
is( $config->{Dhcp4}{'valid-lifetime'}, 600, 'valid lifetime is rendered' );
|
||||
is( $config->{Dhcp4}{'lease-database'}{type}, 'memfile', 'memfile lease backend is the default' );
|
||||
is( $config->{Dhcp4}{'reservations-in-subnet'}, JSON::true, 'subnet host reservations are enabled by default' );
|
||||
is( $config->{Dhcp4}{'reservations-out-of-pool'}, JSON::true, 'out-of-pool host reservations are enabled by default' );
|
||||
is( $config->{Dhcp4}{'reservations-out-of-pool'}, JSON::true, 'out-of-pool host reservations are enabled for xCAT static addresses' );
|
||||
is( $config->{Dhcp4}{'match-client-id'}, JSON::false, 'DHCPv4 leases match MAC reservations when client-id changes across boot stages' );
|
||||
|
||||
my $subnet = $config->{Dhcp4}{subnet4}[0];
|
||||
is( $subnet->{id}, 1, 'subnet id is rendered' );
|
||||
@@ -191,8 +192,9 @@ is( $empty_boot_subnet->{'boot-file-name'}, '', 'empty boot-file-name is preserv
|
||||
|
||||
my $reservation_policy_json = $backend->render_dhcp4_config(
|
||||
{
|
||||
'reservations-in-subnet' => 1,
|
||||
'reservations-out-of-pool' => 1,
|
||||
'reservations-in-subnet' => 0,
|
||||
'reservations-out-of-pool' => 0,
|
||||
'match-client-id' => 1,
|
||||
subnets => [
|
||||
{
|
||||
id => 9,
|
||||
@@ -203,8 +205,9 @@ my $reservation_policy_json = $backend->render_dhcp4_config(
|
||||
}
|
||||
);
|
||||
my $reservation_policy_config = decode_json($reservation_policy_json);
|
||||
is( $reservation_policy_config->{Dhcp4}{'reservations-in-subnet'}, JSON::true, 'reservation in-subnet policy can be overridden' );
|
||||
is( $reservation_policy_config->{Dhcp4}{'reservations-out-of-pool'}, JSON::true, 'reservation out-of-pool policy can be overridden' );
|
||||
is( $reservation_policy_config->{Dhcp4}{'reservations-in-subnet'}, JSON::false, 'reservation in-subnet policy can be overridden' );
|
||||
is( $reservation_policy_config->{Dhcp4}{'reservations-out-of-pool'}, JSON::false, 'reservation out-of-pool policy can be overridden' );
|
||||
is( $reservation_policy_config->{Dhcp4}{'match-client-id'}, JSON::true, 'client-id lease matching policy can be overridden' );
|
||||
|
||||
my $comment_dir = tempdir(CLEANUP => 1);
|
||||
my $commented_config = "$comment_dir/kea-dhcp4.conf";
|
||||
|
||||
43
xCAT-test/unit/dhcp_omshell_parse.t
Normal file
43
xCAT-test/unit/dhcp_omshell_parse.t
Normal file
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../../xCAT-server/lib";
|
||||
use lib "$FindBin::Bin/../../xCAT-server/lib/perl";
|
||||
use lib "$FindBin::Bin/../../perl-xCAT";
|
||||
|
||||
use Test::More;
|
||||
|
||||
$ENV{XCATCFG} ||= 'SQLite:/tmp';
|
||||
|
||||
my $source_dhcp_plugin = "$FindBin::Bin/../../xCAT-server/lib/xcat/plugins/dhcp.pm";
|
||||
if ( -f $source_dhcp_plugin ) {
|
||||
require $source_dhcp_plugin;
|
||||
} else {
|
||||
require xCAT_plugin::dhcp;
|
||||
}
|
||||
|
||||
my ($name, $ip, $mac) = xCAT_plugin::dhcp::_parse_omshell_host_output(
|
||||
'node01',
|
||||
'name = "node01"',
|
||||
'ip-address = 0a:00:00:05',
|
||||
'hardware-address = 00:11:22:33:44:55',
|
||||
);
|
||||
|
||||
is($name, 'node01', 'host name is parsed');
|
||||
is($ip, 'ip-address = 10.0.0.5', 'IPv4 OMAPI hex address is converted to dotted decimal');
|
||||
is($mac, 'hardware-address = 00:11:22:33:44:55', 'hardware address is preserved');
|
||||
|
||||
($name, $ip, $mac) = xCAT_plugin::dhcp::_parse_omshell_host_output(
|
||||
'nodev6',
|
||||
'name = "nodev6"',
|
||||
'ip-address = 2001:db8::50',
|
||||
'hardware-address = 00:aa:bb:cc:dd:ee',
|
||||
);
|
||||
|
||||
is($name, 'nodev6', 'IPv6 host name is parsed');
|
||||
is($ip, 'ip-address = 2001:db8::50', 'IPv6 address is preserved');
|
||||
is($mac, 'hardware-address = 00:aa:bb:cc:dd:ee', 'IPv6 hardware address is preserved');
|
||||
|
||||
done_testing();
|
||||
50
xCAT-test/unit/nfs_export_options.t
Normal file
50
xCAT-test/unit/nfs_export_options.t
Normal file
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../../xCAT-server/lib/perl";
|
||||
use lib "$FindBin::Bin/../../perl-xCAT";
|
||||
|
||||
use File::Temp qw/tempdir/;
|
||||
use Test::More;
|
||||
|
||||
use xCAT::SvrUtils;
|
||||
|
||||
my $dir = tempdir(CLEANUP => 1);
|
||||
my $exports = "$dir/exports";
|
||||
|
||||
open(my $fh, '>', $exports) or die "Unable to write $exports: $!";
|
||||
print $fh <<'EOF';
|
||||
# comment
|
||||
/install *(rw,no_root_squash,sync,no_subtree_check) # install tree
|
||||
/tftpboot -rw,no_root_squash
|
||||
/other host1(rw) host2(ro,insecure)
|
||||
EOF
|
||||
close($fh);
|
||||
|
||||
ok(xCAT::SvrUtils->nfs_export_exists('/install', files => [$exports]), 'target export is detected');
|
||||
ok(!xCAT::SvrUtils->nfs_export_exists('/missing', files => [$exports]), 'missing export is not detected');
|
||||
|
||||
ok(
|
||||
xCAT::SvrUtils->ensure_nfs_export_option('/install', 'insecure', files => [$exports]),
|
||||
'missing option is added to target export'
|
||||
);
|
||||
my $content = do { local $/; open(my $rfh, '<', $exports) or die $!; <$rfh> };
|
||||
like($content, qr{^/install \*\(rw,no_root_squash,sync,no_subtree_check,insecure\) # install tree$}m, 'target export gains insecure and keeps comment');
|
||||
like($content, qr{^/tftpboot -rw,no_root_squash$}m, 'non-target export is unchanged');
|
||||
like($content, qr{^/other host1\(rw\) host2\(ro,insecure\)$}m, 'unrelated multi-client export is unchanged');
|
||||
|
||||
ok(
|
||||
!xCAT::SvrUtils->ensure_nfs_export_option('/install', 'insecure', files => [$exports]),
|
||||
'existing option is not duplicated'
|
||||
);
|
||||
|
||||
ok(
|
||||
xCAT::SvrUtils->ensure_nfs_export_option('/tftpboot', 'insecure', files => [$exports]),
|
||||
'dash-style export gains option'
|
||||
);
|
||||
$content = do { local $/; open(my $rfh, '<', $exports) or die $!; <$rfh> };
|
||||
like($content, qr{^/tftpboot -rw,no_root_squash,insecure$}m, 'dash-style export is updated correctly');
|
||||
|
||||
done_testing();
|
||||
52
xCAT-test/unit/probe_utils_dhcp_reply.t
Normal file
52
xCAT-test/unit/probe_utils_dhcp_reply.t
Normal file
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../../xCAT-probe/lib/perl";
|
||||
|
||||
use Test::More;
|
||||
|
||||
require probe_utils;
|
||||
|
||||
ok(
|
||||
probe_utils::dhcp_query_reply_matches(
|
||||
'xcatmntest: ip-address = 10.10.0.254, hardware-address = aa:aa:aa:aa:aa:aa',
|
||||
'xcatmntest',
|
||||
'10.10.0.254',
|
||||
'aa:aa:aa:aa:aa:aa',
|
||||
),
|
||||
'ISC-style makedhcp query output with comma matches'
|
||||
);
|
||||
|
||||
ok(
|
||||
probe_utils::dhcp_query_reply_matches(
|
||||
'xcatmntest: ip-address = 10.10.0.254 hardware-address = aa:aa:aa:aa:aa:aa',
|
||||
'xcatmntest',
|
||||
'10.10.0.254',
|
||||
'aa:aa:aa:aa:aa:aa',
|
||||
),
|
||||
'Kea-style makedhcp query output without comma matches'
|
||||
);
|
||||
|
||||
is(
|
||||
probe_utils::dhcp_query_reply_mac(
|
||||
'node01: ip-address = 192.0.2.10 hardware-address = AA:BB:CC:DD:EE:FF',
|
||||
'node01',
|
||||
'192.0.2.10',
|
||||
),
|
||||
'AA:BB:CC:DD:EE:FF',
|
||||
'DHCP query MAC is extracted without normalizing display case'
|
||||
);
|
||||
|
||||
ok(
|
||||
!probe_utils::dhcp_query_reply_matches(
|
||||
'xcatmntest: ip-address = 10.10.0.99 hardware-address = aa:aa:aa:aa:aa:aa',
|
||||
'xcatmntest',
|
||||
'10.10.0.254',
|
||||
'aa:aa:aa:aa:aa:aa',
|
||||
),
|
||||
'mismatched IP does not match'
|
||||
);
|
||||
|
||||
done_testing();
|
||||
34
xCAT-test/unit/probe_utils_netplan.t
Normal file
34
xCAT-test/unit/probe_utils_netplan.t
Normal file
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../../xCAT-probe/lib/perl";
|
||||
|
||||
use Test::More;
|
||||
|
||||
require probe_utils;
|
||||
|
||||
my %netplan = (
|
||||
'ethernets.eth0' => 'renderer: networkd',
|
||||
'ethernets.eth0.addresses' => "- 10.0.0.2/24\n",
|
||||
'ethernets.eth0.dhcp4' => 'false',
|
||||
'ethernets.eth1' => 'renderer: networkd',
|
||||
'ethernets.eth1.addresses' => "- 10.0.0.3/24\n",
|
||||
'ethernets.eth1.dhcp4' => 'true',
|
||||
'vlans.bond0\.123' => 'renderer: networkd',
|
||||
'vlans.bond0\.123.addresses' => "- 10.0.123.5/24\n",
|
||||
);
|
||||
|
||||
{
|
||||
no warnings 'redefine';
|
||||
local *probe_utils::_command_available = sub { return $_[0] eq 'netplan' ? 1 : 0; };
|
||||
local *probe_utils::_netplan_get = sub { return $netplan{ $_[0] }; };
|
||||
|
||||
ok(probe_utils::_netplan_has_static_ip('eth0', '10.0.0.2'), 'static netplan address is detected');
|
||||
ok(!probe_utils::_netplan_has_static_ip('eth0', '10.0.0.99'), 'wrong address is not treated as static');
|
||||
ok(!probe_utils::_netplan_has_static_ip('eth1', '10.0.0.3'), 'dhcp4 true is not treated as static');
|
||||
ok(probe_utils::_netplan_has_static_ip('bond0.123', '10.0.123.5'), 'dotted VLAN interface is escaped for netplan get');
|
||||
}
|
||||
|
||||
done_testing();
|
||||
17
xCAT-test/unit/statelite_add_ssh.t
Normal file
17
xCAT-test/unit/statelite_add_ssh.t
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
use Test::More;
|
||||
|
||||
my $script_path = defined $ENV{XCATROOT} ? "$ENV{XCATROOT}/share/xcat/netboot/add-on/statelite/add_ssh" : '';
|
||||
$script_path = "xCAT-server/share/xcat/netboot/add-on/statelite/add_ssh"
|
||||
unless -f $script_path;
|
||||
|
||||
plan skip_all => "add_ssh not found" unless -f $script_path;
|
||||
|
||||
my $script = do { local $/; open my $fh, '<', $script_path or die $!; <$fh> };
|
||||
|
||||
like($script, qr/MaxStartups 100:30:200/, 'add_ssh caps MaxStartups below systemd soft nofile limit');
|
||||
unlike($script, qr/echo\s+"MaxStartups 1024"/, 'add_ssh does not force MaxStartups 1024');
|
||||
|
||||
done_testing();
|
||||
38
xCAT-test/unit/ubuntu_preseed_template.t
Normal file
38
xCAT-test/unit/ubuntu_preseed_template.t
Normal file
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
use Test::More;
|
||||
|
||||
my $tmpl_path = defined $ENV{XCATROOT} ? "$ENV{XCATROOT}/share/xcat/install/ubuntu/compute.tmpl" : '';
|
||||
$tmpl_path = "xCAT-server/share/xcat/install/ubuntu/compute.tmpl"
|
||||
unless -f $tmpl_path;
|
||||
|
||||
plan skip_all => "compute.tmpl not found" unless -f $tmpl_path;
|
||||
|
||||
my $tmpl = do { local $/; open my $fh, '<', $tmpl_path or die $!; <$fh> };
|
||||
my $template_pm_path = defined $ENV{XCATROOT} ? "$ENV{XCATROOT}/lib/perl/xCAT/Template.pm" : '';
|
||||
$template_pm_path = "xCAT-server/lib/perl/xCAT/Template.pm"
|
||||
unless -f $template_pm_path;
|
||||
|
||||
like($tmpl, qr/^d-i apt-setup\/multiverse boolean false$/m, 'legacy Ubuntu preseed disables multiverse');
|
||||
like($tmpl, qr/^d-i apt-setup\/universe boolean false$/m, 'legacy Ubuntu preseed disables universe');
|
||||
like($tmpl, qr/^d-i apt-setup\/backports boolean false$/m, 'legacy Ubuntu preseed disables backports');
|
||||
like($tmpl, qr/^d-i apt-setup\/updates boolean false$/m, 'legacy Ubuntu preseed disables release updates');
|
||||
like($tmpl, qr/^d-i apt-setup\/services-select multiselect\s*$/m, 'legacy Ubuntu preseed disables security/update services for offline installs');
|
||||
unlike($tmpl, qr/^d-i apt-setup\/services-select multiselect .*\S/m, 'legacy Ubuntu preseed does not select any external apt services');
|
||||
like($tmpl, qr/sed -i .*security.*updates.*backports.*\/target\/etc\/apt\/sources\.list/s,
|
||||
'legacy Ubuntu late command comments disabled apt service suites in the installed target');
|
||||
|
||||
SKIP: {
|
||||
skip "Template.pm not found", 3 unless -f $template_pm_path;
|
||||
my $template_pm = do { local $/; open my $fh, '<', $template_pm_path or die $!; <$fh> };
|
||||
|
||||
like($template_pm, qr/\$ENV\{HTTPPORT\} \|\| \$ENV\{httpport\} \|\| '80'/,
|
||||
'legacy Ubuntu mirror spec uses the rendered HTTP port for local mirrors');
|
||||
like($template_pm, qr/d-i apt-setup\/security_host string \$security_host/,
|
||||
'legacy Ubuntu mirror spec redirects installer security host to rendered xCAT master');
|
||||
like($template_pm, qr/d-i apt-setup\/security_path string \$pkgdir/,
|
||||
'legacy Ubuntu mirror spec redirects installer security path to the local pkgdir');
|
||||
}
|
||||
|
||||
done_testing();
|
||||
18
xCAT-test/unit/ubuntu_subiquity_bootparams.t
Normal file
18
xCAT-test/unit/ubuntu_subiquity_bootparams.t
Normal file
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
use Test::More;
|
||||
|
||||
my $plugin_path = defined $ENV{XCATROOT} ? "$ENV{XCATROOT}/lib/perl/xCAT_plugin/debian.pm" : '';
|
||||
$plugin_path = "xCAT-server/lib/xcat/plugins/debian.pm"
|
||||
unless -f $plugin_path;
|
||||
|
||||
plan skip_all => "debian.pm not found" unless -f $plugin_path;
|
||||
|
||||
my $src = do { local $/; open my $fh, '<', $plugin_path or die $!; <$fh> };
|
||||
|
||||
like($src, qr/autoinstall ip=dhcp netboot=nfs/, 'subiquity bootparams enable autoinstall');
|
||||
like($src, qr/ds=nocloud-net;s=http:\/\//, 'subiquity bootparams point at NoCloud seed');
|
||||
like($src, qr/\$kcmdline\s*\.=\s*" ---";/, 'subiquity bootparams end with installer argument separator');
|
||||
|
||||
done_testing();
|
||||
45
xCAT-test/unit/ubuntu_subiquity_storage.t
Normal file
45
xCAT-test/unit/ubuntu_subiquity_storage.t
Normal file
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
use Test::More;
|
||||
|
||||
my $pre_path = defined $ENV{XCATROOT} ? "$ENV{XCATROOT}/share/xcat/install/scripts/pre.ubuntu.subiquity" : '';
|
||||
$pre_path = "xCAT-server/share/xcat/install/scripts/pre.ubuntu.subiquity"
|
||||
unless -f $pre_path;
|
||||
|
||||
plan skip_all => "pre.ubuntu.subiquity not found" unless -f $pre_path;
|
||||
|
||||
my $script = do { local $/; open my $fh, '<', $pre_path or die $!; <$fh> };
|
||||
|
||||
# Shell syntax check
|
||||
my $rc = system("bash -n $pre_path 2>/dev/null");
|
||||
is($rc, 0, 'pre.ubuntu.subiquity passes bash -n syntax check');
|
||||
|
||||
# UEFI storage layout checks
|
||||
like($script, qr/if \[ -d \/sys\/firmware\/efi \]/, 'script detects UEFI via /sys/firmware/efi');
|
||||
|
||||
# UEFI: grub_device on EFI partition, NOT on disk
|
||||
like($script, qr/id: efi-part.*grub_device: true/s, 'UEFI: grub_device on EFI partition');
|
||||
|
||||
# UEFI: EFI partition formatted as fat32
|
||||
like($script, qr/id: efi-part-fs.*fstype: fat32/s, 'UEFI: EFI partition formatted fat32');
|
||||
|
||||
# UEFI: EFI partition mounted at /boot/efi
|
||||
like($script, qr/efi-part-mount.*path: \/boot\/efi/s, 'UEFI: EFI partition mounted at /boot/efi');
|
||||
|
||||
# BIOS storage layout checks
|
||||
like($script, qr/id: bios-grub.*flag: bios_grub/s, 'BIOS: has bios_grub partition');
|
||||
like($script, qr/id: disk-detected.*grub_device: true/s, 'BIOS: grub_device on disk');
|
||||
|
||||
# Both paths must have storage: version: 1
|
||||
my @storage_version = ($script =~ /storage:\s*\n\s*version:\s*1/g);
|
||||
is(scalar @storage_version, 2, 'both UEFI and BIOS have storage: version: 1');
|
||||
|
||||
# Both paths write to /tmp/partitionfile
|
||||
my @partfile = ($script =~ /\/tmp\/partitionfile/g);
|
||||
cmp_ok(scalar @partfile, '>=', 2, 'both paths write to /tmp/partitionfile');
|
||||
|
||||
# Storage at column 0 (for re-serialized autoinstall.yaml)
|
||||
like($script, qr/^storage:\n version: 1/m, 'storage block starts at column 0');
|
||||
|
||||
done_testing();
|
||||
66
xCAT-test/unit/ubuntu_subiquity_template.t
Normal file
66
xCAT-test/unit/ubuntu_subiquity_template.t
Normal file
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
use Test::More;
|
||||
|
||||
my $tmpl_path = defined $ENV{XCATROOT} ? "$ENV{XCATROOT}/share/xcat/install/ubuntu/compute.subiquity.tmpl" : '';
|
||||
$tmpl_path = "xCAT-server/share/xcat/install/ubuntu/compute.subiquity.tmpl"
|
||||
unless -f $tmpl_path;
|
||||
|
||||
plan skip_all => "compute.subiquity.tmpl not found" unless -f $tmpl_path;
|
||||
|
||||
my $tmpl = do { local $/; open my $fh, '<', $tmpl_path or die $!; <$fh> };
|
||||
|
||||
like($tmpl, qr/^#cloud-config/, 'template starts with #cloud-config');
|
||||
like($tmpl, qr/autoinstall:/, 'template has autoinstall: key');
|
||||
like($tmpl, qr/version:\s*1/, 'template has version: 1');
|
||||
|
||||
unlike($tmpl, qr/^\s*identity:/m, 'template must not have identity section (use user-data instead)');
|
||||
like($tmpl, qr/kernel:/, 'template has kernel section');
|
||||
like($tmpl, qr/package:\s*linux-generic/, 'template specifies linux-generic kernel');
|
||||
like($tmpl, qr/apt:/, 'template has apt section');
|
||||
like($tmpl, qr/fallback:\s*offline-install/, 'template uses offline-install fallback');
|
||||
unlike($tmpl, qr/WARN: no partitionfile/, 'template does not silently fall back when xCAT pre-script fails');
|
||||
unlike($tmpl, qr/INSTALL_DISK=""/, 'template does not guess an install disk in early-commands');
|
||||
like($tmpl, qr/geoip:\s*false/, 'template disables geoip');
|
||||
|
||||
like($tmpl, qr/ssh:/, 'template has ssh section');
|
||||
like($tmpl, qr/install-server:\s*true/, 'template enables ssh install-server');
|
||||
|
||||
unlike($tmpl, qr/package_update:\s*true/, 'template does not enable package_update');
|
||||
unlike($tmpl, qr/^\s*-\s+nfs-common\s*$/m, 'template does not require nfs-common from offline ISO packages');
|
||||
|
||||
# YAML safety: use printf with single-quoted arguments instead of shell-specific
|
||||
# escape sequences. dash does not portably interpret printf \xNN.
|
||||
unlike($tmpl, qr/echo.*GRUB_CMDLINE.*\\"/, 'no escaped double quotes in echo GRUB line');
|
||||
unlike($tmpl, qr/\\\\x22/, 'template does not rely on non-portable printf hex escapes');
|
||||
like($tmpl, qr/printf ''%s\\n'' ''GRUB_CMDLINE_LINUX="#TABLEBLANKOKAY:bootparams:\$NODE:kcmdline#"''/, 'GRUB line uses portable printf quoting');
|
||||
like($tmpl, qr/\/target\/etc\/netplan\/00-xcat-install\.yaml/, 'template writes an xCAT-owned target netplan file');
|
||||
like($tmpl, qr/installnic="#TABLE:noderes:\$NODE:installnic#"/, 'target netplan uses node installnic');
|
||||
like($tmpl, qr/installmac="#TABLE:mac:\$NODE:mac#"/, 'target netplan uses node MAC');
|
||||
like($tmpl, qr/installmac="\$\(printf ''%s'' "\$\{installmac\}" \| tr ''A-F'' ''a-f''\)"/, 'target netplan normalizes MAC case');
|
||||
like($tmpl, qr/printf ''%s\\n'' "network:" " version: 2" " ethernets:" " xcat-install:" " match:" " macaddress: \\"\$\{installmac\}\\"" " set-name: \$\{installnic\}" " dhcp4: true" >\/target\/etc\/netplan\/00-xcat-install\.yaml;/, 'target netplan printf stays on one shell line');
|
||||
like($tmpl, qr/" macaddress: \\"\$\{installmac\}\\""/, 'target netplan matches by MAC address');
|
||||
like($tmpl, qr/" set-name: \$\{installnic\}"/, 'target netplan sets the expected installnic name');
|
||||
like($tmpl, qr/"\s+dhcp4: true"/, 'target netplan enables DHCPv4 on installnic');
|
||||
like($tmpl, qr/printf ''%s\\n'' ''#HOSTNAME#'' >\/target\/etc\/hostname/, 'template writes target hostname before disabling cloud-init');
|
||||
like($tmpl, qr/sed -i ''s\/\^127\\\.0\\\.1\\\.1\.\*\/127\.0\.1\.1 #HOSTNAME#\/'' \/target\/etc\/hosts/, 'template updates target hosts entry for hostname');
|
||||
like($tmpl, qr/touch \/target\/etc\/cloud\/cloud-init\.disabled/, 'target cloud-init is disabled after target netplan is written');
|
||||
|
||||
# Regression: downloaded files are required and checked with test -s before use.
|
||||
like($tmpl, qr/wget -T 30 -O \/tmp\/getinstdisk http:\/\/#XCATVAR:XCATMASTER#/, 'getinstdisk download is required');
|
||||
like($tmpl, qr/test -s \/tmp\/getinstdisk/, 'getinstdisk checked with -s not -x');
|
||||
like($tmpl, qr/wget -T 30 -O \/tmp\/pre\.sh http:\/\/#XCATVAR:XCATMASTER#/, 'pre.sh download is required');
|
||||
like($tmpl, qr/test -s \/tmp\/pre\.sh/, 'pre.sh checked with -s not -x');
|
||||
like($tmpl, qr/test -s \/tmp\/partitionfile/, 'partitionfile from pre-script is required');
|
||||
unlike($tmpl, qr/wget .*?\|\| true/, 'xCAT control artifact downloads are not masked');
|
||||
unlike($tmpl, qr/if \[ -x \/tmp\/getinstdisk \]/, 'getinstdisk not checked with -x');
|
||||
unlike($tmpl, qr/if \[ -x \/tmp\/pre\.sh \]/, 'pre.sh not checked with -x');
|
||||
|
||||
# Regression: disable_suites must be release-independent (no noble-*, jammy-*, etc.)
|
||||
unlike($tmpl, qr/noble-|jammy-|focal-/, 'disable_suites uses release-independent names');
|
||||
like($tmpl, qr/- updates/, 'disable_suites includes updates');
|
||||
like($tmpl, qr/- backports/, 'disable_suites includes backports');
|
||||
like($tmpl, qr/- security/, 'disable_suites includes security');
|
||||
|
||||
done_testing();
|
||||
31
xCAT-test/unit/xnba_semicolon.t
Normal file
31
xCAT-test/unit/xnba_semicolon.t
Normal file
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
use Test::More;
|
||||
|
||||
my $xnba_path = defined $ENV{XCATROOT} ? "$ENV{XCATROOT}/lib/perl/xCAT_plugin/xnba.pm" : '';
|
||||
$xnba_path = "xCAT-server/lib/xcat/plugins/xnba.pm"
|
||||
unless -f $xnba_path;
|
||||
|
||||
plan skip_all => "xnba.pm not found" unless -f $xnba_path;
|
||||
|
||||
my $src = do { local $/; open my $fh, '<', $xnba_path or die $!; <$fh> };
|
||||
|
||||
# iPXE only treats a standalone ; token as a command separator.
|
||||
# A ; embedded inside an argument value (e.g. ds=nocloud-net;s=...)
|
||||
# is NOT split by iPXE's parser. Therefore xnba.pm must NOT escape
|
||||
# the semicolon — doing so (e.g. \;) would corrupt the value and
|
||||
# prevent cloud-init from parsing the NoCloud seed URL.
|
||||
unlike($src, qr/kcmd.*=~.*s\/;/, 'BIOS path does not escape semicolons');
|
||||
unlike($src, qr/ucmd.*=~.*s\/;/, 'UEFI path does not escape semicolons');
|
||||
|
||||
# The kcmdline is passed directly to imgargs without modification
|
||||
like($src, qr/imgargs kernel.*\$kern->\{kcmdline\}/, 'BIOS kcmdline passed directly to imgargs');
|
||||
|
||||
# UEFI nodes must not keep a stale install script when the node moves
|
||||
# back to boot/standby; otherwise they PXE back into the installer.
|
||||
like($src, qr/sub _write_uefi_exit_script\b/, 'UEFI local boot helper exists');
|
||||
like($src, qr/_write_uefi_exit_script\(\$bootloader_root, \$node, \$cref->\{currstate\}\);/, 'boot/local states rewrite UEFI xNBA script');
|
||||
like($src, qr/print \$ucfg "exit\\n";/, 'UEFI local boot script exits iPXE to firmware');
|
||||
|
||||
done_testing();
|
||||
Reference in New Issue
Block a user