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

Proposed by Chad Smith
Status: Merged
Merged at revision: 91c0f668530566b4eb943fdda537b96454f0dc7d
Proposed branch: ~chad.smith/cloud-init:ubuntu/zesty
Merge into: cloud-init:ubuntu/zesty
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+333050@code.launchpad.net

Description of the change

Upstream snapshot for SRU

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:91c0f668530566b4eb943fdda537b96454f0dc7d
https://jenkins.ubuntu.com/server/job/cloud-init-ci/455/
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/455/rebuild

review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/cloudinit/distros/gentoo.py b/cloudinit/distros/gentoo.py
index 0ad2f03..dc57717 100644
--- a/cloudinit/distros/gentoo.py
+++ b/cloudinit/distros/gentoo.py
@@ -24,7 +24,7 @@ class Distro(distros.Distro):
24 network_conf_fn = '/etc/conf.d/net'24 network_conf_fn = '/etc/conf.d/net'
25 resolve_conf_fn = '/etc/resolv.conf'25 resolve_conf_fn = '/etc/resolv.conf'
26 hostname_conf_fn = '/etc/conf.d/hostname'26 hostname_conf_fn = '/etc/conf.d/hostname'
27 init_cmd = ['service'] # init scripts27 init_cmd = ['rc-service'] # init scripts
2828
29 def __init__(self, name, cfg, paths):29 def __init__(self, name, cfg, paths):
30 distros.Distro.__init__(self, name, cfg, paths)30 distros.Distro.__init__(self, name, cfg, paths)
diff --git a/cloudinit/sources/DataSourceEc2.py b/cloudinit/sources/DataSourceEc2.py
index 41367a8..0ef2217 100644
--- a/cloudinit/sources/DataSourceEc2.py
+++ b/cloudinit/sources/DataSourceEc2.py
@@ -64,6 +64,9 @@ class DataSourceEc2(sources.DataSource):
64 # Whether we want to get network configuration from the metadata service.64 # Whether we want to get network configuration from the metadata service.
65 get_network_metadata = False65 get_network_metadata = False
6666
67 # Track the discovered fallback nic for use in configuration generation.
68 fallback_nic = None
69
67 def __init__(self, sys_cfg, distro, paths):70 def __init__(self, sys_cfg, distro, paths):
68 sources.DataSource.__init__(self, sys_cfg, distro, paths)71 sources.DataSource.__init__(self, sys_cfg, distro, paths)
69 self.metadata_address = None72 self.metadata_address = None
@@ -89,16 +92,18 @@ class DataSourceEc2(sources.DataSource):
89 elif self.cloud_platform == Platforms.NO_EC2_METADATA:92 elif self.cloud_platform == Platforms.NO_EC2_METADATA:
90 return False93 return False
9194
95 self.fallback_nic = net.find_fallback_nic()
92 if self.get_network_metadata: # Setup networking in init-local stage.96 if self.get_network_metadata: # Setup networking in init-local stage.
93 if util.is_FreeBSD():97 if util.is_FreeBSD():
94 LOG.debug("FreeBSD doesn't support running dhclient with -sf")98 LOG.debug("FreeBSD doesn't support running dhclient with -sf")
95 return False99 return False
96 dhcp_leases = dhcp.maybe_perform_dhcp_discovery()100 dhcp_leases = dhcp.maybe_perform_dhcp_discovery(self.fallback_nic)
97 if not dhcp_leases:101 if not dhcp_leases:
98 # DataSourceEc2Local failed in init-local stage. DataSourceEc2102 # DataSourceEc2Local failed in init-local stage. DataSourceEc2
99 # will still run in init-network stage.103 # will still run in init-network stage.
100 return False104 return False
101 dhcp_opts = dhcp_leases[-1]105 dhcp_opts = dhcp_leases[-1]
106 self.fallback_nic = dhcp_opts.get('interface')
102 net_params = {'interface': dhcp_opts.get('interface'),107 net_params = {'interface': dhcp_opts.get('interface'),
103 'ip': dhcp_opts.get('fixed-address'),108 'ip': dhcp_opts.get('fixed-address'),
104 'prefix_or_mask': dhcp_opts.get('subnet-mask'),109 'prefix_or_mask': dhcp_opts.get('subnet-mask'),
@@ -297,8 +302,13 @@ class DataSourceEc2(sources.DataSource):
297302
298 result = None303 result = None
299 net_md = self.metadata.get('network')304 net_md = self.metadata.get('network')
305 # Limit network configuration to only the primary/fallback nic
306 macs_to_nics = {
307 net.get_interface_mac(self.fallback_nic): self.fallback_nic}
300 if isinstance(net_md, dict):308 if isinstance(net_md, dict):
301 result = convert_ec2_metadata_network_config(net_md)309 result = convert_ec2_metadata_network_config(
310 net_md, macs_to_nics=macs_to_nics,
311 fallback_nic=self.fallback_nic)
302 else:312 else:
303 LOG.warning("unexpected metadata 'network' key not valid: %s",313 LOG.warning("unexpected metadata 'network' key not valid: %s",
304 net_md)314 net_md)
@@ -458,15 +468,18 @@ def _collect_platform_data():
458 return data468 return data
459469
460470
461def convert_ec2_metadata_network_config(network_md, macs_to_nics=None):471def convert_ec2_metadata_network_config(network_md, macs_to_nics=None,
472 fallback_nic=None):
462 """Convert ec2 metadata to network config version 1 data dict.473 """Convert ec2 metadata to network config version 1 data dict.
463474
464 @param: network_md: 'network' portion of EC2 metadata.475 @param: network_md: 'network' portion of EC2 metadata.
465 generally formed as {"interfaces": {"macs": {}} where476 generally formed as {"interfaces": {"macs": {}} where
466 'macs' is a dictionary with mac address as key and contents like:477 'macs' is a dictionary with mac address as key and contents like:
467 {"device-number": "0", "interface-id": "...", "local-ipv4s": ...}478 {"device-number": "0", "interface-id": "...", "local-ipv4s": ...}
468 @param: macs_to_name: Optional dict mac addresses and the nic name. If479 @param: macs_to_nics: Optional dict of mac addresses and nic names. If
469 not provided, get_interfaces_by_mac is called to get it from the OS.480 not provided, get_interfaces_by_mac is called to get it from the OS.
481 @param: fallback_nic: Optionally provide the primary nic interface name.
482 This nic will be guaranteed to minimally have a dhcp4 configuration.
470483
471 @return A dict of network config version 1 based on the metadata and macs.484 @return A dict of network config version 1 based on the metadata and macs.
472 """485 """
@@ -480,7 +493,8 @@ def convert_ec2_metadata_network_config(network_md, macs_to_nics=None):
480 continue # Not a physical nic represented in metadata493 continue # Not a physical nic represented in metadata
481 nic_cfg = {'type': 'physical', 'name': nic_name, 'subnets': []}494 nic_cfg = {'type': 'physical', 'name': nic_name, 'subnets': []}
482 nic_cfg['mac_address'] = mac495 nic_cfg['mac_address'] = mac
483 if nic_metadata.get('public-ipv4s'):496 if (nic_name == fallback_nic or nic_metadata.get('public-ipv4s') or
497 nic_metadata.get('local-ipv4s')):
484 nic_cfg['subnets'].append({'type': 'dhcp4'})498 nic_cfg['subnets'].append({'type': 'dhcp4'})
485 if nic_metadata.get('ipv6s'):499 if nic_metadata.get('ipv6s'):
486 nic_cfg['subnets'].append({'type': 'dhcp6'})500 nic_cfg['subnets'].append({'type': 'dhcp6'})
diff --git a/debian/changelog b/debian/changelog
index 86a8d0c..8a1fc27 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,13 @@
1cloud-init (17.1-27-geb292c18-0ubuntu1~17.04.1) zesty-proposed; urgency=medium
2
3 * New upstream snapshot.
4 - EC2: Limit network config to fallback nic, fix local-ipv4 only
5 instances. (LP: #1728152)
6 - Gentoo: Use "rc-service" rather than "service".
7 [ckonstanski] (LP: #1727121)
8
9 -- Chad Smith <chad.smith@canonical.com> Tue, 31 Oct 2017 13:08:43 -0600
10
1cloud-init (17.1-25-g17a15f9e-0ubuntu1~17.04.1) zesty-proposed; urgency=medium11cloud-init (17.1-25-g17a15f9e-0ubuntu1~17.04.1) zesty-proposed; urgency=medium
212
3 * New upstream snapshot.13 * New upstream snapshot.
diff --git a/tests/unittests/test_datasource/test_ec2.py b/tests/unittests/test_datasource/test_ec2.py
index a7301db..6af699a 100644
--- a/tests/unittests/test_datasource/test_ec2.py
+++ b/tests/unittests/test_datasource/test_ec2.py
@@ -51,6 +51,29 @@ DEFAULT_METADATA = {
51 "vpc-ipv4-cidr-block": "172.31.0.0/16",51 "vpc-ipv4-cidr-block": "172.31.0.0/16",
52 "vpc-ipv4-cidr-blocks": "172.31.0.0/16",52 "vpc-ipv4-cidr-blocks": "172.31.0.0/16",
53 "vpc-ipv6-cidr-blocks": "2600:1f16:aeb:b200::/56"53 "vpc-ipv6-cidr-blocks": "2600:1f16:aeb:b200::/56"
54 },
55 "06:17:04:d7:26:0A": {
56 "device-number": "1", # Only IPv4 local config
57 "interface-id": "eni-e44ef49f",
58 "ipv4-associations": {"": "172.3.3.16"},
59 "ipv6s": "", # No IPv6 config
60 "local-hostname": ("ip-172-3-3-16.us-east-2."
61 "compute.internal"),
62 "local-ipv4s": "172.3.3.16",
63 "mac": "06:17:04:d7:26:0A",
64 "owner-id": "950047163771",
65 "public-hostname": ("ec2-172-3-3-16.us-east-2."
66 "compute.amazonaws.com"),
67 "public-ipv4s": "", # No public ipv4 config
68 "security-group-ids": "sg-5a61d333",
69 "security-groups": "wide-open",
70 "subnet-id": "subnet-20b8565b",
71 "subnet-ipv4-cidr-block": "172.31.16.0/20",
72 "subnet-ipv6-cidr-blocks": "",
73 "vpc-id": "vpc-87e72bee",
74 "vpc-ipv4-cidr-block": "172.31.0.0/16",
75 "vpc-ipv4-cidr-blocks": "172.31.0.0/16",
76 "vpc-ipv6-cidr-blocks": ""
54 }77 }
55 }78 }
56 }79 }
@@ -209,12 +232,20 @@ class TestEc2(test_helpers.HttprettyTestCase):
209232
210 @httpretty.activate233 @httpretty.activate
211 def test_network_config_property_returns_version_1_network_data(self):234 def test_network_config_property_returns_version_1_network_data(self):
212 """network_config property returns network version 1 for metadata."""235 """network_config property returns network version 1 for metadata.
236
237 Only one device is configured even when multiple exist in metadata.
238 """
213 ds = self._setup_ds(239 ds = self._setup_ds(
214 platform_data=self.valid_platform_data,240 platform_data=self.valid_platform_data,
215 sys_cfg={'datasource': {'Ec2': {'strict_id': True}}},241 sys_cfg={'datasource': {'Ec2': {'strict_id': True}}},
216 md=DEFAULT_METADATA)242 md=DEFAULT_METADATA)
217 ds.get_data()243 find_fallback_path = (
244 'cloudinit.sources.DataSourceEc2.net.find_fallback_nic')
245 with mock.patch(find_fallback_path) as m_find_fallback:
246 m_find_fallback.return_value = 'eth9'
247 ds.get_data()
248
218 mac1 = '06:17:04:d7:26:09' # Defined in DEFAULT_METADATA249 mac1 = '06:17:04:d7:26:09' # Defined in DEFAULT_METADATA
219 expected = {'version': 1, 'config': [250 expected = {'version': 1, 'config': [
220 {'mac_address': '06:17:04:d7:26:09', 'name': 'eth9',251 {'mac_address': '06:17:04:d7:26:09', 'name': 'eth9',
@@ -222,9 +253,48 @@ class TestEc2(test_helpers.HttprettyTestCase):
222 'type': 'physical'}]}253 'type': 'physical'}]}
223 patch_path = (254 patch_path = (
224 'cloudinit.sources.DataSourceEc2.net.get_interfaces_by_mac')255 'cloudinit.sources.DataSourceEc2.net.get_interfaces_by_mac')
256 get_interface_mac_path = (
257 'cloudinit.sources.DataSourceEc2.net.get_interface_mac')
258 with mock.patch(patch_path) as m_get_interfaces_by_mac:
259 with mock.patch(find_fallback_path) as m_find_fallback:
260 with mock.patch(get_interface_mac_path) as m_get_mac:
261 m_get_interfaces_by_mac.return_value = {mac1: 'eth9'}
262 m_find_fallback.return_value = 'eth9'
263 m_get_mac.return_value = mac1
264 self.assertEqual(expected, ds.network_config)
265
266 @httpretty.activate
267 def test_network_config_property_set_dhcp4_on_private_ipv4(self):
268 """network_config property configures dhcp4 on private ipv4 nics.
269
270 Only one device is configured even when multiple exist in metadata.
271 """
272 ds = self._setup_ds(
273 platform_data=self.valid_platform_data,
274 sys_cfg={'datasource': {'Ec2': {'strict_id': True}}},
275 md=DEFAULT_METADATA)
276 find_fallback_path = (
277 'cloudinit.sources.DataSourceEc2.net.find_fallback_nic')
278 with mock.patch(find_fallback_path) as m_find_fallback:
279 m_find_fallback.return_value = 'eth9'
280 ds.get_data()
281
282 mac1 = '06:17:04:d7:26:0A' # IPv4 only in DEFAULT_METADATA
283 expected = {'version': 1, 'config': [
284 {'mac_address': '06:17:04:d7:26:0A', 'name': 'eth9',
285 'subnets': [{'type': 'dhcp4'}],
286 'type': 'physical'}]}
287 patch_path = (
288 'cloudinit.sources.DataSourceEc2.net.get_interfaces_by_mac')
289 get_interface_mac_path = (
290 'cloudinit.sources.DataSourceEc2.net.get_interface_mac')
225 with mock.patch(patch_path) as m_get_interfaces_by_mac:291 with mock.patch(patch_path) as m_get_interfaces_by_mac:
226 m_get_interfaces_by_mac.return_value = {mac1: 'eth9'}292 with mock.patch(find_fallback_path) as m_find_fallback:
227 self.assertEqual(expected, ds.network_config)293 with mock.patch(get_interface_mac_path) as m_get_mac:
294 m_get_interfaces_by_mac.return_value = {mac1: 'eth9'}
295 m_find_fallback.return_value = 'eth9'
296 m_get_mac.return_value = mac1
297 self.assertEqual(expected, ds.network_config)
228298
229 def test_network_config_property_is_cached_in_datasource(self):299 def test_network_config_property_is_cached_in_datasource(self):
230 """network_config property is cached in DataSourceEc2."""300 """network_config property is cached in DataSourceEc2."""
@@ -321,9 +391,11 @@ class TestEc2(test_helpers.HttprettyTestCase):
321391
322 @httpretty.activate392 @httpretty.activate
323 @mock.patch('cloudinit.net.EphemeralIPv4Network')393 @mock.patch('cloudinit.net.EphemeralIPv4Network')
394 @mock.patch('cloudinit.net.find_fallback_nic')
324 @mock.patch('cloudinit.net.dhcp.maybe_perform_dhcp_discovery')395 @mock.patch('cloudinit.net.dhcp.maybe_perform_dhcp_discovery')
325 @mock.patch('cloudinit.sources.DataSourceEc2.util.is_FreeBSD')396 @mock.patch('cloudinit.sources.DataSourceEc2.util.is_FreeBSD')
326 def test_ec2_local_performs_dhcp_on_non_bsd(self, m_is_bsd, m_dhcp, m_net):397 def test_ec2_local_performs_dhcp_on_non_bsd(self, m_is_bsd, m_dhcp,
398 m_fallback_nic, m_net):
327 """Ec2Local returns True for valid platform data on non-BSD with dhcp.399 """Ec2Local returns True for valid platform data on non-BSD with dhcp.
328400
329 DataSourceEc2Local will setup initial IPv4 network via dhcp discovery.401 DataSourceEc2Local will setup initial IPv4 network via dhcp discovery.
@@ -331,6 +403,7 @@ class TestEc2(test_helpers.HttprettyTestCase):
331 When the platform data is valid, return True.403 When the platform data is valid, return True.
332 """404 """
333405
406 m_fallback_nic.return_value = 'eth9'
334 m_is_bsd.return_value = False407 m_is_bsd.return_value = False
335 m_dhcp.return_value = [{408 m_dhcp.return_value = [{
336 'interface': 'eth9', 'fixed-address': '192.168.2.9',409 'interface': 'eth9', 'fixed-address': '192.168.2.9',
@@ -344,7 +417,7 @@ class TestEc2(test_helpers.HttprettyTestCase):
344417
345 ret = ds.get_data()418 ret = ds.get_data()
346 self.assertTrue(ret)419 self.assertTrue(ret)
347 m_dhcp.assert_called_once_with()420 m_dhcp.assert_called_once_with('eth9')
348 m_net.assert_called_once_with(421 m_net.assert_called_once_with(
349 broadcast='192.168.2.255', interface='eth9', ip='192.168.2.9',422 broadcast='192.168.2.255', interface='eth9', ip='192.168.2.9',
350 prefix_or_mask='255.255.255.0', router='192.168.2.1')423 prefix_or_mask='255.255.255.0', router='192.168.2.1')
@@ -389,6 +462,57 @@ class TestConvertEc2MetadataNetworkConfig(test_helpers.CiTestCase):
389 ec2.convert_ec2_metadata_network_config(462 ec2.convert_ec2_metadata_network_config(
390 network_metadata_ipv6, macs_to_nics))463 network_metadata_ipv6, macs_to_nics))
391464
465 def test_convert_ec2_metadata_network_config_handles_local_dhcp4(self):
466 """Config dhcp4 when there are no public addresses in public-ipv4s."""
467 macs_to_nics = {self.mac1: 'eth9'}
468 network_metadata_ipv6 = copy.deepcopy(self.network_metadata)
469 nic1_metadata = (
470 network_metadata_ipv6['interfaces']['macs'][self.mac1])
471 nic1_metadata['local-ipv4s'] = '172.3.3.15'
472 nic1_metadata.pop('public-ipv4s')
473 expected = {'version': 1, 'config': [
474 {'mac_address': self.mac1, 'type': 'physical',
475 'name': 'eth9', 'subnets': [{'type': 'dhcp4'}]}]}
476 self.assertEqual(
477 expected,
478 ec2.convert_ec2_metadata_network_config(
479 network_metadata_ipv6, macs_to_nics))
480
481 def test_convert_ec2_metadata_network_config_handles_absent_dhcp4(self):
482 """Config dhcp4 on fallback_nic when there are no ipv4 addresses."""
483 macs_to_nics = {self.mac1: 'eth9'}
484 network_metadata_ipv6 = copy.deepcopy(self.network_metadata)
485 nic1_metadata = (
486 network_metadata_ipv6['interfaces']['macs'][self.mac1])
487 nic1_metadata['public-ipv4s'] = ''
488
489 # When no ipv4 or ipv6 content but fallback_nic set, set dhcp4 config.
490 expected = {'version': 1, 'config': [
491 {'mac_address': self.mac1, 'type': 'physical',
492 'name': 'eth9', 'subnets': [{'type': 'dhcp4'}]}]}
493 self.assertEqual(
494 expected,
495 ec2.convert_ec2_metadata_network_config(
496 network_metadata_ipv6, macs_to_nics, fallback_nic='eth9'))
497
498 def test_convert_ec2_metadata_network_config_handles_local_v4_and_v6(self):
499 """When dhcp6 is public and dhcp4 is set to local enable both."""
500 macs_to_nics = {self.mac1: 'eth9'}
501 network_metadata_both = copy.deepcopy(self.network_metadata)
502 nic1_metadata = (
503 network_metadata_both['interfaces']['macs'][self.mac1])
504 nic1_metadata['ipv6s'] = '2620:0:1009:fd00:e442:c88d:c04d:dc85/64'
505 nic1_metadata.pop('public-ipv4s')
506 nic1_metadata['local-ipv4s'] = '10.0.0.42' # Local ipv4 only on vpc
507 expected = {'version': 1, 'config': [
508 {'mac_address': self.mac1, 'type': 'physical',
509 'name': 'eth9',
510 'subnets': [{'type': 'dhcp4'}, {'type': 'dhcp6'}]}]}
511 self.assertEqual(
512 expected,
513 ec2.convert_ec2_metadata_network_config(
514 network_metadata_both, macs_to_nics))
515
392 def test_convert_ec2_metadata_network_config_handles_dhcp4_and_dhcp6(self):516 def test_convert_ec2_metadata_network_config_handles_dhcp4_and_dhcp6(self):
393 """Config both dhcp4 and dhcp6 when both vpc-ipv6 and ipv4 exists."""517 """Config both dhcp4 and dhcp6 when both vpc-ipv6 and ipv4 exists."""
394 macs_to_nics = {self.mac1: 'eth9'}518 macs_to_nics = {self.mac1: 'eth9'}

Subscribers

People subscribed via source and target branches