]> xenbits.xensource.com Git - libvirt.git/commitdiff
conf: support host-side IP/route information in <interface>
authorLaine Stump <laine@laine.org>
Thu, 9 Jun 2016 19:35:08 +0000 (15:35 -0400)
committerLaine Stump <laine@laine.org>
Sat, 2 Jul 2016 01:13:30 +0000 (21:13 -0400)
This is place as a sub-element of <source>, where other aspects of the
host-side connection to the network device are located (network or
bridge name, udp listen port, etc). It's a bit odd that the interface
we're configuring with this info is itself named in <target dev='x'/>,
but that ship sailed long ago:

    <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 if needed, 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).

(NB: The already-existing configuration of IP info for the guest-side
of interfaces is in subelements directly under <interface>, and the
name of the guest-side interface (when configurable) is in <guest
dev='x'/>).

(This patch had been pushed earlier in
commit fe6a77898a38f491403a70cc49925a584101daee, but was reverted in
commit d658456530c1010a49f45865613ed361a0fcc5b4 because it had been
accidentally pushed during the freeze for release 2.0.0)

docs/formatdomain.html.in
docs/schemas/domaincommon.rng
src/conf/domain_conf.c
src/conf/domain_conf.h
tests/lxcxml2xmldata/lxc-ethernet-hostip.xml [new file with mode: 0644]
tests/lxcxml2xmltest.c

index 2347ed9b4070b66aae95609450208a11c0a42850..ee6d5e875f22d830932c6268bc9d37890facbf99 100644 (file)
@@ -5015,6 +5015,32 @@ qemu-kvm -net nic,model=? /dev/null
     definitions</a>.  This is used by the LXC driver.
     </p>
 
+<pre>
+  ...
+  &lt;devices&gt;
+    &lt;interface type='ethernet'&gt;
+      <b>&lt;source/&gt;</b>
+        <b>&lt;ip address='192.168.123.1' prefix='24'/&gt;</b>
+        <b>&lt;ip address='10.0.0.10' prefix='24' peer='192.168.122.5'/&gt;</b>
+        <b>&lt;route family='ipv4' address='192.168.42.0' prefix='24' gateway='192.168.123.4'/&gt;</b>
+      <b>&lt;source/&gt;</b>
+      ...
+    &lt;/interface&gt;
+    ...
+  &lt;/devices&gt;
+  ...
+</pre>
+
+    <p>
+      <span class="since">Since 2.1.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>&lt;source&gt;</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>
index 59c1155e2c64ff1633fa2629aaadf85512f1ea09..cba2d1223eddfb865ab9701d6c277f98782f596f 100644 (file)
           <interleave>
             <optional>
               <element name="source">
-                <empty/>
+                <ref name="interface-ip-info"/>
               </element>
             </optional>
             <ref name="interface-options"/>
index 27bf7b928785fc7e110c86c16f031e4bbe0a6b84..9f7b90688a6cd100244fa8f5129eebc6efb1e07f 100644 (file)
@@ -1798,6 +1798,7 @@ virDomainNetDefClear(virDomainNetDefPtr def)
     VIR_FREE(def->ifname_guest_actual);
 
     virNetDevIPInfoClear(&def->guestIP);
+    virNetDevIPInfoClear(&def->hostIP);
     virDomainDeviceInfoClear(&def->info);
 
     VIR_FREE(def->filter);
@@ -4552,6 +4553,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)
@@ -4563,9 +4581,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:
@@ -8945,6 +8965,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 &&
@@ -20643,6 +20672,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];
@@ -20712,15 +20742,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:
@@ -20734,13 +20756,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:
@@ -20755,25 +20780,27 @@ 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",
-                                  def->data.internal.name);
+            if (def->data.internal.name) {
+                virBufferEscapeString(buf, "<source name='%s'",
+                                      def->data.internal.name);
+                sourceLines++;
+            }
             break;
 
         case VIR_DOMAIN_NET_TYPE_DIRECT:
@@ -20781,7 +20808,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:
@@ -20796,12 +20823,44 @@ virDomainNetDefFormat(virBufferPtr buf,
             break;
         }
 
+        /* if sourceLines == 0 - no <source> info at all so far
+         *    sourceLines == 1 - first line written, 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)
@@ -20810,6 +20869,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) ||
@@ -20817,6 +20877,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. */
index f29c4bd72a7cd3b113fa8d24af3a6235a25fab06..ba0ad5fda835342190217206a005083cf9b5141d 100644 (file)
@@ -963,7 +963,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 (file)
index 0000000..ce455f7
--- /dev/null
@@ -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>
index 1b16088e6746886a8668a95907779bae1a1979ad..2f7f77973472c506afbd7ae84ed68098e0c337bd 100644 (file)
@@ -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);