Discussion:
[netcf-devel] [PATCH] support multiple IPv4 addresses in redhat driver
Laine Stump
2015-03-26 18:42:56 UTC
Permalink
netcf has always supported multiple IPv6 addresses, but only a single
IPv4 address. This patch remedies that, at least for the redhat driver
(also used for Fedora and CentOS).

I can't claim that the xsl transforms in redhat-(get|put).xsl are the
optimum code, but they do get the job done (no mean feat, since this
is the first time I've ever made anything beyond a cosmetic change to
an xsl script).

Similar to what NetworkManager does, the first IPv4 address is stored
in the ifcfg file as

IPADDR=b.l.a.h
NETMASK=b.l.a.h

while subsequent ip addresses are stored as:

IPADDRn=b.l.a.h
NETMASKn=b.l.a.h

where "n" is 1 - 99 (since that is what initscripts (and probably
NetworkManager) support).

A new test case with multiple IPv4 addresses has also been added -
bridge-multi.
---
data/xml/interface.rng | 14 ++++++------
data/xml/redhat-get.xsl | 25 ++++++++++++++++++----
data/xml/redhat-put.xsl | 23 ++++++++++++++++++--
tests/interface/bridge-multi.xml | 15 +++++++++++++
.../fsroot/etc/sysconfig/network-scripts/ifcfg-br1 | 20 +++++++++++++++++
tests/redhat/schema/bridge-multi.xml | 24 +++++++++++++++++++++
tests/test-redhat.c | 5 +++--
7 files changed, 112 insertions(+), 14 deletions(-)
create mode 100644 tests/interface/bridge-multi.xml
create mode 100644 tests/redhat/fsroot/etc/sysconfig/network-scripts/ifcfg-br1
create mode 100644 tests/redhat/schema/bridge-multi.xml

diff --git a/data/xml/interface.rng b/data/xml/interface.rng
index 7810a7a..c587da8 100644
--- a/data/xml/interface.rng
+++ b/data/xml/interface.rng
@@ -325,12 +325,14 @@
<choice>
<ref name="dhcp-element"/>
<group>
- <element name="ip">
- <attribute name="address"><ref name="ipv4-addr"/></attribute>
- <optional>
- <attribute name="prefix"><ref name="ipv4-prefix"/></attribute>
- </optional>
- </element>
+ <zeroOrMore>
+ <element name="ip">
+ <attribute name="address"><ref name="ipv4-addr"/></attribute>
+ <optional>
+ <attribute name="prefix"><ref name="ipv4-prefix"/></attribute>
+ </optional>
+ </element>
+ </zeroOrMore>
<optional>
<element name="route">
<attribute name="gateway"><ref name="ipv4-addr"/></attribute>
diff --git a/data/xml/redhat-get.xsl b/data/xml/redhat-get.xsl
index 81dc718..5970c67 100644
--- a/data/xml/redhat-get.xsl
+++ b/data/xml/redhat-get.xsl
@@ -192,10 +192,18 @@
</xsl:when>
<xsl:when test="ip">
<node label="BOOTPROTO" value="none"/>
- <node label="IPADDR" value="{ip/@address}"/>
- <xsl:if test="ip/@prefix">
- <node label="NETMASK" value="{ipcalc:netmask(ip/@prefix)}"/>
- </xsl:if>
+ <xsl:for-each select="ip">
+ <xsl:if test="position() = 1">
+ <xsl:call-template name="ipv4-address">
+ <xsl:with-param name="index"/>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:if test="position() > 1 and position() &lt; 100">
+ <xsl:call-template name="ipv4-address">
+ <xsl:with-param name="index" select="position() - 1"/>
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:for-each>
<xsl:if test="route">
<node label="GATEWAY" value="{route/@gateway}"/>
</xsl:if>
@@ -203,6 +211,15 @@
</xsl:choose>
</xsl:template>

+ <xsl:template name="ipv4-address">
+ <xsl:param name="index"/>
+ <node label="IPADDR{$index}" value="{@address}"/>
+ <xsl:if test="@prefix">
+ <node label="NETMASK{$index}" value="{ipcalc:netmask(@prefix)}"/>
+ </xsl:if>
+ </xsl:template>
+
+
<xsl:template name="protocol-ipv6">
<node label="IPV6INIT" value="yes"/>
<xsl:if test="count(autoconf) > 0">
diff --git a/data/xml/redhat-put.xsl b/data/xml/redhat-put.xsl
index ed800d6..7a3e482 100644
--- a/data/xml/redhat-put.xsl
+++ b/data/xml/redhat-put.xsl
@@ -183,8 +183,7 @@
<xsl:variable name="uses_dhcp"
select="node[@label = 'BOOTPROTO']/@value = 'dhcp'"/>
<xsl:variable name="uses_static"
- select="count(node[@label = 'IPADDR']) +
- count(node[@label = 'IPADDR0']) > 0"/>
+ select="count(node[substring(@label,1,6) = 'IPADDR']) > 0"/>
<xsl:variable name="uses_ipv4" select="$uses_dhcp or $uses_static"/>
<xsl:if test="$uses_ipv4">
<protocol family="ipv4">
@@ -197,6 +196,7 @@
</dhcp>
</xsl:when>
<xsl:when test="$uses_static">
+ <!-- IPADDR and IPADDR0 must be treated differently from IPADDR1 - IPADDR99 -->
<xsl:choose>
<xsl:when test="node[@label = 'IPADDR']">
<ip address="{node[@label = 'IPADDR']/@value}">
@@ -217,6 +217,25 @@
<route gateway="{node[@label = 'GATEWAY0']/@value}"/>
</xsl:when>
</xsl:choose>
+ <xsl:for-each select="node[substring(@label, 1, 6) = 'IPADDR']">
+ <xsl:variable name="index" select="substring(@label, 7, 3)"/>
+ <xsl:if test="number($index) &gt; 0 and number($index) &lt; 100">
+ <ip address="{@value}">
+ <xsl:choose>
+ <xsl:when test="../node[@label = concat('PREFIX', $index)]">
+ <xsl:attribute name="prefix">
+ <xsl:value-of select="../node[@label = concat('PREFIX', $index)]/@value"/>
+ </xsl:attribute>
+ </xsl:when>
+ <xsl:when test="../node[@label = concat('NETMASK', $index)]">
+ <xsl:attribute name="prefix">
+ <xsl:value-of select="ipcalc:prefix(../node[@label = concat('NETMASK', $index)]/@value)"/>
+ </xsl:attribute>
+ </xsl:when>
+ </xsl:choose>
+ </ip>
+ </xsl:if>
+ </xsl:for-each>
</xsl:when>
</xsl:choose>
</protocol>
diff --git a/tests/interface/bridge-multi.xml b/tests/interface/bridge-multi.xml
new file mode 100644
index 0000000..d476c5b
--- /dev/null
+++ b/tests/interface/bridge-multi.xml
@@ -0,0 +1,15 @@
+<interface type="bridge" name="br1">
+ <start mode="onboot"/>
+ <protocol family="ipv4">
+ <ip address="10.0.0.1" prefix="24"/>
+ <ip address="192.168.125.4" prefix="24"/>
+ <ip address="192.168.125.5" prefix="31"/>
+ <ip address="192.168.125.6"/>
+ <ip address="192.168.125.7"/>
+ <ip address="192.168.125.8"/>
+ <ip address="192.168.125.9"/>
+ <ip address="192.168.125.10"/>
+ <ip address="172.16.20.32" prefix="12"/>
+ </protocol>
+ <bridge stp="on" delay="0"/>
+</interface>
diff --git a/tests/redhat/fsroot/etc/sysconfig/network-scripts/ifcfg-br1 b/tests/redhat/fsroot/etc/sysconfig/network-scripts/ifcfg-br1
new file mode 100644
index 0000000..5247f7b
--- /dev/null
+++ b/tests/redhat/fsroot/etc/sysconfig/network-scripts/ifcfg-br1
@@ -0,0 +1,20 @@
+# Enclosing the values in useless quotes is intentional
+DEVICE="br1"
+ONBOOT='yes'
+BOOTPROTO="none"
+TYPE="Bridge"
+DELAY=0
+STP=on
+IPADDR="10.0.0.1"
+NETMASK=255.255.255.0
+IPADDR1="192.168.125.4"
+PREFIX1=24
+IPADDR2="192.168.125.5"
+NETMASK2="255.255.255.254"
+IPADDR3="192.168.125.6"
+IPADDR4="192.168.125.7"
+IPADDR5="192.168.125.8"
+IPADDR6="192.168.125.9"
+IPADDR7="192.168.125.10"
+IPADDR8="172.16.20.32"
+PREFIX8=12
diff --git a/tests/redhat/schema/bridge-multi.xml b/tests/redhat/schema/bridge-multi.xml
new file mode 100644
index 0000000..24c6f9d
--- /dev/null
+++ b/tests/redhat/schema/bridge-multi.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<forest>
+ <tree path="/files/etc/sysconfig/network-scripts/ifcfg-br1">
+ <node label="DEVICE" value="br1"/>
+ <node label="ONBOOT" value="yes"/>
+ <node label="TYPE" value="Bridge"/>
+ <node label="BOOTPROTO" value="none"/>
+ <node label="IPADDR" value="10.0.0.1"/>
+ <node label="NETMASK" value="255.255.255.0"/>
+ <node label="IPADDR1" value="192.168.125.4"/>
+ <node label="NETMASK1" value="255.255.255.0"/>
+ <node label="IPADDR2" value="192.168.125.5"/>
+ <node label="NETMASK2" value="255.255.255.254"/>
+ <node label="IPADDR3" value="192.168.125.6"/>
+ <node label="IPADDR4" value="192.168.125.7"/>
+ <node label="IPADDR5" value="192.168.125.8"/>
+ <node label="IPADDR6" value="192.168.125.9"/>
+ <node label="IPADDR7" value="192.168.125.10"/>
+ <node label="IPADDR8" value="172.16.20.32"/>
+ <node label="NETMASK8" value="255.240.0.0"/>
+ <node label="STP" value="on"/>
+ <node label="DELAY" value="0"/>
+ </tree>
+</forest>
diff --git a/tests/test-redhat.c b/tests/test-redhat.c
index ab22a6a..a08bc23 100644
--- a/tests/test-redhat.c
+++ b/tests/test-redhat.c
@@ -1,7 +1,7 @@
/*
* test-redhat.c:
*
- * Copyright (C) 2009 Red Hat Inc.
+ * Copyright (C) 2009, 2011, 2015 Red Hat Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -43,7 +43,7 @@ static void testListInterfaces(CuTest *tc) {
int nint;
char **names;
static const char *const exp_names[] =
- { "br0", "bond0", "lo", "eth3", "eth4" };
+ { "br0", "br1", "bond0", "lo", "eth3", "eth4" };
static const int exp_nint = ARRAY_CARDINALITY(exp_names);

nint = ncf_num_of_interfaces(ncf, NETCF_IFACE_ACTIVE|NETCF_IFACE_INACTIVE);
@@ -180,6 +180,7 @@ static void testTransforms(CuTest *tc) {
assert_transforms(tc, "bridge-vlan");
assert_transforms(tc, "bridge-empty");
assert_transforms(tc, "bridge-bond");
+ assert_transforms(tc, "bridge-multi");
assert_transforms(tc, "ethernet-static");
assert_transforms(tc, "ethernet-static-no-prefix");
assert_transforms(tc, "ethernet-dhcp");
--
2.1.0
Eric Blake
2015-03-27 22:01:40 UTC
Permalink
Post by Laine Stump
netcf has always supported multiple IPv6 addresses, but only a single
IPv4 address. This patch remedies that, at least for the redhat driver
(also used for Fedora and CentOS).
I can't claim that the xsl transforms in redhat-(get|put).xsl are the
optimum code, but they do get the job done (no mean feat, since this
is the first time I've ever made anything beyond a cosmetic change to
an xsl script).
And I'm no xsl transform expert either, so take my review with a grain
of salt.
Post by Laine Stump
+++ b/data/xml/redhat-get.xsl
@@ -192,10 +192,18 @@
</xsl:when>
<xsl:when test="ip">
<node label="BOOTPROTO" value="none"/>
- </xsl:if>
+ <xsl:for-each select="ip">
+ <xsl:if test="position() = 1">
+ <xsl:call-template name="ipv4-address">
+ <xsl:with-param name="index"/>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:if test="position() > 1 and position() &lt; 100">
I think you have an off-by-one:

1 vs. 2-99...
Post by Laine Stump
+++ b/data/xml/redhat-put.xsl
compared to 0 vs. 1-99.

That is, if position() is 1-based, you want <= 100, or < 101, as your
terminating condition (operating on IPADDR[position()-1] for each
iteration of the for-each).

If you fix or explain the difference, then the rest of the patch looks
okay for an ACK.
--
Eric Blake eblake redhat com +1-919-301-3266
Libvirt virtualization library http://libvirt.org
Laine Stump
2015-03-30 15:49:46 UTC
Permalink
Post by Eric Blake
Post by Laine Stump
netcf has always supported multiple IPv6 addresses, but only a single
IPv4 address. This patch remedies that, at least for the redhat driver
(also used for Fedora and CentOS).
I can't claim that the xsl transforms in redhat-(get|put).xsl are the
optimum code, but they do get the job done (no mean feat, since this
is the first time I've ever made anything beyond a cosmetic change to
an xsl script).
And I'm no xsl transform expert either, so take my review with a grain
of salt.
Post by Laine Stump
+++ b/data/xml/redhat-get.xsl
@@ -192,10 +192,18 @@
</xsl:when>
<xsl:when test="ip">
<node label="BOOTPROTO" value="none"/>
- </xsl:if>
+ <xsl:for-each select="ip">
+ <xsl:if test="position() = 1">
+ <xsl:call-template name="ipv4-address">
+ <xsl:with-param name="index"/>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:if test="position() > 1 and position() &lt; 100">
1 vs. 2-99...
Ah, right. All of this indexing starting at 0 instead of 1 is really a
pain for an old C-head like me.

Note that the xsl:if just above that for the case of position() = 1 is
there so that the first address will be IPADDR rather than IPADDR0 -
it's essentially calling ipv4-address with index = "" (that's another
thing I could only learn by trial and error - you can't directly set a
param to "", you have to just not set it at all). Those from 2 - 100
should be called with index = 1 - 99 , but as you've pointed out, the
last time I'll do it is for position() == 99, or index = 99 - 1 = 98.

Thanks for catching that!
Post by Eric Blake
Post by Laine Stump
+++ b/data/xml/redhat-put.xsl
compared to 0 vs. 1-99.
That is, if position() is 1-based, you want <= 100, or < 101, as your
terminating condition (operating on IPADDR[position()-1] for each
iteration of the for-each).
If you fix or explain the difference, then the rest of the patch looks
okay for an ACK.
Okay. I'll change the index before pushing.

Endless thanks for all the reviews.

Loading...