← Back to team overview

cloud-init-dev team mailing list archive

[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