cloud-init-dev team mailing list archive
-
cloud-init-dev team
-
Mailing list archive
-
Message #04484
[Merge] ~raharper/cloud-init:sysconfig-handle-global-static-routes into cloud-init:master
Ryan Harper has proposed merging ~raharper/cloud-init:sysconfig-handle-global-static-routes into cloud-init:master.
Requested reviews:
cloud-init commiters (cloud-init-dev)
For more details, see:
https://code.launchpad.net/~raharper/cloud-init/+git/cloud-init/+merge/342102
--
Your team cloud-init commiters is requested to review the proposed merge of ~raharper/cloud-init:sysconfig-handle-global-static-routes into cloud-init:master.
diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
index 39d89c4..87fb96e 100644
--- a/cloudinit/net/sysconfig.py
+++ b/cloudinit/net/sysconfig.py
@@ -550,6 +550,26 @@ class Renderer(renderer.Renderer):
cls._render_subnet_routes(iface_cfg, route_cfg, iface_subnets)
@classmethod
+ def _render_static_routes(cls, base_sysconf_dir, network_state, contents):
+ """Render global routes into /etc/sysconfig/static-routes file"""
+ sr_content = []
+ for route in network_state.iter_routes():
+ ipr_mapping = {'network': 'net', 'netmask': 'netmask',
+ 'metric': 'metric', 'gateway': 'gw'}
+ route_fmt = ['any']
+ # use a specific order to generate a net-tools/route command
+ for kw in ['network', 'netmask', 'metric', 'gateway']:
+ if kw in route:
+ route_fmt.extend([ipr_mapping[kw], str(route[kw])])
+ if route_fmt:
+ sr_content.append(" ".join(route_fmt))
+
+ if sr_content:
+ static_routes_fn = os.path.join(base_sysconf_dir, 'static-routes')
+ sr_content = [_make_header()] + sr_content
+ contents[static_routes_fn] = "\n".join(sr_content) + "\n"
+
+ @classmethod
def _render_sysconfig(cls, base_sysconf_dir, network_state):
'''Given state, return /etc/sysconfig files + contents'''
iface_contents = {}
@@ -576,6 +596,8 @@ class Renderer(renderer.Renderer):
iface_cfg.routes.to_string("ipv4")
contents[iface_cfg.routes.path_ipv6] = \
iface_cfg.routes.to_string("ipv6")
+ cls._render_static_routes(base_sysconf_dir, network_state, contents)
+
return contents
def render_network_state(self, network_state, target=None):
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
index c12a487..8861353 100644
--- a/tests/unittests/test_net.py
+++ b/tests/unittests/test_net.py
@@ -21,6 +21,7 @@ import gzip
import io
import json
import os
+import re
import textwrap
import yaml
@@ -1575,6 +1576,7 @@ class TestSysConfigRendering(CiTestCase):
def _render_and_read(self, network_config=None, state=None, dir=None):
if dir is None:
dir = self.tmp_dir()
+ self.render_dir = dir
if network_config:
ns = network_state.parse_net_config_data(network_config)
@@ -1582,6 +1584,7 @@ class TestSysConfigRendering(CiTestCase):
ns = state
else:
raise ValueError("Expected data or state, got neither")
+ self.network_state = ns
renderer = sysconfig.Renderer()
renderer.render_network_state(ns, dir)
@@ -1610,6 +1613,32 @@ class TestSysConfigRendering(CiTestCase):
if missing:
raise AssertionError("Missing headers in: %s" % missing)
+ def _assert_static_routes(self, network_config, found):
+ static_routes = list(self.network_state.iter_routes())
+ snet_keys = ['network', 'netmask', 'metric', 'gateway']
+ if len(static_routes) > 0:
+ self.assertIn('/etc/sysconfig/static-routes', found)
+ sroute_fn = os.path.join(self.render_dir,
+ 'etc/sysconfig/static-routes')
+ self.assertTrue(os.path.exists(sroute_fn))
+ sroute_content = util.load_file(sroute_fn)
+ for line in [line for line in sroute_content.splitlines()
+ if line.startswith('any')]:
+ m = re.search(r'any\snet\s(?P<network>\S+)\s'
+ r'netmask\s(?P<netmask>\S+)\s'
+ r'metric\s(?P<metric>\S+)\s'
+ r'gw\s(?P<gateway>\S+)', line)
+ found_route = m.groupdict('')
+ if 'metric' in found_route:
+ found_route['metric'] = int(found_route['metric'])
+ found_match = []
+ for sroute in static_routes:
+ missing = [key for key in snet_keys
+ if found_route[key] != sroute[key]]
+ if not missing:
+ found_match.append(sroute)
+ self.assertEqual(1, len(found_match))
+
@mock.patch("cloudinit.net.sys_dev_path")
@mock.patch("cloudinit.net.read_sys_net")
@mock.patch("cloudinit.net.get_devicelist")
@@ -1789,45 +1818,59 @@ USERCTL=no
def test_bond_config(self):
entry = NETWORK_CONFIGS['bond']
- found = self._render_and_read(network_config=yaml.load(entry['yaml']))
+ network_config = yaml.load(entry['yaml'])
+ found = self._render_and_read(network_config=network_config)
self._compare_files_to_expected(entry['expected_sysconfig'], found)
self._assert_headers(found)
+ self._assert_static_routes(network_config, found)
def test_vlan_config(self):
entry = NETWORK_CONFIGS['vlan']
- found = self._render_and_read(network_config=yaml.load(entry['yaml']))
+ network_config = yaml.load(entry['yaml'])
+ found = self._render_and_read(network_config=network_config)
self._compare_files_to_expected(entry['expected_sysconfig'], found)
self._assert_headers(found)
+ self._assert_static_routes(network_config, found)
def test_bridge_config(self):
entry = NETWORK_CONFIGS['bridge']
- found = self._render_and_read(network_config=yaml.load(entry['yaml']))
+ network_config = yaml.load(entry['yaml'])
+ found = self._render_and_read(network_config=network_config)
self._compare_files_to_expected(entry['expected_sysconfig'], found)
self._assert_headers(found)
+ self._assert_static_routes(network_config, found)
def test_manual_config(self):
entry = NETWORK_CONFIGS['manual']
- found = self._render_and_read(network_config=yaml.load(entry['yaml']))
+ network_config = yaml.load(entry['yaml'])
+ found = self._render_and_read(network_config=network_config)
self._compare_files_to_expected(entry['expected_sysconfig'], found)
self._assert_headers(found)
+ self._assert_static_routes(network_config, found)
def test_all_config(self):
entry = NETWORK_CONFIGS['all']
- found = self._render_and_read(network_config=yaml.load(entry['yaml']))
+ network_config = yaml.load(entry['yaml'])
+ found = self._render_and_read(network_config=network_config)
self._compare_files_to_expected(entry['expected_sysconfig'], found)
self._assert_headers(found)
+ self._assert_static_routes(network_config, found)
def test_small_config(self):
entry = NETWORK_CONFIGS['small']
- found = self._render_and_read(network_config=yaml.load(entry['yaml']))
+ network_config = yaml.load(entry['yaml'])
+ found = self._render_and_read(network_config=network_config)
self._compare_files_to_expected(entry['expected_sysconfig'], found)
self._assert_headers(found)
+ self._assert_static_routes(network_config, found)
def test_v4_and_v6_static_config(self):
entry = NETWORK_CONFIGS['v4_and_v6_static']
- found = self._render_and_read(network_config=yaml.load(entry['yaml']))
+ network_config = yaml.load(entry['yaml'])
+ found = self._render_and_read(network_config=network_config)
self._compare_files_to_expected(entry['expected_sysconfig'], found)
self._assert_headers(found)
+ self._assert_static_routes(network_config, found)
class TestEniNetRendering(CiTestCase):
Follow ups