Adding a VNC WebSocket support for QEMU driver. This funcitonality is
in upstream qemu from commit described as v1.3.0-982-g7536ee4, so the
capability is being recognized based on QEMU version for now.
Signed-off-by: Martin Kletzander <mkletzan(a)redhat.com>
---
src/qemu/libvirtd_qemu.aug | 4 +-
src/qemu/qemu.conf | 7 +++
src/qemu/qemu_capabilities.c | 5 ++
src/qemu/qemu_capabilities.h | 1 +
src/qemu/qemu_command.c | 60 +++++++++++++++++++++-
src/qemu/qemu_command.h | 5 +-
src/qemu/qemu_conf.c | 32 ++++++++++++
src/qemu/qemu_conf.h | 6 +++
src/qemu/qemu_driver.c | 5 ++
src/qemu/qemu_process.c | 31 ++++++++---
src/qemu/test_libvirtd_qemu.aug.in | 2 +
tests/qemuargv2xmltest.c | 1 +
.../qemuxml2argv-graphics-vnc-websocket.args | 4 ++
.../qemuxml2argv-graphics-vnc-websocket.xml | 35 +++++++++++++
tests/qemuxml2argvtest.c | 1 +
tests/qemuxml2xmltest.c | 1 +
16 files changed, 190 insertions(+), 10 deletions(-)
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-graphics-vnc-websocket.args
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-graphics-vnc-websocket.xml
diff --git a/src/qemu/libvirtd_qemu.aug b/src/qemu/libvirtd_qemu.aug
index 61740a9..5344125 100644
--- a/src/qemu/libvirtd_qemu.aug
+++ b/src/qemu/libvirtd_qemu.aug
@@ -41,6 +41,8 @@ module Libvirtd_qemu =
let remote_display_entry = int_entry "remote_display_port_min"
| int_entry "remote_display_port_max"
+ | int_entry "remote_websocket_port_min"
+ | int_entry "remote_websocket_port_max"
let security_entry = str_entry "security_driver"
| bool_entry "security_default_confined"
@@ -74,7 +76,7 @@ module Libvirtd_qemu =
| int_entry "keepalive_interval"
| int_entry "keepalive_count"
- (* Each enty in the config is one of the following three ... *)
+ (* Each entry in the config is one of the following ... *)
let entry = vnc_entry
| spice_entry
| remote_display_entry
diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf
index 87bdf70..809e254 100644
--- a/src/qemu/qemu.conf
+++ b/src/qemu/qemu.conf
@@ -153,6 +153,13 @@
#remote_display_port_min = 5900
#remote_display_port_max = 65535
+# VNC WebSocket port policies, same rules apply as with remote display
+# ports. VNC WebSockets use similar display <-> port mappings, with
+# the exception being that ports starts from 5700 instead of 5900.
+# This is what may have be changed here.
+#
+#remote_websocket_port_min = 5700
+#remote_websocket_port_max = 65535
# The default security driver is SELinux. If SELinux is disabled
# on the host, then the security driver will automatically disable
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index 2acf535..2ddeb8c 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -225,6 +225,7 @@ VIR_ENUM_IMPL(virQEMUCaps, QEMU_CAPS_LAST,
"pci-bridge", /* 141 */
"vfio-pci", /* 142 */
"vfio-pci.bootindex", /* 143 */
+ "vnc-websocket", /* 143 */
);
struct _virQEMUCaps {
@@ -2520,6 +2521,10 @@ virQEMUCapsInitQMP(virQEMUCapsPtr qemuCaps,
if (qemuCaps->version >= 1003000)
virQEMUCapsSet(qemuCaps, QEMU_CAPS_MACHINE_USB_OPT);
+ /* WebSockets were intriduced between 1.3.0 and 1.3.1 */
+ if (qemuCaps->version >= 1003001)
+ virQEMUCapsSet(qemuCaps, QEMU_CAPS_VNC_WEBSOCKET);
+
if (!(archstr = qemuMonitorGetTargetArch(mon)))
goto cleanup;
diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h
index 213f63c..c647274 100644
--- a/src/qemu/qemu_capabilities.h
+++ b/src/qemu/qemu_capabilities.h
@@ -182,6 +182,7 @@ enum virQEMUCapsFlags {
QEMU_CAPS_DEVICE_PCI_BRIDGE = 141, /* -device pci-bridge */
QEMU_CAPS_DEVICE_VFIO_PCI = 142, /* -device vfio-pci */
QEMU_CAPS_VFIO_PCI_BOOTINDEX = 143, /* bootindex param for vfio-pci device */
+ QEMU_CAPS_VNC_WEBSOCKET = 144, /* bootindex param for vfio-pci device */
QEMU_CAPS_LAST, /* this must always be the last item */
};
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index d23bdfc..1f74d26 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -5900,6 +5900,17 @@ qemuBuildGraphicsVNCCommandLine(virQEMUDriverConfigPtr cfg,
}
}
+ if (graphics->data.vnc.websocket) {
+ if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VNC_WEBSOCKET)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("VNC websockets are not supported "
+ "with this QEMU binary"));
+ goto error;
+ }
+
+ virBufferAsprintf(&opt, ",websocket=%d",
graphics->data.vnc.websocket);
+ }
+
virCommandAddArg(cmd, "-vnc");
virCommandAddArgBuffer(cmd, &opt);
if (graphics->data.vnc.keymap)
@@ -9724,6 +9735,7 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr qemuCaps,
* -vnc some.host.name:4
*/
char *opts;
+ char *port;
const char *sep = ":";
if (val[0] == '[')
sep = "]:";
@@ -9734,11 +9746,12 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr qemuCaps,
_("missing VNC port number in
'%s'"), val);
goto error;
}
- if (virStrToLong_i(tmp+strlen(sep), &opts, 10,
+ port = tmp + strlen(sep);
+ if (virStrToLong_i(port, &opts, 10,
&vnc->data.vnc.port) < 0) {
virDomainGraphicsDefFree(vnc);
virReportError(VIR_ERR_INTERNAL_ERROR,
- _("cannot parse VNC port '%s'"),
tmp+1);
+ _("cannot parse VNC port '%s'"),
port);
goto error;
}
if (val[0] == '[')
@@ -9751,6 +9764,49 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr qemuCaps,
virDomainGraphicsDefFree(vnc);
goto no_memory;
}
+
+ if (*opts == ',') {
+ char *orig_opts = strdup(opts + 1);
+ if (!orig_opts) {
+ virDomainGraphicsDefFree(vnc);
+ goto no_memory;
+ }
+ opts = orig_opts;
+
+ while (opts && *opts) {
+ char *nextopt = strchr(opts, ',');
+ if (nextopt)
+ *(nextopt++) = '\0';
+
+ if (STRPREFIX(opts, "websocket")) {
+ char *websocket = opts + strlen("websocket");
+ if (*(websocket++) == '=' &&
+ *websocket) {
+ /* If the websocket continues with
+ * '=<something>', we'll parse it */
+ if (virStrToLong_i(websocket,
+ NULL, 0,
+ &vnc->data.vnc.websocket) <
0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot parse VNC "
+ "websocket port
'%s'"),
+ websocket);
+ virDomainGraphicsDefFree(vnc);
+ VIR_FREE(orig_opts);
+ }
+ } else {
+ /* Otherwise, we'll compute the port the same
+ * way QEMU does, by adding a 5700 to the
+ * display value. */
+ vnc->data.vnc.websocket =
+ vnc->data.vnc.port + 5700;
+ }
+ }
+
+ opts = nextopt;
+ }
+ VIR_FREE(orig_opts);
+ }
vnc->data.vnc.port += 5900;
vnc->data.vnc.autoport = false;
}
diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h
index a706942..724e88e 100644
--- a/src/qemu/qemu_command.h
+++ b/src/qemu/qemu_command.h
@@ -1,7 +1,7 @@
/*
* qemu_command.h: QEMU command generation
*
- * Copyright (C) 2006-2012 Red Hat, Inc.
+ * Copyright (C) 2006-2013 Red Hat, Inc.
* Copyright (C) 2006 Daniel P. Berrange
*
* This library is free software; you can redistribute it and/or
@@ -48,6 +48,9 @@
# define QEMU_REMOTE_PORT_MIN 5900
# define QEMU_REMOTE_PORT_MAX 65535
+# define QEMU_WEBSOCKET_PORT_MIN 5700
+# define QEMU_WEBSOCKET_PORT_MAX 65535
+
virCommandPtr qemuBuildCommandLine(virConnectPtr conn,
virQEMUDriverPtr driver,
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
index 7c3f317..1e56c5b 100644
--- a/src/qemu/qemu_conf.c
+++ b/src/qemu/qemu_conf.c
@@ -228,6 +228,9 @@ virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged)
cfg->remotePortMin = QEMU_REMOTE_PORT_MIN;
cfg->remotePortMax = QEMU_REMOTE_PORT_MAX;
+ cfg->webSocketPortMin = QEMU_WEBSOCKET_PORT_MIN;
+ cfg->webSocketPortMax = QEMU_WEBSOCKET_PORT_MAX;
+
#if defined HAVE_MNTENT_H && defined HAVE_GETMNTENT_R
/* For privileged driver, try and find hugepage mount automatically.
* Non-privileged driver requires admin to create a dir for the
@@ -404,6 +407,35 @@ int virQEMUDriverConfigLoadFile(virQEMUDriverConfigPtr cfg,
GET_VALUE_STR("spice_password", cfg->spicePassword);
+ GET_VALUE_LONG("remote_websocket_port_min", cfg->webSocketPortMin);
+ if (cfg->webSocketPortMin < QEMU_WEBSOCKET_PORT_MIN) {
+ /* if the port is too low, we can't get the display name
+ * to tell to vnc (usually subtract 5700, e.g. localhost:1
+ * for port 5701) */
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("%s: remote_websocket_port_min: port must be greater
"
+ "than or equal to %d"),
+ filename, QEMU_WEBSOCKET_PORT_MIN);
+ goto cleanup;
+ }
+
+ GET_VALUE_LONG("remote_websocket_port_max", cfg->webSocketPortMax);
+ if (cfg->webSocketPortMax > QEMU_WEBSOCKET_PORT_MAX ||
+ cfg->webSocketPortMax < cfg->webSocketPortMin) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("%s: remote_websocket_port_max: port must be between
"
+ "the minimal port and %d"),
+ filename, QEMU_WEBSOCKET_PORT_MAX);
+ goto cleanup;
+ }
+
+ if (cfg->webSocketPortMin > cfg->webSocketPortMax) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("%s: remote_websocket_port_min: min port must not be
"
+ "greater than max port"), filename);
+ goto cleanup;
+ }
+
GET_VALUE_LONG("remote_display_port_min", cfg->remotePortMin);
if (cfg->remotePortMin < QEMU_REMOTE_PORT_MIN) {
/* if the port is too low, we can't get the display name
diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h
index 77d3d2f..8392729 100644
--- a/src/qemu/qemu_conf.h
+++ b/src/qemu/qemu_conf.h
@@ -114,6 +114,9 @@ struct _virQEMUDriverConfig {
int remotePortMin;
int remotePortMax;
+ int webSocketPortMin;
+ int webSocketPortMax;
+
char *hugetlbfsMount;
char *hugepagePath;
char *bridgeHelperName;
@@ -210,6 +213,9 @@ struct _virQEMUDriver {
/* Immutable pointer, self-locking APIs */
virPortAllocatorPtr remotePorts;
+ /* Immutable pointer, self-locking APIs */
+ virPortAllocatorPtr webSocketPorts;
+
/* Immutable pointer, lockless APIs*/
virSysinfoDefPtr hostsysinfo;
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 2d3b24a..1767483 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -663,6 +663,11 @@ qemuStateInitialize(bool privileged,
cfg->remotePortMax)) == NULL)
goto error;
+ if ((qemu_driver->webSocketPorts =
+ virPortAllocatorNew(cfg->webSocketPortMin,
+ cfg->webSocketPortMax)) == NULL)
+ goto error;
+
if (qemuSecurityInit(qemu_driver) < 0)
goto error;
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index f12d7d5..130abd9 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -3233,6 +3233,29 @@ qemuSetUnprivSGIO(virDomainDiskDefPtr disk)
return ret;
}
+static int
+qemuProcessVNCAllocatePorts(virQEMUDriverPtr driver,
+ virDomainGraphicsDefPtr graphics)
+{
+ unsigned short port;
+
+ if (graphics->data.vnc.socket)
+ return 0;
+
+ if (graphics->data.vnc.autoport) {
+ if (virPortAllocatorAcquire(driver->remotePorts, &port) < 0)
+ return -1;
+ graphics->data.vnc.port = port;
+ }
+
+ if (graphics->data.vnc.websocket == -1) {
+ if (virPortAllocatorAcquire(driver->webSocketPorts, &port) < 0)
+ return -1;
+ graphics->data.vnc.websocket = port;
+ }
+
+ return 0;
+}
static int
qemuProcessSPICEAllocatePorts(virQEMUDriverPtr driver,
@@ -3442,13 +3465,9 @@ int qemuProcessStart(virConnectPtr conn,
for (i = 0 ; i < vm->def->ngraphics; ++i) {
virDomainGraphicsDefPtr graphics = vm->def->graphics[i];
- if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC &&
- !graphics->data.vnc.socket &&
- graphics->data.vnc.autoport) {
- unsigned short port;
- if (virPortAllocatorAcquire(driver->remotePorts, &port) < 0)
+ if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) {
+ if (qemuProcessVNCAllocatePorts(driver, graphics) < 0)
goto cleanup;
- graphics->data.vnc.port = port;
} else if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) {
if (qemuProcessSPICEAllocatePorts(driver, cfg, graphics) < 0)
goto cleanup;
diff --git a/src/qemu/test_libvirtd_qemu.aug.in b/src/qemu/test_libvirtd_qemu.aug.in
index 0aec997..115e137 100644
--- a/src/qemu/test_libvirtd_qemu.aug.in
+++ b/src/qemu/test_libvirtd_qemu.aug.in
@@ -17,6 +17,8 @@ module Test_libvirtd_qemu =
{ "spice_password" = "XYZ12345" }
{ "remote_display_port_min" = "5900" }
{ "remote_display_port_max" = "65535" }
+{ "remote_websocket_port_min" = "5700" }
+{ "remote_websocket_port_max" = "65535" }
{ "security_driver" = "selinux" }
{ "security_default_confined" = "1" }
{ "security_require_confined" = "1" }
diff --git a/tests/qemuargv2xmltest.c b/tests/qemuargv2xmltest.c
index 9f1bb24..c8e7825 100644
--- a/tests/qemuargv2xmltest.c
+++ b/tests/qemuargv2xmltest.c
@@ -200,6 +200,7 @@ mymain(void)
DO_TEST("disk-usb");
DO_TEST("graphics-vnc");
DO_TEST("graphics-vnc-socket");
+ DO_TEST("graphics-vnc-websocket");
DO_TEST("graphics-vnc-sasl");
DO_TEST("graphics-vnc-tls");
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-vnc-websocket.args
b/tests/qemuxml2argvdata/qemuxml2argv-graphics-vnc-websocket.args
new file mode 100644
index 0000000..b5d63bd
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-vnc-websocket.args
@@ -0,0 +1,4 @@
+LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=none \
+/usr/bin/qemu -S -M pc -m 214 -smp 1 -monitor unix:/tmp/test-monitor,server,\
+nowait -no-acpi -boot c -usb -hda /dev/HostVG/QEMUGuest1 -net none -serial none \
+-parallel none -vnc '[2001:1:2:3:4:5:1234:1234]:3,websocket=5703'
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-vnc-websocket.xml
b/tests/qemuxml2argvdata/qemuxml2argv-graphics-vnc-websocket.xml
new file mode 100644
index 0000000..b6e66db
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-vnc-websocket.xml
@@ -0,0 +1,35 @@
+<domain type='qemu'>
+ <name>QEMUGuest1</name>
+ <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+ <memory unit='KiB'>219100</memory>
+ <currentMemory unit='KiB'>219100</currentMemory>
+ <vcpu placement='static'>1</vcpu>
+ <os>
+ <type arch='i686' machine='pc'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <emulator>/usr/bin/qemu</emulator>
+ <disk type='block' device='disk'>
+ <driver name='qemu'/>
+ <source dev='/dev/HostVG/QEMUGuest1'/>
+ <target dev='hda' bus='ide'/>
+ <address type='drive' controller='0' bus='0'
target='0' unit='0'/>
+ </disk>
+ <controller type='usb' index='0'/>
+ <controller type='ide' index='0'/>
+ <controller type='pci' index='0' model='pci-root'/>
+ <input type='mouse' bus='ps2'/>
+ <graphics type='vnc' port='5903' autoport='no'
websocket='5703' listen='2001:1:2:3:4:5:1234:1234'>
+ <listen type='address' address='2001:1:2:3:4:5:1234:1234'/>
+ </graphics>
+ <video>
+ <model type='cirrus' vram='9216' heads='1'/>
+ </video>
+ <memballoon model='virtio'/>
+ </devices>
+</domain>
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index 9699f77..0db3181 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -597,6 +597,7 @@ mymain(void)
DO_TEST("graphics-vnc", QEMU_CAPS_VNC);
DO_TEST("graphics-vnc-socket", QEMU_CAPS_VNC);
+ DO_TEST("graphics-vnc-websocket", QEMU_CAPS_VNC, QEMU_CAPS_VNC_WEBSOCKET);
driver.config->vncSASL = 1;
VIR_FREE(driver.config->vncSASLdir);
diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c
index a81cfcf..7400779 100644
--- a/tests/qemuxml2xmltest.c
+++ b/tests/qemuxml2xmltest.c
@@ -186,6 +186,7 @@ mymain(void)
DO_TEST_FULL("disk-mirror", true, WHEN_INACTIVE);
DO_TEST("graphics-listen-network");
DO_TEST("graphics-vnc");
+ DO_TEST("graphics-vnc-websocket");
DO_TEST("graphics-vnc-sasl");
DO_TEST("graphics-vnc-tls");
DO_TEST("graphics-sdl");
--
1.8.2.1