diff options
Diffstat (limited to 'os_net_config')
-rw-r--r-- | os_net_config/__init__.py | 11 | ||||
-rw-r--r-- | os_net_config/impl_ifcfg.py | 47 | ||||
-rw-r--r-- | os_net_config/objects.py | 57 | ||||
-rw-r--r-- | os_net_config/tests/test_impl_ifcfg.py | 18 | ||||
-rw-r--r-- | os_net_config/tests/test_objects.py | 58 |
5 files changed, 191 insertions, 0 deletions
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.. |