forked from pool/cloud-init
Accepting request 738480 from Cloud:Tools
Automatic submission by obs-autosubmit OBS-URL: https://build.opensuse.org/request/show/738480 OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/cloud-init?expand=0&rev=62
This commit is contained in:
commit
08601df903
@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:75be8cbff1431883227c05356cb69400f20bbb2666fd05e085f846ecf1d153cb
|
||||
size 1017682
|
3
cloud-init-19.2.tar.gz
Normal file
3
cloud-init-19.2.tar.gz
Normal file
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f5ead1b3c782c159669f8f8779c45d16a986c7405425d75f915ec55301d83a07
|
||||
size 1028832
|
@ -1,426 +0,0 @@
|
||||
From 07b17236be5665bb552c7460102bcd07bf8f2be8 Mon Sep 17 00:00:00 2001
|
||||
From: Ryan Harper <ryan.harper@canonical.com>
|
||||
Date: Tue, 16 Jul 2019 22:40:15 +0000
|
||||
Subject: net: add rfc3442 (classless static routes) to EphemeralDHCP
|
||||
|
||||
The EphemeralDHCP context manager did not parse or handle
|
||||
rfc3442 classless static routes which prevented reading
|
||||
datasource metadata in some clouds. This branch adds support
|
||||
for extracting the field from the leases output, parsing the
|
||||
format and then adding the required iproute2 ip commands to
|
||||
apply (and teardown) the static routes.
|
||||
|
||||
LP: #1821102
|
||||
|
||||
diff --git a/cloudinit/net/__init__.py b/cloudinit/net/__init__.py
|
||||
index e758006..624c9b4 100644
|
||||
--- a/cloudinit/net/__init__.py
|
||||
+++ b/cloudinit/net/__init__.py
|
||||
@@ -679,7 +679,7 @@ class EphemeralIPv4Network(object):
|
||||
"""
|
||||
|
||||
def __init__(self, interface, ip, prefix_or_mask, broadcast, router=None,
|
||||
- connectivity_url=None):
|
||||
+ connectivity_url=None, static_routes=None):
|
||||
"""Setup context manager and validate call signature.
|
||||
|
||||
@param interface: Name of the network interface to bring up.
|
||||
@@ -690,6 +690,7 @@ class EphemeralIPv4Network(object):
|
||||
@param router: Optionally the default gateway IP.
|
||||
@param connectivity_url: Optionally, a URL to verify if a usable
|
||||
connection already exists.
|
||||
+ @param static_routes: Optionally a list of static routes from DHCP
|
||||
"""
|
||||
if not all([interface, ip, prefix_or_mask, broadcast]):
|
||||
raise ValueError(
|
||||
@@ -706,6 +707,7 @@ class EphemeralIPv4Network(object):
|
||||
self.ip = ip
|
||||
self.broadcast = broadcast
|
||||
self.router = router
|
||||
+ self.static_routes = static_routes
|
||||
self.cleanup_cmds = [] # List of commands to run to cleanup state.
|
||||
|
||||
def __enter__(self):
|
||||
@@ -718,7 +720,21 @@ class EphemeralIPv4Network(object):
|
||||
return
|
||||
|
||||
self._bringup_device()
|
||||
- if self.router:
|
||||
+
|
||||
+ # rfc3442 requires us to ignore the router config *if* classless static
|
||||
+ # routes are provided.
|
||||
+ #
|
||||
+ # https://tools.ietf.org/html/rfc3442
|
||||
+ #
|
||||
+ # If the DHCP server returns both a Classless Static Routes option and
|
||||
+ # a Router option, the DHCP client MUST ignore the Router option.
|
||||
+ #
|
||||
+ # Similarly, if the DHCP server returns both a Classless Static Routes
|
||||
+ # option and a Static Routes option, the DHCP client MUST ignore the
|
||||
+ # Static Routes option.
|
||||
+ if self.static_routes:
|
||||
+ self._bringup_static_routes()
|
||||
+ elif self.router:
|
||||
self._bringup_router()
|
||||
|
||||
def __exit__(self, excp_type, excp_value, excp_traceback):
|
||||
@@ -762,6 +778,20 @@ class EphemeralIPv4Network(object):
|
||||
['ip', '-family', 'inet', 'addr', 'del', cidr, 'dev',
|
||||
self.interface])
|
||||
|
||||
+ def _bringup_static_routes(self):
|
||||
+ # static_routes = [("169.254.169.254/32", "130.56.248.255"),
|
||||
+ # ("0.0.0.0/0", "130.56.240.1")]
|
||||
+ for net_address, gateway in self.static_routes:
|
||||
+ via_arg = []
|
||||
+ if gateway != "0.0.0.0/0":
|
||||
+ via_arg = ['via', gateway]
|
||||
+ util.subp(
|
||||
+ ['ip', '-4', 'route', 'add', net_address] + via_arg +
|
||||
+ ['dev', self.interface], capture=True)
|
||||
+ self.cleanup_cmds.insert(
|
||||
+ 0, ['ip', '-4', 'route', 'del', net_address] + via_arg +
|
||||
+ ['dev', self.interface])
|
||||
+
|
||||
def _bringup_router(self):
|
||||
"""Perform the ip commands to fully setup the router if needed."""
|
||||
# Check if a default route exists and exit if it does
|
||||
diff --git a/cloudinit/net/dhcp.py b/cloudinit/net/dhcp.py
|
||||
index c98a97c..1737991 100644
|
||||
--- a/cloudinit/net/dhcp.py
|
||||
+++ b/cloudinit/net/dhcp.py
|
||||
@@ -92,10 +92,14 @@ class EphemeralDHCPv4(object):
|
||||
nmap = {'interface': 'interface', 'ip': 'fixed-address',
|
||||
'prefix_or_mask': 'subnet-mask',
|
||||
'broadcast': 'broadcast-address',
|
||||
+ 'static_routes': 'rfc3442-classless-static-routes',
|
||||
'router': 'routers'}
|
||||
kwargs = dict([(k, self.lease.get(v)) for k, v in nmap.items()])
|
||||
if not kwargs['broadcast']:
|
||||
kwargs['broadcast'] = bcip(kwargs['prefix_or_mask'], kwargs['ip'])
|
||||
+ if kwargs['static_routes']:
|
||||
+ kwargs['static_routes'] = (
|
||||
+ parse_static_routes(kwargs['static_routes']))
|
||||
if self.connectivity_url:
|
||||
kwargs['connectivity_url'] = self.connectivity_url
|
||||
ephipv4 = EphemeralIPv4Network(**kwargs)
|
||||
@@ -272,4 +276,90 @@ def networkd_get_option_from_leases(keyname, leases_d=None):
|
||||
return data[keyname]
|
||||
return None
|
||||
|
||||
+
|
||||
+def parse_static_routes(rfc3442):
|
||||
+ """ parse rfc3442 format and return a list containing tuple of strings.
|
||||
+
|
||||
+ The tuple is composed of the network_address (including net length) and
|
||||
+ gateway for a parsed static route.
|
||||
+
|
||||
+ @param rfc3442: string in rfc3442 format
|
||||
+ @returns: list of tuple(str, str) for all valid parsed routes until the
|
||||
+ first parsing error.
|
||||
+
|
||||
+ E.g.
|
||||
+ sr = parse_state_routes("32,169,254,169,254,130,56,248,255,0,130,56,240,1")
|
||||
+ sr = [
|
||||
+ ("169.254.169.254/32", "130.56.248.255"), ("0.0.0.0/0", "130.56.240.1")
|
||||
+ ]
|
||||
+
|
||||
+ Python version of isc-dhclient's hooks:
|
||||
+ /etc/dhcp/dhclient-exit-hooks.d/rfc3442-classless-routes
|
||||
+ """
|
||||
+ # raw strings from dhcp lease may end in semi-colon
|
||||
+ rfc3442 = rfc3442.rstrip(";")
|
||||
+ tokens = rfc3442.split(',')
|
||||
+ static_routes = []
|
||||
+
|
||||
+ def _trunc_error(cidr, required, remain):
|
||||
+ msg = ("RFC3442 string malformed. Current route has CIDR of %s "
|
||||
+ "and requires %s significant octets, but only %s remain. "
|
||||
+ "Verify DHCP rfc3442-classless-static-routes value: %s"
|
||||
+ % (cidr, required, remain, rfc3442))
|
||||
+ LOG.error(msg)
|
||||
+
|
||||
+ current_idx = 0
|
||||
+ for idx, tok in enumerate(tokens):
|
||||
+ if idx < current_idx:
|
||||
+ continue
|
||||
+ net_length = int(tok)
|
||||
+ if net_length in range(25, 33):
|
||||
+ req_toks = 9
|
||||
+ if len(tokens[idx:]) < req_toks:
|
||||
+ _trunc_error(net_length, req_toks, len(tokens[idx:]))
|
||||
+ return static_routes
|
||||
+ net_address = ".".join(tokens[idx+1:idx+5])
|
||||
+ gateway = ".".join(tokens[idx+5:idx+req_toks])
|
||||
+ current_idx = idx + req_toks
|
||||
+ elif net_length in range(17, 25):
|
||||
+ req_toks = 8
|
||||
+ if len(tokens[idx:]) < req_toks:
|
||||
+ _trunc_error(net_length, req_toks, len(tokens[idx:]))
|
||||
+ return static_routes
|
||||
+ net_address = ".".join(tokens[idx+1:idx+4] + ["0"])
|
||||
+ gateway = ".".join(tokens[idx+4:idx+req_toks])
|
||||
+ current_idx = idx + req_toks
|
||||
+ elif net_length in range(9, 17):
|
||||
+ req_toks = 7
|
||||
+ if len(tokens[idx:]) < req_toks:
|
||||
+ _trunc_error(net_length, req_toks, len(tokens[idx:]))
|
||||
+ return static_routes
|
||||
+ net_address = ".".join(tokens[idx+1:idx+3] + ["0", "0"])
|
||||
+ gateway = ".".join(tokens[idx+3:idx+req_toks])
|
||||
+ current_idx = idx + req_toks
|
||||
+ elif net_length in range(1, 9):
|
||||
+ req_toks = 6
|
||||
+ if len(tokens[idx:]) < req_toks:
|
||||
+ _trunc_error(net_length, req_toks, len(tokens[idx:]))
|
||||
+ return static_routes
|
||||
+ net_address = ".".join(tokens[idx+1:idx+2] + ["0", "0", "0"])
|
||||
+ gateway = ".".join(tokens[idx+2:idx+req_toks])
|
||||
+ current_idx = idx + req_toks
|
||||
+ elif net_length == 0:
|
||||
+ req_toks = 5
|
||||
+ if len(tokens[idx:]) < req_toks:
|
||||
+ _trunc_error(net_length, req_toks, len(tokens[idx:]))
|
||||
+ return static_routes
|
||||
+ net_address = "0.0.0.0"
|
||||
+ gateway = ".".join(tokens[idx+1:idx+req_toks])
|
||||
+ current_idx = idx + req_toks
|
||||
+ else:
|
||||
+ LOG.error('Parsed invalid net length "%s". Verify DHCP '
|
||||
+ 'rfc3442-classless-static-routes value.', net_length)
|
||||
+ return static_routes
|
||||
+
|
||||
+ static_routes.append(("%s/%s" % (net_address, net_length), gateway))
|
||||
+
|
||||
+ return static_routes
|
||||
+
|
||||
# vi: ts=4 expandtab
|
||||
diff --git a/cloudinit/net/tests/test_dhcp.py b/cloudinit/net/tests/test_dhcp.py
|
||||
index 5139024..91f503c 100644
|
||||
--- a/cloudinit/net/tests/test_dhcp.py
|
||||
+++ b/cloudinit/net/tests/test_dhcp.py
|
||||
@@ -8,7 +8,8 @@ from textwrap import dedent
|
||||
import cloudinit.net as net
|
||||
from cloudinit.net.dhcp import (
|
||||
InvalidDHCPLeaseFileError, maybe_perform_dhcp_discovery,
|
||||
- parse_dhcp_lease_file, dhcp_discovery, networkd_load_leases)
|
||||
+ parse_dhcp_lease_file, dhcp_discovery, networkd_load_leases,
|
||||
+ parse_static_routes)
|
||||
from cloudinit.util import ensure_file, write_file
|
||||
from cloudinit.tests.helpers import (
|
||||
CiTestCase, HttprettyTestCase, mock, populate_dir, wrap_and_call)
|
||||
@@ -64,6 +65,123 @@ class TestParseDHCPLeasesFile(CiTestCase):
|
||||
self.assertItemsEqual(expected, parse_dhcp_lease_file(lease_file))
|
||||
|
||||
|
||||
+class TestDHCPRFC3442(CiTestCase):
|
||||
+
|
||||
+ def test_parse_lease_finds_rfc3442_classless_static_routes(self):
|
||||
+ """parse_dhcp_lease_file returns rfc3442-classless-static-routes."""
|
||||
+ lease_file = self.tmp_path('leases')
|
||||
+ content = dedent("""
|
||||
+ lease {
|
||||
+ interface "wlp3s0";
|
||||
+ fixed-address 192.168.2.74;
|
||||
+ option subnet-mask 255.255.255.0;
|
||||
+ option routers 192.168.2.1;
|
||||
+ option rfc3442-classless-static-routes 0,130,56,240,1;
|
||||
+ renew 4 2017/07/27 18:02:30;
|
||||
+ expire 5 2017/07/28 07:08:15;
|
||||
+ }
|
||||
+ """)
|
||||
+ expected = [
|
||||
+ {'interface': 'wlp3s0', 'fixed-address': '192.168.2.74',
|
||||
+ 'subnet-mask': '255.255.255.0', 'routers': '192.168.2.1',
|
||||
+ 'rfc3442-classless-static-routes': '0,130,56,240,1',
|
||||
+ 'renew': '4 2017/07/27 18:02:30',
|
||||
+ 'expire': '5 2017/07/28 07:08:15'}]
|
||||
+ write_file(lease_file, content)
|
||||
+ self.assertItemsEqual(expected, parse_dhcp_lease_file(lease_file))
|
||||
+
|
||||
+ @mock.patch('cloudinit.net.dhcp.EphemeralIPv4Network')
|
||||
+ @mock.patch('cloudinit.net.dhcp.maybe_perform_dhcp_discovery')
|
||||
+ def test_obtain_lease_parses_static_routes(self, m_maybe, m_ipv4):
|
||||
+ """EphemeralDHPCv4 parses rfc3442 routes for EphemeralIPv4Network"""
|
||||
+ lease = [
|
||||
+ {'interface': 'wlp3s0', 'fixed-address': '192.168.2.74',
|
||||
+ 'subnet-mask': '255.255.255.0', 'routers': '192.168.2.1',
|
||||
+ 'rfc3442-classless-static-routes': '0,130,56,240,1',
|
||||
+ 'renew': '4 2017/07/27 18:02:30',
|
||||
+ 'expire': '5 2017/07/28 07:08:15'}]
|
||||
+ m_maybe.return_value = lease
|
||||
+ eph = net.dhcp.EphemeralDHCPv4()
|
||||
+ eph.obtain_lease()
|
||||
+ expected_kwargs = {
|
||||
+ 'interface': 'wlp3s0',
|
||||
+ 'ip': '192.168.2.74',
|
||||
+ 'prefix_or_mask': '255.255.255.0',
|
||||
+ 'broadcast': '192.168.2.255',
|
||||
+ 'static_routes': [('0.0.0.0/0', '130.56.240.1')],
|
||||
+ 'router': '192.168.2.1'}
|
||||
+ m_ipv4.assert_called_with(**expected_kwargs)
|
||||
+
|
||||
+
|
||||
+class TestDHCPParseStaticRoutes(CiTestCase):
|
||||
+
|
||||
+ with_logs = True
|
||||
+
|
||||
+ def parse_static_routes_empty_string(self):
|
||||
+ self.assertEqual([], parse_static_routes(""))
|
||||
+
|
||||
+ def test_parse_static_routes_invalid_input_returns_empty_list(self):
|
||||
+ rfc3442 = "32,169,254,169,254,130,56,248"
|
||||
+ self.assertEqual([], parse_static_routes(rfc3442))
|
||||
+
|
||||
+ def test_parse_static_routes_bogus_width_returns_empty_list(self):
|
||||
+ rfc3442 = "33,169,254,169,254,130,56,248"
|
||||
+ self.assertEqual([], parse_static_routes(rfc3442))
|
||||
+
|
||||
+ def test_parse_static_routes_single_ip(self):
|
||||
+ rfc3442 = "32,169,254,169,254,130,56,248,255"
|
||||
+ self.assertEqual([('169.254.169.254/32', '130.56.248.255')],
|
||||
+ parse_static_routes(rfc3442))
|
||||
+
|
||||
+ def test_parse_static_routes_single_ip_handles_trailing_semicolon(self):
|
||||
+ rfc3442 = "32,169,254,169,254,130,56,248,255;"
|
||||
+ self.assertEqual([('169.254.169.254/32', '130.56.248.255')],
|
||||
+ parse_static_routes(rfc3442))
|
||||
+
|
||||
+ def test_parse_static_routes_default_route(self):
|
||||
+ rfc3442 = "0,130,56,240,1"
|
||||
+ self.assertEqual([('0.0.0.0/0', '130.56.240.1')],
|
||||
+ parse_static_routes(rfc3442))
|
||||
+
|
||||
+ def test_parse_static_routes_class_c_b_a(self):
|
||||
+ class_c = "24,192,168,74,192,168,0,4"
|
||||
+ class_b = "16,172,16,172,16,0,4"
|
||||
+ class_a = "8,10,10,0,0,4"
|
||||
+ rfc3442 = ",".join([class_c, class_b, class_a])
|
||||
+ self.assertEqual(sorted([
|
||||
+ ("192.168.74.0/24", "192.168.0.4"),
|
||||
+ ("172.16.0.0/16", "172.16.0.4"),
|
||||
+ ("10.0.0.0/8", "10.0.0.4")
|
||||
+ ]), sorted(parse_static_routes(rfc3442)))
|
||||
+
|
||||
+ def test_parse_static_routes_logs_error_truncated(self):
|
||||
+ bad_rfc3442 = {
|
||||
+ "class_c": "24,169,254,169,10",
|
||||
+ "class_b": "16,172,16,10",
|
||||
+ "class_a": "8,10,10",
|
||||
+ "gateway": "0,0",
|
||||
+ "netlen": "33,0",
|
||||
+ }
|
||||
+ for rfc3442 in bad_rfc3442.values():
|
||||
+ self.assertEqual([], parse_static_routes(rfc3442))
|
||||
+
|
||||
+ logs = self.logs.getvalue()
|
||||
+ self.assertEqual(len(bad_rfc3442.keys()), len(logs.splitlines()))
|
||||
+
|
||||
+ def test_parse_static_routes_returns_valid_routes_until_parse_err(self):
|
||||
+ class_c = "24,192,168,74,192,168,0,4"
|
||||
+ class_b = "16,172,16,172,16,0,4"
|
||||
+ class_a_error = "8,10,10,0,0"
|
||||
+ rfc3442 = ",".join([class_c, class_b, class_a_error])
|
||||
+ self.assertEqual(sorted([
|
||||
+ ("192.168.74.0/24", "192.168.0.4"),
|
||||
+ ("172.16.0.0/16", "172.16.0.4"),
|
||||
+ ]), sorted(parse_static_routes(rfc3442)))
|
||||
+
|
||||
+ logs = self.logs.getvalue()
|
||||
+ self.assertIn(rfc3442, logs.splitlines()[0])
|
||||
+
|
||||
+
|
||||
class TestDHCPDiscoveryClean(CiTestCase):
|
||||
with_logs = True
|
||||
|
||||
diff --git a/cloudinit/net/tests/test_init.py b/cloudinit/net/tests/test_init.py
|
||||
index 6d2affe..d393e6a 100644
|
||||
--- a/cloudinit/net/tests/test_init.py
|
||||
+++ b/cloudinit/net/tests/test_init.py
|
||||
@@ -549,6 +549,45 @@ class TestEphemeralIPV4Network(CiTestCase):
|
||||
self.assertEqual(expected_setup_calls, m_subp.call_args_list)
|
||||
m_subp.assert_has_calls(expected_teardown_calls)
|
||||
|
||||
+ def test_ephemeral_ipv4_network_with_rfc3442_static_routes(self, m_subp):
|
||||
+ params = {
|
||||
+ 'interface': 'eth0', 'ip': '192.168.2.2',
|
||||
+ 'prefix_or_mask': '255.255.255.0', 'broadcast': '192.168.2.255',
|
||||
+ 'static_routes': [('169.254.169.254/32', '192.168.2.1'),
|
||||
+ ('0.0.0.0/0', '192.168.2.1')],
|
||||
+ 'router': '192.168.2.1'}
|
||||
+ expected_setup_calls = [
|
||||
+ mock.call(
|
||||
+ ['ip', '-family', 'inet', 'addr', 'add', '192.168.2.2/24',
|
||||
+ 'broadcast', '192.168.2.255', 'dev', 'eth0'],
|
||||
+ capture=True, update_env={'LANG': 'C'}),
|
||||
+ mock.call(
|
||||
+ ['ip', '-family', 'inet', 'link', 'set', 'dev', 'eth0', 'up'],
|
||||
+ capture=True),
|
||||
+ mock.call(
|
||||
+ ['ip', '-4', 'route', 'add', '169.254.169.254/32',
|
||||
+ 'via', '192.168.2.1', 'dev', 'eth0'], capture=True),
|
||||
+ mock.call(
|
||||
+ ['ip', '-4', 'route', 'add', '0.0.0.0/0',
|
||||
+ 'via', '192.168.2.1', 'dev', 'eth0'], capture=True)]
|
||||
+ expected_teardown_calls = [
|
||||
+ mock.call(
|
||||
+ ['ip', '-4', 'route', 'del', '0.0.0.0/0',
|
||||
+ 'via', '192.168.2.1', 'dev', 'eth0'], capture=True),
|
||||
+ mock.call(
|
||||
+ ['ip', '-4', 'route', 'del', '169.254.169.254/32',
|
||||
+ 'via', '192.168.2.1', 'dev', 'eth0'], capture=True),
|
||||
+ mock.call(
|
||||
+ ['ip', '-family', 'inet', 'link', 'set', 'dev',
|
||||
+ 'eth0', 'down'], capture=True),
|
||||
+ mock.call(
|
||||
+ ['ip', '-family', 'inet', 'addr', 'del',
|
||||
+ '192.168.2.2/24', 'dev', 'eth0'], capture=True)
|
||||
+ ]
|
||||
+ with net.EphemeralIPv4Network(**params):
|
||||
+ self.assertEqual(expected_setup_calls, m_subp.call_args_list)
|
||||
+ m_subp.assert_has_calls(expected_setup_calls + expected_teardown_calls)
|
||||
+
|
||||
|
||||
class TestApplyNetworkCfgNames(CiTestCase):
|
||||
V1_CONFIG = textwrap.dedent("""\
|
||||
diff --git a/tests/unittests/test_datasource/test_azure.py b/tests/unittests/test_datasource/test_azure.py
|
||||
index f27ef21..2de2aea 100644
|
||||
--- a/tests/unittests/test_datasource/test_azure.py
|
||||
+++ b/tests/unittests/test_datasource/test_azure.py
|
||||
@@ -1807,7 +1807,8 @@ class TestAzureDataSourcePreprovisioning(CiTestCase):
|
||||
self.assertEqual(m_dhcp.call_count, 2)
|
||||
m_net.assert_any_call(
|
||||
broadcast='192.168.2.255', interface='eth9', ip='192.168.2.9',
|
||||
- prefix_or_mask='255.255.255.0', router='192.168.2.1')
|
||||
+ prefix_or_mask='255.255.255.0', router='192.168.2.1',
|
||||
+ static_routes=None)
|
||||
self.assertEqual(m_net.call_count, 2)
|
||||
|
||||
def test__reprovision_calls__poll_imds(self, fake_resp,
|
||||
@@ -1845,7 +1846,8 @@ class TestAzureDataSourcePreprovisioning(CiTestCase):
|
||||
self.assertEqual(m_dhcp.call_count, 2)
|
||||
m_net.assert_any_call(
|
||||
broadcast='192.168.2.255', interface='eth9', ip='192.168.2.9',
|
||||
- prefix_or_mask='255.255.255.0', router='192.168.2.1')
|
||||
+ prefix_or_mask='255.255.255.0', router='192.168.2.1',
|
||||
+ static_routes=None)
|
||||
self.assertEqual(m_net.call_count, 2)
|
||||
|
||||
|
||||
diff --git a/tests/unittests/test_datasource/test_ec2.py b/tests/unittests/test_datasource/test_ec2.py
|
||||
index 20d59bf..1ec8e00 100644
|
||||
--- a/tests/unittests/test_datasource/test_ec2.py
|
||||
+++ b/tests/unittests/test_datasource/test_ec2.py
|
||||
@@ -538,7 +538,8 @@ class TestEc2(test_helpers.HttprettyTestCase):
|
||||
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')
|
||||
+ prefix_or_mask='255.255.255.0', router='192.168.2.1',
|
||||
+ static_routes=None)
|
||||
self.assertIn('Crawl of metadata service took', self.logs.getvalue())
|
||||
|
||||
|
||||
--
|
||||
cgit v0.10.2
|
||||
|
@ -1,57 +0,0 @@
|
||||
diff --git a/tests/unittests/test_ds_identify.py b/tests/unittests/test_ds_identify.py
|
||||
index 8c18aa1..7575223 100644
|
||||
--- a/tests/unittests/test_ds_identify.py
|
||||
+++ b/tests/unittests/test_ds_identify.py
|
||||
@@ -435,6 +435,14 @@ class TestDsIdentify(DsIdentifyBase):
|
||||
"""Open Telecom identification."""
|
||||
self._test_ds_found('OpenStack-OpenTelekom')
|
||||
|
||||
+ def test_openstack_asset_tag_nova(self):
|
||||
+ """OpenStack identification via asset tag OpenStack Nova."""
|
||||
+ self._test_ds_found('OpenStack-AssetTag-Nova')
|
||||
+
|
||||
+ def test_openstack_asset_tag_copute(self):
|
||||
+ """OpenStack identification via asset tag OpenStack Compute."""
|
||||
+ self._test_ds_found('OpenStack-AssetTag-Compute')
|
||||
+
|
||||
def test_openstack_on_non_intel_is_maybe(self):
|
||||
"""On non-Intel, openstack without dmi info is maybe.
|
||||
|
||||
@@ -759,6 +767,18 @@ VALID_CFG = {
|
||||
'files': {P_CHASSIS_ASSET_TAG: 'OpenTelekomCloud\n'},
|
||||
'mocks': [MOCK_VIRT_IS_XEN],
|
||||
},
|
||||
+ 'OpenStack-AssetTag-Nova': {
|
||||
+ # VMware vSphere can't modify product-name, LP: #1669875
|
||||
+ 'ds': 'OpenStack',
|
||||
+ 'files': {P_CHASSIS_ASSET_TAG: 'OpenStack Nova\n'},
|
||||
+ 'mocks': [MOCK_VIRT_IS_XEN],
|
||||
+ },
|
||||
+ 'OpenStack-AssetTag-Compute': {
|
||||
+ # VMware vSphere can't modify product-name, LP: #1669875
|
||||
+ 'ds': 'OpenStack',
|
||||
+ 'files': {P_CHASSIS_ASSET_TAG: 'OpenStack Compute\n'},
|
||||
+ 'mocks': [MOCK_VIRT_IS_XEN],
|
||||
+ },
|
||||
'OVF-seed': {
|
||||
'ds': 'OVF',
|
||||
'files': {
|
||||
diff --git a/tools/ds-identify b/tools/ds-identify
|
||||
index 6518901..e16708f 100755
|
||||
--- a/tools/ds-identify
|
||||
+++ b/tools/ds-identify
|
||||
@@ -979,6 +979,14 @@ dscheck_OpenStack() {
|
||||
return ${DS_FOUND}
|
||||
fi
|
||||
|
||||
+ # LP: #1669875 : allow identification of OpenStack by asset tag
|
||||
+ if dmi_chassis_asset_tag_matches "$nova"; then
|
||||
+ return ${DS_FOUND}
|
||||
+ fi
|
||||
+ if dmi_chassis_asset_tag_matches "$compute"; then
|
||||
+ return ${DS_FOUND}
|
||||
+ fi
|
||||
+
|
||||
# LP: #1715241 : arch other than intel are not identified properly.
|
||||
case "$DI_UNAME_MACHINE" in
|
||||
i?86|x86_64) :;;
|
@ -1,3 +1,45 @@
|
||||
-------------------------------------------------------------------
|
||||
Thu Sep 26 12:15:50 UTC 2019 - Robert Schweikert <rjschwei@suse.com>
|
||||
|
||||
- Update to cloud-init 19.2 (bsc#1099358)
|
||||
+ Remove, included upstream
|
||||
- cloud-init-detect-nova.diff
|
||||
- cloud-init-add-static-routes.diff
|
||||
+ net: add rfc3442 (classless static routes) to EphemeralDHCP
|
||||
(LP: #1821102)
|
||||
+ templates/ntp.conf.debian.tmpl: fix missing newline for pools
|
||||
(LP: #1836598)
|
||||
+ Support netplan renderer in Arch Linux [Conrad Hoffmann]
|
||||
+ Fix typo in publicly viewable documentation. [David Medberry]
|
||||
+ Add a cdrom size checker for OVF ds to ds-identify
|
||||
[Pengpeng Sun] (LP: #1806701)
|
||||
+ VMWare: Trigger the post customization script via cc_scripts module.
|
||||
[Xiaofeng Wang] (LP: #1833192)
|
||||
+ Cloud-init analyze module: Added ability to analyze boot events.
|
||||
[Sam Gilson]
|
||||
+ Update debian eni network configuration location, retain Ubuntu setting
|
||||
[Janos Lenart]
|
||||
+ net: skip bond interfaces in get_interfaces
|
||||
[Stanislav Makar] (LP: #1812857)
|
||||
+ Fix a couple of issues raised by a coverity scan
|
||||
+ Add missing dsname for Hetzner Cloud datasource [Markus Schade]
|
||||
+ doc: indicate that netplan is default in Ubuntu now
|
||||
+ azure: add region and AZ properties from imds compute location metadata
|
||||
+ sysconfig: support more bonding options [Penghui Liao]
|
||||
+ cloud-init-generator: use libexec path to ds-identify on redhat systems
|
||||
(LP: #1833264)
|
||||
+ tools/build-on-freebsd: update to python3 [Gonéri Le Bouder]
|
||||
+ Allow identification of OpenStack by Asset Tag
|
||||
[Mark T. Voelker] (LP: #1669875)
|
||||
+ Fix spelling error making 'an Ubuntu' consistent. [Brian Murray]
|
||||
+ run-container: centos: comment out the repo mirrorlist [Paride Legovini]
|
||||
+ netplan: update netplan key mappings for gratuitous-arp (LP: #1827238)
|
||||
+ freebsd: fix the name of cloudcfg VARIANT [Gonéri Le Bouder]
|
||||
+ freebsd: ability to grow root file system [Gonéri Le Bouder]
|
||||
+ freebsd: NoCloud data source support [Gonéri Le Bouder] (LP: #1645824)
|
||||
+ Azure: Return static fallback address as if failed to find endpoint
|
||||
[Jason Zions (MSFT)]
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Tue Sep 24 19:50:33 UTC 2019 - Robert Schweikert <rjschwei@suse.com>
|
||||
|
||||
@ -58,7 +100,7 @@ Tue Jun 11 12:37:16 UTC 2019 - Dominique Leuenberger <dimstar@opensuse.org>
|
||||
-------------------------------------------------------------------
|
||||
Fri May 31 12:42:49 UTC 2019 - Robert Schweikert <rjschwei@suse.com>
|
||||
|
||||
- Update to version 19.1 (bsc#1136440)
|
||||
- Update to version 19.1 (bsc#1136440, bsc#1129124)
|
||||
+ Remove, included upstream
|
||||
- fix-default-systemd-unit-dir.patch
|
||||
- cloud-init-sysconf-ethsetup.patch
|
||||
@ -135,6 +177,7 @@ Fri May 31 12:42:49 UTC 2019 - Robert Schweikert <rjschwei@suse.com>
|
||||
+ Enable encrypted_data_bag_secret support for Chef
|
||||
[Eric Williams] (LP: #1817082)
|
||||
+ azure: Filter list of ssh keys pulled from fabric [Jason Zions (MSFT)]
|
||||
CVE-2019-0816
|
||||
+ doc: update merging doc with fixes and some additional details/examples
|
||||
+ tests: integration test failure summary to use traceback if empty error
|
||||
+ This is to fix https://bugs.launchpad.net/cloud-init/+bug/1812676
|
||||
|
@ -18,7 +18,7 @@
|
||||
%global configver 0.7
|
||||
|
||||
Name: cloud-init
|
||||
Version: 19.1
|
||||
Version: 19.2
|
||||
Release: 0
|
||||
License: GPL-3.0 and AGPL-3.0
|
||||
Summary: Cloud node initialization tool
|
||||
@ -27,7 +27,6 @@ Group: System/Management
|
||||
Source0: %{name}-%{version}.tar.gz
|
||||
Source1: rsyslog-cloud-init.cfg
|
||||
|
||||
|
||||
# FIXME
|
||||
# python2 disables SIGPIPE, causing broken pipe errors in shell scripts (bsc#903449)
|
||||
Patch20: cloud-init-python2-sigpipe.patch
|
||||
@ -42,15 +41,11 @@ Patch41: cloud-init-static-net.patch
|
||||
Patch42: cloud-init-ostack-metadat-dencode.patch
|
||||
# FIXME (lp#1812117)
|
||||
Patch43: cloud-init-write-routes.patch
|
||||
# FIXME (lp#1817368)
|
||||
# FIXME (lp#1817368) expected in 19.3
|
||||
Patch47: cloud-init-trigger-udev.patch
|
||||
# FIXME (lp#1669875) patch by mvoelker@launchpad
|
||||
Patch48: cloud-init-detect-nova.diff
|
||||
# FIXME (lp#1821102)
|
||||
Patch49: cloud-init-add-static-routes.diff
|
||||
# FIXME (lp#1843634)
|
||||
# FIXME (lp#1843634) expected in 19.3
|
||||
Patch50: cloud-init-noresolv-merge-no-dns-data.diff
|
||||
# FIXME
|
||||
# FIXME expected in 19.3
|
||||
Patch51: cloud-init-after-wicked.patch
|
||||
|
||||
BuildRequires: fdupes
|
||||
@ -195,8 +190,6 @@ Documentation and examples for cloud-init tools
|
||||
%patch42
|
||||
%patch43
|
||||
%patch47
|
||||
%patch48 -p1
|
||||
%patch49 -p1
|
||||
%patch50 -p1
|
||||
%patch51 -p1
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user