| Index: client/tests/kvm/scripts/virtio_guest.py
|
| diff --git a/client/tests/kvm/scripts/virtio_guest.py b/client/tests/kvm/scripts/virtio_guest.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..4862ef2b920674e3ca4de6af2ceda036efc810ae
|
| --- /dev/null
|
| +++ b/client/tests/kvm/scripts/virtio_guest.py
|
| @@ -0,0 +1,513 @@
|
| +#!/usr/bin/python
|
| +# -*- coding: utf-8 -*-
|
| +"""
|
| +Auxiliary script used to send data between ports on guests.
|
| +
|
| +@copyright: 2008-2009 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
|
| +
|
| +DEBUGPATH = "/sys/kernel/debug"
|
| +SYSFSPATH = "/sys/class/virtio-ports/"
|
| +
|
| +
|
| +class virtio_guest():
|
| +
|
| + LOOP_NONE = 0
|
| + LOOP_POLL = 1
|
| + LOOP_SELECT = 2
|
| +
|
| + def __init__(self):
|
| + self.files = {}
|
| + self.exit_thread = threading.Event()
|
| + self.threads = []
|
| + self.ports = {}
|
| +
|
| +
|
| + def _readfile(self, name):
|
| + """
|
| + Read file and return content as string
|
| +
|
| + @param name: Name of file
|
| + @return: Content of file as string
|
| + """
|
| + out = ""
|
| + try:
|
| + f = open(name, "r")
|
| + out = f.read()
|
| + f.close()
|
| + except:
|
| + print "FAIL: Cannot open file %s" % (name)
|
| +
|
| + return out
|
| +
|
| +
|
| + def _get_port_status(self):
|
| + """
|
| + Get info about ports from kernel debugfs.
|
| +
|
| + @return: Ports dictionary of port properties
|
| + """
|
| + ports = {}
|
| + not_present_msg = "FAIL: There's no virtio-ports dir in debugfs"
|
| + 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 not_present_msg
|
| + except:
|
| + print not_present_msg
|
| + else:
|
| + viop_names = os.listdir('%s/virtio-ports' % (DEBUGPATH))
|
| + for name in viop_names:
|
| + f = open("%s/virtio-ports/%s" % (DEBUGPATH, name), 'r')
|
| + port = {}
|
| + 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 works like a serialport
|
| + else:
|
| + port["path"] = "/dev/%s" % name
|
| +
|
| + if (not os.path.exists(port['path'])):
|
| + print "FAIL: %s not exist" % port['path']
|
| +
|
| + sysfspath = SYSFSPATH + name
|
| + if (not os.path.isdir(sysfspath)):
|
| + print "FAIL: %s not exist" % (sysfspath)
|
| +
|
| + info_name = sysfspath + "/name"
|
| + port_name = self._readfile(info_name).strip()
|
| + if (port_name != port["name"]):
|
| + print ("FAIL: Port info not match \n%s - %s\n%s - %s" %
|
| + (info_name , port_name,
|
| + "%s/virtio-ports/%s" % (DEBUGPATH, name),
|
| + port["name"]))
|
| +
|
| + ports[port['name']] = port
|
| + f.close()
|
| +
|
| + return ports
|
| +
|
| +
|
| + def init(self, in_files):
|
| + """
|
| + Init and check port properties.
|
| + """
|
| + self.ports = self._get_port_status()
|
| +
|
| + for item in in_files:
|
| + if (item[1] != self.ports[item[0]]["is_console"]):
|
| + print self.ports
|
| + print "FAIL: Host console is not like console on guest side\n"
|
| + print "PASS: Init and check virtioconsole files in system."
|
| +
|
| +
|
| + class switch(Thread):
|
| + """
|
| + Thread that sends data between ports.
|
| + """
|
| + def __init__ (self, in_files, out_files, event,
|
| + cachesize=1024, method=0):
|
| + """
|
| + @param in_files: Array of input files.
|
| + @param out_files: Array of output files.
|
| + @param method: Method of read/write access.
|
| + @param cachesize: Block to receive and send.
|
| + """
|
| + Thread.__init__(self)
|
| +
|
| + self.in_files = in_files
|
| + self.out_files = out_files
|
| + self.exit_thread = event
|
| + self.method = method
|
| +
|
| + self.cachesize = cachesize
|
| +
|
| +
|
| + def _none_mode(self):
|
| + """
|
| + Read and write to device in blocking mode
|
| + """
|
| + data = ""
|
| + while not self.exit_thread.isSet():
|
| + data = ""
|
| + for desc in self.in_files:
|
| + data += os.read(desc, self.cachesize)
|
| + if data != "":
|
| + for desc in self.out_files:
|
| + os.write(desc, data)
|
| +
|
| +
|
| + def _poll_mode(self):
|
| + """
|
| + Read and write to device in polling mode.
|
| + """
|
| +
|
| + pi = select.poll()
|
| + po = select.poll()
|
| +
|
| + for fd in self.in_files:
|
| + pi.register(fd, select.POLLIN)
|
| +
|
| + for fd in self.out_files:
|
| + po.register(fd, select.POLLOUT)
|
| +
|
| + while not self.exit_thread.isSet():
|
| + data = ""
|
| + t_out = self.out_files
|
| +
|
| + readyf = pi.poll(1.0)
|
| + for i in readyf:
|
| + data += os.read(i[0], self.cachesize)
|
| +
|
| + if data != "":
|
| + while ((len(t_out) != len(readyf)) and not
|
| + self.exit_thread.isSet()):
|
| + readyf = po.poll(1.0)
|
| + for desc in t_out:
|
| + os.write(desc, data)
|
| +
|
| +
|
| + def _select_mode(self):
|
| + """
|
| + Read and write to device in selecting mode.
|
| + """
|
| + while not self.exit_thread.isSet():
|
| + ret = select.select(self.in_files, [], [], 1.0)
|
| + data = ""
|
| + if ret[0] != []:
|
| + for desc in ret[0]:
|
| + data += os.read(desc, self.cachesize)
|
| + if data != "":
|
| + ret = select.select([], self.out_files, [], 1.0)
|
| + while ((len(self.out_files) != len(ret[1])) and not
|
| + self.exit_thread.isSet()):
|
| + ret = select.select([], self.out_files, [], 1.0)
|
| + for desc in ret[1]:
|
| + os.write(desc, data)
|
| +
|
| +
|
| + def run(self):
|
| + if (self.method == virtio_guest.LOOP_POLL):
|
| + self._poll_mode()
|
| + elif (self.method == virtio_guest.LOOP_SELECT):
|
| + self._select_mode()
|
| + else:
|
| + self._none_mode()
|
| +
|
| +
|
| + class sender(Thread):
|
| + """
|
| + Creates a thread which sends random blocks of data to dst port.
|
| + """
|
| + def __init__(self, port, event, length):
|
| + """
|
| + @param port: Destination port
|
| + @param length: Length of the random data block
|
| + """
|
| + Thread.__init__(self)
|
| + self.port = port
|
| + self.exit_thread = event
|
| + 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 not self.exit_thread.isSet():
|
| + os.write(self.port, self.data)
|
| +
|
| +
|
| + def _open(self, in_files):
|
| + """
|
| + Open devices and return array of descriptors
|
| +
|
| + @param in_files: Files array
|
| + @return: Array of descriptor
|
| + """
|
| + f = []
|
| +
|
| + for item in in_files:
|
| + name = self.ports[item]["path"]
|
| + if (name in self.files):
|
| + f.append(self.files[name])
|
| + else:
|
| + try:
|
| + self.files[name] = os.open(name, os.O_RDWR)
|
| + if (self.ports[item]["is_console"] == "yes"):
|
| + print os.system("stty -F %s raw -echo" % (name))
|
| + print os.system("stty -F %s -a" % (name))
|
| + f.append(self.files[name])
|
| + except Exception as inst:
|
| + print "FAIL: Failed to open file %s" % (name)
|
| + raise inst
|
| + return f
|
| +
|
| +
|
| + def poll(self, port, expected, timeout=500):
|
| + """
|
| + Pool event from device and print event like text.
|
| +
|
| + @param file: Device.
|
| + """
|
| + in_f = self._open([port])
|
| +
|
| + p = select.poll()
|
| + p.register(in_f[0])
|
| +
|
| + mask = p.poll(timeout)
|
| +
|
| + str = ""
|
| + if (mask[0][1] & select.POLLIN):
|
| + str += "IN "
|
| + if (mask[0][1] & select.POLLPRI):
|
| + str += "PRI IN "
|
| + if (mask[0][1] & select.POLLOUT):
|
| + str += "OUT "
|
| + if (mask[0][1] & select.POLLERR):
|
| + str += "ERR "
|
| + if (mask[0][1] & select.POLLHUP):
|
| + str += "HUP "
|
| + if (mask[0][1] & select.POLLMSG):
|
| + str += "MSG "
|
| +
|
| + if (mask[0][1] & expected) == expected:
|
| + print "PASS: Events: " + str
|
| + else:
|
| + print "FAIL: Events: " + str
|
| +
|
| +
|
| + def blocking(self, port, mode=False):
|
| + """
|
| + Set port function mode blocking/nonblocking
|
| +
|
| + @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]
|
| +
|
| + try:
|
| + fl = fcntl.fcntl(fd, fcntl.F_GETFL)
|
| + if not mode:
|
| + fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
|
| + else:
|
| + fcntl.fcntl(fd, fcntl.F_SETFL, fl & ~os.O_NONBLOCK)
|
| +
|
| + except Exception as inst:
|
| + print "FAIL: Setting (non)blocking mode: " + str(inst)
|
| + return
|
| +
|
| + print ("PASS: set blocking mode to %s mode" %
|
| + ("blocking" if mode else "nonblocking"))
|
| +
|
| +
|
| + def close(self, file):
|
| + """
|
| + Close open port.
|
| +
|
| + @param file: File to close.
|
| + """
|
| + descriptor = None
|
| + path = self.ports[file]["path"]
|
| + if path != None:
|
| + 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
|
| + print "PASS: Close"
|
| +
|
| +
|
| + def open(self, in_files):
|
| + """
|
| + Direct open devices.
|
| +
|
| + @param in_files: Array of files.
|
| + @return: Array of descriptors.
|
| + """
|
| + name = self.ports[in_files]["path"]
|
| + try:
|
| + self.files[name] = os.open(name, os.O_RDWR)
|
| + print "PASS: Open all filles correctly."
|
| + except Exception as inst:
|
| + print "%s\nFAIL: Failed open file %s" % (str(inst), name)
|
| +
|
| +
|
| + def loopback(self, in_files, out_files, cachesize=1024, mode=LOOP_NONE):
|
| + """
|
| + Start a switch thread.
|
| +
|
| + (There is a problem with multiple opens of a single file).
|
| +
|
| + @param in_files: Array of input files.
|
| + @param out_files: Array of output files.
|
| + @param cachesize: Cachesize.
|
| + """
|
| + self.ports = self._get_port_status()
|
| +
|
| + 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.start()
|
| + self.threads.append(s)
|
| + print "PASS: Start switch"
|
| +
|
| +
|
| + def exit_threads(self):
|
| + """
|
| + Function end all running data switch.
|
| + """
|
| + self.exit_thread.set()
|
| + for th in self.threads:
|
| + print "join"
|
| + th.join()
|
| + self.exit_thread.clear()
|
| +
|
| + del self.threads[:]
|
| + for desc in self.files.itervalues():
|
| + os.close(desc)
|
| + self.files.clear()
|
| + print "PASS: All threads finished."
|
| +
|
| +
|
| + def die(self):
|
| + """
|
| + Quit consoleswitch.
|
| + """
|
| + self.exit_threads()
|
| + exit()
|
| +
|
| +
|
| + def send_loop_init(self, port, length):
|
| + """
|
| + Prepares the sender thread. Requires clean thread structure.
|
| + """
|
| + self.ports = self._get_port_status()
|
| + in_f = self._open([port])
|
| +
|
| + self.threads.append(self.sender(in_f[0], self.exit_thread, length))
|
| + print "PASS: Sender prepare"
|
| +
|
| +
|
| + def send_loop(self):
|
| + """
|
| + Start sender data transfer. Requires senderprepare run first.
|
| + """
|
| + self.threads[0].start()
|
| + print "PASS: Sender start"
|
| +
|
| +
|
| + def send(self, port, length=1, mode=True):
|
| + """
|
| + Send a data of some length
|
| +
|
| + @param port: Port to write data
|
| + @param length: Length of data
|
| + @param mode: True = loop mode, False = one shoot mode
|
| + """
|
| + in_f = self._open([port])
|
| +
|
| + data = ""
|
| + while len(data) < length:
|
| + data += "%c" % random.randrange(255)
|
| + try:
|
| + writes = os.write(in_f[0], data)
|
| + except Exception as inst:
|
| + print inst
|
| + if not writes:
|
| + writes = 0
|
| + if mode:
|
| + while (writes < length):
|
| + try:
|
| + writes += os.write(in_f[0], data)
|
| + except Exception as inst:
|
| + print inst
|
| + if writes >= length:
|
| + print "PASS: Send data length %d" % writes
|
| + else:
|
| + print ("FAIL: Partial send: desired %d, transfered %d" %
|
| + (length, writes))
|
| +
|
| +
|
| + def recv(self, port, length=1, buffer=1024, mode=True):
|
| + """
|
| + Recv a data of some length
|
| +
|
| + @param port: Port to write data
|
| + @param length: Length of data
|
| + @param mode: True = loop mode, False = one shoot mode
|
| + """
|
| + in_f = self._open([port])
|
| +
|
| + recvs = ""
|
| + try:
|
| + recvs = os.read(in_f[0], buffer)
|
| + except Exception as inst:
|
| + print inst
|
| + if mode:
|
| + while (len(recvs) < length):
|
| + try:
|
| + recvs += os.read(in_f[0], buffer)
|
| + except Exception as inst:
|
| + print inst
|
| + if len(recvs) >= length:
|
| + print "PASS: Recv data length %d" % len(recvs)
|
| + else:
|
| + print ("FAIL: Partial recv: desired %d, transfered %d" %
|
| + (length, len(recvs)))
|
| +
|
| +
|
| +def compile():
|
| + """
|
| + Compile virtio_guest.py to speed up.
|
| + """
|
| + import py_compile
|
| + py_compile.compile(sys.path[0] + "/virtio_guest.py")
|
| + print "PASS: compile"
|
| + exit(0)
|
| +
|
| +
|
| +def main():
|
| + """
|
| + Main (infinite) loop of virtio_guest.
|
| + """
|
| + if (len(sys.argv) > 1) and (sys.argv[1] == "-c"):
|
| + compile()
|
| +
|
| + virt = virtio_guest()
|
| + print "PASS: Start"
|
| +
|
| + while True:
|
| + str = raw_input()
|
| + exec str
|
| +
|
| +
|
| +if __name__ == "__main__":
|
| + main()
|
|
|