From c7d41f8a4b991417c7181fd85b6bda9ea7cfc839 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Mon, 9 Jun 2025 14:12:31 -0400 Subject: [PATCH] Support and prefer psutil The netifaces library seems dead, we can use psutil instead which seems more popular. --- .../confluent/discovery/protocols/pxe.py | 13 ++- confluent_server/confluent/netutil.py | 107 +++++++++++++----- confluent_server/confluent/util.py | 22 +++- confluent_server/confluent_server.spec.tmpl | 6 +- 4 files changed, 109 insertions(+), 39 deletions(-) diff --git a/confluent_server/confluent/discovery/protocols/pxe.py b/confluent_server/confluent/discovery/protocols/pxe.py index 5cb9a43c..56d870d6 100644 --- a/confluent_server/confluent/discovery/protocols/pxe.py +++ b/confluent_server/confluent/discovery/protocols/pxe.py @@ -36,7 +36,11 @@ import ctypes.util import eventlet import eventlet.green.socket as socket import eventlet.green.select as select -import netifaces +try: + import psutil +except ImportError: + psutil = None + import netifaces import os import struct import time @@ -136,7 +140,12 @@ def idxtoname(idx): _idxtobcast = {} def get_bcastaddr(idx): if idx not in _idxtobcast: - bc = netifaces.ifaddresses(idxtoname(idx))[17][0]['broadcast'] + if psutil: + for addr in psutil.net_if_addrs()[idxtoname(idx)]: + if addr.family == socket.AF_PACKET: + bc = addr.broadcast + else: + bc = netifaces.ifaddresses(idxtoname(idx))[17][0]['broadcast'] bc = bytearray([int(x, 16) for x in bc.split(':')]) _idxtobcast[idx] = bc return _idxtobcast[idx] diff --git a/confluent_server/confluent/netutil.py b/confluent_server/confluent/netutil.py index 2e580624..e7f303ba 100644 --- a/confluent_server/confluent/netutil.py +++ b/confluent_server/confluent/netutil.py @@ -18,7 +18,10 @@ import confluent.exceptions as exc import codecs -import netifaces +try: + import psutil +except ImportError: + import netifaces import struct import eventlet.green.socket as socket import eventlet.support.greendns @@ -32,9 +35,18 @@ def msg_align(len): return (len + 3) & ~3 def mask_to_cidr(mask): - maskn = socket.inet_pton(socket.AF_INET, mask) - maskn = struct.unpack('!I', maskn)[0] cidr = 32 + fam = socket.AF_INET + fmt = + if ':' in mask: # ipv6 + fam = socket.AF_INET6 + cidr = 128 + maskn = socket.inet_pton(fam, mask) + if len(maskn) == 4 + maskn = struct.unpack('!I', maskn)[0] + else: + first, second = struct.unpack('!QQ', maskn) + maskn = first << 64 | second while maskn & 0b1 == 0 and cidr > 0: cidr -= 1 maskn >>= 1 @@ -101,16 +113,25 @@ def ipn_is_local(ipn): def address_is_local(address): - for iface in netifaces.interfaces(): - for i4 in netifaces.ifaddresses(iface).get(2, []): - cidr = mask_to_cidr(i4['netmask']) - if ip_on_same_subnet(i4['addr'], address, cidr): - return True - for i6 in netifaces.ifaddresses(iface).get(10, []): - cidr = int(i6['netmask'].split('/')[1]) - laddr = i6['addr'].split('%')[0] - if ip_on_same_subnet(laddr, address, cidr): - return True + if psutil: + ifas = psutil.net_if_addrs() + for iface in ifas: + for addr in ifas[iface]: + if addr.family in (socket.AF_INET, socket.AF_INET6): + cidr = mask_to_cidr(addr.netmask) + if ip_on_same_subnet(addr.address, address, cidr): + return True + else: + for iface in netifaces.interfaces(): + for i4 in netifaces.ifaddresses(iface).get(2, []): + cidr = mask_to_cidr(i4['netmask']) + if ip_on_same_subnet(i4['addr'], address, cidr): + return True + for i6 in netifaces.ifaddresses(iface).get(10, []): + cidr = int(i6['netmask'].split('/')[1]) + laddr = i6['addr'].split('%')[0] + if ip_on_same_subnet(laddr, address, cidr): + return True return False @@ -126,20 +147,35 @@ def _rebuildidxmap(): def myiptonets(svrip): - fam = netifaces.AF_INET + fam = socket.AF_INET if ':' in svrip: - fam = netifaces.AF_INET6 + fam = socket.AF_INET6 relevantnic = None - for iface in netifaces.interfaces(): - for addr in netifaces.ifaddresses(iface).get(fam, []): - addr = addr.get('addr', '') - addr = addr.split('%')[0] - if addresses_match(addr, svrip): - relevantnic = iface - break - else: - continue - break + if psutil: + ifas = psutil.net_if_addrs() + for iface in ifas: + for addr in ifas[iface]: + if addr.fam != fam: + continue + addr = addr.address + addr = addr.split('%')[0] + if addresses_match(addr, svrip): + relevantnic = iface + break + else: + continue + break + else: + for iface in netifaces.interfaces(): + for addr in netifaces.ifaddresses(iface).get(fam, []): + addr = addr.get('addr', '') + addr = addr.split('%')[0] + if addresses_match(addr, svrip): + relevantnic = iface + break + else: + continue + break return inametonets(relevantnic) @@ -150,11 +186,22 @@ def _iftonets(ifidx): return inametonets(ifidx) def inametonets(iname): - addrs = netifaces.ifaddresses(iname) - try: - addrs = addrs[netifaces.AF_INET] - except KeyError: - return + addrs = [] + if psutil: + ifaces = psutil.net_if_addrs() + if iname not in ifaces: + return + for iface in ifaces: + for addrent in ifaces[iface]: + if addrent.family != socket.AF_INET: + continue + addrs.append({'addr': addrent.address, 'netmask': addrent.netmask}) + else: + addrs = netifaces.ifaddresses(iname) + try: + addrs = addrs[netifaces.AF_INET] + except KeyError: + return for addr in addrs: ip = struct.unpack('!I', socket.inet_aton(addr['addr']))[0] mask = struct.unpack('!I', socket.inet_aton(addr['netmask']))[0] diff --git a/confluent_server/confluent/util.py b/confluent_server/confluent/util.py index 462ec930..cb6c2973 100644 --- a/confluent_server/confluent/util.py +++ b/confluent_server/confluent/util.py @@ -20,7 +20,10 @@ import base64 import confluent.exceptions as cexc import confluent.log as log import hashlib -import netifaces +try: + import psutil +except ImportError: + import netifaces import os import re import socket @@ -85,11 +88,18 @@ def list_interface_indexes(): def list_ips(): # Used for getting addresses to indicate the multicast address # as well as getting all the broadcast addresses - for iface in netifaces.interfaces(): - addrs = netifaces.ifaddresses(iface) - if netifaces.AF_INET in addrs: - for addr in addrs[netifaces.AF_INET]: - yield addr + if psutil: + ifas = psutil.net_if_addrs() + for intf in ifas: + for addr in ifas[intf]: + if addr.family == socket.AF_INET and addr.broadcast: + yield {'broadcast': addr.broadcast, 'addr': addr.address} + else: + for iface in netifaces.interfaces(): + addrs = netifaces.ifaddresses(iface) + if netifaces.AF_INET in addrs: + for addr in addrs[netifaces.AF_INET]: + yield addr def randomstring(length=20): """Generate a random string of requested length diff --git a/confluent_server/confluent_server.spec.tmpl b/confluent_server/confluent_server.spec.tmpl index 670757fc..17b3da2c 100644 --- a/confluent_server/confluent_server.spec.tmpl +++ b/confluent_server/confluent_server.spec.tmpl @@ -24,14 +24,18 @@ Requires: python-pyghmi >= 1.5.71, python-eventlet, python-greenlet, python-pycr %if "%{dist}" == ".el8" Requires: python3-pyghmi >= 1.5.71, python3-eventlet, python3-greenlet, python3-pycryptodomex >= 3.4.7, confluent_client == %{version}, python3-pyparsing, python3-paramiko, python3-dns, python3-netifaces, python3-pyasn1 >= 0.2.3, python3-pysnmp >= 4.3.4, python3-lxml, python3-eficompressor, python3-setuptools, python3-dateutil, python3-enum34, python3-asn1crypto, python3-cffi, python3-pyOpenSSL, python3-websocket-client python3-msgpack python3-libarchive-c python3-yaml openssl iproute %else -%if "%{dist}" == ".el9" || "%{dist}" == ".el10" +%if "%{dist}" == ".el9" Requires: python3-pyghmi >= 1.5.71, python3-eventlet, python3-greenlet, python3-pycryptodomex >= 3.4.7, confluent_client == %{version}, python3-pyparsing, python3-paramiko, python3-dns, python3-webauthn, python3-netifaces, python3-pyasn1 >= 0.2.3, python3-pysnmp >= 4.3.4, python3-lxml, python3-eficompressor, python3-setuptools, python3-dateutil, python3-cffi, python3-pyOpenSSL, python3-websocket-client python3-msgpack python3-libarchive-c python3-yaml openssl iproute %else +%if "%{dist}" == ".el10" +Requires: python3-pyghmi >= 1.5.71, python3-eventlet, python3-greenlet, python3-pycryptodomex >= 3.4.7, confluent_client == %{version}, python3-pyparsing, python3-paramiko, python3-dns, python3-webauthn, python3-psutil, python3-pyasn1 >= 0.2.3, python3-pysnmp >= 4.3.4, python3-lxml, python3-eficompressor, python3-setuptools, python3-dateutil, python3-cffi, python3-pyOpenSSL, python3-websocket-client python3-msgpack python3-libarchive-c python3-yaml openssl iproute +%else Requires: python3-dbm,python3-pyghmi >= 1.5.71, python3-eventlet, python3-greenlet, python3-pycryptodome >= 3.4.7, confluent_client == %{version}, python3-pyparsing, python3-paramiko, python3-dnspython, python3-netifaces, python3-pyasn1 >= 0.2.3, python3-pysnmp >= 4.3.4, python3-lxml, python3-eficompressor, python3-setuptools, python3-dateutil, python3-cffi, python3-pyOpenSSL, python3-websocket-client python3-msgpack python3-libarchive-c python3-PyYAML openssl iproute %endif %endif %endif +%endif Vendor: Lenovo Url: https://github.com/lenovo/confluent