Merge branch 'develop'
15
AUTHORS
@ -5,25 +5,34 @@ Developers
|
||||
Nicolas Hennion (aka) Nicolargo
|
||||
http://blog.nicolargo.com
|
||||
https://twitter.com/nicolargo
|
||||
https://github.com/nicolargo
|
||||
nicolashennion@gmail.com
|
||||
PGP Fingerprint: 835F C447 3BCD 60E9 9200 2778 ABA4 D1AB 9731 6A3C
|
||||
PGP Public key: gpg --keyserver pgp.mit.edu --recv-keys 0xaba4d1ab97316a3c
|
||||
|
||||
Alessio Sergi (aka) al3hex
|
||||
Alessio Sergi (aka) Al3hex
|
||||
https://twitter.com/al3hex
|
||||
https://github.com/asergi
|
||||
|
||||
Brandon Philips (aka) Philips
|
||||
http://ifup.org/
|
||||
https://github.com/philips
|
||||
|
||||
Jon Renner (aka) Jrenner
|
||||
https://github.com/jrenner
|
||||
|
||||
Maxime Desbrus (aka) desbma
|
||||
Maxime Desbrus (aka) Desbma
|
||||
https://github.com/desbma
|
||||
|
||||
Nicolas Hart (aka) NclsHart for the UI design
|
||||
Nicolas Hart (aka) NclsHart (for the Web user interface)
|
||||
https://github.com/nclsHart
|
||||
|
||||
Sylvain Mouquet (aka) SylvainMouquet (for the Web user interface)
|
||||
http://github.com/sylvainmouquet
|
||||
|
||||
Floran Brutel (aka) notFloran (for the Web user interface)
|
||||
https://github.com/notFloran
|
||||
|
||||
=========
|
||||
Packagers
|
||||
=========
|
||||
|
@ -3,11 +3,16 @@ include COPYING
|
||||
include NEWS
|
||||
include README.rst
|
||||
include conf/glances.conf
|
||||
include glances/outputs/bottle/*.tpl
|
||||
include glances/outputs/static/html/*.html
|
||||
include glances/outputs/static/html/components/*.html
|
||||
include glances/outputs/static/html/plugins/*.html
|
||||
include glances/outputs/static/*.ico
|
||||
include glances/outputs/static/css/*.css
|
||||
include glances/outputs/static/js/*.js
|
||||
include glances/outputs/static/js/*.js.map
|
||||
include glances/outputs/static/js/vendors/*.js
|
||||
include glances/outputs/static/js/vendors/*.js.map
|
||||
include glances/outputs/static/images/*.png
|
||||
include man/glances.1
|
||||
recursive-include docs images/*.png glances-doc.html
|
||||
recursive-include glances *.py
|
||||
recursive-include i18n *.mo
|
||||
|
45
NEWS
@ -2,10 +2,47 @@
|
||||
Glances Version 2.x
|
||||
==============================================================================
|
||||
|
||||
Version 2.4
|
||||
===========
|
||||
|
||||
Changes:
|
||||
|
||||
* Glances doesn't provide a system-wide configuration file by default anymore.
|
||||
Just copy it in any of the supported locations. See glances-doc.html for
|
||||
more information. (issue #541)
|
||||
* The default key bindings have been changed to:
|
||||
- 'u': sort processes by USER
|
||||
- 'U': show cumulative network I/O
|
||||
* No more translations
|
||||
|
||||
Enhancements and new features:
|
||||
|
||||
* The Web user interface is now based on AngularJS (issue #473, #508, #468)
|
||||
* Implement a 'quick look' plugin (issue #505)
|
||||
* Add sort processes by USER (issue #531)
|
||||
* Add a new IP information plugin (issue #509)
|
||||
* Add RabbitMQ export module (issue #540 Thk to @Katyucha)
|
||||
* Add a quiet mode (-q), can be useful using with export module
|
||||
* Grab FAN speed in the Glances sensors plugin (issue #501)
|
||||
* Allow logical mounts points in the FS plugin (issue #448)
|
||||
* Add a --disable-hddtemp to disable HDD temperature module at startup (issue #515)
|
||||
* Increase alert minimal delay to 6 seconds (issue #522)
|
||||
* If the Curses application raises an exception, restore the terminal correctly (issue #537)
|
||||
|
||||
Bugs corrected:
|
||||
|
||||
* Monitor list, all processes are take into account (issue #507)
|
||||
* Duplicated --enable-history in the doc (issue #511)
|
||||
* Sensors title is displayed if no sensors are detected (issue #510)
|
||||
* Server mode issue when no network interface is available (issue #528)
|
||||
* DEBUG mode activated by default with Python 2.6 (issue #512)
|
||||
* Glances display of time trims the hours showing only minutes and seconds (issue #543)
|
||||
* Process list header not decorating when sorting by command (issue #551)
|
||||
|
||||
Version 2.3
|
||||
===========
|
||||
|
||||
Enhancements and news features:
|
||||
Enhancements and new features:
|
||||
|
||||
* Add the Docker plugin (issue #440) with per container CPU and memory monitoring (issue #490)
|
||||
* Add the RAID plugin (issue #447)
|
||||
@ -37,7 +74,7 @@ Version 2.2.1
|
||||
Version 2.2
|
||||
===========
|
||||
|
||||
Enhancements and news features:
|
||||
Enhancements and new features:
|
||||
|
||||
* Add centralized curse interface with a Glances servers list to monitor (issue #418)
|
||||
* Add processes tree view (--tree) (issue #444)
|
||||
@ -142,7 +179,7 @@ Version 2.0
|
||||
===========
|
||||
|
||||
Glances v2.0 is not a simple upgrade of the version 1.x but a complete code refactoring.
|
||||
Based on a plugins system, it aims at providing an easy way to add news features.
|
||||
Based on a plugins system, it aims at providing an easy way to add new features.
|
||||
- Core defines the basics and commons functions.
|
||||
- all stats are grabbed through plugins (see the glances/plugins source folder).
|
||||
- also outputs methods (Curse, Web mode, CSV) are managed as plugins.
|
||||
@ -348,7 +385,7 @@ Version 1.4.2.1
|
||||
Version 1.4.2
|
||||
=============
|
||||
|
||||
* Use the news virtual_memory() and virtual_swap() fct (PsUtil)
|
||||
* Use the new virtual_memory() and virtual_swap() fct (PsUtil)
|
||||
* Display "Top process" in logs
|
||||
* Minor patch on man page for Debian packaging
|
||||
* Code optimization (less try and except)
|
||||
|
52
README.rst
@ -6,11 +6,11 @@ Glances - An eye on your system
|
||||
:target: https://flattr.com/thing/484466/nicolargoglances-on-GitHub
|
||||
.. image:: https://scrutinizer-ci.com/g/nicolargo/glances/badges/quality-score.png?b=master
|
||||
:target: https://scrutinizer-ci.com/g/nicolargo/glances/
|
||||
.. image:: https://travis-ci.org/nicolargo/glances.png?branch=master
|
||||
.. image:: https://travis-ci.org/nicolargo/glances.svg?branch=master
|
||||
:target: https://travis-ci.org/nicolargo/glances
|
||||
.. image:: https://badge.fury.io/py/Glances.png
|
||||
.. image:: https://badge.fury.io/py/Glances.svg
|
||||
:target: http://badge.fury.io/py/Glances
|
||||
.. image:: https://pypip.in/d/Glances/badge.png
|
||||
.. image:: https://pypip.in/d/Glances/badge.svg
|
||||
:target: https://pypi.python.org/pypi/Glances/
|
||||
:alt: Downloads
|
||||
|
||||
@ -20,18 +20,16 @@ Follow Glances on Twitter: `@nicolargo`_ or `@glances_system`_
|
||||
**Glances** is a cross-platform curses-based system monitoring tool
|
||||
written in Python.
|
||||
|
||||
It uses the `psutil`_ library to get information from your system.
|
||||
|
||||
.. image:: https://raw.github.com/nicolargo/glances/master/docs/images/screenshot-wide.png
|
||||
|
||||
Requirements
|
||||
============
|
||||
|
||||
- ``python >= 2.6`` (tested with version 2.6, 2.7, 3.3, 3.4)
|
||||
- ``python >= 2.6`` or ``>= 3.3`` (tested with version 2.6, 2.7, 3.3, 3.4)
|
||||
- ``psutil >= 2.0.0``
|
||||
- ``setuptools``
|
||||
|
||||
Optionals dependencies:
|
||||
Optional dependencies:
|
||||
|
||||
- ``bottle`` (for Web server mode)
|
||||
- ``py3sensors`` (for hardware monitoring support) [Linux-only]
|
||||
@ -39,11 +37,14 @@ Optionals dependencies:
|
||||
- ``batinfo`` (for battery monitoring support) [Linux-only]
|
||||
- ``pymdstat`` (for RAID support) [Linux-only]
|
||||
- ``pysnmp`` (for SNMP support)
|
||||
- ``zeroconf`` and ``netifaces`` (for the auto discoverer mode)
|
||||
- ``zeroconf`` (for the autodiscover mode)
|
||||
- ``netifaces`` (for the IP plugin)
|
||||
- ``influxdb`` (for the InfluxDB export module)
|
||||
- ``statsd`` (for the StatsD export module)
|
||||
- ``pystache`` (for the action script feature)
|
||||
- ``docker-py`` (for the Docker monitoring support) [Linux-only]
|
||||
- ``matplotlib`` (for graphical/chart support)
|
||||
- ``pika`` (for the RabbitMQ/ActiveMQ export module)
|
||||
|
||||
Installation
|
||||
============
|
||||
@ -51,7 +52,8 @@ Installation
|
||||
Glances Auto Install script
|
||||
---------------------------
|
||||
|
||||
To install both dependencies and latest Glances production ready version (aka *master* branch), just enter the following command line:
|
||||
To install both dependencies and latest Glances production ready version
|
||||
(aka *master* branch), just enter the following command line:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
@ -76,29 +78,30 @@ To install, simply use ``pip``:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pip install Glances
|
||||
pip install glances
|
||||
|
||||
*Note*: Python headers are required to install psutil. For example,
|
||||
on Debian/Ubuntu you need to install first the *python-dev* package.
|
||||
|
||||
*Note 2*: You can also install the following libs in order to use optionnal features:
|
||||
You can also install the following libraries in order to use optional
|
||||
features:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pip install bottle batinfo https://bitbucket.org/gleb_zhulik/py3sensors/get/tip.tar.gz zeroconf netifaces pymdstat influxdb statsd pystache
|
||||
pip install bottle batinfo https://bitbucket.org/gleb_zhulik/py3sensors/get/tip.tar.gz zeroconf netifaces pymdstat influxdb statsd pystache pika
|
||||
|
||||
To upgrade Glances to the latest version:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pip install --upgrade Glances
|
||||
pip install --upgrade glances
|
||||
|
||||
If you need to install Glances in a specific user location, use:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
export PYTHONUSERBASE=~/mylocalpath
|
||||
pip install --user Glances
|
||||
pip install --user glances
|
||||
|
||||
GNU/Linux
|
||||
---------
|
||||
@ -106,11 +109,11 @@ GNU/Linux
|
||||
At the moment, packages exist for the following GNU/Linux distributions:
|
||||
|
||||
- Arch Linux
|
||||
- Debian (Testing/Sid)
|
||||
- Debian
|
||||
- Fedora/CentOS/RHEL
|
||||
- Gentoo
|
||||
- Slackware (SlackBuild)
|
||||
- Ubuntu (13.04+)
|
||||
- Ubuntu
|
||||
- Void Linux
|
||||
|
||||
So you should be able to install it using your favorite package manager.
|
||||
@ -142,7 +145,7 @@ Homebrew
|
||||
.. code-block:: console
|
||||
|
||||
$ brew install python
|
||||
$ pip install Glances
|
||||
$ pip install glances
|
||||
|
||||
MacPorts
|
||||
````````
|
||||
@ -154,10 +157,15 @@ MacPorts
|
||||
Windows
|
||||
-------
|
||||
|
||||
- Install Python for Windows: http://www.python.org/getit/
|
||||
- Install the psutil library: https://pypi.python.org/pypi?:action=display&name=psutil#downloads
|
||||
- Install Python for Windows (Python 2.7.9+ ship with Pip): http://www.python.org/getit/
|
||||
- Install the `psutil`_ library (latest binary version): https://pypi.python.org/pypi/psutil
|
||||
- Install the colorconsole library: https://pypi.python.org/pypi/colorconsole
|
||||
- Install Glances Download Glances from here: http://nicolargo.github.io/glances/
|
||||
- Install optionnals dependencies (see list bellow)
|
||||
- Install Glances using pip
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ pip install glances
|
||||
|
||||
Source
|
||||
------
|
||||
@ -229,14 +237,14 @@ If you have any question (after RTFM!), please post it on the official Q&A `foru
|
||||
Gateway to other services
|
||||
=========================
|
||||
|
||||
Glances can export stats to: ``CSV`` file, ``InfluxDB`` and ``StatsD`` server.
|
||||
Glances can export stats to: ``CSV`` file, ``InfluxDB``, ``StatsD`` and ``RabbitMQ`` server.
|
||||
|
||||
How to contribute ?
|
||||
===================
|
||||
|
||||
If you want to contribute to the Glances project, read this `Wiki`_ page.
|
||||
|
||||
There is also a chat dedicated to the Glances' developpers:
|
||||
There is also a chat dedicated to the Glances developers:
|
||||
|
||||
.. image:: https://badges.gitter.im/Join%20Chat.svg
|
||||
:target: https://gitter.im/nicolargo/glances?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
||||
|
1140
conf/glances-grafana.json
Normal file
@ -1,174 +0,0 @@
|
||||
[cpu]
|
||||
# Default values if not defined: 50/70/90
|
||||
user_careful=50
|
||||
user_warning=70
|
||||
user_critical=90
|
||||
#user_log=False
|
||||
user_critical_action=echo {{user}} {{value}} {{max}} > /tmp/cpu.alert
|
||||
iowait_careful=50
|
||||
iowait_warning=70
|
||||
iowait_critical=90
|
||||
system_careful=50
|
||||
system_warning=70
|
||||
system_critical=90
|
||||
steal_careful=50
|
||||
steal_warning=70
|
||||
steal_critical=90
|
||||
#steal_log=True
|
||||
|
||||
[percpu]
|
||||
# Default values if not defined: 50/70/90
|
||||
user_careful=50
|
||||
user_warning=70
|
||||
user_critical=90
|
||||
iowait_careful=50
|
||||
iowait_warning=70
|
||||
iowait_critical=90
|
||||
system_careful=50
|
||||
system_warning=70
|
||||
system_critical=90
|
||||
|
||||
[load]
|
||||
# Value * number of cores
|
||||
# Default values if not defined: 0.7/1.0/5.0 per number of cores
|
||||
# Source: http://blog.scoutapp.com/articles/2009/07/31/understanding-load-averages
|
||||
# http://www.linuxjournal.com/article/9001
|
||||
careful=0.7
|
||||
warning=1.0
|
||||
critical=5.0
|
||||
#log=False
|
||||
|
||||
[mem]
|
||||
# Default limits for free RAM memory in %
|
||||
# Default values if not defined: 50/70/90
|
||||
careful=50
|
||||
warning=70
|
||||
critical=90
|
||||
|
||||
[memswap]
|
||||
# Default limits for free swap memory in %
|
||||
# Default values if not defined: 50/70/90
|
||||
careful=50
|
||||
warning=70
|
||||
critical=90
|
||||
|
||||
[network]
|
||||
# Define the list of hidden network interfaces (comma separeted)
|
||||
hide=lo
|
||||
# WLAN0 alias name
|
||||
wlan0_alias=Wireless
|
||||
# WLAN0 Default limits (in bits per second aka bps) for interface bitrate
|
||||
wlan0_rx_careful=4000000
|
||||
wlan0_rx_warning=5000000
|
||||
wlan0_rx_critical=6000000
|
||||
wlan0_rx_log=True
|
||||
wlan0_tx_careful=700000
|
||||
wlan0_tx_warning=900000
|
||||
wlan0_tx_critical=1000000
|
||||
wlan0_tx_log=True
|
||||
|
||||
[diskio]
|
||||
# Define the list of hidden disks (comma separeted)
|
||||
hide=sda5
|
||||
# Alias for sda1
|
||||
#sda1_alias=IntDisk
|
||||
# SDA1 limits (in bytes per second aka Bps) for interface bitrate
|
||||
sda2_rx_careful=150000000
|
||||
sda2_rx_warning=180000000
|
||||
sda2_rx_critical=200000000
|
||||
#sda2_rx_log=True
|
||||
sda2_tx_careful=150000000
|
||||
sda2_tx_warning=180000000
|
||||
sda2_tx_critical=200000000
|
||||
#sda2_tx_log=True
|
||||
|
||||
|
||||
[fs]
|
||||
# Default limits for free filesytem space in %
|
||||
# Default values if not defined: 50/70/90
|
||||
careful=50
|
||||
careful_action=echo {{mnt_point}} {{used}}/{{size}} > /tmp/fs.alert
|
||||
warning=70
|
||||
critical=90
|
||||
|
||||
[sensors]
|
||||
# Sensors core limits
|
||||
# Default values if not defined: 60/70/80
|
||||
temperature_core_careful=50
|
||||
temperature_core_warning=70
|
||||
temperature_core_critical=80
|
||||
# Temperatures in °C for hddtemp
|
||||
# Default values if not defined: 45/52/60
|
||||
temperature_hdd_careful=45
|
||||
temperature_hdd_warning=52
|
||||
temperature_hdd_critical=60
|
||||
# Battery % limits
|
||||
battery_careful=80
|
||||
battery_warning=90
|
||||
battery_critical=95
|
||||
# Sensors alias
|
||||
temp1_alias=Motherboard 0
|
||||
temp2_alias=Motherboard 1
|
||||
core 0_alias=CPU Core 0
|
||||
core 1_alias=CPU Core 1
|
||||
|
||||
[processlist]
|
||||
# Limit values for CPU/MEM per process in %
|
||||
# Default values if not defined: 50/70/90
|
||||
cpu_careful=50
|
||||
cpu_warning=70
|
||||
cpu_critical=90
|
||||
mem_careful=50
|
||||
mem_warning=70
|
||||
mem_critical=90
|
||||
|
||||
[monitor]
|
||||
# Define the list of processes to monitor
|
||||
# *** This section is optional ***
|
||||
# The list is composed of items (list_#nb <= 10)
|
||||
# An item is defined:
|
||||
# * description: Description of the processes (max 16 chars)
|
||||
# * regex: regular expression of the processes to monitor
|
||||
# * command: (optional) full path to shell command/script for extended stat
|
||||
# Use with caution. Should return a single line string.
|
||||
# Only execute when at least one process is running
|
||||
# By default display CPU and MEM %
|
||||
# Limitation: Do not use in client / server mode
|
||||
# * countmin: (optional) minimal number of processes
|
||||
# A warning will be displayed if number of process < count
|
||||
# * countmax: (optional) maximum number of processes
|
||||
# A warning will be displayed if number of process > count
|
||||
#list_1_description=Dropbox
|
||||
#list_1_regex=.*dropbox.*
|
||||
#list_1_countmin=1
|
||||
#list_1_command=dropbox status | head -1
|
||||
list_1_description=Python programs
|
||||
list_1_regex=.*python.*
|
||||
list_2_description=Famous Xeyes
|
||||
list_2_regex=.*xeyes.*
|
||||
list_2_countmin=1
|
||||
|
||||
[serverlist]
|
||||
# Define the static server list
|
||||
server_1_name=localhost
|
||||
server_1_alias=My local PC
|
||||
server_1_port=61209
|
||||
server_2_name=localhost
|
||||
server_2_port=61235
|
||||
server_3_name=192.168.0.17
|
||||
server_3_alias=Another PC on my network
|
||||
server_3_port=61209
|
||||
server_4_name=pasbon
|
||||
server_4_port=61237
|
||||
|
||||
[influxdb]
|
||||
host=localhost
|
||||
port=8086
|
||||
user=root
|
||||
password=root
|
||||
db=glances
|
||||
|
||||
[statsd]
|
||||
host=localhost
|
||||
port=8125
|
||||
#prefix=glances
|
@ -1,3 +1,14 @@
|
||||
[quicklook]
|
||||
cpu_careful=50
|
||||
cpu_warning=70
|
||||
cpu_critical=90
|
||||
mem_careful=50
|
||||
mem_warning=70
|
||||
mem_critical=90
|
||||
swap_careful=50
|
||||
swap_warning=70
|
||||
swap_critical=90
|
||||
|
||||
[cpu]
|
||||
# Default values if not defined: 50/70/90
|
||||
user_careful=50
|
||||
@ -81,6 +92,8 @@ critical=90
|
||||
careful=50
|
||||
warning=70
|
||||
critical=90
|
||||
# Allow additionnals files types (comma-separated FS type)
|
||||
#allow=zfs
|
||||
|
||||
[sensors]
|
||||
# Sensors core limits
|
||||
@ -133,11 +146,11 @@ mem_critical=90
|
||||
#list_1_regex=.*dropbox.*
|
||||
#list_1_countmin=1
|
||||
#list_1_command=dropbox status | head -1
|
||||
#list_1_description=Python programs
|
||||
#list_1_regex=.*python.*
|
||||
#list_2_description=Famous Xeyes
|
||||
#list_2_regex=.*xeyes.*
|
||||
#list_2_countmin=1
|
||||
#list_2_description=Python programs
|
||||
#list_2_regex=.*python.*
|
||||
#list_3_description=Famous Xeyes
|
||||
#list_3_regex=.*xeyes.*
|
||||
#list_3_countmin=1
|
||||
|
||||
#[serverlist]
|
||||
# Define the static server list
|
||||
@ -158,8 +171,16 @@ port=8086
|
||||
user=root
|
||||
password=root
|
||||
db=glances
|
||||
#prefix=localhost
|
||||
|
||||
[statsd]
|
||||
host=localhost
|
||||
port=8125
|
||||
#prefix=glances
|
||||
|
||||
[rabbitmq]
|
||||
host=localhost
|
||||
port=5672
|
||||
user=guest
|
||||
password=guest
|
||||
queue=glances_queue
|
||||
|
@ -123,9 +123,9 @@ td.option-group {
|
||||
<div class="document" id="glances">
|
||||
<h1 class="title">Glances</h1>
|
||||
|
||||
<p>This manual describes <em>Glances</em> version 2.3.</p>
|
||||
<p>This manual describes <em>Glances</em> version 2.4.</p>
|
||||
<p>Copyright © 2011-2015 Nicolas Hennion <<a class="reference external" href="mailto:nicolas@nicolargo.com">nicolas@nicolargo.com</a>></p>
|
||||
<p>January 2015</p>
|
||||
<p>May 2015</p>
|
||||
<div class="contents topic" id="table-of-contents">
|
||||
<p class="topic-title first">Table of Contents</p>
|
||||
<ul class="simple">
|
||||
@ -141,28 +141,39 @@ td.option-group {
|
||||
<li><a class="reference internal" href="#interactive-commands" id="id12">Interactive Commands</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#configuration" id="id13">Configuration</a></li>
|
||||
<li><a class="reference internal" href="#logs-and-debug-mode" id="id14">Logs and debug mode</a></li>
|
||||
<li><a class="reference internal" href="#anatomy-of-the-application" id="id15">Anatomy Of The Application</a><ul>
|
||||
<li><a class="reference internal" href="#legend" id="id16">Legend</a></li>
|
||||
<li><a class="reference internal" href="#header" id="id17">Header</a></li>
|
||||
<li><a class="reference internal" href="#cpu" id="id18">CPU</a></li>
|
||||
<li><a class="reference internal" href="#load" id="id19">Load</a></li>
|
||||
<li><a class="reference internal" href="#memory" id="id20">Memory</a></li>
|
||||
<li><a class="reference internal" href="#network" id="id21">Network</a></li>
|
||||
<li><a class="reference internal" href="#disk-i-o" id="id22">Disk I/O</a></li>
|
||||
<li><a class="reference internal" href="#file-system" id="id23">File System</a></li>
|
||||
<li><a class="reference internal" href="#sensors" id="id24">Sensors</a></li>
|
||||
<li><a class="reference internal" href="#processes-list" id="id25">Processes List</a></li>
|
||||
<li><a class="reference internal" href="#monitored-processes-list" id="id26">Monitored Processes List</a></li>
|
||||
<li><a class="reference internal" href="#logs" id="id27">Logs</a></li>
|
||||
<li><a class="reference internal" href="#docker" id="id28">Docker</a></li>
|
||||
<li><a class="reference internal" href="#actions" id="id29">Actions</a></li>
|
||||
<li><a class="reference internal" href="#configuration" id="id13">Configuration</a><ul>
|
||||
<li><a class="reference internal" href="#location" id="id14">Location</a></li>
|
||||
<li><a class="reference internal" href="#syntax" id="id15">Syntax</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#gateway-to-others-services" id="id30">Gateway to others services</a></li>
|
||||
<li><a class="reference internal" href="#apis-documentations" id="id31">APIs Documentations</a></li>
|
||||
<li><a class="reference internal" href="#support" id="id32">Support</a></li>
|
||||
<li><a class="reference internal" href="#logs-and-debug-mode" id="id16">Logs and debug mode</a></li>
|
||||
<li><a class="reference internal" href="#anatomy-of-the-application" id="id17">Anatomy Of The Application</a><ul>
|
||||
<li><a class="reference internal" href="#legend" id="id18">Legend</a></li>
|
||||
<li><a class="reference internal" href="#header" id="id19">Header</a></li>
|
||||
<li><a class="reference internal" href="#quicklook" id="id20">QuickLook</a></li>
|
||||
<li><a class="reference internal" href="#cpu" id="id21">CPU</a></li>
|
||||
<li><a class="reference internal" href="#load" id="id22">Load</a></li>
|
||||
<li><a class="reference internal" href="#memory" id="id23">Memory</a></li>
|
||||
<li><a class="reference internal" href="#network" id="id24">Network</a></li>
|
||||
<li><a class="reference internal" href="#disk-i-o" id="id25">Disk I/O</a></li>
|
||||
<li><a class="reference internal" href="#file-system" id="id26">File System</a></li>
|
||||
<li><a class="reference internal" href="#sensors" id="id27">Sensors</a></li>
|
||||
<li><a class="reference internal" href="#processes-list" id="id28">Processes List</a></li>
|
||||
<li><a class="reference internal" href="#monitored-processes-list" id="id29">Monitored Processes List</a></li>
|
||||
<li><a class="reference internal" href="#logs" id="id30">Logs</a></li>
|
||||
<li><a class="reference internal" href="#docker" id="id31">Docker</a></li>
|
||||
<li><a class="reference internal" href="#actions" id="id32">Actions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#gateway-to-others-services" id="id33">Gateway to others services</a><ul>
|
||||
<li><a class="reference internal" href="#csv" id="id34">CSV</a></li>
|
||||
<li><a class="reference internal" href="#influxdb" id="id35">InfluxDB</a></li>
|
||||
<li><a class="reference internal" href="#statsd" id="id36">Statsd</a></li>
|
||||
<li><a class="reference internal" href="#rabbitmq" id="id37">RabbitMQ</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#apis-documentation" id="id38">APIs documentation</a></li>
|
||||
<li><a class="reference internal" href="#support" id="id39">Support</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="introduction">
|
||||
@ -204,7 +215,12 @@ another one, called <tt class="docutils literal">client</tt>, just run on the se
|
||||
<span class="generic output">client$ glances -c @server</span>
|
||||
</pre>
|
||||
<p>where <tt class="docutils literal">@server</tt> is the IP address or hostname of the server.</p>
|
||||
<p>Glances can centralize available Glances servers using the <tt class="docutils literal"><span class="pre">--browser</span></tt> option. The server list can be staticaly defined in the Glances configuration file (section [serverlist]). Glances can also detect and display all Glances servers available on you network (auto discover mode is based on the the Zeroconf protocol only available on GNU/Linux and Mac OS X):</p>
|
||||
<p>Glances can centralize available Glances servers using the <tt class="docutils literal"><span class="pre">--browser</span></tt>
|
||||
option. The server list can be statically defined in the Glances
|
||||
configuration file (section <tt class="docutils literal">[serverlist]</tt>).</p>
|
||||
<p>Glances can also detect and display all Glances servers available on your
|
||||
network (auto-discover mode is based on the the <tt class="docutils literal">zeroconf</tt> protocol,
|
||||
which is only available on GNU/Linux and OS X):</p>
|
||||
<pre class="code console literal-block">
|
||||
<span class="generic output">client$ glances --browser</span>
|
||||
</pre>
|
||||
@ -223,7 +239,7 @@ client, the latter will try to grab stats using the <tt class="docutils literal"
|
||||
<pre class="code console literal-block">
|
||||
<span class="generic output">client$ glances -c @snmpserver</span>
|
||||
</pre>
|
||||
<p>Note: Stats grabbed by SNMP request are limited (operating system dependent).</p>
|
||||
<p><em>Note</em>: stats grabbed by SNMP request are limited (OS dependent).</p>
|
||||
</div>
|
||||
<div class="section" id="web-server-mode">
|
||||
<h2><a class="toc-backref" href="#id9">Web Server Mode</a></h2>
|
||||
@ -237,7 +253,11 @@ device with a web browser, just run the server with the <tt class="docutils lite
|
||||
http://@server:61208
|
||||
</pre>
|
||||
<p>where <tt class="docutils literal">@server</tt> is the IP address or hostname of the server.</p>
|
||||
<p>To change the refresh rate of the page, just add the period in seconds between refreshes at the end of the URL, ie. to refresh every 10s, use <tt class="docutils literal"><span class="pre">http://@server:61208/10</span></tt>.</p>
|
||||
<p>To change the refresh rate of the page, just add the period in seconds
|
||||
at the end of the URL. For example, to refresh the page every 10s:</p>
|
||||
<pre class="literal-block">
|
||||
http://@server:61208/10
|
||||
</pre>
|
||||
<p>The Glances web interface follows responsive web design principles.</p>
|
||||
<p>Screenshot from Chrome on Android</p>
|
||||
<img alt="images/screenshot-web2.png" src="images/screenshot-web2.png" />
|
||||
@ -260,37 +280,45 @@ http://@server:61208
|
||||
<td>show program's version number and exit</td></tr>
|
||||
<tr><td class="option-group">
|
||||
<kbd><span class="option">-d</span>, <span class="option">--debug</span></kbd></td>
|
||||
<td>Enable debug mode</td></tr>
|
||||
<td>enable debug mode</td></tr>
|
||||
<tr><td class="option-group" colspan="2">
|
||||
<kbd><span class="option">-C <var>CONF_FILE</var></span>, <span class="option">--config <var>CONF_FILE</var></span></kbd></td>
|
||||
</tr>
|
||||
<tr><td> </td><td>path to the configuration file</td></tr>
|
||||
<tr><td class="option-group" colspan="2">
|
||||
<kbd><span class="option">--enable-history</span></kbd></td>
|
||||
<kbd><span class="option">--disable-network</span></kbd></td>
|
||||
</tr>
|
||||
<tr><td> </td><td>enable the history mode</td></tr>
|
||||
<tr><td> </td><td>disable network module</td></tr>
|
||||
<tr><td class="option-group">
|
||||
<kbd><span class="option">--disable-bold</span></kbd></td>
|
||||
<td>disable bold mode in the terminal</td></tr>
|
||||
<kbd><span class="option">--disable-ip</span></kbd></td>
|
||||
<td>disable IP module</td></tr>
|
||||
<tr><td class="option-group" colspan="2">
|
||||
<kbd><span class="option">--disable-diskio</span></kbd></td>
|
||||
</tr>
|
||||
<tr><td> </td><td>disable disk I/O module</td></tr>
|
||||
<tr><td class="option-group">
|
||||
<kbd><span class="option">--disable-fs</span></kbd></td>
|
||||
<td>disable filesystem module</td></tr>
|
||||
<tr><td class="option-group" colspan="2">
|
||||
<kbd><span class="option">--disable-network</span></kbd></td>
|
||||
</tr>
|
||||
<tr><td> </td><td>disable network module</td></tr>
|
||||
<td>disable file system module</td></tr>
|
||||
<tr><td class="option-group" colspan="2">
|
||||
<kbd><span class="option">--disable-sensors</span></kbd></td>
|
||||
</tr>
|
||||
<tr><td> </td><td>disable sensors module</td></tr>
|
||||
<tr><td class="option-group" colspan="2">
|
||||
<kbd><span class="option">--disable-hddtemp</span></kbd></td>
|
||||
</tr>
|
||||
<tr><td> </td><td>disable hddtemp module</td></tr>
|
||||
<tr><td class="option-group">
|
||||
<kbd><span class="option">--disable-raid</span></kbd></td>
|
||||
<td>disable RAID module</td></tr>
|
||||
<tr><td class="option-group" colspan="2">
|
||||
<kbd><span class="option">--disable-docker</span></kbd></td>
|
||||
</tr>
|
||||
<tr><td> </td><td>disable Docker module</td></tr>
|
||||
<tr><td class="option-group" colspan="2">
|
||||
<kbd><span class="option">--disable-left-sidebar</span></kbd></td>
|
||||
</tr>
|
||||
<tr><td> </td><td>disable left sidebar</td></tr>
|
||||
<tr><td> </td><td>disable network, disk I/O, file system and
|
||||
sensors modules (py3sensors needed)</td></tr>
|
||||
<tr><td class="option-group" colspan="2">
|
||||
<kbd><span class="option">--disable-process</span></kbd></td>
|
||||
</tr>
|
||||
@ -299,29 +327,40 @@ http://@server:61208
|
||||
<kbd><span class="option">--disable-log</span></kbd></td>
|
||||
<td>disable log module</td></tr>
|
||||
<tr><td class="option-group" colspan="2">
|
||||
<kbd><span class="option">--disable-quicklook</span></kbd></td>
|
||||
</tr>
|
||||
<tr><td> </td><td>disable quick look module</td></tr>
|
||||
<tr><td class="option-group">
|
||||
<kbd><span class="option">--disable-bold</span></kbd></td>
|
||||
<td>disable bold mode in the terminal</td></tr>
|
||||
<tr><td class="option-group" colspan="2">
|
||||
<kbd><span class="option">--enable-process-extended</span></kbd></td>
|
||||
</tr>
|
||||
<tr><td> </td><td>enable extended stats on top process</td></tr>
|
||||
<tr><td class="option-group" colspan="2">
|
||||
<kbd><span class="option">--enable-history</span></kbd></td>
|
||||
</tr>
|
||||
<tr><td> </td><td>enable the history mode</td></tr>
|
||||
<tr><td> </td><td>enable the history mode (matplotlib needed)</td></tr>
|
||||
<tr><td class="option-group" colspan="2">
|
||||
<kbd><span class="option">--path-history <var>PATH_HISTORY</var></span></kbd></td>
|
||||
</tr>
|
||||
<tr><td> </td><td>Set the export path for graph history</td></tr>
|
||||
<tr><td> </td><td>set the export path for graph history</td></tr>
|
||||
<tr><td class="option-group" colspan="2">
|
||||
<kbd><span class="option">--export-csv <var>CSV_FILE</var></span></kbd></td>
|
||||
<kbd><span class="option">--export-csv <var>EXPORT_CSV</var></span></kbd></td>
|
||||
</tr>
|
||||
<tr><td> </td><td>export stats to a CSV file</td></tr>
|
||||
<tr><td class="option-group" colspan="2">
|
||||
<kbd><span class="option">--export-influxdb</span></kbd></td>
|
||||
</tr>
|
||||
<tr><td> </td><td>export stats to an InfluxDB server</td></tr>
|
||||
<tr><td> </td><td>export stats to an InfluxDB server (influxdb needed)</td></tr>
|
||||
<tr><td class="option-group" colspan="2">
|
||||
<kbd><span class="option">--export-statsd</span></kbd></td>
|
||||
</tr>
|
||||
<tr><td> </td><td>export stats to a Statsd server</td></tr>
|
||||
<tr><td> </td><td>export stats to a StatsD server (statsd needed)</td></tr>
|
||||
<tr><td class="option-group" colspan="2">
|
||||
<kbd><span class="option">--export-rabbitmq</span></kbd></td>
|
||||
</tr>
|
||||
<tr><td> </td><td>export stats to a RabbitMQ server (pika needed)</td></tr>
|
||||
<tr><td class="option-group" colspan="2">
|
||||
<kbd><span class="option">-c <var>CLIENT</var></span>, <span class="option">--client <var>CLIENT</var></span></kbd></td>
|
||||
</tr>
|
||||
@ -332,7 +371,7 @@ hostname</td></tr>
|
||||
<td>run Glances in server mode</td></tr>
|
||||
<tr><td class="option-group">
|
||||
<kbd><span class="option">--browser</span></kbd></td>
|
||||
<td>run the Glances client browser (list of Glances server)</td></tr>
|
||||
<td>start the client browser (list of Glances servers)</td></tr>
|
||||
<tr><td class="option-group" colspan="2">
|
||||
<kbd><span class="option">--disable-autodiscover</span></kbd></td>
|
||||
</tr>
|
||||
@ -345,18 +384,9 @@ hostname</td></tr>
|
||||
<kbd><span class="option">-B <var>BIND_ADDRESS</var></span>, <span class="option">--bind <var>BIND_ADDRESS</var></span></kbd></td>
|
||||
</tr>
|
||||
<tr><td> </td><td>bind server to the given IPv4/IPv6 address or hostname</td></tr>
|
||||
<tr><td class="option-group" colspan="2">
|
||||
<kbd><span class="option">--password-badidea <var>PASSWORD_ARG</var></span></kbd></td>
|
||||
</tr>
|
||||
<tr><td> </td><td>define password from the command line</td></tr>
|
||||
<tr><td class="option-group">
|
||||
<kbd><span class="option">--password</span></kbd></td>
|
||||
<td>define a client/server password from the prompt or
|
||||
file</td></tr>
|
||||
<tr><td class="option-group" colspan="2">
|
||||
<kbd><span class="option">--disable-autodiscover</span></kbd></td>
|
||||
</tr>
|
||||
<tr><td> </td><td>Hide Glances server from the auto discover feature</td></tr>
|
||||
<td>define a client/server password</td></tr>
|
||||
<tr><td class="option-group" colspan="2">
|
||||
<kbd><span class="option">--snmp-community <var>SNMP_COMMUNITY</var></span></kbd></td>
|
||||
</tr>
|
||||
@ -387,11 +417,14 @@ file</td></tr>
|
||||
<tr><td class="option-group" colspan="2">
|
||||
<kbd><span class="option">-w</span>, <span class="option">--webserver</span></kbd></td>
|
||||
</tr>
|
||||
<tr><td> </td><td>run Glances in web server mode</td></tr>
|
||||
<tr><td> </td><td>run Glances in web server mode (bottle needed)</td></tr>
|
||||
<tr><td class="option-group">
|
||||
<kbd><span class="option">-q</span>, <span class="option">--quiet</span></kbd></td>
|
||||
<td>do not display the curses interface</td></tr>
|
||||
<tr><td class="option-group" colspan="2">
|
||||
<kbd><span class="option">-f <var>PROCESS_FILTER</var></span>, <span class="option">--process-filter <var>PROCESS_FILTER</var></span></kbd></td>
|
||||
</tr>
|
||||
<tr><td> </td><td>set the process filter patern (regular expression)</td></tr>
|
||||
<tr><td> </td><td>set the process filter pattern (regular expression)</td></tr>
|
||||
<tr><td class="option-group" colspan="2">
|
||||
<kbd><span class="option">--process-short-name</span></kbd></td>
|
||||
</tr>
|
||||
@ -412,10 +445,10 @@ file</td></tr>
|
||||
<tr><td class="option-group" colspan="2">
|
||||
<kbd><span class="option">--fs-free-space</span></kbd></td>
|
||||
</tr>
|
||||
<tr><td> </td><td>display FS free space instead of used</td></tr>
|
||||
<tr><td> </td><td>display file system free space instead of used</td></tr>
|
||||
<tr><td class="option-group">
|
||||
<kbd><span class="option">--theme-white</span></kbd></td>
|
||||
<td>optimize display for white background</td></tr>
|
||||
<td>optimize display colors for white background</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</blockquote>
|
||||
@ -451,7 +484,7 @@ Filter is a regular expression pattern:</p>
|
||||
<dt><tt class="docutils literal">f</tt></dt>
|
||||
<dd>Show/hide file system stats</dd>
|
||||
<dt><tt class="docutils literal">F</tt></dt>
|
||||
<dd>Switch between FS used and free space</dd>
|
||||
<dd>Switch between file system used and free space</dd>
|
||||
<dt><tt class="docutils literal">g</tt></dt>
|
||||
<dd>Generate graphs for current history</dd>
|
||||
<dt><tt class="docutils literal">h</tt></dt>
|
||||
@ -477,6 +510,8 @@ Filter is a regular expression pattern:</p>
|
||||
<dt><tt class="docutils literal">T</tt></dt>
|
||||
<dd>View network I/O as combination</dd>
|
||||
<dt><tt class="docutils literal">u</tt></dt>
|
||||
<dd>Sort processes by USER</dd>
|
||||
<dt><tt class="docutils literal">U</tt></dt>
|
||||
<dd>View cumulative network I/O</dd>
|
||||
<dt><tt class="docutils literal">w</tt></dt>
|
||||
<dd>Delete finished warning log messages</dd>
|
||||
@ -488,10 +523,13 @@ Filter is a regular expression pattern:</p>
|
||||
<dd>Switch between global CPU and per-CPU stats</dd>
|
||||
<dt><tt class="docutils literal">2</tt></dt>
|
||||
<dd>Enable/disable left sidebar</dd>
|
||||
<dt><tt class="docutils literal">3</tt></dt>
|
||||
<dd>Enable/disable the quick look module</dd>
|
||||
<dt><tt class="docutils literal">/</tt></dt>
|
||||
<dd>Switch between short name / command line (processes name)</dd>
|
||||
</dl>
|
||||
<p>In the Glances client browser (accessible through the --browser command line argument):</p>
|
||||
<p>In the Glances client browser (accessible through the <tt class="docutils literal"><span class="pre">--browser</span></tt>
|
||||
command line argument):</p>
|
||||
<dl class="docutils">
|
||||
<dt><tt class="docutils literal">ENTER</tt></dt>
|
||||
<dd>Run Glances client to the selected server</dd>
|
||||
@ -507,19 +545,24 @@ Filter is a regular expression pattern:</p>
|
||||
<div class="section" id="configuration">
|
||||
<h1><a class="toc-backref" href="#id13">Configuration</a></h1>
|
||||
<p>No configuration file is mandatory to use Glances.</p>
|
||||
<p>Furthermore a configuration file is needed to set up limits, disks or
|
||||
network interfaces to hide and/or monitored processes list or to define
|
||||
alias.</p>
|
||||
<p>By default, the configuration file is under:</p>
|
||||
<p>Furthermore a configuration file is needed to modify limit alerts, to
|
||||
set up monitored processes list, to hide disks or network interfaces or
|
||||
to define alias.</p>
|
||||
<div class="section" id="location">
|
||||
<h2><a class="toc-backref" href="#id14">Location</a></h2>
|
||||
<p>You can put the configuration file <tt class="docutils literal">glances.conf</tt> in the following
|
||||
locations:</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Linux:</th><td class="field-body"><tt class="docutils literal">/etc/glances/glances.conf</tt></td>
|
||||
<tr class="field"><th class="field-name">Linux:</th><td class="field-body"><tt class="docutils literal"><span class="pre">~/.config/glances,</span> /etc/glances</tt></td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">*BSD and OS X:</th><td class="field-body"><tt class="docutils literal">/usr/local/etc/glances/glances.conf</tt></td>
|
||||
<tr class="field"><th class="field-name">*BSD:</th><td class="field-body"><tt class="docutils literal"><span class="pre">~/.config/glances,</span> /usr/local/etc/glances</tt></td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Windows:</th><td class="field-body"><tt class="docutils literal"><span class="pre">%APPDATA%\glances\glances.conf</span></tt></td>
|
||||
<tr class="field"><th class="field-name">OS X:</th><td class="field-body"><tt class="docutils literal">~/Library/Application Support/glances, /usr/local/etc/glances</tt></td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Windows:</th><td class="field-body"><tt class="docutils literal"><span class="pre">%APPDATA%\glances</span></tt></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@ -530,20 +573,12 @@ C:\Documents and Settings\<User>\Application Data
|
||||
<p>Since Windows Vista and newer versions:</p>
|
||||
<pre class="literal-block">
|
||||
C:\Users\<User>\AppData\Roaming
|
||||
or
|
||||
%userprofile%\AppData\Roaming
|
||||
</pre>
|
||||
<p>You can override the default configuration, located in one of the above
|
||||
directories on your system, except for Windows.</p>
|
||||
<p>Just copy the <tt class="docutils literal">glances.conf</tt> file to your <tt class="docutils literal">$XDG_CONFIG_HOME</tt> directory,
|
||||
e.g., on Linux:</p>
|
||||
<pre class="code console literal-block">
|
||||
<span class="generic output">mkdir -p $XDG_CONFIG_HOME/glances
|
||||
cp /usr/share/doc/glances/glances.conf $XDG_CONFIG_HOME/glances/</span>
|
||||
</pre>
|
||||
<p>On OS X, you should copy the configuration file to
|
||||
<tt class="docutils literal">~/Library/Application Support/glances/</tt>.</p>
|
||||
<p><em>Configuration file description</em></p>
|
||||
<p>User-specific options override system-wide options and options given on
|
||||
the command line override either.</p>
|
||||
</div>
|
||||
<div class="section" id="syntax">
|
||||
<h2><a class="toc-backref" href="#id15">Syntax</a></h2>
|
||||
<p>Each plugin and export module can have a section.</p>
|
||||
<p>Example for the CPU plugin:</p>
|
||||
<pre class="code literal-block">
|
||||
@ -561,13 +596,15 @@ steal_careful=50
|
||||
steal_warning=70
|
||||
steal_critical=90
|
||||
</pre>
|
||||
<p>By default Steal CPU time alerts aren't logged. If you want to enable log/alert, just add:</p>
|
||||
<p>By default the <tt class="docutils literal">steal</tt> CPU time alerts aren't logged. If you want to
|
||||
enable log/alert, just add:</p>
|
||||
<pre class="code literal-block">
|
||||
steal_log=True
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="logs-and-debug-mode">
|
||||
<h1><a class="toc-backref" href="#id14">Logs and debug mode</a></h1>
|
||||
<h1><a class="toc-backref" href="#id16">Logs and debug mode</a></h1>
|
||||
<p>Glances logs all its internal messages to a log file. By default, only
|
||||
INFO & WARNING & ERROR &CRITICAL levels are logged, but DEBUG messages
|
||||
can ben logged using the -d option on the command line.</p>
|
||||
@ -576,19 +613,20 @@ can ben logged using the -d option on the command line.</p>
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name" colspan="2">Linux, *BSD and OS X:</th></tr>
|
||||
<tr class="field"><th class="field-name" colspan="2">Linux, *BSD, OS X:</th></tr>
|
||||
<tr class="field"><td> </td><td class="field-body"><tt class="docutils literal">/tmp/glances.log</tt></td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Windows:</th><td class="field-body"><tt class="docutils literal"><span class="pre">%APPDATA%\Local\temp\glances.log</span></tt></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>If glances.log is not writable, a new file will be created and returned to the user console.</p>
|
||||
<p>If <tt class="docutils literal">glances.log</tt> is not writable, a new file will be created and
|
||||
returned to the user console.</p>
|
||||
</div>
|
||||
<div class="section" id="anatomy-of-the-application">
|
||||
<h1><a class="toc-backref" href="#id15">Anatomy Of The Application</a></h1>
|
||||
<h1><a class="toc-backref" href="#id17">Anatomy Of The Application</a></h1>
|
||||
<div class="section" id="legend">
|
||||
<h2><a class="toc-backref" href="#id16">Legend</a></h2>
|
||||
<h2><a class="toc-backref" href="#id18">Legend</a></h2>
|
||||
<div class="line-block">
|
||||
<div class="line"><tt class="docutils literal">GREEN</tt> stat counter is <tt class="docutils literal">"OK"</tt></div>
|
||||
<div class="line"><tt class="docutils literal">BLUE</tt> stat counter is <tt class="docutils literal">"CAREFUL"</tt></div>
|
||||
@ -599,7 +637,7 @@ can ben logged using the -d option on the command line.</p>
|
||||
view.</p>
|
||||
</div>
|
||||
<div class="section" id="header">
|
||||
<h2><a class="toc-backref" href="#id17">Header</a></h2>
|
||||
<h2><a class="toc-backref" href="#id19">Header</a></h2>
|
||||
<img alt="images/header.png" src="images/header.png" />
|
||||
<p>The header shows the hostname, OS name, release version, platform
|
||||
architecture and system uptime (on the upper right corner).
|
||||
@ -610,8 +648,16 @@ Additionally, on GNU/Linux, it also shows the kernel version.</p>
|
||||
<p>Disconnected:</p>
|
||||
<img alt="images/disconnected.png" src="images/disconnected.png" />
|
||||
</div>
|
||||
<div class="section" id="quicklook">
|
||||
<h2><a class="toc-backref" href="#id20">QuickLook</a></h2>
|
||||
<p>The <tt class="docutils literal">quicklook</tt> plugin is only displayed on wide screen and propose a
|
||||
bar view for CPU and memory (virtual and swap).</p>
|
||||
<img alt="images/quicklook.png" src="images/quicklook.png" />
|
||||
<p><em>Note</em>: limit values can be overwritten in the configuration file under
|
||||
the <tt class="docutils literal">[quicklook]</tt> section.</p>
|
||||
</div>
|
||||
<div class="section" id="cpu">
|
||||
<h2><a class="toc-backref" href="#id18">CPU</a></h2>
|
||||
<h2><a class="toc-backref" href="#id21">CPU</a></h2>
|
||||
<p>Short view:</p>
|
||||
<img alt="images/cpu.png" src="images/cpu.png" />
|
||||
<p>If enough horizontal space is available, extended CPU information are
|
||||
@ -632,7 +678,7 @@ time. The total CPU usage is displayed on the first line.</p>
|
||||
the <tt class="docutils literal">[cpu]</tt> and/or <tt class="docutils literal">[percpu]</tt> sections.</p>
|
||||
</div>
|
||||
<div class="section" id="load">
|
||||
<h2><a class="toc-backref" href="#id19">Load</a></h2>
|
||||
<h2><a class="toc-backref" href="#id22">Load</a></h2>
|
||||
<img alt="images/load.png" src="images/load.png" />
|
||||
<p>On the <em>No Sheep</em> blog, <em>Zachary Tirrell</em> defines the load average <a class="footnote-reference" href="#id3" id="id1">[1]</a>:</p>
|
||||
<blockquote>
|
||||
@ -652,7 +698,7 @@ The first line also displays the number of CPU core.</p>
|
||||
the <tt class="docutils literal">[load]</tt> section.</p>
|
||||
</div>
|
||||
<div class="section" id="memory">
|
||||
<h2><a class="toc-backref" href="#id20">Memory</a></h2>
|
||||
<h2><a class="toc-backref" href="#id23">Memory</a></h2>
|
||||
<p>Glances uses two columns: one for the <tt class="docutils literal">RAM</tt> and one for the <tt class="docutils literal">SWAP</tt>.</p>
|
||||
<img alt="images/mem.png" src="images/mem.png" />
|
||||
<p>If enough space is available, Glances displays extended information for
|
||||
@ -669,7 +715,7 @@ the <tt class="docutils literal">RAM</tt>:</p>
|
||||
the <tt class="docutils literal">[memory]</tt> and/or <tt class="docutils literal">[memswap]</tt> sections.</p>
|
||||
</div>
|
||||
<div class="section" id="network">
|
||||
<h2><a class="toc-backref" href="#id21">Network</a></h2>
|
||||
<h2><a class="toc-backref" href="#id24">Network</a></h2>
|
||||
<img alt="images/network.png" src="images/network.png" />
|
||||
<p>Glances displays the network interface bit rate. The unit is adapted
|
||||
dynamically (bits per second, kbits per second, Mbits per second, etc).</p>
|
||||
@ -680,7 +726,7 @@ and per-interface limit values in the <tt class="docutils literal">[network]</tt
|
||||
configuration file and aliases for interface name.</p>
|
||||
</div>
|
||||
<div class="section" id="disk-i-o">
|
||||
<h2><a class="toc-backref" href="#id22">Disk I/O</a></h2>
|
||||
<h2><a class="toc-backref" href="#id25">Disk I/O</a></h2>
|
||||
<img alt="images/diskio.png" src="images/diskio.png" />
|
||||
<p>Glances displays the disk I/O throughput. The unit is adapted dynamically.</p>
|
||||
<p>There is no alert on this information.</p>
|
||||
@ -688,7 +734,7 @@ configuration file and aliases for interface name.</p>
|
||||
<tt class="docutils literal">[diskio]</tt> section in the configuration file and aliases for disk name.</p>
|
||||
</div>
|
||||
<div class="section" id="file-system">
|
||||
<h2><a class="toc-backref" href="#id23">File System</a></h2>
|
||||
<h2><a class="toc-backref" href="#id26">File System</a></h2>
|
||||
<img alt="images/fs.png" src="images/fs.png" />
|
||||
<p>Glances displays the used and total file system disk space. The unit is
|
||||
adapted dynamically.</p>
|
||||
@ -703,9 +749,16 @@ adapted dynamically.</p>
|
||||
the <tt class="docutils literal">[filesystem]</tt> section.</p>
|
||||
<p>If a RAID controller is detected on you system, its status will be displayed:</p>
|
||||
<img alt="images/raid.png" src="images/raid.png" />
|
||||
<p>By default, the plugin only displays physical devices (hard disks, USB
|
||||
keys) and ignore all others. To allow others FS type, you have to use the
|
||||
following section in the configuration file:</p>
|
||||
<pre class="literal-block">
|
||||
[fs]
|
||||
allow=zfs,misc
|
||||
</pre>
|
||||
</div>
|
||||
<div class="section" id="sensors">
|
||||
<h2><a class="toc-backref" href="#id24">Sensors</a></h2>
|
||||
<h2><a class="toc-backref" href="#id27">Sensors</a></h2>
|
||||
<p>Glances can displays the sensors information using <cite>lm-sensors</cite>,
|
||||
<cite>hddtemp</cite> and <cite>batinfo</cite> <a class="footnote-reference" href="#id4" id="id2">[2]</a>.</p>
|
||||
<p>All of the above libraries are available only on Linux.</p>
|
||||
@ -713,11 +766,11 @@ the <tt class="docutils literal">[filesystem]</tt> section.</p>
|
||||
temperature only.</p>
|
||||
<img alt="images/sensors.png" src="images/sensors.png" />
|
||||
<p>There is no alert on this information.</p>
|
||||
<p><em>Note</em>: limit values and sensors alias names can be defined in the configuration
|
||||
file under the <tt class="docutils literal">[sensors]</tt> section.</p>
|
||||
<p><em>Note</em>: limit values and sensors alias names can be defined in the
|
||||
configuration file under the <tt class="docutils literal">[sensors]</tt> section.</p>
|
||||
</div>
|
||||
<div class="section" id="processes-list">
|
||||
<h2><a class="toc-backref" href="#id25">Processes List</a></h2>
|
||||
<h2><a class="toc-backref" href="#id28">Processes List</a></h2>
|
||||
<p>Compact view:</p>
|
||||
<img alt="images/processlist.png" src="images/processlist.png" />
|
||||
<p>Full view:</p>
|
||||
@ -786,7 +839,8 @@ User cans switch to the process name by pressing on the <tt class="docutils lite
|
||||
<dt><tt class="docutils literal">Z</tt></dt>
|
||||
<dd>Zombie</dd>
|
||||
</dl>
|
||||
<p>In standalone mode, additionals informations are provided for the top process:</p>
|
||||
<p>In standalone mode, additional informations are provided for the top
|
||||
process:</p>
|
||||
<img alt="images/processlist-top.png" src="images/processlist-top.png" />
|
||||
<ul class="simple">
|
||||
<li>CPU affinity (number of cores used by the process)</li>
|
||||
@ -794,12 +848,13 @@ User cans switch to the process name by pressing on the <tt class="docutils lite
|
||||
<li>Open threads, files and network sessions (TCP and UDP)</li>
|
||||
<li>IO nice level</li>
|
||||
</ul>
|
||||
<p>The extended stats feature could be enabled using the --enable-process-extended option (command line) or the <tt class="docutils literal">e</tt> key (curses interface).</p>
|
||||
<p>The extended stats feature could be enabled using the <tt class="docutils literal"><span class="pre">--enable-process-extended</span></tt>
|
||||
option (command line) or the <tt class="docutils literal">e</tt> key (curses interface).</p>
|
||||
<p><em>Note</em>: limit values can be overwritten in the configuration file under
|
||||
the <tt class="docutils literal">[process]</tt> section.</p>
|
||||
</div>
|
||||
<div class="section" id="monitored-processes-list">
|
||||
<h2><a class="toc-backref" href="#id26">Monitored Processes List</a></h2>
|
||||
<h2><a class="toc-backref" href="#id29">Monitored Processes List</a></h2>
|
||||
<p>The monitored processes list allows user, through the configuration file,
|
||||
to group processes and quickly show if the number of running processes is
|
||||
not good.</p>
|
||||
@ -835,10 +890,10 @@ list_1_regex=.*nginx.*
|
||||
list_1_command=nginx -v
|
||||
list_1_countmin=1
|
||||
list_1_countmax=4
|
||||
list_1_description=PHP-FPM
|
||||
list_1_regex=.*php-fpm.*
|
||||
list_1_countmin=1
|
||||
list_1_countmax=20
|
||||
list_2_description=PHP-FPM
|
||||
list_2_regex=.*php-fpm.*
|
||||
list_2_countmin=1
|
||||
list_2_countmax=20
|
||||
</pre>
|
||||
<p>In client/server mode, the list is defined on the server side.
|
||||
A new method, called <cite>getAllMonitored</cite>, is available in the APIs and
|
||||
@ -851,7 +906,7 @@ get the JSON representation of the monitored processes list.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="logs">
|
||||
<h2><a class="toc-backref" href="#id27">Logs</a></h2>
|
||||
<h2><a class="toc-backref" href="#id30">Logs</a></h2>
|
||||
<img alt="images/logs.png" src="images/logs.png" />
|
||||
<p>A log messages list is displayed in the bottom of the screen if (and
|
||||
only if):</p>
|
||||
@ -870,31 +925,39 @@ processes list alerts</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div class="section" id="docker">
|
||||
<h2><a class="toc-backref" href="#id28">Docker</a></h2>
|
||||
<p>If you use Docker, Glances can help you to monitor your container. Glances uses the Docker API through the Docker-Py library.</p>
|
||||
<h2><a class="toc-backref" href="#id31">Docker</a></h2>
|
||||
<p>If you use <tt class="docutils literal">Docker</tt>, Glances can help you to monitor your container.
|
||||
Glances uses the Docker API through the <tt class="docutils literal"><span class="pre">docker-py</span></tt> library.</p>
|
||||
<img alt="images/docker.png" src="images/docker.png" />
|
||||
</div>
|
||||
<div class="section" id="actions">
|
||||
<h2><a class="toc-backref" href="#id29">Actions</a></h2>
|
||||
<h2><a class="toc-backref" href="#id32">Actions</a></h2>
|
||||
<p>Glances can trigger actions on events.</p>
|
||||
<p>By action, we mean all shell command line. For example, if you want to execute the foo.py script if the last 5 minutes load are critical then add the action line to the Glances configuration file:</p>
|
||||
<p>By <tt class="docutils literal">action</tt>, we mean all shell command line. For example, if you want
|
||||
to execute the <tt class="docutils literal">foo.py</tt> script if the last 5 minutes load are critical
|
||||
then add the action line to the Glances configuration file:</p>
|
||||
<pre class="code literal-block">
|
||||
[load]
|
||||
critical=5.0
|
||||
critical_action=python /path/to/foo.py
|
||||
</pre>
|
||||
<p>All the stats are available in the command line through the use of the {{mustache}} syntax. Another example to create a log file containing used vs total disk space if a space trigger warning is reached:</p>
|
||||
<p>All the stats are available in the command line through the use of the
|
||||
<tt class="docutils literal">{{mustache}}</tt> syntax. Another example would be to create a log file
|
||||
containing used vs total disk space if a space trigger warning is reached:</p>
|
||||
<pre class="code literal-block">
|
||||
[fs]
|
||||
warning=70
|
||||
warning_action=echo {{mnt_point}} {{used}}/{{size}} > /tmp/fs.alert
|
||||
</pre>
|
||||
<p><em>Note</em>: You can use all the stats for the current plugin (see <a class="reference external" href="https://github.com/nicolargo/glances/wiki/The-Glances-2.x-API-How-to">https://github.com/nicolargo/glances/wiki/The-Glances-2.x-API-How-to</a> for the stats list)</p>
|
||||
<p><em>Note</em>: you can use all the stats for the current plugin (see
|
||||
<a class="reference external" href="https://github.com/nicolargo/glances/wiki/The-Glances-2.x-API-How-to">https://github.com/nicolargo/glances/wiki/The-Glances-2.x-API-How-to</a> for
|
||||
the stats list)</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="gateway-to-others-services">
|
||||
<h1><a class="toc-backref" href="#id30">Gateway to others services</a></h1>
|
||||
<p><em>CSV</em></p>
|
||||
<h1><a class="toc-backref" href="#id33">Gateway to others services</a></h1>
|
||||
<div class="section" id="csv">
|
||||
<h2><a class="toc-backref" href="#id34">CSV</a></h2>
|
||||
<p>It is possible to export statistics to CSV file.</p>
|
||||
<pre class="code console literal-block">
|
||||
<span class="generic prompt">$</span> glances --export-csv /tmp/glances.csv
|
||||
@ -902,8 +965,12 @@ warning_action=echo {{mnt_point}} {{used}}/{{size}} > /tmp/fs.alert
|
||||
<p>CSV file description:
|
||||
- Stats description (first line)
|
||||
- Stats (others lines)</p>
|
||||
<p><em>InfluxDB</em></p>
|
||||
<p>You can export statistics to an InfluxDB server (time series server). The connection should be defined in the Glances configuration file as following:</p>
|
||||
</div>
|
||||
<div class="section" id="influxdb">
|
||||
<h2><a class="toc-backref" href="#id35">InfluxDB</a></h2>
|
||||
<p>You can export statistics to an <tt class="docutils literal">InfluxDB</tt> server (time series server).
|
||||
The connection should be defined in the Glances configuration file as
|
||||
following:</p>
|
||||
<pre class="code literal-block">
|
||||
[influxdb]
|
||||
host=localhost
|
||||
@ -916,15 +983,22 @@ db=glances
|
||||
<pre class="code console literal-block">
|
||||
<span class="generic prompt">$</span> glances --export-influxdb
|
||||
</pre>
|
||||
<p><em>Statsd</em></p>
|
||||
<p>You can export statistics to a Statsd server (welcome to Graphite !). The connection should be defined in the Glances configuration file as following:</p>
|
||||
<p>For Grafana users, Glances provides a dedicated <a class="reference external" href="https://github.com/nicolargo/glances/blob/master/conf/glances-grafana.json">dashboard</a>. Just import
|
||||
the file in your <tt class="docutils literal">Grafana</tt> web interface.</p>
|
||||
<img alt="images/grafana.png" src="images/grafana.png" />
|
||||
</div>
|
||||
<div class="section" id="statsd">
|
||||
<h2><a class="toc-backref" href="#id36">Statsd</a></h2>
|
||||
<p>You can export statistics to a <tt class="docutils literal">Statsd</tt> server (welcome to Graphite!).
|
||||
The connection should be defined in the Glances configuration file as
|
||||
following:</p>
|
||||
<pre class="code literal-block">
|
||||
[statsd]
|
||||
host=localhost
|
||||
port=8125
|
||||
prefix=glances
|
||||
</pre>
|
||||
<p>Note: the prefix option is optionnal ('glances by default')</p>
|
||||
<p><em>Note</em>: the prefix option is optional ('glances by default')</p>
|
||||
<p>and run Glances with:</p>
|
||||
<pre class="code console literal-block">
|
||||
<span class="generic prompt">$</span> glances --export-statsd
|
||||
@ -938,18 +1012,39 @@ prefix=glances
|
||||
...
|
||||
</pre>
|
||||
</div>
|
||||
<div class="section" id="apis-documentations">
|
||||
<h1><a class="toc-backref" href="#id31">APIs Documentations</a></h1>
|
||||
<p>Glances includes a <a class="reference external" href="http://docs.python.org/2/library/simplexmlrpcserver.html">XML-RPC server</a> and a <a class="reference external" href="http://jsonapi.org/">RESTFUL-JSON</a> API which and can be used by another client software.</p>
|
||||
<p>APIs documentations are available at:</p>
|
||||
<div class="section" id="rabbitmq">
|
||||
<h2><a class="toc-backref" href="#id37">RabbitMQ</a></h2>
|
||||
<p>You can export statistics to an <tt class="docutils literal">RabbitMQ</tt> server (AMQP Broker).
|
||||
The connection should be defined in the Glances configuration file as
|
||||
following:</p>
|
||||
<pre class="code literal-block">
|
||||
[rabbitmq]
|
||||
host=localhost
|
||||
port=5672
|
||||
user=glances
|
||||
password=glances
|
||||
queue=glances_queue
|
||||
</pre>
|
||||
<p>and run Glances with:</p>
|
||||
<pre class="code console literal-block">
|
||||
<span class="generic prompt">$</span> glances --export-rabbitmq
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="apis-documentation">
|
||||
<h1><a class="toc-backref" href="#id38">APIs documentation</a></h1>
|
||||
<p>Glances includes a <a class="reference external" href="http://docs.python.org/2/library/simplexmlrpcserver.html">XML-RPC server</a> and a <a class="reference external" href="http://jsonapi.org/">RESTFUL-JSON</a> API which can
|
||||
be used by another client software.</p>
|
||||
<p>APIs documentation is available at:</p>
|
||||
<ul class="simple">
|
||||
<li>XML-RPC: <a class="reference external" href="https://github.com/nicolargo/glances/wiki/The-Glances-2.x-API-How-to">https://github.com/nicolargo/glances/wiki/The-Glances-2.x-API-How-to</a></li>
|
||||
<li>RESTFUL-JSON: <a class="reference external" href="https://github.com/nicolargo/glances/wiki/The-Glances-RESTFULL-JSON-API">https://github.com/nicolargo/glances/wiki/The-Glances-RESTFULL-JSON-API</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="support">
|
||||
<h1><a class="toc-backref" href="#id32">Support</a></h1>
|
||||
<p>To post a question about Glances use case, please post it to the offical Q&A <a class="reference external" href="https://groups.google.com/forum/?hl=en#!forum/glances-users">forum</a>.</p>
|
||||
<h1><a class="toc-backref" href="#id39">Support</a></h1>
|
||||
<p>To post a question about Glances use cases, please post it to the
|
||||
official Q&A <a class="reference external" href="https://groups.google.com/forum/?hl=en#!forum/glances-users">forum</a>.</p>
|
||||
<p>To report a bug or a feature request use the bug tracking system at
|
||||
<a class="reference external" href="https://github.com/nicolargo/glances/issues">https://github.com/nicolargo/glances/issues</a>.</p>
|
||||
<p>Feel free to contribute !</p>
|
||||
|
@ -2,11 +2,11 @@
|
||||
Glances
|
||||
=======
|
||||
|
||||
This manual describes *Glances* version 2.3.
|
||||
This manual describes *Glances* version 2.4.
|
||||
|
||||
Copyright © 2011-2015 Nicolas Hennion <nicolas@nicolargo.com>
|
||||
|
||||
January 2015
|
||||
May 2015
|
||||
|
||||
.. contents:: Table of Contents
|
||||
|
||||
@ -67,7 +67,13 @@ and on the client:
|
||||
|
||||
where ``@server`` is the IP address or hostname of the server.
|
||||
|
||||
Glances can centralize available Glances servers using the ``--browser`` option. The server list can be staticaly defined in the Glances configuration file (section [serverlist]). Glances can also detect and display all Glances servers available on you network (auto discover mode is based on the the Zeroconf protocol only available on GNU/Linux and Mac OS X):
|
||||
Glances can centralize available Glances servers using the ``--browser``
|
||||
option. The server list can be statically defined in the Glances
|
||||
configuration file (section ``[serverlist]``).
|
||||
|
||||
Glances can also detect and display all Glances servers available on your
|
||||
network (auto-discover mode is based on the the ``zeroconf`` protocol,
|
||||
which is only available on GNU/Linux and OS X):
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
@ -97,7 +103,7 @@ client, the latter will try to grab stats using the ``SNMP`` protocol:
|
||||
|
||||
client$ glances -c @snmpserver
|
||||
|
||||
Note: Stats grabbed by SNMP request are limited (operating system dependent).
|
||||
*Note*: stats grabbed by SNMP request are limited (OS dependent).
|
||||
|
||||
Web Server Mode
|
||||
---------------
|
||||
@ -117,7 +123,12 @@ and on the client enter the following URL in your favorite web browser:
|
||||
|
||||
where ``@server`` is the IP address or hostname of the server.
|
||||
|
||||
To change the refresh rate of the page, just add the period in seconds between refreshes at the end of the URL, ie. to refresh every 10s, use ``http://@server:61208/10``.
|
||||
To change the refresh rate of the page, just add the period in seconds
|
||||
at the end of the URL. For example, to refresh the page every 10s:
|
||||
|
||||
::
|
||||
|
||||
http://@server:61208/10
|
||||
|
||||
The Glances web interface follows responsive web design principles.
|
||||
|
||||
@ -125,7 +136,6 @@ Screenshot from Chrome on Android
|
||||
|
||||
.. image:: images/screenshot-web2.png
|
||||
|
||||
|
||||
Command Reference
|
||||
=================
|
||||
|
||||
@ -134,46 +144,45 @@ Command-Line Options
|
||||
|
||||
-h, --help show this help message and exit
|
||||
-V, --version show program's version number and exit
|
||||
-d, --debug Enable debug mode
|
||||
-d, --debug enable debug mode
|
||||
-C CONF_FILE, --config CONF_FILE
|
||||
path to the configuration file
|
||||
--enable-history enable the history mode
|
||||
--disable-bold disable bold mode in the terminal
|
||||
--disable-diskio disable disk I/O module
|
||||
--disable-fs disable filesystem module
|
||||
--disable-network disable network module
|
||||
--disable-ip disable IP module
|
||||
--disable-diskio disable disk I/O module
|
||||
--disable-fs disable file system module
|
||||
--disable-sensors disable sensors module
|
||||
--disable-hddtemp disable hddtemp module
|
||||
--disable-raid disable RAID module
|
||||
--disable-docker disable Docker module
|
||||
--disable-left-sidebar
|
||||
disable left sidebar
|
||||
disable network, disk I/O, file system and
|
||||
sensors modules (py3sensors needed)
|
||||
--disable-process disable process module
|
||||
--disable-log disable log module
|
||||
--disable-quicklook disable quick look module
|
||||
--disable-bold disable bold mode in the terminal
|
||||
--enable-process-extended
|
||||
enable extended stats on top process
|
||||
--enable-history enable the history mode
|
||||
--enable-history enable the history mode (matplotlib needed)
|
||||
--path-history PATH_HISTORY
|
||||
Set the export path for graph history
|
||||
--export-csv CSV_FILE
|
||||
set the export path for graph history
|
||||
--export-csv EXPORT_CSV
|
||||
export stats to a CSV file
|
||||
--export-influxdb
|
||||
export stats to an InfluxDB server
|
||||
--export-statsd
|
||||
export stats to a Statsd server
|
||||
--export-influxdb export stats to an InfluxDB server (influxdb needed)
|
||||
--export-statsd export stats to a StatsD server (statsd needed)
|
||||
--export-rabbitmq export stats to a RabbitMQ server (pika needed)
|
||||
-c CLIENT, --client CLIENT
|
||||
connect to a Glances server by IPv4/IPv6 address or
|
||||
hostname
|
||||
-s, --server run Glances in server mode
|
||||
--browser run the Glances client browser (list of Glances server)
|
||||
--browser start the client browser (list of Glances servers)
|
||||
--disable-autodiscover
|
||||
disable autodiscover feature
|
||||
-p PORT, --port PORT define the client/server TCP port [default: 61209]
|
||||
-B BIND_ADDRESS, --bind BIND_ADDRESS
|
||||
bind server to the given IPv4/IPv6 address or hostname
|
||||
--password-badidea PASSWORD_ARG
|
||||
define password from the command line
|
||||
--password define a client/server password from the prompt or
|
||||
file
|
||||
--disable-autodiscover
|
||||
Hide Glances server from the auto discover feature
|
||||
--password define a client/server password
|
||||
--snmp-community SNMP_COMMUNITY
|
||||
SNMP community
|
||||
--snmp-port SNMP_PORT
|
||||
@ -186,17 +195,18 @@ Command-Line Options
|
||||
SNMP authentication key (only for SNMPv3)
|
||||
--snmp-force force SNMP mode
|
||||
-t TIME, --time TIME set refresh time in seconds [default: 3 sec]
|
||||
-w, --webserver run Glances in web server mode
|
||||
-w, --webserver run Glances in web server mode (bottle needed)
|
||||
-q, --quiet do not display the curses interface
|
||||
-f PROCESS_FILTER, --process-filter PROCESS_FILTER
|
||||
set the process filter patern (regular expression)
|
||||
set the process filter pattern (regular expression)
|
||||
--process-short-name force short name for processes name
|
||||
--hide-kernel-threads
|
||||
hide kernel threads in process list
|
||||
--tree display processes as a tree
|
||||
-b, --byte display network rate in byte per second
|
||||
-1, --percpu start Glances in per CPU mode
|
||||
--fs-free-space display FS free space instead of used
|
||||
--theme-white optimize display for white background
|
||||
--fs-free-space display file system free space instead of used
|
||||
--theme-white optimize display colors for white background
|
||||
|
||||
Interactive Commands
|
||||
--------------------
|
||||
@ -226,7 +236,7 @@ The following commands (key pressed) are supported while in Glances:
|
||||
``f``
|
||||
Show/hide file system stats
|
||||
``F``
|
||||
Switch between FS used and free space
|
||||
Switch between file system used and free space
|
||||
``g``
|
||||
Generate graphs for current history
|
||||
``h``
|
||||
@ -252,6 +262,8 @@ The following commands (key pressed) are supported while in Glances:
|
||||
``T``
|
||||
View network I/O as combination
|
||||
``u``
|
||||
Sort processes by USER
|
||||
``U``
|
||||
View cumulative network I/O
|
||||
``w``
|
||||
Delete finished warning log messages
|
||||
@ -263,10 +275,13 @@ The following commands (key pressed) are supported while in Glances:
|
||||
Switch between global CPU and per-CPU stats
|
||||
``2``
|
||||
Enable/disable left sidebar
|
||||
``3``
|
||||
Enable/disable the quick look module
|
||||
``/``
|
||||
Switch between short name / command line (processes name)
|
||||
|
||||
In the Glances client browser (accessible through the --browser command line argument):
|
||||
In the Glances client browser (accessible through the ``--browser``
|
||||
command line argument):
|
||||
|
||||
``ENTER``
|
||||
Run Glances client to the selected server
|
||||
@ -282,15 +297,20 @@ Configuration
|
||||
|
||||
No configuration file is mandatory to use Glances.
|
||||
|
||||
Furthermore a configuration file is needed to set up limits, disks or
|
||||
network interfaces to hide and/or monitored processes list or to define
|
||||
alias.
|
||||
Furthermore a configuration file is needed to modify limit alerts, to
|
||||
set up monitored processes list, to hide disks or network interfaces or
|
||||
to define alias.
|
||||
|
||||
By default, the configuration file is under:
|
||||
Location
|
||||
--------
|
||||
|
||||
:Linux: ``/etc/glances/glances.conf``
|
||||
:\*BSD and OS X: ``/usr/local/etc/glances/glances.conf``
|
||||
:Windows: ``%APPDATA%\glances\glances.conf``
|
||||
You can put the configuration file ``glances.conf`` in the following
|
||||
locations:
|
||||
|
||||
:Linux: ``~/.config/glances, /etc/glances``
|
||||
:\*BSD: ``~/.config/glances, /usr/local/etc/glances``
|
||||
:OS X: ``~/Library/Application Support/glances, /usr/local/etc/glances``
|
||||
:Windows: ``%APPDATA%\glances``
|
||||
|
||||
On Windows XP, the ``%APPDATA%`` path is:
|
||||
|
||||
@ -303,24 +323,12 @@ Since Windows Vista and newer versions:
|
||||
::
|
||||
|
||||
C:\Users\<User>\AppData\Roaming
|
||||
or
|
||||
%userprofile%\AppData\Roaming
|
||||
|
||||
You can override the default configuration, located in one of the above
|
||||
directories on your system, except for Windows.
|
||||
User-specific options override system-wide options and options given on
|
||||
the command line override either.
|
||||
|
||||
Just copy the ``glances.conf`` file to your ``$XDG_CONFIG_HOME`` directory,
|
||||
e.g., on Linux:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
mkdir -p $XDG_CONFIG_HOME/glances
|
||||
cp /usr/share/doc/glances/glances.conf $XDG_CONFIG_HOME/glances/
|
||||
|
||||
On OS X, you should copy the configuration file to
|
||||
``~/Library/Application Support/glances/``.
|
||||
|
||||
*Configuration file description*
|
||||
Syntax
|
||||
------
|
||||
|
||||
Each plugin and export module can have a section.
|
||||
|
||||
@ -342,7 +350,8 @@ Example for the CPU plugin:
|
||||
steal_warning=70
|
||||
steal_critical=90
|
||||
|
||||
By default Steal CPU time alerts aren't logged. If you want to enable log/alert, just add:
|
||||
By default the ``steal`` CPU time alerts aren't logged. If you want to
|
||||
enable log/alert, just add:
|
||||
|
||||
.. code-block::
|
||||
|
||||
@ -357,10 +366,11 @@ can ben logged using the -d option on the command line.
|
||||
|
||||
By default, the log file is under:
|
||||
|
||||
:Linux, \*BSD and OS X: ``/tmp/glances.log``
|
||||
:Linux, \*BSD, OS X: ``/tmp/glances.log``
|
||||
:Windows: ``%APPDATA%\Local\temp\glances.log``
|
||||
|
||||
If glances.log is not writable, a new file will be created and returned to the user console.
|
||||
If ``glances.log`` is not writable, a new file will be created and
|
||||
returned to the user console.
|
||||
|
||||
Anatomy Of The Application
|
||||
==========================
|
||||
@ -395,6 +405,17 @@ Disconnected:
|
||||
|
||||
.. image:: images/disconnected.png
|
||||
|
||||
QuickLook
|
||||
---------
|
||||
|
||||
The ``quicklook`` plugin is only displayed on wide screen and propose a
|
||||
bar view for CPU and memory (virtual and swap).
|
||||
|
||||
.. image:: images/quicklook.png
|
||||
|
||||
*Note*: limit values can be overwritten in the configuration file under
|
||||
the ``[quicklook]`` section.
|
||||
|
||||
CPU
|
||||
---
|
||||
|
||||
@ -518,6 +539,15 @@ If a RAID controller is detected on you system, its status will be displayed:
|
||||
|
||||
.. image:: images/raid.png
|
||||
|
||||
By default, the plugin only displays physical devices (hard disks, USB
|
||||
keys) and ignore all others. To allow others FS type, you have to use the
|
||||
following section in the configuration file:
|
||||
|
||||
::
|
||||
|
||||
[fs]
|
||||
allow=zfs,misc
|
||||
|
||||
Sensors
|
||||
-------
|
||||
|
||||
@ -533,8 +563,8 @@ temperature only.
|
||||
|
||||
There is no alert on this information.
|
||||
|
||||
*Note*: limit values and sensors alias names can be defined in the configuration
|
||||
file under the ``[sensors]`` section.
|
||||
*Note*: limit values and sensors alias names can be defined in the
|
||||
configuration file under the ``[sensors]`` section.
|
||||
|
||||
Processes List
|
||||
--------------
|
||||
@ -611,7 +641,8 @@ Process status legend:
|
||||
``Z``
|
||||
Zombie
|
||||
|
||||
In standalone mode, additionals informations are provided for the top process:
|
||||
In standalone mode, additional informations are provided for the top
|
||||
process:
|
||||
|
||||
.. image:: images/processlist-top.png
|
||||
|
||||
@ -620,7 +651,8 @@ In standalone mode, additionals informations are provided for the top process:
|
||||
* Open threads, files and network sessions (TCP and UDP)
|
||||
* IO nice level
|
||||
|
||||
The extended stats feature could be enabled using the --enable-process-extended option (command line) or the ``e`` key (curses interface).
|
||||
The extended stats feature could be enabled using the ``--enable-process-extended``
|
||||
option (command line) or the ``e`` key (curses interface).
|
||||
|
||||
*Note*: limit values can be overwritten in the configuration file under
|
||||
the ``[process]`` section.
|
||||
@ -670,10 +702,10 @@ another item:
|
||||
list_1_command=nginx -v
|
||||
list_1_countmin=1
|
||||
list_1_countmax=4
|
||||
list_1_description=PHP-FPM
|
||||
list_1_regex=.*php-fpm.*
|
||||
list_1_countmin=1
|
||||
list_1_countmax=20
|
||||
list_2_description=PHP-FPM
|
||||
list_2_regex=.*php-fpm.*
|
||||
list_2_countmin=1
|
||||
list_2_countmax=20
|
||||
|
||||
In client/server mode, the list is defined on the server side.
|
||||
A new method, called `getAllMonitored`, is available in the APIs and
|
||||
@ -708,7 +740,8 @@ Each alert message displays the following information:
|
||||
Docker
|
||||
------
|
||||
|
||||
If you use Docker, Glances can help you to monitor your container. Glances uses the Docker API through the Docker-Py library.
|
||||
If you use ``Docker``, Glances can help you to monitor your container.
|
||||
Glances uses the Docker API through the ``docker-py`` library.
|
||||
|
||||
.. image:: images/docker.png
|
||||
|
||||
@ -717,7 +750,9 @@ Actions
|
||||
|
||||
Glances can trigger actions on events.
|
||||
|
||||
By action, we mean all shell command line. For example, if you want to execute the foo.py script if the last 5 minutes load are critical then add the action line to the Glances configuration file:
|
||||
By ``action``, we mean all shell command line. For example, if you want
|
||||
to execute the ``foo.py`` script if the last 5 minutes load are critical
|
||||
then add the action line to the Glances configuration file:
|
||||
|
||||
.. code-block::
|
||||
|
||||
@ -725,7 +760,9 @@ By action, we mean all shell command line. For example, if you want to execute t
|
||||
critical=5.0
|
||||
critical_action=python /path/to/foo.py
|
||||
|
||||
All the stats are available in the command line through the use of the {{mustache}} syntax. Another example to create a log file containing used vs total disk space if a space trigger warning is reached:
|
||||
All the stats are available in the command line through the use of the
|
||||
``{{mustache}}`` syntax. Another example would be to create a log file
|
||||
containing used vs total disk space if a space trigger warning is reached:
|
||||
|
||||
.. code-block::
|
||||
|
||||
@ -733,13 +770,15 @@ All the stats are available in the command line through the use of the {{mustach
|
||||
warning=70
|
||||
warning_action=echo {{mnt_point}} {{used}}/{{size}} > /tmp/fs.alert
|
||||
|
||||
*Note*: You can use all the stats for the current plugin (see https://github.com/nicolargo/glances/wiki/The-Glances-2.x-API-How-to for the stats list)
|
||||
|
||||
*Note*: you can use all the stats for the current plugin (see
|
||||
https://github.com/nicolargo/glances/wiki/The-Glances-2.x-API-How-to for
|
||||
the stats list)
|
||||
|
||||
Gateway to others services
|
||||
==========================
|
||||
|
||||
*CSV*
|
||||
CSV
|
||||
---
|
||||
|
||||
It is possible to export statistics to CSV file.
|
||||
|
||||
@ -751,9 +790,12 @@ CSV file description:
|
||||
- Stats description (first line)
|
||||
- Stats (others lines)
|
||||
|
||||
*InfluxDB*
|
||||
InfluxDB
|
||||
--------
|
||||
|
||||
You can export statistics to an InfluxDB server (time series server). The connection should be defined in the Glances configuration file as following:
|
||||
You can export statistics to an ``InfluxDB`` server (time series server).
|
||||
The connection should be defined in the Glances configuration file as
|
||||
following:
|
||||
|
||||
.. code-block::
|
||||
|
||||
@ -770,9 +812,17 @@ and run Glances with:
|
||||
|
||||
$ glances --export-influxdb
|
||||
|
||||
*Statsd*
|
||||
For Grafana users, Glances provides a dedicated `dashboard`_. Just import
|
||||
the file in your ``Grafana`` web interface.
|
||||
|
||||
You can export statistics to a Statsd server (welcome to Graphite !). The connection should be defined in the Glances configuration file as following:
|
||||
.. image:: images/grafana.png
|
||||
|
||||
Statsd
|
||||
------
|
||||
|
||||
You can export statistics to a ``Statsd`` server (welcome to Graphite!).
|
||||
The connection should be defined in the Glances configuration file as
|
||||
following:
|
||||
|
||||
.. code-block::
|
||||
|
||||
@ -781,7 +831,7 @@ You can export statistics to a Statsd server (welcome to Graphite !). The connec
|
||||
port=8125
|
||||
prefix=glances
|
||||
|
||||
Note: the prefix option is optionnal ('glances by default')
|
||||
*Note*: the prefix option is optional ('glances by default')
|
||||
|
||||
and run Glances with:
|
||||
|
||||
@ -799,13 +849,35 @@ Glances will generate stats as:
|
||||
'glances.load.min1': 0.19,
|
||||
...
|
||||
|
||||
RabbitMQ
|
||||
--------
|
||||
|
||||
APIs Documentations
|
||||
===================
|
||||
You can export statistics to an ``RabbitMQ`` server (AMQP Broker).
|
||||
The connection should be defined in the Glances configuration file as
|
||||
following:
|
||||
|
||||
Glances includes a `XML-RPC server`_ and a `RESTFUL-JSON`_ API which and can be used by another client software.
|
||||
.. code-block::
|
||||
|
||||
APIs documentations are available at:
|
||||
[rabbitmq]
|
||||
host=localhost
|
||||
port=5672
|
||||
user=glances
|
||||
password=glances
|
||||
queue=glances_queue
|
||||
|
||||
and run Glances with:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances --export-rabbitmq
|
||||
|
||||
APIs documentation
|
||||
==================
|
||||
|
||||
Glances includes a `XML-RPC server`_ and a `RESTFUL-JSON`_ API which can
|
||||
be used by another client software.
|
||||
|
||||
APIs documentation is available at:
|
||||
|
||||
- XML-RPC: https://github.com/nicolargo/glances/wiki/The-Glances-2.x-API-How-to
|
||||
- RESTFUL-JSON: https://github.com/nicolargo/glances/wiki/The-Glances-RESTFULL-JSON-API
|
||||
@ -813,7 +885,8 @@ APIs documentations are available at:
|
||||
Support
|
||||
=======
|
||||
|
||||
To post a question about Glances use case, please post it to the offical Q&A `forum`_.
|
||||
To post a question about Glances use cases, please post it to the
|
||||
official Q&A `forum`_.
|
||||
|
||||
To report a bug or a feature request use the bug tracking system at
|
||||
https://github.com/nicolargo/glances/issues.
|
||||
@ -828,3 +901,4 @@ Feel free to contribute !
|
||||
.. _XML-RPC server: http://docs.python.org/2/library/simplexmlrpcserver.html
|
||||
.. _RESTFUL-JSON: http://jsonapi.org/
|
||||
.. _forum: https://groups.google.com/forum/?hl=en#!forum/glances-users
|
||||
.. _dashboard: https://github.com/nicolargo/glances/blob/master/conf/glances-grafana.json
|
||||
|
BIN
docs/images/grafana.png
Normal file
After Width: | Height: | Size: 198 KiB |
BIN
docs/images/quicklook.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 198 KiB After Width: | Height: | Size: 258 KiB |
Before Width: | Height: | Size: 242 KiB After Width: | Height: | Size: 161 KiB |
Before Width: | Height: | Size: 427 KiB After Width: | Height: | Size: 600 KiB |
Before Width: | Height: | Size: 182 KiB After Width: | Height: | Size: 496 KiB |
@ -1,6 +1,7 @@
|
||||
You are in the main Glances's source folder. This page is **ONLY** for developpers purposes.
|
||||
You are in the main Glances source folder. This page is **ONLY** for developers.
|
||||
|
||||
If you are looking for the user manual, please follow this link: https://github.com/nicolargo/glances/blob/master/docs/glances-doc.rst
|
||||
If you are looking for the user manual, please follow this link:
|
||||
https://github.com/nicolargo/glances/blob/master/docs/glances-doc.rst
|
||||
|
||||
===
|
||||
|
||||
@ -9,11 +10,10 @@ __main__.py Entry point for Glances module
|
||||
core/
|
||||
=> Glances core folder
|
||||
glances_config.py Manage configuration file
|
||||
glances_globals.py Share variables uppon modules
|
||||
glances_globals.py Share variables upon modules
|
||||
glances_limits.py Manage limits
|
||||
glances_logs.py Manage logs
|
||||
glances_main.py Main script to rule them up...
|
||||
glances_stats.py Inteface to grab stats
|
||||
glances_client.py Glances client
|
||||
glances_server.py Glances server
|
||||
glances_standalone.py Glances standalone (with curse interface)
|
||||
|
@ -20,12 +20,11 @@
|
||||
"""Init the Glances software."""
|
||||
|
||||
__appname__ = 'glances'
|
||||
__version__ = '2.3'
|
||||
__version__ = '2.4'
|
||||
__author__ = 'Nicolas Hennion <nicolas@nicolargo.com>'
|
||||
__license__ = 'LGPL'
|
||||
|
||||
# Import system lib
|
||||
import gettext
|
||||
import locale
|
||||
import platform
|
||||
import signal
|
||||
@ -40,23 +39,24 @@ except ImportError:
|
||||
|
||||
# Import Glances libs
|
||||
# Note: others Glances libs will be imported optionally
|
||||
from glances.core.glances_globals import gettext_domain, locale_dir
|
||||
from glances.core.glances_logging import logger
|
||||
from glances.core.glances_main import GlancesMain
|
||||
|
||||
# Get PSutil version
|
||||
psutil_min_version = (2, 0, 0)
|
||||
psutil_version = tuple([int(num) for num in __psutil_version.split('.')])
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
except locale.Error:
|
||||
print("Warning: Unable to set locale. Expect encoding problems.")
|
||||
|
||||
# First log with Glances and PSUtil version
|
||||
logger.info('Start Glances {0}'.format(__version__))
|
||||
logger.info('{0} {1} and PSutil {2} detected'.format(platform.python_implementation(),
|
||||
platform.python_version(),
|
||||
__psutil_version))
|
||||
# Check Python version
|
||||
if sys.version_info < (2, 6) or (3, 0) <= sys.version_info < (3, 3):
|
||||
print('Glances requires at least Python 2.6 or 3.3 to run.')
|
||||
sys.exit(1)
|
||||
|
||||
# Check PSutil version
|
||||
psutil_min_version = (2, 0, 0)
|
||||
psutil_version = tuple([int(num) for num in __psutil_version.split('.')])
|
||||
if psutil_version < psutil_min_version:
|
||||
logger.critical('PSutil 2.0 or higher is needed. Glances cannot start.')
|
||||
print('PSutil 2.0 or higher is needed. Glances cannot start.')
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@ -94,9 +94,12 @@ def main():
|
||||
Select the mode (standalone, client or server)
|
||||
Run it...
|
||||
"""
|
||||
# Setup translations
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
gettext.install(gettext_domain, locale_dir)
|
||||
# Log Glances and PSutil version
|
||||
logger.info('Start Glances {0}'.format(__version__))
|
||||
logger.info('{0} {1} and PSutil {2} detected'.format(
|
||||
platform.python_implementation(),
|
||||
platform.python_version(),
|
||||
__psutil_version))
|
||||
|
||||
# Share global var
|
||||
global core, standalone, client, server, webserver
|
||||
@ -164,7 +167,7 @@ def main():
|
||||
server = GlancesServer(cached_time=core.cached_time,
|
||||
config=core.get_config(),
|
||||
args=args)
|
||||
print(_("Glances server is running on {0}:{1}").format(args.bind_address, args.port))
|
||||
print('Glances server is running on {0}:{1}'.format(args.bind_address, args.port))
|
||||
|
||||
# Set the server login/password (if -P/--password tag)
|
||||
if args.password != "":
|
||||
|
@ -19,11 +19,12 @@
|
||||
|
||||
"""Manage on alert actions."""
|
||||
|
||||
# Import system lib
|
||||
from subprocess import Popen
|
||||
|
||||
# Import Glances lib
|
||||
from glances.core.glances_logging import logger
|
||||
|
||||
# Import system lib
|
||||
from subprocess import Popen
|
||||
try:
|
||||
import pystache
|
||||
except ImportError:
|
||||
@ -35,11 +36,10 @@ else:
|
||||
|
||||
class GlancesActions(object):
|
||||
|
||||
"""This class manage action if an alert is reached"""
|
||||
"""This class manage action if an alert is reached."""
|
||||
|
||||
def __init__(self):
|
||||
"""Init GlancesActions class"""
|
||||
|
||||
"""Init GlancesActions class."""
|
||||
# Dict with the criticity status
|
||||
# - key: stat_name
|
||||
# - value: criticity
|
||||
@ -47,25 +47,26 @@ class GlancesActions(object):
|
||||
self.status = {}
|
||||
|
||||
def get(self, stat_name):
|
||||
"""Get the stat_name criticity"""
|
||||
"""Get the stat_name criticity."""
|
||||
try:
|
||||
return self.status[stat_name]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def set(self, stat_name, criticity):
|
||||
"""Set the stat_name to criticity"""
|
||||
"""Set the stat_name to criticity."""
|
||||
self.status[stat_name] = criticity
|
||||
|
||||
def run(self, stat_name, criticity, commands, mustache_dict=None):
|
||||
"""Run the commands (in background)
|
||||
"""Run the commands (in background).
|
||||
|
||||
- stats_name: plugin_name (+ header)
|
||||
- criticity: criticity of the trigger
|
||||
- commands: a list of command line with optional {{mustache}}
|
||||
- mustache_dict: Plugin stats (can be use within {{mustache}})
|
||||
|
||||
Return True if the commands have been ran"""
|
||||
|
||||
Return True if the commands have been ran.
|
||||
"""
|
||||
if self.get(stat_name) == criticity:
|
||||
# Action already executed => Exit
|
||||
return False
|
||||
@ -75,7 +76,7 @@ class GlancesActions(object):
|
||||
criticity,
|
||||
mustache_dict))
|
||||
|
||||
# Ran all actions in background
|
||||
# Run all actions in background
|
||||
for cmd in commands:
|
||||
# Replace {{arg}} by the dict one (Thk to {Mustache})
|
||||
if pystache_tag:
|
||||
|
@ -22,11 +22,7 @@
|
||||
# Import system libs
|
||||
import socket
|
||||
import sys
|
||||
try:
|
||||
import netifaces
|
||||
netifaces_tag = True
|
||||
except ImportError:
|
||||
netifaces_tag = False
|
||||
|
||||
try:
|
||||
from zeroconf import (
|
||||
__version__ as __zeroconf_version,
|
||||
@ -42,13 +38,13 @@ except ImportError:
|
||||
from glances.core.glances_globals import appname
|
||||
from glances.core.glances_logging import logger
|
||||
|
||||
# Zeroconf 0.16 or higher is needed
|
||||
# Zeroconf 0.17 or higher is needed
|
||||
if zeroconf_tag:
|
||||
zeroconf_min_version = (0, 16, 0)
|
||||
zeroconf_min_version = (0, 17, 0)
|
||||
zeroconf_version = tuple([int(num) for num in __zeroconf_version.split('.')])
|
||||
logger.debug("Zeroconf version {0} detected.".format(__zeroconf_version))
|
||||
if zeroconf_version < zeroconf_min_version:
|
||||
logger.critical("Please install zeroconf 0.16 or higher.")
|
||||
logger.critical("Please install zeroconf 0.17 or higher.")
|
||||
sys.exit(1)
|
||||
|
||||
# Global var
|
||||
@ -57,7 +53,7 @@ zeroconf_type = "_%s._tcp." % appname
|
||||
|
||||
class AutoDiscovered(object):
|
||||
|
||||
"""Class to manage the auto discovered servers dict"""
|
||||
"""Class to manage the auto discovered servers dict."""
|
||||
|
||||
def __init__(self):
|
||||
# server_dict is a list of dict (JSON compliant)
|
||||
@ -65,30 +61,30 @@ class AutoDiscovered(object):
|
||||
self._server_list = []
|
||||
|
||||
def get_servers_list(self):
|
||||
"""Return the current server list (list of dict)"""
|
||||
"""Return the current server list (list of dict)."""
|
||||
return self._server_list
|
||||
|
||||
def set_server(self, server_pos, key, value):
|
||||
"""Set the key to the value for the server_pos (position in the list)"""
|
||||
"""Set the key to the value for the server_pos (position in the list)."""
|
||||
self._server_list[server_pos][key] = value
|
||||
|
||||
def add_server(self, name, ip, port):
|
||||
"""Add a new server to the list"""
|
||||
new_server = {'key': name, # Zeroconf name with both hostname and port
|
||||
"""Add a new server to the list."""
|
||||
new_server = {
|
||||
'key': name, # Zeroconf name with both hostname and port
|
||||
'name': name.split(':')[0], # Short name
|
||||
'ip': ip, # IP address seen by the client
|
||||
'port': port, # TCP port
|
||||
'username': 'glances', # Default username
|
||||
'password': '', # Default password
|
||||
'status': 'UNKNOWN', # Server status: 'UNKNOWN', 'OFFLINE', 'ONLINE', 'PROTECTED'
|
||||
'type': 'DYNAMIC', # Server type: 'STATIC' or 'DYNAMIC'
|
||||
}
|
||||
'type': 'DYNAMIC'} # Server type: 'STATIC' or 'DYNAMIC'
|
||||
self._server_list.append(new_server)
|
||||
logger.debug("Updated servers list (%s servers): %s" %
|
||||
(len(self._server_list), self._server_list))
|
||||
|
||||
def remove_server(self, name):
|
||||
"""Remove a server from the dict"""
|
||||
"""Remove a server from the dict."""
|
||||
for i in self._server_list:
|
||||
if i['key'] == name:
|
||||
try:
|
||||
@ -103,22 +99,23 @@ class AutoDiscovered(object):
|
||||
|
||||
class GlancesAutoDiscoverListener(object):
|
||||
|
||||
"""Zeroconf listener for Glances server"""
|
||||
"""Zeroconf listener for Glances server."""
|
||||
|
||||
def __init__(self):
|
||||
# Create an instance of the servers list
|
||||
self.servers = AutoDiscovered()
|
||||
|
||||
def get_servers_list(self):
|
||||
"""Return the current server list (list of dict)"""
|
||||
"""Return the current server list (list of dict)."""
|
||||
return self.servers.get_servers_list()
|
||||
|
||||
def set_server(self, server_pos, key, value):
|
||||
"""Set the key to the value for the server_pos (position in the list)"""
|
||||
"""Set the key to the value for the server_pos (position in the list)."""
|
||||
self.servers.set_server(server_pos, key, value)
|
||||
|
||||
def add_service(self, zeroconf, srv_type, srv_name):
|
||||
"""Method called when a new Zeroconf client is detected
|
||||
"""Method called when a new Zeroconf client is detected.
|
||||
|
||||
Return True if the zeroconf client is a Glances server
|
||||
Note: the return code will never be used
|
||||
"""
|
||||
@ -141,7 +138,7 @@ class GlancesAutoDiscoverListener(object):
|
||||
return True
|
||||
|
||||
def remove_service(self, zeroconf, srv_type, srv_name):
|
||||
# Remove the server from the list
|
||||
"""Remove the server from the list."""
|
||||
self.servers.remove_server(srv_name)
|
||||
logger.info(
|
||||
"Glances server %s removed from the autodetect list" % srv_name)
|
||||
@ -149,7 +146,7 @@ class GlancesAutoDiscoverListener(object):
|
||||
|
||||
class GlancesAutoDiscoverServer(object):
|
||||
|
||||
"""Implementation of the Zeroconf protocol (server side for the Glances client)"""
|
||||
"""Implementation of the Zeroconf protocol (server side for the Glances client)."""
|
||||
|
||||
def __init__(self, args=None):
|
||||
if zeroconf_tag:
|
||||
@ -169,14 +166,14 @@ class GlancesAutoDiscoverServer(object):
|
||||
self.zeroconf_enable_tag = False
|
||||
|
||||
def get_servers_list(self):
|
||||
"""Return the current server list (dict of dict)"""
|
||||
"""Return the current server list (dict of dict)."""
|
||||
if zeroconf_tag and self.zeroconf_enable_tag:
|
||||
return self.listener.get_servers_list()
|
||||
else:
|
||||
return []
|
||||
|
||||
def set_server(self, server_pos, key, value):
|
||||
"""Set the key to the value for the server_pos (position in the list)"""
|
||||
"""Set the key to the value for the server_pos (position in the list)."""
|
||||
if zeroconf_tag and self.zeroconf_enable_tag:
|
||||
self.listener.set_server(server_pos, key, value)
|
||||
|
||||
@ -197,16 +194,15 @@ class GlancesAutoDiscoverClient(object):
|
||||
except socket.error as e:
|
||||
logger.error("Cannot start zeroconf: {0}".format(e))
|
||||
|
||||
if netifaces_tag:
|
||||
try:
|
||||
# -B @ overwrite the dynamic IPv4 choice
|
||||
if zeroconf_bind_address == '0.0.0.0':
|
||||
zeroconf_bind_address = self.find_active_ip_address()
|
||||
else:
|
||||
logger.error("Couldn't find the active IP address: netifaces library not found.")
|
||||
except KeyError:
|
||||
# Issue #528 (no network interface available)
|
||||
pass
|
||||
|
||||
logger.info("Announce the Glances server on the LAN (using {0} IP address)".format(zeroconf_bind_address))
|
||||
print("Announce the Glances server on the LAN (using {0} IP address)".format(zeroconf_bind_address))
|
||||
|
||||
self.info = ServiceInfo(
|
||||
zeroconf_type, '{0}:{1}.{2}'.format(hostname, args.port, zeroconf_type),
|
||||
address=socket.inet_aton(zeroconf_bind_address), port=args.port,
|
||||
@ -215,15 +211,14 @@ class GlancesAutoDiscoverClient(object):
|
||||
else:
|
||||
logger.error("Cannot announce Glances server on the network: zeroconf library not found.")
|
||||
|
||||
def find_active_ip_address(self):
|
||||
@staticmethod
|
||||
def find_active_ip_address():
|
||||
"""Try to find the active IP addresses."""
|
||||
try:
|
||||
import netifaces
|
||||
# Interface of the default gateway
|
||||
gateway_itf = netifaces.gateways()['default'][netifaces.AF_INET][1]
|
||||
# IP address for the interface
|
||||
return netifaces.ifaddresses(gateway_itf)[netifaces.AF_INET][0]['addr']
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def close(self):
|
||||
if zeroconf_tag:
|
||||
|
@ -28,11 +28,6 @@ try:
|
||||
except ImportError:
|
||||
# Python 2
|
||||
from xmlrpclib import Transport, ServerProxy, ProtocolError, Fault
|
||||
try:
|
||||
import http.client as httplib
|
||||
except ImportError:
|
||||
# Python 2
|
||||
import httplib
|
||||
|
||||
# Import Glances libs
|
||||
from glances.core.glances_globals import version
|
||||
@ -43,7 +38,7 @@ from glances.outputs.glances_curses import GlancesCursesClient
|
||||
|
||||
class GlancesClientTransport(Transport):
|
||||
|
||||
"""This class overwrite the default XML-RPC transport and manage timeout"""
|
||||
"""This class overwrite the default XML-RPC transport and manage timeout."""
|
||||
|
||||
def set_timeout(self, timeout):
|
||||
self.timeout = timeout
|
||||
@ -58,8 +53,8 @@ class GlancesClient(object):
|
||||
self.args = args
|
||||
self.config = config
|
||||
|
||||
# Client mode:
|
||||
self.set_mode()
|
||||
# Default client mode
|
||||
self._client_mode = 'glances'
|
||||
|
||||
# Return to browser or exit
|
||||
self.return_to_browser = return_to_browser
|
||||
@ -82,29 +77,26 @@ class GlancesClient(object):
|
||||
self.log_and_exit("Client couldn't create socket {0}: {1}".format(uri, e))
|
||||
|
||||
def log_and_exit(self, msg=''):
|
||||
"""Log and (exit)"""
|
||||
"""Log and exit."""
|
||||
if not self.return_to_browser:
|
||||
logger.critical(msg)
|
||||
sys.exit(2)
|
||||
else:
|
||||
logger.error(msg)
|
||||
|
||||
def set_mode(self, mode='glances'):
|
||||
@property
|
||||
def client_mode(self):
|
||||
"""Get the client mode."""
|
||||
return self._client_mode
|
||||
|
||||
@client_mode.setter
|
||||
def client_mode(self, mode):
|
||||
"""Set the client mode.
|
||||
|
||||
- 'glances' = Glances server (default)
|
||||
- 'snmp' = SNMP (fallback)
|
||||
"""
|
||||
self.mode = mode
|
||||
return self.mode
|
||||
|
||||
def get_mode(self):
|
||||
"""Get the client mode.
|
||||
|
||||
- 'glances' = Glances server (default)
|
||||
- 'snmp' = SNMP (fallback)
|
||||
"""
|
||||
return self.mode
|
||||
self._client_mode = mode
|
||||
|
||||
def login(self):
|
||||
"""Logon to the server."""
|
||||
@ -112,15 +104,14 @@ class GlancesClient(object):
|
||||
|
||||
if not self.args.snmp_force:
|
||||
# First of all, trying to connect to a Glances server
|
||||
self.set_mode('glances')
|
||||
client_version = None
|
||||
try:
|
||||
client_version = self.client.init()
|
||||
except socket.error as err:
|
||||
# Fallback to SNMP
|
||||
logger.error("Connection to Glances server failed (%s)" % err)
|
||||
self.set_mode('snmp')
|
||||
fallbackmsg = _("Trying fallback to SNMP...")
|
||||
self.client_mode = 'snmp'
|
||||
logger.error("Connection to Glances server failed: {0}".format(err))
|
||||
fallbackmsg = 'No Glances server found. Trying fallback to SNMP...'
|
||||
if not self.return_to_browser:
|
||||
print(fallbackmsg)
|
||||
else:
|
||||
@ -134,22 +125,25 @@ class GlancesClient(object):
|
||||
self.log_and_exit(msg)
|
||||
return False
|
||||
|
||||
if self.get_mode() == 'glances' and version.split('.')[0] == client_version.split('.')[0]:
|
||||
if self.client_mode == 'glances':
|
||||
# Check that both client and server are in the same major version
|
||||
if version.split('.')[0] == client_version.split('.')[0]:
|
||||
# Init stats
|
||||
self.stats = GlancesStatsClient(config=self.config, args=self.args)
|
||||
self.stats.set_plugins(json.loads(self.client.getAllPlugins()))
|
||||
logger.debug(
|
||||
"Client version: %s / Server version: %s" % (version, client_version))
|
||||
elif self.get_mode() == 'glances':
|
||||
self.log_and_exit("Client and server not compatible: Client version: %s / Server version: %s" % (version, client_version))
|
||||
logger.debug("Client version: {0} / Server version: {1}".format(version, client_version))
|
||||
else:
|
||||
self.log_and_exit("Client and server not compatible: \
|
||||
Client version: {0} / Server version: {1}".format(version, client_version))
|
||||
return False
|
||||
|
||||
else:
|
||||
self.set_mode('snmp')
|
||||
self.client_mode = 'snmp'
|
||||
|
||||
if self.get_mode() == 'snmp':
|
||||
# SNMP mode
|
||||
if self.client_mode == 'snmp':
|
||||
logger.info("Trying to grab stats by SNMP...")
|
||||
# Fallback to SNMP if needed
|
||||
|
||||
from glances.core.glances_stats import GlancesStatsClientSNMP
|
||||
|
||||
# Init stats
|
||||
@ -172,13 +166,13 @@ class GlancesClient(object):
|
||||
|
||||
def update(self):
|
||||
"""Update stats from Glances/SNMP server."""
|
||||
if self.get_mode() == 'glances':
|
||||
if self.client_mode == 'glances':
|
||||
return self.update_glances()
|
||||
elif self.get_mode() == 'snmp':
|
||||
elif self.client_mode == 'snmp':
|
||||
return self.update_snmp()
|
||||
else:
|
||||
self.end()
|
||||
logger.critical("Unknown server mode: {0}".format(self.get_mode()))
|
||||
logger.critical("Unknown server mode: {0}".format(self.client_mode))
|
||||
sys.exit(2)
|
||||
|
||||
def update_glances(self):
|
||||
@ -222,9 +216,8 @@ class GlancesClient(object):
|
||||
|
||||
def serve_forever(self):
|
||||
"""Main client loop."""
|
||||
|
||||
exitkey = False
|
||||
|
||||
try:
|
||||
while True and not exitkey:
|
||||
# Update the stats
|
||||
cs_status = self.update()
|
||||
@ -236,8 +229,11 @@ class GlancesClient(object):
|
||||
|
||||
# Export stats using export modules
|
||||
self.stats.export(self.stats)
|
||||
except Exception as e:
|
||||
logger.critical(e)
|
||||
self.end()
|
||||
|
||||
return self.get_mode()
|
||||
return self.client_mode
|
||||
|
||||
def end(self):
|
||||
"""End of the client session."""
|
||||
|
@ -38,7 +38,7 @@ from glances.outputs.glances_curses import GlancesCursesBrowser
|
||||
|
||||
class GlancesClientBrowser(object):
|
||||
|
||||
"""This class creates and manages the TCP client browser (servers' list)."""
|
||||
"""This class creates and manages the TCP client browser (servers list)."""
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
# Store the arg/config
|
||||
@ -58,9 +58,9 @@ class GlancesClientBrowser(object):
|
||||
self.screen = GlancesCursesBrowser(args=self.args)
|
||||
|
||||
def get_servers_list(self):
|
||||
"""
|
||||
Return the current server list (list of dict)
|
||||
Merge of static + autodiscover servers list
|
||||
"""Return the current server list (list of dict).
|
||||
|
||||
Merge of static + autodiscover servers list.
|
||||
"""
|
||||
ret = []
|
||||
|
||||
@ -71,7 +71,16 @@ class GlancesClientBrowser(object):
|
||||
|
||||
return ret
|
||||
|
||||
def serve_forever(self):
|
||||
def __get_uri(self, server):
|
||||
"""Return the URI for the given server dict."""
|
||||
# Select the connection mode (with or without password)
|
||||
if server['password'] != "":
|
||||
return 'http://{0}:{1}@{2}:{3}'.format(server['username'], server['password'],
|
||||
server['ip'], server['port'])
|
||||
else:
|
||||
return 'http://{0}:{1}'.format(server['ip'], server['port'])
|
||||
|
||||
def __serve_forever(self):
|
||||
"""Main client loop."""
|
||||
while True:
|
||||
# No need to update the server list
|
||||
@ -84,15 +93,11 @@ class GlancesClientBrowser(object):
|
||||
# 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']):
|
||||
if v['type'] == 'STATIC' and v['status'] in ['UNKNOWN', 'SNMP', 'OFFLINE']:
|
||||
continue
|
||||
|
||||
# Select the connection mode (with or without password)
|
||||
if v['password'] != "":
|
||||
uri = 'http://{0}:{1}@{2}:{3}'.format(v['username'], v['password'],
|
||||
v['ip'], v['port'])
|
||||
else:
|
||||
uri = 'http://{0}:{1}'.format(v['ip'], v['port'])
|
||||
# Get the server URI
|
||||
uri = self.__get_uri(v)
|
||||
|
||||
# Try to connect to the server
|
||||
t = GlancesClientTransport()
|
||||
@ -146,46 +151,48 @@ class GlancesClientBrowser(object):
|
||||
"Server list dictionnary change inside the loop (wait next update)")
|
||||
|
||||
# Update the screen (list or Glances client)
|
||||
if self.screen.get_active() is None:
|
||||
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: %s" % self.get_servers_list()[self.screen.get_active()])
|
||||
logger.debug("Selected server: {0}".format(self.get_servers_list()[self.screen.active_server]))
|
||||
|
||||
# Connection can take time
|
||||
# Display a popup
|
||||
self.screen.display_popup(_("Connect to %s:%s" % (v['name'], v['port'])), duration=1)
|
||||
self.screen.display_popup(
|
||||
'Connect to {0}:{1}'.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.get_active()]['password'] is None:
|
||||
if self.get_servers_list()[self.screen.active_server]['password'] is None:
|
||||
from hashlib import sha256
|
||||
# Display a popup to enter password
|
||||
clear_password = self.screen.display_popup(_("Password needed for %s: " % v['name']), is_input=True)
|
||||
clear_password = self.screen.display_popup(
|
||||
'Password needed for {0}: '.format(v['name']), is_input=True)
|
||||
# Hash with SHA256
|
||||
encoded_password = sha256(clear_password).hexdigest()
|
||||
encoded_password = sha256(clear_password.encode('utf-8')).hexdigest()
|
||||
# Store the password for the selected server
|
||||
self.set_in_selected('password', encoded_password)
|
||||
|
||||
# Display the Glance client on the selected server
|
||||
logger.info("Connect Glances client to the %s server" %
|
||||
self.get_servers_list()[self.screen.get_active()]['key'])
|
||||
logger.info("Connect Glances client to the {0} 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.get_active()]['ip']
|
||||
args_server.port = self.get_servers_list()[self.screen.get_active()]['port']
|
||||
args_server.username = self.get_servers_list()[self.screen.get_active()]['username']
|
||||
args_server.password = self.get_servers_list()[self.screen.get_active()]['password']
|
||||
client = GlancesClient(config=self.config,
|
||||
args=args_server,
|
||||
return_to_browser=True)
|
||||
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 %s (see log file for additional information)" % v['name']))
|
||||
self.screen.display_popup(
|
||||
"Sorry, cannot connect to '{0}'\n"
|
||||
"See 'glances.log' for more details".format(v['name']))
|
||||
|
||||
# Set the ONLINE status for the selected server
|
||||
self.set_in_selected('status', 'OFFLINE')
|
||||
@ -195,8 +202,8 @@ class GlancesClientBrowser(object):
|
||||
connection_type = client.serve_forever()
|
||||
|
||||
try:
|
||||
logger.debug("Disconnect Glances client from the %s server" %
|
||||
self.get_servers_list()[self.screen.get_active()]['key'])
|
||||
logger.debug("Disconnect Glances client from the {0} server".format(
|
||||
self.get_servers_list()[self.screen.active_server]['key']))
|
||||
except IndexError:
|
||||
# Server did not exist anymore
|
||||
pass
|
||||
@ -208,19 +215,28 @@ class GlancesClientBrowser(object):
|
||||
self.set_in_selected('status', 'ONLINE')
|
||||
|
||||
# Return to the browser (no server selected)
|
||||
self.screen.set_active(None)
|
||||
self.screen.active_server = None
|
||||
|
||||
def serve_forever(self):
|
||||
"""Wrapper to the serve_forever function.
|
||||
|
||||
This function will restore the terminal to a sane state
|
||||
before re-raising the exception and generating a traceback.
|
||||
"""
|
||||
try:
|
||||
return self.__serve_forever()
|
||||
finally:
|
||||
self.end()
|
||||
|
||||
def set_in_selected(self, key, value):
|
||||
"""Set the (key, value) for the selected server in the list"""
|
||||
"""Set the (key, value) for the selected server in the list."""
|
||||
# Static list then dynamic one
|
||||
if self.screen.get_active() >= len(self.static_server.get_servers_list()):
|
||||
self.autodiscover_server.set_server(self.screen.get_active() - len(self.static_server.get_servers_list()),
|
||||
key,
|
||||
value)
|
||||
if self.screen.active_server >= len(self.static_server.get_servers_list()):
|
||||
self.autodiscover_server.set_server(
|
||||
self.screen.active_server - len(self.static_server.get_servers_list()),
|
||||
key, value)
|
||||
else:
|
||||
self.static_server.set_server(self.screen.get_active(),
|
||||
key,
|
||||
value)
|
||||
self.static_server.set_server(self.screen.active_server, key, value)
|
||||
|
||||
def end(self):
|
||||
"""End of the client browser session."""
|
||||
|
@ -23,10 +23,10 @@
|
||||
import os
|
||||
import sys
|
||||
try:
|
||||
from configparser import RawConfigParser
|
||||
from configparser import ConfigParser
|
||||
from configparser import NoOptionError
|
||||
except ImportError: # Python 2
|
||||
from ConfigParser import RawConfigParser
|
||||
from ConfigParser import SafeConfigParser as ConfigParser
|
||||
from ConfigParser import NoOptionError
|
||||
|
||||
# Import Glances lib
|
||||
@ -37,8 +37,7 @@ from glances.core.glances_globals import (
|
||||
is_mac,
|
||||
is_py3,
|
||||
is_windows,
|
||||
sys_prefix,
|
||||
work_path
|
||||
sys_prefix
|
||||
)
|
||||
from glances.core.glances_logging import logger
|
||||
|
||||
@ -47,96 +46,177 @@ class Config(object):
|
||||
|
||||
"""This class is used to access/read config file, if it exists.
|
||||
|
||||
:param location: the custom path to search for config file
|
||||
:type location: str or None
|
||||
:param config_dir: the path to search for config file
|
||||
:type config_dir: str or None
|
||||
"""
|
||||
|
||||
def __init__(self, location=None):
|
||||
self.location = location
|
||||
|
||||
def __init__(self, config_dir=None):
|
||||
self.config_dir = config_dir
|
||||
self.config_filename = 'glances.conf'
|
||||
|
||||
self.parser = RawConfigParser()
|
||||
|
||||
self._loaded_config_file = None
|
||||
self.load()
|
||||
|
||||
def load(self):
|
||||
"""Load a config file from the list of paths, if it exists."""
|
||||
for config_file in self.get_config_paths():
|
||||
if os.path.isfile(config_file) and os.path.getsize(config_file) > 0:
|
||||
try:
|
||||
if is_py3:
|
||||
self.parser.read(config_file, encoding='utf-8')
|
||||
else:
|
||||
self.parser.read(config_file)
|
||||
logger.info("Read configuration file '{0}'".format(config_file))
|
||||
except UnicodeDecodeError as e:
|
||||
logger.error("Cannot decode configuration file '{0}': {1}".format(config_file, e))
|
||||
sys.exit(1)
|
||||
# Save the loaded configuration file path (issue #374)
|
||||
self._loaded_config_file = config_file
|
||||
break
|
||||
self.parser = ConfigParser()
|
||||
self.read()
|
||||
|
||||
def get_loaded_config_file(self):
|
||||
"""Return the loaded configuration file"""
|
||||
return self._loaded_config_file
|
||||
|
||||
def get_config_paths(self):
|
||||
def config_file_paths(self):
|
||||
r"""Get a list of config file paths.
|
||||
|
||||
The list is built taking into account of the OS, priority and location.
|
||||
|
||||
* running from source: /path/to/glances/conf
|
||||
* per-user install: ~/.local/etc/glances (Unix-like only)
|
||||
* custom path: /path/to/glances
|
||||
* Linux: ~/.config/glances, /etc/glances
|
||||
* BSD: ~/.config/glances, /usr/local/etc/glances
|
||||
* Mac: ~/Library/Application Support/glances, /usr/local/etc/glances
|
||||
* OS X: ~/Library/Application Support/glances, /usr/local/etc/glances
|
||||
* Windows: %APPDATA%\glances
|
||||
|
||||
The config file will be searched in the following order of priority:
|
||||
* /path/to/file (via -C flag)
|
||||
* /path/to/glances/conf
|
||||
* user's local directory (per-user install settings)
|
||||
* user's home directory (per-user settings)
|
||||
* {/usr/local,}/etc directory (system-wide settings)
|
||||
* system-wide directory (system-wide settings)
|
||||
"""
|
||||
paths = []
|
||||
conf_path = os.path.realpath(
|
||||
os.path.join(work_path, '..', '..', 'conf'))
|
||||
|
||||
if self.location is not None:
|
||||
paths.append(self.location)
|
||||
|
||||
if os.path.exists(conf_path):
|
||||
paths.append(os.path.join(conf_path, self.config_filename))
|
||||
|
||||
if not is_windows:
|
||||
paths.append(os.path.join(os.path.expanduser('~/.local'), 'etc', appname, self.config_filename))
|
||||
if self.config_dir:
|
||||
paths.append(self.config_dir)
|
||||
|
||||
if is_linux or is_bsd:
|
||||
paths.append(os.path.join(
|
||||
os.environ.get('XDG_CONFIG_HOME') or os.path.expanduser(
|
||||
'~/.config'),
|
||||
paths.append(
|
||||
os.path.join(os.environ.get('XDG_CONFIG_HOME') or
|
||||
os.path.expanduser('~/.config'),
|
||||
appname, self.config_filename))
|
||||
if hasattr(sys, 'real_prefix') or is_bsd:
|
||||
if is_bsd:
|
||||
paths.append(
|
||||
os.path.join(sys.prefix, 'etc', appname, self.config_filename))
|
||||
else:
|
||||
paths.append(
|
||||
os.path.join('/etc', appname, self.config_filename))
|
||||
elif is_mac:
|
||||
paths.append(os.path.join(
|
||||
os.path.expanduser('~/Library/Application Support/'),
|
||||
paths.append(
|
||||
os.path.join(os.path.expanduser('~/Library/Application Support/'),
|
||||
appname, self.config_filename))
|
||||
paths.append(os.path.join(
|
||||
sys_prefix, 'etc', appname, self.config_filename))
|
||||
paths.append(
|
||||
os.path.join(sys_prefix, 'etc', appname, self.config_filename))
|
||||
elif is_windows:
|
||||
paths.append(os.path.join(
|
||||
os.environ.get('APPDATA'), appname, self.config_filename))
|
||||
paths.append(
|
||||
os.path.join(os.environ.get('APPDATA'), appname, self.config_filename))
|
||||
|
||||
return paths
|
||||
|
||||
def read(self):
|
||||
"""Read the config file, if it exists. Using defaults otherwise."""
|
||||
for config_file in self.config_file_paths():
|
||||
if os.path.exists(config_file):
|
||||
try:
|
||||
if is_py3:
|
||||
self.parser.read(config_file, encoding='utf-8')
|
||||
else:
|
||||
self.parser.read(config_file)
|
||||
logger.info("Read configuration file '{0}'".format(config_file))
|
||||
except UnicodeDecodeError as err:
|
||||
logger.error("Cannot decode configuration file '{0}': {1}".format(config_file, err))
|
||||
sys.exit(1)
|
||||
# Save the loaded configuration file path (issue #374)
|
||||
self._loaded_config_file = config_file
|
||||
break
|
||||
|
||||
# Quicklook
|
||||
if not self.parser.has_section('quicklook'):
|
||||
self.parser.add_section('quicklook')
|
||||
self.parser.set('quicklook', 'cpu_careful', '50')
|
||||
self.parser.set('quicklook', 'cpu_warning', '70')
|
||||
self.parser.set('quicklook', 'cpu_critical', '90')
|
||||
self.parser.set('quicklook', 'mem_careful', '50')
|
||||
self.parser.set('quicklook', 'mem_warning', '70')
|
||||
self.parser.set('quicklook', 'mem_critical', '90')
|
||||
self.parser.set('quicklook', 'swap_careful', '50')
|
||||
self.parser.set('quicklook', 'swap_warning', '70')
|
||||
self.parser.set('quicklook', 'swap_critical', '90')
|
||||
|
||||
# CPU
|
||||
if not self.parser.has_section('cpu'):
|
||||
self.parser.add_section('cpu')
|
||||
self.parser.set('cpu', 'user_careful', '50')
|
||||
self.parser.set('cpu', 'user_warning', '70')
|
||||
self.parser.set('cpu', 'user_critical', '90')
|
||||
self.parser.set('cpu', 'iowait_careful', '50')
|
||||
self.parser.set('cpu', 'iowait_warning', '70')
|
||||
self.parser.set('cpu', 'iowait_critical', '90')
|
||||
self.parser.set('cpu', 'system_careful', '50')
|
||||
self.parser.set('cpu', 'system_warning', '70')
|
||||
self.parser.set('cpu', 'system_critical', '90')
|
||||
self.parser.set('cpu', 'steal_careful', '50')
|
||||
self.parser.set('cpu', 'steal_warning', '70')
|
||||
self.parser.set('cpu', 'steal_critical', '90')
|
||||
|
||||
# Per-CPU
|
||||
if not self.parser.has_section('percpu'):
|
||||
self.parser.add_section('percpu')
|
||||
self.parser.set('percpu', 'user_careful', '50')
|
||||
self.parser.set('percpu', 'user_warning', '70')
|
||||
self.parser.set('percpu', 'user_critical', '90')
|
||||
self.parser.set('percpu', 'iowait_careful', '50')
|
||||
self.parser.set('percpu', 'iowait_warning', '70')
|
||||
self.parser.set('percpu', 'iowait_critical', '90')
|
||||
self.parser.set('percpu', 'system_careful', '50')
|
||||
self.parser.set('percpu', 'system_warning', '70')
|
||||
self.parser.set('percpu', 'system_critical', '90')
|
||||
|
||||
# Load
|
||||
if not self.parser.has_section('load'):
|
||||
self.parser.add_section('load')
|
||||
self.parser.set('load', 'careful', '0.7')
|
||||
self.parser.set('load', 'warning', '1.0')
|
||||
self.parser.set('load', 'critical', '5.0')
|
||||
|
||||
# Mem
|
||||
if not self.parser.has_section('mem'):
|
||||
self.parser.add_section('mem')
|
||||
self.parser.set('mem', 'careful', '50')
|
||||
self.parser.set('mem', 'warning', '70')
|
||||
self.parser.set('mem', 'critical', '90')
|
||||
|
||||
# Swap
|
||||
if not self.parser.has_section('memswap'):
|
||||
self.parser.add_section('memswap')
|
||||
self.parser.set('memswap', 'careful', '50')
|
||||
self.parser.set('memswap', 'warning', '70')
|
||||
self.parser.set('memswap', 'critical', '90')
|
||||
|
||||
# FS
|
||||
if not self.parser.has_section('fs'):
|
||||
self.parser.add_section('fs')
|
||||
self.parser.set('fs', 'careful', '50')
|
||||
self.parser.set('fs', 'warning', '70')
|
||||
self.parser.set('fs', 'critical', '90')
|
||||
|
||||
# Sensors
|
||||
if not self.parser.has_section('sensors'):
|
||||
self.parser.add_section('sensors')
|
||||
self.parser.set('sensors', 'temperature_core_careful', '60')
|
||||
self.parser.set('sensors', 'temperature_core_warning', '70')
|
||||
self.parser.set('sensors', 'temperature_core_critical', '80')
|
||||
self.parser.set('sensors', 'temperature_hdd_careful', '45')
|
||||
self.parser.set('sensors', 'temperature_hdd_warning', '52')
|
||||
self.parser.set('sensors', 'temperature_hdd_critical', '60')
|
||||
self.parser.set('sensors', 'battery_careful', '80')
|
||||
self.parser.set('sensors', 'battery_warning', '90')
|
||||
self.parser.set('sensors', 'battery_critical', '95')
|
||||
|
||||
# Process list
|
||||
if not self.parser.has_section('processlist'):
|
||||
self.parser.add_section('processlist')
|
||||
self.parser.set('processlist', 'cpu_careful', '50')
|
||||
self.parser.set('processlist', 'cpu_warning', '70')
|
||||
self.parser.set('processlist', 'cpu_critical', '90')
|
||||
self.parser.set('processlist', 'mem_careful', '50')
|
||||
self.parser.set('processlist', 'mem_warning', '70')
|
||||
self.parser.set('processlist', 'mem_critical', '90')
|
||||
|
||||
@property
|
||||
def loaded_config_file(self):
|
||||
"""Return the loaded configuration file."""
|
||||
return self._loaded_config_file
|
||||
|
||||
def items(self, section):
|
||||
"""Return the items list of a section."""
|
||||
return self.parser.items(section)
|
||||
@ -145,20 +225,16 @@ class Config(object):
|
||||
"""Return info about the existence of a section."""
|
||||
return self.parser.has_section(section)
|
||||
|
||||
def get_option(self, section, option):
|
||||
def get_value(self, section, option, default=None):
|
||||
"""Get the value of an option, if it exists."""
|
||||
try:
|
||||
return self.parser.get(section, option)
|
||||
except NoOptionError:
|
||||
return default
|
||||
|
||||
def get_float_value(self, section, option, default=0.0):
|
||||
"""Get the float value of an option, if it exists."""
|
||||
try:
|
||||
value = self.parser.getfloat(section, option)
|
||||
return self.parser.getfloat(section, option)
|
||||
except NoOptionError:
|
||||
return
|
||||
else:
|
||||
return value
|
||||
|
||||
def get_raw_option(self, section, option):
|
||||
"""Get the raw value of an option, if it exists."""
|
||||
try:
|
||||
value = self.parser.get(section, option)
|
||||
except NoOptionError:
|
||||
return
|
||||
else:
|
||||
return value
|
||||
return float(default)
|
||||
|
49
glances/core/glances_cpu_percent.py
Normal file
@ -0,0 +1,49 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
# Copyright (C) 2015 Nicolargo <nicolas@nicolargo.com>
|
||||
#
|
||||
# Glances is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Glances is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""CPU percent stats shared between CPU and Quicklook plugins."""
|
||||
|
||||
from glances.core.glances_timer import Timer
|
||||
|
||||
import psutil
|
||||
|
||||
|
||||
class CpuPercent(object):
|
||||
|
||||
"""Get and store the CPU percent."""
|
||||
|
||||
def __init__(self, cached_time=1):
|
||||
self.cpu_percent = 0
|
||||
|
||||
# cached_time is the minimum time interval between stats updates
|
||||
# since last update is passed (will retrieve old cached info instead)
|
||||
self.timer = Timer(0)
|
||||
self.cached_time = cached_time
|
||||
|
||||
def get(self):
|
||||
"""Update and/or return the CPU using the psutil library."""
|
||||
# Never update more than 1 time per cached_time
|
||||
if self.timer.finished():
|
||||
self.cpu_percent = psutil.cpu_percent(interval=0.0)
|
||||
self.timer = Timer(self.cached_time)
|
||||
return self.cpu_percent
|
||||
|
||||
|
||||
# CpuPercent instance shared between plugins
|
||||
cpu_percent = CpuPercent()
|
@ -51,15 +51,3 @@ exports_path = os.path.realpath(os.path.join(work_path, '..', 'exports'))
|
||||
sys_path = sys.path[:]
|
||||
sys.path.insert(1, plugins_path)
|
||||
sys.path.insert(1, exports_path)
|
||||
|
||||
def get_locale_path(paths):
|
||||
for path in paths:
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
|
||||
# i18n
|
||||
gettext_domain = appname
|
||||
i18n_path = os.path.realpath(os.path.join(work_path, '..', '..', 'i18n'))
|
||||
user_i18n_path = os.path.join(os.path.expanduser('~/.local'), 'share', 'locale')
|
||||
sys_i18n_path = os.path.join(sys_prefix, 'share', 'locale')
|
||||
locale_dir = get_locale_path([i18n_path, user_i18n_path, sys_i18n_path])
|
||||
|
@ -17,10 +17,15 @@
|
||||
# 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/>.
|
||||
|
||||
"""Custom logging class"""
|
||||
"""Custom logging class."""
|
||||
|
||||
import logging
|
||||
import logging.config
|
||||
try:
|
||||
# Python 2.6
|
||||
from logutils.dictconfig import dictConfig
|
||||
except ImportError:
|
||||
# Python >= 2.7
|
||||
from logging.config import dictConfig
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
@ -72,29 +77,24 @@ LOGGING_CFG = {
|
||||
|
||||
|
||||
def tempfile_name():
|
||||
"""Return the tempfile name (full path)"""
|
||||
"""Return the tempfile name (full path)."""
|
||||
ret = os.path.join(tempfile.gettempdir(), 'glances.log')
|
||||
if os.access(ret, os.F_OK) and not os.access(ret, os.W_OK):
|
||||
print("Warning: can't write logs to file {} (permission denied)".format(ret))
|
||||
print("WARNING: Couldn't write to log file {0}: (Permission denied)".format(ret))
|
||||
ret = tempfile.mkstemp(prefix='glances', suffix='.tmp', text=True)
|
||||
print("Create a new log file: {}".format(ret[1]))
|
||||
print("Create a new log file: {0}".format(ret[1]))
|
||||
return ret[1]
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def glances_logger():
|
||||
"""Build and return the logger"""
|
||||
"""Build and return the logger."""
|
||||
temp_path = tempfile_name()
|
||||
_logger = logging.getLogger()
|
||||
try:
|
||||
LOGGING_CFG['handlers']['file']['filename'] = temp_path
|
||||
logging.config.dictConfig(LOGGING_CFG)
|
||||
except AttributeError:
|
||||
# dictConfig is only available for Python 2.7 or higher
|
||||
# Minimal configuration for Python 2.6
|
||||
logging.basicConfig(filename=temp_path,
|
||||
level=logging.DEBUG,
|
||||
format='%(asctime)s -- %(levelname)s -- %(message)s')
|
||||
dictConfig(LOGGING_CFG)
|
||||
|
||||
return _logger
|
||||
|
||||
logger = glances_logger()
|
||||
|
@ -87,29 +87,26 @@ class GlancesLogs(object):
|
||||
else:
|
||||
# Default sort is...
|
||||
process_auto_by = 'cpu_percent'
|
||||
glances_processes.setautosortkey(process_auto_by)
|
||||
|
||||
return process_auto_by
|
||||
glances_processes.auto_sort = True
|
||||
glances_processes.sort_key = process_auto_by
|
||||
|
||||
def reset_process_sort(self):
|
||||
"""Reset the process_auto_by variable."""
|
||||
"""Reset the process auto sort key."""
|
||||
# Default sort is...
|
||||
process_auto_by = 'cpu_percent'
|
||||
glances_processes.setautosortkey(process_auto_by)
|
||||
glances_processes.setmanualsortkey(None)
|
||||
|
||||
return process_auto_by
|
||||
glances_processes.auto_sort = True
|
||||
glances_processes.sort_key = 'cpu_percent'
|
||||
|
||||
def add(self, item_state, item_type, item_value,
|
||||
proc_list=[], proc_desc="",
|
||||
peak_time=3):
|
||||
proc_list=None, proc_desc="", peak_time=6):
|
||||
"""Add a new item to the logs list.
|
||||
|
||||
If 'item' is a 'new one', add the new item at the beginning of the logs
|
||||
list.
|
||||
If 'item' is a 'new one', add the new item at the beginning of
|
||||
the logs list.
|
||||
If 'item' is not a 'new one', update the existing item.
|
||||
If event < peak_time the the alert is not setoff
|
||||
If event < peak_time the the alert is not setoff.
|
||||
"""
|
||||
proc_list = proc_list or []
|
||||
|
||||
# Add or update the log
|
||||
item_index = self.__itemexist__(item_type)
|
||||
if item_index < 0:
|
||||
@ -121,24 +118,23 @@ class GlancesLogs(object):
|
||||
# Create the new log item
|
||||
# Time is stored in Epoch format
|
||||
# Epoch -> DMYHMS = datetime.fromtimestamp(epoch)
|
||||
item = []
|
||||
# START DATE
|
||||
item.append(time.mktime(datetime.now().timetuple()))
|
||||
item.append(-1) # END DATE
|
||||
item.append(item_state) # STATE: WARNING|CRITICAL
|
||||
item.append(item_type) # TYPE: CPU, LOAD, MEM...
|
||||
item.append(item_value) # MAX
|
||||
item.append(item_value) # AVG
|
||||
item.append(item_value) # MIN
|
||||
item.append(item_value) # SUM
|
||||
item.append(1) # COUNT
|
||||
# Process list is sorted automaticaly
|
||||
# Overwrite the user choise
|
||||
item = [
|
||||
time.mktime(datetime.now().timetuple()), # START DATE
|
||||
-1, # END DATE
|
||||
item_state, # STATE: WARNING|CRITICAL
|
||||
item_type, # TYPE: CPU, LOAD, MEM...
|
||||
item_value, # MAX
|
||||
item_value, # AVG
|
||||
item_value, # MIN
|
||||
item_value, # SUM
|
||||
1, # COUNT
|
||||
# Process list is sorted automatically
|
||||
# Overwrite the user choice
|
||||
# topprocess = sorted(proc_list, key=lambda process: process[process_auto_by],
|
||||
# reverse=True)
|
||||
# item.append(topprocess[0:3]) # TOP 3 PROCESS LIST
|
||||
item.append([]) # TOP 3 PROCESS LIST
|
||||
item.append(proc_desc) # MONITORED PROCESSES DESC
|
||||
# topprocess[0:3], # TOP 3 PROCESS LIST
|
||||
[], # TOP 3 PROCESS LIST
|
||||
proc_desc] # MONITORED PROCESSES DESC
|
||||
|
||||
# Add the item to the list
|
||||
self.logs_list.insert(0, item)
|
||||
@ -190,12 +186,12 @@ class GlancesLogs(object):
|
||||
def clean(self, critical=False):
|
||||
"""Clean the logs list by deleting finished items.
|
||||
|
||||
By default, only delete WARNING message
|
||||
If critical = True, also delete CRITICAL message
|
||||
By default, only delete WARNING message.
|
||||
If critical = True, also delete CRITICAL message.
|
||||
"""
|
||||
# Create a new clean list
|
||||
clean_logs_list = []
|
||||
while (self.len() > 0):
|
||||
while self.len() > 0:
|
||||
item = self.logs_list.pop()
|
||||
if item[1] < 0 or (not critical and item[2].startswith("CRITICAL")):
|
||||
clean_logs_list.insert(0, item)
|
||||
|
@ -27,7 +27,7 @@ import tempfile
|
||||
|
||||
# Import Glances libs
|
||||
from glances.core.glances_config import Config
|
||||
from glances.core.glances_globals import appname, is_windows, psutil_version, version
|
||||
from glances.core.glances_globals import appname, is_linux, is_windows, psutil_version, version
|
||||
from glances.core.glances_logging import logger
|
||||
|
||||
|
||||
@ -96,94 +96,104 @@ Start the client browser (browser mode):\n\
|
||||
parser.add_argument(
|
||||
'-V', '--version', action='version', version=_version)
|
||||
parser.add_argument('-d', '--debug', action='store_true', default=False,
|
||||
dest='debug', help=_('Enable debug mode'))
|
||||
dest='debug', help='enable debug mode')
|
||||
parser.add_argument('-C', '--config', dest='conf_file',
|
||||
help=_('path to the configuration file'))
|
||||
help='path to the configuration file')
|
||||
# Enable or disable option on startup
|
||||
parser.add_argument('--disable-network', action='store_true', default=False,
|
||||
dest='disable_network', help=_('disable network module'))
|
||||
dest='disable_network', help='disable network 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'))
|
||||
dest='disable_diskio', help='disable disk I/O module')
|
||||
parser.add_argument('--disable-fs', action='store_true', default=False,
|
||||
dest='disable_fs', help=_('disable filesystem module'))
|
||||
dest='disable_fs', help='disable filesystem module')
|
||||
parser.add_argument('--disable-sensors', action='store_true', default=False,
|
||||
dest='disable_sensors', help=_('disable sensors module'))
|
||||
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-raid', action='store_true', default=False,
|
||||
dest='disable_raid', help=_('disable RAID module'))
|
||||
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('--disable-left-sidebar', action='store_true', default=False,
|
||||
dest='disable_left_sidebar', help=_('disable network, disk io, FS and sensors modules (need Py3Sensors lib)'))
|
||||
dest='disable_docker', help='disable Docker module')
|
||||
parser.add_argument('--disable-left-sidebar', action='store_true',
|
||||
default=False, dest='disable_left_sidebar',
|
||||
help='disable network, disk I/O, FS and sensors modules (py3sensors needed)')
|
||||
parser.add_argument('--disable-process', action='store_true', default=False,
|
||||
dest='disable_process', help=_('disable process module'))
|
||||
dest='disable_process', help='disable process module')
|
||||
parser.add_argument('--disable-log', action='store_true', default=False,
|
||||
dest='disable_log', help=_('disable log module'))
|
||||
dest='disable_log', help='disable log module')
|
||||
parser.add_argument('--disable-quicklook', action='store_true', default=False,
|
||||
dest='disable_quicklook', help='disable quick look module')
|
||||
parser.add_argument('--disable-bold', action='store_false', default=True,
|
||||
dest='disable_bold', help=_('disable bold mode in the terminal'))
|
||||
dest='disable_bold', help='disable bold mode in the terminal')
|
||||
parser.add_argument('--enable-process-extended', action='store_true', default=False,
|
||||
dest='enable_process_extended', help=_('enable extended stats on top process'))
|
||||
dest='enable_process_extended', help='enable extended stats on top process')
|
||||
parser.add_argument('--enable-history', action='store_true', default=False,
|
||||
dest='enable_history', help=_('enable the history mode (need MatPlotLib lib)'))
|
||||
dest='enable_history', help='enable the history mode (matplotlib needed)')
|
||||
parser.add_argument('--path-history', default=tempfile.gettempdir(),
|
||||
dest='path_history', help=_('Set the export path for graph history'))
|
||||
dest='path_history', help='set the export path for graph history')
|
||||
# Export modules feature
|
||||
parser.add_argument('--export-csv', default=None,
|
||||
dest='export_csv', help=_('export stats to a CSV file'))
|
||||
dest='export_csv', help='export stats to a CSV file')
|
||||
parser.add_argument('--export-influxdb', action='store_true', default=False,
|
||||
dest='export_influxdb', help=_('export stats to an InfluxDB server (need InfluDB lib)'))
|
||||
dest='export_influxdb', help='export stats to an InfluxDB server (influxdb needed)')
|
||||
parser.add_argument('--export-statsd', action='store_true', default=False,
|
||||
dest='export_statsd', help=_('export stats to a Statsd server (need StatsD lib)'))
|
||||
dest='export_statsd', help='export stats to a StatsD server (statsd needed)')
|
||||
parser.add_argument('--export-rabbitmq', action='store_true', default=False,
|
||||
dest='export_rabbitmq', help='export stats to rabbitmq broker (pika needed)')
|
||||
# Client/Server option
|
||||
parser.add_argument('-c', '--client', dest='client',
|
||||
help=_('connect to a Glances server by IPv4/IPv6 address or hostname'))
|
||||
help='connect to a Glances server by IPv4/IPv6 address or hostname')
|
||||
parser.add_argument('-s', '--server', action='store_true', default=False,
|
||||
dest='server', help=_('run Glances in server mode'))
|
||||
dest='server', help='run Glances in server mode')
|
||||
parser.add_argument('--browser', action='store_true', default=False,
|
||||
dest='browser', help=_('start the client browser (list of servers)'))
|
||||
dest='browser', help='start the client browser (list of servers)')
|
||||
parser.add_argument('--disable-autodiscover', action='store_true', default=False,
|
||||
dest='disable_autodiscover', help=_('disable autodiscover feature'))
|
||||
dest='disable_autodiscover', help='disable autodiscover feature')
|
||||
parser.add_argument('-p', '--port', default=None, type=int, dest='port',
|
||||
help=_('define the client/server TCP port [default: {0}]').format(self.server_port))
|
||||
help='define the client/server TCP port [default: {0}]'.format(self.server_port))
|
||||
parser.add_argument('-B', '--bind', default='0.0.0.0', dest='bind_address',
|
||||
help=_('bind server to the given IPv4/IPv6 address or hostname'))
|
||||
parser.add_argument('--password-badidea', dest='password_arg',
|
||||
help=_('define password from the command line'))
|
||||
help='bind server to the given IPv4/IPv6 address or hostname')
|
||||
parser.add_argument('--password', action='store_true', default=False, dest='password_prompt',
|
||||
help=_('define a client/server password from the prompt or file'))
|
||||
help='define a client/server password')
|
||||
parser.add_argument('--snmp-community', default='public', dest='snmp_community',
|
||||
help=_('SNMP community'))
|
||||
help='SNMP community')
|
||||
parser.add_argument('--snmp-port', default=161, type=int,
|
||||
dest='snmp_port', help=_('SNMP port'))
|
||||
dest='snmp_port', help='SNMP port')
|
||||
parser.add_argument('--snmp-version', default='2c', dest='snmp_version',
|
||||
help=_('SNMP version (1, 2c or 3)'))
|
||||
help='SNMP version (1, 2c or 3)')
|
||||
parser.add_argument('--snmp-user', default='private', dest='snmp_user',
|
||||
help=_('SNMP username (only for SNMPv3)'))
|
||||
help='SNMP username (only for SNMPv3)')
|
||||
parser.add_argument('--snmp-auth', default='password', dest='snmp_auth',
|
||||
help=_('SNMP authentication key (only for SNMPv3)'))
|
||||
help='SNMP authentication key (only for SNMPv3)')
|
||||
parser.add_argument('--snmp-force', action='store_true', default=False,
|
||||
dest='snmp_force', help=_('force SNMP mode'))
|
||||
dest='snmp_force', help='force SNMP mode')
|
||||
parser.add_argument('-t', '--time', default=self.refresh_time, type=float,
|
||||
dest='time', help=_('set refresh time in seconds [default: {0} sec]').format(self.refresh_time))
|
||||
dest='time', help='set refresh time in seconds [default: {0} sec]'.format(self.refresh_time))
|
||||
parser.add_argument('-w', '--webserver', action='store_true', default=False,
|
||||
dest='webserver', help=_('run Glances in web server mode (need Bootle lib)'))
|
||||
dest='webserver', help='run Glances in web server mode (bottle needed)')
|
||||
# Display options
|
||||
parser.add_argument('-q', '--quiet', default=False, action='store_true',
|
||||
dest='quiet', help='do not display the curses interface')
|
||||
parser.add_argument('-f', '--process-filter', default=None, type=str,
|
||||
dest='process_filter', help=_('set the process filter pattern (regular expression)'))
|
||||
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'))
|
||||
dest='process_short_name', help='force short name for processes name')
|
||||
if not is_windows:
|
||||
parser.add_argument('--hide-kernel-threads', action='store_true', default=False,
|
||||
dest='no_kernel_threads', help=_('hide kernel threads in process list'))
|
||||
dest='no_kernel_threads', help='hide kernel threads in process list')
|
||||
if is_linux:
|
||||
parser.add_argument('--tree', action='store_true', default=False,
|
||||
dest='process_tree', help=_('display processes as a tree'))
|
||||
dest='process_tree', help='display processes as a tree')
|
||||
parser.add_argument('-b', '--byte', action='store_true', default=False,
|
||||
dest='byte', help=_('display network rate in byte per second'))
|
||||
dest='byte', help='display network rate in byte per second')
|
||||
parser.add_argument('-1', '--percpu', action='store_true', default=False,
|
||||
dest='percpu', help=_('start Glances in per CPU mode'))
|
||||
dest='percpu', help='start Glances in per CPU mode')
|
||||
parser.add_argument('--fs-free-space', action='store_false', default=False,
|
||||
dest='fs_free_space', help=_('display FS free space instead of used'))
|
||||
dest='fs_free_space', help='display FS free space instead of used')
|
||||
parser.add_argument('--theme-white', action='store_true', default=False,
|
||||
dest='theme_white', help=_('optimize display for white background'))
|
||||
dest='theme_white', help='optimize display colors for white background')
|
||||
|
||||
return parser
|
||||
|
||||
@ -217,22 +227,15 @@ Start the client browser (browser mode):\n\
|
||||
|
||||
# Server or client login/password
|
||||
args.username = self.username
|
||||
if args.password_arg is not None:
|
||||
from hashlib import sha256
|
||||
# Password is given as an argument
|
||||
# Hash with SHA256
|
||||
# Only the SHA will be transmit on the network
|
||||
args.password = sha256(args.password_arg).hexdigest()
|
||||
elif args.password_prompt:
|
||||
if args.password_prompt:
|
||||
# Interactive or file password
|
||||
if args.server:
|
||||
args.password = self.__get_password(
|
||||
description=_(
|
||||
"Define the password for the Glances server"),
|
||||
description='Define the password for the Glances server',
|
||||
confirm=True)
|
||||
elif args.client:
|
||||
args.password = self.__get_password(
|
||||
description=_("Enter the Glances server password"),
|
||||
description='Enter the Glances server password',
|
||||
clear=True)
|
||||
else:
|
||||
# Default is no password
|
||||
@ -260,6 +263,11 @@ Start the client browser (browser mode):\n\
|
||||
sys.exit(2)
|
||||
logger.debug("History output path is set to {0}".format(args.path_history))
|
||||
|
||||
# Disable HDDTemp if sensors are disabled
|
||||
if args.disable_sensors:
|
||||
args.disable_hddtemp = True
|
||||
logger.debug("Sensors and HDDTemp are disabled")
|
||||
|
||||
return args
|
||||
|
||||
def __hash_password(self, plain_password):
|
||||
|
@ -49,11 +49,12 @@ class MonitorList(object):
|
||||
__monitor_list = []
|
||||
|
||||
def __init__(self, config):
|
||||
"""Init the monitoring list from the configuration file."""
|
||||
"""Init the monitoring list from the configuration file, if it exists."""
|
||||
self.config = config
|
||||
|
||||
if self.config is not None and self.config.has_section('monitor'):
|
||||
# Process monitoring list
|
||||
logger.debug("Monitor list configuration detected")
|
||||
self.__set_monitor_list('monitor', 'list')
|
||||
else:
|
||||
self.__monitor_list = []
|
||||
@ -67,14 +68,13 @@ class MonitorList(object):
|
||||
value = {}
|
||||
key = "list_" + str(l) + "_"
|
||||
try:
|
||||
description = self.config.get_raw_option(section, key + "description")
|
||||
regex = self.config.get_raw_option(section, key + "regex")
|
||||
command = self.config.get_raw_option(section, key + "command")
|
||||
countmin = self.config.get_raw_option(section, key + "countmin")
|
||||
countmax = self.config.get_raw_option(section, key + "countmax")
|
||||
description = self.config.get_value(section, key + 'description')
|
||||
regex = self.config.get_value(section, key + 'regex')
|
||||
command = self.config.get_value(section, key + 'command')
|
||||
countmin = self.config.get_value(section, key + 'countmin')
|
||||
countmax = self.config.get_value(section, key + 'countmax')
|
||||
except Exception as e:
|
||||
logger.error("Cannot read monitored list: {0}".format(e))
|
||||
pass
|
||||
else:
|
||||
if description is not None and regex is not None:
|
||||
# Build the new item
|
||||
@ -127,26 +127,26 @@ class MonitorList(object):
|
||||
# Iter upon the monitored list
|
||||
for i in range(0, len(self.get())):
|
||||
# Search monitored processes by a regular expression
|
||||
processlist = glances_processes.getlist()
|
||||
processlist = glances_processes.getalllist()
|
||||
monitoredlist = [p for p in processlist if re.search(self.regex(i), p['cmdline']) is not None]
|
||||
self.__monitor_list[i]['count'] = len(monitoredlist)
|
||||
|
||||
if self.command(i) is None:
|
||||
# If there is no command specified in the conf file
|
||||
# then display CPU and MEM %
|
||||
self.__monitor_list[i]['result'] = 'CPU: {0:.1f}% | MEM: {1:.1f}%'.format(
|
||||
sum([p['cpu_percent'] for p in monitoredlist]),
|
||||
sum([p['memory_percent'] for p in monitoredlist]))
|
||||
continue
|
||||
else:
|
||||
if self.command(i) is not None:
|
||||
# Execute the user command line
|
||||
try:
|
||||
self.__monitor_list[i]['result'] = subprocess.check_output(self.command(i),
|
||||
shell=True)
|
||||
except subprocess.CalledProcessError:
|
||||
self.__monitor_list[i]['result'] = _("Error: ") + self.command(i)
|
||||
self.__monitor_list[i]['result'] = 'Error: ' + self.command(i)
|
||||
except Exception:
|
||||
self.__monitor_list[i]['result'] = _("Cannot execute command")
|
||||
self.__monitor_list[i]['result'] = 'Cannot execute command'
|
||||
|
||||
if self.command(i) is None or self.__monitor_list[i]['result'] == '':
|
||||
# If there is no command specified in the conf file
|
||||
# then display CPU and MEM %
|
||||
self.__monitor_list[i]['result'] = 'CPU: {0:.1f}% | MEM: {1:.1f}%'.format(
|
||||
sum([p['cpu_percent'] for p in monitoredlist]),
|
||||
sum([p['memory_percent'] for p in monitoredlist]))
|
||||
|
||||
return self.__monitor_list
|
||||
|
||||
|
@ -110,11 +110,11 @@ class GlancesPassword(object):
|
||||
|
||||
# password_plain is the plain SHA-256 password
|
||||
# password_hashed is the salt + SHA-256 password
|
||||
password_sha = hashlib.sha256(getpass.getpass(_("Password: ")).encode('utf-8')).hexdigest()
|
||||
password_sha = hashlib.sha256(getpass.getpass('Password: ').encode('utf-8')).hexdigest()
|
||||
password_hashed = self.hash_password(password_sha)
|
||||
if confirm:
|
||||
# password_confirm is the clear password (only used to compare)
|
||||
password_confirm = hashlib.sha256(getpass.getpass(_("Password (confirm): ")).encode('utf-8')).hexdigest()
|
||||
password_confirm = hashlib.sha256(getpass.getpass('Password (confirm): ').encode('utf-8')).hexdigest()
|
||||
|
||||
if not self.check_password(password_hashed, password_confirm):
|
||||
logger.critical("Sorry, passwords do not match. Exit.")
|
||||
@ -128,8 +128,8 @@ class GlancesPassword(object):
|
||||
|
||||
# Save the hashed password to the password file
|
||||
if not clear:
|
||||
save_input = input(_("Do you want to save the password? [Yes/No]: "))
|
||||
if len(save_input) > 0 and save_input[0].upper() == _('Y'):
|
||||
save_input = input('Do you want to save the password? [Yes/No]: ')
|
||||
if len(save_input) > 0 and save_input[0].upper() == 'Y':
|
||||
self.save_password(password_hashed)
|
||||
|
||||
return password
|
||||
|
@ -19,47 +19,47 @@
|
||||
|
||||
# Import Python lib
|
||||
import collections
|
||||
import operator
|
||||
import os
|
||||
import re
|
||||
|
||||
# Import psutil
|
||||
import psutil
|
||||
|
||||
# Import Glances lib
|
||||
from glances.core.glances_globals import is_bsd, is_linux, is_mac, is_windows
|
||||
from glances.core.glances_logging import logger
|
||||
from glances.core.glances_timer import getTimeSinceLastUpdate, Timer
|
||||
from glances.core.glances_timer import Timer, getTimeSinceLastUpdate
|
||||
|
||||
# Import psutil
|
||||
import psutil
|
||||
|
||||
|
||||
def is_kernel_thread(proc):
|
||||
""" Return True if proc is a kernel thread, False instead. """
|
||||
"""Return True if proc is a kernel thread, False instead."""
|
||||
try:
|
||||
return os.getpgid(proc.pid) == 0
|
||||
except OSError: # Python >= 3.3 raises ProcessLookupError, which inherits OSError
|
||||
# Python >= 3.3 raises ProcessLookupError, which inherits OSError
|
||||
except OSError:
|
||||
# return False is process is dead
|
||||
return False
|
||||
|
||||
|
||||
class ProcessTreeNode(object):
|
||||
|
||||
"""
|
||||
Represent a process tree.
|
||||
"""Represent a process tree.
|
||||
|
||||
We avoid recursive algorithm to manipulate the tree because function calls are expensive with CPython.
|
||||
We avoid recursive algorithm to manipulate the tree because function
|
||||
calls are expensive with CPython.
|
||||
"""
|
||||
|
||||
def __init__(self, process=None, stats=None, sort_key=None, root=False):
|
||||
def __init__(self, process=None, stats=None, sort_key=None, sort_reverse=True, root=False):
|
||||
self.process = process
|
||||
self.stats = stats
|
||||
self.children = []
|
||||
self.children_sorted = False
|
||||
self.sort_key = sort_key
|
||||
self.reverse_sorting = (self.sort_key != "name")
|
||||
self.sort_reverse = sort_reverse
|
||||
self.is_root = root
|
||||
|
||||
def __str__(self):
|
||||
""" Return the tree as a string for debugging. """
|
||||
"""Return the tree as a string for debugging."""
|
||||
lines = []
|
||||
nodes_to_print = collections.deque([collections.deque([("#", self)])])
|
||||
while nodes_to_print:
|
||||
@ -69,7 +69,8 @@ class ProcessTreeNode(object):
|
||||
if current_node.is_root:
|
||||
lines.append(indent_str)
|
||||
else:
|
||||
lines.append("%s[%s]" % (indent_str, current_node.process.name()))
|
||||
lines.append("%s[%s]" %
|
||||
(indent_str, current_node.process.name()))
|
||||
indent_str = " " * (len(lines[-1]) - 1)
|
||||
children_nodes_to_print = collections.deque()
|
||||
for child in current_node.children:
|
||||
@ -77,14 +78,18 @@ class ProcessTreeNode(object):
|
||||
tree_char = "└─"
|
||||
else:
|
||||
tree_char = "├─"
|
||||
children_nodes_to_print.appendleft((indent_str + tree_char, child))
|
||||
children_nodes_to_print.appendleft(
|
||||
(indent_str + tree_char, child))
|
||||
if children_nodes_to_print:
|
||||
nodes_to_print.append(children_nodes_to_print)
|
||||
return "\n".join(lines)
|
||||
|
||||
def set_sorting(self, key, reverse):
|
||||
""" Set sorting key or func for user with __iter__ (affects the whole tree from this node). """
|
||||
if (self.sort_key != key) or (self.reverse_sorting != reverse):
|
||||
"""Set sorting key or func for use with __iter__.
|
||||
|
||||
This affects the whole tree from this node.
|
||||
"""
|
||||
if self.sort_key != key or self.sort_reverse != reverse:
|
||||
nodes_to_flag_unsorted = collections.deque([self])
|
||||
while nodes_to_flag_unsorted:
|
||||
current_node = nodes_to_flag_unsorted.pop()
|
||||
@ -94,8 +99,8 @@ class ProcessTreeNode(object):
|
||||
nodes_to_flag_unsorted.extend(current_node.children)
|
||||
|
||||
def get_weight(self):
|
||||
""" Return "weight" of a process and all its children for sorting. """
|
||||
if self.sort_key == "name":
|
||||
"""Return 'weight' of a process and all its children for sorting."""
|
||||
if self.sort_key == 'name' or self.sort_key == 'username':
|
||||
return self.stats[self.sort_key]
|
||||
|
||||
# sum ressource usage for self and children
|
||||
@ -128,53 +133,63 @@ class ProcessTreeNode(object):
|
||||
return total
|
||||
|
||||
def __iter__(self):
|
||||
""" Iterator returning ProcessTreeNode in sorted order, recursively. """
|
||||
"""Iterator returning ProcessTreeNode in sorted order, recursively."""
|
||||
if not self.is_root:
|
||||
yield self
|
||||
if not self.children_sorted:
|
||||
# optimization to avoid sorting twice (once when limiting the maximum processes to grab stats for,
|
||||
# and once before displaying)
|
||||
self.children.sort(key=self.__class__.get_weight, reverse=self.reverse_sorting)
|
||||
self.children.sort(
|
||||
key=self.__class__.get_weight, reverse=self.sort_reverse)
|
||||
self.children_sorted = True
|
||||
for child in self.children:
|
||||
for n in iter(child):
|
||||
yield n
|
||||
|
||||
def iter_children(self, exclude_incomplete_stats=True):
|
||||
"""
|
||||
Iterator returning ProcessTreeNode in sorted order (only children of this node, non recursive).
|
||||
"""Iterator returning ProcessTreeNode in sorted order.
|
||||
|
||||
If exclude_incomplete_stats is True, exclude processes not having full statistics.
|
||||
It can happen after a resort (change of sort key) because process stats are not grabbed immediately,
|
||||
but only at next full update.
|
||||
Return only children of this node, non recursive.
|
||||
|
||||
If exclude_incomplete_stats is True, exclude processes not
|
||||
having full statistics. It can happen after a resort (change of
|
||||
sort key) because process stats are not grabbed immediately, but
|
||||
only at next full update.
|
||||
"""
|
||||
if not self.children_sorted:
|
||||
# optimization to avoid sorting twice (once when limiting the maximum processes to grab stats for,
|
||||
# and once before displaying)
|
||||
self.children.sort(key=self.__class__.get_weight, reverse=self.reverse_sorting)
|
||||
self.children.sort(
|
||||
key=self.__class__.get_weight, reverse=self.sort_reverse)
|
||||
self.children_sorted = True
|
||||
for child in self.children:
|
||||
if (not exclude_incomplete_stats) or ("time_since_update" in child.stats):
|
||||
if not exclude_incomplete_stats or "time_since_update" in child.stats:
|
||||
yield child
|
||||
|
||||
def find_process(self, process):
|
||||
""" Search in tree for the ProcessTreeNode owning process, return it or None if not found. """
|
||||
"""Search in tree for the ProcessTreeNode owning process.
|
||||
|
||||
Return it or None if not found.
|
||||
"""
|
||||
nodes_to_search = collections.deque([self])
|
||||
while nodes_to_search:
|
||||
current_node = nodes_to_search.pop()
|
||||
if (not current_node.is_root) and (current_node.process.pid == process.pid):
|
||||
if not current_node.is_root and current_node.process.pid == process.pid:
|
||||
return current_node
|
||||
nodes_to_search.extend(current_node.children)
|
||||
|
||||
@staticmethod
|
||||
def build_tree(process_dict, sort_key, hide_kernel_threads):
|
||||
""" Build a process tree using using parent/child relationships, and return the tree root node. """
|
||||
def build_tree(process_dict, sort_key, sort_reverse, hide_kernel_threads):
|
||||
"""Build a process tree using using parent/child relationships.
|
||||
|
||||
Return the tree root node.
|
||||
"""
|
||||
tree_root = ProcessTreeNode(root=True)
|
||||
nodes_to_add_last = collections.deque()
|
||||
|
||||
# first pass: add nodes whose parent are in the tree
|
||||
for process, stats in process_dict.items():
|
||||
new_node = ProcessTreeNode(process, stats, sort_key)
|
||||
new_node = ProcessTreeNode(process, stats, sort_key, sort_reverse)
|
||||
try:
|
||||
parent_process = process.parent()
|
||||
except psutil.NoSuchProcess:
|
||||
@ -192,13 +207,16 @@ class ProcessTreeNode(object):
|
||||
# parent is not in tree, add this node later
|
||||
nodes_to_add_last.append(new_node)
|
||||
|
||||
# next pass(es): add nodes to their parents if it could not be done in previous pass
|
||||
# next pass(es): add nodes to their parents if it could not be done in
|
||||
# previous pass
|
||||
while nodes_to_add_last:
|
||||
node_to_add = nodes_to_add_last.popleft() # pop from left and append to right to avoid infinite loop
|
||||
# pop from left and append to right to avoid infinite loop
|
||||
node_to_add = nodes_to_add_last.popleft()
|
||||
try:
|
||||
parent_process = node_to_add.process.parent()
|
||||
except psutil.NoSuchProcess:
|
||||
# parent is dead, consider no parent, add this node at the top level
|
||||
# parent is dead, consider no parent, add this node at the top
|
||||
# level
|
||||
tree_root.children.append(node_to_add)
|
||||
else:
|
||||
if parent_process is None:
|
||||
@ -242,10 +260,11 @@ class GlancesProcesses(object):
|
||||
self.process_tree = None
|
||||
|
||||
# Init stats
|
||||
self.resetsort()
|
||||
self.auto_sort = True
|
||||
self._sort_key = 'cpu_percent'
|
||||
self.allprocesslist = []
|
||||
self.processlist = []
|
||||
self.processcount = {
|
||||
'total': 0, 'running': 0, 'sleeping': 0, 'thread': 0}
|
||||
self.processcount = {'total': 0, 'running': 0, 'sleeping': 0, 'thread': 0}
|
||||
|
||||
# Tag to enable/disable the processes stats (to reduce the Glances CPU consumption)
|
||||
# Default is to enable the processes stats
|
||||
@ -254,13 +273,12 @@ class GlancesProcesses(object):
|
||||
# Extended stats for top process is enable by default
|
||||
self.disable_extended_tag = False
|
||||
|
||||
# Maximum number of processes showed in the UI interface
|
||||
# None if no limit
|
||||
self.max_processes = None
|
||||
# Maximum number of processes showed in the UI (None if no limit)
|
||||
self._max_processes = None
|
||||
|
||||
# Process filter is a regular expression
|
||||
self.process_filter = None
|
||||
self.process_filter_re = None
|
||||
self._process_filter = None
|
||||
self._process_filter_re = None
|
||||
|
||||
# Whether or not to hide kernel threads
|
||||
self.no_kernel_threads = False
|
||||
@ -283,86 +301,82 @@ class GlancesProcesses(object):
|
||||
"""Disable extended process stats."""
|
||||
self.disable_extended_tag = True
|
||||
|
||||
def set_max_processes(self, value):
|
||||
"""Set the maximum number of processes showed in the UI interfaces"""
|
||||
self.max_processes = value
|
||||
return self.max_processes
|
||||
@property
|
||||
def max_processes(self):
|
||||
"""Get the maximum number of processes showed in the UI."""
|
||||
return self._max_processes
|
||||
|
||||
def get_max_processes(self):
|
||||
"""Get the maximum number of processes showed in the UI interfaces"""
|
||||
return self.max_processes
|
||||
@max_processes.setter
|
||||
def max_processes(self, value):
|
||||
"""Set the maximum number of processes showed in the UI."""
|
||||
self._max_processes = value
|
||||
|
||||
def set_process_filter(self, value):
|
||||
"""Set the process filter"""
|
||||
@property
|
||||
def process_filter(self):
|
||||
"""Get the process filter."""
|
||||
return self._process_filter
|
||||
|
||||
@process_filter.setter
|
||||
def process_filter(self, value):
|
||||
"""Set the process filter."""
|
||||
logger.info("Set process filter to {0}".format(value))
|
||||
self.process_filter = value
|
||||
self._process_filter = value
|
||||
if value is not None:
|
||||
try:
|
||||
self.process_filter_re = re.compile(value)
|
||||
logger.debug("Process filter regex compilation OK: {0}".format(self.get_process_filter()))
|
||||
self._process_filter_re = re.compile(value)
|
||||
logger.debug("Process filter regex compilation OK: {0}".format(self.process_filter))
|
||||
except Exception:
|
||||
logger.error("Cannot compile process filter regex: {0}".format(value))
|
||||
self.process_filter_re = None
|
||||
self._process_filter_re = None
|
||||
else:
|
||||
self.process_filter_re = None
|
||||
return self.process_filter
|
||||
self._process_filter_re = None
|
||||
|
||||
def get_process_filter(self):
|
||||
"""Get the process filter"""
|
||||
return self.process_filter
|
||||
|
||||
def get_process_filter_re(self):
|
||||
"""Get the process regular expression compiled"""
|
||||
return self.process_filter_re
|
||||
@property
|
||||
def process_filter_re(self):
|
||||
"""Get the process regular expression compiled."""
|
||||
return self._process_filter_re
|
||||
|
||||
def is_filtered(self, value):
|
||||
"""Return True if the value should be filtered"""
|
||||
if self.get_process_filter() is None:
|
||||
"""Return True if the value should be filtered."""
|
||||
if self.process_filter is None:
|
||||
# No filter => Not filtered
|
||||
return False
|
||||
else:
|
||||
# logger.debug(self.get_process_filter() + " <> " + value + " => " + str(self.get_process_filter_re().match(value) is None))
|
||||
return self.get_process_filter_re().match(value) is None
|
||||
# logger.debug(self.process_filter + " <> " + value + " => " + str(self.process_filter_re.match(value) is None))
|
||||
return self.process_filter_re.match(value) is None
|
||||
|
||||
def disable_kernel_threads(self):
|
||||
""" Ignore kernel threads in process list. """
|
||||
"""Ignore kernel threads in process list."""
|
||||
self.no_kernel_threads = True
|
||||
|
||||
def enable_tree(self):
|
||||
""" Enable process tree. """
|
||||
"""Enable process tree."""
|
||||
self._enable_tree = True
|
||||
|
||||
def is_tree_enabled(self):
|
||||
""" Return True if process tree is enabled, False instead. """
|
||||
"""Return True if process tree is enabled, False instead."""
|
||||
return self._enable_tree
|
||||
|
||||
def __get_process_stats(self, proc,
|
||||
mandatory_stats=True,
|
||||
standard_stats=True,
|
||||
extended_stats=False):
|
||||
@property
|
||||
def sort_reverse(self):
|
||||
"""Return True to sort processes in reverse 'key' order, False instead."""
|
||||
if self.sort_key == 'name' or self.sort_key == 'username':
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def __get_mandatory_stats(self, proc, procstat):
|
||||
"""
|
||||
Get process stats of the proc processes (proc is returned psutil.process_iter())
|
||||
mandatory_stats: need for the sorting/filter step
|
||||
Get mandatory_stats: need for the sorting/filter step.
|
||||
|
||||
=> cpu_percent, memory_percent, io_counters, name, cmdline
|
||||
standard_stats: for all the displayed processes
|
||||
=> username, status, memory_info, cpu_times
|
||||
extended_stats: only for top processes (see issue #403)
|
||||
=> connections (UDP/TCP), memory_swap...
|
||||
"""
|
||||
|
||||
# Process ID (always)
|
||||
procstat = proc.as_dict(attrs=['pid'])
|
||||
|
||||
if mandatory_stats:
|
||||
procstat['mandatory_stats'] = True
|
||||
|
||||
# Process CPU, MEM percent and name
|
||||
try:
|
||||
procstat.update(
|
||||
proc.as_dict(attrs=['cpu_percent', 'memory_percent', 'name', 'cpu_times'], ad_value=''))
|
||||
except psutil.NoSuchProcess:
|
||||
# Correct issue #414
|
||||
return None
|
||||
procstat.update(proc.as_dict(
|
||||
attrs=['username', 'cpu_percent', 'memory_percent',
|
||||
'name', 'cpu_times'], ad_value=''))
|
||||
if procstat['cpu_percent'] == '' or procstat['memory_percent'] == '':
|
||||
# Do not display process if we cannot get the basic
|
||||
# cpu_percent or memory_percent stats
|
||||
@ -391,7 +405,7 @@ class GlancesProcesses(object):
|
||||
# Get the process IO counters
|
||||
proc_io = proc.io_counters()
|
||||
io_new = [proc_io.read_bytes, proc_io.write_bytes]
|
||||
except (psutil.AccessDenied, psutil.NoSuchProcess):
|
||||
except (psutil.AccessDenied, psutil.NoSuchProcess, NotImplementedError):
|
||||
# Access denied to process IO (no root account)
|
||||
# NoSuchProcess (process die between first and second grab)
|
||||
# Put 0 in all values (for sort) and io_tag = 0 (for
|
||||
@ -413,7 +427,14 @@ class GlancesProcesses(object):
|
||||
# Append the IO tag (for display)
|
||||
procstat['io_counters'] += [io_tag]
|
||||
|
||||
if standard_stats:
|
||||
return procstat
|
||||
|
||||
def __get_standard_stats(self, proc, procstat):
|
||||
"""
|
||||
Get standard_stats: for all the displayed processes.
|
||||
|
||||
=> username, status, memory_info, cpu_times
|
||||
"""
|
||||
procstat['standard_stats'] = True
|
||||
|
||||
# Process username (cached with internal cache)
|
||||
@ -440,7 +461,14 @@ class GlancesProcesses(object):
|
||||
else:
|
||||
procstat['status'] = str(procstat['status'])[:1].upper()
|
||||
|
||||
if extended_stats and not self.disable_extended_tag:
|
||||
return procstat
|
||||
|
||||
def __get_extended_stats(self, proc, procstat):
|
||||
"""
|
||||
Get extended_stats: only for top processes (see issue #403).
|
||||
|
||||
=> connections (UDP/TCP), memory_swap...
|
||||
"""
|
||||
procstat['extended_stats'] = True
|
||||
|
||||
# CPU affinity (Windows and Linux only)
|
||||
@ -520,18 +548,32 @@ class GlancesProcesses(object):
|
||||
else:
|
||||
procstat['ionice'] = None
|
||||
|
||||
# logger.debug(procstat)
|
||||
return procstat
|
||||
|
||||
def __get_process_stats(self, proc,
|
||||
mandatory_stats=True,
|
||||
standard_stats=True,
|
||||
extended_stats=False):
|
||||
"""Get stats of running processes."""
|
||||
# Process ID (always)
|
||||
procstat = proc.as_dict(attrs=['pid'])
|
||||
|
||||
if mandatory_stats:
|
||||
procstat = self.__get_mandatory_stats(proc, procstat)
|
||||
|
||||
if procstat is not None and standard_stats:
|
||||
procstat = self.__get_standard_stats(proc, procstat)
|
||||
|
||||
if procstat is not None and extended_stats and not self.disable_extended_tag:
|
||||
procstat = self.__get_extended_stats(proc, procstat)
|
||||
|
||||
return procstat
|
||||
|
||||
def update(self):
|
||||
"""
|
||||
Update the processes stats
|
||||
"""
|
||||
"""Update the processes stats."""
|
||||
# Reset the stats
|
||||
self.processlist = []
|
||||
self.processcount = {
|
||||
'total': 0, 'running': 0, 'sleeping': 0, 'thread': 0}
|
||||
self.processcount = {'total': 0, 'running': 0, 'sleeping': 0, 'thread': 0}
|
||||
|
||||
# Do not process if disable tag is set
|
||||
if self.disable_tag:
|
||||
@ -544,15 +586,14 @@ class GlancesProcesses(object):
|
||||
processdict = {}
|
||||
for proc in psutil.process_iter():
|
||||
# Ignore kernel threads if needed
|
||||
if (self.no_kernel_threads and (not is_windows)
|
||||
and is_kernel_thread(proc)):
|
||||
if self.no_kernel_threads and not is_windows and is_kernel_thread(proc):
|
||||
continue
|
||||
|
||||
# If self.get_max_processes() is None: Only retreive mandatory stats
|
||||
# If self.max_processes is None: Only retreive mandatory stats
|
||||
# Else: retreive mandatory and standard stats
|
||||
s = self.__get_process_stats(proc,
|
||||
mandatory_stats=True,
|
||||
standard_stats=self.get_max_processes() is None)
|
||||
standard_stats=self.max_processes is None)
|
||||
# Continue to the next process if it has to be filtered
|
||||
if s is None or (self.is_filtered(s['cmdline']) and self.is_filtered(s['name'])):
|
||||
continue
|
||||
@ -586,12 +627,13 @@ class GlancesProcesses(object):
|
||||
|
||||
if self._enable_tree:
|
||||
self.process_tree = ProcessTreeNode.build_tree(processdict,
|
||||
self.getsortkey(),
|
||||
self.sort_key,
|
||||
self.sort_reverse,
|
||||
self.no_kernel_threads)
|
||||
|
||||
for i, node in enumerate(self.process_tree):
|
||||
# Only retreive stats for visible processes (get_max_processes)
|
||||
if (self.get_max_processes() is not None) and (i >= self.get_max_processes()):
|
||||
# Only retreive stats for visible processes (max_processes)
|
||||
if self.max_processes is not None and i >= self.max_processes:
|
||||
break
|
||||
|
||||
# add standard stats
|
||||
@ -607,30 +649,32 @@ class GlancesProcesses(object):
|
||||
|
||||
else:
|
||||
# Process optimization
|
||||
# Only retreive stats for visible processes (get_max_processes)
|
||||
if self.get_max_processes() is not None:
|
||||
# Only retreive stats for visible processes (max_processes)
|
||||
if self.max_processes is not None:
|
||||
# Sort the internal dict and cut the top N (Return a list of tuple)
|
||||
# tuple=key (proc), dict (returned by __get_process_stats)
|
||||
try:
|
||||
processiter = sorted(
|
||||
processdict.items(), key=lambda x: x[1][self.getsortkey()], reverse=True)
|
||||
processiter = sorted(processdict.items(),
|
||||
key=lambda x: x[1][self.sort_key],
|
||||
reverse=self.sort_reverse)
|
||||
except (KeyError, TypeError) as e:
|
||||
logger.error("Cannot sort process list by %s (%s)" % (self.getsortkey(), e))
|
||||
logger.error("Cannot sort process list by {0}: {1}".format(self.sort_key, e))
|
||||
logger.error("%s" % str(processdict.items()[0]))
|
||||
# Fallback to all process (issue #423)
|
||||
processloop = processdict.items()
|
||||
first = False
|
||||
else:
|
||||
processloop = processiter[0:self.get_max_processes()]
|
||||
processloop = processiter[0:self.max_processes]
|
||||
first = True
|
||||
else:
|
||||
# Get all processes stats
|
||||
processloop = processdict.items()
|
||||
first = False
|
||||
|
||||
for i in processloop:
|
||||
# Already existing mandatory stats
|
||||
procstat = i[1]
|
||||
if self.get_max_processes() is not None:
|
||||
if self.max_processes is not None:
|
||||
# Update with standard stats
|
||||
# and extended stats but only for TOP (first) process
|
||||
s = self.__get_process_stats(i[0],
|
||||
@ -647,6 +691,9 @@ class GlancesProcesses(object):
|
||||
# Next...
|
||||
first = False
|
||||
|
||||
# Build the all processes list used by the monitored list
|
||||
self.allprocesslist = processdict.values()
|
||||
|
||||
# Clean internals caches if timeout is reached
|
||||
if self.cache_timer.finished():
|
||||
self.username_cache = {}
|
||||
@ -658,6 +705,10 @@ class GlancesProcesses(object):
|
||||
"""Get the number of processes."""
|
||||
return self.processcount
|
||||
|
||||
def getalllist(self):
|
||||
"""Get the allprocesslist."""
|
||||
return self.allprocesslist
|
||||
|
||||
def getlist(self, sortedby=None):
|
||||
"""Get the processlist."""
|
||||
return self.processlist
|
||||
@ -666,65 +717,14 @@ class GlancesProcesses(object):
|
||||
"""Get the process tree."""
|
||||
return self.process_tree
|
||||
|
||||
def getsortkey(self):
|
||||
"""Get the current sort key"""
|
||||
if self.getmanualsortkey() is not None:
|
||||
return self.getmanualsortkey()
|
||||
else:
|
||||
return self.getautosortkey()
|
||||
@property
|
||||
def sort_key(self):
|
||||
"""Get the current sort key."""
|
||||
return self._sort_key
|
||||
|
||||
def getmanualsortkey(self):
|
||||
"""Get the current sort key for manual sort."""
|
||||
return self.processmanualsort
|
||||
|
||||
def getautosortkey(self):
|
||||
"""Get the current sort key for automatic sort."""
|
||||
return self.processautosort
|
||||
|
||||
def setmanualsortkey(self, sortedby):
|
||||
"""Set the current sort key for manual sort."""
|
||||
self.processmanualsort = sortedby
|
||||
if self._enable_tree and (self.process_tree is not None):
|
||||
self.process_tree.set_sorting(sortedby, sortedby != "name")
|
||||
return self.processmanualsort
|
||||
|
||||
def setautosortkey(self, sortedby):
|
||||
"""Set the current sort key for automatic sort."""
|
||||
self.processautosort = sortedby
|
||||
return self.processautosort
|
||||
|
||||
def resetsort(self):
|
||||
"""Set the default sort: Auto"""
|
||||
self.setmanualsortkey(None)
|
||||
self.setautosortkey('cpu_percent')
|
||||
|
||||
def getsortlist(self, sortedby=None):
|
||||
"""Get the sorted processlist."""
|
||||
if sortedby is None:
|
||||
# No need to sort...
|
||||
return self.processlist
|
||||
|
||||
sortedreverse = True
|
||||
if sortedby == 'name':
|
||||
sortedreverse = False
|
||||
|
||||
if sortedby == 'io_counters':
|
||||
# Specific case for io_counters
|
||||
# Sum of io_r + io_w
|
||||
try:
|
||||
# Sort process by IO rate (sum IO read + IO write)
|
||||
self.processlist.sort(key=lambda process: process[sortedby][0] -
|
||||
process[sortedby][2] + process[sortedby][1] -
|
||||
process[sortedby][3],
|
||||
reverse=sortedreverse)
|
||||
except Exception:
|
||||
self.processlist.sort(key=operator.itemgetter('cpu_percent'),
|
||||
reverse=sortedreverse)
|
||||
else:
|
||||
# Others sorts
|
||||
self.processlist.sort(key=operator.itemgetter(sortedby),
|
||||
reverse=sortedreverse)
|
||||
|
||||
return self.processlist
|
||||
@sort_key.setter
|
||||
def sort_key(self, key):
|
||||
"""Set the current sort key."""
|
||||
self._sort_key = key
|
||||
|
||||
glances_processes = GlancesProcesses()
|
||||
|
@ -100,7 +100,7 @@ class GlancesXMLRPCHandler(SimpleXMLRPCRequestHandler):
|
||||
self.send_error(401, 'Authentication failed')
|
||||
return False
|
||||
|
||||
def log_message(self, format, *args):
|
||||
def log_message(self, log_format, *args):
|
||||
# No message displayed on the server side
|
||||
pass
|
||||
|
||||
|
@ -50,7 +50,7 @@ class GlancesSNMPClient(object):
|
||||
self.auth = auth
|
||||
|
||||
def __buid_result(self, varBinds):
|
||||
"""Build the results"""
|
||||
"""Build the results."""
|
||||
ret = {}
|
||||
for name, val in varBinds:
|
||||
if str(val) == '':
|
||||
|
@ -19,6 +19,8 @@
|
||||
|
||||
"""Manage the Glances standalone session."""
|
||||
|
||||
from time import sleep
|
||||
|
||||
# Import Glances libs
|
||||
from glances.core.glances_globals import is_windows
|
||||
from glances.core.glances_logging import logger
|
||||
@ -32,15 +34,16 @@ class GlancesStandalone(object):
|
||||
"""This class creates and manages the Glances standalone session."""
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
# Quiet mode
|
||||
self._quiet = args.quiet
|
||||
self.refresh_time = args.time
|
||||
|
||||
# Init stats
|
||||
self.stats = GlancesStats(config=config, args=args)
|
||||
|
||||
# Default number of processes to displayed is set to 50
|
||||
glances_processes.set_max_processes(50)
|
||||
|
||||
# If process extended stats is disabled by user
|
||||
if not args.enable_process_extended:
|
||||
logger.info("Extended stats for top process are disabled (default behavior)")
|
||||
logger.debug("Extended stats for top process are disabled")
|
||||
glances_processes.disable_extended()
|
||||
else:
|
||||
logger.debug("Extended stats for top process are enabled")
|
||||
@ -48,36 +51,67 @@ class GlancesStandalone(object):
|
||||
|
||||
# Manage optionnal process filter
|
||||
if args.process_filter is not None:
|
||||
glances_processes.set_process_filter(args.process_filter)
|
||||
glances_processes.process_filter = args.process_filter
|
||||
|
||||
if (not is_windows) and args.no_kernel_threads:
|
||||
# Ignore kernel threads in process list
|
||||
glances_processes.disable_kernel_threads()
|
||||
|
||||
try:
|
||||
if args.process_tree:
|
||||
# Enable process tree view
|
||||
glances_processes.enable_tree()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
# Initial system informations update
|
||||
self.stats.update()
|
||||
|
||||
if self.quiet:
|
||||
logger.info("Quiet mode is ON: Nothing will be displayed")
|
||||
# In quiet mode, nothing is displayed
|
||||
glances_processes.max_processes = 0
|
||||
else:
|
||||
# Default number of processes to displayed is set to 50
|
||||
glances_processes.max_processes = 50
|
||||
|
||||
# Init screen
|
||||
self.screen = GlancesCursesStandalone(args=args)
|
||||
|
||||
def serve_forever(self):
|
||||
@property
|
||||
def quiet(self):
|
||||
return self._quiet
|
||||
|
||||
def __serve_forever(self):
|
||||
"""Main loop for the CLI."""
|
||||
while True:
|
||||
# Update system informations
|
||||
self.stats.update()
|
||||
|
||||
if not self.quiet:
|
||||
# Update the screen
|
||||
self.screen.update(self.stats)
|
||||
else:
|
||||
# Wait...
|
||||
sleep(self.refresh_time)
|
||||
|
||||
# Export stats using export modules
|
||||
self.stats.export(self.stats)
|
||||
|
||||
def serve_forever(self):
|
||||
"""Wrapper to the serve_forever function.
|
||||
|
||||
This function will restore the terminal to a sane state
|
||||
before re-raising the exception and generating a traceback.
|
||||
"""
|
||||
try:
|
||||
return self.__serve_forever()
|
||||
finally:
|
||||
self.end()
|
||||
|
||||
def end(self):
|
||||
"""End of the standalone CLI."""
|
||||
if not self.quiet:
|
||||
self.screen.end()
|
||||
|
||||
# Exit from export modules
|
||||
|
@ -17,7 +17,7 @@
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Manage the Glances server static list """
|
||||
"""Manage the Glances server static list."""
|
||||
|
||||
# System lib
|
||||
from socket import gaierror, gethostbyname
|
||||
@ -28,7 +28,7 @@ from glances.core.glances_logging import logger
|
||||
|
||||
class GlancesStaticServer(object):
|
||||
|
||||
"""Manage the static servers list for the client browser"""
|
||||
"""Manage the static servers list for the client browser."""
|
||||
|
||||
_section = "serverlist"
|
||||
|
||||
@ -39,8 +39,7 @@ class GlancesStaticServer(object):
|
||||
self._server_list = self.load(config)
|
||||
|
||||
def load(self, config):
|
||||
"""Load the server list from the configuration file"""
|
||||
|
||||
"""Load the server list from the configuration file."""
|
||||
server_list = []
|
||||
|
||||
if config is None:
|
||||
@ -54,7 +53,7 @@ class GlancesStaticServer(object):
|
||||
postfix = 'server_%s_' % str(i)
|
||||
# Read the server name (mandatory)
|
||||
for s in ['name', 'port', 'alias']:
|
||||
new_server[s] = config.get_raw_option(self._section, '%s%s' % (postfix, s))
|
||||
new_server[s] = config.get_value(self._section, '%s%s' % (postfix, s))
|
||||
if new_server['name'] is not None:
|
||||
# Manage optionnal information
|
||||
if new_server['port'] is None:
|
||||
@ -85,9 +84,9 @@ class GlancesStaticServer(object):
|
||||
return server_list
|
||||
|
||||
def get_servers_list(self):
|
||||
"""Return the current server list (dict of dict)"""
|
||||
"""Return the current server list (dict of dict)."""
|
||||
return self._server_list
|
||||
|
||||
def set_server(self, server_pos, key, value):
|
||||
"""Set the key to the value for the server_pos (position in the list)"""
|
||||
"""Set the key to the value for the server_pos (position in the list)."""
|
||||
self._server_list[server_pos][key] = value
|
||||
|
@ -25,7 +25,7 @@ import re
|
||||
import sys
|
||||
import threading
|
||||
|
||||
from glances.core.glances_globals import plugins_path, exports_path, sys_path
|
||||
from glances.core.glances_globals import exports_path, plugins_path, sys_path
|
||||
from glances.core.glances_logging import logger
|
||||
|
||||
# SNMP OID regexp pattern to short system name dict
|
||||
@ -152,13 +152,17 @@ class GlancesStats(object):
|
||||
# For standalone and server modes
|
||||
# For each plugins, call the update method
|
||||
for p in self._plugins:
|
||||
# logger.debug("Update %s stats" % p)
|
||||
# logger.debug("Update %s stats" % p)
|
||||
self._plugins[p].update()
|
||||
|
||||
def export(self, input_stats={}):
|
||||
def export(self, input_stats=None):
|
||||
"""Export all the stats.
|
||||
Each export module is ran in a dedicated thread."""
|
||||
|
||||
Each export module is ran in a dedicated thread.
|
||||
"""
|
||||
# threads = []
|
||||
input_stats = input_stats or {}
|
||||
|
||||
for e in self._exports:
|
||||
logger.debug("Export stats using the %s module" % e)
|
||||
thread = threading.Thread(target=self._exports[e].update,
|
||||
@ -167,11 +171,11 @@ class GlancesStats(object):
|
||||
thread.start()
|
||||
|
||||
def getAll(self):
|
||||
"""Return all the stats (list)"""
|
||||
"""Return all the stats (list)."""
|
||||
return [self._plugins[p].get_raw() for p in self._plugins]
|
||||
|
||||
def getAllAsDict(self):
|
||||
"""Return all the stats (dict)"""
|
||||
"""Return all the stats (dict)."""
|
||||
# Python > 2.6
|
||||
# {p: self._plugins[p].get_raw() for p in self._plugins}
|
||||
ret = {}
|
||||
@ -181,21 +185,21 @@ class GlancesStats(object):
|
||||
|
||||
def getAllLimits(self):
|
||||
"""Return the plugins limits list."""
|
||||
return [self._plugins[p].get_limits() for p in self._plugins]
|
||||
return [self._plugins[p].limits for p in self._plugins]
|
||||
|
||||
def getAllLimitsAsDict(self):
|
||||
"""Return all the stats limits (dict)"""
|
||||
"""Return all the stats limits (dict)."""
|
||||
ret = {}
|
||||
for p in self._plugins:
|
||||
ret[p] = self._plugins[p].get_limits()
|
||||
ret[p] = self._plugins[p].limits
|
||||
return ret
|
||||
|
||||
def getAllViews(self):
|
||||
"""Return the plugins views"""
|
||||
"""Return the plugins views."""
|
||||
return [self._plugins[p].get_views() for p in self._plugins]
|
||||
|
||||
def getAllViewsAsDict(self):
|
||||
"""Return all the stats views (dict)"""
|
||||
"""Return all the stats views (dict)."""
|
||||
ret = {}
|
||||
for p in self._plugins:
|
||||
ret[p] = self._plugins[p].get_views()
|
||||
@ -203,7 +207,7 @@ class GlancesStats(object):
|
||||
|
||||
def get_plugin_list(self):
|
||||
"""Return the plugin list."""
|
||||
self._plugins
|
||||
return self._plugins
|
||||
|
||||
def get_plugin(self, plugin_name):
|
||||
"""Return the plugin name."""
|
||||
@ -213,7 +217,7 @@ class GlancesStats(object):
|
||||
return None
|
||||
|
||||
def end(self):
|
||||
"""End of the Glances stats"""
|
||||
"""End of the Glances stats."""
|
||||
# Close the export module
|
||||
for e in self._exports:
|
||||
self._exports[e].exit()
|
||||
@ -231,8 +235,10 @@ class GlancesStatsServer(GlancesStats):
|
||||
# all_stats is a dict of dicts filled by the server
|
||||
self.all_stats = collections.defaultdict(dict)
|
||||
|
||||
def update(self, input_stats={}):
|
||||
def update(self, input_stats=None):
|
||||
"""Update the stats."""
|
||||
input_stats = input_stats or {}
|
||||
|
||||
# Force update of all the stats
|
||||
GlancesStats.update(self)
|
||||
|
||||
@ -240,7 +246,7 @@ class GlancesStatsServer(GlancesStats):
|
||||
self.all_stats = self._set_stats(input_stats)
|
||||
|
||||
def _set_stats(self, input_stats):
|
||||
"""Set the stats to the input_stats one"""
|
||||
"""Set the stats to the input_stats one."""
|
||||
# Build the all_stats with the get_raw() method of the plugins
|
||||
ret = collections.defaultdict(dict)
|
||||
for p in self._plugins:
|
||||
@ -248,11 +254,11 @@ class GlancesStatsServer(GlancesStats):
|
||||
return ret
|
||||
|
||||
def getAll(self):
|
||||
"""Return the stats as a list"""
|
||||
"""Return the stats as a list."""
|
||||
return self.all_stats
|
||||
|
||||
def getAllAsDict(self):
|
||||
"""Return the stats as a dict"""
|
||||
"""Return the stats as a dict."""
|
||||
# Python > 2.6
|
||||
# return {p: self.all_stats[p] for p in self._plugins}
|
||||
ret = {}
|
||||
@ -359,7 +365,7 @@ class GlancesStatsClientSNMP(GlancesStats):
|
||||
return ret
|
||||
|
||||
def get_system_name(self, oid_system_name):
|
||||
"""Get the short os name from the OS name OID string"""
|
||||
"""Get the short os name from the OS name OID string."""
|
||||
short_system_name = None
|
||||
|
||||
if oid_system_name == '':
|
||||
@ -383,7 +389,8 @@ class GlancesStatsClientSNMP(GlancesStats):
|
||||
# For each plugins, call the update method
|
||||
for p in self._plugins:
|
||||
# Set the input method to SNMP
|
||||
self._plugins[p].set_input('snmp', self.system_name)
|
||||
self._plugins[p].input_method = 'snmp'
|
||||
self._plugins[p].short_system_name = self.system_name
|
||||
try:
|
||||
self._plugins[p].update()
|
||||
except Exception as e:
|
||||
|
@ -34,7 +34,7 @@ class GlancesWebServer(object):
|
||||
# Init stats
|
||||
self.stats = GlancesStats(config)
|
||||
|
||||
if (not is_windows) and args.no_kernel_threads:
|
||||
if not is_windows and args.no_kernel_threads:
|
||||
# Ignore kernel threads in process list
|
||||
glances_processes.disable_kernel_threads()
|
||||
|
||||
|
@ -72,27 +72,26 @@ class Export(GlancesExport):
|
||||
plugins = stats.getAllPlugins()
|
||||
|
||||
# Loop over available plugin
|
||||
i = 0
|
||||
for plugin in plugins:
|
||||
for i, plugin in enumerate(plugins):
|
||||
if plugin in self.plugins_to_export():
|
||||
if type(all_stats[i]) is list:
|
||||
for item in all_stats[i]:
|
||||
if isinstance(all_stats[i], list):
|
||||
for stat in all_stats[i]:
|
||||
# First line: header
|
||||
if self.first_line:
|
||||
fieldnames = item.keys()
|
||||
csv_header += map(lambda x: plugin+'_'+item[item['key']]+'_'+x, item)
|
||||
csv_header += ('{0}_{1}_{2}'.format(
|
||||
plugin, stat[stat['key']], item) for item in stat)
|
||||
# Others lines: stats
|
||||
fieldvalues = item.values()
|
||||
fieldvalues = stat.values()
|
||||
csv_data += fieldvalues
|
||||
elif type(all_stats[i]) is dict:
|
||||
elif isinstance(all_stats[i], dict):
|
||||
# First line: header
|
||||
if self.first_line:
|
||||
fieldnames = all_stats[i].keys()
|
||||
csv_header += map(lambda x: plugin+'_'+x, fieldnames)
|
||||
csv_header += ('{0}_{1}'.format(plugin, fieldname)
|
||||
for fieldname in fieldnames)
|
||||
# Others lines: stats
|
||||
fieldvalues = all_stats[i].values()
|
||||
csv_data += fieldvalues
|
||||
i += 1
|
||||
|
||||
# Export to CSV
|
||||
if self.first_line:
|
||||
|
@ -32,7 +32,7 @@ from glances.core.glances_logging import logger
|
||||
|
||||
class GlancesExport(object):
|
||||
|
||||
"""Main class for Glances' export IF."""
|
||||
"""Main class for Glances export IF."""
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
"""Init the export class."""
|
||||
@ -53,34 +53,47 @@ class GlancesExport(object):
|
||||
logger.debug("Finalise export interface %s" % self.export_name)
|
||||
|
||||
def plugins_to_export(self):
|
||||
"""Return the list of plugins to export"""
|
||||
return ['cpu', 'load', 'mem', 'memswap', 'network', 'diskio', 'fs', 'processcount']
|
||||
"""Return the list of plugins to export."""
|
||||
return ['cpu',
|
||||
'percpu',
|
||||
'load',
|
||||
'mem',
|
||||
'memswap',
|
||||
'network',
|
||||
'diskio',
|
||||
'fs',
|
||||
'processcount',
|
||||
'ip',
|
||||
'system',
|
||||
'uptime']
|
||||
|
||||
def update(self, stats):
|
||||
"""Update stats to a server.
|
||||
The method buil two list: names and values
|
||||
and call the export method to export the stats"""
|
||||
|
||||
The method builds two lists: names and values
|
||||
and calls the export method to export the stats.
|
||||
"""
|
||||
if not self.export_enable:
|
||||
return False
|
||||
|
||||
# Get the stats
|
||||
all_stats = stats.getAll()
|
||||
all_limits = stats.getAllLimits()
|
||||
plugins = stats.getAllPlugins()
|
||||
|
||||
# Loop over available plugin
|
||||
i = 0
|
||||
for plugin in plugins:
|
||||
# Loop over available plugins
|
||||
for i, plugin in enumerate(plugins):
|
||||
if plugin in self.plugins_to_export():
|
||||
if type(all_stats[i]) is list:
|
||||
if isinstance(all_stats[i], list):
|
||||
for item in all_stats[i]:
|
||||
export_names = map(
|
||||
lambda x: item[item['key']] + '.' + x, item.keys())
|
||||
export_values = item.values()
|
||||
item.update(all_limits[i])
|
||||
export_names = list('{0}.{1}'.format(item[item['key']], key)
|
||||
for key in item.keys())
|
||||
export_values = list(item.values())
|
||||
self.export(plugin, export_names, export_values)
|
||||
elif type(all_stats[i]) is dict:
|
||||
export_names = all_stats[i].keys()
|
||||
export_values = all_stats[i].values()
|
||||
elif isinstance(all_stats[i], dict):
|
||||
export_names = list(all_stats[i].keys()) + list(all_limits[i].keys())
|
||||
export_values = list(all_stats[i].values()) + list(all_limits[i].values())
|
||||
self.export(plugin, export_names, export_values)
|
||||
i += 1
|
||||
|
||||
return True
|
||||
|
@ -39,23 +39,21 @@ else:
|
||||
|
||||
class GlancesHistory(object):
|
||||
|
||||
"""This class define the object to manage stats history"""
|
||||
"""This class define the object to manage stats history."""
|
||||
|
||||
def __init__(self, output_folder):
|
||||
self.output_folder = output_folder
|
||||
|
||||
def get_output_folder(self):
|
||||
"""Return the output folder where the graph are generated"""
|
||||
"""Return the output folder where the graph are generated."""
|
||||
return self.output_folder
|
||||
|
||||
def graph_enabled(self):
|
||||
"""Return True if Glances can generaate history graphs"""
|
||||
"""Return True if Glances can generate history graphs."""
|
||||
return matplotlib_check
|
||||
|
||||
def reset(self, stats):
|
||||
"""
|
||||
Reset all the history
|
||||
"""
|
||||
"""Reset all the history."""
|
||||
if not self.graph_enabled():
|
||||
return False
|
||||
for p in stats.getAllPlugins():
|
||||
@ -65,9 +63,7 @@ class GlancesHistory(object):
|
||||
return True
|
||||
|
||||
def get_graph_color(self, item):
|
||||
"""
|
||||
Get the item's color
|
||||
"""
|
||||
"""Get the item's color."""
|
||||
try:
|
||||
ret = item['color']
|
||||
except KeyError:
|
||||
@ -76,15 +72,11 @@ class GlancesHistory(object):
|
||||
return ret
|
||||
|
||||
def get_graph_legend(self, item):
|
||||
"""
|
||||
Get the item's legend
|
||||
"""
|
||||
"""Get the item's legend."""
|
||||
return item['name']
|
||||
|
||||
def get_graph_yunit(self, item, pre_label=''):
|
||||
"""
|
||||
Get the item's Y unit
|
||||
"""
|
||||
"""Get the item's Y unit."""
|
||||
try:
|
||||
unit = " (%s)" % item['y_unit']
|
||||
except KeyError:
|
||||
@ -96,9 +88,9 @@ class GlancesHistory(object):
|
||||
return "%s%s" % (label, unit)
|
||||
|
||||
def generate_graph(self, stats):
|
||||
"""
|
||||
Generate graphs from plugins history
|
||||
Return the number of output files generated by the function
|
||||
"""Generate graphs from plugins history.
|
||||
|
||||
Return the number of output files generated by the function.
|
||||
"""
|
||||
if not self.graph_enabled():
|
||||
return 0
|
||||
@ -176,8 +168,7 @@ class GlancesHistory(object):
|
||||
fig.set_size_inches(20, 10)
|
||||
plt.legend(handles, labels, loc=1, prop={'size': 9})
|
||||
plt.xlabel('Date')
|
||||
plt.savefig(
|
||||
os.path.join(self.output_folder, 'glances_%s.png' % (p)), dpi=72)
|
||||
plt.savefig(os.path.join(self.output_folder, 'glances_%s.png' % p), dpi=72)
|
||||
index_all += 1
|
||||
|
||||
plt.close()
|
||||
|
@ -20,14 +20,21 @@
|
||||
"""InfluxDB interface class."""
|
||||
|
||||
# Import sys libs
|
||||
from influxdb import InfluxDBClient, client
|
||||
import sys
|
||||
try:
|
||||
from configparser import NoOptionError, NoSectionError
|
||||
except ImportError: # Python 2
|
||||
from ConfigParser import NoOptionError, NoSectionError
|
||||
|
||||
# Import Glances lib
|
||||
from glances.core.glances_logging import logger
|
||||
from ConfigParser import NoSectionError, NoOptionError
|
||||
from glances.exports.glances_export import GlancesExport
|
||||
|
||||
from influxdb import InfluxDBClient
|
||||
from influxdb.client import InfluxDBClientError
|
||||
from influxdb.influxdb08 import InfluxDBClient as InfluxDBClient08
|
||||
from influxdb.influxdb08.client import InfluxDBClientError as InfluxDBClientError08
|
||||
|
||||
|
||||
class Export(GlancesExport):
|
||||
|
||||
@ -38,11 +45,12 @@ class Export(GlancesExport):
|
||||
GlancesExport.__init__(self, config=config, args=args)
|
||||
|
||||
# Load the InfluxDB configuration file
|
||||
self.influxdb_host = None
|
||||
self.influxdb_port = None
|
||||
self.influxdb_user = None
|
||||
self.influxdb_password = None
|
||||
self.influxdb_db = None
|
||||
self.host = None
|
||||
self.port = None
|
||||
self.user = None
|
||||
self.password = None
|
||||
self.db = None
|
||||
self.prefix = None
|
||||
self.export_enable = self.load_conf()
|
||||
if not self.export_enable:
|
||||
sys.exit(2)
|
||||
@ -51,15 +59,15 @@ class Export(GlancesExport):
|
||||
self.client = self.init()
|
||||
|
||||
def load_conf(self, section="influxdb"):
|
||||
"""Load the InfluxDb configuration in the Glances configuration file"""
|
||||
"""Load the InfluxDb configuration in the Glances configuration file."""
|
||||
if self.config is None:
|
||||
return False
|
||||
try:
|
||||
self.influxdb_host = self.config.get_raw_option(section, "host")
|
||||
self.influxdb_port = self.config.get_raw_option(section, "port")
|
||||
self.influxdb_user = self.config.get_raw_option(section, "user")
|
||||
self.influxdb_password = self.config.get_raw_option(section, "password")
|
||||
self.influxdb_db = self.config.get_raw_option(section, "db")
|
||||
self.host = self.config.get_value(section, 'host')
|
||||
self.port = self.config.get_value(section, 'port')
|
||||
self.user = self.config.get_value(section, 'user')
|
||||
self.password = self.config.get_value(section, 'password')
|
||||
self.db = self.config.get_value(section, 'db')
|
||||
except NoSectionError:
|
||||
logger.critical("No InfluxDB configuration found")
|
||||
return False
|
||||
@ -68,39 +76,55 @@ class Export(GlancesExport):
|
||||
return False
|
||||
else:
|
||||
logger.debug("Load InfluxDB from the Glances configuration file")
|
||||
# Prefix is optional
|
||||
try:
|
||||
self.prefix = self.config.get_value(section, 'prefix')
|
||||
except NoOptionError:
|
||||
pass
|
||||
return True
|
||||
|
||||
def init(self):
|
||||
"""Init the connection to the InfluxDB server"""
|
||||
"""Init the connection to the InfluxDB server."""
|
||||
if not self.export_enable:
|
||||
return None
|
||||
db = InfluxDBClient(self.influxdb_host,
|
||||
self.influxdb_port,
|
||||
self.influxdb_user,
|
||||
self.influxdb_password,
|
||||
self.influxdb_db)
|
||||
|
||||
try:
|
||||
get_all_db = db.get_database_list()[0].values()
|
||||
except client.InfluxDBClientError as e:
|
||||
logger.critical("Can not connect to InfluxDB database '%s' (%s)" % (self.influxdb_db, e))
|
||||
db = InfluxDBClient(host=self.host,
|
||||
port=self.port,
|
||||
username=self.user,
|
||||
password=self.password,
|
||||
database=self.db)
|
||||
get_all_db = [i['name'] for i in db.get_list_database()]
|
||||
except InfluxDBClientError:
|
||||
# https://github.com/influxdb/influxdb-python/issues/138
|
||||
logger.info("Trying fallback to InfluxDB v0.8")
|
||||
db = InfluxDBClient08(host=self.host,
|
||||
port=self.port,
|
||||
username=self.user,
|
||||
password=self.password,
|
||||
database=self.db)
|
||||
get_all_db = [i['name'] for i in db.get_list_database()]
|
||||
except InfluxDBClientError08 as e:
|
||||
logger.critical("Cannot connect to InfluxDB database '%s' (%s)" % (self.db, e))
|
||||
sys.exit(2)
|
||||
|
||||
if self.influxdb_db in get_all_db:
|
||||
if self.db in get_all_db:
|
||||
logger.info(
|
||||
"Stats will be exported to InfluxDB server: {0}".format(db._baseurl))
|
||||
else:
|
||||
logger.critical("InfluxDB database '%s' did not exist. Please create it" % self.influxdb_db)
|
||||
logger.critical("InfluxDB database '%s' did not exist. Please create it" % self.db)
|
||||
sys.exit(2)
|
||||
return db
|
||||
|
||||
def export(self, name, columns, points):
|
||||
"""Write the points to the InfluxDB server"""
|
||||
data = [
|
||||
{
|
||||
"name": name,
|
||||
"columns": columns,
|
||||
"points": [points]
|
||||
}]
|
||||
"""Write the points to the InfluxDB server."""
|
||||
# Manage prefix
|
||||
if self.prefix is not None:
|
||||
name = self.prefix + '.' + name
|
||||
# logger.info(self.prefix)
|
||||
# Create DB input
|
||||
data = [{'name': name, 'columns': columns, 'points': [points]}]
|
||||
# Write input to the InfluxDB database
|
||||
try:
|
||||
self.client.write_points(data)
|
||||
except Exception as e:
|
||||
|
112
glances/exports/glances_rabbitmq.py
Normal file
@ -0,0 +1,112 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
# Copyright (C) 2015 Nicolargo <nicolas@nicolargo.com>
|
||||
#
|
||||
# Glances is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Glances is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""JMS interface class."""
|
||||
|
||||
# Import sys libs
|
||||
import datetime
|
||||
import socket
|
||||
import sys
|
||||
from numbers import Number
|
||||
|
||||
# Import Glances lib
|
||||
from glances.core.glances_logging import logger
|
||||
try:
|
||||
from configparser import NoOptionError, NoSectionError
|
||||
except ImportError: # Python 2
|
||||
from ConfigParser import NoOptionError, NoSectionError
|
||||
from glances.exports.glances_export import GlancesExport
|
||||
|
||||
# Import pika for RabbitMQ
|
||||
import pika
|
||||
|
||||
|
||||
class Export(GlancesExport):
|
||||
|
||||
"""This class manages the rabbitMQ export module."""
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
"""Init the RabbitMQ export IF."""
|
||||
GlancesExport.__init__(self, config=config, args=args)
|
||||
|
||||
# Load the rabbitMQ configuration file
|
||||
self.rabbitmq_host = None
|
||||
self.rabbitmq_port = None
|
||||
self.rabbitmq_user = None
|
||||
self.rabbitmq_password = None
|
||||
self.rabbitmq_queue = None
|
||||
self.hostname = socket.gethostname()
|
||||
self.export_enable = self.load_conf()
|
||||
if not self.export_enable:
|
||||
sys.exit(2)
|
||||
|
||||
# Init the rabbitmq client
|
||||
self.client = self.init()
|
||||
|
||||
def load_conf(self, section="rabbitmq"):
|
||||
"""Load the rabbitmq configuration in the Glances configuration file."""
|
||||
if self.config is None:
|
||||
return False
|
||||
try:
|
||||
self.rabbitmq_host = self.config.get_value(section, 'host')
|
||||
self.rabbitmq_port = self.config.get_value(section, 'port')
|
||||
self.rabbitmq_user = self.config.get_value(section, 'user')
|
||||
self.rabbitmq_password = self.config.get_value(section, 'password')
|
||||
self.rabbitmq_queue = self.config.get_value(section, 'queue')
|
||||
except NoSectionError:
|
||||
logger.critical("No rabbitmq configuration found")
|
||||
return False
|
||||
except NoOptionError as e:
|
||||
logger.critical("Error in the RabbitM configuration (%s)" % e)
|
||||
return False
|
||||
else:
|
||||
logger.debug("Load RabbitMQ from the Glances configuration file")
|
||||
return True
|
||||
|
||||
def init(self):
|
||||
"""Init the connection to the rabbitmq server."""
|
||||
if not self.export_enable:
|
||||
return None
|
||||
try:
|
||||
parameters = pika.URLParameters(
|
||||
'amqp://' + self.rabbitmq_user +
|
||||
':' + self.rabbitmq_password +
|
||||
'@' + self.rabbitmq_host +
|
||||
':' + self.rabbitmq_port + '/')
|
||||
connection = pika.BlockingConnection(parameters)
|
||||
channel = connection.channel()
|
||||
return channel
|
||||
except Exception as e:
|
||||
logger.critical("Connection to rabbitMQ failed : %s " % e)
|
||||
return None
|
||||
|
||||
def export(self, name, columns, points):
|
||||
"""Write the points in RabbitMQ."""
|
||||
data = ('hostname=' + self.hostname + ', name=' + name +
|
||||
', dateinfo=' + datetime.datetime.utcnow().isoformat())
|
||||
for i in range(0, len(columns)):
|
||||
if not isinstance(points[i], Number):
|
||||
continue
|
||||
else:
|
||||
data += ", " + columns[i] + "=" + str(points[i])
|
||||
logger.debug(data)
|
||||
try:
|
||||
self.client.basic_publish(exchange='', routing_key=self.rabbitmq_queue, body=data)
|
||||
except Exception as e:
|
||||
logger.error("Can not export stats to RabbitMQ (%s)" % e)
|
@ -20,15 +20,19 @@
|
||||
"""Statsd interface class."""
|
||||
|
||||
# Import sys libs
|
||||
from statsd import StatsClient
|
||||
from numbers import Number
|
||||
import sys
|
||||
from numbers import Number
|
||||
try:
|
||||
from configparser import NoOptionError, NoSectionError
|
||||
except ImportError: # Python 2
|
||||
from ConfigParser import NoOptionError, NoSectionError
|
||||
|
||||
# Import Glances lib
|
||||
from glances.core.glances_logging import logger
|
||||
from ConfigParser import NoSectionError, NoOptionError
|
||||
from glances.exports.glances_export import GlancesExport
|
||||
|
||||
from statsd import StatsClient
|
||||
|
||||
|
||||
class Export(GlancesExport):
|
||||
|
||||
@ -56,12 +60,12 @@ class Export(GlancesExport):
|
||||
prefix=self.prefix)
|
||||
|
||||
def load_conf(self, section="statsd"):
|
||||
"""Load the Statsd configuration in the Glances configuration file"""
|
||||
"""Load the Statsd configuration in the Glances configuration file."""
|
||||
if self.config is None:
|
||||
return False
|
||||
try:
|
||||
self.host = self.config.get_raw_option(section, "host")
|
||||
self.port = self.config.get_raw_option(section, "port")
|
||||
self.host = self.config.get_value(section, 'host')
|
||||
self.port = self.config.get_value(section, 'port')
|
||||
except NoSectionError:
|
||||
logger.critical("No Statsd configuration found")
|
||||
return False
|
||||
@ -72,13 +76,13 @@ class Export(GlancesExport):
|
||||
logger.debug("Load Statsd from the Glances configuration file")
|
||||
# Prefix is optional
|
||||
try:
|
||||
self.prefix = self.config.get_raw_option(section, "prefix")
|
||||
except NoOptionError as e:
|
||||
self.prefix = self.config.get_value(section, 'prefix')
|
||||
except NoOptionError:
|
||||
pass
|
||||
return True
|
||||
|
||||
def init(self, prefix='glances'):
|
||||
"""Init the connection to the Statsd server"""
|
||||
"""Init the connection to the Statsd server."""
|
||||
if not self.export_enable:
|
||||
return None
|
||||
return StatsClient(self.host,
|
||||
@ -86,7 +90,7 @@ class Export(GlancesExport):
|
||||
prefix=prefix)
|
||||
|
||||
def export(self, name, columns, points):
|
||||
"""Export the stats to the Statsd server"""
|
||||
"""Export the stats to the Statsd server."""
|
||||
for i in range(0, len(columns)):
|
||||
if not isinstance(points[i], Number):
|
||||
continue
|
||||
|
@ -1,61 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="{{refresh_time}}">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Glances</title>
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||
<link rel="stylesheet" type="text/css" href="normalize.css" />
|
||||
<link rel="stylesheet" type="text/css" href="bootstrap.min.css" />
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<script src="modernizr.custom.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="pull-left">
|
||||
% include('plugin_text', plugin_name="system", stats=stats['system'])
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
% include('plugin_text', plugin_name="uptime", stats=stats['uptime'])
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-3">
|
||||
% include('plugin_table', plugin_name="cpu", stats=stats['cpu'])
|
||||
</div>
|
||||
<div class="col-sm-3 col-lg-2 col-lg-offset-1">
|
||||
% include('plugin_table', plugin_name="load", stats=stats['load'])
|
||||
</div>
|
||||
<div class="col-sm-3 col-lg-3">
|
||||
% include('plugin_table', plugin_name="mem", stats=stats['mem'])
|
||||
</div>
|
||||
<div class="col-sm-3 col-lg-2 col-lg-offset-1">
|
||||
% include('plugin_table', plugin_name="memswap", stats=stats['memswap'])
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-3">
|
||||
% include('plugin_table', plugin_name="network", stats=stats['network'])
|
||||
% include('plugin_table', plugin_name="diskio", stats=stats['diskio'])
|
||||
% include('plugin_table', plugin_name="fs", stats=stats['fs'])
|
||||
% include('plugin_table', plugin_name="sensors", stats=stats['sensors'])
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
% include('plugin_table', plugin_name="alert", stats=stats['alert'])
|
||||
% include('plugin_text', plugin_name="processcount", stats=stats['processcount'])
|
||||
% include('plugin_table', plugin_name="docker", stats=stats['docker'])
|
||||
<div class="row">
|
||||
<div class="col-sm-9">
|
||||
% include('plugin_table', plugin_name="monitor", stats=stats['monitor'])
|
||||
</div>
|
||||
</div>
|
||||
% include('plugin_table', plugin_name="processlist", stats=stats['processlist'])
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -1,22 +0,0 @@
|
||||
% if stats['msgdict'] != []:
|
||||
<section id="{{ plugin_name }}" class="plugin">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
% for msg in stats['msgdict']:
|
||||
% if msg['msg'].startswith('\n'):
|
||||
</tr>
|
||||
<tr>
|
||||
% else:
|
||||
% if stats['display']:
|
||||
<td class="{{ msg['decoration'].lower() }} {{ 'hidden-xs hidden-sm' if msg['optional'] else '' }}">
|
||||
{{ msg['msg'] }}
|
||||
</td>
|
||||
% end
|
||||
% end
|
||||
% end
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
% end
|
@ -1,11 +0,0 @@
|
||||
% if stats['msgdict'] != []:
|
||||
<section id="{{ plugin_name }}" class="plugin">
|
||||
% for msg in stats['msgdict']:
|
||||
% if stats['display']:
|
||||
<span class="{{ msg['decoration'].lower() }} {{ 'hidden-xs hidden-sm' if msg['optional'] else '' }}">
|
||||
{{ msg['msg'] }}
|
||||
</span>
|
||||
% end
|
||||
% end
|
||||
</section>
|
||||
% end
|
@ -20,54 +20,78 @@
|
||||
"""Manage bars for Glances output."""
|
||||
|
||||
# Import system lib
|
||||
import locale
|
||||
from math import modf
|
||||
|
||||
# Global vars
|
||||
curses_bars = [" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█"]
|
||||
|
||||
|
||||
class Bar(object):
|
||||
"""Manage bar (progression or status)
|
||||
|
||||
r"""Manage bar (progression or status).
|
||||
|
||||
import sys
|
||||
import time
|
||||
b = Bar(10)
|
||||
for p in range(0, 100):
|
||||
b.set_percent(p)
|
||||
b.percent = p
|
||||
print("\r%s" % b),
|
||||
time.sleep(0.1)
|
||||
sys.stdout.flush()
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, size):
|
||||
def __init__(self, size,
|
||||
pre_char='[',
|
||||
post_char=']',
|
||||
empty_char='_',
|
||||
with_text=True):
|
||||
# Bar size
|
||||
self.__size = size
|
||||
# Bar current percent
|
||||
self.__percent = 0
|
||||
# Char used for the decoration
|
||||
self.__pre_char = pre_char
|
||||
self.__post_char = post_char
|
||||
self.__empty_char = empty_char
|
||||
self.__with_text = with_text
|
||||
# Char used for the bar
|
||||
self.curses_bars = self.__get_curses_bars()
|
||||
|
||||
def get_size(self):
|
||||
def __get_curses_bars(self):
|
||||
# Return the chars used to display the bar
|
||||
if locale.getdefaultlocale()[1] == 'UTF-8':
|
||||
return [" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█"]
|
||||
else:
|
||||
return [" ", " ", " ", " ", "|", "|", "|", "|", "|"]
|
||||
|
||||
@property
|
||||
def size(self, with_decoration=False):
|
||||
# Return the bar size, with or without decoration
|
||||
if with_decoration:
|
||||
return self.__size
|
||||
if self.__with_text:
|
||||
return self.__size - 6
|
||||
|
||||
def set_size(self, size):
|
||||
self.__size = size
|
||||
return self.__size
|
||||
# @size.setter
|
||||
# def size(self, value):
|
||||
# self.__size = value
|
||||
|
||||
def get_percent(self):
|
||||
@property
|
||||
def percent(self):
|
||||
return self.__percent
|
||||
|
||||
def set_percent(self, percent):
|
||||
assert percent >= 0
|
||||
assert percent <= 100
|
||||
self.__percent = percent
|
||||
return self.__percent
|
||||
@percent.setter
|
||||
def percent(self, value):
|
||||
assert value >= 0
|
||||
assert value <= 100
|
||||
self.__percent = value
|
||||
|
||||
def __str__(self):
|
||||
"""Return the bars"""
|
||||
frac, whole = modf(self.get_size() * self.get_percent() / 100.0)
|
||||
ret = curses_bars[8] * int(whole)
|
||||
"""Return the bars."""
|
||||
frac, whole = modf(self.size * self.percent / 100.0)
|
||||
ret = self.curses_bars[8] * int(whole)
|
||||
if frac > 0:
|
||||
ret += curses_bars[int(frac * 8)]
|
||||
ret += self.curses_bars[int(frac * 8)]
|
||||
whole += 1
|
||||
ret += '_' * int(self.get_size() - whole)
|
||||
return ret
|
||||
ret += self.__empty_char * int(self.size - whole)
|
||||
if self.__with_text:
|
||||
ret = '{0}{1:>5}%'.format(ret, self.percent)
|
||||
return self.__pre_char + ret + self.__post_char
|
||||
|
@ -23,12 +23,11 @@ import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Import Glances libs
|
||||
from glances.core.glances_globals import is_windows
|
||||
from glances.core.glances_logging import logger
|
||||
|
||||
# Import mandatory Bottle lib
|
||||
try:
|
||||
from bottle import Bottle, template, static_file, TEMPLATE_PATH, abort, response, request
|
||||
from bottle import Bottle, static_file, abort, response, request
|
||||
except ImportError:
|
||||
logger.critical('Bottle module not found. Glances cannot start in web server mode.')
|
||||
sys.exit(2)
|
||||
@ -53,9 +52,6 @@ class GlancesBottle(object):
|
||||
# Define routes
|
||||
self._route()
|
||||
|
||||
# Update the template path (glances/outputs/bottle)
|
||||
TEMPLATE_PATH.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'bottle'))
|
||||
|
||||
# Path where the statics files are stored
|
||||
self.STATIC_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'static')
|
||||
|
||||
@ -63,10 +59,17 @@ class GlancesBottle(object):
|
||||
"""Define route."""
|
||||
self._app.route('/', method="GET", callback=self._index)
|
||||
self._app.route('/<refresh_time:int>', method=["GET", "POST"], callback=self._index)
|
||||
|
||||
self._app.route('/<filename:re:.*\.css>', method="GET", callback=self._css)
|
||||
self._app.route('/<filename:re:.*\.js>', method="GET", callback=self._js)
|
||||
self._app.route('/<filename:re:.*\.js.map>', method="GET", callback=self._js_map)
|
||||
self._app.route('/<filename:re:.*\.html>', method="GET", callback=self._html)
|
||||
|
||||
self._app.route('/<filename:re:.*\.png>', method="GET", callback=self._images)
|
||||
self._app.route('/favicon.ico', method="GET", callback=self._favicon)
|
||||
|
||||
# REST API
|
||||
self._app.route('/api/2/help', method="GET", callback=self._api_help)
|
||||
self._app.route('/api/2/pluginslist', method="GET", callback=self._api_plugins)
|
||||
self._app.route('/api/2/all', method="GET", callback=self._api_all)
|
||||
self._app.route('/api/2/all/limits', method="GET", callback=self._api_all_limits)
|
||||
@ -86,7 +89,7 @@ class GlancesBottle(object):
|
||||
self.plugins_list = self.stats.getAllPlugins()
|
||||
|
||||
# Bind the Bottle TCP address/port
|
||||
bindmsg = _("Glances web server started on http://{0}:{1}/").format(self.args.bind_address, self.args.port)
|
||||
bindmsg = 'Glances web server started on http://{0}:{1}/'.format(self.args.bind_address, self.args.port)
|
||||
logger.info(bindmsg)
|
||||
print(bindmsg)
|
||||
self._app.run(host=self.args.bind_address, port=self.args.port, quiet=not self.args.debug)
|
||||
@ -105,7 +108,12 @@ class GlancesBottle(object):
|
||||
self.stats.update()
|
||||
|
||||
# Display
|
||||
return self.display(self.stats, refresh_time=refresh_time)
|
||||
return static_file("index.html", root=os.path.join(self.STATIC_PATH, 'html'))
|
||||
|
||||
def _html(self, filename):
|
||||
"""Bottle callback for *.html files."""
|
||||
# Return the static file
|
||||
return static_file(filename, root=os.path.join(self.STATIC_PATH, 'html'))
|
||||
|
||||
def _css(self, filename):
|
||||
"""Bottle callback for *.css files."""
|
||||
@ -117,16 +125,60 @@ class GlancesBottle(object):
|
||||
# Return the static file
|
||||
return static_file(filename, root=os.path.join(self.STATIC_PATH, 'js'))
|
||||
|
||||
def _js_map(self, filename):
|
||||
"""Bottle callback for *.js.map files."""
|
||||
# Return the static file
|
||||
return static_file(filename, root=os.path.join(self.STATIC_PATH, 'js'))
|
||||
|
||||
def _images(self, filename):
|
||||
"""Bottle callback for *.png files."""
|
||||
# Return the static file
|
||||
return static_file(filename, root=os.path.join(self.STATIC_PATH, 'images'))
|
||||
|
||||
def _favicon(self):
|
||||
"""Bottle callback for favicon."""
|
||||
# Return the static file
|
||||
return static_file('favicon.ico', root=self.STATIC_PATH)
|
||||
|
||||
def _api_help(self):
|
||||
"""Glances API RESTFul implementation.
|
||||
|
||||
Return the help data or 404 error.
|
||||
"""
|
||||
response.content_type = 'application/json'
|
||||
|
||||
# Update the stat
|
||||
view_data = self.stats.get_plugin("help").get_view_data()
|
||||
try:
|
||||
plist = json.dumps(view_data, sort_keys=True)
|
||||
except Exception as e:
|
||||
abort(404, "Cannot get help view data (%s)" % str(e))
|
||||
return plist
|
||||
|
||||
def _api_plugins(self):
|
||||
"""
|
||||
Glances API RESTFul implementation
|
||||
Return the plugin list
|
||||
or 404 error
|
||||
@api {get} /api/2/pluginslist Get plugins list
|
||||
@apiVersion 2.0
|
||||
@apiName pluginslist
|
||||
@apiGroup plugin
|
||||
|
||||
@apiSuccess {String[]} Plugins list.
|
||||
|
||||
@apiSuccessExample Success-Response:
|
||||
HTTP/1.1 200 OK
|
||||
[
|
||||
"load",
|
||||
"help",
|
||||
"ip",
|
||||
"memswap",
|
||||
"processlist",
|
||||
...
|
||||
]
|
||||
|
||||
@apiError Cannot get plugin list.
|
||||
|
||||
@apiErrorExample Error-Response:
|
||||
HTTP/1.1 404 Not Found
|
||||
"""
|
||||
response.content_type = 'application/json'
|
||||
|
||||
@ -140,8 +192,8 @@ class GlancesBottle(object):
|
||||
return plist
|
||||
|
||||
def _api_all(self):
|
||||
"""
|
||||
Glances API RESTFul implementation
|
||||
"""Glances API RESTFul implementation.
|
||||
|
||||
Return the JSON representation of all the plugins
|
||||
HTTP/200 if OK
|
||||
HTTP/400 if plugin is not found
|
||||
@ -149,19 +201,28 @@ class GlancesBottle(object):
|
||||
"""
|
||||
response.content_type = 'application/json'
|
||||
|
||||
if not self.args.debug:
|
||||
# Update the stat
|
||||
self.stats.update()
|
||||
|
||||
try:
|
||||
# Get the JSON value of the stat value
|
||||
# Get the JSON value of the stat ID
|
||||
statval = json.dumps(self.stats.getAllAsDict())
|
||||
except Exception as e:
|
||||
abort(404, "Cannot get stats (%s)" % str(e))
|
||||
return statval
|
||||
else:
|
||||
path = "~/glances/"
|
||||
if is_windows:
|
||||
path = "D:\\glances\\"
|
||||
filepath = path + "debug.json"
|
||||
|
||||
f = open(filepath)
|
||||
return f.read()
|
||||
|
||||
def _api_all_limits(self):
|
||||
"""
|
||||
Glances API RESTFul implementation
|
||||
"""Glances API RESTFul implementation.
|
||||
|
||||
Return the JSON representation of all the plugins limits
|
||||
HTTP/200 if OK
|
||||
HTTP/400 if plugin is not found
|
||||
@ -177,8 +238,8 @@ class GlancesBottle(object):
|
||||
return limits
|
||||
|
||||
def _api_all_views(self):
|
||||
"""
|
||||
Glances API RESTFul implementation
|
||||
"""Glances API RESTFul implementation.
|
||||
|
||||
Return the JSON representation of all the plugins views
|
||||
HTTP/200 if OK
|
||||
HTTP/400 if plugin is not found
|
||||
@ -194,8 +255,8 @@ class GlancesBottle(object):
|
||||
return limits
|
||||
|
||||
def _api(self, plugin):
|
||||
"""
|
||||
Glances API RESTFul implementation
|
||||
"""Glances API RESTFul implementation.
|
||||
|
||||
Return the JSON representation of a given plugin
|
||||
HTTP/200 if OK
|
||||
HTTP/400 if plugin is not found
|
||||
@ -217,8 +278,8 @@ class GlancesBottle(object):
|
||||
return statval
|
||||
|
||||
def _api_limits(self, plugin):
|
||||
"""
|
||||
Glances API RESTFul implementation
|
||||
"""Glances API RESTFul implementation.
|
||||
|
||||
Return the JSON limits of a given plugin
|
||||
HTTP/200 if OK
|
||||
HTTP/400 if plugin is not found
|
||||
@ -234,14 +295,14 @@ class GlancesBottle(object):
|
||||
|
||||
try:
|
||||
# Get the JSON value of the stat limits
|
||||
ret = self.stats.get_plugin(plugin).get_limits()
|
||||
ret = self.stats.get_plugin(plugin).limits
|
||||
except Exception as e:
|
||||
abort(404, "Cannot get limits for plugin %s (%s)" % (plugin, str(e)))
|
||||
return ret
|
||||
|
||||
def _api_views(self, plugin):
|
||||
"""
|
||||
Glances API RESTFul implementation
|
||||
"""Glances API RESTFul implementation.
|
||||
|
||||
Return the JSON views of a given plugin
|
||||
HTTP/200 if OK
|
||||
HTTP/400 if plugin is not found
|
||||
@ -263,8 +324,8 @@ class GlancesBottle(object):
|
||||
return ret
|
||||
|
||||
def _api_item(self, plugin, item):
|
||||
"""
|
||||
Glances API RESTFul implementation
|
||||
"""Glances API RESTFul implementation.
|
||||
|
||||
Return the JSON represenation of the couple plugin/item
|
||||
HTTP/200 if OK
|
||||
HTTP/400 if plugin is not found
|
||||
@ -287,8 +348,8 @@ class GlancesBottle(object):
|
||||
return plist
|
||||
|
||||
def _api_value(self, plugin, item, value):
|
||||
"""
|
||||
Glances API RESTFul implementation
|
||||
"""Glances API RESTFul implementation.
|
||||
|
||||
Return the process stats (dict) for the given item=value
|
||||
HTTP/200 if OK
|
||||
HTTP/400 if plugin is not found
|
||||
@ -309,33 +370,6 @@ class GlancesBottle(object):
|
||||
else:
|
||||
return pdict
|
||||
|
||||
def display(self, stats, refresh_time=None):
|
||||
"""Display stats on the web page.
|
||||
|
||||
stats: Stats database to display
|
||||
"""
|
||||
|
||||
stats = {
|
||||
'system': self.stats.get_plugin('system').get_stats_display(args=self.args),
|
||||
'uptime': self.stats.get_plugin('uptime').get_stats_display(args=self.args),
|
||||
'cpu': self.stats.get_plugin('cpu').get_stats_display(args=self.args),
|
||||
'load': self.stats.get_plugin('load').get_stats_display(args=self.args),
|
||||
'mem': self.stats.get_plugin('mem').get_stats_display(args=self.args),
|
||||
'memswap': self.stats.get_plugin('memswap').get_stats_display(args=self.args),
|
||||
'network': self.stats.get_plugin('network').get_stats_display(args=self.args),
|
||||
'diskio': self.stats.get_plugin('diskio').get_stats_display(args=self.args),
|
||||
'fs': self.stats.get_plugin('fs').get_stats_display(args=self.args),
|
||||
'raid': self.stats.get_plugin('raid').get_stats_display(args=self.args),
|
||||
'sensors': self.stats.get_plugin('sensors').get_stats_display(args=self.args),
|
||||
'alert': self.stats.get_plugin('alert').get_stats_display(args=self.args),
|
||||
'processcount': self.stats.get_plugin('processcount').get_stats_display(args=self.args),
|
||||
'monitor': self.stats.get_plugin('monitor').get_stats_display(args=self.args),
|
||||
'processlist': self.stats.get_plugin('processlist').get_stats_display(args=self.args),
|
||||
'docker': self.stats.get_plugin('docker').get_stats_display(args=self.args)
|
||||
}
|
||||
|
||||
return template('base', refresh_time=refresh_time, stats=stats)
|
||||
|
||||
|
||||
class EnableCors(object):
|
||||
name = 'enable_cors'
|
||||
|
@ -80,10 +80,10 @@ class Screen(object):
|
||||
def subwin(self, x, y):
|
||||
return self
|
||||
|
||||
def keypad(self, id):
|
||||
def keypad(self, screen_id):
|
||||
return None
|
||||
|
||||
def nodelay(self, id):
|
||||
def nodelay(self, screen_id):
|
||||
return None
|
||||
|
||||
def getch(self):
|
||||
@ -170,8 +170,8 @@ class WCurseLight(object):
|
||||
def napms(self, t):
|
||||
time.sleep(t / 1000 if t > 1000 else 1)
|
||||
|
||||
def init_pair(self, id, fg, bk):
|
||||
self.colors[id] = [max(fg, 0), max(bk, 0)]
|
||||
def init_pair(self, color_id, fg, bk):
|
||||
self.colors[color_id] = [max(fg, 0), max(bk, 0)]
|
||||
|
||||
def color_pair(self, id):
|
||||
return id
|
||||
def color_pair(self, color_id):
|
||||
return color_id
|
||||
|
@ -20,6 +20,7 @@
|
||||
"""Curses interface class."""
|
||||
|
||||
# Import system lib
|
||||
import re
|
||||
import sys
|
||||
|
||||
# Import Glances lib
|
||||
@ -36,7 +37,8 @@ if not is_windows:
|
||||
import curses.panel
|
||||
from curses.textpad import Textbox
|
||||
except ImportError:
|
||||
logger.critical("Curses module not found. Glances cannot start in standalone mode.")
|
||||
logger.critical(
|
||||
"Curses module not found. Glances cannot start in standalone mode.")
|
||||
sys.exit(1)
|
||||
else:
|
||||
from glances.outputs.glances_colorconsole import WCurseLight
|
||||
@ -45,9 +47,9 @@ else:
|
||||
|
||||
class _GlancesCurses(object):
|
||||
|
||||
"""
|
||||
This class manages the curses display (and key pressed).
|
||||
Note: It is a private class, use GlancesCursesClient or GlancesCursesBrowser
|
||||
"""This class manages the curses display (and key pressed).
|
||||
|
||||
Note: It is a private class, use GlancesCursesClient or GlancesCursesBrowser.
|
||||
"""
|
||||
|
||||
def __init__(self, args=None):
|
||||
@ -126,6 +128,7 @@ class _GlancesCurses(object):
|
||||
self.no_color = curses.color_pair(1)
|
||||
self.default_color = curses.color_pair(3) | A_BOLD
|
||||
self.nice_color = curses.color_pair(9) | A_BOLD
|
||||
self.cpu_time_color = curses.color_pair(9) | A_BOLD
|
||||
self.ifCAREFUL_color = curses.color_pair(4) | A_BOLD
|
||||
self.ifWARNING_color = curses.color_pair(5) | A_BOLD
|
||||
self.ifCRITICAL_color = curses.color_pair(2) | A_BOLD
|
||||
@ -139,6 +142,7 @@ class _GlancesCurses(object):
|
||||
self.no_color = curses.A_NORMAL
|
||||
self.default_color = curses.A_NORMAL
|
||||
self.nice_color = A_BOLD
|
||||
self.cpu_time_color = A_BOLD
|
||||
self.ifCAREFUL_color = curses.A_UNDERLINE
|
||||
self.ifWARNING_color = A_BOLD
|
||||
self.ifCRITICAL_color = curses.A_REVERSE
|
||||
@ -160,6 +164,7 @@ class _GlancesCurses(object):
|
||||
'PROCESS': self.default_color2,
|
||||
'STATUS': self.default_color2,
|
||||
'NICE': self.nice_color,
|
||||
'CPU_TIME': self.cpu_time_color,
|
||||
'CAREFUL': self.ifCAREFUL_color2,
|
||||
'WARNING': self.ifWARNING_color2,
|
||||
'CRITICAL': self.ifCRITICAL_color2,
|
||||
@ -175,9 +180,6 @@ class _GlancesCurses(object):
|
||||
# Init refresh time
|
||||
self.__refresh_time = args.time
|
||||
|
||||
# Init process sort method
|
||||
self.args.process_sorted_by = 'auto'
|
||||
|
||||
# Init edit filter tag
|
||||
self.edit_filter = False
|
||||
|
||||
@ -200,7 +202,8 @@ class _GlancesCurses(object):
|
||||
'Stats history disabled because MatPlotLib is not installed')
|
||||
|
||||
def set_cursor(self, value):
|
||||
"""Configure the curse cursor apparence
|
||||
"""Configure the curse cursor apparence.
|
||||
|
||||
0: invisible
|
||||
1: visible
|
||||
2: very visible
|
||||
@ -248,21 +251,24 @@ class _GlancesCurses(object):
|
||||
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('/'):
|
||||
# '/' > 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
|
||||
self.args.process_sorted_by = 'auto'
|
||||
glances_processes.resetsort()
|
||||
# 'a' > Sort processes automatically and reset to 'cpu_percent'
|
||||
glances_processes.auto_sort = True
|
||||
glances_processes.sort_key = 'cpu_percent'
|
||||
elif self.pressedkey == ord('b'):
|
||||
# 'b' > Switch between bit/s and Byte/s for network IO
|
||||
# self.net_byteps_tag = not self.net_byteps_tag
|
||||
self.args.byte = not self.args.byte
|
||||
elif self.pressedkey == ord('c'):
|
||||
# 'c' > Sort processes by CPU usage
|
||||
self.args.process_sorted_by = 'cpu_percent'
|
||||
glances_processes.setmanualsortkey(self.args.process_sorted_by)
|
||||
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
|
||||
@ -290,22 +296,25 @@ class _GlancesCurses(object):
|
||||
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)
|
||||
self.args.process_sorted_by = 'io_counters'
|
||||
glances_processes.setmanualsortkey(self.args.process_sorted_by)
|
||||
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
|
||||
self.args.process_sorted_by = 'memory_percent'
|
||||
glances_processes.setmanualsortkey(self.args.process_sorted_by)
|
||||
glances_processes.auto_sort = False
|
||||
glances_processes.sort_key = 'memory_percent'
|
||||
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
|
||||
self.args.process_sorted_by = 'name'
|
||||
glances_processes.setmanualsortkey(self.args.process_sorted_by)
|
||||
glances_processes.auto_sort = False
|
||||
glances_processes.sort_key = 'name'
|
||||
elif self.pressedkey == ord('r'):
|
||||
# 'r' > Reset history
|
||||
self.reset_history_tag = not self.reset_history_tag
|
||||
@ -317,13 +326,17 @@ class _GlancesCurses(object):
|
||||
self.args.disable_sensors = not self.args.disable_sensors
|
||||
elif self.pressedkey == ord('t'):
|
||||
# 't' > Sort processes by TIME usage
|
||||
self.args.process_sorted_by = 'cpu_times'
|
||||
glances_processes.setmanualsortkey(self.args.process_sorted_by)
|
||||
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' > View cumulative network IO (instead of bitrate)
|
||||
# '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
|
||||
@ -357,31 +370,29 @@ class _GlancesCurses(object):
|
||||
curses.endwin()
|
||||
|
||||
def init_line_column(self):
|
||||
"""Init the line and column position for the curses inteface"""
|
||||
self.line = 0
|
||||
self.column = 0
|
||||
self.next_line = 0
|
||||
self.next_column = 0
|
||||
"""Init the line and column position for the curses inteface."""
|
||||
self.init_line()
|
||||
self.init_column()
|
||||
|
||||
def init_line(self):
|
||||
"""Init the line position for the curses inteface"""
|
||||
"""Init the line position for the curses inteface."""
|
||||
self.line = 0
|
||||
self.next_line = 0
|
||||
|
||||
def init_column(self):
|
||||
"""Init the column position for the curses inteface"""
|
||||
"""Init the column position for the curses inteface."""
|
||||
self.column = 0
|
||||
self.next_column = 0
|
||||
|
||||
def new_line(self):
|
||||
"""New line in the curses interface"""
|
||||
"""New line in the curses interface."""
|
||||
self.line = self.next_line
|
||||
|
||||
def new_column(self):
|
||||
"""New column in the curses interface"""
|
||||
"""New column in the curses interface."""
|
||||
self.column = self.next_column
|
||||
|
||||
def display(self, stats, cs_status="None"):
|
||||
def display(self, stats, cs_status=None):
|
||||
"""Display stats on the screen.
|
||||
|
||||
stats: Stats database to display
|
||||
@ -426,6 +437,10 @@ class _GlancesCurses(object):
|
||||
stats_memswap = stats.get_plugin('memswap').get_stats_display()
|
||||
stats_network = stats.get_plugin('network').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(
|
||||
@ -452,11 +467,10 @@ class _GlancesCurses(object):
|
||||
max_processes_displayed -= 4
|
||||
if max_processes_displayed < 0:
|
||||
max_processes_displayed = 0
|
||||
if glances_processes.get_max_processes() is None or \
|
||||
glances_processes.get_max_processes() != max_processes_displayed:
|
||||
logger.debug("Set number of displayed processes to %s" %
|
||||
max_processes_displayed)
|
||||
glances_processes.set_max_processes(max_processes_displayed)
|
||||
if (glances_processes.max_processes is None or
|
||||
glances_processes.max_processes != max_processes_displayed):
|
||||
logger.debug("Set number of displayed processes to {0}".format(max_processes_displayed))
|
||||
glances_processes.max_processes = max_processes_displayed
|
||||
|
||||
stats_processlist = stats.get_plugin(
|
||||
'processlist').get_stats_display(args=self.args)
|
||||
@ -472,55 +486,93 @@ class _GlancesCurses(object):
|
||||
# ... and exit
|
||||
return False
|
||||
|
||||
# ==================================
|
||||
# Display first line (system+uptime)
|
||||
# ==================================
|
||||
# Space between column
|
||||
self.space_between_column = 0
|
||||
self.new_line()
|
||||
l = self.get_stats_display_width(
|
||||
stats_system) + self.get_stats_display_width(stats_uptime) + self.space_between_column
|
||||
self.display_plugin(stats_system, display_optional=(screen_x >= l))
|
||||
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)
|
||||
self.display_plugin(
|
||||
stats_system, display_optional=(screen_x >= l_uptime))
|
||||
self.new_column()
|
||||
self.display_plugin(stats_ip)
|
||||
# Space between column
|
||||
self.space_between_column = 3
|
||||
self.new_column()
|
||||
self.display_plugin(stats_uptime)
|
||||
|
||||
# Display second line (CPU|PERCPU+LOAD+MEM+SWAP+<SUMMARY>)
|
||||
# CPU|PERCPU
|
||||
# ========================================================
|
||||
# Display second line (<SUMMARY>+CPU|PERCPU+LOAD+MEM+SWAP)
|
||||
# ========================================================
|
||||
self.init_column()
|
||||
self.new_line()
|
||||
# Init quicklook
|
||||
stats_quicklook = {'msgdict': []}
|
||||
# Start with the mandatory stats:
|
||||
# CPU | PERCPU
|
||||
if self.args.percpu:
|
||||
l = self.get_stats_display_width(stats_percpu)
|
||||
cpu_width = self.get_stats_display_width(stats_percpu)
|
||||
quicklook_adapt = 114
|
||||
else:
|
||||
l = self.get_stats_display_width(stats_cpu)
|
||||
l += self.get_stats_display_width(stats_load) + self.get_stats_display_width(
|
||||
stats_mem) + self.get_stats_display_width(stats_memswap)
|
||||
# Space between column
|
||||
space_number = int(stats_load['msgdict'] != [
|
||||
]) + int(stats_mem['msgdict'] != []) + int(stats_memswap['msgdict'] != [])
|
||||
if space_number == 0:
|
||||
cpu_width = self.get_stats_display_width(
|
||||
stats_cpu, without_option=(screen_x < 80))
|
||||
quicklook_adapt = 108
|
||||
l = cpu_width
|
||||
# MEM & SWAP & LOAD
|
||||
l += self.get_stats_display_width(stats_mem,
|
||||
without_option=(screen_x < 100))
|
||||
l += self.get_stats_display_width(stats_memswap)
|
||||
l += self.get_stats_display_width(stats_load)
|
||||
# Quicklook plugin size is dynamic
|
||||
l_ql = 0
|
||||
if screen_x > 126 and not self.args.disable_quicklook:
|
||||
# Limit the size to be align with the process
|
||||
quicklook_width = min(screen_x - quicklook_adapt, 87)
|
||||
try:
|
||||
stats_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:
|
||||
l_ql = self.get_stats_display_width(stats_quicklook)
|
||||
# Display Quicklook
|
||||
self.display_plugin(stats_quicklook)
|
||||
self.new_column()
|
||||
# Compute space between column
|
||||
space_number = int(stats_quicklook['msgdict'] != [])
|
||||
space_number += int(stats_mem['msgdict'] != [])
|
||||
space_number += int(stats_memswap['msgdict'] != [])
|
||||
space_number += int(stats_load['msgdict'] != [])
|
||||
if space_number < 1:
|
||||
space_number = 1
|
||||
if screen_x > (space_number * self.space_between_column + l):
|
||||
self.space_between_column = int((screen_x - l) / space_number)
|
||||
# Display
|
||||
self.space_between_column = int((screen_x - l_ql - l) / space_number)
|
||||
# Display others stats
|
||||
if self.args.percpu:
|
||||
self.display_plugin(stats_percpu)
|
||||
else:
|
||||
self.display_plugin(stats_cpu, display_optional=(screen_x >= 80))
|
||||
self.new_column()
|
||||
self.display_plugin(stats_load)
|
||||
self.new_column()
|
||||
self.display_plugin(stats_mem, display_optional=(
|
||||
screen_x >= (space_number * self.space_between_column + l)))
|
||||
self.display_plugin(stats_mem, display_optional=(screen_x >= 100))
|
||||
self.new_column()
|
||||
self.display_plugin(stats_memswap)
|
||||
self.new_column()
|
||||
self.display_plugin(stats_load)
|
||||
# Space between column
|
||||
self.space_between_column = 3
|
||||
|
||||
# Backup line position
|
||||
self.saved_line = self.next_line
|
||||
|
||||
# ==================================================================
|
||||
# Display left sidebar (NETWORK+DISKIO+FS+SENSORS+Current time)
|
||||
# ==================================================================
|
||||
self.init_column()
|
||||
if (not (self.args.disable_network and self.args.disable_diskio
|
||||
and self.args.disable_fs and self.args.disable_raid
|
||||
and self.args.disable_sensors)) \
|
||||
and not self.args.disable_left_sidebar:
|
||||
if not (self.args.disable_network and self.args.disable_diskio and
|
||||
self.args.disable_fs and self.args.disable_raid and
|
||||
self.args.disable_sensors) and not self.args.disable_left_sidebar:
|
||||
self.new_line()
|
||||
self.display_plugin(stats_network)
|
||||
self.new_line()
|
||||
@ -534,6 +586,9 @@ class _GlancesCurses(object):
|
||||
self.new_line()
|
||||
self.display_plugin(stats_now)
|
||||
|
||||
# ====================================
|
||||
# Display right stats (process and co)
|
||||
# ====================================
|
||||
# If space available...
|
||||
if screen_x > 52:
|
||||
# Restore line position
|
||||
@ -546,7 +601,7 @@ class _GlancesCurses(object):
|
||||
self.display_plugin(stats_docker)
|
||||
self.new_line()
|
||||
self.display_plugin(stats_processcount)
|
||||
if glances_processes.get_process_filter() is None and cs_status == 'None':
|
||||
if glances_processes.process_filter is None and cs_status is None:
|
||||
# Do not display stats monitor list if a filter exist
|
||||
self.new_line()
|
||||
self.display_plugin(stats_monitor)
|
||||
@ -562,34 +617,34 @@ class _GlancesCurses(object):
|
||||
# Generate history graph
|
||||
if self.history_tag and self.args.enable_history:
|
||||
self.display_popup(
|
||||
_("Generate graphs history in %s\nPlease wait...") % self.glances_history.get_output_folder())
|
||||
'Generate graphs history in {0}\nPlease wait...'.format(
|
||||
self.glances_history.get_output_folder()))
|
||||
self.display_popup(
|
||||
_("Generate graphs history in %s\nDone: %s graphs generated") % (self.glances_history.get_output_folder(), self.glances_history.generate_graph(stats)))
|
||||
'Generate graphs history in {0}\nDone: {1} graphs generated'.format(
|
||||
self.glances_history.get_output_folder(),
|
||||
self.glances_history.generate_graph(stats)))
|
||||
elif self.reset_history_tag and self.args.enable_history:
|
||||
self.display_popup(_("Reset history"))
|
||||
self.display_popup('Reset history')
|
||||
self.glances_history.reset(stats)
|
||||
elif (self.history_tag or self.reset_history_tag) and not self.args.enable_history:
|
||||
try:
|
||||
self.glances_history.graph_enabled()
|
||||
except Exception:
|
||||
self.display_popup(
|
||||
_("History disabled\nEnable it using --enable-history"))
|
||||
self.display_popup('History disabled\nEnable it using --enable-history')
|
||||
else:
|
||||
self.display_popup(
|
||||
_("History disabled\nPlease install MatPlotLib"))
|
||||
self.display_popup('History disabled\nPlease install matplotlib')
|
||||
self.history_tag = False
|
||||
self.reset_history_tag = False
|
||||
|
||||
# Display edit filter popup
|
||||
# Only in standalone mode (cs_status == 'None')
|
||||
if self.edit_filter and cs_status == 'None':
|
||||
new_filter = self.display_popup(_("Process filter pattern: "),
|
||||
is_input=True,
|
||||
input_value=glances_processes.get_process_filter())
|
||||
glances_processes.set_process_filter(new_filter)
|
||||
# Only in standalone mode (cs_status is None)
|
||||
if self.edit_filter and cs_status is None:
|
||||
new_filter = self.display_popup(
|
||||
'Process filter pattern: ', is_input=True,
|
||||
input_value=glances_processes.process_filter)
|
||||
glances_processes.process_filter = new_filter
|
||||
elif self.edit_filter and cs_status != 'None':
|
||||
self.display_popup(
|
||||
_("Process filter only available in standalone mode"))
|
||||
self.display_popup('Process filter only available in standalone mode')
|
||||
self.edit_filter = False
|
||||
|
||||
return True
|
||||
@ -601,18 +656,20 @@ class _GlancesCurses(object):
|
||||
input_size=30,
|
||||
input_value=None):
|
||||
"""
|
||||
Display a centered popup.
|
||||
|
||||
If is_input is False:
|
||||
Display a centered popup with the given message during duration seconds
|
||||
If size_x and size_y: set the popup size
|
||||
else set it automatically
|
||||
Return True if the popup could be displayed
|
||||
|
||||
If is_input is True:
|
||||
Display a centered popup with the given message and a input field
|
||||
If size_x and size_y: set the popup size
|
||||
else set it automatically
|
||||
Return the input string or None if the field is empty
|
||||
"""
|
||||
|
||||
# Center the popup
|
||||
sentence_list = message.split('\n')
|
||||
if size_x is None:
|
||||
@ -637,10 +694,8 @@ class _GlancesCurses(object):
|
||||
popup.border()
|
||||
|
||||
# Add the message
|
||||
y = 0
|
||||
for m in message.split('\n'):
|
||||
for y, m in enumerate(message.split('\n')):
|
||||
popup.addnstr(2 + y, 2, m, len(m))
|
||||
y += 1
|
||||
|
||||
if is_input and not is_windows:
|
||||
# Create a subwindow for the text field
|
||||
@ -683,7 +738,7 @@ class _GlancesCurses(object):
|
||||
# Exit if:
|
||||
# - the plugin_stats message is empty
|
||||
# - the display tag = False
|
||||
if not plugin_stats['msgdict'] or not plugin_stats['display']:
|
||||
if plugin_stats is None or not plugin_stats['msgdict'] or not plugin_stats['display']:
|
||||
# Exit
|
||||
return 0
|
||||
|
||||
@ -711,7 +766,7 @@ class _GlancesCurses(object):
|
||||
# New line
|
||||
if m['msg'].startswith('\n'):
|
||||
# Go to the next line
|
||||
y = y + 1
|
||||
y += 1
|
||||
# Return to the first column
|
||||
x = display_x
|
||||
continue
|
||||
@ -743,24 +798,25 @@ class _GlancesCurses(object):
|
||||
try:
|
||||
# Python 2: we need to decode to get real screen size because utf-8 special tree chars
|
||||
# occupy several bytes
|
||||
offset = len(m['msg'].decode("utf-8"))
|
||||
offset = len(m['msg'].decode("utf-8", "replace"))
|
||||
except AttributeError:
|
||||
# Python 3: strings are strings and bytes are bytes, all is
|
||||
# good
|
||||
offset = len(m['msg'])
|
||||
x = x + offset
|
||||
x += offset
|
||||
if x > x_max:
|
||||
x_max = x
|
||||
|
||||
# Compute the next Glances column/line position
|
||||
self.next_column = max(self.next_column, x_max + self.space_between_column)
|
||||
self.next_column = max(
|
||||
self.next_column, x_max + self.space_between_column)
|
||||
self.next_line = max(self.next_line, y + self.space_between_line)
|
||||
|
||||
def erase(self):
|
||||
"""Erase the content of the screen."""
|
||||
self.term_window.erase()
|
||||
|
||||
def flush(self, stats, cs_status="None"):
|
||||
def flush(self, stats, cs_status=None):
|
||||
"""Clear and update the screen.
|
||||
|
||||
stats: Stats database to display
|
||||
@ -772,7 +828,7 @@ class _GlancesCurses(object):
|
||||
self.erase()
|
||||
self.display(stats, cs_status=cs_status)
|
||||
|
||||
def update(self, stats, cs_status="None", return_to_browser=False):
|
||||
def update(self, stats, cs_status=None, return_to_browser=False):
|
||||
"""Update the screen.
|
||||
|
||||
Wait for __refresh_time sec / catch key every 100 ms.
|
||||
@ -818,11 +874,11 @@ class _GlancesCurses(object):
|
||||
try:
|
||||
if without_option:
|
||||
# Size without options
|
||||
c = len(max(''.join([(i['msg'] if not i['optional'] else "")
|
||||
c = len(max(''.join([(re.sub(r'[^\x00-\x7F]+', ' ', i['msg']) if not i['optional'] else "")
|
||||
for i in curse_msg['msgdict']]).split('\n'), key=len))
|
||||
else:
|
||||
# Size with all options
|
||||
c = len(max(''.join([i['msg']
|
||||
c = len(max(''.join([re.sub(r'[^\x00-\x7F]+', ' ', i['msg'])
|
||||
for i in curse_msg['msgdict']]).split('\n'), key=len))
|
||||
except Exception:
|
||||
return 0
|
||||
@ -844,21 +900,21 @@ class _GlancesCurses(object):
|
||||
|
||||
class GlancesCursesStandalone(_GlancesCurses):
|
||||
|
||||
"""Class for the Glances' curse standalone"""
|
||||
"""Class for the Glances curse standalone."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class GlancesCursesClient(_GlancesCurses):
|
||||
|
||||
"""Class for the Glances' curse client"""
|
||||
"""Class for the Glances curse client."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class GlancesCursesBrowser(_GlancesCurses):
|
||||
|
||||
"""Class for the Glances' curse client browser"""
|
||||
"""Class for the Glances curse client browser."""
|
||||
|
||||
def __init__(self, args=None):
|
||||
# Init the father class
|
||||
@ -881,48 +937,44 @@ class GlancesCursesBrowser(_GlancesCurses):
|
||||
self.__refresh_time = args.time
|
||||
|
||||
# Init the cursor position for the client browser
|
||||
self.cursor_init()
|
||||
self.cursor_position = 0
|
||||
|
||||
# Active Glances server number
|
||||
self.set_active()
|
||||
self._active_server = None
|
||||
|
||||
def set_active(self, index=None):
|
||||
"""Set the active server or None if no server selected"""
|
||||
self.active_server = index
|
||||
return self.active_server
|
||||
@property
|
||||
def active_server(self):
|
||||
"""Return the active server or None if it's the browser list."""
|
||||
return self._active_server
|
||||
|
||||
def get_active(self):
|
||||
"""Return the active server (the one display in front) or None if it is the browser list"""
|
||||
return self.active_server
|
||||
@active_server.setter
|
||||
def active_server(self, index):
|
||||
"""Set the active server or None if no server selected."""
|
||||
self._active_server = index
|
||||
|
||||
def cursor_init(self):
|
||||
"""Init the cursor position to the top of the list"""
|
||||
return self.cursor_set(0)
|
||||
|
||||
def cursor_set(self, pos):
|
||||
"""Set the cursor position and return it"""
|
||||
self.cursor_position = pos
|
||||
@property
|
||||
def cursor(self):
|
||||
"""Get the cursor position."""
|
||||
return self.cursor_position
|
||||
|
||||
def cursor_get(self):
|
||||
"""Return the cursor position"""
|
||||
return self.cursor_position
|
||||
@cursor.setter
|
||||
def cursor(self, position):
|
||||
"""Set the cursor position."""
|
||||
self.cursor_position = position
|
||||
|
||||
def cursor_up(self, servers_list):
|
||||
"""Set the cursor to position N-1 in the list"""
|
||||
"""Set the cursor to position N-1 in the list."""
|
||||
if self.cursor_position > 0:
|
||||
self.cursor_position -= 1
|
||||
else:
|
||||
self.cursor_position = len(servers_list) - 1
|
||||
return self.cursor_position
|
||||
|
||||
def cursor_down(self, servers_list):
|
||||
"""Set the cursor to position N-1 in the list"""
|
||||
"""Set the cursor to position N-1 in the list."""
|
||||
if self.cursor_position < len(servers_list) - 1:
|
||||
self.cursor_position += 1
|
||||
else:
|
||||
self.cursor_position = 0
|
||||
return self.cursor_position
|
||||
|
||||
def __catch_key(self, servers_list):
|
||||
# Catch the browser pressed key
|
||||
@ -936,11 +988,10 @@ class GlancesCursesBrowser(_GlancesCurses):
|
||||
sys.exit(0)
|
||||
elif self.pressedkey == 10:
|
||||
# 'ENTER' > Run Glances on the selected server
|
||||
logger.debug("Server number %s selected" % (self.cursor_get() + 1))
|
||||
self.set_active(self.cursor_get())
|
||||
logger.debug("Server number {0} selected".format(self.cursor + 1))
|
||||
self.active_server = self.cursor
|
||||
elif self.pressedkey == 259:
|
||||
# 'UP' > Up in the server list
|
||||
logger
|
||||
self.cursor_up(servers_list)
|
||||
elif self.pressedkey == 258:
|
||||
# 'DOWN' > Down in the server list
|
||||
@ -957,6 +1008,7 @@ class GlancesCursesBrowser(_GlancesCurses):
|
||||
servers_list: Dict of dict with servers stats
|
||||
"""
|
||||
# Flush display
|
||||
logger.debug("Servers list: {}".format(servers_list))
|
||||
self.flush(servers_list)
|
||||
|
||||
# Wait
|
||||
@ -974,17 +1026,19 @@ class GlancesCursesBrowser(_GlancesCurses):
|
||||
# Wait 100ms...
|
||||
curses.napms(100)
|
||||
|
||||
return self.get_active()
|
||||
return self.active_server
|
||||
|
||||
def flush(self, servers_list):
|
||||
"""Update the servers' list screen.
|
||||
|
||||
servers_list: List of dict with servers stats
|
||||
"""
|
||||
self.erase()
|
||||
self.display(servers_list)
|
||||
|
||||
def display(self, servers_list):
|
||||
"""Display the servers list
|
||||
"""Display the servers list.
|
||||
|
||||
Return:
|
||||
True if the stats have been displayed
|
||||
False if the stats have not been displayed (no server available)
|
||||
@ -1003,17 +1057,16 @@ class GlancesCursesBrowser(_GlancesCurses):
|
||||
# Display top header
|
||||
if len(servers_list) == 0:
|
||||
if self.first_scan and not self.args.disable_autodiscover:
|
||||
msg = _("Glances is scanning your network (please wait)...")
|
||||
msg = 'Glances is scanning your network. Please wait...'
|
||||
self.first_scan = False
|
||||
else:
|
||||
msg = _("No Glances servers available")
|
||||
msg = 'No Glances server available'
|
||||
elif len(servers_list) == 1:
|
||||
msg = _("One Glances server available")
|
||||
msg = 'One Glances server available'
|
||||
else:
|
||||
msg = _("%d Glances servers available" %
|
||||
len(servers_list))
|
||||
msg = '{0} Glances servers available'.format(len(servers_list))
|
||||
if self.args.disable_autodiscover:
|
||||
msg += ' ' + _("(auto discover is disabled)")
|
||||
msg += ' ' + '(auto discover is disabled)'
|
||||
self.term_window.addnstr(y, x,
|
||||
msg,
|
||||
screen_x - x,
|
||||
@ -1023,41 +1076,39 @@ class GlancesCursesBrowser(_GlancesCurses):
|
||||
return False
|
||||
|
||||
# Display the Glances server list
|
||||
#================================
|
||||
# ================================
|
||||
|
||||
# Table of table
|
||||
# Item description: [stats_id, column name, column size]
|
||||
column_def = [
|
||||
['name', _('Name'), 16],
|
||||
['name', 'Name', 16],
|
||||
['alias', None, None],
|
||||
['load_min5', _('LOAD'), 6],
|
||||
['cpu_percent', _('CPU%'), 5],
|
||||
['mem_percent', _('MEM%'), 5],
|
||||
['status', _('STATUS'), 8],
|
||||
['ip', _('IP'), 15],
|
||||
# ['port', _('PORT'), 5],
|
||||
['hr_name', _('OS'), 16],
|
||||
['load_min5', 'LOAD', 6],
|
||||
['cpu_percent', 'CPU%', 5],
|
||||
['mem_percent', 'MEM%', 5],
|
||||
['status', 'STATUS', 8],
|
||||
['ip', 'IP', 15],
|
||||
# ['port', 'PORT', 5],
|
||||
['hr_name', 'OS', 16],
|
||||
]
|
||||
y = 2
|
||||
|
||||
# Display table header
|
||||
cpt = 0
|
||||
xc = x + 2
|
||||
for c in column_def:
|
||||
for cpt, c in enumerate(column_def):
|
||||
if xc < screen_x and y < screen_y and c[1] is not None:
|
||||
self.term_window.addnstr(y, xc,
|
||||
c[1],
|
||||
screen_x - x,
|
||||
self.colors_list['BOLD'])
|
||||
xc += c[2] + self.space_between_column
|
||||
cpt += 1
|
||||
y += 1
|
||||
|
||||
# If a servers has been deleted from the list...
|
||||
# ... and if the cursor is in the latest position
|
||||
if self.cursor_get() > len(servers_list) - 1:
|
||||
if self.cursor > len(servers_list) - 1:
|
||||
# Set the cursor position to the latest item
|
||||
self.cursor_set(len(servers_list) - 1)
|
||||
self.cursor = len(servers_list) - 1
|
||||
|
||||
# Display table
|
||||
line = 0
|
||||
@ -1075,7 +1126,7 @@ class GlancesCursesBrowser(_GlancesCurses):
|
||||
try:
|
||||
if c[0] == 'alias' and v[c[0]] is not None:
|
||||
server_stat['name'] = v[c[0]]
|
||||
except KeyError as e:
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
# Display line for server stats
|
||||
@ -1083,25 +1134,18 @@ class GlancesCursesBrowser(_GlancesCurses):
|
||||
xc = x
|
||||
|
||||
# Is the line selected ?
|
||||
if line == self.cursor_get():
|
||||
if line == self.cursor:
|
||||
# Display cursor
|
||||
self.term_window.addnstr(y, xc,
|
||||
">",
|
||||
screen_x - xc,
|
||||
self.colors_list['BOLD'])
|
||||
|
||||
# Display alias instead of name
|
||||
server_stat
|
||||
self.term_window.addnstr(
|
||||
y, xc, ">", screen_x - xc, self.colors_list['BOLD'])
|
||||
|
||||
# Display the line
|
||||
xc += 2
|
||||
for c in column_def:
|
||||
if xc < screen_x and y < screen_y and c[1] is not None:
|
||||
# Display server stats
|
||||
self.term_window.addnstr(y, xc,
|
||||
"%s" % server_stat[c[0]],
|
||||
c[2],
|
||||
self.colors_list[v['status']])
|
||||
self.term_window.addnstr(
|
||||
y, xc, format(server_stat[c[0]]), c[2], self.colors_list[v['status']])
|
||||
xc += c[2] + self.space_between_column
|
||||
cpt += 1
|
||||
# Next line, next server...
|
||||
|
@ -3,31 +3,48 @@ body {
|
||||
color: #BBB;
|
||||
font-family: "Lucida Sans Typewriter", "Lucida Console", Monaco, "Bitstream Vera Sans Mono", monospace;
|
||||
}
|
||||
.plugin {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.plugin table {
|
||||
|
||||
.table {
|
||||
display: table;
|
||||
width: 100%;
|
||||
}
|
||||
.plugin table tr td:not(:first-child) {
|
||||
.table-row-group {
|
||||
display: table-row-group
|
||||
}
|
||||
.table-row {
|
||||
display: table-row;
|
||||
}
|
||||
.table-cell {
|
||||
display: table-cell;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.underline{
|
||||
.plugin {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.plugin.table-row-group .table-row:last-child .table-cell {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.underline {
|
||||
text-decoration: underline
|
||||
}
|
||||
.bold{
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.sort{
|
||||
.sort {
|
||||
font-weight: bold;
|
||||
}
|
||||
.sort:after{
|
||||
content: '\25BC'
|
||||
color: white;
|
||||
}
|
||||
.text-right {
|
||||
text-align: right;
|
||||
}
|
||||
.text-left {
|
||||
text-align: left;
|
||||
}
|
||||
.sidebar .table-cell:not(.text-left) {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
/* Theme */
|
||||
|
||||
@ -35,6 +52,10 @@ body {
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
}
|
||||
.highlight {
|
||||
font-weight: bold;
|
||||
color: magenta;
|
||||
}
|
||||
.ok {
|
||||
color: green;
|
||||
}
|
||||
@ -53,10 +74,10 @@ body {
|
||||
color: white;
|
||||
}
|
||||
.warning {
|
||||
color: orange;
|
||||
color: magenta;
|
||||
}
|
||||
.warning_log {
|
||||
background-color: orange;
|
||||
background-color: magenta;
|
||||
color: white;
|
||||
}
|
||||
.critical {
|
||||
@ -77,25 +98,85 @@ body {
|
||||
}
|
||||
|
||||
/* Plugins */
|
||||
#cpu table tr td:nth-child(3),
|
||||
#mem table tr td:nth-child(3),
|
||||
#monitor table tr td:nth-child(3) {
|
||||
text-align: left;
|
||||
padding-left: 20px;
|
||||
}
|
||||
#processlist table tr td {
|
||||
text-align: right;
|
||||
}
|
||||
#processlist table tr td,
|
||||
#docker table tr td {
|
||||
#processlist .table-cell, #containers .table-cell {
|
||||
padding: 0px 5px 0px 5px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#processlist table tr td:nth-child(6),
|
||||
#processlist table tr td:nth-child(12) {
|
||||
gl-monitor-list {
|
||||
display: block;
|
||||
}
|
||||
gl-monitor-list .table-cell {
|
||||
text-align: left;
|
||||
}
|
||||
#docker table tr td:nth-child(2),
|
||||
#docker table tr td:nth-child(6) {
|
||||
text-align: left;
|
||||
/* Loading page */
|
||||
|
||||
#loading-page .glances-logo {
|
||||
background: url('glances.png') no-repeat center center;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
@media (max-width: 750px) {
|
||||
#loading-page .glances-logo {
|
||||
height: 400px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 750px) {
|
||||
#loading-page .glances-logo {
|
||||
height: 500px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Loading animation
|
||||
source : https://github.com/lukehaas/css-loaders
|
||||
*/
|
||||
#loading-page .loader:before,
|
||||
#loading-page .loader:after,
|
||||
#loading-page .loader {
|
||||
border-radius: 50%;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
-webkit-animation-fill-mode: both;
|
||||
animation-fill-mode: both;
|
||||
-webkit-animation: loader 1.8s infinite ease-in-out;
|
||||
animation: loader 1.8s infinite ease-in-out;
|
||||
}
|
||||
#loading-page .loader {
|
||||
margin: auto;
|
||||
font-size: 10px;
|
||||
position: relative;
|
||||
text-indent: -9999em;
|
||||
-webkit-animation-delay: 0.16s;
|
||||
animation-delay: 0.16s;
|
||||
}
|
||||
#loading-page .loader:before {
|
||||
left: -3.5em;
|
||||
}
|
||||
#loading-page .loader:after {
|
||||
left: 3.5em;
|
||||
-webkit-animation-delay: 0.32s;
|
||||
animation-delay: 0.32s;
|
||||
}
|
||||
#loading-page .loader:before,
|
||||
#loading-page .loader:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
@-webkit-keyframes loader {
|
||||
0%, 80%, 100% {
|
||||
box-shadow: 0 2.5em 0 -1.3em #56CA69;
|
||||
}
|
||||
40% {
|
||||
box-shadow: 0 2.5em 0 0 #56CA69;
|
||||
}
|
||||
}
|
||||
@keyframes loader {
|
||||
0%, 80%, 100% {
|
||||
box-shadow: 0 2.5em 0 -1.3em #56CA69;
|
||||
}
|
||||
40% {
|
||||
box-shadow: 0 2.5em 0 0 #56CA69;
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
<div class="table-cell {{ descriptionClass }}">{{ process.description }}</div>
|
||||
<div class="table-cell">{{ process.count > 1 ? process.count : '' }}</div>
|
||||
<div class="table-cell">{{ process.count > 0 ? 'RUNNING' : 'NOT RUNNING' }}</div>
|
||||
<div class="table-cell">{{ process.result }}</div>
|
63
glances/outputs/static/html/help.html
Normal file
@ -0,0 +1,63 @@
|
||||
<div class="row">
|
||||
<div class="col-sm-2 col-lg-10">{{help.version}} {{help.psutil_version}}</div>
|
||||
</div>
|
||||
<div class="row"> </div>
|
||||
<div class="row">
|
||||
<div class="col-sm-2 col-lg-10">{{help.configuration_file}}</div>
|
||||
</div>
|
||||
<div class="row"> </div>
|
||||
<div class="row">
|
||||
<div class="col-sm-2 col-lg-3">{{help.sort_auto}}</div>
|
||||
<div class="col-sm-2 col-lg-3">{{help.sort_network}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-2 col-lg-3">{{help.sort_cpu}}</div>
|
||||
<div class="col-sm-2 col-lg-3">{{help.show_hide_alert}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-2 col-lg-3">{{help.show_mem}}</div>
|
||||
<div class="col-sm-2 col-lg-3">{{help.delete_warning_alerts}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-2 col-lg-3">{{help.sort_proc}}</div>
|
||||
<div class="col-sm-2 col-lg-3">{{help.delete_warning_critical_alerts}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-2 col-lg-3">{{help.sort_io}}</div>
|
||||
<div class="col-sm-2 col-lg-3">{{help.percpu}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-2 col-lg-3">{{help.show_hide_help}}</div>
|
||||
<div class="col-sm-2 col-lg-3">{{help.show_hide_diskio}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-2 col-lg-3">{{help.view_network_io_combination}}</div>
|
||||
<div class="col-sm-2 col-lg-3">{{help.view_cumulative_network}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-2 col-lg-3">{{help.show_hide_network}}</div>
|
||||
<div class="col-sm-2 col-lg-3">{{help.show_hide_filesytem_freespace}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-2 col-lg-3">{{help.show_hide_sensors}}</div>
|
||||
<div class="col-sm-2 col-lg-3">{{help.generate_graphs}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-2 col-lg-3">{{help.show_hide_left_sidebar}}</div>
|
||||
<div class="col-sm-2 col-lg-3">{{help.reset_history}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-2 col-lg-3">{{help.enable_disable_process_stats}}</div>
|
||||
<div class="col-sm-2 col-lg-3">{{help.quit}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-2 col-lg-3">{{help.enable_disable_top_extends_stats}}</div>
|
||||
<div class="col-sm-2 col-lg-3">{{help.enable_disable_short_processname}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-2 col-lg-3">{{help.enable_disable_docker}}</div>
|
||||
</div>
|
||||
<div class="row"> </div>
|
||||
<div class="row">
|
||||
<div class="col-sm-2 col-lg-3">{{help.edit_pattern_filter}}</div>
|
||||
</div>
|
28
glances/outputs/static/html/index.html
Normal file
@ -0,0 +1,28 @@
|
||||
<!DOCTYPE html>
|
||||
<html ng-app="glancesApp">
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Glances</title>
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||
<link rel="stylesheet" type="text/css" href="normalize.css" />
|
||||
<link rel="stylesheet" type="text/css" href="bootstrap.min.css" />
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
|
||||
<script type="text/javascript" src="vendors/angular.min.js"></script>
|
||||
<script type="text/javascript" src="vendors/angular-route.min.js"></script>
|
||||
<script type="text/javascript" src="vendors/lodash.min.js"></script>
|
||||
<script type="text/javascript" src="app.js"></script>
|
||||
<script type="text/javascript" src="filters.js"></script>
|
||||
<script type="text/javascript" src="variables.js"></script>
|
||||
<script type="text/javascript" src="directives.js"></script>
|
||||
|
||||
<script type="text/javascript" src="stats_controller.js"></script>
|
||||
</head>
|
||||
|
||||
<body ng-view="" ng-keydown="onKeyDown($event)">
|
||||
|
||||
</body>
|
||||
</html>
|
7
glances/outputs/static/html/plugins/alert.html
Normal file
@ -0,0 +1,7 @@
|
||||
<div class="table">
|
||||
<div class="table-row" ng-repeat="alert in result['alert']">
|
||||
<div class="table-cell text-left">
|
||||
{{alert.begin | date : 'yyyy-MM-dd H:mm:ss'}} ({{ alert.ongoing ? 'ongoing' : alert.duration }}) - {{alert[2]}} on <span class="{{ alert[2] | lowercase }}">{{alert[3]}}</span> ({{alert[4]}})
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
2
glances/outputs/static/html/plugins/alerts.html
Normal file
@ -0,0 +1,2 @@
|
||||
<span class="title" ng-show="!result['alert'].length">No warning or critical alert detected</span>
|
||||
<span class="title" ng-show="result['alert'].length">Warning or critical alerts (lasts {{result['alert'].length}} entries)</span>
|
44
glances/outputs/static/html/plugins/cpu.html
Normal file
@ -0,0 +1,44 @@
|
||||
<div class="table" ng-show="!show.per_cpu">
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left title">CPU</div>
|
||||
<div class="table-cell">{{result["cpu"].total}}%</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left">user:</div>
|
||||
<div class="table-cell" ng-class="getAlertLog('cpu', 'cpu_user_', result['cpu'].user)">
|
||||
{{result["cpu"].user}}%
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left">system:</div>
|
||||
<div class="table-cell" ng-class="getAlertLog('cpu', 'cpu_system_', result['cpu'].system)">
|
||||
{{result["cpu"].system}}%
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left">idle:</div>
|
||||
<div class="table-cell">{{result["cpu"].idle}}%</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table" ng-show="show.per_cpu">
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left title">PER CPU</div>
|
||||
<div class="table-cell" ng-repeat="percpu in result.percpu">{{percpu.total}}%</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left">user:</div>
|
||||
<div class="table-cell" ng-repeat="percpu in result.percpu" ng-class="getAlert('percpu', 'percpu_user_', percpu.user)">
|
||||
{{percpu.user}}%
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left">system:</div>
|
||||
<div class="table-cell" ng-repeat="percpu in result.percpu" ng-class="getAlert('percpu', 'percpu_system_', percpu.system)">
|
||||
{{percpu.system}}%
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left">idle:</div>
|
||||
<div class="table-cell" ng-repeat="percpu in result.percpu">{{percpu.idle}}%</div>
|
||||
</div>
|
||||
</div>
|
26
glances/outputs/static/html/plugins/cpu_more.html
Normal file
@ -0,0 +1,26 @@
|
||||
<div class="table">
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left">nice:</div>
|
||||
<div class="table-cell">
|
||||
{{result["cpu"].nice}}%
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-row" ng-if="result['cpu'].irq != undefined">
|
||||
<div class="table-cell text-left">irq:</div>
|
||||
<div class="table-cell">
|
||||
{{result["cpu"].irq}}%
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-row" ng-if="result['cpu'].iowait != undefined">
|
||||
<div class="table-cell text-left">iowait:</div>
|
||||
<div class="table-cell" ng-class="getAlertLog('cpu', 'cpu_iowait_', result['cpu'].iowait)">
|
||||
{{result["cpu"].iowait}}%
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-row" ng-if="result['cpu'].steal != undefined">
|
||||
<div class="table-cell text-left">steal:</div>
|
||||
<div class="table-cell" ng-class="getAlert('cpu', 'cpu_steal_', result['cpu'].steal)">
|
||||
{{result["cpu"].steal}}%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
10
glances/outputs/static/html/plugins/diskio.html
Normal file
@ -0,0 +1,10 @@
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left title">DISK I/O</div>
|
||||
<div class="table-cell">R/s</div>
|
||||
<div class="table-cell">W/s</div>
|
||||
</div>
|
||||
<div class="table-row" ng-repeat="disk in result['diskio'] | orderBy: 'disk_name'">
|
||||
<div class="table-cell text-left">{{disk.disk_name | min_size}}</div>
|
||||
<div class="table-cell">{{disk.read_bytes | bytes}}</div>
|
||||
<div class="table-cell">{{disk.write_bytes | bytes}}</div>
|
||||
</div>
|
20
glances/outputs/static/html/plugins/docker.html
Normal file
@ -0,0 +1,20 @@
|
||||
<span class="title">CONTAINERS</span> {{ result['docker']['containers'].length }} (served by Docker {{ result['docker']['version']['Version'] }})
|
||||
|
||||
<div class="table">
|
||||
<div class="table-row">
|
||||
<div class="table-cell">Id</div>
|
||||
<div class="table-cell text-left">Name</div>
|
||||
<div class="table-cell">Status</div>
|
||||
<div class="table-cell">CPU%</div>
|
||||
<div class="table-cell">MEM</div>
|
||||
<div class="table-cell text-left">Command</div>
|
||||
</div>
|
||||
<div class="table-row" ng-repeat="container in result['docker']['containers']">
|
||||
<div class="table-cell">{{ container.Id | limitTo:12 }}</div>
|
||||
<div class="table-cell text-left">{{ container.Names[0] }}</div>
|
||||
<div class="table-cell" ng-class="container.Status == 'Paused' ? 'careful' : 'ok'">{{ container.Status }}</div>
|
||||
<div class="table-cell">{{ container.cpu.total | number:1 }}</div>
|
||||
<div class="table-cell">{{ container.memory.usage | bytes }}</div>
|
||||
<div class="table-cell text-left">{{ container.Command }}</div>
|
||||
</div>
|
||||
</div>
|
12
glances/outputs/static/html/plugins/fs.html
Normal file
@ -0,0 +1,12 @@
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left title">FILE SYS</div>
|
||||
<div class="table-cell">Used</div>
|
||||
<div class="table-cell">Total</div>
|
||||
</div>
|
||||
<div class="table-row" ng-repeat="fs in result['fs'] | orderBy: 'mnt_point'">
|
||||
<div class="table-cell text-left">{{fs.mnt_point}} ({{fs.device_name}})</div>
|
||||
<div class="table-cell" ng-class="getAlert('fs', 'fs_', fs.percent)">
|
||||
{{fs.size - fs.free | bytes}}
|
||||
</div>
|
||||
<div class="table-cell">{{fs.size | bytes}}</div>
|
||||
</div>
|
1
glances/outputs/static/html/plugins/ip.html
Normal file
@ -0,0 +1 @@
|
||||
- <span class="title">IP</span> <span>{{result["ip"].address}}/{{result["ip"].mask_cidr}}</span>
|
24
glances/outputs/static/html/plugins/load.html
Normal file
@ -0,0 +1,24 @@
|
||||
<div class="table">
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left title">LOAD</div>
|
||||
<div class="table-cell">{{result["load"].cpucore}}-core</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left">1 min:</div>
|
||||
<div class="table-cell">
|
||||
{{result["load"].min1 | number : 2}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left">5 min:</div>
|
||||
<div class="table-cell" ng-class="getAlert('load', 'load_', result['load'].min5, 100 * result['load'].cpucore)">
|
||||
{{result["load"].min5 | number : 2}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left">15 min:</div>
|
||||
<div class="table-cell" ng-class="getAlertLog('load', 'load_', result['load'].min15, 100 * result['load'].cpucore)">
|
||||
{{result["load"].min15 | number : 2}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
20
glances/outputs/static/html/plugins/mem.html
Normal file
@ -0,0 +1,20 @@
|
||||
<div class="table">
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left title">MEM</div>
|
||||
<div class="table-cell">{{result["mem"].percent}}%</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left">total:</div>
|
||||
<div class="table-cell">{{result["mem"].total | bytes}}</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left">used:</div>
|
||||
<div class="table-cell" ng-class="getAlertLog('mem', 'mem_', result['mem'].percent)">
|
||||
{{result["mem"].used | bytes:2}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left">free:</div>
|
||||
<div class="table-cell">{{result["mem"].free | bytes}}</div>
|
||||
</div>
|
||||
</div>
|
18
glances/outputs/static/html/plugins/mem_more.html
Normal file
@ -0,0 +1,18 @@
|
||||
<div class="table">
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left">active:</div>
|
||||
<div class="table-cell">{{result["mem"].active | bytes}}</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left">inactive:</div>
|
||||
<div class="table-cell">{{result["mem"].inactive | bytes}}</div>
|
||||
</div>
|
||||
<div class="table-row" ng-if="result['mem'].buffers != undefined">
|
||||
<div class="table-cell text-left">buffers:</div>
|
||||
<div class="table-cell">{{result["mem"].buffers | bytes}}</div>
|
||||
</div>
|
||||
<div class="table-row" ng-if="result['mem'].cached != undefined">
|
||||
<div class="table-cell text-left">cached:</div>
|
||||
<div class="table-cell">{{result["mem"].cached | bytes}}</div>
|
||||
</div>
|
||||
</div>
|
20
glances/outputs/static/html/plugins/memswap.html
Normal file
@ -0,0 +1,20 @@
|
||||
<div class="table">
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left title">SWAP</div>
|
||||
<div class="table-cell">{{result["memswap"].percent}}%</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left">total:</div>
|
||||
<div class="table-cell">{{result["memswap"].total | bytes}}</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left">used:</div>
|
||||
<div class="table-cell" ng-class="getAlertLog('memswap', 'memswap_', result['memswap'].percent)">
|
||||
{{result["memswap"].used | bytes}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left">free:</div>
|
||||
<div class="table-cell">{{result["memswap"].free | bytes}}</div>
|
||||
</div>
|
||||
</div>
|
3
glances/outputs/static/html/plugins/monitor.html
Normal file
@ -0,0 +1,3 @@
|
||||
<div class="table">
|
||||
<gl-monitor-process class="table-row" ng-repeat="process in processes" process="process"></gl-monitor-process>
|
||||
</div>
|
12
glances/outputs/static/html/plugins/network.html
Normal file
@ -0,0 +1,12 @@
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left title">NETWORK</div>
|
||||
<div class="table-cell">Rx/s</div>
|
||||
<div class="table-cell">Tx/s</div>
|
||||
</div>
|
||||
<div class="table-row" ng-repeat="network in result['network'] | orderBy: 'interface_name'">
|
||||
<div class="table-cell text-left">{{network.interface_name | min_size}}</div>
|
||||
<div class="table-cell" ng-if="show.network_by_bytes">{{network.rx | bytes}}</div>
|
||||
<div class="table-cell" ng-if="show.network_by_bytes">{{network.tx | bytes}}</div>
|
||||
<div class="table-cell" ng-if="!show.network_by_bytes">{{network.rx | bits}}</div>
|
||||
<div class="table-cell" ng-if="!show.network_by_bytes">{{network.tx | bits}}</div>
|
||||
</div>
|
6
glances/outputs/static/html/plugins/processcount.html
Normal file
@ -0,0 +1,6 @@
|
||||
<span class="title">TASKS</span>
|
||||
<span>{{result["processcount"].total}} ({{result["processcount"].thread}} thr),</span>
|
||||
<span>{{result["processcount"].running}} run,</span>
|
||||
<span>{{result["processcount"].sleeping}} slp,</span>
|
||||
<span>{{result["processcount"].stopped}} oth</span>
|
||||
<span> sorted {{ sorter.auto ? 'automatically' : '' }} by {{ sorter.getColumnLabel(sorter.column) }}, flat view</span>
|
33
glances/outputs/static/html/plugins/processlist.html
Normal file
@ -0,0 +1,33 @@
|
||||
<div class="table">
|
||||
<div class="table-row">
|
||||
<div sortable-th sorter="sorter" column="cpu_percent" class="table-cell">CPU%</div>
|
||||
<div sortable-th sorter="sorter" column="memory_percent" class="table-cell">MEM%</div>
|
||||
<div class="table-cell hidden-xs hidden-sm">VIRT</div>
|
||||
<div class="table-cell hidden-xs hidden-sm">RES</div>
|
||||
<div class="table-cell">PID</div>
|
||||
<div sortable-th sorter="sorter" column="username" class="table-cell text-left">USER</div>
|
||||
<div class="table-cell">NI</div>
|
||||
<div class="table-cell">S</div>
|
||||
<div sortable-th sorter="sorter" column="timemillis" class="table-cell hidden-xs hidden-sm">TIME+</div>
|
||||
<div sortable-th sorter="sorter" column="io_read" class="table-cell hidden-xs hidden-sm">IOR/s</div>
|
||||
<div sortable-th sorter="sorter" column="io_write" class="table-cell hidden-xs hidden-sm">IOW/s</div>
|
||||
<div sortable-th sorter="sorter" column="name" class="table-cell text-left">Command</div>
|
||||
</div>
|
||||
<div class="table-row" ng-repeat="process in result['processlist'] | orderBy:sorter.column:sorter.isReverseColumn(sorter.column)">
|
||||
<div class="table-cell" ng-class="getAlert('processlist', 'processlist_cpu_', process.cpu_percent)">{{process.cpu_percent | number:1}}</div>
|
||||
<div class="table-cell" ng-class="getAlert('processlist', 'processlist_mem_', process.memory_percent)">{{process.memory_percent | number:1}}</div>
|
||||
<div class="table-cell hidden-xs hidden-sm">{{process.memvirt | bytes}}</div>
|
||||
<div class="table-cell hidden-xs hidden-sm">{{process.memres | bytes}}</div>
|
||||
<div class="table-cell">{{process.pid}}</div>
|
||||
<div class="table-cell text-left">{{process.username}}</div>
|
||||
<div class="table-cell" ng-class="{nice: isNice(process.nice)}">{{process.nice | exclamation}}</div>
|
||||
<div class="table-cell" ng-class="{status: process.status == 'R'}">{{process.status}}</div>
|
||||
<div class="table-cell hidden-xs hidden-sm">
|
||||
<span ng-show="process.timeplus.hours > 0" class="highlight">{{ process.timeplus.hours }}h</span>{{ process.timeplus.minutes | leftPad:2:'0' }}:{{ process.timeplus.seconds | leftPad:2:'0' }}<span ng-show="process.timeplus.hours <= 0">.{{ process.timeplus.milliseconds | leftPad:2:'0' }}</span>
|
||||
</div>
|
||||
<div class="table-cell hidden-xs hidden-sm">{{process.io_read}}</div>
|
||||
<div class="table-cell hidden-xs hidden-sm">{{process.io_write}}</div>
|
||||
<div class="table-cell text-left" ng-if="show.short_process_name">{{process.name}}</div>
|
||||
<div class="table-cell text-left" ng-if="!show.short_process_name">{{process.cmdline}}</div>
|
||||
</div>
|
||||
</div>
|
13
glances/outputs/static/html/plugins/sensors.html
Normal file
@ -0,0 +1,13 @@
|
||||
<div class="table-row">
|
||||
<div class="table-cell text-left title">SENSORS</div>
|
||||
</div>
|
||||
<div class="table-row" ng-repeat="sensor in result['sensors']">
|
||||
<div class="table-cell text-left">{{ sensor.label }}</div>
|
||||
<div class="table-cell">{{ sensor.unit }}</div>
|
||||
<div class="table-cell" ng-if="sensor.type != 'battery'" ng-class="getAlert('sensors', 'sensors_' + sensor.type + '_', sensor.value)">
|
||||
{{ sensor.value }}
|
||||
</div>
|
||||
<div class="table-cell" ng-if="sensor.type == 'battery'" ng-class="getAlert('sensors', 'sensors_' + sensor.type + '_', 100 - sensor.value)">
|
||||
{{ sensor.value }}
|
||||
</div>
|
||||
</div>
|
3
glances/outputs/static/html/plugins/system.html
Normal file
@ -0,0 +1,3 @@
|
||||
<span class="title">{{ result["system"].hostname }}</span>
|
||||
<span ng-if="is_linux" class="hidden-xs hidden-sm">({{ result["system"].hr_name }} / {{ result["system"].os_name }} {{ result["system"].os_version }})</span>
|
||||
<span ng-if="!is_linux" class="hidden-xs hidden-sm">({{ result["system"].os_name }} {{ result["system"].os_version }} {{ result["system"].platform }})</span>
|
1
glances/outputs/static/html/plugins/uptime.html
Normal file
@ -0,0 +1 @@
|
||||
<span>Uptime: {{result["uptime"]}}</span>
|
64
glances/outputs/static/html/stats.html
Normal file
@ -0,0 +1,64 @@
|
||||
<div ng-show="!result" class="container-fluid" id="loading-page">
|
||||
<div class="glances-logo"></div>
|
||||
<div class="loader">Loading...</div>
|
||||
</div>
|
||||
|
||||
<div ng-show="help_screen" class="container-fluid" ng-include src="'help.html'"></div>
|
||||
|
||||
<div ng-show="result && !help_screen" class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="pull-left">
|
||||
<section id="system" class="plugin" ng-include src="'plugins/system.html'"></section>
|
||||
</div>
|
||||
<div class="pull-left">
|
||||
<section id="ip" class="plugin" ng-if="result['ip'].address != undefined" ng-include src="'plugins/ip.html'"></section>
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
<section id="uptime" class="plugin" ng-include src="'plugins/uptime.html'"></section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-3 col-md-2">
|
||||
<section id="cpu" class="plugin" ng-include src="'plugins/cpu.html'"></section>
|
||||
</div>
|
||||
<div class="hidden-xs hidden-sm col-md-2">
|
||||
<section id="cpu_more" class="plugin" ng-if="result['cpu'].nice != undefined" ng-include src="'plugins/cpu_more.html'"></section>
|
||||
</div>
|
||||
<div class="col-sm-3 col-md-2">
|
||||
<section id="load" class="plugin" ng-if="result['load'].cpucore != undefined" ng-include src="'plugins/load.html'"></section>
|
||||
</div>
|
||||
<div class="col-sm-3 col-md-2">
|
||||
<section id="mem" class="plugin" ng-include src="'plugins/mem.html'"></section>
|
||||
</div>
|
||||
<div class="hidden-xs hidden-sm col-md-2">
|
||||
<section id="mem_more" class="plugin" ng-include src="'plugins/mem_more.html'"></section>
|
||||
</div>
|
||||
<div class="col-sm-3 col-md-2">
|
||||
<section id="memswap" class="plugin" ng-include src="'plugins/memswap.html'"></section>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-3 sidebar" ng-show="show.sidebar">
|
||||
<div class="table">
|
||||
<section id="network" class="plugin table-row-group" ng-show="show.network" ng-include src="'plugins/network.html'"></section>
|
||||
<section id="diskio" class="plugin table-row-group" ng-show="show.diskio" ng-include src="'plugins/diskio.html'"></section>
|
||||
<section id="fs" class="plugin table-row-group" ng-show="show.fs" ng-include src="'plugins/fs.html'"></section>
|
||||
<section id="sensors" class="plugin table-row-group" ng-show="show.sensors && result['sensors'].length > 0" ng-include src="'plugins/sensors.html'"></section>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<section id="containers" class="plugin" ng-if="result['docker']['containers'].length" ng-include src="'plugins/docker.html'"></section>
|
||||
<section id="alerts" ng-show="show.alert" ng-include src="'plugins/alerts.html'"></section>
|
||||
<section id="alert" class="plugin" ng-show="show.alert" ng-include src="'plugins/alert.html'"></section>
|
||||
<section id="processcount" class="plugin" ng-include src="'plugins/processcount.html'"></section>
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<gl-monitor-list class="plugin" processes="result['monitor']"></gl-monitor-list>
|
||||
</div>
|
||||
</div>
|
||||
<section id="processlist" class="plugin" ng-include src="'plugins/processlist.html'"></section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
BIN
glances/outputs/static/images/glances.png
Normal file
After Width: | Height: | Size: 43 KiB |
13
glances/outputs/static/js/app.js
Normal file
@ -0,0 +1,13 @@
|
||||
var glancesApp = angular.module('glancesApp', ['ngRoute'])
|
||||
|
||||
.config(function($routeProvider, $locationProvider) {
|
||||
$routeProvider.when('/', {
|
||||
templateUrl : 'stats.html',
|
||||
controller : 'statsController'
|
||||
}).when('/:refresh_time', {
|
||||
templateUrl : 'stats.html',
|
||||
controller : 'statsController'
|
||||
});
|
||||
|
||||
$locationProvider.html5Mode(true);
|
||||
});
|
78
glances/outputs/static/js/directives.js
Normal file
@ -0,0 +1,78 @@
|
||||
glancesApp.directive("sortableTh", function() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: {
|
||||
sorter: '='
|
||||
},
|
||||
link: function (scope, element, attrs) {
|
||||
|
||||
scope.$watch(function() {
|
||||
return scope.sorter.column;
|
||||
}, function(newValue, oldValue) {
|
||||
|
||||
if (angular.isArray(newValue)) {
|
||||
if (newValue.indexOf(attrs.column) !== -1) {
|
||||
element.addClass('sort');
|
||||
} else {
|
||||
element.removeClass('sort');
|
||||
}
|
||||
} else {
|
||||
if (attrs.column === newValue) {
|
||||
element.addClass('sort');
|
||||
} else {
|
||||
element.removeClass('sort');
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
element.on('click', function() {
|
||||
|
||||
scope.sorter.column = attrs.column;
|
||||
|
||||
scope.$apply();
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
glancesApp.directive("glMonitorList", function() {
|
||||
return {
|
||||
restrict: 'AE',
|
||||
scope: {
|
||||
processes: '='
|
||||
},
|
||||
templateUrl: 'plugins/monitor.html',
|
||||
controller: function() {
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
glancesApp.directive("glMonitorProcess", function() {
|
||||
return {
|
||||
restrict: 'AE',
|
||||
require: "^glMonitorList",
|
||||
templateUrl: 'components/monitor_process.html',
|
||||
scope: {
|
||||
process: '='
|
||||
},
|
||||
link: function(scope, element, attrs) {
|
||||
|
||||
count = scope.process.count;
|
||||
countMin = scope.process.countmin;
|
||||
countMax = scope.process.countmax;
|
||||
|
||||
if (count > 0) {
|
||||
if ((countMin == null || count >= countMin) && (countMax == null || count <= countMax)) {
|
||||
scope.descriptionClass = 'ok';
|
||||
} else {
|
||||
scope.descriptionClass = 'careful';
|
||||
}
|
||||
} else {
|
||||
scope.descriptionClass = countMin == null ? 'ok' : 'critical';
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
});
|
86
glances/outputs/static/js/filters.js
Normal file
@ -0,0 +1,86 @@
|
||||
glancesApp.filter('min_size', function() {
|
||||
return function(input) {
|
||||
var max = 8;
|
||||
if (input.length > max) {
|
||||
return "_" + input.substring(input.length - max)
|
||||
}
|
||||
return input
|
||||
};
|
||||
});
|
||||
glancesApp.filter('exclamation', function() {
|
||||
return function(input) {
|
||||
if (input === undefined || input === '') {
|
||||
return '?';
|
||||
}
|
||||
return input;
|
||||
};
|
||||
});
|
||||
|
||||
glancesApp.filter('bytes', function() {
|
||||
return function (bytes, low_precision) {
|
||||
low_precision = low_precision || false;
|
||||
if (isNaN(parseFloat(bytes)) || !isFinite(bytes) || bytes == 0){
|
||||
return '0';
|
||||
}
|
||||
|
||||
var symbols = ['K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'];
|
||||
var prefix = {
|
||||
'Y': 1208925819614629174706176,
|
||||
'Z': 1180591620717411303424,
|
||||
'E': 1152921504606846976,
|
||||
'P': 1125899906842624,
|
||||
'T': 1099511627776,
|
||||
'G': 1073741824,
|
||||
'M': 1048576,
|
||||
'K': 1024
|
||||
};
|
||||
|
||||
var reverseSymbols = _(symbols).reverse().value();
|
||||
for (var i = 0; i < reverseSymbols.length; i++) {
|
||||
var symbol = reverseSymbols[i];
|
||||
var value = bytes / prefix[symbol];
|
||||
|
||||
if(value > 1) {
|
||||
var decimal_precision = 0;
|
||||
|
||||
if(value < 10) {
|
||||
decimal_precision = 2;
|
||||
}
|
||||
else if(value < 100) {
|
||||
decimal_precision = 1;
|
||||
}
|
||||
|
||||
if(low_precision) {
|
||||
if(symbol == 'MK') {
|
||||
decimal_precision = 0;
|
||||
}
|
||||
else {
|
||||
decimal_precision = _.min([1, decimal_precision]);
|
||||
}
|
||||
}
|
||||
else if(symbol == 'K') {
|
||||
decimal_precision = 0;
|
||||
}
|
||||
|
||||
return parseFloat(value).toFixed(decimal_precision) + symbol;
|
||||
}
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
});
|
||||
|
||||
glancesApp.filter('bits', function($filter) {
|
||||
return function (bits, low_precision) {
|
||||
bits = Math.round(bits) * 8;
|
||||
return $filter('bytes')(bits, low_precision) + 'b';
|
||||
}
|
||||
});
|
||||
|
||||
glancesApp.filter('leftPad', function($filter) {
|
||||
return function (value, length, chars) {
|
||||
length = length || 0;
|
||||
chars = chars || ' ';
|
||||
return _.padLeft(value, length, chars);
|
||||
}
|
||||
});
|
294
glances/outputs/static/js/stats_controller.js
Normal file
@ -0,0 +1,294 @@
|
||||
glancesApp.controller('statsController', function($scope, $http, $interval, $q, $routeParams, $filter) {
|
||||
|
||||
$scope.limitSuffix = ['critical', 'careful', 'warning'];
|
||||
$scope.refreshTime = 3;
|
||||
$scope.pluginLimits = [];
|
||||
$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.help_screen = false;
|
||||
$scope.show = {
|
||||
'diskio' : true,
|
||||
'network' : true,
|
||||
'fs' : true,
|
||||
'sensors' : true,
|
||||
'sidebar' : true,
|
||||
'alert' : true,
|
||||
'short_process_name': true,
|
||||
'per_cpu': false,
|
||||
'warning_alerts':true,
|
||||
'warning_critical_alerts':true,
|
||||
'process_stats':true,
|
||||
'top_extended_stats':true,
|
||||
'docker_stats':true,
|
||||
'network_io_combination':false,
|
||||
'network_io_cumulative':false,
|
||||
'filesystem_freespace':false,
|
||||
'network_by_bytes':true
|
||||
};
|
||||
|
||||
$scope.init_refresh_time = function() {
|
||||
if ($routeParams != undefined && $routeParams.refresh_time != undefined) {
|
||||
var new_refresh_time = parseInt($routeParams.refresh_time)
|
||||
if (new_refresh_time >= 1) {
|
||||
$scope.refreshTime = new_refresh_time
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$scope.init_limits = function() {
|
||||
$http.get('/api/2/all/limits').success(function(response, status, headers, config) {
|
||||
$scope.pluginLimits = response
|
||||
}).error(function(response, status, headers, config) {
|
||||
console.log('error : ' + response+ status + headers + config);
|
||||
});
|
||||
}
|
||||
|
||||
$scope.init_help = function() {
|
||||
$http.get('/api/2/help').success(function(response, status, headers, config) {
|
||||
$scope.help = response
|
||||
});
|
||||
}
|
||||
|
||||
$scope.show_hide = function(bloc) {
|
||||
if(bloc == 'help') {
|
||||
$scope.help_screen = !$scope.help_screen
|
||||
} else {
|
||||
$scope.show[bloc] = !$scope.show[bloc]
|
||||
}
|
||||
}
|
||||
|
||||
var canceler = undefined;
|
||||
|
||||
/**
|
||||
* Refresh all the data of the view
|
||||
*/
|
||||
$scope.refreshData = function() {
|
||||
canceler = $q.defer();
|
||||
$http.get('/api/2/all', {timeout: canceler.promise}).success(function(response, status, headers, config) {
|
||||
|
||||
function timemillis(array) {
|
||||
var sum = 0.0
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
sum += array[i] * 1000.0;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
function timedelta(input) {
|
||||
var sum = timemillis(input);
|
||||
var d = new Date(sum);
|
||||
|
||||
return {
|
||||
hours: d.getUTCHours(), // TODO : multiple days ( * (d.getDay() * 24)))
|
||||
minutes: d.getUTCMinutes(),
|
||||
seconds: d.getUTCSeconds(),
|
||||
milliseconds: parseInt("" + d.getUTCMilliseconds() / 10)
|
||||
};
|
||||
};
|
||||
|
||||
function durationBetweenTwoDates(startDate, endDate) {
|
||||
var duration = endDate - startDate;
|
||||
var seconds = parseInt((duration/1000)%60)
|
||||
, minutes = parseInt((duration/(1000*60))%60)
|
||||
, hours = parseInt((duration/(1000*60*60))%24);
|
||||
|
||||
return _.padLeft(hours,2,'0') + ":" + _.padLeft(minutes,2,'0') + ":" + _.padLeft(seconds,2,'0');
|
||||
}
|
||||
|
||||
for (var i = 0; i < response['processlist'].length; i++) {
|
||||
var process = response['processlist'][i]
|
||||
process.memvirt = process.memory_info[1]
|
||||
process.memres = process.memory_info[0]
|
||||
process.timeplus = timedelta(process.cpu_times)
|
||||
process.timemillis = timemillis(process.cpu_times)
|
||||
|
||||
process.io_read = '?';
|
||||
process.io_write = '?';
|
||||
|
||||
if (process.io_counters) {
|
||||
process.io_read = (process.io_counters[0] - process.io_counters[2]) / process.time_since_update;
|
||||
|
||||
if (process.io_read != 0) {
|
||||
process.io_read = $filter('bytes')(process.io_read);
|
||||
}
|
||||
|
||||
process.io_write = (process.io_counters[1] - process.io_counters[3]) / process.time_since_update;
|
||||
|
||||
if (process.io_write != 0) {
|
||||
process.io_write = $filter('bytes')(process.io_write);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < response['alert'].length; i++) {
|
||||
var alert = response['alert'][i];
|
||||
alert.begin = alert[0] * 1000;
|
||||
alert.end = alert[1] * 1000;
|
||||
alert.ongoing = alert[1] == -1;
|
||||
|
||||
if (!alert.ongoing) {
|
||||
alert.duration = durationBetweenTwoDates(alert.begin, alert.end);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.is_bsd = response['system'].os_name === 'FreeBSD';
|
||||
$scope.is_linux = response['system'].os_name === 'Linux';
|
||||
$scope.is_mac = response['system'].os_name === 'Darwin';
|
||||
$scope.is_windows = response['system'].os_name === 'Windows';
|
||||
|
||||
$scope.result = response;
|
||||
canceler.resolve()
|
||||
}).error(function(d, status, headers, config) {
|
||||
console.log('error status:' + status + " - headers = " + headers);
|
||||
canceler.resolve()
|
||||
});
|
||||
}
|
||||
|
||||
$scope.isNice = function(nice) {
|
||||
if(nice !== undefined && (($scope.is_windows && nice != 32) || (!$scope.is_windows && nice != 0))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$scope.getAlert = function(pluginName, limitNamePrefix, current, maximum, log) {
|
||||
current = current || 0;
|
||||
maximum = maximum || 100;
|
||||
log = log || false;
|
||||
log_str = log ? '_log' : '';
|
||||
|
||||
var value = (current * 100) / maximum;
|
||||
|
||||
if ($scope.pluginLimits != undefined && $scope.pluginLimits[pluginName] != undefined) {
|
||||
for (var i = 0; i < $scope.limitSuffix.length; i++) {
|
||||
var limitName = limitNamePrefix + $scope.limitSuffix[i]
|
||||
var limit = $scope.pluginLimits[pluginName][limitName]
|
||||
|
||||
if (value >= limit) {
|
||||
var pos = limitName.lastIndexOf("_")
|
||||
var className = limitName.substring(pos + 1)
|
||||
|
||||
return className + log_str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "ok" + log_str;
|
||||
}
|
||||
|
||||
$scope.getAlertLog = function(pluginName, limitNamePrefix, current, maximum) {
|
||||
return $scope.getAlert(pluginName, limitNamePrefix, current, maximum, true);
|
||||
}
|
||||
|
||||
$scope.init_refresh_time();
|
||||
$scope.init_limits();
|
||||
$scope.init_help();
|
||||
|
||||
var stop;
|
||||
$scope.configure_refresh = function () {
|
||||
if (!angular.isDefined(stop)) {
|
||||
//$scope.refreshData();
|
||||
stop = $interval(function() {
|
||||
$scope.refreshData();
|
||||
}, $scope.refreshTime * 1000); // in milliseconds
|
||||
}
|
||||
}
|
||||
|
||||
$scope.$watch(
|
||||
function() { return $scope.refreshTime; },
|
||||
function(newValue, oldValue) {
|
||||
$scope.stop_refresh();
|
||||
$scope.configure_refresh();
|
||||
}
|
||||
);
|
||||
|
||||
$scope.stop_refresh = function() {
|
||||
if (angular.isDefined(stop)) {
|
||||
$interval.cancel(stop);
|
||||
stop = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.$on('$destroy', function() {
|
||||
// Make sure that the interval is destroyed too
|
||||
$scope.stop_refresh();
|
||||
});
|
||||
|
||||
$scope.onKeyDown = function($event) {
|
||||
if ($event.keyCode == keycodes.a) { // a Sort processes automatically
|
||||
$scope.sorter.column = "cpu_percent";
|
||||
$scope.sorter.auto = true;
|
||||
} else if ($event.keyCode == keycodes.c) {//c Sort processes by CPU%
|
||||
$scope.sorter.column = "cpu_percent";
|
||||
$scope.sorter.auto = false;
|
||||
} else if ($event.keyCode == keycodes.m) {//m Sort processes by MEM%
|
||||
$scope.sorter.column = "memory_percent";
|
||||
$scope.sorter.auto = false;
|
||||
} else if ($event.keyCode == keycodes.p) {//p Sort processes by name
|
||||
$scope.sorter.column = "name";
|
||||
$scope.sorter.auto = false;
|
||||
} else if ($event.keyCode == keycodes.i) {//i Sort processes by I/O rate
|
||||
$scope.sorter.column = ['io_read', 'io_write'];
|
||||
$scope.sorter.auto = false;
|
||||
} else if ($event.keyCode == keycodes.t) {//t Sort processes by CPU times
|
||||
$scope.sorter.column = "timemillis";
|
||||
$scope.sorter.auto = false;
|
||||
} else if ($event.keyCode == keycodes.u) {//t Sort processes by user
|
||||
$scope.sorter.column = "username";
|
||||
$scope.sorter.auto = false;
|
||||
} else if ($event.keyCode == keycodes.d) {//d Show/hide disk I/O stats
|
||||
$scope.show_hide('diskio')
|
||||
} else if ($event.keyCode == keycodes.f) {//f Show/hide filesystem stats
|
||||
$scope.show_hide('fs')
|
||||
} else if ($event.keyCode == keycodes.n) {//n sort_by Show/hide network stats
|
||||
$scope.show_hide('network')
|
||||
} else if ($event.keyCode == keycodes.s) {//s Show/hide sensors stats
|
||||
$scope.show_hide('sensors')
|
||||
} else if ($event.keyCode == keycodes.TWO && $event.shiftKey) {//2 Show/hide left sidebar
|
||||
$scope.show_hide('sidebar')
|
||||
} else if ($event.keyCode == keycodes.z) {//z Enable/disable processes stats
|
||||
$scope.show_hide('process_stats')
|
||||
} else if ($event.keyCode == keycodes.e) {//e Enable/disable top extended stats
|
||||
$scope.show_hide('top_extended_stats')
|
||||
} else if ($event.keyCode == keycodes.SLASH) {// SLASH Enable/disable short processes name
|
||||
$scope.show_hide('short_process_name')
|
||||
} else if ($event.keyCode == keycodes.D && $event.shiftKey) {//D Enable/disable Docker stats
|
||||
$scope.show_hide('docker_stats')
|
||||
} else if ($event.keyCode == keycodes.b) {//b Bytes or bits for network I/O
|
||||
$scope.show_hide('network_by_bytes')
|
||||
} else if ($event.keyCode == keycodes.l) {//l Show/hide alert logs
|
||||
$scope.show_hide('alert')
|
||||
} else if ($event.keyCode == keycodes.w) {//w Delete warning alerts
|
||||
$scope.show_hide('warning_alerts')
|
||||
} else if ($event.keyCode == keycodes.x) {//x Delete warning and critical alerts
|
||||
$scope.show_hide('warning_critical_alerts')
|
||||
} else if ($event.keyCode == keycodes.ONE && $event.shiftKey) {//1 Global CPU or per-CPU stats
|
||||
$scope.show_hide('per_cpu')
|
||||
} else if ($event.keyCode == keycodes.h) {//h Show/hide this help screen
|
||||
$scope.show_hide('help')
|
||||
} else if ($event.keyCode == keycodes.T && $event.shiftKey) {//T View network I/O as combination
|
||||
$scope.show_hide('network_io_combination')
|
||||
} else if ($event.keyCode == keycodes.u) {//u View cumulative network I/O
|
||||
$scope.show_hide('network_io_cumulative')
|
||||
} else if ($event.keyCode == keycodes.F && $event.shiftKey) {//F Show filesystem free space
|
||||
$scope.show_hide('filesystem_freespace')
|
||||
} else if ($event.keyCode == keycodes.g) {//g Generate graphs for current history
|
||||
// not available
|
||||
} else if ($event.keyCode == keycodes.r) {//r Reset history
|
||||
// not available
|
||||
} else if ($event.keyCode == keycodes.q) {//q Quit (Esc and Ctrl-C also work)
|
||||
// not available
|
||||
}
|
||||
}
|
||||
});
|
30
glances/outputs/static/js/variables.js
Normal file
@ -0,0 +1,30 @@
|
||||
var keycodes = {
|
||||
'a' : '65',
|
||||
'c' : '67',
|
||||
'm' : '77',
|
||||
'p' : '80',
|
||||
'i' : '73',
|
||||
't' : '84',
|
||||
'u' : '85',
|
||||
'd' : '68',
|
||||
'f' : '70',
|
||||
'n' : '78',
|
||||
's' : '83',
|
||||
'TWO': '50',
|
||||
'z' : '90',
|
||||
'e' : '69',
|
||||
'SLASH': '191',
|
||||
'D' : '68',
|
||||
'b' : '66',
|
||||
'l' : '76',
|
||||
'w' : '87',
|
||||
'x' : '88',
|
||||
'ONE': '49',
|
||||
'h' : '72',
|
||||
'T' : '84',
|
||||
'u' : '85',
|
||||
'F' : '70',
|
||||
'g' : '71',
|
||||
'r' : '82',
|
||||
'q' : '81'
|
||||
}
|
15
glances/outputs/static/js/vendors/angular-route.min.js
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
/*
|
||||
AngularJS v1.2.28
|
||||
(c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(n,e,A){'use strict';function x(s,g,h){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",link:function(a,c,b,f,w){function y(){p&&(p.remove(),p=null);k&&(k.$destroy(),k=null);l&&(h.leave(l,function(){p=null}),p=l,l=null)}function v(){var b=s.current&&s.current.locals;if(e.isDefined(b&&b.$template)){var b=a.$new(),d=s.current;l=w(b,function(d){h.enter(d,null,l||c,function(){!e.isDefined(t)||t&&!a.$eval(t)||g()});y()});k=d.scope=b;k.$emit("$viewContentLoaded");k.$eval(u)}else y()}
|
||||
var k,l,p,t=b.autoscroll,u=b.onload||"";a.$on("$routeChangeSuccess",v);v()}}}function z(e,g,h){return{restrict:"ECA",priority:-400,link:function(a,c){var b=h.current,f=b.locals;c.html(f.$template);var w=e(c.contents());b.controller&&(f.$scope=a,f=g(b.controller,f),b.controllerAs&&(a[b.controllerAs]=f),c.data("$ngControllerController",f),c.children().data("$ngControllerController",f));w(a)}}}n=e.module("ngRoute",["ng"]).provider("$route",function(){function s(a,c){return e.extend(new (e.extend(function(){},
|
||||
{prototype:a})),c)}function g(a,e){var b=e.caseInsensitiveMatch,f={originalPath:a,regexp:a},h=f.keys=[];a=a.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)([\?\*])?/g,function(a,e,b,c){a="?"===c?c:null;c="*"===c?c:null;h.push({name:b,optional:!!a});e=e||"";return""+(a?"":e)+"(?:"+(a?e:"")+(c&&"(.+?)"||"([^/]+)")+(a||"")+")"+(a||"")}).replace(/([\/$\*])/g,"\\$1");f.regexp=RegExp("^"+a+"$",b?"i":"");return f}var h={};this.when=function(a,c){h[a]=e.extend({reloadOnSearch:!0},c,a&&g(a,c));if(a){var b=
|
||||
"/"==a[a.length-1]?a.substr(0,a.length-1):a+"/";h[b]=e.extend({redirectTo:a},g(b,c))}return this};this.otherwise=function(a){this.when(null,a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$http","$templateCache","$sce",function(a,c,b,f,g,n,v,k){function l(){var d=p(),m=r.current;if(d&&m&&d.$$route===m.$$route&&e.equals(d.pathParams,m.pathParams)&&!d.reloadOnSearch&&!u)m.params=d.params,e.copy(m.params,b),a.$broadcast("$routeUpdate",m);else if(d||m)u=!1,a.$broadcast("$routeChangeStart",
|
||||
d,m),(r.current=d)&&d.redirectTo&&(e.isString(d.redirectTo)?c.path(t(d.redirectTo,d.params)).search(d.params).replace():c.url(d.redirectTo(d.pathParams,c.path(),c.search())).replace()),f.when(d).then(function(){if(d){var a=e.extend({},d.resolve),c,b;e.forEach(a,function(d,c){a[c]=e.isString(d)?g.get(d):g.invoke(d)});e.isDefined(c=d.template)?e.isFunction(c)&&(c=c(d.params)):e.isDefined(b=d.templateUrl)&&(e.isFunction(b)&&(b=b(d.params)),b=k.getTrustedResourceUrl(b),e.isDefined(b)&&(d.loadedTemplateUrl=
|
||||
b,c=n.get(b,{cache:v}).then(function(a){return a.data})));e.isDefined(c)&&(a.$template=c);return f.all(a)}}).then(function(c){d==r.current&&(d&&(d.locals=c,e.copy(d.params,b)),a.$broadcast("$routeChangeSuccess",d,m))},function(c){d==r.current&&a.$broadcast("$routeChangeError",d,m,c)})}function p(){var a,b;e.forEach(h,function(f,h){var q;if(q=!b){var g=c.path();q=f.keys;var l={};if(f.regexp)if(g=f.regexp.exec(g)){for(var k=1,p=g.length;k<p;++k){var n=q[k-1],r=g[k];n&&r&&(l[n.name]=r)}q=l}else q=null;
|
||||
else q=null;q=a=q}q&&(b=s(f,{params:e.extend({},c.search(),a),pathParams:a}),b.$$route=f)});return b||h[null]&&s(h[null],{params:{},pathParams:{}})}function t(a,c){var b=[];e.forEach((a||"").split(":"),function(a,d){if(0===d)b.push(a);else{var e=a.match(/(\w+)(?:[?*])?(.*)/),f=e[1];b.push(c[f]);b.push(e[2]||"");delete c[f]}});return b.join("")}var u=!1,r={routes:h,reload:function(){u=!0;a.$evalAsync(l)}};a.$on("$locationChangeSuccess",l);return r}]});n.provider("$routeParams",function(){this.$get=
|
||||
function(){return{}}});n.directive("ngView",x);n.directive("ngView",z);x.$inject=["$route","$anchorScroll","$animate"];z.$inject=["$compile","$controller","$route"]})(window,window.angular);
|
||||
//# sourceMappingURL=angular-route.min.js.map
|
8
glances/outputs/static/js/vendors/angular-route.min.js.map
vendored
Normal file
218
glances/outputs/static/js/vendors/angular.min.js
vendored
Normal file
@ -0,0 +1,218 @@
|
||||
|
||||
/*
|
||||
AngularJS v1.2.28
|
||||
(c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(W,X,u){'use strict';function z(b){return function(){var a=arguments[0],c,a="["+(b?b+":":"")+a+"] http://errors.angularjs.org/1.2.28/"+(b?b+"/":"")+a;for(c=1;c<arguments.length;c++)a=a+(1==c?"?":"&")+"p"+(c-1)+"="+encodeURIComponent("function"==typeof arguments[c]?arguments[c].toString().replace(/ \{[\s\S]*$/,""):"undefined"==typeof arguments[c]?"undefined":"string"!=typeof arguments[c]?JSON.stringify(arguments[c]):arguments[c]);return Error(a)}}function Sa(b){if(null==b||Ja(b))return!1;
|
||||
var a=b.length;return 1===b.nodeType&&a?!0:G(b)||L(b)||0===a||"number"===typeof a&&0<a&&a-1 in b}function r(b,a,c){var d;if(b)if(N(b))for(d in b)"prototype"==d||("length"==d||"name"==d||b.hasOwnProperty&&!b.hasOwnProperty(d))||a.call(c,b[d],d);else if(L(b)||Sa(b))for(d=0;d<b.length;d++)a.call(c,b[d],d);else if(b.forEach&&b.forEach!==r)b.forEach(a,c);else for(d in b)b.hasOwnProperty(d)&&a.call(c,b[d],d);return b}function Xb(b){var a=[],c;for(c in b)b.hasOwnProperty(c)&&a.push(c);return a.sort()}function Sc(b,
|
||||
a,c){for(var d=Xb(b),e=0;e<d.length;e++)a.call(c,b[d[e]],d[e]);return d}function Yb(b){return function(a,c){b(c,a)}}function ib(){for(var b=na.length,a;b;){b--;a=na[b].charCodeAt(0);if(57==a)return na[b]="A",na.join("");if(90==a)na[b]="0";else return na[b]=String.fromCharCode(a+1),na.join("")}na.unshift("0");return na.join("")}function Zb(b,a){a?b.$$hashKey=a:delete b.$$hashKey}function E(b){var a=b.$$hashKey;r(arguments,function(a){a!==b&&r(a,function(a,c){b[c]=a})});Zb(b,a);return b}function U(b){return parseInt(b,
|
||||
10)}function $b(b,a){return E(new (E(function(){},{prototype:b})),a)}function v(){}function ga(b){return b}function aa(b){return function(){return b}}function F(b){return"undefined"===typeof b}function D(b){return"undefined"!==typeof b}function T(b){return null!=b&&"object"===typeof b}function G(b){return"string"===typeof b}function jb(b){return"number"===typeof b}function va(b){return"[object Date]"===Ba.call(b)}function N(b){return"function"===typeof b}function kb(b){return"[object RegExp]"===Ba.call(b)}
|
||||
function Ja(b){return b&&b.document&&b.location&&b.alert&&b.setInterval}function Tc(b){return!(!b||!(b.nodeName||b.prop&&b.attr&&b.find))}function Uc(b,a,c){var d=[];r(b,function(b,f,g){d.push(a.call(c,b,f,g))});return d}function Ta(b,a){if(b.indexOf)return b.indexOf(a);for(var c=0;c<b.length;c++)if(a===b[c])return c;return-1}function Ua(b,a){var c=Ta(b,a);0<=c&&b.splice(c,1);return a}function Ka(b,a,c,d){if(Ja(b)||b&&b.$evalAsync&&b.$watch)throw Va("cpws");if(a){if(b===a)throw Va("cpi");c=c||[];
|
||||
d=d||[];if(T(b)){var e=Ta(c,b);if(-1!==e)return d[e];c.push(b);d.push(a)}if(L(b))for(var f=a.length=0;f<b.length;f++)e=Ka(b[f],null,c,d),T(b[f])&&(c.push(b[f]),d.push(e)),a.push(e);else{var g=a.$$hashKey;L(a)?a.length=0:r(a,function(b,c){delete a[c]});for(f in b)e=Ka(b[f],null,c,d),T(b[f])&&(c.push(b[f]),d.push(e)),a[f]=e;Zb(a,g)}}else if(a=b)L(b)?a=Ka(b,[],c,d):va(b)?a=new Date(b.getTime()):kb(b)?(a=RegExp(b.source,b.toString().match(/[^\/]*$/)[0]),a.lastIndex=b.lastIndex):T(b)&&(a=Ka(b,{},c,d));
|
||||
return a}function ha(b,a){if(L(b)){a=a||[];for(var c=0;c<b.length;c++)a[c]=b[c]}else if(T(b))for(c in a=a||{},b)!lb.call(b,c)||"$"===c.charAt(0)&&"$"===c.charAt(1)||(a[c]=b[c]);return a||b}function Ca(b,a){if(b===a)return!0;if(null===b||null===a)return!1;if(b!==b&&a!==a)return!0;var c=typeof b,d;if(c==typeof a&&"object"==c)if(L(b)){if(!L(a))return!1;if((c=b.length)==a.length){for(d=0;d<c;d++)if(!Ca(b[d],a[d]))return!1;return!0}}else{if(va(b))return va(a)?isNaN(b.getTime())&&isNaN(a.getTime())||b.getTime()===
|
||||
a.getTime():!1;if(kb(b)&&kb(a))return b.toString()==a.toString();if(b&&b.$evalAsync&&b.$watch||a&&a.$evalAsync&&a.$watch||Ja(b)||Ja(a)||L(a))return!1;c={};for(d in b)if("$"!==d.charAt(0)&&!N(b[d])){if(!Ca(b[d],a[d]))return!1;c[d]=!0}for(d in a)if(!c.hasOwnProperty(d)&&"$"!==d.charAt(0)&&a[d]!==u&&!N(a[d]))return!1;return!0}return!1}function Bb(b,a){var c=2<arguments.length?wa.call(arguments,2):[];return!N(a)||a instanceof RegExp?a:c.length?function(){return arguments.length?a.apply(b,c.concat(wa.call(arguments,
|
||||
0))):a.apply(b,c)}:function(){return arguments.length?a.apply(b,arguments):a.call(b)}}function Vc(b,a){var c=a;"string"===typeof b&&"$"===b.charAt(0)?c=u:Ja(a)?c="$WINDOW":a&&X===a?c="$DOCUMENT":a&&(a.$evalAsync&&a.$watch)&&(c="$SCOPE");return c}function oa(b,a){return"undefined"===typeof b?u:JSON.stringify(b,Vc,a?" ":null)}function ac(b){return G(b)?JSON.parse(b):b}function Wa(b){"function"===typeof b?b=!0:b&&0!==b.length?(b=x(""+b),b=!("f"==b||"0"==b||"false"==b||"no"==b||"n"==b||"[]"==b)):b=!1;
|
||||
return b}function ia(b){b=A(b).clone();try{b.empty()}catch(a){}var c=A("<div>").append(b).html();try{return 3===b[0].nodeType?x(c):c.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+x(b)})}catch(d){return x(c)}}function bc(b){try{return decodeURIComponent(b)}catch(a){}}function cc(b){var a={},c,d;r((b||"").split("&"),function(b){b&&(c=b.replace(/\+/g,"%20").split("="),d=bc(c[0]),D(d)&&(b=D(c[1])?bc(c[1]):!0,lb.call(a,d)?L(a[d])?a[d].push(b):a[d]=[a[d],b]:a[d]=b))});return a}function Cb(b){var a=
|
||||
[];r(b,function(b,d){L(b)?r(b,function(b){a.push(Da(d,!0)+(!0===b?"":"="+Da(b,!0)))}):a.push(Da(d,!0)+(!0===b?"":"="+Da(b,!0)))});return a.length?a.join("&"):""}function mb(b){return Da(b,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function Da(b,a){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,a?"%20":"+")}function Wc(b,a){function c(a){a&&d.push(a)}var d=[b],e,f,g=["ng:app","ng-app","x-ng-app",
|
||||
"data-ng-app"],h=/\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;r(g,function(a){g[a]=!0;c(X.getElementById(a));a=a.replace(":","\\:");b.querySelectorAll&&(r(b.querySelectorAll("."+a),c),r(b.querySelectorAll("."+a+"\\:"),c),r(b.querySelectorAll("["+a+"]"),c))});r(d,function(a){if(!e){var b=h.exec(" "+a.className+" ");b?(e=a,f=(b[2]||"").replace(/\s+/g,",")):r(a.attributes,function(b){!e&&g[b.name]&&(e=a,f=b.value)})}});e&&a(e,f?[f]:[])}function dc(b,a){var c=function(){b=A(b);if(b.injector()){var c=b[0]===X?
|
||||
"document":ia(b);throw Va("btstrpd",c.replace(/</,"<").replace(/>/,">"));}a=a||[];a.unshift(["$provide",function(a){a.value("$rootElement",b)}]);a.unshift("ng");c=ec(a);c.invoke(["$rootScope","$rootElement","$compile","$injector","$animate",function(a,b,c,d,e){a.$apply(function(){b.data("$injector",d);c(b)(a)})}]);return c},d=/^NG_DEFER_BOOTSTRAP!/;if(W&&!d.test(W.name))return c();W.name=W.name.replace(d,"");Xa.resumeBootstrap=function(b){r(b,function(b){a.push(b)});c()}}function nb(b,a){a=
|
||||
a||"_";return b.replace(Xc,function(b,d){return(d?a:"")+b.toLowerCase()})}function Db(b,a,c){if(!b)throw Va("areq",a||"?",c||"required");return b}function Ya(b,a,c){c&&L(b)&&(b=b[b.length-1]);Db(N(b),a,"not a function, got "+(b&&"object"===typeof b?b.constructor.name||"Object":typeof b));return b}function Ea(b,a){if("hasOwnProperty"===b)throw Va("badname",a);}function fc(b,a,c){if(!a)return b;a=a.split(".");for(var d,e=b,f=a.length,g=0;g<f;g++)d=a[g],b&&(b=(e=b)[d]);return!c&&N(b)?Bb(e,b):b}function Eb(b){var a=
|
||||
b[0];b=b[b.length-1];if(a===b)return A(a);var c=[a];do{a=a.nextSibling;if(!a)break;c.push(a)}while(a!==b);return A(c)}function Yc(b){var a=z("$injector"),c=z("ng");b=b.angular||(b.angular={});b.$$minErr=b.$$minErr||z;return b.module||(b.module=function(){var b={};return function(e,f,g){if("hasOwnProperty"===e)throw c("badname","module");f&&b.hasOwnProperty(e)&&(b[e]=null);return b[e]||(b[e]=function(){function b(a,d,e){return function(){c[e||"push"]([a,d,arguments]);return n}}if(!f)throw a("nomod",
|
||||
e);var c=[],d=[],l=b("$injector","invoke"),n={_invokeQueue:c,_runBlocks:d,requires:f,name:e,provider:b("$provide","provider"),factory:b("$provide","factory"),service:b("$provide","service"),value:b("$provide","value"),constant:b("$provide","constant","unshift"),animation:b("$animateProvider","register"),filter:b("$filterProvider","register"),controller:b("$controllerProvider","register"),directive:b("$compileProvider","directive"),config:l,run:function(a){d.push(a);return this}};g&&l(g);return n}())}}())}
|
||||
function Zc(b){E(b,{bootstrap:dc,copy:Ka,extend:E,equals:Ca,element:A,forEach:r,injector:ec,noop:v,bind:Bb,toJson:oa,fromJson:ac,identity:ga,isUndefined:F,isDefined:D,isString:G,isFunction:N,isObject:T,isNumber:jb,isElement:Tc,isArray:L,version:$c,isDate:va,lowercase:x,uppercase:La,callbacks:{counter:0},$$minErr:z,$$csp:Za});$a=Yc(W);try{$a("ngLocale")}catch(a){$a("ngLocale",[]).provider("$locale",ad)}$a("ng",["ngLocale"],["$provide",function(a){a.provider({$$sanitizeUri:bd});a.provider("$compile",
|
||||
gc).directive({a:cd,input:hc,textarea:hc,form:dd,script:ed,select:fd,style:gd,option:hd,ngBind:id,ngBindHtml:jd,ngBindTemplate:kd,ngClass:ld,ngClassEven:md,ngClassOdd:nd,ngCloak:od,ngController:pd,ngForm:qd,ngHide:rd,ngIf:sd,ngInclude:td,ngInit:ud,ngNonBindable:vd,ngPluralize:wd,ngRepeat:xd,ngShow:yd,ngStyle:zd,ngSwitch:Ad,ngSwitchWhen:Bd,ngSwitchDefault:Cd,ngOptions:Dd,ngTransclude:Ed,ngModel:Fd,ngList:Gd,ngChange:Hd,required:ic,ngRequired:ic,ngValue:Id}).directive({ngInclude:Jd}).directive(Fb).directive(jc);
|
||||
a.provider({$anchorScroll:Kd,$animate:Ld,$browser:Md,$cacheFactory:Nd,$controller:Od,$document:Pd,$exceptionHandler:Qd,$filter:kc,$interpolate:Rd,$interval:Sd,$http:Td,$httpBackend:Ud,$location:Vd,$log:Wd,$parse:Xd,$rootScope:Yd,$q:Zd,$sce:$d,$sceDelegate:ae,$sniffer:be,$templateCache:ce,$timeout:de,$window:ee,$$rAF:fe,$$asyncCallback:ge})}])}function ab(b){return b.replace(he,function(a,b,d,e){return e?d.toUpperCase():d}).replace(ie,"Moz$1")}function Gb(b,a,c,d){function e(b){var e=c&&b?[this.filter(b)]:
|
||||
[this],k=a,m,l,n,q,p,s;if(!d||null!=b)for(;e.length;)for(m=e.shift(),l=0,n=m.length;l<n;l++)for(q=A(m[l]),k?q.triggerHandler("$destroy"):k=!k,p=0,q=(s=q.children()).length;p<q;p++)e.push(Fa(s[p]));return f.apply(this,arguments)}var f=Fa.fn[b],f=f.$original||f;e.$original=f;Fa.fn[b]=e}function S(b){if(b instanceof S)return b;G(b)&&(b=$(b));if(!(this instanceof S)){if(G(b)&&"<"!=b.charAt(0))throw Hb("nosel");return new S(b)}if(G(b)){var a=b;b=X;var c;if(c=je.exec(a))b=[b.createElement(c[1])];else{var d=
|
||||
b,e;b=d.createDocumentFragment();c=[];if(Ib.test(a)){d=b.appendChild(d.createElement("div"));e=(ke.exec(a)||["",""])[1].toLowerCase();e=da[e]||da._default;d.innerHTML="<div> </div>"+e[1]+a.replace(le,"<$1></$2>")+e[2];d.removeChild(d.firstChild);for(a=e[0];a--;)d=d.lastChild;a=0;for(e=d.childNodes.length;a<e;++a)c.push(d.childNodes[a]);d=b.firstChild;d.textContent=""}else c.push(d.createTextNode(a));b.textContent="";b.innerHTML="";b=c}Jb(this,b);A(X.createDocumentFragment()).append(this)}else Jb(this,
|
||||
b)}function Kb(b){return b.cloneNode(!0)}function Ma(b){Lb(b);var a=0;for(b=b.childNodes||[];a<b.length;a++)Ma(b[a])}function lc(b,a,c,d){if(D(d))throw Hb("offargs");var e=pa(b,"events");pa(b,"handle")&&(F(a)?r(e,function(a,c){bb(b,c,a);delete e[c]}):r(a.split(" "),function(a){F(c)?(bb(b,a,e[a]),delete e[a]):Ua(e[a]||[],c)}))}function Lb(b,a){var c=b.ng339,d=cb[c];d&&(a?delete cb[c].data[a]:(d.handle&&(d.events.$destroy&&d.handle({},"$destroy"),lc(b)),delete cb[c],b.ng339=u))}function pa(b,a,c){var d=
|
||||
b.ng339,d=cb[d||-1];if(D(c))d||(b.ng339=d=++me,d=cb[d]={}),d[a]=c;else return d&&d[a]}function Mb(b,a,c){var d=pa(b,"data"),e=D(c),f=!e&&D(a),g=f&&!T(a);d||g||pa(b,"data",d={});if(e)d[a]=c;else if(f){if(g)return d&&d[a];E(d,a)}else return d}function Nb(b,a){return b.getAttribute?-1<(" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").indexOf(" "+a+" "):!1}function ob(b,a){a&&b.setAttribute&&r(a.split(" "),function(a){b.setAttribute("class",$((" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g,
|
||||
" ").replace(" "+$(a)+" "," ")))})}function pb(b,a){if(a&&b.setAttribute){var c=(" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ");r(a.split(" "),function(a){a=$(a);-1===c.indexOf(" "+a+" ")&&(c+=a+" ")});b.setAttribute("class",$(c))}}function Jb(b,a){if(a){a=a.nodeName||!D(a.length)||Ja(a)?[a]:a;for(var c=0;c<a.length;c++)b.push(a[c])}}function mc(b,a){return qb(b,"$"+(a||"ngController")+"Controller")}function qb(b,a,c){9==b.nodeType&&(b=b.documentElement);for(a=L(a)?a:[a];b;){for(var d=
|
||||
0,e=a.length;d<e;d++)if((c=A.data(b,a[d]))!==u)return c;b=b.parentNode||11===b.nodeType&&b.host}}function nc(b){for(var a=0,c=b.childNodes;a<c.length;a++)Ma(c[a]);for(;b.firstChild;)b.removeChild(b.firstChild)}function oc(b,a){var c=rb[a.toLowerCase()];return c&&pc[b.nodeName]&&c}function ne(b,a){var c=function(c,e){c.preventDefault||(c.preventDefault=function(){c.returnValue=!1});c.stopPropagation||(c.stopPropagation=function(){c.cancelBubble=!0});c.target||(c.target=c.srcElement||X);if(F(c.defaultPrevented)){var f=
|
||||
c.preventDefault;c.preventDefault=function(){c.defaultPrevented=!0;f.call(c)};c.defaultPrevented=!1}c.isDefaultPrevented=function(){return c.defaultPrevented||!1===c.returnValue};var g=ha(a[e||c.type]||[]);r(g,function(a){a.call(b,c)});8>=R?(c.preventDefault=null,c.stopPropagation=null,c.isDefaultPrevented=null):(delete c.preventDefault,delete c.stopPropagation,delete c.isDefaultPrevented)};c.elem=b;return c}function Na(b,a){var c=typeof b,d;"function"==c||"object"==c&&null!==b?"function"==typeof(d=
|
||||
b.$$hashKey)?d=b.$$hashKey():d===u&&(d=b.$$hashKey=(a||ib)()):d=b;return c+":"+d}function db(b,a){if(a){var c=0;this.nextUid=function(){return++c}}r(b,this.put,this)}function qc(b){var a,c;"function"===typeof b?(a=b.$inject)||(a=[],b.length&&(c=b.toString().replace(oe,""),c=c.match(pe),r(c[1].split(qe),function(b){b.replace(re,function(b,c,d){a.push(d)})})),b.$inject=a):L(b)?(c=b.length-1,Ya(b[c],"fn"),a=b.slice(0,c)):Ya(b,"fn",!0);return a}function ec(b){function a(a){return function(b,c){if(T(b))r(b,
|
||||
Yb(a));else return a(b,c)}}function c(a,b){Ea(a,"service");if(N(b)||L(b))b=n.instantiate(b);if(!b.$get)throw eb("pget",a);return l[a+h]=b}function d(a,b){return c(a,{$get:b})}function e(a){var b=[],c,d,f,h;r(a,function(a){if(!m.get(a)){m.put(a,!0);try{if(G(a))for(c=$a(a),b=b.concat(e(c.requires)).concat(c._runBlocks),d=c._invokeQueue,f=0,h=d.length;f<h;f++){var g=d[f],k=n.get(g[0]);k[g[1]].apply(k,g[2])}else N(a)?b.push(n.invoke(a)):L(a)?b.push(n.invoke(a)):Ya(a,"module")}catch(p){throw L(a)&&(a=
|
||||
a[a.length-1]),p.message&&(p.stack&&-1==p.stack.indexOf(p.message))&&(p=p.message+"\n"+p.stack),eb("modulerr",a,p.stack||p.message||p);}}});return b}function f(a,b){function c(d){if(a.hasOwnProperty(d)){if(a[d]===g)throw eb("cdep",d+" <- "+k.join(" <- "));return a[d]}try{return k.unshift(d),a[d]=g,a[d]=b(d)}catch(e){throw a[d]===g&&delete a[d],e;}finally{k.shift()}}function d(a,b,e){var f=[],h=qc(a),g,k,p;k=0;for(g=h.length;k<g;k++){p=h[k];if("string"!==typeof p)throw eb("itkn",p);f.push(e&&e.hasOwnProperty(p)?
|
||||
e[p]:c(p))}L(a)&&(a=a[g]);return a.apply(b,f)}return{invoke:d,instantiate:function(a,b){var c=function(){},e;c.prototype=(L(a)?a[a.length-1]:a).prototype;c=new c;e=d(a,c,b);return T(e)||N(e)?e:c},get:c,annotate:qc,has:function(b){return l.hasOwnProperty(b+h)||a.hasOwnProperty(b)}}}var g={},h="Provider",k=[],m=new db([],!0),l={$provide:{provider:a(c),factory:a(d),service:a(function(a,b){return d(a,["$injector",function(a){return a.instantiate(b)}])}),value:a(function(a,b){return d(a,aa(b))}),constant:a(function(a,
|
||||
b){Ea(a,"constant");l[a]=b;q[a]=b}),decorator:function(a,b){var c=n.get(a+h),d=c.$get;c.$get=function(){var a=p.invoke(d,c);return p.invoke(b,null,{$delegate:a})}}}},n=l.$injector=f(l,function(){throw eb("unpr",k.join(" <- "));}),q={},p=q.$injector=f(q,function(a){a=n.get(a+h);return p.invoke(a.$get,a)});r(e(b),function(a){p.invoke(a||v)});return p}function Kd(){var b=!0;this.disableAutoScrolling=function(){b=!1};this.$get=["$window","$location","$rootScope",function(a,c,d){function e(a){var b=null;
|
||||
r(a,function(a){b||"a"!==x(a.nodeName)||(b=a)});return b}function f(){var b=c.hash(),d;b?(d=g.getElementById(b))?d.scrollIntoView():(d=e(g.getElementsByName(b)))?d.scrollIntoView():"top"===b&&a.scrollTo(0,0):a.scrollTo(0,0)}var g=a.document;b&&d.$watch(function(){return c.hash()},function(){d.$evalAsync(f)});return f}]}function ge(){this.$get=["$$rAF","$timeout",function(b,a){return b.supported?function(a){return b(a)}:function(b){return a(b,0,!1)}}]}function se(b,a,c,d){function e(a){try{a.apply(null,
|
||||
wa.call(arguments,1))}finally{if(s--,0===s)for(;J.length;)try{J.pop()()}catch(b){c.error(b)}}}function f(a,b){(function ea(){r(w,function(a){a()});t=b(ea,a)})()}function g(){y!=h.url()&&(y=h.url(),r(ba,function(a){a(h.url())}))}var h=this,k=a[0],m=b.location,l=b.history,n=b.setTimeout,q=b.clearTimeout,p={};h.isMock=!1;var s=0,J=[];h.$$completeOutstandingRequest=e;h.$$incOutstandingRequestCount=function(){s++};h.notifyWhenNoOutstandingRequests=function(a){r(w,function(a){a()});0===s?a():J.push(a)};
|
||||
var w=[],t;h.addPollFn=function(a){F(t)&&f(100,n);w.push(a);return a};var y=m.href,K=a.find("base"),B=null;h.url=function(a,c){m!==b.location&&(m=b.location);l!==b.history&&(l=b.history);if(a){if(y!=a){var e=y&&Ga(y)===Ga(a);y=a;!e&&d.history?c?l.replaceState(null,"",a):(l.pushState(null,"",a),K.attr("href",K.attr("href"))):(e||(B=a),c?m.replace(a):m.href=a);return h}}else return B||m.href.replace(/%27/g,"'")};var ba=[],O=!1;h.onUrlChange=function(a){if(!O){if(d.history)A(b).on("popstate",g);if(d.hashchange)A(b).on("hashchange",
|
||||
g);else h.addPollFn(g);O=!0}ba.push(a);return a};h.$$checkUrlChange=g;h.baseHref=function(){var a=K.attr("href");return a?a.replace(/^(https?\:)?\/\/[^\/]*/,""):""};var M={},ca="",P=h.baseHref();h.cookies=function(a,b){var d,e,f,h;if(a)b===u?k.cookie=escape(a)+"=;path="+P+";expires=Thu, 01 Jan 1970 00:00:00 GMT":G(b)&&(d=(k.cookie=escape(a)+"="+escape(b)+";path="+P).length+1,4096<d&&c.warn("Cookie '"+a+"' possibly not set or overflowed because it was too large ("+d+" > 4096 bytes)!"));else{if(k.cookie!==
|
||||
ca)for(ca=k.cookie,d=ca.split("; "),M={},f=0;f<d.length;f++)e=d[f],h=e.indexOf("="),0<h&&(a=unescape(e.substring(0,h)),M[a]===u&&(M[a]=unescape(e.substring(h+1))));return M}};h.defer=function(a,b){var c;s++;c=n(function(){delete p[c];e(a)},b||0);p[c]=!0;return c};h.defer.cancel=function(a){return p[a]?(delete p[a],q(a),e(v),!0):!1}}function Md(){this.$get=["$window","$log","$sniffer","$document",function(b,a,c,d){return new se(b,d,a,c)}]}function Nd(){this.$get=function(){function b(b,d){function e(a){a!=
|
||||
n&&(q?q==a&&(q=a.n):q=a,f(a.n,a.p),f(a,n),n=a,n.n=null)}function f(a,b){a!=b&&(a&&(a.p=b),b&&(b.n=a))}if(b in a)throw z("$cacheFactory")("iid",b);var g=0,h=E({},d,{id:b}),k={},m=d&&d.capacity||Number.MAX_VALUE,l={},n=null,q=null;return a[b]={put:function(a,b){if(m<Number.MAX_VALUE){var c=l[a]||(l[a]={key:a});e(c)}if(!F(b))return a in k||g++,k[a]=b,g>m&&this.remove(q.key),b},get:function(a){if(m<Number.MAX_VALUE){var b=l[a];if(!b)return;e(b)}return k[a]},remove:function(a){if(m<Number.MAX_VALUE){var b=
|
||||
l[a];if(!b)return;b==n&&(n=b.p);b==q&&(q=b.n);f(b.n,b.p);delete l[a]}delete k[a];g--},removeAll:function(){k={};g=0;l={};n=q=null},destroy:function(){l=h=k=null;delete a[b]},info:function(){return E({},h,{size:g})}}}var a={};b.info=function(){var b={};r(a,function(a,e){b[e]=a.info()});return b};b.get=function(b){return a[b]};return b}}function ce(){this.$get=["$cacheFactory",function(b){return b("templates")}]}function gc(b,a){var c={},d="Directive",e=/^\s*directive\:\s*([\d\w_\-]+)\s+(.*)$/,f=/(([\d\w_\-]+)(?:\:([^;]+))?;?)/,
|
||||
g=/^(on[a-z]+|formaction)$/;this.directive=function k(a,e){Ea(a,"directive");G(a)?(Db(e,"directiveFactory"),c.hasOwnProperty(a)||(c[a]=[],b.factory(a+d,["$injector","$exceptionHandler",function(b,d){var e=[];r(c[a],function(c,f){try{var g=b.invoke(c);N(g)?g={compile:aa(g)}:!g.compile&&g.link&&(g.compile=aa(g.link));g.priority=g.priority||0;g.index=f;g.name=g.name||a;g.require=g.require||g.controller&&g.name;g.restrict=g.restrict||"A";e.push(g)}catch(k){d(k)}});return e}])),c[a].push(e)):r(a,Yb(k));
|
||||
return this};this.aHrefSanitizationWhitelist=function(b){return D(b)?(a.aHrefSanitizationWhitelist(b),this):a.aHrefSanitizationWhitelist()};this.imgSrcSanitizationWhitelist=function(b){return D(b)?(a.imgSrcSanitizationWhitelist(b),this):a.imgSrcSanitizationWhitelist()};this.$get=["$injector","$interpolate","$exceptionHandler","$http","$templateCache","$parse","$controller","$rootScope","$document","$sce","$animate","$$sanitizeUri",function(a,b,l,n,q,p,s,J,w,t,y,K){function B(a,b,c,d,e){a instanceof
|
||||
A||(a=A(a));r(a,function(b,c){3==b.nodeType&&b.nodeValue.match(/\S+/)&&(a[c]=A(b).wrap("<span></span>").parent()[0])});var f=O(a,b,a,c,d,e);ba(a,"ng-scope");return function(b,c,d,e){Db(b,"scope");var g=c?Oa.clone.call(a):a;r(d,function(a,b){g.data("$"+b+"Controller",a)});d=0;for(var k=g.length;d<k;d++){var p=g[d].nodeType;1!==p&&9!==p||g.eq(d).data("$scope",b)}c&&c(g,b);f&&f(b,g,g,e);return g}}function ba(a,b){try{a.addClass(b)}catch(c){}}function O(a,b,c,d,e,f){function g(a,c,d,e){var f,p,l,m,q,
|
||||
n,w;f=c.length;var s=Array(f);for(m=0;m<f;m++)s[m]=c[m];n=m=0;for(q=k.length;m<q;n++)p=s[n],c=k[m++],f=k[m++],c?(c.scope?(l=a.$new(),A.data(p,"$scope",l)):l=a,w=c.transcludeOnThisElement?M(a,c.transclude,e):!c.templateOnThisElement&&e?e:!e&&b?M(a,b):null,c(f,l,p,d,w)):f&&f(a,p.childNodes,u,e)}for(var k=[],p,l,m,q,n=0;n<a.length;n++)p=new Ob,l=ca(a[n],[],p,0===n?d:u,e),(f=l.length?I(l,a[n],p,b,c,null,[],[],f):null)&&f.scope&&ba(p.$$element,"ng-scope"),p=f&&f.terminal||!(m=a[n].childNodes)||!m.length?
|
||||
null:O(m,f?(f.transcludeOnThisElement||!f.templateOnThisElement)&&f.transclude:b),k.push(f,p),q=q||f||p,f=null;return q?g:null}function M(a,b,c){return function(d,e,f){var g=!1;d||(d=a.$new(),g=d.$$transcluded=!0);e=b(d,e,f,c);if(g)e.on("$destroy",function(){d.$destroy()});return e}}function ca(a,b,c,d,g){var k=c.$attr,p;switch(a.nodeType){case 1:ea(b,qa(Pa(a).toLowerCase()),"E",d,g);for(var l,m,q,n=a.attributes,w=0,s=n&&n.length;w<s;w++){var t=!1,J=!1;l=n[w];if(!R||8<=R||l.specified){p=l.name;m=
|
||||
$(l.value);l=qa(p);if(q=U.test(l))p=nb(l.substr(6),"-");var y=l.replace(/(Start|End)$/,"");l===y+"Start"&&(t=p,J=p.substr(0,p.length-5)+"end",p=p.substr(0,p.length-6));l=qa(p.toLowerCase());k[l]=p;if(q||!c.hasOwnProperty(l))c[l]=m,oc(a,l)&&(c[l]=!0);S(a,b,m,l);ea(b,l,"A",d,g,t,J)}}a=a.className;if(G(a)&&""!==a)for(;p=f.exec(a);)l=qa(p[2]),ea(b,l,"C",d,g)&&(c[l]=$(p[3])),a=a.substr(p.index+p[0].length);break;case 3:x(b,a.nodeValue);break;case 8:try{if(p=e.exec(a.nodeValue))l=qa(p[1]),ea(b,l,"M",d,
|
||||
g)&&(c[l]=$(p[2]))}catch(B){}}b.sort(F);return b}function P(a,b,c){var d=[],e=0;if(b&&a.hasAttribute&&a.hasAttribute(b)){do{if(!a)throw ja("uterdir",b,c);1==a.nodeType&&(a.hasAttribute(b)&&e++,a.hasAttribute(c)&&e--);d.push(a);a=a.nextSibling}while(0<e)}else d.push(a);return A(d)}function C(a,b,c){return function(d,e,f,g,k){e=P(e[0],b,c);return a(d,e,f,g,k)}}function I(a,c,d,e,f,g,k,q,n){function w(a,b,c,d){if(a){c&&(a=C(a,c,d));a.require=H.require;a.directiveName=z;if(K===H||H.$$isolateScope)a=rc(a,
|
||||
{isolateScope:!0});k.push(a)}if(b){c&&(b=C(b,c,d));b.require=H.require;b.directiveName=z;if(K===H||H.$$isolateScope)b=rc(b,{isolateScope:!0});q.push(b)}}function t(a,b,c,d){var e,f="data",g=!1;if(G(b)){for(;"^"==(e=b.charAt(0))||"?"==e;)b=b.substr(1),"^"==e&&(f="inheritedData"),g=g||"?"==e;e=null;d&&"data"===f&&(e=d[b]);e=e||c[f]("$"+b+"Controller");if(!e&&!g)throw ja("ctreq",b,a);}else L(b)&&(e=[],r(b,function(b){e.push(t(a,b,c,d))}));return e}function J(a,e,f,g,n){function w(a,b){var c;2>arguments.length&&
|
||||
(b=a,a=u);Ia&&(c=ca);return n(a,b,c)}var y,Q,B,M,C,P,ca={},ra;y=c===f?d:ha(d,new Ob(A(f),d.$attr));Q=y.$$element;if(K){var ue=/^\s*([@=&])(\??)\s*(\w*)\s*$/;P=e.$new(!0);!I||I!==K&&I!==K.$$originalDirective?Q.data("$isolateScopeNoTemplate",P):Q.data("$isolateScope",P);ba(Q,"ng-isolate-scope");r(K.scope,function(a,c){var d=a.match(ue)||[],f=d[3]||c,g="?"==d[2],d=d[1],k,l,n,q;P.$$isolateBindings[c]=d+f;switch(d){case "@":y.$observe(f,function(a){P[c]=a});y.$$observers[f].$$scope=e;y[f]&&(P[c]=b(y[f])(e));
|
||||
break;case "=":if(g&&!y[f])break;l=p(y[f]);q=l.literal?Ca:function(a,b){return a===b||a!==a&&b!==b};n=l.assign||function(){k=P[c]=l(e);throw ja("nonassign",y[f],K.name);};k=P[c]=l(e);P.$watch(function(){var a=l(e);q(a,P[c])||(q(a,k)?n(e,a=P[c]):P[c]=a);return k=a},null,l.literal);break;case "&":l=p(y[f]);P[c]=function(a){return l(e,a)};break;default:throw ja("iscp",K.name,c,a);}})}ra=n&&w;O&&r(O,function(a){var b={$scope:a===K||a.$$isolateScope?P:e,$element:Q,$attrs:y,$transclude:ra},c;C=a.controller;
|
||||
"@"==C&&(C=y[a.name]);c=s(C,b);ca[a.name]=c;Ia||Q.data("$"+a.name+"Controller",c);a.controllerAs&&(b.$scope[a.controllerAs]=c)});g=0;for(B=k.length;g<B;g++)try{M=k[g],M(M.isolateScope?P:e,Q,y,M.require&&t(M.directiveName,M.require,Q,ca),ra)}catch(H){l(H,ia(Q))}g=e;K&&(K.template||null===K.templateUrl)&&(g=P);a&&a(g,f.childNodes,u,n);for(g=q.length-1;0<=g;g--)try{M=q[g],M(M.isolateScope?P:e,Q,y,M.require&&t(M.directiveName,M.require,Q,ca),ra)}catch(D){l(D,ia(Q))}}n=n||{};for(var y=-Number.MAX_VALUE,
|
||||
M,O=n.controllerDirectives,K=n.newIsolateScopeDirective,I=n.templateDirective,ea=n.nonTlbTranscludeDirective,F=!1,E=!1,Ia=n.hasElementTranscludeDirective,x=d.$$element=A(c),H,z,V,S=e,R,Ha=0,sa=a.length;Ha<sa;Ha++){H=a[Ha];var U=H.$$start,Y=H.$$end;U&&(x=P(c,U,Y));V=u;if(y>H.priority)break;if(V=H.scope)M=M||H,H.templateUrl||(fb("new/isolated scope",K,H,x),T(V)&&(K=H));z=H.name;!H.templateUrl&&H.controller&&(V=H.controller,O=O||{},fb("'"+z+"' controller",O[z],H,x),O[z]=H);if(V=H.transclude)F=!0,H.$$tlb||
|
||||
(fb("transclusion",ea,H,x),ea=H),"element"==V?(Ia=!0,y=H.priority,V=x,x=d.$$element=A(X.createComment(" "+z+": "+d[z]+" ")),c=x[0],ra(f,wa.call(V,0),c),S=B(V,e,y,g&&g.name,{nonTlbTranscludeDirective:ea})):(V=A(Kb(c)).contents(),x.empty(),S=B(V,e));if(H.template)if(E=!0,fb("template",I,H,x),I=H,V=N(H.template)?H.template(x,d):H.template,V=W(V),H.replace){g=H;V=Ib.test(V)?A($(V)):[];c=V[0];if(1!=V.length||1!==c.nodeType)throw ja("tplrt",z,"");ra(f,x,c);sa={$attr:{}};V=ca(c,[],sa);var Z=a.splice(Ha+
|
||||
1,a.length-(Ha+1));K&&D(V);a=a.concat(V).concat(Z);v(d,sa);sa=a.length}else x.html(V);if(H.templateUrl)E=!0,fb("template",I,H,x),I=H,H.replace&&(g=H),J=te(a.splice(Ha,a.length-Ha),x,d,f,F&&S,k,q,{controllerDirectives:O,newIsolateScopeDirective:K,templateDirective:I,nonTlbTranscludeDirective:ea}),sa=a.length;else if(H.compile)try{R=H.compile(x,d,S),N(R)?w(null,R,U,Y):R&&w(R.pre,R.post,U,Y)}catch(ve){l(ve,ia(x))}H.terminal&&(J.terminal=!0,y=Math.max(y,H.priority))}J.scope=M&&!0===M.scope;J.transcludeOnThisElement=
|
||||
F;J.templateOnThisElement=E;J.transclude=S;n.hasElementTranscludeDirective=Ia;return J}function D(a){for(var b=0,c=a.length;b<c;b++)a[b]=$b(a[b],{$$isolateScope:!0})}function ea(b,e,f,g,p,m,n){if(e===p)return null;p=null;if(c.hasOwnProperty(e)){var q;e=a.get(e+d);for(var w=0,s=e.length;w<s;w++)try{q=e[w],(g===u||g>q.priority)&&-1!=q.restrict.indexOf(f)&&(m&&(q=$b(q,{$$start:m,$$end:n})),b.push(q),p=q)}catch(y){l(y)}}return p}function v(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;r(a,function(d,e){"$"!=
|
||||
e.charAt(0)&&(b[e]&&b[e]!==d&&(d+=("style"===e?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});r(b,function(b,f){"class"==f?(ba(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):"style"==f?(e.attr("style",e.attr("style")+";"+b),a.style=(a.style?a.style+";":"")+b):"$"==f.charAt(0)||a.hasOwnProperty(f)||(a[f]=b,d[f]=c[f])})}function te(a,b,c,d,e,f,g,k){var p=[],l,m,w=b[0],s=a.shift(),y=E({},s,{templateUrl:null,transclude:null,replace:null,$$originalDirective:s}),J=N(s.templateUrl)?s.templateUrl(b,c):s.templateUrl;
|
||||
b.empty();n.get(t.getTrustedResourceUrl(J),{cache:q}).success(function(q){var n,t;q=W(q);if(s.replace){q=Ib.test(q)?A($(q)):[];n=q[0];if(1!=q.length||1!==n.nodeType)throw ja("tplrt",s.name,J);q={$attr:{}};ra(d,b,n);var B=ca(n,[],q);T(s.scope)&&D(B);a=B.concat(a);v(c,q)}else n=w,b.html(q);a.unshift(y);l=I(a,n,c,e,b,s,f,g,k);r(d,function(a,c){a==n&&(d[c]=b[0])});for(m=O(b[0].childNodes,e);p.length;){q=p.shift();t=p.shift();var K=p.shift(),C=p.shift(),B=b[0];if(t!==w){var P=t.className;k.hasElementTranscludeDirective&&
|
||||
s.replace||(B=Kb(n));ra(K,A(t),B);ba(A(B),P)}t=l.transcludeOnThisElement?M(q,l.transclude,C):C;l(m,q,B,d,t)}p=null}).error(function(a,b,c,d){throw ja("tpload",d.url);});return function(a,b,c,d,e){a=e;p?(p.push(b),p.push(c),p.push(d),p.push(a)):(l.transcludeOnThisElement&&(a=M(b,l.transclude,e)),l(m,b,c,d,a))}}function F(a,b){var c=b.priority-a.priority;return 0!==c?c:a.name!==b.name?a.name<b.name?-1:1:a.index-b.index}function fb(a,b,c,d){if(b)throw ja("multidir",b.name,c.name,a,ia(d));}function x(a,
|
||||
c){var d=b(c,!0);d&&a.push({priority:0,compile:function(a){var b=a.parent().length;b&&ba(a.parent(),"ng-binding");return function(a,c){var e=c.parent(),f=e.data("$binding")||[];f.push(d);e.data("$binding",f);b||ba(e,"ng-binding");a.$watch(d,function(a){c[0].nodeValue=a})}}})}function z(a,b){if("srcdoc"==b)return t.HTML;var c=Pa(a);if("xlinkHref"==b||"FORM"==c&&"action"==b||"IMG"!=c&&("src"==b||"ngSrc"==b))return t.RESOURCE_URL}function S(a,c,d,e){var f=b(d,!0);if(f){if("multiple"===e&&"SELECT"===
|
||||
Pa(a))throw ja("selmulti",ia(a));c.push({priority:100,compile:function(){return{pre:function(c,d,k){d=k.$$observers||(k.$$observers={});if(g.test(e))throw ja("nodomevents");if(f=b(k[e],!0,z(a,e)))k[e]=f(c),(d[e]||(d[e]=[])).$$inter=!0,(k.$$observers&&k.$$observers[e].$$scope||c).$watch(f,function(a,b){"class"===e&&a!=b?k.$updateClass(a,b):k.$set(e,a)})}}}})}}function ra(a,b,c){var d=b[0],e=b.length,f=d.parentNode,g,k;if(a)for(g=0,k=a.length;g<k;g++)if(a[g]==d){a[g++]=c;k=g+e-1;for(var p=a.length;g<
|
||||
p;g++,k++)k<p?a[g]=a[k]:delete a[g];a.length-=e-1;break}f&&f.replaceChild(c,d);a=X.createDocumentFragment();a.appendChild(d);c[A.expando]=d[A.expando];d=1;for(e=b.length;d<e;d++)f=b[d],A(f).remove(),a.appendChild(f),delete b[d];b[0]=c;b.length=1}function rc(a,b){return E(function(){return a.apply(null,arguments)},a,b)}var Ob=function(a,b){this.$$element=a;this.$attr=b||{}};Ob.prototype={$normalize:qa,$addClass:function(a){a&&0<a.length&&y.addClass(this.$$element,a)},$removeClass:function(a){a&&0<
|
||||
a.length&&y.removeClass(this.$$element,a)},$updateClass:function(a,b){var c=sc(a,b),d=sc(b,a);0===c.length?y.removeClass(this.$$element,d):0===d.length?y.addClass(this.$$element,c):y.setClass(this.$$element,c,d)},$set:function(a,b,c,d){var e=oc(this.$$element[0],a);e&&(this.$$element.prop(a,b),d=e);this[a]=b;d?this.$attr[a]=d:(d=this.$attr[a])||(this.$attr[a]=d=nb(a,"-"));e=Pa(this.$$element);if("A"===e&&"href"===a||"IMG"===e&&"src"===a)this[a]=b=K(b,"src"===a);!1!==c&&(null===b||b===u?this.$$element.removeAttr(d):
|
||||
this.$$element.attr(d,b));(c=this.$$observers)&&r(c[a],function(a){try{a(b)}catch(c){l(c)}})},$observe:function(a,b){var c=this,d=c.$$observers||(c.$$observers={}),e=d[a]||(d[a]=[]);e.push(b);J.$evalAsync(function(){e.$$inter||b(c[a])});return b}};var sa=b.startSymbol(),Ia=b.endSymbol(),W="{{"==sa||"}}"==Ia?ga:function(a){return a.replace(/\{\{/g,sa).replace(/}}/g,Ia)},U=/^ngAttr[A-Z]/;return B}]}function qa(b){return ab(b.replace(we,""))}function sc(b,a){var c="",d=b.split(/\s+/),e=a.split(/\s+/),
|
||||
f=0;a:for(;f<d.length;f++){for(var g=d[f],h=0;h<e.length;h++)if(g==e[h])continue a;c+=(0<c.length?" ":"")+g}return c}function Od(){var b={},a=/^(\S+)(\s+as\s+(\w+))?$/;this.register=function(a,d){Ea(a,"controller");T(a)?E(b,a):b[a]=d};this.$get=["$injector","$window",function(c,d){return function(e,f){var g,h,k;G(e)&&(g=e.match(a),h=g[1],k=g[3],e=b.hasOwnProperty(h)?b[h]:fc(f.$scope,h,!0)||fc(d,h,!0),Ya(e,h,!0));g=c.instantiate(e,f);if(k){if(!f||"object"!==typeof f.$scope)throw z("$controller")("noscp",
|
||||
h||e.name,k);f.$scope[k]=g}return g}}]}function Pd(){this.$get=["$window",function(b){return A(b.document)}]}function Qd(){this.$get=["$log",function(b){return function(a,c){b.error.apply(b,arguments)}}]}function tc(b){var a={},c,d,e;if(!b)return a;r(b.split("\n"),function(b){e=b.indexOf(":");c=x($(b.substr(0,e)));d=$(b.substr(e+1));c&&(a[c]=a[c]?a[c]+", "+d:d)});return a}function uc(b){var a=T(b)?b:u;return function(c){a||(a=tc(b));return c?a[x(c)]||null:a}}function vc(b,a,c){if(N(c))return c(b,
|
||||
a);r(c,function(c){b=c(b,a)});return b}function Td(){var b=/^\s*(\[|\{[^\{])/,a=/[\}\]]\s*$/,c=/^\)\]\}',?\n/,d={"Content-Type":"application/json;charset=utf-8"},e=this.defaults={transformResponse:[function(d){G(d)&&(d=d.replace(c,""),b.test(d)&&a.test(d)&&(d=ac(d)));return d}],transformRequest:[function(a){return T(a)&&"[object File]"!==Ba.call(a)&&"[object Blob]"!==Ba.call(a)?oa(a):a}],headers:{common:{Accept:"application/json, text/plain, */*"},post:ha(d),put:ha(d),patch:ha(d)},xsrfCookieName:"XSRF-TOKEN",
|
||||
xsrfHeaderName:"X-XSRF-TOKEN"},f=this.interceptors=[],g=this.responseInterceptors=[];this.$get=["$httpBackend","$browser","$cacheFactory","$rootScope","$q","$injector",function(a,b,c,d,n,q){function p(a){function b(a){var d=E({},a,{data:vc(a.data,a.headers,c.transformResponse)});return 200<=a.status&&300>a.status?d:n.reject(d)}var c={method:"get",transformRequest:e.transformRequest,transformResponse:e.transformResponse},d=function(a){var b=e.headers,c=E({},a.headers),d,f,b=E({},b.common,b[x(a.method)]);
|
||||
a:for(d in b){a=x(d);for(f in c)if(x(f)===a)continue a;c[d]=b[d]}(function(a){var b;r(a,function(c,d){N(c)&&(b=c(),null!=b?a[d]=b:delete a[d])})})(c);return c}(a);E(c,a);c.headers=d;c.method=La(c.method);var f=[function(a){d=a.headers;var c=vc(a.data,uc(d),a.transformRequest);F(c)&&r(d,function(a,b){"content-type"===x(b)&&delete d[b]});F(a.withCredentials)&&!F(e.withCredentials)&&(a.withCredentials=e.withCredentials);return s(a,c,d).then(b,b)},u],g=n.when(c);for(r(t,function(a){(a.request||a.requestError)&&
|
||||
f.unshift(a.request,a.requestError);(a.response||a.responseError)&&f.push(a.response,a.responseError)});f.length;){a=f.shift();var h=f.shift(),g=g.then(a,h)}g.success=function(a){g.then(function(b){a(b.data,b.status,b.headers,c)});return g};g.error=function(a){g.then(null,function(b){a(b.data,b.status,b.headers,c)});return g};return g}function s(c,f,g){function m(a,b,c,e){C&&(200<=a&&300>a?C.put(A,[a,b,tc(c),e]):C.remove(A));q(b,a,c,e);d.$$phase||d.$apply()}function q(a,b,d,e){b=Math.max(b,0);(200<=
|
||||
b&&300>b?t.resolve:t.reject)({data:a,status:b,headers:uc(d),config:c,statusText:e})}function s(){var a=Ta(p.pendingRequests,c);-1!==a&&p.pendingRequests.splice(a,1)}var t=n.defer(),r=t.promise,C,I,A=J(c.url,c.params);p.pendingRequests.push(c);r.then(s,s);!c.cache&&!e.cache||(!1===c.cache||"GET"!==c.method&&"JSONP"!==c.method)||(C=T(c.cache)?c.cache:T(e.cache)?e.cache:w);if(C)if(I=C.get(A),D(I)){if(I&&N(I.then))return I.then(s,s),I;L(I)?q(I[1],I[0],ha(I[2]),I[3]):q(I,200,{},"OK")}else C.put(A,r);F(I)&&
|
||||
((I=Pb(c.url)?b.cookies()[c.xsrfCookieName||e.xsrfCookieName]:u)&&(g[c.xsrfHeaderName||e.xsrfHeaderName]=I),a(c.method,A,f,m,g,c.timeout,c.withCredentials,c.responseType));return r}function J(a,b){if(!b)return a;var c=[];Sc(b,function(a,b){null===a||F(a)||(L(a)||(a=[a]),r(a,function(a){T(a)&&(a=va(a)?a.toISOString():oa(a));c.push(Da(b)+"="+Da(a))}))});0<c.length&&(a+=(-1==a.indexOf("?")?"?":"&")+c.join("&"));return a}var w=c("$http"),t=[];r(f,function(a){t.unshift(G(a)?q.get(a):q.invoke(a))});r(g,
|
||||
function(a,b){var c=G(a)?q.get(a):q.invoke(a);t.splice(b,0,{response:function(a){return c(n.when(a))},responseError:function(a){return c(n.reject(a))}})});p.pendingRequests=[];(function(a){r(arguments,function(a){p[a]=function(b,c){return p(E(c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){r(arguments,function(a){p[a]=function(b,c,d){return p(E(d||{},{method:a,url:b,data:c}))}})})("post","put","patch");p.defaults=e;return p}]}function xe(b){if(8>=R&&(!b.match(/^(get|post|head|put|delete|options)$/i)||
|
||||
!W.XMLHttpRequest))return new W.ActiveXObject("Microsoft.XMLHTTP");if(W.XMLHttpRequest)return new W.XMLHttpRequest;throw z("$httpBackend")("noxhr");}function Ud(){this.$get=["$browser","$window","$document",function(b,a,c){return ye(b,xe,b.defer,a.angular.callbacks,c[0])}]}function ye(b,a,c,d,e){function f(a,b,c){var f=e.createElement("script"),g=null;f.type="text/javascript";f.src=a;f.async=!0;g=function(a){bb(f,"load",g);bb(f,"error",g);e.body.removeChild(f);f=null;var h=-1,s="unknown";a&&("load"!==
|
||||
a.type||d[b].called||(a={type:"error"}),s=a.type,h="error"===a.type?404:200);c&&c(h,s)};sb(f,"load",g);sb(f,"error",g);8>=R&&(f.onreadystatechange=function(){G(f.readyState)&&/loaded|complete/.test(f.readyState)&&(f.onreadystatechange=null,g({type:"load"}))});e.body.appendChild(f);return g}var g=-1;return function(e,k,m,l,n,q,p,s){function J(){t=g;K&&K();B&&B.abort()}function w(a,d,e,f,g){O&&c.cancel(O);K=B=null;0===d&&(d=e?200:"file"==xa(k).protocol?404:0);a(1223===d?204:d,e,f,g||"");b.$$completeOutstandingRequest(v)}
|
||||
var t;b.$$incOutstandingRequestCount();k=k||b.url();if("jsonp"==x(e)){var y="_"+(d.counter++).toString(36);d[y]=function(a){d[y].data=a;d[y].called=!0};var K=f(k.replace("JSON_CALLBACK","angular.callbacks."+y),y,function(a,b){w(l,a,d[y].data,"",b);d[y]=v})}else{var B=a(e);B.open(e,k,!0);r(n,function(a,b){D(a)&&B.setRequestHeader(b,a)});B.onreadystatechange=function(){if(B&&4==B.readyState){var a=null,b=null,c="";t!==g&&(a=B.getAllResponseHeaders(),b="response"in B?B.response:B.responseText);t===g&&
|
||||
10>R||(c=B.statusText);w(l,t||B.status,b,a,c)}};p&&(B.withCredentials=!0);if(s)try{B.responseType=s}catch(ba){if("json"!==s)throw ba;}B.send(m||null)}if(0<q)var O=c(J,q);else q&&N(q.then)&&q.then(J)}}function Rd(){var b="{{",a="}}";this.startSymbol=function(a){return a?(b=a,this):b};this.endSymbol=function(b){return b?(a=b,this):a};this.$get=["$parse","$exceptionHandler","$sce",function(c,d,e){function f(f,m,l){for(var n,q,p=0,s=[],J=f.length,w=!1,t=[];p<J;)-1!=(n=f.indexOf(b,p))&&-1!=(q=f.indexOf(a,
|
||||
n+g))?(p!=n&&s.push(f.substring(p,n)),s.push(p=c(w=f.substring(n+g,q))),p.exp=w,p=q+h,w=!0):(p!=J&&s.push(f.substring(p)),p=J);(J=s.length)||(s.push(""),J=1);if(l&&1<s.length)throw wc("noconcat",f);if(!m||w)return t.length=J,p=function(a){try{for(var b=0,c=J,g;b<c;b++){if("function"==typeof(g=s[b]))if(g=g(a),g=l?e.getTrusted(l,g):e.valueOf(g),null==g)g="";else switch(typeof g){case "string":break;case "number":g=""+g;break;default:g=oa(g)}t[b]=g}return t.join("")}catch(h){a=wc("interr",f,h.toString()),
|
||||
d(a)}},p.exp=f,p.parts=s,p}var g=b.length,h=a.length;f.startSymbol=function(){return b};f.endSymbol=function(){return a};return f}]}function Sd(){this.$get=["$rootScope","$window","$q",function(b,a,c){function d(d,g,h,k){var m=a.setInterval,l=a.clearInterval,n=c.defer(),q=n.promise,p=0,s=D(k)&&!k;h=D(h)?h:0;q.then(null,null,d);q.$$intervalId=m(function(){n.notify(p++);0<h&&p>=h&&(n.resolve(p),l(q.$$intervalId),delete e[q.$$intervalId]);s||b.$apply()},g);e[q.$$intervalId]=n;return q}var e={};d.cancel=
|
||||
function(b){return b&&b.$$intervalId in e?(e[b.$$intervalId].reject("canceled"),a.clearInterval(b.$$intervalId),delete e[b.$$intervalId],!0):!1};return d}]}function ad(){this.$get=function(){return{id:"en-us",NUMBER_FORMATS:{DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{minInt:1,minFrac:0,maxFrac:3,posPre:"",posSuf:"",negPre:"-",negSuf:"",gSize:3,lgSize:3},{minInt:1,minFrac:2,maxFrac:2,posPre:"\u00a4",posSuf:"",negPre:"(\u00a4",negSuf:")",gSize:3,lgSize:3}],CURRENCY_SYM:"$"},DATETIME_FORMATS:{MONTH:"January February March April May June July August September October November December".split(" "),
|
||||
SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),AMPMS:["AM","PM"],medium:"MMM d, y h:mm:ss a","short":"M/d/yy h:mm a",fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",mediumDate:"MMM d, y",shortDate:"M/d/yy",mediumTime:"h:mm:ss a",shortTime:"h:mm a"},pluralCat:function(b){return 1===b?"one":"other"}}}}function Qb(b){b=b.split("/");for(var a=b.length;a--;)b[a]=
|
||||
mb(b[a]);return b.join("/")}function xc(b,a,c){b=xa(b,c);a.$$protocol=b.protocol;a.$$host=b.hostname;a.$$port=U(b.port)||ze[b.protocol]||null}function yc(b,a,c){var d="/"!==b.charAt(0);d&&(b="/"+b);b=xa(b,c);a.$$path=decodeURIComponent(d&&"/"===b.pathname.charAt(0)?b.pathname.substring(1):b.pathname);a.$$search=cc(b.search);a.$$hash=decodeURIComponent(b.hash);a.$$path&&"/"!=a.$$path.charAt(0)&&(a.$$path="/"+a.$$path)}function ta(b,a){if(0===a.indexOf(b))return a.substr(b.length)}function Ga(b){var a=
|
||||
b.indexOf("#");return-1==a?b:b.substr(0,a)}function Rb(b){return b.substr(0,Ga(b).lastIndexOf("/")+1)}function zc(b,a){this.$$html5=!0;a=a||"";var c=Rb(b);xc(b,this,b);this.$$parse=function(a){var e=ta(c,a);if(!G(e))throw Sb("ipthprfx",a,c);yc(e,this,b);this.$$path||(this.$$path="/");this.$$compose()};this.$$compose=function(){var a=Cb(this.$$search),b=this.$$hash?"#"+mb(this.$$hash):"";this.$$url=Qb(this.$$path)+(a?"?"+a:"")+b;this.$$absUrl=c+this.$$url.substr(1)};this.$$parseLinkUrl=function(d,
|
||||
e){var f,g;(f=ta(b,d))!==u?(g=f,g=(f=ta(a,f))!==u?c+(ta("/",f)||f):b+g):(f=ta(c,d))!==u?g=c+f:c==d+"/"&&(g=c);g&&this.$$parse(g);return!!g}}function Tb(b,a){var c=Rb(b);xc(b,this,b);this.$$parse=function(d){var e=ta(b,d)||ta(c,d),e="#"==e.charAt(0)?ta(a,e):this.$$html5?e:"";if(!G(e))throw Sb("ihshprfx",d,a);yc(e,this,b);d=this.$$path;var f=/^\/[A-Z]:(\/.*)/;0===e.indexOf(b)&&(e=e.replace(b,""));f.exec(e)||(d=(e=f.exec(d))?e[1]:d);this.$$path=d;this.$$compose()};this.$$compose=function(){var c=Cb(this.$$search),
|
||||
e=this.$$hash?"#"+mb(this.$$hash):"";this.$$url=Qb(this.$$path)+(c?"?"+c:"")+e;this.$$absUrl=b+(this.$$url?a+this.$$url:"")};this.$$parseLinkUrl=function(a,c){return Ga(b)==Ga(a)?(this.$$parse(a),!0):!1}}function Ac(b,a){this.$$html5=!0;Tb.apply(this,arguments);var c=Rb(b);this.$$parseLinkUrl=function(d,e){var f,g;b==Ga(d)?f=d:(g=ta(c,d))?f=b+a+g:c===d+"/"&&(f=c);f&&this.$$parse(f);return!!f};this.$$compose=function(){var c=Cb(this.$$search),e=this.$$hash?"#"+mb(this.$$hash):"";this.$$url=Qb(this.$$path)+
|
||||
(c?"?"+c:"")+e;this.$$absUrl=b+a+this.$$url}}function tb(b){return function(){return this[b]}}function Bc(b,a){return function(c){if(F(c))return this[b];this[b]=a(c);this.$$compose();return this}}function Vd(){var b="",a=!1;this.hashPrefix=function(a){return D(a)?(b=a,this):b};this.html5Mode=function(b){return D(b)?(a=b,this):a};this.$get=["$rootScope","$browser","$sniffer","$rootElement",function(c,d,e,f){function g(a){c.$broadcast("$locationChangeSuccess",h.absUrl(),a)}var h,k=d.baseHref(),m=d.url();
|
||||
a?(k=m.substring(0,m.indexOf("/",m.indexOf("//")+2))+(k||"/"),e=e.history?zc:Ac):(k=Ga(m),e=Tb);h=new e(k,"#"+b);h.$$parseLinkUrl(m,m);var l=/^\s*(javascript|mailto):/i;f.on("click",function(a){if(!a.ctrlKey&&!a.metaKey&&2!=a.which){for(var b=A(a.target);"a"!==x(b[0].nodeName);)if(b[0]===f[0]||!(b=b.parent())[0])return;var e=b.prop("href"),g=b.attr("href")||b.attr("xlink:href");T(e)&&"[object SVGAnimatedString]"===e.toString()&&(e=xa(e.animVal).href);l.test(e)||(!e||(b.attr("target")||a.isDefaultPrevented())||
|
||||
!h.$$parseLinkUrl(e,g))||(a.preventDefault(),h.absUrl()!=d.url()&&(c.$apply(),W.angular["ff-684208-preventDefault"]=!0))}});h.absUrl()!=m&&d.url(h.absUrl(),!0);d.onUrlChange(function(a){h.absUrl()!=a&&(c.$evalAsync(function(){var b=h.absUrl();h.$$parse(a);c.$broadcast("$locationChangeStart",a,b).defaultPrevented?(h.$$parse(b),d.url(b)):g(b)}),c.$$phase||c.$digest())});var n=0;c.$watch(function(){var a=d.url(),b=h.$$replace;n&&a==h.absUrl()||(n++,c.$evalAsync(function(){c.$broadcast("$locationChangeStart",
|
||||
h.absUrl(),a).defaultPrevented?h.$$parse(a):(d.url(h.absUrl(),b),g(a))}));h.$$replace=!1;return n});return h}]}function Wd(){var b=!0,a=this;this.debugEnabled=function(a){return D(a)?(b=a,this):b};this.$get=["$window",function(c){function d(a){a instanceof Error&&(a.stack?a=a.message&&-1===a.stack.indexOf(a.message)?"Error: "+a.message+"\n"+a.stack:a.stack:a.sourceURL&&(a=a.message+"\n"+a.sourceURL+":"+a.line));return a}function e(a){var b=c.console||{},e=b[a]||b.log||v;a=!1;try{a=!!e.apply}catch(k){}return a?
|
||||
function(){var a=[];r(arguments,function(b){a.push(d(b))});return e.apply(b,a)}:function(a,b){e(a,null==b?"":b)}}return{log:e("log"),info:e("info"),warn:e("warn"),error:e("error"),debug:function(){var c=e("debug");return function(){b&&c.apply(a,arguments)}}()}}]}function ka(b,a){if("__defineGetter__"===b||"__defineSetter__"===b||"__lookupGetter__"===b||"__lookupSetter__"===b||"__proto__"===b)throw la("isecfld",a);return b}function ma(b,a){if(b){if(b.constructor===b)throw la("isecfn",a);if(b.document&&
|
||||
b.location&&b.alert&&b.setInterval)throw la("isecwindow",a);if(b.children&&(b.nodeName||b.prop&&b.attr&&b.find))throw la("isecdom",a);if(b===Object)throw la("isecobj",a);}return b}function ub(b,a,c,d,e){ma(b,d);e=e||{};a=a.split(".");for(var f,g=0;1<a.length;g++){f=ka(a.shift(),d);var h=ma(b[f],d);h||(h={},b[f]=h);b=h;b.then&&e.unwrapPromises&&(ya(d),"$$v"in b||function(a){a.then(function(b){a.$$v=b})}(b),b.$$v===u&&(b.$$v={}),b=b.$$v)}f=ka(a.shift(),d);ma(b[f],d);return b[f]=c}function Qa(b){return"constructor"==
|
||||
b}function Cc(b,a,c,d,e,f,g){ka(b,f);ka(a,f);ka(c,f);ka(d,f);ka(e,f);var h=function(a){return ma(a,f)},k=g.expensiveChecks,m=k||Qa(b)?h:ga,l=k||Qa(a)?h:ga,n=k||Qa(c)?h:ga,q=k||Qa(d)?h:ga,p=k||Qa(e)?h:ga;return g.unwrapPromises?function(g,h){var k=h&&h.hasOwnProperty(b)?h:g,t;if(null==k)return k;(k=m(k[b]))&&k.then&&(ya(f),"$$v"in k||(t=k,t.$$v=u,t.then(function(a){t.$$v=m(a)})),k=m(k.$$v));if(!a)return k;if(null==k)return u;(k=l(k[a]))&&k.then&&(ya(f),"$$v"in k||(t=k,t.$$v=u,t.then(function(a){t.$$v=
|
||||
l(a)})),k=l(k.$$v));if(!c)return k;if(null==k)return u;(k=n(k[c]))&&k.then&&(ya(f),"$$v"in k||(t=k,t.$$v=u,t.then(function(a){t.$$v=n(a)})),k=n(k.$$v));if(!d)return k;if(null==k)return u;(k=q(k[d]))&&k.then&&(ya(f),"$$v"in k||(t=k,t.$$v=u,t.then(function(a){t.$$v=q(a)})),k=q(k.$$v));if(!e)return k;if(null==k)return u;(k=p(k[e]))&&k.then&&(ya(f),"$$v"in k||(t=k,t.$$v=u,t.then(function(a){t.$$v=p(a)})),k=p(k.$$v));return k}:function(f,g){var h=g&&g.hasOwnProperty(b)?g:f;if(null==h)return h;h=m(h[b]);
|
||||
if(!a)return h;if(null==h)return u;h=l(h[a]);if(!c)return h;if(null==h)return u;h=n(h[c]);if(!d)return h;if(null==h)return u;h=q(h[d]);return e?null==h?u:h=p(h[e]):h}}function Ae(b,a){return function(c,d){return b(c,d,ya,ma,a)}}function Dc(b,a,c){var d=a.expensiveChecks,e=d?Be:Ce;if(e.hasOwnProperty(b))return e[b];var f=b.split("."),g=f.length,h;if(a.csp)h=6>g?Cc(f[0],f[1],f[2],f[3],f[4],c,a):function(b,d){var e=0,h;do h=Cc(f[e++],f[e++],f[e++],f[e++],f[e++],c,a)(b,d),d=u,b=h;while(e<g);return h};
|
||||
else{var k="var p;\n";d&&(k+="s = eso(s, fe);\nl = eso(l, fe);\n");var m=d;r(f,function(b,e){ka(b,c);var f=(e?"s":'((l&&l.hasOwnProperty("'+b+'"))?l:s)')+'["'+b+'"]',g=d||Qa(b);g&&(f="eso("+f+", fe)",m=!0);k+="if(s == null) return undefined;\ns="+f+";\n";a.unwrapPromises&&(k+='if (s && s.then) {\n pw("'+c.replace(/(["\r\n])/g,"\\$1")+'");\n if (!("$$v" in s)) {\n p=s;\n p.$$v = undefined;\n p.then(function(v) {p.$$v='+(g?"eso(v)":"v")+";});\n}\n s="+(g?"eso(s.$$v)":"s.$$v")+"\n}\n")});k+="return s;";
|
||||
h=new Function("s","l","pw","eso","fe",k);h.toString=aa(k);if(m||a.unwrapPromises)h=Ae(h,c)}"hasOwnProperty"!==b&&(e[b]=h);return h}function Xd(){var b={},a={},c={csp:!1,unwrapPromises:!1,logPromiseWarnings:!0,expensiveChecks:!1};this.unwrapPromises=function(a){return D(a)?(c.unwrapPromises=!!a,this):c.unwrapPromises};this.logPromiseWarnings=function(a){return D(a)?(c.logPromiseWarnings=a,this):c.logPromiseWarnings};this.$get=["$filter","$sniffer","$log",function(d,e,f){c.csp=e.csp;var g={csp:c.csp,
|
||||
unwrapPromises:c.unwrapPromises,logPromiseWarnings:c.logPromiseWarnings,expensiveChecks:!0};ya=function(a){c.logPromiseWarnings&&!Ec.hasOwnProperty(a)&&(Ec[a]=!0,f.warn("[$parse] Promise found in the expression `"+a+"`. Automatic unwrapping of promises in Angular expressions is deprecated."))};return function(e,f){var m;switch(typeof e){case "string":var l=f?a:b;if(l.hasOwnProperty(e))return l[e];m=f?g:c;var n=new Ub(m);m=(new gb(n,d,m)).parse(e);"hasOwnProperty"!==e&&(l[e]=m);return m;case "function":return e;
|
||||
default:return v}}}]}function Zd(){this.$get=["$rootScope","$exceptionHandler",function(b,a){return De(function(a){b.$evalAsync(a)},a)}]}function De(b,a){function c(a){return a}function d(a){return g(a)}var e=function(){var g=[],m,l;return l={resolve:function(a){if(g){var c=g;g=u;m=f(a);c.length&&b(function(){for(var a,b=0,d=c.length;b<d;b++)a=c[b],m.then(a[0],a[1],a[2])})}},reject:function(a){l.resolve(h(a))},notify:function(a){if(g){var c=g;g.length&&b(function(){for(var b,d=0,e=c.length;d<e;d++)b=
|
||||
c[d],b[2](a)})}},promise:{then:function(b,f,h){var l=e(),J=function(d){try{l.resolve((N(b)?b:c)(d))}catch(e){l.reject(e),a(e)}},w=function(b){try{l.resolve((N(f)?f:d)(b))}catch(c){l.reject(c),a(c)}},t=function(b){try{l.notify((N(h)?h:c)(b))}catch(d){a(d)}};g?g.push([J,w,t]):m.then(J,w,t);return l.promise},"catch":function(a){return this.then(null,a)},"finally":function(a){function b(a,c){var d=e();c?d.resolve(a):d.reject(a);return d.promise}function d(e,f){var g=null;try{g=(a||c)()}catch(h){return b(h,
|
||||
!1)}return g&&N(g.then)?g.then(function(){return b(e,f)},function(a){return b(a,!1)}):b(e,f)}return this.then(function(a){return d(a,!0)},function(a){return d(a,!1)})}}}},f=function(a){return a&&N(a.then)?a:{then:function(c){var d=e();b(function(){d.resolve(c(a))});return d.promise}}},g=function(a){var b=e();b.reject(a);return b.promise},h=function(c){return{then:function(f,g){var h=e();b(function(){try{h.resolve((N(g)?g:d)(c))}catch(b){h.reject(b),a(b)}});return h.promise}}};return{defer:e,reject:g,
|
||||
when:function(h,m,l,n){var q=e(),p,s=function(b){try{return(N(m)?m:c)(b)}catch(d){return a(d),g(d)}},J=function(b){try{return(N(l)?l:d)(b)}catch(c){return a(c),g(c)}},w=function(b){try{return(N(n)?n:c)(b)}catch(d){a(d)}};b(function(){f(h).then(function(a){p||(p=!0,q.resolve(f(a).then(s,J,w)))},function(a){p||(p=!0,q.resolve(J(a)))},function(a){p||q.notify(w(a))})});return q.promise},all:function(a){var b=e(),c=0,d=L(a)?[]:{};r(a,function(a,e){c++;f(a).then(function(a){d.hasOwnProperty(e)||(d[e]=a,
|
||||
--c||b.resolve(d))},function(a){d.hasOwnProperty(e)||b.reject(a)})});0===c&&b.resolve(d);return b.promise}}}function fe(){this.$get=["$window","$timeout",function(b,a){var c=b.requestAnimationFrame||b.webkitRequestAnimationFrame||b.mozRequestAnimationFrame,d=b.cancelAnimationFrame||b.webkitCancelAnimationFrame||b.mozCancelAnimationFrame||b.webkitCancelRequestAnimationFrame,e=!!c,f=e?function(a){var b=c(a);return function(){d(b)}}:function(b){var c=a(b,16.66,!1);return function(){a.cancel(c)}};f.supported=
|
||||
e;return f}]}function Yd(){var b=10,a=z("$rootScope"),c=null;this.digestTtl=function(a){arguments.length&&(b=a);return b};this.$get=["$injector","$exceptionHandler","$parse","$browser",function(d,e,f,g){function h(){this.$id=ib();this.$$phase=this.$parent=this.$$watchers=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=null;this["this"]=this.$root=this;this.$$destroyed=!1;this.$$asyncQueue=[];this.$$postDigestQueue=[];this.$$listeners={};this.$$listenerCount={};this.$$isolateBindings=
|
||||
{}}function k(b){if(q.$$phase)throw a("inprog",q.$$phase);q.$$phase=b}function m(a,b){var c=f(a);Ya(c,b);return c}function l(a,b,c){do a.$$listenerCount[c]-=b,0===a.$$listenerCount[c]&&delete a.$$listenerCount[c];while(a=a.$parent)}function n(){}h.prototype={constructor:h,$new:function(a){a?(a=new h,a.$root=this.$root,a.$$asyncQueue=this.$$asyncQueue,a.$$postDigestQueue=this.$$postDigestQueue):(this.$$childScopeClass||(this.$$childScopeClass=function(){this.$$watchers=this.$$nextSibling=this.$$childHead=
|
||||
this.$$childTail=null;this.$$listeners={};this.$$listenerCount={};this.$id=ib();this.$$childScopeClass=null},this.$$childScopeClass.prototype=this),a=new this.$$childScopeClass);a["this"]=a;a.$parent=this;a.$$prevSibling=this.$$childTail;this.$$childHead?this.$$childTail=this.$$childTail.$$nextSibling=a:this.$$childHead=this.$$childTail=a;return a},$watch:function(a,b,d){var e=m(a,"watch"),f=this.$$watchers,g={fn:b,last:n,get:e,exp:a,eq:!!d};c=null;if(!N(b)){var h=m(b||v,"listener");g.fn=function(a,
|
||||
b,c){h(c)}}if("string"==typeof a&&e.constant){var k=g.fn;g.fn=function(a,b,c){k.call(this,a,b,c);Ua(f,g)}}f||(f=this.$$watchers=[]);f.unshift(g);return function(){Ua(f,g);c=null}},$watchCollection:function(a,b){var c=this,d,e,g,h=1<b.length,k=0,l=f(a),m=[],n={},q=!0,r=0;return this.$watch(function(){d=l(c);var a,b,f;if(T(d))if(Sa(d))for(e!==m&&(e=m,r=e.length=0,k++),a=d.length,r!==a&&(k++,e.length=r=a),b=0;b<a;b++)f=e[b]!==e[b]&&d[b]!==d[b],f||e[b]===d[b]||(k++,e[b]=d[b]);else{e!==n&&(e=n={},r=0,
|
||||
k++);a=0;for(b in d)d.hasOwnProperty(b)&&(a++,e.hasOwnProperty(b)?(f=e[b]!==e[b]&&d[b]!==d[b],f||e[b]===d[b]||(k++,e[b]=d[b])):(r++,e[b]=d[b],k++));if(r>a)for(b in k++,e)e.hasOwnProperty(b)&&!d.hasOwnProperty(b)&&(r--,delete e[b])}else e!==d&&(e=d,k++);return k},function(){q?(q=!1,b(d,d,c)):b(d,g,c);if(h)if(T(d))if(Sa(d)){g=Array(d.length);for(var a=0;a<d.length;a++)g[a]=d[a]}else for(a in g={},d)lb.call(d,a)&&(g[a]=d[a]);else g=d})},$digest:function(){var d,f,h,l,m=this.$$asyncQueue,r=this.$$postDigestQueue,
|
||||
K,B,u=b,O,M=[],A,P,C;k("$digest");g.$$checkUrlChange();c=null;do{B=!1;for(O=this;m.length;){try{C=m.shift(),C.scope.$eval(C.expression)}catch(I){q.$$phase=null,e(I)}c=null}a:do{if(l=O.$$watchers)for(K=l.length;K--;)try{if(d=l[K])if((f=d.get(O))!==(h=d.last)&&!(d.eq?Ca(f,h):"number"===typeof f&&"number"===typeof h&&isNaN(f)&&isNaN(h)))B=!0,c=d,d.last=d.eq?Ka(f,null):f,d.fn(f,h===n?f:h,O),5>u&&(A=4-u,M[A]||(M[A]=[]),P=N(d.exp)?"fn: "+(d.exp.name||d.exp.toString()):d.exp,P+="; newVal: "+oa(f)+"; oldVal: "+
|
||||
oa(h),M[A].push(P));else if(d===c){B=!1;break a}}catch(D){q.$$phase=null,e(D)}if(!(l=O.$$childHead||O!==this&&O.$$nextSibling))for(;O!==this&&!(l=O.$$nextSibling);)O=O.$parent}while(O=l);if((B||m.length)&&!u--)throw q.$$phase=null,a("infdig",b,oa(M));}while(B||m.length);for(q.$$phase=null;r.length;)try{r.shift()()}catch(x){e(x)}},$destroy:function(){if(!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;this!==q&&(r(this.$$listenerCount,Bb(null,l,this)),a.$$childHead==
|
||||
this&&(a.$$childHead=this.$$nextSibling),a.$$childTail==this&&(a.$$childTail=this.$$prevSibling),this.$$prevSibling&&(this.$$prevSibling.$$nextSibling=this.$$nextSibling),this.$$nextSibling&&(this.$$nextSibling.$$prevSibling=this.$$prevSibling),this.$parent=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=this.$root=null,this.$$listeners={},this.$$watchers=this.$$asyncQueue=this.$$postDigestQueue=[],this.$destroy=this.$digest=this.$apply=v,this.$on=this.$watch=function(){return v})}},
|
||||
$eval:function(a,b){return f(a)(this,b)},$evalAsync:function(a){q.$$phase||q.$$asyncQueue.length||g.defer(function(){q.$$asyncQueue.length&&q.$digest()});this.$$asyncQueue.push({scope:this,expression:a})},$$postDigest:function(a){this.$$postDigestQueue.push(a)},$apply:function(a){try{return k("$apply"),this.$eval(a)}catch(b){e(b)}finally{q.$$phase=null;try{q.$digest()}catch(c){throw e(c),c;}}},$on:function(a,b){var c=this.$$listeners[a];c||(this.$$listeners[a]=c=[]);c.push(b);var d=this;do d.$$listenerCount[a]||
|
||||
(d.$$listenerCount[a]=0),d.$$listenerCount[a]++;while(d=d.$parent);var e=this;return function(){var d=Ta(c,b);-1!==d&&(c[d]=null,l(e,1,a))}},$emit:function(a,b){var c=[],d,f=this,g=!1,h={name:a,targetScope:f,stopPropagation:function(){g=!0},preventDefault:function(){h.defaultPrevented=!0},defaultPrevented:!1},k=[h].concat(wa.call(arguments,1)),l,m;do{d=f.$$listeners[a]||c;h.currentScope=f;l=0;for(m=d.length;l<m;l++)if(d[l])try{d[l].apply(null,k)}catch(n){e(n)}else d.splice(l,1),l--,m--;if(g)break;
|
||||
f=f.$parent}while(f);return h},$broadcast:function(a,b){for(var c=this,d=this,f={name:a,targetScope:this,preventDefault:function(){f.defaultPrevented=!0},defaultPrevented:!1},g=[f].concat(wa.call(arguments,1)),h,k;c=d;){f.currentScope=c;d=c.$$listeners[a]||[];h=0;for(k=d.length;h<k;h++)if(d[h])try{d[h].apply(null,g)}catch(l){e(l)}else d.splice(h,1),h--,k--;if(!(d=c.$$listenerCount[a]&&c.$$childHead||c!==this&&c.$$nextSibling))for(;c!==this&&!(d=c.$$nextSibling);)c=c.$parent}return f}};var q=new h;
|
||||
return q}]}function bd(){var b=/^\s*(https?|ftp|mailto|tel|file):/,a=/^\s*((https?|ftp|file):|data:image\/)/;this.aHrefSanitizationWhitelist=function(a){return D(a)?(b=a,this):b};this.imgSrcSanitizationWhitelist=function(b){return D(b)?(a=b,this):a};this.$get=function(){return function(c,d){var e=d?a:b,f;if(!R||8<=R)if(f=xa(c).href,""!==f&&!f.match(e))return"unsafe:"+f;return c}}}function Ee(b){if("self"===b)return b;if(G(b)){if(-1<b.indexOf("***"))throw za("iwcard",b);b=b.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g,
|
||||
"\\$1").replace(/\x08/g,"\\x08").replace("\\*\\*",".*").replace("\\*","[^:/.?&;]*");return RegExp("^"+b+"$")}if(kb(b))return RegExp("^"+b.source+"$");throw za("imatcher");}function Fc(b){var a=[];D(b)&&r(b,function(b){a.push(Ee(b))});return a}function ae(){this.SCE_CONTEXTS=fa;var b=["self"],a=[];this.resourceUrlWhitelist=function(a){arguments.length&&(b=Fc(a));return b};this.resourceUrlBlacklist=function(b){arguments.length&&(a=Fc(b));return a};this.$get=["$injector",function(c){function d(a){var b=
|
||||
function(a){this.$$unwrapTrustedValue=function(){return a}};a&&(b.prototype=new a);b.prototype.valueOf=function(){return this.$$unwrapTrustedValue()};b.prototype.toString=function(){return this.$$unwrapTrustedValue().toString()};return b}var e=function(a){throw za("unsafe");};c.has("$sanitize")&&(e=c.get("$sanitize"));var f=d(),g={};g[fa.HTML]=d(f);g[fa.CSS]=d(f);g[fa.URL]=d(f);g[fa.JS]=d(f);g[fa.RESOURCE_URL]=d(g[fa.URL]);return{trustAs:function(a,b){var c=g.hasOwnProperty(a)?g[a]:null;if(!c)throw za("icontext",
|
||||
a,b);if(null===b||b===u||""===b)return b;if("string"!==typeof b)throw za("itype",a);return new c(b)},getTrusted:function(c,d){if(null===d||d===u||""===d)return d;var f=g.hasOwnProperty(c)?g[c]:null;if(f&&d instanceof f)return d.$$unwrapTrustedValue();if(c===fa.RESOURCE_URL){var f=xa(d.toString()),l,n,q=!1;l=0;for(n=b.length;l<n;l++)if("self"===b[l]?Pb(f):b[l].exec(f.href)){q=!0;break}if(q)for(l=0,n=a.length;l<n;l++)if("self"===a[l]?Pb(f):a[l].exec(f.href)){q=!1;break}if(q)return d;throw za("insecurl",
|
||||
d.toString());}if(c===fa.HTML)return e(d);throw za("unsafe");},valueOf:function(a){return a instanceof f?a.$$unwrapTrustedValue():a}}}]}function $d(){var b=!0;this.enabled=function(a){arguments.length&&(b=!!a);return b};this.$get=["$parse","$sniffer","$sceDelegate",function(a,c,d){if(b&&c.msie&&8>c.msieDocumentMode)throw za("iequirks");var e=ha(fa);e.isEnabled=function(){return b};e.trustAs=d.trustAs;e.getTrusted=d.getTrusted;e.valueOf=d.valueOf;b||(e.trustAs=e.getTrusted=function(a,b){return b},
|
||||
e.valueOf=ga);e.parseAs=function(b,c){var d=a(c);return d.literal&&d.constant?d:function(a,c){return e.getTrusted(b,d(a,c))}};var f=e.parseAs,g=e.getTrusted,h=e.trustAs;r(fa,function(a,b){var c=x(b);e[ab("parse_as_"+c)]=function(b){return f(a,b)};e[ab("get_trusted_"+c)]=function(b){return g(a,b)};e[ab("trust_as_"+c)]=function(b){return h(a,b)}});return e}]}function be(){this.$get=["$window","$document",function(b,a){var c={},d=U((/android (\d+)/.exec(x((b.navigator||{}).userAgent))||[])[1]),e=/Boxee/i.test((b.navigator||
|
||||
{}).userAgent),f=a[0]||{},g=f.documentMode,h,k=/^(Moz|webkit|O|ms)(?=[A-Z])/,m=f.body&&f.body.style,l=!1,n=!1;if(m){for(var q in m)if(l=k.exec(q)){h=l[0];h=h.substr(0,1).toUpperCase()+h.substr(1);break}h||(h="WebkitOpacity"in m&&"webkit");l=!!("transition"in m||h+"Transition"in m);n=!!("animation"in m||h+"Animation"in m);!d||l&&n||(l=G(f.body.style.webkitTransition),n=G(f.body.style.webkitAnimation))}return{history:!(!b.history||!b.history.pushState||4>d||e),hashchange:"onhashchange"in b&&(!g||7<
|
||||
g),hasEvent:function(a){if("input"==a&&9==R)return!1;if(F(c[a])){var b=f.createElement("div");c[a]="on"+a in b}return c[a]},csp:Za(),vendorPrefix:h,transitions:l,animations:n,android:d,msie:R,msieDocumentMode:g}}]}function de(){this.$get=["$rootScope","$browser","$q","$exceptionHandler",function(b,a,c,d){function e(e,h,k){var m=c.defer(),l=m.promise,n=D(k)&&!k;h=a.defer(function(){try{m.resolve(e())}catch(a){m.reject(a),d(a)}finally{delete f[l.$$timeoutId]}n||b.$apply()},h);l.$$timeoutId=h;f[h]=m;
|
||||
return l}var f={};e.cancel=function(b){return b&&b.$$timeoutId in f?(f[b.$$timeoutId].reject("canceled"),delete f[b.$$timeoutId],a.defer.cancel(b.$$timeoutId)):!1};return e}]}function xa(b,a){var c=b;R&&(Y.setAttribute("href",c),c=Y.href);Y.setAttribute("href",c);return{href:Y.href,protocol:Y.protocol?Y.protocol.replace(/:$/,""):"",host:Y.host,search:Y.search?Y.search.replace(/^\?/,""):"",hash:Y.hash?Y.hash.replace(/^#/,""):"",hostname:Y.hostname,port:Y.port,pathname:"/"===Y.pathname.charAt(0)?Y.pathname:
|
||||
"/"+Y.pathname}}function Pb(b){b=G(b)?xa(b):b;return b.protocol===Gc.protocol&&b.host===Gc.host}function ee(){this.$get=aa(W)}function kc(b){function a(d,e){if(T(d)){var f={};r(d,function(b,c){f[c]=a(c,b)});return f}return b.factory(d+c,e)}var c="Filter";this.register=a;this.$get=["$injector",function(a){return function(b){return a.get(b+c)}}];a("currency",Hc);a("date",Ic);a("filter",Fe);a("json",Ge);a("limitTo",He);a("lowercase",Ie);a("number",Jc);a("orderBy",Kc);a("uppercase",Je)}function Fe(){return function(b,
|
||||
a,c){if(!L(b))return b;var d=typeof c,e=[];e.check=function(a){for(var b=0;b<e.length;b++)if(!e[b](a))return!1;return!0};"function"!==d&&(c="boolean"===d&&c?function(a,b){return Xa.equals(a,b)}:function(a,b){if(a&&b&&"object"===typeof a&&"object"===typeof b){for(var d in a)if("$"!==d.charAt(0)&&lb.call(a,d)&&c(a[d],b[d]))return!0;return!1}b=(""+b).toLowerCase();return-1<(""+a).toLowerCase().indexOf(b)});var f=function(a,b){if("string"===typeof b&&"!"===b.charAt(0))return!f(a,b.substr(1));switch(typeof a){case "boolean":case "number":case "string":return c(a,
|
||||
b);case "object":switch(typeof b){case "object":return c(a,b);default:for(var d in a)if("$"!==d.charAt(0)&&f(a[d],b))return!0}return!1;case "array":for(d=0;d<a.length;d++)if(f(a[d],b))return!0;return!1;default:return!1}};switch(typeof a){case "boolean":case "number":case "string":a={$:a};case "object":for(var g in a)(function(b){"undefined"!==typeof a[b]&&e.push(function(c){return f("$"==b?c:c&&c[b],a[b])})})(g);break;case "function":e.push(a);break;default:return b}d=[];for(g=0;g<b.length;g++){var h=
|
||||
b[g];e.check(h)&&d.push(h)}return d}}function Hc(b){var a=b.NUMBER_FORMATS;return function(b,d){F(d)&&(d=a.CURRENCY_SYM);return Lc(b,a.PATTERNS[1],a.GROUP_SEP,a.DECIMAL_SEP,2).replace(/\u00A4/g,d)}}function Jc(b){var a=b.NUMBER_FORMATS;return function(b,d){return Lc(b,a.PATTERNS[0],a.GROUP_SEP,a.DECIMAL_SEP,d)}}function Lc(b,a,c,d,e){if(null==b||!isFinite(b)||T(b))return"";var f=0>b;b=Math.abs(b);var g=b+"",h="",k=[],m=!1;if(-1!==g.indexOf("e")){var l=g.match(/([\d\.]+)e(-?)(\d+)/);l&&"-"==l[2]&&
|
||||
l[3]>e+1?(g="0",b=0):(h=g,m=!0)}if(m)0<e&&(-1<b&&1>b)&&(h=b.toFixed(e));else{g=(g.split(Mc)[1]||"").length;F(e)&&(e=Math.min(Math.max(a.minFrac,g),a.maxFrac));b=+(Math.round(+(b.toString()+"e"+e)).toString()+"e"+-e);0===b&&(f=!1);b=(""+b).split(Mc);g=b[0];b=b[1]||"";var l=0,n=a.lgSize,q=a.gSize;if(g.length>=n+q)for(l=g.length-n,m=0;m<l;m++)0===(l-m)%q&&0!==m&&(h+=c),h+=g.charAt(m);for(m=l;m<g.length;m++)0===(g.length-m)%n&&0!==m&&(h+=c),h+=g.charAt(m);for(;b.length<e;)b+="0";e&&"0"!==e&&(h+=d+b.substr(0,
|
||||
e))}k.push(f?a.negPre:a.posPre);k.push(h);k.push(f?a.negSuf:a.posSuf);return k.join("")}function Vb(b,a,c){var d="";0>b&&(d="-",b=-b);for(b=""+b;b.length<a;)b="0"+b;c&&(b=b.substr(b.length-a));return d+b}function Z(b,a,c,d){c=c||0;return function(e){e=e["get"+b]();if(0<c||e>-c)e+=c;0===e&&-12==c&&(e=12);return Vb(e,a,d)}}function vb(b,a){return function(c,d){var e=c["get"+b](),f=La(a?"SHORT"+b:b);return d[f][e]}}function Ic(b){function a(a){var b;if(b=a.match(c)){a=new Date(0);var f=0,g=0,h=b[8]?
|
||||
a.setUTCFullYear:a.setFullYear,k=b[8]?a.setUTCHours:a.setHours;b[9]&&(f=U(b[9]+b[10]),g=U(b[9]+b[11]));h.call(a,U(b[1]),U(b[2])-1,U(b[3]));f=U(b[4]||0)-f;g=U(b[5]||0)-g;h=U(b[6]||0);b=Math.round(1E3*parseFloat("0."+(b[7]||0)));k.call(a,f,g,h,b)}return a}var c=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;return function(c,e){var f="",g=[],h,k;e=e||"mediumDate";e=b.DATETIME_FORMATS[e]||e;G(c)&&(c=Ke.test(c)?U(c):a(c));jb(c)&&(c=new Date(c));
|
||||
if(!va(c))return c;for(;e;)(k=Le.exec(e))?(g=g.concat(wa.call(k,1)),e=g.pop()):(g.push(e),e=null);r(g,function(a){h=Me[a];f+=h?h(c,b.DATETIME_FORMATS):a.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return f}}function Ge(){return function(b){return oa(b,!0)}}function He(){return function(b,a){if(!L(b)&&!G(b))return b;a=Infinity===Math.abs(Number(a))?Number(a):U(a);if(G(b))return a?0<=a?b.slice(0,a):b.slice(a,b.length):"";var c=[],d,e;a>b.length?a=b.length:a<-b.length&&(a=-b.length);0<a?(d=0,e=a):(d=
|
||||
b.length+a,e=b.length);for(;d<e;d++)c.push(b[d]);return c}}function Kc(b){return function(a,c,d){function e(a,b){return Wa(b)?function(b,c){return a(c,b)}:a}function f(a,b){var c=typeof a,d=typeof b;return c==d?(va(a)&&va(b)&&(a=a.valueOf(),b=b.valueOf()),"string"==c&&(a=a.toLowerCase(),b=b.toLowerCase()),a===b?0:a<b?-1:1):c<d?-1:1}if(!Sa(a))return a;c=L(c)?c:[c];0===c.length&&(c=["+"]);c=Uc(c,function(a){var c=!1,d=a||ga;if(G(a)){if("+"==a.charAt(0)||"-"==a.charAt(0))c="-"==a.charAt(0),a=a.substring(1);
|
||||
if(""===a)return e(function(a,b){return f(a,b)},c);d=b(a);if(d.constant){var m=d();return e(function(a,b){return f(a[m],b[m])},c)}}return e(function(a,b){return f(d(a),d(b))},c)});return wa.call(a).sort(e(function(a,b){for(var d=0;d<c.length;d++){var e=c[d](a,b);if(0!==e)return e}return 0},d))}}function Aa(b){N(b)&&(b={link:b});b.restrict=b.restrict||"AC";return aa(b)}function Nc(b,a,c,d){function e(a,c){c=c?"-"+nb(c,"-"):"";d.setClass(b,(a?wb:xb)+c,(a?xb:wb)+c)}var f=this,g=b.parent().controller("form")||
|
||||
yb,h=0,k=f.$error={},m=[];f.$name=a.name||a.ngForm;f.$dirty=!1;f.$pristine=!0;f.$valid=!0;f.$invalid=!1;g.$addControl(f);b.addClass(Ra);e(!0);f.$addControl=function(a){Ea(a.$name,"input");m.push(a);a.$name&&(f[a.$name]=a)};f.$removeControl=function(a){a.$name&&f[a.$name]===a&&delete f[a.$name];r(k,function(b,c){f.$setValidity(c,!0,a)});Ua(m,a)};f.$setValidity=function(a,b,c){var d=k[a];if(b)d&&(Ua(d,c),d.length||(h--,h||(e(b),f.$valid=!0,f.$invalid=!1),k[a]=!1,e(!0,a),g.$setValidity(a,!0,f)));else{h||
|
||||
e(b);if(d){if(-1!=Ta(d,c))return}else k[a]=d=[],h++,e(!1,a),g.$setValidity(a,!1,f);d.push(c);f.$valid=!1;f.$invalid=!0}};f.$setDirty=function(){d.removeClass(b,Ra);d.addClass(b,zb);f.$dirty=!0;f.$pristine=!1;g.$setDirty()};f.$setPristine=function(){d.removeClass(b,zb);d.addClass(b,Ra);f.$dirty=!1;f.$pristine=!0;r(m,function(a){a.$setPristine()})}}function ua(b,a,c,d){b.$setValidity(a,c);return c?d:u}function Oc(b,a){var c,d;if(a)for(c=0;c<a.length;++c)if(d=a[c],b[d])return!0;return!1}function Ne(b,
|
||||
a,c,d,e){T(e)&&(b.$$hasNativeValidators=!0,b.$parsers.push(function(f){if(b.$error[a]||Oc(e,d)||!Oc(e,c))return f;b.$setValidity(a,!1)}))}function Ab(b,a,c,d,e,f){var g=a.prop(Oe),h=a[0].placeholder,k={},m=x(a[0].type);d.$$validityState=g;if(!e.android){var l=!1;a.on("compositionstart",function(a){l=!0});a.on("compositionend",function(){l=!1;n()})}var n=function(e){if(!l){var f=a.val();if(R&&"input"===(e||k).type&&a[0].placeholder!==h)h=a[0].placeholder;else if("password"!==m&&Wa(c.ngTrim||"T")&&
|
||||
(f=$(f)),e=g&&d.$$hasNativeValidators,d.$viewValue!==f||""===f&&e)b.$root.$$phase?d.$setViewValue(f):b.$apply(function(){d.$setViewValue(f)})}};if(e.hasEvent("input"))a.on("input",n);else{var q,p=function(){q||(q=f.defer(function(){n();q=null}))};a.on("keydown",function(a){a=a.keyCode;91===a||(15<a&&19>a||37<=a&&40>=a)||p()});if(e.hasEvent("paste"))a.on("paste cut",p)}a.on("change",n);d.$render=function(){a.val(d.$isEmpty(d.$viewValue)?"":d.$viewValue)};var s=c.ngPattern;s&&((e=s.match(/^\/(.*)\/([gim]*)$/))?
|
||||
(s=RegExp(e[1],e[2]),e=function(a){return ua(d,"pattern",d.$isEmpty(a)||s.test(a),a)}):e=function(c){var e=b.$eval(s);if(!e||!e.test)throw z("ngPattern")("noregexp",s,e,ia(a));return ua(d,"pattern",d.$isEmpty(c)||e.test(c),c)},d.$formatters.push(e),d.$parsers.push(e));if(c.ngMinlength){var r=U(c.ngMinlength);e=function(a){return ua(d,"minlength",d.$isEmpty(a)||a.length>=r,a)};d.$parsers.push(e);d.$formatters.push(e)}if(c.ngMaxlength){var w=U(c.ngMaxlength);e=function(a){return ua(d,"maxlength",d.$isEmpty(a)||
|
||||
a.length<=w,a)};d.$parsers.push(e);d.$formatters.push(e)}}function Wb(b,a){b="ngClass"+b;return["$animate",function(c){function d(a,b){var c=[],d=0;a:for(;d<a.length;d++){for(var e=a[d],l=0;l<b.length;l++)if(e==b[l])continue a;c.push(e)}return c}function e(a){if(!L(a)){if(G(a))return a.split(" ");if(T(a)){var b=[];r(a,function(a,c){a&&(b=b.concat(c.split(" ")))});return b}}return a}return{restrict:"AC",link:function(f,g,h){function k(a,b){var c=g.data("$classCounts")||{},d=[];r(a,function(a){if(0<
|
||||
b||c[a])c[a]=(c[a]||0)+b,c[a]===+(0<b)&&d.push(a)});g.data("$classCounts",c);return d.join(" ")}function m(b){if(!0===a||f.$index%2===a){var m=e(b||[]);if(!l){var p=k(m,1);h.$addClass(p)}else if(!Ca(b,l)){var s=e(l),p=d(m,s),m=d(s,m),m=k(m,-1),p=k(p,1);0===p.length?c.removeClass(g,m):0===m.length?c.addClass(g,p):c.setClass(g,p,m)}}l=ha(b)}var l;f.$watch(h[b],m,!0);h.$observe("class",function(a){m(f.$eval(h[b]))});"ngClass"!==b&&f.$watch("$index",function(c,d){var g=c&1;if(g!==(d&1)){var l=e(f.$eval(h[b]));
|
||||
g===a?(g=k(l,1),h.$addClass(g)):(g=k(l,-1),h.$removeClass(g))}})}}}]}var Oe="validity",x=function(b){return G(b)?b.toLowerCase():b},lb=Object.prototype.hasOwnProperty,La=function(b){return G(b)?b.toUpperCase():b},R,A,Fa,wa=[].slice,Pe=[].push,Ba=Object.prototype.toString,Va=z("ng"),Xa=W.angular||(W.angular={}),$a,Pa,na=["0","0","0"];R=U((/msie (\d+)/.exec(x(navigator.userAgent))||[])[1]);isNaN(R)&&(R=U((/trident\/.*; rv:(\d+)/.exec(x(navigator.userAgent))||[])[1]));v.$inject=[];ga.$inject=[];var L=
|
||||
function(){return N(Array.isArray)?Array.isArray:function(b){return"[object Array]"===Ba.call(b)}}(),$=function(){return String.prototype.trim?function(b){return G(b)?b.trim():b}:function(b){return G(b)?b.replace(/^\s\s*/,"").replace(/\s\s*$/,""):b}}();Pa=9>R?function(b){b=b.nodeName?b:b[0];return b.scopeName&&"HTML"!=b.scopeName?La(b.scopeName+":"+b.nodeName):b.nodeName}:function(b){return b.nodeName?b.nodeName:b[0].nodeName};var Za=function(){if(D(Za.isActive_))return Za.isActive_;var b=!(!X.querySelector("[ng-csp]")&&
|
||||
!X.querySelector("[data-ng-csp]"));if(!b)try{new Function("")}catch(a){b=!0}return Za.isActive_=b},Xc=/[A-Z]/g,$c={full:"1.2.28",major:1,minor:2,dot:28,codeName:"finnish-disembarkation"};S.expando="ng339";var cb=S.cache={},me=1,sb=W.document.addEventListener?function(b,a,c){b.addEventListener(a,c,!1)}:function(b,a,c){b.attachEvent("on"+a,c)},bb=W.document.removeEventListener?function(b,a,c){b.removeEventListener(a,c,!1)}:function(b,a,c){b.detachEvent("on"+a,c)};S._data=function(b){return this.cache[b[this.expando]]||
|
||||
{}};var he=/([\:\-\_]+(.))/g,ie=/^moz([A-Z])/,Hb=z("jqLite"),je=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,Ib=/<|&#?\w+;/,ke=/<([\w:]+)/,le=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,da={option:[1,'<select multiple="multiple">',"</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};da.optgroup=da.option;da.tbody=da.tfoot=da.colgroup=
|
||||
da.caption=da.thead;da.th=da.td;var Oa=S.prototype={ready:function(b){function a(){c||(c=!0,b())}var c=!1;"complete"===X.readyState?setTimeout(a):(this.on("DOMContentLoaded",a),S(W).on("load",a))},toString:function(){var b=[];r(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return 0<=b?A(this[b]):A(this[this.length+b])},length:0,push:Pe,sort:[].sort,splice:[].splice},rb={};r("multiple selected checked disabled readOnly required open".split(" "),function(b){rb[x(b)]=b});
|
||||
var pc={};r("input select option textarea button form details".split(" "),function(b){pc[La(b)]=!0});r({data:Mb,removeData:Lb},function(b,a){S[a]=b});r({data:Mb,inheritedData:qb,scope:function(b){return A.data(b,"$scope")||qb(b.parentNode||b,["$isolateScope","$scope"])},isolateScope:function(b){return A.data(b,"$isolateScope")||A.data(b,"$isolateScopeNoTemplate")},controller:mc,injector:function(b){return qb(b,"$injector")},removeAttr:function(b,a){b.removeAttribute(a)},hasClass:Nb,css:function(b,
|
||||
a,c){a=ab(a);if(D(c))b.style[a]=c;else{var d;8>=R&&(d=b.currentStyle&&b.currentStyle[a],""===d&&(d="auto"));d=d||b.style[a];8>=R&&(d=""===d?u:d);return d}},attr:function(b,a,c){var d=x(a);if(rb[d])if(D(c))c?(b[a]=!0,b.setAttribute(a,d)):(b[a]=!1,b.removeAttribute(d));else return b[a]||(b.attributes.getNamedItem(a)||v).specified?d:u;else if(D(c))b.setAttribute(a,c);else if(b.getAttribute)return b=b.getAttribute(a,2),null===b?u:b},prop:function(b,a,c){if(D(c))b[a]=c;else return b[a]},text:function(){function b(b,
|
||||
d){var e=a[b.nodeType];if(F(d))return e?b[e]:"";b[e]=d}var a=[];9>R?(a[1]="innerText",a[3]="nodeValue"):a[1]=a[3]="textContent";b.$dv="";return b}(),val:function(b,a){if(F(a)){if("SELECT"===Pa(b)&&b.multiple){var c=[];r(b.options,function(a){a.selected&&c.push(a.value||a.text)});return 0===c.length?null:c}return b.value}b.value=a},html:function(b,a){if(F(a))return b.innerHTML;for(var c=0,d=b.childNodes;c<d.length;c++)Ma(d[c]);b.innerHTML=a},empty:nc},function(b,a){S.prototype[a]=function(a,d){var e,
|
||||
f,g=this.length;if(b!==nc&&(2==b.length&&b!==Nb&&b!==mc?a:d)===u){if(T(a)){for(e=0;e<g;e++)if(b===Mb)b(this[e],a);else for(f in a)b(this[e],f,a[f]);return this}e=b.$dv;g=e===u?Math.min(g,1):g;for(f=0;f<g;f++){var h=b(this[f],a,d);e=e?e+h:h}return e}for(e=0;e<g;e++)b(this[e],a,d);return this}});r({removeData:Lb,dealoc:Ma,on:function a(c,d,e,f){if(D(f))throw Hb("onargs");var g=pa(c,"events"),h=pa(c,"handle");g||pa(c,"events",g={});h||pa(c,"handle",h=ne(c,g));r(d.split(" "),function(d){var f=g[d];if(!f){if("mouseenter"==
|
||||
d||"mouseleave"==d){var l=X.body.contains||X.body.compareDocumentPosition?function(a,c){var d=9===a.nodeType?a.documentElement:a,e=c&&c.parentNode;return a===e||!!(e&&1===e.nodeType&&(d.contains?d.contains(e):a.compareDocumentPosition&&a.compareDocumentPosition(e)&16))}:function(a,c){if(c)for(;c=c.parentNode;)if(c===a)return!0;return!1};g[d]=[];a(c,{mouseleave:"mouseout",mouseenter:"mouseover"}[d],function(a){var c=a.relatedTarget;c&&(c===this||l(this,c))||h(a,d)})}else sb(c,d,h),g[d]=[];f=g[d]}f.push(e)})},
|
||||
off:lc,one:function(a,c,d){a=A(a);a.on(c,function f(){a.off(c,d);a.off(c,f)});a.on(c,d)},replaceWith:function(a,c){var d,e=a.parentNode;Ma(a);r(new S(c),function(c){d?e.insertBefore(c,d.nextSibling):e.replaceChild(c,a);d=c})},children:function(a){var c=[];r(a.childNodes,function(a){1===a.nodeType&&c.push(a)});return c},contents:function(a){return a.contentDocument||a.childNodes||[]},append:function(a,c){r(new S(c),function(c){1!==a.nodeType&&11!==a.nodeType||a.appendChild(c)})},prepend:function(a,
|
||||
c){if(1===a.nodeType){var d=a.firstChild;r(new S(c),function(c){a.insertBefore(c,d)})}},wrap:function(a,c){c=A(c)[0];var d=a.parentNode;d&&d.replaceChild(c,a);c.appendChild(a)},remove:function(a){Ma(a);var c=a.parentNode;c&&c.removeChild(a)},after:function(a,c){var d=a,e=a.parentNode;r(new S(c),function(a){e.insertBefore(a,d.nextSibling);d=a})},addClass:pb,removeClass:ob,toggleClass:function(a,c,d){c&&r(c.split(" "),function(c){var f=d;F(f)&&(f=!Nb(a,c));(f?pb:ob)(a,c)})},parent:function(a){return(a=
|
||||
a.parentNode)&&11!==a.nodeType?a:null},next:function(a){if(a.nextElementSibling)return a.nextElementSibling;for(a=a.nextSibling;null!=a&&1!==a.nodeType;)a=a.nextSibling;return a},find:function(a,c){return a.getElementsByTagName?a.getElementsByTagName(c):[]},clone:Kb,triggerHandler:function(a,c,d){var e,f;e=c.type||c;var g=(pa(a,"events")||{})[e];g&&(e={preventDefault:function(){this.defaultPrevented=!0},isDefaultPrevented:function(){return!0===this.defaultPrevented},stopPropagation:v,type:e,target:a},
|
||||
c.type&&(e=E(e,c)),c=ha(g),f=d?[e].concat(d):[e],r(c,function(c){c.apply(a,f)}))}},function(a,c){S.prototype[c]=function(c,e,f){for(var g,h=0;h<this.length;h++)F(g)?(g=a(this[h],c,e,f),D(g)&&(g=A(g))):Jb(g,a(this[h],c,e,f));return D(g)?g:this};S.prototype.bind=S.prototype.on;S.prototype.unbind=S.prototype.off});db.prototype={put:function(a,c){this[Na(a,this.nextUid)]=c},get:function(a){return this[Na(a,this.nextUid)]},remove:function(a){var c=this[a=Na(a,this.nextUid)];delete this[a];return c}};var pe=
|
||||
/^function\s*[^\(]*\(\s*([^\)]*)\)/m,qe=/,/,re=/^\s*(_?)(\S+?)\1\s*$/,oe=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,eb=z("$injector"),Qe=z("$animate"),Ld=["$provide",function(a){this.$$selectors={};this.register=function(c,d){var e=c+"-animation";if(c&&"."!=c.charAt(0))throw Qe("notcsel",c);this.$$selectors[c.substr(1)]=e;a.factory(e,d)};this.classNameFilter=function(a){1===arguments.length&&(this.$$classNameFilter=a instanceof RegExp?a:null);return this.$$classNameFilter};this.$get=["$timeout","$$asyncCallback",
|
||||
function(a,d){return{enter:function(a,c,g,h){g?g.after(a):(c&&c[0]||(c=g.parent()),c.append(a));h&&d(h)},leave:function(a,c){a.remove();c&&d(c)},move:function(a,c,d,h){this.enter(a,c,d,h)},addClass:function(a,c,g){c=G(c)?c:L(c)?c.join(" "):"";r(a,function(a){pb(a,c)});g&&d(g)},removeClass:function(a,c,g){c=G(c)?c:L(c)?c.join(" "):"";r(a,function(a){ob(a,c)});g&&d(g)},setClass:function(a,c,g,h){r(a,function(a){pb(a,c);ob(a,g)});h&&d(h)},enabled:v}}]}],ja=z("$compile");gc.$inject=["$provide","$$sanitizeUriProvider"];
|
||||
var we=/^(x[\:\-_]|data[\:\-_])/i,wc=z("$interpolate"),Re=/^([^\?#]*)(\?([^#]*))?(#(.*))?$/,ze={http:80,https:443,ftp:21},Sb=z("$location");Ac.prototype=Tb.prototype=zc.prototype={$$html5:!1,$$replace:!1,absUrl:tb("$$absUrl"),url:function(a){if(F(a))return this.$$url;a=Re.exec(a);a[1]&&this.path(decodeURIComponent(a[1]));(a[2]||a[1])&&this.search(a[3]||"");this.hash(a[5]||"");return this},protocol:tb("$$protocol"),host:tb("$$host"),port:tb("$$port"),path:Bc("$$path",function(a){a=null!==a?a.toString():
|
||||
"";return"/"==a.charAt(0)?a:"/"+a}),search:function(a,c){switch(arguments.length){case 0:return this.$$search;case 1:if(G(a)||jb(a))a=a.toString(),this.$$search=cc(a);else if(T(a))r(a,function(c,e){null==c&&delete a[e]}),this.$$search=a;else throw Sb("isrcharg");break;default:F(c)||null===c?delete this.$$search[a]:this.$$search[a]=c}this.$$compose();return this},hash:Bc("$$hash",function(a){return null!==a?a.toString():""}),replace:function(){this.$$replace=!0;return this}};var la=z("$parse"),Ec=
|
||||
{},ya,Se=Function.prototype.call,Te=Function.prototype.apply,Pc=Function.prototype.bind,hb={"null":function(){return null},"true":function(){return!0},"false":function(){return!1},undefined:v,"+":function(a,c,d,e){d=d(a,c);e=e(a,c);return D(d)?D(e)?d+e:d:D(e)?e:u},"-":function(a,c,d,e){d=d(a,c);e=e(a,c);return(D(d)?d:0)-(D(e)?e:0)},"*":function(a,c,d,e){return d(a,c)*e(a,c)},"/":function(a,c,d,e){return d(a,c)/e(a,c)},"%":function(a,c,d,e){return d(a,c)%e(a,c)},"^":function(a,c,d,e){return d(a,c)^
|
||||
e(a,c)},"=":v,"===":function(a,c,d,e){return d(a,c)===e(a,c)},"!==":function(a,c,d,e){return d(a,c)!==e(a,c)},"==":function(a,c,d,e){return d(a,c)==e(a,c)},"!=":function(a,c,d,e){return d(a,c)!=e(a,c)},"<":function(a,c,d,e){return d(a,c)<e(a,c)},">":function(a,c,d,e){return d(a,c)>e(a,c)},"<=":function(a,c,d,e){return d(a,c)<=e(a,c)},">=":function(a,c,d,e){return d(a,c)>=e(a,c)},"&&":function(a,c,d,e){return d(a,c)&&e(a,c)},"||":function(a,c,d,e){return d(a,c)||e(a,c)},"&":function(a,c,d,e){return d(a,
|
||||
c)&e(a,c)},"|":function(a,c,d,e){return e(a,c)(a,c,d(a,c))},"!":function(a,c,d){return!d(a,c)}},Ue={n:"\n",f:"\f",r:"\r",t:"\t",v:"\v","'":"'",'"':'"'},Ub=function(a){this.options=a};Ub.prototype={constructor:Ub,lex:function(a){this.text=a;this.index=0;this.ch=u;this.lastCh=":";for(this.tokens=[];this.index<this.text.length;){this.ch=this.text.charAt(this.index);if(this.is("\"'"))this.readString(this.ch);else if(this.isNumber(this.ch)||this.is(".")&&this.isNumber(this.peek()))this.readNumber();else if(this.isIdent(this.ch))this.readIdent();
|
||||
else if(this.is("(){}[].,;:?"))this.tokens.push({index:this.index,text:this.ch}),this.index++;else if(this.isWhitespace(this.ch)){this.index++;continue}else{a=this.ch+this.peek();var c=a+this.peek(2),d=hb[this.ch],e=hb[a],f=hb[c];f?(this.tokens.push({index:this.index,text:c,fn:f}),this.index+=3):e?(this.tokens.push({index:this.index,text:a,fn:e}),this.index+=2):d?(this.tokens.push({index:this.index,text:this.ch,fn:d}),this.index+=1):this.throwError("Unexpected next character ",this.index,this.index+
|
||||
1)}this.lastCh=this.ch}return this.tokens},is:function(a){return-1!==a.indexOf(this.ch)},was:function(a){return-1!==a.indexOf(this.lastCh)},peek:function(a){a=a||1;return this.index+a<this.text.length?this.text.charAt(this.index+a):!1},isNumber:function(a){return"0"<=a&&"9">=a},isWhitespace:function(a){return" "===a||"\r"===a||"\t"===a||"\n"===a||"\v"===a||"\u00a0"===a},isIdent:function(a){return"a"<=a&&"z">=a||"A"<=a&&"Z">=a||"_"===a||"$"===a},isExpOperator:function(a){return"-"===a||"+"===a||this.isNumber(a)},
|
||||
throwError:function(a,c,d){d=d||this.index;c=D(c)?"s "+c+"-"+this.index+" ["+this.text.substring(c,d)+"]":" "+d;throw la("lexerr",a,c,this.text);},readNumber:function(){for(var a="",c=this.index;this.index<this.text.length;){var d=x(this.text.charAt(this.index));if("."==d||this.isNumber(d))a+=d;else{var e=this.peek();if("e"==d&&this.isExpOperator(e))a+=d;else if(this.isExpOperator(d)&&e&&this.isNumber(e)&&"e"==a.charAt(a.length-1))a+=d;else if(!this.isExpOperator(d)||e&&this.isNumber(e)||"e"!=a.charAt(a.length-
|
||||
1))break;else this.throwError("Invalid exponent")}this.index++}a*=1;this.tokens.push({index:c,text:a,literal:!0,constant:!0,fn:function(){return a}})},readIdent:function(){for(var a=this,c="",d=this.index,e,f,g,h;this.index<this.text.length;){h=this.text.charAt(this.index);if("."===h||this.isIdent(h)||this.isNumber(h))"."===h&&(e=this.index),c+=h;else break;this.index++}if(e)for(f=this.index;f<this.text.length;){h=this.text.charAt(f);if("("===h){g=c.substr(e-d+1);c=c.substr(0,e-d);this.index=f;break}if(this.isWhitespace(h))f++;
|
||||
else break}d={index:d,text:c};if(hb.hasOwnProperty(c))d.fn=hb[c],d.literal=!0,d.constant=!0;else{var k=Dc(c,this.options,this.text);d.fn=E(function(a,c){return k(a,c)},{assign:function(d,e){return ub(d,c,e,a.text,a.options)}})}this.tokens.push(d);g&&(this.tokens.push({index:e,text:"."}),this.tokens.push({index:e+1,text:g}))},readString:function(a){var c=this.index;this.index++;for(var d="",e=a,f=!1;this.index<this.text.length;){var g=this.text.charAt(this.index),e=e+g;if(f)"u"===g?(f=this.text.substring(this.index+
|
||||
1,this.index+5),f.match(/[\da-f]{4}/i)||this.throwError("Invalid unicode escape [\\u"+f+"]"),this.index+=4,d+=String.fromCharCode(parseInt(f,16))):d+=Ue[g]||g,f=!1;else if("\\"===g)f=!0;else{if(g===a){this.index++;this.tokens.push({index:c,text:e,string:d,literal:!0,constant:!0,fn:function(){return d}});return}d+=g}this.index++}this.throwError("Unterminated quote",c)}};var gb=function(a,c,d){this.lexer=a;this.$filter=c;this.options=d};gb.ZERO=E(function(){return 0},{constant:!0});gb.prototype={constructor:gb,
|
||||
parse:function(a){this.text=a;this.tokens=this.lexer.lex(a);a=this.statements();0!==this.tokens.length&&this.throwError("is an unexpected token",this.tokens[0]);a.literal=!!a.literal;a.constant=!!a.constant;return a},primary:function(){var a;if(this.expect("("))a=this.filterChain(),this.consume(")");else if(this.expect("["))a=this.arrayDeclaration();else if(this.expect("{"))a=this.object();else{var c=this.expect();(a=c.fn)||this.throwError("not a primary expression",c);a.literal=!!c.literal;a.constant=
|
||||
!!c.constant}for(var d;c=this.expect("(","[",".");)"("===c.text?(a=this.functionCall(a,d),d=null):"["===c.text?(d=a,a=this.objectIndex(a)):"."===c.text?(d=a,a=this.fieldAccess(a)):this.throwError("IMPOSSIBLE");return a},throwError:function(a,c){throw la("syntax",c.text,a,c.index+1,this.text,this.text.substring(c.index));},peekToken:function(){if(0===this.tokens.length)throw la("ueoe",this.text);return this.tokens[0]},peek:function(a,c,d,e){if(0<this.tokens.length){var f=this.tokens[0],g=f.text;if(g===
|
||||
a||g===c||g===d||g===e||!(a||c||d||e))return f}return!1},expect:function(a,c,d,e){return(a=this.peek(a,c,d,e))?(this.tokens.shift(),a):!1},consume:function(a){this.expect(a)||this.throwError("is unexpected, expecting ["+a+"]",this.peek())},unaryFn:function(a,c){return E(function(d,e){return a(d,e,c)},{constant:c.constant})},ternaryFn:function(a,c,d){return E(function(e,f){return a(e,f)?c(e,f):d(e,f)},{constant:a.constant&&c.constant&&d.constant})},binaryFn:function(a,c,d){return E(function(e,f){return c(e,
|
||||
f,a,d)},{constant:a.constant&&d.constant})},statements:function(){for(var a=[];;)if(0<this.tokens.length&&!this.peek("}",")",";","]")&&a.push(this.filterChain()),!this.expect(";"))return 1===a.length?a[0]:function(c,d){for(var e,f=0;f<a.length;f++){var g=a[f];g&&(e=g(c,d))}return e}},filterChain:function(){for(var a=this.expression(),c;;)if(c=this.expect("|"))a=this.binaryFn(a,c.fn,this.filter());else return a},filter:function(){for(var a=this.expect(),c=this.$filter(a.text),d=[];;)if(a=this.expect(":"))d.push(this.expression());
|
||||
else{var e=function(a,e,h){h=[h];for(var k=0;k<d.length;k++)h.push(d[k](a,e));return c.apply(a,h)};return function(){return e}}},expression:function(){return this.assignment()},assignment:function(){var a=this.ternary(),c,d;return(d=this.expect("="))?(a.assign||this.throwError("implies assignment but ["+this.text.substring(0,d.index)+"] can not be assigned to",d),c=this.ternary(),function(d,f){return a.assign(d,c(d,f),f)}):a},ternary:function(){var a=this.logicalOR(),c,d;if(this.expect("?")){c=this.assignment();
|
||||
if(d=this.expect(":"))return this.ternaryFn(a,c,this.assignment());this.throwError("expected :",d)}else return a},logicalOR:function(){for(var a=this.logicalAND(),c;;)if(c=this.expect("||"))a=this.binaryFn(a,c.fn,this.logicalAND());else return a},logicalAND:function(){var a=this.equality(),c;if(c=this.expect("&&"))a=this.binaryFn(a,c.fn,this.logicalAND());return a},equality:function(){var a=this.relational(),c;if(c=this.expect("==","!=","===","!=="))a=this.binaryFn(a,c.fn,this.equality());return a},
|
||||
relational:function(){var a=this.additive(),c;if(c=this.expect("<",">","<=",">="))a=this.binaryFn(a,c.fn,this.relational());return a},additive:function(){for(var a=this.multiplicative(),c;c=this.expect("+","-");)a=this.binaryFn(a,c.fn,this.multiplicative());return a},multiplicative:function(){for(var a=this.unary(),c;c=this.expect("*","/","%");)a=this.binaryFn(a,c.fn,this.unary());return a},unary:function(){var a;return this.expect("+")?this.primary():(a=this.expect("-"))?this.binaryFn(gb.ZERO,a.fn,
|
||||
this.unary()):(a=this.expect("!"))?this.unaryFn(a.fn,this.unary()):this.primary()},fieldAccess:function(a){var c=this,d=this.expect().text,e=Dc(d,this.options,this.text);return E(function(c,d,h){return e(h||a(c,d))},{assign:function(e,g,h){(h=a(e,h))||a.assign(e,h={});return ub(h,d,g,c.text,c.options)}})},objectIndex:function(a){var c=this,d=this.expression();this.consume("]");return E(function(e,f){var g=a(e,f),h=d(e,f),k;ka(h,c.text);if(!g)return u;(g=ma(g[h],c.text))&&(g.then&&c.options.unwrapPromises)&&
|
||||
(k=g,"$$v"in g||(k.$$v=u,k.then(function(a){k.$$v=a})),g=g.$$v);return g},{assign:function(e,f,g){var h=ka(d(e,g),c.text);(g=ma(a(e,g),c.text))||a.assign(e,g={});return g[h]=f}})},functionCall:function(a,c){var d=[];if(")"!==this.peekToken().text){do d.push(this.expression());while(this.expect(","))}this.consume(")");var e=this;return function(f,g){for(var h=[],k=c?c(f,g):f,m=0;m<d.length;m++)h.push(ma(d[m](f,g),e.text));m=a(f,g,k)||v;ma(k,e.text);var l=e.text;if(m){if(m.constructor===m)throw la("isecfn",
|
||||
l);if(m===Se||m===Te||Pc&&m===Pc)throw la("isecff",l);}h=m.apply?m.apply(k,h):m(h[0],h[1],h[2],h[3],h[4]);return ma(h,e.text)}},arrayDeclaration:function(){var a=[],c=!0;if("]"!==this.peekToken().text){do{if(this.peek("]"))break;var d=this.expression();a.push(d);d.constant||(c=!1)}while(this.expect(","))}this.consume("]");return E(function(c,d){for(var g=[],h=0;h<a.length;h++)g.push(a[h](c,d));return g},{literal:!0,constant:c})},object:function(){var a=[],c=!0;if("}"!==this.peekToken().text){do{if(this.peek("}"))break;
|
||||
var d=this.expect(),d=d.string||d.text;this.consume(":");var e=this.expression();a.push({key:d,value:e});e.constant||(c=!1)}while(this.expect(","))}this.consume("}");return E(function(c,d){for(var e={},k=0;k<a.length;k++){var m=a[k];e[m.key]=m.value(c,d)}return e},{literal:!0,constant:c})}};var Ce={},Be={},za=z("$sce"),fa={HTML:"html",CSS:"css",URL:"url",RESOURCE_URL:"resourceUrl",JS:"js"},Y=X.createElement("a"),Gc=xa(W.location.href,!0);kc.$inject=["$provide"];Hc.$inject=["$locale"];Jc.$inject=["$locale"];
|
||||
var Mc=".",Me={yyyy:Z("FullYear",4),yy:Z("FullYear",2,0,!0),y:Z("FullYear",1),MMMM:vb("Month"),MMM:vb("Month",!0),MM:Z("Month",2,1),M:Z("Month",1,1),dd:Z("Date",2),d:Z("Date",1),HH:Z("Hours",2),H:Z("Hours",1),hh:Z("Hours",2,-12),h:Z("Hours",1,-12),mm:Z("Minutes",2),m:Z("Minutes",1),ss:Z("Seconds",2),s:Z("Seconds",1),sss:Z("Milliseconds",3),EEEE:vb("Day"),EEE:vb("Day",!0),a:function(a,c){return 12>a.getHours()?c.AMPMS[0]:c.AMPMS[1]},Z:function(a){a=-1*a.getTimezoneOffset();return a=(0<=a?"+":"")+(Vb(Math[0<
|
||||
a?"floor":"ceil"](a/60),2)+Vb(Math.abs(a%60),2))}},Le=/((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/,Ke=/^\-?\d+$/;Ic.$inject=["$locale"];var Ie=aa(x),Je=aa(La);Kc.$inject=["$parse"];var cd=aa({restrict:"E",compile:function(a,c){8>=R&&(c.href||c.name||c.$set("href",""),a.append(X.createComment("IE fix")));if(!c.href&&!c.xlinkHref&&!c.name)return function(a,c){var f="[object SVGAnimatedString]"===Ba.call(c.prop("href"))?"xlink:href":"href";c.on("click",function(a){c.attr(f)||
|
||||
a.preventDefault()})}}}),Fb={};r(rb,function(a,c){if("multiple"!=a){var d=qa("ng-"+c);Fb[d]=function(){return{priority:100,link:function(a,f,g){a.$watch(g[d],function(a){g.$set(c,!!a)})}}}}});r(["src","srcset","href"],function(a){var c=qa("ng-"+a);Fb[c]=function(){return{priority:99,link:function(d,e,f){var g=a,h=a;"href"===a&&"[object SVGAnimatedString]"===Ba.call(e.prop("href"))&&(h="xlinkHref",f.$attr[h]="xlink:href",g=null);f.$observe(c,function(c){c?(f.$set(h,c),R&&g&&e.prop(g,f[h])):"href"===
|
||||
a&&f.$set(h,null)})}}}});var yb={$addControl:v,$removeControl:v,$setValidity:v,$setDirty:v,$setPristine:v};Nc.$inject=["$element","$attrs","$scope","$animate"];var Qc=function(a){return["$timeout",function(c){return{name:"form",restrict:a?"EAC":"E",controller:Nc,compile:function(){return{pre:function(a,e,f,g){if(!f.action){var h=function(a){a.preventDefault?a.preventDefault():a.returnValue=!1};sb(e[0],"submit",h);e.on("$destroy",function(){c(function(){bb(e[0],"submit",h)},0,!1)})}var k=e.parent().controller("form"),
|
||||
m=f.name||f.ngForm;m&&ub(a,m,g,m);if(k)e.on("$destroy",function(){k.$removeControl(g);m&&ub(a,m,u,m);E(g,yb)})}}}}}]},dd=Qc(),qd=Qc(!0),Ve=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,We=/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i,Xe=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/,Rc={text:Ab,number:function(a,c,d,e,f,g){Ab(a,c,d,e,f,g);e.$parsers.push(function(a){var c=e.$isEmpty(a);if(c||Xe.test(a))return e.$setValidity("number",
|
||||
!0),""===a?null:c?a:parseFloat(a);e.$setValidity("number",!1);return u});Ne(e,"number",Ye,null,e.$$validityState);e.$formatters.push(function(a){return e.$isEmpty(a)?"":""+a});d.min&&(a=function(a){var c=parseFloat(d.min);return ua(e,"min",e.$isEmpty(a)||a>=c,a)},e.$parsers.push(a),e.$formatters.push(a));d.max&&(a=function(a){var c=parseFloat(d.max);return ua(e,"max",e.$isEmpty(a)||a<=c,a)},e.$parsers.push(a),e.$formatters.push(a));e.$formatters.push(function(a){return ua(e,"number",e.$isEmpty(a)||
|
||||
jb(a),a)})},url:function(a,c,d,e,f,g){Ab(a,c,d,e,f,g);a=function(a){return ua(e,"url",e.$isEmpty(a)||Ve.test(a),a)};e.$formatters.push(a);e.$parsers.push(a)},email:function(a,c,d,e,f,g){Ab(a,c,d,e,f,g);a=function(a){return ua(e,"email",e.$isEmpty(a)||We.test(a),a)};e.$formatters.push(a);e.$parsers.push(a)},radio:function(a,c,d,e){F(d.name)&&c.attr("name",ib());c.on("click",function(){c[0].checked&&a.$apply(function(){e.$setViewValue(d.value)})});e.$render=function(){c[0].checked=d.value==e.$viewValue};
|
||||
d.$observe("value",e.$render)},checkbox:function(a,c,d,e){var f=d.ngTrueValue,g=d.ngFalseValue;G(f)||(f=!0);G(g)||(g=!1);c.on("click",function(){a.$apply(function(){e.$setViewValue(c[0].checked)})});e.$render=function(){c[0].checked=e.$viewValue};e.$isEmpty=function(a){return a!==f};e.$formatters.push(function(a){return a===f});e.$parsers.push(function(a){return a?f:g})},hidden:v,button:v,submit:v,reset:v,file:v},Ye=["badInput"],hc=["$browser","$sniffer",function(a,c){return{restrict:"E",require:"?ngModel",
|
||||
link:function(d,e,f,g){g&&(Rc[x(f.type)]||Rc.text)(d,e,f,g,c,a)}}}],wb="ng-valid",xb="ng-invalid",Ra="ng-pristine",zb="ng-dirty",Ze=["$scope","$exceptionHandler","$attrs","$element","$parse","$animate",function(a,c,d,e,f,g){function h(a,c){c=c?"-"+nb(c,"-"):"";g.removeClass(e,(a?xb:wb)+c);g.addClass(e,(a?wb:xb)+c)}this.$modelValue=this.$viewValue=Number.NaN;this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$pristine=!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$name=
|
||||
d.name;var k=f(d.ngModel),m=k.assign;if(!m)throw z("ngModel")("nonassign",d.ngModel,ia(e));this.$render=v;this.$isEmpty=function(a){return F(a)||""===a||null===a||a!==a};var l=e.inheritedData("$formController")||yb,n=0,q=this.$error={};e.addClass(Ra);h(!0);this.$setValidity=function(a,c){q[a]!==!c&&(c?(q[a]&&n--,n||(h(!0),this.$valid=!0,this.$invalid=!1)):(h(!1),this.$invalid=!0,this.$valid=!1,n++),q[a]=!c,h(c,a),l.$setValidity(a,c,this))};this.$setPristine=function(){this.$dirty=!1;this.$pristine=
|
||||
!0;g.removeClass(e,zb);g.addClass(e,Ra)};this.$setViewValue=function(d){this.$viewValue=d;this.$pristine&&(this.$dirty=!0,this.$pristine=!1,g.removeClass(e,Ra),g.addClass(e,zb),l.$setDirty());r(this.$parsers,function(a){d=a(d)});this.$modelValue!==d&&(this.$modelValue=d,m(a,d),r(this.$viewChangeListeners,function(a){try{a()}catch(d){c(d)}}))};var p=this;a.$watch(function(){var c=k(a);if(p.$modelValue!==c){var d=p.$formatters,e=d.length;for(p.$modelValue=c;e--;)c=d[e](c);p.$viewValue!==c&&(p.$viewValue=
|
||||
c,p.$render())}return c})}],Fd=function(){return{require:["ngModel","^?form"],controller:Ze,link:function(a,c,d,e){var f=e[0],g=e[1]||yb;g.$addControl(f);a.$on("$destroy",function(){g.$removeControl(f)})}}},Hd=aa({require:"ngModel",link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),ic=function(){return{require:"?ngModel",link:function(a,c,d,e){if(e){d.required=!0;var f=function(a){if(d.required&&e.$isEmpty(a))e.$setValidity("required",!1);else return e.$setValidity("required",
|
||||
!0),a};e.$formatters.push(f);e.$parsers.unshift(f);d.$observe("required",function(){f(e.$viewValue)})}}}},Gd=function(){return{require:"ngModel",link:function(a,c,d,e){var f=(a=/\/(.*)\//.exec(d.ngList))&&RegExp(a[1])||d.ngList||",";e.$parsers.push(function(a){if(!F(a)){var c=[];a&&r(a.split(f),function(a){a&&c.push($(a))});return c}});e.$formatters.push(function(a){return L(a)?a.join(", "):u});e.$isEmpty=function(a){return!a||!a.length}}}},$e=/^(true|false|\d+)$/,Id=function(){return{priority:100,
|
||||
compile:function(a,c){return $e.test(c.ngValue)?function(a,c,f){f.$set("value",a.$eval(f.ngValue))}:function(a,c,f){a.$watch(f.ngValue,function(a){f.$set("value",a)})}}}},id=Aa({compile:function(a){a.addClass("ng-binding");return function(a,d,e){d.data("$binding",e.ngBind);a.$watch(e.ngBind,function(a){d.text(a==u?"":a)})}}}),kd=["$interpolate",function(a){return function(c,d,e){c=a(d.attr(e.$attr.ngBindTemplate));d.addClass("ng-binding").data("$binding",c);e.$observe("ngBindTemplate",function(a){d.text(a)})}}],
|
||||
jd=["$sce","$parse",function(a,c){return{compile:function(d){d.addClass("ng-binding");return function(d,f,g){f.data("$binding",g.ngBindHtml);var h=c(g.ngBindHtml);d.$watch(function(){return(h(d)||"").toString()},function(c){f.html(a.getTrustedHtml(h(d))||"")})}}}}],ld=Wb("",!0),nd=Wb("Odd",0),md=Wb("Even",1),od=Aa({compile:function(a,c){c.$set("ngCloak",u);a.removeClass("ng-cloak")}}),pd=[function(){return{scope:!0,controller:"@",priority:500}}],jc={},af={blur:!0,focus:!0};r("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "),
|
||||
function(a){var c=qa("ng-"+a);jc[c]=["$parse","$rootScope",function(d,e){return{compile:function(f,g){var h=d(g[c],!0);return function(c,d){d.on(a,function(d){var f=function(){h(c,{$event:d})};af[a]&&e.$$phase?c.$evalAsync(f):c.$apply(f)})}}}}]});var sd=["$animate",function(a){return{transclude:"element",priority:600,terminal:!0,restrict:"A",$$tlb:!0,link:function(c,d,e,f,g){var h,k,m;c.$watch(e.ngIf,function(f){Wa(f)?k||(k=c.$new(),g(k,function(c){c[c.length++]=X.createComment(" end ngIf: "+e.ngIf+
|
||||
" ");h={clone:c};a.enter(c,d.parent(),d)})):(m&&(m.remove(),m=null),k&&(k.$destroy(),k=null),h&&(m=Eb(h.clone),a.leave(m,function(){m=null}),h=null))})}}}],td=["$http","$templateCache","$anchorScroll","$animate","$sce",function(a,c,d,e,f){return{restrict:"ECA",priority:400,terminal:!0,transclude:"element",controller:Xa.noop,compile:function(g,h){var k=h.ngInclude||h.src,m=h.onload||"",l=h.autoscroll;return function(g,h,p,r,J){var w=0,t,y,u,B=function(){y&&(y.remove(),y=null);t&&(t.$destroy(),t=null);
|
||||
u&&(e.leave(u,function(){y=null}),y=u,u=null)};g.$watch(f.parseAsResourceUrl(k),function(f){var k=function(){!D(l)||l&&!g.$eval(l)||d()},p=++w;f?(a.get(f,{cache:c}).success(function(a){if(p===w){var c=g.$new();r.template=a;a=J(c,function(a){B();e.enter(a,null,h,k)});t=c;u=a;t.$emit("$includeContentLoaded");g.$eval(m)}}).error(function(){p===w&&B()}),g.$emit("$includeContentRequested")):(B(),r.template=null)})}}}}],Jd=["$compile",function(a){return{restrict:"ECA",priority:-400,require:"ngInclude",
|
||||
link:function(c,d,e,f){d.html(f.template);a(d.contents())(c)}}}],ud=Aa({priority:450,compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}),vd=Aa({terminal:!0,priority:1E3}),wd=["$locale","$interpolate",function(a,c){var d=/{}/g;return{restrict:"EA",link:function(e,f,g){var h=g.count,k=g.$attr.when&&f.attr(g.$attr.when),m=g.offset||0,l=e.$eval(k)||{},n={},q=c.startSymbol(),p=c.endSymbol(),s=/^when(Minus)?(.+)$/;r(g,function(a,c){s.test(c)&&(l[x(c.replace("when","").replace("Minus","-"))]=
|
||||
f.attr(g.$attr[c]))});r(l,function(a,e){n[e]=c(a.replace(d,q+h+"-"+m+p))});e.$watch(function(){var c=parseFloat(e.$eval(h));if(isNaN(c))return"";c in l||(c=a.pluralCat(c-m));return n[c](e,f,!0)},function(a){f.text(a)})}}}],xd=["$parse","$animate",function(a,c){var d=z("ngRepeat");return{transclude:"element",priority:1E3,terminal:!0,$$tlb:!0,link:function(e,f,g,h,k){var m=g.ngRepeat,l=m.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/),n,q,p,s,u,w,t={$id:Na};if(!l)throw d("iexp",
|
||||
m);g=l[1];h=l[2];(l=l[3])?(n=a(l),q=function(a,c,d){w&&(t[w]=a);t[u]=c;t.$index=d;return n(e,t)}):(p=function(a,c){return Na(c)},s=function(a){return a});l=g.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);if(!l)throw d("iidexp",g);u=l[3]||l[1];w=l[2];var y={};e.$watchCollection(h,function(a){var g,h,l=f[0],n,t={},D,C,I,x,G,v,z,F=[];if(Sa(a))v=a,G=q||p;else{G=q||s;v=[];for(I in a)a.hasOwnProperty(I)&&"$"!=I.charAt(0)&&v.push(I);v.sort()}D=v.length;h=F.length=v.length;for(g=0;g<h;g++)if(I=a===
|
||||
v?g:v[g],x=a[I],n=G(I,x,g),Ea(n,"`track by` id"),y.hasOwnProperty(n))z=y[n],delete y[n],t[n]=z,F[g]=z;else{if(t.hasOwnProperty(n))throw r(F,function(a){a&&a.scope&&(y[a.id]=a)}),d("dupes",m,n,oa(x));F[g]={id:n};t[n]=!1}for(I in y)y.hasOwnProperty(I)&&(z=y[I],g=Eb(z.clone),c.leave(g),r(g,function(a){a.$$NG_REMOVED=!0}),z.scope.$destroy());g=0;for(h=v.length;g<h;g++){I=a===v?g:v[g];x=a[I];z=F[g];F[g-1]&&(l=F[g-1].clone[F[g-1].clone.length-1]);if(z.scope){C=z.scope;n=l;do n=n.nextSibling;while(n&&n.$$NG_REMOVED);
|
||||
z.clone[0]!=n&&c.move(Eb(z.clone),null,A(l));l=z.clone[z.clone.length-1]}else C=e.$new();C[u]=x;w&&(C[w]=I);C.$index=g;C.$first=0===g;C.$last=g===D-1;C.$middle=!(C.$first||C.$last);C.$odd=!(C.$even=0===(g&1));z.scope||k(C,function(a){a[a.length++]=X.createComment(" end ngRepeat: "+m+" ");c.enter(a,null,A(l));l=a;z.scope=C;z.clone=a;t[z.id]=z})}y=t})}}}],yd=["$animate",function(a){return function(c,d,e){c.$watch(e.ngShow,function(c){a[Wa(c)?"removeClass":"addClass"](d,"ng-hide")})}}],rd=["$animate",
|
||||
function(a){return function(c,d,e){c.$watch(e.ngHide,function(c){a[Wa(c)?"addClass":"removeClass"](d,"ng-hide")})}}],zd=Aa(function(a,c,d){a.$watch(d.ngStyle,function(a,d){d&&a!==d&&r(d,function(a,d){c.css(d,"")});a&&c.css(a)},!0)}),Ad=["$animate",function(a){return{restrict:"EA",require:"ngSwitch",controller:["$scope",function(){this.cases={}}],link:function(c,d,e,f){var g=[],h=[],k=[],m=[];c.$watch(e.ngSwitch||e.on,function(d){var n,q;n=0;for(q=k.length;n<q;++n)k[n].remove();n=k.length=0;for(q=
|
||||
m.length;n<q;++n){var p=h[n];m[n].$destroy();k[n]=p;a.leave(p,function(){k.splice(n,1)})}h.length=0;m.length=0;if(g=f.cases["!"+d]||f.cases["?"])c.$eval(e.change),r(g,function(d){var e=c.$new();m.push(e);d.transclude(e,function(c){var e=d.element;h.push(c);a.enter(c,e.parent(),e)})})})}}}],Bd=Aa({transclude:"element",priority:800,require:"^ngSwitch",link:function(a,c,d,e,f){e.cases["!"+d.ngSwitchWhen]=e.cases["!"+d.ngSwitchWhen]||[];e.cases["!"+d.ngSwitchWhen].push({transclude:f,element:c})}}),Cd=
|
||||
Aa({transclude:"element",priority:800,require:"^ngSwitch",link:function(a,c,d,e,f){e.cases["?"]=e.cases["?"]||[];e.cases["?"].push({transclude:f,element:c})}}),Ed=Aa({link:function(a,c,d,e,f){if(!f)throw z("ngTransclude")("orphan",ia(c));f(function(a){c.empty();c.append(a)})}}),ed=["$templateCache",function(a){return{restrict:"E",terminal:!0,compile:function(c,d){"text/ng-template"==d.type&&a.put(d.id,c[0].text)}}}],bf=z("ngOptions"),Dd=aa({terminal:!0}),fd=["$compile","$parse",function(a,c){var d=
|
||||
/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/,e={$setViewValue:v};return{restrict:"E",require:["select","?ngModel"],controller:["$element","$scope","$attrs",function(a,c,d){var k=this,m={},l=e,n;k.databound=d.ngModel;k.init=function(a,c,d){l=a;n=d};k.addOption=function(c){Ea(c,'"option value"');m[c]=!0;l.$viewValue==c&&(a.val(c),n.parent()&&n.remove())};
|
||||
k.removeOption=function(a){this.hasOption(a)&&(delete m[a],l.$viewValue==a&&this.renderUnknownOption(a))};k.renderUnknownOption=function(c){c="? "+Na(c)+" ?";n.val(c);a.prepend(n);a.val(c);n.prop("selected",!0)};k.hasOption=function(a){return m.hasOwnProperty(a)};c.$on("$destroy",function(){k.renderUnknownOption=v})}],link:function(e,g,h,k){function m(a,c,d,e){d.$render=function(){var a=d.$viewValue;e.hasOption(a)?(x.parent()&&x.remove(),c.val(a),""===a&&w.prop("selected",!0)):F(a)&&w?c.val(""):e.renderUnknownOption(a)};
|
||||
c.on("change",function(){a.$apply(function(){x.parent()&&x.remove();d.$setViewValue(c.val())})})}function l(a,c,d){var e;d.$render=function(){var a=new db(d.$viewValue);r(c.find("option"),function(c){c.selected=D(a.get(c.value))})};a.$watch(function(){Ca(e,d.$viewValue)||(e=ha(d.$viewValue),d.$render())});c.on("change",function(){a.$apply(function(){var a=[];r(c.find("option"),function(c){c.selected&&a.push(c.value)});d.$setViewValue(a)})})}function n(e,f,g){function h(){var a={"":[]},c=[""],d,k,
|
||||
s,u,v;s=g.$modelValue;u=A(e)||[];var F=n?Xb(u):u,G,Q,C;Q={};C=!1;if(p)if(k=g.$modelValue,w&&L(k))for(C=new db([]),d={},v=0;v<k.length;v++)d[m]=k[v],C.put(w(e,d),k[v]);else C=new db(k);v=C;var E,K;for(C=0;G=F.length,C<G;C++){k=C;if(n){k=F[C];if("$"===k.charAt(0))continue;Q[n]=k}Q[m]=u[k];d=r(e,Q)||"";(k=a[d])||(k=a[d]=[],c.push(d));p?d=D(v.remove(w?w(e,Q):x(e,Q))):(w?(d={},d[m]=s,d=w(e,d)===w(e,Q)):d=s===x(e,Q),v=v||d);E=l(e,Q);E=D(E)?E:"";k.push({id:w?w(e,Q):n?F[C]:C,label:E,selected:d})}p||(z||null===
|
||||
s?a[""].unshift({id:"",label:"",selected:!v}):v||a[""].unshift({id:"?",label:"",selected:!0}));Q=0;for(F=c.length;Q<F;Q++){d=c[Q];k=a[d];B.length<=Q?(s={element:y.clone().attr("label",d),label:k.label},u=[s],B.push(u),f.append(s.element)):(u=B[Q],s=u[0],s.label!=d&&s.element.attr("label",s.label=d));E=null;C=0;for(G=k.length;C<G;C++)d=k[C],(v=u[C+1])?(E=v.element,v.label!==d.label&&(E.text(v.label=d.label),E.prop("label",v.label)),v.id!==d.id&&E.val(v.id=d.id),E[0].selected!==d.selected&&(E.prop("selected",
|
||||
v.selected=d.selected),R&&E.prop("selected",v.selected))):(""===d.id&&z?K=z:(K=t.clone()).val(d.id).prop("selected",d.selected).attr("selected",d.selected).prop("label",d.label).text(d.label),u.push({element:K,label:d.label,id:d.id,selected:d.selected}),q.addOption(d.label,K),E?E.after(K):s.element.append(K),E=K);for(C++;u.length>C;)d=u.pop(),q.removeOption(d.label),d.element.remove()}for(;B.length>Q;)B.pop()[0].element.remove()}var k;if(!(k=s.match(d)))throw bf("iexp",s,ia(f));var l=c(k[2]||k[1]),
|
||||
m=k[4]||k[6],n=k[5],r=c(k[3]||""),x=c(k[2]?k[1]:m),A=c(k[7]),w=k[8]?c(k[8]):null,B=[[{element:f,label:""}]];z&&(a(z)(e),z.removeClass("ng-scope"),z.remove());f.empty();f.on("change",function(){e.$apply(function(){var a,c=A(e)||[],d={},k,l,q,r,s,t,v;if(p)for(l=[],r=0,t=B.length;r<t;r++)for(a=B[r],q=1,s=a.length;q<s;q++){if((k=a[q].element)[0].selected){k=k.val();n&&(d[n]=k);if(w)for(v=0;v<c.length&&(d[m]=c[v],w(e,d)!=k);v++);else d[m]=c[k];l.push(x(e,d))}}else if(k=f.val(),"?"==k)l=u;else if(""===
|
||||
k)l=null;else if(w)for(v=0;v<c.length;v++){if(d[m]=c[v],w(e,d)==k){l=x(e,d);break}}else d[m]=c[k],n&&(d[n]=k),l=x(e,d);g.$setViewValue(l);h()})});g.$render=h;e.$watchCollection(A,h);e.$watchCollection(function(){var a={},c=A(e);if(c){for(var d=Array(c.length),f=0,g=c.length;f<g;f++)a[m]=c[f],d[f]=l(e,a);return d}},h);p&&e.$watchCollection(function(){return g.$modelValue},h)}if(k[1]){var q=k[0];k=k[1];var p=h.multiple,s=h.ngOptions,z=!1,w,t=A(X.createElement("option")),y=A(X.createElement("optgroup")),
|
||||
x=t.clone();h=0;for(var B=g.children(),v=B.length;h<v;h++)if(""===B[h].value){w=z=B.eq(h);break}q.init(k,z,x);p&&(k.$isEmpty=function(a){return!a||0===a.length});s?n(e,g,k):p?l(e,g,k):m(e,g,k,q)}}}}],hd=["$interpolate",function(a){var c={addOption:v,removeOption:v};return{restrict:"E",priority:100,compile:function(d,e){if(F(e.value)){var f=a(d.text(),!0);f||e.$set("value",d.text())}return function(a,d,e){var m=d.parent(),l=m.data("$selectController")||m.parent().data("$selectController");l&&l.databound?
|
||||
d.prop("selected",!1):l=c;f?a.$watch(f,function(a,c){e.$set("value",a);a!==c&&l.removeOption(c);l.addOption(a)}):l.addOption(e.value);d.on("$destroy",function(){l.removeOption(e.value)})}}}}],gd=aa({restrict:"E",terminal:!0});W.angular.bootstrap?console.log("WARNING: Tried to load angular more than once."):((Fa=W.jQuery)&&Fa.fn.on?(A=Fa,E(Fa.fn,{scope:Oa.scope,isolateScope:Oa.isolateScope,controller:Oa.controller,injector:Oa.injector,inheritedData:Oa.inheritedData}),Gb("remove",!0,!0,!1),Gb("empty",
|
||||
!1,!1,!1),Gb("html",!1,!1,!0)):A=S,Xa.element=A,Zc(Xa),A(X).ready(function(){Wc(X,dc)}))})(window,document);!window.angular.$$csp()&&window.angular.element(document).find("head").prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide{display:none !important;}ng\\:form{display:block;}.ng-animate-block-transitions{transition:0s all!important;-webkit-transition:0s all!important;}.ng-hide-add-active,.ng-hide-remove{display:block!important;}</style>');
|
||||
//# sourceMappingURL=angular.min.js.map
|
8
glances/outputs/static/js/vendors/angular.min.js.map
vendored
Normal file
137
glances/outputs/static/js/vendors/lodash.min.js
vendored
Normal file
@ -0,0 +1,137 @@
|
||||
/**
|
||||
* @license
|
||||
* lodash 3.8.0 (Custom Build) lodash.com/license | Underscore.js 1.8.3 underscorejs.org/LICENSE
|
||||
* Build: `lodash modern -o ./lodash.js`
|
||||
*/
|
||||
;(function(){function n(n,t){if(n!==t){var r=n===n,e=t===t;if(n>t||!r||n===w&&e)return 1;if(n<t||!e||t===w&&r)return-1}return 0}function t(n,t,r){for(var e=n.length,u=r?e:-1;r?u--:++u<e;)if(t(n[u],u,n))return u;return-1}function r(n,t,r){if(t!==t)return p(n,r);r-=1;for(var e=n.length;++r<e;)if(n[r]===t)return r;return-1}function e(n){return typeof n=="function"||false}function u(n){return typeof n=="string"?n:null==n?"":n+""}function o(n){return n.charCodeAt(0)}function i(n,t){for(var r=-1,e=n.length;++r<e&&-1<t.indexOf(n.charAt(r)););
|
||||
return r}function f(n,t){for(var r=n.length;r--&&-1<t.indexOf(n.charAt(r)););return r}function a(t,r){return n(t.a,r.a)||t.b-r.b}function c(n){return $n[n]}function l(n){return Ln[n]}function s(n){return"\\"+Mn[n]}function p(n,t,r){var e=n.length;for(t+=r?0:-1;r?t--:++t<e;){var u=n[t];if(u!==u)return t}return-1}function h(n){return!!n&&typeof n=="object"}function _(n){return 160>=n&&9<=n&&13>=n||32==n||160==n||5760==n||6158==n||8192<=n&&(8202>=n||8232==n||8233==n||8239==n||8287==n||12288==n||65279==n);
|
||||
|
||||
}function v(n,t){for(var r=-1,e=n.length,u=-1,o=[];++r<e;)n[r]===t&&(n[r]=z,o[++u]=r);return o}function g(n){for(var t=-1,r=n.length;++t<r&&_(n.charCodeAt(t)););return t}function y(n){for(var t=n.length;t--&&_(n.charCodeAt(t)););return t}function d(n){return zn[n]}function m(_){function $n(n){if(h(n)&&!(To(n)||n instanceof Bn)){if(n instanceof zn)return n;if(Ge.call(n,"__chain__")&&Ge.call(n,"__wrapped__"))return Lr(n)}return new zn(n)}function Ln(){}function zn(n,t,r){this.__wrapped__=n,this.__actions__=r||[],
|
||||
this.__chain__=!!t}function Bn(n){this.__wrapped__=n,this.__actions__=null,this.__dir__=1,this.__filtered__=false,this.__iteratees__=null,this.__takeCount__=Iu,this.__views__=null}function Mn(){this.__data__={}}function Dn(n){var t=n?n.length:0;for(this.data={hash:du(null),set:new lu};t--;)this.push(n[t])}function Pn(n,t){var r=n.data;return(typeof t=="string"||se(t)?r.set.has(t):r.hash[t])?0:-1}function qn(n,t){var r=-1,e=n.length;for(t||(t=Ue(e));++r<e;)t[r]=n[r];return t}function Kn(n,t){for(var r=-1,e=n.length;++r<e&&false!==t(n[r],r,n););
|
||||
return n}function Vn(n,t){for(var r=-1,e=n.length;++r<e;)if(!t(n[r],r,n))return false;return true}function Gn(n,t){for(var r=-1,e=n.length,u=-1,o=[];++r<e;){var i=n[r];t(i,r,n)&&(o[++u]=i)}return o}function Jn(n,t){for(var r=-1,e=n.length,u=Ue(e);++r<e;)u[r]=t(n[r],r,n);return u}function Xn(n,t,r,e){var u=-1,o=n.length;for(e&&o&&(r=n[++u]);++u<o;)r=t(r,n[u],u,n);return r}function Hn(n,t){for(var r=-1,e=n.length;++r<e;)if(t(n[r],r,n))return true;return false}function Qn(n,t){return n===w?t:n}function nt(n,t,r,e){
|
||||
return n!==w&&Ge.call(e,r)?n:t}function tt(n,t,r){var e=Ko(t);fu.apply(e,Zu(t));for(var u=-1,o=e.length;++u<o;){var i=e[u],f=n[i],a=r(f,t[i],i,n,t);(a===a?a===f:f!==f)&&(f!==w||i in n)||(n[i]=a)}return n}function rt(n,t){for(var r=-1,e=null==n,u=!e&&jr(n),o=u&&n.length,i=t.length,f=Ue(i);++r<i;){var a=t[r];f[r]=u?kr(a,o)?n[a]:w:e?w:n[a]}return f}function et(n,t,r){r||(r={});for(var e=-1,u=t.length;++e<u;){var o=t[e];r[o]=n[o]}return r}function ut(n,t,r){var e=typeof n;return"function"==e?t===w?n:zt(n,t,r):null==n?Re:"object"==e?wt(n):t===w?Te(n):bt(n,t);
|
||||
|
||||
}function ot(n,t,r,e,u,o,i){var f;if(r&&(f=u?r(n,e,u):r(n)),f!==w)return f;if(!se(n))return n;if(e=To(n)){if(f=wr(n),!t)return qn(n,f)}else{var a=Xe.call(n),c=a==K;if(a!=Y&&a!=B&&(!c||u))return Nn[a]?xr(n,a,t):u?n:{};if(f=br(c?{}:n),!t)return $u(f,n)}for(o||(o=[]),i||(i=[]),u=o.length;u--;)if(o[u]==n)return i[u];return o.push(n),i.push(f),(e?Kn:ht)(n,function(e,u){f[u]=ot(e,t,r,u,n,o,i)}),f}function it(n,t,r){if(typeof n!="function")throw new Pe(L);return su(function(){n.apply(w,r)},t)}function ft(n,t){
|
||||
var e=n?n.length:0,u=[];if(!e)return u;var o=-1,i=mr(),f=i==r,a=f&&200<=t.length?qu(t):null,c=t.length;a&&(i=Pn,f=false,t=a);n:for(;++o<e;)if(a=n[o],f&&a===a){for(var l=c;l--;)if(t[l]===a)continue n;u.push(a)}else 0>i(t,a,0)&&u.push(a);return u}function at(n,t){var r=true;return zu(n,function(n,e,u){return r=!!t(n,e,u)}),r}function ct(n,t){var r=[];return zu(n,function(n,e,u){t(n,e,u)&&r.push(n)}),r}function lt(n,t,r,e){var u;return r(n,function(n,r,o){return t(n,r,o)?(u=e?r:n,false):void 0}),u}function st(n,t,r){
|
||||
for(var e=-1,u=n.length,o=-1,i=[];++e<u;){var f=n[e];if(h(f)&&jr(f)&&(r||To(f)||ae(f))){t&&(f=st(f,t,r));for(var a=-1,c=f.length;++a<c;)i[++o]=f[a]}else r||(i[++o]=f)}return i}function pt(n,t){Mu(n,t,me)}function ht(n,t){return Mu(n,t,Ko)}function _t(n,t){return Du(n,t,Ko)}function vt(n,t){for(var r=-1,e=t.length,u=-1,o=[];++r<e;){var i=t[r];No(n[i])&&(o[++u]=i)}return o}function gt(n,t,r){if(null!=n){r!==w&&r in Fr(n)&&(t=[r]),r=-1;for(var e=t.length;null!=n&&++r<e;)n=n[t[r]];return r&&r==e?n:w}
|
||||
}function yt(n,t,r,e,u,o){if(n===t)return true;var i=typeof n,f=typeof t;if("function"!=i&&"object"!=i&&"function"!=f&&"object"!=f||null==n||null==t)n=n!==n&&t!==t;else n:{var i=yt,f=To(n),a=To(t),c=M,l=M;f||(c=Xe.call(n),c==B?c=Y:c!=Y&&(f=ge(n))),a||(l=Xe.call(t),l==B?l=Y:l!=Y&&ge(t));var s=c==Y,a=l==Y,l=c==l;if(!l||f||s){if(!e&&(c=s&&Ge.call(n,"__wrapped__"),a=a&&Ge.call(t,"__wrapped__"),c||a)){n=i(c?n.value():n,a?t.value():t,r,e,u,o);break n}if(l){for(u||(u=[]),o||(o=[]),c=u.length;c--;)if(u[c]==n){
|
||||
n=o[c]==t;break n}u.push(n),o.push(t),n=(f?_r:gr)(n,t,i,r,e,u,o),u.pop(),o.pop()}else n=false}else n=vr(n,t,c)}return n}function dt(n,t,r,e,u){for(var o=-1,i=t.length,f=!u;++o<i;)if(f&&e[o]?r[o]!==n[t[o]]:!(t[o]in n))return false;for(o=-1;++o<i;){var a=t[o],c=n[a],l=r[o];if(f&&e[o]?a=c!==w||a in n:(a=u?u(c,l,a):w,a===w&&(a=yt(l,c,u,true))),!a)return false}return true}function mt(n,t){var r=-1,e=jr(n)?Ue(n.length):[];return zu(n,function(n,u,o){e[++r]=t(n,u,o)}),e}function wt(n){var t=Ko(n),r=t.length;if(!r)return Ie(true);
|
||||
|
||||
if(1==r){var e=t[0],u=n[e];if(Cr(u))return function(n){return null==n?false:n[e]===u&&(u!==w||e in Fr(n))}}for(var o=Ue(r),i=Ue(r);r--;)u=n[t[r]],o[r]=u,i[r]=Cr(u);return function(n){return null!=n&&dt(Fr(n),t,o,i)}}function bt(n,t){var r=To(n),e=Er(n)&&Cr(t),u=n+"";return n=$r(n),function(o){if(null==o)return false;var i=u;if(o=Fr(o),!(!r&&e||i in o)){if(o=1==n.length?o:gt(o,It(n,0,-1)),null==o)return false;i=Pr(n),o=Fr(o)}return o[i]===t?t!==w||i in o:yt(t,o[i],null,true)}}function xt(n,t,r,e,u){if(!se(n))return n;
|
||||
|
||||
var o=jr(t)&&(To(t)||ge(t));if(!o){var i=Ko(t);fu.apply(i,Zu(t))}return Kn(i||t,function(f,a){if(i&&(a=f,f=t[a]),h(f)){e||(e=[]),u||(u=[]);n:{for(var c=a,l=e,s=u,p=l.length,_=t[c];p--;)if(l[p]==_){n[c]=s[p];break n}var p=n[c],v=r?r(p,_,c,n,t):w,g=v===w;g&&(v=_,jr(_)&&(To(_)||ge(_))?v=To(p)?p:jr(p)?qn(p):[]:Fo(_)||ae(_)?v=ae(p)?ye(p):Fo(p)?p:{}:g=false),l.push(_),s.push(v),g?n[c]=xt(v,_,r,l,s):(v===v?v!==p:p===p)&&(n[c]=v)}}else c=n[a],l=r?r(c,f,a,n,t):w,(s=l===w)&&(l=f),!o&&l===w||!s&&(l===l?l===c:c!==c)||(n[a]=l);
|
||||
|
||||
}),n}function At(n){return function(t){return null==t?w:t[n]}}function jt(n){var t=n+"";return n=$r(n),function(r){return gt(r,n,t)}}function kt(n,t){for(var r=n?t.length:0;r--;){var e=parseFloat(t[r]);if(e!=u&&kr(e)){var u=e;pu.call(n,e,1)}}}function Ot(n,t){return n+uu(Ou()*(t-n+1))}function Et(n,t,r,e,u){return u(n,function(n,u,o){r=e?(e=false,n):t(r,n,u,o)}),r}function It(n,t,r){var e=-1,u=n.length;for(t=null==t?0:+t||0,0>t&&(t=-t>u?0:u+t),r=r===w||r>u?u:+r||0,0>r&&(r+=u),u=t>r?0:r-t>>>0,t>>>=0,
|
||||
r=Ue(u);++e<u;)r[e]=n[e+t];return r}function Rt(n,t){var r;return zu(n,function(n,e,u){return r=t(n,e,u),!r}),!!r}function Ct(n,t){var r=n.length;for(n.sort(t);r--;)n[r]=n[r].c;return n}function Wt(t,r,e){var u=dr(),o=-1;return r=Jn(r,function(n){return u(n)}),t=mt(t,function(n){return{a:Jn(r,function(t){return t(n)}),b:++o,c:n}}),Ct(t,function(t,r){var u;n:{u=-1;for(var o=t.a,i=r.a,f=o.length,a=e.length;++u<f;){var c=n(o[u],i[u]);if(c){u=u<a?c*(e[u]?1:-1):c;break n}}u=t.b-r.b}return u})}function St(n,t){
|
||||
var r=0;return zu(n,function(n,e,u){r+=+t(n,e,u)||0}),r}function Tt(n,t){var e=-1,u=mr(),o=n.length,i=u==r,f=i&&200<=o,a=f?qu():null,c=[];a?(u=Pn,i=false):(f=false,a=t?[]:c);n:for(;++e<o;){var l=n[e],s=t?t(l,e,n):l;if(i&&l===l){for(var p=a.length;p--;)if(a[p]===s)continue n;t&&a.push(s),c.push(l)}else 0>u(a,s,0)&&((t||f)&&a.push(s),c.push(l))}return c}function Ut(n,t){for(var r=-1,e=t.length,u=Ue(e);++r<e;)u[r]=n[t[r]];return u}function Nt(n,t,r,e){for(var u=n.length,o=e?u:-1;(e?o--:++o<u)&&t(n[o],o,n););
|
||||
return r?It(n,e?0:o,e?o+1:u):It(n,e?o+1:0,e?u:o)}function Ft(n,t){var r=n;r instanceof Bn&&(r=r.value());for(var e=-1,u=t.length;++e<u;){var r=[r],o=t[e];fu.apply(r,o.args),r=o.func.apply(o.thisArg,r)}return r}function $t(n,t,r){var e=0,u=n?n.length:e;if(typeof t=="number"&&t===t&&u<=Wu){for(;e<u;){var o=e+u>>>1,i=n[o];(r?i<=t:i<t)?e=o+1:u=o}return u}return Lt(n,t,Re,r)}function Lt(n,t,r,e){t=r(t);for(var u=0,o=n?n.length:0,i=t!==t,f=t===w;u<o;){var a=uu((u+o)/2),c=r(n[a]),l=c===c;(i?l||e:f?l&&(e||c!==w):e?c<=t:c<t)?u=a+1:o=a;
|
||||
|
||||
}return xu(o,Cu)}function zt(n,t,r){if(typeof n!="function")return Re;if(t===w)return n;switch(r){case 1:return function(r){return n.call(t,r)};case 3:return function(r,e,u){return n.call(t,r,e,u)};case 4:return function(r,e,u,o){return n.call(t,r,e,u,o)};case 5:return function(r,e,u,o,i){return n.call(t,r,e,u,o,i)}}return function(){return n.apply(t,arguments)}}function Bt(n){return tu.call(n,0)}function Mt(n,t,r){for(var e=r.length,u=-1,o=bu(n.length-e,0),i=-1,f=t.length,a=Ue(o+f);++i<f;)a[i]=t[i];
|
||||
|
||||
for(;++u<e;)a[r[u]]=n[u];for(;o--;)a[i++]=n[u++];return a}function Dt(n,t,r){for(var e=-1,u=r.length,o=-1,i=bu(n.length-u,0),f=-1,a=t.length,c=Ue(i+a);++o<i;)c[o]=n[o];for(i=o;++f<a;)c[i+f]=t[f];for(;++e<u;)c[i+r[e]]=n[o++];return c}function Pt(n,t){return function(r,e,u){var o=t?t():{};if(e=dr(e,u,3),To(r)){u=-1;for(var i=r.length;++u<i;){var f=r[u];n(o,f,e(f,u,r),r)}}else zu(r,function(t,r,u){n(o,t,e(t,r,u),u)});return o}}function qt(n){return fe(function(t,r){var e=-1,u=null==t?0:r.length,o=2<u&&r[u-2],i=2<u&&r[2],f=1<u&&r[u-1];
|
||||
|
||||
for(typeof o=="function"?(o=zt(o,f,5),u-=2):(o=typeof f=="function"?f:null,u-=o?1:0),i&&Or(r[0],r[1],i)&&(o=3>u?null:o,u=1);++e<u;)(i=r[e])&&n(t,i,o);return t})}function Kt(n,t){return function(r,e){var u=r?Yu(r):0;if(!Rr(u))return n(r,e);for(var o=t?u:-1,i=Fr(r);(t?o--:++o<u)&&false!==e(i[o],o,i););return r}}function Vt(n){return function(t,r,e){var u=Fr(t);e=e(t);for(var o=e.length,i=n?o:-1;n?i--:++i<o;){var f=e[i];if(false===r(u[f],f,u))break}return t}}function Yt(n,t){function r(){return(this&&this!==Yn&&this instanceof r?e:n).apply(t,arguments);
|
||||
|
||||
}var e=Gt(n);return r}function Zt(n){return function(t){var r=-1;t=Oe(be(t));for(var e=t.length,u="";++r<e;)u=n(u,t[r],r);return u}}function Gt(n){return function(){var t=Lu(n.prototype),r=n.apply(t,arguments);return se(r)?r:t}}function Jt(n){function t(r,e,u){return u&&Or(r,e,u)&&(e=null),r=hr(r,n,null,null,null,null,null,e),r.placeholder=t.placeholder,r}return t}function Xt(n,t){return function(r,e,u){u&&Or(r,e,u)&&(e=null);var i=dr(),f=null==e;if(i===ut&&f||(f=false,e=i(e,u,3)),f){if(e=To(r),e||!ve(r))return n(e?r:Nr(r));
|
||||
|
||||
e=o}return yr(r,e,t)}}function Ht(n,r){return function(e,u,o){return u=dr(u,o,3),To(e)?(u=t(e,u,r),-1<u?e[u]:w):lt(e,u,n)}}function Qt(n){return function(r,e,u){return r&&r.length?(e=dr(e,u,3),t(r,e,n)):-1}}function nr(n){return function(t,r,e){return r=dr(r,e,3),lt(t,r,n,true)}}function tr(n){return function(){var t=arguments.length;if(!t)return function(){return arguments[0]};for(var r,e=n?t:-1,u=0,o=Ue(t);n?e--:++e<t;){var i=o[u++]=arguments[e];if(typeof i!="function")throw new Pe(L);var f=r?"":Vu(i);
|
||||
|
||||
r="wrapper"==f?new zn([]):r}for(e=r?-1:t;++e<t;)i=o[e],f=Vu(i),r=(u="wrapper"==f?Ku(i):null)&&Ir(u[0])&&u[1]==(R|k|E|C)&&!u[4].length&&1==u[9]?r[Vu(u[0])].apply(r,u[3]):1==i.length&&Ir(i)?r[f]():r.thru(i);return function(){var n=arguments;if(r&&1==n.length&&To(n[0]))return r.plant(n[0]).value();for(var e=0,n=o[e].apply(this,n);++e<t;)n=o[e].call(this,n);return n}}}function rr(n,t){return function(r,e,u){return typeof e=="function"&&u===w&&To(r)?n(r,e):t(r,zt(e,u,3))}}function er(n){return function(t,r,e){
|
||||
return(typeof r!="function"||e!==w)&&(r=zt(r,e,3)),n(t,r,me)}}function ur(n){return function(t,r,e){return(typeof r!="function"||e!==w)&&(r=zt(r,e,3)),n(t,r)}}function or(n){return function(t,r,e){var u={};return r=dr(r,e,3),ht(t,function(t,e,o){o=r(t,e,o),e=n?o:e,t=n?t:o,u[e]=t}),u}}function ir(n){return function(t,r,e){return t=u(t),(n?t:"")+lr(t,r,e)+(n?"":t)}}function fr(n){var t=fe(function(r,e){var u=v(e,t.placeholder);return hr(r,n,null,e,u)});return t}function ar(n,t){return function(r,e,u,o){
|
||||
var i=3>arguments.length;return typeof e=="function"&&o===w&&To(r)?n(r,e,u,i):Et(r,dr(e,o,4),u,i,t)}}function cr(n,t,r,e,u,o,i,f,a,c){function l(){for(var b=arguments.length,j=b,k=Ue(b);j--;)k[j]=arguments[j];if(e&&(k=Mt(k,e,u)),o&&(k=Dt(k,o,i)),_||y){var j=l.placeholder,O=v(k,j),b=b-O.length;if(b<c){var R=f?qn(f):null,b=bu(c-b,0),C=_?O:null,O=_?null:O,W=_?k:null,k=_?null:k;return t|=_?E:I,t&=~(_?I:E),g||(t&=~(x|A)),k=[n,t,r,W,C,k,O,R,a,b],R=cr.apply(w,k),Ir(n)&&Gu(R,k),R.placeholder=j,R}}if(j=p?r:this,
|
||||
h&&(n=j[m]),f)for(R=k.length,b=xu(f.length,R),C=qn(k);b--;)O=f[b],k[b]=kr(O,R)?C[O]:w;return s&&a<k.length&&(k.length=a),(this&&this!==Yn&&this instanceof l?d||Gt(n):n).apply(j,k)}var s=t&R,p=t&x,h=t&A,_=t&k,g=t&j,y=t&O,d=!h&&Gt(n),m=n;return l}function lr(n,t,r){return n=n.length,t=+t,n<t&&mu(t)?(t-=n,r=null==r?" ":r+"",je(r,ru(t/r.length)).slice(0,t)):""}function sr(n,t,r,e){function u(){for(var t=-1,f=arguments.length,a=-1,c=e.length,l=Ue(f+c);++a<c;)l[a]=e[a];for(;f--;)l[a++]=arguments[++t];return(this&&this!==Yn&&this instanceof u?i:n).apply(o?r:this,l);
|
||||
|
||||
}var o=t&x,i=Gt(n);return u}function pr(n){return function(t,r,e,u){var o=dr(e);return o===ut&&null==e?$t(t,r,n):Lt(t,r,o(e,u,1),n)}}function hr(n,t,r,e,u,o,i,f){var a=t&A;if(!a&&typeof n!="function")throw new Pe(L);var c=e?e.length:0;if(c||(t&=~(E|I),e=u=null),c-=u?u.length:0,t&I){var l=e,s=u;e=u=null}var p=a?null:Ku(n);return r=[n,t,r,e,u,l,s,o,i,f],p&&(e=r[1],t=p[1],f=e|t,u=t==R&&e==k||t==R&&e==C&&r[7].length<=p[8]||t==(R|C)&&e==k,(f<R||u)&&(t&x&&(r[2]=p[2],f|=e&x?0:j),(e=p[3])&&(u=r[3],r[3]=u?Mt(u,e,p[4]):qn(e),
|
||||
r[4]=u?v(r[3],z):qn(p[4])),(e=p[5])&&(u=r[5],r[5]=u?Dt(u,e,p[6]):qn(e),r[6]=u?v(r[5],z):qn(p[6])),(e=p[7])&&(r[7]=qn(e)),t&R&&(r[8]=null==r[8]?p[8]:xu(r[8],p[8])),null==r[9]&&(r[9]=p[9]),r[0]=p[0],r[1]=f),t=r[1],f=r[9]),r[9]=null==f?a?0:n.length:bu(f-c,0)||0,(p?Pu:Gu)(t==x?Yt(r[0],r[2]):t!=E&&t!=(x|E)||r[4].length?cr.apply(w,r):sr.apply(w,r),r)}function _r(n,t,r,e,u,o,i){var f=-1,a=n.length,c=t.length,l=true;if(a!=c&&(!u||c<=a))return false;for(;l&&++f<a;){var s=n[f],p=t[f],l=w;if(e&&(l=u?e(p,s,f):e(s,p,f)),
|
||||
l===w)if(u)for(var h=c;h--&&(p=t[h],!(l=s&&s===p||r(s,p,e,u,o,i))););else l=s&&s===p||r(s,p,e,u,o,i)}return!!l}function vr(n,t,r){switch(r){case D:case P:return+n==+t;case q:return n.name==t.name&&n.message==t.message;case V:return n!=+n?t!=+t:n==+t;case Z:case G:return n==t+""}return false}function gr(n,t,r,e,u,o,i){var f=Ko(n),a=f.length,c=Ko(t).length;if(a!=c&&!u)return false;for(var c=u,l=-1;++l<a;){var s=f[l],p=u?s in t:Ge.call(t,s);if(p){var h=n[s],_=t[s],p=w;e&&(p=u?e(_,h,s):e(h,_,s)),p===w&&(p=h&&h===_||r(h,_,e,u,o,i));
|
||||
|
||||
}if(!p)return false;c||(c="constructor"==s)}return c||(r=n.constructor,e=t.constructor,!(r!=e&&"constructor"in n&&"constructor"in t)||typeof r=="function"&&r instanceof r&&typeof e=="function"&&e instanceof e)?true:false}function yr(n,t,r){var e=r?Iu:Eu,u=e,o=u;return zu(n,function(n,i,f){i=t(n,i,f),((r?i<u:i>u)||i===e&&i===o)&&(u=i,o=n)}),o}function dr(n,t,r){var e=$n.callback||Ee,e=e===Ee?ut:e;return r?e(n,t,r):e}function mr(n,t,e){var u=$n.indexOf||Dr,u=u===Dr?r:u;return n?u(n,t,e):u}function wr(n){var t=n.length,r=new n.constructor(t);
|
||||
|
||||
return t&&"string"==typeof n[0]&&Ge.call(n,"index")&&(r.index=n.index,r.input=n.input),r}function br(n){return n=n.constructor,typeof n=="function"&&n instanceof n||(n=Be),new n}function xr(n,t,r){var e=n.constructor;switch(t){case J:return Bt(n);case D:case P:return new e(+n);case X:case H:case Q:case nn:case tn:case rn:case en:case un:case on:return t=n.buffer,new e(r?Bt(t):t,n.byteOffset,n.length);case V:case G:return new e(n);case Z:var u=new e(n.source,kn.exec(n));u.lastIndex=n.lastIndex}return u;
|
||||
|
||||
}function Ar(n,t,r){return null==n||Er(t,n)||(t=$r(t),n=1==t.length?n:gt(n,It(t,0,-1)),t=Pr(t)),t=null==n?n:n[t],null==t?w:t.apply(n,r)}function jr(n){return null!=n&&Rr(Yu(n))}function kr(n,t){return n=+n,t=null==t?Tu:t,-1<n&&0==n%1&&n<t}function Or(n,t,r){if(!se(r))return false;var e=typeof t;return("number"==e?jr(r)&&kr(t,r.length):"string"==e&&t in r)?(t=r[t],n===n?n===t:t!==t):false}function Er(n,t){var r=typeof n;return"string"==r&&dn.test(n)||"number"==r?true:To(n)?false:!yn.test(n)||null!=t&&n in Fr(t);
|
||||
|
||||
}function Ir(n){var t=Vu(n);return!!t&&n===$n[t]&&t in Bn.prototype}function Rr(n){return typeof n=="number"&&-1<n&&0==n%1&&n<=Tu}function Cr(n){return n===n&&!se(n)}function Wr(n,t){n=Fr(n);for(var r=-1,e=t.length,u={};++r<e;){var o=t[r];o in n&&(u[o]=n[o])}return u}function Sr(n,t){var r={};return pt(n,function(n,e,u){t(n,e,u)&&(r[e]=n)}),r}function Tr(n){var t;if(!h(n)||Xe.call(n)!=Y||!(Ge.call(n,"constructor")||(t=n.constructor,typeof t!="function"||t instanceof t)))return false;var r;return pt(n,function(n,t){
|
||||
r=t}),r===w||Ge.call(n,r)}function Ur(n){for(var t=me(n),r=t.length,e=r&&n.length,u=$n.support,u=e&&Rr(e)&&(To(n)||u.nonEnumArgs&&ae(n)),o=-1,i=[];++o<r;){var f=t[o];(u&&kr(f,e)||Ge.call(n,f))&&i.push(f)}return i}function Nr(n){return null==n?[]:jr(n)?se(n)?n:Be(n):we(n)}function Fr(n){return se(n)?n:Be(n)}function $r(n){if(To(n))return n;var t=[];return u(n).replace(mn,function(n,r,e,u){t.push(e?u.replace(An,"$1"):r||n)}),t}function Lr(n){return n instanceof Bn?n.clone():new zn(n.__wrapped__,n.__chain__,qn(n.__actions__));
|
||||
|
||||
}function zr(n,t,r){return n&&n.length?((r?Or(n,t,r):null==t)&&(t=1),It(n,0>t?0:t)):[]}function Br(n,t,r){var e=n?n.length:0;return e?((r?Or(n,t,r):null==t)&&(t=1),t=e-(+t||0),It(n,0,0>t?0:t)):[]}function Mr(n){return n?n[0]:w}function Dr(n,t,e){var u=n?n.length:0;if(!u)return-1;if(typeof e=="number")e=0>e?bu(u+e,0):e;else if(e)return e=$t(n,t),n=n[e],(t===t?t===n:n!==n)?e:-1;return r(n,t,e||0)}function Pr(n){var t=n?n.length:0;return t?n[t-1]:w}function qr(n){return zr(n,1)}function Kr(n,t,e,u){
|
||||
if(!n||!n.length)return[];null!=t&&typeof t!="boolean"&&(u=e,e=Or(n,t,u)?null:t,t=false);var o=dr();if((o!==ut||null!=e)&&(e=o(e,u,3)),t&&mr()==r){t=e;var i;e=-1,u=n.length;for(var o=-1,f=[];++e<u;){var a=n[e],c=t?t(a,e,n):a;e&&i===c||(i=c,f[++o]=a)}n=f}else n=Tt(n,e);return n}function Vr(n){if(!n||!n.length)return[];var t=-1,r=0;n=Gn(n,function(n){return jr(n)?(r=bu(n.length,r),true):void 0});for(var e=Ue(r);++t<r;)e[t]=Jn(n,At(t));return e}function Yr(n,t,r){return n&&n.length?(n=Vr(n),null==t?n:(t=zt(t,r,4),
|
||||
Jn(n,function(n){return Xn(n,t,w,true)}))):[]}function Zr(n,t){var r=-1,e=n?n.length:0,u={};for(!e||t||To(n[0])||(t=[]);++r<e;){var o=n[r];t?u[o]=t[r]:o&&(u[o[0]]=o[1])}return u}function Gr(n){return n=$n(n),n.__chain__=true,n}function Jr(n,t,r){return t.call(r,n)}function Xr(n,t,r){var e=To(n)?Vn:at;return r&&Or(n,t,r)&&(t=null),(typeof t!="function"||r!==w)&&(t=dr(t,r,3)),e(n,t)}function Hr(n,t,r){var e=To(n)?Gn:ct;return t=dr(t,r,3),e(n,t)}function Qr(n,t,r,e){var u=n?Yu(n):0;return Rr(u)||(n=we(n),
|
||||
u=n.length),u?(r=typeof r!="number"||e&&Or(t,r,e)?0:0>r?bu(u+r,0):r||0,typeof n=="string"||!To(n)&&ve(n)?r<u&&-1<n.indexOf(t,r):-1<mr(n,t,r)):false}function ne(n,t,r){var e=To(n)?Jn:mt;return t=dr(t,r,3),e(n,t)}function te(n,t,r){return(r?Or(n,t,r):null==t)?(n=Nr(n),t=n.length,0<t?n[Ot(0,t-1)]:w):(n=re(n),n.length=xu(0>t?0:+t||0,n.length),n)}function re(n){n=Nr(n);for(var t=-1,r=n.length,e=Ue(r);++t<r;){var u=Ot(0,t);t!=u&&(e[t]=e[u]),e[u]=n[t]}return e}function ee(n,t,r){var e=To(n)?Hn:Rt;return r&&Or(n,t,r)&&(t=null),
|
||||
(typeof t!="function"||r!==w)&&(t=dr(t,r,3)),e(n,t)}function ue(n,t){var r;if(typeof t!="function"){if(typeof n!="function")throw new Pe(L);var e=n;n=t,t=e}return function(){return 0<--n&&(r=t.apply(this,arguments)),1>=n&&(t=null),r}}function oe(n,t,r){function e(){var r=t-(wo()-c);0>=r||r>t?(f&&eu(f),r=p,f=s=p=w,r&&(h=wo(),a=n.apply(l,i),s||f||(i=l=null))):s=su(e,r)}function u(){s&&eu(s),f=s=p=w,(v||_!==t)&&(h=wo(),a=n.apply(l,i),s||f||(i=l=null))}function o(){if(i=arguments,c=wo(),l=this,p=v&&(s||!g),
|
||||
!1===_)var r=g&&!s;else{f||g||(h=c);var o=_-(c-h),y=0>=o||o>_;y?(f&&(f=eu(f)),h=c,a=n.apply(l,i)):f||(f=su(u,o))}return y&&s?s=eu(s):s||t===_||(s=su(e,t)),r&&(y=true,a=n.apply(l,i)),!y||s||f||(i=l=null),a}var i,f,a,c,l,s,p,h=0,_=false,v=true;if(typeof n!="function")throw new Pe(L);if(t=0>t?0:+t||0,true===r)var g=true,v=false;else se(r)&&(g=r.leading,_="maxWait"in r&&bu(+r.maxWait||0,t),v="trailing"in r?r.trailing:v);return o.cancel=function(){s&&eu(s),f&&eu(f),f=s=p=w},o}function ie(n,t){function r(){var e=arguments,u=r.cache,o=t?t.apply(this,e):e[0];
|
||||
|
||||
return u.has(o)?u.get(o):(e=n.apply(this,e),u.set(o,e),e)}if(typeof n!="function"||t&&typeof t!="function")throw new Pe(L);return r.cache=new ie.Cache,r}function fe(n,t){if(typeof n!="function")throw new Pe(L);return t=bu(t===w?n.length-1:+t||0,0),function(){for(var r=arguments,e=-1,u=bu(r.length-t,0),o=Ue(u);++e<u;)o[e]=r[t+e];switch(t){case 0:return n.call(this,o);case 1:return n.call(this,r[0],o);case 2:return n.call(this,r[0],r[1],o)}for(u=Ue(t+1),e=-1;++e<t;)u[e]=r[e];return u[t]=o,n.apply(this,u);
|
||||
|
||||
}}function ae(n){return h(n)&&jr(n)&&Xe.call(n)==B}function ce(n){return!!n&&1===n.nodeType&&h(n)&&-1<Xe.call(n).indexOf("Element")}function le(n){return h(n)&&typeof n.message=="string"&&Xe.call(n)==q}function se(n){var t=typeof n;return"function"==t||!!n&&"object"==t}function pe(n){return null==n?false:Xe.call(n)==K?Qe.test(Ze.call(n)):h(n)&&En.test(n)}function he(n){return typeof n=="number"||h(n)&&Xe.call(n)==V}function _e(n){return h(n)&&Xe.call(n)==Z}function ve(n){return typeof n=="string"||h(n)&&Xe.call(n)==G;
|
||||
|
||||
}function ge(n){return h(n)&&Rr(n.length)&&!!Un[Xe.call(n)]}function ye(n){return et(n,me(n))}function de(n){return vt(n,me(n))}function me(n){if(null==n)return[];se(n)||(n=Be(n));for(var t=n.length,t=t&&Rr(t)&&(To(n)||Fu.nonEnumArgs&&ae(n))&&t||0,r=n.constructor,e=-1,r=typeof r=="function"&&r.prototype===n,u=Ue(t),o=0<t;++e<t;)u[e]=e+"";for(var i in n)o&&kr(i,t)||"constructor"==i&&(r||!Ge.call(n,i))||u.push(i);return u}function we(n){return Ut(n,Ko(n))}function be(n){return(n=u(n))&&n.replace(In,c).replace(xn,"");
|
||||
|
||||
}function xe(n){return(n=u(n))&&bn.test(n)?n.replace(wn,"\\$&"):n}function Ae(n,t,r){return r&&Or(n,t,r)&&(t=0),ku(n,t)}function je(n,t){var r="";if(n=u(n),t=+t,1>t||!n||!mu(t))return r;do t%2&&(r+=n),t=uu(t/2),n+=n;while(t);return r}function ke(n,t,r){var e=n;return(n=u(n))?(r?Or(e,t,r):null==t)?n.slice(g(n),y(n)+1):(t+="",n.slice(i(n,t),f(n,t)+1)):n}function Oe(n,t,r){return r&&Or(n,t,r)&&(t=null),n=u(n),n.match(t||Wn)||[]}function Ee(n,t,r){return r&&Or(n,t,r)&&(t=null),h(n)?Ce(n):ut(n,t)}function Ie(n){
|
||||
return function(){return n}}function Re(n){return n}function Ce(n){return wt(ot(n,true))}function We(n,t,r){if(null==r){var e=se(t),u=e&&Ko(t);((u=u&&u.length&&vt(t,u))?u.length:e)||(u=false,r=t,t=n,n=this)}u||(u=vt(t,Ko(t)));var o=true,e=-1,i=No(n),f=u.length;false===r?o=false:se(r)&&"chain"in r&&(o=r.chain);for(;++e<f;){r=u[e];var a=t[r];n[r]=a,i&&(n.prototype[r]=function(t){return function(){var r=this.__chain__;if(o||r){var e=n(this.__wrapped__);return(e.__actions__=qn(this.__actions__)).push({func:t,args:arguments,
|
||||
thisArg:n}),e.__chain__=r,e}return r=[this.value()],fu.apply(r,arguments),t.apply(n,r)}}(a))}return n}function Se(){}function Te(n){return Er(n)?At(n):jt(n)}_=_?Zn.defaults(Yn.Object(),_,Zn.pick(Yn,Tn)):Yn;var Ue=_.Array,Ne=_.Date,Fe=_.Error,$e=_.Function,Le=_.Math,ze=_.Number,Be=_.Object,Me=_.RegExp,De=_.String,Pe=_.TypeError,qe=Ue.prototype,Ke=Be.prototype,Ve=De.prototype,Ye=(Ye=_.window)&&Ye.document,Ze=$e.prototype.toString,Ge=Ke.hasOwnProperty,Je=0,Xe=Ke.toString,He=_._,Qe=Me("^"+xe(Xe).replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),nu=pe(nu=_.ArrayBuffer)&&nu,tu=pe(tu=nu&&new nu(0).slice)&&tu,ru=Le.ceil,eu=_.clearTimeout,uu=Le.floor,ou=pe(ou=Be.getOwnPropertySymbols)&&ou,iu=pe(iu=Be.getPrototypeOf)&&iu,fu=qe.push,au=pe(au=Be.preventExtensions)&&au,cu=Ke.propertyIsEnumerable,lu=pe(lu=_.Set)&&lu,su=_.setTimeout,pu=qe.splice,hu=pe(hu=_.Uint8Array)&&hu,_u=pe(_u=_.WeakMap)&&_u,vu=function(){
|
||||
try{var n=pe(n=_.Float64Array)&&n,t=new n(new nu(10),0,1)&&n}catch(r){}return t}(),gu=function(){var n=au&&pe(n=Be.assign)&&n;try{if(n){var t=au({1:0});t[0]=1}}catch(r){try{n(t,"xo")}catch(e){}return!t[1]&&n}return false}(),yu=pe(yu=Ue.isArray)&&yu,du=pe(du=Be.create)&&du,mu=_.isFinite,wu=pe(wu=Be.keys)&&wu,bu=Le.max,xu=Le.min,Au=pe(Au=Ne.now)&&Au,ju=pe(ju=ze.isFinite)&&ju,ku=_.parseInt,Ou=Le.random,Eu=ze.NEGATIVE_INFINITY,Iu=ze.POSITIVE_INFINITY,Ru=Le.pow(2,32)-1,Cu=Ru-1,Wu=Ru>>>1,Su=vu?vu.BYTES_PER_ELEMENT:0,Tu=Le.pow(2,53)-1,Uu=_u&&new _u,Nu={},Fu=$n.support={};
|
||||
|
||||
!function(n){function t(){this.x=n}var r=arguments,e=[];t.prototype={valueOf:n,y:n};for(var u in new t)e.push(u);Fu.funcDecomp=/\bthis\b/.test(function(){return this}),Fu.funcNames=typeof $e.name=="string";try{Fu.dom=11===Ye.createDocumentFragment().nodeType}catch(o){Fu.dom=false}try{Fu.nonEnumArgs=!cu.call(r,1)}catch(i){Fu.nonEnumArgs=true}}(1,0),$n.templateSettings={escape:_n,evaluate:vn,interpolate:gn,variable:"",imports:{_:$n}};var $u=gu||function(n,t){return null==t?n:et(t,Zu(t),et(t,Ko(t),n))},Lu=function(){
|
||||
function n(){}return function(t){if(se(t)){n.prototype=t;var r=new n;n.prototype=null}return r||_.Object()}}(),zu=Kt(ht),Bu=Kt(_t,true),Mu=Vt(),Du=Vt(true),Pu=Uu?function(n,t){return Uu.set(n,t),n}:Re;tu||(Bt=nu&&hu?function(n){var t=n.byteLength,r=vu?uu(t/Su):0,e=r*Su,u=new nu(t);if(r){var o=new vu(u,0,r);o.set(new vu(n,0,r))}return t!=e&&(o=new hu(u,e),o.set(new hu(n,e))),u}:Ie(null));var qu=du&&lu?function(n){return new Dn(n)}:Ie(null),Ku=Uu?function(n){return Uu.get(n)}:Se,Vu=function(){return Fu.funcNames?"constant"==Ie.name?At("name"):function(n){
|
||||
for(var t=n.name,r=Nu[t],e=r?r.length:0;e--;){var u=r[e],o=u.func;if(null==o||o==n)return u.name}return t}:Ie("")}(),Yu=At("length"),Zu=ou?function(n){return ou(Fr(n))}:Ie([]),Gu=function(){var n=0,t=0;return function(r,e){var u=wo(),o=U-(u-t);if(t=u,0<o){if(++n>=T)return r}else n=0;return Pu(r,e)}}(),Ju=fe(function(n,t){return jr(n)?ft(n,st(t,false,true)):[]}),Xu=Qt(),Hu=Qt(true),Qu=fe(function(t,r){r=st(r);var e=rt(t,r);return kt(t,r.sort(n)),e}),no=pr(),to=pr(true),ro=fe(function(n){return Tt(st(n,false,true));
|
||||
|
||||
}),eo=fe(function(n,t){return jr(n)?ft(n,t):[]}),uo=fe(Vr),oo=fe(function(n){var t=n.length,r=n[t-2],e=n[t-1];return 2<t&&typeof r=="function"?t-=2:(r=1<t&&typeof e=="function"?(--t,e):w,e=w),n.length=t,Yr(n,r,e)}),io=fe(function(n,t){return rt(n,st(t))}),fo=Pt(function(n,t,r){Ge.call(n,r)?++n[r]:n[r]=1}),ao=Ht(zu),co=Ht(Bu,true),lo=rr(Kn,zu),so=rr(function(n,t){for(var r=n.length;r--&&false!==t(n[r],r,n););return n},Bu),po=Pt(function(n,t,r){Ge.call(n,r)?n[r].push(t):n[r]=[t]}),ho=Pt(function(n,t,r){
|
||||
n[r]=t}),_o=fe(function(n,t,r){var e=-1,u=typeof t=="function",o=Er(t),i=jr(n)?Ue(n.length):[];return zu(n,function(n){var f=u?t:o&&null!=n&&n[t];i[++e]=f?f.apply(n,r):Ar(n,t,r)}),i}),vo=Pt(function(n,t,r){n[r?0:1].push(t)},function(){return[[],[]]}),go=ar(Xn,zu),yo=ar(function(n,t,r,e){var u=n.length;for(e&&u&&(r=n[--u]);u--;)r=t(r,n[u],u,n);return r},Bu),mo=fe(function(n,t){if(null==n)return[];var r=t[2];return r&&Or(t[0],t[1],r)&&(t.length=1),Wt(n,st(t),[])}),wo=Au||function(){return(new Ne).getTime();
|
||||
|
||||
},bo=fe(function(n,t,r){var e=x;if(r.length)var u=v(r,bo.placeholder),e=e|E;return hr(n,e,t,r,u)}),xo=fe(function(n,t){t=t.length?st(t):de(n);for(var r=-1,e=t.length;++r<e;){var u=t[r];n[u]=hr(n[u],x,n)}return n}),Ao=fe(function(n,t,r){var e=x|A;if(r.length)var u=v(r,Ao.placeholder),e=e|E;return hr(t,e,n,r,u)}),jo=Jt(k),ko=Jt(O),Oo=fe(function(n,t){return it(n,1,t)}),Eo=fe(function(n,t,r){return it(n,t,r)}),Io=tr(),Ro=tr(true),Co=fr(E),Wo=fr(I),So=fe(function(n,t){return hr(n,C,null,null,null,st(t));
|
||||
|
||||
}),To=yu||function(n){return h(n)&&Rr(n.length)&&Xe.call(n)==M};Fu.dom||(ce=function(n){return!!n&&1===n.nodeType&&h(n)&&!Fo(n)});var Uo=ju||function(n){return typeof n=="number"&&mu(n)},No=e(/x/)||hu&&!e(hu)?function(n){return Xe.call(n)==K}:e,Fo=iu?function(n){if(!n||Xe.call(n)!=Y)return false;var t=n.valueOf,r=pe(t)&&(r=iu(t))&&iu(r);return r?n==r||iu(n)==r:Tr(n)}:Tr,$o=qt(function(n,t,r){return r?tt(n,t,r):$u(n,t)}),Lo=fe(function(n){var t=n[0];return null==t?t:(n.push(Qn),$o.apply(w,n))}),zo=nr(ht),Bo=nr(_t),Mo=er(Mu),Do=er(Du),Po=ur(ht),qo=ur(_t),Ko=wu?function(n){
|
||||
var t=null!=n&&n.constructor;return typeof t=="function"&&t.prototype===n||typeof n!="function"&&jr(n)?Ur(n):se(n)?wu(n):[]}:Ur,Vo=or(true),Yo=or(),Zo=qt(xt),Go=fe(function(n,t){if(null==n)return{};if("function"!=typeof t[0])return t=Jn(st(t),De),Wr(n,ft(me(n),t));var r=zt(t[0],t[1],3);return Sr(n,function(n,t,e){return!r(n,t,e)})}),Jo=fe(function(n,t){return null==n?{}:"function"==typeof t[0]?Sr(n,zt(t[0],t[1],3)):Wr(n,st(t))}),Xo=Zt(function(n,t,r){return t=t.toLowerCase(),n+(r?t.charAt(0).toUpperCase()+t.slice(1):t);
|
||||
|
||||
}),Ho=Zt(function(n,t,r){return n+(r?"-":"")+t.toLowerCase()}),Qo=ir(),ni=ir(true);8!=ku(Sn+"08")&&(Ae=function(n,t,r){return(r?Or(n,t,r):null==t)?t=0:t&&(t=+t),n=ke(n),ku(n,t||(On.test(n)?16:10))});var ti=Zt(function(n,t,r){return n+(r?"_":"")+t.toLowerCase()}),ri=Zt(function(n,t,r){return n+(r?" ":"")+(t.charAt(0).toUpperCase()+t.slice(1))}),ei=fe(function(n,t){try{return n.apply(w,t)}catch(r){return le(r)?r:new Fe(r)}}),ui=fe(function(n,t){return function(r){return Ar(r,n,t)}}),oi=fe(function(n,t){
|
||||
return function(r){return Ar(n,r,t)}}),ii=Xt(function(n){for(var t=-1,r=n.length,e=Eu;++t<r;){var u=n[t];u>e&&(e=u)}return e}),fi=Xt(function(n){for(var t=-1,r=n.length,e=Iu;++t<r;){var u=n[t];u<e&&(e=u)}return e},true);return $n.prototype=Ln.prototype,zn.prototype=Lu(Ln.prototype),zn.prototype.constructor=zn,Bn.prototype=Lu(Ln.prototype),Bn.prototype.constructor=Bn,Mn.prototype["delete"]=function(n){return this.has(n)&&delete this.__data__[n]},Mn.prototype.get=function(n){return"__proto__"==n?w:this.__data__[n];
|
||||
|
||||
},Mn.prototype.has=function(n){return"__proto__"!=n&&Ge.call(this.__data__,n)},Mn.prototype.set=function(n,t){return"__proto__"!=n&&(this.__data__[n]=t),this},Dn.prototype.push=function(n){var t=this.data;typeof n=="string"||se(n)?t.set.add(n):t.hash[n]=true},ie.Cache=Mn,$n.after=function(n,t){if(typeof t!="function"){if(typeof n!="function")throw new Pe(L);var r=n;n=t,t=r}return n=mu(n=+n)?n:0,function(){return 1>--n?t.apply(this,arguments):void 0}},$n.ary=function(n,t,r){return r&&Or(n,t,r)&&(t=null),
|
||||
t=n&&null==t?n.length:bu(+t||0,0),hr(n,R,null,null,null,null,t)},$n.assign=$o,$n.at=io,$n.before=ue,$n.bind=bo,$n.bindAll=xo,$n.bindKey=Ao,$n.callback=Ee,$n.chain=Gr,$n.chunk=function(n,t,r){t=(r?Or(n,t,r):null==t)?1:bu(+t||1,1),r=0;for(var e=n?n.length:0,u=-1,o=Ue(ru(e/t));r<e;)o[++u]=It(n,r,r+=t);return o},$n.compact=function(n){for(var t=-1,r=n?n.length:0,e=-1,u=[];++t<r;){var o=n[t];o&&(u[++e]=o)}return u},$n.constant=Ie,$n.countBy=fo,$n.create=function(n,t,r){var e=Lu(n);return r&&Or(n,t,r)&&(t=null),
|
||||
t?$u(e,t):e},$n.curry=jo,$n.curryRight=ko,$n.debounce=oe,$n.defaults=Lo,$n.defer=Oo,$n.delay=Eo,$n.difference=Ju,$n.drop=zr,$n.dropRight=Br,$n.dropRightWhile=function(n,t,r){return n&&n.length?Nt(n,dr(t,r,3),true,true):[]},$n.dropWhile=function(n,t,r){return n&&n.length?Nt(n,dr(t,r,3),true):[]},$n.fill=function(n,t,r,e){var u=n?n.length:0;if(!u)return[];for(r&&typeof r!="number"&&Or(n,t,r)&&(r=0,e=u),u=n.length,r=null==r?0:+r||0,0>r&&(r=-r>u?0:u+r),e=e===w||e>u?u:+e||0,0>e&&(e+=u),u=r>e?0:e>>>0,r>>>=0;r<u;)n[r++]=t;
|
||||
|
||||
return n},$n.filter=Hr,$n.flatten=function(n,t,r){var e=n?n.length:0;return r&&Or(n,t,r)&&(t=false),e?st(n,t):[]},$n.flattenDeep=function(n){return n&&n.length?st(n,true):[]},$n.flow=Io,$n.flowRight=Ro,$n.forEach=lo,$n.forEachRight=so,$n.forIn=Mo,$n.forInRight=Do,$n.forOwn=Po,$n.forOwnRight=qo,$n.functions=de,$n.groupBy=po,$n.indexBy=ho,$n.initial=function(n){return Br(n,1)},$n.intersection=function(){for(var n=[],t=-1,e=arguments.length,u=[],o=mr(),i=o==r,f=[];++t<e;){var a=arguments[t];jr(a)&&(n.push(a),
|
||||
u.push(i&&120<=a.length?qu(t&&a):null))}if(e=n.length,2>e)return f;var i=n[0],c=-1,l=i?i.length:0,s=u[0];n:for(;++c<l;)if(a=i[c],0>(s?Pn(s,a):o(f,a,0))){for(t=e;--t;){var p=u[t];if(0>(p?Pn(p,a):o(n[t],a,0)))continue n}s&&s.push(a),f.push(a)}return f},$n.invert=function(n,t,r){r&&Or(n,t,r)&&(t=null),r=-1;for(var e=Ko(n),u=e.length,o={};++r<u;){var i=e[r],f=n[i];t?Ge.call(o,f)?o[f].push(i):o[f]=[i]:o[f]=i}return o},$n.invoke=_o,$n.keys=Ko,$n.keysIn=me,$n.map=ne,$n.mapKeys=Vo,$n.mapValues=Yo,$n.matches=Ce,
|
||||
$n.matchesProperty=function(n,t){return bt(n,ot(t,true))},$n.memoize=ie,$n.merge=Zo,$n.method=ui,$n.methodOf=oi,$n.mixin=We,$n.negate=function(n){if(typeof n!="function")throw new Pe(L);return function(){return!n.apply(this,arguments)}},$n.omit=Go,$n.once=function(n){return ue(2,n)},$n.pairs=function(n){for(var t=-1,r=Ko(n),e=r.length,u=Ue(e);++t<e;){var o=r[t];u[t]=[o,n[o]]}return u},$n.partial=Co,$n.partialRight=Wo,$n.partition=vo,$n.pick=Jo,$n.pluck=function(n,t){return ne(n,Te(t))},$n.property=Te,
|
||||
$n.propertyOf=function(n){return function(t){return gt(n,$r(t),t+"")}},$n.pull=function(){var n=arguments,t=n[0];if(!t||!t.length)return t;for(var r=0,e=mr(),u=n.length;++r<u;)for(var o=0,i=n[r];-1<(o=e(t,i,o));)pu.call(t,o,1);return t},$n.pullAt=Qu,$n.range=function(n,t,r){r&&Or(n,t,r)&&(t=r=null),n=+n||0,r=null==r?1:+r||0,null==t?(t=n,n=0):t=+t||0;var e=-1;t=bu(ru((t-n)/(r||1)),0);for(var u=Ue(t);++e<t;)u[e]=n,n+=r;return u},$n.rearg=So,$n.reject=function(n,t,r){var e=To(n)?Gn:ct;return t=dr(t,r,3),
|
||||
e(n,function(n,r,e){return!t(n,r,e)})},$n.remove=function(n,t,r){var e=[];if(!n||!n.length)return e;var u=-1,o=[],i=n.length;for(t=dr(t,r,3);++u<i;)r=n[u],t(r,u,n)&&(e.push(r),o.push(u));return kt(n,o),e},$n.rest=qr,$n.restParam=fe,$n.set=function(n,t,r){if(null==n)return n;var e=t+"";t=null!=n[e]||Er(t,n)?[e]:$r(t);for(var e=-1,u=t.length,o=u-1,i=n;null!=i&&++e<u;){var f=t[e];se(i)&&(e==o?i[f]=r:null==i[f]&&(i[f]=kr(t[e+1])?[]:{})),i=i[f]}return n},$n.shuffle=re,$n.slice=function(n,t,r){var e=n?n.length:0;
|
||||
|
||||
return e?(r&&typeof r!="number"&&Or(n,t,r)&&(t=0,r=e),It(n,t,r)):[]},$n.sortBy=function(n,t,r){if(null==n)return[];r&&Or(n,t,r)&&(t=null);var e=-1;return t=dr(t,r,3),n=mt(n,function(n,r,u){return{a:t(n,r,u),b:++e,c:n}}),Ct(n,a)},$n.sortByAll=mo,$n.sortByOrder=function(n,t,r,e){return null==n?[]:(e&&Or(t,r,e)&&(r=null),To(t)||(t=null==t?[]:[t]),To(r)||(r=null==r?[]:[r]),Wt(n,t,r))},$n.spread=function(n){if(typeof n!="function")throw new Pe(L);return function(t){return n.apply(this,t)}},$n.take=function(n,t,r){
|
||||
return n&&n.length?((r?Or(n,t,r):null==t)&&(t=1),It(n,0,0>t?0:t)):[]},$n.takeRight=function(n,t,r){var e=n?n.length:0;return e?((r?Or(n,t,r):null==t)&&(t=1),t=e-(+t||0),It(n,0>t?0:t)):[]},$n.takeRightWhile=function(n,t,r){return n&&n.length?Nt(n,dr(t,r,3),false,true):[]},$n.takeWhile=function(n,t,r){return n&&n.length?Nt(n,dr(t,r,3)):[]},$n.tap=function(n,t,r){return t.call(r,n),n},$n.throttle=function(n,t,r){var e=true,u=true;if(typeof n!="function")throw new Pe(L);return false===r?e=false:se(r)&&(e="leading"in r?!!r.leading:e,
|
||||
u="trailing"in r?!!r.trailing:u),Fn.leading=e,Fn.maxWait=+t,Fn.trailing=u,oe(n,t,Fn)},$n.thru=Jr,$n.times=function(n,t,r){if(n=uu(n),1>n||!mu(n))return[];var e=-1,u=Ue(xu(n,Ru));for(t=zt(t,r,1);++e<n;)e<Ru?u[e]=t(e):t(e);return u},$n.toArray=function(n){var t=n?Yu(n):0;return Rr(t)?t?qn(n):[]:we(n)},$n.toPlainObject=ye,$n.transform=function(n,t,r,e){var u=To(n)||ge(n);return t=dr(t,e,4),null==r&&(u||se(n)?(e=n.constructor,r=u?To(n)?new e:[]:Lu(No(e)&&e.prototype)):r={}),(u?Kn:ht)(n,function(n,e,u){
|
||||
return t(r,n,e,u)}),r},$n.union=ro,$n.uniq=Kr,$n.unzip=Vr,$n.unzipWith=Yr,$n.values=we,$n.valuesIn=function(n){return Ut(n,me(n))},$n.where=function(n,t){return Hr(n,wt(t))},$n.without=eo,$n.wrap=function(n,t){return t=null==t?Re:t,hr(t,E,null,[n],[])},$n.xor=function(){for(var n=-1,t=arguments.length;++n<t;){var r=arguments[n];if(jr(r))var e=e?ft(e,r).concat(ft(r,e)):r}return e?Tt(e):[]},$n.zip=uo,$n.zipObject=Zr,$n.zipWith=oo,$n.backflow=Ro,$n.collect=ne,$n.compose=Ro,$n.each=lo,$n.eachRight=so,
|
||||
$n.extend=$o,$n.iteratee=Ee,$n.methods=de,$n.object=Zr,$n.select=Hr,$n.tail=qr,$n.unique=Kr,We($n,$n),$n.add=function(n,t){return(+n||0)+(+t||0)},$n.attempt=ei,$n.camelCase=Xo,$n.capitalize=function(n){return(n=u(n))&&n.charAt(0).toUpperCase()+n.slice(1)},$n.clone=function(n,t,r,e){return t&&typeof t!="boolean"&&Or(n,t,r)?t=false:typeof t=="function"&&(e=r,r=t,t=false),r=typeof r=="function"&&zt(r,e,1),ot(n,t,r)},$n.cloneDeep=function(n,t,r){return t=typeof t=="function"&&zt(t,r,1),ot(n,true,t)},$n.deburr=be,
|
||||
$n.endsWith=function(n,t,r){n=u(n),t+="";var e=n.length;return r=r===w?e:xu(0>r?0:+r||0,e),r-=t.length,0<=r&&n.indexOf(t,r)==r},$n.escape=function(n){return(n=u(n))&&hn.test(n)?n.replace(sn,l):n},$n.escapeRegExp=xe,$n.every=Xr,$n.find=ao,$n.findIndex=Xu,$n.findKey=zo,$n.findLast=co,$n.findLastIndex=Hu,$n.findLastKey=Bo,$n.findWhere=function(n,t){return ao(n,wt(t))},$n.first=Mr,$n.get=function(n,t,r){return n=null==n?w:gt(n,$r(t),t+""),n===w?r:n},$n.has=function(n,t){if(null==n)return false;var r=Ge.call(n,t);
|
||||
|
||||
return r||Er(t)||(t=$r(t),n=1==t.length?n:gt(n,It(t,0,-1)),t=Pr(t),r=null!=n&&Ge.call(n,t)),r},$n.identity=Re,$n.includes=Qr,$n.indexOf=Dr,$n.inRange=function(n,t,r){return t=+t||0,"undefined"===typeof r?(r=t,t=0):r=+r||0,n>=xu(t,r)&&n<bu(t,r)},$n.isArguments=ae,$n.isArray=To,$n.isBoolean=function(n){return true===n||false===n||h(n)&&Xe.call(n)==D},$n.isDate=function(n){return h(n)&&Xe.call(n)==P},$n.isElement=ce,$n.isEmpty=function(n){return null==n?true:jr(n)&&(To(n)||ve(n)||ae(n)||h(n)&&No(n.splice))?!n.length:!Ko(n).length;
|
||||
|
||||
},$n.isEqual=function(n,t,r,e){return r=typeof r=="function"&&zt(r,e,3),!r&&Cr(n)&&Cr(t)?n===t:(e=r?r(n,t):w,e===w?yt(n,t,r):!!e)},$n.isError=le,$n.isFinite=Uo,$n.isFunction=No,$n.isMatch=function(n,t,r,e){var u=Ko(t),o=u.length;if(!o)return true;if(null==n)return false;if(r=typeof r=="function"&&zt(r,e,3),n=Fr(n),!r&&1==o){var i=u[0];if(e=t[i],Cr(e))return e===n[i]&&(e!==w||i in n)}for(var i=Ue(o),f=Ue(o);o--;)e=i[o]=t[u[o]],f[o]=Cr(e);return dt(n,u,i,f,r)},$n.isNaN=function(n){return he(n)&&n!=+n},$n.isNative=pe,
|
||||
$n.isNull=function(n){return null===n},$n.isNumber=he,$n.isObject=se,$n.isPlainObject=Fo,$n.isRegExp=_e,$n.isString=ve,$n.isTypedArray=ge,$n.isUndefined=function(n){return n===w},$n.kebabCase=Ho,$n.last=Pr,$n.lastIndexOf=function(n,t,r){var e=n?n.length:0;if(!e)return-1;var u=e;if(typeof r=="number")u=(0>r?bu(e+r,0):xu(r||0,e-1))+1;else if(r)return u=$t(n,t,true)-1,n=n[u],(t===t?t===n:n!==n)?u:-1;if(t!==t)return p(n,u,true);for(;u--;)if(n[u]===t)return u;return-1},$n.max=ii,$n.min=fi,$n.noConflict=function(){
|
||||
return _._=He,this},$n.noop=Se,$n.now=wo,$n.pad=function(n,t,r){n=u(n),t=+t;var e=n.length;return e<t&&mu(t)?(e=(t-e)/2,t=uu(e),e=ru(e),r=lr("",e,r),r.slice(0,t)+n+r):n},$n.padLeft=Qo,$n.padRight=ni,$n.parseInt=Ae,$n.random=function(n,t,r){r&&Or(n,t,r)&&(t=r=null);var e=null==n,u=null==t;return null==r&&(u&&typeof n=="boolean"?(r=n,n=1):typeof t=="boolean"&&(r=t,u=true)),e&&u&&(t=1,u=false),n=+n||0,u?(t=n,n=0):t=+t||0,r||n%1||t%1?(r=Ou(),xu(n+r*(t-n+parseFloat("1e-"+((r+"").length-1))),t)):Ot(n,t)},$n.reduce=go,
|
||||
$n.reduceRight=yo,$n.repeat=je,$n.result=function(n,t,r){var e=null==n?w:n[t];return e===w&&(null==n||Er(t,n)||(t=$r(t),n=1==t.length?n:gt(n,It(t,0,-1)),e=null==n?w:n[Pr(t)]),e=e===w?r:e),No(e)?e.call(n):e},$n.runInContext=m,$n.size=function(n){var t=n?Yu(n):0;return Rr(t)?t:Ko(n).length},$n.snakeCase=ti,$n.some=ee,$n.sortedIndex=no,$n.sortedLastIndex=to,$n.startCase=ri,$n.startsWith=function(n,t,r){return n=u(n),r=null==r?0:xu(0>r?0:+r||0,n.length),n.lastIndexOf(t,r)==r},$n.sum=function(n,t,r){r&&Or(n,t,r)&&(t=null);
|
||||
|
||||
var e=dr(),u=null==t;if(e===ut&&u||(u=false,t=e(t,r,3)),u){for(n=To(n)?n:Nr(n),t=n.length,r=0;t--;)r+=+n[t]||0;n=r}else n=St(n,t);return n},$n.template=function(n,t,r){var e=$n.templateSettings;r&&Or(n,t,r)&&(t=r=null),n=u(n),t=tt($u({},r||t),e,nt),r=tt($u({},t.imports),e.imports,nt);var o,i,f=Ko(r),a=Ut(r,f),c=0;r=t.interpolate||Rn;var l="__p+='";r=Me((t.escape||Rn).source+"|"+r.source+"|"+(r===gn?jn:Rn).source+"|"+(t.evaluate||Rn).source+"|$","g");var p="sourceURL"in t?"//# sourceURL="+t.sourceURL+"\n":"";
|
||||
|
||||
if(n.replace(r,function(t,r,e,u,f,a){return e||(e=u),l+=n.slice(c,a).replace(Cn,s),r&&(o=true,l+="'+__e("+r+")+'"),f&&(i=true,l+="';"+f+";\n__p+='"),e&&(l+="'+((__t=("+e+"))==null?'':__t)+'"),c=a+t.length,t}),l+="';",(t=t.variable)||(l="with(obj){"+l+"}"),l=(i?l.replace(fn,""):l).replace(an,"$1").replace(cn,"$1;"),l="function("+(t||"obj")+"){"+(t?"":"obj||(obj={});")+"var __t,__p=''"+(o?",__e=_.escape":"")+(i?",__j=Array.prototype.join;function print(){__p+=__j.call(arguments,'')}":";")+l+"return __p}",
|
||||
t=ei(function(){return $e(f,p+"return "+l).apply(w,a)}),t.source=l,le(t))throw t;return t},$n.trim=ke,$n.trimLeft=function(n,t,r){var e=n;return(n=u(n))?n.slice((r?Or(e,t,r):null==t)?g(n):i(n,t+"")):n},$n.trimRight=function(n,t,r){var e=n;return(n=u(n))?(r?Or(e,t,r):null==t)?n.slice(0,y(n)+1):n.slice(0,f(n,t+"")+1):n},$n.trunc=function(n,t,r){r&&Or(n,t,r)&&(t=null);var e=W;if(r=S,null!=t)if(se(t)){var o="separator"in t?t.separator:o,e="length"in t?+t.length||0:e;r="omission"in t?u(t.omission):r}else e=+t||0;
|
||||
|
||||
if(n=u(n),e>=n.length)return n;if(e-=r.length,1>e)return r;if(t=n.slice(0,e),null==o)return t+r;if(_e(o)){if(n.slice(e).search(o)){var i,f=n.slice(0,e);for(o.global||(o=Me(o.source,(kn.exec(o)||"")+"g")),o.lastIndex=0;n=o.exec(f);)i=n.index;t=t.slice(0,null==i?e:i)}}else n.indexOf(o,e)!=e&&(o=t.lastIndexOf(o),-1<o&&(t=t.slice(0,o)));return t+r},$n.unescape=function(n){return(n=u(n))&&pn.test(n)?n.replace(ln,d):n},$n.uniqueId=function(n){var t=++Je;return u(n)+t},$n.words=Oe,$n.all=Xr,$n.any=ee,$n.contains=Qr,
|
||||
$n.detect=ao,$n.foldl=go,$n.foldr=yo,$n.head=Mr,$n.include=Qr,$n.inject=go,We($n,function(){var n={};return ht($n,function(t,r){$n.prototype[r]||(n[r]=t)}),n}(),false),$n.sample=te,$n.prototype.sample=function(n){return this.__chain__||null!=n?this.thru(function(t){return te(t,n)}):te(this.value())},$n.VERSION=b,Kn("bind bindKey curry curryRight partial partialRight".split(" "),function(n){$n[n].placeholder=$n}),Kn(["dropWhile","filter","map","takeWhile"],function(n,t){var r=t!=$,e=t==N;Bn.prototype[n]=function(n,u){
|
||||
var o=this.__filtered__,i=o&&e?new Bn(this):this.clone();return(i.__iteratees__||(i.__iteratees__=[])).push({done:false,count:0,index:0,iteratee:dr(n,u,1),limit:-1,type:t}),i.__filtered__=o||r,i}}),Kn(["drop","take"],function(n,t){var r=n+"While";Bn.prototype[n]=function(r){var e=this.__filtered__,u=e&&!t?this.dropWhile():this.clone();return r=null==r?1:bu(uu(r)||0,0),e?t?u.__takeCount__=xu(u.__takeCount__,r):Pr(u.__iteratees__).limit=r:(u.__views__||(u.__views__=[])).push({size:r,type:n+(0>u.__dir__?"Right":"")
|
||||
}),u},Bn.prototype[n+"Right"]=function(t){return this.reverse()[n](t).reverse()},Bn.prototype[n+"RightWhile"]=function(n,t){return this.reverse()[r](n,t).reverse()}}),Kn(["first","last"],function(n,t){var r="take"+(t?"Right":"");Bn.prototype[n]=function(){return this[r](1).value()[0]}}),Kn(["initial","rest"],function(n,t){var r="drop"+(t?"":"Right");Bn.prototype[n]=function(){return this[r](1)}}),Kn(["pluck","where"],function(n,t){var r=t?"filter":"map",e=t?wt:Te;Bn.prototype[n]=function(n){return this[r](e(n));
|
||||
|
||||
}}),Bn.prototype.compact=function(){return this.filter(Re)},Bn.prototype.reject=function(n,t){return n=dr(n,t,1),this.filter(function(t){return!n(t)})},Bn.prototype.slice=function(n,t){n=null==n?0:+n||0;var r=this;return 0>n?r=this.takeRight(-n):n&&(r=this.drop(n)),t!==w&&(t=+t||0,r=0>t?r.dropRight(-t):r.take(t-n)),r},Bn.prototype.toArray=function(){return this.drop(0)},ht(Bn.prototype,function(n,t){var r=$n[t];if(r){var e=/^(?:filter|map|reject)|While$/.test(t),u=/^(?:first|last)$/.test(t);$n.prototype[t]=function(){
|
||||
function t(n){return n=[n],fu.apply(n,o),r.apply($n,n)}var o=arguments,i=this.__chain__,f=this.__wrapped__,a=!!this.__actions__.length,c=f instanceof Bn,l=o[0],s=c||To(f);return s&&e&&typeof l=="function"&&1!=l.length&&(c=s=false),c=c&&!a,u&&!i?c?n.call(f):r.call($n,this.value()):s?(f=n.apply(c?f:new Bn(this),o),u||!a&&!f.__actions__||(f.__actions__||(f.__actions__=[])).push({func:Jr,args:[t],thisArg:$n}),new zn(f,i)):this.thru(t)}}}),Kn("concat join pop push replace shift sort splice split unshift".split(" "),function(n){
|
||||
var t=(/^(?:replace|split)$/.test(n)?Ve:qe)[n],r=/^(?:push|sort|unshift)$/.test(n)?"tap":"thru",e=/^(?:join|pop|replace|shift)$/.test(n);$n.prototype[n]=function(){var n=arguments;return e&&!this.__chain__?t.apply(this.value(),n):this[r](function(r){return t.apply(r,n)})}}),ht(Bn.prototype,function(n,t){var r=$n[t];if(r){var e=r.name;(Nu[e]||(Nu[e]=[])).push({name:t,func:r})}}),Nu[cr(null,A).name]=[{name:"wrapper",func:null}],Bn.prototype.clone=function(){var n=this.__actions__,t=this.__iteratees__,r=this.__views__,e=new Bn(this.__wrapped__);
|
||||
|
||||
return e.__actions__=n?qn(n):null,e.__dir__=this.__dir__,e.__filtered__=this.__filtered__,e.__iteratees__=t?qn(t):null,e.__takeCount__=this.__takeCount__,e.__views__=r?qn(r):null,e},Bn.prototype.reverse=function(){if(this.__filtered__){var n=new Bn(this);n.__dir__=-1,n.__filtered__=true}else n=this.clone(),n.__dir__*=-1;return n},Bn.prototype.value=function(){var n=this.__wrapped__.value();if(!To(n))return Ft(n,this.__actions__);var t,r=this.__dir__,e=0>r;t=n.length;for(var u=this.__views__,o=0,i=-1,f=u?u.length:0;++i<f;){
|
||||
var a=u[i],c=a.size;switch(a.type){case"drop":o+=c;break;case"dropRight":t-=c;break;case"take":t=xu(t,o+c);break;case"takeRight":o=bu(o,t-c)}}t={start:o,end:t},u=t.start,o=t.end,t=o-u,u=e?o:u-1,o=xu(t,this.__takeCount__),f=(i=this.__iteratees__)?i.length:0,a=0,c=[];n:for(;t--&&a<o;){for(var u=u+r,l=-1,s=n[u];++l<f;){var p=i[l],h=p.iteratee,_=p.type;if(_==N){if(p.done&&(e?u>p.index:u<p.index)&&(p.count=0,p.done=false),p.index=u,!(p.done||(_=p.limit,p.done=-1<_?p.count++>=_:!h(s))))continue n}else if(p=h(s),
|
||||
_==$)s=p;else if(!p){if(_==F)continue n;break n}}c[a++]=s}return c},$n.prototype.chain=function(){return Gr(this)},$n.prototype.commit=function(){return new zn(this.value(),this.__chain__)},$n.prototype.plant=function(n){for(var t,r=this;r instanceof Ln;){var e=Lr(r);t?u.__wrapped__=e:t=e;var u=e,r=r.__wrapped__}return u.__wrapped__=n,t},$n.prototype.reverse=function(){var n=this.__wrapped__;return n instanceof Bn?(this.__actions__.length&&(n=new Bn(this)),new zn(n.reverse(),this.__chain__)):this.thru(function(n){
|
||||
return n.reverse()})},$n.prototype.toString=function(){return this.value()+""},$n.prototype.run=$n.prototype.toJSON=$n.prototype.valueOf=$n.prototype.value=function(){return Ft(this.__wrapped__,this.__actions__)},$n.prototype.collect=$n.prototype.map,$n.prototype.head=$n.prototype.first,$n.prototype.select=$n.prototype.filter,$n.prototype.tail=$n.prototype.rest,$n}var w,b="3.8.0",x=1,A=2,j=4,k=8,O=16,E=32,I=64,R=128,C=256,W=30,S="...",T=150,U=16,N=0,F=1,$=2,L="Expected a function",z="__lodash_placeholder__",B="[object Arguments]",M="[object Array]",D="[object Boolean]",P="[object Date]",q="[object Error]",K="[object Function]",V="[object Number]",Y="[object Object]",Z="[object RegExp]",G="[object String]",J="[object ArrayBuffer]",X="[object Float32Array]",H="[object Float64Array]",Q="[object Int8Array]",nn="[object Int16Array]",tn="[object Int32Array]",rn="[object Uint8Array]",en="[object Uint8ClampedArray]",un="[object Uint16Array]",on="[object Uint32Array]",fn=/\b__p\+='';/g,an=/\b(__p\+=)''\+/g,cn=/(__e\(.*?\)|\b__t\))\+'';/g,ln=/&(?:amp|lt|gt|quot|#39|#96);/g,sn=/[&<>"'`]/g,pn=RegExp(ln.source),hn=RegExp(sn.source),_n=/<%-([\s\S]+?)%>/g,vn=/<%([\s\S]+?)%>/g,gn=/<%=([\s\S]+?)%>/g,yn=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/,dn=/^\w*$/,mn=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g,wn=/[.*+?^${}()|[\]\/\\]/g,bn=RegExp(wn.source),xn=/[\u0300-\u036f\ufe20-\ufe23]/g,An=/\\(\\)?/g,jn=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,kn=/\w*$/,On=/^0[xX]/,En=/^\[object .+?Constructor\]$/,In=/[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g,Rn=/($^)/,Cn=/['\n\r\u2028\u2029\\]/g,Wn=RegExp("[A-Z\\xc0-\\xd6\\xd8-\\xde]+(?=[A-Z\\xc0-\\xd6\\xd8-\\xde][a-z\\xdf-\\xf6\\xf8-\\xff]+)|[A-Z\\xc0-\\xd6\\xd8-\\xde]?[a-z\\xdf-\\xf6\\xf8-\\xff]+|[A-Z\\xc0-\\xd6\\xd8-\\xde]+|[0-9]+","g"),Sn=" \t\x0b\f\xa0\ufeff\n\r\u2028\u2029\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000",Tn="Array ArrayBuffer Date Error Float32Array Float64Array Function Int8Array Int16Array Int32Array Math Number Object RegExp Set String _ clearTimeout document isFinite parseInt setTimeout TypeError Uint8Array Uint8ClampedArray Uint16Array Uint32Array WeakMap window".split(" "),Un={};
|
||||
|
||||
Un[X]=Un[H]=Un[Q]=Un[nn]=Un[tn]=Un[rn]=Un[en]=Un[un]=Un[on]=true,Un[B]=Un[M]=Un[J]=Un[D]=Un[P]=Un[q]=Un[K]=Un["[object Map]"]=Un[V]=Un[Y]=Un[Z]=Un["[object Set]"]=Un[G]=Un["[object WeakMap]"]=false;var Nn={};Nn[B]=Nn[M]=Nn[J]=Nn[D]=Nn[P]=Nn[X]=Nn[H]=Nn[Q]=Nn[nn]=Nn[tn]=Nn[V]=Nn[Y]=Nn[Z]=Nn[G]=Nn[rn]=Nn[en]=Nn[un]=Nn[on]=true,Nn[q]=Nn[K]=Nn["[object Map]"]=Nn["[object Set]"]=Nn["[object WeakMap]"]=false;var Fn={leading:false,maxWait:0,trailing:false},$n={"\xc0":"A","\xc1":"A","\xc2":"A","\xc3":"A","\xc4":"A","\xc5":"A",
|
||||
"\xe0":"a","\xe1":"a","\xe2":"a","\xe3":"a","\xe4":"a","\xe5":"a","\xc7":"C","\xe7":"c","\xd0":"D","\xf0":"d","\xc8":"E","\xc9":"E","\xca":"E","\xcb":"E","\xe8":"e","\xe9":"e","\xea":"e","\xeb":"e","\xcc":"I","\xcd":"I","\xce":"I","\xcf":"I","\xec":"i","\xed":"i","\xee":"i","\xef":"i","\xd1":"N","\xf1":"n","\xd2":"O","\xd3":"O","\xd4":"O","\xd5":"O","\xd6":"O","\xd8":"O","\xf2":"o","\xf3":"o","\xf4":"o","\xf5":"o","\xf6":"o","\xf8":"o","\xd9":"U","\xda":"U","\xdb":"U","\xdc":"U","\xf9":"u","\xfa":"u",
|
||||
"\xfb":"u","\xfc":"u","\xdd":"Y","\xfd":"y","\xff":"y","\xc6":"Ae","\xe6":"ae","\xde":"Th","\xfe":"th","\xdf":"ss"},Ln={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},zn={"&":"&","<":"<",">":">",""":'"',"'":"'","`":"`"},Bn={"function":true,object:true},Mn={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},Dn=Bn[typeof exports]&&exports&&!exports.nodeType&&exports,Pn=Bn[typeof module]&&module&&!module.nodeType&&module,qn=Bn[typeof self]&&self&&self.Object&&self,Kn=Bn[typeof window]&&window&&window.Object&&window,Vn=Pn&&Pn.exports===Dn&&Dn,Yn=Dn&&Pn&&typeof global=="object"&&global&&global.Object&&global||Kn!==(this&&this.window)&&Kn||qn||this,Zn=m();
|
||||
|
||||
typeof define=="function"&&typeof define.amd=="object"&&define.amd?(Yn._=Zn, define(function(){return Zn})):Dn&&Pn?Vn?(Pn.exports=Zn)._=Zn:Dn._=Zn:Yn._=Zn}).call(this);
|
@ -29,7 +29,7 @@ from glances.plugins.glances_plugin import GlancesPlugin
|
||||
|
||||
class Plugin(GlancesPlugin):
|
||||
|
||||
"""Glances' alert plugin.
|
||||
"""Glances alert plugin.
|
||||
|
||||
Only for display.
|
||||
"""
|
||||
@ -42,7 +42,7 @@ class Plugin(GlancesPlugin):
|
||||
self.display_curse = True
|
||||
|
||||
# Set the message position
|
||||
self.set_align('bottom')
|
||||
self.align = 'bottom'
|
||||
|
||||
# Init the stats
|
||||
self.reset()
|
||||
@ -68,17 +68,17 @@ class Plugin(GlancesPlugin):
|
||||
# Build the string message
|
||||
# Header
|
||||
if not self.stats:
|
||||
msg = _("No warning or critical alert detected")
|
||||
msg = 'No warning or critical alert detected'
|
||||
ret.append(self.curse_add_line(msg, "TITLE"))
|
||||
else:
|
||||
# Header
|
||||
msg = _("Warning or critical alerts")
|
||||
msg = 'Warning or critical alerts'
|
||||
ret.append(self.curse_add_line(msg, "TITLE"))
|
||||
logs_len = glances_logs.len()
|
||||
if logs_len > 1:
|
||||
msg = _(" (lasts {0} entries)").format(logs_len)
|
||||
msg = ' (lasts {0} entries)'.format(logs_len)
|
||||
else:
|
||||
msg = _(" (one entry)")
|
||||
msg = ' (one entry)'
|
||||
ret.append(self.curse_add_line(msg, "TITLE"))
|
||||
# Loop over alerts
|
||||
for alert in self.stats:
|
||||
@ -90,15 +90,16 @@ class Plugin(GlancesPlugin):
|
||||
# Duration
|
||||
if alert[1] > 0:
|
||||
# If finished display duration
|
||||
msg = ' ({0})'.format(datetime.fromtimestamp(alert[1]) - datetime.fromtimestamp(alert[0]))
|
||||
msg = ' ({0})'.format(datetime.fromtimestamp(alert[1]) -
|
||||
datetime.fromtimestamp(alert[0]))
|
||||
else:
|
||||
msg = _(" (ongoing)")
|
||||
msg = ' (ongoing)'
|
||||
ret.append(self.curse_add_line(msg))
|
||||
ret.append(self.curse_add_line(" - "))
|
||||
# Infos
|
||||
if alert[1] > 0:
|
||||
# If finished do not display status
|
||||
msg = _("{0} on {1}").format(alert[2], alert[3])
|
||||
msg = '{0} on {1}'.format(alert[2], alert[3])
|
||||
ret.append(self.curse_add_line(msg))
|
||||
else:
|
||||
msg = str(alert[3])
|
||||
@ -107,11 +108,12 @@ class Plugin(GlancesPlugin):
|
||||
if self.approx_equal(alert[6], alert[4], tolerance=0.1):
|
||||
msg = ' ({0:.1f})'.format(alert[5])
|
||||
else:
|
||||
msg = _(" (Min:{0:.1f} Mean:{1:.1f} Max:{2:.1f})").format(alert[6], alert[5], alert[4])
|
||||
msg = ' (Min:{0:.1f} Mean:{1:.1f} Max:{2:.1f})'.format(
|
||||
alert[6], alert[5], alert[4])
|
||||
ret.append(self.curse_add_line(msg))
|
||||
|
||||
# else:
|
||||
# msg = _(" Running...")
|
||||
# msg = ' Running...'
|
||||
# ret.append(self.curse_add_line(msg))
|
||||
|
||||
# !!! Debug only
|
||||
@ -121,9 +123,7 @@ class Plugin(GlancesPlugin):
|
||||
return ret
|
||||
|
||||
def approx_equal(self, a, b, tolerance=0.0):
|
||||
"""
|
||||
Compare a with b using the tolerance (if numerical)
|
||||
"""
|
||||
"""Compare a with b using the tolerance (if numerical)."""
|
||||
if str(int(a)).isdigit() and str(int(b)).isdigit():
|
||||
return abs(a - b) <= max(abs(a), abs(b)) * tolerance
|
||||
else:
|
||||
|
@ -28,12 +28,11 @@ try:
|
||||
import batinfo
|
||||
except ImportError:
|
||||
logger.debug("Batinfo library not found. Glances cannot grab battery info.")
|
||||
pass
|
||||
|
||||
|
||||
class Plugin(GlancesPlugin):
|
||||
|
||||
"""Glances' battery capacity plugin.
|
||||
"""Glances battery capacity plugin.
|
||||
|
||||
stats is a list
|
||||
"""
|
||||
@ -62,12 +61,12 @@ class Plugin(GlancesPlugin):
|
||||
# Reset stats
|
||||
self.reset()
|
||||
|
||||
if self.get_input() == 'local':
|
||||
if self.input_method == 'local':
|
||||
# Update stats
|
||||
self.glancesgrabbat.update()
|
||||
self.stats = self.glancesgrabbat.get()
|
||||
|
||||
elif self.get_input() == 'snmp':
|
||||
elif self.input_method == 'snmp':
|
||||
# Update stats using SNMP
|
||||
# Not avalaible
|
||||
pass
|
||||
@ -94,7 +93,10 @@ class GlancesGrabBat(object):
|
||||
"""Update the stats."""
|
||||
if self.initok:
|
||||
self.bat.update()
|
||||
self.bat_list = [{'label': _("Battery (%)"), 'value': self.getcapacitypercent()}]
|
||||
self.bat_list = [{
|
||||
'label': 'Battery',
|
||||
'value': self.battery_percent,
|
||||
'unit': '%'}]
|
||||
else:
|
||||
self.bat_list = []
|
||||
|
||||
@ -102,7 +104,8 @@ class GlancesGrabBat(object):
|
||||
"""Get the stats."""
|
||||
return self.bat_list
|
||||
|
||||
def getcapacitypercent(self):
|
||||
@property
|
||||
def battery_percent(self):
|
||||
"""Get batteries capacity percent."""
|
||||
if not self.initok or not self.bat.stat:
|
||||
return []
|
||||
@ -112,7 +115,7 @@ class GlancesGrabBat(object):
|
||||
bsum = 0
|
||||
for b in self.bat.stat:
|
||||
try:
|
||||
bsum = bsum + int(b.capacity)
|
||||
bsum += int(b.capacity)
|
||||
except ValueError:
|
||||
return []
|
||||
|
||||
|
@ -19,14 +19,14 @@
|
||||
|
||||
"""CPU core plugin."""
|
||||
|
||||
import psutil
|
||||
|
||||
from glances.plugins.glances_plugin import GlancesPlugin
|
||||
|
||||
import psutil
|
||||
|
||||
|
||||
class Plugin(GlancesPlugin):
|
||||
|
||||
"""Glances' CPU core plugin.
|
||||
"""Glances CPU core plugin.
|
||||
|
||||
Get stats about CPU core number.
|
||||
|
||||
@ -56,7 +56,7 @@ class Plugin(GlancesPlugin):
|
||||
# Reset the stats
|
||||
self.reset()
|
||||
|
||||
if self.get_input() == 'local':
|
||||
if self.input_method == 'local':
|
||||
# Update stats using the standard system lib
|
||||
|
||||
# The PSUtil 2.0 include psutil.cpu_count() and psutil.cpu_count(logical=False)
|
||||
@ -70,7 +70,7 @@ class Plugin(GlancesPlugin):
|
||||
except NameError:
|
||||
self.reset()
|
||||
|
||||
elif self.get_input() == 'snmp':
|
||||
elif self.input_method == 'snmp':
|
||||
# Update stats using SNMP
|
||||
# http://stackoverflow.com/questions/5662467/how-to-find-out-the-number-of-cpus-using-snmp
|
||||
pass
|
||||
|
@ -19,10 +19,10 @@
|
||||
|
||||
"""CPU plugin."""
|
||||
|
||||
import psutil
|
||||
|
||||
from glances.core.glances_cpu_percent import cpu_percent
|
||||
from glances.plugins.glances_plugin import GlancesPlugin
|
||||
# from glances.core.glances_logging import logger
|
||||
|
||||
import psutil
|
||||
|
||||
# SNMP OID
|
||||
# percentage of user CPU time: .1.3.6.1.4.1.2021.11.9.0
|
||||
@ -77,25 +77,25 @@ class Plugin(GlancesPlugin):
|
||||
|
||||
# Grab CPU stats using psutil's cpu_percent and cpu_times_percent
|
||||
# methods
|
||||
if self.get_input() == 'local':
|
||||
if self.input_method == 'local':
|
||||
# Get all possible values for CPU stats: user, system, idle,
|
||||
# nice (UNIX), iowait (Linux), irq (Linux, FreeBSD), steal (Linux 2.6.11+)
|
||||
# The following stats are returned by the API but not displayed in the UI:
|
||||
# softirq (Linux), guest (Linux 2.6.24+), guest_nice (Linux 3.2.0+)
|
||||
self.stats['total'] = psutil.cpu_percent(interval=0.0)
|
||||
self.stats['total'] = cpu_percent.get()
|
||||
cpu_times_percent = psutil.cpu_times_percent(interval=0.0)
|
||||
for stat in ['user', 'system', 'idle', 'nice', 'iowait',
|
||||
'irq', 'softirq', 'steal', 'guest', 'guest_nice']:
|
||||
if hasattr(cpu_times_percent, stat):
|
||||
self.stats[stat] = getattr(cpu_times_percent, stat)
|
||||
elif self.get_input() == 'snmp':
|
||||
elif self.input_method == 'snmp':
|
||||
# Update stats using SNMP
|
||||
if self.get_short_system_name() in ('windows', 'esxi'):
|
||||
if self.short_system_name in ('windows', 'esxi'):
|
||||
# Windows or VMWare ESXi
|
||||
# You can find the CPU utilization of windows system by querying the oid
|
||||
# Give also the number of core (number of element in the table)
|
||||
try:
|
||||
cpu_stats = self.set_stats_snmp(snmp_oid=snmp_oid[self.get_short_system_name()],
|
||||
cpu_stats = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name],
|
||||
bulk=True)
|
||||
except KeyError:
|
||||
self.reset()
|
||||
@ -116,10 +116,10 @@ class Plugin(GlancesPlugin):
|
||||
else:
|
||||
# Default behavor
|
||||
try:
|
||||
self.stats = self.set_stats_snmp(
|
||||
snmp_oid=snmp_oid[self.get_short_system_name()])
|
||||
self.stats = self.get_stats_snmp(
|
||||
snmp_oid=snmp_oid[self.short_system_name])
|
||||
except KeyError:
|
||||
self.stats = self.set_stats_snmp(
|
||||
self.stats = self.get_stats_snmp(
|
||||
snmp_oid=snmp_oid['default'])
|
||||
|
||||
if self.stats['idle'] == '':
|
||||
@ -140,7 +140,7 @@ class Plugin(GlancesPlugin):
|
||||
return self.stats
|
||||
|
||||
def update_views(self):
|
||||
"""Update stats views"""
|
||||
"""Update stats views."""
|
||||
# Call the father's method
|
||||
GlancesPlugin.update_views(self)
|
||||
|
||||
@ -149,9 +149,8 @@ class Plugin(GlancesPlugin):
|
||||
for key in ['user', 'system', 'iowait']:
|
||||
if key in self.stats:
|
||||
self.views[key]['decoration'] = self.get_alert_log(self.stats[key], header=key)
|
||||
self.views['total']['decoration'] = self.get_alert_log(self.stats['total'], header="system")
|
||||
# Alert only
|
||||
for key in ['steal']:
|
||||
for key in ['steal', 'total']:
|
||||
if key in self.stats:
|
||||
self.views[key]['decoration'] = self.get_alert(self.stats[key], header=key)
|
||||
# Optional
|
||||
@ -160,12 +159,12 @@ class Plugin(GlancesPlugin):
|
||||
self.views[key]['optional'] = True
|
||||
|
||||
def msg_curse(self, args=None):
|
||||
"""Return the list to display in the UI"""
|
||||
"""Return the list to display in the UI."""
|
||||
# Init the return message
|
||||
ret = []
|
||||
|
||||
# Only process if stats exist...
|
||||
if self.stats == {}:
|
||||
if not self.stats:
|
||||
return ret
|
||||
|
||||
# Build the string message
|
||||
@ -173,7 +172,7 @@ class Plugin(GlancesPlugin):
|
||||
# exemple on Windows OS)
|
||||
idle_tag = 'user' not in self.stats
|
||||
# Header
|
||||
msg = '{0:8}'.format(_("CPU"))
|
||||
msg = '{0:8}'.format('CPU')
|
||||
ret.append(self.curse_add_line(msg, "TITLE"))
|
||||
# Total CPU usage
|
||||
msg = '{0:>5}%'.format(self.stats['total'])
|
||||
@ -184,7 +183,7 @@ class Plugin(GlancesPlugin):
|
||||
ret.append(self.curse_add_line(msg))
|
||||
# Nice CPU
|
||||
if 'nice' in self.stats:
|
||||
msg = ' {0:8}'.format(_("nice:"))
|
||||
msg = ' {0:8}'.format('nice:')
|
||||
ret.append(self.curse_add_line(msg, optional=self.get_views(key='nice', option='optional')))
|
||||
msg = '{0:>5}%'.format(self.stats['nice'])
|
||||
ret.append(self.curse_add_line(msg, optional=self.get_views(key='nice', option='optional')))
|
||||
@ -192,19 +191,19 @@ class Plugin(GlancesPlugin):
|
||||
ret.append(self.curse_new_line())
|
||||
# User CPU
|
||||
if 'user' in self.stats:
|
||||
msg = '{0:8}'.format(_("user:"))
|
||||
msg = '{0:8}'.format('user:')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{0:>5}%'.format(self.stats['user'])
|
||||
ret.append(self.curse_add_line(
|
||||
msg, self.get_views(key='user', option='decoration')))
|
||||
elif 'idle' in self.stats:
|
||||
msg = '{0:8}'.format(_("idle:"))
|
||||
msg = '{0:8}'.format('idle:')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{0:>5}%'.format(self.stats['idle'])
|
||||
ret.append(self.curse_add_line(msg))
|
||||
# IRQ CPU
|
||||
if 'irq' in self.stats:
|
||||
msg = ' {0:8}'.format(_("irq:"))
|
||||
msg = ' {0:8}'.format('irq:')
|
||||
ret.append(self.curse_add_line(msg, optional=self.get_views(key='irq', option='optional')))
|
||||
msg = '{0:>5}%'.format(self.stats['irq'])
|
||||
ret.append(self.curse_add_line(msg, optional=self.get_views(key='irq', option='optional')))
|
||||
@ -212,19 +211,19 @@ class Plugin(GlancesPlugin):
|
||||
ret.append(self.curse_new_line())
|
||||
# System CPU
|
||||
if 'system' in self.stats and not idle_tag:
|
||||
msg = '{0:8}'.format(_("system:"))
|
||||
msg = '{0:8}'.format('system:')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{0:>5}%'.format(self.stats['system'])
|
||||
ret.append(self.curse_add_line(
|
||||
msg, self.get_views(key='system', option='decoration')))
|
||||
else:
|
||||
msg = '{0:8}'.format(_("core:"))
|
||||
msg = '{0:8}'.format('core:')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{0:>6}'.format(self.stats['nb_log_core'])
|
||||
ret.append(self.curse_add_line(msg))
|
||||
# IOWait CPU
|
||||
if 'iowait' in self.stats:
|
||||
msg = ' {0:8}'.format(_("iowait:"))
|
||||
msg = ' {0:8}'.format('iowait:')
|
||||
ret.append(self.curse_add_line(msg, optional=self.get_views(key='iowait', option='optional')))
|
||||
msg = '{0:>5}%'.format(self.stats['iowait'])
|
||||
ret.append(self.curse_add_line(
|
||||
@ -234,13 +233,13 @@ class Plugin(GlancesPlugin):
|
||||
ret.append(self.curse_new_line())
|
||||
# Idle CPU
|
||||
if 'idle' in self.stats and not idle_tag:
|
||||
msg = '{0:8}'.format(_("idle:"))
|
||||
msg = '{0:8}'.format('idle:')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{0:>5}%'.format(self.stats['idle'])
|
||||
ret.append(self.curse_add_line(msg))
|
||||
# Steal CPU usage
|
||||
if 'steal' in self.stats:
|
||||
msg = ' {0:8}'.format(_("steal:"))
|
||||
msg = ' {0:8}'.format('steal:')
|
||||
ret.append(self.curse_add_line(msg, optional=self.get_views(key='steal', option='optional')))
|
||||
msg = '{0:>5}%'.format(self.stats['steal'])
|
||||
ret.append(self.curse_add_line(
|
||||
|
@ -21,12 +21,13 @@
|
||||
|
||||
import operator
|
||||
|
||||
import psutil
|
||||
|
||||
# Import Glances libs
|
||||
from glances.core.glances_timer import getTimeSinceLastUpdate
|
||||
from glances.plugins.glances_plugin import GlancesPlugin
|
||||
|
||||
import psutil
|
||||
|
||||
|
||||
# Define the history items list
|
||||
# All items in this list will be historised if the --enable-history tag is set
|
||||
# 'color' define the graph color in #RGB format
|
||||
@ -36,7 +37,7 @@ items_history_list = [{'name': 'read_bytes', 'color': '#00FF00', 'y_unit': 'B/s'
|
||||
|
||||
class Plugin(GlancesPlugin):
|
||||
|
||||
"""Glances' disks I/O plugin.
|
||||
"""Glances disks I/O plugin.
|
||||
|
||||
stats is a list
|
||||
"""
|
||||
@ -53,7 +54,7 @@ class Plugin(GlancesPlugin):
|
||||
self.reset()
|
||||
|
||||
def get_key(self):
|
||||
"""Return the key of the list"""
|
||||
"""Return the key of the list."""
|
||||
return 'disk_name'
|
||||
|
||||
def reset(self):
|
||||
@ -66,7 +67,7 @@ class Plugin(GlancesPlugin):
|
||||
# Reset stats
|
||||
self.reset()
|
||||
|
||||
if self.get_input() == 'local':
|
||||
if self.input_method == 'local':
|
||||
# Update stats using the standard system lib
|
||||
# Grab the stat using the PsUtil disk_io_counters method
|
||||
# read_count: number of reads
|
||||
@ -96,16 +97,15 @@ class Plugin(GlancesPlugin):
|
||||
diskio_new = diskiocounters
|
||||
for disk in diskio_new:
|
||||
try:
|
||||
# Try necessary to manage dynamic disk creation/del
|
||||
diskstat = {}
|
||||
diskstat['time_since_update'] = time_since_update
|
||||
diskstat['disk_name'] = disk
|
||||
diskstat['read_bytes'] = (
|
||||
diskio_new[disk].read_bytes -
|
||||
read_bytes = (diskio_new[disk].read_bytes -
|
||||
self.diskio_old[disk].read_bytes)
|
||||
diskstat['write_bytes'] = (
|
||||
diskio_new[disk].write_bytes -
|
||||
write_bytes = (diskio_new[disk].write_bytes -
|
||||
self.diskio_old[disk].write_bytes)
|
||||
diskstat = {
|
||||
'time_since_update': time_since_update,
|
||||
'disk_name': disk,
|
||||
'read_bytes': read_bytes,
|
||||
'write_bytes': write_bytes}
|
||||
except KeyError:
|
||||
continue
|
||||
else:
|
||||
@ -114,7 +114,7 @@ class Plugin(GlancesPlugin):
|
||||
|
||||
# Save stats to compute next bitrate
|
||||
self.diskio_old = diskio_new
|
||||
elif self.get_input() == 'snmp':
|
||||
elif self.input_method == 'snmp':
|
||||
# Update stats using SNMP
|
||||
# No standard way for the moment...
|
||||
pass
|
||||
@ -128,7 +128,7 @@ class Plugin(GlancesPlugin):
|
||||
return self.stats
|
||||
|
||||
def update_views(self):
|
||||
"""Update stats views"""
|
||||
"""Update stats views."""
|
||||
# Call the father's method
|
||||
GlancesPlugin.update_views(self)
|
||||
|
||||
@ -152,11 +152,11 @@ class Plugin(GlancesPlugin):
|
||||
|
||||
# Build the string message
|
||||
# Header
|
||||
msg = '{0:9}'.format(_("DISK I/O"))
|
||||
msg = '{0:9}'.format('DISK I/O')
|
||||
ret.append(self.curse_add_line(msg, "TITLE"))
|
||||
msg = '{0:>7}'.format(_("R/s"))
|
||||
msg = '{0:>7}'.format('R/s')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{0:>7}'.format(_("W/s"))
|
||||
msg = '{0:>7}'.format('W/s')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
# Disk list (sorted by name)
|
||||
for i in sorted(self.stats, key=operator.itemgetter(self.get_key())):
|
||||
|
@ -19,8 +19,13 @@
|
||||
|
||||
"""Docker plugin."""
|
||||
|
||||
import numbers
|
||||
import os
|
||||
import re
|
||||
|
||||
# Import Glances libs
|
||||
from glances.core.glances_logging import logger
|
||||
from glances.core.glances_timer import getTimeSinceLastUpdate
|
||||
from glances.plugins.glances_plugin import GlancesPlugin
|
||||
|
||||
# Docker-py library (optional and Linux-only)
|
||||
@ -33,14 +38,11 @@ except ImportError as e:
|
||||
docker_tag = False
|
||||
else:
|
||||
docker_tag = True
|
||||
import os
|
||||
import re
|
||||
import numbers
|
||||
|
||||
|
||||
class Plugin(GlancesPlugin):
|
||||
|
||||
"""Glances' Docker plugin.
|
||||
"""Glances Docker plugin.
|
||||
|
||||
stats is a list
|
||||
"""
|
||||
@ -59,7 +61,7 @@ class Plugin(GlancesPlugin):
|
||||
self.docker_client = False
|
||||
|
||||
def connect(self, version=None):
|
||||
"""Connect to the Docker server"""
|
||||
"""Connect to the Docker server."""
|
||||
# Init connection to the Docker API
|
||||
try:
|
||||
if version is None:
|
||||
@ -82,7 +84,6 @@ class Plugin(GlancesPlugin):
|
||||
# API error (Version mismatch ?)
|
||||
logger.debug("Docker API error (%s)" % e)
|
||||
# Try the connection with the server version
|
||||
import re
|
||||
version = re.search('server\:\ (.*)\)\".*\)', str(e))
|
||||
if version:
|
||||
logger.debug("Try connection with Docker API version %s" % version.group(1))
|
||||
@ -112,8 +113,7 @@ class Plugin(GlancesPlugin):
|
||||
|
||||
@GlancesPlugin._log_result_decorator
|
||||
def update(self):
|
||||
"""Update Docker stats using the input method.
|
||||
"""
|
||||
"""Update Docker stats using the input method."""
|
||||
# Reset stats
|
||||
self.reset()
|
||||
|
||||
@ -129,7 +129,7 @@ class Plugin(GlancesPlugin):
|
||||
if not docker_tag or (self.args is not None and self.args.disable_docker):
|
||||
return self.stats
|
||||
|
||||
if self.get_input() == 'local':
|
||||
if self.input_method == 'local':
|
||||
# Update stats
|
||||
# Exemple: {
|
||||
# "KernelVersion": "3.16.4-tinycore64",
|
||||
@ -150,26 +150,52 @@ class Plugin(GlancesPlugin):
|
||||
# u'Names': [u'/webstack_nginx_1'],
|
||||
# u'Id': u'b0da859e84eb4019cf1d965b15e9323006e510352c402d2f442ea632d61faaa5'}]
|
||||
self.stats['containers'] = self.docker_client.containers()
|
||||
# Get CPU and MEMORY stats for containers
|
||||
# Get stats for all containers
|
||||
for c in self.stats['containers']:
|
||||
c['cpu'] = self.get_docker_cpu(c['Id'])
|
||||
c['memory'] = self.get_docker_memory(c['Id'])
|
||||
if not hasattr(self, 'docker_stats'):
|
||||
# Create a dict with all the containers' stats instance
|
||||
self.docker_stats = {}
|
||||
|
||||
elif self.get_input() == 'snmp':
|
||||
# TODO: Find a way to correct this
|
||||
# The following optimization is not compatible with the network stats
|
||||
# The self.docker_client.stats method should be call every time in order to have network stats refreshed
|
||||
# Nevertheless, if we call it every time, Glances is slow...
|
||||
if c['Id'] not in self.docker_stats:
|
||||
# Create the stats instance for the current container
|
||||
try:
|
||||
self.docker_stats[c['Id']] = self.docker_client.stats(c['Id'], decode=True)
|
||||
logger.debug("Create Docker stats object for container {}".format(c['Id']))
|
||||
except (AttributeError, docker.errors.InvalidVersion) as e:
|
||||
logger.error("Can not call Docker stats method {}".format(e))
|
||||
|
||||
# Get the docker stats
|
||||
try:
|
||||
# self.docker_stats[c['Id']] = self.docker_client.stats(c['Id'], decode=True)
|
||||
all_stats = self.docker_stats[c['Id']].next()
|
||||
except Exception:
|
||||
all_stats = {}
|
||||
|
||||
c['cpu'] = self.get_docker_cpu(c['Id'], all_stats)
|
||||
c['memory'] = self.get_docker_memory(c['Id'], all_stats)
|
||||
# c['network'] = self.get_docker_network(c['Id'], all_stats)
|
||||
|
||||
elif self.input_method == 'snmp':
|
||||
# Update stats using SNMP
|
||||
# Not available
|
||||
pass
|
||||
|
||||
return self.stats
|
||||
|
||||
def get_docker_cpu(self, id):
|
||||
"""Return the container CPU usage by reading /sys/fs/cgroup/...
|
||||
def get_docker_cpu_old(self, container_id):
|
||||
"""Return the container CPU usage by reading /sys/fs/cgroup/.
|
||||
|
||||
Input: id is the full container id
|
||||
Output: a dict {'total': 1.49, 'user': 0.65, 'system': 0.84}"""
|
||||
Output: a dict {'total': 1.49, 'user': 0.65, 'system': 0.84}
|
||||
"""
|
||||
ret = {}
|
||||
# Read the stats
|
||||
try:
|
||||
with open('/sys/fs/cgroup/cpuacct/docker/' + id + '/cpuacct.stat', 'r') as f:
|
||||
with open('/sys/fs/cgroup/cpuacct/docker/' + container_id + '/cpuacct.stat', 'r') as f:
|
||||
for line in f:
|
||||
m = re.search(r"(system|user)\s+(\d+)", line)
|
||||
if m:
|
||||
@ -177,23 +203,77 @@ class Plugin(GlancesPlugin):
|
||||
except IOError as e:
|
||||
logger.error("Can not grab container CPU stat ({0})".format(e))
|
||||
return ret
|
||||
# Get the user ticks
|
||||
ticks = self.get_user_ticks()
|
||||
if isinstance(ret["system"], numbers.Number) and isinstance(ret["user"], numbers.Number):
|
||||
ret["total"] = ret["system"] + ret["user"]
|
||||
for k in ret.keys():
|
||||
ret[k] = float(ret[k]) / ticks
|
||||
# Return the stats
|
||||
return ret
|
||||
|
||||
def get_docker_memory(self, id):
|
||||
"""Return the container MEMORY usage by reading /sys/fs/cgroup/...
|
||||
def get_docker_cpu(self, container_id, all_stats):
|
||||
"""Return the container CPU usage.
|
||||
|
||||
Input: id is the full container id
|
||||
Output: a dict {'rss': 1015808, 'cache': 356352}"""
|
||||
all_stats is the output of the stats method of the Docker API
|
||||
Output: a dict {'total': 1.49}
|
||||
"""
|
||||
cpu_new = {}
|
||||
ret = {'total': 0.0}
|
||||
|
||||
# Read the stats
|
||||
# For each container, you will find a pseudo-file cpuacct.stat,
|
||||
# containing the CPU usage accumulated by the processes of the container.
|
||||
# Those times are expressed in ticks of 1/USER_HZ of a second.
|
||||
# On x86 systems, USER_HZ is 100.
|
||||
try:
|
||||
cpu_new['total'] = all_stats['cpu_stats']['cpu_usage']['total_usage']
|
||||
cpu_new['system'] = all_stats['cpu_stats']['system_cpu_usage']
|
||||
cpu_new['nb_core'] = len(all_stats['cpu_stats']['cpu_usage']['percpu_usage'])
|
||||
except KeyError as e:
|
||||
# all_stats do not have CPU information
|
||||
logger.debug("Can not grab CPU usage for container {0} ({1}). Trying fallback method.".format(container_id, e))
|
||||
# Trying fallback to old grab method
|
||||
ret = self.get_docker_cpu_old(container_id)
|
||||
# Get the user ticks
|
||||
ticks = self.get_user_ticks()
|
||||
for k in ret.keys():
|
||||
ret[k] = float(ret[k]) / ticks
|
||||
else:
|
||||
# Previous CPU stats stored in the cpu_old variable
|
||||
if not hasattr(self, 'cpu_old'):
|
||||
# First call, we init the cpu_old variable
|
||||
self.cpu_old = {}
|
||||
try:
|
||||
self.cpu_old[container_id] = cpu_new
|
||||
except (IOError, UnboundLocalError):
|
||||
pass
|
||||
|
||||
if container_id not in self.cpu_old:
|
||||
try:
|
||||
self.cpu_old[container_id] = cpu_new
|
||||
except (IOError, UnboundLocalError):
|
||||
pass
|
||||
else:
|
||||
#
|
||||
cpu_delta = float(cpu_new['total'] - self.cpu_old[container_id]['total'])
|
||||
system_delta = float(cpu_new['system'] - self.cpu_old[container_id]['system'])
|
||||
if cpu_delta > 0.0 and system_delta > 0.0:
|
||||
ret['total'] = (cpu_delta / system_delta) * float(cpu_new['nb_core']) * 100
|
||||
|
||||
# Save stats to compute next stats
|
||||
self.cpu_old[container_id] = cpu_new
|
||||
|
||||
# Return the stats
|
||||
return ret
|
||||
|
||||
def get_docker_memory_old(self, container_id):
|
||||
"""Return the container MEMORY usage by reading /sys/fs/cgroup/.
|
||||
|
||||
Input: id is the full container id
|
||||
Output: a dict {'rss': 1015808, 'cache': 356352}
|
||||
"""
|
||||
ret = {}
|
||||
# Read the stats
|
||||
try:
|
||||
with open('/sys/fs/cgroup/memory/docker/' + id + '/memory.stat', 'r') as f:
|
||||
with open('/sys/fs/cgroup/memory/docker/' + container_id + '/memory.stat', 'r') as f:
|
||||
for line in f:
|
||||
m = re.search(r"(rss|cache)\s+(\d+)", line)
|
||||
if m:
|
||||
@ -204,8 +284,78 @@ class Plugin(GlancesPlugin):
|
||||
# Return the stats
|
||||
return ret
|
||||
|
||||
def get_docker_memory(self, container_id, all_stats):
|
||||
"""Return the container MEMORY.
|
||||
|
||||
Input: id is the full container id
|
||||
all_stats is the output of the stats method of the Docker API
|
||||
Output: a dict {'rss': 1015808, 'cache': 356352, 'usage': ..., 'max_usage': ...}
|
||||
"""
|
||||
ret = {}
|
||||
# Read the stats
|
||||
try:
|
||||
ret['rss'] = all_stats['memory_stats']['stats']['rss']
|
||||
ret['cache'] = all_stats['memory_stats']['stats']['cache']
|
||||
ret['usage'] = all_stats['memory_stats']['usage']
|
||||
ret['max_usage'] = all_stats['memory_stats']['max_usage']
|
||||
except KeyError as e:
|
||||
# all_stats do not have MEM information
|
||||
logger.debug("Can not grab MEM usage for container {0} ({1}). Trying fallback method.".format(container_id, e))
|
||||
# Trying fallback to old grab method
|
||||
ret = self.get_docker_memory_old(container_id)
|
||||
# Return the stats
|
||||
return ret
|
||||
|
||||
def get_docker_network(self, container_id, all_stats):
|
||||
"""Return the container network usage using the Docker API (v1.0 or higher).
|
||||
|
||||
Input: id is the full container id
|
||||
Output: a dict {'time_since_update': 3000, 'rx': 10, 'tx': 65}.
|
||||
"""
|
||||
# Init the returned dict
|
||||
network_new = {}
|
||||
|
||||
# Read the rx/tx stats (in bytes)
|
||||
try:
|
||||
netiocounters = all_stats["network"]
|
||||
except KeyError as e:
|
||||
# all_stats do not have NETWORK information
|
||||
logger.debug("Can not grab NET usage for container {0} ({1})".format(container_id, e))
|
||||
# No fallback available...
|
||||
return network_new
|
||||
|
||||
# Previous network interface stats are stored in the network_old variable
|
||||
if not hasattr(self, 'netiocounters_old'):
|
||||
# First call, we init the network_old var
|
||||
self.netiocounters_old = {}
|
||||
try:
|
||||
self.netiocounters_old[container_id] = netiocounters
|
||||
except (IOError, UnboundLocalError):
|
||||
pass
|
||||
|
||||
if container_id not in self.netiocounters_old:
|
||||
try:
|
||||
self.netiocounters_old[container_id] = netiocounters
|
||||
except (IOError, UnboundLocalError):
|
||||
pass
|
||||
else:
|
||||
# By storing time data we enable Rx/s and Tx/s calculations in the
|
||||
# XML/RPC API, which would otherwise be overly difficult work
|
||||
# for users of the API
|
||||
network_new['time_since_update'] = getTimeSinceLastUpdate('docker_net_{}'.format(container_id))
|
||||
network_new['rx'] = netiocounters["rx_bytes"] - self.netiocounters_old[container_id]["rx_bytes"]
|
||||
network_new['tx'] = netiocounters["tx_bytes"] - self.netiocounters_old[container_id]["tx_bytes"]
|
||||
network_new['cumulative_rx'] = netiocounters["rx_bytes"]
|
||||
network_new['cumulative_tx'] = netiocounters["tx_bytes"]
|
||||
|
||||
# Save stats to compute next bitrate
|
||||
self.netiocounters_old[container_id] = netiocounters
|
||||
|
||||
# Return the stats
|
||||
return network_new
|
||||
|
||||
def get_user_ticks(self):
|
||||
"""return the user ticks by reading the environment variable"""
|
||||
"""Return the user ticks by reading the environment variable."""
|
||||
return os.sysconf(os.sysconf_names['SC_CLK_TCK'])
|
||||
|
||||
def msg_curse(self, args=None):
|
||||
@ -214,32 +364,36 @@ class Plugin(GlancesPlugin):
|
||||
ret = []
|
||||
|
||||
# Only process if stats exist (and non null) and display plugin enable...
|
||||
if self.stats == {} or args.disable_docker or len(self.stats['containers']) == 0:
|
||||
if not self.stats or args.disable_docker or len(self.stats['containers']) == 0:
|
||||
return ret
|
||||
|
||||
# Build the string message
|
||||
# Title
|
||||
msg = '{0}'.format(_("CONTAINERS"))
|
||||
msg = '{0}'.format('CONTAINERS')
|
||||
ret.append(self.curse_add_line(msg, "TITLE"))
|
||||
msg = ' {0}'.format(len(self.stats['containers']))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = ' ({0} {1})'.format(_("served by Docker"),
|
||||
msg = ' ({0} {1})'.format('served by Docker',
|
||||
self.stats['version']["Version"])
|
||||
ret.append(self.curse_add_line(msg))
|
||||
ret.append(self.curse_new_line())
|
||||
# Header
|
||||
ret.append(self.curse_new_line())
|
||||
msg = '{0:>14}'.format(_("Id"))
|
||||
msg = '{0:>14}'.format('Id')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = ' {0:20}'.format(_("Name"))
|
||||
msg = ' {0:20}'.format('Name')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{0:>26}'.format(_("Status"))
|
||||
msg = '{0:>26}'.format('Status')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{0:>6}'.format(_("CPU%"))
|
||||
msg = '{0:>6}'.format('CPU%')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{0:>6}'.format(_("MEM"))
|
||||
msg = '{0:>7}'.format('MEM')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = ' {0:8}'.format(_("Command"))
|
||||
# msg = '{0:>6}'.format('Rx/s')
|
||||
# ret.append(self.curse_add_line(msg))
|
||||
# msg = '{0:>6}'.format('Tx/s')
|
||||
# ret.append(self.curse_add_line(msg))
|
||||
msg = ' {0:8}'.format('Command')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
# Data
|
||||
for container in self.stats['containers']:
|
||||
@ -252,7 +406,7 @@ class Plugin(GlancesPlugin):
|
||||
if len(name) > 20:
|
||||
name = '_' + name[:-19]
|
||||
else:
|
||||
name[0:20]
|
||||
name = name[:20]
|
||||
msg = ' {0:20}'.format(name)
|
||||
ret.append(self.curse_add_line(msg))
|
||||
# Status
|
||||
@ -268,10 +422,18 @@ class Plugin(GlancesPlugin):
|
||||
ret.append(self.curse_add_line(msg))
|
||||
# MEM
|
||||
try:
|
||||
msg = '{0:>6}'.format(self.auto_unit(container['memory']['rss']))
|
||||
msg = '{0:>7}'.format(self.auto_unit(container['memory']['usage']))
|
||||
except KeyError:
|
||||
msg = '{0:>6}'.format('?')
|
||||
msg = '{0:>7}'.format('?')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
# NET RX/TX
|
||||
# for r in ['rx', 'tx']:
|
||||
# try:
|
||||
# value = self.auto_unit(int(container['network'][r] // container['network']['time_since_update'] * 8)) + "b"
|
||||
# msg = '{0:>6}'.format(value)
|
||||
# except KeyError:
|
||||
# msg = '{0:>6}'.format('?')
|
||||
# ret.append(self.curse_add_line(msg))
|
||||
# Command
|
||||
msg = ' {0}'.format(container['Command'])
|
||||
ret.append(self.curse_add_line(msg))
|
||||
@ -279,7 +441,7 @@ class Plugin(GlancesPlugin):
|
||||
return ret
|
||||
|
||||
def container_alert(self, status):
|
||||
"""Analyse the container status"""
|
||||
"""Analyse the container status."""
|
||||
if "Paused" in status:
|
||||
return 'CAREFUL'
|
||||
else:
|
||||
|
@ -21,10 +21,10 @@
|
||||
|
||||
import operator
|
||||
|
||||
from glances.plugins.glances_plugin import GlancesPlugin
|
||||
|
||||
import psutil
|
||||
|
||||
from glances.plugins.glances_plugin import GlancesPlugin
|
||||
from glances.core.glances_logging import logger
|
||||
|
||||
# SNMP OID
|
||||
# The snmpd.conf needs to be edited.
|
||||
@ -64,7 +64,7 @@ items_history_list = [{'name': 'percent', 'color': '#00FF00'}]
|
||||
|
||||
class Plugin(GlancesPlugin):
|
||||
|
||||
"""Glances' file system plugin.
|
||||
"""Glances file system plugin.
|
||||
|
||||
stats is a list
|
||||
"""
|
||||
@ -81,7 +81,7 @@ class Plugin(GlancesPlugin):
|
||||
self.reset()
|
||||
|
||||
def get_key(self):
|
||||
"""Return the key of the list"""
|
||||
"""Return the key of the list."""
|
||||
return 'mnt_point'
|
||||
|
||||
def reset(self):
|
||||
@ -94,7 +94,7 @@ class Plugin(GlancesPlugin):
|
||||
# Reset the list
|
||||
self.reset()
|
||||
|
||||
if self.get_input() == 'local':
|
||||
if self.input_method == 'local':
|
||||
# Update stats using the standard system lib
|
||||
|
||||
# Grab the stats using the PsUtil disk_partitions
|
||||
@ -105,12 +105,17 @@ class Plugin(GlancesPlugin):
|
||||
except UnicodeDecodeError:
|
||||
return self.stats
|
||||
|
||||
# Optionnal hack to allow logicals mounts points (issue #448)
|
||||
# Ex: Had to put 'allow=zfs' in the [fs] section of the conf file
|
||||
# to allow zfs monitoring
|
||||
for fstype in self.get_conf_value('allow'):
|
||||
try:
|
||||
fs_stat += [f for f in psutil.disk_partitions(all=True) if f.fstype.find(fstype) >= 0]
|
||||
except UnicodeDecodeError:
|
||||
return self.stats
|
||||
|
||||
# Loop over fs
|
||||
for fs in fs_stat:
|
||||
fs_current = {}
|
||||
fs_current['device_name'] = fs.device
|
||||
fs_current['fs_type'] = fs.fstype
|
||||
fs_current['mnt_point'] = fs.mountpoint
|
||||
# Grab the disk usage
|
||||
try:
|
||||
fs_usage = psutil.disk_usage(fs.mountpoint)
|
||||
@ -118,52 +123,56 @@ class Plugin(GlancesPlugin):
|
||||
# Correct issue #346
|
||||
# Disk is ejected during the command
|
||||
continue
|
||||
fs_current['size'] = fs_usage.total
|
||||
fs_current['used'] = fs_usage.used
|
||||
fs_current['free'] = fs_usage.total - fs_usage.used
|
||||
fs_current['percent'] = fs_usage.percent
|
||||
fs_current['key'] = self.get_key()
|
||||
fs_current = {
|
||||
'device_name': fs.device,
|
||||
'fs_type': fs.fstype,
|
||||
'mnt_point': fs.mountpoint,
|
||||
'size': fs_usage.total,
|
||||
'used': fs_usage.used,
|
||||
'free': fs_usage.total - fs_usage.used,
|
||||
'percent': fs_usage.percent,
|
||||
'key': self.get_key()}
|
||||
self.stats.append(fs_current)
|
||||
|
||||
elif self.get_input() == 'snmp':
|
||||
elif self.input_method == 'snmp':
|
||||
# Update stats using SNMP
|
||||
|
||||
# SNMP bulk command to get all file system in one shot
|
||||
try:
|
||||
fs_stat = self.set_stats_snmp(snmp_oid=snmp_oid[self.get_short_system_name()],
|
||||
fs_stat = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name],
|
||||
bulk=True)
|
||||
except KeyError:
|
||||
fs_stat = self.set_stats_snmp(snmp_oid=snmp_oid['default'],
|
||||
fs_stat = self.get_stats_snmp(snmp_oid=snmp_oid['default'],
|
||||
bulk=True)
|
||||
|
||||
# Loop over fs
|
||||
if self.get_short_system_name() in ('windows', 'esxi'):
|
||||
if self.short_system_name in ('windows', 'esxi'):
|
||||
# Windows or ESXi tips
|
||||
for fs in fs_stat:
|
||||
# Memory stats are grabed in the same OID table (ignore it)
|
||||
# Memory stats are grabbed in the same OID table (ignore it)
|
||||
if fs == 'Virtual Memory' or fs == 'Physical Memory' or fs == 'Real Memory':
|
||||
continue
|
||||
fs_current = {}
|
||||
fs_current['device_name'] = ''
|
||||
fs_current['mnt_point'] = fs.partition(' ')[0]
|
||||
fs_current['size'] = int(
|
||||
fs_stat[fs]['size']) * int(fs_stat[fs]['alloc_unit'])
|
||||
fs_current['used'] = int(
|
||||
fs_stat[fs]['used']) * int(fs_stat[fs]['alloc_unit'])
|
||||
fs_current['percent'] = float(
|
||||
fs_current['used'] * 100 / fs_current['size'])
|
||||
fs_current['key'] = self.get_key()
|
||||
size = int(fs_stat[fs]['size']) * int(fs_stat[fs]['alloc_unit'])
|
||||
used = int(fs_stat[fs]['used']) * int(fs_stat[fs]['alloc_unit'])
|
||||
percent = float(used * 100 / size)
|
||||
fs_current = {
|
||||
'device_name': '',
|
||||
'mnt_point': fs.partition(' ')[0],
|
||||
'size': size,
|
||||
'used': used,
|
||||
'percent': percent,
|
||||
'key': self.get_key()}
|
||||
self.stats.append(fs_current)
|
||||
else:
|
||||
# Default behavor
|
||||
# Default behavior
|
||||
for fs in fs_stat:
|
||||
fs_current = {}
|
||||
fs_current['device_name'] = fs_stat[fs]['device_name']
|
||||
fs_current['mnt_point'] = fs
|
||||
fs_current['size'] = int(fs_stat[fs]['size']) * 1024
|
||||
fs_current['used'] = int(fs_stat[fs]['used']) * 1024
|
||||
fs_current['percent'] = float(fs_stat[fs]['percent'])
|
||||
fs_current['key'] = self.get_key()
|
||||
fs_current = {
|
||||
'device_name': fs_stat[fs]['device_name'],
|
||||
'mnt_point': fs,
|
||||
'size': int(fs_stat[fs]['size']) * 1024,
|
||||
'used': int(fs_stat[fs]['used']) * 1024,
|
||||
'percent': float(fs_stat[fs]['percent']),
|
||||
'key': self.get_key()}
|
||||
self.stats.append(fs_current)
|
||||
|
||||
# Update the history list
|
||||
@ -175,14 +184,15 @@ class Plugin(GlancesPlugin):
|
||||
return self.stats
|
||||
|
||||
def update_views(self):
|
||||
"""Update stats views"""
|
||||
"""Update stats views."""
|
||||
# Call the father's method
|
||||
GlancesPlugin.update_views(self)
|
||||
|
||||
# Add specifics informations
|
||||
# Alert
|
||||
for i in self.stats:
|
||||
self.views[i[self.get_key()]]['used']['decoration'] = self.get_alert(i['used'], max=i['size'], header=i['mnt_point'])
|
||||
self.views[i[self.get_key()]]['used']['decoration'] = self.get_alert(
|
||||
i['used'], maximum=i['size'], header=i['mnt_point'])
|
||||
|
||||
def msg_curse(self, args=None, max_width=None):
|
||||
"""Return the dict to display in the curse interface."""
|
||||
@ -202,14 +212,14 @@ class Plugin(GlancesPlugin):
|
||||
|
||||
# Build the string message
|
||||
# Header
|
||||
msg = '{0:{width}}'.format(_("FILE SYS"), width=fsname_max_width)
|
||||
msg = '{0:{width}}'.format('FILE SYS', width=fsname_max_width)
|
||||
ret.append(self.curse_add_line(msg, "TITLE"))
|
||||
if args.fs_free_space:
|
||||
msg = '{0:>7}'.format(_("Free"))
|
||||
msg = '{0:>7}'.format('Free')
|
||||
else:
|
||||
msg = '{0:>7}'.format(_("Used"))
|
||||
msg = '{0:>7}'.format('Used')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{0:>7}'.format(_("Total"))
|
||||
msg = '{0:>7}'.format('Total')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
|
||||
# Disk list (sorted by name)
|
||||
|
@ -24,12 +24,13 @@ import os
|
||||
import socket
|
||||
|
||||
# Import Glances libs
|
||||
from glances.core.glances_logging import logger
|
||||
from glances.plugins.glances_plugin import GlancesPlugin
|
||||
|
||||
|
||||
class Plugin(GlancesPlugin):
|
||||
|
||||
"""Glances' HDD temperature sensors plugin.
|
||||
"""Glances HDD temperature sensors plugin.
|
||||
|
||||
stats is a list
|
||||
"""
|
||||
@ -39,7 +40,7 @@ class Plugin(GlancesPlugin):
|
||||
GlancesPlugin.__init__(self, args=args)
|
||||
|
||||
# Init the sensor class
|
||||
self.glancesgrabhddtemp = GlancesGrabHDDTemp()
|
||||
self.glancesgrabhddtemp = GlancesGrabHDDTemp(args=args)
|
||||
|
||||
# We do not want to display the stat in a dedicated area
|
||||
# The HDD temp is displayed within the sensors plugin
|
||||
@ -57,7 +58,7 @@ class Plugin(GlancesPlugin):
|
||||
# Reset stats
|
||||
self.reset()
|
||||
|
||||
if self.get_input() == 'local':
|
||||
if self.input_method == 'local':
|
||||
# Update stats using the standard system lib
|
||||
self.stats = self.glancesgrabhddtemp.get()
|
||||
|
||||
@ -73,8 +74,9 @@ class GlancesGrabHDDTemp(object):
|
||||
|
||||
"""Get hddtemp stats using a socket connection."""
|
||||
|
||||
def __init__(self, host='127.0.0.1', port=7634):
|
||||
def __init__(self, host='127.0.0.1', port=7634, args=None):
|
||||
"""Init hddtemp stats."""
|
||||
self.args = args
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.cache = ""
|
||||
@ -89,6 +91,10 @@ 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 = self.fetch()
|
||||
|
||||
@ -125,7 +131,10 @@ class GlancesGrabHDDTemp(object):
|
||||
sck.connect((self.host, self.port))
|
||||
data = sck.recv(4096)
|
||||
sck.close()
|
||||
except socket.error:
|
||||
except socket.error as e:
|
||||
logger.warning("Can not connect to an HDDtemp server ({0}:{1} => {2})".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
|
||||
data = ""
|
||||
|
||||
return data
|
||||
|
@ -30,7 +30,7 @@ from glances.plugins.glances_plugin import GlancesPlugin
|
||||
|
||||
class Plugin(GlancesPlugin):
|
||||
|
||||
"""Glances' help plugin."""
|
||||
"""Glances help plugin."""
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
@ -42,10 +42,60 @@ class Plugin(GlancesPlugin):
|
||||
# We want to display the stat in the curse interface
|
||||
self.display_curse = True
|
||||
|
||||
# init data dictionary
|
||||
self.view_data = {}
|
||||
self.generate_view_data()
|
||||
|
||||
def update(self):
|
||||
"""No stats. It is just a plugin to display the help."""
|
||||
pass
|
||||
|
||||
def generate_view_data(self):
|
||||
self.view_data['version'] = '{0} {1}'.format(appname.title(), version)
|
||||
self.view_data['psutil_version'] = ' with PSutil {0}'.format(psutil_version)
|
||||
|
||||
try:
|
||||
self.view_data['configuration_file'] = 'Configuration file: {0}'.format(self.config.loaded_config_file)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
msg_col = ' {0:1} {1:35}'
|
||||
msg_col2 = ' {0:1} {1:35}'
|
||||
self.view_data['sort_auto'] = msg_col.format('a', 'Sort processes automatically')
|
||||
self.view_data['sort_network'] = msg_col2.format('b', 'Bytes or bits for network I/O')
|
||||
self.view_data['sort_cpu'] = msg_col.format('c', 'Sort processes by CPU%')
|
||||
self.view_data['show_hide_alert'] = msg_col2.format('l', 'Show/hide alert logs')
|
||||
self.view_data['sort_mem'] = msg_col.format('m', 'Sort processes by MEM%')
|
||||
self.view_data['sort_user'] = msg_col.format('u', 'Sort processes by USER')
|
||||
self.view_data['delete_warning_alerts'] = msg_col2.format('w', 'Delete warning alerts')
|
||||
self.view_data['sort_proc'] = msg_col.format('p', 'Sort processes by name')
|
||||
self.view_data['delete_warning_critical_alerts'] = msg_col2.format('x', 'Delete warning and critical alerts')
|
||||
self.view_data['sort_io'] = msg_col.format('i', 'Sort processes by I/O rate')
|
||||
self.view_data['percpu'] = msg_col2.format('1', 'Global CPU or per-CPU stats')
|
||||
self.view_data['sort_cpu_times'] = msg_col.format('t', 'Sort processes by TIME')
|
||||
self.view_data['show_hide_help'] = msg_col2.format('h', 'Show/hide this help screen')
|
||||
self.view_data['show_hide_diskio'] = msg_col.format('d', 'Show/hide disk I/O stats')
|
||||
self.view_data['view_network_io_combination'] = msg_col2.format('T', 'View network I/O as combination')
|
||||
self.view_data['show_hide_filesystem'] = msg_col.format('f', 'Show/hide filesystem stats')
|
||||
self.view_data['view_cumulative_network'] = msg_col2.format('U', 'View cumulative network I/O')
|
||||
self.view_data['show_hide_network'] = msg_col.format('n', 'Show/hide network stats')
|
||||
self.view_data['show_hide_filesytem_freespace'] = msg_col2.format('F', 'Show filesystem free space')
|
||||
self.view_data['show_hide_sensors'] = msg_col.format('s', 'Show/hide sensors stats')
|
||||
self.view_data['generate_graphs'] = msg_col2.format('g', 'Generate graphs for current history')
|
||||
self.view_data['show_hide_left_sidebar'] = msg_col.format('2', 'Show/hide left sidebar')
|
||||
self.view_data['reset_history'] = msg_col2.format('r', 'Reset history')
|
||||
self.view_data['enable_disable_process_stats'] = msg_col.format('z', 'Enable/disable processes stats')
|
||||
self.view_data['quit'] = msg_col2.format('q', 'Quit (Esc and Ctrl-C also work)')
|
||||
self.view_data['enable_disable_top_extends_stats'] = msg_col.format('e', 'Enable/disable top extended stats')
|
||||
self.view_data['enable_disable_short_processname'] = msg_col.format('/', 'Enable/disable short processes name')
|
||||
self.view_data['enable_disable_docker'] = msg_col2.format('D', 'Enable/disable Docker stats')
|
||||
self.view_data['enable_disable_quick_look'] = msg_col.format('3', 'Enable/disable quick look plugin')
|
||||
self.view_data['show_hide_ip'] = msg_col2.format('I', 'Show/hide IP module')
|
||||
self.view_data['edit_pattern_filter'] = 'ENTER: Edit the process filter pattern'
|
||||
|
||||
def get_view_data(self, args=None):
|
||||
return self.view_data
|
||||
|
||||
def msg_curse(self, args=None):
|
||||
"""Return the list to display in the curse interface."""
|
||||
# Init the return message
|
||||
@ -53,101 +103,69 @@ class Plugin(GlancesPlugin):
|
||||
|
||||
# Build the string message
|
||||
# Header
|
||||
msg = '{0} {1}'.format(appname.title(), version)
|
||||
ret.append(self.curse_add_line(msg, "TITLE"))
|
||||
msg = _(" with PSutil {0}").format(psutil_version)
|
||||
ret.append(self.curse_add_line(msg))
|
||||
ret.append(self.curse_add_line(self.view_data['version'], 'TITLE'))
|
||||
ret.append(self.curse_add_line(self.view_data['psutil_version']))
|
||||
ret.append(self.curse_new_line())
|
||||
|
||||
# Configuration file path
|
||||
try:
|
||||
msg = '{0}: {1}'.format(_("Configuration file"), self.config.get_loaded_config_file())
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
if 'configuration_file' in self.view_data:
|
||||
ret.append(self.curse_new_line())
|
||||
ret.append(self.curse_add_line(msg))
|
||||
ret.append(self.curse_add_line(self.view_data['configuration_file']))
|
||||
ret.append(self.curse_new_line())
|
||||
|
||||
# Keys
|
||||
msg_col = ' {0:1} {1:35}'
|
||||
msg_col2 = ' {0:1} {1:35}'
|
||||
ret.append(self.curse_new_line())
|
||||
ret.append(self.curse_add_line(self.view_data['sort_auto']))
|
||||
ret.append(self.curse_add_line(self.view_data['sort_network']))
|
||||
ret.append(self.curse_new_line())
|
||||
ret.append(self.curse_add_line(self.view_data['sort_cpu']))
|
||||
ret.append(self.curse_add_line(self.view_data['show_hide_alert']))
|
||||
ret.append(self.curse_new_line())
|
||||
|
||||
ret.append(self.curse_add_line(self.view_data['sort_mem']))
|
||||
ret.append(self.curse_add_line(self.view_data['delete_warning_alerts']))
|
||||
ret.append(self.curse_new_line())
|
||||
ret.append(self.curse_add_line(self.view_data['sort_user']))
|
||||
ret.append(self.curse_add_line(self.view_data['delete_warning_critical_alerts']))
|
||||
ret.append(self.curse_new_line())
|
||||
ret.append(self.curse_add_line(self.view_data['sort_proc']))
|
||||
ret.append(self.curse_add_line(self.view_data['percpu']))
|
||||
ret.append(self.curse_new_line())
|
||||
ret.append(self.curse_add_line(self.view_data['sort_io']))
|
||||
ret.append(self.curse_add_line(self.view_data['show_hide_ip']))
|
||||
ret.append(self.curse_new_line())
|
||||
ret.append(self.curse_add_line(self.view_data['sort_cpu_times']))
|
||||
ret.append(self.curse_add_line(self.view_data['enable_disable_docker']))
|
||||
ret.append(self.curse_new_line())
|
||||
ret.append(self.curse_add_line(self.view_data['show_hide_diskio']))
|
||||
ret.append(self.curse_add_line(self.view_data['view_network_io_combination']))
|
||||
ret.append(self.curse_new_line())
|
||||
ret.append(self.curse_add_line(self.view_data['show_hide_filesystem']))
|
||||
ret.append(self.curse_add_line(self.view_data['view_cumulative_network']))
|
||||
ret.append(self.curse_new_line())
|
||||
ret.append(self.curse_add_line(self.view_data['show_hide_network']))
|
||||
ret.append(self.curse_add_line(self.view_data['show_hide_filesytem_freespace']))
|
||||
ret.append(self.curse_new_line())
|
||||
ret.append(self.curse_add_line(self.view_data['show_hide_sensors']))
|
||||
ret.append(self.curse_add_line(self.view_data['generate_graphs']))
|
||||
ret.append(self.curse_new_line())
|
||||
ret.append(self.curse_add_line(self.view_data['show_hide_left_sidebar']))
|
||||
ret.append(self.curse_add_line(self.view_data['reset_history']))
|
||||
ret.append(self.curse_new_line())
|
||||
ret.append(self.curse_add_line(self.view_data['enable_disable_process_stats']))
|
||||
ret.append(self.curse_add_line(self.view_data['show_hide_help']))
|
||||
ret.append(self.curse_new_line())
|
||||
ret.append(self.curse_add_line(self.view_data['enable_disable_quick_look']))
|
||||
ret.append(self.curse_add_line(self.view_data['quit']))
|
||||
ret.append(self.curse_new_line())
|
||||
ret.append(self.curse_add_line(self.view_data['enable_disable_top_extends_stats']))
|
||||
ret.append(self.curse_new_line())
|
||||
ret.append(self.curse_add_line(self.view_data['enable_disable_short_processname']))
|
||||
ret.append(self.curse_new_line())
|
||||
|
||||
ret.append(self.curse_new_line())
|
||||
msg = msg_col.format("a", _("Sort processes automatically"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = msg_col2.format("b", _("Bytes or bits for network I/O"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
ret.append(self.curse_new_line())
|
||||
msg = msg_col.format("c", _("Sort processes by CPU%"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = msg_col2.format("l", _("Show/hide alert logs"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
ret.append(self.curse_new_line())
|
||||
msg = msg_col.format("m", _("Sort processes by MEM%"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = msg_col2.format("w", _("Delete warning alerts"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
ret.append(self.curse_new_line())
|
||||
msg = msg_col.format("p", _("Sort processes by name"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = msg_col2.format("x", _("Delete warning and critical alerts"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
ret.append(self.curse_new_line())
|
||||
msg = msg_col.format("i", _("Sort processes by I/O rate"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = msg_col2.format("1", _("Global CPU or per-CPU stats"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
ret.append(self.curse_new_line())
|
||||
msg = msg_col.format("t", _("Sort processes by CPU times"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = msg_col2.format("h", _("Show/hide this help screen"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
ret.append(self.curse_new_line())
|
||||
msg = msg_col.format("d", _("Show/hide disk I/O stats"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = msg_col2.format("T", _("View network I/O as combination"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
ret.append(self.curse_new_line())
|
||||
msg = msg_col.format("f", _("Show/hide filesystem stats"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = msg_col2.format("u", _("View cumulative network I/O"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
ret.append(self.curse_new_line())
|
||||
msg = msg_col.format("n", _("Show/hide network stats"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = msg_col2.format("F", _("Show filesystem free space"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
ret.append(self.curse_new_line())
|
||||
msg = msg_col.format("s", _("Show/hide sensors stats"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = msg_col2.format("g", _("Generate graphs for current history"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
ret.append(self.curse_new_line())
|
||||
msg = msg_col.format("2", _("Show/hide left sidebar"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = msg_col2.format("r", _("Reset history"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
ret.append(self.curse_new_line())
|
||||
msg = msg_col.format("z", _("Enable/disable processes stats"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = msg_col2.format("q", _("Quit (Esc and Ctrl-C also work)"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
ret.append(self.curse_new_line())
|
||||
msg = msg_col.format("e", _("Enable/disable top extended stats"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
ret.append(self.curse_new_line())
|
||||
msg = msg_col.format("/", _("Enable/disable short processes name"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
ret.append(self.curse_new_line())
|
||||
msg = msg_col.format("D", _("Enable/disable Docker stats"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
|
||||
|
||||
ret.append(self.curse_new_line())
|
||||
ret.append(self.curse_new_line())
|
||||
msg = '{0}: {1}'.format("ENTER", _("Edit the process filter pattern"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
ret.append(self.curse_add_line(self.view_data['edit_pattern_filter']))
|
||||
|
||||
# Return the message with decoration
|
||||
return ret
|
||||
|
123
glances/plugins/glances_ip.py
Normal file
@ -0,0 +1,123 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
# Copyright (C) 2015 Nicolargo <nicolas@nicolargo.com>
|
||||
#
|
||||
# Glances is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Glances is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""IP plugin."""
|
||||
|
||||
# Import system libs
|
||||
try:
|
||||
import netifaces
|
||||
netifaces_tag = True
|
||||
except ImportError:
|
||||
netifaces_tag = False
|
||||
|
||||
# Import Glances libs
|
||||
from glances.core.glances_logging import logger
|
||||
from glances.plugins.glances_plugin import GlancesPlugin
|
||||
|
||||
|
||||
class Plugin(GlancesPlugin):
|
||||
|
||||
"""Glances IP Plugin.
|
||||
|
||||
stats is a dict
|
||||
"""
|
||||
|
||||
def __init__(self, args=None):
|
||||
"""Init the plugin."""
|
||||
GlancesPlugin.__init__(self, args=args)
|
||||
|
||||
# We want to display the stat in the curse interface
|
||||
self.display_curse = True
|
||||
|
||||
# Init the stats
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
"""Reset/init the stats."""
|
||||
self.stats = {}
|
||||
|
||||
@GlancesPlugin._log_result_decorator
|
||||
def update(self):
|
||||
"""Update IP stats using the input method.
|
||||
|
||||
Stats is dict
|
||||
"""
|
||||
# Reset stats
|
||||
self.reset()
|
||||
|
||||
if self.input_method == 'local' and netifaces_tag:
|
||||
# Update stats using the netifaces lib
|
||||
try:
|
||||
default_gw = netifaces.gateways()['default'][netifaces.AF_INET]
|
||||
except KeyError:
|
||||
logger.debug("Can not grab the default gateway")
|
||||
else:
|
||||
try:
|
||||
self.stats['address'] = netifaces.ifaddresses(default_gw[1])[netifaces.AF_INET][0]['addr']
|
||||
self.stats['mask'] = netifaces.ifaddresses(default_gw[1])[netifaces.AF_INET][0]['netmask']
|
||||
self.stats['mask_cidr'] = self.ip_to_cidr(self.stats['mask'])
|
||||
self.stats['gateway'] = netifaces.gateways()['default'][netifaces.AF_INET][0]
|
||||
except KeyError as e:
|
||||
logger.debug("Can not grab IP information (%s)".format(e))
|
||||
|
||||
elif self.input_method == 'snmp':
|
||||
# Not implemented yet
|
||||
pass
|
||||
|
||||
# Update the view
|
||||
self.update_views()
|
||||
|
||||
return self.stats
|
||||
|
||||
def update_views(self):
|
||||
"""Update stats views."""
|
||||
# Call the father's method
|
||||
GlancesPlugin.update_views(self)
|
||||
|
||||
# Add specifics informations
|
||||
# Optional
|
||||
for key in self.stats.keys():
|
||||
self.views[key]['optional'] = 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 display plugin enable...
|
||||
if not self.stats or args.disable_ip:
|
||||
return ret
|
||||
|
||||
# Build the string message
|
||||
msg = ' - '
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = 'IP '
|
||||
ret.append(self.curse_add_line(msg, 'TITLE'))
|
||||
msg = '{0:}/{1}'.format(self.stats['address'], self.stats['mask_cidr'])
|
||||
ret.append(self.curse_add_line(msg))
|
||||
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def ip_to_cidr(ip):
|
||||
"""Convert IP address to CIDR.
|
||||
|
||||
Example: '255.255.255.0' will return 24
|
||||
"""
|
||||
return sum(map(lambda x: int(x) << 8, ip.split('.'))) // 8128
|
@ -44,7 +44,7 @@ items_history_list = [{'name': 'min1', 'color': '#0000FF'},
|
||||
|
||||
class Plugin(GlancesPlugin):
|
||||
|
||||
"""Glances' load plugin.
|
||||
"""Glances load plugin.
|
||||
|
||||
stats is a dict
|
||||
"""
|
||||
@ -76,7 +76,7 @@ class Plugin(GlancesPlugin):
|
||||
# Reset stats
|
||||
self.reset()
|
||||
|
||||
if self.get_input() == 'local':
|
||||
if self.input_method == 'local':
|
||||
# Update stats using the standard system lib
|
||||
|
||||
# Get the load using the os standard lib
|
||||
@ -89,9 +89,9 @@ class Plugin(GlancesPlugin):
|
||||
'min5': load[1],
|
||||
'min15': load[2],
|
||||
'cpucore': self.nb_log_core}
|
||||
elif self.get_input() == 'snmp':
|
||||
elif self.input_method == 'snmp':
|
||||
# Update stats using SNMP
|
||||
self.stats = self.set_stats_snmp(snmp_oid=snmp_oid)
|
||||
self.stats = self.get_stats_snmp(snmp_oid=snmp_oid)
|
||||
|
||||
if self.stats['min1'] == '':
|
||||
self.reset()
|
||||
@ -117,16 +117,16 @@ class Plugin(GlancesPlugin):
|
||||
return self.stats
|
||||
|
||||
def update_views(self):
|
||||
"""Update stats views"""
|
||||
"""Update stats views."""
|
||||
# Call the father's method
|
||||
GlancesPlugin.update_views(self)
|
||||
|
||||
# Add specifics informations
|
||||
try:
|
||||
# Alert and log
|
||||
self.views['min15']['decoration'] = self.get_alert_log(self.stats['min15'], max=100 * self.stats['cpucore'])
|
||||
self.views['min15']['decoration'] = self.get_alert_log(self.stats['min15'], maximum=100 * self.stats['cpucore'])
|
||||
# Alert only
|
||||
self.views['min5']['decoration'] = self.get_alert(self.stats['min5'], max=100 * self.stats['cpucore'])
|
||||
self.views['min5']['decoration'] = self.get_alert(self.stats['min5'], maximum=100 * self.stats['cpucore'])
|
||||
except KeyError:
|
||||
# try/except mandatory for Windows compatibility (no load stats)
|
||||
pass
|
||||
@ -137,28 +137,28 @@ class Plugin(GlancesPlugin):
|
||||
ret = []
|
||||
|
||||
# Only process if stats exist...
|
||||
if self.stats == {}:
|
||||
if not self.stats:
|
||||
return ret
|
||||
|
||||
# Build the string message
|
||||
# Header
|
||||
msg = '{0:8}'.format(_("LOAD"))
|
||||
msg = '{0:8}'.format('LOAD')
|
||||
ret.append(self.curse_add_line(msg, "TITLE"))
|
||||
# Core number
|
||||
if self.stats['cpucore'] > 0:
|
||||
msg = _("{0:d}-core").format(int(self.stats['cpucore']), '>1')
|
||||
msg = '{0}-core'.format(int(self.stats['cpucore']))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
# New line
|
||||
ret.append(self.curse_new_line())
|
||||
# 1min load
|
||||
msg = '{0:8}'.format(_("1 min:"))
|
||||
msg = '{0:8}'.format('1 min:')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{0:>6.2f}'.format(self.stats['min1'])
|
||||
ret.append(self.curse_add_line(msg))
|
||||
# New line
|
||||
ret.append(self.curse_new_line())
|
||||
# 5min load
|
||||
msg = '{0:8}'.format(_("5 min:"))
|
||||
msg = '{0:8}'.format('5 min:')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{0:>6.2f}'.format(self.stats['min5'])
|
||||
ret.append(self.curse_add_line(
|
||||
@ -166,7 +166,7 @@ class Plugin(GlancesPlugin):
|
||||
# New line
|
||||
ret.append(self.curse_new_line())
|
||||
# 15min load
|
||||
msg = '{0:8}'.format(_("15 min:"))
|
||||
msg = '{0:8}'.format('15 min:')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{0:>6.2f}'.format(self.stats['min15'])
|
||||
ret.append(self.curse_add_line(
|
||||
|
@ -19,10 +19,10 @@
|
||||
|
||||
"""Virtual memory plugin."""
|
||||
|
||||
import psutil
|
||||
|
||||
from glances.plugins.glances_plugin import GlancesPlugin
|
||||
|
||||
import psutil
|
||||
|
||||
# SNMP OID
|
||||
# Total RAM in machine: .1.3.6.1.4.1.2021.4.5.0
|
||||
# Total RAM used: .1.3.6.1.4.1.2021.4.6.0
|
||||
@ -78,7 +78,7 @@ class Plugin(GlancesPlugin):
|
||||
# Reset stats
|
||||
self.reset()
|
||||
|
||||
if self.get_input() == 'local':
|
||||
if self.input_method == 'local':
|
||||
# Update stats using the standard system lib
|
||||
# Grab MEM using the PSUtil virtual_memory method
|
||||
vm_stats = psutil.virtual_memory()
|
||||
@ -112,18 +112,18 @@ class Plugin(GlancesPlugin):
|
||||
self.stats['free'] += self.stats['cached']
|
||||
# used=total-free
|
||||
self.stats['used'] = self.stats['total'] - self.stats['free']
|
||||
elif self.get_input() == 'snmp':
|
||||
elif self.input_method == 'snmp':
|
||||
# Update stats using SNMP
|
||||
if self.get_short_system_name() in ('windows', 'esxi'):
|
||||
if self.short_system_name in ('windows', 'esxi'):
|
||||
# Mem stats for Windows|Vmware Esxi are stored in the FS table
|
||||
try:
|
||||
fs_stat = self.set_stats_snmp(snmp_oid=snmp_oid[self.get_short_system_name()],
|
||||
fs_stat = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name],
|
||||
bulk=True)
|
||||
except KeyError:
|
||||
self.reset()
|
||||
else:
|
||||
for fs in fs_stat:
|
||||
# The Physical Memory (Windows) or Real Memory (VmWare)
|
||||
# The Physical Memory (Windows) or Real Memory (VMware)
|
||||
# gives statistics on RAM usage and availability.
|
||||
if fs in ('Physical Memory', 'Real Memory'):
|
||||
self.stats['total'] = int(fs_stat[fs]['size']) * int(fs_stat[fs]['alloc_unit'])
|
||||
@ -133,7 +133,7 @@ class Plugin(GlancesPlugin):
|
||||
break
|
||||
else:
|
||||
# Default behavor for others OS
|
||||
self.stats = self.set_stats_snmp(snmp_oid=snmp_oid['default'])
|
||||
self.stats = self.get_stats_snmp(snmp_oid=snmp_oid['default'])
|
||||
|
||||
if self.stats['total'] == '':
|
||||
self.reset()
|
||||
@ -161,13 +161,13 @@ class Plugin(GlancesPlugin):
|
||||
return self.stats
|
||||
|
||||
def update_views(self):
|
||||
"""Update stats views"""
|
||||
"""Update stats views."""
|
||||
# Call the father's method
|
||||
GlancesPlugin.update_views(self)
|
||||
|
||||
# Add specifics informations
|
||||
# Alert and log
|
||||
self.views['used']['decoration'] = self.get_alert_log(self.stats['used'], max=self.stats['total'])
|
||||
self.views['used']['decoration'] = self.get_alert_log(self.stats['used'], maximum=self.stats['total'])
|
||||
# Optional
|
||||
for key in ['active', 'inactive', 'buffers', 'cached']:
|
||||
if key in self.stats:
|
||||
@ -179,59 +179,59 @@ class Plugin(GlancesPlugin):
|
||||
ret = []
|
||||
|
||||
# Only process if stats exist...
|
||||
if self.stats == {}:
|
||||
if not self.stats:
|
||||
return ret
|
||||
|
||||
# Build the string message
|
||||
# Header
|
||||
msg = '{0:5} '.format(_("MEM"))
|
||||
msg = '{0:5} '.format('MEM')
|
||||
ret.append(self.curse_add_line(msg, "TITLE"))
|
||||
# Percent memory usage
|
||||
msg = '{0:>7.1%}'.format(self.stats['percent'] / 100)
|
||||
ret.append(self.curse_add_line(msg))
|
||||
# Active memory usage
|
||||
if 'active' in self.stats:
|
||||
msg = ' {0:9}'.format(_("active:"))
|
||||
msg = ' {0:9}'.format('active:')
|
||||
ret.append(self.curse_add_line(msg, optional=self.get_views(key='active', option='optional')))
|
||||
msg = '{0:>7}'.format(self.auto_unit(self.stats['active']))
|
||||
ret.append(self.curse_add_line(msg, optional=self.get_views(key='active', option='optional')))
|
||||
# New line
|
||||
ret.append(self.curse_new_line())
|
||||
# Total memory usage
|
||||
msg = '{0:6}'.format(_("total:"))
|
||||
msg = '{0:6}'.format('total:')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{0:>7}'.format(self.auto_unit(self.stats['total']))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
# Inactive memory usage
|
||||
if 'inactive' in self.stats:
|
||||
msg = ' {0:9}'.format(_("inactive:"))
|
||||
msg = ' {0:9}'.format('inactive:')
|
||||
ret.append(self.curse_add_line(msg, optional=self.get_views(key='inactive', option='optional')))
|
||||
msg = '{0:>7}'.format(self.auto_unit(self.stats['inactive']))
|
||||
ret.append(self.curse_add_line(msg, optional=self.get_views(key='inactive', option='optional')))
|
||||
# New line
|
||||
ret.append(self.curse_new_line())
|
||||
# Used memory usage
|
||||
msg = '{0:6}'.format(_("used:"))
|
||||
msg = '{0:6}'.format('used:')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{0:>7}'.format(self.auto_unit(self.stats['used']))
|
||||
ret.append(self.curse_add_line(
|
||||
msg, self.get_views(key='used', option='decoration')))
|
||||
# Buffers memory usage
|
||||
if 'buffers' in self.stats:
|
||||
msg = ' {0:9}'.format(_("buffers:"))
|
||||
msg = ' {0:9}'.format('buffers:')
|
||||
ret.append(self.curse_add_line(msg, optional=self.get_views(key='buffers', option='optional')))
|
||||
msg = '{0:>7}'.format(self.auto_unit(self.stats['buffers']))
|
||||
ret.append(self.curse_add_line(msg, optional=self.get_views(key='buffers', option='optional')))
|
||||
# New line
|
||||
ret.append(self.curse_new_line())
|
||||
# Free memory usage
|
||||
msg = '{0:6}'.format(_("free:"))
|
||||
msg = '{0:6}'.format('free:')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{0:>7}'.format(self.auto_unit(self.stats['free']))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
# Cached memory usage
|
||||
if 'cached' in self.stats:
|
||||
msg = ' {0:9}'.format(_("cached:"))
|
||||
msg = ' {0:9}'.format('cached:')
|
||||
ret.append(self.curse_add_line(msg, optional=self.get_views(key='cached', option='optional')))
|
||||
msg = '{0:>7}'.format(self.auto_unit(self.stats['cached']))
|
||||
ret.append(self.curse_add_line(msg, optional=self.get_views(key='cached', option='optional')))
|
||||
|
@ -19,10 +19,10 @@
|
||||
|
||||
"""Swap memory plugin."""
|
||||
|
||||
import psutil
|
||||
|
||||
from glances.plugins.glances_plugin import GlancesPlugin
|
||||
|
||||
import psutil
|
||||
|
||||
# SNMP OID
|
||||
# Total Swap Size: .1.3.6.1.4.1.2021.4.3.0
|
||||
# Available Swap Space: .1.3.6.1.4.1.2021.4.4.0
|
||||
@ -41,7 +41,7 @@ items_history_list = [{'name': 'percent', 'color': '#00FF00', 'y_unit': '%'}]
|
||||
|
||||
class Plugin(GlancesPlugin):
|
||||
|
||||
"""Glances' swap memory plugin.
|
||||
"""Glances swap memory plugin.
|
||||
|
||||
stats is a dict
|
||||
"""
|
||||
@ -67,7 +67,7 @@ class Plugin(GlancesPlugin):
|
||||
# Reset stats
|
||||
self.reset()
|
||||
|
||||
if self.get_input() == 'local':
|
||||
if self.input_method == 'local':
|
||||
# Update stats using the standard system lib
|
||||
# Grab SWAP using the PSUtil swap_memory method
|
||||
sm_stats = psutil.swap_memory()
|
||||
@ -84,20 +84,21 @@ class Plugin(GlancesPlugin):
|
||||
'sin', 'sout']:
|
||||
if hasattr(sm_stats, swap):
|
||||
self.stats[swap] = getattr(sm_stats, swap)
|
||||
elif self.get_input() == 'snmp':
|
||||
elif self.input_method == 'snmp':
|
||||
# Update stats using SNMP
|
||||
if self.get_short_system_name() == 'windows':
|
||||
if self.short_system_name == 'windows':
|
||||
# Mem stats for Windows OS are stored in the FS table
|
||||
try:
|
||||
fs_stat = self.set_stats_snmp(snmp_oid=snmp_oid[self.get_short_system_name()],
|
||||
fs_stat = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name],
|
||||
bulk=True)
|
||||
except KeyError:
|
||||
self.reset()
|
||||
else:
|
||||
for fs in fs_stat:
|
||||
# The virtual memory concept is used by the operating system to extend (virtually) the physical
|
||||
# memory and thus to run more programs by swapping
|
||||
# unused memory zone (page) to a disk file.
|
||||
# The virtual memory concept is used by the operating
|
||||
# system to extend (virtually) the physical memory and
|
||||
# thus to run more programs by swapping unused memory
|
||||
# zone (page) to a disk file.
|
||||
if fs == 'Virtual Memory':
|
||||
self.stats['total'] = int(
|
||||
fs_stat[fs]['size']) * int(fs_stat[fs]['alloc_unit'])
|
||||
@ -109,7 +110,7 @@ class Plugin(GlancesPlugin):
|
||||
'total'] - self.stats['used']
|
||||
break
|
||||
else:
|
||||
self.stats = self.set_stats_snmp(snmp_oid=snmp_oid['default'])
|
||||
self.stats = self.get_stats_snmp(snmp_oid=snmp_oid['default'])
|
||||
|
||||
if self.stats['total'] == '':
|
||||
self.reset()
|
||||
@ -136,13 +137,13 @@ class Plugin(GlancesPlugin):
|
||||
return self.stats
|
||||
|
||||
def update_views(self):
|
||||
"""Update stats views"""
|
||||
"""Update stats views."""
|
||||
# Call the father's method
|
||||
GlancesPlugin.update_views(self)
|
||||
|
||||
# Add specifics informations
|
||||
# Alert and log
|
||||
self.views['used']['decoration'] = self.get_alert_log(self.stats['used'], max=self.stats['total'])
|
||||
self.views['used']['decoration'] = self.get_alert_log(self.stats['used'], maximum=self.stats['total'])
|
||||
|
||||
def msg_curse(self, args=None):
|
||||
"""Return the dict to display in the curse interface."""
|
||||
@ -150,12 +151,12 @@ class Plugin(GlancesPlugin):
|
||||
ret = []
|
||||
|
||||
# Only process if stats exist...
|
||||
if self.stats == {}:
|
||||
if not self.stats:
|
||||
return ret
|
||||
|
||||
# Build the string message
|
||||
# Header
|
||||
msg = '{0:7} '.format(_("SWAP"))
|
||||
msg = '{0:7} '.format('SWAP')
|
||||
ret.append(self.curse_add_line(msg, "TITLE"))
|
||||
# Percent memory usage
|
||||
msg = '{0:>6.1%}'.format(self.stats['percent'] / 100)
|
||||
@ -163,14 +164,14 @@ class Plugin(GlancesPlugin):
|
||||
# New line
|
||||
ret.append(self.curse_new_line())
|
||||
# Total memory usage
|
||||
msg = '{0:8}'.format(_("total:"))
|
||||
msg = '{0:8}'.format('total:')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{0:>6}'.format(self.auto_unit(self.stats['total']))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
# New line
|
||||
ret.append(self.curse_new_line())
|
||||
# Used memory usage
|
||||
msg = '{0:8}'.format(_("used:"))
|
||||
msg = '{0:8}'.format('used:')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{0:>6}'.format(self.auto_unit(self.stats['used']))
|
||||
ret.append(self.curse_add_line(
|
||||
@ -178,7 +179,7 @@ class Plugin(GlancesPlugin):
|
||||
# New line
|
||||
ret.append(self.curse_new_line())
|
||||
# Free memory usage
|
||||
msg = '{0:8}'.format(_("free:"))
|
||||
msg = '{0:8}'.format('free:')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{0:>6}'.format(self.auto_unit(self.stats['free']))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
|
@ -20,14 +20,13 @@
|
||||
"""Monitor plugin."""
|
||||
|
||||
# Import Glances lib
|
||||
from glances.core.glances_logging import logger
|
||||
from glances.core.glances_monitor_list import MonitorList as glancesMonitorList
|
||||
from glances.plugins.glances_plugin import GlancesPlugin
|
||||
|
||||
|
||||
class Plugin(GlancesPlugin):
|
||||
|
||||
"""Glances' monitor plugin."""
|
||||
"""Glances monitor plugin."""
|
||||
|
||||
def __init__(self, args=None):
|
||||
"""Init the plugin."""
|
||||
@ -41,13 +40,12 @@ class Plugin(GlancesPlugin):
|
||||
self.stats = []
|
||||
|
||||
def load_limits(self, config):
|
||||
"""Load the monitored list from the conf file."""
|
||||
logger.debug("Monitor plugin configuration detected in the configuration file")
|
||||
"""Load the monitored list from the config file, if it exists."""
|
||||
self.glances_monitors = glancesMonitorList(config)
|
||||
|
||||
def update(self):
|
||||
"""Update the monitored list."""
|
||||
if self.get_input() == 'local':
|
||||
if self.input_method == 'local':
|
||||
# Monitor list only available in a full Glances environment
|
||||
# Check if the glances_monitor instance is init
|
||||
if self.glances_monitors is None:
|
||||
@ -98,7 +96,7 @@ class Plugin(GlancesPlugin):
|
||||
msg, self.get_alert(m['count'], m['countmin'], m['countmax'])))
|
||||
msg = '{0:<3} '.format(m['count'] if m['count'] > 1 else '')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{0:13} '.format(_("RUNNING") if m['count'] >= 1 else _("NOT RUNNING"))
|
||||
msg = '{0:13} '.format('RUNNING' if m['count'] >= 1 else 'NOT RUNNING')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
# Decode to UTF8 (only for Python 3)
|
||||
try:
|
||||
|
@ -22,11 +22,11 @@
|
||||
import base64
|
||||
import operator
|
||||
|
||||
import psutil
|
||||
|
||||
from glances.core.glances_timer import getTimeSinceLastUpdate
|
||||
from glances.plugins.glances_plugin import GlancesPlugin
|
||||
|
||||
import psutil
|
||||
|
||||
# SNMP OID
|
||||
# http://www.net-snmp.org/docs/mibs/interfaces.html
|
||||
# Dict key = interface_name
|
||||
@ -43,7 +43,7 @@ items_history_list = [{'name': 'rx', 'color': '#00FF00', 'y_unit': 'bit/s'},
|
||||
|
||||
class Plugin(GlancesPlugin):
|
||||
|
||||
"""Glances' network Plugin.
|
||||
"""Glances network plugin.
|
||||
|
||||
stats is a list
|
||||
"""
|
||||
@ -59,7 +59,7 @@ class Plugin(GlancesPlugin):
|
||||
self.reset()
|
||||
|
||||
def get_key(self):
|
||||
"""Return the key of the list"""
|
||||
"""Return the key of the list."""
|
||||
return 'interface_name'
|
||||
|
||||
def reset(self):
|
||||
@ -75,7 +75,7 @@ class Plugin(GlancesPlugin):
|
||||
# Reset stats
|
||||
self.reset()
|
||||
|
||||
if self.get_input() == 'local':
|
||||
if self.input_method == 'local':
|
||||
# Update stats using the standard system lib
|
||||
|
||||
# Grab network interface stat using the PsUtil net_io_counter method
|
||||
@ -101,19 +101,21 @@ class Plugin(GlancesPlugin):
|
||||
network_new = netiocounters
|
||||
for net in network_new:
|
||||
try:
|
||||
# Try necessary to manage dynamic network interface
|
||||
netstat = {}
|
||||
netstat['interface_name'] = net
|
||||
netstat['time_since_update'] = time_since_update
|
||||
netstat['cumulative_rx'] = network_new[net].bytes_recv
|
||||
netstat['rx'] = (network_new[net].bytes_recv -
|
||||
self.network_old[net].bytes_recv)
|
||||
netstat['cumulative_tx'] = network_new[net].bytes_sent
|
||||
netstat['tx'] = (network_new[net].bytes_sent -
|
||||
self.network_old[net].bytes_sent)
|
||||
netstat['cumulative_cx'] = (netstat['cumulative_rx'] +
|
||||
netstat['cumulative_tx'])
|
||||
netstat['cx'] = netstat['rx'] + netstat['tx']
|
||||
cumulative_rx = network_new[net].bytes_recv
|
||||
cumulative_tx = network_new[net].bytes_sent
|
||||
cumulative_cx = cumulative_rx + cumulative_tx
|
||||
rx = cumulative_rx - self.network_old[net].bytes_recv
|
||||
tx = cumulative_tx - self.network_old[net].bytes_sent
|
||||
cx = rx + tx
|
||||
netstat = {
|
||||
'interface_name': net,
|
||||
'time_since_update': time_since_update,
|
||||
'cumulative_rx': cumulative_rx,
|
||||
'rx': rx,
|
||||
'cumulative_tx': cumulative_tx,
|
||||
'tx': tx,
|
||||
'cumulative_cx': cumulative_cx,
|
||||
'cx': cx}
|
||||
except KeyError:
|
||||
continue
|
||||
else:
|
||||
@ -123,15 +125,15 @@ class Plugin(GlancesPlugin):
|
||||
# Save stats to compute next bitrate
|
||||
self.network_old = network_new
|
||||
|
||||
elif self.get_input() == 'snmp':
|
||||
elif self.input_method == 'snmp':
|
||||
# Update stats using SNMP
|
||||
|
||||
# SNMP bulk command to get all network interface in one shot
|
||||
try:
|
||||
netiocounters = self.set_stats_snmp(snmp_oid=snmp_oid[self.get_short_system_name()],
|
||||
netiocounters = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name],
|
||||
bulk=True)
|
||||
except KeyError:
|
||||
netiocounters = self.set_stats_snmp(snmp_oid=snmp_oid['default'],
|
||||
netiocounters = self.get_stats_snmp(snmp_oid=snmp_oid['default'],
|
||||
bulk=True)
|
||||
|
||||
# Previous network interface stats are stored in the network_old variable
|
||||
@ -150,27 +152,30 @@ class Plugin(GlancesPlugin):
|
||||
|
||||
for net in network_new:
|
||||
try:
|
||||
# Try necessary to manage dynamic network interface
|
||||
netstat = {}
|
||||
# Windows: a tips is needed to convert HEX to TXT
|
||||
# http://blogs.technet.com/b/networking/archive/2009/12/18/how-to-query-the-list-of-network-interfaces-using-snmp-via-the-ifdescr-counter.aspx
|
||||
if self.get_short_system_name() == 'windows':
|
||||
if self.short_system_name == 'windows':
|
||||
try:
|
||||
netstat['interface_name'] = str(base64.b16decode(net[2:-2].upper()))
|
||||
interface_name = str(base64.b16decode(net[2:-2].upper()))
|
||||
except TypeError:
|
||||
netstat['interface_name'] = net
|
||||
interface_name = net
|
||||
else:
|
||||
netstat['interface_name'] = net
|
||||
netstat['time_since_update'] = time_since_update
|
||||
netstat['cumulative_rx'] = float(network_new[net]['cumulative_rx'])
|
||||
netstat['rx'] = (float(network_new[net]['cumulative_rx']) -
|
||||
float(self.network_old[net]['cumulative_rx']))
|
||||
netstat['cumulative_tx'] = float(network_new[net]['cumulative_tx'])
|
||||
netstat['tx'] = (float(network_new[net]['cumulative_tx']) -
|
||||
float(self.network_old[net]['cumulative_tx']))
|
||||
netstat['cumulative_cx'] = (netstat['cumulative_rx'] +
|
||||
netstat['cumulative_tx'])
|
||||
netstat['cx'] = netstat['rx'] + netstat['tx']
|
||||
interface_name = net
|
||||
cumulative_rx = float(network_new[net]['cumulative_rx'])
|
||||
cumulative_tx = float(network_new[net]['cumulative_tx'])
|
||||
cumulative_cx = cumulative_rx + cumulative_tx
|
||||
rx = cumulative_rx - float(self.network_old[net]['cumulative_rx'])
|
||||
tx = cumulative_tx - float(self.network_old[net]['cumulative_tx'])
|
||||
cx = rx + tx
|
||||
netstat = {
|
||||
'interface_name': interface_name,
|
||||
'time_since_update': time_since_update,
|
||||
'cumulative_rx': cumulative_rx,
|
||||
'rx': rx,
|
||||
'cumulative_tx': cumulative_tx,
|
||||
'tx': tx,
|
||||
'cumulative_cx': cumulative_cx,
|
||||
'cx': cx}
|
||||
except KeyError:
|
||||
continue
|
||||
else:
|
||||
@ -189,7 +194,7 @@ class Plugin(GlancesPlugin):
|
||||
return self.stats
|
||||
|
||||
def update_views(self):
|
||||
"""Update stats views"""
|
||||
"""Update stats views."""
|
||||
# Call the father's method
|
||||
GlancesPlugin.update_views(self)
|
||||
|
||||
@ -204,7 +209,6 @@ class Plugin(GlancesPlugin):
|
||||
|
||||
def msg_curse(self, args=None, max_width=None):
|
||||
"""Return the dict to display in the curse interface."""
|
||||
|
||||
# Init the return message
|
||||
ret = []
|
||||
|
||||
@ -221,30 +225,30 @@ class Plugin(GlancesPlugin):
|
||||
|
||||
# Build the string message
|
||||
# Header
|
||||
msg = '{0:{width}}'.format(_("NETWORK"), width=ifname_max_width)
|
||||
msg = '{0:{width}}'.format('NETWORK', width=ifname_max_width)
|
||||
ret.append(self.curse_add_line(msg, "TITLE"))
|
||||
if args.network_cumul:
|
||||
# Cumulative stats
|
||||
if args.network_sum:
|
||||
# Sum stats
|
||||
msg = '{0:>14}'.format(_("Rx+Tx"))
|
||||
msg = '{0:>14}'.format('Rx+Tx')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
else:
|
||||
# Rx/Tx stats
|
||||
msg = '{0:>7}'.format(_("Rx"))
|
||||
msg = '{0:>7}'.format('Rx')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{0:>7}'.format(_("Tx"))
|
||||
msg = '{0:>7}'.format('Tx')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
else:
|
||||
# Bitrate stats
|
||||
if args.network_sum:
|
||||
# Sum stats
|
||||
msg = '{0:>14}'.format(_("Rx+Tx/s"))
|
||||
msg = '{0:>14}'.format('Rx+Tx/s')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
else:
|
||||
msg = '{0:>7}'.format(_("Rx/s"))
|
||||
msg = '{0:>7}'.format('Rx/s')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{0:>7}'.format(_("Tx/s"))
|
||||
msg = '{0:>7}'.format('Tx/s')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
# Interface list (sorted by name)
|
||||
for i in sorted(self.stats, key=operator.itemgetter(self.get_key())):
|
||||
@ -265,20 +269,20 @@ class Plugin(GlancesPlugin):
|
||||
if args.network_cumul:
|
||||
rx = self.auto_unit(int(i['cumulative_rx']))
|
||||
tx = self.auto_unit(int(i['cumulative_tx']))
|
||||
sx = self.auto_unit(int(i['cumulative_tx'])
|
||||
+ int(i['cumulative_tx']))
|
||||
sx = self.auto_unit(int(i['cumulative_tx']) +
|
||||
int(i['cumulative_tx']))
|
||||
else:
|
||||
rx = self.auto_unit(int(i['rx'] // i['time_since_update']))
|
||||
tx = self.auto_unit(int(i['tx'] // i['time_since_update']))
|
||||
sx = self.auto_unit(int(i['rx'] // i['time_since_update'])
|
||||
+ int(i['tx'] // i['time_since_update']))
|
||||
sx = self.auto_unit(int(i['rx'] // i['time_since_update']) +
|
||||
int(i['tx'] // i['time_since_update']))
|
||||
else:
|
||||
# Bits per second (for real network administrator | Default)
|
||||
if args.network_cumul:
|
||||
rx = self.auto_unit(int(i['cumulative_rx'] * 8)) + "b"
|
||||
tx = self.auto_unit(int(i['cumulative_tx'] * 8)) + "b"
|
||||
sx = self.auto_unit(int(i['cumulative_rx'] * 8)
|
||||
+ int(i['cumulative_tx'] * 8)) + "b"
|
||||
sx = self.auto_unit(int(i['cumulative_rx'] * 8) +
|
||||
int(i['cumulative_tx'] * 8)) + "b"
|
||||
else:
|
||||
rx = self.auto_unit(int(i['rx'] // i['time_since_update'] * 8)) + "b"
|
||||
tx = self.auto_unit(int(i['tx'] // i['time_since_update'] * 8)) + "b"
|
||||
|