openwisp-monitoring


Nameopenwisp-monitoring JSON
Version 1.0.4 PyPI version JSON
download
home_pagehttp://openwisp.org
SummaryOpenWISP 2 Monitoring
upload_time2023-01-24 19:48:59
maintainer
docs_urlNone
authorFederico Capoano
requires_python
licenseGPL3
keywords django netjson networking openwisp monitoring
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            openwisp-monitoring
===================

.. image:: https://github.com/openwisp/openwisp-monitoring/workflows/OpenWISP%20Monitoring%20CI%20Build/badge.svg?branch=master
    :target: https://github.com/openwisp/openwisp-monitoring/actions?query=workflow%3A%22OpenWISP+Monitoring+CI+Build%22
    :alt: CI build status

.. image:: https://coveralls.io/repos/github/openwisp/openwisp-monitoring/badge.svg?branch=master
    :target: https://coveralls.io/github/openwisp/openwisp-monitoring?branch=master
    :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

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

.. image:: https://img.shields.io/gitter/room/nwjs/nw.js.svg?style=flat-square
   :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

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

------------

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.

See the `available features <#available-features>`_.

`OpenWISP <http://openwisp.org>`_ 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 <https://github.com/openwisp/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 <https://github.com/openwisp/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 <https://github.com/openwisp/openwisp-firmware-upgrader>`_:
  automated firmware upgrades (single device or mass network upgrades)
- `openwisp-radius <https://github.com/openwisp/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 <https://github.com/openwisp/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
<https://openwisp.io/docs/general/architecture.html>`_.

.. 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 `uptime <#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>`_)
* Charts can be viewed at resolutions of 1 day, 3 days, a week, a month and a year
* 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;
  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 <http://netjson.org/docs/what.html#devicemonitoring>`_

------------

.. contents:: **Table of Contents**:
   :backlinks: none
   :depth: 3

------------

Installation instructions
-------------------------

Deploy it in production
~~~~~~~~~~~~~~~~~~~~~~~

See:

- `ansible-openwisp2 <https://github.com/openwisp/ansible-openwisp2>`_
- `docker-openwisp <https://github.com/openwisp/docker-openwisp>`_

Install system dependencies
~~~~~~~~~~~~~~~~~~~~~~~~~~~

*openwisp-monitoring* uses InfluxDB to store metrics. Follow the
`installation instructions from InfluxDB's official documentation <https://docs.influxdata.com/influxdb/v1.8/introduction/install/>`_.

**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/<your_fork>/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 <https://pypi.org/project/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 --parallel

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
<https://github.com/openwisp/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
<https://github.com/openwisp/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
        'django.contrib.admin',
        'django.forms',
        # 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',
    }

``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',
            'schedule': timedelta(minutes=5),
        },
    }

    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
<https://github.com/openwisp/openwisp-config#install-precompiled-package>`_
on your device.

Install monitoring packages on the device
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

`Install the openwrt-openwisp-monitoring packages
<https://github.com/openwisp/openwrt-openwisp-monitoring/tree/master#install-pre-compiled-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
<https://github.com/openwisp/openwisp-controller#how-to-configure-push-updates>`_,
`executing shell commands
<https://github.com/openwisp/openwisp-controller#sending-commands-to-devices>`_ or
`performing firmware upgrades
<https://github.com/openwisp/openwisp-firmware-upgrader#perform-a-firmware-upgrade-to-a-specific-device>`_,
**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
    <https://openwisp.io/docs/user/vpn.html>`_
  - `Wireguard tunnel automation
    <https://github.com/openwisp/openwisp-controller#how-to-setup-wireguard-tunnels>`_

  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 <https://openwisp.io/docs/user/templates.html>`_.

- The `openwisp-config <https://github.com/openwisp/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
<https://github.com/openwisp/openwisp-monitoring#default-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
   `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``, ``packet_loss``, ``rtt``                           |
+--------------------+----------------------------------------------------------------+

**Uptime**:

.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/uptime.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': '<organization-id-of-the-related-device>',      |
|                    |       'ifname': '<interface-name>',                                      |
|                    |       # optional                                                         |
|                    |       'location_id': '<location-id-of-the-related-device-if-present>',   |
|                    |       'floorplan_id': '<floorplan-id-of-the-related-device-if-present>', |
|                    |     }                                                                    |
+--------------------+--------------------------------------------------------------------------+
| **configuration**: | ``traffic``                                                              |
+--------------------+--------------------------------------------------------------------------+
| **charts**:        | ``traffic``                                                              |
+--------------------+--------------------------------------------------------------------------+

.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/traffic.png
  :align: center

WiFi Clients
~~~~~~~~~~~~

+--------------------+--------------------------------------------------------------------------+
| **measurement**:   | ``wifi_clients``                                                         |
+--------------------+--------------------------------------------------------------------------+
| **type**:          | ``int``                                                                  |
+--------------------+--------------------------------------------------------------------------+
| **fields**:        | ``clients``                                                              |
+--------------------+--------------------------------------------------------------------------+
| **tags**:          | .. code-block:: python                                                   |
|                    |                                                                          |
|                    |     {                                                                    |
|                    |       'organization_id': '<organization-id-of-the-related-device>',      |
|                    |       'ifname': '<interface-name>',                                      |
|                    |       # optional                                                         |
|                    |       'location_id': '<location-id-of-the-related-device-if-present>',   |
|                    |       'floorplan_id': '<floorplan-id-of-the-related-device-if-present>', |
|                    |     }                                                                    |
+--------------------+--------------------------------------------------------------------------+
| **configuration**: | ``clients``                                                              |
+--------------------+--------------------------------------------------------------------------+
| **charts**:        | ``wifi_clients``                                                         |
+--------------------+--------------------------------------------------------------------------+


.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/wifi-clients.png
  :align: center

Memory Usage
~~~~~~~~~~~~

+--------------------+--------------------------------------------------------------------------------------------------------------------------------------+
| **measurement**:   | ``<memory>``                                                                                                                         |
+--------------------+--------------------------------------------------------------------------------------------------------------------------------------+
| **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

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 device ``uptime`` and ``RTT (Round trip time)``.
The Charts ``uptime``, ``packet loss`` and ``rtt`` are created. The ``fping``
command is used to collect these metrics.
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 <https://github.com/openwisp/openwisp-config/>`_
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.

Settings
--------

``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_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_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``.

``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 <https://github.com/openwisp/openwisp-network-topology#integration-with-openwisp-controller-and-openwisp-monitoring>`_

``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
<https://docs.celeryproject.org/en/stable/userguide/tasks.html#automatic-retry-for-known-exceptions>`_.

``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 <https://github.com/openwisp/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_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``.

For example, if you want to change the traffic chart to show
MB (megabytes) instead of GB (Gigabytes) you can use:

.. code-block:: python

    OPENWISP_MONITORING_CHARTS = {
        'traffic': {
            'unit': ' MB',
            'description': (
                'Network traffic, download and upload, measured on '
                'the interface "{metric.key}", measured in MB.'
            ),
            'query': {
                'influxdb': (
                    "SELECT SUM(tx_bytes) / 1000000 AS upload, "
                    "SUM(rx_bytes) / 1000000 AS download FROM {key} "
                    "WHERE time >= '{time}' AND content_type = '{content_type}' "
                    "AND object_id = '{object_id}' GROUP BY time(1d)"
                )
            },
        }
    }

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']
        }
    }

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

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': _('Uptime'),
                '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 uptime')],
                'unit': '%',
                'order': 200,
                'colorscale': {
                    'max': 100,
                    'min': 0,
                    'label': _('Reachable'),
                    'scale': [
                        [[0, '#c13000'],
                        [0.1,'cb7222'],
                        [0.5,'#deed0e'],
                        [0.9, '#7db201'],
                        [1, '#498b26']],
                    ],
                    'map': [
                       [100, '#498b26', _('Reachable')],
                       [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*.

**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
<https://github.com/openwisp/openwisp-notifications#registering--unregistering-notification-types>`_.

Once a new notification type is registered, you have to use the `"notify" signal provided in
openwisp-notifications <https://github.com/openwisp/openwisp-notifications#sending-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
<https://www.django-rest-framework.org/topics/browsable-api/>`_,
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 device charts and device status data
#############################################

.. code-block:: text

    GET /v1/monitoring/device/{pk}/?key={key}&status=true

The format used for Device Status is inspired by `NetJSON DeviceMonitoring <http://netjson.org/docs/what.html#devicemonitoring>`_.

**Note**: If the request is made without ``?status=true`` then only device charts
data would be returned.

Collect device metrics and status
#################################

.. code-block:: text

    POST /v1/monitoring/device/{pk}/?key={key}&time={time}

If data is latest then an additional parameter current can also be passed. For e.g.:

.. code-block:: text

    POST /v1/monitoring/device/{pk}/?key={key}&time={time}&current=true

The format used for Device Status is inspired by `NetJSON DeviceMonitoring <http://netjson.org/docs/what.html#devicemonitoring>`_.

**Note**: Device data will be saved with in timeseries database with the 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 <https://github.com/openwisp/openwrt-openwisp-monitoring#collecting-vs-sending>`_.

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 <https://github.com/openwisp/openwrt-openwisp-monitoring#openwrt-openwisp-monitoring>`_.
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 <https://github.com/openwisp/openwrt-openwisp-monitoring#openwrt-openwisp-monitoring>`_.

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 <https://github.com/openwisp/openwrt-openwisp-monitoring#openwrt-openwisp-monitoring>`_
  is installed on the device.

Install the `monitoring packages <https://github.com/openwisp/openwrt-openwisp-monitoring#openwrt-openwisp-monitoring>`_
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 <https://github.com/openwisp/openwrt-openwisp-monitoring#openwrt-openwisp-monitoring>`_
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 <https://github.com/openwisp/openwrt-openwisp-monitoring#openwrt-openwisp-monitoring>`_.

Extending openwisp-monitoring
-----------------------------

One of the core values of the OpenWISP project is `Software Reusability <http://openwisp.io/docs/general/values.html#software-reusability-means-long-term-sustainability>`_,
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 <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/>`_
and the ``sample apps`` namely `sample_check <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_check/>`_,
`sample_monitoring <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_monitoring/>`_, `sample_device_monitoring <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/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 <https://docs.python.org/3/tutorial/modules.html#packages>`_
(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 <https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH>`_
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 <https://docs.djangoproject.com/en/dev/intro/tutorial01/>`_.

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 <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_check/__init__.py>`_.
- `sample_check/apps.py <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_check/apps.py>`_.
- `sample_monitoring/__init__.py <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_monitoring/__init__.py>`_.
- `sample_monitoring/apps.py <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_monitoring/apps.py>`_.
- `sample_device_monitoring/__init__.py <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_device_monitoring/__init__.py>`_.
- `sample_device_monitoring/apps.py <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_device_monitoring/apps.py>`_.

For more information regarding the concept of ``AppConfig`` please refer to
the `"Applications" section in the django documentation <https://docs.djangoproject.com/en/dev/ref/applications/>`_.

7. Create your custom models
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

To extend ``check`` app, refer to `sample_check models.py file <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_check/models.py>`_.

To extend ``monitoring`` app, refer to `sample_monitoring models.py file <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_monitoring/models.py>`_.

To extend ``device_monitoring`` app, refer to `sample_device_monitoring models.py file <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_device_monitoring/models.py>`_.

**Note**:

- For doubts regarding how to use, extend or develop models please refer to
  the `"Models" section in the django documentation <https://docs.djangoproject.com/en/dev/topics/db/models/>`_.
- For doubts regarding proxy models please refer to `proxy models <https://docs.djangoproject.com/en/dev/topics/db/models/#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'

Substitute ``<YOUR_MODULE_NAME>`` 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 <https://docs.djangoproject.com/en/dev/topics/migrations/>`_.

10. Create your custom admin
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

To extend ``check`` app, refer to `sample_check admin.py file <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_check/admin.py>`_.

To extend ``monitoring`` app, refer to `sample_monitoring admin.py file <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_monitoring/admin.py>`_.

To extend ``device_monitoring`` app, refer to `sample_device_monitoring admin.py file <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_device_monitoring/admin.py>`_.

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 <https://docs.djangoproject.com/en/dev/ref/contrib/admin/>`_.

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

    DeviceAdmin.list_display.insert(1, 'my_custom_field')
    DeviceAdmin.ordering = ['-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 swapper import load_model

    Device = load_model('config', 'Device')

    admin.site.unregister(Device)

    @admin.register(Device)
    class DeviceAdmin(BaseDeviceAdmin):
        # 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 <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/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 <https://docs.djangoproject.com/en/dev/topics/http/urls/>`_.

12. Create celery.py
~~~~~~~~~~~~~~~~~~~~

Please refer to the `celery.py <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/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 <https://docs.celeryproject.org/en/master/django/first-steps-with-django.html>`_.

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 <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_check/management/commands/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 <https://docs.djangoproject.com/en/dev/howto/custom-management-commands/>`_.

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 <https://github.com/openwisp/openwisp-monitoring/blob/master/tests/openwisp2/sample_check/tests.py>`_
to find out how to do this.

For, extending ``device_monitoring`` app see the `tests of sample_device_monitoring app <https://github.com/openwisp/openwisp-monitoring/blob/master/tests/openwisp2/sample_device_monitoring/tests.py>`_
to find out how to do this.

For, extending ``monitoring`` app see the `tests of sample_monitoring app <https://github.com/openwisp/openwisp-monitoring/blob/master/tests/openwisp2/sample_monitoring/tests.py>`_
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<pk>[^/]+)/$',
        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<path>.*)$', DeviceMetricView.as_view(), name='api_device_metric',),
    ]

Contributing
------------

Please refer to the `OpenWISP contributing guidelines <http://openwisp.io/docs/developer/contributing.html>`_.



            

Raw data

            {
    "_id": null,
    "home_page": "http://openwisp.org",
    "name": "openwisp-monitoring",
    "maintainer": "",
    "docs_url": null,
    "requires_python": "",
    "maintainer_email": "",
    "keywords": "django,netjson,networking,openwisp,monitoring",
    "author": "Federico Capoano",
    "author_email": "federico.capoano@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/4b/3a/74bfcbb6c8411762060fbe9deba442a2d571d6f0ffd1efa01f39051fdef5/openwisp-monitoring-1.0.4.tar.gz",
    "platform": "Platform Independent",
    "description": "openwisp-monitoring\n===================\n\n.. image:: https://github.com/openwisp/openwisp-monitoring/workflows/OpenWISP%20Monitoring%20CI%20Build/badge.svg?branch=master\n    :target: https://github.com/openwisp/openwisp-monitoring/actions?query=workflow%3A%22OpenWISP+Monitoring+CI+Build%22\n    :alt: CI build status\n\n.. image:: https://coveralls.io/repos/github/openwisp/openwisp-monitoring/badge.svg?branch=master\n    :target: https://coveralls.io/github/openwisp/openwisp-monitoring?branch=master\n    :alt: Test coverage\n\n.. image:: https://img.shields.io/librariesio/github/openwisp/openwisp-monitoring\n   :target: https://libraries.io/github/openwisp/openwisp-monitoring#repository_dependencies\n   :alt: Dependency monitoring\n\n.. image:: https://badge.fury.io/py/openwisp-monitoring.svg\n    :target: http://badge.fury.io/py/openwisp-monitoring\n    :alt: pypi\n\n.. image:: https://pepy.tech/badge/openwisp-monitoring\n   :target: https://pepy.tech/project/openwisp-monitoring\n   :alt: downloads\n\n.. image:: https://img.shields.io/gitter/room/nwjs/nw.js.svg?style=flat-square\n   :target: https://gitter.im/openwisp/monitoring\n   :alt: support chat\n\n.. image:: https://img.shields.io/badge/code%20style-black-000000.svg\n   :target: https://pypi.org/project/black/\n   :alt: code style: black\n\n.. image:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/monitoring-demo.gif\n   :target: https://github.com/openwisp/openwisp-monitoring/tree/docs/docs/monitoring-demo.gif\n   :alt: Feature Highlights\n\n------------\n\nOpenWISP Monitoring is a network monitoring system written in Python and Django,\ndesigned to be **extensible**, **programmable**, **scalable** and easy to use by end users:\nonce the system is configured, monitoring checks, alerts and metric collection\nhappens automatically.\n\nSee the `available features <#available-features>`_.\n\n`OpenWISP <http://openwisp.org>`_ is not only an application designed for end users,\nbut can also be used as a framework on which custom network automation solutions can be\nbuilt on top of its building blocks.\n\nOther popular building blocks that are part of the OpenWISP ecosystem are:\n\n- `openwisp-controller <https://github.com/openwisp/openwisp-controller>`_:\n  network and WiFi controller: provisioning, configuration management,\n  x509 PKI management and more; works on OpenWRT, but designed to work also on other systems.\n- `openwisp-network-topology <https://github.com/openwisp/openwisp-network-topology>`_:\n  provides way to collect and visualize network topology data from\n  dynamic mesh routing daemons or other network software (eg: OpenVPN);\n  it can be used in conjunction with openwisp-monitoring to get a better idea\n  of the state of the network\n- `openwisp-firmware-upgrader <https://github.com/openwisp/openwisp-firmware-upgrader>`_:\n  automated firmware upgrades (single device or mass network upgrades)\n- `openwisp-radius <https://github.com/openwisp/openwisp-radius>`_:\n  based on FreeRADIUS, allows to implement network access authentication systems like\n  802.1x WPA2 Enterprise, captive portal authentication, Hotspot 2.0 (802.11u)\n- `openwisp-ipam <https://github.com/openwisp/openwisp-ipam>`_:\n  it allows to manage the IP address space of networks\n\n**For a more complete overview of the OpenWISP modules and architecture**,\nsee the\n`OpenWISP Architecture Overview\n<https://openwisp.io/docs/general/architecture.html>`_.\n\n.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/dashboard.png\n  :align: center\n\nAvailable Features\n------------------\n\n* Collection of monitoring information in a timeseries database (currently only influxdb is supported)\n* Allows to browse alerts easily from the user interface with one click\n* Collects and displays `device status <#device-status>`_ information like\n  uptime, RAM status, CPU load averages,\n  Interface properties and addresses, WiFi interface status and associated clients,\n  Neighbors information, DHCP Leases, Disk/Flash status\n* Monitoring charts for `uptime <#ping>`_, `packet loss <#ping>`_,\n  `round trip time (latency) <#ping>`_,\n  `associated wifi clients <#wifi-clients>`_, `interface traffic <#traffic>`_,\n  `RAM usage <#memory-usage>`_, `CPU load <#cpu-load>`_, `flash/disk usage <#disk-usage>`_,\n  mobile signal (LTE/UMTS/GSM `signal strength <#mobile-signal-strength>`_,\n  `signal quality <#mobile-signal-quality>`_,\n  `access technology in use <#mobile-access-technology-in-use>`_)\n* Charts can be viewed at resolutions of 1 day, 3 days, a week, a month and a year\n* Configurable alerts\n* CSV Export of monitoring data\n* An overview of the status of the network is shown in the admin dashboard,\n  a chart shows the percentages of devices which are online, offline or having issues;\n  a geographic map is also available for those who use the geographic features of OpenWISP\n* Possibility to configure additional `Metrics <#openwisp_monitoring_metrics>`_ and `Charts <#openwisp_monitoring_charts>`_\n* Extensible active check system: it's possible to write additional checks that\n  are run periodically using python classes\n* Extensible metrics and charts: it's possible to define new metrics and new charts\n* API to retrieve the chart metrics and status information of each device\n  based on `NetJSON DeviceMonitoring <http://netjson.org/docs/what.html#devicemonitoring>`_\n\n------------\n\n.. contents:: **Table of Contents**:\n   :backlinks: none\n   :depth: 3\n\n------------\n\nInstallation instructions\n-------------------------\n\nDeploy it in production\n~~~~~~~~~~~~~~~~~~~~~~~\n\nSee:\n\n- `ansible-openwisp2 <https://github.com/openwisp/ansible-openwisp2>`_\n- `docker-openwisp <https://github.com/openwisp/docker-openwisp>`_\n\nInstall system dependencies\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n*openwisp-monitoring* uses InfluxDB to store metrics. Follow the\n`installation instructions from InfluxDB's official documentation <https://docs.influxdata.com/influxdb/v1.8/introduction/install/>`_.\n\n**Note:** Only *InfluxDB 1.8.x* is supported in *openwisp-monitoring*.\n\nInstall system packages:\n\n.. code-block:: shell\n\n    sudo apt install -y openssl libssl-dev \\\n                        gdal-bin libproj-dev libgeos-dev \\\n                        fping\n\nInstall stable version from PyPI\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nInstall from PyPI:\n\n.. code-block:: shell\n\n    pip install openwisp-monitoring\n\nInstall development version\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nInstall tarball:\n\n.. code-block:: shell\n\n    pip install https://github.com/openwisp/openwisp-monitoring/tarball/master\n\nAlternatively, you can install via pip using git:\n\n.. code-block:: shell\n\n    pip install -e git+git://github.com/openwisp/openwisp-monitoring#egg=openwisp_monitoring\n\nIf you want to contribute, follow the instructions in\n`\"Installing for development\" <#installing-for-development>`_ section.\n\nInstalling for development\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nInstall the system dependencies as mentioned in the\n`\"Install system dependencies\" <#install-system-dependencies>`_ section.\nInstall these additional packages that are required for development:\n\n.. code-block:: shell\n\n    sudo apt install -y sqlite3 libsqlite3-dev \\\n                        libspatialite-dev libsqlite3-mod-spatialite \\\n                        chromium\n\nFork and clone the forked repository:\n\n.. code-block:: shell\n\n    git clone git://github.com/<your_fork>/openwisp-monitoring\n\nNavigate into the cloned repository:\n\n.. code-block:: shell\n\n    cd openwisp-monitoring/\n\nStart Redis and InfluxDB using Docker:\n\n.. code-block:: shell\n\n    docker-compose up -d redis influxdb\n\nSetup and activate a virtual-environment. (we'll be using  `virtualenv <https://pypi.org/project/virtualenv/>`_)\n\n.. code-block:: shell\n\n    python -m virtualenv env\n    source env/bin/activate\n\nMake sure that you are using pip version 20.2.4 before moving to the next step:\n\n.. code-block:: shell\n\n    pip install -U pip wheel setuptools\n\nInstall development dependencies:\n\n.. code-block:: shell\n\n    pip install -e .\n    pip install -r requirements-test.txt\n    npm install -g jshint stylelint\n\nInstall WebDriver for Chromium for your browser version from `<https://chromedriver.chromium.org/home>`_\nand extract ``chromedriver`` to one of directories from your ``$PATH`` (example: ``~/.local/bin/``).\n\nCreate database:\n\n.. code-block:: shell\n\n    cd tests/\n    ./manage.py migrate\n    ./manage.py createsuperuser\n\nRun celery and celery-beat with the following commands (separate terminal windows are needed):\n\n.. code-block:: shell\n\n    cd tests/\n    celery -A openwisp2 worker -l info\n    celery -A openwisp2 beat -l info\n\nLaunch development server:\n\n.. code-block:: shell\n\n    ./manage.py runserver 0.0.0.0:8000\n\nYou can access the admin interface at http://127.0.0.1:8000/admin/.\n\nRun tests with:\n\n.. code-block:: shell\n\n    ./runtests.py --parallel\n\nRun quality assurance tests with:\n\n.. code-block:: shell\n\n    ./run-qa-checks\n\nInstall and run on docker\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n**Note**: This Docker image is for development purposes only.\nFor the official OpenWISP Docker images, see: `docker-openwisp\n<https://github.com/openwisp/docker-openwisp>`_.\n\nBuild from the Dockerfile:\n\n.. code-block:: shell\n\n    docker-compose build\n\nRun the docker container:\n\n.. code-block:: shell\n\n    docker-compose up\n\nSetup (integrate in an existing Django project)\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFollow the setup instructions of `openwisp-controller\n<https://github.com/openwisp/openwisp-controller>`_, then add the settings described below.\n\n.. code-block:: python\n\n    INSTALLED_APPS = [\n        # django apps\n        # all-auth\n        'django.contrib.sites',\n        'allauth',\n        'allauth.account',\n        'allauth.socialaccount',\n        'django_extensions',\n        'django_filters',\n        # openwisp2 modules\n        'openwisp_users',\n        'openwisp_controller.pki',\n        'openwisp_controller.config',\n        'openwisp_controller.connection',\n        'openwisp_controller.geo',\n        # monitoring\n        'openwisp_monitoring.monitoring',\n        'openwisp_monitoring.device',\n        'openwisp_monitoring.check',\n        'nested_admin',\n        # notifications\n        'openwisp_notifications',\n        # openwisp2 admin theme (must be loaded here)\n        'openwisp_utils.admin_theme',\n        # admin\n        'django.contrib.admin',\n        'django.forms',\n        # other dependencies ...\n    ]\n\n    # Make sure you change them in production\n    # You can select one of the backends located in openwisp_monitoring.db.backends\n    TIMESERIES_DATABASE = {\n        'BACKEND': 'openwisp_monitoring.db.backends.influxdb',\n        'USER': 'openwisp',\n        'PASSWORD': 'openwisp',\n        'NAME': 'openwisp2',\n        'HOST': 'localhost',\n        'PORT': '8086',\n    }\n\n``urls.py``:\n\n.. code-block:: python\n\n    from django.conf import settings\n    from django.conf.urls import include, url\n    from django.contrib.staticfiles.urls import staticfiles_urlpatterns\n\n    from openwisp_utils.admin_theme.admin import admin, openwisp_admin\n\n    openwisp_admin()\n\n    urlpatterns = [\n        url(r'^admin/', include(admin.site.urls)),\n        url(r'', include('openwisp_controller.urls')),\n        url(r'', include('openwisp_monitoring.urls')),\n    ]\n\n    urlpatterns += staticfiles_urlpatterns()\n\nConfigure caching (you may use a different cache storage if you want):\n\n.. code-block:: python\n\n    CACHES = {\n        'default': {\n            'BACKEND': 'django_redis.cache.RedisCache',\n            'LOCATION': 'redis://localhost/0',\n            'OPTIONS': {\n                'CLIENT_CLASS': 'django_redis.client.DefaultClient',\n            }\n        }\n    }\n\n    SESSION_ENGINE = 'django.contrib.sessions.backends.cache'\n    SESSION_CACHE_ALIAS = 'default'\n\nConfigure celery (you may use a different broker if you want):\n\n.. code-block:: python\n\n    # here we show how to configure celery with redis but you can\n    # use other brokers if you want, consult the celery docs\n    CELERY_BROKER_URL = 'redis://localhost/1'\n    CELERY_BEAT_SCHEDULE = {\n        'run_checks': {\n            'task': 'openwisp_monitoring.check.tasks.run_checks',\n            'schedule': timedelta(minutes=5),\n        },\n    }\n\n    INSTALLED_APPS.append('djcelery_email')\n    EMAIL_BACKEND = 'djcelery_email.backends.CeleryEmailBackend'\n\nIf you decide to use Redis (as shown in these examples),\ninstall the following python packages.\n\n.. code-block:: shell\n\n    pip install redis django-redis\n\nQuickstart Guide\n----------------\n\nInstall OpenWISP Monitoring\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nInstall *OpenWISP Monitoring* using one of the methods mentioned in the\n`\"Installation instructions\" <#installation-instructions>`_.\n\nInstall openwisp-config on the device\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n`Install the openwisp-config agent for OpenWrt\n<https://github.com/openwisp/openwisp-config#install-precompiled-package>`_\non your device.\n\nInstall monitoring packages on the device\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n`Install the openwrt-openwisp-monitoring packages\n<https://github.com/openwisp/openwrt-openwisp-monitoring/tree/master#install-pre-compiled-packages>`_\non your device.\n\nThese packages collect and send the\nmonitoring data from the device to OpenWISP Monitoring and\nare required to collect `metrics <#openwisp_monitoring_metrics>`_\nlike interface traffic, WiFi clients, CPU load, memory usage, etc.\n\n**Note**: if you are an existing user of *openwisp-monitoring* and are using\nthe legacy *monitoring template* for collecting metrics, we highly recommend\n`Migrating from monitoring scripts to monitoring packages\n<#migrating-from-monitoring-scripts-to-monitoring-packages>`_.\n\nMake sure OpenWISP can reach your devices\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIn order to perform `active checks <#available-checks>`_ and other actions like\n`triggering the push of configuration changes\n<https://github.com/openwisp/openwisp-controller#how-to-configure-push-updates>`_,\n`executing shell commands\n<https://github.com/openwisp/openwisp-controller#sending-commands-to-devices>`_ or\n`performing firmware upgrades\n<https://github.com/openwisp/openwisp-firmware-upgrader#perform-a-firmware-upgrade-to-a-specific-device>`_,\n**the OpenWISP server needs to be able to reach the network devices**.\n\nThere are mainly two deployment scenarios for OpenWISP:\n\n1. the OpenWISP server is deployed on the public internet and the devices are\n   geographically distributed across different locations:\n   **in this case a management tunnel is needed**\n2. the OpenWISP server is deployed on a computer/server which is located in\n   the same Layer 2 network (that is, in the same LAN) where the devices\n   are located.\n   **in this case a management tunnel is NOT needed**\n\n1. Public internet deployment\n#############################\n\nThis is the most common scenario:\n\n- the OpenWISP server is deployed to the public internet, hence the\n  server has a public IPv4 (and IPv6) address and usually a valid\n  SSL certificate provided by Mozilla Letsencrypt or another SSL provider\n- the network devices are geographically distributed across different\n  locations (different cities, different regions, different countries)\n\nIn this scenario, the OpenWISP application will not be able to reach the\ndevices **unless a management tunnel** is used, for that reason having\na management VPN like OpenVPN, Wireguard or any other tunneling solution\nis paramount, not only to allow OpenWISP to work properly, but also to\nbe able to perform debugging and troubleshooting when needed.\n\nIn this scenario, the following requirements are needed:\n\n- a VPN server must be installed in a way that the OpenWISP\n  server can reach the VPN peers, for more information on how to do this\n  via OpenWISP please refer to the following sections:\n\n  - `OpenVPN tunnel automation\n    <https://openwisp.io/docs/user/vpn.html>`_\n  - `Wireguard tunnel automation\n    <https://github.com/openwisp/openwisp-controller#how-to-setup-wireguard-tunnels>`_\n\n  If you prefer to use other tunneling solutions (L2TP, Softether, etc.)\n  and know how to configure those solutions on your own,\n  that's totally fine as well.\n\n  If the OpenWISP server is connected to a network infrastructure\n  which allows it to reach the devices via pre-existing tunneling or\n  Intranet solutions (eg: MPLS, SD-WAN), then setting up a VPN server\n  is not needed, as long as there's a dedicated interface on OpenWrt\n  which gets an IP address assigned to it and which is reachable from\n  the OpenWISP server.\n\n- The devices must be configured to join the management tunnel automatically,\n  either via a pre-existing configuration in the firmware or via an\n  `OpenWISP Template <https://openwisp.io/docs/user/templates.html>`_.\n\n- The `openwisp-config <https://github.com/openwisp/openwisp-config>`_\n  agent on the devices must be configured to specify\n  the ``management_interface`` option, the agent will communicate the\n  IP of the management interface to the OpenWISP Server and OpenWISP will\n  use the management IP for reaching the device.\n\n  For example, if the *management interface* is named ``tun0``,\n  the openwisp-config configuration should look like the following example:\n\n.. code-block:: text\n\n    # In /etc/config/openwisp on the device\n\n    config controller 'http'\n        # ... other configuration directives ...\n        option management_interface 'tun0'\n\n2. LAN deployment\n#################\n\nWhen the OpenWISP server and the network devices are deployed in the same\nL2 network (eg: an office LAN) and the OpenWISP server is reachable\non the LAN address, OpenWISP can then use the **Last IP** field of the\ndevices to reach them.\n\nIn this scenario it's necessary to set the\n`\"OPENWISP_MONITORING_MANAGEMENT_IP_ONLY\" <#openwisp-monitoring-management-ip-only>`_\nsetting to ``False``.\n\nCreating checks for a device\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nBy default, the `active checks <#available-checks>`_ are created\nautomatically for all devices, unless the automatic creation of some\nspecific checks has been disabled, for more information on how to do this,\nrefer to the `active checks <#available-checks>`_ section.\n\nThese checks are created and executed in the background by celery workers.\n\nPassive vs Active Metric Collection\n-----------------------------------\n\nThe `the different device metric\n<https://github.com/openwisp/openwisp-monitoring#default-metrics>`_\ncollected by OpenWISP Monitoring can be divided in two categories:\n\n1. **metrics collected actively by OpenWISP**:\n   these metrics are collected by the celery workers running on the\n   OpenWISP server, which continuously sends network requests to the\n   devices and store the results;\n2. **metrics collected passively by OpenWISP**:\n   these metrics are sent by the\n   `openwrt-openwisp-monitoring agent <#install-monitoring-packages-on-the-device>`_\n   installed on the network devices and are collected by OpenWISP via\n   its REST API.\n\nThe `\"Available Checks\" <#available-checks>`_ section of this document\nlists the currently implemented **active checks**.\n\nDevice Health Status\n--------------------\n\nThe possible values for the health status field (``DeviceMonitoring.status``)\nare explained below.\n\n``UNKNOWN``\n~~~~~~~~~~~\n\nWhenever a new device is created it will have ``UNKNOWN`` as it's default Heath Status.\n\nIt implies that the system doesn't know whether the device is reachable yet.\n\n``OK``\n~~~~~~\n\nEverything is working normally.\n\n``PROBLEM``\n~~~~~~~~~~~\n\nOne of the metrics has a value which is not in the expected range\n(the threshold value set in the alert settings has been crossed).\n\nExample: CPU usage should be less than 90% but current value is at 95%.\n\n``CRITICAL``\n~~~~~~~~~~~~\n\nOne of the metrics defined in ``OPENWISP_MONITORING_CRITICAL_DEVICE_METRICS``\nhas a value which is not in the expected range\n(the threshold value set in the alert settings has been crossed).\n\nExample: ping is by default a critical metric which is expected to be always 1\n(reachable).\n\nDefault Metrics\n---------------\n\nDevice Status\n~~~~~~~~~~~~~\n\nThis metric stores the status of the device for viewing purposes.\n\n.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/device-status-1.png\n  :align: center\n\n.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/device-status-2.png\n  :align: center\n\n.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/device-status-3.png\n  :align: center\n\n.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/device-status-4.png\n  :align: center\n\nPing\n~~~~\n\n+--------------------+----------------------------------------------------------------+\n| **measurement**:   | ``ping``                                                       |\n+--------------------+----------------------------------------------------------------+\n| **types**:         | ``int`` (reachable and loss), ``float`` (rtt)                  |\n+--------------------+----------------------------------------------------------------+\n| **fields**:        | ``reachable``, ``loss``, ``rtt_min``, ``rtt_max``, ``rtt_avg`` |\n+--------------------+----------------------------------------------------------------+\n| **configuration**: | ``ping``                                                       |\n+--------------------+----------------------------------------------------------------+\n| **charts**:        | ``uptime``, ``packet_loss``, ``rtt``                           |\n+--------------------+----------------------------------------------------------------+\n\n**Uptime**:\n\n.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/uptime.png\n  :align: center\n\n**Packet loss**:\n\n.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/packet-loss.png\n  :align: center\n\n**Round Trip Time**:\n\n.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/rtt.png\n  :align: center\n\nTraffic\n~~~~~~~\n\n+--------------------+--------------------------------------------------------------------------+\n| **measurement**:   | ``traffic``                                                              |\n+--------------------+--------------------------------------------------------------------------+\n| **type**:          | ``int``                                                                  |\n+--------------------+--------------------------------------------------------------------------+\n| **fields**:        | ``rx_bytes``, ``tx_bytes``                                               |\n+--------------------+--------------------------------------------------------------------------+\n| **tags**:          | .. code-block:: python                                                   |\n|                    |                                                                          |\n|                    |     {                                                                    |\n|                    |       'organization_id': '<organization-id-of-the-related-device>',      |\n|                    |       'ifname': '<interface-name>',                                      |\n|                    |       # optional                                                         |\n|                    |       'location_id': '<location-id-of-the-related-device-if-present>',   |\n|                    |       'floorplan_id': '<floorplan-id-of-the-related-device-if-present>', |\n|                    |     }                                                                    |\n+--------------------+--------------------------------------------------------------------------+\n| **configuration**: | ``traffic``                                                              |\n+--------------------+--------------------------------------------------------------------------+\n| **charts**:        | ``traffic``                                                              |\n+--------------------+--------------------------------------------------------------------------+\n\n.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/traffic.png\n  :align: center\n\nWiFi Clients\n~~~~~~~~~~~~\n\n+--------------------+--------------------------------------------------------------------------+\n| **measurement**:   | ``wifi_clients``                                                         |\n+--------------------+--------------------------------------------------------------------------+\n| **type**:          | ``int``                                                                  |\n+--------------------+--------------------------------------------------------------------------+\n| **fields**:        | ``clients``                                                              |\n+--------------------+--------------------------------------------------------------------------+\n| **tags**:          | .. code-block:: python                                                   |\n|                    |                                                                          |\n|                    |     {                                                                    |\n|                    |       'organization_id': '<organization-id-of-the-related-device>',      |\n|                    |       'ifname': '<interface-name>',                                      |\n|                    |       # optional                                                         |\n|                    |       'location_id': '<location-id-of-the-related-device-if-present>',   |\n|                    |       'floorplan_id': '<floorplan-id-of-the-related-device-if-present>', |\n|                    |     }                                                                    |\n+--------------------+--------------------------------------------------------------------------+\n| **configuration**: | ``clients``                                                              |\n+--------------------+--------------------------------------------------------------------------+\n| **charts**:        | ``wifi_clients``                                                         |\n+--------------------+--------------------------------------------------------------------------+\n\n\n.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/wifi-clients.png\n  :align: center\n\nMemory Usage\n~~~~~~~~~~~~\n\n+--------------------+--------------------------------------------------------------------------------------------------------------------------------------+\n| **measurement**:   | ``<memory>``                                                                                                                         |\n+--------------------+--------------------------------------------------------------------------------------------------------------------------------------+\n| **type**:          | ``float``                                                                                                                            |\n+--------------------+--------------------------------------------------------------------------------------------------------------------------------------+\n| **fields**:        | ``percent_used``, ``free_memory``, ``total_memory``, ``buffered_memory``, ``shared_memory``, ``cached_memory``, ``available_memory`` |\n+--------------------+--------------------------------------------------------------------------------------------------------------------------------------+\n| **configuration**: | ``memory``                                                                                                                           |\n+--------------------+--------------------------------------------------------------------------------------------------------------------------------------+\n| **charts**:        | ``memory``                                                                                                                           |\n+--------------------+--------------------------------------------------------------------------------------------------------------------------------------+\n\n.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/memory.png\n  :align: center\n\nCPU Load\n~~~~~~~~\n\n+--------------------+----------------------------------------------------+\n| **measurement**:   | ``load``                                           |\n+--------------------+----------------------------------------------------+\n| **type**:          | ``float``                                          |\n+--------------------+----------------------------------------------------+\n| **fields**:        | ``cpu_usage``, ``load_1``, ``load_5``, ``load_15`` |\n+--------------------+----------------------------------------------------+\n| **configuration**: | ``load``                                           |\n+--------------------+----------------------------------------------------+\n| **charts**:        | ``load``                                           |\n+--------------------+----------------------------------------------------+\n\n.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/cpu-load.png\n  :align: center\n\nDisk Usage\n~~~~~~~~~~\n\n+--------------------+-------------------+\n| **measurement**:   | ``disk``          |\n+--------------------+-------------------+\n| **type**:          | ``float``         |\n+--------------------+-------------------+\n| **fields**:        | ``used_disk``     |\n+--------------------+-------------------+\n| **configuration**: | ``disk``          |\n+--------------------+-------------------+\n| **charts**:        | ``disk``          |\n+--------------------+-------------------+\n\n.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/disk-usage.png\n  :align: center\n\nMobile Signal Strength\n~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------------+-----------------------------------------+\n| **measurement**:   | ``signal_strength``                     |\n+--------------------+-----------------------------------------+\n| **type**:          | ``float``                               |\n+--------------------+-----------------------------------------+\n| **fields**:        | ``signal_strength``, ``signal_power``   |\n+--------------------+-----------------------------------------+\n| **configuration**: | ``signal_strength``                     |\n+--------------------+-----------------------------------------+\n| **charts**:        | ``signal_strength``                     |\n+--------------------+-----------------------------------------+\n\n.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/signal-strength.png\n  :align: center\n\nMobile Signal Quality\n~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------------+-----------------------------------------+\n| **measurement**:   | ``signal_quality``                      |\n+--------------------+-----------------------------------------+\n| **type**:          | ``float``                               |\n+--------------------+-----------------------------------------+\n| **fields**:        | ``signal_quality``, ``signal_quality``  |\n+--------------------+-----------------------------------------+\n| **configuration**: | ``signal_quality``                      |\n+--------------------+-----------------------------------------+\n| **charts**:        | ``signal_quality``                      |\n+--------------------+-----------------------------------------+\n\n.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/signal-quality.png\n  :align: center\n\nMobile Access Technology in use\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------------+-------------------+\n| **measurement**:   | ``access_tech``   |\n+--------------------+-------------------+\n| **type**:          | ``int``           |\n+--------------------+-------------------+\n| **fields**:        | ``access_tech``   |\n+--------------------+-------------------+\n| **configuration**: | ``access_tech``   |\n+--------------------+-------------------+\n| **charts**:        | ``access_tech``   |\n+--------------------+-------------------+\n\n.. figure:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/access-technology.png\n  :align: center\n\nDefault Alerts / Notifications\n------------------------------\n\n+-------------------------------+------------------------------------------------------------------+\n| Notification Type             | Use                                                              |\n+-------------------------------+------------------------------------------------------------------+\n| ``threshold_crossed``         | Fires when a metric crosses the boundary defined in the          |\n|                               | threshold value of the alert settings.                           |\n+-------------------------------+------------------------------------------------------------------+\n| ``threshold_recovery``        | Fires when a metric goes back within the expected range.         |\n+-------------------------------+------------------------------------------------------------------+\n| ``connection_is_working``     | Fires when the connection to a device is working.                |\n+-------------------------------+------------------------------------------------------------------+\n| ``connection_is_not_working`` | Fires when the connection (eg: SSH) to a device stops working    |\n|                               | (eg: credentials are outdated, management IP address is          |\n|                               | outdated, or device is not reachable).                           |\n+-------------------------------+------------------------------------------------------------------+\n\nAvailable Checks\n----------------\n\nPing\n~~~~\n\nThis check returns information on device ``uptime`` and ``RTT (Round trip time)``.\nThe Charts ``uptime``, ``packet loss`` and ``rtt`` are created. The ``fping``\ncommand is used to collect these metrics.\nYou may choose to disable auto creation of this check by setting\n`OPENWISP_MONITORING_AUTO_PING <#OPENWISP_MONITORING_AUTO_PING>`_ to ``False``.\n\nYou can change the default values used for ping checks using\n`OPENWISP_MONITORING_PING_CHECK_CONFIG <#OPENWISP_MONITORING_PING_CHECK_CONFIG>`_ setting.\n\nConfiguration applied\n~~~~~~~~~~~~~~~~~~~~~\n\nThis check ensures that the `openwisp-config agent <https://github.com/openwisp/openwisp-config/>`_\nis running and applying configuration changes in a timely manner.\nYou may choose to disable auto creation of this check by using the\nsetting `OPENWISP_MONITORING_AUTO_DEVICE_CONFIG_CHECK <#OPENWISP_MONITORING_AUTO_DEVICE_CONFIG_CHECK>`_.\n\nThis check runs periodically, but it is also triggered whenever the\nconfiguration status of a device changes, this ensures the check reacts\nquickly to events happening in the network and informs the user promptly\nif there's anything that is not working as intended.\n\nSettings\n--------\n\n``OPENWISP_MONITORING_SHORT_RETENTION_POLICY``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-------------+\n| **type**:    | ``str``     |\n+--------------+-------------+\n| **default**: | ``24h0m0s`` |\n+--------------+-------------+\n\nThe default retention policy used to store raw device data.\n\nThis data is only used to assess the recent status of devices, keeping\nit for a long time would not add much benefit and would cost a lot more\nin terms of disk space.\n\n``OPENWISP_MONITORING_AUTO_PING``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-------------+\n| **type**:    | ``bool``    |\n+--------------+-------------+\n| **default**: | ``True``    |\n+--------------+-------------+\n\nWhether ping checks are created automatically for devices.\n\n``OPENWISP_MONITORING_PING_CHECK_CONFIG``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-------------+\n| **type**:    | ``dict``    |\n+--------------+-------------+\n| **default**: | ``{}``      |\n+--------------+-------------+\n\nThis setting allows to override the default ping check configuration defined in\n``openwisp_monitoring.check.classes.ping.DEFAULT_PING_CHECK_CONFIG``.\n\nFor example, if you want to change only the **timeout** of\n``ping`` you can use:\n\n.. code-block:: python\n\n    OPENWISP_MONITORING_PING_CHECK_CONFIG = {\n        'timeout': {\n            'default': 1000,\n        },\n    }\n\nIf you are overriding the default value for any parameter\nbeyond the maximum or minimum value defined in\n``openwisp_monitoring.check.classes.ping.DEFAULT_PING_CHECK_CONFIG``,\nyou will also need to override the ``maximum`` or ``minimum`` fields\nas following:\n\n.. code-block:: python\n\n    OPENWISP_MONITORING_PING_CHECK_CONFIG = {\n        'timeout': {\n            'default': 2000,\n            'minimum': 1500,\n            'maximum': 2500,\n        },\n    }\n\n**Note:** Above ``maximum`` and ``minimum`` values are only used for\nvalidating custom parameters of a ``Check`` object.\n\n``OPENWISP_MONITORING_AUTO_DEVICE_CONFIG_CHECK``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-------------+\n| **type**:    | ``bool``    |\n+--------------+-------------+\n| **default**: | ``True``    |\n+--------------+-------------+\n\nThis setting allows you to choose whether `config_applied <#configuration-applied>`_ checks should be\ncreated automatically for newly registered devices. It's enabled by default.\n\n``OPENWISP_MONITORING_CONFIG_CHECK_INTERVAL``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-------------+\n| **type**:    | ``int``     |\n+--------------+-------------+\n| **default**: | ``5``       |\n+--------------+-------------+\n\nThis setting allows you to configure the config check interval used by\n`config_applied <#configuration-applied>`_. By default it is set to 5 minutes.\n\n``OPENWISP_MONITORING_AUTO_CHARTS``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-----------------------------------------------------------------+\n| **type**:    | ``list``                                                        |\n+--------------+-----------------------------------------------------------------+\n| **default**: | ``('traffic', 'wifi_clients', 'uptime', 'packet_loss', 'rtt')`` |\n+--------------+-----------------------------------------------------------------+\n\nAutomatically created charts.\n\n``OPENWISP_MONITORING_CRITICAL_DEVICE_METRICS``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-----------------------------------------------------------------+\n| **type**:    | ``list`` of ``dict`` objects                                    |\n+--------------+-----------------------------------------------------------------+\n| **default**: | ``[{'key': 'ping', 'field_name': 'reachable'}]``                |\n+--------------+-----------------------------------------------------------------+\n\nDevice metrics that are considered critical:\n\nwhen a value crosses the boundary defined in the \"threshold value\" field\nof the alert settings related to one of these metric types, the health status\nof the device related to the metric moves into ``CRITICAL``.\n\nBy default, if devices are not reachable by pings they are flagged as ``CRITICAL``.\n\n``OPENWISP_MONITORING_HEALTH_STATUS_LABELS``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+--------------------------------------------------------------------------------------+\n| **type**:    | ``dict``                                                                             |\n+--------------+--------------------------------------------------------------------------------------+\n| **default**: | ``{'unknown': 'unknown', 'ok': 'ok', 'problem': 'problem', 'critical': 'critical'}`` |\n+--------------+--------------------------------------------------------------------------------------+\n\nThis setting allows to change the health status labels, for example, if we\nwant to use ``online`` instead of ``ok`` and ``offline`` instead of ``critical``,\nyou can use the following configuration:\n\n.. code-block:: python\n\n    OPENWISP_MONITORING_HEALTH_STATUS_LABELS = {\n        'ok': 'online',\n        'problem': 'problem',\n        'critical': 'offline'\n    }\n\n``OPENWISP_MONITORING_MANAGEMENT_IP_ONLY``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-------------+\n| **type**:    | ``bool``    |\n+--------------+-------------+\n| **default**: | ``True``    |\n+--------------+-------------+\n\nBy default, only the management IP will be used to perform active checks to\nthe devices.\n\nIf the devices are connecting to your OpenWISP instance using a shared layer2\nnetwork, hence the OpenWSP server can reach the devices using the ``last_ip``\nfield, you can set this to ``False``.\n\n``OPENWISP_MONITORING_DEVICE_RECOVERY_DETECTION``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-------------+\n| **type**:    | ``bool``    |\n+--------------+-------------+\n| **default**: | ``True``    |\n+--------------+-------------+\n\nWhen device recovery detection is enabled, recoveries are discovered as soon as\na device contacts the openwisp system again (eg: to get the configuration checksum\nor to send monitoring metrics).\n\nThis feature is enabled by default.\n\nIf you use OpenVPN as the management VPN, you may want to check out a similar\nintegration built in **openwisp-network-topology**: when the status of an OpenVPN link\nchanges (detected by monitoring the status information of OpenVPN), the\nnetwork topology module will trigger the monitoring checks.\nFor more information see:\n`Network Topology Device Integration <https://github.com/openwisp/openwisp-network-topology#integration-with-openwisp-controller-and-openwisp-monitoring>`_\n\n``OPENWISP_MONITORING_MAC_VENDOR_DETECTION``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-------------+\n| **type**:    | ``bool``    |\n+--------------+-------------+\n| **default**: | ``True``    |\n+--------------+-------------+\n\nIndicates whether mac addresses will be complemented with hardware vendor\ninformation by performing lookups on the OUI\n(Organization Unique Identifier) table.\n\nThis feature is enabled by default.\n\n``OPENWISP_MONITORING_WRITE_RETRY_OPTIONS``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-----------+\n| **type**:    | ``dict``  |\n+--------------+-----------+\n| **default**: | see below |\n+--------------+-----------+\n\n.. code-block:: python\n\n    # default value of OPENWISP_MONITORING_RETRY_OPTIONS:\n\n    dict(\n        max_retries=None,\n        retry_backoff=True,\n        retry_backoff_max=600,\n        retry_jitter=True,\n    )\n\nRetry settings for recoverable failures during metric writes.\n\nBy default if a metric write fails (eg: due to excessive load on timeseries database at that moment)\nthen the operation will be retried indefinitely with an exponential random backoff and a maximum delay of 10 minutes.\n\nThis feature makes the monitoring system resilient to temporary outages and helps to prevent data loss.\n\nFor more information regarding these settings, consult the `celery documentation\nregarding automatic retries for known errors\n<https://docs.celeryproject.org/en/stable/userguide/tasks.html#automatic-retry-for-known-exceptions>`_.\n\n``OPENWISP_MONITORING_TIMESERIES_RETRY_OPTIONS``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-----------+\n| **type**:    | ``dict``  |\n+--------------+-----------+\n| **default**: | see below |\n+--------------+-----------+\n\n.. code-block:: python\n\n    # default value of OPENWISP_MONITORING_RETRY_OPTIONS:\n\n    dict(\n        max_retries=6,\n        delay=2\n    )\n\nOn busy systems, communication with the timeseries DB can occasionally fail.\nThe timeseries DB backend will retry on any exception according to these settings.\nThe delay kicks in only after the third consecutive attempt.\n\nThis setting shall not be confused with ``OPENWISP_MONITORING_WRITE_RETRY_OPTIONS``,\nwhich is used to configure the infinite retrying of the celery task which writes\nmetric data to the timeseries DB, while ``OPENWISP_MONITORING_TIMESERIES_RETRY_OPTIONS``\ndeals with any other read/write operation on the timeseries DB which may fail.\n\nHowever these retries are not handled by celery but are simple python loops,\nwhich will eventually give up if a problem persists.\n\n``OPENWISP_MONITORING_TIMESERIES_RETRY_DELAY``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-------------+\n| **type**:    |   ``int``   |\n+--------------+-------------+\n| **default**: |    ``2``    |\n+--------------+-------------+\n\nThis settings allow you to configure the retry delay time (in seconds) after 3 failed attempt in timeseries database.\n\nThis retry setting is used in retry mechanism to make the requests to the timeseries database resilient.\n\nThis setting is independent of celery retry settings.\n\n``OPENWISP_MONITORING_DASHBOARD_MAP``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-------------+\n| **type**:    | ``bool``    |\n+--------------+-------------+\n| **default**: | ``True``    |\n+--------------+-------------+\n\nWhether the geographic map in the dashboard is enabled or not.\nThis feature provides a geographic map which shows the locations\nwhich have devices installed in and provides a visual representation\nof the monitoring status of the devices, this allows to get\nan overview of the network at glance.\n\nThis feature is enabled by default and depends on the setting\n``OPENWISP_ADMIN_DASHBOARD_ENABLED`` from\n`openwisp-utils <https://github.com/openwisp/openwisp-utils>`__\nbeing set to ``True`` (which is the default).\n\nYou can turn this off if you do not use the geographic features\nof OpenWISP.\n\n``OPENWISP_MONITORING_METRICS``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-------------+\n| **type**:    | ``dict``    |\n+--------------+-------------+\n| **default**: | ``{}``      |\n+--------------+-------------+\n\nThis setting allows to define additional metric configuration or to override\nthe default metric configuration defined in\n``openwisp_monitoring.monitoring.configuration.DEFAULT_METRICS``.\n\nFor example, if you want to change only the **field_name** of\n``clients`` metric to ``wifi_clients`` you can use:\n\n.. code-block:: python\n\n    from django.utils.translation import gettext_lazy as _\n\n    OPENWISP_MONITORING_METRICS = {\n        'clients': {\n            'label': _('WiFi clients'),\n            'field_name': 'wifi_clients',\n        },\n    }\n\nFor example, if you want to change only the default alert settings of\n``memory`` metric you can use:\n\n.. code-block:: python\n\n    OPENWISP_MONITORING_METRICS = {\n        'memory': {\n            'alert_settings': {'threshold': 75, 'tolerance': 10}\n        },\n    }\n\nFor example, if you want to change only the notification of\n``config_applied`` metric you can use:\n\n.. code-block:: python\n\n    from django.utils.translation import gettext_lazy as _\n\n    OPENWISP_MONITORING_METRICS = {\n        'config_applied': {\n            'notification': {\n                'problem': {\n                    'verbose_name': 'Configuration PROBLEM',\n                    'verb': _('has not been applied'),\n                    'email_subject': _(\n                        '[{site.name}] PROBLEM: {notification.target} configuration '\n                        'status issue'\n                    ),\n                    'message': _(\n                        'The configuration for device [{notification.target}]'\n                        '({notification.target_link}) {notification.verb} in a timely manner.'\n                    ),\n                },\n                'recovery': {\n                    'verbose_name': 'Configuration RECOVERY',\n                    'verb': _('configuration has been applied again'),\n                    'email_subject': _(\n                        '[{site.name}] RECOVERY: {notification.target} {notification.verb} '\n                        'successfully'\n                    ),\n                    'message': _(\n                        'The device [{notification.target}]({notification.target_link}) '\n                        '{notification.verb} successfully.'\n                    ),\n                },\n            },\n        },\n    }\n\nOr if you want to define a new metric configuration, which you can then\ncall in your custom code (eg: a custom check class), you can do so as follows:\n\n.. code-block:: python\n\n    from django.utils.translation import gettext_lazy as _\n\n    OPENWISP_MONITORING_METRICS = {\n        'top_fields_mean': {\n            'name': 'Top Fields Mean',\n            'key': '{key}',\n            'field_name': '{field_name}',\n            'label': '_(Top fields mean)',\n            'related_fields': ['field1', 'field2', 'field3'],\n        },\n    }\n\n``OPENWISP_MONITORING_CHARTS``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-------------+\n| **type**:    | ``dict``    |\n+--------------+-------------+\n| **default**: | ``{}``      |\n+--------------+-------------+\n\nThis setting allows to define additional charts or to override\nthe default chart configuration defined in\n``openwisp_monitoring.monitoring.configuration.DEFAULT_CHARTS``.\n\nFor example, if you want to change the traffic chart to show\nMB (megabytes) instead of GB (Gigabytes) you can use:\n\n.. code-block:: python\n\n    OPENWISP_MONITORING_CHARTS = {\n        'traffic': {\n            'unit': ' MB',\n            'description': (\n                'Network traffic, download and upload, measured on '\n                'the interface \"{metric.key}\", measured in MB.'\n            ),\n            'query': {\n                'influxdb': (\n                    \"SELECT SUM(tx_bytes) / 1000000 AS upload, \"\n                    \"SUM(rx_bytes) / 1000000 AS download FROM {key} \"\n                    \"WHERE time >= '{time}' AND content_type = '{content_type}' \"\n                    \"AND object_id = '{object_id}' GROUP BY time(1d)\"\n                )\n            },\n        }\n    }\n\nOr if you want to define a new chart configuration, which you can then\ncall in your custom code (eg: a custom check class), you can do so as follows:\n\n.. code-block:: python\n\n    from django.utils.translation import gettext_lazy as _\n\n    OPENWISP_MONITORING_CHARTS = {\n        'ram': {\n            'type': 'line',\n            'title': 'RAM usage',\n            'description': 'RAM usage',\n            'unit': 'bytes',\n            'order': 100,\n            'query': {\n                'influxdb': (\n                    \"SELECT MEAN(total) AS total, MEAN(free) AS free, \"\n                    \"MEAN(buffered) AS buffered FROM {key} WHERE time >= '{time}' AND \"\n                    \"content_type = '{content_type}' AND object_id = '{object_id}' \"\n                    \"GROUP BY time(1d)\"\n                )\n            },\n        }\n    }\n\nIn case you just want to change the colors used in a chart here's how to do it:\n\n.. code-block:: python\n\n    OPENWISP_MONITORING_CHARTS = {\n        'traffic': {\n            'colors': ['#000000', '#cccccc']\n        }\n    }\n\n``OPENWISP_MONITORING_AUTO_CLEAR_MANAGEMENT_IP``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-------------+\n| **type**:    | ``bool``    |\n+--------------+-------------+\n| **default**: | ``True``    |\n+--------------+-------------+\n\nThis setting allows you to automatically clear management_ip of a device\nwhen it goes offline. It is enabled by default.\n\n``OPENWISP_MONITORING_API_URLCONF``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-------------+\n| **type**:    | ``string``  |\n+--------------+-------------+\n| **default**: | ``None``    |\n+--------------+-------------+\n\nChanges the urlconf option of django urls to point the monitoring API\nurls to another installed module, example, ``myapp.urls``.\n(Useful when you have a seperate API instance.)\n\n``OPENWISP_MONITORING_API_BASEURL``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-------------+\n| **type**:    | ``string``  |\n+--------------+-------------+\n| **default**: | ``None``    |\n+--------------+-------------+\n\nIf you have a seperate server for API of openwisp-monitoring on a different\ndomain, you can use this option to change the base of the url, this will\nenable you to point all the API urls to your openwisp-monitoring API server's\ndomain, example: ``https://mymonitoring.myapp.com``.\n\nRegistering / Unregistering Metric Configuration\n------------------------------------------------\n\n**OpenWISP Monitoring** provides registering and unregistering metric configuration through utility functions\n``openwisp_monitoring.monitoring.configuration.register_metric`` and ``openwisp_monitoring.monitoring.configuration.unregister_metric``.\nUsing these functions you can register or unregister metric configurations from anywhere in your code.\n\n``register_metric``\n~~~~~~~~~~~~~~~~~~~\n\nThis function is used to register a new metric configuration from anywhere in your code.\n\n+--------------------------+------------------------------------------------------+\n|      **Parameter**       |                   **Description**                    |\n+--------------------------+------------------------------------------------------+\n|     **metric_name**:     | A ``str`` defining name of the metric configuration. |\n+--------------------------+------------------------------------------------------+\n|**metric_configuration**: | A ``dict`` defining configuration of the metric.     |\n+--------------------------+------------------------------------------------------+\n\nAn example usage has been shown below.\n\n.. code-block:: python\n\n    from django.utils.translation import gettext_lazy as _\n    from openwisp_monitoring.monitoring.configuration import register_metric\n\n    # Define configuration of your metric\n    metric_config = {\n        'label': _('Ping'),\n        'name': 'Ping',\n        'key': 'ping',\n        'field_name': 'reachable',\n        'related_fields': ['loss', 'rtt_min', 'rtt_max', 'rtt_avg'],\n        'charts': {\n            'uptime': {\n                'type': 'bar',\n                'title': _('Uptime'),\n                'description': _(\n                    'A value of 100% means reachable, 0% means unreachable, values in '\n                    'between 0% and 100% indicate the average reachability in the '\n                    'period observed. Obtained with the fping linux program.'\n                ),\n                'summary_labels': [_('Average uptime')],\n                'unit': '%',\n                'order': 200,\n                'colorscale': {\n                    'max': 100,\n                    'min': 0,\n                    'label': _('Reachable'),\n                    'scale': [\n                        [[0, '#c13000'],\n                        [0.1,'cb7222'],\n                        [0.5,'#deed0e'],\n                        [0.9, '#7db201'],\n                        [1, '#498b26']],\n                    ],\n                    'map': [\n                       [100, '#498b26', _('Reachable')],\n                       [90, '#7db201', _('Mostly Reachable')],\n                       [50, '#deed0e', _('Partly Reachable')],\n                       [10, '#cb7222', _('Mostly Unreachable')],\n                       [None, '#c13000', _('Unreachable')],\n                    ],\n                    'fixed_value': 100,\n                },\n                'query': chart_query['uptime'],\n            },\n            'packet_loss': {\n                'type': 'bar',\n                'title': _('Packet loss'),\n                'description': _(\n                    'Indicates the percentage of lost packets observed in ICMP probes. '\n                    'Obtained with the fping linux program.'\n                ),\n                'summary_labels': [_('Average packet loss')],\n                'unit': '%',\n                'colors': '#d62728',\n                'order': 210,\n                'query': chart_query['packet_loss'],\n            },\n            'rtt': {\n                'type': 'scatter',\n                'title': _('Round Trip Time'),\n                'description': _(\n                    'Round trip time observed in ICMP probes, measuered in milliseconds.'\n                ),\n                'summary_labels': [\n                    _('Average RTT'),\n                    _('Average Max RTT'),\n                    _('Average Min RTT'),\n                ],\n                'unit': _(' ms'),\n                'order': 220,\n                'query': chart_query['rtt'],\n            },\n        },\n        'alert_settings': {'operator': '<', 'threshold': 1, 'tolerance': 0},\n        'notification': {\n            'problem': {\n                'verbose_name': 'Ping PROBLEM',\n                'verb': 'cannot be reached anymore',\n                'level': 'warning',\n                'email_subject': _(\n                    '[{site.name}] {notification.target} is not reachable'\n                ),\n                'message': _(\n                    'The device [{notification.target}] {notification.verb} anymore by our ping '\n                    'messages.'\n                ),\n            },\n            'recovery': {\n                'verbose_name': 'Ping RECOVERY',\n                'verb': 'has become reachable',\n                'level': 'info',\n                'email_subject': _(\n                    '[{site.name}] {notification.target} is reachable again'\n                ),\n                'message': _(\n                    'The device [{notification.target}] {notification.verb} again by our ping '\n                    'messages.'\n                ),\n            },\n        },\n    }\n\n    # Register your custom metric configuration\n    register_metric('ping', metric_config)\n\nThe above example will register one metric configuration (named ``ping``), three chart\nconfigurations (named ``rtt``, ``packet_loss``, ``uptime``) as defined in the **charts** key,\ntwo notification types (named ``ping_recovery``, ``ping_problem``) as defined in **notification** key.\n\nThe ``AlertSettings`` of ``ping`` metric will by default use ``threshold`` and ``tolerance``\ndefined in the ``alert_settings`` key.\nYou can always override them and define your own custom values via the *admin*.\n\n**Note**: It will raise ``ImproperlyConfigured`` exception if a metric configuration\nis already registered with same name (not to be confused with verbose_name).\n\nIf you don't need to register a new metric but need to change a specific key of an\nexisting metric configuration, you can use `OPENWISP_MONITORING_METRICS <#openwisp_monitoring_metrics>`_.\n\n``unregister_metric``\n~~~~~~~~~~~~~~~~~~~~~\n\nThis function is used to unregister a metric configuration from anywhere in your code.\n\n+------------------+------------------------------------------------------+\n|  **Parameter**   |                   **Description**                    |\n+------------------+------------------------------------------------------+\n| **metric_name**: | A ``str`` defining name of the metric configuration. |\n+------------------+------------------------------------------------------+\n\nAn example usage is shown below.\n\n.. code-block:: python\n\n    from openwisp_monitoring.monitoring.configuration import unregister_metric\n\n    # Unregister previously registered metric configuration\n    unregister_metric('metric_name')\n\n**Note**: It will raise ``ImproperlyConfigured`` exception if the concerned metric\nconfiguration is not registered.\n\nRegistering / Unregistering Chart Configuration\n-----------------------------------------------\n\n**OpenWISP Monitoring** provides registering and unregistering chart configuration through utility functions\n``openwisp_monitoring.monitoring.configuration.register_chart`` and ``openwisp_monitoring.monitoring.configuration.unregister_chart``.\nUsing these functions you can register or unregister chart configurations from anywhere in your code.\n\n``register_chart``\n~~~~~~~~~~~~~~~~~~\n\nThis function is used to register a new chart configuration from anywhere in your code.\n\n+--------------------------+-----------------------------------------------------+\n|      **Parameter**       |                   **Description**                   |\n+--------------------------+-----------------------------------------------------+\n|      **chart_name**:     | A ``str`` defining name of the chart configuration. |\n+--------------------------+-----------------------------------------------------+\n| **chart_configuration**: | A ``dict`` defining configuration of the chart.     |\n+--------------------------+-----------------------------------------------------+\n\nAn example usage has been shown below.\n\n.. code-block:: python\n\n    from openwisp_monitoring.monitoring.configuration import register_chart\n\n    # Define configuration of your chart\n    chart_config = {\n        'type': 'histogram',\n        'title': 'Histogram',\n        'description': 'Histogram',\n        'top_fields': 2,\n        'order': 999,\n        'query': {\n            'influxdb': (\n                \"SELECT {fields|SUM|/ 1} FROM {key} \"\n                \"WHERE time >= '{time}' AND content_type = \"\n                \"'{content_type}' AND object_id = '{object_id}'\"\n            )\n        },\n    }\n\n    # Register your custom chart configuration\n    register_chart('chart_name', chart_config)\n\n**Note**: It will raise ``ImproperlyConfigured`` exception if a chart configuration\nis already registered with same name (not to be confused with verbose_name).\n\nIf you don't need to register a new chart but need to change a specific key of an\nexisting chart configuration, you can use `OPENWISP_MONITORING_CHARTS <#openwisp_monitoring_charts>`_.\n\n``unregister_chart``\n~~~~~~~~~~~~~~~~~~~~\n\nThis function is used to unregister a chart configuration from anywhere in your code.\n\n+------------------+-----------------------------------------------------+\n|  **Parameter**   |                   **Description**                   |\n+------------------+-----------------------------------------------------+\n|  **chart_name**: | A ``str`` defining name of the chart configuration. |\n+------------------+-----------------------------------------------------+\n\nAn example usage is shown below.\n\n.. code-block:: python\n\n    from openwisp_monitoring.monitoring.configuration import unregister_chart\n\n    # Unregister previously registered chart configuration\n    unregister_chart('chart_name')\n\n**Note**: It will raise ``ImproperlyConfigured`` exception if the concerned chart\nconfiguration is not registered.\n\nRegistering new notification types\n----------------------------------\n\nYou can define your own notification types using ``register_notification_type`` function from OpenWISP\nNotifications. For more information, see the relevant `openwisp-notifications section about registering notification types\n<https://github.com/openwisp/openwisp-notifications#registering--unregistering-notification-types>`_.\n\nOnce a new notification type is registered, you have to use the `\"notify\" signal provided in\nopenwisp-notifications <https://github.com/openwisp/openwisp-notifications#sending-notifications>`_\nto send notifications for this type.\n\nExceptions\n----------\n\n``TimeseriesWriteException``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n**Path**: ``openwisp_monitoring.db.exceptions.TimeseriesWriteException``\n\nIf there is any failure due while writing data in timeseries database, this exception shall\nbe raised with a helpful error message explaining the cause of the failure.\nThis exception will normally be caught and the failed write task will be retried in the background\nso that there is no loss of data if failures occur due to overload of Timeseries server.\nYou can read more about this retry mechanism at `OPENWISP_MONITORING_WRITE_RETRY_OPTIONS <#openwisp-monitoring-write-retry-options>`_.\n\n``InvalidMetricConfigException``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n**Path**: ``openwisp_monitoring.monitoring.exceptions.InvalidMetricConfigException``\n\nThis exception shall be raised if the metric configuration is broken.\n\n``InvalidChartConfigException``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n**Path**: ``openwisp_monitoring.monitoring.exceptions.InvalidChartConfigException``\n\nThis exception shall be raised if the chart configuration is broken.\n\nRest API\n--------\n\nLive documentation\n~~~~~~~~~~~~~~~~~~\n\n.. image:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/api-doc.png\n\nA general live API documentation (following the OpenAPI specification) at ``/api/v1/docs/``.\n\nBrowsable web interface\n~~~~~~~~~~~~~~~~~~~~~~~\n\n.. image:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/api-ui-1.png\n.. image:: https://github.com/openwisp/openwisp-monitoring/raw/docs/docs/api-ui-2.png\n\nAdditionally, opening any of the endpoints `listed below <#list-of-endpoints>`_\ndirectly in the browser will show the `browsable API interface of Django-REST-Framework\n<https://www.django-rest-framework.org/topics/browsable-api/>`_,\nwhich makes it even easier to find out the details of each endpoint.\n\nList of endpoints\n~~~~~~~~~~~~~~~~~\n\nSince the detailed explanation is contained in the `Live documentation <#live-documentation>`_\nand in the `Browsable web page <#browsable-web-interface>`_ of each point,\nhere we'll provide just a list of the available endpoints,\nfor further information please open the URL of the endpoint in your browser.\n\nRetrieve device charts and device status data\n#############################################\n\n.. code-block:: text\n\n    GET /v1/monitoring/device/{pk}/?key={key}&status=true\n\nThe format used for Device Status is inspired by `NetJSON DeviceMonitoring <http://netjson.org/docs/what.html#devicemonitoring>`_.\n\n**Note**: If the request is made without ``?status=true`` then only device charts\ndata would be returned.\n\nCollect device metrics and status\n#################################\n\n.. code-block:: text\n\n    POST /v1/monitoring/device/{pk}/?key={key}&time={time}\n\nIf data is latest then an additional parameter current can also be passed. For e.g.:\n\n.. code-block:: text\n\n    POST /v1/monitoring/device/{pk}/?key={key}&time={time}&current=true\n\nThe format used for Device Status is inspired by `NetJSON DeviceMonitoring <http://netjson.org/docs/what.html#devicemonitoring>`_.\n\n**Note**: Device data will be saved with in timeseries database with the specified ``time``,\nthis should be in the format ``%d-%m-%Y_%H:%M:%S.%f``, otherwise 400 Bad Response will be returned.\n\nIf the request is made without passing the ``time`` argument, the server local time will be used.\n\nThe ``time`` parameter was added to support `resilient collection and sending of data by the OpenWISP Monitoring Agent <https://github.com/openwisp/openwrt-openwisp-monitoring#collecting-vs-sending>`_.\n\nSignals\n-------\n\n``device_metrics_received``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n**Path**: ``openwisp_monitoring.device.signals.device_metrics_received``\n\n**Arguments**:\n\n- ``instance``: instance of ``Device`` whose metrics have been received\n- ``request``: the HTTP request object\n- ``time``: time with which metrics will be saved. If none, then server time will be used\n- ``current``: whether the data has just been collected or was collected previously and sent now due to network connectivity issues\n\nThis signal is emitted when device metrics are received to the ``DeviceMetric``\nview (only when using HTTP POST).\n\nThe signal is emitted just before a successful response is returned,\nit is not sent if the response was not successful.\n\n``health_status_changed``\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n**Path**: ``openwisp_monitoring.device.signals.health_status_changed``\n\n**Arguments**:\n\n- ``instance``: instance of ``DeviceMonitoring`` whose status has been changed\n- ``status``: the status by which DeviceMonitoring's existing status has been updated with\n\nThis signal is emitted only if the health status of DeviceMonitoring object gets updated.\n\n``threshold_crossed``\n~~~~~~~~~~~~~~~~~~~~~\n\n**Path**: ``openwisp_monitoring.monitoring.signals.threshold_crossed``\n\n**Arguments**:\n\n- ``metric``: ``Metric`` object whose threshold defined in related alert settings was crossed\n- ``alert_settings``: ``AlertSettings`` related to the ``Metric``\n- ``target``: related ``Device`` object\n- ``first_time``: it will be set to true when the metric is written for the first time. It shall be set to false afterwards.\n- ``tolerance_crossed``: it will be set to true if the metric has crossed the threshold for tolerance configured in alert settings.\n  Otherwise, it will be set to false.\n\n``first_time`` parameter can be used to avoid initiating unneeded actions.\nFor example, sending recovery notifications.\n\nThis signal is emitted when the threshold value of a ``Metric`` defined in\nalert settings is crossed.\n\n``pre_metric_write``\n~~~~~~~~~~~~~~~~~~~~\n\n**Path**: ``openwisp_monitoring.monitoring.signals.pre_metric_write``\n\n**Arguments**:\n\n- ``metric``: ``Metric`` object whose data shall be stored in timeseries database\n- ``values``: metric data that shall be stored in the timeseries database\n- ``time``: time with which metrics will be saved\n- ``current``: whether the data has just been collected or was collected previously and sent now due to network connectivity issues\n\nThis signal is emitted for every metric before the write operation is sent to\nthe timeseries database.\n\n``post_metric_write``\n~~~~~~~~~~~~~~~~~~~~~\n\n**Path**: ``openwisp_monitoring.monitoring.signals.post_metric_write``\n\n**Arguments**:\n\n- ``metric``: ``Metric`` object whose data is being stored in timeseries database\n- ``values``: metric data that is being stored in the timeseries database\n- ``time``: time with which metrics will be saved\n- ``current``: whether the data has just been collected or was collected previously and sent now due to network connectivity issues\n\nThis signal is emitted for every metric after the write operation is successfully\nexecuted in the background.\n\nManagement commands\n-------------------\n\n``run_checks``\n~~~~~~~~~~~~~~\n\nThis command will execute all the `available checks <#available-checks>`_ for all the devices.\nBy default checks are run periodically by *celery beat*. You can learn more\nabout this in `Setup <#setup-integrate-in-an-existing-django-project>`_.\n\nExample usage:\n\n.. code-block:: shell\n\n    cd tests/\n    ./manage.py run_checks\n\n``migrate_timeseries``\n~~~~~~~~~~~~~~~~~~~~~~\n\nThis command triggers asynchronous migration of the time-series database.\n\nExample usage:\n\n.. code-block:: shell\n\n    cd tests/\n    ./manage.py migrate_timeseries\n\nMonitoring scripts\n------------------\n\nMonitoring scripts are now deprecated in favour of `monitoring packages <https://github.com/openwisp/openwrt-openwisp-monitoring#openwrt-openwisp-monitoring>`_.\nFollow the migration guide in `Migrating from monitoring scripts to monitoring packages <#migrating-from-monitoring-scripts-to-monitoring-packages>`_\nsection of this documentation.\n\nMigrating from monitoring scripts to monitoring packages\n--------------------------------------------------------\n\nThis section is intended for existing users of *openwisp-monitoring*.\nThe older version of *openwisp-monitoring* used *monitoring scripts* that\nare now deprecated in favour of `monitoring packages <https://github.com/openwisp/openwrt-openwisp-monitoring#openwrt-openwisp-monitoring>`_.\n\nIf you already had a *monitoring template* created on your installation,\nthen the migrations of *openwisp-monitoring* will update that template\nby making the following changes:\n\n- The file name of all scripts will be appended with ``legacy-`` keyword\n  in order to differentiate them from the scripts bundled with the new packages.\n- The ``/usr/sbin/legacy-openwisp-monitoring`` (previously ``/usr/sbin/openwisp-monitoring``)\n  script will be updated to exit if `openwisp-monitoring package <https://github.com/openwisp/openwrt-openwisp-monitoring#openwrt-openwisp-monitoring>`_\n  is installed on the device.\n\nInstall the `monitoring packages <https://github.com/openwisp/openwrt-openwisp-monitoring#openwrt-openwisp-monitoring>`_\nas mentioned in the `Install monitoring packages on device <#install-monitoring-packages-on-the-device>`_\nsection of this documentation.\n\nAfter the proper configuration of the `openwisp-monitoring package <https://github.com/openwisp/openwrt-openwisp-monitoring#openwrt-openwisp-monitoring>`_\non your device, you can remove the monitoring template from your devices.\n\nWe suggest removing the monitoring template from the devices one at a time instead\nof deleting the template. This ensures the correctness of\n*openwisp monitoring package* configuration and you'll not miss out on\nany monitoring data.\n\n**Note:** If you have made changes to the default monitoring template created\nby *openwisp-monitoring* or you are using custom monitoring templates, then you should\nremove such templates from the device before installing the\n`monitoring packages <https://github.com/openwisp/openwrt-openwisp-monitoring#openwrt-openwisp-monitoring>`_.\n\nExtending openwisp-monitoring\n-----------------------------\n\nOne of the core values of the OpenWISP project is `Software Reusability <http://openwisp.io/docs/general/values.html#software-reusability-means-long-term-sustainability>`_,\nfor this reason *openwisp-monitoring* provides a set of base classes\nwhich can be imported, extended and reused to create derivative apps.\n\nIn order to implement your custom version of *openwisp-monitoring*,\nyou need to perform the steps described in the rest of this section.\n\nWhen in doubt, the code in the `test project <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/>`_\nand the ``sample apps`` namely `sample_check <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_check/>`_,\n`sample_monitoring <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_monitoring/>`_, `sample_device_monitoring <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_device_monitoring/>`_\nwill guide you in the correct direction:\njust replicate and adapt that code to get a basic derivative of\n*openwisp-monitoring* working.\n\n**Premise**: if you plan on using a customized version of this module,\nwe suggest to start with it since the beginning, because migrating your data\nfrom the default module to your extended version may be time consuming.\n\n1. Initialize your custom module\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe first thing you need to do in order to extend any *openwisp-monitoring* app is create\na new django app which will contain your custom version of that *openwisp-monitoring* app.\n\nA django app is nothing more than a\n`python package <https://docs.python.org/3/tutorial/modules.html#packages>`_\n(a directory of python scripts), in the following examples we'll call these django apps as\n``mycheck``, ``mydevicemonitoring``, ``mymonitoring`` but you can name it how you want::\n\n    django-admin startapp mycheck\n    django-admin startapp mydevicemonitoring\n    django-admin startapp mymonitoring\n\nKeep in mind that the command mentioned above must be called from a directory\nwhich is available in your `PYTHON_PATH <https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH>`_\nso that you can then import the result into your project.\n\nNow you need to add ``mycheck`` to ``INSTALLED_APPS`` in your ``settings.py``,\nensuring also that ``openwisp_monitoring.check`` has been removed:\n\n.. code-block:: python\n\n    INSTALLED_APPS = [\n        # ... other apps ...\n        # 'openwisp_monitoring.check',        <-- comment out or delete this line\n        # 'openwisp_monitoring.device',       <-- comment out or delete this line\n        # 'openwisp_monitoring.monitoring'    <-- comment out or delete this line\n        'mycheck',\n        'mydevicemonitoring',\n        'mymonitoring',\n        'nested_admin',\n    ]\n\nFor more information about how to work with django projects and django apps,\nplease refer to the `\"Tutorial: Writing your first Django app\" in the django docunmentation <https://docs.djangoproject.com/en/dev/intro/tutorial01/>`_.\n\n2. Install ``openwisp-monitoring``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nInstall (and add to the requirement of your project) *openwisp-monitoring*::\n\n    pip install --U https://github.com/openwisp/openwisp-monitoring/tarball/master\n\n3. Add ``EXTENDED_APPS``\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nAdd the following to your ``settings.py``:\n\n.. code-block:: python\n\n    EXTENDED_APPS = ['device_monitoring', 'monitoring', 'check']\n\n4. Add ``openwisp_utils.staticfiles.DependencyFinder``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nAdd ``openwisp_utils.staticfiles.DependencyFinder`` to\n``STATICFILES_FINDERS`` in your ``settings.py``:\n\n.. code-block:: python\n\n    STATICFILES_FINDERS = [\n        'django.contrib.staticfiles.finders.FileSystemFinder',\n        'django.contrib.staticfiles.finders.AppDirectoriesFinder',\n        'openwisp_utils.staticfiles.DependencyFinder',\n    ]\n\n5. Add ``openwisp_utils.loaders.DependencyLoader``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nAdd ``openwisp_utils.loaders.DependencyLoader`` to ``TEMPLATES`` in your ``settings.py``:\n\n.. code-block:: python\n\n    TEMPLATES = [\n        {\n            'BACKEND': 'django.template.backends.django.DjangoTemplates',\n            'OPTIONS': {\n                'loaders': [\n                    'django.template.loaders.filesystem.Loader',\n                    'django.template.loaders.app_directories.Loader',\n                    'openwisp_utils.loaders.DependencyLoader',\n                ],\n                'context_processors': [\n                    'django.template.context_processors.debug',\n                    'django.template.context_processors.request',\n                    'django.contrib.auth.context_processors.auth',\n                    'django.contrib.messages.context_processors.messages',\n                ],\n            },\n        }\n    ]\n\n6. Inherit the AppConfig class\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nPlease refer to the following files in the sample app of the test project:\n\n- `sample_check/__init__.py <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_check/__init__.py>`_.\n- `sample_check/apps.py <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_check/apps.py>`_.\n- `sample_monitoring/__init__.py <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_monitoring/__init__.py>`_.\n- `sample_monitoring/apps.py <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_monitoring/apps.py>`_.\n- `sample_device_monitoring/__init__.py <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_device_monitoring/__init__.py>`_.\n- `sample_device_monitoring/apps.py <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_device_monitoring/apps.py>`_.\n\nFor more information regarding the concept of ``AppConfig`` please refer to\nthe `\"Applications\" section in the django documentation <https://docs.djangoproject.com/en/dev/ref/applications/>`_.\n\n7. Create your custom models\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nTo extend ``check`` app, refer to `sample_check models.py file <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_check/models.py>`_.\n\nTo extend ``monitoring`` app, refer to `sample_monitoring models.py file <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_monitoring/models.py>`_.\n\nTo extend ``device_monitoring`` app, refer to `sample_device_monitoring models.py file <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_device_monitoring/models.py>`_.\n\n**Note**:\n\n- For doubts regarding how to use, extend or develop models please refer to\n  the `\"Models\" section in the django documentation <https://docs.djangoproject.com/en/dev/topics/db/models/>`_.\n- For doubts regarding proxy models please refer to `proxy models <https://docs.djangoproject.com/en/dev/topics/db/models/#proxy-models>`_.\n\n8. Add swapper configurations\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nAdd the following to your ``settings.py``:\n\n.. code-block:: python\n\n    # Setting models for swapper module\n    # For extending check app\n    CHECK_CHECK_MODEL = 'YOUR_MODULE_NAME.Check'\n    # For extending monitoring app\n    MONITORING_CHART_MODEL = 'YOUR_MODULE_NAME.Chart'\n    MONITORING_METRIC_MODEL = 'YOUR_MODULE_NAME.Metric'\n    MONITORING_ALERTSETTINGS_MODEL = 'YOUR_MODULE_NAME.AlertSettings'\n    # For extending device_monitoring app\n    DEVICE_MONITORING_DEVICEDATA_MODEL = 'YOUR_MODULE_NAME.DeviceData'\n    DEVICE_MONITORING_DEVICEMONITORING_MODEL = 'YOUR_MODULE_NAME.DeviceMonitoring'\n\nSubstitute ``<YOUR_MODULE_NAME>`` with your actual django app name\n(also known as ``app_label``).\n\n9. Create database migrations\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nCreate and apply database migrations::\n\n    ./manage.py makemigrations\n    ./manage.py migrate\n\nFor more information, refer to the\n`\"Migrations\" section in the django documentation <https://docs.djangoproject.com/en/dev/topics/migrations/>`_.\n\n10. Create your custom admin\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nTo extend ``check`` app, refer to `sample_check admin.py file <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_check/admin.py>`_.\n\nTo extend ``monitoring`` app, refer to `sample_monitoring admin.py file <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_monitoring/admin.py>`_.\n\nTo extend ``device_monitoring`` app, refer to `sample_device_monitoring admin.py file <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_device_monitoring/admin.py>`_.\n\nTo introduce changes to the admin, you can do it in the two ways described below.\n\n**Note**: for doubts regarding how the django admin works, or how it can be customized,\nplease refer to `\"The django admin site\" section in the django documentation <https://docs.djangoproject.com/en/dev/ref/contrib/admin/>`_.\n\n1. Monkey patching\n##################\n\nIf the changes you need to add are relatively small, you can resort to monkey patching.\n\nFor example, for ``check`` app you can do it as:\n\n.. code-block:: python\n\n    from openwisp_monitoring.check.admin import CheckAdmin\n\n    CheckAdmin.list_display.insert(1, 'my_custom_field')\n    CheckAdmin.ordering = ['-my_custom_field']\n\nSimilarly for ``device_monitoring`` app, you can do it as:\n\n.. code-block:: python\n\n    from openwisp_monitoring.device.admin import DeviceAdmin\n\n    DeviceAdmin.list_display.insert(1, 'my_custom_field')\n    DeviceAdmin.ordering = ['-my_custom_field']\n\nSimilarly for ``monitoring`` app, you can do it as:\n\n.. code-block:: python\n\n    from openwisp_monitoring.monitoring.admin import MetricAdmin, AlertSettingsAdmin\n\n    MetricAdmin.list_display.insert(1, 'my_custom_field')\n    MetricAdmin.ordering = ['-my_custom_field']\n    AlertSettingsAdmin.list_display.insert(1, 'my_custom_field')\n    AlertSettingsAdmin.ordering = ['-my_custom_field']\n\n2. Inheriting admin classes\n###########################\n\nIf you need to introduce significant changes and/or you don't want to resort to\nmonkey patching, you can proceed as follows:\n\nFor ``check`` app,\n\n.. code-block:: python\n\n    from django.contrib import admin\n\n    from openwisp_monitoring.check.admin import CheckAdmin as BaseCheckAdmin\n    from swapper import load_model\n\n    Check = load_model('check', 'Check')\n\n    admin.site.unregister(Check)\n\n    @admin.register(Check)\n    class CheckAdmin(BaseCheckAdmin):\n        # add your changes here\n\nFor ``device_monitoring`` app,\n\n.. code-block:: python\n\n    from django.contrib import admin\n\n    from openwisp_monitoring.device_monitoring.admin import DeviceAdmin as BaseDeviceAdmin\n    from swapper import load_model\n\n    Device = load_model('config', 'Device')\n\n    admin.site.unregister(Device)\n\n    @admin.register(Device)\n    class DeviceAdmin(BaseDeviceAdmin):\n        # add your changes here\n\nFor ``monitoring`` app,\n\n.. code-block:: python\n\n    from django.contrib import admin\n\n    from openwisp_monitoring.monitoring.admin import (\n        AlertSettingsAdmin as BaseAlertSettingsAdmin,\n        MetricAdmin as BaseMetricAdmin\n    )\n    from swapper import load_model\n\n    Metric = load_model('Metric')\n    AlertSettings = load_model('AlertSettings')\n\n    admin.site.unregister(Metric)\n    admin.site.unregister(AlertSettings)\n\n    @admin.register(Metric)\n    class MetricAdmin(BaseMetricAdmin):\n        # add your changes here\n\n    @admin.register(AlertSettings)\n    class AlertSettingsAdmin(BaseAlertSettingsAdmin):\n        # add your changes here\n\n11. Create root URL configuration\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nPlease refer to the `urls.py <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/urls.py>`_\nfile in the test project.\n\nFor more information about URL configuration in django, please refer to the\n`\"URL dispatcher\" section in the django documentation <https://docs.djangoproject.com/en/dev/topics/http/urls/>`_.\n\n12. Create celery.py\n~~~~~~~~~~~~~~~~~~~~\n\nPlease refer to the `celery.py <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/celery.py>`_\nfile in the test project.\n\nFor more information about the usage of celery in django, please refer to the\n`\"First steps with Django\" section in the celery documentation <https://docs.celeryproject.org/en/master/django/first-steps-with-django.html>`_.\n\n13. Import Celery Tasks\n~~~~~~~~~~~~~~~~~~~~~~~\n\nAdd the following in your settings.py to import celery tasks from ``device_monitoring`` app.\n\n.. code-block:: python\n\n    CELERY_IMPORTS = ('openwisp_monitoring.device.tasks',)\n\n14. Create the custom command ``run_checks``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nPlease refer to the `run_checks.py <https://github.com/openwisp/openwisp-monitoring/tree/master/tests/openwisp2/sample_check/management/commands/run_checks.py>`_\nfile in the test project.\n\nFor more information about the usage of custom management commands in django, please refer to the\n`\"Writing custom django-admin commands\" section in the django documentation <https://docs.djangoproject.com/en/dev/howto/custom-management-commands/>`_.\n\n15. Import the automated tests\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWhen developing a custom application based on this module, it's a good idea\nto import and run the base tests too, so that you can be sure the changes you're introducing\nare not breaking some of the existing features of openwisp-monitoring.\n\nIn case you need to add breaking changes, you can overwrite the tests defined\nin the base classes to test your own behavior.\n\nFor, extending ``check`` app see the `tests of sample_check app <https://github.com/openwisp/openwisp-monitoring/blob/master/tests/openwisp2/sample_check/tests.py>`_\nto find out how to do this.\n\nFor, extending ``device_monitoring`` app see the `tests of sample_device_monitoring app <https://github.com/openwisp/openwisp-monitoring/blob/master/tests/openwisp2/sample_device_monitoring/tests.py>`_\nto find out how to do this.\n\nFor, extending ``monitoring`` app see the `tests of sample_monitoring app <https://github.com/openwisp/openwisp-monitoring/blob/master/tests/openwisp2/sample_monitoring/tests.py>`_\nto find out how to do this.\n\nOther base classes that can be inherited and extended\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n**The following steps are not required and are intended for more advanced customization.**\n\n``DeviceMetricView``\n####################\n\nThis view is responsible for displaying ``Charts`` and ``Status`` primarily.\n\nThe full python path is: ``openwisp_monitoring.device.api.views.DeviceMetricView``.\n\nIf you want to extend this view, you will have to perform the additional steps below.\n\nStep 1. Import and extend view:\n\n.. code-block:: python\n\n    # mydevice/api/views.py\n    from openwisp_monitoring.device.api.views import (\n        DeviceMetricView as BaseDeviceMetricView\n    )\n\n    class DeviceMetricView(BaseDeviceMetricView):\n        # add your customizations here ...\n        pass\n\nStep 2: remove the following line from your root ``urls.py`` file:\n\n.. code-block:: python\n\n    re_path(\n        'api/v1/monitoring/device/(?P<pk>[^/]+)/$',\n        views.device_metric,\n        name='api_device_metric',\n    ),\n\nStep 3: add an URL route pointing to your custom view in ``urls.py`` file:\n\n.. code-block:: python\n\n    # urls.py\n    from mydevice.api.views import DeviceMetricView\n\n    urlpatterns = [\n        # ... other URLs\n        re_path(r'^(?P<path>.*)$', DeviceMetricView.as_view(), name='api_device_metric',),\n    ]\n\nContributing\n------------\n\nPlease refer to the `OpenWISP contributing guidelines <http://openwisp.io/docs/developer/contributing.html>`_.\n\n\n",
    "bugtrack_url": null,
    "license": "GPL3",
    "summary": "OpenWISP 2 Monitoring",
    "version": "1.0.4",
    "split_keywords": [
        "django",
        "netjson",
        "networking",
        "openwisp",
        "monitoring"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "0c2bb3c9c6e05b704b74288aa675dff1e2cabc1b78ef7b2e74255ff40ce58b58",
                "md5": "9e2f6b3746d7a72b8ed173748c026fa5",
                "sha256": "61717c284d1dd9fb3d1bf1cdb6c9b80a010e04b4e33924647c4fb801abe4a031"
            },
            "downloads": -1,
            "filename": "openwisp_monitoring-1.0.4-py2.py3-none-any.whl",
            "has_sig": true,
            "md5_digest": "9e2f6b3746d7a72b8ed173748c026fa5",
            "packagetype": "bdist_wheel",
            "python_version": "py2.py3",
            "requires_python": null,
            "size": 538818,
            "upload_time": "2023-01-24T19:48:51",
            "upload_time_iso_8601": "2023-01-24T19:48:51.959781Z",
            "url": "https://files.pythonhosted.org/packages/0c/2b/b3c9c6e05b704b74288aa675dff1e2cabc1b78ef7b2e74255ff40ce58b58/openwisp_monitoring-1.0.4-py2.py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "4b3a74bfcbb6c8411762060fbe9deba442a2d571d6f0ffd1efa01f39051fdef5",
                "md5": "4eb933ce23329093481761aee056a4d0",
                "sha256": "66fa1f6fa0bf441a4104388b3cf98f061c1d605dd06a4dab3ba7dce15d693c14"
            },
            "downloads": -1,
            "filename": "openwisp-monitoring-1.0.4.tar.gz",
            "has_sig": true,
            "md5_digest": "4eb933ce23329093481761aee056a4d0",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 531009,
            "upload_time": "2023-01-24T19:48:59",
            "upload_time_iso_8601": "2023-01-24T19:48:59.474231Z",
            "url": "https://files.pythonhosted.org/packages/4b/3a/74bfcbb6c8411762060fbe9deba442a2d571d6f0ffd1efa01f39051fdef5/openwisp-monitoring-1.0.4.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-01-24 19:48:59",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "lcname": "openwisp-monitoring"
}
        
Elapsed time: 0.03780s