diff options
author | PMR <pmr@pmr-lander> | 2020-04-23 13:58:46 +0000 |
---|---|---|
committer | PMR <pmr@pmr-lander> | 2020-04-23 13:58:46 +0000 |
commit | d2f6e1a6f652a1c205f9519109fcc8ac80a371bc (patch) | |
tree | d4811b6d136359d767d70f0f648e6c4c04fdd467 | |
parent | dc5c1e6549ea0cfed301ea12650953bf31b50bd7 (diff) | |
parent | 5bb77b16f3a4eec58db991e5717b2408f555978a (diff) |
record new upstream branch created by importing plainbox-provider-checkbox_0.53.0.orig.tar.gz and merge it
-rwxr-xr-x | bin/booted_kernel_tests.py | 8 | ||||
-rwxr-xr-x | bin/failed_service_check.sh | 11 | ||||
-rwxr-xr-x | bin/ipmi_test | 84 | ||||
-rwxr-xr-x | bin/ipmi_test.py | 324 | ||||
-rw-r--r-- | debian/.git-dpm | 14 | ||||
-rwxr-xr-x | manage.py | 2 | ||||
-rw-r--r-- | units/miscellanea/jobs.pxu | 2 | ||||
-rw-r--r-- | units/nvdimm/jobs.pxu | 4 | ||||
-rw-r--r-- | units/nvdimm/test-plan.pxu | 2 | ||||
-rw-r--r-- | units/power-management/jobs.pxu | 20 | ||||
-rw-r--r-- | units/power-management/test-plan.pxu | 2 | ||||
-rw-r--r-- | units/watchdog/jobs.pxu | 10 | ||||
-rw-r--r-- | units/watchdog/test-plan.pxu | 1 |
13 files changed, 386 insertions, 98 deletions
diff --git a/bin/booted_kernel_tests.py b/bin/booted_kernel_tests.py index bff9ff9..f190d8d 100755 --- a/bin/booted_kernel_tests.py +++ b/bin/booted_kernel_tests.py @@ -9,7 +9,8 @@ import hashlib import os import sys -from checkbox_support.snap_utils.system import get_kernel_snap +from checkbox_support.snap_utils.system import ( + get_kernel_snap, add_hostfs_prefix) # 64kb buffer, hopefully suitable for all devices that might run this test BUF_SIZE = 65536 @@ -54,8 +55,9 @@ if __name__ == '__main__': booted_kernel_image = sys.argv[1] print('Supplied booted kernel image: {}'.format(booted_kernel_image)) + prefixed_image = add_hostfs_prefix(booted_kernel_image) - if not os.path.exists(booted_kernel_image): + if not os.path.exists(prefixed_image): raise SystemExit('ERROR: invalid path to booted kernel supplied') - sys.exit(kernel_matches_current(booted_kernel_image)) + sys.exit(kernel_matches_current(prefixed_image)) diff --git a/bin/failed_service_check.sh b/bin/failed_service_check.sh new file mode 100755 index 0000000..172f20a --- /dev/null +++ b/bin/failed_service_check.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +COUNT=$(systemctl --system --no-ask-password --no-pager --no-legend list-units --state=failed | wc -l) +printf "Found %s failed units\n" "$COUNT" +if [ "$COUNT" -eq 0 ]; then + exit 0 +else + printf "\nFailed units:\n" + systemctl --system --no-ask-password --no-pager list-units --state=failed +fi +exit 1 diff --git a/bin/ipmi_test b/bin/ipmi_test deleted file mode 100755 index 74a320b..0000000 --- a/bin/ipmi_test +++ /dev/null @@ -1,84 +0,0 @@ -#!/bin/bash - -# Now make sure the modules are loaded - -echo '---------------------------------' && echo 'Verifying kernel modules:' && echo -for module in ipmi_si ipmi_devintf ipmi_powernv ipmi_ssif ipmi_msghandler; do - if lsmod |grep -q $module; then - echo "$module already loaded" - else - echo "Attempting to load $module..." - modprobe $module > /dev/null - result=$? - # If the IPMI drivers don't load, it could be the system has no BMC, or - # the IPMI driver is bad or not applicable to this SUT. - if [ $result -eq 1 ]; then - echo - echo "*******************************************" - echo "WARNING: Unable to load module $module" - echo "Continuing run, but in-band IPMI may fail" - echo "*******************************************" - echo - else - echo "Successfully loaded module $module\n\n" - fi - fi -done - - -# Now get our info from FreeIPMI to make sure communication works -# First lets check chassis status - -echo '---------------------------------' && echo "Fetching chassis status:" && echo -(ipmi-chassis --get-status) && echo && echo "Successfully fetched chassis status..." && chassis=0 \ - || chassis=1 - - -echo '---------------------------------' && echo "Fetching power status:" && echo -(ipmi-chassis --get-status | grep 'System Power') && echo \ - && echo "Successfully fetched power status.." && power=0 || power=1 - - -echo '---------------------------------' && echo "Fetching IPMI channel user data:" && echo -# LP:1794926 Find the active channel. blindly calling user list sometimes -# fails. -channel=99 -for x in 0 1 2 3 4 5 6 7 8 9 10 11 14 15; do - if !(ipmi-config --checkout --lan-channel-number $x 2>&1 >/dev/null | grep -q '^Unable to get Number of Users$'); then - channel=$x - echo "IPMI channel: $channel" && echo - break - fi -done - -# Extrapolate user list from general IPMI function -(ipmi-config --checkout --category=core | grep -A 19 "User[0-9]*.$" | sed '/#/d' | grep -v "Section$" | sed 's/Section //') \ - && echo && echo "Successfully fetched IPMI channel & user data..." && user=0 || user=1 - - -echo '---------------------------------' && echo "Fetching BMC information and checking IPMI version:" && echo -bmc-info && echo && echo "Successfully called BMC-info..." && echo && bmc=0 || bmc=1 - -version=$(bmc-info | awk '/IPMI Version/ {print $4}') -echo "IPMI Version: $version" && echo -# parse major from ipmi version -version=$(echo $version| cut -d'.' -f1) -# can refactor to evaluate in final check function -if [ $version -lt 2 ]; then - ipmiV=1 && echo "IPMI version below 2.0..." - else - ipmiV=0 && echo "IPMI version OK..." - fi - - -echo '---------------------------------' && echo "Calling IPMI-locate" && echo -(ipmi-locate) && echo "Successfully called ipmi-locate..." && ipmiL=0 || ipmiL=1 - - -# if everything passes, exit 0 -[ $chassis -eq 0 ] && [ $power -eq 0 ] && [ $user -eq 0 ] && [ $bmc -eq 0 ] && [ $ipmiV -eq 0 ] && [ $ipmiL -eq 0 ] \ - && echo "## IPMI checks succeeded. ##" && echo && exit 0 \ - || echo "## FAILURE: chassis: $chassis power: $power user: $user bmc: $bmc ipmi_version: $ipmiV ipmi_locate: $ipmiL ##" - -# otherwise exit 1 -exit 1 diff --git a/bin/ipmi_test.py b/bin/ipmi_test.py new file mode 100755 index 0000000..c074f6b --- /dev/null +++ b/bin/ipmi_test.py @@ -0,0 +1,324 @@ +#!/usr/bin/env python3 +""" +Copyright (C) 2020 Canonical Ltd. + +Authors + Adrian Lane <adrian.lane@canonical.com> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License version 3, +as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. + +Tests IPMI subsystem on SUT. +""" + +import re +import os +import shutil +import sys +import argparse +import logging +from subprocess import ( + Popen, + check_call, + PIPE, + TimeoutExpired, + SubprocessError) + + +class IpmiTest(object): + def __init__(self): + # paths to kernel_module binaries + self.path_lsmod = self._get_path('lsmod') + self.path_modprobe = self._get_path('modprobe') + # kernel modules to load/verify + self.kernel_modules = ( + 'ipmi_si', + 'ipmi_devintf', + 'ipmi_powernv', + 'ipmi_ssif', + 'ipmi_msghandler') + # paths to freeipmi tools + self.path_ipmi_chassis = self._get_path('ipmi-chassis') + self.path_ipmi_config = self._get_path('ipmi-config') + self.path_bmc_info = self._get_path('bmc-info') + self.path_ipmi_locate = self._get_path('ipmi-locate') + # function subprocess commands + self.cmd_kernel_mods = [ + 'sudo', self.path_lsmod] + self.cmd_ipmi_chassis = [ + 'sudo', self.path_ipmi_chassis, '--get-status'] + self.cmd_ipmi_channel = [ + 'sudo', self.path_ipmi_config, '--checkout', + '--lan-channel-number'] + self.cmd_bmc_info = [ + 'sudo', self.path_bmc_info] + self.cmd_ipmi_locate = [ + 'sudo', self.path_ipmi_locate] + # min. ipmi version to pass + self.ipmi_ver = 2.0 + # subprocess call timeout (s) + self.subproc_timeout = 10 + # raised subproc exceptions to handle + self.sub_proc_excs = ( + TimeoutExpired, + SubprocessError, + OSError, + TypeError) + + # fetch absolute path via shutil lib w/ exception handling + def _get_path(self, binary): + try: + path_full = shutil.which(binary) + return path_full + except (self.sub_proc_excs[2:3]): + logging.info('Unable to stat path via shutil lib!') + logging.info('Using relative paths...') + return binary + + # subprocess stdin/stderr handling + def _subproc_logging(self, cmd): + process = Popen( + cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True) + output, error = process.communicate(timeout=self.subproc_timeout) + logging.debug('## Debug Output: ##') + if (len(output) > 0): + # padding + logging.debug(' [Stdout]\n') + logging.debug(f'{output}\n') + if (len(error) > 0): + # padding + logging.debug(' [Stderr]\n') + logging.debug(f'{error}\n') + logging.debug('## End Debug Output ##\n') + return output + + # post-process exception handling + def _proc_exc(self, exc, subtest): + if (type(exc) == TimeoutExpired): + logging.info( + f'Timeout calling {subtest}!' + f' ({self.subproc_timeout}s)\n') + elif (type(exc) == TypeError): + logging.info( + f'Error calling {subtest}!' + ' Check your paths!\n') + else: + logging.info(f'Error calling {subtest}!\n') + + # kernel_mods() helper function to call modprobe + def _modprobe_hlpr(self, module): + try: + check_call( + [self.path_modprobe, module], + stderr=PIPE, timeout=self.subproc_timeout) + except self.sub_proc_excs: + logging.info(f'* Unable to load module {module}!') + logging.info(' **********************************************') + logging.info(f' Warning: proceeding, but in-band IPMI may fail') + logging.info(' **********************************************') + else: + logging.info(f'- Successfully loaded module {module}') + + # check (and load) kernel modules + def kernel_mods(self): + logging.info('-----------------------') + logging.info('Verifying kernel modules:') + try: + output = self._subproc_logging(self.cmd_kernel_mods) + for module in self.kernel_modules: + if module in output: + logging.info(f'- {module} already loaded') + else: + self._modprobe_hlpr(module) + logging.info('') + except self.sub_proc_excs as exc: + self._proc_exc(exc, 'lsmod') + + # get ipmi chassis data + # pass if called w/o error + def impi_chassis(self): + logging.info('-----------------------') + logging.info('Fetching chassis status:') + try: + self._subproc_logging(self.cmd_ipmi_chassis) + except self.sub_proc_excs as exc: + self._proc_exc(exc, 'ipmi_chassis()') + return 1 + else: + logging.info('Fetched chassis status!\n') + return 0 + + # get power status via ipmi chassis data + # pass if called w/o error & system power field present + def pwr_status(self): + logging.info('-----------------------') + logging.info('Fetching power status:') + regex = re.compile('^System Power') + try: + output = self._subproc_logging(self.cmd_ipmi_chassis) + for line in output.rstrip().split('\n'): + if re.search(regex, line): + logging.info('Fetched power status!\n') + return 0 + else: + logging.info('Unable to retrieve power status via IPMI.\n') + return 1 + except self.sub_proc_excs as exc: + self._proc_exc(exc, 'pwr_status()') + return 1 + + # ipmi_channel discovery loop + def _ipmi_channel_hlpr(self, i, matches, channel): + regex = re.compile('Section User') + cmd = self.cmd_ipmi_channel + if (len(cmd) > 4): + cmd.pop(-1) + cmd.append(str(i)) + output = self._subproc_logging(cmd) + for line in output.rstrip().split('\n'): + if re.search(regex, line): + matches.append(1) + channel.append(i) + break + return (matches, channel) + + # get ipmi channel(s) in use + # pass if user data returns after calling ipmi-config + def ipmi_channel(self): + logging.info('-----------------------') + logging.info('Fetching IPMI channel:') + matches = [] + # support multiple channels + channel = [] + # test channels 0 - 15 + for i in range(16): + try: + self._ipmi_channel_hlpr(i, matches, channel) + except self.sub_proc_excs as exc: + self._proc_exc(exc, 'ipmi_channel()') + return 1 + else: + if (sum(matches) > 0): + logging.info(f'Found {sum(matches)} channel(s)!') + logging.info(f'IPMI Channel(s): {channel}\n') + return 0 + else: + logging.info('Unable to fetch IPMI channel!\n') + return 1 + + # call bmc-info + # pass if called w/o error + def bmc_info(self): + logging.info('-----------------------') + logging.info('Fetching BMC information:') + try: + self._subproc_logging(self.cmd_bmc_info) + except self.sub_proc_excs as exc: + self._proc_exc(exc, 'bmc_info()') + return 1 + else: + logging.info('Fetched BMC information!\n') + return 0 + + # fetch ipmi version via bmc-info sdout + # pass if ipmi version >= self.ipmi_ver + def ipmi_version(self): + logging.info('-----------------------') + logging.info('Testing IPMI version:') + try: + output = self._subproc_logging(self.cmd_bmc_info) + # Prefer .index() over .find() for exception handling + res_index = output.index('IPMI Version') + version = output[(res_index + 24):(res_index + 27)] + logging.info(f'IPMI Version: {version}\n') + if (float(version) < float(self.ipmi_ver)): + logging.info(f'IPMI Version below {self.ipmi_ver}!\n') + return 1 + else: + return 0 + except self.sub_proc_excs as exc: + self._proc_exc(exc, 'ipmi_version()') + return 1 + + # call ipmi-locate + # pass if driver is loaded + def ipmi_locate(self): + logging.info('-----------------------') + logging.info('Testing ipmi-locate:') + regex = re.compile('driver:') + try: + output = self._subproc_logging(self.cmd_ipmi_locate) + if re.search(regex, output): + logging.info('Located IPMI driver!\n') + return 0 + else: + logging.info('Unable to locate IPMI driver!\n') + return 1 + except self.sub_proc_excs as exc: + self._proc_exc(exc, 'ipmi_locate()') + return 1 + + # initialize kernel modules and run ipmi tests + def run_test(self): + # load/val kernel modules + self.kernel_mods() + # tally results + results = [self.impi_chassis(), + self.pwr_status(), + self.ipmi_channel(), + self.bmc_info(), + self.ipmi_version(), + self.ipmi_locate()] + return results + + +def main(): + # init logging subsystem + # instantiate argparse as parser + parser = argparse.ArgumentParser() + parser.add_argument('-d', '--debug', action='store_true', + help='debug/verbose output (stdout/stderr)') + parser.add_argument('-q', '--quiet', action='store_true', + help='suppress output') + args = parser.parse_args() + if ((not args.quiet) or args.debug): + logger = logging.getLogger() + logger.setLevel(logging.DEBUG) + if (not args.quiet): + console_handler = logging.StreamHandler() + console_handler.setLevel(logging.INFO) + logger.addHandler(console_handler) + if args.debug: + console_handler.setLevel(logging.DEBUG) + + # instantiate IpmiTest as ipmi_test + # pass to [results] for post-processing + ipmi_test = IpmiTest() + results = ipmi_test.run_test() + # tally results + if (sum(results) > 0): + print ('-----------------------') + print ('## IPMI tests failed! ##') + print ( + f'## Chassis: {results[0]} Power: {results[1]} ', + f'Channel: {results[2]} BMC: {results[3]} ', + f'IPMI Version: {results[4]} IPMI Locate: {results[5]} ##') + return 1 + else: + print ('-----------------------') + print ('## IPMI tests passed! ##') + return 0 + + +# call main() +if __name__ == '__main__': + sys.exit(main()) diff --git a/debian/.git-dpm b/debian/.git-dpm index a9698ca..ccae789 100644 --- a/debian/.git-dpm +++ b/debian/.git-dpm @@ -1,8 +1,8 @@ # see git-dpm(1) from git-dpm package -5a75e501e0b67e53011d392fb2e4efc07bf59f3e -5a75e501e0b67e53011d392fb2e4efc07bf59f3e -5a75e501e0b67e53011d392fb2e4efc07bf59f3e -5a75e501e0b67e53011d392fb2e4efc07bf59f3e -plainbox-provider-checkbox_0.52.0.orig.tar.gz -e680f10962a972cf7760e14f9bc970b5e0875ab6 -2051169 +5bb77b16f3a4eec58db991e5717b2408f555978a +5bb77b16f3a4eec58db991e5717b2408f555978a +5bb77b16f3a4eec58db991e5717b2408f555978a +5bb77b16f3a4eec58db991e5717b2408f555978a +plainbox-provider-checkbox_0.53.0.orig.tar.gz +dbea87859d5e0a77fb633a68591e3a236a9f01cb +2052526 @@ -5,7 +5,7 @@ from plainbox.provider_manager import N_ setup( name='plainbox-provider-checkbox', namespace='com.canonical.certification', - version="0.52.0", + version="0.53.0", description=N_("Checkbox provider"), gettext_domain='plainbox-provider-checkbox', strict=False, deprecated=False, diff --git a/units/miscellanea/jobs.pxu b/units/miscellanea/jobs.pxu index d722295..95d781e 100644 --- a/units/miscellanea/jobs.pxu +++ b/units/miscellanea/jobs.pxu @@ -102,7 +102,7 @@ requires: package.name == 'ipmitool' or executable.name == 'ipmitool' cpuinfo.platform != 's390x' user: root -command: ipmi_test +command: ipmi_test.py _summary: Test IPMI in-band communications _description: diff --git a/units/nvdimm/jobs.pxu b/units/nvdimm/jobs.pxu index 6db1b66..b663e52 100644 --- a/units/nvdimm/jobs.pxu +++ b/units/nvdimm/jobs.pxu @@ -6,6 +6,7 @@ user: root requires: executable.name == "ipmctl" lsb.release >= "18.04" + nvdimm_resource.detected == "true" command: ipmctl show -dimm _summary: Verify that NVDIMMs are discovered @@ -20,7 +21,8 @@ user: root requires: executable.name == "ipmctl" lsb.release >= "18.04" -command: ipmctl show -d CurrentValue,CurrentState -sensor Health + nvdimm_resource.detected == "true" +command: ipmctl show -sensor Health _summary: Report health state of installed NVDIMM devices _description: diff --git a/units/nvdimm/test-plan.pxu b/units/nvdimm/test-plan.pxu index 9951039..601a788 100644 --- a/units/nvdimm/test-plan.pxu +++ b/units/nvdimm/test-plan.pxu @@ -3,5 +3,5 @@ unit: test plan _name: NVDIM Specific Tests _description: NVDIMM related tests include: - nvidmm/info + nvdimm/info nvdimm/health diff --git a/units/power-management/jobs.pxu b/units/power-management/jobs.pxu index 36e31c2..a84f004 100644 --- a/units/power-management/jobs.pxu +++ b/units/power-management/jobs.pxu @@ -290,6 +290,16 @@ user: root flags: preserve-locale noreturn autorestart estimated_duration: 180.0 +id: power-management/post-warm-reboot +after: power-management/warm-reboot +category_id: com.canonical.plainbox::power-management +_summary: Post warm reboot service check +_description: Check there are no failed services after the warm reboot +unit: job +plugin: shell +command: failed_service_check.sh +estimated_duration: 1.0 + id: power-management/cold-reboot category_id: com.canonical.plainbox::power-management _summary: Cold reboot @@ -307,6 +317,16 @@ user: root flags: preserve-locale noreturn autorestart estimated_duration: 300 +id: power-management/post-cold-reboot +after: power-management/cold-reboot +category_id: com.canonical.plainbox::power-management +_summary: Post cold reboot service check +_description: Check there are no failed services after the cold reboot +unit: job +plugin: shell +command: failed_service_check.sh +estimated_duration: 1.0 + unit: template template-resource: model_assertion template-unit: job diff --git a/units/power-management/test-plan.pxu b/units/power-management/test-plan.pxu index bb782c1..8391bca 100644 --- a/units/power-management/test-plan.pxu +++ b/units/power-management/test-plan.pxu @@ -75,7 +75,9 @@ _name: Automated power tests _description: Automated power tests for Snappy Ubuntu Core devices include: power-management/warm-reboot + power-management/post-warm-reboot power-management/cold-reboot + power-management/post-cold-reboot id: power-manual unit: test plan diff --git a/units/watchdog/jobs.pxu b/units/watchdog/jobs.pxu index 2b5b236..3247941 100644 --- a/units/watchdog/jobs.pxu +++ b/units/watchdog/jobs.pxu @@ -65,3 +65,13 @@ user: root plugin: shell category_id: com.canonical.plainbox::power-management estimated_duration: 60 + +id: watchdog/post-trigger-system-reset-auto +after: watchdog/trigger-system-reset-auto +category_id: com.canonical.plainbox::power-management +_summary: Post watchdog reset service check +_description: Check there are no failed services after the watchdog triggered +unit: job +plugin: shell +command: failed_service_check.sh +estimated_duration: 1.0 diff --git a/units/watchdog/test-plan.pxu b/units/watchdog/test-plan.pxu index 407a273..6de8e71 100644 --- a/units/watchdog/test-plan.pxu +++ b/units/watchdog/test-plan.pxu @@ -25,3 +25,4 @@ estimated_duration: 1s include: watchdog/systemd-config watchdog/trigger-system-reset-auto + watchdog/post-trigger-system-reset-auto |