[libvirt] [PATCH 0/4] support the listenNetwork attribute in <graphics>

This patch series resolves the following bug: https://bugzilla.redhat.com/show_bug.cgi?id=703851 In short, it implements support for a new "listenNetwork" attribute in the domain's <graphics> element. When listenNetwork is specified, libvirt will look for a network of that name (from the networks that can be created within libvirt), and derive a listen address from the physical interface used to connect that network to the outside world. (More details are given in each commit message). This patch series is meant to be applied on top of the series I sent on Tuesday: network: physical device abstraction aka 'virtual switch' https://www.redhat.com/archives/libvir-list/2011-July/msg00149.html Don't attempt to apply it by itself! Note that I'm not particularly fond of the attribute name (it's too long and has the ugly capital letter in the middle). If anyone has a suggestion for something better, please give your opinion now, before it's pushed and becomes permanent!

This function uses ioctl(SIOCGIFADDR), which limits it to returning the first IPv4 address of an interface, but that's what we want right now (the place we're going to use the address only accepts one). --- src/libvirt_private.syms | 1 + src/util/interface.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++ src/util/interface.h | 3 ++ 3 files changed, 64 insertions(+), 0 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 4ef20bc..f89d92e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -506,6 +506,7 @@ ifaceCtrl; ifaceGetFlags; ifaceGetIndex; ifaceGetMacAddress; +ifaceGetIPAddress; ifaceGetNthParent; ifaceGetVlanID; ifaceIsUp; diff --git a/src/util/interface.c b/src/util/interface.c index 837ecce..a714bad 100644 --- a/src/util/interface.c +++ b/src/util/interface.c @@ -29,6 +29,7 @@ #include <sys/socket.h> #include <sys/ioctl.h> #include <fcntl.h> +#include <netinet/in.h> #ifdef __linux__ # include <linux/if.h> @@ -511,6 +512,65 @@ ifaceSetMacAddress(const char *ifname ATTRIBUTE_UNUSED, /** + * ifaceGetIPAddress: + * @ifname: name of the interface whose IP address we want + * @macaddr: MAC address (VIR_MAC_BUFLEN in size) + * + * This function gets the @macaddr for a given interface @ifname. + * + * Returns 0 in case of success or an errno code in case of failure. + */ +#ifdef __linux__ +int +ifaceGetIPAddress(const char *ifname, + virSocketAddrPtr addr) +{ + struct ifreq ifr; + int fd; + int rc = 0; + + if (!ifname || !addr) + return EINVAL; + + memset (addr, 0, sizeof(*addr)); + addr->data.stor.ss_family = AF_UNSPEC; + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) + return errno; + + memset(&ifr, 0, sizeof(struct ifreq)); + if (virStrcpyStatic(ifr.ifr_name, ifname) == NULL) { + rc = EINVAL; + goto err_exit; + } + + if (ioctl(fd, SIOCGIFADDR, (char *)&ifr) != 0) { + rc = errno; + goto err_exit; + } + + addr->data.stor.ss_family = AF_INET; + addr->len = sizeof(addr->data.inet4); + memcpy(&addr->data.inet4, &ifr.ifr_addr, addr->len); + +err_exit: + VIR_FORCE_CLOSE(fd); + return rc; +} + +#else + +int +ifaceGetIPAddress(const char *ifname ATTRIBUTE_UNUSED, + virSocketAddrPtr addr ATTRIBUTE_UNUSED) +{ + return ENOSYS; +} + +#endif /* __linux__ */ + +/** * ifaceLinkAdd * * @type: The type of device, i.e., "macvtap" diff --git a/src/util/interface.h b/src/util/interface.h index 5cbf5c1..9647653 100644 --- a/src/util/interface.h +++ b/src/util/interface.h @@ -24,6 +24,7 @@ struct nlattr; # endif # include "datatypes.h" +# include "network.h" int ifaceGetFlags(const char *name, short *flags); int ifaceIsUp(const char *name, bool *up); @@ -49,6 +50,8 @@ int ifaceSetMacAddress(const char *ifname, const unsigned char *macaddr); int ifaceGetMacAddress(const char *ifname, unsigned char *macaddr); +int ifaceGetIPAddress(const char *ifname, virSocketAddrPtr addr); + int ifaceMacvtapLinkAdd(const char *type, const unsigned char *macaddress, int macaddrsize, const char *ifname, -- 1.7.3.4

The new listenNetwork atribute needs to learn an IP address based on a named network. This patch provides a function networkGetNetworkAddress which provides that. Some networks have an IP address explicitly in their configuration (ie, those with a forward type of "none", "route", or "nat"). For those, we can just return the IP address from the config. The rest will have a physical device associated with them (either via <bridge name='...'/>, <forward ... dev='...'/>, or possibly via a pool of interfaces inside the network's <forward> element) and we will need to ask the kernel for the current IP address of that device (via the newly added ifaceGetIPAddress --- src/libvirt_network.syms | 1 + src/network/bridge_driver.c | 98 +++++++++++++++++++++++++++++++++++++++++++ src/network/bridge_driver.h | 2 + 3 files changed, 101 insertions(+), 0 deletions(-) diff --git a/src/libvirt_network.syms b/src/libvirt_network.syms index e402b5f..1fe8902 100644 --- a/src/libvirt_network.syms +++ b/src/libvirt_network.syms @@ -5,5 +5,6 @@ # bridge_driver.h networkAllocateActualDevice; networkBuildDhcpDaemonCommandLine; +networkGetNetworkAddress; networkNotifyActualDevice; networkReleaseActualDevice; diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 56c77f2..a7453ea 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -55,6 +55,7 @@ #include "uuid.h" #include "iptables.h" #include "bridge.h" +#include "interface.h" #include "logging.h" #include "dnsmasq.h" #include "util/network.h" @@ -3102,3 +3103,100 @@ cleanup: iface->data.network.actual = NULL; return ret; } + +/* + * networkGetNetworkAddress: + * @netname: the name of a network + * + * Attempt to return the IP (v4) address associated with the named + * network. If a libvirt virtual network, that will be provided in the + * configuration. For host bridge and direct (macvtap) networks, we + * must do an ioctl to learn the address. + * + * Note: This function returns the 1st IPv4 address it finds. It might + * be useful if it was more flexible, but the current use (getting a + * listen address for qemu's vnc/spice graphics server) can only use a + * single address anyway. + * + * Returns a string (which must be free'd by the caller) on success, + * or NULL on failure. + */ +char * +networkGetNetworkAddress(const char *netname) +{ + struct network_driver *driver = driverState; + virNetworkObjPtr network = NULL; + virNetworkDefPtr netdef; + virNetworkIpDefPtr ipdef; + virSocketAddr addr; + virSocketAddrPtr addrptr = NULL; + char *devname = NULL; + char *addrstr = NULL; + + networkDriverLock(driver); + network = virNetworkFindByName(&driver->networks, netname); + networkDriverUnlock(driver); + if (!network) { + networkReportError(VIR_ERR_NO_NETWORK, + _("no network with matching name '%s'"), + netname); + goto cleanup; + } + netdef = network->def; + + switch (netdef->forwardType) { + case VIR_NETWORK_FORWARD_NONE: + case VIR_NETWORK_FORWARD_NAT: + case VIR_NETWORK_FORWARD_ROUTE: + /* if there's an ipv4def, get it's address */ + ipdef = virNetworkDefGetIpByIndex(netdef, AF_INET, 0); + if (!ipdef) { + networkReportError(VIR_ERR_INTERNAL_ERROR, + _("network '%s' doesn't have an IP address"), + netdef->name); + break; + } + addrptr = &ipdef->address; + break; + + case VIR_NETWORK_FORWARD_BRIDGE: + if ((devname = netdef->bridge)) + break; + /* + * If netdef->bridge wasn't set, this is a direct-mode + * interface, so purposefully drop through to the next case + */ + case VIR_NETWORK_FORWARD_PRIVATE: + case VIR_NETWORK_FORWARD_VEPA: + case VIR_NETWORK_FORWARD_PASSTHROUGH: + if (netdef->nForwardIfs == 0) + devname = netdef->forwardDev; + else if (netdef->forwardIfs) + devname = netdef->forwardIfs[0].dev; + + if (!devname) { + networkReportError(VIR_ERR_INTERNAL_ERROR, + _("network '%s' has no associated interface or bridge"), + netdef->name); + } + break; + } + + if (devname) { + if (ifaceGetIPAddress(devname, &addr)) { + virReportSystemError(errno, + _("Failed to get IP address for '%s' (network '%s')"), + devname, netdef->name); + } else { + addrptr = &addr; + } + } + + if (addrptr) + addrstr = virSocketFormatAddr(addrptr); + +cleanup: + if (network) + virNetworkObjUnlock(network); + return addrstr; +} diff --git a/src/network/bridge_driver.h b/src/network/bridge_driver.h index 4f6a54d..3c76af2 100644 --- a/src/network/bridge_driver.h +++ b/src/network/bridge_driver.h @@ -39,6 +39,8 @@ int networkAllocateActualDevice(virDomainNetDefPtr iface); int networkNotifyActualDevice(virDomainNetDefPtr iface); int networkReleaseActualDevice(virDomainNetDefPtr iface); +char *networkGetNetworkAddress(const char *netname); + int networkBuildDhcpDaemonCommandLine(virNetworkObjPtr network, virCommandPtr *cmdout, char *pidfile, dnsmasqContext *dctx); -- 1.7.3.4

Once it's plugged in, listenNetwork will be an optional replacement for the "listen" attribute. While listen can be a host name or IP address, listenNetwork names one of the networks managed by libvirt (with virNetwork*()/visrh net-*). --- docs/schemas/domain.rng | 33 +++++++++++++++++------ src/conf/domain_conf.c | 67 +++++++++++++++++++++++++++++++++++++++------- src/conf/domain_conf.h | 3 ++ 3 files changed, 83 insertions(+), 20 deletions(-) diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index 65572df..deefe4f 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -1235,9 +1235,14 @@ </attribute> </optional> <optional> - <attribute name="listen"> - <ref name="addrIPorName"/> - </attribute> + <choice> + <attribute name="listen"> + <ref name="addrIPorName"/> + </attribute> + <attribute name="listenNetwork"> + <text/> + </attribute> + </choice> </optional> </group> <group> @@ -1287,9 +1292,14 @@ </attribute> </optional> <optional> - <attribute name="listen"> - <ref name="addrIPorName"/> - </attribute> + <choice> + <attribute name="listen"> + <ref name="addrIPorName"/> + </attribute> + <attribute name="listenNetwork"> + <text/> + </attribute> + </choice> </optional> <optional> <attribute name="passwd"> @@ -1439,9 +1449,14 @@ </attribute> </optional> <optional> - <attribute name="listen"> - <ref name="addrIPorName"/> - </attribute> + <choice> + <attribute name="listen"> + <ref name="addrIPorName"/> + </attribute> + <attribute name="listenNetwork"> + <text/> + </attribute> + </choice> </optional> </group> <group> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index f7a2689..eb9224d 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -617,6 +617,7 @@ void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def) switch (def->type) { case VIR_DOMAIN_GRAPHICS_TYPE_VNC: VIR_FREE(def->data.vnc.listenAddr); + VIR_FREE(def->data.vnc.listenNetwork); VIR_FREE(def->data.vnc.socket); VIR_FREE(def->data.vnc.keymap); virDomainGraphicsAuthDefClear(&def->data.vnc.auth); @@ -629,6 +630,7 @@ void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def) case VIR_DOMAIN_GRAPHICS_TYPE_RDP: VIR_FREE(def->data.rdp.listenAddr); + VIR_FREE(def->data.rdp.listenNetwork); break; case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: @@ -637,6 +639,7 @@ void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def) case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: VIR_FREE(def->data.spice.listenAddr); + VIR_FREE(def->data.spice.listenNetwork); VIR_FREE(def->data.spice.keymap); virDomainGraphicsAuthDefClear(&def->data.spice.auth); break; @@ -4031,13 +4034,22 @@ virDomainGraphicsDefParseXML(xmlNodePtr node, int flags) { } def->data.vnc.listenAddr = virXMLPropString(node, "listen"); - def->data.vnc.socket = virXMLPropString(node, "socket"); - def->data.vnc.keymap = virXMLPropString(node, "keymap"); + if (def->data.vnc.listenAddr && !def->data.vnc.listenAddr[0]) + VIR_FREE(def->data.vnc.listenAddr); - if (def->data.vnc.listenAddr && - !def->data.vnc.listenAddr[0]) + def->data.vnc.listenNetwork = virXMLPropString(node, "listenNetwork"); + if (def->data.vnc.listenNetwork && !def->data.vnc.listenNetwork[0]) VIR_FREE(def->data.vnc.listenAddr); + if (def->data.vnc.listenAddr && def->data.vnc.listenNetwork) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Both listen and listenNetwork given in graphics element")); + goto error; + } + + def->data.vnc.socket = virXMLPropString(node, "socket"); + def->data.vnc.keymap = virXMLPropString(node, "keymap"); + if (virDomainGraphicsAuthDefParseXML(node, &def->data.vnc.auth) < 0) goto error; } else if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) { @@ -4102,10 +4114,19 @@ virDomainGraphicsDefParseXML(xmlNodePtr node, int flags) { } def->data.rdp.listenAddr = virXMLPropString(node, "listen"); - - if (def->data.rdp.listenAddr && - !def->data.rdp.listenAddr[0]) + if (def->data.rdp.listenAddr && !def->data.rdp.listenAddr[0]) VIR_FREE(def->data.rdp.listenAddr); + + def->data.rdp.listenNetwork = virXMLPropString(node, "listenNetwork"); + if (def->data.rdp.listenNetwork && !def->data.rdp.listenNetwork[0]) + VIR_FREE(def->data.rdp.listenNetwork); + + if (def->data.rdp.listenAddr && def->data.rdp.listenNetwork) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Both listen and listenNetwork given in graphics element")); + goto error; + } + } else if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP) { char *fullscreen = virXMLPropString(node, "fullscreen"); @@ -4168,12 +4189,21 @@ virDomainGraphicsDefParseXML(xmlNodePtr node, int flags) { } def->data.spice.listenAddr = virXMLPropString(node, "listen"); - def->data.spice.keymap = virXMLPropString(node, "keymap"); - - if (def->data.spice.listenAddr && - !def->data.spice.listenAddr[0]) + if (def->data.spice.listenAddr && !def->data.spice.listenAddr[0]) VIR_FREE(def->data.spice.listenAddr); + def->data.spice.listenNetwork = virXMLPropString(node, "listenNetwork"); + if (def->data.spice.listenNetwork && !def->data.spice.listenNetwork[0]) + VIR_FREE(def->data.spice.listenNetwork); + + if (def->data.spice.listenAddr && def->data.spice.listenNetwork) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Both listen and listenNetwork given in graphics element")); + goto error; + } + + def->data.spice.keymap = virXMLPropString(node, "keymap"); + if (virDomainGraphicsAuthDefParseXML(node, &def->data.spice.auth) < 0) goto error; @@ -9323,6 +9353,11 @@ virDomainGraphicsDefFormat(virBufferPtr buf, if (def->data.vnc.listenAddr) virBufferAsprintf(buf, " listen='%s'", def->data.vnc.listenAddr); + + if (def->data.vnc.listenNetwork) { + virBufferAsprintf(buf, " listenNetwork='%s'", + def->data.vnc.listenNetwork); + } } if (def->data.vnc.keymap) @@ -9364,6 +9399,11 @@ virDomainGraphicsDefFormat(virBufferPtr buf, if (def->data.rdp.listenAddr) virBufferAsprintf(buf, " listen='%s'", def->data.rdp.listenAddr); + if (def->data.rdp.listenNetwork) { + virBufferAsprintf(buf, " listenNetwork='%s'", + def->data.rdp.listenNetwork); + } + break; case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: @@ -9392,6 +9432,11 @@ virDomainGraphicsDefFormat(virBufferPtr buf, virBufferAsprintf(buf, " listen='%s'", def->data.spice.listenAddr); + if (def->data.spice.listenNetwork) { + virBufferAsprintf(buf, " listenNetwork='%s'", + def->data.spice.listenNetwork); + } + if (def->data.spice.keymap) virBufferEscapeString(buf, " keymap='%s'", def->data.spice.keymap); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 4ce59d8..3745b27 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -759,6 +759,7 @@ struct _virDomainGraphicsDef { int port; unsigned int autoport :1; char *listenAddr; + char *listenNetwork; char *keymap; char *socket; virDomainGraphicsAuthDef auth; @@ -771,6 +772,7 @@ struct _virDomainGraphicsDef { struct { int port; char *listenAddr; + char *listenNetwork; unsigned int autoport :1; unsigned int replaceUser :1; unsigned int multiUser :1; @@ -783,6 +785,7 @@ struct _virDomainGraphicsDef { int port; int tlsPort; char *listenAddr; + char *listenNetwork; char *keymap; virDomainGraphicsAuthDef auth; unsigned int autoport :1; -- 1.7.3.4

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 | 56 ++++++++++++++++++++++++++++++++++++++++----- src/qemu/qemu_hotplug.c | 12 +++++++++ 3 files changed, 80 insertions(+), 10 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 10d87a9..626d163 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -1890,8 +1890,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 @@ -1912,8 +1919,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 0b957cb..89b0da0 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -4097,16 +4097,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; + const 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) { +#if WITH_NETWORK + addr = networkGetNetworkAddress(def->graphics[0]->data.vnc.listenNetwork); + if (!addr) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("listenNetwork '%s' had no usable address"), + def->graphics[0]->data.vnc.listenNetwork); + goto error; + } + freeAddr = true; +#else + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", _("listenNetwork not supported, network driver not present")); + goto error; +#endif + } 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", @@ -4203,10 +4227,30 @@ 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) { +#if WITH_NETWORK + char *addr + = networkGetNetworkAddress(def->graphics[0]->data.spice.listenNetwork); + + if (addr) { + virBufferAsprintf(&opt, ",addr=%s", addr); + VIR_FREE(addr); + } else { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("listenNetwork '%s' had no usable address"), + def->graphics[0]->data.spice.listenNetwork); + goto error; + } +#else + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", _("listenNetwork not supported, network driver not present")); + goto error; +#endif + } 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 5f1a424..303312d 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1078,6 +1078,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

On 07/07/2011 12:44 AM, Laine Stump wrote:
This patch series resolves the following bug:
https://bugzilla.redhat.com/show_bug.cgi?id=703851
In short, it implements support for a new "listenNetwork" attribute in the domain's<graphics> element. When listenNetwork is specified, libvirt will look for a network of that name (from the networks that can be created within libvirt), and derive a listen address from the physical interface used to connect that network to the outside world. (More details are given in each commit message).
I guess it's not possible to reuse the existing 'listen' attribute, by doing context-sensitive parsing to allow either ip address or a network name, because 'listen' already supports host names which get resolved into an ip address. I haven't looked at the series in detail, so maybe that will help me think of a possibly better attribute name.
Note that I'm not particularly fond of the attribute name (it's too long and has the ugly capital letter in the middle). If anyone has a suggestion for something better, please give your opinion now, before it's pushed and becomes permanent!
-- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org
participants (2)
-
Eric Blake
-
Laine Stump