From patchwork Mon Oct 10 14:07:25 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Kacur X-Patchwork-Id: 614085 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 45488C433F5 for ; Mon, 10 Oct 2022 14:07:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229779AbiJJOHo (ORCPT ); Mon, 10 Oct 2022 10:07:44 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59082 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229452AbiJJOHn (ORCPT ); Mon, 10 Oct 2022 10:07:43 -0400 Received: from mail-qk1-x736.google.com (mail-qk1-x736.google.com [IPv6:2607:f8b0:4864:20::736]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0FEE16E8BB for ; Mon, 10 Oct 2022 07:07:42 -0700 (PDT) Received: by mail-qk1-x736.google.com with SMTP id s7so2748151qkj.1 for ; Mon, 10 Oct 2022 07:07:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:sender:from:to:cc:subject:date:message-id:reply-to; bh=8YrPl5h8+80qDvCbGlLNHRN27s6/11HixGs4gCTqXCg=; b=D8eHQG2zzJ0i4eg7a9Dhe2nijqAiq/JENR7PLF3zRiNPe6xLnE3kkrqJUG4oQfBB0E FLEgSeWVbbgMpAWnqqyVfJjV1kkJVYZ0X4sXzcblKaVXUbe4g4+n9HZ2l3Hnxt4bA2KA JOsoevZ3RBnrDbd4KBEW3A7ufD+YGBmc1ndmQz6541ZUwOR3OVrVbygJatww40pk+Au1 vC77KGETLfE8UcKuSXeQXkxWSRe7ztZfFF5ooGJUUGD899UJukFcN+/lhZ2fQD0f5EfD 0IK3ad8jFQHPUT/rO6pmoyazQbwjDsxIqnauS+8IZiO8yDOCo7wBSgAQ4hbuvmxFnxCO ypbQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:sender:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=8YrPl5h8+80qDvCbGlLNHRN27s6/11HixGs4gCTqXCg=; b=HEKbR86xO1Hw/4bKQHhZLjjH7N78ndBFt4sgJaapKHEJmamwNeSwAyFYgotBPbprk9 0H4bdGaOl1UOEyIC70qlz4crzI/wMwF8FNOWnPTRMCFxzh87/W9UnBZ1kLv7b5N1wRpq uKc57p5VF5tUASU8Bg4TC+39jVMwNfPrEbriWz+g0uX/xaa/2TlmFs/lsGZnJZQR1kLN QireXxJZPz0DNA3TuwQ0rqWCWxKAXHq2426Bdmr+8yip5GHXdKHsLQly70zzJs5ILA2W ZAN98kmVB02jORZyz5dzjK0D2zZdzJMagQUC7cxpUzu57lpbQZLqPOqzUcc9+rZhORAT nWmQ== X-Gm-Message-State: ACrzQf3f34jw7jE84Nw89k7/gr1oXPHbCIBsNJWhl83Zk+WVpzzaW3Yx HQFAQiBsDqxYU3BVfvo4ZiNr9VwVZoU= X-Google-Smtp-Source: AMsMyM7Oi4nbbQY7booQB9Vre9EOsLFbOqBP7duLxt0GTO1IaBV0dQXD4lnRdUjaD3B1ygyHme6odA== X-Received: by 2002:a05:620a:44c7:b0:6ce:a0b5:4614 with SMTP id y7-20020a05620a44c700b006cea0b54614mr12785436qkp.753.1665410861130; Mon, 10 Oct 2022 07:07:41 -0700 (PDT) Received: from localhost.localdomain (bras-base-rdwyon0600w-grc-11-174-88-121-224.dsl.bell.ca. [174.88.121.224]) by smtp.gmail.com with ESMTPSA id i17-20020ac871d1000000b00397c4528a5bsm5960578qtp.79.2022.10.10.07.07.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 10 Oct 2022 07:07:40 -0700 (PDT) Sender: John Kacur From: John Kacur To: Clark Williams , RT Cc: Leah Leshchinsky , Kate Carcia Poulin , John Kacur Subject: [RFC PATCH] rteval: Replace python-ethtool with inline code Date: Mon, 10 Oct 2022 10:07:25 -0400 Message-Id: <20221010140725.125546-1-jkacur@redhat.com> X-Mailer: git-send-email 2.37.3 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-rt-users@vger.kernel.org This patch adds the file newnet.py which to replace the use of python-ethtool The information it generates will appear in the summary.xml You can also test the functionality by running python rteval/sysinfo/newnet.py Signed-off-by: John Kacur Reviewed-by: Kate Stewart --- rteval/sysinfo/__init__.py | 3 +- rteval/sysinfo/network.py | 117 ------------------- rteval/sysinfo/newnet.py | 224 +++++++++++++++++++++++++++++++++++++ 3 files changed, 226 insertions(+), 118 deletions(-) delete mode 100644 rteval/sysinfo/network.py create mode 100644 rteval/sysinfo/newnet.py diff --git a/rteval/sysinfo/__init__.py b/rteval/sysinfo/__init__.py index a4359382f006..bb1d00810856 100644 --- a/rteval/sysinfo/__init__.py +++ b/rteval/sysinfo/__init__.py @@ -32,7 +32,7 @@ from rteval.sysinfo.services import SystemServices from rteval.sysinfo.cputopology import CPUtopology from rteval.sysinfo.memory import MemoryInfo from rteval.sysinfo.osinfo import OSInfo -from rteval.sysinfo.network import NetworkInfo +from rteval.sysinfo.newnet import NetworkInfo from rteval.sysinfo.cmdline import cmdlineInfo from rteval.sysinfo import dmi @@ -46,6 +46,7 @@ class SystemInfo(KernelInfo, SystemServices, dmi.DMIinfo, CPUtopology, CPUtopology.__init__(self) OSInfo.__init__(self, logger=logger) cmdlineInfo.__init__(self, logger=logger) + NetworkInfo.__init__(self, logger=logger) # Parse initial DMI decoding errors dmi.ProcessWarnings() diff --git a/rteval/sysinfo/network.py b/rteval/sysinfo/network.py deleted file mode 100644 index ce9989a1240b..000000000000 --- a/rteval/sysinfo/network.py +++ /dev/null @@ -1,117 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2009 - 2013 David Sommerseth -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# For the avoidance of doubt the "preferred form" of this code is one which -# is in an open unpatent encumbered format. Where cryptographic key signing -# forms part of the process of creating an executable the information -# including keys needed to generate an equivalently functional executable -# are deemed to be part of the source code. -# - -import ethtool, libxml2 - -class NetworkInfo(object): - def __init__(self): - pass - - def net_GetDefaultGW(self): - # Get the interface name for the IPv4 default gw - route = open('/proc/net/route') - defgw4 = None - if route: - rl = route.readline() - while rl != '' : - rl = route.readline() - splt = rl.split("\t") - # Only catch default route - if len(splt) > 2 and splt[2] != '00000000' and splt[1] == '00000000': - defgw4 = splt[0] - break - route.close() - return (defgw4, None) # IPv6 gw not yet implemented - - def MakeReport(self): - ncfg_n = libxml2.newNode("NetworkConfig") - (defgw4, defgw6) = self.net_GetDefaultGW() - - # Make an interface tag for each device found - if hasattr(ethtool, 'get_interfaces_info'): - # Using the newer python-ethtool API (version >= 0.4) - for dev in ethtool.get_interfaces_info(ethtool.get_devices()): - if dev.device == 'lo': - continue - - intf_n = libxml2.newNode('interface') - intf_n.newProp('device', dev.device) - intf_n.newProp('hwaddr', dev.mac_address) - ncfg_n.addChild(intf_n) - - # Protcol configurations - if dev.ipv4_address: - ipv4_n = libxml2.newNode('IPv4') - ipv4_n.newProp('ipaddr', dev.ipv4_address) - ipv4_n.newProp('netmask', str(dev.ipv4_netmask)) - ipv4_n.newProp('broadcast', dev.ipv4_broadcast) - ipv4_n.newProp('defaultgw', (defgw4 == dev.device) and '1' or '0') - intf_n.addChild(ipv4_n) - - for ip6 in dev.get_ipv6_addresses(): - ipv6_n = libxml2.newNode('IPv6') - ipv6_n.newProp('ipaddr', ip6.address) - ipv6_n.newProp('netmask', str(ip6.netmask)) - ipv6_n.newProp('scope', ip6.scope) - intf_n.addChild(ipv6_n) - - else: # Fall back to older python-ethtool API (version < 0.4) - ifdevs = ethtool.get_active_devices() - ifdevs.remove('lo') - ifdevs.sort() - - for dev in ifdevs: - intf_n = libxml2.newNode('interface') - intf_n.newProp('device', dev.device) - intf_n.newProp('hwaddr', dev.mac_address) - ncfg_n.addChild(intf_n) - - ipv4_n = libxml2.newNode('IPv4') - ipv4_n.newProp('ipaddr', ethtool.get_ipaddr(dev)) - ipv4_n.newProp('netmask', str(ethtool.get_netmask(dev))) - ipv4_n.newProp('defaultgw', (defgw4 == dev) and '1' or '0') - intf_n.addChild(ipv4_n) - - return ncfg_n - - -def unit_test(rootdir): - import sys - try: - net = NetworkInfo() - doc = libxml2.newDoc('1.0') - cfg = net.MakeReport() - doc.setRootElement(cfg) - doc.saveFormatFileEnc('-', 'UTF-8', 1) - - except Exception as e: - import traceback - traceback.print_exc(file=sys.stdout) - print("** EXCEPTION %s", str(e)) - return 1 - -if __name__ == '__main__': - unit_test(None) - diff --git a/rteval/sysinfo/newnet.py b/rteval/sysinfo/newnet.py new file mode 100644 index 000000000000..c8763fb68957 --- /dev/null +++ b/rteval/sysinfo/newnet.py @@ -0,0 +1,224 @@ +''' Module to obtain network information for the rteval report ''' +# +# Copyright 2022 John Kacur 0: + (iface, dest, gateway, _, _, _, _, _, _, _, _) = line.split() + if iface == 'Iface': + line = f.readline().strip() + continue + if dest == '00000000' and gateway != '00000000': + addr = int(gateway, base=16) + defaultgw = str(ipaddress.IPv4Address(socket.ntohl(addr))) + return defaultgw + line = f.readline().strip() + return defaultgw + +class IPv6Addresses(): + ''' Obtains a list of IPv6 addresses from the proc file system ''' + + def __init__(self): + self.data = {} + IPv6Addresses.load(self) + + def __contains__(self, dev): + return dev in self.data + + def __getitem__(self, dev): + return self.data.get(dev, None) + + def __iter__(self): + return iter(self.data) + + def load(self): + ''' + Called by init to load the self.data dictionary with device keys + and a list of ipv6addresses + ''' + MYP = '/proc/net/if_inet6' + with open(MYP, 'r') as f: + mystr = f.readline().strip() + while len(mystr) > 0: + ipv6addr , _, _, _, _, intf = mystr.split() + ipv6addr = compress_iv6(ipv6addr) + if intf == 'lo': + mystr = f.readline().strip() + continue + if intf not in self.data: + self.data[intf] = [ipv6addr] + else: + self.data[intf].append(ipv6addr) + mystr = f.readline().strip() + +class IPv4Addresses(): + ''' Obtains a list of IPv4 addresses from the proc file system ''' + + def __init__(self): + self.data = {} + IPv4Addresses.load(self) + + def __contains__(self, dev): + return dev in self.data + + def __getitem__(self, dev): + return self.data[dev] + + def __iter__(self): + return iter(self.data) + + def load(self): + ''' + Called by init to load the self.data dictionary with + device keys, and value consisting of a list of + ipv4address, netmask and broadcast address + ''' + MYP = '/proc/net/route' + with open(MYP, 'r') as f: + mystr = f.readline().strip() + while len(mystr) > 0: + intf, dest, _, _, _, _, _, mask, _, _, _ = mystr.split() + # Skip over the head of the table an the gateway line + if intf == "Iface" or dest == '00000000': + mystr = f.readline().strip() + continue + d1 = int(dest, base=16) + m1 = int(mask, base=16) + addr = str(ipaddress.IPv4Address(socket.ntohl(d1))) + netmask = str(ipaddress.IPv4Address(socket.ntohl(m1))) + addr_with_mask = ipaddress.ip_network(addr + '/' + netmask) + broadcast = str(addr_with_mask.broadcast_address) + if intf not in self.data: + self.data[intf] = [(addr, netmask, broadcast)] + else: + self.data[intf].append((addr, netmask, broadcast)) + mystr = f.readline().strip() + + +class MacAddresses(): + ''' Obtains a list of hardware addresses of network devices ''' + + def __init__(self): + self.mac_address = {} + self.path = None + MacAddresses.load(self) + + def load(self): + ''' + called by init to load self.mac_address as a dictionary of + device keys, and mac or hardware addresses as values + ''' + nics = get_active_devices() + for nic in nics: + self.path = f'/sys/class/net/{nic}' + hwaddr = MacAddresses.set_val(self, 'address') + self.mac_address[nic] = hwaddr + + def set_val(self, val): + ''' Return the result of reading self.path/val ''' + val_path = f'{self.path}/{val}' + if os.path.exists(val_path): + with open(val_path, 'r') as f: + return f.readline().strip() + return None + + def __contains__(self, dev): + return dev in self.mac_address + + def __getitem__(self, dev): + return self.mac_address[dev] + + def __iter__(self): + return iter(self.mac_address) + +class NetworkInfo(): + ''' Creates an xml report of the network for rteval ''' + + def __init__(self, logger): + self.defgw4 = get_defaultgw() + self.__logger = logger + + def MakeReport(self): + ''' Make an xml report for rteval ''' + ncfg_n = libxml2.newNode("NetworkConfig") + defgw4 = self.defgw4 + + mads = MacAddresses() + for device in mads: + if device == 'lo': + continue + intf_n = libxml2.newNode('interface') + intf_n.newProp('device', device) + intf_n.newProp('hwaddr', mads[device]) + ncfg_n.addChild(intf_n) + + ipv4ads = IPv4Addresses() + ipv6ads = IPv6Addresses() + for dev in ipv4ads: + if dev != device: + continue + for lelem in ipv4ads[dev]: + ipv4_n = libxml2.newNode('IPv4') + (ipaddr, netmask, broadcast) = lelem + ipv4_n.newProp('ipaddr', ipaddr) + ipv4_n.newProp('netmask', netmask) + ipv4_n.newProp('broadcast', broadcast) + ipv4_n.newProp('defaultgw', (defgw4 == ipaddr) and '1' or '0') + intf_n.addChild(ipv4_n) + if ipv6ads[dev]: + for lelem in ipv6ads[dev]: + ipv6_n = libxml2.newNode('IPv6') + ipaddr = lelem + ipv6_n.newProp('ipaddr', ipaddr) + intf_n.addChild(ipv6_n) + return ncfg_n + +if __name__ == "__main__": + + try: + log = Log() + log.SetLogVerbosity(Log.DEBUG|Log.INFO) + net = NetworkInfo(logger=log) + doc = libxml2.newDoc('1.0') + cfg = net.MakeReport() + doc.setRootElement(cfg) + doc.saveFormatFileEnc('-', 'UTF-8', 1) + + except Exception as e: + import traceback + traceback.print_exc(file=sys.stdout) + print(f"** EXCEPTION {str(e)}")