new file mode 100644
@@ -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()
new file mode 100644
@@ -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])
@@ -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)