Merge ~chad.smith/cloud-init:ubuntu/devel into cloud-init:ubuntu/devel

Proposed by Chad Smith
Status: Merged
Merged at revision: 24452ef32a3be4d26bb0d551bea08b5b0d0df2fb
Proposed branch: ~chad.smith/cloud-init:ubuntu/devel
Merge into: cloud-init:ubuntu/devel
Diff against target: 307 lines (+160/-12)
4 files modified
cloudinit/distros/gentoo.py (+1/-1)
cloudinit/sources/DataSourceEc2.py (+19/-5)
debian/changelog (+10/-0)
tests/unittests/test_datasource/test_ec2.py (+130/-6)
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Approve
Scott Moser Pending
Review via email: mp+333047@code.launchpad.net

Description of the change

Upstream snapshot for master for devel

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:24452ef32a3be4d26bb0d551bea08b5b0d0df2fb
https://jenkins.ubuntu.com/server/job/cloud-init-ci/452/
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/452/rebuild

review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/cloudinit/distros/gentoo.py b/cloudinit/distros/gentoo.py
2index 0ad2f03..dc57717 100644
3--- a/cloudinit/distros/gentoo.py
4+++ b/cloudinit/distros/gentoo.py
5@@ -24,7 +24,7 @@ class Distro(distros.Distro):
6 network_conf_fn = '/etc/conf.d/net'
7 resolve_conf_fn = '/etc/resolv.conf'
8 hostname_conf_fn = '/etc/conf.d/hostname'
9- init_cmd = ['service'] # init scripts
10+ init_cmd = ['rc-service'] # init scripts
11
12 def __init__(self, name, cfg, paths):
13 distros.Distro.__init__(self, name, cfg, paths)
14diff --git a/cloudinit/sources/DataSourceEc2.py b/cloudinit/sources/DataSourceEc2.py
15index 41367a8..0ef2217 100644
16--- a/cloudinit/sources/DataSourceEc2.py
17+++ b/cloudinit/sources/DataSourceEc2.py
18@@ -64,6 +64,9 @@ class DataSourceEc2(sources.DataSource):
19 # Whether we want to get network configuration from the metadata service.
20 get_network_metadata = False
21
22+ # Track the discovered fallback nic for use in configuration generation.
23+ fallback_nic = None
24+
25 def __init__(self, sys_cfg, distro, paths):
26 sources.DataSource.__init__(self, sys_cfg, distro, paths)
27 self.metadata_address = None
28@@ -89,16 +92,18 @@ class DataSourceEc2(sources.DataSource):
29 elif self.cloud_platform == Platforms.NO_EC2_METADATA:
30 return False
31
32+ self.fallback_nic = net.find_fallback_nic()
33 if self.get_network_metadata: # Setup networking in init-local stage.
34 if util.is_FreeBSD():
35 LOG.debug("FreeBSD doesn't support running dhclient with -sf")
36 return False
37- dhcp_leases = dhcp.maybe_perform_dhcp_discovery()
38+ dhcp_leases = dhcp.maybe_perform_dhcp_discovery(self.fallback_nic)
39 if not dhcp_leases:
40 # DataSourceEc2Local failed in init-local stage. DataSourceEc2
41 # will still run in init-network stage.
42 return False
43 dhcp_opts = dhcp_leases[-1]
44+ self.fallback_nic = dhcp_opts.get('interface')
45 net_params = {'interface': dhcp_opts.get('interface'),
46 'ip': dhcp_opts.get('fixed-address'),
47 'prefix_or_mask': dhcp_opts.get('subnet-mask'),
48@@ -297,8 +302,13 @@ class DataSourceEc2(sources.DataSource):
49
50 result = None
51 net_md = self.metadata.get('network')
52+ # Limit network configuration to only the primary/fallback nic
53+ macs_to_nics = {
54+ net.get_interface_mac(self.fallback_nic): self.fallback_nic}
55 if isinstance(net_md, dict):
56- result = convert_ec2_metadata_network_config(net_md)
57+ result = convert_ec2_metadata_network_config(
58+ net_md, macs_to_nics=macs_to_nics,
59+ fallback_nic=self.fallback_nic)
60 else:
61 LOG.warning("unexpected metadata 'network' key not valid: %s",
62 net_md)
63@@ -458,15 +468,18 @@ def _collect_platform_data():
64 return data
65
66
67-def convert_ec2_metadata_network_config(network_md, macs_to_nics=None):
68+def convert_ec2_metadata_network_config(network_md, macs_to_nics=None,
69+ fallback_nic=None):
70 """Convert ec2 metadata to network config version 1 data dict.
71
72 @param: network_md: 'network' portion of EC2 metadata.
73 generally formed as {"interfaces": {"macs": {}} where
74 'macs' is a dictionary with mac address as key and contents like:
75 {"device-number": "0", "interface-id": "...", "local-ipv4s": ...}
76- @param: macs_to_name: Optional dict mac addresses and the nic name. If
77+ @param: macs_to_nics: Optional dict of mac addresses and nic names. If
78 not provided, get_interfaces_by_mac is called to get it from the OS.
79+ @param: fallback_nic: Optionally provide the primary nic interface name.
80+ This nic will be guaranteed to minimally have a dhcp4 configuration.
81
82 @return A dict of network config version 1 based on the metadata and macs.
83 """
84@@ -480,7 +493,8 @@ def convert_ec2_metadata_network_config(network_md, macs_to_nics=None):
85 continue # Not a physical nic represented in metadata
86 nic_cfg = {'type': 'physical', 'name': nic_name, 'subnets': []}
87 nic_cfg['mac_address'] = mac
88- if nic_metadata.get('public-ipv4s'):
89+ if (nic_name == fallback_nic or nic_metadata.get('public-ipv4s') or
90+ nic_metadata.get('local-ipv4s')):
91 nic_cfg['subnets'].append({'type': 'dhcp4'})
92 if nic_metadata.get('ipv6s'):
93 nic_cfg['subnets'].append({'type': 'dhcp6'})
94diff --git a/debian/changelog b/debian/changelog
95index bede4fe..a6f1f07 100644
96--- a/debian/changelog
97+++ b/debian/changelog
98@@ -1,3 +1,13 @@
99+cloud-init (17.1-27-geb292c18-0ubuntu1) bionic; urgency=medium
100+
101+ * New upstream snapshot.
102+ - EC2: Limit network config to fallback nic, fix local-ipv4 only
103+ instances. (LP: #1728152)
104+ - Gentoo: Use "rc-service" rather than "service".
105+ [ckonstanski] (LP: #1727121)
106+
107+ -- Chad Smith <chad.smith@canonical.com> Tue, 31 Oct 2017 12:51:10 -0600
108+
109 cloud-init (17.1-25-g17a15f9e-0ubuntu1) bionic; urgency=medium
110
111 * New upstream snapshot.
112diff --git a/tests/unittests/test_datasource/test_ec2.py b/tests/unittests/test_datasource/test_ec2.py
113index a7301db..6af699a 100644
114--- a/tests/unittests/test_datasource/test_ec2.py
115+++ b/tests/unittests/test_datasource/test_ec2.py
116@@ -51,6 +51,29 @@ DEFAULT_METADATA = {
117 "vpc-ipv4-cidr-block": "172.31.0.0/16",
118 "vpc-ipv4-cidr-blocks": "172.31.0.0/16",
119 "vpc-ipv6-cidr-blocks": "2600:1f16:aeb:b200::/56"
120+ },
121+ "06:17:04:d7:26:0A": {
122+ "device-number": "1", # Only IPv4 local config
123+ "interface-id": "eni-e44ef49f",
124+ "ipv4-associations": {"": "172.3.3.16"},
125+ "ipv6s": "", # No IPv6 config
126+ "local-hostname": ("ip-172-3-3-16.us-east-2."
127+ "compute.internal"),
128+ "local-ipv4s": "172.3.3.16",
129+ "mac": "06:17:04:d7:26:0A",
130+ "owner-id": "950047163771",
131+ "public-hostname": ("ec2-172-3-3-16.us-east-2."
132+ "compute.amazonaws.com"),
133+ "public-ipv4s": "", # No public ipv4 config
134+ "security-group-ids": "sg-5a61d333",
135+ "security-groups": "wide-open",
136+ "subnet-id": "subnet-20b8565b",
137+ "subnet-ipv4-cidr-block": "172.31.16.0/20",
138+ "subnet-ipv6-cidr-blocks": "",
139+ "vpc-id": "vpc-87e72bee",
140+ "vpc-ipv4-cidr-block": "172.31.0.0/16",
141+ "vpc-ipv4-cidr-blocks": "172.31.0.0/16",
142+ "vpc-ipv6-cidr-blocks": ""
143 }
144 }
145 }
146@@ -209,12 +232,20 @@ class TestEc2(test_helpers.HttprettyTestCase):
147
148 @httpretty.activate
149 def test_network_config_property_returns_version_1_network_data(self):
150- """network_config property returns network version 1 for metadata."""
151+ """network_config property returns network version 1 for metadata.
152+
153+ Only one device is configured even when multiple exist in metadata.
154+ """
155 ds = self._setup_ds(
156 platform_data=self.valid_platform_data,
157 sys_cfg={'datasource': {'Ec2': {'strict_id': True}}},
158 md=DEFAULT_METADATA)
159- ds.get_data()
160+ find_fallback_path = (
161+ 'cloudinit.sources.DataSourceEc2.net.find_fallback_nic')
162+ with mock.patch(find_fallback_path) as m_find_fallback:
163+ m_find_fallback.return_value = 'eth9'
164+ ds.get_data()
165+
166 mac1 = '06:17:04:d7:26:09' # Defined in DEFAULT_METADATA
167 expected = {'version': 1, 'config': [
168 {'mac_address': '06:17:04:d7:26:09', 'name': 'eth9',
169@@ -222,9 +253,48 @@ class TestEc2(test_helpers.HttprettyTestCase):
170 'type': 'physical'}]}
171 patch_path = (
172 'cloudinit.sources.DataSourceEc2.net.get_interfaces_by_mac')
173+ get_interface_mac_path = (
174+ 'cloudinit.sources.DataSourceEc2.net.get_interface_mac')
175+ with mock.patch(patch_path) as m_get_interfaces_by_mac:
176+ with mock.patch(find_fallback_path) as m_find_fallback:
177+ with mock.patch(get_interface_mac_path) as m_get_mac:
178+ m_get_interfaces_by_mac.return_value = {mac1: 'eth9'}
179+ m_find_fallback.return_value = 'eth9'
180+ m_get_mac.return_value = mac1
181+ self.assertEqual(expected, ds.network_config)
182+
183+ @httpretty.activate
184+ def test_network_config_property_set_dhcp4_on_private_ipv4(self):
185+ """network_config property configures dhcp4 on private ipv4 nics.
186+
187+ Only one device is configured even when multiple exist in metadata.
188+ """
189+ ds = self._setup_ds(
190+ platform_data=self.valid_platform_data,
191+ sys_cfg={'datasource': {'Ec2': {'strict_id': True}}},
192+ md=DEFAULT_METADATA)
193+ find_fallback_path = (
194+ 'cloudinit.sources.DataSourceEc2.net.find_fallback_nic')
195+ with mock.patch(find_fallback_path) as m_find_fallback:
196+ m_find_fallback.return_value = 'eth9'
197+ ds.get_data()
198+
199+ mac1 = '06:17:04:d7:26:0A' # IPv4 only in DEFAULT_METADATA
200+ expected = {'version': 1, 'config': [
201+ {'mac_address': '06:17:04:d7:26:0A', 'name': 'eth9',
202+ 'subnets': [{'type': 'dhcp4'}],
203+ 'type': 'physical'}]}
204+ patch_path = (
205+ 'cloudinit.sources.DataSourceEc2.net.get_interfaces_by_mac')
206+ get_interface_mac_path = (
207+ 'cloudinit.sources.DataSourceEc2.net.get_interface_mac')
208 with mock.patch(patch_path) as m_get_interfaces_by_mac:
209- m_get_interfaces_by_mac.return_value = {mac1: 'eth9'}
210- self.assertEqual(expected, ds.network_config)
211+ with mock.patch(find_fallback_path) as m_find_fallback:
212+ with mock.patch(get_interface_mac_path) as m_get_mac:
213+ m_get_interfaces_by_mac.return_value = {mac1: 'eth9'}
214+ m_find_fallback.return_value = 'eth9'
215+ m_get_mac.return_value = mac1
216+ self.assertEqual(expected, ds.network_config)
217
218 def test_network_config_property_is_cached_in_datasource(self):
219 """network_config property is cached in DataSourceEc2."""
220@@ -321,9 +391,11 @@ class TestEc2(test_helpers.HttprettyTestCase):
221
222 @httpretty.activate
223 @mock.patch('cloudinit.net.EphemeralIPv4Network')
224+ @mock.patch('cloudinit.net.find_fallback_nic')
225 @mock.patch('cloudinit.net.dhcp.maybe_perform_dhcp_discovery')
226 @mock.patch('cloudinit.sources.DataSourceEc2.util.is_FreeBSD')
227- def test_ec2_local_performs_dhcp_on_non_bsd(self, m_is_bsd, m_dhcp, m_net):
228+ def test_ec2_local_performs_dhcp_on_non_bsd(self, m_is_bsd, m_dhcp,
229+ m_fallback_nic, m_net):
230 """Ec2Local returns True for valid platform data on non-BSD with dhcp.
231
232 DataSourceEc2Local will setup initial IPv4 network via dhcp discovery.
233@@ -331,6 +403,7 @@ class TestEc2(test_helpers.HttprettyTestCase):
234 When the platform data is valid, return True.
235 """
236
237+ m_fallback_nic.return_value = 'eth9'
238 m_is_bsd.return_value = False
239 m_dhcp.return_value = [{
240 'interface': 'eth9', 'fixed-address': '192.168.2.9',
241@@ -344,7 +417,7 @@ class TestEc2(test_helpers.HttprettyTestCase):
242
243 ret = ds.get_data()
244 self.assertTrue(ret)
245- m_dhcp.assert_called_once_with()
246+ m_dhcp.assert_called_once_with('eth9')
247 m_net.assert_called_once_with(
248 broadcast='192.168.2.255', interface='eth9', ip='192.168.2.9',
249 prefix_or_mask='255.255.255.0', router='192.168.2.1')
250@@ -389,6 +462,57 @@ class TestConvertEc2MetadataNetworkConfig(test_helpers.CiTestCase):
251 ec2.convert_ec2_metadata_network_config(
252 network_metadata_ipv6, macs_to_nics))
253
254+ def test_convert_ec2_metadata_network_config_handles_local_dhcp4(self):
255+ """Config dhcp4 when there are no public addresses in public-ipv4s."""
256+ macs_to_nics = {self.mac1: 'eth9'}
257+ network_metadata_ipv6 = copy.deepcopy(self.network_metadata)
258+ nic1_metadata = (
259+ network_metadata_ipv6['interfaces']['macs'][self.mac1])
260+ nic1_metadata['local-ipv4s'] = '172.3.3.15'
261+ nic1_metadata.pop('public-ipv4s')
262+ expected = {'version': 1, 'config': [
263+ {'mac_address': self.mac1, 'type': 'physical',
264+ 'name': 'eth9', 'subnets': [{'type': 'dhcp4'}]}]}
265+ self.assertEqual(
266+ expected,
267+ ec2.convert_ec2_metadata_network_config(
268+ network_metadata_ipv6, macs_to_nics))
269+
270+ def test_convert_ec2_metadata_network_config_handles_absent_dhcp4(self):
271+ """Config dhcp4 on fallback_nic when there are no ipv4 addresses."""
272+ macs_to_nics = {self.mac1: 'eth9'}
273+ network_metadata_ipv6 = copy.deepcopy(self.network_metadata)
274+ nic1_metadata = (
275+ network_metadata_ipv6['interfaces']['macs'][self.mac1])
276+ nic1_metadata['public-ipv4s'] = ''
277+
278+ # When no ipv4 or ipv6 content but fallback_nic set, set dhcp4 config.
279+ expected = {'version': 1, 'config': [
280+ {'mac_address': self.mac1, 'type': 'physical',
281+ 'name': 'eth9', 'subnets': [{'type': 'dhcp4'}]}]}
282+ self.assertEqual(
283+ expected,
284+ ec2.convert_ec2_metadata_network_config(
285+ network_metadata_ipv6, macs_to_nics, fallback_nic='eth9'))
286+
287+ def test_convert_ec2_metadata_network_config_handles_local_v4_and_v6(self):
288+ """When dhcp6 is public and dhcp4 is set to local enable both."""
289+ macs_to_nics = {self.mac1: 'eth9'}
290+ network_metadata_both = copy.deepcopy(self.network_metadata)
291+ nic1_metadata = (
292+ network_metadata_both['interfaces']['macs'][self.mac1])
293+ nic1_metadata['ipv6s'] = '2620:0:1009:fd00:e442:c88d:c04d:dc85/64'
294+ nic1_metadata.pop('public-ipv4s')
295+ nic1_metadata['local-ipv4s'] = '10.0.0.42' # Local ipv4 only on vpc
296+ expected = {'version': 1, 'config': [
297+ {'mac_address': self.mac1, 'type': 'physical',
298+ 'name': 'eth9',
299+ 'subnets': [{'type': 'dhcp4'}, {'type': 'dhcp6'}]}]}
300+ self.assertEqual(
301+ expected,
302+ ec2.convert_ec2_metadata_network_config(
303+ network_metadata_both, macs_to_nics))
304+
305 def test_convert_ec2_metadata_network_config_handles_dhcp4_and_dhcp6(self):
306 """Config both dhcp4 and dhcp6 when both vpc-ipv6 and ipv4 exists."""
307 macs_to_nics = {self.mac1: 'eth9'}

Subscribers

People subscribed via source and target branches