Add flag for DEFROUTE=no in ifcfg files to ignore DHCP gateway
When multiple interfaces are configured with DHCP, and more than one interface receives a gateway from the DHCP server(s), the resulting default gateway on the system is unpredictable. This change adds the "defroute" boolean to the configuration syntax for os-net-config. Any interface type may be marked so that the gateway received from the DHCP server will not be eligible as a default gateway for the system. This only works for ifcfg files, /etc/network/interfaces lacks an equivalent option. Change-Id: Id775f3506b2ec60c9a2833efd49fb8319151c00d Closes-Bug: 1449288
This commit is contained in:
@@ -7,7 +7,20 @@
|
|||||||
{
|
{
|
||||||
"ip_netmask": "192.0.2.1/24"
|
"ip_netmask": "192.0.2.1/24"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"ip_netmask": "0.0.0.0/0",
|
||||||
|
"next_hop": "192.0.2.254",
|
||||||
|
"default": "true"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "interface",
|
||||||
|
"name": "em2",
|
||||||
|
"use_dhcp": true,
|
||||||
|
"defroute": no
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,3 +6,13 @@ network_config:
|
|||||||
addresses:
|
addresses:
|
||||||
-
|
-
|
||||||
ip_netmask: 192.0.2.1/24
|
ip_netmask: 192.0.2.1/24
|
||||||
|
routes:
|
||||||
|
-
|
||||||
|
ip_netmask: 0.0.0.0/0
|
||||||
|
next_hop: 192.0.2.254
|
||||||
|
default: true
|
||||||
|
-
|
||||||
|
type: interface
|
||||||
|
name: em2
|
||||||
|
use_dhcp: true
|
||||||
|
defroute: no
|
||||||
@@ -145,6 +145,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
|
|||||||
data += "HWADDR=%s\n" % base_opt.hwaddr
|
data += "HWADDR=%s\n" % base_opt.hwaddr
|
||||||
if ovs_extra:
|
if ovs_extra:
|
||||||
data += "OVS_EXTRA=\"%s\"\n" % " -- ".join(ovs_extra)
|
data += "OVS_EXTRA=\"%s\"\n" % " -- ".join(ovs_extra)
|
||||||
|
if not base_opt.defroute:
|
||||||
|
data += "DEFROUTE=no\n"
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def _add_routes(self, interface_name, routes=[]):
|
def _add_routes(self, interface_name, routes=[]):
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ class _BaseOpts(object):
|
|||||||
|
|
||||||
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=[],
|
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=[],
|
||||||
routes=[], mtu=1500, primary=False, nic_mapping=None,
|
routes=[], mtu=1500, primary=False, nic_mapping=None,
|
||||||
persist_mapping=False):
|
persist_mapping=False, defroute=True):
|
||||||
numbered_nic_names = _numbered_nics(nic_mapping)
|
numbered_nic_names = _numbered_nics(nic_mapping)
|
||||||
self.hwaddr = None
|
self.hwaddr = None
|
||||||
self.hwname = None
|
self.hwname = None
|
||||||
@@ -152,6 +152,7 @@ class _BaseOpts(object):
|
|||||||
self.addresses = addresses
|
self.addresses = addresses
|
||||||
self.routes = routes
|
self.routes = routes
|
||||||
self.primary = primary
|
self.primary = primary
|
||||||
|
self.defroute = defroute
|
||||||
self.bridge_name = None # internal
|
self.bridge_name = None # internal
|
||||||
self.ovs_port = False # internal
|
self.ovs_port = False # internal
|
||||||
self.primary_interface_name = None # internal
|
self.primary_interface_name = None # internal
|
||||||
@@ -177,6 +178,8 @@ class _BaseOpts(object):
|
|||||||
use_dhcp = strutils.bool_from_string(str(json.get('use_dhcp', False)))
|
use_dhcp = strutils.bool_from_string(str(json.get('use_dhcp', False)))
|
||||||
use_dhcpv6 = strutils.bool_from_string(str(json.get('use_dhcpv6',
|
use_dhcpv6 = strutils.bool_from_string(str(json.get('use_dhcpv6',
|
||||||
False)))
|
False)))
|
||||||
|
defroute = strutils.bool_from_string(str(json.get('defroute',
|
||||||
|
True)))
|
||||||
mtu = json.get('mtu', 1500)
|
mtu = json.get('mtu', 1500)
|
||||||
primary = strutils.bool_from_string(str(json.get('primary', False)))
|
primary = strutils.bool_from_string(str(json.get('primary', False)))
|
||||||
addresses = []
|
addresses = []
|
||||||
@@ -207,10 +210,10 @@ class _BaseOpts(object):
|
|||||||
|
|
||||||
if include_primary:
|
if include_primary:
|
||||||
return (use_dhcp, use_dhcpv6, addresses, routes, mtu, primary,
|
return (use_dhcp, use_dhcpv6, addresses, routes, mtu, primary,
|
||||||
nic_mapping, persist_mapping)
|
nic_mapping, persist_mapping, defroute)
|
||||||
else:
|
else:
|
||||||
return (use_dhcp, use_dhcpv6, addresses, routes, mtu,
|
return (use_dhcp, use_dhcpv6, addresses, routes, mtu,
|
||||||
nic_mapping, persist_mapping)
|
nic_mapping, persist_mapping, defroute)
|
||||||
|
|
||||||
|
|
||||||
class Interface(_BaseOpts):
|
class Interface(_BaseOpts):
|
||||||
@@ -218,10 +221,10 @@ class Interface(_BaseOpts):
|
|||||||
|
|
||||||
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=[],
|
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=[],
|
||||||
routes=[], mtu=1500, primary=False, nic_mapping=None,
|
routes=[], mtu=1500, primary=False, nic_mapping=None,
|
||||||
persist_mapping=False):
|
persist_mapping=False, defroute=True):
|
||||||
super(Interface, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
|
super(Interface, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
|
||||||
routes, mtu, primary, nic_mapping,
|
routes, mtu, primary, nic_mapping,
|
||||||
persist_mapping)
|
persist_mapping, defroute)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_json(json):
|
def from_json(json):
|
||||||
@@ -239,11 +242,11 @@ class Vlan(_BaseOpts):
|
|||||||
|
|
||||||
def __init__(self, device, vlan_id, use_dhcp=False, use_dhcpv6=False,
|
def __init__(self, device, vlan_id, use_dhcp=False, use_dhcpv6=False,
|
||||||
addresses=[], routes=[], mtu=1500, primary=False,
|
addresses=[], routes=[], mtu=1500, primary=False,
|
||||||
nic_mapping=None, persist_mapping=False):
|
nic_mapping=None, persist_mapping=False, defroute=True):
|
||||||
name = 'vlan%i' % vlan_id
|
name = 'vlan%i' % vlan_id
|
||||||
super(Vlan, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
|
super(Vlan, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
|
||||||
routes, mtu, primary, nic_mapping,
|
routes, mtu, primary, nic_mapping,
|
||||||
persist_mapping)
|
persist_mapping, defroute)
|
||||||
self.vlan_id = int(vlan_id)
|
self.vlan_id = int(vlan_id)
|
||||||
|
|
||||||
numbered_nic_names = _numbered_nics(nic_mapping)
|
numbered_nic_names = _numbered_nics(nic_mapping)
|
||||||
@@ -266,10 +269,11 @@ class OvsBridge(_BaseOpts):
|
|||||||
|
|
||||||
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=[],
|
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=[],
|
||||||
routes=[], mtu=1500, members=[], ovs_options=None,
|
routes=[], mtu=1500, members=[], ovs_options=None,
|
||||||
ovs_extra=[], nic_mapping=None, persist_mapping=False):
|
ovs_extra=[], nic_mapping=None, persist_mapping=False,
|
||||||
|
defroute=True):
|
||||||
super(OvsBridge, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
|
super(OvsBridge, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
|
||||||
routes, mtu, False, nic_mapping,
|
routes, mtu, False, nic_mapping,
|
||||||
persist_mapping)
|
persist_mapping, defroute)
|
||||||
self.members = members
|
self.members = members
|
||||||
self.ovs_options = ovs_options
|
self.ovs_options = ovs_options
|
||||||
self.ovs_extra = ovs_extra
|
self.ovs_extra = ovs_extra
|
||||||
@@ -289,7 +293,7 @@ class OvsBridge(_BaseOpts):
|
|||||||
def from_json(json):
|
def from_json(json):
|
||||||
name = _get_required_field(json, 'name', 'OvsBridge')
|
name = _get_required_field(json, 'name', 'OvsBridge')
|
||||||
(use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping,
|
(use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping,
|
||||||
persist_mapping) = _BaseOpts.base_opts_from_json(
|
persist_mapping, defroute) = _BaseOpts.base_opts_from_json(
|
||||||
json, include_primary=False)
|
json, include_primary=False)
|
||||||
ovs_options = json.get('ovs_options')
|
ovs_options = json.get('ovs_options')
|
||||||
ovs_extra = json.get('ovs_extra', [])
|
ovs_extra = json.get('ovs_extra', [])
|
||||||
@@ -309,7 +313,7 @@ class OvsBridge(_BaseOpts):
|
|||||||
addresses=addresses, routes=routes, mtu=mtu,
|
addresses=addresses, routes=routes, mtu=mtu,
|
||||||
members=members, ovs_options=ovs_options,
|
members=members, ovs_options=ovs_options,
|
||||||
ovs_extra=ovs_extra, nic_mapping=nic_mapping,
|
ovs_extra=ovs_extra, nic_mapping=nic_mapping,
|
||||||
persist_mapping=persist_mapping)
|
persist_mapping=persist_mapping, defroute=defroute)
|
||||||
|
|
||||||
|
|
||||||
class OvsBond(_BaseOpts):
|
class OvsBond(_BaseOpts):
|
||||||
@@ -318,10 +322,10 @@ class OvsBond(_BaseOpts):
|
|||||||
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=[],
|
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=[],
|
||||||
routes=[], mtu=1500, primary=False, members=[],
|
routes=[], mtu=1500, primary=False, members=[],
|
||||||
ovs_options=None, ovs_extra=[], nic_mapping=None,
|
ovs_options=None, ovs_extra=[], nic_mapping=None,
|
||||||
persist_mapping=False):
|
persist_mapping=False, defroute=True):
|
||||||
super(OvsBond, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
|
super(OvsBond, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
|
||||||
routes, mtu, primary, nic_mapping,
|
routes, mtu, primary, nic_mapping,
|
||||||
persist_mapping)
|
persist_mapping, defroute)
|
||||||
self.members = members
|
self.members = members
|
||||||
self.ovs_options = ovs_options
|
self.ovs_options = ovs_options
|
||||||
self.ovs_extra = ovs_extra
|
self.ovs_extra = ovs_extra
|
||||||
@@ -339,7 +343,7 @@ class OvsBond(_BaseOpts):
|
|||||||
def from_json(json):
|
def from_json(json):
|
||||||
name = _get_required_field(json, 'name', 'OvsBond')
|
name = _get_required_field(json, 'name', 'OvsBond')
|
||||||
(use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping,
|
(use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping,
|
||||||
persist_mapping) = _BaseOpts.base_opts_from_json(
|
persist_mapping, defroute) = _BaseOpts.base_opts_from_json(
|
||||||
json, include_primary=False)
|
json, include_primary=False)
|
||||||
ovs_options = json.get('ovs_options')
|
ovs_options = json.get('ovs_options')
|
||||||
ovs_extra = json.get('ovs_extra', [])
|
ovs_extra = json.get('ovs_extra', [])
|
||||||
@@ -359,4 +363,4 @@ class OvsBond(_BaseOpts):
|
|||||||
addresses=addresses, routes=routes, mtu=mtu,
|
addresses=addresses, routes=routes, mtu=mtu,
|
||||||
members=members, ovs_options=ovs_options,
|
members=members, ovs_options=ovs_options,
|
||||||
ovs_extra=ovs_extra, nic_mapping=nic_mapping,
|
ovs_extra=ovs_extra, nic_mapping=nic_mapping,
|
||||||
persist_mapping=persist_mapping)
|
persist_mapping=persist_mapping, defroute=defroute)
|
||||||
|
|||||||
@@ -260,6 +260,27 @@ BOOTPROTO=none
|
|||||||
self.assertEqual(_OVS_BOND_DHCP,
|
self.assertEqual(_OVS_BOND_DHCP,
|
||||||
self.get_interface_config('bond0'))
|
self.get_interface_config('bond0'))
|
||||||
|
|
||||||
|
def test_interface_defroute(self):
|
||||||
|
interface1 = objects.Interface('em1')
|
||||||
|
interface2 = objects.Interface('em2', defroute=False)
|
||||||
|
self.provider.add_interface(interface1)
|
||||||
|
self.provider.add_interface(interface2)
|
||||||
|
em1_config = """# This file is autogenerated by os-net-config
|
||||||
|
DEVICE=em1
|
||||||
|
ONBOOT=yes
|
||||||
|
HOTPLUG=no
|
||||||
|
BOOTPROTO=none
|
||||||
|
"""
|
||||||
|
em2_config = """# This file is autogenerated by os-net-config
|
||||||
|
DEVICE=em2
|
||||||
|
ONBOOT=yes
|
||||||
|
HOTPLUG=no
|
||||||
|
BOOTPROTO=none
|
||||||
|
DEFROUTE=no
|
||||||
|
"""
|
||||||
|
self.assertEqual(em1_config, self.get_interface_config('em1'))
|
||||||
|
self.assertEqual(em2_config, self.get_interface_config('em2'))
|
||||||
|
|
||||||
|
|
||||||
class TestIfcfgNetConfigApply(base.TestCase):
|
class TestIfcfgNetConfigApply(base.TestCase):
|
||||||
|
|
||||||
|
|||||||
@@ -94,6 +94,20 @@ class TestInterface(base.TestCase):
|
|||||||
self.assertEqual("em1", interface.name)
|
self.assertEqual("em1", interface.name)
|
||||||
self.assertEqual(True, interface.use_dhcp)
|
self.assertEqual(True, interface.use_dhcp)
|
||||||
|
|
||||||
|
def test_from_json_defroute(self):
|
||||||
|
data = '{"type": "interface", "name": "em1", "use_dhcp": true}'
|
||||||
|
interface1 = objects.object_from_json(json.loads(data))
|
||||||
|
data = """{
|
||||||
|
"type": "interface",
|
||||||
|
"name": "em1",
|
||||||
|
"use_dhcp": true,
|
||||||
|
"defroute": false
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
interface2 = objects.object_from_json(json.loads(data))
|
||||||
|
self.assertEqual(True, interface1.defroute)
|
||||||
|
self.assertEqual(False, interface2.defroute)
|
||||||
|
|
||||||
def test_from_json_dhcp_nic1(self):
|
def test_from_json_dhcp_nic1(self):
|
||||||
def dummy_numbered_nics(nic_mapping=None):
|
def dummy_numbered_nics(nic_mapping=None):
|
||||||
return {"nic1": "em3"}
|
return {"nic1": "em3"}
|
||||||
|
|||||||
Reference in New Issue
Block a user