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@xxxxxxxxxx> --- 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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list