[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