Add linux bond support for nmstate provider

Extend the support for linux bonds in nmstate provider.

Change-Id: I7602b121f3ad0f86e6925208d7691b4faff24686
This commit is contained in:
Karthik S
2023-05-05 08:20:04 +05:30
parent 0350a82f19
commit 447d07550d
2 changed files with 184 additions and 40 deletions

View File

@@ -17,6 +17,8 @@
import copy
from libnmstate import netapplier
from libnmstate import netinfo
from libnmstate.schema import Bond
from libnmstate.schema import BondMode
from libnmstate.schema import DNS
from libnmstate.schema import Ethernet
from libnmstate.schema import Ethtool
@@ -130,6 +132,37 @@ def _add_sub_tree(data, subtree):
return config
def parse_bonding_options(bond_options_str):
bond_options_dict = {}
if bond_options_str:
options = re.findall(r'(.+?)=(.+?)($|\s)', bond_options_str)
for option in options:
bond_options_dict[option[0]] = _get_type_value(option[1])
return bond_options_dict
def set_linux_bonding_options(bond_options, primary_iface=None):
linux_bond_options = ["updelay", "miimon", "lacp_rate"]
bond_data = {Bond.MODE: BondMode.ACTIVE_BACKUP,
Bond.OPTIONS_SUBTREE: {},
Bond.PORT: []}
bond_options_data = {}
if 'mode' in bond_options:
bond_data[Bond.MODE] = bond_options['mode']
for options in linux_bond_options:
if options in bond_options:
bond_options_data[options] = bond_options[options]
bond_data[Bond.OPTIONS_SUBTREE] = bond_options_data
if primary_iface and bond_data[Bond.MODE] == BondMode.ACTIVE_BACKUP:
bond_options_data['primary'] = primary_iface
if len(bond_data[Bond.OPTIONS_SUBTREE]) == 0:
del bond_data[Bond.OPTIONS_SUBTREE]
return bond_data
def _is_any_ip_addr(address):
if address.lower() == 'any' or address.lower() == 'all':
return True
@@ -145,6 +178,8 @@ class NmstateNetConfig(os_net_config.NetConfig):
self.route_data = {}
self.rules_data = []
self.dns_data = {'server': [], 'domain': []}
self.linuxbond_data = {}
self.member_names = {}
self.route_table_data = {}
logger.info('nmstate net config provider created.')
@@ -556,48 +591,13 @@ class NmstateNetConfig(os_net_config.NetConfig):
else:
data[Interface.STATE] = InterfaceState.DOWN
if not base_opt.nm_controlled:
logger.info('Using NetworkManager, nm_controlled is always true.'
'Deprecating it from next release')
if isinstance(base_opt, objects.Interface):
if not base_opt.hotplug:
logger.info('Using NetworkManager, hotplug is always set to'
'true. Deprecating it from next release')
elif isinstance(base_opt, objects.Vlan) or \
re.match(r'\w+\.\d+$', base_opt.name):
msg = 'Error: VLAN interfaces not yet supported by impl_nmstate'
raise os_net_config.NotImplemented(msg)
elif isinstance(base_opt, objects.IvsInterface):
msg = 'Error: IVS interfaces not yet supported by impl_nmstate'
raise os_net_config.NotImplemented(msg)
elif isinstance(base_opt, objects.NfvswitchInternal):
msg = 'Error: NFVSwitch not yet supported by impl_nmstate'
raise os_net_config.NotImplemented(msg)
elif isinstance(base_opt, objects.IbInterface):
msg = 'Error: Infiniband not yet supported by impl_nmstate'
raise os_net_config.NotImplemented(msg)
elif isinstance(base_opt, objects.OvsBond):
msg = "Error: Ovs Bonds are not yet supported by impl_nmstate"
raise os_net_config.NotImplemented(msg)
elif isinstance(base_opt, objects.LinuxBridge):
msg = "Error: Linux bridges are not yet supported by impl_nmstate"
raise os_net_config.NotImplemented(msg)
elif isinstance(base_opt, objects.LinuxTeam):
msg = "Error: Linux Teams are not yet supported by impl_nmstate"
raise os_net_config.NotImplemented(msg)
elif isinstance(base_opt, objects.OvsTunnel):
msg = "Error: OVS tunnels not yet supported by impl_nmstate"
raise os_net_config.NotImplemented(msg)
elif isinstance(base_opt, objects.OvsPatchPort):
msg = "Error: OVS tunnels not yet supported by impl_nmstate"
raise os_net_config.NotImplemented(msg)
elif isinstance(base_opt, objects.OvsDpdkBond):
msg = "Error: OVS DPDK Bonds not yet supported by impl_nmstate"
raise os_net_config.NotImplemented(msg)
else:
msg = "Error: Unsupported interface by impl_nmstate"
raise os_net_config.NotImplemented(msg)
if not base_opt.nm_controlled:
logger.info('Using NetworkManager, nm_controlled is always true.'
'Deprecating it from next release')
if base_opt.mtu:
data[Interface.MTU] = base_opt.mtu
@@ -810,14 +810,43 @@ class NmstateNetConfig(os_net_config.NetConfig):
interface.ethtool_opts)
if interface.renamed:
# TODO(Karthik S) Handle renamed interfaces
pass
logger.info(f"Interface {interface.hwname} being renamed to"
f"{interface.name}")
self.renamed_interfaces[interface.hwname] = interface.name
if interface.hwaddr:
data[Interface.MAC] = interface.hwaddr
logger.debug(f'interface data: {data}')
self.interface_data[interface.name] = data
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)
data[Interface.TYPE] = InterfaceType.BOND
data[Interface.STATE] = InterfaceState.UP
bond_options = {}
if bond.bonding_options:
bond_options = parse_bonding_options(bond.bonding_options)
bond_data = set_linux_bonding_options(
bond_options, primary_iface=bond.primary_interface_name)
if bond_data:
data[Bond.CONFIG_SUBTREE] = bond_data
if bond.members:
members = [member.name for member in bond.members]
self.member_names[bond.name] = members
data[Bond.CONFIG_SUBTREE][Bond.PORT] = members
logger.debug('bond data: %s' % data)
self.linuxbond_data[bond.name] = data
def apply(self, cleanup=False, activate=True):
"""Apply the network configuration.
@@ -852,6 +881,17 @@ class NmstateNetConfig(os_net_config.NetConfig):
logger.info(f'Routes_data {routes_data}')
apply_routes.extend(routes_data)
for bond_name, bond_data in self.linuxbond_data.items():
bond_state = self.iface_state(bond_name)
if not is_dict_subset(bond_state, bond_data):
updated_interfaces[bond_name] = bond_data
else:
logger.info('No changes required for bond: %s' %
bond_name)
routes_data = self.generate_routes(bond_name)
logger.info('Routes_data %s' % routes_data)
apply_routes.extend(routes_data)
if activate:
if not self.noop:
try:
@@ -889,4 +929,6 @@ class NmstateNetConfig(os_net_config.NetConfig):
raise os_net_config.ConfigurationError(message)
self.interface_data = {}
self.linuxbond_data = {}
return updated_interfaces

View File

@@ -184,6 +184,9 @@ class TestNmstateNetConfig(base.TestCase):
def get_interface_config(self, name='em1'):
return self.provider.interface_data[name]
def get_linuxbond_config(self, name='bond0'):
return self.provider.linuxbond_data[name]
def get_nmstate_ethtool_opts(self, name):
data = {}
data[Ethernet.CONFIG_SUBTREE] = \
@@ -571,6 +574,105 @@ class TestNmstateNetConfig(base.TestCase):
self.assertEqual(yaml.safe_load(expected_route_table),
self.get_route_config('em1'))
def test_linux_bond(self):
expected_config1 = """
name: bond0
type: bond
state: up
link-aggregation:
mode: active-backup
port:
- em1
- em2
options:
primary: em1
ipv4:
auto-dns: True
enabled: True
dhcp: True
auto-routes: True
auto-gateway: True
ipv6:
enabled: False
autoconf: False
dhcp: False
"""
expected_em1_cfg = """
name: em1
state: up
ethernet: {}
ipv4:
dhcp: False
enabled: False
ipv6:
autoconf: False
dhcp: False
enabled: False
type: ethernet
"""
expected_em2_cfg = """
name: em2
state: up
ethernet: {}
ipv4:
dhcp: False
enabled: False
ipv6:
autoconf: False
dhcp: False
enabled: False
type: ethernet
"""
expected_config2 = """
name: bond1
type: bond
state: up
link-aggregation:
mode: 802.3ad
options:
miimon: 100
updelay: 1000
lacp_rate: slow
port:
- em3
- em4
ipv4:
auto-dns: True
enabled: True
dhcp: True
auto-routes: True
auto-gateway: True
ipv6:
enabled: False
autoconf: False
dhcp: False
"""
interface1 = objects.Interface('em1', primary=True)
interface2 = objects.Interface('em2')
bond = objects.LinuxBond('bond0', use_dhcp=True,
members=[interface1, interface2])
self.provider.add_linux_bond(bond)
self.provider.add_interface(interface1)
self.provider.add_interface(interface2)
self.assertEqual(yaml.safe_load(expected_config1),
self.get_linuxbond_config('bond0'))
self.assertEqual(yaml.safe_load(expected_em1_cfg),
self.get_interface_config('em1'))
self.assertEqual(yaml.safe_load(expected_em2_cfg),
self.get_interface_config('em2'))
# primary interface is used only for active-slave bonds
interface1 = objects.Interface('em3')
interface2 = objects.Interface('em4', primary=True)
bond = objects.LinuxBond('bond1', use_dhcp=True,
members=[interface1, interface2],
bonding_options="mode=802.3ad "
"lacp_rate=slow updelay=1000 miimon=100")
self.provider.add_linux_bond(bond)
self.assertEqual(yaml.safe_load(expected_config2),
self.get_linuxbond_config('bond1'))
class TestNmstateNetConfigApply(base.TestCase):