|
Packit Service |
0535c1 |
#
|
|
Packit Service |
0535c1 |
# Copyright (c) 2018-2020 Red Hat, Inc.
|
|
Packit Service |
0535c1 |
#
|
|
Packit Service |
0535c1 |
# This file is part of nmstate
|
|
Packit Service |
0535c1 |
#
|
|
Packit Service |
0535c1 |
# This program is free software: you can redistribute it and/or modify
|
|
Packit Service |
0535c1 |
# it under the terms of the GNU Lesser General Public License as published by
|
|
Packit Service |
0535c1 |
# the Free Software Foundation, either version 2.1 of the License, or
|
|
Packit Service |
0535c1 |
# (at your option) any later version.
|
|
Packit Service |
0535c1 |
#
|
|
Packit Service |
0535c1 |
# This program is distributed in the hope that it will be useful,
|
|
Packit Service |
0535c1 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
0535c1 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit Service |
0535c1 |
# GNU Lesser General Public License for more details.
|
|
Packit Service |
0535c1 |
#
|
|
Packit Service |
0535c1 |
# You should have received a copy of the GNU Lesser General Public License
|
|
Packit Service |
0535c1 |
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
Packit Service |
0535c1 |
#
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
import logging
|
|
Packit Service |
0535c1 |
from operator import itemgetter
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
from libnmstate.schema import OVSBridge as OB
|
|
Packit Service |
0535c1 |
from libnmstate.schema import OVSInterface
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
from . import connection
|
|
Packit Service |
0535c1 |
from .common import NM
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
PORT_PROFILE_PREFIX = "ovs-port-"
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
NM_OVS_VLAN_MODE_MAP = {
|
|
Packit Service |
0535c1 |
"trunk": OB.Port.Vlan.Mode.TRUNK,
|
|
Packit Service |
0535c1 |
"access": OB.Port.Vlan.Mode.ACCESS,
|
|
Packit Service |
0535c1 |
"native-tagged": OB.Port.Vlan.Mode.TRUNK,
|
|
Packit Service |
0535c1 |
"native-untagged": OB.Port.Vlan.Mode.UNKNOWN, # Not supported yet
|
|
Packit Service |
0535c1 |
"dot1q-tunnel": OB.Port.Vlan.Mode.UNKNOWN, # Not supported yet
|
|
Packit Service |
0535c1 |
}
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
class LacpValue:
|
|
Packit Service |
0535c1 |
ACTIVE = "active"
|
|
Packit Service |
0535c1 |
OFF = "off"
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def has_ovs_capability(nm_client):
|
|
Packit Service |
0535c1 |
return NM.Capability.OVS in nm_client.get_capabilities()
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def create_bridge_setting(options_state):
|
|
Packit Service |
0535c1 |
bridge_setting = NM.SettingOvsBridge.new()
|
|
Packit Service |
0535c1 |
for option_name, option_value in options_state.items():
|
|
Packit Service |
0535c1 |
if option_name == "fail-mode":
|
|
Packit Service |
0535c1 |
if option_value:
|
|
Packit Service |
0535c1 |
bridge_setting.props.fail_mode = option_value
|
|
Packit Service |
0535c1 |
elif option_name == "mcast-snooping-enable":
|
|
Packit Service |
0535c1 |
bridge_setting.props.mcast_snooping_enable = option_value
|
|
Packit Service |
0535c1 |
elif option_name == "rstp":
|
|
Packit Service |
0535c1 |
bridge_setting.props.rstp_enable = option_value
|
|
Packit Service |
0535c1 |
elif option_name == "stp":
|
|
Packit Service |
0535c1 |
bridge_setting.props.stp_enable = option_value
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
return bridge_setting
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def create_port_setting(port_state):
|
|
Packit Service |
0535c1 |
port_setting = NM.SettingOvsPort.new()
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
lag_state = port_state.get(OB.Port.LINK_AGGREGATION_SUBTREE)
|
|
Packit Service |
0535c1 |
if lag_state:
|
|
Packit Service |
0535c1 |
mode = lag_state.get(OB.Port.LinkAggregation.MODE)
|
|
Packit Service |
0535c1 |
if mode == OB.Port.LinkAggregation.Mode.LACP:
|
|
Packit Service |
0535c1 |
port_setting.props.lacp = LacpValue.ACTIVE
|
|
Packit Service |
0535c1 |
elif mode in (
|
|
Packit Service |
0535c1 |
OB.Port.LinkAggregation.Mode.ACTIVE_BACKUP,
|
|
Packit Service |
0535c1 |
OB.Port.LinkAggregation.Mode.BALANCE_SLB,
|
|
Packit Service |
0535c1 |
):
|
|
Packit Service |
0535c1 |
port_setting.props.lacp = LacpValue.OFF
|
|
Packit Service |
0535c1 |
port_setting.props.bond_mode = mode
|
|
Packit Service |
0535c1 |
elif mode == OB.Port.LinkAggregation.Mode.BALANCE_TCP:
|
|
Packit Service |
0535c1 |
port_setting.props.lacp = LacpValue.ACTIVE
|
|
Packit Service |
0535c1 |
port_setting.props.bond_mode = mode
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
down_delay = lag_state.get(OB.Port.LinkAggregation.Options.DOWN_DELAY)
|
|
Packit Service |
0535c1 |
if down_delay:
|
|
Packit Service |
0535c1 |
port_setting.props.bond_downdelay = down_delay
|
|
Packit Service |
0535c1 |
up_delay = lag_state.get(OB.Port.LinkAggregation.Options.UP_DELAY)
|
|
Packit Service |
0535c1 |
if up_delay:
|
|
Packit Service |
0535c1 |
port_setting.props.bond_updelay = up_delay
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
vlan_state = port_state.get(OB.Port.VLAN_SUBTREE, {})
|
|
Packit Service |
0535c1 |
if OB.Port.Vlan.MODE in vlan_state:
|
|
Packit Service |
0535c1 |
if vlan_state[OB.Port.Vlan.MODE] != OB.Port.Vlan.Mode.UNKNOWN:
|
|
Packit Service |
0535c1 |
port_setting.props.vlan_mode = vlan_state[OB.Port.Vlan.MODE]
|
|
Packit Service |
0535c1 |
if OB.Port.Vlan.TAG in vlan_state:
|
|
Packit Service |
0535c1 |
port_setting.props.tag = vlan_state[OB.Port.Vlan.TAG]
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
return port_setting
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def create_interface_setting(patch_state):
|
|
Packit Service |
0535c1 |
interface_setting = NM.SettingOvsInterface.new()
|
|
Packit Service |
0535c1 |
settings = [interface_setting]
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
if patch_state and patch_state.get(OVSInterface.Patch.PEER):
|
|
Packit Service |
0535c1 |
interface_setting.props.type = "patch"
|
|
Packit Service |
0535c1 |
settings.append(create_patch_setting(patch_state))
|
|
Packit Service |
0535c1 |
else:
|
|
Packit Service |
0535c1 |
interface_setting.props.type = "internal"
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
return settings
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def create_patch_setting(patch_state):
|
|
Packit Service |
0535c1 |
patch_setting = NM.SettingOvsPatch.new()
|
|
Packit Service |
0535c1 |
patch_setting.props.peer = patch_state[OVSInterface.Patch.PEER]
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
return patch_setting
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def is_ovs_bridge_type_id(type_id):
|
|
Packit Service |
0535c1 |
return type_id == NM.DeviceType.OVS_BRIDGE
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def is_ovs_port_type_id(type_id):
|
|
Packit Service |
0535c1 |
return type_id == NM.DeviceType.OVS_PORT
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def is_ovs_interface_type_id(type_id):
|
|
Packit Service |
0535c1 |
return type_id == NM.DeviceType.OVS_INTERFACE
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def get_port_by_slave(nmdev):
|
|
Packit Service |
0535c1 |
active_con = connection.get_device_active_connection(nmdev)
|
|
Packit Service |
0535c1 |
if active_con:
|
|
Packit Service |
0535c1 |
master = active_con.get_master()
|
|
Packit Service |
0535c1 |
if master and is_ovs_port_type_id(master.get_device_type()):
|
|
Packit Service |
0535c1 |
return master
|
|
Packit Service |
0535c1 |
return None
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def get_ovs_info(context, bridge_device, devices_info):
|
|
Packit Service |
99aaff |
port_profiles = _get_slave_profiles(bridge_device, devices_info)
|
|
Packit Service |
0535c1 |
ports = _get_bridge_ports_info(context, port_profiles, devices_info)
|
|
Packit Service |
0535c1 |
options = _get_bridge_options(context, bridge_device)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
if ports or options:
|
|
Packit Service |
0535c1 |
return {"port": ports, "options": options}
|
|
Packit Service |
0535c1 |
else:
|
|
Packit Service |
0535c1 |
return {}
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def get_interface_info(act_con):
|
|
Packit Service |
0535c1 |
"""
|
|
Packit Service |
0535c1 |
Get OVS interface information from the NM profile.
|
|
Packit Service |
0535c1 |
"""
|
|
Packit Service |
0535c1 |
info = {}
|
|
Packit Service |
0535c1 |
if act_con:
|
|
Packit Service |
0535c1 |
patch_setting = _get_patch_setting(act_con)
|
|
Packit Service |
0535c1 |
if patch_setting:
|
|
Packit Service |
0535c1 |
info[OVSInterface.PATCH_CONFIG_SUBTREE] = {
|
|
Packit Service |
0535c1 |
OVSInterface.Patch.PEER: patch_setting.props.peer,
|
|
Packit Service |
0535c1 |
}
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
return info
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _get_patch_setting(act_con):
|
|
Packit Service |
0535c1 |
"""
|
|
Packit Service |
0535c1 |
Get NM.SettingOvsPatch from NM.ActiveConnection.
|
|
Packit Service |
0535c1 |
For any error, return None.
|
|
Packit Service |
0535c1 |
"""
|
|
Packit Service |
0535c1 |
remote_con = act_con.get_connection()
|
|
Packit Service |
0535c1 |
if remote_con:
|
|
Packit Service |
0535c1 |
return remote_con.get_setting_ovs_patch()
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
return None
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def get_slaves(nm_device):
|
|
Packit Service |
0535c1 |
return nm_device.get_slaves()
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _get_bridge_ports_info(context, port_profiles, devices_info):
|
|
Packit Service |
0535c1 |
ports_info = []
|
|
Packit Service |
0535c1 |
for p in port_profiles:
|
|
Packit Service |
0535c1 |
port_info = _get_bridge_port_info(context, p, devices_info)
|
|
Packit Service |
0535c1 |
if port_info:
|
|
Packit Service |
0535c1 |
ports_info.append(port_info)
|
|
Packit Service |
0535c1 |
ports_info.sort(key=itemgetter(OB.Port.NAME))
|
|
Packit Service |
0535c1 |
return ports_info
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _get_bridge_port_info(context, port_profile, devices_info):
|
|
Packit Service |
0535c1 |
"""
|
|
Packit Service |
0535c1 |
Report port information.
|
|
Packit Service |
0535c1 |
Note: The current implementation supports only system OVS ports and
|
|
Packit Service |
0535c1 |
access vlan-mode (trunks are not supported).
|
|
Packit Service |
0535c1 |
"""
|
|
Packit Service |
0535c1 |
port_info = {}
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
port_setting = port_profile.get_setting(NM.SettingOvsPort)
|
|
Packit Service |
0535c1 |
vlan_mode = port_setting.props.vlan_mode
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
port_name = port_profile.get_interface_name()
|
|
Packit Service |
99aaff |
port_device = context.get_nm_dev(port_name)
|
|
Packit Service |
99aaff |
port_slave_profiles = _get_slave_profiles(port_device, devices_info)
|
|
Packit Service |
0535c1 |
port_slave_names = [c.get_interface_name() for c in port_slave_profiles]
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
if port_slave_names:
|
|
Packit Service |
0535c1 |
number_of_interfaces = len(port_slave_names)
|
|
Packit Service |
0535c1 |
if number_of_interfaces == 1:
|
|
Packit Service |
0535c1 |
port_info[OB.Port.NAME] = port_slave_names[0]
|
|
Packit Service |
0535c1 |
else:
|
|
Packit Service |
0535c1 |
port_lag_info = _get_lag_info(
|
|
Packit Service |
0535c1 |
port_name, port_setting, port_slave_names
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
port_info.update(port_lag_info)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
if vlan_mode:
|
|
Packit Service |
0535c1 |
nmstate_vlan_mode = NM_OVS_VLAN_MODE_MAP.get(
|
|
Packit Service |
0535c1 |
vlan_mode, OB.Port.Vlan.Mode.UNKNOWN
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
if nmstate_vlan_mode == OB.Port.Vlan.Mode.UNKNOWN:
|
|
Packit Service |
0535c1 |
logging.warning(
|
|
Packit Service |
0535c1 |
f"OVS Port VLAN mode '{vlan_mode}' is not supported yet"
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
port_info[OB.Port.VLAN_SUBTREE] = {
|
|
Packit Service |
0535c1 |
OB.Port.Vlan.MODE: nmstate_vlan_mode,
|
|
Packit Service |
0535c1 |
OB.Port.Vlan.TAG: port_setting.get_tag(),
|
|
Packit Service |
0535c1 |
}
|
|
Packit Service |
0535c1 |
return port_info
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _get_lag_info(port_name, port_setting, port_slave_names):
|
|
Packit Service |
0535c1 |
port_info = {}
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
lacp = port_setting.props.lacp
|
|
Packit Service |
0535c1 |
mode = port_setting.props.bond_mode
|
|
Packit Service |
0535c1 |
if not mode:
|
|
Packit Service |
0535c1 |
if lacp == LacpValue.ACTIVE:
|
|
Packit Service |
0535c1 |
mode = OB.Port.LinkAggregation.Mode.LACP
|
|
Packit Service |
0535c1 |
else:
|
|
Packit Service |
0535c1 |
mode = OB.Port.LinkAggregation.Mode.ACTIVE_BACKUP
|
|
Packit Service |
0535c1 |
port_info[OB.Port.NAME] = port_name
|
|
Packit Service |
0535c1 |
port_info[OB.Port.LINK_AGGREGATION_SUBTREE] = {
|
|
Packit Service |
0535c1 |
OB.Port.LinkAggregation.MODE: mode,
|
|
Packit Service |
0535c1 |
OB.Port.LinkAggregation.SLAVES_SUBTREE: sorted(
|
|
Packit Service |
0535c1 |
[
|
|
Packit Service |
0535c1 |
{OB.Port.LinkAggregation.Slave.NAME: iface_name}
|
|
Packit Service |
0535c1 |
for iface_name in port_slave_names
|
|
Packit Service |
0535c1 |
],
|
|
Packit Service |
0535c1 |
key=itemgetter(OB.Port.LinkAggregation.Slave.NAME),
|
|
Packit Service |
0535c1 |
),
|
|
Packit Service |
0535c1 |
}
|
|
Packit Service |
0535c1 |
return port_info
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _get_bridge_options(context, bridge_device):
|
|
Packit Service |
0535c1 |
bridge_options = {}
|
|
Packit Service |
0535c1 |
con = connection.ConnectionProfile(context)
|
|
Packit Service |
0535c1 |
con.import_by_device(bridge_device)
|
|
Packit Service |
0535c1 |
if con.profile:
|
|
Packit Service |
0535c1 |
bridge_setting = con.profile.get_setting(NM.SettingOvsBridge)
|
|
Packit Service |
0535c1 |
bridge_options["stp"] = bridge_setting.props.stp_enable
|
|
Packit Service |
0535c1 |
bridge_options["rstp"] = bridge_setting.props.rstp_enable
|
|
Packit Service |
0535c1 |
bridge_options["fail-mode"] = bridge_setting.props.fail_mode or ""
|
|
Packit Service |
0535c1 |
bridge_options[
|
|
Packit Service |
0535c1 |
"mcast-snooping-enable"
|
|
Packit Service |
0535c1 |
] = bridge_setting.props.mcast_snooping_enable
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
return bridge_options
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _get_slave_profiles(master_device, devices_info):
|
|
Packit Service |
0535c1 |
slave_profiles = []
|
|
Packit Service |
0535c1 |
for dev, _ in devices_info:
|
|
Packit Service |
0535c1 |
active_con = connection.get_device_active_connection(dev)
|
|
Packit Service |
0535c1 |
if active_con:
|
|
Packit Service |
0535c1 |
master = active_con.props.master
|
|
Packit Service |
0535c1 |
if master and (master.get_iface() == master_device.get_iface()):
|
|
Packit Service |
99aaff |
slave_profiles.append(active_con.props.connection)
|
|
Packit Service |
0535c1 |
return slave_profiles
|