summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Levy <alevy@redhat.com>2011-07-14 17:32:41 +0300
committerAlon Levy <alevy@redhat.com>2011-07-14 17:32:41 +0300
commit8d979278000400598519c11996fecf928c584151 (patch)
tree2105bae38374ac6a9abe8f78ba70d5328836b11e
initial
-rwxr-xr-xspice2665
1 files changed, 665 insertions, 0 deletions
diff --git a/spice2 b/spice2
new file mode 100755
index 0000000..d76d6f3
--- /dev/null
+++ b/spice2
@@ -0,0 +1,665 @@
+#!/usr/bin/python
+
+import signal
+import os
+import sys
+import subprocess
+import argparse
+import atexit
+import socket
+import datetime
+import re
+import time
+import shutil
+
+try:
+ import guestfs
+ import hivex
+except:
+ print "missing guestfs or hivex"
+ guestfs = None
+
+HOME = os.environ['HOME']
+################################################################################
+qemu_exec_name = "qemu-system-x86_64"
+images = {
+ 'windevel': '/store/images/windbg_winxp.img',
+ 'winxp': '/store/images/winxp_sp3_qxl_after_cirrus.img',
+ 'win7': '/home/alon/images2/win7x86_qxl_tests.img',
+ 'kindle': '/home/alon/images2/win7_kindle.img',
+ 'win2008r2': '/media/PassportExt4/images/win2008r2_qxl.qcow2',
+ 'f14ccid': '/media/PassportExt4/images/F14_CCID.testing.qcow2',
+ 'winxp.inst': '/store/images/winxp_installation_test.qcow2',
+}
+
+spicy_exe = os.path.join(HOME, 'spice/upstream/bin/spicy')
+spicec_exe = os.path.join(HOME, 'spice/upstream/bin/spicec')
+spicy_teuf_exe = os.path.join(HOME, 'src/spice_upstream/teuf-spice-gtk/gtk/spicy')
+qemus = {'rhel6':'rhel6', 'upstream':'upstream'}
+qemu_default = 'upstream'
+
+defaults = dict(
+ win7=dict(
+ qemu='upstream',
+ args='--port 8888 --clients 1 --tablet --guestdebug 2 --qxldebug 1 --windbg-client 9000'.split()
+ ),
+ windevel=dict(
+ qemu='rhel6',
+ args='--port 6666 --clients 1 --windbg-server 9000'.split()
+ ),
+ f14ccid=dict(
+ qemu='upstream',
+ args='--port 9999 --clients 1 --tablet --guestdebug 4 --qxldebug 1 --smartcard'.split()
+ ),
+)
+
+spice_src_path = os.path.join(HOME, 'src/spice_upstream/spice')
+
+qxl_win_root = os.path.join(HOME, 'shared_win/qxl/dist')
+
+################################################################################
+
+win_tests = dict(
+shutdown=dict(contents="""
+shutdown /p
+"""),
+suspend=dict(contents=r"""
+%s
+shutdown /p
+""" % ("""
+c:\suspend.exe
+rem ping -n 2 localhost
+""" * 20), files=[HOME+'/shared_win/spice_upstream/util/suspend/suspend.exe']),
+resolution1=dict(contents=r"""
+set RESCHANGE="c:\reschange.exe"
+%s
+shutdown /p
+""" % ("""
+%RESCHANGE% -mon 800,600,32,0,0
+%RESCHANGE% -mon 600,800,32,0,0
+%RESCHANGE% -mon 1024,768,32,0,0
+%RESCHANGE% -mon 768,1024,32,0,0
+%RESCHANGE% -mon 1024,768,32,0,0
+""" * 60), files=[HOME+'/shared_win/spice_upstream/util/reschange/reschange.exe']),
+resolution2=dict(contents=r"""
+set RESCHANGE="c:\reschange.exe"
+%s
+shutdown /p
+""" % ("""
+%RESCHANGE% -attach -mon 1024,768,32,0,0 -mon 800,600,32,1024,0
+%RESCHANGE% -attach -mon 1024,768,32,0,0
+%RESCHANGE% -attach -mon 1024,768,32,0,0 -mon 800,600,32,1024,0
+%RESCHANGE% -attach -mon 1024,768,32,0,0
+""" * 60), files=[HOME+'/shared_win/spice_upstream/util/reschange/reschange.exe']),
+)
+
+################################################################################
+
+red = "\x1b[101m%s\x1b[0m"
+path = os.environ['PATH']
+
+################################################################################
+
+def set_title(x):
+ sys.stdout.write("\033]0;%s\007" % str(x))
+
+def file_in_use(x):
+ return not os.system('lsof -t %s > /dev/null' % x)
+
+def which(x):
+ for f in path.split(':'):
+ full_path = os.path.join(f, x)
+ if os.path.exists(full_path):
+ return full_path
+ return None
+
+def oldness(f):
+ if not os.path.exists(f):
+ return 'file doesn\'t exist'
+ d = datetime.datetime.now() - datetime.datetime.fromtimestamp(os.stat(f).st_mtime)
+ if d.days > 0:
+ return '%d days' % d.days
+ elif d.seconds > 3600:
+ return '%d hours' % int(d.seconds / 3600)
+ elif d.seconds > 60:
+ return '%d minutes' % int(d.seconds / 60)
+ return '%d seconds' % d.seconds
+
+lddpath = which('ldd')
+def get_lib(fname, rexp):
+ l = os.popen('%s "%s"' % (lddpath, fname)).readlines()
+ refs_raw=[x.split('=>') for x in l if '=>' in x]
+ refs=[(r[0].strip().split()[0], r[1].strip().split()[0]) for r in refs_raw]
+ ref = [r[1] for r in refs if re.match(rexp, r[0])]
+ if len(ref) == 0:
+ return 'library not found'
+ return ref[0]
+
+vncviewer = which('vncviewer')
+
+def make_iso(iso_name, root, title):
+ cmd = 'mkisofs -J -R -V %s_%s -o %s %s' % (title, datetime.datetime.now().strtime('%Y%m%d_%H%M'), iso_name, root)
+ print cmd
+ os.system(cmd)
+
+def print_qemu_and_spice_versions(which, qemuexe, bios_dir):
+ base = which + ' '
+ spice_lib = get_lib(qemuexe, 'libspice-server.*')
+ bios_bin = os.path.join(bios_dir, 'bios.bin')
+ print red % (base * (80 / len(base)))
+ print "QEMU age: %s (%s)" % (oldness(qemuexe), qemuexe)
+ print "spice age: %s (%s)" % (oldness(spice_lib), spice_lib)
+ print "bios.bin age: %s (%s)" % (oldness(bios_bin), bios_dir)
+ print red % (base * (80 / len(base)))
+
+def get_temp_name(prefix, postfix):
+ global temp_files
+ i = [0]
+ def make_name(i):
+ name = '%s_%03d%s' % (prefix, i[0], postfix)
+ i[0] += 1
+ return name
+ filename = make_name(i)
+ while os.path.exists(filename):
+ filename = make_name(i)
+ return filename
+
+def create_qcow_with_backing_file(target_base, backing_file, erase_at_exit):
+ filename = get_temp_name(target_base, '.qcow2')
+ os.system('qemu-img create -f qcow2 -o backing_file="%s" %s' % (backing_file, filename))
+ if erase_at_exit:
+ temp_files.append(filename)
+ return filename
+
+def get_image(args):
+ image_base_fullpath = args.image
+ if not os.path.exists(image_base_fullpath):
+ print "missing image %s" % image_base_fullpath
+ sys.exit(1)
+ if not args.second and file_in_use(image_base_fullpath):
+ print "image in use and --second not in arguments"
+ sys.exit(1)
+ if args.backing_file:
+ base_image_name = os.path.basename(args.image)
+ image_fullpath = create_qcow_with_backing_file(
+ target_base='/tmp/'+base_image_name,
+ backing_file=image_base_fullpath,
+ erase_at_exit=args.erase_temp_backing_file)
+ else:
+ image_fullpath = image_base_fullpath
+ print "image %s" % image_fullpath
+ return image_fullpath
+
+def build_test_cmdline(args):
+ test_progs = {
+ 'streaming': 'test_display_streaming',
+ 'display': 'test_display_no_ssl',
+ 'playback': 'test_playback',
+ 'sockets': 'test_just_sockets_no_ssl',
+ 'replay': 'replay',
+ }
+ parts = (['/usr/bin/libtool', '--mode=execute', 'cgdb', '--args'] if args.cgdb else []) + [os.path.join(spice_src_path, 'server/tests', test_progs[args.test])]
+ if args.test == 'replay' and args.replay_file:
+ parts.append(args.replay_file)
+ # tests have this port hardcoded
+ args.port = 5912
+ return ' '.join(parts)
+
+def build_cmdline(args):
+ if args.test:
+ return build_test_cmdline(args)
+ qemu = args.qemu
+ if qemu not in qemus:
+ print "missing qemu %r" % qemu
+ sys.exit(1)
+ args.image_fullpath = image_fullpath = get_image(args)
+ install_root = qemus[qemu]
+ root = os.path.join(HOME, 'spice', install_root)
+ libdir = os.path.join(root, 'lib')
+ bindir = os.path.join(root, 'bin')
+ bios_dir = os.path.join(root, 'share/qemu')
+ qemuexe = os.path.join(bindir, qemu_exec_name)
+ os.environ['LD_LIBRARY_PATH'] = libdir
+ os.environ['PATH'] = bindir + ':' + os.environ['PATH']
+ if args.record_cmd:
+ print "recording cmd ring to %r" % args.record_cmd
+ os.environ['SPICE_WORKER_RECORD_FILENAME'] = args.record_cmd
+ os.system('echo $LD_LIBRARY_PATH')
+ cmdline = ["%(qemuexe)s -chardev stdio,id=muxstdio,mux=on -spice disable-ticketing,port=%(spice_port)s -vga %(vga)s %(guest_debug_option)s %(qxl_debug_option)s %(cmdlog_option)s -drive file=%(image_fullpath)s,cache=%(cache)s -snapshot -mon chardev=muxstdio -enable-kvm -net nic,model=virtio -L %(bios_dir)s -m %(memory)d %(smp_option)s -cpu %(cpu)s %(no_shutdown)s" % dict(
+ qemuexe = qemuexe,
+ vga = args.vga,
+ spice_port = args.spice_port,
+ cache = args.cache,
+ image_fullpath = image_fullpath,
+ bios_dir = bios_dir,
+ guest_debug_option = '' if args.guestdebug == 0 else '-global qxl-vga.guestdebug=%d' % args.guestdebug,
+ qxl_debug_option = '' if args.qxldebug == 0 else '-global qxl-vga.debug=%d' % args.qxldebug,
+ cmdlog_option = '' if args.cmdlog == 0 else '-global qxl-vga.cmdlog=%d' % args.cmdlog,
+ memory = args.memory,
+ smp_option = '' if args.cpus == 1 else '-smp %d' % args.cpus,
+ no_shutdown = '-no-shutdown' if args.no_shutdown else '',
+ cpu = args.cpu,
+ )]
+ if args.qxl > 1:
+ cmdline.append((' -device qxl,guestdebug=%d,cmdlog=%d,debug=%d' % (args.guestdebug,
+ args.cmdlog, args.qxldebug)) * (args.qxl - 1))
+ if args.revision:
+ cmdline.append(' -global qxl-vga.revision=%d' % args.revision)
+ if args.tablet:
+ cmdline.append(' -usb -device usb-tablet')
+ else:
+ cmdline.append(' -device virtio-serial -chardev spicevmc,name=vdagent,id=vdagent -device virtserialport,chardev=vdagent,name=com.redhat.spice.0')
+ # user networking
+ if args.smb:
+ cmdline.append(' -net user,smb=%s,smbserver=10.0.2.2' % args.smb)
+ else:
+ cmdline.append(' -net user')
+ # windbg (TODO - better then serial)
+ if args.windbg_server:
+ cmdline.append(' -serial tcp::%d,server,nowait' % args.windbg_server)
+ if args.windbg_client:
+ if port_is_available(args.windbg_client):
+ cmdline.append(' -serial tcp:localhost:%d' % args.windbg_client)
+ else:
+ print "nothing listening on %d, do you want to start something there? (p.s. maybe systemd service?)" % args.windbg_client
+ # smartcard
+ if args.smartcard:
+ ccid_debug = 1
+ passthru_debug = 1
+ cmdline.extend([' -chardev spicevmc,id=smartcard,debug=3,name=smartcard',
+ " -device usb-ccid,debug=%s,id=ccid" % ccid_debug,
+ " -device ccid-card-passthru,debug=%s,chardev=smartcard" % (passthru_debug),
+ ])
+ # bios debugging
+ if args.debugbios:
+ cmdline.append(' -device isa-debugcon,iobase=0x402,chardev=muxstdio')
+ # incoming migration
+ if args.incoming:
+ cmdline.append(' -incoming tcp:localhost:%d' % args.incoming)
+ if args.freeze:
+ cmdline.append(' -S')
+ # vnc server
+ if args.vnc_port:
+ if (args.vnc_port < 5900):
+ print "vnc_port must be >= 5900"
+ cmdline.append(' -vnc %s:%s' % (args.spice_host, args.vnc_port - 5900))
+
+ # cdrom
+ if args.winqxl_cdrom:
+ if args.cdrom:
+ print "winqxl-cdrom and cdrom are mutually exclusive"
+ sys.exit(1)
+ if not os.path.exists(qxl_win_root):
+ print "you need to edit qxl_win_root path in %s for this option" % (
+ sys.argv[0])
+ sys.exit(1)
+ make_iso('/tmp/winqxl.iso', qxl_win_root, title='qxl')
+ args.cdrom = '/tmp/winqxl.iso'
+ if args.cdrom:
+ if not os.path.exists(args.cdrom):
+ print "missing cdrom image %r" % args.cdrom
+ sys.exit(1)
+ cmdline.append(' -cdrom "%s"' % args.cdrom)
+
+ ######
+ cmdline = ''.join(cmdline)
+
+ print_qemu_and_spice_versions(qemuexe=qemuexe, which=qemu, bios_dir=bios_dir)
+ # wrap in various tools
+ if args.cgdb:
+ cgdb = which('cgdb')
+ cmdline = '%s --args %s' % (cgdb, cmdline)
+ if args.memcheck:
+ print "wrapping with valgrind memcheck"
+ cmdline = 'valgrind --error-limit=no --leak-check=full --suppressions=%(HOME)s/bin/kvm.supp --log-file=/tmp/kvm_%(time_str)s.valgrind %(cmdline)s' % {
+ 'cmdline':cmdline,
+ 'time_str': time.strftime('%Y%m%d_%H%M%S',time.gmtime()),
+ 'HOME': HOME
+ }
+ if args.time:
+ print "wrapping in time"
+ cmdline = '/usr/bin/time --verbose %s' % cmdline
+ return cmdline
+
+def run_xephyr(display, size=(1024,768+40)):
+ return start_process(('Xephyr -screen %dx%d :%d.0' %
+ (size[0], size[1], display)).split())
+
+def run_client(host, port, oldclient, smartcard, certs, dbdir, display=None):
+ s_opts = ''
+ old_display = None
+ if display is not None:
+ old_display = os.environ['DISPLAY']
+ os.environ['DISPLAY'] = ':%d.0' % display
+ if smartcard and certs:
+ if oldclient:
+ s_opts = '--smartcard --smartcard-certs %s --smartcard-db %s' % (
+ ','.join(certs), dbdir)
+ else:
+ s_opts = '--certificates=%s --certificate-db=%s' % (
+ ','.join(certs), dbdir)
+
+ p = start_process(
+ args=('%(exe)s -h %(host)s -p %(port)s %(smartcard)s' % dict(
+ host=host, port=port, exe=spicec_exe if oldclient else spicy_exe,
+ smartcard=s_opts)).split(), kill=True)
+ if old_display:
+ os.environ['DISPLAY'] = old_display
+ return p
+
+def run_vnc_client(host, port):
+ return start_process(args=('%s %s::%s' % (vncviewer, host, port)).split(), kill=True)
+
+def port_is_available(port):
+ s = socket.socket(socket.AF_INET)
+ ret = False
+ try:
+ s.connect(('localhost', port))
+ s.close()
+ ret = True
+ except:
+ pass
+ return ret
+
+def wait_for_port(port):
+ # TODO: do this without actually opening the port - maybe just look at /proc/qemu_process_id/fd?
+ s = socket.socket(socket.AF_INET)
+ while True:
+ try:
+ s.connect(('localhost', port))
+ s.close()
+ break
+ except:
+ time.sleep(1)
+ pass
+
+################################################################################
+
+
+
+def parseargs():
+ # if provided with a name of the form image.qemu take that
+ # instead of --image <image> , --qemu <qemu>
+ # but still let --image and --qemu override it
+ image, qemu = None, None
+ name = sys.argv[0]
+ parts = name.split('.')
+ default_args = []
+ if len(parts) == 1:
+ # this used to be a bunch of bash scripts until I found it prevented
+ # from ever quoting a space
+ image = os.path.basename(parts[0])
+ if len(parts) == 2:
+ image, qemu = parts
+ image = os.path.basename(image)
+ if image:
+ if image in defaults:
+ default_args = defaults[image]['args']
+ if image not in images:
+ print "missing image %r" % image
+ sys.exit(1)
+ image = images[image]
+ parser = argparse.ArgumentParser(description='Process some integers.')
+ parser.add_argument('--record-cmd', dest='record_cmd', help='record qxl command ring')
+ parser.add_argument('--copy-in', dest='copy_in', action='append')
+ parser.add_argument('--nobacking-file', default=True, action='store_false', dest='backing_file', help='use the image as a backing file for the actual image')
+ parser.add_argument('--startup', dest='startup')
+ parser.add_argument('--wintest', dest='wintest', choices=win_tests.keys())
+ parser.add_argument('--cache', dest='cache', choices=['unsafe','off','writeback','writethrough'], default='unsafe')
+ parser.add_argument('--shutdown', dest='no_shutdown', action='store_false', default=True)
+ parser.add_argument('--time', dest='time', action='store_true', default=False)
+ parser.add_argument('--runonce', dest='runonce')
+ parser.add_argument('--clear-runonce', dest='clear_runonce', default=False, action='store_true')
+ parser.add_argument('--showcmdline', dest='showcmdline', default=False, action='store_true')
+ parser.add_argument('--vga', dest='vga', choices=['qxl', 'cirrus'], default='qxl')
+ parser.add_argument('--memcheck', dest='memcheck', default=False, action='store_true')
+ parser.add_argument('--oldclient', dest='oldclient', default=False, action='store_true')
+ parser.add_argument('--clients', dest='clients', default=0, type=int)
+ parser.add_argument('--port', dest='spice_port', type=int, action='store', default=6666)
+ parser.add_argument('--host', dest='spice_host', default='127.0.0.1')
+ parser.add_argument('--qxl', dest='qxl', type=int, default=1)
+ parser.add_argument('--tablet', dest='tablet', action='store_true', default=False)
+ parser.add_argument('--notablet', dest='tablet', action='store_false')
+ parser.add_argument('--xephyr', dest='xephyr', action='store_true', default=False)
+ parser.add_argument('--smb', dest='smb', default=None)
+ parser.add_argument('--cgdb', dest='cgdb', default=False, action='store_true')
+ parser.add_argument('--guestdebug', dest='guestdebug', type=int, default=0)
+ parser.add_argument('--qxldebug', dest='qxldebug', type=int, default=0)
+ parser.add_argument('--cmdlog', dest='cmdlog', type=int, default=0)
+ parser.add_argument('--windbg-server', dest='windbg_server', type=int, default=None)
+ parser.add_argument('--windbg-client', dest='windbg_client', type=int, default=None)
+ parser.add_argument('--memory', dest='memory', type=int, default=512)
+ parser.add_argument('--cpus', dest='cpus', type=int, default=2)
+ parser.add_argument('--nosmartcard', dest='smartcard', action='store_false', default=False)
+ parser.add_argument('--smartcard', dest='smartcard', action='store_true', default=False)
+ parser.add_argument('--smartcard-dbdir', dest='dbdir')
+ parser.add_argument('--smartcard-certs', dest='certs', action='append')
+ parser.add_argument('--debugbios', dest='debugbios', action='store_true', default=False)
+ parser.add_argument('--image', dest='image', default=image)
+ parser.add_argument('--cdrom', dest='cdrom', default=None)
+ parser.add_argument('--winqxl-cdrom', dest='winqxl_cdrom', default=False, action='store_true')
+ parser.add_argument('--qemu', dest='qemu', default=None, choices=qemus.keys() + [None])
+ parser.add_argument('--incoming', dest='incoming', type=int, default=None)
+ parser.add_argument('--title', dest='title', default=None)
+ parser.add_argument('--second', dest='second', default=False, action='store_true')
+ parser.add_argument('--revision', dest='revision', default=None, type=int)
+ parser.add_argument('--freeze', dest='freeze', default=False, action='store_true')
+ parser.add_argument('--vncport', dest='vnc_port', type=int)
+ parser.add_argument('--vncclients', dest='vnc_clients', type=int, default=1)
+ parser.add_argument('--cpu', dest='cpu', choices=['host'], default='host', help='qemu cpu flag')
+ parser.add_argument('--keep-temp-backing-file', dest='erase_temp_backing_file', default=True, action='store_false')
+ # convenient to run tests via the same framework, though this is not using
+ # any of of the qemu flags, just the client running parts.
+ parser.add_argument('--test', dest='test', choices=['streaming', 'display', 'playback', 'replay'])
+ parser.add_argument('--replay-file', dest='replay_file')
+ args = parser.parse_args(default_args + sys.argv[1:])
+ if len(parts) == 1 and not args.test and not args.image:
+ if image not in defaults:
+ print "symlink %r not in %r" % (image, defaults.keys())
+ sys.exit(1)
+ if not args.qemu:
+ args.qemu = defaults[image]['qemu']
+ if not args.qemu:
+ args.qemu = qemu_default
+ return args
+
+def set_runonce_registry(g, root, runonce_paths):
+ print "setting RunOnce to %r" % runonce_paths
+ systemroot = g.inspect_get_windows_systemroot(root)
+ path = "%s/system32/config/software" % systemroot # equivalent to HKLM\\Software
+ path = g.case_sensitive_path(path)
+ g.download (path, "/tmp/software")
+ h = hivex.Hivex("/tmp/software", write=True)
+ p = h.root()
+ for k in r'Microsoft\Windows\CurrentVersion\RunOnce'.split('\\'):
+ p = h.node_get_child(p, k)
+ # 1 - hive_t_REG_SZ - unknown
+ h.node_set_values(p, [{'key': 'Spice2RunOnce', 't': 1, 'value': runonce_path.encode('utf-16le')} for runonce_path in runonce_paths])
+ h.commit(None)
+ g.upload("/tmp/software", path)
+
+def get_guestfs(image):
+ if not guestfs:
+ print "missing guestfs"
+ sys.exit(1)
+ g = guestfs.GuestFS()
+ g.add_drive_opts(image)
+ g.launch()
+ roots = g.inspect_os ()
+ root = roots[0]
+ g.mount_options("", root, "/")
+ return g
+
+def set_runonce(image, program):
+ g = get_guestfs(image)
+ if program is None:
+ values = []
+ else:
+ runonce_base = 'runonce.bat'
+ if os.path.exists(program):
+ runonce_base = os.path.basename(program)
+ g.upload(filename=program, remotefilename='/' + runonce_base)
+ else:
+ g.write('/' + runonce_base, program)
+ values = ['c:\\' + runonce_base]
+ set_runonce_registry(g, root, values)
+ g.sync()
+ g.umount_all()
+ del g
+
+def copy_file_in(g, image, path, dest):
+ if g == None:
+ g = get_guestfs(image)
+ if not os.path.exists(path):
+ print "%s does not exist" % path
+ sys.exit(1)
+ print "copying %r contents to c:\\" % (path)
+ g.upload(path, os.path.join(dest, os.path.basename(path)))
+
+def copy_dir_in(g, image, path):
+ if not os.path.isdir(path):
+ print "expected %r to be a path" % path
+ sys.exit(1)
+ if g == None:
+ g = get_guestfs(image)
+ print "copying %r contents to c:\\" % (path)
+ temp_name = shutil.make_archive('/tmp/spice2.copy_in', 'tar', path + '/')
+ dest = '/' + os.path.basename(path)
+ if not g.is_dir(dest):
+ print "mkdir %r in image" % dest
+ g.mkdir(dest)
+ g.tar_in(tarfile=temp_name, directory=dest)
+ os.unlink(temp_name)
+ return g
+
+startup = '/ProgramData/Microsoft/Windows/Start Menu/Programs/Startup'
+def set_startup_from_file(g, image, path):
+ if not os.path.exists(path):
+ print "startup parameter is either a filename or a path"
+ print "copying file %s to Startup on %s" % (path, image)
+ filename = os.path.basename(path)
+ dest = os.path.join(startup, filename)
+ if g == None:
+ g = get_guestfs(image)
+ g.upload(filename=path, remotefilename=dest)
+ return g
+
+def set_startup_from_contents(g, image, filename, contents):
+ dest = os.path.join(startup, filename)
+ if g == None:
+ g = get_guestfs(image)
+ g.write(dest, contents)
+ return g
+
+################################################################################
+processes = []
+temp_files = []
+
+def start_process(args, kill=False, **kw):
+ global processes
+ p = subprocess.Popen(args, **kw)
+ p.spice_kill = kill
+ processes.append(p)
+ return p
+
+def cleanup():
+ for p in processes:
+ print "killing pid %s" % p.pid
+ try:
+ p.kill()
+ p.kill() # qemu started with --no-shutdown, needs two signals
+ p.wait()
+ except OSError, e:
+ pass
+ for m in temp_files:
+ if not os.path.exists(m):
+ continue
+ print "removing %s" % m
+ os.unlink(m)
+ os.system('reset -I')
+
+def if_processes_are_dead_then_exit(*args):
+ if all((p.poll() is not None or p.spice_kill) for p in processes):
+ cleanup()
+ sys.exit(0)
+ #print '|'.join(('%d: %s, %s' % (p.pid, p.poll(), p.spice_kill)) for p in processes)
+
+def ignore_ctrlc():
+ signal.signal(signal.SIGINT, if_processes_are_dead_then_exit)
+
+################################################################################
+clients = []
+vnc_clients = []
+
+def main():
+ args = parseargs()
+ cmdline = build_cmdline(args)
+ atexit.register(cleanup)
+ if args.runonce:
+ set_runonce(args.image_fullpath, args.runonce)
+ elif args.clear_runonce:
+ set_runonce(args.image_fullpath, None)
+ g = None
+ if args.startup:
+ g = set_startup_from_file(g, args.image_fullpath, args.startup)
+ if args.wintest:
+ g = set_startup_from_contents(g, image=args.image_fullpath, filename='startup.bat',
+ contents=win_tests[args.wintest]['contents'])
+ # NOTE: can just use autoplay and a suitable cdrom iso for these, faster then guestfs by far.
+ for filename in win_tests[args.wintest].get('files', []):
+ g = copy_file_in(g, args.image_fullpath, filename, '/')
+ if args.copy_in:
+ for p in args.copy_in:
+ g = copy_dir_in(g, args.image_fullpath, p)
+ if g:
+ g.sync()
+ g.umount_all()
+ del g
+ set_title("%s%s" % (
+ 'I ' if args.incoming else '',
+ args.title if args.title else os.path.basename(sys.argv[0])
+ ))
+ print cmdline
+ if args.showcmdline:
+ return
+ ignore_ctrlc()
+ test_process = start_process(args=cmdline.split())
+ def default_get_display():
+ while True:
+ yield None
+ def xephyr_get_display():
+ i = 2
+ while True:
+ yield i
+ i += 1
+ if args.xephyr:
+ display = xephyr_get_display()
+ else:
+ display = default_get_display()
+ if args.clients > 0:
+ print "starting clients"
+ cur_display = display.next()
+ if cur_display:
+ run_xephyr(display=cur_display)
+ wait_for_port(args.spice_port)
+ for i in xrange(args.clients):
+ client_process = run_client(
+ host=args.spice_host, port=args.spice_port,
+ oldclient=args.oldclient, smartcard=args.smartcard,
+ certs=args.certs, dbdir=args.dbdir,
+ display=cur_display)
+ clients.append(client_process)
+ print "waiting for test_process"
+ if args.vnc_port and args.vnc_clients > 0:
+ if not vncviewer:
+ print "vncviewer not in path - not starting"
+ else:
+ print "starting vnc clients"
+ wait_for_port(args.vnc_port)
+ for i in xrange(args.vnc_clients):
+ client_process = run_vnc_client(host=args.spice_host, port=args.vnc_port)
+ vnc_clients.append(client_process)
+ test_process.wait()
+
+if __name__ == '__main__':
+ main()