All of lore.kernel.org
 help / color / mirror / Atom feed
From: Lukas Doktor <ldoktor@redhat.com>
To: ldoktor@redhat.com, jzupka@redhat.com, autotest@test.kernel.org,
	kvm@vger.kernel.org, akong@redhat.com
Subject: [PATCH] NEW virtio_console test
Date: Fri, 20 Aug 2010 15:40:12 +0200	[thread overview]
Message-ID: <1282311612-3282-2-git-send-email-ldoktor@redhat.com> (raw)
In-Reply-To: <1282311612-3282-1-git-send-email-ldoktor@redhat.com>

Signed-off-by: Lukas Doktor <ldoktor@redhat.com>
---
 client/tests/kvm/scripts/consoleSwitch.py |  217 +++++++++
 client/tests/kvm/tests/virtio_console.py  |  730 +++++++++++++++++++++++++++++
 client/tests/kvm/tests_base.cfg.sample    |    7 +
 3 files changed, 954 insertions(+), 0 deletions(-)
 create mode 100644 client/tests/kvm/scripts/consoleSwitch.py
 create mode 100644 client/tests/kvm/tests/virtio_console.py

diff --git a/client/tests/kvm/scripts/consoleSwitch.py b/client/tests/kvm/scripts/consoleSwitch.py
new file mode 100644
index 0000000..814388f
--- /dev/null
+++ b/client/tests/kvm/scripts/consoleSwitch.py
@@ -0,0 +1,217 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+"""
+Auxiliary script used to send data betwen ports on guests.
+
+@copyright: 2008-2009 Red Hat Inc.
+@author: Jiri Zupka (jzupka@redhat.com)
+@author: Lukas Doktor (ldoktor@redhat.com)
+"""
+
+
+import threading
+from threading import Thread
+import os,time,select,re,random,sys,array
+
+
+files = dict()
+ev = threading.Event()
+threads = []
+
+DEBUGPATH="/sys/kernel/debug"
+
+
+
+class switch(Thread):
+    """
+    Class create thread which send data between ports
+    """
+    def __init__ (self,exitevent,in_files,out_files,cachesize=1):
+        """
+        @param exitevent: event to end switch
+        @param in_files: array of input files
+        @param out_files: array of output files
+        @param cachesize: block to receive and send
+        """
+        Thread.__init__(self)
+        
+        self.in_files = in_files
+        self.out_files = out_files
+
+        self.cachesize = cachesize
+        self.exitevent = exitevent
+        
+        
+    def run(self):
+        while not self.exitevent.isSet():    
+            #TODO: Why select causes trouble? :-(
+            #ret = select.select(self.in_files,[],[],1.0)
+            data = ""
+            #if not ret[0] == []:
+            for desc in self.in_files:
+                data += os.read(desc,self.cachesize)
+            for desc in self.out_files:
+                os.write(desc,data)
+
+class sender(Thread):
+    """
+    Class creates thread which sends random block of data to the destination 
+    port
+    """
+    def __init__(self, port, length):
+        """
+        @param port: destination port
+        @param length: length of the random data block
+        """
+        Thread.__init__(self)
+        self.port = port
+        self.data = array.array('L')
+        for i in range(max(length / self.data.itemsize,1)):
+            self.data.append(random.randrange(sys.maxint))
+
+    def run(self):
+        while True:
+            os.write(self.port, self.data)
+        del threads[:]
+                 
+def getPortStatus():
+    """
+    Function get info about ports from kernel debugfs.
+    
+    @return: ports dictionary of port properties
+    """
+    ports = dict()
+    
+    if (not os.path.ismount(DEBUGPATH)):
+        os.system('mount -t debugfs none %s' % (DEBUGPATH))
+    try:
+        if not os.path.isdir('%s/virtio-ports' % (DEBUGPATH)):
+            print "FAIL: Not exist virtio-ports in debug fs"
+    except:
+        print "FAIL: virtio-ports not exist"
+    else:
+        viop_names = os.listdir('%s/virtio-ports' % (DEBUGPATH))
+        for name in viop_names:
+            f = open("%s/virtio-ports/%s" % (DEBUGPATH,name),'r')
+            port = dict()
+            for line in iter(f):
+                m = re.match("(\S+): (\S+)",line)
+                port[m.group(1)] = m.group(2)
+                
+            if (port['is_console'] == "yes"):
+                port["path"] = "/dev/hvc%s" % (port["console_vtermno"])
+                #Console work like serialport
+            else:
+                port["path"] = "/dev/%s" % (name)
+            ports[port['name']] = port
+            f.close()
+         
+    return ports    
+                    
+                    
+def opendevice(in_files,ports):
+    """
+    Open devices and return array of descriptors
+    
+    @param in_files: files array
+    @return: array of descriptor
+    """
+    
+    f = []
+    
+    for item in in_files:
+        name = ports[item[0]]["path"]
+        if (not item[1] == ports[item[0]]["is_console"]):
+            print ports
+            print "FAIL: Host console is not like console on guest side\n"
+            
+        if (name in files):
+            f.append(files[name])
+        else:
+            try:          
+                files[name] = os.open(name, os.O_RDWR)
+                if (ports[item[0]]["is_console"] == "yes"):
+                    print os.system("stty -F %s raw -echo" % (ports[item[0]]["path"]))
+                    print os.system("stty -F %s -a" % (ports[item[0]]["path"]))
+                f.append(files[name])
+            except Exception as inst:
+                print "FAIL: Failed open file %s" % (name)
+                raise inst
+    return f
+                 
+                 
+def startswitch(in_files,out_files,cachesize=1):
+    """
+    Function starts switch thread (because there is problem with multiple open 
+    of one files).
+    
+    @param in_files: array of input files
+    @param out_files: array of output files
+    @param cachesize: cachesize 
+    """
+    
+    
+    ports = getPortStatus()
+    
+    in_f = opendevice(in_files,ports)
+    out_f = opendevice(out_files,ports)
+    
+   
+    s = switch(ev,in_f,out_f,cachesize)
+    s.start()
+    threads.append(s)
+    print "PASS: Start switch"
+    
+def endswitchs():
+    """
+    Function end all running data switch.
+    """
+    ev.set()
+    for th in threads:
+        print "join"
+        th.join(3.0)
+    ev.clear()
+    
+    del threads[:]
+    print "PASS: End switch"
+
+def die():
+    """
+    Quit consoleswitch.
+    """
+    for desc in files.itervalues():
+        os.close(desc)
+    exit(0)
+
+def senderprepare(port, length):
+    """
+    Prepares the sender thread. Requires clean thread structure.
+    """
+    del threads[:]
+    ports = getPortStatus()
+    in_f = opendevice([port],ports)
+
+    threads.append(sender(in_f[0], length))
+    print "PASS: Sender prepare"
+
+def senderstart():
+    """
+    Start sender data transfer. Requires senderprepare run firs.
+    """
+    threads[0].start()
+    print "PASS: Sender start"
+
+def main():
+    """
+    Main (infinite) loop of consoleswitch.
+    """
+    print "PASS: Start"
+    end = False
+    while not end:
+        str = raw_input()
+        exec str
+            
+
+
+if __name__ == "__main__":
+    main()
diff --git a/client/tests/kvm/tests/virtio_console.py b/client/tests/kvm/tests/virtio_console.py
new file mode 100644
index 0000000..f05500d
--- /dev/null
+++ b/client/tests/kvm/tests/virtio_console.py
@@ -0,0 +1,730 @@
+import logging, time
+from autotest_lib.client.common_lib import error
+import kvm_subprocess, kvm_test_utils, kvm_utils, kvm_preprocessing
+import socket, random, array, sys, os, tempfile, shutil, threading, select
+from threading import Thread
+from collections import deque
+import re
+
+
+def run_virtio_console(test, params, env):
+    """
+    KVM virtio_console test
+    
+    @param test: kvm test object
+    @param params: Dictionary with the test parameters
+    @param env: Dictionary with test environment
+    """ 
+    class th_send(Thread):
+        """
+        Random data sender thread
+        """
+        def __init__(self, port, length, buffers, blocklen=32):
+            """
+            @param port: destination port
+            @param length: amount of data we want to send
+            @param buffers: buffers for the control data (FIFOs)
+            @param blocklen: block length
+            """
+            Thread.__init__(self)
+            self.ExitState = True
+            self.port = port[0]
+            self.length = length
+            self.buffers = buffers
+            self.blocklen = blocklen
+        def run(self):
+            logging.debug("th_send %s: run" % self.getName())
+            idx = 0
+            while idx < self.length:
+                ret = select.select([], [self.port], [], 1.0)
+                if ret:
+                    # Generate blocklen of random data add them to the FIFO
+                    # and send tham over virtio_console
+                    buf = ""
+                    for i in range(min(self.blocklen, self.length-idx)):
+                        ch = "%c" % random.randrange(255)
+                        buf += ch
+                        for buffer in self.buffers:
+                            buffer.append(ch)
+                    idx += len(buf)
+                    self.port.sendall(buf)
+            logging.debug("th_send %s: exit(%d)" % (self.getName(), idx))
+            if idx >= self.length:
+                self.ExitState = False
+    class th_send_loop(Thread):
+        """
+        Send data in the loop until the exit event is set
+        """
+        def __init__(self, port, data, event):
+            """
+            @param port: destination port
+            @param data: the data intend to be send in a loop
+            @param event: exit event
+            """
+            Thread.__init__(self)
+            self.port = port
+            self.data = data
+            self.exitevent = event
+            self.idx = 0
+        def run(self):
+            logging.debug("th_send_loop %s: run" % self.getName())
+            while not self.exitevent.isSet():
+                self.idx += self.port.send(self.data)
+            logging.debug("th_send_loop %s: exit(%d)" % (self.getName(), \
+                                                        self.idx))
+
+    class th_recv(Thread):
+        """
+        Random data reciever/checker thread
+        """
+        def __init__(self, port, buffer, length, blocklen=32):
+            """
+            @param port: source port
+            @param buffer: control data buffer (FIFO)
+            @param length: amount of data we want to receive
+            @param blocklen: block length
+            """
+            Thread.__init__(self)
+            self.ExitState = True
+            self.port = port[0]
+            self.buffer = buffer
+            self.length = length
+            self.blocklen = blocklen
+        def run(self):
+            logging.debug("th_recv %s: run" % self.getName())
+            idx = 0
+            while idx < self.length:
+                ret = select.select([self.port], [], [], 1.0)
+                if ret:
+                    buf = self.port.recv(self.blocklen)
+                    if buf:
+                        # Compare the recvd data with the control data
+                        for ch in buf:
+                            if not ch == self.buffer.popleft():
+                                error.TestFail("th_recv: incorrect data")
+                    idx += len(buf)
+            logging.debug("th_recv %s: exit(%d)" % (self.getName(), idx))
+            if (idx >= self.length) and (len(self.buffer) == 0):
+                self.ExitState = False
+
+    class th_recv_null(Thread):
+        """
+        Recieves data and throw them away
+        """
+        def __init__(self, port, event, blocklen=32):
+            """
+            @param port: data source port
+            @param event: exit event
+            @param blocklen: block length
+            """
+            Thread.__init__(self)
+            self.port = port[0]
+            self._port_timeout = self.port.gettimeout()
+            self.port.settimeout(0.1)
+            self.exitevent = event
+            self.blocklen = blocklen
+            self.idx = 0
+        def run(self):
+            logging.debug("th_recv_null %s: run" % self.getName())
+            while not self.exitevent.isSet():
+                # Workaround, it didn't work with select :-/
+                try:
+                    self.idx += len(self.port.recv(self.blocklen))
+                except socket.timeout:
+                    pass
+            self.port.settimeout(self._port_timeout)
+            logging.debug("th_recv_null %s: exit(%d)" % (self.getName(), \
+                                                        self.idx))
+                
+    seqTest = threading.Lock();
+
+    class averageCpuLoad():
+        """
+        Get average cpu load between start and getLoad
+        """
+        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 try if we can calc system load.
+            try:
+                f = open("/proc/stat", "r")
+                tmp = f.readlines(200)
+                f.close()
+            except:
+                self.log_entry(severity['CRIT'], "Something went terribly" \
+                                    "wrong when trying to open /proc/stat")
+                error.TestFail("averageCpuLoad: Error reading /proc/stat")
+                # 200 bytes should be enough because the information we need 
+                # ist 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.startTime = time.time();
+            self._get_cpu_load()
+                
+        def getLoad(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 averageProcessCpuLoad():
+        """
+        Get average process cpu load between start and getLoad
+        """
+        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 try if we can calc system load.
+            try:
+                f = open("/proc/%d/stat" % (pid), "r")
+                line = f.readline()
+                f.close()
+            except:
+                self.log_entry(severity['CRIT'],\
+                     "Something went terribly wrong when" + \
+                     "trying to open /proc/%d/stat" % (pid))
+                error.TestFail("averageProcessCpuLoad: 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.startTime = time.time();
+            self._get_cpu_load(self.pid)
+                
+        def getLoad(self):
+            """
+            Get and reset CPU usage
+            @return: 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
+        
+        @param stats: list of measured data
+        """
+        if not stats:
+            return None
+        for i in range((len(stats)-1),0,-1):
+            stats[i] = stats[i] - stats[i-1]
+            stats[i] /= scale
+        stats[0] /= scale
+        stats = sorted(stats)
+        return stats
+        
+    def _start_consoleSwitch(vm, timeout=2):
+        """
+        Execute consoleSwitch.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("Starting consoleSwitch.py on guest %s", vm[0].name)
+        vm[1].sendline("python /tmp/consoleSwitch.py")
+        (match, data) = vm[1].read_until_last_line_matches(["PASS:", "FAIL:"],
+                                                             timeout)
+        if match == 1 or match is None:
+            raise error.TestFail("Command consoleSwitch.py on guest %s failed."\
+                                 "\nreturn code: %s\n output:\n%s" \
+                                 % (vm[0].name, match, data))
+            
+    def _execute_consoleSwitch(command, vm, timeout=2):
+        """
+        Execute given command inside the script's main loop, indicating the vm
+        the command was executed on.
+    
+        @param command: Command that will be executed.
+        @param vm: Informations about the guest
+        @param timeout: Timeout used to verify expected output.
+    
+        @return: Tuple (match index, data)
+        """
+        logging.debug("Executing '%s' on consoleSwitch.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:"],
+                                                             timeout)
+        if match == 1 or match is None:
+            raise error.TestFail("Failed to execute '%s' on consoleSwitch.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 _vm_create(no_console=3,no_serialport=3):
+        """
+        Creates the VM and connects the specified number of consoles and serial
+        ports
+
+        @param no_console: number of desired virtconsoles
+        @param no_serialport: number of desired virtserialports
+        @return tupple with (guest information, consoles information)
+            guest informations = [vm, session, tmp_dir]
+            consoles informations = [consoles[], serialports[]]
+        """
+        consoles = []
+        serialports = []
+        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):
+            params['extra_params'] +=\
+                    " -chardev socket,path=%s/%d,id=%d,server,nowait"\
+                    % (tmp_dir, i, i)
+            params['extra_params'] += " -device virtconsole,chardev=%d,"\
+                                      "name=org.fedoraproject.console.%d,id=c%d"\
+                                      % (i,i,i)
+
+        for i in  range(no_console, no_console + no_serialport):
+            params['extra_params'] +=\
+                    " -chardev socket,path=%s/%d,id=%d,server,nowait"\
+                    % (tmp_dir, i, i)
+            params['extra_params'] += " -device virtserialport,chardev=%d,"\
+                                      "name=org.fedoraproject.data.%d,id=p%d"\
+                                      % (i,i,i)
+
+
+        logging.debug("Booting first guest %s", params.get("main_vm"))
+        kvm_preprocessing.preprocess_vm(test, params, env, 
+                                        params.get("main_vm"))
+        
+        
+        vm = kvm_utils.env_get_vm(env, params.get("main_vm"))
+        
+        session = kvm_test_utils.wait_for_login(vm, 0,
+                                         float(params.get("boot_timeout", 240)),
+                                         0, 2)
+
+        # 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, \
+                    "org.fedoraproject.console.%d" % i, \
+                    "yes"])
+        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, \
+                    "org.fedoraproject.data.%d" %i, \
+                    "no"])
+
+        return [vm, session, tmp_dir], [consoles, serialports]
+
+    def test_smoke(vm, consoles, params):
+        """
+        Creates loopback on the vm machine between providen ports[>=2] and 
+        sends the data
+
+        @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;...'
+        """
+        # 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]
+            _start_consoleSwitch(vm, 10.0)
+
+            # TEST
+            _execute_consoleSwitch('startswitch([%s], [%s])' % (
+                            str(send_pt[1:3]),
+                            str(recv_pt[1:3])
+                            ), vm, 2.0)
+
+            send_pt[0].sendall(data)
+            d = socket_readall(recv_pt[0], 1.0, len(data))
+            if data != d:
+                raise error.TestFail("test_smoke: Error, recieved data"\
+                                     " doesn't match the original")
+            else:
+                logging.info("test_smoke: data = %s" % d)
+
+
+            vm[1].sendline("die()")
+
+    def test_loopback(vm, consoles, params):
+        """
+        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 those data
+
+        @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, multiple recievers allowed.
+                '$source_console_type@buffer_length:
+                 $destination_console_type1@buffer_length:...:
+                 $loopback_buffer_length;...'
+        """
+        # PREPARE
+        for param in params.split(';'):
+            if not param:
+                continue
+            logging.info("test_loopback: PARAMS: %s" % param)
+            param = param.split(':')
+            idx_serialport = 0
+            idx_console = 0
+            buf_len = []
+            if (param[0].startswith('console')):
+                send_pt = consoles[0][idx_console]
+                idx_console += 1
+            else:
+                send_pt = consoles[1][idx_serialport]
+                idx_serialport += 1
+            if (len(param[0].split('@')) == 2):
+                buf_len.append(int(param[0].split('@')[1]))
+            else:
+                buf_len.append(32)
+            recv_pts = []
+            for parm in param[1:]:
+                if (parm.isdigit()):
+                    buf_len.append(int(parm))
+                    break   # buf_len is the last portion of param
+                if (parm.startswith('console')):
+                    recv_pts.append(consoles[0][idx_console])
+                    idx_console += 1
+                else:
+                    recv_pts.append(consoles[1][idx_serialport])
+                    idx_serialport += 1
+                if (len(parm[0].split('@')) == 2):
+                    buf_len.append(int(parm[0].split('@')[1]))
+                else:
+                    buf_len.append(32)
+            # There must be sum(idx_*) consoles + last item as loopback buf_len
+            if len(buf_len) == (idx_console + idx_serialport):
+                buf_len.append(32)
+
+            if len(recv_pts) == 0:
+                raise error.TestFail("test_loopback: incorrect recv consoles"
+                                     "definition")
+            threads = []
+            buffers = []
+            for i in range(0, len(recv_pts)):
+                buffers.append(deque())
+
+            _start_consoleSwitch(vm, 10.0)
+            tmp = str(recv_pts[0][1:3])
+            for recv_pt in recv_pts[1:]:
+                tmp += ", " + str(recv_pt[1:3])
+            _execute_consoleSwitch('startswitch([%s], [%s], %d)' % (
+                            str(send_pt[1:3]), tmp, buf_len[-1]
+                            ), vm, 2.0)
+
+            # TEST
+            thread = th_send(send_pt, 1048576, buffers, buf_len[0])
+            thread.start()
+            threads.append(thread)
+
+            for i in range(len(recv_pts)):
+                thread = th_recv(recv_pts[i], buffers[i], 1048576, 
+                                 buf_len[i+1])
+                thread.start()
+                threads.append(thread)
+
+            death = False
+            # Send+recv threads, DL 60s
+            for i in range(60):
+                for thread in threads:
+                    if not thread.is_alive():
+                        if thread.ExitState:
+                            error.TestFail("test_loopback send/recv thread" \
+                                            " failed")
+                        death = True
+                if death:
+                    break
+                tmp = ""
+                for buf in buffers:
+                    tmp += str(len(buf)) + ", "
+                logging.debug("test_loopback: buffer length (%s)" % (tmp[:-2]))
+                time.sleep(1)
+
+            if not death:
+                raise error.TestFail("test_loopback send/recv timeout")
+            # at this point at least one thread died. It should be the send one.
+    
+            # Wait for recv threads to finish their's work
+            for i in range(60):
+                death = True
+                for thread in threads:
+                    if thread.is_alive():
+                        death = False
+                # There are no living threads
+                if death:
+                    break
+                tmp = ""
+                for buf in buffers:
+                    tmp += str(len(buf)) + ", "
+                logging.debug("test_loopback: buffer length (%s)" % (tmp[:-2]))
+                time.sleep(1)
+
+            for thread in threads:
+                if thread.ExitState:
+                    error.TestFail("test_loopback recv thread failed")
+
+            # At least one thread is still alive
+            if not death:
+                raise error.TestFail("test_loopback recv timeout")
+
+            vm[1].sendline("die()")
+
+    def test_perf(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 params: test parameters: 
+                '$console_type@buffer_length:$test_duration;...'
+        """
+        # PREPARE
+        for param in params.split(';'):
+            if not param:
+                continue
+            logging.info("test_perf: PARAMS:%s" % param)
+            param = param.split(':')
+            if len(param) > 1 and param[1].isdigit():
+                duration = float(param[1])
+            else:
+                duration = 30.0
+            param = param[0].split('@')
+            if len(param) > 1 and param[1].isdigit():
+                buf_len = int(param[1])
+            else:
+                buf_len = 32
+            if param[0] == "serialport":
+                port = consoles[1][0]
+            else:
+                port = consoles[0][0]
+            data = array.array("L")
+            for i in range(max((buf_len / data.itemsize), 1)):
+                data.append(random.randrange(sys.maxint))
+
+            ev = threading.Event()
+            thread = th_send_loop(port[0], data.tostring(), ev)
+        
+            _start_consoleSwitch(vm, 10.0)
+
+            _execute_consoleSwitch('startswitch([%s], [], %d)' % (
+                            str(port[1:3]),
+                            buf_len,
+                            ), vm, 2.0)
+
+            # TEST
+            # Host -> Guest
+            load = []
+            stats = array.array('f', [])
+            slice = float(duration)/100
+            load.append(averageCpuLoad())
+            load.append(averageProcessCpuLoad(os.getpid(), 'autotest'))
+            load.append(averageProcessCpuLoad(vm[0].get_pid(), 'VM'))
+            for ld in load:
+                ld.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([load[1].getLoad(), load[2].getLoad()], \
+                            load[0].getLoad())
+            ev.set()
+            thread.join()
+            if (_time > slice):
+                logging.error(
+                    "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*1024*1024)
+            logging.info("Host->Guest [mb/s] min/med/max = %.3f/%.3f/%.3f" \
+                        % (stats[0], stats[len(stats)/2], stats[-1]))
+            time.sleep(5)
+            vm[1].sendline("die()")
+
+            # Guest -> Host
+            _start_consoleSwitch(vm, 10.0)
+            _execute_consoleSwitch('senderprepare(%s, %d)' % (
+                                    str(port[1:3]),
+                                    buf_len,
+                                    ), vm, 10)
+            stats = array.array('f', [])
+            ev.clear()
+            thread = th_recv_null(port, ev, buf_len)
+            thread.start()
+            # reset load measures
+            for ld in load:
+                ld.getLoad()
+            _execute_consoleSwitch('senderstart()', vm, 2)
+            _time = time.time()
+            for i in range(100):
+                stats.append(thread.idx)
+                time.sleep(slice)
+            _time = time.time() - _time - duration
+            print_load([load[1].getLoad(), load[2].getLoad()], \
+                            load[0].getLoad())
+            vm[1].sendline("die()")
+            time.sleep(5)
+            ev.set()
+            thread.join()
+            if (_time > slice): # Deviation is higher than 1 slice
+                logging.error(
+                    "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*1024*1024)
+            logging.info("Guest->Host [mb/s] min/med/max = %.3f/%.3f/%.3f" \
+                        % (stats[0], stats[len(stats)/2], stats[-1]))
+            for ld in load:
+                del(ld)
+
+
+    # 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', '')
+    
+    no_serialports = 0
+    no_consoles = 0
+    # consoles required for Smoke test
+    if (test_smoke_params.count('serialport')):
+        no_serialports = max(2, no_serialports)
+    if (test_smoke_params.count('console')):
+        no_consoles = max(2, no_consoles)
+    # consoles required for Loopback test
+    for param in test_loopback_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')):
+        no_serialports = max(1, no_serialports)
+    if (test_perf_params.count('console')):
+        no_consoles = max(1, no_consoles)
+
+    vm, consoles = _vm_create(no_consoles, no_serialports)
+    
+    # Copy allocator.py into guests
+    pwd = os.path.join(os.environ['AUTODIR'],'tests/kvm')
+    vksmd_src = os.path.join(pwd, "scripts/consoleSwitch.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)
+    
+    # ACTUAL TESTING
+    test_smoke(vm, consoles, test_smoke_params)
+    test_loopback(vm, consoles, test_loopback_params)
+    test_perf(vm, consoles, test_perf_params)
+    
+    
+    # CLEANUP
+    vm[1].close()
+    vm[0].destroy(gracefully = False)
+    shutil.rmtree(vm[2])
diff --git a/client/tests/kvm/tests_base.cfg.sample b/client/tests/kvm/tests_base.cfg.sample
index cb727c4..2773ea2 100644
--- a/client/tests/kvm/tests_base.cfg.sample
+++ b/client/tests/kvm/tests_base.cfg.sample
@@ -380,6 +380,13 @@ variants:
                 ksm_mode = "parallel"
     - iofuzz:
         type = iofuzz
+        
+    - virtio_console:        
+        vms = ''
+        type = virtio_console
+        virtio_console_smoke = "serialport;console:Custom data"
+        virtio_console_loopback = "serialport:serialport;serialport@1024:serialport@32:console@1024:console@8:16"
+        virtio_console_perf = "serialport;serialport@1000000:120;console@1024:60"
 
     # This unit test module is for older branches of KVM that use the
     # kvmctl test harness (such as the code shipped with RHEL 5.x)
-- 
1.7.2.1

  reply	other threads:[~2010-08-20 13:40 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-08-20 13:40 [KVM-autotest] virtio_console test Lukas Doktor
2010-08-20 13:40 ` Lukas Doktor [this message]
2010-08-20 14:12 ` Lukáš Doktor
2010-08-23  7:53   ` Amit Shah
2010-08-23 13:20     ` Lukáš Doktor
2010-08-23 13:42       ` Amit Shah

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1282311612-3282-2-git-send-email-ldoktor@redhat.com \
    --to=ldoktor@redhat.com \
    --cc=akong@redhat.com \
    --cc=autotest@test.kernel.org \
    --cc=jzupka@redhat.com \
    --cc=kvm@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.