From 99e4b30b39558d15e23f7c453551f7100c363244 Mon Sep 17 00:00:00 2001 From: Laine Stump Date: Thu, 7 Jul 2011 02:12:04 -0400 Subject: [PATCH] qemu: support type=network in domain graphics The domain XML now understands the subelement of its element (including when listen type='network'), and the network driver has an internal API that will turn a network name into an IP address, so the final logical step is to put the glue into the qemu driver so that when it is starting up a domain, if it finds in the XML, it will call the network driver to get an IPv4 address associated with network xyz, and tell qemu to listen for vnc (or spice) on that address rather than the default address (localhost). The motivation for this is that a large installation may want the guests' VNC servers listening on physical interfaces rather than localhost, so that users can connect directly from the outside; this requires sending qemu the appropriate IP address to listen on. But this address will of course be different for each host, and if a guest might be migrated around from one host to another, it's important that the guest's config not have any information embedded in it that is specific to one particular host. , or host bridge network: 2) in the element of each guest's domain xml, tell vnc to listen on the network name used in step 1: (all the above also applies for graphics type='spice'). --- src/qemu/qemu_command.c | 73 +++++++++++++++++++++++++++++++++++++++-- src/qemu/qemu_hotplug.c | 13 ++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 740468f859..ee42f1db72 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -4127,10 +4127,43 @@ qemuBuildCommandLine(virConnectPtr conn, def->graphics[0]->data.vnc.socket); } else if (qemuCapsGet(qemuCaps, QEMU_CAPS_VNC_COLON)) { + const char *listenNetwork; const char *listenAddr = NULL; + char *netAddr = NULL; bool escapeAddr; + int ret; + + switch (virDomainGraphicsListenGetType(def->graphics[0], 0)) { + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS: + listenAddr = virDomainGraphicsListenGetAddress(def->graphics[0], 0); + break; + + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK: + listenNetwork = virDomainGraphicsListenGetNetwork(def->graphics[0], 0); + if (!listenNetwork) + break; + ret = networkGetNetworkAddress(listenNetwork, &netAddr); + if (ret <= -2) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", _("network-based listen not possible, " + "network driver not present")); + goto error; + } + if (ret < 0) { + qemuReportError(VIR_ERR_XML_ERROR, + _("listen network '%s' had no usable address"), + listenNetwork); + goto error; + } + listenAddr = netAddr; + /* store the address we found in the element so it will + * show up in status. */ + if (virDomainGraphicsListenSetAddress(def->graphics[0], 0, + listenAddr, -1, false) < 0) + goto error; + break; + } - listenAddr = virDomainGraphicsListenGetAddress(def->graphics[0], 0); if (!listenAddr) listenAddr = driver->vncListen; @@ -4142,6 +4175,7 @@ qemuBuildCommandLine(virConnectPtr conn, virBufferAsprintf(&opt, ":%d", def->graphics[0]->data.vnc.port - 5900); + VIR_FREE(netAddr); } else { virBufferAsprintf(&opt, "%d", def->graphics[0]->data.vnc.port - 5900); @@ -4225,7 +4259,10 @@ qemuBuildCommandLine(virConnectPtr conn, } else if ((def->ngraphics == 1) && def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { virBuffer opt = VIR_BUFFER_INITIALIZER; + const char *listenNetwork; const char *listenAddr = NULL; + char *netAddr = NULL; + int ret; if (!qemuCapsGet(qemuCaps, QEMU_CAPS_SPICE)) { qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", @@ -4238,12 +4275,44 @@ qemuBuildCommandLine(virConnectPtr conn, if (driver->spiceTLS && def->graphics[0]->data.spice.tlsPort != -1) virBufferAsprintf(&opt, ",tls-port=%u", def->graphics[0]->data.spice.tlsPort); - listenAddr = virDomainGraphicsListenGetAddress(def->graphics[0], 0); + switch (virDomainGraphicsListenGetType(def->graphics[0], 0)) { + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS: + listenAddr = virDomainGraphicsListenGetAddress(def->graphics[0], 0); + break; + + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK: + listenNetwork = virDomainGraphicsListenGetNetwork(def->graphics[0], 0); + if (!listenNetwork) + break; + ret = networkGetNetworkAddress(listenNetwork, &netAddr); + if (ret <= -2) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", _("network-based listen not possible, " + "network driver not present")); + goto error; + } + if (ret < 0) { + qemuReportError(VIR_ERR_XML_ERROR, + _("listen network '%s' had no usable address"), + listenNetwork); + goto error; + } + listenAddr = netAddr; + /* store the address we found in the element so it will + * show up in status. */ + if (virDomainGraphicsListenSetAddress(def->graphics[0], 0, + listenAddr, -1, false) < 0) + goto error; + break; + } + if (!listenAddr) listenAddr = driver->spiceListen; if (listenAddr) virBufferAsprintf(&opt, ",addr=%s", listenAddr); + VIR_FREE(netAddr); + /* In the password case we set it via monitor command, to avoid * making it visible on CLI, so there's no use of password=XXX * in this bit of the code */ diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 279f591a62..7dfbfcd0ed 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1053,6 +1053,7 @@ qemuDomainChangeGraphics(struct qemud_driver *driver, { virDomainGraphicsDefPtr olddev = qemuDomainFindGraphics(vm, dev); const char *oldListenAddr, *newListenAddr; + const char *oldListenNetwork, *newListenNetwork; int ret = -1; if (!olddev) { @@ -1063,6 +1064,8 @@ qemuDomainChangeGraphics(struct qemud_driver *driver, oldListenAddr = virDomainGraphicsListenGetAddress(olddev, 0); newListenAddr = virDomainGraphicsListenGetAddress(dev, 0); + oldListenNetwork = virDomainGraphicsListenGetNetwork(olddev, 0); + newListenNetwork = virDomainGraphicsListenGetNetwork(dev, 0); switch (dev->type) { case VIR_DOMAIN_GRAPHICS_TYPE_VNC: @@ -1078,6 +1081,11 @@ qemuDomainChangeGraphics(struct qemud_driver *driver, _("cannot change listen address setting on vnc graphics")); return -1; } + if (STRNEQ_NULLABLE(oldListenNetwork,newListenNetwork)) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot change listen network setting on vnc graphics")); + return -1; + } if (STRNEQ_NULLABLE(olddev->data.vnc.keymap, dev->data.vnc.keymap)) { qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot change keymap setting on vnc graphics")); @@ -1126,6 +1134,11 @@ qemuDomainChangeGraphics(struct qemud_driver *driver, _("cannot change listen address setting on spice graphics")); return -1; } + if (STRNEQ_NULLABLE(oldListenNetwork,newListenNetwork)) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot change listen network setting on vnc graphics")); + return -1; + } if (STRNEQ_NULLABLE(olddev->data.spice.keymap, dev->data.spice.keymap)) { qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",