Merge ~raharper/cloud-init:fix/cloud-init-network-rename-with-v2 into cloud-init:master

Proposed by Ryan Harper
Status: Merged
Approved by: Scott Moser
Approved revision: 425de9363cecf740df7c7ee3f815a1dc2803a5f4
Merged at revision: 0d30c9575f9e3e4cfb7771cee992e7f669ac3e76
Proposed branch: ~raharper/cloud-init:fix/cloud-init-network-rename-with-v2
Merge into: cloud-init:master
Diff against target: 180 lines (+137/-17)
2 files modified
cloudinit/net/__init__.py (+46/-17)
cloudinit/net/tests/test_init.py (+91/-0)
Reviewer Review Type Date Requested Status
Scott Moser Approve
Server Team CI bot continuous-integration Approve
Review via email: mp+336464@code.launchpad.net

Description of the change

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.

LP: #1709715

To post a comment you must log in.
Revision history for this message
Server Team CI bot (server-team-bot) wrote :

PASSED: Continuous integration, rev:6b29ea05bdaf2696db950761388fd413334ebc93
https://jenkins.ubuntu.com/server/job/cloud-init-ci/718/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    SUCCESS: MAAS Compatability Testing
    IN_PROGRESS: Declarative: Post Actions

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/718/rebuild

review: Approve (continuous-integration)
Revision history for this message
Scott Moser (smoser) wrote :

I have some nits there, but I think this looks good. Thanks.

Revision history for this message
Server Team CI bot (server-team-bot) wrote :

PASSED: Continuous integration, rev:425de9363cecf740df7c7ee3f815a1dc2803a5f4
https://jenkins.ubuntu.com/server/job/cloud-init-ci/749/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    SUCCESS: MAAS Compatability Testing
    IN_PROGRESS: Declarative: Post Actions

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/749/rebuild

review: Approve (continuous-integration)
Revision history for this message
Ryan Harper (raharper) wrote :

Updated based on feedback.

Revision history for this message
Scott Moser (smoser) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/cloudinit/net/__init__.py b/cloudinit/net/__init__.py
2index c015e79..f69c0ef 100644
3--- a/cloudinit/net/__init__.py
4+++ b/cloudinit/net/__init__.py
5@@ -274,23 +274,52 @@ def apply_network_config_names(netcfg, strict_present=True, strict_busy=True):
6 renames are only attempted for interfaces of type 'physical'. It is
7 expected that the network system will create other devices with the
8 correct name in place."""
9- renames = []
10- for ent in netcfg.get('config', {}):
11- if ent.get('type') != 'physical':
12- continue
13- mac = ent.get('mac_address')
14- if not mac:
15- continue
16- name = ent.get('name')
17- driver = ent.get('params', {}).get('driver')
18- device_id = ent.get('params', {}).get('device_id')
19- if not driver:
20- driver = device_driver(name)
21- if not device_id:
22- device_id = device_devid(name)
23- renames.append([mac, name, driver, device_id])
24-
25- return _rename_interfaces(renames)
26+
27+ def _version_1(netcfg):
28+ renames = []
29+ for ent in netcfg.get('config', {}):
30+ if ent.get('type') != 'physical':
31+ continue
32+ mac = ent.get('mac_address')
33+ if not mac:
34+ continue
35+ name = ent.get('name')
36+ driver = ent.get('params', {}).get('driver')
37+ device_id = ent.get('params', {}).get('device_id')
38+ if not driver:
39+ driver = device_driver(name)
40+ if not device_id:
41+ device_id = device_devid(name)
42+ renames.append([mac, name, driver, device_id])
43+ return renames
44+
45+ def _version_2(netcfg):
46+ renames = []
47+ for key, ent in netcfg.get('ethernets', {}).items():
48+ # only rename if configured to do so
49+ name = ent.get('set-name')
50+ if not name:
51+ continue
52+ # cloud-init requires macaddress for renaming
53+ mac = ent.get('match', {}).get('macaddress')
54+ if not mac:
55+ continue
56+ driver = ent.get('match', {}).get('driver')
57+ device_id = ent.get('match', {}).get('device_id')
58+ if not driver:
59+ driver = device_driver(name)
60+ if not device_id:
61+ device_id = device_devid(name)
62+ renames.append([mac, name, driver, device_id])
63+ return renames
64+
65+ if netcfg.get('version') == 1:
66+ return _rename_interfaces(_version_1(netcfg))
67+ elif netcfg.get('version') == 2:
68+ return _rename_interfaces(_version_2(netcfg))
69+
70+ raise RuntimeError('Failed to apply network config names. Found bad'
71+ ' network config version: %s' % netcfg.get('version'))
72
73
74 def interface_has_own_mac(ifname, strict=False):
75diff --git a/cloudinit/net/tests/test_init.py b/cloudinit/net/tests/test_init.py
76index 8cb4114..276556e 100644
77--- a/cloudinit/net/tests/test_init.py
78+++ b/cloudinit/net/tests/test_init.py
79@@ -4,6 +4,8 @@ import copy
80 import errno
81 import mock
82 import os
83+import textwrap
84+import yaml
85
86 import cloudinit.net as net
87 from cloudinit.util import ensure_file, write_file, ProcessExecutionError
88@@ -520,3 +522,92 @@ class TestEphemeralIPV4Network(CiTestCase):
89 with net.EphemeralIPv4Network(**params):
90 self.assertEqual(expected_setup_calls, m_subp.call_args_list)
91 m_subp.assert_has_calls(expected_teardown_calls)
92+
93+
94+class TestApplyNetworkCfgNames(CiTestCase):
95+ V1_CONFIG = textwrap.dedent("""\
96+ version: 1
97+ config:
98+ - type: physical
99+ name: interface0
100+ mac_address: "52:54:00:12:34:00"
101+ subnets:
102+ - type: static
103+ address: 10.0.2.15
104+ netmask: 255.255.255.0
105+ gateway: 10.0.2.2
106+ """)
107+ V2_CONFIG = textwrap.dedent("""\
108+ version: 2
109+ ethernets:
110+ interface0:
111+ match:
112+ macaddress: "52:54:00:12:34:00"
113+ addresses:
114+ - 10.0.2.15/24
115+ gateway4: 10.0.2.2
116+ set-name: interface0
117+ """)
118+
119+ V2_CONFIG_NO_SETNAME = textwrap.dedent("""\
120+ version: 2
121+ ethernets:
122+ interface0:
123+ match:
124+ macaddress: "52:54:00:12:34:00"
125+ addresses:
126+ - 10.0.2.15/24
127+ gateway4: 10.0.2.2
128+ """)
129+
130+ V2_CONFIG_NO_MAC = textwrap.dedent("""\
131+ version: 2
132+ ethernets:
133+ interface0:
134+ match:
135+ driver: virtio-net
136+ addresses:
137+ - 10.0.2.15/24
138+ gateway4: 10.0.2.2
139+ set-name: interface0
140+ """)
141+
142+ @mock.patch('cloudinit.net.device_devid')
143+ @mock.patch('cloudinit.net.device_driver')
144+ @mock.patch('cloudinit.net._rename_interfaces')
145+ def test_apply_v1_renames(self, m_rename_interfaces, m_device_driver,
146+ m_device_devid):
147+ m_device_driver.return_value = 'virtio_net'
148+ m_device_devid.return_value = '0x15d8'
149+
150+ net.apply_network_config_names(yaml.load(self.V1_CONFIG))
151+
152+ call = ['52:54:00:12:34:00', 'interface0', 'virtio_net', '0x15d8']
153+ m_rename_interfaces.assert_called_with([call])
154+
155+ @mock.patch('cloudinit.net.device_devid')
156+ @mock.patch('cloudinit.net.device_driver')
157+ @mock.patch('cloudinit.net._rename_interfaces')
158+ def test_apply_v2_renames(self, m_rename_interfaces, m_device_driver,
159+ m_device_devid):
160+ m_device_driver.return_value = 'virtio_net'
161+ m_device_devid.return_value = '0x15d8'
162+
163+ net.apply_network_config_names(yaml.load(self.V2_CONFIG))
164+
165+ call = ['52:54:00:12:34:00', 'interface0', 'virtio_net', '0x15d8']
166+ m_rename_interfaces.assert_called_with([call])
167+
168+ @mock.patch('cloudinit.net._rename_interfaces')
169+ def test_apply_v2_renames_skips_without_setname(self, m_rename_interfaces):
170+ net.apply_network_config_names(yaml.load(self.V2_CONFIG_NO_SETNAME))
171+ m_rename_interfaces.assert_called_with([])
172+
173+ @mock.patch('cloudinit.net._rename_interfaces')
174+ def test_apply_v2_renames_skips_without_mac(self, m_rename_interfaces):
175+ net.apply_network_config_names(yaml.load(self.V2_CONFIG_NO_MAC))
176+ m_rename_interfaces.assert_called_with([])
177+
178+ def test_apply_v2_renames_raises_runtime_error_on_unknown_version(self):
179+ with self.assertRaises(RuntimeError):
180+ net.apply_network_config_names(yaml.load("version: 3"))

Subscribers

People subscribed via source and target branches