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..4a47d4c7d 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/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/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/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/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..01fbe54d2 --- /dev/null +++ b/xCAT-test/README.md @@ -0,0 +1,21 @@ +# 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/bats/*.bats` | `bats -r xCAT-test/bats` | +| CLI functional tests | `xCAT-test/autotest/testcase/` and `xCAT-test/autotest/bundle/` | `xcattest -f -t ` or `xcattest -f -b ` | + +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/bats` instead. + +See `unit/README.md` and `bats/README.md` for the detailed rules for +each unit-test suite. diff --git a/xCAT-test/bats/README.md b/xCAT-test/bats/README.md new file mode 100644 index 000000000..ac65379ab --- /dev/null +++ b/xCAT-test/bats/README.md @@ -0,0 +1,21 @@ +# xCAT-test/bats + +Shell-script unit tests live here and run with: + +```bash +bats -r xCAT-test/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/bats/genesis_ib_modules.bats b/xCAT-test/bats/genesis_ib_modules.bats new file mode 100644 index 000000000..ed132043e --- /dev/null +++ b/xCAT-test/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/bats/go_xcat_common_repository.bats b/xCAT-test/bats/go_xcat_common_repository.bats new file mode 100644 index 000000000..b49f4fce0 --- /dev/null +++ b/xCAT-test/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/bats/go_xcat_el_repo_check.bats b/xCAT-test/bats/go_xcat_el_repo_check.bats new file mode 100644 index 000000000..0064f8673 --- /dev/null +++ b/xCAT-test/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/bats/helpers/go_xcat.bash b/xCAT-test/bats/helpers/go_xcat.bash new file mode 100644 index 000000000..ee4ec3c44 --- /dev/null +++ b/xCAT-test/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/bats/helpers/shell_source.bash b/xCAT-test/bats/helpers/shell_source.bash new file mode 100644 index 000000000..61a0bea8d --- /dev/null +++ b/xCAT-test/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/bats/post_xcat_download_policy.bats b/xCAT-test/bats/post_xcat_download_policy.bats new file mode 100644 index 000000000..41810bb97 --- /dev/null +++ b/xCAT-test/bats/post_xcat_download_policy.bats @@ -0,0 +1,88 @@ +#!/usr/bin/env bats + +load 'helpers/shell_source' + +setup() +{ + SCRIPT_LIB="$(repo_path 'xCAT-server/share/xcat/install/scripts/scriptlib')" + XCATDSKLSPOST="$(repo_path 'xCAT/postscripts/xcatdsklspost')" + [ -r "$SCRIPT_LIB" ] || skip "$SCRIPT_LIB is required" + [ -r "$XCATDSKLSPOST" ] || skip "$XCATDSKLSPOST is required" + export SCRIPT_LIB XCATDSKLSPOST +} + +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_xcatdsklspost_wget() +{ + local wget_log="$1" + local host_init_log="${BATS_TEST_TMPDIR}/host-init.log" + local download_log="${BATS_TEST_TMPDIR}/xcatdsklspost-wget-errors.log" + + cat() { printf 'cat %s\n' "$*" >>"$host_init_log"; return 1; } + grep() + { + printf 'grep %s\n' "$*" >>"$host_init_log" + command grep "$@" + } + dirname() { printf 'dirname %s\n' "$*" >>"$host_init_log"; return 1; } + + XCATDSKLSPOST_SOURCE_ONLY=1 + XCAT_WGET_LOG="$download_log" + source "$XCATDSKLSPOST" + [ ! -e "$host_init_log" ] || return 1 + [ "$XCAT_WGET_LOG" = "$download_log" ] || return 1 + unset -f cat grep dirname + + xcatpost="${BATS_TEST_TMPDIR}/xcatpost" + INSTALLDIR=/install + echolog() { :; } + sleep() { :; } + wget() + { + printf '%s\n' "$*" >"$wget_log" + printf '%s\n' 'mock wget stderr' >&2 + return 0 + } + + download_postscripts 192.0.2.10:80 + [ "$(read_file_or_empty "$download_log")" = "mock wget stderr" ] +} + +assert_download_policy() +{ + local args="$1" + + [[ "$args" == *'--reject index.html*,post.xcat.ng,post.xcat.rhels10'* ]] + [[ "$args" == *'--no-parent'* ]] + [[ "$args" == *'postscripts/'* ]] + [[ "$args" != *'>"$EVENT_LOG" + return 0 + fi + + poll_count=$((poll_count + 1)) + if [ "$poll_count" -eq 1 ]; then + printf 'poll %s alive\n' "$2" >>"$EVENT_LOG" + return 0 + fi + printf 'poll %s gone\n' "$2" >>"$EVENT_LOG" + return 1 + } + sleep() { :; } + sshd() + { + printf '%s\n' start >>"$EVENT_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 kills, waits, then starts sshd" { + EVENT_LOG="${BATS_TEST_TMPDIR}/events.log" + export EVENT_LOG + + run run_restart_fallback + [ "$status" -eq 0 ] + [ "$(read_file_or_empty "$EVENT_LOG")" = $'kill -9 4321\npoll 4321 alive\npoll 4321 gone\nstart' ] + run -1 grep -Eq '^kill 9( |$)' "$EVENT_LOG" +} + +@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/bats/sles_pre_script.bats b/xCAT-test/bats/sles_pre_script.bats new file mode 100644 index 000000000..bd8387ab0 --- /dev/null +++ b/xCAT-test/bats/sles_pre_script.bats @@ -0,0 +1,47 @@ +#!/usr/bin/env bats + +bats_require_minimum_version 1.5.0 + +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" + run -1 grep -q 'mbr' "$profile" + run -1 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/bats/statelite_add_ssh.bats b/xCAT-test/bats/statelite_add_ssh.bats new file mode 100644 index 000000000..b633666f9 --- /dev/null +++ b/xCAT-test/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..daff2691c 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 [`../bats`](../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..6f7dcc63a 100755 --- a/xCAT/postscripts/xcatdsklspost +++ b/xCAT/postscripts/xcatdsklspost @@ -19,26 +19,34 @@ # ##################################################### -[ -f "/xcatpost/xcatlib.sh" ] && . /xcatpost/xcatlib.sh +if [ "$XCATDSKLSPOST_SOURCE_ONLY" != "1" ]; then + 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` - MASTER_IP=`grep '^MASTER_IP=' /xcatpost/mypostscript.post |cut -d= -f2|sed s/\'//g` - NODE=`grep '^NODE=' /xcatpost/mypostscript.post |cut -d= -f2|sed s/\'//g` -else - for param in `cat /proc/cmdline`; do - key=`echo $param|awk -F= '{print $1}'` - if [ "$key" = "xcatdebugmode" ]; then - XCATDEBUGMODE=`echo $param|awk -F= '{print $2}'| tr -d \'\" | tr A-Z a-z` - fi + if [ -f /xcatpost/mypostscript.post ]; then + XCATDEBUGMODE=`grep 'XCATDEBUGMODE=' /xcatpost/mypostscript.post | cut -d= -f2 | tr -d \'\" | tr A-Z a-z` + MASTER_IP=`grep '^MASTER_IP=' /xcatpost/mypostscript.post |cut -d= -f2|sed s/\'//g` + NODE=`grep '^NODE=' /xcatpost/mypostscript.post |cut -d= -f2|sed s/\'//g` + else + for param in `cat /proc/cmdline`; do + key=`echo $param|awk -F= '{print $1}'` + if [ "$key" = "xcatdebugmode" ]; then + XCATDEBUGMODE=`echo $param|awk -F= '{print $2}'| tr -d \'\" | tr A-Z a-z` + fi - if [ "$key" = "LOGSERVER" ]; then - MASTER_IP=`echo $param|awk -F= '{print $2}'` - fi - done + if [ "$key" = "LOGSERVER" ]; then + MASTER_IP=`echo $param|awk -F= '{print $2}'` + fi + done + fi fi XCATINFOFILE=/opt/xcat/xcatinfo +XCAT_WGET_LOG=${XCAT_WGET_LOG:-/tmp/wget.log} #echolog: process message log and echo in xcatdsklspost #arguments: @@ -83,6 +91,45 @@ echolog() } +download_mypostscript() +{ + server=$1 + node=$2 + max_retries=$3 + TFTPDIR=$4 + if [ -z $server ]; then + return 1; + fi + if [ -z "$TFTPDIR" ]; then + TFTPDIR="/tftpboot" + fi + retry=0 + rc=1 + + + echolog "debug" "trying to download http://$server$TFTPDIR/mypostscripts/mypostscript.$node..." + while [ 0 -eq 0 ]; do + wget -N --waitretry=10 --random-wait -T 60 http://$server$TFTPDIR/mypostscripts/mypostscript.$node -P /$xcatpost 2>> "$XCAT_WGET_LOG" + rc=$? + # if no error and the file was downloaded + if [ $rc -eq 0 ] && [ -f /$xcatpost/mypostscript.$node ]; then + mv /$xcatpost/mypostscript.$node /$xcatpost/mypostscript + echolog "debug" "mypostscript.$node is downloaded successfully." + return 0 + fi + + + retry=$(($retry+1)) + if [ $retry -eq $max_retries ]; then + echolog "debug" "http://$server$TFTPDIR/mypostscripts/mypostscript.$node is not available." + break + fi + + done + return $rc +} + + download_postscripts() { server=$1 @@ -111,16 +158,16 @@ download_postscripts() # 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 + 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> "$XCAT_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 + grep -i -E "... failed: Connection refused.$" "$XCAT_WGET_LOG" rc1=$? - grep -i -E "ERROR 404: Not Found.$" /tmp/wget.log + grep -i -E "ERROR 404: Not Found.$" "$XCAT_WGET_LOG" rc2=$? # check to see no errors at all, grep returns 1 if [ $rc1 -eq 1 ] && [ $rc2 -eq 1 ]; then @@ -143,45 +190,6 @@ download_postscripts() } -download_mypostscript() -{ - server=$1 - node=$2 - max_retries=$3 - TFTPDIR=$4 - if [ -z $server ]; then - return 1; - fi - if [ -z "$TFTPDIR" ]; then - TFTPDIR="/tftpboot" - fi - retry=0 - rc=1 - - - echolog "debug" "trying to download http://$server$TFTPDIR/mypostscripts/mypostscript.$node..." - while [ 0 -eq 0 ]; do - wget -N --waitretry=10 --random-wait -T 60 http://$server$TFTPDIR/mypostscripts/mypostscript.$node -P /$xcatpost 2>> /tmp/wget.log - rc=$? - # if no error and the file was downloaded - if [ $rc -eq 0 ] && [ -f /$xcatpost/mypostscript.$node ]; then - mv /$xcatpost/mypostscript.$node /$xcatpost/mypostscript - echolog "debug" "mypostscript.$node is downloaded successfully." - return 0 - fi - - - retry=$(($retry+1)) - if [ $retry -eq $max_retries ]; then - echolog "debug" "http://$server$TFTPDIR/mypostscripts/mypostscript.$node is not available." - break - fi - - done - return $rc -} - - # pmatch determines if 1st argument string is matched by 2nd argument pattern pmatch () @@ -216,6 +224,10 @@ parsehttpserver () fi } +if [ "$XCATDSKLSPOST_SOURCE_ONLY" = "1" ]; then + return 0 2>/dev/null || exit 0 +fi + # Main # parse the arguments log_label="xcat.updatenode" diff --git a/xCAT/postscripts/xcatlib.sh b/xCAT/postscripts/xcatlib.sh index b06838589..6dd27ded5 100755 --- a/xCAT/postscripts/xcatlib.sh +++ b/xCAT/postscripts/xcatlib.sh @@ -833,6 +833,42 @@ function msgutil { msgutil_r "" "$@" } +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