This is non-intuitively placed as a sub-element of <target>, because it will be used to configure the interface that is named in <target dev='x'/> (which is the interface on the host-side). (The already-existing configuration for the guest-side of interfaces is in subelements directly under <interface>): <interface type='ethernet'> <mac address='00:16:3e:0f:ef:8a'/> <source> <ip address='192.168.122.12' family='ipv4' prefix='24' peer='192.168.122.1'/> <ip address='192.168.122.13' family='ipv4' prefix='24'/> <route family='ipv4' address='0.0.0.0' gateway='192.168.122.1'/> <route family='ipv4' address='192.168.124.0' prefix='24' gateway='192.168.124.1'/> </source> </interface> In practice, this will likely only be useful for type='ethernet', so its presence in any other type of interface is currently forbidden in the generic device Validate function (but it's been put into the general population of virDomainNetDef rather than the ethernet-specific union member so that 1) we can more easily add the capability to other types, and 2) we can retain the info when set to an invalid interface type all the way through to validation and report a proper error, rather than just ignoring it (which is currently what happens for many other type-specific settings). --- docs/formatdomain.html.in | 26 ++++++++ docs/schemas/domaincommon.rng | 3 +- src/conf/domain_conf.c | 97 ++++++++++++++++++++++------ src/conf/domain_conf.h | 3 +- tests/lxcxml2xmldata/lxc-ethernet-hostip.xml | 44 +++++++++++++ tests/lxcxml2xmltest.c | 1 + 6 files changed, 152 insertions(+), 22 deletions(-) create mode 100644 tests/lxcxml2xmldata/lxc-ethernet-hostip.xml diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 2466df7..bb1c079 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -5012,6 +5012,32 @@ qemu-kvm -net nic,model=? /dev/null definitions</a>. This is used by the LXC driver. </p> +<pre> + ... + <devices> + <interface type='ethernet'> + <b><source/></b> + <b><ip address='192.168.123.1' prefix='24'/></b> + <b><ip address='10.0.0.10' prefix='24' peer='192.168.122.5'/></b> + <b><route family='ipv4' address='192.168.42.0' prefix='24' gateway='192.168.123.4'/></b> + <b><source/></b> + ... + </interface> + ... + </devices> + ... +</pre> + + <p> + <span class="since">Since 2.0.0</span> network devices of type + "ethernet" can optionally be provided one or more IP addresses + and one or more routes to set on the <b>host</b> side of the + network device. These are configured as subelements of + the <code><source></code> element of the interface, and + have the same attributes as the similarly named elements used to + configure the guest side of the interface (described above). + </p> + <h5><a name="elementVhostuser">vhost-user interface</a></h5> <p> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 2d12da9..964ff92 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2142,7 +2142,7 @@ <interleave> <optional> <element name="source"> - <empty/> + <ref name="interface-ip-info"/> </element> </optional> <ref name="interface-options"/> @@ -2392,7 +2392,6 @@ <attribute name="dev"> <ref name="deviceName"/> </attribute> - <empty/> </element> </optional> <optional> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index ad2d983..c2e6663 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1800,6 +1800,7 @@ virDomainNetDefClear(virDomainNetDefPtr def) VIR_FREE(def->ifname_guest_actual); virNetDevIPInfoClear(&def->guestIP); + virNetDevIPInfoClear(&def->hostIP); virDomainDeviceInfoClear(&def->info); VIR_FREE(def->filter); @@ -4610,6 +4611,23 @@ virDomainRedirdevDefValidate(const virDomainDef *def, static int +virDomainNetDefValidate(const virDomainNetDef *net) +{ + if ((net->hostIP.nroutes || net->hostIP.nips) && + net->type != VIR_DOMAIN_NET_TYPE_ETHERNET) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Invalid attempt to set network interface " + "host-side IP route and/or address info on " + "interface of type '%s'. This is only supported " + "on interfaces of type 'ethernet'"), + virDomainNetTypeToString(net->type)); + return -1; + } + return 0; +} + + +static int virDomainDeviceDefValidateInternal(const virDomainDeviceDef *dev, const virDomainDef *def) { @@ -4620,9 +4638,11 @@ virDomainDeviceDefValidateInternal(const virDomainDeviceDef *dev, case VIR_DOMAIN_DEVICE_REDIRDEV: return virDomainRedirdevDefValidate(def, dev->data.redirdev); + case VIR_DOMAIN_DEVICE_NET: + return virDomainNetDefValidate(dev->data.net); + case VIR_DOMAIN_DEVICE_LEASE: case VIR_DOMAIN_DEVICE_FS: - case VIR_DOMAIN_DEVICE_NET: case VIR_DOMAIN_DEVICE_INPUT: case VIR_DOMAIN_DEVICE_SOUND: case VIR_DOMAIN_DEVICE_VIDEO: @@ -8977,6 +8997,15 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, cur = node->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { + if (xmlStrEqual(cur->name, BAD_CAST "source")) { + xmlNodePtr tmpnode = ctxt->node; + + ctxt->node = cur; + if (virDomainNetIPInfoParseXML(_("interface host IP"), + ctxt, &def->hostIP) < 0) + goto error; + ctxt->node = tmpnode; + } if (!macaddr && xmlStrEqual(cur->name, BAD_CAST "mac")) { macaddr = virXMLPropString(cur, "address"); } else if (!network && @@ -20682,6 +20711,7 @@ virDomainNetDefFormat(virBufferPtr buf, { unsigned int actualType = virDomainNetGetActualType(def); bool publicActual = false; + int sourceLines = 0; const char *typeStr; virDomainHostdevDefPtr hostdef = NULL; char macstr[VIR_MAC_STRING_BUFLEN]; @@ -20751,15 +20781,7 @@ virDomainNetDefFormat(virBufferPtr buf, def->data.network.name); virBufferEscapeString(buf, " portgroup='%s'", def->data.network.portgroup); - virBufferAddLit(buf, "/>\n"); - - /* ONLY for internal status storage - format the ActualNetDef - * as a subelement of <interface> so that no persistent config - * data is overwritten. - */ - if ((flags & VIR_DOMAIN_DEF_FORMAT_ACTUAL_NET) && - (virDomainActualNetDefFormat(buf, def, flags) < 0)) - return -1; + sourceLines++; break; case VIR_DOMAIN_NET_TYPE_ETHERNET: @@ -20773,13 +20795,16 @@ virDomainNetDefFormat(virBufferPtr buf, virBufferAsprintf(buf, " mode='%s'", def->data.vhostuser->data.nix.listen ? "server" : "client"); - virBufferAddLit(buf, "/>\n"); + sourceLines++; } break; case VIR_DOMAIN_NET_TYPE_BRIDGE: - virBufferEscapeString(buf, "<source bridge='%s'/>\n", - def->data.bridge.brname); + if (def->data.bridge.brname) { + virBufferEscapeString(buf, "<source bridge='%s'", + def->data.bridge.brname); + sourceLines++; + } break; case VIR_DOMAIN_NET_TYPE_SERVER: @@ -20794,25 +20819,25 @@ virDomainNetDefFormat(virBufferPtr buf, virBufferAsprintf(buf, "<source port='%d'", def->data.socket.port); } + sourceLines++; - if (def->type != VIR_DOMAIN_NET_TYPE_UDP) { - virBufferAddLit(buf, "/>\n"); + if (def->type != VIR_DOMAIN_NET_TYPE_UDP) break; - } virBufferAddLit(buf, ">\n"); + sourceLines++; virBufferAdjustIndent(buf, 2); virBufferAsprintf(buf, "<local address='%s' port='%d'/>\n", def->data.socket.localaddr, def->data.socket.localport); virBufferAdjustIndent(buf, -2); - virBufferAddLit(buf, "</source>\n"); break; case VIR_DOMAIN_NET_TYPE_INTERNAL: - virBufferEscapeString(buf, "<source name='%s'/>\n", + virBufferEscapeString(buf, "<source name='%s'", def->data.internal.name); + sourceLines++; break; case VIR_DOMAIN_NET_TYPE_DIRECT: @@ -20820,7 +20845,7 @@ virDomainNetDefFormat(virBufferPtr buf, def->data.direct.linkdev); virBufferAsprintf(buf, " mode='%s'", virNetDevMacVLanModeTypeToString(def->data.direct.mode)); - virBufferAddLit(buf, "/>\n"); + sourceLines++; break; case VIR_DOMAIN_NET_TYPE_HOSTDEV: @@ -20835,12 +20860,44 @@ virDomainNetDefFormat(virBufferPtr buf, break; } + /* if sourceLines == 0 - no <source> info at all so far + * sourceLines == 1 - first line writte, no terminating ">" + * sourceLines > 1 - multiple lines, including subelements + */ + if (def->hostIP.nips || def->hostIP.nroutes) { + if (sourceLines == 0) { + virBufferAddLit(buf, "<source>\n"); + sourceLines += 2; + } else if (sourceLines == 1) { + virBufferAddLit(buf, ">\n"); + sourceLines++; + } + virBufferAdjustIndent(buf, 2); + if (virDomainNetIPInfoFormat(buf, &def->hostIP) < 0) + return -1; + virBufferAdjustIndent(buf, -2); + } + if (sourceLines == 1) + virBufferAddLit(buf, "/>\n"); + else if (sourceLines > 1) + virBufferAddLit(buf, "</source>\n"); + if (virNetDevVlanFormat(&def->vlan, buf) < 0) return -1; if (virNetDevVPortProfileFormat(def->virtPortProfile, buf) < 0) return -1; if (virNetDevBandwidthFormat(def->bandwidth, buf) < 0) return -1; + + /* ONLY for internal status storage - format the ActualNetDef + * as a subelement of <interface> so that no persistent config + * data is overwritten. + */ + if (def->type == VIR_DOMAIN_NET_TYPE_NETWORK && + (flags & VIR_DOMAIN_DEF_FORMAT_ACTUAL_NET) && + (virDomainActualNetDefFormat(buf, def, flags) < 0)) + return -1; + } if (virDomainNetIPInfoFormat(buf, &def->guestIP) < 0) @@ -20849,6 +20906,7 @@ virDomainNetDefFormat(virBufferPtr buf, virBufferEscapeString(buf, "<script path='%s'/>\n", def->script); virBufferEscapeString(buf, "<backenddomain name='%s'/>\n", def->domain_name); + if (def->ifname && !((flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE) && (STRPREFIX(def->ifname, VIR_NET_GENERATED_PREFIX) || @@ -20856,6 +20914,7 @@ virDomainNetDefFormat(virBufferPtr buf, /* Skip auto-generated target names for inactive config. */ virBufferEscapeString(buf, "<target dev='%s'/>\n", def->ifname); } + if (def->ifname_guest || def->ifname_guest_actual) { virBufferAddLit(buf, "<guest"); /* Skip auto-generated target names for inactive config. */ diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 7ff966f..8f6c24a 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -966,7 +966,8 @@ struct _virDomainNetDef { } tune; char *script; char *domain_name; /* backend domain name */ - char *ifname; + char *ifname; /* interface name on the host (<target dev='x'/>) */ + virNetDevIPInfo hostIP; char *ifname_guest_actual; char *ifname_guest; virNetDevIPInfo guestIP; diff --git a/tests/lxcxml2xmldata/lxc-ethernet-hostip.xml b/tests/lxcxml2xmldata/lxc-ethernet-hostip.xml new file mode 100644 index 0000000..ce455f7 --- /dev/null +++ b/tests/lxcxml2xmldata/lxc-ethernet-hostip.xml @@ -0,0 +1,44 @@ +<domain type='lxc'> + <name>8675309</name> + <uuid>e21987a5-e98e-9c99-0e35-803e4d9ad1fe</uuid> + <memory unit='KiB'>1048576</memory> + <currentMemory unit='KiB'>1048576</currentMemory> + <vcpu placement='static'>1</vcpu> + <resource> + <partition>/machine</partition> + </resource> + <os> + <type arch='x86_64'>exe</type> + <init>/sbin/init</init> + </os> + <idmap> + <uid start='0' target='100000' count='100000'/> + <gid start='0' target='100000' count='100000'/> + </idmap> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>restart</on_crash> + <devices> + <emulator>/usr/libexec/libvirt_lxc</emulator> + <filesystem type='mount' accessmode='passthrough'> + <source dir='/mach/8675309'/> + <target dir='/'/> + </filesystem> + <interface type='ethernet'> + <mac address='00:16:3e:0f:ef:8a'/> + <source> + <ip address='192.168.122.12' family='ipv4' prefix='24' peer='192.168.122.1'/> + <ip address='192.168.122.13' family='ipv4' prefix='24'/> + <route family='ipv4' address='0.0.0.0' gateway='192.168.122.1'/> + <route family='ipv4' address='192.168.124.0' prefix='24' gateway='192.168.124.1'/> + </source> + <ip address='192.168.122.1' family='ipv4' prefix='32' peer='192.168.122.12'/> + <guest dev='eth2'/> + </interface> + <console type='pty'> + <target type='lxc' port='0'/> + </console> + </devices> + <seclabel type='none'/> +</domain> diff --git a/tests/lxcxml2xmltest.c b/tests/lxcxml2xmltest.c index 1b16088..2f7f779 100644 --- a/tests/lxcxml2xmltest.c +++ b/tests/lxcxml2xmltest.c @@ -95,6 +95,7 @@ mymain(void) DO_TEST("capabilities"); DO_TEST("sharenet"); DO_TEST("ethernet"); + DO_TEST("ethernet-hostip"); DO_TEST_FULL("filesystem-root", 0, false, VIR_DOMAIN_DEF_PARSE_SKIP_OSTYPE_CHECKS); -- 2.5.5 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list