From a08145450b513607f44ee0cc6749483c8265be5e Mon Sep 17 00:00:00 2001 From: Pierre Equoy Date: Tue, 7 Feb 2017 17:42:00 +0800 Subject: Update graphics_driver script to handle Matrox cards One of the devices being tested uses a Matrox card. This card is using a kernel mode setting (kms) driver called 'modesetting' to work, but this use case was not handled in our graphics_driver script that displays information about drivers currently in use. LP: #1655211 --- bin/graphics_driver | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bin/graphics_driver b/bin/graphics_driver index e1b6abd..dfb5522 100755 --- a/bin/graphics_driver +++ b/bin/graphics_driver @@ -183,11 +183,14 @@ class XorgLog(object): # EDID and Modelines # We use this part to determine which driver is in use - # For Intel / RADEON + # For Intel / RADEON / Matrox (using modesetting) m = re.search(r'\(II\) (.*)\(\d+\): EDID for output (.*)', line) if m: self.displays[display_name] = display - self.video_driver = m.group(1) + if m.group(1) == "modeset": + self.video_driver = "modesetting" + else: + self.video_driver = m.group(1) display_name = m.group(2) display = {'Output': display_name} continue -- cgit v1.2.3 From 26823e56b9219fca3d0b8e6e8dcd79bb272009f7 Mon Sep 17 00:00:00 2001 From: Jeff Lane Date: Wed, 8 Feb 2017 19:48:01 -0500 Subject: bin/virtualization: fixed bug that broke download of cloud image. LP: #1662580 --- bin/virtualization | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/bin/virtualization b/bin/virtualization index 0ee74e9..5b70a54 100755 --- a/bin/virtualization +++ b/bin/virtualization @@ -23,7 +23,6 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ - from argparse import ArgumentParser import configparser from glob import glob @@ -222,7 +221,7 @@ class KVMTest(object): cloud-images.ubuntu.com or on a maas server hosting a mirror of cloud-images.ubuntu.com """ - def _construct_filename(alt_pattern=None): + def _construct_filename(alt_pattern=None, initial_url=None): 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']) @@ -233,6 +232,10 @@ class KVMTest(object): elif self.qemu_config['cloudimg_type'] == CLOUD_IMAGE_TYPE_DISK: cloud_iso = "%s-server-cloudimg-%s-disk1.img" % ( self.release, self.qemu_config['cloudimg_arch']) + elif initial_url is not none: + # LP 1662580 - if we pass a full URL, assume the last piece is + # the filname and return that. + cloud_iso = initial_url.split('/')[-1] else: logging.error("Unknown cloud image type") sys.exit(1) @@ -306,17 +309,18 @@ class KVMTest(object): sys.exit(1) else: full_url = image_url + cloud_iso = _construct_filename(initial_url=full_url) - return full_url + return full_url, cloud_iso def download_image(self, image_url=None): """ Downloads Cloud image for same release as host machine """ if image_url is None: - full_url = self.construct_cloud_url() + full_url, cloud_iso = self.construct_cloud_url() else: - full_url = self.construct_cloud_url(image_url) + full_url, cloud_iso = self.construct_cloud_url(image_url) logging.debug("Acquiring cloud image from: {}".format(full_url)) # Attempt download -- cgit v1.2.3 From 366e3bec91fafca36f906bbd1e616728f6d01ac7 Mon Sep 17 00:00:00 2001 From: Jeff Lane Date: Fri, 10 Feb 2017 11:49:07 -0500 Subject: bin/boot_mode_test: breaks now when getting linux_distribution output. Now split on the . in 16.04 explicitly. LP: #1663665 --- bin/boot_mode_test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/boot_mode_test b/bin/boot_mode_test index 330bb2b..53e4fac 100755 --- a/bin/boot_mode_test +++ b/bin/boot_mode_test @@ -38,7 +38,7 @@ def version_check(check): return code from requested check if the installed version is 16.04 or newer. """ - installed_version = int(linux_distribution()[1].split()[0]) + installed_version = int(linux_distribution()[1].split('.')[0]) if installed_version < 16: logging.info("This system appears to be older than 16.04 LTS so this " "will not block a certification in progress.") -- cgit v1.2.3 From 47684a3effff731c20190f940fda4a635d633138 Mon Sep 17 00:00:00 2001 From: Maciej Kisielewski Date: Mon, 13 Feb 2017 13:52:31 +0100 Subject: bring back ethtool And use it as the main tool for determining NIC's speed. If it's not available try mii-tool. This should give us maximum resilience. Fixes: LP: #1662726 Signed-off-by: Maciej Kisielewski --- bin/network | 57 +++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/bin/network b/bin/network index ae9cd38..88fd931 100755 --- a/bin/network +++ b/bin/network @@ -280,29 +280,46 @@ class Interface(socket.socket): @property def max_speed(self): - # Parse mii-tool data for max speed - # search for numbers in the line starting with 'capabilities' - # return largest number as max_speed + speeds = [0] + # parse ethtool output, look for things like: + # 100baseSX, 40000baseNX, 10000baseT try: - info = check_output(['mii-tool', '-v', self.interface], - universal_newlines=True, - stderr=STDOUT).split('\n') - except FileNotFoundError: - logging.warning('mii-tool not found! Unable to get max speed') - ethinfo = None - except CalledProcessError as e: - logging.error('mii-tool returned an error!') - logging.error(e.output) - ethinfo = None - finally: - regex = re.compile(r'(\d+)(base)([A-Z]+)') - speeds = [0] - for line in filter(lambda l: 'capabilities' in l, info): - for s in line.split(' '): - hit = regex.search(s) + ethinfo = check_output(['ethtool', self.interface], + universal_newlines=True, + stderr=STDOUT).split(' ') + expression = '(\\d+)(base)([A-Z]+)|(\d+)(Mb/s)' + + regex = re.compile(expression) + if ethinfo: + for i in ethinfo: + hit = regex.search(i) if hit: speeds.append(int(re.sub("\D", "", hit.group(0)))) - return max(speeds) + except CalledProcessError as e: + logging.error('ethtool returned an error!') + logging.error(e.output) + except FileNotFoundError: + logging.warning('ethtool not found! Trying mii-tool') + # Parse mii-tool data for max speed + # search for numbers in the line starting with 'capabilities' + # return largest number as max_speed + try: + info = check_output(['mii-tool', '-v', self.interface], + universal_newlines=True, + stderr=STDOUT).split('\n') + regex = re.compile(r'(\d+)(base)([A-Z]+)') + speeds = [0] + for line in filter(lambda l: 'capabilities' in l, info): + for s in line.split(' '): + hit = regex.search(s) + if hit: + speeds.append(int(re.sub("\D", "", hit.group(0)))) + except FileNotFoundError: + logging.warning('mii-tool not found! Unable to get max speed') + except CalledProcessError as e: + logging.error('mii-tool returned an error!') + logging.error(e.output) + return max(speeds) @property def macaddress(self): -- cgit v1.2.3 From f4731f869fc726dca4ee5ec39e4c03a06b63ad32 Mon Sep 17 00:00:00 2001 From: Jeff Lane Date: Mon, 13 Feb 2017 14:08:19 -0500 Subject: Fixes from review comments. Also fixed bug caught while testing other fixes when a bad URL is provided causing _test_url to traceback. Also a few PEP8 fixes. --- bin/virtualization | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/bin/virtualization b/bin/virtualization index 5b70a54..ff11061 100755 --- a/bin/virtualization +++ b/bin/virtualization @@ -31,6 +31,7 @@ import re import logging import lsb_release import requests +from requests.packages.urllib3.exceptions import NewConnectionError import shlex import signal from subprocess import ( @@ -215,7 +216,7 @@ class KVMTest(object): # Gives us the stuff needed to build the URL to download the image return self.download_image(image_path) - def construct_cloud_url(self, image_url=None): + def get_image_name_and_url(self, image_url=None): """ Build a URL for official Ubuntu images hosted either at cloud-images.ubuntu.com or on a maas server hosting a mirror of @@ -232,7 +233,7 @@ class KVMTest(object): elif self.qemu_config['cloudimg_type'] == CLOUD_IMAGE_TYPE_DISK: cloud_iso = "%s-server-cloudimg-%s-disk1.img" % ( self.release, self.qemu_config['cloudimg_arch']) - elif initial_url is not none: + elif initial_url: # LP 1662580 - if we pass a full URL, assume the last piece is # the filname and return that. cloud_iso = initial_url.split('/')[-1] @@ -247,7 +248,13 @@ class KVMTest(object): def _test_cloud_url(url): # test our URL to make sure it's reachable - ret = requests.head(url) + try: + ret = requests.head(url) + except (OSError, NewConnectionError) as e: + logging.error("Unable to connect to {}".format(url)) + logging.error(e) + return False + if ret.status_code is not 200: return False else: @@ -279,10 +286,10 @@ class KVMTest(object): else: url = urlparse(image_url) if ( - url.path.endswith('/') or - url.path == '' or + url.path.endswith('/') or + url.path == '' or not url.path.endswith(".img") - ): + ): # If we have a relative URL (local copies of official images) # http://192.168.0.1/ or http://192.168.0.1/images/ cloud_iso = _construct_filename() @@ -310,7 +317,7 @@ class KVMTest(object): else: full_url = image_url cloud_iso = _construct_filename(initial_url=full_url) - + return full_url, cloud_iso def download_image(self, image_url=None): @@ -318,9 +325,9 @@ class KVMTest(object): Downloads Cloud image for same release as host machine """ if image_url is None: - full_url, cloud_iso = self.construct_cloud_url() + full_url, cloud_iso = self.get_image_name_and_url() else: - full_url, cloud_iso = self.construct_cloud_url(image_url) + full_url, cloud_iso = self.get_image_name_and_url(image_url) logging.debug("Acquiring cloud image from: {}".format(full_url)) # Attempt download -- cgit v1.2.3 From 0564789ff10112d2d8c995a0af0da62ff186b94f Mon Sep 17 00:00:00 2001 From: Pierre Equoy Date: Thu, 16 Feb 2017 15:04:59 +0800 Subject: Automatically mount drives when running storage tests When running disk/storage_device_* jobs, if the drive is not mounted, we now check if it can be mounted (i.e. if it has a partition) and if possible we mount it to a dedicated temporary location in order to perform the storage tests. LP: #1660386 --- bin/storage_test | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/bin/storage_test b/bin/storage_test index ef19c5e..40cc3f2 100755 --- a/bin/storage_test +++ b/bin/storage_test @@ -30,6 +30,34 @@ function run_bonnie() { fi } +# Find the largest partition that holds a supported filesystem on $disk_device. +# This code is adapted from a similar function in `disk_stress_ng`. +# 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 logical volume +# $unsupported_fs -- Empty or contains name of unsupported filesystem found on disk +find_largest_partition() { + largest_part="" + largest_size=0 + partitions=$(lsblk -b -l -n -o NAME,SIZE,TYPE,MOUNTPOINT $disk | grep part | tr -s " ") + unsupported_fs="" + for partition in $(echo "$partitions" | cut -d " " -f 1) ; do + part_size=$(echo "$partitions" | grep "$partition " | cut -d " " -f 2) + local blkid_info=$(blkid -s TYPE /dev/$partition | grep -E ext2\|ext3\|ext4\|xfs\|jfs\|btrfs) + if [ "$part_size" -gt "$largest_size" ] && [ -n "$blkid_info" ] ; then + largest_size=$part_size + largest_part="/dev/$partition" + largest_fs=$(blkid -s TYPE "/dev/$partition" | cut -d "=" -f 2) + fi + local blkid_info=$(blkid -s TYPE /dev/$partition | 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() + disk=/dev/$1 if [ -b $disk ] @@ -62,7 +90,17 @@ then echo "$disk is mounted, proceeding." else echo "$disk is not mounted. It must be mounted before testing." - exit 1 + find_largest_partition + if [ -n "$largest_part" ] + then + dest=/tmp/drive/$partition + echo "Mounting $largest_part into $dest..." + mkdir -p $dest + mount $largest_part $dest + else + echo "$disk has no partition. Please format this drive and re-launch the test." + exit 1 + fi fi -- cgit v1.2.3 From 1b45020f0cba5c6489a33dc16cb42a5595c2cd29 Mon Sep 17 00:00:00 2001 From: Jeff Lane Date: Fri, 17 Feb 2017 10:18:02 -0500 Subject: Added cking's modifications to disk_stress_ng --- bin/disk_stress_ng | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/bin/disk_stress_ng b/bin/disk_stress_ng index 802d116..a47ab79 100755 --- a/bin/disk_stress_ng +++ b/bin/disk_stress_ng @@ -131,7 +131,7 @@ find_largest_partition() { # $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" + test_dir="/tmp/disk_stress_ng_$(uuidgen)" if [ -b $disk_device ] then echo "$disk_device is a block device" @@ -167,9 +167,9 @@ mount_filesystem() { mounted_part="Y" fi if [ "$mount_point" == "/" ] ; then - test_dir="/tmp/disk_stress_ng" + test_dir="/tmp/disk_stress_ng_$(uuidgen)" else - test_dir="$mount_point/tmp/disk_stress_ng" + test_dir="$mount_point/tmp/disk_stress_ng_$(uuidgen)" fi echo "Test will use $largest_part, mounted at \"$mount_point\", using $largest_fs" else @@ -200,11 +200,28 @@ run_stressor() { 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 9 $end_time stress-ng --aggressive --verify --timeout $runtime \ - --temp-path $test_dir --$1 0 + 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 = "137" ] ; then @@ -244,6 +261,19 @@ disk_stressors=("aio" "aiol" "chdir" "chmod" "dentry" "dir" "fallocate" \ 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 "" -- cgit v1.2.3 From b53fb0ac87dd52f2157416dcf92230347604eece Mon Sep 17 00:00:00 2001 From: Pierre Equoy Date: Thu, 23 Feb 2017 12:30:16 +0800 Subject: Add auto-unmount feature in the storage test It's better to put things back into the state they were before we started the test. The storage test now unmount any partition it may have mounted itself and removes the temporary directory created to mount it into. LP: #1660386 --- bin/storage_test | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/bin/storage_test b/bin/storage_test index 40cc3f2..1aed0da 100755 --- a/bin/storage_test +++ b/bin/storage_test @@ -59,6 +59,7 @@ find_largest_partition() { } # find_largest_partition() disk=/dev/$1 +scripted_mount=0 if [ -b $disk ] then @@ -93,10 +94,13 @@ then find_largest_partition if [ -n "$largest_part" ] then - dest=/tmp/drive/$partition + dest=`mktemp -dq --tmpdir drive.XXX` echo "Mounting $largest_part into $dest..." - mkdir -p $dest mount $largest_part $dest + if [[ $? == 0 ]] + then + scripted_mount=1 + fi else echo "$disk has no partition. Please format this drive and re-launch the test." exit 1 @@ -115,12 +119,33 @@ then if [ $size_int -gt 10 ] then run_bonnie $disk + if [[ $scripted_mount == 1 ]] + then + echo "$largest_part was mounted by this script. Unmounting it now..." + umount $largest_part + echo "Removing temporary mount directory $dest..." + rm -rf $dest + fi else echo "$disk is too small to be used for testing." + if [[ $scripted_mount == 1 ]] + then + echo "$largest_part was mounted by this script. Unmounting it now..." + umount $largest_part + echo "Removing temporary mount directory $dest..." + rm -rf $dest + fi exit 1 fi else run_bonnie $disk + if [[ $scripted_mount == 1 ]] + then + echo "$largest_part was mounted by this script. Unmounting it now..." + umount $largest_part + echo "Removing temporary mount directory $dest..." + rm -rf $dest + fi fi else echo "$disk doesn't report a size." -- cgit v1.2.3 From 4cfbd35b20fb48ccb551b3aa1d9caf17a4acde9b Mon Sep 17 00:00:00 2001 From: Rod Smith Date: Thu, 23 Feb 2017 13:21:14 -0500 Subject: Fix memory_stress_ng test to not rely on numactl; if that binary is absent, set the # of NUMA nodes to 1. --- bin/memory_stress_ng | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/bin/memory_stress_ng b/bin/memory_stress_ng index e619772..35c7725 100755 --- a/bin/memory_stress_ng +++ b/bin/memory_stress_ng @@ -114,7 +114,12 @@ echo "Variable run time is $variable_time seconds per stressor" had_error=0 -numa_nodes=$(numactl --hardware | grep available | head -n 1 | cut -f 2 -d " ") +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: -- cgit v1.2.3 From bc56699705a0320a700157e30a1ede7cedb55598 Mon Sep 17 00:00:00 2001 From: Jeff Lane Date: Thu, 23 Feb 2017 16:06:47 -0500 Subject: bin/network: fix traceback when device to be tested has no IP address LP: #1662724 --- bin/network | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/bin/network b/bin/network index ae9cd38..dda64da 100755 --- a/bin/network +++ b/bin/network @@ -260,7 +260,7 @@ class Interface(socket.socket): nic_data = fcntl.ioctl(self.fileno(), 0x8915, freq) except IOError: logging.error("No IP address for %s", self.interface) - return 1 + return None return socket.inet_ntoa(nic_data[20:24]) @property @@ -271,7 +271,7 @@ class Interface(socket.socket): mask_data = fcntl.ioctl(self.fileno(), 0x891b, freq) except IOError: logging.error("No netmask for %s", self.interface) - return 1 + return None return socket.inet_ntoa(mask_data[20:24]) @property @@ -416,9 +416,15 @@ def make_target_list(iface, test_targets, log_warnings): List form of input string, minus invalid values """ test_targets_list = test_targets.split(",") - net = ipaddress.IPv4Network("{}/{}".format(Interface(iface).ipaddress, - Interface(iface).netmask), - False) + try: + net = ipaddress.IPv4Network("{}/{}".format(Interface(iface).ipaddress, + Interface(iface).netmask), + False) + except ipaddress.AddressValueError as e: + logging.error("Device {}: Invalid IP Address".format(iface)) + logging.error(" {}".format(e)) + logging.error("Aborting test now") + sys.exit(1) first_addr = net.network_address + 1 last_addr = first_addr + net.num_addresses - 2 return_list = list(test_targets_list) -- cgit v1.2.3 From ebb1f2b354f1ea4124e62a037fa7cfaa489111b0 Mon Sep 17 00:00:00 2001 From: Jeff Lane Date: Thu, 23 Feb 2017 16:08:03 -0500 Subject: bin/network: pep8 fixes --- bin/network | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bin/network b/bin/network index dda64da..19c2ac3 100755 --- a/bin/network +++ b/bin/network @@ -460,7 +460,8 @@ def interface_test(args): if (args.test_type.lower() == "iperf" or args.test_type.lower() == "stress"): test_targets = test_parameters["test_target_iperf"] - test_targets_list = make_target_list(args.interface, test_targets, True) + test_targets_list = make_target_list(args.interface, test_targets, + True) # Validate that we got reasonable values if not test_targets_list or "example.com" in test_targets: @@ -524,7 +525,9 @@ def interface_test(args): if not test_targets_list: logging.info(" Exhausted test target list; trying again " .center(60, "=")) - test_targets_list = make_target_list(args.interface, test_targets, False) + test_targets_list = make_target_list(args.interface, + test_targets, + False) time.sleep(30) first_loop = False -- cgit v1.2.3 From 68aaa79e797742ca87fb27ab844c16b107c24e94 Mon Sep 17 00:00:00 2001 From: Jeff Lane Date: Thu, 23 Feb 2017 17:08:30 -0500 Subject: bin/disk_stress_ng: Warn and exit if disk is smaller than 10GiB because small disks tend to run out of space when testing. LP: #1667488 --- bin/disk_stress_ng | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bin/disk_stress_ng b/bin/disk_stress_ng index a47ab79..de96f46 100755 --- a/bin/disk_stress_ng +++ b/bin/disk_stress_ng @@ -151,9 +151,15 @@ mount_filesystem() { 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") -- cgit v1.2.3 From 16e040e7c95256529e40feebfae3bf6196152c1a Mon Sep 17 00:00:00 2001 From: Jeff Lane Date: Fri, 24 Feb 2017 16:31:25 -0500 Subject: bin/network: don't remove targets that are not in the right subnet, just warn and try them anyway. LP: #1667781 --- bin/network | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/bin/network b/bin/network index 88fd931..4ebb786 100755 --- a/bin/network +++ b/bin/network @@ -448,10 +448,9 @@ def make_target_list(iface, test_targets, log_warnings): target = ipaddress.IPv4Address(test_target_ip) if (target < first_addr) or (target > last_addr): if log_warnings: - logging.warning("test server {} ({}) is NOT ". - format(test_target, target)) - logging.warning("within {}; skipping".format(net)) - return_list.remove(test_target) + logging.warning("Test server {} ({}) is NOT within {}". + format(test_target, target, net)) + logging.warning("This may cause test issues") except ValueError: if log_warnings: logging.warning("Invalid address: {}; skipping". -- cgit v1.2.3 From 7492b2d0b19e7ecad5c58598034eb80a5498c26c Mon Sep 17 00:00:00 2001 From: Mike Rushton Date: Fri, 3 Mar 2017 21:16:16 -0500 Subject: Fix memory_stress_ng for LP: #1573062 --- bin/memory_stress_ng | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/bin/memory_stress_ng b/bin/memory_stress_ng index 35c7725..a2b232b 100755 --- a/bin/memory_stress_ng +++ b/bin/memory_stress_ng @@ -75,10 +75,27 @@ run_stressor() { end_time=$((runtime*15/10)) 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 9 $end_time stress-ng --aggressive --verify --timeout $runtime --$1 0 + 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 = "137" ] ; then -- cgit v1.2.3 From 047db5050e2e25e25d35805d21a2d6b337f41960 Mon Sep 17 00:00:00 2001 From: Pierre Equoy Date: Mon, 6 Mar 2017 16:22:33 +0800 Subject: Bump to v0.35.0rc1 --- .bumpversion.cfg | 2 +- manage.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index aec6c1a..0fa5720 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.35.0.dev0 +current_version = 0.35.0rc1 files = manage.py parse = (?P\d+)\.(?P\d+)(\.(?P\d+))?((?P\.?[a-z]+)(?P\d+))? serialize = diff --git a/manage.py b/manage.py index 86fb301..661b8be 100755 --- a/manage.py +++ b/manage.py @@ -30,7 +30,7 @@ class InstallPyModules(InstallCommand): setup( name='plainbox-provider-checkbox', namespace='2013.com.canonical.certification', - version="0.35.0.dev0", + version="0.35.0rc1", description=N_("Checkbox provider"), gettext_domain='plainbox-provider-checkbox', strict=False, deprecated=False, -- cgit v1.2.3 From 52dd11f3354d9c0aac8c2ed393fb4496edfaddab Mon Sep 17 00:00:00 2001 From: Pierre Equoy Date: Mon, 13 Mar 2017 11:34:11 +0800 Subject: Bump to v0.35.0 --- .bumpversion.cfg | 2 +- manage.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 0fa5720..03b7cf3 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.35.0rc1 +current_version = 0.35.0 files = manage.py parse = (?P\d+)\.(?P\d+)(\.(?P\d+))?((?P\.?[a-z]+)(?P\d+))? serialize = diff --git a/manage.py b/manage.py index 661b8be..23c6ea6 100755 --- a/manage.py +++ b/manage.py @@ -30,7 +30,7 @@ class InstallPyModules(InstallCommand): setup( name='plainbox-provider-checkbox', namespace='2013.com.canonical.certification', - version="0.35.0rc1", + version="0.35.0", description=N_("Checkbox provider"), gettext_domain='plainbox-provider-checkbox', strict=False, deprecated=False, -- cgit v1.2.3 From 3db20646bd9dc1ce3a1cfcdda08cd28565c0cd1a Mon Sep 17 00:00:00 2001 From: Pierre Equoy Date: Mon, 13 Mar 2017 11:34:12 +0800 Subject: increment version to v0.36.0.dev0 --- .bumpversion.cfg | 2 +- manage.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 03b7cf3..6339998 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.35.0 +current_version = 0.36.0.dev0 files = manage.py parse = (?P\d+)\.(?P\d+)(\.(?P\d+))?((?P\.?[a-z]+)(?P\d+))? serialize = diff --git a/manage.py b/manage.py index 23c6ea6..d83829c 100755 --- a/manage.py +++ b/manage.py @@ -30,7 +30,7 @@ class InstallPyModules(InstallCommand): setup( name='plainbox-provider-checkbox', namespace='2013.com.canonical.certification', - version="0.35.0", + version="0.36.0.dev0", description=N_("Checkbox provider"), gettext_domain='plainbox-provider-checkbox', strict=False, deprecated=False, -- cgit v1.2.3 From 15a136e32ae8efea3a6883babfdd87ad36ba0955 Mon Sep 17 00:00:00 2001 From: Jeff Lane Date: Tue, 14 Mar 2017 13:10:07 -0400 Subject: bin/get_make_and_model: Now also reveals make/model info for the mainboard to assist in identifying whitebox systems like those from SuperMicro LP: #1672726 --- bin/get_make_and_model | 46 +++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/bin/get_make_and_model b/bin/get_make_and_model index bb5efd9..496a949 100755 --- a/bin/get_make_and_model +++ b/bin/get_make_and_model @@ -4,31 +4,39 @@ import os.path import shlex from subprocess import check_output +def print_header(value): + print("{}:".format(value)) + def print_data(key, value): - print("{}: {}".format(key, value)) + print(" {}: {}".format(key, value)) + +def run_cmd(option): + cmd = "lshw -C " + option + out = check_output(shlex.split(cmd), + universal_newlines = True) + return out.split('\n') def main(): keys = {'Manufacturer': 'vendor', 'Model': 'product', 'Version': 'version'} - - cmd = "lshw -C system" - - out = check_output(shlex.split(cmd), - universal_newlines = True) - output = out.split('\n') - - data = {} - for key in keys: - for line in output: - if keys[key] in line: - data[key] = line.split(':')[1].strip() - break - else: - data[key] = "NOT FOUND" - - for key in data: - print_data(key, data[key]) + lshw_classes = {'system': 'System', + 'bus': 'Mainboard'} + + for lshw_class in lshw_classes: + output = run_cmd(lshw_class) + data = {} + for key in keys: + for line in output: + if keys[key] in line: + data[key] = line.split(':')[1].strip() + break + else: + data[key] = "NOT FOUND" + + print_header(lshw_classes[lshw_class]) + for key in data: + print_data(key, data[key]) if __name__ == "__main__": raise SystemExit(main()) -- cgit v1.2.3 From 54f2839e34dcb13549fec63fa68783e19a30ed1a Mon Sep 17 00:00:00 2001 From: Jeff Lane Date: Mon, 27 Mar 2017 17:12:10 -0400 Subject: Back out use of NewConnectionError which does not exist in the Trusty version of urllib3, causing script to break on Trusty LP: #1676585 --- bin/virtualization | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bin/virtualization b/bin/virtualization index ff11061..e221261 100755 --- a/bin/virtualization +++ b/bin/virtualization @@ -31,7 +31,6 @@ import re import logging import lsb_release import requests -from requests.packages.urllib3.exceptions import NewConnectionError import shlex import signal from subprocess import ( @@ -250,9 +249,9 @@ class KVMTest(object): # test our URL to make sure it's reachable try: ret = requests.head(url) - except (OSError, NewConnectionError) as e: + except OSError as e: logging.error("Unable to connect to {}".format(url)) - logging.error(e) + logging.error(" * Message: {}".format(e.with_traceback(None))) return False if ret.status_code is not 200: -- cgit v1.2.3 From 4a4ae927611a5939a45c8018a26cd639066e9fde Mon Sep 17 00:00:00 2001 From: Jeff Lane Date: Wed, 1 Feb 2017 12:50:07 -0500 Subject: First working version of LXD test --- bin/virtualization | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 131 insertions(+), 3 deletions(-) diff --git a/bin/virtualization b/bin/virtualization index ff11061..2b04aed 100755 --- a/bin/virtualization +++ b/bin/virtualization @@ -37,8 +37,11 @@ import signal from subprocess import ( Popen, PIPE, + STDOUT, + DEVNULL, CalledProcessError, check_output, + check_call, call ) import sys @@ -514,6 +517,123 @@ final_message: CERTIFICATION BOOT COMPLETE return status +class LXDTest(object): + + def __init__(self, template = None, rootfs = None): + self.rootfs_url = rootfs + self.template_url = template + self.rootfs_tarball = None + self.template_tarball = None + + def setup(self): + # Initialize LXD + result = True + logging.debug("Attempting to initialize LXD") + try: + check_output(['lxd'], universal_newlines=True, stderr=STDOUT) + except CalledProcessError as err: + logging.debug('LXD appears to be running') + logging.debug(err.output) + else: + try: + check_output(['lxd', 'init', '--auto'], stderr=DEVNULL) + except CalledProcessError as init_error: + logging.error('Unable to initialize LXD') + logging.error(init_error.output) + result = False + + # Retrieve and insert LXD images + if self.template_url is not None: + filename = urlparse(self.template_url).path.split('/')[-1] + self.template_tarball = self.download_images(self.template_url, + filename) + if not self.template_tarball: + logging.error("Unable to download {} from " + "{}".format(self.template_tarball, self.template_url)) + logging.error("Aborting") + result = False + + if self.rootfs_url is not None: + filename = urlparse(self.rootfs_url).path.split('/')[-1] + self.rootfs_tarball = self.download_images(self.rootfs_url, + filename) + if not self.rootfs_tarball: + logging.error("Unable to download {} from{}".format( + self.rootfs_tarball, self.rootfs_url)) + logging.error("Aborting") + result = False + + # Insert images + # TODO: + # * catch situation where image already exists + logging.debug("Importing images into LXD") + cmd = 'lxc image import {} rootfs {} --alias ubuntu'.format( + self.template_tarball, self.rootfs_tarball) + try: + check_output(shlex.split(cmd), universal_newlines=True) + except CalledProcessError as err: + logging.error("Failed to insert LXC images.") + logging.error(err.output) + result = False + + return result + + def download_images(self, url, filename): + """ + Downloads LXD files for same release as host machine + """ + logging.debug("Attempting download of {} from {}".format(filename, + url)) + try: + resp = urllib.request.urlretrieve(url, filename) + except (IOError, + Error, + urllib.error.HTTPError, + urllib.error.URLError) as exception: + logging.error("Failed download of image from %s: %s", + image_url, exception) + return False + + if not os.path.isfile(filename): + logging.warn("Can not find {}".format(filename)) + return False + + return filename + + def start(self): + """ + Creates a container and performs the test + """ + result = self.setup() + if not result: + logging.warn("One or more setup stages failed.") + + # Create container + try: + check_call(['lxc', 'launch', 'ubuntu', "testbed"]) + except CalledProcessError as err: + logging.error("Unable to launch container!") + logging.error(err) + return False + + cmd = "lxc exec testbed uptime" + try: + print(check_output(shlex.split(cmd), universal_newlines=True)) + except CalledProcessError as err: + logging.error("Failed to connect to container!") + logging.error(err) + return False + + print("PASS: Container was succssfully started and checked") + return True + +def test_lxd(args): + print("Executing LXD Test") + + lxd_test = LXDTest(args.template, args.rootfs) + + result = lxd_test.start() + sys.exit(result) def test_kvm(args): print("Executing KVM Test", file=sys.stderr) @@ -559,6 +679,11 @@ def main(): # Main cli options kvm_test_parser = subparsers.add_parser( 'kvm', help=("Run kvm virtualization test")) + lxd_test_parser = subparsers.add_parser( + 'lxd', help=("Run the LXD validation test")) + parser.add_argument('--debug', dest='log_level', + action="store_const", const=logging.DEBUG, + default=logging.INFO) # Sub test options kvm_test_parser.add_argument( @@ -568,11 +693,14 @@ def main(): kvm_test_parser.add_argument( '-l', '--log-file', default='virt_debug', help="Location for debugging output log. Defaults to %(default)s.") - kvm_test_parser.add_argument('--debug', dest='log_level', - action="store_const", const=logging.DEBUG, - default=logging.INFO) kvm_test_parser.set_defaults(func=test_kvm) + lxd_test_parser.add_argument( + '--template', type=str, default=None) + lxd_test_parser.add_argument( + '--rootfs', type=str, default=None) + lxd_test_parser.set_defaults(func=test_lxd) + args = parser.parse_args() try: -- cgit v1.2.3 From b5438a2892c36cc9f0a71817ff2c67ccc0abbb13 Mon Sep 17 00:00:00 2001 From: Jeff Lane Date: Thu, 9 Feb 2017 16:08:37 -0500 Subject: Working LXD test now present. --- bin/virtualization | 100 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 66 insertions(+), 34 deletions(-) diff --git a/bin/virtualization b/bin/virtualization index 2b04aed..0954691 100755 --- a/bin/virtualization +++ b/bin/virtualization @@ -529,48 +529,56 @@ class LXDTest(object): # Initialize LXD result = True logging.debug("Attempting to initialize LXD") + # TODO: Need a method to see if LXD is already initialized try: - check_output(['lxd'], universal_newlines=True, stderr=STDOUT) - except CalledProcessError as err: - logging.debug('LXD appears to be running') - logging.debug(err.output) - else: - try: - check_output(['lxd', 'init', '--auto'], stderr=DEVNULL) - except CalledProcessError as init_error: - logging.error('Unable to initialize LXD') - logging.error(init_error.output) - result = False + check_output(['lxd', 'init', '--auto'], stderr=DEVNULL) + except CalledProcessError as init_error: + logging.error('Unable to initialize LXD') + logging.debug(init_error.output) + result = False # Retrieve and insert LXD images + logging.debug("Downloading template.") if self.template_url is not None: - filename = urlparse(self.template_url).path.split('/')[-1] - self.template_tarball = self.download_images(self.template_url, - filename) - if not self.template_tarball: - logging.error("Unable to download {} from " - "{}".format(self.template_tarball, self.template_url)) - logging.error("Aborting") - result = False - + targetfile = urlparse(self.template_url).path.split('/')[-1] + filename = os.path.join('/tmp', targetfile) + if not os.path.isfile(filename): + self.template_tarball = self.download_images(self.template_url, + filename) + if not self.template_tarball: + logging.error("Unable to download {} from " + "{}".format(self.template_tarball, self.template_url)) + logging.error("Aborting") + result = False + else: + logging.debug("Template file {} already exists. " + "Skipping Download.".format(filename)) + self.template_tarball = filename + + logging.debug("Downloading rootfs.") if self.rootfs_url is not None: - filename = urlparse(self.rootfs_url).path.split('/')[-1] - self.rootfs_tarball = self.download_images(self.rootfs_url, + targetfile = urlparse(self.rootfs_url).path.split('/')[-1] + filename = os.path.join('/tmp', targetfile) + if not os.path.isfile(filename): + self.rootfs_tarball = self.download_images(self.rootfs_url, filename) - if not self.rootfs_tarball: - logging.error("Unable to download {} from{}".format( + if not self.rootfs_tarball: + logging.error("Unable to download {} from{}".format( self.rootfs_tarball, self.rootfs_url)) - logging.error("Aborting") - result = False + logging.error("Aborting") + result = False + else: + logging.debug("Template file {} already exists. " + "Skipping Download.".format(filename)) + self.rootfs_tarball = filename # Insert images - # TODO: - # * catch situation where image already exists + # TODO: check to see if ubuntu already exists logging.debug("Importing images into LXD") cmd = 'lxc image import {} rootfs {} --alias ubuntu'.format( self.template_tarball, self.rootfs_tarball) try: - check_output(shlex.split(cmd), universal_newlines=True) + check_output(shlex.split(cmd), stderr=STDOUT, universal_newlines=True) except CalledProcessError as err: logging.error("Failed to insert LXC images.") logging.error(err.output) @@ -600,6 +608,22 @@ class LXDTest(object): return filename + def cleanup(self): + # Clean up image + logging.debug('Cleaning up images and containers created during test') + try: + check_output(['lxc', 'image', 'delete', 'ubuntu'], stderr=STDOUT, universal_newlines=True) + except CalledProcessError as err: + logging.error('Unable to remove image from LXC image store. This may cause problems on re-runs') + logging.debug(err.output) + + # Clean up container + try: + check_output(['lxc', 'delete', 'testbed', '--force'], stderr=STDOUT, universal_newlines=True) + except CalledProcessError as err: + logging.error('Unable to destroy container. This may cause problems on re-runs') + logging.debug(err.output) + def start(self): """ Creates a container and performs the test @@ -609,22 +633,24 @@ class LXDTest(object): logging.warn("One or more setup stages failed.") # Create container + logging.debug("Launching container") try: - check_call(['lxc', 'launch', 'ubuntu', "testbed"]) + check_output(['lxc', 'launch', 'ubuntu', "testbed"], stderr=STDOUT, universal_newlines=True) except CalledProcessError as err: logging.error("Unable to launch container!") - logging.error(err) + logging.debug(err.output) return False + + time.sleep(60) cmd = "lxc exec testbed uptime" try: - print(check_output(shlex.split(cmd), universal_newlines=True)) + check_output(shlex.split(cmd), stderr=STDOUT, universal_newlines=True) except CalledProcessError as err: logging.error("Failed to connect to container!") - logging.error(err) + logging.debug(err.output) return False - print("PASS: Container was succssfully started and checked") return True def test_lxd(args): @@ -633,6 +659,12 @@ def test_lxd(args): lxd_test = LXDTest(args.template, args.rootfs) result = lxd_test.start() + lxd_test.cleanup() + if result: + print("PASS: Container was succssfully started and checked") + else: + print("FAIL: Container was not started and checked") + sys.exit(result) def test_kvm(args): -- cgit v1.2.3 From e58502d706daec15637a5f7b3baeb88c8ba09dd1 Mon Sep 17 00:00:00 2001 From: Jeff Lane Date: Fri, 10 Feb 2017 17:13:30 -0500 Subject: refactored lxd test code --- bin/virtualization | 109 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 62 insertions(+), 47 deletions(-) diff --git a/bin/virtualization b/bin/virtualization index 0954691..5c43cc5 100755 --- a/bin/virtualization +++ b/bin/virtualization @@ -23,6 +23,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ +import pdb from argparse import ArgumentParser import configparser from glob import glob @@ -517,6 +518,31 @@ final_message: CERTIFICATION BOOT COMPLETE return status +class RunCommand(object): + """ + Runs a command and can return all needed info: + * stdout + * stderr + * return code + * original command + + Convenince class to avoid the same repetitive code to run shell commands. + """ + + def __init__(self, cmd = None): + self.stdout = None + self.stderr = None + self.returncode = None + self.cmd = cmd + self.run(self.cmd) + + + def run(self,cmd): + proc = Popen(shlex.split(cmd), stdout=PIPE, stderr=PIPE, universal_newlines=True) + self.stdout, self.stderr = proc.communicate() + self.returncode = proc.returncode + + class LXDTest(object): def __init__(self, template = None, rootfs = None): @@ -524,24 +550,34 @@ class LXDTest(object): self.template_url = template self.rootfs_tarball = None self.template_tarball = None + self.name = 'testbed' + self.image_alias = 'ubuntu' + + def run_command(self, cmd): + task = RunCommand(cmd) + if task.returncode != 0: + logging.error('Command {} returnd a code of {}'.format(task.cmd, task.returncode)) + logging.error(' STDOUT: {}'.format(task.stdout)) + logging.error(' STDERR: {}'.format(task.stderr)) + return False + + return True def setup(self): # Initialize LXD result = True logging.debug("Attempting to initialize LXD") # TODO: Need a method to see if LXD is already initialized - try: - check_output(['lxd', 'init', '--auto'], stderr=DEVNULL) - except CalledProcessError as init_error: - logging.error('Unable to initialize LXD') - logging.debug(init_error.output) + if not self.run_command('lxd init --auto'): + logging.error('Error encounterd while initializing LXD') result = False - + # Retrieve and insert LXD images logging.debug("Downloading template.") if self.template_url is not None: - targetfile = urlparse(self.template_url).path.split('/')[-1] - filename = os.path.join('/tmp', targetfile) + filename = urlparse(self.template_url).path.split('/')[-1] + #targetfile = urlparse(self.template_url).path.split('/')[-1] + #filename = os.path.join('/tmp', targetfile) if not os.path.isfile(filename): self.template_tarball = self.download_images(self.template_url, filename) @@ -557,8 +593,9 @@ class LXDTest(object): logging.debug("Downloading rootfs.") if self.rootfs_url is not None: - targetfile = urlparse(self.rootfs_url).path.split('/')[-1] - filename = os.path.join('/tmp', targetfile) + filname = urlparse(self.rootfs_url).path.split('/')[-1] + #targetfile = urlparse(self.rootfs_url).path.split('/')[-1] + #filename = os.path.join('/tmp', targetfile) if not os.path.isfile(filename): self.rootfs_tarball = self.download_images(self.rootfs_url, filename) @@ -575,13 +612,10 @@ class LXDTest(object): # Insert images # TODO: check to see if ubuntu already exists logging.debug("Importing images into LXD") - cmd = 'lxc image import {} rootfs {} --alias ubuntu'.format( - self.template_tarball, self.rootfs_tarball) - try: - check_output(shlex.split(cmd), stderr=STDOUT, universal_newlines=True) - except CalledProcessError as err: - logging.error("Failed to insert LXC images.") - logging.error(err.output) + cmd = 'lxc image import {} rootfs {} --alias {}'.format( + self.template_tarball, self.rootfs_tarball, self.image_alias) + if not self.run_command(cmd): + logging.error('Error encountered while attempting to import images into LXD') result = False return result @@ -609,20 +643,12 @@ class LXDTest(object): return filename def cleanup(self): - # Clean up image + """ + Clean up test files an containers created + """ logging.debug('Cleaning up images and containers created during test') - try: - check_output(['lxc', 'image', 'delete', 'ubuntu'], stderr=STDOUT, universal_newlines=True) - except CalledProcessError as err: - logging.error('Unable to remove image from LXC image store. This may cause problems on re-runs') - logging.debug(err.output) - - # Clean up container - try: - check_output(['lxc', 'delete', 'testbed', '--force'], stderr=STDOUT, universal_newlines=True) - except CalledProcessError as err: - logging.error('Unable to destroy container. This may cause problems on re-runs') - logging.debug(err.output) + self.run_command('lxc image delete {}'.format(self.image_alias)) + self.run_command('lxc delete --force {}'.format(self.name)) def start(self): """ @@ -634,27 +660,16 @@ class LXDTest(object): # Create container logging.debug("Launching container") - try: - check_output(['lxc', 'launch', 'ubuntu', "testbed"], stderr=STDOUT, universal_newlines=True) - except CalledProcessError as err: - logging.error("Unable to launch container!") - logging.debug(err.output) + if not self.run_command('lxc launch {} {}'.format(self.image_alias,self.name)): return False - - time.sleep(60) - - cmd = "lxc exec testbed uptime" - try: - check_output(shlex.split(cmd), stderr=STDOUT, universal_newlines=True) - except CalledProcessError as err: - logging.error("Failed to connect to container!") - logging.debug(err.output) + cmd = "lxc exec {} dd if=/dev/urandom of=testdata.txt bs=1024 count=1000".format(self.name) + if not self.run_command(cmd): return False return True def test_lxd(args): - print("Executing LXD Test") + logging.debug("Executing LXD Test") lxd_test = LXDTest(args.template, args.rootfs) @@ -662,10 +677,10 @@ def test_lxd(args): lxd_test.cleanup() if result: print("PASS: Container was succssfully started and checked") + sys.exit(0) else: print("FAIL: Container was not started and checked") - - sys.exit(result) + sys.exit(1) def test_kvm(args): print("Executing KVM Test", file=sys.stderr) -- cgit v1.2.3 From f9b953a8f4039a9dfa0702649cee8bceba08df9a Mon Sep 17 00:00:00 2001 From: Jeff Lane Date: Fri, 10 Feb 2017 17:35:41 -0500 Subject: pep8 fixes --- bin/virtualization | 71 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/bin/virtualization b/bin/virtualization index 5c43cc5..0551c4a 100755 --- a/bin/virtualization +++ b/bin/virtualization @@ -518,6 +518,7 @@ final_message: CERTIFICATION BOOT COMPLETE return status + class RunCommand(object): """ Runs a command and can return all needed info: @@ -527,25 +528,25 @@ class RunCommand(object): * original command Convenince class to avoid the same repetitive code to run shell commands. - """ + """ - def __init__(self, cmd = None): + def __init__(self, cmd=None): self.stdout = None self.stderr = None self.returncode = None self.cmd = cmd self.run(self.cmd) - - def run(self,cmd): - proc = Popen(shlex.split(cmd), stdout=PIPE, stderr=PIPE, universal_newlines=True) + def run(self, cmd): + proc = Popen(shlex.split(cmd), stdout=PIPE, stderr=PIPE, + universal_newlines=True) self.stdout, self.stderr = proc.communicate() - self.returncode = proc.returncode + self.returncode = proc.returncode class LXDTest(object): - def __init__(self, template = None, rootfs = None): + def __init__(self, template=None, rootfs=None): self.rootfs_url = rootfs self.template_url = template self.rootfs_tarball = None @@ -556,7 +557,8 @@ class LXDTest(object): def run_command(self, cmd): task = RunCommand(cmd) if task.returncode != 0: - logging.error('Command {} returnd a code of {}'.format(task.cmd, task.returncode)) + logging.error('Command {} returnd a code of {}'.format( + task.cmd, task.returncode)) logging.error(' STDOUT: {}'.format(task.stdout)) logging.error(' STDERR: {}'.format(task.stderr)) return False @@ -571,34 +573,33 @@ class LXDTest(object): if not self.run_command('lxd init --auto'): logging.error('Error encounterd while initializing LXD') result = False - + # Retrieve and insert LXD images logging.debug("Downloading template.") if self.template_url is not None: - filename = urlparse(self.template_url).path.split('/')[-1] - #targetfile = urlparse(self.template_url).path.split('/')[-1] - #filename = os.path.join('/tmp', targetfile) + targetfile = urlparse(self.template_url).path.split('/')[-1] + filename = os.path.join('/tmp', targetfile) if not os.path.isfile(filename): - self.template_tarball = self.download_images(self.template_url, + self.template_tarball = self.download_images(self.template_url, filename) - if not self.template_tarball: - logging.error("Unable to download {} from " - "{}".format(self.template_tarball, self.template_url)) - logging.error("Aborting") - result = False + if not self.template_tarball: + logging.error("Unable to download {} from " + "{}".format(self.template_tarball, + self.template_url)) + logging.error("Aborting") + result = False else: logging.debug("Template file {} already exists. " "Skipping Download.".format(filename)) self.template_tarball = filename - + logging.debug("Downloading rootfs.") if self.rootfs_url is not None: - filname = urlparse(self.rootfs_url).path.split('/')[-1] - #targetfile = urlparse(self.rootfs_url).path.split('/')[-1] - #filename = os.path.join('/tmp', targetfile) + targetfile = urlparse(self.rootfs_url).path.split('/')[-1] + filename = os.path.join('/tmp', targetfile) if not os.path.isfile(filename): self.rootfs_tarball = self.download_images(self.rootfs_url, - filename) + filename) if not self.rootfs_tarball: logging.error("Unable to download {} from{}".format( self.rootfs_tarball, self.rootfs_url)) @@ -613,18 +614,20 @@ class LXDTest(object): # TODO: check to see if ubuntu already exists logging.debug("Importing images into LXD") cmd = 'lxc image import {} rootfs {} --alias {}'.format( - self.template_tarball, self.rootfs_tarball, self.image_alias) + self.template_tarball, self.rootfs_tarball, + self.image_alias) if not self.run_command(cmd): - logging.error('Error encountered while attempting to import images into LXD') + logging.error('Error encountered while attempting to ' + 'import images into LXD') result = False - + return result def download_images(self, url, filename): """ Downloads LXD files for same release as host machine """ - logging.debug("Attempting download of {} from {}".format(filename, + logging.debug("Attempting download of {} from {}".format(filename, url)) try: resp = urllib.request.urlretrieve(url, filename) @@ -660,19 +663,22 @@ class LXDTest(object): # Create container logging.debug("Launching container") - if not self.run_command('lxc launch {} {}'.format(self.image_alias,self.name)): + if not self.run_command('lxc launch {} {}'.format(self.image_alias, + self.name)): return False - cmd = "lxc exec {} dd if=/dev/urandom of=testdata.txt bs=1024 count=1000".format(self.name) - if not self.run_command(cmd): + cmd = ("lxc exec {} dd if=/dev/urandom of=testdata.txt " + "bs=1024 count=1000".format(self.name)) + if not self.run_command(cmd): return False return True + def test_lxd(args): logging.debug("Executing LXD Test") - + lxd_test = LXDTest(args.template, args.rootfs) - + result = lxd_test.start() lxd_test.cleanup() if result: @@ -682,6 +688,7 @@ def test_lxd(args): print("FAIL: Container was not started and checked") sys.exit(1) + def test_kvm(args): print("Executing KVM Test", file=sys.stderr) -- cgit v1.2.3 From 22537dbaebf92cb15c2b7173f915e8bb1b3e0387 Mon Sep 17 00:00:00 2001 From: Jeff Lane Date: Fri, 10 Feb 2017 18:27:42 -0500 Subject: jobs/virtualization.txt.in: added lxd job --- jobs/virtualization.txt.in | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/jobs/virtualization.txt.in b/jobs/virtualization.txt.in index 919691b..4def608 100644 --- a/jobs/virtualization.txt.in +++ b/jobs/virtualization.txt.in @@ -14,3 +14,18 @@ _description: cloud image. _summary: Verify KVM guest boots + +plugin: shell +category_id: 2013.com.canonical.plainbox::virtualization +id: virtualization/verify_lxd +user: root +environ: LXD_TEMPLATE LXD_ROOTFS +estimated_duration: 30.0 +requires: + package.name == 'lxd-client' + package.name == 'lxd' +command: virtualization lxd --template $LXD_TEMPLATE --rootfs $LXD_ROOTFS +_description: + Verifies that an LXD container can be created and launched +_summary: + Verify LXD container launches -- cgit v1.2.3 From ac933b55d333d433dd41c19e6bf749aaccb1143f Mon Sep 17 00:00:00 2001 From: Jeff Lane Date: Sat, 11 Feb 2017 10:41:28 -0500 Subject: fixed regression in kvm test description caused by my changes for LXD tests --- jobs/virtualization.txt.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jobs/virtualization.txt.in b/jobs/virtualization.txt.in index 4def608..2a466af 100644 --- a/jobs/virtualization.txt.in +++ b/jobs/virtualization.txt.in @@ -8,7 +8,7 @@ requires: package.name == 'qemu-system' package.name == 'qemu-utils' virtualization.kvm == 'supported' -command: virtualization kvm --debug --log-file=$PLAINBOX_SESSION_SHARE/virt_debug +command: virtualization --debug kvm --log-file=$PLAINBOX_SESSION_SHARE/virt_debug _description: Verifies that a KVM guest can be created and booted using an Ubuntu Server cloud image. -- cgit v1.2.3 From a80a96028ad572d22cfb893700ff02970dfed4b2 Mon Sep 17 00:00:00 2001 From: Jeff Lane Date: Mon, 13 Feb 2017 19:18:43 -0500 Subject: Forgot to remove the import of pdb from debugging --- bin/virtualization | 1 - 1 file changed, 1 deletion(-) diff --git a/bin/virtualization b/bin/virtualization index 0551c4a..acb3c79 100755 --- a/bin/virtualization +++ b/bin/virtualization @@ -23,7 +23,6 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -import pdb from argparse import ArgumentParser import configparser from glob import glob -- cgit v1.2.3 From 38847a8f5b6e6a5989e5ce399c6734df290aa09f Mon Sep 17 00:00:00 2001 From: Jeff Lane Date: Tue, 28 Mar 2017 15:00:50 -0400 Subject: More refinements to LXD tests LP: #1677003 --- bin/virtualization | 75 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 21 deletions(-) diff --git a/bin/virtualization b/bin/virtualization index acb3c79..db70849 100755 --- a/bin/virtualization +++ b/bin/virtualization @@ -30,6 +30,7 @@ import os import re import logging import lsb_release +import platform import requests from requests.packages.urllib3.exceptions import NewConnectionError import shlex @@ -50,6 +51,7 @@ import tarfile import time import urllib.request from urllib.parse import urlparse +from uuid import uuid4 DEFAULT_TIMEOUT = 500 @@ -551,7 +553,9 @@ class LXDTest(object): self.rootfs_tarball = None self.template_tarball = None self.name = 'testbed' - self.image_alias = 'ubuntu' + self.image_alias = uuid4().hex + self.default_remote = "ubuntu:" + self.os_version = platform.linux_distribution()[1] def run_command(self, cmd): task = RunCommand(cmd) @@ -561,8 +565,15 @@ class LXDTest(object): logging.error(' STDOUT: {}'.format(task.stdout)) logging.error(' STDERR: {}'.format(task.stderr)) return False - - return True + else: + logging.debug('Command {}:'.format(task.cmd)) + if task.stdout != '': + logging.debug(' STDOUT: {}'.format(task.stdout)) + elif task.stderr != '': + logging.debug(' STDERR: {}'.format(task.stderr)) + else: + logging.debug(' Command returned no output') + return True def setup(self): # Initialize LXD @@ -570,12 +581,12 @@ class LXDTest(object): logging.debug("Attempting to initialize LXD") # TODO: Need a method to see if LXD is already initialized if not self.run_command('lxd init --auto'): - logging.error('Error encounterd while initializing LXD') + logging.debug('Error encounterd while initializing LXD') result = False # Retrieve and insert LXD images - logging.debug("Downloading template.") if self.template_url is not None: + logging.debug("Downloading template.") targetfile = urlparse(self.template_url).path.split('/')[-1] filename = os.path.join('/tmp', targetfile) if not os.path.isfile(filename): @@ -592,8 +603,8 @@ class LXDTest(object): "Skipping Download.".format(filename)) self.template_tarball = filename - logging.debug("Downloading rootfs.") if self.rootfs_url is not None: + logging.debug("Downloading rootfs.") targetfile = urlparse(self.rootfs_url).path.split('/')[-1] filename = os.path.join('/tmp', targetfile) if not os.path.isfile(filename): @@ -610,15 +621,24 @@ class LXDTest(object): self.rootfs_tarball = filename # Insert images - # TODO: check to see if ubuntu already exists - logging.debug("Importing images into LXD") - cmd = 'lxc image import {} rootfs {} --alias {}'.format( - self.template_tarball, self.rootfs_tarball, - self.image_alias) - if not self.run_command(cmd): - logging.error('Error encountered while attempting to ' - 'import images into LXD') - result = False + if result is True: + logging.debug("Importing images into LXD") + cmd = 'lxc image import {} rootfs {} --alias {}'.format( + self.template_tarball, self.rootfs_tarball, + self.image_alias) + if not self.run_command(cmd): + logging.error('Error encountered while attempting to ' + 'import images into LXD') + result = False + else: + logging.debug("No local image available, attempting to " + "import from default remote.") + cmd = 'lxc image copy {}{} local: --alias {}'.format( + self.default_remote, self.os_version, self.image_alias) + if not self.run_command(cmd): + loggging.error('Error encountered while attempting to ' + 'import images from default remote.') + result = False return result @@ -626,16 +646,21 @@ class LXDTest(object): """ Downloads LXD files for same release as host machine """ + # TODO: Clean this up to use a non-internet simplestream on MAAS server logging.debug("Attempting download of {} from {}".format(filename, url)) try: resp = urllib.request.urlretrieve(url, filename) except (IOError, - Error, + OSError, urllib.error.HTTPError, urllib.error.URLError) as exception: logging.error("Failed download of image from %s: %s", - image_url, exception) + url, exception) + return False + except ValueError as verr: + logging.error("Invalid URL %s" % url) + logging.error("%s" % verr) return False if not os.path.isfile(filename): @@ -665,10 +690,18 @@ class LXDTest(object): if not self.run_command('lxc launch {} {}'.format(self.image_alias, self.name)): return False + + logging.debug("Container listing:") + cmd = ("lxc list") + if not self.run_command(cmd): + return False + + logging.debug("Testing container") cmd = ("lxc exec {} dd if=/dev/urandom of=testdata.txt " "bs=1024 count=1000".format(self.name)) if not self.run_command(cmd): return False + return True @@ -764,12 +797,12 @@ def main(): # silence normal output from requests module logging.getLogger("requests").setLevel(logging.WARNING) - # to check if not len(sys.argv) > 1 - if len(vars(args)) == 0: + # Verify args + try: + args.func(args) + except AttributeError: parser.print_help() return False - args.func(args) - if __name__ == "__main__": main() -- cgit v1.2.3 From f832021568e9a6205baa6234a7dd7968ebfaadf5 Mon Sep 17 00:00:00 2001 From: Jeff Lane Date: Tue, 28 Mar 2017 15:01:50 -0400 Subject: PEP8 cleanup --- bin/virtualization | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bin/virtualization b/bin/virtualization index db70849..6091fe8 100755 --- a/bin/virtualization +++ b/bin/virtualization @@ -690,7 +690,7 @@ class LXDTest(object): if not self.run_command('lxc launch {} {}'.format(self.image_alias, self.name)): return False - + logging.debug("Container listing:") cmd = ("lxc list") if not self.run_command(cmd): @@ -701,7 +701,6 @@ class LXDTest(object): "bs=1024 count=1000".format(self.name)) if not self.run_command(cmd): return False - return True -- cgit v1.2.3 From 1d6da6d616d6342dc411e65e05b4ee36cc223a46 Mon Sep 17 00:00:00 2001 From: Maciej Kisielewski Date: Thu, 30 Mar 2017 12:25:47 +0200 Subject: fix validation warnings in optical.txt.in Signed-off-by: Maciej Kisielewski --- jobs/optical.txt.in | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/jobs/optical.txt.in b/jobs/optical.txt.in index ad77185..75e8e2d 100644 --- a/jobs/optical.txt.in +++ b/jobs/optical.txt.in @@ -19,7 +19,7 @@ id: optical/read_{name} estimated_duration: 120.0 user: root command: optical_read_test /dev/{name} -description: +_description: PURPOSE: This test will check your {product} device's ability to read CD media STEPS: @@ -114,7 +114,7 @@ requires: estimated_duration: 120.0 user: root command: set -o pipefail; optical_write_test /dev/{name} dvd | ansi_parser -description: +_description: PURPOSE: This test will check your system's {product} writing capabilities. This test requires a blank DVD-R or DVD+R. STEPS: @@ -136,7 +136,7 @@ requires: optical_drive_{name}.dvd_write == 'supported' user: root command: set -o pipefail; optical_write_test /dev/{name} dvd | ansi_parser -description: +_description: This is an automated version of optical/dvd-write. It assumes you have already inserted a data DVD into your {product} optical drive prior to running Checkbox. plugin: user-interact-verify @@ -167,7 +167,7 @@ requires: optical_drive_{name}.bd_read == "supported" user: root command: optical_read_test /dev/{name} -description: +_description: PURPOSE: This test will check your {product} device's ability to read Blu-Ray (BD) media STEPS: @@ -189,7 +189,7 @@ requires: user: root command: set -o pipefail; optical_write_test /dev/{name} bd | ansi_parser estimated_duration: 120.00 -description: +_description: PURPOSE: This test will check your {product} device's ability to write Blu-Ray (BD) media STEPS: -- cgit v1.2.3