summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Levy <alevy@redhat.com>2011-07-28 19:14:52 +0300
committerAlon Levy <alevy@redhat.com>2011-07-28 19:15:55 +0300
commit115b16df48908ebe24c7c856e6ce6b26d9a88bb2 (patch)
treea0999a17785ac4197f63892dce335621ebd327c6
parente4a983f3365514c62151fe3704c1bade1b547ccf (diff)
multiple
command line refactor function for client command line build options for tls channel (ca, key) beginning of qmp usage, just adding the monitor. (via qmp.py from qemu repo)
-rw-r--r--qmp.py157
-rwxr-xr-xspice2129
2 files changed, 256 insertions, 30 deletions
diff --git a/qmp.py b/qmp.py
new file mode 100644
index 0000000..c7dbea0
--- /dev/null
+++ b/qmp.py
@@ -0,0 +1,157 @@
+# QEMU Monitor Protocol Python class
+#
+# Copyright (C) 2009, 2010 Red Hat Inc.
+#
+# Authors:
+# Luiz Capitulino <lcapitulino@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2. See
+# the COPYING file in the top-level directory.
+
+import json
+import errno
+import socket
+
+class QMPError(Exception):
+ pass
+
+class QMPConnectError(QMPError):
+ pass
+
+class QMPCapabilitiesError(QMPError):
+ pass
+
+class QEMUMonitorProtocol:
+ def __init__(self, address, server=False):
+ """
+ Create a QEMUMonitorProtocol class.
+
+ @param address: QEMU address, can be either a unix socket path (string)
+ or a tuple in the form ( address, port ) for a TCP
+ connection
+ @param server: server mode listens on the socket (bool)
+ @raise socket.error on socket connection errors
+ @note No connection is established, this is done by the connect() or
+ accept() methods
+ """
+ self.__events = []
+ self.__address = address
+ self.__sock = self.__get_sock()
+ if server:
+ self.__sock.bind(self.__address)
+ self.__sock.listen(1)
+
+ def __get_sock(self):
+ if isinstance(self.__address, tuple):
+ family = socket.AF_INET
+ else:
+ family = socket.AF_UNIX
+ return socket.socket(family, socket.SOCK_STREAM)
+
+ def __negotiate_capabilities(self):
+ self.__sockfile = self.__sock.makefile()
+ greeting = self.__json_read()
+ if greeting is None or not greeting.has_key('QMP'):
+ raise QMPConnectError
+ # Greeting seems ok, negotiate capabilities
+ resp = self.cmd('qmp_capabilities')
+ if "return" in resp:
+ return greeting
+ raise QMPCapabilitiesError
+
+ def __json_read(self, only_event=False):
+ while True:
+ data = self.__sockfile.readline()
+ if not data:
+ return
+ resp = json.loads(data)
+ if 'event' in resp:
+ self.__events.append(resp)
+ if not only_event:
+ continue
+ return resp
+
+ error = socket.error
+
+ def connect(self):
+ """
+ Connect to the QMP Monitor and perform capabilities negotiation.
+
+ @return QMP greeting dict
+ @raise socket.error on socket connection errors
+ @raise QMPConnectError if the greeting is not received
+ @raise QMPCapabilitiesError if fails to negotiate capabilities
+ """
+ self.__sock.connect(self.__address)
+ return self.__negotiate_capabilities()
+
+ def accept(self):
+ """
+ Await connection from QMP Monitor and perform capabilities negotiation.
+
+ @return QMP greeting dict
+ @raise socket.error on socket connection errors
+ @raise QMPConnectError if the greeting is not received
+ @raise QMPCapabilitiesError if fails to negotiate capabilities
+ """
+ self.__sock, _ = self.__sock.accept()
+ return self.__negotiate_capabilities()
+
+ def cmd_obj(self, qmp_cmd):
+ """
+ Send a QMP command to the QMP Monitor.
+
+ @param qmp_cmd: QMP command to be sent as a Python dict
+ @return QMP response as a Python dict or None if the connection has
+ been closed
+ """
+ try:
+ self.__sock.sendall(json.dumps(qmp_cmd))
+ except socket.error, err:
+ if err[0] == errno.EPIPE:
+ return
+ raise socket.error(err)
+ return self.__json_read()
+
+ def cmd(self, name, args=None, id=None):
+ """
+ Build a QMP command and send it to the QMP Monitor.
+
+ @param name: command name (string)
+ @param args: command arguments (dict)
+ @param id: command id (dict, list, string or int)
+ """
+ qmp_cmd = { 'execute': name }
+ if args:
+ qmp_cmd['arguments'] = args
+ if id:
+ qmp_cmd['id'] = id
+ return self.cmd_obj(qmp_cmd)
+
+ def get_events(self, wait=False):
+ """
+ Get a list of available QMP events.
+
+ @param wait: block until an event is available (bool)
+ """
+ self.__sock.setblocking(0)
+ try:
+ self.__json_read()
+ except socket.error, err:
+ if err[0] == errno.EAGAIN:
+ # No data available
+ pass
+ self.__sock.setblocking(1)
+ if not self.__events and wait:
+ self.__json_read(only_event=True)
+ return self.__events
+
+ def clear_events(self):
+ """
+ Clear current list of pending events.
+ """
+ self.__events = []
+
+ def close(self):
+ self.__sock.close()
+ self.__sockfile.close()
diff --git a/spice2 b/spice2
index b3af347..7648cd3 100755
--- a/spice2
+++ b/spice2
@@ -315,7 +315,37 @@ def build_test_cmdline(args):
args.port = 5912
return ' '.join(parts)
-def build_cmdline(args):
+def build_spice_option(args):
+ ret = ["disable-ticketing"]
+ if args.spice_port:
+ ret.append('port=%s' % args.spice_port)
+ if args.tls_port:
+ if not args.x509_dir:
+ if not args.server_key:
+ print "have tls-port but missing server-key"
+ sys.exit(1)
+ if not args.server_cert:
+ print "have tls-port but missing server-cert"
+ sys.exit(1)
+ if not args.ca_cert:
+ print "have tls-port but missing ca-cert"
+ sys.exit(1)
+ ret.append('tls-port=%s' % args.tls_port)
+ if args.x509_dir:
+ ret.extend(['x509-dir=%s' % args.x509_dir])
+ else:
+ ret.extend([
+ 'x509-key-file=%s' % args.server_key,
+ 'x509-cert-file=%s' % args.server_cert,
+ 'x509-cacert-file=%s' % args.ca_cert,
+ ])
+ if args.server_key_password:
+ ret.append('x509-key-password=%s' % args.server_key_password)
+ if args.sasl:
+ ret.append('sasl')
+ return ['-spice', ','.join(ret)]
+
+def build_qemu_cmdline(args):
if args.test_prog:
return build_test_cmdline(args)
qemu = args.qemu
@@ -336,16 +366,20 @@ def build_cmdline(args):
os.environ['SPICE_WORKER_RECORD_FILENAME'] = args.record_cmd
os.system('echo $LD_LIBRARY_PATH')
cmdline = [qemuexe,
- "-chardev", "stdio,id=muxstdio,mux=on",
- "-spice", "disable-ticketing,port=%s" % args.spice_port,
+ "-chardev", "stdio,id=muxstdio,mux=on", "-mon", "chardev=muxstdio,mode=readline",
"-vga", args.vga,
"-drive", "file=%s,cache=%s" % (args.image_fullpath, args.cache),
- "-snapshot", "-mon", "chardev=muxstdio", "-enable-kvm",
+ "-snapshot", "-enable-kvm",
"-net", "nic,model=virtio",
"-L", bios_dir, "-m",
str(args.memory),
- "-cpu", str(args.cpu),
- '-no-shutdown' if args.no_shutdown else '']
+ "-cpu", str(args.cpu)] + build_spice_option(args)
+ if args.qmp_port:
+ cmdline.extend(["-chardev",
+ "socket,id=qmpmon,host=localhost,port=%s,server" % args.qmp_port,
+ "-mon", "chardev=qmpmon,mode=control",])
+ if args.no_shutdown:
+ cmdline.append('-no-shutdown')
if args.cpus > 1:
cmdline.extend(['-smp', str(args.cpus)])
if args.guestdebug > 0:
@@ -361,7 +395,7 @@ def build_cmdline(args):
cmdline.extend(['-global', 'qxl-vga.revision=%d' % args.revision])
if args.tablet:
cmdline.extend(['-usb', '-device', 'usb-tablet'])
- else:
+ if args.vdagent:
cmdline.extend(['-device', 'virtio-serial', '-chardev', 'spicevmc,name=vdagent,id=vdagent',
'-device', 'virtserialport,chardev=vdagent,name=com.redhat.spice.0'])
# user networking
@@ -381,7 +415,7 @@ def build_cmdline(args):
if args.smartcard != 'off':
ccid_debug = 1
passthru_debug = 1
- cmdline.extend(['-chardev spicevmc,id=smartcard,debug=3,name=smartcard',
+ 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),
])
@@ -418,7 +452,9 @@ def build_cmdline(args):
######
- print_qemu_and_spice_versions(qemuexe=qemuexe, which=qemu, bios_dir=bios_dir)
+ print_qemu_and_spice_versions(qemuexe=qemuexe,
+ clientexe=get_client_ext(args.oldclient),
+ which=qemu, bios_dir=bios_dir)
# wrap in various tools
if args.cgdb:
cgdb = which('cgdb')
@@ -441,33 +477,42 @@ def rpdb2_break():
import rpbd2
rpdb2.rpdb2.start_embedded_debugger('a')
-def run_client(host, port, oldclient, smartcard, certs, dbdir, display=None,
- auto_conf=False):
- s_opts = ''
- ac_opts = ''
+def get_client_ext(oldclient):
+ return spicec_exe if oldclient else spicy_exe
+
+def run_client(host, port, oldclient,
+ smartcard, certs, dbdir,
+ tls_port=None, ca_file=None, host_subject=None,
+ display=None, auto_conf=False):
+ args = [get_client_ext(oldclient), '-h', host]
+ if port:
+ args.extend(['-p', str(port)])
old_display = None
if display is not None:
old_display = os.environ['DISPLAY']
os.environ['DISPLAY'] = ':%d.0' % display
if smartcard == 'sw':
if oldclient:
- s_opts = '--smartcard --smartcard-certs %s --smartcard-db %s' % (
- ','.join(certs), dbdir)
+ args.extend(['--smartcard',
+ '--smartcard-cert', ','.join(certs), '--smartcard-db', dbdir])
else:
- s_opts = '--certificates=%s --certificate-db=%s' % (
- ','.join(certs), dbdir)
+ args.extend(['--spice-smartcard',
+ '--spice-smartcard-certificates=%s' % (','.join(certs)),
+ '--spice-smartcard-db=%s' % dbdir])
if auto_conf and oldclient:
- ac_opts = '--full-screen=auto-conf'
+ args.append('--full-screen=auto-conf')
+ if tls_port:
+ if oldclient:
+ args.extend(['-s', str(tls_port), '--host-subject',
+ host_subject, '--ca-file', ca_file])
+ else:
+ args.extend(['-s', str(tls_port),
+ '--spice-host-subject=%s' % host_subject,
+ '--spice-ca-file=%s' % ca_file])
if dbdir and not os.path.exists(dbdir):
print "error - dbdir supplied does not exist"
sys.exit(1)
- p = start_process(
- args=('%(exe)s -h %(host)s -p %(port)s %(smartcard)s %(autoconf)s' %
- dict(
- host=host, port=port,
- exe=spicec_exe if oldclient else spicy_exe,
- autoconf=ac_opts,
- smartcard=s_opts)).split(), kill=True)
+ p = start_process(args=args, kill=True)
if old_display:
os.environ['DISPLAY'] = old_display
return p
@@ -545,12 +590,23 @@ def parseargs():
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('--port', dest='spice_port', type=int, action='store')
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('--qmp-port', dest='qmp_port', type=int, default=0)
parser.add_argument('--tablet', dest='tablet', action='store_true', default=False)
+ parser.add_argument('--vdagent', dest='vdagent', action='store_true', default=False)
+ parser.add_argument('--novdagent', dest='vdagent', action='store_false')
parser.add_argument('--notablet', dest='tablet', action='store_false')
parser.add_argument('--xephyr', dest='xephyr', action='store_true', default=False)
+ parser.add_argument('--tls-port', type=int)
+ parser.add_argument('--x509-dir')
+ parser.add_argument('--ca-cert')
+ parser.add_argument('--server-cert')
+ parser.add_argument('--server-key')
+ parser.add_argument('--server-key-password')
+ parser.add_argument('--host-subject')
+ parser.add_argument('--sasl', dest='sasl', 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)
@@ -598,6 +654,9 @@ def parseargs():
print "image not provided. use --image"
parser.print_usage()
sys.exit(1)
+ # tls arguments
+ if args.x509_dir:
+ args.ca_cert = os.path.join(args.x509_dir, 'ca-cert.pem')
return args
def set_runonce_registry(g, root, runonce_paths):
@@ -708,6 +767,7 @@ class MyProcess(subprocess.Popen):
def start_process(args, kill=False, show_output=True, **kw):
global processes
+ print repr(args)
if show_output:
p = MyProcess(args, **kw)
else:
@@ -765,7 +825,7 @@ def xephyr_get_display():
def main():
args = parseargs()
update_certs(args)
- cmdline = build_cmdline(args)
+ cmdline = build_qemu_cmdline(args)
atexit.register(cleanup)
if args.runonce:
set_runonce(args.image_fullpath, args.runonce)
@@ -791,8 +851,8 @@ def main():
'I ' if args.incoming else '',
args.title if args.title else os.path.basename(sys.argv[0])
))
- print repr(cmdline)
if args.showcmdline:
+ print repr(cmdline)
return
ignore_ctrlc()
exit_on_hup()
@@ -809,6 +869,9 @@ def main():
(python_exe, spicedump_exe, proxy_port, real_spice_port,
' -f %s' % args.spicedump_filter if args.spicedump_filter else ''
)).split())
+ if args.qmp_port:
+ #start_process(
+ print "TODO"
if args.test_func:
test_func = globals()['test_'+args.test_func]
test_func(server=test_process, args=args)
@@ -818,13 +881,19 @@ def main():
cur_display = display.next()
if cur_display:
run_xephyr(display=cur_display)
- wait_for_port(args.spice_port)
+ if args.spice_port:
+ wait_for_port(args.spice_port)
+ else:
+ wait_for_port(args.tls_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)
+ display=cur_display,
+ tls_port=args.tls_port,
+ ca_file=args.ca_cert,
+ host_subject=args.host_subject)
clients.append(client_process)
if args.vnc_port and args.vnc_clients > 0:
if not vncviewer: