OLD | NEW |
| (Empty) |
1 #!/usr/bin/python | |
2 # -*- coding: utf-8 -*- | |
3 """ | |
4 Auxiliary script used to send data between ports on guests. | |
5 | |
6 @copyright: 2008-2009 Red Hat Inc. | |
7 @author: Jiri Zupka (jzupka@redhat.com) | |
8 @author: Lukas Doktor (ldoktor@redhat.com) | |
9 """ | |
10 #from _pydev_SimpleXMLRPCServer import fcntl | |
11 | |
12 """ | |
13 TODO: | |
14 virt.init([consoles]) # sysfs, udev, OK | |
15 virt.open(name) | |
16 virt.close(name) | |
17 virt.poll(name, eventmask, timeout) # poll.register(), poll.poll(), | |
18 return event | |
19 virt.send(name, length) # host disconnected | |
20 virt.recv(name, length) # host disconnected | |
21 virt.blocking(name, true) # true = blocking, false = nonblocking | |
22 virt.loopback(in_names, out_names, type="None") # use select/poll | |
23 """ | |
24 | |
25 import threading | |
26 from threading import Thread | |
27 import os, time, select, re, random, sys, array, fcntl, array, subprocess | |
28 | |
29 DEBUGPATH = "/sys/kernel/debug" | |
30 SYSFSPATH = "/sys/class/virtio-ports/" | |
31 | |
32 | |
33 class virtio_guest(): | |
34 | |
35 LOOP_NONE = 0 | |
36 LOOP_POLL = 1 | |
37 LOOP_SELECT = 2 | |
38 | |
39 def __init__(self): | |
40 self.files = {} | |
41 self.exit_thread = threading.Event() | |
42 self.threads = [] | |
43 self.ports = {} | |
44 | |
45 | |
46 def _readfile(self, name): | |
47 """ | |
48 Read file and return content as string | |
49 | |
50 @param name: Name of file | |
51 @return: Content of file as string | |
52 """ | |
53 out = "" | |
54 try: | |
55 f = open(name, "r") | |
56 out = f.read() | |
57 f.close() | |
58 except: | |
59 print "FAIL: Cannot open file %s" % (name) | |
60 | |
61 return out | |
62 | |
63 | |
64 def _get_port_status(self): | |
65 """ | |
66 Get info about ports from kernel debugfs. | |
67 | |
68 @return: Ports dictionary of port properties | |
69 """ | |
70 ports = {} | |
71 not_present_msg = "FAIL: There's no virtio-ports dir in debugfs" | |
72 if (not os.path.ismount(DEBUGPATH)): | |
73 os.system('mount -t debugfs none %s' % (DEBUGPATH)) | |
74 try: | |
75 if not os.path.isdir('%s/virtio-ports' % (DEBUGPATH)): | |
76 print not_present_msg | |
77 except: | |
78 print not_present_msg | |
79 else: | |
80 viop_names = os.listdir('%s/virtio-ports' % (DEBUGPATH)) | |
81 for name in viop_names: | |
82 f = open("%s/virtio-ports/%s" % (DEBUGPATH, name), 'r') | |
83 port = {} | |
84 for line in iter(f): | |
85 m = re.match("(\S+): (\S+)", line) | |
86 port[m.group(1)] = m.group(2) | |
87 | |
88 if (port['is_console'] == "yes"): | |
89 port["path"] = "/dev/hvc%s" % (port["console_vtermno"]) | |
90 # Console works like a serialport | |
91 else: | |
92 port["path"] = "/dev/%s" % name | |
93 | |
94 if (not os.path.exists(port['path'])): | |
95 print "FAIL: %s not exist" % port['path'] | |
96 | |
97 sysfspath = SYSFSPATH + name | |
98 if (not os.path.isdir(sysfspath)): | |
99 print "FAIL: %s not exist" % (sysfspath) | |
100 | |
101 info_name = sysfspath + "/name" | |
102 port_name = self._readfile(info_name).strip() | |
103 if (port_name != port["name"]): | |
104 print ("FAIL: Port info not match \n%s - %s\n%s - %s" % | |
105 (info_name , port_name, | |
106 "%s/virtio-ports/%s" % (DEBUGPATH, name), | |
107 port["name"])) | |
108 | |
109 ports[port['name']] = port | |
110 f.close() | |
111 | |
112 return ports | |
113 | |
114 | |
115 def init(self, in_files): | |
116 """ | |
117 Init and check port properties. | |
118 """ | |
119 self.ports = self._get_port_status() | |
120 | |
121 for item in in_files: | |
122 if (item[1] != self.ports[item[0]]["is_console"]): | |
123 print self.ports | |
124 print "FAIL: Host console is not like console on guest side\n" | |
125 print "PASS: Init and check virtioconsole files in system." | |
126 | |
127 | |
128 class switch(Thread): | |
129 """ | |
130 Thread that sends data between ports. | |
131 """ | |
132 def __init__ (self, in_files, out_files, event, | |
133 cachesize=1024, method=0): | |
134 """ | |
135 @param in_files: Array of input files. | |
136 @param out_files: Array of output files. | |
137 @param method: Method of read/write access. | |
138 @param cachesize: Block to receive and send. | |
139 """ | |
140 Thread.__init__(self) | |
141 | |
142 self.in_files = in_files | |
143 self.out_files = out_files | |
144 self.exit_thread = event | |
145 self.method = method | |
146 | |
147 self.cachesize = cachesize | |
148 | |
149 | |
150 def _none_mode(self): | |
151 """ | |
152 Read and write to device in blocking mode | |
153 """ | |
154 data = "" | |
155 while not self.exit_thread.isSet(): | |
156 data = "" | |
157 for desc in self.in_files: | |
158 data += os.read(desc, self.cachesize) | |
159 if data != "": | |
160 for desc in self.out_files: | |
161 os.write(desc, data) | |
162 | |
163 | |
164 def _poll_mode(self): | |
165 """ | |
166 Read and write to device in polling mode. | |
167 """ | |
168 | |
169 pi = select.poll() | |
170 po = select.poll() | |
171 | |
172 for fd in self.in_files: | |
173 pi.register(fd, select.POLLIN) | |
174 | |
175 for fd in self.out_files: | |
176 po.register(fd, select.POLLOUT) | |
177 | |
178 while not self.exit_thread.isSet(): | |
179 data = "" | |
180 t_out = self.out_files | |
181 | |
182 readyf = pi.poll(1.0) | |
183 for i in readyf: | |
184 data += os.read(i[0], self.cachesize) | |
185 | |
186 if data != "": | |
187 while ((len(t_out) != len(readyf)) and not | |
188 self.exit_thread.isSet()): | |
189 readyf = po.poll(1.0) | |
190 for desc in t_out: | |
191 os.write(desc, data) | |
192 | |
193 | |
194 def _select_mode(self): | |
195 """ | |
196 Read and write to device in selecting mode. | |
197 """ | |
198 while not self.exit_thread.isSet(): | |
199 ret = select.select(self.in_files, [], [], 1.0) | |
200 data = "" | |
201 if ret[0] != []: | |
202 for desc in ret[0]: | |
203 data += os.read(desc, self.cachesize) | |
204 if data != "": | |
205 ret = select.select([], self.out_files, [], 1.0) | |
206 while ((len(self.out_files) != len(ret[1])) and not | |
207 self.exit_thread.isSet()): | |
208 ret = select.select([], self.out_files, [], 1.0) | |
209 for desc in ret[1]: | |
210 os.write(desc, data) | |
211 | |
212 | |
213 def run(self): | |
214 if (self.method == virtio_guest.LOOP_POLL): | |
215 self._poll_mode() | |
216 elif (self.method == virtio_guest.LOOP_SELECT): | |
217 self._select_mode() | |
218 else: | |
219 self._none_mode() | |
220 | |
221 | |
222 class sender(Thread): | |
223 """ | |
224 Creates a thread which sends random blocks of data to dst port. | |
225 """ | |
226 def __init__(self, port, event, length): | |
227 """ | |
228 @param port: Destination port | |
229 @param length: Length of the random data block | |
230 """ | |
231 Thread.__init__(self) | |
232 self.port = port | |
233 self.exit_thread = event | |
234 self.data = array.array('L') | |
235 for i in range(max(length / self.data.itemsize, 1)): | |
236 self.data.append(random.randrange(sys.maxint)) | |
237 | |
238 def run(self): | |
239 while not self.exit_thread.isSet(): | |
240 os.write(self.port, self.data) | |
241 | |
242 | |
243 def _open(self, in_files): | |
244 """ | |
245 Open devices and return array of descriptors | |
246 | |
247 @param in_files: Files array | |
248 @return: Array of descriptor | |
249 """ | |
250 f = [] | |
251 | |
252 for item in in_files: | |
253 name = self.ports[item]["path"] | |
254 if (name in self.files): | |
255 f.append(self.files[name]) | |
256 else: | |
257 try: | |
258 self.files[name] = os.open(name, os.O_RDWR) | |
259 if (self.ports[item]["is_console"] == "yes"): | |
260 print os.system("stty -F %s raw -echo" % (name)) | |
261 print os.system("stty -F %s -a" % (name)) | |
262 f.append(self.files[name]) | |
263 except Exception as inst: | |
264 print "FAIL: Failed to open file %s" % (name) | |
265 raise inst | |
266 return f | |
267 | |
268 | |
269 def poll(self, port, expected, timeout=500): | |
270 """ | |
271 Pool event from device and print event like text. | |
272 | |
273 @param file: Device. | |
274 """ | |
275 in_f = self._open([port]) | |
276 | |
277 p = select.poll() | |
278 p.register(in_f[0]) | |
279 | |
280 mask = p.poll(timeout) | |
281 | |
282 str = "" | |
283 if (mask[0][1] & select.POLLIN): | |
284 str += "IN " | |
285 if (mask[0][1] & select.POLLPRI): | |
286 str += "PRI IN " | |
287 if (mask[0][1] & select.POLLOUT): | |
288 str += "OUT " | |
289 if (mask[0][1] & select.POLLERR): | |
290 str += "ERR " | |
291 if (mask[0][1] & select.POLLHUP): | |
292 str += "HUP " | |
293 if (mask[0][1] & select.POLLMSG): | |
294 str += "MSG " | |
295 | |
296 if (mask[0][1] & expected) == expected: | |
297 print "PASS: Events: " + str | |
298 else: | |
299 print "FAIL: Events: " + str | |
300 | |
301 | |
302 def blocking(self, port, mode=False): | |
303 """ | |
304 Set port function mode blocking/nonblocking | |
305 | |
306 @param port: port to set mode | |
307 @param mode: False to set nonblock mode, True for block mode | |
308 """ | |
309 path = self.ports[port]["path"] | |
310 fd = self.files[path] | |
311 | |
312 try: | |
313 fl = fcntl.fcntl(fd, fcntl.F_GETFL) | |
314 if not mode: | |
315 fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) | |
316 else: | |
317 fcntl.fcntl(fd, fcntl.F_SETFL, fl & ~os.O_NONBLOCK) | |
318 | |
319 except Exception as inst: | |
320 print "FAIL: Setting (non)blocking mode: " + str(inst) | |
321 return | |
322 | |
323 print ("PASS: set blocking mode to %s mode" % | |
324 ("blocking" if mode else "nonblocking")) | |
325 | |
326 | |
327 def close(self, file): | |
328 """ | |
329 Close open port. | |
330 | |
331 @param file: File to close. | |
332 """ | |
333 descriptor = None | |
334 path = self.ports[file]["path"] | |
335 if path != None: | |
336 if path in self.files.keys(): | |
337 descriptor = self.files[path] | |
338 del self.files[path] | |
339 try: | |
340 os.close(descriptor) | |
341 except Exception as inst: | |
342 print "FAIL: Closing the file: " + str(inst) | |
343 return | |
344 print "PASS: Close" | |
345 | |
346 | |
347 def open(self, in_files): | |
348 """ | |
349 Direct open devices. | |
350 | |
351 @param in_files: Array of files. | |
352 @return: Array of descriptors. | |
353 """ | |
354 name = self.ports[in_files]["path"] | |
355 try: | |
356 self.files[name] = os.open(name, os.O_RDWR) | |
357 print "PASS: Open all filles correctly." | |
358 except Exception as inst: | |
359 print "%s\nFAIL: Failed open file %s" % (str(inst), name) | |
360 | |
361 | |
362 def loopback(self, in_files, out_files, cachesize=1024, mode=LOOP_NONE): | |
363 """ | |
364 Start a switch thread. | |
365 | |
366 (There is a problem with multiple opens of a single file). | |
367 | |
368 @param in_files: Array of input files. | |
369 @param out_files: Array of output files. | |
370 @param cachesize: Cachesize. | |
371 """ | |
372 self.ports = self._get_port_status() | |
373 | |
374 in_f = self._open(in_files) | |
375 out_f = self._open(out_files) | |
376 | |
377 s = self.switch(in_f, out_f, self.exit_thread, cachesize, mode) | |
378 s.start() | |
379 self.threads.append(s) | |
380 print "PASS: Start switch" | |
381 | |
382 | |
383 def exit_threads(self): | |
384 """ | |
385 Function end all running data switch. | |
386 """ | |
387 self.exit_thread.set() | |
388 for th in self.threads: | |
389 print "join" | |
390 th.join() | |
391 self.exit_thread.clear() | |
392 | |
393 del self.threads[:] | |
394 for desc in self.files.itervalues(): | |
395 os.close(desc) | |
396 self.files.clear() | |
397 print "PASS: All threads finished." | |
398 | |
399 | |
400 def die(self): | |
401 """ | |
402 Quit consoleswitch. | |
403 """ | |
404 self.exit_threads() | |
405 exit() | |
406 | |
407 | |
408 def send_loop_init(self, port, length): | |
409 """ | |
410 Prepares the sender thread. Requires clean thread structure. | |
411 """ | |
412 self.ports = self._get_port_status() | |
413 in_f = self._open([port]) | |
414 | |
415 self.threads.append(self.sender(in_f[0], self.exit_thread, length)) | |
416 print "PASS: Sender prepare" | |
417 | |
418 | |
419 def send_loop(self): | |
420 """ | |
421 Start sender data transfer. Requires senderprepare run first. | |
422 """ | |
423 self.threads[0].start() | |
424 print "PASS: Sender start" | |
425 | |
426 | |
427 def send(self, port, length=1, mode=True): | |
428 """ | |
429 Send a data of some length | |
430 | |
431 @param port: Port to write data | |
432 @param length: Length of data | |
433 @param mode: True = loop mode, False = one shoot mode | |
434 """ | |
435 in_f = self._open([port]) | |
436 | |
437 data = "" | |
438 while len(data) < length: | |
439 data += "%c" % random.randrange(255) | |
440 try: | |
441 writes = os.write(in_f[0], data) | |
442 except Exception as inst: | |
443 print inst | |
444 if not writes: | |
445 writes = 0 | |
446 if mode: | |
447 while (writes < length): | |
448 try: | |
449 writes += os.write(in_f[0], data) | |
450 except Exception as inst: | |
451 print inst | |
452 if writes >= length: | |
453 print "PASS: Send data length %d" % writes | |
454 else: | |
455 print ("FAIL: Partial send: desired %d, transfered %d" % | |
456 (length, writes)) | |
457 | |
458 | |
459 def recv(self, port, length=1, buffer=1024, mode=True): | |
460 """ | |
461 Recv a data of some length | |
462 | |
463 @param port: Port to write data | |
464 @param length: Length of data | |
465 @param mode: True = loop mode, False = one shoot mode | |
466 """ | |
467 in_f = self._open([port]) | |
468 | |
469 recvs = "" | |
470 try: | |
471 recvs = os.read(in_f[0], buffer) | |
472 except Exception as inst: | |
473 print inst | |
474 if mode: | |
475 while (len(recvs) < length): | |
476 try: | |
477 recvs += os.read(in_f[0], buffer) | |
478 except Exception as inst: | |
479 print inst | |
480 if len(recvs) >= length: | |
481 print "PASS: Recv data length %d" % len(recvs) | |
482 else: | |
483 print ("FAIL: Partial recv: desired %d, transfered %d" % | |
484 (length, len(recvs))) | |
485 | |
486 | |
487 def compile(): | |
488 """ | |
489 Compile virtio_guest.py to speed up. | |
490 """ | |
491 import py_compile | |
492 py_compile.compile(sys.path[0] + "/virtio_guest.py") | |
493 print "PASS: compile" | |
494 exit(0) | |
495 | |
496 | |
497 def main(): | |
498 """ | |
499 Main (infinite) loop of virtio_guest. | |
500 """ | |
501 if (len(sys.argv) > 1) and (sys.argv[1] == "-c"): | |
502 compile() | |
503 | |
504 virt = virtio_guest() | |
505 print "PASS: Start" | |
506 | |
507 while True: | |
508 str = raw_input() | |
509 exec str | |
510 | |
511 | |
512 if __name__ == "__main__": | |
513 main() | |
OLD | NEW |