Merge ~chad.smith/cloud-init:ubuntu/devel into cloud-init:ubuntu/devel
- Git
- lp:~chad.smith/cloud-init
- ubuntu/devel
- Merge into 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) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Server Team CI bot | continuous-integration | Approve | |
Scott Moser | Pending | ||
Review via email: mp+333047@code.launchpad.net |
Commit message
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 : | # |
review:
Approve
(continuous-integration)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/cloudinit/distros/gentoo.py b/cloudinit/distros/gentoo.py |
2 | index 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) |
14 | diff --git a/cloudinit/sources/DataSourceEc2.py b/cloudinit/sources/DataSourceEc2.py |
15 | index 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'}) |
94 | diff --git a/debian/changelog b/debian/changelog |
95 | index 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. |
112 | diff --git a/tests/unittests/test_datasource/test_ec2.py b/tests/unittests/test_datasource/test_ec2.py |
113 | index 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'} |
PASSED: Continuous integration, rev:24452ef32a3 be4d26bb0d551be a08b5b0d0df2fb /jenkins. ubuntu. com/server/ job/cloud- init-ci/ 452/
https:/
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: /jenkins. ubuntu. com/server/ job/cloud- init-ci/ 452/rebuild
https:/