From nobody Wed Apr 24 23:12:58 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of ovirt.org designates 66.187.230.42 as permitted sender) client-ip=66.187.230.42; envelope-from=kimchi-devel-bounces@ovirt.org; helo=lists.ovirt.org; Authentication-Results: mx.zoho.com; spf=pass (zoho.com: domain of ovirt.org designates 66.187.230.42 as permitted sender) smtp.mailfrom=kimchi-devel-bounces@ovirt.org; Return-Path: Received: from lists.ovirt.org (lists.phx.ovirt.org [66.187.230.42]) by mx.zohomail.com with SMTPS id 1493136979425697.7674791779621; Tue, 25 Apr 2017 09:16:19 -0700 (PDT) Received: from lists.phx.ovirt.org (localhost [127.0.0.1]) by lists.ovirt.org (Postfix) with ESMTP id 2C86A82051A; Tue, 25 Apr 2017 16:16:18 +0000 (UTC) Received: from mx0a-001b2d01.pphosted.com (mx0a-001b2d01.pphosted.com [148.163.156.1]) by lists.ovirt.org (Postfix) with ESMTPS id 05765820518 for ; Tue, 25 Apr 2017 16:15:47 +0000 (UTC) Received: from pps.filterd (m0098394.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.20/8.16.0.20) with SMTP id v3PGDvFM126970 for ; Tue, 25 Apr 2017 12:15:47 -0400 Received: from e24smtp02.br.ibm.com (e24smtp02.br.ibm.com [32.104.18.86]) by mx0a-001b2d01.pphosted.com with ESMTP id 2a24a4ghmc-1 (version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT) for ; Tue, 25 Apr 2017 12:15:47 -0400 Received: from localhost by e24smtp02.br.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Tue, 25 Apr 2017 13:15:44 -0300 Received: from d24relay02.br.ibm.com (9.18.232.42) by e24smtp02.br.ibm.com (10.172.0.142) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Tue, 25 Apr 2017 13:15:43 -0300 Received: from d24av03.br.ibm.com (d24av03.br.ibm.com [9.8.31.95]) by d24relay02.br.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id v3PGFhvg2752856 for ; Tue, 25 Apr 2017 13:15:43 -0300 Received: from d24av03.br.ibm.com (localhost [127.0.0.1]) by d24av03.br.ibm.com (8.14.4/8.14.4/NCO v10.0 AVout) with ESMTP id v3PGFhSi013092 for ; Tue, 25 Apr 2017 13:15:43 -0300 Received: from alinefm-TP440.br.ibm.com (alinefm-TP440.br.ibm.com [9.18.239.173]) by d24av03.br.ibm.com (8.14.4/8.14.4/NCO v10.0 AVin) with ESMTP id v3PGFhuh013084 for ; Tue, 25 Apr 2017 13:15:43 -0300 X-Original-To: kimchi-devel@ovirt.org From: Aline Manera To: Kimchi Devel Date: Tue, 25 Apr 2017 13:15:41 -0300 X-Mailer: git-send-email 2.9.3 X-TM-AS-MML: disable x-cbid: 17042516-0020-0000-0000-000002A2FD80 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 17042516-0021-0000-0000-000030C024E6 Message-Id: <20170425161541.4225-1-alinefm@linux.vnet.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-04-25_09:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 suspectscore=13 malwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1703280000 definitions=main-1704250293 Subject: [Kimchi-devel] [PATCH] [Kimchi] Move Kimchi specific functions from gingerbase.netinfo to Kimchi X-BeenThere: kimchi-devel@ovirt.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: kimchi-devel-bounces@ovirt.org Errors-To: kimchi-devel-bounces@ovirt.org X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" 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 Reviewed-by: Daniel Barboza --- 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): =20 @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-130= 1 USA =20 +import ethtool + from wok.exception import InvalidParameter, NotFoundError +from wok.stringutils import encode_value +from wok.utils import wok_log =20 -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 =20 =20 class InterfacesModel(object): @@ -50,7 +53,29 @@ class InterfaceModel(object): pass =20 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 =3D '' + netmask =3D '' + module =3D 'unknown' + status =3D 'down' + try: + ipaddr =3D ethtool.get_ipaddr(encode_value(name)) + netmask =3D ethtool.get_netmask(encode_value(name)) + module =3D ethtool.get_module(encode_value(name)) + + flags =3D ethtool.get_flags(encode_value(name)) + status =3D 'up' if flags & (ethtool.IFF_RUNNING | ethtool.IFF_= UP) \ + else 'down' + except IOError: + pass + + iface_type =3D 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, NotFoundErro= r, OperationFailed from wok.utils import run_command, wok_log from wok.xmlutils.utils import xpath_get_text =20 -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 =3D network.XMLDesc(0) subnet =3D NetworkModel.get_network_from_xml(xml)['subnet'] subnet and invalid_addrs.append(ipaddr.IPNetwork(subnet)) - addr_pools =3D addr_pools if addr_pools else knetwork.PrivateN= ets - return knetwork.get_one_free_network(invalid_addrs, addr_pools) + addr_pools =3D addr_pools if addr_pools else netinfo.PrivateNe= ts + return netinfo.get_one_free_network(invalid_addrs, addr_pools) =20 def _set_network_subnet(self, params): netaddr =3D params.get('subnet', '') @@ -281,7 +278,7 @@ class NetworksModel(object): conn =3D self.conn.get() if iface_xml is None: try: - mac =3D knetwork.get_dev_macaddr(str(interface)) + mac =3D netinfo.get_dev_macaddr(str(interface)) iface_xml =3D get_iface_xml({'type': 'ethernet', 'name': interface, 'mac': mac, @@ -364,7 +361,7 @@ class NetworkModel(object): connection =3D 'macvtap' =20 # exposing the network on linux bridge or macvtap interface - interface_subnet =3D knetwork.get_dev_netaddr(interface) + interface_subnet =3D netinfo.get_dev_netaddr(interface) subnet =3D subnet if subnet else interface_subnet =20 # 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 =3D=3D 'bridge': iface =3D info['interfaces'][0] - if is_bridge(iface) and iface.startswith(KIMCHI_BRIDGE_PREFIX): - port =3D ports(iface)[0] - if is_vlan(port): - dev =3D get_vlan_device(port) + if (netinfo.is_bridge(iface) and + iface.startswith(KIMCHI_BRIDGE_PREFIX)): + port =3D netinfo.ports(iface)[0] + if netinfo.is_vlan(port): + dev =3D netinfo.get_vlan_device(port) info['interfaces'] =3D original['interfaces'] =3D [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-130= 1 USA =20 -from wok.plugins.gingerbase.netinfo import ovs_bridges +from wok.plugins.kimchi.network import ovs_bridges =20 =20 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 @@ # =20 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 =20 =20 APrivateNets =3D ipaddr.IPNetwork("10.0.0.0/8") @@ -30,6 +36,432 @@ DefaultNetsPool =3D [ipaddr.IPNetwork('192.168.122.0/23= '), ipaddr.IPNetwork('192.168.124.0/22'), ipaddr.IPNetwork('192.168.128.0/17')] =20 +NET_PATH =3D '/sys/class/net' +NIC_PATH =3D '/sys/class/net/*/device' +BRIDGE_PATH =3D '/sys/class/net/*/bridge' +BONDING_PATH =3D '/sys/class/net/*/bonding' +WLAN_PATH =3D '/sys/class/net/*/wireless' +NET_BRPORT =3D '/sys/class/net/%s/brport' +NET_MASTER =3D '/sys/class/net/%s/master' +PROC_NET_VLAN =3D '/proc/net/vlan/' +BONDING_SLAVES =3D '/sys/class/net/%s/bonding/slaves' +BRIDGE_PORTS =3D '/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 =3D ['systemctl', 'is-active', 'openvswitch', '--quiet'] + _, _, r_code =3D run_command(cmd, silent=3DTrue) + return r_code =3D=3D 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/ 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 =3D find_executable("ovs-vsctl") + + # openvswitch not installed: there is no OVS bridge configured + if ovs_cmd is None: + return [] + + out, _, r_code =3D run_command([ovs_cmd, 'list-br'], silent=3DTrue) + if r_code !=3D 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/ 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/ 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 =3D find_executable("ovs-vsctl") + + # openvswitch not installed: there is no OVS bridge configured + if ovs_cmd is None: + return [] + + out, _, r_code =3D run_command([ovs_cmd, 'list-ports', ovsbr], silent= =3DTrue) + if r_code !=3D 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 =3D 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 =3D [] + + for ovsbr in ovs_bridges(): + ovs_brports +=3D 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 =3D ethtool.get_flags(encode_value(dev)) + return 'up' if flags & (ethtool.IFF_RUNNING | ethtool.IFF_UP) else 'do= wn' + + +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 =3D 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 =3D 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 =3D [] + for port in ports(bridge): + if encode_value(port) in map(encode_value, vlans()): + device =3D 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(ni= c))] + + +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' + =20 def get_dev_macaddr(dev): info =3D 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 =20 -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 --=20 2.9.3 _______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel