|
|
69219a |
From a22a059e36ec56d0d6d7e2a63ccff56d6c19f9d6 Mon Sep 17 00:00:00 2001
|
|
|
69219a |
From: Eduardo Otubo <otubo@redhat.com>
|
|
|
69219a |
Date: Mon, 4 May 2020 12:39:55 +0200
|
|
|
69219a |
Subject: [PATCH 2/6] net/sysconfig: Handle default route setup for dhcp
|
|
|
69219a |
configured NICs
|
|
|
69219a |
|
|
|
69219a |
RH-Author: Eduardo Otubo <otubo@redhat.com>
|
|
|
69219a |
Message-id: <20200327152826.13343-3-otubo@redhat.com>
|
|
|
69219a |
Patchwork-id: 94457
|
|
|
69219a |
O-Subject: [RHEL-8.1.z/RHEL-8.2.z cloud-init PATCHv2 2/6] net/sysconfig: Handle default route setup for dhcp configured NICs
|
|
|
69219a |
Bugzilla: 1811753
|
|
|
69219a |
RH-Acked-by: Cathy Avery <cavery@redhat.com>
|
|
|
69219a |
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
|
|
|
69219a |
|
|
|
69219a |
commit 3acaacc92be1b7d7bad099c323d6e923664a8afa
|
|
|
69219a |
Author: Robert Schweikert <rjschwei@suse.com>
|
|
|
69219a |
Date: Tue Mar 12 21:08:22 2019 +0000
|
|
|
69219a |
|
|
|
69219a |
net/sysconfig: Handle default route setup for dhcp configured NICs
|
|
|
69219a |
|
|
|
69219a |
When the network configuration has a default route configured and
|
|
|
69219a |
another network device that is configured with dhcp, SUSE sysconfig
|
|
|
69219a |
output should not accept the default route provided by the dhcp
|
|
|
69219a |
server.
|
|
|
69219a |
|
|
|
69219a |
LP: #1812117
|
|
|
69219a |
|
|
|
69219a |
Signed-off-by: Eduardo Otubo <otubo@redhat.com>
|
|
|
69219a |
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
69219a |
---
|
|
|
69219a |
cloudinit/net/network_state.py | 41 +++++++++++++++++++++------
|
|
|
69219a |
cloudinit/net/sysconfig.py | 31 +++++++++++++++------
|
|
|
69219a |
tests/unittests/test_net.py | 63 ++++++++++++++++++++++++++++++++++++++++++
|
|
|
69219a |
3 files changed, 118 insertions(+), 17 deletions(-)
|
|
|
69219a |
|
|
|
69219a |
diff --git a/cloudinit/net/network_state.py b/cloudinit/net/network_state.py
|
|
|
69219a |
index 539b76d..4d19f56 100644
|
|
|
69219a |
--- a/cloudinit/net/network_state.py
|
|
|
69219a |
+++ b/cloudinit/net/network_state.py
|
|
|
69219a |
@@ -148,6 +148,7 @@ class NetworkState(object):
|
|
|
69219a |
self._network_state = copy.deepcopy(network_state)
|
|
|
69219a |
self._version = version
|
|
|
69219a |
self.use_ipv6 = network_state.get('use_ipv6', False)
|
|
|
69219a |
+ self._has_default_route = None
|
|
|
69219a |
|
|
|
69219a |
@property
|
|
|
69219a |
def config(self):
|
|
|
69219a |
@@ -157,14 +158,6 @@ class NetworkState(object):
|
|
|
69219a |
def version(self):
|
|
|
69219a |
return self._version
|
|
|
69219a |
|
|
|
69219a |
- def iter_routes(self, filter_func=None):
|
|
|
69219a |
- for route in self._network_state.get('routes', []):
|
|
|
69219a |
- if filter_func is not None:
|
|
|
69219a |
- if filter_func(route):
|
|
|
69219a |
- yield route
|
|
|
69219a |
- else:
|
|
|
69219a |
- yield route
|
|
|
69219a |
-
|
|
|
69219a |
@property
|
|
|
69219a |
def dns_nameservers(self):
|
|
|
69219a |
try:
|
|
|
69219a |
@@ -179,6 +172,12 @@ class NetworkState(object):
|
|
|
69219a |
except KeyError:
|
|
|
69219a |
return []
|
|
|
69219a |
|
|
|
69219a |
+ @property
|
|
|
69219a |
+ def has_default_route(self):
|
|
|
69219a |
+ if self._has_default_route is None:
|
|
|
69219a |
+ self._has_default_route = self._maybe_has_default_route()
|
|
|
69219a |
+ return self._has_default_route
|
|
|
69219a |
+
|
|
|
69219a |
def iter_interfaces(self, filter_func=None):
|
|
|
69219a |
ifaces = self._network_state.get('interfaces', {})
|
|
|
69219a |
for iface in six.itervalues(ifaces):
|
|
|
69219a |
@@ -188,6 +187,32 @@ class NetworkState(object):
|
|
|
69219a |
if filter_func(iface):
|
|
|
69219a |
yield iface
|
|
|
69219a |
|
|
|
69219a |
+ def iter_routes(self, filter_func=None):
|
|
|
69219a |
+ for route in self._network_state.get('routes', []):
|
|
|
69219a |
+ if filter_func is not None:
|
|
|
69219a |
+ if filter_func(route):
|
|
|
69219a |
+ yield route
|
|
|
69219a |
+ else:
|
|
|
69219a |
+ yield route
|
|
|
69219a |
+
|
|
|
69219a |
+ def _maybe_has_default_route(self):
|
|
|
69219a |
+ for route in self.iter_routes():
|
|
|
69219a |
+ if self._is_default_route(route):
|
|
|
69219a |
+ return True
|
|
|
69219a |
+ for iface in self.iter_interfaces():
|
|
|
69219a |
+ for subnet in iface.get('subnets', []):
|
|
|
69219a |
+ for route in subnet.get('routes', []):
|
|
|
69219a |
+ if self._is_default_route(route):
|
|
|
69219a |
+ return True
|
|
|
69219a |
+ return False
|
|
|
69219a |
+
|
|
|
69219a |
+ def _is_default_route(self, route):
|
|
|
69219a |
+ default_nets = ('::', '0.0.0.0')
|
|
|
69219a |
+ return (
|
|
|
69219a |
+ route.get('prefix') == 0
|
|
|
69219a |
+ and route.get('network') in default_nets
|
|
|
69219a |
+ )
|
|
|
69219a |
+
|
|
|
69219a |
|
|
|
69219a |
@six.add_metaclass(CommandHandlerMeta)
|
|
|
69219a |
class NetworkStateInterpreter(object):
|
|
|
69219a |
diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
|
|
|
69219a |
index 52bb848..5c1b4eb 100644
|
|
|
69219a |
--- a/cloudinit/net/sysconfig.py
|
|
|
69219a |
+++ b/cloudinit/net/sysconfig.py
|
|
|
69219a |
@@ -320,7 +320,7 @@ class Renderer(renderer.Renderer):
|
|
|
69219a |
iface_cfg[new_key] = old_value
|
|
|
69219a |
|
|
|
69219a |
@classmethod
|
|
|
69219a |
- def _render_subnets(cls, iface_cfg, subnets):
|
|
|
69219a |
+ def _render_subnets(cls, iface_cfg, subnets, has_default_route):
|
|
|
69219a |
# setting base values
|
|
|
69219a |
iface_cfg['BOOTPROTO'] = 'none'
|
|
|
69219a |
|
|
|
69219a |
@@ -329,6 +329,7 @@ class Renderer(renderer.Renderer):
|
|
|
69219a |
mtu_key = 'MTU'
|
|
|
69219a |
subnet_type = subnet.get('type')
|
|
|
69219a |
if subnet_type == 'dhcp6':
|
|
|
69219a |
+ # TODO need to set BOOTPROTO to dhcp6 on SUSE
|
|
|
69219a |
iface_cfg['IPV6INIT'] = True
|
|
|
69219a |
iface_cfg['DHCPV6C'] = True
|
|
|
69219a |
elif subnet_type in ['dhcp4', 'dhcp']:
|
|
|
69219a |
@@ -372,9 +373,9 @@ class Renderer(renderer.Renderer):
|
|
|
69219a |
ipv6_index = -1
|
|
|
69219a |
for i, subnet in enumerate(subnets, start=len(iface_cfg.children)):
|
|
|
69219a |
subnet_type = subnet.get('type')
|
|
|
69219a |
- if subnet_type == 'dhcp6':
|
|
|
69219a |
- continue
|
|
|
69219a |
- elif subnet_type in ['dhcp4', 'dhcp']:
|
|
|
69219a |
+ if subnet_type in ['dhcp', 'dhcp4', 'dhcp6']:
|
|
|
69219a |
+ if has_default_route and iface_cfg['BOOTPROTO'] != 'none':
|
|
|
69219a |
+ iface_cfg['DHCLIENT_SET_DEFAULT_ROUTE'] = False
|
|
|
69219a |
continue
|
|
|
69219a |
elif subnet_type == 'static':
|
|
|
69219a |
if subnet_is_ipv6(subnet):
|
|
|
69219a |
@@ -440,6 +441,8 @@ class Renderer(renderer.Renderer):
|
|
|
69219a |
# TODO(harlowja): add validation that no other iface has
|
|
|
69219a |
# also provided the default route?
|
|
|
69219a |
iface_cfg['DEFROUTE'] = True
|
|
|
69219a |
+ if iface_cfg['BOOTPROTO'] in ('dhcp', 'dhcp4', 'dhcp6'):
|
|
|
69219a |
+ iface_cfg['DHCLIENT_SET_DEFAULT_ROUTE'] = True
|
|
|
69219a |
if 'gateway' in route:
|
|
|
69219a |
if is_ipv6 or is_ipv6_addr(route['gateway']):
|
|
|
69219a |
iface_cfg['IPV6_DEFAULTGW'] = route['gateway']
|
|
|
69219a |
@@ -490,7 +493,9 @@ class Renderer(renderer.Renderer):
|
|
|
69219a |
iface_cfg = iface_contents[iface_name]
|
|
|
69219a |
route_cfg = iface_cfg.routes
|
|
|
69219a |
|
|
|
69219a |
- cls._render_subnets(iface_cfg, iface_subnets)
|
|
|
69219a |
+ cls._render_subnets(
|
|
|
69219a |
+ iface_cfg, iface_subnets, network_state.has_default_route
|
|
|
69219a |
+ )
|
|
|
69219a |
cls._render_subnet_routes(iface_cfg, route_cfg, iface_subnets)
|
|
|
69219a |
|
|
|
69219a |
@classmethod
|
|
|
69219a |
@@ -515,7 +520,9 @@ class Renderer(renderer.Renderer):
|
|
|
69219a |
|
|
|
69219a |
iface_subnets = iface.get("subnets", [])
|
|
|
69219a |
route_cfg = iface_cfg.routes
|
|
|
69219a |
- cls._render_subnets(iface_cfg, iface_subnets)
|
|
|
69219a |
+ cls._render_subnets(
|
|
|
69219a |
+ iface_cfg, iface_subnets, network_state.has_default_route
|
|
|
69219a |
+ )
|
|
|
69219a |
cls._render_subnet_routes(iface_cfg, route_cfg, iface_subnets)
|
|
|
69219a |
|
|
|
69219a |
# iter_interfaces on network-state is not sorted to produce
|
|
|
69219a |
@@ -544,7 +551,9 @@ class Renderer(renderer.Renderer):
|
|
|
69219a |
|
|
|
69219a |
iface_subnets = iface.get("subnets", [])
|
|
|
69219a |
route_cfg = iface_cfg.routes
|
|
|
69219a |
- cls._render_subnets(iface_cfg, iface_subnets)
|
|
|
69219a |
+ cls._render_subnets(
|
|
|
69219a |
+ iface_cfg, iface_subnets, network_state.has_default_route
|
|
|
69219a |
+ )
|
|
|
69219a |
cls._render_subnet_routes(iface_cfg, route_cfg, iface_subnets)
|
|
|
69219a |
|
|
|
69219a |
@staticmethod
|
|
|
69219a |
@@ -603,7 +612,9 @@ class Renderer(renderer.Renderer):
|
|
|
69219a |
|
|
|
69219a |
iface_subnets = iface.get("subnets", [])
|
|
|
69219a |
route_cfg = iface_cfg.routes
|
|
|
69219a |
- cls._render_subnets(iface_cfg, iface_subnets)
|
|
|
69219a |
+ cls._render_subnets(
|
|
|
69219a |
+ iface_cfg, iface_subnets, network_state.has_default_route
|
|
|
69219a |
+ )
|
|
|
69219a |
cls._render_subnet_routes(iface_cfg, route_cfg, iface_subnets)
|
|
|
69219a |
|
|
|
69219a |
@classmethod
|
|
|
69219a |
@@ -615,7 +626,9 @@ class Renderer(renderer.Renderer):
|
|
|
69219a |
iface_cfg.kind = 'infiniband'
|
|
|
69219a |
iface_subnets = iface.get("subnets", [])
|
|
|
69219a |
route_cfg = iface_cfg.routes
|
|
|
69219a |
- cls._render_subnets(iface_cfg, iface_subnets)
|
|
|
69219a |
+ cls._render_subnets(
|
|
|
69219a |
+ iface_cfg, iface_subnets, network_state.has_default_route
|
|
|
69219a |
+ )
|
|
|
69219a |
cls._render_subnet_routes(iface_cfg, route_cfg, iface_subnets)
|
|
|
69219a |
|
|
|
69219a |
@classmethod
|
|
|
69219a |
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
|
|
|
69219a |
index 4224301..a975678 100644
|
|
|
69219a |
--- a/tests/unittests/test_net.py
|
|
|
69219a |
+++ b/tests/unittests/test_net.py
|
|
|
69219a |
@@ -546,6 +546,7 @@ NETWORK_CONFIGS = {
|
|
|
69219a |
BOOTPROTO=dhcp
|
|
|
69219a |
DEFROUTE=yes
|
|
|
69219a |
DEVICE=eth99
|
|
|
69219a |
+ DHCLIENT_SET_DEFAULT_ROUTE=yes
|
|
|
69219a |
DNS1=8.8.8.8
|
|
|
69219a |
DNS2=8.8.4.4
|
|
|
69219a |
DOMAIN="barley.maas sach.maas"
|
|
|
69219a |
@@ -913,6 +914,7 @@ pre-down route del -net 10.0.0.0 netmask 255.0.0.0 gw 11.0.0.1 metric 3 || true
|
|
|
69219a |
'ifcfg-bond0.200': textwrap.dedent("""\
|
|
|
69219a |
BOOTPROTO=dhcp
|
|
|
69219a |
DEVICE=bond0.200
|
|
|
69219a |
+ DHCLIENT_SET_DEFAULT_ROUTE=no
|
|
|
69219a |
ONBOOT=yes
|
|
|
69219a |
PHYSDEV=bond0
|
|
|
69219a |
TYPE=Ethernet
|
|
|
69219a |
@@ -996,6 +998,7 @@ pre-down route del -net 10.0.0.0 netmask 255.0.0.0 gw 11.0.0.1 metric 3 || true
|
|
|
69219a |
'ifcfg-eth5': textwrap.dedent("""\
|
|
|
69219a |
BOOTPROTO=dhcp
|
|
|
69219a |
DEVICE=eth5
|
|
|
69219a |
+ DHCLIENT_SET_DEFAULT_ROUTE=no
|
|
|
69219a |
HWADDR=98:bb:9f:2c:e8:8a
|
|
|
69219a |
ONBOOT=no
|
|
|
69219a |
TYPE=Ethernet
|
|
|
69219a |
@@ -1624,6 +1627,23 @@ CONFIG_V1_SIMPLE_SUBNET = {
|
|
|
69219a |
'type': 'static'}],
|
|
|
69219a |
'type': 'physical'}]}
|
|
|
69219a |
|
|
|
69219a |
+CONFIG_V1_MULTI_IFACE = {
|
|
|
69219a |
+ 'version': 1,
|
|
|
69219a |
+ 'config': [{'type': 'physical',
|
|
|
69219a |
+ 'mtu': 1500,
|
|
|
69219a |
+ 'subnets': [{'type': 'static',
|
|
|
69219a |
+ 'netmask': '255.255.240.0',
|
|
|
69219a |
+ 'routes': [{'netmask': '0.0.0.0',
|
|
|
69219a |
+ 'network': '0.0.0.0',
|
|
|
69219a |
+ 'gateway': '51.68.80.1'}],
|
|
|
69219a |
+ 'address': '51.68.89.122',
|
|
|
69219a |
+ 'ipv4': True}],
|
|
|
69219a |
+ 'mac_address': 'fa:16:3e:25:b4:59',
|
|
|
69219a |
+ 'name': 'eth0'},
|
|
|
69219a |
+ {'type': 'physical',
|
|
|
69219a |
+ 'mtu': 9000,
|
|
|
69219a |
+ 'subnets': [{'type': 'dhcp4'}],
|
|
|
69219a |
+ 'mac_address': 'fa:16:3e:b1:ca:29', 'name': 'eth1'}]}
|
|
|
69219a |
|
|
|
69219a |
DEFAULT_DEV_ATTRS = {
|
|
|
69219a |
'eth1000': {
|
|
|
69219a |
@@ -2088,6 +2108,49 @@ USERCTL=no
|
|
|
69219a |
"""
|
|
|
69219a |
self.assertEqual(expected, found[nspath + 'ifcfg-interface0'])
|
|
|
69219a |
|
|
|
69219a |
+ def test_network_config_v1_multi_iface_samples(self):
|
|
|
69219a |
+ ns = network_state.parse_net_config_data(CONFIG_V1_MULTI_IFACE)
|
|
|
69219a |
+ render_dir = self.tmp_path("render")
|
|
|
69219a |
+ os.makedirs(render_dir)
|
|
|
69219a |
+ renderer = self._get_renderer()
|
|
|
69219a |
+ renderer.render_network_state(ns, target=render_dir)
|
|
|
69219a |
+ found = dir2dict(render_dir)
|
|
|
69219a |
+ nspath = '/etc/sysconfig/network-scripts/'
|
|
|
69219a |
+ self.assertNotIn(nspath + 'ifcfg-lo', found.keys())
|
|
|
69219a |
+ expected_i1 = """\
|
|
|
69219a |
+# Created by cloud-init on instance boot automatically, do not edit.
|
|
|
69219a |
+#
|
|
|
69219a |
+BOOTPROTO=none
|
|
|
69219a |
+DEFROUTE=yes
|
|
|
69219a |
+DEVICE=eth0
|
|
|
69219a |
+GATEWAY=51.68.80.1
|
|
|
69219a |
+HWADDR=fa:16:3e:25:b4:59
|
|
|
69219a |
+IPADDR=51.68.89.122
|
|
|
69219a |
+MTU=1500
|
|
|
69219a |
+NETMASK=255.255.240.0
|
|
|
69219a |
+NM_CONTROLLED=no
|
|
|
69219a |
+ONBOOT=yes
|
|
|
69219a |
+STARTMODE=auto
|
|
|
69219a |
+TYPE=Ethernet
|
|
|
69219a |
+USERCTL=no
|
|
|
69219a |
+"""
|
|
|
69219a |
+ self.assertEqual(expected_i1, found[nspath + 'ifcfg-eth0'])
|
|
|
69219a |
+ expected_i2 = """\
|
|
|
69219a |
+# Created by cloud-init on instance boot automatically, do not edit.
|
|
|
69219a |
+#
|
|
|
69219a |
+BOOTPROTO=dhcp
|
|
|
69219a |
+DEVICE=eth1
|
|
|
69219a |
+DHCLIENT_SET_DEFAULT_ROUTE=no
|
|
|
69219a |
+HWADDR=fa:16:3e:b1:ca:29
|
|
|
69219a |
+MTU=9000
|
|
|
69219a |
+NM_CONTROLLED=no
|
|
|
69219a |
+ONBOOT=yes
|
|
|
69219a |
+STARTMODE=auto
|
|
|
69219a |
+TYPE=Ethernet
|
|
|
69219a |
+USERCTL=no
|
|
|
69219a |
+"""
|
|
|
69219a |
+ self.assertEqual(expected_i2, found[nspath + 'ifcfg-eth1'])
|
|
|
69219a |
+
|
|
|
69219a |
def test_config_with_explicit_loopback(self):
|
|
|
69219a |
ns = network_state.parse_net_config_data(CONFIG_V1_EXPLICIT_LOOPBACK)
|
|
|
69219a |
render_dir = self.tmp_path("render")
|
|
|
69219a |
--
|
|
|
69219a |
1.8.3.1
|
|
|
69219a |
|