aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2015-10-12 07:57:35 +0000
committerGerrit Code Review <review@openstack.org>2015-10-12 07:57:35 +0000
commitcf68668803886fb68b0f0a4a33edf65cf17c6b7c (patch)
tree3415170b00d922626b110b57bde0f597076dc11b
parent95a8412bd9a80ce1e15363ae6c1a51db9432f9d7 (diff)
parent62bc734ad540cde0cf47b863db686b328d575d33 (diff)
Merge "Add support for Linux Bonding to os-net-config ifcfg"
-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 20bbe30..c1f44dd 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:
@@ -238,6 +257,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.
@@ -288,6 +320,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 f8864c4..cb2e881 100644
--- a/os_net_config/tests/test_impl_ifcfg.py
+++ b/os_net_config/tests/test_impl_ifcfg.py
@@ -146,6 +146,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):
@@ -321,6 +330,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..