The domain XML now understands an attribute named "listenNetwork" in
the <graphics> element, 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 such that when it is
starting up a domain, if it finds "listenNetwork" in the XML, it will
call the network driver to get an associated IP address, and tell qemu
to listen for vnc (or spice) on that address rather than the default
(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. listenNetwork can solve this problem
in the following manner:
1) on each host, define a libvirt network of the same name,
associated with some interface on that host (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' autoport='yes'
listenNetwork='example-net'/>
(all the above also applies for graphics type='spice').
Since this is the commit that turns on the new functionality, I've
included the doc changes here.
---
docs/formatdomain.html.in | 22 ++++++++++++++---
src/qemu/qemu_command.c | 55 ++++++++++++++++++++++++++++++++++++++++-----
src/qemu/qemu_hotplug.c | 12 +++++++++
3 files changed, 79 insertions(+), 10 deletions(-)
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index 8f42ba9..2788190 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -1934,8 +1934,15 @@ qemu-kvm -net nic,model=? /dev/null
auto-allocated). The <code>autoport</code> attribute is
the new preferred syntax for indicating autoallocation of
the TCP port to use. The <code>listen</code> attribute is
- an IP address for the server to listen
- on. The <code>passwd</code> attribute provides a VNC
+ an IP address for the server to listen on.
+ Alternately, a <code>listenNetwork</code> attribute can be
+ specified instead<span class="since">Since
0.9.4</span>;
+ the named network will be looked up in the list of
+ networks managed by libvirt and, if found, the VNC server
+ will listen on the IP address associated with the physical
+ device used by that network (e.g. the bridge device or the
+ direct mode forward <code>dev</code>).
+ The <code>passwd</code> attribute provides a VNC
password in clear text. The <code>keymap</code> attribute
specifies the keymap to use. It is possible to set a limit
on the validity of the password be giving an
@@ -1959,8 +1966,15 @@ qemu-kvm -net nic,model=? /dev/null
port number. The <code>autoport</code> attribute is the
new preferred syntax for indicating autoallocation of
both port numbers. The <code>listen</code> attribute is
- an IP address for the server to listen
- on. The <code>passwd</code> attribute provides a SPICE
+ an IP address for the server to listen on.
+ Alternately, a <code>listenNetwork</code> attribute can be
+ specified instead<span class="since">Since
0.9.4</span>;
+ the named network will be looked up in the list of
+ networks managed by libvirt and, if found, the VNC server
+ will listen on the IP address associated with the physical
+ device used by that network (e.g. the bridge device or the
+ direct mode forward <code>dev</code>).
+ The <code>passwd</code> attribute provides a SPICE
password in clear text. The <code>keymap</code>
attribute specifies the keymap to use. It is possible to
set a limit on the validity of the password be giving an
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 0ae8d67..bccb984 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -4116,16 +4116,40 @@ qemuBuildCommandLine(virConnectPtr conn,
def->graphics[0]->data.vnc.socket);
} else if (qemuCapsGet(qemuCaps, QEMU_CAPS_VNC_COLON)) {
- const char *addr = def->graphics[0]->data.vnc.listenAddr ?
- def->graphics[0]->data.vnc.listenAddr :
- driver->vncListen;
- bool escapeAddr = strchr(addr, ':') != NULL;
+ int ret;
+ char *addr;
+ bool freeAddr = false;
+ bool escapeAddr;
+
+ if (def->graphics[0]->data.vnc.listenAddr) {
+ addr = def->graphics[0]->data.vnc.listenAddr;
+ } else if (def->graphics[0]->data.vnc.listenNetwork) {
+ ret =
networkGetNetworkAddress(def->graphics[0]->data.vnc.listenNetwork,
+ &addr);
+ if (ret <= -2) {
+ qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ "%s", _("listenNetwork not supported,
network driver not present"));
+ }
+ if (!addr) {
+ qemuReportError(VIR_ERR_XML_ERROR,
+ _("listenNetwork '%s' had no usable
address"),
+ def->graphics[0]->data.vnc.listenNetwork);
+ goto error;
+ }
+ freeAddr = true;
+ } else {
+ addr = driver->vncListen;
+ }
+
+ escapeAddr = strchr(addr, ':') != NULL;
if (escapeAddr)
virBufferAsprintf(&opt, "[%s]", addr);
else
virBufferAdd(&opt, addr, -1);
virBufferAsprintf(&opt, ":%d",
def->graphics[0]->data.vnc.port - 5900);
+ if (freeAddr)
+ VIR_FREE(addr);
} else {
virBufferAsprintf(&opt, "%d",
@@ -4222,10 +4246,29 @@ qemuBuildCommandLine(virConnectPtr conn,
if (driver->spiceTLS && def->graphics[0]->data.spice.tlsPort !=
-1)
virBufferAsprintf(&opt, ",tls-port=%u",
def->graphics[0]->data.spice.tlsPort);
- if (def->graphics[0]->data.spice.listenAddr)
+ if (def->graphics[0]->data.spice.listenAddr) {
virBufferAsprintf(&opt, ",addr=%s",
def->graphics[0]->data.spice.listenAddr);
- else if (driver->spiceListen)
+ } else if (def->graphics[0]->data.spice.listenNetwork) {
+ int ret;
+ char *addr;
+
+ ret =
networkGetNetworkAddress(def->graphics[0]->data.spice.listenNetwork,
+ &addr);
+ if (ret <= -2) {
+ qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ "%s", _("listenNetwork not supported,
network driver not present"));
+ }
+ if (!addr) {
+ qemuReportError(VIR_ERR_XML_ERROR,
+ _("listenNetwork '%s' had no usable
address"),
+ def->graphics[0]->data.spice.listenNetwork);
+ goto error;
+ }
+ virBufferAsprintf(&opt, ",addr=%s", addr);
+ VIR_FREE(addr);
+ } else if (driver->spiceListen) {
virBufferAsprintf(&opt, ",addr=%s", driver->spiceListen);
+ }
/* 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
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index 74ce9be..09023b0 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -1075,6 +1075,12 @@ qemuDomainChangeGraphics(struct qemud_driver *driver,
_("cannot change listen address setting on vnc
graphics"));
return -1;
}
+ if (STRNEQ_NULLABLE(olddev->data.vnc.listenNetwork,
+ dev->data.vnc.listenNetwork)) {
+ 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"));
@@ -1124,6 +1130,12 @@ qemuDomainChangeGraphics(struct qemud_driver *driver,
_("cannot change listen address setting on spice
graphics"));
return -1;
}
+ if (STRNEQ_NULLABLE(olddev->data.spice.listenNetwork,
+ dev->data.spice.listenNetwork)) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot change listen address setting on spice
graphics"));
+ return -1;
+ }
if (STRNEQ_NULLABLE(olddev->data.spice.keymap,
dev->data.spice.keymap)) {
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
--
1.7.3.4