summaryrefslogtreecommitdiff
path: root/tests/migrate.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/migrate.py')
-rwxr-xr-xtests/migrate.py218
1 files changed, 149 insertions, 69 deletions
diff --git a/tests/migrate.py b/tests/migrate.py
index 9a3cef47..5feba5f9 100755
--- a/tests/migrate.py
+++ b/tests/migrate.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
"""
Spice Migration test
@@ -8,7 +8,7 @@ VGA mode since it will just be SeaBIOS).
Dependencies:
either qmp in python path or running with spice and qemu side by side:
-qemu/QMP/qmp.py
+qemu/python/qemu/qmp.py
spice/tests/migrate.py
Will create two temporary unix sockets in /tmp
@@ -16,11 +16,11 @@ Will leave a log file, migrate_test.log, in current directory.
"""
#
-# start one spiceclient, have two machines (active and target),
+# start one spice client, have two machines (active and target),
# and repeat:
# active wait until it's active
# active client_migrate_info
-# active migrate tcp:localhost:9000
+# active migrate tcp:$hostname:9000
# _wait for event of quit
# active stop, active<->passive
#
@@ -32,11 +32,11 @@ try:
import qmp
except:
import sys
- sys.path.append("../../qemu/QMP")
+ sys.path.append("../../qemu/python/qemu/")
try:
import qmp
except:
- print "can't find qmp"
+ print("can't find qmp")
raise SystemExit
import sys
from subprocess import Popen, PIPE
@@ -47,6 +47,13 @@ import datetime
import atexit
import argparse
+# python3 does not have raw_input
+if sys.version_info[0] == 3:
+ raw_input = input
+
+def run_shell_command(cmd):
+ return os.popen(cmd).read().strip()
+
def get_args():
parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('--qmp1', dest='qmp1', default='/tmp/migrate_test.1.qmp')
@@ -54,33 +61,59 @@ def get_args():
parser.add_argument('--spice_port1', dest='spice_port1', type=int, default=5911)
parser.add_argument('--spice_port2', dest='spice_port2', type=int, default=6911)
parser.add_argument('--migrate_port', dest='migrate_port', type=int, default=8000)
- parser.add_argument('--client_count', dest='client_count', type=int, default=1)
- parser.add_argument('--qemu', dest='qemu', default='../../qemu/x86_64-softmmu/qemu-system-x86_64')
+ parser.add_argument('--qemu', dest='qemu', default='qemu-system-x86_64')
parser.add_argument('--log_filename', dest='log_filename', default='migrate.log')
parser.add_argument('--image', dest='image', default='')
- parser.add_argument('--client', dest='client', default='spicy', choices=['spicec', 'spicy'])
- parser.add_argument('--vdagent', choices=['on', 'off'], default='on')
+ parser.add_argument("--hostname", dest='hostname', default='localhost',
+ help="Set hostname used in migration message (default: localhost")
+ parser.add_argument('--client', dest='client', default='none', choices=['spicy', 'remote-viewer', 'none'],
+ help="Automatically lunch one of supported clients or none (default)")
+ parser.add_argument('--vdagent', dest="vdagent", action='store_true', default=False,
+ help="Append options for agent's virtserialport")
+ parser.add_argument('--wait-user-input', dest="wait_user_input", action='store_true', default=False,
+ help="Wait user's input to start migration test")
+ parser.add_argument('--wait-user-connect', dest="wait_user_connect", action='store_true', default=False,
+ help="Wait spice client to connect to move to next step of migration (default False)")
+ parser.add_argument('--count', dest='counter', type=int, default=100,
+ help="Number of migrations to run (set 0 for infinite)")
+ parser.add_argument('--seamless', dest="seamless_migration", action='store_true', default=False,
+ help="Enable seamless-migration support")
args = parser.parse_args(sys.argv[1:])
if os.path.exists(args.qemu):
args.qemu_exec = args.qemu
else:
- args.qemu_exec = os.popen("which %s" % args.qemu).read().strip()
+ args.qemu_exec = run_shell_command("which %s" % args.qemu)
if not os.path.exists(args.qemu_exec):
- print "qemu not found (qemu = %r)" % args.qemu_exec
+ print("qemu not found (qemu = %r)" % args.qemu_exec)
sys.exit(1)
return args
-def start_qemu(qemu_exec, image, spice_port, qmp_filename, incoming_port=None, extra_args=[]):
- incoming_args = []
+def start_qemu(qemu_exec, seamless_migration, image, spice_port, qmp_filename, incoming_port=None, with_agent=False):
+ seamless_option = "on" if seamless_migration else "off"
+ args = [
+ qemu_exec,
+ "-qmp", f"unix:{qmp_filename},server,nowait",
+ "-spice", f"seamless-migration={seamless_option},disable-ticketing,port={spice_port}"
+ ]
if incoming_port:
- incoming_args = ("-incoming tcp::%s" % incoming_port).split()
- args = ([qemu_exec, "-qmp", "unix:%s,server,nowait" % qmp_filename,
- "-spice", "disable-ticketing,port=%s" % spice_port]
- + incoming_args + extra_args)
+ args += (f"-incoming tcp::{incoming_port}").split()
+
+ if with_agent:
+ args += [
+ '-device', 'virtio-serial',
+ '-chardev', 'spicevmc,name=vdagent,id=vdagent',
+ '-device', 'virtserialport,chardev=vdagent,name=com.redhat.spice.0'
+ ]
+
if os.path.exists(image):
- args += ["-m", "512", "-drive",
- "file=%s,index=0,media=disk,cache=unsafe" % image, "-snapshot"]
+ args += [
+ "-m", "512",
+ "-enable-kvm",
+ "-drive", f"file={image},index=0,media=disk,cache=writeback"
+ ]
+
proc = Popen(args, executable=qemu_exec, stdin=PIPE, stdout=PIPE)
+
while not os.path.exists(qmp_filename):
time.sleep(0.1)
proc.qmp_filename = qmp_filename
@@ -89,31 +122,30 @@ def start_qemu(qemu_exec, image, spice_port, qmp_filename, incoming_port=None, e
try:
proc.qmp.connect()
break
- except socket.error, err:
+ except socket.error:
pass
proc.spice_port = spice_port
proc.incoming_port = incoming_port
return proc
-def start_client(client, spice_port):
- return Popen(("%(client)s -h localhost -p %(port)d" % dict(port=spice_port,
- client=client)).split(), executable=client)
+def start_client(client, hostname, spice_port):
+ client_cmd = f"spicy --uri spice://{hostname}:{spice_port}"
+ if client == "remote-viewer":
+ client_cmd = f"remote-viewer spice://{hostname}:{spice_port}"
+
+ return Popen(client_cmd.split(), executable=client)
def wait_active(q, active):
- events = ["RESUME"] if active else ["STOP"]
while True:
try:
ret = q.cmd("query-status")
+ if ret["return"]["running"] == active:
+ break
except:
# ValueError
time.sleep(0.1)
continue
- if ret and ret.has_key("return"):
- if ret["return"]["running"] == active:
- break
- for e in q.get_events():
- if e["event"] in events:
- break
+
time.sleep(0.5)
def wait_for_event(q, event):
@@ -124,33 +156,44 @@ def wait_for_event(q, event):
time.sleep(0.5)
def cleanup(migrator):
- print "doing cleanup"
+ print("doing cleanup")
migrator.close()
+def remove_image_file(filename):
+ run_shell_command('rm -f %s' % filename)
+
class Migrator(object):
migration_count = 0
- def __init__(self, log, client, qemu_exec, image, monitor_files, client_count,
- spice_ports, migration_port, vdagent):
- self.client = client
+ def __init__(self, log, client, qemu_exec, image, monitor_files,
+ spice_ports, migration_port, vdagent, hostname, seamless_migration):
+ self.client = client if client != "none" else None
self.log = log
self.qemu_exec = qemu_exec
self.image = image
self.migration_port = migration_port
- self.client_count = client_count
self.monitor_files = monitor_files
self.spice_ports = spice_ports
self.vdagent = vdagent
- 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']
- self.active = start_qemu(qemu_exec=qemu_exec, image=image, spice_port=spice_ports[0],
- qmp_filename=monitor_files[0], extra_args=extra_args)
- self.target = start_qemu(qemu_exec=qemu_exec, image=image, spice_port=spice_ports[1],
- qmp_filename=monitor_files[1], incoming_port=migration_port)
+ self.hostname = hostname
+ self.seamless_migration = seamless_migration
+
+ self.active = start_qemu(qemu_exec=qemu_exec,
+ image=image,
+ spice_port=spice_ports[0],
+ qmp_filename=monitor_files[0],
+ seamless_migration=self.seamless_migration,
+ with_agent=self.vdagent)
+ self.target = start_qemu(qemu_exec=qemu_exec,
+ image=image,
+ spice_port=spice_ports[1],
+ qmp_filename=monitor_files[1],
+ seamless_migration=self.seamless_migration,
+ with_agent=self.vdagent,
+ incoming_port=migration_port)
self.remove_monitor_files()
- self.clients = []
+ self.connected_client = None
def close(self):
self.remove_monitor_files()
@@ -158,7 +201,7 @@ class Migrator(object):
def kill_qemu(self):
for p in [self.active, self.target]:
- print "killing and waiting for qemu pid %s" % p.pid
+ print("killing and waiting for qemu pid %s" % p.pid)
p.kill()
p.wait()
@@ -167,52 +210,89 @@ class Migrator(object):
if os.path.exists(x):
os.unlink(x)
- def iterate(self, wait_for_user_input=False):
+ def iterate(self, wait_for_user_input=False, wait_user_connect=False):
wait_active(self.active.qmp, True)
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]))
- wait_for_event(self.active.qmp, 'SPICE_INITIALIZED')
+ if not self.connected_client:
+ if self.client:
+ self.connected_client = start_client(client=self.client,
+ hostname=self.hostname,
+ spice_port=self.spice_ports[0])
+
if wait_for_user_input:
- print "waiting for Enter to start migrations"
+ print("waiting for Enter to start migrations")
raw_input()
- self.active.qmp.cmd('client_migrate_info', {'protocol':'spice',
- 'hostname':'localhost', 'port':self.target.spice_port})
- self.active.qmp.cmd('migrate', {'uri': 'tcp:localhost:%s' % self.migration_port})
+
+ # Tester can launch its own client or we wait start_client() to connect
+ if self.connected_client or wait_user_connect:
+ wait_for_event(self.active.qmp, 'SPICE_INITIALIZED')
+
+ self.active.qmp.cmd('client_migrate_info', {
+ 'protocol' : 'spice',
+ 'hostname' : self.hostname,
+ 'port' : self.target.spice_port
+ })
+ self.active.qmp.cmd('migrate', {
+ 'uri': f'tcp:localhost:self.migration_port',
+ 'uri': f'tcp:{self.hostname}:{self.migration_port}'
+ })
wait_active(self.active.qmp, False)
wait_active(self.target.qmp, True)
- wait_for_event(self.target.qmp, 'SPICE_CONNECTED')
+
+ if self.connected_client or wait_user_connect:
+ 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
- self.log.write("# STDOUT dead %s\n" % dead.pid)
- self.log.write(dead.stdout.read())
+
+ outstr = dead.stdout.read()
+ if outstr:
+ outstr = outstr.decode(encoding='utf-8', errors='ignore')
+ self.log.write("# STDOUT dead %s\n" % dead.pid)
+ self.log.write(outstr)
+
del dead
self.active = self.target
self.target = start_qemu(spice_port=new_spice_port,
- qemu_exec=self.qemu_exec, image=self.image,
- qmp_filename=new_qmp_filename,
- incoming_port=self.migration_port)
- print self.migration_count
+ qemu_exec=self.qemu_exec,
+ image=self.image,
+ qmp_filename=new_qmp_filename,
+ seamless_migration=self.seamless_migration,
+ with_agent=self.vdagent,
+ incoming_port=self.migration_port)
self.migration_count += 1
+ print(self.migration_count)
def main():
args = get_args()
- print "log file %s" % args.log_filename
+ print("log file %s" % args.log_filename)
log = open(args.log_filename, "a+")
log.write("# "+str(datetime.datetime.now())+"\n")
- migrator = Migrator(client=args.client, qemu_exec=args.qemu_exec,
- image=args.image, log=log, monitor_files=[args.qmp1, args.qmp2],
- migration_port=args.migrate_port, spice_ports=[args.spice_port1,
- args.spice_port2], client_count=args.client_count, vdagent=(args.vdagent=='on'))
+ newimage = run_shell_command("mktemp --dry-run /tmp/migrate_XXXXXX.qcow2")
+ qemu_img = run_shell_command("dirname %s" % args.qemu_exec) + '/qemu-img'
+ run_shell_command('%s create -f qcow2 -b %s %s' % (qemu_img, args.image, newimage))
+ print('using new image %s' % newimage)
+ migrator = Migrator(client=args.client,
+ qemu_exec=args.qemu_exec,
+ image=newimage,
+ log=log,
+ monitor_files=[args.qmp1, args.qmp2],
+ migration_port=args.migrate_port,
+ spice_ports=[args.spice_port1, args.spice_port2],
+ vdagent=args.vdagent,
+ seamless_migration=args.seamless_migration,
+ hostname=args.hostname)
atexit.register(cleanup, migrator)
- while True:
- migrator.iterate()
+ atexit.register(remove_image_file, newimage)
+ counter = 0
+ while args.counter == 0 or counter < args.counter:
+ migrator.iterate(wait_for_user_input=args.wait_user_input,
+ wait_user_connect=args.wait_user_connect)
+ counter += 1
if __name__ == '__main__':
main()