summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Harper <ryan.harper@canonical.com>2018-12-11 17:24:11 +0000
committerServer Team CI Bot <josh.powers+server-team-bot@canonical.com>2018-12-11 17:24:11 +0000
commit6aef6c7d402b17ebc04516a088a91f8e6ed86510 (patch)
tree91b7b816fa74cd67ca9a5c456599db42c4a6fa44
parenta4007d063f96b82545aa678ef2cb472ea3b48b1e (diff)
downloadvyos-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.py29
-rw-r--r--cloudinit/net/netplan.py6
-rw-r--r--cloudinit/net/sysconfig.py25
-rw-r--r--tests/unittests/test_net.py50
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