The domain XML now understands the <listen> subelement of its
<graphics> 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
<listen type='network' network='xyz'/> 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. <listen type='network.../> can solve
this problem in the following manner:
1) on each host, define a libvirt network of the same name,
associated with the interface on that host that should be used
for listening (for example, a simple macvtap network: <forward
mode='bridge' dev='eth0'/>, or host bridge network: <forward
mode='bridge'/> <bridge name='br0'/>
2) in the <graphics> element of each guest's domain xml, tell vnc to
listen on the network name used in step 1:
<graphics type='vnc'>
<listen type='network'network='example-net'
autoport='yes'/>
</graphics>
(all the above also applies for graphics type='spice').
---
src/qemu/qemu_command.c | 65 +++++++++++++++++++++++++++++++++++++++++++++-
src/qemu/qemu_hotplug.c | 13 +++++++++
2 files changed, 76 insertions(+), 2 deletions(-)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 4a39078..9ee7658 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -4111,7 +4111,6 @@ qemuBuildCommandLine(virConnectPtr conn,
if ((def->ngraphics == 1) &&
def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) {
int port = virDomainGraphicsListenGetPort(def->graphics[0], 0);
- const char *listenAddr = virDomainGraphicsListenGetAddress(def->graphics[0],
0);
virBuffer opt = VIR_BUFFER_INITIALIZER;
if (def->graphics[0]->data.vnc.socket ||
@@ -4127,7 +4126,37 @@ 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;
+ break;
+ }
if (!listenAddr)
listenAddr = driver->vncListen;
@@ -4139,6 +4168,7 @@ qemuBuildCommandLine(virConnectPtr conn,
virBufferAdd(&opt, listenAddr, -1);
virBufferAsprintf(&opt, ":%d", port - 5900);
+ VIR_FREE(netAddr);
} else {
virBufferAsprintf(&opt, "%d", port - 5900);
}
@@ -4223,7 +4253,10 @@ qemuBuildCommandLine(virConnectPtr conn,
virBuffer opt = VIR_BUFFER_INITIALIZER;
int port = virDomainGraphicsListenGetPort(def->graphics[0], 0);
int tlsPort = virDomainGraphicsListenGetTlsPort(def->graphics[0], 0);
- const char *listenAddr = virDomainGraphicsListenGetAddress(def->graphics[0],
0);
+ const char *listenNetwork;
+ const char *listenAddr = NULL;
+ char *netAddr = NULL;
+ int ret;
if (!qemuCapsGet(qemuCaps, QEMU_CAPS_SPICE)) {
qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
@@ -4236,11 +4269,39 @@ qemuBuildCommandLine(virConnectPtr conn,
if (driver->spiceTLS && tlsPort != -1)
virBufferAsprintf(&opt, ",tls-port=%u", tlsPort);
+ 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;
+ 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 18b5164..6aa3bef 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -1055,6 +1055,7 @@ qemuDomainChangeGraphics(struct qemud_driver *driver,
int oldPort, newPort, oldTlsPort, newTlsPort;
bool oldAutoport, newAutoport;
const char *oldListenAddr, *newListenAddr;
+ const char *oldListenNetwork, *newListenNetwork;
int ret = -1;
if (!olddev) {
@@ -1071,6 +1072,8 @@ qemuDomainChangeGraphics(struct qemud_driver *driver,
newTlsPort = virDomainGraphicsListenGetTlsPort(dev, 0);
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:
@@ -1086,6 +1089,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"));
@@ -1132,6 +1140,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",
--
1.7.3.4