提交 43183615 编写于 作者: A Alessio Sergi

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.
上级 9a879245
......@@ -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)
......
[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
......@@ -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
--------
You can put the configuration file ``glances.conf`` in the following
locations:
:Linux: ``/etc/glances/glances.conf``
:\*BSD and OS X: ``/usr/local/etc/glances/glances.conf``
:Windows: ``%APPDATA%\glances\glances.conf``
: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.
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/
User-specific options override system-wide options and options given on
the command line override either.
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::
......
......@@ -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
def get_loaded_config_file(self):
"""Return the loaded configuration file"""
return self._loaded_config_file
self.parser = ConfigParser()
self.read()
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):
"""Get the float value of an option, if it exists."""
def get_value(self, section, option, default=None):
"""Get the value of an option, if it exists."""
try:
value = self.parser.getfloat(section, option)
return self.parser.get(section, option)
except NoOptionError:
return
else:
return value
return default
def get_raw_option(self, section, option):
"""Get the raw value of an option, if it exists."""
def get_float_value(self, section, option, default=0.0):
"""Get the float value of an option, if it exists."""
try:
value = self.parser.get(section, option)
return self.parser.getfloat(section, option)
except NoOptionError:
return
else:
return value
return float(default)
......@@ -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])
......@@ -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:
......
......@@ -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:
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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:
......
......@@ -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):
......
......@@ -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
......
......@@ -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
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册