2 # -*- coding: utf-8 -*-
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)
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/"
23 Test tools of virtio_ports.
31 self
.exit_thread
= threading
.Event()
35 self
.catch_signal
= None
36 self
.use_config
= threading
.Event()
39 def _readfile(self
, name
):
41 Read file and return content as string
43 @param name: Name of file
44 @return: Content of file as string
52 print "FAIL: Cannot open file %s" % (name
)
57 def _get_port_status(self
, in_files
=None):
59 Get info about ports from kernel debugfs.
61 @param in_files: Array of input files.
62 @return: Ports dictionary of port properties
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
))
69 if not os
.path
.isdir('%s/virtio-ports' % (DEBUGPATH
)):
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"+
82 "only %d from %d." % (len(dev_names
),
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
),
92 for name
in viop_names
:
93 open_db_file
= "%s/virtio-ports/%s" % (DEBUGPATH
, name
)
94 f
= open(open_db_file
, 'r')
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
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
),
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.")
135 ports
[port
['name']] = port
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:
149 for item
in in_files
:
150 if (item
[1] != self
.ports
[item
[0]]["is_console"]):
152 print "FAIL: Host console is not like console on guest side\n"
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
177 self
.cachesize
= cachesize
180 def _none_mode(self
):
182 Read and write to device in blocking mode
185 while not self
.exit_thread
.isSet():
187 for desc
in self
.in_files
:
188 data
+= os
.read(desc
, self
.cachesize
)
190 for desc
in self
.out_files
:
194 def _poll_mode(self
):
196 Read and write to device in polling mode.
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():
210 t_out
= self
.out_files
212 readyf
= pi
.poll(1.0)
214 data
+= os
.read(i
[0], self
.cachesize
)
217 while ((len(t_out
) != len(readyf
)) and not
218 self
.exit_thread
.isSet()):
219 readyf
= po
.poll(1.0)
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)
233 data
+= os
.read(desc
, self
.cachesize
)
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)
244 if (self
.method
== VirtioGuest
.LOOP_POLL
):
246 elif (self
.method
== VirtioGuest
.LOOP_SELECT
):
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")
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
))
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
282 for item
in in_files
:
283 name
= self
.ports
[item
]["path"]
284 if (name
in self
.files
):
285 f
.append(self
.files
[name
])
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
)
299 def pollmask_to_str(mask
):
301 Conver pool mast to string
303 @param mask: poll return mask
306 if (mask
& select
.POLLIN
):
308 if (mask
& select
.POLLPRI
):
310 if (mask
& select
.POLLOUT
):
312 if (mask
& select
.POLLERR
):
314 if (mask
& select
.POLLHUP
):
316 if (mask
& select
.POLLMSG
):
321 def poll(self
, port
, expected
, timeout
=500):
323 Pool event from device and print event like text.
327 in_f
= self
._open
([port
])
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
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
349 @param how: Relativ offset os.SEEK_{SET,CUR,END}
351 fd
= self
._open
([port
])[0]
354 os
.lseek(fd
, pos
, how
)
355 except Exception, inst
:
357 print "PASS: the lseek failed as expected"
360 print "FAIL: unknown error"
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]
375 fl
= fcntl
.fcntl(fd
, fcntl
.F_GETFL
)
377 fcntl
.fcntl(fd
, fcntl
.F_SETFL
, fl | os
.O_NONBLOCK
)
379 fcntl
.fcntl(fd
, fcntl
.F_SETFL
, fl
& ~os
.O_NONBLOCK
)
381 except Exception, inst
:
382 print "FAIL: Setting (non)blocking mode: " + str(inst
)
386 print "PASS: set to blocking mode"
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
408 map(p
.register
, self
.poll_fds
.keys())
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
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
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]
466 fcntl
.fcntl(fd
, fcntl
.F_SETOWN
, os
.getpid())
467 fl
= fcntl
.fcntl(fd
, fcntl
.F_GETFL
)
469 self
.use_config
.clear()
471 fcntl
.fcntl(fd
, fcntl
.F_SETFL
, fl | os
.O_ASYNC
)
472 self
.poll_fds
[fd
] = [exp_val
, 0]
473 self
.catch_signal
= True
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
)
487 print "PASS: Set to async mode"
489 print "PASS: Set to sync mode"
492 def close(self
, file):
496 @param file: File to close.
499 path
= self
.ports
[file]["path"]
501 if path
in self
.files
.keys():
502 descriptor
= self
.files
[path
]
504 if descriptor
!= None:
507 except Exception, inst
:
508 print "FAIL: Closing the file: " + str(inst
)
513 def open(self
, in_file
):
517 @param in_file: Array of files.
518 @return: Array of descriptors.
520 name
= self
.ports
[in_file
]["path"]
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
)
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
:
559 self
.exit_thread
.clear()
562 for desc
in self
.files
.itervalues():
565 print "PASS: All threads finished."
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"
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
])
609 while len(data
) < length
:
610 data
+= "%c" % random
.randrange(255)
612 writes
= os
.write(in_f
[0], data
)
613 except Exception, inst
:
616 while len(data
) < 4096:
617 data
+= "%c" % random
.randrange(255)
619 while (writes
< length
):
621 writes
+= os
.write(in_f
[0], data
)
622 except Exception, inst
:
625 print "PASS: Send data length %d" % writes
627 print ("FAIL: Partial send: desired %d, transfered %d" %
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
])
643 recvs
= os
.read(in_f
[0], buffer)
644 except Exception, inst
:
647 while (len(recvs
) < length
):
649 recvs
+= os
.read(in_f
[0], buffer)
650 except Exception, inst
:
652 if len(recvs
) >= length
:
653 print "PASS: Recv data length %d" % len(recvs
)
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)
664 buf
= os
.read(in_f
[0], buffer)
665 print ("PASS: Rest in socket: ") + str(buf
[:10])
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")
676 for thread
in threading
.enumerate():
677 threads
+= thread
.name
+ ", "
678 print ("FAIL: On guest run thread. Active thread:" + threads
)
683 Compile virtio_console_guest.py to speed up.
686 py_compile
.compile(sys
.path
[0] + "/virtio_console_guest.py")
687 print "PASS: compile"
694 os
.kill(os
.getpid(), signal
.SIGUSR1
)
699 Worker thread (infinite) loop of virtio_guest.
709 exc_type
, exc_value
, exc_traceback
= sys
.exc_info()
710 print "On Guest exception from: \n" + "".join(
711 traceback
.format_exception(exc_type
,
714 print "FAIL: Guest command exception."
717 def sigusr_handler(sig
, frame
):
723 Main function with infinite loop to catch signal from system.
725 if (len(sys
.argv
) > 1) and (sys
.argv
[1] == "-c"):
730 slave
= Thread(target
=worker
, args
=(virt
, ))
732 signal
.signal(signal
.SIGUSR1
, sigusr_handler
)
735 catch
= virt
.catching_signal()
737 signal
.signal(signal
.SIGIO
, virt
)
739 signal
.signal(signal
.SIGIO
, signal
.SIG_DFL
)
741 virt
.use_config
.set()
742 print "PASS: guest_exit"
745 if __name__
== "__main__":