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 | 61 | ||||
-rw-r--r-- | os_net_config/objects.py | 59 | ||||
-rw-r--r-- | os_net_config/tests/test_impl_ifcfg.py | 40 | ||||
-rw-r--r-- | os_net_config/tests/test_objects.py | 22 |
5 files changed, 192 insertions, 1 deletions
diff --git a/os_net_config/__init__.py b/os_net_config/__init__.py index fc1a38a..18c96a2 100644 --- a/os_net_config/__init__.py +++ b/os_net_config/__init__.py @@ -71,6 +71,10 @@ class NetConfig(object): self.add_linux_bond(obj) for member in obj.members: self.add_object(member) + elif isinstance(obj, objects.LinuxTeam): + self.add_linux_team(obj) + for member in obj.members: + self.add_object(member) elif isinstance(obj, objects.OvsTunnel): self.add_ovs_tunnel(obj) elif isinstance(obj, objects.OvsPatchPort): @@ -127,6 +131,13 @@ class NetConfig(object): """ raise NotImplemented("add_linux_bond is not implemented.") + def add_linux_team(self, team): + """Add a LinuxTeam object to the net config object. + + :param team: The LinuxTeam object to add. + """ + raise NotImplemented("add_linux_team is not implemented.") + def add_ovs_tunnel(self, tunnel): """Add a OvsTunnel object to the net config object. diff --git a/os_net_config/impl_ifcfg.py b/os_net_config/impl_ifcfg.py index 22aef22..49ad317 100644 --- a/os_net_config/impl_ifcfg.py +++ b/os_net_config/impl_ifcfg.py @@ -65,6 +65,7 @@ class IfcfgNetConfig(os_net_config.NetConfig): self.linuxbridge_data = {} self.linuxbond_data = {} self.ib_interface_data = {} + self.linuxteam_data = {} self.member_names = {} self.renamed_interfaces = {} self.bond_primary_ifaces = {} @@ -109,11 +110,16 @@ class IfcfgNetConfig(os_net_config.NetConfig): if base_opt.linux_bond_name: data += "MASTER=%s\n" % base_opt.linux_bond_name data += "SLAVE=yes\n" + if base_opt.linux_team_name: + data += "TEAM_MASTER=%s\n" % base_opt.linux_team_name + if base_opt.primary: + data += "TEAM_PORT_CONFIG='{\"prio\": 100}'\n" if base_opt.ivs_bridge_name: data += "DEVICETYPE=ivs\n" data += "IVS_BRIDGE=%s\n" % base_opt.ivs_bridge_name if base_opt.ovs_port: - data += "DEVICETYPE=ovs\n" + if not isinstance(base_opt, objects.LinuxTeam): + data += "DEVICETYPE=ovs\n" if base_opt.bridge_name: if isinstance(base_opt, objects.Vlan): data += "TYPE=OVSIntPort\n" @@ -180,6 +186,19 @@ class IfcfgNetConfig(os_net_config.NetConfig): self.member_names[base_opt.name] = members if base_opt.bonding_options: data += "BONDING_OPTS=\"%s\"\n" % base_opt.bonding_options + elif isinstance(base_opt, objects.LinuxTeam): + 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 + data += "DEVICETYPE=Team\n" + if base_opt.bonding_options: + data += "TEAM_CONFIG='%s'\n" % base_opt.bonding_options elif isinstance(base_opt, objects.OvsTunnel): ovs_extra.extend(base_opt.ovs_extra) data += "DEVICETYPE=ovs\n" @@ -374,6 +393,18 @@ class IfcfgNetConfig(os_net_config.NetConfig): if bond.routes: self._add_routes(bond.name, bond.routes) + def add_linux_team(self, team): + """Add a LinuxTeam object to the net config object. + + :param team: The LinuxTeam object to add. + """ + logger.info('adding linux team: %s' % team.name) + data = self._add_common(team) + logger.debug('team data: %s' % data) + self.linuxteam_data[team.name] = data + if team.routes: + self._add_routes(team.name, team.routes) + def add_ovs_tunnel(self, tunnel): """Add a OvsTunnel object to the net config object. @@ -451,6 +482,7 @@ class IfcfgNetConfig(os_net_config.NetConfig): restart_vlans = [] restart_bridges = [] restart_linux_bonds = [] + restart_linux_teams = [] update_files = {} all_file_names = [] ivs_uplinks = [] # ivs physical uplinks @@ -561,6 +593,27 @@ class IfcfgNetConfig(os_net_config.NetConfig): else: logger.info('No changes required for bridge: %s' % bridge_name) + for team_name, team_data in self.linuxteam_data.iteritems(): + route_data = self.route_data.get(team_name, '') + route6_data = self.route6_data.get(team_name, '') + team_path = self.root_dir + bridge_config_path(team_name) + team_route_path = self.root_dir + route_config_path(team_name) + team_route6_path = self.root_dir + route6_config_path(team_name) + all_file_names.append(team_path) + all_file_names.append(team_route_path) + all_file_names.append(team_route6_path) + if (utils.diff(team_path, team_data) or + utils.diff(team_route_path, route_data) or + utils.diff(team_route6_path, route6_data)): + restart_linux_teams.append(team_name) + restart_interfaces.extend(self.child_members(team_name)) + update_files[team_path] = team_data + update_files[team_route_path] = route_data + update_files[team_route6_path] = route6_data + else: + logger.info('No changes required for linux team: %s' % + team_name) + for bond_name, bond_data in self.linuxbond_data.iteritems(): route_data = self.route_data.get(bond_name, '') route6_data = self.route6_data.get(bond_name, '') @@ -628,6 +681,9 @@ class IfcfgNetConfig(os_net_config.NetConfig): for linux_bond in restart_linux_bonds: self.ifdown(linux_bond) + for linux_team in restart_linux_teams: + self.ifdown(linux_team) + for bridge in restart_bridges: self.ifdown(bridge, iftype='bridge') @@ -646,6 +702,9 @@ class IfcfgNetConfig(os_net_config.NetConfig): for linux_bond in restart_linux_bonds: self.ifup(linux_bond) + for linux_team in restart_linux_teams: + self.ifup(linux_team) + for bridge in restart_bridges: self.ifup(bridge, iftype='bridge') diff --git a/os_net_config/objects.py b/os_net_config/objects.py index 289a0da..5137263 100644 --- a/os_net_config/objects.py +++ b/os_net_config/objects.py @@ -42,6 +42,8 @@ def object_from_json(json): return OvsBond.from_json(json) elif obj_type == "linux_bond": return LinuxBond.from_json(json) + elif obj_type == "team": + return LinuxTeam.from_json(json) elif obj_type == "linux_bridge": return LinuxBridge.from_json(json) elif obj_type == "ivs_bridge": @@ -180,6 +182,7 @@ class _BaseOpts(object): self.linux_bridge_name = None # internal self.ivs_bridge_name = None # internal self.linux_bond_name = None # internal + self.linux_team_name = None # internal self.ovs_port = False # internal self.primary_interface_name = None # internal @@ -507,6 +510,62 @@ class IvsBridge(_BaseOpts): dns_servers=dns_servers) +class LinuxTeam(_BaseOpts): + """Base class for Linux bonds using teamd.""" + + def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None, + routes=None, mtu=None, 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(LinuxTeam, 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: + member.linux_team_name = name + if member.primary: + if self.primary_interface_name: + msg = 'Only one primary interface allowed per team.' + 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', 'LinuxTeam') + (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 LinuxTeam(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 LinuxBond(_BaseOpts): """Base class for Linux bonds.""" diff --git a/os_net_config/tests/test_impl_ifcfg.py b/os_net_config/tests/test_impl_ifcfg.py index f30eb89..b8a8402 100644 --- a/os_net_config/tests/test_impl_ifcfg.py +++ b/os_net_config/tests/test_impl_ifcfg.py @@ -244,11 +244,24 @@ BOOTPROTO=dhcp """ +_LINUX_TEAM_DHCP = """# This file is autogenerated by os-net-config +DEVICE=team0 +ONBOOT=yes +HOTPLUG=no +NM_CONTROLLED=no +BOOTPROTO=dhcp +DEVICETYPE=Team +""" + + _LINUX_BOND_INTERFACE = _BASE_IFCFG + """MASTER=bond0 SLAVE=yes BOOTPROTO=none """ +_LINUX_TEAM_INTERFACE = _BASE_IFCFG + """TEAM_MASTER=team0 +BOOTPROTO=none +""" _IVS_UPLINK = """# This file is autogenerated by os-net-config DEVICE=em1 @@ -291,6 +304,17 @@ OVS_BRIDGE=br-ex OVS_PATCH_PEER=br-ex-patch """ +_LINUX_TEAM_PRIMARY_IFACE = """# This file is autogenerated by os-net-config +DEVICE=em1 +ONBOOT=yes +HOTPLUG=no +NM_CONTROLLED=no +PEERDNS=no +TEAM_MASTER=team1 +TEAM_PORT_CONFIG='{"prio": 100}' +BOOTPROTO=none +""" + class TestIfcfgNetConfig(base.TestCase): @@ -311,6 +335,9 @@ class TestIfcfgNetConfig(base.TestCase): def get_linux_bond_config(self, name='bond0'): return self.provider.linuxbond_data[name] + def get_linux_team_config(self, name='team0'): + return self.provider.linuxteam_data[name] + def get_route_config(self, name='em1'): return self.provider.route_data.get(name, '') @@ -604,6 +631,19 @@ BOOTPROTO=none self.assertEqual(_LINUX_BOND_INTERFACE, self.get_interface_config('em1')) + def test_linux_team(self): + interface1 = objects.Interface('em1') + interface2 = objects.Interface('em2') + team = objects.LinuxTeam('team0', use_dhcp=True, + members=[interface1, interface2]) + self.provider.add_linux_team(team) + self.provider.add_interface(interface1) + self.provider.add_interface(interface2) + self.assertEqual(_LINUX_TEAM_DHCP, + self.get_linux_team_config('team0')) + self.assertEqual(_LINUX_TEAM_INTERFACE, + self.get_interface_config('em1')) + 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 51119ec..17b6927 100644 --- a/os_net_config/tests/test_objects.py +++ b/os_net_config/tests/test_objects.py @@ -456,6 +456,28 @@ class TestBond(base.TestCase): self.assertEqual("em2", interface2.name) +class TestLinuxTeam(base.TestCase): + + def test_from_json_dhcp(self): + data = """{ +"type": "team", +"name": "team1", +"use_dhcp": true, +"members": [ + { "type": "interface", "name": "em1", "primary": true }, + { "type": "interface", "name": "em2" } +] +} +""" + team = objects.object_from_json(json.loads(data)) + self.assertEqual("team1", team.name) + self.assertTrue(team.use_dhcp) + interface1 = team.members[0] + self.assertEqual("em1", interface1.name) + interface2 = team.members[1] + self.assertEqual("em2", interface2.name) + + class TestLinuxBond(base.TestCase): def test_from_json_dhcp(self): |