Merge branch 'develop' into issue1985

This commit is contained in:
nicolargo 2023-02-19 10:02:49 +01:00
commit 89a7383630
30 changed files with 3826 additions and 962 deletions

View File

@ -136,7 +136,7 @@ jobs:
password: ${{ secrets.DOCKER_TOKEN }}
- name: Build and push image
uses: docker/build-push-action@v3
uses: docker/build-push-action@v4
with:
push: ${{ env.PUSH_BRANCH == 'true' }}
tags: "${{ env.DEFAULT_DOCKER_IMAGE }}:${{ matrix.os != 'alpine' && format('{0}-', matrix.os) || '' }}${{ matrix.tag.tag }}"

View File

@ -64,6 +64,9 @@ flake8: venv-dev-upgrade ## Run flake8 linter.
codespell: venv-dev-upgrade ## Run codespell to fix common misspellings in text files
./venv/bin/codespell -S .git,./docs/_build,./Glances.egg-info,./venv,./glances/outputs,*.svg -L hart,bu,te,statics
semgrep: venv-dev-upgrade ## Run semgrep to find bugs and enforce code standards
./venv/bin/semgrep --config=auto --lang python --use-git-ignore ./glances
profiling: ## How to start the profiling of the Glances software
@echo "Please complete and run: sudo ./venv/bin/py-spy record -o ./docs/_static/glances-flame.svg -d 60 -s --pid <GLANCES PID>"

View File

@ -117,6 +117,7 @@ Optional dependencies:
- ``py-cpuinfo`` (for the Quicklook CPU info module)
- ``pygal`` (for the graph export module)
- ``pymdstat`` (for RAID support) [Linux-only]
- ``pymongo`` (for the MongoDB export module) [Only for Python >= 3.7]
- ``pysnmp`` (for SNMP support)
- ``pySMART.smartx`` (for HDD Smart support) [Linux-only]
- ``pyzmq`` (for the ZeroMQ export module)

View File

@ -586,6 +586,15 @@ db=glances
#user=root
#password=root
[mongodb]
# Configuration for the --export mongodb option
# https://www.mongodb.com
host=localhost
port=27017
db=glances
user=root
password=example
[kafka]
# Configuration for the --export kafka option
# http://kafka.apache.org/

View File

@ -8,4 +8,6 @@ autoflake
codespell
memory-profiler
matplotlib
setuptools>=65.5.1 # not directly required, pinned by Snyk to avoid a vulnerability
semgrep
setuptools>=65.5.1 # not directly required, pinned by Snyk to avoid a vulnerability
numpy>=1.22.2 # not directly required, pinned by Snyk to avoid a vulnerability

View File

@ -584,6 +584,15 @@ db=glances
#user=root
#password=root
[mongodb]
# Configuration for the --export mongodb option
# https://www.mongodb.com
host=localhost
port=27017
db=glances
user=root
password=example
[kafka]
# Configuration for the --export kafka option
# http://kafka.apache.org/

View File

@ -26,6 +26,7 @@ RUN apk add --no-cache \
curl \
lm-sensors \
wireless-tools \
smartmontools \
iputils
##############################################################################
@ -93,6 +94,7 @@ RUN apk add --no-cache \
curl \
lm-sensors \
wireless-tools \
smartmontools \
iputils
COPY --from=buildRequirements /root/.local/bin /usr/local/bin/

View File

@ -8,7 +8,7 @@
# Ex: Python 3.10 for Ubuntu 22.04
# Note: ENV is for future running containers. ARG for building your Docker image.
ARG IMAGE_VERSION=12.0.0-base-ubuntu22.04
ARG IMAGE_VERSION=12.0.1-base-ubuntu22.04
ARG PYTHON_VERSION=3.10
ARG PIP_MIRROR=https://mirrors.aliyun.com/pypi/simple/
FROM nvidia/cuda:${IMAGE_VERSION} as build
@ -25,6 +25,7 @@ RUN apt-get update \
curl \
lm-sensors \
wireless-tools \
smartmontools \
net-tools \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
@ -102,6 +103,7 @@ RUN apt-get update \
curl \
lm-sensors \
wireless-tools \
smartmontools \
net-tools \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

File diff suppressed because it is too large Load Diff

View File

@ -37,3 +37,9 @@ or another example:
[diskio]
show=sda.*
Filtering is based on regular expression. Please be sure that your regular
expression works as expected. You can use an online tool like `regex101`_ in
order to test your regular expression.
.. _regex101: https://regex101.com/

View File

@ -47,4 +47,9 @@ under the ``[docker]`` section:
You can use all the variables ({{foo}}) available in the Docker plugin.
Filtering (for hide or show) is based on regular expression. Please be sure that your regular
expression works as expected. You can use an online tool like `regex101`_ in
order to test your regular expression.
.. _regex101: https://regex101.com/
.. _docker-py: https://github.com/docker/docker-py

View File

@ -53,3 +53,9 @@ Example to only show /dev/sdb mount points:
[fs]
show=/dev/sdb.*
Filtering is based on regular expression. Please be sure that your regular
expression works as expected. You can use an online tool like `regex101`_ in
order to test your regular expression.
.. _regex101: https://regex101.com/

View File

@ -53,3 +53,9 @@ virtual docker interface (docker0, docker1, ...):
wlan0_tx_warning=900000
wlan0_tx_critical=1000000
wlan0_tx_log=True
Filtering is based on regular expression. Please be sure that your regular
expression works as expected. You can use an online tool like `regex101`_ in
order to test your regular expression.
.. _regex101: https://regex101.com/

File diff suppressed because it is too large Load Diff

View File

@ -9,42 +9,35 @@ following:
.. code-block:: ini
[couchdb]
[mongodb]
host=localhost
port=5984
user=root
password=root
port=27017
db=glances
user=root
password=example
and run Glances with:
.. code-block:: console
$ glances --export couchdb
$ glances --export mongodb
Documents are stored in native ``JSON`` format. Glances adds ``"type"``
and ``"time"`` entries:
Documents are stored in native the configured database (glances by default)
with one collection per plugin.
- ``type``: plugin name
- ``time``: timestamp (format: "2016-09-24T16:39:08.524828Z")
Example of Couch Document for the load stats:
Example of MongoDB Document for the load stats:
.. code-block:: json
{
"_id": "36cbbad81453c53ef08804cb2612d5b6",
"_rev": "1-382400899bec5615cabb99aa34df49fb",
"min15": 0.33,
"time": "2016-09-24T16:39:08.524828Z",
"min5": 0.4,
"cpucore": 4,
"load_warning": 1,
"min1": 0.5,
"history_size": 28800,
"load_critical": 5,
"type": "load",
"load_careful": 0.7
_id: ObjectId('63d78ffee5528e543ce5af3a'),
min1: 1.46337890625,
min5: 1.09619140625,
min15: 1.07275390625,
cpucore: 4,
history_size: 1200,
load_disable: 'False',
load_careful: 0.7,
load_warning: 1,
load_critical: 5
}
You can view the result using the CouchDB utils URL: http://127.0.0.1:5984/_utils/database.html?glances.

View File

@ -18,6 +18,7 @@ to providing stats to multiple services (see list below).
json
kafka
mqtt
mongodb
opentsdb
prometheus
rabbitmq

50
docs/gw/mongodb.rst Normal file
View File

@ -0,0 +1,50 @@
.. _couchdb:
MongoDB
=======
You can export statistics to a ``MongoDB`` server.
The connection should be defined in the Glances configuration file as
following:
.. code-block:: ini
[couchdb]
host=localhost
port=
user=root
password=example
db=glances
and run Glances with:
.. code-block:: console
$ glances --export couchdb
Documents are stored in native ``JSON`` format. Glances adds ``"type"``
and ``"time"`` entries:
- ``type``: plugin name
- ``time``: timestamp (format: "2016-09-24T16:39:08.524828Z")
Example of Couch Document for the load stats:
.. code-block:: json
{
"_id": "36cbbad81453c53ef08804cb2612d5b6",
"_rev": "1-382400899bec5615cabb99aa34df49fb",
"min15": 0.33,
"time": "2016-09-24T16:39:08.524828Z",
"min5": 0.4,
"cpucore": 4,
"load_warning": 1,
"min1": 0.5,
"history_size": 28800,
"load_critical": 5,
"type": "load",
"load_careful": 0.7
}
You can view the result using the CouchDB utils URL: http://127.0.0.1:5984/_utils/database.html?glances.

View File

@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "GLANCES" "1" "Jan 14, 2023" "3.3.1" "Glances"
.TH "GLANCES" "1" "Jan 30, 2023" "3.4.0_beta1" "Glances"
.SH NAME
glances \- An eye on your system
.SH SYNOPSIS

View File

@ -1,4 +1,3 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Glances - An eye on your system

View File

@ -75,7 +75,7 @@ class GlancesClientBrowser(object):
# Try with the preconfigure password (only if status is PROTECTED)
clear_password = self.password.get_password(server['name'])
if clear_password is not None:
server['password'] = self.password.sha256_hash(clear_password)
server['password'] = self.password.get_hash(clear_password)
return 'http://{}:{}@{}:{}'.format(server['username'], server['password'], server['ip'], server['port'])
else:
return 'http://{}:{}'.format(server['ip'], server['port'])
@ -151,7 +151,7 @@ class GlancesClientBrowser(object):
)
# Store the password for the selected server
if clear_password is not None:
self.set_in_selected('password', self.password.sha256_hash(clear_password))
self.set_in_selected('password', self.password.get_hash(clear_password))
# Display the Glance client on the selected server
logger.info("Connect Glances client to the {} server".format(server['key']))

View File

@ -69,6 +69,10 @@ if PY3:
return s.decode()
return s.encode('ascii', 'ignore').decode()
def to_hex(s):
"""Convert the bytes string to a hex string"""
return s.hex()
def listitems(d):
return list(d.items())
@ -167,6 +171,10 @@ else:
return s
return unicodedata.normalize('NFKD', s).encode('ascii', 'ignore')
def to_hex(s):
"""Convert the string to a hex string in Python 2"""
return s.encode('hex')
def listitems(d):
return d.items()

View File

@ -0,0 +1,81 @@
# -*- coding: utf-8 -*-
#
# This file is part of Glances.
#
# SPDX-FileCopyrightText: 2023 Nicolas Hennion <nicolas@nicolargo.com>
#
# SPDX-License-Identifier: LGPL-3.0-only
#
"""MongoDB interface class."""
import sys
from datetime import datetime
from glances.logger import logger
from glances.exports.glances_export import GlancesExport
import pymongo
from urllib.parse import quote_plus
class Export(GlancesExport):
"""This class manages the MongoDB export module."""
def __init__(self, config=None, args=None):
"""Init the MongoDB export IF."""
super(Export, self).__init__(config=config, args=args)
# Mandatory configuration keys (additional to host and port)
self.db = None
# Optional configuration keys
self.user = None
self.password = None
# Load the Cassandra configuration file section
self.export_enable = self.load_conf('mongodb', mandatories=['host', 'port', 'db'], options=['user', 'password'])
if not self.export_enable:
sys.exit(2)
# Init the CouchDB client
self.client = self.init()
def init(self):
"""Init the connection to the CouchDB server."""
if not self.export_enable:
return None
server_uri = 'mongodb://%s:%s@%s:%s' % (quote_plus(self.user),
quote_plus(self.password),
self.host,
self.port)
try:
client = pymongo.MongoClient(server_uri)
client.admin.command('ping')
except Exception as e:
logger.critical("Cannot connect to MongoDB server %s:%s (%s)" % (self.host, self.port, e))
sys.exit(2)
else:
logger.info("Connected to the MongoDB server")
return client
def database(self):
"""Return the CouchDB database object"""
return self.client[self.db]
def export(self, name, columns, points):
"""Write the points to the MongoDB server."""
logger.debug("Export {} stats to MongoDB".format(name))
# Create DB input
data = dict(zip(columns, points))
# Write data to the MongoDB database
try:
self.database()[name].insert_one(data)
except Exception as e:
logger.error("Cannot export {} stats to MongoDB ({})".format(name, e))

View File

@ -127,8 +127,10 @@ class GlancesBottle(object):
if username == self.args.username:
from glances.password import GlancesPassword
pwd = GlancesPassword(username=username, config=self.config)
return pwd.check_password(self.args.password, pwd.sha256_hash(password))
pwd = GlancesPassword(username=username,
config=self.config)
return pwd.check_password(self.args.password,
pwd.get_hash(password))
else:
return False
@ -160,6 +162,7 @@ class GlancesBottle(object):
'/api/%s/<plugin>/<item>/history/<nb:int>' % self.API_VERSION, method="GET", callback=self._api_item_history
)
self._app.route('/api/%s/<plugin>/<item>/<value>' % self.API_VERSION, method="GET", callback=self._api_value)
self._app.route('/api/%s/<plugin>/<item>/<value:path>' % self.API_VERSION, method="GET", callback=self._api_value)
bindmsg = 'Glances RESTful API Server started on {}api/{}/'.format(self.bind_url, self.API_VERSION)
logger.info(bindmsg)

View File

@ -123,6 +123,7 @@
v-if="!args.disable_sensors"
:data="data"
></glances-plugin-sensors>
<glances-plugin-now :data="data"></glances-plugin-now>
</div>
</div>
<div class="col-sm-18">
@ -138,13 +139,6 @@
</div>
</div>
</div>
<div class="container-fluid">
<div class="row">
<div class="col-sm-24">
<glances-plugin-now :data="data"></glances-plugin-now>
</div>
</div>
</div>
</main>
</template>

File diff suppressed because it is too large Load Diff

View File

@ -3,27 +3,27 @@
"dependencies": {
"bootstrap": "^3.4.1",
"favico.js": "^0.3.10",
"hotkeys-js": "^3.10.0",
"hotkeys-js": "^3.10.1",
"lodash": "^4.17.21",
"sanitize-html": "^2.7.2",
"vue": "^3.2.41"
"sanitize-html": "^2.8.1",
"vue": "^3.2.45"
},
"devDependencies": {
"copy-webpack-plugin": "^11.0.0",
"css-loader": "^6.7.1",
"css-loader": "^6.7.3",
"del": "^7.0.0",
"eslint": "^8.25.0",
"eslint-plugin-vue": "^9.6.0",
"eslint": "^8.32.0",
"eslint-plugin-vue": "^9.9.0",
"html-webpack-plugin": "^5.5.0",
"less": "^4.1.3",
"less-loader": "^11.1.0",
"sass": "^1.55.0",
"sass-loader": "^13.1.0",
"sass": "^1.57.1",
"sass-loader": "^13.2.0",
"style-loader": "^3.3.1",
"url-loader": "^4.1.1",
"vue-loader": "^17.0.0",
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0",
"vue-loader": "^17.0.1",
"webpack": "^5.75.0",
"webpack-cli": "^5.0.1",
"webpack-dev-server": "^4.11.1"
},
"scripts": {

Binary file not shown.

View File

@ -16,7 +16,7 @@ import sys
import uuid
from io import open
from glances.compat import b, input
from glances.compat import b, input, to_hex
from glances.config import user_config_dir
from glances.globals import safe_makedirs
from glances.logger import logger
@ -36,25 +36,26 @@ class GlancesPassword(object):
def local_password_path(self):
"""Return the local password path.
Related toissue: Password files in same configuration dir in effect #2143
Related to issue: Password files in same configuration dir in effect #2143
"""
if self.config is None:
return user_config_dir()
else:
return self.config.get_value('passwords', 'local_password_path', default=user_config_dir())
def sha256_hash(self, plain_password):
"""Return the SHA-256 of the given password."""
return hashlib.sha256(b(plain_password)).hexdigest()
def get_hash(self, salt, plain_password):
"""Return the hashed password, salt + SHA-256."""
return hashlib.sha256(salt.encode() + plain_password.encode()).hexdigest()
def get_hash(self, plain_password, salt=''):
"""Return the hashed password, salt + pbkdf2_hmac."""
return to_hex(hashlib.pbkdf2_hmac('sha256',
plain_password.encode(),
salt.encode(),
100000,
dklen=128))
def hash_password(self, plain_password):
"""Hash password with a salt based on UUID (universally unique identifier)."""
salt = uuid.uuid4().hex
encrypted_password = self.get_hash(salt, plain_password)
encrypted_password = self.get_hash(plain_password,
salt=salt)
return salt + '$' + encrypted_password
def check_password(self, hashed_password, plain_password):
@ -63,7 +64,8 @@ class GlancesPassword(object):
Return the comparison with the encrypted_password.
"""
salt, encrypted_password = hashed_password.split('$')
re_encrypted_password = self.get_hash(salt, plain_password)
re_encrypted_password = self.get_hash(plain_password,
salt = salt)
return encrypted_password == re_encrypted_password
def get_password(self, description='', confirm=False, clear=False):
@ -72,11 +74,11 @@ class GlancesPassword(object):
For Glances server, get the password (confirm=True, clear=False):
1) from the password file (if it exists)
2) from the CLI
Optionally: save the password to a file (hashed with salt + SHA-256)
Optionally: save the password to a file (hashed with salt + SHA-pbkdf2_hmac)
For Glances client, get the password (confirm=False, clear=True):
1) from the CLI
2) the password is hashed with SHA-256 (only SHA string transit
2) the password is hashed with SHA-pbkdf2_hmac (only SHA string transit
through the network)
"""
if os.path.exists(self.password_file) and not clear:
@ -84,21 +86,21 @@ class GlancesPassword(object):
logger.info("Read password from file {}".format(self.password_file))
password = self.load_password()
else:
# password_sha256 is the plain SHA-256 password
# password_hashed is the salt + SHA-256 password
password_sha256 = self.sha256_hash(getpass.getpass(description))
password_hashed = self.hash_password(password_sha256)
# password_hash is the plain SHA-pbkdf2_hmac password
# password_hashed is the salt + SHA-pbkdf2_hmac password
password_hash = self.get_hash(getpass.getpass(description))
password_hashed = self.hash_password(password_hash)
if confirm:
# password_confirm is the clear password (only used to compare)
password_confirm = self.sha256_hash(getpass.getpass('Password (confirm): '))
password_confirm = self.get_hash(getpass.getpass('Password (confirm): '))
if not self.check_password(password_hashed, password_confirm):
logger.critical("Sorry, passwords do not match. Exit.")
sys.exit(1)
# Return the plain SHA-256 or the salted password
# Return the plain SHA-pbkdf2_hmac or the salted password
if clear:
password = password_sha256
password = password_hash
else:
password = password_hashed

View File

@ -23,6 +23,7 @@ potsdb
prometheus_client
pygal
pymdstat
pymongo; python_version >= "3.7"
pysnmp
pySMART.smartx
python-dateutil
@ -33,5 +34,5 @@ six
sparklines
statsd
wifi
zeroconf==0.47.1; python_version < "3.7"
zeroconf==0.47.3; python_version < "3.7"
zeroconf; python_version >= "3.7"

View File

@ -61,12 +61,12 @@ def get_install_requires():
def get_install_extras_require():
extras_require = {
'action': ['chevron'],
'browser': ['zeroconf==0.47.1' if PY2 else 'zeroconf>=0.19.1'],
'browser': ['zeroconf==0.47.3' if PY2 else 'zeroconf>=0.19.1'],
'cloud': ['requests'],
'docker': ['docker>=2.0.0', 'python-dateutil', 'six'],
'export': ['bernhard', 'cassandra-driver', 'couchdb', 'elasticsearch',
'graphitesender', 'influxdb>=1.0.0', 'kafka-python', 'pika',
'paho-mqtt', 'potsdb', 'prometheus_client', 'pyzmq',
'graphitesender', 'influxdb>=1.0.0', 'kafka-python', 'pymongo',
'pika', 'paho-mqtt', 'potsdb', 'prometheus_client', 'pyzmq',
'statsd'],
'folders': ['scandir'], # python_version<"3.5"
'graph': ['pygal'],