qemu: support type=network in domain graphics <listen>

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' port='5922'>
       <listen type='network'network='example-net'/>
     </graphics>

(all the above also applies for graphics type='spice').
This commit is contained in:
Laine Stump 2011-07-07 02:12:04 -04:00
parent ef79fb5b5f
commit 99e4b30b39
2 changed files with 84 additions and 2 deletions

View File

@ -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 <graphics> 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->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);
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 <graphics> 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 */

View File

@ -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",