提交 16218325 编写于 作者: N nicolargo

Almost finished. Just had to correct an issue on the WebUI displaying AMP with empty result

上级 9999f558
##############################################################################
# plugins
##############################################################################
[quicklook]
# Define CPU, MEM and SWAP thresholds in %
cpu_careful=50
......@@ -156,31 +160,9 @@ 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_2_description=Python programs
list_2_regex=.*python.*
list_3_description=Famous Xeyes
list_3_regex=.*xeyes.*
list_3_countmin=1
##############################################################################
# Client/server
##############################################################################
[serverlist]
# Define the static servers list
......@@ -204,6 +186,10 @@ list_3_countmin=1
#xps=abc
#default=defaultpassword
##############################################################################
# Exports
##############################################################################
[influxdb]
# Configuration for the --export-influxdb option
# https://influxdb.com/
......@@ -251,7 +237,7 @@ user=guest
password=guest
queue=glances_queue
######
##############################################################################
# AMPS
# * enable: Enable (true) or disable (false) the AMP
# * regex: Regular expression to filter the process(es)
......@@ -263,7 +249,7 @@ queue=glances_queue
# * countmax: (optional) maximum number of processes
# A warning will be displayed if number of process > count
# * <foo>: Others variables can be defined and used in the AMP script
######
##############################################################################
[amp_dropbox]
# Use the default AMP (no dedicated AMP Python script)
......
......@@ -181,7 +181,6 @@ class GlancesClient(object):
# Update the stats
try:
server_stats = json.loads(self.client.getAll())
server_stats['monitor'] = json.loads(self.client.getAllMonitored())
except socket.error:
# Client cannot get server stats
return "Disconnected"
......
......@@ -138,8 +138,6 @@ Start the client browser (browser mode):\n\
help='disable network, disk I/O, FS and sensors modules')
parser.add_argument('--disable-process', action='store_true', default=False,
dest='disable_process', help='disable process module')
parser.add_argument('--disable-monitoring', action='store_true', default=False,
dest='disable_monitor', help='disable monitoring list module')
parser.add_argument('--disable-amp', action='store_true', default=False,
dest='disable_amp', help='disable applications monitoring process (AMP) module')
parser.add_argument('--disable-log', action='store_true', default=False,
......
# -*- coding: utf-8 -*-
#
# This file is part of Glances.
#
# Copyright (C) 2015 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 monitor list."""
import re
import subprocess
from glances.compat import range, u
from glances.logger import logger
from glances.processes import glances_processes
class MonitorList(object):
"""This class describes the optional monitored processes list.
The monitored list is a list of 'important' processes to monitor.
The list (Python list) is composed of items (Python dict).
An item is defined (dict keys):
* description: Description of the processes (max 16 chars)
* regex: regular expression of the processes to monitor
* command: (optional) shell command for extended stat
* countmin: (optional) minimal number of processes
* countmax: (optional) maximum number of processes
"""
# Maximum number of items in the list
__monitor_list_max_size = 10
# The list
__monitor_list = []
def __init__(self, config):
"""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 = []
def __set_monitor_list(self, section, key):
"""Init the monitored processes list.
The list is defined in the Glances configuration file.
"""
for l in range(1, self.__monitor_list_max_size + 1):
value = {}
key = "list_" + str(l) + "_"
try:
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:
if description is not None and regex is not None:
# Build the new item
value["description"] = description
try:
re.compile(regex)
except Exception:
continue
else:
value["regex"] = regex
value["command"] = command
value["countmin"] = countmin
value["countmax"] = countmax
value["count"] = None
value["result"] = None
# Add the item to the list
self.__monitor_list.append(value)
def __str__(self):
return str(self.__monitor_list)
def __repr__(self):
return self.__monitor_list
def __getitem__(self, item):
return self.__monitor_list[item]
def __len__(self):
return len(self.__monitor_list)
def __get__(self, item, key):
"""Meta function to return key value of item.
Return None if not defined or item > len(list)
"""
if item < len(self.__monitor_list):
try:
return self.__monitor_list[item][key]
except Exception:
return None
else:
return None
def update(self):
"""Update the command result attributed."""
# Only continue if monitor list is not empty
if len(self.__monitor_list) == 0:
return self.__monitor_list
# Search monitored processes by a regular expression
processlist = glances_processes.getalllist()
# Iter upon the monitored list
for i in range(len(self.get())):
monitoredlist = [p for p in processlist for c in p['cmdline'] if re.search(self.regex(i), c) is not None]
self.__monitor_list[i]['count'] = len(monitoredlist)
# Always get processes CPU and MEM
self.__monitor_list[i]['default_result'] = 'CPU: {0:.1f}% | MEM: {1:.1f}%'.format(
sum([p['cpu_percent'] for p in monitoredlist]),
sum([p['memory_percent'] for p in monitoredlist]))
if self.command(i) is not None:
# Execute the user command line
try:
self.__monitor_list[i]['result'] = subprocess.check_output(self.command(i),
shell=True)
except subprocess.CalledProcessError:
self.__monitor_list[i]['result'] = 'Error: ' + self.command(i)
except Exception:
self.__monitor_list[i]['result'] = 'Cannot execute command'
# Only save the first line
try:
self.__monitor_list[i]['result'] = u(self.__monitor_list[i]['result']).split('\n')[0]
except:
self.__monitor_list[i]['result'] = ''
if self.command(i) is None or self.__monitor_list[i]['result'] == '':
# If there is no command specified in the conf file
# then display CPU and MEM %
self.__monitor_list[i]['result'] = self.__monitor_list[i]['default_result']
return self.__monitor_list
def get(self):
"""Return the monitored list (list of dict)."""
return self.__monitor_list
def set(self, newlist):
"""Set the monitored list (list of dict)."""
self.__monitor_list = newlist
def getAll(self):
# Deprecated: use get()
return self.get()
def setAll(self, newlist):
# Deprecated: use set()
self.set(newlist)
def description(self, item):
"""Return the description of the item number (item)."""
return self.__get__(item, "description")
def regex(self, item):
"""Return the regular expression of the item number (item)."""
return self.__get__(item, "regex")
def command(self, item):
"""Return the stat command of the item number (item)."""
return self.__get__(item, "command")
def result(self, item):
"""Return the reult command of the item number (item)."""
return self.__get__(item, "result")
def countmin(self, item):
"""Return the minimum number of processes of the item number (item)."""
return self.__get__(item, "countmin")
def countmax(self, item):
"""Return the maximum number of processes of the item number (item)."""
return self.__get__(item, "countmax")
......@@ -425,7 +425,7 @@ class _GlancesCurses(object):
# 'x' > Delete finished warning and critical logs
glances_logs.clean(critical=True)
elif self.pressedkey == ord('z'):
# 'z' > Enable/Disable processes stats (count + list + monitor)
# 'z' > Enable/Disable processes stats (count + list + AMPs)
# Enable/Disable display
self.args.disable_process = not self.args.disable_process
# Enable/Disable update
......@@ -536,8 +536,6 @@ class _GlancesCurses(object):
args=self.args)
stats_processcount = stats.get_plugin(
'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(
......@@ -722,19 +720,15 @@ class _GlancesCurses(object):
self.next_line = self.saved_line
# Display right sidebar
# ((DOCKER)+PROCESS_COUNT+(MONITORED)+(AMPS)+PROCESS_LIST+ALERT)
# DOCKER+PROCESS_COUNT+AMPS+PROCESS_LIST+ALERT
self.new_column()
self.new_line()
self.display_plugin(stats_docker)
if glances_processes.process_filter is None:
# Do not display stats monitor list if a filter exist
self.new_line()
self.display_plugin(stats_monitor)
self.new_line()
self.display_plugin(stats_amps)
self.new_line()
self.display_plugin(stats_processcount)
self.new_line()
self.display_plugin(stats_amps)
self.new_line()
self.display_plugin(stats_processlist,
display_optional=(screen_x > 102),
display_additional=(not OSX),
......
......@@ -133,7 +133,7 @@ body {
width: 100%;
text-overflow: ellipsis;
}
#monitor .process-result {
#amps .process-result {
max-width: 300px;
overflow: hidden;
white-space: nowrap;
......
......@@ -31,7 +31,7 @@
<script type="text/javascript" src="services/plugins/glances_load.js"></script>
<script type="text/javascript" src="services/plugins/glances_mem.js"></script>
<script type="text/javascript" src="services/plugins/glances_memswap.js"></script>
<script type="text/javascript" src="services/plugins/glances_monitor.js"></script>
<script type="text/javascript" src="services/plugins/glances_amps.js"></script>
<script type="text/javascript" src="services/plugins/glances_network.js"></script>
<script type="text/javascript" src="services/plugins/glances_percpu.js"></script>
<script type="text/javascript" src="services/plugins/glances_processcount.js"></script>
......
<div class="table">
<div class="table-row" ng-repeat="process in statsAmps.processes">
<div class="table-cell" ng-class="statsAmps.getDescriptionDecoration(process)">{{ process.name }}</div>
<div class="table-cell">{{ process.count }}</div>
<div class="table-cell process-result">{{ process.result }}</div>
</div>
</div>
<div class="table">
<div class="table-row" ng-repeat="process in statsMonitor.processes">
<div class="table-cell" ng-class="statsMonitor.getDescriptionDecoration(process)">{{ process.description }}</div>
<div class="table-cell">{{ process.count > 1 ? process.count : '' }}</div>
<div class="table-cell">{{ process.count > 0 ? 'RUNNING' : 'NOT RUNNING' }}</div>
<div class="table-cell process-result">{{ process.result }}</div>
</div>
</div>
......@@ -61,7 +61,7 @@
<section id="processcount" class="plugin" ng-include src="'plugins/processcount.html'"></section>
<div class="row">
<div class="col-lg-18">
<section id="monitor" class="plugin" ng-include src="'plugins/monitor.html'"></section>
<section id="amps" class="plugin" ng-include src="'plugins/amps.html'"></section>
</div>
</div>
<section id="processlist" class="plugin" ng-include src="'plugins/processlist.html'"></section>
......
......@@ -12,7 +12,7 @@ glancesApp.service('GlancesStats', function($http, $injector, $q, GlancesPlugin)
'load': 'GlancesPluginLoad',
'mem': 'GlancesPluginMem',
'memswap': 'GlancesPluginMemSwap',
'monitor': 'GlancesPluginMonitor',
'amps': 'GlancesPluginAmps',
'network': 'GlancesPluginNetwork',
'percpu': 'GlancesPluginPerCpu',
'processcount': 'GlancesPluginProcessCount',
......
glancesApp.service('GlancesPluginMonitor', function() {
var _pluginName = "monitor";
glancesApp.service('GlancesPluginAmps', function() {
var _pluginName = "amps";
this.processes = [];
this.setData = function(data, views) {
......
......@@ -31,7 +31,7 @@ glancesApp.controller('statsController', function ($scope, $rootScope, $interval
$scope.statsLoad = GlancesStats.getPlugin('load');
$scope.statsMem = GlancesStats.getPlugin('mem');
$scope.statsMemSwap = GlancesStats.getPlugin('memswap');
$scope.statsMonitor = GlancesStats.getPlugin('monitor');
$scope.statsAmps = GlancesStats.getPlugin('amps');
$scope.statsNetwork = GlancesStats.getPlugin('network');
$scope.statsPerCpu = GlancesStats.getPlugin('percpu');
$scope.statsProcessCount = GlancesStats.getPlugin('processcount');
......
# -*- coding: utf-8 -*-
#
# This file is part of Glances.
#
# Copyright (C) 2015 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 u
from glances.monitor_list import MonitorList as glancesMonitorList
from glances.logger import logger
from glances.plugins.glances_plugin import GlancesPlugin
class Plugin(GlancesPlugin):
"""Glances monitor plugin."""
def __init__(self, args=None):
"""Init the plugin."""
super(Plugin, self).__init__(args=args)
# We want to display the stat in the curse interface
self.display_curse = True
# Init stats
self.glances_monitors = None
self.stats = []
def load_limits(self, config):
"""Load the monitored list from the config file, if it exists."""
self.glances_monitors = glancesMonitorList(config)
@GlancesPlugin._log_result_decorator
def update(self):
"""Update the monitored list."""
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:
return self.stats
# Update the monitored list (result of command)
self.glances_monitors.update()
# Put it on the stats var
self.stats = self.glances_monitors.get()
else:
pass
return self.stats
def get_alert(self, nbprocess=0, countmin=None, countmax=None, header="", log=False):
"""Return the alert status relative to the process number."""
if nbprocess is None:
return 'OK'
if countmin is None:
countmin = nbprocess
if countmax is None:
countmax = nbprocess
if nbprocess > 0:
if int(countmin) <= int(nbprocess) <= int(countmax):
return 'OK'
else:
return 'WARNING'
else:
if int(countmin) == 0:
return 'OK'
else:
return 'CRITICAL'
def msg_curse(self, args=None):
"""Return the dict to display in the curse interface."""
# Init the return message
ret = []
# Only process if stats exist and display plugin enable...
if not self.stats or args.disable_process or args.disable_monitor:
return ret
# Build the string message
for m in self.stats:
msg = '{0:<16} '.format(m['description'])
ret.append(self.curse_add_line(
msg, self.get_alert(m['count'], m['countmin'], m['countmax'])))
msg = '{0:<3} '.format(m['count'] if m['count'] > 1 else '')
ret.append(self.curse_add_line(msg))
msg = '{0:13} '.format('RUNNING' if m['count'] >= 1 else 'NOT RUNNING')
ret.append(self.curse_add_line(msg))
# Decode to UTF-8 (for Python 2)
try:
msg = u(m['result']) if m['count'] >= 1 else ''
except UnicodeEncodeError:
# Hack if return message contains non UTF-8 compliant char
msg = u(m['default_result']) if m['count'] >= 1 else ''
ret.append(self.curse_add_line(msg, optional=True, splittable=True))
ret.append(self.curse_new_line())
# Delete the last empty line
try:
ret.pop()
except IndexError:
pass
return ret
......@@ -510,7 +510,7 @@ class GlancesProcesses(object):
# Next...
first = False
# Build the all processes list used by the monitored list
# Build the all processes list used by the AMPs
self.allprocesslist = [p for p in itervalues(processdict)]
# Clean internals caches if timeout is reached
......
......@@ -156,11 +156,6 @@ class GlancesInstance(object):
# Return all the plugins views
return json.dumps(self.stats.getAllViewsAsDict())
def getAllMonitored(self):
# Return the processes monitored list
# return json.dumps(self.monitors.getAll())
return json.dumps(self.stats.getAll()['monitor'])
def __getattr__(self, item):
"""Overwrite the getattr method in case of attribute is not found.
......
......@@ -93,8 +93,8 @@ class TestGlances(unittest.TestCase):
self.assertTrue(req.ok)
if p in ('uptime', 'now'):
self.assertIsInstance(req.json(), text_type)
elif p in ('fs', 'monitor', 'percpu', 'sensors', 'alert', 'processlist',
'diskio', 'hddtemp', 'batpercent', 'network', 'folders', 'amps'):
elif p in ('fs', 'percpu', 'sensors', 'alert', 'processlist', 'diskio',
'hddtemp', 'batpercent', 'network', 'folders', 'amps'):
self.assertIsInstance(req.json(), list)
elif p in ('psutilversion', 'help'):
pass
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册