update to 19.4

This commit is contained in:
liquor 2020-07-31 15:06:51 +08:00
parent 19c75d6eb8
commit 1711c423fb
20 changed files with 919 additions and 2179 deletions

View File

@ -1,175 +0,0 @@
From 281a82181716183d526e76f4e0415e0a6f680cbe Mon Sep 17 00:00:00 2001
From: Scott Moser <smoser@brickies.net>
Date: Mon, 20 Nov 2017 15:56:40 -0500
Subject: [PATCH 040/354] EC2: Fix bug using fallback_nic and metadata when
restoring from cache.
If user upgraded to new cloud-init and attempted to run 'cloud-init init'
without rebooting, cloud-init restores the datasource object from pickle.
The older version pickled datasource object had no value for
_network_config or fallback_nic. This caused the Ec2 datasource to attempt
to reconfigure networking with a None fallback_nic. The pickled object
also cached an older version of ec2 metadata which didn't contain network
information.
This branch does two things:
- Add a fallback_interface property to DatasourceEC2 to support reading the
old .fallback_nic attribute if it was set. New versions will
call net.find_fallback_nic() if there has not been one found.
- Re-crawl metadata if we are on Ec2 and don't have a 'network' key in
metadata
LP: #1732917
---
cloudinit/net/dhcp.py | 3 +-
cloudinit/sources/DataSourceEc2.py | 44 +++++++++++++++++++++--------
tests/unittests/test_datasource/test_ec2.py | 33 ++++++++++++++++++++++
3 files changed, 67 insertions(+), 13 deletions(-)
diff --git a/cloudinit/net/dhcp.py b/cloudinit/net/dhcp.py
index f3a412a..d8624d8 100644
--- a/cloudinit/net/dhcp.py
+++ b/cloudinit/net/dhcp.py
@@ -42,8 +42,7 @@ def maybe_perform_dhcp_discovery(nic=None):
if nic is None:
nic = find_fallback_nic()
if nic is None:
- LOG.debug(
- 'Skip dhcp_discovery: Unable to find fallback nic.')
+ LOG.debug('Skip dhcp_discovery: Unable to find fallback nic.')
return {}
elif nic not in get_devicelist():
LOG.debug(
diff --git a/cloudinit/sources/DataSourceEc2.py b/cloudinit/sources/DataSourceEc2.py
index 0ef2217..7bbbfb6 100644
--- a/cloudinit/sources/DataSourceEc2.py
+++ b/cloudinit/sources/DataSourceEc2.py
@@ -65,7 +65,7 @@ class DataSourceEc2(sources.DataSource):
get_network_metadata = False
# Track the discovered fallback nic for use in configuration generation.
- fallback_nic = None
+ _fallback_interface = None
def __init__(self, sys_cfg, distro, paths):
sources.DataSource.__init__(self, sys_cfg, distro, paths)
@@ -92,18 +92,17 @@ class DataSourceEc2(sources.DataSource):
elif self.cloud_platform == Platforms.NO_EC2_METADATA:
return False
- self.fallback_nic = net.find_fallback_nic()
if self.get_network_metadata: # Setup networking in init-local stage.
if util.is_FreeBSD():
LOG.debug("FreeBSD doesn't support running dhclient with -sf")
return False
- dhcp_leases = dhcp.maybe_perform_dhcp_discovery(self.fallback_nic)
+ dhcp_leases = dhcp.maybe_perform_dhcp_discovery(
+ self.fallback_interface)
if not dhcp_leases:
# DataSourceEc2Local failed in init-local stage. DataSourceEc2
# will still run in init-network stage.
return False
dhcp_opts = dhcp_leases[-1]
- self.fallback_nic = dhcp_opts.get('interface')
net_params = {'interface': dhcp_opts.get('interface'),
'ip': dhcp_opts.get('fixed-address'),
'prefix_or_mask': dhcp_opts.get('subnet-mask'),
@@ -301,21 +300,44 @@ class DataSourceEc2(sources.DataSource):
return None
result = None
- net_md = self.metadata.get('network')
+ no_network_metadata_on_aws = bool(
+ 'network' not in self.metadata and
+ self.cloud_platform == Platforms.AWS)
+ if no_network_metadata_on_aws:
+ LOG.debug("Metadata 'network' not present:"
+ " Refreshing stale metadata from prior to upgrade.")
+ util.log_time(
+ logfunc=LOG.debug, msg='Re-crawl of metadata service',
+ func=self._crawl_metadata)
+
# Limit network configuration to only the primary/fallback nic
- macs_to_nics = {
- net.get_interface_mac(self.fallback_nic): self.fallback_nic}
+ iface = self.fallback_interface
+ macs_to_nics = {net.get_interface_mac(iface): iface}
+ net_md = self.metadata.get('network')
if isinstance(net_md, dict):
result = convert_ec2_metadata_network_config(
- net_md, macs_to_nics=macs_to_nics,
- fallback_nic=self.fallback_nic)
+ net_md, macs_to_nics=macs_to_nics, fallback_nic=iface)
else:
- LOG.warning("unexpected metadata 'network' key not valid: %s",
- net_md)
+ LOG.warning("Metadata 'network' key not valid: %s.", net_md)
self._network_config = result
return self._network_config
+ @property
+ def fallback_interface(self):
+ if self._fallback_interface is None:
+ # fallback_nic was used at one point, so restored objects may
+ # have an attribute there. respect that if found.
+ _legacy_fbnic = getattr(self, 'fallback_nic', None)
+ if _legacy_fbnic:
+ self._fallback_interface = _legacy_fbnic
+ self.fallback_nic = None
+ else:
+ self._fallback_interface = net.find_fallback_nic()
+ if self._fallback_interface is None:
+ LOG.warning("Did not find a fallback interface on EC2.")
+ return self._fallback_interface
+
def _crawl_metadata(self):
"""Crawl metadata service when available.
diff --git a/tests/unittests/test_datasource/test_ec2.py b/tests/unittests/test_datasource/test_ec2.py
index 6af699a..ba328ee 100644
--- a/tests/unittests/test_datasource/test_ec2.py
+++ b/tests/unittests/test_datasource/test_ec2.py
@@ -307,6 +307,39 @@ class TestEc2(test_helpers.HttprettyTestCase):
@httpretty.activate
@mock.patch('cloudinit.net.dhcp.maybe_perform_dhcp_discovery')
+ def test_network_config_cached_property_refreshed_on_upgrade(self, m_dhcp):
+ """Refresh the network_config Ec2 cache if network key is absent.
+
+ This catches an upgrade issue where obj.pkl contained stale metadata
+ which lacked newly required network key.
+ """
+ old_metadata = copy.deepcopy(DEFAULT_METADATA)
+ old_metadata.pop('network')
+ ds = self._setup_ds(
+ platform_data=self.valid_platform_data,
+ sys_cfg={'datasource': {'Ec2': {'strict_id': True}}},
+ md=old_metadata)
+ self.assertTrue(ds.get_data())
+ # Provide new revision of metadata that contains network data
+ register_mock_metaserver(
+ 'http://169.254.169.254/2009-04-04/meta-data/', DEFAULT_METADATA)
+ mac1 = '06:17:04:d7:26:09' # Defined in DEFAULT_METADATA
+ get_interface_mac_path = (
+ 'cloudinit.sources.DataSourceEc2.net.get_interface_mac')
+ ds.fallback_nic = 'eth9'
+ with mock.patch(get_interface_mac_path) as m_get_interface_mac:
+ m_get_interface_mac.return_value = mac1
+ ds.network_config # Will re-crawl network metadata
+ self.assertIn('Re-crawl of metadata service', self.logs.getvalue())
+ expected = {'version': 1, 'config': [
+ {'mac_address': '06:17:04:d7:26:09',
+ 'name': 'eth9',
+ 'subnets': [{'type': 'dhcp4'}, {'type': 'dhcp6'}],
+ 'type': 'physical'}]}
+ self.assertEqual(expected, ds.network_config)
+
+ @httpretty.activate
+ @mock.patch('cloudinit.net.dhcp.maybe_perform_dhcp_discovery')
def test_valid_platform_with_strict_true(self, m_dhcp):
"""Valid platform data should return true with strict_id true."""
ds = self._setup_ds(
--
1.7.12.4

View File

@ -1,297 +0,0 @@
From eb292c18c3d83b9f7e5d1fd81b0e8aefaab0cc2d Mon Sep 17 00:00:00 2001
From: Chad Smith <chad.smith@canonical.com>
Date: Tue, 31 Oct 2017 12:42:15 -0600
Subject: [PATCH] EC2: Limit network config to fallback nic, fix local-ipv4
only instances.
VPC instances have the option to specific local only IPv4 addresses. Allow
Ec2Datasource to enable dhcp4 on instances even if local-ipv4s is
configured on an instance.
Also limit network_configuration to only the primary (fallback) nic.
LP: #1728152
---
cloudinit/sources/DataSourceEc2.py | 24 +++-
tests/unittests/test_datasource/test_ec2.py | 136 +++++++++++++++++++-
2 files changed, 149 insertions(+), 11 deletions(-)
diff --git a/cloudinit/sources/DataSourceEc2.py b/cloudinit/sources/DataSourceEc2.py
index 41367a8b..0ef22174 100644
--- a/cloudinit/sources/DataSourceEc2.py
+++ b/cloudinit/sources/DataSourceEc2.py
@@ -64,6 +64,9 @@ class DataSourceEc2(sources.DataSource):
# Whether we want to get network configuration from the metadata service.
get_network_metadata = False
+ # Track the discovered fallback nic for use in configuration generation.
+ fallback_nic = None
+
def __init__(self, sys_cfg, distro, paths):
sources.DataSource.__init__(self, sys_cfg, distro, paths)
self.metadata_address = None
@@ -89,16 +92,18 @@ class DataSourceEc2(sources.DataSource):
elif self.cloud_platform == Platforms.NO_EC2_METADATA:
return False
+ self.fallback_nic = net.find_fallback_nic()
if self.get_network_metadata: # Setup networking in init-local stage.
if util.is_FreeBSD():
LOG.debug("FreeBSD doesn't support running dhclient with -sf")
return False
- dhcp_leases = dhcp.maybe_perform_dhcp_discovery()
+ dhcp_leases = dhcp.maybe_perform_dhcp_discovery(self.fallback_nic)
if not dhcp_leases:
# DataSourceEc2Local failed in init-local stage. DataSourceEc2
# will still run in init-network stage.
return False
dhcp_opts = dhcp_leases[-1]
+ self.fallback_nic = dhcp_opts.get('interface')
net_params = {'interface': dhcp_opts.get('interface'),
'ip': dhcp_opts.get('fixed-address'),
'prefix_or_mask': dhcp_opts.get('subnet-mask'),
@@ -297,8 +302,13 @@ class DataSourceEc2(sources.DataSource):
result = None
net_md = self.metadata.get('network')
+ # Limit network configuration to only the primary/fallback nic
+ macs_to_nics = {
+ net.get_interface_mac(self.fallback_nic): self.fallback_nic}
if isinstance(net_md, dict):
- result = convert_ec2_metadata_network_config(net_md)
+ result = convert_ec2_metadata_network_config(
+ net_md, macs_to_nics=macs_to_nics,
+ fallback_nic=self.fallback_nic)
else:
LOG.warning("unexpected metadata 'network' key not valid: %s",
net_md)
@@ -458,15 +468,18 @@ def _collect_platform_data():
return data
-def convert_ec2_metadata_network_config(network_md, macs_to_nics=None):
+def convert_ec2_metadata_network_config(network_md, macs_to_nics=None,
+ fallback_nic=None):
"""Convert ec2 metadata to network config version 1 data dict.
@param: network_md: 'network' portion of EC2 metadata.
generally formed as {"interfaces": {"macs": {}} where
'macs' is a dictionary with mac address as key and contents like:
{"device-number": "0", "interface-id": "...", "local-ipv4s": ...}
- @param: macs_to_name: Optional dict mac addresses and the nic name. If
+ @param: macs_to_nics: Optional dict of mac addresses and nic names. If
not provided, get_interfaces_by_mac is called to get it from the OS.
+ @param: fallback_nic: Optionally provide the primary nic interface name.
+ This nic will be guaranteed to minimally have a dhcp4 configuration.
@return A dict of network config version 1 based on the metadata and macs.
"""
@@ -480,7 +493,8 @@ def convert_ec2_metadata_network_config(network_md, macs_to_nics=None):
continue # Not a physical nic represented in metadata
nic_cfg = {'type': 'physical', 'name': nic_name, 'subnets': []}
nic_cfg['mac_address'] = mac
- if nic_metadata.get('public-ipv4s'):
+ if (nic_name == fallback_nic or nic_metadata.get('public-ipv4s') or
+ nic_metadata.get('local-ipv4s')):
nic_cfg['subnets'].append({'type': 'dhcp4'})
if nic_metadata.get('ipv6s'):
nic_cfg['subnets'].append({'type': 'dhcp6'})
diff --git a/tests/unittests/test_datasource/test_ec2.py b/tests/unittests/test_datasource/test_ec2.py
index a7301dbf..6af699a6 100644
--- a/tests/unittests/test_datasource/test_ec2.py
+++ b/tests/unittests/test_datasource/test_ec2.py
@@ -51,6 +51,29 @@ DEFAULT_METADATA = {
"vpc-ipv4-cidr-block": "172.31.0.0/16",
"vpc-ipv4-cidr-blocks": "172.31.0.0/16",
"vpc-ipv6-cidr-blocks": "2600:1f16:aeb:b200::/56"
+ },
+ "06:17:04:d7:26:0A": {
+ "device-number": "1", # Only IPv4 local config
+ "interface-id": "eni-e44ef49f",
+ "ipv4-associations": {"": "172.3.3.16"},
+ "ipv6s": "", # No IPv6 config
+ "local-hostname": ("ip-172-3-3-16.us-east-2."
+ "compute.internal"),
+ "local-ipv4s": "172.3.3.16",
+ "mac": "06:17:04:d7:26:0A",
+ "owner-id": "950047163771",
+ "public-hostname": ("ec2-172-3-3-16.us-east-2."
+ "compute.amazonaws.com"),
+ "public-ipv4s": "", # No public ipv4 config
+ "security-group-ids": "sg-5a61d333",
+ "security-groups": "wide-open",
+ "subnet-id": "subnet-20b8565b",
+ "subnet-ipv4-cidr-block": "172.31.16.0/20",
+ "subnet-ipv6-cidr-blocks": "",
+ "vpc-id": "vpc-87e72bee",
+ "vpc-ipv4-cidr-block": "172.31.0.0/16",
+ "vpc-ipv4-cidr-blocks": "172.31.0.0/16",
+ "vpc-ipv6-cidr-blocks": ""
}
}
}
@@ -209,12 +232,20 @@ class TestEc2(test_helpers.HttprettyTestCase):
@httpretty.activate
def test_network_config_property_returns_version_1_network_data(self):
- """network_config property returns network version 1 for metadata."""
+ """network_config property returns network version 1 for metadata.
+
+ Only one device is configured even when multiple exist in metadata.
+ """
ds = self._setup_ds(
platform_data=self.valid_platform_data,
sys_cfg={'datasource': {'Ec2': {'strict_id': True}}},
md=DEFAULT_METADATA)
- ds.get_data()
+ find_fallback_path = (
+ 'cloudinit.sources.DataSourceEc2.net.find_fallback_nic')
+ with mock.patch(find_fallback_path) as m_find_fallback:
+ m_find_fallback.return_value = 'eth9'
+ ds.get_data()
+
mac1 = '06:17:04:d7:26:09' # Defined in DEFAULT_METADATA
expected = {'version': 1, 'config': [
{'mac_address': '06:17:04:d7:26:09', 'name': 'eth9',
@@ -222,9 +253,48 @@ class TestEc2(test_helpers.HttprettyTestCase):
'type': 'physical'}]}
patch_path = (
'cloudinit.sources.DataSourceEc2.net.get_interfaces_by_mac')
+ get_interface_mac_path = (
+ 'cloudinit.sources.DataSourceEc2.net.get_interface_mac')
+ with mock.patch(patch_path) as m_get_interfaces_by_mac:
+ with mock.patch(find_fallback_path) as m_find_fallback:
+ with mock.patch(get_interface_mac_path) as m_get_mac:
+ m_get_interfaces_by_mac.return_value = {mac1: 'eth9'}
+ m_find_fallback.return_value = 'eth9'
+ m_get_mac.return_value = mac1
+ self.assertEqual(expected, ds.network_config)
+
+ @httpretty.activate
+ def test_network_config_property_set_dhcp4_on_private_ipv4(self):
+ """network_config property configures dhcp4 on private ipv4 nics.
+
+ Only one device is configured even when multiple exist in metadata.
+ """
+ ds = self._setup_ds(
+ platform_data=self.valid_platform_data,
+ sys_cfg={'datasource': {'Ec2': {'strict_id': True}}},
+ md=DEFAULT_METADATA)
+ find_fallback_path = (
+ 'cloudinit.sources.DataSourceEc2.net.find_fallback_nic')
+ with mock.patch(find_fallback_path) as m_find_fallback:
+ m_find_fallback.return_value = 'eth9'
+ ds.get_data()
+
+ mac1 = '06:17:04:d7:26:0A' # IPv4 only in DEFAULT_METADATA
+ expected = {'version': 1, 'config': [
+ {'mac_address': '06:17:04:d7:26:0A', 'name': 'eth9',
+ 'subnets': [{'type': 'dhcp4'}],
+ 'type': 'physical'}]}
+ patch_path = (
+ 'cloudinit.sources.DataSourceEc2.net.get_interfaces_by_mac')
+ get_interface_mac_path = (
+ 'cloudinit.sources.DataSourceEc2.net.get_interface_mac')
with mock.patch(patch_path) as m_get_interfaces_by_mac:
- m_get_interfaces_by_mac.return_value = {mac1: 'eth9'}
- self.assertEqual(expected, ds.network_config)
+ with mock.patch(find_fallback_path) as m_find_fallback:
+ with mock.patch(get_interface_mac_path) as m_get_mac:
+ m_get_interfaces_by_mac.return_value = {mac1: 'eth9'}
+ m_find_fallback.return_value = 'eth9'
+ m_get_mac.return_value = mac1
+ self.assertEqual(expected, ds.network_config)
def test_network_config_property_is_cached_in_datasource(self):
"""network_config property is cached in DataSourceEc2."""
@@ -321,9 +391,11 @@ class TestEc2(test_helpers.HttprettyTestCase):
@httpretty.activate
@mock.patch('cloudinit.net.EphemeralIPv4Network')
+ @mock.patch('cloudinit.net.find_fallback_nic')
@mock.patch('cloudinit.net.dhcp.maybe_perform_dhcp_discovery')
@mock.patch('cloudinit.sources.DataSourceEc2.util.is_FreeBSD')
- def test_ec2_local_performs_dhcp_on_non_bsd(self, m_is_bsd, m_dhcp, m_net):
+ def test_ec2_local_performs_dhcp_on_non_bsd(self, m_is_bsd, m_dhcp,
+ m_fallback_nic, m_net):
"""Ec2Local returns True for valid platform data on non-BSD with dhcp.
DataSourceEc2Local will setup initial IPv4 network via dhcp discovery.
@@ -331,6 +403,7 @@ class TestEc2(test_helpers.HttprettyTestCase):
When the platform data is valid, return True.
"""
+ m_fallback_nic.return_value = 'eth9'
m_is_bsd.return_value = False
m_dhcp.return_value = [{
'interface': 'eth9', 'fixed-address': '192.168.2.9',
@@ -344,7 +417,7 @@ class TestEc2(test_helpers.HttprettyTestCase):
ret = ds.get_data()
self.assertTrue(ret)
- m_dhcp.assert_called_once_with()
+ m_dhcp.assert_called_once_with('eth9')
m_net.assert_called_once_with(
broadcast='192.168.2.255', interface='eth9', ip='192.168.2.9',
prefix_or_mask='255.255.255.0', router='192.168.2.1')
@@ -389,6 +462,57 @@ class TestConvertEc2MetadataNetworkConfig(test_helpers.CiTestCase):
ec2.convert_ec2_metadata_network_config(
network_metadata_ipv6, macs_to_nics))
+ def test_convert_ec2_metadata_network_config_handles_local_dhcp4(self):
+ """Config dhcp4 when there are no public addresses in public-ipv4s."""
+ macs_to_nics = {self.mac1: 'eth9'}
+ network_metadata_ipv6 = copy.deepcopy(self.network_metadata)
+ nic1_metadata = (
+ network_metadata_ipv6['interfaces']['macs'][self.mac1])
+ nic1_metadata['local-ipv4s'] = '172.3.3.15'
+ nic1_metadata.pop('public-ipv4s')
+ expected = {'version': 1, 'config': [
+ {'mac_address': self.mac1, 'type': 'physical',
+ 'name': 'eth9', 'subnets': [{'type': 'dhcp4'}]}]}
+ self.assertEqual(
+ expected,
+ ec2.convert_ec2_metadata_network_config(
+ network_metadata_ipv6, macs_to_nics))
+
+ def test_convert_ec2_metadata_network_config_handles_absent_dhcp4(self):
+ """Config dhcp4 on fallback_nic when there are no ipv4 addresses."""
+ macs_to_nics = {self.mac1: 'eth9'}
+ network_metadata_ipv6 = copy.deepcopy(self.network_metadata)
+ nic1_metadata = (
+ network_metadata_ipv6['interfaces']['macs'][self.mac1])
+ nic1_metadata['public-ipv4s'] = ''
+
+ # When no ipv4 or ipv6 content but fallback_nic set, set dhcp4 config.
+ expected = {'version': 1, 'config': [
+ {'mac_address': self.mac1, 'type': 'physical',
+ 'name': 'eth9', 'subnets': [{'type': 'dhcp4'}]}]}
+ self.assertEqual(
+ expected,
+ ec2.convert_ec2_metadata_network_config(
+ network_metadata_ipv6, macs_to_nics, fallback_nic='eth9'))
+
+ def test_convert_ec2_metadata_network_config_handles_local_v4_and_v6(self):
+ """When dhcp6 is public and dhcp4 is set to local enable both."""
+ macs_to_nics = {self.mac1: 'eth9'}
+ network_metadata_both = copy.deepcopy(self.network_metadata)
+ nic1_metadata = (
+ network_metadata_both['interfaces']['macs'][self.mac1])
+ nic1_metadata['ipv6s'] = '2620:0:1009:fd00:e442:c88d:c04d:dc85/64'
+ nic1_metadata.pop('public-ipv4s')
+ nic1_metadata['local-ipv4s'] = '10.0.0.42' # Local ipv4 only on vpc
+ expected = {'version': 1, 'config': [
+ {'mac_address': self.mac1, 'type': 'physical',
+ 'name': 'eth9',
+ 'subnets': [{'type': 'dhcp4'}, {'type': 'dhcp6'}]}]}
+ self.assertEqual(
+ expected,
+ ec2.convert_ec2_metadata_network_config(
+ network_metadata_both, macs_to_nics))
+
def test_convert_ec2_metadata_network_config_handles_dhcp4_and_dhcp6(self):
"""Config both dhcp4 and dhcp6 when both vpc-ipv6 and ipv4 exists."""
macs_to_nics = {self.mac1: 'eth9'}
--
2.19.1

View File

@ -1,89 +0,0 @@
From 45289a00bf8c043c5783c527c4ea720e67e0524b Mon Sep 17 00:00:00 2001
From: Tatiana Kholkina <holkina@selectel.ru>
Date: Thu, 1 Feb 2018 18:08:15 +0300
Subject: [PATCH 092/354] Fix ssh keys validation in ssh_util
This fixes a bug where invalid keys would sneak into authorized_keys.
---
cloudinit/ssh_util.py | 5 +----
tests/unittests/test_sshutil.py | 42 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 43 insertions(+), 4 deletions(-)
diff --git a/cloudinit/ssh_util.py b/cloudinit/ssh_util.py
index b95b956..882517f 100644
--- a/cloudinit/ssh_util.py
+++ b/cloudinit/ssh_util.py
@@ -171,16 +171,13 @@ def parse_authorized_keys(fname):
def update_authorized_keys(old_entries, keys):
- to_add = list(keys)
-
+ to_add = list([k for k in keys if k.valid()])
for i in range(0, len(old_entries)):
ent = old_entries[i]
if not ent.valid():
continue
# Replace those with the same base64
for k in keys:
- if not ent.valid():
- continue
if k.base64 == ent.base64:
# Replace it with our better one
ent = k
diff --git a/tests/unittests/test_sshutil.py b/tests/unittests/test_sshutil.py
index 2a8e6ab..4c62c8b 100644
--- a/tests/unittests/test_sshutil.py
+++ b/tests/unittests/test_sshutil.py
@@ -126,6 +126,48 @@ class TestAuthKeyLineParser(test_helpers.TestCase):
self.assertFalse(key.valid())
+class TestUpdateAuthorizedKeys(test_helpers.TestCase):
+
+ def test_new_keys_replace(self):
+ """new entries with the same base64 should replace old."""
+ orig_entries = [
+ ' '.join(('rsa', VALID_CONTENT['rsa'], 'orig_comment1')),
+ ' '.join(('dsa', VALID_CONTENT['dsa'], 'orig_comment2'))]
+
+ new_entries = [
+ ' '.join(('rsa', VALID_CONTENT['rsa'], 'new_comment1')), ]
+
+ expected = '\n'.join([new_entries[0], orig_entries[1]]) + '\n'
+
+ parser = ssh_util.AuthKeyLineParser()
+ found = ssh_util.update_authorized_keys(
+ [parser.parse(p) for p in orig_entries],
+ [parser.parse(p) for p in new_entries])
+
+ self.assertEqual(expected, found)
+
+ def test_new_invalid_keys_are_ignored(self):
+ """new entries that are invalid should be skipped."""
+ orig_entries = [
+ ' '.join(('rsa', VALID_CONTENT['rsa'], 'orig_comment1')),
+ ' '.join(('dsa', VALID_CONTENT['dsa'], 'orig_comment2'))]
+
+ new_entries = [
+ ' '.join(('rsa', VALID_CONTENT['rsa'], 'new_comment1')),
+ 'xxx-invalid-thing1',
+ 'xxx-invalid-blob2'
+ ]
+
+ expected = '\n'.join([new_entries[0], orig_entries[1]]) + '\n'
+
+ parser = ssh_util.AuthKeyLineParser()
+ found = ssh_util.update_authorized_keys(
+ [parser.parse(p) for p in orig_entries],
+ [parser.parse(p) for p in new_entries])
+
+ self.assertEqual(expected, found)
+
+
class TestParseSSHConfig(test_helpers.TestCase):
def setUp(self):
--
1.7.12.4

View File

@ -7,26 +7,26 @@ reason: add variable to forbid temporary directory
Signed-off-by: chengquan <chengquan3@huawei.com> Signed-off-by: chengquan <chengquan3@huawei.com>
--- ---
cloud-init-17.1/setup.py | 14 +++++++++++--- cloud-init-19.4/setup.py | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-) 1 file changed, 11 insertions(+), 3 deletions(-)
diff -Nur cloud-init-17.1/setup.py cloud-init-17.1_bak/setup.py diff -Nur a/setup.py b/setup.py
--- cloud-init-17.1/setup.py 2019-04-12 19:00:20.782000000 +0800 --- a/setup.py 2019-04-12 19:00:20.782000000 +0800
+++ cloud-init-17.1_bak/setup.py 2019-04-12 19:48:04.246000000 +0800 +++ b/setup.py 2019-04-12 19:48:04.246000000 +0800
@@ -86,6 +86,8 @@ @@ -91,6 +91,8 @@
(deps, _e) = tiny_p(cmd) (deps, _e) = tiny_p(cmd)
return str(deps).splitlines() return str(deps).splitlines()
+# add variable to forbid tmp dir +# add variable to forbid tmp dir
+num = 0 +num = 0
def render_tmpl(template): def render_tmpl(template, mode=None):
"""render template into a tmpdir under same dir as setup.py """render template into a tmpdir under same dir as setup.py
@@ -107,7 +109,10 @@ @@ -112,7 +114,10 @@
return template return template
topdir = os.path.dirname(sys.argv[0]) topdir = os.path.dirname(sys.argv[0])
- tmpd = tempfile.mkdtemp(dir=topdir) - tmpd = tempfile.mkdtemp(dir=topdir, prefix=RENDERED_TMPD_PREFIX)
+ global num + global num
+ os.mkdir(topdir + str(num)) + os.mkdir(topdir + str(num))
+ tmpd = os.path.abspath(topdir + str(num)) + tmpd = os.path.abspath(topdir + str(num))
@ -34,25 +34,26 @@ diff -Nur cloud-init-17.1/setup.py cloud-init-17.1_bak/setup.py
atexit.register(shutil.rmtree, tmpd) atexit.register(shutil.rmtree, tmpd)
bname = os.path.basename(template).rstrip(tmpl_ext) bname = os.path.basename(template).rstrip(tmpl_ext)
fpath = os.path.join(tmpd, bname) fpath = os.path.join(tmpd, bname)
@@ -115,6 +120,9 @@ @@ -126,6 +131,10 @@
# return path relative to setup.py # return path relative to setup.py
return os.path.join(os.path.basename(tmpd), bname) return os.path.join(os.path.basename(tmpd), bname)
+def sort_files(file_list): +def sort_files(file_list):
+ file_list.sort() + file_list.sort()
+ return file_list + return file_list
+
INITSYS_FILES = { # User can set the variant for template rendering
'sysvinit': [f for f in glob('sysvinit/redhat/*') if is_f(f)], if '--distro' in sys.argv:
@@ -123,9 +131,9 @@ idx = sys.argv.index('--distro')
@@ -140,9 +149,9 @@
'sysvinit_openrc': [f for f in glob('sysvinit/gentoo/*') if is_f(f)], 'sysvinit_openrc': [f for f in glob('sysvinit/gentoo/*') if is_f(f)],
'sysvinit_suse': [f for f in glob('sysvinit/suse/*') if is_f(f)], 'sysvinit_suse': [f for f in glob('sysvinit/suse/*') if is_f(f)],
'systemd': [render_tmpl(f) 'systemd': [render_tmpl(f)
- for f in (glob('systemd/*.tmpl') + - for f in (glob('systemd/*.tmpl') +
+ for f in sort_files((glob('systemd/*.tmpl') + + for f in sort_files((glob('systemd/*.tmpl') +
glob('systemd/*.service') + glob('systemd/*.service') +
- glob('systemd/*.target')) if is_f(f)], - glob('systemd/*.target'))
+ glob('systemd/*.target'))) if is_f(f)], + glob('systemd/*.target')))
'systemd.generators': [f for f in glob('systemd/*-generator') if is_f(f)], if (is_f(f) and not is_generator(f))],
'upstart': [f for f in glob('upstart/*') if is_f(f)], 'systemd.generators': [
} render_tmpl(f, mode=0o755)

View File

@ -8,35 +8,34 @@ reason: add openEuler into distros
Signed-off-by: chengquan <chengquan3@huawei.com> Signed-off-by: chengquan <chengquan3@huawei.com>
--- ---
.../0001-cloud-init-Update-patch-information.patch | 68 ++++++++++++++++++++++ .../0001-cloud-init-Update-patch-information.patch | 68 ++++++++++++++++++++++
cloud-init-17.1/cloudinit/config/cc_ntp.py | 2 +- cloud-init-19.4/cloudinit/config/cc_ntp.py | 2 +-
cloud-init-17.1/cloudinit/config/cc_resolv_conf.py | 2 +- cloud-init-19.4/cloudinit/config/cc_resolv_conf.py | 2 +-
.../cloudinit/config/cc_rh_subscription.py | 2 +- cloud-init-19.4/cloudinit/config/cc_rh_subscription.py | 2 +-
cloud-init-17.1/cloudinit/config/cc_spacewalk.py | 2 +- cloud-init-19.4/cloudinit/config/cc_spacewalk.py | 2 +-
.../cloudinit/config/cc_yum_add_repo.py | 2 +- .../cloudinit/config/cc_yum_add_repo.py | 2 +-
cloud-init-17.1/cloudinit/distros/__init__.py | 2 +- cloud-init-19.4/cloudinit/distros/__init__.py | 2 +-
cloud-init-17.1/cloudinit/distros/openEuler.py | 12 ++++ cloud-init-19.4/cloudinit/distros/openEuler.py | 12 ++++
cloud-init-17.1/cloudinit/util.py | 2 +- cloud-init-19.4/cloudinit/util.py | 2 +-
cloud-init-17.1/config/cloud.cfg.tmpl | 8 +-- cloud-init-19.4/config/cloud.cfg.tmpl | 8 +--
cloud-init-17.1/systemd/cloud-init.service.tmpl | 2 +- cloud-init-19.4/systemd/cloud-init.service.tmpl | 2 +-
cloud-init-17.1/tests/cloud_tests/util.py | 2 +- cloud-init-19.4/tests/cloud_tests/util.py | 2 +-
.../unittests/test_handler/test_handler_ntp.py | 2 +- cloud-init-19.4/tools/render-cloudcfg | 2 +-
cloud-init-17.1/tools/render-cloudcfg | 2 +-
14 files changed, 95 insertions(+), 15 deletions(-) 14 files changed, 95 insertions(+), 15 deletions(-)
create mode 100644 cloud-init-17.1/cloudinit/distros/openEuler.py create mode 100644 cloud-init-19.4/cloudinit/distros/openEuler.py
diff --git a/cloudinit/config/cc_ntp.py b/cloudinit/config/cc_ntp.py diff --git a/cloudinit/config/cc_ntp.py b/cloudinit/config/cc_ntp.py
index d43d060..4f14c10 100644 index d43d060..4f14c10 100644
--- a/cloudinit/config/cc_ntp.py --- a/cloudinit/config/cc_ntp.py
+++ b/cloudinit/config/cc_ntp.py +++ b/cloudinit/config/cc_ntp.py
@@ -23,7 +23,7 @@ frequency = PER_INSTANCE @@ -23,7 +23,7 @@ LOG = logging.getLogger(_name_)
frequency = PER_INSTANCE
NTP_CONF = '/etc/ntp.conf' NTP_CONF = '/etc/ntp.conf'
TIMESYNCD_CONF = '/etc/systemd/timesyncd.conf.d/cloud-init.conf'
NR_POOL_SERVERS = 4 NR_POOL_SERVERS = 4
-distros = ['centos', 'debian', 'fedora', 'opensuse', 'ubuntu'] -distros = ['centos', 'debian', 'fedora', 'opensuse', 'rhel', 'sles', 'ubuntu']
+distros = ['centos', 'debian', 'fedora', 'opensuse', 'ubuntu', 'openEuler'] +distros = ['centos', 'debian', 'fedora', 'opensuse', 'rhel', 'sles', 'ubuntu', 'openEuler']
NTP_CLIENT_CONFIG = {
# The schema definition for each cloud-config module is a strict contract for 'chrony': {
diff --git a/cloudinit/config/cc_resolv_conf.py b/cloudinit/config/cc_resolv_conf.py diff --git a/cloudinit/config/cc_resolv_conf.py b/cloudinit/config/cc_resolv_conf.py
index 9812562..973fe2e 100644 index 9812562..973fe2e 100644
--- a/cloudinit/config/cc_resolv_conf.py --- a/cloudinit/config/cc_resolv_conf.py
@ -54,9 +53,9 @@ diff --git a/cloudinit/config/cc_rh_subscription.py b/cloudinit/config/cc_rh_sub
index 7f36cf8..23f3a5a 100644 index 7f36cf8..23f3a5a 100644
--- a/cloudinit/config/cc_rh_subscription.py --- a/cloudinit/config/cc_rh_subscription.py
+++ b/cloudinit/config/cc_rh_subscription.py +++ b/cloudinit/config/cc_rh_subscription.py
@@ -40,7 +40,7 @@ Subscription`` example config. @@ -43,7 +43,7 @@ from cloudinit import util
from cloudinit import util LOG = logging.getLogger(__name__)
-distros = ['fedora', 'rhel'] -distros = ['fedora', 'rhel']
+distros = ['fedora', 'rhel', 'openEuler'] +distros = ['fedora', 'rhel', 'openEuler']
@ -97,8 +96,8 @@ index d5becd1..f6eb899 100755
OSFAMILIES = { OSFAMILIES = {
'debian': ['debian', 'ubuntu'], 'debian': ['debian', 'ubuntu'],
- 'redhat': ['centos', 'fedora', 'rhel'], - 'redhat': ['amazon', 'centos', 'fedora', 'rhel'],
+ 'redhat': ['centos', 'fedora', 'rhel', 'openEuler'], + 'redhat': ['amazon', 'centos', 'fedora', 'rhel', 'openEuler'],
'gentoo': ['gentoo'], 'gentoo': ['gentoo'],
'freebsd': ['freebsd'], 'freebsd': ['freebsd'],
'suse': ['opensuse', 'sles'], 'suse': ['opensuse', 'sles'],
@ -106,12 +105,12 @@ diff --git a/cloudinit/util.py b/cloudinit/util.py
index e1290aa..d85daf0 100644 index e1290aa..d85daf0 100644
--- a/cloudinit/util.py --- a/cloudinit/util.py
+++ b/cloudinit/util.py +++ b/cloudinit/util.py
@@ -592,7 +592,7 @@ def system_info(): @@ -593,7 +593,7 @@ def system_info():
var = 'unknown'
if system == "linux": if system == "linux":
linux_dist = info['dist'][0].lower() linux_dist = info['dist'][0].lower()
- if linux_dist in ('centos', 'fedora', 'debian'): if linux_dist in (
+ if linux_dist in ('centos', 'fedora', 'debian', 'openEuler'): - 'arch', 'centos', 'debian', 'fedora', 'rhel', 'suse'):
+ 'arch', 'centos', 'debian', 'fedora', 'rhel', 'suse', 'openEuler'):
var = linux_dist var = linux_dist
elif linux_dist in ('ubuntu', 'linuxmint', 'mint'): elif linux_dist in ('ubuntu', 'linuxmint', 'mint'):
var = 'ubuntu' var = 'ubuntu'
@ -119,7 +118,7 @@ diff --git a/config/cloud.cfg.tmpl b/config/cloud.cfg.tmpl
index 50e3bd8..e3816f2 100644 index 50e3bd8..e3816f2 100644
--- a/config/cloud.cfg.tmpl --- a/config/cloud.cfg.tmpl
+++ b/config/cloud.cfg.tmpl +++ b/config/cloud.cfg.tmpl
@@ -19,7 +19,7 @@ disable_root: false @@ -21,7 +21,7 @@
disable_root: true disable_root: true
{% endif %} {% endif %}
@ -127,8 +126,8 @@ index 50e3bd8..e3816f2 100644
+{% if variant in ["centos", "fedora", "rhel", "openEuler"] %} +{% if variant in ["centos", "fedora", "rhel", "openEuler"] %}
mount_default_fields: [~, ~, 'auto', 'defaults,nofail', '0', '2'] mount_default_fields: [~, ~, 'auto', 'defaults,nofail', '0', '2']
resize_rootfs_tmp: /dev resize_rootfs_tmp: /dev
ssh_deletekeys: 0 ssh_pwauth: 0
@@ -75,7 +75,7 @@ cloud_config_modules: @@ -76,7 +76,7 @@
- ssh-import-id - ssh-import-id
- locale - locale
- set-passwords - set-passwords
@ -137,21 +136,21 @@ index 50e3bd8..e3816f2 100644
- spacewalk - spacewalk
- yum-add-repo - yum-add-repo
{% endif %} {% endif %}
@@ -127,7 +127,7 @@ cloud_final_modules: @@ -137,7 +137,7 @@
# (not accessible to handlers/transforms) # (not accessible to handlers/transforms)
system_info: system_info:
# This will affect which distro class gets used # This will affect which distro class gets used
-{% if variant in ["centos", "debian", "fedora", "rhel", "suse", "ubuntu", "freebsd"] %} -{% if variant in ["arch", "centos", "debian", "fedora", "freebsd", "rhel", "suse", "ubuntu"] %}
+{% if variant in ["centos", "debian", "fedora", "rhel", "suse", "ubuntu", "freebsd", "openEuler"] %} +{% if variant in ["arch", "centos", "debian", "fedora", "freebsd", "rhel", "suse", "ubuntu", "openEuler"] %}
distro: {{ variant }} distro: {{ variant }}
{% else %} {% else %}
# Unknown/fallback distro. # Unknown/fallback distro.
@@ -163,7 +163,7 @@ system_info: @@ -185,7 +185,7 @@
primary: http://ports.ubuntu.com/ubuntu-ports primary: http://ports.ubuntu.com/ubuntu-ports
security: http://ports.ubuntu.com/ubuntu-ports security: http://ports.ubuntu.com/ubuntu-ports
ssh_svcname: ssh ssh_svcname: ssh
-{% elif variant in ["centos", "rhel", "fedora", "suse"] %} -{% elif variant in ["arch", "centos", "fedora", "rhel", "suse"] %}
+{% elif variant in ["centos", "rhel", "fedora", "suse", "openEuler"] %} +{% elif variant in ["arch", "centos", "fedora", "rhel", "suse", "openEuler"] %}
# Default user name + that default users groups (if added/used) # Default user name + that default users groups (if added/used)
default_user: default_user:
name: {{ variant }} name: {{ variant }}
@ -166,8 +165,8 @@ index b92e8ab..f59d4fd 100644
-{% if variant in ["centos", "fedora", "redhat"] %} -{% if variant in ["centos", "fedora", "redhat"] %}
+{% if variant in ["centos", "fedora", "redhat", "openEuler"] %} +{% if variant in ["centos", "fedora", "redhat", "openEuler"] %}
After=network.service After=network.service
After=NetworkManager.service
{% endif %} {% endif %}
{% if variant in ["suse"] %}
diff --git a/tests/cloud_tests/util.py b/tests/cloud_tests/util.py diff --git a/tests/cloud_tests/util.py b/tests/cloud_tests/util.py
index 4357fbb..7d3034d 100644 index 4357fbb..7d3034d 100644
--- a/tests/cloud_tests/util.py --- a/tests/cloud_tests/util.py
@ -181,29 +180,16 @@ index 4357fbb..7d3034d 100644
'gentoo': ['gentoo'], 'gentoo': ['gentoo'],
'freebsd': ['freebsd'], 'freebsd': ['freebsd'],
'suse': ['sles'], 'suse': ['sles'],
diff --git a/tests/unittests/test_handler/test_handler_ntp.py b/tests/unittests/test_handler/test_handler_ntp.py
index 3abe578..78548cc 100644
--- a/tests/unittests/test_handler/test_handler_ntp.py
+++ b/tests/unittests/test_handler/test_handler_ntp.py
@@ -258,7 +258,7 @@ class TestNtp(FilesystemMockingTestCase):
}
}
ntp_conf = self.tmp_path('ntp.conf', self.new_root) # Doesn't exist
- for distro in ('debian', 'ubuntu', 'fedora', 'rhel', 'sles'):
+ for distro in ('debian', 'ubuntu', 'fedora', 'rhel', 'sles', 'openEuler'):
mycloud = self._get_cloud(distro)
root_dir = dirname(dirname(os.path.realpath(util.__file__)))
tmpl_file = os.path.join(
diff --git a/tools/render-cloudcfg b/tools/render-cloudcfg diff --git a/tools/render-cloudcfg b/tools/render-cloudcfg
index 91d074b..7a8a2c4 100755 index 91d074b..7a8a2c4 100755
--- a/tools/render-cloudcfg --- a/tools/render-cloudcfg
+++ b/tools/render-cloudcfg +++ b/tools/render-cloudcfg
@@ -4,7 +4,7 @@ import argparse @@ -5,7 +5,7 @@ import argparse
import os
import sys import sys
-VARIANTS = ["bsd", "centos", "fedora", "rhel", "suse", "ubuntu", "unknown"] VARIANTS = ["arch", "centos", "debian", "fedora", "freebsd", "rhel", "suse",
+VARIANTS = ["bsd", "centos", "fedora", "rhel", "suse", "ubuntu", "unknown", "openEuler"] - "ubuntu", "unknown"]
+ "ubuntu", "unknown", "openEuler"]
if "avoid-pep8-E402-import-not-top-of-file": if "avoid-pep8-E402-import-not-top-of-file":
_tdir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) _tdir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))

View File

@ -7,13 +7,13 @@ reason: sort requirements in setup
Signed-off-by: chengquan <chengquan3@huawei.com> Signed-off-by: chengquan <chengquan3@huawei.com>
--- ---
cloud-init-17.1/setup.py | 1 + cloud-init-19.4/setup.py | 1 +
1 file changed, 1 insertion(+) 1 file changed, 1 insertion(+)
diff -Nur cloud-init-17.1_bak/setup.py cloud-init-17.1/setup.py diff -Nur a/setup.py b/setup.py
--- cloud-init-17.1_bak/setup.py 2019-04-11 20:27:41.526622810 +0800 --- a/setup.py 2019-04-11 20:27:41.526622810 +0800
+++ cloud-init-17.1/setup.py 2019-04-11 20:28:21.734622815 +0800 +++ b/setup.py 2019-04-11 20:28:21.734622815 +0800
@@ -232,6 +232,7 @@ @@ -289,6 +289,7 @@
} }
requirements = read_requires() requirements = read_requires()
@ -21,4 +21,3 @@ diff -Nur cloud-init-17.1_bak/setup.py cloud-init-17.1/setup.py
setuptools.setup( setuptools.setup(
name='cloud-init', name='cloud-init',

View File

@ -1,17 +0,0 @@
Index: cloud-init-17.1/tests/cloud_tests/platforms/__init__.py
===================================================================
--- cloud-init-17.1.orig/tests/cloud_tests/platforms/__init__.py
+++ cloud-init-17.1/tests/cloud_tests/platforms/__init__.py
@@ -2,12 +2,10 @@
"""Main init."""
-from tests.cloud_tests.platforms import lxd
from tests.cloud_tests.platforms import nocloudkvm
PLATFORMS = {
'nocloud-kvm': nocloudkvm.NoCloudKVMPlatform,
- 'lxd': lxd.LXDPlatform,
}

View File

@ -1,291 +0,0 @@
Index: cloud-init-17.1/cloudinit/net/sysconfig.py
===================================================================
--- cloud-init-17.1.orig/cloudinit/net/sysconfig.py
+++ cloud-init-17.1/cloudinit/net/sysconfig.py
@@ -230,7 +230,6 @@ class Renderer(renderer.Renderer):
iface_defaults = tuple([
('ONBOOT', True),
('USERCTL', False),
- ('NM_CONTROLLED', False),
('BOOTPROTO', 'none'),
])
Index: cloud-init-17.1/tests/unittests/test_net.py
===================================================================
--- cloud-init-17.1.orig/tests/unittests/test_net.py
+++ cloud-init-17.1/tests/unittests/test_net.py
@@ -146,7 +146,6 @@ GATEWAY=172.19.3.254
HWADDR=fa:16:3e:ed:9a:59
IPADDR=172.19.1.34
NETMASK=255.255.252.0
-NM_CONTROLLED=no
ONBOOT=yes
TYPE=Ethernet
USERCTL=no
@@ -214,7 +213,6 @@ IPADDR=172.19.1.34
IPADDR1=10.0.0.10
NETMASK=255.255.252.0
NETMASK1=255.255.255.0
-NM_CONTROLLED=no
ONBOOT=yes
TYPE=Ethernet
USERCTL=no
@@ -304,7 +302,6 @@ IPV6ADDR_SECONDARIES="2001:DB9::10/64 20
IPV6INIT=yes
IPV6_DEFAULTGW=2001:DB8::1
NETMASK=255.255.252.0
-NM_CONTROLLED=no
ONBOOT=yes
TYPE=Ethernet
USERCTL=no
@@ -428,7 +425,6 @@ NETWORK_CONFIGS = {
BOOTPROTO=none
DEVICE=eth1
HWADDR=cf:d6:af:48:e8:80
- NM_CONTROLLED=no
ONBOOT=yes
TYPE=Ethernet
USERCTL=no"""),
@@ -440,7 +436,6 @@ NETWORK_CONFIGS = {
HWADDR=c0:d6:9f:2c:e8:80
IPADDR=192.168.21.3
NETMASK=255.255.255.0
- NM_CONTROLLED=no
ONBOOT=yes
TYPE=Ethernet
USERCTL=no"""),
@@ -552,7 +547,6 @@ NETWORK_CONFIGS = {
IPV6ADDR=2001:1::1/64
IPV6INIT=yes
NETMASK=255.255.255.0
- NM_CONTROLLED=no
ONBOOT=yes
TYPE=Ethernet
USERCTL=no
@@ -794,14 +788,12 @@ pre-down route del -net 10.0.0.0 netmask
DHCPV6C=yes
IPV6INIT=yes
MACADDR=aa:bb:cc:dd:ee:ff
- NM_CONTROLLED=no
ONBOOT=yes
TYPE=Bond
USERCTL=no"""),
'ifcfg-bond0.200': textwrap.dedent("""\
BOOTPROTO=dhcp
DEVICE=bond0.200
- NM_CONTROLLED=no
ONBOOT=yes
PHYSDEV=bond0
TYPE=Ethernet
@@ -817,7 +809,6 @@ pre-down route del -net 10.0.0.0 netmask
IPV6INIT=yes
IPV6_DEFAULTGW=2001:4800:78ff:1b::1
NETMASK=255.255.255.0
- NM_CONTROLLED=no
ONBOOT=yes
PRIO=22
STP=off
@@ -827,7 +818,6 @@ pre-down route del -net 10.0.0.0 netmask
BOOTPROTO=none
DEVICE=eth0
HWADDR=c0:d6:9f:2c:e8:80
- NM_CONTROLLED=no
ONBOOT=yes
TYPE=Ethernet
USERCTL=no"""),
@@ -841,7 +831,6 @@ pre-down route del -net 10.0.0.0 netmask
MTU=1500
NETMASK=255.255.255.0
NETMASK1=255.255.255.0
- NM_CONTROLLED=no
ONBOOT=yes
PHYSDEV=eth0
TYPE=Ethernet
@@ -852,7 +841,6 @@ pre-down route del -net 10.0.0.0 netmask
DEVICE=eth1
HWADDR=aa:d6:9f:2c:e8:80
MASTER=bond0
- NM_CONTROLLED=no
ONBOOT=yes
SLAVE=yes
TYPE=Ethernet
@@ -862,7 +850,6 @@ pre-down route del -net 10.0.0.0 netmask
DEVICE=eth2
HWADDR=c0:bb:9f:2c:e8:80
MASTER=bond0
- NM_CONTROLLED=no
ONBOOT=yes
SLAVE=yes
TYPE=Ethernet
@@ -872,7 +859,6 @@ pre-down route del -net 10.0.0.0 netmask
BRIDGE=br0
DEVICE=eth3
HWADDR=66:bb:9f:2c:e8:80
- NM_CONTROLLED=no
ONBOOT=yes
TYPE=Ethernet
USERCTL=no"""),
@@ -881,7 +867,6 @@ pre-down route del -net 10.0.0.0 netmask
BRIDGE=br0
DEVICE=eth4
HWADDR=98:bb:9f:2c:e8:80
- NM_CONTROLLED=no
ONBOOT=yes
TYPE=Ethernet
USERCTL=no"""),
@@ -889,7 +874,6 @@ pre-down route del -net 10.0.0.0 netmask
BOOTPROTO=dhcp
DEVICE=eth5
HWADDR=98:bb:9f:2c:e8:8a
- NM_CONTROLLED=no
ONBOOT=no
TYPE=Ethernet
USERCTL=no""")
@@ -1171,7 +1155,6 @@ pre-down route del -net 10.0.0.0 netmask
IPV6INIT=yes
NETMASK=255.255.255.0
NETMASK1=255.255.255.0
- NM_CONTROLLED=no
ONBOOT=yes
TYPE=Bond
USERCTL=no
@@ -1181,7 +1164,6 @@ pre-down route del -net 10.0.0.0 netmask
DEVICE=bond0s0
HWADDR=aa:bb:cc:dd:e8:00
MASTER=bond0
- NM_CONTROLLED=no
ONBOOT=yes
SLAVE=yes
TYPE=Ethernet
@@ -1199,7 +1181,6 @@ pre-down route del -net 10.0.0.0 netmask
DEVICE=bond0s1
HWADDR=aa:bb:cc:dd:e8:01
MASTER=bond0
- NM_CONTROLLED=no
ONBOOT=yes
SLAVE=yes
TYPE=Ethernet
@@ -1236,7 +1217,6 @@ pre-down route del -net 10.0.0.0 netmask
BOOTPROTO=none
DEVICE=en0
HWADDR=aa:bb:cc:dd:e8:00
- NM_CONTROLLED=no
ONBOOT=yes
TYPE=Ethernet
USERCTL=no"""),
@@ -1252,7 +1232,6 @@ pre-down route del -net 10.0.0.0 netmask
IPV6_DEFAULTGW=2001:1::1
NETMASK=255.255.255.0
NETMASK1=255.255.255.0
- NM_CONTROLLED=no
ONBOOT=yes
PHYSDEV=en0
TYPE=Ethernet
@@ -1293,7 +1272,6 @@ pre-down route del -net 10.0.0.0 netmask
DEVICE=br0
IPADDR=192.168.2.2
NETMASK=255.255.255.0
- NM_CONTROLLED=no
ONBOOT=yes
PRIO=22
STP=off
@@ -1307,7 +1285,6 @@ pre-down route del -net 10.0.0.0 netmask
HWADDR=52:54:00:12:34:00
IPV6ADDR=2001:1::100/96
IPV6INIT=yes
- NM_CONTROLLED=no
ONBOOT=yes
TYPE=Ethernet
USERCTL=no
@@ -1319,7 +1296,6 @@ pre-down route del -net 10.0.0.0 netmask
HWADDR=52:54:00:12:34:01
IPV6ADDR=2001:1::101/96
IPV6INIT=yes
- NM_CONTROLLED=no
ONBOOT=yes
TYPE=Ethernet
USERCTL=no
@@ -1393,7 +1369,6 @@ pre-down route del -net 10.0.0.0 netmask
HWADDR=52:54:00:12:34:00
IPADDR=192.168.1.2
NETMASK=255.255.255.0
- NM_CONTROLLED=no
ONBOOT=no
TYPE=Ethernet
USERCTL=no
@@ -1403,7 +1378,6 @@ pre-down route del -net 10.0.0.0 netmask
DEVICE=eth1
HWADDR=52:54:00:12:34:aa
MTU=1480
- NM_CONTROLLED=no
ONBOOT=yes
TYPE=Ethernet
USERCTL=no
@@ -1412,7 +1386,6 @@ pre-down route del -net 10.0.0.0 netmask
BOOTPROTO=none
DEVICE=eth2
HWADDR=52:54:00:12:34:ff
- NM_CONTROLLED=no
ONBOOT=no
TYPE=Ethernet
USERCTL=no
@@ -1685,7 +1658,6 @@ class TestSysConfigRendering(CiTestCase)
BOOTPROTO=dhcp
DEVICE=eth1000
HWADDR=07-1C-C6-75-A4-BE
-NM_CONTROLLED=no
ONBOOT=yes
TYPE=Ethernet
USERCTL=no
@@ -1805,7 +1777,6 @@ GATEWAY=10.0.2.2
HWADDR=52:54:00:12:34:00
IPADDR=10.0.2.15
NETMASK=255.255.255.0
-NM_CONTROLLED=no
ONBOOT=yes
TYPE=Ethernet
USERCTL=no
@@ -1826,7 +1797,6 @@ USERCTL=no
#
BOOTPROTO=dhcp
DEVICE=eth0
-NM_CONTROLLED=no
ONBOOT=yes
TYPE=Ethernet
USERCTL=no
Index: cloud-init-17.1/tests/unittests/test_distros/test_netconfig.py
===================================================================
--- cloud-init-17.1.orig/tests/unittests/test_distros/test_netconfig.py
+++ cloud-init-17.1/tests/unittests/test_distros/test_netconfig.py
@@ -481,7 +481,6 @@ DEVICE=eth0
GATEWAY=192.168.1.254
IPADDR=192.168.1.5
NETMASK=255.255.255.0
-NM_CONTROLLED=no
ONBOOT=yes
TYPE=Ethernet
USERCTL=no
@@ -498,7 +497,6 @@ USERCTL=no
#
BOOTPROTO=dhcp
DEVICE=eth1
-NM_CONTROLLED=no
ONBOOT=yes
TYPE=Ethernet
USERCTL=no
@@ -632,7 +630,6 @@ DEVICE=eth0
IPV6ADDR=2607:f0d0:1002:0011::2/64
IPV6INIT=yes
IPV6_DEFAULTGW=2607:f0d0:1002:0011::1
-NM_CONTROLLED=no
ONBOOT=yes
TYPE=Ethernet
USERCTL=no
@@ -647,7 +644,6 @@ USERCTL=no
#
BOOTPROTO=dhcp
DEVICE=eth1
-NM_CONTROLLED=no
ONBOOT=yes
TYPE=Ethernet
USERCTL=no

View File

@ -1,200 +0,0 @@
diff -rup cloud-init-17.1.orig/cloudinit/net/sysconfig.py cloud-init-17.1/cloudinit/net/sysconfig.py
--- cloud-init-17.1.orig/cloudinit/net/sysconfig.py 2018-03-26 19:22:35.693111559 +0200
+++ cloud-init-17.1/cloudinit/net/sysconfig.py 2018-03-26 23:47:41.424803588 +0200
@@ -586,7 +586,17 @@ class Renderer(renderer.Renderer):
# always write /etc/sysconfig/network configuration
sysconfig_path = util.target_path(target, "etc/sysconfig/network")
- netcfg = [_make_header(), 'NETWORKING=yes']
+ # Make sure that existing lines, other than overriding ones, remain
+ netcfg = []
+ for line in util.load_file(sysconfig_path, quiet=True).split('\n'):
+ if 'cloud-init' in line:
+ break
+ if not line.startswith(('NETWORKING=',
+ 'IPV6_AUTOCONF=',
+ 'NETWORKING_IPV6=')):
+ netcfg.append(line)
+ # Now generate the cloud-init portion of sysconfig/network
+ netcfg.extend([_make_header(), 'NETWORKING=yes'])
if network_state.use_ipv6:
netcfg.append('NETWORKING_IPV6=yes')
netcfg.append('IPV6_AUTOCONF=no')
diff -rup cloud-init-17.1.orig/tests/unittests/test_distros/test_netconfig.py cloud-init-17.1/tests/unittests/test_distros/test_netconfig.py
--- cloud-init-17.1.orig/tests/unittests/test_distros/test_netconfig.py 2018-03-26 19:22:35.717111557 +0200
+++ cloud-init-17.1/tests/unittests/test_distros/test_netconfig.py 2018-03-26 22:08:25.008717651 +0200
@@ -384,6 +384,82 @@ hn0: flags=8843<UP,BROADCAST,RUNNING,SIM
buf.write(content)
write_bufs[filename] = buf
+ def replace_load_file(filename, *args, **kwargs):
+ if filename == '/etc/sysconfig/network':
+ return 'TEST=yes\nTEST2=yes'
+ else:
+ return ''
+
+ with ExitStack() as mocks:
+ mocks.enter_context(
+ mock.patch.object(util, 'write_file', replace_write))
+ mocks.enter_context(
+ mock.patch.object(util, 'load_file', replace_load_file))
+ mocks.enter_context(
+ mock.patch.object(os.path, 'isfile', return_value=False))
+
+ rh_distro.apply_network(BASE_NET_CFG, False)
+
+ self.assertEqual(len(write_bufs), 4)
+ self.assertIn('/etc/sysconfig/network-scripts/ifcfg-lo',
+ write_bufs)
+ write_buf = write_bufs['/etc/sysconfig/network-scripts/ifcfg-lo']
+ expected_buf = '''
+DEVICE="lo"
+ONBOOT=yes
+'''
+ self.assertCfgEquals(expected_buf, str(write_buf))
+ self.assertEqual(write_buf.mode, 0o644)
+
+ self.assertIn('/etc/sysconfig/network-scripts/ifcfg-eth0',
+ write_bufs)
+ write_buf = write_bufs['/etc/sysconfig/network-scripts/ifcfg-eth0']
+ expected_buf = '''
+DEVICE="eth0"
+BOOTPROTO="static"
+NETMASK="255.255.255.0"
+IPADDR="192.168.1.5"
+ONBOOT=yes
+GATEWAY="192.168.1.254"
+BROADCAST="192.168.1.0"
+'''
+ self.assertCfgEquals(expected_buf, str(write_buf))
+ self.assertEqual(write_buf.mode, 0o644)
+
+ self.assertIn('/etc/sysconfig/network-scripts/ifcfg-eth1',
+ write_bufs)
+ write_buf = write_bufs['/etc/sysconfig/network-scripts/ifcfg-eth1']
+ expected_buf = '''
+DEVICE="eth1"
+BOOTPROTO="dhcp"
+ONBOOT=yes
+'''
+ self.assertCfgEquals(expected_buf, str(write_buf))
+ self.assertEqual(write_buf.mode, 0o644)
+
+ self.assertIn('/etc/sysconfig/network', write_bufs)
+ write_buf = write_bufs['/etc/sysconfig/network']
+ expected_buf = '''
+# Created by cloud-init v. 0.7
+NETWORKING=yes
+TEST=yes
+TEST2=yes
+'''
+ self.assertCfgEquals(expected_buf, str(write_buf))
+ self.assertEqual(write_buf.mode, 0o644)
+
+ def test_simple_write_rh_no_extra(self):
+ rh_distro = self._get_distro('rhel')
+
+ write_bufs = {}
+
+ def replace_write(filename, content, mode=0o644, omode="wb"):
+ buf = WriteBuffer()
+ buf.mode = mode
+ buf.omode = omode
+ buf.write(content)
+ write_bufs[filename] = buf
+
with ExitStack() as mocks:
mocks.enter_context(
mock.patch.object(util, 'write_file', replace_write))
@@ -453,6 +529,12 @@ NETWORKING=yes
buf.write(content)
write_bufs[filename] = buf
+ def replace_load_file(filename, *args, **kwargs):
+ if filename == '/etc/sysconfig/network':
+ return 'TEST=yes\nTEST2=yes'
+ else:
+ return ''
+
with ExitStack() as mocks:
# sysconfig availability checks
mocks.enter_context(
@@ -460,7 +542,7 @@ NETWORKING=yes
mocks.enter_context(
mock.patch.object(util, 'write_file', replace_write))
mocks.enter_context(
- mock.patch.object(util, 'load_file', return_value=''))
+ mock.patch.object(util, 'load_file', replace_load_file))
mocks.enter_context(
mock.patch.object(os.path, 'isfile', return_value=True))
@@ -509,6 +591,8 @@ USERCTL=no
expected_buf = '''
# Created by cloud-init v. 0.7
NETWORKING=yes
+TEST=yes
+TEST2=yes
'''
self.assertCfgEquals(expected_buf, str(write_buf))
self.assertEqual(write_buf.mode, 0o644)
@@ -525,13 +609,20 @@ NETWORKING=yes
buf.write(content)
write_bufs[filename] = buf
+ def replace_load_file(filename, *args, **kwargs):
+ if filename == '/etc/sysconfig/network':
+ return 'TEST=yes\nTEST2=yes'
+ else:
+ return ''
+
with ExitStack() as mocks:
mocks.enter_context(
mock.patch.object(util, 'write_file', replace_write))
mocks.enter_context(
- mock.patch.object(util, 'load_file', return_value=''))
+ mock.patch.object(util, 'load_file', replace_load_file))
mocks.enter_context(
mock.patch.object(os.path, 'isfile', return_value=False))
+
rh_distro.apply_network(BASE_NET_CFG_IPV6, False)
self.assertEqual(len(write_bufs), 4)
@@ -587,6 +678,8 @@ IPV6_DEFAULTGW="2607:f0d0:1002:0011::1"
NETWORKING=yes
NETWORKING_IPV6=yes
IPV6_AUTOCONF=no
+TEST=yes
+TEST2=yes
'''
self.assertCfgEquals(expected_buf, str(write_buf))
self.assertEqual(write_buf.mode, 0o644)
@@ -604,13 +697,19 @@ IPV6_AUTOCONF=no
buf.write(content)
write_bufs[filename] = buf
+ def replace_load_file(filename, *args, **kwargs):
+ if filename == '/etc/sysconfig/network':
+ return 'TEST=yes\nTEST2=yes'
+ else:
+ return ''
+
with ExitStack() as mocks:
mocks.enter_context(
mock.patch.object(util, 'which', return_value=True))
mocks.enter_context(
mock.patch.object(util, 'write_file', replace_write))
mocks.enter_context(
- mock.patch.object(util, 'load_file', return_value=''))
+ mock.patch.object(util, 'load_file', replace_load_file))
mocks.enter_context(
mock.patch.object(os.path, 'isfile', return_value=True))
@@ -658,6 +757,8 @@ USERCTL=no
NETWORKING=yes
NETWORKING_IPV6=yes
IPV6_AUTOCONF=no
+TEST=yes
+TEST2=yes
'''
self.assertCfgEquals(expected_buf, str(write_buf))
self.assertEqual(write_buf.mode, 0o644)

Binary file not shown.

View File

@ -0,0 +1,32 @@
From 1a8143951839afd5b0f80e9d00af582daa429fdc Mon Sep 17 00:00:00 2001
From: Eduardo Otubo <otubo@redhat.com>
Date: Fri, 21 Feb 2020 10:52:26 +0100
Subject: [PATCH] Disable LXD tests
Signed-off-by: Eduardo Otubo <otubo@redhat.com>
---
tests/cloud_tests/platforms/__init__.py | 2 --
1 file changed, 2 deletions(-)
diff --git a/tests/cloud_tests/platforms/__init__.py b/tests/cloud_tests/platforms/__init__.py
index 6a410b84..2076d1c7 100644
--- a/tests/cloud_tests/platforms/__init__.py
+++ b/tests/cloud_tests/platforms/__init__.py
@@ -3,14 +3,12 @@
"""Main init."""
from .ec2 import platform as ec2
-from .lxd import platform as lxd
from .nocloudkvm import platform as nocloudkvm
from .azurecloud import platform as azurecloud
PLATFORMS = {
'ec2': ec2.EC2Platform,
'nocloud-kvm': nocloudkvm.NoCloudKVMPlatform,
- 'lxd': lxd.LXDPlatform,
'azurecloud': azurecloud.AzureCloudPlatform,
}
--
2.17.2

View File

@ -0,0 +1,540 @@
From 98657d64a1d40769b31fcf375ebb1ea0b373350c Mon Sep 17 00:00:00 2001
From: Eduardo Otubo <otubo@redhat.com>
Date: Fri, 21 Feb 2020 11:10:09 +0100
Subject: [PATCH] Do not write NM_CONTROLLED=no in generated interface config
files
Signed-off-by: Eduardo Otubo <otubo@redhat.com>
---
cloudinit/net/sysconfig.py | 1 -
.../unittests/test_distros/test_netconfig.py | 8 ---
tests/unittests/test_net.py | 55 -------------------
3 files changed, 64 deletions(-)
diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
index 310cdf01..8bd7e887 100644
--- a/cloudinit/net/sysconfig.py
+++ b/cloudinit/net/sysconfig.py
@@ -272,7 +272,6 @@ class Renderer(renderer.Renderer):
iface_defaults = tuple([
('ONBOOT', True),
('USERCTL', False),
- ('NM_CONTROLLED', False),
('BOOTPROTO', 'none'),
('STARTMODE', 'auto'),
])
diff --git a/tests/unittests/test_distros/test_netconfig.py b/tests/unittests/test_distros/test_netconfig.py
index 67209955..1df3bfb5 100644
--- a/tests/unittests/test_distros/test_netconfig.py
+++ b/tests/unittests/test_distros/test_netconfig.py
@@ -466,7 +466,6 @@ class TestNetCfgDistroRedhat(TestNetCfgDistroBase):
GATEWAY=192.168.1.254
IPADDR=192.168.1.5
NETMASK=255.255.255.0
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -475,7 +474,6 @@ class TestNetCfgDistroRedhat(TestNetCfgDistroBase):
self.ifcfg_path('eth1'): dedent("""\
BOOTPROTO=dhcp
DEVICE=eth1
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -500,7 +498,6 @@ class TestNetCfgDistroRedhat(TestNetCfgDistroBase):
IPV6ADDR=2607:f0d0:1002:0011::2/64
IPV6INIT=yes
IPV6_DEFAULTGW=2607:f0d0:1002:0011::1
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -509,7 +506,6 @@ class TestNetCfgDistroRedhat(TestNetCfgDistroBase):
self.ifcfg_path('eth1'): dedent("""\
BOOTPROTO=dhcp
DEVICE=eth1
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -562,7 +558,6 @@ class TestNetCfgDistroOpensuse(TestNetCfgDistroBase):
GATEWAY=192.168.1.254
IPADDR=192.168.1.5
NETMASK=255.255.255.0
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -571,7 +566,6 @@ class TestNetCfgDistroOpensuse(TestNetCfgDistroBase):
self.ifcfg_path('eth1'): dedent("""\
BOOTPROTO=dhcp
DEVICE=eth1
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -593,7 +587,6 @@ class TestNetCfgDistroOpensuse(TestNetCfgDistroBase):
IPV6ADDR=2607:f0d0:1002:0011::2/64
IPV6INIT=yes
IPV6_DEFAULTGW=2607:f0d0:1002:0011::1
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -602,7 +595,6 @@ class TestNetCfgDistroOpensuse(TestNetCfgDistroBase):
self.ifcfg_path('eth1'): dedent("""\
BOOTPROTO=dhcp
DEVICE=eth1
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
index 01119e0a..40427461 100644
--- a/tests/unittests/test_net.py
+++ b/tests/unittests/test_net.py
@@ -496,7 +496,6 @@ GATEWAY=172.19.3.254
HWADDR=fa:16:3e:ed:9a:59
IPADDR=172.19.1.34
NETMASK=255.255.252.0
-NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -530,7 +529,6 @@ GATEWAY=172.19.3.254
HWADDR=fa:16:3e:ed:9a:59
IPADDR=172.19.1.34
NETMASK=255.255.252.0
-NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -600,7 +598,6 @@ IPADDR=172.19.1.34
IPADDR1=10.0.0.10
NETMASK=255.255.252.0
NETMASK1=255.255.255.0
-NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -636,7 +633,6 @@ IPADDR=172.19.1.34
IPADDR1=10.0.0.10
NETMASK=255.255.252.0
NETMASK1=255.255.255.0
-NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -731,7 +727,6 @@ IPV6ADDR_SECONDARIES="2001:DB9::10/64 2001:DB10::10/64"
IPV6INIT=yes
IPV6_DEFAULTGW=2001:DB8::1
NETMASK=255.255.252.0
-NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -772,7 +767,6 @@ IPV6ADDR_SECONDARIES="2001:DB9::10/64 2001:DB10::10/64"
IPV6INIT=yes
IPV6_DEFAULTGW=2001:DB8::1
NETMASK=255.255.252.0
-NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -889,7 +883,6 @@ NETWORK_CONFIGS = {
BOOTPROTO=none
DEVICE=eth1
HWADDR=cf:d6:af:48:e8:80
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -907,7 +900,6 @@ NETWORK_CONFIGS = {
IPADDR=192.168.21.3
NETMASK=255.255.255.0
METRIC=10000
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -1022,7 +1014,6 @@ NETWORK_CONFIGS = {
IPV6ADDR=2001:1::1/64
IPV6INIT=yes
NETMASK=255.255.255.0
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -1062,7 +1053,6 @@ NETWORK_CONFIGS = {
DHCPV6C=yes
IPV6INIT=yes
DEVICE=iface0
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -1111,7 +1101,6 @@ NETWORK_CONFIGS = {
IPV6INIT=yes
IPV6_FORCE_ACCEPT_RA=yes
DEVICE=iface0
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -1160,7 +1149,6 @@ NETWORK_CONFIGS = {
IPV6INIT=yes
IPV6_FORCE_ACCEPT_RA=no
DEVICE=iface0
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -1199,7 +1187,6 @@ NETWORK_CONFIGS = {
IPV6_AUTOCONF=yes
IPV6INIT=yes
DEVICE=iface0
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -1240,7 +1227,6 @@ NETWORK_CONFIGS = {
IPV6_AUTOCONF=yes
IPV6INIT=yes
DEVICE=iface0
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -1281,7 +1267,6 @@ NETWORK_CONFIGS = {
IPV6INIT=yes
IPV6_FORCE_ACCEPT_RA=yes
DEVICE=iface0
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -1491,7 +1476,6 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
DHCPV6C=yes
IPV6INIT=yes
MACADDR=aa:bb:cc:dd:ee:ff
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Bond
@@ -1500,7 +1484,6 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
BOOTPROTO=dhcp
DEVICE=bond0.200
DHCLIENT_SET_DEFAULT_ROUTE=no
- NM_CONTROLLED=no
ONBOOT=yes
PHYSDEV=bond0
STARTMODE=auto
@@ -1519,7 +1502,6 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
IPV6_DEFAULTGW=2001:4800:78ff:1b::1
MACADDR=bb:bb:bb:bb:bb:aa
NETMASK=255.255.255.0
- NM_CONTROLLED=no
ONBOOT=yes
PRIO=22
STARTMODE=auto
@@ -1530,7 +1512,6 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
BOOTPROTO=none
DEVICE=eth0
HWADDR=c0:d6:9f:2c:e8:80
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -1548,7 +1529,6 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
MTU=1500
NETMASK=255.255.255.0
NETMASK1=255.255.255.0
- NM_CONTROLLED=no
ONBOOT=yes
PHYSDEV=eth0
STARTMODE=auto
@@ -1560,7 +1540,6 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
DEVICE=eth1
HWADDR=aa:d6:9f:2c:e8:80
MASTER=bond0
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
SLAVE=yes
@@ -1571,7 +1550,6 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
DEVICE=eth2
HWADDR=c0:bb:9f:2c:e8:80
MASTER=bond0
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
SLAVE=yes
@@ -1582,7 +1560,6 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
BRIDGE=br0
DEVICE=eth3
HWADDR=66:bb:9f:2c:e8:80
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -1592,7 +1569,6 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
BRIDGE=br0
DEVICE=eth4
HWADDR=98:bb:9f:2c:e8:80
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -1602,7 +1578,6 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
DEVICE=eth5
DHCLIENT_SET_DEFAULT_ROUTE=no
HWADDR=98:bb:9f:2c:e8:8a
- NM_CONTROLLED=no
ONBOOT=no
STARTMODE=manual
TYPE=Ethernet
@@ -1614,7 +1589,6 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
IPADDR=192.168.200.7
MTU=9000
NETMASK=255.255.255.0
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=InfiniBand
@@ -2027,7 +2001,6 @@ iface bond0 inet6 static
MTU=9000
NETMASK=255.255.255.0
NETMASK1=255.255.255.0
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Bond
@@ -2038,7 +2011,6 @@ iface bond0 inet6 static
DEVICE=bond0s0
HWADDR=aa:bb:cc:dd:e8:00
MASTER=bond0
- NM_CONTROLLED=no
ONBOOT=yes
SLAVE=yes
STARTMODE=auto
@@ -2055,7 +2027,6 @@ iface bond0 inet6 static
DEVICE=bond0s1
HWADDR=aa:bb:cc:dd:e8:01
MASTER=bond0
- NM_CONTROLLED=no
ONBOOT=yes
SLAVE=yes
STARTMODE=auto
@@ -2088,7 +2059,6 @@ iface bond0 inet6 static
MTU=9000
NETMASK=255.255.255.0
NETMASK1=255.255.255.0
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Bond
@@ -2099,7 +2069,6 @@ iface bond0 inet6 static
DEVICE=bond0s0
HWADDR=aa:bb:cc:dd:e8:00
MASTER=bond0
- NM_CONTROLLED=no
ONBOOT=yes
SLAVE=yes
STARTMODE=auto
@@ -2122,7 +2091,6 @@ iface bond0 inet6 static
DEVICE=bond0s1
HWADDR=aa:bb:cc:dd:e8:01
MASTER=bond0
- NM_CONTROLLED=no
ONBOOT=yes
SLAVE=yes
STARTMODE=auto
@@ -2161,7 +2129,6 @@ iface bond0 inet6 static
BOOTPROTO=none
DEVICE=en0
HWADDR=aa:bb:cc:dd:e8:00
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -2180,7 +2147,6 @@ iface bond0 inet6 static
MTU=2222
NETMASK=255.255.255.0
NETMASK1=255.255.255.0
- NM_CONTROLLED=no
ONBOOT=yes
PHYSDEV=en0
STARTMODE=auto
@@ -2222,7 +2188,6 @@ iface bond0 inet6 static
DEVICE=br0
IPADDR=192.168.2.2
NETMASK=255.255.255.0
- NM_CONTROLLED=no
ONBOOT=yes
PRIO=22
STARTMODE=auto
@@ -2238,7 +2203,6 @@ iface bond0 inet6 static
IPADDR6=2001:1::100/96
IPV6ADDR=2001:1::100/96
IPV6INIT=yes
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -2252,7 +2216,6 @@ iface bond0 inet6 static
IPADDR6=2001:1::101/96
IPV6ADDR=2001:1::101/96
IPV6INIT=yes
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -2327,7 +2290,6 @@ iface bond0 inet6 static
HWADDR=52:54:00:12:34:00
IPADDR=192.168.1.2
NETMASK=255.255.255.0
- NM_CONTROLLED=no
ONBOOT=no
STARTMODE=manual
TYPE=Ethernet
@@ -2338,7 +2300,6 @@ iface bond0 inet6 static
DEVICE=eth1
HWADDR=52:54:00:12:34:aa
MTU=1480
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -2348,7 +2309,6 @@ iface bond0 inet6 static
BOOTPROTO=none
DEVICE=eth2
HWADDR=52:54:00:12:34:ff
- NM_CONTROLLED=no
ONBOOT=no
STARTMODE=manual
TYPE=Ethernet
@@ -2766,7 +2726,6 @@ class TestRhelSysConfigRendering(CiTestCase):
BOOTPROTO=dhcp
DEVICE=eth1000
HWADDR=07-1c-c6-75-a4-be
-NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -2888,7 +2847,6 @@ GATEWAY=10.0.2.2
HWADDR=52:54:00:12:34:00
IPADDR=10.0.2.15
NETMASK=255.255.255.0
-NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -2920,7 +2878,6 @@ HWADDR=fa:16:3e:25:b4:59
IPADDR=51.68.89.122
MTU=1500
NETMASK=255.255.240.0
-NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -2935,7 +2892,6 @@ DEVICE=eth1
DHCLIENT_SET_DEFAULT_ROUTE=no
HWADDR=fa:16:3e:b1:ca:29
MTU=9000
-NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -2961,7 +2917,6 @@ USERCTL=no
#
BOOTPROTO=dhcp
DEVICE=eth0
-NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -3168,7 +3123,6 @@ USERCTL=no
IPV6INIT=yes
IPV6_DEFAULTGW=2001:db8::1
NETMASK=255.255.255.0
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -3194,7 +3148,6 @@ USERCTL=no
'ifcfg-eno1': textwrap.dedent("""\
BOOTPROTO=none
DEVICE=eno1
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -3206,7 +3159,6 @@ USERCTL=no
IPADDR=192.6.1.9
MTU=1495
NETMASK=255.255.255.0
- NM_CONTROLLED=no
ONBOOT=yes
PHYSDEV=eno1
STARTMODE=auto
@@ -3238,7 +3190,6 @@ USERCTL=no
IPADDR=10.101.8.65
MTU=1334
NETMASK=255.255.255.192
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Bond
@@ -3249,7 +3200,6 @@ USERCTL=no
BOOTPROTO=none
DEVICE=enp0s0
MASTER=bond0
- NM_CONTROLLED=no
ONBOOT=yes
SLAVE=yes
STARTMODE=auto
@@ -3261,7 +3211,6 @@ USERCTL=no
BOOTPROTO=none
DEVICE=enp0s1
MASTER=bond0
- NM_CONTROLLED=no
ONBOOT=yes
SLAVE=yes
STARTMODE=auto
@@ -3286,7 +3235,6 @@ USERCTL=no
DEVICE=eno1
HWADDR=07-1c-c6-75-a4-be
METRIC=100
- NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -3386,7 +3334,6 @@ class TestOpenSuseSysConfigRendering(CiTestCase):
BOOTPROTO=dhcp
DEVICE=eth1000
HWADDR=07-1c-c6-75-a4-be
-NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -3508,7 +3455,6 @@ GATEWAY=10.0.2.2
HWADDR=52:54:00:12:34:00
IPADDR=10.0.2.15
NETMASK=255.255.255.0
-NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
@@ -3538,7 +3484,6 @@ USERCTL=no
#
BOOTPROTO=dhcp
DEVICE=eth0
-NM_CONTROLLED=no
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
--
2.17.2

View File

@ -0,0 +1,36 @@
From 674873573abf2b6e10b09d533a58437c35d508f8 Mon Sep 17 00:00:00 2001
From: Eduardo Otubo <otubo@redhat.com>
Date: Fri, 21 Feb 2020 11:40:34 +0100
Subject: [PATCH] Don't override default network configuration
Signed-off-by: Eduardo Otubo <otubo@redhat.com>
---
cloudinit/net/sysconfig.py | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
index 310cdf01..87b8f743 100644
--- a/cloudinit/net/sysconfig.py
+++ b/cloudinit/net/sysconfig.py
@@ -755,7 +755,17 @@ class Renderer(renderer.Renderer):
# Distros configuring /etc/sysconfig/network as a file e.g. Centos
if sysconfig_path.endswith('network'):
util.ensure_dir(os.path.dirname(sysconfig_path))
- netcfg = [_make_header(), 'NETWORKING=yes']
+ # Make sure that existing lines, other than overriding ones, remain
+ netcfg = []
+ for line in util.load_file(sysconfig_path, quiet=True).split('\n'):
+ if 'cloud-init' in line:
+ break
+ if not line.startswith(('NETWORKING=',
+ 'IPV6_AUTOCONF=',
+ 'NETWORKING_IPV6=')):
+ netcfg.append(line)
+ # Now generate the cloud-init portion of sysconfig/network
+ netcfg.extend([_make_header(), 'NETWORKING=yes'])
if network_state.use_ipv6:
netcfg.append('NETWORKING_IPV6=yes')
netcfg.append('IPV6_AUTOCONF=no')
--
2.17.2

BIN
cloud-init-19.4.tar.gz Normal file

Binary file not shown.

View File

@ -1,6 +1,6 @@
Name: cloud-init Name: cloud-init
Version: 17.1 Version: 19.4
Release: 13 Release: 1
Summary: the defacto multi-distribution package that handles early initialization of a cloud instance. Summary: the defacto multi-distribution package that handles early initialization of a cloud instance.
License: ASL 2.0 or GPLv3 License: ASL 2.0 or GPLv3
URL: http://launchpad.net/cloud-init URL: http://launchpad.net/cloud-init
@ -8,21 +8,12 @@ Source0: https://launchpad.net/%{name}/trunk/%{version}/+download/%{name}-%{vers
Source1: cloud-init-tmpfiles.conf Source1: cloud-init-tmpfiles.conf
Patch0000: cloud-init-17.1-disable-lxd-tests.patch Patch0: cloud-init-19.4-disable-lxd-tests.patch
Patch0001: cloud-init-17.1-nm-controlled.patch Patch1: cloud-init-19.4-nm-controlled.patch
Patch0002: cloud-init-17.1-no-override-default-network.patch Patch2: cloud-init-19.4-no-override-default-network.patch
Patch0003: EC2-Limit-network-config-to-fallback-nic-fix-local-i.patch Patch3: bugfix-cloud-init-add-openEuler-os.patch
Patch0004: ntp-fix-config-module-schema-to-allow-empty-ntp-conf.patch Patch4: bugfix-sort-requirements.patch
Patch0005: resizefs-Fix-regression-when-system-booted-with-root.patch Patch5: add-variable-to-forbid-tmp-dir.patch
Patch0006: hosts-Fix-openSUSE-and-SLES-setup-for-etc-hosts-and-.patch
Patch0007: EC2-Fix-bug-using-fallback_nic-and-metadata-when-res.patch
Patch0008: Fix-ssh-keys-validation-in-ssh_util.patch
Patch0009: stages-fix-tracebacks-if-a-module-stage-is-undefined.patch
Patch0010: stages-Fix-bug-causing-datasource-to-have-incorrect-.patch
Patch0011: bugfix-cloud-init-add-openEuler-os.patch
Patch0012: bugfix-sort-requirements.patch
Patch0013: add-variable-to-forbid-tmp-dir.patch
Patch0014: util-add-get_linux_distro-function-to-replace-platfo.patch
BuildRequires: pkgconfig(systemd) python3-devel python3-setuptools systemd BuildRequires: pkgconfig(systemd) python3-devel python3-setuptools systemd
BuildRequires: iproute python3-configobj python3-httpretty >= 0.8.14-2 BuildRequires: iproute python3-configobj python3-httpretty >= 0.8.14-2
@ -116,9 +107,11 @@ fi
%{_tmpfilesdir}/%{name}.conf %{_tmpfilesdir}/%{name}.conf
%{_libexecdir}/%{name} %{_libexecdir}/%{name}
%{_bindir}/cloud-init* %{_bindir}/cloud-init*
%{_bindir}/cloud-id
%{python3_sitelib}/* %{python3_sitelib}/*
%dir /run/%{name} %dir /run/%{name}
%dir /var/lib/cloud %dir /var/lib/cloud
%{_datadir}/bash-completion/completions/cloud-init
%files help %files help
%doc doc/* %doc doc/*
@ -127,6 +120,12 @@ fi
%exclude /usr/share/doc/* %exclude /usr/share/doc/*
%changelog %changelog
* Fri Jul 31 2020 Liquor <lirui130@huawei.com> - 19.4-1
- Type:update
- ID:NA
- SUG:NA
- DESC:update to 19.4
* Tue Jun 23 2020 chenditang <chenditang1@huawei.com> - 17.1-13 * Tue Jun 23 2020 chenditang <chenditang1@huawei.com> - 17.1-13
- Type:bugfix - Type:bugfix
- ID:NA - ID:NA

View File

@ -1,172 +0,0 @@
From 22a14a6a6d45ae55d2c2307d7b097eef9863bb0c Mon Sep 17 00:00:00 2001
From: Robert Schweikert <rjschwei@suse.com>
Date: Wed, 8 Nov 2017 15:45:53 -0500
Subject: [PATCH 035/354] hosts: Fix openSUSE and SLES setup for /etc/hosts
and clarify docs.
The etc/hosts file is was not properly setup for openSUSE or SLES
when manage_etc_hosts is set in the config file.
Improve the doc to address the fact that the 'localhost' ip is
distribution dependent (not always 127.0.0.1).
LP: #1731022
---
cloudinit/config/cc_update_etc_hosts.py | 4 +-
templates/hosts.opensuse.tmpl | 26 --------
templates/hosts.suse.tmpl | 10 +++-
.../test_handler/test_handler_etc_hosts.py | 69 ++++++++++++++++++++++
4 files changed, 79 insertions(+), 30 deletions(-)
delete mode 100644 templates/hosts.opensuse.tmpl
create mode 100644 tests/unittests/test_handler/test_handler_etc_hosts.py
diff --git a/cloudinit/config/cc_update_etc_hosts.py b/cloudinit/config/cc_update_etc_hosts.py
index b394784..c96eede 100644
--- a/cloudinit/config/cc_update_etc_hosts.py
+++ b/cloudinit/config/cc_update_etc_hosts.py
@@ -23,8 +23,8 @@ using the template located in ``/etc/cloud/templates/hosts.tmpl``. In the
If ``manage_etc_hosts`` is set to ``localhost``, then cloud-init will not
rewrite ``/etc/hosts`` entirely, but rather will ensure that a entry for the
-fqdn with ip ``127.0.1.1`` is present in ``/etc/hosts`` (i.e.
-``ping <hostname>`` will ping ``127.0.1.1``).
+fqdn with a distribution dependent ip is present in ``/etc/hosts`` (i.e.
+``ping <hostname>`` will ping ``127.0.0.1`` or ``127.0.1.1`` or other ip).
.. note::
if ``manage_etc_hosts`` is set ``true`` or ``template``, the contents
diff --git a/templates/hosts.opensuse.tmpl b/templates/hosts.opensuse.tmpl
deleted file mode 100644
index 655da3f..0000000
--- a/templates/hosts.opensuse.tmpl
+++ /dev/null
@@ -1,26 +0,0 @@
-*
- This file /etc/cloud/templates/hosts.opensuse.tmpl is only utilized
- if enabled in cloud-config. Specifically, in order to enable it
- you need to add the following to config:
- manage_etc_hosts: True
-*#
-# Your system has configured 'manage_etc_hosts' as True.
-# As a result, if you wish for changes to this file to persist
-# then you will need to either
-# a.) make changes to the master file in
-# /etc/cloud/templates/hosts.opensuse.tmpl
-# b.) change or remove the value of 'manage_etc_hosts' in
-# /etc/cloud/cloud.cfg or cloud-config from user-data
-#
-# The following lines are desirable for IPv4 capable hosts
-127.0.0.1 localhost
-
-# The following lines are desirable for IPv6 capable hosts
-::1 localhost ipv6-localhost ipv6-loopback
-fe00::0 ipv6-localnet
-
-ff00::0 ipv6-mcastprefix
-ff02::1 ipv6-allnodes
-ff02::2 ipv6-allrouters
-ff02::3 ipv6-allhosts
-
diff --git a/templates/hosts.suse.tmpl b/templates/hosts.suse.tmpl
index b608269..8e664db 100644
--- a/templates/hosts.suse.tmpl
+++ b/templates/hosts.suse.tmpl
@@ -13,12 +13,18 @@ you need to add the following to config:
# /etc/cloud/cloud.cfg or cloud-config from user-data
#
# The following lines are desirable for IPv4 capable hosts
-127.0.0.1 localhost
+127.0.0.1 {{fqdn}} {{hostname}}
+127.0.0.1 localhost.localdomain localhost
+127.0.0.1 localhost4.localdomain4 localhost4
# The following lines are desirable for IPv6 capable hosts
+::1 {{fqdn}} {{hostname}}
+::1 localhost.localdomain localhost
+::1 localhost6.localdomain6 localhost6
::1 localhost ipv6-localhost ipv6-loopback
-fe00::0 ipv6-localnet
+
+fe00::0 ipv6-localnet
ff00::0 ipv6-mcastprefix
ff02::1 ipv6-allnodes
ff02::2 ipv6-allrouters
diff --git a/tests/unittests/test_handler/test_handler_etc_hosts.py b/tests/unittests/test_handler/test_handler_etc_hosts.py
new file mode 100644
index 0000000..ced05a8
--- /dev/null
+++ b/tests/unittests/test_handler/test_handler_etc_hosts.py
@@ -0,0 +1,69 @@
+# This file is part of cloud-init. See LICENSE file for license information.
+
+from cloudinit.config import cc_update_etc_hosts
+
+from cloudinit import cloud
+from cloudinit import distros
+from cloudinit import helpers
+from cloudinit import util
+
+from cloudinit.tests import helpers as t_help
+
+import logging
+import os
+import shutil
+
+LOG = logging.getLogger(__name__)
+
+
+class TestHostsFile(t_help.FilesystemMockingTestCase):
+ def setUp(self):
+ super(TestHostsFile, self).setUp()
+ self.tmp = self.tmp_dir()
+
+ def _fetch_distro(self, kind):
+ cls = distros.fetch(kind)
+ paths = helpers.Paths({})
+ return cls(kind, {}, paths)
+
+ def test_write_etc_hosts_suse_localhost(self):
+ cfg = {
+ 'manage_etc_hosts': 'localhost',
+ 'hostname': 'cloud-init.test.us'
+ }
+ os.makedirs('%s/etc/' % self.tmp)
+ hosts_content = '192.168.1.1 blah.blah.us blah\n'
+ fout = open('%s/etc/hosts' % self.tmp, 'w')
+ fout.write(hosts_content)
+ fout.close()
+ distro = self._fetch_distro('sles')
+ distro.hosts_fn = '%s/etc/hosts' % self.tmp
+ paths = helpers.Paths({})
+ ds = None
+ cc = cloud.Cloud(ds, paths, {}, distro, None)
+ self.patchUtils(self.tmp)
+ cc_update_etc_hosts.handle('test', cfg, cc, LOG, [])
+ contents = util.load_file('%s/etc/hosts' % self.tmp)
+ if '127.0.0.1\tcloud-init.test.us\tcloud-init' not in contents:
+ self.assertIsNone('No entry for 127.0.0.1 in etc/hosts')
+ if '192.168.1.1\tblah.blah.us\tblah' not in contents:
+ self.assertIsNone('Default etc/hosts content modified')
+
+ def test_write_etc_hosts_suse_template(self):
+ cfg = {
+ 'manage_etc_hosts': 'template',
+ 'hostname': 'cloud-init.test.us'
+ }
+ shutil.copytree('templates', '%s/etc/cloud/templates' % self.tmp)
+ distro = self._fetch_distro('sles')
+ paths = helpers.Paths({})
+ paths.template_tpl = '%s' % self.tmp + '/etc/cloud/templates/%s.tmpl'
+ ds = None
+ cc = cloud.Cloud(ds, paths, {}, distro, None)
+ self.patchUtils(self.tmp)
+ cc_update_etc_hosts.handle('test', cfg, cc, LOG, [])
+ contents = util.load_file('%s/etc/hosts' % self.tmp)
+ if '127.0.0.1 cloud-init.test.us cloud-init' not in contents:
+ self.assertIsNone('No entry for 127.0.0.1 in etc/hosts')
+ if '::1 cloud-init.test.us cloud-init' not in contents:
+ self.assertIsNone('No entry for 127.0.0.1 in etc/hosts')
--
1.7.12.4

View File

@ -1,163 +0,0 @@
From 6bc504e41666329631cdfd5b947ed5b0e2529a76 Mon Sep 17 00:00:00 2001
From: Chad Smith <chad.smith@canonical.com>
Date: Fri, 20 Oct 2017 13:24:22 -0600
Subject: [PATCH 021/354] ntp: fix config module schema to allow empty ntp
config
Fix three things related to the ntp module:
1. Fix invalid cloud-config schema in the integration test which
provided empty dicts instead of emptylists for pools and servers
2. Correct logic in the ntp module to allow support for the minimal
cloud-config 'ntp:' without raising a RuntimeError. Docs and schema
definitions already describe that cloud-config's ntp can be empty.
An ntp configuration with neither pools nor servers will be
configured with a default set of ntp pools. As such, the ntp module
now officially allows the following ntp cloud-configs:
- ntp:
- ntp: {}
- ntp:
servers: []
pools: []
3. Add a simple unit test which validates all cloud-config provided to
our integration tests to ensure it adheres to any defined module
schema so as more jsonschema definitions are added, we validate our
integration test configs.
LP: #1724951
---
cloudinit/config/cc_ntp.py | 4 ++-
tests/cloud_tests/testcases/modules/ntp.yaml | 4 +--
tests/unittests/test_handler/test_handler_ntp.py | 23 ++++++++-------
tests/unittests/test_handler/test_schema.py | 37 +++++++++++++++++++++++-
4 files changed, 53 insertions(+), 15 deletions(-)
diff --git a/cloudinit/config/cc_ntp.py b/cloudinit/config/cc_ntp.py
index 15ae1ec..d43d060 100644
--- a/cloudinit/config/cc_ntp.py
+++ b/cloudinit/config/cc_ntp.py
@@ -100,7 +100,9 @@ def handle(name, cfg, cloud, log, _args):
LOG.debug(
"Skipping module named %s, not present or disabled by cfg", name)
return
- ntp_cfg = cfg.get('ntp', {})
+ ntp_cfg = cfg['ntp']
+ if ntp_cfg is None:
+ ntp_cfg = {} # Allow empty config which will install the package
# TODO drop this when validate_cloudconfig_schema is strict=True
if not isinstance(ntp_cfg, (dict)):
diff --git a/tests/cloud_tests/configs/modules/ntp.yaml b/tests/cloud_tests/configs/modules/ntp.yaml
index fbef431..2530d72 100644
--- a/tests/cloud_tests/configs/modules/ntp.yaml
+++ b/tests/cloud_tests/configs/modules/ntp.yaml
@@ -4,8 +4,8 @@
cloud_config: |
#cloud-config
ntp:
- pools: {}
- servers: {}
+ pools: []
+ servers: []
collect_scripts:
ntp_installed: |
#!/bin/bash
diff --git a/tests/unittests/test_handler/test_handler_ntp.py b/tests/unittests/test_handler/test_handler_ntp.py
index 4f29124..3abe578 100644
--- a/tests/unittests/test_handler/test_handler_ntp.py
+++ b/tests/unittests/test_handler/test_handler_ntp.py
@@ -293,23 +293,24 @@ class TestNtp(FilesystemMockingTestCase):
def test_ntp_handler_schema_validation_allows_empty_ntp_config(self):
"""Ntp schema validation allows for an empty ntp: configuration."""
- invalid_config = {'ntp': {}}
+ valid_empty_configs = [{'ntp': {}}, {'ntp': None}]
distro = 'ubuntu'
cc = self._get_cloud(distro)
ntp_conf = os.path.join(self.new_root, 'ntp.conf')
with open('{0}.tmpl'.format(ntp_conf), 'wb') as stream:
stream.write(NTP_TEMPLATE)
- with mock.patch('cloudinit.config.cc_ntp.NTP_CONF', ntp_conf):
- cc_ntp.handle('cc_ntp', invalid_config, cc, None, [])
+ for valid_empty_config in valid_empty_configs:
+ with mock.patch('cloudinit.config.cc_ntp.NTP_CONF', ntp_conf):
+ cc_ntp.handle('cc_ntp', valid_empty_config, cc, None, [])
+ with open(ntp_conf) as stream:
+ content = stream.read()
+ default_pools = [
+ "{0}.{1}.pool.ntp.org".format(x, distro)
+ for x in range(0, cc_ntp.NR_POOL_SERVERS)]
+ self.assertEqual(
+ "servers []\npools {0}\n".format(default_pools),
+ content)
self.assertNotIn('Invalid config:', self.logs.getvalue())
- with open(ntp_conf) as stream:
- content = stream.read()
- default_pools = [
- "{0}.{1}.pool.ntp.org".format(x, distro)
- for x in range(0, cc_ntp.NR_POOL_SERVERS)]
- self.assertEqual(
- "servers []\npools {0}\n".format(default_pools),
- content)
@skipIf(_missing_jsonschema_dep, "No python-jsonschema dependency")
def test_ntp_handler_schema_validation_warns_non_string_item_type(self):
diff --git a/tests/unittests/test_handler/test_schema.py b/tests/unittests/test_handler/test_schema.py
index b8fc893..648573f 100644
--- a/tests/unittests/test_handler/test_schema.py
+++ b/tests/unittests/test_handler/test_schema.py
@@ -4,11 +4,12 @@ from cloudinit.config.schema import (
CLOUD_CONFIG_HEADER, SchemaValidationError, annotated_cloudconfig_file,
get_schema_doc, get_schema, validate_cloudconfig_file,
validate_cloudconfig_schema, main)
-from cloudinit.util import write_file
+from cloudinit.util import subp, write_file
from cloudinit.tests.helpers import CiTestCase, mock, skipIf
from copy import copy
+import os
from six import StringIO
from textwrap import dedent
from yaml import safe_load
@@ -364,4 +365,38 @@ class MainTest(CiTestCase):
self.assertIn(
'Valid cloud-config file {0}'.format(myyaml), m_stdout.getvalue())
+
+class CloudTestsIntegrationTest(CiTestCase):
+ """Validate all cloud-config yaml schema provided in integration tests.
+
+ It is less expensive to have unittests validate schema of all cloud-config
+ yaml provided to integration tests, than to run an integration test which
+ raises Warnings or errors on invalid cloud-config schema.
+ """
+
+ @skipIf(_missing_jsonschema_dep, "No python-jsonschema dependency")
+ def test_all_integration_test_cloud_config_schema(self):
+ """Validate schema of cloud_tests yaml files looking for warnings."""
+ schema = get_schema()
+ testsdir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
+ integration_testdir = os.path.sep.join(
+ [testsdir, 'cloud_tests', 'testcases'])
+ errors = []
+ out, _ = subp(['find', integration_testdir, '-name', '*yaml'])
+ for filename in out.splitlines():
+ test_cfg = safe_load(open(filename))
+ cloud_config = test_cfg.get('cloud_config')
+ if cloud_config:
+ cloud_config = safe_load(
+ cloud_config.replace("#cloud-config\n", ""))
+ try:
+ validate_cloudconfig_schema(
+ cloud_config, schema, strict=True)
+ except SchemaValidationError as e:
+ errors.append(
+ '{0}: {1}'.format(
+ filename, e))
+ if errors:
+ raise AssertionError(', '.join(errors))
+
# vi: ts=4 expandtab syntax=python
--
1.7.12.4

View File

@ -1,322 +0,0 @@
From 17a15f9e0ae78e4fc4e24fab0caebdf78f06ef66 Mon Sep 17 00:00:00 2001
From: Chad Smith <chad.smith@canonical.com>
Date: Mon, 23 Oct 2017 14:34:23 -0600
Subject: [PATCH 025/354] resizefs: Fix regression when system booted with
root=PARTUUID=
A recent cleanup of the resizefs module broke resizing when a system was
booted with root=PARTUUID=<uuid> and the device /dev/root does not exist.
This path is exposed with the Ubuntu 16.04 but not with Ubuntu 17.10. A
recreate exists under bug 1684869.
LP: #1725067
---
cloudinit/config/cc_resizefs.py | 43 ++++------
.../test_handler/test_handler_resizefs.py | 91 ++++++++++++++--------
2 files changed, 70 insertions(+), 64 deletions(-)
diff --git a/cloudinit/config/cc_resizefs.py b/cloudinit/config/cc_resizefs.py
index f774baa..0d282e6 100644
--- a/cloudinit/config/cc_resizefs.py
+++ b/cloudinit/config/cc_resizefs.py
@@ -145,25 +145,6 @@ RESIZE_FS_PRECHECK_CMDS = {
}
-def rootdev_from_cmdline(cmdline):
- found = None
- for tok in cmdline.split():
- if tok.startswith("root="):
- found = tok[5:]
- break
- if found is None:
- return None
-
- if found.startswith("/dev/"):
- return found
- if found.startswith("LABEL="):
- return "/dev/disk/by-label/" + found[len("LABEL="):]
- if found.startswith("UUID="):
- return "/dev/disk/by-uuid/" + found[len("UUID="):]
-
- return "/dev/" + found
-
-
def can_skip_resize(fs_type, resize_what, devpth):
fstype_lc = fs_type.lower()
for i, func in RESIZE_FS_PRECHECK_CMDS.items():
@@ -172,14 +153,15 @@ def can_skip_resize(fs_type, resize_what, devpth):
return False
-def is_device_path_writable_block(devpath, info, log):
- """Return True if devpath is a writable block device.
+def maybe_get_writable_device_path(devpath, info, log):
+ """Return updated devpath if the devpath is a writable block device.
- @param devpath: Path to the root device we want to resize.
+ @param devpath: Requested path to the root device we want to resize.
@param info: String representing information about the requested device.
@param log: Logger to which logs will be added upon error.
- @returns Boolean True if block device is writable
+ @returns devpath or updated devpath per kernel commandline if the device
+ path is a writable block device, returns None otherwise.
"""
container = util.is_container()
@@ -189,12 +171,12 @@ def is_device_path_writable_block(devpath, info, log):
devpath = util.rootdev_from_cmdline(util.get_cmdline())
if devpath is None:
log.warn("Unable to find device '/dev/root'")
- return False
+ return None
log.debug("Converted /dev/root to '%s' per kernel cmdline", devpath)
if devpath == 'overlayroot':
log.debug("Not attempting to resize devpath '%s': %s", devpath, info)
- return False
+ return None
try:
statret = os.stat(devpath)
@@ -207,7 +189,7 @@ def is_device_path_writable_block(devpath, info, log):
devpath, info)
else:
raise exc
- return False
+ return None
if not stat.S_ISBLK(statret.st_mode) and not stat.S_ISCHR(statret.st_mode):
if container:
@@ -216,8 +198,8 @@ def is_device_path_writable_block(devpath, info, log):
else:
log.warn("device '%s' not a block device. cannot resize: %s" %
(devpath, info))
- return False
- return True
+ return None
+ return devpath # The writable block devpath
def handle(name, cfg, _cloud, log, args):
@@ -242,8 +224,9 @@ def handle(name, cfg, _cloud, log, args):
info = "dev=%s mnt_point=%s path=%s" % (devpth, mount_point, resize_what)
log.debug("resize_info: %s" % info)
- if not is_device_path_writable_block(devpth, info, log):
- return
+ devpth = maybe_get_writable_device_path(devpth, info, log)
+ if not devpth:
+ return # devpath was not a writable block device
resizer = None
if can_skip_resize(fs_type, resize_what, devpth):
diff --git a/tests/unittests/test_handler/test_handler_resizefs.py b/tests/unittests/test_handler/test_handler_resizefs.py
index 3e5d436..29d5574 100644
--- a/tests/unittests/test_handler/test_handler_resizefs.py
+++ b/tests/unittests/test_handler/test_handler_resizefs.py
@@ -1,9 +1,9 @@
# This file is part of cloud-init. See LICENSE file for license information.
from cloudinit.config.cc_resizefs import (
- can_skip_resize, handle, is_device_path_writable_block,
- rootdev_from_cmdline)
+ can_skip_resize, handle, maybe_get_writable_device_path)
+from collections import namedtuple
import logging
import textwrap
@@ -138,47 +138,48 @@ class TestRootDevFromCmdline(CiTestCase):
invalid_cases = [
'BOOT_IMAGE=/adsf asdfa werasef root adf', 'BOOT_IMAGE=/adsf', '']
for case in invalid_cases:
- self.assertIsNone(rootdev_from_cmdline(case))
+ self.assertIsNone(util.rootdev_from_cmdline(case))
def test_rootdev_from_cmdline_with_root_startswith_dev(self):
"""Return the cmdline root when the path starts with /dev."""
self.assertEqual(
- '/dev/this', rootdev_from_cmdline('asdf root=/dev/this'))
+ '/dev/this', util.rootdev_from_cmdline('asdf root=/dev/this'))
def test_rootdev_from_cmdline_with_root_without_dev_prefix(self):
"""Add /dev prefix to cmdline root when the path lacks the prefix."""
- self.assertEqual('/dev/this', rootdev_from_cmdline('asdf root=this'))
+ self.assertEqual(
+ '/dev/this', util.rootdev_from_cmdline('asdf root=this'))
def test_rootdev_from_cmdline_with_root_with_label(self):
"""When cmdline root contains a LABEL, our root is disk/by-label."""
self.assertEqual(
'/dev/disk/by-label/unique',
- rootdev_from_cmdline('asdf root=LABEL=unique'))
+ util.rootdev_from_cmdline('asdf root=LABEL=unique'))
def test_rootdev_from_cmdline_with_root_with_uuid(self):
"""When cmdline root contains a UUID, our root is disk/by-uuid."""
self.assertEqual(
'/dev/disk/by-uuid/adsfdsaf-adsf',
- rootdev_from_cmdline('asdf root=UUID=adsfdsaf-adsf'))
+ util.rootdev_from_cmdline('asdf root=UUID=adsfdsaf-adsf'))
-class TestIsDevicePathWritableBlock(CiTestCase):
+class TestMaybeGetDevicePathAsWritableBlock(CiTestCase):
with_logs = True
- def test_is_device_path_writable_block_false_on_overlayroot(self):
+ def test_maybe_get_writable_device_path_none_on_overlayroot(self):
"""When devpath is overlayroot (on MAAS), is_dev_writable is False."""
info = 'does not matter'
- is_writable = wrap_and_call(
+ devpath = wrap_and_call(
'cloudinit.config.cc_resizefs.util',
{'is_container': {'return_value': False}},
- is_device_path_writable_block, 'overlayroot', info, LOG)
- self.assertFalse(is_writable)
+ maybe_get_writable_device_path, 'overlayroot', info, LOG)
+ self.assertIsNone(devpath)
self.assertIn(
"Not attempting to resize devpath 'overlayroot'",
self.logs.getvalue())
- def test_is_device_path_writable_block_warns_missing_cmdline_root(self):
+ def test_maybe_get_writable_device_path_warns_missing_cmdline_root(self):
"""When root does not exist isn't in the cmdline, log warning."""
info = 'does not matter'
@@ -190,43 +191,43 @@ class TestIsDevicePathWritableBlock(CiTestCase):
exists_mock_path = 'cloudinit.config.cc_resizefs.os.path.exists'
with mock.patch(exists_mock_path) as m_exists:
m_exists.return_value = False
- is_writable = wrap_and_call(
+ devpath = wrap_and_call(
'cloudinit.config.cc_resizefs.util',
{'is_container': {'return_value': False},
'get_mount_info': {'side_effect': fake_mount_info},
'get_cmdline': {'return_value': 'BOOT_IMAGE=/vmlinuz.efi'}},
- is_device_path_writable_block, '/dev/root', info, LOG)
- self.assertFalse(is_writable)
+ maybe_get_writable_device_path, '/dev/root', info, LOG)
+ self.assertIsNone(devpath)
logs = self.logs.getvalue()
self.assertIn("WARNING: Unable to find device '/dev/root'", logs)
- def test_is_device_path_writable_block_does_not_exist(self):
+ def test_maybe_get_writable_device_path_does_not_exist(self):
"""When devpath does not exist, a warning is logged."""
info = 'dev=/I/dont/exist mnt_point=/ path=/dev/none'
- is_writable = wrap_and_call(
+ devpath = wrap_and_call(
'cloudinit.config.cc_resizefs.util',
{'is_container': {'return_value': False}},
- is_device_path_writable_block, '/I/dont/exist', info, LOG)
- self.assertFalse(is_writable)
+ maybe_get_writable_device_path, '/I/dont/exist', info, LOG)
+ self.assertIsNone(devpath)
self.assertIn(
"WARNING: Device '/I/dont/exist' did not exist."
' cannot resize: %s' % info,
self.logs.getvalue())
- def test_is_device_path_writable_block_does_not_exist_in_container(self):
+ def test_maybe_get_writable_device_path_does_not_exist_in_container(self):
"""When devpath does not exist in a container, log a debug message."""
info = 'dev=/I/dont/exist mnt_point=/ path=/dev/none'
- is_writable = wrap_and_call(
+ devpath = wrap_and_call(
'cloudinit.config.cc_resizefs.util',
{'is_container': {'return_value': True}},
- is_device_path_writable_block, '/I/dont/exist', info, LOG)
- self.assertFalse(is_writable)
+ maybe_get_writable_device_path, '/I/dont/exist', info, LOG)
+ self.assertIsNone(devpath)
self.assertIn(
"DEBUG: Device '/I/dont/exist' did not exist in container."
' cannot resize: %s' % info,
self.logs.getvalue())
- def test_is_device_path_writable_block_raises_oserror(self):
+ def test_maybe_get_writable_device_path_raises_oserror(self):
"""When unexpected OSError is raises by os.stat it is reraised."""
info = 'dev=/I/dont/exist mnt_point=/ path=/dev/none'
with self.assertRaises(OSError) as context_manager:
@@ -234,41 +235,63 @@ class TestIsDevicePathWritableBlock(CiTestCase):
'cloudinit.config.cc_resizefs',
{'util.is_container': {'return_value': True},
'os.stat': {'side_effect': OSError('Something unexpected')}},
- is_device_path_writable_block, '/I/dont/exist', info, LOG)
+ maybe_get_writable_device_path, '/I/dont/exist', info, LOG)
self.assertEqual(
'Something unexpected', str(context_manager.exception))
- def test_is_device_path_writable_block_non_block(self):
+ def test_maybe_get_writable_device_path_non_block(self):
"""When device is not a block device, emit warning return False."""
fake_devpath = self.tmp_path('dev/readwrite')
util.write_file(fake_devpath, '', mode=0o600) # read-write
info = 'dev=/dev/root mnt_point=/ path={0}'.format(fake_devpath)
- is_writable = wrap_and_call(
+ devpath = wrap_and_call(
'cloudinit.config.cc_resizefs.util',
{'is_container': {'return_value': False}},
- is_device_path_writable_block, fake_devpath, info, LOG)
- self.assertFalse(is_writable)
+ maybe_get_writable_device_path, fake_devpath, info, LOG)
+ self.assertIsNone(devpath)
self.assertIn(
"WARNING: device '{0}' not a block device. cannot resize".format(
fake_devpath),
self.logs.getvalue())
- def test_is_device_path_writable_block_non_block_on_container(self):
+ def test_maybe_get_writable_device_path_non_block_on_container(self):
"""When device is non-block device in container, emit debug log."""
fake_devpath = self.tmp_path('dev/readwrite')
util.write_file(fake_devpath, '', mode=0o600) # read-write
info = 'dev=/dev/root mnt_point=/ path={0}'.format(fake_devpath)
- is_writable = wrap_and_call(
+ devpath = wrap_and_call(
'cloudinit.config.cc_resizefs.util',
{'is_container': {'return_value': True}},
- is_device_path_writable_block, fake_devpath, info, LOG)
- self.assertFalse(is_writable)
+ maybe_get_writable_device_path, fake_devpath, info, LOG)
+ self.assertIsNone(devpath)
self.assertIn(
"DEBUG: device '{0}' not a block device in container."
' cannot resize'.format(fake_devpath),
self.logs.getvalue())
+ def test_maybe_get_writable_device_path_returns_cmdline_root(self):
+ """When root device is UUID in kernel commandline, update devpath."""
+ # XXX Long-term we want to use FilesystemMocking test to avoid
+ # touching os.stat.
+ FakeStat = namedtuple(
+ 'FakeStat', ['st_mode', 'st_size', 'st_mtime']) # minimal def.
+ info = 'dev=/dev/root mnt_point=/ path=/does/not/matter'
+ devpath = wrap_and_call(
+ 'cloudinit.config.cc_resizefs',
+ {'util.get_cmdline': {'return_value': 'asdf root=UUID=my-uuid'},
+ 'util.is_container': False,
+ 'os.path.exists': False, # /dev/root doesn't exist
+ 'os.stat': {
+ 'return_value': FakeStat(25008, 0, 1)} # char block device
+ },
+ maybe_get_writable_device_path, '/dev/root', info, LOG)
+ self.assertEqual('/dev/disk/by-uuid/my-uuid', devpath)
+ self.assertIn(
+ "DEBUG: Converted /dev/root to '/dev/disk/by-uuid/my-uuid'"
+ " per kernel cmdline",
+ self.logs.getvalue())
+
# vi: ts=4 expandtab
--
1.7.12.4

View File

@ -1,33 +0,0 @@
From f0ff194054da90b7b49620b5658342e52156d68e Mon Sep 17 00:00:00 2001
From: Scott Moser <smoser@ubuntu.com>
Date: Thu, 20 Sep 2018 12:45:00 +0000
Subject: [PATCH 275/354] stages: Fix bug causing datasource to have incorrect
sys_cfg.
The Init object had a bug/odd side effect where when retrieving a distro
object it would update the datasources's sys_cfg. That was probably
intended to refresh the possibly stale config stored there. Unfortunately
what it actually did limit the config there to the 'system_info' top level
key where initially it had the whole config.
LP: #1787459
---
cloudinit/stages.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cloudinit/stages.py b/cloudinit/stages.py
index ef5c699..8a06412 100644
--- a/cloudinit/stages.py
+++ b/cloudinit/stages.py
@@ -88,7 +88,7 @@ class Init(object):
# from whatever it was to a new set...
if self.datasource is not NULL_DATA_SOURCE:
self.datasource.distro = self._distro
- self.datasource.sys_cfg = system_config
+ self.datasource.sys_cfg = self.cfg
return self._distro
@property
--
1.7.12.4

View File

@ -1,94 +0,0 @@
From fef2616b9876d3d354b0de1a8e753361e52e77b0 Mon Sep 17 00:00:00 2001
From: Robert Schweikert <rjschwei@suse.com>
Date: Fri, 15 Jun 2018 13:41:21 -0600
Subject: [PATCH 219/354] stages: fix tracebacks if a module stage is
undefined or empty
In /etc/cloud/cloud.cfg, users and imagees can configure which modules run
during a specific cloud-init stage by modifying one of the following
lists: cloud_init_modules, cloud_init_modules, cloud_init_final_modules.
If any of the configured module lists are absent or empty, cloud-init will
emit the same message it already does for existing lists that only contain
modules which are not unsupported on that platform:
No 'config' modules to run under section 'cloud_config_modules'
LP: #1770462
---
cloudinit/stages.py | 4 +++-
tests/unittests/test_runs/test_simple_run.py | 32 ++++++++++++++++++++++++++--
2 files changed, 33 insertions(+), 3 deletions(-)
diff --git a/cloudinit/stages.py b/cloudinit/stages.py
index 3998cf6..286607b 100644
--- a/cloudinit/stages.py
+++ b/cloudinit/stages.py
@@ -697,7 +697,9 @@ class Modules(object):
module_list = []
if name not in self.cfg:
return module_list
- cfg_mods = self.cfg[name]
+ cfg_mods = self.cfg.get(name)
+ if not cfg_mods:
+ return module_list
# Create 'module_list', an array of hashes
# Where hash['mod'] = module name
# hash['freq'] = frequency
diff --git a/tests/unittests/test_runs/test_simple_run.py b/tests/unittests/test_runs/test_simple_run.py
index 762974e..d67c422 100644
--- a/tests/unittests/test_runs/test_simple_run.py
+++ b/tests/unittests/test_runs/test_simple_run.py
@@ -1,5 +1,6 @@
# This file is part of cloud-init. See LICENSE file for license information.
+import copy
import os
from cloudinit.tests import helpers
@@ -127,8 +128,9 @@ class TestSimpleRun(helpers.FilesystemMockingTestCase):
"""run_section forced skipped modules by using unverified_modules."""
# re-write cloud.cfg with unverified_modules override
- self.cfg['unverified_modules'] = ['spacewalk'] # Would have skipped
- cloud_cfg = util.yaml_dumps(self.cfg)
+ cfg = copy.deepcopy(self.cfg)
+ cfg['unverified_modules'] = ['spacewalk'] # Would have skipped
+ cloud_cfg = util.yaml_dumps(cfg)
util.ensure_dir(os.path.join(self.new_root, 'etc', 'cloud'))
util.write_file(os.path.join(self.new_root, 'etc',
'cloud', 'cloud.cfg'), cloud_cfg)
@@ -150,4 +152,30 @@ class TestSimpleRun(helpers.FilesystemMockingTestCase):
"running unverified_modules: 'spacewalk'",
self.logs.getvalue())
+ def test_none_ds_run_with_no_config_modules(self):
+ """run_section will report no modules run when none are configured."""
+
+ # re-write cloud.cfg with unverified_modules override
+ cfg = copy.deepcopy(self.cfg)
+ # Represent empty configuration in /etc/cloud/cloud.cfg
+ cfg['cloud_init_modules'] = None
+ cloud_cfg = util.yaml_dumps(cfg)
+ util.ensure_dir(os.path.join(self.new_root, 'etc', 'cloud'))
+ util.write_file(os.path.join(self.new_root, 'etc',
+ 'cloud', 'cloud.cfg'), cloud_cfg)
+
+ initer = stages.Init()
+ initer.read_cfg()
+ initer.initialize()
+ initer.fetch()
+ initer.instancify()
+ initer.update()
+ initer.cloudify().run('consume_data', initer.consume_data,
+ args=[PER_INSTANCE], freq=PER_INSTANCE)
+
+ mods = stages.Modules(initer)
+ (which_ran, failures) = mods.run_section('cloud_init_modules')
+ self.assertTrue(len(failures) == 0)
+ self.assertEqual([], which_ran)
+
# vi: ts=4 expandtab
--
1.7.12.4