mirror of
https://github.com/nicolargo/glances.git
synced 2024-12-25 10:12:55 +03:00
Merge branch 'develop' into issue1985
# Conflicts: # optional-requirements.txt
This commit is contained in:
commit
50102c1496
8
.github/workflows/build.yml
vendored
8
.github/workflows/build.yml
vendored
@ -8,8 +8,10 @@ env:
|
|||||||
DEFAULT_DOCKER_IMAGE: nicolargo/glances
|
DEFAULT_DOCKER_IMAGE: nicolargo/glances
|
||||||
NODE_ENV: ${{ (contains('refs/heads/master', github.ref) || startsWith(github.ref, 'refs/tags/v')) && 'prod' || 'dev' }}
|
NODE_ENV: ${{ (contains('refs/heads/master', github.ref) || startsWith(github.ref, 'refs/tags/v')) && 'prod' || 'dev' }}
|
||||||
PUSH_BRANCH: ${{ 'refs/heads/develop' == github.ref || 'refs/heads/master' == github.ref || startsWith(github.ref, 'refs/tags/v') }}
|
PUSH_BRANCH: ${{ 'refs/heads/develop' == github.ref || 'refs/heads/master' == github.ref || startsWith(github.ref, 'refs/tags/v') }}
|
||||||
# linux/arm/v6 support following issue #2120
|
# linux/arm/v7 (drop support for v6) support following issue - See issue #2120
|
||||||
DOCKER_PLATFORMS: linux/amd64,linux/arm/v7,linux/arm64,linux/386
|
DOCKER_PLATFORMS: linux/amd64,linux/arm/v7,linux/arm64,linux/386
|
||||||
|
# Ubuntu image only support linux/amd64 and linux/arm64 - See issue #2185
|
||||||
|
DOCKER_PLATFORMS_UBUNTU: linux/amd64,linux/arm64
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
@ -96,7 +98,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: ['alpine']
|
os: ['alpine', 'ubuntu']
|
||||||
tag: ${{ fromJson(needs.create_Docker_builds.outputs.tags) }}
|
tag: ${{ fromJson(needs.create_Docker_builds.outputs.tags) }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@ -144,7 +146,7 @@ jobs:
|
|||||||
CHANGING_ARG=${{ github.sha }}
|
CHANGING_ARG=${{ github.sha }}
|
||||||
context: .
|
context: .
|
||||||
file: "docker-files/${{ matrix.os }}.Dockerfile"
|
file: "docker-files/${{ matrix.os }}.Dockerfile"
|
||||||
platforms: ${{env.DOCKER_PLATFORMS}}
|
platforms: ${{ matrix.os != 'ubuntu' && env.DOCKER_PLATFORMS || env.DOCKER_PLATFORMS_UBUNTU }}
|
||||||
target: ${{ matrix.tag.target }}
|
target: ${{ matrix.tag.target }}
|
||||||
labels: ${{ steps.docker_meta.outputs.labels }}
|
labels: ${{ steps.docker_meta.outputs.labels }}
|
||||||
cache-from: type=local,src=/tmp/.buildx-cache
|
cache-from: type=local,src=/tmp/.buildx-cache
|
||||||
|
14
Makefile
14
Makefile
@ -133,7 +133,7 @@ flatpak: venv-dev-upgrade ## Generate FlatPack JSON file
|
|||||||
# Docker
|
# Docker
|
||||||
# ===================================================================
|
# ===================================================================
|
||||||
|
|
||||||
docker: docker-alpine ## Generate local docker images
|
docker: docker-alpine docker-ubuntu## Generate local docker images
|
||||||
|
|
||||||
docker-alpine: ## Generate local docker images (Alpine)
|
docker-alpine: ## Generate local docker images (Alpine)
|
||||||
docker build --target full -f ./docker-files/alpine.Dockerfile -t glances:local-alpine-full .
|
docker build --target full -f ./docker-files/alpine.Dockerfile -t glances:local-alpine-full .
|
||||||
@ -159,22 +159,22 @@ run-local-conf: ## Start Glances in console mode with the system conf file
|
|||||||
./venv/bin/python -m glances
|
./venv/bin/python -m glances
|
||||||
|
|
||||||
run-docker-alpine-minimal: ## Start Glances Alpine Docker minimal in console mode
|
run-docker-alpine-minimal: ## Start Glances Alpine Docker minimal in console mode
|
||||||
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock:ro --pid host --network host -it glances:local-alpine-minimal
|
docker run --rm -e TZ="${TZ}" -v /var/run/docker.sock:/var/run/docker.sock:ro --pid host --network host -it glances:local-alpine-minimal
|
||||||
|
|
||||||
run-docker-alpine-full: ## Start Glances Alpine Docker full in console mode
|
run-docker-alpine-full: ## Start Glances Alpine Docker full in console mode
|
||||||
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock:ro --pid host --network host -it glances:local-alpine-full
|
docker run --rm -e TZ="${TZ}" -v /var/run/docker.sock:/var/run/docker.sock:ro --pid host --network host -it glances:local-alpine-full
|
||||||
|
|
||||||
run-docker-alpine-dev: ## Start Glances Alpine Docker dev in console mode
|
run-docker-alpine-dev: ## Start Glances Alpine Docker dev in console mode
|
||||||
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock:ro --pid host --network host -it glances:local-alpine-dev
|
docker run --rm -e TZ="${TZ}" -v /var/run/docker.sock:/var/run/docker.sock:ro --pid host --network host -it glances:local-alpine-dev
|
||||||
|
|
||||||
run-docker-ubuntu-minimal: ## Start Glances Ubuntu Docker minimal in console mode
|
run-docker-ubuntu-minimal: ## Start Glances Ubuntu Docker minimal in console mode
|
||||||
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock:ro --pid host --network host -it glances:local-ubuntu-minimal
|
docker run --rm -e TZ="${TZ}" -v /var/run/docker.sock:/var/run/docker.sock:ro --pid host --network host -it glances:local-ubuntu-minimal
|
||||||
|
|
||||||
run-docker-ubuntu-full: ## Start Glances Ubuntu Docker full in console mode
|
run-docker-ubuntu-full: ## Start Glances Ubuntu Docker full in console mode
|
||||||
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock:ro --pid host --network host -it glances:local-ubuntu-full
|
docker run --rm -e TZ="${TZ}" -v /var/run/docker.sock:/var/run/docker.sock:ro --pid host --network host -it glances:local-ubuntu-full
|
||||||
|
|
||||||
run-docker-ubuntu-dev: ## Start Glances Ubuntu Docker dev in console mode
|
run-docker-ubuntu-dev: ## Start Glances Ubuntu Docker dev in console mode
|
||||||
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock:ro --pid host --network host -it glances:local-ubuntu-dev
|
docker run --rm -e TZ="${TZ}" -v /var/run/docker.sock:/var/run/docker.sock:ro --pid host --network host -it glances:local-ubuntu-dev
|
||||||
|
|
||||||
run-webserver: ## Start Glances in Web server mode
|
run-webserver: ## Start Glances in Web server mode
|
||||||
./venv/bin/python -m glances -C ./conf/glances.conf -w
|
./venv/bin/python -m glances -C ./conf/glances.conf -w
|
||||||
|
@ -220,7 +220,7 @@ Run last version of Glances container in *console mode*:
|
|||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock:ro --pid host --network host -it nicolargo/glances:latest-full
|
docker run --rm -e TZ="${TZ}" -v /var/run/docker.sock:/var/run/docker.sock:ro --pid host --network host -it nicolargo/glances:latest-full
|
||||||
|
|
||||||
Additionally, if you want to use your own glances.conf file, you can
|
Additionally, if you want to use your own glances.conf file, you can
|
||||||
create your own Dockerfile:
|
create your own Dockerfile:
|
||||||
@ -237,7 +237,7 @@ variable setting parameters for the glances startup command):
|
|||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
docker run -v `pwd`/glances.conf:/etc/glances.conf -v /var/run/docker.sock:/var/run/docker.sock:ro --pid host -e GLANCES_OPT="-C /etc/glances.conf" -it nicolargo/glances:latest-full
|
docker run -e TZ="${TZ}" -v `pwd`/glances.conf:/etc/glances.conf -v /var/run/docker.sock:/var/run/docker.sock:ro --pid host -e GLANCES_OPT="-C /etc/glances.conf" -it nicolargo/glances:latest-full
|
||||||
|
|
||||||
Where \`pwd\`/glances.conf is a local directory containing your glances.conf file.
|
Where \`pwd\`/glances.conf is a local directory containing your glances.conf file.
|
||||||
|
|
||||||
@ -245,7 +245,7 @@ Run the container in *Web server mode*:
|
|||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
docker run -d --restart="always" -p 61208-61209:61208-61209 -e GLANCES_OPT="-w" -v /var/run/docker.sock:/var/run/docker.sock:ro --pid host nicolargo/glances:latest-full
|
docker run -d --restart="always" -p 61208-61209:61208-61209 -e TZ="${TZ}" -e GLANCES_OPT="-w" -v /var/run/docker.sock:/var/run/docker.sock:ro --pid host nicolargo/glances:latest-full
|
||||||
|
|
||||||
GNU/Linux
|
GNU/Linux
|
||||||
---------
|
---------
|
||||||
|
@ -237,12 +237,12 @@ critical=90
|
|||||||
#allow=shm
|
#allow=shm
|
||||||
|
|
||||||
[irq]
|
[irq]
|
||||||
# Documentation: https://glances.readthedocs.io/en/stable/aoa/irq.html
|
# Documentation: https://glances.readthedocs.io/en/latest/aoa/irq.html
|
||||||
# This plugin is disabled by default
|
# This plugin is disabled by default
|
||||||
disable=True
|
disable=True
|
||||||
|
|
||||||
[folders]
|
[folders]
|
||||||
# Documentation: https://glances.readthedocs.io/en/stable/aoa/folders.html
|
# Documentation: https://glances.readthedocs.io/en/latest/aoa/folders.html
|
||||||
disable=False
|
disable=False
|
||||||
# Define a folder list to monitor
|
# Define a folder list to monitor
|
||||||
# The list is composed of items (list_#nb <= 10)
|
# The list is composed of items (list_#nb <= 10)
|
||||||
@ -263,13 +263,18 @@ disable=False
|
|||||||
#folder_3_path=/nonexisting
|
#folder_3_path=/nonexisting
|
||||||
#folder_4_path=/root
|
#folder_4_path=/root
|
||||||
|
|
||||||
|
[cloud]
|
||||||
|
# Documentation: https://glances.readthedocs.io/en/latest/aoa/cloud.html
|
||||||
|
# This plugin is disabled by default
|
||||||
|
disable=True
|
||||||
|
|
||||||
[raid]
|
[raid]
|
||||||
# Documentation: https://glances.readthedocs.io/en/stable/aoa/raid.html
|
# Documentation: https://glances.readthedocs.io/en/latest/aoa/raid.html
|
||||||
# This plugin is disabled by default
|
# This plugin is disabled by default
|
||||||
disable=True
|
disable=True
|
||||||
|
|
||||||
[smart]
|
[smart]
|
||||||
# Documentation: https://glances.readthedocs.io/en/stable/aoa/smart.html
|
# Documentation: https://glances.readthedocs.io/en/latest/aoa/smart.html
|
||||||
# This plugin is disabled by default
|
# This plugin is disabled by default
|
||||||
disable=True
|
disable=True
|
||||||
|
|
||||||
|
@ -21,9 +21,11 @@ services:
|
|||||||
privileged: true
|
privileged: true
|
||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||||
|
- "./glances.conf:/glances/conf/glances.conf"
|
||||||
environment:
|
environment:
|
||||||
- "GLANCES_OPT=-w"
|
- GLANCES_OPT: "-C /glances/conf/glances.conf -w"
|
||||||
|
- TZ: "${TZ}"
|
||||||
labels:
|
labels:
|
||||||
- "traefik.port=61208"
|
- "traefik.port=61208"
|
||||||
- "traefik.frontend.rule=Host:glances.docker.localhost"
|
- "traefik.frontend.rule=Host:glances.docker.localhost"
|
||||||
|
@ -5,13 +5,12 @@ services:
|
|||||||
context: ./
|
context: ./
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
|
||||||
- "61208:61208"
|
|
||||||
environment:
|
|
||||||
GLANCES_OPT: "-C /glances/conf/glances.conf -w"
|
|
||||||
volumes:
|
|
||||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
|
||||||
- "./glances.conf:/glances/conf/glances.conf"
|
|
||||||
pid: "host"
|
pid: "host"
|
||||||
privileged: true
|
privileged: true
|
||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
|
volumes:
|
||||||
|
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||||
|
- "./glances.conf:/glances/conf/glances.conf"
|
||||||
|
environment:
|
||||||
|
- GLANCES_OPT: "-C /glances/conf/glances.conf -w"
|
||||||
|
- TZ: "${TZ}"
|
||||||
|
@ -237,12 +237,12 @@ critical=90
|
|||||||
#allow=shm
|
#allow=shm
|
||||||
|
|
||||||
[irq]
|
[irq]
|
||||||
# Documentation: https://glances.readthedocs.io/en/stable/aoa/irq.html
|
# Documentation: https://glances.readthedocs.io/en/latest/aoa/irq.html
|
||||||
# This plugin is disabled by default
|
# This plugin is disabled by default
|
||||||
disable=True
|
disable=True
|
||||||
|
|
||||||
[folders]
|
[folders]
|
||||||
# Documentation: https://glances.readthedocs.io/en/stable/aoa/folders.html
|
# Documentation: https://glances.readthedocs.io/en/latest/aoa/folders.html
|
||||||
disable=False
|
disable=False
|
||||||
# Define a folder list to monitor
|
# Define a folder list to monitor
|
||||||
# The list is composed of items (list_#nb <= 10)
|
# The list is composed of items (list_#nb <= 10)
|
||||||
@ -263,13 +263,18 @@ disable=False
|
|||||||
#folder_3_path=/nonexisting
|
#folder_3_path=/nonexisting
|
||||||
#folder_4_path=/root
|
#folder_4_path=/root
|
||||||
|
|
||||||
|
[cloud]
|
||||||
|
# Documentation: https://glances.readthedocs.io/en/latest/aoa/cloud.html
|
||||||
|
# This plugin is disabled by default
|
||||||
|
disable=True
|
||||||
|
|
||||||
[raid]
|
[raid]
|
||||||
# Documentation: https://glances.readthedocs.io/en/stable/aoa/raid.html
|
# Documentation: https://glances.readthedocs.io/en/latest/aoa/raid.html
|
||||||
# This plugin is disabled by default
|
# This plugin is disabled by default
|
||||||
disable=True
|
disable=True
|
||||||
|
|
||||||
[smart]
|
[smart]
|
||||||
# Documentation: https://glances.readthedocs.io/en/stable/aoa/smart.html
|
# Documentation: https://glances.readthedocs.io/en/latest/aoa/smart.html
|
||||||
# This plugin is disabled by default
|
# This plugin is disabled by default
|
||||||
disable=True
|
disable=True
|
||||||
|
|
||||||
|
@ -27,7 +27,8 @@ RUN apk add --no-cache \
|
|||||||
lm-sensors \
|
lm-sensors \
|
||||||
wireless-tools \
|
wireless-tools \
|
||||||
smartmontools \
|
smartmontools \
|
||||||
iputils
|
iputils \
|
||||||
|
tzdata
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# Install the dependencies beforehand to make them cacheable
|
# Install the dependencies beforehand to make them cacheable
|
||||||
@ -95,7 +96,8 @@ RUN apk add --no-cache \
|
|||||||
lm-sensors \
|
lm-sensors \
|
||||||
wireless-tools \
|
wireless-tools \
|
||||||
smartmontools \
|
smartmontools \
|
||||||
iputils
|
iputils \
|
||||||
|
tzdata
|
||||||
|
|
||||||
COPY --from=buildRequirements /root/.local/bin /usr/local/bin/
|
COPY --from=buildRequirements /root/.local/bin /usr/local/bin/
|
||||||
COPY --from=buildRequirements /root/.local/lib/python${PYTHON_VERSION}/site-packages /usr/lib/python${PYTHON_VERSION}/site-packages/
|
COPY --from=buildRequirements /root/.local/lib/python${PYTHON_VERSION}/site-packages /usr/lib/python${PYTHON_VERSION}/site-packages/
|
||||||
|
@ -8,11 +8,13 @@
|
|||||||
# Ex: Python 3.10 for Ubuntu 22.04
|
# Ex: Python 3.10 for Ubuntu 22.04
|
||||||
# Note: ENV is for future running containers. ARG for building your Docker image.
|
# Note: ENV is for future running containers. ARG for building your Docker image.
|
||||||
|
|
||||||
ARG IMAGE_VERSION=12.0.1-base-ubuntu22.04
|
ARG IMAGE_VERSION=12.1.0-base-ubuntu22.04
|
||||||
ARG PYTHON_VERSION=3.10
|
ARG PYTHON_VERSION=3.10
|
||||||
ARG PIP_MIRROR=https://mirrors.aliyun.com/pypi/simple/
|
ARG PIP_MIRROR=https://mirrors.aliyun.com/pypi/simple/
|
||||||
FROM nvidia/cuda:${IMAGE_VERSION} as build
|
FROM nvidia/cuda:${IMAGE_VERSION} as build
|
||||||
|
|
||||||
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y --no-install-recommends \
|
&& apt-get install -y --no-install-recommends \
|
||||||
python3 \
|
python3 \
|
||||||
@ -27,6 +29,7 @@ RUN apt-get update \
|
|||||||
wireless-tools \
|
wireless-tools \
|
||||||
smartmontools \
|
smartmontools \
|
||||||
net-tools \
|
net-tools \
|
||||||
|
tzdata \
|
||||||
&& apt-get clean \
|
&& apt-get clean \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
@ -92,7 +95,6 @@ FROM nvidia/cuda:${IMAGE_VERSION} as minimal
|
|||||||
ARG PYTHON_VERSION
|
ARG PYTHON_VERSION
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
ENV TZ=Asia/Shanghai
|
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y --no-install-recommends \
|
&& apt-get install -y --no-install-recommends \
|
||||||
@ -105,6 +107,7 @@ RUN apt-get update \
|
|||||||
wireless-tools \
|
wireless-tools \
|
||||||
smartmontools \
|
smartmontools \
|
||||||
net-tools \
|
net-tools \
|
||||||
|
tzdata \
|
||||||
&& apt-get clean \
|
&& apt-get clean \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
BIN
docs/_static/cloud.png
vendored
Normal file
BIN
docs/_static/cloud.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
BIN
docs/_static/glances-architecture.png
vendored
Normal file
BIN
docs/_static/glances-architecture.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 121 KiB |
BIN
docs/_static/processlist-extended.png
vendored
Normal file
BIN
docs/_static/processlist-extended.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 62 KiB |
15
docs/aoa/cloud.rst
Normal file
15
docs/aoa/cloud.rst
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
.. _cloud:
|
||||||
|
|
||||||
|
CLOUD
|
||||||
|
=====
|
||||||
|
|
||||||
|
This plugin diplays information about the cloud provider if your host is running on OpenStack.
|
||||||
|
|
||||||
|
The plugin use the standard OpenStack `metadata`_ service to retrieve the information.
|
||||||
|
|
||||||
|
This plugin is disable by default, please use the --enable-plugin cloud option
|
||||||
|
to enable it.
|
||||||
|
|
||||||
|
.. image:: ../_static/cloud.png
|
||||||
|
|
||||||
|
.. _metadata: https://docs.openstack.org/nova/latest/user/metadata.html
|
@ -34,6 +34,7 @@ Legend:
|
|||||||
fs
|
fs
|
||||||
irq
|
irq
|
||||||
folders
|
folders
|
||||||
|
cloud
|
||||||
raid
|
raid
|
||||||
smart
|
smart
|
||||||
sensors
|
sensors
|
||||||
|
@ -15,10 +15,15 @@ Filtered view:
|
|||||||
|
|
||||||
.. image:: ../_static/processlist-filter.png
|
.. image:: ../_static/processlist-filter.png
|
||||||
|
|
||||||
|
Extended view:
|
||||||
|
|
||||||
|
.. image:: ../_static/processlist-extended.png
|
||||||
|
|
||||||
The process view consists of 3 parts:
|
The process view consists of 3 parts:
|
||||||
|
|
||||||
- Processes summary
|
- Processes summary
|
||||||
- Monitored processes list (optional)
|
- Monitored processes list (optional, only in standalone mode)
|
||||||
|
- Extended stats for the selected process (optional)
|
||||||
- Processes list
|
- Processes list
|
||||||
|
|
||||||
The processes summary line displays:
|
The processes summary line displays:
|
||||||
@ -56,12 +61,15 @@ You can also set the sort key in the UI:
|
|||||||
* - c
|
* - c
|
||||||
- --sort-processes cpu_percent
|
- --sort-processes cpu_percent
|
||||||
- Sort by CPU
|
- Sort by CPU
|
||||||
|
* - e
|
||||||
|
- N/A
|
||||||
|
- Pin the process and display extended stats
|
||||||
* - i
|
* - i
|
||||||
- --sort-processes io_counters
|
- --sort-processes io_counters
|
||||||
- Sort by DISK I/O
|
- Sort by DISK I/O
|
||||||
* - j
|
* - j
|
||||||
- --programs
|
- --programs
|
||||||
- Accumulate processes by program
|
- Accumulate processes by program (extended stats disable in this mode)
|
||||||
* - m
|
* - m
|
||||||
- --sort-processes memory_percent
|
- --sort-processes memory_percent
|
||||||
- Sort by MEM
|
- Sort by MEM
|
||||||
|
@ -31,6 +31,12 @@ There is no alert on this information.
|
|||||||
unitname_fan_speed_alias=Alias for fan speed
|
unitname_fan_speed_alias=Alias for fan speed
|
||||||
|
|
||||||
.. note 4::
|
.. note 4::
|
||||||
|
If a sensors has multiple identical features names (see #2280), then
|
||||||
|
Glances will add a suffix to the feature name.
|
||||||
|
For example, if you have one sensor with two Composite features, the
|
||||||
|
second one will be named Composite_1.
|
||||||
|
|
||||||
|
.. note 5::
|
||||||
The plugin could crash on some operating system (FreeBSD) with the
|
The plugin could crash on some operating system (FreeBSD) with the
|
||||||
TCP or UDP blackhole option > 0 (see issue #2106). In this case, you
|
TCP or UDP blackhole option > 0 (see issue #2106). In this case, you
|
||||||
should disable the sensors (--disable-plugin sensors or from the
|
should disable the sensors (--disable-plugin sensors or from the
|
||||||
|
982
docs/api.rst
982
docs/api.rst
File diff suppressed because it is too large
Load Diff
@ -9,7 +9,20 @@ Get the Glances container:
|
|||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
docker pull nicolargo/glances
|
docker pull nicolargo/glances:<version>
|
||||||
|
|
||||||
|
Available tags (all images are based on the Alpine Operating System):
|
||||||
|
|
||||||
|
- `latest` for a minimal Glances image (latest release) version with Console, WebUI and Docker dependencies (Recommended)
|
||||||
|
- `latest-full` for a full Glances image (latest release) with all dependencies
|
||||||
|
- `dev` for a full Glances image (development branch) with all dependencies (may be instable)
|
||||||
|
You can also specify a version (example: 3.3.0.4). All available versions can be found on `DockerHub`_.
|
||||||
|
|
||||||
|
An Example to pull the `latest` tag:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
docker pull nicolargo/glances:latest
|
||||||
|
|
||||||
Run the container in *console mode*:
|
Run the container in *console mode*:
|
||||||
|
|
||||||
@ -152,3 +165,6 @@ You can add a ``[passwords]`` block to the Glances configuration file as mention
|
|||||||
# Additionally (and optionally) a default password could be defined
|
# Additionally (and optionally) a default password could be defined
|
||||||
localhost=mylocalhostpassword
|
localhost=mylocalhostpassword
|
||||||
default=mydefaultpassword
|
default=mydefaultpassword
|
||||||
|
|
||||||
|
|
||||||
|
.. _DockerHub: https://hub.docker.com/r/nicolargo/glances/tags
|
||||||
|
@ -31,9 +31,13 @@ Glances InfluxDB data model:
|
|||||||
| | time_since_update... | |
|
| | time_since_update... | |
|
||||||
| | | |
|
| | | |
|
||||||
+---------------+-----------------------+-----------------------+
|
+---------------+-----------------------+-----------------------+
|
||||||
| docker | cpu_percent | hostname |
|
| docker | cpu_percent | hostname |
|
||||||
| | memory_usage... | name |
|
| | memory_usage... | name |
|
||||||
+---------------+-----------------------+-----------------------+
|
+---------------+-----------------------+-----------------------+
|
||||||
|
| gpu | proc | hostname |
|
||||||
|
| | mem | gpu_id |
|
||||||
|
| | temperature... | |
|
||||||
|
+---------------+-----------------------+-----------------------+
|
||||||
|
|
||||||
InfluxDB (up to version 1.7.x)
|
InfluxDB (up to version 1.7.x)
|
||||||
------------------------------
|
------------------------------
|
||||||
|
@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
|||||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||||
..
|
..
|
||||||
.TH "GLANCES" "1" "Jan 30, 2023" "3.4.0_beta1" "Glances"
|
.TH "GLANCES" "1" "Mar 11, 2023" "3.4.0_beta1" "Glances"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
glances \- An eye on your system
|
glances \- An eye on your system
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
|
@ -61,7 +61,7 @@ if PY3:
|
|||||||
|
|
||||||
|
|
||||||
def __signal_handler(signal, frame):
|
def __signal_handler(signal, frame):
|
||||||
"""Callback for CTRL-C."""
|
logger.debug("Signal {} catched".format(signal))
|
||||||
end()
|
end()
|
||||||
|
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ def end():
|
|||||||
# ...after starting the server mode (issue #1175)
|
# ...after starting the server mode (issue #1175)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
logger.info("Glances stopped (key pressed: CTRL-C)")
|
logger.info("Glances stopped gracefully")
|
||||||
|
|
||||||
# The end...
|
# The end...
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
@ -156,8 +156,9 @@ def main():
|
|||||||
Select the mode (standalone, client or server)
|
Select the mode (standalone, client or server)
|
||||||
Run it...
|
Run it...
|
||||||
"""
|
"""
|
||||||
# Catch the CTRL-C signal
|
# Catch the kill signal
|
||||||
signal.signal(signal.SIGINT, __signal_handler)
|
for sig in (signal.SIGTERM, signal.SIGINT, signal.SIGHUP):
|
||||||
|
signal.signal(sig, __signal_handler)
|
||||||
|
|
||||||
# Log Glances and psutil version
|
# Log Glances and psutil version
|
||||||
logger.info('Start Glances {}'.format(__version__))
|
logger.info('Start Glances {}'.format(__version__))
|
||||||
|
@ -99,7 +99,7 @@ class GlancesClient(object):
|
|||||||
# Fallback to SNMP
|
# Fallback to SNMP
|
||||||
self.client_mode = 'snmp'
|
self.client_mode = 'snmp'
|
||||||
logger.error("Connection to Glances server failed ({} {})".format(err.errno, err.strerror))
|
logger.error("Connection to Glances server failed ({} {})".format(err.errno, err.strerror))
|
||||||
fall_back_msg = 'No Glances server found on {}. Trying fallback to SNMP...'.format(self.uri)
|
fall_back_msg = 'No Glances server found. Trying fallback to SNMP...'
|
||||||
if not self.return_to_browser:
|
if not self.return_to_browser:
|
||||||
print(fall_back_msg)
|
print(fall_back_msg)
|
||||||
else:
|
else:
|
||||||
|
@ -93,7 +93,7 @@ class GlancesClientBrowser(object):
|
|||||||
try:
|
try:
|
||||||
s = ServerProxy(uri, transport=t)
|
s = ServerProxy(uri, transport=t)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning("Client browser couldn't create socket {}: {}".format(uri, e))
|
logger.warning("Client browser couldn't create socket ({})".format(e))
|
||||||
else:
|
else:
|
||||||
# Mandatory stats
|
# Mandatory stats
|
||||||
try:
|
try:
|
||||||
@ -105,7 +105,7 @@ class GlancesClientBrowser(object):
|
|||||||
# OS (Human Readable name)
|
# OS (Human Readable name)
|
||||||
server['hr_name'] = ujson.loads(s.getSystem())['hr_name']
|
server['hr_name'] = ujson.loads(s.getSystem())['hr_name']
|
||||||
except (socket.error, Fault, KeyError) as e:
|
except (socket.error, Fault, KeyError) as e:
|
||||||
logger.debug("Error while grabbing stats form {}: {}".format(uri, e))
|
logger.debug("Error while grabbing stats form server ({})".format(e))
|
||||||
server['status'] = 'OFFLINE'
|
server['status'] = 'OFFLINE'
|
||||||
except ProtocolError as e:
|
except ProtocolError as e:
|
||||||
if e.errcode == 401:
|
if e.errcode == 401:
|
||||||
@ -115,7 +115,7 @@ class GlancesClientBrowser(object):
|
|||||||
server['status'] = 'PROTECTED'
|
server['status'] = 'PROTECTED'
|
||||||
else:
|
else:
|
||||||
server['status'] = 'OFFLINE'
|
server['status'] = 'OFFLINE'
|
||||||
logger.debug("Cannot grab stats from {} ({} {})".format(uri, e.errcode, e.errmsg))
|
logger.debug("Cannot grab stats from server ({} {})".format(e.errcode, e.errmsg))
|
||||||
else:
|
else:
|
||||||
# Status
|
# Status
|
||||||
server['status'] = 'ONLINE'
|
server['status'] = 'ONLINE'
|
||||||
@ -126,7 +126,7 @@ class GlancesClientBrowser(object):
|
|||||||
load_min5 = ujson.loads(s.getLoad())['min5']
|
load_min5 = ujson.loads(s.getLoad())['min5']
|
||||||
server['load_min5'] = '{:.2f}'.format(load_min5)
|
server['load_min5'] = '{:.2f}'.format(load_min5)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning("Error while grabbing stats form {}: {}".format(uri, e))
|
logger.warning("Error while grabbing stats form server ({})".format(e))
|
||||||
|
|
||||||
return server
|
return server
|
||||||
|
|
||||||
|
@ -236,6 +236,13 @@ Examples of use:
|
|||||||
dest='enable_separator',
|
dest='enable_separator',
|
||||||
help='enable separator in the UI',
|
help='enable separator in the UI',
|
||||||
),
|
),
|
||||||
|
parser.add_argument(
|
||||||
|
'--disable-cursor',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
dest='disable_cursor',
|
||||||
|
help='disable cursor (process selection) in the UI',
|
||||||
|
),
|
||||||
# Sort processes list
|
# Sort processes list
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--sort-processes',
|
'--sort-processes',
|
||||||
@ -542,7 +549,6 @@ Examples of use:
|
|||||||
logger.setLevel(DEBUG)
|
logger.setLevel(DEBUG)
|
||||||
else:
|
else:
|
||||||
from warnings import simplefilter
|
from warnings import simplefilter
|
||||||
|
|
||||||
simplefilter("ignore")
|
simplefilter("ignore")
|
||||||
|
|
||||||
# Plugins refresh rate
|
# Plugins refresh rate
|
||||||
@ -696,9 +702,12 @@ Examples of use:
|
|||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
# Filter is only available in standalone mode
|
# Filter is only available in standalone mode
|
||||||
if args.process_filter is not None and not self.is_standalone():
|
if not args.process_filter and not self.is_standalone():
|
||||||
logger.critical("Process filter is only available in standalone mode")
|
logger.debug("Process filter is only available in standalone mode")
|
||||||
sys.exit(2)
|
|
||||||
|
# Cursor option is only available in standalone mode
|
||||||
|
if not args.disable_cursor and not self.is_standalone():
|
||||||
|
logger.debug("Cursor is only available in standalone mode")
|
||||||
|
|
||||||
# Disable HDDTemp if sensors are disabled
|
# Disable HDDTemp if sensors are disabled
|
||||||
if getattr(self.args, 'disable_sensors', False):
|
if getattr(self.args, 'disable_sensors', False):
|
||||||
@ -727,8 +736,17 @@ Examples of use:
|
|||||||
self.args.is_server = self.is_server()
|
self.args.is_server = self.is_server()
|
||||||
self.args.is_webserver = self.is_webserver()
|
self.args.is_webserver = self.is_webserver()
|
||||||
|
|
||||||
|
# Check mode compatibility
|
||||||
|
self.check_mode_compatibility()
|
||||||
|
|
||||||
return args
|
return args
|
||||||
|
|
||||||
|
def check_mode_compatibility(self):
|
||||||
|
"""Check mode compatibility"""
|
||||||
|
if self.args.is_server and self.args.is_webserver:
|
||||||
|
logger.critical("Server and Web server mode are incompatible")
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
def is_standalone(self):
|
def is_standalone(self):
|
||||||
"""Return True if Glances is running in standalone mode."""
|
"""Return True if Glances is running in standalone mode."""
|
||||||
return not self.args.client and not self.args.browser and not self.args.server and not self.args.webserver
|
return not self.args.client and not self.args.browser and not self.args.server and not self.args.webserver
|
||||||
|
@ -230,7 +230,7 @@ class GlancesBottle(object):
|
|||||||
"""
|
"""
|
||||||
response.status = 200
|
response.status = 200
|
||||||
|
|
||||||
return None
|
return "Active"
|
||||||
|
|
||||||
@compress
|
@compress
|
||||||
def _api_help(self):
|
def _api_help(self):
|
||||||
|
@ -268,6 +268,7 @@ class _GlancesCurses(object):
|
|||||||
self.ifCAREFUL_color2 = curses.color_pair(8) | A_BOLD
|
self.ifCAREFUL_color2 = curses.color_pair(8) | A_BOLD
|
||||||
self.ifWARNING_color2 = curses.color_pair(5) | A_BOLD
|
self.ifWARNING_color2 = curses.color_pair(5) | A_BOLD
|
||||||
self.ifCRITICAL_color2 = curses.color_pair(6) | A_BOLD
|
self.ifCRITICAL_color2 = curses.color_pair(6) | A_BOLD
|
||||||
|
self.ifINFO_color = curses.color_pair(8)
|
||||||
self.filter_color = A_BOLD
|
self.filter_color = A_BOLD
|
||||||
self.selected_color = A_BOLD
|
self.selected_color = A_BOLD
|
||||||
|
|
||||||
@ -301,6 +302,7 @@ class _GlancesCurses(object):
|
|||||||
self.ifCAREFUL_color2 = curses.A_UNDERLINE
|
self.ifCAREFUL_color2 = curses.A_UNDERLINE
|
||||||
self.ifWARNING_color2 = A_BOLD
|
self.ifWARNING_color2 = A_BOLD
|
||||||
self.ifCRITICAL_color2 = curses.A_REVERSE
|
self.ifCRITICAL_color2 = curses.A_REVERSE
|
||||||
|
self.ifINFO_color = A_BOLD
|
||||||
self.filter_color = A_BOLD
|
self.filter_color = A_BOLD
|
||||||
self.selected_color = A_BOLD
|
self.selected_color = A_BOLD
|
||||||
|
|
||||||
@ -328,6 +330,7 @@ class _GlancesCurses(object):
|
|||||||
'CRITICAL_LOG': self.ifCRITICAL_color,
|
'CRITICAL_LOG': self.ifCRITICAL_color,
|
||||||
'PASSWORD': curses.A_PROTECT,
|
'PASSWORD': curses.A_PROTECT,
|
||||||
'SELECTED': self.selected_color,
|
'SELECTED': self.selected_color,
|
||||||
|
'INFO': self.ifINFO_color
|
||||||
}
|
}
|
||||||
|
|
||||||
def set_cursor(self, value):
|
def set_cursor(self, value):
|
||||||
@ -407,13 +410,15 @@ class _GlancesCurses(object):
|
|||||||
elif self.pressedkey == ord('9'):
|
elif self.pressedkey == ord('9'):
|
||||||
# '9' > Theme from black to white and reverse
|
# '9' > Theme from black to white and reverse
|
||||||
self._init_colors()
|
self._init_colors()
|
||||||
elif self.pressedkey == ord('e'):
|
elif self.pressedkey == ord('e') and not self.args.programs:
|
||||||
# 'e' > Enable/Disable process extended
|
# 'e' > Enable/Disable process extended
|
||||||
self.args.enable_process_extended = not self.args.enable_process_extended
|
self.args.enable_process_extended = not self.args.enable_process_extended
|
||||||
if not self.args.enable_process_extended:
|
if not self.args.enable_process_extended:
|
||||||
glances_processes.disable_extended()
|
glances_processes.disable_extended()
|
||||||
else:
|
else:
|
||||||
glances_processes.enable_extended()
|
glances_processes.enable_extended()
|
||||||
|
# When a process is selected (and only in standalone mode), disable the cursor
|
||||||
|
self.args.disable_cursor = self.args.enable_process_extended and self.args.is_standalone
|
||||||
elif self.pressedkey == ord('E'):
|
elif self.pressedkey == ord('E'):
|
||||||
# 'E' > Erase the process filter
|
# 'E' > Erase the process filter
|
||||||
glances_processes.process_filter = None
|
glances_processes.process_filter = None
|
||||||
@ -427,7 +432,7 @@ class _GlancesCurses(object):
|
|||||||
elif self.pressedkey == ord('-'):
|
elif self.pressedkey == ord('-'):
|
||||||
# '+' > Decrease process nice level
|
# '+' > Decrease process nice level
|
||||||
self.decrease_nice_process = not self.decrease_nice_process
|
self.decrease_nice_process = not self.decrease_nice_process
|
||||||
elif self.pressedkey == ord('k'):
|
elif self.pressedkey == ord('k') and not self.args.disable_cursor:
|
||||||
# 'k' > Kill selected process (after confirmation)
|
# 'k' > Kill selected process (after confirmation)
|
||||||
self.kill_process = not self.kill_process
|
self.kill_process = not self.kill_process
|
||||||
elif self.pressedkey == ord('w'):
|
elif self.pressedkey == ord('w'):
|
||||||
@ -451,11 +456,11 @@ class _GlancesCurses(object):
|
|||||||
# ">" (right arrow) navigation through process sort
|
# ">" (right arrow) navigation through process sort
|
||||||
next_sort = (self.loop_position() + 1) % len(self._sort_loop)
|
next_sort = (self.loop_position() + 1) % len(self._sort_loop)
|
||||||
glances_processes.set_sort_key(self._sort_loop[next_sort], False)
|
glances_processes.set_sort_key(self._sort_loop[next_sort], False)
|
||||||
elif self.pressedkey == curses.KEY_UP or self.pressedkey == 65:
|
elif self.pressedkey == curses.KEY_UP or self.pressedkey == 65 and not self.args.disable_cursor:
|
||||||
# 'UP' > Up in the server list
|
# 'UP' > Up in the server list
|
||||||
if self.args.cursor_position > 0:
|
if self.args.cursor_position > 0:
|
||||||
self.args.cursor_position -= 1
|
self.args.cursor_position -= 1
|
||||||
elif self.pressedkey == curses.KEY_DOWN or self.pressedkey == 66:
|
elif self.pressedkey == curses.KEY_DOWN or self.pressedkey == 66 and not self.args.disable_cursor:
|
||||||
# 'DOWN' > Down in the server list
|
# 'DOWN' > Down in the server list
|
||||||
# if self.args.cursor_position < glances_processes.max_processes - 2:
|
# if self.args.cursor_position < glances_processes.max_processes - 2:
|
||||||
if self.args.cursor_position < glances_processes.processes_count:
|
if self.args.cursor_position < glances_processes.processes_count:
|
||||||
|
@ -77,7 +77,7 @@ class Sparkline(object):
|
|||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
"""Return the sparkline."""
|
"""Return the sparkline."""
|
||||||
ret = sparklines(self.percents)[0]
|
ret = sparklines(self.percents, minimum=0, maximum=100)[0]
|
||||||
if self.__with_text:
|
if self.__with_text:
|
||||||
percents_without_none = [x for x in self.percents if x is not None]
|
percents_without_none = [x for x in self.percents if x is not None]
|
||||||
if len(percents_without_none) > 0:
|
if len(percents_without_none) > 0:
|
||||||
|
@ -16,12 +16,12 @@ export default {
|
|||||||
return this.data.stats['cloud'];
|
return this.data.stats['cloud'];
|
||||||
},
|
},
|
||||||
provider() {
|
provider() {
|
||||||
return this.stats['ami-id'] !== undefined ? 'AWS EC2' : null;
|
return this.stats['id'] !== undefined ? `${stats['platform']}` : null;
|
||||||
},
|
},
|
||||||
instance() {
|
instance() {
|
||||||
const { stats } = this;
|
const { stats } = this;
|
||||||
return this.stats['ami-id'] !== undefined
|
return this.stats['id'] !== undefined
|
||||||
? `${stats['instance-type']} instance ${stats['instance-id']} (${stats['reggion']})`
|
? `${stats['type']} instance ${stats['name']} (${stats['region']})`
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2219
glances/outputs/static/package-lock.json
generated
2219
glances/outputs/static/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
6
glances/outputs/static/public/glances.js
vendored
6
glances/outputs/static/public/glances.js
vendored
File diff suppressed because one or more lines are too long
@ -10,7 +10,8 @@
|
|||||||
"""Cloud plugin.
|
"""Cloud plugin.
|
||||||
|
|
||||||
Supported Cloud API:
|
Supported Cloud API:
|
||||||
- OpenStack meta data (class ThreadOpenStack, see below): AWS, OVH...
|
- OpenStack meta data (class ThreadOpenStack) - Vanilla OpenStack
|
||||||
|
- OpenStackEC2 meta data (class ThreadOpenStackEC2) - Amazon EC2 compatible
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import threading
|
import threading
|
||||||
@ -53,13 +54,16 @@ class Plugin(GlancesPlugin):
|
|||||||
|
|
||||||
# Init thread to grab OpenStack stats asynchronously
|
# Init thread to grab OpenStack stats asynchronously
|
||||||
self.OPENSTACK = ThreadOpenStack()
|
self.OPENSTACK = ThreadOpenStack()
|
||||||
|
self.OPENSTACKEC2 = ThreadOpenStackEC2()
|
||||||
|
|
||||||
# Run the thread
|
# Run the thread
|
||||||
self.OPENSTACK.start()
|
self.OPENSTACK.start()
|
||||||
|
self.OPENSTACKEC2.start()
|
||||||
|
|
||||||
def exit(self):
|
def exit(self):
|
||||||
"""Overwrite the exit method to close threads."""
|
"""Overwrite the exit method to close threads."""
|
||||||
self.OPENSTACK.stop()
|
self.OPENSTACK.stop()
|
||||||
|
self.OPENSTACKEC2.stop()
|
||||||
# Call the father class
|
# Call the father class
|
||||||
super(Plugin, self).exit()
|
super(Plugin, self).exit()
|
||||||
|
|
||||||
@ -80,12 +84,15 @@ class Plugin(GlancesPlugin):
|
|||||||
# Update the stats
|
# Update the stats
|
||||||
if self.input_method == 'local':
|
if self.input_method == 'local':
|
||||||
stats = self.OPENSTACK.stats
|
stats = self.OPENSTACK.stats
|
||||||
|
if not stats:
|
||||||
|
stats = self.OPENSTACKEC2.stats
|
||||||
# Example:
|
# Example:
|
||||||
# Uncomment to test on physical computer
|
# Uncomment to test on physical computer (only for test purpose)
|
||||||
# stats = {'ami-id': 'ami-id',
|
# stats = {'id': 'ami-id',
|
||||||
# 'instance-id': 'instance-id',
|
# 'name': 'My VM',
|
||||||
# 'instance-type': 'instance-type',
|
# 'type': 'Gold',
|
||||||
# 'region': 'placement/availability-zone'}
|
# 'region': 'France',
|
||||||
|
# 'platform': 'OpenStack'}
|
||||||
|
|
||||||
# Update the stats
|
# Update the stats
|
||||||
self.stats = stats
|
self.stats = stats
|
||||||
@ -101,13 +108,14 @@ class Plugin(GlancesPlugin):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
# Generate the output
|
# Generate the output
|
||||||
if 'instance-type' in self.stats and 'instance-id' in self.stats and 'region' in self.stats:
|
msg = self.stats.get('platform', 'Unknown')
|
||||||
msg = 'Cloud '
|
ret.append(self.curse_add_line(msg, "TITLE"))
|
||||||
ret.append(self.curse_add_line(msg, "TITLE"))
|
msg = ' {} instance {} ({})'.format(
|
||||||
msg = '{} instance {} ({})'.format(
|
self.stats.get('type', 'Unknown'),
|
||||||
self.stats['instance-type'], self.stats['instance-id'], self.stats['region']
|
self.stats.get('name', 'Unknown'),
|
||||||
)
|
self.stats.get('region', 'Unknown')
|
||||||
ret.append(self.curse_add_line(msg))
|
)
|
||||||
|
ret.append(self.curse_add_line(msg))
|
||||||
|
|
||||||
# Return the message with decoration
|
# Return the message with decoration
|
||||||
# logger.info(ret)
|
# logger.info(ret)
|
||||||
@ -121,13 +129,19 @@ class ThreadOpenStack(threading.Thread):
|
|||||||
stats is a dict
|
stats is a dict
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# The metadata service provides a way for instances to retrieve
|
||||||
|
# instance-specific data via a REST API. Instances access this
|
||||||
|
# service at 169.254.169.254 or at fe80::a9fe:a9fe.
|
||||||
|
# All types of metadata, be it user-, nova- or vendor-provided,
|
||||||
|
# can be accessed via this service.
|
||||||
# https://docs.openstack.org/nova/latest/user/metadata-service.html
|
# https://docs.openstack.org/nova/latest/user/metadata-service.html
|
||||||
OPENSTACK_API_URL = 'http://169.254.169.254/latest/meta-data'
|
OPENSTACK_PLATFORM = "OpenStack"
|
||||||
|
OPENSTACK_API_URL = 'http://169.254.169.254/openstack/latest/meta-data'
|
||||||
OPENSTACK_API_METADATA = {
|
OPENSTACK_API_METADATA = {
|
||||||
'ami-id': 'ami-id',
|
'id': 'project_id',
|
||||||
'instance-id': 'instance-id',
|
'name': 'name',
|
||||||
'instance-type': 'instance-type',
|
'type': 'meta/role',
|
||||||
'region': 'placement/availability-zone',
|
'region': 'availability_zone',
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -159,6 +173,9 @@ class ThreadOpenStack(threading.Thread):
|
|||||||
else:
|
else:
|
||||||
if r.ok:
|
if r.ok:
|
||||||
self._stats[k] = to_ascii(r.content)
|
self._stats[k] = to_ascii(r.content)
|
||||||
|
else:
|
||||||
|
# No break during the loop, so we can set the platform
|
||||||
|
self._stats['platform'] = self.OPENSTACK_PLATFORM
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -180,3 +197,26 @@ class ThreadOpenStack(threading.Thread):
|
|||||||
def stopped(self):
|
def stopped(self):
|
||||||
"""Return True is the thread is stopped."""
|
"""Return True is the thread is stopped."""
|
||||||
return self._stopper.is_set()
|
return self._stopper.is_set()
|
||||||
|
|
||||||
|
|
||||||
|
class ThreadOpenStackEC2(ThreadOpenStack):
|
||||||
|
"""
|
||||||
|
Specific thread to grab OpenStack EC2 (Amazon cloud) stats.
|
||||||
|
|
||||||
|
stats is a dict
|
||||||
|
"""
|
||||||
|
|
||||||
|
# The metadata service provides a way for instances to retrieve
|
||||||
|
# instance-specific data via a REST API. Instances access this
|
||||||
|
# service at 169.254.169.254 or at fe80::a9fe:a9fe.
|
||||||
|
# All types of metadata, be it user-, nova- or vendor-provided,
|
||||||
|
# can be accessed via this service.
|
||||||
|
# https://docs.openstack.org/nova/latest/user/metadata-service.html
|
||||||
|
OPENSTACK_PLATFORM = "Amazon EC2"
|
||||||
|
OPENSTACK_API_URL = 'http://169.254.169.254/latest/meta-data'
|
||||||
|
OPENSTACK_API_METADATA = {
|
||||||
|
'id': 'ami-id',
|
||||||
|
'name': 'instance-id',
|
||||||
|
'type': 'instance-type',
|
||||||
|
'region': 'placement/availability-zone',
|
||||||
|
}
|
||||||
|
@ -13,6 +13,7 @@ from __future__ import unicode_literals
|
|||||||
import operator
|
import operator
|
||||||
|
|
||||||
from glances.compat import u, nativestr, PermissionError
|
from glances.compat import u, nativestr, PermissionError
|
||||||
|
from glances.logger import logger
|
||||||
from glances.plugins.glances_plugin import GlancesPlugin
|
from glances.plugins.glances_plugin import GlancesPlugin
|
||||||
|
|
||||||
import psutil
|
import psutil
|
||||||
@ -95,14 +96,26 @@ class Plugin(GlancesPlugin):
|
|||||||
try:
|
try:
|
||||||
fs_stat = psutil.disk_partitions(all=False)
|
fs_stat = psutil.disk_partitions(all=False)
|
||||||
except (UnicodeDecodeError, PermissionError):
|
except (UnicodeDecodeError, PermissionError):
|
||||||
|
logger.debug("Plugin - fs: PsUtil fetch failed")
|
||||||
return self.stats
|
return self.stats
|
||||||
|
|
||||||
# Optional hack to allow logical mounts points (issue #448)
|
# Optional hack to allow logical mounts points (issue #448)
|
||||||
for fs_type in self.get_conf_value('allow'):
|
allowed_fs_types = self.get_conf_value('allow')
|
||||||
|
if allowed_fs_types:
|
||||||
|
# Avoid Psutil call unless mounts need to be allowed
|
||||||
try:
|
try:
|
||||||
fs_stat += [f for f in psutil.disk_partitions(all=True) if f.fstype.find(fs_type) >= 0]
|
all_mounted_fs = psutil.disk_partitions(all=True)
|
||||||
except UnicodeDecodeError:
|
except (UnicodeDecodeError, PermissionError):
|
||||||
return self.stats
|
logger.debug("Plugin - fs: PsUtil extended fetch failed")
|
||||||
|
else:
|
||||||
|
# Discard duplicates (#2299) and add entries matching allowed fs types
|
||||||
|
tracked_mnt_points = set(f.mountpoint for f in fs_stat)
|
||||||
|
for f in all_mounted_fs:
|
||||||
|
if (
|
||||||
|
any(f.fstype.find(fs_type) >= 0 for fs_type in allowed_fs_types)
|
||||||
|
and f.mountpoint not in tracked_mnt_points
|
||||||
|
):
|
||||||
|
fs_stat.append(f)
|
||||||
|
|
||||||
# Loop over fs
|
# Loop over fs
|
||||||
for fs in fs_stat:
|
for fs in fs_stat:
|
||||||
|
@ -84,7 +84,8 @@ class Plugin(GlancesPlugin):
|
|||||||
# "name": "Fake GeForce GTX",
|
# "name": "Fake GeForce GTX",
|
||||||
# "mem": 5.792331695556641,
|
# "mem": 5.792331695556641,
|
||||||
# "proc": 4,
|
# "proc": 4,
|
||||||
# "temperature": 26
|
# "temperature": 26,
|
||||||
|
# "fan_speed": 30
|
||||||
# }
|
# }
|
||||||
# ]
|
# ]
|
||||||
# Two GPU sample:
|
# Two GPU sample:
|
||||||
@ -95,7 +96,8 @@ class Plugin(GlancesPlugin):
|
|||||||
# "name": "Fake GeForce GTX1",
|
# "name": "Fake GeForce GTX1",
|
||||||
# "mem": 5.792331695556641,
|
# "mem": 5.792331695556641,
|
||||||
# "proc": 4,
|
# "proc": 4,
|
||||||
# "temperature": 26
|
# "temperature": 26,
|
||||||
|
# "fan_speed": 30
|
||||||
# },
|
# },
|
||||||
# {
|
# {
|
||||||
# "key": "gpu_id",
|
# "key": "gpu_id",
|
||||||
@ -103,7 +105,8 @@ class Plugin(GlancesPlugin):
|
|||||||
# "name": "Fake GeForce GTX2",
|
# "name": "Fake GeForce GTX2",
|
||||||
# "mem": 15,
|
# "mem": 15,
|
||||||
# "proc": 8,
|
# "proc": 8,
|
||||||
# "temperature": 65
|
# "temperature": 65,
|
||||||
|
# "fan_speed": 75
|
||||||
# }
|
# }
|
||||||
# ]
|
# ]
|
||||||
return self.stats
|
return self.stats
|
||||||
@ -274,6 +277,8 @@ class Plugin(GlancesPlugin):
|
|||||||
device_stats['proc'] = get_proc(device_handle)
|
device_stats['proc'] = get_proc(device_handle)
|
||||||
# Processor temperature in °C
|
# Processor temperature in °C
|
||||||
device_stats['temperature'] = get_temperature(device_handle)
|
device_stats['temperature'] = get_temperature(device_handle)
|
||||||
|
# Fan speed in %
|
||||||
|
device_stats['fan_speed'] = get_fan_speed(device_handle)
|
||||||
stats.append(device_stats)
|
stats.append(device_stats)
|
||||||
|
|
||||||
return stats
|
return stats
|
||||||
@ -324,8 +329,16 @@ def get_proc(device_handle):
|
|||||||
|
|
||||||
|
|
||||||
def get_temperature(device_handle):
|
def get_temperature(device_handle):
|
||||||
"""Get GPU device CPU consumption in percent."""
|
"""Get GPU device CPU temperature in Celsius."""
|
||||||
try:
|
try:
|
||||||
return pynvml.nvmlDeviceGetTemperature(device_handle, pynvml.NVML_TEMPERATURE_GPU)
|
return pynvml.nvmlDeviceGetTemperature(device_handle, pynvml.NVML_TEMPERATURE_GPU)
|
||||||
except pynvml.NVMLError:
|
except pynvml.NVMLError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_fan_speed(device_handle):
|
||||||
|
"""Get GPU device fan speed in percent."""
|
||||||
|
try:
|
||||||
|
return pynvml.nvmlDeviceGetFanSpeed(device_handle)
|
||||||
|
except pynvml.NVMLError:
|
||||||
|
return None
|
||||||
|
@ -19,7 +19,7 @@ from glances.processes import glances_processes, sort_stats
|
|||||||
from glances.outputs.glances_unicode import unicode_message
|
from glances.outputs.glances_unicode import unicode_message
|
||||||
from glances.plugins.glances_core import Plugin as CorePlugin
|
from glances.plugins.glances_core import Plugin as CorePlugin
|
||||||
from glances.plugins.glances_plugin import GlancesPlugin
|
from glances.plugins.glances_plugin import GlancesPlugin
|
||||||
from glances.programs import processes_to_programs
|
from glances.outputs.glances_bars import Bar
|
||||||
|
|
||||||
|
|
||||||
def seconds_to_hms(input_seconds):
|
def seconds_to_hms(input_seconds):
|
||||||
@ -146,9 +146,10 @@ class Plugin(GlancesPlugin):
|
|||||||
# Update stats using the standard system lib
|
# Update stats using the standard system lib
|
||||||
# Note: Update is done in the processcount plugin
|
# Note: Update is done in the processcount plugin
|
||||||
# Just return the processes list
|
# Just return the processes list
|
||||||
stats = glances_processes.getlist()
|
|
||||||
if self.args.programs:
|
if self.args.programs:
|
||||||
stats = processes_to_programs(stats)
|
stats = glances_processes.getlist(as_programs=True)
|
||||||
|
else:
|
||||||
|
stats = glances_processes.getlist()
|
||||||
|
|
||||||
elif self.input_method == 'snmp':
|
elif self.input_method == 'snmp':
|
||||||
# No SNMP grab for processes
|
# No SNMP grab for processes
|
||||||
@ -353,11 +354,11 @@ class Plugin(GlancesPlugin):
|
|||||||
- selected is a tag=True if p is the selected process
|
- selected is a tag=True if p is the selected process
|
||||||
"""
|
"""
|
||||||
ret = [self.curse_new_line()]
|
ret = [self.curse_new_line()]
|
||||||
|
|
||||||
# When a process is selected:
|
# When a process is selected:
|
||||||
# * display a special character at the beginning of the line
|
# * display a special character at the beginning of the line
|
||||||
# * underline the command name
|
# * underline the command name
|
||||||
if args.is_standalone:
|
ret.append(self.curse_add_line(unicode_message('PROCESS_SELECTOR') if (selected and not args.disable_cursor) else ' ', 'SELECTED'))
|
||||||
ret.append(self.curse_add_line(unicode_message('PROCESS_SELECTOR') if selected else ' ', 'SELECTED'))
|
|
||||||
|
|
||||||
# CPU
|
# CPU
|
||||||
ret.append(self._get_process_curses_cpu(p, selected, args))
|
ret.append(self._get_process_curses_cpu(p, selected, args))
|
||||||
@ -404,7 +405,7 @@ class Plugin(GlancesPlugin):
|
|||||||
cmdline = p.get('cmdline', '?')
|
cmdline = p.get('cmdline', '?')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
process_decoration = 'PROCESS_SELECTED' if (selected and args.is_standalone) else 'PROCESS'
|
process_decoration = 'PROCESS_SELECTED' if (selected and not args.disable_cursor) else 'PROCESS'
|
||||||
if cmdline:
|
if cmdline:
|
||||||
path, cmd, arguments = split_cmdline(bare_process_name, cmdline)
|
path, cmd, arguments = split_cmdline(bare_process_name, cmdline)
|
||||||
# Manage end of line in arguments (see #1692)
|
# Manage end of line in arguments (see #1692)
|
||||||
@ -428,74 +429,14 @@ class Plugin(GlancesPlugin):
|
|||||||
logger.debug("Can not decode command line '{}' ({})".format(cmdline, e))
|
logger.debug("Can not decode command line '{}' ({})".format(cmdline, e))
|
||||||
ret.append(self.curse_add_line('', splittable=True))
|
ret.append(self.curse_add_line('', splittable=True))
|
||||||
|
|
||||||
# Add extended stats but only for the top processes
|
|
||||||
if args.cursor_position == 0 and 'extended_stats' in p and args.enable_process_extended:
|
|
||||||
# Left padding
|
|
||||||
xpad = ' ' * 13
|
|
||||||
# First line is CPU affinity
|
|
||||||
if 'cpu_affinity' in p and p['cpu_affinity'] is not None:
|
|
||||||
ret.append(self.curse_new_line())
|
|
||||||
msg = xpad + 'CPU affinity: ' + str(len(p['cpu_affinity'])) + ' cores'
|
|
||||||
ret.append(self.curse_add_line(msg, splittable=True))
|
|
||||||
# Second line is memory info
|
|
||||||
if 'memory_info' in p and p['memory_info'] is not None:
|
|
||||||
ret.append(self.curse_new_line())
|
|
||||||
msg = '{}Memory info: {}'.format(xpad, p['memory_info'])
|
|
||||||
if 'memory_swap' in p and p['memory_swap'] is not None:
|
|
||||||
msg += ' swap ' + self.auto_unit(p['memory_swap'], low_precision=False)
|
|
||||||
ret.append(self.curse_add_line(msg, splittable=True))
|
|
||||||
# Third line is for open files/network sessions
|
|
||||||
msg = ''
|
|
||||||
if 'num_threads' in p and p['num_threads'] is not None:
|
|
||||||
msg += str(p['num_threads']) + ' threads '
|
|
||||||
if 'num_fds' in p and p['num_fds'] is not None:
|
|
||||||
msg += str(p['num_fds']) + ' files '
|
|
||||||
if 'num_handles' in p and p['num_handles'] is not None:
|
|
||||||
msg += str(p['num_handles']) + ' handles '
|
|
||||||
if 'tcp' in p and p['tcp'] is not None:
|
|
||||||
msg += str(p['tcp']) + ' TCP '
|
|
||||||
if 'udp' in p and p['udp'] is not None:
|
|
||||||
msg += str(p['udp']) + ' UDP'
|
|
||||||
if msg != '':
|
|
||||||
ret.append(self.curse_new_line())
|
|
||||||
msg = xpad + 'Open: ' + msg
|
|
||||||
ret.append(self.curse_add_line(msg, splittable=True))
|
|
||||||
# Fourth line is IO nice level (only Linux and Windows OS)
|
|
||||||
if 'ionice' in p and p['ionice'] is not None and hasattr(p['ionice'], 'ioclass'):
|
|
||||||
ret.append(self.curse_new_line())
|
|
||||||
msg = xpad + 'IO nice: '
|
|
||||||
k = 'Class is '
|
|
||||||
v = p['ionice'].ioclass
|
|
||||||
# Linux: The scheduling class. 0 for none, 1 for real time, 2 for best-effort, 3 for idle.
|
|
||||||
# Windows: On Windows only ioclass is used and it can be set to 2 (normal), 1 (low) or 0 (very low).
|
|
||||||
if WINDOWS:
|
|
||||||
if v == 0:
|
|
||||||
msg += k + 'Very Low'
|
|
||||||
elif v == 1:
|
|
||||||
msg += k + 'Low'
|
|
||||||
elif v == 2:
|
|
||||||
msg += 'No specific I/O priority'
|
|
||||||
else:
|
|
||||||
msg += k + str(v)
|
|
||||||
else:
|
|
||||||
if v == 0:
|
|
||||||
msg += 'No specific I/O priority'
|
|
||||||
elif v == 1:
|
|
||||||
msg += k + 'Real Time'
|
|
||||||
elif v == 2:
|
|
||||||
msg += k + 'Best Effort'
|
|
||||||
elif v == 3:
|
|
||||||
msg += k + 'IDLE'
|
|
||||||
else:
|
|
||||||
msg += k + str(v)
|
|
||||||
# value is a number which goes from 0 to 7.
|
|
||||||
# The higher the value, the lower the I/O priority of the process.
|
|
||||||
if hasattr(p['ionice'], 'value') and p['ionice'].value != 0:
|
|
||||||
msg += ' (value %s/7)' % str(p['ionice'].value)
|
|
||||||
ret.append(self.curse_add_line(msg, splittable=True))
|
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
def is_selected_process(self, args):
|
||||||
|
return args.is_standalone and \
|
||||||
|
self.args.enable_process_extended and \
|
||||||
|
args.cursor_position is not None and \
|
||||||
|
glances_processes.extended_process is not None
|
||||||
|
|
||||||
def msg_curse(self, args=None, max_width=None):
|
def msg_curse(self, args=None, max_width=None):
|
||||||
"""Return the dict to display in the curse interface."""
|
"""Return the dict to display in the curse interface."""
|
||||||
# Init the return message
|
# Init the return message
|
||||||
@ -507,6 +448,16 @@ class Plugin(GlancesPlugin):
|
|||||||
|
|
||||||
# Compute the sort key
|
# Compute the sort key
|
||||||
process_sort_key = glances_processes.sort_key
|
process_sort_key = glances_processes.sort_key
|
||||||
|
processes_list_sorted = self.__sort_stats(process_sort_key)
|
||||||
|
|
||||||
|
# Display extended stats for selected process
|
||||||
|
#############################################
|
||||||
|
|
||||||
|
if self.is_selected_process(args):
|
||||||
|
self.__msg_curse_extended_process(ret, glances_processes.extended_process)
|
||||||
|
|
||||||
|
# Display others processes list
|
||||||
|
###############################
|
||||||
|
|
||||||
# Header
|
# Header
|
||||||
self.__msg_curse_header(ret, process_sort_key, args)
|
self.__msg_curse_header(ret, process_sort_key, args)
|
||||||
@ -515,10 +466,10 @@ class Plugin(GlancesPlugin):
|
|||||||
# Loop over processes (sorted by the sort key previously compute)
|
# Loop over processes (sorted by the sort key previously compute)
|
||||||
# This is a Glances bottleneck (see flame graph),
|
# This is a Glances bottleneck (see flame graph),
|
||||||
# get_process_curses_data should be optimzed
|
# get_process_curses_data should be optimzed
|
||||||
i = 0
|
for position, process in enumerate(processes_list_sorted):
|
||||||
for p in self.__sort_stats(process_sort_key):
|
ret.extend(self.get_process_curses_data(process,
|
||||||
ret.extend(self.get_process_curses_data(p, i == args.cursor_position, args))
|
position == args.cursor_position,
|
||||||
i += 1
|
args))
|
||||||
|
|
||||||
# A filter is set Display the stats summaries
|
# A filter is set Display the stats summaries
|
||||||
if glances_processes.process_filter is not None:
|
if glances_processes.process_filter is not None:
|
||||||
@ -532,6 +483,116 @@ class Plugin(GlancesPlugin):
|
|||||||
# Return the message with decoration
|
# Return the message with decoration
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
def __msg_curse_extended_process(self, ret, p):
|
||||||
|
"""Get extended curses data for the selected process (see issue #2225)
|
||||||
|
|
||||||
|
The result depends of the process type (process or thread).
|
||||||
|
|
||||||
|
Input p is a dict with the following keys:
|
||||||
|
{'status': 'S',
|
||||||
|
'memory_info': pmem(rss=466890752, vms=3365347328, shared=68153344, text=659456, lib=0, data=774647808, dirty=0),
|
||||||
|
'pid': 4980,
|
||||||
|
'io_counters': [165385216, 0, 165385216, 0, 1],
|
||||||
|
'num_threads': 20,
|
||||||
|
'nice': 0,
|
||||||
|
'memory_percent': 5.958135664449709,
|
||||||
|
'cpu_percent': 0.0,
|
||||||
|
'gids': pgids(real=1000, effective=1000, saved=1000),
|
||||||
|
'cpu_times': pcputimes(user=696.38, system=119.98, children_user=0.0, children_system=0.0, iowait=0.0),
|
||||||
|
'name': 'WebExtensions',
|
||||||
|
'key': 'pid',
|
||||||
|
'time_since_update': 2.1997854709625244,
|
||||||
|
'cmdline': ['/snap/firefox/2154/usr/lib/firefox/firefox', '-contentproc', '-childID', '...'],
|
||||||
|
'username': 'nicolargo',
|
||||||
|
'cpu_min': 0.0,
|
||||||
|
'cpu_max': 7.0,
|
||||||
|
'cpu_mean': 3.2}
|
||||||
|
"""
|
||||||
|
if self.args.programs:
|
||||||
|
self.__msg_curse_extended_process_program(ret, p)
|
||||||
|
else:
|
||||||
|
self.__msg_curse_extended_process_thread(ret, p)
|
||||||
|
|
||||||
|
def __msg_curse_extended_process_program(self, ret, p):
|
||||||
|
# Title
|
||||||
|
msg = "Pinned program {} ('e' to unpin)".format(p['name'])
|
||||||
|
ret.append(self.curse_add_line(msg, "TITLE"))
|
||||||
|
|
||||||
|
ret.append(self.curse_new_line())
|
||||||
|
ret.append(self.curse_new_line())
|
||||||
|
|
||||||
|
def __msg_curse_extended_process_thread(self, ret, p):
|
||||||
|
# Title
|
||||||
|
ret.append(self.curse_add_line("Pinned thread ", "TITLE"))
|
||||||
|
ret.append(self.curse_add_line(p['name'], "UNDERLINE"))
|
||||||
|
ret.append(self.curse_add_line(" ('e' to unpin)"))
|
||||||
|
|
||||||
|
# First line is CPU affinity
|
||||||
|
ret.append(self.curse_new_line())
|
||||||
|
ret.append(self.curse_add_line(' CPU Min/Max/Mean: '))
|
||||||
|
msg = '{: >7.1f}{: >7.1f}{: >7.1f}%'.format(p['cpu_min'], p['cpu_max'], p['cpu_mean'])
|
||||||
|
ret.append(self.curse_add_line(msg, decoration='INFO'))
|
||||||
|
if 'cpu_affinity' in p and p['cpu_affinity'] is not None:
|
||||||
|
ret.append(self.curse_add_line(' Affinity: '))
|
||||||
|
ret.append(self.curse_add_line(str(len(p['cpu_affinity'])), decoration='INFO'))
|
||||||
|
ret.append(self.curse_add_line(' cores', decoration='INFO'))
|
||||||
|
if 'ionice' in p and p['ionice'] is not None and hasattr(p['ionice'], 'ioclass'):
|
||||||
|
msg = ' IO nice: '
|
||||||
|
k = 'Class is '
|
||||||
|
v = p['ionice'].ioclass
|
||||||
|
# Linux: The scheduling class. 0 for none, 1 for real time, 2 for best-effort, 3 for idle.
|
||||||
|
# Windows: On Windows only ioclass is used and it can be set to 2 (normal), 1 (low) or 0 (very low).
|
||||||
|
if WINDOWS:
|
||||||
|
if v == 0:
|
||||||
|
msg += k + 'Very Low'
|
||||||
|
elif v == 1:
|
||||||
|
msg += k + 'Low'
|
||||||
|
elif v == 2:
|
||||||
|
msg += 'No specific I/O priority'
|
||||||
|
else:
|
||||||
|
msg += k + str(v)
|
||||||
|
else:
|
||||||
|
if v == 0:
|
||||||
|
msg += 'No specific I/O priority'
|
||||||
|
elif v == 1:
|
||||||
|
msg += k + 'Real Time'
|
||||||
|
elif v == 2:
|
||||||
|
msg += k + 'Best Effort'
|
||||||
|
elif v == 3:
|
||||||
|
msg += k + 'IDLE'
|
||||||
|
else:
|
||||||
|
msg += k + str(v)
|
||||||
|
# value is a number which goes from 0 to 7.
|
||||||
|
# The higher the value, the lower the I/O priority of the process.
|
||||||
|
if hasattr(p['ionice'], 'value') and p['ionice'].value != 0:
|
||||||
|
msg += ' (value %s/7)' % str(p['ionice'].value)
|
||||||
|
ret.append(self.curse_add_line(msg, splittable=True))
|
||||||
|
|
||||||
|
# Second line is memory info
|
||||||
|
ret.append(self.curse_new_line())
|
||||||
|
ret.append(self.curse_add_line(' MEM Min/Max/Mean: '))
|
||||||
|
msg = '{: >7.1f}{: >7.1f}{: >7.1f}%'.format(p['memory_min'], p['memory_max'], p['memory_mean'])
|
||||||
|
ret.append(self.curse_add_line(msg, decoration='INFO'))
|
||||||
|
if 'memory_info' in p and p['memory_info'] is not None:
|
||||||
|
ret.append(self.curse_add_line(' Memory info: '))
|
||||||
|
for k in p['memory_info']._asdict():
|
||||||
|
ret.append(self.curse_add_line(self.auto_unit(p['memory_info']._asdict()[k], low_precision=False), decoration='INFO', splittable=True))
|
||||||
|
ret.append(self.curse_add_line(' ' + k + ' ', splittable=True))
|
||||||
|
if 'memory_swap' in p and p['memory_swap'] is not None:
|
||||||
|
ret.append(self.curse_add_line(self.auto_unit(p['memory_swap'], low_precision=False), decoration='INFO', splittable=True))
|
||||||
|
ret.append(self.curse_add_line(' swap ', splittable=True))
|
||||||
|
|
||||||
|
# Third line is for open files/network sessions
|
||||||
|
ret.append(self.curse_new_line())
|
||||||
|
ret.append(self.curse_add_line(' Open: '))
|
||||||
|
for stat_prefix in ['num_threads', 'num_fds', 'num_handles', 'tcp', 'udp']:
|
||||||
|
if stat_prefix in p and p[stat_prefix] is not None:
|
||||||
|
ret.append(self.curse_add_line(str(p[stat_prefix]), decoration='INFO'))
|
||||||
|
ret.append(self.curse_add_line(' {} '.format(stat_prefix.replace('num_', ''))))
|
||||||
|
|
||||||
|
ret.append(self.curse_new_line())
|
||||||
|
ret.append(self.curse_new_line())
|
||||||
|
|
||||||
def __msg_curse_header(self, ret, process_sort_key, args=None):
|
def __msg_curse_header(self, ret, process_sort_key, args=None):
|
||||||
"""Build the header and add it to the ret dict."""
|
"""Build the header and add it to the ret dict."""
|
||||||
sort_style = 'SORT'
|
sort_style = 'SORT'
|
||||||
@ -578,10 +639,15 @@ class Plugin(GlancesPlugin):
|
|||||||
msg, sort_style if process_sort_key == 'io_counters' else 'DEFAULT', optional=True, additional=True
|
msg, sort_style if process_sort_key == 'io_counters' else 'DEFAULT', optional=True, additional=True
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if not self.args.programs:
|
if args.is_standalone and not args.disable_cursor:
|
||||||
msg = self.layout_header['command'].format('Command', "('k' to kill)" if args.is_standalone else "")
|
if self.args.programs:
|
||||||
|
shortkey = "('k' to kill)"
|
||||||
|
else:
|
||||||
|
shortkey = "('e' to pin | 'k' to kill)"
|
||||||
else:
|
else:
|
||||||
msg = self.layout_header['command'].format('Programs', "('k' to kill)" if args.is_standalone else "")
|
shortkey = ""
|
||||||
|
msg = self.layout_header['command'].format("Programs" if self.args.programs else "Command",
|
||||||
|
shortkey)
|
||||||
ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'name' else 'DEFAULT'))
|
ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'name' else 'DEFAULT'))
|
||||||
|
|
||||||
def __msg_curse_sum(self, ret, sep_char='_', mmm=None, args=None):
|
def __msg_curse_sum(self, ret, sep_char='_', mmm=None, args=None):
|
||||||
|
@ -92,6 +92,8 @@ class Plugin(GlancesPlugin):
|
|||||||
# New line
|
# New line
|
||||||
ret.append(self.curse_new_line())
|
ret.append(self.curse_new_line())
|
||||||
# Display the current status
|
# Display the current status
|
||||||
|
if not isinstance(self.stats[array], dict):
|
||||||
|
continue
|
||||||
status = self.raid_alert(
|
status = self.raid_alert(
|
||||||
self.stats[array]['status'],
|
self.stats[array]['status'],
|
||||||
self.stats[array]['used'],
|
self.stats[array]['used'],
|
||||||
|
@ -175,7 +175,7 @@ class Plugin(GlancesPlugin):
|
|||||||
# Alert processing
|
# Alert processing
|
||||||
if i['type'] == SENSOR_TEMP_TYPE:
|
if i['type'] == SENSOR_TEMP_TYPE:
|
||||||
if self.is_limit('critical', stat_name='sensors_temperature_' + i['label']):
|
if self.is_limit('critical', stat_name='sensors_temperature_' + i['label']):
|
||||||
# By default use the thresholds confiured in the glances.conf file (see #2058)
|
# By default use the thresholds configured in the glances.conf file (see #2058)
|
||||||
alert = self.get_alert(current=i['value'], header='temperature_' + i['label'])
|
alert = self.get_alert(current=i['value'], header='temperature_' + i['label'])
|
||||||
else:
|
else:
|
||||||
# Else use the system thresholds
|
# Else use the system thresholds
|
||||||
@ -197,7 +197,7 @@ class Plugin(GlancesPlugin):
|
|||||||
self.views[i[self.get_key()]]['value']['decoration'] = alert
|
self.views[i[self.get_key()]]['value']['decoration'] = alert
|
||||||
|
|
||||||
def battery_trend(self, stats):
|
def battery_trend(self, stats):
|
||||||
"""Return the trend characterr for the battery"""
|
"""Return the trend character for the battery"""
|
||||||
if 'status' not in stats:
|
if 'status' not in stats:
|
||||||
return ''
|
return ''
|
||||||
if stats['status'].startswith('Charg'):
|
if stats['status'].startswith('Charg'):
|
||||||
@ -331,12 +331,15 @@ class GlancesGrabSensors(object):
|
|||||||
else:
|
else:
|
||||||
return ret
|
return ret
|
||||||
for chip_name, chip in iteritems(input_list):
|
for chip_name, chip in iteritems(input_list):
|
||||||
i = 1
|
label_index = 1
|
||||||
for feature in chip:
|
for chip_name_index, feature in enumerate(chip):
|
||||||
sensors_current = {}
|
sensors_current = {}
|
||||||
# Sensor name
|
# Sensor name
|
||||||
if feature.label == '':
|
if feature.label == '':
|
||||||
sensors_current['label'] = chip_name + ' ' + str(i)
|
sensors_current['label'] = chip_name + ' ' + str(chip_name_index)
|
||||||
|
elif feature.label in [i['label'] for i in ret]:
|
||||||
|
sensors_current['label'] = feature.label + ' ' + str(label_index)
|
||||||
|
label_index += 1
|
||||||
else:
|
else:
|
||||||
sensors_current['label'] = feature.label
|
sensors_current['label'] = feature.label
|
||||||
# Sensors value, limit and unit
|
# Sensors value, limit and unit
|
||||||
@ -348,7 +351,6 @@ class GlancesGrabSensors(object):
|
|||||||
sensors_current['critical'] = int(system_critical) if system_critical is not None else None
|
sensors_current['critical'] = int(system_critical) if system_critical is not None else None
|
||||||
# Add sensor to the list
|
# Add sensor to the list
|
||||||
ret.append(sensors_current)
|
ret.append(sensors_current)
|
||||||
i += 1
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def get(self, sensor_type=SENSOR_TEMP_TYPE):
|
def get(self, sensor_type=SENSOR_TEMP_TYPE):
|
||||||
|
@ -13,6 +13,7 @@ from glances.compat import iterkeys
|
|||||||
from glances.globals import BSD, LINUX, MACOS, WINDOWS
|
from glances.globals import BSD, LINUX, MACOS, WINDOWS
|
||||||
from glances.timer import Timer, getTimeSinceLastUpdate
|
from glances.timer import Timer, getTimeSinceLastUpdate
|
||||||
from glances.filter import GlancesFilter
|
from glances.filter import GlancesFilter
|
||||||
|
from glances.programs import processes_to_programs
|
||||||
from glances.logger import logger
|
from glances.logger import logger
|
||||||
|
|
||||||
import psutil
|
import psutil
|
||||||
@ -37,6 +38,10 @@ class GlancesProcesses(object):
|
|||||||
|
|
||||||
def __init__(self, cache_timeout=60):
|
def __init__(self, cache_timeout=60):
|
||||||
"""Init the class to collect stats about processes."""
|
"""Init the class to collect stats about processes."""
|
||||||
|
# Init the args, coming from the GlancesStandalone class
|
||||||
|
# Should be set by the set_args method
|
||||||
|
self.args = None
|
||||||
|
|
||||||
# Add internals caches because psutil do not cache all the stats
|
# Add internals caches because psutil do not cache all the stats
|
||||||
# See: https://github.com/giampaolo/psutil/issues/462
|
# See: https://github.com/giampaolo/psutil/issues/462
|
||||||
self.username_cache = {}
|
self.username_cache = {}
|
||||||
@ -70,6 +75,7 @@ class GlancesProcesses(object):
|
|||||||
|
|
||||||
# Extended stats for top process is enable by default
|
# Extended stats for top process is enable by default
|
||||||
self.disable_extended_tag = False
|
self.disable_extended_tag = False
|
||||||
|
self.extended_process = None
|
||||||
|
|
||||||
# Test if the system can grab io_counters
|
# Test if the system can grab io_counters
|
||||||
try:
|
try:
|
||||||
@ -109,6 +115,10 @@ class GlancesProcesses(object):
|
|||||||
self._max_values = {}
|
self._max_values = {}
|
||||||
self.reset_max_values()
|
self.reset_max_values()
|
||||||
|
|
||||||
|
def set_args(self, args):
|
||||||
|
"""Set args."""
|
||||||
|
self.args = args
|
||||||
|
|
||||||
def reset_processcount(self):
|
def reset_processcount(self):
|
||||||
"""Reset the global process count"""
|
"""Reset the global process count"""
|
||||||
self.processcount = {'total': 0, 'running': 0, 'sleeping': 0, 'thread': 0, 'pid_max': None}
|
self.processcount = {'total': 0, 'running': 0, 'sleeping': 0, 'thread': 0, 'pid_max': None}
|
||||||
@ -247,6 +257,91 @@ class GlancesProcesses(object):
|
|||||||
for k in self._max_values_list:
|
for k in self._max_values_list:
|
||||||
self._max_values[k] = 0.0
|
self._max_values[k] = 0.0
|
||||||
|
|
||||||
|
def get_extended_stats(self, proc):
|
||||||
|
"""Get the extended stats for the given PID."""
|
||||||
|
# - cpu_affinity (Linux, Windows, FreeBSD)
|
||||||
|
# - ionice (Linux and Windows > Vista)
|
||||||
|
# - num_ctx_switches (not available on Illumos/Solaris)
|
||||||
|
# - num_fds (Unix-like)
|
||||||
|
# - num_handles (Windows)
|
||||||
|
# - memory_maps (only swap, Linux)
|
||||||
|
# https://www.cyberciti.biz/faq/linux-which-process-is-using-swap/
|
||||||
|
# - connections (TCP and UDP)
|
||||||
|
# - CPU min/max/mean
|
||||||
|
|
||||||
|
# Set the extended stats list (OS dependant)
|
||||||
|
extended_stats = ['cpu_affinity', 'ionice', 'num_ctx_switches']
|
||||||
|
if LINUX:
|
||||||
|
# num_fds only available on Unix system (see issue #1351)
|
||||||
|
extended_stats += ['num_fds']
|
||||||
|
if WINDOWS:
|
||||||
|
extended_stats += ['num_handles']
|
||||||
|
|
||||||
|
ret = {}
|
||||||
|
try:
|
||||||
|
# Get the extended stats
|
||||||
|
selected_process = psutil.Process(proc['pid'])
|
||||||
|
ret = selected_process.as_dict(attrs=extended_stats, ad_value=None)
|
||||||
|
|
||||||
|
if LINUX:
|
||||||
|
try:
|
||||||
|
ret['memory_swap'] = sum([v.swap for v in selected_process.memory_maps()])
|
||||||
|
except (psutil.NoSuchProcess, KeyError):
|
||||||
|
# (KeyError catch for issue #1551)
|
||||||
|
pass
|
||||||
|
except (psutil.AccessDenied, NotImplementedError):
|
||||||
|
# NotImplementedError: /proc/${PID}/smaps file doesn't exist
|
||||||
|
# on kernel < 2.6.14 or CONFIG_MMU kernel configuration option
|
||||||
|
# is not enabled (see psutil #533/glances #413).
|
||||||
|
ret['memory_swap'] = None
|
||||||
|
try:
|
||||||
|
ret['tcp'] = len(selected_process.connections(kind="tcp"))
|
||||||
|
ret['udp'] = len(selected_process.connections(kind="udp"))
|
||||||
|
except (psutil.AccessDenied, psutil.NoSuchProcess):
|
||||||
|
# Manage issue1283 (psutil.AccessDenied)
|
||||||
|
ret['tcp'] = None
|
||||||
|
ret['udp'] = None
|
||||||
|
except (psutil.NoSuchProcess, ValueError, AttributeError) as e:
|
||||||
|
logger.error('Can not grab extended stats ({})'.format(e))
|
||||||
|
self.extended_process = None
|
||||||
|
ret['extended_stats'] = False
|
||||||
|
else:
|
||||||
|
logger.debug('Grab extended stats for process {}'.format(proc['pid']))
|
||||||
|
|
||||||
|
# Compute CPU and MEM min/max/mean
|
||||||
|
for stat_prefix in ['cpu', 'memory']:
|
||||||
|
if stat_prefix + '_min' not in self.extended_process:
|
||||||
|
ret[stat_prefix + '_min'] = proc[stat_prefix + '_percent']
|
||||||
|
else:
|
||||||
|
ret[stat_prefix + '_min'] = proc[stat_prefix + '_percent'] if proc[stat_prefix + '_min'] > proc[stat_prefix + '_percent'] else proc[stat_prefix + '_min']
|
||||||
|
if stat_prefix + '_max' not in self.extended_process:
|
||||||
|
ret[stat_prefix + '_max'] = proc[stat_prefix + '_percent']
|
||||||
|
else:
|
||||||
|
ret[stat_prefix + '_max'] = proc[stat_prefix + '_percent'] if proc[stat_prefix + '_max'] < proc[stat_prefix + '_percent'] else proc[stat_prefix + '_max']
|
||||||
|
if stat_prefix + '_mean_sum' not in self.extended_process:
|
||||||
|
ret[stat_prefix + '_mean_sum'] = proc[stat_prefix + '_percent']
|
||||||
|
else:
|
||||||
|
ret[stat_prefix + '_mean_sum'] = proc[stat_prefix + '_mean_sum'] + proc[stat_prefix + '_percent']
|
||||||
|
if stat_prefix + '_mean_counter' not in self.extended_process:
|
||||||
|
ret[stat_prefix + '_mean_counter'] = 1
|
||||||
|
else:
|
||||||
|
ret[stat_prefix + '_mean_counter'] = proc[stat_prefix + '_mean_counter'] + 1
|
||||||
|
ret[stat_prefix + '_mean'] = ret[stat_prefix + '_mean_sum'] / ret[stat_prefix + '_mean_counter']
|
||||||
|
|
||||||
|
ret['extended_stats'] = True
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def is_selected_extended_process(self, position):
|
||||||
|
"""Return True if the process is the selected one for extended stats."""
|
||||||
|
return hasattr(self.args, 'programs') and \
|
||||||
|
not self.args.programs and \
|
||||||
|
hasattr(self.args, 'enable_process_extended') and \
|
||||||
|
self.args.enable_process_extended and \
|
||||||
|
not self.disable_extended_tag and \
|
||||||
|
hasattr(self.args, 'cursor_position') and \
|
||||||
|
position == self.args.cursor_position and \
|
||||||
|
not self.args.disable_cursor
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Update the processes stats."""
|
"""Update the processes stats."""
|
||||||
# Reset the stats
|
# Reset the stats
|
||||||
@ -305,59 +400,25 @@ class GlancesProcesses(object):
|
|||||||
# Update the processcount
|
# Update the processcount
|
||||||
self.update_processcount(self.processlist)
|
self.update_processcount(self.processlist)
|
||||||
|
|
||||||
# Loop over processes and add metadata
|
# Loop over processes and :
|
||||||
first = True
|
# - add extended stats for selected process
|
||||||
for proc in self.processlist:
|
# - add metadata
|
||||||
# Get extended stats, only for top processes (see issue #403).
|
for position, proc in enumerate(self.processlist):
|
||||||
if first and not self.disable_extended_tag:
|
# Extended stats
|
||||||
# - cpu_affinity (Linux, Windows, FreeBSD)
|
################
|
||||||
# - ionice (Linux and Windows > Vista)
|
|
||||||
# - num_ctx_switches (not available on Illumos/Solaris)
|
|
||||||
# - num_fds (Unix-like)
|
|
||||||
# - num_handles (Windows)
|
|
||||||
# - memory_maps (only swap, Linux)
|
|
||||||
# https://www.cyberciti.biz/faq/linux-which-process-is-using-swap/
|
|
||||||
# - connections (TCP and UDP)
|
|
||||||
extended = {}
|
|
||||||
try:
|
|
||||||
top_process = psutil.Process(proc['pid'])
|
|
||||||
extended_stats = ['cpu_affinity', 'ionice', 'num_ctx_switches']
|
|
||||||
if LINUX:
|
|
||||||
# num_fds only available on Unix system (see issue #1351)
|
|
||||||
extended_stats += ['num_fds']
|
|
||||||
if WINDOWS:
|
|
||||||
extended_stats += ['num_handles']
|
|
||||||
|
|
||||||
# Get the extended stats
|
# Get the selected process when the 'e' key is pressed
|
||||||
extended = top_process.as_dict(attrs=extended_stats, ad_value=None)
|
if self.is_selected_extended_process(position):
|
||||||
|
self.extended_process = proc
|
||||||
|
|
||||||
if LINUX:
|
# Grab extended stats only for the selected process (see issue #2225)
|
||||||
try:
|
if self.extended_process is not None and \
|
||||||
extended['memory_swap'] = sum([v.swap for v in top_process.memory_maps()])
|
proc['pid'] == self.extended_process['pid']:
|
||||||
except (psutil.NoSuchProcess, KeyError):
|
proc.update(self.get_extended_stats(self.extended_process))
|
||||||
# (KeyError catch for issue #1551)
|
self.extended_process = proc
|
||||||
pass
|
|
||||||
except (psutil.AccessDenied, NotImplementedError):
|
# Meta data
|
||||||
# NotImplementedError: /proc/${PID}/smaps file doesn't exist
|
###########
|
||||||
# on kernel < 2.6.14 or CONFIG_MMU kernel configuration option
|
|
||||||
# is not enabled (see psutil #533/glances #413).
|
|
||||||
extended['memory_swap'] = None
|
|
||||||
try:
|
|
||||||
extended['tcp'] = len(top_process.connections(kind="tcp"))
|
|
||||||
extended['udp'] = len(top_process.connections(kind="udp"))
|
|
||||||
except (psutil.AccessDenied, psutil.NoSuchProcess):
|
|
||||||
# Manage issue1283 (psutil.AccessDenied)
|
|
||||||
extended['tcp'] = None
|
|
||||||
extended['udp'] = None
|
|
||||||
except (psutil.NoSuchProcess, ValueError, AttributeError) as e:
|
|
||||||
logger.error('Can not grab extended stats ({})'.format(e))
|
|
||||||
extended['extended_stats'] = False
|
|
||||||
else:
|
|
||||||
logger.debug('Grab extended stats for process {}'.format(proc['pid']))
|
|
||||||
extended['extended_stats'] = True
|
|
||||||
proc.update(extended)
|
|
||||||
first = False
|
|
||||||
# /End of extended stats
|
|
||||||
|
|
||||||
# PID is the key
|
# PID is the key
|
||||||
proc['key'] = 'pid'
|
proc['key'] = 'pid'
|
||||||
@ -424,9 +485,14 @@ class GlancesProcesses(object):
|
|||||||
"""Get the number of processes."""
|
"""Get the number of processes."""
|
||||||
return self.processcount
|
return self.processcount
|
||||||
|
|
||||||
def getlist(self, sorted_by=None):
|
def getlist(self, sorted_by=None, as_programs=False):
|
||||||
"""Get the processlist."""
|
"""Get the processlist.
|
||||||
return self.processlist
|
By default, return the list of threads.
|
||||||
|
If as_programs is True, return the list of programs."""
|
||||||
|
if as_programs:
|
||||||
|
return processes_to_programs(self.processlist)
|
||||||
|
else:
|
||||||
|
return self.processlist
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def sort_key(self):
|
def sort_key(self):
|
||||||
|
@ -50,6 +50,9 @@ class GlancesStandalone(object):
|
|||||||
self.display_modules_list()
|
self.display_modules_list()
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
# The args is needed to get the selected process in the process list (Curses mode)
|
||||||
|
glances_processes.set_args(args)
|
||||||
|
|
||||||
# If process extended stats is disabled by user
|
# If process extended stats is disabled by user
|
||||||
if not args.enable_process_extended:
|
if not args.enable_process_extended:
|
||||||
logger.debug("Extended stats for top process are disabled")
|
logger.debug("Extended stats for top process are disabled")
|
||||||
@ -193,5 +196,7 @@ class GlancesStandalone(object):
|
|||||||
)
|
)
|
||||||
print("You should consider upgrading using: pip install --upgrade glances")
|
print("You should consider upgrading using: pip install --upgrade glances")
|
||||||
print("Disable this warning temporarily using: glances --disable-check-update")
|
print("Disable this warning temporarily using: glances --disable-check-update")
|
||||||
print("To disable it permanently, refer config reference at "
|
print(
|
||||||
"https://glances.readthedocs.io/en/latest/config.html#syntax")
|
"To disable it permanently, refer config reference at "
|
||||||
|
"https://glances.readthedocs.io/en/latest/config.html#syntax"
|
||||||
|
)
|
||||||
|
@ -34,5 +34,5 @@ six
|
|||||||
sparklines
|
sparklines
|
||||||
statsd
|
statsd
|
||||||
wifi
|
wifi
|
||||||
zeroconf==0.47.3; python_version < "3.7"
|
zeroconf==0.54.0; python_version < "3.7"
|
||||||
zeroconf>=0.19.1; python_version >= "3.7"
|
zeroconf; python_version >= "3.7"
|
||||||
|
2
setup.py
2
setup.py
@ -61,7 +61,7 @@ def get_install_requires():
|
|||||||
def get_install_extras_require():
|
def get_install_extras_require():
|
||||||
extras_require = {
|
extras_require = {
|
||||||
'action': ['chevron'],
|
'action': ['chevron'],
|
||||||
'browser': ['zeroconf==0.47.3' if PY2 else 'zeroconf>=0.19.1'],
|
'browser': ['zeroconf==0.54.0' if PY2 else 'zeroconf>=0.19.1'],
|
||||||
'cloud': ['requests'],
|
'cloud': ['requests'],
|
||||||
'docker': ['docker>=2.0.0', 'python-dateutil', 'six'],
|
'docker': ['docker>=2.0.0', 'python-dateutil', 'six'],
|
||||||
'export': ['bernhard', 'cassandra-driver', 'couchdb', 'elasticsearch',
|
'export': ['bernhard', 'cassandra-driver', 'couchdb', 'elasticsearch',
|
||||||
|
@ -221,6 +221,16 @@ class TestGlances(unittest.TestCase):
|
|||||||
self.assertIsInstance(req.json(), dict)
|
self.assertIsInstance(req.json(), dict)
|
||||||
self.assertIsInstance(req.json()['interface_name'], list)
|
self.assertIsInstance(req.json()['interface_name'], list)
|
||||||
|
|
||||||
|
def test_012_status(self):
|
||||||
|
"""Check status endpoint."""
|
||||||
|
method = "status"
|
||||||
|
print('INFO: [TEST_012] Status')
|
||||||
|
print("HTTP RESTful request: %s/%s" % (URL, method))
|
||||||
|
req = self.http_get("%s/%s" % (URL, method))
|
||||||
|
|
||||||
|
self.assertTrue(req.ok)
|
||||||
|
self.assertEqual(req.text, "Active")
|
||||||
|
|
||||||
def test_999_stop_server(self):
|
def test_999_stop_server(self):
|
||||||
"""Stop the Glances Web Server."""
|
"""Stop the Glances Web Server."""
|
||||||
print('INFO: [TEST_999] Stop the Glances Web Server')
|
print('INFO: [TEST_999] Stop the Glances Web Server')
|
||||||
|
Loading…
Reference in New Issue
Block a user