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

Martin Kletzander mkletzan at redhat.com
Mon Apr 29 14:52:07 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                         |  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




More information about the libvir-list mailing list