mirror of
https://github.com/nicolargo/glances.git
synced 2024-11-25 08:33:22 +03:00
First run for the Nginx AMP process
This commit is contained in:
parent
b5e59d3e3a
commit
8efb5e2ac8
@ -155,7 +155,7 @@ mem_careful=50
|
||||
mem_warning=70
|
||||
mem_critical=90
|
||||
|
||||
#[monitor]
|
||||
[monitor]
|
||||
# Define the list of processes to monitor
|
||||
# *** This section is optional ***
|
||||
# The list is composed of items (list_#nb <= 10)
|
||||
@ -181,7 +181,7 @@ mem_critical=90
|
||||
#list_3_regex=.*xeyes.*
|
||||
#list_3_countmin=1
|
||||
|
||||
#[serverlist]
|
||||
[serverlist]
|
||||
# Define the static servers list
|
||||
#server_1_name=localhost
|
||||
#server_1_alias=My local PC
|
||||
@ -194,7 +194,7 @@ mem_critical=90
|
||||
#server_4_name=pasbon
|
||||
#server_4_port=61237
|
||||
|
||||
#[passwords]
|
||||
[passwords]
|
||||
# Define the passwords list
|
||||
# Syntax: host=password
|
||||
# Where: host is the hostname
|
||||
@ -250,3 +250,13 @@ user=guest
|
||||
password=guest
|
||||
queue=glances_queue
|
||||
|
||||
######
|
||||
# AMPS
|
||||
######
|
||||
|
||||
[nginx]
|
||||
# Nginx status page should be enable (https://easyengine.io/tutorials/nginx/status-page/)
|
||||
enable=true
|
||||
regex=\/usr\/sbin\/nginx
|
||||
refresh=60
|
||||
status_url=http://localhost/nginx_status
|
||||
|
0
glances/amps/__init__.py
Normal file
0
glances/amps/__init__.py
Normal file
116
glances/amps/glances_amp.py
Normal file
116
glances/amps/glances_amp.py
Normal file
@ -0,0 +1,116 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
# Copyright (C) 2016 Nicolargo <nicolas@nicolargo.com>
|
||||
#
|
||||
# Glances is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Glances is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
"""
|
||||
I am your father...
|
||||
|
||||
...for all Glances Application Monitoring Processes (AMP).
|
||||
"""
|
||||
|
||||
from glances.compat import u
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
class GlancesAmp(object):
|
||||
|
||||
"""Main class for Glances AMP."""
|
||||
|
||||
def __init__(self, args=None):
|
||||
"""Init AMP classe."""
|
||||
# AMP name (= module name without glances_)
|
||||
self.amp_name = self.__class__.__module__[len('glances_'):]
|
||||
|
||||
# Init the args
|
||||
self.args = args
|
||||
|
||||
# Init the configs
|
||||
self.configs = {}
|
||||
|
||||
def load_config(self, config):
|
||||
"""Load AMP parameters from the configuration file."""
|
||||
|
||||
# Read AMP confifuration.
|
||||
# For ex, the AMP foo should have the following section:
|
||||
#
|
||||
# [foo]
|
||||
# enable=true
|
||||
# regex=\/usr\/bin\/nginx
|
||||
# refresh=60
|
||||
#
|
||||
# and optionnaly:
|
||||
#
|
||||
# option1=opt1
|
||||
#
|
||||
if (hasattr(config, 'has_section') and
|
||||
config.has_section(self.amp_name)):
|
||||
logger.debug("AMP: Load {0} configuration".format(self.amp_name))
|
||||
for param, _ in config.items(self.amp_name):
|
||||
try:
|
||||
self.configs[param] = config.get_float_value(self.amp_name, param)
|
||||
except ValueError:
|
||||
self.configs[param] = config.get_value(self.amp_name, param).split(',')
|
||||
if len(self.configs[param]) == 1:
|
||||
self.configs[param] = self.configs[param][0]
|
||||
logger.debug("AMP: Load {0} parameter: {1} = {2}".format(self.amp_name, param, self.configs[param]))
|
||||
else:
|
||||
logger.warning("AMP: Can not find section {0} in the configuration file".format(self.amp_name))
|
||||
|
||||
# enable, regex and refresh are mandatories
|
||||
# if not configured then AMP is disabled
|
||||
for k in ['enable', 'regex', 'refresh']:
|
||||
if k not in self.configs:
|
||||
logger.warning("AMP: Can not find configuration key {0} in section {1}".format(k, self.amp_name))
|
||||
self.configs['enable'] = 'false'
|
||||
|
||||
if not self.enable():
|
||||
logger.warning("AMP: {0} is disabled".format(self.amp_name))
|
||||
|
||||
def get(self, key):
|
||||
"""Generic method to get the item in the AMP configuration"""
|
||||
if key in self.configs:
|
||||
return self.configs[key]
|
||||
else:
|
||||
return None
|
||||
|
||||
def enable(self):
|
||||
"""Return True|False if the AMP is enabled in the configuration file (enable=true|false)."""
|
||||
return self.get('enable').lower().startswith('true')
|
||||
|
||||
def regex(self):
|
||||
"""Return regular expression used to identified the current application."""
|
||||
return self.get('regex')
|
||||
|
||||
def refresh(self):
|
||||
"""Return refresh time in seconds for the current application monitoring process."""
|
||||
return self.get('refresh')
|
||||
|
||||
def should_update(self):
|
||||
"""Return True is the AMP should be updated:
|
||||
- AMP is enable
|
||||
- only update every 'refresh' seconds
|
||||
"""
|
||||
return True
|
||||
|
||||
def set_result(self, result):
|
||||
"""Store the result (string) into the result key of the AMP"""
|
||||
self.configs['result'] = str(result)
|
||||
|
||||
def result(self):
|
||||
""" Return the result of the AMP (as a string)"""
|
||||
return u(self.get('result'))
|
48
glances/amps/glances_nginx.py
Normal file
48
glances/amps/glances_nginx.py
Normal file
@ -0,0 +1,48 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
# Copyright (C) 2016 Nicolargo <nicolas@nicolargo.com>
|
||||
#
|
||||
# Glances is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Glances is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
"""Nginx AMP."""
|
||||
|
||||
import requests
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.amps.glances_amp import GlancesAmp
|
||||
|
||||
|
||||
class Amp(GlancesAmp):
|
||||
|
||||
"""Glances' Nginx AMP."""
|
||||
|
||||
def __init__(self, args=None):
|
||||
"""Init the AMP."""
|
||||
super(Amp, self).__init__(args=args)
|
||||
|
||||
def update(self):
|
||||
"""Update the AMP"""
|
||||
|
||||
if self.should_update():
|
||||
logger.debug('AMPS: Update {0} using status URL {1}'.format(self.amp_name, self.get('status_url')))
|
||||
req = requests.get(self.get('status_url'))
|
||||
if req.ok:
|
||||
# u'Active connections: 1 \nserver accepts handled requests\n 1 1 1 \nReading: 0 Writing: 1 Waiting: 0 \n'
|
||||
self.set_result(req.text)
|
||||
else:
|
||||
logger.debug('AMPS: Can not grab status URL {0} ({1})'.format(self.get('status_url'), req.reason))
|
||||
|
||||
return self.result()
|
117
glances/amps_list.py
Normal file
117
glances/amps_list.py
Normal file
@ -0,0 +1,117 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
# Copyright (C) 2016 Nicolargo <nicolas@nicolargo.com>
|
||||
#
|
||||
# Glances is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Glances is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
"""Manage the AMPs list."""
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
from glances.compat import listkeys, iteritems
|
||||
from glances.logger import logger
|
||||
from glances.globals import amps_path
|
||||
from glances.processes import glances_processes
|
||||
|
||||
|
||||
class AmpsList(object):
|
||||
|
||||
"""This class describes the optional application process monitoring list.
|
||||
|
||||
The AMP list is a list of processes with a specific monitoring action.
|
||||
|
||||
The list (Python list) is composed of items (Python dict).
|
||||
An item is defined (dict keys):
|
||||
*...
|
||||
"""
|
||||
|
||||
# The dict
|
||||
__amps_dict = {}
|
||||
|
||||
def __init__(self, args, config):
|
||||
"""Init the AMPs list."""
|
||||
self.args = args
|
||||
self.config = config
|
||||
|
||||
# Create the AMPS list
|
||||
self.load_amps()
|
||||
self.load_configs()
|
||||
|
||||
def load_amps(self):
|
||||
"""Load all amps in the 'amps' folder."""
|
||||
header = "glances_"
|
||||
for item in os.listdir(amps_path):
|
||||
if (item.startswith(header) and
|
||||
item.endswith(".py") and
|
||||
item != (header + "amp.py")):
|
||||
# Import the amp
|
||||
amp = __import__(os.path.basename(item)[:-3])
|
||||
# Add the AMP to the dictionary
|
||||
# The key is the AMP name
|
||||
# for example, the file glances_xxx.py
|
||||
# generate self._amps_list["xxx"] = ...
|
||||
amp_name = os.path.basename(item)[len(header):-3].lower()
|
||||
self.__amps_dict[amp_name] = amp.Amp(self.args)
|
||||
# Log AMPs list
|
||||
logger.debug("Available AMPs list: {0}".format(self.getList()))
|
||||
|
||||
def load_configs(self):
|
||||
"""Load the AMP configuration files."""
|
||||
# For each AMPs, call the load_config method
|
||||
for a in self.get():
|
||||
self.get()[a].load_config(self.config)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.__amps_dict)
|
||||
|
||||
def __repr__(self):
|
||||
return self.__amps_dict
|
||||
|
||||
def __getitem__(self, item):
|
||||
return self.__amps_dict[item]
|
||||
|
||||
def __len__(self):
|
||||
return len(self.__amps_dict)
|
||||
|
||||
def update(self):
|
||||
"""Update the command result attributed."""
|
||||
# Search application monitored processes by a regular expression
|
||||
processlist = [p for p in glances_processes.getalllist()]
|
||||
|
||||
# Iter upon the AMPs dict
|
||||
for k, v in iteritems(self.get()):
|
||||
amps_list = [p for p in processlist for c in p['cmdline'] if re.search(v.regex(), c) is not None]
|
||||
if len(amps_list) > 0:
|
||||
# At least one process is matching the regex
|
||||
logger.debug("AMPS: {} process detected (PID={})".format(k, amps_list[0]['pid']))
|
||||
# Call the AMP update method
|
||||
v.update()
|
||||
|
||||
return self.__amps_dict
|
||||
|
||||
def getList(self):
|
||||
"""Return the AMPs list."""
|
||||
return listkeys(self.__amps_dict)
|
||||
|
||||
def get(self):
|
||||
"""Return the AMPs dict."""
|
||||
return self.__amps_dict
|
||||
|
||||
def set(self, new_dict):
|
||||
"""Set the AMPs dict."""
|
||||
self.__amps_dict = new_dict
|
@ -34,9 +34,11 @@ work_path = os.path.realpath(os.path.dirname(__file__))
|
||||
appname_path = os.path.split(sys.argv[0])[0]
|
||||
sys_prefix = os.path.realpath(os.path.dirname(appname_path))
|
||||
|
||||
# Set the plugins and export modules path
|
||||
# Set the AMPs, plugins and export modules path
|
||||
amps_path = os.path.realpath(os.path.join(work_path, 'amps'))
|
||||
plugins_path = os.path.realpath(os.path.join(work_path, 'plugins'))
|
||||
exports_path = os.path.realpath(os.path.join(work_path, 'exports'))
|
||||
sys_path = sys.path[:]
|
||||
sys.path.insert(1, exports_path)
|
||||
sys.path.insert(1, plugins_path)
|
||||
sys.path.insert(1, amps_path)
|
||||
|
@ -535,6 +535,8 @@ class _GlancesCurses(object):
|
||||
'processcount').get_stats_display(args=self.args)
|
||||
stats_monitor = stats.get_plugin(
|
||||
'monitor').get_stats_display(args=self.args)
|
||||
stats_amps = stats.get_plugin(
|
||||
'amps').get_stats_display(args=self.args)
|
||||
stats_alert = stats.get_plugin(
|
||||
'alert').get_stats_display(args=self.args)
|
||||
|
||||
@ -717,12 +719,14 @@ class _GlancesCurses(object):
|
||||
self.next_line = self.saved_line
|
||||
|
||||
# Display right sidebar
|
||||
# ((DOCKER)+PROCESS_COUNT+(MONITORED)+PROCESS_LIST+ALERT)
|
||||
# ((DOCKER)+PROCESS_COUNT+(MONITORED)+(AMPS)+PROCESS_LIST+ALERT)
|
||||
self.new_column()
|
||||
self.new_line()
|
||||
self.display_plugin(stats_docker)
|
||||
self.new_line()
|
||||
self.display_plugin(stats_processcount)
|
||||
self.new_line()
|
||||
self.display_plugin(stats_amps)
|
||||
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()
|
||||
|
89
glances/plugins/glances_amps.py
Normal file
89
glances/plugins/glances_amps.py
Normal file
@ -0,0 +1,89 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
# Copyright (C) 2016 Nicolargo <nicolas@nicolargo.com>
|
||||
#
|
||||
# Glances is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Glances is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
"""Monitor plugin."""
|
||||
|
||||
from glances.compat import iteritems
|
||||
from glances.amps_list import AmpsList as glancesAmpsList
|
||||
from glances.plugins.glances_plugin import GlancesPlugin
|
||||
|
||||
|
||||
class Plugin(GlancesPlugin):
|
||||
|
||||
"""Glances AMPs plugin."""
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
super(Plugin, self).__init__(args=args)
|
||||
self.args = args
|
||||
self.config = config
|
||||
|
||||
# We want to display the stat in the curse interface
|
||||
self.display_curse = True
|
||||
|
||||
# Init the list of AMP (classe define in the glances/amps_list.py script)
|
||||
self.glances_amps = glancesAmpsList(self.args, self.config)
|
||||
|
||||
# Init stats
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
"""Reset/init the stats."""
|
||||
self.stats = []
|
||||
|
||||
@GlancesPlugin._log_result_decorator
|
||||
def update(self):
|
||||
"""Update the AMP list."""
|
||||
# Reset stats
|
||||
self.reset()
|
||||
|
||||
if self.input_method == 'local':
|
||||
# TODO
|
||||
for k, v in iteritems(self.glances_amps.update()):
|
||||
self.stats.append({k: v.result()})
|
||||
else:
|
||||
pass
|
||||
|
||||
return self.stats
|
||||
|
||||
def msg_curse(self, args=None):
|
||||
"""Return the dict to display in the curse interface."""
|
||||
# Init the return message
|
||||
# Only process if stats exist and display plugin enable...
|
||||
ret = []
|
||||
|
||||
if not self.stats or args.disable_process:
|
||||
return ret
|
||||
|
||||
# Build the string message
|
||||
for m in self.stats:
|
||||
for k, v in iteritems(m):
|
||||
msg = '{0:<16} '.format(k)
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{0}'.format(v.replace('\n', ''))
|
||||
ret.append(self.curse_add_line(msg, splittable=True))
|
||||
ret.append(self.curse_new_line())
|
||||
|
||||
# Delete the last empty line
|
||||
try:
|
||||
ret.pop()
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
return ret
|
@ -403,8 +403,6 @@ class GlancesPlugin(object):
|
||||
except KeyError:
|
||||
return 'DEFAULT'
|
||||
|
||||
logger.debug("{0} => ret = {1}".format(stat_name, ret))
|
||||
|
||||
# Manage log
|
||||
log_str = ""
|
||||
if self.__get_limit_log(stat_name=stat_name, default_action=log):
|
||||
|
@ -33,16 +33,16 @@ class GlancesStats(object):
|
||||
"""This class stores, updates and gives stats."""
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
# Set the argument instance
|
||||
self.args = args
|
||||
|
||||
# Set the config instance
|
||||
self.config = config
|
||||
|
||||
# Load plugins and export modules
|
||||
self.load_plugins_and_exports(self.args)
|
||||
# Set the argument instance
|
||||
self.args = args
|
||||
|
||||
# Load the limits
|
||||
# Load plugins and exports modules
|
||||
self.load_modules(self.args)
|
||||
|
||||
# Load the limits (for plugins)
|
||||
self.load_limits(config)
|
||||
|
||||
def __getattr__(self, item):
|
||||
@ -67,8 +67,9 @@ class GlancesStats(object):
|
||||
# Default behavior
|
||||
raise AttributeError(item)
|
||||
|
||||
def load_plugins_and_exports(self, args):
|
||||
"""Wrapper to load both plugins and export modules."""
|
||||
def load_modules(self, args):
|
||||
"""Wrapper to load: plugins and export modules."""
|
||||
|
||||
# Init the plugins dict
|
||||
self._plugins = collections.defaultdict(dict)
|
||||
# Load the plugins
|
||||
@ -96,7 +97,7 @@ class GlancesStats(object):
|
||||
# for example, the file glances_xxx.py
|
||||
# generate self._plugins_list["xxx"] = ...
|
||||
plugin_name = os.path.basename(item)[len(header):-3].lower()
|
||||
if plugin_name == 'help':
|
||||
if plugin_name in ('help', 'amps'):
|
||||
self._plugins[plugin_name] = plugin.Plugin(args=args, config=self.config)
|
||||
else:
|
||||
self._plugins[plugin_name] = plugin.Plugin(args=args)
|
||||
@ -136,13 +137,12 @@ class GlancesStats(object):
|
||||
|
||||
def getExportList(self):
|
||||
"""Return the exports modules list."""
|
||||
return [p for p in self._exports]
|
||||
return [e for e in self._exports]
|
||||
|
||||
def load_limits(self, config=None):
|
||||
"""Load the stats limits."""
|
||||
# For each plugins, call the init_limits method
|
||||
# For each plugins, call the load_limits method
|
||||
for p in self._plugins:
|
||||
# logger.debug("Load limits for %s" % p)
|
||||
self._plugins[p].load_limits(config)
|
||||
|
||||
def update(self):
|
||||
|
@ -40,8 +40,8 @@ class GlancesStatsClient(GlancesStats):
|
||||
# Init the arguments
|
||||
self.args = args
|
||||
|
||||
# Load plugins and exports
|
||||
self.load_plugins_and_exports(self.args)
|
||||
# Load AMPs, plugins and exports modules
|
||||
self.load_modules(self.args)
|
||||
|
||||
def set_plugins(self, input_plugins):
|
||||
"""Set the plugin list according to the Glances server."""
|
||||
|
@ -51,8 +51,8 @@ class GlancesStatsClientSNMP(GlancesStats):
|
||||
# OS name is used because OID is differents between system
|
||||
self.os_name = None
|
||||
|
||||
# Load plugins and export modules
|
||||
self.load_plugins_and_exports(self.args)
|
||||
# Load AMPs, plugins and exports modules
|
||||
self.load_modules(self.args)
|
||||
|
||||
def check_snmp(self):
|
||||
"""Chek if SNMP is available on the server."""
|
||||
|
Loading…
Reference in New Issue
Block a user