From patchwork Mon Nov 22 13:38:58 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiri Zupka X-Patchwork-Id: 347461 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id oAMDdHV9016843 for ; Mon, 22 Nov 2010 13:39:18 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755793Ab0KVNjK (ORCPT ); Mon, 22 Nov 2010 08:39:10 -0500 Received: from mx1.redhat.com ([209.132.183.28]:54871 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755129Ab0KVNjI (ORCPT ); Mon, 22 Nov 2010 08:39:08 -0500 Received: from int-mx12.intmail.prod.int.phx2.redhat.com (int-mx12.intmail.prod.int.phx2.redhat.com [10.5.11.25]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id oAMDd7lf006742 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Mon, 22 Nov 2010 08:39:07 -0500 Received: from jzupka.local.no ([10.3.112.12]) by int-mx12.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id oAMDd37X021695; Mon, 22 Nov 2010 08:39:04 -0500 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C5=BDupka?= To: ldoktor@redhat.com, kvm-autotest@redhat.com, kvm@vger.kernel.org, autotest@test.kernel.org Cc: akong@redhat.com, jzupka@redhat.com Subject: [KVM-AUTOTEST][PATCH 1/2][virtio-console] Add subtest layer and prepare to add new tests. Date: Mon, 22 Nov 2010 14:38:58 +0100 Message-Id: <1290433139-13805-1-git-send-email-jzupka@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.25 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Mon, 22 Nov 2010 13:39:18 +0000 (UTC) diff --git a/client/tests/kvm/scripts/virtio_guest.py b/client/tests/kvm/scripts/virtio_guest.py index 4862ef2..87727c7 100755 --- a/client/tests/kvm/scripts/virtio_guest.py +++ b/client/tests/kvm/scripts/virtio_guest.py @@ -3,35 +3,23 @@ """ Auxiliary script used to send data between ports on guests. -@copyright: 2008-2009 Red Hat Inc. +@copyright: 2010 Red Hat, Inc. @author: Jiri Zupka (jzupka@redhat.com) @author: Lukas Doktor (ldoktor@redhat.com) """ -#from _pydev_SimpleXMLRPCServer import fcntl - -""" -TODO: -virt.init([consoles]) # sysfs, udev, OK -virt.open(name) -virt.close(name) -virt.poll(name, eventmask, timeout) # poll.register(), poll.poll(), -return event -virt.send(name, length) # host disconnected -virt.recv(name, length) # host disconnected -virt.blocking(name, true) # true = blocking, false = nonblocking -virt.loopback(in_names, out_names, type="None") # use select/poll -""" - import threading from threading import Thread -import os, time, select, re, random, sys, array, fcntl, array, subprocess +import os, time, select, re, random, sys, array +import fcntl, array, subprocess, traceback DEBUGPATH = "/sys/kernel/debug" SYSFSPATH = "/sys/class/virtio-ports/" -class virtio_guest(): - +class VirtioGuest: + """ + Test tools of virtio_ports. + """ LOOP_NONE = 0 LOOP_POLL = 1 LOOP_SELECT = 2 @@ -125,7 +113,7 @@ class virtio_guest(): print "PASS: Init and check virtioconsole files in system." - class switch(Thread): + class Switch(Thread): """ Thread that sends data between ports. """ @@ -137,7 +125,7 @@ class virtio_guest(): @param method: Method of read/write access. @param cachesize: Block to receive and send. """ - Thread.__init__(self) + Thread.__init__(self, name="Switch") self.in_files = in_files self.out_files = out_files @@ -211,15 +199,15 @@ class virtio_guest(): def run(self): - if (self.method == virtio_guest.LOOP_POLL): + if (self.method == VirtioGuest.LOOP_POLL): self._poll_mode() - elif (self.method == virtio_guest.LOOP_SELECT): + elif (self.method == VirtioGuest.LOOP_SELECT): self._select_mode() else: self._none_mode() - class sender(Thread): + class Sender(Thread): """ Creates a thread which sends random blocks of data to dst port. """ @@ -228,7 +216,7 @@ class virtio_guest(): @param port: Destination port @param length: Length of the random data block """ - Thread.__init__(self) + Thread.__init__(self, name="Sender") self.port = port self.exit_thread = event self.data = array.array('L') @@ -296,7 +284,20 @@ class virtio_guest(): if (mask[0][1] & expected) == expected: print "PASS: Events: " + str else: - print "FAIL: Events: " + str + estr = "" + if (expected & select.POLLIN): + estr += "IN " + if (expected & select.POLLPRI): + estr += "PRI IN " + if (expected & select.POLLOUT): + estr += "OUT " + if (expected & select.POLLERR): + estr += "ERR " + if (expected & select.POLLHUP): + estr += "HUP " + if (expected & select.POLLMSG): + estr += "MSG " + print "FAIL: Events: " + str + " Expected: " + estr def blocking(self, port, mode=False): @@ -306,8 +307,7 @@ class virtio_guest(): @param port: port to set mode @param mode: False to set nonblock mode, True for block mode """ - path = self.ports[port]["path"] - fd = self.files[path] + fd = self._open([port])[0] try: fl = fcntl.fcntl(fd, fcntl.F_GETFL) @@ -336,24 +336,28 @@ class virtio_guest(): if path in self.files.keys(): descriptor = self.files[path] del self.files[path] - try: - os.close(descriptor) - except Exception as inst: - print "FAIL: Closing the file: " + str(inst) - return + if descriptor != None: + try: + os.close(descriptor) + except Exception as inst: + print "FAIL: Closing the file: " + str(inst) + return print "PASS: Close" - def open(self, in_files): + def open(self, in_file): """ Direct open devices. - @param in_files: Array of files. + @param in_file: Array of files. @return: Array of descriptors. """ - name = self.ports[in_files]["path"] + name = self.ports[in_file]["path"] try: self.files[name] = os.open(name, os.O_RDWR) + if (self.ports[in_file]["is_console"] == "yes"): + print os.system("stty -F %s raw -echo" % (name)) + print os.system("stty -F %s -a" % (name)) print "PASS: Open all filles correctly." except Exception as inst: print "%s\nFAIL: Failed open file %s" % (str(inst), name) @@ -374,7 +378,7 @@ class virtio_guest(): in_f = self._open(in_files) out_f = self._open(out_files) - s = self.switch(in_f, out_f, self.exit_thread, cachesize, mode) + s = self.Switch(in_f, out_f, self.exit_thread, cachesize, mode) s.start() self.threads.append(s) print "PASS: Start switch" @@ -412,7 +416,7 @@ class virtio_guest(): self.ports = self._get_port_status() in_f = self._open([port]) - self.threads.append(self.sender(in_f[0], self.exit_thread, length)) + self.threads.append(self.Sender(in_f[0], self.exit_thread, length)) print "PASS: Sender prepare" @@ -484,6 +488,28 @@ class virtio_guest(): (length, len(recvs))) + def clean_port(self, port, buffer=1024): + in_f = self._open([port]) + ret = select.select([in_f[0]], [], [], 1.0) + buf = "" + if ret[0]: + buf = os.read(in_f[0], buffer) + print ("PASS: Rest in socket: " + buf) + + +def is_alive(): + """ + Check is only main thread is alive and if guest react. + """ + if threading.activeCount() == 1: + print ("PASS: Guest is ok no thread alive") + else: + threads = "" + for thread in threading.enumerate(): + threads += thread.name + ", " + print ("FAIL: On guest run thread. Active thread:" + threads) + + def compile(): """ Compile virtio_guest.py to speed up. @@ -501,12 +527,19 @@ def main(): if (len(sys.argv) > 1) and (sys.argv[1] == "-c"): compile() - virt = virtio_guest() + virt = VirtioGuest() print "PASS: Start" while True: str = raw_input() - exec str + try: + exec str + except: + exc_type, exc_value, exc_traceback = sys.exc_info() + print "On Guest exception from: \n" + "".join( + traceback.format_exception(exc_type, + exc_value, + exc_traceback)) if __name__ == "__main__": diff --git a/client/tests/kvm/tests/virtio_console.py b/client/tests/kvm/tests/virtio_console.py index aa668e1..d69c0a5 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 def run_virtio_console(test, params, env): @@ -30,7 +31,194 @@ 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. + @return: Return what returned executed subtest. + @raise TestError: If collapse of test is fatal raise forward + exception from subtest. + """ + if args == None: + args = [] + try: + logging.debug("Start test %s." % function.func_name) + ret = function(*args) + logging.info(self.result_to_string((True, function.func_name, + args))) + self.result.append((True, function.func_name, args)) + 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 + if cleanup: + self.cleanup_func(*self.cleanup_args) + logging.info(self.result_to_string((False, function.func_name, + args))) + self.result.append((False, function.func_name, args)) + self.failed += 1 + if fatal: + raise + + + def is_failed(self): + """ + @return: If any of subtest not pass return True. + """ + return True if self.failed > 0 else 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] + return ("Subtest (%s(%s)): -->%s") % (result[1], sargs, + "PASS"if result[0] else "FAIL") + + + def result_to_string(self, result): + """ + @param result: Result of test. + """ + return ("Subtest (%s): -->%s") % (result[1], "PASS"if result[0] + else "FAIL") + + + def get_full_text_result(self): + """ + @return string with text form of result + """ + result = "" + for res in self.result: + result += self.result_to_string_debug(res) + "\n" + return result + + + def get_text_result(self): + """ + @return string with text form of result + """ + result = "" + for res in self.result: + result += self.result_to_string(res) + "\n" + return result + + + 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 +241,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 +273,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 +297,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 +322,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,10 +330,10 @@ 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. """ @@ -165,10 +353,10 @@ def run_virtio_console(test, params, env): 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 +374,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,7 +397,7 @@ 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. @@ -384,6 +429,21 @@ def run_virtio_console(test, params, env): time.sleep(2) + def init_guest(vm, consoles): + """ + Prepares guest, executes virtio_guest.py and initialize for testing + + @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 _on_guest(command, vm, timeout=2): """ Execute given command inside the script's main loop, indicating the vm @@ -425,29 +485,6 @@ def run_virtio_console(test, params, env): 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 +500,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 +508,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,6 +519,13 @@ 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 ; + 2xC,1xS => CC|cS|s = len(data): + break + if tmp != data: + raise error.TestFail("Incorrect data: '%s' != '%s'", + data, tmp) + _guest_exit_threads(vm, [send_port], [recv_port]) + + + 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 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;...' + @param vm: Target virtual machine [vm, session, tmp_dir]. + @param consoles: Field of virtio ports with the minimum of 2 items. + @param params: Test parameters '$console_type:$data;...' """ - logging.info("Smoke test: Tests the basic capabilities of " - "virtio_consoles.") # PREPARE for param in params.split(';'): if not param: @@ -560,121 +791,18 @@ def run_virtio_console(test, params, env): send_pt = consoles[param][0] recv_pt = consoles[param][1] - # 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): + test.do_test(topen, [vm, send_pt] , True) + test.do_test(tclose, [vm, send_pt], True) + test.do_test(tmulti_open, [vm, send_pt], True) + test.do_test(tpooling, [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) + + + + def tloopback(vm, consoles, params): """ Virtio console loopback test. @@ -682,16 +810,13 @@ def run_virtio_console(test, params, env): 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]. + @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 +855,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 +871,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, 2) 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 +903,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 +912,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]. + @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,39 +942,38 @@ 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 + 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, 2) + 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 = 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, 2)[0]: time.sleep(1) _guest_exit_threads(vm, [port], []) @@ -853,30 +983,29 @@ def run_virtio_console(test, params, env): "Test ran %fs longer which is more than one slice", _time) else: logging.debug("Test ran %fs longer", _time) - stats = process_stats(stats[1:], slice*1048576) + stats = process_stats(stats[1:], 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() + loads.start() on_guest("virt.send_loop()", vm, 2) _time = time.time() for i in range(100): stats.append(thread.idx) time.sleep(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]) on_guest("virt.exit_threads()", vm, 2) exit_event.set() thread.join() @@ -885,37 +1014,86 @@ def run_virtio_console(test, params, env): "Test ran %fs longer which is more than one slice", _time) else: logging.debug("Test ran %fs longer" % _time) - stats = process_stats(stats[1:], slice*1048576) + stats = process_stats(stats[1:], 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): + """ + 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 consoles: Consoles which should be clean. + """ + # Check if python is still alive + print "CLEANED" + match, tmp = _on_guest("is_alive()", vm, 10) + if (match == None) or (match != 0): + logging.error("Python died/is stucked/have remaining threads") + vm[1].close() + vm[1] = kvm_test_utils.wait_for_login(vm[0], 0, + float(params.get("boot_timeout", 240)), + 0, 2) + (match, data) = _on_guest("killall -9 python " + "&& echo -n PASS: python killed" + "|| echo -n PASS: python was death", + vm, 30) + if (match == None): + # killall -9 command didn't finished - python stucked + raise error.TestFail("Python is really stucked - " + "can't kill -9 it") + + on_guest("rmmod -f virtio_console && echo -n PASS: rmmod " + "|| echo -n FAIL: rmmod", vm, 10) + on_guest("modprobe virtio_console " + "&& echo -n PASS: modprobe || echo -n FAIL: modprobe", + vm, 10) + + init_guest(vm, consoles) + (match, data) = _on_guest("virt.clean_port('%s'),1024" % + consoles[0][0].name, vm, 2) + if (match == None) or (match != 0): + raise error.TestFail("Virtio-console driver is irreparably" + " blocked. Every comd end with sig KILL.") + + for ctype in consoles: + for port in ctype: + openned = port.is_open + port.clean_port() + #on_guest("virt.blocking('%s', True)" % port.name, vm, 2) + on_guest("virt.clean_port('%s'),1024" % port.name, vm, 2) + if not openned: + port.close() # 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: @@ -933,16 +1111,27 @@ def run_virtio_console(test, params, env): # 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) + init_guest(vm, consoles) + + test = SubTest() + test.set_cleanup_func(clean_ports, [vm, consoles]) + #Test Smoke + test_smoke(test, vm, consoles, tsmoke_params) + + #Test Loopback + test.do_test(tloopback, [vm, consoles, tloopback_params]) + + #Test Performance + test.do_test(tperf, [vm, consoles, tperf_params]) + + logging.info(("Summary: %d tests passed %d test failed :\n" % + (test.passed, test.failed)) + test.get_text_result()) + logging.debug(("Summary: %d tests passed %d test failed :\n" % + (test.passed, test.failed)) + test.get_full_text_result()) + + if test.is_failed(): + raise error.TestFail("Virtio_console test FAILED.") + # CLEANUP vm[1].close()