Merge branch 'develop' into feature/issue480

This commit is contained in:
Nicolas Hart 2015-03-30 22:28:22 +02:00
commit ac3d68e393
57 changed files with 1982 additions and 640 deletions

2
NEWS
View File

@ -9,6 +9,8 @@ Enhancements and news features:
* Grab FAN speed in the Glances sensors plugin (issue #501)
* Allow logical mounts points in the FS plugin (issue #448)
* Add a --disable-hddtemp to disable HDD temperature module at startup (issue #515)
* Add a quiet mode (-q). Can be usefull if you need a Statsd or Influxdb provider only.
Bugs corrected:

View File

@ -27,7 +27,7 @@ It uses the `psutil`_ library to get information from your system.
Requirements
============
- ``python >= 2.6`` (tested with version 2.6, 2.7, 3.3, 3.4)
- ``python >= 2.6`` or ``>= 3.3`` (tested with version 2.6, 2.7, 3.3, 3.4)
- ``psutil >= 2.0.0``
- ``setuptools``

1140
conf/glances-grafana.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -180,6 +180,7 @@ port=8086
user=root
password=root
db=glances
#prefix=localhost
[statsd]
host=localhost

View File

@ -171,6 +171,7 @@ port=8086
user=root
password=root
db=glances
#prefix=localhost
[statsd]
host=localhost

View File

@ -2,11 +2,11 @@
Glances
=======
This manual describes *Glances* version 2.3.
This manual describes *Glances* version 2.4.
Copyright © 2011-2015 Nicolas Hennion <nicolas@nicolargo.com>
January 2015
April 2015
.. contents:: Table of Contents
@ -143,6 +143,7 @@ Command-Line Options
--disable-fs disable filesystem module
--disable-network disable network module
--disable-sensors disable sensors module
--disable-hddtemp disable HDDTemp module
--disable-left-sidebar
disable left sidebar
--disable-process disable process module
@ -187,6 +188,7 @@ Command-Line Options
--snmp-force force SNMP mode
-t TIME, --time TIME set refresh time in seconds [default: 3 sec]
-w, --webserver run Glances in web server mode
-q, --quiet run Glances in quiet mode (nothing is displayed)
-f PROCESS_FILTER, --process-filter PROCESS_FILTER
set the process filter patern (regular expression)
--process-short-name force short name for processes name
@ -397,7 +399,7 @@ Disconnected:
.. image:: images/disconnected.png
QUICKLOOK
QuickLook
---------
The quicklook plugin is only display on wide screen and propose a bar view for CPU and memory (virtual and swap).
@ -789,6 +791,10 @@ and run Glances with:
$ glances --export-influxdb
For Grafana users', Glances provides a dedicated `dashboard`_. Just import the file in your Grafana Web interface.
.. image:: images/grafana.png
*Statsd*
You can export statistics to a Statsd server (welcome to Graphite !). The connection should be defined in the Glances configuration file as following:
@ -847,3 +853,4 @@ Feel free to contribute !
.. _XML-RPC server: http://docs.python.org/2/library/simplexmlrpcserver.html
.. _RESTFUL-JSON: http://jsonapi.org/
.. _forum: https://groups.google.com/forum/?hl=en#!forum/glances-users
.. _forum: https://github.com/nicolargo/glances/blob/master/conf/glances-grafana.json

BIN
docs/images/grafana.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

View File

@ -44,21 +44,17 @@ from glances.core.glances_globals import gettext_domain, locale_dir
from glances.core.glances_logging import logger
from glances.core.glances_main import GlancesMain
# Get PSutil version
psutil_min_version = (2, 0, 0)
psutil_version = tuple([int(num) for num in __psutil_version.split('.')])
# First log with Glances and PSUtil version
logger.info('Start Glances {0}'.format(__version__))
logger.info('{0} {1} and PSutil {2} detected'.format(platform.python_implementation(),
platform.python_version(),
__psutil_version))
# Check PSutil version
if psutil_version < psutil_min_version:
logger.critical('PSutil 2.0 or higher is needed. Glances cannot start.')
# Check Python version
if sys.version_info < (2, 6) or (3, 0) <= sys.version_info < (3, 3):
print('Glances requires at least Python 2.6 or 3.3 to run.')
sys.exit(1)
# Check PSutil version
psutil_min_version = (2, 0, 0)
psutil_version = tuple([int(num) for num in __psutil_version.split('.')])
if psutil_version < psutil_min_version:
print('PSutil 2.0 or higher is needed. Glances cannot start.')
sys.exit(1)
def __signal_handler(signal, frame):
"""Callback for CTRL-C."""
@ -94,8 +90,23 @@ def main():
Select the mode (standalone, client or server)
Run it...
"""
# Log Glances and PSutil version
logger.info('Start Glances {0}'.format(__version__))
logger.info('{0} {1} and PSutil {2} detected'.format(
platform.python_implementation(),
platform.python_version(),
__psutil_version))
# Setup translations
locale.setlocale(locale.LC_ALL, '')
try:
locale.setlocale(locale.LC_ALL, '')
except locale.Error:
# Issue #517
# Setting LC_ALL to '' should not generate an error unless LC_ALL is not
# defined in the user environment, which can be the case when used via SSH.
# So simply skip this error, as python will use the C locale by default.
logger.warning("No locale LC_ALL variable found. Use the default C locale.")
pass
gettext.install(gettext_domain, locale_dir)
# Share global var

View File

@ -28,11 +28,6 @@ try:
except ImportError:
# Python 2
from xmlrpclib import Transport, ServerProxy, ProtocolError, Fault
try:
import http.client as httplib
except ImportError:
# Python 2
import httplib
# Import Glances libs
from glances.core.glances_globals import version
@ -58,8 +53,8 @@ class GlancesClient(object):
self.args = args
self.config = config
# Client mode:
self.set_mode()
# Default client mode
self._client_mode = 'glances'
# Return to browser or exit
self.return_to_browser = return_to_browser
@ -89,22 +84,19 @@ class GlancesClient(object):
else:
logger.error(msg)
def set_mode(self, mode='glances'):
@property
def client_mode(self):
"""Get the client mode."""
return self._client_mode
@client_mode.setter
def client_mode(self, mode):
"""Set the client mode.
- 'glances' = Glances server (default)
- 'snmp' = SNMP (fallback)
"""
self.mode = mode
return self.mode
def get_mode(self):
"""Get the client mode.
- 'glances' = Glances server (default)
- 'snmp' = SNMP (fallback)
"""
return self.mode
self._client_mode = mode
def login(self):
"""Logon to the server."""
@ -112,15 +104,14 @@ class GlancesClient(object):
if not self.args.snmp_force:
# First of all, trying to connect to a Glances server
self.set_mode('glances')
client_version = None
try:
client_version = self.client.init()
except socket.error as err:
# Fallback to SNMP
logger.error("Connection to Glances server failed (%s)" % err)
self.set_mode('snmp')
fallbackmsg = _("Trying fallback to SNMP...")
self.client_mode = 'snmp'
logger.error("Connection to Glances server failed: {0}".format(err))
fallbackmsg = _("No Glances server found. Trying fallback to SNMP...")
if not self.return_to_browser:
print(fallbackmsg)
else:
@ -134,22 +125,25 @@ class GlancesClient(object):
self.log_and_exit(msg)
return False
if self.get_mode() == 'glances' and version.split('.')[0] == client_version.split('.')[0]:
# Init stats
self.stats = GlancesStatsClient(config=self.config, args=self.args)
self.stats.set_plugins(json.loads(self.client.getAllPlugins()))
logger.debug(
"Client version: %s / Server version: %s" % (version, client_version))
elif self.get_mode() == 'glances':
self.log_and_exit("Client and server not compatible: Client version: %s / Server version: %s" % (version, client_version))
return False
if self.client_mode == 'glances':
# Check that both client and server are in the same major version
if version.split('.')[0] == client_version.split('.')[0]:
# Init stats
self.stats = GlancesStatsClient(config=self.config, args=self.args)
self.stats.set_plugins(json.loads(self.client.getAllPlugins()))
logger.debug("Client version: {0} / Server version: {1}".format(version, client_version))
else:
self.log_and_exit("Client and server not compatible: \
Client version: {0} / Server version: {1}".format(version, client_version))
return False
else:
self.set_mode('snmp')
self.client_mode = 'snmp'
if self.get_mode() == 'snmp':
# SNMP mode
if self.client_mode == 'snmp':
logger.info("Trying to grab stats by SNMP...")
# Fallback to SNMP if needed
from glances.core.glances_stats import GlancesStatsClientSNMP
# Init stats
@ -172,13 +166,13 @@ class GlancesClient(object):
def update(self):
"""Update stats from Glances/SNMP server."""
if self.get_mode() == 'glances':
if self.client_mode == 'glances':
return self.update_glances()
elif self.get_mode() == 'snmp':
elif self.client_mode == 'snmp':
return self.update_snmp()
else:
self.end()
logger.critical("Unknown server mode: {0}".format(self.get_mode()))
logger.critical("Unknown server mode: {0}".format(self.client_mode))
sys.exit(2)
def update_glances(self):
@ -237,7 +231,7 @@ class GlancesClient(object):
# Export stats using export modules
self.stats.export(self.stats)
return self.get_mode()
return self.client_mode
def end(self):
"""End of the client session."""

View File

@ -146,42 +146,40 @@ class GlancesClientBrowser(object):
"Server list dictionnary change inside the loop (wait next update)")
# Update the screen (list or Glances client)
if self.screen.get_active() is None:
if self.screen.active_server is None:
# Display the Glances browser
self.screen.update(self.get_servers_list())
else:
# Display the Glances client for the selected server
logger.debug("Selected server: %s" % self.get_servers_list()[self.screen.get_active()])
logger.debug("Selected server: {0}".format(self.get_servers_list()[self.screen.active_server]))
# Connection can take time
# Display a popup
self.screen.display_popup(_("Connect to %s:%s" % (v['name'], v['port'])), duration=1)
# A password is needed to access to the server's stats
if self.get_servers_list()[self.screen.get_active()]['password'] is None:
if self.get_servers_list()[self.screen.active_server]['password'] is None:
from hashlib import sha256
# Display a popup to enter password
clear_password = self.screen.display_popup(_("Password needed for %s: " % v['name']), is_input=True)
# Hash with SHA256
encoded_password = sha256(clear_password).hexdigest()
encoded_password = sha256(clear_password.encode('utf-8')).hexdigest()
# Store the password for the selected server
self.set_in_selected('password', encoded_password)
# Display the Glance client on the selected server
logger.info("Connect Glances client to the %s server" %
self.get_servers_list()[self.screen.get_active()]['key'])
logger.info("Connect Glances client to the {0} server".format(
self.get_servers_list()[self.screen.active_server]['key']))
# Init the client
args_server = self.args
# Overwrite connection setting
args_server.client = self.get_servers_list()[self.screen.get_active()]['ip']
args_server.port = self.get_servers_list()[self.screen.get_active()]['port']
args_server.username = self.get_servers_list()[self.screen.get_active()]['username']
args_server.password = self.get_servers_list()[self.screen.get_active()]['password']
client = GlancesClient(config=self.config,
args=args_server,
return_to_browser=True)
args_server.client = self.get_servers_list()[self.screen.active_server]['ip']
args_server.port = self.get_servers_list()[self.screen.active_server]['port']
args_server.username = self.get_servers_list()[self.screen.active_server]['username']
args_server.password = self.get_servers_list()[self.screen.active_server]['password']
client = GlancesClient(config=self.config, args=args_server, return_to_browser=True)
# Test if client and server are in the same major version
if not client.login():
@ -195,8 +193,8 @@ class GlancesClientBrowser(object):
connection_type = client.serve_forever()
try:
logger.debug("Disconnect Glances client from the %s server" %
self.get_servers_list()[self.screen.get_active()]['key'])
logger.debug("Disconnect Glances client from the {0} server".format(
self.get_servers_list()[self.screen.active_server]['key']))
except IndexError:
# Server did not exist anymore
pass
@ -208,19 +206,17 @@ class GlancesClientBrowser(object):
self.set_in_selected('status', 'ONLINE')
# Return to the browser (no server selected)
self.screen.set_active(None)
self.screen.active_server = None
def set_in_selected(self, key, value):
"""Set the (key, value) for the selected server in the list"""
"""Set the (key, value) for the selected server in the list."""
# Static list then dynamic one
if self.screen.get_active() >= len(self.static_server.get_servers_list()):
self.autodiscover_server.set_server(self.screen.get_active() - len(self.static_server.get_servers_list()),
key,
value)
if self.screen.active_server >= len(self.static_server.get_servers_list()):
self.autodiscover_server.set_server(
self.screen.active_server - len(self.static_server.get_servers_list()),
key, value)
else:
self.static_server.set_server(self.screen.get_active(),
key,
value)
self.static_server.set_server(self.screen.active_server, key, value)
def end(self):
"""End of the client browser session."""

View File

@ -17,10 +17,15 @@
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Custom logging class"""
"""Custom logging class."""
import logging
import logging.config
try:
# Python 2.6
from logutils.dictconfig import dictConfig
except ImportError:
# Python >= 2.7
from logging.config import dictConfig
import os
import tempfile
@ -72,29 +77,24 @@ LOGGING_CFG = {
def tempfile_name():
"""Return the tempfile name (full path)"""
"""Return the tempfile name (full path)."""
ret = os.path.join(tempfile.gettempdir(), 'glances.log')
if os.access(ret, os.F_OK) and not os.access(ret, os.W_OK):
print("Warning: can't write logs to file {} (permission denied)".format(ret))
print("WARNING: Couldn't write to log file {0}: (Permission denied)".format(ret))
ret = tempfile.mkstemp(prefix='glances', suffix='.tmp', text=True)
print("Create a new log file: {}".format(ret[1]))
print("Create a new log file: {0}".format(ret[1]))
return ret[1]
return ret
def glances_logger():
"""Build and return the logger"""
"""Build and return the logger."""
temp_path = tempfile_name()
_logger = logging.getLogger()
try:
LOGGING_CFG['handlers']['file']['filename'] = temp_path
logging.config.dictConfig(LOGGING_CFG)
except AttributeError:
# dictConfig is only available for Python 2.7 or higher
# Minimal configuration for Python 2.6
logging.basicConfig(filename=temp_path,
level=logging.DEBUG,
format='%(asctime)s -- %(levelname)s -- %(message)s')
LOGGING_CFG['handlers']['file']['filename'] = temp_path
dictConfig(LOGGING_CFG)
return _logger
logger = glances_logger()

View File

@ -87,22 +87,17 @@ class GlancesLogs(object):
else:
# Default sort is...
process_auto_by = 'cpu_percent'
glances_processes.setautosortkey(process_auto_by)
return process_auto_by
glances_processes.auto_sort = True
glances_processes.sort_key = process_auto_by
def reset_process_sort(self):
"""Reset the process_auto_by variable."""
"""Reset the process auto sort key."""
# Default sort is...
process_auto_by = 'cpu_percent'
glances_processes.setautosortkey(process_auto_by)
glances_processes.setmanualsortkey(None)
return process_auto_by
glances_processes.auto_sort = True
glances_processes.sort_key = 'cpu_percent'
def add(self, item_state, item_type, item_value,
proc_list=[], proc_desc="",
peak_time=3):
proc_list=None, proc_desc="", peak_time=6):
"""Add a new item to the logs list.
If 'item' is a 'new one', add the new item at the beginning of the logs
@ -110,6 +105,8 @@ class GlancesLogs(object):
If 'item' is not a 'new one', update the existing item.
If event < peak_time the the alert is not setoff
"""
proc_list = proc_list or []
# Add or update the log
item_index = self.__itemexist__(item_type)
if item_index < 0:
@ -121,24 +118,23 @@ class GlancesLogs(object):
# Create the new log item
# Time is stored in Epoch format
# Epoch -> DMYHMS = datetime.fromtimestamp(epoch)
item = []
# START DATE
item.append(time.mktime(datetime.now().timetuple()))
item.append(-1) # END DATE
item.append(item_state) # STATE: WARNING|CRITICAL
item.append(item_type) # TYPE: CPU, LOAD, MEM...
item.append(item_value) # MAX
item.append(item_value) # AVG
item.append(item_value) # MIN
item.append(item_value) # SUM
item.append(1) # COUNT
# Process list is sorted automaticaly
# Overwrite the user choise
# topprocess = sorted(proc_list, key=lambda process: process[process_auto_by],
# reverse=True)
# item.append(topprocess[0:3]) # TOP 3 PROCESS LIST
item.append([]) # TOP 3 PROCESS LIST
item.append(proc_desc) # MONITORED PROCESSES DESC
item = [
time.mktime(datetime.now().timetuple()), # START DATE
-1, # END DATE
item_state, # STATE: WARNING|CRITICAL
item_type, # TYPE: CPU, LOAD, MEM...
item_value, # MAX
item_value, # AVG
item_value, # MIN
item_value, # SUM
1, # COUNT
# Process list is sorted automatically
# Overwrite the user choice
# topprocess = sorted(proc_list, key=lambda process: process[process_auto_by],
# reverse=True)
# topprocess[0:3], # TOP 3 PROCESS LIST
[], # TOP 3 PROCESS LIST
proc_desc] # MONITORED PROCESSES DESC
# Add the item to the list
self.logs_list.insert(0, item)

View File

@ -110,6 +110,8 @@ Start the client browser (browser mode):\n\
dest='disable_fs', help=_('disable filesystem module'))
parser.add_argument('--disable-sensors', action='store_true', default=False,
dest='disable_sensors', help=_('disable sensors module'))
parser.add_argument('--disable-hddtemp', action='store_true', default=False,
dest='disable_hddtemp', help=_('disable HD Temperature module'))
parser.add_argument('--disable-raid', action='store_true', default=False,
dest='disable_raid', help=_('disable RAID module'))
parser.add_argument('--disable-docker', action='store_true', default=False,
@ -171,6 +173,8 @@ Start the client browser (browser mode):\n\
parser.add_argument('-w', '--webserver', action='store_true', default=False,
dest='webserver', help=_('run Glances in web server mode (need Bootle lib)'))
# Display options
parser.add_argument('-q', '--quiet', default=False, action='store_true',
dest='quiet', help=_('Do not display the Curse interface'))
parser.add_argument('-f', '--process-filter', default=None, type=str,
dest='process_filter', help=_('set the process filter pattern (regular expression)'))
parser.add_argument('--process-short-name', action='store_true', default=False,
@ -187,7 +191,7 @@ Start the client browser (browser mode):\n\
parser.add_argument('--fs-free-space', action='store_false', default=False,
dest='fs_free_space', help=_('display FS free space instead of used'))
parser.add_argument('--theme-white', action='store_true', default=False,
dest='theme_white', help=_('optimize display for white background'))
dest='theme_white', help=_('optimize display colors for white background'))
return parser
@ -264,6 +268,11 @@ Start the client browser (browser mode):\n\
sys.exit(2)
logger.debug("History output path is set to {0}".format(args.path_history))
# Disable HDDTemp if sensors are disabled
if args.disable_sensors:
args.disable_hddtemp = True
logger.debug("Sensors and HDDTemp are disabled")
return args
def __hash_password(self, plain_password):

View File

@ -74,7 +74,6 @@ class MonitorList(object):
countmax = self.config.get_raw_option(section, key + "countmax")
except Exception as e:
logger.error("Cannot read monitored list: {0}".format(e))
pass
else:
if description is not None and regex is not None:
# Build the new item

View File

@ -250,11 +250,11 @@ class GlancesProcesses(object):
self.process_tree = None
# Init stats
self.resetsort()
self.auto_sort = True
self._sort_key = 'cpu_percent'
self.allprocesslist = []
self.processlist = []
self.processcount = {
'total': 0, 'running': 0, 'sleeping': 0, 'thread': 0}
self.processcount = {'total': 0, 'running': 0, 'sleeping': 0, 'thread': 0}
# Tag to enable/disable the processes stats (to reduce the Glances CPU consumption)
# Default is to enable the processes stats
@ -263,13 +263,12 @@ class GlancesProcesses(object):
# Extended stats for top process is enable by default
self.disable_extended_tag = False
# Maximum number of processes showed in the UI interface
# None if no limit
self.max_processes = None
# Maximum number of processes showed in the UI (None if no limit)
self._max_processes = None
# Process filter is a regular expression
self.process_filter = None
self.process_filter_re = None
self._process_filter = None
self._process_filter_re = None
# Whether or not to hide kernel threads
self.no_kernel_threads = False
@ -292,48 +291,49 @@ class GlancesProcesses(object):
"""Disable extended process stats."""
self.disable_extended_tag = True
def set_max_processes(self, value):
"""Set the maximum number of processes showed in the UI interfaces"""
self.max_processes = value
return self.max_processes
@property
def max_processes(self):
"""Get the maximum number of processes showed in the UI."""
return self._max_processes
def get_max_processes(self):
"""Get the maximum number of processes showed in the UI interfaces"""
return self.max_processes
@max_processes.setter
def max_processes(self, value):
"""Set the maximum number of processes showed in the UI."""
self._max_processes = value
def set_process_filter(self, value):
"""Set the process filter"""
@property
def process_filter(self):
"""Get the process filter."""
return self._process_filter
@process_filter.setter
def process_filter(self, value):
"""Set the process filter."""
logger.info("Set process filter to {0}".format(value))
self.process_filter = value
self._process_filter = value
if value is not None:
try:
self.process_filter_re = re.compile(value)
logger.debug(
"Process filter regex compilation OK: {0}".format(self.get_process_filter()))
self._process_filter_re = re.compile(value)
logger.debug("Process filter regex compilation OK: {0}".format(self.process_filter))
except Exception:
logger.error(
"Cannot compile process filter regex: {0}".format(value))
self.process_filter_re = None
logger.error("Cannot compile process filter regex: {0}".format(value))
self._process_filter_re = None
else:
self.process_filter_re = None
return self.process_filter
self._process_filter_re = None
def get_process_filter(self):
"""Get the process filter"""
return self.process_filter
def get_process_filter_re(self):
"""Get the process regular expression compiled"""
return self.process_filter_re
@property
def process_filter_re(self):
"""Get the process regular expression compiled."""
return self._process_filter_re
def is_filtered(self, value):
"""Return True if the value should be filtered"""
if self.get_process_filter() is None:
if self.process_filter is None:
# No filter => Not filtered
return False
else:
# logger.debug(self.get_process_filter() + " <> " + value + " => " + str(self.get_process_filter_re().match(value) is None))
return self.get_process_filter_re().match(value) is None
# logger.debug(self.process_filter + " <> " + value + " => " + str(self.process_filter_re.match(value) is None))
return self.process_filter_re.match(value) is None
def disable_kernel_threads(self):
""" Ignore kernel threads in process list. """
@ -541,8 +541,7 @@ class GlancesProcesses(object):
"""
# Reset the stats
self.processlist = []
self.processcount = {
'total': 0, 'running': 0, 'sleeping': 0, 'thread': 0}
self.processcount = {'total': 0, 'running': 0, 'sleeping': 0, 'thread': 0}
# Do not process if disable tag is set
if self.disable_tag:
@ -558,11 +557,11 @@ class GlancesProcesses(object):
if self.no_kernel_threads and not is_windows and is_kernel_thread(proc):
continue
# If self.get_max_processes() is None: Only retreive mandatory stats
# If self.max_processes is None: Only retreive mandatory stats
# Else: retreive mandatory and standard stats
s = self.__get_process_stats(proc,
mandatory_stats=True,
standard_stats=self.get_max_processes() is None)
standard_stats=self.max_processes is None)
# Continue to the next process if it has to be filtered
if s is None or (self.is_filtered(s['cmdline']) and self.is_filtered(s['name'])):
continue
@ -571,8 +570,10 @@ class GlancesProcesses(object):
# ignore the 'idle' process on Windows and *BSD
# ignore the 'kernel_task' process on OS X
# waiting for upstream patch from psutil
if is_bsd and processdict[proc]['name'] == 'idle' or is_windows and processdict[proc]['name'] == 'System Idle Process' or is_mac and processdict[proc]['name'] == 'kernel_task':
continue
if (is_bsd and processdict[proc]['name'] == 'idle' or
is_windows and processdict[proc]['name'] == 'System Idle Process' or
is_mac and processdict[proc]['name'] == 'kernel_task'):
continue
# Update processcount (global statistics)
try:
self.processcount[str(proc.status())] += 1
@ -594,12 +595,12 @@ class GlancesProcesses(object):
if self._enable_tree:
self.process_tree = ProcessTreeNode.build_tree(processdict,
self.getsortkey(),
self.sort_key,
self.no_kernel_threads)
for i, node in enumerate(self.process_tree):
# Only retreive stats for visible processes (get_max_processes)
if self.get_max_processes() is not None and i >= self.get_max_processes():
# Only retreive stats for visible processes (max_processes)
if self.max_processes is not None and i >= self.max_processes:
break
# add standard stats
@ -615,22 +616,21 @@ class GlancesProcesses(object):
else:
# Process optimization
# Only retreive stats for visible processes (get_max_processes)
if self.get_max_processes() is not None:
# Only retreive stats for visible processes (max_processes)
if self.max_processes is not None:
# Sort the internal dict and cut the top N (Return a list of tuple)
# tuple=key (proc), dict (returned by __get_process_stats)
try:
processiter = sorted(
processdict.items(), key=lambda x: x[1][self.getsortkey()], reverse=True)
processdict.items(), key=lambda x: x[1][self.sort_key], reverse=True)
except (KeyError, TypeError) as e:
logger.error(
"Cannot sort process list by %s (%s)" % (self.getsortkey(), e))
logger.error("Cannot sort process list by {0}: {1}".format(self.sort_key, e))
logger.error("%s" % str(processdict.items()[0]))
# Fallback to all process (issue #423)
processloop = processdict.items()
first = False
else:
processloop = processiter[0:self.get_max_processes()]
processloop = processiter[0:self.max_processes]
first = True
else:
# Get all processes stats
@ -640,7 +640,7 @@ class GlancesProcesses(object):
for i in processloop:
# Already existing mandatory stats
procstat = i[1]
if self.get_max_processes() is not None:
if self.max_processes is not None:
# Update with standard stats
# and extended stats but only for TOP (first) process
s = self.__get_process_stats(i[0],
@ -687,37 +687,17 @@ class GlancesProcesses(object):
"""Get the process tree."""
return self.process_tree
def getsortkey(self):
"""Get the current sort key"""
if self.getmanualsortkey() is not None:
return self.getmanualsortkey()
else:
return self.getautosortkey()
@property
def sort_key(self):
"""Get the current sort key."""
return self._sort_key
def getmanualsortkey(self):
"""Get the current sort key for manual sort."""
return self.processmanualsort
def getautosortkey(self):
"""Get the current sort key for automatic sort."""
return self.processautosort
def setmanualsortkey(self, sortedby):
"""Set the current sort key for manual sort."""
self.processmanualsort = sortedby
if self._enable_tree and (self.process_tree is not None):
self.process_tree.set_sorting(sortedby, sortedby != "name")
return self.processmanualsort
def setautosortkey(self, sortedby):
"""Set the current sort key for automatic sort."""
self.processautosort = sortedby
return self.processautosort
def resetsort(self):
"""Set the default sort: Auto"""
self.setmanualsortkey(None)
self.setautosortkey('cpu_percent')
@sort_key.setter
def sort_key(self, key):
"""Set the current sort key."""
self._sort_key = key
if not self.auto_sort and self._enable_tree and self.process_tree is not None:
self.process_tree.set_sorting(key, key != "name")
def getsortlist(self, sortedby=None):
"""Get the sorted processlist."""

View File

@ -100,7 +100,7 @@ class GlancesXMLRPCHandler(SimpleXMLRPCRequestHandler):
self.send_error(401, 'Authentication failed')
return False
def log_message(self, format, *args):
def log_message(self, log_format, *args):
# No message displayed on the server side
pass

View File

@ -32,12 +32,12 @@ class GlancesStandalone(object):
"""This class creates and manages the Glances standalone session."""
def __init__(self, config=None, args=None):
# Quiet mode
self._quiet = args.quiet
# Init stats
self.stats = GlancesStats(config=config, args=args)
# Default number of processes to displayed is set to 50
glances_processes.set_max_processes(50)
# If process extended stats is disabled by user
if not args.enable_process_extended:
logger.debug("Extended stats for top process are disabled")
@ -48,7 +48,7 @@ class GlancesStandalone(object):
# Manage optionnal process filter
if args.process_filter is not None:
glances_processes.set_process_filter(args.process_filter)
glances_processes.process_filter = args.process_filter
if (not is_windows) and args.no_kernel_threads:
# Ignore kernel threads in process list
@ -61,8 +61,20 @@ class GlancesStandalone(object):
# Initial system informations update
self.stats.update()
# Init screen
self.screen = GlancesCursesStandalone(args=args)
if self.quiet:
logger.info("Quiet mode is ON: Nothing will be displayed")
# In quiet mode, nothing is displayed
glances_processes.max_processes = 0
else:
# Default number of processes to displayed is set to 50
glances_processes.max_processes = 50
# Init screen
self.screen = GlancesCursesStandalone(args=args)
@property
def quiet(self):
return self._quiet
def serve_forever(self):
"""Main loop for the CLI."""
@ -71,14 +83,16 @@ class GlancesStandalone(object):
self.stats.update()
# Update the screen
self.screen.update(self.stats)
if not self.quiet:
self.screen.update(self.stats)
# Export stats using export modules
self.stats.export(self.stats)
def end(self):
"""End of the standalone CLI."""
self.screen.end()
if not self.quiet:
self.screen.end()
# Exit from export modules
self.stats.end()

View File

@ -155,10 +155,12 @@ class GlancesStats(object):
# logger.debug("Update %s stats" % p)
self._plugins[p].update()
def export(self, input_stats={}):
def export(self, input_stats=None):
"""Export all the stats.
Each export module is ran in a dedicated thread."""
# threads = []
input_stats = input_stats or {}
for e in self._exports:
logger.debug("Export stats using the %s module" % e)
thread = threading.Thread(target=self._exports[e].update,
@ -181,13 +183,13 @@ class GlancesStats(object):
def getAllLimits(self):
"""Return the plugins limits list."""
return [self._plugins[p].get_limits() for p in self._plugins]
return [self._plugins[p].limits for p in self._plugins]
def getAllLimitsAsDict(self):
"""Return all the stats limits (dict)"""
ret = {}
for p in self._plugins:
ret[p] = self._plugins[p].get_limits()
ret[p] = self._plugins[p].limits
return ret
def getAllViews(self):
@ -203,7 +205,7 @@ class GlancesStats(object):
def get_plugin_list(self):
"""Return the plugin list."""
self._plugins
return self._plugins
def get_plugin(self, plugin_name):
"""Return the plugin name."""
@ -231,8 +233,10 @@ class GlancesStatsServer(GlancesStats):
# all_stats is a dict of dicts filled by the server
self.all_stats = collections.defaultdict(dict)
def update(self, input_stats={}):
def update(self, input_stats=None):
"""Update the stats."""
input_stats = input_stats or {}
# Force update of all the stats
GlancesStats.update(self)
@ -383,7 +387,8 @@ class GlancesStatsClientSNMP(GlancesStats):
# For each plugins, call the update method
for p in self._plugins:
# Set the input method to SNMP
self._plugins[p].set_input('snmp', self.system_name)
self._plugins[p].input_method = 'snmp'
self._plugins[p].short_system_name = self.system_name
try:
self._plugins[p].update()
except Exception as e:

View File

@ -58,8 +58,10 @@ class GlancesExport(object):
def update(self, stats):
"""Update stats to a server.
The method buil two list: names and values
and call the export method to export the stats"""
The method builds two lists: names and values
and calls the export method to export the stats.
"""
if not self.export_enable:
return False
@ -67,20 +69,17 @@ class GlancesExport(object):
all_stats = stats.getAll()
plugins = stats.getAllPlugins()
# Loop over available plugin
i = 0
for plugin in plugins:
# Loop over available plugins
for i, plugin in enumerate(plugins):
if plugin in self.plugins_to_export():
if type(all_stats[i]) is list:
for item in all_stats[i]:
export_names = map(
lambda x: item[item['key']] + '.' + x, item.keys())
export_values = item.values()
export_names = list(map(lambda x: item[item['key']] + '.' + x, item.keys()))
export_values = list(item.values())
self.export(plugin, export_names, export_values)
elif type(all_stats[i]) is dict:
export_names = all_stats[i].keys()
export_values = all_stats[i].values()
export_names = list(all_stats[i].keys())
export_values = list(all_stats[i].values())
self.export(plugin, export_names, export_values)
i += 1
return True

View File

@ -176,8 +176,7 @@ class GlancesHistory(object):
fig.set_size_inches(20, 10)
plt.legend(handles, labels, loc=1, prop={'size': 9})
plt.xlabel('Date')
plt.savefig(
os.path.join(self.output_folder, 'glances_%s.png' % (p)), dpi=72)
plt.savefig(os.path.join(self.output_folder, 'glances_%s.png' % p), dpi=72)
index_all += 1
plt.close()

View File

@ -20,14 +20,19 @@
"""InfluxDB interface class."""
# Import sys libs
from influxdb import InfluxDBClient, client
import sys
try:
from configparser import NoOptionError, NoSectionError
except ImportError: # Python 2
from ConfigParser import NoOptionError, NoSectionError
# Import Glances lib
from glances.core.glances_logging import logger
from ConfigParser import NoSectionError, NoOptionError
from glances.exports.glances_export import GlancesExport
from influxdb import InfluxDBClient, client
from influxdb.influxdb08 import InfluxDBClient as InfluxDBClient_Legacy
class Export(GlancesExport):
@ -38,11 +43,12 @@ class Export(GlancesExport):
GlancesExport.__init__(self, config=config, args=args)
# Load the InfluxDB configuration file
self.influxdb_host = None
self.influxdb_port = None
self.influxdb_user = None
self.influxdb_password = None
self.influxdb_db = None
self.host = None
self.port = None
self.user = None
self.password = None
self.db = None
self.prefix = None
self.export_enable = self.load_conf()
if not self.export_enable:
sys.exit(2)
@ -55,11 +61,11 @@ class Export(GlancesExport):
if self.config is None:
return False
try:
self.influxdb_host = self.config.get_raw_option(section, "host")
self.influxdb_port = self.config.get_raw_option(section, "port")
self.influxdb_user = self.config.get_raw_option(section, "user")
self.influxdb_password = self.config.get_raw_option(section, "password")
self.influxdb_db = self.config.get_raw_option(section, "db")
self.host = self.config.get_raw_option(section, "host")
self.port = self.config.get_raw_option(section, "port")
self.user = self.config.get_raw_option(section, "user")
self.password = self.config.get_raw_option(section, "password")
self.db = self.config.get_raw_option(section, "db")
except NoSectionError:
logger.critical("No InfluxDB configuration found")
return False
@ -68,39 +74,61 @@ class Export(GlancesExport):
return False
else:
logger.debug("Load InfluxDB from the Glances configuration file")
# Prefix is optional
try:
self.prefix = self.config.get_raw_option(section, "prefix")
except NoOptionError as e:
pass
return True
def init(self):
"""Init the connection to the InfluxDB server"""
if not self.export_enable:
return None
db = InfluxDBClient(self.influxdb_host,
self.influxdb_port,
self.influxdb_user,
self.influxdb_password,
self.influxdb_db)
try:
get_all_db = db.get_database_list()[0].values()
except client.InfluxDBClientError as e:
logger.critical("Can not connect to InfluxDB database '%s' (%s)" % (self.influxdb_db, e))
sys.exit(2)
if self.influxdb_db in get_all_db:
try:
db = InfluxDBClient(host=self.host,
port=self.port,
username=self.user,
password=self.password,
database=self.db)
get_all_db = [i['name'] for i in db.get_list_database()]
except client.InfluxDBClientError as e:
try:
# https://github.com/influxdb/influxdb-python/issues/138
logger.info("Trying fallback to InfluxDB v0.8")
db = InfluxDBClient_Legacy(host=self.host,
port=self.port,
username=self.user,
password=self.password,
database=self.db)
get_all_db = [i['name'] for i in db.get_list_database()]
except:
logger.critical("Can not connect to InfluxDB database '%s' (%s)" % (self.db, e))
sys.exit(2)
if self.db in get_all_db:
logger.info(
"Stats will be exported to InfluxDB server: {0}".format(db._baseurl))
else:
logger.critical("InfluxDB database '%s' did not exist. Please create it" % self.influxdb_db)
logger.critical("InfluxDB database '%s' did not exist. Please create it" % self.db)
sys.exit(2)
return db
def export(self, name, columns, points):
"""Write the points to the InfluxDB server"""
# Manage prefix
if self.prefix is not None:
name = self.prefix + '.' + name
# logger.info(self.prefix)
# Create DB input
data = [
{
"name": name,
"columns": columns,
"points": [points]
}]
# Write input to the InfluxDB database
try:
self.client.write_points(data)
except Exception as e:

View File

@ -20,15 +20,19 @@
"""Statsd interface class."""
# Import sys libs
from statsd import StatsClient
from numbers import Number
import sys
from numbers import Number
try:
from configparser import NoOptionError, NoSectionError
except ImportError: # Python 2
from ConfigParser import NoOptionError, NoSectionError
# Import Glances lib
from glances.core.glances_logging import logger
from ConfigParser import NoSectionError, NoOptionError
from glances.exports.glances_export import GlancesExport
from statsd import StatsClient
class Export(GlancesExport):

View File

@ -33,7 +33,7 @@ class Bar(object):
import time
b = Bar(10)
for p in range(0, 100):
b.set_percent(p)
b.percent = p
print("\r%s" % b),
time.sleep(0.1)
sys.stdout.flush()
@ -43,7 +43,8 @@ class Bar(object):
def __init__(self, size,
pre_char='[',
post_char=']',
empty_char='_'):
empty_char='_',
with_text=True):
# Bar size
self.__size = size
# Bar current percent
@ -52,29 +53,38 @@ class Bar(object):
self.__pre_char = pre_char
self.__post_char = post_char
self.__empty_char = empty_char
self.__with_text = with_text
def get_size(self):
return self.__size
@property
def size(self, with_decoration=False):
# Return the bar size, with or without decoration
if with_decoration:
return self.__size
if self.__with_text:
return self.__size - 6
def set_size(self, size):
self.__size = size
return self.__size
# @size.setter
# def size(self, value):
# self.__size = value
def get_percent(self):
@property
def percent(self):
return self.__percent
def set_percent(self, percent):
assert percent >= 0
assert percent <= 100
self.__percent = percent
return self.__percent
@percent.setter
def percent(self, value):
assert value >= 0
assert value <= 100
self.__percent = value
def __str__(self):
"""Return the bars"""
frac, whole = modf(self.get_size() * self.get_percent() / 100.0)
"""Return the bars."""
frac, whole = modf(self.size * self.percent / 100.0)
ret = curses_bars[8] * int(whole)
if frac > 0:
ret += curses_bars[int(frac * 8)]
whole += 1
ret += self.__empty_char * int(self.get_size() - whole)
ret += self.__empty_char * int(self.size - whole)
if self.__with_text:
ret = '{0}{1:>5}%'.format(ret, self.percent)
return self.__pre_char + ret + self.__post_char

View File

@ -279,7 +279,7 @@ class GlancesBottle(object):
try:
# Get the JSON value of the stat limits
ret = self.stats.get_plugin(plugin).get_limits()
ret = self.stats.get_plugin(plugin).limits
except Exception as e:
abort(404, "Cannot get limits for plugin %s (%s)" % (plugin, str(e)))
return ret

View File

@ -80,10 +80,10 @@ class Screen(object):
def subwin(self, x, y):
return self
def keypad(self, id):
def keypad(self, screen_id):
return None
def nodelay(self, id):
def nodelay(self, screen_id):
return None
def getch(self):
@ -170,8 +170,8 @@ class WCurseLight(object):
def napms(self, t):
time.sleep(t / 1000 if t > 1000 else 1)
def init_pair(self, id, fg, bk):
self.colors[id] = [max(fg, 0), max(bk, 0)]
def init_pair(self, color_id, fg, bk):
self.colors[color_id] = [max(fg, 0), max(bk, 0)]
def color_pair(self, id):
return id
def color_pair(self, color_id):
return color_id

View File

@ -177,9 +177,6 @@ class _GlancesCurses(object):
# Init refresh time
self.__refresh_time = args.time
# Init process sort method
self.args.process_sorted_by = 'auto'
# Init edit filter tag
self.edit_filter = False
@ -257,17 +254,17 @@ class _GlancesCurses(object):
# '/' > Switch between short/long name for processes
self.args.process_short_name = not self.args.process_short_name
elif self.pressedkey == ord('a'):
# 'a' > Sort processes automatically
self.args.process_sorted_by = 'auto'
glances_processes.resetsort()
# 'a' > Sort processes automatically and reset to 'cpu_percent'
glances_processes.auto_sort = True
glances_processes.sort_key = 'cpu_percent'
elif self.pressedkey == ord('b'):
# 'b' > Switch between bit/s and Byte/s for network IO
# self.net_byteps_tag = not self.net_byteps_tag
self.args.byte = not self.args.byte
elif self.pressedkey == ord('c'):
# 'c' > Sort processes by CPU usage
self.args.process_sorted_by = 'cpu_percent'
glances_processes.setmanualsortkey(self.args.process_sorted_by)
glances_processes.auto_sort = False
glances_processes.sort_key = 'cpu_percent'
elif self.pressedkey == ord('d'):
# 'd' > Show/hide disk I/O stats
self.args.disable_diskio = not self.args.disable_diskio
@ -295,8 +292,8 @@ class _GlancesCurses(object):
self.args.help_tag = not self.args.help_tag
elif self.pressedkey == ord('i'):
# 'i' > Sort processes by IO rate (not available on OS X)
self.args.process_sorted_by = 'io_counters'
glances_processes.setmanualsortkey(self.args.process_sorted_by)
glances_processes.auto_sort = False
glances_processes.sort_key = 'io_counters'
elif self.pressedkey == ord('I'):
# 'I' > Show/hide IP module
self.args.disable_ip = not self.args.disable_ip
@ -305,15 +302,15 @@ class _GlancesCurses(object):
self.args.disable_log = not self.args.disable_log
elif self.pressedkey == ord('m'):
# 'm' > Sort processes by MEM usage
self.args.process_sorted_by = 'memory_percent'
glances_processes.setmanualsortkey(self.args.process_sorted_by)
glances_processes.auto_sort = False
glances_processes.sort_key = 'memory_percent'
elif self.pressedkey == ord('n'):
# 'n' > Show/hide network stats
self.args.disable_network = not self.args.disable_network
elif self.pressedkey == ord('p'):
# 'p' > Sort processes by name
self.args.process_sorted_by = 'name'
glances_processes.setmanualsortkey(self.args.process_sorted_by)
glances_processes.auto_sort = False
glances_processes.sort_key = 'name'
elif self.pressedkey == ord('r'):
# 'r' > Reset history
self.reset_history_tag = not self.reset_history_tag
@ -325,8 +322,8 @@ class _GlancesCurses(object):
self.args.disable_sensors = not self.args.disable_sensors
elif self.pressedkey == ord('t'):
# 't' > Sort processes by TIME usage
self.args.process_sorted_by = 'cpu_times'
glances_processes.setmanualsortkey(self.args.process_sorted_by)
glances_processes.auto_sort = False
glances_processes.sort_key = 'cpu_times'
elif self.pressedkey == ord('T'):
# 'T' > View network traffic as sum Rx+Tx
self.args.network_sum = not self.args.network_sum
@ -389,7 +386,7 @@ class _GlancesCurses(object):
"""New column in the curses interface"""
self.column = self.next_column
def display(self, stats, cs_status="None"):
def display(self, stats, cs_status=None):
"""Display stats on the screen.
stats: Stats database to display
@ -464,11 +461,10 @@ class _GlancesCurses(object):
max_processes_displayed -= 4
if max_processes_displayed < 0:
max_processes_displayed = 0
if glances_processes.get_max_processes() is None or \
glances_processes.get_max_processes() != max_processes_displayed:
logger.debug("Set number of displayed processes to %s" %
max_processes_displayed)
glances_processes.set_max_processes(max_processes_displayed)
if (glances_processes.max_processes is None or
glances_processes.max_processes != max_processes_displayed):
logger.debug("Set number of displayed processes to {0}".format(max_processes_displayed))
glances_processes.max_processes = max_processes_displayed
stats_processlist = stats.get_plugin(
'processlist').get_stats_display(args=self.args)
@ -600,7 +596,7 @@ class _GlancesCurses(object):
self.display_plugin(stats_docker)
self.new_line()
self.display_plugin(stats_processcount)
if glances_processes.get_process_filter() is None and cs_status == 'None':
if glances_processes.process_filter is None and cs_status is None:
# Do not display stats monitor list if a filter exist
self.new_line()
self.display_plugin(stats_monitor)
@ -635,12 +631,12 @@ class _GlancesCurses(object):
self.reset_history_tag = False
# Display edit filter popup
# Only in standalone mode (cs_status == 'None')
if self.edit_filter and cs_status == 'None':
# Only in standalone mode (cs_status is None)
if self.edit_filter and cs_status is None:
new_filter = self.display_popup(_("Process filter pattern: "),
is_input=True,
input_value=glances_processes.get_process_filter())
glances_processes.set_process_filter(new_filter)
input_value=glances_processes.process_filter)
glances_processes.process_filter = new_filter
elif self.edit_filter and cs_status != 'None':
self.display_popup(
_("Process filter only available in standalone mode"))
@ -765,7 +761,7 @@ class _GlancesCurses(object):
# New line
if m['msg'].startswith('\n'):
# Go to the next line
y = y + 1
y += 1
# Return to the first column
x = display_x
continue
@ -802,7 +798,7 @@ class _GlancesCurses(object):
# Python 3: strings are strings and bytes are bytes, all is
# good
offset = len(m['msg'])
x = x + offset
x += offset
if x > x_max:
x_max = x
@ -815,7 +811,7 @@ class _GlancesCurses(object):
"""Erase the content of the screen."""
self.term_window.erase()
def flush(self, stats, cs_status="None"):
def flush(self, stats, cs_status=None):
"""Clear and update the screen.
stats: Stats database to display
@ -827,7 +823,7 @@ class _GlancesCurses(object):
self.erase()
self.display(stats, cs_status=cs_status)
def update(self, stats, cs_status="None", return_to_browser=False):
def update(self, stats, cs_status=None, return_to_browser=False):
"""Update the screen.
Wait for __refresh_time sec / catch key every 100 ms.
@ -936,32 +932,30 @@ class GlancesCursesBrowser(_GlancesCurses):
self.__refresh_time = args.time
# Init the cursor position for the client browser
self.cursor_init()
self.cursor_position = 0
# Active Glances server number
self.set_active()
self._active_server = None
def set_active(self, index=None):
"""Set the active server or None if no server selected"""
self.active_server = index
return self.active_server
@property
def active_server(self):
"""Return the active server or None if it's the browser list."""
return self._active_server
def get_active(self):
"""Return the active server (the one display in front) or None if it is the browser list"""
return self.active_server
@active_server.setter
def active_server(self, index):
"""Set the active server or None if no server selected."""
self._active_server = index
def cursor_init(self):
"""Init the cursor position to the top of the list"""
return self.cursor_set(0)
def cursor_set(self, pos):
"""Set the cursor position and return it"""
self.cursor_position = pos
@property
def cursor(self):
"""Get the cursor position."""
return self.cursor_position
def cursor_get(self):
"""Return the cursor position"""
return self.cursor_position
@cursor.setter
def cursor(self, position):
"""Set the cursor position."""
self.cursor_position = position
def cursor_up(self, servers_list):
"""Set the cursor to position N-1 in the list"""
@ -969,7 +963,6 @@ class GlancesCursesBrowser(_GlancesCurses):
self.cursor_position -= 1
else:
self.cursor_position = len(servers_list) - 1
return self.cursor_position
def cursor_down(self, servers_list):
"""Set the cursor to position N-1 in the list"""
@ -977,7 +970,6 @@ class GlancesCursesBrowser(_GlancesCurses):
self.cursor_position += 1
else:
self.cursor_position = 0
return self.cursor_position
def __catch_key(self, servers_list):
# Catch the browser pressed key
@ -991,11 +983,10 @@ class GlancesCursesBrowser(_GlancesCurses):
sys.exit(0)
elif self.pressedkey == 10:
# 'ENTER' > Run Glances on the selected server
logger.debug("Server number %s selected" % (self.cursor_get() + 1))
self.set_active(self.cursor_get())
logger.debug("Server number {0} selected".format(self.cursor + 1))
self.active_server = self.cursor
elif self.pressedkey == 259:
# 'UP' > Up in the server list
logger
self.cursor_up(servers_list)
elif self.pressedkey == 258:
# 'DOWN' > Down in the server list
@ -1029,7 +1020,7 @@ class GlancesCursesBrowser(_GlancesCurses):
# Wait 100ms...
curses.napms(100)
return self.get_active()
return self.active_server
def flush(self, servers_list):
"""Update the servers' list screen.
@ -1110,9 +1101,9 @@ class GlancesCursesBrowser(_GlancesCurses):
# If a servers has been deleted from the list...
# ... and if the cursor is in the latest position
if self.cursor_get() > len(servers_list) - 1:
if self.cursor > len(servers_list) - 1:
# Set the cursor position to the latest item
self.cursor_set(len(servers_list) - 1)
self.cursor = len(servers_list) - 1
# Display table
line = 0
@ -1138,25 +1129,18 @@ class GlancesCursesBrowser(_GlancesCurses):
xc = x
# Is the line selected ?
if line == self.cursor_get():
if line == self.cursor:
# Display cursor
self.term_window.addnstr(y, xc,
">",
screen_x - xc,
self.colors_list['BOLD'])
# Display alias instead of name
server_stat
self.term_window.addnstr(
y, xc, ">", screen_x - xc, self.colors_list['BOLD'])
# Display the line
xc += 2
for c in column_def:
if xc < screen_x and y < screen_y and c[1] is not None:
# Display server stats
self.term_window.addnstr(y, xc,
"%s" % server_stat[c[0]],
c[2],
self.colors_list[v['status']])
self.term_window.addnstr(
y, xc, format(server_stat[c[0]]), c[2], self.colors_list[v['status']])
xc += c[2] + self.space_between_column
cpt += 1
# Next line, next server...

View File

@ -42,7 +42,7 @@ class Plugin(GlancesPlugin):
self.display_curse = True
# Set the message position
self.set_align('bottom')
self.align = 'bottom'
# Init the stats
self.reset()

View File

@ -28,7 +28,6 @@ try:
import batinfo
except ImportError:
logger.debug("Batinfo library not found. Glances cannot grab battery info.")
pass
class Plugin(GlancesPlugin):
@ -62,12 +61,12 @@ class Plugin(GlancesPlugin):
# Reset stats
self.reset()
if self.get_input() == 'local':
if self.input_method == 'local':
# Update stats
self.glancesgrabbat.update()
self.stats = self.glancesgrabbat.get()
elif self.get_input() == 'snmp':
elif self.input_method == 'snmp':
# Update stats using SNMP
# Not avalaible
pass
@ -94,7 +93,7 @@ class GlancesGrabBat(object):
"""Update the stats."""
if self.initok:
self.bat.update()
self.bat_list = [{'label': _("Battery"), 'value': self.getcapacitypercent(), 'unit': '%'}]
self.bat_list = [{'label': _("Battery"), 'value': self.battery_percent, 'unit': '%'}]
else:
self.bat_list = []
@ -102,7 +101,8 @@ class GlancesGrabBat(object):
"""Get the stats."""
return self.bat_list
def getcapacitypercent(self):
@property
def battery_percent(self):
"""Get batteries capacity percent."""
if not self.initok or not self.bat.stat:
return []
@ -112,7 +112,7 @@ class GlancesGrabBat(object):
bsum = 0
for b in self.bat.stat:
try:
bsum = bsum + int(b.capacity)
bsum += int(b.capacity)
except ValueError:
return []

View File

@ -56,7 +56,7 @@ class Plugin(GlancesPlugin):
# Reset the stats
self.reset()
if self.get_input() == 'local':
if self.input_method == 'local':
# Update stats using the standard system lib
# The PSUtil 2.0 include psutil.cpu_count() and psutil.cpu_count(logical=False)
@ -70,7 +70,7 @@ class Plugin(GlancesPlugin):
except NameError:
self.reset()
elif self.get_input() == 'snmp':
elif self.input_method == 'snmp':
# Update stats using SNMP
# http://stackoverflow.com/questions/5662467/how-to-find-out-the-number-of-cpus-using-snmp
pass

View File

@ -78,7 +78,7 @@ class Plugin(GlancesPlugin):
# Grab CPU stats using psutil's cpu_percent and cpu_times_percent
# methods
if self.get_input() == 'local':
if self.input_method == 'local':
# Get all possible values for CPU stats: user, system, idle,
# nice (UNIX), iowait (Linux), irq (Linux, FreeBSD), steal (Linux 2.6.11+)
# The following stats are returned by the API but not displayed in the UI:
@ -89,14 +89,14 @@ class Plugin(GlancesPlugin):
'irq', 'softirq', 'steal', 'guest', 'guest_nice']:
if hasattr(cpu_times_percent, stat):
self.stats[stat] = getattr(cpu_times_percent, stat)
elif self.get_input() == 'snmp':
elif self.input_method == 'snmp':
# Update stats using SNMP
if self.get_short_system_name() in ('windows', 'esxi'):
if self.short_system_name in ('windows', 'esxi'):
# Windows or VMWare ESXi
# You can find the CPU utilization of windows system by querying the oid
# Give also the number of core (number of element in the table)
try:
cpu_stats = self.set_stats_snmp(snmp_oid=snmp_oid[self.get_short_system_name()],
cpu_stats = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name],
bulk=True)
except KeyError:
self.reset()
@ -117,10 +117,10 @@ class Plugin(GlancesPlugin):
else:
# Default behavor
try:
self.stats = self.set_stats_snmp(
snmp_oid=snmp_oid[self.get_short_system_name()])
self.stats = self.get_stats_snmp(
snmp_oid=snmp_oid[self.short_system_name])
except KeyError:
self.stats = self.set_stats_snmp(
self.stats = self.get_stats_snmp(
snmp_oid=snmp_oid['default'])
if self.stats['idle'] == '':
@ -150,9 +150,8 @@ class Plugin(GlancesPlugin):
for key in ['user', 'system', 'iowait']:
if key in self.stats:
self.views[key]['decoration'] = self.get_alert_log(self.stats[key], header=key)
self.views['total']['decoration'] = self.get_alert_log(self.stats['total'], header="system")
# Alert only
for key in ['steal']:
for key in ['steal', 'total']:
if key in self.stats:
self.views[key]['decoration'] = self.get_alert(self.stats[key], header=key)
# Optional
@ -166,7 +165,7 @@ class Plugin(GlancesPlugin):
ret = []
# Only process if stats exist...
if self.stats == {}:
if not self.stats:
return ret
# Build the string message

View File

@ -66,7 +66,7 @@ class Plugin(GlancesPlugin):
# Reset stats
self.reset()
if self.get_input() == 'local':
if self.input_method == 'local':
# Update stats using the standard system lib
# Grab the stat using the PsUtil disk_io_counters method
# read_count: number of reads
@ -96,16 +96,15 @@ class Plugin(GlancesPlugin):
diskio_new = diskiocounters
for disk in diskio_new:
try:
# Try necessary to manage dynamic disk creation/del
diskstat = {}
diskstat['time_since_update'] = time_since_update
diskstat['disk_name'] = disk
diskstat['read_bytes'] = (
diskio_new[disk].read_bytes -
self.diskio_old[disk].read_bytes)
diskstat['write_bytes'] = (
diskio_new[disk].write_bytes -
self.diskio_old[disk].write_bytes)
read_bytes = (diskio_new[disk].read_bytes -
self.diskio_old[disk].read_bytes)
write_bytes = (diskio_new[disk].write_bytes -
self.diskio_old[disk].write_bytes)
diskstat = {
'time_since_update': time_since_update,
'disk_name': disk,
'read_bytes': read_bytes,
'write_bytes': write_bytes}
except KeyError:
continue
else:
@ -114,7 +113,7 @@ class Plugin(GlancesPlugin):
# Save stats to compute next bitrate
self.diskio_old = diskio_new
elif self.get_input() == 'snmp':
elif self.input_method == 'snmp':
# Update stats using SNMP
# No standard way for the moment...
pass

View File

@ -19,7 +19,12 @@
"""Docker plugin."""
import numbers
import os
import re
# Import Glances libs
from glances.core.glances_timer import getTimeSinceLastUpdate
from glances.core.glances_logging import logger
from glances.plugins.glances_plugin import GlancesPlugin
@ -33,9 +38,6 @@ except ImportError as e:
docker_tag = False
else:
docker_tag = True
import os
import re
import numbers
class Plugin(GlancesPlugin):
@ -82,7 +84,6 @@ class Plugin(GlancesPlugin):
# API error (Version mismatch ?)
logger.debug("Docker API error (%s)" % e)
# Try the connection with the server version
import re
version = re.search('server\:\ (.*)\)\".*\)', str(e))
if version:
logger.debug("Try connection with Docker API version %s" % version.group(1))
@ -129,7 +130,7 @@ class Plugin(GlancesPlugin):
if not docker_tag or (self.args is not None and self.args.disable_docker):
return self.stats
if self.get_input() == 'local':
if self.input_method == 'local':
# Update stats
# Exemple: {
# "KernelVersion": "3.16.4-tinycore64",
@ -150,12 +151,31 @@ class Plugin(GlancesPlugin):
# u'Names': [u'/webstack_nginx_1'],
# u'Id': u'b0da859e84eb4019cf1d965b15e9323006e510352c402d2f442ea632d61faaa5'}]
self.stats['containers'] = self.docker_client.containers()
# Get CPU and MEMORY stats for containers
# Get stats for all containers
for c in self.stats['containers']:
c['cpu'] = self.get_docker_cpu(c['Id'])
c['memory'] = self.get_docker_memory(c['Id'])
if not hasattr(self, 'docker_stats'):
# Create a dict with all the containers' stats instance
self.docker_stats = {}
elif self.get_input() == 'snmp':
if c['Id'] not in self.docker_stats:
# Create the stats instance for the current container
try:
self.docker_stats[c['Id']] = self.docker_client.stats(c['Id'], decode=True)
logger.debug("Create Docker stats object for container {}".format(c['Id']))
except (AttributeError, docker.errors.InvalidVersion) as e:
logger.error("Can not call Docker stats method {}".format(e))
# Get the docker stats
try:
all_stats = self.docker_stats[c['Id']].next()
except:
all_stats = {}
c['cpu'] = self.get_docker_cpu(c['Id'], all_stats)
c['memory'] = self.get_docker_memory(c['Id'], all_stats)
# c['network'] = self.get_docker_network(c['Id'], all_stats)
elif self.input_method == 'snmp':
# Update stats using SNMP
# Not available
pass
@ -164,14 +184,14 @@ class Plugin(GlancesPlugin):
return self.stats
def get_docker_cpu(self, id):
def get_docker_cpu_old(self, container_id):
"""Return the container CPU usage by reading /sys/fs/cgroup/...
Input: id is the full container id
Output: a dict {'total': 1.49, 'user': 0.65, 'system': 0.84}"""
ret = {}
# Read the stats
try:
with open('/sys/fs/cgroup/cpuacct/docker/' + id + '/cpuacct.stat', 'r') as f:
with open('/sys/fs/cgroup/cpuacct/docker/' + container_id + '/cpuacct.stat', 'r') as f:
for line in f:
m = re.search(r"(system|user)\s+(\d+)", line)
if m:
@ -179,23 +199,45 @@ class Plugin(GlancesPlugin):
except IOError as e:
logger.error("Can not grab container CPU stat ({0})".format(e))
return ret
# Get the user ticks
ticks = self.get_user_ticks()
if isinstance(ret["system"], numbers.Number) and isinstance(ret["user"], numbers.Number):
ret["total"] = ret["system"] + ret["user"]
for k in ret.keys():
ret[k] = float(ret[k]) / ticks
# Return the stats
return ret
def get_docker_memory(self, id):
def get_docker_cpu(self, container_id, all_stats):
"""Return the container CPU usage
Input: id is the full container id
all_stats is the output of the stats method of the Docker API
Output: a dict {'total': 1.49}"""
ret = {}
# Read the stats
# try:
# ret['total'] = all_stats['cpu_stats']['cpu_usage']['total_usage']
# except KeyError as e:
# # all_stats do not have CPU information
# logger.error("Can not grab CPU usage for container {0} ({1})".format(container_id, e))
# # Trying fallback to old grab method
# ret = self.get_docker_cpu_old(container_id)
# Did not work has expected, replace by the old method...
ret = self.get_docker_cpu_old(container_id)
# Get the user ticks
ticks = self.get_user_ticks()
for k in ret.keys():
ret[k] = float(ret[k]) / ticks
# Return the stats
return ret
def get_docker_memory_old(self, container_id):
"""Return the container MEMORY usage by reading /sys/fs/cgroup/...
Input: id is the full container id
Output: a dict {'rss': 1015808, 'cache': 356352}"""
ret = {}
# Read the stats
try:
with open('/sys/fs/cgroup/memory/docker/' + id + '/memory.stat', 'r') as f:
with open('/sys/fs/cgroup/memory/docker/' + container_id + '/memory.stat', 'r') as f:
for line in f:
m = re.search(r"(rss|cache)\s+(\d+)", line)
if m:
@ -206,6 +248,73 @@ class Plugin(GlancesPlugin):
# Return the stats
return ret
def get_docker_memory(self, container_id, all_stats):
"""Return the container MEMORY
Input: id is the full container id
all_stats is the output of the stats method of the Docker API
Output: a dict {'rss': 1015808, 'cache': 356352, 'usage': ..., 'max_usage': ...}"""
ret = {}
# Read the stats
try:
ret['rss'] = all_stats['memory_stats']['stats']['rss']
ret['cache'] = all_stats['memory_stats']['stats']['cache']
ret['usage'] = all_stats['memory_stats']['usage']
ret['max_usage'] = all_stats['memory_stats']['max_usage']
except KeyError as e:
# all_stats do not have MEM information
logger.error("Can not grab MEM usage for container {0} ({1})".format(container_id, e))
# Trying fallback to old grab method
ret = self.get_docker_memory_old(container_id)
# Return the stats
return ret
def get_docker_network(self, container_id, all_stats):
"""Return the container network usage using the Docker API (v1.0 or higher)
Input: id is the full container id
Output: a dict {'time_since_update': 3000, 'rx': 10, 'tx': 65}"""
# Init the returned dict
network_new = {}
# Read the rx/tx stats (in bytes)
try:
netiocounters = all_stats["network"]
except KeyError as e:
# all_stats do not have NETWORK information
logger.debug("Can not grab NET usage for container {0} ({1})".format(container_id, e))
# No fallback available...
return network_new
# Previous network interface stats are stored in the network_old variable
if not hasattr(self, 'netiocounters_old'):
# First call, we init the network_old var
self.netiocounters_old = {}
try:
self.netiocounters_old[container_id] = netiocounters
except (IOError, UnboundLocalError):
pass
if container_id not in self.netiocounters_old:
try:
self.netiocounters_old[container_id] = netiocounters
except (IOError, UnboundLocalError):
pass
else:
# By storing time data we enable Rx/s and Tx/s calculations in the
# XML/RPC API, which would otherwise be overly difficult work
# for users of the API
network_new['time_since_update'] = getTimeSinceLastUpdate('docker_net')
network_new['rx'] = netiocounters["rx_bytes"] - self.netiocounters_old[container_id]["rx_bytes"]
network_new['tx'] = netiocounters["tx_bytes"] - self.netiocounters_old[container_id]["tx_bytes"]
network_new['cumulative_rx'] = netiocounters["rx_bytes"]
network_new['cumulative_tx'] = netiocounters["tx_bytes"]
# Save stats to compute next bitrate
self.netiocounters_old[container_id] = netiocounters
# Return the stats
return network_new
def get_user_ticks(self):
"""return the user ticks by reading the environment variable"""
return os.sysconf(os.sysconf_names['SC_CLK_TCK'])
@ -216,7 +325,7 @@ class Plugin(GlancesPlugin):
ret = []
# Only process if stats exist (and non null) and display plugin enable...
if self.stats == {} or args.disable_docker or len(self.stats['containers']) == 0:
if not self.stats or args.disable_docker or len(self.stats['containers']) == 0:
return ret
# Build the string message
@ -239,8 +348,12 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_add_line(msg))
msg = '{0:>6}'.format(_("CPU%"))
ret.append(self.curse_add_line(msg))
msg = '{0:>6}'.format(_("MEM"))
msg = '{0:>7}'.format(_("MEM"))
ret.append(self.curse_add_line(msg))
# msg = '{0:>6}'.format(_("Rx/s"))
# ret.append(self.curse_add_line(msg))
# msg = '{0:>6}'.format(_("Tx/s"))
# ret.append(self.curse_add_line(msg))
msg = ' {0:8}'.format(_("Command"))
ret.append(self.curse_add_line(msg))
# Data
@ -254,7 +367,7 @@ class Plugin(GlancesPlugin):
if len(name) > 20:
name = '_' + name[:-19]
else:
name[0:20]
name = name[:20]
msg = ' {0:20}'.format(name)
ret.append(self.curse_add_line(msg))
# Status
@ -270,10 +383,18 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_add_line(msg))
# MEM
try:
msg = '{0:>6}'.format(self.auto_unit(container['memory']['rss']))
msg = '{0:>7}'.format(self.auto_unit(container['memory']['usage']))
except KeyError:
msg = '{0:>6}'.format('?')
msg = '{0:>7}'.format('?')
ret.append(self.curse_add_line(msg))
# NET RX/TX
# for r in ['rx', 'tx']:
# try:
# value = self.auto_unit(int(container['network'][r] // container['network']['time_since_update'] * 8)) + "b"
# msg = '{0:>6}'.format(value)
# except KeyError:
# msg = '{0:>6}'.format('?')
# ret.append(self.curse_add_line(msg))
# Command
msg = ' {0}'.format(container['Command'])
ret.append(self.curse_add_line(msg))

View File

@ -24,7 +24,6 @@ import operator
import psutil
from glances.plugins.glances_plugin import GlancesPlugin
from glances.core.glances_logging import logger
# SNMP OID
# The snmpd.conf needs to be edited.
@ -94,7 +93,7 @@ class Plugin(GlancesPlugin):
# Reset the list
self.reset()
if self.get_input() == 'local':
if self.input_method == 'local':
# Update stats using the standard system lib
# Grab the stats using the PsUtil disk_partitions
@ -116,10 +115,6 @@ class Plugin(GlancesPlugin):
# Loop over fs
for fs in fs_stat:
fs_current = {}
fs_current['device_name'] = fs.device
fs_current['fs_type'] = fs.fstype
fs_current['mnt_point'] = fs.mountpoint
# Grab the disk usage
try:
fs_usage = psutil.disk_usage(fs.mountpoint)
@ -127,52 +122,56 @@ class Plugin(GlancesPlugin):
# Correct issue #346
# Disk is ejected during the command
continue
fs_current['size'] = fs_usage.total
fs_current['used'] = fs_usage.used
fs_current['free'] = fs_usage.total - fs_usage.used
fs_current['percent'] = fs_usage.percent
fs_current['key'] = self.get_key()
fs_current = {
'device_name': fs.device,
'fs_type': fs.fstype,
'mnt_point': fs.mountpoint,
'size': fs_usage.total,
'used': fs_usage.used,
'free': fs_usage.total - fs_usage.used,
'percent': fs_usage.percent,
'key': self.get_key()}
self.stats.append(fs_current)
elif self.get_input() == 'snmp':
elif self.input_method == 'snmp':
# Update stats using SNMP
# SNMP bulk command to get all file system in one shot
try:
fs_stat = self.set_stats_snmp(snmp_oid=snmp_oid[self.get_short_system_name()],
fs_stat = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name],
bulk=True)
except KeyError:
fs_stat = self.set_stats_snmp(snmp_oid=snmp_oid['default'],
fs_stat = self.get_stats_snmp(snmp_oid=snmp_oid['default'],
bulk=True)
# Loop over fs
if self.get_short_system_name() in ('windows', 'esxi'):
if self.short_system_name in ('windows', 'esxi'):
# Windows or ESXi tips
for fs in fs_stat:
# Memory stats are grabed in the same OID table (ignore it)
# Memory stats are grabbed in the same OID table (ignore it)
if fs == 'Virtual Memory' or fs == 'Physical Memory' or fs == 'Real Memory':
continue
fs_current = {}
fs_current['device_name'] = ''
fs_current['mnt_point'] = fs.partition(' ')[0]
fs_current['size'] = int(
fs_stat[fs]['size']) * int(fs_stat[fs]['alloc_unit'])
fs_current['used'] = int(
fs_stat[fs]['used']) * int(fs_stat[fs]['alloc_unit'])
fs_current['percent'] = float(
fs_current['used'] * 100 / fs_current['size'])
fs_current['key'] = self.get_key()
size = int(fs_stat[fs]['size']) * int(fs_stat[fs]['alloc_unit'])
used = int(fs_stat[fs]['used']) * int(fs_stat[fs]['alloc_unit'])
percent = float(used * 100 / size)
fs_current = {
'device_name': '',
'mnt_point': fs.partition(' ')[0],
'size': size,
'used': used,
'percent': percent,
'key': self.get_key()}
self.stats.append(fs_current)
else:
# Default behavor
# Default behavior
for fs in fs_stat:
fs_current = {}
fs_current['device_name'] = fs_stat[fs]['device_name']
fs_current['mnt_point'] = fs
fs_current['size'] = int(fs_stat[fs]['size']) * 1024
fs_current['used'] = int(fs_stat[fs]['used']) * 1024
fs_current['percent'] = float(fs_stat[fs]['percent'])
fs_current['key'] = self.get_key()
fs_current = {
'device_name': fs_stat[fs]['device_name'],
'mnt_point': fs,
'size': int(fs_stat[fs]['size']) * 1024,
'used': int(fs_stat[fs]['used']) * 1024,
'percent': float(fs_stat[fs]['percent']),
'key': self.get_key()}
self.stats.append(fs_current)
# Update the history list
@ -192,7 +191,7 @@ class Plugin(GlancesPlugin):
# Alert
for i in self.stats:
self.views[i[self.get_key()]]['used']['decoration'] = self.get_alert(
i['used'], max=i['size'], header=i['mnt_point'])
i['used'], maximum=i['size'], header=i['mnt_point'])
def msg_curse(self, args=None, max_width=None):
"""Return the dict to display in the curse interface."""

View File

@ -25,6 +25,7 @@ import socket
# Import Glances libs
from glances.plugins.glances_plugin import GlancesPlugin
from glances.core.glances_logging import logger
class Plugin(GlancesPlugin):
@ -39,7 +40,7 @@ class Plugin(GlancesPlugin):
GlancesPlugin.__init__(self, args=args)
# Init the sensor class
self.glancesgrabhddtemp = GlancesGrabHDDTemp()
self.glancesgrabhddtemp = GlancesGrabHDDTemp(args=args)
# We do not want to display the stat in a dedicated area
# The HDD temp is displayed within the sensors plugin
@ -57,7 +58,7 @@ class Plugin(GlancesPlugin):
# Reset stats
self.reset()
if self.get_input() == 'local':
if self.input_method == 'local':
# Update stats using the standard system lib
self.stats = self.glancesgrabhddtemp.get()
@ -73,8 +74,9 @@ class GlancesGrabHDDTemp(object):
"""Get hddtemp stats using a socket connection."""
def __init__(self, host='127.0.0.1', port=7634):
def __init__(self, host='127.0.0.1', port=7634, args=None):
"""Init hddtemp stats."""
self.args = args
self.host = host
self.port = port
self.cache = ""
@ -89,6 +91,10 @@ class GlancesGrabHDDTemp(object):
# Reset the list
self.reset()
# Only update if --disable-hddtemp is not set
if self.args is None or self.args.disable_hddtemp:
return
# Fetch the data
data = self.fetch()
@ -125,7 +131,10 @@ class GlancesGrabHDDTemp(object):
sck.connect((self.host, self.port))
data = sck.recv(4096)
sck.close()
except socket.error:
except socket.error as e:
logger.warning("Can not connect to an HDDtemp server ({0}:{1} => {2})".format(self.host, self.port, e))
logger.debug("Disable the HDDtemp module. Use the --disable-hddtemp to hide the previous message.")
self.args.disable_hddtemp = True
data = ""
return data

View File

@ -61,7 +61,7 @@ class Plugin(GlancesPlugin):
# Reset stats
self.reset()
if self.get_input() == 'local' and netifaces_tag:
if self.input_method == 'local' and netifaces_tag:
# Update stats using the netifaces lib
try:
default_gw = netifaces.gateways()['default'][netifaces.AF_INET]
@ -76,7 +76,7 @@ class Plugin(GlancesPlugin):
except KeyError as e:
logger.debug("Can not grab IP information (%s)".format(e))
elif self.get_input() == 'snmp':
elif self.input_method == 'snmp':
# Not implemented yet
pass
@ -106,9 +106,11 @@ class Plugin(GlancesPlugin):
return ret
# Build the string message
msg = _(' - IP')
msg = _(' - ')
ret.append(self.curse_add_line(msg))
msg = _('IP ')
ret.append(self.curse_add_line(msg, 'TITLE'))
msg = ' {0:}/{1}'.format(self.stats['address'], self.stats['mask_cidr'])
msg = '{0:}/{1}'.format(self.stats['address'], self.stats['mask_cidr'])
ret.append(self.curse_add_line(msg))
return ret
@ -117,4 +119,4 @@ class Plugin(GlancesPlugin):
def ip_to_cidr(ip):
# Convert IP address to CIDR
# Exemple: '255.255.255.0' will return 24
return sum(map(lambda x: int(x) << 8, ip.split('.'))) / 8128
return sum(map(lambda x: int(x) << 8, ip.split('.'))) // 8128

View File

@ -76,7 +76,7 @@ class Plugin(GlancesPlugin):
# Reset stats
self.reset()
if self.get_input() == 'local':
if self.input_method == 'local':
# Update stats using the standard system lib
# Get the load using the os standard lib
@ -89,9 +89,9 @@ class Plugin(GlancesPlugin):
'min5': load[1],
'min15': load[2],
'cpucore': self.nb_log_core}
elif self.get_input() == 'snmp':
elif self.input_method == 'snmp':
# Update stats using SNMP
self.stats = self.set_stats_snmp(snmp_oid=snmp_oid)
self.stats = self.get_stats_snmp(snmp_oid=snmp_oid)
if self.stats['min1'] == '':
self.reset()
@ -124,9 +124,9 @@ class Plugin(GlancesPlugin):
# Add specifics informations
try:
# Alert and log
self.views['min15']['decoration'] = self.get_alert_log(self.stats['min15'], max=100 * self.stats['cpucore'])
self.views['min15']['decoration'] = self.get_alert_log(self.stats['min15'], maximum=100 * self.stats['cpucore'])
# Alert only
self.views['min5']['decoration'] = self.get_alert(self.stats['min5'], max=100 * self.stats['cpucore'])
self.views['min5']['decoration'] = self.get_alert(self.stats['min5'], maximum=100 * self.stats['cpucore'])
except KeyError:
# try/except mandatory for Windows compatibility (no load stats)
pass
@ -137,7 +137,7 @@ class Plugin(GlancesPlugin):
ret = []
# Only process if stats exist...
if self.stats == {}:
if not self.stats:
return ret
# Build the string message

View File

@ -78,7 +78,7 @@ class Plugin(GlancesPlugin):
# Reset stats
self.reset()
if self.get_input() == 'local':
if self.input_method == 'local':
# Update stats using the standard system lib
# Grab MEM using the PSUtil virtual_memory method
vm_stats = psutil.virtual_memory()
@ -112,12 +112,12 @@ class Plugin(GlancesPlugin):
self.stats['free'] += self.stats['cached']
# used=total-free
self.stats['used'] = self.stats['total'] - self.stats['free']
elif self.get_input() == 'snmp':
elif self.input_method == 'snmp':
# Update stats using SNMP
if self.get_short_system_name() in ('windows', 'esxi'):
if self.short_system_name in ('windows', 'esxi'):
# Mem stats for Windows|Vmware Esxi are stored in the FS table
try:
fs_stat = self.set_stats_snmp(snmp_oid=snmp_oid[self.get_short_system_name()],
fs_stat = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name],
bulk=True)
except KeyError:
self.reset()
@ -133,7 +133,7 @@ class Plugin(GlancesPlugin):
break
else:
# Default behavor for others OS
self.stats = self.set_stats_snmp(snmp_oid=snmp_oid['default'])
self.stats = self.get_stats_snmp(snmp_oid=snmp_oid['default'])
if self.stats['total'] == '':
self.reset()
@ -167,7 +167,7 @@ class Plugin(GlancesPlugin):
# Add specifics informations
# Alert and log
self.views['used']['decoration'] = self.get_alert_log(self.stats['used'], max=self.stats['total'])
self.views['used']['decoration'] = self.get_alert_log(self.stats['used'], maximum=self.stats['total'])
# Optional
for key in ['active', 'inactive', 'buffers', 'cached']:
if key in self.stats:
@ -179,7 +179,7 @@ class Plugin(GlancesPlugin):
ret = []
# Only process if stats exist...
if self.stats == {}:
if not self.stats:
return ret
# Build the string message

View File

@ -67,7 +67,7 @@ class Plugin(GlancesPlugin):
# Reset stats
self.reset()
if self.get_input() == 'local':
if self.input_method == 'local':
# Update stats using the standard system lib
# Grab SWAP using the PSUtil swap_memory method
sm_stats = psutil.swap_memory()
@ -84,12 +84,12 @@ class Plugin(GlancesPlugin):
'sin', 'sout']:
if hasattr(sm_stats, swap):
self.stats[swap] = getattr(sm_stats, swap)
elif self.get_input() == 'snmp':
elif self.input_method == 'snmp':
# Update stats using SNMP
if self.get_short_system_name() == 'windows':
if self.short_system_name == 'windows':
# Mem stats for Windows OS are stored in the FS table
try:
fs_stat = self.set_stats_snmp(snmp_oid=snmp_oid[self.get_short_system_name()],
fs_stat = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name],
bulk=True)
except KeyError:
self.reset()
@ -109,7 +109,7 @@ class Plugin(GlancesPlugin):
'total'] - self.stats['used']
break
else:
self.stats = self.set_stats_snmp(snmp_oid=snmp_oid['default'])
self.stats = self.get_stats_snmp(snmp_oid=snmp_oid['default'])
if self.stats['total'] == '':
self.reset()
@ -142,7 +142,7 @@ class Plugin(GlancesPlugin):
# Add specifics informations
# Alert and log
self.views['used']['decoration'] = self.get_alert_log(self.stats['used'], max=self.stats['total'])
self.views['used']['decoration'] = self.get_alert_log(self.stats['used'], maximum=self.stats['total'])
def msg_curse(self, args=None):
"""Return the dict to display in the curse interface."""
@ -150,7 +150,7 @@ class Plugin(GlancesPlugin):
ret = []
# Only process if stats exist...
if self.stats == {}:
if not self.stats:
return ret
# Build the string message

View File

@ -47,7 +47,7 @@ class Plugin(GlancesPlugin):
def update(self):
"""Update the monitored list."""
if self.get_input() == 'local':
if self.input_method == 'local':
# Monitor list only available in a full Glances environment
# Check if the glances_monitor instance is init
if self.glances_monitors is None:

View File

@ -75,7 +75,7 @@ class Plugin(GlancesPlugin):
# Reset stats
self.reset()
if self.get_input() == 'local':
if self.input_method == 'local':
# Update stats using the standard system lib
# Grab network interface stat using the PsUtil net_io_counter method
@ -101,19 +101,21 @@ class Plugin(GlancesPlugin):
network_new = netiocounters
for net in network_new:
try:
# Try necessary to manage dynamic network interface
netstat = {}
netstat['interface_name'] = net
netstat['time_since_update'] = time_since_update
netstat['cumulative_rx'] = network_new[net].bytes_recv
netstat['rx'] = (network_new[net].bytes_recv -
self.network_old[net].bytes_recv)
netstat['cumulative_tx'] = network_new[net].bytes_sent
netstat['tx'] = (network_new[net].bytes_sent -
self.network_old[net].bytes_sent)
netstat['cumulative_cx'] = (netstat['cumulative_rx'] +
netstat['cumulative_tx'])
netstat['cx'] = netstat['rx'] + netstat['tx']
cumulative_rx = network_new[net].bytes_recv
cumulative_tx = network_new[net].bytes_sent
cumulative_cx = cumulative_rx + cumulative_tx
rx = cumulative_rx - self.network_old[net].bytes_recv
tx = cumulative_tx - self.network_old[net].bytes_sent
cx = rx + tx
netstat = {
'interface_name': net,
'time_since_update': time_since_update,
'cumulative_rx': cumulative_rx,
'rx': rx,
'cumulative_tx': cumulative_tx,
'tx': tx,
'cumulative_cx': cumulative_cx,
'cx': cx}
except KeyError:
continue
else:
@ -123,15 +125,15 @@ class Plugin(GlancesPlugin):
# Save stats to compute next bitrate
self.network_old = network_new
elif self.get_input() == 'snmp':
elif self.input_method == 'snmp':
# Update stats using SNMP
# SNMP bulk command to get all network interface in one shot
try:
netiocounters = self.set_stats_snmp(snmp_oid=snmp_oid[self.get_short_system_name()],
netiocounters = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name],
bulk=True)
except KeyError:
netiocounters = self.set_stats_snmp(snmp_oid=snmp_oid['default'],
netiocounters = self.get_stats_snmp(snmp_oid=snmp_oid['default'],
bulk=True)
# Previous network interface stats are stored in the network_old variable
@ -150,27 +152,30 @@ class Plugin(GlancesPlugin):
for net in network_new:
try:
# Try necessary to manage dynamic network interface
netstat = {}
# Windows: a tips is needed to convert HEX to TXT
# http://blogs.technet.com/b/networking/archive/2009/12/18/how-to-query-the-list-of-network-interfaces-using-snmp-via-the-ifdescr-counter.aspx
if self.get_short_system_name() == 'windows':
if self.short_system_name == 'windows':
try:
netstat['interface_name'] = str(base64.b16decode(net[2:-2].upper()))
interface_name = str(base64.b16decode(net[2:-2].upper()))
except TypeError:
netstat['interface_name'] = net
interface_name = net
else:
netstat['interface_name'] = net
netstat['time_since_update'] = time_since_update
netstat['cumulative_rx'] = float(network_new[net]['cumulative_rx'])
netstat['rx'] = (float(network_new[net]['cumulative_rx']) -
float(self.network_old[net]['cumulative_rx']))
netstat['cumulative_tx'] = float(network_new[net]['cumulative_tx'])
netstat['tx'] = (float(network_new[net]['cumulative_tx']) -
float(self.network_old[net]['cumulative_tx']))
netstat['cumulative_cx'] = (netstat['cumulative_rx'] +
netstat['cumulative_tx'])
netstat['cx'] = netstat['rx'] + netstat['tx']
interface_name = net
cumulative_rx = float(network_new[net]['cumulative_rx'])
cumulative_tx = float(network_new[net]['cumulative_tx'])
cumulative_cx = cumulative_rx + cumulative_tx
rx = cumulative_rx - float(self.network_old[net]['cumulative_rx'])
tx = cumulative_tx - float(self.network_old[net]['cumulative_tx'])
cx = rx + tx
netstat = {
'interface_name': interface_name,
'time_since_update': time_since_update,
'cumulative_rx': cumulative_rx,
'rx': rx,
'cumulative_tx': cumulative_tx,
'tx': tx,
'cumulative_cx': cumulative_cx,
'cx': cx}
except KeyError:
continue
else:

View File

@ -39,7 +39,7 @@ class Plugin(GlancesPlugin):
self.display_curse = True
# Set the message position
self.set_align('bottom')
self.align = 'bottom'
def update(self):
"""Update current date/time."""

View File

@ -53,7 +53,7 @@ class Plugin(GlancesPlugin):
# Grab per-CPU stats using psutil's cpu_percent(percpu=True) and
# cpu_times_percent(percpu=True) methods
if self.get_input() == 'local':
if self.input_method == 'local':
percpu_percent = psutil.cpu_percent(interval=0.0, percpu=True)
percpu_times_percent = psutil.cpu_times_percent(interval=0.0, percpu=True)
for cputimes in percpu_times_percent:

View File

@ -49,11 +49,11 @@ class GlancesPlugin(object):
self.args = args
# Init the default alignement (for curses)
self.set_align('left')
self._align = 'left'
# Init the input method
self.input_method = 'local'
self.short_system_name = None
self._input_method = 'local'
self._short_system_name = None
# Init the stats list
self.stats = None
@ -63,7 +63,7 @@ class GlancesPlugin(object):
self.stats_history = self.init_stats_history()
# Init the limits dictionnary
self.limits = dict()
self._limits = dict()
# Init the actions
self.actions = GlancesActions()
@ -107,11 +107,12 @@ class GlancesPlugin(object):
logger.debug("Reset history for plugin {0} (items: {0})".format(
self.plugin_name, reset_list))
self.stats_history = {}
return self.stats_history
def update_stats_history(self, item_name=''):
"""Update stats history"""
if self.stats != [] and self.args is not None and self.args.enable_history and self.get_items_history_list() is not None:
if (self.stats and self.args is not None and
self.args.enable_history and
self.get_items_history_list() is not None):
self.add_item_history('date', datetime.now())
for i in self.get_items_history_list():
if type(self.stats) is list:
@ -125,7 +126,6 @@ class GlancesPlugin(object):
# Stats is not a list
# Add the item to the history directly
self.add_item_history(i['name'], self.stats[i['name']])
return self.stats_history
def get_stats_history(self):
"""Return the stats history"""
@ -135,37 +135,42 @@ class GlancesPlugin(object):
"""Return the items history list"""
return self.items_history_list
def set_input(self, input_method, short_system_name=None):
@property
def input_method(self):
"""Get the input method."""
return self._input_method
@input_method.setter
def input_method(self, input_method):
"""Set the input method.
* local: system local grab (psutil or direct access)
* snmp: Client server mode via SNMP
* glances: Client server mode via Glances API
For SNMP, short_system_name is detected short OS name
"""
self.input_method = input_method
self.short_system_name = short_system_name
return self.input_method
self._input_method = input_method
def get_input(self):
"""Get the input method."""
return self.input_method
@property
def short_system_name(self):
"""Get the short detected OS name (SNMP)."""
return self._short_system_name
def get_short_system_name(self):
"""Get the short detected OS name"""
return self.short_system_name
@short_system_name.setter
def short_system_name(self, short_name):
"""Set the short detected OS name (SNMP)."""
self._short_system_name = short_name
def set_stats(self, input_stats):
"""Set the stats to input_stats."""
self.stats = input_stats
return self.stats
def set_stats_snmp(self, bulk=False, snmp_oid={}):
def get_stats_snmp(self, bulk=False, snmp_oid=None):
"""Update stats using SNMP.
If bulk=True, use a bulk request instead of a get request.
"""
snmp_oid = snmp_oid or {}
from glances.core.glances_snmp import GlancesSNMPClient
# Init the SNMP request
@ -201,7 +206,7 @@ class GlancesPlugin(object):
item_key = item[oid]
else:
item_stats[key] = item[oid]
if item_stats != {}:
if item_stats:
ret[item_key] = item_stats
index += 1
else:
@ -299,7 +304,6 @@ class GlancesPlugin(object):
def set_views(self, input_views):
"""Set the views to input_views."""
self.views = input_views
return self.views
def get_views(self, item=None, key=None, option=None):
"""Return the views object.
@ -326,27 +330,26 @@ class GlancesPlugin(object):
"""Load the limits from the configuration file."""
if (hasattr(config, 'has_section') and
config.has_section(self.plugin_name)):
for s, v in config.items(self.plugin_name):
for level, v in config.items(self.plugin_name):
# Read limits
limit = '_'.join([self.plugin_name, level])
try:
self.limits[
self.plugin_name + '_' + s] = config.get_option(self.plugin_name, s)
self._limits[limit] = config.get_option(self.plugin_name, level)
except ValueError:
self.limits[
self.plugin_name + '_' + s] = config.get_raw_option(self.plugin_name, s).split(",")
logger.debug("Load limit: {0} = {1}".format(self.plugin_name + '_' + s,
self.limits[self.plugin_name + '_' + s]))
self._limits[limit] = config.get_raw_option(self.plugin_name, level).split(",")
logger.debug("Load limit: {0} = {1}".format(limit, self._limits[limit]))
def set_limits(self, input_limits):
"""Set the limits to input_limits."""
self.limits = input_limits
return self.limits
def get_limits(self):
@property
def limits(self):
"""Return the limits object."""
return self.limits
return self._limits
def get_alert(self, current=0, min=0, max=100, header="", log=False):
@limits.setter
def limits(self, input_limits):
"""Set the limits to input_limits."""
self._limits = input_limits
def get_alert(self, current=0, minimum=0, maximum=100, header="", log=False):
"""Return the alert status relative to a current value.
Use this function for minor stats.
@ -365,7 +368,7 @@ class GlancesPlugin(object):
"""
# Compute the %
try:
value = (current * 100) / max
value = (current * 100) / maximum
except ZeroDivisionError:
return 'DEFAULT'
except TypeError:
@ -386,7 +389,7 @@ class GlancesPlugin(object):
ret = 'WARNING'
elif value > self.__get_limit('careful', stat_name=stat_name):
ret = 'CAREFUL'
elif current < min:
elif current < minimum:
ret = 'CAREFUL'
except KeyError:
return 'DEFAULT'
@ -428,20 +431,20 @@ class GlancesPlugin(object):
# Default is ok
return ret + log_str
def get_alert_log(self, current=0, min=0, max=100, header=""):
def get_alert_log(self, current=0, minimum=0, maximum=100, header=""):
"""Get the alert log."""
return self.get_alert(current, min, max, header, log=True)
return self.get_alert(current, minimum, maximum, header, log=True)
def __get_limit(self, criticity, stat_name=""):
"""Return the limit value for the alert"""
# Get the limit for stat + header
# Exemple: network_wlan0_rx_careful
try:
limit = self.limits[stat_name + '_' + criticity]
limit = self._limits[stat_name + '_' + criticity]
except KeyError:
# Try fallback to plugin default limit
# Exemple: network_careful
limit = self.limits[self.plugin_name + '_' + criticity]
limit = self._limits[self.plugin_name + '_' + criticity]
# Return the limit
return limit
@ -451,11 +454,11 @@ class GlancesPlugin(object):
# Get the action for stat + header
# Exemple: network_wlan0_rx_careful_action
try:
ret = self.limits[stat_name + '_' + criticity + '_action']
ret = self._limits[stat_name + '_' + criticity + '_action']
except KeyError:
# Try fallback to plugin default limit
# Exemple: network_careful_action
ret = self.limits[self.plugin_name + '_' + criticity + '_action']
ret = self._limits[self.plugin_name + '_' + criticity + '_action']
# Return the action list
return ret
@ -465,12 +468,12 @@ class GlancesPlugin(object):
# Get the log tag for stat + header
# Exemple: network_wlan0_rx_log
try:
log_tag = self.limits[stat_name + '_log']
log_tag = self._limits[stat_name + '_log']
except KeyError:
# Try fallback to plugin default log
# Exemple: network_log
try:
log_tag = self.limits[self.plugin_name + '_log']
log_tag = self._limits[self.plugin_name + '_log']
except KeyError:
# By defaukt, log are disabled
return default_action
@ -484,12 +487,12 @@ class GlancesPlugin(object):
plugin_name = self.plugin_name
if header == "":
try:
return self.limits[plugin_name + '_' + value]
return self._limits[plugin_name + '_' + value]
except KeyError:
return []
else:
try:
return self.limits[plugin_name + '_' + header + '_' + value]
return self._limits[plugin_name + '_' + header + '_' + value]
except KeyError:
return []
@ -500,7 +503,7 @@ class GlancesPlugin(object):
def has_alias(self, header):
"""Return the alias name for the relative header or None if nonexist"""
try:
return self.limits[self.plugin_name + '_' + header + '_' + 'alias'][0]
return self._limits[self.plugin_name + '_' + header + '_' + 'alias'][0]
except (KeyError, IndexError):
return None
@ -522,7 +525,7 @@ class GlancesPlugin(object):
if hasattr(self, 'display_curse'):
display_curse = self.display_curse
if hasattr(self, 'align'):
align_curse = self.align
align_curse = self._align
if max_width is not None:
ret = {'display': display_curse,
@ -568,16 +571,18 @@ class GlancesPlugin(object):
"""Go to a new line."""
return self.curse_add_line('\n')
def set_align(self, align='left'):
"""Set the Curse align"""
if align in ('left', 'right', 'bottom'):
self.align = align
else:
self.align = 'left'
@property
def align(self):
"""Get the curse align."""
return self._align
def get_align(self):
"""Get the Curse align"""
return self.align
@align.setter
def align(self, value):
"""Set the curse align.
value: left, right, bottom.
"""
self._align = value
def auto_unit(self, number, low_precision=False):
"""Make a nice human-readable string out of number.

View File

@ -52,14 +52,14 @@ class Plugin(GlancesPlugin):
# Reset stats
self.reset()
if self.get_input() == 'local':
if self.input_method == 'local':
# Update stats using the standard system lib
# Here, update is call for processcount AND processlist
glances_processes.update()
# Return the processes count
self.stats = glances_processes.getcount()
elif self.get_input() == 'snmp':
elif self.input_method == 'snmp':
# Update stats using SNMP
# !!! TODO
pass
@ -77,14 +77,14 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_add_line(msg))
return ret
if self.stats == {}:
if not self.stats:
return ret
# Display the filter (if it exists)
if glances_processes.get_process_filter() is not None:
if glances_processes.process_filter is not None:
msg = _("Processes filter:")
ret.append(self.curse_add_line(msg, "TITLE"))
msg = _(" {0} ").format(glances_processes.get_process_filter())
msg = _(" {0} ").format(glances_processes.process_filter)
ret.append(self.curse_add_line(msg, "FILTER"))
msg = _("(press ENTER to edit)")
ret.append(self.curse_add_line(msg))
@ -117,13 +117,13 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_add_line(msg))
# Display sort information
if glances_processes.getmanualsortkey() is None:
if glances_processes.auto_sort:
msg = _("sorted automatically")
ret.append(self.curse_add_line(msg))
msg = _(" by {0}").format(glances_processes.getautosortkey())
msg = _(" by {0}").format(glances_processes.sort_key)
ret.append(self.curse_add_line(msg))
else:
msg = _("sorted by {0}").format(glances_processes.getmanualsortkey())
msg = _("sorted by {0}").format(glances_processes.sort_key)
ret.append(self.curse_add_line(msg))
ret[-1]["msg"] += ", %s view" % ("tree" if glances_processes.is_tree_enabled() else "flat")

View File

@ -59,7 +59,7 @@ class Plugin(GlancesPlugin):
# Reset stats
self.reset()
if self.get_input() == 'local':
if self.input_method == 'local':
# Update stats using the standard system lib
# Note: Update is done in the processcount plugin
# Just return the processes list
@ -67,7 +67,7 @@ class Plugin(GlancesPlugin):
self.stats = glances_processes.gettree()
else:
self.stats = glances_processes.getlist()
elif self.get_input() == 'snmp':
elif self.input_method == 'snmp':
# No SNMP grab for processes
pass
@ -154,8 +154,7 @@ class Plugin(GlancesPlugin):
def get_process_curses_data(self, p, first, args):
""" Get curses data to display for a process. """
ret = []
ret.append(self.curse_new_line())
ret = [self.curse_new_line()]
# CPU
if 'cpu_percent' in p and p['cpu_percent'] is not None and p['cpu_percent'] != '':
msg = '{0:>6.1f}'.format(p['cpu_percent'])
@ -365,7 +364,7 @@ class Plugin(GlancesPlugin):
return ret
# Compute the sort key
process_sort_key = glances_processes.getsortkey()
process_sort_key = glances_processes.sort_key
sort_style = 'SORT'
# Header
@ -401,7 +400,7 @@ class Plugin(GlancesPlugin):
ret.extend(self.get_process_tree_curses_data(self.sortstats(process_sort_key),
args,
first_level=True,
max_node_count=glances_processes.get_max_processes()))
max_node_count=glances_processes.max_processes))
else:
# Loop over processes (sorted by the sort key previously compute)
first = True

View File

@ -45,7 +45,7 @@ class Plugin(GlancesPlugin):
self.reset()
# Return PsUtil version as a tuple
if self.get_input() == 'local':
if self.input_method == 'local':
# PsUtil version only available in local
try:
self.stats = tuple([int(num) for num in __psutil_version.split('.')])

View File

@ -24,7 +24,7 @@ import psutil
from glances.plugins.glances_plugin import GlancesPlugin
from glances.core.glances_cpu_percent import cpu_percent
from glances.outputs.glances_bars import Bar
from glances.core.glances_logging import logger
#from glances.core.glances_logging import logger
class Plugin(GlancesPlugin):
@ -55,13 +55,13 @@ class Plugin(GlancesPlugin):
self.reset()
# Grab quicklook stats: CPU, MEM and SWAP
if self.get_input() == 'local':
if self.input_method == 'local':
# Get the latest CPU percent value
self.stats['cpu'] = cpu_percent.get()
# Use the PsUtil lib for the memory (virtual and swap)
self.stats['mem'] = psutil.virtual_memory().percent
self.stats['swap'] = psutil.swap_memory().percent
elif self.get_input() == 'snmp':
elif self.input_method == 'snmp':
# Not available
pass
@ -87,7 +87,7 @@ class Plugin(GlancesPlugin):
ret = []
# Only process if stats exist...
if self.stats == {} or args.disable_quicklook:
if not self.stats or args.disable_quicklook:
return ret
# Define the bar
@ -95,7 +95,7 @@ class Plugin(GlancesPlugin):
# Build the string message
for key in ['cpu', 'mem', 'swap']:
bar.set_percent(self.stats[key])
bar.percent = self.stats[key]
msg = '{0:>4} '.format(key.upper())
ret.append(self.curse_add_line(msg))
msg = '{0}'.format(bar)

View File

@ -28,7 +28,6 @@ try:
from pymdstat import MdStat
except ImportError:
logger.debug("pymdstat library not found. Glances cannot grab RAID info.")
pass
class Plugin(GlancesPlugin):
@ -58,7 +57,7 @@ class Plugin(GlancesPlugin):
# Reset stats
self.reset()
if self.get_input() == 'local':
if self.input_method == 'local':
# Update stats using the PyMDstat lib (https://github.com/nicolargo/pymdstat)
try:
mds = MdStat()
@ -67,7 +66,7 @@ class Plugin(GlancesPlugin):
logger.debug("Can not grab RAID stats (%s)" % e)
return self.stats
elif self.get_input() == 'snmp':
elif self.input_method == 'snmp':
# Update stats using SNMP
# No standard way for the moment...
pass

View File

@ -83,7 +83,7 @@ class Plugin(GlancesPlugin):
# Reset the stats
self.reset()
if self.get_input() == 'local':
if self.input_method == 'local':
# Update stats using the dedicated lib
self.stats = []
# Get the temperature
@ -123,7 +123,7 @@ class Plugin(GlancesPlugin):
# Append Batteries %
self.stats.extend(batpercent)
elif self.get_input() == 'snmp':
elif self.input_method == 'snmp':
# Update stats using SNMP
# No standard:
# http://www.net-snmp.org/wiki/index.php/Net-SNMP_and_lm-sensors_on_Ubuntu_10.04
@ -156,7 +156,7 @@ class Plugin(GlancesPlugin):
# Add specifics informations
# Alert
for i in self.stats:
if i['value'] == []:
if not i['value']:
continue
if i['type'] == 'battery':
self.views[i[self.get_key()]]['value']['decoration'] = self.get_alert(100 - i['value'], header=i['type'])
@ -169,7 +169,7 @@ class Plugin(GlancesPlugin):
ret = []
# Only process if stats exist and display plugin enable...
if not self.stats or args.disable_sensors or self.stats == []:
if not self.stats or args.disable_sensors:
return ret
# Build the string message
@ -178,7 +178,7 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_add_line(msg, "TITLE"))
for i in self.stats:
if i['value'] is not None and i['value'] != []:
if i['value']:
# New line
ret.append(self.curse_new_line())
# Alias for the lable name ?
@ -187,8 +187,8 @@ class Plugin(GlancesPlugin):
label = i['label']
try:
msg = "{0:12} {1:3}".format(label[:11], i['unit'])
except KeyError:
msg = '{0:16}'.format(label[:15])
except (KeyError, UnicodeEncodeError):
msg = "{0:16}".format(label[:15])
ret.append(self.curse_add_line(msg))
msg = '{0:>7}'.format(i['value'])
ret.append(self.curse_add_line(
@ -234,23 +234,23 @@ class GlancesGrabSensors(object):
elif feature.name.startswith(b'fan'):
# Fan speed sensor
sensors_current['unit'] = SENSOR_FAN_UNIT
if sensors_current != {}:
if sensors_current:
sensors_current['label'] = feature.label
sensors_current['value'] = int(feature.get_value())
self.sensors_list.append(sensors_current)
return self.sensors_list
def get(self, type='temperature_core'):
def get(self, sensor_type='temperature_core'):
"""Get sensors list."""
self.__update__()
if type == 'temperature_core':
if sensor_type == 'temperature_core':
ret = [s for s in self.sensors_list if s['unit'] == SENSOR_TEMP_UNIT]
elif type == 'fan_speed':
elif sensor_type == 'fan_speed':
ret = [s for s in self.sensors_list if s['unit'] == SENSOR_FAN_UNIT]
else:
# Unknown type
logger.debug("Unknown sensor type %s" % type)
logger.debug("Unknown sensor type %s" % sensor_type)
ret = []
return ret

View File

@ -103,7 +103,7 @@ class Plugin(GlancesPlugin):
# Reset stats
self.reset()
if self.get_input() == 'local':
if self.input_method == 'local':
# Update stats using the standard system lib
self.stats['os_name'] = platform.system()
self.stats['hostname'] = platform.node()
@ -136,17 +136,17 @@ class Plugin(GlancesPlugin):
self.stats['os_name'], self.stats['os_version'])
self.stats['hr_name'] += ' ({0})'.format(self.stats['platform'])
elif self.get_input() == 'snmp':
elif self.input_method == 'snmp':
# Update stats using SNMP
try:
self.stats = self.set_stats_snmp(
snmp_oid=snmp_oid[self.get_short_system_name()])
self.stats = self.get_stats_snmp(
snmp_oid=snmp_oid[self.short_system_name])
except KeyError:
self.stats = self.set_stats_snmp(snmp_oid=snmp_oid['default'])
self.stats = self.get_stats_snmp(snmp_oid=snmp_oid['default'])
# Default behavor: display all the information
self.stats['os_name'] = self.stats['system_name']
# Windows OS tips
if self.get_short_system_name() == 'windows':
if self.short_system_name == 'windows':
try:
iteritems = snmp_to_human['windows'].iteritems()
except AttributeError:

View File

@ -47,7 +47,7 @@ class Plugin(GlancesPlugin):
self.display_curse = True
# Set the message position
self.set_align('right')
self.align = 'right'
# Init the stats
self.reset()
@ -61,16 +61,15 @@ class Plugin(GlancesPlugin):
# Reset stats
self.reset()
if self.get_input() == 'local':
if self.input_method == 'local':
# Update stats using the standard system lib
uptime = datetime.now() - \
datetime.fromtimestamp(psutil.boot_time())
uptime = datetime.now() - datetime.fromtimestamp(psutil.boot_time())
# Convert uptime to string (because datetime is not JSONifi)
self.stats = str(uptime).split('.')[0]
elif self.get_input() == 'snmp':
elif self.input_method == 'snmp':
# Update stats using SNMP
uptime = self.set_stats_snmp(snmp_oid=snmp_oid)['_uptime']
uptime = self.get_stats_snmp(snmp_oid=snmp_oid)['_uptime']
try:
# In hundredths of seconds
self.stats = str(timedelta(seconds=int(uptime) / 100))
@ -82,11 +81,4 @@ class Plugin(GlancesPlugin):
def msg_curse(self, args=None):
"""Return the string to display in the curse interface."""
# Init the return message
ret = []
# Add the line with decoration
ret.append(self.curse_add_line(_("Uptime: {0}").format(self.stats)))
# Return the message with decoration
return ret
return [self.curse_add_line(_("Uptime: {0}").format(self.stats))]

View File

@ -49,6 +49,10 @@ disable network module
.B \-\-disable-sensors
disable sensors module
.TP
.B \-\-disable-hddtemp
disable HDDTemp module
.TP
.TP
.B \-\-disable-left-sidebar
disable network, disk IO, FS and sensors modules
.TP
@ -121,6 +125,12 @@ set refresh time in seconds [default: 3 sec]
.B \-w, \-\-webserver
run Glances in Web server mode
.TP
.B \-q, \-\-quiet
run Glances in quiet mode (nothing is displayed)
.TP
.B -\f PROCESS_FILTER, \-\-process\-filter PROCESS_FILTER
set the process filter patern (regular expression)
.TP
.B \-1, \-\-percpu
start Glances in per CPU mode
.TP
@ -210,9 +220,31 @@ Show/hide processes list (for low CPU consumption)
Switch between global CPU and per-CPU stats
.SH EXAMPLES
.TP
Refresh information every 5 seconds:
.B glances
\-t 5
Monitor local machine (standalone mode):
.B $ glances
.PP
Monitor local machine with the Web interface (Web UI):
.B $ glances -w
.PP
Glances web server started on http://0.0.0.0:61208/
.PP
Monitor local machine and export stats to a CSV file (standalone mode):
.B $ glances --export-csv
.PP
Monitor local machine and export stats to a InfluxDB server with 5s refresh time (standalone mode):
.B $ glances -t 5 --export-influxdb
.PP
Start a Glances server (server mode):
.B $ glances -s
.PP
Connect Glances to a Glances server (client mode):
.B $ glances -c <ip_server>
.PP
Connect Glances to a Glances server and export stats to a StatsD server (client mode):
.B $ glances -c <ip_server> --export-statsd
.PP
Start the client browser (browser mode):
.B $ glances --browser
.PP
.SH EXIT STATUS
Glances returns a zero exit status if it succeeds to print/grab information.

View File

@ -1 +1 @@
psutil==2.2.0
psutil==2.2.1

View File

@ -8,6 +8,9 @@ from setuptools import setup
is_chroot = os.stat('/').st_ino != 2
if sys.version_info < (2, 6) or (3, 0) <= sys.version_info < (3, 3):
print('Glances requires at least Python 2.6 or 3.3 to run.')
sys.exit(1)
def get_data_files():
data_files = [
@ -31,7 +34,6 @@ def get_data_files():
conf_path = os.path.join(os.environ.get('APPDATA'), 'glances')
else: # Unix-like + per-user install
conf_path = os.path.join('etc', 'glances')
data_files.append((conf_path, ['conf/glances.conf']))
for mo in glob.glob('i18n/*/LC_MESSAGES/*.mo'):
@ -45,8 +47,8 @@ def get_requires():
requires = ['psutil>=2.0.0']
if sys.platform.startswith('win'):
requires += ['colorconsole']
if sys.version_info < (2, 7):
requires += ['argparse']
if sys.version_info == (2, 6):
requires += ['argparse', 'logutils']
return requires
@ -71,7 +73,7 @@ setup(
'BROWSER': ['zeroconf>=0.16', 'netifaces'],
'RAID': ['pymdstat'],
'DOCKER': ['docker-py'],
'EXPORT': ['influxdb', 'statsd'],
'EXPORT': ['influxdb>=1.0.0', 'statsd'],
'ACTION': ['pystache']
},
packages=['glances'],

View File

@ -1,7 +1,7 @@
# Required metadata
sonar.projectKey=glances
sonar.projectName=Glances
sonar.projectVersion=2.3_beta
sonar.projectVersion=2.4_beta
# Path to the parent source code directory.
# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
@ -17,4 +17,4 @@ sonar.language=py
sonar.sourceEncoding=UTF-8
# Additional parameters
#sonar.my.property=value
#sonar.my.property=value

View File

@ -91,7 +91,7 @@ class TestGlances(unittest.TestCase):
args = shlex.split(cmdline)
pid = subprocess.Popen(args)
print("Please wait...")
time.sleep(1)
time.sleep(3)
self.assertTrue(pid is not None)

View File

@ -78,14 +78,14 @@ class TestGlances(unittest.TestCase):
print('INFO: [TEST_000] Test the stats update function')
try:
stats.update()
except:
print('ERROR: Stats update failed')
except Exception as e:
print('ERROR: Stats update failed ({})'.format(e))
self.assertTrue(False)
time.sleep(1)
try:
stats.update()
except:
print('ERROR: Stats update failed')
except Exception as e:
print('ERROR: Stats update failed ({})'.format(e))
self.assertTrue(False)
self.assertTrue(True)