summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Harper <ryan.harper@canonical.com>2018-01-22 18:33:08 -0600
committerScott Moser <smoser@ubuntu.com>2018-02-08 16:12:32 -0500
commit0d30c9575f9e3e4cfb7771cee992e7f669ac3e76 (patch)
tree7770933abd35e335924812c0e031041c5f55a353
parent45289a00bf8c043c5783c527c4ea720e67e0524b (diff)
downloadvyos-cloud-init-0d30c9575f9e3e4cfb7771cee992e7f669ac3e76.tar.gz
vyos-cloud-init-0d30c9575f9e3e4cfb7771cee992e7f669ac3e76.zip
net: accept network-config in netplan format for renaming interfaces
net.apply_network_config_names currently only accepts network-config in version 1 format. When users include a netplan format network-config the rename code does not find any of the 'set-name' directives and does not rename any of the interfaces. This causes some netplan configurations to fail. This patch adds support for parsing netplan format and extracts the needed information (macaddress and set-name values) to allow cloud-init to issue interface rename commands. We know raise a RuntimeError if presented with an unknown config format. LP: #1709715
-rw-r--r--cloudinit/net/__init__.py63
-rw-r--r--cloudinit/net/tests/test_init.py91
2 files changed, 137 insertions, 17 deletions
diff --git a/cloudinit/net/__init__.py b/cloudinit/net/__init__.py
index c015e793..f69c0ef2 100644
--- a/cloudinit/net/__init__.py
+++ b/cloudinit/net/__init__.py
@@ -274,23 +274,52 @@ def apply_network_config_names(netcfg, strict_present=True, strict_busy=True):
renames are only attempted for interfaces of type 'physical'. It is
expected that the network system will create other devices with the
correct name in place."""
- renames = []
- for ent in netcfg.get('config', {}):
- if ent.get('type') != 'physical':
- continue
- mac = ent.get('mac_address')
- if not mac:
- continue
- name = ent.get('name')
- driver = ent.get('params', {}).get('driver')
- device_id = ent.get('params', {}).get('device_id')
- if not driver:
- driver = device_driver(name)
- if not device_id:
- device_id = device_devid(name)
- renames.append([mac, name, driver, device_id])
-
- return _rename_interfaces(renames)
+
+ def _version_1(netcfg):
+ renames = []
+ for ent in netcfg.get('config', {}):
+ if ent.get('type') != 'physical':
+ continue
+ mac = ent.get('mac_address')
+ if not mac:
+ continue
+ name = ent.get('name')
+ driver = ent.get('params', {}).get('driver')
+ device_id = ent.get('params', {}).get('device_id')
+ if not driver:
+ driver = device_driver(name)
+ if not device_id:
+ device_id = device_devid(name)
+ renames.append([mac, name, driver, device_id])
+ return renames
+
+ def _version_2(netcfg):
+ renames = []
+ for key, ent in netcfg.get('ethernets', {}).items():
+ # only rename if configured to do so
+ name = ent.get('set-name')
+ if not name:
+ continue
+ # cloud-init requires macaddress for renaming
+ mac = ent.get('match', {}).get('macaddress')
+ if not mac:
+ continue
+ driver = ent.get('match', {}).get('driver')
+ device_id = ent.get('match', {}).get('device_id')
+ if not driver:
+ driver = device_driver(name)
+ if not device_id:
+ device_id = device_devid(name)
+ renames.append([mac, name, driver, device_id])
+ return renames
+
+ if netcfg.get('version') == 1:
+ return _rename_interfaces(_version_1(netcfg))
+ elif netcfg.get('version') == 2:
+ return _rename_interfaces(_version_2(netcfg))
+
+ raise RuntimeError('Failed to apply network config names. Found bad'
+ ' network config version: %s' % netcfg.get('version'))
def interface_has_own_mac(ifname, strict=False):
diff --git a/cloudinit/net/tests/test_init.py b/cloudinit/net/tests/test_init.py
index 8cb4114e..276556ee 100644
--- a/cloudinit/net/tests/test_init.py
+++ b/cloudinit/net/tests/test_init.py
@@ -4,6 +4,8 @@ import copy
import errno
import mock
import os
+import textwrap
+import yaml
import cloudinit.net as net
from cloudinit.util import ensure_file, write_file, ProcessExecutionError
@@ -520,3 +522,92 @@ class TestEphemeralIPV4Network(CiTestCase):
with net.EphemeralIPv4Network(**params):
self.assertEqual(expected_setup_calls, m_subp.call_args_list)
m_subp.assert_has_calls(expected_teardown_calls)
+
+
+class TestApplyNetworkCfgNames(CiTestCase):
+ V1_CONFIG = textwrap.dedent("""\
+ version: 1
+ config:
+ - type: physical
+ name: interface0
+ mac_address: "52:54:00:12:34:00"
+ subnets:
+ - type: static
+ address: 10.0.2.15
+ netmask: 255.255.255.0
+ gateway: 10.0.2.2
+ """)
+ V2_CONFIG = textwrap.dedent("""\
+ version: 2
+ ethernets:
+ interface0:
+ match:
+ macaddress: "52:54:00:12:34:00"
+ addresses:
+ - 10.0.2.15/24
+ gateway4: 10.0.2.2
+ set-name: interface0
+ """)
+
+ V2_CONFIG_NO_SETNAME = textwrap.dedent("""\
+ version: 2
+ ethernets:
+ interface0:
+ match:
+ macaddress: "52:54:00:12:34:00"
+ addresses:
+ - 10.0.2.15/24
+ gateway4: 10.0.2.2
+ """)
+
+ V2_CONFIG_NO_MAC = textwrap.dedent("""\
+ version: 2
+ ethernets:
+ interface0:
+ match:
+ driver: virtio-net
+ addresses:
+ - 10.0.2.15/24
+ gateway4: 10.0.2.2
+ set-name: interface0
+ """)
+
+ @mock.patch('cloudinit.net.device_devid')
+ @mock.patch('cloudinit.net.device_driver')
+ @mock.patch('cloudinit.net._rename_interfaces')
+ def test_apply_v1_renames(self, m_rename_interfaces, m_device_driver,
+ m_device_devid):
+ m_device_driver.return_value = 'virtio_net'
+ m_device_devid.return_value = '0x15d8'
+
+ net.apply_network_config_names(yaml.load(self.V1_CONFIG))
+
+ call = ['52:54:00:12:34:00', 'interface0', 'virtio_net', '0x15d8']
+ m_rename_interfaces.assert_called_with([call])
+
+ @mock.patch('cloudinit.net.device_devid')
+ @mock.patch('cloudinit.net.device_driver')
+ @mock.patch('cloudinit.net._rename_interfaces')
+ def test_apply_v2_renames(self, m_rename_interfaces, m_device_driver,
+ m_device_devid):
+ m_device_driver.return_value = 'virtio_net'
+ m_device_devid.return_value = '0x15d8'
+
+ net.apply_network_config_names(yaml.load(self.V2_CONFIG))
+
+ call = ['52:54:00:12:34:00', 'interface0', 'virtio_net', '0x15d8']
+ m_rename_interfaces.assert_called_with([call])
+
+ @mock.patch('cloudinit.net._rename_interfaces')
+ def test_apply_v2_renames_skips_without_setname(self, m_rename_interfaces):
+ net.apply_network_config_names(yaml.load(self.V2_CONFIG_NO_SETNAME))
+ m_rename_interfaces.assert_called_with([])
+
+ @mock.patch('cloudinit.net._rename_interfaces')
+ def test_apply_v2_renames_skips_without_mac(self, m_rename_interfaces):
+ net.apply_network_config_names(yaml.load(self.V2_CONFIG_NO_MAC))
+ m_rename_interfaces.assert_called_with([])
+
+ def test_apply_v2_renames_raises_runtime_error_on_unknown_version(self):
+ with self.assertRaises(RuntimeError):
+ net.apply_network_config_names(yaml.load("version: 3"))