[Kimchi-devel] [PATCH] [Kimchi] Move Kimchi specific functions from gingerbase.netinfo to Kimchi

Daniel Henrique Barboza dhbarboza82 at gmail.com
Tue Apr 25 16:32:16 UTC 2017


Reviewed-by: Daniel Barboza <danielhb at linux.vnet.ibm.com>

On 04/25/2017 01:15 PM, Aline Manera wrote:
> The idea behind this patch is to eliminate the Ginger Base dependency.
> Almost all the functions in gingerbase.netinfo are for only Kimchi
> matters. So move them to Kimchi and when posible use ethtool module to
> provide the necessary info
>
> Signed-off-by: Aline Manera <alinefm at linux.vnet.ibm.com>
> ---
>   control/interfaces.py |   7 +-
>   model/interfaces.py   |  37 ++++-
>   model/networks.py     |  22 ++-
>   model/ovsbridges.py   |   4 +-
>   network.py            | 434 +++++++++++++++++++++++++++++++++++++++++++++++++-
>   tests/test_model.py   |   2 +-
>   6 files changed, 478 insertions(+), 28 deletions(-)
>
> diff --git a/control/interfaces.py b/control/interfaces.py
> index 7b6127a..7aba664 100644
> --- a/control/interfaces.py
> +++ b/control/interfaces.py
> @@ -37,9 +37,4 @@ class Interface(Resource):
>   
>       @property
>       def data(self):
> -        return {'name': self.ident,
> -                'type': self.info['type'],
> -                'ipaddr': self.info['ipaddr'],
> -                'netmask': self.info['netmask'],
> -                'status': self.info['status'],
> -                'module': self.info['module']}
> +        return self.info
> diff --git a/model/interfaces.py b/model/interfaces.py
> index 6be4356..0532072 100644
> --- a/model/interfaces.py
> +++ b/model/interfaces.py
> @@ -1,7 +1,7 @@
>   #
>   # Project Kimchi
>   #
> -# Copyright IBM Corp, 2015-2016
> +# Copyright IBM Corp, 2015-2017
>   #
>   # This library is free software; you can redistribute it and/or
>   # modify it under the terms of the GNU Lesser General Public
> @@ -17,11 +17,14 @@
>   # License along with this library; if not, write to the Free Software
>   # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
>   
> +import ethtool
> +
>   from wok.exception import InvalidParameter, NotFoundError
> +from wok.stringutils import encode_value
> +from wok.utils import wok_log
>   
> -from wok.plugins.gingerbase import netinfo
> +from wok.plugins.kimchi import network as netinfo
>   from wok.plugins.kimchi.model.networks import NetworksModel
> -from wok.utils import wok_log
>   
>   
>   class InterfacesModel(object):
> @@ -50,7 +53,29 @@ class InterfaceModel(object):
>           pass
>   
>       def lookup(self, name):
> -        try:
> -            return netinfo.get_interface_info(name)
> -        except ValueError:
> +        if encode_value(name) not in map(encode_value, ethtool.get_devices()):
>               raise NotFoundError("KCHIFACE0001E", {'name': name})
> +
> +        ipaddr = ''
> +        netmask = ''
> +        module = 'unknown'
> +        status = 'down'
> +        try:
> +            ipaddr = ethtool.get_ipaddr(encode_value(name))
> +            netmask = ethtool.get_netmask(encode_value(name))
> +            module = ethtool.get_module(encode_value(name))
> +
> +            flags = ethtool.get_flags(encode_value(name))
> +            status = 'up' if flags & (ethtool.IFF_RUNNING | ethtool.IFF_UP) \
> +                     else 'down'
> +        except IOError:
> +            pass
> +
> +        iface_type = netinfo.get_interface_type(name)
> +
> +        return {'name': name,
> +                'type': iface_type,
> +                'status': status,
> +                'ipaddr': ipaddr,
> +                'netmask': netmask,
> +                'module': module}
> diff --git a/model/networks.py b/model/networks.py
> index 59b111b..722b97b 100644
> --- a/model/networks.py
> +++ b/model/networks.py
> @@ -28,10 +28,7 @@ from wok.exception import MissingParameter, NotFoundError, OperationFailed
>   from wok.utils import run_command, wok_log
>   from wok.xmlutils.utils import xpath_get_text
>   
> -from wok.plugins.gingerbase import netinfo
> -from wok.plugins.gingerbase.netinfo import get_vlan_device, is_bridge, is_vlan
> -from wok.plugins.gingerbase.netinfo import ports
> -from wok.plugins.kimchi import network as knetwork
> +from wok.plugins.kimchi import network as netinfo
>   from wok.plugins.kimchi.config import kimchiPaths
>   from wok.plugins.kimchi.model.featuretests import FeatureTests
>   from wok.plugins.kimchi.osinfo import defaults as tmpl_defaults
> @@ -130,8 +127,8 @@ class NetworksModel(object):
>               xml = network.XMLDesc(0)
>               subnet = NetworkModel.get_network_from_xml(xml)['subnet']
>               subnet and invalid_addrs.append(ipaddr.IPNetwork(subnet))
> -            addr_pools = addr_pools if addr_pools else knetwork.PrivateNets
> -        return knetwork.get_one_free_network(invalid_addrs, addr_pools)
> +            addr_pools = addr_pools if addr_pools else netinfo.PrivateNets
> +        return netinfo.get_one_free_network(invalid_addrs, addr_pools)
>   
>       def _set_network_subnet(self, params):
>           netaddr = params.get('subnet', '')
> @@ -281,7 +278,7 @@ class NetworksModel(object):
>           conn = self.conn.get()
>           if iface_xml is None:
>               try:
> -                mac = knetwork.get_dev_macaddr(str(interface))
> +                mac = netinfo.get_dev_macaddr(str(interface))
>                   iface_xml = get_iface_xml({'type': 'ethernet',
>                                              'name': interface,
>                                              'mac': mac,
> @@ -364,7 +361,7 @@ class NetworkModel(object):
>                   connection = 'macvtap'
>   
>               # exposing the network on linux bridge or macvtap interface
> -            interface_subnet = knetwork.get_dev_netaddr(interface)
> +            interface_subnet = netinfo.get_dev_netaddr(interface)
>               subnet = subnet if subnet else interface_subnet
>   
>           # libvirt use format 192.168.0.1/24, standard should be 192.168.0.0/24
> @@ -536,10 +533,11 @@ class NetworkModel(object):
>           # get target device if bridge was created by Kimchi
>           if connection == 'bridge':
>               iface = info['interfaces'][0]
> -            if is_bridge(iface) and iface.startswith(KIMCHI_BRIDGE_PREFIX):
> -                port = ports(iface)[0]
> -                if is_vlan(port):
> -                    dev = get_vlan_device(port)
> +            if (netinfo.is_bridge(iface) and
> +               iface.startswith(KIMCHI_BRIDGE_PREFIX)):
> +                port = netinfo.ports(iface)[0]
> +                if netinfo.is_vlan(port):
> +                    dev = netinfo.get_vlan_device(port)
>                       info['interfaces'] = original['interfaces'] = [dev]
>                   # nic
>                   else:
> diff --git a/model/ovsbridges.py b/model/ovsbridges.py
> index 212520f..f5bb3df 100644
> --- a/model/ovsbridges.py
> +++ b/model/ovsbridges.py
> @@ -1,7 +1,7 @@
>   #
>   # Project Kimchi
>   #
> -# Copyright IBM Corp, 2016
> +# Copyright IBM Corp, 2016-2017
>   #
>   # This library is free software; you can redistribute it and/or
>   # modify it under the terms of the GNU Lesser General Public
> @@ -17,7 +17,7 @@
>   # License along with this library; if not, write to the Free Software
>   # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
>   
> -from wok.plugins.gingerbase.netinfo import ovs_bridges
> +from wok.plugins.kimchi.network import ovs_bridges
>   
>   
>   class OVSBridgesModel(object):
> diff --git a/network.py b/network.py
> index ec567ca..69944e7 100644
> --- a/network.py
> +++ b/network.py
> @@ -1,7 +1,7 @@
>   #
>   # Project Kimchi
>   #
> -# Copyright IBM Corp, 2015-2016
> +# Copyright IBM Corp, 2015-2017
>   #
>   # This library is free software; you can redistribute it and/or
>   # modify it under the terms of the GNU Lesser General Public
> @@ -19,7 +19,13 @@
>   #
>   
>   import ethtool
> +import glob
>   import ipaddr
> +import os
> +from distutils.spawn import find_executable
> +
> +from wok.stringutils import encode_value
> +from wok.utils import run_command
>   
>   
>   APrivateNets = ipaddr.IPNetwork("10.0.0.0/8")
> @@ -30,6 +36,432 @@ DefaultNetsPool = [ipaddr.IPNetwork('192.168.122.0/23'),
>                      ipaddr.IPNetwork('192.168.124.0/22'),
>                      ipaddr.IPNetwork('192.168.128.0/17')]
>   
> +NET_PATH = '/sys/class/net'
> +NIC_PATH = '/sys/class/net/*/device'
> +BRIDGE_PATH = '/sys/class/net/*/bridge'
> +BONDING_PATH = '/sys/class/net/*/bonding'
> +WLAN_PATH = '/sys/class/net/*/wireless'
> +NET_BRPORT = '/sys/class/net/%s/brport'
> +NET_MASTER = '/sys/class/net/%s/master'
> +PROC_NET_VLAN = '/proc/net/vlan/'
> +BONDING_SLAVES = '/sys/class/net/%s/bonding/slaves'
> +BRIDGE_PORTS = '/sys/class/net/%s/brif'
> +
> +
> +def wlans():
> +    """Get all wlans declared in /sys/class/net/*/wireless.
> +
> +    Returns:
> +        List[str]: a list with the wlans found.
> +
> +    """
> +    return [b.split('/')[-2] for b in glob.glob(WLAN_PATH)]
> +
> +
> +def nics():
> +    """Get all nics of the host.
> +
> +    This function returns every nic, including those
> +    that might be loaded from an usb port.
> +
> +    Returns:
> +        List[str]: a list with the nics found.
> +
> +    """
> +    return list(set([b.split('/')[-2] for b in glob.glob(NIC_PATH)]) -
> +                set(wlans()))
> +
> +
> +def is_nic(iface):
> +    """Checks if iface is a nic.
> +
> +    Args:
> +        iface (str): name of the interface.
> +
> +    Returns:
> +        bool: True if iface is a nic, False otherwise.
> +
> +    """
> +    return encode_value(iface) in map(encode_value, nics())
> +
> +
> +def bondings():
> +    """Get all bondings of the host.
> +
> +    Returns:
> +        List[str]: a list with the bonds found.
> +
> +    """
> +    return [b.split('/')[-2] for b in glob.glob(BONDING_PATH)]
> +
> +
> +def is_bonding(iface):
> +    """Checks if iface is a bond.
> +
> +    Args:
> +        iface (str): name of the interface.
> +
> +    Returns:
> +        bool: True if iface is a bond, False otherwise.
> +
> +    """
> +    return encode_value(iface) in map(encode_value, bondings())
> +
> +
> +def vlans():
> +    """Get all vlans of the host.
> +
> +    Returns:
> +        List[str]: a list with the vlans found.
> +
> +    """
> +    return list(set([b.split('/')[-1]
> +                     for b in glob.glob(NET_PATH + '/*')]) &
> +                set([b.split('/')[-1]
> +                     for b in glob.glob(PROC_NET_VLAN + '*')]))
> +
> +
> +def is_vlan(iface):
> +    """Checks if iface is a vlan.
> +
> +    Args:
> +        iface (str): name of the interface.
> +
> +    Returns:
> +        bool: True if iface is a vlan, False otherwise.
> +
> +    """
> +    return encode_value(iface) in map(encode_value, vlans())
> +
> +
> +def bridges():
> +    """Get all bridges of the host.
> +
> +    Returns:
> +        List[str]: a list with the bridges found.
> +
> +    """
> +    return list(set([b.split('/')[-2] for b in glob.glob(BRIDGE_PATH)] +
> +                    ovs_bridges()))
> +
> +
> +def is_bridge(iface):
> +    """Checks if iface is a bridge.
> +
> +    Args:
> +        iface (str): name of the interface.
> +
> +    Returns:
> +        bool: True if iface is a bridge, False otherwise.
> +
> +    """
> +    return encode_value(iface) in map(encode_value, bridges())
> +
> +
> +def is_openvswitch_running():
> +    """Checks if the openvswitch service is running in the host.
> +
> +    Returns:
> +        bool: True if openvswitch service is running, False otherwise.
> +
> +    """
> +    cmd = ['systemctl', 'is-active', 'openvswitch', '--quiet']
> +    _, _, r_code = run_command(cmd, silent=True)
> +    return r_code == 0
> +
> +
> +def ovs_bridges():
> +    """Get the OVS Bridges of the host.
> +
> +    In some distributions, like Fedora, the files bridge and brif are
> +    not created under /sys/class/net/<ovsbridge> for OVS bridges.
> +    These specific functions allows one to differentiate OVS bridges
> +    from other types of bridges.
> +
> +    Returns:
> +        List[str]: a list with the OVS bridges found.
> +
> +    """
> +    if not is_openvswitch_running():
> +        return []
> +
> +    ovs_cmd = find_executable("ovs-vsctl")
> +
> +    # openvswitch not installed: there is no OVS bridge configured
> +    if ovs_cmd is None:
> +        return []
> +
> +    out, _, r_code = run_command([ovs_cmd, 'list-br'], silent=True)
> +    if r_code != 0:
> +        return []
> +
> +    return [x.strip() for x in out.rstrip('\n').split('\n') if x.strip()]
> +
> +
> +def is_ovs_bridge(iface):
> +    """Checks if iface is an OVS bridge.
> +
> +    In some distributions, like Fedora, the files bridge and brif are
> +    not created under /sys/class/net/<ovsbridge> for OVS bridges.
> +    These specific functions allows one to differentiate OVS bridges
> +    from other types of bridges.
> +
> +    Args:
> +        iface (str): name of the interface.
> +
> +    Returns:
> +        bool: True if iface is an OVS bridge, False otherwise.
> +
> +    """
> +    return iface in ovs_bridges()
> +
> +
> +def ovs_bridge_ports(ovsbr):
> +    """Get the ports of a OVS bridge.
> +
> +    In some distributions, like Fedora, the files bridge and brif are
> +    not created under /sys/class/net/<ovsbridge> for OVS bridges.
> +    These specific functions allows one to differentiate OVS bridges
> +    from other types of bridges.
> +
> +    Args:
> +        ovsbr (str): name of the OVS bridge
> +
> +    Returns:
> +        List[str]: a list with the ports of this bridge.
> +
> +    """
> +    if not is_openvswitch_running():
> +        return []
> +
> +    ovs_cmd = find_executable("ovs-vsctl")
> +
> +    # openvswitch not installed: there is no OVS bridge configured
> +    if ovs_cmd is None:
> +        return []
> +
> +    out, _, r_code = run_command([ovs_cmd, 'list-ports', ovsbr], silent=True)
> +    if r_code != 0:
> +        return []
> +
> +    return [x.strip() for x in out.rstrip('\n').split('\n') if x.strip()]
> +
> +
> +def all_interfaces():
> +    """Returns all interfaces of the host.
> +
> +    Returns:
> +        List[str]: a list with all interfaces of the host.
> +
> +    """
> +    return [d.rsplit("/", 1)[-1] for d in glob.glob(NET_PATH + '/*')]
> +
> +
> +def slaves(bonding):
> +    """Get all slaves from a bonding.
> +
> +    Args:
> +        bonding (str): the name of the bond.
> +
> +    Returns:
> +        List[str]: a list with all slaves.
> +
> +    """
> +    with open(BONDING_SLAVES % bonding) as bonding_file:
> +        res = bonding_file.readline().split()
> +    return res
> +
> +
> +def ports(bridge):
> +    """Get all ports from a bridge.
> +
> +    Args:
> +        bridge (str): the name of the OVS bridge.
> +
> +    Returns:
> +        List[str]: a list with all ports.
> +
> +    """
> +    if bridge in ovs_bridges():
> +        return ovs_bridge_ports(bridge)
> +
> +    return os.listdir(BRIDGE_PORTS % bridge)
> +
> +
> +def is_brport(nic):
> +    """Checks if nic is a port of a bridge.
> +
> +    Args:
> +        iface (str): name of the interface.
> +
> +    Returns:
> +        bool: True if iface is a port of a bridge, False otherwise.
> +
> +    """
> +    ovs_brports = []
> +
> +    for ovsbr in ovs_bridges():
> +        ovs_brports += ovs_bridge_ports(ovsbr)
> +
> +    return os.path.exists(NET_BRPORT % nic) or nic in ovs_brports
> +
> +
> +def is_bondlave(nic):
> +    """Checks if nic is a bond slave.
> +
> +    Args:
> +        iface (str): name of the interface.
> +
> +    Returns:
> +        bool: True if iface is a bond slave, False otherwise.
> +
> +    """
> +    return os.path.exists(NET_MASTER % nic)
> +
> +
> +def operstate(dev):
> +    """Get the operstate status of a device.
> +
> +    Args:
> +        dev (str): name of the device.
> +
> +    Returns:
> +        str: "up" or "down"
> +
> +    """
> +    flags = ethtool.get_flags(encode_value(dev))
> +    return 'up' if flags & (ethtool.IFF_RUNNING | ethtool.IFF_UP) else 'down'
> +
> +
> +def get_vlan_device(vlan):
> +    """ Return the device of the given VLAN.
> +
> +    Args:
> +        vlan (str): the vlan name.
> +
> +    Returns:
> +        str: the device of the VLAN.
> +
> +    """
> +    dev = None
> +
> +    if os.path.exists(PROC_NET_VLAN + vlan):
> +        with open(PROC_NET_VLAN + vlan) as vlan_file:
> +            for line in vlan_file:
> +                if "Device:" in line:
> +                    dummy, dev = line.split()
> +                    break
> +    return dev
> +
> +
> +def get_bridge_port_device(bridge):
> +    """Return the nics list that belongs to a port of 'bridge'.
> +
> +    Args:
> +        bridge (str): the bridge name.
> +
> +    Returns:
> +        List[str]: the nic list.
> +
> +    """
> +    #   br  --- v  --- bond --- nic1
> +    if encode_value(bridge) not in map(encode_value, bridges()):
> +        raise ValueError('unknown bridge %s' % bridge)
> +    nics_list = []
> +    for port in ports(bridge):
> +        if encode_value(port) in map(encode_value, vlans()):
> +            device = get_vlan_device(port)
> +            if encode_value(device) in map(encode_value, bondings()):
> +                nics_list.extend(slaves(device))
> +            else:
> +                nics_list.append(device)
> +        if encode_value(port) in map(encode_value, bondings()):
> +            nics_list.extend(slaves(port))
> +        else:
> +            nics_list.append(port)
> +    return nics_list
> +
> +
> +def aggregated_bridges():
> +    """Get the list of aggregated bridges of the host.
> +
> +    Returns:
> +        List[str]: the aggregated bridges list.
> +
> +    """
> +    return [bridge for bridge in bridges() if
> +            (set(get_bridge_port_device(bridge)) & set(nics()))]
> +
> +
> +def bare_nics():
> +    """Get the list of bare nics of the host.
> +
> +    A nic is called bare when it is not a port of a bridge
> +    or a slave of bond.
> +
> +    Returns:
> +        List[str]: the list of bare nics of the host.
> +
> +    """
> +    return [nic for nic in nics() if not (is_brport(nic) or is_bondlave(nic))]
> +
> +
> +def is_bare_nic(iface):
> +    """Checks if iface is a bare nic.
> +
> +    Args:
> +        iface (str): name of the interface.
> +
> +    Returns:
> +        bool: True if iface is a bare nic, False otherwise.
> +
> +    """
> +    return encode_value(iface) in map(encode_value, bare_nics())
> +
> +
> +#  The nic will not be exposed when it is a port of a bridge or
> +#  a slave of bond.
> +#  The bridge will not be exposed when all it's port are tap.
> +def all_favored_interfaces():
> +    """Get the list of all favored interfaces of the host.
> +
> +    The nic will not be exposed when it is a port of a bridge or
> +    a slave of bond. The bridge will not be exposed when all its
> +    port are tap.
> +
> +    Returns:
> +        List[str]: the list of favored interfaces.
> +
> +   """
> +    return aggregated_bridges() + bare_nics() + bondings()
> +
> +
> +def get_interface_type(iface):
> +    """Get the interface type of iface.
> +
> +    Types supported: nic, bonding, bridge, vlan. If the type
> +    can't be verified, 'unknown' is returned.
> +
> +    Args:
> +        iface (str): the interface name.
> +
> +    Returns:
> +        str: the interface type.
> +
> +    """
> +    try:
> +        if is_nic(iface):
> +            return "nic"
> +        if is_bonding(iface):
> +            return "bonding"
> +        if is_bridge(iface):
> +            return "bridge"
> +        if is_vlan(iface):
> +            return "vlan"
> +        return 'unknown'
> +    except IOError:
> +        return 'unknown'
> +
>   
>   def get_dev_macaddr(dev):
>       info = ethtool.get_interfaces_info(dev)[0]
> diff --git a/tests/test_model.py b/tests/test_model.py
> index 95c9e08..5398d08 100644
> --- a/tests/test_model.py
> +++ b/tests/test_model.py
> @@ -48,7 +48,7 @@ from wok.rollbackcontext import RollbackContext
>   from wok.utils import convert_data_size
>   from wok.xmlutils.utils import xpath_get_text
>   
> -from wok.plugins.gingerbase import netinfo
> +from wok.plugins.kimchi import network as netinfo
>   from wok.plugins.kimchi import osinfo
>   from wok.plugins.kimchi.config import kimchiPaths as paths
>   from wok.plugins.kimchi.model import model



More information about the Kimchi-devel mailing list