mirror of
https://github.com/nicolargo/glances.git
synced 2026-10-07 09:21:56 -04:00
185 lines
6.3 KiB
Python
Executable File
185 lines
6.3 KiB
Python
Executable File
#!/usr/bin/env python
|
|
#
|
|
# Glances - An eye on your system
|
|
#
|
|
# SPDX-FileCopyrightText: 2026 Nicolas Hennion <nicolas@nicolargo.com>
|
|
#
|
|
# SPDX-License-Identifier: LGPL-3.0-only
|
|
#
|
|
|
|
"""Tests for glances.globals helper functions."""
|
|
|
|
import errno
|
|
import socket
|
|
from collections import OrderedDict
|
|
from types import SimpleNamespace
|
|
from unittest import TestCase
|
|
from unittest.mock import patch
|
|
|
|
from glances.globals import exit_after, get_ip_address
|
|
|
|
|
|
class TestExitAfter(TestCase):
|
|
def test_preserves_result_when_queue_is_denied(self):
|
|
# Strict Snap confinement can prevent creation of the timeout queue.
|
|
# The synchronous fallback must still return the wrapped function's result.
|
|
with (
|
|
patch('glances.globals.LINUX', True),
|
|
patch('glances.globals.ctx_mp_fork.Queue', side_effect=PermissionError(errno.EACCES, 'Permission denied')),
|
|
):
|
|
|
|
@exit_after(2, default=None)
|
|
def calculate_free(total, *, used):
|
|
return total - used
|
|
|
|
self.assertEqual(calculate_free(100, used=40), 60)
|
|
|
|
|
|
def _stat(isup=True):
|
|
return SimpleNamespace(isup=isup, duplex=0, speed=0, mtu=1500, flags='')
|
|
|
|
|
|
def _addr(family, address, netmask='255.255.255.0'):
|
|
return SimpleNamespace(family=family, address=address, netmask=netmask, broadcast=None, ptp=None)
|
|
|
|
|
|
def _no_route(*args, **kwargs):
|
|
"""Simulate a host without a default route (socket probe fails)."""
|
|
raise OSError("Network is unreachable")
|
|
|
|
|
|
class _FakeSocket:
|
|
"""A connected UDP socket whose kernel-chosen source address we control."""
|
|
|
|
def __init__(self, address):
|
|
self._address = address
|
|
|
|
def __call__(self, *args, **kwargs):
|
|
return self
|
|
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, *exc):
|
|
return False
|
|
|
|
def connect(self, target):
|
|
pass
|
|
|
|
def getsockname(self):
|
|
return (self._address, 0)
|
|
|
|
|
|
class TestGetIpAddress:
|
|
"""get_ip_address() should return the default-route interface's address,
|
|
falling back to the first up, non-loopback interface."""
|
|
|
|
def test_default_route_address_wins_over_interface_order(self):
|
|
# Regression test for #3465: the interface order must not matter.
|
|
# docker0 comes FIRST here; the kernel routes default traffic from
|
|
# eth0's address, so eth0's address must be returned.
|
|
addrs = {
|
|
'docker0': [_addr(socket.AF_INET, '172.18.0.1')],
|
|
'eth0': [_addr(socket.AF_INET, '192.168.0.150')],
|
|
}
|
|
|
|
with (
|
|
patch('glances.globals.socket.socket', _FakeSocket('192.168.0.150')),
|
|
patch('glances.globals.psutil.net_if_addrs', return_value=addrs),
|
|
):
|
|
ip_address, ip_netmask = get_ip_address()
|
|
|
|
assert ip_address == '192.168.0.150'
|
|
assert ip_netmask == '255.255.255.0'
|
|
|
|
def test_routed_address_without_psutil_entry_returns_none_netmask(self):
|
|
# ip_to_cidr() handles a None netmask (#1528), so the correct address
|
|
# must not be discarded just because its netmask cannot be found.
|
|
with (
|
|
patch('glances.globals.socket.socket', _FakeSocket('10.9.8.7')),
|
|
patch('glances.globals.psutil.net_if_addrs', return_value={}),
|
|
):
|
|
ip_address, ip_netmask = get_ip_address()
|
|
|
|
assert ip_address == '10.9.8.7'
|
|
assert ip_netmask is None
|
|
|
|
def test_returns_first_matching_interface_not_last(self):
|
|
# Regression test for #3617: on hosts with Docker, a virtual bridge
|
|
# interface (docker0) sorted after the real LAN interface used to
|
|
# silently win because the outer loop never stopped scanning.
|
|
stats = OrderedDict(
|
|
[
|
|
('eth0', _stat()),
|
|
('docker0', _stat()),
|
|
]
|
|
)
|
|
addrs = {
|
|
'eth0': [_addr(socket.AF_INET, '192.168.0.150')],
|
|
'docker0': [_addr(socket.AF_INET, '172.18.0.1')],
|
|
}
|
|
|
|
with (
|
|
patch('glances.globals.socket.socket', _no_route),
|
|
patch('glances.globals.psutil.net_if_stats', return_value=stats),
|
|
patch('glances.globals.psutil.net_if_addrs', return_value=addrs),
|
|
):
|
|
ip_address, ip_netmask = get_ip_address()
|
|
|
|
assert ip_address == '192.168.0.150'
|
|
assert ip_netmask == '255.255.255.0'
|
|
|
|
def test_loopback_probe_result_falls_back_to_interface_scan(self):
|
|
# Hosts that locally blackhole documentation/bogon ranges can resolve
|
|
# the probe to a loopback source; that must never be reported as the
|
|
# primary address (nor used as the zeroconf bind address).
|
|
stats = OrderedDict([('eth0', _stat())])
|
|
addrs = {'eth0': [_addr(socket.AF_INET, '192.168.0.150')]}
|
|
|
|
with (
|
|
patch('glances.globals.socket.socket', _FakeSocket('127.0.0.1')),
|
|
patch('glances.globals.psutil.net_if_stats', return_value=stats),
|
|
patch('glances.globals.psutil.net_if_addrs', return_value=addrs),
|
|
):
|
|
ip_address, ip_netmask = get_ip_address()
|
|
|
|
assert ip_address == '192.168.0.150'
|
|
assert ip_netmask == '255.255.255.0'
|
|
|
|
def test_skips_loopback_and_down_interfaces(self):
|
|
stats = OrderedDict(
|
|
[
|
|
('lo', _stat()),
|
|
('eth1', _stat(isup=False)),
|
|
('eth0', _stat()),
|
|
]
|
|
)
|
|
addrs = {
|
|
'lo': [_addr(socket.AF_INET, '127.0.0.1')],
|
|
'eth1': [_addr(socket.AF_INET, '10.0.0.5')],
|
|
'eth0': [_addr(socket.AF_INET, '192.168.0.150')],
|
|
}
|
|
|
|
with (
|
|
patch('glances.globals.socket.socket', _no_route),
|
|
patch('glances.globals.psutil.net_if_stats', return_value=stats),
|
|
patch('glances.globals.psutil.net_if_addrs', return_value=addrs),
|
|
):
|
|
ip_address, ip_netmask = get_ip_address()
|
|
|
|
assert ip_address == '192.168.0.150'
|
|
|
|
def test_returns_none_when_no_interface_matches(self):
|
|
stats = OrderedDict([('lo', _stat())])
|
|
addrs = {'lo': [_addr(socket.AF_INET, '127.0.0.1')]}
|
|
|
|
with (
|
|
patch('glances.globals.socket.socket', _no_route),
|
|
patch('glances.globals.psutil.net_if_stats', return_value=stats),
|
|
patch('glances.globals.psutil.net_if_addrs', return_value=addrs),
|
|
):
|
|
ip_address, ip_netmask = get_ip_address()
|
|
|
|
assert ip_address is None
|
|
assert ip_netmask is None
|