aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--os_net_config/impl_ifcfg.py44
-rw-r--r--os_net_config/objects.py49
-rw-r--r--os_net_config/tests/test_impl_eni.py50
-rw-r--r--os_net_config/tests/test_impl_ifcfg.py66
-rw-r--r--os_net_config/tests/test_objects.py56
5 files changed, 245 insertions, 20 deletions
diff --git a/os_net_config/impl_ifcfg.py b/os_net_config/impl_ifcfg.py
index e8dbd46..41d5f8c 100644
--- a/os_net_config/impl_ifcfg.py
+++ b/os_net_config/impl_ifcfg.py
@@ -16,6 +16,7 @@
import glob
import logging
+import os
import re
import os_net_config
@@ -25,6 +26,9 @@ from os_net_config import utils
logger = logging.getLogger(__name__)
+# Import the raw NetConfig object so we can call its methods
+netconfig = os_net_config.NetConfig()
+
def ifcfg_config_path(name):
return "/etc/sysconfig/network-scripts/ifcfg-%s" % name
@@ -55,6 +59,39 @@ def cleanup_pattern():
return "/etc/sysconfig/network-scripts/ifcfg-*"
+def dhclient_path():
+ if os.path.exists("/usr/sbin/dhclient"):
+ return "/usr/sbin/dhclient"
+ elif os.path.exists("/sbin/dhclient"):
+ return "/sbin/dhclient"
+ else:
+ raise RuntimeError("Could not find dhclient")
+
+
+def stop_dhclient_process(interface):
+ """Stop a DHCP process when no longer needed.
+
+ This method exists so that it may be stubbed out for unit tests.
+ :param interface: The interface on which to stop dhclient.
+ """
+ pid_file = '/var/run/dhclient-%s.pid' % (interface)
+ try:
+ dhclient = dhclient_path()
+ except RuntimeError as err:
+ logger.info('Exception when stopping dhclient: %s' % err)
+ return
+
+ if os.path.exists(pid_file):
+ msg = 'Stopping %s on interface %s' % (dhclient, interface)
+ netconfig.execute(msg, dhclient, '-r', '-pf',
+ pid_file, interface)
+ try:
+ os.unlink(pid_file)
+ except OSError as err:
+ logger.error('Could not remove dhclient pid file \'%s\': %s' %
+ (pid_file, err))
+
+
class IfcfgNetConfig(os_net_config.NetConfig):
"""Configure network interfaces using the ifcfg format."""
@@ -639,6 +676,7 @@ class IfcfgNetConfig(os_net_config.NetConfig):
ivs_interfaces = [] # ivs internal ports
nfvswitch_interfaces = [] # nfvswitch physical interfaces
nfvswitch_internal_ifaces = [] # nfvswitch internal/management ports
+ stop_dhclient_interfaces = []
for interface_name, iface_data in self.interface_data.iteritems():
route_data = self.route_data.get(interface_name, '')
@@ -662,6 +700,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
update_files[interface_path] = iface_data
update_files[route_path] = route_data
update_files[route6_path] = route6_data
+ if "BOOTPROTO=dhcp" not in iface_data:
+ stop_dhclient_interfaces.append(interface_name)
else:
logger.info('No changes required for interface: %s' %
interface_name)
@@ -890,6 +930,10 @@ class IfcfgNetConfig(os_net_config.NetConfig):
for bridge in restart_bridges:
self.ifup(bridge, iftype='bridge')
+ # If dhclient is running and dhcp not set, stop dhclient
+ for interface in stop_dhclient_interfaces:
+ stop_dhclient_process(interface)
+
for interface in restart_interfaces:
self.ifup(interface)
diff --git a/os_net_config/objects.py b/os_net_config/objects.py
index 8d7ee4c..8d52e73 100644
--- a/os_net_config/objects.py
+++ b/os_net_config/objects.py
@@ -25,6 +25,8 @@ logger = logging.getLogger(__name__)
_MAPPED_NICS = None
+DEFAULT_OVS_BRIDGE_FAIL_MODE = 'standalone'
+
class InvalidConfigException(ValueError):
pass
@@ -131,6 +133,12 @@ def _mapped_nics(nic_mapping=None):
return _MAPPED_NICS
+def format_ovs_extra(obj, templates):
+ """Map OVS object properties into a string to be used for ovs_extra."""
+
+ return [t.format(name=obj.name) for t in templates or []]
+
+
class Route(object):
"""Base class for network routes."""
@@ -391,11 +399,11 @@ class OvsBridge(_BaseOpts):
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
routes=None, mtu=None, members=None, ovs_options=None,
ovs_extra=None, nic_mapping=None, persist_mapping=False,
- defroute=True, dhclient_args=None, dns_servers=None):
+ defroute=True, dhclient_args=None, dns_servers=None,
+ fail_mode=None):
addresses = addresses or []
routes = routes or []
members = members or []
- ovs_extra = ovs_extra or []
dns_servers = dns_servers or []
super(OvsBridge, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
routes, mtu, False, nic_mapping,
@@ -403,7 +411,10 @@ class OvsBridge(_BaseOpts):
dhclient_args, dns_servers)
self.members = members
self.ovs_options = ovs_options
- self.ovs_extra = ovs_extra
+ ovs_extra = ovs_extra or []
+ if fail_mode:
+ ovs_extra.append('set bridge {name} fail_mode=%s' % fail_mode)
+ self.ovs_extra = format_ovs_extra(self, ovs_extra)
for member in self.members:
member.bridge_name = name
if not isinstance(member, OvsTunnel):
@@ -425,7 +436,8 @@ class OvsBridge(_BaseOpts):
dhclient_args, dns_servers) = _BaseOpts.base_opts_from_json(
json, include_primary=False)
ovs_options = json.get('ovs_options')
- ovs_extra = json.get('ovs_extra', [])
+ ovs_extra = json.get('ovs_extra')
+ fail_mode = json.get('ovs_fail_mode', DEFAULT_OVS_BRIDGE_FAIL_MODE)
members = []
# members
@@ -443,7 +455,8 @@ class OvsBridge(_BaseOpts):
members=members, ovs_options=ovs_options,
ovs_extra=ovs_extra, nic_mapping=nic_mapping,
persist_mapping=persist_mapping, defroute=defroute,
- dhclient_args=dhclient_args, dns_servers=dns_servers)
+ dhclient_args=dhclient_args, dns_servers=dns_servers,
+ fail_mode=fail_mode)
class OvsUserBridge(_BaseOpts):
@@ -452,7 +465,8 @@ class OvsUserBridge(_BaseOpts):
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
routes=None, mtu=None, members=None, ovs_options=None,
ovs_extra=None, nic_mapping=None, persist_mapping=False,
- defroute=True, dhclient_args=None, dns_servers=None):
+ defroute=True, dhclient_args=None, dns_servers=None,
+ fail_mode=None):
super(OvsUserBridge, self).__init__(name, use_dhcp, use_dhcpv6,
addresses, routes, mtu, False,
nic_mapping, persist_mapping,
@@ -460,7 +474,10 @@ class OvsUserBridge(_BaseOpts):
dns_servers)
self.members = members or []
self.ovs_options = ovs_options
- self.ovs_extra = ovs_extra or []
+ ovs_extra = ovs_extra or []
+ if fail_mode:
+ ovs_extra.append('set bridge {name} fail_mode=%s' % fail_mode)
+ self.ovs_extra = format_ovs_extra(self, ovs_extra)
for member in self.members:
member.bridge_name = name
if not isinstance(member, OvsTunnel) and \
@@ -484,7 +501,8 @@ class OvsUserBridge(_BaseOpts):
dhclient_args, dns_servers) = _BaseOpts.base_opts_from_json(
json, include_primary=False)
ovs_options = json.get('ovs_options')
- ovs_extra = json.get('ovs_extra', [])
+ ovs_extra = json.get('ovs_extra')
+ fail_mode = json.get('ovs_fail_mode', DEFAULT_OVS_BRIDGE_FAIL_MODE)
members = []
# members
@@ -503,7 +521,7 @@ class OvsUserBridge(_BaseOpts):
ovs_extra=ovs_extra, nic_mapping=nic_mapping,
persist_mapping=persist_mapping,
defroute=defroute, dhclient_args=dhclient_args,
- dns_servers=dns_servers)
+ dns_servers=dns_servers, fail_mode=fail_mode)
class LinuxBridge(_BaseOpts):
@@ -808,7 +826,6 @@ class OvsBond(_BaseOpts):
addresses = addresses or []
routes = routes or []
members = members or []
- ovs_extra = ovs_extra or []
dns_servers = dns_servers or []
super(OvsBond, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
routes, mtu, primary, nic_mapping,
@@ -816,7 +833,7 @@ class OvsBond(_BaseOpts):
dns_servers)
self.members = members
self.ovs_options = ovs_options
- self.ovs_extra = ovs_extra
+ self.ovs_extra = format_ovs_extra(self, ovs_extra)
for member in self.members:
if member.primary:
if self.primary_interface_name:
@@ -870,7 +887,6 @@ class OvsTunnel(_BaseOpts):
ovs_extra=None):
addresses = addresses or []
routes = routes or []
- ovs_extra = ovs_extra or []
dns_servers = dns_servers or []
super(OvsTunnel, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
routes, mtu, primary, nic_mapping,
@@ -878,7 +894,7 @@ class OvsTunnel(_BaseOpts):
dhclient_args, dns_servers)
self.tunnel_type = tunnel_type
self.ovs_options = ovs_options or []
- self.ovs_extra = ovs_extra or []
+ self.ovs_extra = format_ovs_extra(self, ovs_extra)
@staticmethod
def from_json(json):
@@ -902,7 +918,6 @@ class OvsPatchPort(_BaseOpts):
ovs_options=None, ovs_extra=None):
addresses = addresses or []
routes = routes or []
- ovs_extra = ovs_extra or []
dns_servers = dns_servers or []
super(OvsPatchPort, self).__init__(name, use_dhcp, use_dhcpv6,
addresses, routes, mtu, primary,
@@ -912,7 +927,7 @@ class OvsPatchPort(_BaseOpts):
self.bridge_name = bridge_name
self.peer = peer
self.ovs_options = ovs_options or []
- self.ovs_extra = ovs_extra or []
+ self.ovs_extra = format_ovs_extra(self, ovs_extra)
@staticmethod
def from_json(json):
@@ -967,7 +982,7 @@ class OvsDpdkPort(_BaseOpts):
dns_servers)
self.members = members or []
self.ovs_options = ovs_options or []
- self.ovs_extra = ovs_extra or []
+ self.ovs_extra = format_ovs_extra(self, ovs_extra)
self.driver = driver
@staticmethod
@@ -1024,7 +1039,7 @@ class OvsDpdkBond(_BaseOpts):
defroute, dhclient_args, dns_servers)
self.members = members or []
self.ovs_options = ovs_options
- self.ovs_extra = ovs_extra or []
+ self.ovs_extra = format_ovs_extra(self, ovs_extra)
for member in self.members:
if member.primary:
diff --git a/os_net_config/tests/test_impl_eni.py b/os_net_config/tests/test_impl_eni.py
index 5d3bb8c..e445ed4 100644
--- a/os_net_config/tests/test_impl_eni.py
+++ b/os_net_config/tests/test_impl_eni.py
@@ -66,13 +66,18 @@ iface br0 inet dhcp
pre-up ip addr flush dev eth0
"""
+_OVS_BRIDGE_DHCP_STANDALONE = _OVS_BRIDGE_DHCP + \
+ " ovs_extra set bridge br0 fail_mode=standalone\n"
+
+_OVS_BRIDGE_DHCP_SECURE = _OVS_BRIDGE_DHCP + \
+ " ovs_extra set bridge br0 fail_mode=secure\n"
+
_OVS_BRIDGE_DHCP_PRIMARY_INTERFACE = _OVS_BRIDGE_DHCP + \
" ovs_extra set bridge br0 other-config:hwaddr=a1:b2:c3:d4:e5\n"
-
_OVS_BRIDGE_DHCP_OVS_EXTRA = _OVS_BRIDGE_DHCP + \
" ovs_extra set bridge br0 other-config:hwaddr=a1:b2:c3:d4:e5" + \
- " -- br-set-external-id br-ctlplane bridge-id br-ctlplane\n"
+ " -- br-set-external-id br0 bridge-id br0\n"
_VLAN_NO_IP = """auto vlan5
@@ -188,6 +193,28 @@ class TestENINetConfig(base.TestCase):
self.assertEqual(_OVS_PORT_IFACE, self.get_interface_config())
self.assertEqual(_OVS_BRIDGE_DHCP, self.provider.bridges['br0'])
+ def test_network_ovs_bridge_with_standalone_fail_mode(self):
+ interface = self._default_interface()
+ bridge = objects.OvsBridge('br0', use_dhcp=True,
+ members=[interface],
+ fail_mode='standalone')
+ self.provider.add_bridge(bridge)
+ self.provider.add_interface(interface)
+ self.assertEqual(_OVS_PORT_IFACE, self.get_interface_config())
+ self.assertEqual(_OVS_BRIDGE_DHCP_STANDALONE,
+ self.provider.bridges['br0'])
+
+ def test_network_ovs_bridge_with_secure_fail_mode(self):
+ interface = self._default_interface()
+ bridge = objects.OvsBridge('br0', use_dhcp=True,
+ members=[interface],
+ fail_mode='secure')
+ self.provider.add_bridge(bridge)
+ self.provider.add_interface(interface)
+ self.assertEqual(_OVS_PORT_IFACE, self.get_interface_config())
+ self.assertEqual(_OVS_BRIDGE_DHCP_SECURE,
+ self.provider.bridges['br0'])
+
def test_network_ovs_bridge_with_dhcp_and_primary_interface(self):
def test_interface_mac(name):
@@ -210,7 +237,24 @@ class TestENINetConfig(base.TestCase):
self.stubs.Set(utils, 'interface_mac', test_interface_mac)
interface = objects.Interface(self.if_name, primary=True)
- ovs_extra = "br-set-external-id br-ctlplane bridge-id br-ctlplane"
+ ovs_extra = "br-set-external-id br0 bridge-id br0"
+ bridge = objects.OvsBridge('br0', use_dhcp=True,
+ members=[interface],
+ ovs_extra=[ovs_extra])
+ self.provider.add_bridge(bridge)
+ self.provider.add_interface(interface)
+ self.assertEqual(_OVS_PORT_IFACE, self.get_interface_config())
+ self.assertEqual(_OVS_BRIDGE_DHCP_OVS_EXTRA,
+ self.provider.bridges['br0'])
+
+ def test_network_ovs_bridge_with_dhcp_and_primary_with_ovs_format(self):
+
+ def test_interface_mac(name):
+ return "a1:b2:c3:d4:e5"
+ self.stubs.Set(utils, 'interface_mac', test_interface_mac)
+
+ interface = objects.Interface(self.if_name, primary=True)
+ ovs_extra = "br-set-external-id {name} bridge-id {name}"
bridge = objects.OvsBridge('br0', use_dhcp=True,
members=[interface],
ovs_extra=[ovs_extra])
diff --git a/os_net_config/tests/test_impl_ifcfg.py b/os_net_config/tests/test_impl_ifcfg.py
index 2e79aa1..3a5a7db 100644
--- a/os_net_config/tests/test_impl_ifcfg.py
+++ b/os_net_config/tests/test_impl_ifcfg.py
@@ -143,6 +143,13 @@ OVSBOOTPROTO=dhcp
OVSDHCPINTERFACES="em1"
"""
+_OVS_BRIDGE_DHCP_STANDALONE = _OVS_BRIDGE_DHCP + \
+ "OVS_EXTRA=\"set bridge br-ctlplane fail_mode=standalone\"\n"
+
+_OVS_BRIDGE_DHCP_SECURE = _OVS_BRIDGE_DHCP + \
+ "OVS_EXTRA=\"set bridge br-ctlplane fail_mode=secure\"\n"
+
+
_LINUX_BRIDGE_DHCP = """# This file is autogenerated by os-net-config
DEVICE=br-ctlplane
ONBOOT=yes
@@ -499,6 +506,28 @@ class TestIfcfgNetConfig(base.TestCase):
self.assertEqual(_OVS_BRIDGE_DHCP,
self.provider.bridge_data['br-ctlplane'])
+ def test_network_ovs_bridge_with_standalone_fail_mode(self):
+ interface = objects.Interface('em1')
+ bridge = objects.OvsBridge('br-ctlplane', use_dhcp=True,
+ members=[interface],
+ fail_mode='standalone')
+ self.provider.add_interface(interface)
+ self.provider.add_bridge(bridge)
+ self.assertEqual(_OVS_INTERFACE, self.get_interface_config())
+ self.assertEqual(_OVS_BRIDGE_DHCP_STANDALONE,
+ self.provider.bridge_data['br-ctlplane'])
+
+ def test_network_ovs_bridge_with_secure_fail_mode(self):
+ interface = objects.Interface('em1')
+ bridge = objects.OvsBridge('br-ctlplane', use_dhcp=True,
+ members=[interface],
+ fail_mode='secure')
+ self.provider.add_interface(interface)
+ self.provider.add_bridge(bridge)
+ self.assertEqual(_OVS_INTERFACE, self.get_interface_config())
+ self.assertEqual(_OVS_BRIDGE_DHCP_SECURE,
+ self.provider.bridge_data['br-ctlplane'])
+
def test_network_linux_bridge_with_dhcp(self):
interface = objects.Interface('em1')
bridge = objects.LinuxBridge('br-ctlplane', use_dhcp=True,
@@ -577,6 +606,22 @@ class TestIfcfgNetConfig(base.TestCase):
self.assertEqual(_OVS_BRIDGE_DHCP_OVS_EXTRA,
self.provider.bridge_data['br-ctlplane'])
+ def test_network_ovs_bridge_with_dhcp_primary_interface_with_format(self):
+ def test_interface_mac(name):
+ return "a1:b2:c3:d4:e5"
+ self.stubs.Set(utils, 'interface_mac', test_interface_mac)
+
+ interface = objects.Interface('em1', primary=True)
+ ovs_extra = "br-set-external-id {name} bridge-id {name}"
+ bridge = objects.OvsBridge('br-ctlplane', use_dhcp=True,
+ members=[interface],
+ ovs_extra=[ovs_extra])
+ self.provider.add_interface(interface)
+ self.provider.add_bridge(bridge)
+ self.assertEqual(_OVS_INTERFACE, self.get_interface_config())
+ self.assertEqual(_OVS_BRIDGE_DHCP_OVS_EXTRA,
+ self.provider.bridge_data['br-ctlplane'])
+
def test_network_ivs_with_uplink_and_interface(self):
interface = objects.Interface('em1')
v4_addr = objects.Address('172.16.2.7/24')
@@ -870,6 +915,7 @@ class TestIfcfgNetConfigApply(base.TestCase):
self.temp_cleanup_file = tempfile.NamedTemporaryFile(delete=False)
self.ifup_interface_names = []
self.ovs_appctl_cmds = []
+ self.stop_dhclient_interfaces = []
def test_ifcfg_path(name):
return self.temp_ifcfg_file.name
@@ -891,6 +937,11 @@ class TestIfcfgNetConfigApply(base.TestCase):
return self.temp_cleanup_file.name
self.stubs.Set(impl_ifcfg, 'cleanup_pattern', test_cleanup_pattern)
+ def test_stop_dhclient_process(interface):
+ self.stop_dhclient_interfaces.append(interface)
+ self.stubs.Set(impl_ifcfg, 'stop_dhclient_process',
+ test_stop_dhclient_process)
+
def test_execute(*args, **kwargs):
if args[0] == '/sbin/ifup':
self.ifup_interface_names.append(args[1])
@@ -943,6 +994,21 @@ class TestIfcfgNetConfigApply(base.TestCase):
route_data = utils.get_file_data(self.temp_route_file.name)
self.assertEqual("", route_data)
+ def test_dhclient_stop_on_iface_activate(self):
+ self.stop_dhclient_interfaces = []
+ v4_addr = objects.Address('192.168.1.2/24')
+ interface = objects.Interface('em1', addresses=[v4_addr])
+ interface2 = objects.Interface('em2', use_dhcp=True)
+ interface3 = objects.Interface('em3', use_dhcp=False)
+ self.provider.add_interface(interface)
+ self.provider.add_interface(interface2)
+ self.provider.add_interface(interface3)
+ self.provider.apply()
+ # stop dhclient on em1 due to static IP and em3 due to no IP
+ self.assertIn('em1', self.stop_dhclient_interfaces)
+ self.assertIn('em3', self.stop_dhclient_interfaces)
+ self.assertNotIn('em2', self.stop_dhclient_interfaces)
+
def test_apply_noactivate(self):
interface = objects.Interface('em1')
bridge = objects.OvsBridge('br-ctlplane', use_dhcp=True,
diff --git a/os_net_config/tests/test_objects.py b/os_net_config/tests/test_objects.py
index 3911ef3..ca2dd47 100644
--- a/os_net_config/tests/test_objects.py
+++ b/os_net_config/tests/test_objects.py
@@ -646,6 +646,44 @@ class TestOvsTunnel(base.TestCase):
["ovs extra"],
tun0.ovs_extra)
+ def test_ovs_extra_formatting(self):
+ data = """{
+"type": "ovs_bridge",
+"name": "br-foo",
+"ovs_extra": [
+ "set bridge {name} something"
+],
+"members": [{
+ "type": "ovs_tunnel",
+ "name": "tun0",
+ "tunnel_type": "gre",
+ "ovs_options": [
+ "remote_ip=192.168.1.1"
+ ],
+ "ovs_extra": [
+ "ovs extra",
+ "ovs {name} extra"
+ ]
+}]
+}
+"""
+ bridge = objects.object_from_json(json.loads(data))
+ self.assertEqual("br-foo", bridge.name)
+ self.assertEqual(["set bridge br-foo something",
+ "set bridge br-foo fail_mode=standalone"],
+ bridge.ovs_extra)
+ tun0 = bridge.members[0]
+ self.assertEqual("tun0", tun0.name)
+ self.assertFalse(tun0.ovs_port)
+ self.assertEqual("br-foo", tun0.bridge_name)
+ self.assertEqual("gre", tun0.tunnel_type)
+ self.assertEqual(
+ ["options:remote_ip=192.168.1.1"],
+ tun0.ovs_options)
+ self.assertEqual(
+ ["ovs extra", "ovs tun0 extra"],
+ tun0.ovs_extra)
+
class TestOvsPatchPort(base.TestCase):
@@ -662,6 +700,24 @@ class TestOvsPatchPort(base.TestCase):
self.assertEqual("br-ex", patch_port.bridge_name)
self.assertEqual("br-ex-patch", patch_port.peer)
+ def test_from_json_with_extra(self):
+ data = """{
+"type": "ovs_patch_port",
+"name": "br-pub-patch",
+"bridge_name": "br-ex",
+"peer": "br-ex-patch",
+"ovs_extra": [
+ "ovs {name} extra"
+]
+}
+"""
+ patch_port = objects.object_from_json(json.loads(data))
+ self.assertEqual(["ovs br-pub-patch extra"],
+ patch_port.ovs_extra)
+ self.assertEqual("br-pub-patch", patch_port.name)
+ self.assertEqual("br-ex", patch_port.bridge_name)
+ self.assertEqual("br-ex-patch", patch_port.peer)
+
class TestIbInterface(base.TestCase):