提交 baf79e9d 编写于 作者: N nicolargo

Merge branch 'issue937' into develop

......@@ -5,10 +5,11 @@ Glances Version 2
Version 2.8
===========
Enhancements and new features:
Enhancements and news features:
* Add ZeroMQ exporter (issue #939)
* Add CouchDB exporter (issue #928)
* Add hotspot Wifi informations (issue #937)
* Highlight max stats in the processes list (issue #878)
* Docker alerts and actions (issue #875)
* Glances API returns the processes PPID (issue #926)
......
......@@ -51,6 +51,7 @@ Optional dependencies:
- ``bernhard`` (for the Riemann export module)
- ``py-cpuinfo`` (for the Quicklook CPU info module)
- ``scandir`` (for the Folders plugin) [Only for Python < 3.5]
- ``wifi`` (for the wifi plugin) [Linux-only]
*Note for Python 2.6 users*
......
......@@ -111,6 +111,15 @@ critical=90
#wlan0_tx_critical=1000000
#wlan0_tx_log=True
[wifi]
# Define the list of hidden wireless network interfaces (comma-separated regexp)
hide=lo,docker.*
# Define SIGNAL thresholds in db (lower is better...)
# Based on: http://serverfault.com/questions/501025/industry-standard-for-minimum-wifi-signal-strength
careful=-65
warning=-75
critical=-85
#[diskio]
# Define the list of hidden disks (comma-separated regexp)
#hide=sda2,sda5,loop.*
......
......@@ -26,6 +26,7 @@ Legend:
load
memory
network
wifi
ports
disk
fs
......
......@@ -11,7 +11,7 @@ dynamically (bit/s, kbit/s, Mbit/s, etc).
Alerts are only set if the maximum speed per network interface is
available (see sample in the configuration file).
It's also possibile to define:
It's also possible to define:
- a list of network interfaces to hide
- per-interface limit values
......
.. _wifi:
Wifi
=====
*Availability: Linux*
.. image:: ../_static/wifi.png
Glances displays the Wifi hotspots' name and signal quality.
If Glances is ran as root, then all the available hotspots are displayed.
In the configuration file, you can define signal quality thresholds.
"Poor" quality is between -100 and -85dBm, "Good" quality between -85
and -60dBm, and "Excellent" between -60 and -40dBm.
It's also possible to disable the scan on a specific interface from the
configuration file (``[wifi]`` section). For example, if you want to
hide the loopback interface (lo) and all the virtual docker interfaces:
.. code-block:: ini
[wifi]
hide=lo,docker.*
# Define SIGNAL thresholds in dBm (lower is better...)
careful=-65
warning=-75
critical=-85
You can disable this plugin using the --disable-wifi option or by heating
the 'W' from the user interface.
......@@ -22,12 +22,13 @@ features (like the Web interface, exports modules, sensors...):
.. code-block:: console
pip install bottle requests batinfo py3sensors zeroconf netifaces pymdstat influxdb elasticsearch potsdb statsd pystache docker-py pysnmp pika py-cpuinfo bernhard cassandra scandir couchdb pyzmq
pip install bottle requests batinfo py3sensors zeroconf netifaces pymdstat influxdb elasticsearch potsdb statsd pystache docker-py pysnmp pika py-cpuinfo bernhard cassandra scandir couchdb pyzmq wifi
To upgrade Glances to the latest version:
To upgrade Glances and all its dependencies to the latests versions:
.. code-block:: console
pip install --upgrade bottle requests batinfo py3sensors zeroconf netifaces pymdstat influxdb elasticsearch potsdb statsd pystache docker-py pysnmp pika py-cpuinfo bernhard cassandra scandir couchdb pyzmq wifi
pip install --upgrade glances
For additionnal installation methods, read the official `README`_ file.
......
.\" Man page generated from reStructuredText.
.
.TH "GLANCES" "1" "Oct 15, 2016" "2.8_DEVELOP" "Glances"
.TH "GLANCES" "1" "Oct 21, 2016" "2.8_DEVELOP" "Glances"
.SH NAME
glances \- An eye on your system
.
......
......@@ -113,6 +113,8 @@ Start the client browser (browser mode):\n\
dest='disable_load', help='disable load module')
parser.add_argument('--disable-network', action='store_true', default=False,
dest='disable_network', help='disable network module')
parser.add_argument('--disable-wifi', action='store_true', default=False,
dest='disable_wifi', help='disable wifi module')
parser.add_argument('--disable-ports', action='store_true', default=False,
dest='disable_ports', help='disable ports scanner module')
parser.add_argument('--disable-ip', action='store_true', default=False,
......
......@@ -444,6 +444,9 @@ class _GlancesCurses(object):
elif self.pressedkey == ord('w'):
# 'w' > Delete finished warning logs
glances_logs.clean()
elif self.pressedkey == ord('W'):
# 'W' > Enable/Disable Wifi plugin
self.args.disable_wifi = not self.args.disable_wifi
elif self.pressedkey == ord('x'):
# 'x' > Delete finished warning and critical logs
glances_logs.clean(critical=True)
......@@ -540,6 +543,8 @@ class _GlancesCurses(object):
stats_memswap = stats.get_plugin('memswap').get_stats_display(args=self.args)
stats_network = stats.get_plugin('network').get_stats_display(
args=self.args, max_width=plugin_max_width)
stats_wifi = stats.get_plugin('wifi').get_stats_display(
args=self.args, max_width=plugin_max_width)
stats_irq = stats.get_plugin('irq').get_stats_display(
args=self.args, max_width=plugin_max_width)
try:
......@@ -718,13 +723,15 @@ class _GlancesCurses(object):
# ==================================================================
self.init_column()
if not (self.args.disable_network and
self.args.disable_wifi and
self.args.disable_ports and
self.args.disable_diskio and
self.args.disable_fs and
self.args.disable_irq and
self.args.disable_folder and
self.args.disable_raid and
self.args.disable_sensors) and not self.args.disable_left_sidebar:
for s in (stats_network, stats_ports, stats_diskio, stats_fs, stats_irq, stats_folders, stats_raid, stats_sensors, stats_now):
for s in (stats_network, stats_wifi, stats_ports, stats_diskio, stats_fs, stats_irq, stats_folders, stats_raid, stats_sensors, stats_now):
self.new_line()
self.display_plugin(s)
......
......@@ -503,11 +503,11 @@ class GlancesPlugin(object):
# If is_max is set then display the value in MAX
ret = 'MAX' if is_max else 'OK'
try:
if value > self.__get_limit('critical', stat_name=stat_name):
if value >= self.get_limit('critical', stat_name=stat_name):
ret = 'CRITICAL'
elif value > self.__get_limit('warning', stat_name=stat_name):
elif value >= self.get_limit('warning', stat_name=stat_name):
ret = 'WARNING'
elif value > self.__get_limit('careful', stat_name=stat_name):
elif value >= self.get_limit('careful', stat_name=stat_name):
ret = 'CAREFUL'
elif current < minimum:
ret = 'CAREFUL'
......@@ -516,7 +516,7 @@ class GlancesPlugin(object):
# Manage log
log_str = ""
if self.__get_limit_log(stat_name=stat_name, default_action=log):
if self.get_limit_log(stat_name=stat_name, default_action=log):
# Add _LOG to the return string
# So stats will be highlited with a specific color
log_str = "_LOG"
......@@ -537,7 +537,7 @@ class GlancesPlugin(object):
"""Manage the action for the current stat"""
# Here is a command line for the current trigger ?
try:
command = self.__get_limit_action(trigger, stat_name=stat_name)
command = self.get_limit_action(trigger, stat_name=stat_name)
except KeyError:
# Reset the trigger
self.actions.set(stat_name, trigger)
......@@ -578,7 +578,7 @@ class GlancesPlugin(object):
action_key=action_key,
log=True)
def __get_limit(self, criticity, stat_name=""):
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
......@@ -594,7 +594,7 @@ class GlancesPlugin(object):
# Return the limit
return limit
def __get_limit_action(self, criticity, stat_name=""):
def get_limit_action(self, criticity, stat_name=""):
"""Return the action for the alert."""
# Get the action for stat + header
# Exemple: network_wlan0_rx_careful_action
......@@ -608,7 +608,7 @@ class GlancesPlugin(object):
# Return the action list
return ret
def __get_limit_log(self, stat_name, default_action=False):
def get_limit_log(self, stat_name, default_action=False):
"""Return the log tag for the alert."""
# Get the log tag for stat + header
# Exemple: network_wlan0_rx_log
......
# -*- 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/>.
"""Wifi plugin."""
import base64
import operator
from glances.logger import logger
from glances.timer import getTimeSinceLastUpdate
from glances.plugins.glances_plugin import GlancesPlugin
import psutil
# Use the Wifi Python lib (https://pypi.python.org/pypi/wifi)
# Linux-only
try:
from wifi.scan import Cell
from wifi.exceptions import InterfaceError
except ImportError as e:
logger.debug("Wifi library not found. Glances cannot grab Wifi info.")
wifi_tag = False
else:
wifi_tag = True
class Plugin(GlancesPlugin):
"""Glances Wifi plugin.
Get stats of the current Wifi hotspots.
"""
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 the stats
self.reset()
def get_key(self):
"""Return the key of the list.
:returns: string -- SSID is the dict key
"""
return 'ssid'
def reset(self):
"""Reset/init the stats to an empty list.
:returns: None
"""
self.stats = []
@GlancesPlugin._check_decorator
@GlancesPlugin._log_result_decorator
def update(self):
"""Update Wifi stats using the input method.
Stats is a list of dict (one dict per hotspot)
:returns: list -- Stats is a list of dict (hotspot)
"""
# Reset stats
self.reset()
# Exist if we can not grab the stats
if not wifi_tag:
return self.stats
if self.input_method == 'local':
# Update stats using the standard system lib
# Grab network interface stat using the PsUtil net_io_counter method
try:
netiocounters = psutil.net_io_counters(pernic=True)
except UnicodeDecodeError:
return self.stats
for net in netiocounters:
# Do not take hidden interface into account
if self.is_hide(net):
continue
# Grab the stats using the Wifi Python lib
try:
wifi_cells = Cell.all(net)
except InterfaceError:
# Not a Wifi interface
pass
else:
for wifi_cell in wifi_cells:
hotspot = {
'key': self.get_key(),
'ssid': wifi_cell.ssid,
'signal': wifi_cell.signal,
'quality': wifi_cell.quality,
'encrypted': wifi_cell.encrypted,
'encryption_type': wifi_cell.encryption_type if wifi_cell.encrypted else None
}
# Add the hotspot to the list
self.stats.append(hotspot)
elif self.input_method == 'snmp':
# Update stats using SNMP
# Not implemented yet
pass
# Update the view
self.update_views()
return self.stats
def get_alert(self, value):
"""Overwrite the default get_alert method.
Alert is on signal quality where lower is better...
:returns: string -- Signal alert
"""
ret = 'OK'
try:
if value <= self.get_limit('critical', stat_name=self.plugin_name):
ret = 'CRITICAL'
elif value <= self.get_limit('warning', stat_name=self.plugin_name):
ret = 'WARNING'
elif value <= self.get_limit('careful', stat_name=self.plugin_name):
ret = 'CAREFUL'
except KeyError:
ret = 'DEFAULT'
return ret
def update_views(self):
"""Update stats views."""
# Call the father's method
super(Plugin, self).update_views()
# Add specifics informations
# Alert on signal thresholds
for i in self.stats:
self.views[i[self.get_key()]]['signal']['decoration'] = self.get_alert(i['signal'])
self.views[i[self.get_key()]]['quality']['decoration'] = self.views[i[self.get_key()]]['signal']['decoration']
def msg_curse(self, args=None, max_width=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_wifi or not wifi_tag:
return ret
# Max size for the interface name
if max_width is not None and max_width >= 23:
# Interface size name = max_width - space for encyption + quality
ifname_max_width = max_width - 5
else:
ifname_max_width = 16
# Build the string message
# Header
msg = '{:{width}}'.format('WIFI', width=ifname_max_width)
ret.append(self.curse_add_line(msg, "TITLE"))
msg = '{:>7}'.format('dBm')
ret.append(self.curse_add_line(msg))
# Hotspot list (sorted by name)
for i in sorted(self.stats, key=operator.itemgetter(self.get_key())):
# Do not display hotspot with no name (/ssid)
if i['ssid'] == '':
continue
ret.append(self.curse_new_line())
# New hotspot
hotspotname = i['ssid']
# Add the encryption type (if it is available)
if i['encrypted']:
hotspotname = hotspotname + ' {}'.format(i['encryption_type'])
# Cut hotspotname if it is too long
if len(hotspotname) > ifname_max_width:
hotspotname = '_' + hotspotname[-ifname_max_width + 1:]
# Add the new hotspot to the message
msg = '{:{width}}'.format(hotspotname, width=ifname_max_width)
ret.append(self.curse_add_line(msg))
msg = '{:>7}'.format(i['signal'], width=ifname_max_width)
ret.append(self.curse_add_line(msg,
self.get_views(item=i[self.get_key()],
key='signal',
option='decoration')))
return ret
......@@ -92,7 +92,8 @@ setup(
'EXPORT': ['influxdb>=1.0.0', 'elasticsearch', 'potsdb' 'statsd', 'pika', 'bernhard', 'cassandra-driver'],
'ACTION': ['pystache'],
'CPUINFO': ['py-cpuinfo'],
'FOLDERS': ['scandir']
'FOLDERS': ['scandir'],
'WIFI': ['wifi']
},
packages=['glances'],
include_package_data=True,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册