KVM test: Corrects virtio-console test
[autotest-zwu.git] / client / tests / kvm / scripts / virtio_console_guest.py
blobf11436ca6fe4187125461575a2575ea5dea759ac
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3 """
4 Auxiliary script used to send data between ports on guests.
6 @copyright: 2010 Red Hat, Inc.
7 @author: Jiri Zupka (jzupka@redhat.com)
8 @author: Lukas Doktor (ldoktor@redhat.com)
9 """
10 import threading
11 from threading import Thread
12 import os, time, select, re, random, sys, array
13 import fcntl, subprocess, traceback, signal
15 DEBUGPATH = "/sys/kernel/debug"
16 SYSFSPATH = "/sys/class/virtio-ports/"
17 DEVPATH = "/dev/virtio-ports/"
19 exiting = False
21 class VirtioGuest:
22 """
23 Test tools of virtio_ports.
24 """
25 LOOP_NONE = 0
26 LOOP_POLL = 1
27 LOOP_SELECT = 2
29 def __init__(self):
30 self.files = {}
31 self.exit_thread = threading.Event()
32 self.threads = []
33 self.ports = {}
34 self.poll_fds = {}
35 self.catch_signal = None
36 self.use_config = threading.Event()
39 def _readfile(self, name):
40 """
41 Read file and return content as string
43 @param name: Name of file
44 @return: Content of file as string
45 """
46 out = ""
47 try:
48 f = open(name, "r")
49 out = f.read()
50 f.close()
51 except:
52 print "FAIL: Cannot open file %s" % (name)
54 return out
57 def _get_port_status(self, in_files=None):
58 """
59 Get info about ports from kernel debugfs.
61 @param in_files: Array of input files.
62 @return: Ports dictionary of port properties
63 """
64 ports = {}
65 not_present_msg = "FAIL: There's no virtio-ports dir in debugfs"
66 if (not os.path.ismount(DEBUGPATH)):
67 os.system('mount -t debugfs none %s' % (DEBUGPATH))
68 try:
69 if not os.path.isdir('%s/virtio-ports' % (DEBUGPATH)):
70 print not_present_msg
71 except:
72 print not_present_msg
73 else:
74 viop_names = os.listdir('%s/virtio-ports' % (DEBUGPATH))
75 if (in_files != None):
76 dev_names = os.listdir('/dev')
77 rep = re.compile(r"vport[0-9]p[0-9]+")
78 dev_names = filter(lambda x: rep.match(x) != None, dev_names)
79 if len(dev_names) != len(in_files):
80 print ("FAIL: Not all ports are sucesfully inicailized"+
81 " in /dev "+
82 "only %d from %d." % (len(dev_names),
83 len(in_files)))
84 return
86 if len(viop_names) != len(in_files):
87 print ("FAIL: No all ports are sucesfully inicailized "
88 "in debugfs only %d from %d." % (len(viop_names),
89 len(in_files)))
90 return
92 for name in viop_names:
93 open_db_file = "%s/virtio-ports/%s" % (DEBUGPATH, name)
94 f = open(open_db_file, 'r')
95 port = {}
96 file = []
97 for line in iter(f):
98 file.append(line)
99 try:
100 for line in file:
101 m = re.match("(\S+): (\S+)", line)
102 port[m.group(1)] = m.group(2)
104 if (port['is_console'] == "yes"):
105 port["path"] = "/dev/hvc%s" % (port["console_vtermno"])
106 # Console works like a serialport
107 else:
108 port["path"] = "/dev/%s" % name
110 if (not os.path.exists(port['path'])):
111 print "FAIL: %s not exist" % port['path']
113 sysfspath = SYSFSPATH + name
114 if (not os.path.isdir(sysfspath)):
115 print "FAIL: %s not exist" % (sysfspath)
117 info_name = sysfspath + "/name"
118 port_name = self._readfile(info_name).strip()
119 if (port_name != port["name"]):
120 print ("FAIL: Port info not match \n%s - %s\n%s - %s" %
121 (info_name , port_name,
122 "%s/virtio-ports/%s" % (DEBUGPATH, name),
123 port["name"]))
124 dev_ppath = DEVPATH + port_name
125 if not (os.path.exists(dev_ppath)):
126 print ("FAIL: Symlink " + dev_ppath + " not exist.")
127 if not (os.path.realpath(dev_ppath) != "/dev/name"):
128 print ("FAIL: Sumlink " + dev_ppath + " not correct.")
129 except AttributeError:
130 print ("In file " + open_db_file +
131 " are incorrect data\n" + "".join(file).strip())
132 print ("FAIL: Fail file data.")
133 return
135 ports[port['name']] = port
136 f.close()
138 return ports
141 def init(self, in_files):
143 Init and check port properties.
145 self.ports = self._get_port_status(in_files)
147 if self.ports == None:
148 return
149 for item in in_files:
150 if (item[1] != self.ports[item[0]]["is_console"]):
151 print self.ports
152 print "FAIL: Host console is not like console on guest side\n"
153 return
155 print "PASS: Init and check virtioconsole files in system."
158 class Switch(Thread):
160 Thread that sends data between ports.
162 def __init__ (self, in_files, out_files, event,
163 cachesize=1024, method=0):
165 @param in_files: Array of input files.
166 @param out_files: Array of output files.
167 @param method: Method of read/write access.
168 @param cachesize: Block to receive and send.
170 Thread.__init__(self, name="Switch")
172 self.in_files = in_files
173 self.out_files = out_files
174 self.exit_thread = event
175 self.method = method
177 self.cachesize = cachesize
180 def _none_mode(self):
182 Read and write to device in blocking mode
184 data = ""
185 while not self.exit_thread.isSet():
186 data = ""
187 for desc in self.in_files:
188 data += os.read(desc, self.cachesize)
189 if data != "":
190 for desc in self.out_files:
191 os.write(desc, data)
194 def _poll_mode(self):
196 Read and write to device in polling mode.
199 pi = select.poll()
200 po = select.poll()
202 for fd in self.in_files:
203 pi.register(fd, select.POLLIN)
205 for fd in self.out_files:
206 po.register(fd, select.POLLOUT)
208 while not self.exit_thread.isSet():
209 data = ""
210 t_out = self.out_files
212 readyf = pi.poll(1.0)
213 for i in readyf:
214 data += os.read(i[0], self.cachesize)
216 if data != "":
217 while ((len(t_out) != len(readyf)) and not
218 self.exit_thread.isSet()):
219 readyf = po.poll(1.0)
220 for desc in t_out:
221 os.write(desc, data)
224 def _select_mode(self):
226 Read and write to device in selecting mode.
228 while not self.exit_thread.isSet():
229 ret = select.select(self.in_files, [], [], 1.0)
230 data = ""
231 if ret[0] != []:
232 for desc in ret[0]:
233 data += os.read(desc, self.cachesize)
234 if data != "":
235 ret = select.select([], self.out_files, [], 1.0)
236 while ((len(self.out_files) != len(ret[1])) and not
237 self.exit_thread.isSet()):
238 ret = select.select([], self.out_files, [], 1.0)
239 for desc in ret[1]:
240 os.write(desc, data)
243 def run(self):
244 if (self.method == VirtioGuest.LOOP_POLL):
245 self._poll_mode()
246 elif (self.method == VirtioGuest.LOOP_SELECT):
247 self._select_mode()
248 else:
249 self._none_mode()
252 class Sender(Thread):
254 Creates a thread which sends random blocks of data to dst port.
256 def __init__(self, port, event, length):
258 @param port: Destination port
259 @param length: Length of the random data block
261 Thread.__init__(self, name="Sender")
262 self.port = port
263 self.exit_thread = event
264 self.data = array.array('L')
265 for i in range(max(length / self.data.itemsize, 1)):
266 self.data.append(random.randrange(sys.maxint))
268 def run(self):
269 while not self.exit_thread.isSet():
270 os.write(self.port, self.data)
273 def _open(self, in_files):
275 Open devices and return array of descriptors
277 @param in_files: Files array
278 @return: Array of descriptor
280 f = []
282 for item in in_files:
283 name = self.ports[item]["path"]
284 if (name in self.files):
285 f.append(self.files[name])
286 else:
287 try:
288 self.files[name] = os.open(name, os.O_RDWR)
289 if (self.ports[item]["is_console"] == "yes"):
290 print os.system("stty -F %s raw -echo" % (name))
291 print os.system("stty -F %s -a" % (name))
292 f.append(self.files[name])
293 except Exception, inst:
294 print "FAIL: Failed to open file %s" % (name)
295 raise inst
296 return f
298 @staticmethod
299 def pollmask_to_str(mask):
301 Conver pool mast to string
303 @param mask: poll return mask
305 str = ""
306 if (mask & select.POLLIN):
307 str += "IN "
308 if (mask & select.POLLPRI):
309 str += "PRI IN "
310 if (mask & select.POLLOUT):
311 str += "OUT "
312 if (mask & select.POLLERR):
313 str += "ERR "
314 if (mask & select.POLLHUP):
315 str += "HUP "
316 if (mask & select.POLLMSG):
317 str += "MSG "
318 return str
321 def poll(self, port, expected, timeout=500):
323 Pool event from device and print event like text.
325 @param file: Device.
327 in_f = self._open([port])
329 p = select.poll()
330 p.register(in_f[0])
332 mask = p.poll(timeout)
334 maskstr = VirtioGuest.pollmask_to_str(mask[0][1])
335 if (mask[0][1] & expected) == expected:
336 print "PASS: Events: " + maskstr
337 else:
338 emaskstr = VirtioGuest.pollmask_to_str(expected)
339 print "FAIL: Events: " + maskstr + " Expected: " + emaskstr
342 def lseek(self, port, pos, how):
344 Use lseek on the device. The device is unseekable so PASS is returned
345 when lseek command fails and vice versa.
347 @param port: Name of the port
348 @param pos: Offset
349 @param how: Relativ offset os.SEEK_{SET,CUR,END}
351 fd = self._open([port])[0]
353 try:
354 os.lseek(fd, pos, how)
355 except Exception, inst:
356 if inst.errno == 29:
357 print "PASS: the lseek failed as expected"
358 else:
359 print inst
360 print "FAIL: unknown error"
361 else:
362 print "FAIL: the lseek unexpectedly passed"
365 def blocking(self, port, mode=False):
367 Set port function mode blocking/nonblocking
369 @param port: port to set mode
370 @param mode: False to set nonblock mode, True for block mode
372 fd = self._open([port])[0]
374 try:
375 fl = fcntl.fcntl(fd, fcntl.F_GETFL)
376 if not mode:
377 fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
378 else:
379 fcntl.fcntl(fd, fcntl.F_SETFL, fl & ~os.O_NONBLOCK)
381 except Exception, inst:
382 print "FAIL: Setting (non)blocking mode: " + str(inst)
383 return
385 if mode:
386 print "PASS: set to blocking mode"
387 else:
388 print "PASS: set to nonblocking mode"
391 def __call__(self, sig, frame):
393 Call function. Used for signal handle.
395 if (sig == signal.SIGIO):
396 self.sigio_handler(sig, frame)
399 def sigio_handler(self, sig, frame):
401 Handler for sigio operation.
403 @param sig: signal which call handler.
404 @param frame: frame of caller
406 if self.poll_fds:
407 p = select.poll()
408 map(p.register, self.poll_fds.keys())
410 masks = p.poll(1)
411 print masks
412 for mask in masks:
413 self.poll_fds[mask[0]][1] |= mask[1]
416 def get_sigio_poll_return(self, port):
418 Return PASS, FAIL and poll walue in string format.
420 @param port: Port to check poll information.
422 fd = self._open([port])[0]
424 maskstr = VirtioGuest.pollmask_to_str(self.poll_fds[fd][1])
425 if (self.poll_fds[fd][0] ^ self.poll_fds[fd][1]):
426 emaskstr = VirtioGuest.pollmask_to_str(self.poll_fds[fd][0])
427 print "FAIL: Events: " + maskstr + " Expected: " + emaskstr
428 else:
429 print "PASS: Events: " + maskstr
430 self.poll_fds[fd][1] = 0
433 def set_pool_want_return(self, port, poll_value):
435 Set value to static variable.
437 @param port: Port which should be set excepted mask
438 @param poll_value: Value to check sigio signal.
440 fd = self._open([port])[0]
441 self.poll_fds[fd] = [poll_value, 0]
442 print "PASS: Events: " + VirtioGuest.pollmask_to_str(poll_value)
445 def catching_signal(self):
447 return: True if should set catch signal, False if ignore signal and
448 none when configuration is not changed.
450 ret = self.catch_signal
451 self.catch_signal = None
452 return ret
455 def async(self, port, mode=True, exp_val=0):
457 Set port function mode async/sync.
459 @param port: port which should be pooled.
460 @param mode: False to set sync mode, True for sync mode.
461 @param exp_val: Value which should be pooled.
463 fd = self._open([port])[0]
465 try:
466 fcntl.fcntl(fd, fcntl.F_SETOWN, os.getpid())
467 fl = fcntl.fcntl(fd, fcntl.F_GETFL)
469 self.use_config.clear()
470 if mode:
471 fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_ASYNC)
472 self.poll_fds[fd] = [exp_val, 0]
473 self.catch_signal = True
474 else:
475 del self.poll_fds[fd]
476 fcntl.fcntl(fd, fcntl.F_SETFL, fl & ~os.O_ASYNC)
477 self.catch_signal = False
479 os.kill(os.getpid(), signal.SIGUSR1)
480 self.use_config.wait()
482 except Exception, inst:
483 print "FAIL: Setting (a)sync mode: " + str(inst)
484 return
486 if mode:
487 print "PASS: Set to async mode"
488 else:
489 print "PASS: Set to sync mode"
492 def close(self, file):
494 Close open port.
496 @param file: File to close.
498 descriptor = None
499 path = self.ports[file]["path"]
500 if path != None:
501 if path in self.files.keys():
502 descriptor = self.files[path]
503 del self.files[path]
504 if descriptor != None:
505 try:
506 os.close(descriptor)
507 except Exception, inst:
508 print "FAIL: Closing the file: " + str(inst)
509 return
510 print "PASS: Close"
513 def open(self, in_file):
515 Direct open devices.
517 @param in_file: Array of files.
518 @return: Array of descriptors.
520 name = self.ports[in_file]["path"]
521 try:
522 self.files[name] = os.open(name, os.O_RDWR)
523 if (self.ports[in_file]["is_console"] == "yes"):
524 print os.system("stty -F %s raw -echo" % (name))
525 print "PASS: Open all filles correctly."
526 except Exception, inst:
527 print "%s\nFAIL: Failed open file %s" % (str(inst), name)
530 def loopback(self, in_files, out_files, cachesize=1024, mode=LOOP_NONE):
532 Start a switch thread.
534 (There is a problem with multiple opens of a single file).
536 @param in_files: Array of input files.
537 @param out_files: Array of output files.
538 @param cachesize: Cachesize.
540 self.ports = self._get_port_status()
542 in_f = self._open(in_files)
543 out_f = self._open(out_files)
545 s = self.Switch(in_f, out_f, self.exit_thread, cachesize, mode)
546 s.start()
547 self.threads.append(s)
548 print "PASS: Start switch"
551 def exit_threads(self):
553 Function end all running data switch.
555 self.exit_thread.set()
556 for th in self.threads:
557 print "join"
558 th.join()
559 self.exit_thread.clear()
561 del self.threads[:]
562 for desc in self.files.itervalues():
563 os.close(desc)
564 self.files.clear()
565 print "PASS: All threads finished."
568 def die(self):
570 Quit consoleswitch.
572 self.exit_threads()
573 exit()
576 def send_loop_init(self, port, length):
578 Prepares the sender thread. Requires clean thread structure.
580 self.ports = self._get_port_status()
581 in_f = self._open([port])
583 self.threads.append(self.Sender(in_f[0], self.exit_thread, length))
584 print "PASS: Sender prepare"
587 def send_loop(self):
589 Start sender data transfer. Requires senderprepare run first.
591 self.threads[0].start()
592 print "PASS: Sender start"
595 def send(self, port, length=1, mode=True, is_static=False):
597 Send a data of some length
599 @param port: Port to write data
600 @param length: Length of data
601 @param mode: True = loop mode, False = one shoot mode
603 in_f = self._open([port])
605 data = ""
606 writes = 0
608 if not is_static:
609 while len(data) < length:
610 data += "%c" % random.randrange(255)
611 try:
612 writes = os.write(in_f[0], data)
613 except Exception, inst:
614 print inst
615 else:
616 while len(data) < 4096:
617 data += "%c" % random.randrange(255)
618 if mode:
619 while (writes < length):
620 try:
621 writes += os.write(in_f[0], data)
622 except Exception, inst:
623 print inst
624 if writes >= length:
625 print "PASS: Send data length %d" % writes
626 else:
627 print ("FAIL: Partial send: desired %d, transfered %d" %
628 (length, writes))
631 def recv(self, port, length=1, buffer=1024, mode=True):
633 Recv a data of some length
635 @param port: Port to write data
636 @param length: Length of data
637 @param mode: True = loop mode, False = one shoot mode
639 in_f = self._open([port])
641 recvs = ""
642 try:
643 recvs = os.read(in_f[0], buffer)
644 except Exception, inst:
645 print inst
646 if mode:
647 while (len(recvs) < length):
648 try:
649 recvs += os.read(in_f[0], buffer)
650 except Exception, inst:
651 print inst
652 if len(recvs) >= length:
653 print "PASS: Recv data length %d" % len(recvs)
654 else:
655 print ("FAIL: Partial recv: desired %d, transfered %d" %
656 (length, len(recvs)))
659 def clean_port(self, port, buffer=1024):
660 in_f = self._open([port])
661 ret = select.select([in_f[0]], [], [], 1.0)
662 buf = ""
663 if ret[0]:
664 buf = os.read(in_f[0], buffer)
665 print ("PASS: Rest in socket: ") + str(buf[:10])
668 def is_alive():
670 Check is only main thread is alive and if guest react.
672 if threading.activeCount() == 2:
673 print ("PASS: Guest is ok no thread alive")
674 else:
675 threads = ""
676 for thread in threading.enumerate():
677 threads += thread.name + ", "
678 print ("FAIL: On guest run thread. Active thread:" + threads)
681 def compile():
683 Compile virtio_console_guest.py to speed up.
685 import py_compile
686 py_compile.compile(sys.path[0] + "/virtio_console_guest.py")
687 print "PASS: compile"
688 sys.exit()
691 def guest_exit():
692 global exiting
693 exiting = True
694 os.kill(os.getpid(), signal.SIGUSR1)
697 def worker(virt):
699 Worker thread (infinite) loop of virtio_guest.
701 global exiting
702 print "PASS: Start"
704 while not exiting:
705 str = raw_input()
706 try:
707 exec str
708 except:
709 exc_type, exc_value, exc_traceback = sys.exc_info()
710 print "On Guest exception from: \n" + "".join(
711 traceback.format_exception(exc_type,
712 exc_value,
713 exc_traceback))
714 print "FAIL: Guest command exception."
717 def sigusr_handler(sig, frame):
718 pass
721 def main():
723 Main function with infinite loop to catch signal from system.
725 if (len(sys.argv) > 1) and (sys.argv[1] == "-c"):
726 compile()
728 global exiting
729 virt = VirtioGuest()
730 slave = Thread(target=worker, args=(virt, ))
731 slave.start()
732 signal.signal(signal.SIGUSR1, sigusr_handler)
733 while not exiting:
734 signal.pause()
735 catch = virt.catching_signal()
736 if catch:
737 signal.signal(signal.SIGIO, virt)
738 elif catch == False:
739 signal.signal(signal.SIGIO, signal.SIG_DFL)
740 if (catch != None):
741 virt.use_config.set()
742 print "PASS: guest_exit"
745 if __name__ == "__main__":
746 main()