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
|
||||
|
||||
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.plugins.plugin.model import GlancesPluginModel
|
||||
from glances.timer import Timer, getTimeSinceLastUpdate
|
||||
|
||||
# Fields description
|
||||
# description: human readable description
|
||||
|
|
@ -76,6 +75,17 @@ class IpPlugin(GlancesPluginModel):
|
|||
"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):
|
||||
stats['address'], stats['mask'] = get_ip_address()
|
||||
stats['mask_cidr'] = self.ip_to_cidr(stats['mask'])
|
||||
|
|
@ -83,16 +93,19 @@ class IpPlugin(GlancesPluginModel):
|
|||
return 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:
|
||||
if not self.public_disabled and (
|
||||
self.public_address == "" or time_since_update > self.public_address_refresh_interval
|
||||
):
|
||||
self.public_info = PublicIpInfo(self.public_api, self.public_username, self.public_password).get()
|
||||
self.public_address = self.public_info['ip']
|
||||
# Read public IP info from the background thread (non-blocking)
|
||||
self.public_info = self.public_ip_thread.public_info
|
||||
if self.public_info:
|
||||
self.public_address = self.public_info.get('ip', '')
|
||||
except (KeyError, AttributeError, TypeError) as e:
|
||||
logger.debug(f"Cannot grab public IP information ({e})")
|
||||
else:
|
||||
|
||||
if self.public_address:
|
||||
stats['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)
|
||||
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):
|
||||
"""Hide last to digit of the given IP address"""
|
||||
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('.'))
|
||||
|
||||
|
||||
class PublicIpInfo:
|
||||
"""Get public IP information from online service."""
|
||||
class ThreadPublicIpAddress(threading.Thread):
|
||||
"""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.username = username
|
||||
self.password = password
|
||||
self.refresh_interval = refresh_interval
|
||||
self.timeout = timeout
|
||||
|
||||
def get(self):
|
||||
"""Return the public IP information returned by one of the online service."""
|
||||
q = queue.Queue()
|
||||
# Public IP information (shared with main thread)
|
||||
self._public_info = {}
|
||||
self._public_info_lock = threading.Lock()
|
||||
|
||||
t = threading.Thread(target=self._get_ip_public_info, args=(q, self.url, self.username, self.password))
|
||||
t.daemon = True
|
||||
t.start()
|
||||
def run(self):
|
||||
"""Grab public IP information in a loop.
|
||||
|
||||
timer = Timer(self.timeout)
|
||||
info = None
|
||||
while not timer.finished() and info is None:
|
||||
if q.qsize() > 0:
|
||||
info = q.get()
|
||||
Runs until stop() is called, refreshing at the configured interval.
|
||||
"""
|
||||
while not self._stopper.is_set():
|
||||
# Fetch public IP information
|
||||
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):
|
||||
"""Request the url service and put the result in the queue_target."""
|
||||
def _fetch_public_ip_info(self):
|
||||
"""Fetch public IP information from the configured API."""
|
||||
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:
|
||||
logger.debug(f"IP plugin - Cannot get public IP information from {url} ({e})")
|
||||
queue_target.put(None)
|
||||
else:
|
||||
try:
|
||||
queue_target.put(json_loads(response))
|
||||
except (ValueError, KeyError) as e:
|
||||
logger.debug(f"IP plugin - Cannot load public IP information from {url} ({e})")
|
||||
queue_target.put(None)
|
||||
logger.debug(f"IP plugin - Cannot get public IP information from {self.url} ({e})")
|
||||
return None
|
||||
|
||||
@property
|
||||
def public_info(self):
|
||||
"""Return the current public IP information (thread-safe)."""
|
||||
with self._public_info_lock:
|
||||
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