diff options
author | Alon Levy <alevy@redhat.com> | 2012-03-04 18:57:18 +0200 |
---|---|---|
committer | Alon Levy <alevy@redhat.com> | 2012-03-04 18:57:18 +0200 |
commit | 9a9d91e73da4dba70177dd01611810e028d6515b (patch) | |
tree | be90d1fc367ed44a3347fd04d06f8c634083fb08 | |
parent | fd2e94cb5cf10127dff244ea40d20c85b26028f4 (diff) |
add a non blocking version (just blocking tested so far), also qmp can use socket (untested)
-rwxr-xr-x | migrate.py | 159 |
1 files changed, 123 insertions, 36 deletions
@@ -15,6 +15,10 @@ Will create two temporary unix sockets in /tmp Will leave a log file, migrate_test.log, in current directory. """ +""" +uses glib event loop for timers +""" + # # start one spiceclient, have two machines (active and target), # and repeat: @@ -28,6 +32,8 @@ Will leave a log file, migrate_test.log, in current directory. # command query-status, if running good # if not listen to events until event of running +import glib + try: import qmp except: @@ -79,7 +85,9 @@ def get_args(): print "qemu = %s" % args.qemu_exec return args -def start_qemu(qemu_exec, image, spice_port, qmp_filename, incoming_port=None, extra_args=[], secure=False): +def start_qemu(qemu_exec, image, spice_port, qmp_connection, + incoming_port=None, extra_args=[], secure=False, + debug=None, verbose=False): incoming_args = [] if incoming_port: incoming_args = ("-incoming tcp::%s" % incoming_port).split() @@ -88,20 +96,28 @@ def start_qemu(qemu_exec, image, spice_port, qmp_filename, incoming_port=None, e spice_params.extend(['tls-port=%s' % spice_port, 'x509-dir=%s' % os.getcwd()]) else: spice_params.append("port=%s" % spice_port) - args = ([qemu_exec, "--enable-kvm", "-qmp", - "unix:%s,server,nowait" % qmp_filename, - "-spice", ','.join(spice_params), '-vga', 'qxl', '-device', 'qxl'] - + extra_args + incoming_args) + if ':' not in qmp_connection: + qmp_argument = "unix:%s,server,nowait" % qmp_connection + else: + host, port = qmp_connection.split(':') + qmp_argument = "socket:host=%s,port=%s,server,nowait" % (host, port) + args = ([qemu_exec, "--enable-kvm", "-qmp", qmp_argument, + '-spice', ','.join(spice_params)] + extra_args + incoming_args) if os.path.exists(image): args += ["-m", "512", "-drive", "file=%s,index=0,media=disk,cache=unsafe" % image, "-snapshot"] - print args - print ' '.join(args) + if debug: + executable = debug[0] + args = list(debug) + args + else: + executable = qemu_exec + if verbose: + print repr(args) proc = Popen(args, executable=qemu_exec, stdin=PIPE, stdout=PIPE) - while not os.path.exists(qmp_filename): + while not os.path.exists(qmp_connection): time.sleep(0.1) - proc.qmp_filename = qmp_filename - proc.qmp = qmp.QEMUMonitorProtocol(qmp_filename) + proc.qmp_connection = qmp_connection + proc.qmp = qmp.QEMUMonitorProtocol(qmp_connection) while True: try: proc.qmp.connect() @@ -112,25 +128,40 @@ def start_qemu(qemu_exec, image, spice_port, qmp_filename, incoming_port=None, e proc.incoming_port = incoming_port return proc -def start_client(client, spice_port, secure, host_subject): - cmdline = [str(client), '-h', 'localhost'] +def client_old_commandline(spice_port, secure, host_subject): + cmdline = ['-h', 'localhost'] if secure: cmdline.extend(['-s', str(spice_port), '--host-subject', host_subject, '--ca-file', os.path.join(os.getcwd(), 'ca-cert.pem')]) else: cmdline.extend(['-p', str(spice_port)]) - print cmdline - print ' '.join(cmdline) + return cmdline + +def client_url_commandline(spice_port, secure, host_subject): + if secure: + raise Exception("don't know how a spice:// works for secure connections") + return ['spice://localhost:%s' % (spice_port)] + +def start_client(client, spice_port, secure, host_subject, verbose=False): + client = str(client) + if (client.endswith('remote-viewer')): + cmdline = client_url_commandline(spice_port, secure, host_subject) + else: + cmdline = client_old_commandline(spice_port, secure, host_subject) + cmdline = [client] + cmdline + if verbose: + print cmdline + print ' '.join(cmdline) return Popen(cmdline, executable=client) -def wait_active(q, active): +def _wait_active(q, active): events = ["RESUME"] if active else ["STOP"] while True: try: ret = q.cmd("query-status") except: # ValueError - time.sleep(0.1) + yield 'sleep', (0.1) continue if ret and ret.has_key("return"): if ret["return"]["running"] == active: @@ -138,25 +169,73 @@ def wait_active(q, active): for e in q.get_events(): if e["event"] in events: break - time.sleep(0.5) + yield 'sleep', (0.5) -def wait_for_event(q, event): +def _wait_for_event(q, event): while True: for e in q.get_events(): if e["event"] == event: return - time.sleep(0.5) + yield 'sleep', (0.5) def cleanup(migrator): print "doing cleanup" migrator.close() +known_commands = { + 'wait_active': _wait_active, + 'wait_for_event': _wait_for_event +} + +def blocker(it): + for cmd, args in it: + if not hasattr(args, '__len__'): + args = (args,) + if cmd in known_commands: + blocker(known_commands[cmd](*args)) + elif cmd == 'sleep': + time.sleep(*args) + else: + import pdb; pdb.set_trace() + raise Exception('unhandled cmd %s' % cmd) + +def glib_wrap_iter(it): + def sleep_done(*args): + glib_wrap_iter(it) + return False + cmd, args = it.next() + if not hasattr(args, '__len__'): + args = (args,) + if cmd in known_commands: + return glib_wrap_iter(known_commands[cmd](*args)) + elif cmd == 'sleep': + assert(len(args) == 1) + glib.timeout_add(int(1000 * args[0]), sleep_done) + else: + raise Exception("unhandled cmd") + +def test_glib_wrap_iter(): + mainloop = glib.MainLoop() + def it(): + t = time.time() + yield 'sleep', 0.1 + t2 = time.time() + yield 'sleep', 0.3 + t3 = time.time() + print ('%2.2f, %2.2f' % (t2 - t, t3 - t2)) + mainloop.quit() + raise SystemExit + glib_wrap_iter(it()) + mainloop.run() + class Migrator(object): migration_count = 0 - def __init__(self, log, client, qemu_exec, image, monitor_files, client_count, - spice_ports, migration_port, vdagent, usbtablet, secure, host_subject): + def __init__(self, log, client, qemu_exec, image, monitor_files, client_count=1, + spice_ports=[17000, 17001], migration_port=17002, vdagent=False, + usbtablet=False, secure=False, host_subject='', + extra_args=[], sound=False): self.client = client self.log = log self.qemu_exec = qemu_exec @@ -169,13 +248,14 @@ class Migrator(object): self.usbtablet = usbtablet self.secure = secure self.host_subject = host_subject - extra_args = [] if self.vdagent: - extra_args = ['-device', 'virtio-serial', '-chardev', 'spicevmc,name=vdagent,id=vdagent', '-device', 'virtserialport,chardev=vdagent,name=com.redhat.spice.0'] + extra_args.extend(['-device', 'virtio-serial', + '-chardev', 'spicevmc,name=vdagent,id=vdagent', + '-device', 'virtserialport,chardev=vdagent,name=com.redhat.spice.0']) if self.usbtablet: extra_args.extend(['-usb', '-device', 'usb-tablet']) - # add sound (TODO - disableable) - extra_args.extend(['-device', 'intel-hda,id=sound0', '-device', 'hda-duplex,id=sound0-codec0,bus=sound0.0,cad=0']) + if sound: + extra_args.extend(['-device', 'intel-hda,id=sound0', '-device', 'hda-duplex,id=sound0-codec0,bus=sound0.0,cad=0']) self.extra_args = extra_args self.active = self.start_qemu(which=0, incoming=False) self.target = self.start_qemu(which=1, incoming=True) @@ -188,11 +268,11 @@ class Migrator(object): kw['incoming_port'] = self.migration_port return start_qemu(qemu_exec=self.qemu_exec, image=self.image, spice_port=self.spice_ports[which], - qmp_filename=self.monitor_files[which], + qmp_connection=self.monitor_files[which], extra_args=self.extra_args, secure=self.secure, **kw) - + def close(self): self.remove_monitor_files() @@ -209,14 +289,14 @@ class Migrator(object): if os.path.exists(x): os.unlink(x) - def iterate(self, wait_for_user_input=False): - wait_active(self.active.qmp, True) - wait_active(self.target.qmp, False) + def _iterate(self, wait_for_user_input=False): + yield 'wait_active', (self.active.qmp, True) + yield 'wait_active', (self.target.qmp, False) if len(self.clients) == 0: for i in range(self.client_count): self.clients.append(start_client(client=self.client, spice_port=self.spice_ports[0], secure=self.secure, host_subject=self.host_subject)) - wait_for_event(self.active.qmp, 'SPICE_INITIALIZED') + yield 'wait_for_event', (self.active.qmp, 'SPICE_INITIALIZED') if wait_for_user_input: print "waiting for Enter to start migrations" raw_input() @@ -229,15 +309,15 @@ class Migrator(object): migrate_info_arguments['port'] = self.target.spice_port self.active.qmp.cmd('client_migrate_info', migrate_info_arguments) self.active.qmp.cmd('migrate', {'uri': 'tcp:localhost:%s' % self.migration_port}) - wait_active(self.active.qmp, False) - wait_active(self.target.qmp, True) - wait_for_event(self.target.qmp, 'SPICE_CONNECTED') + yield 'wait_active', (self.active.qmp, False) + yield 'wait_active', (self.target.qmp, True) + yield 'wait_for_event', (self.target.qmp, 'SPICE_CONNECTED') dead = self.active dead.qmp.cmd("quit") dead.qmp.close() dead.wait() new_spice_port = dead.spice_port - new_qmp_filename = dead.qmp_filename + new_qmp_connection = dead.qmp_connection self.log.write("# STDOUT dead %s\n" % dead.pid) self.log.write(dead.stdout.read()) del dead @@ -247,6 +327,12 @@ class Migrator(object): print self.migration_count self.migration_count += 1 + def iterate(self, wait_for_user_input=False): + blocker(self._iterate(wait_for_user_input)) + + def start(self): + glib_wrap_iter(self._iterate(False)) + def main(): args = get_args() host_subject = None @@ -262,7 +348,8 @@ def main(): migration_port=args.migrate_port, spice_ports=[args.spice_port1, args.spice_port2], client_count=args.client_count, vdagent=(args.vdagent=='on'), usbtablet=(args.usbtablet=='on'), - secure=(args.secure=='on'), host_subject=host_subject) + secure=(args.secure=='on'), host_subject=host_subject, + extra_args = ['-vga', 'qxl', '-device', 'qxl'], sound=False) atexit.register(cleanup, migrator) #while True: for i in xrange(50): |