[libvirt] [PATCH v3 0/2] VNC WebSocket support

I'm re-sending this series even after previous ACK, because I've found out there was missing virPortAllocatorRelease call when disposing of domain. There were also many cleanups in the meantime and both John and Eric found out some nits in the previous series. I also *somehow* lost the tests (not even git-reflog found them) and had to re-create them again. Martin Kletzander (2): Add VNC WebSocket support qemu: Add VNC WebSocket support docs/formatdomain.html.in | 5 ++ docs/schemas/domaincommon.rng | 5 ++ src/conf/domain_conf.c | 16 ++++++ src/conf/domain_conf.h | 1 + src/qemu/libvirtd_qemu.aug | 2 + src/qemu/qemu.conf | 6 +++ src/qemu/qemu_capabilities.c | 5 ++ src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_command.c | 60 +++++++++++++++++++++- src/qemu/qemu_command.h | 3 ++ src/qemu/qemu_conf.c | 32 ++++++++++++ src/qemu/qemu_conf.h | 6 +++ src/qemu/qemu_driver.c | 5 ++ src/qemu/qemu_process.c | 44 ++++++++++++---- src/qemu/test_libvirtd_qemu.aug.in | 2 + tests/qemuargv2xmltest.c | 1 + .../qemuxml2argv-graphics-vnc-websocket.args | 4 ++ .../qemuxml2argv-graphics-vnc-websocket.xml | 28 ++++++++++ tests/qemuxml2argvtest.c | 1 + tests/qemuxml2xmltest.c | 1 + 20 files changed, 216 insertions(+), 12 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-graphics-vnc-websocket.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-graphics-vnc-websocket.xml -- 1.8.2.1

Adding support for new attribute 'websocket' in the '<graphics>' element, the attribute value is the port to listen on with '-1' meaning auto-allocation, '0' meaning no websockets. Signed-off-by: Martin Kletzander <mkletzan@redhat.com> --- docs/formatdomain.html.in | 5 +++++ docs/schemas/domaincommon.rng | 5 +++++ src/conf/domain_conf.c | 16 ++++++++++++++++ src/conf/domain_conf.h | 1 + 4 files changed, 27 insertions(+) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 9ade507..e5b69f8 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -3589,6 +3589,11 @@ qemu-kvm -net nic,model=? /dev/null Rather than using listen/port, QEMU supports a <code>socket</code> attribute for listening on a unix domain socket path.<span class="since">Since 0.8.8</span> + + For VNC WebSocket functionality, <code>websocket</code> + attribute may be used to specify port to listen on (with -1 + meaning auto-allocation). <span class="since">Since + 1.0.6</span> </dd> <dt><code>"spice"</code></dt> <dd> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 8004428..472bcd6 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2103,6 +2103,11 @@ </attribute> </optional> <optional> + <attribute name="websocket"> + <ref name="PortNumber"/> + </attribute> + </optional> + <optional> <attribute name="listen"> <ref name="addrIPorName"/> </attribute> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 862b997..7279827 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -7667,6 +7667,7 @@ virDomainGraphicsDefParseXML(xmlNodePtr node, if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) { char *port = virXMLPropString(node, "port"); + char *websocket = virXMLPropString(node, "websocket"); char *autoport; if (port) { @@ -7697,6 +7698,18 @@ virDomainGraphicsDefParseXML(xmlNodePtr node, VIR_FREE(autoport); } + if (websocket) { + if (virStrToLong_i(websocket, + NULL, 10, + &def->data.vnc.websocket) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse vnc WebSocket port %s"), websocket); + VIR_FREE(websocket); + goto error; + } + VIR_FREE(websocket); + } + def->data.vnc.socket = virXMLPropString(node, "socket"); def->data.vnc.keymap = virXMLPropString(node, "keymap"); @@ -15173,6 +15186,9 @@ virDomainGraphicsDefFormat(virBufferPtr buf, virBufferAsprintf(buf, " autoport='%s'", def->data.vnc.autoport ? "yes" : "no"); + if (def->data.vnc.websocket) + virBufferAsprintf(buf, " websocket='%d'", def->data.vnc.websocket); + if (listenAddr) virBufferAsprintf(buf, " listen='%s'", listenAddr); } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index a9d3410..0d61579 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1411,6 +1411,7 @@ struct _virDomainGraphicsDef { union { struct { int port; + int websocket; bool autoport; char *keymap; char *socket; -- 1.8.2.1

On 05/13/2013 09:10 AM, Martin Kletzander wrote:
Adding support for new attribute 'websocket' in the '<graphics>' element, the attribute value is the port to listen on with '-1' meaning auto-allocation, '0' meaning no websockets.
Signed-off-by: Martin Kletzander <mkletzan@redhat.com> --- docs/formatdomain.html.in | 5 +++++ docs/schemas/domaincommon.rng | 5 +++++ src/conf/domain_conf.c | 16 ++++++++++++++++ src/conf/domain_conf.h | 1 + 4 files changed, 27 insertions(+)
ACK John

On 05/13/2013 08:38 PM, John Ferlan wrote:
On 05/13/2013 09:10 AM, Martin Kletzander wrote:
Adding support for new attribute 'websocket' in the '<graphics>' element, the attribute value is the port to listen on with '-1' meaning auto-allocation, '0' meaning no websockets.
Signed-off-by: Martin Kletzander <mkletzan@redhat.com> --- docs/formatdomain.html.in | 5 +++++ docs/schemas/domaincommon.rng | 5 +++++ src/conf/domain_conf.c | 16 ++++++++++++++++ src/conf/domain_conf.h | 1 + 4 files changed, 27 insertions(+)
ACK
John
Thanks, pushed. Martin

Adding a VNC WebSocket support for QEMU driver. This functionality 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@redhat.com> --- src/qemu/libvirtd_qemu.aug | 2 + src/qemu/qemu.conf | 6 +++ src/qemu/qemu_capabilities.c | 5 ++ src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_command.c | 60 +++++++++++++++++++++- src/qemu/qemu_command.h | 3 ++ src/qemu/qemu_conf.c | 32 ++++++++++++ src/qemu/qemu_conf.h | 6 +++ src/qemu/qemu_driver.c | 5 ++ src/qemu/qemu_process.c | 44 ++++++++++++---- src/qemu/test_libvirtd_qemu.aug.in | 2 + tests/qemuargv2xmltest.c | 1 + .../qemuxml2argv-graphics-vnc-websocket.args | 4 ++ .../qemuxml2argv-graphics-vnc-websocket.xml | 28 ++++++++++ tests/qemuxml2argvtest.c | 1 + tests/qemuxml2xmltest.c | 1 + 16 files changed, 189 insertions(+), 12 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 a3dcb30..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" diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index 0f0a24c..cdf1ec4 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf @@ -153,6 +153,12 @@ #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. +# +#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 74ac43c..18ebe14 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -227,6 +227,7 @@ VIR_ENUM_IMPL(virQEMUCaps, QEMU_CAPS_LAST, "scsi-generic", "scsi-generic.bootindex", /* 145 */ + "vnc-websocket", ); struct _virQEMUCaps { @@ -2567,6 +2568,10 @@ virQEMUCapsInitQMP(virQEMUCapsPtr qemuCaps, if (qemuCaps->version >= 1003000) virQEMUCapsSet(qemuCaps, QEMU_CAPS_MACHINE_USB_OPT); + /* WebSockets were introduced between 1.3.0 and 1.3.1 */ + if (qemuCaps->version >= 1003001) + virQEMUCapsSet(qemuCaps, QEMU_CAPS_VNC_WEBSOCKET); + if (virQEMUCapsProbeQMPCommands(qemuCaps, mon) < 0) goto cleanup; if (virQEMUCapsProbeQMPEvents(qemuCaps, mon) < 0) diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index a9eea4e..4b07b75 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -184,6 +184,7 @@ enum virQEMUCapsFlags { QEMU_CAPS_VFIO_PCI_BOOTINDEX = 143, /* bootindex param for vfio-pci device */ QEMU_CAPS_DEVICE_SCSI_GENERIC = 144, /* -device scsi-generic */ QEMU_CAPS_DEVICE_SCSI_GENERIC_BOOTINDEX = 145, /* -device scsi-generic.bootindex */ + QEMU_CAPS_VNC_WEBSOCKET = 146, /* -vnc x:y,websocket */ 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 eddc263..7d80e74 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -6070,6 +6070,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) @@ -9888,6 +9899,7 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr qemuCaps, * -vnc some.host.name:4 */ char *opts; + char *port; const char *sep = ":"; if (val[0] == '[') sep = "]:"; @@ -9898,11 +9910,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] == '[') @@ -9915,6 +9928,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 ba42bb9..36dfa6b 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -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 c16b90d..a625ae7 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -227,6 +227,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 @@ -403,6 +406,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 ab52693..8a090bc 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 d7b0aee..57cc003 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, @@ -3467,13 +3490,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; @@ -4155,10 +4174,15 @@ retry: */ 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.autoport) { - ignore_value(virPortAllocatorRelease(driver->remotePorts, - graphics->data.vnc.port)); + if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) { + if (graphics->data.vnc.autoport) { + ignore_value(virPortAllocatorRelease(driver->remotePorts, + graphics->data.vnc.port)); + } + if (graphics->data.vnc.websocket) { + ignore_value(virPortAllocatorRelease(driver->webSocketPorts, + graphics->data.vnc.port)); + } } if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE && graphics->data.spice.autoport) { diff --git a/src/qemu/test_libvirtd_qemu.aug.in b/src/qemu/test_libvirtd_qemu.aug.in index 26ca068..d4e4fae 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 005aa0c..15ee757 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..b0c59b1 --- /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 -net none -serial none -parallel none -vnc 127.0.0.1:0,websocket=5700 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-vnc-websocket.xml b/tests/qemuxml2argvdata/qemuxml2argv-graphics-vnc-websocket.xml new file mode 100644 index 0000000..dd0bb57 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-vnc-websocket.xml @@ -0,0 +1,28 @@ +<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> + <controller type='usb' index='0'/> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <graphics type='vnc' port='5900' autoport='no' websocket='5700' listen='127.0.0.1'> + <listen type='address' address='127.0.0.1'/> + </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 98ceb83..8e59f2c 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -599,6 +599,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 492ac60..550e7f9 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -187,6 +187,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

On 05/13/2013 09:10 PM, Martin Kletzander wrote:
Adding a VNC WebSocket support for QEMU driver. This functionality 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@redhat.com> --- src/qemu/libvirtd_qemu.aug | 2 + src/qemu/qemu.conf | 6 +++ src/qemu/qemu_capabilities.c | 5 ++ src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_command.c | 60 +++++++++++++++++++++- src/qemu/qemu_command.h | 3 ++ src/qemu/qemu_conf.c | 32 ++++++++++++ src/qemu/qemu_conf.h | 6 +++ src/qemu/qemu_driver.c | 5 ++ src/qemu/qemu_process.c | 44 ++++++++++++---- src/qemu/test_libvirtd_qemu.aug.in | 2 + tests/qemuargv2xmltest.c | 1 + .../qemuxml2argv-graphics-vnc-websocket.args | 4 ++ .../qemuxml2argv-graphics-vnc-websocket.xml | 28 ++++++++++ tests/qemuxml2argvtest.c | 1 + tests/qemuxml2xmltest.c | 1 + 16 files changed, 189 insertions(+), 12 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 a3dcb30..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" diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index 0f0a24c..cdf1ec4 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf @@ -153,6 +153,12 @@ #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. +# +#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 74ac43c..18ebe14 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -227,6 +227,7 @@ VIR_ENUM_IMPL(virQEMUCaps, QEMU_CAPS_LAST, "scsi-generic",
"scsi-generic.bootindex", /* 145 */ + "vnc-websocket", );
struct _virQEMUCaps { @@ -2567,6 +2568,10 @@ virQEMUCapsInitQMP(virQEMUCapsPtr qemuCaps, if (qemuCaps->version >= 1003000) virQEMUCapsSet(qemuCaps, QEMU_CAPS_MACHINE_USB_OPT);
+ /* WebSockets were introduced between 1.3.0 and 1.3.1 */ + if (qemuCaps->version >= 1003001) + virQEMUCapsSet(qemuCaps, QEMU_CAPS_VNC_WEBSOCKET); + if (virQEMUCapsProbeQMPCommands(qemuCaps, mon) < 0) goto cleanup; if (virQEMUCapsProbeQMPEvents(qemuCaps, mon) < 0) diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index a9eea4e..4b07b75 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -184,6 +184,7 @@ enum virQEMUCapsFlags { QEMU_CAPS_VFIO_PCI_BOOTINDEX = 143, /* bootindex param for vfio-pci device */ QEMU_CAPS_DEVICE_SCSI_GENERIC = 144, /* -device scsi-generic */ QEMU_CAPS_DEVICE_SCSI_GENERIC_BOOTINDEX = 145, /* -device scsi-generic.bootindex */ + QEMU_CAPS_VNC_WEBSOCKET = 146, /* -vnc x:y,websocket */
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 eddc263..7d80e74 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -6070,6 +6070,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); + } +
I think the above block should be moved in if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_VNC_COLON)) { if (graphics->data.vnc.websocket) ... }
@@ -9915,6 +9928,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);
missing goto error, but still nice parsing The rest is good for me. I use noVNC to connect it, it works well. Right now, the 'autoport' attribute only manages automatic port allocation for 'port'. 'websocket' doesn't use it, -1 means auto port allocation itself. If we should change doc to avoid confusing. ACK with above fixed. Guannan

On 05/14/2013 09:15 AM, Guannan Ren wrote:
On 05/13/2013 09:10 PM, Martin Kletzander wrote: [...]
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index eddc263..7d80e74 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -6070,6 +6070,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); + } +
I think the above block should be moved in if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_VNC_COLON)) { if (graphics->data.vnc.websocket) ... }
That would mean there will be no error reported in case WebSocket is requested with old qemu, we just won't format the option. But that's what we do with some other options as well and it should also be added only if vnc is listening on an open port.
@@ -9915,6 +9928,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);
missing goto error, but still nice parsing
The rest is good for me. I use noVNC to connect it, it works well. Right now, the 'autoport' attribute only manages automatic port allocation for 'port'. 'websocket' doesn't use it, -1 means auto port allocation itself. If we should change doc to avoid confusing.
ACK with above fixed.
Guannan
I've added the goto, fixed the upper hunk as well and added info into the documentation about autoport having no effect on websocket. Thanks, pushed now, Martin
participants (3)
-
Guannan Ren
-
John Ferlan
-
Martin Kletzander