qemu: Add VNC WebSocket support

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.
This commit is contained in:
Martin Kletzander 2013-04-30 16:26:43 +02:00
parent f1ad8d2079
commit 85ec7ff6fd
16 changed files with 190 additions and 12 deletions

View File

@ -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"

View File

@ -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

View File

@ -228,6 +228,7 @@ VIR_ENUM_IMPL(virQEMUCaps, QEMU_CAPS_LAST,
"scsi-generic.bootindex", /* 145 */
"mem-merge",
"vnc-websocket",
);
struct _virQEMUCaps {
@ -2602,6 +2603,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)

View File

@ -185,6 +185,7 @@ enum virQEMUCapsFlags {
QEMU_CAPS_DEVICE_SCSI_GENERIC = 144, /* -device scsi-generic */
QEMU_CAPS_DEVICE_SCSI_GENERIC_BOOTINDEX = 145, /* -device scsi-generic.bootindex */
QEMU_CAPS_MEM_MERGE = 146, /* -machine mem-merge */
QEMU_CAPS_VNC_WEBSOCKET = 147, /* -vnc x:y,websocket */
QEMU_CAPS_LAST, /* this must always be the last item */
};

View File

@ -6076,6 +6076,17 @@ qemuBuildGraphicsVNCCommandLine(virQEMUDriverConfigPtr cfg,
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_VNC_COLON)) {
if (!graphics->data.vnc.socket &&
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);
}
if (graphics->data.vnc.auth.passwd || cfg->vncPassword)
virBufferAddLit(&opt, ",password");
@ -9915,6 +9926,7 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr qemuCaps,
* -vnc some.host.name:4
*/
char *opts;
char *port;
const char *sep = ":";
if (val[0] == '[')
sep = "]:";
@ -9925,11 +9937,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] == '[')
@ -9942,6 +9955,50 @@ 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);
goto error;
}
} 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;
}

View File

@ -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,

View File

@ -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

View File

@ -115,6 +115,9 @@ struct _virQEMUDriverConfig {
int remotePortMin;
int remotePortMax;
int webSocketPortMin;
int webSocketPortMax;
char *hugetlbfsMount;
char *hugepagePath;
char *bridgeHelperName;
@ -212,6 +215,9 @@ struct _virQEMUDriver {
/* Immutable pointer, self-locking APIs */
virPortAllocatorPtr remotePorts;
/* Immutable pointer, self-locking APIs */
virPortAllocatorPtr webSocketPorts;
/* Immutable pointer, lockless APIs*/
virSysinfoDefPtr hostsysinfo;

View File

@ -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;

View File

@ -3236,6 +3236,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,
@ -3470,13 +3493,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;
@ -4154,10 +4173,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) {

View File

@ -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" }

View File

@ -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");

View File

@ -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

View File

@ -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>

View File

@ -605,6 +605,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);

View File

@ -188,6 +188,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");