[libvirt] [PATCH v2 2/2] qemu: Add VNC WebSocket support

Martin Kletzander mkletzan at redhat.com
Tue Apr 30 14:42:17 UTC 2013


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 at redhat.com>
---
 src/qemu/libvirtd_qemu.aug         |  2 ++
 src/qemu/qemu.conf                 |  7 +++++
 src/qemu/qemu_capabilities.c       | 11 +++++--
 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 +
 tests/qemuxml2argvtest.c           |  1 +
 tests/qemuxml2xmltest.c            |  1 +
 14 files changed, 153 insertions(+), 12 deletions(-)

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..9257d3d 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..9e5eedf 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -222,9 +222,10 @@ VIR_ENUM_IMPL(virQEMUCaps, QEMU_CAPS_LAST,
               "tpm-tis",

               "nvram",  /* 140 */
-              "pci-bridge", /* 141 */
-              "vfio-pci", /* 142 */
-              "vfio-pci.bootindex", /* 143 */
+              "pci-bridge",
+              "vfio-pci",
+              "vfio-pci.bootindex",
+              "vnc-websocket",
     );

 struct _virQEMUCaps {
@@ -2520,6 +2521,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 (!(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 3184e5b..f718434 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -5903,6 +5903,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)
@@ -9726,6 +9737,7 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr qemuCaps,
                  * -vnc some.host.name:4
                  */
                 char *opts;
+                char *port;
                 const char *sep = ":";
                 if (val[0] == '[')
                     sep = "]:";
@@ -9736,11 +9748,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] == '[')
@@ -9753,6 +9766,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 296efe3..cf0bc55 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 e75c8c9..296e9b3 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,
@@ -3448,13 +3471,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 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 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/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




More information about the libvir-list mailing list