Blame libnmstate/nm/sriov.py

Packit Service 0535c1
#
Packit Service 0535c1
# Copyright (c) 2019-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 re
Packit Service 0535c1
import subprocess
Packit Service 0535c1
Packit Service 0535c1
from libnmstate.error import NmstateNotSupportedError
Packit Service 0535c1
from libnmstate.schema import Ethernet
Packit Service 0535c1
from libnmstate.schema import Interface
Packit Service 0535c1
Packit Service 0535c1
from .common import NM
Packit Service 0535c1
from .common import GLib
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
SRIOV_NMSTATE_TO_NM_MAP = {
Packit Service 0535c1
    Ethernet.SRIOV.VFS.MAC_ADDRESS: (
Packit Service 0535c1
        NM.SRIOV_VF_ATTRIBUTE_MAC,
Packit Service 0535c1
        GLib.Variant.new_string,
Packit Service 0535c1
    ),
Packit Service 0535c1
    Ethernet.SRIOV.VFS.SPOOF_CHECK: (
Packit Service 0535c1
        NM.SRIOV_VF_ATTRIBUTE_SPOOF_CHECK,
Packit Service 0535c1
        GLib.Variant.new_boolean,
Packit Service 0535c1
    ),
Packit Service 0535c1
    Ethernet.SRIOV.VFS.TRUST: (
Packit Service 0535c1
        NM.SRIOV_VF_ATTRIBUTE_TRUST,
Packit Service 0535c1
        GLib.Variant.new_boolean,
Packit Service 0535c1
    ),
Packit Service 0535c1
    Ethernet.SRIOV.VFS.MIN_TX_RATE: (
Packit Service 0535c1
        NM.SRIOV_VF_ATTRIBUTE_MIN_TX_RATE,
Packit Service 0535c1
        GLib.Variant.new_uint32,
Packit Service 0535c1
    ),
Packit Service 0535c1
    Ethernet.SRIOV.VFS.MAX_TX_RATE: (
Packit Service 0535c1
        NM.SRIOV_VF_ATTRIBUTE_MAX_TX_RATE,
Packit Service 0535c1
        GLib.Variant.new_uint32,
Packit Service 0535c1
    ),
Packit Service 0535c1
}
Packit Service 0535c1
Packit Service 0535c1
SRIOV_NMSTATE_TO_REGEX = {
Packit Service 0535c1
    Ethernet.SRIOV.VFS.MAC_ADDRESS: re.compile(
Packit Service 0535c1
        r"[a-fA-F0-9:]{17}|[a-fA-F0-9]{12}"
Packit Service 0535c1
    ),
Packit Service 0535c1
    Ethernet.SRIOV.VFS.SPOOF_CHECK: re.compile(r"checking (on|off)"),
Packit Service 0535c1
    Ethernet.SRIOV.VFS.TRUST: re.compile(r"trust (on|off)"),
Packit Service 0535c1
    Ethernet.SRIOV.VFS.MIN_TX_RATE: re.compile(r"min_tx_rate ([0-9]+)"),
Packit Service 0535c1
    Ethernet.SRIOV.VFS.MAX_TX_RATE: re.compile(r"max_tx_rate ([0-9]+)"),
Packit Service 0535c1
}
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def create_setting(context, iface_state, base_con_profile):
Packit Service 0535c1
    sriov_setting = None
Packit Service 0535c1
    ifname = iface_state[Interface.NAME]
Packit Service 0535c1
    sriov_config = iface_state.get(Ethernet.CONFIG_SUBTREE, {}).get(
Packit Service 0535c1
        Ethernet.SRIOV_SUBTREE
Packit Service 0535c1
    )
Packit Service 0535c1
    if sriov_config:
Packit Service 0535c1
        if not _has_sriov_capability(context, ifname):
Packit Service 0535c1
            raise NmstateNotSupportedError(
Packit Service 0535c1
                f"Interface '{ifname}' does not support SR-IOV"
Packit Service 0535c1
            )
Packit Service 0535c1
Packit Service 0535c1
        sriov_setting = base_con_profile.get_setting_duplicate(
Packit Service 0535c1
            NM.SETTING_SRIOV_SETTING_NAME
Packit Service 0535c1
        )
Packit Service 0535c1
        if not sriov_setting:
Packit Service 0535c1
            sriov_setting = NM.SettingSriov.new()
Packit Service 0535c1
Packit Service 0535c1
        vfs_config = sriov_config.get(Ethernet.SRIOV.VFS_SUBTREE, [])
Packit Service 0535c1
        vf_object_ids = {vf.get_index() for vf in sriov_setting.props.vfs}
Packit Service 0535c1
        vf_config_ids = {
Packit Service 0535c1
            vf_config[Ethernet.SRIOV.VFS.ID] for vf_config in vfs_config
Packit Service 0535c1
        }
Packit Service 0535c1
Packit Service 0535c1
        # As the user must do full edit of vfs, nmstate is deleting all the vfs
Packit Service 0535c1
        # and then adding all the vfs from the config.
Packit Service 0535c1
        for vf_id in _remove_sriov_vfs_in_setting(
Packit Service 0535c1
            vfs_config, sriov_setting, vf_object_ids
Packit Service 0535c1
        ):
Packit Service 0535c1
            sriov_setting.remove_vf_by_index(vf_id)
Packit Service 0535c1
Packit Service 0535c1
        for vf_object in _create_sriov_vfs_from_config(
Packit Service 0535c1
            vfs_config, sriov_setting, vf_config_ids
Packit Service 0535c1
        ):
Packit Service 0535c1
            sriov_setting.add_vf(vf_object)
Packit Service 0535c1
Packit Service 0535c1
        sriov_setting.props.total_vfs = sriov_config[Ethernet.SRIOV.TOTAL_VFS]
Packit Service 0535c1
Packit Service 0535c1
    return sriov_setting
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def _create_sriov_vfs_from_config(vfs_config, sriov_setting, vf_ids_to_add):
Packit Service 0535c1
    vfs_config_to_add = (
Packit Service 0535c1
        vf_config
Packit Service 0535c1
        for vf_config in vfs_config
Packit Service 0535c1
        if vf_config[Ethernet.SRIOV.VFS.ID] in vf_ids_to_add
Packit Service 0535c1
    )
Packit Service 0535c1
    for vf_config in vfs_config_to_add:
Packit Service 0535c1
        vf_id = vf_config.pop(Ethernet.SRIOV.VFS.ID)
Packit Service 0535c1
        vf_object = NM.SriovVF.new(vf_id)
Packit Service 0535c1
        for key, val in vf_config.items():
Packit Service 0535c1
            _set_nm_attribute(vf_object, key, val)
Packit Service 0535c1
Packit Service 0535c1
        yield vf_object
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def _set_nm_attribute(vf_object, key, value):
Packit Service 0535c1
    nm_attr, nm_variant = SRIOV_NMSTATE_TO_NM_MAP[key]
Packit Service 0535c1
    vf_object.set_attribute(nm_attr, nm_variant(value))
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def _remove_sriov_vfs_in_setting(vfs_config, sriov_setting, vf_ids_to_remove):
Packit Service 0535c1
    for vf_id in vf_ids_to_remove:
Packit Service 0535c1
        yield vf_id
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def _has_sriov_capability(context, ifname):
Packit Service 0535c1
    dev = context.get_nm_dev(ifname)
Packit Service 0535c1
    return dev and (NM.DeviceCapabilities.SRIOV & dev.props.capabilities)
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def get_info(device):
Packit Service 0535c1
    """
Packit Service 0535c1
    Provide the current active SR-IOV runtime values
Packit Service 0535c1
    """
Packit Service 0535c1
    sriov_running_info = {}
Packit Service 0535c1
Packit Service 0535c1
    ifname = device.get_iface()
Packit Service 0535c1
    numvf_path = f"/sys/class/net/{ifname}/device/sriov_numvfs"
Packit Service 0535c1
    try:
Packit Service 0535c1
        with open(numvf_path) as f:
Packit Service 0535c1
            sriov_running_info[Ethernet.SRIOV.TOTAL_VFS] = int(f.read())
Packit Service 0535c1
    except FileNotFoundError:
Packit Service 0535c1
        return sriov_running_info
Packit Service 0535c1
Packit Service 0535c1
    if sriov_running_info[Ethernet.SRIOV.TOTAL_VFS]:
Packit Service 0535c1
        sriov_running_info[Ethernet.SRIOV.VFS_SUBTREE] = _get_sriov_vfs_info(
Packit Service 0535c1
            ifname
Packit Service 0535c1
        )
Packit Service 0535c1
    else:
Packit Service 0535c1
        sriov_running_info[Ethernet.SRIOV.VFS_SUBTREE] = []
Packit Service 0535c1
Packit Service 0535c1
    return {Ethernet.SRIOV_SUBTREE: sriov_running_info}
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def _get_sriov_vfs_info(ifname):
Packit Service 0535c1
    """
Packit Service 0535c1
    This is a workaround to get the VFs configuration from runtime.
Packit Service 0535c1
    Ref: https://bugzilla.redhat.com/1777520
Packit Service 0535c1
    """
Packit Service 0535c1
    proc = subprocess.run(
Packit Service 0535c1
        ("ip", "link", "show", ifname),
Packit Service 0535c1
        stdout=subprocess.PIPE,
Packit Service 0535c1
        encoding="utf-8",
Packit Service 0535c1
    )
Packit Service 0535c1
    iplink_output = proc.stdout
Packit Service 0535c1
Packit Service 0535c1
    # This is ignoring the first two line of the ip link output because they
Packit Service 0535c1
    # are about the PF and we don't need them.
Packit Service 0535c1
    vfs = iplink_output.splitlines(False)[2:]
Packit Service 0535c1
    vfs_config = [
Packit Service 0535c1
        vf_config for vf_config in _parse_ip_link_output_for_vfs(vfs)
Packit Service 0535c1
    ]
Packit Service 0535c1
Packit Service 0535c1
    return vfs_config
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def _parse_ip_link_output_for_vfs(vfs):
Packit Service 0535c1
    for vf_id, vf in enumerate(vfs):
Packit Service 0535c1
        vf_config = _parse_ip_link_output_options_for_vf(vf)
Packit Service 0535c1
        vf_config[Ethernet.SRIOV.VFS.ID] = vf_id
Packit Service 0535c1
        yield vf_config
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def _parse_ip_link_output_options_for_vf(vf):
Packit Service 0535c1
    vf_options = {}
Packit Service 0535c1
    for option, expr in SRIOV_NMSTATE_TO_REGEX.items():
Packit Service 0535c1
        match_expr = expr.search(vf)
Packit Service 0535c1
        if match_expr:
Packit Service 0535c1
            if option == Ethernet.SRIOV.VFS.MAC_ADDRESS:
Packit Service 0535c1
                value = match_expr.group(0).upper()
Packit Service 0535c1
            else:
Packit Service 0535c1
                value = match_expr.group(1)
Packit Service 0535c1
Packit Service 0535c1
            if value.isdigit():
Packit Service 0535c1
                value = int(value)
Packit Service 0535c1
            elif value == "on":
Packit Service 0535c1
                value = True
Packit Service 0535c1
            elif value == "off":
Packit Service 0535c1
                value = False
Packit Service 0535c1
            vf_options[option] = value
Packit Service 0535c1
Packit Service 0535c1
    return vf_options