"""
High-level virt test utility functions.
This module is meant to reduce code size by performing common test procedures.
Generally, code here should look like test code.
More specifically:
- Functions in this module should raise exceptions if things go wrong
- Functions in this module typically use functions and classes from
lower-level modules (e.g. utils_misc, aexpect).
- Functions in this module should not be used by lower-level modules.
- Functions in this module should be used in the right context.
For example, a function should not be used where it may display
misleading or inaccurate info or debug messages.
:copyright: 2008-2013 Red Hat Inc.
"""
import commands
import glob
import imp
import locale
import logging
import os
import re
import signal
import tempfile
import threading
import time
import subprocess
from autotest.client import utils, os_dep
from autotest.client.shared import error
from autotest.client.shared import base_packages
from autotest.client.tools import scan_results
from virttest import qemu_virtio_port
from virttest import aexpect, utils_misc, virt_vm, data_dir, utils_net
from virttest import storage, asset, bootstrap, remote
import virttest
# Import submodules, should not be considered as unused import
import libvirt
import qemu
import libguestfs
try:
from virttest.staging import utils_memory
except ImportError:
from autotest.client.shared import utils_memory
# Handle transition from autotest global_config (0.14.x series) to
# settings (0.15.x onwards)
try:
# pylint: disable=E0611
from autotest.client.shared import global_config
section_values = global_config.global_config.get_section_values
settings_value = global_config.global_config.get_config_value
except ImportError:
from autotest.client.shared.settings import settings
section_values = settings.get_section_values
settings_value = settings.get_value
@error.context_aware
def update_boot_option(vm, args_removed=None, args_added=None,
need_reboot=True):
"""
Update guest default kernel option.
:param vm: The VM object.
:param args_removed: Kernel options want to remove.
:param args_added: Kernel options want to add.
:param need_reboot: Whether need reboot VM or not.
:raise error.TestError: Raised if fail to update guest kernel cmdline.
"""
if vm.params.get("os_type") == 'windows':
# this function is only for linux, if we need to change
# windows guest's boot option, we can use a function like:
# update_win_bootloader(args_removed, args_added, reboot)
# (this function is not implement.)
# here we just:
return
login_timeout = int(vm.params.get("login_timeout"))
session = vm.wait_for_login(timeout=login_timeout)
msg = "Update guest kernel option. "
cmd = "grubby --update-kernel=`grubby --default-kernel` "
if args_removed is not None:
msg += " remove args: %s." % args_removed
cmd += '--remove-args="%s." ' % args_removed
if args_added is not None:
msg += " add args: %s" % args_added
cmd += '--args="%s"' % args_added
error.context(msg, logging.info)
status, output = session.cmd_status_output(cmd)
if status != 0:
logging.error(output)
raise error.TestError("Fail to modify guest kernel option")
if need_reboot:
error.context("Rebooting guest ...", logging.info)
session = vm.reboot(session=session, timeout=login_timeout)
cmdline = session.cmd("cat /proc/cmdline", timeout=60)
if args_removed and args_removed in cmdline:
logging.error(output)
err = "Fail to remove guest kernel option %s" % args_removed
raise error.TestError(err)
if args_added and args_added not in cmdline:
logging.error(output)
err = "Fail to add guest kernel option %s" % args_added
raise error.TestError(err)
[docs]def stop_windows_service(session, service, timeout=120):
"""
Stop a Windows service using sc.
If the service is already stopped or is not installed, do nothing.
:param service: The name of the service
:param timeout: Time duration to wait for service to stop
:raise error.TestError: Raised if the service can't be stopped
"""
end_time = time.time() + timeout
while time.time() < end_time:
o = session.cmd_output("sc stop %s" % service, timeout=60)
# FAILED 1060 means the service isn't installed.
# FAILED 1062 means the service hasn't been started.
if re.search(r"\bFAILED (1060|1062)\b", o, re.I):
break
time.sleep(1)
else:
raise error.TestError("Could not stop service '%s'" % service)
[docs]def start_windows_service(session, service, timeout=120):
"""
Start a Windows service using sc.
If the service is already running, do nothing.
If the service isn't installed, fail.
:param service: The name of the service
:param timeout: Time duration to wait for service to start
:raise error.TestError: Raised if the service can't be started
"""
end_time = time.time() + timeout
while time.time() < end_time:
o = session.cmd_output("sc start %s" % service, timeout=60)
# FAILED 1060 means the service isn't installed.
if re.search(r"\bFAILED 1060\b", o, re.I):
raise error.TestError("Could not start service '%s' "
"(service not installed)" % service)
# FAILED 1056 means the service is already running.
if re.search(r"\bFAILED 1056\b", o, re.I):
break
time.sleep(1)
else:
raise error.TestError("Could not start service '%s'" % service)
[docs]def get_windows_file_abs_path(session, filename, extension="exe", tmout=240):
"""
return file abs path "drive+path" by "wmic datafile"
"""
cmd_tmp = "wmic datafile where \"Filename='%s' and "
cmd_tmp += "extension='%s'\" get drive^,path"
cmd = cmd_tmp % (filename, extension)
info = session.cmd_output(cmd, timeout=tmout).strip()
drive_path = re.search(r'(\w):\s+(\S+)', info, re.M)
if not drive_path:
raise error.TestError("Not found file %s.%s in your guest"
% (filename, extension))
return ":".join(drive_path.groups())
[docs]def get_windows_disk_drive(session, filename, extension="exe", tmout=240):
"""
Get the windows disk drive number
"""
return get_windows_file_abs_path(session, filename,
extension).split(":")[0]
[docs]def get_time(session, time_command, time_filter_re, time_format):
"""
Return the host time and guest time. If the guest time cannot be fetched
a TestError exception is raised.
Note that the shell session should be ready to receive commands
(i.e. should "display" a command prompt and should be done with all
previous commands).
:param session: A shell session.
:param time_command: Command to issue to get the current guest time.
:param time_filter_re: Regex filter to apply on the output of
time_command in order to get the current time.
:param time_format: Format string to pass to time.strptime() with the
result of the regex filter.
:return: A tuple containing the host time and guest time.
"""
if re.findall("ntpdate|w32tm", time_command):
output = session.cmd_output_safe(time_command)
if re.match('ntpdate', time_command):
try:
offset = re.findall('offset (.*) sec', output)[0]
except IndexError:
msg = "Fail to get guest time offset. Command "
msg += "'%s', output: %s" % (time_command, output)
raise error.TestError(msg)
try:
host_main, host_mantissa = re.findall(time_filter_re, output)[0]
host_time = (time.mktime(time.strptime(host_main, time_format)) +
float("0.%s" % host_mantissa))
except Exception:
msg = "Fail to get host time. Command '%s', " % time_command
msg += "output: %s" % output
raise error.TestError(msg)
guest_time = host_time - float(offset)
else:
try:
guest_time = re.findall(time_filter_re, output)[0]
except IndexError:
msg = "Fail to get guest time. Command '%s', " % time_command
msg += "output: %s" % output
raise error.TestError(msg)
try:
offset = re.findall("o:(.*)s", output)[0]
except IndexError:
msg = "Fail to get guest time offset. Command "
msg += "'%s', output: %s" % (time_command, output)
raise error.TestError(msg)
if re.match('PM', guest_time):
hour = re.findall('\d+ (\d+):', guest_time)[0]
hour = str(int(hour) + 12)
guest_time = re.sub('\d+\s\d+:', "\d+\s%s:" % hour,
guest_time)[:-3]
else:
guest_time = guest_time[:-3]
guest_time = time.mktime(time.strptime(guest_time, time_format))
host_time = guest_time + float(offset)
elif re.findall("hwclock", time_command):
loc = locale.getlocale(locale.LC_TIME)
# Get and parse host time
host_time_out = utils.run(time_command).stdout
diff = host_time_out.split()[-2]
host_time_out = " ".join(host_time_out.split()[:-2])
try:
try:
locale.setlocale(locale.LC_TIME, "C")
host_time = time.mktime(time.strptime(host_time_out, time_format))
host_time += float(diff)
except Exception, err:
logging.debug("(time_format, time_string): (%s, %s)",
time_format, host_time_out)
raise err
finally:
locale.setlocale(locale.LC_TIME, loc)
output = session.cmd_output_safe(time_command)
# Get and parse guest time
try:
str_time = re.findall(time_filter_re, output)[0]
diff = str_time.split()[-2]
str_time = " ".join(str_time.split()[:-2])
except IndexError:
logging.debug("The time string from guest is:\n%s", str_time)
raise error.TestError("The time string from guest is unexpected.")
except Exception, err:
logging.debug("(time_filter_re, time_string): (%s, %s)",
time_filter_re, str_time)
raise err
guest_time = None
try:
try:
locale.setlocale(locale.LC_TIME, "C")
guest_time = time.mktime(time.strptime(str_time, time_format))
guest_time += float(diff)
except Exception, err:
logging.debug("(time_format, time_string): (%s, %s)",
time_format, str_time)
raise err
finally:
locale.setlocale(locale.LC_TIME, loc)
else:
host_time = time.time()
output = session.cmd_output_safe(time_command).strip()
num = 0.0
reo = None
try:
reo = re.findall(time_filter_re, output)
str_time = reo[0]
if len(reo) > 1:
num = float(reo[1])
except IndexError:
logging.debug("The time string from guest is:\n%s", output)
raise error.TestError("The time string from guest is unexpected.")
except ValueError, err:
logging.debug("Couldn't create float number from %s" % (reo[1]))
except Exception, err:
logging.debug("(time_filter_re, time_string): (%s, %s)",
time_filter_re, output)
raise err
guest_time = time.mktime(time.strptime(str_time, time_format)) + num
return (host_time, guest_time)
[docs]def get_memory_info(lvms):
"""
Get memory information from host and guests in format:
Host: memfree = XXXM; Guests memsh = {XXX,XXX,...}
:params lvms: List of VM objects
:return: String with memory info report
"""
if not isinstance(lvms, list):
raise error.TestError("Invalid list passed to get_stat: %s " % lvms)
try:
meminfo = "Host: memfree = "
meminfo += str(int(utils_memory.freememtotal()) / 1024) + "M; "
meminfo += "swapfree = "
mf = int(utils_memory.read_from_meminfo("SwapFree")) / 1024
meminfo += str(mf) + "M; "
except Exception, e:
raise error.TestFail("Could not fetch host free memory info, "
"reason: %s" % e)
meminfo += "Guests memsh = {"
for vm in lvms:
shm = vm.get_shared_meminfo()
if shm is None:
raise error.TestError("Could not get shared meminfo from "
"VM %s" % vm)
meminfo += "%dM; " % shm
meminfo = meminfo[0:-2] + "}"
return meminfo
@error.context_aware
def run_image_copy(test, params, env):
"""
Copy guest images from nfs server.
1) Mount the NFS share directory
2) Check the existence of source image
3) If it exists, copy the image from NFS
:param test: kvm test object
:param params: Dictionary with the test parameters
:param env: Dictionary with test environment.
"""
vm = env.get_vm(params["main_vm"])
if vm is not None:
vm.destroy()
src = params.get('images_good')
asset_name = '%s' % (os.path.split(params['image_name'])[1])
image = '%s.%s' % (params['image_name'], params['image_format'])
dst_path = storage.get_image_filename(params, data_dir.get_data_dir())
image_dir = os.path.dirname(dst_path)
if params.get("rename_error_image", "no") == "yes":
error_image = os.path.basename(params['image_name']) + "-error"
error_image += '.' + params['image_format']
error_dst_path = os.path.join(image_dir, error_image)
mv_cmd = "/bin/mv %s %s" % (dst_path, error_dst_path)
utils.system(mv_cmd, timeout=360, ignore_status=True)
if src:
mount_dest_dir = params.get('dst_dir', '/mnt/images')
if not os.path.exists(mount_dest_dir):
try:
os.makedirs(mount_dest_dir)
except OSError, err:
logging.warning('mkdir %s error:\n%s', mount_dest_dir, err)
if not os.path.exists(mount_dest_dir):
raise error.TestError('Failed to create NFS share dir %s' %
mount_dest_dir)
error.context("Mount the NFS share directory")
if not utils_misc.mount(src, mount_dest_dir, 'nfs', 'ro'):
raise error.TestError('Could not mount NFS share %s to %s' %
(src, mount_dest_dir))
error.context("Check the existence of source image")
src_path = '%s/%s.%s' % (mount_dest_dir, asset_name,
params['image_format'])
asset_info = virttest.asset.get_file_asset(asset_name, src_path,
dst_path)
if asset_info is None:
raise error.TestError('Could not find %s' % image)
else:
asset_info = virttest.asset.get_asset_info(asset_name)
# Do not force extraction if integrity information is available
if asset_info['sha1_url']:
force = params.get("force_copy", "no") == "yes"
else:
force = params.get("force_copy", "yes") == "yes"
try:
error.context("Copy image '%s'" % image, logging.info)
if utils.is_url(asset_info['url']):
virttest.asset.download_file(asset_info, interactive=False,
force=force)
else:
utils.get_file(asset_info['url'], asset_info['destination'])
finally:
sub_type = params.get("sub_type")
if sub_type:
error.context("Run sub test '%s'" % sub_type, logging.info)
params['image_name'] += "-error"
params['boot_once'] = "c"
vm.create(params=params)
virttest.utils_test.run_virt_sub_test(test, params, env,
params.get("sub_type"))
@error.context_aware
def run_file_transfer(test, params, env):
"""
Transfer a file back and forth between host and guest.
1) Boot up a VM.
2) Create a large file by dd on host.
3) Copy this file from host to guest.
4) Copy this file from guest to host.
5) Check if file transfers ended good.
:param test: QEMU test object.
:param params: Dictionary with the test parameters.
:param env: Dictionary with test environment.
"""
vm = env.get_vm(params["main_vm"])
vm.verify_alive()
login_timeout = int(params.get("login_timeout", 360))
error.context("Login to guest", logging.info)
session = vm.wait_for_login(timeout=login_timeout)
dir_name = test.tmpdir
transfer_timeout = int(params.get("transfer_timeout"))
tmp_dir = params.get("tmp_dir", "/tmp/")
clean_cmd = params.get("clean_cmd", "rm -f")
filesize = int(params.get("filesize", 4000))
count = int(filesize / 10)
if count == 0:
count = 1
host_path = os.path.join(dir_name, "tmp-%s" %
utils_misc.generate_random_string(8))
host_path2 = host_path + ".2"
cmd = "dd if=/dev/zero of=%s bs=10M count=%d" % (host_path, count)
guest_path = (tmp_dir + "file_transfer-%s" %
utils_misc.generate_random_string(8))
try:
error.context("Creating %dMB file on host" % filesize, logging.info)
utils.run(cmd)
error.context("Transferring file host -> guest,"
" timeout: %ss" % transfer_timeout, logging.info)
t_begin = time.time()
vm.copy_files_to(host_path, guest_path, timeout=transfer_timeout)
t_end = time.time()
throughput = filesize / (t_end - t_begin)
logging.info("File transfer host -> guest succeed, "
"estimated throughput: %.2fMB/s", throughput)
error.context("Transferring file guest -> host,"
" timeout: %ss" % transfer_timeout, logging.info)
t_begin = time.time()
vm.copy_files_from(guest_path, host_path2, timeout=transfer_timeout)
t_end = time.time()
throughput = filesize / (t_end - t_begin)
logging.info("File transfer guest -> host succeed, "
"estimated throughput: %.2fMB/s", throughput)
error.context("Compare md5sum between original file and"
" transferred file", logging.info)
if (utils.hash_file(host_path, method="md5") !=
utils.hash_file(host_path2, method="md5")):
raise error.TestFail("File changed after transfer host -> guest "
"and guest -> host")
finally:
logging.info('Cleaning temp file on guest')
try:
session.cmd("%s %s" % (clean_cmd, guest_path))
except aexpect.ShellError, detail:
logging.warn("Could not remove temp files in guest: '%s'", detail)
logging.info('Cleaning temp files on host')
try:
os.remove(host_path)
os.remove(host_path2)
except OSError:
pass
session.close()
@error.context_aware
def run_virtio_serial_file_transfer(test, params, env, port_name=None,
sender="guest", md5_check=True):
"""
Transfer file between host and guest through virtio serial.
:param test: QEMU test object.
:param params: Dictionary with the test parameters.
:param env: Dictionary with test environment.
:param port_name: VM's serial port name used to transfer data.
:param sender: Who is data sender. guest, host or both.
:param md5_check: Check md5 or not.
"""
def get_virtio_port_host_file(vm, port_name):
"""
Returns separated virtserialports
:param vm: VM object
:return: All virtserialports
"""
for port in vm.virtio_ports:
if isinstance(port, qemu_virtio_port.VirtioSerial):
if port.name == port_name:
return port.hostfile
def run_host_cmd(host_cmd, timeout=720):
return utils.system_output(host_cmd, timeout=timeout)
def transfer_data(session, host_cmd, guest_cmd, n_time, timeout,
md5_check, action):
for num in xrange(n_time):
md5_host = "1"
md5_guest = "2"
logging.info("Data transfer repeat %s/%s." % (num + 1, n_time))
try:
args = (host_cmd, timeout)
host_thread = utils.InterruptedThread(run_host_cmd, args)
host_thread.start()
g_output = session.cmd_output(guest_cmd, timeout=timeout)
if action == "both":
if "Md5MissMatch" in g_output:
err = "Data lost during file transfer. Md5 miss match."
err += " Script output:\n%s" % g_output
if md5_check:
raise error.TestFail(err)
else:
logging.warn(err)
else:
md5_re = "md5_sum = (\w{32})"
try:
md5_guest = re.findall(md5_re, g_output)[0]
except Exception:
err = "Fail to get md5, script may fail."
err += " Script output:\n%s" % g_output
raise error.TestError(err)
finally:
if host_thread:
output = ""
output = host_thread.join(10)
if action == "both":
if "Md5MissMatch" in output:
err = "Data lost during file transfer. Md5 miss "
err += "match. Script output:\n%s" % output
if md5_check:
raise error.TestFail(err)
else:
logging.warn(err)
else:
md5_re = "md5_sum = (\w{32})"
try:
md5_host = re.findall(md5_re, output)[0]
except Exception:
err = "Fail to get md5, script may fail."
err += " Script output:\n%s" % output
raise error.TestError(err)
if action != "both" and md5_host != md5_guest:
err = "Data lost during file transfer. Md5 miss match."
err += " Guest script output:\n %s" % g_output
err += " Host script output:\n%s" % output
if md5_check:
raise error.TestFail(err)
else:
logging.warn(err)
env["serial_file_transfer_start"] = False
vm = env.get_vm(params["main_vm"])
vm.verify_alive()
timeout = int(params.get("login_timeout", 360))
session = vm.wait_for_login(timeout=timeout)
if not port_name:
port_name = params["file_transfer_serial_port"]
guest_scripts = params["guest_scripts"]
guest_path = params.get("guest_script_folder", "C:\\")
error.context("Copy test scripts to guest.", logging.info)
for script in guest_scripts.split(";"):
link = os.path.join(data_dir.get_root_dir(), "shared", "deps",
"serial", script)
vm.copy_files_to(link, guest_path, timeout=60)
host_device = get_virtio_port_host_file(vm, port_name)
dir_name = test.tmpdir
transfer_timeout = int(params.get("transfer_timeout", 720))
tmp_dir = params.get("tmp_dir", "/var/tmp/")
filesize = int(params.get("filesize", 10))
count = int(filesize)
host_data_file = os.path.join(dir_name,
"tmp-%s" % utils_misc.generate_random_string(8))
guest_data_file = os.path.join(tmp_dir,
"tmp-%s" % utils_misc.generate_random_string(8))
if sender == "host" or sender == "both":
cmd = "dd if=/dev/zero of=%s bs=1M count=%d" % (host_data_file, count)
error.context("Creating %dMB file on host" % filesize, logging.info)
utils.run(cmd)
else:
guest_file_create_cmd = "dd if=/dev/zero of=%s bs=1M count=%d"
guest_file_create_cmd = params.get("guest_file_create_cmd",
guest_file_create_cmd)
cmd = guest_file_create_cmd % (guest_data_file, count)
error.context("Creating %dMB file on host" % filesize, logging.info)
session.cmd(cmd, timeout=600)
if sender == "host":
action = "send"
guest_action = "receive"
txt = "Transfer data from host to guest"
elif sender == "guest":
action = "receive"
guest_action = "send"
txt = "Transfer data from guest to host"
else:
action = "both"
guest_action = "both"
txt = "Transfer data betwwen guest and host"
host_script = params.get("host_script", "serial_host_send_receive.py")
host_script = os.path.join(data_dir.get_root_dir(), "shared", "deps",
"serial", host_script)
host_cmd = "python %s -s %s -f %s -a %s" % (host_script, host_device,
host_data_file, action)
guest_script = params.get("guest_script",
"VirtIoChannel_guest_send_receive.py")
guest_script = os.path.join(guest_path, guest_script)
guest_cmd = "python %s -d %s -f %s -a %s" % (guest_script, port_name,
guest_data_file, guest_action)
n_time = int(params.get("repeat_times", 1))
txt += " for %s times" % n_time
try:
env["serial_file_transfer_start"] = True
transfer_data(session, host_cmd, guest_cmd, n_time, transfer_timeout,
md5_check, action)
finally:
env["serial_file_transfer_start"] = False
if session:
session.close()
[docs]def run_autotest(vm, session, control_path, timeout,
outputdir, params, copy_only=False, control_args=None,
ignore_session_terminated=False):
"""
Run an autotest control file inside a guest (linux only utility).
:param vm: VM object.
:param session: A shell session on the VM provided.
:param control_path: A path to an autotest control file.
:param timeout: Timeout under which the autotest control file must complete.
:param outputdir: Path on host where we should copy the guest autotest
results to.
:param copy_only: If copy_only is True, copy the autotest to guest and
return the command which need to run test on guest, without
executing it.
:param control_args: The arguments for control file.
:param ignore_session_terminated: If set up this parameter to True we will
ignore the session terminated during test.
The following params is used by the migration
:param params: Test params used in the migration test
"""
def directory_exists(remote_path):
return session.cmd_status("test -d %s" % remote_path) == 0
def copy_if_hash_differs(vm, local_path, remote_path):
"""
Copy a file to a guest if it doesn't exist or if its MD5sum differs.
:param vm: VM object.
:param local_path: Local path.
:param remote_path: Remote path.
:return: Whether the hash differs (True) or not (False).
"""
hash_differs = False
local_hash = utils.hash_file(local_path)
basename = os.path.basename(local_path)
output = session.cmd_output("md5sum %s" % remote_path,
timeout=int(
params.get("md5sum_timeout", 240)))
if "such file" in output:
remote_hash = "0"
elif output:
remote_hash = output.split()[0]
else:
logging.warning("MD5 check for remote path %s did not return.",
remote_path)
# Let's be a little more lenient here and see if it wasn't a
# temporary problem
remote_hash = "0"
if remote_hash != local_hash:
hash_differs = True
logging.debug("Copying %s to guest "
"(remote hash: %s, local hash:%s)",
basename, remote_hash, local_hash)
vm.copy_files_to(local_path, remote_path)
return hash_differs
def extract(vm, remote_path, dest_dir):
"""
Extract the autotest .tar.bz2 file on the guest, ensuring the final
destination path will be dest_dir.
:param vm: VM object
:param remote_path: Remote file path
:param dest_dir: Destination dir for the contents
"""
basename = os.path.basename(remote_path)
logging.debug("Extracting %s on VM %s", basename, vm.name)
session.cmd("rm -rf %s" % dest_dir, timeout=240)
dirname = os.path.dirname(remote_path)
session.cmd("cd %s" % dirname)
session.cmd("mkdir -p %s" % os.path.dirname(dest_dir))
has_pbzip2, pbzip2_path = session.cmd_status_output("which pbzip2")
has_lbzip2, lbzip2_path = session.cmd_status_output("which lbzip2")
if (has_pbzip2 == 0) and "pbzip2" in pbzip2_path:
tar_cmds = "--use-compress-program=pbzip2 -xvmf"
elif (has_lbzip2 == 0) and "lbzip2" in lbzip2_path:
tar_cmds = "--use-compress-program=lbzip2 -xvmf"
else:
tar_cmds = "xvjmf"
e_cmd = "tar %s %s -C %s" % (tar_cmds, basename, os.path.dirname(dest_dir))
output = session.cmd(e_cmd, timeout=120)
autotest_dirname = ""
for line in output.splitlines()[1:]:
autotest_dirname = line.split("/")[0]
break
if autotest_dirname != os.path.basename(dest_dir):
session.cmd("cd %s" % os.path.dirname(dest_dir))
session.cmd("mv %s %s" %
(autotest_dirname, os.path.basename(dest_dir)))
def get_last_guest_results_index():
res_index = 0
for subpath in os.listdir(outputdir):
if re.search("guest_autotest_results\d+", subpath):
res_index = max(res_index, int(re.search("guest_autotest_results(\d+)", subpath).group(1)))
return res_index
def get_results(base_results_dir):
"""
Copy autotest results present on the guest back to the host.
"""
logging.debug("Trying to copy autotest results from guest")
res_index = get_last_guest_results_index()
guest_results_dir = os.path.join(outputdir, "guest_autotest_results%s" % (res_index + 1))
os.mkdir(guest_results_dir)
# result info tarball to host result dir
session = vm.wait_for_login(timeout=360)
results_dir = "%s/results/default" % base_results_dir
results_tarball = "/tmp/results.tgz"
compress_cmd = "cd %s && " % results_dir
compress_cmd += "tar cjvf %s ./*" % results_tarball
compress_cmd += " --exclude=*core*"
compress_cmd += " --exclude=*crash*"
session.cmd(compress_cmd, timeout=600)
vm.copy_files_from(results_tarball, guest_results_dir)
# cleanup autotest subprocess which not terminated, change PWD to
# avoid current connection kill by fuser command;
clean_cmd = "cd /tmp && fuser -k %s" % results_dir
session.sendline(clean_cmd)
session.cmd("rm -f %s" % results_tarball, timeout=240)
results_tarball = os.path.basename(results_tarball)
results_tarball = os.path.join(guest_results_dir, results_tarball)
uncompress_cmd = "tar xjvf %s -C %s" % (results_tarball,
guest_results_dir)
utils.run(uncompress_cmd)
utils.run("rm -f %s" % results_tarball)
def get_results_summary():
"""
Get the status of the tests that were executed on the guest.
NOTE: This function depends on the results copied to host by
get_results() function, so call get_results() first.
"""
res_index = get_last_guest_results_index()
base_dir = os.path.join(outputdir, "guest_autotest_results%s" % res_index)
status_paths = glob.glob(os.path.join(base_dir, "*/status"))
# for control files that do not use job.run_test()
status_no_job = os.path.join(base_dir, "status")
if os.path.exists(status_no_job):
status_paths.append(status_no_job)
status_path = " ".join(status_paths)
try:
output = utils.system_output("cat %s" % status_path)
except error.CmdError, e:
logging.error("Error getting guest autotest status file: %s", e)
return None
try:
results = scan_results.parse_results(output)
# Report test results
logging.info("Results (test, status, duration, info):")
for result in results:
logging.info("\t %s", str(result))
return results
except Exception, e:
logging.error("Error processing guest autotest results: %s", e)
return None
def config_control(control_path, job_args=None):
"""
Edit the control file to adapt the current environment.
Replace CLIENTIP with guestip, and replace SERVERIP with hostip.
Support to pass arguments for client jobs.
For example:
stress args: job.run_test('stress', args="...")
so job_args can be {'args': "..."}, they should be arguments
of this job.
:return: Path of a temp file which contains the result of replacing.
"""
pattern2repl_dict = {r'CLIENTIP': vm.get_address(),
r'SERVERIP': utils_net.get_host_ip_address(params)}
control_file = open(control_path)
lines = control_file.readlines()
control_file.close()
for pattern, repl in pattern2repl_dict.items():
for index in range(len(lines)):
line = lines[index]
lines[index] = re.sub(pattern, repl, line)
# Provided arguments need to be added
if job_args is not None and type(job_args) is dict:
newlines = []
for index in range(len(lines)):
line = lines[index]
# Only job lines need to be configured now
if re.search("job.run_test", line):
# Get job type
allargs = line.split('(')[1].split(',')
if len(allargs) > 1:
job_type = allargs[0]
elif len(allargs) == 1:
job_type = allargs[0].split(')')[0]
else:
job_type = ""
# Assemble job function
jobline = "job.run_test(%s" % job_type
for key, value in job_args.items():
jobline += ", %s='%s'" % (key, value)
jobline += ")\n"
newlines.append(jobline)
break # No need following lines
else:
# None of these lines' business
newlines.append(line)
lines = newlines
fd, temp_control_path = tempfile.mkstemp(prefix="control",
dir=data_dir.get_tmp_dir())
os.close(fd)
temp_control = open(temp_control_path, "w")
temp_control.writelines(lines)
temp_control.close()
return temp_control_path
migrate_background = params.get("migrate_background") == "yes"
if migrate_background:
mig_timeout = float(params.get("mig_timeout", "3600"))
mig_protocol = params.get("migration_protocol", "tcp")
compressed_autotest_path = "/tmp/autotest.tar.bz2"
destination_autotest_path = "/usr/local/autotest"
# To avoid problems, let's make the test use the current AUTODIR
# (autotest client path) location
from autotest.client import common
autotest_path = os.path.dirname(common.__file__)
autotest_local_path = os.path.join(autotest_path, 'autotest-local')
single_dir_install = os.path.isfile(autotest_local_path)
if not single_dir_install:
autotest_local_path = os_dep.command('autotest-local')
kernel_install_path = os.path.join(autotest_path, 'tests',
'kernelinstall')
kernel_install_present = os.path.isdir(kernel_install_path)
autotest_basename = os.path.basename(autotest_path)
autotest_parentdir = os.path.dirname(autotest_path)
# tar the contents of bindir/autotest
if base_packages.has_pbzip2():
tar_cmds = "--use-compress-program=pbzip2 -cvf"
else:
tar_cmds = "cvjf"
cmd = ("cd %s; tar %s %s %s/*" %
(autotest_parentdir, tar_cmds, compressed_autotest_path, autotest_basename))
cmd += " --exclude=%s/results*" % autotest_basename
cmd += " --exclude=%s/tmp" % autotest_basename
cmd += " --exclude=%s/control*" % autotest_basename
cmd += " --exclude=*.pyc"
cmd += " --exclude=*.svn"
cmd += " --exclude=*.git"
cmd += " --exclude=%s/tests/virt/*" % autotest_basename
utils.run(cmd)
# Copy autotest.tar.bz2
update = copy_if_hash_differs(vm, compressed_autotest_path,
compressed_autotest_path)
# Extract autotest.tar.bz2
if update or not directory_exists(destination_autotest_path):
extract(vm, compressed_autotest_path, destination_autotest_path)
g_fd, g_path = tempfile.mkstemp(dir='/tmp/')
aux_file = os.fdopen(g_fd, 'w')
config = section_values(('CLIENT', 'COMMON'))
config.set('CLIENT', 'output_dir', destination_autotest_path)
config.set('COMMON', 'autotest_top_path', destination_autotest_path)
destination_test_dir = os.path.join(destination_autotest_path, 'tests')
config.set('COMMON', 'test_dir', destination_test_dir)
destination_test_output_dir = os.path.join(destination_autotest_path,
'results')
config.set('COMMON', 'test_output_dir', destination_test_output_dir)
config.write(aux_file)
aux_file.close()
global_config_guest = os.path.join(destination_autotest_path,
'global_config.ini')
vm.copy_files_to(g_path, global_config_guest)
os.unlink(g_path)
if not single_dir_install:
vm.copy_files_to(autotest_local_path,
os.path.join(destination_autotest_path,
'autotest-local'))
# Support autotests that are in client-server model.
server_control_path = None
if os.path.isdir(control_path):
server_control_path = os.path.join(control_path, "control.server")
server_control_path = config_control(server_control_path)
control_path = os.path.join(control_path, "control.client")
# Edit control file and copy it to vm.
if control_args is not None:
job_args = {'args': control_args}
else:
job_args = None
temp_control_path = config_control(control_path, job_args=job_args)
vm.copy_files_to(temp_control_path,
os.path.join(destination_autotest_path, 'control'))
# remove the temp control file.
if os.path.exists(temp_control_path):
os.remove(temp_control_path)
if not kernel_install_present:
kernel_install_dir = os.path.join(virttest.data_dir.get_root_dir(),
"shared", "deps", "run_autotest",
"kernel_install")
kernel_install_dest = os.path.join(destination_autotest_path, 'tests',
'kernelinstall')
vm.copy_files_to(kernel_install_dir, kernel_install_dest)
module_dir = os.path.dirname(virttest.__file__)
utils_koji_file = os.path.join(module_dir, 'staging', 'utils_koji.py')
vm.copy_files_to(utils_koji_file, kernel_install_dest)
# Copy a non crippled boottool and make it executable
boottool_path = os.path.join(virttest.data_dir.get_root_dir(),
"shared", "deps", "run_autotest",
"boottool.py")
boottool_dest = '/usr/local/autotest/tools/boottool.py'
vm.copy_files_to(boottool_path, boottool_dest)
session.cmd("chmod +x %s" % boottool_dest)
# Clean the environment.
session.cmd("cd %s" % destination_autotest_path)
try:
session.cmd("rm -f control.state")
session.cmd("rm -rf results/*")
session.cmd("rm -rf tmp/*")
except aexpect.ShellError:
pass
# Check copy_only.
if copy_only:
return ("%s/autotest-local --verbose %s/control" %
(destination_autotest_path, destination_autotest_path))
# Run the test
logging.info("Running autotest control file %s on guest, timeout %ss",
os.path.basename(control_path), timeout)
# Start a background job to run server process if needed.
server_process = None
if server_control_path:
command = ("%s %s --verbose -t %s" % (autotest_local_path,
server_control_path,
os.path.basename(server_control_path)))
server_process = aexpect.run_bg(command)
try:
bg = None
try:
start_time = time.time()
logging.info("---------------- Test output ----------------")
if migrate_background:
mig_timeout = float(params.get("mig_timeout", "3600"))
mig_protocol = params.get("migration_protocol", "tcp")
bg = utils.InterruptedThread(session.cmd_output,
kwargs={
'cmd': "./autotest-local "
" control",
'timeout': timeout,
'print_func': logging.info})
bg.start()
while bg.isAlive():
logging.info("Autotest job did not end, start a round of "
"migration")
vm.migrate(timeout=mig_timeout, protocol=mig_protocol)
else:
if params.get("guest_autotest_verbosity", "yes") == "yes":
verbose = " --verbose"
else:
verbose = ""
session.cmd_output("./autotest-local %s control" % verbose,
timeout=timeout,
print_func=logging.info)
finally:
logging.info("------------- End of test output ------------")
if migrate_background and bg:
bg.join()
# Do some cleanup work on host if test need a server.
if server_process:
if server_process.is_alive():
utils_misc.kill_process_tree(server_process.get_pid(),
signal.SIGINT)
server_process.close()
# Remove the result dir produced by server_process.
server_result = os.path.join(autotest_path,
"results",
os.path.basename(server_control_path))
if os.path.isdir(server_result):
utils.safe_rmdir(server_result)
# Remove the control file for server.
if os.path.exists(server_control_path):
os.remove(server_control_path)
except aexpect.ShellTimeoutError:
if vm.is_alive():
get_results(destination_autotest_path)
get_results_summary()
raise error.TestError("Timeout elapsed while waiting for job to "
"complete")
else:
raise error.TestError("Autotest job on guest failed "
"(VM terminated during job)")
except aexpect.ShellProcessTerminatedError:
if ignore_session_terminated:
try:
vm.verify_alive()
except Exception:
get_results(destination_autotest_path)
raise error.TestError("Autotest job on guest failed "
"(VM terminated during job)")
logging.debug("Wait for autotest job finished on guest.")
session.close()
session = vm.wait_for_login()
while time.time() < start_time + timeout:
ps_cmd = "ps ax"
_, processes = session.cmd_status_output(ps_cmd)
if "autotest-local" not in processes:
logging.debug("Autotest job finished on guest")
break
time.sleep(1)
else:
get_results(destination_autotest_path)
get_results_summary()
raise error.TestError("Timeout elapsed while waiting for job "
"to complete")
else:
get_results(destination_autotest_path)
raise error.TestError("Autotest job on guest failed "
"(Remote session terminated during job)")
get_results(destination_autotest_path)
results = get_results_summary()
if results is not None:
# Make a list of FAIL/ERROR/ABORT results (make sure FAIL results appear
# before ERROR results, and ERROR results appear before ABORT results)
bad_results = [r[0] for r in results if r[1] == "FAIL"]
bad_results += [r[0] for r in results if r[1] == "ERROR"]
bad_results += [r[0] for r in results if r[1] == "ABORT"]
# Fail the test if necessary
if not results:
raise error.TestFail("Autotest control file run did not produce any "
"recognizable results")
if bad_results:
if len(bad_results) == 1:
e_msg = ("Test %s failed during control file execution" %
bad_results[0])
else:
e_msg = ("Tests %s failed during control file execution" %
" ".join(bad_results))
raise error.TestFail(e_msg)
[docs]def get_loss_ratio(output):
"""
Get the packet loss ratio from the output of ping.
:param output: Ping output.
"""
try:
return int(re.findall('(\d+)% packet loss', output)[0])
except IndexError:
logging.debug(output)
return -1
[docs]def raw_ping(command, timeout, session, output_func):
"""
Low-level ping command execution.
:param command: Ping command.
:param timeout: Timeout of the ping command.
:param session: Local executon hint or session to execute the ping command.
"""
if session is None:
logging.info("The command of Ping is: %s", command)
process = aexpect.run_bg(command, output_func=output_func,
timeout=timeout)
# Send SIGINT signal to notify the timeout of running ping process,
# Because ping have the ability to catch the SIGINT signal so we can
# always get the packet loss ratio even if timeout.
if process.is_alive():
utils_misc.kill_process_tree(process.get_pid(), signal.SIGINT)
status = process.get_status()
output = process.get_output()
process.close()
return status, output
else:
output = ""
try:
output = session.cmd_output(command, timeout=timeout,
print_func=output_func)
except aexpect.ShellTimeoutError:
# Send ctrl+c (SIGINT) through ssh session
session.send("\003")
try:
output2 = session.read_up_to_prompt(print_func=output_func)
output += output2
except aexpect.ExpectTimeoutError, e:
output += e.output
# We also need to use this session to query the return value
session.send("\003")
session.sendline(session.status_test_command)
try:
o2 = session.read_up_to_prompt()
except aexpect.ExpectError:
status = -1
else:
try:
status = int(re.findall("\d+", o2)[0])
except Exception:
status = -1
return status, output
[docs]def ping(dest=None, count=None, interval=None, interface=None,
packetsize=None, ttl=None, hint=None, adaptive=False,
broadcast=False, flood=False, timeout=0,
output_func=logging.debug, session=None):
"""
Wrapper of ping.
:param dest: Destination address.
:param count: Count of icmp packet.
:param interval: Interval of two icmp echo request.
:param interface: Specified interface of the source address.
:param packetsize: Packet size of icmp.
:param ttl: IP time to live.
:param hint: Path mtu discovery hint.
:param adaptive: Adaptive ping flag.
:param broadcast: Broadcast ping flag.
:param flood: Flood ping flag.
:param timeout: Timeout for the ping command.
:param output_func: Function used to log the result of ping.
:param session: Local executon hint or session to execute the ping command.
"""
command = "ping"
if ":" in dest:
command = "ping6"
if dest is not None:
command += " %s " % dest
else:
command += " localhost "
if count is not None:
command += " -c %s" % count
if interval is not None:
command += " -i %s" % interval
if interface is not None:
command += " -I %s" % interface
else:
if dest.upper().startswith("FE80"):
err_msg = "Using ipv6 linklocal must assigne interface"
raise error.TestNAError(err_msg)
if packetsize is not None:
command += " -s %s" % packetsize
if ttl is not None:
command += " -t %s" % ttl
if hint is not None:
command += " -M %s" % hint
if adaptive:
command += " -A"
if broadcast:
command += " -b"
if flood:
command += " -f -q"
command = "sleep %s && kill -2 `pidof ping` & %s" % (timeout, command)
output_func = None
timeout += 1
return raw_ping(command, timeout, session, output_func)
[docs]def run_virt_sub_test(test, params, env, sub_type=None, tag=None):
"""
Call another test script in one test script.
:param test: Virt Test object.
:param params: Dictionary with the test parameters.
:param env: Dictionary with test environment.
:param sub_type: Type of called test script.
:param tag: Tag for get the sub_test params
"""
if sub_type is None:
raise error.TestError("Unspecified sub test type. Please specify a"
"sub test type")
provider = params.get("provider", None)
subtest_dirs = []
subtest_dir = None
if provider is None:
# Verify if we have the correspondent source file for it
for generic_subdir in asset.get_test_provider_subdirs('generic'):
subtest_dirs += data_dir.SubdirList(generic_subdir,
bootstrap.test_filter)
for specific_subdir in asset.get_test_provider_subdirs(params.get("vm_type")):
subtest_dirs += data_dir.SubdirList(specific_subdir,
bootstrap.test_filter)
else:
provider_info = asset.get_test_provider_info(provider)
for key in provider_info['backends']:
subtest_dirs += data_dir.SubdirList(
provider_info['backends'][key]['path'],
bootstrap.test_filter)
for d in subtest_dirs:
module_path = os.path.join(d, "%s.py" % sub_type)
if os.path.isfile(module_path):
subtest_dir = d
break
if subtest_dir is None:
raise error.TestError("Could not find test file %s.py "
"on directories %s" % (sub_type, subtest_dirs))
f, p, d = imp.find_module(sub_type, [subtest_dir])
test_module = imp.load_module(sub_type, f, p, d)
f.close()
# Run the test function
run_func = utils_misc.get_test_entrypoint_func(sub_type, test_module)
if tag is not None:
params = params.object_params(tag)
run_func(test, params, env)
[docs]def get_readable_cdroms(params, session):
"""
Get the cdrom list which contain media in guest.
:param params: Dictionary with the test parameters.
:param session: A shell session on the VM provided.
"""
get_cdrom_cmd = params.get("cdrom_get_cdrom_cmd")
check_cdrom_patttern = params.get("cdrom_check_cdrom_pattern")
o = session.get_command_output(get_cdrom_cmd)
cdrom_list = re.findall(check_cdrom_patttern, o)
logging.debug("Found cdroms on guest: %s" % cdrom_list)
readable_cdroms = []
test_cmd = params.get("cdrom_test_cmd")
for d in cdrom_list:
s, o = session.cmd_status_output(test_cmd % d)
if s == 0:
readable_cdroms.append(d)
break
if not readable_cdroms:
info_cmd = params.get("cdrom_info_cmd")
output = session.cmd_output(info_cmd)
logging.debug("Guest cdroms info: %s" % output)
return readable_cdroms
[docs]def service_setup(vm, session, directory):
params = vm.get_params()
rh_perf_envsetup_script = params.get("rh_perf_envsetup_script")
rebooted = params.get("rebooted", "rebooted")
if rh_perf_envsetup_script:
src = os.path.join(directory, rh_perf_envsetup_script)
vm.copy_files_to(src, "/tmp/rh_perf_envsetup.sh")
logging.info("setup perf environment for host")
commands.getoutput("bash %s host %s" % (src, rebooted))
logging.info("setup perf environment for guest")
session.cmd("bash /tmp/rh_perf_envsetup.sh guest %s" % rebooted)
[docs]def summary_up_result(result_file, ignore, row_head, column_mark):
"""
Use to summary the monitor or other kinds of results. Now it calculates
the average value for each item in the results. It fits to the records
that are in matrix form.
@result_file: files which need to calculate
@ignore: pattern for the comment in results which need to through away
@row_head: pattern for the items in row
@column_mark: pattern for the first line in matrix which used to generate
the items in column
:return: A dictionary with the average value of results
"""
head_flag = False
result_dict = {}
column_list = {}
row_list = []
fd = open(result_file, "r")
for eachLine in fd:
if len(re.findall(ignore, eachLine)) == 0:
if len(re.findall(column_mark, eachLine)) != 0 and not head_flag:
column = 0
_, row, eachLine = re.split(row_head, eachLine)
for i in re.split("\s+", eachLine):
if i:
result_dict[i] = {}
column_list[column] = i
column += 1
head_flag = True
elif len(re.findall(column_mark, eachLine)) == 0:
column = 0
_, row, eachLine = re.split(row_head, eachLine)
row_flag = False
for i in row_list:
if row == i:
row_flag = True
if row_flag is False:
row_list.append(row)
for i in result_dict:
result_dict[i][row] = []
for i in re.split("\s+", eachLine):
if i:
result_dict[column_list[column]][row].append(i)
column += 1
fd.close()
# Calculate the average value
average_list = {}
for i in column_list:
average_list[column_list[i]] = {}
for j in row_list:
average_list[column_list[i]][j] = {}
check = result_dict[column_list[i]][j][0]
if utils_misc.aton(check) or utils_misc.aton(check) == 0.0:
count = 0
for k in result_dict[column_list[i]][j]:
count += utils_misc.aton(k)
average_list[column_list[i]][j] = "%.2f" % (count /
len(result_dict[column_list[i]][j]))
return average_list
[docs]def get_driver_hardware_id(driver_path, mount_point="/tmp/mnt-virtio",
storage_path="/tmp/prewhql.iso",
re_hw_id="(PCI.{14,50})", run_cmd=True):
"""
Get windows driver's hardware id from inf files.
:param dirver: Configurable driver name.
:param mount_point: Mount point for the driver storage
:param storage_path: The path of the virtio driver storage
:param re_hw_id: the pattern for getting hardware id from inf files
:param run_cmd: Use hardware id in windows cmd command or not
:return: Windows driver's hardware id
"""
if not os.path.exists(mount_point):
os.mkdir(mount_point)
if not os.path.ismount(mount_point):
utils.system("mount %s %s -o loop" % (storage_path, mount_point),
timeout=60)
driver_link = os.path.join(mount_point, driver_path)
txt_file = ""
try:
txt_file = open(driver_link, "r")
txt = txt_file.read()
hwid = re.findall(re_hw_id, txt)[-1].rstrip()
if run_cmd:
hwid = '^&'.join(hwid.split('&'))
txt_file.close()
utils.system("umount %s" % mount_point)
return hwid
except Exception, e:
logging.error("Fail to get hardware id with exception: %s" % e)
if txt_file:
txt_file.close()
utils.system("umount %s" % mount_point, ignore_status=True)
return ""
[docs]class BackgroundTest(object):
"""
This class would run a test in background through a dedicated thread.
"""
def __init__(self, func, params, kwargs={}):
"""
Initialize the object and set a few attributes.
"""
self.thread = threading.Thread(target=self.launch,
args=(func, params, kwargs))
self.exception = None
[docs] def launch(self, func, params, kwargs):
"""
Catch and record the exception.
"""
try:
func(*params, **kwargs)
except Exception, e:
self.exception = e
[docs] def start(self):
"""
Run func(params) in a dedicated thread
"""
self.thread.start()
[docs] def join(self, timeout=600, ignore_status=False):
"""
Wait for the join of thread and raise its exception if any.
"""
self.thread.join(timeout)
# pylint: disable=E0702
if self.exception and (not ignore_status):
raise self.exception
[docs] def is_alive(self):
"""
Check whether the test is still alive.
"""
return self.thread.isAlive()
[docs]def get_image_info(image_file):
return utils_misc.get_image_info(image_file)
[docs]def ntpdate(service_ip, session=None):
"""
set the date and time via NTP
"""
try:
ntpdate_cmd = "ntpdate %s" % service_ip
if session:
session.cmd(ntpdate_cmd)
else:
utils.run(ntpdate_cmd)
except (error.CmdError, aexpect.ShellError), detail:
raise error.TestFail("Failed to set the date and time. %s" % detail)
[docs]def get_date(session=None):
"""
Get the date time
"""
try:
date_cmd = "date +%s"
if session:
date_info = session.cmd_output(date_cmd).strip()
else:
date_info = utils.run(date_cmd).stdout.strip()
return date_info
except (error.CmdError, aexpect.ShellError), detail:
raise error.TestFail("Get date failed. %s " % detail)
##########Stress functions################
[docs]class StressError(Exception):
"""
Stress test exception.
"""
def __init__(self, msg):
Exception.__init__(self, msg)
self.msg = msg
def __str__(self):
return self.msg
[docs]class VMStress(object):
"""
Run Stress tool in vms, such as stress, unixbench, iozone and etc.
"""
def __init__(self, vm, stress_type):
"""
Set parameters for stress type
"""
def _parameters_filter(stress_type):
"""Set parameters according stress_type"""
_control_files = {'unixbench': "unixbench5.control",
'stress': "stress.control",
'iozone': "iozone.control"}
_check_cmds = {'unixbench': "pidof -s ./Run",
'stress': "pidof -s stress",
'iozone': "pidof -s iozone"}
_stop_cmds = {'unixbench': "killall ./Run",
'stress': "killall stress",
'iozone': "killall iozone"}
try:
control_file = _control_files[stress_type]
self.control_path = os.path.join(data_dir.get_root_dir(),
"shared/control",
control_file)
self.check_cmd = _check_cmds[stress_type]
self.stop_cmd = _stop_cmds[stress_type]
except KeyError:
self.control_path = ""
self.check_cmd = ""
self.stop_cmd = ""
self.vm = vm
self.params = vm.params
self.timeout = 60
self.stress_type = stress_type
if stress_type not in ["stress", "unixbench", "iozone"]:
raise StressError("Stress %s is not supported now." % stress_type)
_parameters_filter(stress_type)
self.stress_args = self.params.get("stress_args", "")
[docs] def get_session(self):
try:
session = self.vm.wait_for_login()
return session
except aexpect.ShellError, detail:
raise StressError("Login %s failed:\n%s", self.vm.name, detail)
@error.context_aware
def load_stress_tool(self):
"""
load stress tool in guest
"""
session = self.get_session()
command = run_autotest(self.vm, session, self.control_path,
None, None, self.params, copy_only=True,
control_args=self.stress_args)
session.cmd("%s &" % command)
logging.info("Command: %s", command)
running = utils_misc.wait_for(self.app_running, first=0.5, timeout=60)
if not running:
raise StressError("Stress tool %s isn't running"
% self.stress_type)
@error.context_aware
def unload_stress(self):
"""
stop stress tool manually
"""
def _unload_stress():
session = self.get_session()
session.sendline(self.stop_cmd)
if not self.app_running():
return True
return False
error.context("stop stress app in guest", logging.info)
utils_misc.wait_for(_unload_stress, first=2.0,
text="wait stress app quit", step=1.0, timeout=60)
[docs] def app_running(self):
"""
check whether app really run in background
"""
session = self.get_session()
status = session.cmd_status(self.check_cmd, timeout=60)
return status == 0
[docs]class HostStress(object):
"""
Run Stress tool on host, such as stress, unixbench, iozone and etc.
"""
def __init__(self, params, stress_type):
"""
Set parameters for stress type
"""
def _parameters_filter(stress_type):
"""Set parameters according stress_type"""
_control_files = {'unixbench': "unixbench5.control",
'stress': "stress.control"}
_check_cmds = {'unixbench': "pidof -s ./Run",
'stress': "pidof -s stress"}
_stop_cmds = {'unixbench': "killall ./Run",
'stress': "killall stress"}
try:
control_file = _control_files[stress_type]
self.control_path = os.path.join(data_dir.get_root_dir(),
"shared/control",
control_file)
self.check_cmd = _check_cmds[stress_type]
self.stop_cmd = _stop_cmds[stress_type]
except KeyError:
self.control_path = ""
self.check_cmd = ""
self.stop_cmd = ""
self.params = {}
if params:
self.params = params
self.timeout = 60
self.stress_type = stress_type
self.host_stress_process = None
if stress_type not in ["stress", "unixbench"]:
raise StressError("Stress %s is not supported now." % stress_type)
_parameters_filter(stress_type)
self.stress_args = self.params.get("stress_args", "")
@error.context_aware
def load_stress_tool(self):
"""
load stress tool on host.
"""
# Run stress tool on host.
from autotest.client import common
autotest_client_dir = os.path.dirname(common.__file__)
autotest_local_path = os.path.join(autotest_client_dir,
"autotest-local")
args = [autotest_local_path, '--args=%s' % self.stress_args,
self.control_path, '--verbose']
logging.info("Start sub-process by args: %s", args)
self.host_stress_process = subprocess.Popen(args,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
running = utils_misc.wait_for(self.app_running, first=0.5, timeout=60)
if not running:
raise StressError("Stress tool %s isn't running"
% self.stress_type)
@error.context_aware
def unload_stress(self):
"""
stop stress tool manually
"""
def _unload_stress():
if self.host_stress_process is not None:
utils_misc.kill_process_tree(self.host_stress_process.pid)
if not self.app_running():
return True
return False
error.context("stop stress app on host", logging.info)
utils_misc.wait_for(_unload_stress, first=2.0,
text="wait stress app quit", step=1.0, timeout=60)
[docs] def app_running(self):
"""
check whether app really run in background
"""
result = utils.run(self.check_cmd, timeout=60, ignore_status=True)
return result.exit_status == 0
[docs]def load_stress(stress_type, vms, params):
"""
Load stress for tests.
:param stress_type: The stress type you need
:param params: Useful parameters for stress
:param vms: Used when it's stress in vms
"""
fail_info = []
# Add stress/iozone tool in vms
if stress_type in ['stress_in_vms', 'iozone_in_vms']:
for vm in vms:
try:
vstress = VMStress(vm, stress_type.split('_')[0])
vstress.load_stress_tool()
except StressError, detail:
fail_info.append("Launch stress in %s failed: %s"
% (vm.name, detail))
# Add stress for host
elif stress_type == "stress_on_host":
try:
hstress = HostStress(params, "stress")
hstress.load_stress_tool()
except StressError, detail:
fail_info.append("Launch stress on host failed: %s" % str(detail))
# Booting vm for following test
elif stress_type == "load_vm_booting":
load_vms = params.get("load_vms", [])
if len(load_vms):
load_vm = load_vms[0]
try:
if load_vm.is_alive():
load_vm.destroy()
load_vm.start()
except virt_vm.VMStartError:
fail_info.append("Start load vm %s failed." % load_vm.name)
else:
fail_info.append("No load vm provided.")
# Booting vms for following test
elif stress_type == "load_vms_booting":
load_vms = params.get("load_vms", [])
for load_vm in load_vms:
if load_vm.is_alive():
load_vm.destroy()
# Booting load_vms at same time
for load_vm in load_vms:
try:
load_vm.start()
except virt_vm.VMStartError:
fail_info.append("Start load vm %s failed." % load_vm.name)
break
# Booting test vms for following test
elif stress_type == "vms_booting":
for vm in vms:
if vm.is_alive():
vm.destroy()
try:
for vm in vms:
vm.start()
except virt_vm.VMStartError:
fail_info.append("Start vms failed.")
return fail_info
[docs]def unload_stress(stress_type, vms):
"""
Unload stress loaded by load_stress(...).
"""
if stress_type == "stress_in_vms":
for vm in vms:
VMStress(vm, "stress").unload_stress()
elif stress_type == "stress_on_host":
HostStress(None, "stress").unload_stress()
[docs]class RemoteDiskManager(object):
"""Control images on remote host"""
def __init__(self, params):
remote_host = params.get("remote_ip")
remote_user = params.get("remote_user")
remote_pwd = params.get("remote_pwd")
self.runner = remote.RemoteRunner(host=remote_host,
username=remote_user,
password=remote_pwd)
[docs] def get_free_space(self, disk_type, path='/', vgname=None):
"""
Get free space of remote host for path.
:return : the unit is 'G'.
"""
if disk_type == "file":
cmd = "df -BG %s" % os.path.dirname(path)
elif disk_type == "lvm":
cmd = "vgs --units=g | grep %s" % vgname
else:
raise error.TestError("Unsupported Disk Type %s" % disk_type)
try:
output = self.runner.run(cmd).stdout
except error.CmdError, detail:
logging.debug(output)
raise error.TestError("Get space failed: %s." % str(detail))
if disk_type == "file":
try:
return int(output.splitlines()[1].split()[3].split('G')[0])
except IndexError, detail:
raise error.TestError("Get %s space failed: %s." %
(os.path.dirname(path), str(detail)))
elif disk_type == "lvm":
if re.search(vgname, output):
try:
# "int('50.00')" will ValueError, so needs float()
return int(float(output.split()[6].split('g')[0]))
except (IndexError, ValueError), detail:
raise error.TestError("Get %s space failed: %s." %
(vgname, str(detail)))
else:
raise error.TestError("Get %s space failed: %s." %
(vgname, output))
[docs] def occupy_space(self, disk_type, need_size, path=None, vgname=None,
timeout=60):
"""
Create an image or volume to occupy the space of destination path
"""
free = self.get_free_space(disk_type, path, vgname)
logging.debug("Allowed space on remote path:%sGB", free)
occupied_size = free - need_size / 2
occupied_path = os.path.join(os.path.dirname(path), "occupied")
return self.create_image(disk_type, occupied_path, occupied_size,
vgname, "occupied", False, timeout)
[docs] def iscsi_login_setup(self, host, target_name, is_login=True):
"""
Login or logout to a target on remote host.
"""
if is_login:
discovery_cmd = "iscsiadm -m discovery -t sendtargets -p %s" % host
output = self.runner.run(discovery_cmd, ignore_status=True).stdout
if target_name not in output:
raise error.TestError("Discovery %s on %s failed."
% (target_name, host))
cmd = "iscsiadm --mode node --login --targetname %s" % target_name
output = self.runner.run(cmd).stdout
if "successful" not in output:
raise error.TestError("Login to %s failed." % target_name)
else:
cmd = "iscsiadm -m session -P 3"
output = self.runner.run(cmd).stdout
pattern = r"Target:\s+%s.*?disk\s(\w+)\s+\S+\srunning" % target_name
device_name = re.findall(pattern, output, re.S)
try:
return "/dev/%s" % device_name[0]
except IndexError:
raise error.TestError("Can not find target '%s' after login."
% self.target)
else:
if target_name:
cmd = "iscsiadm --mode node --logout -T %s" % target_name
else:
cmd = "iscsiadm --mode node --logout all"
output = self.runner.run(cmd, ignore_status=True).stdout
if "successful" not in output:
logging.error("Logout to %s failed.", target_name)
[docs] def create_vg(self, vgname, device):
"""
Create volume group with provided device.
"""
try:
self.runner.run("vgs | grep %s" % vgname)
logging.debug("Volume group %s does already exist.", vgname)
return True
except error.CmdError:
pass # Not found
try:
self.runner.run("vgcreate %s %s" % (vgname, device))
return True
except error.CmdError, detail:
logging.error("Create vgroup '%s' on remote host failed:%s",
vgname, detail)
return False
[docs] def remove_vg(self, vgname):
"""
Remove volume group on remote host.
"""
try:
self.runner.run("vgremove -f %s" % vgname)
except error.CmdError:
return False
return True
[docs] def create_image(self, disk_type, path=None, size=10, vgname=None,
lvname=None, sparse=True, timeout=60, img_frmt=None):
"""
Create an image for target path.
"""
if disk_type == "file":
self.runner.run("mkdir -p %s" % os.path.dirname(path))
if not os.path.basename(path):
path = os.path.join(path, "temp.img")
cmd = "qemu-img create"
if img_frmt is not None:
cmd += " -f %s" % img_frmt
if sparse:
cmd += " %s %sG" % (path, size)
else:
cmd = "dd if=/dev/zero of=%s bs=1G count=%s" % (path, size)
elif disk_type == "lvm":
if sparse:
cmd = "lvcreate -V %sG %s --name %s --size 1M" % (size, vgname,
lvname)
else:
cmd = "lvcreate -L %sG %s --name %s" % (size, vgname, lvname)
path = "/dev/%s/%s" % (vgname, lvname)
result = self.runner.run(cmd, ignore_status=True, timeout=timeout)
logging.debug(result)
if result.exit_status:
raise error.TestFail("Create image '%s' on remote host failed."
% path)
else:
return path
[docs] def remove_path(self, disk_type, path):
"""
Only allowed to remove path to file or volume.
"""
if disk_type == "file":
if os.path.isdir(path):
return
self.runner.run("rm -f %s" % path, ignore_status=True)
elif disk_type == "lvm":
self.runner.run("lvremove -f %s" % path, ignore_status=True)
[docs]def check_dest_vm_network(vm, ip, remote_host, username, password):
"""
Ping migrated vms on remote host.
"""
runner = remote.RemoteRunner(host=remote_host, username=username,
password=password)
# Timeout to wait vm's network
logging.debug("Verifying VM IP...")
timeout = 60
ping_failed = True
ping_cmd = "ping -c 4 %s" % ip
while timeout > 0:
ping_result = runner.run(ping_cmd, ignore_status=True)
if ping_result.exit_status:
time.sleep(5)
timeout -= 5
continue
ping_failed = False
break
if ping_failed:
raise error.TestFail("Check %s IP failed:%s" % (vm.name,
ping_result.stdout))
[docs]def canonicalize_disk_address(disk_address):
"""
Canonicalize disk address.
Convert {decimal|octal|hexadecimal} to decimal
pci:0x0000.0x00.0x0b.0x0 => pci:0.0.11.0
ide:00.00.00 => ide:0.0.0
scsi:00.00.0x11 => scsi:0.0.17
"""
add_info = disk_address.split(":")
add_bus_type = add_info[0]
add_detail = add_info[-1]
add_detail_str = ""
for add_item in add_detail.split("."):
add_detail_str += ("%s." % int(add_item, 0))
add_detail_str = "%s:%s" % (add_bus_type, add_detail_str[:-1])
return add_detail_str