← Back to team overview

cloud-init-dev team mailing list archive

[Merge] ~smoser/cloud-init:feature/curtin-centos8 into cloud-init:master

 

Scott Moser has proposed merging ~smoser/cloud-init:feature/curtin-centos8 into cloud-init:master.

Commit message:
sysconfig: use MACADDR on bonds/bridges to configure mac_address

Previously, sysconfig rendered HWADDR for all interface types, but
that value is only used to identify physical devices.  Instead use
MACADDR to configure the MAC on virtual devices, like bonds and
bridges.

- Sort bond slave list to ensure consistent ordering in sysconfig
  rendered files.
- Add unittests for sysconfig rendering of bonds/bridges with
  mac_address

LP: #1701417



Requested reviews:
  cloud-init commiters (cloud-init-dev)
Related bugs:
  Bug #1701417 in cloud-init: "cloud-init fails to configure bonding on CentOS 7"
  https://bugs.launchpad.net/cloud-init/+bug/1701417

For more details, see:
https://code.launchpad.net/~smoser/cloud-init/+git/cloud-init/+merge/327838
-- 
Your team cloud-init commiters is requested to review the proposed merge of ~smoser/cloud-init:feature/curtin-centos8 into cloud-init:master.
diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
index c7df36c..8494e66 100644
--- a/cloudinit/net/sysconfig.py
+++ b/cloudinit/net/sysconfig.py
@@ -264,6 +264,9 @@ class Renderer(renderer.Renderer):
         for (old_key, new_key) in [('mac_address', 'HWADDR'), ('mtu', 'MTU')]:
             old_value = iface.get(old_key)
             if old_value is not None:
+                # only set HWADDR on physical interfaces
+                if old_key == 'mac_address' and iface['type'] != 'physical':
+                    continue
                 iface_cfg[new_key] = old_value
 
     @classmethod
@@ -430,6 +433,9 @@ class Renderer(renderer.Renderer):
                 master_cfg['BONDING_MASTER'] = True
                 master_cfg.kind = 'bond'
 
+            if 'mac_address' in iface and iface.get('mac_address'):
+                iface_cfg['MACADDR'] = iface.get('mac_address')
+
             iface_subnets = iface.get("subnets", [])
             route_cfg = iface_cfg.routes
             cls._render_subnets(iface_cfg, iface_subnets)
@@ -441,6 +447,7 @@ class Renderer(renderer.Renderer):
                 [slave_iface['name'] for slave_iface in
                  network_state.iter_interfaces(slave_filter)
                  if slave_iface['bond-master'] == iface_name])
+
             for index, bond_slave in enumerate(bond_slaves):
                 slavestr = 'BONDING_SLAVE%s' % index
                 iface_cfg[slavestr] = bond_slave
@@ -499,6 +506,10 @@ class Renderer(renderer.Renderer):
             for old_key, new_key in cls.bridge_opts_keys:
                 if old_key in iface:
                     iface_cfg[new_key] = iface[old_key]
+
+            if 'mac_address' in iface and iface.get('mac_address'):
+                iface_cfg['MACADDR'] = iface.get('mac_address')
+
             # Is this the right key to get all the connected interfaces?
             for bridged_iface_name in iface.get('bridge_ports', []):
                 # Ensure all bridged interfaces are correctly tagged
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
index d15cd1f..38f11e7 100644
--- a/tests/unittests/test_net.py
+++ b/tests/unittests/test_net.py
@@ -422,6 +422,28 @@ NETWORK_CONFIGS = {
                             via: 65.61.151.37
                         set-name: eth99
         """).rstrip(' '),
+        'expected_sysconfig': {
+            'ifcfg-eth1': textwrap.dedent("""\
+                BOOTPROTO=none
+                DEVICE=eth1
+                HWADDR=cf:d6:af:48:e8:80
+                NM_CONTROLLED=no
+                ONBOOT=yes
+                TYPE=Ethernet
+                USERCTL=no"""),
+            'ifcfg-eth99': textwrap.dedent("""\
+                BOOTPROTO=dhcp
+                DEFROUTE=yes
+                DEVICE=eth99
+                GATEWAY=65.61.151.37
+                HWADDR=c0:d6:9f:2c:e8:80
+                IPADDR=192.168.21.3
+                NETMASK=255.255.255.0
+                NM_CONTROLLED=no
+                ONBOOT=yes
+                TYPE=Ethernet
+                USERCTL=no"""),
+        },
         'yaml': textwrap.dedent("""
             version: 1
             config:
@@ -758,6 +780,118 @@ pre-down route del -net 10.0.0.0 netmask 255.0.0.0 gw 11.0.0.1 metric 3 || true
                             - sacchromyces.maas
                             - brettanomyces.maas
         """).rstrip(' '),
+        'expected_sysconfig': {
+            'ifcfg-bond0': textwrap.dedent("""\
+                BONDING_MASTER=yes
+                BONDING_OPTS="mode=active-backup """
+                    """xmit_hash_policy=layer3+4 miimon=100"
+                BONDING_SLAVE0=eth1
+                BONDING_SLAVE1=eth2
+                BOOTPROTO=dhcp
+                DEVICE=bond0
+                DHCPV6C=yes
+                IPV6INIT=yes
+                MACADDR=aa:bb:cc:dd:ee:ff
+                NM_CONTROLLED=no
+                ONBOOT=yes
+                TYPE=Bond
+                USERCTL=no"""),
+            'ifcfg-bond0.200': textwrap.dedent("""\
+                BOOTPROTO=dhcp
+                DEVICE=bond0.200
+                NM_CONTROLLED=no
+                ONBOOT=yes
+                PHYSDEV=bond0
+                TYPE=Ethernet
+                USERCTL=no
+                VLAN=yes"""),
+            'ifcfg-br0': textwrap.dedent("""\
+                AGEING=250
+                BOOTPROTO=none
+                DEFROUTE=yes
+                DEVICE=br0
+                IPADDR=192.168.14.2
+                IPV6ADDR=2001:1::1/64
+                IPV6INIT=yes
+                IPV6_DEFAULTGW=2001:4800:78ff:1b::1
+                NETMASK=255.255.255.0
+                NM_CONTROLLED=no
+                ONBOOT=yes
+                PRIO=22
+                STP=off
+                TYPE=Bridge
+                USERCTL=no"""),
+            'ifcfg-eth0': textwrap.dedent("""\
+                BOOTPROTO=none
+                DEVICE=eth0
+                HWADDR=c0:d6:9f:2c:e8:80
+                NM_CONTROLLED=no
+                ONBOOT=yes
+                TYPE=Ethernet
+                USERCTL=no"""),
+            'ifcfg-eth0.101': textwrap.dedent("""\
+                BOOTPROTO=none
+                DEFROUTE=yes
+                DEVICE=eth0.101
+                GATEWAY=192.168.0.1
+                IPADDR=192.168.0.2
+                IPADDR1=192.168.2.10
+                MTU=1500
+                NETMASK=255.255.255.0
+                NETMASK1=255.255.255.0
+                NM_CONTROLLED=no
+                ONBOOT=yes
+                PHYSDEV=eth0
+                TYPE=Ethernet
+                USERCTL=no
+                VLAN=yes"""),
+            'ifcfg-eth1': textwrap.dedent("""\
+                BOOTPROTO=none
+                DEVICE=eth1
+                HWADDR=aa:d6:9f:2c:e8:80
+                MASTER=bond0
+                NM_CONTROLLED=no
+                ONBOOT=yes
+                SLAVE=yes
+                TYPE=Ethernet
+                USERCTL=no"""),
+            'ifcfg-eth2': textwrap.dedent("""\
+                BOOTPROTO=none
+                DEVICE=eth2
+                HWADDR=c0:bb:9f:2c:e8:80
+                MASTER=bond0
+                NM_CONTROLLED=no
+                ONBOOT=yes
+                SLAVE=yes
+                TYPE=Ethernet
+                USERCTL=no"""),
+            'ifcfg-eth3': textwrap.dedent("""\
+                BOOTPROTO=none
+                BRIDGE=br0
+                DEVICE=eth3
+                HWADDR=66:bb:9f:2c:e8:80
+                NM_CONTROLLED=no
+                ONBOOT=yes
+                TYPE=Ethernet
+                USERCTL=no"""),
+            'ifcfg-eth4': textwrap.dedent("""\
+                BOOTPROTO=none
+                BRIDGE=br0
+                DEVICE=eth4
+                HWADDR=98:bb:9f:2c:e8:80
+                NM_CONTROLLED=no
+                ONBOOT=yes
+                TYPE=Ethernet
+                USERCTL=no"""),
+            'ifcfg-eth5': textwrap.dedent("""\
+                BOOTPROTO=dhcp
+                DEVICE=eth5
+                HWADDR=98:bb:9f:2c:e8:8a
+                NM_CONTROLLED=no
+                ONBOOT=no
+                TYPE=Ethernet
+                USERCTL=no""")
+        },
         'yaml': textwrap.dedent("""
             version: 1
             config:
@@ -934,7 +1068,7 @@ pre-down route del -net 10.0.0.0 netmask 255.0.0.0 gw 11.0.0.1 metric 3 || true
         DEFROUTE=yes
         DEVICE=bond0
         GATEWAY=192.168.0.1
-        HWADDR=aa:bb:cc:dd:e8:ff
+        MACADDR=aa:bb:cc:dd:e8:ff
         IPADDR=192.168.0.2
         IPADDR1=192.168.1.2
         IPV6ADDR=2001:1::1/92
@@ -2042,6 +2176,20 @@ class TestEniRoundTrip(CiTestCase):
             entry['expected_eni'].splitlines(),
             files['/etc/network/interfaces'].splitlines())
 
+    def testsimple_render_small(self):
+        entry = NETWORK_CONFIGS['small']
+        files = self._render_and_read(network_config=yaml.load(entry['yaml']))
+        self.assertEqual(
+            entry['expected_eni'].splitlines(),
+            files['/etc/network/interfaces'].splitlines())
+
+    def testsimple_render_all(self):
+        entry = NETWORK_CONFIGS['all']
+        files = self._render_and_read(network_config=yaml.load(entry['yaml']))
+        self.assertEqual(
+            entry['expected_eni'].splitlines(),
+            files['/etc/network/interfaces'].splitlines())
+
     def test_routes_rendered(self):
         # as reported in bug 1649652
         conf = [