2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-05-05 16:49:08 +00:00

Merge pull request #7529 from VersatusHPC/fix/ubuntu-2604-support

Add Ubuntu 26.04 provisioning support
This commit is contained in:
Markus Hilger
2026-05-01 23:25:09 +02:00
committed by GitHub
20 changed files with 616 additions and 100 deletions

View File

@@ -104,8 +104,8 @@ for package in ${packages[@]}; do
fi
done
# Supported distributions
dists="saucy trusty utopic xenial bionic focal jammy"
# Supported distributions. Set DISTS="jammy noble resolute" to limit local validation builds.
dists="${DISTS:-saucy trusty utopic xenial bionic focal jammy noble resolute}"
c_flag= # xcat-core (trunk-delvel) path
d_flag= # xcat-dep (trunk) path

View File

@@ -122,6 +122,13 @@ The following matrix is the default live validation gate for DHCP backend work.
- ``makedhcp -n``; ``kea-dhcp4 -t``; reservation add/query/delete;
xNBA shell boot; compute-image boot artifact handoff through kernel,
initrd, and root image when image validation is in scope
* - Ubuntu 26.04 LTS
- ``x86_64``
- ``Kea``
- ``xNBA shell`` and compute-image handoff
- ``makedhcp -n``; ``kea-dhcp4 -t``; reservation add/query/delete;
xNBA shell boot; stateful subiquity and stateless compute-image handoff
through kernel, initrd, and root image when image validation is in scope
Kea Boot and Reservation Regression Matrix
------------------------------------------
@@ -212,18 +219,20 @@ paths.
- ``POWER GRUB handoff``
- DHCP offer; boot file handoff; POWER boot-path correctness; Genesis
shell when Genesis payload validation is in scope
* - Ubuntu 26.04 LTS
- ``ppc64le``
- ``Kea``
- ``POWER GRUB handoff``
- package installation on ``ppc64el``; DHCP offer; boot file handoff;
POWER boot-path correctness; Genesis shell when live validation is in
scope
Current Lab Baseline
--------------------
The current KVM validation hosts are:
* ``rome01.local.versatushpc.com.br`` for ``x86_64``
* ``power.local.versatushpc.com.br`` for ``ppc64le``
Validation access should use the ``builder`` account and the
``id_ed25519_reposync`` SSH key. Avoid relying on ad-hoc root login or
one-off cloud-init keys when recording repeatable validation procedure.
KVM validation should cover both ``x86_64`` and ``ppc64le`` hosts. Record the
repeatable lab access method privately with the validation run instead of
embedding site-specific hostnames or credentials in this repository.
Full Ubuntu 24.04 ``x86_64`` stateless KVM validation required Ubuntu
``genimage`` fixes for early BOOTIF handling and a lean initrd driver set.
@@ -240,14 +249,14 @@ rendering.
Current exceptions:
* Ubuntu 22.04 LTS ISC OMAPI/``omshell`` host reservation updates are blocked by
xCAT3 issue ``#11``. The failure reproduces on upstream ``master`` and is not
caused by the Kea backend work. ``site.dhcpbackend=auto`` therefore selects
Kea for Ubuntu 22.04 and newer releases; live Ubuntu 22.04 Kea validation
remains a follow-up matrix row.
* Ubuntu 22.04 LTS ISC OMAPI/``omshell`` host reservation updates are
unreliable on current upstream code and are not caused by the Kea backend
work. ``site.dhcpbackend=auto`` therefore selects Kea for Ubuntu 22.04 and
newer releases; live Ubuntu 22.04 Kea validation remains a follow-up matrix
row.
* Ubuntu ``ppc64le`` package installation is missing required boot packages such
as ``goconserver``, ``grub2-xcat``, and ``xcat-genesis-base-ppc64``. This is
tracked by xCAT3 issue ``#13`` and is separate from Kea DHCP behavior.
tracked separately and is not a Kea DHCP behavior issue.
Current PR Validation Snapshot
------------------------------
@@ -319,8 +328,7 @@ backend scope described above.
- Raw DHCP fixture on the KVM provisioning bridge sent option 93
``0x0010`` from the reserved outside-pool node MAC and received lease
``10.241.10.22`` with boot file ``xcat/xnba.efi``; no
``ALLOC_FAIL_NO_POOLS`` was observed. xCAT3 issue ``#17`` is closed as
completed.
``ALLOC_FAIL_NO_POOLS`` was observed.
* - Ubuntu 24.04 LTS
- ``x86_64``
- ``Kea 2.4.1``
@@ -339,9 +347,9 @@ backend scope described above.
``genesis.kernel.ppc64`` and ``genesis.fs.ppc64.gz`` reached xCAT
``shell`` state after replacing the broken ``grub2-xcat 1.0-3`` ppc
GRUB core/module tree with the coherent
``2.02-0.76.el7.1.snap201905160255`` tree. The remaining xCAT3 issue
``#16`` follow-up is package provenance for ``grub2-xcat``, not DHCP or
Genesis payload behavior.
``2.02-0.76.el7.1.snap201905160255`` tree. The remaining follow-up is
package provenance for ``grub2-xcat``, not DHCP or Genesis payload
behavior.
* - Ubuntu 24.04 LTS
- ``ppc64le``
- ``Kea 2.4.1``
@@ -354,24 +362,28 @@ backend scope described above.
Ubuntu LTS KVM Validation Snapshot
----------------------------------
As of April 29, 2026, the Ubuntu LTS KVM validation for the Ubuntu
As of May 1, 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
:widths: 14 9 9 12 12 12 12 30
* - Platform
- Arch
- Backend
- BIOS
- UEFI
- BIOS Stateless
- BIOS Stateful
- UEFI Stateless
- UEFI Stateful
- Notes
* - Ubuntu 18.04 LTS
- ``x86_64``
- ``ISC``
- Pass
- Pass
- 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
@@ -384,6 +396,8 @@ provisioning restoration work has the following result:
- ``ISC``
- Pass
- Pass
- Pass
- Pass
- Stateless and stateful compute boots passed. Stateful validation used a
manual static reservation workaround for the known forced-ISC OMAPI
issue.
@@ -392,6 +406,8 @@ provisioning restoration work has the following result:
- ``Kea``
- Pass
- Pass
- 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
@@ -401,40 +417,30 @@ provisioning restoration work has the following result:
- ``Kea``
- Pass
- Pass
- 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
------------
The following rows are intentionally not counted in the current PR acceptance
table:
.. list-table::
:header-rows: 1
:widths: 18 10 12 60
* - Platform
- Arch
- Backend
- Reason
* - Ubuntu 22.04 LTS
* - Ubuntu 26.04 LTS
- ``x86_64``
- ``Kea``
- Skipped in this PR. Unit coverage now pins Ubuntu 22.04 to Kea by
default because ISC OMAPI/``omshell`` is blocked by xCAT3 issue ``#11``;
live KVM validation remains a follow-up row.
* - Ubuntu 22.04 LTS
- ``ppc64le``
- ``Kea``
- Skipped for the same Ubuntu 22.04 Kea follow-up validation reason as the
``x86_64`` row.
- Pass
- Pass
- Pass
- Pass
- Stateless and stateful compute boots passed against an Ubuntu 26.04
headnode. Kea 3.0.3 configuration validation with ``kea-dhcp4 -t``
passed. Stateless BIOS and UEFI nodes reached ``sshd`` after the
netboot image was repacked with the recursive postscript download fix;
stateful BIOS and UEFI nodes completed Subiquity install and booted from
disk. Kea logs showed no ``ALLOC_FAIL_NO_POOLS``. UEFI validation used
OVMF Secure Boot disabled.
External Follow-up Tracking
---------------------------
Follow-up Tracking
------------------
These issues are outside the DHCP acceptance result but must be referenced when
reporting this validation run:
@@ -446,12 +452,12 @@ reporting this validation run:
* - Issue
- Area
- Impact
* - xCAT3 ``#13``
* - Ubuntu ``ppc64le`` packages
- Ubuntu ``ppc64le`` packages
- Missing ``goconserver``, ``grub2-xcat``, and
``xcat-genesis-base-ppc64`` block clean Ubuntu ppc64le package
installation.
* - xCAT3 ``#16``
* - ``ppc64le`` GRUB package
- ``ppc64le`` GRUB package
- EL10 ppc64le fails with ``grub2-xcat 1.0-3`` because its POWER GRUB
core/module tree cannot load the ppc64le Genesis kernel. The live row is

View File

@@ -310,7 +310,7 @@ Backend selection tests:
* ``auto`` uses ISC on EL9
* ``auto`` uses ISC on Ubuntu 20.04
* ``auto`` uses Kea on EL10
* ``auto`` uses Kea on Ubuntu 22.04 and Ubuntu 24.04
* ``auto`` uses Kea on Ubuntu 22.04 and newer
* forced ``kea`` works on EL9 when Kea packages are installed
* forced unavailable backend fails clearly
@@ -323,6 +323,7 @@ Integration matrix:
* Ubuntu 20.04 plus ISC
* Ubuntu 22.04 plus Kea
* Ubuntu 24.04 plus Kea
* Ubuntu 26.04 plus Kea
Semantic parity tests:
@@ -348,21 +349,18 @@ Test Infrastructure
-------------------
Existing container-based EL8, EL9, and EL10 tests should be extended for backend
coverage. The libvirt/KVM infrastructure on ``rome01.local.versatushpc.com.br``
can be used for network and PXE smoke tests that are difficult to validate in
ordinary containers.
coverage. Libvirt/KVM infrastructure can be used for network and PXE smoke
tests that are difficult to validate in 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 18.04, Ubuntu 20.04, Ubuntu
22.04, and Ubuntu 24.04
22.04, Ubuntu 24.04, and Ubuntu 26.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
* cleanup expectations for temporary VMs, networks, and storage volumes
* the ``builder`` account and ``id_ed25519_reposync`` SSH key are available for
repeatable validation access
* repeatable validation access method and credentials
Manual Validation Snapshot
--------------------------

View File

@@ -154,19 +154,19 @@ sub render_ctrl_agent_config {
my %sockets = (
dhcp4 => {
'socket-type' => 'unix',
'socket-name' => $intent->{'dhcp4-socket'} || '/var/run/kea/kea4-ctrl-socket',
'socket-name' => $intent->{'dhcp4-socket'} || $self->_kea_control_socket('kea4-ctrl-socket'),
},
);
if ( $intent->{dhcp6} || $intent->{'dhcp6-socket'} ) {
$sockets{dhcp6} = {
'socket-type' => 'unix',
'socket-name' => $intent->{'dhcp6-socket'} || '/var/run/kea/kea6-ctrl-socket',
'socket-name' => $intent->{'dhcp6-socket'} || $self->_kea_control_socket('kea6-ctrl-socket'),
};
}
if ( $intent->{ddns} || $intent->{'ddns-socket'} ) {
$sockets{d2} = {
'socket-type' => 'unix',
'socket-name' => $intent->{'ddns-socket'} || '/var/run/kea/kea-ddns-ctrl-socket',
'socket-name' => $intent->{'ddns-socket'} || $self->_kea_control_socket('kea-ddns-ctrl-socket'),
};
}
@@ -218,7 +218,7 @@ sub load_dhcp4_config {
my $content = <$fh>;
close($fh);
my $json = eval { decode_json( _strip_json_comments($content) ) };
my $json = eval { _decode_kea_json($content) };
return { error => "Unable to parse $path as JSON: $@" } if $@;
$json->{Dhcp4}{subnet4} ||= [];
@@ -236,7 +236,7 @@ sub load_dhcp6_config {
my $content = <$fh>;
close($fh);
my $json = eval { decode_json( _strip_json_comments($content) ) };
my $json = eval { _decode_kea_json($content) };
return { error => "Unable to parse $path as JSON: $@" } if $@;
$json->{Dhcp6}{subnet6} ||= [];
@@ -334,6 +334,15 @@ sub encode_config {
return JSON->new->canonical->pretty->encode($config);
}
sub _decode_kea_json {
my ($content) = @_;
# Kea accepts JSON with C/C++ comments and trailing commas. JSON->relaxed
# is backend-dependent, so normalize these Kea extensions explicitly before
# handing the content to the strict decoder.
return decode_json( _strip_json_trailing_commas( _strip_json_comments($content) ) );
}
sub _strip_json_comments {
my ($content) = @_;
@@ -387,6 +396,48 @@ sub _strip_json_comments {
return $out;
}
sub _strip_json_trailing_commas {
my ($content) = @_;
my $out = '';
my $in_string = 0;
my $escaped = 0;
my $length = length($content);
for ( my $idx = 0; $idx < $length; $idx++ ) {
my $char = substr( $content, $idx, 1 );
if ($in_string) {
$out .= $char;
if ($escaped) {
$escaped = 0;
} elsif ($char eq '\\') {
$escaped = 1;
} elsif ($char eq '"') {
$in_string = 0;
}
next;
}
if ($char eq '"') {
$in_string = 1;
$out .= $char;
next;
}
if ($char eq ',') {
my $lookahead = $idx + 1;
$lookahead++ while $lookahead < $length && substr( $content, $lookahead, 1 ) =~ /\s/;
my $next = $lookahead < $length ? substr( $content, $lookahead, 1 ) : '';
next if $next eq '}' || $next eq ']';
}
$out .= $char;
}
return $out;
}
sub write_ctrl_agent_config {
my ( $self, $intent, %opts ) = @_;
@@ -425,7 +476,17 @@ sub _validate_config_with {
my $kea = _command_path($command);
return { error => "Unable to validate $label configuration: $command was not found." } unless $kea;
my $cmd = "$kea -t " . _shell_quote($path) . " 2>&1";
my $prefix = '';
if ( $> == 0 ) {
my $kea_user = _kea_user();
my $runuser = _command_path('runuser');
# Validate as the daemon user when possible so root does not hide
# packaged Kea runtime-directory or config-readability failures.
$prefix = _shell_quote($runuser) . ' -u ' . _shell_quote($kea_user) . ' -- '
if $kea_user && $runuser;
}
my $cmd = $prefix . _shell_quote($kea) . " -t " . _shell_quote($path) . " 2>&1";
my $output = `$cmd`;
my $rc = $? >> 8;
return { error => "$label configuration validation failed: $output" } if $rc != 0;
@@ -918,6 +979,15 @@ sub _kea_group {
return;
}
sub _kea_user {
foreach my $user ( 'kea', '_kea' ) {
my @entry = getpwnam($user);
return $entry[0] if @entry;
}
return;
}
sub _kea_service {
my ( $self, $service ) = @_;
@@ -953,6 +1023,28 @@ sub _systemd_unit_dirs {
];
}
sub _kea_socket_dir {
my ($self) = @_;
return $self->{kea_socket_dir} if defined $self->{kea_socket_dir};
# Kea validates Control Agent sockets against its packaged runtime
# directory, and newer packages reject /var/run/kea even when it resolves
# to /run/kea. Keep the legacy path as the unknown-state fallback for
# older Kea builds that validate before the runtime directory exists.
foreach my $dir ( @{ $self->{kea_socket_dirs} || [ '/run/kea', '/var/run/kea' ] } ) {
return $dir if -d $dir;
}
return '/var/run/kea';
}
sub _kea_control_socket {
my ( $self, $socket_name ) = @_;
return $self->_kea_socket_dir() . "/$socket_name";
}
sub _command_path {
my ($command) = @_;

View File

@@ -37,6 +37,8 @@ my $key;
my $field;
my $idir;
my $node;
my $master;
my $httpportsuffix;
my %loggedrealms;
my $lastmachinepassdata;
my $localadminenabled; #indicate whether Windows template has local logins enabled or not
@@ -86,7 +88,7 @@ sub subvars {
## 1, the "xcatmaster" attribute of the node
## 2, the ip address of the mn/sn facing the compute node
## 3, the site.master
my $master;
$master = undef;
#the "xcatmaster" attribute of the node
if ($tmpl_hash->{'xcatmaster'}) {
@@ -146,7 +148,7 @@ sub subvars {
}
$ENV{HTTPPORT} = $httpport;
my $httpportsuffix=":$httpport";
$httpportsuffix=":$httpport";
#replace the env with the right value so that correct include files can be found
$inc =~ s/#ENV:([^#]+)#/envvar($1)/eg;
my $res;
@@ -364,6 +366,7 @@ sub subvars {
$inc =~ s/#INSTALL_SOURCES_IN_PRE#/$source_in_pre/g;
if (("ubuntu" eq $platform) || ("debian" eq $platform)) {
$inc =~ s/#INCLUDE_OSIMAGE_PKGDIR#/$pkgdirs[-1]/;
$inc =~ s/#UBUNTU_SUBIQUITY_APT_CONFIG#/ubuntu_subiquity_apt_config($media_dir)/eg;
}
$inc =~ s/#WRITEREPO#/$writerepo/g;
}
@@ -376,8 +379,10 @@ sub subvars {
$inc =~ s/#INCLUDE_NOP:([^#^\n]+)#/includefile($1,1,0)/eg;
$inc =~ s/#XCATVAR:([^#]+)#/envvar($1)/eg;
$inc =~ s/#ENV:([^#]+)#/envvar($1)/eg;
$inc =~ s/#UBUNTU_SUBIQUITY_APT_CONFIG#/ubuntu_subiquity_apt_config($media_dir)/eg;
$inc =~ s/#MACHINEPASSWORD#/machinepassword()/eg;
$inc =~ s/#CRYPT:([^:]+):([^:]+):([^#]+)#/crydb($1,$2,$3)/eg;
$inc =~ s/#CRYPTORLOCKED:([^:]+):([^:]+):([^#]+)#/crydb_or_locked($1,$2,$3)/eg;
$inc =~ s/#COMMAND:([^#]+)#/command($1)/eg;
$inc =~ s/#KICKSTARTNET#/kickstartnetwork()/eg;
$inc =~ s/#MIRRORSPEC#/mirrorspec()/eg;
@@ -1647,6 +1652,158 @@ sub crydb
return xCAT::PasswordUtils::crypt_system_password($table, $kp, \@fields);
}
sub crydb_or_locked
{
my ( $table, $key, $field ) = @_;
my $crypted = crydb( $table, $key, $field );
return $crypted if defined($crypted) && length($crypted);
return '*';
}
sub ubuntu_subiquity_apt_config
{
my ($media_dir) = @_;
my $use_deb822 = ubuntu_subiquity_uses_deb822_sources($media_dir);
my @otherpkg_sources = ubuntu_subiquity_otherpkg_sources();
my @lines = (
' apt:',
' preserve_sources_list: false',
' fallback: offline-install',
' geoip: false',
' disable_suites:',
' - updates',
' - backports',
' - security',
);
if ($use_deb822) {
if (ubuntu_subiquity_uses_generated_cdrom_source($media_dir)) {
# Ubuntu 26.04 and newer Subiquity overlays provide cdrom.sources.
# Rendering a second file:///cdrom source triggers apt option conflicts.
# Curtin still requires a valid Deb822 template, so write an inactive source.
push @lines, ' sources_list: |';
push @lines, ' Types: deb';
push @lines, ' URIs: http://xcat.invalid/disabled';
push @lines, ' Suites: $RELEASE';
push @lines, ' Components: main';
push @lines, ' Enabled: no';
} else {
push @lines, ' sources_list: |';
push @lines, ' Types: deb';
push @lines, ' URIs: file:///cdrom';
push @lines, ' Suites: $RELEASE';
push @lines, ' Components: main universe restricted multiverse';
push @lines, ' Check-Date: no';
}
foreach my $source (@otherpkg_sources) {
push @lines, '';
push @lines, ' Types: deb';
push @lines, " URIs: $source";
push @lines, ' Suites: ./';
push @lines, ' Components:';
push @lines, ' Trusted: yes';
}
} else {
push @lines, ' mirror-selection:';
push @lines, ' primary:';
push @lines, ' - uri: file:/cdrom';
if (@otherpkg_sources) {
push @lines, ' sources:';
my $index = 0;
foreach my $source (@otherpkg_sources) {
push @lines, " xcat-otherpkgs-$index.list:";
push @lines, qq( source: "deb [trusted=yes] $source ./");
$index++;
}
}
}
return join( "\n", @lines );
}
sub ubuntu_subiquity_otherpkg_sources
{
my $nodetype_tab = xCAT::Table->new('nodetype');
return unless $nodetype_tab;
my $nodetype_ent = $nodetype_tab->getNodeAttribs( $node, ['provmethod'] );
return unless $nodetype_ent && $nodetype_ent->{provmethod};
my $linuximage_tab = xCAT::Table->new('linuximage');
return unless $linuximage_tab;
my $linuximage_ent = $linuximage_tab->getAttribs( { imagename => $nodetype_ent->{provmethod} }, 'otherpkgdir' );
return unless $linuximage_ent && $linuximage_ent->{otherpkgdir};
my @sources;
foreach my $otherpkgdir ( split( /,/, $linuximage_ent->{otherpkgdir} ) ) {
$otherpkgdir =~ s/^\s+|\s+$//g;
next unless $otherpkgdir;
next if $otherpkgdir !~ m{^https?://} && !ubuntu_subiquity_local_apt_repo($otherpkgdir);
my $uri = ubuntu_subiquity_pkgdir_uri($otherpkgdir);
push @sources, $uri;
}
return @sources;
}
sub ubuntu_subiquity_uses_deb822_sources
{
my ($media_dir) = @_;
my $release = ubuntu_subiquity_release($media_dir);
return 1 if $release =~ m{ubuntu([2-9][4-9]|[3-9][0-9])\.\d+};
return 0;
}
sub ubuntu_subiquity_uses_generated_cdrom_source
{
my ($media_dir) = @_;
my $release = ubuntu_subiquity_release($media_dir);
return 1 if $release =~ m{ubuntu([2-9][6-9]|[3-9][0-9])\.\d+};
return 0;
}
sub ubuntu_subiquity_release
{
my ($media_dir) = @_;
my $release = $media_dir || '';
if ( !$release ) {
my $nodetype_tab = xCAT::Table->new('nodetype');
my $nodetype_ent = $nodetype_tab ? $nodetype_tab->getNodeAttribs( $node, ['os'] ) : undef;
$release = $nodetype_ent->{os} if $nodetype_ent && $nodetype_ent->{os};
}
return $release;
}
sub ubuntu_subiquity_local_apt_repo
{
my ($path) = @_;
return 0 unless $path && -d $path;
return 1 if ( -f "$path/Packages" || -f "$path/Packages.gz" ) && -f "$path/Release";
return 0;
}
sub ubuntu_subiquity_pkgdir_uri
{
my ($path) = @_;
$path ||= '/install';
$path =~ s/\s+$//;
$path =~ s{/+$}{};
return $path if $path =~ m{^https?://};
$path = "/$path" if $path !~ m{^/};
return "http://$master$httpportsuffix$path";
}
sub tabdb
{
my $table = shift;

View File

@@ -100,7 +100,10 @@ if [ ! -x /usr/bin/wget ]; then
sleep 36500d
fi
wget -l inf -N -r --waitretry=10 --random-wait --retry-connrefused -e robots=off -nH --cut-dirs=2 --reject "index.html*" --no-parent -t 20 -T 60 http://${MASTER_IP}:${HTTPPORT}${INSTALLDIR}/postscripts/ -P /xcatpost 2> /tmp/wget.log
# These dispatcher scripts are not needed by the legacy post.xcat path. Newer
# wget parses HTML-looking regex strings inside downloaded scripts and fails the
# whole recursive download on bogus URLs.
wget -l inf -N -r --waitretry=10 --random-wait --retry-connrefused -e robots=off -nH --cut-dirs=2 --reject "index.html*,post.xcat.ng,post.xcat.rhels10" --no-parent -t 20 -T 60 http://${MASTER_IP}:${HTTPPORT}${INSTALLDIR}/postscripts/ -P /xcatpost 2> /tmp/wget.log
if [ "$?" != "0" ]; then
msgutil_r "$MASTER_IP" "error" "failed to download postscripts from http://$MASTER_IP$INSTALLDIR/postscripts/,check /tmp/wget.log on the node, halt ..." "/var/log/xcat/xcat.log" "$log_label"
/tmp/updateflag $MASTER $XCATIPORT "installstatus failed"

View File

@@ -10,13 +10,14 @@ systemctl stop syslog.socket
systemctl stop rsyslog
# however, lets make sure . . .
[ -e /var/run/rsyslogd.pid ] && kill -TERM $(cat /var/run/rsyslogd.pid)
# bare minimum rsyslog.conf to support UDP forwarding
cat <<EOF > /tmp/rsyslog.conf
# bare minimum rsyslog.conf to support UDP forwarding. Newer Ubuntu releases
# run rsyslog under AppArmor and do not allow a config file from /tmp.
cat <<EOF > /etc/rsyslog.conf
module(load="imuxsock")
module(load="imklog" permitnonkernelfacility="on")
*.* @#XCATVAR:XCATMASTER#
EOF
rsyslogd -f /tmp/rsyslog.conf
rsyslogd -f /etc/rsyslog.conf
log_label="xcat.deployment"
logger -t $log_label -p "info" "============deployment starting============"
logger -t $log_label -p "info" "Running preseeding early_command Installation script..."

View File

@@ -12,13 +12,7 @@ autoinstall:
allow-pw: true
authorized-keys: []
install-server: true
apt:
fallback: offline-install
geoip: false
disable_suites:
- updates
- backports
- security
#UBUNTU_SUBIQUITY_APT_CONFIG#
kernel:
package: linux-generic
user-data:
@@ -28,13 +22,20 @@ autoinstall:
package_upgrade: false
timezone: #TABLE:site:key=timezone:value#
chpasswd:
list: |
root:#CRYPT:passwd:key=system,username=root:password#
list:
- "root:#CRYPTORLOCKED:passwd:key=system,username=root:password#"
expire: false
packages:
- openssh-server
- openssh-client
- wget
- vim
- rsync
- busybox-static
- gawk
- bind9-dnsutils
- chrony
- gpg
early-commands:
- |
exec >/tmp/pre-install.log 2>&1
@@ -90,4 +91,4 @@ autoinstall:
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/log/installer /tmp/pre-install.log /autoinstall.yaml 2>/dev/null |nc -l 8080
- tar -c --ignore-failed-read --transform='s/^/#HOSTNAME#-logs\//' /var/crash /var/log/installer /tmp/pre-install.log /autoinstall.yaml 2>/dev/null |nc -l 8080

View File

@@ -0,0 +1,15 @@
bash
nfs-common
openssl
isc-dhcp-client
libc-bin
openssh-server
openssh-client
wget
vim
rsync
busybox-static
gawk
bind9-dnsutils
chrony
gpg

View File

@@ -0,0 +1 @@
compute.ubuntu26.04.ppc64el.pkglist

View File

@@ -0,0 +1,15 @@
bash
nfs-common
openssl
isc-dhcp-client
libc-bin
openssh-server
openssh-client
wget
vim
rsync
busybox-static
gawk
bind9-dnsutils
chrony
gpg

View File

@@ -0,0 +1,20 @@
bash
ifupdown
nfs-common
openssl
isc-dhcp-client
libc-bin
linux-image-generic
openssh-server
openssh-client
wget
vim
rsync
busybox-static
gawk
bind9-dnsutils
tar
gzip
xz-utils
cpio
chrony

View File

@@ -0,0 +1 @@
compute.ubuntu26.04.ppc64el.pkglist

View File

@@ -0,0 +1,18 @@
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

View File

@@ -273,7 +273,7 @@ unless ($onlyinitrd) {
open($aptconfig, ">", "/tmp/genimage.$$.apt.list");
my $repnum = 0;
foreach $tmpsrcdir (@aptdirs) {
print $aptconfig "deb file://$tmpsrcdir main stable\n\n";
print $aptconfig "deb [trusted=yes] file://$tmpsrcdir main stable\n\n";
$repnum += 1;
}
$repnum -= 1;
@@ -357,14 +357,18 @@ unless ($onlyinitrd) {
open($aptconfig, ">", "$rootimg_dir/etc/apt/sources.list");
if ($srcdir) {
print $aptconfig "deb http://$masternode:$httpport$srcdir $dist main restricted universe\n";
print $aptconfig "deb [trusted=yes] http://$masternode:$httpport$srcdir $dist main restricted universe\n";
}
foreach (@pkgdir_internet) {
print $aptconfig "deb $_\n";
}
foreach (@pkgdir_local) {
print $aptconfig "deb $_\n";
if (/^(http:\/\/\S+)\s+(\S.*)$/) {
print $aptconfig "deb [trusted=yes] $1 $2\n";
} else {
print $aptconfig "deb $_\n";
}
}
close($aptconfig);
@@ -1254,10 +1258,10 @@ for i in `cat /proc/cmdline`; do
done
if [ -z "\$IFACE" ]; then
if iface_exists "\$BOOTIF"; then
IFACE=\$BOOTIF
elif iface_exists "\$NETDEV"; then
if iface_exists "\$NETDEV"; then
IFACE=\$NETDEV
elif iface_exists "\$BOOTIF"; then
IFACE=\$BOOTIF
elif iface_exists "\$PRINIC"; then
IFACE=\$PRINIC
else

View File

@@ -209,6 +209,16 @@ is( $reservation_policy_config->{Dhcp4}{'reservations-in-subnet'}, JSON::false,
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 $client_id_policy_json = $backend->render_dhcp4_config(
{
interfaces => ['eth0'],
match_client_id => 1,
subnets => [],
}
);
my $client_id_policy_config = decode_json($client_id_policy_json);
is( $client_id_policy_config->{Dhcp4}{'match-client-id'}, JSON::true, 'DHCPv4 client-id matching can be explicitly restored' );
my $comment_dir = tempdir(CLEANUP => 1);
my $commented_config = "$comment_dir/kea-dhcp4.conf";
my $commented_content = <<'COMMENTED_JSON';
@@ -220,9 +230,21 @@ my $commented_content = <<'COMMENTED_JSON';
{
"id": 1,
"subnet": "10.20.0.0/24",
"pools": [] // Keep URLs such as "http://server/path" intact.
"pools": [], // Keep URLs such as "http://server/path" intact.
}
]
],
"loggers": [
{
"name": "kea-dhcp4",
"output-options": [
{
"output": "stdout",
"pattern": "%-5p %m\n",
// "flush": false
},
],
},
],
}
}
COMMENTED_JSON
@@ -374,6 +396,27 @@ is( $ddns_config->{DhcpDdns}{'forward-ddns'}{'ddns-domains'}[0]{name}, 'cluster.
my $ctrl_agent_config = decode_json($backend->render_ctrl_agent_config({ 'http-port' => '8000' }));
is( $ctrl_agent_config->{'Control-agent'}{'http-port'}, 8000, 'Control Agent HTTP port is numeric' );
my $runtime_socket_dir = "$unit_dir/run/kea";
mkdir "$unit_dir/run" or die "Unable to create $unit_dir/run: $!";
mkdir $runtime_socket_dir or die "Unable to create $runtime_socket_dir: $!";
my $runtime_socket_backend = xCAT::DHCP::Backend::Kea->new( kea_socket_dirs => [ $runtime_socket_dir, "$unit_dir/var/run/kea" ] );
my $runtime_socket_config = decode_json($runtime_socket_backend->render_ctrl_agent_config({}));
is( $runtime_socket_config->{'Control-agent'}{'control-sockets'}{dhcp4}{'socket-name'}, "$runtime_socket_dir/kea4-ctrl-socket", 'Control Agent socket uses the detected runtime directory' );
my $legacy_socket_backend = xCAT::DHCP::Backend::Kea->new( kea_socket_dirs => [] );
my $legacy_socket_config = decode_json($legacy_socket_backend->render_ctrl_agent_config({}));
is( $legacy_socket_config->{'Control-agent'}{'control-sockets'}{dhcp4}{'socket-name'}, '/var/run/kea/kea4-ctrl-socket', 'Control Agent socket falls back to the legacy runtime path when no runtime directory exists' );
my $socket_backend = xCAT::DHCP::Backend::Kea->new( kea_socket_dir => '/run/kea' );
my $ctrl_agent_socket_config = decode_json($socket_backend->render_ctrl_agent_config({ dhcp6 => 1, ddns => 1 }));
is( $ctrl_agent_socket_config->{'Control-agent'}{'control-sockets'}{dhcp4}{'socket-name'}, '/run/kea/kea4-ctrl-socket', 'Control Agent DHCPv4 socket uses the detected Kea socket directory' );
is( $ctrl_agent_socket_config->{'Control-agent'}{'control-sockets'}{dhcp6}{'socket-name'}, '/run/kea/kea6-ctrl-socket', 'Control Agent DHCPv6 socket uses the detected Kea socket directory' );
is( $ctrl_agent_socket_config->{'Control-agent'}{'control-sockets'}{d2}{'socket-name'}, '/run/kea/kea-ddns-ctrl-socket', 'Control Agent DDNS socket uses the detected Kea socket directory' );
my $explicit_socket_config = decode_json($socket_backend->render_ctrl_agent_config({ 'dhcp4-socket' => '/tmp/kea4.sock' }));
is( $explicit_socket_config->{'Control-agent'}{'control-sockets'}{dhcp4}{'socket-name'}, '/tmp/kea4.sock', 'explicit Control Agent socket path overrides the default' );
my @commands;
my $ca_backend = xCAT::DHCP::Backend::Kea->new(
control_agent_handler => sub {

View File

@@ -0,0 +1,48 @@
#!/usr/bin/env perl
use strict;
use warnings;
use FindBin;
use File::Spec;
use Test::More;
my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' );
my %scripts = (
'legacy install post.xcat' => File::Spec->catfile(
$repo_root,
'xCAT-server/share/xcat/install/scripts/post.xcat'
),
'legacy netboot xcatdsklspost' => File::Spec->catfile(
$repo_root,
'xCAT/postscripts/xcatdsklspost'
),
);
foreach my $path ( values %scripts ) {
plan skip_all => "$path not found" unless -r $path;
}
foreach my $name ( sort keys %scripts ) {
my $path = $scripts{$name};
open( my $fh, '<', $path ) or die "Unable to read $path: $!";
my $script = do { local $/; <$fh> };
close($fh);
like(
$script,
qr/--reject\s+"index\.html\*,post\.xcat\.ng,post\.xcat\.rhels10"/,
"$name recursive wget ignores dispatcher scripts that contain literal HTML-link regexes"
);
like(
$script,
qr/Newer\s+(?:#\s+)?wget\s+parses\s+HTML-looking\s+regex\s+strings/s,
"$name download policy documents why dispatcher scripts are excluded"
);
unlike(
$script,
qr/<a\s+href=/i,
"$name comments do not contain HTML links that wget can recurse into"
);
}
done_testing();

View File

@@ -0,0 +1,91 @@
use strict;
use warnings;
use FindBin;
use File::Spec;
use Test::More;
my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' );
my @pkglist_files = qw(
xCAT-server/share/xcat/install/ubuntu/compute.ubuntu26.04.ppc64el.pkglist
xCAT-server/share/xcat/install/ubuntu/compute.ubuntu26.04.x86_64.pkglist
xCAT-server/share/xcat/netboot/ubuntu/compute.ubuntu26.04.ppc64el.pkglist
xCAT-server/share/xcat/netboot/ubuntu/compute.ubuntu26.04.x86_64.pkglist
);
foreach my $file (@pkglist_files) {
my $path = File::Spec->catfile( $repo_root, $file );
ok( -r $path, "$file exists" );
open( my $fh, '<', $path ) or die "Unable to read $path: $!";
my @packages = grep { /\S/ && !/^\s*#/ } map { chomp; $_ } <$fh>;
close($fh);
my %packages = map { $_ => 1 } @packages;
ok( $packages{'bind9-dnsutils'}, "$file uses bind9-dnsutils" );
ok( $packages{'chrony'}, "$file uses chrony" );
ok( !$packages{'dnsutils'}, "$file avoids removed dnsutils package" );
ok( !$packages{'ntp'}, "$file avoids removed ntp package" );
ok( !$packages{'ntpdate'}, "$file avoids removed ntpdate package" );
}
foreach my $file (qw(
xCAT-server/share/xcat/install/ubuntu/compute.ubuntu26.04.ppc64le.pkglist
xCAT-server/share/xcat/netboot/ubuntu/compute.ubuntu26.04.ppc64le.pkglist
)) {
my $path = File::Spec->catfile( $repo_root, $file );
ok( -l $path, "$file aliases ppc64le to ppc64el" );
}
my $subiquity_template = File::Spec->catfile(
$repo_root,
'xCAT-server/share/xcat/install/ubuntu/compute.subiquity.tmpl'
);
open( my $tmpl_fh, '<', $subiquity_template ) or die "Unable to read $subiquity_template: $!";
my $template = do { local $/; <$tmpl_fh> };
close($tmpl_fh);
like( $template, qr/\n\s+- bind9-dnsutils\n/, 'subiquity template uses bind9-dnsutils' );
unlike( $template, qr/\n\s+- dnsutils\n/, 'subiquity template avoids removed dnsutils package' );
like( $template, qr/\n\s+- "root:#CRYPTORLOCKED:passwd:key=system,username=root:password#"\n/, 'subiquity uses a locked root password marker when unset' );
like( $template, qr/#UBUNTU_SUBIQUITY_APT_CONFIG#/, 'subiquity apt configuration is rendered from osimage package sources' );
like( $template, qr/package_update: false/, 'subiquity install does not require online package update' );
like( $template, qr/package_upgrade: false/, 'subiquity install does not require online package upgrade' );
my $template_module = File::Spec->catfile( $repo_root, 'xCAT-server/lib/perl/xCAT/Template.pm' );
open( my $module_fh, '<', $template_module ) or die "Unable to read $template_module: $!";
my $module = do { local $/; <$module_fh> };
close($module_fh);
like( $module, qr/URIs: http:\/\/xcat\.invalid\/disabled.*Enabled: no/s, 'subiquity renderer disables duplicate archive sources when Subiquity provides cdrom.sources' );
like( $module, qr/sources_list: \|/, 'subiquity renderer owns Deb822 install media sources when Subiquity does not provide cdrom.sources' );
like( $module, qr/URIs: file:\/\/\/cdrom/, 'subiquity renderer can use the mounted install media as the primary mirror' );
like( $module, qr/Check-Date: no/, 'subiquity renderer avoids cdrom Check-Date conflicts' );
like( $module, qr/fallback: offline-install/, 'subiquity renderer can complete without external apt mirrors' );
like( $module, qr/geoip: false/, 'subiquity renderer does not require external geoip lookup' );
like( $module, qr/- updates.*- backports.*- security/s, 'subiquity renderer disables online update suites' );
like( $module, qr/mirror-selection:/, 'subiquity renderer keeps classic mirror-selection fallback for older Ubuntu releases' );
like( $module, qr/Types: deb.*URIs: \$source.*Suites: \.\/.*Components:.*Trusted: yes/s, 'subiquity renderer includes trusted local xCAT otherpkgdir repositories in Deb822 sources_list' );
like( $module, qr/deb \[trusted=yes\] \$source \.\/"/, 'subiquity renderer keeps classic source-list fallback for older Ubuntu releases' );
like( $module, qr/-f "\$path\/Release"/, 'subiquity renderer requires indexed otherpkgdir repositories' );
my $subiquity_pre = File::Spec->catfile(
$repo_root,
'xCAT-server/share/xcat/install/scripts/pre.ubuntu.subiquity'
);
open( my $pre_fh, '<', $subiquity_pre ) or die "Unable to read $subiquity_pre: $!";
my $pre = do { local $/; <$pre_fh> };
close($pre_fh);
like( $pre, qr/id: efi-part\s+type: partition\s+device: disk-detected\s+size: 512M\s+flag: boot\s+number: 1\s+preserve: false\s+grub_device: true/s, 'subiquity UEFI storage marks the EFI partition as grub device' );
like( $pre, qr/id: efi-part-fs\s+type: format\s+fstype: fat32\s+volume: efi-part/s, 'subiquity UEFI storage formats ESP as fat32' );
my $repo_builder = File::Spec->catfile( $repo_root, 'build-ubunturepo' );
open( my $builder_fh, '<', $repo_builder ) or die "Unable to read $repo_builder: $!";
my $builder = do { local $/; <$builder_fh> };
close($builder_fh);
like( $builder, qr/dists="\$\{DISTS:-[^"]*\bresolute\b[^"]*\}"/, 'Ubuntu repo builder includes resolute by default' );
done_testing();

View File

@@ -18,11 +18,11 @@ 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');
like($tmpl, qr/#UBUNTU_SUBIQUITY_APT_CONFIG#/, 'template renders apt section from osimage context');
unlike($tmpl, qr/^\s*apt:/m, 'template does not carry a static apt section');
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');
unlike($tmpl, qr/geoip:\s*true/, 'template does not enable geoip');
like($tmpl, qr/ssh:/, 'template has ssh section');
like($tmpl, qr/install-server:\s*true/, 'template enables ssh install-server');
@@ -57,10 +57,9 @@ unlike($tmpl, qr/wget .*?\|\| true/, 'xCAT control artifact downloads are not ma
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');
# Regression: apt configuration is generated by Template.pm so release-specific
# Subiquity behavior can be handled without cloning this template per release.
unlike($tmpl, qr/noble-|jammy-|focal-/, 'template avoids release-specific apt suite names');
like($tmpl, qr/#UBUNTU_SUBIQUITY_APT_CONFIG#/, 'template keeps dynamic apt renderer marker');
done_testing();

View File

@@ -108,7 +108,10 @@ download_postscripts()
rm -rf "$xcatpost"
fi
export LANG=C; wget -l inf -nH -N -r --waitretry=10 --random-wait -e robots=off -T 60 -nH --cut-dirs=2 --reject "index.html*" --no-parent http://$server$INSTALLDIR/postscripts/ -P /$xcatpost 2> /tmp/wget.log
# These dispatcher scripts are not needed by the legacy netboot post
# path. Newer wget parses HTML-looking regex strings inside downloaded
# scripts and fails the whole recursive download on bogus URLs.
export LANG=C; wget -l inf -nH -N -r --waitretry=10 --random-wait -e robots=off -T 60 -nH --cut-dirs=2 --reject "index.html*,post.xcat.ng,post.xcat.rhels10" --no-parent http://$server$INSTALLDIR/postscripts/ -P /$xcatpost 2> /tmp/wget.log
rc=$?
if [ $rc -eq 0 ]; then
# return from wget was 0 but some OS do not return errors, so we