Merge ~raharper/cloud-init:fix/net-wait-for-interfaces into cloud-init:master
- Git
- lp:~raharper/cloud-init
- fix/net-wait-for-interfaces
- Merge into master
Status: | Merged |
---|---|
Approved by: | Ryan Harper |
Approved revision: | 68d4cb807e34bfe03bbff1daae4bf95ff8e30cb3 |
Merge reported by: | Server Team CI bot |
Merged at revision: | not available |
Proposed branch: | ~raharper/cloud-init:fix/net-wait-for-interfaces |
Merge into: | cloud-init:master |
Diff against target: |
496 lines (+315/-32) 6 files modified
cloudinit/distros/opensuse.py (+2/-0) cloudinit/net/__init__.py (+67/-21) cloudinit/net/tests/test_init.py (+213/-0) cloudinit/stages.py (+21/-6) cloudinit/tests/test_stages.py (+9/-2) tests/unittests/test_net.py (+3/-3) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Server Team CI bot | continuous-integration | Approve | |
Dan Watkins | Approve | ||
Review via email: mp+366667@code.launchpad.net |
Commit message
net: update net sequence, include wait on netdevs, opensuse netrules path
On systems with many interfaces, processing udev events may take a while.
Cloud-init expects devices included in a provided network-
to be present when attempting to configure them. This patch adds a step
in net configuration where it will check for devices provided in the
configuration and if not found, issue udevadm settle commands to wait
for them to appear.
Additionally, the default path for udev persistent network rules
70-persistent-
the 75-net-
generator may race and interleave values causing issues. OpenSUSE
will now use a newer file, 85-persistent-
will take precedence over values created by 75-net-generator and
avoid collisions on the same file.
LP: #1817368
Description of the change
Server Team CI bot (server-team-bot) wrote : | # |
Robert Schweikert (rjschwei) wrote : | # |
Fine with me, should address the problem.
Server Team CI bot (server-team-bot) wrote : | # |
FAILED: Continuous integration, rev:13440971487
https:/
Executed test runs:
SUCCESS: Checkout
FAILED: Unit & Style Tests
Click here to trigger a rebuild:
https:/
Dan Watkins (oddbloke) wrote : | # |
Overall, the logic here looks good, I just have a few minor inline comments. I think we do need some testing for the new functionality, though.
Ryan Harper (raharper) wrote : | # |
Thanks for the review. I've rebased to master and applied your suggestions. I also dropped some unrelated changes to the mounts unittest.
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:9123dc08eff
https:/
Executed test runs:
SUCCESS: Checkout
SUCCESS: Unit & Style Tests
SUCCESS: Ubuntu LTS: Build
SUCCESS: Ubuntu LTS: Integration
IN_PROGRESS: Declarative: Post Actions
Click here to trigger a rebuild:
https:/
Dan Watkins (oddbloke) wrote : | # |
Code LGTM now, but I still think we need some testing of wait_for_physdevs (and maybe a higher-level test that captures the issue we're actually addressing here?).
Ryan Harper (raharper) wrote : | # |
On Tue, Jul 16, 2019 at 12:00 PM Dan Watkins <email address hidden>
wrote:
> Code LGTM now, but I still think we need some testing of wait_for_physdevs
> (and maybe a higher-level test that captures the issue we're actually
> addressing here?).
>
Yes, let me add some unittests here.
> --
>
> https:/
> You are the owner of ~raharper/
>
Dan Watkins (oddbloke) wrote : | # |
Tests look good, thanks!
Server Team CI bot (server-team-bot) wrote : | # |
FAILED: Continuous integration, rev:e0665bd03a5
https:/
Executed test runs:
SUCCESS: Checkout
FAILED: Unit & Style Tests
Click here to trigger a rebuild:
https:/
- 68d4cb8... by Ryan Harper
-
Fix leaked calls to net.device_driver, net.device_devid, add testing
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:68d4cb807e3
https:/
Executed test runs:
SUCCESS: Checkout
SUCCESS: Unit & Style Tests
SUCCESS: Ubuntu LTS: Build
SUCCESS: Ubuntu LTS: Integration
IN_PROGRESS: Declarative: Post Actions
Click here to trigger a rebuild:
https:/
Preview Diff
1 | diff --git a/cloudinit/distros/opensuse.py b/cloudinit/distros/opensuse.py |
2 | index 1bfe047..e41e2f7 100644 |
3 | --- a/cloudinit/distros/opensuse.py |
4 | +++ b/cloudinit/distros/opensuse.py |
5 | @@ -38,6 +38,8 @@ class Distro(distros.Distro): |
6 | 'sysconfig': { |
7 | 'control': 'etc/sysconfig/network/config', |
8 | 'iface_templates': '%(base)s/network/ifcfg-%(name)s', |
9 | + 'netrules_path': ( |
10 | + 'etc/udev/rules.d/85-persistent-net-cloud-init.rules'), |
11 | 'route_templates': { |
12 | 'ipv4': '%(base)s/network/ifroute-%(name)s', |
13 | 'ipv6': '%(base)s/network/ifroute-%(name)s', |
14 | diff --git a/cloudinit/net/__init__.py b/cloudinit/net/__init__.py |
15 | index 624c9b4..f3cec79 100644 |
16 | --- a/cloudinit/net/__init__.py |
17 | +++ b/cloudinit/net/__init__.py |
18 | @@ -9,6 +9,7 @@ import errno |
19 | import logging |
20 | import os |
21 | import re |
22 | +from functools import partial |
23 | |
24 | from cloudinit.net.network_state import mask_to_net_prefix |
25 | from cloudinit import util |
26 | @@ -292,18 +293,10 @@ def generate_fallback_config(blacklist_drivers=None, config_driver=None): |
27 | return None |
28 | |
29 | |
30 | -def apply_network_config_names(netcfg, strict_present=True, strict_busy=True): |
31 | - """read the network config and rename devices accordingly. |
32 | - if strict_present is false, then do not raise exception if no devices |
33 | - match. if strict_busy is false, then do not raise exception if the |
34 | - device cannot be renamed because it is currently configured. |
35 | - |
36 | - renames are only attempted for interfaces of type 'physical'. It is |
37 | - expected that the network system will create other devices with the |
38 | - correct name in place.""" |
39 | +def extract_physdevs(netcfg): |
40 | |
41 | def _version_1(netcfg): |
42 | - renames = [] |
43 | + physdevs = [] |
44 | for ent in netcfg.get('config', {}): |
45 | if ent.get('type') != 'physical': |
46 | continue |
47 | @@ -317,11 +310,11 @@ def apply_network_config_names(netcfg, strict_present=True, strict_busy=True): |
48 | driver = device_driver(name) |
49 | if not device_id: |
50 | device_id = device_devid(name) |
51 | - renames.append([mac, name, driver, device_id]) |
52 | - return renames |
53 | + physdevs.append([mac, name, driver, device_id]) |
54 | + return physdevs |
55 | |
56 | def _version_2(netcfg): |
57 | - renames = [] |
58 | + physdevs = [] |
59 | for ent in netcfg.get('ethernets', {}).values(): |
60 | # only rename if configured to do so |
61 | name = ent.get('set-name') |
62 | @@ -337,16 +330,69 @@ def apply_network_config_names(netcfg, strict_present=True, strict_busy=True): |
63 | driver = device_driver(name) |
64 | if not device_id: |
65 | device_id = device_devid(name) |
66 | - renames.append([mac, name, driver, device_id]) |
67 | - return renames |
68 | + physdevs.append([mac, name, driver, device_id]) |
69 | + return physdevs |
70 | + |
71 | + version = netcfg.get('version') |
72 | + if version == 1: |
73 | + return _version_1(netcfg) |
74 | + elif version == 2: |
75 | + return _version_2(netcfg) |
76 | + |
77 | + raise RuntimeError('Unknown network config version: %s' % version) |
78 | + |
79 | |
80 | - if netcfg.get('version') == 1: |
81 | - return _rename_interfaces(_version_1(netcfg)) |
82 | - elif netcfg.get('version') == 2: |
83 | - return _rename_interfaces(_version_2(netcfg)) |
84 | +def wait_for_physdevs(netcfg, strict=True): |
85 | + physdevs = extract_physdevs(netcfg) |
86 | + |
87 | + # set of expected iface names and mac addrs |
88 | + expected_ifaces = dict([(iface[0], iface[1]) for iface in physdevs]) |
89 | + expected_macs = set(expected_ifaces.keys()) |
90 | + |
91 | + # set of current macs |
92 | + present_macs = get_interfaces_by_mac().keys() |
93 | + |
94 | + # compare the set of expected mac address values to |
95 | + # the current macs present; we only check MAC as cloud-init |
96 | + # has not yet renamed interfaces and the netcfg may include |
97 | + # such renames. |
98 | + for _ in range(0, 5): |
99 | + if expected_macs.issubset(present_macs): |
100 | + LOG.debug('net: all expected physical devices present') |
101 | + return |
102 | |
103 | - raise RuntimeError('Failed to apply network config names. Found bad' |
104 | - ' network config version: %s' % netcfg.get('version')) |
105 | + missing = expected_macs.difference(present_macs) |
106 | + LOG.debug('net: waiting for expected net devices: %s', missing) |
107 | + for mac in missing: |
108 | + # trigger a settle, unless this interface exists |
109 | + syspath = sys_dev_path(expected_ifaces[mac]) |
110 | + settle = partial(util.udevadm_settle, exists=syspath) |
111 | + msg = 'Waiting for udev events to settle or %s exists' % syspath |
112 | + util.log_time(LOG.debug, msg, func=settle) |
113 | + |
114 | + # update present_macs after settles |
115 | + present_macs = get_interfaces_by_mac().keys() |
116 | + |
117 | + msg = 'Not all expected physical devices present: %s' % missing |
118 | + LOG.warning(msg) |
119 | + if strict: |
120 | + raise RuntimeError(msg) |
121 | + |
122 | + |
123 | +def apply_network_config_names(netcfg, strict_present=True, strict_busy=True): |
124 | + """read the network config and rename devices accordingly. |
125 | + if strict_present is false, then do not raise exception if no devices |
126 | + match. if strict_busy is false, then do not raise exception if the |
127 | + device cannot be renamed because it is currently configured. |
128 | + |
129 | + renames are only attempted for interfaces of type 'physical'. It is |
130 | + expected that the network system will create other devices with the |
131 | + correct name in place.""" |
132 | + |
133 | + try: |
134 | + _rename_interfaces(extract_physdevs(netcfg)) |
135 | + except RuntimeError as e: |
136 | + raise RuntimeError('Failed to apply network config names: %s' % e) |
137 | |
138 | |
139 | def interface_has_own_mac(ifname, strict=False): |
140 | diff --git a/cloudinit/net/tests/test_init.py b/cloudinit/net/tests/test_init.py |
141 | index d393e6a..e6e77d7 100644 |
142 | --- a/cloudinit/net/tests/test_init.py |
143 | +++ b/cloudinit/net/tests/test_init.py |
144 | @@ -708,3 +708,216 @@ class TestHasURLConnectivity(HttprettyTestCase): |
145 | httpretty.register_uri(httpretty.GET, self.url, body={}, status=404) |
146 | self.assertFalse( |
147 | net.has_url_connectivity(self.url), 'Expected False on url fail') |
148 | + |
149 | + |
150 | +def _mk_v1_phys(mac, name, driver, device_id): |
151 | + v1_cfg = {'type': 'physical', 'name': name, 'mac_address': mac} |
152 | + params = {} |
153 | + if driver: |
154 | + params.update({'driver': driver}) |
155 | + if device_id: |
156 | + params.update({'device_id': device_id}) |
157 | + |
158 | + if params: |
159 | + v1_cfg.update({'params': params}) |
160 | + |
161 | + return v1_cfg |
162 | + |
163 | + |
164 | +def _mk_v2_phys(mac, name, driver=None, device_id=None): |
165 | + v2_cfg = {'set-name': name, 'match': {'macaddress': mac}} |
166 | + if driver: |
167 | + v2_cfg['match'].update({'driver': driver}) |
168 | + if device_id: |
169 | + v2_cfg['match'].update({'device_id': device_id}) |
170 | + |
171 | + return v2_cfg |
172 | + |
173 | + |
174 | +class TestExtractPhysdevs(CiTestCase): |
175 | + |
176 | + def setUp(self): |
177 | + super(TestExtractPhysdevs, self).setUp() |
178 | + self.add_patch('cloudinit.net.device_driver', 'm_driver') |
179 | + self.add_patch('cloudinit.net.device_devid', 'm_devid') |
180 | + |
181 | + def test_extract_physdevs_looks_up_driver_v1(self): |
182 | + driver = 'virtio' |
183 | + self.m_driver.return_value = driver |
184 | + physdevs = [ |
185 | + ['aa:bb:cc:dd:ee:ff', 'eth0', None, '0x1000'], |
186 | + ] |
187 | + netcfg = { |
188 | + 'version': 1, |
189 | + 'config': [_mk_v1_phys(*args) for args in physdevs], |
190 | + } |
191 | + # insert the driver value for verification |
192 | + physdevs[0][2] = driver |
193 | + self.assertEqual(sorted(physdevs), |
194 | + sorted(net.extract_physdevs(netcfg))) |
195 | + self.m_driver.assert_called_with('eth0') |
196 | + |
197 | + def test_extract_physdevs_looks_up_driver_v2(self): |
198 | + driver = 'virtio' |
199 | + self.m_driver.return_value = driver |
200 | + physdevs = [ |
201 | + ['aa:bb:cc:dd:ee:ff', 'eth0', None, '0x1000'], |
202 | + ] |
203 | + netcfg = { |
204 | + 'version': 2, |
205 | + 'ethernets': {args[1]: _mk_v2_phys(*args) for args in physdevs}, |
206 | + } |
207 | + # insert the driver value for verification |
208 | + physdevs[0][2] = driver |
209 | + self.assertEqual(sorted(physdevs), |
210 | + sorted(net.extract_physdevs(netcfg))) |
211 | + self.m_driver.assert_called_with('eth0') |
212 | + |
213 | + def test_extract_physdevs_looks_up_devid_v1(self): |
214 | + devid = '0x1000' |
215 | + self.m_devid.return_value = devid |
216 | + physdevs = [ |
217 | + ['aa:bb:cc:dd:ee:ff', 'eth0', 'virtio', None], |
218 | + ] |
219 | + netcfg = { |
220 | + 'version': 1, |
221 | + 'config': [_mk_v1_phys(*args) for args in physdevs], |
222 | + } |
223 | + # insert the driver value for verification |
224 | + physdevs[0][3] = devid |
225 | + self.assertEqual(sorted(physdevs), |
226 | + sorted(net.extract_physdevs(netcfg))) |
227 | + self.m_devid.assert_called_with('eth0') |
228 | + |
229 | + def test_extract_physdevs_looks_up_devid_v2(self): |
230 | + devid = '0x1000' |
231 | + self.m_devid.return_value = devid |
232 | + physdevs = [ |
233 | + ['aa:bb:cc:dd:ee:ff', 'eth0', 'virtio', None], |
234 | + ] |
235 | + netcfg = { |
236 | + 'version': 2, |
237 | + 'ethernets': {args[1]: _mk_v2_phys(*args) for args in physdevs}, |
238 | + } |
239 | + # insert the driver value for verification |
240 | + physdevs[0][3] = devid |
241 | + self.assertEqual(sorted(physdevs), |
242 | + sorted(net.extract_physdevs(netcfg))) |
243 | + self.m_devid.assert_called_with('eth0') |
244 | + |
245 | + def test_get_v1_type_physical(self): |
246 | + physdevs = [ |
247 | + ['aa:bb:cc:dd:ee:ff', 'eth0', 'virtio', '0x1000'], |
248 | + ['00:11:22:33:44:55', 'ens3', 'e1000', '0x1643'], |
249 | + ['09:87:65:43:21:10', 'ens0p1', 'mlx4_core', '0:0:1000'], |
250 | + ] |
251 | + netcfg = { |
252 | + 'version': 1, |
253 | + 'config': [_mk_v1_phys(*args) for args in physdevs], |
254 | + } |
255 | + self.assertEqual(sorted(physdevs), |
256 | + sorted(net.extract_physdevs(netcfg))) |
257 | + |
258 | + def test_get_v2_type_physical(self): |
259 | + physdevs = [ |
260 | + ['aa:bb:cc:dd:ee:ff', 'eth0', 'virtio', '0x1000'], |
261 | + ['00:11:22:33:44:55', 'ens3', 'e1000', '0x1643'], |
262 | + ['09:87:65:43:21:10', 'ens0p1', 'mlx4_core', '0:0:1000'], |
263 | + ] |
264 | + netcfg = { |
265 | + 'version': 2, |
266 | + 'ethernets': {args[1]: _mk_v2_phys(*args) for args in physdevs}, |
267 | + } |
268 | + self.assertEqual(sorted(physdevs), |
269 | + sorted(net.extract_physdevs(netcfg))) |
270 | + |
271 | + def test_get_v2_type_physical_skips_if_no_set_name(self): |
272 | + netcfg = { |
273 | + 'version': 2, |
274 | + 'ethernets': { |
275 | + 'ens3': { |
276 | + 'match': {'macaddress': '00:11:22:33:44:55'}, |
277 | + } |
278 | + } |
279 | + } |
280 | + self.assertEqual([], net.extract_physdevs(netcfg)) |
281 | + |
282 | + def test_runtime_error_on_unknown_netcfg_version(self): |
283 | + with self.assertRaises(RuntimeError): |
284 | + net.extract_physdevs({'version': 3, 'awesome_config': []}) |
285 | + |
286 | + |
287 | +class TestWaitForPhysdevs(CiTestCase): |
288 | + |
289 | + with_logs = True |
290 | + |
291 | + def setUp(self): |
292 | + super(TestWaitForPhysdevs, self).setUp() |
293 | + self.add_patch('cloudinit.net.get_interfaces_by_mac', |
294 | + 'm_get_iface_mac') |
295 | + self.add_patch('cloudinit.util.udevadm_settle', 'm_udev_settle') |
296 | + |
297 | + def test_wait_for_physdevs_skips_settle_if_all_present(self): |
298 | + physdevs = [ |
299 | + ['aa:bb:cc:dd:ee:ff', 'eth0', 'virtio', '0x1000'], |
300 | + ['00:11:22:33:44:55', 'ens3', 'e1000', '0x1643'], |
301 | + ] |
302 | + netcfg = { |
303 | + 'version': 2, |
304 | + 'ethernets': {args[1]: _mk_v2_phys(*args) |
305 | + for args in physdevs}, |
306 | + } |
307 | + self.m_get_iface_mac.side_effect = iter([ |
308 | + {'aa:bb:cc:dd:ee:ff': 'eth0', |
309 | + '00:11:22:33:44:55': 'ens3'}, |
310 | + ]) |
311 | + net.wait_for_physdevs(netcfg) |
312 | + self.assertEqual(0, self.m_udev_settle.call_count) |
313 | + |
314 | + def test_wait_for_physdevs_calls_udev_settle_on_missing(self): |
315 | + physdevs = [ |
316 | + ['aa:bb:cc:dd:ee:ff', 'eth0', 'virtio', '0x1000'], |
317 | + ['00:11:22:33:44:55', 'ens3', 'e1000', '0x1643'], |
318 | + ] |
319 | + netcfg = { |
320 | + 'version': 2, |
321 | + 'ethernets': {args[1]: _mk_v2_phys(*args) |
322 | + for args in physdevs}, |
323 | + } |
324 | + self.m_get_iface_mac.side_effect = iter([ |
325 | + {'aa:bb:cc:dd:ee:ff': 'eth0'}, # first call ens3 is missing |
326 | + {'aa:bb:cc:dd:ee:ff': 'eth0', |
327 | + '00:11:22:33:44:55': 'ens3'}, # second call has both |
328 | + ]) |
329 | + net.wait_for_physdevs(netcfg) |
330 | + self.m_udev_settle.assert_called_with(exists=net.sys_dev_path('ens3')) |
331 | + |
332 | + def test_wait_for_physdevs_raise_runtime_error_if_missing_and_strict(self): |
333 | + physdevs = [ |
334 | + ['aa:bb:cc:dd:ee:ff', 'eth0', 'virtio', '0x1000'], |
335 | + ['00:11:22:33:44:55', 'ens3', 'e1000', '0x1643'], |
336 | + ] |
337 | + netcfg = { |
338 | + 'version': 2, |
339 | + 'ethernets': {args[1]: _mk_v2_phys(*args) |
340 | + for args in physdevs}, |
341 | + } |
342 | + self.m_get_iface_mac.return_value = {} |
343 | + with self.assertRaises(RuntimeError): |
344 | + net.wait_for_physdevs(netcfg) |
345 | + |
346 | + self.assertEqual(5 * len(physdevs), self.m_udev_settle.call_count) |
347 | + |
348 | + def test_wait_for_physdevs_no_raise_if_not_strict(self): |
349 | + physdevs = [ |
350 | + ['aa:bb:cc:dd:ee:ff', 'eth0', 'virtio', '0x1000'], |
351 | + ['00:11:22:33:44:55', 'ens3', 'e1000', '0x1643'], |
352 | + ] |
353 | + netcfg = { |
354 | + 'version': 2, |
355 | + 'ethernets': {args[1]: _mk_v2_phys(*args) |
356 | + for args in physdevs}, |
357 | + } |
358 | + self.m_get_iface_mac.return_value = {} |
359 | + net.wait_for_physdevs(netcfg, strict=False) |
360 | + self.assertEqual(5 * len(physdevs), self.m_udev_settle.call_count) |
361 | diff --git a/cloudinit/stages.py b/cloudinit/stages.py |
362 | index da7d349..5f9d47b 100644 |
363 | --- a/cloudinit/stages.py |
364 | +++ b/cloudinit/stages.py |
365 | @@ -644,18 +644,21 @@ class Init(object): |
366 | return (ncfg, loc) |
367 | return (self.distro.generate_fallback_config(), "fallback") |
368 | |
369 | - def apply_network_config(self, bring_up): |
370 | - netcfg, src = self._find_networking_config() |
371 | - if netcfg is None: |
372 | - LOG.info("network config is disabled by %s", src) |
373 | - return |
374 | - |
375 | + def _apply_netcfg_names(self, netcfg): |
376 | try: |
377 | LOG.debug("applying net config names for %s", netcfg) |
378 | self.distro.apply_network_config_names(netcfg) |
379 | except Exception as e: |
380 | LOG.warning("Failed to rename devices: %s", e) |
381 | |
382 | + def apply_network_config(self, bring_up): |
383 | + # get a network config |
384 | + netcfg, src = self._find_networking_config() |
385 | + if netcfg is None: |
386 | + LOG.info("network config is disabled by %s", src) |
387 | + return |
388 | + |
389 | + # request an update if needed/available |
390 | if self.datasource is not NULL_DATA_SOURCE: |
391 | if not self.is_new_instance(): |
392 | if not self.datasource.update_metadata([EventType.BOOT]): |
393 | @@ -663,8 +666,20 @@ class Init(object): |
394 | "No network config applied. Neither a new instance" |
395 | " nor datasource network update on '%s' event", |
396 | EventType.BOOT) |
397 | + # nothing new, but ensure proper names |
398 | + self._apply_netcfg_names(netcfg) |
399 | return |
400 | + else: |
401 | + # refresh netcfg after update |
402 | + netcfg, src = self._find_networking_config() |
403 | + |
404 | + # ensure all physical devices in config are present |
405 | + net.wait_for_physdevs(netcfg) |
406 | + |
407 | + # apply renames from config |
408 | + self._apply_netcfg_names(netcfg) |
409 | |
410 | + # rendering config |
411 | LOG.info("Applying network configuration from %s bringup=%s: %s", |
412 | src, bring_up, netcfg) |
413 | try: |
414 | diff --git a/cloudinit/tests/test_stages.py b/cloudinit/tests/test_stages.py |
415 | index 94b6b25..9b48312 100644 |
416 | --- a/cloudinit/tests/test_stages.py |
417 | +++ b/cloudinit/tests/test_stages.py |
418 | @@ -37,6 +37,7 @@ class FakeDataSource(sources.DataSource): |
419 | |
420 | class TestInit(CiTestCase): |
421 | with_logs = True |
422 | + allowed_subp = False |
423 | |
424 | def setUp(self): |
425 | super(TestInit, self).setUp() |
426 | @@ -166,8 +167,9 @@ class TestInit(CiTestCase): |
427 | 'INFO: network config is disabled by %s' % disable_file, |
428 | self.logs.getvalue()) |
429 | |
430 | + @mock.patch('cloudinit.net.get_interfaces_by_mac') |
431 | @mock.patch('cloudinit.distros.ubuntu.Distro') |
432 | - def test_apply_network_on_new_instance(self, m_ubuntu): |
433 | + def test_apply_network_on_new_instance(self, m_ubuntu, m_macs): |
434 | """Call distro apply_network_config methods on is_new_instance.""" |
435 | net_cfg = { |
436 | 'version': 1, 'config': [ |
437 | @@ -177,6 +179,8 @@ class TestInit(CiTestCase): |
438 | def fake_network_config(): |
439 | return net_cfg, 'fallback' |
440 | |
441 | + m_macs.return_value = {'42:42:42:42:42:42': 'eth9'} |
442 | + |
443 | self.init._find_networking_config = fake_network_config |
444 | self.init.apply_network_config(True) |
445 | self.init.distro.apply_network_config_names.assert_called_with(net_cfg) |
446 | @@ -206,8 +210,9 @@ class TestInit(CiTestCase): |
447 | " nor datasource network update on '%s' event" % EventType.BOOT, |
448 | self.logs.getvalue()) |
449 | |
450 | + @mock.patch('cloudinit.net.get_interfaces_by_mac') |
451 | @mock.patch('cloudinit.distros.ubuntu.Distro') |
452 | - def test_apply_network_on_datasource_allowed_event(self, m_ubuntu): |
453 | + def test_apply_network_on_datasource_allowed_event(self, m_ubuntu, m_macs): |
454 | """Apply network if datasource.update_metadata permits BOOT event.""" |
455 | old_instance_id = os.path.join( |
456 | self.init.paths.get_cpath('data'), 'instance-id') |
457 | @@ -220,6 +225,8 @@ class TestInit(CiTestCase): |
458 | def fake_network_config(): |
459 | return net_cfg, 'fallback' |
460 | |
461 | + m_macs.return_value = {'42:42:42:42:42:42': 'eth9'} |
462 | + |
463 | self.init._find_networking_config = fake_network_config |
464 | self.init.datasource = FakeDataSource(paths=self.init.paths) |
465 | self.init.datasource.update_events = {'network': [EventType.BOOT]} |
466 | diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py |
467 | index 18efce9..de4e7f4 100644 |
468 | --- a/tests/unittests/test_net.py |
469 | +++ b/tests/unittests/test_net.py |
470 | @@ -515,7 +515,7 @@ nameserver 172.19.0.12 |
471 | [main] |
472 | dns = none |
473 | """.lstrip()), |
474 | - ('etc/udev/rules.d/70-persistent-net.rules', |
475 | + ('etc/udev/rules.d/85-persistent-net-cloud-init.rules', |
476 | "".join(['SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ', |
477 | 'ATTR{address}=="fa:16:3e:ed:9a:59", NAME="eth0"\n']))], |
478 | 'out_sysconfig_rhel': [ |
479 | @@ -619,7 +619,7 @@ nameserver 172.19.0.12 |
480 | [main] |
481 | dns = none |
482 | """.lstrip()), |
483 | - ('etc/udev/rules.d/70-persistent-net.rules', |
484 | + ('etc/udev/rules.d/85-persistent-net-cloud-init.rules', |
485 | "".join(['SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ', |
486 | 'ATTR{address}=="fa:16:3e:ed:9a:59", NAME="eth0"\n']))], |
487 | 'out_sysconfig_rhel': [ |
488 | @@ -750,7 +750,7 @@ nameserver 172.19.0.12 |
489 | [main] |
490 | dns = none |
491 | """.lstrip()), |
492 | - ('etc/udev/rules.d/70-persistent-net.rules', |
493 | + ('etc/udev/rules.d/85-persistent-net-cloud-init.rules', |
494 | "".join(['SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ', |
495 | 'ATTR{address}=="fa:16:3e:ed:9a:59", NAME="eth0"\n']))], |
496 | 'out_sysconfig_rhel': [ |
FAILED: Continuous integration, rev:13440971487 dba5f8708a22ef6 745e8172bcd642 /jenkins. ubuntu. com/server/ job/cloud- init-ci/ 702/
https:/
Executed test runs:
SUCCESS: Checkout
FAILED: Unit & Style Tests
Click here to trigger a rebuild: /jenkins. ubuntu. com/server/ job/cloud- init-ci/ 702/rebuild
https:/