aboutsummaryrefslogtreecommitdiffstats
path: root/tools/networkcard.py
blob: 758010d2da755a25a7a5483e245e427b4923df9a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200
# Copyright 2016-2017 Intel Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Tools for network card manipulation
"""

import os
import subprocess
import logging
import glob
from conf import settings

_LOGGER = logging.getLogger('tools.networkcard')

_PCI_DIR = '/sys/bus/pci/devices/{}/'
_SRIOV_NUMVFS = os.path.join(_PCI_DIR, 'sriov_numvfs')
_SRIOV_TOTALVFS = os.path.join(_PCI_DIR, 'sriov_totalvfs')
_SRIOV_VF_PREFIX = 'virtfn'
_SRIOV_PF = 'physfn'
_PCI_NET = 'net'
_PCI_DRIVER = 'driver'


def check_pci(pci_handle):
    """ Checks if given extended PCI handle has correct length and fixes
        it if possible.

    :param pci_handle: PCI slot identifier. It can contain vsperf specific
        suffix after '|' with VF indication. e.g. '0000:05:00.0|vf1'

    :returns: PCI handle
    """
    pci = pci_handle.split('|')
    pci_len = len(pci[0])
    if pci_len == 12:
        return pci_handle
    elif pci_len == 7:
        pci[0] = '0000:' + pci[0][-7:]
        _LOGGER.debug('Adding domain part to PCI slot %s', pci[0])
        return '|'.join(pci)
    elif pci_len > 12:
        pci[0] = pci[0][-12:]
        _LOGGER.warning('PCI slot is too long, it will be shortened to %s', pci[0])
        return '|'.join(pci)
    else:
        # pci_handle has a strange length, but let us try to use it
        _LOGGER.error('Unknown format of PCI slot %s', pci_handle)
        return pci_handle

def is_sriov_supported(pci_handle):
    """ Checks if sriov is supported by given NIC

    :param pci_handle: PCI slot identifier with domain part.

    :returns: True on success, False otherwise
    """
    return os.path.isfile(_SRIOV_TOTALVFS.format(pci_handle))

def is_sriov_nic(pci_handle):
    """ Checks if given extended PCI ID refers to the VF

    :param pci_handle: PCI slot identifier with domain part. It can contain
        vsperf specific suffix after '|' with VF indication.
        e.g. '0000:05:00.0|vf1'

    :returns: True on success, False otherwise
    """
    for item in pci_handle.split('|'):
        if item.lower().startswith('vf'):
            return True
    return False

def set_sriov_numvfs(pci_handle, numvfs):
    """ Checks if sriov is supported and configures given number of VFs

    :param pci_handle: PCI slot identifier with domain part.
    :param numvfs: Number of VFs to be configured at given NIC.

    :returns: True on success, False otherwise
    """
    if not is_sriov_supported(pci_handle):
        return False

    if get_sriov_numvfs(pci_handle) == numvfs:
        return True

    if numvfs and get_sriov_numvfs(pci_handle) != 0:
        if not set_sriov_numvfs(pci_handle, 0):
            return False

    try:
        subprocess.call('sudo bash -c "echo {} > {}"'.format(numvfs, _SRIOV_NUMVFS.format(pci_handle)), shell=True)
        return get_sriov_numvfs(pci_handle) == numvfs
    except OSError:
        _LOGGER.debug('Number of VFs cant be changed to %s for PF %s', numvfs, pci_handle)
        return False

def get_sriov_numvfs(pci_handle):
    """ Returns the number of configured VFs

    :param pci_handle: PCI slot identifier with domain part
    :returns: the number of configured VFs
    """
    if is_sriov_supported(pci_handle):
        with open(_SRIOV_NUMVFS.format(pci_handle), 'r') as numvfs:
            return int(numvfs.readline().rstrip('\n'))

    return None

def get_sriov_totalvfs(pci_handle):
    """ Checks if sriov is supported and returns max number of supported VFs

    :param pci_handle: PCI slot identifier with domain part
    :returns: the max number of supported VFs by given NIC
    """
    if is_sriov_supported(pci_handle):
        with open(_SRIOV_TOTALVFS.format(pci_handle), 'r') as total:
            return int(total.readline().rstrip('\n'))

    return None

def get_sriov_vfs_list(pf_pci_handle):
    """ Returns list of PCI handles of VFs configured at given NIC/PF

    :param pf_pci_handle: PCI slot identifier of PF with domain part.
    :returns: list
    """
    vfs = []
    if is_sriov_supported(pf_pci_handle):
        for vf_name in glob.glob(os.path.join(_PCI_DIR, _SRIOV_VF_PREFIX + '*').format(pf_pci_handle)):
            vfs.append(os.path.basename(os.path.realpath(vf_name)))

    return vfs

def get_sriov_pf(vf_pci_handle):
    """ Get PCI handle of PF which belongs to given VF

    :param vf_pci_handle: PCI slot identifier of VF with domain part.
    :returns: PCI handle of parent PF
    """
    pf_path = os.path.join(_PCI_DIR, _SRIOV_PF).format(vf_pci_handle)
    if os.path.isdir(pf_path):
        return os.path.basename(os.path.realpath(pf_path))

    return None

def get_driver(pci_handle):
    """ Returns name of kernel driver assigned to given NIC

    :param pci_handle: PCI slot identifier with domain part.
    :returns: string with assigned kernel driver, None otherwise
    """
    driver_path = os.path.join(_PCI_DIR, _PCI_DRIVER).format(pci_handle)
    if os.path.isdir(driver_path):
        return os.path.basename(os.path.realpath(driver_path))

    return None

def get_device_name(pci_handle):
    """ Returns name of network card device name

    :param pci_handle: PCI slot identifier with domain part.
    :returns: string with assigned NIC device name, None otherwise
    """
    net_path = os.path.join(_PCI_DIR, _PCI_NET).format(pci_handle)
    try:
        return os.listdir(net_path)[0]
    except FileNotFoundError:
        return None
    except IndexError:
        return None

    return None

def get_mac(pci_handle):
    """ Returns MAC address of given NIC

    :param pci_handle: PCI slot identifier with domain part.
    :returns: string with assigned MAC address, None otherwise
    """
    mac_path = glob.glob(os.path.join(_PCI_DIR, _PCI_NET, '*', 'address').format(pci_handle))
    # kernel driver is loaded and MAC can be read
    if mac_path and os.path.isfile(mac_path[0]):
        with open(mac_path[0], 'r') as _file:
            return _file.readline().rstrip('\n')

    # MAC address is unknown, e.g. NIC is assigned to DPDK
    return None

def get_nic_info(full_pci_handle):
    """ Parse given pci handle with additional info and returns
        requested NIC info.

    :param full_pci_handle: A string with extended network card PCI ID.
        extended PCI ID syntax: PCI_ID[|vfx][|(mac|dev)]
        examples:
            0000:06:00.0            - returns the same value
            0000:06:00.0|vf0        - returns PCI ID of 1st virtual function of given NIC
            0000:06:00.0|mac        - returns MAC address of given NIC
            0000:06:00.0|vf0|mac    - returns MAC address of 1st virtual function of given NIC

    :returns: A string with requested NIC data or None if data cannot be read.
    """
    parsed_handle = full_pci_handle.split('|')
    if len(parsed_handle) not in (1, 2, 3):
        _LOGGER.error("Invalid PCI device name: '%s'", full_pci_handle)
        return None

    pci_handle = parsed_handle[0]

    for action in parsed_handle[1:]:
        # in case of SRIOV get PCI handle of given virtual function
        if action.lower().startswith('vf'):
            try:
                vf_num = int(action[2:])
                pci_handle = get_sriov_vfs_list(pci_handle)[vf_num]
            except ValueError:
                _LOGGER.error("Pci device '%s', does not have VF with index '%s'", pci_handle, action[2:])
                return None
            except IndexError:
                _LOGGER.error("Pci device '%s', does not have VF with index '%s'", pci_handle, vf_num)
                return None
            continue

        # return requested info for given PCI handle
        if action.lower() == 'mac':
            return get_mac(pci_handle)
        elif action.lower() == 'dev':
            return get_device_name(pci_handle)
        else:
            _LOGGER.error("Invalid item '%s' in PCI handle '%s'", action, full_pci_handle)
            return None

    return pci_handle

def reinit_vfs(pf_pci_handle):
    """ Reinitializates all VFs, which belong to given PF

    :param pf_pci_handle: PCI slot identifier of PF with domain part.
    """
    rte_pci_tool = settings.getValue('TOOLS')['bind-tool']

    for vf_nic in get_sriov_vfs_list(pf_pci_handle):
        nic_driver = get_driver(vf_nic)
        if nic_driver:
            try:
                subprocess.call(['sudo', rte_pci_tool, '--unbind', vf_nic],
                                stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
                subprocess.call(['sudo', rte_pci_tool, '--bind=' + nic_driver, vf_nic],
                                stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
            except subprocess.CalledProcessError:
                _LOGGER.warning('Error during reinitialization of VF %s', vf_nic)
        else:
            _LOGGER.warning("Can't detect driver for VF %s", vf_nic)