diff --git a/xCAT-test/integration/dhcp_isc_config_validation.t b/xCAT-test/integration/dhcp_isc_config_validation.t new file mode 100644 index 000000000..35dc3bf0e --- /dev/null +++ b/xCAT-test/integration/dhcp_isc_config_validation.t @@ -0,0 +1,52 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use FindBin; +use lib "$FindBin::Bin/../../perl-xCAT"; + +use File::Spec; +use File::Temp qw(tempdir); +use Test::More; +use xCAT::DHCP::BootPolicy; + +my ($dhcpd) = grep { -x $_ } qw(/usr/sbin/dhcpd /usr/local/sbin/dhcpd); +plan skip_all => 'dhcpd is required for ISC configuration validation' unless $dhcpd; +plan skip_all => 'root is required to validate from the ISC configuration directory' + unless $> == 0; + +my @config = ( + "#xCAT generated dhcp configuration\n", + "\n", + "option conf-file code 209 = text;\n", + "option user-class-identifier code 77 = string;\n", + "option client-architecture code 93 = unsigned integer 16;\n", + "option www-server code 114 = string;\n", + "default-lease-time 600;\n", + "max-lease-time 600;\n", + "subnet 192.0.2.0 netmask 255.255.255.0 {\n", + " range 192.0.2.100 192.0.2.110;\n", +); + +ok( + xCAT::DHCP::BootPolicy->ensure_isc_path_prefix_definition(\@config), + 'the upgrade path adds option 210 to an existing configuration', +); +push @config, @{ xCAT::DHCP::BootPolicy->isc_client_architecture_lines( + next_server => '192.0.2.1', + portsuffix => '', + net => '192.0.2.0', + prefix => 24, + ) }, "}\n"; + +my $configuration_root = -d '/etc/dhcp' ? '/etc/dhcp' : '/etc'; +my $directory = tempdir(DIR => $configuration_root, CLEANUP => 1); +my $path = File::Spec->catfile($directory, 'dhcpd.conf'); +open(my $config_file, '>', $path) or die "Cannot create $path: $!"; +print {$config_file} @config; +close($config_file) or die "Cannot close $path: $!"; + +my $status = system($dhcpd, '-t', '-cf', $path); +is($status, 0, 'ISC accepts the upgraded s390x boot policy'); + +done_testing(); diff --git a/xCAT-test/integration/dhcp_kea_config_validation.t b/xCAT-test/integration/dhcp_kea_config_validation.t index 5afecaedc..125189705 100644 --- a/xCAT-test/integration/dhcp_kea_config_validation.t +++ b/xCAT-test/integration/dhcp_kea_config_validation.t @@ -24,6 +24,7 @@ my $json = $backend->render_dhcp4_config( interfaces => ['*'], 'option-def' => [ { name => 'conf-file', code => 209, type => 'string', space => 'dhcp4' }, + { name => 'path-prefix', code => 210, type => 'string', space => 'dhcp4' }, { name => 'iscsi-initiator-iqn', code => 203, type => 'string', space => 'dhcp4' }, { name => 'cumulus-provision-url', code => 239, type => 'string', space => 'dhcp4' }, ], @@ -41,6 +42,15 @@ my $json = $backend->render_dhcp4_config( { name => 'conf-file', data => 'http://192.168.122.1:80/tftpboot/pxelinux.cfg/p/192.168.122.0_24' }, ], }, + { + name => 'xcat-s390x-qemu-192.168.122.0_24', + test => 'option[93].hex == 0x001f', + additional_only => JSON::true, + 'option-data' => [ + { name => 'conf-file', data => '192.168.122.0_24' }, + { name => 'path-prefix', data => 'pxelinux.cfg/s390x/' }, + ], + }, { name => 'xcat-uefi-x64', test => "(option[93].hex == 0x0007 or option[93].hex == 0x0009 or option[93].hex == 0x0010) and not ((option[77].exists and (option[77].text == 'xNBA' or option[77].hex == 0x784e4241 or substring(option[77].hex,1,4) == 'xNBA')))", @@ -65,7 +75,10 @@ my $json = $backend->render_dhcp4_config( subnet => '192.168.122.0/24', dynamicrange => '192.168.122.100-192.168.122.120', next_server => '192.168.122.1', - additional_client_classes => ['xcat-opal-v3-192.168.122.0-24'], + additional_client_classes => [ + 'xcat-opal-v3-192.168.122.0-24', + 'xcat-s390x-qemu-192.168.122.0_24', + ], option_data => [ { name => 'routers', data => '192.168.122.1' }, { name => 'domain-name', data => 'cluster.test' }, diff --git a/xCAT-test/unit/dhcp_boot_policy.t b/xCAT-test/unit/dhcp_boot_policy.t index e06aafdda..7dd200a1b 100644 --- a/xCAT-test/unit/dhcp_boot_policy.t +++ b/xCAT-test/unit/dhcp_boot_policy.t @@ -217,4 +217,63 @@ unlike( 'the global class list keeps HTTP boot out: it needs the address of the management node', ); +my $s390x = xCAT::DHCP::BootPolicy->kea_s390x_network_classes( + net => '10.0.0.0', + prefix => 24, +); +is_deeply( + $s390x, + [ + { + name => 'xcat-s390x-qemu-10.0.0.0_24', + test => 'option[93].hex == 0x001f', + additional_only => 1, + 'option-data' => [ + { + name => 'conf-file', + data => '10.0.0.0_24', + 'always-send' => 1, + }, + { + name => 'path-prefix', + data => 'pxelinux.cfg/s390x/', + 'always-send' => 1, + }, + ], + }, + ], + 'QEMU s390x stays within its architecture-specific configuration path', +); +is_deeply( + xCAT::DHCP::BootPolicy->kea_s390x_network_classes(prefix => 24), + [], + 's390x classes require a network', +); + +my @legacy_isc_config = ( + "#xCAT generated dhcp configuration\n", + "\n", + "option conf-file code 209 = text;\n", +); +ok( + xCAT::DHCP::BootPolicy->ensure_isc_path_prefix_definition( + \@legacy_isc_config + ), + 'an older ISC configuration receives the path-prefix definition', +); +is( + $legacy_isc_config[1], + "option path-prefix code 210 = text;\n", + 'the definition is inserted in the global configuration header', +); +ok( + !xCAT::DHCP::BootPolicy->ensure_isc_path_prefix_definition( + \@legacy_isc_config + ), + 'an existing path-prefix definition is preserved', +); +my $definition_count = + grep { /^\s*option\s+path-prefix\s+code\s+210\b/ } @legacy_isc_config; +is($definition_count, 1, 'repeated updates do not duplicate the definition'); + done_testing(); diff --git a/xCAT-test/unit/dhcp_isc_client_arch.t b/xCAT-test/unit/dhcp_isc_client_arch.t index 8cec79091..dc584f8b2 100644 --- a/xCAT-test/unit/dhcp_isc_client_arch.t +++ b/xCAT-test/unit/dhcp_isc_client_arch.t @@ -36,6 +36,11 @@ like( qr/option conf-file = "http:\/\/192\.0\.2\.10:8080\/tftpboot\/pxelinux\.cfg\/p\/192\.0\.2\.0_24";/, 'the existing OPAL branch keeps its subnet URL', ); +like( + $rendered, + qr/client-architecture = 00:1f \{ #QEMU s390x\n\s+option path-prefix = "pxelinux\.cfg\/s390x\/";\n\s+option conf-file = "192\.0\.2\.0_24";/, + 'QEMU s390x receives its subnet configuration and fallback path', +); my @riscv_ids = $rendered =~ /client-architecture = (00:1[9a-e])/g; is_deeply( @@ -47,12 +52,14 @@ is_deeply( my $aarch64_pos = index($rendered, 'client-architecture = 00:0b'); my $tftp_pos = index($rendered, 'client-architecture = 00:1b'); my $http_pos = index($rendered, 'client-architecture = 00:1c'); +my $s390_qemu_pos = index($rendered, 'client-architecture = 00:1f'); my $opal_pos = index($rendered, 'client-architecture = 00:0e'); my $fallback_pos = index($rendered, 'substring(filename,0,1) = null'); cmp_ok($aarch64_pos, '<', $tftp_pos, 'riscv64 follows the aarch64 branch'); cmp_ok($tftp_pos, '<', $http_pos, 'the TFTP branch precedes the HTTP branch'); -cmp_ok($http_pos, '<', $opal_pos, 'the HTTP branch precedes the OPAL branch'); +cmp_ok($http_pos, '<', $s390_qemu_pos, 's390x follows the RISC-V HTTP branch'); +cmp_ok($s390_qemu_pos, '<', $opal_pos, 's390x precedes the OPAL branch'); cmp_ok($http_pos, '<', $fallback_pos, 'the HTTP branch is reachable before the fallback'); like($rendered, qr/filename "\/yaboot";\n\s*\}\n\z/, 'the policy ends with the existing yaboot fallback'); diff --git a/xCAT-test/unit/dhcp_kea_plugin_intent.t b/xCAT-test/unit/dhcp_kea_plugin_intent.t index 4e338ae6d..eb117d740 100644 --- a/xCAT-test/unit/dhcp_kea_plugin_intent.t +++ b/xCAT-test/unit/dhcp_kea_plugin_intent.t @@ -120,6 +120,38 @@ my %network_entry = ( tftpserver => '', ); +{ + no warnings 'redefine'; + local *xCAT::NetworkUtils::thishostisnot = sub { return 0; }; + my $nettab = DHCPKeaIntentNetTable->new(\%network_entry); + my $subnet = xCAT_plugin::dhcp::kea_subnet4_intent( + $nettab, '10.0.0.0', '255.255.255.0', 'eth0', 0, 1, 80 + ); + my %classes = map { $_->{name} => $_ } @{ $subnet->{client_classes} }; + ok($classes{'xcat-s390x-qemu-10.0.0.0_24'}, 'the Kea subnet includes QEMU s390x boot policy'); + is_deeply( + [ grep { /^xcat-s390x-/ } @{ $subnet->{additional_client_classes} } ], + ['xcat-s390x-qemu-10.0.0.0_24'], + 'the s390x policy is evaluated only for its subnet', + ); + is_deeply( + $classes{'xcat-s390x-qemu-10.0.0.0_24'}{'option-data'}, + [ + { + name => 'conf-file', + data => '10.0.0.0_24', + 'always-send' => 1, + }, + { + name => 'path-prefix', + data => 'pxelinux.cfg/s390x/', + 'always-send' => 1, + }, + ], + 'the rendered subnet keeps s390x fallback lookups in their own path', + ); +} + my @sysconfig_policy_cases = ( [ 'sles10', 0, 'SLES 10' ], [ 'sles11', 1, 'SLES 11' ], diff --git a/xCAT-test/unit/genesis_openembedded_activation.t b/xCAT-test/unit/genesis_openembedded_activation.t index 7aeef2c5c..9fd0e6e1a 100644 --- a/xCAT-test/unit/genesis_openembedded_activation.t +++ b/xCAT-test/unit/genesis_openembedded_activation.t @@ -14,6 +14,7 @@ my $rpm_weak_dependencies = join( 'Recommends: xCAT-genesis-openembedded-x86_64', 'Recommends: xCAT-genesis-openembedded-ppc64le', 'Recommends: xCAT-genesis-openembedded-riscv64', + 'Recommends: xCAT-genesis-openembedded-s390x', '%endif', ); @@ -45,6 +46,11 @@ like( qr/^Recommends:.*\bxcat-genesis-openembedded-riscv64\b/m, 'DEB installations recommend the first-class riscv64 image', ); +like( + $deb_control, + qr/^Recommends:.*\bxcat-genesis-openembedded-s390x\b/m, + 'DEB installations recommend the s390x image', +); unlike( $deb_control, qr/^Depends:.*\bxcat-genesis-openembedded-/m, @@ -74,6 +80,11 @@ like( qr/^Recommends:.*\bxcat-genesis-openembedded-riscv64\b/m, 'DEB service nodes recommend the first-class riscv64 image', ); +like( + $sn_deb_control, + qr/^Recommends:.*\bxcat-genesis-openembedded-s390x\b/m, + 'DEB service nodes recommend the s390x image', +); my $go_xcat = slurp_repo_file('xCAT-server/share/xcat/tools/go-xcat'); unlike( @@ -81,7 +92,7 @@ unlike( qr/GO_XCAT_LIBRARY_ONLY/, 'go-xcat cannot be disabled by an inherited test environment variable', ); -for my $architecture (qw(x86 x86_64 ppc64 ppc64le armv7hf aarch64 riscv64)) { +for my $architecture (qw(x86 x86_64 ppc64 ppc64le armv7hf aarch64 riscv64 s390x)) { like( $go_xcat, qr/\bxCAT-genesis-openembedded-\Q$architecture\E\b/, diff --git a/xCAT-test/unit/mknb_exported_genesis.t b/xCAT-test/unit/mknb_exported_genesis.t index faaf980a2..1cd01ada5 100644 --- a/xCAT-test/unit/mknb_exported_genesis.t +++ b/xCAT-test/unit/mknb_exported_genesis.t @@ -156,6 +156,29 @@ ok(!-e "$tftpdir/xcat/genesis.exact-arch.ppc64le", ok(-f "$tftpdir/xcat/genesis.kernel.ppc64", 'retiring ppc64le does not remove the legacy ppc64 fallback'); +for my $artifact (qw( + genesis.kernel.s390x + genesis.fs.s390x.gz + genesis.fs.s390x.lzma + genesis.exact-arch.s390x +)) { + write_file("$tftpdir/xcat/$artifact", 'stale artifact'); +} +make_path("$tftpdir/pxelinux.cfg/s390x"); +write_file( + "$tftpdir/pxelinux.cfg/s390x/192.0.2.0_24", + "# pxelinux.cfg xCAT Genesis s390x\nstale config\n", +); +write_file("$tftpdir/pxelinux.cfg/s390x/default", "admin fallback\n"); +($removed, $remove_error) = + xCAT_plugin::mknb::_remove_openembedded_genesis($tftpdir, 's390x'); +is($remove_error, undef, 's390x boot artifacts can be retired cleanly'); +is($removed, 5, 's390x image and generated discovery configuration are retired'); +ok(!-e "$tftpdir/pxelinux.cfg/s390x/192.0.2.0_24", + 'retiring s390x removes the generated discovery configuration'); +is(read_file("$tftpdir/pxelinux.cfg/s390x/default"), "admin fallback\n", + 'retiring s390x preserves administrator-owned configurations'); + my $failed_kernel = "$tftpdir/xcat/genesis.kernel.aarch64"; make_path($failed_kernel); for my $artifact (qw( diff --git a/xCAT-test/unit/mknb_xcatd_address.t b/xCAT-test/unit/mknb_xcatd_address.t index aa2c77d4b..fc641e58a 100644 --- a/xCAT-test/unit/mknb_xcatd_address.t +++ b/xCAT-test/unit/mknb_xcatd_address.t @@ -508,4 +508,83 @@ ok( %xCAT::TableUtils::site_extra = (); $xCAT::NetworkUtils::nic_ips = undef; +make_path("$::XCATROOT/share/xcat/netboot/genesis-openembedded/s390x"); +use_reporter_address_maps(); +prepare_tftpdir($tmpdir, 'tftpboot-s390x', 's390x'); +$responses = run_mknb('s390x'); +generation_succeeded($responses, 's390x configuration generation succeeds'); + +my $s390x_qemu_path = + "$xCAT::TableUtils::tftpdir/pxelinux.cfg/s390x/192.168.144.0_20"; +ok(-f $s390x_qemu_path, 's390x writes a QEMU network IPL configuration'); +is( + read_config($s390x_qemu_path), + "# pxelinux.cfg xCAT Genesis s390x\n" + . "DEFAULT xCAT\n" + . "LABEL xCAT\n" + . " KERNEL xcat/genesis.kernel.s390x\n" + . " INITRD xcat/genesis.fs.s390x.gz\n" + . " APPEND xcatd=192.168.148.10:3001 xcat.bootloader=s390-ccw console=ttysclp0\n", + 'the QEMU configuration selects Genesis and the SCLP console', +); +unlike( + read_config($s390x_qemu_path), + qr/192\.168\.149\.100/, + 's390x configurations do not use the later floating address', +); + +write_text($s390x_qemu_path, "admin network configuration\n"); +%xCAT::TableUtils::site_extra = ( dhcpinterfaces => 'eth0,eth1:noboot' ); +$xCAT::NetworkUtils::nic_ips = { eth0 => '10.0.0.1', eth1 => '192.168.148.10' }; +$responses = run_mknb('s390x'); +generation_succeeded($responses, 's390x configuration generation accepts a :noboot interface'); +is( + read_config($s390x_qemu_path), + "admin network configuration\n", + 'a :noboot interface preserves an administrator-owned s390x configuration', +); +%xCAT::TableUtils::site_extra = (); +$xCAT::NetworkUtils::nic_ips = undef; +$responses = run_mknb('s390x'); +generation_succeeded($responses, 's390x configuration is restored after removing :noboot'); + +my $s390x_failure_root = "$tmpdir/tftpboot-s390x-failure"; +make_path("$s390x_failure_root/pxelinux.cfg/s390x/192.168.144.0_20"); +my (undef, $s390x_write_error) = + xCAT_plugin::mknb::_write_s390x_discovery_config( + tftpdir => $s390x_failure_root, + network => '192.168.144.0_20', + xcatd_address => '192.168.148.10', + xcatdport => 3001, + consolecmdline => 'console=ttysclp0', + initrd => "$s390x_failure_root/xcat/genesis.fs.s390x.gz", + ); +like( + $s390x_write_error, + qr/^Unable to write s390x Genesis configuration:/, + 's390x configuration write failures are reported', +); + +%xCAT::TableUtils::site_extra = ( + defserialport => '2', + defserialspeed => '9600', + defserialflow => 'hard', + xcatdport => '3002', +); +$responses = run_mknb('s390x'); +generation_succeeded($responses, 's390x ignores PC serial settings'); +like( + read_config($s390x_qemu_path), + qr/^ APPEND xcatd=192\.168\.148\.10:3002 xcat\.bootloader=s390-ccw console=ttysclp0$/m, + 's390x keeps its SCLP console and the configured xcatd port', +); + +%xCAT::TableUtils::site_extra = ( dhcpinterfaces => 'eth0,eth1:noboot' ); +$xCAT::NetworkUtils::nic_ips = { eth0 => '10.0.0.1', eth1 => '192.168.148.10' }; +$responses = run_mknb('s390x'); +generation_succeeded($responses, 's390x configuration generation honors :noboot'); +ok(!-e $s390x_qemu_path, 'a :noboot network gets no QEMU s390x configuration'); +%xCAT::TableUtils::site_extra = (); +$xCAT::NetworkUtils::nic_ips = undef; + done_testing(); diff --git a/xCAT-test/unit/xcatconfig_genesis_openembedded.t b/xCAT-test/unit/xcatconfig_genesis_openembedded.t index b59d6eba6..59c8fc3e7 100644 --- a/xCAT-test/unit/xcatconfig_genesis_openembedded.t +++ b/xCAT-test/unit/xcatconfig_genesis_openembedded.t @@ -27,6 +27,7 @@ make_path( "$tmpdir/share/xcat/netboot/genesis/x86_64/fs", "$tmpdir/share/xcat/netboot/genesis-openembedded/aarch64", "$tmpdir/share/xcat/netboot/genesis-openembedded/ppc64le", + "$tmpdir/share/xcat/netboot/genesis-openembedded/s390x", "$tmpdir/share/xcat/netboot/genesis-openembedded/x86_64", ); my $symlink_target = "$tmpdir/openembedded-riscv64"; @@ -38,25 +39,25 @@ symlink( is_deeply( [ _installed_genesis_architectures($tmpdir) ], - [ qw(aarch64 ppc64 ppc64le x86_64) ], + [ qw(aarch64 ppc64 ppc64le s390x x86_64) ], 'xcatconfig ignores linked OpenEmbedded images and finds legacy images', ); is_deeply( [ _genesis_architectures_to_build($tmpdir, 0) ], - [ qw(aarch64 ppc64le x86_64) ], + [ qw(aarch64 ppc64le s390x x86_64) ], 'an ordinary xCAT update rebuilds only installed OpenEmbedded images', ); is_deeply( [ _genesis_architectures_to_build($tmpdir, 1) ], - [ qw(aarch64 ppc64 ppc64le x86_64) ], + [ qw(aarch64 ppc64 ppc64le s390x x86_64) ], 'a legacy package trigger rebuilds every installed Genesis image', ); make_path("$tmpdir/share/xcat/netboot/genesis-openembedded/unsupported"); is_deeply( [ _installed_genesis_architectures($tmpdir) ], - [ qw(aarch64 ppc64 ppc64le x86_64) ], + [ qw(aarch64 ppc64 ppc64le s390x x86_64) ], 'unknown directories are not passed to mknb', ); @@ -67,7 +68,7 @@ symlink($legacy_target, "$tmpdir/share/xcat/netboot/genesis/ppc64") or die "create legacy directory symlink: $!"; is_deeply( [ _installed_genesis_architectures($tmpdir) ], - [ qw(aarch64 ppc64 ppc64le x86_64) ], + [ qw(aarch64 ppc64 ppc64le s390x x86_64) ], 'legacy Genesis directory symlinks remain supported', );