No more system-wide configuration file by default

Support remains there, but no system-wide configuration file will be
provided anymore.

Default configuration settings are initialized by code which may be
overridden by a configuration file.
This commit is contained in:
Alessio Sergi 2015-04-16 12:36:16 +02:00
parent 9a87924596
commit 4318361517
14 changed files with 206 additions and 343 deletions

6
NEWS
View File

@ -5,6 +5,12 @@ Glances Version 2.x
Version 2.4
===========
Changes:
* Glances doesn't provide a system-wide configuration file by default anymore.
Just copy it in any of the supported locations. See glances-doc.html for
more information.
Enhancements and new features:
* Implement a 'quick look' plugin (issue #505)

View File

@ -1,195 +0,0 @@
[quicklook]
cpu_careful=50
cpu_warning=70
cpu_critical=90
mem_careful=50
mem_warning=70
mem_critical=90
swap_careful=50
swap_warning=70
swap_critical=90
[cpu]
# Default values if not defined: 50/70/90
user_careful=50
user_warning=70
user_critical=90
#user_log=False
user_critical_action=echo {{user}} {{value}} {{max}} > /tmp/cpu.alert
iowait_careful=50
iowait_warning=70
iowait_critical=90
system_careful=50
system_warning=70
system_critical=90
steal_careful=50
steal_warning=70
steal_critical=90
#steal_log=True
[percpu]
# Default values if not defined: 50/70/90
user_careful=50
user_warning=70
user_critical=90
iowait_careful=50
iowait_warning=70
iowait_critical=90
system_careful=50
system_warning=70
system_critical=90
[load]
# Value * number of cores
# Default values if not defined: 0.7/1.0/5.0 per number of cores
# Source: http://blog.scoutapp.com/articles/2009/07/31/understanding-load-averages
# http://www.linuxjournal.com/article/9001
careful=0.7
warning=1.0
critical=5.0
#log=False
[mem]
# Default limits for free RAM memory in %
# Default values if not defined: 50/70/90
careful=50
warning=70
critical=90
[memswap]
# Default limits for free swap memory in %
# Default values if not defined: 50/70/90
careful=50
warning=70
critical=90
[network]
# Define the list of hidden network interfaces (comma separeted)
hide=lo
# WLAN0 alias name
wlan0_alias=Wireless
# WLAN0 Default limits (in bits per second aka bps) for interface bitrate
wlan0_rx_careful=4000000
wlan0_rx_warning=5000000
wlan0_rx_critical=6000000
wlan0_rx_log=True
wlan0_tx_careful=700000
wlan0_tx_warning=900000
wlan0_tx_critical=1000000
wlan0_tx_log=True
[diskio]
# Define the list of hidden disks (comma separeted)
hide=sda5
# Alias for sda1
#sda1_alias=IntDisk
# SDA1 limits (in bytes per second aka Bps) for interface bitrate
sda2_rx_careful=150000000
sda2_rx_warning=180000000
sda2_rx_critical=200000000
#sda2_rx_log=True
sda2_tx_careful=150000000
sda2_tx_warning=180000000
sda2_tx_critical=200000000
#sda2_tx_log=True
[fs]
# Default limits for free filesytem space in %
# Default values if not defined: 50/70/90
careful=50
careful_action=echo {{mnt_point}} {{used}}/{{size}} > /tmp/fs.alert
warning=70
critical=90
# Allow additionnals files types (comma-separated FS type)
allow=zfs
[sensors]
# Sensors core limits
# Default values if not defined: 60/70/80
temperature_core_careful=50
temperature_core_warning=70
temperature_core_critical=80
# Temperatures in °C for hddtemp
# Default values if not defined: 45/52/60
temperature_hdd_careful=45
temperature_hdd_warning=52
temperature_hdd_critical=60
# Battery % limits
battery_careful=80
battery_warning=90
battery_critical=95
# Sensors alias
temp1_alias=Motherboard 0
temp2_alias=Motherboard 1
core 0_alias=CPU Core 0
core 1_alias=CPU Core 1
[processlist]
# Limit values for CPU/MEM per process in %
# Default values if not defined: 50/70/90
cpu_careful=50
cpu_warning=70
cpu_critical=90
mem_careful=50
mem_warning=70
mem_critical=90
[monitor]
# Define the list of processes to monitor
# *** This section is optional ***
# The list is composed of items (list_#nb <= 10)
# An item is defined:
# * description: Description of the processes (max 16 chars)
# * regex: regular expression of the processes to monitor
# * command: (optional) full path to shell command/script for extended stat
# Use with caution. Should return a single line string.
# Only execute when at least one process is running
# By default display CPU and MEM %
# Limitation: Do not use in client / server mode
# * countmin: (optional) minimal number of processes
# A warning will be displayed if number of process < count
# * countmax: (optional) maximum number of processes
# A warning will be displayed if number of process > count
#list_1_description=Dropbox
#list_1_regex=.*dropbox.*
#list_1_countmin=1
#list_1_command=dropbox status | head -1
list_1_description=Python programs
list_1_regex=.*python.*
list_2_description=Famous Xeyes
list_2_regex=.*xeyes.*
list_2_countmin=1
[serverlist]
# Define the static server list
server_1_name=localhost
server_1_alias=My local PC
server_1_port=61209
server_2_name=localhost
server_2_port=61235
server_3_name=192.168.0.17
server_3_alias=Another PC on my network
server_3_port=61209
server_4_name=pasbon
server_4_port=61237
[influxdb]
host=localhost
port=8086
user=root
password=root
db=glances
#prefix=localhost
[statsd]
host=localhost
port=8125
#prefix=glances
[rabbitmq]
host=localhost
port=5672
user=guest
password=guest
queue=glances_queue

View File

@ -288,15 +288,20 @@ Configuration
No configuration file is mandatory to use Glances.
Furthermore a configuration file is needed to set up limits, disks or
network interfaces to hide and/or monitored processes list or to define
alias.
Furthermore a configuration file is needed to modify limit alerts, to
set up monitored processes list, to hide disks or network interfaces or
to define alias.
By default, the configuration file is under:
Location
--------
:Linux: ``/etc/glances/glances.conf``
:\*BSD and OS X: ``/usr/local/etc/glances/glances.conf``
:Windows: ``%APPDATA%\glances\glances.conf``
You can put the configuration file ``glances.conf`` in the following
locations:
:Linux: ``~/.config/glances, /etc/glances``
:\*BSD: ``~/.config/glances, /usr/local/etc/glances``
:OS X: ``~/Library/Application Support/glances, /usr/local/etc/glances``
:Windows: ``%APPDATA%\glances``
On Windows XP, the ``%APPDATA%`` path is:
@ -309,24 +314,12 @@ Since Windows Vista and newer versions:
::
C:\Users\<User>\AppData\Roaming
or
%userprofile%\AppData\Roaming
You can override the default configuration, located in one of the above
directories on your system, except for Windows.
User-specific options override system-wide options and options given on
the command line override either.
Just copy the ``glances.conf`` file to your ``$XDG_CONFIG_HOME`` directory,
e.g., on Linux:
.. code-block:: console
mkdir -p $XDG_CONFIG_HOME/glances
cp /usr/share/doc/glances/glances.conf $XDG_CONFIG_HOME/glances/
On OS X, you should copy the configuration file to
``~/Library/Application Support/glances/``.
*Configuration file description*
Syntax
------
Each plugin and export module can have a section.
@ -348,7 +341,8 @@ Example for the CPU plugin:
steal_warning=70
steal_critical=90
By default Steal CPU time alerts aren't logged. If you want to enable log/alert, just add:
By default the ``steal`` CPU time alerts aren't logged. If you want to
enable log/alert, just add:
.. code-block::

View File

@ -23,10 +23,10 @@
import os
import sys
try:
from configparser import RawConfigParser
from configparser import ConfigParser
from configparser import NoOptionError
except ImportError: # Python 2
from ConfigParser import RawConfigParser
from ConfigParser import SafeConfigParser as ConfigParser
from ConfigParser import NoOptionError
# Import Glances lib
@ -37,8 +37,7 @@ from glances.core.glances_globals import (
is_mac,
is_py3,
is_windows,
sys_prefix,
work_path
sys_prefix
)
from glances.core.glances_logging import logger
@ -47,96 +46,177 @@ class Config(object):
"""This class is used to access/read config file, if it exists.
:param location: the custom path to search for config file
:type location: str or None
:param config_dir: the path to search for config file
:type config_dir: str or None
"""
def __init__(self, location=None):
self.location = location
def __init__(self, config_dir=None):
self.config_dir = config_dir
self.config_filename = 'glances.conf'
self.parser = RawConfigParser()
self._loaded_config_file = None
self.load()
def load(self):
"""Load a config file from the list of paths, if it exists."""
for config_file in self.get_config_paths():
if os.path.isfile(config_file) and os.path.getsize(config_file) > 0:
try:
if is_py3:
self.parser.read(config_file, encoding='utf-8')
else:
self.parser.read(config_file)
logger.info("Read configuration file '{0}'".format(config_file))
except UnicodeDecodeError as e:
logger.error("Cannot decode configuration file '{0}': {1}".format(config_file, e))
sys.exit(1)
# Save the loaded configuration file path (issue #374)
self._loaded_config_file = config_file
break
self.parser = ConfigParser()
self.read()
def get_loaded_config_file(self):
"""Return the loaded configuration file"""
return self._loaded_config_file
def get_config_paths(self):
def config_file_paths(self):
r"""Get a list of config file paths.
The list is built taking into account of the OS, priority and location.
* running from source: /path/to/glances/conf
* per-user install: ~/.local/etc/glances (Unix-like only)
* custom path: /path/to/glances
* Linux: ~/.config/glances, /etc/glances
* BSD: ~/.config/glances, /usr/local/etc/glances
* Mac: ~/Library/Application Support/glances, /usr/local/etc/glances
* OS X: ~/Library/Application Support/glances, /usr/local/etc/glances
* Windows: %APPDATA%\glances
The config file will be searched in the following order of priority:
* /path/to/file (via -C flag)
* /path/to/glances/conf
* user's local directory (per-user install settings)
* user's home directory (per-user settings)
* {/usr/local,}/etc directory (system-wide settings)
* system-wide directory (system-wide settings)
"""
paths = []
conf_path = os.path.realpath(
os.path.join(work_path, '..', '..', 'conf'))
if self.location is not None:
paths.append(self.location)
if os.path.exists(conf_path):
paths.append(os.path.join(conf_path, self.config_filename))
if not is_windows:
paths.append(os.path.join(os.path.expanduser('~/.local'), 'etc', appname, self.config_filename))
if self.config_dir:
paths.append(self.config_dir)
if is_linux or is_bsd:
paths.append(os.path.join(
os.environ.get('XDG_CONFIG_HOME') or os.path.expanduser(
'~/.config'),
appname, self.config_filename))
if hasattr(sys, 'real_prefix') or is_bsd:
paths.append(
os.path.join(os.environ.get('XDG_CONFIG_HOME') or
os.path.expanduser('~/.config'),
appname, self.config_filename))
if is_bsd:
paths.append(
os.path.join(sys.prefix, 'etc', appname, self.config_filename))
else:
paths.append(
os.path.join('/etc', appname, self.config_filename))
elif is_mac:
paths.append(os.path.join(
os.path.expanduser('~/Library/Application Support/'),
appname, self.config_filename))
paths.append(os.path.join(
sys_prefix, 'etc', appname, self.config_filename))
paths.append(
os.path.join(os.path.expanduser('~/Library/Application Support/'),
appname, self.config_filename))
paths.append(
os.path.join(sys_prefix, 'etc', appname, self.config_filename))
elif is_windows:
paths.append(os.path.join(
os.environ.get('APPDATA'), appname, self.config_filename))
paths.append(
os.path.join(os.environ.get('APPDATA'), appname, self.config_filename))
return paths
def read(self):
"""Read the config file, if it exists. Using defaults otherwise."""
for config_file in self.config_file_paths():
if os.path.exists(config_file):
try:
if is_py3:
self.parser.read(config_file, encoding='utf-8')
else:
self.parser.read(config_file)
logger.info("Read configuration file '{0}'".format(config_file))
except UnicodeDecodeError as err:
logger.error("Cannot decode configuration file '{0}': {1}".format(config_file, err))
sys.exit(1)
# Save the loaded configuration file path (issue #374)
self._loaded_config_file = config_file
break
# Quicklook
if not self.parser.has_section('quicklook'):
self.parser.add_section('quicklook')
self.parser.set('quicklook', 'cpu_careful', '50')
self.parser.set('quicklook', 'cpu_warning', '70')
self.parser.set('quicklook', 'cpu_critical', '90')
self.parser.set('quicklook', 'mem_careful', '50')
self.parser.set('quicklook', 'mem_warning', '70')
self.parser.set('quicklook', 'mem_critical', '90')
self.parser.set('quicklook', 'swap_careful', '50')
self.parser.set('quicklook', 'swap_warning', '70')
self.parser.set('quicklook', 'swap_critical', '90')
# CPU
if not self.parser.has_section('cpu'):
self.parser.add_section('cpu')
self.parser.set('cpu', 'user_careful', '50')
self.parser.set('cpu', 'user_warning', '70')
self.parser.set('cpu', 'user_critical', '90')
self.parser.set('cpu', 'iowait_careful', '50')
self.parser.set('cpu', 'iowait_warning', '70')
self.parser.set('cpu', 'iowait_critical', '90')
self.parser.set('cpu', 'system_careful', '50')
self.parser.set('cpu', 'system_warning', '70')
self.parser.set('cpu', 'system_critical', '90')
self.parser.set('cpu', 'steal_careful', '50')
self.parser.set('cpu', 'steal_warning', '70')
self.parser.set('cpu', 'steal_critical', '90')
# Per-CPU
if not self.parser.has_section('percpu'):
self.parser.add_section('percpu')
self.parser.set('percpu', 'user_careful', '50')
self.parser.set('percpu', 'user_warning', '70')
self.parser.set('percpu', 'user_critical', '90')
self.parser.set('percpu', 'iowait_careful', '50')
self.parser.set('percpu', 'iowait_warning', '70')
self.parser.set('percpu', 'iowait_critical', '90')
self.parser.set('percpu', 'system_careful', '50')
self.parser.set('percpu', 'system_warning', '70')
self.parser.set('percpu', 'system_critical', '90')
# Load
if not self.parser.has_section('load'):
self.parser.add_section('load')
self.parser.set('load', 'careful', '0.7')
self.parser.set('load', 'warning', '1.0')
self.parser.set('load', 'critical', '5.0')
# Mem
if not self.parser.has_section('mem'):
self.parser.add_section('mem')
self.parser.set('mem', 'careful', '50')
self.parser.set('mem', 'warning', '70')
self.parser.set('mem', 'critical', '90')
# Swap
if not self.parser.has_section('memswap'):
self.parser.add_section('memswap')
self.parser.set('memswap', 'careful', '50')
self.parser.set('memswap', 'warning', '70')
self.parser.set('memswap', 'critical', '90')
# FS
if not self.parser.has_section('fs'):
self.parser.add_section('fs')
self.parser.set('fs', 'careful', '50')
self.parser.set('fs', 'warning', '70')
self.parser.set('fs', 'critical', '90')
# Sensors
if not self.parser.has_section('sensors'):
self.parser.add_section('sensors')
self.parser.set('sensors', 'temperature_core_careful', '60')
self.parser.set('sensors', 'temperature_core_warning', '70')
self.parser.set('sensors', 'temperature_core_critical', '80')
self.parser.set('sensors', 'temperature_hdd_careful', '45')
self.parser.set('sensors', 'temperature_hdd_warning', '52')
self.parser.set('sensors', 'temperature_hdd_critical', '60')
self.parser.set('sensors', 'battery_careful', '80')
self.parser.set('sensors', 'battery_warning', '90')
self.parser.set('sensors', 'battery_critical', '95')
# Process list
if not self.parser.has_section('processlist'):
self.parser.add_section('processlist')
self.parser.set('cpu', 'careful', '50')
self.parser.set('cpu', 'warning', '70')
self.parser.set('cpu', 'critical', '90')
self.parser.set('mem', 'careful', '50')
self.parser.set('mem', 'warning', '70')
self.parser.set('mem', 'critical', '90')
@property
def loaded_config_file(self):
"""Return the loaded configuration file."""
return self._loaded_config_file
def items(self, section):
"""Return the items list of a section."""
return self.parser.items(section)
@ -145,20 +225,16 @@ class Config(object):
"""Return info about the existence of a section."""
return self.parser.has_section(section)
def get_option(self, section, option):
def get_value(self, section, option, default=None):
"""Get the value of an option, if it exists."""
try:
return self.parser.get(section, option)
except NoOptionError:
return default
def get_float_value(self, section, option, default=0.0):
"""Get the float value of an option, if it exists."""
try:
value = self.parser.getfloat(section, option)
return self.parser.getfloat(section, option)
except NoOptionError:
return
else:
return value
def get_raw_option(self, section, option):
"""Get the raw value of an option, if it exists."""
try:
value = self.parser.get(section, option)
except NoOptionError:
return
else:
return value
return float(default)

View File

@ -60,6 +60,5 @@ def get_locale_path(paths):
# i18n
gettext_domain = appname
i18n_path = os.path.realpath(os.path.join(work_path, '..', '..', 'i18n'))
user_i18n_path = os.path.join(os.path.expanduser('~/.local'), 'share', 'locale')
sys_i18n_path = os.path.join(sys_prefix, 'share', 'locale')
locale_dir = get_locale_path([i18n_path, user_i18n_path, sys_i18n_path])
locale_dir = get_locale_path([i18n_path, sys_i18n_path])

View File

@ -49,11 +49,12 @@ class MonitorList(object):
__monitor_list = []
def __init__(self, config):
"""Init the monitoring list from the configuration file."""
"""Init the monitoring list from the configuration file, if it exists."""
self.config = config
if self.config is not None and self.config.has_section('monitor'):
# Process monitoring list
logger.debug("Monitor list configuration detected")
self.__set_monitor_list('monitor', 'list')
else:
self.__monitor_list = []
@ -67,11 +68,11 @@ class MonitorList(object):
value = {}
key = "list_" + str(l) + "_"
try:
description = self.config.get_raw_option(section, key + "description")
regex = self.config.get_raw_option(section, key + "regex")
command = self.config.get_raw_option(section, key + "command")
countmin = self.config.get_raw_option(section, key + "countmin")
countmax = self.config.get_raw_option(section, key + "countmax")
description = self.config.get_value(section, key + 'description')
regex = self.config.get_value(section, key + 'regex')
command = self.config.get_value(section, key + 'command')
countmin = self.config.get_value(section, key + 'countmin')
countmax = self.config.get_value(section, key + 'countmax')
except Exception as e:
logger.error("Cannot read monitored list: {0}".format(e))
else:

View File

@ -54,7 +54,7 @@ class GlancesStaticServer(object):
postfix = 'server_%s_' % str(i)
# Read the server name (mandatory)
for s in ['name', 'port', 'alias']:
new_server[s] = config.get_raw_option(self._section, '%s%s' % (postfix, s))
new_server[s] = config.get_value(self._section, '%s%s' % (postfix, s))
if new_server['name'] is not None:
# Manage optionnal information
if new_server['port'] is None:

View File

@ -61,11 +61,11 @@ class Export(GlancesExport):
if self.config is None:
return False
try:
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")
self.host = self.config.get_value(section, 'host')
self.port = self.config.get_value(section, 'port')
self.user = self.config.get_value(section, 'user')
self.password = self.config.get_value(section, 'password')
self.db = self.config.get_value(section, 'db')
except NoSectionError:
logger.critical("No InfluxDB configuration found")
return False
@ -76,7 +76,7 @@ class Export(GlancesExport):
logger.debug("Load InfluxDB from the Glances configuration file")
# Prefix is optional
try:
self.prefix = self.config.get_raw_option(section, "prefix")
self.prefix = self.config.get_value(section, 'prefix')
except NoOptionError as e:
pass
return True

View File

@ -62,11 +62,11 @@ class Export(GlancesExport):
if self.config is None:
return False
try:
self.rabbitmq_host = self.config.get_raw_option(section, "host")
self.rabbitmq_port = self.config.get_raw_option(section, "port")
self.rabbitmq_user = self.config.get_raw_option(section, "user")
self.rabbitmq_password = self.config.get_raw_option(section, "password")
self.rabbitmq_queue = self.config.get_raw_option(section, "queue")
self.rabbitmq_host = self.config.get_value(section, 'host')
self.rabbitmq_port = self.config.get_value(section, 'port')
self.rabbitmq_user = self.config.get_value(section, 'user')
self.rabbitmq_password = self.config.get_value(section, 'password')
self.rabbitmq_queue = self.config.get_value(section, 'queue')
except NoSectionError:
logger.critical("No rabbitmq configuration found")
return False

View File

@ -64,8 +64,8 @@ class Export(GlancesExport):
if self.config is None:
return False
try:
self.host = self.config.get_raw_option(section, "host")
self.port = self.config.get_raw_option(section, "port")
self.host = self.config.get_value(section, 'host')
self.port = self.config.get_value(section, 'port')
except NoSectionError:
logger.critical("No Statsd configuration found")
return False
@ -76,7 +76,7 @@ class Export(GlancesExport):
logger.debug("Load Statsd from the Glances configuration file")
# Prefix is optional
try:
self.prefix = self.config.get_raw_option(section, "prefix")
self.prefix = self.config.get_value(section, 'prefix')
except NoOptionError as e:
pass
return True

View File

@ -61,7 +61,7 @@ class Plugin(GlancesPlugin):
# Configuration file path
try:
msg = '{0}: {1}'.format(_("Configuration file"), self.config.get_loaded_config_file())
msg = '{0}: {1}'.format(_("Configuration file"), self.config.loaded_config_file)
except AttributeError:
pass
else:

View File

@ -41,8 +41,7 @@ class Plugin(GlancesPlugin):
self.stats = []
def load_limits(self, config):
"""Load the monitored list from the conf file."""
logger.debug("Monitor plugin configuration detected in the configuration file")
"""Load the monitored list from the config file, if it exists."""
self.glances_monitors = glancesMonitorList(config)
def update(self):

View File

@ -327,16 +327,16 @@ class GlancesPlugin(object):
return item_views[key][option]
def load_limits(self, config):
"""Load the limits from the configuration file."""
"""Load limits from the configuration file, if it exists."""
if (hasattr(config, 'has_section') and
config.has_section(self.plugin_name)):
for level, v in config.items(self.plugin_name):
# Read limits
limit = '_'.join([self.plugin_name, level])
try:
self._limits[limit] = config.get_option(self.plugin_name, level)
self._limits[limit] = config.get_float_value(self.plugin_name, level)
except ValueError:
self._limits[limit] = config.get_raw_option(self.plugin_name, level).split(",")
self._limits[limit] = config.get_value(self.plugin_name, level).split(",")
logger.debug("Load limit: {0} = {1}".format(limit, self._limits[limit]))
@property

View File

@ -6,12 +6,12 @@ import sys
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 = [
('share/doc/glances', ['AUTHORS', 'COPYING', 'NEWS', 'README.rst',
@ -20,25 +20,8 @@ def get_data_files():
('share/man/man1', ['man/glances.1'])
]
if hasattr(sys, 'real_prefix'): # virtualenv
conf_path = os.path.join(sys.prefix, 'etc', 'glances')
elif os.name == 'posix' and (os.getuid() == 0 or is_chroot):
# Unix-like + root privileges/chroot environment
if 'bsd' in sys.platform:
conf_path = os.path.join(sys.prefix, 'etc', 'glances')
elif 'linux' in sys.platform:
conf_path = os.path.join('/etc', 'glances')
elif 'darwin' in sys.platform:
conf_path = os.path.join('/usr/local', 'etc', 'glances')
elif 'win32' in sys.platform: # windows
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'):
data_files.append(
(os.path.dirname(mo).replace('i18n/', 'share/locale/'), [mo]))
data_files.append((os.path.dirname(mo).replace('i18n/', 'share/locale/'), [mo]))
return data_files