diff --git a/README.rst b/README.rst index 7ec1c3604..c02d32662 100644 --- a/README.rst +++ b/README.rst @@ -10,3252 +10,85 @@ openwisp-monitoring :alt: Test coverage .. image:: https://img.shields.io/librariesio/github/openwisp/openwisp-monitoring - :target: https://libraries.io/github/openwisp/openwisp-monitoring#repository_dependencies - :alt: Dependency monitoring + :target: https://libraries.io/github/openwisp/openwisp-monitoring#repository_dependencies + :alt: Dependency monitoring .. image:: https://badge.fury.io/py/openwisp-monitoring.svg :target: http://badge.fury.io/py/openwisp-monitoring :alt: pypi .. image:: https://pepy.tech/badge/openwisp-monitoring - :target: https://pepy.tech/project/openwisp-monitoring - :alt: downloads + :target: https://pepy.tech/project/openwisp-monitoring + :alt: downloads .. image:: https://img.shields.io/gitter/room/nwjs/nw.js.svg?style=flat-square - :target: https://gitter.im/openwisp/monitoring - :alt: support chat + :target: https://gitter.im/openwisp/monitoring + :alt: support chat .. image:: https://img.shields.io/badge/code%20style-black-000000.svg - :target: https://pypi.org/project/black/ - :alt: code style: black + :target: https://pypi.org/project/black/ + :alt: code style: black .. image:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/monitoring-demo.gif - :target: https://github.com/openwisp/openwisp-monitoring/tree/docs/docs/monitoring-demo.gif - :alt: Feature Highlights + :target: https://github.com/openwisp/openwisp-monitoring/tree/docs/docs/monitoring-demo.gif + :alt: Feature Highlights ------------- - -**Need a quick overview?** `Try the OpenWISP Demo `_. +---- -OpenWISP Monitoring is a network monitoring system written in Python and Django, -designed to be **extensible**, **programmable**, **scalable** and easy to use by end users: -once the system is configured, monitoring checks, alerts and metric collection -happens automatically. +**Need a quick overview?** `Try the OpenWISP Demo +`_. -See the `available features <#available-features>`_. +OpenWISP Monitoring is a network monitoring system written in Python and +Django, designed to be **extensible**, **programmable**, **scalable** and +easy to use by end users: once the system is configured, monitoring +checks, alerts and metric collection happens automatically. -`OpenWISP `_ is not only an application designed for end users, -but can also be used as a framework on which custom network automation solutions can be -built on top of its building blocks. +`OpenWISP `_ is not only an application designed for +end users, but can also be used as a framework on which custom network +automation solutions can be built on top of its building blocks. Other popular building blocks that are part of the OpenWISP ecosystem are: -- `openwisp-controller `_: +- `openwisp-controller `_: network and WiFi controller: provisioning, configuration management, - x509 PKI management and more; works on OpenWRT, but designed to work also on other systems. -- `openwisp-network-topology `_: - provides way to collect and visualize network topology data from - dynamic mesh routing daemons or other network software (eg: OpenVPN); - it can be used in conjunction with openwisp-monitoring to get a better idea - of the state of the network -- `openwisp-firmware-upgrader `_: - automated firmware upgrades (single device or mass network upgrades) -- `openwisp-radius `_: - based on FreeRADIUS, allows to implement network access authentication systems like - 802.1x WPA2 Enterprise, captive portal authentication, Hotspot 2.0 (802.11u) -- `openwisp-ipam `_: - it allows to manage the IP address space of networks + x509 PKI management and more; works on OpenWrt, but designed to work + also on other systems. +- `openwisp-network-topology + `_: provides way to + collect and visualize network topology data from dynamic mesh routing + daemons or other network software (eg: OpenVPN); it can be used in + conjunction with openwisp-monitoring to get a better idea of the state + of the network +- `openwisp-firmware-upgrader + `_: automated firmware + upgrades (single device or mass network upgrades) +- `openwisp-radius `_: + based on FreeRADIUS, allows to implement network access authentication + systems like 802.1x WPA2 Enterprise, captive portal authentication, + Hotspot 2.0 (802.11u) +- `openwisp-ipam `_: it allows to + manage the IP address space of networks **For a more complete overview of the OpenWISP modules and architecture**, -see the -`OpenWISP Architecture Overview +see the `OpenWISP Architecture Overview `_. .. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/dashboard.png - :align: center - -Available Features ------------------- - -* Collection of monitoring information in a timeseries database (currently only influxdb is supported) -* Allows to browse alerts easily from the user interface with one click -* Collects and displays `device status <#device-status>`_ information like - uptime, RAM status, CPU load averages, - Interface properties and addresses, WiFi interface status and associated clients, - Neighbors information, DHCP Leases, Disk/Flash status -* Monitoring charts for `ping success rate <#ping>`_, `packet loss <#ping>`_, - `round trip time (latency) <#ping>`_, - `associated wifi clients <#wifi-clients>`_, `interface traffic <#traffic>`_, - `RAM usage <#memory-usage>`_, `CPU load <#cpu-load>`_, `flash/disk usage <#disk-usage>`_, - mobile signal (LTE/UMTS/GSM `signal strength <#mobile-signal-strength>`_, - `signal quality <#mobile-signal-quality>`_, - `access technology in use <#mobile-access-technology-in-use>`_), `bandwidth <#iperf3>`_, - `transferred data <#iperf3>`_, `restransmits <#iperf3>`_, `jitter <#iperf3>`_, - `datagram <#iperf3>`_, `datagram loss <#iperf3>`_ -* Maintains a record of `WiFi sessions <#monitoring-wifi-sessions>`_ with clients' - MAC address and vendor, session start and stop time and connected device - along with other information -* Charts can be viewed at resolutions of the last 1 day, 3 days, 7 days, 30 days, and 365 days -* Configurable alerts -* CSV Export of monitoring data -* An overview of the status of the network is shown in the admin dashboard, - a chart shows the percentages of devices which are online, offline or having issues; - there are also `two timeseries charts which show the total unique WiFI clients and - the traffic flowing to the network `_, - a geographic map is also available for those who use the geographic features of OpenWISP -* Possibility to configure additional `Metrics <#openwisp_monitoring_metrics>`_ and `Charts <#openwisp_monitoring_charts>`_ -* Extensible active check system: it's possible to write additional checks that - are run periodically using python classes -* Extensible metrics and charts: it's possible to define new metrics and new charts -* API to retrieve the chart metrics and status information of each device - based on `NetJSON DeviceMonitoring `_ -* `Iperf3 check <#iperf3-1>`_ that provides network performance measurements such as maximum - achievable bandwidth, jitter, datagram loss etc of the openwrt device using `iperf3 utility `_ - ------------- - -.. contents:: **Table of Contents**: - :backlinks: none - :depth: 3 - ------------- - -Installation instructions -------------------------- - -Deploy it in production -~~~~~~~~~~~~~~~~~~~~~~~ - -See: - -- `ansible-openwisp2 `_ -- `docker-openwisp `_ - -Install system dependencies -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -*openwisp-monitoring* uses InfluxDB to store metrics. Follow the -`installation instructions from InfluxDB's official documentation `_. - -**Note:** Only *InfluxDB 1.8.x* is supported in *openwisp-monitoring*. - -Install system packages: - -.. code-block:: shell - - sudo apt install -y openssl libssl-dev \ - gdal-bin libproj-dev libgeos-dev \ - fping - -Install stable version from PyPI -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Install from PyPI: - -.. code-block:: shell - - pip install openwisp-monitoring - -Install development version -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Install tarball: - -.. code-block:: shell - - pip install https://github.com/openwisp/openwisp-monitoring/tarball/master - -Alternatively, you can install via pip using git: - -.. code-block:: shell - - pip install -e git+git://github.com/openwisp/openwisp-monitoring#egg=openwisp_monitoring - -If you want to contribute, follow the instructions in -`"Installing for development" <#installing-for-development>`_ section. - -Installing for development -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Install the system dependencies as mentioned in the -`"Install system dependencies" <#install-system-dependencies>`_ section. -Install these additional packages that are required for development: - -.. code-block:: shell - - sudo apt install -y sqlite3 libsqlite3-dev \ - libspatialite-dev libsqlite3-mod-spatialite \ - chromium - -Fork and clone the forked repository: - -.. code-block:: shell - - git clone git://github.com//openwisp-monitoring - -Navigate into the cloned repository: - -.. code-block:: shell - - cd openwisp-monitoring/ - -Start Redis and InfluxDB using Docker: - -.. code-block:: shell - - docker-compose up -d redis influxdb - -Setup and activate a virtual-environment. (we'll be using `virtualenv `_) - -.. code-block:: shell - - python -m virtualenv env - source env/bin/activate - -Make sure that you are using pip version 20.2.4 before moving to the next step: - -.. code-block:: shell - - pip install -U pip wheel setuptools - -Install development dependencies: - -.. code-block:: shell - - pip install -e . - pip install -r requirements-test.txt - npm install -g jshint stylelint - -Install WebDriver for Chromium for your browser version from ``_ -and extract ``chromedriver`` to one of directories from your ``$PATH`` (example: ``~/.local/bin/``). - -Create database: - -.. code-block:: shell - - cd tests/ - ./manage.py migrate - ./manage.py createsuperuser - -Run celery and celery-beat with the following commands (separate terminal windows are needed): - -.. code-block:: shell - - cd tests/ - celery -A openwisp2 worker -l info - celery -A openwisp2 beat -l info - -Launch development server: - -.. code-block:: shell - - ./manage.py runserver 0.0.0.0:8000 - -You can access the admin interface at http://127.0.0.1:8000/admin/. - -Run tests with: - -.. code-block:: shell - - ./runtests.py # using --parallel is not supported in this module - -Run quality assurance tests with: - -.. code-block:: shell - - ./run-qa-checks - -Install and run on docker -~~~~~~~~~~~~~~~~~~~~~~~~~ - -**Note**: This Docker image is for development purposes only. -For the official OpenWISP Docker images, see: `docker-openwisp -`_. - -Build from the Dockerfile: - -.. code-block:: shell - - docker-compose build - -Run the docker container: - -.. code-block:: shell - - docker-compose up - -Setup (integrate in an existing Django project) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Follow the setup instructions of `openwisp-controller -`_, then add the settings described below. - -.. code-block:: python - - INSTALLED_APPS = [ - # django apps - # all-auth - 'django.contrib.sites', - 'allauth', - 'allauth.account', - 'allauth.socialaccount', - 'django_extensions', - 'django_filters', - # openwisp2 modules - 'openwisp_users', - 'openwisp_controller.pki', - 'openwisp_controller.config', - 'openwisp_controller.connection', - 'openwisp_controller.geo', - # monitoring - 'openwisp_monitoring.monitoring', - 'openwisp_monitoring.device', - 'openwisp_monitoring.check', - 'nested_admin', - # notifications - 'openwisp_notifications', - # openwisp2 admin theme (must be loaded here) - 'openwisp_utils.admin_theme', - 'admin_auto_filters', - # admin - 'django.contrib.admin', - 'django.forms', - 'import_export' - # other dependencies ... - ] - - # Make sure you change them in production - # You can select one of the backends located in openwisp_monitoring.db.backends - TIMESERIES_DATABASE = { - 'BACKEND': 'openwisp_monitoring.db.backends.influxdb', - 'USER': 'openwisp', - 'PASSWORD': 'openwisp', - 'NAME': 'openwisp2', - 'HOST': 'localhost', - 'PORT': '8086', - 'OPTIONS': { - # Specify additional options to be used while initializing - # database connection. - # Note: These options may differ based on the backend used. - 'udp_writes': True, - 'udp_port': 8089, - } - } - -``urls.py``: - -.. code-block:: python - - from django.conf import settings - from django.conf.urls import include, url - from django.contrib.staticfiles.urls import staticfiles_urlpatterns - - from openwisp_utils.admin_theme.admin import admin, openwisp_admin - - openwisp_admin() - - urlpatterns = [ - url(r'^admin/', include(admin.site.urls)), - url(r'', include('openwisp_controller.urls')), - url(r'', include('openwisp_monitoring.urls')), - ] - - urlpatterns += staticfiles_urlpatterns() - -Configure caching (you may use a different cache storage if you want): - -.. code-block:: python - - CACHES = { - 'default': { - 'BACKEND': 'django_redis.cache.RedisCache', - 'LOCATION': 'redis://localhost/0', - 'OPTIONS': { - 'CLIENT_CLASS': 'django_redis.client.DefaultClient', - } - } - } - - SESSION_ENGINE = 'django.contrib.sessions.backends.cache' - SESSION_CACHE_ALIAS = 'default' - -Configure celery (you may use a different broker if you want): - -.. code-block:: python - - # here we show how to configure celery with redis but you can - # use other brokers if you want, consult the celery docs - CELERY_BROKER_URL = 'redis://localhost/1' - CELERY_BEAT_SCHEDULE = { - 'run_checks': { - 'task': 'openwisp_monitoring.check.tasks.run_checks', - # Executes only ping & config check every 5 min - 'schedule': timedelta(minutes=5), - 'args': ( - [ # Checks path - 'openwisp_monitoring.check.classes.Ping', - 'openwisp_monitoring.check.classes.ConfigApplied', - ], - ), - 'relative': True, - }, - # Delete old WifiSession - 'delete_wifi_clients_and_sessions': { - 'task': 'openwisp_monitoring.monitoring.tasks.delete_wifi_clients_and_sessions', - 'schedule': timedelta(days=180), - }, - } - - INSTALLED_APPS.append('djcelery_email') - EMAIL_BACKEND = 'djcelery_email.backends.CeleryEmailBackend' - -If you decide to use Redis (as shown in these examples), -install the following python packages. - -.. code-block:: shell - - pip install redis django-redis - -Quickstart Guide ----------------- - -Install OpenWISP Monitoring -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Install *OpenWISP Monitoring* using one of the methods mentioned in the -`"Installation instructions" <#installation-instructions>`_. - -Install openwisp-config on the device -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -`Install the openwisp-config agent for OpenWrt -`_ -on your device. - -Install monitoring packages on the device -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -`Install the openwrt-openwisp-monitoring packages -`_ -on your device. - -These packages collect and send the -monitoring data from the device to OpenWISP Monitoring and -are required to collect `metrics <#openwisp_monitoring_metrics>`_ -like interface traffic, WiFi clients, CPU load, memory usage, etc. - -**Note**: if you are an existing user of *openwisp-monitoring* and are using -the legacy *monitoring template* for collecting metrics, we highly recommend -`Migrating from monitoring scripts to monitoring packages -<#migrating-from-monitoring-scripts-to-monitoring-packages>`_. - -Make sure OpenWISP can reach your devices -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In order to perform `active checks <#available-checks>`_ and other actions like -`triggering the push of configuration changes -`_, -`executing shell commands -`_ or -`performing firmware upgrades -`_, -**the OpenWISP server needs to be able to reach the network devices**. - -There are mainly two deployment scenarios for OpenWISP: - -1. the OpenWISP server is deployed on the public internet and the devices are - geographically distributed across different locations: - **in this case a management tunnel is needed** -2. the OpenWISP server is deployed on a computer/server which is located in - the same Layer 2 network (that is, in the same LAN) where the devices - are located. - **in this case a management tunnel is NOT needed** - -1. Public internet deployment -############################# - -This is the most common scenario: - -- the OpenWISP server is deployed to the public internet, hence the - server has a public IPv4 (and IPv6) address and usually a valid - SSL certificate provided by Mozilla Letsencrypt or another SSL provider -- the network devices are geographically distributed across different - locations (different cities, different regions, different countries) - -In this scenario, the OpenWISP application will not be able to reach the -devices **unless a management tunnel** is used, for that reason having -a management VPN like OpenVPN, Wireguard or any other tunneling solution -is paramount, not only to allow OpenWISP to work properly, but also to -be able to perform debugging and troubleshooting when needed. - -In this scenario, the following requirements are needed: - -- a VPN server must be installed in a way that the OpenWISP - server can reach the VPN peers, for more information on how to do this - via OpenWISP please refer to the following sections: - - - `OpenVPN tunnel automation - `_ - - `Wireguard tunnel automation - `_ - - If you prefer to use other tunneling solutions (L2TP, Softether, etc.) - and know how to configure those solutions on your own, - that's totally fine as well. - - If the OpenWISP server is connected to a network infrastructure - which allows it to reach the devices via pre-existing tunneling or - Intranet solutions (eg: MPLS, SD-WAN), then setting up a VPN server - is not needed, as long as there's a dedicated interface on OpenWrt - which gets an IP address assigned to it and which is reachable from - the OpenWISP server. - -- The devices must be configured to join the management tunnel automatically, - either via a pre-existing configuration in the firmware or via an - `OpenWISP Template `_. - -- The `openwisp-config `_ - agent on the devices must be configured to specify - the ``management_interface`` option, the agent will communicate the - IP of the management interface to the OpenWISP Server and OpenWISP will - use the management IP for reaching the device. - - For example, if the *management interface* is named ``tun0``, - the openwisp-config configuration should look like the following example: - -.. code-block:: text - - # In /etc/config/openwisp on the device - - config controller 'http' - # ... other configuration directives ... - option management_interface 'tun0' - -2. LAN deployment -################# - -When the OpenWISP server and the network devices are deployed in the same -L2 network (eg: an office LAN) and the OpenWISP server is reachable -on the LAN address, OpenWISP can then use the **Last IP** field of the -devices to reach them. - -In this scenario it's necessary to set the -`"OPENWISP_MONITORING_MANAGEMENT_IP_ONLY" <#openwisp-monitoring-management-ip-only>`_ -setting to ``False``. - -Creating checks for a device -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -By default, the `active checks <#available-checks>`_ are created -automatically for all devices, unless the automatic creation of some -specific checks has been disabled, for more information on how to do this, -refer to the `active checks <#available-checks>`_ section. - -These checks are created and executed in the background by celery workers. - -Passive vs Active Metric Collection ------------------------------------ - -The `the different device metric -`_ -collected by OpenWISP Monitoring can be divided in two categories: - -1. **metrics collected actively by OpenWISP**: - these metrics are collected by the celery workers running on the - OpenWISP server, which continuously sends network requests to the - devices and store the results; -2. **metrics collected passively by OpenWISP**: - these metrics are sent by the - `openwrt-openwisp-monitoring agent <#install-monitoring-packages-on-the-device>`_ - installed on the network devices and are collected by OpenWISP via - its REST API. - -The `"Available Checks" <#available-checks>`_ section of this document -lists the currently implemented **active checks**. - -Device Health Status --------------------- - -The possible values for the health status field (``DeviceMonitoring.status``) -are explained below. - -``UNKNOWN`` -~~~~~~~~~~~ - -Whenever a new device is created it will have ``UNKNOWN`` as it's default Heath Status. - -It implies that the system doesn't know whether the device is reachable yet. - -``OK`` -~~~~~~ - -Everything is working normally. - -``PROBLEM`` -~~~~~~~~~~~ - -One of the metrics has a value which is not in the expected range -(the threshold value set in the alert settings has been crossed). - -Example: CPU usage should be less than 90% but current value is at 95%. - -``CRITICAL`` -~~~~~~~~~~~~ - -One of the metrics defined in ``OPENWISP_MONITORING_CRITICAL_DEVICE_METRICS`` -has a value which is not in the expected range -(the threshold value set in the alert settings has been crossed). - -Example: ping is by default a critical metric which is expected to be always 1 -(reachable). - -Default Metrics ---------------- - -Device Status -~~~~~~~~~~~~~ - -This metric stores the status of the device for viewing purposes. - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/device-status-1.png - :align: center - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/device-status-2.png - :align: center - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/device-status-3.png - :align: center - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/device-status-4.png - :align: center - -Ping -~~~~ - -+--------------------+----------------------------------------------------------------+ -| **measurement**: | ``ping`` | -+--------------------+----------------------------------------------------------------+ -| **types**: | ``int`` (reachable and loss), ``float`` (rtt) | -+--------------------+----------------------------------------------------------------+ -| **fields**: | ``reachable``, ``loss``, ``rtt_min``, ``rtt_max``, ``rtt_avg`` | -+--------------------+----------------------------------------------------------------+ -| **configuration**: | ``ping`` | -+--------------------+----------------------------------------------------------------+ -| **charts**: | ``uptime`` (Ping Success Rate), ``packet_loss``, ``rtt`` | -+--------------------+----------------------------------------------------------------+ - -**Ping Success Rate**: - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/1.1/ping-success-rate.png - :align: center - -**Packet loss**: - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/packet-loss.png - :align: center - -**Round Trip Time**: - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/rtt.png - :align: center - -Traffic -~~~~~~~ - -+--------------------+--------------------------------------------------------------------------+ -| **measurement**: | ``traffic`` | -+--------------------+--------------------------------------------------------------------------+ -| **type**: | ``int`` | -+--------------------+--------------------------------------------------------------------------+ -| **fields**: | ``rx_bytes``, ``tx_bytes`` | -+--------------------+--------------------------------------------------------------------------+ -| **tags**: | .. code-block:: python | -| | | -| | { | -| | 'organization_id': '', | -| | 'ifname': '', | -| | # optional | -| | 'location_id': '', | -| | 'floorplan_id': '', | -| | } | -+--------------------+--------------------------------------------------------------------------+ -| **configuration**: | ``traffic`` | -+--------------------+--------------------------------------------------------------------------+ -| **charts**: | ``traffic`` | -+--------------------+--------------------------------------------------------------------------+ - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/1.1/traffic.png - :align: center - -WiFi Clients -~~~~~~~~~~~~ - -+--------------------+--------------------------------------------------------------------------+ -| **measurement**: | ``wifi_clients`` | -+--------------------+--------------------------------------------------------------------------+ -| **type**: | ``int`` | -+--------------------+--------------------------------------------------------------------------+ -| **fields**: | ``clients`` | -+--------------------+--------------------------------------------------------------------------+ -| **tags**: | .. code-block:: python | -| | | -| | { | -| | 'organization_id': '', | -| | 'ifname': '', | -| | # optional | -| | 'location_id': '', | -| | 'floorplan_id': '', | -| | } | -+--------------------+--------------------------------------------------------------------------+ -| **configuration**: | ``clients`` | -+--------------------+--------------------------------------------------------------------------+ -| **charts**: | ``wifi_clients`` | -+--------------------+--------------------------------------------------------------------------+ - - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/wifi-clients.png - :align: center - -Memory Usage -~~~~~~~~~~~~ - -+--------------------+--------------------------------------------------------------------------------------------------------------------------------------+ -| **measurement**: | ```` | -+--------------------+--------------------------------------------------------------------------------------------------------------------------------------+ -| **type**: | ``float`` | -+--------------------+--------------------------------------------------------------------------------------------------------------------------------------+ -| **fields**: | ``percent_used``, ``free_memory``, ``total_memory``, ``buffered_memory``, ``shared_memory``, ``cached_memory``, ``available_memory`` | -+--------------------+--------------------------------------------------------------------------------------------------------------------------------------+ -| **configuration**: | ``memory`` | -+--------------------+--------------------------------------------------------------------------------------------------------------------------------------+ -| **charts**: | ``memory`` | -+--------------------+--------------------------------------------------------------------------------------------------------------------------------------+ - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/memory.png - :align: center - -CPU Load -~~~~~~~~ - -+--------------------+----------------------------------------------------+ -| **measurement**: | ``load`` | -+--------------------+----------------------------------------------------+ -| **type**: | ``float`` | -+--------------------+----------------------------------------------------+ -| **fields**: | ``cpu_usage``, ``load_1``, ``load_5``, ``load_15`` | -+--------------------+----------------------------------------------------+ -| **configuration**: | ``load`` | -+--------------------+----------------------------------------------------+ -| **charts**: | ``load`` | -+--------------------+----------------------------------------------------+ - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/cpu-load.png - :align: center - -Disk Usage -~~~~~~~~~~ - -+--------------------+-------------------+ -| **measurement**: | ``disk`` | -+--------------------+-------------------+ -| **type**: | ``float`` | -+--------------------+-------------------+ -| **fields**: | ``used_disk`` | -+--------------------+-------------------+ -| **configuration**: | ``disk`` | -+--------------------+-------------------+ -| **charts**: | ``disk`` | -+--------------------+-------------------+ - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/disk-usage.png - :align: center - -Mobile Signal Strength -~~~~~~~~~~~~~~~~~~~~~~ - -+--------------------+-----------------------------------------+ -| **measurement**: | ``signal_strength`` | -+--------------------+-----------------------------------------+ -| **type**: | ``float`` | -+--------------------+-----------------------------------------+ -| **fields**: | ``signal_strength``, ``signal_power`` | -+--------------------+-----------------------------------------+ -| **configuration**: | ``signal_strength`` | -+--------------------+-----------------------------------------+ -| **charts**: | ``signal_strength`` | -+--------------------+-----------------------------------------+ - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/signal-strength.png - :align: center - -Mobile Signal Quality -~~~~~~~~~~~~~~~~~~~~~~ - -+--------------------+-----------------------------------------+ -| **measurement**: | ``signal_quality`` | -+--------------------+-----------------------------------------+ -| **type**: | ``float`` | -+--------------------+-----------------------------------------+ -| **fields**: | ``signal_quality``, ``signal_quality`` | -+--------------------+-----------------------------------------+ -| **configuration**: | ``signal_quality`` | -+--------------------+-----------------------------------------+ -| **charts**: | ``signal_quality`` | -+--------------------+-----------------------------------------+ - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/signal-quality.png - :align: center - -Mobile Access Technology in use -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------------+-------------------+ -| **measurement**: | ``access_tech`` | -+--------------------+-------------------+ -| **type**: | ``int`` | -+--------------------+-------------------+ -| **fields**: | ``access_tech`` | -+--------------------+-------------------+ -| **configuration**: | ``access_tech`` | -+--------------------+-------------------+ -| **charts**: | ``access_tech`` | -+--------------------+-------------------+ - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/access-technology.png - :align: center - -Iperf3 -~~~~~~ - -+--------------------+---------------------------------------------------------------------------------------------------------------------------+ -| **measurement**: | ``iperf3`` | -+--------------------+---------------------------------------------------------------------------------------------------------------------------+ -| **types**: | | ``int`` (iperf3_result, sent_bytes_tcp, received_bytes_tcp, retransmits, sent_bytes_udp, total_packets, lost_packets), | -| | | ``float`` (sent_bps_tcp, received_bps_tcp, sent_bps_udp, jitter, lost_percent) | -+--------------------+---------------------------------------------------------------------------------------------------------------------------+ -| **fields**: | | ``iperf3_result``, ``sent_bps_tcp``, ``received_bps_tcp``, ``sent_bytes_tcp``, ``received_bytes_tcp``, ``retransmits``, | -| | | ``sent_bps_udp``, ``sent_bytes_udp``, ``jitter``, ``total_packets``, ``lost_packets``, ``lost_percent`` | -+--------------------+---------------------------------------------------------------------------------------------------------------------------+ -| **configuration**: | ``iperf3`` | -+--------------------+---------------------------------------------------------------------------------------------------------------------------+ -| **charts**: | ``bandwidth``, ``transfer``, ``retransmits``, ``jitter``, ``datagram``, ``datagram_loss`` | -+--------------------+---------------------------------------------------------------------------------------------------------------------------+ - -**Bandwidth**: - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/1.1/bandwidth.png - :align: center - -**Transferred Data**: - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/1.1/transferred-data.png - :align: center - -**Retransmits**: - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/1.1/retransmits.png - :align: center - -**Jitter**: - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/1.1/jitter.png - :align: center - -**Datagram**: - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/1.1/datagram.png - :align: center - -**Datagram loss**: - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/1.1/datagram-loss.png - :align: center - -For more info on how to configure and use Iperf3, please refer to -`iperf3 check usage instructions <#iperf3-check-usage-instructions>`_. - -**Note:** Iperf3 charts uses ``connect_points=True`` in -`default chart configuration <#openwisp_monitoring_charts>`_ that joins it's individual chart data points. - -Dashboard Monitoring Charts ---------------------------- - -.. figure:: https://github.com/openwisp/openwisp-monitoring/blob/docs/docs/1.1/dashboard-charts.png - :align: center - -OpenWISP Monitoring adds two timeseries charts to the admin dashboard: - -- **General WiFi clients Chart**: Shows the number of connected clients to the WiFi - interfaces of devices in the network. -- **General traffic Chart**: Shows the amount of traffic flowing in the network. - -You can configure the interfaces included in the **General traffic chart** using -the `"OPENWISP_MONITORING_DASHBOARD_TRAFFIC_CHART" -<#openwisp_monitoring_dashboard_traffic_chart>`_ setting. - -Adaptive size charts --------------------- - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/1.1/adaptive-chart.png - :align: center - -When configuring charts, it is possible to flag their unit -as ``adaptive_prefix``, this allows to make the charts more readable because -the units are shown in either `K`, `M`, `G` and `T` depending on -the size of each point, the summary values and Y axis are also resized. - -Example taken from the default configuration of the traffic chart: - -.. code-block:: python - - 'traffic': { - # other configurations for this chart - - # traffic measured in 'B' (bytes) - # unit B, KB, MB, GB, TB - 'unit': 'adaptive_prefix+B', - }, - - 'bandwidth': { - # adaptive unit for bandwidth related charts - # bandwidth measured in 'bps'(bits/sec) - # unit bps, Kbps, Mbps, Gbps, Tbps - 'unit': 'adaptive_prefix+bps', - }, - -Monitoring WiFi Sessions ------------------------- - -OpenWISP Monitoring maintains a record of WiFi sessions created by clients -joined to a radio of managed devices. The WiFi sessions are created -asynchronously from the monitoring data received from the device. - -You can filter both currently open sessions and past sessions by their -*start* or *stop* time or *organization* or *group* of the device clients -are connected to or even directly by a *device* name or ID. - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/wifi-session-changelist.png - :align: center - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/wifi-session-change.png - :align: center - -You can disable this feature by configuring -`OPENWISP_MONITORING_WIFI_SESSIONS_ENABLED <#openwisp_monitoring_wifi_sessions_enabled>`_ -setting. - -You can also view open WiFi sessions of a device directly from the device's change admin -under the "WiFi Sessions" tab. - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/device-wifi-session-inline.png - :align: center - -Scheduled deletion of WiFi sessions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -OpenWISP Monitoring provides a celery task to automatically delete -WiFi sessions older than a pre-configured number of days. In order to run this -task periodically, you will need to configure ``CELERY_BEAT_SCHEDULE`` setting as shown -in `setup instructions <#setup-integrate-in-an-existing-django-project>`_. - -The celery task takes only one argument, i.e. number of days. You can provide -any number of days in `args` key while configuring ``CELERY_BEAT_SCHEDULE`` setting. - -E.g., if you want WiFi Sessions older than 30 days to get deleted automatically, -then configure ``CELERY_BEAT_SCHEDULE`` as follows: - -.. code-block:: python - - CELERY_BEAT_SCHEDULE = { - 'delete_wifi_clients_and_sessions': { - 'task': 'openwisp_monitoring.monitoring.tasks.delete_wifi_clients_and_sessions', - 'schedule': timedelta(days=1), - 'args': (30,), # Here we have defined 30 instead of 180 as shown in setup instructions - }, - } - -Please refer to `"Periodic Tasks" section of Celery's documentation `_ -to learn more. - -Default Alerts / Notifications ------------------------------- - -+-------------------------------+------------------------------------------------------------------+ -| Notification Type | Use | -+-------------------------------+------------------------------------------------------------------+ -| ``threshold_crossed`` | Fires when a metric crosses the boundary defined in the | -| | threshold value of the alert settings. | -+-------------------------------+------------------------------------------------------------------+ -| ``threshold_recovery`` | Fires when a metric goes back within the expected range. | -+-------------------------------+------------------------------------------------------------------+ -| ``connection_is_working`` | Fires when the connection to a device is working. | -+-------------------------------+------------------------------------------------------------------+ -| ``connection_is_not_working`` | Fires when the connection (eg: SSH) to a device stops working | -| | (eg: credentials are outdated, management IP address is | -| | outdated, or device is not reachable). | -+-------------------------------+------------------------------------------------------------------+ - -Available Checks ----------------- - -Ping -~~~~ - -This check returns information on Ping Success Rate and RTT (Round trip time). -It creates charts like Ping Success Rate, Packet Loss and RTT. -These metrics are collected using the ``fping`` Linux program. -You may choose to disable auto creation of this check by setting -`OPENWISP_MONITORING_AUTO_PING <#OPENWISP_MONITORING_AUTO_PING>`_ to ``False``. - -You can change the default values used for ping checks using -`OPENWISP_MONITORING_PING_CHECK_CONFIG <#OPENWISP_MONITORING_PING_CHECK_CONFIG>`_ setting. - -Configuration applied -~~~~~~~~~~~~~~~~~~~~~ - -This check ensures that the `openwisp-config agent `_ -is running and applying configuration changes in a timely manner. -You may choose to disable auto creation of this check by using the -setting `OPENWISP_MONITORING_AUTO_DEVICE_CONFIG_CHECK <#OPENWISP_MONITORING_AUTO_DEVICE_CONFIG_CHECK>`_. - -This check runs periodically, but it is also triggered whenever the -configuration status of a device changes, this ensures the check reacts -quickly to events happening in the network and informs the user promptly -if there's anything that is not working as intended. - -Iperf3 -~~~~~~ - -This check provides network performance measurements such as maximum achievable bandwidth, -jitter, datagram loss etc of the device using `iperf3 utility `_. - -This check is **disabled by default**. You can enable auto creation of this check by setting the -`OPENWISP_MONITORING_AUTO_IPERF3 <#OPENWISP_MONITORING_AUTO_IPERF3>`_ to ``True``. - -You can also `add the iperf3 check -<#add-checks-and-alert-settings-from-the-device-page>`_ directly from the device page. - -It also supports tuning of various parameters. - -You can also change the parameters used for iperf3 checks (e.g. timing, port, username, -password, rsa_publc_key etc) using the `OPENWISP_MONITORING_IPERF3_CHECK_CONFIG -<#OPENWISP_MONITORING_IPERF3_CHECK_CONFIG>`_ setting. - -**Note:** When setting `OPENWISP_MONITORING_AUTO_IPERF3 <#OPENWISP_MONITORING_AUTO_IPERF3>`_ to ``True``, -you may need to update the `metric configuration <#add-checks-and-alert-settings-from-the-device-page>`_ -to enable alerts for the iperf3 check. - -Iperf3 Check Usage Instructions -------------------------------- - -1. Make sure iperf3 is installed on the device -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Register your device to OpenWISP and make sure the `iperf3 openwrt package -`_ is installed on the device, -eg: - -.. code-block:: shell - - opkg install iperf3 # if using without authentication - opkg install iperf3-ssl # if using with authentication (read below for more info) - -2. Ensure SSH access from OpenWISP is enabled on your devices -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Follow the steps in `"How to configure push updates" section of the -OpenWISP documentation -`_ -to allow SSH access to you device from OpenWISP. - -**Note:** Make sure device connection is enabled -& working with right update strategy i.e. ``OpenWRT SSH``. - -.. image:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/1.1/enable-openwrt-ssh.png - :alt: Enable ssh access from openwisp to device - :align: center - -3. Set up and configure Iperf3 server settings -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -After having deployed your Iperf3 servers, you need to -configure the iperf3 settings on the django side of OpenWISP, -see the `test project settings for reference -`_. - -The host can be specified by hostname, IPv4 literal, or IPv6 literal. -Example: - -.. code-block:: python - - OPENWISP_MONITORING_IPERF3_CHECK_CONFIG = { - # 'org_pk' : {'host' : [], 'client_options' : {}} - 'a9734710-db30-46b0-a2fc-01f01046fe4f': { - # Some public iperf3 servers - # https://iperf.fr/iperf-servers.php#public-servers - 'host': ['iperf3.openwisp.io', '2001:db8::1', '192.168.5.2'], - 'client_options': { - 'port': 5209, - 'udp': {'bitrate': '30M'}, - 'tcp': {'bitrate': '0'}, - }, - }, - # another org - 'b9734710-db30-46b0-a2fc-01f01046fe4f': { - # available iperf3 servers - 'host': ['iperf3.openwisp2.io', '192.168.5.3'], - 'client_options': { - 'port': 5207, - 'udp': {'bitrate': '50M'}, - 'tcp': {'bitrate': '20M'}, - }, - }, - } - -**Note:** If an organization has more than one iperf3 server configured, then it enables -the iperf3 checks to run concurrently on different devices. If all of the available servers -are busy, then it will add the check back in the queue. - -The celery-beat configuration for the iperf3 check needs to be added too: - -.. code-block:: python - - from celery.schedules import crontab - - # Celery TIME_ZONE should be equal to django TIME_ZONE - # In order to schedule run_iperf3_checks on the correct time intervals - CELERY_TIMEZONE = TIME_ZONE - CELERY_BEAT_SCHEDULE = { - # Other celery beat configurations - # Celery beat configuration for iperf3 check - 'run_iperf3_checks': { - 'task': 'openwisp_monitoring.check.tasks.run_checks', - # https://docs.celeryq.dev/en/latest/userguide/periodic-tasks.html#crontab-schedules - # Executes check every 5 mins from 00:00 AM to 6:00 AM (night) - 'schedule': crontab(minute='*/5', hour='0-6'), - # Iperf3 check path - 'args': (['openwisp_monitoring.check.classes.Iperf3'],), - 'relative': True, - } - } - -Once the changes are saved, you will need to restart all the processes. - -**Note:** We recommended to configure this check to run in non peak -traffic times to not interfere with standard traffic. - -4. Run the check -~~~~~~~~~~~~~~~~ - -This should happen automatically if you have celery-beat correctly -configured and running in the background. -For testing purposes, you can run this check manually using the -`run_checks <#run_checks>`_ command. - -After that, you should see the iperf3 network measurements charts. - -.. image:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/1.1/iperf3-charts.png - :alt: Iperf3 network measurement charts - -Iperf3 check parameters -~~~~~~~~~~~~~~~~~~~~~~~ - -Currently, iperf3 check supports the following parameters: - -+-----------------------+----------+--------------------------------------------------------------------+ -| **Parameter** | **Type** | **Default Value** | -+-----------------------+----------+--------------------------------------------------------------------+ -|``host`` | ``list`` | ``[]`` | -+-----------------------+----------+--------------------------------------------------------------------+ -|``username`` | ``str`` | ``''`` | -+-----------------------+----------+--------------------------------------------------------------------+ -|``password`` | ``str`` | ``''`` | -+-----------------------+----------+--------------------------------------------------------------------+ -|``rsa_public_key`` | ``str`` | ``''`` | -+-----------------------+----------+--------------------------------------------------------------------+ -|``client_options`` | +---------------------+----------+------------------------------------------+ | -| | | **Parameters** | **Type** | **Default Value** | | -| | +---------------------+----------+------------------------------------------+ | -| | | ``port`` | ``int`` | ``5201`` | | -| | +---------------------+----------+------------------------------------------+ | -| | | ``time`` | ``int`` | ``10`` | | -| | +---------------------+----------+------------------------------------------+ | -| | | ``bytes`` | ``str`` | ``''`` | | -| | +---------------------+----------+------------------------------------------+ | -| | | ``blockcount`` | ``str`` | ``''`` | | -| | +---------------------+----------+------------------------------------------+ | -| | | ``window`` | ``str`` | ``0`` | | -| | +---------------------+----------+------------------------------------------+ | -| | | ``parallel`` | ``int`` | ``1`` | | -| | +---------------------+----------+------------------------------------------+ | -| | | ``reverse`` | ``bool`` | ``False`` | | -| | +---------------------+----------+------------------------------------------+ | -| | | ``bidirectional`` | ``bool`` | ``False`` | | -| | +---------------------+----------+------------------------------------------+ | -| | | ``connect_timeout`` | ``int`` | ``1000`` | | -| | +---------------------+----------+------------------------------------------+ | -| | | ``tcp`` | +----------------+----------+---------------------+ | | -| | | | | **Parameters** | **Type** | **Default Value** | | | -| | | | +----------------+----------+---------------------+ | | -| | | | |``bitrate`` | ``str`` | ``0`` | | | -| | | | +----------------+----------+---------------------+ | | -| | | | |``length`` | ``str`` | ``128K`` | | | -| | | | +----------------+----------+---------------------+ | | -| | +---------------------+-----------------------------------------------------+ | -| | | ``udp`` | +----------------+----------+---------------------+ | | -| | | | | **Parameters** | **Type** | **Default Value** | | | -| | | | +----------------+----------+---------------------+ | | -| | | | |``bitrate`` | ``str`` | ``30M`` | | | -| | | | +----------------+----------+---------------------+ | | -| | | | |``length`` | ``str`` | ``0`` | | | -| | | | +----------------+----------+---------------------+ | | -| | +---------------------+-----------------------------------------------------+ | -+-----------------------+-------------------------------------------------------------------------------+ - -To learn how to use these parameters, please see the -`iperf3 check configuration example <#OPENWISP_MONITORING_IPERF3_CHECK_CONFIG>`_. - -Visit the `official documentation `_ -to learn more about the iperf3 parameters. - -Iperf3 authentication -~~~~~~~~~~~~~~~~~~~~~ - -By default iperf3 check runs without any kind of **authentication**, -in this section we will explain how to configure **RSA authentication** -between the **client** and the **server** to restrict connections -to authenticated clients. - -Server side -########### - -1. Generate RSA keypair -^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: shell - - openssl genrsa -des3 -out private.pem 2048 - openssl rsa -in private.pem -outform PEM -pubout -out public_key.pem - openssl rsa -in private.pem -out private_key.pem -outform PEM - -After running the commands mentioned above, the public key will be stored in -``public_key.pem`` which will be used in **rsa_public_key** parameter -in `OPENWISP_MONITORING_IPERF3_CHECK_CONFIG -<#OPENWISP_MONITORING_IPERF3_CHECK_CONFIG>`_ -and the private key will be contained in the file ``private_key.pem`` -which will be used with **--rsa-private-key-path** command option when -starting the iperf3 server. - -2. Create user credentials -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: shell - - USER=iperfuser PASSWD=iperfpass - echo -n "{$USER}$PASSWD" | sha256sum | awk '{ print $1 }' - ---- - ee17a7f98cc87a6424fb52682396b2b6c058e9ab70e946188faa0714905771d7 #This is the hash of "iperfuser" - -Add the above hash with username in ``credentials.csv`` - -.. code-block:: shell - - # file format: username,sha256 - iperfuser,ee17a7f98cc87a6424fb52682396b2b6c058e9ab70e946188faa0714905771d7 - -3. Now start the iperf3 server with auth options -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: shell - - iperf3 -s --rsa-private-key-path ./private_key.pem --authorized-users-path ./credentials.csv - -Client side (OpenWrt device) -############################ - -1. Install iperf3-ssl -^^^^^^^^^^^^^^^^^^^^^ - -Install the `iperf3-ssl openwrt package -`_ -instead of the normal -`iperf3 openwrt package `_ -because the latter comes without support for authentication. - -You may also check your installed **iperf3 openwrt package** features: - -.. code-block:: shell - - root@vm-openwrt:~ iperf3 -v - iperf 3.7 (cJSON 1.5.2) - Linux vm-openwrt 4.14.171 #0 SMP Thu Feb 27 21:05:12 2020 x86_64 - Optional features available: CPU affinity setting, IPv6 flow label, TCP congestion algorithm setting, - sendfile / zerocopy, socket pacing, authentication # contains 'authentication' - -2. Configure iperf3 check auth parameters -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Now, add the following iperf3 authentication parameters -to `OPENWISP_MONITORING_IPERF3_CHECK_CONFIG -<#OPENWISP_MONITORING_IPERF3_CHECK_CONFIG>`_ -in the settings: - -.. code-block:: python - - OPENWISP_MONITORING_IPERF3_CHECK_CONFIG = { - 'a9734710-db30-46b0-a2fc-01f01046fe4f': { - 'host': ['iperf1.openwisp.io', 'iperf2.openwisp.io', '192.168.5.2'], - # All three parameters (username, password, rsa_publc_key) - # are required for iperf3 authentication - 'username': 'iperfuser', - 'password': 'iperfpass', - # Add RSA public key without any headers - # ie. -----BEGIN PUBLIC KEY-----, -----BEGIN END KEY----- - 'rsa_public_key': ( - """ - MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwuEm+iYrfSWJOupy6X3N - dxZvUCxvmoL3uoGAs0O0Y32unUQrwcTIxudy38JSuCccD+k2Rf8S4WuZSiTxaoea - 6Du99YQGVZeY67uJ21SWFqWU+w6ONUj3TrNNWoICN7BXGLE2BbSBz9YaXefE3aqw - GhEjQz364Itwm425vHn2MntSp0weWb4hUCjQUyyooRXPrFUGBOuY+VvAvMyAG4Uk - msapnWnBSxXt7Tbb++A5XbOMdM2mwNYDEtkD5ksC/x3EVBrI9FvENsH9+u/8J9Mf - 2oPl4MnlCMY86MQypkeUn7eVWfDnseNky7TyC0/IgCXve/iaydCCFdkjyo1MTAA4 - BQIDAQAB - """ - ), - 'client_options': { - 'port': 5209, - 'udp': {'bitrate': '20M'}, - 'tcp': {'bitrate': '0'}, - }, - } - } - -Adding Checks and Alert settings from the device page ------------------------------------------------------ - -We can add checks and define alert settings directly from the **device page**. - -To add a check, you just need to select an available **check type** as shown below: - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/1.1/device-inline-check.png - :align: center - -The following example shows how to use the -`OPENWISP_MONITORING_METRICS setting <#openwisp_monitoring_metrics>`_ -to reconfigure the system for `iperf3 check <#iperf3-1>`_ to send an alert if -the measured **TCP bandwidth** has been less than **10 Mbit/s** for more than **2 days**. - -1. By default, `Iperf3 checks <#iperf3-1>`_ come with default alert settings, -but it is easy to customize alert settings through the device page as shown below: - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/1.1/device-inline-alertsettings.png - :align: center - -2. Now, add the following notification configuration to send an alert for **TCP bandwidth**: - -.. code-block:: python - - # Main project settings.py - from django.utils.translation import gettext_lazy as _ - - OPENWISP_MONITORING_METRICS = { - 'iperf3': { - 'notification': { - 'problem': { - 'verbose_name': 'Iperf3 PROBLEM', - 'verb': _('Iperf3 bandwidth is less than normal value'), - 'level': 'warning', - 'email_subject': _( - '[{site.name}] PROBLEM: {notification.target} {notification.verb}' - ), - 'message': _( - 'The device [{notification.target}]({notification.target_link}) ' - '{notification.verb}.' - ), - }, - 'recovery': { - 'verbose_name': 'Iperf3 RECOVERY', - 'verb': _('Iperf3 bandwidth now back to normal'), - 'level': 'info', - 'email_subject': _( - '[{site.name}] RECOVERY: {notification.target} {notification.verb}' - ), - 'message': _( - 'The device [{notification.target}]({notification.target_link}) ' - '{notification.verb}.' - ), - }, - }, - }, - } - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/1.1/alert_field_warn.png - :align: center - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/1.1/alert_field_info.png - :align: center - -**Note:** To access the features described above, the user must have permissions for ``Check`` and ``AlertSetting`` inlines, -these permissions are included by default in the "Administrator" and "Operator" groups and are shown in the screenshot below. - -.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/1.1/inline-permissions.png - :align: center - -Settings --------- - -``TIMESERIES_DATABASE`` -~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+-----------+ -| **type**: | ``str`` | -+--------------+-----------+ -| **default**: | see below | -+--------------+-----------+ - -.. code-block:: python - - TIMESERIES_DATABASE = { - 'BACKEND': 'openwisp_monitoring.db.backends.influxdb', - 'USER': 'openwisp', - 'PASSWORD': 'openwisp', - 'NAME': 'openwisp2', - 'HOST': 'localhost', - 'PORT': '8086', - 'OPTIONS': { - 'udp_writes': False, - 'udp_port': 8089, - } - } - -The following table describes all keys available in ``TIMESERIES_DATABASE`` -setting: - -+---------------+--------------------------------------------------------------------------------------+ -| **Key** | ``Description`` | -+---------------+--------------------------------------------------------------------------------------+ -| ``BACKEND`` | The timeseries database backend to use. You can select one of the backends | -| | located in ``openwisp_monitoring.db.backends`` | -+---------------+--------------------------------------------------------------------------------------+ -| ``USER`` | User for logging into the timeseries database | -+---------------+--------------------------------------------------------------------------------------+ -| ``PASSWORD`` | Password of the timeseries database user | -+---------------+--------------------------------------------------------------------------------------+ -| ``NAME`` | Name of the timeseries database | -+---------------+--------------------------------------------------------------------------------------+ -| ``HOST`` | IP address/hostname of machine where the timeseries database is running | -+---------------+--------------------------------------------------------------------------------------+ -| ``PORT`` | Port for connecting to the timeseries database | -+---------------+--------------------------------------------------------------------------------------+ -| ``OPTIONS`` | These settings depends on the timeseries backend: | -| | | -| | +-----------------+----------------------------------------------------------------+ | -| | | ``udp_writes`` | Whether to use UDP for writing data to the timeseries database | | -| | +-----------------+----------------------------------------------------------------+ | -| | | ``udp_port`` | Timeseries database port for writing data using UDP | | -| | +-----------------+----------------------------------------------------------------+ | -+---------------+--------------------------------------------------------------------------------------+ - -**Note:** UDP packets can have a maximum size of 64KB. When using UDP for writing timeseries -data, if the size of the data exceeds 64KB, TCP mode will be used instead. - -**Note:** If you want to use the ``openwisp_monitoring.db.backends.influxdb`` backend -with UDP writes enabled, then you need to enable two different ports for UDP -(each for different retention policy) in your InfluxDB configuration. The UDP configuration -section of your InfluxDB should look similar to the following: - -.. code-block:: text - - # For writing data with the "default" retention policy - [[udp]] - enabled = true - bind-address = "127.0.0.1:8089" - database = "openwisp2" - - # For writing data with the "short" retention policy - [[udp]] - enabled = true - bind-address = "127.0.0.1:8090" - database = "openwisp2" - retention-policy = 'short' - -If you are using `ansible-openwisp2 `_ -for deploying OpenWISP, you can set the ``influxdb_udp_mode`` ansible variable to ``true`` -in your playbook, this will make the ansible role automatically configure the InfluxDB UDP listeners. -You can refer to the `ansible-ow-influxdb's `_ -(a dependency of ansible-openwisp2) documentation to learn more. - -``OPENWISP_MONITORING_DEFAULT_RETENTION_POLICY`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+--------------------------+ -| **type**: | ``str`` | -+--------------+--------------------------+ -| **default**: | ``26280h0m0s`` (3 years) | -+--------------+--------------------------+ - -The default retention policy that applies to the timeseries data. - -``OPENWISP_MONITORING_SHORT_RETENTION_POLICY`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+-------------+ -| **type**: | ``str`` | -+--------------+-------------+ -| **default**: | ``24h0m0s`` | -+--------------+-------------+ - -The default retention policy used to store raw device data. - -This data is only used to assess the recent status of devices, keeping -it for a long time would not add much benefit and would cost a lot more -in terms of disk space. - -``OPENWISP_MONITORING_AUTO_PING`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+-------------+ -| **type**: | ``bool`` | -+--------------+-------------+ -| **default**: | ``True`` | -+--------------+-------------+ - -Whether ping checks are created automatically for devices. - -``OPENWISP_MONITORING_PING_CHECK_CONFIG`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+-------------+ -| **type**: | ``dict`` | -+--------------+-------------+ -| **default**: | ``{}`` | -+--------------+-------------+ - -This setting allows to override the default ping check configuration defined in -``openwisp_monitoring.check.classes.ping.DEFAULT_PING_CHECK_CONFIG``. - -For example, if you want to change only the **timeout** of -``ping`` you can use: - -.. code-block:: python - - OPENWISP_MONITORING_PING_CHECK_CONFIG = { - 'timeout': { - 'default': 1000, - }, - } - -If you are overriding the default value for any parameter -beyond the maximum or minimum value defined in -``openwisp_monitoring.check.classes.ping.DEFAULT_PING_CHECK_CONFIG``, -you will also need to override the ``maximum`` or ``minimum`` fields -as following: - -.. code-block:: python - - OPENWISP_MONITORING_PING_CHECK_CONFIG = { - 'timeout': { - 'default': 2000, - 'minimum': 1500, - 'maximum': 2500, - }, - } - -**Note:** Above ``maximum`` and ``minimum`` values are only used for -validating custom parameters of a ``Check`` object. - -``OPENWISP_MONITORING_AUTO_DEVICE_CONFIG_CHECK`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+-------------+ -| **type**: | ``bool`` | -+--------------+-------------+ -| **default**: | ``True`` | -+--------------+-------------+ - -This setting allows you to choose whether `config_applied <#configuration-applied>`_ checks should be -created automatically for newly registered devices. It's enabled by default. - -``OPENWISP_MONITORING_CONFIG_CHECK_INTERVAL`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+-------------+ -| **type**: | ``int`` | -+--------------+-------------+ -| **default**: | ``5`` | -+--------------+-------------+ - -This setting allows you to configure the config check interval used by -`config_applied <#configuration-applied>`_. By default it is set to 5 minutes. - -``OPENWISP_MONITORING_AUTO_IPERF3`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+-------------+ -| **type**: | ``bool`` | -+--------------+-------------+ -| **default**: | ``False`` | -+--------------+-------------+ - -This setting allows you to choose whether `iperf3 <#iperf3-1>`_ checks should be -created automatically for newly registered devices. It's disabled by default. - -``OPENWISP_MONITORING_IPERF3_CHECK_CONFIG`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+-------------+ -| **type**: | ``dict`` | -+--------------+-------------+ -| **default**: | ``{}`` | -+--------------+-------------+ - -This setting allows to override the default iperf3 check configuration defined in -``openwisp_monitoring.check.classes.iperf3.DEFAULT_IPERF3_CHECK_CONFIG``. - -For example, you can change the values of `supported iperf3 check parameters <#iperf3-check-parameters>`_. - -.. code-block:: python - - OPENWISP_MONITORING_IPERF3_CHECK_CONFIG = { - # 'org_pk' : {'host' : [], 'client_options' : {}} - 'a9734710-db30-46b0-a2fc-01f01046fe4f': { - # Some public iperf3 servers - # https://iperf.fr/iperf-servers.php#public-servers - 'host': ['iperf3.openwisp.io', '2001:db8::1', '192.168.5.2'], - 'client_options': { - 'port': 6209, - # Number of parallel client streams to run - # note that iperf3 is single threaded - # so if you are CPU bound this will not - # yield higher throughput - 'parallel': 5, - # Set the connect_timeout (in milliseconds) for establishing - # the initial control connection to the server, the lower the value - # the faster the down iperf3 server will be detected (ex. 1000 ms (1 sec)) - 'connect_timeout': 1000, - # Window size / socket buffer size - 'window': '300K', - # Only one reverse condition can be chosen, - # reverse or bidirectional - 'reverse': True, - # Only one test end condition can be chosen, - # time, bytes or blockcount - 'blockcount': '1K', - 'udp': {'bitrate': '50M', 'length': '1460K'}, - 'tcp': {'bitrate': '20M', 'length': '256K'}, - }, - } - } - -``OPENWISP_MONITORING_IPERF3_CHECK_DELETE_RSA_KEY`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+-------------------------------+ -| **type**: | ``bool`` | -+--------------+-------------------------------+ -| **default**: | ``True`` | -+--------------+-------------------------------+ - -This setting allows you to set whether -`iperf3 check RSA public key <#configure-iperf3-check-for-authentication>`_ -will be deleted after successful completion of the check or not. - -``OPENWISP_MONITORING_IPERF3_CHECK_LOCK_EXPIRE`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+-------------------------------+ -| **type**: | ``int`` | -+--------------+-------------------------------+ -| **default**: | ``600`` | -+--------------+-------------------------------+ - -This setting allows you to set a cache lock expiration time for the iperf3 check when -running on multiple servers. Make sure it is always greater than the total iperf3 check -time, i.e. greater than the TCP + UDP test time. By default, it is set to **600 seconds (10 mins)**. - -``OPENWISP_MONITORING_AUTO_CHARTS`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+-----------------------------------------------------------------+ -| **type**: | ``list`` | -+--------------+-----------------------------------------------------------------+ -| **default**: | ``('traffic', 'wifi_clients', 'uptime', 'packet_loss', 'rtt')`` | -+--------------+-----------------------------------------------------------------+ - -Automatically created charts. - -``OPENWISP_MONITORING_CRITICAL_DEVICE_METRICS`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+-----------------------------------------------------------------+ -| **type**: | ``list`` of ``dict`` objects | -+--------------+-----------------------------------------------------------------+ -| **default**: | ``[{'key': 'ping', 'field_name': 'reachable'}]`` | -+--------------+-----------------------------------------------------------------+ - -Device metrics that are considered critical: - -when a value crosses the boundary defined in the "threshold value" field -of the alert settings related to one of these metric types, the health status -of the device related to the metric moves into ``CRITICAL``. - -By default, if devices are not reachable by pings they are flagged as ``CRITICAL``. - -``OPENWISP_MONITORING_HEALTH_STATUS_LABELS`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+--------------------------------------------------------------------------------------+ -| **type**: | ``dict`` | -+--------------+--------------------------------------------------------------------------------------+ -| **default**: | ``{'unknown': 'unknown', 'ok': 'ok', 'problem': 'problem', 'critical': 'critical'}`` | -+--------------+--------------------------------------------------------------------------------------+ - -This setting allows to change the health status labels, for example, if we -want to use ``online`` instead of ``ok`` and ``offline`` instead of ``critical``, -you can use the following configuration: - -.. code-block:: python - - OPENWISP_MONITORING_HEALTH_STATUS_LABELS = { - 'ok': 'online', - 'problem': 'problem', - 'critical': 'offline' - } - -``OPENWISP_MONITORING_WIFI_SESSIONS_ENABLED`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+-------------+ -| **type**: | ``bool`` | -+--------------+-------------+ -| **default**: | ``True`` | -+--------------+-------------+ - -Setting this to ``False`` will disable `Monitoring Wifi Sessions <#monitoring-wifi-sessions>`_ -feature. - -``OPENWISP_MONITORING_MANAGEMENT_IP_ONLY`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+-------------+ -| **type**: | ``bool`` | -+--------------+-------------+ -| **default**: | ``True`` | -+--------------+-------------+ - -By default, only the management IP will be used to perform active checks to -the devices. - -If the devices are connecting to your OpenWISP instance using a shared layer2 -network, hence the OpenWSP server can reach the devices using the ``last_ip`` -field, you can set this to ``False``. - -**Note:** If this setting is not configured, it will fallback to the value of -`OPENWISP_CONTROLLER_MANAGEMENT_IP_ONLY setting -`_. -If ``OPENWISP_CONTROLLER_MANAGEMENT_IP_ONLY`` also not configured, -then it will fallback to ``True``. - -``OPENWISP_MONITORING_DEVICE_RECOVERY_DETECTION`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+-------------+ -| **type**: | ``bool`` | -+--------------+-------------+ -| **default**: | ``True`` | -+--------------+-------------+ - -When device recovery detection is enabled, recoveries are discovered as soon as -a device contacts the openwisp system again (eg: to get the configuration checksum -or to send monitoring metrics). - -This feature is enabled by default. - -If you use OpenVPN as the management VPN, you may want to check out a similar -integration built in **openwisp-network-topology**: when the status of an OpenVPN link -changes (detected by monitoring the status information of OpenVPN), the -network topology module will trigger the monitoring checks. -For more information see: -`Network Topology Device Integration `_ - -``OPENWISP_MONITORING_MAC_VENDOR_DETECTION`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+-------------+ -| **type**: | ``bool`` | -+--------------+-------------+ -| **default**: | ``True`` | -+--------------+-------------+ - -Indicates whether mac addresses will be complemented with hardware vendor -information by performing lookups on the OUI -(Organization Unique Identifier) table. - -This feature is enabled by default. - -``OPENWISP_MONITORING_WRITE_RETRY_OPTIONS`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+-----------+ -| **type**: | ``dict`` | -+--------------+-----------+ -| **default**: | see below | -+--------------+-----------+ - -.. code-block:: python - - # default value of OPENWISP_MONITORING_RETRY_OPTIONS: - - dict( - max_retries=None, - retry_backoff=True, - retry_backoff_max=600, - retry_jitter=True, - ) - -Retry settings for recoverable failures during metric writes. - -By default if a metric write fails (eg: due to excessive load on timeseries database at that moment) -then the operation will be retried indefinitely with an exponential random backoff and a maximum delay of 10 minutes. - -This feature makes the monitoring system resilient to temporary outages and helps to prevent data loss. - -For more information regarding these settings, consult the `celery documentation -regarding automatic retries for known errors -`_. - -**Note:** The retry mechanism does not work when using ``UDP`` for writing -data to the timeseries database. It is due to the nature of ``UDP`` protocol -which does not acknowledge receipt of data packets. - -``OPENWISP_MONITORING_TIMESERIES_RETRY_OPTIONS`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+-----------+ -| **type**: | ``dict`` | -+--------------+-----------+ -| **default**: | see below | -+--------------+-----------+ - -.. code-block:: python - - # default value of OPENWISP_MONITORING_RETRY_OPTIONS: - - dict( - max_retries=6, - delay=2 - ) - -On busy systems, communication with the timeseries DB can occasionally fail. -The timeseries DB backend will retry on any exception according to these settings. -The delay kicks in only after the third consecutive attempt. - -This setting shall not be confused with ``OPENWISP_MONITORING_WRITE_RETRY_OPTIONS``, -which is used to configure the infinite retrying of the celery task which writes -metric data to the timeseries DB, while ``OPENWISP_MONITORING_TIMESERIES_RETRY_OPTIONS`` -deals with any other read/write operation on the timeseries DB which may fail. - -However these retries are not handled by celery but are simple python loops, -which will eventually give up if a problem persists. - -``OPENWISP_MONITORING_TIMESERIES_RETRY_DELAY`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+-------------+ -| **type**: | ``int`` | -+--------------+-------------+ -| **default**: | ``2`` | -+--------------+-------------+ - -This settings allow you to configure the retry delay time (in seconds) after 3 failed attempt in timeseries database. - -This retry setting is used in retry mechanism to make the requests to the timeseries database resilient. - -This setting is independent of celery retry settings. - -``OPENWISP_MONITORING_DASHBOARD_MAP`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+-------------+ -| **type**: | ``bool`` | -+--------------+-------------+ -| **default**: | ``True`` | -+--------------+-------------+ - -Whether the geographic map in the dashboard is enabled or not. -This feature provides a geographic map which shows the locations -which have devices installed in and provides a visual representation -of the monitoring status of the devices, this allows to get -an overview of the network at glance. - -This feature is enabled by default and depends on the setting -``OPENWISP_ADMIN_DASHBOARD_ENABLED`` from -`openwisp-utils `__ -being set to ``True`` (which is the default). - -You can turn this off if you do not use the geographic features -of OpenWISP. - -``OPENWISP_MONITORING_DASHBOARD_TRAFFIC_CHART`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+--------------------------------------------+ -| **type**: | ``dict`` | -+--------------+--------------------------------------------+ -| **default**: | ``{'__all__': ['wan', 'eth1', 'eth0.2']}`` | -+--------------+--------------------------------------------+ - -This settings allows to configure the interfaces which should -be included in the **General Traffic** chart in the admin dashboard. - -This setting should be defined in the following format: - -.. code-block::python - - OPENWISP_MONITORING_DASHBOARD_TRAFFIC_CHART = { - '': [''] - } - -E.g., if you want the **General Traffic** chart to show data from -two interfaces for an organization, you need to configure this setting -as follows: - -.. code-block::python - - OPENWISP_MONITORING_DASHBOARD_TRAFFIC_CHART = { - # organization uuid - 'f9601bbd-b6d5-4704-85e3-5851894437bf': ['eth1', 'eth2'] - } - -**Note**: The value of ``__all__`` key is used if an organization -does not have list of interfaces defined in ``OPENWISP_MONITORING_DASHBOARD_TRAFFIC_CHART``. - -**Note**: If a user can manage more than one organization (e.g. superusers), -then the **General Traffic** chart will always show data from interfaces -of ``__all__`` configuration. - -``OPENWISP_MONITORING_METRICS`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+-------------+ -| **type**: | ``dict`` | -+--------------+-------------+ -| **default**: | ``{}`` | -+--------------+-------------+ - -This setting allows to define additional metric configuration or to override -the default metric configuration defined in -``openwisp_monitoring.monitoring.configuration.DEFAULT_METRICS``. - -For example, if you want to change only the **field_name** of -``clients`` metric to ``wifi_clients`` you can use: - -.. code-block:: python - - from django.utils.translation import gettext_lazy as _ - - OPENWISP_MONITORING_METRICS = { - 'clients': { - 'label': _('WiFi clients'), - 'field_name': 'wifi_clients', - }, - } - -For example, if you want to change only the default alert settings of -``memory`` metric you can use: - -.. code-block:: python - - OPENWISP_MONITORING_METRICS = { - 'memory': { - 'alert_settings': {'threshold': 75, 'tolerance': 10} - }, - } - -For example, if you want to change only the notification of -``config_applied`` metric you can use: - -.. code-block:: python - - from django.utils.translation import gettext_lazy as _ - - OPENWISP_MONITORING_METRICS = { - 'config_applied': { - 'notification': { - 'problem': { - 'verbose_name': 'Configuration PROBLEM', - 'verb': _('has not been applied'), - 'email_subject': _( - '[{site.name}] PROBLEM: {notification.target} configuration ' - 'status issue' - ), - 'message': _( - 'The configuration for device [{notification.target}]' - '({notification.target_link}) {notification.verb} in a timely manner.' - ), - }, - 'recovery': { - 'verbose_name': 'Configuration RECOVERY', - 'verb': _('configuration has been applied again'), - 'email_subject': _( - '[{site.name}] RECOVERY: {notification.target} {notification.verb} ' - 'successfully' - ), - 'message': _( - 'The device [{notification.target}]({notification.target_link}) ' - '{notification.verb} successfully.' - ), - }, - }, - }, - } - -Or if you want to define a new metric configuration, which you can then -call in your custom code (eg: a custom check class), you can do so as follows: - -.. code-block:: python - - from django.utils.translation import gettext_lazy as _ - - OPENWISP_MONITORING_METRICS = { - 'top_fields_mean': { - 'name': 'Top Fields Mean', - 'key': '{key}', - 'field_name': '{field_name}', - 'label': '_(Top fields mean)', - 'related_fields': ['field1', 'field2', 'field3'], - }, - } - -``OPENWISP_MONITORING_CHARTS`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+-------------+ -| **type**: | ``dict`` | -+--------------+-------------+ -| **default**: | ``{}`` | -+--------------+-------------+ - -This setting allows to define additional charts or to override -the default chart configuration defined in -``openwisp_monitoring.monitoring.configuration.DEFAULT_CHARTS``. - -In the following example, we modify the description of the traffic chart: - -.. code-block:: python - - OPENWISP_MONITORING_CHARTS = { - 'traffic': { - 'description': ( - 'Network traffic, download and upload, measured on ' - 'the interface "{metric.key}", custom message here.' - ), - } - } - -Or if you want to define a new chart configuration, which you can then -call in your custom code (eg: a custom check class), you can do so as follows: - -.. code-block:: python - - from django.utils.translation import gettext_lazy as _ - - OPENWISP_MONITORING_CHARTS = { - 'ram': { - 'type': 'line', - 'title': 'RAM usage', - 'description': 'RAM usage', - 'unit': 'bytes', - 'order': 100, - 'query': { - 'influxdb': ( - "SELECT MEAN(total) AS total, MEAN(free) AS free, " - "MEAN(buffered) AS buffered FROM {key} WHERE time >= '{time}' AND " - "content_type = '{content_type}' AND object_id = '{object_id}' " - "GROUP BY time(1d)" - ) - }, - } - } - -In case you just want to change the colors used in a chart here's how to do it: - -.. code-block:: python - - OPENWISP_MONITORING_CHARTS = { - 'traffic': { - 'colors': ['#000000', '#cccccc', '#111111'] - } - } - -``OPENWISP_MONITORING_DEFAULT_CHART_TIME`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+---------------------+---------------------------------------------+ -| **type**: | ``str`` | -+---------------------+---------------------------------------------+ -| **default**: | ``7d`` | -+---------------------+---------------------------------------------+ -| **possible values** | ``1d``, ``3d``, ``7d``, ``30d`` or ``365d`` | -+---------------------+---------------------------------------------+ - -Allows to set the default time period of the time series charts. - -``OPENWISP_MONITORING_AUTO_CLEAR_MANAGEMENT_IP`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+-------------+ -| **type**: | ``bool`` | -+--------------+-------------+ -| **default**: | ``True`` | -+--------------+-------------+ - -This setting allows you to automatically clear management_ip of a device -when it goes offline. It is enabled by default. - -``OPENWISP_MONITORING_API_URLCONF`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+-------------+ -| **type**: | ``string`` | -+--------------+-------------+ -| **default**: | ``None`` | -+--------------+-------------+ - -Changes the urlconf option of django urls to point the monitoring API -urls to another installed module, example, ``myapp.urls``. -(Useful when you have a seperate API instance.) - -``OPENWISP_MONITORING_API_BASEURL`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+-------------+ -| **type**: | ``string`` | -+--------------+-------------+ -| **default**: | ``None`` | -+--------------+-------------+ - -If you have a seperate server for API of openwisp-monitoring on a different -domain, you can use this option to change the base of the url, this will -enable you to point all the API urls to your openwisp-monitoring API server's -domain, example: ``https://mymonitoring.myapp.com``. - -``OPENWISP_MONITORING_CACHE_TIMEOUT`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------+----------------------------------+ -| **type**: | ``int`` | -+--------------+----------------------------------+ -| **default**: | ``86400`` (24 hours in seconds) | -+--------------+----------------------------------+ - -This setting allows to configure timeout (in seconds) for monitoring data cache. - -Registering / Unregistering Metric Configuration ------------------------------------------------- - -**OpenWISP Monitoring** provides registering and unregistering metric configuration through utility functions -``openwisp_monitoring.monitoring.configuration.register_metric`` and ``openwisp_monitoring.monitoring.configuration.unregister_metric``. -Using these functions you can register or unregister metric configurations from anywhere in your code. - -``register_metric`` -~~~~~~~~~~~~~~~~~~~ - -This function is used to register a new metric configuration from anywhere in your code. - -+--------------------------+------------------------------------------------------+ -| **Parameter** | **Description** | -+--------------------------+------------------------------------------------------+ -| **metric_name**: | A ``str`` defining name of the metric configuration. | -+--------------------------+------------------------------------------------------+ -|**metric_configuration**: | A ``dict`` defining configuration of the metric. | -+--------------------------+------------------------------------------------------+ - -An example usage has been shown below. - -.. code-block:: python - - from django.utils.translation import gettext_lazy as _ - from openwisp_monitoring.monitoring.configuration import register_metric - - # Define configuration of your metric - metric_config = { - 'label': _('Ping'), - 'name': 'Ping', - 'key': 'ping', - 'field_name': 'reachable', - 'related_fields': ['loss', 'rtt_min', 'rtt_max', 'rtt_avg'], - 'charts': { - 'uptime': { - 'type': 'bar', - 'title': _('Ping Success Rate'), - 'description': _( - 'A value of 100% means reachable, 0% means unreachable, values in ' - 'between 0% and 100% indicate the average reachability in the ' - 'period observed. Obtained with the fping linux program.' - ), - 'summary_labels': [_('Average Ping Success Rate')], - 'unit': '%', - 'order': 200, - 'colorscale': { - 'max': 100, - 'min': 0, - 'label': _('Rate'), - 'scale': [ - [[0, '#c13000'], - [0.1,'cb7222'], - [0.5,'#deed0e'], - [0.9, '#7db201'], - [1, '#498b26']], - ], - 'map': [ - [100, '#498b26', _('Flawless')], - [90, '#7db201', _('Mostly Reachable')], - [50, '#deed0e', _('Partly Reachable')], - [10, '#cb7222', _('Mostly Unreachable')], - [None, '#c13000', _('Unreachable')], - ], - 'fixed_value': 100, - }, - 'query': chart_query['uptime'], - }, - 'packet_loss': { - 'type': 'bar', - 'title': _('Packet loss'), - 'description': _( - 'Indicates the percentage of lost packets observed in ICMP probes. ' - 'Obtained with the fping linux program.' - ), - 'summary_labels': [_('Average packet loss')], - 'unit': '%', - 'colors': '#d62728', - 'order': 210, - 'query': chart_query['packet_loss'], - }, - 'rtt': { - 'type': 'scatter', - 'title': _('Round Trip Time'), - 'description': _( - 'Round trip time observed in ICMP probes, measuered in milliseconds.' - ), - 'summary_labels': [ - _('Average RTT'), - _('Average Max RTT'), - _('Average Min RTT'), - ], - 'unit': _(' ms'), - 'order': 220, - 'query': chart_query['rtt'], - }, - }, - 'alert_settings': {'operator': '<', 'threshold': 1, 'tolerance': 0}, - 'notification': { - 'problem': { - 'verbose_name': 'Ping PROBLEM', - 'verb': 'cannot be reached anymore', - 'level': 'warning', - 'email_subject': _( - '[{site.name}] {notification.target} is not reachable' - ), - 'message': _( - 'The device [{notification.target}] {notification.verb} anymore by our ping ' - 'messages.' - ), - }, - 'recovery': { - 'verbose_name': 'Ping RECOVERY', - 'verb': 'has become reachable', - 'level': 'info', - 'email_subject': _( - '[{site.name}] {notification.target} is reachable again' - ), - 'message': _( - 'The device [{notification.target}] {notification.verb} again by our ping ' - 'messages.' - ), - }, - }, - } - - # Register your custom metric configuration - register_metric('ping', metric_config) - -The above example will register one metric configuration (named ``ping``), three chart -configurations (named ``rtt``, ``packet_loss``, ``uptime``) as defined in the **charts** key, -two notification types (named ``ping_recovery``, ``ping_problem``) as defined in **notification** key. - -The ``AlertSettings`` of ``ping`` metric will by default use ``threshold`` and ``tolerance`` -defined in the ``alert_settings`` key. -You can always override them and define your own custom values via the *admin*. - -You can also use the ``alert_field`` key in metric configuration -which allows ``AlertSettings`` to check the ``threshold`` on -``alert_field`` instead of the default ``field_name`` key. - -**Note**: It will raise ``ImproperlyConfigured`` exception if a metric configuration -is already registered with same name (not to be confused with verbose_name). - -If you don't need to register a new metric but need to change a specific key of an -existing metric configuration, you can use `OPENWISP_MONITORING_METRICS <#openwisp_monitoring_metrics>`_. - -``unregister_metric`` -~~~~~~~~~~~~~~~~~~~~~ - -This function is used to unregister a metric configuration from anywhere in your code. - -+------------------+------------------------------------------------------+ -| **Parameter** | **Description** | -+------------------+------------------------------------------------------+ -| **metric_name**: | A ``str`` defining name of the metric configuration. | -+------------------+------------------------------------------------------+ - -An example usage is shown below. - -.. code-block:: python - - from openwisp_monitoring.monitoring.configuration import unregister_metric - - # Unregister previously registered metric configuration - unregister_metric('metric_name') - -**Note**: It will raise ``ImproperlyConfigured`` exception if the concerned metric -configuration is not registered. - -Registering / Unregistering Chart Configuration ------------------------------------------------ - -**OpenWISP Monitoring** provides registering and unregistering chart configuration through utility functions -``openwisp_monitoring.monitoring.configuration.register_chart`` and ``openwisp_monitoring.monitoring.configuration.unregister_chart``. -Using these functions you can register or unregister chart configurations from anywhere in your code. - -``register_chart`` -~~~~~~~~~~~~~~~~~~ - -This function is used to register a new chart configuration from anywhere in your code. - -+--------------------------+-----------------------------------------------------+ -| **Parameter** | **Description** | -+--------------------------+-----------------------------------------------------+ -| **chart_name**: | A ``str`` defining name of the chart configuration. | -+--------------------------+-----------------------------------------------------+ -| **chart_configuration**: | A ``dict`` defining configuration of the chart. | -+--------------------------+-----------------------------------------------------+ - -An example usage has been shown below. - -.. code-block:: python - - from openwisp_monitoring.monitoring.configuration import register_chart - - # Define configuration of your chart - chart_config = { - 'type': 'histogram', - 'title': 'Histogram', - 'description': 'Histogram', - 'top_fields': 2, - 'order': 999, - 'query': { - 'influxdb': ( - "SELECT {fields|SUM|/ 1} FROM {key} " - "WHERE time >= '{time}' AND content_type = " - "'{content_type}' AND object_id = '{object_id}'" - ) - }, - } - - # Register your custom chart configuration - register_chart('chart_name', chart_config) - -**Note**: It will raise ``ImproperlyConfigured`` exception if a chart configuration -is already registered with same name (not to be confused with verbose_name). - -If you don't need to register a new chart but need to change a specific key of an -existing chart configuration, you can use `OPENWISP_MONITORING_CHARTS <#openwisp_monitoring_charts>`_. - -``unregister_chart`` -~~~~~~~~~~~~~~~~~~~~ - -This function is used to unregister a chart configuration from anywhere in your code. - -+------------------+-----------------------------------------------------+ -| **Parameter** | **Description** | -+------------------+-----------------------------------------------------+ -| **chart_name**: | A ``str`` defining name of the chart configuration. | -+------------------+-----------------------------------------------------+ - -An example usage is shown below. - -.. code-block:: python - - from openwisp_monitoring.monitoring.configuration import unregister_chart - - # Unregister previously registered chart configuration - unregister_chart('chart_name') - -**Note**: It will raise ``ImproperlyConfigured`` exception if the concerned chart -configuration is not registered. - -Registering new notification types ----------------------------------- - -You can define your own notification types using ``register_notification_type`` function from OpenWISP -Notifications. For more information, see the relevant `openwisp-notifications section about registering notification types -`_. - -Once a new notification type is registered, you have to use the `"notify" signal provided in -openwisp-notifications `_ -to send notifications for this type. - -Exceptions ----------- - -``TimeseriesWriteException`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**Path**: ``openwisp_monitoring.db.exceptions.TimeseriesWriteException`` - -If there is any failure due while writing data in timeseries database, this exception shall -be raised with a helpful error message explaining the cause of the failure. -This exception will normally be caught and the failed write task will be retried in the background -so that there is no loss of data if failures occur due to overload of Timeseries server. -You can read more about this retry mechanism at `OPENWISP_MONITORING_WRITE_RETRY_OPTIONS <#openwisp-monitoring-write-retry-options>`_. - -``InvalidMetricConfigException`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**Path**: ``openwisp_monitoring.monitoring.exceptions.InvalidMetricConfigException`` - -This exception shall be raised if the metric configuration is broken. - -``InvalidChartConfigException`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**Path**: ``openwisp_monitoring.monitoring.exceptions.InvalidChartConfigException`` - -This exception shall be raised if the chart configuration is broken. - -Rest API --------- - -Live documentation -~~~~~~~~~~~~~~~~~~ - -.. image:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/api-doc.png - -A general live API documentation (following the OpenAPI specification) at ``/api/v1/docs/``. - -Browsable web interface -~~~~~~~~~~~~~~~~~~~~~~~ - -.. image:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/api-ui-1.png -.. image:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/api-ui-2.png - -Additionally, opening any of the endpoints `listed below <#list-of-endpoints>`_ -directly in the browser will show the `browsable API interface of Django-REST-Framework -`_, -which makes it even easier to find out the details of each endpoint. - -List of endpoints -~~~~~~~~~~~~~~~~~ - -Since the detailed explanation is contained in the `Live documentation <#live-documentation>`_ -and in the `Browsable web page <#browsable-web-interface>`_ of each point, -here we'll provide just a list of the available endpoints, -for further information please open the URL of the endpoint in your browser. - -Retrieve general monitoring charts -################################## - -.. code-block:: text - - GET /api/v1/monitoring/dashboard/ - -This API endpoint is used to show dashboard monitoring charts. It supports -multi-tenancy and allows filtering monitoring data by ``organization_slug``, -``location_id`` and ``floorplan_id`` e.g.: - -.. code-block:: text - - GET /api/v1/monitoring/dashboard/?organization_slug=,&location_id=,&floorplan_id=, - -- When retrieving chart data, the ``time`` parameter allows to specify - the time frame, eg: - - - ``1d``: returns data of the last day - - ``3d``: returns data of the last 3 days - - ``7d``: returns data of the last 7 days - - ``30d``: returns data of the last 30 days - - ``365d``: returns data of the last 365 days - -- In alternative to ``time`` it is possible to request chart data for a custom - date range by using the ``start`` and ``end`` parameters, eg: - -.. code-block:: text - - GET /api/v1/monitoring/dashboard/?start={start_datetime}&end={end_datetime} - -**Note**: ``start`` and ``end`` parameters should be in the format -``YYYY-MM-DD H:M:S``, otherwise 400 Bad Response will be returned. - -Retrieve device charts and device status data -############################################# - -.. code-block:: text - - GET /api/v1/monitoring/device/{pk}/?key={key}&status=true&time={timeframe} - -The format used for Device Status is inspired by -`NetJSON DeviceMonitoring `_. - -**Notes**: - -- If the request is made without ``?status=true`` the response will - contain only charts data and will not include any device status information - (current load average, ARP table, DCHP leases, etc.). - -- When retrieving chart data, the ``time`` parameter allows to specify - the time frame, eg: - - - ``1d``: returns data of the last day - - ``3d``: returns data of the last 3 days - - ``7d``: returns data of the last 7 days - - ``30d``: returns data of the last 30 days - - ``365d``: returns data of the last 365 days - -- In alternative to ``time`` it is possible to request chart data for a custom - date range by using the ``start`` and ``end`` parameters, eg: - -- The response contains device information, monitoring status (health status), - a list of metrics with their respective statuses, chart data and - device status information (only if ``?status=true``). - -- This endpoint can be accessed with session authentication, token authentication, - or alternatively with the device key passed as query string parameter - as shown below (`?key={key}`); - note: this method is meant to be used by the devices. - -.. code-block:: text - - GET /api/v1/monitoring/device/{pk}/?key={key}&status=true&start={start_datetime}&end={end_datetime} - -**Note**: ``start`` and ``end`` parameters must be in the format -``YYYY-MM-DD H:M:S``, otherwise 400 Bad Response will be returned. - -List device monitoring information -################################## - -.. code-block:: text - - GET /api/v1/monitoring/device/ - -**Notes**: - -- The response contains device information and monitoring status (health status), - but it does not include the information and - health status of the specific metrics, this information - can be retrieved in the detail endpoint of each device. - -- This endpoint can be accessed with session authentication and token authentication. - -**Available filters** - -Data can be filtered by health status (e.g. critical, ok, problem, and unknown) -to obtain the list of devices in the corresponding status, for example, -to retrieve the list of devices which are in critical conditions -(eg: unreachable), the following will work: - -.. code-block:: text - - GET /api/v1/monitoring/device/?monitoring__status=critical - -To filter a list of device monitoring data based -on their organization, you can use the ``organization_id``. - -.. code-block:: text - - GET /api/v1/monitoring/device/?organization={organization_id} - -To filter a list of device monitoring data based -on their organization slug, you can use the ``organization_slug``. - -.. code-block:: text - - GET /api/v1/monitoring/device/?organization_slug={organization_slug} - -Collect device metrics and status -################################# - -.. code-block:: text - - POST /api/v1/monitoring/device/{pk}/?key={key}&time={datetime} - -If data is latest then an additional parameter current can also be passed. For e.g.: - -.. code-block:: text - - POST /api/v1/monitoring/device/{pk}/?key={key}&time={datetime}¤t=true - -The format used for Device Status is inspired by -`NetJSON DeviceMonitoring `_. - -**Note**: the device data will be saved in the timeseries database using -the date time specified ``time``, this should be in the format -``%d-%m-%Y_%H:%M:%S.%f``, otherwise 400 Bad Response will be returned. - -If the request is made without passing the ``time`` argument, -the server local time will be used. - -The ``time`` parameter was added to support `resilient collection -and sending of data by the OpenWISP Monitoring Agent -`_, -this feature allows sending data collected while the device is offline. - -List nearby devices -################### - -.. code-block:: text - - GET /api/v1/monitoring/device/{pk}/nearby-devices/ - -Returns list of nearby devices along with respective distance (in metres) and -monitoring status. - -**Available filters** - -The list of nearby devices provides the following filters: - -- ``organization`` (Organization ID of the device) -- ``organization__slug`` (Organization slug of the device) -- ``monitoring__status`` (Monitoring status (``unknown``, ``ok``, ``problem``, or ``critical``)) -- ``model`` (Pipe `|` separated list of device models) -- ``distance__lte`` (Distance in metres) - -Here's a few examples: - -.. code-block:: text - - GET /api/v1/monitoring/device/{pk}/nearby-devices/?organization={organization_id} - GET /api/v1/monitoring/device/{pk}/nearby-devices/?organization__slug={organization_slug} - GET /api/v1/monitoring/device/{pk}/nearby-devices/?monitoring__status={monitoring_status} - GET /api/v1/monitoring/device/{pk}/nearby-devices/?model={model1,model2} - GET /api/v1/monitoring/device/{pk}/nearby-devices/?distance__lte={distance} - -List wifi session -################# - -.. code-block:: text - - GET /api/v1/monitoring/wifi-session/ - -**Available filters** - -The list of wifi session provides the following filters: - -- ``device__organization`` (Organization ID of the device) -- ``device`` (Device ID) -- ``device__group`` (Device group ID) -- ``start_time`` (Start time of the wifi session) -- ``stop_time`` (Stop time of the wifi session) - -Here's a few examples: - -.. code-block:: text - - GET /api/v1/monitoring/wifi-session/?device__organization={organization_id} - GET /api/v1/monitoring/wifi-session/?device={device_id} - GET /api/v1/monitoring/wifi-session/?device__group={group_id} - GET /api/v1/monitoring/wifi-session/?start_time={stop_time} - GET /api/v1/monitoring/wifi-session/?stop_time={stop_time} - -**Note:** Both `start_time` and `stop_time` support -greater than or equal to, as well as less than or equal to, filter lookups. - -For example: - -.. code-block:: text - - GET /api/v1/monitoring/wifi-session/?start_time__gt={start_time} - GET /api/v1/monitoring/wifi-session/?start_time__gte={start_time} - GET /api/v1/monitoring/wifi-session/?stop_time__lt={stop_time} - GET /api/v1/monitoring/wifi-session/?stop_time__lte={stop_time} - -Get wifi session -################ - -.. code-block:: text - - GET /api/v1/monitoring/wifi-session/{id}/ - -Pagination -########## - -Wifi session endpoint support the ``page_size`` parameter -that allows paginating the results in conjunction with the page parameter. - -.. code-block:: text - - GET /api/v1/monitoring/wifi-session/?page_size=10 - GET /api/v1/monitoring/wifi-session/?page_size=10&page=1 - -Signals -------- - -``device_metrics_received`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**Path**: ``openwisp_monitoring.device.signals.device_metrics_received`` - -**Arguments**: - -- ``instance``: instance of ``Device`` whose metrics have been received -- ``request``: the HTTP request object -- ``time``: time with which metrics will be saved. If none, then server time will be used -- ``current``: whether the data has just been collected or was collected previously and sent now due to network connectivity issues - -This signal is emitted when device metrics are received to the ``DeviceMetric`` -view (only when using HTTP POST). - -The signal is emitted just before a successful response is returned, -it is not sent if the response was not successful. - -``health_status_changed`` -~~~~~~~~~~~~~~~~~~~~~~~~~ - -**Path**: ``openwisp_monitoring.device.signals.health_status_changed`` - -**Arguments**: - -- ``instance``: instance of ``DeviceMonitoring`` whose status has been changed -- ``status``: the status by which DeviceMonitoring's existing status has been updated with - -This signal is emitted only if the health status of DeviceMonitoring object gets updated. - -``threshold_crossed`` -~~~~~~~~~~~~~~~~~~~~~ - -**Path**: ``openwisp_monitoring.monitoring.signals.threshold_crossed`` - -**Arguments**: - -- ``metric``: ``Metric`` object whose threshold defined in related alert settings was crossed -- ``alert_settings``: ``AlertSettings`` related to the ``Metric`` -- ``target``: related ``Device`` object -- ``first_time``: it will be set to true when the metric is written for the first time. It shall be set to false afterwards. -- ``tolerance_crossed``: it will be set to true if the metric has crossed the threshold for tolerance configured in alert settings. - Otherwise, it will be set to false. - -``first_time`` parameter can be used to avoid initiating unneeded actions. -For example, sending recovery notifications. - -This signal is emitted when the threshold value of a ``Metric`` defined in -alert settings is crossed. - -``pre_metric_write`` -~~~~~~~~~~~~~~~~~~~~ - -**Path**: ``openwisp_monitoring.monitoring.signals.pre_metric_write`` - -**Arguments**: - -- ``metric``: ``Metric`` object whose data shall be stored in timeseries database -- ``values``: metric data that shall be stored in the timeseries database -- ``time``: time with which metrics will be saved -- ``current``: whether the data has just been collected or was collected previously and sent now due to network connectivity issues - -This signal is emitted for every metric before the write operation is sent to -the timeseries database. - -``post_metric_write`` -~~~~~~~~~~~~~~~~~~~~~ - -**Path**: ``openwisp_monitoring.monitoring.signals.post_metric_write`` - -**Arguments**: - -- ``metric``: ``Metric`` object whose data is being stored in timeseries database -- ``values``: metric data that is being stored in the timeseries database -- ``time``: time with which metrics will be saved -- ``current``: whether the data has just been collected or was collected previously and sent now due to network connectivity issues - -This signal is emitted for every metric after the write operation is successfully -executed in the background. - -Management commands -------------------- - -``run_checks`` -~~~~~~~~~~~~~~ - -This command will execute all the `available checks <#available-checks>`_ for all the devices. -By default checks are run periodically by *celery beat*. You can learn more -about this in `Setup <#setup-integrate-in-an-existing-django-project>`_. - -Example usage: - -.. code-block:: shell - - cd tests/ - ./manage.py run_checks - -``migrate_timeseries`` -~~~~~~~~~~~~~~~~~~~~~~ - -This command triggers asynchronous migration of the time-series database. - -Example usage: - -.. code-block:: shell - - cd tests/ - ./manage.py migrate_timeseries - -Monitoring scripts ------------------- - -Monitoring scripts are now deprecated in favour of `monitoring packages `_. -Follow the migration guide in `Migrating from monitoring scripts to monitoring packages <#migrating-from-monitoring-scripts-to-monitoring-packages>`_ -section of this documentation. - -Migrating from monitoring scripts to monitoring packages --------------------------------------------------------- - -This section is intended for existing users of *openwisp-monitoring*. -The older version of *openwisp-monitoring* used *monitoring scripts* that -are now deprecated in favour of `monitoring packages `_. - -If you already had a *monitoring template* created on your installation, -then the migrations of *openwisp-monitoring* will update that template -by making the following changes: - -- The file name of all scripts will be appended with ``legacy-`` keyword - in order to differentiate them from the scripts bundled with the new packages. -- The ``/usr/sbin/legacy-openwisp-monitoring`` (previously ``/usr/sbin/openwisp-monitoring``) - script will be updated to exit if `openwisp-monitoring package `_ - is installed on the device. - -Install the `monitoring packages `_ -as mentioned in the `Install monitoring packages on device <#install-monitoring-packages-on-the-device>`_ -section of this documentation. - -After the proper configuration of the `openwisp-monitoring package `_ -on your device, you can remove the monitoring template from your devices. - -We suggest removing the monitoring template from the devices one at a time instead -of deleting the template. This ensures the correctness of -*openwisp monitoring package* configuration and you'll not miss out on -any monitoring data. - -**Note:** If you have made changes to the default monitoring template created -by *openwisp-monitoring* or you are using custom monitoring templates, then you should -remove such templates from the device before installing the -`monitoring packages `_. - -Extending openwisp-monitoring ------------------------------ - -One of the core values of the OpenWISP project is `Software Reusability `_, -for this reason *openwisp-monitoring* provides a set of base classes -which can be imported, extended and reused to create derivative apps. - -In order to implement your custom version of *openwisp-monitoring*, -you need to perform the steps described in the rest of this section. - -When in doubt, the code in the `test project `_ -and the ``sample apps`` namely `sample_check `_, -`sample_monitoring `_, `sample_device_monitoring `_ -will guide you in the correct direction: -just replicate and adapt that code to get a basic derivative of -*openwisp-monitoring* working. - -**Premise**: if you plan on using a customized version of this module, -we suggest to start with it since the beginning, because migrating your data -from the default module to your extended version may be time consuming. - -1. Initialize your custom module -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The first thing you need to do in order to extend any *openwisp-monitoring* app is create -a new django app which will contain your custom version of that *openwisp-monitoring* app. - -A django app is nothing more than a -`python package `_ -(a directory of python scripts), in the following examples we'll call these django apps as -``mycheck``, ``mydevicemonitoring``, ``mymonitoring`` but you can name it how you want:: - - django-admin startapp mycheck - django-admin startapp mydevicemonitoring - django-admin startapp mymonitoring - -Keep in mind that the command mentioned above must be called from a directory -which is available in your `PYTHON_PATH `_ -so that you can then import the result into your project. - -Now you need to add ``mycheck`` to ``INSTALLED_APPS`` in your ``settings.py``, -ensuring also that ``openwisp_monitoring.check`` has been removed: - -.. code-block:: python - - INSTALLED_APPS = [ - # ... other apps ... - # 'openwisp_monitoring.check', <-- comment out or delete this line - # 'openwisp_monitoring.device', <-- comment out or delete this line - # 'openwisp_monitoring.monitoring' <-- comment out or delete this line - 'mycheck', - 'mydevicemonitoring', - 'mymonitoring', - 'nested_admin', - ] - -For more information about how to work with django projects and django apps, -please refer to the `"Tutorial: Writing your first Django app" in the django docunmentation `_. - -2. Install ``openwisp-monitoring`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Install (and add to the requirement of your project) *openwisp-monitoring*:: - - pip install --U https://github.com/openwisp/openwisp-monitoring/tarball/master - -3. Add ``EXTENDED_APPS`` -~~~~~~~~~~~~~~~~~~~~~~~~ - -Add the following to your ``settings.py``: - -.. code-block:: python - - EXTENDED_APPS = ['device_monitoring', 'monitoring', 'check'] - -4. Add ``openwisp_utils.staticfiles.DependencyFinder`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Add ``openwisp_utils.staticfiles.DependencyFinder`` to -``STATICFILES_FINDERS`` in your ``settings.py``: - -.. code-block:: python - - STATICFILES_FINDERS = [ - 'django.contrib.staticfiles.finders.FileSystemFinder', - 'django.contrib.staticfiles.finders.AppDirectoriesFinder', - 'openwisp_utils.staticfiles.DependencyFinder', - ] - -5. Add ``openwisp_utils.loaders.DependencyLoader`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Add ``openwisp_utils.loaders.DependencyLoader`` to ``TEMPLATES`` in your ``settings.py``: - -.. code-block:: python - - TEMPLATES = [ - { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'OPTIONS': { - 'loaders': [ - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', - 'openwisp_utils.loaders.DependencyLoader', - ], - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - ], - }, - } - ] - -6. Inherit the AppConfig class -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Please refer to the following files in the sample app of the test project: - -- `sample_check/__init__.py `_. -- `sample_check/apps.py `_. -- `sample_monitoring/__init__.py `_. -- `sample_monitoring/apps.py `_. -- `sample_device_monitoring/__init__.py `_. -- `sample_device_monitoring/apps.py `_. - -For more information regarding the concept of ``AppConfig`` please refer to -the `"Applications" section in the django documentation `_. - -7. Create your custom models -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To extend ``check`` app, refer to `sample_check models.py file `_. - -To extend ``monitoring`` app, refer to `sample_monitoring models.py file `_. - -To extend ``device_monitoring`` app, refer to `sample_device_monitoring models.py file `_. - -**Note**: - -- For doubts regarding how to use, extend or develop models please refer to - the `"Models" section in the django documentation `_. -- For doubts regarding proxy models please refer to `proxy models `_. - -8. Add swapper configurations -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Add the following to your ``settings.py``: - -.. code-block:: python - - # Setting models for swapper module - # For extending check app - CHECK_CHECK_MODEL = 'YOUR_MODULE_NAME.Check' - # For extending monitoring app - MONITORING_CHART_MODEL = 'YOUR_MODULE_NAME.Chart' - MONITORING_METRIC_MODEL = 'YOUR_MODULE_NAME.Metric' - MONITORING_ALERTSETTINGS_MODEL = 'YOUR_MODULE_NAME.AlertSettings' - # For extending device_monitoring app - DEVICE_MONITORING_DEVICEDATA_MODEL = 'YOUR_MODULE_NAME.DeviceData' - DEVICE_MONITORING_DEVICEMONITORING_MODEL = 'YOUR_MODULE_NAME.DeviceMonitoring' - DEVICE_MONITORING_WIFICLIENT_MODEL = 'YOUR_MODULE_NAME.WifiClient' - DEVICE_MONITORING_WIFISESSION_MODEL = 'YOUR_MODULE_NAME.WifiSession' - -Substitute ```` with your actual django app name -(also known as ``app_label``). - -9. Create database migrations -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Create and apply database migrations:: - - ./manage.py makemigrations - ./manage.py migrate - -For more information, refer to the -`"Migrations" section in the django documentation `_. - -10. Create your custom admin -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To extend ``check`` app, refer to `sample_check admin.py file `_. - -To extend ``monitoring`` app, refer to `sample_monitoring admin.py file `_. - -To extend ``device_monitoring`` app, refer to `sample_device_monitoring admin.py file `_. - -To introduce changes to the admin, you can do it in the two ways described below. - -**Note**: for doubts regarding how the django admin works, or how it can be customized, -please refer to `"The django admin site" section in the django documentation `_. - -1. Monkey patching -################## - -If the changes you need to add are relatively small, you can resort to monkey patching. - -For example, for ``check`` app you can do it as: - -.. code-block:: python - - from openwisp_monitoring.check.admin import CheckAdmin - - CheckAdmin.list_display.insert(1, 'my_custom_field') - CheckAdmin.ordering = ['-my_custom_field'] - -Similarly for ``device_monitoring`` app, you can do it as: - -.. code-block:: python - - from openwisp_monitoring.device.admin import DeviceAdmin, WifiSessionAdmin - - DeviceAdmin.list_display.insert(1, 'my_custom_field') - DeviceAdmin.ordering = ['-my_custom_field'] - WifiSessionAdmin.fields += ['my_custom_field'] - -Similarly for ``monitoring`` app, you can do it as: - -.. code-block:: python - - from openwisp_monitoring.monitoring.admin import MetricAdmin, AlertSettingsAdmin - - MetricAdmin.list_display.insert(1, 'my_custom_field') - MetricAdmin.ordering = ['-my_custom_field'] - AlertSettingsAdmin.list_display.insert(1, 'my_custom_field') - AlertSettingsAdmin.ordering = ['-my_custom_field'] - -2. Inheriting admin classes -########################### - -If you need to introduce significant changes and/or you don't want to resort to -monkey patching, you can proceed as follows: - -For ``check`` app, - -.. code-block:: python - - from django.contrib import admin - - from openwisp_monitoring.check.admin import CheckAdmin as BaseCheckAdmin - from swapper import load_model - - Check = load_model('check', 'Check') - - admin.site.unregister(Check) - - @admin.register(Check) - class CheckAdmin(BaseCheckAdmin): - # add your changes here - -For ``device_monitoring`` app, - -.. code-block:: python - - from django.contrib import admin - - from openwisp_monitoring.device_monitoring.admin import DeviceAdmin as BaseDeviceAdmin - from openwisp_monitoring.device_monitoring.admin import WifiSessionAdmin as BaseWifiSessionAdmin - from swapper import load_model - - Device = load_model('config', 'Device') - WifiSession = load_model('device_monitoring', 'WifiSession') - - admin.site.unregister(Device) - admin.site.unregister(WifiSession) - - @admin.register(Device) - class DeviceAdmin(BaseDeviceAdmin): - # add your changes here - - @admin.register(WifiSession) - class WifiSessionAdmin(BaseWifiSessionAdmin): - # add your changes here - -For ``monitoring`` app, - -.. code-block:: python - - from django.contrib import admin - - from openwisp_monitoring.monitoring.admin import ( - AlertSettingsAdmin as BaseAlertSettingsAdmin, - MetricAdmin as BaseMetricAdmin - ) - from swapper import load_model - - Metric = load_model('Metric') - AlertSettings = load_model('AlertSettings') - - admin.site.unregister(Metric) - admin.site.unregister(AlertSettings) - - @admin.register(Metric) - class MetricAdmin(BaseMetricAdmin): - # add your changes here - - @admin.register(AlertSettings) - class AlertSettingsAdmin(BaseAlertSettingsAdmin): - # add your changes here - -11. Create root URL configuration -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Please refer to the `urls.py `_ -file in the test project. - -For more information about URL configuration in django, please refer to the -`"URL dispatcher" section in the django documentation `_. - -12. Create celery.py -~~~~~~~~~~~~~~~~~~~~ - -Please refer to the `celery.py `_ -file in the test project. - -For more information about the usage of celery in django, please refer to the -`"First steps with Django" section in the celery documentation `_. - -13. Import Celery Tasks -~~~~~~~~~~~~~~~~~~~~~~~ - -Add the following in your settings.py to import celery tasks from ``device_monitoring`` app. - -.. code-block:: python - - CELERY_IMPORTS = ('openwisp_monitoring.device.tasks',) - -14. Create the custom command ``run_checks`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Please refer to the `run_checks.py `_ -file in the test project. - -For more information about the usage of custom management commands in django, please refer to the -`"Writing custom django-admin commands" section in the django documentation `_. - -15. Import the automated tests -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When developing a custom application based on this module, it's a good idea -to import and run the base tests too, so that you can be sure the changes you're introducing -are not breaking some of the existing features of openwisp-monitoring. - -In case you need to add breaking changes, you can overwrite the tests defined -in the base classes to test your own behavior. - -For, extending ``check`` app see the `tests of sample_check app `_ -to find out how to do this. - -For, extending ``device_monitoring`` app see the `tests of sample_device_monitoring app `_ -to find out how to do this. - -For, extending ``monitoring`` app see the `tests of sample_monitoring app `_ -to find out how to do this. - -Other base classes that can be inherited and extended -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**The following steps are not required and are intended for more advanced customization.** - -``DeviceMetricView`` -#################### - -This view is responsible for displaying ``Charts`` and ``Status`` primarily. - -The full python path is: ``openwisp_monitoring.device.api.views.DeviceMetricView``. - -If you want to extend this view, you will have to perform the additional steps below. - -Step 1. Import and extend view: - -.. code-block:: python - - # mydevice/api/views.py - from openwisp_monitoring.device.api.views import ( - DeviceMetricView as BaseDeviceMetricView - ) - - class DeviceMetricView(BaseDeviceMetricView): - # add your customizations here ... - pass - -Step 2: remove the following line from your root ``urls.py`` file: - -.. code-block:: python - - re_path( - 'api/v1/monitoring/device/(?P[^/]+)/$', - views.device_metric, - name='api_device_metric', - ), - -Step 3: add an URL route pointing to your custom view in ``urls.py`` file: + :align: center -.. code-block:: python +For a complete overview of features, refer to the `Monitoring: Features +`_ section of the +OpenWISP documentation. - # urls.py - from mydevice.api.views import DeviceMetricView +Documentation +------------- - urlpatterns = [ - # ... other URLs - re_path(r'^(?P.*)$', DeviceMetricView.as_view(), name='api_device_metric',), - ] +- `Developer documentation + `_ +- `User documentation `_ Contributing ------------ -Please refer to the `OpenWISP contributing guidelines `_. +Please refer to the `OpenWISP contributing guidelines +`_. diff --git a/docs/developer/extending.rst b/docs/developer/extending.rst new file mode 100644 index 000000000..5f9edb14d --- /dev/null +++ b/docs/developer/extending.rst @@ -0,0 +1,496 @@ +Extending OpenWISP Monitoring +============================= + +.. include:: ../partials/developer-docs.rst + +One of the core values of the OpenWISP project is :ref:`Software +Reusability `, for this reason +*openwisp-monitoring* provides a set of base classes which can be +imported, extended and reused to create derivative apps. + +In order to implement your custom version of *openwisp-monitoring*, you +need to perform the steps described in the rest of this section. + +When in doubt, the code in the `test project +`_ +and the ``sample apps`` namely `sample_check +`_, +`sample_monitoring +`_, +`sample_device_monitoring +`_ +will guide you in the correct direction: just replicate and adapt that +code to get a basic derivative of *openwisp-monitoring* working. + +.. important:: + + If you plan on using a customized version of this module, we suggest + to start with it since the beginning, because migrating your data from + the default module to your extended version may be time consuming. + +.. contents:: **Table of Contents**: + :depth: 2 + :local: + +1. Initialize your Custom Module +-------------------------------- + +The first thing you need to do in order to extend any +*openwisp-monitoring* app is create a new django app which will contain +your custom version of that *openwisp-monitoring* app. + +A django app is nothing more than a `python package +`_ (a directory +of python scripts), in the following examples we'll call these django apps +as ``mycheck``, ``mydevicemonitoring``, ``mymonitoring`` but you can name +it how you want: + +.. code-block:: + + django-admin startapp mycheck + django-admin startapp mydevicemonitoring + django-admin startapp mymonitoring + +Keep in mind that the command mentioned above must be called from a +directory which is available in your `PYTHON_PATH +`_ so that +you can then import the result into your project. + +Now you need to add ``mycheck`` to ``INSTALLED_APPS`` in your +``settings.py``, ensuring also that ``openwisp_monitoring.check`` has been +removed: + +.. code-block:: python + + INSTALLED_APPS = [ + # ... other apps ... + # 'openwisp_monitoring.check', <-- comment out or delete this line + # 'openwisp_monitoring.device', <-- comment out or delete this line + # 'openwisp_monitoring.monitoring' <-- comment out or delete this line + "mycheck", + "mydevicemonitoring", + "mymonitoring", + "nested_admin", + ] + +For more information about how to work with django projects and django +apps, please refer to the `"Tutorial: Writing your first Django app" in +the django documentation +`_. + +2. Install ``openwisp-monitoring`` +---------------------------------- + +Install (and add to the requirement of your project) +*openwisp-monitoring*: + +.. code-block:: + + pip install --U https://github.com/openwisp/openwisp-monitoring/tarball/master + +3. Add ``EXTENDED_APPS`` +------------------------ + +Add the following to your ``settings.py``: + +.. code-block:: python + + EXTENDED_APPS = ["device_monitoring", "monitoring", "check"] + +4. Add ``openwisp_utils.staticfiles.DependencyFinder`` +------------------------------------------------------ + +Add ``openwisp_utils.staticfiles.DependencyFinder`` to +``STATICFILES_FINDERS`` in your ``settings.py``: + +.. code-block:: python + + STATICFILES_FINDERS = [ + "django.contrib.staticfiles.finders.FileSystemFinder", + "django.contrib.staticfiles.finders.AppDirectoriesFinder", + "openwisp_utils.staticfiles.DependencyFinder", + ] + +5. Add ``openwisp_utils.loaders.DependencyLoader`` +-------------------------------------------------- + +Add ``openwisp_utils.loaders.DependencyLoader`` to ``TEMPLATES`` in your +``settings.py``: + +.. code-block:: python + + TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "OPTIONS": { + "loaders": [ + "django.template.loaders.filesystem.Loader", + "django.template.loaders.app_directories.Loader", + "openwisp_utils.loaders.DependencyLoader", + ], + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ], + }, + } + ] + +6. Inherit the AppConfig Class +------------------------------ + +Please refer to the following files in the sample app of the test project: + +- `sample_check/__init__.py + `_. +- `sample_check/apps.py + `_. +- `sample_monitoring/__init__.py + `_. +- `sample_monitoring/apps.py + `_. +- `sample_device_monitoring/__init__.py + `_. +- `sample_device_monitoring/apps.py + `_. + +For more information regarding the concept of ``AppConfig`` please refer +to the `"Applications" section in the django documentation +`_. + +7. Create your Custom Models +---------------------------- + +To extend ``check`` app, refer to `sample_check models.py file +`_. + +To extend ``monitoring`` app, refer to `sample_monitoring models.py file +`_. + +To extend ``device_monitoring`` app, refer to `sample_device_monitoring +models.py file +`_. + +.. note:: + + - For doubts regarding how to use, extend or develop models please + refer to the `"Models" section in the django documentation + `_. + - For doubts regarding proxy models please refer to `proxy models + `_. + +8. Add Swapper Configurations +----------------------------- + +Add the following to your ``settings.py``: + +.. code-block:: python + + # Setting models for swapper module + # For extending check app + CHECK_CHECK_MODEL = "YOUR_MODULE_NAME.Check" + # For extending monitoring app + MONITORING_CHART_MODEL = "YOUR_MODULE_NAME.Chart" + MONITORING_METRIC_MODEL = "YOUR_MODULE_NAME.Metric" + MONITORING_ALERTSETTINGS_MODEL = "YOUR_MODULE_NAME.AlertSettings" + # For extending device_monitoring app + DEVICE_MONITORING_DEVICEDATA_MODEL = "YOUR_MODULE_NAME.DeviceData" + DEVICE_MONITORING_DEVICEMONITORING_MODEL = ( + "YOUR_MODULE_NAME.DeviceMonitoring" + ) + DEVICE_MONITORING_WIFICLIENT_MODEL = "YOUR_MODULE_NAME.WifiClient" + DEVICE_MONITORING_WIFISESSION_MODEL = "YOUR_MODULE_NAME.WifiSession" + +Substitute ```` with your actual django app name (also +known as ``app_label``). + +9. Create Database Migrations +----------------------------- + +Create and apply database migrations: + +.. code-block:: + + ./manage.py makemigrations + ./manage.py migrate + +For more information, refer to the `"Migrations" section in the django +documentation +`_. + +10. Create your Custom Admin +---------------------------- + +To extend ``check`` app, refer to `sample_check admin.py file +`_. + +To extend ``monitoring`` app, refer to `sample_monitoring admin.py file +`_. + +To extend ``device_monitoring`` app, refer to `sample_device_monitoring +admin.py file +`_. + +To introduce changes to the admin, you can do it in the two ways described +below. + +.. note:: + + For doubts regarding how the django admin works, or how it can be + customized, please refer to `"The django admin site" section in the + django documentation + `_. + +1. Monkey Patching +~~~~~~~~~~~~~~~~~~ + +If the changes you need to add are relatively small, you can resort to +monkey patching. + +For example, for ``check`` app you can do it as: + +.. code-block:: python + + from openwisp_monitoring.check.admin import CheckAdmin + + CheckAdmin.list_display.insert(1, "my_custom_field") + CheckAdmin.ordering = ["-my_custom_field"] + +Similarly for ``device_monitoring`` app, you can do it as: + +.. code-block:: python + + from openwisp_monitoring.device.admin import DeviceAdmin, WifiSessionAdmin + + DeviceAdmin.list_display.insert(1, "my_custom_field") + DeviceAdmin.ordering = ["-my_custom_field"] + WifiSessionAdmin.fields += ["my_custom_field"] + +Similarly for ``monitoring`` app, you can do it as: + +.. code-block:: python + + from openwisp_monitoring.monitoring.admin import ( + MetricAdmin, + AlertSettingsAdmin, + ) + + MetricAdmin.list_display.insert(1, "my_custom_field") + MetricAdmin.ordering = ["-my_custom_field"] + AlertSettingsAdmin.list_display.insert(1, "my_custom_field") + AlertSettingsAdmin.ordering = ["-my_custom_field"] + +2. Inheriting Admin Classes +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you need to introduce significant changes and/or you don't want to +resort to monkey patching, you can proceed as follows: + +For ``check`` app, + +.. code-block:: python + + from django.contrib import admin + + from openwisp_monitoring.check.admin import CheckAdmin as BaseCheckAdmin + from swapper import load_model + + Check = load_model("check", "Check") + + admin.site.unregister(Check) + + + @admin.register(Check) + class CheckAdmin(BaseCheckAdmin): + # add your changes here + pass + +For ``device_monitoring`` app, + +.. code-block:: python + + from django.contrib import admin + + from openwisp_monitoring.device_monitoring.admin import ( + DeviceAdmin as BaseDeviceAdmin, + ) + from openwisp_monitoring.device_monitoring.admin import ( + WifiSessionAdmin as BaseWifiSessionAdmin, + ) + from swapper import load_model + + Device = load_model("config", "Device") + WifiSession = load_model("device_monitoring", "WifiSession") + + admin.site.unregister(Device) + admin.site.unregister(WifiSession) + + + @admin.register(Device) + class DeviceAdmin(BaseDeviceAdmin): + # add your changes here + pass + + + @admin.register(WifiSession) + class WifiSessionAdmin(BaseWifiSessionAdmin): + # add your changes here + pass + +For ``monitoring`` app, + +.. code-block:: python + + from django.contrib import admin + + from openwisp_monitoring.monitoring.admin import ( + AlertSettingsAdmin as BaseAlertSettingsAdmin, + MetricAdmin as BaseMetricAdmin, + ) + from swapper import load_model + + Metric = load_model("Metric") + AlertSettings = load_model("AlertSettings") + + admin.site.unregister(Metric) + admin.site.unregister(AlertSettings) + + + @admin.register(Metric) + class MetricAdmin(BaseMetricAdmin): + # add your changes here + pass + + + @admin.register(AlertSettings) + class AlertSettingsAdmin(BaseAlertSettingsAdmin): + # add your changes here + pass + +11. Create Root URL Configuration +--------------------------------- + +Please refer to the `urls.py +`_ +file in the test project. + +For more information about URL configuration in django, please refer to +the `"URL dispatcher" section in the django documentation +`_. + +12. Create ``celery.py`` +------------------------ + +Please refer to the `celery.py +`_ +file in the test project. + +For more information about the usage of celery in django, please refer to +the `"First steps with Django" section in the celery documentation +`_. + +13. Import Celery Tasks +----------------------- + +Add the following in your settings.py to import celery tasks from +``device_monitoring`` app. + +.. code-block:: python + + CELERY_IMPORTS = ("openwisp_monitoring.device.tasks",) + +14. Create the Custom Command ``run_checks`` +-------------------------------------------- + +Please refer to the `run_checks.py +`_ +file in the test project. + +For more information about the usage of custom management commands in +django, please refer to the `"Writing custom django-admin commands" +section in the django documentation +`_. + +15. Import the Automated Tests +------------------------------ + +When developing a custom application based on this module, it's a good +idea to import and run the base tests too, so that you can be sure the +changes you're introducing are not breaking some of the existing features +of openwisp-monitoring. + +In case you need to add breaking changes, you can overwrite the tests +defined in the base classes to test your own behavior. + +For, extending ``check`` app see the `tests of sample_check app +`_ +to find out how to do this. + +For, extending ``device_monitoring`` app see the `tests of +sample_device_monitoring app +`_ +to find out how to do this. + +For, extending ``monitoring`` app see the `tests of sample_monitoring app +`_ +to find out how to do this. + +Other Base Classes that can be Inherited and Extended +----------------------------------------------------- + +**The following steps are not required and are intended for more advanced +customization.** + +``DeviceMetricView`` +~~~~~~~~~~~~~~~~~~~~ + +This view is responsible for displaying ``Charts`` and ``Status`` +primarily. + +The full python path is: +``openwisp_monitoring.device.api.views.DeviceMetricView``. + +If you want to extend this view, you will have to perform the additional +steps below. + +Step 1. Import and extend view: + +.. code-block:: python + + # mydevice/api/views.py + from openwisp_monitoring.device.api.views import ( + DeviceMetricView as BaseDeviceMetricView, + ) + + + class DeviceMetricView(BaseDeviceMetricView): + # add your customizations here ... + pass + +Step 2: remove the following line from your root ``urls.py`` file: + +.. code-block:: python + + re_path( + "api/v1/monitoring/device/(?P[^/]+)/$", + views.device_metric, + name="api_device_metric", + ), + +Step 3: add an URL route pointing to your custom view in ``urls.py`` file: + +.. code-block:: python + + # urls.py + from mydevice.api.views import DeviceMetricView + + urlpatterns = [ + # ... other URLs + re_path( + r"^(?P.*)$", + DeviceMetricView.as_view(), + name="api_device_metric", + ), + ] diff --git a/docs/developer/index.rst b/docs/developer/index.rst new file mode 100644 index 000000000..99915fc96 --- /dev/null +++ b/docs/developer/index.rst @@ -0,0 +1,16 @@ +Developer Docs +============== + +.. include:: ../partials/developer-docs.rst + +.. toctree:: + :maxdepth: 2 + + ./installation.rst + ./utils.rst + ./extending.rst + +Other useful resources: + + - :doc:`../user/rest-api` + - :doc:`../user/settings` diff --git a/docs/developer/installation.rst b/docs/developer/installation.rst new file mode 100644 index 000000000..2e9d64147 --- /dev/null +++ b/docs/developer/installation.rst @@ -0,0 +1,161 @@ +Developer Installation Instructions +=================================== + +.. include:: ../partials/developer-docs.rst + +.. contents:: **Table of contents**: + :depth: 2 + :local: + +Dependencies +------------ + +- Python >= 3.8 +- InfluxDB 1.8 +- fping +- OpenSSL + +Installing for Development +-------------------------- + +Install the system dependencies: + +Install system packages: + +.. code-block:: shell + + sudo apt update + sudo apt install -y sqlite3 libsqlite3-dev openssl libssl-dev + sudo apt install -y gdal-bin libproj-dev libgeos-dev libspatialite-dev libsqlite3-mod-spatialite + sudo apt install -y fping + sudo apt install -y chromium + +Fork and clone the forked repository: + +.. code-block:: shell + + git clone git://github.com//openwisp-monitoring + +Navigate into the cloned repository: + +.. code-block:: shell + + cd openwisp-monitoring/ + +Start Redis and InfluxDB using Docker: + +.. code-block:: shell + + docker-compose up -d redis influxdb + +Setup and activate a virtual-environment. (we'll be using `virtualenv +`_) + +.. code-block:: shell + + python -m virtualenv env + source env/bin/activate + +Make sure that you are using pip version 20.2.4 before moving to the next +step: + +.. code-block:: shell + + pip install -U pip wheel setuptools + +Install development dependencies: + +.. code-block:: shell + + pip install -e . + pip install -r requirements-test.txt + npm install -g jshint stylelint + +Install WebDriver for Chromium for your browser version from +https://chromedriver.chromium.org/home and extract ``chromedriver`` to one +of directories from your ``$PATH`` (example: ``~/.local/bin/``). + +Create database: + +.. code-block:: shell + + cd tests/ + ./manage.py migrate + ./manage.py createsuperuser + +Run celery and celery-beat with the following commands (separate terminal +windows are needed): + +.. code-block:: shell + + cd tests/ + celery -A openwisp2 worker -l info + celery -A openwisp2 beat -l info + +Launch development server: + +.. code-block:: shell + + ./manage.py runserver 0.0.0.0:8000 + +You can access the admin interface at ``http://127.0.0.1:8000/admin/``. + +Run tests with: + +.. code-block:: shell + + ./runtests.py # using --parallel is not supported in this module + +Run quality assurance tests with: + +.. code-block:: shell + + ./run-qa-checks + +Alternative Sources +------------------- + +PyPI +~~~~ + +To install the latest stable version from pypi: + +.. code-block:: shell + + pip install openwisp-monitoring + +Github +~~~~~~ + +To install the latest development version tarball via HTTPs: + +.. code-block:: shell + + pip install https://github.com/openwisp/openwisp-monitoring/tarball/master + +Alternatively you can use the git protocol: + +.. code-block:: shell + + pip install -e git+git://github.com/openwisp/openwisp-monitoring#egg=openwisp_monitoring + +Install and Run on Docker +------------------------- + +.. warning:: + + This Docker image is for development purposes only. + + For the official OpenWISP Docker images, see: :doc:`/docker/index`. + +Build from the Dockerfile: + +.. code-block:: shell + + docker-compose build + +Run the docker container: + +.. code-block:: shell + + docker-compose up diff --git a/docs/developer/utils.rst b/docs/developer/utils.rst new file mode 100644 index 000000000..31d85bb5f --- /dev/null +++ b/docs/developer/utils.rst @@ -0,0 +1,439 @@ +Code Utilities +============== + +.. include:: ../partials/developer-docs.rst + +.. contents:: **Table of contents**: + :depth: 1 + :local: + +Registering / Unregistering Metric Configuration +------------------------------------------------ + +**OpenWISP Monitoring** provides registering and unregistering metric +configuration through utility functions +``openwisp_monitoring.monitoring.configuration.register_metric`` and +``openwisp_monitoring.monitoring.configuration.unregister_metric``. Using +these functions you can register or unregister metric configurations from +anywhere in your code. + +``register_metric`` +~~~~~~~~~~~~~~~~~~~ + +This function is used to register a new metric configuration from anywhere +in your code. + +========================= ================================================ +**Parameter** **Description** +**metric_name**: A ``str`` defining name of the metric + configuration. +**metric_configuration**: A ``dict`` defining configuration of the metric. +========================= ================================================ + +An example usage has been shown below. + +.. code-block:: python + + from django.utils.translation import gettext_lazy as _ + from openwisp_monitoring.monitoring.configuration import register_metric + + # Define configuration of your metric + metric_config = { + "label": _("Ping"), + "name": "Ping", + "key": "ping", + "field_name": "reachable", + "related_fields": ["loss", "rtt_min", "rtt_max", "rtt_avg"], + "charts": { + "uptime": { + "type": "bar", + "title": _("Ping Success Rate"), + "description": _( + "A value of 100% means reachable, 0% means unreachable, values in " + "between 0% and 100% indicate the average reachability in the " + "period observed. Obtained with the fping linux program." + ), + "summary_labels": [_("Average Ping Success Rate")], + "unit": "%", + "order": 200, + "colorscale": { + "max": 100, + "min": 0, + "label": _("Rate"), + "scale": [ + [ + [0, "#c13000"], + [0.1, "cb7222"], + [0.5, "#deed0e"], + [0.9, "#7db201"], + [1, "#498b26"], + ], + ], + "map": [ + [100, "#498b26", _("Flawless")], + [90, "#7db201", _("Mostly Reachable")], + [50, "#deed0e", _("Partly Reachable")], + [10, "#cb7222", _("Mostly Unreachable")], + [None, "#c13000", _("Unreachable")], + ], + "fixed_value": 100, + }, + "query": chart_query["uptime"], + }, + "packet_loss": { + "type": "bar", + "title": _("Packet loss"), + "description": _( + "Indicates the percentage of lost packets observed in ICMP probes. " + "Obtained with the fping linux program." + ), + "summary_labels": [_("Average packet loss")], + "unit": "%", + "colors": "#d62728", + "order": 210, + "query": chart_query["packet_loss"], + }, + "rtt": { + "type": "scatter", + "title": _("Round Trip Time"), + "description": _( + "Round trip time observed in ICMP probes, measuered in milliseconds." + ), + "summary_labels": [ + _("Average RTT"), + _("Average Max RTT"), + _("Average Min RTT"), + ], + "unit": _(" ms"), + "order": 220, + "query": chart_query["rtt"], + }, + }, + "alert_settings": {"operator": "<", "threshold": 1, "tolerance": 0}, + "notification": { + "problem": { + "verbose_name": "Ping PROBLEM", + "verb": "cannot be reached anymore", + "level": "warning", + "email_subject": _( + "[{site.name}] {notification.target} is not reachable" + ), + "message": _( + "The device [{notification.target}] {notification.verb} anymore by our ping " + "messages." + ), + }, + "recovery": { + "verbose_name": "Ping RECOVERY", + "verb": "has become reachable", + "level": "info", + "email_subject": _( + "[{site.name}] {notification.target} is reachable again" + ), + "message": _( + "The device [{notification.target}] {notification.verb} again by our ping " + "messages." + ), + }, + }, + } + + # Register your custom metric configuration + register_metric("ping", metric_config) + +The above example will register one metric configuration (named ``ping``), +three chart configurations (named ``rtt``, ``packet_loss``, ``uptime``) as +defined in the **charts** key, two notification types (named +``ping_recovery``, ``ping_problem``) as defined in **notification** key. + +The ``AlertSettings`` of ``ping`` metric will by default use ``threshold`` +and ``tolerance`` defined in the ``alert_settings`` key. You can always +override them and define your own custom values via the *admin*. + +You can also use the ``alert_field`` key in metric configuration which +allows ``AlertSettings`` to check the ``threshold`` on ``alert_field`` +instead of the default ``field_name`` key. + +.. note:: + + It will raise ``ImproperlyConfigured`` exception if a metric + configuration is already registered with same name (not to be confused + with verbose_name). + +If you don't need to register a new metric but need to change a specific +key of an existing metric configuration, you can use +:ref:`OPENWISP_MONITORING_METRICS `. + +``unregister_metric`` +~~~~~~~~~~~~~~~~~~~~~ + +This function is used to unregister a metric configuration from anywhere +in your code. + +================ ==================================================== +**Parameter** **Description** +**metric_name**: A ``str`` defining name of the metric configuration. +================ ==================================================== + +An example usage is shown below. + +.. code-block:: python + + from openwisp_monitoring.monitoring.configuration import unregister_metric + + # Unregister previously registered metric configuration + unregister_metric("metric_name") + +.. note:: + + It will raise ``ImproperlyConfigured`` exception if the concerned + metric configuration is not registered. + +Registering / Unregistering Chart Configuration +----------------------------------------------- + +**OpenWISP Monitoring** provides registering and unregistering chart +configuration through utility functions +``openwisp_monitoring.monitoring.configuration.register_chart`` and +``openwisp_monitoring.monitoring.configuration.unregister_chart``. Using +these functions you can register or unregister chart configurations from +anywhere in your code. + +``register_chart`` +~~~~~~~~~~~~~~~~~~ + +This function is used to register a new chart configuration from anywhere +in your code. + +======================== =============================================== +**Parameter** **Description** +**chart_name**: A ``str`` defining name of the chart + configuration. +**chart_configuration**: A ``dict`` defining configuration of the chart. +======================== =============================================== + +An example usage has been shown below. + +.. code-block:: python + + from openwisp_monitoring.monitoring.configuration import register_chart + + # Define configuration of your chart + chart_config = { + "type": "histogram", + "title": "Histogram", + "description": "Histogram", + "top_fields": 2, + "order": 999, + "query": { + "influxdb": ( + "SELECT {fields|SUM|/ 1} FROM {key} " + "WHERE time >= '{time}' AND content_type = " + "'{content_type}' AND object_id = '{object_id}'" + ) + }, + } + + # Register your custom chart configuration + register_chart("chart_name", chart_config) + +.. note:: + + It will raise ``ImproperlyConfigured`` exception if a chart + configuration is already registered with same name (not to be confused + with verbose_name). + +If you don't need to register a new chart but need to change a specific +key of an existing chart configuration, you can use +:ref:`OPENWISP_MONITORING_CHARTS `. + +``unregister_chart`` +~~~~~~~~~~~~~~~~~~~~ + +This function is used to unregister a chart configuration from anywhere in +your code. + +=============== =================================================== +**Parameter** **Description** +**chart_name**: A ``str`` defining name of the chart configuration. +=============== =================================================== + +An example usage is shown below. + +.. code-block:: python + + from openwisp_monitoring.monitoring.configuration import unregister_chart + + # Unregister previously registered chart configuration + unregister_chart("chart_name") + +.. note:: + + It will raise ``ImproperlyConfigured`` exception if the concerned + chart configuration is not registered. + +Monitoring Notifications +------------------------ + +OpenWISP Monitoring registers and uses the following notification types: + +- ``threshold_crossed``: Fires when a metric crosses the boundary defined + in the threshold value of the alert settings. +- ``threhold_recovery``: Fires when a metric goes back within the expected + range. +- ``connection_is_working``: Fires when the connection to a device is + working. +- ``connection_is_not_working``: Fires when the connection (eg: SSH) to a + device stops working (eg: credentials are outdated, management IP + address is outdated, or device is not reachable). + +Registering Notification Types +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can define your own notification types using +``register_notification_type`` function from OpenWISP Notifications. + +For more information, see the relevant :ref:`documentation section about +registering notification types in the Notifications module +`. + +Once a new notification type is registered, you have to use the +:doc:`"notify" signal provided the Notifications module +` to send notifications for +this type. + +Signals +------- + +.. include:: /partials/signals-note.rst + +``device_metrics_received`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Full Python path**: +``openwisp_monitoring.device.signals.device_metrics_received`` + +**Arguments**: + +- ``instance``: instance of ``Device`` whose metrics have been received +- ``request``: the HTTP request object +- ``time``: time with which metrics will be saved. If none, then server + time will be used +- ``current``: whether the data has just been collected or was collected + previously and sent now due to network connectivity issues + +This signal is emitted when device metrics are received to the +``DeviceMetric`` view (only when using HTTP POST). + +The signal is emitted just before a successful response is returned, it is +not sent if the response was not successful. + +``health_status_changed`` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Full Python path**: +``openwisp_monitoring.device.signals.health_status_changed`` + +**Arguments**: + +- ``instance``: instance of ``DeviceMonitoring`` whose status has been + changed +- ``status``: the status by which DeviceMonitoring's existing status has + been updated with + +This signal is emitted only if the health status of DeviceMonitoring +object gets updated. + +``threshold_crossed`` +~~~~~~~~~~~~~~~~~~~~~ + +**Full Python path**: +``openwisp_monitoring.monitoring.signals.threshold_crossed`` + +**Arguments**: + +- ``metric``: ``Metric`` object whose threshold defined in related alert + settings was crossed +- ``alert_settings``: ``AlertSettings`` related to the ``Metric`` +- ``target``: related ``Device`` object +- ``first_time``: it will be set to true when the metric is written for + the first time. It shall be set to false afterwards. +- ``tolerance_crossed``: it will be set to true if the metric has crossed + the threshold for tolerance configured in alert settings. Otherwise, it + will be set to false. + +``first_time`` parameter can be used to avoid initiating unneeded actions. +For example, sending recovery notifications. + +This signal is emitted when the threshold value of a ``Metric`` defined in +alert settings is crossed. + +``pre_metric_write`` +~~~~~~~~~~~~~~~~~~~~ + +**Full Python path**: +``openwisp_monitoring.monitoring.signals.pre_metric_write`` + +**Arguments**: + +- ``metric``: ``Metric`` object whose data shall be stored in timeseries + database +- ``values``: metric data that shall be stored in the timeseries database +- ``time``: time with which metrics will be saved +- ``current``: whether the data has just been collected or was collected + previously and sent now due to network connectivity issues + +This signal is emitted for every metric before the write operation is sent +to the timeseries database. + +``post_metric_write`` +~~~~~~~~~~~~~~~~~~~~~ + +**Full Python path**: +``openwisp_monitoring.monitoring.signals.post_metric_write`` + +**Arguments**: + +- ``metric``: ``Metric`` object whose data is being stored in timeseries + database +- ``values``: metric data that is being stored in the timeseries database +- ``time``: time with which metrics will be saved +- ``current``: whether the data has just been collected or was collected + previously and sent now due to network connectivity issues + +This signal is emitted for every metric after the write operation is +successfully executed in the background. + +Exceptions +---------- + +``TimeseriesWriteException`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Full Python path**: +``openwisp_monitoring.db.exceptions.TimeseriesWriteException`` + +If there is any failure due while writing data in timeseries database, +this exception will be raised with a helpful error message explaining the +cause of the failure. This exception will normally be caught and the +failed write task will be retried in the background so that there is no +loss of data if failures occur due to overload of Timeseries server. You +can read more about this retry mechanism at +:ref:`OPENWISP_MONITORING_WRITE_RETRY_OPTIONS +`. + +``InvalidMetricConfigException`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Full Python path**: +``openwisp_monitoring.monitoring.exceptions.InvalidMetricConfigException`` + +This exception will be raised if the metric configuration is broken. + +``InvalidChartConfigException`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Full Python path**: +``openwisp_monitoring.monitoring.exceptions.InvalidChartConfigException`` + +This exception will be raised if the chart configuration is broken. diff --git a/docs/images/architecture-v2-openwisp-monitoring.png b/docs/images/architecture-v2-openwisp-monitoring.png new file mode 100644 index 000000000..5a67d8033 Binary files /dev/null and b/docs/images/architecture-v2-openwisp-monitoring.png differ diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 000000000..357c6181e --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,56 @@ +Monitoring +========== + +.. seealso:: + + **Source code**: `github.com/openwisp/openwisp-monitoring + `_. + +The OpenWISP Monitoring module leverages the capabilities of Python and +the Django Framework to provide OpenWISP with robust network monitoring +features. Designed to be extensible, programmable, scalable, and +user-friendly, this module automates monitoring checks, alerts, and metric +collection, ensuring efficient and comprehensive network management. + +For a comprehensive overview of its features, please refer to the +:doc:`user/intro` page. + +The following diagram illustrates the role of the Monitoring module within +the OpenWISP architecture. + +.. figure:: images/architecture-v2-openwisp-monitoring.png + :target: ../_images/architecture-v2-openwisp-monitoring.png + :align: center + :alt: OpenWISP Architecture: Monitoring module + + **OpenWISP Architecture: highlighted monitoring module** + +.. important:: + + For an enhanced viewing experience, open the image above in a new + browser tab. + + Refer to :doc:`/general/architecture` for more information. + +.. toctree:: + :caption: Monitoring Module Usage Docs + :maxdepth: 1 + + ./user/intro.rst + ./user/quickstart.rst + ./user/device-health-status.rst + ./user/metrics.rst + ./user/checks.rst + ./user/device-checks-and-alert-settings + ./user/configuring-iperf3-check.rst + ./user/dashboard-monitoring-charts.rst + ./user/wifi-sessions.rst + ./user/rest-api.rst + ./user/settings.rst + ./user/management-commands.rst + +.. toctree:: + :caption: Monitoring Module Developer Docs + :maxdepth: 2 + + Developer Docs Index diff --git a/docs/partials/developer-docs.rst b/docs/partials/developer-docs.rst new file mode 100644 index 000000000..4047ab4b1 --- /dev/null +++ b/docs/partials/developer-docs.rst @@ -0,0 +1,12 @@ +.. note:: + + This documentation page is aimed at developers who want to customize, + change or extend the code of OpenWISP Monitoring in order to modify + its behavior (eg: for personal or commercial purposes or to fix a bug, + implement a new feature or contribute to the project in general). + + If you aren't a developer and you are looking for information on how + to use OpenWISP, please refer to: + + - :doc:`General OpenWISP Quickstart ` + - :doc:`OpenWISP Monitoring User Docs ` diff --git a/docs/user/checks.rst b/docs/user/checks.rst new file mode 100644 index 000000000..83328d494 --- /dev/null +++ b/docs/user/checks.rst @@ -0,0 +1,64 @@ +Checks +====== + +.. contents:: **Table of contents**: + :depth: 2 + :local: + +.. _ping_check: + +Ping +---- + +This check returns information on Ping Success Rate and RTT (Round trip +time). It creates charts like Ping Success Rate, Packet Loss and RTT. +These metrics are collected using the ``fping`` Linux program. You may +choose to disable auto creation of this check by setting +:ref:`openwisp_monitoring_auto_ping` to ``False``. + +You can change the default values used for ping checks using +:ref:`openwisp_monitoring_ping_check_config` setting. + +.. _config_applied_check: + +Configuration Applied +--------------------- + +This check ensures that the :doc:`openwisp-config agent +` is running and applying configuration +changes in a timely manner. You may choose to disable auto creation of +this check by using the setting +:ref:`openwisp_monitoring_auto_device_config_check`. + +This check runs periodically, but it is also triggered whenever the +configuration status of a device changes, this ensures the check reacts +quickly to events happening in the network and informs the user promptly +if there's anything that is not working as intended. + +.. _iperf3_check: + +Iperf3 +------ + +This check provides network performance measurements such as maximum +achievable bandwidth, jitter, datagram loss etc of the device using +`iperf3 utility `_. + +This check is **disabled by default**. You can enable auto creation of +this check by setting the :ref:`openwisp_monitoring_auto_iperf3` to +``True``. + +You can also :doc:`add the iperf3 check +` directly from the device page. + +It also supports tuning of various parameters. You can change the +parameters used for iperf3 checks (e.g. timing, port, username, password, +rsa_publc_key etc) using the +:ref:`openwisp_monitoring_iperf3_check_config` setting. + +.. note:: + + When setting :ref:`openwisp_monitoring_auto_iperf3` to ``True``, you + may need to update the :doc:`metric configuration + ` to enable alerts for the iperf3 + check. diff --git a/docs/user/configuring-iperf3-check.rst b/docs/user/configuring-iperf3-check.rst new file mode 100644 index 000000000..d3c44b93d --- /dev/null +++ b/docs/user/configuring-iperf3-check.rst @@ -0,0 +1,302 @@ +Configuring Iperf3 Check +======================== + +.. contents:: **Table of contents**: + :depth: 2 + :local: + +1. Make Sure Iperf3 is Installed on the Device +---------------------------------------------- + +Register your device to OpenWISP and make sure the `iperf3 openwrt package +`_ is installed on the +device, eg: + +.. code-block:: shell + + opkg install iperf3 # if using without authentication + opkg install iperf3-ssl # if using with authentication (read below for more info) + +2. Ensure SSH Access from OpenWISP is Enabled on your Devices +------------------------------------------------------------- + +Follow the steps in :doc:`"Configuring Push Operations" +` section of the documentation to allow +SSH access to you device from OpenWISP. + +.. important:: + + Make sure device connection is enabled & working with right update + strategy i.e. ``OpenWrt SSH``. + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/enable-openwrt-ssh.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/enable-openwrt-ssh.png + :alt: Enable ssh access from openwisp to device + :align: center + +3. Set Up and Configure Iperf3 Server Settings +---------------------------------------------- + +After having deployed your Iperf3 servers, you need to configure the +iperf3 settings on the django side of OpenWISP, see the `test project +settings for reference +`_. + +The host can be specified by hostname, IPv4 literal, or IPv6 literal. +Example: + +.. code-block:: python + + OPENWISP_MONITORING_IPERF3_CHECK_CONFIG = { + # 'org_pk' : {'host' : [], 'client_options' : {}} + "a9734710-db30-46b0-a2fc-01f01046fe4f": { + # Some public iperf3 servers + # https://iperf.fr/iperf-servers.php#public-servers + "host": ["iperf3.openwisp.io", "2001:db8::1", "192.168.5.2"], + "client_options": { + "port": 5209, + "udp": {"bitrate": "30M"}, + "tcp": {"bitrate": "0"}, + }, + }, + # another org + "b9734710-db30-46b0-a2fc-01f01046fe4f": { + # available iperf3 servers + "host": ["iperf3.openwisp2.io", "192.168.5.3"], + "client_options": { + "port": 5207, + "udp": {"bitrate": "50M"}, + "tcp": {"bitrate": "20M"}, + }, + }, + } + +.. note:: + + If an organization has more than one iperf3 server configured, then it + enables the iperf3 checks to run concurrently on different devices. If + all of the available servers are busy, then it will add the check back + in the queue. + +The celery-beat configuration for the iperf3 check needs to be added too: + +.. code-block:: python + + from celery.schedules import crontab + + # Celery TIME_ZONE should be equal to django TIME_ZONE + # In order to schedule run_iperf3_checks on the correct time intervals + CELERY_TIMEZONE = TIME_ZONE + CELERY_BEAT_SCHEDULE = { + # Other celery beat configurations + # Celery beat configuration for iperf3 check + "run_iperf3_checks": { + "task": "openwisp_monitoring.check.tasks.run_checks", + # https://docs.celeryq.dev/en/latest/userguide/periodic-tasks.html#crontab-schedules + # Executes check every 5 mins from 00:00 AM to 6:00 AM (night) + "schedule": crontab(minute="*/5", hour="0-6"), + # Iperf3 check path + "args": (["openwisp_monitoring.check.classes.Iperf3"],), + "relative": True, + } + } + +Once the changes are saved, you will need to restart all the processes. + +.. note:: + + We recommended to configure this check to run in non peak traffic + times to not interfere with standard traffic. + +4. Run the Check +---------------- + +This should happen automatically if you have celery-beat correctly +configured and running in the background. For testing purposes, you can +run this check manually using the :ref:`run_checks ` command. + +After that, you should see the iperf3 network measurements charts. + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/iperf3-charts.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/iperf3-charts.png + :alt: Iperf3 network measurement charts + +.. _iperf3_check_parameters: + +Iperf3 Check Parameters +----------------------- + +Currently, iperf3 check supports the following parameters: + +================== ======== ========================================= +**Parameter** **Type** **Default Value** +``host`` ``list`` ``[]`` +``username`` ``str`` ``''`` +``password`` ``str`` ``''`` +``rsa_public_key`` ``str`` ``''`` +``client_options`` ``dict`` Refer the :ref:`iperf3_client_parameters` + table below for available parameters +================== ======== ========================================= + +.. _iperf3_client_parameters: + +Iperf3 Client Options +~~~~~~~~~~~~~~~~~~~~~ + +=================== ======== ========================================== +**Parameters** **Type** **Default Value** +``port`` ``int`` ``5201`` +``time`` ``int`` ``10`` +``bytes`` ``str`` ``''`` +``blockcount`` ``str`` ``''`` +``window`` ``str`` ``0`` +``parallel`` ``int`` ``1`` +``reverse`` ``bool`` ``False`` +``bidirectional`` ``bool`` ``False`` +``connect_timeout`` ``int`` ``1000`` +``tcp`` ``dict`` Refer the :ref:`iperf3_client_tcp_options` + table below for available parameters +``udp`` ``dict`` Refer the :ref:`iperf3_client_udp_options` + table below for available parameters +=================== ======== ========================================== + +.. _iperf3_client_tcp_options: + +Iperf3 Client's TCP Options ++++++++++++++++++++++++++++ + +============== ======== ================= +**Parameters** **Type** **Default Value** +``bitrate`` ``str`` ``0`` +``length`` ``str`` ``128K`` +============== ======== ================= + +.. _iperf3_client_udp_options: + +Iperf3 Client's UDP Options ++++++++++++++++++++++++++++ + +============== ======== ================= +**Parameters** **Type** **Default Value** +``bitrate`` ``str`` ``30M`` +``length`` ``str`` ``0`` +============== ======== ================= + +To learn how to use these parameters, please see the :ref:`iperf3 check +configuration example `. + +Visit the `official documentation `_ to +learn more about the iperf3 parameters. + +Iperf3 Authentication +--------------------- + +By default iperf3 check runs without any kind of **authentication**, in +this section we will explain how to configure **RSA authentication** +between the **client** and the **server** to restrict connections to +authenticated clients. + +Server Side +~~~~~~~~~~~ + +1. Generate RSA Keypair ++++++++++++++++++++++++ + +.. code-block:: shell + + openssl genrsa -des3 -out private.pem 2048 + openssl rsa -in private.pem -outform PEM -pubout -out public_key.pem + openssl rsa -in private.pem -out private_key.pem -outform PEM + +After running the commands mentioned above, the public key will be stored +in ``public_key.pem`` which will be used in **rsa_public_key** parameter +in :ref:`openwisp_monitoring_iperf3_check_config` and the private key will +be contained in the file ``private_key.pem`` which will be used with +**--rsa-private-key-path** command option when starting the iperf3 server. + +2. Create User Credentials +++++++++++++++++++++++++++ + +.. code-block:: shell + + USER=iperfuser PASSWD=iperfpass + echo -n "{$USER}$PASSWD" | sha256sum | awk '{ print $1 }' + ---- + ee17a7f98cc87a6424fb52682396b2b6c058e9ab70e946188faa0714905771d7 #This is the hash of "iperfuser" + +Add the above hash with username in ``credentials.csv`` + +.. code-block:: shell + + # file format: username,sha256 + iperfuser,ee17a7f98cc87a6424fb52682396b2b6c058e9ab70e946188faa0714905771d7 + +3. Now Start the Iperf3 Server with Auth Options +++++++++++++++++++++++++++++++++++++++++++++++++ + +.. code-block:: shell + + iperf3 -s --rsa-private-key-path ./private_key.pem --authorized-users-path ./credentials.csv + +Client Side (OpenWrt Device) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +1. Install iperf3-ssl ++++++++++++++++++++++ + +Install the `iperf3-ssl openwrt package +`_ instead of the normal +`iperf3 openwrt package `_ +because the latter comes without support for authentication. + +You may also check your installed **iperf3 openwrt package** features: + +.. code-block:: shell + + root@vm-openwrt:- iperf3 -v + iperf 3.7 (cJSON 1.5.2) + Linux vm-openwrt 4.14.171 #0 SMP Thu Feb 27 21:05:12 2020 x86_64 + Optional features available: CPU affinity setting, IPv6 flow label, TCP congestion algorithm setting, + sendfile / zerocopy, socket pacing, authentication # contains 'authentication' + +.. _configure_iperf3_check_auth_parameters: + +2. Configure Iperf3 Check Auth Parameters ++++++++++++++++++++++++++++++++++++++++++ + +Now, add the following iperf3 authentication parameters to +:ref:`openwisp_monitoring_iperf3_check_config` in the settings: + +.. code-block:: python + + OPENWISP_MONITORING_IPERF3_CHECK_CONFIG = { + "a9734710-db30-46b0-a2fc-01f01046fe4f": { + "host": [ + "iperf1.openwisp.io", + "iperf2.openwisp.io", + "192.168.5.2", + ], + # All three parameters (username, password, rsa_publc_key) + # are required for iperf3 authentication + "username": "iperfuser", + "password": "iperfpass", + # Add RSA public key without any headers + # ie. -----BEGIN PUBLIC KEY-----, -----BEGIN END KEY----- + "rsa_public_key": ( + """ + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwuEm+iYrfSWJOupy6X3N + dxZvUCxvmoL3uoGAs0O0Y32unUQrwcTIxudy38JSuCccD+k2Rf8S4WuZSiTxaoea + 6Du99YQGVZeY67uJ21SWFqWU+w6ONUj3TrNNWoICN7BXGLE2BbSBz9YaXefE3aqw + GhEjQz364Itwm425vHn2MntSp0weWb4hUCjQUyyooRXPrFUGBOuY+VvAvMyAG4Uk + msapnWnBSxXt7Tbb++A5XbOMdM2mwNYDEtkD5ksC/x3EVBrI9FvENsH9+u/8J9Mf + 2oPl4MnlCMY86MQypkeUn7eVWfDnseNky7TyC0/IgCXve/iaydCCFdkjyo1MTAA4 + BQIDAQAB + """ + ), + "client_options": { + "port": 5209, + "udp": {"bitrate": "20M"}, + "tcp": {"bitrate": "0"}, + }, + } + } diff --git a/docs/user/dashboard-monitoring-charts.rst b/docs/user/dashboard-monitoring-charts.rst new file mode 100644 index 000000000..e2a6a86aa --- /dev/null +++ b/docs/user/dashboard-monitoring-charts.rst @@ -0,0 +1,16 @@ +Dashboard Monitoring Charts +=========================== + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/dashboard-charts.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/dashboard-charts.png + :align: center + +OpenWISP Monitoring adds two timeseries charts to the admin dashboard: + +- **General WiFi clients Chart**: Shows the number of connected clients to + the WiFi interfaces of devices in the network. +- **General traffic Chart**: Shows the amount of traffic flowing in the + network. + +You can configure the interfaces included in the **General traffic chart** +using the :ref:`openwisp_monitoring_dashboard_traffic_chart` setting. diff --git a/docs/user/device-checks-and-alert-settings.rst b/docs/user/device-checks-and-alert-settings.rst new file mode 100644 index 000000000..ae7e73e5c --- /dev/null +++ b/docs/user/device-checks-and-alert-settings.rst @@ -0,0 +1,83 @@ +Managing Device Checks & Alert Settings +======================================= + +We can add checks and define alert settings directly from the **device +page**. + +To add a check, you just need to select an available **check type** as +shown below: + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/device-inline-check.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/device-inline-check.png + :align: center + +The following example shows how to use the +:ref:`openwisp_monitoring_metrics` setting to reconfigure the system for +:ref:`iperf3 check ` to send an alert if the measured **TCP +bandwidth** has been less than **10Mbit/s** for more than **2 days**. + +1. By default, :ref:`Iperf3 checks ` come with default alert +settings, but it is easy to customize alert settings through the device +page as shown below: + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/device-inline-alertsettings.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/device-inline-alertsettings.png + :align: center + +2. Now, add the following notification configuration to send an alert for + **TCP bandwidth**: + +.. code-block:: python + + # Main project settings.py + from django.utils.translation import gettext_lazy as _ + + OPENWISP_MONITORING_METRICS = { + "iperf3": { + "notification": { + "problem": { + "verbose_name": "Iperf3 PROBLEM", + "verb": _("Iperf3 bandwidth is less than normal value"), + "level": "warning", + "email_subject": _( + "[{site.name}] PROBLEM: {notification.target} {notification.verb}" + ), + "message": _( + "The device [{notification.target}]({notification.target_link}) " + "{notification.verb}." + ), + }, + "recovery": { + "verbose_name": "Iperf3 RECOVERY", + "verb": _("Iperf3 bandwidth now back to normal"), + "level": "info", + "email_subject": _( + "[{site.name}] RECOVERY: {notification.target} {notification.verb}" + ), + "message": _( + "The device [{notification.target}]({notification.target_link}) " + "{notification.verb}." + ), + }, + }, + }, + } + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/alert_field_warn.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/alert_field_warn.png + :align: center + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/alert_field_info.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/alert_field_info.png + :align: center + +.. note:: + + To access the features described above, the user must have permissions + for ``Check`` and ``AlertSetting`` inlines, these permissions are + included by default in the "Administrator" and "Operator" groups and + are shown in the screenshot below. + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/inline-permissions.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/inline-permissions.png + :align: center diff --git a/docs/user/device-health-status.rst b/docs/user/device-health-status.rst new file mode 100644 index 000000000..7dfd4e4ce --- /dev/null +++ b/docs/user/device-health-status.rst @@ -0,0 +1,38 @@ +Device Health Status +==================== + +The possible values for the health status field +(``DeviceMonitoring.status``) are explained below. + +``UNKNOWN`` +----------- + +Whenever a new device is created it will have ``UNKNOWN`` as it's default +Heath Status. + +It implies that the system doesn't know whether the device is reachable +yet. + +``OK`` +------ + +Everything is working normally. + +``PROBLEM`` +----------- + +One of the metrics has a value which is not in the expected range (the +threshold value set in the alert settings has been crossed). + +Example: CPU usage should be less than 90% but current value is at 95%. + +``CRITICAL`` +------------ + +One of the metrics defined in +``OPENWISP_MONITORING_CRITICAL_DEVICE_METRICS`` has a value which is not +in the expected range (the threshold value set in the alert settings has +been crossed). + +Example: ping is by default a critical metric which is expected to be +always 1 (reachable). diff --git a/docs/user/intro.rst b/docs/user/intro.rst new file mode 100644 index 000000000..bd4f07706 --- /dev/null +++ b/docs/user/intro.rst @@ -0,0 +1,51 @@ +Monitoring: Features +==================== + +OpenWISP provides the following monitoring capabilities: + +- An overview of the status of the network is shown in the admin + dashboard, a chart shows the percentages of devices which are online, + offline or having issues; there are also :doc:`two timeseries charts + which show the total unique WiFI clients and the traffic flowing to the + network `, a geographic map is also + available for those who use the geographic features of OpenWISP +- Collection of monitoring information in a timeseries database (currently + only influxdb is supported) +- Allows to browse alerts easily from the user interface with one click +- Collects and displays :ref:`device status ` information + like uptime, RAM status, CPU load averages, Interface properties and + addresses, WiFi interface status and associated clients, Neighbors + information, DHCP Leases, Disk/Flash status +- Monitoring charts for :ref:`ping success rate `, + :ref:`packet loss `, :ref:`round trip time (latency) + `, :ref:`associated wifi clients `, + :ref:`interface traffic `, :ref:`RAM usage `, + :ref:`CPU load `, :ref:`flash/disk usage `, mobile + signal (LTE/UMTS/GSM :ref:`signal strength `, + :ref:`signal quality `, :ref:`access technology + in use `), :ref:`bandwidth `, + :ref:`transferred data `, :ref:`restransmits `, + :ref:`jitter `, :ref:`datagram `, :ref:`datagram loss + ` +- Maintains a record of :doc:`WiFi sessions ` with clients' + MAC address and vendor, session start and stop time and connected device + along with other information +- Charts can be viewed at resolutions of the last 1 day, 3 days, 7 days, + 30 days, and 365 days +- Configurable alerts +- CSV Export of monitoring data +- Possibility to configure additional :ref:`Metrics + ` and :ref:`Charts + ` +- :doc:`Extensible active check system + `: it's possible to write additional + checks that are run periodically using python classes +- Extensible :ref:`metrics ` and :ref:`charts + `: it's possible to define new metrics and + new charts +- API to retrieve the chart metrics and status information of each device + based on `NetJSON DeviceMonitoring + `_ +- :ref:`Iperf3 check ` that provides network performance + measurements such as maximum achievable bandwidth, jitter, datagram loss + etc of the openwrt device using `iperf3 utility `_ diff --git a/docs/user/management-commands.rst b/docs/user/management-commands.rst new file mode 100644 index 000000000..713ffaecb --- /dev/null +++ b/docs/user/management-commands.rst @@ -0,0 +1,29 @@ +Management Commands +=================== + +.. _run_checks: + +``run_checks`` +-------------- + +This command will execute all the :doc:`available checks ` for all +the devices. By default checks are run periodically by *celery beat*. + +Example usage: + +.. code-block:: shell + + cd tests/ + ./manage.py run_checks + +``migrate_timeseries`` +---------------------- + +This command triggers asynchronous migration of the time-series database. + +Example usage: + +.. code-block:: shell + + cd tests/ + ./manage.py migrate_timeseries diff --git a/docs/user/metrics.rst b/docs/user/metrics.rst new file mode 100644 index 000000000..aaff7bc67 --- /dev/null +++ b/docs/user/metrics.rst @@ -0,0 +1,304 @@ +Metrics +======= + +.. contents:: **Table of contents**: + :depth: 2 + :local: + +.. _device_status: + +Device Status +------------- + +This metric stores the status of the device for viewing purposes. + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/device-status-1.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/device-status-1.png + :align: center + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/device-status-2.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/device-status-2.png + :align: center + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/device-status-3.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/device-status-3.png + :align: center + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/device-status-4.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/device-status-4.png + :align: center + +.. _ping: + +Ping +---- + +================== ================================================== +**measurement**: ``ping`` +**types**: ``int`` (reachable and loss), ``float`` (rtt) +**fields**: ``reachable``, ``loss``, ``rtt_min``, ``rtt_max``, + ``rtt_avg`` +**configuration**: ``ping`` +**charts**: ``uptime`` (Ping Success Rate), ``packet_loss``, + ``rtt`` +================== ================================================== + +**Ping Success Rate**: + +.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/1.1/ping-success-rate.png + :target: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/1.1/ping-success-rate.png + :align: center + +**Packet loss**: + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/packet-loss.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/packet-loss.png + :align: center + +**Round Trip Time**: + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/rtt.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/rtt.png + :align: center + +.. _traffic: + +Traffic +------- + +================== ========================================================================== +**measurement**: ``traffic`` +**type**: ``int`` +**fields**: ``rx_bytes``, ``tx_bytes`` +**tags**: .. code-block:: python + + { + "organization_id": "", + "ifname": "", + # optional + "location_id": "", + "floorplan_id": "", + } +**configuration**: ``traffic`` +**charts**: ``traffic`` +================== ========================================================================== + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/traffic.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/traffic.png + :align: center + +.. _wifi_clients: + +WiFi Clients +------------ + +================== ========================================================================== +**measurement**: ``wifi_clients`` +**type**: ``int`` +**fields**: ``clients`` +**tags**: .. code-block:: python + + { + "organization_id": "", + "ifname": "", + # optional + "location_id": "", + "floorplan_id": "", + } +**configuration**: ``clients`` +**charts**: ``wifi_clients`` +================== ========================================================================== + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/wifi-clients.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/wifi-clients.png + :align: center + +.. _memory_usage: + +Memory Usage +------------ + +================== ==================================================== +**measurement**: ```` +**type**: ``float`` +**fields**: ``percent_used``, ``free_memory``, ``total_memory``, + ``buffered_memory``, ``shared_memory``, + ``cached_memory``, ``available_memory`` +**configuration**: ``memory`` +**charts**: ``memory`` +================== ==================================================== + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/memory.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/memory.png + :align: center + +.. _cpu_load: + +CPU Load +-------- + +================== ================================================== +**measurement**: ``load`` +**type**: ``float`` +**fields**: ``cpu_usage``, ``load_1``, ``load_5``, ``load_15`` +**configuration**: ``load`` +**charts**: ``load`` +================== ================================================== + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/cpu-load.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/cpu-load.png + :align: center + +.. _disk_usage: + +Disk Usage +---------- + +================== ============= +**measurement**: ``disk`` +**type**: ``float`` +**fields**: ``used_disk`` +**configuration**: ``disk`` +**charts**: ``disk`` +================== ============= + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/disk-usage.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/disk-usage.png + :align: center + +.. _mobile_signal_strength: + +Mobile Signal Strength +---------------------- + +================== ===================================== +**measurement**: ``signal_strength`` +**type**: ``float`` +**fields**: ``signal_strength``, ``signal_power`` +**configuration**: ``signal_strength`` +**charts**: ``signal_strength`` +================== ===================================== + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/signal-strength.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/signal-strength.png + :align: center + +.. _mobile_signal_quality: + +Mobile Signal Quality +--------------------- + +================== ====================================== +**measurement**: ``signal_quality`` +**type**: ``float`` +**fields**: ``signal_quality``, ``signal_quality`` +**configuration**: ``signal_quality`` +**charts**: ``signal_quality`` +================== ====================================== + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/signal-quality.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/signal-quality.png + :align: center + +.. _mobile_access_technology_in_use: + +Mobile Access Technology in Use +------------------------------- + +================== =============== +**measurement**: ``access_tech`` +**type**: ``int`` +**fields**: ``access_tech`` +**configuration**: ``access_tech`` +**charts**: ``access_tech`` +================== =============== + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/access-technology.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/access-technology.png + :align: center + +.. _iperf3: + +Iperf3 +------ + +================== ===================================================== +**measurement**: ``iperf3`` +**types**: | ``int`` (iperf3_result, sent_bytes_tcp, + received_bytes_tcp, retransmits, sent_bytes_udp, + total_packets, lost_packets), + | ``float`` (sent_bps_tcp, received_bps_tcp, + sent_bps_udp, jitter, lost_percent) +**fields**: | ``iperf3_result``, ``sent_bps_tcp``, + ``received_bps_tcp``, ``sent_bytes_tcp``, + ``received_bytes_tcp``, ``retransmits``, + | ``sent_bps_udp``, ``sent_bytes_udp``, ``jitter``, + ``total_packets``, ``lost_packets``, + ``lost_percent`` +**configuration**: ``iperf3`` +**charts**: ``bandwidth``, ``transfer``, ``retransmits``, + ``jitter``, ``datagram``, ``datagram_loss`` +================== ===================================================== + +**Bandwidth**: + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/bandwidth.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/bandwidth.png + :align: center + +**Transferred Data**: + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/transferred-data.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/transferred-data.png + :align: center + +**Retransmits**: + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/retransmits.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/retransmits.png + :align: center + +**Jitter**: + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/jitter.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/jitter.png + :align: center + +**Datagram**: + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/datagram.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/datagram.png + :align: center + +**Datagram loss**: + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/datagram-loss.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/datagram-loss.png + :align: center + +For more info on how to configure and use Iperf3, please refer to +:doc:`configuring-iperf3-check`. + +.. note:: + + Iperf3 charts uses ``connect_points=True`` in :ref:`default chart + configuration ` that joins it's individual + chart data points. + +Passive vs Active Metric Collection +----------------------------------- + +The the different :doc:`device metric <./metrics>` collected by OpenWISP +Monitoring can be divided in two categories: + +1. **metrics collected actively by OpenWISP**: these metrics are collected + by the celery workers running on the OpenWISP server, which + continuously sends network requests to the devices and store the + results; +2. **metrics collected passively by OpenWISP**: these metrics are sent by + the :doc:`OpenWrt Monitoring Agent ` + installed on the network devices and are collected by OpenWISP via its + REST API. + +The :doc:`checks` section of the documentation lists the currently +implemented **active checks**. diff --git a/docs/user/quickstart.rst b/docs/user/quickstart.rst new file mode 100644 index 000000000..becba743e --- /dev/null +++ b/docs/user/quickstart.rst @@ -0,0 +1,26 @@ +Quick Start Guide +================= + +.. contents:: **Table of contents**: + :depth: 2 + :local: + +.. _install_monitoring_packages_on_device: + +Install Monitoring Packages on the Device +----------------------------------------- + +First of all, :doc:`Install the OpenWrt Monitoring Agent +` on your device. + +The agent is responsible for collecting some of the :doc:`monitoring +metrics <./metrics>` from the device and sending these to the server. It's +required to collect interface traffic, WiFi clients, CPU load, memory +usage, storage usage, cellular signal strength, etc. + +.. _openwisp_reach_devices: + +Make Sure OpenWISP can Reach your Devices +----------------------------------------- + +Please make sure that :doc:`OpenWISP can reach your devices `. diff --git a/docs/user/rest-api.rst b/docs/user/rest-api.rst new file mode 100644 index 000000000..704927166 --- /dev/null +++ b/docs/user/rest-api.rst @@ -0,0 +1,286 @@ +REST API Reference +================== + +.. contents:: **Table of contents**: + :depth: 1 + :local: + +.. _monitoring_live_documentation: + +Live Documentation +------------------ + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/api-doc.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/api-doc.png + +A general live API documentation (following the OpenAPI specification) at +``/api/v1/docs/``. + +.. _monitoring_browsable_web_interface: + +Browsable Web Interface +----------------------- + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/api-ui-1.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/api-ui-1.png + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/api-ui-2.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/api-ui-2.png + +Additionally, opening any of the endpoints :ref:`listed below +` directly in the browser will show the +`browsable API interface of Django-REST-Framework +`_, which +makes it even easier to find out the details of each endpoint. + +.. _monitoring_rest_endpoints: + +List of Endpoints +----------------- + +Since the detailed explanation is contained in the +:ref:`monitoring_live_documentation` and in the +:ref:`monitoring_browsable_web_interface` of each point, here we'll +provide just a list of the available endpoints, for further information +please open the URL of the endpoint in your browser. + +Retrieve General Monitoring Charts +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/monitoring/dashboard/ + +This API endpoint is used to show dashboard monitoring charts. It supports +multi-tenancy and allows filtering monitoring data by +``organization_slug``, ``location_id`` and ``floorplan_id`` e.g.: + +.. code-block:: text + + GET /api/v1/monitoring/dashboard/?organization_slug=,&location_id=,&floorplan_id=, + +- When retrieving chart data, the ``time`` parameter allows to specify the + time frame, eg: + + - ``1d``: returns data of the last day + - ``3d``: returns data of the last 3 days + - ``7d``: returns data of the last 7 days + - ``30d``: returns data of the last 30 days + - ``365d``: returns data of the last 365 days + +- In alternative to ``time`` it is possible to request chart data for a + custom date range by using the ``start`` and ``end`` parameters, eg: + +.. code-block:: text + + GET /api/v1/monitoring/dashboard/?start={start_datetime}&end={end_datetime} + +.. note:: + + The ``start`` and ``end`` parameters should be in the format + ``YYYY-MM-DD H:M:S``, otherwise 400 Bad Response will be returned. + +Retrieve Device Charts and Device Status Data +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/monitoring/device/{pk}/?key={key}&status=true&time={timeframe} + +The format used for Device Status is inspired by `NetJSON DeviceMonitoring +`_. + +.. note:: + + - If the request is made without ``?status=true`` the response will + contain only charts data and will not include any device status + information (current load average, ARP table, DCHP leases, etc.). + - When retrieving chart data, the ``time`` parameter allows to specify + the time frame, eg: + + - ``1d``: returns data of the last day + - ``3d``: returns data of the last 3 days + - ``7d``: returns data of the last 7 days + - ``30d``: returns data of the last 30 days + - ``365d``: returns data of the last 365 days + +- In alternative to ``time`` it is possible to request chart data for a + custom date range by using the ``start`` and ``end`` parameters, eg: +- The response contains device information, monitoring status (health + status), a list of metrics with their respective statuses, chart data + and device status information (only if ``?status=true``). +- This endpoint can be accessed with session authentication, token + authentication, or alternatively with the device key passed as query + string parameter as shown below (``?key={key}``); note: this method is + meant to be used by the devices. + +.. code-block:: text + + GET /api/v1/monitoring/device/{pk}/?key={key}&status=true&start={start_datetime}&end={end_datetime} + +.. note:: + + The ``start`` and ``end`` parameters must be in the format + ``YYYY-MM-DD H:M:S``, otherwise 400 Bad Response will be returned. + +List Device Monitoring Information +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/monitoring/device/ + +.. note:: + + - The response contains device information and monitoring status + (health status), but it does not include the information and health + status of the specific metrics, this information can be retrieved in + the detail endpoint of each device. + - This endpoint can be accessed with session authentication and token + authentication. + +**Available filters** + +Data can be filtered by health status (e.g. critical, ok, problem, and +unknown) to obtain the list of devices in the corresponding status, for +example, to retrieve the list of devices which are in critical conditions +(eg: unreachable), the following will work: + +.. code-block:: text + + GET /api/v1/monitoring/device/?monitoring__status=critical + +To filter a list of device monitoring data based on their organization, +you can use the ``organization_id``. + +.. code-block:: text + + GET /api/v1/monitoring/device/?organization={organization_id} + +To filter a list of device monitoring data based on their organization +slug, you can use the ``organization_slug``. + +.. code-block:: text + + GET /api/v1/monitoring/device/?organization_slug={organization_slug} + +Collect Device Metrics and Status +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + POST /api/v1/monitoring/device/{pk}/?key={key}&time={datetime} + +If data is latest then an additional parameter current can also be passed. +For e.g.: + +.. code-block:: text + + POST /api/v1/monitoring/device/{pk}/?key={key}&time={datetime}¤t=true + +The format used for Device Status is inspired by `NetJSON DeviceMonitoring +`_. + +.. note:: + + The device data will be saved in the timeseries database using the + date time specified ``time``, this should be in the format + ``%d-%m-%Y_%H:%M:%S.%f``, otherwise 400 Bad Response will be returned. + +If the request is made without passing the ``time`` argument, the server +local time will be used. + +The ``time`` parameter was added to support :ref:`resilient collection and +sending of data by the OpenWISP Monitoring Agent +`, this feature allows sending +data collected while the device is offline. + +List Nearby Devices +~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/monitoring/device/{pk}/nearby-devices/ + +Returns list of nearby devices along with respective distance (in metres) +and monitoring status. + +**Available filters** + +The list of nearby devices provides the following filters: + +- ``organization`` (Organization ID of the device) +- ``organization__slug`` (Organization slug of the device) +- ``monitoring__status`` (Monitoring status (``unknown``, ``ok``, + ``problem``, or ``critical``)) +- ``model`` (Pipe `|` separated list of device models) +- ``distance__lte`` (Distance in metres) + +Here's a few examples: + +.. code-block:: text + + GET /api/v1/monitoring/device/{pk}/nearby-devices/?organization={organization_id} + GET /api/v1/monitoring/device/{pk}/nearby-devices/?organization__slug={organization_slug} + GET /api/v1/monitoring/device/{pk}/nearby-devices/?monitoring__status={monitoring_status} + GET /api/v1/monitoring/device/{pk}/nearby-devices/?model={model1,model2} + GET /api/v1/monitoring/device/{pk}/nearby-devices/?distance__lte={distance} + +List WiFi Session +~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/monitoring/wifi-session/ + +**Available filters** + +The list of wifi session provides the following filters: + +- ``device__organization`` (Organization ID of the device) +- ``device`` (Device ID) +- ``device__group`` (Device group ID) +- ``start_time`` (Start time of the wifi session) +- ``stop_time`` (Stop time of the wifi session) + +Here's a few examples: + +.. code-block:: text + + GET /api/v1/monitoring/wifi-session/?device__organization={organization_id} + GET /api/v1/monitoring/wifi-session/?device={device_id} + GET /api/v1/monitoring/wifi-session/?device__group={group_id} + GET /api/v1/monitoring/wifi-session/?start_time={stop_time} + GET /api/v1/monitoring/wifi-session/?stop_time={stop_time} + +.. note:: + + Both ``start_time`` and ``stop_time`` support greater than or equal + to, as well as less than or equal to, filter lookups. + +For example: + +.. code-block:: text + + GET /api/v1/monitoring/wifi-session/?start_time__gt={start_time} + GET /api/v1/monitoring/wifi-session/?start_time__gte={start_time} + GET /api/v1/monitoring/wifi-session/?stop_time__lt={stop_time} + GET /api/v1/monitoring/wifi-session/?stop_time__lte={stop_time} + +Get WiFi Session +~~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/monitoring/wifi-session/{id}/ + +Pagination +~~~~~~~~~~ + +WiFi session endpoint support the ``page_size`` parameter that allows +paginating the results in conjunction with the page parameter. + +.. code-block:: text + + GET /api/v1/monitoring/wifi-session/?page_size=10 + GET /api/v1/monitoring/wifi-session/?page_size=10&page=1 diff --git a/docs/user/settings.rst b/docs/user/settings.rst new file mode 100644 index 000000000..aa3c0dac4 --- /dev/null +++ b/docs/user/settings.rst @@ -0,0 +1,844 @@ +Settings +======== + +.. include:: /partials/settings-note.rst + +.. _timeseries_database: + +``TIMESERIES_DATABASE`` +----------------------- + +============ ========= +**type**: ``str`` +**default**: see below +============ ========= + +.. code-block:: python + + TIMESERIES_DATABASE = { + "BACKEND": "openwisp_monitoring.db.backends.influxdb", + "USER": "openwisp", + "PASSWORD": "openwisp", + "NAME": "openwisp2", + "HOST": "localhost", + "PORT": "8086", + "OPTIONS": { + "udp_writes": False, + "udp_port": 8089, + }, + } + +The following table describes all keys available in +``TIMESERIES_DATABASE`` setting: + +============ ============================================================= +**Key** ``Description`` +``BACKEND`` The timeseries database backend to use. You can select one of + the backends located in ``openwisp_monitoring.db.backends`` +``USER`` User for logging into the timeseries database +``PASSWORD`` Password of the timeseries database user +``NAME`` Name of the timeseries database +``HOST`` IP address/hostname of machine where the timeseries database + is running +``PORT`` Port for connecting to the timeseries database +``OPTIONS`` These settings depends on the timeseries backend. Refer the + :ref:`timeseries_backend_options` table below for available + options +============ ============================================================= + +.. _timeseries_backend_options: + +Timeseries Database Options +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +============== ===================================================== +``udp_writes`` Whether to use UDP for writing data to the timeseries + database +``udp_port`` Timeseries database port for writing data using UDP +============== ===================================================== + +.. important:: + + UDP packets can have a maximum size of 64KB. When using UDP for + writing timeseries data, if the size of the data exceeds 64KB, TCP + mode will be used instead. + +.. note:: + + If you want to use the ``openwisp_monitoring.db.backends.influxdb`` + backend with UDP writes enabled, then you need to enable two different + ports for UDP (each for different retention policy) in your InfluxDB + configuration. The UDP configuration section of your InfluxDB should + look similar to the following: + + .. code-block:: text + + # For writing data with the "default" retention policy + [[udp]] + enabled = true + bind-address = "127.0.0.1:8089" + database = "openwisp2" + + # For writing data with the "short" retention policy + [[udp]] + enabled = true + bind-address = "127.0.0.1:8090" + database = "openwisp2" + retention-policy = 'short' + + If you are using :doc:`ansible-openwisp2 ` for + deploying OpenWISP, you can set the ``influxdb_udp_mode`` ansible + variable to ``true`` in your playbook, this will make the ansible role + automatically configure the InfluxDB UDP listeners. You can refer to + the `ansible-ow-influxdb's + `_ (a + dependency of ansible-openwisp2) documentation to learn more. + +.. _openwisp_monitoring_default_retention_policy: + +``OPENWISP_MONITORING_DEFAULT_RETENTION_POLICY`` +------------------------------------------------ + +============ ======================== +**type**: ``str`` +**default**: ``26280h0m0s`` (3 years) +============ ======================== + +The default retention policy that applies to the timeseries data. + +.. _openwisp_monitoring_short_retention_policy: + +``OPENWISP_MONITORING_SHORT_RETENTION_POLICY`` +---------------------------------------------- + +============ =========== +**type**: ``str`` +**default**: ``24h0m0s`` +============ =========== + +The default retention policy used to store raw device data. + +This data is only used to assess the recent status of devices, keeping it +for a long time would not add much benefit and would cost a lot more in +terms of disk space. + +.. _openwisp_monitoring_auto_ping: + +``OPENWISP_MONITORING_AUTO_PING`` +--------------------------------- + +============ ======== +**type**: ``bool`` +**default**: ``True`` +============ ======== + +Whether ping checks are created automatically for devices. + +.. _openwisp_monitoring_ping_check_config: + +``OPENWISP_MONITORING_PING_CHECK_CONFIG`` +----------------------------------------- + +============ ======== +**type**: ``dict`` +**default**: ``{}`` +============ ======== + +This setting allows to override the default ping check configuration +defined in +``openwisp_monitoring.check.classes.ping.DEFAULT_PING_CHECK_CONFIG``. + +For example, if you want to change only the **timeout** of ``ping`` you +can use: + +.. code-block:: python + + OPENWISP_MONITORING_PING_CHECK_CONFIG = { + "timeout": { + "default": 1000, + }, + } + +If you are overriding the default value for any parameter beyond the +maximum or minimum value defined in +``openwisp_monitoring.check.classes.ping.DEFAULT_PING_CHECK_CONFIG``, you +will also need to override the ``maximum`` or ``minimum`` fields as +following: + +.. code-block:: python + + OPENWISP_MONITORING_PING_CHECK_CONFIG = { + "timeout": { + "default": 2000, + "minimum": 1500, + "maximum": 2500, + }, + } + +.. note:: + + Above ``maximum`` and ``minimum`` values are only used for validating + custom parameters of a ``Check`` object. + +.. _openwisp_monitoring_auto_device_config_check: + +``OPENWISP_MONITORING_AUTO_DEVICE_CONFIG_CHECK`` +------------------------------------------------ + +============ ======== +**type**: ``bool`` +**default**: ``True`` +============ ======== + +This setting allows you to choose whether :ref:`config_applied +` checks should be created automatically for newly +registered devices. It's enabled by default. + +.. _openwisp_monitoring_config_check_interval: + +``OPENWISP_MONITORING_CONFIG_CHECK_INTERVAL`` +--------------------------------------------- + +============ ======= +**type**: ``int`` +**default**: ``5`` +============ ======= + +This setting allows you to configure the config check interval used by +:ref:`config_applied `. By default it is set to 5 +minutes. + +.. _openwisp_monitoring_auto_iperf3: + +``OPENWISP_MONITORING_AUTO_IPERF3`` +----------------------------------- + +============ ========= +**type**: ``bool`` +**default**: ``False`` +============ ========= + +This setting allows you to choose whether :ref:`iperf3 ` +checks should be created automatically for newly registered devices. It's +disabled by default. + +.. _openwisp_monitoring_iperf3_check_config: + +``OPENWISP_MONITORING_IPERF3_CHECK_CONFIG`` +------------------------------------------- + +============ ======== +**type**: ``dict`` +**default**: ``{}`` +============ ======== + +This setting allows to override the default iperf3 check configuration +defined in +``openwisp_monitoring.check.classes.iperf3.DEFAULT_IPERF3_CHECK_CONFIG``. + +For example, you can change the values of :ref:`supported iperf3 check +parameters `. + +.. code-block:: python + + OPENWISP_MONITORING_IPERF3_CHECK_CONFIG = { + # 'org_pk' : {'host' : [], 'client_options' : {}} + "a9734710-db30-46b0-a2fc-01f01046fe4f": { + # Some public iperf3 servers + # https://iperf.fr/iperf-servers.php#public-servers + "host": ["iperf3.openwisp.io", "2001:db8::1", "192.168.5.2"], + "client_options": { + "port": 6209, + # Number of parallel client streams to run + # note that iperf3 is single threaded + # so if you are CPU bound this will not + # yield higher throughput + "parallel": 5, + # Set the connect_timeout (in milliseconds) for establishing + # the initial control connection to the server, the lower the value + # the faster the down iperf3 server will be detected (ex. 1000 ms (1 sec)) + "connect_timeout": 1000, + # Window size / socket buffer size + "window": "300K", + # Only one reverse condition can be chosen, + # reverse or bidirectional + "reverse": True, + # Only one test end condition can be chosen, + # time, bytes or blockcount + "blockcount": "1K", + "udp": {"bitrate": "50M", "length": "1460K"}, + "tcp": {"bitrate": "20M", "length": "256K"}, + }, + } + } + +``OPENWISP_MONITORING_IPERF3_CHECK_DELETE_RSA_KEY`` +--------------------------------------------------- + +============ ======== +**type**: ``bool`` +**default**: ``True`` +============ ======== + +This setting allows you to set whether :ref:`iperf3 check RSA public key +` will be deleted after successful +completion of the check or not. + +``OPENWISP_MONITORING_IPERF3_CHECK_LOCK_EXPIRE`` +------------------------------------------------ + +============ ======= +**type**: ``int`` +**default**: ``600`` +============ ======= + +This setting allows you to set a cache lock expiration time for the iperf3 +check when running on multiple servers. Make sure it is always greater +than the total iperf3 check time, i.e. greater than the TCP + UDP test +time. By default, it is set to **600 seconds (10 mins)**. + +.. _openwisp_monitoring_auto_charts: + +``OPENWISP_MONITORING_AUTO_CHARTS`` +----------------------------------- + +============ ====================================================== +**type**: ``list`` +**default**: ``('traffic', 'wifi_clients', 'uptime', 'packet_loss', + 'rtt')`` +============ ====================================================== + +Automatically created charts. + +.. _openwisp_monitoring_critical_device_metrics: + +``OPENWISP_MONITORING_CRITICAL_DEVICE_METRICS`` +----------------------------------------------- + +============ ================================================ +**type**: ``list`` of ``dict`` objects +**default**: ``[{'key': 'ping', 'field_name': 'reachable'}]`` +============ ================================================ + +Device metrics that are considered critical: + +when a value crosses the boundary defined in the "threshold value" field +of the alert settings related to one of these metric types, the health +status of the device related to the metric moves into ``CRITICAL``. + +By default, if devices are not reachable by pings they are flagged as +``CRITICAL``. + +.. _openwisp_monitoring_health_status_labels: + +``OPENWISP_MONITORING_HEALTH_STATUS_LABELS`` +-------------------------------------------- + +============ ========================================================== +**type**: ``dict`` +**default**: ``{'unknown': 'unknown', 'ok': 'ok', 'problem': 'problem', + 'critical': 'critical'}`` +============ ========================================================== + +This setting allows to change the health status labels, for example, if we +want to use ``online`` instead of ``ok`` and ``offline`` instead of +``critical``, you can use the following configuration: + +.. code-block:: python + + OPENWISP_MONITORING_HEALTH_STATUS_LABELS = { + "ok": "online", + "problem": "problem", + "critical": "offline", + } + +.. _openwisp_monitoring_wifi_sessions_enabled: + +``OPENWISP_MONITORING_WIFI_SESSIONS_ENABLED`` +--------------------------------------------- + +============ ======== +**type**: ``bool`` +**default**: ``True`` +============ ======== + +Setting this to ``False`` will disable :doc:`wifi-sessions` feature. + +.. _openwisp_monitoring_management_ip_only: + +``OPENWISP_MONITORING_MANAGEMENT_IP_ONLY`` +------------------------------------------ + +============ ======== +**type**: ``bool`` +**default**: ``True`` +============ ======== + +By default, only the management IP will be used to perform active checks +to the devices. + +If the devices are connecting to your OpenWISP instance using a shared +layer2 network, hence the OpenWSP server can reach the devices using the +``last_ip`` field, you can set this to ``False``. + +.. note:: + + If this setting is not configured, it will fallback to the value of + :ref:`OPENWISP_CONTROLLER_MANAGEMENT_IP_ONLY setting + `. If + ``OPENWISP_CONTROLLER_MANAGEMENT_IP_ONLY`` also not configured, then + it will fallback to ``True``. + +.. _openwisp_monitoring_device_recovery_detection: + +``OPENWISP_MONITORING_DEVICE_RECOVERY_DETECTION`` +------------------------------------------------- + +============ ======== +**type**: ``bool`` +**default**: ``True`` +============ ======== + +When device recovery detection is enabled, recoveries are discovered as +soon as a device contacts the openwisp system again (eg: to get the +configuration checksum or to send monitoring metrics). + +This feature is enabled by default. + +If you use OpenVPN as the management VPN, you may want to check out a +similar integration built in **openwisp-network-topology**: when the +status of an OpenVPN link changes (detected by monitoring the status +information of OpenVPN), the network topology module will trigger the +monitoring checks. For more information see: :doc:`Network Topology Device +Integration `. + +.. _openwisp_monitoring_mac_vendor_detection: + +``OPENWISP_MONITORING_MAC_VENDOR_DETECTION`` +-------------------------------------------- + +============ ======== +**type**: ``bool`` +**default**: ``True`` +============ ======== + +Indicates whether mac addresses will be complemented with hardware vendor +information by performing lookups on the OUI (Organization Unique +Identifier) table. + +This feature is enabled by default. + +.. _openwisp_monitoring_write_retry_options: + +``OPENWISP_MONITORING_WRITE_RETRY_OPTIONS`` +------------------------------------------- + +============ ========= +**type**: ``dict`` +**default**: see below +============ ========= + +.. code-block:: python + + # default value of OPENWISP_MONITORING_RETRY_OPTIONS: + + dict( + max_retries=None, + retry_backoff=True, + retry_backoff_max=600, + retry_jitter=True, + ) + +Retry settings for recoverable failures during metric writes. + +By default if a metric write fails (eg: due to excessive load on +timeseries database at that moment) then the operation will be retried +indefinitely with an exponential random backoff and a maximum delay of 10 +minutes. + +This feature makes the monitoring system resilient to temporary outages +and helps to prevent data loss. + +For more information regarding these settings, consult the `celery +documentation regarding automatic retries for known errors +`_. + +.. note:: + + The retry mechanism does not work when using ``UDP`` for writing data + to the timeseries database. It is due to the nature of ``UDP`` + protocol which does not acknowledge receipt of data packets. + +.. _openwisp_monitoring_timeseries_retry_options: + +``OPENWISP_MONITORING_TIMESERIES_RETRY_OPTIONS`` +------------------------------------------------ + +============ ========= +**type**: ``dict`` +**default**: see below +============ ========= + +.. code-block:: python + + # default value of OPENWISP_MONITORING_RETRY_OPTIONS: + + dict(max_retries=6, delay=2) + +On busy systems, communication with the timeseries DB can occasionally +fail. The timeseries DB backend will retry on any exception according to +these settings. The delay kicks in only after the third consecutive +attempt. + +This setting shall not be confused with +``OPENWISP_MONITORING_WRITE_RETRY_OPTIONS``, which is used to configure +the infinite retrying of the celery task which writes metric data to the +timeseries DB, while ``OPENWISP_MONITORING_TIMESERIES_RETRY_OPTIONS`` +deals with any other read/write operation on the timeseries DB which may +fail. + +However these retries are not handled by celery but are simple python +loops, which will eventually give up if a problem persists. + +.. _openwisp_monitoring_timeseries_retry_delay: + +``OPENWISP_MONITORING_TIMESERIES_RETRY_DELAY`` +---------------------------------------------- + +============ ======= +**type**: ``int`` +**default**: ``2`` +============ ======= + +This settings allow you to configure the retry delay time (in seconds) +after 3 failed attempt in timeseries database. + +This retry setting is used in retry mechanism to make the requests to the +timeseries database resilient. + +This setting is independent of celery retry settings. + +.. _openwisp_monitoring_dashboard_map: + +``OPENWISP_MONITORING_DASHBOARD_MAP`` +------------------------------------- + +============ ======== +**type**: ``bool`` +**default**: ``True`` +============ ======== + +Whether the geographic map in the dashboard is enabled or not. This +feature provides a geographic map which shows the locations which have +devices installed in and provides a visual representation of the +monitoring status of the devices, this allows to get an overview of the +network at glance. + +This feature is enabled by default and depends on the setting +``OPENWISP_ADMIN_DASHBOARD_ENABLED`` from :ref:`openwisp-utils +` being set to ``True`` (which is the +default). + +You can turn this off if you do not use the geographic features of +OpenWISP. + +.. _openwisp_monitoring_dashboard_traffic_chart: + +``OPENWISP_MONITORING_DASHBOARD_TRAFFIC_CHART`` +----------------------------------------------- + +============ ========================================== +**type**: ``dict`` +**default**: ``{'__all__': ['wan', 'eth1', 'eth0.2']}`` +============ ========================================== + +This settings allows to configure the interfaces which should be included +in the **General Traffic** chart in the admin dashboard. + +This setting should be defined in the following format: + +.. + code-block::python + + OPENWISP_MONITORING_DASHBOARD_TRAFFIC_CHART = { + '': [''] + } + +E.g., if you want the **General Traffic** chart to show data from two +interfaces for an organization, you need to configure this setting as +follows: + +.. + code-block::python + + OPENWISP_MONITORING_DASHBOARD_TRAFFIC_CHART = { + # organization uuid + 'f9601bbd-b6d5-4704-85e3-5851894437bf': ['eth1', 'eth2'] + } + +.. note:: + + The value of ``__all__`` key is used if an organization does not have + list of interfaces defined in + ``OPENWISP_MONITORING_DASHBOARD_TRAFFIC_CHART``. + +.. note:: + + If a user can manage more than one organization (e.g. superusers), + then the **General Traffic** chart will always show data from + interfaces of ``__all__`` configuration. + +.. _openwisp_monitoring_metrics: + +``OPENWISP_MONITORING_METRICS`` +------------------------------- + +============ ======== +**type**: ``dict`` +**default**: ``{}`` +============ ======== + +This setting allows to define additional metric configuration or to +override the default metric configuration defined in +``openwisp_monitoring.monitoring.configuration.DEFAULT_METRICS``. + +For example, if you want to change only the **field_name** of ``clients`` +metric to ``wifi_clients`` you can use: + +.. code-block:: python + + from django.utils.translation import gettext_lazy as _ + + OPENWISP_MONITORING_METRICS = { + "clients": { + "label": _("WiFi clients"), + "field_name": "wifi_clients", + }, + } + +For example, if you want to change only the default alert settings of +``memory`` metric you can use: + +.. code-block:: python + + OPENWISP_MONITORING_METRICS = { + "memory": {"alert_settings": {"threshold": 75, "tolerance": 10}}, + } + +For example, if you want to change only the notification of +``config_applied`` metric you can use: + +.. code-block:: python + + from django.utils.translation import gettext_lazy as _ + + OPENWISP_MONITORING_METRICS = { + "config_applied": { + "notification": { + "problem": { + "verbose_name": "Configuration PROBLEM", + "verb": _("has not been applied"), + "email_subject": _( + "[{site.name}] PROBLEM: {notification.target} configuration " + "status issue" + ), + "message": _( + "The configuration for device [{notification.target}]" + "({notification.target_link}) {notification.verb} in a timely manner." + ), + }, + "recovery": { + "verbose_name": "Configuration RECOVERY", + "verb": _("configuration has been applied again"), + "email_subject": _( + "[{site.name}] RECOVERY: {notification.target} {notification.verb} " + "successfully" + ), + "message": _( + "The device [{notification.target}]({notification.target_link}) " + "{notification.verb} successfully." + ), + }, + }, + }, + } + +Or if you want to define a new metric configuration, which you can then +call in your custom code (eg: a custom check class), you can do so as +follows: + +.. code-block:: python + + from django.utils.translation import gettext_lazy as _ + + OPENWISP_MONITORING_METRICS = { + "top_fields_mean": { + "name": "Top Fields Mean", + "key": "{key}", + "field_name": "{field_name}", + "label": "_(Top fields mean)", + "related_fields": ["field1", "field2", "field3"], + }, + } + +.. _openwisp_monitoring_charts: + +``OPENWISP_MONITORING_CHARTS`` +------------------------------ + +============ ======== +**type**: ``dict`` +**default**: ``{}`` +============ ======== + +This setting allows to define additional charts or to override the default +chart configuration defined in +``openwisp_monitoring.monitoring.configuration.DEFAULT_CHARTS``. + +In the following example, we modify the description of the traffic chart: + +.. code-block:: python + + OPENWISP_MONITORING_CHARTS = { + "traffic": { + "description": ( + "Network traffic, download and upload, measured on " + 'the interface "{metric.key}", custom message here.' + ), + } + } + +Or if you want to define a new chart configuration, which you can then +call in your custom code (eg: a custom check class), you can do so as +follows: + +.. code-block:: python + + from django.utils.translation import gettext_lazy as _ + + OPENWISP_MONITORING_CHARTS = { + "ram": { + "type": "line", + "title": "RAM usage", + "description": "RAM usage", + "unit": "bytes", + "order": 100, + "query": { + "influxdb": ( + "SELECT MEAN(total) AS total, MEAN(free) AS free, " + "MEAN(buffered) AS buffered FROM {key} WHERE time >= '{time}' AND " + "content_type = '{content_type}' AND object_id = '{object_id}' " + "GROUP BY time(1d)" + ) + }, + } + } + +In case you just want to change the colors used in a chart here's how to +do it: + +.. code-block:: python + + OPENWISP_MONITORING_CHARTS = { + "traffic": {"colors": ["#000000", "#cccccc", "#111111"]} + } + +Adaptive Size Charts +~~~~~~~~~~~~~~~~~~~~ + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/adaptive-chart.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/1.1/adaptive-chart.png + :align: center + +When configuring charts, it is possible to flag their unit as +``adaptive_prefix``, this allows to make the charts more readable because +the units are shown in either `KB`, `MB`, `GB` and `TB` depending on the +size of each point, the summary values and Y axis are also resized. + +Example taken from the default configuration of the traffic chart: + +.. code-block:: python + + OPENWISP_MONITORING_CHARTS = { + "traffic": { + # other configurations for this chart + # traffic measured in 'B' (bytes) + # unit B, KB, MB, GB, TB + "unit": "adaptive_prefix+B", + }, + "bandwidth": { + # other configurations for this chart + # adaptive unit for bandwidth related charts + # bandwidth measured in 'bps'(bits/sec) + # unit bps, Kbps, Mbps, Gbps, Tbps + "unit": "adaptive_prefix+bps", + }, + } + +.. _openwisp_monitoring_default_chart_time: + +``OPENWISP_MONITORING_DEFAULT_CHART_TIME`` +------------------------------------------ + +=================== =========================================== +**type**: ``str`` +**default**: ``7d`` +**possible values** ``1d``, ``3d``, ``7d``, ``30d`` or ``365d`` +=================== =========================================== + +Allows to set the default time period of the time series charts. + +.. _openwisp_monitoring_auto_clear_management_ip: + +``OPENWISP_MONITORING_AUTO_CLEAR_MANAGEMENT_IP`` +------------------------------------------------ + +============ ======== +**type**: ``bool`` +**default**: ``True`` +============ ======== + +This setting allows you to automatically clear management_ip of a device +when it goes offline. It is enabled by default. + +.. _openwisp_monitoring_api_urlconf: + +``OPENWISP_MONITORING_API_URLCONF`` +----------------------------------- + +============ ========== +**type**: ``string`` +**default**: ``None`` +============ ========== + +Changes the urlconf option of django urls to point the monitoring API urls +to another installed module, example, ``myapp.urls``. (Useful when you +have a seperate API instance.) + +.. _openwisp_monitoring_api_baseurl: + +``OPENWISP_MONITORING_API_BASEURL`` +----------------------------------- + +============ ========== +**type**: ``string`` +**default**: ``None`` +============ ========== + +If you have a seperate server for API of openwisp-monitoring on a +different domain, you can use this option to change the base of the url, +this will enable you to point all the API urls to your openwisp-monitoring +API server's domain, example: ``https://mymonitoring.myapp.com``. + +.. _openwisp_monitoring_cache_timeout: + +``OPENWISP_MONITORING_CACHE_TIMEOUT`` +------------------------------------- + +============ =============================== +**type**: ``int`` +**default**: ``86400`` (24 hours in seconds) +============ =============================== + +This setting allows to configure timeout (in seconds) for monitoring data +cache. diff --git a/docs/user/wifi-sessions.rst b/docs/user/wifi-sessions.rst new file mode 100644 index 000000000..83b8c657d --- /dev/null +++ b/docs/user/wifi-sessions.rst @@ -0,0 +1,65 @@ +Monitoring WiFi Sessions +======================== + +OpenWISP Monitoring maintains a record of WiFi sessions created by clients +joined to a radio of managed devices. The WiFi sessions are created +asynchronously from the monitoring data received from the device. + +You can filter both currently open sessions and past sessions by their +*start* or *stop* time or *organization* or *group* of the device clients +are connected to or even directly by a *device* name or ID. + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/wifi-session-changelist.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/wifi-session-changelist.png + :align: center + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/wifi-session-change.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/wifi-session-change.png + :align: center + +You can disable this feature by configuring +:ref:`OPENWISP_MONITORING_WIFI_SESSIONS_ENABLED +` setting. + +You can also view open WiFi sessions of a device directly from the +device's change admin under the "WiFi Sessions" tab. + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/device-wifi-session-inline.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-monitoring/docs/docs/device-wifi-session-inline.png + :align: center + +Scheduled Deletion of WiFi Sessions +----------------------------------- + +OpenWISP Monitoring provides a celery task to automatically delete WiFi +sessions older than a pre-configured number of days. + +.. note:: + + If you have deployed OpenWISP using :doc:`ansible-openwisp2 + ` or :doc:`docker-openwisp `, then this + feature has been already configured for you. Refer to the + documentation of your deployment method to know the default value. + This section is only for reference for users who wish to customize + OpenWISP, or who have deployed OpenWISP in a different way. + +The celery task takes only one argument, i.e. number of days. You can +provide any number of days in `args` key while configuring +``CELERY_BEAT_SCHEDULE`` setting. + +E.g., if you want WiFi Sessions older than 30 days to get deleted +automatically, then configure ``CELERY_BEAT_SCHEDULE`` as follows: + +.. code-block:: python + + CELERY_BEAT_SCHEDULE = { + "delete_wifi_clients_and_sessions": { + "task": "openwisp_monitoring.monitoring.tasks.delete_wifi_clients_and_sessions", + "schedule": timedelta(days=1), + "args": (30,), # Here we have defined 30 days + }, + } + +Please refer to `"Periodic Tasks" section of Celery's documentation +`_ +to learn more. diff --git a/openwisp_monitoring/check/base/models.py b/openwisp_monitoring/check/base/models.py index 2f2e05b2a..c5156698f 100644 --- a/openwisp_monitoring/check/base/models.py +++ b/openwisp_monitoring/check/base/models.py @@ -81,23 +81,17 @@ def full_clean(self, *args, **kwargs): @cached_property def check_class(self): - """ - returns check class - """ + """Returns the check class.""" return import_string(self.check_type) @cached_property def check_instance(self): - """ - returns check class instance - """ + """Returns the check class instance.""" check_class = self.check_class return check_class(check=self, params=self.params) def perform_check(self, store=True): - """ - initiates check instance and calls its check method - """ + """Initializes check instance and calls the check method.""" if ( hasattr(self.content_object, 'organization_id') and self.content_object.organization.is_active is False @@ -112,9 +106,9 @@ def perform_check_delayed(self, duration=0): def auto_ping_receiver(sender, instance, created, **kwargs): - """ - Implements OPENWISP_MONITORING_AUTO_PING - The creation step is executed in the background + """Implements OPENWISP_MONITORING_AUTO_PING. + + The creation step is executed in the background. """ # we need to skip this otherwise this task will be executed # every time the configuration is requested via checksum @@ -130,9 +124,9 @@ def auto_ping_receiver(sender, instance, created, **kwargs): def auto_config_check_receiver(sender, instance, created, **kwargs): - """ - Implements OPENWISP_MONITORING_AUTO_DEVICE_CONFIG_CHECK - The creation step is executed in the background + """Implements OPENWISP_MONITORING_AUTO_DEVICE_CONFIG_CHECK. + + The creation step is executed in the background. """ # we need to skip this otherwise this task will be executed # every time the configuration is requested via checksum @@ -148,9 +142,9 @@ def auto_config_check_receiver(sender, instance, created, **kwargs): def auto_iperf3_check_receiver(sender, instance, created, **kwargs): - """ - Implements OPENWISP_MONITORING_AUTO_IPERF3 - The creation step is executed in the background + """Implements OPENWISP_MONITORING_AUTO_IPERF3. + + The creation step is executed in the background. """ # we need to skip this otherwise this task will be executed # every time the configuration is requested via checksum diff --git a/openwisp_monitoring/check/classes/base.py b/openwisp_monitoring/check/classes/base.py index 1c16f5201..12cf51c9e 100644 --- a/openwisp_monitoring/check/classes/base.py +++ b/openwisp_monitoring/check/classes/base.py @@ -31,9 +31,7 @@ def check(self, store=True): raise NotImplementedError def _get_or_create_metric(self, configuration=None): - """ - Gets or creates metric - """ + """Gets or creates metric.""" check = self.check_instance if check.object_id and check.content_type_id: obj_id = check.object_id diff --git a/openwisp_monitoring/check/classes/iperf3.py b/openwisp_monitoring/check/classes/iperf3.py index fc859c2dd..265e5e547 100644 --- a/openwisp_monitoring/check/classes/iperf3.py +++ b/openwisp_monitoring/check/classes/iperf3.py @@ -284,9 +284,7 @@ def _run_iperf3_check(self, store, server, time): return result def _get_check_commands(self, server): - """ - Returns tcp & udp commands for iperf3 check - """ + """Returns tcp & udp commands for iperf3 check.""" username = self._get_param('username', 'username.default') port = self._get_param( 'client_options.port', 'client_options.properties.port.default' @@ -356,9 +354,7 @@ def _get_check_commands(self, server): return command_tcp, command_udp def _get_iperf3_test_conditions(self): - """ - Returns iperf3 check test conditions (rev_or_bidir, end_condition) - """ + """Returns iperf3 check test conditions (rev_or_bidir, end_condition).""" time = self._get_param( 'client_options.time', 'client_options.properties.time.default' ) @@ -396,19 +392,17 @@ def _get_iperf3_test_conditions(self): return rev_or_bidir, test_end_condition def _get_compelete_rsa_key(self, key): - """ - Returns RSA key with proper format - """ + """Returns RSA key with proper format.""" pem_prefix = '-----BEGIN PUBLIC KEY-----\n' pem_suffix = '\n-----END PUBLIC KEY-----' key = key.strip() return f'{pem_prefix}{key}{pem_suffix}' def _deep_get(self, dictionary, keys, default=None): - """ - Returns dict key value using dict & - it's dot_key string ie. key1.key2_nested.key3_nested - if found otherwise returns default + """Returns dict key value using dict and it's dot_key string, + + ie: key1.key2_nested.key3_nested, if found, otherwise returns + default. """ return reduce( lambda d, key: d.get(key, default) if isinstance(d, dict) else default, @@ -417,9 +411,7 @@ def _deep_get(self, dictionary, keys, default=None): ) def _get_param(self, conf_key, default_conf_key): - """ - Returns specified param or its default value according to the schema - """ + """Returns specified param or its default value according to the schema.""" org_id = str(self.related_object.organization.id) iperf3_config = app_settings.IPERF3_CHECK_CONFIG @@ -437,9 +429,7 @@ def _get_param(self, conf_key, default_conf_key): return self._deep_get(DEFAULT_IPERF3_CHECK_CONFIG, default_conf_key) def _get_iperf3_result(self, result, exit_code, mode): - """ - Returns iperf3 test result - """ + """Returns iperf3 test result.""" try: result = loads(result) except JSONDecodeError: @@ -500,18 +490,14 @@ def _get_iperf3_result(self, result, exit_code, mode): } def store_result(self, result): - """ - Store result in the DB - """ + """Store result in the DB.""" metric = self._get_metric() copied = result.copy() iperf3_result = copied.pop('iperf3_result') metric.write(iperf3_result, extra_values=copied) def _get_metric(self): - """ - Gets or creates metric - """ + """Gets or creates metric.""" metric, created = self._get_or_create_metric() if created: self._create_alert_settings(metric) @@ -519,17 +505,13 @@ def _get_metric(self): return metric def _create_alert_settings(self, metric): - """ - Creates default iperf3 alert settings with is_active=False - """ + """Creates default iperf3 alert settings with is_active=False.""" alert_settings = AlertSettings(metric=metric, is_active=False) alert_settings.full_clean() alert_settings.save() def _create_charts(self, metric): - """ - Creates iperf3 related charts - """ + """Creates iperf3 related charts.""" charts = [ 'bandwidth', 'transfer', diff --git a/openwisp_monitoring/check/classes/ping.py b/openwisp_monitoring/check/classes/ping.py index 22b316abd..933abf58e 100644 --- a/openwisp_monitoring/check/classes/ping.py +++ b/openwisp_monitoring/check/classes/ping.py @@ -120,24 +120,18 @@ def check(self, store=True): return result def store_result(self, result): - """ - store result in the DB - """ + """Stores result in the DB.""" metric = self._get_metric() copied = result.copy() reachable = copied.pop('reachable') metric.write(reachable, extra_values=copied) def _get_param(self, param): - """ - Gets specified param or its default value according to the schema - """ + """Gets specified param or its default value according to the schema.""" return self.params.get(param, self.schema['properties'][param]['default']) def _get_ip(self): - """ - Figures out ip to use or fails raising OperationalError - """ + """Figures out ip to use or fails raising OperationalError.""" device = self.related_object ip = device.management_ip if not ip and not app_settings.MANAGEMENT_IP_ONLY: @@ -145,16 +139,12 @@ def _get_ip(self): return ip def _command(self, command): - """ - Executes command (easier to mock) - """ + """Executes command (easier to mock).""" p = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) return p.stdout, p.stderr def _get_metric(self): - """ - Gets or creates metric - """ + """Gets or creates metric.""" metric, created = self._get_or_create_metric() if created: self._create_alert_settings(metric) @@ -167,9 +157,7 @@ def _create_alert_settings(self, metric): alert_settings.save() def _create_charts(self, metric): - """ - Creates device charts if necessary - """ + """Creates device charts if necessary.""" charts = ['uptime', 'packet_loss', 'rtt'] for chart in charts: if chart not in monitoring_settings.AUTO_CHARTS: diff --git a/openwisp_monitoring/check/tasks.py b/openwisp_monitoring/check/tasks.py index 3f56878ae..ee2c2b4a2 100644 --- a/openwisp_monitoring/check/tasks.py +++ b/openwisp_monitoring/check/tasks.py @@ -20,12 +20,13 @@ def get_check_model(): @shared_task(time_limit=2 * 60 * 60) def run_checks(checks=None): - """ - Retrieves the id of all active checks in chunks of 2000 items - and calls the ``perform_check`` task (defined below) for each of them. + """Runs all the checks. + + Retrieves the id of all active checks in chunks of 2000 items and + calls the ``perform_check`` task (defined below) for each of them. - This allows to enqueue all the checks that need to be performed - and execute them in parallel with multiple workers if needed. + This allows to enqueue all the checks that need to be performed and + execute them in parallel with multiple workers if needed. """ # If checks is None, We should execute all the checks if checks is None: @@ -53,9 +54,10 @@ def run_checks(checks=None): @shared_task(time_limit=30 * 60) def perform_check(uuid): - """ - Retrieves check according to the passed UUID - and calls ``check.perform_check()`` + """Performs check with specified uuid. + + Retrieves check according to the passed UUID and calls the + ``perform_check()`` method. """ try: check = get_check_model().objects.get(pk=uuid) @@ -71,9 +73,10 @@ def perform_check(uuid): def auto_create_ping( model, app_label, object_id, check_model=None, content_type_model=None ): - """ - Called by django signal (dispatch_uid: auto_ping) - registered in check app's apps.py file. + """Implements the auto creation of the ping check. + + Called by django signal (dispatch_uid: auto_ping) registered in check + app's apps.py file. """ Check = check_model or get_check_model() ping_path = 'openwisp_monitoring.check.classes.Ping' @@ -96,8 +99,9 @@ def auto_create_ping( def auto_create_config_check( model, app_label, object_id, check_model=None, content_type_model=None ): - """ - Called by openwisp_monitoring.check.models.auto_config_check_receiver + """Implements the auto creation of the config modified check. + + Called by openwisp_monitoring.check.models.auto_config_check_receiver. """ Check = check_model or get_check_model() config_check_path = 'openwisp_monitoring.check.classes.ConfigApplied' @@ -123,8 +127,10 @@ def auto_create_config_check( def auto_create_iperf3_check( model, app_label, object_id, check_model=None, content_type_model=None ): - """ - Called by openwisp_monitoring.check.models.auto_iperf3_check_receiver + """Implements the auto creation of the iperf3 check. + + Called by the + openwisp_monitoring.check.models.auto_iperf3_check_receiver. """ Check = check_model or get_check_model() iperf3_check_path = 'openwisp_monitoring.check.classes.Iperf3' diff --git a/openwisp_monitoring/check/tests/test_iperf3.py b/openwisp_monitoring/check/tests/test_iperf3.py index 9f331378d..c54fdc1f2 100644 --- a/openwisp_monitoring/check/tests/test_iperf3.py +++ b/openwisp_monitoring/check/tests/test_iperf3.py @@ -621,7 +621,7 @@ def test_iperf3_check_auth_config(self, mock_warn, mock_exec_command): with patch.object( app_settings, 'IPERF3_CHECK_CONFIG', - iperf3_config + iperf3_config, # It is required to mock "Iperf3.schema" here so that it # uses the updated configuration from "IPERF3_CHECK_CONFIG" setting. ), patch.object(Iperf3, 'schema', get_iperf3_schema()): diff --git a/openwisp_monitoring/check/tests/test_models.py b/openwisp_monitoring/check/tests/test_models.py index c0e5aef8c..503a58054 100644 --- a/openwisp_monitoring/check/tests/test_models.py +++ b/openwisp_monitoring/check/tests/test_models.py @@ -176,9 +176,7 @@ def test_config_modified_device_problem(self): self.assertEqual(Notification.objects.count(), 1) def test_config_error(self): - """ - Test that ConfigApplied checks are skipped when device config status is errored - """ + """Test that ConfigApplied checks are skipped when device config status is errored.""" self._create_admin() self.assertEqual(Check.objects.count(), 0) with freeze_time( diff --git a/openwisp_monitoring/check/utils.py b/openwisp_monitoring/check/utils.py index 4897c77a9..e37e7ab22 100644 --- a/openwisp_monitoring/check/utils.py +++ b/openwisp_monitoring/check/utils.py @@ -2,8 +2,5 @@ def run_checks_async(): - """ - Calls celery task run_checks - is run in a background worker - """ + """Calls celery task run_checks is run in a background worker.""" run_checks.delay() diff --git a/openwisp_monitoring/db/backends/__init__.py b/openwisp_monitoring/db/backends/__init__.py index 715b1113c..3b9e04263 100644 --- a/openwisp_monitoring/db/backends/__init__.py +++ b/openwisp_monitoring/db/backends/__init__.py @@ -24,9 +24,11 @@ def load_backend_module(backend_name=TIMESERIES_DB['BACKEND'], module=None): - """ - Returns database backend module given a fully qualified database backend name, - or raise an error if it doesn't exist or backend is not well defined. + """Loads backend module. + + Returns database backend module given a fully qualified database + backend name, or raise an error if it doesn't exist or backend is not + well defined. """ try: assert 'BACKEND' in TIMESERIES_DB, 'BACKEND' diff --git a/openwisp_monitoring/db/backends/influxdb/client.py b/openwisp_monitoring/db/backends/influxdb/client.py index 906769a00..b6b9428db 100644 --- a/openwisp_monitoring/db/backends/influxdb/client.py +++ b/openwisp_monitoring/db/backends/influxdb/client.py @@ -62,21 +62,21 @@ def __init__(self, db_name=None): @retry def create_database(self): - """creates database if necessary""" + """Creates database if necessary.""" # InfluxDB does not create a new database, neither raise an error if database exists self.db.create_database(self.db_name) logger.debug(f'Created InfluxDB database "{self.db_name}"') @retry def drop_database(self): - """drops database if it exists""" + """Drops database if it exists.""" # InfluxDB does not raise an error if database does not exist self.db.drop_database(self.db_name) logger.debug(f'Dropped InfluxDB database "{self.db_name}"') @cached_property def db(self): - """Returns an ``InfluxDBClient`` instance""" + """Returns an ``InfluxDBClient`` instance.""" return self.dbs['default'] @cached_property @@ -123,7 +123,7 @@ def use_udp(self): @retry def create_or_alter_retention_policy(self, name, duration): - """creates or alters existing retention policy if necessary""" + """Creates or alters existing retention policy if necessary.""" retention_policies = self.db.get_list_retention_policies() exists = False duration_changed = False @@ -280,9 +280,10 @@ def get_list_retention_policies(self): return self.db.get_list_retention_policies() def delete_metric_data(self, key=None, tags=None): - """ - deletes a specific metric based on the key and tags - provided, you may also choose to delete all metrics + """Deletes a specific metric. + + Deletes a metric based on the key and tags provided, you may also + choose to delete all metrics. """ if not key and not tags: self.query('DROP SERIES FROM /.*/') @@ -393,10 +394,11 @@ def _group_by(self, query, time, chart_type, group_map, strip=False): ) def _fields(self, fields, query, field_name): - """ - support substitution of {fields||} - with (field1) AS field1 , - (field2) AS field2 + """Support substitution: + + of {fields||} with + (field1) AS field1 , + (field2) AS field2 """ matches = re.search(self._fields_regex, query) if not matches and not fields: diff --git a/openwisp_monitoring/device/admin.py b/openwisp_monitoring/device/admin.py index 0e0f82d46..baea8ce1c 100644 --- a/openwisp_monitoring/device/admin.py +++ b/openwisp_monitoring/device/admin.py @@ -291,9 +291,7 @@ def get_object(self, request, object_id, from_field=None): return obj def get_form(self, request, obj=None, **kwargs): - """ - Adds the help_text of DeviceMonitoring.status field - """ + """Adds the help_text of DeviceMonitoring.status field""" health_status = DeviceMonitoring._meta.get_field('status').help_text kwargs.update( {'help_texts': {'health_status': health_status.replace('\n', '
')}} diff --git a/openwisp_monitoring/device/api/views.py b/openwisp_monitoring/device/api/views.py index 697318dc6..2f0d7390a 100644 --- a/openwisp_monitoring/device/api/views.py +++ b/openwisp_monitoring/device/api/views.py @@ -69,9 +69,7 @@ class ListViewPagination(pagination.PageNumberPagination): def get_device_args_rewrite(view, pk): - """ - Use only the PK parameter for calculating the cache key - """ + """Use only the PK parameter for calculating the cache key""" try: pk = uuid.UUID(pk) except ValueError: @@ -100,14 +98,15 @@ def get_authenticators(self): class DeviceMetricView( DeviceKeyAuthenticationMixin, MonitoringApiViewMixin, GenericAPIView ): - """ - Retrieve device information, monitoring status (health status), - a list of metrics, chart data and - optionally device status information (if ``?status=true``). + """Device Monitoring View. - Suports session authentication, token authentication, - or alternatively device key authentication passed as query - string parameter (this method is meant to be used by network devices). + Retrieve device information, monitoring status (health status), a list + of metrics, chart data and optionally device status information (if + ``?status=true``). + + Suports session authentication, token authentication, or alternatively + device key authentication passed as query string parameter (this + method is meant to be used by network devices). """ model = DeviceData @@ -125,9 +124,7 @@ class DeviceMetricView( @classmethod def invalidate_get_device_cache(cls, instance, **kwargs): - """ - Called from signal receiver which performs cache invalidation - """ + """Called from signal receiver which performs cache invalidation""" view = cls() view.get_object.invalidate(view, str(instance.pk)) logger.debug(f'invalidated view cache for device ID {instance.pk}') @@ -286,14 +283,13 @@ def get_queryset(self): class MonitoringDeviceList(DeviceListCreateView): - """ - Lists devices and their monitoring status (health status). + """Lists devices and their monitoring status (health status). Supports session authentication and token authentication. - `NOTE:` The response does not include the information and - health status of the specific metrics, this information - can be retrieved in the detail endpoint of each device. + `NOTE:` The response does not include the information and health + status of the specific metrics, this information can be retrieved in + the detail endpoint of each device. """ serializer_class = MonitoringDeviceListSerializer diff --git a/openwisp_monitoring/device/apps.py b/openwisp_monitoring/device/apps.py index 6cad85179..67d988073 100644 --- a/openwisp_monitoring/device/apps.py +++ b/openwisp_monitoring/device/apps.py @@ -191,9 +191,11 @@ def device_recovery_detection(self): @classmethod def manage_device_recovery_cache_key(cls, instance, status, **kwargs): - """ - It sets the ``cache_key`` as 1 when device ``health_status`` goes to ``critical`` - and deletes the ``cache_key`` when device recovers from ``critical`` state + """Returns a cache key string. + + It sets the ``cache_key`` as 1 when device ``health_status`` goes + to ``critical`` and deletes the ``cache_key`` when device recovers + from ``critical`` state """ cache_key = get_device_cache_key(device=instance.device) if status == 'critical': diff --git a/openwisp_monitoring/device/base/models.py b/openwisp_monitoring/device/base/models.py index fd0ca8c31..60a72f3ca 100644 --- a/openwisp_monitoring/device/base/models.py +++ b/openwisp_monitoring/device/base/models.py @@ -37,9 +37,9 @@ def mac_lookup_cache_timeout(): - """ - returns a random number of hours between 48 and 96 - this avoids timing out most of the cache at the same time + """Returns a random number of hours between 48 and 96. + + This avoids timing out the entire cache at the same time. """ return 60 * 60 * random.randint(48, 96) @@ -83,9 +83,7 @@ def invalidate_cache(cls, instance, *args, **kwargs): cls.get_devicedata.invalidate(cls, str(pk)) def can_be_updated(self): - """ - Do not attempt at pushing the conf if the device is not reachable - """ + """Do not attempt to push the conf if the device is not reachable.""" can_be_updated = super().can_be_updated() return can_be_updated and self.monitoring.status not in ['critical', 'unknown'] @@ -150,9 +148,7 @@ def data_user_friendly(self): @property def data(self): - """ - retrieves last data snapshot from Timeseries Database - """ + """Retrieves last data snapshot from Timeseries Database.""" if self.__data: return self.__data q = device_data_query.format(SHORT_RP, self.__key, self.pk) @@ -167,29 +163,21 @@ def data(self): @data.setter def data(self, data): - """ - sets data - """ + """Sets data.""" self.__data = data @property def data_timestamp(self): - """ - retrieves timestamp at which the data was recorded - """ + """Retrieves timestamp at which the data was recorded.""" return self.__data_timestamp @data_timestamp.setter def data_timestamp(self, value): - """ - sets the timestamp related to the data - """ + """Sets the timestamp related to the data.""" self.__data_timestamp = value def validate_data(self): - """ - validate data according to NetJSON DeviceMonitoring schema - """ + """Validates data according to NetJSON DeviceMonitoring schema.""" try: validate(self.data, self.schema, format_checker=draft7_format_checker) except SchemaError as e: @@ -201,9 +189,7 @@ def validate_data(self): raise ValidationError(message) def _transform_data(self): - """ - performs corrections or additions to the device data - """ + """Performs corrections or additions to the device data.""" mac_detection = app_settings.MAC_VENDOR_DETECTION for interface in self.data.get('interfaces', []): # loop over mobile signal values to convert them to float @@ -270,9 +256,7 @@ def _mac_lookup(self, value): return '' def save_data(self, time=None): - """ - validates and saves data to Timeseries Database - """ + """Validates and saves data to Timeseries Database.""" self.validate_data() self._transform_data() time = time or now() @@ -400,9 +384,10 @@ def related_metrics(self): @staticmethod @receiver(threshold_crossed, dispatch_uid='threshold_crossed_receiver') def threshold_crossed(sender, metric, alert_settings, target, first_time, **kwargs): - """ - Changes the health status of a devicewhen a threshold defined in the - alert settings related to the metric is crossed. + """Executed when a threshold is crossed. + + Changes the health status of a devicewhen a threshold defined in + the alert settings related to the metric is crossed. """ DeviceMonitoring = load_model('device_monitoring', 'DeviceMonitoring') if not isinstance(target, DeviceMonitoring.device.field.related_model): @@ -442,15 +427,15 @@ def is_metric_critical(metric): @classmethod def handle_disabled_organization(cls, organization_id): - """ - Clears the management IP of all devices belonging to a - disabled organization and set their monitoring status to 'unknown'. + """Handles the disabling of an organization. + + Clears the management IP of all devices belonging to a disabled + organization and set their monitoring status to 'unknown'. - Parameters: - organization_id (int): The ID of the disabled organization. + Parameters: - organization_id (int): The ID of the disabled + organization. - Returns: - None + Returns: - None """ load_model('config', 'Device').objects.filter( organization_id=organization_id diff --git a/openwisp_monitoring/device/migrations/0001_squashed_0002_devicemonitoring.py b/openwisp_monitoring/device/migrations/0001_squashed_0002_devicemonitoring.py index 78f9cfac0..80279e23d 100644 --- a/openwisp_monitoring/device/migrations/0001_squashed_0002_devicemonitoring.py +++ b/openwisp_monitoring/device/migrations/0001_squashed_0002_devicemonitoring.py @@ -15,9 +15,7 @@ def create_device_monitoring(apps, schema_editor): - """ - Data migration - """ + """Data migration""" Device = apps.get_model('config', 'Device') DeviceMonitoring = apps.get_model('device_monitoring', 'DeviceMonitoring') for device in Device.objects.all(): diff --git a/openwisp_monitoring/device/tasks.py b/openwisp_monitoring/device/tasks.py index 03ab8577a..94efe24be 100644 --- a/openwisp_monitoring/device/tasks.py +++ b/openwisp_monitoring/device/tasks.py @@ -14,11 +14,13 @@ @shared_task(base=OpenwispCeleryTask) def trigger_device_checks(pk, recovery=True): - """ - Retrieves all related checks to the passed ``device`` - and calls the ``perform_check`` task from each of them. - If no check exists changes the status according to the - ``recovery`` argument. + """Triggers the monitoring checks for the specified device pk. + + Retrieves all related checks to the passed ``device`` and calls the + ``perform_check`` task from each of them. + + If no check exists changes the status according to the ``recovery`` + argument. """ DeviceData = load_model('device_monitoring', 'DeviceData') try: diff --git a/openwisp_monitoring/device/tests/__init__.py b/openwisp_monitoring/device/tests/__init__.py index b0d580110..98af88309 100644 --- a/openwisp_monitoring/device/tests/__init__.py +++ b/openwisp_monitoring/device/tests/__init__.py @@ -78,10 +78,10 @@ def _transform_wireless_interface_test_data(self, data): return data def assertDataDict(self, dd_data, data): - """ - This method is necessary as the wireless interface data - is modified by the `AbstractDeviceData._transform_data` - method. + """Compares monitoring data. + + This method is necessary because the wireless interface data is + modified by the `AbstractDeviceData._transform_data` method. """ data = self._transform_wireless_interface_test_data(data) self.assertDictEqual(dd_data, data) diff --git a/openwisp_monitoring/device/tests/test_admin.py b/openwisp_monitoring/device/tests/test_admin.py index 79821f406..721b24040 100644 --- a/openwisp_monitoring/device/tests/test_admin.py +++ b/openwisp_monitoring/device/tests/test_admin.py @@ -41,9 +41,7 @@ class TestAdmin( TestWifiClientSessionMixin, TestImportExportMixin, DeviceMonitoringTestCase ): - """ - Test the additions of openwisp-monitoring to DeviceAdmin - """ + """Test the additions of openwisp-monitoring to DeviceAdmin""" resources_fields = TestImportExportMixin.resource_fields resources_fields.append('monitoring_status') @@ -1092,9 +1090,11 @@ def test_wifi_client_he_vht_ht_unknown(self): """.format( # TODO: Remove this when dropping support for Django 3.2 and 4.0 - start_div='
' - if django.VERSION >= (4, 2) - else '', + start_div=( + '
' + if django.VERSION >= (4, 2) + else '' + ), end_div='
' if django.VERSION >= (4, 2) else '', ), html=True, diff --git a/openwisp_monitoring/device/tests/test_api.py b/openwisp_monitoring/device/tests/test_api.py index db369f701..9a56d314d 100644 --- a/openwisp_monitoring/device/tests/test_api.py +++ b/openwisp_monitoring/device/tests/test_api.py @@ -39,9 +39,7 @@ class TestDeviceApi(AuthenticationMixin, TestGeoMixin, DeviceMonitoringTestCase): - """ - Tests API (device metric collection) - """ + """Tests API (device metric collection).""" location_model = Location object_location_model = DeviceLocation diff --git a/openwisp_monitoring/device/tests/test_models.py b/openwisp_monitoring/device/tests/test_models.py index 99af76cd9..412da9ca9 100644 --- a/openwisp_monitoring/device/tests/test_models.py +++ b/openwisp_monitoring/device/tests/test_models.py @@ -247,9 +247,7 @@ def _create_device_data(self, **kwargs): class TestDeviceData(BaseTestCase): - """ - Test openwisp_monitoring.device.models.DeviceData - """ + """Test openwisp_monitoring.device.models.DeviceData""" def test_clean_data_ok(self): dd = self._create_device_data() @@ -548,9 +546,7 @@ def test_calculate_increment(self): class TestDeviceMonitoring(CreateConnectionsMixin, BaseTestCase): - """ - Test openwisp_monitoring.device.models.DeviceMonitoring - """ + """Test openwisp_monitoring.device.models.DeviceMonitoring""" def _create_env(self): d = self._create_device() diff --git a/openwisp_monitoring/device/tests/test_recovery.py b/openwisp_monitoring/device/tests/test_recovery.py index 0861ae383..cf3553f5e 100644 --- a/openwisp_monitoring/device/tests/test_recovery.py +++ b/openwisp_monitoring/device/tests/test_recovery.py @@ -15,9 +15,7 @@ class TestRecovery(DeviceMonitoringTestCase): - """ - Tests ``Device Recovery Detection`` functionality - """ + """Tests ``Device Recovery Detection`` functionality""" def test_device_recovery_cache_key_not_set(self): device_monitoring_app = DeviceMonitoring._meta.app_config @@ -56,9 +54,7 @@ def test_device_recovery_cache_key_set(self): self.assertEqual(cache.get(cache_key), None) def test_status_set_ok(self): - """ - Tests device status is set to ok if no related checks present - """ + """Tests device status is set to ok if no related checks present""" dm = self._create_device_monitoring() dm.update_status('critical') trigger_device_checks.delay(dm.device.pk) @@ -66,10 +62,7 @@ def test_status_set_ok(self): self.assertEqual(dm.status, 'ok') def test_status_set_critical(self): - """ - Tests device status is set to critical if no related checks present - and recovery=False is passed - """ + """Tests device status is set to critical if no related checks present and recovery=False is passed""" dm = self._create_device_monitoring() dm.update_status('critical') trigger_device_checks.delay(dm.device.pk, recovery=False) diff --git a/openwisp_monitoring/device/tests/test_settings.py b/openwisp_monitoring/device/tests/test_settings.py index 44725310f..6ef73610c 100644 --- a/openwisp_monitoring/device/tests/test_settings.py +++ b/openwisp_monitoring/device/tests/test_settings.py @@ -6,9 +6,7 @@ class TestSettings(DeviceMonitoringTestCase): - """ - Tests ``OpenWISP Device settings`` functionality - """ + """Tests ``OpenWISP Device settings`` functionality""" @patch( 'django.conf.settings.OPENWISP_MONITORING_CRITICAL_DEVICE_METRICS', diff --git a/openwisp_monitoring/device/utils.py b/openwisp_monitoring/device/utils.py index 151b62609..931cd991a 100644 --- a/openwisp_monitoring/device/utils.py +++ b/openwisp_monitoring/device/utils.py @@ -10,16 +10,12 @@ def get_device_cache_key(device, context='react-to-updates'): def manage_short_retention_policy(): - """ - creates or updates the "short" retention policy - """ + """creates or updates the "short" retention policy""" duration = app_settings.SHORT_RETENTION_POLICY timeseries_db.create_or_alter_retention_policy(SHORT_RP, duration) def manage_default_retention_policy(): - """ - creates or updates the "default" retention policy - """ + """creates or updates the "default" retention policy""" duration = app_settings.DEFAULT_RETENTION_POLICY timeseries_db.create_or_alter_retention_policy(DEFAULT_RP, duration) diff --git a/openwisp_monitoring/device/writer.py b/openwisp_monitoring/device/writer.py index 8b0fdd7a2..48071a4c9 100644 --- a/openwisp_monitoring/device/writer.py +++ b/openwisp_monitoring/device/writer.py @@ -19,21 +19,18 @@ class DeviceDataWriter(object): - """ - This class is in charge of writing the device metric data. - Before these methods were shipped in the REST API view - but later have been moved here to allow writing this data in - the background processes of OpenWISP. + """This class is in charge of writing the device metric data. + + Before these methods were shipped in the REST API view but later have + been moved here to allow writing this data in the background processes + of OpenWISP. """ def __init__(self, device_data): self.device_data = device_data def _init_previous_data(self): - """ - makes NetJSON interfaces of previous - snapshots more easy to access - """ + """makes NetJSON interfaces of previous snapshots more easy to access""" data = self.device_data.data or {} if data: data = deepcopy(data) @@ -45,9 +42,10 @@ def _init_previous_data(self): def _append_metric_data( self, metric, value, current=False, time=None, extra_values=None ): - """ - Appends to the data structure which holds metric data - and which will be sent to the timeseries DB. + """Appends data for writing. + + Appends to the data structure which holds metric data and which + will be sent to the timeseries DB. """ self.write_device_metrics.append( ( @@ -340,10 +338,7 @@ def _write_memory( ) def _calculate_increment(self, ifname, stat, value): - """ - compares value with previously stored counter and - calculates the increment of the value (which is returned) - """ + """Returns how much a counter has incremented since its last saved value.""" # get previous counters data = self._previous_data try: @@ -364,9 +359,7 @@ def _calculate_increment(self, ifname, stat, value): return int(value) def _create_traffic_chart(self, metric): - """ - create "traffic (GB)" chart - """ + """Creates "traffic (GB)" chart.""" if 'traffic' not in monitoring_settings.AUTO_CHARTS: return chart = Chart(metric=metric, configuration='traffic') @@ -374,9 +367,7 @@ def _create_traffic_chart(self, metric): chart.save() def _create_clients_chart(self, metric): - """ - creates "WiFi associations" chart - """ + """Creates "WiFi associations" chart.""" if 'wifi_clients' not in monitoring_settings.AUTO_CHARTS: return chart = Chart(metric=metric, configuration='wifi_clients') diff --git a/openwisp_monitoring/monitoring/api/views.py b/openwisp_monitoring/monitoring/api/views.py index 83f66be75..e597a38fa 100644 --- a/openwisp_monitoring/monitoring/api/views.py +++ b/openwisp_monitoring/monitoring/api/views.py @@ -19,9 +19,7 @@ class DashboardTimeseriesView(ProtectedAPIMixin, MonitoringApiViewMixin, APIView): - """ - Multi-tenant view that returns general monitoring - charts for the admin dashboard. + """Multi-tenant view that returns general monitoring charts for the admin dashboard. Allows filtering with organization slugs. """ @@ -167,10 +165,7 @@ def invalidate_cache(cls, instance, *args, **kwargs): cls._get_charts.invalidate() def _get_user_managed_orgs(self, request): - """ - Return list of dictionary containing organization name and slug - in select2 compatible format. - """ + """Return list of dictionary containing organization name and slug in select2 compatible format.""" orgs = [] qs = Organization.objects.only('slug', 'name') if not request.user.is_superuser: diff --git a/openwisp_monitoring/monitoring/base/models.py b/openwisp_monitoring/monitoring/base/models.py index 5d7bf0ebe..743025e1d 100644 --- a/openwisp_monitoring/monitoring/base/models.py +++ b/openwisp_monitoring/monitoring/base/models.py @@ -161,9 +161,10 @@ def _get_or_create( cls, **kwargs, ): - """ - like ``get_or_create`` method of django model managers - but with validation before creation + """Gets or creates a metric. + + Like ``get_or_create`` method of django model managers but with + validation before creation. """ if 'key' in kwargs: kwargs['key'] = cls._makekey(kwargs['key']) @@ -215,7 +216,7 @@ def invalidate_cache(cls, instance, *args, **kwargs): @property def codename(self): - """identifier stored in timeseries db""" + """Identifier stored in timeseries db.""" return self._makekey(self.name) @property @@ -234,7 +235,10 @@ def related_fields(self): # TODO: This method needs to be refactored when adding the other db @staticmethod def _makekey(value): - """makes value suited for InfluxDB key""" + """Produces a valid InfluxDB key from ``value``. + + Takes ``value`` as input argument and returs a valid InfluxDB key. + """ return clean_timeseries_data_key(value) @property @@ -255,9 +259,7 @@ def tags(self): @staticmethod def _sort_dict(dict_): - """ - ensures the order of the keys in the dict not random - """ + """Ensures the order of the keys in the dict is predictable.""" if not isinstance(dict_, OrderedDict): return OrderedDict(sorted(dict_.items())) return dict_ @@ -281,23 +283,20 @@ def alert_on_related_field(self): return self.alert_field in self.related_fields def _get_time(self, time): - """ - If time is a string, convert it to a datetime - """ + """If time is a string, it converts it to a datetime.""" if isinstance(time, str): return parse_date(time) return time def _set_is_healthy(self, alert_settings, value): - """ - Sets the value of "is_healthy" field if "value" - crosses threshold defined in "alert_settings". - Returns "True" if "is_healthy" field is changed. + """Sets the value of "is_healthy" field when necessary. + + Executes only if "value" crosses the threshold defined in + "alert_settings". Returns "True" if "is_healthy" field is changed. Otherwise, returns "None". - This method does not take into account the alert - settings tolerance, which is done by - "_set_is_healthy_tolerant" method. + This method does not take into account the alert settings + tolerance, which is done by "_set_is_healthy_tolerant" method. """ crossed = alert_settings._value_crossed(value) if (not crossed and self.is_healthy) or (crossed and self.is_healthy is False): @@ -313,14 +312,14 @@ def _set_is_healthy(self, alert_settings, value): def _set_is_healthy_tolerant( self, alert_settings, value, time, retention_policy, send_alert ): - """ - Sets the value of "is_tolerance_healthy" if "value" - crosses the threshold for more than the amount of seconds - defined in the alert_settings "tolerance" field. - It also sends the notification if required. - Returns "None" if value of "is_healthy_tolerant" is unchanged. - Returns "True" if it is the first metric write within threshold. - Returns "False" in other cases. + """Sets the value of "is_tolerance_healthy" if necessary. + + Executes only if "value" crosses the threshold for more than the + amount of seconds defined in the alert_settings "tolerance" field. + It also sends the notification if required. Returns "None" if + value of "is_healthy_tolerant" is unchanged. Returns "True" if it + is the first metric write within threshold. Returns "False" in + other cases. This method is similar to "_set_is_healthy" but it takes into account the alert settings tolerance so it's slightly different @@ -364,9 +363,7 @@ def _set_is_healthy_tolerant( return first_time def check_threshold(self, value, time=None, retention_policy=None, send_alert=True): - """ - Checks if the threshold is crossed and notifies users accordingly - """ + """Checks if the threshold is crossed and notifies users accordingly""" try: alert_settings = self.alertsettings except ObjectDoesNotExist: @@ -624,9 +621,10 @@ def _default_query(self): @classmethod def _get_group_map(cls, time=None): - """ - Returns the chart group map for the specified days, - otherwise the default Chart.GROUP_MAP is returned + """Returns group map. + + Returns the chart group map for the specified days, otherwise the + default Chart.GROUP_MAP is returned. """ if ( not time @@ -689,7 +687,8 @@ def get_query( ) def get_top_fields(self, number): - """ + """Returns the top fields. + Returns list of top ``number`` of fields (highest sum) of a measurement in the specified time range (descending order). """ @@ -826,9 +825,7 @@ def json(self, time=DEFAULT_TIME, **kwargs): @staticmethod def _round(value, decimal_places): - """ - rounds value if it makes sense - """ + """Rounds value when necessary.""" control = 1.0 / 10**decimal_places if value < control: decimal_places += 2 @@ -933,10 +930,10 @@ def _time_crossed(self, time): @property def _tolerance_search_range(self): - """ - Allow sufficient room for checking - if the tolerance has been trepassed. - Minimum 15 minutes, maximum self._MINUTES_MAX * 1.05 + """Returns an amount of minutes to search. + + Allows sufficient room for checking if the tolerance has been + trepassed. Minimum 15 minutes, maximum self._MINUTES_MAX * 1.05. """ minutes = self.tolerance * 2 minutes = minutes if minutes > 15 else 15 @@ -944,8 +941,10 @@ def _tolerance_search_range(self): return int(minutes) def _is_crossed_by(self, current_value, time=None, retention_policy=None): - """ - do current_value and time cross the threshold and trepass the tolerance? + """Answers the following question: + + do current_value and time cross the threshold and trepass the + tolerance? """ value_crossed = self._value_crossed(current_value) if value_crossed is NotImplemented: diff --git a/openwisp_monitoring/monitoring/configuration.py b/openwisp_monitoring/monitoring/configuration.py index 15dbd4bda..83b2e5a98 100644 --- a/openwisp_monitoring/monitoring/configuration.py +++ b/openwisp_monitoring/monitoring/configuration.py @@ -743,9 +743,7 @@ def get_metric_configuration_choices(): def register_metric(metric_name, metric_config): - """ - Registers a new metric configuration. - """ + """Registers a new metric configuration.""" if not isinstance(metric_name, str): raise ImproperlyConfigured('Metric configuration name should be type "str".') if not isinstance(metric_config, dict): @@ -817,9 +815,7 @@ def get_chart_configuration_choices(): def register_chart(chart_name, chart_config): - """ - Registers a new chart configuration. - """ + """Registers a new chart configuration.""" if not isinstance(chart_name, str): raise ImproperlyConfigured('Chart name should be type "str".') if not isinstance(chart_config, dict): diff --git a/openwisp_monitoring/monitoring/migrations/influxdb/influxdb_alter_structure_0006.py b/openwisp_monitoring/monitoring/migrations/influxdb/influxdb_alter_structure_0006.py index 406bc4ea2..af4372622 100644 --- a/openwisp_monitoring/monitoring/migrations/influxdb/influxdb_alter_structure_0006.py +++ b/openwisp_monitoring/monitoring/migrations/influxdb/influxdb_alter_structure_0006.py @@ -40,9 +40,7 @@ def get_writable_data(read_data, tags, old_measurement, new_measurement): - """ - Prepares data that can be written by "timeseries_db.db.write_points" - """ + """Prepares data that can be written by "timeseries_db.db.write_points".""" write_data = [] for data_point in read_data.get_points(measurement=old_measurement): data = { @@ -171,11 +169,11 @@ def migrate_traffic_data(): def requires_migration(): - """ - Returns "False" if all measurements presents in InfluxDB - are present in EXCLUDED_MEASUREMENTS. This means that there - are no interface specific measurements. - Otherwise, returns "True". + """Indicates whether influxdb data migration is necessary. + + Returns "False" if all measurements presents in InfluxDB are present + in EXCLUDED_MEASUREMENTS. This means that there are no interface + specific measurements. Otherwise, returns "True". """ tsdb_measurements = timeseries_db.db.get_list_measurements() for measurement in tsdb_measurements: diff --git a/openwisp_monitoring/monitoring/tasks.py b/openwisp_monitoring/monitoring/tasks.py index 392cb6748..9778b8ffb 100644 --- a/openwisp_monitoring/monitoring/tasks.py +++ b/openwisp_monitoring/monitoring/tasks.py @@ -42,18 +42,13 @@ def _metric_post_write(name, values, metric, check_threshold_kwargs, **kwargs): def timeseries_write( self, name, values, metric=None, check_threshold_kwargs=None, **kwargs ): - """ - write with exponential backoff on a failure - """ + """Writes and retries with exponential backoff on failures.""" timeseries_db.write(name, values, **kwargs) _metric_post_write(name, values, metric, check_threshold_kwargs, **kwargs) def _timeseries_write(name, values, metric=None, check_threshold_kwargs=None, **kwargs): - """ - If the timeseries database is using UDP to write data, - then write data synchronously. - """ + """Handles writes synchronously when using UDP mode.""" if timeseries_db.use_udp: func = timeseries_write else: @@ -75,9 +70,10 @@ def _timeseries_write(name, values, metric=None, check_threshold_kwargs=None, ** **RETRY_OPTIONS ) def timeseries_batch_write(self, data): - """ - Similar to timeseries_write function above, but operates on - list of metric data (batch operation) + """Writes data in batches. + + Similar to timeseries_write function above, but operates on list of + metric data (batch operation) """ timeseries_db.batch_write(data) for metric_data in data: @@ -85,10 +81,7 @@ def timeseries_batch_write(self, data): def _timeseries_batch_write(data): - """ - If the timeseries database is using UDP to write data, - then write data synchronously. - """ + """If the timeseries database is using UDP to write data, then write data synchronously.""" if timeseries_db.use_udp: timeseries_batch_write(data=data) else: @@ -104,12 +97,12 @@ def delete_timeseries(key, tags): @shared_task def migrate_timeseries_database(): - """ - Perform migrations on timeseries database - asynchronously for changes introduced in + """Performs migrations of timeseries datab. + + Performed asynchronously, due to changes introduced in https://github.com/openwisp/openwisp-monitoring/pull/368 - To be removed in 1.1.0 release. + To be removed in a future release. """ from .migrations.influxdb.influxdb_alter_structure_0006 import ( migrate_influxdb_structure, diff --git a/openwisp_monitoring/monitoring/tests/test_charts.py b/openwisp_monitoring/monitoring/tests/test_charts.py index 23680ee4f..d5fe9cb55 100644 --- a/openwisp_monitoring/monitoring/tests/test_charts.py +++ b/openwisp_monitoring/monitoring/tests/test_charts.py @@ -24,9 +24,7 @@ class TestCharts(TestMonitoringMixin, TestCase): - """ - Tests for functionalities related to charts - """ + """Tests for functionalities related to charts""" def test_read(self): c = self._create_chart() diff --git a/openwisp_monitoring/monitoring/tests/test_monitoring_notifications.py b/openwisp_monitoring/monitoring/tests/test_monitoring_notifications.py index 65580d033..d183e550b 100644 --- a/openwisp_monitoring/monitoring/tests/test_monitoring_notifications.py +++ b/openwisp_monitoring/monitoring/tests/test_monitoring_notifications.py @@ -108,12 +108,12 @@ def test_resources_metric_threshold_deferred_not_crossed(self): self.assertEqual(Notification.objects.count(), 0) def test_general_check_threshold_crossed_for_long_time(self): - """ - this is going to be the most realistic scenario: - incoming metrics will always be stored with the current - timestamp, which means the system must be able to look - back in previous measurements to see if the AlertSettings - has been crossed for long enough + """Test threshold remains crossed for a long time. + + This is the most common scenario: incoming metrics will always be + stored with the current timestamp, which means the system must be + able to look back in previous measurements to see if the + AlertSettings has been crossed for long enough. """ admin = self._create_admin() m = self._create_general_metric(name='load') diff --git a/openwisp_monitoring/views.py b/openwisp_monitoring/views.py index 840042a5a..3be0c5e1d 100644 --- a/openwisp_monitoring/views.py +++ b/openwisp_monitoring/views.py @@ -22,16 +22,11 @@ class MonitoringApiViewMixin: def _get_charts(self, request, *args, **kwargs): - """ - Hook to return Chart query. - """ + """Hook to return Chart query.""" raise NotImplementedError def _get_additional_data(request, *args, **kwargs): - """ - Hook to return any additonal data that should be - included in the response. - """ + """Hook to return any additonal data that should be included in the response.""" return {} def _validate_custom_date(self, start, end, tmz): @@ -88,9 +83,7 @@ def get(self, request, *args, **kwargs): return Response(data) def _get_chart_additional_query_kwargs(self, chart): - """ - Hook to provide additional kwargs to Chart.read. - """ + """Hook to provide additional kwargs to Chart.read.""" return None def _get_charts_data(self, charts, time, timezone, start_date, end_date): diff --git a/pyproject.toml b/pyproject.toml index 424b6800e..ef07e60d0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ omit = [ ] [tool.docstrfmt] -extend_exclude = ["**/*.py", "README.rst"] +extend_exclude = ["**/*.py"] [tool.isort] known_third_party = ["django", "django_x509"] diff --git a/setup.py b/setup.py index 250396304..0a9273ee8 100755 --- a/setup.py +++ b/setup.py @@ -8,9 +8,7 @@ def get_install_requires(): - """ - parse requirements.txt, ignore links, exclude comments - """ + """parse requirements.txt, ignore links, exclude comments""" requirements = [] for line in open('requirements.txt').readlines(): # skip to next iteration if comment or empty line diff --git a/tests/openwisp2/sample_check/models.py b/tests/openwisp2/sample_check/models.py index 10ad15a04..b39291ca9 100644 --- a/tests/openwisp2/sample_check/models.py +++ b/tests/openwisp2/sample_check/models.py @@ -12,9 +12,10 @@ class Meta(AbstractCheck.Meta): abstract = False def perform_check(self, store=True): - """ - This method has been added for testing `last_called` field. - It need not be added to retain original behaviour. + """Performs check. + + This method has been added for testing `last_called` field. It + need not be added to retain original behaviour. """ self.last_called = now() self.full_clean()