Add linux bond support for nmstate provider
Extend the support for linux bonds in nmstate provider. Change-Id: I7602b121f3ad0f86e6925208d7691b4faff24686
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
|
||||
Reference in New Issue
Block a user