From 3e302e7f192778a30bc04a84570d247fd3ddc440 Mon Sep 17 00:00:00 2001 From: Daniel Hilst <392820+dhilst@users.noreply.github.com> Date: Tue, 8 Sep 2026 12:11:42 -0300 Subject: [PATCH] test(xcat-core): Introduce BATS & convert shell scripting tests to it The go-xcat shell behavior tests were written as Perl harnesses, which made the shell assertions harder to read and kept shell-specific setup outside a native shell test framework. Add BATS to the GitHub Actions dependency set, run BATS tests from the same preserved source tree as the Perl unit suite, and move the go-xcat repository checks into xCAT-test/autotest/bats. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> --- .github/workflows/xcat_test.yml | 2 +- github_action_xcat_test.pl | 49 ++++- .../dracut_105/el/module-setup.sh | 13 +- .../dracut_105/ubuntu/module-setup.sh | 13 +- .../share/xcat/install/scripts/post.xcat | 5 +- .../share/xcat/install/scripts/pre.sles | 11 - .../share/xcat/install/scripts/scriptlib | 24 ++ xCAT-test/README.md | 20 ++ xCAT-test/autotest/bats/README.md | 21 ++ .../autotest/bats/genesis_ib_modules.bats | 118 ++++++++++ .../bats/go_xcat_common_repository.bats | 122 +++++++++++ .../autotest/bats/go_xcat_el_repo_check.bats | 206 ++++++++++++++++++ xCAT-test/autotest/bats/helpers/go_xcat.bash | 56 +++++ .../autotest/bats/helpers/shell_source.bash | 132 +++++++++++ .../bats/post_xcat_download_policy.bats | 76 +++++++ .../autotest/bats/remoteshell_restart.bats | 77 +++++++ xCAT-test/autotest/bats/sles_pre_script.bats | 45 ++++ .../autotest/bats/statelite_add_ssh.bats | 40 ++++ xCAT-test/unit/README.md | 5 + xCAT-test/unit/genesis_ib_modules.t | 40 ---- xCAT-test/unit/go_xcat_common_repository.t | 165 -------------- xCAT-test/unit/go_xcat_el_repo_check.t | 161 -------------- xCAT-test/unit/post_xcat_download_policy.t | 48 ---- xCAT-test/unit/remoteshell_kill_signal.t | 26 --- xCAT-test/unit/remoteshell_kill_wait.t | 62 ------ xCAT-test/unit/sles_pre_script.t | 20 -- xCAT-test/unit/statelite_add_ssh.t | 17 -- xCAT/postscripts/remoteshell | 16 +- xCAT/postscripts/xcatdsklspost | 67 +----- xCAT/postscripts/xcatlib.sh | 101 +++++++++ 30 files changed, 1113 insertions(+), 645 deletions(-) create mode 100644 xCAT-test/README.md create mode 100644 xCAT-test/autotest/bats/README.md create mode 100644 xCAT-test/autotest/bats/genesis_ib_modules.bats create mode 100644 xCAT-test/autotest/bats/go_xcat_common_repository.bats create mode 100644 xCAT-test/autotest/bats/go_xcat_el_repo_check.bats create mode 100644 xCAT-test/autotest/bats/helpers/go_xcat.bash create mode 100644 xCAT-test/autotest/bats/helpers/shell_source.bash create mode 100644 xCAT-test/autotest/bats/post_xcat_download_policy.bats create mode 100644 xCAT-test/autotest/bats/remoteshell_restart.bats create mode 100644 xCAT-test/autotest/bats/sles_pre_script.bats create mode 100644 xCAT-test/autotest/bats/statelite_add_ssh.bats delete mode 100644 xCAT-test/unit/genesis_ib_modules.t delete mode 100644 xCAT-test/unit/go_xcat_common_repository.t delete mode 100644 xCAT-test/unit/go_xcat_el_repo_check.t delete mode 100644 xCAT-test/unit/post_xcat_download_policy.t delete mode 100644 xCAT-test/unit/remoteshell_kill_signal.t delete mode 100644 xCAT-test/unit/remoteshell_kill_wait.t delete mode 100644 xCAT-test/unit/sles_pre_script.t delete mode 100644 xCAT-test/unit/statelite_add_ssh.t diff --git a/.github/workflows/xcat_test.yml b/.github/workflows/xcat_test.yml index 0fe2cd911..04d50aff0 100644 --- a/.github/workflows/xcat_test.yml +++ b/.github/workflows/xcat_test.yml @@ -7,7 +7,7 @@ jobs: steps: - uses: actions/checkout@v6 - name: Install dependencies - run: sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends --no-install-suggests build-essential fakeroot reprepro devscripts debhelper libcapture-tiny-perl libfile-slurper-perl libjson-perl libparallel-forkmanager-perl libsoap-lite-perl libdbi-perl libcgi-pm-perl quilt openssh-server dpkg looptools genometools software-properties-common + run: sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends --no-install-suggests bats build-essential fakeroot reprepro devscripts debhelper libcapture-tiny-perl libfile-slurper-perl libjson-perl libparallel-forkmanager-perl libsoap-lite-perl libdbi-perl libcgi-pm-perl quilt openssh-server dpkg looptools genometools software-properties-common - name: Run tests run: perl github_action_xcat_test.pl diff --git a/github_action_xcat_test.pl b/github_action_xcat_test.pl index f4480771b..42d565885 100644 --- a/github_action_xcat_test.pl +++ b/github_action_xcat_test.pl @@ -311,8 +311,11 @@ sub preserve_source_tree{ return 1; } - @output = runcmd("ls $unitsrc/xCAT-test/unit/*.t | wc -l"); - print "[preserve_source_tree] preserved $srcdir in $unitsrc ($output[0] unit tests)\n"; + @output = runcmd("find $unitsrc/xCAT-test/unit -name '*.t' | wc -l"); + my $perl_count = $output[0]; + @output = runcmd("find $unitsrc/xCAT-test/autotest/bats -name '*.bats' 2>/dev/null | wc -l"); + my $bats_count = $output[0]; + print "[preserve_source_tree] preserved $srcdir in $unitsrc ($perl_count Perl unit tests, $bats_count BATS tests)\n"; return 0; } @@ -466,6 +469,39 @@ sub run_unit_tests{ return 0; } +#-------------------------------------------------------- +# Fuction name: run_bats_tests +# Description: Run shell-script unit tests under xCAT-test/autotest/bats. +# Runs against the pre-build copy of the source tree taken by +# preserve_source_tree(), like the Perl unit tests. +# Attributes: +# Return code: 0 all tests passed, 1 otherwise +#-------------------------------------------------------- +sub run_bats_tests{ + my $testdir = "$unitsrc/xCAT-test/autotest/bats"; + my @output = runcmd("find $testdir -name '*.bats' -print -quit 2>/dev/null"); + if (!@output) { + print "[run_bats_tests] no BATS tests found under $testdir\n"; + return 0; + } + + my $cmd = "cd $unitsrc && bats -r xCAT-test/autotest/bats"; + print "[run_bats_tests] running $cmd\n"; + @output = runcmd("$cmd"); + print Dumper \@output; + if($::RUNCMD_RC){ + print RED "[run_bats_tests] $cmd ....[Failed]\n"; + $check_result_str .= "> **BATS TESTS Failed** : Please click ``Details`` label in ``Merge pull request`` box for detailed information\n"; + print $check_result_str; + return 1; + } + + print "[run_bats_tests] $cmd ....[Pass]\n"; + $check_result_str .= "> **BATS TESTS Successful**\n"; + print $check_result_str; + return 0; +} + #-------------------------------------------------------- # Fuction name: check_syntax # Description: @@ -689,6 +725,15 @@ if($rst){ } mark_time("run_unit_tests"); +#Run shell-script unit tests. +print GREEN "\n------Running xCAT-test BATS tests ------\n"; +$rst = run_bats_tests(); +if($rst){ + print RED "Run of xCAT-test BATS tests failed\n"; + exit $rst; +} +mark_time("run_bats_tests"); + #Check the syntax of changing code print GREEN "\n------ Checking the syntax of changed code------\n"; $rst = check_syntax(); diff --git a/xCAT-genesis-builder/dracut_105/el/module-setup.sh b/xCAT-genesis-builder/dracut_105/el/module-setup.sh index e2bb98250..302c4758f 100755 --- a/xCAT-genesis-builder/dracut_105/el/module-setup.sh +++ b/xCAT-genesis-builder/dracut_105/el/module-setup.sh @@ -9,14 +9,15 @@ depends() { } installkernel() { - local modules_dep modfile modname + local modules_dep modules_root modfile modname - if [[ -n "${kernel:-}" && -r "/lib/modules/$kernel/modules.dep" ]]; then - modules_dep="/lib/modules/$kernel/modules.dep" - elif [[ -n "${KERNELVERSION:-}" && -r "/lib/modules/$KERNELVERSION/modules.dep" ]]; then - modules_dep="/lib/modules/$KERNELVERSION/modules.dep" + modules_root="${DRACUT_MODULES_ROOT:-/lib/modules}" + if [[ -n "${kernel:-}" && -r "$modules_root/$kernel/modules.dep" ]]; then + modules_dep="$modules_root/$kernel/modules.dep" + elif [[ -n "${KERNELVERSION:-}" && -r "$modules_root/$KERNELVERSION/modules.dep" ]]; then + modules_dep="$modules_root/$KERNELVERSION/modules.dep" else - modules_dep=$(ls -1 /lib/modules/*/modules.dep 2>/dev/null | head -n 1) + modules_dep=$(ls -1 "$modules_root"/*/modules.dep 2>/dev/null | head -n 1) fi [[ -r "$modules_dep" ]] || return 0 diff --git a/xCAT-genesis-builder/dracut_105/ubuntu/module-setup.sh b/xCAT-genesis-builder/dracut_105/ubuntu/module-setup.sh index e4b8b0e3d..ba5325247 100755 --- a/xCAT-genesis-builder/dracut_105/ubuntu/module-setup.sh +++ b/xCAT-genesis-builder/dracut_105/ubuntu/module-setup.sh @@ -9,14 +9,15 @@ depends() { } installkernel() { - local modules_dep modfile modname + local modules_dep modules_root modfile modname - if [[ -n "${kernel:-}" && -r "/lib/modules/$kernel/modules.dep" ]]; then - modules_dep="/lib/modules/$kernel/modules.dep" - elif [[ -n "${KERNELVERSION:-}" && -r "/lib/modules/$KERNELVERSION/modules.dep" ]]; then - modules_dep="/lib/modules/$KERNELVERSION/modules.dep" + modules_root="${DRACUT_MODULES_ROOT:-/lib/modules}" + if [[ -n "${kernel:-}" && -r "$modules_root/$kernel/modules.dep" ]]; then + modules_dep="$modules_root/$kernel/modules.dep" + elif [[ -n "${KERNELVERSION:-}" && -r "$modules_root/$KERNELVERSION/modules.dep" ]]; then + modules_dep="$modules_root/$KERNELVERSION/modules.dep" else - modules_dep=$(ls -1 /lib/modules/*/modules.dep 2>/dev/null | head -n 1) + modules_dep=$(ls -1 "$modules_root"/*/modules.dep 2>/dev/null | head -n 1) fi [[ -r "$modules_dep" ]] || return 0 diff --git a/xCAT-server/share/xcat/install/scripts/post.xcat b/xCAT-server/share/xcat/install/scripts/post.xcat index d6ac63ff0..bb40d0f22 100755 --- a/xCAT-server/share/xcat/install/scripts/post.xcat +++ b/xCAT-server/share/xcat/install/scripts/post.xcat @@ -100,10 +100,7 @@ if [ ! -x /usr/bin/wget ]; then sleep 36500d fi -# 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 +xcat_download_postscripts "${MASTER_IP}:${HTTPPORT}" "$INSTALLDIR" "/xcatpost" "/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" diff --git a/xCAT-server/share/xcat/install/scripts/pre.sles b/xCAT-server/share/xcat/install/scripts/pre.sles index 292b4c6b7..1eaa4c7c1 100644 --- a/xCAT-server/share/xcat/install/scripts/pre.sles +++ b/xCAT-server/share/xcat/install/scripts/pre.sles @@ -191,17 +191,6 @@ if [ -e "/tmp/xcat.install_disk" ]; then fi msgutil_r "$MASTER_IP" "info" "Found $instdisk, generate partition file..." "/var/log/xcat/xcat.log" "$log_label" -set_sles11_uefi_bootloader() -{ - if grep -E 'install=.*sles11' /proc/cmdline >/dev/null 2>&1; then - # SLES 11 AutoYaST keeps the template's legacy MBR bootloader - # location unless the UEFI path explicitly selects elilo. - sed -i -e '/mbr!elilo!' \ - /tmp/profile/modified.xml - fi -} if [ -d /sys/firmware/efi ]; then sed -e 's!XCATPARTITIONHOOK!'$instdisk'vfat/boot/efi128mbswapauto/auto!' /tmp/profile/autoinst.xml > /tmp/profile/modified.xml diff --git a/xCAT-server/share/xcat/install/scripts/scriptlib b/xCAT-server/share/xcat/install/scripts/scriptlib index ab74bddb6..de427d3e2 100644 --- a/xCAT-server/share/xcat/install/scripts/scriptlib +++ b/xCAT-server/share/xcat/install/scripts/scriptlib @@ -63,3 +63,27 @@ declare -F xcat_enable_active_nm_autoconnect &>/dev/null || function xcat_enable nmcli con mod "$con_name" connection.autoconnect yes done } + +declare -F xcat_download_postscripts &>/dev/null || function xcat_download_postscripts { + local server="$1" + local install_dir="${2:-/install}" + local postroot="${3:-/xcatpost}" + local log_file="${4:-/tmp/wget.log}" + + export LANG=C + 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://${server}${install_dir}/postscripts/" -P "$postroot" 2> "$log_file" +} + +declare -F set_sles11_uefi_bootloader &>/dev/null || function set_sles11_uefi_bootloader { + local cmdline="${1:-/proc/cmdline}" + local profile="${2:-/tmp/profile/modified.xml}" + + if grep -E 'install=.*sles11' "$cmdline" >/dev/null 2>&1; then + # SLES 11 AutoYaST keeps the template's legacy MBR bootloader + # location unless the UEFI path explicitly selects elilo. + sed -i -e '/mbr!elilo!' \ + "$profile" + fi +} diff --git a/xCAT-test/README.md b/xCAT-test/README.md new file mode 100644 index 000000000..7406be0d4 --- /dev/null +++ b/xCAT-test/README.md @@ -0,0 +1,20 @@ +# xCAT-test + +Unit tests that run from the source checkout are split by implementation +language: + +| Test type | Location | Runner | +| --------- | -------- | ------ | +| Perl unit tests | `xCAT-test/unit/*.t` | `prove -r xCAT-test/unit` | +| Shell unit tests | `xCAT-test/autotest/bats/*.bats` | `bats -r xCAT-test/autotest/bats` | + +Use Perl `.t` tests for Perl modules, Perl scripts, templates, and repository +artifacts. Use BATS tests for shell-script behavior that can be exercised from +the checkout by sourcing a shell library or script and shadowing external +commands. + +Shell behavior should not be tested by Perl tests that grep shell source. Put +those tests under `xCAT-test/autotest/bats` instead. + +See `unit/README.md` and `autotest/bats/README.md` for the detailed rules for +each unit-test suite. diff --git a/xCAT-test/autotest/bats/README.md b/xCAT-test/autotest/bats/README.md new file mode 100644 index 000000000..29a1403e8 --- /dev/null +++ b/xCAT-test/autotest/bats/README.md @@ -0,0 +1,21 @@ +# xCAT-test/autotest/bats + +Shell-script unit tests live here and run with: + +```bash +bats -r xCAT-test/autotest/bats +``` + +The GitHub Actions `xcat_test` workflow runs this command after the Perl `.t` +unit tests. Use BATS for shell behavior that can be exercised from the source +tree without an installed xCAT, a live management node, or real services. + +Prefer sourcing an existing shell library or sourceable script and calling the +function under test. Keep reusable install-template helpers in +`xCAT-server/share/xcat/install/scripts/scriptlib`, and reusable postscript +helpers in `xCAT/postscripts/xcatlib.sh`. Use scratch directories and shadowed +commands so tests cannot write to the host. + +Extraction helpers in `helpers/shell_source.bash` are only for legacy code that +cannot safely be sourced yet. Do not add Perl `.t` tests that grep shell source +when the behavior can be tested with BATS. diff --git a/xCAT-test/autotest/bats/genesis_ib_modules.bats b/xCAT-test/autotest/bats/genesis_ib_modules.bats new file mode 100644 index 000000000..ed132043e --- /dev/null +++ b/xCAT-test/autotest/bats/genesis_ib_modules.bats @@ -0,0 +1,118 @@ +#!/usr/bin/env bats + +load 'helpers/shell_source' + +setup() +{ + GENESIS_SPEC="$(repo_path 'xCAT-genesis-builder/xCAT-genesis-base.spec')" + DRACUT_MODULE="$(repo_path 'xCAT-genesis-builder/dracut_105/el/module-setup.sh')" + DOXCAT="$(repo_path 'xCAT-genesis-scripts/usr/bin/doxcat')" + [ -r "$GENESIS_SPEC" ] || skip "$GENESIS_SPEC is required" + [ -r "$DRACUT_MODULE" ] || skip "$DRACUT_MODULE is required" + [ -r "$DOXCAT" ] || skip "$DOXCAT is required" + export GENESIS_SPEC DRACUT_MODULE DOXCAT +} + +run_installkernel() +{ + local modules_root="$1" + local instmods_log="$2" + + kernel=5.14.0-test + DRACUT_MODULES_ROOT="$modules_root" + instmods() + { + printf '%s\n' "$1" >>"$instmods_log" + } + + source "$DRACUT_MODULE" + installkernel +} + +run_doxcat_modprobe_preamble() +{ + local preamble="$1" + local modprobe_log="$2" + + modprobe() + { + printf '%s\n' "$*" >>"$modprobe_log" + } + + eval "$preamble" +} + +run_doxcat_bootif_block() +{ + local block="$1" + + BOOTIF=01-aa-bb-cc-dd-ee-ff + bootnic= + log_label=test + gripeiter=2 + + logger() { :; } + sleep() { :; } + ip() + { + printf '%s\n' "$*" >>"$IP_LOG" + if [ "$*" = "link show" ]; then + cat <<'EOF' +2: eth0: mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000 + link/ether 00:11:22:33:44:55 brd ff:ff:ff:ff:ff:ff +3: ib0: mtu 65520 qdisc mq state UP mode DEFAULT group default qlen 256 + link/infiniband 00:bb:cc:dd:ee:ff brd 00:ff:ff:ff:ff:ff +EOF + fi + } + + eval "$block" + printf '%s\n' "$bootnic" +} + +@test "genesis build requires kernel module packages" { + grep -Fxq 'BuildRequires: kernel-core' "$GENESIS_SPEC" + grep -Fxq 'BuildRequires: kernel-modules' "$GENESIS_SPEC" + grep -Fxq 'BuildRequires: kernel-modules-extra' "$GENESIS_SPEC" +} + +@test "dracut genesis module installs every module from modules.dep" { + local modules_root="${BATS_TEST_TMPDIR}/modules" + local modules_dep="${modules_root}/5.14.0-test/modules.dep" + local instmods_log="${BATS_TEST_TMPDIR}/instmods.log" + + mkdir -p "${modules_root}/5.14.0-test" + cat >"$modules_dep" <<'EOF' +kernel/drivers/infiniband/ulp/ipoib/ib_ipoib.ko.xz: +kernel/drivers/net/ethernet/intel/e1000e/e1000e.ko.xz: +EOF + + run run_installkernel "$modules_root" "$instmods_log" + [ "$status" -eq 0 ] + grep -Fxq 'ib_ipoib' "$instmods_log" + grep -Fxq 'e1000e' "$instmods_log" +} + +@test "doxcat loads IP over InfiniBand support during startup" { + local preamble + local modprobe_log="${BATS_TEST_TMPDIR}/modprobe.log" + + preamble="$(extract_line_range "$DOXCAT" '^modprobe acpi_cpufreq' '^modprobe ib_ipoib$')" || return 1 + + run run_doxcat_modprobe_preamble "$preamble" "$modprobe_log" + [ "$status" -eq 0 ] + grep -Fxq 'ib_ipoib' "$modprobe_log" +} + +@test "doxcat falls back to InfiniBand BOOTIF lookup after Ethernet lookup misses" { + local block + + IP_LOG="${BATS_TEST_TMPDIR}/ip.log" + export IP_LOG + block="$(extract_shell_if_block "$DOXCAT" 'if [ ! -z "$BOOTIF" ]; then')" || return 1 + + run run_doxcat_bootif_block "$block" + [ "$status" -eq 0 ] + [ "$output" = "ib0" ] + [ "$(grep -c '^link show$' "$IP_LOG")" -eq 2 ] +} diff --git a/xCAT-test/autotest/bats/go_xcat_common_repository.bats b/xCAT-test/autotest/bats/go_xcat_common_repository.bats new file mode 100644 index 000000000..b49f4fce0 --- /dev/null +++ b/xCAT-test/autotest/bats/go_xcat_common_repository.bats @@ -0,0 +1,122 @@ +#!/usr/bin/env bats + +load 'helpers/go_xcat' + +setup() +{ + go_xcat_require_source +} + +run_common_repository_case() +{ + local case_dir="$1" + local common_present="$2" + shift 2 + + mkdir -p "$case_dir" + export ADD_LOG="${case_dir}/add.log" + export COMMON_PRESENT="$common_present" + export DOWNLOAD_LOG="${case_dir}/download.log" + export ID_LOG="${case_dir}/id.log" + export TEST_TMP="$case_dir" + + go_xcat_load_functions \ + add_xcat_dep_common_repo_yum_or_zypper \ + xcat_dep_common_repo_configured \ + refresh_xcat_dep_repository_ids + + TMP_DIR="$TEST_TMP" + GO_XCAT_DEFAULT_BASE_URL=https://repo.example.invalid + GO_XCAT_DEP_REPOSITORY_IDS=(xcat-dep) + + yum() { :; } + + download_file() + { + printf '%s\n' "$1" >>"$DOWNLOAD_LOG" + [[ ${COMMON_PRESENT:-0} == 1 ]] || return 1 + : >"$2" + } + + add_repo_by_url_yum_or_zypper() + { + printf '%s %s\n' "$1" "$2" >>"$ADD_LOG" + } + + xcat_dep_common_repo_configured() + { + [[ -s "$ADD_LOG" ]] + } + + ( add_xcat_dep_common_repo_yum_or_zypper "$@" ) + refresh_xcat_dep_repository_ids + printf '%s\n' "${GO_XCAT_DEP_REPOSITORY_IDS[*]}" >"$ID_LOG" +} + +run_template_generation() +{ + local tmp_dir="$1" + local repo_log="$2" + + mkdir -p "$tmp_dir" + export REPO_LOG="$repo_log" + export TEST_TMP="$tmp_dir" + + go_xcat_load_functions add_repo_by_url_yum_or_zypper + + TMP_DIR="$TEST_TMP" + GO_XCAT_DEFAULT_INSTALL_PATH=/install/xcat + yum() { :; } + add_repo_by_file() { cp "$1" "$REPO_LOG"; } + + add_repo_by_url_yum_or_zypper \ + https://repo.example.invalid/xcat-dep/common xcat-dep-common optional +} + +@test "an available remote common repository is enabled" { + local case_dir="${BATS_TEST_TMPDIR}/remote-present" + + run run_common_repository_case "$case_dir" 1 "" latest + [ "$status" -eq 0 ] + [ "$(read_file_or_empty "${case_dir}/download.log")" = "https://repo.example.invalid/yum/latest/xcat-dep/common/repodata/repomd.xml" ] + [ "$(read_file_or_empty "${case_dir}/add.log")" = "https://repo.example.invalid/yum/latest/xcat-dep/common xcat-dep-common" ] + [ "$(read_file_or_empty "${case_dir}/id.log")" = "xcat-dep xcat-dep-common" ] +} + +@test "a release without the remote common repository remains usable" { + local case_dir="${BATS_TEST_TMPDIR}/remote-missing" + + run run_common_repository_case "$case_dir" 0 "" 2.18 + [ "$status" -eq 0 ] + [ "$(read_file_or_empty "${case_dir}/add.log")" = "" ] + [ "$(read_file_or_empty "${case_dir}/id.log")" = "xcat-dep" ] +} + +@test "a custom repository file does not guess an unrelated common repository" { + local case_dir="${BATS_TEST_TMPDIR}/repo-file" + + run run_common_repository_case "$case_dir" 1 https://repo.example.invalid/custom/xcat-dep.repo latest + [ "$status" -eq 0 ] + [ "$(read_file_or_empty "${case_dir}/download.log")" = "" ] + [ "$(read_file_or_empty "${case_dir}/add.log")" = "" ] +} + +@test "a local common repository is enabled beside the distribution repository" { + local local_root="${BATS_TEST_TMPDIR}/local-repository" + local case_dir="${BATS_TEST_TMPDIR}/local-present" + mkdir -p "${local_root}/common/repodata" + : >"${local_root}/common/repodata/repomd.xml" + + run run_common_repository_case "$case_dir" 0 "$local_root" latest + [ "$status" -eq 0 ] + [ "$(read_file_or_empty "${case_dir}/add.log")" = "${local_root}/common xcat-dep-common" ] +} + +@test "the optional common repository template tolerates outages and verifies metadata" { + local template_log="${BATS_TEST_TMPDIR}/generated-common.repo" + + run run_template_generation "$BATS_TEST_TMPDIR" "$template_log" + [ "$status" -eq 0 ] + grep -Fxq 'skip_if_unavailable=1' "$template_log" + grep -Fxq 'repo_gpgcheck=1' "$template_log" +} diff --git a/xCAT-test/autotest/bats/go_xcat_el_repo_check.bats b/xCAT-test/autotest/bats/go_xcat_el_repo_check.bats new file mode 100644 index 000000000..0064f8673 --- /dev/null +++ b/xCAT-test/autotest/bats/go_xcat_el_repo_check.bats @@ -0,0 +1,206 @@ +#!/usr/bin/env bats + +load 'helpers/go_xcat' + +setup() +{ + go_xcat_require_source + export CALLS="${BATS_TEST_TMPDIR}/calls" + export GO_XCAT_ARCH=x86_64 + export GO_XCAT_LINUX_DISTRO=rocky + export GO_XCAT_LINUX_VERSION=10.2 + export EPEL_HAS=1 + export CRB_HAS=1 + export QUERY_FAIL=0 + export QUERY_WARNS=0 + export SOURCE_ONLY=0 + export ENTRY=check +} + +run_el_repo_check() +{ + rm -f "$CALLS" + + go_xcat_load_functions \ + repo_carries \ + el_epel_and_crb_check \ + install_packages_dnf \ + install_packages_yum + + EL_EPEL_TEST_RPM=perl-Crypt-CBC + EL_CRB_TEST_RPM=perl-IO-Tty + + dnf() + { + echo "$*" >>"$CALLS" + if [[ ${QUERY_FAIL:-0} == 1 ]]; then + echo "Error: Failed to download metadata for repo 'epel'" >&2 + return 1 + fi + [[ ${QUERY_WARNS:-0} == 1 ]] && echo "Warning: repository 'extras' metadata is stale" >&2 + + local has=0 + case "$*" in + *perl-Crypt-CBC*) has="$EPEL_HAS" ;; + *perl-IO-Tty*) has="$CRB_HAS" ;; + esac + + [[ ${SOURCE_ONLY:-0} == 1 && "$*" != *"--arch"* ]] && has=1 + case "$1" in + repoquery) [[ $has == 1 ]] && { echo "${@: -1}"; echo "${@: -1}"; }; return 0 ;; + list) [[ $has == 1 ]] && return 0; return 1 ;; + esac + return 0 + } + + yum() + { + dnf "$@" + } + + case "${ENTRY:-check}" in + dnf) install_packages_dnf -y xCAT ;; + yum) install_packages_yum -y xCAT ;; + *) el_epel_and_crb_check dnf ;; + esac +} + +@test "EL9 with EPEL and CRB passes after probing binary repositories" { + export GO_XCAT_LINUX_VERSION=9.5 + + run run_el_repo_check + [ "$status" -eq 0 ] + probes="$(joined_file_lines "$CALLS")" + [[ "$probes" =~ perl-Crypt-CBC.*\;.*perl-IO-Tty ]] + [[ "$probes" =~ ^repoquery\ ]] + [[ "$probes" =~ --arch\ x86_64,noarch ]] +} + +@test "EL9 without EPEL stops and names the EL9 release package" { + export GO_XCAT_LINUX_VERSION=9.5 + export EPEL_HAS=0 + + run run_el_repo_check + [ "$status" -eq 1 ] + [[ "$output" =~ epel-release-latest-9\.noarch ]] +} + +@test "EL10 with EPEL and CRB passes after probing both repositories" { + run run_el_repo_check + [ "$status" -eq 0 ] + probes="$(joined_file_lines "$CALLS")" + [[ "$probes" =~ perl-Crypt-CBC.*\;.*perl-IO-Tty ]] +} + +@test "EL10 without EPEL stops with the EL10 EPEL release package" { + export EPEL_HAS=0 + + run run_el_repo_check + [ "$status" -eq 1 ] + [[ "$output" =~ requires\ EPEL\ repository ]] + [[ "$output" =~ epel-release-latest-10\.noarch ]] +} + +@test "EL10 without CRB stops with current CRB guidance" { + export GO_XCAT_LINUX_DISTRO=rhel + export CRB_HAS=0 + + run run_el_repo_check + [ "$status" -eq 1 ] + [[ "$output" =~ requires\ CRB\ repository ]] + [[ "$output" =~ "'dnf update epel-release' and then 'crb enable'" ]] + [[ ! "$output" =~ gpgcheck=0|centos-crb|subscription-manager ]] +} + +@test "a source repository does not stand in for the binary one" { + export EPEL_HAS=0 + export SOURCE_ONLY=1 + + run run_el_repo_check + [ "$status" -eq 1 ] + [[ "$output" =~ requires\ EPEL\ repository ]] +} + +@test "Oracle Linux 10 without CRB names its CodeReady Builder command" { + export GO_XCAT_LINUX_DISTRO=ol + export GO_XCAT_LINUX_VERSION=10.1 + export CRB_HAS=0 + + run run_el_repo_check + [ "$status" -eq 1 ] + [[ "$output" =~ dnf\ config-manager\ --enable\ ol10_codeready_builder ]] +} + +@test "a failed repository query stops with the package manager error" { + export QUERY_FAIL=1 + + run run_el_repo_check + [ "$status" -eq 1 ] + [[ "$output" =~ Failed\ to\ download\ metadata ]] + [[ ! "$output" =~ requires\ EPEL\ repository ]] +} + +@test "a warning on stderr does not stand in for a package" { + export EPEL_HAS=0 + export QUERY_WARNS=1 + + run run_el_repo_check + [ "$status" -eq 1 ] + [[ "$output" =~ requires\ EPEL\ repository ]] +} + +@test "a warning beside a real match does not fail the check" { + export QUERY_WARNS=1 + + run run_el_repo_check + [ "$status" -eq 0 ] +} + +@test "CentOS Stream 10, which reports the major version alone, is checked" { + export GO_XCAT_LINUX_DISTRO=centos + export GO_XCAT_LINUX_VERSION=10 + export EPEL_HAS=0 + export CRB_HAS=0 + + run run_el_repo_check + [ "$status" -eq 1 ] + [[ "$output" =~ requires\ EPEL\ repository ]] +} + +@test "EL8 and Fedora are not checked" { + export GO_XCAT_LINUX_VERSION=8.10 + export EPEL_HAS=0 + export CRB_HAS=0 + + run run_el_repo_check + [ "$status" -eq 0 ] + [ "$(joined_file_lines "$CALLS")" = "" ] + + export GO_XCAT_LINUX_DISTRO=fedora + export GO_XCAT_LINUX_VERSION=42 + + run run_el_repo_check + [ "$status" -eq 0 ] +} + +@test "dnf and yum installer paths run the check before installing" { + for entry in dnf yum; do + export ENTRY="$entry" + export EPEL_HAS=0 + export CRB_HAS=1 + + run run_el_repo_check + [ "$status" -eq 1 ] + [[ "$output" =~ requires\ EPEL\ repository ]] + probes="$(joined_file_lines "$CALLS")" + [[ ! "$probes" =~ install ]] + + export EPEL_HAS=1 + export CRB_HAS=1 + + run run_el_repo_check + [ "$status" -eq 0 ] + probes="$(joined_file_lines "$CALLS")" + [[ "$probes" =~ perl-IO-Tty.*\;.*install\ initscripts.*\;.*install\ xCAT ]] + done +} diff --git a/xCAT-test/autotest/bats/helpers/go_xcat.bash b/xCAT-test/autotest/bats/helpers/go_xcat.bash new file mode 100644 index 000000000..b863c963f --- /dev/null +++ b/xCAT-test/autotest/bats/helpers/go_xcat.bash @@ -0,0 +1,56 @@ +#!/usr/bin/env bash + +go_xcat_default_source() +{ + printf '%s\n' "${BATS_TEST_DIRNAME}/../../../xCAT-server/share/xcat/tools/go-xcat" +} + +go_xcat_require_source() +{ + GO_XCAT_SOURCE="${XCAT_TEST_GO_XCAT:-$(go_xcat_default_source)}" + export GO_XCAT_SOURCE + [ -r "$GO_XCAT_SOURCE" ] || skip "$GO_XCAT_SOURCE is required" +} + +go_xcat_extract_functions() +{ + local function_name + for function_name in "$@"; do + awk -v name="$function_name" ' + $0 == "function " name "()" { copy = 1 } + copy { print } + copy && /^}$/ { exit } + ' "$GO_XCAT_SOURCE" + done +} + +go_xcat_load_functions() +{ + local function_body function_name + function_body="$(go_xcat_extract_functions "$@")" || return 1 + eval "$function_body" + for function_name in "$@"; do + declare -F "$function_name" >/dev/null || { + printf 'missing %s\n' "$function_name" >&2 + return 70 + } + done +} + +read_file_or_empty() +{ + local path="$1" + [ -f "$path" ] || return 0 + cat "$path" +} + +joined_file_lines() +{ + local path="$1" + local line separator="" + [ -f "$path" ] || return 0 + while IFS= read -r line; do + printf '%s%s' "$separator" "$line" + separator=";" + done <"$path" +} diff --git a/xCAT-test/autotest/bats/helpers/shell_source.bash b/xCAT-test/autotest/bats/helpers/shell_source.bash new file mode 100644 index 000000000..f76ed55d3 --- /dev/null +++ b/xCAT-test/autotest/bats/helpers/shell_source.bash @@ -0,0 +1,132 @@ +#!/usr/bin/env bash + +repo_root() +{ + printf '%s\n' "${BATS_TEST_DIRNAME}/../../.." +} + +repo_path() +{ + printf '%s/%s\n' "$(repo_root)" "$1" +} + +require_repo_file() +{ + local path + path="$(repo_path "$1")" + [ -r "$path" ] || skip "$path is required" + printf '%s\n' "$path" +} + +read_file_or_empty() +{ + local path="$1" + [ -f "$path" ] || return 0 + cat "$path" +} + +extract_shell_function() +{ + local file="$1" + local name="$2" + + awk -v name="$name" ' + BEGIN { + signature = "^[[:space:]]*(function[[:space:]]+)?" name "([[:space:]]*\\(\\))?[[:space:]]*$" + inline_signature = "^[[:space:]]*(function[[:space:]]+)?" name "([[:space:]]*\\(\\))?[[:space:]]*\\{" + } + $0 ~ signature || $0 ~ inline_signature { + copy = 1 + } + copy { + print + opened += gsub(/\{/, "{") + closed += gsub(/\}/, "}") + if (opened > 0 && opened == closed) { + found = 1 + exit + } + } + END { + if (!found) { + exit 1 + } + } + ' "$file" +} + +extract_shell_if_block() +{ + local file="$1" + local start="$2" + + awk -v start="$start" ' + index($0, start) { + copy = 1 + } + copy { + print + if ($0 ~ /^[[:space:]]*if[[:space:]\[]/) { + depth++ + } + line = $0 + while (line ~ /(^|[;[:space:]])fi([;[:space:]]|$)/) { + depth-- + sub(/(^|[;[:space:]])fi([;[:space:]]|$)/, " ", line) + } + if (depth == 0) { + found = 1 + exit + } + } + END { + if (!found) { + exit 1 + } + } + ' "$file" +} + +extract_line_range() +{ + local file="$1" + local start="$2" + local end="$3" + + awk -v start="$start" -v end="$end" ' + $0 ~ start { + copy = 1 + } + copy { + print + if ($0 ~ end) { + found = 1 + exit + } + } + END { + if (!found) { + exit 1 + } + } + ' "$file" +} + +extract_first_matching_line() +{ + local file="$1" + local pattern="$2" + + awk -v pattern="$pattern" ' + $0 ~ pattern { + print + found = 1 + exit + } + END { + if (!found) { + exit 1 + } + } + ' "$file" +} diff --git a/xCAT-test/autotest/bats/post_xcat_download_policy.bats b/xCAT-test/autotest/bats/post_xcat_download_policy.bats new file mode 100644 index 000000000..4ec06387a --- /dev/null +++ b/xCAT-test/autotest/bats/post_xcat_download_policy.bats @@ -0,0 +1,76 @@ +#!/usr/bin/env bats + +load 'helpers/shell_source' + +setup() +{ + SCRIPT_LIB="$(repo_path 'xCAT-server/share/xcat/install/scripts/scriptlib')" + XCATLIB="$(repo_path 'xCAT/postscripts/xcatlib.sh')" + [ -r "$SCRIPT_LIB" ] || skip "$SCRIPT_LIB is required" + [ -r "$XCATLIB" ] || skip "$XCATLIB is required" + export SCRIPT_LIB XCATLIB +} + +capture_install_scriptlib_wget() +{ + local wget_log="$1" + + wget() + { + printf '%s\n' "$*" >"$wget_log" + return 0 + } + + source "$SCRIPT_LIB" + xcat_download_postscripts "192.0.2.10:80" "/install" "/xcatpost" "$wget_log" +} + +capture_xcatlib_wget() +{ + local wget_log="$1" + + xcatpost="${BATS_TEST_TMPDIR}/xcatpost" + INSTALLDIR=/install + + echolog() { :; } + sleep() { :; } + grep() + { + [ "${*: -1}" = "/tmp/wget.log" ] && return 1 + command grep "$@" + } + wget() + { + printf '%s\n' "$*" >"$wget_log" + return 0 + } + + source "$XCATLIB" + download_postscripts 192.0.2.10:80 +} + +assert_download_policy() +{ + local args="$1" + + [[ "$args" == *'--reject index.html*,post.xcat.ng,post.xcat.rhels10'* ]] + [[ "$args" == *'--no-parent'* ]] + [[ "$args" == *'postscripts/'* ]] + [[ "$args" != *'>"$KILL_LOG" + [ "$1" = "-0" ] && return 1 + return 0 + } + sleep() { :; } + sshd() + { + printf '%s\n' start >>"$SSHD_LOG" + } + + source "$XCATLIB" + xcat_restart_sshd_after_failed_service_restart sshd +} + +run_wait_for_processes() +{ + local rc + + source "$XCATLIB" + xcat_wait_for_processes_to_exit "$*" 3 + rc=$? + printf '%s\n' "$rc" + return 0 +} + +@test "remoteshell restart fallback sends an uncatchable signal before starting sshd" { + KILL_LOG="${BATS_TEST_TMPDIR}/kill.log" + SSHD_LOG="${BATS_TEST_TMPDIR}/sshd.log" + export KILL_LOG SSHD_LOG + + run run_restart_fallback + [ "$status" -eq 0 ] + grep -Fxq -- '-9 4321' "$KILL_LOG" + ! grep -Eq '^9( |$)' "$KILL_LOG" + [ "$(read_file_or_empty "$SSHD_LOG")" = "start" ] +} + +@test "remoteshell wait loop reports a still-running process and gives up" { + local child + + sleep 30 & + child=$! + + run run_wait_for_processes "$child" + kill -KILL "$child" 2>/dev/null || true + wait "$child" 2>/dev/null || true + + [ "$status" -eq 0 ] + [ "$output" = "1" ] +} + +@test "remoteshell wait loop returns as soon as killed processes are gone" { + run run_wait_for_processes 999999 + [ "$status" -eq 0 ] + [ "$output" = "0" ] +} diff --git a/xCAT-test/autotest/bats/sles_pre_script.bats b/xCAT-test/autotest/bats/sles_pre_script.bats new file mode 100644 index 000000000..f35153200 --- /dev/null +++ b/xCAT-test/autotest/bats/sles_pre_script.bats @@ -0,0 +1,45 @@ +#!/usr/bin/env bats + +load 'helpers/shell_source' + +setup() +{ + SCRIPT_LIB="$(repo_path 'xCAT-server/share/xcat/install/scripts/scriptlib')" + [ -r "$SCRIPT_LIB" ] || skip "$SCRIPT_LIB is required" + export SCRIPT_LIB +} + +@test "SLES 11 UEFI install changes the AutoYaST bootloader to elilo" { + local cmdline="${BATS_TEST_TMPDIR}/cmdline" + local profile="${BATS_TEST_TMPDIR}/modified.xml" + + printf '%s\n' 'BOOT_IMAGE=/linux install=http://192.0.2.10/install/sles11/ppc64le' >"$cmdline" + cat >"$profile" <<'EOF' + +true +true +mbr + +EOF + + source "$SCRIPT_LIB" + run set_sles11_uefi_bootloader "$cmdline" "$profile" + [ "$status" -eq 0 ] + grep -Fxq 'elilo' "$profile" + ! grep -q 'mbr' "$profile" + ! grep -q '"$cmdline" + printf '%s\n' 'mbr' >"$profile" + + source "$SCRIPT_LIB" + run set_sles11_uefi_bootloader "$cmdline" "$profile" + [ "$status" -eq 0 ] + grep -Fxq 'mbr' "$profile" +} diff --git a/xCAT-test/autotest/bats/statelite_add_ssh.bats b/xCAT-test/autotest/bats/statelite_add_ssh.bats new file mode 100644 index 000000000..b633666f9 --- /dev/null +++ b/xCAT-test/autotest/bats/statelite_add_ssh.bats @@ -0,0 +1,40 @@ +#!/usr/bin/env bats + +load 'helpers/shell_source' + +setup() +{ + ADD_SSH="$(repo_path 'xCAT-server/share/xcat/netboot/add-on/statelite/add_ssh')" + [ -r "$ADD_SSH" ] || skip "$ADD_SSH is required" + export ADD_SSH +} + +run_sshd_config_block() +{ + local root="$1" + local block + + block="$(extract_shell_if_block "$ADD_SSH" 'if [ -r $ROOTDIR/etc/ssh/sshd_config ]')" || return 1 + ROOTDIR="$root" + eval "$block" +} + +@test "statelite add_ssh writes sshd settings below systemd's open-file limit" { + local root="${BATS_TEST_TMPDIR}/rootimg" + local sshd_config="${root}/etc/ssh/sshd_config" + + mkdir -p "${root}/etc/ssh" + cat >"$sshd_config" <<'EOF' +X11Forwarding no +KeyRegenerationInterval 3600 +MaxStartups 1024 +EOF + + run run_sshd_config_block "$root" + [ "$status" -eq 0 ] + grep -Fxq 'X11Forwarding yes' "$sshd_config" + grep -Fxq 'KeyRegenerationInterval 0' "$sshd_config" + grep -Fxq '#MaxStartups 1024' "$sshd_config" + grep -Fxq 'MaxStartups 100:30:200' "$sshd_config" + ! grep -Fxq 'MaxStartups 1024' "$sshd_config" +} diff --git a/xCAT-test/unit/README.md b/xCAT-test/unit/README.md index 79096bcbf..2fd67bcb6 100644 --- a/xCAT-test/unit/README.md +++ b/xCAT-test/unit/README.md @@ -46,6 +46,11 @@ so putting a test in `integration/` does not cost it CI coverage. What differs i each suite is allowed to depend on, and that unit tests also run standalone from a bare checkout with no xCAT at all. +Shell-script unit tests belong in [`../autotest/bats`](../autotest/bats/README.md) +and run with BATS. Do not add Perl `.t` tests that grep shell source when the +behavior can be exercised by sourcing a shell library or script and shadowing the +external commands it calls. + The distinction matters because a test that needs an absent environment does not fail -- it calls `plan skip_all` and reports as skipped. A handful of those in a suite of several hundred assertions is easy to stop reading. Keeping the two kinds in separate diff --git a/xCAT-test/unit/genesis_ib_modules.t b/xCAT-test/unit/genesis_ib_modules.t deleted file mode 100644 index ef2c3f36e..000000000 --- a/xCAT-test/unit/genesis_ib_modules.t +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env perl -use strict; -use warnings; - -use File::Spec; -use FindBin; -use Test::More; - -my $repo_root = File::Spec->rel2abs(File::Spec->catdir($FindBin::Bin, '..', '..')); - -my $spec = read_file('xCAT-genesis-builder/xCAT-genesis-base.spec'); -like($spec, qr/^BuildRequires:\s+kernel-core$/m, 'genesis build installs the kernel core'); -like($spec, qr/^BuildRequires:\s+kernel-modules$/m, 'genesis build installs the standard kernel modules'); -like($spec, qr/^BuildRequires:\s+kernel-modules-extra$/m, 'genesis build installs the extra kernel modules'); - -my $dracut_module = read_file('xCAT-genesis-builder/dracut_105/el/module-setup.sh'); -like( - $dracut_module, - qr/installkernel\(\) \{.*?modules_dep=.*?while IFS= read -r modfile;.*?instmods "\$modname".*?done < "\$modules_dep"/s, - 'genesis dracut module installs every module available to the build' -); - -my $doxcat = read_file('xCAT-genesis-scripts/usr/bin/doxcat'); -like($doxcat, qr/^modprobe ib_ipoib$/m, 'genesis loads the IP over InfiniBand module'); -like( - $doxcat, - qr/if \[ -z "\$bootnic" \]; then\s+bootnic=`ip link show\|grep -B1 infiniband/s, - 'genesis checks InfiniBand addresses only after the Ethernet lookup misses' -); - -done_testing(); - -sub read_file { - my ($file) = @_; - my $path = File::Spec->catfile($repo_root, split m{/}, $file); - open(my $fh, '<', $path) or die "open $path: $!"; - my $contents = do { local $/; <$fh> }; - close($fh) or die "close $path: $!"; - return $contents; -} diff --git a/xCAT-test/unit/go_xcat_common_repository.t b/xCAT-test/unit/go_xcat_common_repository.t deleted file mode 100644 index 21931d248..000000000 --- a/xCAT-test/unit/go_xcat_common_repository.t +++ /dev/null @@ -1,165 +0,0 @@ -#!/usr/bin/env perl -use strict; -use warnings; - -use File::Path qw(make_path); -use File::Temp qw(tempdir); -use FindBin; -use Test::More; - -my $go_xcat = "$FindBin::Bin/../../xCAT-server/share/xcat/tools/go-xcat"; -my $tmpdir = tempdir(CLEANUP => 1); -my $driver = "$tmpdir/driver.sh"; - -open(my $driver_fh, '>', $driver) or die "open $driver: $!"; -print {$driver_fh} <<'DRIVER'; -#!/bin/bash -set -euo pipefail - -function_body=$( - for function_name in \ - add_xcat_dep_common_repo_yum_or_zypper \ - xcat_dep_common_repo_configured \ - refresh_xcat_dep_repository_ids - do - awk -v name="$function_name" ' - $0 == "function " name "()" { copy = 1 } - copy { print } - copy && /^}$/ { exit } - ' "$GO_XCAT_SOURCE" - done -) -eval "$function_body" - -TMP_DIR=$TEST_TMP -GO_XCAT_DEFAULT_BASE_URL=https://repo.example.invalid -GO_XCAT_DEP_REPOSITORY_IDS=(xcat-dep) - -yum() { :; } - -download_file() { - printf '%s\n' "$1" >>"$DOWNLOAD_LOG" - [[ ${COMMON_PRESENT:-0} == 1 ]] || return 1 - : >"$2" -} - -add_repo_by_url_yum_or_zypper() { - printf '%s %s\n' "$1" "$2" >>"$ADD_LOG" -} - -xcat_dep_common_repo_configured() { [[ -s "$ADD_LOG" ]]; } -( add_xcat_dep_common_repo_yum_or_zypper "$@" ) -refresh_xcat_dep_repository_ids -printf '%s\n' "${GO_XCAT_DEP_REPOSITORY_IDS[*]}" >"$ID_LOG" -DRIVER -close($driver_fh) or die "close $driver: $!"; -chmod(0755, $driver) or die "chmod $driver: $!"; - -sub run_case { - my ($name, $present, @arguments) = @_; - my $case_dir = "$tmpdir/$name"; - make_path($case_dir); - local %ENV = ( - %ENV, - ADD_LOG => "$case_dir/add.log", - COMMON_PRESENT => $present, - DOWNLOAD_LOG => "$case_dir/download.log", - GO_XCAT_SOURCE => $go_xcat, - ID_LOG => "$case_dir/id.log", - TEST_TMP => $case_dir, - ); - my $status = system('bash', $driver, @arguments); - return ($status >> 8, $case_dir); -} - -sub read_file { - my ($path) = @_; - return '' unless -f $path; - open(my $fh, '<', $path) or die "open $path: $!"; - my $content = do { local $/; <$fh> }; - close($fh) or die "close $path: $!"; - return $content; -} - -my ($status, $case_dir) = run_case('remote-present', 1, '', 'latest'); -is($status, 0, 'an available common repository is accepted'); -is( - read_file("$case_dir/download.log"), - "https://repo.example.invalid/yum/latest/xcat-dep/common/repodata/repomd.xml\n", - 'the default repository is probed before it is enabled', -); -is( - read_file("$case_dir/add.log"), - "https://repo.example.invalid/yum/latest/xcat-dep/common xcat-dep-common\n", - 'the common repository uses its own repository ID', -); -is(read_file("$case_dir/id.log"), "xcat-dep xcat-dep-common\n", - 'common packages are included in repository listings'); - -($status, $case_dir) = run_case('remote-missing', 0, '', '2.18'); -is($status, 0, 'a release without the common repository remains usable'); -is(read_file("$case_dir/add.log"), '', 'a missing common repository is not enabled'); -is(read_file("$case_dir/id.log"), "xcat-dep\n", - 'legacy package listings remain unchanged when common is absent'); - -($status, $case_dir) = run_case( - 'repo-file', 1, 'https://repo.example.invalid/custom/xcat-dep.repo', 'latest' -); -is($status, 0, 'a custom repository file remains supported'); -is(read_file("$case_dir/download.log"), '', - 'the common URL is not guessed from a custom repository file'); -is(read_file("$case_dir/add.log"), '', - 'a custom repository file does not enable an unrelated repository'); - -my $local_root = "$tmpdir/local-repository"; -make_path("$local_root/common/repodata"); -open(my $repomd_fh, '>', "$local_root/common/repodata/repomd.xml") or die $!; -close($repomd_fh) or die $!; -($status, $case_dir) = run_case('local-present', 0, $local_root, 'latest'); -is($status, 0, 'a local common repository is accepted'); -is( - read_file("$case_dir/add.log"), - "$local_root/common xcat-dep-common\n", - 'the local common repository is enabled beside the distribution repository', -); - -my $template_driver = "$tmpdir/template-driver.sh"; -open(my $template_fh, '>', $template_driver) or die "open $template_driver: $!"; -print {$template_fh} <<'DRIVER'; -#!/bin/bash -set -euo pipefail - -function_body=$( - awk ' - /^function add_repo_by_url_yum_or_zypper\(\)/ { copy = 1 } - copy { print } - copy && /^}$/ { exit } - ' "$GO_XCAT_SOURCE" -) -eval "$function_body" - -TMP_DIR=$TEST_TMP -GO_XCAT_DEFAULT_INSTALL_PATH=/install/xcat -yum() { :; } -add_repo_by_file() { cp "$1" "$REPO_LOG"; } -add_repo_by_url_yum_or_zypper \ - https://repo.example.invalid/xcat-dep/common xcat-dep-common optional -DRIVER -close($template_fh) or die "close $template_driver: $!"; -chmod(0755, $template_driver) or die "chmod $template_driver: $!"; - -my $template_log = "$tmpdir/generated-common.repo"; -local %ENV = ( - %ENV, - GO_XCAT_SOURCE => $go_xcat, - REPO_LOG => $template_log, - TEST_TMP => $tmpdir, -); -$status = system('bash', $template_driver); -is($status >> 8, 0, 'go-xcat can generate the optional common repository'); -like(read_file($template_log), qr/^skip_if_unavailable=1$/m, - 'the generated common repository stays optional during outages'); -like(read_file($template_log), qr/^repo_gpgcheck=1$/m, - 'the generated common repository verifies signed metadata'); - -done_testing(); diff --git a/xCAT-test/unit/go_xcat_el_repo_check.t b/xCAT-test/unit/go_xcat_el_repo_check.t deleted file mode 100644 index f8413b165..000000000 --- a/xCAT-test/unit/go_xcat_el_repo_check.t +++ /dev/null @@ -1,161 +0,0 @@ -#!/usr/bin/env perl -use strict; -use warnings; - -use File::Temp qw(tempdir); -use FindBin; -use Test::More; - -# go-xcat checked for the EPEL and CRB repositories on EL9 only, and only when the version -# carried a minor number, so CentOS Stream was never checked. On EL10 a management node without -# them failed inside dnf install with a dependency error instead of the message that names the -# missing repository, and the CRB message proposed a CentOS Stream repository file with -# signature checks disabled. The probe used dnf list, which an installed copy of the probe -# package satisfies, and which reports a failed query as a missing repository. The check -# functions are taken from the shipped script and run against a dnf stand-in that answers for -# each probe and records what was asked. - -my $go_xcat = "$FindBin::Bin/../../xCAT-server/share/xcat/tools/go-xcat"; -plan skip_all => 'go-xcat not found' unless -r $go_xcat; - -my $tmpdir = tempdir( CLEANUP => 1 ); -my $driver = "$tmpdir/driver.sh"; -my $calls = "$tmpdir/calls"; -open( my $fh, '>', $driver ) or die "open $driver: $!"; -print {$fh} <<'DRIVER'; -#!/bin/bash -set -uo pipefail -eval "$(awk ' - /^function (repo_carries|el9?_epel_and_crb_check|install_packages_(dnf|yum))\(\)/ { copy = 1 } - copy { print } - copy && /^}$/ { copy = 0 } -' "$GO_XCAT_SOURCE")" -EL9_EPEL_TEST_RPM="perl-Crypt-CBC"; EL_EPEL_TEST_RPM="perl-Crypt-CBC" -EL9_CRB_TEST_RPM="perl-IO-Tty"; EL_CRB_TEST_RPM="perl-IO-Tty" -# repoquery prints the name when an enabled repository carries the package and nothing -# otherwise, and fails with a message when the repositories cannot be read. list -q exits 1 -# when neither a repository nor the installed set has the package. -dnf() { - echo "$*" >> "$CALLS" - if [[ ${QUERY_FAIL:-0} == 1 ]]; then - echo "Error: Failed to download metadata for repo 'epel'" >&2 - return 1 - fi - [[ ${QUERY_WARNS:-0} == 1 ]] && echo "Warning: repository 'extras' metadata is stale" >&2 - local has=0 - case "$*" in - *perl-Crypt-CBC*) has="$EPEL_HAS" ;; - *perl-IO-Tty*) has="$CRB_HAS" ;; - esac - # A source repository answers for the name unless the query is limited to binary architectures. - [[ ${SOURCE_ONLY:-0} == 1 && "$*" != *"--arch"* ]] && has=1 - case "$1" in - repoquery) [[ $has == 1 ]] && { echo "${@: -1}"; echo "${@: -1}"; }; return 0 ;; - list) [[ $has == 1 ]] && return 0; return 1 ;; - esac - return 0 -} -yum() { dnf "$@"; } -# ENTRY=dnf or yum runs the installer function go-xcat dispatches to, which owns the check. -case "${ENTRY:-check}" in - dnf) install_packages_dnf -y xCAT ;; - yum) install_packages_yum -y xCAT ;; - *) if declare -F el_epel_and_crb_check >/dev/null; then el_epel_and_crb_check dnf; else el9_epel_and_crb_check dnf; fi ;; -esac -DRIVER -close($fh); - -# Runs the check as go-xcat would on a host of this distro and version. Returns the exit status, -# the output, and the probes the dnf stand-in saw. -sub check { - my (%host) = @_; - unlink $calls; - local $ENV{GO_XCAT_SOURCE} = $go_xcat; - local $ENV{CALLS} = $calls; - local $ENV{GO_XCAT_LINUX_DISTRO} = $host{distro} || 'rocky'; - local $ENV{GO_XCAT_LINUX_VERSION} = $host{version}; - local $ENV{GO_XCAT_ARCH} = 'x86_64'; - local $ENV{EPEL_HAS} = $host{epel} ? 1 : 0; - local $ENV{CRB_HAS} = $host{crb} ? 1 : 0; - local $ENV{QUERY_FAIL} = $host{query_fail} ? 1 : 0; - local $ENV{QUERY_WARNS} = $host{query_warns} ? 1 : 0; - local $ENV{SOURCE_ONLY} = $host{source_only} ? 1 : 0; - local $ENV{ENTRY} = $host{entry} || 'check'; - my $out = `bash '$driver' 2>&1`; - my $rc = $? >> 8; - my @probes; - if ( open( my $cfh, '<', $calls ) ) { chomp( @probes = <$cfh> ); close($cfh); } - return ( $rc, $out, join( ';', @probes ) ); -} - -my ( $rc, $out, $probes ); - -( $rc, $out, $probes ) = check( version => '9.5', epel => 1, crb => 1 ); -is( $rc, 0, 'EL9 with EPEL and CRB passes' ); -like( $probes, qr/perl-Crypt-CBC.*;.*perl-IO-Tty/, '... after probing both repositories' ); -like( $probes, qr/^repoquery /, '... through repoquery, which an installed copy does not satisfy' ); -like( $probes, qr/--arch x86_64,noarch/, '... limited to the binary architectures' ); - -( $rc, $out, $probes ) = check( version => '9.5', epel => 0, crb => 1 ); -is( $rc, 1, 'EL9 without EPEL stops' ); -like( $out, qr/epel-release-latest-9\.noarch/, '... and names the EL9 EPEL release package' ); - -( $rc, $out, $probes ) = check( version => '10.2', epel => 1, crb => 1 ); -is( $rc, 0, 'EL10 with EPEL and CRB passes' ); -like( $probes, qr/perl-Crypt-CBC.*;.*perl-IO-Tty/, '... after probing both repositories' ); - -( $rc, $out, $probes ) = check( version => '10.2', epel => 0, crb => 1 ); -is( $rc, 1, 'EL10 without EPEL stops' ); -like( $out, qr/requires EPEL repository/, '... and names the missing repository' ); -like( $out, qr/epel-release-latest-10\.noarch/, '... and the EL10 EPEL release package' ); - -( $rc, $out, $probes ) = check( distro => 'rhel', version => '10.2', epel => 1, crb => 0 ); -is( $rc, 1, 'EL10 without CRB stops' ); -like( $out, qr/requires CRB repository/, '... and names the missing repository' ); -like( $out, qr/'dnf update epel-release' and then 'crb enable'/, '... with a current crb helper, which covers RHEL under RHSM and RHUI' ); -unlike( $out, qr/gpgcheck=0|centos-crb|subscription-manager/, '... and no repository file or subscription-only command' ); - -( $rc, $out, $probes ) = check( version => '10.2', epel => 0, crb => 1, source_only => 1 ); -is( $rc, 1, 'a source repository does not stand in for the binary one' ); -like( $out, qr/requires EPEL repository/, '... so the missing repository is still reported' ); - -( $rc, $out, $probes ) = check( distro => 'ol', version => '10.1', epel => 1, crb => 0 ); -is( $rc, 1, 'Oracle Linux 10 without CRB stops' ); -like( $out, qr/dnf config-manager --enable ol10_codeready_builder/, '... with the command that enables its CodeReady Builder' ); - -( $rc, $out, $probes ) = check( version => '10.2', epel => 1, crb => 1, query_fail => 1 ); -is( $rc, 1, 'a failed repository query stops' ); -like( $out, qr/Failed to download metadata/, '... with the package manager error' ); -unlike( $out, qr/requires EPEL repository/, '... and not as a missing repository' ); - -( $rc, $out, $probes ) = check( version => '10.2', epel => 0, crb => 1, query_warns => 1 ); -is( $rc, 1, 'a warning on stderr does not stand in for a package' ); -like( $out, qr/requires EPEL repository/, '... so the missing repository is still reported' ); - -( $rc, $out, $probes ) = check( version => '10.2', epel => 1, crb => 1, query_warns => 1 ); -is( $rc, 0, 'a warning beside a real match does not fail the check' ); - -( $rc, $out, $probes ) = check( distro => 'centos', version => '10', epel => 0, crb => 0 ); -is( $rc, 1, 'CentOS Stream 10, which reports the major version alone, is checked' ); -like( $out, qr/requires EPEL repository/, '... and told about EPEL' ); - -( $rc, $out, $probes ) = check( version => '8.10', epel => 0, crb => 0 ); -is( $rc, 0, 'EL8 is not checked' ); -is( $probes, '', '... and nothing is probed' ); - -( $rc, $out, $probes ) = check( distro => 'fedora', version => '42', epel => 0, crb => 0 ); -is( $rc, 0, 'Fedora is not checked' ); - -# The installer functions go-xcat dispatches to run the check before they install anything. -foreach my $entry (qw(dnf yum)) { - ( $rc, $out, $probes ) = check( entry => $entry, version => '10.2', epel => 0, crb => 1 ); - is( $rc, 1, "install_packages_$entry on EL10 without EPEL stops" ); - like( $out, qr/requires EPEL repository/, '... with the EPEL message' ); - unlike( $probes, qr/install/, '... before anything is installed' ); - - ( $rc, $out, $probes ) = check( entry => $entry, version => '10.2', epel => 1, crb => 1 ); - is( $rc, 0, "install_packages_$entry on EL10 with EPEL and CRB installs" ); - like( $probes, qr/perl-IO-Tty.*;.*install initscripts.*;.*install xCAT/, '... after the check passed' ); -} - -done_testing(); diff --git a/xCAT-test/unit/post_xcat_download_policy.t b/xCAT-test/unit/post_xcat_download_policy.t deleted file mode 100644 index 7cbf70492..000000000 --- a/xCAT-test/unit/post_xcat_download_policy.t +++ /dev/null @@ -1,48 +0,0 @@ -#!/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/catfile( $FindBin::Bin, '..', '..', - 'xCAT', 'postscripts', 'remoteshell' ); -plan skip_all => 'remoteshell not found' unless -r $script; - -open( my $fh, '<', $script ) or die "Unable to read $script: $!"; -my $source = do { local $/; <$fh> }; -close($fh); - -# A kill without the hyphen reads the signal number as one more process id, so -# the target gets the default signal, which a process can catch, and the -# process with that id is signalled as well. -my @bare = ( $source =~ /^[^#\n]*\bkill\s+\d/gm ); -is( scalar(@bare), 0, 'no kill gives the signal number without a hyphen' ); - -like( $source, qr/kill -9 \$PIDLIST/, - 'the ssh daemon gets the signal that a process cannot catch' ); - -done_testing(); diff --git a/xCAT-test/unit/remoteshell_kill_wait.t b/xCAT-test/unit/remoteshell_kill_wait.t deleted file mode 100644 index 505fd2418..000000000 --- a/xCAT-test/unit/remoteshell_kill_wait.t +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env perl -use strict; -use warnings; - -use File::Spec; -use FindBin; -use Test::More; - -my $script = File::Spec->catfile( $FindBin::Bin, '..', '..', - 'xCAT', 'postscripts', 'remoteshell' ); -plan skip_all => 'remoteshell not found' unless -r $script; -plan skip_all => 'no bash' unless -x '/bin/bash'; - -open( my $fh, '<', $script ) or die "Unable to read $script: $!"; -my $source = do { local $/; <$fh> }; -close($fh); - -my ($loop) = $source =~ /(waited=0\n.*?\n done)/s; -ok( defined($loop), 'the wait loop was found in the postscript' ); - -# Run the loop of the postscript against real processes, with a shorter bound -# so that the test does not spend the whole timeout of the postscript. -sub waits_for { - my (@pids) = @_; - ( my $body = $loop ) =~ s/\$waited -lt 10/\$waited -lt 3/; - my $list = join( ' ', @pids ); - my $out = `/bin/bash -c 'PIDLIST="$list"\n$body\necho \$alive' 2>/dev/null`; - chomp $out; - return $out; -} - -# A process that is still running must be reported as alive, and the loop must -# give up rather than run for ever. -my $child = fork(); -if ( !defined $child ) { plan skip_all => 'cannot fork' } -if ( $child == 0 ) { exec( 'sleep', '30' ); exit 1 } -my $start = time; -is( waits_for($child), '1', 'a process that is still running is reported alive' ); -cmp_ok( time - $start, '<', 10, 'the loop gives up instead of waiting for ever' ); -kill 'KILL', $child; -waitpid( $child, 0 ); - -# A process that has ended must be reported as gone, without waiting. -my $gone = fork(); -if ( $gone == 0 ) { exit 0 } -waitpid( $gone, 0 ); -$start = time; -is( waits_for($gone), '0', 'a process that has ended is reported gone' ); -cmp_ok( time - $start, '<', 3, 'no time is spent once the process is gone' ); - -# The bound of the postscript itself, and the shape of the test. -like( $source, qr/while \[ \$waited -lt 10 \]/, 'the postscript waits at most ten seconds' ); -like( $source, qr/kill -0 \$pid/, 'the check for a running process sends no signal' ); -like( $source, qr/kill -9 \$PIDLIST/, 'the daemon still gets the signal it cannot catch' ); - -# The wait has to happen before the new daemon starts. -my ($block) = $source =~ /(PIDLIST=.*?\/usr\/sbin\/sshd)/s; -ok( defined($block), 'the fallback block was found' ); -cmp_ok( index( $block, 'waited=0' ), '<', index( $block, '/usr/sbin/sshd' ), - 'the wait comes before the new daemon starts' ); - -done_testing(); diff --git a/xCAT-test/unit/sles_pre_script.t b/xCAT-test/unit/sles_pre_script.t deleted file mode 100644 index 1ffa01eea..000000000 --- a/xCAT-test/unit/sles_pre_script.t +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env perl -use strict; -use warnings; -use Test::More; - -my $pre_sles_path = defined $ENV{XCATROOT} ? "$ENV{XCATROOT}/share/xcat/install/scripts/pre.sles" : ''; -$pre_sles_path = "xCAT-server/share/xcat/install/scripts/pre.sles" - unless -f $pre_sles_path; - -plan skip_all => "pre.sles not found" unless -f $pre_sles_path; - -my $src = do { local $/; open my $fh, '<', $pre_sles_path or die $!; <$fh> }; - -like($src, qr/sub set_sles11_uefi_bootloader\b|set_sles11_uefi_bootloader\(\)/, 'SLES UEFI bootloader helper exists'); -like($src, qr/install=\.\*sles11/, 'SLES 11 UEFI bootloader change is scoped to SLES 11 install media'); -like($src, qr/elilo<\/loader_type>/, 'SLES 11 UEFI install selects elilo'); -like($src, qr/mbr<\/location>/, 'legacy MBR template value is replaced at install time'); -like($src, qr/if \[ -d \/sys\/firmware\/efi \]; then\s+sed .*?set_sles11_uefi_bootloader/s, 'UEFI default partitioning applies bootloader helper'); - -done_testing(); diff --git a/xCAT-test/unit/statelite_add_ssh.t b/xCAT-test/unit/statelite_add_ssh.t deleted file mode 100644 index b3dc6ddbe..000000000 --- a/xCAT-test/unit/statelite_add_ssh.t +++ /dev/null @@ -1,17 +0,0 @@ -#!/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(); diff --git a/xCAT/postscripts/remoteshell b/xCAT/postscripts/remoteshell index 3850585f9..04fc145b4 100755 --- a/xCAT/postscripts/remoteshell +++ b/xCAT/postscripts/remoteshell @@ -631,20 +631,6 @@ fi #if the service restart with "service/systemctl" failed #try to kill the process and start if [ "$?" != "0" ];then - PIDLIST=`ps aux | grep -v grep | grep "/usr/sbin/sshd"|awk -F" " '{print $2}'|xargs` - if [ -n "$PIDLIST" ]; then - kill -9 $PIDLIST - waited=0 - while [ $waited -lt 10 ]; do - alive=0 - for pid in $PIDLIST; do - if kill -0 $pid 2>/dev/null; then alive=1; fi - done - if [ $alive -eq 0 ]; then break; fi - sleep 1 - waited=`expr $waited + 1` - done - fi - /usr/sbin/sshd + xcat_restart_sshd_after_failed_service_restart fi kill -9 $CREDPID diff --git a/xCAT/postscripts/xcatdsklspost b/xCAT/postscripts/xcatdsklspost index 13110575d..6e2ad718e 100755 --- a/xCAT/postscripts/xcatdsklspost +++ b/xCAT/postscripts/xcatdsklspost @@ -19,7 +19,12 @@ # ##################################################### -[ -f "/xcatpost/xcatlib.sh" ] && . /xcatpost/xcatlib.sh +if [ -f "/xcatpost/xcatlib.sh" ]; then + . /xcatpost/xcatlib.sh +else + str_dir_name=`dirname $0` + [ -f "$str_dir_name/xcatlib.sh" ] && . "$str_dir_name/xcatlib.sh" +fi if [ -f /xcatpost/mypostscript.post ]; then XCATDEBUGMODE=`grep 'XCATDEBUGMODE=' /xcatpost/mypostscript.post | cut -d= -f2 | tr -d \'\" | tr A-Z a-z` @@ -83,66 +88,6 @@ echolog() } -download_postscripts() -{ - server=$1 - if [ -z $server ]; then - return 1; - fi - - # Do not override the parameter --installdir - if [ -z "$INSTALLDIR" ]; then - if [ -f /opt/xcat/xcatinfo ]; then - INSTALLDIR=`grep 'INSTALLDIR' /opt/xcat/xcatinfo |cut -d= -f2` - fi - if [ -z "$INSTALLDIR" ]; then - INSTALLDIR="/install" - fi - fi - echolog "debug" "trying to download postscripts from http://$server$INSTALLDIR/postscripts/" - max_retries=5 - retry=0 - rc=1 # this is a fail return - while [ 0 -eq 0 ]; do - if [ -e "$xcatpost" ]; then - rm -rf "$xcatpost" - fi - - # 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 - # have additional checks for - # failed: Connection httpd not running - # 404: Not Found - if directory does not exist - grep -i -E "... failed: Connection refused.$" /tmp/wget.log - rc1=$? - grep -i -E "ERROR 404: Not Found.$" /tmp/wget.log - rc2=$? - # check to see no errors at all, grep returns 1 - if [ $rc1 -eq 1 ] && [ $rc2 -eq 1 ]; then - echolog "debug" "postscripts are downloaded from $server successfully." - return 0 - fi - fi - - retry=$(($retry+1)) - echolog "debug" "download_postscripts retry $retry" - if [ $retry -eq $max_retries ]; then - echolog "debug" "failed to download postscripts from http://$server$INSTALLDIR/postscripts/ after several retries." - break - fi - - SLI=$(awk 'BEGIN{srand(); printf("%d\n",rand()*20)}') - sleep $SLI - done - return $rc -} - - download_mypostscript() { server=$1 diff --git a/xCAT/postscripts/xcatlib.sh b/xCAT/postscripts/xcatlib.sh index b06838589..8e2dd1431 100755 --- a/xCAT/postscripts/xcatlib.sh +++ b/xCAT/postscripts/xcatlib.sh @@ -833,6 +833,107 @@ function msgutil { msgutil_r "" "$@" } +function xcat_download_postscripts { + local server="$1" + local install_dir="${2:-${INSTALLDIR:-/install}}" + local postroot="${3:-/$xcatpost}" + local log_file="${4:-/tmp/wget.log}" + + 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$install_dir/postscripts/" -P "$postroot" 2> "$log_file" +} + +function download_postscripts { + server=$1 + if [ -z $server ]; then + return 1; + fi + + # Do not override the parameter --installdir + if [ -z "$INSTALLDIR" ]; then + if [ -f /opt/xcat/xcatinfo ]; then + INSTALLDIR=`grep 'INSTALLDIR' /opt/xcat/xcatinfo |cut -d= -f2` + fi + if [ -z "$INSTALLDIR" ]; then + INSTALLDIR="/install" + fi + fi + echolog "debug" "trying to download postscripts from http://$server$INSTALLDIR/postscripts/" + max_retries=5 + retry=0 + rc=1 # this is a fail return + while [ 0 -eq 0 ]; do + if [ -e "$xcatpost" ]; then + rm -rf "$xcatpost" + fi + + xcat_download_postscripts "$server" "$INSTALLDIR" "/$xcatpost" "/tmp/wget.log" + rc=$? + if [ $rc -eq 0 ]; then + # return from wget was 0 but some OS do not return errors, so we + # have additional checks for + # failed: Connection httpd not running + # 404: Not Found - if directory does not exist + grep -i -E "... failed: Connection refused.$" /tmp/wget.log + rc1=$? + grep -i -E "ERROR 404: Not Found.$" /tmp/wget.log + rc2=$? + # check to see no errors at all, grep returns 1 + if [ $rc1 -eq 1 ] && [ $rc2 -eq 1 ]; then + echolog "debug" "postscripts are downloaded from $server successfully." + return 0 + fi + fi + + retry=$(($retry+1)) + echolog "debug" "download_postscripts retry $retry" + if [ $retry -eq $max_retries ]; then + echolog "debug" "failed to download postscripts from http://$server$INSTALLDIR/postscripts/ after several retries." + break + fi + + SLI=$(awk 'BEGIN{srand(); printf("%d\n",rand()*20)}') + sleep $SLI + done + return $rc +} + +function xcat_wait_for_processes_to_exit { + local pidlist="$1" + local max_wait="${2:-10}" + local waited=0 + local alive + local pid + + while [ $waited -lt $max_wait ]; do + alive=0 + for pid in $pidlist; do + if kill -0 $pid 2>/dev/null; then + alive=1 + fi + done + if [ $alive -eq 0 ]; then + return 0 + fi + sleep 1 + waited=`expr $waited + 1` + done + + return 1 +} + +function xcat_restart_sshd_after_failed_service_restart { + local sshd_cmd="${1:-/usr/sbin/sshd}" + local PIDLIST + + PIDLIST=`ps aux | grep -v grep | grep "/usr/sbin/sshd"|awk -F" " '{print $2}'|xargs` + if [ -n "$PIDLIST" ]; then + kill -9 $PIDLIST + xcat_wait_for_processes_to_exit "$PIDLIST" + fi + $sshd_cmd +} + function fetch_mypostscript { local postroot postroot=$1