mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 13:45:38 +00:00
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:
parent
f1ad8d2079
commit
85ec7ff6fd
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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 */
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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" }
|
||||
|
@ -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");
|
||||
|
@ -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
|
@ -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>
|
@ -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);
|
||||
|
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user