Index: client/tests/kvm/tests/virtio_console.py |
diff --git a/client/tests/kvm/tests/virtio_console.py b/client/tests/kvm/tests/virtio_console.py |
index 008ec63724343b7ccd3079eb63fde88bcc22be52..af32bf2f337f4bd7b6cda8c7877ea4f182717ba7 100644 |
--- a/client/tests/kvm/tests/virtio_console.py |
+++ b/client/tests/kvm/tests/virtio_console.py |
@@ -1,15 +1,16 @@ |
""" |
virtio_console test |
-@copyright: Red Hat 2010 |
+@copyright: 2010 Red Hat, Inc. |
""" |
import array, logging, os, random, re, select, shutil, socket, sys, tempfile |
-import threading, time |
+import threading, time, traceback |
from collections import deque |
from threading import Thread |
-import kvm_subprocess, kvm_test_utils, kvm_utils, kvm_preprocessing |
from autotest_lib.client.common_lib import error |
+from autotest_lib.client.bin import utils |
+import kvm_subprocess, kvm_test_utils, kvm_preprocessing |
def run_virtio_console(test, params, env): |
@@ -30,7 +31,228 @@ def run_virtio_console(test, params, env): |
@param params: Dictionary with the test parameters |
@param env: Dictionary with test environment |
""" |
- class th_send(Thread): |
+ class SubTest(object): |
+ """ |
+ Collect result of subtest of main test. |
+ """ |
+ def __init__(self): |
+ """ |
+ Initialize object |
+ """ |
+ self.result = [] |
+ self.passed = 0 |
+ self.failed = 0 |
+ self.cleanup_func = None |
+ self.cleanup_args = None |
+ |
+ |
+ def set_cleanup_func(self, func, args): |
+ """ |
+ Set cleanup function which is called when subtest fails. |
+ |
+ @param func: Function which should be called when test fails. |
+ @param args: Arguments of cleanup function. |
+ """ |
+ self.cleanup_func = func |
+ self.cleanup_args = args |
+ |
+ |
+ def do_test(self, function, args=None, fatal=False, cleanup=True): |
+ """ |
+ Execute subtest function. |
+ |
+ @param function: Object of function. |
+ @param args: List of arguments of function. |
+ @param fatal: If true exception is forwarded to main test. |
+ @param cleanup: If true call cleanup function after crash of test. |
+ @return: Return what returned executed subtest. |
+ @raise TestError: If collapse of test is fatal raise forward |
+ exception from subtest. |
+ """ |
+ if args == None: |
+ args = [] |
+ res = [None, function.func_name, args] |
+ try: |
+ logging.debug("Start test %s.", function.func_name) |
+ ret = function(*args) |
+ res[0] = True |
+ logging.info(self.result_to_string(res)) |
+ self.result.append(res) |
+ self.passed += 1 |
+ return ret |
+ except: |
+ exc_type, exc_value, exc_traceback = sys.exc_info() |
+ logging.error("In function (" + function.func_name + "):") |
+ logging.error("Call from:\n" + |
+ traceback.format_stack()[-2][:-1]) |
+ logging.error("Exception from:\n" + |
+ "".join(traceback.format_exception( |
+ exc_type, exc_value, |
+ exc_traceback.tb_next))) |
+ # Clean up environment after subTest crash |
+ res[0] = False |
+ logging.info(self.result_to_string(res)) |
+ self.result.append(res) |
+ self.failed += 1 |
+ |
+ if cleanup: |
+ try: |
+ self.cleanup_func(*self.cleanup_args) |
+ except: |
+ error.TestFail("Cleanup function crash too.") |
+ if fatal: |
+ raise |
+ |
+ |
+ def is_failed(self): |
+ """ |
+ @return: If any of subtest not pass return True. |
+ """ |
+ if self.failed > 0: |
+ return True |
+ else: |
+ return False |
+ |
+ |
+ def get_result(self): |
+ """ |
+ @return: Result of subtests. |
+ Format: |
+ tuple(pass/fail,function_name,call_arguments) |
+ """ |
+ return self.result |
+ |
+ |
+ def result_to_string_debug(self, result): |
+ """ |
+ @param result: Result of test. |
+ """ |
+ sargs = "" |
+ for arg in result[2]: |
+ sargs += str(arg) + "," |
+ sargs = sargs[:-1] |
+ if result[0]: |
+ status = "PASS" |
+ else: |
+ status = "FAIL" |
+ return ("Subtest (%s(%s)): --> %s") % (result[1], sargs, status) |
+ |
+ |
+ def result_to_string(self, result): |
+ """ |
+ @param result: Result of test. |
+ """ |
+ if result[0]: |
+ status = "PASS" |
+ else: |
+ status = "FAIL" |
+ return ("Subtest (%s): --> %s") % (result[1], status) |
+ |
+ |
+ def headline(self, msg): |
+ """ |
+ Add headline to result output. |
+ |
+ @param msg: Test of headline |
+ """ |
+ self.result.append([msg]) |
+ |
+ |
+ def _gen_res(self, format_func): |
+ """ |
+ Format result with foramting function |
+ |
+ @param format_func: Func for formating result. |
+ """ |
+ result = "" |
+ for res in self.result: |
+ if (len(res) == 3): |
+ result += format_func(res) + "\n" |
+ else: |
+ result += res[0] + "\n" |
+ return result |
+ |
+ |
+ def get_full_text_result(self): |
+ """ |
+ @return string with text form of result |
+ """ |
+ return self._gen_res(lambda str: self.result_to_string_debug(str)) |
+ |
+ |
+ def get_text_result(self): |
+ """ |
+ @return string with text form of result |
+ """ |
+ return self._gen_res(lambda str: self.result_to_string(str)) |
+ |
+ |
+ class Port(object): |
+ """ |
+ Define structure to keep information about used port. |
+ """ |
+ def __init__(self, sock, name, port_type, path): |
+ """ |
+ @param vm: virtual machine object that port owned |
+ @param sock: Socket of port if port is open. |
+ @param name: Name of port for guest side. |
+ @param port_type: Type of port yes = console, no= serialport. |
+ @param path: Path to port on host side. |
+ """ |
+ self.sock = sock |
+ self.name = name |
+ self.port_type = port_type |
+ self.path = path |
+ self.is_open = False |
+ |
+ |
+ def for_guest(self): |
+ """ |
+ Format data for communication with guest side. |
+ """ |
+ return [self.name, self.port_type] |
+ |
+ |
+ def open(self): |
+ """ |
+ Open port on host side. |
+ """ |
+ self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) |
+ self.sock.connect(self.path) |
+ self.is_open = True |
+ |
+ |
+ def clean_port(self): |
+ """ |
+ Clean all data from opened port on host side. |
+ """ |
+ if self.is_open: |
+ self.close() |
+ self.open() |
+ ret = select.select([self.sock], [], [], 1.0) |
+ if ret[0]: |
+ buf = self.sock.recv(1024) |
+ logging.debug("Rest in socket: " + buf) |
+ |
+ |
+ def close(self): |
+ """ |
+ Close port. |
+ """ |
+ self.sock.shutdown(socket.SHUT_RDWR) |
+ self.sock.close() |
+ self.is_open = False |
+ |
+ |
+ def __str__(self): |
+ """ |
+ Convert to text. |
+ """ |
+ return ("%s,%s,%s,%s,%d" % ("Socket", self.name, self.port_type, |
+ self.path, self.is_open)) |
+ |
+ |
+ class ThSend(Thread): |
""" |
Random data sender thread. |
""" |
@@ -53,14 +275,14 @@ def run_virtio_console(test, params, env): |
def run(self): |
- logging.debug("th_send %s: run", self.getName()) |
+ logging.debug("ThSend %s: run", self.getName()) |
while not self.exitevent.isSet(): |
self.idx += self.port.send(self.data) |
- logging.debug("th_send %s: exit(%d)", self.getName(), |
+ logging.debug("ThSend %s: exit(%d)", self.getName(), |
self.idx) |
- class th_send_check(Thread): |
+ class ThSendCheck(Thread): |
""" |
Random data sender thread. |
""" |
@@ -85,7 +307,7 @@ def run_virtio_console(test, params, env): |
def run(self): |
- logging.debug("th_send_check %s: run", self.getName()) |
+ logging.debug("ThSendCheck %s: run", self.getName()) |
too_much_data = False |
while not self.exitevent.isSet(): |
# FIXME: workaround the problem with qemu-kvm stall when too |
@@ -109,14 +331,14 @@ def run_virtio_console(test, params, env): |
idx = self.port.send(buf) |
buf = buf[idx:] |
self.idx += idx |
- logging.debug("th_send_check %s: exit(%d)", self.getName(), |
+ logging.debug("ThSendCheck %s: exit(%d)", self.getName(), |
self.idx) |
if too_much_data: |
- logging.error("th_send_check: workaround the 'too_much_data'" |
+ logging.error("ThSendCheck: workaround the 'too_much_data'" |
"bug") |
- class th_recv(Thread): |
+ class ThRecv(Thread): |
""" |
Recieves data and throws it away. |
""" |
@@ -134,7 +356,7 @@ def run_virtio_console(test, params, env): |
self.blocklen = blocklen |
self.idx = 0 |
def run(self): |
- logging.debug("th_recv %s: run", self.getName()) |
+ logging.debug("ThRecv %s: run", self.getName()) |
while not self.exitevent.isSet(): |
# TODO: Workaround, it didn't work with select :-/ |
try: |
@@ -142,33 +364,33 @@ def run_virtio_console(test, params, env): |
except socket.timeout: |
pass |
self.port.settimeout(self._port_timeout) |
- logging.debug("th_recv %s: exit(%d)", self.getName(), self.idx) |
+ logging.debug("ThRecv %s: exit(%d)", self.getName(), self.idx) |
- class th_recv_check(Thread): |
+ class ThRecvCheck(Thread): |
""" |
Random data receiver/checker thread. |
""" |
- def __init__(self, port, buffer, event, blocklen=1024): |
+ def __init__(self, port, buf, event, blocklen=1024): |
""" |
@param port: Source port. |
- @param buffer: Control data buffer (FIFO). |
+ @param buf: Control data buffer (FIFO). |
@param length: Amount of data we want to receive. |
@param blocklen: Block length. |
""" |
Thread.__init__(self) |
self.port = port |
- self.buffer = buffer |
+ self.buffer = buf |
self.exitevent = event |
self.blocklen = blocklen |
self.idx = 0 |
def run(self): |
- logging.debug("th_recv_check %s: run", self.getName()) |
+ logging.debug("ThRecvCheck %s: run", self.getName()) |
while not self.exitevent.isSet(): |
ret = select.select([self.port], [], [], 1.0) |
- if ret and (not self.exitevent.isSet()): |
+ if ret[0] and (not self.exitevent.isSet()): |
buf = self.port.recv(self.blocklen) |
if buf: |
# Compare the recvd data with the control data |
@@ -186,156 +408,13 @@ def run_virtio_console(test, params, env): |
for buf in self.buffer: |
ch_ += buf |
logging.error("Queue = %s", repr(ch_)) |
- raise error.TestFail("th_recv_check: incorrect " |
+ raise error.TestFail("ThRecvCheck: incorrect " |
"data") |
self.idx += len(buf) |
- logging.debug("th_recv_check %s: exit(%d)", self.getName(), |
+ logging.debug("ThRecvCheck %s: exit(%d)", self.getName(), |
self.idx) |
- class cpu_load(): |
- """ |
- Get average cpu load between start and get_load. |
- """ |
- def __init__ (self): |
- self.old_load = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] |
- self.startTime = 0 |
- self.endTime = 0 |
- |
- |
- def _get_cpu_load(self): |
- # Let's see if we can calc system load. |
- try: |
- f = open("/proc/stat", "r") |
- tmp = f.readlines(200) |
- f.close() |
- except: |
- logging.critical("Error reading /proc/stat") |
- error.TestFail("average_cpu_load: Error reading /proc/stat") |
- |
- # 200 bytes should be enough because the information we need |
- # is typically stored in the first line |
- # Info about individual processors (not yet supported) is in |
- # the second (third, ...?) line |
- for line in tmp: |
- if line[0:4] == "cpu ": |
- reg = re.compile('[0-9]+') |
- load_values = reg.findall(line) |
- # extract values from /proc/stat |
- load = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] |
- for i in range(8): |
- load[i] = int(load_values[i]) - self.old_load[i] |
- |
- for i in range(8): |
- self.old_load[i] = int(load_values[i]) |
- return load |
- |
- |
- def start (self): |
- """ |
- Start CPU usage measurement |
- """ |
- self.old_load = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] |
- self.startTime = time.time() |
- self._get_cpu_load() |
- |
- |
- def get_load(self): |
- """ |
- Get and reset CPU usage |
- |
- @return: return group cpu (user[%], system[%], sum[%], testTime[s]) |
- """ |
- self.endTime = time.time() |
- testTime = self.endTime - self.startTime |
- load = self._get_cpu_load() |
- |
- user = load[0] / testTime |
- system = load[2] / testTime |
- sum = user + system |
- |
- return (user, system, sum, testTime) |
- |
- |
- class pid_load(): |
- """ |
- Get average process cpu load between start and get_load |
- """ |
- def __init__ (self, pid, name): |
- self.old_load = [0, 0] |
- self.startTime = 0 |
- self.endTime = 0 |
- self.pid = pid |
- self.name = name |
- |
- |
- def _get_cpu_load(self, pid): |
- # Let's see if we can calc system load. |
- try: |
- f = open("/proc/%d/stat" % (pid), "r") |
- line = f.readline() |
- f.close() |
- except: |
- logging.critical("Error reading /proc/%d/stat", pid) |
- error.TestFail("average_process_cpu_load: Error reading " |
- "/proc/stat") |
- else: |
- reg = re.compile('[0-9]+') |
- load_values = reg.findall(line) |
- del load_values[0:11] |
- # extract values from /proc/stat |
- load = [0, 0] |
- for i in range(2): |
- load[i] = int(load_values[i]) - self.old_load[i] |
- |
- for i in range(2): |
- self.old_load[i] = int(load_values[i]) |
- return load |
- |
- |
- def start (self): |
- """ |
- Start CPU usage measurement |
- """ |
- self.old_load = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] |
- self.startTime = time.time() |
- self._get_cpu_load(self.pid) |
- |
- |
- def get_load(self): |
- """ |
- Get and reset CPU usage. |
- |
- @return: Group cpu |
- (pid, user[%], system[%], sum[%], testTime[s]) |
- """ |
- self.endTime = time.time() |
- testTime = self.endTime - self.startTime |
- load = self._get_cpu_load(self.pid) |
- |
- user = load[0] / testTime |
- system = load[1] / testTime |
- sum = user + system |
- |
- return (self.name, self.pid, user, system, sum, testTime) |
- |
- |
- def print_load(process, system): |
- """ |
- Print load in tabular mode. |
- |
- @param process: List of process statistic tuples. |
- @param system: Tuple of system cpu usage. |
- """ |
- |
- logging.info("%-10s %6s %5s %5s %5s %11s", |
- "NAME", "PID", "USER", "SYS", "SUM", "TIME") |
- for pr in process: |
- logging.info("%-10s %6d %4.0f%% %4.0f%% %4.0f%% %10.3fs" % pr) |
- logging.info("TOTAL: ------ %4.0f%% %4.0f%% %4.0f%% %10.3fs" % |
- system) |
- |
- |
def process_stats(stats, scale=1.0): |
""" |
Process and print the statistic. |
@@ -352,31 +431,32 @@ def run_virtio_console(test, params, env): |
return stats |
- def init_guest(vm, timeout=2): |
+ def _init_guest(vm, timeout=2): |
""" |
- Execute virtio_guest.py on guest, wait until it is initialized. |
+ Execute virtio_console_guest.py on guest, wait until it is initialized. |
@param vm: Informations about the guest. |
@param timeout: Timeout that will be used to verify if the script |
started properly. |
""" |
- logging.debug("compile virtio_guest.py on guest %s", vm[0].name) |
- vm[1].sendline("python -OO /tmp/virtio_guest.py -c &&" |
+ logging.debug("compile virtio_console_guest.py on guest %s", vm[0].name) |
+ |
+ (match, data) = _on_guest("python -OO /tmp/virtio_console_guest.py -c &&" |
"echo -n 'PASS: Compile virtio_guest finished' ||" |
- "echo -n 'FAIL: Compile virtio_guest failed'") |
- (match, data) = vm[1].read_until_last_line_matches(["PASS:", "FAIL:"], |
- timeout) |
- if match == 1 or match is None: |
+ "echo -n 'FAIL: Compile virtio_guest failed'", |
+ vm, timeout) |
+ |
+ if match != 0: |
raise error.TestFail("Command console_switch.py on guest %s failed." |
"\nreturn code: %s\n output:\n%s" % |
(vm[0].name, match, data)) |
- logging.debug("Starting virtio_guest.py on guest %s", vm[0].name) |
- vm[1].sendline("python /tmp/virtio_guest.pyo &&" |
+ logging.debug("Starting virtio_console_guest.py on guest %s", vm[0].name) |
+ vm[1].sendline() |
+ (match, data) = _on_guest("python /tmp/virtio_console_guest.pyo &&" |
"echo -n 'PASS: virtio_guest finished' ||" |
- "echo -n 'FAIL: virtio_guest failed'") |
- (match, data) = vm[1].read_until_last_line_matches(["PASS:", "FAIL:"], |
- timeout) |
- if match == 1 or match is None: |
+ "echo -n 'FAIL: virtio_guest failed'", |
+ vm, timeout) |
+ if match != 0: |
raise error.TestFail("Command console_switch.py on guest %s failed." |
"\nreturn code: %s\n output:\n%s" % |
(vm[0].name, match, data)) |
@@ -384,6 +464,47 @@ def run_virtio_console(test, params, env): |
time.sleep(2) |
+ def init_guest(vm, consoles): |
+ """ |
+ Prepares guest, executes virtio_console_guest.py and initializes test. |
+ |
+ @param vm: Informations about the guest. |
+ @param consoles: Informations about consoles. |
+ """ |
+ conss = [] |
+ for mode in consoles: |
+ for cons in mode: |
+ conss.append(cons.for_guest()) |
+ _init_guest(vm, 10) |
+ on_guest("virt.init(%s)" % (conss), vm, 10) |
+ |
+ |
+ def _search_kernel_crashlog(vm, timeout = 2): |
+ """ |
+ Find kernel crash message. |
+ |
+ @param vm: Informations about the guest. |
+ @param timeout: Timeout used to verify expected output. |
+ |
+ @return: Kernel crash log or None. |
+ """ |
+ data = vm[3].read_nonblocking() |
+ match = re.search("^BUG:", data, re.MULTILINE) |
+ if match == None: |
+ return None |
+ |
+ match = re.search(r"^BUG:.*^---\[ end trace .* \]---", |
+ data, re.DOTALL |re.MULTILINE) |
+ if match == None: |
+ data += vm[3].read_until_last_line_matches( |
+ ["---\[ end trace .* \]---"],timeout) |
+ |
+ match = re.search(r"(^BUG:.*^---\[ end trace .* \]---)", |
+ data, re.DOTALL |re.MULTILINE) |
+ return match.group(0) |
+ |
+ |
+ |
def _on_guest(command, vm, timeout=2): |
""" |
Execute given command inside the script's main loop, indicating the vm |
@@ -395,12 +516,23 @@ def run_virtio_console(test, params, env): |
@return: Tuple (match index, data) |
""" |
- logging.debug("Executing '%s' on virtio_guest.py loop, vm: %s," + |
+ logging.debug("Executing '%s' on virtio_console_guest.py loop, vm: %s," + |
"timeout: %s", command, vm[0].name, timeout) |
vm[1].sendline(command) |
- (match, data) = vm[1].read_until_last_line_matches(["PASS:", |
- "FAIL:[Failed to execute]"], |
- timeout) |
+ try: |
+ (match, data) = vm[1].read_until_last_line_matches(["PASS:", |
+ "FAIL:"], |
+ timeout) |
+ |
+ except (kvm_subprocess.ExpectError): |
+ match = None |
+ data = "Timeout." |
+ |
+ kcrash_data = _search_kernel_crashlog(vm) |
+ if (kcrash_data != None): |
+ logging.error(kcrash_data) |
+ vm[4] = True |
+ |
return (match, data) |
@@ -418,36 +550,13 @@ def run_virtio_console(test, params, env): |
""" |
match, data = _on_guest(command, vm, timeout) |
if match == 1 or match is None: |
- raise error.TestFail("Failed to execute '%s' on virtio_guest.py, " |
+ raise error.TestFail("Failed to execute '%s' on virtio_console_guest.py, " |
"vm: %s, output:\n%s" % |
(command, vm[0].name, data)) |
return (match, data) |
- def socket_readall(sock, read_timeout, mesagesize): |
- """ |
- Read everything from the socket. |
- |
- @param sock: Socket. |
- @param read_timeout: Read timeout. |
- @param mesagesize: Size of message. |
- """ |
- sock_decriptor = sock.fileno() |
- sock.settimeout(read_timeout) |
- message = "" |
- try: |
- while (len(message) < mesagesize): |
- message += sock.recv(mesagesize) |
- except Exception as inst: |
- if (inst.args[0] == "timed out"): |
- logging.debug("Reading timeout") |
- else: |
- logging.debug(inst) |
- sock.setblocking(1) |
- return message |
- |
- |
def _guest_exit_threads(vm, send_pts, recv_pts): |
""" |
Safely executes on_guest("virt.exit_threads()") using workaround of |
@@ -463,7 +572,7 @@ def run_virtio_console(test, params, env): |
logging.debug("Workaround the stuck thread on guest") |
# Thread is stucked in read/write |
for send_pt in send_pts: |
- send_pt[0].sendall(".") |
+ send_pt.sock.sendall(".") |
elif match != 0: |
# Something else |
raise error.TestFail("Unexpected fail\nMatch: %s\nData:\n%s" |
@@ -471,8 +580,8 @@ def run_virtio_console(test, params, env): |
# Read-out all remaining data |
for recv_pt in recv_pts: |
- while select.select([recv_pt[0]], [], [], 0.1)[0]: |
- recv_pt[0].recv(1024) |
+ while select.select([recv_pt.sock], [], [], 0.1)[0]: |
+ recv_pt.sock.recv(1024) |
# This will cause fail in case anything went wrong. |
on_guest("print 'PASS: nothing'", vm, 10) |
@@ -482,11 +591,18 @@ def run_virtio_console(test, params, env): |
""" |
Creates the VM and connects the specified number of consoles and serial |
ports. |
+ Ports are allocated by 2 per 1 virtio-serial-pci device starting with |
+ console. (3+2 => CC|CS|S; 0+2 => SS; 3+4 => CC|CS|SS|S, ...) This way |
+ it's easy to test communication on the same or different |
+ virtio-serial-pci device. |
+ Further in tests the consoles are being picked always from the first |
+ available one (3+2: 2xC => CC|cs|s <communication on the same PCI>; |
+ 2xC,1xS => CC|cS|s <communication between 2 PCI devs) |
@param no_console: Number of desired virtconsoles. |
@param no_serialport: Number of desired virtserialports. |
@return: Tuple with (guest information, consoles information) |
- guest informations = [vm, session, tmp_dir] |
+ guest informations = [vm, session, tmp_dir, kcrash] |
consoles informations = [consoles[], serialports[]] |
""" |
consoles = [] |
@@ -494,204 +610,326 @@ def run_virtio_console(test, params, env): |
tmp_dir = tempfile.mkdtemp(prefix="virtio-console-", dir="/tmp/") |
if not params.get('extra_params'): |
params['extra_params'] = '' |
- params['extra_params'] += " -device virtio-serial" |
- for i in range(0, no_console): |
+ for i in range(0, no_console): |
+ # Spread consoles between multiple PCI devices (2 per a dev) |
+ if not i % 2: |
+ pci = "virtio-serial-pci%d" % (i / 2) |
+ params['extra_params'] += (" -device virtio-serial-pci,id=" |
+ + pci) |
+ pci += ".0" |
params['extra_params'] += (" -chardev socket,path=%s/%d,id=vc%d," |
"server,nowait" % (tmp_dir, i, i)) |
params['extra_params'] += (" -device virtconsole,chardev=vc%d," |
- "name=console-%d,id=c%d" % (i, i, i)) |
+ "name=console-%d,id=c%d,bus=%s" |
+ % (i, i, i, pci)) |
for i in range(no_console, no_console + no_serialport): |
+ # Spread seroal ports between multiple PCI devices (2 per a dev) |
+ if not i % 2: |
+ pci = "virtio-serial-pci%d" % (i / 2) |
+ params['extra_params'] += (" -device virtio-serial-pci,id=" |
+ + pci) |
+ pci += ".0" |
params['extra_params'] += (" -chardev socket,path=%s/%d,id=vs%d," |
"server,nowait" % (tmp_dir, i, i)) |
params['extra_params'] += (" -device virtserialport,chardev=vs%d," |
- "name=serialport-%d,id=p%d" % (i, i, i)) |
- |
+ "name=serialport-%d,id=p%d,bus=%s" |
+ % (i, i, i, pci)) |
logging.debug("Booting first guest %s", params.get("main_vm")) |
kvm_preprocessing.preprocess_vm(test, params, env, |
params.get("main_vm")) |
+ vm = env.get_vm(params.get("main_vm")) |
- vm = kvm_utils.env_get_vm(env, params.get("main_vm")) |
+ session = vm.wait_for_login(timeout=float(params.get("boot_timeout", 240))) |
- session = kvm_test_utils.wait_for_login(vm, 0, |
+ sserial = kvm_test_utils.wait_for_login(vm, 0, |
float(params.get("boot_timeout", 240)), |
- 0, 2) |
+ 0, 2, serial=True) |
# connect the sockets |
for i in range(0, no_console): |
- sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) |
- sock.connect("%s/%d" % (tmp_dir, i)) |
- consoles.append([sock, "console-%d" % i, "yes"]) |
+ consoles.append(Port(None ,"console-%d" % i, |
+ "yes", "%s/%d" % (tmp_dir, i))) |
for i in range(no_console, no_console + no_serialport): |
- sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) |
- sock.connect("%s/%d" % (tmp_dir, i)) |
- serialports.append([sock, "serialport-%d" % i, "no"]) |
+ serialports.append(Port(None ,"serialport-%d" % i, |
+ "no", "%s/%d" % (tmp_dir, i))) |
- return [vm, session, tmp_dir], [consoles, serialports] |
+ kcrash = False |
+ return [vm, session, tmp_dir, sserial, kcrash], [consoles, serialports] |
- def test_smoke(vm, consoles, params): |
+ |
+ def topen(vm, port): |
""" |
- Virtio console smoke test. |
+ Open virtioconsole port. |
- Tests the basic functionalities (poll, read/write with and without |
- connected host, etc. |
+ @param vm: Target virtual machine [vm, session, tmp_dir, ser_session]. |
+ @param port: Port identifier. |
+ """ |
+ on_guest("virt.open('%s')" % (port.name), vm, 10) |
+ port.open() |
- @param vm: target virtual machine [vm, session, tmp_dir] |
- @param consoles: a field of virtio ports with the minimum of 2 items |
- @param params: test parameters '$console_type:$data;...' |
+ |
+ def tmulti_open(vm, port): |
""" |
- logging.info("Smoke test: Tests the basic capabilities of " |
- "virtio_consoles.") |
- # PREPARE |
- for param in params.split(';'): |
- if not param: |
- continue |
- logging.info("test_smoke: params: %s", param) |
- param = param.split(':') |
- if len(param) > 1: |
- data = param[1] |
- else: |
- data = "Smoke test data" |
- param = (param[0] == 'serialport') |
- send_pt = consoles[param][0] |
- recv_pt = consoles[param][1] |
+ Multiopen virtioconsole port. |
+ |
+ @param vm: Target virtual machine [vm, session, tmp_dir, ser_session]. |
+ @param port: Port identifier. |
+ """ |
+ on_guest("virt.close('%s')" % (port.name), vm, 10) |
+ on_guest("virt.open('%s')" % (port.name), vm, 10) |
+ (match, data) = _on_guest("virt.open('%s')" % (port.name), vm, 10) |
+ # Console is permitted to open the device multiple times |
+ if port.port_type == "yes": #is console? |
+ if match != 0: #Multiopen not pass |
+ raise error.TestFail("Unexpected fail of opening the console" |
+ " device for the 2nd time.\n%s" % data) |
+ else: |
+ if match != 1: #Multiopen not fail: |
+ raise error.TestFail("Unexpetded pass of opening the" |
+ " serialport device for the 2nd time.") |
+ elif not "[Errno 24]" in data: |
+ raise error.TestFail("Multiple opening fail but with another" |
+ " exception %s" % data) |
+ port.open() |
+ |
+ def tclose(vm, port): |
+ """ |
+ Close socket. |
+ |
+ @param vm: Target virtual machine [vm, session, tmp_dir, ser_session]. |
+ @param port: Port to open. |
+ """ |
+ on_guest("virt.close('%s')" % (port.name), vm, 10) |
+ port.close() |
+ |
+ |
+ def tpooling(vm, port): |
+ """ |
+ Test try pooling function. |
+ |
+ @param vm: Target virtual machine [vm, session, tmp_dir, ser_session]. |
+ @param port: Port used in test. |
+ """ |
+ # Poll (OUT) |
+ on_guest("virt.poll('%s', %s)" % (port.name, select.POLLOUT), vm, |
+ 2) |
+ |
+ # Poll (IN, OUT) |
+ port.sock.sendall("test") |
+ for test in [select.POLLIN, select.POLLOUT]: |
+ on_guest("virt.poll('%s', %s)" % (port.name, test), vm, 10) |
+ |
+ # Poll (IN HUP) |
+ # I store the socket informations and close the socket |
+ port.close() |
+ for test in [select.POLLIN, select.POLLHUP]: |
+ on_guest("virt.poll('%s', %s)" % (port.name, test), vm, 10) |
+ |
+ # Poll (HUP) |
+ on_guest("virt.recv('%s', 4, 1024, False)" % (port.name), vm, 10) |
+ on_guest("virt.poll('%s', %s)" % (port.name, select.POLLHUP), vm, |
+ 2) |
+ |
+ # Reconnect the socket |
+ port.open() |
+ # Redefine socket in consoles |
+ on_guest("virt.poll('%s', %s)" % (port.name, select.POLLOUT), vm, |
+ 2) |
+ |
+ |
+ def tsigio(vm, port): |
+ """ |
+ Test try sigio function. |
+ |
+ @param vm: Target virtual machine [vm, session, tmp_dir, ser_session]. |
+ @param port: Port used in test. |
+ """ |
+ if port.is_open: |
+ port.close() |
+ |
+ # Enable sigio on specific port |
+ on_guest("virt.async('%s', True, 0)" % |
+ (port.name) , vm, 10) |
+ on_guest("virt.get_sigio_poll_return('%s')" % (port.name) , vm, 10) |
+ |
+ #Test sigio when port open |
+ on_guest("virt.set_pool_want_return('%s', select.POLLOUT)" % |
+ (port.name), vm, 10) |
+ port.open() |
+ match = _on_guest("virt.get_sigio_poll_return('%s')" % |
+ (port.name) , vm, 10)[0] |
+ if match == 1: |
+ raise error.TestFail("Problem with HUP on console port.") |
+ |
+ #Test sigio when port receive data |
+ on_guest("virt.set_pool_want_return('%s', select.POLLOUT |" |
+ " select.POLLIN)" % (port.name), vm, 10) |
+ port.sock.sendall("0123456789") |
+ on_guest("virt.get_sigio_poll_return('%s')" % (port.name) , vm, 10) |
+ |
+ #Test sigio port close event |
+ on_guest("virt.set_pool_want_return('%s', select.POLLHUP |" |
+ " select.POLLIN)" % (port.name), vm, 10) |
+ port.close() |
+ on_guest("virt.get_sigio_poll_return('%s')" % (port.name) , vm, 10) |
+ |
+ #Test sigio port open event and persistence of written data on port. |
+ on_guest("virt.set_pool_want_return('%s', select.POLLOUT |" |
+ " select.POLLIN)" % (port.name), vm, 10) |
+ port.open() |
+ on_guest("virt.get_sigio_poll_return('%s')" % (port.name) , vm, 10) |
+ |
+ #Test event when erase data. |
+ on_guest("virt.clean_port('%s')" % (port.name), vm, 10) |
+ port.close() |
+ on_guest("virt.set_pool_want_return('%s', select.POLLOUT)" |
+ % (port.name), vm, 10) |
+ port.open() |
+ on_guest("virt.get_sigio_poll_return('%s')" % (port.name) , vm, 10) |
+ |
+ # Disable sigio on specific port |
+ on_guest("virt.async('%s', False, 0)" % |
+ (port.name) , vm, 10) |
+ |
+ |
+ def tlseek(vm, port): |
+ """ |
+ Tests the correct handling of lseek (expected fail) |
+ |
+ @param vm: Target virtual machine [vm, session, tmp_dir, ser_session]. |
+ @param port: Port used in test. |
+ """ |
+ # The virt.lseek returns PASS when the seek fails |
+ on_guest("virt.lseek('%s', 0, 0)" % (port.name), vm, 10) |
- # TEST |
- # Poll (OUT) |
- on_guest("virt.poll('%s', %s)" % (send_pt[1], select.POLLOUT), vm, |
- 2) |
- |
- # Poll (IN, OUT) |
- send_pt[0].sendall("test") |
- for test in [select.POLLIN, select.POLLOUT]: |
- on_guest("virt.poll('%s', %s)" % (send_pt[1], test), vm, 2) |
- |
- # Poll (IN HUP) |
- # I store the socket informations and close the socket |
- sock = send_pt[0] |
- send_pt[0] = sock.getpeername() |
- sock.shutdown(2) |
- sock.close() |
- del sock |
- for test in [select.POLLIN, select.POLLHUP]: |
- on_guest("virt.poll('%s', %s)" % (send_pt[1], test), vm, 2) |
- |
- # Poll (HUP) |
- on_guest("virt.recv('%s', 4, 1024, False)" % (send_pt[1]), vm, 2) |
- on_guest("virt.poll('%s', %s)" % (send_pt[1], select.POLLHUP), vm, |
- 2) |
- |
- # Reconnect the socket |
- sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) |
- sock.connect(send_pt[0]) |
- send_pt[0] = sock |
- # Redefine socket in consoles |
- consoles[param][0] = send_pt |
- on_guest("virt.poll('%s', %s)" % (send_pt[1], select.POLLOUT), vm, |
- 2) |
- |
- # Read/write without host connected |
- # I store the socket informations and close the socket |
- sock = send_pt[0] |
- send_pt[0] = sock.getpeername() |
- sock.shutdown(2) |
- sock.close() |
- del sock |
- # Read should pass |
- on_guest("virt.recv('%s', 0, 1024, False)" % send_pt[1], vm, 2) |
- # Write should timed-out |
- match, tmp = _on_guest("virt.send('%s', 10, False)" |
- % send_pt[1], vm, 2) |
- if match != None: |
- raise error.TestFail("Read on guest while host disconnected " |
- "didn't timed out.\nOutput:\n%s" |
- % tmp) |
- sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) |
- sock.connect(send_pt[0]) |
- send_pt[0] = sock |
- |
- # Redefine socket in consoles |
- consoles[param][0] = send_pt |
- if (send_pt[0].recv(1024) < 10): |
- raise error.TestFail("Didn't received data from guest") |
- # Now the _on_guest("virt.send('%s'... command should be finished |
- on_guest("print 'PASS: nothing'", vm, 2) |
- |
- # Non-blocking mode |
- on_guest("virt.blocking('%s', False)" % send_pt[1], vm, 2) |
- # Recv should return FAIL with 0 received data |
- match, tmp = _on_guest("virt.recv('%s', 10, 1024, False)" % |
- send_pt[1], vm, 2) |
- if match == 0: |
- raise error.TestFail("Received data even when non were sent\n" |
- "Data:\n%s" % tmp) |
- elif match == None: |
- raise error.TestFail("Timed out, probably in blocking mode\n" |
- "Data:\n%s" % tmp) |
- elif match != 1: |
- raise error.TestFail("Unexpected fail\nMatch: %s\nData:\n%s" % |
- (match, tmp)) |
- send_pt[0].sendall("1234567890") |
- on_guest("virt.recv('%s', 10, 1024, False)" % send_pt[1], vm, 2) |
- |
- # Blocking mode |
- on_guest("virt.blocking('%s', True)" % send_pt[1], vm, 2) |
- # Recv should timed out |
- match, tmp = _on_guest("virt.recv('%s', 10, 1024, False)" % |
- send_pt[1], vm, 2) |
- if match == 0: |
- raise error.TestFail("Received data even when non were sent\n" |
- "Data:\n%s" % tmp) |
- elif match != None: |
- raise error.TestFail("Unexpected fail\nMatch: %s\nData:\n%s" % |
- (match, tmp)) |
- send_pt[0].sendall("1234567890") |
- # Now guest received the data end escaped from the recv() |
- on_guest("print 'PASS: nothing'", vm, 2) |
- |
- # Basic loopback test |
- on_guest("virt.loopback(['%s'], ['%s'], 1024, virt.LOOP_NONE)" % |
- (send_pt[1], recv_pt[1]), vm, 2) |
- send_pt[0].sendall(data) |
- tmp = "" |
- i = 0 |
- while i <= 10: |
- i += 1 |
- ret = select.select([recv_pt[0]], [], [], 1.0) |
- if ret: |
- tmp += recv_pt[0].recv(1024) |
- if len(tmp) >= len(data): |
- break |
- if tmp != data: |
- raise error.TestFail("Incorrect data: '%s' != '%s'", |
- data, tmp) |
- _guest_exit_threads(vm, [send_pt], [recv_pt]) |
- |
- return consoles |
- |
- |
- def test_loopback(vm, consoles, params): |
- """ |
- Virtio console loopback test. |
+ |
+ def trw_host_offline(vm, port): |
+ """ |
+ Guest read/write from host when host is disconnected. |
+ |
+ @param vm: Target virtual machine [vm, session, tmp_dir, ser_session]. |
+ @param port: Port used in test. |
+ """ |
+ if port.is_open: |
+ port.close() |
+ |
+ on_guest("virt.recv('%s', 0, 1024, False)" % port.name, vm, 10) |
+ match, tmp = _on_guest("virt.send('%s', 10, False)" |
+ % port.name, vm, 10) |
+ if match != None: |
+ raise error.TestFail("Write on guest while host disconnected " |
+ "didn't timed out.\nOutput:\n%s" |
+ % tmp) |
+ |
+ port.open() |
+ |
+ if (port.sock.recv(1024) < 10): |
+ raise error.TestFail("Didn't received data from guest") |
+ # Now the _on_guest("virt.send('%s'... command should be finished |
+ on_guest("print 'PASS: nothing'", vm, 10) |
+ |
+ |
+ def trw_blocking_mode(vm, port): |
+ """ |
+ Guest read\write data in blocking mode. |
+ |
+ @param vm: Target virtual machine [vm, session, tmp_dir, ser_session]. |
+ @param port: Port used in test. |
+ """ |
+ # Blocking mode |
+ if not port.is_open: |
+ port.open() |
+ on_guest("virt.blocking('%s', True)" % port.name, vm, 10) |
+ # Recv should timed out |
+ match, tmp = _on_guest("virt.recv('%s', 10, 1024, False)" % |
+ port.name, vm, 10) |
+ if match == 0: |
+ raise error.TestFail("Received data even when non were sent\n" |
+ "Data:\n%s" % tmp) |
+ elif match != None: |
+ raise error.TestFail("Unexpected fail\nMatch: %s\nData:\n%s" % |
+ (match, tmp)) |
+ port.sock.sendall("1234567890") |
+ # Now guest received the data end escaped from the recv() |
+ on_guest("print 'PASS: nothing'", vm, 10) |
+ |
+ |
+ def trw_nonblocking_mode(vm, port): |
+ """ |
+ Guest read\write data in nonblocking mode. |
+ |
+ @param vm: Target virtual machine [vm, session, tmp_dir, ser_session]. |
+ @param port: Port used in test. |
+ """ |
+ # Non-blocking mode |
+ if not port.is_open: |
+ port.open() |
+ on_guest("virt.blocking('%s', False)" % port.name, vm, 10) |
+ # Recv should return FAIL with 0 received data |
+ match, tmp = _on_guest("virt.recv('%s', 10, 1024, False)" % |
+ port.name, vm, 10) |
+ if match == 0: |
+ raise error.TestFail("Received data even when non were sent\n" |
+ "Data:\n%s" % tmp) |
+ elif match == None: |
+ raise error.TestFail("Timed out, probably in blocking mode\n" |
+ "Data:\n%s" % tmp) |
+ elif match != 1: |
+ raise error.TestFail("Unexpected fail\nMatch: %s\nData:\n%s" % |
+ (match, tmp)) |
+ port.sock.sendall("1234567890") |
+ on_guest("virt.recv('%s', 10, 1024, False)" % port.name, vm, 10) |
+ |
+ |
+ def tbasic_loopback(vm, send_port, recv_port, data="Smoke test data"): |
+ """ |
+ Easy loop back test with loop over only two port. |
+ |
+ @param vm: Target virtual machine [vm, session, tmp_dir, ser_session]. |
+ @param port: Port used in test. |
+ """ |
+ if not send_port.is_open: |
+ send_port.open() |
+ if not recv_port.is_open: |
+ recv_port.open() |
+ on_guest("virt.loopback(['%s'], ['%s'], 1024, virt.LOOP_NONE)" % |
+ (send_port.name, recv_port.name), vm, 10) |
+ send_port.sock.sendall(data) |
+ tmp = "" |
+ i = 0 |
+ while i <= 10: |
+ i += 1 |
+ ret = select.select([recv_port.sock], [], [], 1.0) |
+ if ret: |
+ tmp += recv_port.sock.recv(1024) |
+ if len(tmp) >= len(data): |
+ break |
+ if tmp != data: |
+ raise error.TestFail("Incorrect data: '%s' != '%s'", |
+ data, tmp) |
+ _guest_exit_threads(vm, [send_port], [recv_port]) |
+ |
+ |
+ def tloopback(vm, consoles, params): |
+ """ |
+ Virtio console loopback subtest. |
Creates loopback on the vm machine between send_pt and recv_pts |
ports and sends length amount of data through this connection. |
It validates the correctness of the data sent. |
- @param vm: target virtual machine [vm, session, tmp_dir] |
- @param consoles: a field of virtio ports with the minimum of 2 items |
+ @param vm: Target virtual machine [vm, session, tmp_dir, ser_session]. |
+ @param consoles: Field of virtio ports with the minimum of 2 items. |
@param params: test parameters, multiple recievers allowed. |
'$source_console_type@buffer_length: |
$destination_console_type1@$buffer_length:...: |
$loopback_buffer_length;...' |
""" |
- logging.info("Loopback test: Creates a loopback between sender port " |
- "and receiving port, send data through this connection, " |
- "verify data correctness.") |
# PREPARE |
for param in params.split(';'): |
if not param: |
@@ -730,6 +968,13 @@ def run_virtio_console(test, params, env): |
if len(buf_len) == (idx_console + idx_serialport): |
buf_len.append(1024) |
+ for p in recv_pts: |
+ if not p.is_open: |
+ p.open() |
+ |
+ if not send_pt.is_open: |
+ send_pt.open() |
+ |
if len(recv_pts) == 0: |
raise error.TestFail("test_loopback: incorrect recv consoles" |
"definition") |
@@ -739,21 +984,22 @@ def run_virtio_console(test, params, env): |
for i in range(0, len(recv_pts)): |
queues.append(deque()) |
- tmp = "'%s'" % recv_pts[0][1] |
+ tmp = "'%s'" % recv_pts[0].name |
for recv_pt in recv_pts[1:]: |
- tmp += ", '%s'" % (recv_pt[1]) |
+ tmp += ", '%s'" % (recv_pt.name) |
on_guest("virt.loopback(['%s'], [%s], %d, virt.LOOP_POLL)" |
- % (send_pt[1], tmp, buf_len[-1]), vm, 2) |
+ % (send_pt.name, tmp, buf_len[-1]), vm, 10) |
exit_event = threading.Event() |
# TEST |
- thread = th_send_check(send_pt[0], exit_event, queues, buf_len[0]) |
+ thread = ThSendCheck(send_pt.sock, exit_event, queues, |
+ buf_len[0]) |
thread.start() |
threads.append(thread) |
for i in range(len(recv_pts)): |
- thread = th_recv_check(recv_pts[i][0], queues[i], exit_event, |
+ thread = ThRecvCheck(recv_pts[i].sock, queues[i], exit_event, |
buf_len[i + 1]) |
thread.start() |
threads.append(thread) |
@@ -770,8 +1016,8 @@ def run_virtio_console(test, params, env): |
# Read-out all remaining data |
for recv_pt in recv_pts: |
- while select.select([recv_pt[0]], [], [], 0.1)[0]: |
- recv_pt[0].recv(1024) |
+ while select.select([recv_pt.sock], [], [], 0.1)[0]: |
+ recv_pt.sock.recv(1024) |
_guest_exit_threads(vm, [send_pt], recv_pts) |
@@ -779,19 +1025,17 @@ def run_virtio_console(test, params, env): |
del threads[:] |
- def test_perf(vm, consoles, params): |
+ def tperf(vm, consoles, params): |
""" |
Tests performance of the virtio_console tunel. First it sends the data |
from host to guest and than back. It provides informations about |
computer utilisation and statistic informations about the troughput. |
- @param vm: target virtual machine [vm, session, tmp_dir] |
- @param consoles: a field of virtio ports with the minimum of 2 items |
+ @param vm: Target virtual machine [vm, session, tmp_dir, ser_session]. |
+ @param consoles: Field of virtio ports with the minimum of 2 items. |
@param params: test parameters: |
'$console_type@$buffer_length:$test_duration;...' |
""" |
- logging.info("Performance test: Measure performance for the " |
- "virtio console tunnel") |
for param in params.split(';'): |
if not param: |
continue |
@@ -811,111 +1055,233 @@ def run_virtio_console(test, params, env): |
param = (param[0] == 'serialport') |
port = consoles[param][0] |
+ if not port.is_open: |
+ port.open() |
+ |
data = "" |
for i in range(buf_len): |
data += "%c" % random.randrange(255) |
exit_event = threading.Event() |
- slice = float(duration)/100 |
+ time_slice = float(duration) / 100 |
# HOST -> GUEST |
on_guest('virt.loopback(["%s"], [], %d, virt.LOOP_NONE)' % |
- (port[1], buf_len), vm, 2) |
- thread = th_send(port[0], data, exit_event) |
+ (port.name, buf_len), vm, 10) |
+ thread = ThSend(port.sock, data, exit_event) |
stats = array.array('f', []) |
- loads = [] |
- loads.append(cpu_load()) |
- loads.append(pid_load(os.getpid(), 'autotest')) |
- loads.append(pid_load(vm[0].get_pid(), 'VM')) |
- |
- for load in loads: |
- load.start() |
+ loads = utils.SystemLoad([(os.getpid(), 'autotest'), |
+ (vm[0].get_pid(), 'VM'), 0]) |
+ loads.start() |
_time = time.time() |
thread.start() |
for i in range(100): |
stats.append(thread.idx) |
- time.sleep(slice) |
+ time.sleep(time_slice) |
_time = time.time() - _time - duration |
- print_load([loads[1].get_load(), loads[2].get_load()], |
- loads[0].get_load()) |
+ logging.info("\n" + loads.get_cpu_status_string()[:-1]) |
+ logging.info("\n" + loads.get_mem_status_string()[:-1]) |
exit_event.set() |
thread.join() |
# Let the guest read-out all the remaining data |
while not _on_guest("virt.poll('%s', %s)" % |
- (port[1], select.POLLIN), vm, 2)[0]: |
+ (port.name, select.POLLIN), vm, 10)[0]: |
time.sleep(1) |
_guest_exit_threads(vm, [port], []) |
- if (_time > slice): |
+ if (_time > time_slice): |
logging.error( |
- "Test ran %fs longer which is more than one slice", _time) |
+ "Test ran %fs longer which is more than one time slice", _time) |
else: |
logging.debug("Test ran %fs longer", _time) |
- stats = process_stats(stats[1:], slice*1048576) |
+ stats = process_stats(stats[1:], time_slice * 1048576) |
logging.debug("Stats = %s", stats) |
logging.info("Host -> Guest [MB/s] (min/med/max) = %.3f/%.3f/%.3f", |
- stats[0], stats[len(stats)/2], stats[-1]) |
+ stats[0], stats[len(stats) / 2], stats[-1]) |
del thread |
# GUEST -> HOST |
exit_event.clear() |
stats = array.array('f', []) |
- on_guest("virt.send_loop_init('%s', %d)" % (port[1], buf_len), |
+ on_guest("virt.send_loop_init('%s', %d)" % (port.name, buf_len), |
vm, 30) |
- thread = th_recv(port[0], exit_event, buf_len) |
+ thread = ThRecv(port.sock, exit_event, buf_len) |
thread.start() |
- for load in loads: |
- load.start() |
- on_guest("virt.send_loop()", vm, 2) |
+ loads.start() |
+ on_guest("virt.send_loop()", vm, 10) |
_time = time.time() |
for i in range(100): |
stats.append(thread.idx) |
- time.sleep(slice) |
+ time.sleep(time_slice) |
_time = time.time() - _time - duration |
- print_load([loads[1].get_load(), loads[2].get_load()], |
- loads[0].get_load()) |
- on_guest("virt.exit_threads()", vm, 2) |
+ logging.info("\n" + loads.get_cpu_status_string()[:-1]) |
+ logging.info("\n" + loads.get_mem_status_string()[:-1]) |
+ on_guest("virt.exit_threads()", vm, 10) |
exit_event.set() |
thread.join() |
- if (_time > slice): # Deviation is higher than 1 slice |
+ if (_time > time_slice): # Deviation is higher than 1 time_slice |
logging.error( |
- "Test ran %fs longer which is more than one slice", _time) |
+ "Test ran %fs longer which is more than one time slice", _time) |
else: |
- logging.debug("Test ran %fs longer" % _time) |
- stats = process_stats(stats[1:], slice*1048576) |
+ logging.debug("Test ran %fs longer", _time) |
+ stats = process_stats(stats[1:], time_slice * 1048576) |
logging.debug("Stats = %s", stats) |
logging.info("Guest -> Host [MB/s] (min/med/max) = %.3f/%.3f/%.3f", |
- stats[0], stats[len(stats)/2], stats[-1]) |
+ stats[0], stats[len(stats) / 2], stats[-1]) |
del thread |
- |
del exit_event |
- del loads[:] |
+ |
+ |
+ def _clean_ports(vm, consoles): |
+ """ |
+ Read all data all port from both side of port. |
+ |
+ @param vm: Target virtual machine [vm, session, tmp_dir, ser_session]. |
+ @param consoles: Consoles which should be clean. |
+ """ |
+ for ctype in consoles: |
+ for port in ctype: |
+ openned = port.is_open |
+ port.clean_port() |
+ #on_guest("virt.blocking('%s', True)" % port.name, vm, 10) |
+ on_guest("virt.clean_port('%s'),1024" % port.name, vm, 10) |
+ if not openned: |
+ port.close() |
+ on_guest("virt.close('%s'),1024" % port.name, vm, 10) |
+ |
+ |
+ def clean_ports(vm, consoles): |
+ """ |
+ Clean state of all ports and set port to default state. |
+ Default state: |
+ No data on port or in port buffer. |
+ Read mode = blocking. |
+ |
+ @param vm: Target virtual machine [vm, session, tmp_dir, ser_session]. |
+ @param consoles: Consoles which should be clean. |
+ """ |
+ # Check if python is still alive |
+ print "CLEANING" |
+ match, tmp = _on_guest("is_alive()", vm, 10) |
+ if (match == None) or (match != 0): |
+ logging.error("Python died/is stuck/has remaining threads") |
+ logging.debug(tmp) |
+ try: |
+ if vm[4] == True: |
+ raise error.TestFail("Kernel crash.") |
+ match, tmp = _on_guest("guest_exit()", vm, 10) |
+ if (match == None) or (match == 0): |
+ vm[1].close() |
+ vm[1] = vm[0].wait_for_login(timeout=float(params.get("boot_timeout", 240))) |
+ on_guest("killall -9 python " |
+ "&& echo -n PASS: python killed" |
+ "|| echo -n PASS: python died", |
+ vm, 10) |
+ |
+ init_guest(vm, consoles) |
+ _clean_ports(vm, consoles) |
+ |
+ except (error.TestFail, kvm_subprocess.ExpectError, |
+ Exception), inst: |
+ logging.error(inst) |
+ logging.error("Virtio-console driver is irreparably" |
+ " blocked. Every comd end with sig KILL." |
+ "Try reboot vm for continue in testing.") |
+ vm[1] = vm[0].reboot(vm[1], "system_reset") |
+ init_guest(vm, consoles) |
+ match = _on_guest("virt.clean_port('%s'),1024" % |
+ consoles[0][0].name, vm, 10)[0] |
+ |
+ if (match == None) or (match != 0): |
+ raise error.TestFail("Virtio-console driver is irrepar" |
+ "ably blocked. Every comd end" |
+ " with sig KILL. Neither the " |
+ "restart did not help.") |
+ _clean_ports(vm, consoles) |
+ |
+ |
+ def test_smoke(test, vm, consoles, params): |
+ """ |
+ Virtio console smoke test. |
+ |
+ Tests the basic functionalities (poll, read/write with and without |
+ connected host, etc. |
+ |
+ @param test: Main test object. |
+ @param vm: Target virtual machine [vm, session, tmp_dir, ser_session]. |
+ @param consoles: Field of virtio ports with the minimum of 2 items. |
+ @param params: Test parameters '$console_type:$data;...' |
+ """ |
+ # PREPARE |
+ for param in params.split(';'): |
+ if not param: |
+ continue |
+ headline = "test_smoke: params: %s" % (param) |
+ logging.info(headline) |
+ param = param.split(':') |
+ if len(param) > 1: |
+ data = param[1] |
+ else: |
+ data = "Smoke test data" |
+ param = (param[0] == 'serialport') |
+ send_pt = consoles[param][0] |
+ recv_pt = consoles[param][1] |
+ test.headline(headline) |
+ test.do_test(topen, [vm, send_pt], True) |
+ test.do_test(tclose, [vm, send_pt], True) |
+ test.do_test(tmulti_open, [vm, send_pt]) |
+ test.do_test(tpooling, [vm, send_pt]) |
+ test.do_test(tsigio, [vm, send_pt]) |
+ test.do_test(tlseek, [vm, send_pt]) |
+ test.do_test(trw_host_offline, [vm, send_pt]) |
+ test.do_test(trw_nonblocking_mode, [vm, send_pt]) |
+ test.do_test(trw_blocking_mode, [vm, send_pt]) |
+ test.do_test(tbasic_loopback, [vm, send_pt, recv_pt, data], True) |
+ |
+ |
+ def test_multiport(test, vm, consoles, params): |
+ """ |
+ This is group of test which test virtio_console in maximal load and |
+ with multiple ports. |
+ |
+ @param test: Main test object. |
+ @param vm: Target virtual machine [vm, session, tmp_dir, ser_session]. |
+ @param consoles: Field of virtio ports with the minimum of 2 items. |
+ @param params: Test parameters '$console_type:$data;...' |
+ """ |
+ test.headline("test_multiport:") |
+ #Test Loopback |
+ test.do_test(tloopback, [vm, consoles, params[0]]) |
+ |
+ #Test Performance |
+ test.do_test(tperf, [vm, consoles, params[1]]) |
# INITIALIZE |
- test_smoke_params = params.get('virtio_console_smoke', '') |
- test_loopback_params = params.get('virtio_console_loopback', '') |
- test_perf_params = params.get('virtio_console_perf', '') |
+ |
+ tsmoke_params = params.get('virtio_console_smoke', '') |
+ tloopback_params = params.get('virtio_console_loopback', '') |
+ tperf_params = params.get('virtio_console_perf', '') |
no_serialports = 0 |
no_consoles = 0 |
# consoles required for Smoke test |
- if (test_smoke_params.count('serialport')): |
+ if (tsmoke_params.count('serialport')): |
no_serialports = max(2, no_serialports) |
- if (test_smoke_params.count('console')): |
+ if (tsmoke_params.count('console')): |
no_consoles = max(2, no_consoles) |
# consoles required for Loopback test |
- for param in test_loopback_params.split(';'): |
+ for param in tloopback_params.split(';'): |
no_serialports = max(no_serialports, param.count('serialport')) |
no_consoles = max(no_consoles, param.count('console')) |
# consoles required for Performance test |
- if (test_perf_params.count('serialport')): |
+ if (tperf_params.count('serialport')): |
no_serialports = max(1, no_serialports) |
- if (test_perf_params.count('console')): |
+ if (tperf_params.count('console')): |
no_consoles = max(1, no_consoles) |
if (no_serialports + no_consoles) == 0: |
@@ -924,28 +1290,34 @@ def run_virtio_console(test, params, env): |
vm, consoles = _vm_create(no_consoles, no_serialports) |
- # Copy allocator.py into guests |
+ # Copy virtio_console_guest.py into guests |
pwd = os.path.join(os.environ['AUTODIR'], 'tests/kvm') |
- vksmd_src = os.path.join(pwd, "scripts/virtio_guest.py") |
+ vksmd_src = os.path.join(pwd, "scripts/virtio_console_guest.py") |
dst_dir = "/tmp" |
- if not vm[0].copy_files_to(vksmd_src, dst_dir): |
- raise error.TestFail("copy_files_to failed %s" % vm[0].name) |
+ vm[0].copy_files_to(vksmd_src, dst_dir) |
# ACTUAL TESTING |
# Defines all available consoles; tests udev and sysfs |
- conss = [] |
- for mode in consoles: |
- for cons in mode: |
- conss.append(cons[1:3]) |
- init_guest(vm, 10) |
- on_guest("virt.init(%s)" % (conss), vm, 10) |
- consoles = test_smoke(vm, consoles, test_smoke_params) |
- test_loopback(vm, consoles, test_loopback_params) |
- test_perf(vm, consoles, test_perf_params) |
+ test = SubTest() |
+ try: |
+ init_guest(vm, consoles) |
+ |
+ test.set_cleanup_func(clean_ports, [vm, consoles]) |
+ #Test Smoke |
+ test_smoke(test, vm, consoles, tsmoke_params) |
+ |
+ #Test multiport functionality and performance. |
+ test_multiport(test, vm, consoles, [tloopback_params, tperf_params]) |
+ finally: |
+ logging.info(("Summary: %d tests passed %d test failed :\n" % |
+ (test.passed, test.failed)) + test.get_text_result()) |
+ |
+ if test.is_failed(): |
+ raise error.TestFail("Virtio_console test FAILED.") |
+ |
# CLEANUP |
vm[1].close() |
vm[0].destroy(gracefully=False) |
shutil.rmtree(vm[2]) |
- |