]> xenbits.xensource.com Git - libvirt.git/commitdiff
qemu: Add VNC WebSocket support
authorMartin Kletzander <mkletzan@redhat.com>
Tue, 30 Apr 2013 14:26:43 +0000 (16:26 +0200)
committerMartin Kletzander <mkletzan@redhat.com>
Wed, 15 May 2013 07:48:05 +0000 (09:48 +0200)
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.

16 files changed:
src/qemu/libvirtd_qemu.aug
src/qemu/qemu.conf
src/qemu/qemu_capabilities.c
src/qemu/qemu_capabilities.h
src/qemu/qemu_command.c
src/qemu/qemu_command.h
src/qemu/qemu_conf.c
src/qemu/qemu_conf.h
src/qemu/qemu_driver.c
src/qemu/qemu_process.c
src/qemu/test_libvirtd_qemu.aug.in
tests/qemuargv2xmltest.c
tests/qemuxml2argvdata/qemuxml2argv-graphics-vnc-websocket.args [new file with mode: 0644]
tests/qemuxml2argvdata/qemuxml2argv-graphics-vnc-websocket.xml [new file with mode: 0644]
tests/qemuxml2argvtest.c
tests/qemuxml2xmltest.c

index a3dcb30d22058389fbe4622dc9f49d0cf0eb9f4b..5344125b21e290788fe7674fd54e61284e6adb0c 100644 (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"
index 0f0a24c20ef4eaee1a6eead466370edc0b6b892d..cdf1ec4cbf8a0baacdac81685dc95d40b0b2eeda 100644 (file)
 #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
index c711e92c056235ccc81cd001b0d69d1c00b4a464..49cb7a664562fa9c39b1503f1704df5431c3812d 100644 (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)
index 477e526ad7f3f85bf7863d79c044677b6f55a5f0..609f3f939035399407af002a0be03d6699e537b5 100644 (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 */
 };
index 5941f706bdb334ed37f5bbd61739448df86e272a..588316649e9004a4a2c7371f498997a250bdf540 100644 (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;
             }
index ba42bb9eccb2a3b222874180170332465b8bb3f5..36dfa6bc9f0ef2370a61e30a8251dcc0ddf8c2ea 100644 (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,
index c16b90d3feedfbb7bbc0772df86a49b9dd6c84d4..a625ae7a7684277e5af2ecb2e63c775fd8655131 100644 (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
index 30a2a2209416eefc9dee0cd6c469e96cff4fc667..cdfb98559484c84e241512ec75a2e12f50a603f0 100644 (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;
 
index 9453c2284a5d2e5945feaa91504a55599e23b417..522556810849bf6d69103d94f5d2f9ac7e3d665f 100644 (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;
 
index 6499f4af96a44f99348f89216d7029e8adcaeeb4..dbbb7bf983363c5c573b65c8243733fa2a6a075d 100644 (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) {
index 26ca0688d81ad9ff2b1149637b82b75b727f398d..d4e4fae717b898a33eb36a1b02c6f0bd0eb55aaa 100644 (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" }
index 528b8bf65f465afbf63c18d073af6b03d1c20169..58fabdb0ad7f45118f0972dab9e63ea7bdf567f6 100644 (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");
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-vnc-websocket.args b/tests/qemuxml2argvdata/qemuxml2argv-graphics-vnc-websocket.args
new file mode 100644 (file)
index 0000000..b0c59b1
--- /dev/null
@@ -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 (file)
index 0000000..dd0bb57
--- /dev/null
@@ -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>
index a2226f8dcf2a4966f5c7ffe929c36c6cebd83e6a..d0b49b0fe4672c53bfd6241e52303e339ade8b91 100644 (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);
index ddf3230d38be498c807bf5937520d0fcc68e8908..92b7383fbeb5cce9c1d2d7a0807766ced5e80e9f 100644 (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");