{"id":22371411,"url":"https://github.com/manukumarnm/openwisp-controller","last_synced_at":"2025-06-11T20:08:36.060Z","repository":{"id":108754070,"uuid":"513497200","full_name":"ManukumarNM/openwisp-controller","owner":"ManukumarNM","description":null,"archived":false,"fork":false,"pushed_at":"2022-07-13T11:45:07.000Z","size":5464,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-26T16:53:11.433Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ManukumarNM.png","metadata":{"files":{"readme":"README.rst","changelog":"CHANGES.rst","contributing":"CONTRIBUTING.rst","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":"publiccode.yml","codemeta":null}},"created_at":"2022-07-13T11:38:01.000Z","updated_at":"2022-07-13T11:45:12.000Z","dependencies_parsed_at":"2023-06-04T18:45:36.089Z","dependency_job_id":null,"html_url":"https://github.com/ManukumarNM/openwisp-controller","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ManukumarNM/openwisp-controller","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ManukumarNM%2Fopenwisp-controller","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ManukumarNM%2Fopenwisp-controller/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ManukumarNM%2Fopenwisp-controller/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ManukumarNM%2Fopenwisp-controller/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ManukumarNM","download_url":"https://codeload.github.com/ManukumarNM/openwisp-controller/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ManukumarNM%2Fopenwisp-controller/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259330619,"owners_count":22841674,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-04T20:19:56.226Z","updated_at":"2025-06-11T20:08:36.052Z","avatar_url":"https://github.com/ManukumarNM.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"openwisp-controller\n===================\n\n.. image:: https://github.com/openwisp/openwisp-controller/workflows/OpenWISP%20Controller%20CI%20Build/badge.svg?branch=master\n   :target: https://github.com/openwisp/openwisp-controller/actions?query=workflow%3A%22OpenWISP+Controller+CI+Build%22\n   :alt: CI build status\n\n.. image:: https://coveralls.io/repos/openwisp/openwisp-controller/badge.svg\n   :target: https://coveralls.io/r/openwisp/openwisp-controller\n   :alt: Test Coverage\n\n.. image:: https://img.shields.io/librariesio/release/github/openwisp/openwisp-controller\n  :target: https://libraries.io/github/openwisp/openwisp-controller#repository_dependencies\n  :alt: Dependency monitoring\n\n.. image:: https://img.shields.io/gitter/room/nwjs/nw.js.svg\n   :target: https://gitter.im/openwisp/general\n   :alt: Chat\n\n.. image:: https://badge.fury.io/py/openwisp-controller.svg\n   :target: http://badge.fury.io/py/openwisp-controller\n   :alt: Pypi Version\n\n.. image:: https://pepy.tech/badge/openwisp-controller\n   :target: https://pepy.tech/project/openwisp-controller\n   :alt: Downloads\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://raw.githubusercontent.com/openwisp/openwisp-controller/master/docs/controller_demo.gif\n   :target: https://github.com/openwisp/openwisp-controller/tree/master/docs/controller_demo.gif\n   :alt: Feature Highlights\n\n------------\n\nOpenWISP Controller is a configuration manager that allows to automate several\nnetworking tasks like adoption, provisioning, management VPN configuration,\nX509 certificates automatic generation, revocation of x509 certificates and\na lot more features.\n\nOpenWISP is not only an application designed for end users, but can also be\nused as a framework on which custom network automation solutions can be built\non top of its building blocks.\n\nOther popular building blocks that are part of the OpenWISP ecosystem are:\n\n- `openwisp-monitoring \u003chttps://github.com/openwisp/openwisp-monitoring\u003e`_:\n  provides device status monitoring, collection of metrics, charts, alerts,\n  possibility to define custom checks\n- `openwisp-firmware-upgrader \u003chttps://github.com/openwisp/openwisp-firmware-upgrader\u003e`_:\n  automated firmware upgrades (single devices or mass network upgrades)\n- `openwisp-radius \u003chttps://github.com/openwisp/openwisp-radius\u003e`_:\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-network-topology \u003chttps://github.com/openwisp/openwisp-network-topology\u003e`_:\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-ipam \u003chttps://github.com/openwisp/openwisp-ipam\u003e`_:\n  allows to manage the assignment of IP addresses used in the network\n- `openwisp-notifications \u003chttps://github.com/openwisp/openwisp-notifications\u003e`_:\n  allows users to be aware of important events happening in the network.\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp2-docs/master/assets/design/openwisp-logo-black.svg\n  :target: http://openwisp.org\n  :alt: OpenWISP\n\n**Want to help OpenWISP?** `Find out how to help us grow here\n\u003chttp://openwisp.io/docs/general/help-us.html\u003e`_.\n\n------------\n\n.. contents:: **Table of Contents**:\n   :backlinks: none\n   :depth: 3\n\n------------\n\nDeploy it in production\n-----------------------\n\nAn automated installer is available at `ansible-openwisp2 \u003chttps://github.com/openwisp/ansible-openwisp2\u003e`_.\n\nDependencies\n------------\n\n* Python \u003e= 3.7\n* OpenSSL\n\nInstall stable version from pypi\n--------------------------------\n\nInstall from pypi:\n\n.. code-block:: shell\n\n    pip install openwisp-controller\n\nInstall development version\n---------------------------\n\nInstall tarball:\n\n.. code-block:: shell\n\n    pip install https://github.com/openwisp/openwisp-controller/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-controller#egg=openwisp_controller\n\nIf you want to contribute, follow the instructions in\n`Installing for development \u003c#installing-for-development\u003e`_.\n\nProject Structure \u0026 main features\n----------------------------------\n\nOpenWISP Controller is a python package consisting of four django apps:\n\nConfig App\n~~~~~~~~~~\n\n* **configuration management** for embedded devices supporting different firmwares:\n    - `OpenWRT \u003chttp://openwrt.org\u003e`_\n    - `OpenWISP Firmware \u003chttps://github.com/openwisp/OpenWISP-Firmware\u003e`_\n    - support for additional firmware can be added by `specifying custom backends \u003c#netjsonconfig-backends\u003e`_\n* **configuration editor** based on `JSON-Schema editor \u003chttps://github.com/jdorn/json-editor\u003e`_\n* **advanced edit mode**: edit `NetJSON  \u003chttp://netjson.org\u003e`_ *DeviceConfiguration* objects for maximum flexibility\n* **configuration templates**: reduce repetition to the minimum\n* `configuration variables \u003c#how-to-use-configuration-variables\u003e`_: reference ansible-like variables in the configuration and templates\n* **template tags**: tag templates to automate different types of auto-configurations (eg: mesh, WDS, 4G)\n* **device groups**: add `devices to dedicated groups \u003c#device-groups\u003e`_ for easy management\n* **simple HTTP resources**: allow devices to automatically download configuration updates\n* **VPN management**: `automatically provision VPN tunnels \u003c#openwisp-controller-default-auto-cert\u003e`_,\n  including cryptographic keys, IP addresses\n\nPKI App\n~~~~~~~\n\nThe PKI app is based on `django-x509 \u003chttps://github.com/openwisp/django-x509\u003e`_,\nit allows to create, import and view x509 CAs and certificates directly from\nthe administration dashboard.\n\nConnection App\n~~~~~~~~~~~~~~\n\nThis app enables the controller to instantiate connections to the devices\nin order perform `push operations \u003c#how-to-configure-push-updates\u003e`__:\n\n- Sending configuration updates.\n- `Executing shell commands \u003c#sending-commands-to-devices\u003e`_.\n- Perform `firmware upgrades via the additional firmware upgrade module \u003chttps://github.com/openwisp/openwisp-firmware-upgrader\u003e`_.\n\nThe default connection protocol implemented is SSH, but other protocol\nmechanism is extensible and custom protocols can be implemented as well.\n\nAccess via SSH key is recommended, the SSH key algorithms supported are:\n\n- RSA\n- Ed25519\n\nGeo App\n~~~~~~~\n\nThe geographic app is based on `django-loci \u003chttps://github.com/openwisp/django-loci\u003e`_\nand allows to define the geographic coordinates of the devices,\nas well as their indoor coordinates on floorplan images.\n\nSubnet Division App\n~~~~~~~~~~~~~~~~~~~\n\nThis app allows to automatically provision subnets and IP addresses which will be\navailable as `system defined configuration variables \u003c#system-defined-variables\u003e`_\nthat can be used in templates. The purpose of this app is to allow users to automatically\nprovision and configure specific\nsubnets and IP addresses to the devices without the need of manual intervention.\n\nRefer to `\"How to configure automatic provisioning of subnets and IPs\" section of this documentation \u003c#how-to-configure-automatic-provisioning-of-subnets-and-ips\u003e`_\nto learn about features provided by this app.\n\nThis app is optional, if you don't need it you can avoid adding it to ``settings.INSTALLED_APPS``.\n\nSettings\n--------\n\nYou can change the values for the following variables in\n``settings.py`` to configure your instance of openwisp-controller.\n\n``OPENWISP_SSH_AUTH_TIMEOUT``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-------------+\n| **type**:    |   ``int``   |\n+--------------+-------------+\n| **default**: |    ``2``    |\n+--------------+-------------+\n| **unit**:    | ``seconds`` |\n+--------------+-------------+\n\nConfigure timeout to wait for an authentication response when establishing a SSH connection.\n\n``OPENWISP_SSH_BANNER_TIMEOUT``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-------------+\n| **type**:    |   ``int``   |\n+--------------+-------------+\n| **default**: |    ``60``   |\n+--------------+-------------+\n| **unit**:    | ``seconds`` |\n+--------------+-------------+\n\nConfigure timeout to wait for the banner to be presented when establishing a SSH connection.\n\n``OPENWISP_SSH_COMMAND_TIMEOUT``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-------------+\n| **type**:    |   ``int``   |\n+--------------+-------------+\n| **default**: |    ``30``   |\n+--------------+-------------+\n| **unit**:    | ``seconds`` |\n+--------------+-------------+\n\nConfigure timeout on blocking read/write operations when executing a command in a SSH connection.\n\n``OPENWISP_SSH_CONNECTION_TIMEOUT``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-------------+\n| **type**:    |   ``int``   |\n+--------------+-------------+\n| **default**: |    ``5``    |\n+--------------+-------------+\n| **unit**:    | ``seconds`` |\n+--------------+-------------+\n\nConfigure timeout for the TCP connect when establishing a SSH connection.\n\n``OPENWISP_CONNECTORS``\n~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+--------------------------------------------------------------------+\n| **type**:    | ``tuple``                                                          |\n+--------------+--------------------------------------------------------------------+\n| **default**: | .. code-block:: python                                             |\n|              |                                                                    |\n|              |   (                                                                |\n|              |     ('openwisp_controller.connection.connectors.ssh.Ssh', 'SSH'),  |\n|              |   )                                                                |\n+--------------+--------------------------------------------------------------------+\n\nAvailable connector classes. Connectors are python classes that specify ways\nin which OpenWISP can connect to devices in order to launch commands.\n\n``OPENWISP_UPDATE_STRATEGIES``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+----------------------------------------------------------------------------------------+\n| **type**:    | ``tuple``                                                                              |\n+--------------+----------------------------------------------------------------------------------------+\n| **default**: | .. code-block:: python                                                                 |\n|              |                                                                                        |\n|              |   (                                                                                    |\n|              |     ('openwisp_controller.connection.connectors.openwrt.ssh.OpenWrt', 'OpenWRT SSH'),  |\n|              |   )                                                                                    |\n+--------------+----------------------------------------------------------------------------------------+\n\nAvailable update strategies. An update strategy is a subclass of a\nconnector class which defines an ``update_config`` method which is\nin charge of updating the configuration of the device.\n\nThis operation is launched in a background worker when the configuration\nof a device is changed.\n\nIt's possible to write custom update strategies and add them to this\nsetting to make them available in OpenWISP.\n\n``OPENWISP_CONFIG_UPDATE_MAPPING``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+--------------------------------------------------------------------+\n| **type**:    | ``dict``                                                           |\n+--------------+--------------------------------------------------------------------+\n| **default**: | .. code-block:: python                                             |\n|              |                                                                    |\n|              |   {                                                                |\n|              |     'netjsonconfig.OpenWrt': OPENWISP_UPDATE_STRATEGIES[0][0],     |\n|              |   }                                                                |\n+--------------+--------------------------------------------------------------------+\n\nA dictionary that maps configuration backends to update strategies in order to\nautomatically determine the update strategy of a device connection if the\nupdate strategy field is left blank by the user.\n\n``OPENWISP_CONTROLLER_BACKENDS``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-----------------------------------------------+\n| **type**:    | ``tuple``                                     |\n+--------------+-----------------------------------------------+\n| **default**: | .. code-block:: python                        |\n|              |                                               |\n|              |   (                                           |\n|              |     ('netjsonconfig.OpenWrt', 'OpenWRT'),     |\n|              |     ('netjsonconfig.OpenWisp', 'OpenWISP'),   |\n|              |   )                                           |\n+--------------+-----------------------------------------------+\n\nAvailable configuration backends. For more information, see `netjsonconfig backends\n\u003chttp://netjsonconfig.openwisp.org/en/latest/general/basics.html#backend\u003e`_.\n\n``OPENWISP_CONTROLLER_VPN_BACKENDS``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+----------------------------------------------------------------------------------+\n| **type**:    | ``tuple``                                                                        |\n+--------------+----------------------------------------------------------------------------------+\n| **default**: | .. code-block:: python                                                           |\n|              |                                                                                  |\n|              |   (                                                                              |\n|              |     ('openwisp_controller.vpn_backends.OpenVpn', 'OpenVPN'),                     |\n|              |     ('openwisp_controller.vpn_backends.Wireguard', 'WireGuard'),                 |\n|              |     ('openwisp_controller.vpn_backends.VxlanWireguard', 'VXLAN over WireGuard'), |\n|              |   )                                                                              |\n+--------------+----------------------------------------------------------------------------------+\n\nAvailable VPN backends for VPN Server objects. For more information, see `netjsonconfig VPN backends\n\u003chttps://netjsonconfig.openwisp.org/en/latest/backends/vpn-backends.html\u003e`_.\n\nA VPN backend must follow some basic rules in order to be compatible with *openwisp-controller*:\n\n* it MUST allow at minimum and at maximum one VPN instance\n* the main *NetJSON* property MUST match the lowercase version of the class name,\n  eg: when using the ``OpenVpn`` backend, the system will look into\n  ``config['openvpn']``\n* it SHOULD focus on the server capabilities of the VPN software being used\n\n``OPENWISP_CONTROLLER_DEFAULT_BACKEND``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+----------------------------------------+\n| **type**:    | ``str``                                |\n+--------------+----------------------------------------+\n| **default**: | ``OPENWISP_CONTROLLER_BACKENDS[0][0]`` |\n+--------------+----------------------------------------+\n\nThe preferred backend that will be used as initial value when adding new ``Config`` or\n``Template`` objects in the admin.\n\nThis setting defaults to the raw value of the first item in the ``OPENWISP_CONTROLLER_BACKENDS`` setting,\nwhich is ``netjsonconfig.OpenWrt``.\n\nSetting it to ``None`` will force the user to choose explicitly.\n\n``OPENWISP_CONTROLLER_DEFAULT_VPN_BACKEND``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+--------------------------------------------+\n| **type**:    | ``str``                                    |\n+--------------+--------------------------------------------+\n| **default**: | ``OPENWISP_CONTROLLER_VPN_BACKENDS[0][0]`` |\n+--------------+--------------------------------------------+\n\nThe preferred backend that will be used as initial value when adding new ``Vpn`` objects in the admin.\n\nThis setting defaults to the raw value of the first item in the ``OPENWISP_CONTROLLER_VPN_BACKENDS`` setting,\nwhich is ``openwisp_controller.vpn_backends.OpenVpn``.\n\nSetting it to ``None`` will force the user to choose explicitly.\n\n``OPENWISP_CONTROLLER_REGISTRATION_ENABLED``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-------------+\n| **type**:    | ``bool``    |\n+--------------+-------------+\n| **default**: | ``True``    |\n+--------------+-------------+\n\nWhether devices can automatically register through the controller or not.\n\nThis feature is enabled by default.\n\nAutoregistration must be supported on the devices in order to work, see `openwisp-config automatic\nregistration \u003chttps://github.com/openwisp/openwisp-config#automatic-registration\u003e`_ for more information.\n\n``OPENWISP_CONTROLLER_CONSISTENT_REGISTRATION``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-------------+\n| **type**:    | ``bool``    |\n+--------------+-------------+\n| **default**: | ``True``    |\n+--------------+-------------+\n\nWhether devices that are already registered are recognized when reflashed or reset, hence keeping\nthe existing configuration without creating a new one.\n\nThis feature is enabled by default.\n\nAutoregistration must be enabled also on the devices in order to work, see `openwisp-config\nconsistent key generation \u003chttps://github.com/openwisp/openwisp-config#consistent-key-generation\u003e`_\nfor more information.\n\n``OPENWISP_CONTROLLER_REGISTRATION_SELF_CREATION``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-------------+\n| **type**:    | ``bool``    |\n+--------------+-------------+\n| **default**: | ``True``    |\n+--------------+-------------+\n\nWhether devices that are not already present in the system are allowed to register or not.\n\nTurn this off if you still want to use auto-registration to avoid having to\nmanually set the device UUID and key in its configuration file but also want\nto avoid indiscriminate registration of new devices without explicit permission.\n\n``OPENWISP_CONTROLLER_CONTEXT``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+------------------+\n| **type**:    | ``dict``         |\n+--------------+------------------+\n| **default**: | ``{}``           |\n+--------------+------------------+\n\nAdditional context that is passed to the default context of each device object.\n\n``OPENWISP_CONTROLLER_CONTEXT`` can be used to define system-wide configuration variables.\n\nFor more information regarding how to use configuration variables in OpenWISP,\nsee `How to use configuration variables \u003c#how-to-use-configuration-variables\u003e`_.\n\nFor technical information about how variables are handled in the lower levels\nof OpenWISP, see `netjsonconfig context: configuration variables\n\u003chttp://netjsonconfig.openwisp.org/en/latest/general/basics.html#context-configuration-variables\u003e`_.\n\n``OPENWISP_CONTROLLER_DEFAULT_AUTO_CERT``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+---------------------------+\n| **type**:    | ``bool``                  |\n+--------------+---------------------------+\n| **default**: | ``True``                  |\n+--------------+---------------------------+\n\nThe default value of the ``auto_cert`` field for new ``Template`` objects.\n\nThe ``auto_cert`` field is valid only for templates which have ``type``\nset to ``VPN`` and indicates whether configuration regarding the VPN tunnel is\nprovisioned automatically to each device using the template, eg:\n\n- when using OpenVPN, new `x509 \u003chttps://tools.ietf.org/html/rfc5280\u003e`_ certificates\n  will be generated automatically using the same CA assigned to the related VPN object\n- when using WireGuard, new pair of private and public keys\n  (using `Curve25519 \u003chttp://cr.yp.to/ecdh.html\u003e`_) will be generated, as well as\n  an IP address of the subnet assigned to the related VPN object\n- when using `VXLAN \u003chttps://tools.ietf.org/html/rfc7348\u003e`_ tunnels over Wireguad,\n  in addition to the configuration generated for WireGuard, a new VID will be generated\n  automatically for each device if the configuration option \"auto VNI\" is turned on in\n  the VPN object\n\nAll these auto generated configuration options will be available as\ntemplate variables.\n\nThe objects that are automatically created will also be removed when they are not\nneeded anymore (eg: when the VPN template is removed from a configuration object).\n\n``OPENWISP_CONTROLLER_CERT_PATH``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+---------------------------+\n| **type**:    | ``str``                   |\n+--------------+---------------------------+\n| **default**: | ``/etc/x509``             |\n+--------------+---------------------------+\n\nThe filesystem path where x509 certificate will be installed when\ndownloaded on routers when ``auto_cert`` is being used (enabled by default).\n\n``OPENWISP_CONTROLLER_COMMON_NAME_FORMAT``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+------------------------------+\n| **type**:    | ``str``                      |\n+--------------+------------------------------+\n| **default**: | ``{mac_address}-{name}``     |\n+--------------+------------------------------+\n\nDefines the format of the ``common_name`` attribute of VPN client certificates that are automatically\ncreated when using VPN templates which have ``auto_cert`` set to ``True``.\n\n``OPENWISP_CONTROLLER_MANAGEMENT_IP_DEVICE_LIST``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+------------------------------+\n| **type**:    | ``bool``                     |\n+--------------+------------------------------+\n| **default**: | ``True``                     |\n+--------------+------------------------------+\n\nIn the device list page, the column ``IP`` will show the ``management_ip`` if\navailable, defaulting to ``last_ip`` otherwise.\n\nIf this setting is set to ``False`` the ``management_ip`` won't be shown\nin the device list page even if present, it will be shown only in the device\ndetail page.\n\nYou may set this to ``False`` if for some reason the majority of your user\ndoesn't care about the management ip address.\n\n``OPENWISP_CONTROLLER_CONFIG_BACKEND_FIELD_SHOWN``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+------------------------------+\n| **type**:    | ``bool``                     |\n+--------------+------------------------------+\n| **default**: | ``True``                     |\n+--------------+------------------------------+\n\nThis setting toggles the ``backend`` fields in add/edit pages in Device and Template configuration,\nas well as the ``backend`` field/filter in Device list and Template list.\n\nIf this setting is set to ``False`` these items will be removed from the UI.\n\nNote: This setting affects only the configuration backend and NOT the VPN backend.\n\n``OPENWISP_CONTROLLER_DEVICE_NAME_UNIQUE``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-------------+\n| **type**:    | ``bool``    |\n+--------------+-------------+\n| **default**: | ``True``    |\n+--------------+-------------+\n\nThis setting conditionally enforces unique Device names in an Organization.\nThe query to enforce this is case-insensitive.\n\nNote: For this constraint to be optional, it is enforced on an application level and not on database.\n\n``OPENWISP_CONTROLLER_HARDWARE_ID_ENABLED``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-------------+\n| **type**:    | ``bool``    |\n+--------------+-------------+\n| **default**: | ``False``   |\n+--------------+-------------+\n\nThe field ``hardware_id`` can be used to store a unique hardware id, for example a serial number.\n\nIf this setting is set to ``True`` then this field will be shown first in the device list page\nand in the add/edit device page.\n\nThis feature is disabled by default.\n\n``OPENWISP_CONTROLLER_HARDWARE_ID_OPTIONS``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+--------------------------------------------------------------+\n| **type**:    | ``dict``                                                     |\n+--------------+--------------------------------------------------------------+\n| **default**: | .. code-block:: python                                       |\n|              |                                                              |\n|              |    {                                                         |\n|              |        'blank': not OPENWISP_CONTROLLER_HARDWARE_ID_ENABLED, |\n|              |        'null': True,                                         |\n|              |        'max_length': 32,                                     |\n|              |        'unique': True,                                       |\n|              |        'verbose_name': _('Serial number'),                   |\n|              |        'help_text': _('Serial number of this device')        |\n|              |    }                                                         |\n+--------------+--------------------------------------------------------------+\n\nOptions for the model field ``hardware_id``.\n\n* ``blank``: wether the field is allowed to be blank\n* ``null``: wether an empty value will be stored as ``NULL`` in the database\n* ``max_length``: maximum length of the field\n* ``unique``: wether the value of the field must be unique\n* ``verbose_name``: text for the human readable label of the field\n* ``help_text``: help text to be displayed with the field\n\n``OPENWISP_CONTROLLER_HARDWARE_ID_AS_NAME``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-------------+\n| **type**:    | ``bool``    |\n+--------------+-------------+\n| **default**: | ``True``    |\n+--------------+-------------+\n\nWhen the hardware ID feature is enabled, devices will be referenced with\ntheir hardware ID instead of their name.\n\nIf you still want to reference devices by their name, set this to ``False``.\n\n``OPENWISP_CONTROLLER_DEVICE_VERBOSE_NAME``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+----------------------------+\n| **type**:    | ``tuple``                  |\n+--------------+----------------------------+\n| **default**: | ``('Device', 'Devices')``  |\n+--------------+----------------------------+\n\nDefines the ``verbose_name`` attribute of the ``Device`` model, which is displayed in the\nadmin site. The first and second element of the tuple represent the singular and plural forms.\n\nFor example, if we want to change the verbose name to \"Hotspot\", we could write:\n\n.. code-block:: python\n\n    OPENWISP_CONTROLLER_DEVICE_VERBOSE_NAME = ('Hotspot', 'Hotspots')\n\n``OPENWISP_CONTROLLER_HIDE_AUTOMATICALLY_GENERATED_SUBNETS_AND_IPS``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-----------+\n| **type**:    | ``bool``  |\n+--------------+-----------+\n| **default**: | ``False`` |\n+--------------+-----------+\n\nSetting this to ``True`` will hide subnets and IPs generated using `subnet division rules \u003c#subnet-division-app\u003e`_\nfrom being displayed on the changelist view of Subnet and IP admin.\n\n``OPENWISP_CONTROLLER_SUBNET_DIVISION_TYPES``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+---------------------------------------------------------------------------------------------------------+\n| **type**:    | ``tuple``                                                                                               |\n+--------------+---------------------------------------------------------------------------------------------------------+\n| **default**: | .. code-block:: python                                                                                  |\n|              |                                                                                                         |\n|              |    (                                                                                                    |\n|              |       ('openwisp_controller.subnet_division.rule_types.device.DeviceSubnetDivisionRuleType', 'Device'), |\n|              |       ('openwisp_controller.subnet_division.rule_types.vpn.VpnSubnetDivisionRuleType', 'VPN'),          |\n|              |    )                                                                                                    |\n|              |                                                                                                         |\n+--------------+---------------------------------------------------------------------------------------------------------+\n\n`Available types for Subject Division Rule \u003c#device-subnet-division-rule\u003e`_ objects.\nFor more information on how to write your own types, read\n`\"Custom Subnet Division Rule Types\" section of this documentation \u003c#custom-subnet-division-rule-types\u003e`_\n\n``OPENWISP_CONTROLLER_API``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-----------+\n| **type**:    | ``bool``  |\n+--------------+-----------+\n| **default**: | ``True``  |\n+--------------+-----------+\n\nIndicates whether the API for Openwisp Controller is enabled or not.\nTo disable the API by default add `OPENWISP_CONTROLLER_API = False` in `settings.py` file.\n\n``OPENWISP_CONTROLLER_API_HOST``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+-----------+\n| **type**:    | ``str``   |\n+--------------+-----------+\n| **default**: | ``None``  |\n+--------------+-----------+\n\nAllows to specify backend URL for API requests, if the frontend is hosted separately.\n\n``OPENWISP_CONTROLLER_USER_COMMANDS``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+----------+\n| **type**:    | ``list`` |\n+--------------+----------+\n| **default**: | ``[]``   |\n+--------------+----------+\n\nAllows to specify a `list` of tuples for adding commands as described in\n`'How to add commands\" \u003c#how-to-add-commands\u003e`_ section.\n\n``OPENWISP_CONTROLLER_DEVICE_GROUP_SCHEMA``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+------------------------------------------+\n| **type**:    | ``dict``                                 |\n+--------------+------------------------------------------+\n| **default**: | ``{'type': 'object', 'properties': {}}`` |\n+--------------+------------------------------------------+\n\nAllows specifying JSONSchema used for validating meta-data of `Device Group \u003c#device-groups\u003e`_.\n\n``OPENWISP_CONTROLLER_SHARED_MANAGEMENT_IP_ADDRESS_SPACE``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n+--------------+----------+\n| **type**:    | ``bool`` |\n+--------------+----------+\n| **default**: | ``True`` |\n+--------------+----------+\n\nBy default, the system assumes that the address space of the management\ntunnel is shared among all the organizations using the system, that is,\nthe system assumes there's only one management VPN, tunnel or other\nnetworking technology to reach the devices it controls.\n\nWhen set to ``True``, any device belonging to any\norganization will never have the same ``management_ip`` as another device,\nthe latest device declaring the management IP will take the IP and any\nother device who declared the same IP in the past will have the field\nreset to empty state to avoid potential conflicts.\n\nSet this to ``False`` if every organization has its dedicated management\ntunnel with a dedicated address space that is reachable by the OpenWISP server.\n\nREST API\n--------\n\nLive documentation\n~~~~~~~~~~~~~~~~~~\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/master/docs/live-docu-api.png\n\nA general live API documentation (following the OpenAPI specification) at ``/api/v1/docs/``.\n\nBrowsable web interface\n~~~~~~~~~~~~~~~~~~~~~~~\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/master/docs/browsable-api-ui.png\n\nAdditionally, opening any of the endpoints `listed below \u003c#list-of-endpoints\u003e`_\ndirectly in the browser will show the `browsable API interface of Django-REST-Framework\n\u003chttps://www.django-rest-framework.org/topics/browsable-api/\u003e`_,\nwhich makes it even easier to find out the details of each endpoint.\n\nAuthentication\n~~~~~~~~~~~~~~\n\nSee openwisp-users: `authenticating with the user token\n\u003chttps://github.com/openwisp/openwisp-users#authenticating-with-the-user-token\u003e`_.\n\nWhen browsing the API via the `Live documentation \u003c#live-documentation\u003e`_\nor the `Browsable web page \u003c#browsable-web-interface\u003e`_, you can also use\nthe session authentication by logging in the django admin.\n\nPagination\n~~~~~~~~~~\n\nAll *list* endpoints support the ``page_size`` parameter that allows paginating\nthe results in conjunction with the ``page`` parameter.\n\n.. code-block:: text\n\n    GET /api/v1/controller/template/?page_size=10\n    GET /api/v1/controller/template/?page_size=10\u0026page=2\n\nList of endpoints\n~~~~~~~~~~~~~~~~~\n\nSince the detailed explanation is contained in the `Live documentation \u003c#live-documentation\u003e`_\nand in the `Browsable web page \u003c#browsable-web-interface\u003e`_ 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\nList devices\n^^^^^^^^^^^^\n\n.. code-block:: text\n\n    GET /api/v1/controller/device/\n\nCreate device\n^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    POST /api/v1/controller/device/\n\nGet device detail\n^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    GET /api/v1/controller/device/{id}/\n\nDownload device configuration\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    GET /api/v1/controller/device/{id}/configuration/\n\nThe above endpoint triggers the download of a ``tar.gz`` file containing the generated configuration for that specific device.\n\nChange details of device\n^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    PUT /api/v1/controller/device/{id}/\n\nPatch details of device\n^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    PATCH /api/v1/controller/device/{id}/\n\n**Note**: To assign, unassign, and change the order of the assigned templates add,\nremove, and change the order of the ``{id}`` of the templates under the ``config`` field in the JSON response respectively.\nMoreover, you can also select and unselect templates in the HTML Form of the Browsable API.\n\nThe required template(s) from the organization(s) of the device will added automatically\nto the ``config`` and cannot be removed.\n\n**Example usage**: For assigning template(s) add the/their {id} to the config of a device,\n\n.. code-block:: shell\n\n    echo '{\"config\":{\"templates\": [\"4791fa4c-2cef-4f42-8bb4-c86018d71bd3\"]}}' | \\\n    http PATCH http://127.0.0.1:8000/api/v1/controller/device/76b7d9cc-4ffd-4a43-b1b0-8f8befd1a7c0/ \\\n    \"Authorization: Bearer 9b5e40da02d107cfdb9d6b69b26dc00332ec2fbc\"\n\n**Example usage**: For removing assigned templates, simply remove the/their {id} from the config of a device,\n\n.. code-block:: shell\n\n    echo '{\"config\":{\"templates\": []}}' | \\\n    http PATCH http://127.0.0.1:8000/api/v1/controller/device/76b7d9cc-4ffd-4a43-b1b0-8f8befd1a7c0/ \\\n    \"Authorization: Bearer 9b5e40da02d107cfdb9d6b69b26dc00332ec2fbc\"\n\n**Example usage**: For reordering the templates simply change their order from the config of a device,\n\n.. code-block:: shell\n\n    echo '{\"config\":{\"templates\": [\"c5bbc697-170e-44bc-8eb7-b944b55ee88f\",\"4791fa4c-2cef-4f42-8bb4-c86018d71bd3\"]}}' | \\\n    http PATCH http://127.0.0.1:8000/api/v1/controller/device/76b7d9cc-4ffd-4a43-b1b0-8f8befd1a7c0/ \\\n    \"Authorization: Bearer 9b5e40da02d107cfdb9d6b69b26dc00332ec2fbc\"\n\nDelete device\n^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    DELETE /api/v1/controller/device/{id}/\n\nList device connections\n^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    GET /api/v1/controller/device/{id}/connection/\n\nCreate device connection\n^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    POST /api/v1/controller/device/{id}/connection/\n\nGet device connection detail\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    GET /api/v1/controller/device/{id}/connection/{id}/\n\nChange device connection detail\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    PUT /api/v1/controller/device/{id}/connection/{id}/\n\nPatch device connection detail\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    PATCH /api/v1/controller/device/{id}/connection/{id}/\n\nDelete device connection\n^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    DELETE /api/v1/controller/device/{id}/connection/{id}/\n\nList credentials\n^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    GET /api/v1/connection/credential/\n\nCreate credential\n^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    POST /api/v1/connection/credential/\n\nGet credential detail\n^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    GET /api/v1/connection/credential/{id}/\n\nChange credential detail\n^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    PUT /api/v1/connection/credential/{id}/\n\nPatch credential detail\n^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    PATCH /api/v1/connection/credential/{id}/\n\nDelete credential\n^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    DELETE /api/v1/connection/credential/{id}/\n\nList commands of a device\n^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    GET /api/v1/controller/device/{id}/command/\n\nExecute a command a device\n^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    POST /api/v1/controller/device/{id}/command/\n\nGet command details\n^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    GET /api/v1/controller/device/{device_id}/command/{command_id}/\n\nGet device coordinates\n^^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    GET /api/v1/controller/device/{id}/location/\n\nUpdate device coordinates\n^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    PUT /api/v1/controller/device/{id}/location/\n\nList of devices in a location\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code:: text\n\n    GET /api/v1/controller/location/{id}/device/\n\nList locations with devices deployed (in GeoJSON format)\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code:: text\n\n    GET /api/v1/controller/location/geojson/\n\nYou can filter using ``organization_slug`` to list location of\ndevices from that organization\n\n.. code:: text\n\n    GET /api/v1/controller/location/geojson/?organization_slug=\u003corganization_slug\u003e\n\nList device groups\n^^^^^^^^^^^^^^^^^^\n\n.. code:: text\n\n    GET api/v1/controller/group/\n\nCreate device group\n^^^^^^^^^^^^^^^^^^^\n\n.. code:: text\n\n    POST api/v1/controller/group/\n\nGet device group detail\n^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    GET /api/v1/controller/group/{id}/\n\nGet device group from certificate common name\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    GET /api/v1/controller/cert/{common_name}/group/\n\nThis endpoint can be used to retrieve group information and metadata by the\ncommon name of a certificate used in a VPN client tunnel, this endpoint is\nused in layer 2 tunneling solutions for firewall/captive portals.\n\nIt is also possible to filter device group by providing organization slug\nof certificate's organization as show in the example below:\n\n.. code-block:: text\n\n    GET /api/v1/controller/cert/{common_name}/group/?org={org1_slug},{org2_slug}\n\nList templates\n^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    GET /api/v1/controller/template/\n\nCreate template\n^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    POST /api/v1/controller/template/\n\nGet template detail\n^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    GET /api/v1/controller/template/{id}/\n\nDownload template configuration\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    GET /api/v1/controller/template/{id}/configuration/\n\nThe above endpoint triggers the download of a ``tar.gz`` file\ncontaining the generated configuration for that specific template.\n\nChange details of template\n^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    PUT /api/v1/controller/template/{id}/\n\nPatch details of template\n^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    PATCH /api/v1/controller/template/{id}/\n\nDelete template\n^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    DELETE /api/v1/controller/template/{id}/\n\nList VPNs\n^^^^^^^^^\n\n.. code-block:: text\n\n    GET /api/v1/controller/vpn/\n\nCreate VPN\n^^^^^^^^^^\n\n.. code-block:: text\n\n    POST /api/v1/controller/vpn/\n\nGet VPN detail\n^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    GET /api/v1/controller/vpn/{id}/\n\nDownload VPN configuration\n^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    GET /api/v1/controller/vpn/{id}/configuration/\n\nThe above endpoint triggers the download of a ``tar.gz`` file\ncontaining the generated configuration for that specific VPN.\n\nChange details of VPN\n^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    PUT /api/v1/controller/vpn/{id}/\n\nPatch details of VPN\n^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    PATCH /api/v1/controller/vpn/{id}/\n\nDelete VPN\n^^^^^^^^^^\n\n.. code-block:: text\n\n    DELETE /api/v1/controller/vpn/{id}/\n\nList CA\n^^^^^^^\n\n.. code-block:: text\n\n    GET /api/v1/controller/ca/\n\nCreate new CA\n^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    POST /api/v1/controller/ca/\n\nImport existing CA\n^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    POST /api/v1/controller/ca/\n\n**Note**: To import an existing CA, only ``name``, ``certificate``\nand ``private_key`` fields have to be filled in the ``HTML`` form or\nincluded in the ``JSON`` format.\n\nGet CA Detail\n^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    GET /api/v1/controller/ca/{id}/\n\nChange details of CA\n^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    PUT /api/v1/controller/ca/{id}/\n\nPatch details of CA\n^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    PATCH /api/v1/controller/ca/{id}/\n\nDownload CA(crl)\n^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    GET /api/v1/controller/ca/{id}/crl/\n\nThe above endpoint triggers the download of ``{id}.crl`` file containing\nup to date CRL of that specific CA.\n\nDelete CA\n^^^^^^^^^\n\n.. code-block:: text\n\n    DELETE /api/v1/controller/ca/{id}/\n\nRenew CA\n^^^^^^^^\n\n.. code-block:: text\n\n    POST /api/v1/controller/ca/{id}/renew/\n\nList Cert\n^^^^^^^^^\n\n.. code-block:: text\n\n    GET /api/v1/controller/cert/\n\nCreate new Cert\n^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    POST /api/v1/controller/cert/\n\nImport existing Cert\n^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    POST /api/v1/controller/cert/\n\n**Note**: To import an existing Cert, only ``name``, ``ca``,\n``certificate`` and ``private_key`` fields have to be filled\nin the ``HTML`` form or included in the ``JSON`` format.\n\nGet Cert Detail\n^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    GET /api/v1/controller/cert/{id}/\n\nChange details of Cert\n^^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    PUT /api/v1/controller/cert/{id}/\n\nPatch details of Cert\n^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: text\n\n    PATCH /api/v1/controller/cert/{id}/\n\nDelete Cert\n^^^^^^^^^^^\n\n.. code-block:: text\n\n    DELETE /api/v1/controller/cert/{id}/\n\nRenew Cert\n^^^^^^^^^^\n\n.. code-block:: text\n\n    POST /api/v1/controller/cert/{id}/renew/\n\nRevoke Cert\n^^^^^^^^^^^\n\n.. code-block:: text\n\n    POST /api/v1/controller/cert/{id}/revoke/\n\nDefault Alerts / Notifications\n------------------------------\n\n+-----------------------+---------------------------------------------------------------------+\n| Notification Type     | Use                                                                 |\n+-----------------------+---------------------------------------------------------------------+\n| ``config_error``      | Fires when status of a device configuration changes to  ``error``.  |\n+-----------------------+---------------------------------------------------------------------+\n| ``device_registered`` | Fires when a new device is registered automatically on the network. |\n+-----------------------+---------------------------------------------------------------------+\n\nInstalling for development\n--------------------------\n\nInstall the system dependencies:\n\n.. code-block:: shell\n\n    sudo apt update\n    sudo apt install -y sqlite3 libsqlite3-dev openssl libssl-dev\n    sudo apt install -y gdal-bin libproj-dev libgeos-dev libspatialite-dev libsqlite3-mod-spatialite\n    sudo apt install -y chromium\n\nFork and clone the forked repository:\n\n.. code-block:: shell\n\n    git clone git://github.com/\u003cyour_fork\u003e/openwisp-controller\n\nNavigate into the cloned repository:\n\n.. code-block:: shell\n\n    cd openwisp-controller/\n\nLaunch Redis:\n\n.. code-block:: shell\n\n    docker-compose up -d redis\n\nSetup and activate a virtual-environment. (we'll be using  `virtualenv \u003chttps://pypi.org/project/virtualenv/\u003e`_)\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==20.2.4\" wheel setuptools\n\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 `\u003chttps://chromedriver.chromium.org/home\u003e`_\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\nLaunch celery worker (for background jobs):\n\n.. code-block:: shell\n\n    celery -A openwisp2 worker -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\nNOTE: This Docker image is for development purposes only.\nFor the official OpenWISP Docker images, see: `docker-openwisp\n\u003chttps://github.com/openwisp/docker-openwisp\u003e`_.\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\nTroubleshooting Steps\n---------------------\n\nYou may encounter some issues while installing GeoDjango.\n\nUnable to load SpatiaLite library extension?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIf you are getting below exception::\n\n   django.core.exceptions.ImproperlyConfigured: Unable to load the SpatiaLite library extension\n\nthen, You need to specify ``SPATIALITE_LIBRARY_PATH`` in your ``settings.py`` as explained in\n`django documentation regarding how to install and configure spatialte\n\u003chttps://docs.djangoproject.com/en/2.1/ref/contrib/gis/install/spatialite/\u003e`_.\n\nHaving Issues with other geospatial libraries?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nPlease refer\n`troubleshooting issues related to geospatial libraries\n\u003chttps://docs.djangoproject.com/en/2.1/ref/contrib/gis/install/#library-environment-settings/\u003e`_.\n\nDevice Groups\n-------------\n\nDevice Groups provide an easy way to organize devices of a particular organization.\nYou can achieve following by using Device Groups:\n\n- Group similar devices by having dedicated groups for access points, routers, etc.\n- Store additional information regarding a group in the structured metadata field.\n- Customize structure and validation of metadata field of DeviceGroup to standardize\n  information across all groups using `\"OPENWISP_CONTROLLER_DEVICE_GROUP_SCHEMA\" \u003c#openwisp-controller-device-group-schema\u003e`_\n  setting.\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/master/docs/device-groups.png\n  :alt: Device Group example\n\nHow to use configuration variables\n----------------------------------\n\nSometimes the configuration is not exactly equal on all the devices,\nsome parameters are unique to each device or need to be changed\nby the user.\n\nIn these cases it is possible to use configuration variables in conjunction\nwith templates, this feature is also known as *configuration context*, think of\nit like a dictionary which is passed to the function which renders the\nconfiguration, so that it can fill variables according to the passed context.\n\nThe different ways in which variables are defined are described below.\n\nPredefined device variables\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nEach device gets the following attributes passed as configuration variables:\n\n* ``id``\n* ``key``\n* ``name``\n* ``mac_address``\n\nUser defined device variables\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIn the device configuration section you can find a section named\n\"Configuration variables\" where it is possible to define the configuration\nvariables and their values, as shown in the example below:\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/master/docs/device-context.png\n   :alt: context\n\nTemplate default values\n~~~~~~~~~~~~~~~~~~~~~~~\n\nIt's possible to specify the default values of variables defined in a template.\n\nThis allows to achieve 2 goals:\n\n1. pass schema validation without errors (otherwise it would not be possible\n   to save the template in the first place)\n2. provide good default values that are valid in most cases but can be\n   overridden in the device if needed\n\nThese default values will be overridden by the\n`User defined device variables \u003c#user-defined-device-variables\u003e`_.\n\nThe default values of variables can be manipulated from the section\n\"configuration variables\" in the edit template page:\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/master/docs/template-default-values.png\n  :alt: default values\n\nGlobal variables\n~~~~~~~~~~~~~~~~\n\nVariables can also be defined globally using the\n`OPENWISP_CONTROLLER_CONTEXT \u003c#openwisp-controller-context\u003e`_ setting.\n\nSystem defined variables\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nPredefined device variables, global variables and other variables that\nare automatically managed by the system (eg: when using templates of\ntype VPN-client) are displayed in the admin UI as *System Defined Variables*\nin read-only mode.\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/master/docs/system-defined-variables.png\n   :alt: system defined variables\n\nExample usage of variables\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nHere's a typical use case, the WiFi SSID and WiFi password.\nYou don't want to define this for every device, but you may want to\nallow operators to easily change the SSID or WiFi password for a\nspecific device without having to re-define the whole wifi interface\nto avoid duplicating information.\n\nThis would be the template:\n\n.. code-block:: json\n\n    {\n        \"interfaces\": [\n            {\n                \"type\": \"wireless\",\n                \"name\": \"wlan0\",\n                \"wireless\": {\n                    \"mode\": \"access_point\",\n                    \"radio\": \"radio0\",\n                    \"ssid\": \"{{wlan0_ssid}}\",\n                    \"encryption\": {\n                        \"protocol\": \"wpa2_personal\",\n                        \"key\": \"{{wlan0_password}}\",\n                        \"cipher\": \"auto\"\n                    }\n                }\n            }\n        ]\n    }\n\nThese would be the default values in the template:\n\n.. code-block:: json\n\n    {\n        \"wlan0_ssid\": \"SnakeOil PublicWiFi\",\n        \"wlan0_password\": \"Snakeoil_pwd!321654\"\n    }\n\nThe default values can then be overridden at\n`device level \u003c#user-defined-device-variables\u003e`_ if needed, eg:\n\n.. code-block:: json\n\n    {\n        \"wlan0_ssid\": \"Room 23 ACME Hotel\",\n        \"wlan0_password\": \"room_23pwd!321654\"\n    }\n\nHow to configure push updates\n-----------------------------\n\nFollow the procedure described below to enable secure SSH access from OpenWISP to your\ndevices, this is required to enable push updates (whenever the configuration is changed,\nOpenWISP will trigger the update in the background) and/or\n`firmware upgrades (via the additional module openwisp-firmware-upgrader)\n\u003chttps://github.com/openwisp/openwisp-firmware-upgrader\u003e`_.\n\n**Note**: If you have installed OpenWISP with `openwisp2 Ansbile role \u003chttps://galaxy.ansible.com/openwisp/openwisp2\u003e`_\nthen you can skip the following steps. The Ansible role automatically creates a\ndefault template to update ``authorized_keys`` on networking devices using the\ndefault access credentials.\n\n1. Generate SSH key\n~~~~~~~~~~~~~~~~~~~\n\nFirst of all, we need to generate the SSH key which will be\nused by OpenWISP to access the devices, to do so, you can use the following command:\n\n.. code-block:: shell\n\n    echo './sshkey' | ssh-keygen -t rsa -b 4096 -C \"openwisp\"\n\nThis will create two files in the current directory, one called ``sshkey`` (the private key) and one called\n``sshkey.pub`` (the public key).\n\nStore the content of these files in a secure location.\n\n2. Save SSH private key in OpenWISP (access credentials)\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/master/docs/add-ssh-credentials-private-key.png\n  :alt: add SSH private key as access credential in OpenWISP\n\nFrom the first page of OpenWISP click on \"Access credentials\", then click\non the **\"ADD ACCESS CREDENTIALS\"** button in the upper right corner\n(alternatively, go to the following URL: ``/admin/connection/credentials/add/``).\n\nSelect SSH as ``type``, enable the **Auto add** checkbox, then at the field\n\"Credentials type\" select \"SSH (private key)\", now type \"root\" in the ``username`` field,\nwhile in the ``key`` field you have to paste the contents of the private key just created.\n\nNow hit save.\n\nThe credentials just created will be automatically enabled for all the devices in the system\n(both existing devices and devices which will be added in the future).\n\n3. Add the public key to your devices\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/master/docs/add-authorized-ssh-keys-template.png\n  :alt: Add authorized SSH public keys template to OpenWISP (OpenWRT)\n\nNow we need to instruct your devices to allow OpenWISP accessing via SSH,\nin order to do this we need to add the contents of the public key file created in step 1\n(``sshkey.pub``) in the file ``/etc/dropbear/authorized_keys`` on the devices, the\nrecommended way to do this is to create a configuration template in OpenWISP:\nfrom the first page of OpenWISP, click on \"Templates\", then and click on the\n**\"ADD TEMPLATE\"** button in the upper right corner (alternatively, go to the following URL:\n``/admin/config/template/add/``).\n\nCheck **enabled by default**, then scroll down the configuration section,\nclick on \"Configuration Menu\", scroll down, click on \"Files\" then close the menu\nby clicking again on \"Configuration Menu\". Now type ``/etc/dropbear/authorized_keys``\nin the ``path`` field of the file, then paste the contents of ``sshkey.pub`` in ``contents``.\n\nNow hit save.\n\n**There's a catch**: you will need to assign the template to any existing device.\n\n4. Test it\n~~~~~~~~~~\n\nOnce you have performed the 3 steps above, you can test it as follows:\n\n1. Ensure there's at least one device turned on and connected to OpenWISP, ensure\n   this device has the \"SSH Authorized Keys\" assigned to it.\n2. Ensure the celery worker of OpenWISP Controller is running (eg: ``ps aux | grep celery``)\n3. SSH into the device and wait (maximum 2 minutes) until ``/etc/dropbear/authorized_keys``\n   appears as specified in the template.\n4. While connected via SSH to the device run the following command in the console:\n   ``logread -f``, now try changing the device name in OpenWISP\n5. Shortly after you change the name in OpenWISP, you should see some output in the\n   SSH console indicating another SSH access and the configuration update being performed.\n\nSending Commands to Devices\n---------------------------\n\nBy default, there are three options in the **Send Command** dropdown:\n\n1. Reboot\n2. Change Password\n3. Custom Command\n\nWhile the first two options are self-explanatory, the **custom command** option\nallows you to execute any command on the device as shown in the example below.\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/commands_demo.gif\n   :target: https://github.com/openwisp/openwisp-controller/tree/docs/docs/commands_demo.gif\n   :alt: Executing commands on device example\n\n**Note**: in order for this feature to work, a device needs to have at least\none **Access Credential** (see `How to configure push updates \u003c#how-to-configure-push-updates\u003e`__).\n\nThe **Send Command** button will be hidden until the device\nhas at least one **Access Credential**.\n\nIf you need to allow your users to quickly send specific commands that are used often in your\nnetwork regardless of your users' knowledge of Linux shell commands, you can add new commands\nby following instructions in `\"How to add commands\" \u003c#how-to-add-commands\u003e`_ section.\n\nIf you are an advanced user and want to register commands programatically, then refer to\n`\"Register / Unregistering commands\" \u003c#registering--unregistering-commands\u003e`_ section.\n\nHow to add commands\n~~~~~~~~~~~~~~~~~~~\n\nThis example introduces a simple command that could ``ping`` an input\n``destination_address`` through an interface, ``interface_name``.\n\n.. code-block:: python\n\n    # In yourproject/settings.py\n\n    def ping_command_callable(destination_address, interface_name=None):\n        command = f'ping -c 4 {destination_address}'\n        if interface_name:\n            command += f' -I {interface_name}'\n        return command\n\n    OPENWISP_CONTROLLER_USER_COMMANDS = [\n        (\n            'ping',\n            {\n                'label': 'Ping',\n                'schema': {\n                    'title': 'Ping',\n                    'type': 'object',\n                    'required': ['destination_address'],\n                    'properties': {\n                        'destination_address': {\n                            'type': 'string',\n                            'title': 'Destination Address',\n                        },\n                        'interface_name': {\n                            'type': 'string',\n                            'title': 'Interface Name',\n                        },\n                    },\n                    'message': 'Destination Address cannot be empty',\n                    'additionalProperties': False,\n                },\n                'callable': ping_command_callable,\n            }\n        )\n    ]\n\nThe above code will add \"Ping\" command as show in the GIF below:\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/ping_command_example.gif\n   :target: https://github.com/openwisp/openwisp-controller/tree/docs/docs/ping_command_example.gif\n   :alt: Adding a \"ping\" command\n\n``OPENWISP_CONTROLLER_USER_COMMANDS`` setting takes a ``list`` of ``tuple``\neach containing two elements. The first element of the tuple should contain an\nidentifier for the command and the second element should contain a ``dict``\ndefining configuration of the command.\n\nCommand Configuration\n^^^^^^^^^^^^^^^^^^^^^\n\nThe ``dict`` defining configuration for command should contain following keys:\n\n1. ``label``\n\"\"\"\"\"\"\"\"\"\"\"\"\n\nA ``str`` defining label for the command used internally by Django.\n\n2. ``schema``\n\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nA ``dict`` defining `JSONSchema \u003chttps://json-schema.org/\u003e`_ for inputs of command.\nYou can specify the inputs for your command, add rules for performing validation\nand make inputs required or optional.\n\nHere is a detailed explanation of the schema used in above example:\n\n.. code-block:: python\n\n    {\n        # Name of the command displayed in \"Send Command\" widget\n        'title': 'Ping',\n        # Use type \"object\" if the command needs to accept inputs\n        # Use type \"null\" if the command does not accepts any input\n        'type': 'object',\n        # Specify list of inputs that are required\n        'required': ['destination_address'],\n        # Define the inputs for the commands along with their properties\n        'properties': {\n            'destination_address': {\n                # type of the input value\n                'type': 'string',\n                # label used for displaying this input field\n                'title': 'Destination Address',\n            },\n            'interface_name': {\n                'type': 'string',\n                'title': 'Interface Name',\n            },\n        },\n        # Error message to be shown if validation fails\n        'message': 'Destination Address cannot be empty'),\n        # Whether specifying addtionaly inputs is allowed from the input form\n        'additionalProperties': False,\n    }\n\nThis example uses only handful of properties available in JSONSchema. You can\nexperiment with other properties of JSONSchema for schema of your command.\n\n3. ``callable``\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nA ``callable`` or ``str`` defining dotted path to a callable. It should return\nthe command (``str``) to be executed on the device. Inputs of the command are\npassed as arguments to this callable.\n\nThe example above includes a callable(``ping_command_callable``) for\n``ping`` command.\n\nRegistering / Unregistering Commands\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nOpenWISP Controller provides registering and unregistering commands\nthrough utility functions ``openwisp_controller.connection.commands.register_command``\nand ``openwisp_notifications.types.unregister_notification_type``.\nUsing these functions you can register or unregister commands from your code.\n\n**Note**: These functions are to be used as an alternative to the\n`\"OPENWISP_CONTROLLER_USER_COMMANDS\" \u003c#openwisp-controller-user-commands\u003e`_ when\n`developing custom modules based on openwisp-controller \u003c#extending-openwisp-controller\u003e`_\n\n``register_command``\n^^^^^^^^^^^^^^^^^^^^\n\n+--------------------+------------------------------------------------------------------+\n| Parameter          | Description                                                      |\n+--------------------+------------------------------------------------------------------+\n| ``command_name``   | A ``str`` defining identifier for the command.                   |\n+--------------------+------------------------------------------------------------------+\n| ``command_config`` | A ``dict`` defining configuration of the command                 |\n|                    | as shown in `\"Command Configuration\" \u003c#command-configuration\u003e`_. |\n+--------------------+------------------------------------------------------------------+\n\n**Note:** It will raise ``ImproperlyConfigured`` exception if a command is already\nregistered with the same name.\n\n``unregister_command``\n^^^^^^^^^^^^^^^^^^^^^^\n\n+--------------------+-----------------------------------------+\n| Parameter          | Description                             |\n+--------------------+-----------------------------------------+\n| ``command_name``   | A ``str`` defining name of the command. |\n+--------------------+-----------------------------------------+\n\n**Note:** It will raise ``ImproperlyConfigured`` exception if such command does not exists.\n\nDefault Templates\n-----------------\n\nWhen templates are flagged as default, they will be automatically assigned to new devices.\n\nIf there are multiple default templates, these are assigned to the device in alphabetical\norder based on their names, for example, given the following default templates:\n\n- Access\n- Interfaces\n- SSH Keys\n\nThey will be assigned to devices in exactly that order.\n\nIf for some technical reason (eg: one default template depends on the presence of another\ndefault template which must be assigned earlier) you need to change the ordering, you can\nsimply rename the templates by prefixing them with numbers, eg:\n\n- 1 Interfaces\n- 2. SSH Keys\n- 3. Access\n\nRequired Templates\n------------------\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/master/docs/required-templates.png\n  :alt: Required template example\n\nRequired templates are similar to `Default templates \u003c#default-templates\u003e`__\nbut cannot be unassigned from a device configuration, they can only be overridden.\n\nThey will be always assigned earlier than default templates,\nso they can be overridden if needed.\n\nIn the example above, the \"SSID\" template is flagged as \"(required)\"\nand its checkbox is always checked and disabled.\n\nHow to configure automatic provisioning of subnets and IPs\n----------------------------------------------------------\n\nThe following steps will help you configure automatic provisioning of subnets and IPs\nfor devices:\n\n1. Create a Subnet and a Subnet Division Rule\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nCreate a master subnet under which automatically generated subnets will be provisioned.\n\n**Note**: Choose the size of the subnet appropriately considering your use case.\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/subnet-division-rule/subnet.png\n  :alt: Creating a master subnet example\n\nOn the same page, add a **subnet division rule** that will be used to provision subnets\nunder the master subnet.\n\nThe type of subnet division rule controls when subnets and IP addresses will be provisioned\nfor a device. The subnet division rule types currently implemented are described below.\n\nDevice Subnet Division Rule\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThis rule type is triggered whenever a device configuration (``config.Config`` model)\nis created for the organization specified in the rule.\n\nCreating a new rule of \"Device\" type will also provision subnets and\nIP addresses for existing devices of the organization automatically.\n\n**Note**: a device without a configuration will not trigger this rule.\n\nVPN Subnet Division Rule\n^^^^^^^^^^^^^^^^^^^^^^^^\n\nThis rule is triggered when a VPN client template is assigned to a device,\nprovided the VPN server to which the VPN client template relates to has\nthe same subnet for which the subnet division rule is created.\n\n**Note:** This rule will only work for **WireGuard** and **VXLAN over WireGuard**\nVPN servers.\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/subnet-division-rule/subnet-division-rule.png\n  :alt: Creating a subnet division rule example\n\nIn this example, **VPN subnet division rule** is used.\n\n2. Create a VPN Server\n~~~~~~~~~~~~~~~~~~~~~~\n\nNow create a VPN Server and choose the previously created **master subnet** as the subnet for\nthis VPN Server.\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/subnet-division-rule/vpn-server.png\n  :alt: Creating a VPN Server example\n\n3. Create a VPN Client Template\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nCreate a template, setting the **Type** field to **VPN Client** and **VPN** field to use the\npreviously created VPN Server.\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/subnet-division-rule/vpn-client.png\n  :alt: Creating a VPN Client template example\n\n**Note**: You can also check the **Enable by default** field if you want to automatically\napply this template to devices that will register in future.\n\n4. Apply VPN Client Template to Devices\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWith everything in place, you can now apply the VPN Client Template to devices.\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/subnet-division-rule/apply-template-to-device.png\n  :alt: Adding template to device example\n\nAfter saving the device, you should see all provisioned Subnets and IPs for this device\nunder the `System Defined Variables \u003c#system-defined-variables\u003e`_.\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/subnet-division-rule/system-defined-variables.png\n  :alt: Provisioned Subnets and IPs available as System Defined Variables example\n\nVoila! You can now use these variables in configuration of the device. Refer to `How to use configuration variables \u003c#how-to-use-configuration-variables\u003e`_\nsection of this documentation to learn how to use configuration variables.\n\nImportant notes for using Subnet Division\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- In the above example Subnet, VPN Server, and VPN Client Template belonged to the **default** organization.\n  You can use **Systemwide Shared** Subnet, VPN Server, or VPN Client Template too, but\n  Subnet Division Rule will be always related to an organization. The Subnet Division Rule will only be\n  triggered when such VPN Client Template will be applied to a Device having the same organization as Subnet Division Rule.\n\n- You can also use the configuration variables for provisioned subnets and IPs in the Template.\n  Each variable will be resolved differently for different devices. E.g. ``OW_subnet1_ip1`` will resolve to\n  ``10.0.0.1`` for one device and ``10.0.0.55`` for another. Every device gets its own set of subnets and IPs.\n  But don't forget to provide the default fall back values in the \"default values\" template field\n  (used mainly for validation).\n\n- The Subnet Division Rule will automatically create a reserved subnet, this subnet can be used\n  to provision any IP addresses that have to be created manually. The rest of the master subnet\n  address space **must not** be interfered with or the automation implemented in this module\n  will not work.\n\n- The above example used `VPN subnet division rule \u003c#vpn-subnet-division-rule\u003e`_. Similarly,\n  `device subnet division rule \u003c#device-subnet-division-rule\u003e`_ can be used, which only requires\n  `creating a subnet and a subnet division rule \u003c#1-create-a-subnet-and-a-subnet-division-rule\u003e`_.\n\nLimitations of Subnet Division\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nIn the current implementation, it is not possible to change \"Size\", \"Number of Subnets\" and\n\"Number of IPs\" fields of an existing subnet division rule due to following reasons:\n\nSize\n\"\"\"\"\n\nAllowing to change size of provisioned subnets of an existing subnet division rule\nwill require rebuilding of Subnets and IP addresses which has possibility of breaking\nexisting configurations.\n\nNumber of Subnets\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nAllowing to decrease number of subnets of an existing subnet division\nrule can create patches of unused subnets dispersed everywhere in the master subnet.\nAllowing to increase number of subnets will break the continuous allocation of subnets for\nevery device. It can also break configuration of devices.\n\nNumber of IPs\n\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nAllowing to decrease number of IPs of an existing subnet division rule\nwill lead to deletion of IP Addresses which can break configuration of devices being used.\nIt **is allowed** to increase number of IPs.\n\nIf you want to make changes to any of above fields, delete the existing rule and create a\nnew one. The automation will provision for all existing devices that meets the criteria\nfor provisioning. **WARNING**: It is possible that devices get different subnets and IPs\nfrom previous provisioning.\n\nHow to setup WireGuard tunnels\n------------------------------\n\nFollow the procedure described below to setup WireGuard tunnels on your devices.\n\n**Note:** This example uses **Shared systemwide (no organization)** option as\nthe organization for VPN server and VPN client template. You can use any\norganization as long as VPN server, VPN client template and Device has same\norganization.\n\n1. Create VPN server configuration for WireGuard\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n1. Visit ``/admin/config/vpn/add/`` to add a new VPN server.\n2. We will set **Name** of this VPN server ``Wireguard`` and **Host** as\n   ``wireguard-server.mydomain.com`` (update this to point to your\n   WireGuard VPN server).\n3. Select ``WireGuard`` from the dropdown as **VPN Backend**.\n4. When using WireGuard, OpenWISP takes care of managing IP addresses\n   (assigning an IP address to each VPN peer). You can create a new subnet or\n   select an existing one from the dropdown menu. You can also assign an\n   **Internal IP** to the WireGuard Server or leave it empty for OpenWISP to\n   configure. This IP address will be used by the WireGuard interface on\n   server.\n5. We have set the **Webhook Endpoint** as ``https://wireguard-server.mydomain.com:8081/trigger-update``\n   for this example. You will need to update this according to you VPN upgrader\n   endpoint. Set **Webhook AuthToken** to any strong passphrase, this will be\n   used to ensure that configuration upgrades are requested from trusted\n   sources.\n\n   **Note**: If you are following this tutorial for also setting up WireGuard\n   VPN server, just substitute ``wireguard-server.mydomain.com`` with hostname\n   of your VPN server and follow the steps in next section.\n\n6. Under the configuration section, set the name of WireGuard tunnel 1 interface.\n   We have used ``wg0`` in this example.\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-tutorial/vpn-server-1.png\n   :alt: WireGuard VPN server configuration example 1\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-tutorial/vpn-server-2.png\n   :alt: WireGuard VPN server configuration example 2\n\n7. After clicking on **Save and continue editing**, you will see that OpenWISP\n   has automatically created public and private key for WireGuard server in\n   **System Defined Variables** along with internal IP address information.\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-tutorial/vpn-server-3.png\n   :alt: WireGuard VPN server configuration example 3\n\n2. Deploy Wireguard VPN Server\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIf you haven't already setup WireGuard on your VPN server, this will be a good\ntime do so. We stress on using `ansible-wireguard-openwisp \u003chttps://github.com/openwisp/ansible-wireguard-openwisp\u003e`_\nrole for installing WireGuard since it also installs scripts that allows\nOpenWISP to manage WireGuard VPN server.\n\nPay attention to the VPN server attributes used in your playbook. It should be same as\nVPN server configuration in OpenWISP.\n\n3. Create VPN client template for WireGuard VPN Server\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n1. Visit ``/admin/config/template/add/`` to add a new template.\n2. Set ``Wireguard Client`` as **Name** (you can set whatever you want) and\n   select ``VPN-client`` as **type** from the dropdown list.\n3. The **Backend** field refers to the backend of the device this template can\n   be applied to. For this example, we will leave it to ``OpenWRT``.\n4. Select the correct VPN server from the dropdown for the **VPN** field. Here\n   it is ``Wireguard``.\n5. Ensure that **Automatic tunnel provisioning** is checked. This will make\n   OpenWISP to automatically generate public and private keys and provision IP\n   address for each WireGuard VPN client.\n6. After clicking on **Save and continue editing** button, you will see details\n   of *Wireguard* VPN server in **System Defined Variables**. The template\n   configuration will be automatically generated which you can tweak\n   accordingly. We will use the automatically generated VPN client configuration\n   for this example.\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-tutorial/template.png\n    :alt: WireGuard VPN client template example\n\n4. Apply Wireguard VPN template to devices\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n**Note**: This step assumes that you already have a device registered on\nOpenWISP. Register or create a device before proceeding.\n\n1. Open the **Configuration** tab of the concerned device.\n2. Select the *WireGuard Client* template.\n3. Upon clicking on **Save and continue editing** button, you will see some\n   entries in **System Defined Variables**. It will contain internal IP address,\n   private and public key for the WireGuard client on the device along with\n   details of WireGuard VPN server.\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-tutorial/device-configuration.png\n   :alt: WireGuard VPN device configuration example\n\n**Voila!** You have successfully configured OpenWISP to manage WireGuard\ntunnels for your devices.\n\nHow to setup VXLAN over WireGuard tunnels\n-----------------------------------------\n\nBy following these steps, you will be able to setup layer 2 VXLAN tunnels\nencapsulated in WireGuard tunnels which work on layer 3.\n\n**Note:** This example uses **Shared systemwide (no organization)** option as\nthe organization for VPN server and VPN client template. You can use any\norganization as long as VPN server, VPN client template and Device has same\norganization.\n\n1. Create VPN server configuration for VXLAN over WireGuard\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n1. Visit ``/admin/config/vpn/add/`` to add a new VPN server.\n2. We will set **Name** of this VPN server ``Wireguard VXLAN`` and **Host** as\n   ``wireguard-vxlan-server.mydomain.com`` (update this to point to your\n   WireGuard VXLAN VPN server).\n3. Select ``VXLAN over WireGuard`` from the dropdown as **VPN Backend**.\n4. When using VXLAN over WireGuard, OpenWISP takes care of managing IP addresses\n   (assigning an IP address to each VPN peer). You can create a new subnet or\n   select an existing one from the dropdown menu. You can also assign an\n   **Internal IP** to the WireGuard Server or leave it empty for OpenWISP to\n   configure. This IP address will be used by the WireGuard interface on\n   server.\n5. We have set the **Webhook Endpoint** as ``https://wireguard-vxlan-server.mydomain.com:8081/trigger-update``\n   for this example. You will need to update this according to you VPN upgrader\n   endpoint. Set **Webhook AuthToken** to any strong passphrase, this will be\n   used to ensure that configuration upgrades are requested from trusted\n   sources.\n\n   **Note**: If you are following this tutorial for also setting up WireGuard\n   VPN server, just substitute ``wireguard-server.mydomain.com`` with hostname\n   of your VPN server and follow the steps in next section.\n\n6. Under the configuration section, set the name of WireGuard tunnel 1 interface.\n   We have used ``wg0`` in this example.\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-vxlan-tutorial/vpn-server-1.png\n   :alt: WireGuard VPN VXLAN server configuration example 1\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-vxlan-tutorial/vpn-server-2.png\n   :alt: WireGuard VPN VXLAN server configuration example 2\n\n7. After clicking on **Save and continue editing**, you will see that OpenWISP\n   has automatically created public and private key for WireGuard server in\n   **System Defined Variables** along with internal IP address information.\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-vxlan-tutorial/vpn-server-3.png\n   :alt: WireGuard VXLAN VPN server configuration example 3\n\n2. Deploy Wireguard VXLAN VPN Server\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIf you haven't already setup WireGuard on your VPN server, this will be a good\ntime do so. We stress on using `ansible-wireguard-openwisp \u003chttps://github.com/openwisp/ansible-wireguard-openwisp\u003e`_\nrole for installing WireGuard since it also installs scripts that allows\nOpenWISP to manage WireGuard VPN server along with VXLAN tunnels.\n\nPay attention to the VPN server attributes used in your playbook. It should be same as\nVPN server configuration in OpenWISP.\n\n3. Create VPN client template for WireGuard VXLAN VPN Server\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n1. Visit ``/admin/config/template/add/`` to add a new template.\n2. Set ``Wireguard VXLAN Client`` as **Name** (you can set whatever you want) and\n   select ``VPN-client`` as **type** from the dropdown list.\n3. The **Backend** field refers to the backend of the device this template can\n   be applied to. For this example, we will leave it to ``OpenWRT``.\n4. Select the correct VPN server from the dropdown for the **VPN** field. Here\n   it is ``Wireguard VXLAN``.\n5. Ensure that **Automatic tunnel provisioning** is checked. This will make\n   OpenWISP to automatically generate public and private keys and provision IP\n   address for each WireGuard VPN client along with VXLAN Network Indentifier(VNI).\n6. After clicking on **Save and continue editing** button, you will see details\n   of *Wireguard VXLAN* VPN server in **System Defined Variables**. The template\n   configuration will be automatically generated which you can tweak\n   accordingly. We will use the automatically generated VPN client configuration\n   for this example.\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-vxlan-tutorial/template.png\n    :alt: WireGuard VXLAN VPN client template example\n\n4. Apply Wireguard VXLAN VPN template to devices\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n**Note**: This step assumes that you already have a device registered on\nOpenWISP. Register or create a device before proceeding.\n\n1. Open the **Configuration** tab of the concerned device.\n2. Select the *WireGuard VXLAN Client* template.\n3. Upon clicking on **Save and continue editing** button, you will see some\n   entries in **System Defined Variables**. It will contain internal IP address,\n   private and public key for the WireGuard client on the device and details of\n   WireGuard VPN server along with VXLAN Network Identifier(VNI) of this device.\n\n.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-vxlan-tutorial/device-configuration.png\n   :alt: WireGuard VXLAN VPN device configuration example\n\n**Voila!** You have successfully configured OpenWISP to manage VXLAN over\nWireGuard tunnels for your devices.\n\nSignals\n-------\n\n``config_modified``\n~~~~~~~~~~~~~~~~~~~\n\n**Path**: ``openwisp_controller.config.signals.config_modified``\n\n**Arguments**:\n\n- ``instance``: instance of ``Config`` which got its ``config`` modified\n- ``previous_status``: indicates the status of the config object before the\n  signal was emitted\n- ``action``: action which emitted the signal, can be any of the list below:\n  - ``config_changed``: the configuration of the config object was changed\n  - ``related_template_changed``: the configuration of a related template was changed\n  - ``m2m_templates_changed``: the assigned templates were changed\n  (either templates were added, removed or their order was changed)\n\nThis signal is emitted every time the configuration of a device is modified.\n\nIt does not matter if ``Config.status`` is already modified, this signal will\nbe emitted anyway because it signals that the device configuration has changed.\n\nThis signal is used to trigger the update of the configuration on devices,\nwhen the push feature is enabled (requires Device credentials).\n\nThe signal is also emitted when one of the templates used by the device\nis modified or if the templates assigned to the device are changed.\n\nSpecial cases in which ``config_modified`` is not emitted\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThis signal is not emitted when the device is created for the first time.\n\nIt is also not emitted when templates assigned to a config object are\ncleared (``post_clear`` m2m signal), this is necessary because\n`sortedm2m \u003chttps://github.com/jazzband/django-sortedm2m\u003e`_, the package\nwe use to implement ordered templates, uses the clear action to\nreorder templates (m2m relationships are first cleared and then added back),\ntherefore we ignore ``post_clear`` to avoid emitting signals twice\n(one for the clear action and one for the add action).\nPlease keep this in mind if you plan on using the clear method\nof the m2m manager.\n\n``config_status_changed``\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n**Path**: ``openwisp_controller.config.signals.config_status_changed``\n\n**Arguments**:\n\n- ``instance``: instance of ``Config`` which got its ``status`` changed\n\nThis signal is emitted only when the configuration status of a device has changed.\n\nThe signal is emitted also when the m2m template relationships of a config\nobject are changed, but only on ``post_add`` or ``post_remove`` actions,\n``post_clear`` is ignored for the same reason explained\nin the previous section.\n\n``checksum_requested``\n~~~~~~~~~~~~~~~~~~~~~~\n\n**Path**: ``openwisp_controller.config.signals.checksum_requested``\n\n**Arguments**:\n\n- ``instance``: instance of ``Device`` for which its configuration\n  checksum has been requested\n- ``request``: the HTTP request object\n\nThis signal is emitted when a device requests a checksum via the controller views.\n\nThe signal is emitted just before a successful response is returned,\nit is not sent if the response was not successful.\n\n``config_download_requested``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n**Path**: ``openwisp_controller.config.signals.config_download_requested``\n\n**Arguments**:\n\n- ``instance``: instance of ``Device`` for which its configuration has been\n  requested for download\n- ``request``: the HTTP request object\n\nThis signal is emitted when a device requests to download its configuration\nvia the controller views.\n\nThe signal is emitted just before a successful response is returned,\nit is not sent if the response was not successful.\n\n``is_working_changed``\n~~~~~~~~~~~~~~~~~~~~~~\n\n**Path**: ``openwisp_controller.connection.signals.is_working_changed``\n\n**Arguments**:\n\n- ``instance``: instance of ``DeviceConnection``\n- ``is_working``: value of ``DeviceConnection.is_working``\n- ``old_is_working``: previous value of ``DeviceConnection.is_working``,\n  either ``None`` (for new connections), ``True`` or ``False``\n- ``failure_reason``: error message explaining reason for failure in establishing connection\n\nThis signal is emitted every time ``DeviceConnection.is_working`` changes.\n\nIt is not triggered when the device is created for the first time.\n\n``management_ip_changed``\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\n**Path**: ``openwisp_controller.config.signals.management_ip_changed``\n\n**Arguments**:\n\n- ``instance``: instance of ``Device``\n- ``management_ip``: value of ``Device.management_ip``\n- ``old_management_ip``: previous value of ``Device.management_ip``\n\nThis signal is emitted every time ``Device.management_ip`` changes.\n\nIt is not triggered when the device is created for the first time.\n\n``device_registered``\n~~~~~~~~~~~~~~~~~~~~~\n\n**Path**: ``openwisp_controller.config.signals.device_registered``\n\n**Arguments**:\n\n- ``instance``: instance of ``Device`` which got registered.\n- ``is_new``: boolean, will be ``True`` when the device is new,\n  ``False`` when the device already exists\n  (eg: a device which gets a factory reset will register again)\n\nThis signal is emitted when a device registers automatically through the controller\nHTTP API.\n\n``device_name_changed``\n~~~~~~~~~~~~~~~~~~~~~~~\n\n**Path**: ``openwisp_controller.config.signals.device_name_changed``\n\n**Arguments**:\n\n- ``instance``: instance of ``Device``.\n\nThe signal is emitted when the device name changes.\n\nIt is not emitted when the device is created.\n\n``device_group_changed``\n~~~~~~~~~~~~~~~~~~~~~~~~\n\n**Path**: ``openwisp_controller.config.signals.device_group_changed``\n\n**Arguments**:\n\n- ``instance``: instance of ``Device``.\n- ``group_id``: primary key of ``DeviceGroup`` of ``Device``\n- ``old_group_id``: primary key of previous ``DeviceGroup`` of ``Device``\n\nThe signal is emitted when the device group changes.\n\nIt is not emitted when the device is created.\n\n``subnet_provisioned``\n~~~~~~~~~~~~~~~~~~~~~~\n\n**Path**: ``openwisp_controller.subnet_division.signals.subnet_provisioned``\n\n**Arguments**:\n\n- ``instance``: instance of ``VpnClient``.\n- ``provisioned``: dictionary of ``Subnet`` and ``IpAddress`` provisioned,\n  ``None`` if nothing is provisioned\n\nThe signal is emitted when subnets and IP addresses have been provisioned\nfor a ``VpnClient`` for a VPN server with a subnet with\n`subnet division rule \u003c#subnet-division-app\u003e`_.\n\n``vpn_peers_changed``\n~~~~~~~~~~~~~~~~~~~~~\n\n**Path**: ``openwisp_controller.config.signals.vpn_peers_changed``\n\n**Arguments**:\n\n- ``instance``: instance of ``Vpn``.\n\nThe signal is emitted when the peers of VPN server gets changed.\n\nIt is only emitted for ``Vpn`` object with **WireGuard** or\n**VXLAN over WireGuard** backend.\n\nSetup (integrate in an existing django project)\n-----------------------------------------------\n\nAdd ``openwisp_controller`` applications to ``INSTALLED_APPS``:\n\n.. code-block:: python\n\n    INSTALLED_APPS = [\n        ...\n        # openwisp2 modules\n        'openwisp_controller.config',\n        'openwisp_controller.pki',\n        'openwisp_controller.geo',\n        'openwisp_controller.connection',\n        'openwisp_controller.subnet_division', # Optional\n        'openwisp_controller.notifications',\n        'openwisp_users',\n        'openwisp_notifications',\n        'openwisp_ipam',\n        # openwisp2 admin theme\n        # (must be loaded here)\n        'openwisp_utils.admin_theme',\n        'django.contrib.admin',\n        'django.forms',\n        ...\n    ]\n    EXTENDED_APPS = ('django_x509', 'django_loci')\n\n**Note**: The order of applications in ``INSTALLED_APPS`` should be maintained,\notherwise it might not work properly.\n\nOther settings needed in ``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\n    ASGI_APPLICATION = 'openwisp_controller.geo.channels.routing.channel_routing'\n    CHANNEL_LAYERS = {\n        # in production you should use another channel layer backend\n        'default': {'BACKEND': 'channels.layers.InMemoryChannelLayer'},\n    }\n\n    TEMPLATES = [\n        {\n            'BACKEND': 'django.template.backends.django.DjangoTemplates',\n            'DIRS': [],\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                    'openwisp_utils.admin_theme.context_processor.menu_items',\n                    'openwisp_notifications.context_processors.notification_api_settings',\n                ],\n            },\n        }\n    ]\n\n    FORM_RENDERER = 'django.forms.renderers.TemplatesSetting'\n\nAdd the URLs to your main ``urls.py``:\n\n.. code-block:: python\n\n    urlpatterns = [\n        # ... other urls in your project ...\n        # openwisp-controller urls\n        url(r'^admin/', admin.site.urls),\n        url(r'', include('openwisp_controller.urls')),\n        url(r'', include('openwisp_notifications.urls')),\n        url(r'', include('openwisp_ipam.urls')),\n    ]\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\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 required python packages::\n\n    pip install redis django-redis\n\nThen run:\n\n.. code-block:: shell\n\n    ./manage.py migrate\n\nExtending openwisp-controller\n-----------------------------\n\nOne of the core values of the OpenWISP project is\n`Software Reusability \u003chttp://openwisp.io/docs/general/values.html#software-reusability-means-long-term-sustainability\u003e`_,\nfor this reason *openwisp-controller* 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-controller*,\nyou need to perform the steps described in this section.\n\nWhen in doubt, the code in the\n`test project \u003chttps://github.com/openwisp/openwisp-controller/tree/master/tests/openwisp2/\u003e`_\nwill serve you as source of truth: just replicate and adapt that code\nto get a basic derivative of *openwisp-controller* working.\n\nIf you want to add new users fields, please follow the `tutorial to extend the\nopenwisp-users \u003chttps://github.com/openwisp/openwisp-users/#extend-openwisp-users\u003e`_.\nAs an example, we have extended *openwisp-users* to *sample_users* app and\nadded a field ``social_security_number`` in the `sample_users/models.py\n\u003chttps://github.com/openwisp/openwisp-controller/blob/master/tests/openwisp2/sample_users/models.py\u003e`_.\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 project \u0026 custom apps\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFirstly, to get started you need to create a django project::\n\n    django-admin startproject mycontroller\n\nNow, you need to do is to create some new django apps which will\ncontain your custom version of *openwisp-controller*.\n\nA django project is a collection of django apps. There are 4 django apps in the\nopenwisp_controller project, namely config, pki, connection \u0026 geo.\nYou'll need to create 4 apps in your project for each app in openwisp_controller.\n\nA django app is nothing more than a\n`python package \u003chttps://docs.python.org/3/tutorial/modules.html#packages\u003e`_\n(a directory of python scripts), in the following examples we'll call these django app\n``sample_config``, ``sample_pki``, ``sample_connection``, ``sample_geo``\n\u0026 ``sample_subnet_division``. but you can name it how you want::\n\n    django-admin startapp sample_config\n    django-admin startapp sample_pki\n    django-admin startapp sample_connection\n    django-admin startapp sample_geo\n    django-admin startapp sample_subnet_division\n\nKeep in mind that the command mentioned above must be called from a directory\nwhich is available in your `PYTHON_PATH \u003chttps://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH\u003e`_\nso that you can then import the result into your project.\n\nFor more information about how to work with django projects and django apps,\nplease refer to the `django documentation \u003chttps://docs.djangoproject.com/en/dev/intro/tutorial01/\u003e`_.\n\n2. Install ``openwisp-controller``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nInstall (and add to the requirement of your project) openwisp-controller::\n\n    pip install openwisp-controller\n\n3. Add your apps in INSTALLED_APPS\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nNow you need to add ``mycontroller.sample_config``,\n``mycontroller.sample_pki``, ``mycontroller.sample_connection``,\n``mycontroller.sample_geo`` \u0026 ``mycontroller.sample_subnet_division`` to\n``INSTALLED_APPS`` in your ``settings.py``, ensuring also that\n``openwisp_controller.config``, ``openwisp_controller.geo``,\n``openwisp_controller.pki``, ``openwisp_controller.connnection`` \u0026\n``openwisp_controller.subnet_division`` have been removed:\n\n.. code-block:: python\n\n    # Remember: Order in INSTALLED_APPS is important.\n    INSTALLED_APPS = [\n        # other django installed apps\n        'openwisp_utils.admin_theme',\n        # all-auth\n        'django.contrib.sites',\n        'allauth',\n        'allauth.account',\n        'allauth.socialaccount',\n        # openwisp2 module\n        # 'openwisp_controller.config', \u003c-- comment out or delete this line\n        # 'openwisp_controller.pki', \u003c-- comment out or delete this line\n        # 'openwisp_controller.geo', \u003c-- comment out or delete this line\n        # 'openwisp_controller.connection', \u003c-- comment out or delete this line\n        # 'openwisp_controller.subnet_division', \u003c-- comment out or delete this line\n        'mycontroller.sample_config',\n        'mycontroller.sample_pki',\n        'mycontroller.sample_geo',\n        'mycontroller.sample_connection',\n        'mycontroller.sample_subnet_division',\n        'openwisp_users',\n        # admin\n        'django.contrib.admin',\n        # other dependencies\n        'sortedm2m',\n        'reversion',\n        'leaflet',\n        # rest framework\n        'rest_framework',\n        'rest_framework_gis',\n        # channels\n        'channels',\n    ]\n\nSubstitute ``mycontroller``, ``sample_config``, ``sample_pki``, ``sample_connection``,\n``sample_geo`` \u0026 ``sample_subnet_division`` with the name you chose in step 1.\n\n4. Add ``EXTENDED_APPS``\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nAdd the following to your ``settings.py``:\n\n.. code-block:: python\n\n    EXTENDED_APPS = (\n        'django_x509',\n        'django_loci',\n        'openwisp_controller.config',\n        'openwisp_controller.pki',\n        'openwisp_controller.geo',\n        'openwisp_controller.connection',\n        'openwisp_controller.subnet_division',\n    )\n\n5. 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\n6. Add ``openwisp_utils.loaders.DependencyLoader``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nAdd ``openwisp_utils.loaders.DependencyLoader`` to ``TEMPLATES``\nin your ``settings.py``, but ensure it comes before\n``django.template.loaders.app_directories.Loader``:\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                    'openwisp_utils.loaders.DependencyLoader',\n                    'django.template.loaders.app_directories.Loader',\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                    'openwisp_utils.admin_theme.context_processor.menu_items',\n                    'openwisp_notifications.context_processors.notification_api_settings',\n                ],\n            },\n        }\n    ]\n\n5. Initial Database setup\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\nEnsure you are using one of the available geodjango backends, eg:\n\n.. code-block:: python\n\n    DATABASES = {\n        'default': {\n            'ENGINE': 'django.contrib.gis.db.backends.spatialite',\n            'NAME': 'openwisp-controller.db',\n        }\n    }\n\nFor more information about GeoDjango, please refer to the `geodjango documentation \u003chttps://docs.djangoproject.com/en/dev/ref/contrib/gis/\u003e`_.\n\n6. Django Channels Setup\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nCreate ``asgi.py`` in your project folder and add following lines in it:\n\n.. code-block:: python\n\n    from channels.auth import AuthMiddlewareStack\n    from channels.routing import ProtocolTypeRouter, URLRouter\n    from channels.security.websocket import AllowedHostsOriginValidator\n    from django.core.asgi import get_asgi_application\n\n    from openwisp_controller.routing import get_routes\n    # You can also add your routes like this\n    from my_app.routing import my_routes\n\n    applic","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmanukumarnm%2Fopenwisp-controller","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmanukumarnm%2Fopenwisp-controller","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmanukumarnm%2Fopenwisp-controller/lists"}