Merge ~chad.smith/cloud-init:feature/ec2-secondary-nics into cloud-init:master

Proposed by Chad Smith on 2019-07-06
Status: Work in progress
Proposed branch: ~chad.smith/cloud-init:feature/ec2-secondary-nics
Merge into: cloud-init:master
Diff against target: 463 lines (+210/-75)
3 files modified
cloudinit/sources/DataSourceEc2.py (+47/-15)
doc/rtd/topics/datasources/ec2.rst (+3/-0)
tests/unittests/test_datasource/test_ec2.py (+160/-60)
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Approve on 2019-07-09
cloud-init Commiters 2019-07-06 Pending
Review via email: mp+369792@code.launchpad.net

Commit message

ec2: render secondary IPs on primary nic when present in metadata

Parse local-ipv4s and subnet-ipv4-cidr-block on EC2 metadata version
2018-09-24 to obtain secondary nic private IPs and network mask for
the primary nic.

In adding this feature, convert DataSourceEc2.network_config to
emit network version 2 instead of version 1.

To allow for retaining original network config behavior on earlier
distribution series, surface a datasource config option
configure_secondary_ips which defaults to True on tip.

Older/stable distribution series will set configure_secondary_ips
default to False.

To post a comment you must log in.

FAILED: Continuous integration, rev:a16796ed79fc4d58710fa018571011d1adefc032
https://jenkins.ubuntu.com/server/job/cloud-init-ci/757/
Executed test runs:
    SUCCESS: Checkout
    FAILED: Unit & Style Tests

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

review: Needs Fixing (continuous-integration)
Dan Watkins (daniel-thewatkins) wrote :

Overall, this looks good, thanks! I have a few inline comments/questions, but the overall structure/logic seems sound to me.

(Looks like the CI failures are due to linting.)

Dan Watkins (daniel-thewatkins) wrote :

Thanks! A couple of follow-ups, and a couple of nits I missed first time around.

Ryan Harper (raharper) :

PASSED: Continuous integration, rev:f6b0d9eb283917f4c2a5a5f863ad07c048346199
https://jenkins.ubuntu.com/server/job/cloud-init-ci/759/
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://jenkins.ubuntu.com/server/job/cloud-init-ci/759/rebuild

review: Approve (continuous-integration)

PASSED: Continuous integration, rev:db953566a9e6df91b4eaeebb813e45726136316c
https://jenkins.ubuntu.com/server/job/cloud-init-ci/761/
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://jenkins.ubuntu.com/server/job/cloud-init-ci/761/rebuild

review: Approve (continuous-integration)

FAILED: Continuous integration, rev:047a654e9b7ca8dd8115aab2467c55f25ba312ca
https://jenkins.ubuntu.com/server/job/cloud-init-ci/762/
Executed test runs:
    SUCCESS: Checkout
    FAILED: Unit & Style Tests

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

review: Needs Fixing (continuous-integration)

PASSED: Continuous integration, rev:2ae7c7c3b3ee6522f31a4efc6b993e43e82c5bb9
https://jenkins.ubuntu.com/server/job/cloud-init-ci/765/
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://jenkins.ubuntu.com/server/job/cloud-init-ci/765/rebuild

review: Approve (continuous-integration)
Chad Smith (chad.smith) wrote :

Waiting on this change of behavior until after 19.2 upstream release

Leo Crawford (leo-leocrawford) wrote :

> Waiting on this change of behavior until after 19.2 upstream release

cloud-init 19.2 was released 17 July. Does this unblock this change being applied?

Ryan Harper (raharper) wrote :

I think Chad meant after our next SRU[1], which will begin shortly. I'd expect this come in the SRU after the current planned one.

1. https://wiki.ubuntu.com/StableReleaseUpdates

Ryan Harper (raharper) wrote :

Inline comment asking for clarification on AWS DHCP lease contents, requirements for routing traffic to IMDS, DNS, and off-box, as with Azure. Let's document what's needed; we may need to add secondary ips with a metric. See

https://github.com/aws/ec2-net-utils/blob/master/ec2net-functions

For AmazonLinux net implementation; I do see some setting of source_ip and route table/metrics in use.

Unmerged commits

2ae7c7c... by Chad Smith on 2019-07-06

ec2: render secondary IPs on primary nic when present in metadata

Parse local-ipv4s and subnet-ipv4-cidr-block on EC2 metadata version
2018-09-24 to obtain secondary nic private IPs and network mask for
the primary nic.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/cloudinit/sources/DataSourceEc2.py b/cloudinit/sources/DataSourceEc2.py
2index 5c017bf..f39b73f 100644
3--- a/cloudinit/sources/DataSourceEc2.py
4+++ b/cloudinit/sources/DataSourceEc2.py
5@@ -54,7 +54,7 @@ class DataSourceEc2(sources.DataSource):
6
7 # Priority ordered list of additional metadata versions which will be tried
8 # for extended metadata content. IPv6 support comes in 2016-09-02
9- extended_metadata_versions = ['2016-09-02']
10+ extended_metadata_versions = ['2018-09-24', '2016-09-02']
11
12 # Setup read_url parameters per get_url_params.
13 url_max_wait = 120
14@@ -332,8 +332,13 @@ class DataSourceEc2(sources.DataSource):
15 macs_to_nics = {net.get_interface_mac(iface): iface}
16 net_md = self.metadata.get('network')
17 if isinstance(net_md, dict):
18+ # SRU_BLOCKER: xenial, bionic and disco should default
19+ # configure_secondary_ips to False to retain original behavior on
20+ # those releases.
21 result = convert_ec2_metadata_network_config(
22- net_md, macs_to_nics=macs_to_nics, fallback_nic=iface)
23+ net_md, macs_to_nics=macs_to_nics, fallback_nic=iface,
24+ config_secondary_ips=util.is_true(
25+ self.ds_cfg.get('configure_secondary_ips', True)))
26
27 # RELEASE_BLOCKER: xenial should drop the below if statement,
28 # because the issue being addressed doesn't exist pre-netplan.
29@@ -373,7 +378,7 @@ class DataSourceEc2(sources.DataSource):
30 if not self.wait_for_metadata_service():
31 return {}
32 api_version = self.get_metadata_api_version()
33- crawled_metadata = {}
34+ crawled_metadata = {'_metadata_api_version': api_version}
35 try:
36 crawled_metadata['user-data'] = ec2.get_instance_userdata(
37 api_version, self.metadata_address)
38@@ -388,7 +393,6 @@ class DataSourceEc2(sources.DataSource):
39 LOG, "Failed reading from metadata address %s",
40 self.metadata_address)
41 return {}
42- crawled_metadata['_metadata_api_version'] = api_version
43 return crawled_metadata
44
45
46@@ -523,8 +527,9 @@ def _collect_platform_data():
47 return data
48
49
50-def convert_ec2_metadata_network_config(network_md, macs_to_nics=None,
51- fallback_nic=None):
52+def convert_ec2_metadata_network_config(
53+ network_md, macs_to_nics=None, fallback_nic=None,
54+ config_secondary_ips=True):
55 """Convert ec2 metadata to network config version 1 data dict.
56
57 @param: network_md: 'network' portion of EC2 metadata.
58@@ -535,25 +540,52 @@ def convert_ec2_metadata_network_config(network_md, macs_to_nics=None,
59 not provided, get_interfaces_by_mac is called to get it from the OS.
60 @param: fallback_nic: Optionally provide the primary nic interface name.
61 This nic will be guaranteed to minimally have a dhcp4 configuration.
62+ @param: config_secondary_ips: Boolean set True to configure any
63+ secondary IPs described by the metadata service.
64
65- @return A dict of network config version 1 based on the metadata and macs.
66+ @return A dict of network config version 2 based on the metadata and macs.
67 """
68- netcfg = {'version': 1, 'config': []}
69+ netcfg = {'version': 2, 'ethernets': {}}
70 if not macs_to_nics:
71 macs_to_nics = net.get_interfaces_by_mac()
72 macs_metadata = network_md['interfaces']['macs']
73 for mac, nic_name in macs_to_nics.items():
74+ dev_config = {}
75 nic_metadata = macs_metadata.get(mac)
76 if not nic_metadata:
77 continue # Not a physical nic represented in metadata
78- nic_cfg = {'type': 'physical', 'name': nic_name, 'subnets': []}
79- nic_cfg['mac_address'] = mac
80- if (nic_name == fallback_nic or nic_metadata.get('public-ipv4s') or
81- nic_metadata.get('local-ipv4s')):
82- nic_cfg['subnets'].append({'type': 'dhcp4'})
83+ local_ipv4s = nic_metadata.get('local-ipv4s')
84+ if not config_secondary_ips:
85+ LOG.debug(
86+ 'Skipping secondary IP config because configure_secondary_ips'
87+ ' datasource config option is False')
88+ elif (nic_name == fallback_nic or
89+ nic_metadata.get('public-ipv4s') or
90+ local_ipv4s):
91+ dev_config['dhcp4'] = True
92+ # In version < 2018-09-24 local_ipvs is a str with a single IP
93+ if isinstance(local_ipv4s, list) and len(local_ipv4s) > 1:
94+ dev_config['addresses'] = []
95+ subnet_cidr = nic_metadata.get('subnet-ipv4-cidr-block')
96+ if not subnet_cidr or len(subnet_cidr.split('/')) != 2:
97+ LOG.warning(
98+ 'Could not parse subnet-ipv4-cidr-block %s.'
99+ ' Network config for Secondary IPs default to /32',
100+ subnet_cidr)
101+ prefix = '32'
102+ else:
103+ _ip, prefix = subnet_cidr.split('/')
104+ # Primary nic IP is at index 0 and obtained via dhcp on Ec2
105+ # Iterate over all secondary IPs in local_ipv4s at index >= 1
106+ for secondary_ip in local_ipv4s[1:]:
107+ dev_config['addresses'].append(
108+ '{ip}/{prefix}'.format(ip=secondary_ip, prefix=prefix))
109 if nic_metadata.get('ipv6s'):
110- nic_cfg['subnets'].append({'type': 'dhcp6'})
111- netcfg['config'].append(nic_cfg)
112+ dev_config['dhcp6'] = True
113+ dev_config.update({
114+ 'match': {'macaddress': mac.lower()},
115+ 'set-name': nic_name})
116+ netcfg['ethernets'][nic_name] = dev_config
117 return netcfg
118
119
120diff --git a/doc/rtd/topics/datasources/ec2.rst b/doc/rtd/topics/datasources/ec2.rst
121index 76beca9..5535edf 100644
122--- a/doc/rtd/topics/datasources/ec2.rst
123+++ b/doc/rtd/topics/datasources/ec2.rst
124@@ -79,6 +79,9 @@ The settings that may be configured are:
125 * **timeout**: the timeout value provided to urlopen for each individual http
126 request. This is used both when selecting a metadata_url and when crawling
127 the metadata service. (default: 50)
128+ * **configure_secondary_ips**: Boolean (default: True) to allow cloud-init
129+ to configure any secondary IPs described by the metadata service.
130+ On Ubuntu Xenial, Bionic and Disco, this defaults to False.
131
132 An example configuration with the default values is provided below:
133
134diff --git a/tests/unittests/test_datasource/test_ec2.py b/tests/unittests/test_datasource/test_ec2.py
135index 20d59bf..8ed4c18 100644
136--- a/tests/unittests/test_datasource/test_ec2.py
137+++ b/tests/unittests/test_datasource/test_ec2.py
138@@ -112,6 +112,96 @@ DEFAULT_METADATA = {
139 "services": {"domain": "amazonaws.com", "partition": "aws"},
140 }
141
142+# collected from api version 2018-09-24/ with
143+# python3 -c 'import json
144+# from cloudinit.ec2_utils import get_instance_metadata as gm
145+# print(json.dumps(gm("2018-09-24"), indent=1, sort_keys=True))'
146+SECONDARY_IP_METADATA_2018_09_24 = {
147+ "ami-id": "ami-0986c2ac728528ac2",
148+ "ami-launch-index": "0",
149+ "ami-manifest-path": "(unknown)",
150+ "block-device-mapping": {
151+ "ami": "/dev/sda1",
152+ "root": "/dev/sda1"
153+ },
154+ "events": {
155+ "maintenance": {
156+ "history": "[]",
157+ "scheduled": "[]"
158+ }
159+ },
160+ "hostname": "ip-172-31-44-13.us-east-2.compute.internal",
161+ "identity-credentials": {
162+ "ec2": {
163+ "info": {
164+ "AccountId": "329910648901",
165+ "Code": "Success",
166+ "LastUpdated": "2019-07-06T14:22:56Z"
167+ }
168+ }
169+ },
170+ "instance-action": "none",
171+ "instance-id": "i-069e01e8cc43732f8",
172+ "instance-type": "t2.micro",
173+ "local-hostname": "ip-172-31-44-13.us-east-2.compute.internal",
174+ "local-ipv4": "172.31.44.13",
175+ "mac": "0a:07:84:3d:6e:38",
176+ "metrics": {
177+ "vhostmd": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
178+ },
179+ "network": {
180+ "interfaces": {
181+ "macs": {
182+ "0a:07:84:3d:6e:38": {
183+ "device-number": "0",
184+ "interface-id": "eni-0d6335689899ce9cc",
185+ "ipv4-associations": {
186+ "18.218.219.181": "172.31.44.13"
187+ },
188+ "local-hostname": ("ip-172-31-44-13.us-east-2."
189+ "compute.internal"),
190+ "local-ipv4s": [
191+ "172.31.44.13",
192+ "172.31.45.70"
193+ ],
194+ "mac": "0a:07:84:3d:6e:38",
195+ "owner-id": "329910648901",
196+ "public-hostname": ("ec2-18-218-219-181.us-east-2."
197+ "compute.amazonaws.com"),
198+ "public-ipv4s": "18.218.219.181",
199+ "security-group-ids": "sg-0c387755222ba8d2e",
200+ "security-groups": "launch-wizard-4",
201+ "subnet-id": "subnet-9d7ba0d1",
202+ "subnet-ipv4-cidr-block": "172.31.32.0/20",
203+ "vpc-id": "vpc-a07f62c8",
204+ "vpc-ipv4-cidr-block": "172.31.0.0/16",
205+ "vpc-ipv4-cidr-blocks": "172.31.0.0/16"
206+ }
207+ }
208+ }
209+ },
210+ "placement": {
211+ "availability-zone": "us-east-2c"
212+ },
213+ "profile": "default-hvm",
214+ "public-hostname": (
215+ "ec2-18-218-219-181.us-east-2.compute.amazonaws.com"),
216+ "public-ipv4": "18.218.219.181",
217+ "public-keys": {
218+ "yourkeyname,e": [
219+ "ssh-rsa AAAAW...DZ yourkeyname"
220+ ]
221+ },
222+ "reservation-id": "r-09b4917135cdd33be",
223+ "security-groups": "launch-wizard-4",
224+ "services": {
225+ "domain": "amazonaws.com",
226+ "partition": "aws"
227+ }
228+}
229+
230+M_PATH_NET = 'cloudinit.sources.DataSourceEc2.net.'
231+
232
233 def _register_ssh_keys(rfunc, base_url, keys_data):
234 """handle ssh key inconsistencies.
235@@ -261,30 +351,23 @@ class TestEc2(test_helpers.HttprettyTestCase):
236 register_mock_metaserver(instance_id_url, None)
237 return ds
238
239- def test_network_config_property_returns_version_1_network_data(self):
240- """network_config property returns network version 1 for metadata.
241-
242- Only one device is configured even when multiple exist in metadata.
243- """
244+ def test_network_config_property_returns_version_2_network_data(self):
245+ """network_config property returns network version 2 for metadata"""
246 ds = self._setup_ds(
247 platform_data=self.valid_platform_data,
248 sys_cfg={'datasource': {'Ec2': {'strict_id': True}}},
249 md={'md': DEFAULT_METADATA})
250- find_fallback_path = (
251- 'cloudinit.sources.DataSourceEc2.net.find_fallback_nic')
252+ find_fallback_path = M_PATH_NET + 'find_fallback_nic'
253 with mock.patch(find_fallback_path) as m_find_fallback:
254 m_find_fallback.return_value = 'eth9'
255 ds.get_data()
256
257 mac1 = '06:17:04:d7:26:09' # Defined in DEFAULT_METADATA
258- expected = {'version': 1, 'config': [
259- {'mac_address': '06:17:04:d7:26:09', 'name': 'eth9',
260- 'subnets': [{'type': 'dhcp4'}, {'type': 'dhcp6'}],
261- 'type': 'physical'}]}
262- patch_path = (
263- 'cloudinit.sources.DataSourceEc2.net.get_interfaces_by_mac')
264- get_interface_mac_path = (
265- 'cloudinit.sources.DataSourceEc2.net.get_interface_mac')
266+ expected = {'version': 2, 'ethernets': {'eth9': {
267+ 'match': {'macaddress': '06:17:04:d7:26:09'}, 'set-name': 'eth9',
268+ 'dhcp4': True, 'dhcp6': True}}}
269+ patch_path = M_PATH_NET + 'get_interfaces_by_mac'
270+ get_interface_mac_path = M_PATH_NET + 'get_interface_mac'
271 with mock.patch(patch_path) as m_get_interfaces_by_mac:
272 with mock.patch(find_fallback_path) as m_find_fallback:
273 with mock.patch(get_interface_mac_path) as m_get_mac:
274@@ -302,21 +385,45 @@ class TestEc2(test_helpers.HttprettyTestCase):
275 platform_data=self.valid_platform_data,
276 sys_cfg={'datasource': {'Ec2': {'strict_id': True}}},
277 md={'md': DEFAULT_METADATA})
278- find_fallback_path = (
279- 'cloudinit.sources.DataSourceEc2.net.find_fallback_nic')
280+ find_fallback_path = M_PATH_NET + 'find_fallback_nic'
281 with mock.patch(find_fallback_path) as m_find_fallback:
282 m_find_fallback.return_value = 'eth9'
283 ds.get_data()
284
285 mac1 = '06:17:04:d7:26:0A' # IPv4 only in DEFAULT_METADATA
286- expected = {'version': 1, 'config': [
287- {'mac_address': '06:17:04:d7:26:0A', 'name': 'eth9',
288- 'subnets': [{'type': 'dhcp4'}],
289- 'type': 'physical'}]}
290- patch_path = (
291- 'cloudinit.sources.DataSourceEc2.net.get_interfaces_by_mac')
292- get_interface_mac_path = (
293- 'cloudinit.sources.DataSourceEc2.net.get_interface_mac')
294+ expected = {'version': 2, 'ethernets': {'eth9': {
295+ 'match': {'macaddress': mac1.lower()}, 'set-name': 'eth9',
296+ 'dhcp4': True}}}
297+ patch_path = M_PATH_NET + 'get_interfaces_by_mac'
298+ get_interface_mac_path = M_PATH_NET + 'get_interface_mac'
299+ with mock.patch(patch_path) as m_get_interfaces_by_mac:
300+ with mock.patch(find_fallback_path) as m_find_fallback:
301+ with mock.patch(get_interface_mac_path) as m_get_mac:
302+ m_get_interfaces_by_mac.return_value = {mac1: 'eth9'}
303+ m_find_fallback.return_value = 'eth9'
304+ m_get_mac.return_value = mac1
305+ self.assertEqual(expected, ds.network_config)
306+
307+ def test_network_config_property_secondary_private_ips(self):
308+ """network_config property configures any secondary ipv4 addresses.
309+
310+ Only one device is configured even when multiple exist in metadata.
311+ """
312+ ds = self._setup_ds(
313+ platform_data=self.valid_platform_data,
314+ sys_cfg={'datasource': {'Ec2': {'strict_id': True}}},
315+ md={'md': SECONDARY_IP_METADATA_2018_09_24})
316+ find_fallback_path = M_PATH_NET + 'find_fallback_nic'
317+ with mock.patch(find_fallback_path) as m_find_fallback:
318+ m_find_fallback.return_value = 'eth9'
319+ ds.get_data()
320+
321+ mac1 = '0a:07:84:3d:6e:38' # IPv4 with 1 secondary IP
322+ expected = {'version': 2, 'ethernets': {'eth9': {
323+ 'match': {'macaddress': mac1}, 'set-name': 'eth9',
324+ 'addresses': ['172.31.45.70/20'], 'dhcp4': True}}}
325+ patch_path = M_PATH_NET + 'get_interfaces_by_mac'
326+ get_interface_mac_path = M_PATH_NET + 'get_interface_mac'
327 with mock.patch(patch_path) as m_get_interfaces_by_mac:
328 with mock.patch(find_fallback_path) as m_find_fallback:
329 with mock.patch(get_interface_mac_path) as m_get_mac:
330@@ -352,8 +459,7 @@ class TestEc2(test_helpers.HttprettyTestCase):
331 register_mock_metaserver(
332 'http://169.254.169.254/2009-04-04/meta-data/', DEFAULT_METADATA)
333 mac1 = '06:17:04:d7:26:09' # Defined in DEFAULT_METADATA
334- get_interface_mac_path = (
335- 'cloudinit.sources.DataSourceEc2.net.get_interface_mac')
336+ get_interface_mac_path = M_PATH_NET + 'get_interface_mac'
337 ds.fallback_nic = 'eth9'
338 with mock.patch(get_interface_mac_path) as m_get_interface_mac:
339 m_get_interface_mac.return_value = mac1
340@@ -362,11 +468,9 @@ class TestEc2(test_helpers.HttprettyTestCase):
341 self.assertIn(
342 'Refreshing stale metadata from prior to upgrade',
343 self.logs.getvalue())
344- expected = {'version': 1, 'config': [
345- {'mac_address': '06:17:04:d7:26:09',
346- 'name': 'eth9',
347- 'subnets': [{'type': 'dhcp4'}, {'type': 'dhcp6'}],
348- 'type': 'physical'}]}
349+ expected = {'version': 2, 'ethernets': {'eth9': {
350+ 'match': {'macaddress': mac1}, 'set-name': 'eth9',
351+ 'dhcp4': True, 'dhcp6': True}}}
352 self.assertEqual(expected, ds.network_config)
353
354 def test_ec2_get_instance_id_refreshes_identity_on_upgrade(self):
355@@ -546,19 +650,19 @@ class TestConvertEc2MetadataNetworkConfig(test_helpers.CiTestCase):
356
357 def setUp(self):
358 super(TestConvertEc2MetadataNetworkConfig, self).setUp()
359- self.mac1 = '06:17:04:d7:26:09'
360+ self.mac1 = '06:17:04:D7:26:09'
361 self.network_metadata = {
362 'interfaces': {'macs': {
363- self.mac1: {'public-ipv4s': '172.31.2.16'}}}}
364+ self.mac1: {'mac': self.mac1, 'public-ipv4s': '172.31.2.16'}}}}
365
366 def test_convert_ec2_metadata_network_config_skips_absent_macs(self):
367 """Any mac absent from metadata is skipped by network config."""
368 macs_to_nics = {self.mac1: 'eth9', 'DE:AD:BE:EF:FF:FF': 'vitualnic2'}
369
370 # DE:AD:BE:EF:FF:FF represented by OS but not in metadata
371- expected = {'version': 1, 'config': [
372- {'mac_address': self.mac1, 'type': 'physical',
373- 'name': 'eth9', 'subnets': [{'type': 'dhcp4'}]}]}
374+ expected = {'version': 2, 'ethernets': {'eth9': {
375+ 'match': {'macaddress': self.mac1.lower()}, 'set-name': 'eth9',
376+ 'dhcp4': True}}}
377 self.assertEqual(
378 expected,
379 ec2.convert_ec2_metadata_network_config(
380@@ -572,9 +676,9 @@ class TestConvertEc2MetadataNetworkConfig(test_helpers.CiTestCase):
381 network_metadata_ipv6['interfaces']['macs'][self.mac1])
382 nic1_metadata['ipv6s'] = '2620:0:1009:fd00:e442:c88d:c04d:dc85/64'
383 nic1_metadata.pop('public-ipv4s')
384- expected = {'version': 1, 'config': [
385- {'mac_address': self.mac1, 'type': 'physical',
386- 'name': 'eth9', 'subnets': [{'type': 'dhcp6'}]}]}
387+ expected = {'version': 2, 'ethernets': {'eth9': {
388+ 'match': {'macaddress': self.mac1.lower()}, 'set-name': 'eth9',
389+ 'dhcp6': True}}}
390 self.assertEqual(
391 expected,
392 ec2.convert_ec2_metadata_network_config(
393@@ -588,9 +692,9 @@ class TestConvertEc2MetadataNetworkConfig(test_helpers.CiTestCase):
394 network_metadata_ipv6['interfaces']['macs'][self.mac1])
395 nic1_metadata['local-ipv4s'] = '172.3.3.15'
396 nic1_metadata.pop('public-ipv4s')
397- expected = {'version': 1, 'config': [
398- {'mac_address': self.mac1, 'type': 'physical',
399- 'name': 'eth9', 'subnets': [{'type': 'dhcp4'}]}]}
400+ expected = {'version': 2, 'ethernets': {'eth9': {
401+ 'match': {'macaddress': self.mac1.lower()}, 'set-name': 'eth9',
402+ 'dhcp4': True}}}
403 self.assertEqual(
404 expected,
405 ec2.convert_ec2_metadata_network_config(
406@@ -605,9 +709,9 @@ class TestConvertEc2MetadataNetworkConfig(test_helpers.CiTestCase):
407 nic1_metadata['public-ipv4s'] = ''
408
409 # When no ipv4 or ipv6 content but fallback_nic set, set dhcp4 config.
410- expected = {'version': 1, 'config': [
411- {'mac_address': self.mac1, 'type': 'physical',
412- 'name': 'eth9', 'subnets': [{'type': 'dhcp4'}]}]}
413+ expected = {'version': 2, 'ethernets': {'eth9': {
414+ 'match': {'macaddress': self.mac1.lower()}, 'set-name': 'eth9',
415+ 'dhcp4': True}}}
416 self.assertEqual(
417 expected,
418 ec2.convert_ec2_metadata_network_config(
419@@ -622,10 +726,9 @@ class TestConvertEc2MetadataNetworkConfig(test_helpers.CiTestCase):
420 nic1_metadata['ipv6s'] = '2620:0:1009:fd00:e442:c88d:c04d:dc85/64'
421 nic1_metadata.pop('public-ipv4s')
422 nic1_metadata['local-ipv4s'] = '10.0.0.42' # Local ipv4 only on vpc
423- expected = {'version': 1, 'config': [
424- {'mac_address': self.mac1, 'type': 'physical',
425- 'name': 'eth9',
426- 'subnets': [{'type': 'dhcp4'}, {'type': 'dhcp6'}]}]}
427+ expected = {'version': 2, 'ethernets': {'eth9': {
428+ 'match': {'macaddress': self.mac1.lower()}, 'set-name': 'eth9',
429+ 'dhcp4': True, 'dhcp6': True}}}
430 self.assertEqual(
431 expected,
432 ec2.convert_ec2_metadata_network_config(
433@@ -638,10 +741,9 @@ class TestConvertEc2MetadataNetworkConfig(test_helpers.CiTestCase):
434 nic1_metadata = (
435 network_metadata_both['interfaces']['macs'][self.mac1])
436 nic1_metadata['ipv6s'] = '2620:0:1009:fd00:e442:c88d:c04d:dc85/64'
437- expected = {'version': 1, 'config': [
438- {'mac_address': self.mac1, 'type': 'physical',
439- 'name': 'eth9',
440- 'subnets': [{'type': 'dhcp4'}, {'type': 'dhcp6'}]}]}
441+ expected = {'version': 2, 'ethernets': {'eth9': {
442+ 'match': {'macaddress': self.mac1.lower()}, 'set-name': 'eth9',
443+ 'dhcp4': True, 'dhcp6': True}}}
444 self.assertEqual(
445 expected,
446 ec2.convert_ec2_metadata_network_config(
447@@ -649,12 +751,10 @@ class TestConvertEc2MetadataNetworkConfig(test_helpers.CiTestCase):
448
449 def test_convert_ec2_metadata_gets_macs_from_get_interfaces_by_mac(self):
450 """Convert Ec2 Metadata calls get_interfaces_by_mac by default."""
451- expected = {'version': 1, 'config': [
452- {'mac_address': self.mac1, 'type': 'physical',
453- 'name': 'eth9',
454- 'subnets': [{'type': 'dhcp4'}]}]}
455- patch_path = (
456- 'cloudinit.sources.DataSourceEc2.net.get_interfaces_by_mac')
457+ expected = {'version': 2, 'ethernets': {'eth9': {
458+ 'match': {'macaddress': self.mac1.lower()},
459+ 'set-name': 'eth9', 'dhcp4': True}}}
460+ patch_path = M_PATH_NET + 'get_interfaces_by_mac'
461 with mock.patch(patch_path) as m_get_interfaces_by_mac:
462 m_get_interfaces_by_mac.return_value = {self.mac1: 'eth9'}
463 self.assertEqual(

Subscribers

People subscribed via source and target branches