diff options
Diffstat (limited to 'bin')
| -rwxr-xr-x | bin/cpu_stress | 75 | ||||
| -rwxr-xr-x | bin/cpuid.py | 5 | ||||
| -rwxr-xr-x | bin/disk_read_performance_test | 16 | ||||
| -rwxr-xr-x | bin/disk_stress_ng | 330 | ||||
| -rwxr-xr-x | bin/ipmi_test | 61 | ||||
| -rwxr-xr-x | bin/light_sensor_test | 46 | ||||
| -rwxr-xr-x | bin/memory_stress_ng | 190 | ||||
| -rwxr-xr-x | bin/pm_test | 12 | ||||
| -rwxr-xr-x | bin/roundtrip_qr.py | 121 | ||||
| -rwxr-xr-x | bin/snap_tests.py | 15 | ||||
| -rwxr-xr-x | bin/storage_test.py | 2 | ||||
| -rwxr-xr-x | bin/stress_ng_test | 594 | ||||
| -rwxr-xr-x | bin/virtualization | 7 | ||||
| -rwxr-xr-x | bin/wwan_tests.py | 4 | ||||
| -rwxr-xr-x | bin/xrandr_cycle | 17 |
15 files changed, 860 insertions, 635 deletions
diff --git a/bin/cpu_stress b/bin/cpu_stress deleted file mode 100755 index 56fccee..0000000 --- a/bin/cpu_stress +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/sh - -# Script to perform CPU stress tests -# -# Copyright (c) 2016 Canonical Ltd. -# -# Authors -# Rod Smith <rod.smith@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/>. -# -# The purpose of this script is to run CPU stress tests using the -# stress-ng program. -# -# Usage: -# cpu_stress [ --runtime <time-in-seconds> ] -# -# If --runtime is not specified, it defaults to 7200 (2 hours). - -runtime=7200 -if [ "$#" = "2" ] && [ "$1" = "--runtime" ] && [ "$2" -eq "$2" ] ; then - runtime=$2 -elif [ "$#" != "0" ] ; then - echo "Usage:" - echo " $0 [ --runtime <time-in-seconds> ]" - exit 1 -fi -echo "Setting run time to $runtime seconds" -# Add 10% to runtime; will forcefully terminate if stress-ng -# fails to return in that time. -end_time=$((runtime*11/10)) - -# NOTE: -# Options --af-alg 0 through --wcs 0 specify CPU stressors. As of stress-ng -# version 0.05.12, this is equivalent to --class cpu --all 0 --exclude numa,cpu_online. -# This script specifies stressors individually because the list of stressors keeps -# increasing, and we want consistency -- if the stress-ng version bumps up, we -# don't want new stressors being run. We're omitting numa because it's most -# useful on systems with massive numbers of CPUs, and cpu_online because it's -# failed on 4 of 8 test systems, so it seems too strict. -# Use "timeout" command to launch stress-ng, to catch it should it go into la-la land -timeout -s 9 $end_time stress-ng --aggressive --verify --timeout $runtime \ - --metrics-brief --tz --times \ - --af-alg 0 --bsearch 0 --context 0 --cpu 0 \ - --crypt 0 --hsearch 0 --longjmp 0 --lsearch 0 \ - --matrix 0 --qsort 0 --str 0 --stream 0 \ - --tsearch 0 --vecmath 0 --wcs 0 -result="$?" - -echo "**********************************************************" -if [ $result = "0" ] ; then - echo "* stress-ng CPU test passed!" -else - if [ $result = "137" ] ; then - echo "** stress-ng CPU test timed out and SIGKILL was used to " \ - "terminate the test (Error $result)!" - elif [ $return_code = "124" ] ; then - echo "* stress-ng CPU test timed out and was forcefully terminated " \ - "(Error $result)!" - else - echo "* stress-ng CPU test failed with result $result" - fi -fi -echo "**********************************************************" -exit $result diff --git a/bin/cpuid.py b/bin/cpuid.py index fcb6e0c..d0cdea9 100755 --- a/bin/cpuid.py +++ b/bin/cpuid.py @@ -78,12 +78,13 @@ is_64bit = ctypes.sizeof(ctypes.c_voidp) == 8 CPUIDS = { "Amber Lake": ['0x806e9'], "AMD EPYC": ['0x800f12'], - "AMD Opteron 6100": ['0x100f91'], + "AMD Lisbon": ['0x100f81'], + "AMD Magny-Cours": ['0x100f91'], "AMD ROME": ['0x830f10'], "Broadwell": ['0x4067', '0x306d4', '0x5066', '0x406f'], "Canon Lake": ['0x6066'], "Cascade Lake": ['0x50655', '0x50656', '0x50657'], - "Coffee Lake": ['0x806ea', '0x906e'], + "Coffee Lake": ['0x806ea', '0x906ea', '0x906eb', '0x906ec', '0x906ed'], "Haswell": ['0x306c', '0x4065', '0x4066', '0x306f'], "Ice Lake": ['0x706e'], "Ivy Bridge": ['0x306a', '0x306e'], diff --git a/bin/disk_read_performance_test b/bin/disk_read_performance_test index fec27c2..7734543 100755 --- a/bin/disk_read_performance_test +++ b/bin/disk_read_performance_test @@ -13,6 +13,10 @@ for disk in $@; do disk_type=`udevadm info --name /dev/$disk --query property | grep "ID_BUS" | awk '{gsub(/ID_BUS=/," ")}{printf $1}'` dev_path=`udevadm info --name /dev/$disk --query property | grep "DEVPATH" | awk '{gsub(/DEVPATH=/," ")}{printf $1}'` + # /sys/block/$disk/queue/rotational was added with Linux 2.6.29. If file is + # not present, test below will fail & disk will be considered an HDD, not + # an SSD. + rotational=`cat /sys/block/$disk/queue/rotational` if [[ $dev_path =~ dm ]]; then disk_type="devmapper" fi @@ -25,6 +29,12 @@ for disk in $@; do if [[ $dev_path =~ mmc ]]; then disk_type="mmc" fi + if [[ $dev_path =~ pmem ]]; then + disk_type="nvdimm" + fi + if [[ ($disk_type == "scsi" || $disk_type == "ata") && $rotational == 0 ]]; then + disk_type="ssd" + fi if [ -z "$disk_type" ]; then echo "ERROR: disk type not recognized" exit 1 @@ -49,7 +59,11 @@ for disk in $@; do "ide" ) MIN_BUF_READ=40;; "mmc" ) MIN_BUF_READ=$DEFAULT_BUF_READ;; "nvme" ) MIN_BUF_READ=200;; - "mdadm" ) MIN_BUF_READ=500;; + "nvdimm" ) MIN_BUF_READ=500;; + "mdadm" ) MIN_BUF_READ=80;; + "ata" ) MIN_BUF_READ=80;; + "scsi" ) MIN_BUF_READ=100;; + "ssd" ) MIN_BUF_READ=200;; * ) MIN_BUF_READ=$DEFAULT_BUF_READ;; esac echo "INFO: $disk_type: Using $MIN_BUF_READ MB/sec as the minimum throughput speed" diff --git a/bin/disk_stress_ng b/bin/disk_stress_ng deleted file mode 100755 index 9d8668e..0000000 --- a/bin/disk_stress_ng +++ /dev/null @@ -1,330 +0,0 @@ -#!/bin/bash - -# Script to disk stress tests using stress-ng -# -# Copyright (c) 2016 Canonical Ltd. -# -# Authors -# Rod Smith <rod.smith@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/>. -# -# The purpose of this script is to run disk stress tests using the -# stress-ng program. -# -# Usage: -# disk_stress_ng [ <device-filename> ] -# [ --base-time <time-in-seconds> ] -# [ --really-run ] -# -# Parameters: -# --disk-device -- This is the WHOLE-DISK device filename WITHOUT "/dev/" -# (e.g., sda). The script finds a filesystem on that -# device, mounts it if necessary, and runs the tests on -# that mounted filesystem. -# Test with iostat - -get_params() { - disk_device="/dev/sda" - short_device="sda" - base_time="240" - really_run="N" - while [ $# -gt 0 ] ; do - case $1 in - --base-time) base_time="$2" - shift - ;; - --really-run) really_run="Y" - ;; - *) disk_device="/dev/$1" - disk_device=`echo $disk_device | sed "s/\/dev\/\/dev/\/dev/g"` - short_device=$(echo $disk_device | sed "s/\/dev//g") - if [ ! -b $disk_device ] ; then - echo "Unknown block device \"$disk_device\"" - echo "Usage: $0 [ --base-time <time-in-seconds> ] [ --really-run ]" - echo " [ device-file ]" - exit 1 - fi - ;; - esac - shift - done - mounted_part="N" -} # get_params() - - -# Find the largest logical volume in an LVM partition. -# Output: -# $largest_part -- Device filename of largest qualifying partition -# $largest_size -- Size of largest qualifying partition -# $largest_fs -- Filesystem (ext4, etc.) used on largest qualifying partition -# Note: Above variables are initialized in find_largest_partition(), which -# calls this function. -# Caveat: If LVM is used, there can be no guarantee that a specific disk -# device is actually being tested. Thus, an LVM configuration should span -# just one disk device. LVM may be used on one disk, but subsequent disks -# should use "raw" partitions. -find_largest_lv() { - local partonly=$(echo $partition | cut -f 3 -d "/") - for syslv in $(ls -d /sys/block/dm-*/slaves/$partonly) ; do - lv=$(echo "$syslv" | cut -f 4 -d "/") - size=$(cat /sys/block/$lv/size) - sector_size=$(cat /sys/block/$lv/queue/hw_sector_size) - let size=$size*$sector_size - local blkid_info=$(blkid -s TYPE /dev/$lv | grep -E ext2\|ext3\|ext4\|xfs\|jfs\|btrfs) - if [ "$size" -gt "$largest_size" ] && [ -n "$blkid_info" ] ; then - local blkid_info=$(blkid -s TYPE /dev/$lv) - largest_size=$size - largest_part="/dev/$lv" - largest_fs=$(blkid -s TYPE "/dev/$lv" | cut -d "=" -f 2) - fi - done -} # find_largest_lv() - - -# Find the largest partition that holds a supported filesystem on $disk_device. -# Output: -# $largest_part -- Device filename of largest qualifying partition or logical volume -# $largest_size -- Size of largest qualifying partition or logical volume -# $largest_fs -- Filesystem (ext4, etc.) used on largest qualifying partition or logicl volume -# $unsupported_fs -- Empty or contains name of unsupported filesystem found on disk -find_largest_partition() { - largest_part="" - largest_size=0 - mapper_string="dm-" - if [ "${disk_device#*$mapper_string}" = "$disk_device" ]; then - partitions=$(lsblk -b -l -n -o NAME,SIZE,TYPE,MOUNTPOINT $disk_device | grep -E 'part|lvm|raid' | tr -s " ") - else - partitions=$(lsblk -b -l -n -o NAME,SIZE,TYPE,MOUNTPOINT $disk_device) - fi - unsupported_fs="" - for partition in $(echo "$partitions" | cut -d " " -f 1) ; do - if [ -b "/dev/$partition" ]; then - part_size=$(echo "$partitions" | grep "$partition " | cut -d " " -f 2) - part_location="/dev/$partition" - elif [ -b "/dev/mapper/$partition" ]; then - part_size=$(echo "$partitions" | grep "$partition " | cut -d " " -f 2) - part_location="/dev/mapper/$partition" - else - echo "$partition not found!" - echo "Aborting test" - exit 1 - fi - local blkid_info=$(blkid -s TYPE $part_location | grep -E ext2\|ext3\|ext4\|xfs\|jfs\|btrfs\|LVM2_member) - if [ "$part_size" -gt "$largest_size" ] && [ -n "$blkid_info" ] ; then - if [[ "$blkid_info" =~ .*LVM2_member.* ]] ; then - find_largest_lv - else - largest_size=$part_size - largest_part="$part_location" - largest_fs=$(blkid -s TYPE "$part_location" | cut -d "=" -f 2) - fi - fi - local blkid_info=$(blkid -s TYPE $part_location | grep -E ntfs\|vfat\|hfs) - if [ -n "$blkid_info" ] ; then - # If there's an NTFS, HFS+, or FAT filesystem on the disk make note of it.... - unsupported_fs=$(blkid -s TYPE "/dev/$partition" | cut -d "=" -f 2) - fi - done -} # find_largest_partition() - -# Find the largest filesystem on $disk_device. If that partition is not -# already mounted, try to mount it. -# Output: -# $test_dir -- Directory in which tests will occur -# $mount_point -- Location where filesystem is mounted -# $mounted_part -- Sets to "Y" if script mounted partition -# $made_mountpoint -- Sets to "Y" if script created the mount point -mount_filesystem() { - test_dir="/tmp/disk_stress_ng_$(uuidgen)" - if [ -b $disk_device ] - then - echo "$disk_device is a block device" - - #Add a check for warnings - WARN=$(parted -s ${disk_device} print | grep "^Warning.*${disk}.*[Rr]ead-only" 2>&1) - if [[ $? == 0 ]] - then - echo "Warning found in parted output:" - echo $WARN - echo "Aborting Test" - exit 1 - fi - else - echo "$disk_device is not a block device! Aborting!" - exit 1 - fi - - find_largest_partition - - if [ -n "$largest_part" ] ; then - echo "Found largest partition: \"$largest_part\"" - # If largest partition is too small, just abort with a message - if [ $largest_size -lt 10000000000 ] ; then - echo "Warning: $largest_part is less than 10GiB in size" - echo "Disk is too small to test. Aborting test!" - exit 1 - fi - mount_point=$(df | grep "$largest_part " | tr -s " " | cut -d " " -f 6) - if [ "$mount_point" == "" ] && [ "$really_run" == "Y" ] ; then - disk_device=$(echo $disk_device | sed "s/\/dev\/\/dev/\/dev/g") - mount_point="/mnt$short_device" - echo "No partition is mounted from $disk_device; attempting to mount one...." - if [ ! -d $mount_point ] ; then - mkdir -p "$mount_point" - made_mountpoint="Y" - fi - mount "$largest_part" "$mount_point" - mounted_part="Y" - fi - if [ "$mount_point" == "/" ] ; then - test_dir="/tmp/disk_stress_ng_$(uuidgen)" - else - test_dir="$mount_point/tmp/disk_stress_ng_$(uuidgen)" - fi - echo "Test will use $largest_part, mounted at \"$mount_point\", using $largest_fs" - else - echo "There appears to be no partition with a suitable filesystem" - echo "on $disk_device; please create a suitable partition and re-run" - echo "this test." - if [ -n "unsupported_fs" ] ; then - echo "NOTE: A filesystem of type $unsupported_fs was found, but is not supported" - echo "by this test. A Linux-native filesystem (ext2/3/4fs, XFS, JFS, or Btrfs)" - echo "is required." - fi - exit 1 - fi -} # mount_filesystem() - - -# Run an individual stressor -# Input: -# $1 = stressor name (e.g., copyfile, dentry) -# $2 = run time -# Output: -# had_error -- sets to "1" if an error occurred -run_stressor() { - local runtime="$2" - # Multiply runtime by 5; will forcefully terminate if stress-ng - # fails to return in that time. - end_time=$((runtime*5)) - echo "Running stress-ng $1 stressor for $2 seconds...." - # Use "timeout" command to launch stress-ng, to catch it should it go into - # la-la land - timeout -s 14 $end_time stress-ng --aggressive --verify --timeout $runtime \ - --temp-path $test_dir --$1 0 --hdd-opts dsync --readahead-bytes 16M -k - return_code="$?" - echo "return_code is $return_code" - if [ "$return_code" != "0" ] ; then - # - # a small grace period to allow stressors to terminate - # - sleep 10 - # - # still running? aggressively kill all stressors - # - pids=$(pidof stress-ng) - if [ -n "$pids" ]; then - kill -9 $pids - sleep 1 - kill -9 $pids - pids=$(pidof stress-ng) - if [ -n "$pids" ]; then - echo "Note: stress-ng (PIDS $pids) could not be killed" - fi - fi - had_error=1 - echo "*****************************************************************" - if [ $return_code = "124" ] ; then - echo "** stress-ng $stressor test timed out and was forcefully " \ - "terminated! (Error $return_code)" - elif [ $return_code = "137" ] ; then - echo "** stress-ng $stressor test timed out and SIGKILL was used to " \ - "terminate the test case! (Error $return_code)" - else - echo "** Error $return_code reported on stressor $stressor!)" - fi - echo "*****************************************************************" - had_error=1 - result=$return_code - fi -} # run_stressor() - - -# -# Main program body.... -# - - -get_params "$@" -mount_filesystem -echo "test_dir is $test_dir" - -had_error=0 - -# Tests Colin said to try but that aren't present as of standard stress-ng -# in Ubuntu 16.04: -# -# "chown" "copyfile" "ioprio" "locka" "lockofd" "madvise" "msync" "seal" -# -# TODO: Consider adding these tests for Ubuntu 18.04, or ealier with an -# updated stress-ng in the certification PPA.... - -disk_stressors=("aio" "aiol" "chdir" "chmod" "dentry" "dir" "fallocate" \ - "fiemap" "filename" "flock" "fstat" "hdd" "lease" "lockf" \ - "mknod" "readahead" "seek" "sync-file" "xattr") - -total_runtime=$((${#disk_stressors[@]}*$base_time)) - -# -# Ensure we have emnough async I/O events available, scale it -# based on number of CPUs on the machine -# -if [ -e /proc/sys/fs/aio-max-nr ] ; then - aiomax=$((8192 * $(nproc))) - aionow=$(cat /proc/sys/fs/aio-max-nr) - if [ $aiomax -gt $aionow ] ; then - echo $aiomax > /proc/sys/fs/aio-max-nr - echo "Setting aio-max-nr to $aiomax" - fi -fi - -echo "Estimated total run time is $total_runtime seconds" -echo "" - -if [ "$really_run" == "Y" ] ; then - mkdir -p "$test_dir" - for stressor in ${disk_stressors[@]}; do - run_stressor $stressor $base_time - done - rm -rf "$test_dir" - if [ "$mounted_part" == "Y" ] ; then - umount "$mount_point" - if [ "$made_mountpoint" == "Y" ] ; then - rmdir "$mount_point" - fi - fi -else - echo "To actually run tests, pass the --really-run option." - echo "Script is now terminating...." - exit 1 -fi - -echo "*******************************************************************" -if [ $had_error = "0" ] ; then - echo "** stress-ng disk test passed!" -else - echo "** stress-ng disk test failed; most recent error was $result" -fi -echo "*******************************************************************" -exit $result diff --git a/bin/ipmi_test b/bin/ipmi_test index 12ca42c..74a320b 100755 --- a/bin/ipmi_test +++ b/bin/ipmi_test @@ -1,6 +1,8 @@ #!/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" @@ -23,37 +25,60 @@ for module in ipmi_si ipmi_devintf ipmi_powernv ipmi_ssif ipmi_msghandler; do fi done -# Now get our info from ipmitool to make sure communication works -# First lest check chassis status -echo -echo "Checking for chassis status" -ipmitool chassis status && echo "Successfully got chassis status" && chassis=0 || chassis=1 -echo "Checking to see if we can get power status" -ipmitool power status && echo "Successfully got power status" && power=0 || power=1 +# 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 "Checking to see if we can get user data" + +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 ipmitool channel getaccess $x 2>1 >/dev/null; then + if !(ipmi-config --checkout --lan-channel-number $x 2>&1 >/dev/null | grep -q '^Unable to get Number of Users$'); then channel=$x - echo "Channel in use appears to be $channel" + echo "IPMI channel: $channel" && echo break fi done -if [ $channel -lt 99 ]; then - ipmitool user list $channel && echo "Successfully got user data" && user=0 || user=1 -else - user=1 -fi -echo "Checking to see if we can get info on the BMC" -ipmitool bmc info && echo "Successfully got BMC information" && bmc=0 || bmc=1 +# 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 ] && exit 0 || echo "FAILURE: chassis: $chassis power: $power user: $user bmc: $bmc" +[ $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/light_sensor_test b/bin/light_sensor_test new file mode 100755 index 0000000..f413396 --- /dev/null +++ b/bin/light_sensor_test @@ -0,0 +1,46 @@ +#!/bin/bash + +#Check if light sensor driver available, this section will be retired after checkbox resource jobs implement the light sensor in Device check +#Bug for reference https://bugs.launchpad.net/checkbox-support/+bug/1864960 +als_sensors=$(udevadm info --export-db|grep hid_sensor_als) + +#Check hid_sensor_als driver is loaded and available first. +if [ -z "$als_sensors" ] +then + echo "Light sensor driver not found" + exit 1 +else + echo "Light sensor driver is available" + echo "$als_sensors" +fi + +echo -e "\e[91mStart testing Ambient Light Sensor......\e[0m" +sleep 2 +echo -e "\e[92mwaiting for sensor to be covered......\e[0m" +sleep 3 + +#Output and print light sensor events 5 sec to light_sensor_test.log +timeout 5 monitor-sensor | tee $PLAINBOX_SESSION_SHARE/light_sensor_test.log & + + +#Print backlight value for 5 sec on the screen +for i in {1..10} +do + echo "Current Backlight Percentage is:" $(gdbus call --session --dest org.gnome.SettingsDaemon.Power --object-path /org/gnome/SettingsDaemon/Power --method org.freedesktop.DBus.Properties.Get org.gnome.SettingsDaemon.Power.Screen Brightness)| tr -d '()<>,' + sleep 0.5 +done + +# Fail when the user didn't wave their hand and no events have been collected. +if [[ $(cat $PLAINBOX_SESSION_SHARE/light_sensor_test.log | grep "Light changed" | wc -l) -lt 5 ]]; then +echo -e "\e[91mFAIL: Not enough data to be collect, Please rerun the test case and wave your hand around Light Sensor.\e[0m" +exit 1 +fi + + +#Print 5 values of the Light sensor value form log file +for i in {1..5} +do + echo "Ambient light sensor value " $i: `grep 'Light' $PLAINBOX_SESSION_SHARE/light_sensor_test.log | awk '{print $3}' | sed -n "$i"p` +done +exit 0 + diff --git a/bin/memory_stress_ng b/bin/memory_stress_ng deleted file mode 100755 index e717f12..0000000 --- a/bin/memory_stress_ng +++ /dev/null @@ -1,190 +0,0 @@ -#!/bin/bash - -# Script to perform memory stress tests -# -# Copyright (c) 2016 Canonical Ltd. -# -# Authors -# Rod Smith <rod.smith@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/>. -# -# The purpose of this script is to run memory stress tests using the -# stress-ng program. It also happens to impose a heavy CPU load, but -# that's a side effect of the memory stressors, not their purpose. -# -# Usage: -# memory_stress_ng [ --base-time <time> ] [ --time-per-gig <time> ] -# -# Parameters: -# --base-time is the time in seconds to run each stressor. (The default -# is 300 seconds, or five minutes.) -# --time-per-gig is extra time given to SOME stressors, measured in a -# seconds per GiB way. (The default is 10 seconds per GiB.) -# -# There are a total of 22 constant-run-time stressors and 6 variable- -# run-time stressors. Given the defaults, this works out to a total -# expected default run time of 8400 seconds (145 minutes) plus 60 seconds -# per GiB of RAM -- so a system with 16 GiB should take 156 minutes; one -# with 32 GiB should take 172 minutes, and so on, using the default -# values. - - -get_params() { - base_time=300 - time_per_gig=10 - while [ $# -gt 0 ] ; do - case $1 in - --base-time) base_time="$2" - shift - ;; - --time-per-gig) time_per_gig="$2" - shift - ;; - *) echo "Usage: $0 [ --base-time <time> ] [ --time-per-gig <time> ]" - exit 1 - ;; - esac - shift - done - local extra_time=$(($time_per_gig * $total_mem_in_GiB)) - variable_time=$(($base_time + $extra_time )) -} # get_params() - - -# Run an individual stressor -# Input: -# $1 = stressor name (e.g., malloc, brk) -# $2 = run time -# Output: -# had_error -- sets to "1" if an error occurred -run_stressor() { - local runtime="$2" - # Double runtime; will forcefully terminate if stress-ng - # fails to return in that time. - end_time=$((runtime*2)) - echo "Running stress-ng $1 stressor for $2 seconds...." - logger -t "memory_stress_ng" "Running stress-ng $1 stressor for $2 seconds..." - # Use "timeout" command to launch stress-ng, to catch it should it go into la-la land - timeout -s 14 $end_time stress-ng -k --aggressive --verify --timeout $runtime --$1 0 - return_code="$?" - echo "return_code is $return_code" - if [ "$return_code" != "0" ] ; then - # - # a small grace period to allow stressors to terminate - # - sleep 10 - # - # still running? aggressively kill all stressors - # - pids=$(pidof stress-ng) - if [ -n "$pids" ]; then - kill -9 $pids - sleep 1 - kill -9 $pids - pids=$(pidof stress-ng) - if [ -n "$pids" ]; then - echo "Note: stress-ng (PIDS $pids) could not be killed" - fi - fi - had_error=1 - echo "*****************************************************************" - if [ $return_code = "124" ] ; then - echo "** stress-ng $stressor timed out and was forcefully " - "terminated! (Error $return_code)" - elif [ $return_code = "137" ] ; then - echo "** stress-ng memory test timed out and SIGKILL was used to " \ - "terminate the test case! (Error $return_code)" - else - echo "** Error $return_code reported on stressor $stressor!)" - fi - echo "*****************************************************************" - had_error=1 - result=$return_code - fi -} # run_stressor() - - -# -# Main program body.... -# - -swap_space=`cat /proc/meminfo | grep -i SwapTotal | tr -s " " | cut -f 2 -d " "` -if [ -z $swap_space ] || [ $swap_space = "0" ] ; then - echo "Swap space unavailable! Please activate swap space and re-run this test!" - exit 1 -fi - -# Total memory in KiB.... -total_mem_in_KiB=`cat /proc/meminfo | grep MemTotal | tr -s " " | cut -f 2 -d " "` -total_mem_in_GiB=$((($total_mem_in_KiB/1048576)+1)) -echo "Total memory is $total_mem_in_GiB GiB" - -get_params "$@" -echo "Constant run time is $base_time seconds per stressor" -echo "Variable run time is $variable_time seconds per stressor" - -had_error=0 - -command -v numactl >/dev/null 2>&1 -if [ $? == 0 ] ; then - numa_nodes=$(numactl --hardware | grep available | head -n 1 | cut -f 2 -d " ") -else - numa_nodes=1 -fi - -# NOTE: Specify stressors in two arrays rather than rely on stress-ng's -# --class memory,vm option for two reasons: -# 1. We want to run some stressors (those that exhaust all memory) -# for longer than others, so we need to specify different run -# times for different stressors. -# 2. stress-ng is constantly being updated with new tests. We don't -# want to run one set of tests on SUT 1 and a larger set of tests -# on SUT 2 if we happen to have updated stress-ng for some unrelated -# reason (like a bug fix); thus, we specify tests individually. - -# Constant-run-time stressors -- run them for the same length of time on all -# systems.... -crt_stressors=("bsearch" "context" "hsearch" "lsearch" "matrix" \ - "memcpy" "null" "pipe" "qsort" "stack" "str" "stream" \ - "tsearch" "vm-rw" "wcs" "zero" "mlock" "mmapfork" "mmapmany" \ - "mremap" "shm-sysv" "vm-splice") -if [ "$numa_nodes" -gt 1 ]; then - crt_stressors+=("numa") -fi -crt_runtime=$((${#crt_stressors[@]}*$base_time)) - -# Variable-run-time stressors -- run them longer on systems with more RAM.... -vrt_stressors=("malloc" "mincore" "vm" "bigheap" "brk" "mmap") -vrt_runtime=$((${#vrt_stressors[@]}*$variable_time)) - -total_runtime=$((($crt_runtime + $vrt_runtime) / 60)) -echo "Estimated total run time is $total_runtime minutes" -echo "" - -for stressor in ${crt_stressors[@]}; do - run_stressor $stressor $base_time -done - -for stressor in ${vrt_stressors[@]}; do - run_stressor $stressor $variable_time -done - -echo "*******************************************************************" -if [ $had_error = "0" ] ; then - echo "** stress-ng memory test passed!" -else - echo "** stress-ng memory test failed; most recent error was $result" -fi -echo "*******************************************************************" -exit $result diff --git a/bin/pm_test b/bin/pm_test index b03e680..3dcdf28 100755 --- a/bin/pm_test +++ b/bin/pm_test @@ -36,11 +36,13 @@ def main(): sys.exit(1) # Obtain name of the invoking user. - uid = os.getenv('SUDO_UID') or os.getenv('PKEXEC_UID') - if not uid: - sys.stderr.write('Unable to determine invoking user\n') - sys.exit(1) - username = pwd.getpwuid(int(uid)).pw_name + username = os.getenv('NORMAL_USER') + if not username: + uid = os.getenv('SUDO_UID') or os.getenv('PKEXEC_UID') + if not uid:q + sys.stderr.write('Unable to determine invoking user\n') + sys.exit(1) + username = pwd.getpwuid(int(uid)).pw_name LoggingConfiguration.set(args.log_level, args.log_filename, args.append) logging.debug('Invoking username: %s', username) diff --git a/bin/roundtrip_qr.py b/bin/roundtrip_qr.py new file mode 100755 index 0000000..e2eb5b5 --- /dev/null +++ b/bin/roundtrip_qr.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python3 +# This file is part of Checkbox. +# +# Copyright 2020 Canonical Ltd. +# Written by: +# Jonathan Cave <jonathan.cave@canonical.com> +# +# Checkbox 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. +# +# Checkbox 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 Checkbox. If not, see <http://www.gnu.org/licenses/>. + + +import os +import random +import string +import time +import subprocess as sp +import sys + +import pyqrcode +import zbar +from PIL import Image + + +def capture_rpi(name): + import picamera + file = os.path.join(os.path.expandvars( + '$PLAINBOX_SESSION_SHARE'), '{}_qrcapture.png'.format(name)) + with picamera.PiCamera() as camera: + time.sleep(2) + camera.capture(file) + return file + + +def capture_webcam(name): + file = os.path.join(os.path.expandvars( + '$PLAINBOX_SESSION_SHARE'), '{}_qrcapture.jpg'.format(name)) + cmd = ('gst-launch-1.0 v4l2src device=/dev/{} num-buffers=1 ! jpegenc !' + 'filesink location={}').format(name, file) + try: + sp.check_call(cmd, shell=True) + except (sp.CalledProcessError, OSError) as e: + print(e) + raise SystemExit('ERROR: failed to capture image') + return file + + +def generate_data(): + return ''.join(random.choice(string.ascii_letters) for i in range(20)) + + +def generate_qr_code(data): + return pyqrcode.create(data) + + +def display_code(qr): + with open('/dev/tty1', 'wb+', buffering=0) as term: + # clear the tty so the qr is always printed at the top of the sceen + term.write(str.encode('\033c')) + # print the qr code + term.write(qr.terminal(quiet_zone=5).encode()) + + +def decode_image(filename): + scanner = zbar.ImageScanner() + scanner.parse_config('enable') + pil = Image.open(filename).convert('L') + width, height = pil.size + raw = pil.tobytes() + image = zbar.Image(width, height, 'Y800', raw) + scanner.scan(image) + result = None + for code in image: + result = code.data + del(image) + if result is None: + raise SystemExit('ERROR: no qrcodes decoded') + return result + + +def main(): + if len(sys.argv) != 2: + raise SystemExit('ERROR: expected a device name') + name = sys.argv[1] + print('Testing device name: {}\n'.format(name)) + + test_str = generate_data() + print('Input string: {}'.format(test_str), flush=True) + + print('Generating QR code...', flush=True) + qr = generate_qr_code(test_str) + + print('Displaying on screen', flush=True) + display_code(qr) + + print('Capture image of screen', flush=True) + if name == 'vchiq': + file = capture_rpi(name) + else: + file = capture_webcam(name) + print('Image {} captured'.format(file)) + + print('Decoding image file', flush=True) + result = decode_image(file) + print('Decoded data: {}'.format(result)) + + if result != test_str: + raise SystemExit('FAIL: decoded data does not match input') + print('PASS: decoded data and input match') + + +if __name__ == '__main__': + main() diff --git a/bin/snap_tests.py b/bin/snap_tests.py index f548db8..fe80288 100755 --- a/bin/snap_tests.py +++ b/bin/snap_tests.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright 2015-2019 Canonical Ltd. +# Copyright 2015-2020 Canonical Ltd. # All rights reserved. # # Written by: @@ -15,7 +15,16 @@ from checkbox_support.snap_utils.snapd import Snapd # - the snap must not be installed at the start of the nested test plan # - the snap must be strictly confined (no classic or devmode flags) # - there must be different revisions on the stable & edge channels -TEST_SNAP = os.getenv('TEST_SNAP', 'test-snapd-tools') +try: + TEST_SNAP = os.environ['TEST_SNAP'] +except KeyError: + runtime = os.getenv('CHECKBOX_RUNTIME', '/snap/checkbox/current') + if 'checkbox18' in runtime: + TEST_SNAP = 'test-snapd-tools-core18' + elif 'checkbox20' in runtime: + TEST_SNAP = 'test-snapd-tools-core20' + else: + TEST_SNAP = 'test-snapd-tools' SNAPD_TASK_TIMEOUT = int(os.getenv('SNAPD_TASK_TIMEOUT', 30)) SNAPD_POLL_INTERVAL = int(os.getenv('SNAPD_POLL_INTERVAL', 1)) @@ -60,7 +69,7 @@ class SnapInstall(): parser.add_argument('channel', help='channel to install from') args = parser.parse_args(sys.argv[2:]) print('Install {}...'.format(TEST_SNAP)) - s = Snapd(SNAPD_TASK_TIMEOUT, SNAPD_POLL_INTERVAL) + s = Snapd(SNAPD_TASK_TIMEOUT, SNAPD_POLL_INTERVAL, verbose=True) s.install(TEST_SNAP, args.channel) print('Confirm in snap list...') data = s.list() diff --git a/bin/storage_test.py b/bin/storage_test.py index 4f363bc..1481a0d 100755 --- a/bin/storage_test.py +++ b/bin/storage_test.py @@ -30,7 +30,7 @@ def find_largest_partition(device): out = sp.check_output(cmd, shell=True) blk_devs = [BlkDev(*p.strip().split()) for p in out.decode(sys.stdout.encoding).splitlines()] - blk_devs[:] = [bd for bd in blk_devs if bd.type == 'part'] + blk_devs[:] = [bd for bd in blk_devs if bd.type in ('part', 'md')] if not blk_devs: raise SystemExit( 'ERROR: No partitions found on device {}'.format(device)) diff --git a/bin/stress_ng_test b/bin/stress_ng_test new file mode 100755 index 0000000..1bc384a --- /dev/null +++ b/bin/stress_ng_test @@ -0,0 +1,594 @@ +#!/usr/bin/env python3 +""" +Copyright (C) 2020 Canonical Ltd. + +Authors + Rod Smith <rod.smith@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/>. + +The purpose of this script is to run CPU, memory, and disk stress +tests using the stress-ng binary program. It replaces the older +cpu_stress, memory_stress_ng, and disk_stress_ng Bash scripts. +""" + + +from argparse import ( + ArgumentParser, + RawTextHelpFormatter +) +from subprocess import ( + CalledProcessError, + PIPE, + Popen, + STDOUT, + TimeoutExpired +) +import os +import psutil +import shlex +import shutil +import signal +import stat +import sys +import time +import uuid + +# 10GiB (smallest acceptable size for disk tests): +min_fs_size = 10 * 1024 * 1024 * 1024 +# Swap filename +my_swap = None + + +class stress_ng(): + """Interfaces with the external stress-ng binary -- accepts + test parameters, runs the test, and enables access to test + results.""" + + def __init__(self, + stressors=['str'], + wrapper_timeout=25, + sng_timeout=20, + test_dir="/tmp", + extra_options=""): + + self.stressors = stressors + self.wrapper_timeout = wrapper_timeout + self.sng_timeout = sng_timeout + self.test_dir = test_dir + self.extra_options = extra_options + self.results = "" + self.returncode = 0 + + def run(self): + """Run a stress-ng test, storing results in self.results.""" + + stressor_list = "--" + " 0 --".join(self.stressors) + command = "stress-ng --aggressive --verify --timeout {} {} {} 0". \ + format(int(self.sng_timeout), + self.extra_options, + stressor_list) + time_str = time.strftime("%d %b %H:%M", time.gmtime()) + if len(self.stressors) == 1: + print("{}: Running stress-ng {} stressor for {:.0f} seconds...". + format(time_str, self.stressors[0], self.sng_timeout)) + else: + print("{}: Running multiple stress-ng ".format(time_str) + + "stressors in parallel for {:.0f}".format(self.sng_timeout)) + print("seconds...") + try: + run = Popen(shlex.split(command), stderr=STDOUT, stdout=PIPE) + local_results = run.communicate(timeout=self.wrapper_timeout)[0] + self.results = (local_results.decode(encoding="utf-8", + errors="ignore")) + self.returncode = run.returncode + if self.returncode != 0: + print("stress_ng exited with code {}".format(self.returncode)) + except CalledProcessError as err: + print("stress_ng exited with code {}".format(err.returncode)) + self.results = err.stdout + self.returncode = run.returncode + except TimeoutExpired: + print("stress_ng timed out!") + os.kill(run.pid, signal.SIGINT) + self.results = "" + # For consistency with old bash script & "timeout" wrapper... + self.returncode = 124 + except KeyboardInterrupt: + self.results = "" + self.returncode = 125 + return self.returncode + + def get_results(self): + return self.results + + def get_returncode(self): + return self.returncode + + +"""Define CPU-related functions...""" + + +def stress_cpu(args): + """Run stress-ng tests on CPUs.""" + + retval = 0 + stressors = ['af-alg', 'bsearch', 'context', 'cpu', 'crypt', 'hsearch', + 'longjmp', 'lsearch', 'matrix', 'qsort', 'str', 'stream', + 'tsearch', 'vecmath', 'wcs'] + # Add 10% to runtime; will forcefully terminate if stress-ng + # fails to return in that time. + end_time = args.base_time * 11 / 10 + print("Estimated total run time is {:.0f} minutes\n". + format(args.base_time/60)) + + test_object = stress_ng(stressors=stressors, + sng_timeout=args.base_time, + wrapper_timeout=end_time, + extra_options="--metrics-brief --tz --times") + retval = test_object.run() + print(test_object.get_results()) + return retval + + +"""Define memory-related functions...""" + + +def num_numa_nodes(): + """Return the number of NUMA nodes supported by the CPU.""" + + if shutil.which("numactl") is None: + return 1 + else: + command = "numactl --hardware" + numactl = Popen(shlex.split(command), stderr=STDOUT, stdout=PIPE) + local_results = numactl.communicate()[0].split() + # local_results[1] will sometimes hold the number of NUMA nodes; + # but "numactl --hardware" sometimes returns the error message + # "No NUMA available on this system", so if this (or some other) + # error message appears, assume one NUMA node.... + try: + num_nodes = int(local_results[1]) + except ValueError: + num_nodes = 1 + return num_nodes + + +def swap_space_ok(args): + """Check available swap space. If too small, add more. The minimum + acceptable mount is defined as the GREATER of the amount specified + by the command-line -s/--swap-space option OR the amount specified + by the STRESS_NG_MIN_SWAP_SIZE environment variable. Both values are + specified in gibibytes (GiB). If neither is specified, a value of 0 + (no swap required) is assumed. + Returns: + - True if OK (already or after adding more) + - False if insufficient swap space""" + + retval = 0 + all_ok = True + global my_swap + min_swap_space = 0 + if "STRESS_NG_MIN_SWAP_SIZE" in os.environ: + min_swap_space = int(os.environ['STRESS_NG_MIN_SWAP_SIZE']) \ + * 1024 * 1024 * 1024 + if args.swap_size > min_swap_space: + min_swap_space = args.swap_size * 1024 * 1024 * 1024 + print("Minimum swap space is set to {:.0f} GiB". + format(min_swap_space / 1024 / 1024 / 1024)) + swap = psutil.swap_memory() + if swap.total < min_swap_space: + print("Swap space too small! Attempting to add more (this may take " + + "a while)....") + my_swap = "/swap-{}.img".format(uuid.uuid1()) + # Create swap file 10KiB bigger than minimum because there's a 4KiB + # overhead in the file, so if it were exactly the minimum, it would + # still be too small.... + try: + with open(my_swap, "w+b") as f: + # Swap file zeroed out and increased in size in 1KiB chunks to + # avoid problems with sparse files and creating temporary RAM + # use that potentially exceeds available RAM.... + for i in range(int((min_swap_space + 10240) / 1024)): + f.write(b"\x00" * 1024) + f.close() + except OSError: + print("Unable to create temporary swap file! Aborting test!") + f.close() + os.remove(my_swap) + all_ok = False + if all_ok: + os.chmod(my_swap, stat.S_IRUSR | stat.S_IWUSR) + cmd = "mkswap {}".format(my_swap) + Popen(shlex.split(cmd), stderr=STDOUT, stdout=PIPE).communicate()[0] + cmd = "swapon {}".format(my_swap) + Popen(shlex.split(cmd), stderr=STDOUT, stdout=PIPE).communicate()[0] + else: + retval = False + swap = psutil.swap_memory() + if swap.total < min_swap_space: + retval = False + else: + retval = True + return retval + + +def stress_memory(args): + """Run stress-ng tests on memory.""" + + retval = 0 + if not swap_space_ok(args): + return 130 + + ram = psutil.virtual_memory() + total_mem_in_gb = ram.total / 1073741824 + vrt = args.base_time + total_mem_in_gb * args.time_per_gig + print("Total memory is {:.1f} GiB".format(total_mem_in_gb)) + print("Constant run time is {} seconds per stressor". + format(args.base_time)) + print("Variable run time is {:.0f} seconds per stressor".format(vrt)) + print("Number of NUMA nodes is {}".format(num_numa_nodes())) + + # Constant-run-time stressors -- run them for the same length of time on + # all systems.... + crt_stressors = ['bsearch', 'context', 'hsearch', 'lsearch', 'matrix', + 'memcpy', 'null', 'pipe', 'qsort', 'stack', 'str', + 'stream', 'tsearch', 'vm-rw', 'wcs', 'zero', 'mlock', + 'mmapfork', 'mmapmany', 'mremap', 'shm-sysv', + 'vm-splice'] + if num_numa_nodes() > 1: + crt_stressors.append('numa') + + # Variable-run-time stressors -- run longer on systems with more RAM.... + vrt_stressors = ['malloc', 'mincore', 'vm', 'bigheap', 'brk', 'mmap'] + + est_runtime = len(crt_stressors) * args.base_time + \ + len(vrt_stressors) * vrt + print("Estimated total run time is {:.0f} minutes\n". + format(est_runtime/60)) + for stressor in crt_stressors: + test_object = stress_ng(stressors=stressor.split(), + sng_timeout=args.base_time, + wrapper_timeout=args.base_time*2) + retval = retval | test_object.run() + print(test_object.get_results()) + for stressor in vrt_stressors: + test_object = stress_ng(stressors=stressor.split(), sng_timeout=vrt, + wrapper_timeout=vrt*2) + retval = retval | test_object.run() + print(test_object.get_results()) + if my_swap is not None and args.keep_swap is False: + print("Deleting temporary swap file....") + cmd = "swapoff {}".format(my_swap) + Popen(shlex.split(cmd), stderr=STDOUT, stdout=PIPE).communicate()[0] + os.remove(my_swap) + return retval + + +"""Define disk-related functions...""" + + +def get_partition_data(file): + """Get partition details (size & type) on /dev/{file} & return in + dictionary.""" + + part_data = {} + part_data['name'] = file + + # Get size of device, in bytes.... + command = "blockdev --getsize64 /dev/{}".format(file) + run = Popen(shlex.split(command), stderr=STDOUT, stdout=PIPE) + part_data['size'] = int(run.communicate()[0]) + + # Get filesystem type.... + part_data['fs_type'] = "" + command = "blkid /dev/{} -o export".format(file) + run = Popen(shlex.split(command), stderr=STDOUT, stdout=PIPE) + local_results = run.communicate()[0].split() + for result in local_results: + result_str = result.decode(encoding="utf-8", errors="ignore") + if "TYPE" in result_str: + part_data['fs_type'] = result_str.split("=")[1] + return part_data + + +def find_mount_point(file): + """Find the mount point of /dev/{file}. + Returns: + * None if unmounted + * The mount point (as a string) if it's mounted.""" + + mount_point = None + command = "df /dev/{} --output=target".format(file) + run = Popen(shlex.split(command), stderr=STDOUT, stdout=PIPE) + output = run.communicate()[0].decode(encoding="utf-8", errors="ignore"). \ + split() + potential_mount_point = str(output[-1]) + # If df is fed a non-mounted-partition, it returns "/dev" as the + # mount point, so ignore that.... + if potential_mount_point != "/dev": + mount_point = potential_mount_point + return mount_point + + +class disk(): + """Interfaces to disk device, to check device status, find largest + partition, mount it, etc.""" + + def __init__(self, device=""): + self.device = device + self.all_parts = [] + self.unsupported_fs = None + self.test_dir = "/tmp" + lvm_detected = False + # Find final element of device name; for instance "sda" for "/dev/sda" + stripped_devname = self.device.split("/")[-1] + + # Do first pass to collect data on partitions & software RAID + # devices (which we treat like partitions).... + for file in os.listdir("/sys/class/block"): + if stripped_devname in file: + part_data = get_partition_data(file) + part_data['part_type'] = "partition" + if part_data['fs_type'] == "LVM2_member": + lvm_detected = True + self.all_parts.append(part_data) + + # Do another pass to collect data on logical volumes, if any exist + # on the target device.... + # NOTE: This code ignores where an LVM exists; it could span multiple + # disks, or be on one other than the one being tested. Canonical + # certification specifies use of partitions, not LVMs, so this code + # exists mainly for software development using development systems, + # not on servers actually being tested. + if lvm_detected: + for file in os.listdir("/sys/class/block/"): + if "dm-" in file: + part_data = get_partition_data(file) + part_data['part_type'] = "lv" + self.all_parts.append(part_data) + + def is_block_device(self): + try: + mode = os.stat(self.device).st_mode + if not stat.S_ISBLK(mode): + print("{} is NOT a block device! Aborting!". + format(self.device)) + return False + except FileNotFoundError: + print("{} does not exist! Aborting!".format(self.device)) + return False + return True + + def find_largest_partition(self): + """Find the largest partition that holds a supported filesystem on + self.device. Sets: + self.largest_part -- Dictionary containing information on largest + partition + self.unsupported_fs -- Empty or contains information about largest + unsupported filesystem (of certain known types) + found on disk""" + + self.largest_part = {'name': "", + 'size': 0, + 'part_type': "lv", + 'fs_type': ""} + self.unsupported_fs = None + + # A filesystem can be supported for the test; unsupported but worth + # noting in an error message; or unsupported and not worth noting. + # The first two categories are enumerated in lists.... + supported_filesystems = ['ext2', 'ext3', 'ext4', 'xfs', 'jfs', 'btrfs'] + unsupported_filesystems = ['ntfs', 'vfat', 'hfs', 'LVM2_member'] + + for part in self.all_parts: + new_sz = int(part['size']) + old_sz = int(self.largest_part['size']) + new_lv = part['part_type'] == "lv" + old_lv = self.largest_part['part_type'] == "lv" + if (new_sz > 0 and old_sz == 0) or \ + (new_sz > min_fs_size and old_sz < min_fs_size) or \ + (new_sz > min_fs_size and new_sz > old_sz and old_lv) or \ + (new_sz > old_sz and not new_lv): + if part['fs_type'] in supported_filesystems: + self.largest_part = part + elif part['fs_type'] in unsupported_filesystems: + # Make note of it if it might be an old filesystem + # that was not properly re-allocated.... + self.unsupported_fs = part + return self.largest_part + + def mount_filesystem(self, simulate): + print("Disk device is {}".format(self.device)) + target_part = self.find_largest_partition() + if target_part['name'] == "": + if self.unsupported_fs is not None: + print("A filesystem of type {} was found, but is not " + "supported by this test.". + format(self.unsupported_fs['fs_type'])) + print("A Linux-native filesystem (ext2/3/4fs, XFS, JFS, or " + "Btrfs) is required.") + else: + print("No suitable partition found!") + return False + + if target_part['size'] < min_fs_size: + print("Warning: {} is less than {:.0f} GiB in size!". + format(target_part['name'], min_fs_size/1024/1024/1024)) + print("Disk is too small to test. Aborting test!") + return False + + full_device = "/dev/{}".format(target_part['name']) + print("Testing partition {}".format(full_device)) + mount_point = find_mount_point(target_part['name']) + if simulate: + print("Run with --simulate, so not mounting filesystems.") + print("If run without --simulate, would mount {} to {}". + format(full_device, mount_point)) + print("(if not already mounted).") + else: + if not mount_point: + mount_point = "/mnt/{}".format(target_part['name']) + print("Trying to mount {} to {}...". + format(full_device, mount_point)) + os.makedirs(mount_point, exist_ok=True) + command = "mount {} {}".format(full_device, mount_point) + run = Popen(shlex.split(command), stderr=STDOUT, stdout=PIPE) + output = run.communicate()[0].decode(encoding="utf-8", + errors="ignore") + print(output) + else: + print("{} is already mounted at {}". + format(full_device, mount_point)) + self.test_dir = "{}/tmp/stress-ng-{}".format(mount_point, + uuid.uuid1()) + os.makedirs(self.test_dir, exist_ok=True) + return True + + +def stress_disk(args): + """Run stress-ng tests on disk.""" + + disk_stressors = ['aio', 'aiol', 'chdir', 'chmod', 'chown', 'dentry', + 'dir', 'fallocate', 'fiemap', 'filename', 'flock', + 'fstat', 'hdd', 'ioprio', 'lease', 'locka', 'lockf', + 'lockofd', 'madvise', 'mknod', 'msync', 'readahead', + 'seal', 'seek', 'sync-file', 'xattr'] + + retval = 0 + if "/dev" not in args.device and args.device != "": + args.device = "/dev/" + args.device + + test_disk = disk(args.device) + if not test_disk.is_block_device(): + return 131 + if test_disk.mount_filesystem(args.simulate): + est_runtime = len(disk_stressors) * args.base_time + print("Using test directory: '{}'".format(test_disk.test_dir)) + print("Estimated total run time is {:.0f} minutes\n". + format(est_runtime/60)) + retval = 0 + if not args.simulate: + for stressor in disk_stressors: + disk_options = "--temp-path {} ".format(test_disk.test_dir) + \ + "--hdd-opts dsync --readahead-bytes 16M -k" + test_object = stress_ng(stressors=stressor.split(), + sng_timeout=args.base_time, + wrapper_timeout=args.base_time*5, + extra_options=disk_options) + retval = retval | test_object.run() + print(test_object.get_results()) + if test_disk.test_dir != "/tmp" and not args.simulate: + shutil.rmtree(test_disk.test_dir, ignore_errors=True) + else: + retval = 132 + + return retval + + +"""Main program body...""" + + +def main(): + """Run a stress_ng-based stress run.""" + + parser = ArgumentParser( + description="Run tests based on stress-ng", + formatter_class=RawTextHelpFormatter) + subparsers = parser.add_subparsers() + + # Main cli options + cpu_parser = subparsers.add_parser('cpu', help=("Run CPU tests")) + memory_parser = subparsers.add_parser('memory', help=("Run memory tests")) + disk_parser = subparsers.add_parser('disk', help=("Run disk tests")) + + # Sub test options + # action = test_parser.add_mutually_exclusive_group() + + # CPU parameters + cpu_parser.add_argument("-b", "--base-time", type=int, default=7200, + help="Run time, in seconds (default=7200)") + + # Memory parameters + memory_parser.add_argument("-b", "--base-time", type=int, + help="Base time for each test, in seconds " + + "(default=300)", default=300) + memory_parser.add_argument("-t", "--time-per-gig", type=int, + help="Extra time per GiB for some stressors " + + "(default=10)", default=10) + memory_parser.add_argument("-s", "--swap-size", type=int, + help="swap size in GiB", default=0) + memory_parser.add_argument("-k", "--keep-swap", action="store_true", + help="Keep swap file, if added by test") + + # Disk parameters + disk_parser.add_argument("-d", "--device", type=str, required=True, + help="Disk device (/dev/sda, etc.)") + disk_parser.add_argument("-b", "--base-time", type=int, + help="Time for each test, in seconds " + + "(default=240)", default=240) + disk_parser.add_argument("-s", "--simulate", action="store_true", + help="Report disk info, but don't run tests") + + cpu_parser.set_defaults(func=stress_cpu) + memory_parser.set_defaults(func=stress_memory) + disk_parser.set_defaults(func=stress_disk) + + args = parser.parse_args() + + # logging.basicConfig(level=logging.INFO) + + if shutil.which("stress-ng") is None: + print("The stress-ng utility is not installed; exiting!") + return(128) + if not os.geteuid() == 0: + print("This program must be run as root (or via sudo); exiting!") + return(129) + + retval = 1 + if 'func' not in args: + parser.print_help() + else: + retval = args.func(args) + print("**************************************************************") + if retval == 0: + print("* stress-ng test passed!") + elif retval == 124: # Terminated by Python timeout + print("** stress-ng test timed out and was forcefully ") + print(" terminated (Error {})".format(retval)) + elif retval == 125: # Terminated by SIGINT + print("** stress-ng test timed out and SIGINT (Ctrl+C) " + + "was used to terminate") + print(" the test (Error {})!".format(retval)) + elif retval == 130: # Insufficient swap space for memory test + print("** Swap space unavailable! Please activate swap space " + + "and re-run this test!") + print(" (Error {})".format(retval)) + elif retval == 131: # Alleged disk device is not a device file + print("** {} is not a block device! Aborting!".format(args.device)) + print(" (Error {})".format(retval)) + elif retval == 132: # Unable to find a partition for disk test + print("** Unable to find a suitable partition! Aborting!") + print(" (Error {})".format(retval)) + elif retval == 137: # Terminated by SIGKILL + print("** stress-ng test timed out and SIGKILL was used to ") + print(" terminate the test (Error {})!".format(retval)) + else: + print("stress-ng test failed with return code: {}".format(retval)) + print("**************************************************************") + + return(retval) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/bin/virtualization b/bin/virtualization index 487fba5..2b7cd54 100755 --- a/bin/virtualization +++ b/bin/virtualization @@ -27,7 +27,6 @@ from argparse import ArgumentParser import os import logging import lsb_release -import platform import requests import shlex from subprocess import ( @@ -224,7 +223,7 @@ class KVMTest(object): if self.qemu_config['cloudimg_type'] == CLOUD_IMAGE_TYPE_TAR: cloud_iso = "%s-server-cloudimg-%s.tar.gz" % ( self.release, self.qemu_config['cloudimg_arch']) - elif alt_pattern is "modern": + elif alt_pattern == "modern": # LP 1635345 - yakkety and beyond have a new naming scheme cloud_iso = "%s-server-cloudimg-%s.img" % ( self.release, self.qemu_config['cloudimg_arch']) @@ -253,7 +252,7 @@ class KVMTest(object): logging.error(" * Message: {}".format(e.with_traceback(None))) return False - if ret.status_code is not 200: + if ret.status_code != 200: return False else: return True @@ -561,7 +560,7 @@ class LXDTest(object): self.name = 'testbed' self.image_alias = uuid4().hex self.default_remote = "ubuntu:" - self.os_version = platform.linux_distribution()[1] + self.os_version = lsb_release.get_distro_information()["RELEASE"] def run_command(self, cmd): task = RunCommand(cmd) diff --git a/bin/wwan_tests.py b/bin/wwan_tests.py index c19c547..df44cf6 100755 --- a/bin/wwan_tests.py +++ b/bin/wwan_tests.py @@ -261,7 +261,7 @@ def _ping_test(if_name): class ThreeGppConnection(): - def invoked(self, ctx): + def invoked(self): parser = argparse.ArgumentParser() parser.add_argument('wwan_control_if', type=str, help='The control interface for the device') @@ -274,7 +274,7 @@ class ThreeGppConnection(): args = parser.parse_args(sys.argv[2:]) ret_code = 1 try: - _create_3gpp_connection(ctx.args.wwan_control_if, args.apn) + _create_3gpp_connection(args.wwan_control_if, args.apn) _wwan_radio_on() time.sleep(args.wwan_setup_time) ret_code = _ping_test(args.wwan_net_if) diff --git a/bin/xrandr_cycle b/bin/xrandr_cycle index 34f953c..6743f6f 100755 --- a/bin/xrandr_cycle +++ b/bin/xrandr_cycle @@ -33,7 +33,8 @@ failure_messages = [] # remember which modes failed success_messages = [] # remember which modes succeeded # Run xrandr and ask it what devices and modes are supported -xrandrinfo = subprocess.Popen('xrandr -q', shell=True, stdout=subprocess.PIPE) +xrandrinfo = subprocess.Popen( + 'xrandr -q --verbose', shell=True, stdout=subprocess.PIPE) output = xrandrinfo.communicate()[0].decode().split('\n') @@ -62,12 +63,20 @@ for line in output: elif device_context != '': # we've previously seen a 'connected' dev # mode names seem to always be of the format [horiz]x[vert] # (there can be non-mode information inside of a device context!) - if foo[0].find('x') != -1: - modes.append((device_context, foo[0])) + x_pos = foo[0].find('x') + # for a resolution there has to be at least 3 chars: + # a digit, an x, and a digit + if x_pos > 0 and x_pos < len(foo[0]) - 1: + if 'DoubleScan' in foo: + # xrandr lists DoubleScan resolutions but cannot set them + # so for the purposes of this test let's not use them + continue + if foo[0][x_pos-1].isdigit() and foo[0][x_pos+1].isdigit(): + modes.append((device_context, foo[0])) # we also want to remember what the current mode is, which xrandr # marks with a '*' character, so we can set things back the way # we found them at the end: - if foo[1].find('*') != -1: + if "*current" in foo: current_modes.append((device_context, foo[0])) # let's create a dict of aspect_ratio:largest_width for each display # (width, because it's easier to compare simple ints when looking for the |
