summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Levy <alevy@redhat.com>2012-03-04 18:57:18 +0200
committerAlon Levy <alevy@redhat.com>2012-03-04 18:57:18 +0200
commit9a9d91e73da4dba70177dd01611810e028d6515b (patch)
treebe90d1fc367ed44a3347fd04d06f8c634083fb08
parentfd2e94cb5cf10127dff244ea40d20c85b26028f4 (diff)
add a non blocking version (just blocking tested so far), also qmp can use socket (untested)
-rwxr-xr-xmigrate.py159
1 files changed, 123 insertions, 36 deletions
diff --git a/migrate.py b/migrate.py
index 9cd9d99..f0d8029 100755
--- a/migrate.py
+++ b/migrate.py
@@ -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):