diff options
author | Ryan Harper <ryan.harper@canonical.com> | 2018-12-11 17:24:11 +0000 |
---|---|---|
committer | Server Team CI Bot <josh.powers+server-team-bot@canonical.com> | 2018-12-11 17:24:11 +0000 |
commit | 6aef6c7d402b17ebc04516a088a91f8e6ed86510 (patch) | |
tree | 91b7b816fa74cd67ca9a5c456599db42c4a6fa44 | |
parent | a4007d063f96b82545aa678ef2cb472ea3b48b1e (diff) | |
download | vyos-cloud-init-6aef6c7d402b17ebc04516a088a91f8e6ed86510.tar.gz vyos-cloud-init-6aef6c7d402b17ebc04516a088a91f8e6ed86510.zip |
net: render 'metric' values in per-subnet routes
It is possible to have a metric value in a per-subnet route.
This is currently missing in all renderers. Update each
renderer to emit the correct metric value from the config.
LP: #1805871
-rw-r--r-- | cloudinit/net/eni.py | 29 | ||||
-rw-r--r-- | cloudinit/net/netplan.py | 6 | ||||
-rw-r--r-- | cloudinit/net/sysconfig.py | 25 | ||||
-rw-r--r-- | tests/unittests/test_net.py | 50 |
4 files changed, 83 insertions, 27 deletions
diff --git a/cloudinit/net/eni.py b/cloudinit/net/eni.py index c6f631a9..64236320 100644 --- a/cloudinit/net/eni.py +++ b/cloudinit/net/eni.py @@ -371,22 +371,23 @@ class Renderer(renderer.Renderer): 'gateway': 'gw', 'metric': 'metric', } + + default_gw = '' if route['network'] == '0.0.0.0' and route['netmask'] == '0.0.0.0': - default_gw = " default gw %s" % route['gateway'] - content.append(up + default_gw + or_true) - content.append(down + default_gw + or_true) + default_gw = ' default' elif route['network'] == '::' and route['prefix'] == 0: - # ipv6! - default_gw = " -A inet6 default gw %s" % route['gateway'] - content.append(up + default_gw + or_true) - content.append(down + default_gw + or_true) - else: - route_line = "" - for k in ['network', 'netmask', 'gateway', 'metric']: - if k in route: - route_line += " %s %s" % (mapping[k], route[k]) - content.append(up + route_line + or_true) - content.append(down + route_line + or_true) + default_gw = ' -A inet6 default' + + route_line = '' + for k in ['network', 'netmask', 'gateway', 'metric']: + if default_gw and k in ['network', 'netmask']: + continue + if k == 'gateway': + route_line += '%s %s %s' % (default_gw, mapping[k], route[k]) + elif k in route: + route_line += ' %s %s' % (mapping[k], route[k]) + content.append(up + route_line + or_true) + content.append(down + route_line + or_true) return content def _render_iface(self, iface, render_hwaddress=False): diff --git a/cloudinit/net/netplan.py b/cloudinit/net/netplan.py index bc1087f9..21517fda 100644 --- a/cloudinit/net/netplan.py +++ b/cloudinit/net/netplan.py @@ -114,13 +114,13 @@ def _extract_addresses(config, entry, ifname): for route in subnet.get('routes', []): to_net = "%s/%s" % (route.get('network'), route.get('prefix')) - route = { + new_route = { 'via': route.get('gateway'), 'to': to_net, } if 'metric' in route: - route.update({'metric': route.get('metric', 100)}) - routes.append(route) + new_route.update({'metric': route.get('metric', 100)}) + routes.append(new_route) addresses.append(addr) diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py index 9c16d3a7..17293e1d 100644 --- a/cloudinit/net/sysconfig.py +++ b/cloudinit/net/sysconfig.py @@ -156,13 +156,23 @@ class Route(ConfigMap): _quote_value(gateway_value))) buf.write("%s=%s\n" % ('NETMASK' + str(reindex), _quote_value(netmask_value))) + metric_key = 'METRIC' + index + if metric_key in self._conf: + metric_value = str(self._conf['METRIC' + index]) + buf.write("%s=%s\n" % ('METRIC' + str(reindex), + _quote_value(metric_value))) elif proto == "ipv6" and self.is_ipv6_route(address_value): netmask_value = str(self._conf['NETMASK' + index]) gateway_value = str(self._conf['GATEWAY' + index]) - buf.write("%s/%s via %s dev %s\n" % (address_value, - netmask_value, - gateway_value, - self._route_name)) + metric_value = ( + 'metric ' + str(self._conf['METRIC' + index]) + if 'METRIC' + index in self._conf else '') + buf.write( + "%s/%s via %s %s dev %s\n" % (address_value, + netmask_value, + gateway_value, + metric_value, + self._route_name)) return buf.getvalue() @@ -370,6 +380,9 @@ class Renderer(renderer.Renderer): else: iface_cfg['GATEWAY'] = subnet['gateway'] + if 'metric' in subnet: + iface_cfg['METRIC'] = subnet['metric'] + if 'dns_search' in subnet: iface_cfg['DOMAIN'] = ' '.join(subnet['dns_search']) @@ -414,15 +427,19 @@ class Renderer(renderer.Renderer): else: iface_cfg['GATEWAY'] = route['gateway'] route_cfg.has_set_default_ipv4 = True + if 'metric' in route: + iface_cfg['METRIC'] = route['metric'] else: gw_key = 'GATEWAY%s' % route_cfg.last_idx nm_key = 'NETMASK%s' % route_cfg.last_idx addr_key = 'ADDRESS%s' % route_cfg.last_idx + metric_key = 'METRIC%s' % route_cfg.last_idx route_cfg.last_idx += 1 # add default routes only to ifcfg files, not # to route-* or route6-* for (old_key, new_key) in [('gateway', gw_key), + ('metric', metric_key), ('netmask', nm_key), ('network', addr_key)]: if old_key in route: diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py index 8e383739..195f261c 100644 --- a/tests/unittests/test_net.py +++ b/tests/unittests/test_net.py @@ -488,8 +488,8 @@ NETWORK_CONFIGS = { address 192.168.21.3/24 dns-nameservers 8.8.8.8 8.8.4.4 dns-search barley.maas sach.maas - post-up route add default gw 65.61.151.37 || true - pre-down route del default gw 65.61.151.37 || true + post-up route add default gw 65.61.151.37 metric 10000 || true + pre-down route del default gw 65.61.151.37 metric 10000 || true """).rstrip(' '), 'expected_netplan': textwrap.dedent(""" network: @@ -513,7 +513,8 @@ NETWORK_CONFIGS = { - barley.maas - sach.maas routes: - - to: 0.0.0.0/0 + - metric: 10000 + to: 0.0.0.0/0 via: 65.61.151.37 set-name: eth99 """).rstrip(' '), @@ -537,6 +538,7 @@ NETWORK_CONFIGS = { HWADDR=c0:d6:9f:2c:e8:80 IPADDR=192.168.21.3 NETMASK=255.255.255.0 + METRIC=10000 NM_CONTROLLED=no ONBOOT=yes TYPE=Ethernet @@ -561,7 +563,7 @@ NETWORK_CONFIGS = { - gateway: 65.61.151.37 netmask: 0.0.0.0 network: 0.0.0.0 - metric: 2 + metric: 10000 - type: physical name: eth1 mac_address: "cf:d6:af:48:e8:80" @@ -1161,6 +1163,13 @@ pre-down route del -net 10.0.0.0 netmask 255.0.0.0 gw 11.0.0.1 metric 3 || true - gateway: 192.168.0.3 netmask: 255.255.255.0 network: 10.1.3.0 + - gateway: 2001:67c:1562:1 + network: 2001:67c:1 + netmask: ffff:ffff:0 + - gateway: 3001:67c:1562:1 + network: 3001:67c:1 + netmask: ffff:ffff:0 + metric: 10000 - type: static address: 192.168.1.2/24 - type: static @@ -1197,6 +1206,11 @@ pre-down route del -net 10.0.0.0 netmask 255.0.0.0 gw 11.0.0.1 metric 3 || true routes: - to: 10.1.3.0/24 via: 192.168.0.3 + - to: 2001:67c:1/32 + via: 2001:67c:1562:1 + - metric: 10000 + to: 3001:67c:1/32 + via: 3001:67c:1562:1 """), 'yaml-v2': textwrap.dedent(""" version: 2 @@ -1228,6 +1242,11 @@ pre-down route del -net 10.0.0.0 netmask 255.0.0.0 gw 11.0.0.1 metric 3 || true routes: - to: 10.1.3.0/24 via: 192.168.0.3 + - to: 2001:67c:1562:8007::1/64 + via: 2001:67c:1562:8007::aac:40b2 + - metric: 10000 + to: 3001:67c:1562:8007::1/64 + via: 3001:67c:1562:8007::aac:40b2 """), 'expected_netplan-v2': textwrap.dedent(""" network: @@ -1249,6 +1268,11 @@ pre-down route del -net 10.0.0.0 netmask 255.0.0.0 gw 11.0.0.1 metric 3 || true routes: - to: 10.1.3.0/24 via: 192.168.0.3 + - to: 2001:67c:1562:8007::1/64 + via: 2001:67c:1562:8007::aac:40b2 + - metric: 10000 + to: 3001:67c:1562:8007::1/64 + via: 3001:67c:1562:8007::aac:40b2 ethernets: eth0: match: @@ -1349,6 +1373,10 @@ pre-down route del -net 10.0.0.0 netmask 255.0.0.0 gw 11.0.0.1 metric 3 || true USERCTL=no """), 'route6-bond0': textwrap.dedent("""\ + # Created by cloud-init on instance boot automatically, do not edit. + # + 2001:67c:1/ffff:ffff:0 via 2001:67c:1562:1 dev bond0 + 3001:67c:1/ffff:ffff:0 via 3001:67c:1562:1 metric 10000 dev bond0 """), 'route-bond0': textwrap.dedent("""\ ADDRESS0=10.1.3.0 @@ -1879,14 +1907,24 @@ class TestRhelSysConfigRendering(CiTestCase): return dir2dict(dir) def _compare_files_to_expected(self, expected, found): + + def _try_load(f): + ''' Attempt to load shell content, otherwise return as-is ''' + try: + return util.load_shell_content(f) + except ValueError: + pass + # route6- * files aren't shell content, but iproute2 params + return f + orig_maxdiff = self.maxDiff expected_d = dict( - (os.path.join(self.scripts_dir, k), util.load_shell_content(v)) + (os.path.join(self.scripts_dir, k), _try_load(v)) for k, v in expected.items()) # only compare the files in scripts_dir scripts_found = dict( - (k, util.load_shell_content(v)) for k, v in found.items() + (k, _try_load(v)) for k, v in found.items() if k.startswith(self.scripts_dir)) try: self.maxDiff = None |