Merge branch 'develop' of https://github.com/nicolargo/glances into feature/gpu-nvidia

This commit is contained in:
Kirby Banman
2016-12-15 22:46:03 -07:00
119 changed files with 4090 additions and 1642 deletions

30
.coveragerc Normal file
View File

@@ -0,0 +1,30 @@
[report]
include =
*glances*
omit =
setup.py
glances/outputs/*
glances/exports/*
glances/compat.py
glances/autodiscover.py
glances/client_browser.py
glances/config.py
glances/history.py
glances/monitored_list.py
glances/outdated.py
glances/password*.py
glances/snmp.py
glances/static_list.py
exclude_lines =
pragma: no cover
if PY3:
if __name__ == .__main__.:
if sys.platform.startswith
except ImportError:
raise NotImplementedError
if WINDOWS
if OSX
if BSD

2
.gitattributes vendored
View File

@@ -1 +1 @@
glances/outputs/static/public/js/*.js -diff
glances/outputs/static/public/* -diff linguist-vendored

19
NEWS
View File

@@ -5,16 +5,35 @@ Glances Version 2
Version 2.8
===========
Deprecated:
* Deprecate Windows Curse UI: automaticaly open Web Browser for the standalone mode (issue #946)
Changes:
* IRQ plugin off by default. '--disable-irq' option replaced by '--enable-irq'.
Enhancements and new features:
* Add ZeroMQ exporter (issue #939)
* Add CouchDB exporter (issue #928)
* Add hotspot Wifi informations (issue #937)
* Add default interface speed and automatic rate thresolds (issue #718)
* Highlight max stats in the processes list (issue #878)
* Docker alerts and actions (issue #875)
* Glances API returns the processes PPID (issue #926)
* Configure server cached time from the command line --cached-time (issue #901)
* Make the log logger configurable (issue #900)
* System uptime in export (issue #890)
* Refactor the --disable-* options (issue #948)
* PID column too small if kernel.pid_max is > 99999 (issue #959)
Bugs corrected:
* Glances RAID plugin Traceback (issue #927)
* Default AMP crashes when 'command' given (issue #933)
* Default AMP ignores `enable` setting (issue #932)
* /proc/interrupts not found in an OpenVZ container (issue #947)
Version 2.7.1
=============

View File

@@ -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*
@@ -109,14 +110,14 @@ features (like the Web interface, exports modules, sensors...):
.. code-block:: console
pip install bottle requests batinfo https://bitbucket.org/gleb_zhulik/py3sensors/get/tip.tar.gz zeroconf netifaces pymdstat influxdb elasticsearch potsdb statsd pystache docker-py pysnmp pika py-cpuinfo bernhard cassandra-driver scandir
pip install bottle requests batinfo https://bitbucket.org/gleb_zhulik/py3sensors/get/tip.tar.gz zeroconf netifaces pymdstat influxdb elasticsearch potsdb statsd pystache docker-py pysnmp pika py-cpuinfo bernhard cassandra-driver scandir pyzmq
To upgrade Glances to the latest version:
.. code-block:: console
pip install --upgrade glances
pip install --upgrade bottle requests batinfo https://bitbucket.org/gleb_zhulik/py3sensors/get/tip.tar.gz zeroconf netifaces pymdstat influxdb elasticsearch potsdb statsd pystache docker-py pysnmp pika py-cpuinfo bernhard cassandra-driver scandir
pip install --upgrade bottle requests batinfo https://bitbucket.org/gleb_zhulik/py3sensors/get/tip.tar.gz zeroconf netifaces pymdstat influxdb elasticsearch potsdb statsd pystache docker-py pysnmp pika py-cpuinfo bernhard cassandra-driver scandir pyzmq
If you need to install Glances in a specific user location, use:

View File

@@ -96,11 +96,20 @@ careful=50
warning=70
critical=90
#[network]
[network]
# Default bitrate thresholds in % of the network interface speed
# Default values if not defined: 70/80/90
rx_careful=70
rx_warning=80
rx_critical=90
tx_careful=70
tx_warning=80
tx_critical=90
# Define the list of hidden network interfaces (comma-separated regexp)
#hide=docker.*,lo
# WLAN 0 alias
#wlan0_alias=Wireless IF
# It is possible to overwrite the bitrate thresholds per interface
# WLAN 0 Default limits (in bits per second aka bps) for interface bitrate
#wlan0_rx_careful=4000000
#wlan0_rx_warning=5000000
@@ -111,6 +120,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.*
@@ -207,6 +225,19 @@ port_default_gateway=True
#port_4_port=80
#port_4_rtt_warning=1000
[docker]
# Thresholds for CPU and MEM (in %)
#cpu_careful=50
#cpu_warning=70
#cpu_critical=90
#mem_careful=20
#mem_warning=50
#mem_critical=70
# Per container thresholds
#containername_cpu_careful=10
#containername_cpu_warning=20
#containername_cpu_critical=30
##############################################################################
# Client/server
##############################################################################
@@ -306,6 +337,18 @@ db=glances
#user=root
#password=root
[zeromq]
# Configuration for the --export-zeromq option
# http://www.zeromq.org
# Use * to bind on all interfaces
host=*
port=5678
# Glances envelopes the stats in a publish message with two frames:
# - First frame containing the following prefix (STRING)
# - Second frame with the Glances plugin name (STRING)
# - Third frame with the Glances plugin stats (JSON)
prefix=G
##############################################################################
# AMPS
# * enable: Enable (true) or disable (false) the AMP
@@ -352,10 +395,10 @@ status_url=http://localhost/nginx_status
[amp_systemd]
# Use the Systemd AMP
enable=true
regex=\/usr\/lib\/systemd\/systemd
regex=\/lib\/systemd\/systemd
refresh=30
one_line=true
systemctl_cmd=/usr/bin/systemctl --plain
systemctl_cmd=/bin/systemctl --plain
[amp_systemv]
# Use the Systemv AMP

View File

25
docker/master/Dockerfile Normal file
View File

@@ -0,0 +1,25 @@
#
# Glances Dockerfile
#
# https://github.com/nicolargo/glances
#
# Pull base image.
FROM ubuntu
# Install Glances (develop branch)
RUN apt-get update && apt-get -y install curl && rm -rf /var/lib/apt/lists/*
RUN curl -L https://raw.githubusercontent.com/nicolargo/glancesautoinstall/master/install.sh | /bin/bash && rm -rf /var/lib/apt/lists/*
# Define working directory.
WORKDIR /glances
# EXPOSE PORT (For XMLRPC)
EXPOSE 61209
# EXPOSE PORT (For Web UI)
EXPOSE 61208
# Define default command.
CMD python -m glances -C /glances/conf/glances.conf $GLANCES_OPT

BIN
docs/_static/wifi.png vendored Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

@@ -16,13 +16,21 @@ displayed.
CPU stats description:
* user: percent time spent in user space
> User CPU time is time spent on the processor running your program's code (or code in libraries)
* system: percent time spent in kernel space
> System CPU time is the time spent running code in the operating system kernel
* idle: percent of CPU used by any program
> Every program or task that runs on a computer system occupies a certain amount of processing time on the CPU. If the CPU has completed all tasks it is idle.
* nice: percent time occupied by user level processes with a positive nice value
> The time the CPU has spent running users' processes that have been "niced"
* irq: percent time spent servicing/handling hardware/software interrupts
> Time servicing interrupts (hardware + software)
* iowait: percent time spent in wait (on disk)
* steal: percent time in involuntary wait by virtual cpu while hypervisor is servicing another processor/virtual machine
> Time spent by the CPU waiting for a IO operations to complete
* steal: percent time in involuntary wait by virtual CPU
> Steal time is the percentage of time a virtual CPU waits for a real CPU while the hypervisor is servicing another virtual processor
* ctx_sw: number of context switches (voluntary + involuntary) per second
> A context switch is a procedure that a computer's CPU (central processing unit) follows to change from one task (or process) to another while ensuring that the tasks do not conflict
* inter: number of interrupts per second
* sw_inter: number of software interrupts per second. Always set to 0 on Windows and SunOS.
* syscal: number of system calls per second. Do not displayed on Linux (always 0).

View File

@@ -8,4 +8,25 @@ Glances uses the Docker API through the `docker-py`_ library.
.. image:: ../_static/docker.png
It is possible to define limits and actions from the configuration file
under the ``[docker]`` section:
.. code-block:: ini
[docker]
# Global containers' thresholds for CPU and MEM (in %)
cpu_careful=50
cpu_warning=70
cpu_critical=90
mem_careful=20
mem_warning=50
mem_critical=70
# Per container thresholds
containername_cpu_careful=10
containername_cpu_warning=20
containername_cpu_critical=30
containername_cpu_critical_action=echo {{Image}} {{Id}} {{cpu}} > /tmp/container_{{name}}.alert
You can use all the variables ({{foo}}) available in the Docker plugin.
.. _docker-py: https://github.com/docker/docker-py

View File

@@ -26,6 +26,7 @@ Legend:
load
memory
network
wifi
ports
disk
fs

View File

@@ -8,6 +8,8 @@ IRQ
Glances displays the top 5 interrupts rate. This plugin is only available on
GNU/Linux machine (stats are grabbed from the /proc/interrupts file).
Note: /proc/interrupts file did not exist inside OpenVZ containers.
How to read the informations:
* The first Column is the IRQ number / name

View File

@@ -8,16 +8,20 @@ Network
Glances displays the network interface bit rate. The unit is adapted
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).
If the interface speed is detected (not on all systems), the defaults
thresholds are applied (70% for careful, 80% warning and 90% critical).
It is possible to define this percents thresholds form the configuration
file. It is also possible to define per interface bit rate thresholds.
In this case thresholds values are define in bps.
It's also possibile to define:
Additionally, you can define:
- a list of network interfaces to hide
- per-interface limit values
- aliases for interface name
in the ``[network]`` section of the configuration file.
The configuration should be done in the ``[network]`` section of the
Glances configuration file.
For example, if you want to hide the loopback interface (lo) and all the
virtual docker interface (docker0, docker1, ...):
@@ -25,4 +29,25 @@ virtual docker interface (docker0, docker1, ...):
.. code-block:: ini
[network]
hide=lo,docker.*
# Default bitrate thresholds in % of the network interface speed
# Default values if not defined: 70/80/90
rx_careful=70
rx_warning=80
rx_critical=90
tx_careful=70
tx_warning=80
tx_critical=90
# Define the list of hidden network interfaces (comma-separated regexp)
hide=docker.*,lo
# WLAN 0 alias
wlan0_alias=Wireless IF
# It is possible to overwrite the bitrate thresholds per interface
# WLAN 0 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

View File

@@ -52,23 +52,25 @@ Columns display
``VIRT`` Virtual Memory Size
The total amount of virtual memory used by the
process
process. It includes all code, data and shared
libraries plus pages that have been swapped out
and pages that have been mapped but not used.
``RES`` Resident Memory Size
The non-swapped physical memory a process is
using
using (what's currently in the physical memory).
``PID`` Process ID
``USER`` User ID
``NI`` Nice level of the process
``S`` Process status
The status of the process:
- ``R``: running
- ``S``: sleeping (may be interrupted)
- ``D``: disk sleep (may not be interrupted)
- ``T``: traced/stopped
- ``Z``: zombie
- ``R``: running or runnable (on run queue)
- ``S``: interruptible sleep (waiting for an event)
- ``D``: uninterruptible sleep (usually IO)
- ``Z``: defunct ("zombie") process
- ``T``: traced/stopped by job control signal
- ``X``: dead (should never be seen)
``TIME+`` Cumulative CPU time used by the process
``R/s`` Per process I/O read rate in B/s

31
docs/aoa/wifi.rst Normal file
View File

@@ -0,0 +1,31 @@
.. _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.

View File

@@ -16,12 +16,101 @@ Command-Line Options
.. option:: -d, --debug
enable debug mode
enable debug mode. The debugging output is saved to /tmp/glances.log.
.. option:: -C CONF_FILE, --config CONF_FILE
path to the configuration file
.. option:: --disable-alert
disable alert/log module
.. option:: --disable-amps
disable application monitoring process module
.. option:: --disable-cpu
disable CPU module
.. option:: --disable-diskio
disable disk I/O module
.. option:: --disable-docker
disable Docker module
.. option:: --disable-folders
disable folders module
.. option:: --disable-fs
disable file system module
.. option:: --disable-hddtemp
disable HD temperature module
.. option:: --disable-ip
disable IP module
.. option:: --disable-irq
disable IRQ module
.. option:: --disable-load
disable load module
.. option:: --disable-mem
disable memory module
.. option:: --disable-memswap
disable memory swap module
.. option:: --disable-network
disable network module
.. option:: --disable-ports
disable Ports module
.. option:: --disable-process
disable process module
.. option:: --disable-raid
disable RAID module
.. option:: --disable-sensors
disable sensors module
.. option:: --disable-wifi
disable Wifi module
.. option:: -0, --disable-irix
task's CPU usage will be divided by the total number of CPUs
.. option:: -1, --percpu
start Glances in per CPU mode
.. option:: -2, --disable-left-sidebar
disable network, disk I/O, FS and sensors modules (py3sensors lib
needed)
.. option:: -3, --disable-quicklook
disable quick look module
@@ -30,86 +119,13 @@ Command-Line Options
disable all but quick look and load
.. option:: --disable-cpu
disable CPU module
.. option:: --disable-mem
disable memory module
.. option:: --disable-swap
disable swap module
.. option:: --disable-load
disable load module
.. option:: --disable-network
disable network module
.. option:: --disable-ip
disable IP module
.. option:: --disable-diskio
disable disk I/O module
.. option:: --disable-fs
disable filesystem module
.. option:: --disable-folder
disable folder module
.. option:: --disable-irq
disable IRQ module
.. option:: --disable-sensors
disable sensors module
.. option:: --disable-hddtemp
disable HD temperature module
.. option:: --disable-raid
disable RAID module
.. option:: --disable-docker
disable Docker module
.. option:: --disable-ports
disable Ports module
.. option:: -5, --disable-top
disable top menu (QuickLook, CPU, MEM, SWAP and LOAD)
.. option:: -2, --disable-left-sidebar
.. option:: --enable-history
disable network, disk I/O, FS and sensors modules (py3sensors lib
needed)
.. option:: --disable-process
disable process module
.. option:: --disable-amps
disable application monitoring process module
.. option:: --disable-log
disable log module
enable the history mode (matplotlib lib needed)
.. option:: --disable-bold
@@ -123,11 +139,11 @@ Command-Line Options
enable extended stats on top process
.. option:: --enable-history
.. option:: --export-graph
enable the history mode (matplotlib lib needed)
export stats to graph
.. option:: --path-history PATH_HISTORY
.. option:: --path-graph PATH_GRAPH
set the export path for graph history
@@ -135,37 +151,41 @@ Command-Line Options
export stats to a CSV file
.. option:: --export-influxdb
export stats to an InfluxDB server (influxdb lib needed)
.. option:: --export-cassandra
export stats to a Cassandra/Scylla server (cassandra lib needed)
.. option:: --export-opentsdb
.. option:: --export-couchdb
export stats to an OpenTSDB server (potsdb lib needed)
.. option:: --export-statsd
export stats to a StatsD server (statsd lib needed)
.. option:: --export-rabbitmq
export stats to RabbitMQ broker (pika lib needed)
.. option:: --export-riemann
export stats to Riemann server (bernhard lib needed)
export stats to a CouchDB server (couchdb lib needed)
.. option:: --export-elasticsearch
export stats to an Elasticsearch server (elasticsearch lib needed)
.. option:: --export-couchdb
.. option:: --export-influxdb
export stats to a CouchDB server (couchdb lib needed)
export stats to an InfluxDB server (influxdb lib needed)
.. option:: --export-opentsdb
export stats to an OpenTSDB server (potsdb lib needed)
.. option:: --export-rabbitmq
export stats to RabbitMQ broker (pika lib needed)
.. option:: --export-statsd
export stats to a StatsD server (statsd lib needed)
.. option:: --export-riemann
export stats to Riemann server (bernhard lib needed)
.. option:: --export-zeromq
export stats to a ZeroMQ server (zmq lib needed)
.. option:: -c CLIENT, --client CLIENT
@@ -231,6 +251,14 @@ Command-Line Options
run Glances in web server mode (bottle lib needed)
.. option:: --cached-time CACHED_TIME
set the server cache time [default: 1 sec]
.. option:: open-web-browser
try to open the Web UI in the default Web browser
.. option:: -q, --quiet
do not display the curses interface
@@ -243,10 +271,6 @@ Command-Line Options
force short name for processes name
.. option:: -0, --disable-irix
task's CPU usage will be divided by the total number of CPUs
.. option:: --hide-kernel-threads
hide kernel threads in process list
@@ -271,10 +295,6 @@ Command-Line Options
display temperature in Fahrenheit (default is Celsius)
.. option:: -1, --percpu
start Glances in per CPU mode
.. option:: --fs-free-space
display FS free space instead of used
@@ -283,6 +303,10 @@ Command-Line Options
optimize display colors for white background
.. option:: --disable-check-update
disable online Glances version ckeck
Interactive Commands
--------------------
@@ -398,6 +422,9 @@ The following commands (key pressed) are supported while in Glances:
``w``
Delete finished warning log messages
``W``
Show/hide Wifi module
``x``
Delete finished warning and critical log messages

View File

@@ -110,3 +110,76 @@ By default, the ``glances.log`` file is under the temporary directory:
If ``glances.log`` is not writable, a new file will be created and
returned to the user console.
If you want to use another system path or change the log message, you can use
your own logger configuration. First of all you have to create a glances.json
file with (for example) the following content (JSON format):
.. code-block:: json
{
"version": 1,
"disable_existing_loggers": "False",
"root": {
"level": "INFO",
"handlers": ["file", "console"]
},
"formatters": {
"standard": {
"format": "%(asctime)s -- %(levelname)s -- %(message)s"
},
"short": {
"format": "%(levelname)s: %(message)s"
},
"free": {
"format": "%(message)s"
}
},
"handlers": {
"file": {
"level": "DEBUG",
"class": "logging.handlers.RotatingFileHandler",
"formatter": "standard",
"filename": "/var/tmp/glances.log"
},
"console": {
"level": "CRITICAL",
"class": "logging.StreamHandler",
"formatter": "free"
}
},
"loggers": {
"debug": {
"handlers": ["file", "console"],
"level": "DEBUG"
},
"verbose": {
"handlers": ["file", "console"],
"level": "INFO"
},
"standard": {
"handlers": ["file"],
"level": "INFO"
},
"requests": {
"handlers": ["file", "console"],
"level": "ERROR"
},
"elasticsearch": {
"handlers": ["file", "console"],
"level": "ERROR"
},
"elasticsearch.trace": {
"handlers": ["file", "console"],
"level": "ERROR"
}
}
}
and start Glances using the following command line:
.. code-block:: console
LOG_CFG=<path>/glances.json glances
Note: Replace <path> by the folder where your glances.json file is hosted.

View File

@@ -10,10 +10,11 @@ to providing stats to multiple services (see list below).
:maxdepth: 2
csv
influxdb
cassandra
opentsdb
statsd
rabbitmq
elastic
influxdb
opentsdb
rabbitmq
riemann
statsd
zeromq

57
docs/gw/zeromq.rst Normal file
View File

@@ -0,0 +1,57 @@
.. _zeromq:
ZeroMQ
======
You can export statistics to a ``ZeroMQ`` server.
The connection should be defined in the Glances configuration file as
following:
.. code-block:: ini
[zeromq]
host=127.0.0.1
port=5678
prefix=G
Note: Glances `envelopes`_ the stats before publishing it.
The message is composed of three frames.
- first frame containing the prefix configured in the [zeromq] section (as STRING)
- second frame with the Glances plugin name (as STRING)
- third frame with the Glances plugin stats (as JSON)
Run Glances with:
.. code-block:: console
$ glances --export-zeromq -C <path>/glances.conf
Following is a simple Python client to subscribe to the Glances stats:
.. code-block:: python
# -*- coding: utf-8 -*-
#
# ZeroMQ subscriber for Glances
#
import json
import zmq
context = zmq.Context()
subscriber = context.socket(zmq.SUB)
subscriber.setsockopt(zmq.SUBSCRIBE, 'G')
subscriber.connect("tcp://127.0.0.1:5678")
while True:
_, plugin, data_raw = subscriber.recv_multipart()
data = json.loads(data_raw)
print('{} => {}'.format(plugin, data))
subscriber.close()
context.term()
.. _envelopes: http://zguide.zeromq.org/page:all#Pub-Sub-Message-Envelopes

View File

@@ -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
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.

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "GLANCES" "1" "Sep 24, 2016" "2.8_DEVELOP" "Glances"
.TH "GLANCES" "1" "Nov 21, 2016" "2.8_DEVELOP" "Glances"
.SH NAME
glances \- An eye on your system
.
@@ -59,7 +59,7 @@ show program\(aqs version number and exit
.INDENT 0.0
.TP
.B \-d, \-\-debug
enable debug mode
enable debug mode. The debugging output is saved to /tmp/glances.log.
.UNINDENT
.INDENT 0.0
.TP
@@ -68,6 +68,117 @@ path to the configuration file
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-alert
disable alert/log module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-amps
disable application monitoring process module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-cpu
disable CPU module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-diskio
disable disk I/O module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-docker
disable Docker module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-folders
disable folders module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-fs
disable file system module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-hddtemp
disable HD temperature module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-ip
disable IP module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-irq
disable IRQ module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-load
disable load module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-mem
disable memory module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-memswap
disable memory swap module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-network
disable network module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-ports
disable Ports module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-process
disable process module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-raid
disable RAID module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-sensors
disable sensors module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-wifi
disable Wifi module
.UNINDENT
.INDENT 0.0
.TP
.B \-0, \-\-disable\-irix
task\(aqs CPU usage will be divided by the total number of CPUs
.UNINDENT
.INDENT 0.0
.TP
.B \-1, \-\-percpu
start Glances in per CPU mode
.UNINDENT
.INDENT 0.0
.TP
.B \-2, \-\-disable\-left\-sidebar
disable network, disk I/O, FS and sensors modules (py3sensors lib
needed)
.UNINDENT
.INDENT 0.0
.TP
.B \-3, \-\-disable\-quicklook
disable quick look module
.UNINDENT
@@ -78,104 +189,13 @@ disable all but quick look and load
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-cpu
disable CPU module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-mem
disable memory module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-swap
disable swap module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-load
disable load module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-network
disable network module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-ip
disable IP module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-diskio
disable disk I/O module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-fs
disable filesystem module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-folder
disable folder module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-irq
disable IRQ module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-sensors
disable sensors module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-hddtemp
disable HD temperature module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-raid
disable RAID module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-docker
disable Docker module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-ports
disable Ports module
.UNINDENT
.INDENT 0.0
.TP
.B \-5, \-\-disable\-top
disable top menu (QuickLook, CPU, MEM, SWAP and LOAD)
.UNINDENT
.INDENT 0.0
.TP
.B \-2, \-\-disable\-left\-sidebar
disable network, disk I/O, FS and sensors modules (py3sensors lib
needed)
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-process
disable process module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-amps
disable application monitoring process module
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-log
disable log module
.B \-\-enable\-history
enable the history mode (matplotlib lib needed)
.UNINDENT
.INDENT 0.0
.TP
@@ -194,12 +214,12 @@ enable extended stats on top process
.UNINDENT
.INDENT 0.0
.TP
.B \-\-enable\-history
enable the history mode (matplotlib lib needed)
.B \-\-export\-graph
export stats to graph
.UNINDENT
.INDENT 0.0
.TP
.B \-\-path\-history PATH_HISTORY
.B \-\-path\-graph PATH_GRAPH
set the export path for graph history
.UNINDENT
.INDENT 0.0
@@ -209,33 +229,13 @@ export stats to a CSV file
.UNINDENT
.INDENT 0.0
.TP
.B \-\-export\-influxdb
export stats to an InfluxDB server (influxdb lib needed)
.UNINDENT
.INDENT 0.0
.TP
.B \-\-export\-cassandra
export stats to a Cassandra/Scylla server (cassandra lib needed)
.UNINDENT
.INDENT 0.0
.TP
.B \-\-export\-opentsdb
export stats to an OpenTSDB server (potsdb lib needed)
.UNINDENT
.INDENT 0.0
.TP
.B \-\-export\-statsd
export stats to a StatsD server (statsd lib needed)
.UNINDENT
.INDENT 0.0
.TP
.B \-\-export\-rabbitmq
export stats to RabbitMQ broker (pika lib needed)
.UNINDENT
.INDENT 0.0
.TP
.B \-\-export\-riemann
export stats to Riemann server (bernhard lib needed)
.B \-\-export\-couchdb
export stats to a CouchDB server (couchdb lib needed)
.UNINDENT
.INDENT 0.0
.TP
@@ -244,8 +244,33 @@ export stats to an Elasticsearch server (elasticsearch lib needed)
.UNINDENT
.INDENT 0.0
.TP
.B \-\-export\-couchdb
export stats to a CouchDB server (couchdb lib needed)
.B \-\-export\-influxdb
export stats to an InfluxDB server (influxdb lib needed)
.UNINDENT
.INDENT 0.0
.TP
.B \-\-export\-opentsdb
export stats to an OpenTSDB server (potsdb lib needed)
.UNINDENT
.INDENT 0.0
.TP
.B \-\-export\-rabbitmq
export stats to RabbitMQ broker (pika lib needed)
.UNINDENT
.INDENT 0.0
.TP
.B \-\-export\-statsd
export stats to a StatsD server (statsd lib needed)
.UNINDENT
.INDENT 0.0
.TP
.B \-\-export\-riemann
export stats to Riemann server (bernhard lib needed)
.UNINDENT
.INDENT 0.0
.TP
.B \-\-export\-zeromq
export stats to a ZeroMQ server (zmq lib needed)
.UNINDENT
.INDENT 0.0
.TP
@@ -329,6 +354,16 @@ run Glances in web server mode (bottle lib needed)
.UNINDENT
.INDENT 0.0
.TP
.B \-\-cached\-time CACHED_TIME
set the server cache time [default: 1 sec]
.UNINDENT
.INDENT 0.0
.TP
.B open\-web\-browser
try to open the Web UI in the default Web browser
.UNINDENT
.INDENT 0.0
.TP
.B \-q, \-\-quiet
do not display the curses interface
.UNINDENT
@@ -344,11 +379,6 @@ force short name for processes name
.UNINDENT
.INDENT 0.0
.TP
.B \-0, \-\-disable\-irix
task\(aqs CPU usage will be divided by the total number of CPUs
.UNINDENT
.INDENT 0.0
.TP
.B \-\-hide\-kernel\-threads
hide kernel threads in process list
.UNINDENT
@@ -379,11 +409,6 @@ display temperature in Fahrenheit (default is Celsius)
.UNINDENT
.INDENT 0.0
.TP
.B \-1, \-\-percpu
start Glances in per CPU mode
.UNINDENT
.INDENT 0.0
.TP
.B \-\-fs\-free\-space
display FS free space instead of used
.UNINDENT
@@ -392,6 +417,11 @@ display FS free space instead of used
.B \-\-theme\-white
optimize display colors for white background
.UNINDENT
.INDENT 0.0
.TP
.B \-\-disable\-check\-update
disable online Glances version ckeck
.UNINDENT
.SH INTERACTIVE COMMANDS
.sp
The following commands (key pressed) are supported while in Glances:
@@ -511,6 +541,9 @@ View cumulative network I/O
.B \fBw\fP
Delete finished warning log messages
.TP
.B \fBW\fP
Show/hide Wifi module
.TP
.B \fBx\fP
Delete finished warning and critical log messages
.TP

View File

@@ -20,13 +20,13 @@
"""Init the Glances software."""
# Import system libs
import locale
import platform
import signal
import sys
# Global name
__appname__ = 'glances'
__version__ = '2.8_DEVELOP'
__author__ = 'Nicolas Hennion <nicolas@nicolargo.com>'
__license__ = 'LGPL'
@@ -42,7 +42,9 @@ except ImportError:
# Note: others Glances libs will be imported optionally
from glances.logger import logger
from glances.main import GlancesMain
from glances.globals import WINDOWS
# Check locale
try:
locale.setlocale(locale.LC_ALL, '')
except locale.Error:
@@ -93,6 +95,111 @@ def end():
sys.exit(0)
def start_standalone(config, args):
"""Start the standalone mode"""
logger.info("Start standalone mode")
# Share global var
global standalone
# Import the Glances standalone module
from glances.standalone import GlancesStandalone
# Init the standalone mode
standalone = GlancesStandalone(config=config, args=args)
# Start the standalone (CLI) loop
standalone.serve_forever()
def start_clientbrowser(config, args):
"""Start the browser client mode"""
logger.info("Start client mode (browser)")
# Share global var
global client
# Import the Glances client browser module
from glances.client_browser import GlancesClientBrowser
# Init the client
client = GlancesClientBrowser(config=config, args=args)
# Start the client loop
client.serve_forever()
# Shutdown the client
client.end()
def start_client(config, args):
"""Start the client mode"""
logger.info("Start client mode")
# Share global var
global client
# Import the Glances client browser module
from glances.client import GlancesClient
# Init the client
client = GlancesClient(config=config, args=args)
# Test if client and server are in the same major version
if not client.login():
logger.critical("The server version is not compatible with the client")
sys.exit(2)
# Start the client loop
client.serve_forever()
# Shutdown the client
client.end()
def start_server(config, args):
"""Start the server mode"""
logger.info("Start server mode")
# Share global var
global server
# Import the Glances server module
from glances.server import GlancesServer
server = GlancesServer(cached_time=args.cached_time,
config=config,
args=args)
print('Glances server is running on {}:{}'.format(args.bind_address, args.port))
# Set the server login/password (if -P/--password tag)
if args.password != "":
server.add_user(args.username, args.password)
# Start the server loop
server.serve_forever()
# Shutdown the server?
server.server_close()
def start_webserver(config, args):
"""Start the Web server mode"""
logger.info("Start web server mode")
# Share global var
global webserver
# Import the Glances web server module
from glances.webserver import GlancesWebServer
# Init the web server mode
webserver = GlancesWebServer(config=config, args=args)
# Start the web server loop
webserver.serve_forever()
def main():
"""Main entry point for Glances.
@@ -107,92 +214,29 @@ def main():
psutil_version))
# Share global var
global core, standalone, client, server, webserver
global core
# Create the Glances main instance
core = GlancesMain()
config = core.get_config()
args = core.get_args()
# Catch the CTRL-C signal
signal.signal(signal.SIGINT, __signal_handler)
# Glances can be ran in standalone, client or server mode
if core.is_standalone():
logger.info("Start standalone mode")
# Import the Glances standalone module
from glances.standalone import GlancesStandalone
# Init the standalone mode
standalone = GlancesStandalone(config=core.get_config(),
args=core.get_args())
# Start the standalone (CLI) loop
standalone.serve_forever()
elif core.is_client():
if core.is_standalone() and not WINDOWS:
start_standalone(config=config, args=args)
elif core.is_client() and not WINDOWS:
if core.is_client_browser():
logger.info("Start client mode (browser)")
# Import the Glances client browser module
from glances.client_browser import GlancesClientBrowser
# Init the client
client = GlancesClientBrowser(config=core.get_config(),
args=core.get_args())
start_clientbrowser(config=config, args=args)
else:
logger.info("Start client mode")
# Import the Glances client module
from glances.client import GlancesClient
# Init the client
client = GlancesClient(config=core.get_config(),
args=core.get_args())
# Test if client and server are in the same major version
if not client.login():
logger.critical("The server version is not compatible with the client")
sys.exit(2)
# Start the client loop
client.serve_forever()
# Shutdown the client
client.end()
start_client(config=config, args=args)
elif core.is_server():
logger.info("Start server mode")
# Import the Glances server module
from glances.server import GlancesServer
args = core.get_args()
print args.cached_time
server = GlancesServer(cached_time=args.cached_time,
config=core.get_config(),
args=args)
print('Glances server is running on {}:{}'.format(args.bind_address, args.port))
# Set the server login/password (if -P/--password tag)
if args.password != "":
server.add_user(args.username, args.password)
# Start the server loop
server.serve_forever()
# Shutdown the server?
server.server_close()
elif core.is_webserver():
logger.info("Start web server mode")
# Import the Glances web server module
from glances.webserver import GlancesWebServer
# Init the web server mode
webserver = GlancesWebServer(config=core.get_config(),
args=core.get_args())
# Start the web server loop
webserver.serve_forever()
start_server(config=config, args=args)
elif core.is_webserver() or (core.is_standalone() and WINDOWS):
# Web server mode replace the standalone mode on Windows OS
# In this case, try to start the web browser mode automaticaly
if core.is_standalone() and WINDOWS:
args.open_web_browser = True
start_webserver(config=config, args=args)

View File

@@ -35,7 +35,7 @@ one_line=false
command=foo status
"""
from subprocess import check_output, STDOUT
from subprocess import check_output, STDOUT, CalledProcessError
from glances.compat import u, to_ascii
from glances.logger import logger
@@ -66,8 +66,11 @@ class Amp(GlancesAmp):
logger.debug('{}: Error while executing service ({})'.format(self.NAME, e))
else:
if res is not None:
msg = u(check_output(res.split(), stderr=STDOUT))
self.set_result(to_ascii(msg.rstrip()))
try:
msg = u(check_output(res.split(), stderr=STDOUT))
self.set_result(to_ascii(msg.rstrip()))
except CalledProcessError as e:
self.set_result(e.output)
else:
# Set the default message if command return None
# Default sum of CPU and MEM for the matching regex

View File

@@ -106,9 +106,11 @@ class AmpsList(object):
"""Update the command result attributed."""
# Search application monitored processes by a regular expression
processlist = glances_processes.getalllist()
# Iter upon the AMPs dict
for k, v in iteritems(self.get()):
if not v.enable():
# Do not update if the enable tag is set
continue
try:
amps_list = [p for p in processlist for c in p['cmdline'] if re.search(v.regex(), c) is not None]
except TypeError:

View File

@@ -22,7 +22,6 @@
import socket
import sys
from glances import __appname__
from glances.globals import BSD
from glances.logger import logger
@@ -48,8 +47,8 @@ if zeroconf_tag:
# Global var
# Recent versions of the zeroconf python package doesnt like a zeroconf type that ends with '._tcp.'.
# Correct issue: zeroconf problem with zeroconf_type = "_%s._tcp." % __appname__ #888
zeroconf_type = "_%s._tcp.local." % __appname__
# Correct issue: zeroconf problem with zeroconf_type = "_%s._tcp." % 'glances' #888
zeroconf_type = "_%s._tcp.local." % 'glances'
class AutoDiscovered(object):

View File

@@ -21,6 +21,7 @@
import json
import socket
import threading
from glances.compat import Fault, ProtocolError, ServerProxy
from glances.autodiscover import GlancesAutoDiscoverServer
@@ -90,144 +91,150 @@ class GlancesClientBrowser(object):
else:
return 'http://{}:{}'.format(server['ip'], server['port'])
def __update_stats(self, server):
"""
Update stats for the given server (picked from the server list)
"""
# Get the server URI
uri = self.__get_uri(server)
# Try to connect to the server
t = GlancesClientTransport()
t.set_timeout(3)
# Get common stats
try:
s = ServerProxy(uri, transport=t)
except Exception as e:
logger.warning(
"Client browser couldn't create socket {}: {}".format(uri, e))
else:
# Mandatory stats
try:
# CPU%
cpu_percent = 100 - json.loads(s.getCpu())['idle']
server['cpu_percent'] = '{:.1f}'.format(cpu_percent)
# MEM%
server['mem_percent'] = json.loads(s.getMem())['percent']
# OS (Human Readable name)
server['hr_name'] = json.loads(s.getSystem())['hr_name']
except (socket.error, Fault, KeyError) as e:
logger.debug(
"Error while grabbing stats form {}: {}".format(uri, e))
server['status'] = 'OFFLINE'
except ProtocolError as e:
if e.errcode == 401:
# Error 401 (Authentication failed)
# Password is not the good one...
server['password'] = None
server['status'] = 'PROTECTED'
else:
server['status'] = 'OFFLINE'
logger.debug("Cannot grab stats from {} ({} {})".format(uri, e.errcode, e.errmsg))
else:
# Status
server['status'] = 'ONLINE'
# Optional stats (load is not available on Windows OS)
try:
# LOAD
load_min5 = json.loads(s.getLoad())['min5']
server['load_min5'] = '{:.2f}'.format(load_min5)
except Exception as e:
logger.warning(
"Error while grabbing stats form {}: {}".format(uri, e))
return server
def __display_server(self, server):
"""
Connect and display the given server
"""
# Display the Glances client for the selected server
logger.debug("Selected server: {}".format(server))
# Connection can take time
# Display a popup
self.screen.display_popup(
'Connect to {}:{}'.format(server['name'], server['port']), duration=1)
# A password is needed to access to the server's stats
if server['password'] is None:
# First of all, check if a password is available in the [passwords] section
clear_password = self.password.get_password(server['name'])
if (clear_password is None or self.get_servers_list()
[self.screen.active_server]['status'] == 'PROTECTED'):
# Else, the password should be enter by the user
# Display a popup to enter password
clear_password = self.screen.display_popup(
'Password needed for {}: '.format(server['name']), is_input=True)
# Store the password for the selected server
if clear_password is not None:
self.set_in_selected('password', self.password.sha256_hash(clear_password))
# Display the Glance client on the selected server
logger.info("Connect Glances client to the {} server".format(server['key']))
# Init the client
args_server = self.args
# Overwrite connection setting
args_server.client = server['ip']
args_server.port = server['port']
args_server.username = server['username']
args_server.password = server['password']
client = GlancesClient(config=self.config, args=args_server, return_to_browser=True)
# Test if client and server are in the same major version
if not client.login():
self.screen.display_popup(
"Sorry, cannot connect to '{}'\n"
"See 'glances.log' for more details".format(server['name']))
# Set the ONLINE status for the selected server
self.set_in_selected('status', 'OFFLINE')
else:
# Start the client loop
# Return connection type: 'glances' or 'snmp'
connection_type = client.serve_forever()
try:
logger.debug("Disconnect Glances client from the {} server".format(server['key']))
except IndexError:
# Server did not exist anymore
pass
else:
# Set the ONLINE status for the selected server
if connection_type == 'snmp':
self.set_in_selected('status', 'SNMP')
else:
self.set_in_selected('status', 'ONLINE')
# Return to the browser (no server selected)
self.screen.active_server = None
def __serve_forever(self):
"""Main client loop."""
while True:
# No need to update the server list
# It's done by the GlancesAutoDiscoverListener class (autodiscover.py)
# Or define staticaly in the configuration file (module static_list.py)
# For each server in the list, grab elementary stats (CPU, LOAD, MEM, OS...)
# logger.debug(self.get_servers_list())
try:
for v in self.get_servers_list():
# Do not retreive stats for statics server
# Why ? Because for each offline servers, the timeout will be reached
# So ? The curse interface freezes
if v['type'] == 'STATIC' and v['status'] in ['UNKNOWN', 'SNMP', 'OFFLINE']:
continue
# No need to update the server list
# It's done by the GlancesAutoDiscoverListener class (autodiscover.py)
# Or define staticaly in the configuration file (module static_list.py)
# For each server in the list, grab elementary stats (CPU, LOAD, MEM, OS...)
# Get the server URI
uri = self.__get_uri(v)
logger.debug("Iter through the following server list: {}".format(self.get_servers_list()))
for v in self.get_servers_list():
thread = threading.Thread(target=self.__update_stats, args=[v])
thread.start()
# Try to connect to the server
t = GlancesClientTransport()
t.set_timeout(3)
# Update the screen (list or Glances client)
if self.screen.active_server is None:
# Display the Glances browser
self.screen.update(self.get_servers_list())
else:
# Display the active server
self.__display_server(self.get_servers_list()[self.screen.active_server])
# Get common stats
try:
s = ServerProxy(uri, transport=t)
except Exception as e:
logger.warning(
"Client browser couldn't create socket {}: {}".format(uri, e))
else:
# Mandatory stats
try:
# CPU%
cpu_percent = 100 - json.loads(s.getCpu())['idle']
v['cpu_percent'] = '{:.1f}'.format(cpu_percent)
# MEM%
v['mem_percent'] = json.loads(s.getMem())['percent']
# OS (Human Readable name)
v['hr_name'] = json.loads(s.getSystem())['hr_name']
except (socket.error, Fault, KeyError) as e:
logger.debug(
"Error while grabbing stats form {}: {}".format(uri, e))
v['status'] = 'OFFLINE'
except ProtocolError as e:
if e.errcode == 401:
# Error 401 (Authentication failed)
# Password is not the good one...
v['password'] = None
v['status'] = 'PROTECTED'
else:
v['status'] = 'OFFLINE'
logger.debug("Cannot grab stats from {} ({} {})".format(uri, e.errcode, e.errmsg))
else:
# Status
v['status'] = 'ONLINE'
# Optional stats (load is not available on Windows OS)
try:
# LOAD
load_min5 = json.loads(s.getLoad())['min5']
v['load_min5'] = '{:.2f}'.format(load_min5)
except Exception as e:
logger.warning(
"Error while grabbing stats form {}: {}".format(uri, e))
# List can change size during iteration...
except RuntimeError:
logger.debug(
"Server list dictionnary change inside the loop (wait next update)")
# Update the screen (list or Glances client)
if self.screen.active_server is None:
# Display the Glances browser
self.screen.update(self.get_servers_list())
else:
# Display the Glances client for the selected server
logger.debug("Selected server: {}".format(self.get_servers_list()[self.screen.active_server]))
# Connection can take time
# Display a popup
self.screen.display_popup(
'Connect to {}:{}'.format(v['name'], v['port']), duration=1)
# A password is needed to access to the server's stats
if self.get_servers_list()[self.screen.active_server]['password'] is None:
# First of all, check if a password is available in the [passwords] section
clear_password = self.password.get_password(v['name'])
if (clear_password is None or self.get_servers_list()
[self.screen.active_server]['status'] == 'PROTECTED'):
# Else, the password should be enter by the user
# Display a popup to enter password
clear_password = self.screen.display_popup(
'Password needed for {}: '.format(v['name']), is_input=True)
# Store the password for the selected server
if clear_password is not None:
self.set_in_selected('password', self.password.sha256_hash(clear_password))
# Display the Glance client on the selected server
logger.info("Connect Glances client to the {} server".format(
self.get_servers_list()[self.screen.active_server]['key']))
# Init the client
args_server = self.args
# Overwrite connection setting
args_server.client = self.get_servers_list()[self.screen.active_server]['ip']
args_server.port = self.get_servers_list()[self.screen.active_server]['port']
args_server.username = self.get_servers_list()[self.screen.active_server]['username']
args_server.password = self.get_servers_list()[self.screen.active_server]['password']
client = GlancesClient(config=self.config, args=args_server, return_to_browser=True)
# Test if client and server are in the same major version
if not client.login():
self.screen.display_popup(
"Sorry, cannot connect to '{}'\n"
"See 'glances.log' for more details".format(v['name']))
# Set the ONLINE status for the selected server
self.set_in_selected('status', 'OFFLINE')
else:
# Start the client loop
# Return connection type: 'glances' or 'snmp'
connection_type = client.serve_forever()
try:
logger.debug("Disconnect Glances client from the {} server".format(
self.get_servers_list()[self.screen.active_server]['key']))
except IndexError:
# Server did not exist anymore
pass
else:
# Set the ONLINE status for the selected server
if connection_type == 'snmp':
self.set_in_selected('status', 'SNMP')
else:
self.set_in_selected('status', 'ONLINE')
# Return to the browser (no server selected)
self.screen.active_server = None
# Loop
self.__serve_forever()
def serve_forever(self):
"""Wrapper to the serve_forever function.

View File

@@ -28,12 +28,6 @@ import types
PY3 = sys.version_info[0] == 3
def to_ascii(s):
"""Convert the unicode 's' to a ASCII string
Usefull to remove accent (diacritics)"""
return unicodedata.normalize('NFKD', s).encode('ASCII', 'ignore')
if PY3:
import queue
from configparser import ConfigParser, NoOptionError, NoSectionError
@@ -54,6 +48,11 @@ if PY3:
viewvalues = operator.methodcaller('values')
viewitems = operator.methodcaller('items')
def to_ascii(s):
"""Convert the bytes string to a ASCII string
Usefull to remove accent (diacritics)"""
return str(s, 'utf-8')
def listitems(d):
return list(d.items())
@@ -104,6 +103,11 @@ else:
viewvalues = operator.methodcaller('viewvalues')
viewitems = operator.methodcaller('viewitems')
def to_ascii(s):
"""Convert the unicode 's' to a ASCII string
Usefull to remove accent (diacritics)"""
return unicodedata.normalize('NFKD', s).encode('ASCII', 'ignore')
def listitems(d):
return d.items()

View File

@@ -24,7 +24,6 @@ import sys
import multiprocessing
from io import open
from glances import __appname__
from glances.compat import ConfigParser, NoOptionError
from glances.globals import BSD, LINUX, OSX, WINDOWS, sys_prefix
from glances.logger import logger
@@ -71,22 +70,18 @@ class Config(object):
paths.append(
os.path.join(os.environ.get('XDG_CONFIG_HOME') or
os.path.expanduser('~/.config'),
__appname__, self.config_filename))
'glances', self.config_filename))
if BSD:
paths.append(
os.path.join(sys.prefix, 'etc', __appname__, self.config_filename))
paths.append(os.path.join(sys.prefix, 'etc', 'glances', self.config_filename))
else:
paths.append(
os.path.join('/etc', __appname__, self.config_filename))
paths.append(os.path.join('/etc/glances', self.config_filename))
elif OSX:
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))
os.path.join(os.path.expanduser('~/Library/Application Support/glances'),
self.config_filename))
paths.append(os.path.join(sys_prefix, 'etc', 'glances', self.config_filename))
elif WINDOWS:
paths.append(
os.path.join(os.environ.get('APPDATA'), __appname__, self.config_filename))
paths.append(os.path.join(os.environ.get('APPDATA'), 'glances', self.config_filename))
return paths
@@ -172,6 +167,16 @@ class Config(object):
self.set_default('memswap', 'warning', '70')
self.set_default('memswap', 'critical', '90')
# NETWORK
if not self.parser.has_section('network'):
self.parser.add_section('network')
self.set_default('network', 'rx_careful', '70')
self.set_default('network', 'rx_warning', '80')
self.set_default('network', 'rx_critical', '90')
self.set_default('network', 'tx_careful', '70')
self.set_default('network', 'tx_warning', '80')
self.set_default('network', 'tx_critical', '90')
# FS
if not self.parser.has_section('fs'):
self.parser.add_section('fs')
@@ -207,6 +212,15 @@ class Config(object):
"""Return the loaded configuration file."""
return self._loaded_config_file
def as_dict(self):
"""Return the configuration as a dict"""
dictionary = {}
for section in self.parser.sections():
dictionary[section] = {}
for option in self.parser.options(section):
dictionary[section][option] = self.parser.get(section, option)
return dictionary
def sections(self):
"""Return a list of all sections."""
return self.parser.sections()
@@ -231,6 +245,13 @@ class Config(object):
except NoOptionError:
return default
def get_int_value(self, section, option, default=0):
"""Get the int value of an option, if it exists."""
try:
return self.parser.getint(section, option)
except NoOptionError:
return int(default)
def get_float_value(self, section, option, default=0.0):
"""Get the float value of an option, if it exists."""
try:

View File

@@ -68,7 +68,8 @@ class Export(GlancesExport):
plugins = stats.getAllPlugins()
# Init data with timestamp (issue#708)
csv_header = ['timestamp']
if self.first_line:
csv_header = ['timestamp']
csv_data = [time.strftime('%Y-%m-%d %H:%M:%S')]
# Loop over available plugin

View File

@@ -64,7 +64,8 @@ class GlancesExport(object):
'system',
'uptime',
'sensors',
'docker']
'docker',
'uptime']
def get_item_key(self, item):
"""Return the value of the item 'key'."""

View File

@@ -0,0 +1,122 @@
# -*- 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/>.
"""ZeroMQ interface class."""
import sys
import json
import zmq
from zmq.utils.strtypes import asbytes
from glances.compat import NoOptionError, NoSectionError, b
from glances.logger import logger
from glances.exports.glances_export import GlancesExport
class Export(GlancesExport):
"""This class manages the ZeroMQ export module."""
def __init__(self, config=None, args=None):
"""Init the ZeroMQ export IF."""
super(Export, self).__init__(config=config, args=args)
# Load the ZeroMQ configuration file section ([export_zeromq])
self.host = None
self.port = None
self.export_enable = self.load_conf()
if not self.export_enable:
sys.exit(2)
# Init the ZeroMQ context
self.context = None
self.client = self.init()
def load_conf(self, section="zeromq"):
"""Load the ZeroMQ configuration in the Glances configuration file."""
if self.config is None:
return False
try:
self.host = self.config.get_value(section, 'host')
self.port = self.config.get_value(section, 'port')
self.prefix = str(self.config.get_value(section, 'prefix'))
except NoSectionError:
logger.critical("No ZeroMQ configuration found")
return False
except NoOptionError as e:
logger.critical("Error in the ZeroMQ configuration (%s)" % e)
return False
else:
logger.debug("Load ZeroMQ from the Glances configuration file")
return True
def init(self):
"""Init the connection to the CouchDB server."""
if not self.export_enable:
return None
server_uri = 'tcp://{}:{}'.format(self.host, self.port)
try:
self.context = zmq.Context()
publisher = self.context.socket(zmq.PUB)
publisher.bind(server_uri)
except Exception as e:
logger.critical("Cannot connect to ZeroMQ server %s (%s)" % (server_uri, e))
sys.exit(2)
else:
logger.info("Connected to the ZeroMQ server %s" % server_uri)
return publisher
def exit(self):
"""Close the socket and context"""
if self.client is not None:
self.client.close()
if self.context is not None:
self.context.destroy()
def export(self, name, columns, points):
"""Write the points to the ZeroMQ server."""
logger.debug("Export {} stats to ZeroMQ".format(name))
# Create DB input
data = dict(zip(columns, points))
# Do not publish empty stats
if data == {}:
return False
# Glances envelopes the stats in a publish message with two frames:
# - First frame containing the following prefix (STRING)
# - Second frame with the Glances plugin name (STRING)
# - Third frame with the Glances plugin stats (JSON)
message = [b(self.prefix),
b(name),
asbytes(json.dumps(data))]
# Write data to the ZeroMQ bus
# Result can be view: tcp://host:port
try:
self.client.send_multipart(message)
except Exception as e:
logger.error("Cannot export {} stats to ZeroMQ ({})".format(name, e))
return True

View File

@@ -22,66 +22,66 @@
import logging
import os
import tempfile
import json
from logging.config import dictConfig
# Define the logging configuration
LOGGING_CFG = {
'version': 1,
'disable_existing_loggers': False,
'root': {
'level': 'INFO',
'handlers': ['file', 'console']
"version": 1,
"disable_existing_loggers": "False",
"root": {
"level": "INFO",
"handlers": ["file", "console"]
},
'formatters': {
'standard': {
'format': '%(asctime)s -- %(levelname)s -- %(message)s'
"formatters": {
"standard": {
"format": "%(asctime)s -- %(levelname)s -- %(message)s"
},
'short': {
'format': '%(levelname)s: %(message)s'
"short": {
"format": "%(levelname)s: %(message)s"
},
'free': {
'format': '%(message)s'
"free": {
"format": "%(message)s"
}
},
'handlers': {
'file': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'formatter': 'standard',
# http://stackoverflow.com/questions/847850/cross-platform-way-of-getting-temp-directory-in-python
"handlers": {
"file": {
"level": "DEBUG",
"class": "logging.handlers.RotatingFileHandler",
"formatter": "standard",
'filename': os.path.join(tempfile.gettempdir(), 'glances.log')
},
'console': {
'level': 'CRITICAL',
'class': 'logging.StreamHandler',
'formatter': 'free'
"console": {
"level": "CRITICAL",
"class": "logging.StreamHandler",
"formatter": "free"
}
},
'loggers': {
'debug': {
'handlers': ['file', 'console'],
'level': 'DEBUG',
"loggers": {
"debug": {
"handlers": ["file", "console"],
"level": "DEBUG"
},
'verbose': {
'handlers': ['file', 'console'],
'level': 'INFO'
"verbose": {
"handlers": ["file", "console"],
"level": "INFO"
},
'standard': {
'handlers': ['file'],
'level': 'INFO'
"standard": {
"handlers": ["file"],
"level": "INFO"
},
'requests': {
'handlers': ['file', 'console'],
'level': 'ERROR',
"requests": {
"handlers": ["file", "console"],
"level": "ERROR"
},
'elasticsearch': {
'handlers': ['file', 'console'],
'level': 'ERROR',
},
'elasticsearch.trace': {
'handlers': ['file', 'console'],
'level': 'ERROR',
"elasticsearch": {
"handlers": ["file", "console"],
"level": "ERROR"
},
"elasticsearch.trace": {
"handlers": ["file", "console"],
"level": "ERROR"
}
}
}
@@ -98,12 +98,31 @@ def tempfile_name():
return ret
def glances_logger():
"""Build and return the logger."""
temp_path = tempfile_name()
def glances_logger(env_key='LOG_CFG'):
"""Build and return the logger.
env_key define the env var where a path to a specific JSON logger
could be defined
:return: logger -- Logger instance
"""
_logger = logging.getLogger()
LOGGING_CFG['handlers']['file']['filename'] = temp_path
dictConfig(LOGGING_CFG)
# Overwrite the default logger file
LOGGING_CFG['handlers']['file']['filename'] = tempfile_name()
# By default, use the LOGGING_CFG lgger configuration
config = LOGGING_CFG
# Check if a specific configuration is available
user_file = os.getenv(env_key, None)
if user_file and os.path.exists(user_file):
# A user file as been defined. Use it...
with open(user_file, 'rt') as f:
config = json.load(f)
# Load the configuration
dictConfig(config)
return _logger

View File

@@ -147,7 +147,7 @@ class GlancesLogs(object):
1, # COUNT
[], # TOP 3 PROCESS LIST
proc_desc, # MONITORED PROCESSES DESC
'cpu_percent'] # TOP PROCESS SORTKEY
glances_processes.sort_key] # TOP PROCESS SORTKEY
# Add the item to the list
self.logs_list.insert(0, item)

View File

@@ -2,7 +2,7 @@
#
# This file is part of Glances.
#
# Copyright (C) 2015 Nicolargo <nicolas@nicolargo.com>
# 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
@@ -24,8 +24,8 @@ import os
import sys
import tempfile
from glances import __appname__, __version__, psutil_version
from glances.compat import input, NoOptionError, NoSectionError
from glances import __version__, psutil_version
from glances.compat import input
from glances.config import Config
from glances.globals import LINUX, WINDOWS
from glances.logger import logger
@@ -61,7 +61,7 @@ Monitor local machine with the Web interface (Web UI):\n\
Glances web server started on http://0.0.0.0:61208/\n\
\n\
Monitor local machine and export stats to a CSV file (standalone mode):\n\
$ glances --export-csv\n\
$ glances --export-csv /tmp/glances.csv\n\
\n\
Monitor local machine and export stats to a InfluxDB server with 5s refresh time (standalone mode):\n\
$ glances -t 5 --export-influxdb\n\
@@ -88,7 +88,7 @@ Start the client browser (browser mode):\n\
"""Init all the command line arguments."""
version = "Glances v" + __version__ + " with psutil v" + psutil_version
parser = argparse.ArgumentParser(
prog=__appname__,
prog='glances',
conflict_handler='resolve',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=self.example_of_use)
@@ -99,58 +99,64 @@ Start the client browser (browser mode):\n\
parser.add_argument('-C', '--config', dest='conf_file',
help='path to the configuration file')
# Enable or disable option on startup
parser.add_argument('-3', '--disable-quicklook', action='store_true', default=False,
dest='disable_quicklook', help='disable quick look module')
parser.add_argument('-4', '--full-quicklook', action='store_true', default=False,
dest='full_quicklook', help='disable all but quick look and load')
parser.add_argument('--disable-alert', action='store_true', default=False,
dest='disable_alert', help='disable alert module')
parser.add_argument('--disable-amps', action='store_true', default=False,
dest='disable_amps', help='disable applications monitoring process (AMP) module')
parser.add_argument('--disable-cpu', action='store_true', default=False,
dest='disable_cpu', help='disable CPU module')
parser.add_argument('--disable-mem', action='store_true', default=False,
dest='disable_mem', help='disable memory module')
parser.add_argument('--disable-swap', action='store_true', default=False,
dest='disable_swap', help='disable swap module')
parser.add_argument('--disable-diskio', action='store_true', default=False,
dest='disable_diskio', help='disable disk I/O module')
parser.add_argument('--disable-docker', action='store_true', default=False,
dest='disable_docker', help='disable Docker module')
parser.add_argument('--disable-folders', action='store_true', default=False,
dest='disable_folders', help='disable folder module')
parser.add_argument('--disable-fs', action='store_true', default=False,
dest='disable_fs', help='disable filesystem module')
parser.add_argument('--disable-hddtemp', action='store_true', default=False,
dest='disable_hddtemp', help='disable HD temperature module')
parser.add_argument('--disable-ip', action='store_true', default=False,
dest='disable_ip', help='disable IP module')
parser.add_argument('--disable-load', action='store_true', default=False,
dest='disable_load', help='disable load module')
parser.add_argument('--disable-mem', action='store_true', default=False,
dest='disable_mem', help='disable memory module')
parser.add_argument('--disable-memswap', action='store_true', default=False,
dest='disable_memswap', help='disable memory swap module')
parser.add_argument('--disable-network', action='store_true', default=False,
dest='disable_network', help='disable network 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,
dest='disable_ip', help='disable IP module')
parser.add_argument('--disable-diskio', action='store_true', default=False,
dest='disable_diskio', help='disable disk I/O module')
parser.add_argument('--disable-irq', action='store_true', default=False,
dest='disable_irq', help='disable IRQ module'),
parser.add_argument('--disable-fs', action='store_true', default=False,
dest='disable_fs', help='disable filesystem module')
parser.add_argument('--disable-folder', action='store_true', default=False,
dest='disable_folder', help='disable folder module')
parser.add_argument('--disable-sensors', action='store_true', default=False,
dest='disable_sensors', help='disable sensors module')
parser.add_argument('--disable-hddtemp', action='store_true', default=False,
dest='disable_hddtemp', help='disable HD temperature module')
parser.add_argument('--disable-process', action='store_true', default=False,
dest='disable_process', help='disable process module')
parser.add_argument('--disable-raid', action='store_true', default=False,
dest='disable_raid', help='disable RAID module')
parser.add_argument('--disable-docker', action='store_true', default=False,
dest='disable_docker', help='disable Docker module')
parser.add_argument('-5', '--disable-top', action='store_true',
default=False, dest='disable_top',
help='disable top menu (QL, CPU, MEM, SWAP and LOAD)')
parser.add_argument('--disable-sensors', action='store_true', default=False,
dest='disable_sensors', help='disable sensors module')
parser.add_argument('--disable-wifi', action='store_true', default=False,
dest='disable_wifi', help='disable wifi module')
parser.add_argument('-0', '--disable-irix', action='store_true', default=False,
dest='disable_irix', help='task\'s cpu usage will be divided by the total number of CPUs')
parser.add_argument('-1', '--percpu', action='store_true', default=False,
dest='percpu', help='start Glances in per CPU mode')
parser.add_argument('-2', '--disable-left-sidebar', action='store_true',
default=False, dest='disable_left_sidebar',
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-amps', action='store_true', default=False,
dest='disable_amps', help='disable applications monitoring process (AMP) module')
parser.add_argument('--disable-log', action='store_true', default=False,
dest='disable_log', help='disable log module')
parser.add_argument('-3', '--disable-quicklook', action='store_true', default=False,
dest='disable_quicklook', help='disable quick look module')
parser.add_argument('-4', '--full-quicklook', action='store_true', default=False,
dest='full_quicklook', help='disable all but quick look and load')
parser.add_argument('-5', '--disable-top', action='store_true',
default=False, dest='disable_top',
help='disable top menu (QL, CPU, MEM, SWAP and LOAD)')
parser.add_argument('--disable-history', action='store_true', default=False,
dest='disable_history', help='disable stats history')
parser.add_argument('--disable-bold', action='store_true', default=False,
dest='disable_bold', help='disable bold mode in the terminal')
parser.add_argument('--disable-bg', action='store_true', default=False,
dest='disable_bg', help='disable background colors in the terminal')
parser.add_argument('--enable-irq', action='store_true', default=False,
dest='enable_irq', help='enable IRQ module'),
parser.add_argument('--enable-process-extended', action='store_true', default=False,
dest='enable_process_extended', help='enable extended stats on top process')
# Export modules feature
@@ -176,6 +182,8 @@ Start the client browser (browser mode):\n\
dest='export_riemann', help='export stats to riemann broker (bernhard lib needed)')
parser.add_argument('--export-couchdb', action='store_true', default=False,
dest='export_couchdb', help='export stats to a CouchDB server (couch lib needed)')
parser.add_argument('--export-zeromq', action='store_true', default=False,
dest='export_zeromq', help='export stats to a ZeroMQ server (pyzmq lib needed)')
# Client/Server option
parser.add_argument('-c', '--client', dest='client',
help='connect to a Glances server by IPv4/IPv6 address or hostname')
@@ -211,6 +219,8 @@ Start the client browser (browser mode):\n\
dest='webserver', help='run Glances in web server mode (bottle needed)')
parser.add_argument('--cached-time', default=self.cached_time, type=int,
dest='cached_time', help='set the server cache time [default: {} sec]'.format(self.cached_time))
parser.add_argument('--open-web-browser', action='store_true', default=False,
dest='open_web_browser', help='try to open the Web UI in the default Web browser')
# Display options
parser.add_argument('-q', '--quiet', default=False, action='store_true',
dest='quiet', help='do not display the curses interface')
@@ -218,8 +228,6 @@ Start the client browser (browser mode):\n\
dest='process_filter', help='set the process filter pattern (regular expression)')
parser.add_argument('--process-short-name', action='store_true', default=False,
dest='process_short_name', help='force short name for processes name')
parser.add_argument('-0', '--disable-irix', action='store_true', default=False,
dest='disable_irix', help='task\'s cpu usage will be divided by the total number of CPUs')
if not WINDOWS:
parser.add_argument('--hide-kernel-threads', action='store_true', default=False,
dest='no_kernel_threads', help='hide kernel threads in process list')
@@ -234,8 +242,6 @@ Start the client browser (browser mode):\n\
dest='diskio_iops', help='show IO per second in the DiskIO plugin')
parser.add_argument('--fahrenheit', action='store_true', default=False,
dest='fahrenheit', help='display temperature in Fahrenheit (default is Celsius)')
parser.add_argument('-1', '--percpu', action='store_true', default=False,
dest='percpu', help='start Glances in per CPU mode')
parser.add_argument('--fs-free-space', action='store_true', default=False,
dest='fs_free_space', help='display FS free space instead of used')
parser.add_argument('--theme-white', action='store_true', default=False,
@@ -278,11 +284,14 @@ Start the client browser (browser mode):\n\
args.password_prompt = True
# Prompt username
if args.server:
args.username = self.__get_username(description='Define the Glances server username: ')
args.username = self.__get_username(
description='Define the Glances server username: ')
elif args.webserver:
args.username = self.__get_username(description='Define the Glances webserver username: ')
args.username = self.__get_username(
description='Define the Glances webserver username: ')
elif args.client:
args.username = self.__get_username(description='Enter the Glances server username: ')
args.username = self.__get_username(
description='Enter the Glances server username: ')
else:
# Default user name is 'glances'
args.username = self.username
@@ -291,17 +300,20 @@ Start the client browser (browser mode):\n\
# Interactive or file password
if args.server:
args.password = self.__get_password(
description='Define the Glances server password ({} username): '.format(args.username),
description='Define the Glances server password ({} username): '.format(
args.username),
confirm=True,
username=args.username)
elif args.webserver:
args.password = self.__get_password(
description='Define the Glances webserver password ({} username): '.format(args.username),
description='Define the Glances webserver password ({} username): '.format(
args.username),
confirm=True,
username=args.username)
elif args.client:
args.password = self.__get_password(
description='Enter the Glances server password ({} username): '.format(args.username),
description='Enter the Glances server password ({} username): '.format(
args.username),
clear=True,
username=args.username)
else:
@@ -321,7 +333,7 @@ Start the client browser (browser mode):\n\
args.disable_quicklook = False
args.disable_cpu = True
args.disable_mem = True
args.disable_swap = True
args.disable_memswap = True
args.disable_load = False
# Manage disable_top option
@@ -330,7 +342,7 @@ Start the client browser (browser mode):\n\
args.disable_quicklook = True
args.disable_cpu = True
args.disable_mem = True
args.disable_swap = True
args.disable_memswap = True
args.disable_load = True
# Control parameter and exit if it is not OK
@@ -338,28 +350,32 @@ Start the client browser (browser mode):\n\
# Export is only available in standalone or client mode (issue #614)
export_tag = args.export_csv or \
args.export_elasticsearch or \
args.export_statsd or \
args.export_influxdb or \
args.export_cassandra or \
args.export_opentsdb or \
args.export_rabbitmq or \
args.export_couchdb
args.export_elasticsearch or \
args.export_statsd or \
args.export_influxdb or \
args.export_cassandra or \
args.export_opentsdb or \
args.export_rabbitmq or \
args.export_couchdb
if not (self.is_standalone() or self.is_client()) and export_tag:
logger.critical("Export is only available in standalone or client mode")
logger.critical(
"Export is only available in standalone or client mode")
sys.exit(2)
# Filter is only available in standalone mode
if args.process_filter is not None and not self.is_standalone():
logger.critical("Process filter is only available in standalone mode")
logger.critical(
"Process filter is only available in standalone mode")
sys.exit(2)
# Check graph output path
if args.export_graph and args.path_graph is not None:
if not os.access(args.path_graph, os.W_OK):
logger.critical("Graphs output path {0} do not exist or is not writable".format(args.path_graph))
logger.critical(
"Graphs output path {0} do not exist or is not writable".format(args.path_graph))
sys.exit(2)
logger.debug("Graphs output path is set to {0}".format(args.path_graph))
logger.debug(
"Graphs output path is set to {0}".format(args.path_graph))
# For export graph, history is mandatory
if args.export_graph and args.disable_history:

View File

@@ -32,7 +32,7 @@ except ImportError:
else:
outdated_tag = True
from glances import __version__, __appname__
from glances import __version__
from glances.globals import BSD, LINUX, OSX, WINDOWS
from glances.logger import logger
@@ -155,12 +155,11 @@ class Outdated(object):
if LINUX or BSD:
return os.path.join(os.environ.get('XDG_CONFIG_HOME') or
os.path.expanduser('~/.config'),
__appname__)
'glances')
elif OSX:
return os.path.join(os.path.expanduser('~/Library/Application Support/'),
__appname__)
return os.path.expanduser('~/Library/Application Support/glances')
elif WINDOWS:
return os.path.join(os.environ.get('APPDATA'), __appname__)
return os.path.join(os.environ.get('APPDATA'), 'glances')
def _update_pypi_version(self):
"""Get the latest Pypi version (as a string) via the Restful JSON API"""

View File

@@ -24,8 +24,10 @@ import os
import sys
import tempfile
from io import open
import webbrowser
from glances.timer import Timer
from glances.globals import WINDOWS
from glances.logger import logger
try:
@@ -39,7 +41,10 @@ class GlancesBottle(object):
"""This class manages the Bottle Web server."""
def __init__(self, args=None):
def __init__(self, config=None, args=None):
# Init config
self.config = config
# Init args
self.args = args
@@ -89,6 +94,8 @@ class GlancesBottle(object):
self._app.route('/<refresh_time:int>', method=["GET"], callback=self._index)
# REST API
self._app.route('/api/2/config', method="GET", callback=self._api_config)
self._app.route('/api/2/config/<item>', method="GET", callback=self._api_config_item)
self._app.route('/api/2/args', method="GET", callback=self._api_args)
self._app.route('/api/2/args/<item>', method="GET", callback=self._api_args_item)
self._app.route('/api/2/help', method="GET", callback=self._api_help)
@@ -117,9 +124,19 @@ class GlancesBottle(object):
self.plugins_list = self.stats.getAllPlugins()
# Bind the Bottle TCP address/port
bindmsg = 'Glances web server started on http://{}:{}/'.format(self.args.bind_address, self.args.port)
bindurl = 'http://{}:{}/'.format(self.args.bind_address,
self.args.port)
bindmsg = 'Glances web server started on {}'.format(bindurl)
logger.info(bindmsg)
print(bindmsg)
if self.args.open_web_browser:
# Implementation of the issue #946
# Try to open the Glances Web UI in the default Web browser if:
# 1) --open-web-browser option is used
# 2) Glances standalone mode is running on Windows OS
webbrowser.open(bindurl,
new=2,
autoraise=1)
self._app.run(host=self.args.bind_address, port=self.args.port, quiet=not self.args.debug)
def end(self):
@@ -407,6 +424,43 @@ class GlancesBottle(object):
"""
return self._api_itemvalue(plugin, item, value)
def _api_config(self):
"""Glances API RESTFul implementation.
Return the JSON representation of the Glances configuration file
HTTP/200 if OK
HTTP/404 if others error
"""
response.content_type = 'application/json'
try:
# Get the JSON value of the config' dict
args_json = json.dumps(self.config.as_dict())
except Exception as e:
abort(404, "Cannot get config (%s)" % str(e))
return args_json
def _api_config_item(self, item):
"""Glances API RESTFul implementation.
Return the JSON representation of the Glances configuration item
HTTP/200 if OK
HTTP/400 if item is not found
HTTP/404 if others error
"""
response.content_type = 'application/json'
config_dict = self.config.as_dict()
if item not in config_dict:
abort(400, "Unknown configuration item %s" % item)
try:
# Get the JSON value of the config' dict
args_json = json.dumps(config_dict[item])
except Exception as e:
abort(404, "Cannot get config item (%s)" % str(e))
return args_json
def _api_args(self):
"""Glances API RESTFul implementation.
@@ -436,7 +490,7 @@ class GlancesBottle(object):
response.content_type = 'application/json'
if item not in self.args:
abort(400, "Unknown item %s" % item)
abort(400, "Unknown argument item %s" % item)
try:
# Get the JSON value of the args' dict

View File

@@ -51,6 +51,45 @@ class _GlancesCurses(object):
Note: It is a private class, use GlancesCursesClient or GlancesCursesBrowser.
"""
_hotkeys = {
'0': {'switch': 'disable_irix'},
'1': {'switch': 'percpu'},
'2': {'switch': 'disable_left_sidebar'},
'3': {'switch': 'disable_quicklook'},
'4': {'switch': 'full_quicklook'},
'5': {'switch': 'disable_top'},
'/': {'switch': 'process_short_name'},
'd': {'switch': 'disable_diskio'},
'A': {'switch': 'disable_amps'},
'b': {'switch': 'byte'},
'B': {'switch': 'diskio_iops'},
'D': {'switch': 'disable_docker'},
'e': {'switch': 'enable_process_extended'},
'F': {'switch': 'fs_free_space'},
'h': {'switch': 'help_tag'},
'I': {'switch': 'disable_ip'},
'l': {'switch': 'disable_alert'},
'M': {'switch': 'reset_minmax_tag'},
'n': {'switch': 'disable_network'},
'P': {'switch': 'disable_ports'},
'Q': {'switch': 'enable_irq'},
'r': {'switch': 'reset_history_tag'},
'R': {'switch': 'disable_raid'},
's': {'switch': 'disable_sensors'},
'T': {'switch': 'network_sum'},
'U': {'switch': 'network_cumul'},
'W': {'switch': 'disable_wifi'},
# Processes sort hotkeys
'a': {'auto_sort': True, 'sort_key': 'cpu_percent'},
'c': {'auto_sort': False, 'sort_key': 'cpu_percent'},
'i': {'auto_sort': False, 'sort_key': 'io_counters'},
'm': {'auto_sort': False, 'sort_key': 'memory_percent'},
'p': {'auto_sort': False, 'sort_key': 'name'},
't': {'auto_sort': False, 'sort_key': 'cpu_times'},
'u': {'auto_sort': False, 'sort_key': 'username'},
'c': {'auto_sort': False, 'sort_key': 'cpu_percent'}
}
def __init__(self, config=None, args=None):
# Init
self.config = config
@@ -73,7 +112,6 @@ class _GlancesCurses(object):
# Load the 'outputs' section of the configuration file
# - Init the theme (default is black)
self.theme = {'name': 'black'}
self.load_config(self.config)
# Init cursor
self._init_cursor()
@@ -286,7 +324,23 @@ class _GlancesCurses(object):
# Catch the pressed key
self.pressedkey = self.get_key(self.term_window)
# Actions...
# Actions (available in the global hotkey dict)...
for hotkey in self._hotkeys:
if self.pressedkey == ord(hotkey) and 'switch' in self._hotkeys[hotkey]:
setattr(self.args,
self._hotkeys[hotkey]['switch'],
not getattr(self.args,
self._hotkeys[hotkey]['switch']))
if self.pressedkey == ord(hotkey) and 'auto_sort' in self._hotkeys[hotkey]:
setattr(glances_processes,
'auto_sort',
self._hotkeys[hotkey]['auto_sort'])
if self.pressedkey == ord(hotkey) and 'sort_key' in self._hotkeys[hotkey]:
setattr(glances_processes,
'sort_key',
self._hotkeys[hotkey]['sort_key'])
# Other actions...
if self.pressedkey == ord('\x1b') or self.pressedkey == ord('q'):
# 'ESC'|'q' > Quit
if return_to_browser:
@@ -295,152 +349,29 @@ class _GlancesCurses(object):
self.end()
logger.info("Stop Glances")
sys.exit(0)
elif self.pressedkey == 10:
elif self.pressedkey == ord('\n'):
# 'ENTER' > Edit the process filter
self.edit_filter = not self.edit_filter
elif self.pressedkey == ord('0'):
# '0' > Switch between IRIX and Solaris mode
self.args.disable_irix = not self.args.disable_irix
elif self.pressedkey == ord('1'):
# '1' > Switch between CPU and PerCPU information
self.args.percpu = not self.args.percpu
elif self.pressedkey == ord('2'):
# '2' > Enable/disable left sidebar
self.args.disable_left_sidebar = not self.args.disable_left_sidebar
elif self.pressedkey == ord('3'):
# '3' > Enable/disable quicklook
self.args.disable_quicklook = not self.args.disable_quicklook
elif self.pressedkey == ord('4'):
# '4' > Enable/disable all but quick look and load
self.args.full_quicklook = not self.args.full_quicklook
if self.args.full_quicklook:
self.args.disable_quicklook = False
self.args.disable_cpu = True
self.args.disable_mem = True
self.args.disable_swap = True
self.enable_fullquicklook()
else:
self.args.disable_quicklook = False
self.args.disable_cpu = False
self.args.disable_mem = False
self.args.disable_swap = False
self.disable_fullquicklook()
elif self.pressedkey == ord('5'):
# '5' > Enable/disable top menu
logger.info(self.args.disable_top)
self.args.disable_top = not self.args.disable_top
if self.args.disable_top:
self.args.disable_quicklook = True
self.args.disable_cpu = True
self.args.disable_mem = True
self.args.disable_swap = True
self.args.disable_load = True
self.disable_top()
else:
self.args.disable_quicklook = False
self.args.disable_cpu = False
self.args.disable_mem = False
self.args.disable_swap = False
self.args.disable_load = False
elif self.pressedkey == ord('/'):
# '/' > Switch between short/long name for processes
self.args.process_short_name = not self.args.process_short_name
elif self.pressedkey == ord('a'):
# 'a' > Sort processes automatically and reset to 'cpu_percent'
glances_processes.auto_sort = True
glances_processes.sort_key = 'cpu_percent'
elif self.pressedkey == ord('A'):
# 'A' > enable/disable AMP module
self.args.disable_amps = not self.args.disable_amps
elif self.pressedkey == ord('b'):
# 'b' > Switch between bit/s and Byte/s for network IO
self.args.byte = not self.args.byte
elif self.pressedkey == ord('B'):
# 'B' > Switch between bit/s and IO/s for Disk IO
self.args.diskio_iops = not self.args.diskio_iops
elif self.pressedkey == ord('c'):
# 'c' > Sort processes by CPU usage
glances_processes.auto_sort = False
glances_processes.sort_key = 'cpu_percent'
elif self.pressedkey == ord('d'):
# 'd' > Show/hide disk I/O stats
self.args.disable_diskio = not self.args.disable_diskio
elif self.pressedkey == ord('D'):
# 'D' > Show/hide Docker stats
self.args.disable_docker = not self.args.disable_docker
elif self.pressedkey == ord('e'):
# 'e' > Enable/Disable extended stats for top process
self.args.enable_process_extended = not self.args.enable_process_extended
if not self.args.enable_process_extended:
glances_processes.disable_extended()
else:
glances_processes.enable_extended()
self.enable_top()
elif self.pressedkey == ord('E'):
# 'E' > Erase the process filter
logger.info("Erase process filter")
glances_processes.process_filter = None
elif self.pressedkey == ord('F'):
# 'F' > Switch between FS available and free space
self.args.fs_free_space = not self.args.fs_free_space
elif self.pressedkey == ord('f'):
# 'f' > Show/hide fs / folder stats
self.args.disable_fs = not self.args.disable_fs
self.args.disable_folder = not self.args.disable_folder
self.args.disable_folders = not self.args.disable_folders
elif self.pressedkey == ord('g'):
# 'g' > Export graphs to file
# 'g' > Generate graph from history
self.graph_tag = not self.graph_tag
elif self.pressedkey == ord('h'):
# 'h' > Show/hide help
self.args.help_tag = not self.args.help_tag
elif self.pressedkey == ord('i'):
# 'i' > Sort processes by IO rate (not available on OS X)
glances_processes.auto_sort = False
glances_processes.sort_key = 'io_counters'
elif self.pressedkey == ord('I'):
# 'I' > Show/hide IP module
self.args.disable_ip = not self.args.disable_ip
elif self.pressedkey == ord('l'):
# 'l' > Show/hide log messages
self.args.disable_log = not self.args.disable_log
elif self.pressedkey == ord('m'):
# 'm' > Sort processes by MEM usage
glances_processes.auto_sort = False
glances_processes.sort_key = 'memory_percent'
elif self.pressedkey == ord('M'):
# 'M' > Reset processes summary min/max
self.args.reset_minmax_tag = not self.args.reset_minmax_tag
elif self.pressedkey == ord('n'):
# 'n' > Show/hide network stats
self.args.disable_network = not self.args.disable_network
elif self.pressedkey == ord('p'):
# 'p' > Sort processes by name
glances_processes.auto_sort = False
glances_processes.sort_key = 'name'
elif self.pressedkey == ord('P'):
# 'P' > Disable ports scan plugins
self.args.disable_ports = not self.args.disable_ports
elif self.pressedkey == ord('Q'):
self.args.disable_irq = not self.args.disable_irq
elif self.pressedkey == ord('r'):
# 'r' > Reset history
self.reset_history_tag = not self.reset_history_tag
elif self.pressedkey == ord('R'):
# 'R' > Hide RAID plugins
self.args.disable_raid = not self.args.disable_raid
elif self.pressedkey == ord('s'):
# 's' > Show/hide sensors stats (Linux-only)
self.args.disable_sensors = not self.args.disable_sensors
elif self.pressedkey == ord('t'):
# 't' > Sort processes by TIME usage
glances_processes.auto_sort = False
glances_processes.sort_key = 'cpu_times'
elif self.pressedkey == ord('T'):
# 'T' > View network traffic as sum Rx+Tx
self.args.network_sum = not self.args.network_sum
elif self.pressedkey == ord('u'):
# 'u' > Sort processes by USER
glances_processes.auto_sort = False
glances_processes.sort_key = 'username'
elif self.pressedkey == ord('U'):
# 'U' > View cumulative network I/O (instead of bitrate)
self.args.network_cumul = not self.args.network_cumul
elif self.pressedkey == ord('w'):
# 'w' > Delete finished warning logs
glances_logs.clean()
@@ -448,17 +379,52 @@ 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 + AMPs)
# Enable/Disable display
# 'z' > Enable or disable processes
self.args.disable_process = not self.args.disable_process
# Enable/Disable update
if self.args.disable_process:
glances_processes.disable()
else:
glances_processes.enable()
# Change the curse interface according to the current configuration
if not self.args.enable_process_extended:
glances_processes.disable_extended()
else:
glances_processes.enable_extended()
# Return the key code
return self.pressedkey
def disable_top(self):
"""Disable the top panel"""
self.args.disable_quicklook = True
self.args.disable_cpu = True
self.args.disable_mem = True
self.args.disable_memswap = True
self.args.disable_load = True
def enable_top(self):
"""Enable the top panel"""
self.args.disable_quicklook = False
self.args.disable_cpu = False
self.args.disable_mem = False
self.args.disable_memswap = False
self.args.disable_load = False
def disable_fullquicklook(self):
"""Disable the full quicklook mode"""
self.args.disable_quicklook = False
self.args.disable_cpu = False
self.args.disable_mem = False
self.args.disable_memswap = False
def enable_fullquicklook(self):
"""Disable the full quicklook mode"""
self.args.disable_quicklook = False
self.args.disable_cpu = True
self.args.disable_mem = True
self.args.disable_memswap = True
def end(self):
"""Shutdown the curses window."""
if hasattr(curses, 'echo'):
@@ -495,6 +461,51 @@ class _GlancesCurses(object):
"""New column in the curses interface."""
self.column = self.next_column
def __get_stat_display(self, stats, plugin_max_width):
ret = {}
ret["system"] = stats.get_plugin(
'system').get_stats_display(args=self.args)
ret["uptime"] = stats.get_plugin('uptime').get_stats_display()
if self.args.percpu:
ret["cpu"] = stats.get_plugin('percpu').get_stats_display(args=self.args)
else:
ret["cpu"] = stats.get_plugin('cpu').get_stats_display(args=self.args)
ret["load"] = stats.get_plugin('load').get_stats_display(args=self.args)
ret["mem"] = stats.get_plugin('mem').get_stats_display(args=self.args)
ret["memswap"] = stats.get_plugin('memswap').get_stats_display(args=self.args)
ret["network"] = stats.get_plugin('network').get_stats_display(
args=self.args, max_width=plugin_max_width)
ret["wifi"] = stats.get_plugin('wifi').get_stats_display(
args=self.args, max_width=plugin_max_width)
ret["irq"] = stats.get_plugin('irq').get_stats_display(
args=self.args, max_width=plugin_max_width)
try:
ret["ip"] = stats.get_plugin('ip').get_stats_display(args=self.args)
except AttributeError:
ret["ip"] = None
ret["diskio"] = stats.get_plugin(
'diskio').get_stats_display(args=self.args)
ret["fs"] = stats.get_plugin('fs').get_stats_display(
args=self.args, max_width=plugin_max_width)
ret["folders"] = stats.get_plugin('folders').get_stats_display(
args=self.args, max_width=plugin_max_width)
ret["raid"] = stats.get_plugin('raid').get_stats_display(
args=self.args)
ret["sensors"] = stats.get_plugin(
'sensors').get_stats_display(args=self.args)
ret["ports"] = stats.get_plugin(
'ports').get_stats_display(args=self.args)
ret["now"] = stats.get_plugin('now').get_stats_display()
ret["docker"] = stats.get_plugin('docker').get_stats_display(
args=self.args)
ret["processcount"] = stats.get_plugin(
'processcount').get_stats_display(args=self.args)
ret["amps"] = stats.get_plugin(
'amps').get_stats_display(args=self.args)
ret["alert"] = stats.get_plugin(
'alert').get_stats_display(args=self.args)
return ret
def display(self, stats, cs_status=None):
"""Display stats on the screen.
@@ -528,50 +539,12 @@ class _GlancesCurses(object):
# Update the client server status
self.args.cs_status = cs_status
stats_system = stats.get_plugin(
'system').get_stats_display(args=self.args)
stats_uptime = stats.get_plugin('uptime').get_stats_display()
if self.args.percpu:
stats_cpu = stats.get_plugin('percpu').get_stats_display(args=self.args)
else:
stats_cpu = stats.get_plugin('cpu').get_stats_display(args=self.args)
stats_load = stats.get_plugin('load').get_stats_display(args=self.args)
stats_mem = stats.get_plugin('mem').get_stats_display(args=self.args)
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_irq = stats.get_plugin('irq').get_stats_display(
args=self.args, max_width=plugin_max_width)
try:
stats_ip = stats.get_plugin('ip').get_stats_display(args=self.args)
except AttributeError:
stats_ip = None
stats_diskio = stats.get_plugin(
'diskio').get_stats_display(args=self.args)
stats_fs = stats.get_plugin('fs').get_stats_display(
args=self.args, max_width=plugin_max_width)
stats_folders = stats.get_plugin('folders').get_stats_display(
args=self.args, max_width=plugin_max_width)
stats_raid = stats.get_plugin('raid').get_stats_display(
args=self.args)
stats_sensors = stats.get_plugin(
'sensors').get_stats_display(args=self.args)
stats_ports = stats.get_plugin(
'ports').get_stats_display(args=self.args)
stats_now = stats.get_plugin('now').get_stats_display()
stats_docker = stats.get_plugin('docker').get_stats_display(
args=self.args)
stats_processcount = stats.get_plugin(
'processcount').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)
__stat_display = self.__get_stat_display(stats, plugin_max_width)
# Adapt number of processes to the available space
max_processes_displayed = screen_y - 11 - \
self.get_stats_display_height(stats_alert) - \
self.get_stats_display_height(stats_docker)
self.get_stats_display_height(__stat_display["alert"]) - \
self.get_stats_display_height(__stat_display["docker"])
try:
if self.args.enable_process_extended and not self.args.process_tree:
max_processes_displayed -= 4
@@ -584,7 +557,7 @@ class _GlancesCurses(object):
logger.debug("Set number of displayed processes to {}".format(max_processes_displayed))
glances_processes.max_processes = max_processes_displayed
stats_processlist = stats.get_plugin(
__stat_display["processlist"] = stats.get_plugin(
'processlist').get_stats_display(args=self.args)
# Display the stats on the curses interface
@@ -604,16 +577,19 @@ class _GlancesCurses(object):
# Space between column
self.space_between_column = 0
self.new_line()
l_uptime = self.get_stats_display_width(
stats_system) + self.space_between_column + self.get_stats_display_width(stats_ip) + 3 + self.get_stats_display_width(stats_uptime)
l_uptime = self.get_stats_display_width(__stat_display["system"]) \
+ self.space_between_column \
+ self.get_stats_display_width(__stat_display["ip"]) + 3 \
+ self.get_stats_display_width(__stat_display["uptime"])
self.display_plugin(
stats_system, display_optional=(screen_x >= l_uptime))
__stat_display["system"],
display_optional=(screen_x >= l_uptime))
self.new_column()
self.display_plugin(stats_ip)
self.display_plugin(__stat_display["ip"])
# Space between column
self.space_between_column = 3
self.new_column()
self.display_plugin(stats_uptime)
self.display_plugin(__stat_display["uptime"])
# ========================================================
# Display second line (<SUMMARY>+CPU|PERCPU+LOAD+MEM+SWAP)
@@ -622,36 +598,36 @@ class _GlancesCurses(object):
self.new_line()
# Init quicklook
stats_quicklook = {'msgdict': []}
__stat_display["quicklook"] = {'msgdict': []}
quicklook_width = 0
# Get stats for CPU, MEM, SWAP and LOAD (if needed)
if self.args.disable_cpu:
cpu_width = 0
else:
cpu_width = self.get_stats_display_width(stats_cpu)
cpu_width = self.get_stats_display_width(__stat_display["cpu"])
if self.args.disable_mem:
mem_width = 0
else:
mem_width = self.get_stats_display_width(stats_mem)
if self.args.disable_swap:
mem_width = self.get_stats_display_width(__stat_display["mem"])
if self.args.disable_memswap:
swap_width = 0
else:
swap_width = self.get_stats_display_width(stats_memswap)
swap_width = self.get_stats_display_width(__stat_display["memswap"])
if self.args.disable_load:
load_width = 0
else:
load_width = self.get_stats_display_width(stats_load)
load_width = self.get_stats_display_width(__stat_display["load"])
# Size of plugins but quicklook
stats_width = cpu_width + mem_width + swap_width + load_width
# Number of plugin but quicklook
stats_number = (
int(not self.args.disable_cpu and stats_cpu['msgdict'] != []) +
int(not self.args.disable_mem and stats_mem['msgdict'] != []) +
int(not self.args.disable_swap and stats_memswap['msgdict'] != []) +
int(not self.args.disable_load and stats_load['msgdict'] != []))
int(not self.args.disable_cpu and __stat_display["cpu"]['msgdict'] != []) +
int(not self.args.disable_mem and __stat_display["mem"]['msgdict'] != []) +
int(not self.args.disable_memswap and __stat_display["memswap"]['msgdict'] != []) +
int(not self.args.disable_load and __stat_display["load"]['msgdict'] != []))
if not self.args.disable_quicklook:
# Quick look is in the place !
@@ -660,15 +636,15 @@ class _GlancesCurses(object):
else:
quicklook_width = min(screen_x - (stats_width + 8 + stats_number * self.space_between_column), 79)
try:
stats_quicklook = stats.get_plugin(
__stat_display["quicklook"] = stats.get_plugin(
'quicklook').get_stats_display(max_width=quicklook_width, args=self.args)
except AttributeError as e:
logger.debug("Quicklook plugin not available (%s)" % e)
else:
quicklook_width = self.get_stats_display_width(stats_quicklook)
quicklook_width = self.get_stats_display_width(__stat_display["quicklook"])
stats_width += quicklook_width + 1
self.space_between_column = 1
self.display_plugin(stats_quicklook)
self.display_plugin(__stat_display["quicklook"])
self.new_column()
# Compute spaces between plugins
@@ -683,7 +659,7 @@ class _GlancesCurses(object):
if self.args.disable_mem:
mem_width = 0
else:
mem_width = self.get_stats_display_width(stats_mem, without_option=True)
mem_width = self.get_stats_display_width(__stat_display["mem"], without_option=True)
stats_width = quicklook_width + 1 + cpu_width + mem_width + swap_width + load_width
self.space_between_column = max(1, int((screen_x - stats_width) / (stats_number - 1)))
# No space again ? Remove optionnal CPU stats
@@ -692,20 +668,20 @@ class _GlancesCurses(object):
if self.args.disable_cpu:
cpu_width = 0
else:
cpu_width = self.get_stats_display_width(stats_cpu, without_option=True)
cpu_width = self.get_stats_display_width(__stat_display["cpu"], without_option=True)
stats_width = quicklook_width + 1 + cpu_width + mem_width + swap_width + load_width
self.space_between_column = max(1, int((screen_x - stats_width) / (stats_number - 1)))
else:
self.space_between_column = 0
# Display CPU, MEM, SWAP and LOAD
self.display_plugin(stats_cpu, display_optional=display_optional_cpu)
self.display_plugin(__stat_display["cpu"], display_optional=display_optional_cpu)
self.new_column()
self.display_plugin(stats_mem, display_optional=display_optional_mem)
self.display_plugin(__stat_display["mem"], display_optional=display_optional_mem)
self.new_column()
self.display_plugin(stats_memswap)
self.display_plugin(__stat_display["memswap"])
self.new_column()
self.display_plugin(stats_load)
self.display_plugin(__stat_display["load"])
# Space between column
self.space_between_column = 3
@@ -718,13 +694,24 @@ 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_folder and
self.args.enable_irq and
self.args.disable_folders 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 (__stat_display["network"],
__stat_display["wifi"],
__stat_display["ports"],
__stat_display["diskio"],
__stat_display["fs"],
__stat_display["irq"],
__stat_display["folders"],
__stat_display["raid"],
__stat_display["sensors"],
__stat_display["now"]):
self.new_line()
self.display_plugin(s)
@@ -740,18 +727,18 @@ class _GlancesCurses(object):
# DOCKER+PROCESS_COUNT+AMPS+PROCESS_LIST+ALERT
self.new_column()
self.new_line()
self.display_plugin(stats_docker)
self.display_plugin(__stat_display["docker"])
self.new_line()
self.display_plugin(stats_processcount)
self.display_plugin(__stat_display["processcount"])
self.new_line()
self.display_plugin(stats_amps)
self.display_plugin(__stat_display["amps"])
self.new_line()
self.display_plugin(stats_processlist,
self.display_plugin(__stat_display["processlist"],
display_optional=(screen_x > 102),
display_additional=(not OSX),
max_y=(screen_y - self.get_stats_display_height(stats_alert) - 2))
max_y=(screen_y - self.get_stats_display_height(__stat_display["alert"]) - 2))
self.new_line()
self.display_plugin(stats_alert)
self.display_plugin(__stat_display["alert"])
# History option
# Generate history graph

View File

@@ -4,6 +4,7 @@ var mainBowerFiles = require('main-bower-files');
var ngAnnotate = require('gulp-ng-annotate');
var templateCache = require('gulp-angular-templatecache');
var del = require('del');
var rename = require('gulp-rename');
gulp.task('clean', function() {
del('./public/*')
@@ -14,6 +15,7 @@ gulp.task('copy', function() {
.pipe(gulp.dest('./public'));
gulp.src('./css/*.css')
.pipe(rename({suffix: '.min'}))
.pipe(gulp.dest('./public/css'));
gulp.src('./images/*.png')
@@ -26,6 +28,7 @@ gulp.task('copy', function() {
gulp.task('bower', function() {
return gulp.src(mainBowerFiles())
.pipe(concat('vendor.js'))
.pipe(rename({suffix: '.min'}))
.pipe(gulp.dest('./public/js'))
});
@@ -33,12 +36,14 @@ gulp.task('build-js', function() {
return gulp.src('./js/**/*.js')
.pipe(ngAnnotate())
.pipe(concat('main.js'))
.pipe(rename({suffix: '.min'}))
.pipe(gulp.dest('./public/js'))
});
gulp.task('template', function () {
return gulp.src('./html/plugins/*.html')
.pipe(templateCache('templates.js', {'root': 'plugins/', 'module': 'glancesApp'}))
.pipe(rename({suffix: '.min'}))
.pipe(gulp.dest('./public/js'));
});

View File

@@ -8,13 +8,13 @@
<base href="/">
<link rel="icon" type="image/x-icon" href="favicon.ico" />
<link rel="stylesheet" type="text/css" href="css/normalize.css" />
<link rel="stylesheet" type="text/css" href="css/normalize.min.css" />
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="css/style.css" />
<link rel="stylesheet" type="text/css" href="css/style.min.css" />
<script type="text/javascript" src="js/vendor.js"></script>
<script type="text/javascript" src="js/main.js"></script>
<script type="text/javascript" src="js/templates.js"></script>
<script type="text/javascript" src="js/vendor.min.js"></script>
<script type="text/javascript" src="js/main.min.js"></script>
<script type="text/javascript" src="js/templates.min.js"></script>
</head>
<body ng-view="" ng-keydown="onKeyDown($event)">

View File

@@ -0,0 +1,10 @@
<div class="table-row">
<div class="table-cell text-left title">WIFI</div>
<div class="table-cell"></div>
<div class="table-cell">dBm</div>
</div>
<div class="table-row" ng-repeat="hotspot in statsWifi.hotspots | orderBy: 'ssid'">
<div class="table-cell text-left">{{ hotspot.ssid|limitTo:20 }} <span ng-if="hotspot.encrypted">{{ hotspot.encryption_type }}</span></div>
<div class="table-cell"></div>
<div class="table-cell" ng-class="statsWifi.getDecoration(hotspot, 'signal')">{{ hotspot.signal }}</div>
</div>

View File

@@ -35,7 +35,7 @@
<div class="hidden-xs hidden-sm col-md-4 col-lg-3" ng-if="!arguments.disable_mem">
<section id="mem_more" class="plugin" ng-include src="'plugins/mem_more.html'"></section>
</div>
<div class="col-sm-6 col-md-4 col-lg-3" ng-if="!arguments.disable_swap">
<div class="col-sm-6 col-md-4 col-lg-3" ng-if="!arguments.disable_memswap">
<section id="memswap" class="plugin" ng-include src="'plugins/memswap.html'"></section>
</div>
<div class="col-sm-6 col-md-4 col-lg-3" ng-if="!arguments.disable_load">
@@ -46,20 +46,21 @@
<div class="col-sm-6 sidebar" ng-show="!arguments.disable_left_sidebar">
<div class="table">
<section id="network" class="plugin table-row-group" ng-show="!arguments.disable_network" ng-include src="'plugins/network.html'"></section>
<section id="wifi" class="plugin table-row-group" ng-show="!arguments.disable_wifi && statsWifi.hotspots.length > 0" ng-include src="'plugins/wifi.html'"></section>
<section id="ports" class="plugin table-row-group" ng-show="!arguments.disable_ports" ng-include src="'plugins/ports.html'"></section>
<section id="diskio" class="plugin table-row-group" ng-show="!arguments.disable_diskio && statsDiskio.disks.length > 0" ng-include src="'plugins/diskio.html'"></section>
<section id="fs" class="plugin table-row-group" ng-show="!arguments.disable_fs" ng-include src="'plugins/fs.html'"></section>
<section id="irq" class="plugin table-row-group" ng-show="!arguments.disable_irq" ng-include src="'plugins/irq.html'"></section>
<section id="folders" class="plugin table-row-group" ng-show="!arguments.disable_fs && statsFolders.folders.length > 0" ng-include src="'plugins/folders.html'"></section>
<section id="irq" class="plugin table-row-group" ng-show="arguments.enable_irq && statsIrq.irqs.length > 0" ng-include src="'plugins/irq.html'"></section>
<section id="folders" class="plugin table-row-group" ng-show="!arguments.disable_fs && statsFolders.folders.length > 0" ng-include src="'plugins/folders.html'"></section>
<section id="raid" class="plugin table-row-group" ng-show="statsRaid.hasDisks()" ng-include src="'plugins/raid.html'"></section>
<section id="sensors" class="plugin table-row-group" ng-show="!arguments.disable_sensors && statsSensors.sensors.length > 0" ng-include src="'plugins/sensors.html'"></section>
</div>
</div>
<div class="col-sm-18">
<section id="containers" class="plugin" ng-show="statsDocker.containers.length && !arguments.disable_docker" ng-include src="'plugins/docker.html'"></section>
<section id="alerts" ng-show="!arguments.disable_log" ng-include src="'plugins/alerts.html'"></section>
<section id="alert" class="plugin" ng-show="!arguments.disable_log" ng-include src="'plugins/alert.html'"></section>
<section id="alerts" ng-show="!arguments.disable_alert" ng-include src="'plugins/alerts.html'"></section>
<section id="alert" class="plugin" ng-show="!arguments.disable_alert" ng-include src="'plugins/alert.html'"></section>
<div ng-show="!arguments.disable_process">
<section id="processcount" class="plugin" ng-include src="'plugins/processcount.html'"></section>
<div class="row" ng-if="!arguments.disable_amps">

View File

@@ -43,6 +43,7 @@ glancesApp.controller('statsController', function ($scope, $rootScope, $interval
$scope.statsSystem = GlancesStats.getPlugin('system');
$scope.statsUptime = GlancesStats.getPlugin('uptime');
$scope.statsPorts = GlancesStats.getPlugin('ports');
$scope.statsWifi = GlancesStats.getPlugin('wifi');
$rootScope.title = $scope.statsSystem.hostname + ' - Glances';
@@ -112,7 +113,7 @@ glancesApp.controller('statsController', function ($scope, $rootScope, $interval
break;
case $event.shiftKey && $event.keyCode == keycodes.Q:
// Q => Show/hide IRQ
$scope.arguments.disable_irq = !$scope.arguments.disable_irq;
$scope.arguments.enable_irq = !$scope.arguments.enable_irq;
break;
case !$event.shiftKey && $event.keyCode == keycodes.f:
// f => Show/hide filesystem stats
@@ -152,7 +153,7 @@ glancesApp.controller('statsController', function ($scope, $rootScope, $interval
break;
case !$event.shiftKey && $event.keyCode == keycodes.l:
// l => Show/hide alert logs
$scope.arguments.disable_log = !$scope.arguments.disable_log;
$scope.arguments.disable_alert = !$scope.arguments.disable_alert;
break;
case $event.shiftKey && $event.keyCode == keycodes.ONE:
// 1 => Global CPU or per-CPU stats
@@ -182,7 +183,7 @@ glancesApp.controller('statsController', function ($scope, $rootScope, $interval
$scope.arguments.disable_quicklook = !$scope.arguments.disable_quicklook;
$scope.arguments.disable_cpu = !$scope.arguments.disable_cpu;
$scope.arguments.disable_mem = !$scope.arguments.disable_mem;
$scope.arguments.disable_swap = !$scope.arguments.disable_swap;
$scope.arguments.disable_memswap = !$scope.arguments.disable_memswap;
$scope.arguments.disable_load = !$scope.arguments.disable_load;
break;
case $event.shiftKey && $event.keyCode == keycodes.i:
@@ -193,6 +194,10 @@ glancesApp.controller('statsController', function ($scope, $rootScope, $interval
// I => Enable/disable ports module
$scope.arguments.disable_ports = !$scope.arguments.disable_ports;
break;
case $event.shiftKey && $event.keyCode == keycodes.w:
// 'W' > Enable/Disable Wifi plugin
$scope.arguments.disable_wifi = !$scope.arguments.disable_wifi;
break;
}
};
});

View File

@@ -23,7 +23,8 @@ glancesApp.service('GlancesStats', function($http, $injector, $q, GlancesPlugin)
'sensors': 'GlancesPluginSensors',
'system': 'GlancesPluginSystem',
'uptime': 'GlancesPluginUptime',
'ports': 'GlancesPluginPorts'
'ports': 'GlancesPluginPorts',
'wifi': 'GlancesPluginWifi'
};
this.getData = function() {

View File

@@ -12,7 +12,7 @@ glancesApp.service('GlancesPluginIrq', function() {
var irq = {
'irq_line': IrqData['irq_line'],
'irq_rate': IrqData['irq_rate']
'irq_rate': IrqData['irq_rate']
};
this.irqs.push(irq);

View File

@@ -3,7 +3,7 @@ glancesApp.service('GlancesPluginRaid', function () {
this.disks = [];
this.setData = function (data, views) {
this.disks = [];
var disks = [];
data = data[_pluginName];
_.forIn(data, function(diskData, diskKey) {
@@ -26,8 +26,10 @@ glancesApp.service('GlancesPluginRaid', function () {
});
});
this.disks.push(disk);
}, this);
disks.push(disk);
});
this.disks = disks;
};
this.hasDisks = function() {

View File

@@ -0,0 +1,36 @@
glancesApp.service('GlancesPluginWifi', function() {
var _pluginName = "wifi";
var _view = {};
this.hotspots = [];
this.setData = function(data, views) {
data = data[_pluginName];
_view = views[_pluginName];
this.hotspots = [];
for (var i = 0; i < data.length; i++) {
var hotspotData = data[i];
if (hotspotData['ssid'] === '') {
continue;
}
var hotspot = {
'ssid': hotspotData['ssid'],
'encrypted': hotspotData['encrypted'],
'signal': hotspotData['signal'],
'encryption_type': hotspotData['encryption_type'],
};
this.hotspots.push(hotspot);
}
};
this.getDecoration = function(hotpost, field) {
if(_view[hotpost.ssid][field] == undefined) {
return;
}
return _view[hotpost.ssid][field].decoration.toLowerCase();
};
});

View File

@@ -30,5 +30,5 @@ var keycodes = {
'r' : '82',
'q' : '81',
'A' : '65',
'R' : '82',
'Q' : '81'
}

View File

@@ -8,6 +8,7 @@
"gulp-angular-templatecache": "^2.0.0",
"gulp-concat": "^2.6.0",
"gulp-ng-annotate": "^2.0.0",
"gulp-rename": "^1.2.2",
"main-bower-files": "^2.13.1"
},
"scripts": {

View File

@@ -62,11 +62,15 @@ body {
}
.ok, .status, .process {
color: #3E7B04;
font-weight: bold;
/*font-weight: bold;*/
}
.ok_log {
background-color: #3E7B04;
color: white;
/*font-weight: bold;*/
}
.max {
color: #3E7B04;
font-weight: bold;
}
.careful {

View File

Binary file not shown.

View File

@@ -27,6 +27,210 @@ var glancesApp = angular.module('glancesApp', ['ngRoute'])
$rootScope.title = "Glances";
}]);
glancesApp.controller('statsController', ["$scope", "$rootScope", "$interval", "GlancesStats", "help", "arguments", "favicoService", function ($scope, $rootScope, $interval, GlancesStats, help, arguments, favicoService) {
$scope.help = help;
$scope.arguments = arguments;
$scope.sorter = {
column: "cpu_percent",
auto: true,
isReverseColumn: function (column) {
return !(column == 'username' || column == 'name');
},
getColumnLabel: function (column) {
if (_.isEqual(column, ['io_read', 'io_write'])) {
return 'io_counters';
} else {
return column;
}
}
};
$scope.dataLoaded = false;
$scope.refreshData = function () {
GlancesStats.getData().then(function (data) {
$scope.statsAlert = GlancesStats.getPlugin('alert');
$scope.statsCpu = GlancesStats.getPlugin('cpu');
$scope.statsDiskio = GlancesStats.getPlugin('diskio');
$scope.statsIrq = GlancesStats.getPlugin('irq');
$scope.statsDocker = GlancesStats.getPlugin('docker');
$scope.statsFs = GlancesStats.getPlugin('fs');
$scope.statsFolders = GlancesStats.getPlugin('folders');
$scope.statsIp = GlancesStats.getPlugin('ip');
$scope.statsLoad = GlancesStats.getPlugin('load');
$scope.statsMem = GlancesStats.getPlugin('mem');
$scope.statsMemSwap = GlancesStats.getPlugin('memswap');
$scope.statsAmps = GlancesStats.getPlugin('amps');
$scope.statsNetwork = GlancesStats.getPlugin('network');
$scope.statsPerCpu = GlancesStats.getPlugin('percpu');
$scope.statsProcessCount = GlancesStats.getPlugin('processcount');
$scope.statsProcessList = GlancesStats.getPlugin('processlist');
$scope.statsQuicklook = GlancesStats.getPlugin('quicklook');
$scope.statsRaid = GlancesStats.getPlugin('raid');
$scope.statsSensors = GlancesStats.getPlugin('sensors');
$scope.statsSystem = GlancesStats.getPlugin('system');
$scope.statsUptime = GlancesStats.getPlugin('uptime');
$scope.statsPorts = GlancesStats.getPlugin('ports');
$scope.statsWifi = GlancesStats.getPlugin('wifi');
$rootScope.title = $scope.statsSystem.hostname + ' - Glances';
if ($scope.statsAlert.hasOngoingAlerts()) {
favicoService.badge($scope.statsAlert.countOngoingAlerts());
} else {
favicoService.reset();
}
$scope.is_disconnected = false;
$scope.dataLoaded = true;
}, function() {
$scope.is_disconnected = true;
});
};
$scope.refreshData();
$interval(function () {
$scope.refreshData();
}, arguments.time * 1000); // in milliseconds
$scope.onKeyDown = function ($event) {
switch (true) {
case !$event.shiftKey && $event.keyCode == keycodes.a:
// a => Sort processes automatically
$scope.sorter.column = "cpu_percent";
$scope.sorter.auto = true;
break;
case $event.shiftKey && $event.keyCode == keycodes.A:
// A => Enable/disable AMPs
$scope.arguments.disable_amps = !$scope.arguments.disable_amps;
break;
case !$event.shiftKey && $event.keyCode == keycodes.c:
// c => Sort processes by CPU%
$scope.sorter.column = "cpu_percent";
$scope.sorter.auto = false;
break;
case !$event.shiftKey && $event.keyCode == keycodes.m:
// m => Sort processes by MEM%
$scope.sorter.column = "memory_percent";
$scope.sorter.auto = false;
break;
case !$event.shiftKey && $event.keyCode == keycodes.u:
// u => Sort processes by user
$scope.sorter.column = "username";
$scope.sorter.auto = false;
break;
case !$event.shiftKey && $event.keyCode == keycodes.p:
// p => Sort processes by name
$scope.sorter.column = "name";
$scope.sorter.auto = false;
break;
case !$event.shiftKey && $event.keyCode == keycodes.i:
// i => Sort processes by I/O rate
$scope.sorter.column = ['io_read', 'io_write'];
$scope.sorter.auto = false;
break;
case !$event.shiftKey && $event.keyCode == keycodes.t:
// t => Sort processes by time
$scope.sorter.column = "timemillis";
$scope.sorter.auto = false;
break;
case !$event.shiftKey && $event.keyCode == keycodes.d:
// d => Show/hide disk I/O stats
$scope.arguments.disable_diskio = !$scope.arguments.disable_diskio;
break;
case $event.shiftKey && $event.keyCode == keycodes.Q:
// Q => Show/hide IRQ
$scope.arguments.enable_irq = !$scope.arguments.enable_irq;
break;
case !$event.shiftKey && $event.keyCode == keycodes.f:
// f => Show/hide filesystem stats
$scope.arguments.disable_fs = !$scope.arguments.disable_fs;
break;
case !$event.shiftKey && $event.keyCode == keycodes.n:
// n => Show/hide network stats
$scope.arguments.disable_network = !$scope.arguments.disable_network;
break;
case !$event.shiftKey && $event.keyCode == keycodes.s:
// s => Show/hide sensors stats
$scope.arguments.disable_sensors = !$scope.arguments.disable_sensors;
break;
case $event.shiftKey && $event.keyCode == keycodes.TWO:
// 2 => Show/hide left sidebar
$scope.arguments.disable_left_sidebar = !$scope.arguments.disable_left_sidebar;
break;
case !$event.shiftKey && $event.keyCode == keycodes.z:
// z => Enable/disable processes stats
$scope.arguments.disable_process = !$scope.arguments.disable_process;
break;
case $event.keyCode == keycodes.SLASH:
// SLASH => Enable/disable short processes name
$scope.arguments.process_short_name = !$scope.arguments.process_short_name;
break;
case $event.shiftKey && $event.keyCode == keycodes.D:
// D => Enable/disable Docker stats
$scope.arguments.disable_docker = !$scope.arguments.disable_docker;
break;
case !$event.shiftKey && $event.keyCode == keycodes.b:
// b => Bytes or bits for network I/O
$scope.arguments.byte = !$scope.arguments.byte;
break;
case $event.shiftKey && $event.keyCode == keycodes.b:
// 'B' => Switch between bit/s and IO/s for Disk IO
$scope.arguments.diskio_iops = !$scope.arguments.diskio_iops;
break;
case !$event.shiftKey && $event.keyCode == keycodes.l:
// l => Show/hide alert logs
$scope.arguments.disable_alert = !$scope.arguments.disable_alert;
break;
case $event.shiftKey && $event.keyCode == keycodes.ONE:
// 1 => Global CPU or per-CPU stats
$scope.arguments.percpu = !$scope.arguments.percpu;
break;
case !$event.shiftKey && $event.keyCode == keycodes.h:
// h => Show/hide this help screen
$scope.arguments.help_tag = !$scope.arguments.help_tag;
break;
case $event.shiftKey && $event.keyCode == keycodes.T:
// T => View network I/O as combination
$scope.arguments.network_sum = !$scope.arguments.network_sum;
break;
case $event.shiftKey && $event.keyCode == keycodes.u:
// U => View cumulative network I/O
$scope.arguments.network_cumul = !$scope.arguments.network_cumul;
break;
case $event.shiftKey && $event.keyCode == keycodes.f:
// F => Show filesystem free space
$scope.arguments.fs_free_space = !$scope.arguments.fs_free_space;
break;
case $event.shiftKey && $event.keyCode == keycodes.THREE:
// 3 => Enable/disable quick look plugin
$scope.arguments.disable_quicklook = !$scope.arguments.disable_quicklook;
break;
case $event.shiftKey && $event.keyCode == keycodes.FIVE:
$scope.arguments.disable_quicklook = !$scope.arguments.disable_quicklook;
$scope.arguments.disable_cpu = !$scope.arguments.disable_cpu;
$scope.arguments.disable_mem = !$scope.arguments.disable_mem;
$scope.arguments.disable_memswap = !$scope.arguments.disable_memswap;
$scope.arguments.disable_load = !$scope.arguments.disable_load;
break;
case $event.shiftKey && $event.keyCode == keycodes.i:
// I => Show/hide IP module
$scope.arguments.disable_ip = !$scope.arguments.disable_ip;
break;
case $event.shiftKey && $event.keyCode == keycodes.p:
// I => Enable/disable ports module
$scope.arguments.disable_ports = !$scope.arguments.disable_ports;
break;
case $event.shiftKey && $event.keyCode == keycodes.w:
// 'W' > Enable/Disable Wifi plugin
$scope.arguments.disable_wifi = !$scope.arguments.disable_wifi;
break;
}
};
}]);
glancesApp.directive("sortableTh", function() {
return {
restrict: 'A',
@@ -177,199 +381,6 @@ glancesApp.filter('timedelta', ["$filter", function($filter) {
}
}]);
glancesApp.controller('statsController', ["$scope", "$rootScope", "$interval", "GlancesStats", "help", "arguments", function ($scope, $rootScope, $interval, GlancesStats, help, arguments) {
$scope.help = help;
$scope.arguments = arguments;
$scope.sorter = {
column: "cpu_percent",
auto: true,
isReverseColumn: function (column) {
return !(column == 'username' || column == 'name');
},
getColumnLabel: function (column) {
if (_.isEqual(column, ['io_read', 'io_write'])) {
return 'io_counters';
} else {
return column;
}
}
};
$scope.dataLoaded = false;
$scope.refreshData = function () {
GlancesStats.getData().then(function (data) {
$scope.statsAlert = GlancesStats.getPlugin('alert');
$scope.statsCpu = GlancesStats.getPlugin('cpu');
$scope.statsDiskio = GlancesStats.getPlugin('diskio');
$scope.statsIrq = GlancesStats.getPlugin('irq');
$scope.statsDocker = GlancesStats.getPlugin('docker');
$scope.statsFs = GlancesStats.getPlugin('fs');
$scope.statsFolders = GlancesStats.getPlugin('folders');
$scope.statsIp = GlancesStats.getPlugin('ip');
$scope.statsLoad = GlancesStats.getPlugin('load');
$scope.statsMem = GlancesStats.getPlugin('mem');
$scope.statsMemSwap = GlancesStats.getPlugin('memswap');
$scope.statsAmps = GlancesStats.getPlugin('amps');
$scope.statsNetwork = GlancesStats.getPlugin('network');
$scope.statsPerCpu = GlancesStats.getPlugin('percpu');
$scope.statsProcessCount = GlancesStats.getPlugin('processcount');
$scope.statsProcessList = GlancesStats.getPlugin('processlist');
$scope.statsQuicklook = GlancesStats.getPlugin('quicklook');
$scope.statsRaid = GlancesStats.getPlugin('raid');
$scope.statsSensors = GlancesStats.getPlugin('sensors');
$scope.statsSystem = GlancesStats.getPlugin('system');
$scope.statsUptime = GlancesStats.getPlugin('uptime');
$scope.statsPorts = GlancesStats.getPlugin('ports');
$rootScope.title = $scope.statsSystem.hostname + ' - Glances';
$scope.is_disconnected = false;
$scope.dataLoaded = true;
}, function() {
$scope.is_disconnected = true;
});
};
$scope.refreshData();
$interval(function () {
$scope.refreshData();
}, arguments.time * 1000); // in milliseconds
$scope.onKeyDown = function ($event) {
switch (true) {
case !$event.shiftKey && $event.keyCode == keycodes.a:
// a => Sort processes automatically
$scope.sorter.column = "cpu_percent";
$scope.sorter.auto = true;
break;
case $event.shiftKey && $event.keyCode == keycodes.A:
// A => Enable/disable AMPs
$scope.arguments.disable_amps = !$scope.arguments.disable_amps;
break;
case !$event.shiftKey && $event.keyCode == keycodes.c:
// c => Sort processes by CPU%
$scope.sorter.column = "cpu_percent";
$scope.sorter.auto = false;
break;
case !$event.shiftKey && $event.keyCode == keycodes.m:
// m => Sort processes by MEM%
$scope.sorter.column = "memory_percent";
$scope.sorter.auto = false;
break;
case !$event.shiftKey && $event.keyCode == keycodes.u:
// u => Sort processes by user
$scope.sorter.column = "username";
$scope.sorter.auto = false;
break;
case !$event.shiftKey && $event.keyCode == keycodes.p:
// p => Sort processes by name
$scope.sorter.column = "name";
$scope.sorter.auto = false;
break;
case !$event.shiftKey && $event.keyCode == keycodes.i:
// i => Sort processes by I/O rate
$scope.sorter.column = ['io_read', 'io_write'];
$scope.sorter.auto = false;
break;
case !$event.shiftKey && $event.keyCode == keycodes.t:
// t => Sort processes by time
$scope.sorter.column = "timemillis";
$scope.sorter.auto = false;
break;
case !$event.shiftKey && $event.keyCode == keycodes.d:
// d => Show/hide disk I/O stats
$scope.arguments.disable_diskio = !$scope.arguments.disable_diskio;
break;
case $event.shiftKey && $event.keyCode == keycodes.Q:
// R => Show/hide IRQ
$scope.arguments.disable_irq = !$scope.arguments.disable_irq;
break;
case !$event.shiftKey && $event.keyCode == keycodes.f:
// f => Show/hide filesystem stats
$scope.arguments.disable_fs = !$scope.arguments.disable_fs;
break;
case !$event.shiftKey && $event.keyCode == keycodes.n:
// n => Show/hide network stats
$scope.arguments.disable_network = !$scope.arguments.disable_network;
break;
case !$event.shiftKey && $event.keyCode == keycodes.s:
// s => Show/hide sensors stats
$scope.arguments.disable_sensors = !$scope.arguments.disable_sensors;
break;
case $event.shiftKey && $event.keyCode == keycodes.TWO:
// 2 => Show/hide left sidebar
$scope.arguments.disable_left_sidebar = !$scope.arguments.disable_left_sidebar;
break;
case !$event.shiftKey && $event.keyCode == keycodes.z:
// z => Enable/disable processes stats
$scope.arguments.disable_process = !$scope.arguments.disable_process;
break;
case $event.keyCode == keycodes.SLASH:
// SLASH => Enable/disable short processes name
$scope.arguments.process_short_name = !$scope.arguments.process_short_name;
break;
case $event.shiftKey && $event.keyCode == keycodes.D:
// D => Enable/disable Docker stats
$scope.arguments.disable_docker = !$scope.arguments.disable_docker;
break;
case !$event.shiftKey && $event.keyCode == keycodes.b:
// b => Bytes or bits for network I/O
$scope.arguments.byte = !$scope.arguments.byte;
break;
case $event.shiftKey && $event.keyCode == keycodes.b:
// 'B' => Switch between bit/s and IO/s for Disk IO
$scope.arguments.diskio_iops = !$scope.arguments.diskio_iops;
break;
case !$event.shiftKey && $event.keyCode == keycodes.l:
// l => Show/hide alert logs
$scope.arguments.disable_log = !$scope.arguments.disable_log;
break;
case $event.shiftKey && $event.keyCode == keycodes.ONE:
// 1 => Global CPU or per-CPU stats
$scope.arguments.percpu = !$scope.arguments.percpu;
break;
case !$event.shiftKey && $event.keyCode == keycodes.h:
// h => Show/hide this help screen
$scope.arguments.help_tag = !$scope.arguments.help_tag;
break;
case $event.shiftKey && $event.keyCode == keycodes.T:
// T => View network I/O as combination
$scope.arguments.network_sum = !$scope.arguments.network_sum;
break;
case $event.shiftKey && $event.keyCode == keycodes.u:
// U => View cumulative network I/O
$scope.arguments.network_cumul = !$scope.arguments.network_cumul;
break;
case $event.shiftKey && $event.keyCode == keycodes.f:
// F => Show filesystem free space
$scope.arguments.fs_free_space = !$scope.arguments.fs_free_space;
break;
case $event.shiftKey && $event.keyCode == keycodes.THREE:
// 3 => Enable/disable quick look plugin
$scope.arguments.disable_quicklook = !$scope.arguments.disable_quicklook;
break;
case $event.shiftKey && $event.keyCode == keycodes.FIVE:
$scope.arguments.disable_quicklook = !$scope.arguments.disable_quicklook;
$scope.arguments.disable_cpu = !$scope.arguments.disable_cpu;
$scope.arguments.disable_mem = !$scope.arguments.disable_mem;
$scope.arguments.disable_swap = !$scope.arguments.disable_swap;
$scope.arguments.disable_load = !$scope.arguments.disable_load;
break;
case $event.shiftKey && $event.keyCode == keycodes.i:
// I => Show/hide IP module
$scope.arguments.disable_ip = !$scope.arguments.disable_ip;
break;
case $event.shiftKey && $event.keyCode == keycodes.p:
// I => Enable/disable ports module
$scope.arguments.disable_ports = !$scope.arguments.disable_ports;
break;
}
};
}]);
var keycodes = {
'a' : '65',
'c' : '67',
@@ -405,6 +416,21 @@ var keycodes = {
'R' : '82',
}
glancesApp.service('favicoService', function() {
var favico = new Favico({
animation : 'none'
});
this.badge = function(nb) {
favico.badge(nb);
};
this.reset = function() {
favico.reset();
};
});
glancesApp.service('GlancesStats', ["$http", "$injector", "$q", "GlancesPlugin", function($http, $injector, $q, GlancesPlugin) {
var _stats = [], _views = [], _limits = [];
@@ -412,7 +438,7 @@ glancesApp.service('GlancesStats', ["$http", "$injector", "$q", "GlancesPlugin",
'alert': 'GlancesPluginAlert',
'cpu': 'GlancesPluginCpu',
'diskio': 'GlancesPluginDiskio',
'irq' : 'GlancesPluginIrq',
'irq' : 'GlancesPluginIrq',
'docker': 'GlancesPluginDocker',
'ip': 'GlancesPluginIp',
'fs': 'GlancesPluginFs',
@@ -430,7 +456,8 @@ glancesApp.service('GlancesStats', ["$http", "$injector", "$q", "GlancesPlugin",
'sensors': 'GlancesPluginSensors',
'system': 'GlancesPluginSystem',
'uptime': 'GlancesPluginUptime',
'ports': 'GlancesPluginPorts'
'ports': 'GlancesPluginPorts',
'wifi': 'GlancesPluginWifi'
};
this.getData = function() {
@@ -532,7 +559,7 @@ glancesApp.service('GlancesPluginAlert', function () {
, minutes = parseInt((duration / (1000 * 60)) % 60)
, hours = parseInt((duration / (1000 * 60 * 60)) % 24);
alert.duration = _.padLeft(hours, 2, '0') + ":" + _.padLeft(minutes, 2, '0') + ":" + _.padLeft(seconds, 2, '0');
alert.duration = _.padStart(hours, 2, '0') + ":" + _.padStart(minutes, 2, '0') + ":" + _.padStart(seconds, 2, '0');
}
_alerts.push(alert);
@@ -550,6 +577,14 @@ glancesApp.service('GlancesPluginAlert', function () {
this.count = function () {
return _alerts.length;
};
this.hasOngoingAlerts = function () {
return _.filter(_alerts, { 'ongoing': true }).length > 0;
};
this.countOngoingAlerts = function () {
return _.filter(_alerts, { 'ongoing': true }).length;
}
});
glancesApp.service('GlancesPluginAmps', function() {
@@ -824,7 +859,7 @@ glancesApp.service('GlancesPluginIrq', function() {
var irq = {
'irq_line': IrqData['irq_line'],
'irq_rate': IrqData['irq_rate']
'irq_rate': IrqData['irq_rate']
};
this.irqs.push(irq);
@@ -1176,7 +1211,7 @@ glancesApp.service('GlancesPluginRaid', function () {
this.disks = [];
this.setData = function (data, views) {
this.disks = [];
var disks = [];
data = data[_pluginName];
_.forIn(data, function(diskData, diskKey) {
@@ -1199,8 +1234,10 @@ glancesApp.service('GlancesPluginRaid', function () {
});
});
this.disks.push(disk);
}, this);
disks.push(disk);
});
this.disks = disks;
};
this.hasDisks = function() {
@@ -1287,3 +1324,40 @@ glancesApp.service('GlancesPluginUptime', function() {
this.uptime = data['uptime'];
};
});
glancesApp.service('GlancesPluginWifi', function() {
var _pluginName = "wifi";
var _view = {};
this.hotspots = [];
this.setData = function(data, views) {
data = data[_pluginName];
_view = views[_pluginName];
this.hotspots = [];
for (var i = 0; i < data.length; i++) {
var hotspotData = data[i];
if (hotspotData['ssid'] === '') {
continue;
}
var hotspot = {
'ssid': hotspotData['ssid'],
'encrypted': hotspotData['encrypted'],
'signal': hotspotData['signal'],
'encryption_type': hotspotData['encryption_type'],
};
this.hotspots.push(hotspot);
}
};
this.getDecoration = function(hotpost, field) {
if(_view[hotpost.ssid][field] == undefined) {
return;
}
return _view[hotpost.ssid][field].decoration.toLowerCase();
};
});

View File

@@ -21,4 +21,5 @@ $templateCache.put('plugins/quicklook.html','<div class="cpu-name">\n {{ stat
$templateCache.put('plugins/raid.html','<div class="table-row">\n <div class="table-cell text-left title">RAID disks</div>\n <div class="table-cell">Used</div>\n <div class="table-cell">Total</div>\n</div>\n<div class="table-row" ng-repeat="disk in statsRaid.disks | orderBy: \'name\'">\n <div class="table-cell text-left">\n {{ disk.type | uppercase }} {{ disk.name }}\n <div class="warning" ng-show="disk.degraded">\u2514\u2500 Degraded mode</div>\n <div ng-show="disk.degraded"> &nbsp; &nbsp;\u2514\u2500 {{ disk.config }}</div>\n\n <div class="critical" ng-show="disk.inactive">\u2514\u2500 Status {{ disk.status }}</div>\n <div ng-show="disk.inactive" ng-repeat="component in disk.components | orderBy: \'number\'">\n &nbsp; &nbsp;{{ $last ? \'\u2514\u2500\' : \'\u251C\u2500\' }} disk {{ component.number }}: {{ component.name }}\n </div>\n </div>\n <div class="table-cell" ng-show="!disk.inactive" ng-class="statsRaid.getAlert(disk)">{{ disk.used }}</div>\n <div class="table-cell" ng-show="!disk.inactive" ng-class="statsRaid.getAlert(disk)">{{ disk.available }}</div>\n</div>');
$templateCache.put('plugins/sensors.html','<div class="table-row">\n <div class="table-cell text-left title">SENSORS</div>\n</div>\n\n<div class="table-row" ng-repeat="sensor in statsSensors.sensors">\n <div class="table-cell text-left">{{ sensor.label }}</div>\n <div class="table-cell">{{ sensor.unit }}</div>\n <div class="table-cell" ng-class="statsSensors.getAlert(sensor)">{{ sensor.value }}</div>\n</div>\n');
$templateCache.put('plugins/system.html','<span ng-if="is_disconnected" class="critical">Disconnected from</span>\n<span class="title">{{ statsSystem.hostname }}</span>\n<span ng-show="statsSystem.isLinux()" class="hidden-xs hidden-sm">({{ statsSystem.humanReadableName }} / {{ statsSystem.os.name }} {{ statsSystem.os.version }})</span>\n<span ng-show="!statsSystem.isLinux()" class="hidden-xs hidden-sm">({{ statsSystem.os.name }} {{ statsSystem.os.version }} {{ statsSystem.platform }})</span>');
$templateCache.put('plugins/uptime.html','<span>Uptime: {{ statsUptime.uptime }}</span>\n');}]);
$templateCache.put('plugins/uptime.html','<span>Uptime: {{ statsUptime.uptime }}</span>\n');
$templateCache.put('plugins/wifi.html','<div class="table-row">\n <div class="table-cell text-left title">WIFI</div>\n <div class="table-cell"></div>\n <div class="table-cell">dBm</div>\n</div>\n<div class="table-row" ng-repeat="hotspot in statsWifi.hotspots | orderBy: \'ssid\'">\n <div class="table-cell text-left">{{ hotspot.ssid|limitTo:20 }} <span ng-if="hotspot.encrypted">{{ hotspot.encryption_type }}</span></div>\n <div class="table-cell"></div>\n <div class="table-cell" ng-class="statsWifi.getDecoration(hotspot, \'signal\')">{{ hotspot.signal }}</div>\n</div>\n');}]);

View File

File diff suppressed because it is too large Load Diff

View File

Binary file not shown.

View File

@@ -26,7 +26,6 @@ import sys
import uuid
from io import open
from glances import __appname__
from glances.compat import b, input
from glances.globals import BSD, LINUX, OSX, WINDOWS
from glances.logger import logger
@@ -59,7 +58,7 @@ class GlancesPassword(object):
app_path = '.'
# Append the Glances folder
app_path = os.path.join(app_path, __appname__)
app_path = os.path.join(app_path, 'glances')
return app_path

View File

@@ -60,7 +60,7 @@ class Plugin(GlancesPlugin):
ret = []
# Only process if display plugin enable...
if args.disable_log:
if not self.stats and self.is_disable():
return ret
# Build the string message

View File

@@ -21,7 +21,6 @@
from glances.compat import iteritems
from glances.amps_list import AmpsList as glancesAmpsList
from glances.logger import logger
from glances.plugins.glances_plugin import GlancesPlugin
@@ -48,6 +47,7 @@ class Plugin(GlancesPlugin):
"""Reset/init the stats."""
self.stats = []
@GlancesPlugin._check_decorator
@GlancesPlugin._log_result_decorator
def update(self):
"""Update the AMP list."""
@@ -97,7 +97,7 @@ class Plugin(GlancesPlugin):
# Only process if stats exist and display plugin enable...
ret = []
if not self.stats or args.disable_process or args.disable_amps:
if not self.stats or args.disable_process or self.is_disable():
return ret
# Build the string message

View File

@@ -54,6 +54,7 @@ class Plugin(GlancesPlugin):
"""Reset/init the stats."""
self.stats = []
@GlancesPlugin._check_decorator
@GlancesPlugin._log_result_decorator
def update(self):
"""Update battery capacity stats using the input method."""

View File

@@ -84,9 +84,11 @@ class Plugin(GlancesPlugin):
"""Reset/init the stats."""
self.stats = {}
@GlancesPlugin._check_decorator
@GlancesPlugin._log_result_decorator
def update(self):
"""Update CPU stats using the input method."""
# Reset stats
self.reset()
@@ -96,12 +98,6 @@ class Plugin(GlancesPlugin):
elif self.input_method == 'snmp':
self.update_snmp()
# Update the history list
self.update_stats_history()
# Update the view
self.update_views()
return self.stats
def update_local(self):
@@ -140,7 +136,8 @@ class Plugin(GlancesPlugin):
self.cpu_stats_old = cpu_stats
else:
for stat in cpu_stats._fields:
self.stats[stat] = getattr(cpu_stats, stat) - getattr(self.cpu_stats_old, stat)
if getattr(cpu_stats, stat) is not None:
self.stats[stat] = getattr(cpu_stats, stat) - getattr(self.cpu_stats_old, stat)
self.stats['time_since_update'] = time_since_update
@@ -223,7 +220,7 @@ class Plugin(GlancesPlugin):
ret = []
# Only process if stats exist and plugin not disable
if not self.stats or args.disable_cpu:
if not self.stats or self.is_disable():
return ret
# Build the string message

View File

@@ -65,6 +65,7 @@ class Plugin(GlancesPlugin):
"""Reset/init the stats."""
self.stats = []
@GlancesPlugin._check_decorator
@GlancesPlugin._log_result_decorator
def update(self):
"""Update disk I/O stats using the input method."""
@@ -141,12 +142,6 @@ class Plugin(GlancesPlugin):
# No standard way for the moment...
pass
# Update the history list
self.update_stats_history('disk_name')
# Update the view
self.update_views()
return self.stats
def update_views(self):
@@ -169,7 +164,7 @@ class Plugin(GlancesPlugin):
ret = []
# Only process if stats exist and display plugin enable...
if not self.stats or args.disable_diskio:
if not self.stats or self.is_disable():
return ret
# Build the string message

View File

@@ -66,6 +66,9 @@ class Plugin(GlancesPlugin):
# value: instance of ThreadDockerGrabber
self.thread_list = {}
# Init the stats
self.reset()
def exit(self):
"""Overwrite the exit method to close threads"""
for t in itervalues(self.thread_list):
@@ -141,22 +144,28 @@ class Plugin(GlancesPlugin):
"""Reset/init the stats."""
self.stats = {}
@GlancesPlugin._check_decorator
@GlancesPlugin._log_result_decorator
def update(self):
"""Update Docker stats using the input method."""
global docker_tag
# Reset stats
self.reset()
# Get the current Docker API client
if not self.docker_client:
# First time, try to connect to the server
self.docker_client = self.connect()
if self.docker_client is None:
global docker_tag
try:
self.docker_client = self.connect()
except:
docker_tag = False
else:
if self.docker_client is None:
docker_tag = False
# The Docker-py lib is mandatory
if not docker_tag or (self.args is not None and self.args.disable_docker):
if not docker_tag:
return self.stats
if self.input_method == 'local':
@@ -356,7 +365,7 @@ class Plugin(GlancesPlugin):
network_new['tx'] = netcounters["eth0"]["tx_bytes"] - self.netcounters_old[container_id]["eth0"]["tx_bytes"]
network_new['cumulative_rx'] = netcounters["eth0"]["rx_bytes"]
network_new['cumulative_tx'] = netcounters["eth0"]["tx_bytes"]
except KeyError:
except KeyError as e:
# all_stats do not have INTERFACE information
logger.debug("Can not grab network interface usage for container {0} ({1})".format(container_id, e))
logger.debug(all_stats)
@@ -434,13 +443,59 @@ class Plugin(GlancesPlugin):
"""Return the user ticks by reading the environment variable."""
return os.sysconf(os.sysconf_names['SC_CLK_TCK'])
def get_stats_action(self):
"""Return stats for the action
Docker will return self.stats['containers']"""
return self.stats['containers']
def update_views(self):
"""Update stats views."""
# Call the father's method
super(Plugin, self).update_views()
if 'containers' not in self.stats:
return False
# Add specifics informations
# Alert
for i in self.stats['containers']:
# Init the views for the current container (key = container name)
self.views[i[self.get_key()]] = {'cpu': {}, 'mem': {}}
# CPU alert
if 'cpu' in i and 'total' in i['cpu']:
# Looking for specific CPU container threasold in the conf file
alert = self.get_alert(i['cpu']['total'],
header=i['name'] + '_cpu',
action_key=i['name'])
if alert == 'DEFAULT':
# Not found ? Get back to default CPU threasold value
alert = self.get_alert(i['cpu']['total'], header='cpu')
self.views[i[self.get_key()]]['cpu']['decoration'] = alert
# MEM alert
if 'memory' in i and 'usage' in i['memory']:
# Looking for specific MEM container threasold in the conf file
alert = self.get_alert(i['memory']['usage'],
maximum=i['memory']['limit'],
header=i['name'] + '_mem',
action_key=i['name'])
if alert == 'DEFAULT':
# Not found ? Get back to default MEM threasold value
alert = self.get_alert(i['memory']['usage'],
maximum=i['memory']['limit'],
header='mem')
self.views[i[self.get_key()]]['mem']['decoration'] = alert
return True
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 non null) and display plugin enable...
if not self.stats or args.disable_docker or len(self.stats['containers']) == 0:
if not self.stats \
or len(self.stats['containers']) == 0 \
or self.is_disable():
return ret
# Build the string message
@@ -502,13 +557,17 @@ class Plugin(GlancesPlugin):
msg = '{:>6.1f}'.format(container['cpu']['total'])
except KeyError:
msg = '{:>6}'.format('?')
ret.append(self.curse_add_line(msg))
ret.append(self.curse_add_line(msg, self.get_views(item=container['name'],
key='cpu',
option='decoration')))
# MEM
try:
msg = '{:>7}'.format(self.auto_unit(container['memory']['usage']))
except KeyError:
msg = '{:>7}'.format('?')
ret.append(self.curse_add_line(msg))
ret.append(self.curse_add_line(msg, self.get_views(item=container['name'],
key='mem',
option='decoration')))
try:
msg = '{:>7}'.format(self.auto_unit(container['memory']['limit']))
except KeyError:

View File

@@ -52,6 +52,8 @@ class Plugin(GlancesPlugin):
"""Load the foldered list from the config file, if it exists."""
self.glances_folders = glancesFolderList(config)
@GlancesPlugin._check_decorator
@GlancesPlugin._log_result_decorator
def update(self):
"""Update the foldered list."""
# Reset the list
@@ -96,7 +98,7 @@ class Plugin(GlancesPlugin):
ret = []
# Only process if stats exist and display plugin enable...
if not self.stats or args.disable_folder:
if not self.stats or self.is_disable():
return ret
# Build the string message

View File

@@ -89,6 +89,7 @@ class Plugin(GlancesPlugin):
"""Reset/init the stats."""
self.stats = []
@GlancesPlugin._check_decorator
@GlancesPlugin._log_result_decorator
def update(self):
"""Update the FS stats using the input method."""
@@ -179,12 +180,6 @@ class Plugin(GlancesPlugin):
'key': self.get_key()}
self.stats.append(fs_current)
# Update the history list
self.update_stats_history('mnt_point')
# Update the view
self.update_views()
return self.stats
def update_views(self):
@@ -204,7 +199,7 @@ class Plugin(GlancesPlugin):
ret = []
# Only process if stats exist and display plugin enable...
if not self.stats or args.disable_fs:
if not self.stats or self.is_disable():
return ret
# Max size for the fsname name

View File

@@ -52,6 +52,8 @@ class Plugin(GlancesPlugin):
"""Reset/init the stats."""
self.stats = []
@GlancesPlugin._check_decorator
@GlancesPlugin._log_result_decorator
def update(self):
"""Update HDD stats using the input method."""
# Reset stats
@@ -90,10 +92,6 @@ class GlancesGrabHDDTemp(object):
# Reset the list
self.reset()
# Only update if --disable-hddtemp is not set
if self.args is None or self.args.disable_hddtemp:
return
# Fetch the data
# data = ("|/dev/sda|WDC WD2500JS-75MHB0|44|C|"
# "|/dev/sdb|WDC WD2500JS-75MHB0|35|C|"
@@ -146,7 +144,8 @@ class GlancesGrabHDDTemp(object):
except socket.error as e:
logger.debug("Cannot connect to an HDDtemp server ({}:{} => {})".format(self.host, self.port, e))
logger.debug("Disable the HDDtemp module. Use the --disable-hddtemp to hide the previous message.")
self.args.disable_hddtemp = True
if self.args is not None:
self.args.disable_hddtemp = True
data = ""
finally:
sck.close()

View File

@@ -23,7 +23,7 @@ Help plugin.
Just a stupid plugin to display the help screen.
"""
from glances import __appname__, __version__, psutil_version
from glances import __version__, psutil_version
from glances.plugins.glances_plugin import GlancesPlugin
@@ -50,7 +50,7 @@ class Plugin(GlancesPlugin):
pass
def generate_view_data(self):
self.view_data['version'] = '{0} {1}'.format(__appname__.title(), __version__)
self.view_data['version'] = '{0} {1}'.format('Glances', __version__)
self.view_data['psutil_version'] = ' with PSutil {0}'.format(psutil_version)
try:

View File

@@ -22,7 +22,7 @@
import threading
from json import loads
from glances.compat import iterkeys, urlopen, URLError, queue
from glances.compat import iterkeys, urlopen, queue
from glances.globals import BSD
from glances.logger import logger
from glances.timer import Timer
@@ -75,6 +75,7 @@ class Plugin(GlancesPlugin):
"""Reset/init the stats."""
self.stats = {}
@GlancesPlugin._check_decorator
@GlancesPlugin._log_result_decorator
def update(self):
"""Update IP stats using the input method.
@@ -104,9 +105,6 @@ class Plugin(GlancesPlugin):
# Not implemented yet
pass
# Update the view
self.update_views()
return self.stats
def update_views(self):
@@ -125,7 +123,7 @@ class Plugin(GlancesPlugin):
ret = []
# Only process if stats exist and display plugin enable...
if not self.stats or args.disable_ip:
if not self.stats or self.is_disable():
return ret
# Build the string message

View File

@@ -2,7 +2,7 @@
#
# This file is part of Glances.
#
# Copyright (C) 2015 Angelo Poerio <angelo.poerio@gmail.com>
# Copyright (C) 2016 Angelo Poerio <angelo.poerio@gmail.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
@@ -19,7 +19,10 @@
"""IRQ plugin."""
import os
import operator
from glances.logger import logger
from glances.globals import LINUX
from glances.timer import getTimeSinceLastUpdate
from glances.plugins.glances_plugin import GlancesPlugin
@@ -39,19 +42,19 @@ class Plugin(GlancesPlugin):
# We want to display the stat in the curse interface
self.display_curse = True
self.lasts = {}
# Init the stats
self.irq = GlancesIRQ()
self.reset()
def get_key(self):
"""Return the key of the list."""
return 'irq_line'
return self.irq.get_key()
def reset(self):
"""Reset/init the stats."""
self.stats = []
@GlancesPlugin._check_decorator
@GlancesPlugin._log_result_decorator
def update(self):
"""Update the IRQ stats"""
@@ -59,40 +62,22 @@ class Plugin(GlancesPlugin):
# Reset the list
self.reset()
if not LINUX: # only available on GNU/Linux
# IRQ plugin only available on GNU/Linux
if not LINUX:
return self.stats
if self.input_method == 'local':
with open('/proc/interrupts') as irq_proc:
time_since_update = getTimeSinceLastUpdate('irq')
irq_proc.readline() # skip header line
for irq_line in irq_proc.readlines():
splitted_line = irq_line.split()
irq_line = splitted_line[0].replace(':', '')
current_irqs = sum([int(count) for count in splitted_line[
1:] if count.isdigit()]) # sum interrupts on all CPUs
irq_rate = int(
current_irqs -
self.lasts.get(irq_line) if self.lasts.get(irq_line) else 0 //
time_since_update)
irq_current = {
'irq_line': irq_line,
'irq_rate': irq_rate,
'key': self.get_key(),
'time_since_update': time_since_update
}
self.stats.append(irq_current)
self.lasts[irq_line] = current_irqs
# Grab the stats
self.stats = self.irq.get()
elif self.input_method == 'snmp':
# not available
pass
# Update the view
self.update_views()
# Get the TOP 5
self.stats = sorted(self.stats, key=operator.itemgetter(
'irq_rate'), reverse=True)[:5] # top 5 IRQ by rate/s
return self.stats
def update_views(self):
@@ -105,11 +90,9 @@ class Plugin(GlancesPlugin):
# Init the return message
ret = []
if not LINUX: # only available on GNU/Linux
return ret
# Only available on GNU/Linux
# Only process if stats exist and display plugin enable...
if not self.stats or args.disable_irq:
if not LINUX or not self.stats or not self.args.enable_irq:
return ret
if max_width is not None and max_width >= 23:
@@ -126,9 +109,111 @@ class Plugin(GlancesPlugin):
for i in self.stats:
ret.append(self.curse_new_line())
msg = '{:>3}'.format(i['irq_line'])
msg = '{:<15}'.format(i['irq_line'][:15])
ret.append(self.curse_add_line(msg))
msg = '{:>20}'.format(str(i['irq_rate']))
msg = '{:>8}'.format(str(i['irq_rate']))
ret.append(self.curse_add_line(msg))
return ret
class GlancesIRQ(object):
"""
This class manages the IRQ file
"""
IRQ_FILE = '/proc/interrupts'
def __init__(self):
"""
Init the class
The stat are stored in a internal list of dict
"""
self.lasts = {}
self.reset()
def reset(self):
"""Reset the stats"""
self.stats = []
self.cpu_number = 0
def get(self):
"""Return the current IRQ stats"""
return self.__update()
def get_key(self):
"""Return the key of the dict."""
return 'irq_line'
def __header(self, line):
"""The header contain the number of CPU
CPU0 CPU1 CPU2 CPU3
0: 21 0 0 0 IO-APIC 2-edge timer
"""
self.cpu_number = len(line.split())
return self.cpu_number
def __humanname(self, line):
"""Get a line and
Return the IRQ name, alias or number (choose the best for human)
IRQ line samples:
1: 44487 341 44 72 IO-APIC 1-edge i8042
LOC: 33549868 22394684 32474570 21855077 Local timer interrupts
"""
splitted_line = line.split()
irq_line = splitted_line[0].replace(':', '')
if irq_line.isdigit():
# If the first column is a digit, use the alias (last column)
irq_line += '_{}'.format(splitted_line[-1])
return irq_line
def __sum(self, line):
"""Get a line and
Return the IRQ sum number
IRQ line samples:
1: 44487 341 44 72 IO-APIC 1-edge i8042
LOC: 33549868 22394684 32474570 21855077 Local timer interrupts
"""
splitted_line = line.split()
# sum interrupts on all CPUs
return sum(map(int, splitted_line[1:(self.cpu_number + 1)]))
def __update(self):
"""
Load the IRQ file and update the internal dict
"""
self.reset()
if not os.path.exists(self.IRQ_FILE):
# Correct issue #947: IRQ file do not exist on OpenVZ container
return self.stats
try:
with open(self.IRQ_FILE) as irq_proc:
time_since_update = getTimeSinceLastUpdate('irq')
# Read the header
self.__header(irq_proc.readline())
# Read the rest of the lines (one line per IRQ)
for line in irq_proc.readlines():
irq_line = self.__humanname(line)
current_irqs = self.__sum(line)
irq_rate = int(
current_irqs - self.lasts.get(irq_line)
if self.lasts.get(irq_line)
else 0 // time_since_update)
irq_current = {
'irq_line': irq_line,
'irq_rate': irq_rate,
'key': self.get_key(),
'time_since_update': time_since_update
}
self.stats.append(irq_current)
self.lasts[irq_line] = current_irqs
except (OSError, IOError):
pass
return self.stats

View File

@@ -74,6 +74,7 @@ class Plugin(GlancesPlugin):
"""Reset/init the stats."""
self.stats = {}
@GlancesPlugin._check_decorator
@GlancesPlugin._log_result_decorator
def update(self):
"""Update load stats."""
@@ -108,12 +109,6 @@ class Plugin(GlancesPlugin):
self.stats['cpucore'] = self.nb_log_core
# Update the history list
self.update_stats_history()
# Update the view
self.update_views()
return self.stats
def update_views(self):
@@ -137,7 +132,7 @@ class Plugin(GlancesPlugin):
ret = []
# Only process if stats exist, not empty (issue #871) and plugin not disabled
if not self.stats or (self.stats == {}) or args.disable_load:
if not self.stats or (self.stats == {}) or self.is_disable():
return ret
# Build the string message

View File

@@ -76,6 +76,7 @@ class Plugin(GlancesPlugin):
"""Reset/init the stats."""
self.stats = {}
@GlancesPlugin._check_decorator
@GlancesPlugin._log_result_decorator
def update(self):
"""Update RAM memory stats using the input method."""
@@ -156,12 +157,6 @@ class Plugin(GlancesPlugin):
# percent: the percentage usage calculated as (total - available) / total * 100.
self.stats['percent'] = float((self.stats['total'] - self.stats['free']) / self.stats['total'] * 100)
# Update the history list
self.update_stats_history()
# Update the view
self.update_views()
return self.stats
def update_views(self):
@@ -183,7 +178,7 @@ class Plugin(GlancesPlugin):
ret = []
# Only process if stats exist and plugin not disabled
if not self.stats or args.disable_mem:
if not self.stats or self.is_disable():
return ret
# Build the string message

View File

@@ -64,6 +64,7 @@ class Plugin(GlancesPlugin):
"""Reset/init the stats."""
self.stats = {}
@GlancesPlugin._check_decorator
@GlancesPlugin._log_result_decorator
def update(self):
"""Update swap memory stats using the input method."""
@@ -131,12 +132,6 @@ class Plugin(GlancesPlugin):
self.stats['percent'] = float(
(self.stats['total'] - self.stats['free']) / self.stats['total'] * 100)
# Update the history list
self.update_stats_history()
# Update the view
self.update_views()
return self.stats
def update_views(self):
@@ -154,7 +149,7 @@ class Plugin(GlancesPlugin):
ret = []
# Only process if stats exist and plugin not disabled
if not self.stats or args.disable_swap:
if not self.stats or self.is_disable():
return ret
# Build the string message

View File

@@ -22,6 +22,7 @@
import base64
import operator
from glances.logger import logger
from glances.timer import getTimeSinceLastUpdate
from glances.plugins.glances_plugin import GlancesPlugin
@@ -72,6 +73,7 @@ class Plugin(GlancesPlugin):
"""Reset/init the stats."""
self.stats = []
@GlancesPlugin._check_decorator
@GlancesPlugin._log_result_decorator
def update(self):
"""Update network stats using the input method.
@@ -90,7 +92,9 @@ class Plugin(GlancesPlugin):
except UnicodeDecodeError:
return self.stats
# New in PsUtil 3.0: optionaly import the interface's status (issue #765)
# New in PsUtil 3.0
# - import the interface's status (issue #765)
# - import the interface's speed (issue #718)
netstatus = {}
try:
netstatus = psutil.net_if_stats()
@@ -136,11 +140,19 @@ class Plugin(GlancesPlugin):
continue
else:
# Optional stats (only compliant with PsUtil 3.0+)
# Interface status
try:
netstat['is_up'] = netstatus[net].isup
except (KeyError, AttributeError):
pass
# Set the key
# Interface speed in Mbps, convert it to bps
# Can be always 0 on some OS
try:
netstat['speed'] = netstatus[net].speed * 1048576
except (KeyError, AttributeError):
pass
# Finaly, set the key
netstat['key'] = self.get_key()
self.stats.append(netstat)
@@ -212,12 +224,6 @@ class Plugin(GlancesPlugin):
# Save stats to compute next bitrate
self.network_old = network_new
# Update the history list
self.update_stats_history(self.get_key())
# Update the view
self.update_views()
return self.stats
def update_views(self):
@@ -229,10 +235,25 @@ class Plugin(GlancesPlugin):
# Alert
for i in self.stats:
ifrealname = i['interface_name'].split(':')[0]
self.views[i[self.get_key()]]['rx']['decoration'] = self.get_alert(int(i['rx'] // i['time_since_update'] * 8),
header=ifrealname + '_rx')
self.views[i[self.get_key()]]['tx']['decoration'] = self.get_alert(int(i['tx'] // i['time_since_update'] * 8),
header=ifrealname + '_tx')
# Convert rate in bps ( to be able to compare to interface speed)
bps_rx = int(i['rx'] // i['time_since_update'] * 8)
bps_tx = int(i['tx'] // i['time_since_update'] * 8)
# Decorate the bitrate with the configuration file thresolds
alert_rx = self.get_alert(bps_rx, header=ifrealname + '_rx')
alert_tx = self.get_alert(bps_tx, header=ifrealname + '_tx')
# If nothing is define in the configuration file...
# ... then use the interface speed (not available on all systems)
if alert_rx == 'DEFAULT' and 'speed' in i and i['speed'] != 0:
alert_rx = self.get_alert(current=bps_rx,
maximum=i['speed'],
header='rx')
if alert_tx == 'DEFAULT' and 'speed' in i and i['speed'] != 0:
alert_tx = self.get_alert(current=bps_tx,
maximum=i['speed'],
header='tx')
# then decorates
self.views[i[self.get_key()]]['rx']['decoration'] = alert_rx
self.views[i[self.get_key()]]['tx']['decoration'] = alert_tx
def msg_curse(self, args=None, max_width=None):
"""Return the dict to display in the curse interface."""
@@ -240,7 +261,7 @@ class Plugin(GlancesPlugin):
ret = []
# Only process if stats exist and display plugin enable...
if not self.stats or args.disable_network:
if not self.stats or self.is_disable():
return ret
# Max size for the interface name

View File

@@ -49,6 +49,8 @@ class Plugin(GlancesPlugin):
"""Reset/init the stats."""
self.stats = []
@GlancesPlugin._check_decorator
@GlancesPlugin._log_result_decorator
def update(self):
"""Update per-CPU stats using the input method."""
# Reset stats
@@ -69,10 +71,6 @@ class Plugin(GlancesPlugin):
# Init the return message
ret = []
# Only process if plugin not disable
if args.disable_cpu:
return ret
# No per CPU stat ? Exit...
if not self.stats:
msg = 'PER CPU not available'

View File

@@ -86,6 +86,19 @@ class GlancesPlugin(object):
"""Return the key of the list."""
return None
def is_enable(self):
"""Return true if plugin is enabled"""
try:
d = getattr(self.args, 'disable_' + self.plugin_name)
except AttributeError:
return True
else:
return d is False
def is_disable(self):
"""Return true if plugin is disabled"""
return not self.is_enable()
def _json_dumps(self, d):
"""Return the object 'd' in a JSON format
Manage the issue #815 for Windows OS"""
@@ -111,8 +124,14 @@ class GlancesPlugin(object):
logger.debug("Reset history for plugin {0} (items: {1})".format(self.plugin_name, reset_list))
self.stats_history.reset()
def update_stats_history(self, item_name=''):
def update_stats_history(self):
"""Update stats history."""
# If the plugin data is a dict, the dict's key should be used
if self.get_key() is None:
item_name = ''
else:
item_name = self.get_key()
# Build the history
if self.stats and self._history_enable():
for i in self.get_items_history_list():
if isinstance(self.stats, list):
@@ -393,7 +412,10 @@ class GlancesPlugin(object):
if option is None:
return item_views[key]
else:
return item_views[key][option]
if option in item_views[key]:
return item_views[key][option]
else:
return 'DEFAULT'
def load_limits(self, config):
"""Load limits from the configuration file, if it exists."""
@@ -432,6 +454,13 @@ class GlancesPlugin(object):
"""Set the limits to input_limits."""
self._limits = input_limits
def get_stats_action(self):
"""Return stats for the action
By default return all the stats.
Can be overwrite by plugins implementation.
For example, Docker will return self.stats['containers']"""
return self.stats
def get_alert(self,
current=0,
minimum=0,
@@ -439,6 +468,7 @@ class GlancesPlugin(object):
highlight_zero=True,
is_max=False,
header="",
action_key=None,
log=False):
"""Return the alert status relative to a current value.
@@ -454,6 +484,9 @@ class GlancesPlugin(object):
If defined 'header' is added between the plugin name and the status.
Only useful for stats with several alert status.
If defined, 'action_key' define the key for the actions.
By default, the action_key is equal to the header.
If log=True than add log if necessary
elif log=False than do not log
elif log=None than apply the config given in the conf file
@@ -480,11 +513,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'
@@ -493,7 +526,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"
@@ -501,42 +534,61 @@ class GlancesPlugin(object):
glances_logs.add(ret, stat_name.upper(), value)
# Manage action
# Here is a command line for the current trigger ?
try:
command = self.__get_limit_action(ret.lower(), stat_name=stat_name)
except KeyError:
# Reset the trigger
self.actions.set(stat_name, ret.lower())
else:
# A command line is available for the current alert, run it
# Build the {{mustache}} dictionnary
if isinstance(self.stats, list):
# If the stats are stored in a list of dict (fs plugin for exemple)
# Return the dict for the current header
mustache_dict = {}
for item in self.stats:
if item[self.get_key()] == header:
mustache_dict = item
break
else:
# Use the stats dict
mustache_dict = self.stats
# Run the action
self.actions.run(
stat_name, ret.lower(), command, mustache_dict=mustache_dict)
self.manage_action(stat_name, ret.lower(), header, action_key)
# Default is ok
return ret + log_str
def get_alert_log(self, current=0, minimum=0, maximum=100, header=""):
def manage_action(self,
stat_name,
trigger,
header,
action_key):
"""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)
except KeyError:
# Reset the trigger
self.actions.set(stat_name, trigger)
else:
# Define the action key for the stats dict
# If not define, then it sets to header
if action_key is None:
action_key = header
# A command line is available for the current alert
# 1) Build the {{mustache}} dictionnary
if isinstance(self.get_stats_action(), list):
# If the stats are stored in a list of dict (fs plugin for exemple)
# Return the dict for the current header
mustache_dict = {}
for item in self.get_stats_action():
if item[self.get_key()] == action_key:
mustache_dict = item
break
else:
# Use the stats dict
mustache_dict = self.get_stats_action()
# 2) Run the action
self.actions.run(
stat_name, trigger, command, mustache_dict=mustache_dict)
def get_alert_log(self,
current=0,
minimum=0,
maximum=100,
header="",
action_key=None):
"""Get the alert log."""
return self.get_alert(current=current,
minimum=minimum,
maximum=maximum,
header=header,
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
@@ -552,7 +604,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
@@ -566,7 +618,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
@@ -746,6 +798,16 @@ class GlancesPlugin(object):
value, decimal=decimal_precision, symbol=symbol)
return '{!s}'.format(number)
def _check_decorator(fct):
"""Check if the plugin is enabled."""
def wrapper(self, *args, **kw):
if self.is_enable():
ret = fct(self, *args, **kw)
else:
ret = self.stats
return ret
return wrapper
def _log_result_decorator(fct):
"""Log (DEBUG) the result of the function fct."""
def wrapper(*args, **kw):
@@ -758,4 +820,5 @@ class GlancesPlugin(object):
return wrapper
# Mandatory to call the decorator in childs' classes
_check_decorator = staticmethod(_check_decorator)
_log_result_decorator = staticmethod(_log_result_decorator)

View File

@@ -23,7 +23,6 @@ import os
import subprocess
import threading
import socket
import types
import time
from glances.globals import WINDOWS

View File

@@ -2,7 +2,7 @@
#
# This file is part of Glances.
#
# Copyright (C) 2015 Nicolargo <nicolas@nicolargo.com>
# 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
@@ -80,7 +80,11 @@ class Plugin(GlancesPlugin):
self.nb_log_core = 0
# Get the max values (dict)
max_values = glances_processes.max_values()
self.max_values = glances_processes.max_values()
# Get the maximum PID number
# Use to optimize space (see https://github.com/nicolargo/glances/issues/959)
self.pid_max = glances_processes.pid_max
# Note: 'glances_processes' is already init in the processes.py script
@@ -240,7 +244,7 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_add_line(msg))
ret.append(self.curse_add_line(msg))
# PID
msg = '{:>6}'.format(p['pid'])
msg = '{:>{width}}'.format(p['pid'], width=self.__max_pid_size() + 1)
ret.append(self.curse_add_line(msg))
# USER
if 'username' in p:
@@ -480,7 +484,7 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_add_line(msg, optional=True))
msg = '{:>6}'.format('RES')
ret.append(self.curse_add_line(msg, optional=True))
msg = '{:>6}'.format('PID')
msg = '{:>{width}}'.format('PID', width=self.__max_pid_size() + 1)
ret.append(self.curse_add_line(msg))
msg = ' {:10}'.format('USER')
ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'username' else 'DEFAULT'))
@@ -647,3 +651,11 @@ class Plugin(GlancesPlugin):
return sort_stats(self.stats, sortedby,
tree=glances_processes.is_tree_enabled(),
reverse=glances_processes.sort_reverse)
def __max_pid_size(self):
"""Return the maximum PID size in number of char"""
if self.pid_max is not None:
return len(str(self.pid_max))
else:
# By default return 5 (corresponding to 99999 PID number)
return 5

View File

@@ -37,6 +37,8 @@ class Plugin(GlancesPlugin):
"""Reset/init the stats."""
self.stats = None
@GlancesPlugin._check_decorator
@GlancesPlugin._log_result_decorator
def update(self):
"""Update the stats."""
# Reset stats

View File

@@ -22,7 +22,6 @@
from glances.cpu_percent import cpu_percent
from glances.outputs.glances_bars import Bar
from glances.plugins.glances_plugin import GlancesPlugin
from glances.logger import logger
import psutil
@@ -58,6 +57,7 @@ class Plugin(GlancesPlugin):
"""Reset/init the stats."""
self.stats = {}
@GlancesPlugin._check_decorator
@GlancesPlugin._log_result_decorator
def update(self):
"""Update quicklook stats using the input method."""
@@ -86,9 +86,6 @@ class Plugin(GlancesPlugin):
self.stats['cpu_hz_current'] = cpu_info['hz_actual_raw'][0]
self.stats['cpu_hz'] = cpu_info['hz_advertised_raw'][0]
# Update the view
self.update_views()
return self.stats
def update_views(self):
@@ -108,7 +105,7 @@ class Plugin(GlancesPlugin):
ret = []
# Only process if stats exist...
if not self.stats or args.disable_quicklook:
if not self.stats or self.is_disable():
return ret
# Define the bar

Some files were not shown because too many files have changed in this diff Show More