Bug 1438082

Summary: cloud-init fails to configure network in mixed ipv4/ipv6 environment
Product: Red Hat Enterprise Linux 7 Reporter: Lars Kellogg-Stedman <lars>
Component: cloud-initAssignee: Ryan McCabe <rmccabe>
Status: CLOSED ERRATA QA Contact: Vratislav Hutsky <vhutsky>
Severity: high Docs Contact:
Priority: high    
Version: 7.4CC: akaris, dmaley, fdinitto, huzhao, lars, lnatapov, mbracho, mvermaes, pgervase, rmccabe, ushkalim, yacao
Target Milestone: rcKeywords: Triaged
Target Release: ---Flags: lars: needinfo-
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: cloud-init-0.7.9-8.el7 Doc Type: If docs needed, set a value
Doc Text:
Story Points: ---
Clone Of:
: 1462022 (view as bug list) Environment:
Last Closed: 2017-08-01 23:23:42 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On:    
Bug Blocks: 1462022    

Description Lars Kellogg-Stedman 2017-03-31 20:38:50 UTC
From the upstream bug report:

When I attempt to use cloud-init 0.7.9 with RHEL72, I am finding that IPv4 network configuration information is not properly configured on the eth0 interface. Instead, I end up with an ifcfg-eth0 that looks like this:

[root@projectb-fwam-rhel72 ~]# cat /etc/sysconfig/network-scripts/ifcfg-eth0
# Created by cloud-init on instance boot automatically, do not edit.
#
BOOTPROTO=dhcp
DEVICE=eth0
HWADDR=fa:16:3e:54:5a:77
ONBOOT=yes
TYPE=Ethernet
USERCTL=no

cloud-init.log indicates that network_data.json is being used for the network configuration information (although the network_config content file is available as well).

The config drive is attached.

Comment 2 Lars Kellogg-Stedman 2017-04-04 14:21:09 UTC
Information from the original reporter:

I see the following in the logs:
# grep ERROR /var/log/messages
ERROR    : [/etc/sysconfig/network-scripts/ifup-aliases] error in ifcfg-eth0:1: didn't specify device or ipaddr
ERROR    : [/etc/sysconfig/network-scripts/ifup-aliases] error in ifcfg-eth0:1: didn't specify device or ipaddr
ERROR    : [/etc/sysconfig/network-scripts/ifup-aliases] error in ifcfg-eth0:1: didn't specify device or spader

The interfaces look like this:

$ /sbin/ifconfig -a
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::f816:3eff:feb2:4bd4  prefixlen 64  scopeid 0x20<link>
        ether fa:16:3e:b2:4b:d4  txqueuelen 1000  (Ethernet)
        RX packets 88  bytes 17669 (17.2 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 81  bytes 13735 (13.4 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

eth0:0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.252.197.143  netmask 255.255.255.224  broadcast 10.252.197.159
        ether fa:16:3e:b2:4b:d4  txqueuelen 1000  (Ethernet)

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 0  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

If I "ifup” the eth0:1 interface, the eth0 interface is configured with the IPv6 address (not the subinterface).

# ifup eth0:1
# /sbin/ifconfig -a
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 2620:160:d2ff:1c07::b  prefixlen 64  scopeid 0x0<global>
        inet6 fe80::f816:3eff:feb2:4bd4  prefixlen 64  scopeid 0x20<link>
        ether fa:16:3e:b2:4b:d4  txqueuelen 1000  (Ethernet)
        RX packets 760  bytes 133925 (130.7 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 576  bytes 114201 (111.5 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

eth0:0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.252.197.143  netmask 255.255.255.224  broadcast 10.252.197.159
        ether fa:16:3e:b2:4b:d4  txqueuelen 1000  (Ethernet)

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 0  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

Here are the configuration files:

# cat ifcfg-eth0
# Created by cloud-init on instance boot automatically, do not edit.
#
BOOTPROTO=none
DEVICE=eth0
HWADDR=fa:16:3e:b2:4b:d4
ONBOOT=yes
TYPE=Ethernet
USERCTL=no

# cat ifcfg-eth0:0
# Created by cloud-init on instance boot automatically, do not edit.
#
BOOTPROTO=static
DEFROUTE=yes
DEVICE=eth0:0
GATEWAY=10.252.197.129
HWADDR=fa:16:3e:b2:4b:d4
IPADDR=10.252.197.143
NETMASK=255.255.255.224
ONBOOT=yes
TYPE=Ethernet
USERCTL=no

# cat ifcfg-eth0:1
# Created by cloud-init on instance boot automatically, do not edit.
#
BOOTPROTO=static
DEFROUTE=yes
DEVICE=eth0:1
HWADDR=fa:16:3e:b2:4b:d4
IPV6ADDR=2620:160:d2ff:1c07::b
IPV6INIT=yes
IPV6_DEFAULTGW=2620:160:d2ff:1c07::1
NETMASK=64
ONBOOT=yes
TYPE=Ethernet
USERCTL=no

Comment 3 Andreas Karis 2017-04-17 14:36:25 UTC
removing https://bugs.launchpad.net/cloud-init/+bug/1677846 from this bug. Let's focus here on https://bugs.launchpad.net/cloud-init/+bug/1679817

I created https://bugzilla.redhat.com/1438082 to work on the ESXi config drive issue with DVS

Comment 4 Andreas Karis 2017-04-17 14:44:54 UTC
I created https://bugzilla.redhat.com/show_bug.cgi?id=1442783 to work on the ESXi config drive issue with DVS

Comment 5 Andreas Karis 2017-04-19 05:54:25 UTC
Hi,

++++++++++++++++++++++++++++++++++++++++++++++++++

Long story short:

messages-20170409:Apr  3 18:15:05 projectb-fwam-rhel72-cloud-init-0 /etc/sysconfig/network-scripts/ifup-aliases: error in ifcfg-eth0:1: didn't specify device or ipaddr is coming from the absence of IPv4 addresses.

The error message comes from here: 

/etc/sysconfig/network-scripts/ifup-aliases
~~~
(...)
       if [ -z "$DEVICE" -o -z "$IPADDR" ]; then
              net_log $"error in $FILE: didn't specify device or ipaddr"
              return 1
       fi
(...)
~~~

Meaning that if DEVICE or IPADDR is not set, then this error message will be logged, and the script returns.

Note that in /etc/sysconfig/network-scripts/ifup-aliases, it calls
~~~
(...)
[[ "$IPV6INIT" != [nN0]* ]]  && /etc/sysconfig/network-scripts/ifup-ipv6 ${DEVICE}
(...)
~~~
However, due to the fact that the script returns function new_interface  (returns, not exits!) with the above error message ('didn't specify device or ipaddr'), it will never get here.

++++++++++++++++++++++++++++++++++++++++++++++++++

Looking into this in more detail:

The aliases script ifup-aliases is being called in
~~~
ifup-post:    /etc/sysconfig/network-scripts/ifup-aliases ${DEVICE} ${CONFIG}
~~~

Now, looking at /etc/sysconfig/network-scripts/network-functions
~~~
(...)
[ -z "$REALDEVICE" ] && REALDEVICE=${DEVICE%%:*}
    [ -z "$SYSCTLDEVICE" ] && SYSCTLDEVICE=${REALDEVICE/.//}
    if [ "${DEVICE}" != "${REALDEVICE}" ]; then
        ISALIAS=yes
    else
        ISALIAS=no
    fi
(...)
~~~

Meaning that DEVICE != REALDEVICE  (eth0:0 != eth0), so this is ISALIAS = yes

Now, looking at /etc/sysconfig/network-scripts/network-functions
~~~
(...)
if [ "$ISALIAS" = no ] ; then
    /etc/sysconfig/network-scripts/ifup-aliases ${DEVICE} ${CONFIG}
fi
(...)
~~~

Meaning that if this is not an alias, then call ifup-aliases <INTERFACE> <CONFIG>, e.g. ifup-aliases eth0  <CONFIG>

ifup-aliases then processes any alias file for the ${DEVICE}
~~~
(...)
for FILE in ifcfg-${parent_device}:* ; do
       is_ignored_file "$FILE" && continue
       ini_env;
       . ./$FILE;
       [ -z "$DEVICE" ] && DEVICE=${FILE##ifcfg-}
       [ "$ONPARENT" != "no" -a "$ONPARENT" != "NO" ] && new_interface;
       unset DEVICE
done
(...)
~~~

new_interface is called with the alias name as DEVICE, e.g. eth0:0 and returns (fails) before it can call ifup-ipv6
~~~
function new_interface ()
{
(...)
       if [ -z "$DEVICE" -o -z "$IPADDR" ]; then
              net_log $"error in $FILE: didn't specify device or ipaddr"
              return 1
       fi
(...)
       [[ "$IPV6INIT" != [nN0]* ]]  && /etc/sysconfig/network-scripts/ifup-ipv6 ${DEVICE}
}
(...)
~~~

+++++++++++++++++++++++++++++++++++++++

Looking at IPv6, IPv6 is only configured by
~~~
[root@undercloud-4 network-scripts]# egrep '\-6' *
ifdown-routes:           proto="-6"
ifup-eth:    if /sbin/dhclient -6 -1 ${DHCPV6C_OPTIONS} ${DHCLIENTCONF} -lf ${LEASEFILE} -pf /var/run/dhclient6-${DEVICE}.pid -H ${DHCP_HOSTNAME:-${HOSTNAME%%.*}} ${DEVICE} ; then
ifup-ib:    if /sbin/dhclient -6 -1 ${DHCPV6C_OPTIONS} ${DHCLIENTCONF} -lf ${LEASEFILE} -pf /var/run/dhclient6-${DEVICE}.pid -H ${DHCP_HOSTNAME:-${HOSTNAME%%.*}} ${DEVICE} ; then
ifup-ipv6:				/sbin/ip -6 route add $line
ifup-ipv6:		/sbin/ip -6 route del ::/96 dev tun6to4
ifup-ipv6:		/sbin/ip -6 addr del "::$ipv4addrlocal/128" dev tun6to4
ifup-routes:        proto="-6"
ifup-tunnel:	proto=-6
network-functions-ipv6:		local returntxt="$(/sbin/ip -6 route add $networkipv6 via $gatewayipv6 metric 1 2>&1)"
network-functions-ipv6:			local returntxt="$(/sbin/ip -6 route add $networkipv6 dev $device metric 1 2>&1)"
network-functions-ipv6:			local returntxt="$(/sbin/ip -6 route add $networkipv6 via $gatewayipv6 dev $device metric 1 2>&1)"
network-functions-ipv6:	/sbin/ip -6 addr add $address dev $device
network-functions-ipv6:		/sbin/ip -6 route flush dev $device scope global >/dev/null 2>&1
network-functions-ipv6:		/sbin/ip -6 route flush dev $device scope site   >/dev/null 2>&1
network-functions-ipv6:	/sbin/ip -6 addr flush dev $device scope global >/dev/null 2>&1
network-functions-ipv6:	/sbin/ip -6 addr flush dev $device scope site   >/dev/null 2>&1
network-functions-ipv6:	/sbin/ip -6 addr show dev $dev scope global permanent | awk '/\<inet6\>/ && $2 ~ /^2002:/ { print $2 }' | while read addr; do
network-functions-ipv6:		/sbin/ip -6 addr del ${addr} dev ${dev}
network-functions-ipv6:	/sbin/ip -6 route show dev $device | LC_ALL=C grep "^2002:" | while read ipv6net dummy; do
network-functions-ipv6:		/sbin/ip -6 route del $ipv6net dev $device
network-functions-ipv6:		/sbin/ip -6 route | LC_ALL=C grep "^unreachable 2002:.*/48 dev lo" | while read token net rest; do
network-functions-ipv6:        while [ ${countdown} -gt 0 -a -n "$(ip -6 addr show dev ${device} scope global tentative)" ]; do
~~~

Also note that IPV6ADDR and IPV6ADDR_SECONDARIES is only processed by:
~~~
[root@undercloud-4 network-scripts]# grep IPV6ADDR *
ifup-ippp:    if [[ "$IPV6INIT" != [nN0]* && ! -z "$IPV6ADDR" ]]; then
ifup-ipv6:#  IPV6ADDR=<IPv6 address>[/<prefix length>]: specify primary static IPv6 address
ifup-ipv6:#  IPV6ADDR_SECONDARIES="<IPv6 address>[/<prefix length>] ..." (optional)
ifup-ipv6:if [ -n "$IPV6ADDR" ]; then
ifup-ipv6:	ipv6_add_addr_on_device $DEVICE $IPV6ADDR || exit 1
ifup-ipv6:if [ -n "$IPV6ADDR_SECONDARIES" ]; then
ifup-ipv6:	for ipv6addr in $IPV6ADDR_SECONDARIES; do
ifup-isdn:    if [[ "$IPV6INIT" != [nN0]* && ! -z "$IPV6ADDR" ]]; then
ifup-sit:#  IPV6ADDR=<IPv6 address>[/<prefix length>]: (optional) local IPv6 address of a numbered tunnel
ifup-sit:#  IPV6ADDR_SECONDARIES="<IPv6 address>[/<prefix length>] ..." (optional) additional local IPv6 addresses
ifup-sit:if [ -n "$IPV6ADDR" ]; then
ifup-sit:	ipv6_add_addr_on_device $DEVICE $IPV6ADDR
ifup-sit:if [ -n "$IPV6ADDR_SECONDARIES" ]; then
ifup-sit:	for ipv6addr in $IPV6ADDR_SECONDARIES; do
~~~

The primary IPV6ADDR is added in ifup-ipv6 via
~~~
# Setup IPv6 address on specified interface
if [ -n "$IPV6ADDR" ]; then
        ipv6_add_addr_on_device $DEVICE $IPV6ADDR || exit 1
fi
~~~

The secondaries are added in ifup-ipv6 via
~~~
(...)
# Setup additional IPv6 addresses from list, if given
if [ -n "$IPV6ADDR_SECONDARIES" ]; then
        for ipv6addr in $IPV6ADDR_SECONDARIES; do
                ipv6_add_addr_on_device $DEVICE $ipv6addr
        done
fi
(...)
~~~

Regards,

Andreas

Comment 6 Andreas Karis 2017-04-19 05:54:43 UTC
I talked to one of our network support engineers, and he immediately mentioned the use of IPV6ADDR_SECONDARIES as well. According to him, sysconfig has had IPADDRn and IPV6ADDR_SECONDARIES for ages, so we can put the interfaces all on ethX - RHEL 5 was about the time alias interfaces became the old way.

Comment 8 Andreas Karis 2017-04-19 06:00:14 UTC
I am currently working on a reproducer for this:

===============================================

 I upgraded cloud-init to the latest version that we have in the repositories (0.7.9-3) and it works like a charm now ...

i) Upgrade cloud-init in RHEL image:
~~~
sudo yumdownloader cloud-init pyserial python-jinja2 python-babel python-markupsafe pytz
for i in *.rpm;do virt-customize -a rhel.qcow2 --upload $i:/root/$i ; done        
virt-customize -a rhel.qcow2 -v --run-command 'yum -y localinstall /root/*.rpm'
glance image-create --name rhel2 --file rhel.qcow2 --container-format bare --disk-format qcow2 --progress
~~~

ii) Configure config drive for network injection:
On the compute node, modify /etc/nova/nova.conf and then restart openstack-nova-compute.service
~~~
[DEFAULT]
injected_network_template = /usr/lib/python2.7/site-packages/nova/virt/interfaces.template
flat_injected=true
force_config_drive = true
config_drive_cdrom = True
debug=true
[libvirt]
inject_partition=-1
~~~

iii) Spawning instance with the new image:
~~~
NETID=88bdc97d-9c3e-4f0d-80c5-00df3f7dc5d1 ; nova boot --nic net-id=$NETID --image rhel2 --flavor m1.small --key-name id_rsa --availability-zone nova:overcloud-compute-1.localdomain rhel2 ; sleep 10 ; nova floating-ip-associate rhel2 10.0.0.114
~~~

iv) Then, after spawning an instance with this image, verify network configuration and cloud-init log:
~~~
[root@rhel2 ~]# mount /dev/sr0 /mnt
mount: /dev/sr0 is write-protected, mounting read-only
[root@rhel2 ~]# cat /mnt/
ec2/       openstack/ 
[root@rhel2 ~]# cat /mnt/openstack/
2012-08-10/ 2013-04-04/ 2013-10-17/ 2015-10-15/ 2016-06-30/ 2016-10-06/ content/    latest/     
[root@rhel2 ~]# cat /mnt/openstack/content/0000 
# Injected by Nova on instance boot
#
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

# The loopback network interface
auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
    hwaddress ether fa:16:3e:23:a4:17
    address 192.168.0.2
    netmask 255.255.255.0
    broadcast 192.168.0.255
    gateway 192.168.0.1
[root@rhel2 ~]# cat /mnt/openstack/latest/
meta_data.json     network_data.json  vendor_data2.json  vendor_data.json   
[root@rhel2 ~]# cat /mnt/openstack/latest/network_data.json 
{"services": [], "networks": [{"network_id": "88bdc97d-9c3e-4f0d-80c5-00df3f7dc5d1", "type": "ipv4", "netmask": "255.255.255.0", "link": "tapeaab7dd8-8c", "routes": [{"netmask": "0.0.0.0", "network": "0.0.0.0", "gateway": "192.168.0.1"}], "ip_address": "192.168.0.2", "id": "network0"}], "links": [{"ethernet_mac_address": "fa:16:3e:23:a4:17", "mtu": 1446, "type": "ovs", "id": "tapeaab7dd8-8c", "vif_id": "eaab7dd8-8c10-4b8d-acab-0e032a6e4c18"}]}[root@rhel2 ~]# grep network_data.json /var/log/cloud-init* -R
/var/log/cloud-init.log:2017-04-19 01:36:17,798 - util.py[DEBUG]: Reading from /tmp/tmp5c01EG/openstack/2015-10-15/network_data.json (quiet=False)
/var/log/cloud-init.log:2017-04-19 01:36:17,798 - util.py[DEBUG]: Read 447 bytes from /tmp/tmp5c01EG/openstack/2015-10-15/network_data.json
[root@rhel2 ~]# less /var/log/cloud-init.log 
[root@rhel2 ~]# cat /etc/sysconfig/network-scripts/ifcfg-eth0
# Created by cloud-init on instance boot automatically, do not edit.
#
BOOTPROTO=static
DEFROUTE=yes
DEVICE=eth0
GATEWAY=192.168.0.1
HWADDR=fa:16:3e:23:a4:17
IPADDR=192.168.0.2
MTU=1446
NETMASK=255.255.255.0
ONBOOT=yes
TYPE=Ethernet
USERCTL=no
[root@rhel2 ~]# ls /etc/sysconfig/network-scripts/ifcfg-*
/etc/sysconfig/network-scripts/ifcfg-eth0  /etc/sysconfig/network-scripts/ifcfg-lo
[root@rhel2 ~]# rpm -qa | grep cloud-init
cloud-init-0.7.9-3.el7.x86_64
~~~

======================================

Next step, I tried:

~~~
/etc/nova/nova.conf
[os_vif_linux_bridge]
use_ipv6 = true
~~~

I am still not getting the IPv6 data into network_data.json 
~~~
[root@rhel2 network-scripts]# cat /mnt/openstack/latest/network_data.json 
{"services": [], "networks": [{"network_id": "88bdc97d-9c3e-4f0d-80c5-00df3f7dc5d1", "type": "ipv4", "netmask": "255.255.255.0", "link": "tap4384c1bb-c2", "routes": [{"netmask": "0.0.0.0", "network": "0.0.0.0", "gateway": "192.168.0.1"}], "ip_address": "192.168.0.10", "id": "network0"}], "links": [{"ethernet_mac_address": "fa:16:3e:49:15:fe", "mtu": 1446, "type": "ovs", "id": "tap4384c1bb-c2", "vif_id": "4384c1bb-c29a-40dc-bd0b-576180de64d1"}]}[root@rhel2 network-scripts]# 
~~~

=============================================

currently checking with the customer what he used to inject ipv6 data into network_data.json.

Comment 9 Andreas Karis 2017-04-20 03:47:27 UTC
Turns out that there is a setting on the compute in /etc/nova/nova.conf for DEFAULT and os_vif_linux_bridge, and that I missed that the setting in DEFAULT was being overwritten by another use_ipv6=false later in the config ...:

[root@overcloud-compute-1 ~]# grep use_ipv6 /etc/nova/nova.conf -B1
config_drive_cdrom = True
use_ipv6 = true
--
[os_vif_linux_bridge]
use_ipv6 = true

Also meaning that the second setting (of which I though that it was important) is irrelevant.

Summary for this:
====================================================

I upgraded cloud-init to the latest version that we have in the repositories (0.7.9-3) and it works like a charm now ...

i) Upgrade cloud-init in RHEL image:
~~~
sudo yumdownloader cloud-init pyserial python-jinja2 python-babel python-markupsafe pytz
for i in *.rpm;do virt-customize -a rhel.qcow2 --upload $i:/root/$i ; done        
virt-customize -a rhel.qcow2 -v --run-command 'yum -y localinstall /root/*.rpm'
glance image-create --name rhel2 --file rhel.qcow2 --container-format bare --disk-format qcow2 --progress
~~~

ii) Configure config drive for network injection:
On the compute node, modify /etc/nova/nova.conf and then restart openstack-nova-compute.service
~~~
[DEFAULT]
injected_network_template = /usr/lib/python2.7/site-packages/nova/virt/interfaces.template
flat_injected=true
force_config_drive = true
config_drive_cdrom = True
debug=true
use_ipv6=true
[libvirt]
inject_partition=-1
~~~

iii) Configure 3 networks with 5 subnets:
~~~
[stack@undercloud-1 ~]$ neutron net-list
+--------------------------------------+-----------+--------------------------------------------------------+
| id                                   | name      | subnets                                                |
+--------------------------------------+-----------+--------------------------------------------------------+
| 7cf160c2-4423-4044-bdfb-23887da5c6e0 | provider1 | b373bceb-6770-4e6b-b53c-f63b979b67be 2000:10::/64      |
|                                      |           | 0da11b66-dbcf-445f-ae46-8347b0ebcbf1 10.0.0.0/24       |
| 88bdc97d-9c3e-4f0d-80c5-00df3f7dc5d1 | private   | f0306d30-b908-41db-bb99-75a77ce61ca1 192.168.0.0/24    |
|                                      |           | 63cc5607-a084-4f00-938f-be47f8f18362 2000:192:168::/64 |
| ae53dd16-27d9-40e0-b0b5-36e1140a7e0b | ipv6      | b84ff03d-2568-4099-aaef-f42c5c0be180 2000:123::/64     |
+--------------------------------------+-----------+--------------------------------------------------------+
[stack@undercloud-1 ~]$ neutron net-show 7cf160c2-4423-4044-bdfb-23887da5c6e0 ; neutron net-show 88bdc97d-9c3e-4f0d-80c5-00df3f7dc5d1 ; neutron net-show ae53dd16-27d9-40e0-b0b5-36e1140a7e0b ; neutron subnet-show b373bceb-6770-4e6b-b53c-f63b979b67be ; neutron subnet-show 0da11b66-dbcf-445f-ae46-8347b0ebcbf1 ; neutron subnet-show f0306d30-b908-41db-bb99-75a77ce61ca1 ; neutron subnet-show 63cc5607-a084-4f00-938f-be47f8f18362 ; neutron subnet-show b84ff03d-2568-4099-aaef-f42c5c0be180
+---------------------------+--------------------------------------+
| Field                     | Value                                |
+---------------------------+--------------------------------------+
| admin_state_up            | True                                 |
| availability_zone_hints   |                                      |
| availability_zones        | nova                                 |
| created_at                | 2017-04-09T03:54:41Z                 |
| description               |                                      |
| id                        | 7cf160c2-4423-4044-bdfb-23887da5c6e0 |
| ipv4_address_scope        |                                      |
| ipv6_address_scope        |                                      |
| is_default                | False                                |
| mtu                       | 1496                                 |
| name                      | provider1                            |
| port_security_enabled     | True                                 |
| project_id                | 49ebd4ddc3b242c1909939157ae77c55     |
| provider:network_type     | vlan                                 |
| provider:physical_network | datacentre                           |
| provider:segmentation_id  | 905                                  |
| qos_policy_id             |                                      |
| revision_number           | 8                                    |
| router:external           | True                                 |
| shared                    | True                                 |
| status                    | ACTIVE                               |
| subnets                   | b373bceb-6770-4e6b-b53c-f63b979b67be |
|                           | 0da11b66-dbcf-445f-ae46-8347b0ebcbf1 |
| tags                      |                                      |
| tenant_id                 | 49ebd4ddc3b242c1909939157ae77c55     |
| updated_at                | 2017-04-20T02:45:23Z                 |
+---------------------------+--------------------------------------+
+---------------------------+--------------------------------------+
| Field                     | Value                                |
+---------------------------+--------------------------------------+
| admin_state_up            | True                                 |
| availability_zone_hints   |                                      |
| availability_zones        | nova                                 |
| created_at                | 2017-04-09T03:54:40Z                 |
| description               |                                      |
| id                        | 88bdc97d-9c3e-4f0d-80c5-00df3f7dc5d1 |
| ipv4_address_scope        |                                      |
| ipv6_address_scope        |                                      |
| mtu                       | 1446                                 |
| name                      | private                              |
| port_security_enabled     | True                                 |
| project_id                | 49ebd4ddc3b242c1909939157ae77c55     |
| provider:network_type     | vxlan                                |
| provider:physical_network |                                      |
| provider:segmentation_id  | 44                                   |
| qos_policy_id             |                                      |
| revision_number           | 27                                   |
| router:external           | False                                |
| shared                    | False                                |
| status                    | ACTIVE                               |
| subnets                   | 63cc5607-a084-4f00-938f-be47f8f18362 |
|                           | f0306d30-b908-41db-bb99-75a77ce61ca1 |
| tags                      |                                      |
| tenant_id                 | 49ebd4ddc3b242c1909939157ae77c55     |
| updated_at                | 2017-04-20T02:28:56Z                 |
+---------------------------+--------------------------------------+
+---------------------------+--------------------------------------+
| Field                     | Value                                |
+---------------------------+--------------------------------------+
| admin_state_up            | True                                 |
| availability_zone_hints   |                                      |
| availability_zones        | nova                                 |
| created_at                | 2017-04-20T02:56:13Z                 |
| description               |                                      |
| id                        | ae53dd16-27d9-40e0-b0b5-36e1140a7e0b |
| ipv4_address_scope        |                                      |
| ipv6_address_scope        |                                      |
| mtu                       | 1446                                 |
| name                      | ipv6                                 |
| port_security_enabled     | True                                 |
| project_id                | 49ebd4ddc3b242c1909939157ae77c55     |
| provider:network_type     | vxlan                                |
| provider:physical_network |                                      |
| provider:segmentation_id  | 21                                   |
| qos_policy_id             |                                      |
| revision_number           | 5                                    |
| router:external           | False                                |
| shared                    | False                                |
| status                    | ACTIVE                               |
| subnets                   | b84ff03d-2568-4099-aaef-f42c5c0be180 |
| tags                      |                                      |
| tenant_id                 | 49ebd4ddc3b242c1909939157ae77c55     |
| updated_at                | 2017-04-20T02:56:36Z                 |
+---------------------------+--------------------------------------+
+-------------------+------------------------------------------------------------------+
| Field             | Value                                                            |
+-------------------+------------------------------------------------------------------+
| allocation_pools  | {"start": "2000:10::251", "end": "2000:10::ffff:ffff:ffff:ffff"} |
|                   | {"start": "2000:10::1", "end": "2000:10::24f"}                   |
| cidr              | 2000:10::/64                                                     |
| created_at        | 2017-04-20T02:29:01Z                                             |
| description       |                                                                  |
| dns_nameservers   |                                                                  |
| enable_dhcp       | False                                                            |
| gateway_ip        | 2000:10::250                                                     |
| host_routes       |                                                                  |
| id                | b373bceb-6770-4e6b-b53c-f63b979b67be                             |
| ip_version        | 6                                                                |
| ipv6_address_mode |                                                                  |
| ipv6_ra_mode      |                                                                  |
| name              | provider1-ipv6-subnet                                            |
| network_id        | 7cf160c2-4423-4044-bdfb-23887da5c6e0                             |
| project_id        | 49ebd4ddc3b242c1909939157ae77c55                                 |
| revision_number   | 2                                                                |
| service_types     |                                                                  |
| subnetpool_id     |                                                                  |
| tenant_id         | 49ebd4ddc3b242c1909939157ae77c55                                 |
| updated_at        | 2017-04-20T02:29:01Z                                             |
+-------------------+------------------------------------------------------------------+
+-------------------+----------------------------------------------+
| Field             | Value                                        |
+-------------------+----------------------------------------------+
| allocation_pools  | {"start": "10.0.0.100", "end": "10.0.0.150"} |
| cidr              | 10.0.0.0/24                                  |
| created_at        | 2017-04-09T03:54:41Z                         |
| description       |                                              |
| dns_nameservers   | 8.8.8.8                                      |
| enable_dhcp       | False                                        |
| gateway_ip        | 10.0.0.250                                   |
| host_routes       |                                              |
| id                | 0da11b66-dbcf-445f-ae46-8347b0ebcbf1         |
| ip_version        | 4                                            |
| ipv6_address_mode |                                              |
| ipv6_ra_mode      |                                              |
| name              | provider1-subnet                             |
| network_id        | 7cf160c2-4423-4044-bdfb-23887da5c6e0         |
| project_id        | 49ebd4ddc3b242c1909939157ae77c55             |
| revision_number   | 3                                            |
| service_types     |                                              |
| subnetpool_id     |                                              |
| tenant_id         | 49ebd4ddc3b242c1909939157ae77c55             |
| updated_at        | 2017-04-20T02:45:23Z                         |
+-------------------+----------------------------------------------+
+-------------------+--------------------------------------------------+
| Field             | Value                                            |
+-------------------+--------------------------------------------------+
| allocation_pools  | {"start": "192.168.0.2", "end": "192.168.0.254"} |
| cidr              | 192.168.0.0/24                                   |
| created_at        | 2017-04-09T03:54:45Z                             |
| description       |                                                  |
| dns_nameservers   |                                                  |
| enable_dhcp       | False                                            |
| gateway_ip        | 192.168.0.1                                      |
| host_routes       |                                                  |
| id                | f0306d30-b908-41db-bb99-75a77ce61ca1             |
| ip_version        | 4                                                |
| ipv6_address_mode |                                                  |
| ipv6_ra_mode      |                                                  |
| name              | private-subnet                                   |
| network_id        | 88bdc97d-9c3e-4f0d-80c5-00df3f7dc5d1             |
| project_id        | 49ebd4ddc3b242c1909939157ae77c55                 |
| revision_number   | 3                                                |
| service_types     |                                                  |
| subnetpool_id     |                                                  |
| tenant_id         | 49ebd4ddc3b242c1909939157ae77c55                 |
| updated_at        | 2017-04-19T01:57:58Z                             |
+-------------------+--------------------------------------------------+
+-------------------+---------------------------------------------------------------------------+
| Field             | Value                                                                     |
+-------------------+---------------------------------------------------------------------------+
| allocation_pools  | {"start": "2000:192:168::2", "end": "2000:192:168:0:ffff:ffff:ffff:ffff"} |
| cidr              | 2000:192:168::/64                                                         |
| created_at        | 2017-04-20T02:24:02Z                                                      |
| description       |                                                                           |
| dns_nameservers   |                                                                           |
| enable_dhcp       | False                                                                     |
| gateway_ip        | 2000:192:168::1                                                           |
| host_routes       |                                                                           |
| id                | 63cc5607-a084-4f00-938f-be47f8f18362                                      |
| ip_version        | 6                                                                         |
| ipv6_address_mode |                                                                           |
| ipv6_ra_mode      |                                                                           |
| name              | private-ipv6-subnet                                                       |
| network_id        | 88bdc97d-9c3e-4f0d-80c5-00df3f7dc5d1                                      |
| project_id        | 49ebd4ddc3b242c1909939157ae77c55                                          |
| revision_number   | 2                                                                         |
| service_types     |                                                                           |
| subnetpool_id     |                                                                           |
| tenant_id         | 49ebd4ddc3b242c1909939157ae77c55                                          |
| updated_at        | 2017-04-20T02:24:02Z                                                      |
+-------------------+---------------------------------------------------------------------------+
+-------------------+------------------------------------------------------------------+
| Field             | Value                                                            |
+-------------------+------------------------------------------------------------------+
| allocation_pools  | {"start": "2000:123::1", "end": "2000:123::ffff:ffff:ffff:ffff"} |
| cidr              | 2000:123::/64                                                    |
| created_at        | 2017-04-20T02:56:36Z                                             |
| description       |                                                                  |
| dns_nameservers   |                                                                  |
| enable_dhcp       | False                                                            |
| gateway_ip        | 2000:10::250                                                     |
| host_routes       |                                                                  |
| id                | b84ff03d-2568-4099-aaef-f42c5c0be180                             |
| ip_version        | 6                                                                |
| ipv6_address_mode |                                                                  |
| ipv6_ra_mode      |                                                                  |
| name              | ipv6-subnet                                                      |
| network_id        | ae53dd16-27d9-40e0-b0b5-36e1140a7e0b                             |
| project_id        | 49ebd4ddc3b242c1909939157ae77c55                                 |
| revision_number   | 2                                                                |
| service_types     |                                                                  |
| subnetpool_id     |                                                                  |
| tenant_id         | 49ebd4ddc3b242c1909939157ae77c55                                 |
| updated_at        | 2017-04-20T02:56:36Z                                             |
+-------------------+------------------------------------------------------------------+
~~~

iv) Spawn an instance on all 3 networks
~~~
nova boot --nic net-id=88bdc97d-9c3e-4f0d-80c5-00df3f7dc5d1 --nic net-id=7cf160c2-4423-4044-bdfb-23887da5c6e0 --nic net-id=ae53dd16-27d9-40e0-b0b5-36e1140a7e0b --config-drive=True  --image rhel2 --flavor m1.small --key-name id_rsa --availability-zone nova:overcloud-compute-1.localdomain rhel2
~~~

v) Verify instance configuration and config-drive
~~~
[root@rhel2 ~]# cat /mnt/openstack/latest/network_data.json | python -m json.tool
{
    "links": [
        {
            "ethernet_mac_address": "fa:16:3e:61:a3:f3",
            "id": "tapda32bb2c-06",
            "mtu": 1446,
            "type": "ovs",
            "vif_id": "da32bb2c-0624-4553-9fbd-aab434440324"
        },
        {
            "ethernet_mac_address": "fa:16:3e:d7:f5:e6",
            "id": "tap7f77b17a-3c",
            "mtu": 1496,
            "type": "ovs",
            "vif_id": "7f77b17a-3c1e-46ca-a6e4-9265515e3b16"
        },
        {
            "ethernet_mac_address": "fa:16:3e:7a:38:5e",
            "id": "tap1a385c51-f6",
            "mtu": 1446,
            "type": "ovs",
            "vif_id": "1a385c51-f67d-4da8-b8c8-059abf0d6b03"
        }
    ],
    "networks": [
        {
            "id": "network0",
            "ip_address": "192.168.0.8",
            "link": "tapda32bb2c-06",
            "netmask": "255.255.255.0",
            "network_id": "88bdc97d-9c3e-4f0d-80c5-00df3f7dc5d1",
            "routes": [
                {
                    "gateway": "192.168.0.1",
                    "netmask": "0.0.0.0",
                    "network": "0.0.0.0"
                }
            ],
            "type": "ipv4"
        },
        {
            "id": "network1",
            "ip_address": "2000:192:168::6",
            "link": "tapda32bb2c-06",
            "netmask": "ffff:ffff:ffff:ffff::",
            "network_id": "88bdc97d-9c3e-4f0d-80c5-00df3f7dc5d1",
            "routes": [
                {
                    "gateway": "2000:192:168::1",
                    "netmask": "::",
                    "network": "::"
                }
            ],
            "type": "ipv6"
        },
        {
            "id": "network2",
            "ip_address": "10.0.0.103",
            "link": "tap7f77b17a-3c",
            "netmask": "255.255.255.0",
            "network_id": "7cf160c2-4423-4044-bdfb-23887da5c6e0",
            "routes": [
                {
                    "gateway": "10.0.0.250",
                    "netmask": "0.0.0.0",
                    "network": "0.0.0.0"
                }
            ],
            "type": "ipv4"
        },
        {
            "id": "network3",
            "ip_address": "2000:10::b",
            "link": "tap7f77b17a-3c",
            "netmask": "ffff:ffff:ffff:ffff::",
            "network_id": "7cf160c2-4423-4044-bdfb-23887da5c6e0",
            "routes": [
                {
                    "gateway": "2000:10::250",
                    "netmask": "::",
                    "network": "::"
                }
            ],
            "type": "ipv6"
        },
        {
            "id": "network4",
            "ip_address": "2000:123::7",
            "link": "tap1a385c51-f6",
            "netmask": "ffff:ffff:ffff:ffff::",
            "network_id": "ae53dd16-27d9-40e0-b0b5-36e1140a7e0b",
            "routes": [
                {
                    "gateway": "2000:10::250",
                    "netmask": "::",
                    "network": "::"
                }
            ],
            "type": "ipv6"
        }
    ],
    "services": [
        {
            "address": "8.8.8.8",
            "type": "dns"
        }
    ]
}
~~~

~~~
[root@rhel2 ~]# cat /mnt/openstack/latest/network_data.json | python -m json.tool
{
    "links": [
        {
            "ethernet_mac_address": "fa:16:3e:61:a3:f3",
            "id": "tapda32bb2c-06",
            "mtu": 1446,
            "type": "ovs",
            "vif_id": "da32bb2c-0624-4553-9fbd-aab434440324"
        },
        {
            "ethernet_mac_address": "fa:16:3e:d7:f5:e6",
            "id": "tap7f77b17a-3c",
            "mtu": 1496,
            "type": "ovs",
            "vif_id": "7f77b17a-3c1e-46ca-a6e4-9265515e3b16"
        },
        {
            "ethernet_mac_address": "fa:16:3e:7a:38:5e",
            "id": "tap1a385c51-f6",
            "mtu": 1446,
            "type": "ovs",
            "vif_id": "1a385c51-f67d-4da8-b8c8-059abf0d6b03"
        }
    ],
    "networks": [
        {
            "id": "network0",
            "ip_address": "192.168.0.8",
            "link": "tapda32bb2c-06",
            "netmask": "255.255.255.0",
            "network_id": "88bdc97d-9c3e-4f0d-80c5-00df3f7dc5d1",
            "routes": [
                {
                    "gateway": "192.168.0.1",
                    "netmask": "0.0.0.0",
                    "network": "0.0.0.0"
                }
            ],
            "type": "ipv4"
        },
        {
            "id": "network1",
            "ip_address": "2000:192:168::6",
            "link": "tapda32bb2c-06",
            "netmask": "ffff:ffff:ffff:ffff::",
            "network_id": "88bdc97d-9c3e-4f0d-80c5-00df3f7dc5d1",
            "routes": [
                {
                    "gateway": "2000:192:168::1",
                    "netmask": "::",
                    "network": "::"
                }
            ],
            "type": "ipv6"
        },
        {
            "id": "network2",
            "ip_address": "10.0.0.103",
            "link": "tap7f77b17a-3c",
            "netmask": "255.255.255.0",
            "network_id": "7cf160c2-4423-4044-bdfb-23887da5c6e0",
            "routes": [
                {
                    "gateway": "10.0.0.250",
                    "netmask": "0.0.0.0",
                    "network": "0.0.0.0"
                }
            ],
            "type": "ipv4"
        },
        {
            "id": "network3",
            "ip_address": "2000:10::b",
            "link": "tap7f77b17a-3c",
            "netmask": "ffff:ffff:ffff:ffff::",
            "network_id": "7cf160c2-4423-4044-bdfb-23887da5c6e0",
            "routes": [
                {
                    "gateway": "2000:10::250",
                    "netmask": "::",
                    "network": "::"
                }
            ],
            "type": "ipv6"
        },
        {
            "id": "network4",
            "ip_address": "2000:123::7",
            "link": "tap1a385c51-f6",
            "netmask": "ffff:ffff:ffff:ffff::",
            "network_id": "ae53dd16-27d9-40e0-b0b5-36e1140a7e0b",
            "routes": [
                {
                    "gateway": "2000:10::250",
                    "netmask": "::",
                    "network": "::"
                }
            ],
            "type": "ipv6"
        }
    ],
    "services": [
        {
            "address": "8.8.8.8",
            "type": "dns"
        }
    ]
}
[root@rhel2 ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1446 qdisc pfifo_fast state UP qlen 1000
    link/ether fa:16:3e:61:a3:f3 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.8/24 brd 192.168.0.255 scope global eth0:0
       valid_lft forever preferred_lft forever
    inet6 fe80::f816:3eff:fe61:a3f3/64 scope link 
       valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1496 qdisc pfifo_fast state UP qlen 1000
    link/ether fa:16:3e:d7:f5:e6 brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.103/24 brd 10.0.0.255 scope global eth1:0
       valid_lft forever preferred_lft forever
    inet6 fe80::f816:3eff:fed7:f5e6/64 scope link 
       valid_lft forever preferred_lft forever
4: eth2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1446 qdisc pfifo_fast state UP qlen 1000
    link/ether fa:16:3e:7a:38:5e brd ff:ff:ff:ff:ff:ff
    inet6 2000:123::7/64 scope global 
       valid_lft forever preferred_lft forever
    inet6 fe80::f816:3eff:fe7a:385e/64 scope link 
       valid_lft forever preferred_lft forever
[root@rhel2 ~]# ip -6 a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 state UNKNOWN qlen 1
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1446 state UP qlen 1000
    inet6 fe80::f816:3eff:fe61:a3f3/64 scope link 
       valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1496 state UP qlen 1000
    inet6 fe80::f816:3eff:fed7:f5e6/64 scope link 
       valid_lft forever preferred_lft forever
4: eth2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1446 state UP qlen 1000
    inet6 2000:123::7/64 scope global 
       valid_lft forever preferred_lft forever
    inet6 fe80::f816:3eff:fe7a:385e/64 scope link 
       valid_lft forever preferred_lft forever
[root@rhel2 ~]# ls /etc/sysconfig/network-scripts/ifcfg-*
/etc/sysconfig/network-scripts/ifcfg-eth0    /etc/sysconfig/network-scripts/ifcfg-eth1:0
/etc/sysconfig/network-scripts/ifcfg-eth0:0  /etc/sysconfig/network-scripts/ifcfg-eth1:1
/etc/sysconfig/network-scripts/ifcfg-eth0:1  /etc/sysconfig/network-scripts/ifcfg-eth2
/etc/sysconfig/network-scripts/ifcfg-eth1    /etc/sysconfig/network-scripts/ifcfg-lo
~~~

~~~
[root@rhel2 network-scripts]# cat ifcfg-eth0
# Created by cloud-init on instance boot automatically, do not edit.
#
BOOTPROTO=none
DEVICE=eth0
HWADDR=fa:16:3e:61:a3:f3
MTU=1446
ONBOOT=yes
TYPE=Ethernet
USERCTL=no
[root@rhel2 network-scripts]# cat ifcfg-eth0:0
# Created by cloud-init on instance boot automatically, do not edit.
#
BOOTPROTO=static
DEFROUTE=yes
DEVICE=eth0:0
GATEWAY=192.168.0.1
HWADDR=fa:16:3e:61:a3:f3
IPADDR=192.168.0.8
MTU=1446
NETMASK=255.255.255.0
ONBOOT=yes
TYPE=Ethernet
USERCTL=no
[root@rhel2 network-scripts]# cat ifcfg-eth0:1
# Created by cloud-init on instance boot automatically, do not edit.
#
BOOTPROTO=static
DEFROUTE=yes
DEVICE=eth0:1
HWADDR=fa:16:3e:61:a3:f3
IPV6ADDR=2000:192:168::6
IPV6INIT=yes
IPV6_DEFAULTGW=2000:192:168::1
MTU=1446
NETMASK=64
ONBOOT=yes
TYPE=Ethernet
USERCTL=no
[root@rhel2 network-scripts]# cat ifcfg-eth1:0
# Created by cloud-init on instance boot automatically, do not edit.
#
BOOTPROTO=static
DEFROUTE=yes
DEVICE=eth1:0
GATEWAY=10.0.0.250
HWADDR=fa:16:3e:d7:f5:e6
IPADDR=10.0.0.103
MTU=1496
NETMASK=255.255.255.0
ONBOOT=yes
TYPE=Ethernet
USERCTL=no
[root@rhel2 network-scripts]# cat ifcfg-eth1:1
# Created by cloud-init on instance boot automatically, do not edit.
#
BOOTPROTO=static
DEFROUTE=yes
DEVICE=eth1:1
HWADDR=fa:16:3e:d7:f5:e6
IPV6ADDR=2000:10::b
IPV6INIT=yes
IPV6_DEFAULTGW=2000:10::250
MTU=1496
NETMASK=64
ONBOOT=yes
TYPE=Ethernet
USERCTL=no
[root@rhel2 network-scripts]# cat ifcfg-eth2
# Created by cloud-init on instance boot automatically, do not edit.
#
BOOTPROTO=static
DEFROUTE=yes
DEVICE=eth2
HWADDR=fa:16:3e:7a:38:5e
IPV6ADDR=2000:123::7
IPV6INIT=yes
IPV6_DEFAULTGW=2000:10::250
MTU=1446
NETMASK=64
ONBOOT=yes
TYPE=Ethernet
USERCTL=no
~~~

Comment 10 Andreas Karis 2017-04-20 05:05:29 UTC
Hi,

I discovered another bug in cloud-init while testing (and filed it):
https://bugs.launchpad.net/cloud-init/+bug/1684349

I also identified the code that is responsible for subinterfaces:
/usr/lib/python2.7/site-packages/cloudinit/net/sysconfig.py
~~~
    @classmethod
    def _render_physical_interfaces(cls, network_state, iface_contents):
        physical_filter = renderer.filter_by_physical
        for iface in network_state.iter_interfaces(physical_filter):
            iface_name = iface['name']
            iface_subnets = iface.get("subnets", [])
            iface_cfg = iface_contents[iface_name]
            route_cfg = iface_cfg.routes
            if len(iface_subnets) == 1:
                cls._render_subnet(iface_cfg, route_cfg, iface_subnets[0])
            elif len(iface_subnets) > 1:
                for i, iface_subnet in enumerate(iface_subnets,
                                                 start=len(iface_cfg.children)):
                    iface_sub_cfg = iface_cfg.copy()
                    iface_sub_cfg.name = "%s:%s" % (iface_name, i)
                    iface_cfg.children.append(iface_sub_cfg)
                    cls._render_subnet(iface_sub_cfg, route_cfg, iface_subnet)
~~~

And a git blame on the master branch reveals:
~~~
f47d811a (Joshua Harlow        2016-06-10 14:26:30 -0700 295)             iface_subnets = iface.get("subnets", [])
f47d811a (Joshua Harlow        2016-06-10 14:26:30 -0700 296)             iface_cfg = iface_contents[iface_name]
f47d811a (Joshua Harlow        2016-06-10 14:26:30 -0700 297)             route_cfg = iface_cfg.routes
f47d811a (Joshua Harlow        2016-06-10 14:26:30 -0700 298)             if len(iface_subnets) == 1:
f47d811a (Joshua Harlow        2016-06-10 14:26:30 -0700 299)                 cls._render_subnet(iface_cfg, route_cfg, iface_subnets[0])
f47d811a (Joshua Harlow        2016-06-10 14:26:30 -0700 300)             elif len(iface_subnets) > 1:
da25385d (Scott Moser          2017-02-17 12:05:38 -0500 301)                 for i, isubnet in enumerate(iface_subnets,
da25385d (Scott Moser          2017-02-17 12:05:38 -0500 302)                                             start=len(iface_cfg.children)):
f47d811a (Joshua Harlow        2016-06-10 14:26:30 -0700 303)                     iface_sub_cfg = iface_cfg.copy()
f47d811a (Joshua Harlow        2016-06-10 14:26:30 -0700 304)                     iface_sub_cfg.name = "%s:%s" % (iface_name, i)
f81d6c7b (Lars Kellogg-Stedman 2017-02-17 08:55:05 -0500 305)                     iface_cfg.children.append(iface_sub_cfg)
da25385d (Scott Moser          2017-02-17 12:05:38 -0500 306)                     cls._render_subnet(iface_sub_cfg, route_cfg, isubnet)
~~~

Introduced by commit:
~~~
commit f47d811af4f2ae0977db076b3eedb27634da2132
Author: Joshua Harlow <harlowja>
Date:   Fri Jun 10 14:26:30 2016 -0700

    Add a sysconfig renderer
~~~

IMO, we should replace _render_subnet with a method _render_subnets and we should get rid of the .length condition.

The new code needs to be able to parse:
~~~
{'subnets': [{u'routes': [{u'netmask': 0, u'network': u'::', u'gateway': u'2000:10::250'}], u'netmask': 64, 'address': u'2000:123::7', u'type': 'static', 'ipv6': True}], 'name': 'eth2', 'address': None, 'mtu': 1446, 'mode': 'manual', 'mac_address': u'fa:16:3e:0d:bc:68', 'type': 'physical', 'gateway': None, 'inet': 'inet'}
eth1
{'subnets': [{u'routes': [{u'netmask': u'0.0.0.0', u'network': u'0.0.0.0', u'gateway': u'10.0.0.250'}], u'netmask': u'255.255.255.0', u'type': 'static', 'ipv4': True, 'address': u'10.0.0.111'}, {u'routes': [{u'netmask': 0, u'network': u'::', u'gateway': u'2000:10::250'}], u'netmask': 64, 'address': u'2000:10::9', u'type': 'static', 'ipv6': True}], 'name': 'eth1', 'address': None, 'mtu': 1496, 'mode': 'manual', 'mac_address': u'fa:16:3e:18:ca:3d', 'type': 'physical', 'gateway': None, 'inet': 'inet'}
eth0
{'subnets': [{u'routes': [{u'netmask': u'0.0.0.0', u'network': u'0.0.0.0', u'gateway': u'192.168.0.1'}], u'netmask': u'255.255.255.0', u'type': 'static', 'ipv4': True, 'address': u'192.168.0.11'}, {u'routes': [{u'netmask': 0, u'network': u'::', u'gateway': u'2000:192:168::1'}], u'netmask': 64, 'address': u'2000:192:168::4', u'type': 'static', 'ipv6': True}], 'name': 'eth0', 'address': None, 'mtu': 1446, 'mode': 'manual', 'mac_address': u'fa:16:3e:84:0c:82', 'type': 'physical', 'gateway': None, 'inet': 'inet'}
~~~

And a subnets array:
[
{u'routes': [{u'netmask': u'0.0.0.0', u'network': u'0.0.0.0', u'gateway': u'10.0.0.250'}], u'netmask': u'255.255.255.0', u'type': 'static', 'ipv4': True, 'address': u'10.0.0.111'}, 
{u'routes': [{u'netmask': 0, u'network': u'::', u'gateway': u'2000:10::250'}], u'netmask': 64, 'address': u'2000:10::9', u'type': 'static', 'ipv6': True}
]

So, in code:
~~~
    @classmethod
    def _render_physical_interfaces(cls, network_state, iface_contents):
        physical_filter = renderer.filter_by_physical
        for iface in network_state.iter_interfaces(physical_filter):
            iface_name = iface['name']
            print iface_name
            print iface
            iface_subnets = iface.get("subnets", [])
            iface_cfg = iface_contents[iface_name]
            route_cfg = iface_cfg.routes
            if len(iface_subnets) >= 1:
                cls._render_subnets(iface_cfg, route_cfg, iface_subnets[0])
            #elif len(iface_subnets) > 1:
            #    for i, iface_subnet in enumerate(iface_subnets,
            #                                     start=len(iface_cfg.children)):
            #        iface_sub_cfg = iface_cfg.copy()
            #        iface_sub_cfg.name = "%s:%s" % (iface_name, i)
            #        iface_cfg.children.append(iface_sub_cfg)
            #        cls._render_subnet(iface_sub_cfg, route_cfg, iface_subnet)
~~~

The next step though is to convert iface_subnet to iface_subnets ... and I hope that you see here that this is not so trivial:
* while the subnets array contains 'equal' subnets which can be static, dhcp, etc. ... and is not hierarchical ... note that the RHEL network config scripts have a hierarchy, by using IPV6ADDRESS and secondary addresses, and by using IPADDR and IPADDR2, etc ... and what happens if dhcp and static are mixed?

I am tired now. Looking into this tomorrow.

- Andreas

Comment 11 Andreas Karis 2017-04-22 20:21:25 UTC
Hi,

I fixed the code and also updated the unit tests (which contained invalid IPv6 alias files, no surprise here).

This is the merge request:
https://code.launchpad.net/~akaris/cloud-init/+git/cloud-init/+merge/322998

The merge is currently waiting for review.

Here are the changes (from master, but the sysconfig.py file has not diverged too much between the newest RHEL version and master):

diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
index 504e4d0..babc5cd 100644
--- a/cloudinit/net/sysconfig.py
+++ b/cloudinit/net/sysconfig.py
@@ -59,6 +59,9 @@ class ConfigMap(object):
     def __setitem__(self, key, value):
         self._conf[key] = value
 
+    def __getitem__(self, key):
+        return self._conf[key]
+
     def drop(self, key):
         self._conf.pop(key, None)
 
@@ -211,27 +214,67 @@ class Renderer(renderer.Renderer):
                 iface_cfg[new_key] = old_value
 
     @classmethod
-    def _render_subnet(cls, iface_cfg, route_cfg, subnet):
-        subnet_type = subnet.get('type')
-        if subnet_type == 'dhcp6':
-            iface_cfg['DHCPV6C'] = True
-            iface_cfg['IPV6INIT'] = True
-            iface_cfg['BOOTPROTO'] = 'dhcp'
-        elif subnet_type in ['dhcp4', 'dhcp']:
-            iface_cfg['BOOTPROTO'] = 'dhcp'
-        elif subnet_type == 'static':
-            iface_cfg['BOOTPROTO'] = 'static'
-            if subnet_is_ipv6(subnet):
-                iface_cfg['IPV6ADDR'] = subnet['address']
+    def _render_subnets(cls, iface_cfg, subnets):
+        # setting base values
+        # iface_cfg['DHCPV6C'] = False
+        # iface_cfg['IPV6INIT'] = False
+        iface_cfg['BOOTPROTO'] = 'none'
+        
+        # modifying base values according to subnets
+        for i, subnet in enumerate(subnets,start=len(iface_cfg.children)):
+            subnet_type = subnet.get('type')
+            if subnet_type == 'dhcp6':
                 iface_cfg['IPV6INIT'] = True
+                iface_cfg['DHCPV6C'] = True
+                iface_cfg['BOOTPROTO'] = 'dhcp'
+            elif subnet_type in ['dhcp4', 'dhcp']:
+                iface_cfg['BOOTPROTO'] = 'dhcp'
+            elif subnet_type == 'static':
+                if iface_cfg['BOOTPROTO'] == 'none':
+                    iface_cfg['BOOTPROTO'] = 'static'
+                if subnet_is_ipv6(subnet):
+                    iface_cfg['IPV6INIT'] = True
             else:
-                iface_cfg['IPADDR'] = subnet['address']
-        else:
-            raise ValueError("Unknown subnet type '%s' found"
-                             " for interface '%s'" % (subnet_type,
+                raise ValueError("Unknown subnet type '%s' found"
+                                 " for interface '%s'" % (subnet_type,
                                                       iface_cfg.name))
-        if 'netmask' in subnet:
-            iface_cfg['NETMASK'] = subnet['netmask']
+
+        # set IPv4 and IPv6 static addresses
+        ipv4_index = -1
+        ipv6_index = -1
+        for i, subnet in enumerate(subnets,start=len(iface_cfg.children)):
+            subnet_type = subnet.get('type')
+            if subnet_type == 'dhcp6':
+                continue
+            elif subnet_type in ['dhcp4', 'dhcp']:
+                continue
+            elif subnet_type == 'static':
+                if subnet_is_ipv6(subnet):
+                    ipv6_index = ipv6_index + 1
+                    if 'netmask' in subnet and subnet['netmask'] != "":
+                        ipv6_cidr = subnet['address'] + '/' + subnet['netmask']
+                    else:
+                        ipv6_cidr = subnet['address']
+                    if ipv6_index == 0:
+                        iface_cfg['IPV6ADDR'] = ipv6_cidr
+                    elif ipv6_index == 1:
+                        iface_cfg['IPV6ADDR_SECONDARIES'] = ipv6_cidr
+                    else:
+                        iface_cfg['IPV6ADDR_SECONDARIES'] = iface_cfg['IPV6ADDR_SECONDARIES'] + " " + ipv6_cidr
+                else:
+                    ipv4_index = ipv4_index + 1
+                    if ipv4_index == 0:
+                        iface_cfg['IPADDR'] = subnet['address']
+                        if 'netmask' in subnet:
+                            iface_cfg['NETMASK'] = subnet['netmask']
+                    else:
+                        iface_cfg['IPADDR' + str(ipv4_index)] = subnet['address']
+                        if 'netmask' in subnet:
+                            iface_cfg['NETMASK' + str(ipv4_index)] = subnet['netmask']
+
+    @classmethod 
+    def _render_subnet_routes(cls, iface_cfg, route_cfg, subnets):
+      for i, subnet in enumerate(subnets,start=len(iface_cfg.children)):
         for route in subnet.get('routes', []):
             if subnet.get('ipv6'):
                 gw_cfg = 'IPV6_DEFAULTGW'
@@ -295,15 +338,9 @@ class Renderer(renderer.Renderer):
             iface_subnets = iface.get("subnets", [])
             iface_cfg = iface_contents[iface_name]
             route_cfg = iface_cfg.routes
-            if len(iface_subnets) == 1:
-                cls._render_subnet(iface_cfg, route_cfg, iface_subnets[0])
-            elif len(iface_subnets) > 1:
-                for i, isubnet in enumerate(iface_subnets,
-                                            start=len(iface_cfg.children)):
-                    iface_sub_cfg = iface_cfg.copy()
-                    iface_sub_cfg.name = "%s:%s" % (iface_name, i)
-                    iface_cfg.children.append(iface_sub_cfg)
-                    cls._render_subnet(iface_sub_cfg, route_cfg, isubnet)
+
+            cls._render_subnets(iface_cfg, iface_subnets)
+ cls._render_subnet_routes(iface_cfg, route_cfg, iface_subnets)

Comment 12 Andreas Karis 2017-04-25 21:44:28 UTC
Hi,

Can we do something to speed up the merge of the upstream commit and possibly get the customer a hotfix RPM?

- Andreas

Comment 14 Lars Kellogg-Stedman 2017-05-01 19:54:18 UTC
Andreas,

The original merge request, https://code.launchpad.net/~akaris/cloud-init/+git/cloud-init/+merge/322998, is marked as "superseded", and the first url from comment #13,
https://code.launchpad.net/~akaris/cloud-init/+git/cloud-init/+merge/323089, leads to a 404.  Would you mind posting updated links?  Thanks!

Comment 15 Andreas Karis 2017-05-01 19:58:16 UTC
Hi,

Merge request:
https://code.launchpad.net/~akaris/cloud-init/+git/cloud-init/+merge/323340

Also note that along the way I had discovered a cast error somewhere in cloud-init https://code.launchpad.net/~akaris/cloud-init/+git/cloud-init/+merge/322992

Feel free to provide your own code. What the customer cares about is that we eliminate the use of alias files (which in the case of IPv4 are considered outdated, and in the case of IPv6 are invalid). Also note that IPv6 routes need to be provided in route6-<interface> and cannot be provided in route-<interface> files.

Comment 16 Andreas Karis 2017-05-04 18:27:32 UTC
Hi,

I'm just checking to see if we have any news for this.

- Andreas

Comment 17 Fabio Massimo Di Nitto 2017-05-09 03:37:36 UTC
(In reply to Andreas Karis from comment #16)
> Hi,
> 
> I'm just checking to see if we have any news for this.
> 
> - Andreas

No news, the upstream patch is still pending review.

Comment 18 Ryan McCabe 2017-05-17 14:47:33 UTC
Created attachment 1279727 [details]
new patch against master

I've reworked the patch so it now applies against master, and I ran the unittests against the resultant tree with no differences from the previous results, but beyond that, I'm not sure how to test that this is sane. Could somebody more familiar have a look/check?

Comment 19 Andreas Karis 2017-05-17 16:35:33 UTC
Hi,

I created a new merge request against master with a rebace: https://code.launchpad.net/~akaris/cloud-init/+git/cloud-init/+merge/324195

Comment 20 Andreas Karis 2017-05-17 16:36:50 UTC
*rebase

Comment 30 Andreas Karis 2017-06-06 17:08:03 UTC
Hi,

I identified the issue with default route creation. I still have to test this more thoroughly and asked the customer to confirm as well.

Upstream bug: https://bugs.launchpad.net/cloud-init/+bug/1696176
Merge request: https://code.launchpad.net/~akaris/cloud-init/+git/cloud-init/+merge/325174

Do I open a new BZ for this or are we going to move this one back to ASSIGNED?

Thanks,

- Andreas

Comment 32 Lars Kellogg-Stedman 2017-06-07 14:20:54 UTC
Reading the upstream bug, it's not clear if this is an actual problem or just a cosmetic issue:

"Since f38fa41317602908139aa96e930b634f65e39555 , default routes
get added to both ifcfg-* and route-* and route6-* files.
Default routes should only go to ifcfg-* files, otherwise the
information is redundant."

Does the redundant information cause an operational problem (no default route is set) or does it result in error messages but no operational impact, etc?  Thanks.

Comment 33 Andreas Karis 2017-06-07 14:55:00 UTC
It's mostly cosmetic from my point of view:
a) redundant routes in IPv4 sysconfig files cause no issue at all
* if GATEWAY is set in ifcfg-* and a default route is configured in route-*, then only one default route will be configured

b) redundant routes in IPv6 sysconfig files cause duplicate default route which AFAICT doesn't cause issues:
* if IPV6_DEFAULTGW is set in in ifcfg-* and a default route is configured in route6-*, then 2 default routes with the same gateway but different metric will be configured (which shouldn't cause an issue, though)

default via 2000:192:168:200::1 dev eth2  metric 1 
default via 2000:192:168:200::1 dev eth2  metric 1024

c) With dual-stack networks, the current code will create case b) for IPv6, while it does not create case a) for IPv4. This is due to 
                    gw_key = 'GATEWAY0'
                    nm_key = 'NETMASK0'
                    addr_key = 'ADDRESS0'
Due to the fact that I hardcoded the '0' in there, and for the IPv4 and IPv6 default route, it will pass that one twice, meaning that it assigns the same index to the IPv4 and IPv6 route, meaning that the first one will not appear in the array of routes  (it will however still be configured as a default route). So accidentally, in dual stack networks, IPv4 does behave as it should - only creating a GATEWAY in ifcfg-* and not creating a route in route-*

Comment 38 Ryan McCabe 2017-06-22 15:00:39 UTC
*** Bug 1457971 has been marked as a duplicate of this bug. ***

Comment 41 Leonid Natapov 2017-07-30 20:27:47 UTC
Code verification done on rhel 7.4 guest.
cloud-init: cloud-init[1750]: Cloud-init v. 0.7.9

Comment 42 errata-xmlrpc 2017-08-01 23:23:42 UTC
Since the problem described in this bug report should be
resolved in a recent advisory, it has been closed with a
resolution of ERRATA.

For information on the advisory, and where to find the updated
files, follow the link below.

If the solution does not work for you, open a new bug report.

https://access.redhat.com/errata/RHEA-2017:2275