mirror of https://github.com/nicolargo/glances.git
Make public IP address retreiving call async
This commit is contained in:
parent
0d8352c9c7
commit
37d2fe13d0
|
|
@ -10,10 +10,9 @@
|
||||||
|
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from glances.globals import get_ip_address, json_loads, queue, urlopen_auth
|
from glances.globals import get_ip_address, json_loads, urlopen_auth
|
||||||
from glances.logger import logger
|
from glances.logger import logger
|
||||||
from glances.plugins.plugin.model import GlancesPluginModel
|
from glances.plugins.plugin.model import GlancesPluginModel
|
||||||
from glances.timer import Timer, getTimeSinceLastUpdate
|
|
||||||
|
|
||||||
# Fields description
|
# Fields description
|
||||||
# description: human readable description
|
# description: human readable description
|
||||||
|
|
@ -76,6 +75,17 @@ class IpPlugin(GlancesPluginModel):
|
||||||
"public_refresh_interval", default=self._default_public_refresh_interval
|
"public_refresh_interval", default=self._default_public_refresh_interval
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Init thread to grab public IP address asynchronously
|
||||||
|
self.public_ip_thread = None
|
||||||
|
if not self.public_disabled:
|
||||||
|
self.public_ip_thread = ThreadPublicIpAddress(
|
||||||
|
url=self.public_api,
|
||||||
|
username=self.public_username,
|
||||||
|
password=self.public_password,
|
||||||
|
refresh_interval=self.public_address_refresh_interval,
|
||||||
|
)
|
||||||
|
self.public_ip_thread.start()
|
||||||
|
|
||||||
def get_first_ip(self, stats):
|
def get_first_ip(self, stats):
|
||||||
stats['address'], stats['mask'] = get_ip_address()
|
stats['address'], stats['mask'] = get_ip_address()
|
||||||
stats['mask_cidr'] = self.ip_to_cidr(stats['mask'])
|
stats['mask_cidr'] = self.ip_to_cidr(stats['mask'])
|
||||||
|
|
@ -83,16 +93,19 @@ class IpPlugin(GlancesPluginModel):
|
||||||
return stats
|
return stats
|
||||||
|
|
||||||
def get_public_ip(self, stats):
|
def get_public_ip(self, stats):
|
||||||
time_since_update = getTimeSinceLastUpdate('public-ip')
|
"""Get public IP information from the background thread (non-blocking)."""
|
||||||
|
if self.public_ip_thread is None:
|
||||||
|
return stats
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if not self.public_disabled and (
|
# Read public IP info from the background thread (non-blocking)
|
||||||
self.public_address == "" or time_since_update > self.public_address_refresh_interval
|
self.public_info = self.public_ip_thread.public_info
|
||||||
):
|
if self.public_info:
|
||||||
self.public_info = PublicIpInfo(self.public_api, self.public_username, self.public_password).get()
|
self.public_address = self.public_info.get('ip', '')
|
||||||
self.public_address = self.public_info['ip']
|
|
||||||
except (KeyError, AttributeError, TypeError) as e:
|
except (KeyError, AttributeError, TypeError) as e:
|
||||||
logger.debug(f"Cannot grab public IP information ({e})")
|
logger.debug(f"Cannot grab public IP information ({e})")
|
||||||
else:
|
|
||||||
|
if self.public_address:
|
||||||
stats['public_address'] = (
|
stats['public_address'] = (
|
||||||
self.public_address if not self.args.hide_public_info else self.__hide_ip(self.public_address)
|
self.public_address if not self.args.hide_public_info else self.__hide_ip(self.public_address)
|
||||||
)
|
)
|
||||||
|
|
@ -128,6 +141,13 @@ class IpPlugin(GlancesPluginModel):
|
||||||
self.get_public_ip(stats)
|
self.get_public_ip(stats)
|
||||||
return stats
|
return stats
|
||||||
|
|
||||||
|
def exit(self):
|
||||||
|
"""Overwrite the exit method to close the thread."""
|
||||||
|
if self.public_ip_thread is not None:
|
||||||
|
self.public_ip_thread.stop()
|
||||||
|
# Call the parent class
|
||||||
|
super().exit()
|
||||||
|
|
||||||
def __hide_ip(self, ip):
|
def __hide_ip(self, ip):
|
||||||
"""Hide last to digit of the given IP address"""
|
"""Hide last to digit of the given IP address"""
|
||||||
return '.'.join(ip.split('.')[0:2]) + '.*.*'
|
return '.'.join(ip.split('.')[0:2]) + '.*.*'
|
||||||
|
|
@ -190,42 +210,71 @@ class IpPlugin(GlancesPluginModel):
|
||||||
return sum(bin(int(x)).count('1') for x in ip.split('.'))
|
return sum(bin(int(x)).count('1') for x in ip.split('.'))
|
||||||
|
|
||||||
|
|
||||||
class PublicIpInfo:
|
class ThreadPublicIpAddress(threading.Thread):
|
||||||
"""Get public IP information from online service."""
|
"""Thread class to fetch public IP address asynchronously.
|
||||||
|
|
||||||
|
This prevents blocking the main Glances startup and update cycle.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, url, username, password, refresh_interval, timeout=2):
|
||||||
|
"""Init the thread.
|
||||||
|
|
||||||
|
:param url: URL of the public IP API
|
||||||
|
:param username: Optional username for API authentication
|
||||||
|
:param password: Optional password for API authentication
|
||||||
|
:param refresh_interval: Time in seconds between refreshes
|
||||||
|
:param timeout: Timeout for the API request
|
||||||
|
"""
|
||||||
|
logger.debug("IP plugin - Create thread for public IP address")
|
||||||
|
super().__init__()
|
||||||
|
self.daemon = True
|
||||||
|
self._stopper = threading.Event()
|
||||||
|
|
||||||
def __init__(self, url, username, password, timeout=2):
|
|
||||||
"""Init the class."""
|
|
||||||
self.url = url
|
self.url = url
|
||||||
self.username = username
|
self.username = username
|
||||||
self.password = password
|
self.password = password
|
||||||
|
self.refresh_interval = refresh_interval
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
|
|
||||||
def get(self):
|
# Public IP information (shared with main thread)
|
||||||
"""Return the public IP information returned by one of the online service."""
|
self._public_info = {}
|
||||||
q = queue.Queue()
|
self._public_info_lock = threading.Lock()
|
||||||
|
|
||||||
t = threading.Thread(target=self._get_ip_public_info, args=(q, self.url, self.username, self.password))
|
def run(self):
|
||||||
t.daemon = True
|
"""Grab public IP information in a loop.
|
||||||
t.start()
|
|
||||||
|
|
||||||
timer = Timer(self.timeout)
|
Runs until stop() is called, refreshing at the configured interval.
|
||||||
info = None
|
"""
|
||||||
while not timer.finished() and info is None:
|
while not self._stopper.is_set():
|
||||||
if q.qsize() > 0:
|
# Fetch public IP information
|
||||||
info = q.get()
|
info = self._fetch_public_ip_info()
|
||||||
|
if info is not None:
|
||||||
|
with self._public_info_lock:
|
||||||
|
self._public_info = info
|
||||||
|
|
||||||
return info
|
# Wait for the refresh interval or until stopped
|
||||||
|
self._stopper.wait(self.refresh_interval)
|
||||||
|
|
||||||
def _get_ip_public_info(self, queue_target, url, username, password):
|
def _fetch_public_ip_info(self):
|
||||||
"""Request the url service and put the result in the queue_target."""
|
"""Fetch public IP information from the configured API."""
|
||||||
try:
|
try:
|
||||||
response = urlopen_auth(url, username, password).read()
|
response = urlopen_auth(self.url, self.username, self.password, self.timeout).read()
|
||||||
|
return json_loads(response)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f"IP plugin - Cannot get public IP information from {url} ({e})")
|
logger.debug(f"IP plugin - Cannot get public IP information from {self.url} ({e})")
|
||||||
queue_target.put(None)
|
return None
|
||||||
else:
|
|
||||||
try:
|
@property
|
||||||
queue_target.put(json_loads(response))
|
def public_info(self):
|
||||||
except (ValueError, KeyError) as e:
|
"""Return the current public IP information (thread-safe)."""
|
||||||
logger.debug(f"IP plugin - Cannot load public IP information from {url} ({e})")
|
with self._public_info_lock:
|
||||||
queue_target.put(None)
|
return self._public_info.copy()
|
||||||
|
|
||||||
|
def stop(self, timeout=None):
|
||||||
|
"""Stop the thread."""
|
||||||
|
logger.debug("IP plugin - Close thread for public IP address")
|
||||||
|
self._stopper.set()
|
||||||
|
|
||||||
|
def stopped(self):
|
||||||
|
"""Return True if the thread is stopped."""
|
||||||
|
return self._stopper.is_set()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue