aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Sneddon <dsneddon@redhat.com>2015-08-28 00:22:27 -0700
committerDan Sneddon <dsneddon@redhat.com>2015-10-05 15:40:12 -0700
commit62bc734ad540cde0cf47b863db686b328d575d33 (patch)
tree1c7ef8896c7b58c20cbdfc77d5ccd98d968630be
parent2497f596be89f3f6cfb1431fba68a0a599879e40 (diff)
Add support for Linux Bonding to os-net-config ifcfg
This change adds support for Linux Bonding to the impl_ifcfg in os-net-config. This change adds support for configuring Linux Bonds using the Bonding module rather than Open vSwitch. Most of the options for Linux Bonds are the same as OVS, with the exception of bonding_options instead of ovs_options. Change-Id: If8c6de1554234277843de9fac58536dd5b0a941b
-rw-r--r--etc/os-net-config/samples/linux_bond.yaml13
-rw-r--r--os_net_config/__init__.py11
-rw-r--r--os_net_config/impl_ifcfg.py47
-rw-r--r--os_net_config/objects.py57
-rw-r--r--os_net_config/tests/test_impl_ifcfg.py18
-rw-r--r--os_net_config/tests/test_objects.py58
6 files changed, 204 insertions, 0 deletions
diff --git a/etc/os-net-config/samples/linux_bond.yaml b/etc/os-net-config/samples/linux_bond.yaml
new file mode 100644
index 0000000..566c747
--- /dev/null
+++ b/etc/os-net-config/samples/linux_bond.yaml
@@ -0,0 +1,13 @@
+network_config:
+ -
+ type: linux_bond
+ name: bond1
+ use_dhcp: true
+ bonding_options: "mode=active-backup"
+ members:
+ -
+ type: interface
+ name: em1
+ -
+ type: interface
+ name: em2
diff --git a/os_net_config/__init__.py b/os_net_config/__init__.py
index 8c9b70d..489b27a 100644
--- a/os_net_config/__init__.py
+++ b/os_net_config/__init__.py
@@ -56,6 +56,10 @@ class NetConfig(object):
self.add_bond(obj)
for member in obj.members:
self.add_object(member)
+ elif isinstance(obj, objects.LinuxBond):
+ self.add_linux_bond(obj)
+ for member in obj.members:
+ self.add_object(member)
def add_interface(self, interface):
"""Add an Interface object to the net config object.
@@ -85,6 +89,13 @@ class NetConfig(object):
"""
raise NotImplemented("add_bond is not implemented.")
+ def add_linux_bond(self, bond):
+ """Add a LinuxBond object to the net config object.
+
+ :param bridge: The LinuxBond object to add.
+ """
+ raise NotImplemented("add_linuxbond is not implemented.")
+
def apply(self, cleanup=False):
"""Apply the network configuration.
diff --git a/os_net_config/impl_ifcfg.py b/os_net_config/impl_ifcfg.py
index 81a1df4..3d05f9c 100644
--- a/os_net_config/impl_ifcfg.py
+++ b/os_net_config/impl_ifcfg.py
@@ -50,7 +50,9 @@ class IfcfgNetConfig(os_net_config.NetConfig):
self.interface_data = {}
self.route_data = {}
self.bridge_data = {}
+ self.linuxbond_data = {}
self.member_names = {}
+ self.bond_slaves = {}
self.renamed_interfaces = {}
self.bond_primary_ifaces = {}
logger.info('Ifcfg net config provider created.')
@@ -122,7 +124,24 @@ class IfcfgNetConfig(os_net_config.NetConfig):
if base_opt.ovs_options:
data += "OVS_OPTIONS=\"%s\"\n" % base_opt.ovs_options
ovs_extra.extend(base_opt.ovs_extra)
+ elif isinstance(base_opt, objects.LinuxBond):
+ if base_opt.primary_interface_name:
+ primary_name = base_opt.primary_interface_name
+ primary_mac = utils.interface_mac(primary_name)
+ data += "MACADDR=\"%s\"\n" % primary_mac
+ if base_opt.use_dhcp:
+ data += "BOOTPROTO=dhcp\n"
+ if base_opt.members:
+ members = [member.name for member in base_opt.members]
+ self.member_names[base_opt.name] = members
+ for member in members:
+ self.bond_slaves[member] = base_opt.name
+ if base_opt.bonding_options:
+ data += "BONDING_OPTS=\"%s\"\n" % base_opt.bonding_options
else:
+ if base_opt.name in self.bond_slaves:
+ data += "MASTER=%s\n" % self.bond_slaves[base_opt.name]
+ data += "SLAVE=yes\n"
if base_opt.use_dhcp:
data += "BOOTPROTO=dhcp\n"
elif not base_opt.addresses:
@@ -233,6 +252,19 @@ class IfcfgNetConfig(os_net_config.NetConfig):
if bond.routes:
self._add_routes(bond.name, bond.routes)
+ def add_linux_bond(self, bond):
+ """Add a LinuxBond object to the net config object.
+
+ :param bond: The LinuxBond object to add.
+ """
+ logger.info('adding linux bond: %s' % bond.name)
+ data = self._add_common(bond)
+ logger.debug('bond data: %s' % data)
+ self.interface_data[bond.name] = data
+ self.linuxbond_data[bond.name] = data
+ if bond.routes:
+ self._add_routes(bond.name, bond.routes)
+
def apply(self, cleanup=False, activate=True):
"""Apply the network configuration.
@@ -283,6 +315,21 @@ class IfcfgNetConfig(os_net_config.NetConfig):
update_files[bridge_route_path] = route_data
logger.info('No changes required for bridge: %s' % bridge_name)
+ for bond_name, bond_data in self.linuxbond_data.iteritems():
+ route_data = self.route_data.get(bond_name, '')
+ bond_path = self.root_dir + bridge_config_path(bond_name)
+ bond_route_path = self.root_dir + route_config_path(bond_name)
+ all_file_names.append(bond_path)
+ all_file_names.append(bond_route_path)
+ if (utils.diff(bond_path, bond_data) or
+ utils.diff(bond_route_path, route_data)):
+ restart_interfaces.append(bond_name)
+ restart_interfaces.extend(self.child_members(bond_name))
+ update_files[bond_path] = bond_data
+ update_files[bond_route_path] = route_data
+ logger.info('No changes required for linux bond: %s' %
+ bond_name)
+
if cleanup:
for ifcfg_file in glob.iglob(cleanup_pattern()):
if ifcfg_file not in all_file_names:
diff --git a/os_net_config/objects.py b/os_net_config/objects.py
index 388ea7c..84ef3a1 100644
--- a/os_net_config/objects.py
+++ b/os_net_config/objects.py
@@ -40,6 +40,8 @@ def object_from_json(json):
return OvsBridge.from_json(json)
elif obj_type == "ovs_bond":
return OvsBond.from_json(json)
+ elif obj_type == "linux_bond":
+ return LinuxBond.from_json(json)
def _get_required_field(json, name, object_name):
@@ -330,6 +332,61 @@ class OvsBridge(_BaseOpts):
dhclient_args=dhclient_args, dns_servers=dns_servers)
+class LinuxBond(_BaseOpts):
+ """Base class for Linux bonds."""
+
+ def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
+ routes=None, mtu=1500, primary=False, members=None,
+ bonding_options=None, nic_mapping=None, persist_mapping=False,
+ defroute=True, dhclient_args=None, dns_servers=None):
+ addresses = addresses or []
+ routes = routes or []
+ members = members or []
+ dns_servers = dns_servers or []
+ super(LinuxBond, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
+ routes, mtu, primary, nic_mapping,
+ persist_mapping, defroute,
+ dhclient_args, dns_servers)
+ self.members = members
+ self.bonding_options = bonding_options
+ for member in self.members:
+ if member.primary:
+ if self.primary_interface_name:
+ msg = 'Only one primary interface allowed per bond.'
+ raise InvalidConfigException(msg)
+ if member.primary_interface_name:
+ self.primary_interface_name = member.primary_interface_name
+ else:
+ self.primary_interface_name = member.name
+
+ @staticmethod
+ def from_json(json):
+ name = _get_required_field(json, 'name', 'LinuxBond')
+ (use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping,
+ persist_mapping, defroute, dhclient_args,
+ dns_servers) = _BaseOpts.base_opts_from_json(
+ json, include_primary=False)
+ bonding_options = json.get('bonding_options')
+ members = []
+
+ # members
+ members_json = json.get('members')
+ if members_json:
+ if isinstance(members_json, list):
+ for member in members_json:
+ members.append(object_from_json(member))
+ else:
+ msg = 'Members must be a list.'
+ raise InvalidConfigException(msg)
+
+ return LinuxBond(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6,
+ addresses=addresses, routes=routes, mtu=mtu,
+ members=members, bonding_options=bonding_options,
+ nic_mapping=nic_mapping,
+ persist_mapping=persist_mapping, defroute=defroute,
+ dhclient_args=dhclient_args, dns_servers=dns_servers)
+
+
class OvsBond(_BaseOpts):
"""Base class for OVS bonds."""
diff --git a/os_net_config/tests/test_impl_ifcfg.py b/os_net_config/tests/test_impl_ifcfg.py
index f083ef8..efee254 100644
--- a/os_net_config/tests/test_impl_ifcfg.py
+++ b/os_net_config/tests/test_impl_ifcfg.py
@@ -137,6 +137,15 @@ BOND_IFACES="em1 em2"
"""
+_LINUX_BOND_DHCP = """# This file is autogenerated by os-net-config
+DEVICE=bond0
+ONBOOT=yes
+HOTPLUG=no
+NM_CONTROLLED=no
+BOOTPROTO=dhcp
+"""
+
+
class TestIfcfgNetConfig(base.TestCase):
def setUp(self):
@@ -295,6 +304,15 @@ BOOTPROTO=none
self.assertEqual(_OVS_BOND_DHCP,
self.get_interface_config('bond0'))
+ def test_linux_bond(self):
+ interface1 = objects.Interface('em1')
+ interface2 = objects.Interface('em2')
+ bond = objects.LinuxBond('bond0', use_dhcp=True,
+ members=[interface1, interface2])
+ self.provider.add_linux_bond(bond)
+ self.assertEqual(_LINUX_BOND_DHCP,
+ self.get_interface_config('bond0'))
+
def test_interface_defroute(self):
interface1 = objects.Interface('em1')
interface2 = objects.Interface('em2', defroute=False)
diff --git a/os_net_config/tests/test_objects.py b/os_net_config/tests/test_objects.py
index 8488370..3d95bd6 100644
--- a/os_net_config/tests/test_objects.py
+++ b/os_net_config/tests/test_objects.py
@@ -325,6 +325,64 @@ class TestBond(base.TestCase):
self.assertEqual("em2", interface2.name)
+class TestLinuxBond(base.TestCase):
+
+ def test_from_json_dhcp(self):
+ data = """{
+"type": "linux_bond",
+"name": "bond1",
+"use_dhcp": true,
+"members": [
+ {
+ "type": "interface",
+ "name": "em1"
+ },
+ {
+ "type": "interface",
+ "name": "em2"
+ }
+]
+}
+"""
+ bridge = objects.object_from_json(json.loads(data))
+ self.assertEqual("bond1", bridge.name)
+ self.assertEqual(True, bridge.use_dhcp)
+ interface1 = bridge.members[0]
+ self.assertEqual("em1", interface1.name)
+ interface2 = bridge.members[1]
+ self.assertEqual("em2", interface2.name)
+
+ def test_from_json_dhcp_with_nic1_nic2(self):
+
+ def dummy_numbered_nics(nic_mapping=None):
+ return {"nic1": "em1", "nic2": "em2"}
+ self.stubs.Set(objects, '_numbered_nics', dummy_numbered_nics)
+
+ data = """{
+"type": "ovs_bond",
+"name": "bond1",
+"use_dhcp": true,
+"members": [
+ {
+ "type": "interface",
+ "name": "nic1"
+ },
+ {
+ "type": "interface",
+ "name": "nic2"
+ }
+]
+}
+"""
+ bridge = objects.object_from_json(json.loads(data))
+ self.assertEqual("bond1", bridge.name)
+ self.assertEqual(True, bridge.use_dhcp)
+ interface1 = bridge.members[0]
+ self.assertEqual("em1", interface1.name)
+ interface2 = bridge.members[1]
+ self.assertEqual("em2", interface2.name)
+
+
class TestNumberedNicsMapping(base.TestCase):
# We want to test the function, not the dummy..