From 39ca54d6dd7f02c1f583ecd74ea133d1efcc7fbb Mon Sep 17 00:00:00 2001 From: Federico Capoano Date: Sat, 3 Aug 2024 13:39:13 -0400 Subject: [PATCH] [docs] Restructured Documentation Restructured the documentation in a way that allows it to be included in the Unified Documentation of OpenWISP. For more information see https://github.com/openwisp/openwisp-docs/issues/107. --- README.rst | 4533 +---------------- docs/developer/extending.rst | 873 ++++ docs/developer/index.rst | 16 + docs/developer/installation.rst | 203 + docs/developer/utils.rst | 332 ++ .../architecture-v2-openwisp-controller.png | Bin 0 -> 397425 bytes docs/index.rst | 57 + docs/partials/developer-docs.rst | 12 + docs/partials/shared-object.rst | 8 + docs/user/device-groups.rst | 91 + docs/user/import-export.rst | 35 + docs/user/intro.rst | 117 + docs/user/openvpn.rst | 223 + docs/user/organization-limits.rst | 17 + docs/user/push-operations.rst | 117 + docs/user/rest-api.rst | 1152 +++++ docs/user/settings.rst | 733 +++ docs/user/shell-commands.rst | 177 + docs/user/subnet-division-rules.rst | 188 + docs/user/templates.rst | 212 + docs/user/variables.rst | 171 + docs/user/vxlan-wireguard.rst | 125 + docs/user/wireguard.rst | 123 + docs/user/zerotier.rst | 156 + pyproject.toml | 2 +- 25 files changed, 5160 insertions(+), 4513 deletions(-) create mode 100644 docs/developer/extending.rst create mode 100644 docs/developer/index.rst create mode 100644 docs/developer/installation.rst create mode 100644 docs/developer/utils.rst create mode 100644 docs/images/architecture-v2-openwisp-controller.png create mode 100644 docs/index.rst create mode 100644 docs/partials/developer-docs.rst create mode 100644 docs/partials/shared-object.rst create mode 100644 docs/user/device-groups.rst create mode 100644 docs/user/import-export.rst create mode 100644 docs/user/intro.rst create mode 100644 docs/user/openvpn.rst create mode 100644 docs/user/organization-limits.rst create mode 100644 docs/user/push-operations.rst create mode 100644 docs/user/rest-api.rst create mode 100644 docs/user/settings.rst create mode 100644 docs/user/shell-commands.rst create mode 100644 docs/user/subnet-division-rules.rst create mode 100644 docs/user/templates.rst create mode 100644 docs/user/variables.rst create mode 100644 docs/user/vxlan-wireguard.rst create mode 100644 docs/user/wireguard.rst create mode 100644 docs/user/zerotier.rst diff --git a/README.rst b/README.rst index 3fd73b9a3..c353faf2a 100644 --- a/README.rst +++ b/README.rst @@ -49,4532 +49,41 @@ built on top of its building blocks. Other popular building blocks that are part of the OpenWISP ecosystem are: -- `openwisp-monitoring - `_: provides device - status monitoring, collection of metrics, charts, alerts, possibility to - define custom checks +- `openwisp-monitoring `_: + provides device status monitoring, collection of metrics, charts, + alerts, possibility to define custom checks - `openwisp-firmware-upgrader - `_: automated - firmware upgrades (single devices or mass network upgrades) -- `openwisp-radius `_: based - on FreeRADIUS, allows to implement network access authentication systems - like 802.1x WPA2 Enterprise, captive portal authentication, Hotspot 2.0 - (802.11u) + `_: automated firmware + upgrades (single devices or mass network upgrades) +- `openwisp-radius `_: + based on FreeRADIUS, allows to implement network access authentication + systems like 802.1x WPA2 Enterprise, captive portal authentication, + Hotspot 2.0 (802.11u) - `openwisp-network-topology - `_: provides way - to collect and visualize network topology data from dynamic mesh routing + `_: provides way to + collect and visualize network topology data from dynamic mesh routing daemons or other network software (eg: OpenVPN); it can be used in conjunction with openwisp-monitoring to get a better idea of the state of the network -- `openwisp-ipam `_: allows to - manage the assignment of IP addresses used in the network -- `openwisp-notifications - `_: allows users to - be aware of important events happening in the network. +- `openwisp-ipam `_: allows to manage + the assignment of IP addresses used in the network +- `openwisp-notifications `_: + allows users to be aware of important events happening in the network. **For a more complete overview of the OpenWISP modules and architecture**, see the `OpenWISP Architecture Overview -`_. +`_. .. image:: https://raw.githubusercontent.com/openwisp/openwisp2-docs/master/assets/design/openwisp-logo-black.svg :target: http://openwisp.org :alt: OpenWISP -**Want to help OpenWISP?** `Find out how to help us grow here -`_. +Documentation +------------- ----- - -.. contents:: **Table of Contents**: - :backlinks: none - :depth: 3 - ----- - -Project Structure & main features ---------------------------------- - -OpenWISP Controller is a python package consisting of four django apps: - -Config App -~~~~~~~~~~ - -- **configuration management** for embedded devices supporting different firmwares: - - `OpenWRT `_ - - `OpenWISP Firmware - `_ - - support for additional firmware can be added by `specifying custom - backends <#netjsonconfig-backends>`_ -- **configuration editor** based on `JSON-Schema editor - `_ -- **advanced edit mode**: edit `NetJSON `_ - *DeviceConfiguration* objects for maximum flexibility -- `configuration templates - `_: reduce repetition to - the minimum, configure default and required templates -- `configuration variables <#how-to-use-configuration-variables>`_: - reference ansible-like variables in the configuration and templates -- **template tags**: tag templates to automate different types of - auto-configurations (eg: mesh, WDS, 4G) -- **device groups**: add `devices to dedicated groups <#device-groups>`_ - to ease management of group of devices -- **simple HTTP resources**: allow devices to automatically download - configuration updates -- **VPN management**: `automatically provision VPN tunnels - <#openwisp-controller-default-auto-cert>`_, including cryptographic - keys, IP addresses -- `REST API <#rest-api-reference>`_ -- `Export/Import devices <#exportimport-device-data>`_ - -PKI App -~~~~~~~ - -The PKI app is based on `django-x509 -`_, it allows to create, import -and view x509 CAs and certificates directly from the administration -dashboard, it also adds different endpoints to the `REST API -<#rest-api-reference>`_. - -Connection App -~~~~~~~~~~~~~~ - -This app allows OpenWISP Controller to use different protocols to reach -network devices. Currently, the default connnection protocols are SSH and -SNMP, but the protocol mechanism is extensible and more protocols can be -implemented if needed. - -SSH -+++ - -The SSH connector allows the controller to initialize connections to the -devices in order perform `push operations -<#how-to-configure-push-updates>`__: - -- Sending configuration updates. -- `Executing shell commands <#sending-commands-to-devices>`_. -- Perform `firmware upgrades via the additional firmware upgrade module - `_. -- `REST API <#rest-api-reference>`_ - -The default connection protocol implemented is SSH, but other protocol -mechanism is extensible and custom protocols can be implemented as well. - -Access via SSH key is recommended, the SSH key algorithms supported are: - -- RSA -- Ed25519 - -SNMP -++++ - -The SNMP connector is useful to collect monitoring information and it's -used in openwisp-monitoring_ for performing checks to collect monitoring -information. `Read more -`_ -on how to use it. - -Geo App -~~~~~~~ - -The geographic app is based on `django-loci -`_ and allows to define the -geographic coordinates of the devices, as well as their indoor coordinates -on floorplan images. - -It also adds different endpoints to the `REST API <#rest-api-reference>`_. - -Subnet Division App -~~~~~~~~~~~~~~~~~~~ - -This app allows to automatically provision subnets and IP addresses which -will be available as `system defined configuration variables -<#system-defined-variables>`_ that can be used in templates. The purpose -of this app is to allow users to automatically provision and configure -specific subnets and IP addresses to the devices without the need of -manual intervention. - -Refer to `"How to configure automatic provisioning of subnets and IPs" -section of this documentation -<#how-to-configure-automatic-provisioning-of-subnets-and-ips>`_ to learn -about features provided by this app. - -This app is optional, if you don't need it you can avoid adding it to -``settings.INSTALLED_APPS``. - -Installation instructions -------------------------- - -Deploy it in production -~~~~~~~~~~~~~~~~~~~~~~~ - -See: - -- `ansible-openwisp2 `_ -- `docker-openwisp `_ - -Dependencies -~~~~~~~~~~~~ - -- Python >= 3.7 -- OpenSSL - -Install stable version from pypi -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Install from pypi: - -.. code-block:: shell - - pip install openwisp-controller - -Install development version -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Install tarball: - -.. code-block:: shell - - pip install https://github.com/openwisp/openwisp-controller/tarball/master - -Alternatively you can install via pip using git: - -.. code-block:: shell - - pip install -e git+git://github.com/openwisp/openwisp-controller#egg=openwisp_controller - -If you want to contribute, follow the instructions in `Installing for -development <#installing-for-development>`_. - -Installing for development -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Install the system dependencies: - -.. code-block:: shell - - sudo apt update - sudo apt install -y sqlite3 libsqlite3-dev openssl libssl-dev - sudo apt install -y gdal-bin libproj-dev libgeos-dev libspatialite-dev libsqlite3-mod-spatialite - sudo apt install -y chromium - -Fork and clone the forked repository: - -.. code-block:: shell - - git clone git://github.com//openwisp-controller - -Navigate into the cloned repository: - -.. code-block:: shell - - cd openwisp-controller/ - -Launch Redis and PostgreSQL: - -.. code-block:: shell - - docker-compose up -d redis postgres - -Setup and activate a virtual-environment. (we'll be using `virtualenv -`_) - -.. code-block:: shell - - python -m virtualenv env - source env/bin/activate - -Make sure that you are using pip version 20.2.4 before moving to the next -step: - -.. code-block:: shell - - pip install -U pip wheel setuptools - -Install development dependencies: - -.. code-block:: shell - - pip install -e . - pip install -r requirements-test.txt - npm install -g jshint stylelint - -Install WebDriver for Chromium for your browser version from -https://chromedriver.chromium.org/home and Extract ``chromedriver`` to one -of directories from your ``$PATH`` (example: ``~/.local/bin/``). - -Create database: - -.. code-block:: shell - - cd tests/ - ./manage.py migrate - ./manage.py createsuperuser - -Launch celery worker (for background jobs): - -.. code-block:: shell - - celery -A openwisp2 worker -l info - -Launch development server: - -.. code-block:: shell - - ./manage.py runserver 0.0.0.0:8000 - -You can access the admin interface at http://127.0.0.1:8000/admin/. - -Run tests with: - -.. code-block:: shell - - ./runtests.py --parallel - # To run database tests against PostgreSQL backend - POSTGRESQL=1 ./runtests.py --parallel - -Run quality assurance tests with: - -.. code-block:: shell - - ./run-qa-checks - -Install and run on docker -~~~~~~~~~~~~~~~~~~~~~~~~~ - -NOTE: This Docker image is for development purposes only. For the official -OpenWISP Docker images, see: `docker-openwisp -`_. - -Build from the Dockerfile: - -.. code-block:: shell - - docker-compose build - -Run the docker container: - -.. code-block:: shell - - docker-compose up - -Troubleshooting steps for common installation issues -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You may encounter some issues while installing GeoDjango. - -Unable to load SpatiaLite library extension? -++++++++++++++++++++++++++++++++++++++++++++ - -If you are getting below exception: - -:: - - django.core.exceptions.ImproperlyConfigured: Unable to load the SpatiaLite library extension - -then, You need to specify ``SPATIALITE_LIBRARY_PATH`` in your -``settings.py`` as explained in `django documentation regarding how to -install and configure spatialte -`_. - -Having Issues with other geospatial libraries? -++++++++++++++++++++++++++++++++++++++++++++++ - -Please refer `troubleshooting issues related to geospatial libraries -`_. - -Setup (integrate in an existing django project) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Add ``openwisp_controller`` applications to ``INSTALLED_APPS``: - -.. code-block:: python - - INSTALLED_APPS = [ - ... - # openwisp2 modules - 'openwisp_controller.config', - 'openwisp_controller.pki', - 'openwisp_controller.geo', - 'openwisp_controller.connection', - 'openwisp_controller.subnet_division', # Optional - 'openwisp_controller.notifications', - 'openwisp_users', - 'openwisp_notifications', - 'openwisp_ipam', - # openwisp2 admin theme - # (must be loaded here) - 'openwisp_utils.admin_theme', - 'admin_auto_filters', - 'django.contrib.admin', - 'django.forms', - 'import_export', - ... - ] - EXTENDED_APPS = ('django_x509', 'django_loci') - -**Note**: The order of applications in ``INSTALLED_APPS`` should be -maintained, otherwise it might not work properly. - -Other settings needed in ``settings.py``: - -.. code-block:: python - - STATICFILES_FINDERS = [ - "django.contrib.staticfiles.finders.FileSystemFinder", - "django.contrib.staticfiles.finders.AppDirectoriesFinder", - "openwisp_utils.staticfiles.DependencyFinder", - ] - - ASGI_APPLICATION = ( - "openwisp_controller.geo.channels.routing.channel_routing" - ) - CHANNEL_LAYERS = { - # in production you should use another channel layer backend - "default": {"BACKEND": "channels.layers.InMemoryChannelLayer"}, - } - - TEMPLATES = [ - { - "BACKEND": "django.template.backends.django.DjangoTemplates", - "DIRS": [], - "OPTIONS": { - "loaders": [ - "django.template.loaders.filesystem.Loader", - "django.template.loaders.app_directories.Loader", - "openwisp_utils.loaders.DependencyLoader", - ], - "context_processors": [ - "django.template.context_processors.debug", - "django.template.context_processors.request", - "django.contrib.auth.context_processors.auth", - "django.contrib.messages.context_processors.messages", - "openwisp_utils.admin_theme.context_processor.menu_items", - "openwisp_notifications.context_processors.notification_api_settings", - ], - }, - } - ] - - FORM_RENDERER = "django.forms.renderers.TemplatesSetting" - -Add the URLs to your main ``urls.py``: - -.. code-block:: python - - urlpatterns = [ - # ... other urls in your project ... - # openwisp-controller urls - url(r"^admin/", admin.site.urls), - url(r"", include("openwisp_controller.urls")), - ] - -Configure caching (you may use a different cache storage if you want): - -.. code-block:: python - - CACHES = { - "default": { - "BACKEND": "django_redis.cache.RedisCache", - "LOCATION": "redis://localhost/0", - "OPTIONS": { - "CLIENT_CLASS": "django_redis.client.DefaultClient", - }, - } - } - - SESSION_ENGINE = "django.contrib.sessions.backends.cache" - SESSION_CACHE_ALIAS = "default" - -Configure celery (you may use a different broker if you want): - -.. code-block:: python - - # here we show how to configure celery with redis but you can - # use other brokers if you want, consult the celery docs - CELERY_BROKER_URL = "redis://localhost/1" - - INSTALLED_APPS.append("djcelery_email") - EMAIL_BACKEND = "djcelery_email.backends.CeleryEmailBackend" - -If you decide to use redis (as shown in these examples), install the -required python packages: - -:: - - pip install redis django-redis - -Then run: - -.. code-block:: shell - - ./manage.py migrate - -Usage reference ---------------- - -Default Templates -~~~~~~~~~~~~~~~~~ - -When templates are flagged as default, they will be automatically assigned -to new devices. - -If there are multiple default templates, these are assigned to the device -in alphabetical order based on their names, for example, given the -following default templates: - -- Access -- Interfaces -- SSH Keys - -They will be assigned to devices in exactly that order. - -If for some technical reason (eg: one default template depends on the -presence of another default template which must be assigned earlier) you -need to change the ordering, you can simply rename the templates by -prefixing them with numbers, eg: - -- 1 Interfaces -- 2. SSH Keys -- 3. Access - -Required Templates -~~~~~~~~~~~~~~~~~~ - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/required-templates.png - :alt: Required template example - -Required templates are similar to `Default templates -<#default-templates>`__ but cannot be unassigned from a device -configuration, they can only be overridden. - -They will be always assigned earlier than default templates, so they can -be overridden if needed. - -In the example above, the "SSID" template is flagged as "(required)" and -its checkbox is always checked and disabled. - -How to use configuration variables -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Sometimes the configuration is not exactly equal on all the devices, some -parameters are unique to each device or need to be changed by the user. - -In these cases it is possible to use configuration variables in -conjunction with templates, this feature is also known as *configuration -context*, think of it like a dictionary which is passed to the function -which renders the configuration, so that it can fill variables according -to the passed context. - -The different ways in which variables are defined are described below in -the order (high to low) of their precedence: - -1. `User defined device variables <#user-defined-device-variables>`_ -2. `Predefined device variables <#predefined-device-variables>`_ -3. `Group variables <#group-variables>`_ -4. `Organization variables <#organization-variables>`_ -5. `Global variables <#global-variables>`_ -6. `Template default values <#template-default-values>`_ - -User defined device variables -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In the device configuration section you can find a section named -"Configuration variables" where it is possible to define the configuration -variables and their values, as shown in the example below: - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/device-context.png - :alt: context - -Predefined device variables -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Each device gets the following attributes passed as configuration -variables: - -- ``id`` -- ``key`` -- ``name`` -- ``mac_address`` - -Group variables -~~~~~~~~~~~~~~~ - -Variables can also be defined in `Device groups <#device-groups>`__. - -Refer the `Group configuration variables `_ -section for detailed information. - -Organization variables -~~~~~~~~~~~~~~~~~~~~~~ - -Variables can also be defined at the organization level. - -You can set the *organization variables* from the organization change page -``/admin/openwisp_users/organization//change/``, under -the **Configuration Management Settings**. - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/organization-variables.png - :alt: organization variables - -Global variables -~~~~~~~~~~~~~~~~ - -Variables can also be defined globally using the -`OPENWISP_CONTROLLER_CONTEXT <#openwisp-controller-context>`_ setting. - -Template default values -~~~~~~~~~~~~~~~~~~~~~~~ - -It's possible to specify the default values of variables defined in a -template. - -This allows to achieve 2 goals: - -1. pass schema validation without errors (otherwise it would not be - possible to save the template in the first place) -2. provide good default values that are valid in most cases but can be - overridden in the device if needed - -These default values will be overridden by the `User defined device -variables <#user-defined-device-variables>`_. - -The default values of variables can be manipulated from the section -"configuration variables" in the edit template page: - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/template-default-values.png - :alt: default values - -System defined variables -~~~~~~~~~~~~~~~~~~~~~~~~ - -Predefined device variables, global variables and other variables that are -automatically managed by the system (eg: when using templates of type -VPN-client) are displayed in the admin UI as *System Defined Variables* in -read-only mode. - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/system-defined-variables.png - :alt: system defined variables - -**Note:** `Group configuration variables -<#group-configuration-variables>`__ are also added to the **System Defined -Variables** of the device. - -Example usage of variables -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Here's a typical use case, the WiFi SSID and WiFi password. You don't want -to define this for every device, but you may want to allow operators to -easily change the SSID or WiFi password for a specific device without -having to re-define the whole wifi interface to avoid duplicating -information. - -This would be the template: - -.. code-block:: json - - { - "interfaces": [ - { - "type": "wireless", - "name": "wlan0", - "wireless": { - "mode": "access_point", - "radio": "radio0", - "ssid": "{{wlan0_ssid}}", - "encryption": { - "protocol": "wpa2_personal", - "key": "{{wlan0_password}}", - "cipher": "auto" - } - } - } - ] - } - -These would be the default values in the template: - -.. code-block:: json - - { - "wlan0_ssid": "SnakeOil PublicWiFi", - "wlan0_password": "Snakeoil_pwd!321654" - } - -The default values can then be overridden at `device level -<#user-defined-device-variables>`_ if needed, eg: - -.. code-block:: json - - { - "wlan0_ssid": "Room 23 ACME Hotel", - "wlan0_password": "room_23pwd!321654" - } - -How to configure push updates -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Follow the procedure described below to enable secure SSH access from -OpenWISP to your devices, this is required to enable push updates -(whenever the configuration is changed, OpenWISP will trigger the update -in the background) and/or `firmware upgrades (via the additional module -openwisp-firmware-upgrader) -`_. - -**Note**: If you have installed OpenWISP with `openwisp2 Ansbile role -`_ then you can skip the -following steps. The Ansible role automatically creates a default template -to update ``authorized_keys`` on networking devices using the default -access credentials. - -1. Generate SSH key -+++++++++++++++++++ - -First of all, we need to generate the SSH key which will be used by -OpenWISP to access the devices, to do so, you can use the following -command: - -.. code-block:: shell - - echo './sshkey' | ssh-keygen -t ed25519 -C "openwisp" - -This will create two files in the current directory, one called ``sshkey`` -(the private key) and one called ``sshkey.pub`` (the public key). - -Store the content of these files in a secure location. - -**Note:** Support for **ED25519** was added in OpenWrt 21.02 (requires -Dropbear > 2020.79). If you are managing devices with OpenWrt < 21, then -you will need to use RSA keys: - -.. code-block:: shell - - echo './sshkey' | ssh-keygen -t rsa -b 4096 -C "openwisp" - -2. Save SSH private key in OpenWISP (access credentials) -++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/add-ssh-credentials-private-key.png - :alt: add SSH private key as access credential in OpenWISP - -From the first page of OpenWISP click on "Access credentials", then click -on the **"ADD ACCESS CREDENTIALS"** button in the upper right corner -(alternatively, go to the following URL: -``/admin/connection/credentials/add/``). - -Select SSH as ``type``, enable the **Auto add** checkbox, then at the -field "Credentials type" select "SSH (private key)", now type "root" in -the ``username`` field, while in the ``key`` field you have to paste the -contents of the private key just created. - -Now hit save. - -The credentials just created will be automatically enabled for all the -devices in the system (both existing devices and devices which will be -added in the future). - -3. Add the public key to your devices -+++++++++++++++++++++++++++++++++++++ - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/add-authorized-ssh-keys-template.png - :alt: Add authorized SSH public keys template to OpenWISP (OpenWRT) - -Now we need to instruct your devices to allow OpenWISP accessing via SSH, -in order to do this we need to add the contents of the public key file -created in step 1 (``sshkey.pub``) in the file -``/etc/dropbear/authorized_keys`` on the devices, the recommended way to -do this is to create a configuration template in OpenWISP: from the first -page of OpenWISP, click on "Templates", then and click on the **"ADD -TEMPLATE"** button in the upper right corner (alternatively, go to the -following URL: ``/admin/config/template/add/``). - -Check **enabled by default**, then scroll down the configuration section, -click on "Configuration Menu", scroll down, click on "Files" then close -the menu by clicking again on "Configuration Menu". Now type -``/etc/dropbear/authorized_keys`` in the ``path`` field of the file, then -paste the contents of ``sshkey.pub`` in ``contents``. - -Now hit save. - -**There's a catch**: you will need to assign the template to any existing -device. - -4. Test it -++++++++++ - -Once you have performed the 3 steps above, you can test it as follows: - -1. Ensure there's at least one device turned on and connected to OpenWISP, - ensure this device has the "SSH Authorized Keys" assigned to it. -2. Ensure the celery worker of OpenWISP Controller is running (eg: ``ps - aux | grep celery``) -3. SSH into the device and wait (maximum 2 minutes) until - ``/etc/dropbear/authorized_keys`` appears as specified in the template. -4. While connected via SSH to the device run the following command in the - console: ``logread -f``, now try changing the device name in OpenWISP -5. Shortly after you change the name in OpenWISP, you should see some - output in the SSH console indicating another SSH access and the - configuration update being performed. - -Sending Commands to Devices -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -By default, there are three options in the **Send Command** dropdown: - -1. Reboot -2. Change Password -3. Custom Command - -While the first two options are self-explanatory, the **custom command** -option allows you to execute any command on the device as shown in the -example below. - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/commands_demo.gif - :target: https://github.com/openwisp/openwisp-controller/tree/docs/docs/commands_demo.gif - :alt: Executing commands on device example - -**Note**: in order for this feature to work, a device needs to have at -least one **Access Credential** (see `How to configure push updates -<#how-to-configure-push-updates>`__). - -The **Send Command** button will be hidden until the device has at least -one **Access Credential**. - -If you need to allow your users to quickly send specific commands that are -used often in your network regardless of your users' knowledge of Linux -shell commands, you can add new commands by following instructions in the -`"How to define new options in the commands menu" -<#how-to-define-new-options-in-the-commands-menu>`_ section below. - -If you are an advanced user and want to register commands programatically, -then refer to `"Register / Unregistering commands" -<#registering--unregistering-commands>`_ section. - -How to define new options in the commands menu -++++++++++++++++++++++++++++++++++++++++++++++ - -Let's explore to define new custom commands to help users perform -additional management actions without having to be Linux/Unix experts. - -We can do so by using the ``OPENWISP_CONTROLLER_USER_COMMANDS`` django -setting. - -The following example defines a simple command that can ``ping`` an input -``destination_address`` through a network interface, ``interface_name``. - -.. code-block:: python - - # In yourproject/settings.py - - - def ping_command_callable(destination_address, interface_name=None): - command = f"ping -c 4 {destination_address}" - if interface_name: - command += f" -I {interface_name}" - return command - - - OPENWISP_CONTROLLER_USER_COMMANDS = [ - ( - "ping", - { - "label": "Ping", - "schema": { - "title": "Ping", - "type": "object", - "required": ["destination_address"], - "properties": { - "destination_address": { - "type": "string", - "title": "Destination Address", - }, - "interface_name": { - "type": "string", - "title": "Interface Name", - }, - }, - "message": "Destination Address cannot be empty", - "additionalProperties": False, - }, - "callable": ping_command_callable, - }, - ) - ] - -The above code will add the "Ping" command in the user interface as show -in the GIF below: - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/ping_command_example.gif - :target: https://github.com/openwisp/openwisp-controller/tree/docs/docs/ping_command_example.gif - :alt: Adding a "ping" command - -The ``OPENWISP_CONTROLLER_USER_COMMANDS`` setting takes a ``list`` of -``tuple`` each containing two elements. The first element of the tuple -should contain an identifier for the command and the second element should -contain a ``dict`` defining configuration of the command. - -Command Configuration -..................... - -The ``dict`` defining configuration for command should contain following -keys: - -1. ``label`` -'''''''''''' - -A ``str`` defining label for the command used internally by Django. - -2. ``schema`` -''''''''''''' - -A ``dict`` defining `JSONSchema `_ for inputs of -command. You can specify the inputs for your command, add rules for -performing validation and make inputs required or optional. - -Here is a detailed explanation of the schema used in above example: - -.. code-block:: python - - { - # Name of the command displayed in "Send Command" widget - 'title': 'Ping', - # Use type "object" if the command needs to accept inputs - # Use type "null" if the command does not accepts any input - 'type': 'object', - # Specify list of inputs that are required - 'required': ['destination_address'], - # Define the inputs for the commands along with their properties - 'properties': { - 'destination_address': { - # type of the input value - 'type': 'string', - # label used for displaying this input field - 'title': 'Destination Address', - }, - 'interface_name': { - 'type': 'string', - 'title': 'Interface Name', - }, - }, - # Error message to be shown if validation fails - 'message': 'Destination Address cannot be empty'), - # Whether specifying addtionaly inputs is allowed from the input form - 'additionalProperties': False, - } - -This example uses only handful of properties available in JSONSchema. You -can experiment with other properties of JSONSchema for schema of your -command. - -3. ``callable`` -''''''''''''''' - -A ``callable`` or ``str`` defining dotted path to a callable. It should -return the command (``str``) to be executed on the device. Inputs of the -command are passed as arguments to this callable. - -The example above includes a callable(``ping_command_callable``) for -``ping`` command. - -Registering / Unregistering Commands -++++++++++++++++++++++++++++++++++++ - -OpenWISP Controller provides registering and unregistering commands -through utility functions -``openwisp_controller.connection.commands.register_command`` and -``openwisp_notifications.types.unregister_notification_type``. You can use -these functions to register or unregister commands from your code. - -**Note**: These functions are to be used as an alternative to the -`"OPENWISP_CONTROLLER_USER_COMMANDS" -<#openwisp-controller-user-commands>`_ when `developing custom modules -based on openwisp-controller <#extending-openwisp-controller>`_ or when -developing custom third party apps. - -``register_command`` -.................... - -================== =================================================== -Parameter Description -``command_name`` A ``str`` defining identifier for the command. -``command_config`` A ``dict`` defining configuration of the command as - shown in `"Command Configuration" - <#command-configuration>`_. -================== =================================================== - -**Note:** It will raise ``ImproperlyConfigured`` exception if a command is -already registered with the same name. - -``unregister_command`` -...................... - -================ ======================================= -Parameter Description -``command_name`` A ``str`` defining name of the command. -================ ======================================= - -**Note:** It will raise ``ImproperlyConfigured`` exception if such command -does not exists. - -Device Groups -~~~~~~~~~~~~~ - -Device Groups provide features aimed at adding specific management rules -for the devices of an organization: - -- Group similar devices by having dedicated groups for access points, - routers, etc. -- Define `group metadata <#group-metadata>`_. -- Define `group configuration templates <#group-templates>`_. -- Define `group configuration variables - <#group-configuration-variables>`__. - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/1.1/device-groups.png - :alt: Device Group example - -Group Templates -+++++++++++++++ - -Groups allow to define templates which are automatically assigned to -devices belonging to the group. When using this feature, keep in mind the -following important points: - -- Templates of any configuration backend can be selected, when a device is - assigned to a group, only the templates which matches the device - configuration backend are applied to the device. -- The system will not force group templates onto devices, this means that - users can remove the applied group templates from a specific device if - needed. -- If a device group is changed, the system will automatically remove the - group templates of the old group and apply the new templates of the new - group (this operation is implemented by leveraging the - `group_templates_changed <#group_templates_changed>`_ signal). -- If the group templates are changed, the devices which belong to the - group will be automatically updated to reflect the changes (this - operation is executed in a background task). -- In case the configuration backend of a device is changed, the system - will handle this automatically too and update the group templates - accordingly (this operation is implemented by leveraging the - `config_backend_changed <#config_backend_changed>`_ signal). -- If a device does not have a configuration defined yet, but it is - assigned to a group which has templates defined, the system will - automatically create a configuration for it using the default backend - specified in `OPENWISP_CONTROLLER_DEFAULT_BACKEND - <#OPENWISP_CONTROLLER_DEFAULT_BACKEND>`_ setting. - -**Note:** the list of templates shown in the edit group page do not -contain templates flagged as "default" or "required" to avoid redundancy -because those templates are automatically assigned by the system to new -devices. - -This feature works also when editing group templates or the group assigned -to a device via the `REST API <#change-device-group-detail>`__. - -Group Configuration Variables -+++++++++++++++++++++++++++++ - -Groups allow to define configuration variables which are automatically -added to the device's context in the **System Defined Variables**. Check -the `"How to use configuration variables" section -<#how-to-use-configuration-variables>`_ to learn about precedence of -different configuration variables. - -This feature works also when editing group templates or the group assigned -to a device via the `REST API <#change-device-group-detail>`__. - -Group Metadata -++++++++++++++ - -Groups allow to store additional information regarding a group in the -structured metadata field (which can be accessed via the REST API). - -The metadata field allows custom structure and validation to standardize -information across all groups using the -`"OPENWISP_CONTROLLER_DEVICE_GROUP_SCHEMA" -<#openwisp-controller-device-group-schema>`_ setting. - -**Note:** *Group configuration variables* and *Group metadata* serves -different purposes. The group configuration variables should be used when -the device configuration is required to be changed for particular group of -devices. Group metadata should be used to store additional data for the -devices. Group metadata is not used for configuration generation. - -Export/Import Device data -~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/1.1/import-export/device-list.png - :alt: Import / Export - -The device list page offers two buttons to export and import device data -in different formats. - -The export feature respects any filters selected in the device list. - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/1.1/import-export/export-page.png - :alt: Export - -For importing devices into the system, only the required fields are -needed, for example, the following CSV file will import a device named -``TestImport`` with mac address ``00:11:22:09:44:55`` in the organization -with UUID ``3cb5e18c-0312-48ab-8dbd-038b8415bd6f``: - -:: - - organization_id,name,mac_address - 3cb5e18c-0312-48ab-8dbd-038b8415bd6f,TestImport,00:11:22:09:44:55 - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/1.1/import-export/import-page.png - :alt: Import / Export - -Organization Limits -~~~~~~~~~~~~~~~~~~~ - -Allows configuring following limits for each organization: - -- Limit number of devices managed by the organization. - -You can change the limits from the organization's admin page: - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/1.1/organization-limits.png - :alt: Organization limits - -How to setup WireGuard tunnels -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Follow the procedure described below to setup WireGuard tunnels on your -devices. - -**Note:** This example uses **Shared systemwide (no organization)** option -as the organization for VPN server and VPN client template. You can use -any organization as long as VPN server, VPN client template and Device has -same organization. - -1. Create VPN server configuration for WireGuard -++++++++++++++++++++++++++++++++++++++++++++++++ - -1. Visit ``/admin/config/vpn/add/`` to add a new VPN server. -2. We will set **Name** of this VPN server ``Wireguard`` and **Host** as - ``wireguard-server.mydomain.com`` (update this to point to your - WireGuard VPN server). -3. Select ``WireGuard`` from the dropdown as **VPN Backend**. -4. When using WireGuard, OpenWISP takes care of managing IP addresses - (assigning an IP address to each VPN peer). You can create a new subnet - or select an existing one from the dropdown menu. You can also assign - an **Internal IP** to the WireGuard Server or leave it empty for - OpenWISP to configure. This IP address will be used by the WireGuard - interface on server. -5. We have set the **Webhook Endpoint** as - ``https://wireguard-server.mydomain.com:8081/trigger-update`` for this - example. You will need to update this according to you VPN upgrader - endpoint. Set **Webhook AuthToken** to any strong passphrase, this will - be used to ensure that configuration upgrades are requested from - trusted sources. - - **Note**: If you are following this tutorial for also setting up - WireGuard VPN server, just substitute ``wireguard-server.mydomain.com`` - with hostname of your VPN server and follow the steps in next section. - -6. Under the configuration section, set the name of WireGuard tunnel 1 - interface. We have used ``wg0`` in this example. - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-tutorial/vpn-server-1.png - :alt: WireGuard VPN server configuration example 1 - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-tutorial/vpn-server-2.png - :alt: WireGuard VPN server configuration example 2 - -7. After clicking on **Save and continue editing**, you will see that - OpenWISP has automatically created public and private key for WireGuard - server in **System Defined Variables** along with internal IP address - information. - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-tutorial/vpn-server-3.png - :alt: WireGuard VPN server configuration example 3 - -2. Deploy Wireguard VPN Server -++++++++++++++++++++++++++++++ - -If you haven't already setup WireGuard on your VPN server, this will be a -good time do so. We recommend using the `ansible-wireguard-openwisp -`_ role for -installing WireGuard since it also installs scripts that allows OpenWISP -to manage WireGuard VPN server. - -Pay attention to the VPN server attributes used in your playbook. It -should be same as VPN server configuration in OpenWISP. - -3. Create VPN client template for WireGuard VPN Server -++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -1. Visit ``/admin/config/template/add/`` to add a new template. -2. Set ``Wireguard Client`` as **Name** (you can set whatever you want) - and select ``VPN-client`` as **type** from the dropdown list. -3. The **Backend** field refers to the backend of the device this template - can be applied to. For this example, we will leave it to ``OpenWRT``. -4. Select the correct VPN server from the dropdown for the **VPN** field. - Here it is ``Wireguard``. -5. Ensure that **Automatic tunnel provisioning** is checked. This will - make OpenWISP to automatically generate public and private keys and - provision IP address for each WireGuard VPN client. -6. After clicking on **Save and continue editing** button, you will see - details of *Wireguard* VPN server in **System Defined Variables**. The - template configuration will be automatically generated which you can - tweak accordingly. We will use the automatically generated VPN client - configuration for this example. - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-tutorial/template.png - :alt: WireGuard VPN client template example - -4. Apply Wireguard VPN template to devices -++++++++++++++++++++++++++++++++++++++++++ - -**Note**: This step assumes that you already have a device registered on -OpenWISP. Register or create a device before proceeding. - -1. Open the **Configuration** tab of the concerned device. -2. Select the *WireGuard Client* template. -3. Upon clicking on **Save and continue editing** button, you will see - some entries in **System Defined Variables**. It will contain internal - IP address, private and public key for the WireGuard client on the - device along with details of WireGuard VPN server. - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-tutorial/device-configuration.png - :alt: WireGuard VPN device configuration example - -**Voila!** You have successfully configured OpenWISP to manage WireGuard -tunnels for your devices. - -How to setup VXLAN over WireGuard tunnels -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -By following these steps, you will be able to setup layer 2 VXLAN tunnels -encapsulated in WireGuard tunnels which work on layer 3. - -**Note:** This example uses **Shared systemwide (no organization)** option -as the organization for VPN server and VPN client template. You can use -any organization as long as VPN server, VPN client template and Device has -same organization. - -1. Create VPN server configuration for VXLAN over WireGuard -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -1. Visit ``/admin/config/vpn/add/`` to add a new VPN server. -2. We will set **Name** of this VPN server ``Wireguard VXLAN`` and - **Host** as ``wireguard-vxlan-server.mydomain.com`` (update this to - point to your WireGuard VXLAN VPN server). -3. Select ``VXLAN over WireGuard`` from the dropdown as **VPN Backend**. -4. When using VXLAN over WireGuard, OpenWISP takes care of managing IP - addresses (assigning an IP address to each VPN peer). You can create a - new subnet or select an existing one from the dropdown menu. You can - also assign an **Internal IP** to the WireGuard Server or leave it - empty for OpenWISP to configure. This IP address will be used by the - WireGuard interface on server. -5. We have set the **Webhook Endpoint** as - ``https://wireguard-vxlan-server.mydomain.com:8081/trigger-update`` for - this example. You will need to update this according to you VPN - upgrader endpoint. Set **Webhook AuthToken** to any strong passphrase, - this will be used to ensure that configuration upgrades are requested - from trusted sources. - - **Note**: If you are following this tutorial for also setting up - WireGuard VPN server, just substitute ``wireguard-server.mydomain.com`` - with hostname of your VPN server and follow the steps in next section. - -6. Under the configuration section, set the name of WireGuard tunnel 1 - interface. We have used ``wg0`` in this example. - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-vxlan-tutorial/vpn-server-1.png - :alt: WireGuard VPN VXLAN server configuration example 1 - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-vxlan-tutorial/vpn-server-2.png - :alt: WireGuard VPN VXLAN server configuration example 2 - -7. After clicking on **Save and continue editing**, you will see that - OpenWISP has automatically created public and private key for WireGuard - server in **System Defined Variables** along with internal IP address - information. - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-vxlan-tutorial/vpn-server-3.png - :alt: WireGuard VXLAN VPN server configuration example 3 - -2. Deploy Wireguard VXLAN VPN Server -++++++++++++++++++++++++++++++++++++ - -If you haven't already setup WireGuard on your VPN server, this will be a -good time do so. We recommend using the `ansible-wireguard-openwisp -`_ role for -installing WireGuard since it also installs scripts that allows OpenWISP -to manage WireGuard VPN server along with VXLAN tunnels. - -Pay attention to the VPN server attributes used in your playbook. It -should be same as VPN server configuration in OpenWISP. - -3. Create VPN client template for WireGuard VXLAN VPN Server -++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -1. Visit ``/admin/config/template/add/`` to add a new template. -2. Set ``Wireguard VXLAN Client`` as **Name** (you can set whatever you - want) and select ``VPN-client`` as **type** from the dropdown list. -3. The **Backend** field refers to the backend of the device this template - can be applied to. For this example, we will leave it to ``OpenWRT``. -4. Select the correct VPN server from the dropdown for the **VPN** field. - Here it is ``Wireguard VXLAN``. -5. Ensure that **Automatic tunnel provisioning** is checked. This will - make OpenWISP to automatically generate public and private keys and - provision IP address for each WireGuard VPN client along with VXLAN - Network Indentifier(VNI). -6. After clicking on **Save and continue editing** button, you will see - details of *Wireguard VXLAN* VPN server in **System Defined - Variables**. The template configuration will be automatically generated - which you can tweak accordingly. We will use the automatically - generated VPN client configuration for this example. - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-vxlan-tutorial/template.png - :alt: WireGuard VXLAN VPN client template example - -4. Apply Wireguard VXLAN VPN template to devices -++++++++++++++++++++++++++++++++++++++++++++++++ - -**Note**: This step assumes that you already have a device registered on -OpenWISP. Register or create a device before proceeding. - -1. Open the **Configuration** tab of the concerned device. -2. Select the *WireGuard VXLAN Client* template. -3. Upon clicking on **Save and continue editing** button, you will see - some entries in **System Defined Variables**. It will contain internal - IP address, private and public key for the WireGuard client on the - device and details of WireGuard VPN server along with VXLAN Network - Identifier(VNI) of this device. - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-vxlan-tutorial/device-configuration.png - :alt: WireGuard VXLAN VPN device configuration example - -**Voila!** You have successfully configured OpenWISP to manage VXLAN over -WireGuard tunnels for your devices. - -How to setup ZeroTier Tunnels -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Follow the procedure described below to setup ZeroTier tunnels on your -devices. - -**Note:** This example uses **Shared systemwide (no organization)** option -as the organization for VPN server and VPN client template. You can use -any organization as long as VPN server, VPN client template and Device has -same organization. - -1. Configure Self-Hosted ZeroTier Network Controller -++++++++++++++++++++++++++++++++++++++++++++++++++++ - -If you haven't already set up a self-hosted Zerotier network controller on -your server, now is a good time to do so. You can start by simply -installing Zerotier on your server from the `official website -`_. - -2. Create VPN server configuration for ZeroTier -+++++++++++++++++++++++++++++++++++++++++++++++ - -1. Visit ``/admin/config/vpn/add/`` to add a new VPN server. -2. We will set **Name** of this VPN server ``ZeroTier`` and **Host** as - ``my-zerotier-server.mydomain.com:9993`` (update this to point to your - ZeroTier VPN server). -3. Select ``ZeroTier`` from the dropdown as **VPN Backend**. -4. When using ZeroTier, OpenWISP takes care of managing IP addresses - (assigning an IP address to each VPN clients (Zerotier network - members). You can create a new subnet or select an existing one from - the dropdown menu. You can also assign an **Internal IP** to the - Zerotier controller or leave it empty for OpenWISP to configure. This - IP address will be used to assign it to the Zerotier controller running - on the server. -5. Set the **Webhook AuthToken**, this will be ZeroTier authorization - token which you can obtain by running the following command on the - ZeroTier controller: - - .. code-block:: shell - - sudo cat /var/lib/zerotier-one/authtoken.secret - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/vpn-server-1.png - :alt: ZeroTier VPN server configuration example 1 - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/vpn-server-2.png - :alt: ZeroTier VPN server configuration example 2 - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/vpn-server-3.png - :alt: ZeroTier VPN server configuration example 3 - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/vpn-server-4.png - :alt: ZeroTier VPN server configuration example 4 - -6. After clicking on **Save and continue editing**, OpenWISP automatically - detects the node address of the Zerotier controller and creates a - Zerotier network. The **network_id** of this network can be viewed in - the **System Defined Variables** section, where it also provides - internal IP address information. - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/vpn-server-5.png - :alt: ZeroTier VPN server configuration example 5 - -3. Create VPN client template for ZeroTier VPN Server -+++++++++++++++++++++++++++++++++++++++++++++++++++++ - -1. Visit ``/admin/config/template/add/`` to add a new template. -2. Set ``ZeroTier Client`` as **Name** (you can set whatever you want) and - select ``VPN-client`` as **type** from the dropdown list. -3. The **Backend** field refers to the backend of the device this template - can be applied to. For this example, we will leave it to ``OpenWRT``. -4. Select the correct VPN server from the dropdown for the **VPN** field. - Here it is ``ZeroTier``. -5. Ensure that the **Automatic tunnel provisioning** option is checked. - This will enable OpenWISP to automatically provision an IP address and - ZeroTier identity secrets (used for assigning member IDs) for each - ZeroTier VPN client. -6. After clicking on **Save and continue editing** button, you will see - details of *ZeroTier* VPN server in **System Defined Variables**. The - template configuration will be automatically generated which you can - tweak accordingly. We will use the automatically generated VPN client - configuration for this example. - -**Note:** OpenWISP uses `zerotier-idtool -`_ -to manage **ZeroTier identity secrets**. Please make sure that you have -`ZeroTier package installed `_ on the -server. - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/template.png - :alt: ZeroTier VPN client template example - -4. Apply ZeroTier VPN template to devices -+++++++++++++++++++++++++++++++++++++++++ - -**Note**: This step assumes that you already have a device registered on -OpenWISP. Register or create a device before proceeding. - -1. Open the **Configuration** tab of the concerned device. -2. Select the *ZeroTier Client* template. -3. Upon clicking the **Save and Continue Editing** button, you will see - entries in the **System Defined Variables** section. These entries will - include **zerotier_member_id**, **identity_secret**, and the internal - **IP address** of the ZeroTier client (network member) on the device, - along with details of the VPN server. - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/device-configuration-1.png - :alt: ZeroTier VPN device configuration example 1 - -4. Once the configuration is successfully applied to the device, you will - notice a new ZeroTier interface that is up and running. This interface - will have the name ``owzt89f498`` (where ``owzt`` is followed by the - last six hexadecimal characters of the ZeroTier **network ID**). - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/device-configuration-2.png - :alt: ZeroTier VPN device configuration example 2 - -**Voila!** You have successfully configured OpenWISP to manage ZeroTier -tunnels for your devices. - -How to configure automatic provisioning of subnets and IPs -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The following steps will help you configure automatic provisioning of -subnets and IPs for devices. - -1. Create a Subnet and a Subnet Division Rule -+++++++++++++++++++++++++++++++++++++++++++++ - -Create a master subnet under which automatically generated subnets will be -provisioned. - -**Note**: Choose the size of the subnet appropriately considering your use -case. - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/subnet-division-rule/subnet.png - :alt: Creating a master subnet example - -On the same page, add a **subnet division rule** that will be used to -provision subnets under the master subnet. - -The type of subnet division rule controls when subnets and IP addresses -will be provisioned for a device. The subnet division rule types currently -implemented are described below. - -Device Subnet Division Rule -........................... - -This rule type is triggered whenever a device configuration -(``config.Config`` model) is created for the organization specified in the -rule. - -Creating a new rule of "Device" type will also provision subnets and IP -addresses for existing devices of the organization automatically. - -**Note**: a device without a configuration will not trigger this rule. - -VPN Subnet Division Rule -........................ - -This rule is triggered when a VPN client template is assigned to a device, -provided the VPN server to which the VPN client template relates to has -the same subnet for which the subnet division rule is created. - -**Note:** This rule will only work for **WireGuard** and **VXLAN over -WireGuard** VPN servers. - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/subnet-division-rule/subnet-division-rule.png - :alt: Creating a subnet division rule example - -In this example, **VPN subnet division rule** is used. - -2. Create a VPN Server -++++++++++++++++++++++ - -Now create a VPN Server and choose the previously created **master -subnet** as the subnet for this VPN Server. - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/subnet-division-rule/vpn-server.png - :alt: Creating a VPN Server example - -3. Create a VPN Client Template -+++++++++++++++++++++++++++++++ - -Create a template, setting the **Type** field to **VPN Client** and -**VPN** field to use the previously created VPN Server. - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/subnet-division-rule/vpn-client.png - :alt: Creating a VPN Client template example - -**Note**: You can also check the **Enable by default** field if you want -to automatically apply this template to devices that will register in -future. - -4. Apply VPN Client Template to Devices -+++++++++++++++++++++++++++++++++++++++ - -With everything in place, you can now apply the VPN Client Template to -devices. - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/subnet-division-rule/apply-template-to-device.png - :alt: Adding template to device example - -After saving the device, you should see all provisioned Subnets and IPs -for this device under `System Defined Variables -<#system-defined-variables>`_. - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/subnet-division-rule/system-defined-variables.png - :alt: Provisioned Subnets and IPs available as System Defined Variables example - -Voila! You can now use these variables in configuration of the device. -Refer to `How to use configuration variables -<#how-to-use-configuration-variables>`_ section of this documentation to -learn how to use configuration variables. - -Important notes for using Subnet Division -+++++++++++++++++++++++++++++++++++++++++ - -- In the above example Subnet, VPN Server, and VPN Client Template - belonged to the **default** organization. You can use **Systemwide - Shared** Subnet, VPN Server, or VPN Client Template too, but Subnet - Division Rule will be always related to an organization. The Subnet - Division Rule will only be triggered when such VPN Client Template will - be applied to a Device having the same organization as Subnet Division - Rule. -- You can also use the configuration variables for provisioned subnets and - IPs in the Template. Each variable will be resolved differently for - different devices. E.g. ``OW_subnet1_ip1`` will resolve to ``10.0.0.1`` - for one device and ``10.0.0.55`` for another. Every device gets its own - set of subnets and IPs. But don't forget to provide the default fall - back values in the "default values" template field (used mainly for - validation). -- The Subnet Division Rule will automatically create a reserved subnet, - this subnet can be used to provision any IP addresses that have to be - created manually. The rest of the master subnet address space **must - not** be interfered with or the automation implemented in this module - will not work. -- The above example used `VPN subnet division rule - <#vpn-subnet-division-rule>`_. Similarly, `device subnet division rule - <#device-subnet-division-rule>`_ can be used, which only requires - `creating a subnet and a subnet division rule - <#1-create-a-subnet-and-a-subnet-division-rule>`_. - -Limitations of Subnet Division -++++++++++++++++++++++++++++++ - -In the current implementation, it is not possible to change "Size", -"Number of Subnets" and "Number of IPs" fields of an existing subnet -division rule due to following reasons: - -Size -.... - -Allowing to change size of provisioned subnets of an existing subnet -division rule will require rebuilding of Subnets and IP addresses which -has possibility of breaking existing configurations. - -Number of Subnets -................. - -Allowing to decrease number of subnets of an existing subnet division rule -can create patches of unused subnets dispersed everywhere in the master -subnet. Allowing to increase number of subnets will break the continuous -allocation of subnets for every device. It can also break configuration of -devices. - -Number of IPs -............. - -Allowing to decrease number of IPs of an existing subnet division rule -will lead to deletion of IP Addresses which can break configuration of -devices being used. It **is allowed** to increase number of IPs. - -If you want to make changes to any of above fields, delete the existing -rule and create a new one. The automation will provision for all existing -devices that meets the criteria for provisioning. **WARNING**: It is -possible that devices get different subnets and IPs from previous -provisioning. - -Default Alerts / Notifications -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -===================== =================================================== -Notification Type Use -``config_error`` Fires when status of a device configuration changes - to ``error``. -``device_registered`` Fires when a new device is registered automatically - on the network. -===================== =================================================== - -REST API Reference ------------------- - -Live documentation -~~~~~~~~~~~~~~~~~~ - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/live-docu-api.png - -A general live API documentation (following the OpenAPI specification) at -``/api/v1/docs/``. - -Browsable web interface -~~~~~~~~~~~~~~~~~~~~~~~ - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/browsable-api-ui.png - -Additionally, opening any of the endpoints `listed below -<#list-of-endpoints>`_ directly in the browser will show the `browsable -API interface of Django-REST-Framework -`_, which -makes it even easier to find out the details of each endpoint. - -Authentication -~~~~~~~~~~~~~~ - -See openwisp-users: `authenticating with the user token -`_. - -When browsing the API via the `Live documentation <#live-documentation>`_ -or the `Browsable web page <#browsable-web-interface>`_, you can also use -the session authentication by logging in the django admin. - -Pagination -~~~~~~~~~~ - -All *list* endpoints support the ``page_size`` parameter that allows -paginating the results in conjunction with the ``page`` parameter. - -.. code-block:: text - - GET /api/v1/controller/template/?page_size=10 - GET /api/v1/controller/template/?page_size=10&page=2 - -List of endpoints -~~~~~~~~~~~~~~~~~ - -Since the detailed explanation is contained in the `Live documentation -<#live-documentation>`_ and in the `Browsable web page -<#browsable-web-interface>`_ of each point, here we'll provide just a list -of the available endpoints, for further information please open the URL of -the endpoint in your browser. - -List devices -++++++++++++ - -.. code-block:: text - - GET /api/v1/controller/device/ - -**Available filters** - -You can filter a list of devices based on their configuration status using -the ``status`` (e.g modified, applied, or error). - -.. code-block:: text - - GET /api/v1/controller/device/?config__status={status} - -You can filter a list of devices based on their configuration backend -using the ``backend`` (e.g netjsonconfig.OpenWrt or -netjsonconfig.OpenWisp). - -.. code-block:: text - - GET /api/v1/controller/device/?config__backend={backend} - -You can filter a list of devices based on their organization using the -``organization_id`` or ``organization_slug``. - -.. code-block:: text - - GET /api/v1/controller/device/?organization={organization_id} - -.. code-block:: text - - GET /api/v1/controller/device/?organization_slug={organization_slug} - -You can filter a list of devices based on their configuration templates -using the ``template_id``. - -.. code-block:: text - - GET /api/v1/controller/device/?config__templates={template_id} - -You can filter a list of devices based on their device group using the -``group_id``. - -.. code-block:: text - - GET /api/v1/controller/device/?group={group_id} - -You can filter a list of devices that have a device location object using -the ``with_geo`` (eg. true or false). - -.. code-block:: text - - GET /api/v1/controller/device/?with_geo={with_geo} - -You can filter a list of devices based on their creation time using the -``creation_time``. - -.. code-block:: text - - # Created exact - GET /api/v1/controller/device/?created={creation_time} - - # Created greater than or equal to - GET /api/v1/controller/device/?created__gte={creation_time} - - # Created is less than - GET /api/v1/controller/device/?created__lt={creation_time} - -Create device -+++++++++++++ - -.. code-block:: text - - POST /api/v1/controller/device/ - -Get device detail -+++++++++++++++++ - -.. code-block:: text - - GET /api/v1/controller/device/{id}/ - -Download device configuration -+++++++++++++++++++++++++++++ - -.. code-block:: text - - GET /api/v1/controller/device/{id}/configuration/ - -The above endpoint triggers the download of a ``tar.gz`` file containing -the generated configuration for that specific device. - -Change details of device -++++++++++++++++++++++++ - -.. code-block:: text - - PUT /api/v1/controller/device/{id}/ - -Patch details of device -+++++++++++++++++++++++ - -.. code-block:: text - - PATCH /api/v1/controller/device/{id}/ - -**Note**: To assign, unassign, and change the order of the assigned -templates add, remove, and change the order of the ``{id}`` of the -templates under the ``config`` field in the JSON response respectively. -Moreover, you can also select and unselect templates in the HTML Form of -the Browsable API. - -The required template(s) from the organization(s) of the device will added -automatically to the ``config`` and cannot be removed. - -**Example usage**: For assigning template(s) add the/their {id} to the -config of a device, - -.. code-block:: shell - - curl -X PATCH \ - http://127.0.0.1:8000/api/v1/controller/device/76b7d9cc-4ffd-4a43-b1b0-8f8befd1a7c0/ \ - -H 'authorization: Bearer dc8d497838d4914c9db9aad9b6ec66f6c36ff46b' \ - -H 'content-type: application/json' \ - -d '{ - "config": { - "templates": ["4791fa4c-2cef-4f42-8bb4-c86018d71bd3"] - } - }' - -**Example usage**: For removing assigned templates, simply remove -the/their {id} from the config of a device, - -.. code-block:: shell - - curl -X PATCH \ - http://127.0.0.1:8000/api/v1/controller/device/76b7d9cc-4ffd-4a43-b1b0-8f8befd1a7c0/ \ - -H 'authorization: Bearer dc8d497838d4914c9db9aad9b6ec66f6c36ff46b' \ - -H 'content-type: application/json' \ - -d '{ - "config": { - "templates": [] - } - }' - -**Example usage**: For reordering the templates simply change their order -from the config of a device, - -.. code-block:: shell - - curl -X PATCH \ - http://127.0.0.1:8000/api/v1/controller/device/76b7d9cc-4ffd-4a43-b1b0-8f8befd1a7c0/ \ - -H 'authorization: Bearer dc8d497838d4914c9db9aad9b6ec66f6c36ff46b' \ - -H 'cache-control: no-cache' \ - -H 'content-type: application/json' \ - -H 'postman-token: b3f6a1cc-ff13-5eba-e460-8f394e485801' \ - -d '{ - "config": { - "templates": [ - "c5bbc697-170e-44bc-8eb7-b944b55ee88f", - "4791fa4c-2cef-4f42-8bb4-c86018d71bd3" - ] - } - }' - -Delete device -+++++++++++++ - -.. code-block:: text - - DELETE /api/v1/controller/device/{id}/ - -List device connections -+++++++++++++++++++++++ - -.. code-block:: text - - GET /api/v1/controller/device/{id}/connection/ - -Create device connection -++++++++++++++++++++++++ - -.. code-block:: text - - POST /api/v1/controller/device/{id}/connection/ - -Get device connection detail -++++++++++++++++++++++++++++ - -.. code-block:: text - - GET /api/v1/controller/device/{id}/connection/{id}/ - -Change device connection detail -+++++++++++++++++++++++++++++++ - -.. code-block:: text - - PUT /api/v1/controller/device/{id}/connection/{id}/ - -Patch device connection detail -++++++++++++++++++++++++++++++ - -.. code-block:: text - - PATCH /api/v1/controller/device/{id}/connection/{id}/ - -Delete device connection -++++++++++++++++++++++++ - -.. code-block:: text - - DELETE /api/v1/controller/device/{id}/connection/{id}/ - -List credentials -++++++++++++++++ - -.. code-block:: text - - GET /api/v1/connection/credential/ - -Create credential -+++++++++++++++++ - -.. code-block:: text - - POST /api/v1/connection/credential/ - -Get credential detail -+++++++++++++++++++++ - -.. code-block:: text - - GET /api/v1/connection/credential/{id}/ - -Change credential detail -++++++++++++++++++++++++ - -.. code-block:: text - - PUT /api/v1/connection/credential/{id}/ - -Patch credential detail -+++++++++++++++++++++++ - -.. code-block:: text - - PATCH /api/v1/connection/credential/{id}/ - -Delete credential -+++++++++++++++++ - -.. code-block:: text - - DELETE /api/v1/connection/credential/{id}/ - -List commands of a device -+++++++++++++++++++++++++ - -.. code-block:: text - - GET /api/v1/controller/device/{id}/command/ - -Execute a command a device -++++++++++++++++++++++++++ - -.. code-block:: text - - POST /api/v1/controller/device/{id}/command/ - -Get command details -+++++++++++++++++++ - -.. code-block:: text - - GET /api/v1/controller/device/{device_id}/command/{command_id}/ - -List device groups -++++++++++++++++++ - -.. code-block:: text - - GET /api/v1/controller/group/ - -**Available filters** - -You can filter a list of device groups based on their organization using -the ``organization_id`` or ``organization_slug``. - -.. code-block:: text - - GET /api/v1/controller/group/?organization={organization_id} - -.. code-block:: text - - GET /api/v1/controller/group/?organization_slug={organization_slug} - -You can filter a list of device groups that have a device object using the -``empty`` (eg. true or false). - -.. code-block:: text - - GET /api/v1/controller/group/?empty={empty} - -Create device group -+++++++++++++++++++ - -.. code-block:: text - - POST /api/v1/controller/group/ - -Get device group detail -+++++++++++++++++++++++ - -.. code-block:: text - - GET /api/v1/controller/group/{id}/ - -Change device group detail -++++++++++++++++++++++++++ - -.. code-block:: text - - PUT /api/v1/controller/group/{id}/ - -This endpoint allows to change the `group templates <#group-templates>`_ -too. - -Get device group from certificate common name -+++++++++++++++++++++++++++++++++++++++++++++ - -.. code-block:: text - - GET /api/v1/controller/cert/{common_name}/group/ - -This endpoint can be used to retrieve group information and metadata by -the common name of a certificate used in a VPN client tunnel, this -endpoint is used in layer 2 tunneling solutions for firewall/captive -portals. - -It is also possible to filter device group by providing organization slug -of certificate's organization as show in the example below: - -.. code-block:: text - - GET /api/v1/controller/cert/{common_name}/group/?org={org1_slug},{org2_slug} - -Get device location -+++++++++++++++++++ - -.. code-block:: text - - GET /api/v1/controller/device/{id}/location/ - -Create device location -++++++++++++++++++++++ - -.. code-block:: text - - PUT /api/v1/controller/device/{id}/location/ - -You can create ``DeviceLocation`` object by using primary keys of existing -``Location`` and ``FloorPlan`` objects as shown in the example below. - -.. code-block:: json - - { - "location": "f0cb5762-3711-4791-95b6-c2f6656249fa", - "floorplan": "dfeb6724-aab4-4533-aeab-f7feb6648acd", - "indoor": "-36,264" - } - -**Note:** The ``indoor`` field represents the coordinates of the point -placed on the image from the top left corner. E.g. if you placed the -pointer on the top left corner of the floorplan image, its indoor -coordinates will be ``0,0``. - -.. code-block:: text - - curl -X PUT \ - http://127.0.0.1:8000/api/v1/controller/device/8a85cc23-bad5-4c7e-b9f4-ffe298defb5c/location/ \ - -H 'authorization: Bearer dc8d497838d4914c9db9aad9b6ec66f6c36ff46b' \ - -H 'content-type: application/json' \ - -d '{ - "location": "f0cb5762-3711-4791-95b6-c2f6656249fa", - "floorplan": "dfeb6724-aab4-4533-aeab-f7feb6648acd", - "indoor": "-36,264" - }' - -You can also create related ``Location`` and ``FloorPlan`` objects for the -device directly from this endpoint. - -The following example demonstrates creating related location object in a -single request. - -.. code-block:: json - - { - "location": { - "name": "Via del Corso", - "address": "Via del Corso, Roma, Italia", - "geometry": { - "type": "Point", - "coordinates": [12.512124, 41.898903] - }, - "type": "outdoor", - } - } - -.. code-block:: text - - curl -X PUT \ - http://127.0.0.1:8000/api/v1/controller/device/8a85cc23-bad5-4c7e-b9f4-ffe298defb5c/location/ \ - -H 'authorization: Bearer dc8d497838d4914c9db9aad9b6ec66f6c36ff46b' \ - -H 'content-type: application/json' \ - -d '{ - "location": { - "name": "Via del Corso", - "address": "Via del Corso, Roma, Italia", - "geometry": { - "type": "Point", - "coordinates": [12.512124, 41.898903] - }, - "type": "outdoor" - } - }' - -**Note:** You can also specify the ``geometry`` in **Well-known text -(WKT)** format, like following: - -.. code-block:: json - - { - "location": { - "name": "Via del Corso", - "address": "Via del Corso, Roma, Italia", - "geometry": "POINT (12.512124 41.898903)", - "type": "outdoor", - } - } - -Similarly, you can create ``Floorplan`` object with the same request. But, -note that a ``FloorPlan`` can be added to ``DeviceLocation`` only if the -related ``Location`` object defines an indoor location. The example below -demonstrates creating both ``Location`` and ``FloorPlan`` objects. - -.. code-block:: text - - // This is not a valid JSON object. The JSON format is - // only used for showing available fields. - { - "location.name": "Via del Corso", - "location.address": "Via del Corso, Roma, Italia", - "location.geometry.type": "Point", - "location.geometry.coordinates": [12.512124, 41.898903] - "location.type": "outdoor", - "floorplan.floor": 1, - "floorplan.image": floorplan.png, - } - -.. code-block:: text - - curl -X PUT \ - http://127.0.0.1:8000/api/v1/controller/device/8a85cc23-bad5-4c7e-b9f4-ffe298defb5c/location/ \ - -H 'authorization: Bearer dc8d497838d4914c9db9aad9b6ec66f6c36ff46b' \ - -H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' \ - -F 'location.name=Via del Corso' \ - -F 'location.address=Via del Corso, Roma, Italia' \ - -F location.geometry.type=Point \ - -F 'location.geometry.coordinates=[12.512124, 41.898903]' \ - -F location.type=indoor \ - -F floorplan.floor=1 \ - -F 'floorplan.image=@floorplan.png' - -**Note:** The request in above example uses ``multipart content-type`` for -uploading floorplan image. - -You can also use an existing ``Location`` object and create a new -floorplan for that location using this endpoint. - -.. code-block:: text - - // This is not a valid JSON object. The JSON format is - // only used for showing available fields. - { - "location": "f0cb5762-3711-4791-95b6-c2f6656249fa", - "floorplan.floor": 1, - "floorplan.image": floorplan.png - } - -.. code-block:: text - - curl -X PUT \ - http://127.0.0.1:8000/api/v1/controller/device/8a85cc23-bad5-4c7e-b9f4-ffe298defb5c/location/ \ - -H 'authorization: Bearer dc8d497838d4914c9db9aad9b6ec66f6c36ff46b' \ - -H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' \ - -F location=f0cb5762-3711-4791-95b6-c2f6656249fa \ - -F floorplan.floor=1 \ - -F 'floorplan.image=@floorplan.png' - -Change details of device location -+++++++++++++++++++++++++++++++++ - -.. code-block:: text - - PUT /api/v1/controller/device/{id}/location/ - -**Note:** This endpoint can be used to update related ``Location`` and -``Floorplan`` objects. Refer `examples of "Create device location" section -for information on payload format <#create-device-location>`_. - -Delete device location -++++++++++++++++++++++ - -.. code-block:: text - - DELETE /api/v1/controller/device/{id}/location/ - -Get device coordinates -++++++++++++++++++++++ - -.. code-block:: text - - GET /api/v1/controller/device/{id}/coordinates/ - -**Note:** This endpoint is intended to be used by devices. - -This endpoint skips multi-tenancy and permission checks if the device -``key`` is passed as ``query_param`` because the system assumes that the -device is updating it's position. - -.. code-block:: text - - curl -X GET \ - 'http://127.0.0.1:8000/api/v1/controller/device/8a85cc23-bad5-4c7e-b9f4-ffe298defb5c/coordinates/?key=10a0cb5a553c71099c0e4ef236435496' - -Update device coordinates -+++++++++++++++++++++++++ - -.. code-block:: text - - PUT /api/v1/controller/device/{id}/coordinates/ - -**Note:** This endpoint is intended to be used by devices. - -This endpoint skips multi-tenancy and permission checks if the device -``key`` is passed as ``query_param`` because the system assumes that the -device is updating it's position. - -.. code-block:: json - - { - "type": "Feature", - "geometry": { - "type": "Point", - "coordinates": [12.512124, 41.898903] - }, - } - -.. code-block:: text - - curl -X PUT \ - 'http://127.0.0.1:8000/api/v1/controller/device/8a85cc23-bad5-4c7e-b9f4-ffe298defb5c/coordinates/?key=10a0cb5a553c71099c0e4ef236435496' \ - -H 'content-type: application/json' \ - -d '{ - "type": "Feature", - "geometry": { - "type": "Point", - "coordinates": [12.512124, 41.898903] - }, - }' - -List locations -++++++++++++++ - -.. code-block:: text - - GET /api/v1/controller/location/ - -**Available filters** - -You can filter using ``organization_id`` or ``organization_slug`` to get -list locations that belongs to an organization. - -.. code-block:: text - - GET /api/v1/controller/location/?organization={organization_id} - -.. code-block:: text - - GET /api/v1/controller/location/?organization_slug={organization_slug} - -Create location -+++++++++++++++ - -.. code-block:: text - - POST /api/v1/controller/location/ - -If you are creating an ``indoor`` location, you can use this endpoint to -create floorplan for the location. - -The following example demonstrates creating floorplan along with location -in a single request. - -.. code-block:: text - - { - "name": "Via del Corso", - "address": "Via del Corso, Roma, Italia", - "geometry.type": "Point", - "geometry.location": [12.512124, 41.898903], - "type": "indoor", - "is_mobile": "false", - "floorplan.floor": "1", - "floorplan.image": floorplan.png, - "organization": "1f6c5666-1011-4f1d-bce9-fc6fcb4f3a05" - } - -.. code-block:: text - - curl -X POST \ - http://127.0.0.1:8000/api/v1/controller/location/ \ - -H 'authorization: Bearer dc8d497838d4914c9db9aad9b6ec66f6c36ff46b' \ - -H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' \ - -F 'name=Via del Corso' \ - -F 'address=Via del Corso, Roma, Italia' \ - -F geometry.type=Point \ - -F 'geometry.coordinates=[12.512124, 41.898903]' \ - -F type=indoor \ - -F is_mobile=false \ - -F floorplan.floor=1 \ - -F 'floorplan.image=@floorplan.png' \ - -F organization=1f6c5666-1011-4f1d-bce9-fc6fcb4f3a05 - -**Note:** You can also specify the ``geometry`` in **Well-known text -(WKT)** format, like following: - -.. code-block:: text - - { - "name": "Via del Corso", - "address": "Via del Corso, Roma, Italia", - "geometry": "POINT (12.512124 41.898903)", - "type": "indoor", - "is_mobile": "false", - "floorplan.floor": "1", - "floorplan.image": floorplan.png, - "organization": "1f6c5666-1011-4f1d-bce9-fc6fcb4f3a05" - } - -Get location details -++++++++++++++++++++ - -.. code-block:: text - - GET /api/v1/controller/location/{pk}/ - -Change location details -+++++++++++++++++++++++ - -.. code-block:: text - - PUT /api/v1/controller/location/{pk}/ - -**Note**: Only the first floorplan data present can be edited or changed. -Setting the ``type`` of location to outdoor will remove all the floorplans -associated with it. - -Refer `examples of "Create location" section for information on payload -format <#create-location>`_. - -Delete location -+++++++++++++++ - -.. code-block:: text - - DELETE /api/v1/controller/location/{pk}/ - -List devices in a location -++++++++++++++++++++++++++ - -.. code-block:: text - - GET /api/v1/controller/location/{id}/device/ - -List locations with devices deployed (in GeoJSON format) -++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -**Note**: this endpoint will only list locations that have been assigned -to a device. - -.. code-block:: text - - GET /api/v1/controller/location/geojson/ - -**Available filters** - -You can filter using ``organization_id`` or ``organization_slug`` to get -list location of devices from that organization. - -.. code-block:: text - - GET /api/v1/controller/location/geojson/?organization_id={organization_id} - -.. code-block:: text - - GET /api/v1/controller/location/geojson/?organization_slug={organization_slug} - -List floorplans -+++++++++++++++ - -.. code-block:: text - - GET /api/v1/controller/floorplan/ - -**Available filters** - -You can filter using ``organization_id`` or ``organization_slug`` to get -list floorplans that belongs to an organization. - -.. code-block:: text - - GET /api/v1/controller/floorplan/?organization={organization_id} - -.. code-block:: text - - GET /api/v1/controller/floorplan/?organization_slug={organization_slug} - -Create floorplan -++++++++++++++++ - -.. code-block:: text - - POST /api/v1/controller/floorplan/ - -Get floorplan details -+++++++++++++++++++++ - -.. code-block:: text - - GET /api/v1/controller/floorplan/{pk}/ - -Change floorplan details -++++++++++++++++++++++++ - -.. code-block:: text - - PUT /api/v1/controller/floorplan/{pk}/ - -Delete floorplan -++++++++++++++++ - -.. code-block:: text - - DELETE /api/v1/controller/floorplan/{pk}/ - -List templates -++++++++++++++ - -.. code-block:: text - - GET /api/v1/controller/template/ - -**Available filters** - -You can filter a list of templates based on their organization using the -``organization_id`` or ``organization_slug``. - -.. code-block:: text - - GET /api/v1/controller/template/?organization={organization_id} - -.. code-block:: text - - GET /api/v1/controller/template/?organization_slug={organization_slug} - -You can filter a list of templates based on their backend using the -``backend`` (e.g netjsonconfig.OpenWrt or netjsonconfig.OpenWisp). - -.. code-block:: text - - GET /api/v1/controller/template/?backend={backend} - -You can filter a list of templates based on their type using the ``type`` -(eg. vpn or generic). - -.. code-block:: text - - GET /api/v1/controller/template/?type={type} - -You can filter a list of templates that are enabled by default or not -using the ``default`` (eg. true or false). - -.. code-block:: text - - GET /api/v1/controller/template/?default={default} - -You can filter a list of templates that are required or not using the -``required`` (eg. true or false). - -.. code-block:: text - - GET /api/v1/controller/template/?required={required} - -You can filter a list of templates based on their creation time using the -``creation_time``. - -.. code-block:: text - - # Created exact - - GET /api/v1/controller/template/?created={creation_time} - - # Created greater than or equal to - - GET /api/v1/controller/template/?created__gte={creation_time} - - # Created is less than - - GET /api/v1/controller/template/?created__lt={creation_time} - -Create template -+++++++++++++++ - -.. code-block:: text - - POST /api/v1/controller/template/ - -Get template detail -+++++++++++++++++++ - -.. code-block:: text - - GET /api/v1/controller/template/{id}/ - -Download template configuration -+++++++++++++++++++++++++++++++ - -.. code-block:: text - - GET /api/v1/controller/template/{id}/configuration/ - -The above endpoint triggers the download of a ``tar.gz`` file containing -the generated configuration for that specific template. - -Change details of template -++++++++++++++++++++++++++ - -.. code-block:: text - - PUT /api/v1/controller/template/{id}/ - -Patch details of template -+++++++++++++++++++++++++ - -.. code-block:: text - - PATCH /api/v1/controller/template/{id}/ - -Delete template -+++++++++++++++ - -.. code-block:: text - - DELETE /api/v1/controller/template/{id}/ - -List VPNs -+++++++++ - -.. code-block:: text - - GET /api/v1/controller/vpn/ - -**Available filters** - -You can filter a list of vpns based on their backend using the ``backend`` -(e.g openwisp_controller.vpn_backends.OpenVpn or -openwisp_controller.vpn_backends.Wireguard). - -.. code-block:: text - - GET /api/v1/controller/vpn/?backend={backend} - -You can filter a list of vpns based on their subnet using the -``subnet_id``. - -.. code-block:: text - - GET /api/v1/controller/vpn/?subnet={subnet_id} - -You can filter a list of vpns based on their organization using the -``organization_id`` or ``organization_slug``. - -.. code-block:: text - - GET /api/v1/controller/vpn/?organization={organization_id} - -.. code-block:: text - - GET /api/v1/controller/vpn/?organization_slug={organization_slug} - -Create VPN -++++++++++ - -.. code-block:: text - - POST /api/v1/controller/vpn/ - -Get VPN detail -++++++++++++++ - -.. code-block:: text - - GET /api/v1/controller/vpn/{id}/ - -Download VPN configuration -++++++++++++++++++++++++++ - -.. code-block:: text - - GET /api/v1/controller/vpn/{id}/configuration/ - -The above endpoint triggers the download of a ``tar.gz`` file containing -the generated configuration for that specific VPN. - -Change details of VPN -+++++++++++++++++++++ - -.. code-block:: text - - PUT /api/v1/controller/vpn/{id}/ - -Patch details of VPN -++++++++++++++++++++ - -.. code-block:: text - - PATCH /api/v1/controller/vpn/{id}/ - -Delete VPN -++++++++++ - -.. code-block:: text - - DELETE /api/v1/controller/vpn/{id}/ - -List CA -+++++++ - -.. code-block:: text - - GET /api/v1/controller/ca/ - -Create new CA -+++++++++++++ - -.. code-block:: text - - POST /api/v1/controller/ca/ - -Import existing CA -++++++++++++++++++ - -.. code-block:: text - - POST /api/v1/controller/ca/ - -**Note**: To import an existing CA, only ``name``, ``certificate`` and -``private_key`` fields have to be filled in the ``HTML`` form or included -in the ``JSON`` format. - -Get CA Detail -+++++++++++++ - -.. code-block:: text - - GET /api/v1/controller/ca/{id}/ - -Change details of CA -++++++++++++++++++++ - -.. code-block:: text - - PUT /api/v1/controller/ca/{id}/ - -Patch details of CA -+++++++++++++++++++ - -.. code-block:: text - - PATCH /api/v1/controller/ca/{id}/ - -Download CA(crl) -++++++++++++++++ - -.. code-block:: text - - GET /api/v1/controller/ca/{id}/crl/ - -The above endpoint triggers the download of ``{id}.crl`` file containing -up to date CRL of that specific CA. - -Delete CA -+++++++++ - -.. code-block:: text - - DELETE /api/v1/controller/ca/{id}/ - -Renew CA -++++++++ - -.. code-block:: text - - POST /api/v1/controller/ca/{id}/renew/ - -List Cert -+++++++++ - -.. code-block:: text - - GET /api/v1/controller/cert/ - -Create new Cert -+++++++++++++++ - -.. code-block:: text - - POST /api/v1/controller/cert/ - -Import existing Cert -++++++++++++++++++++ - -.. code-block:: text - - POST /api/v1/controller/cert/ - -**Note**: To import an existing Cert, only ``name``, ``ca``, -``certificate`` and ``private_key`` fields have to be filled in the -``HTML`` form or included in the ``JSON`` format. - -Get Cert Detail -+++++++++++++++ - -.. code-block:: text - - GET /api/v1/controller/cert/{id}/ - -Change details of Cert -++++++++++++++++++++++ - -.. code-block:: text - - PUT /api/v1/controller/cert/{id}/ - -Patch details of Cert -+++++++++++++++++++++ - -.. code-block:: text - - PATCH /api/v1/controller/cert/{id}/ - -Delete Cert -+++++++++++ - -.. code-block:: text - - DELETE /api/v1/controller/cert/{id}/ - -Renew Cert -++++++++++ - -.. code-block:: text - - POST /api/v1/controller/cert/{id}/renew/ - -Revoke Cert -+++++++++++ - -.. code-block:: text - - POST /api/v1/controller/cert/{id}/revoke/ - -Settings --------- - -You can change the values for the following variables in ``settings.py`` -to configure your instance of openwisp-controller. - -``OPENWISP_SSH_AUTH_TIMEOUT`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ =========== -**type**: ``int`` -**default**: ``2`` -**unit**: ``seconds`` -============ =========== - -Configure timeout to wait for an authentication response when establishing -a SSH connection. - -``OPENWISP_SSH_BANNER_TIMEOUT`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ =========== -**type**: ``int`` -**default**: ``60`` -**unit**: ``seconds`` -============ =========== - -Configure timeout to wait for the banner to be presented when establishing -a SSH connection. - -``OPENWISP_SSH_COMMAND_TIMEOUT`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ =========== -**type**: ``int`` -**default**: ``30`` -**unit**: ``seconds`` -============ =========== - -Configure timeout on blocking read/write operations when executing a -command in a SSH connection. - -``OPENWISP_SSH_CONNECTION_TIMEOUT`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ =========== -**type**: ``int`` -**default**: ``5`` -**unit**: ``seconds`` -============ =========== - -Configure timeout for the TCP connect when establishing a SSH connection. - -``OPENWISP_CONNECTORS`` -~~~~~~~~~~~~~~~~~~~~~~~ - -============ ================================================================================= -**type**: ``tuple`` -**default**: .. code-block:: python - - ( - ("openwisp_controller.connection.connectors.ssh.Ssh", "SSH"), - ( - "openwisp_controller.connection.connectors.openwrt.snmp.OpenWRTSnmp", - "OpenWRT SNMP", - ), - ( - "openwisp_controller.connection.connectors.airos.snmp.AirOsSnmp", - "Ubiquiti AirOS SNMP", - ), - ) -============ ================================================================================= - -Available connector classes. Connectors are python classes that specify -ways in which OpenWISP can connect to devices in order to launch commands. - -``OPENWISP_UPDATE_STRATEGIES`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ============================================================================ -**type**: ``tuple`` -**default**: .. code-block:: python - - ( - ( - "openwisp_controller.connection.connectors.openwrt.ssh.OpenWrt", - "OpenWRT SSH", - ), - ) -============ ============================================================================ - -Available update strategies. An update strategy is a subclass of a -connector class which defines an ``update_config`` method which is in -charge of updating the configuration of the device. - -This operation is launched in a background worker when the configuration -of a device is changed. - -It's possible to write custom update strategies and add them to this -setting to make them available in OpenWISP. - -``OPENWISP_CONFIG_UPDATE_MAPPING`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ================================================================== -**type**: ``dict`` -**default**: .. code-block:: python - - { - "netjsonconfig.OpenWrt": OPENWISP_UPDATE_STRATEGIES[0][0], - } -============ ================================================================== - -A dictionary that maps configuration backends to update strategies in -order to automatically determine the update strategy of a device -connection if the update strategy field is left blank by the user. - -``OPENWISP_CONTROLLER_BACKENDS`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ =============================================== -**type**: ``tuple`` -**default**: .. code-block:: python - - ( - ("netjsonconfig.OpenWrt", "OpenWRT"), - ("netjsonconfig.OpenWisp", "OpenWISP"), - ) -============ =============================================== - -Available configuration backends. For more information, see `netjsonconfig -backends -`_. - -``OPENWISP_CONTROLLER_VPN_BACKENDS`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ==================================================================== -**type**: ``tuple`` -**default**: .. code-block:: python - - ( - ("openwisp_controller.vpn_backends.OpenVpn", "OpenVPN"), - ("openwisp_controller.vpn_backends.Wireguard", "WireGuard"), - ( - "openwisp_controller.vpn_backends.VxlanWireguard", - "VXLAN over WireGuard", - ), - ("openwisp_controller.vpn_backends.ZeroTier", "ZeroTier"), - ) -============ ==================================================================== - -Available VPN backends for VPN Server objects. For more information, see -`netjsonconfig VPN backends -`_. - -A VPN backend must follow some basic rules in order to be compatible with -*openwisp-controller*: - -- it MUST allow at minimum and at maximum one VPN instance -- the main *NetJSON* property MUST match the lowercase version of the - class name, eg: when using the ``OpenVpn`` backend, the system will look - into ``config['openvpn']`` -- it SHOULD focus on the server capabilities of the VPN software being - used - -``OPENWISP_CONTROLLER_DEFAULT_BACKEND`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ====================================== -**type**: ``str`` -**default**: ``OPENWISP_CONTROLLER_BACKENDS[0][0]`` -============ ====================================== - -The preferred backend that will be used as initial value when adding new -``Config`` or ``Template`` objects in the admin. - -This setting defaults to the raw value of the first item in the -``OPENWISP_CONTROLLER_BACKENDS`` setting, which is -``netjsonconfig.OpenWrt``. - -Setting it to ``None`` will force the user to choose explicitly. - -``OPENWISP_CONTROLLER_DEFAULT_VPN_BACKEND`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ========================================== -**type**: ``str`` -**default**: ``OPENWISP_CONTROLLER_VPN_BACKENDS[0][0]`` -============ ========================================== - -The preferred backend that will be used as initial value when adding new -``Vpn`` objects in the admin. - -This setting defaults to the raw value of the first item in the -``OPENWISP_CONTROLLER_VPN_BACKENDS`` setting, which is -``openwisp_controller.vpn_backends.OpenVpn``. - -Setting it to ``None`` will force the user to choose explicitly. - -``OPENWISP_CONTROLLER_REGISTRATION_ENABLED`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ======== -**type**: ``bool`` -**default**: ``True`` -============ ======== - -Whether devices can automatically register through the controller or not. - -This feature is enabled by default. - -Autoregistration must be supported on the devices in order to work, see -`openwisp-config automatic registration -`_ for -more information. - -``OPENWISP_CONTROLLER_CONSISTENT_REGISTRATION`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ======== -**type**: ``bool`` -**default**: ``True`` -============ ======== - -Whether devices that are already registered are recognized when reflashed -or reset, hence keeping the existing configuration without creating a new -one. - -This feature is enabled by default. - -Autoregistration must be enabled also on the devices in order to work, see -`openwisp-config consistent key generation -`_ -for more information. - -``OPENWISP_CONTROLLER_REGISTRATION_SELF_CREATION`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ======== -**type**: ``bool`` -**default**: ``True`` -============ ======== - -Whether devices that are not already present in the system are allowed to -register or not. - -Turn this off if you still want to use auto-registration to avoid having -to manually set the device UUID and key in its configuration file but also -want to avoid indiscriminate registration of new devices without explicit -permission. - -``OPENWISP_CONTROLLER_CONTEXT`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ======== -**type**: ``dict`` -**default**: ``{}`` -============ ======== - -Additional context that is passed to the default context of each device -object. - -``OPENWISP_CONTROLLER_CONTEXT`` can be used to define system-wide -configuration variables. - -For more information regarding how to use configuration variables in -OpenWISP, see `How to use configuration variables -<#how-to-use-configuration-variables>`_. - -For technical information about how variables are handled in the lower -levels of OpenWISP, see `netjsonconfig context: configuration variables -`_. - -``OPENWISP_CONTROLLER_DEFAULT_AUTO_CERT`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ======== -**type**: ``bool`` -**default**: ``True`` -============ ======== - -The default value of the ``auto_cert`` field for new ``Template`` objects. - -The ``auto_cert`` field is valid only for templates which have ``type`` -set to ``VPN`` and indicates whether configuration regarding the VPN -tunnel is provisioned automatically to each device using the template, eg: - -- when using OpenVPN, new `x509 `_ - certificates will be generated automatically using the same CA assigned - to the related VPN object -- when using WireGuard, new pair of private and public keys (using - `Curve25519 `_) will be generated, as well as - an IP address of the subnet assigned to the related VPN object -- when using `VXLAN `_ tunnels over - Wireguad, in addition to the configuration generated for WireGuard, a - new VID will be generated automatically for each device if the - configuration option "auto VNI" is turned on in the VPN object - -All these auto generated configuration options will be available as -template variables. - -The objects that are automatically created will also be removed when they -are not needed anymore (eg: when the VPN template is removed from a -configuration object). - -``OPENWISP_CONTROLLER_CERT_PATH`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ============= -**type**: ``str`` -**default**: ``/etc/x509`` -============ ============= - -The filesystem path where x509 certificate will be installed when -downloaded on routers when ``auto_cert`` is being used (enabled by -default). - -``OPENWISP_CONTROLLER_COMMON_NAME_FORMAT`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ======================== -**type**: ``str`` -**default**: ``{mac_address}-{name}`` -============ ======================== - -Defines the format of the ``common_name`` attribute of VPN client -certificates that are automatically created when using VPN templates which -have ``auto_cert`` set to ``True``. A unique slug generated using -`shortuuid `_ is appended to -the common name to introduce uniqueness. Therefore, resulting common names -will have ``{OPENWISP_CONTROLLER_COMMON_NAME_FORMAT}-{unique-slug}`` -format. - -**Note:** If the ``name`` and ``mac address`` of the device are equal, the -``name`` of the device will be omitted from the common name to avoid -redundancy. - -``OPENWISP_CONTROLLER_MANAGEMENT_IP_DEVICE_LIST`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ======== -**type**: ``bool`` -**default**: ``True`` -============ ======== - -In the device list page, the column ``IP`` will show the ``management_ip`` -if available, defaulting to ``last_ip`` otherwise. - -If this setting is set to ``False`` the ``management_ip`` won't be shown -in the device list page even if present, it will be shown only in the -device detail page. - -You may set this to ``False`` if for some reason the majority of your user -doesn't care about the management ip address. - -``OPENWISP_CONTROLLER_CONFIG_BACKEND_FIELD_SHOWN`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ======== -**type**: ``bool`` -**default**: ``True`` -============ ======== - -This setting toggles the ``backend`` fields in add/edit pages in Device -and Template configuration, as well as the ``backend`` field/filter in -Device list and Template list. - -If this setting is set to ``False`` these items will be removed from the -UI. - -Note: This setting affects only the configuration backend and NOT the VPN -backend. - -``OPENWISP_CONTROLLER_DEVICE_NAME_UNIQUE`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ======== -**type**: ``bool`` -**default**: ``True`` -============ ======== - -This setting conditionally enforces unique Device names in an -Organization. The query to enforce this is case-insensitive. - -Note: For this constraint to be optional, it is enforced on an application -level and not on database. - -``OPENWISP_CONTROLLER_HARDWARE_ID_ENABLED`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ========= -**type**: ``bool`` -**default**: ``False`` -============ ========= - -The field ``hardware_id`` can be used to store a unique hardware id, for -example a serial number. - -If this setting is set to ``True`` then this field will be shown first in -the device list page and in the add/edit device page. - -This feature is disabled by default. - -``OPENWISP_CONTROLLER_HARDWARE_ID_OPTIONS`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ============================================================= -**type**: ``dict`` -**default**: .. code-block:: python - - { - "blank": not OPENWISP_CONTROLLER_HARDWARE_ID_ENABLED, - "null": True, - "max_length": 32, - "unique": True, - "verbose_name": _("Serial number"), - "help_text": _("Serial number of this device"), - } -============ ============================================================= - -Options for the model field ``hardware_id``. - -- ``blank``: wether the field is allowed to be blank -- ``null``: wether an empty value will be stored as ``NULL`` in the - database -- ``max_length``: maximum length of the field -- ``unique``: wether the value of the field must be unique -- ``verbose_name``: text for the human readable label of the field -- ``help_text``: help text to be displayed with the field - -``OPENWISP_CONTROLLER_HARDWARE_ID_AS_NAME`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ======== -**type**: ``bool`` -**default**: ``True`` -============ ======== - -When the hardware ID feature is enabled, devices will be referenced with -their hardware ID instead of their name. - -If you still want to reference devices by their name, set this to -``False``. - -``OPENWISP_CONTROLLER_DEVICE_VERBOSE_NAME`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ========================= -**type**: ``tuple`` -**default**: ``('Device', 'Devices')`` -============ ========================= - -Defines the ``verbose_name`` attribute of the ``Device`` model, which is -displayed in the admin site. The first and second element of the tuple -represent the singular and plural forms. - -For example, if we want to change the verbose name to "Hotspot", we could -write: - -.. code-block:: python - - OPENWISP_CONTROLLER_DEVICE_VERBOSE_NAME = ("Hotspot", "Hotspots") - -``OPENWISP_CONTROLLER_HIDE_AUTOMATICALLY_GENERATED_SUBNETS_AND_IPS`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ========= -**type**: ``bool`` -**default**: ``False`` -============ ========= - -Setting this to ``True`` will hide subnets and IPs generated using `subnet -division rules <#subnet-division-app>`_ from being displayed on the -changelist view of Subnet and IP admin. - -``OPENWISP_CONTROLLER_SUBNET_DIVISION_TYPES`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ================================================================================================= -**type**: ``tuple`` -**default**: .. code-block:: python - - ( - ( - "openwisp_controller.subnet_division.rule_types.device.DeviceSubnetDivisionRuleType", - "Device", - ), - ( - "openwisp_controller.subnet_division.rule_types.vpn.VpnSubnetDivisionRuleType", - "VPN", - ), - ) -============ ================================================================================================= - -`Available types for Subject Division Rule -<#device-subnet-division-rule>`_ objects. For more information on how to -write your own types, read `"Custom Subnet Division Rule Types" section of -this documentation <#custom-subnet-division-rule-types>`_ - -``OPENWISP_CONTROLLER_API`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ======== -**type**: ``bool`` -**default**: ``True`` -============ ======== - -Indicates whether the API for Openwisp Controller is enabled or not. To -disable the API by default add `OPENWISP_CONTROLLER_API = False` in -`settings.py` file. - -``OPENWISP_CONTROLLER_API_HOST`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ======== -**type**: ``str`` -**default**: ``None`` -============ ======== - -Allows to specify backend URL for API requests, if the frontend is hosted -separately. - -``OPENWISP_CONTROLLER_USER_COMMANDS`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ======== -**type**: ``list`` -**default**: ``[]`` -============ ======== - -Allows to specify a ``list`` of tuples for adding commands as described in -`'How to define custom commands" -<#how-to-define-new-options-in-the-commands-menu>`_ section. - -``OPENWISP_CONTROLLER_ORGANIZATION_ENABLED_COMMANDS`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ============================================= -**type**: ``dict`` -**default**: .. code-block:: python - - { - # By default all commands are allowed - "__all__": "*", - } -============ ============================================= - -This setting controls the command types that are enabled on the system By -default, all command types are enabled to all the organizations, but it's -possible to disable a specific command for a specific organization as -shown in the following example: - -.. code-block:: python - - OPENWISP_CONTROLLER_ORGANIZATION_ENABLED_COMMANDS = { - "__all__": "*", - # Organization UUID: # Tuple of enabled commands - "7448a190-6e65-42bf-b8ea-bb6603e593a5": ("reboot", "change_password"), - } - -In the example above, the organization with UUID -``7448a190-6e65-42bf-b8ea-bb6603e593a5`` will allow to send only commands -of type ``reboot`` and ``change_password``, while all the other -organizations will have all command types enabled. - -``OPENWISP_CONTROLLER_DEVICE_GROUP_SCHEMA`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ======================================== -**type**: ``dict`` -**default**: ``{'type': 'object', 'properties': {}}`` -============ ======================================== - -Allows specifying JSONSchema used for validating meta-data of `Device -Group <#device-groups>`__. - -``OPENWISP_CONTROLLER_SHARED_MANAGEMENT_IP_ADDRESS_SPACE`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ======== -**type**: ``bool`` -**default**: ``True`` -============ ======== - -By default, the system assumes that the address space of the management -tunnel is shared among all the organizations using the system, that is, -the system assumes there's only one management VPN, tunnel or other -networking technology to reach the devices it controls. - -When set to ``True``, any device belonging to any organization will never -have the same ``management_ip`` as another device, the latest device -declaring the management IP will take the IP and any other device who -declared the same IP in the past will have the field reset to empty state -to avoid potential conflicts. - -Set this to ``False`` if every organization has its dedicated management -tunnel with a dedicated address space that is reachable by the OpenWISP -server. - -``OPENWISP_CONTROLLER_MANAGEMENT_IP_ONLY`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ======== -**type**: ``bool`` -**default**: ``True`` -============ ======== - -By default, only the management IP will be used to establish connection -with the devices. - -If the devices are connecting to your OpenWISP instance using a shared -layer2 network, hence the OpenWSP server can reach the devices using the -``last_ip`` field, you can set this to ``False``. - -``OPENWISP_CONTROLLER_DSA_OS_MAPPING`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ======== -**type**: ``dict`` -**default**: ``{}`` -============ ======== - -OpenWISP Controller can figure out whether it should use the new OpenWrt -syntax for DSA interfaces (Distributed Switch Architecture) introduced in -OpenWrt 21 by reading the ``os`` field of the ``Device`` object. However, -if the firmware you are using has a custom firmware identifier, the system -will not be able to figure out whether it should use the new syntax and it -will default to `OPENWISP_CONTROLLER_DSA_DEFAULT_FALLBACK -<#openwisp_controller_dsa_default_fallback>`_. - -If you want to make sure the system can parse your custom firmware -identifier properly, you can follow the example below. - -For the sake of the example, the OS identifier ``MyCustomFirmware 2.0`` -corresponds to ``OpenWrt 19.07``, while ``MyCustomFirmware 2.1`` -corresponds to ``OpenWrt 21.02``. Configuring this setting as indicated -below will allow OpenWISP to supply the right syntax automatically. - -Example: - -.. code-block:: python - - OPENWISP_CONTROLLER_DSA_OS_MAPPING = { - "netjsonconfig.OpenWrt": { - # OpenWrt >=21.02 configuration syntax will be used for - # these OS identifiers. - ">=21.02": [r"MyCustomFirmware 2.1(.*)"], - # OpenWrt <=21.02 configuration syntax will be used for - # these OS identifiers. - "<21.02": [r"MyCustomFirmware 2.0(.*)"], - } - } - -**Note**: The OS identifier should be a regular expression as shown in -above example. - -``OPENWISP_CONTROLLER_DSA_DEFAULT_FALLBACK`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ======== -**type**: ``bool`` -**default**: ``True`` -============ ======== - -The value of this setting decides whether to use DSA syntax (OpenWrt >=21 -configuration syntax) if openwisp-controller fails to make that decision -automatically. - -``OPENWISP_CONTROLLER_GROUP_PIE_CHART`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ========= -**type**: ``bool`` -**default**: ``False`` -============ ========= - -Allows to show a pie chart like the one in the screenshot. - -.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/devicegroups-piechart.png - :alt: device groups piechart - -Active groups are groups which have at least one device in them, while -emtpy groups do not have any device assigned. - -``OPENWISP_CONTROLLER_API_TASK_RETRY_OPTIONS`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============ ========= -**type**: ``dict`` -**default**: see below -============ ========= - -.. code-block:: python - - # default value of OPENWISP_CONTROLLER_API_TASK_RETRY_OPTIONS: - - dict( - max_retries=5, # total number of retries - retry_backoff=True, # exponential backoff - retry_backoff_max=600, # 10 minutes - retry_jitter=True, # randomness into exponential backoff - ) - -This setting is utilized by background API tasks executed by `ZeroTier VPN -servers and ZeroTier VPN clients <#how-to-setup-zerotier-tunnels>`_ to -handle recoverable HTTP status codes such as 429, 500, 502, 503, and 504. -These tasks are retried with a maximum of 5 attempts with an exponential -backoff and jitter, with a maximum delay of 10 minutes. - -This feature ensures that ZeroTier Service API calls are resilient to -recoverable failures, improving the reliability of the system. - -For more information on these settings, you can refer to the `the celery -documentation regarding automatic retries for known errors. -`_ - -Signals -------- - -``config_modified`` -~~~~~~~~~~~~~~~~~~~ - -**Path**: ``openwisp_controller.config.signals.config_modified`` - -**Arguments**: - -- ``instance``: instance of ``Config`` which got its ``config`` modified -- ``previous_status``: indicates the status of the config object before - the signal was emitted -- ``action``: action which emitted the signal, can be any of the list - below: - ``config_changed``: the configuration of the config object was - changed - ``related_template_changed``: the configuration of a related - template was changed - ``m2m_templates_changed``: the assigned templates - were changed (either templates were added, removed or their order was - changed) - -This signal is emitted every time the configuration of a device is -modified. - -It does not matter if ``Config.status`` is already modified, this signal -will be emitted anyway because it signals that the device configuration -has changed. - -This signal is used to trigger the update of the configuration on devices, -when the push feature is enabled (requires Device credentials). - -The signal is also emitted when one of the templates used by the device is -modified or if the templates assigned to the device are changed. - -Special cases in which ``config_modified`` is not emitted -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -This signal is not emitted when the device is created for the first time. - -It is also not emitted when templates assigned to a config object are -cleared (``post_clear`` m2m signal), this is necessary because `sortedm2m -`_, the package we use to -implement ordered templates, uses the clear action to reorder templates -(m2m relationships are first cleared and then added back), therefore we -ignore ``post_clear`` to avoid emitting signals twice (one for the clear -action and one for the add action). Please keep this in mind if you plan -on using the clear method of the m2m manager. - -``config_status_changed`` -~~~~~~~~~~~~~~~~~~~~~~~~~ - -**Path**: ``openwisp_controller.config.signals.config_status_changed`` - -**Arguments**: - -- ``instance``: instance of ``Config`` which got its ``status`` changed - -This signal is emitted only when the configuration status of a device has -changed. - -The signal is emitted also when the m2m template relationships of a config -object are changed, but only on ``post_add`` or ``post_remove`` actions, -``post_clear`` is ignored for the same reason explained in the previous -section. - -``config_backend_changed`` -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**Path**: ``openwisp_controller.config.signals.config_backend_changed`` -**Arguments**: - -- ``instance``: instance of ``Config`` which got its ``backend`` changed -- ``old_backend``: the old backend of the config object -- ``backend``: the new backend of the config object - -It is not emitted when the device or config is created. - -``checksum_requested`` -~~~~~~~~~~~~~~~~~~~~~~ - -**Path**: ``openwisp_controller.config.signals.checksum_requested`` - -**Arguments**: - -- ``instance``: instance of ``Device`` for which its configuration - checksum has been requested -- ``request``: the HTTP request object - -This signal is emitted when a device requests a checksum via the -controller views. - -The signal is emitted just before a successful response is returned, it is -not sent if the response was not successful. - -``config_download_requested`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**Path**: ``openwisp_controller.config.signals.config_download_requested`` - -**Arguments**: - -- ``instance``: instance of ``Device`` for which its configuration has - been requested for download -- ``request``: the HTTP request object - -This signal is emitted when a device requests to download its -configuration via the controller views. - -The signal is emitted just before a successful response is returned, it is -not sent if the response was not successful. - -``is_working_changed`` -~~~~~~~~~~~~~~~~~~~~~~ - -**Path**: ``openwisp_controller.connection.signals.is_working_changed`` - -**Arguments**: - -- ``instance``: instance of ``DeviceConnection`` -- ``is_working``: value of ``DeviceConnection.is_working`` -- ``old_is_working``: previous value of ``DeviceConnection.is_working``, - either ``None`` (for new connections), ``True`` or ``False`` -- ``failure_reason``: error message explaining reason for failure in - establishing connection -- ``old_failure_reason``: previous value of - ``DeviceConnection.failure_reason`` - -This signal is emitted every time ``DeviceConnection.is_working`` changes. - -It is not triggered when the device is created for the first time. - -``management_ip_changed`` -~~~~~~~~~~~~~~~~~~~~~~~~~ - -**Path**: ``openwisp_controller.config.signals.management_ip_changed`` - -**Arguments**: - -- ``instance``: instance of ``Device`` -- ``management_ip``: value of ``Device.management_ip`` -- ``old_management_ip``: previous value of ``Device.management_ip`` - -This signal is emitted every time ``Device.management_ip`` changes. - -It is not triggered when the device is created for the first time. - -``device_registered`` -~~~~~~~~~~~~~~~~~~~~~ - -**Path**: ``openwisp_controller.config.signals.device_registered`` - -**Arguments**: - -- ``instance``: instance of ``Device`` which got registered. -- ``is_new``: boolean, will be ``True`` when the device is new, ``False`` - when the device already exists (eg: a device which gets a factory reset - will register again) - -This signal is emitted when a device registers automatically through the -controller HTTP API. - -``device_name_changed`` -~~~~~~~~~~~~~~~~~~~~~~~ - -**Path**: ``openwisp_controller.config.signals.device_name_changed`` - -**Arguments**: - -- ``instance``: instance of ``Device``. - -The signal is emitted when the device name changes. - -It is not emitted when the device is created. - -``device_group_changed`` -~~~~~~~~~~~~~~~~~~~~~~~~ - -**Path**: ``openwisp_controller.config.signals.device_group_changed`` - -**Arguments**: - -- ``instance``: instance of ``Device``. -- ``group_id``: primary key of ``DeviceGroup`` of ``Device`` -- ``old_group_id``: primary key of previous ``DeviceGroup`` of ``Device`` - -The signal is emitted when the device group changes. - -It is not emitted when the device is created. - -``group_templates_changed`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**Path**: ``openwisp_controller.config.signals.group_templates_changed`` - -**Arguments**: - -- ``instance``: instance of ``DeviceGroup``. -- ``templates``: list of ``Template`` objects assigned to ``DeviceGroup`` -- ``old_templates``: list of ``Template`` objects assigned earlier to - ``DeviceGroup`` - -The signal is emitted when the device group templates changes. - -It is not emitted when the device is created. - -``subnet_provisioned`` -~~~~~~~~~~~~~~~~~~~~~~ - -**Path**: -``openwisp_controller.subnet_division.signals.subnet_provisioned`` - -**Arguments**: - -- ``instance``: instance of ``VpnClient``. -- ``provisioned``: dictionary of ``Subnet`` and ``IpAddress`` provisioned, - ``None`` if nothing is provisioned - -The signal is emitted when subnets and IP addresses have been provisioned -for a ``VpnClient`` for a VPN server with a subnet with `subnet division -rule <#subnet-division-app>`_. - -``vpn_server_modified`` -~~~~~~~~~~~~~~~~~~~~~~~ - -**Path**: ``openwisp_controller.config.signals.vpn_server_modified`` - -**Arguments**: - -- ``instance``: instance of ``Vpn``. - -The signal is emitted when the VPN server is modified. - -``vpn_peers_changed`` -~~~~~~~~~~~~~~~~~~~~~ - -**Path**: ``openwisp_controller.config.signals.vpn_peers_changed`` - -**Arguments**: - -- ``instance``: instance of ``Vpn``. - -The signal is emitted when the peers of VPN server gets changed. - -It is only emitted for ``Vpn`` object with **WireGuard** or **VXLAN over -WireGuard** backend. - -Extending openwisp-controller ------------------------------ - -One of the core values of the OpenWISP project is `Software Reusability -`_, -for this reason *openwisp-controller* provides a set of base classes which -can be imported, extended and reused to create derivative apps. - -In order to implement your custom version of *openwisp-controller*, you -need to perform the steps described in this section. - -When in doubt, the code in the `test project -`_ -will serve you as source of truth: just replicate and adapt that code to -get a basic derivative of *openwisp-controller* working. - -If you want to add new users fields, please follow the `tutorial to extend -the openwisp-users -`_. As -an example, we have extended *openwisp-users* to *sample_users* app and -added a field ``social_security_number`` in the `sample_users/models.py -`_. - -**Premise**: if you plan on using a customized version of this module, we -suggest to start with it since the beginning, because migrating your data -from the default module to your extended version may be time consuming. - -1. Initialize your project & custom apps -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Firstly, to get started you need to create a django project: - -:: - - django-admin startproject mycontroller - -Now, you need to do is to create some new django apps which will contain -your custom version of *openwisp-controller*. - -A django project is a collection of django apps. There are 4 django apps -in the openwisp_controller project, namely config, pki, connection & geo. -You'll need to create 4 apps in your project for each app in -openwisp_controller. - -A django app is nothing more than a `python package -`_ (a directory -of python scripts), in the following examples we'll call these django app -``sample_config``, ``sample_pki``, ``sample_connection``, ``sample_geo`` & -``sample_subnet_division``. but you can name it how you want: - -:: - - django-admin startapp sample_config - django-admin startapp sample_pki - django-admin startapp sample_connection - django-admin startapp sample_geo - django-admin startapp sample_subnet_division - -Keep in mind that the command mentioned above must be called from a -directory which is available in your `PYTHON_PATH -`_ so that -you can then import the result into your project. - -For more information about how to work with django projects and django -apps, please refer to the `django documentation -`_. - -2. Install ``openwisp-controller`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Install (and add to the requirement of your project) openwisp-controller: - -:: - - pip install openwisp-controller - -3. Add your apps in INSTALLED_APPS -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Now you need to add ``mycontroller.sample_config``, -``mycontroller.sample_pki``, ``mycontroller.sample_connection``, -``mycontroller.sample_geo`` & ``mycontroller.sample_subnet_division`` to -``INSTALLED_APPS`` in your ``settings.py``, ensuring also that -``openwisp_controller.config``, ``openwisp_controller.geo``, -``openwisp_controller.pki``, ``openwisp_controller.connnection`` & -``openwisp_controller.subnet_division`` have been removed: - -.. code-block:: python - - # Remember: Order in INSTALLED_APPS is important. - INSTALLED_APPS = [ - # other django installed apps - "openwisp_utils.admin_theme", - "admin_auto_filters", - # all-auth - "django.contrib.sites", - "allauth", - "allauth.account", - "allauth.socialaccount", - # openwisp2 module - # 'openwisp_controller.config', <-- comment out or delete this line - # 'openwisp_controller.pki', <-- comment out or delete this line - # 'openwisp_controller.geo', <-- comment out or delete this line - # 'openwisp_controller.connection', <-- comment out or delete this line - # 'openwisp_controller.subnet_division', <-- comment out or delete this line - "mycontroller.sample_config", - "mycontroller.sample_pki", - "mycontroller.sample_geo", - "mycontroller.sample_connection", - "mycontroller.sample_subnet_division", - "openwisp_users", - # admin - "django.contrib.admin", - # other dependencies - "sortedm2m", - "reversion", - "leaflet", - # rest framework - "rest_framework", - "rest_framework_gis", - # channels - "channels", - # django-import-export - "import_export", - ] - -Substitute ``mycontroller``, ``sample_config``, ``sample_pki``, -``sample_connection``, ``sample_geo`` & ``sample_subnet_division`` with -the name you chose in step 1. - -4. Add ``EXTENDED_APPS`` -~~~~~~~~~~~~~~~~~~~~~~~~ - -Add the following to your ``settings.py``: - -.. code-block:: python - - EXTENDED_APPS = ( - "django_x509", - "django_loci", - "openwisp_controller.config", - "openwisp_controller.pki", - "openwisp_controller.geo", - "openwisp_controller.connection", - "openwisp_controller.subnet_division", - ) - -5. Add ``openwisp_utils.staticfiles.DependencyFinder`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Add ``openwisp_utils.staticfiles.DependencyFinder`` to -``STATICFILES_FINDERS`` in your ``settings.py``: - -.. code-block:: python - - STATICFILES_FINDERS = [ - "django.contrib.staticfiles.finders.FileSystemFinder", - "django.contrib.staticfiles.finders.AppDirectoriesFinder", - "openwisp_utils.staticfiles.DependencyFinder", - ] - -6. Add ``openwisp_utils.loaders.DependencyLoader`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Add ``openwisp_utils.loaders.DependencyLoader`` to ``TEMPLATES`` in your -``settings.py``, but ensure it comes before -``django.template.loaders.app_directories.Loader``: - -.. code-block:: python - - TEMPLATES = [ - { - "BACKEND": "django.template.backends.django.DjangoTemplates", - "OPTIONS": { - "loaders": [ - "django.template.loaders.filesystem.Loader", - "openwisp_utils.loaders.DependencyLoader", - "django.template.loaders.app_directories.Loader", - ], - "context_processors": [ - "django.template.context_processors.debug", - "django.template.context_processors.request", - "django.contrib.auth.context_processors.auth", - "django.contrib.messages.context_processors.messages", - "openwisp_utils.admin_theme.context_processor.menu_items", - "openwisp_notifications.context_processors.notification_api_settings", - ], - }, - } - ] - -5. Initial Database setup -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Ensure you are using one of the available geodjango backends, eg: - -.. code-block:: python - - DATABASES = { - "default": { - "ENGINE": "openwisp_utils.db.backends.spatialite", - "NAME": "openwisp-controller.db", - } - } - -For more information about GeoDjango, please refer to the `geodjango -documentation `_. - -6. Django Channels Setup -~~~~~~~~~~~~~~~~~~~~~~~~ - -Create ``asgi.py`` in your project folder and add following lines in it: - -.. code-block:: python - - from channels.auth import AuthMiddlewareStack - from channels.routing import ProtocolTypeRouter, URLRouter - from channels.security.websocket import AllowedHostsOriginValidator - from django.core.asgi import get_asgi_application - - from openwisp_controller.routing import get_routes - - # You can also add your routes like this - from my_app.routing import my_routes - - application = ProtocolTypeRouter( - { - "http": get_asgi_application(), - "websocket": AllowedHostsOriginValidator( - AuthMiddlewareStack(URLRouter(get_routes() + my_routes)) - ), - } - ) - -7. Other Settings -~~~~~~~~~~~~~~~~~ - -Add the following settings to ``settings.py``: - -.. code-block:: python - - FORM_RENDERER = "django.forms.renderers.TemplatesSetting" - - ASGI_APPLICATION = "my_project.asgi.application" - CHANNEL_LAYERS = { - "default": {"BACKEND": "channels.layers.InMemoryChannelLayer"}, - } - -For more information about FORM_RENDERER setting, please refer to the -`FORM_RENDERER documentation -`_. For -more information about ASGI_APPLICATION setting, please refer to the -`ASGI_APPLICATION documentation -`_. -For more information about CHANNEL_LAYERS setting, please refer to the -`CHANNEL_LAYERS documentation -`_. - -6. Inherit the AppConfig class -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Please refer to the following files in the sample app of the test project: - -- sample_config: - - `sample_config/__init__.py - `_. - - `sample_config/apps.py - `_. -- sample_geo: - - `sample_geo/__init__.py - `_. - - `sample_geo/apps.py - `_. -- sample_pki: - - `sample_pki/__init__.py - `_. - - `sample_pki/apps.py - `_. -- sample_connection: - - `sample_connection/__init__.py - `_. - - `sample_connection/apps.py - `_. -- sample_subnet_division: - - `sample_subnet_division/__init__.py - `_. - - `sample_subnet_division/apps.py - `_. - -You have to replicate and adapt that code in your project. - -For more information regarding the concept of ``AppConfig`` please refer -to the `"Applications" section in the django documentation -`_. - -7. Create your custom models -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For the purpose of showing an example, we added a simple "details" field -to the models of the sample app in the test project. - -- `sample_config models - `_ -- `sample_geo models - `_ -- `sample_pki models - `_ -- `sample_connection models - `_ -- `sample_subnet_division - `_ - -You can add fields in a similar way in your ``models.py`` file. - -**Note**: for doubts regarding how to use, extend or develop models please -refer to the `"Models" section in the django documentation -`_. - -8. Add swapper configurations -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Once you have created the models, add the following to your -``settings.py``: - -.. code-block:: python - - # Setting models for swapper module - CONFIG_DEVICE_MODEL = "sample_config.Device" - CONFIG_DEVICEGROUP_MODEL = "sample_config.DeviceGroup" - CONFIG_CONFIG_MODEL = "sample_config.Config" - CONFIG_TEMPLATETAG_MODEL = "sample_config.TemplateTag" - CONFIG_TAGGEDTEMPLATE_MODEL = "sample_config.TaggedTemplate" - CONFIG_TEMPLATE_MODEL = "sample_config.Template" - CONFIG_VPN_MODEL = "sample_config.Vpn" - CONFIG_VPNCLIENT_MODEL = "sample_config.VpnClient" - CONFIG_ORGANIZATIONCONFIGSETTINGS_MODEL = ( - "sample_config.OrganizationConfigSettings" - ) - CONFIG_ORGANIZATIONLIMITS_MODEL = "sample_config.OrganizationLimits" - DJANGO_X509_CA_MODEL = "sample_pki.Ca" - DJANGO_X509_CERT_MODEL = "sample_pki.Cert" - GEO_LOCATION_MODEL = "sample_geo.Location" - GEO_FLOORPLAN_MODEL = "sample_geo.FloorPlan" - GEO_DEVICELOCATION_MODEL = "sample_geo.DeviceLocation" - CONNECTION_CREDENTIALS_MODEL = "sample_connection.Credentials" - CONNECTION_DEVICECONNECTION_MODEL = "sample_connection.DeviceConnection" - CONNECTION_COMMAND_MODEL = "sample_connection.Command" - SUBNET_DIVISION_SUBNETDIVISIONRULE_MODEL = ( - "sample_subnet_division.SubnetDivisionRule" - ) - SUBNET_DIVISION_SUBNETDIVISIONINDEX_MODEL = ( - "sample_subnet_division.SubnetDivisionIndex" - ) - -Substitute ``sample_config``, ``sample_pki``, ``sample_connection``, -``sample_geo`` & ``sample_subnet_division`` with the name you chose in -step 1. - -9. Create database migrations -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Create database migrations: - -:: - - ./manage.py makemigrations - -Now, to use the default ``administrator`` and ``operator`` user groups -like the used in the openwisp_controller module, you'll manually need to -make a migrations file which would look like: - -- `sample_config/migrations/0002_default_groups_permissions.py - `_ -- `sample_geo/migrations/0002_default_group_permissions.py - `_ -- `sample_pki/migrations/0002_default_group_permissions.py - `_ -- `sample_connection/migrations/0002_default_group_permissions.py - `_ -- `sample_subnet_division/migrations/0002_default_group_permissions.py - `_ - -Create database migrations: - -:: - - ./manage.py migrate - -For more information, refer to the `"Migrations" section in the django -documentation -`_. - -10. Create the admin -~~~~~~~~~~~~~~~~~~~~ - -Refer to the ``admin.py`` file of the sample app. - -- `sample_config admin.py - `_. -- `sample_geo admin.py - `_. -- `sample_pki admin.py - `_. -- `sample_connection admin.py - `_. -- `sample_subnet_division admin.py - `_. - -To introduce changes to the admin, you can do it in two main ways which -are described below. - -**Note**: for more information regarding how the django admin works, or -how it can be customized, please refer to `"The django admin site" section -in the django documentation -`_. - -1. Monkey patching -++++++++++++++++++ - -If the changes you need to add are relatively small, you can resort to -monkey patching. - -For example: - -sample_config -............. - -.. code-block:: python - - from openwisp_controller.config.admin import ( - DeviceAdmin, - DeviceGroupAdmin, - TemplateAdmin, - VpnAdmin, - ) - - # DeviceAdmin.fields += ['example'] <-- monkey patching example - -sample_connection -................. - -.. code-block:: python - - from openwisp_controller.connection.admin import CredentialsAdmin - - # CredentialsAdmin.fields += ['example'] <-- monkey patching example - -sample_geo -.......... - -.. code-block:: python - - from openwisp_controller.geo.admin import FloorPlanAdmin, LocationAdmin - - # FloorPlanAdmin.fields += ['example'] <-- monkey patching example - -sample_pki -.......... - -.. code-block:: python - - from openwisp_controller.pki.admin import CaAdmin, CertAdmin - - # CaAdmin.fields += ['example'] <-- monkey patching example - -sample_subnet_division -...................... - -.. code-block:: python - - from openwisp_controller.subnet_division.admin import ( - SubnetDivisionRuleInlineAdmin, - ) - - # SubnetDivisionRuleInlineAdmin.fields += ['example'] <-- monkey patching example - -2. Inheriting admin classes -+++++++++++++++++++++++++++ - -If you need to introduce significant changes and/or you don't want to -resort to monkey patching, you can proceed as follows: - -sample_config -............. - -.. code-block:: python - - from django.contrib import admin - from openwisp_controller.config.admin import ( - DeviceAdmin as BaseDeviceAdmin, - TemplateAdmin as BaseTemplateAdmin, - VpnAdmin as BaseVpnAdmin, - DeviceGroupAdmin as BaseDeviceGroupAdmin, - from swapper import load_model - - Vpn = load_model('openwisp_controller', 'Vpn') - Device = load_model('openwisp_controller', 'Device') - DeviceGroup = load_model('openwisp_controller', 'DeviceGroup') - Template = load_model('openwisp_controller', 'Template') - - admin.site.unregister(Vpn) - admin.site.unregister(Device) - admin.site.unregister(DeviceGroup) - admin.site.unregister(Template) - - @admin.register(Vpn) - class VpnAdmin(BaseVpnAdmin): - # add your changes here - - @admin.register(Device) - class DeviceAdmin(BaseDeviceAdmin): - # add your changes here - - @admin.register(DeviceGroup) - class DeviceGroupAdmin(BaseDeviceGroupAdmin): - # add your changes here - - @admin.register(Template) - class TemplateAdmin(BaseTemplateAdmin): - # add your changes here - -sample_connection -................. - -.. code-block:: python - - from openwisp_controller.connection.admin import CredentialsAdmin as BaseCredentialsAdmin - from django.contrib import admin - from swapper import load_model - - Credentials = load_model('openwisp_controller', 'Credentials') - - admin.site.unregister(Credentials) - - @admin.register(Device) - class CredentialsAdmin(BaseCredentialsAdmin): - # add your changes here - -sample_geo -.......... - -.. code-block:: python - - from openwisp_controller.geo.admin import ( - FloorPlanAdmin as BaseFloorPlanAdmin, - LocationAdmin as BaseLocationAdmin - ) - from django.contrib import admin - from swapper import load_model - - Location = load_model('openwisp_controller', 'Location') - FloorPlan = load_model('openwisp_controller', 'FloorPlan') - - admin.site.unregister(FloorPlan) - admin.site.unregister(Location) - - @admin.register(FloorPlan) - class FloorPlanAdmin(BaseFloorPlanAdmin): - # add your changes here - - @admin.register(Location) - class LocationAdmin(BaseLocationAdmin): - # add your changes here - -sample_pki -.......... - -.. code-block:: python - - from openwisp_controller.geo.admin import ( - CaAdmin as BaseCaAdmin, - CertAdmin as BaseCertAdmin - ) - from django.contrib import admin - from swapper import load_model - - Ca = load_model('openwisp_controller', 'Ca') - Cert = load_model('openwisp_controller', 'Cert') - - admin.site.unregister(Ca) - admin.site.unregister(Cert) - - @admin.register(Ca) - class CaAdmin(BaseCaAdmin): - # add your changes here - - @admin.register(Cert) - class CertAdmin(BaseCertAdmin): - # add your changes here - -sample_subnet_division -...................... - -.. code-block:: python - - from openwisp_controller.subnet_division.admin import ( - SubnetAdmin as BaseSubnetAdmin, - IpAddressAdmin as BaseIpAddressAdmin, - SubnetDivisionRuleInlineAdmin as BaseSubnetDivisionRuleInlineAdmin, - ) - from django.contrib import admin - from swapper import load_model - - Subnet = load_model('openwisp_ipam', 'Subnet') - IpAddress = load_model('openwisp_ipam', 'IpAddress') - SubnetDivisionRule = load_model('subnet_division', 'SubnetDivisionRule') - - admin.site.unregister(Subnet) - admin.site.unregister(IpAddress) - admin.site.unregister(SubnetDivisionRule) - - @admin.register(Subnet) - class SubnetAdmin(BaseSubnetAdmin): - # add your changes here - - @admin.register(IpAddress) - class IpAddressAdmin(BaseIpAddressAdmin): - # add your changes here - - @admin.register(SubnetDivisionRule) - class SubnetDivisionRuleInlineAdmin(BaseSubnetDivisionRuleInlineAdmin): - # add your changes here - -11. Create root URL configuration -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code-block:: python - - from django.contrib import admin - from openwisp_controller.config.utils import get_controller_urls - from openwisp_controller.geo.utils import get_geo_urls - - # from .sample_config import views as config_views - # from .sample_geo import views as geo_views - - urlpatterns = [ - # ... other urls in your project ... - # Use only when changing controller API views (discussed below) - # url(r'^controller/', include((get_controller_urls(config_views), 'controller'), namespace='controller')) - # Use only when changing geo API views (discussed below) - # url(r'^geo/', include((get_geo_urls(geo_views), 'geo'), namespace='geo')), - # openwisp-controller urls - url( - r"", - include( - ("openwisp_controller.config.urls", "config"), - namespace="config", - ), - ), - url(r"", include("openwisp_controller.urls")), - ] - -For more information about URL configuration in django, please refer to -the `"URL dispatcher" section in the django documentation -`_. - -12. Import the automated tests -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When developing a custom application based on this module, it's a good -idea to import and run the base tests too, so that you can be sure the -changes you're introducing are not breaking some of the existing features -of *openwisp-controller*. - -In case you need to add breaking changes, you can overwrite the tests -defined in the base classes to test your own behavior. - -See the tests in sample_app to find out how to do this. - -- `project common tests.py - `_ -- `sample_config tests.py - `_ -- `sample_geo tests.py - `_ -- `sample_geo pytest.py - `_ -- `sample_pki tests.py - `_ -- `sample_connection tests.py - `_ -- `sample_subnet_division tests.py - `_ - -For running the tests, you need to copy fixtures as well: - -- Change `sample_config` to your config app's name in `sample_config - fixtures - `_ - and paste it in the ``sample_config/fixtures/`` directory. - -You can then run tests with: - -:: - - # the --parallel flag is optional - ./manage.py test --parallel mycontroller - -Substitute ``mycontroller`` with the name you chose in step 1. - -For more information about automated tests in django, please refer to -`"Testing in Django" -`_. - -Other base classes that can be inherited and extended -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The following steps are not required and are intended for more advanced -customization. - -1. Extending the Controller API Views -+++++++++++++++++++++++++++++++++++++ - -Extending the `sample_config/views.py -`_ -is required only when you want to make changes in the controller API, -Remember to change ``config_views`` location in ``urls.py`` in point 11 -for extending views. - -For more information about django views, please refer to the `views -section in the django documentation -`_. - -2. Extending the Geo API Views -++++++++++++++++++++++++++++++ - -Extending the `sample_geo/views.py -`_ -is required only when you want to make changes in the geo API, Remember to -change ``geo_views`` location in ``urls.py`` in point 11 for extending -views. - -For more information about django views, please refer to the `views -section in the django documentation -`_. - -Custom Subnet Division Rule Types -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -It is possible to create your own `subnet division rule types -<#subnet-division-app>`_. The rule type determines when subnets and IPs -will be provisioned and when they will be destroyed. - -You can create your custom rule types by extending -``openwisp_controller.subnet_division.rule_types.base.BaseSubnetDivisionRuleType``. - -Below is an example to create a subnet division rule type that will -provision subnets and IPs when a new device is created and will delete -them upon deletion for that device. - -.. code-block:: python - - # In mycontroller/sample_subnet_division/rules_types/custom.py - - from django.db.models.signals import post_delete, post_save - from swapper import load_model - - from openwisp_controller.subnet_division.rule_types.base import ( - BaseSubnetDivisionRuleType, - ) - - Device = load_model("config", "Device") - - - class CustomRuleType(BaseSubnetDivisionRuleType): - # The signal on which provisioning should be triggered - provision_signal = post_save - # The sender of the provision_signal - provision_sender = Device - # Dispatch UID for connecting provision_signal to provision_receiver - provision_dispatch_uid = "some_unique_identifier_string" - - # The signal on which deletion should be triggered - destroyer_signal = post_delete - # The sender of the destroyer_signal - destroyer_sender = Device - # Dispatch UID for connecting destroyer_signal to destroyer_receiver - destroyer_dispatch_uid = "another_unique_identifier_string" - - # Attribute path to organization_id - # Example 1: If organization_id is direct attribute of provision_signal - # sender instance, then - # organization_id_path = 'organization_id' - # Example 2: If organization_id is indirect attribute of provision signal - # sender instance, then - # organization_id_path = 'some_attribute.another_intermediate.organization_id' - organization_id_path = "organization_id" - - # Similar to organization_id_path but for the required subnet attribute - subnet_path = "subnet" - - # An intermediate method through which you can specify conditions for provisions - @classmethod - def should_create_subnets_ips(cls, instance, **kwargs): - # Using "post_save" provision_signal, the rule should be only - # triggered when a new object is created. - return kwargs["created"] - - # You can define logic to trigger provisioning for existing objects - # using following classmethod. By default, BaseSubnetDivisionRuleType - # performs no operation for existing objects. - @classmethod - def provision_for_existing_objects(cls, rule_obj): - for device in Device.objects.filter( - organization=rule_obj.organization - ): - cls.provision_receiver(device, created=True) - -After creating a class for your custom rule type, you will need to set -`OPENWISP_CONTROLLER_SUBNET_DIVISION_TYPES -<#openwisp-controller-subnet-division-types>`_ setting as follows: - -.. code-block:: python - - OPENWISP_CONTROLLER_SUBNET_DIVISION_TYPES = ( | - ('openwisp_controller.subnet_division.rule_types.vpn.VpnSubnetDivisionRuleType', 'VPN'), - ('openwisp_controller.subnet_division.rule_types.device.DeviceSubnetDivisionRuleType', 'Device'), - ('mycontroller.sample_subnet_division.rules_types.custom.CustomRuleType', 'Custom Rule'), - ) - -Registering new notification types -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can define your own notification types using -``register_notification_type`` function from OpenWISP Notifications. - -For more information, see the relevant `documentation section about -registering notification types in openwisp-notifications -`_. - -Once a new notification type is registered, you have to use the `"notify" -signal provided in openwisp-notifications -`_ -to send notifications for this type. +- `Developer documentation `_ +- `User documentation + `_ Contributing ------------ diff --git a/docs/developer/extending.rst b/docs/developer/extending.rst new file mode 100644 index 000000000..599d81f85 --- /dev/null +++ b/docs/developer/extending.rst @@ -0,0 +1,873 @@ +Extending OpenWISP Controller +============================= + +.. include:: ../partials/developer-docs.rst + +One of the core values of the OpenWISP project is :ref:`Software +Reusability `, for this reason *OpenWISP +Controller* provides a set of base classes which can be imported, extended +and reused to create derivative apps. + +In order to implement your custom version of *OpenWISP Controller*, you +need to perform the steps described in this section. + +When in doubt, the code in the `test project +`_ +will serve you as source of truth: just replicate and adapt that code to +get a basic derivative of *OpenWISP Controller* working. + +If you want to add new users fields, please follow the :doc:`tutorial to +extend the openwisp-users module `. As an +example, we have extended *openwisp-users* to *sample_users* app and added +a field ``social_security_number`` in the `sample_users/models.py +`_. + +.. important:: + + If you plan on using a customized version of this module, we suggest + to start with it since the beginning, because migrating your data from + the default module to your extended version may be time consuming. + +.. contents:: **Table of Contents**: + :depth: 2 + :local: + +1. Initialize Your Project & Custom Apps +---------------------------------------- + +Firstly, to get started you need to create a django project: + +.. code-block:: + + django-admin startproject mycontroller + +Now, you need to do is to create some new django apps which will contain +your custom version of *OpenWISP Controller*. + +A django project is a collection of django apps. There are 4 django apps +in the openwisp_controller project, namely config, pki, connection & geo. +You'll need to create 4 apps in your project for each app in +openwisp_controller. + +A django app is nothing more than a `python package +`_ (a directory +of python scripts), in the following examples we'll call these django app +``sample_config``, ``sample_pki``, ``sample_connection``, ``sample_geo`` & +``sample_subnet_division``. but you can name it how you want: + +.. code-block:: + + django-admin startapp sample_config + django-admin startapp sample_pki + django-admin startapp sample_connection + django-admin startapp sample_geo + django-admin startapp sample_subnet_division + +Keep in mind that the command mentioned above must be called from a +directory which is available in your `PYTHON_PATH +`_ so that +you can then import the result into your project. + +For more information about how to work with django projects and django +apps, please refer to the `django documentation +`_. + +2. Install ``openwisp-controller`` +---------------------------------- + +Install (and add to the requirement of your project) openwisp-controller: + +.. code-block:: + + pip install openwisp-controller + +3. Add Your Apps to ``INSTALLED_APPS`` +-------------------------------------- + +Now you need to add ``mycontroller.sample_config``, +``mycontroller.sample_pki``, ``mycontroller.sample_connection``, +``mycontroller.sample_geo`` & ``mycontroller.sample_subnet_division`` to +``INSTALLED_APPS`` in your ``settings.py``, ensuring also that +``openwisp_controller.config``, ``openwisp_controller.geo``, +``openwisp_controller.pki``, ``openwisp_controller.connnection`` & +``openwisp_controller.subnet_division`` have been removed: + +.. code-block:: python + + # Remember: Order in INSTALLED_APPS is important. + INSTALLED_APPS = [ + # other django installed apps + "openwisp_utils.admin_theme", + "admin_auto_filters", + # all-auth + "django.contrib.sites", + "allauth", + "allauth.account", + "allauth.socialaccount", + # openwisp2 module + # 'openwisp_controller.config', <-- comment out or delete this line + # 'openwisp_controller.pki', <-- comment out or delete this line + # 'openwisp_controller.geo', <-- comment out or delete this line + # 'openwisp_controller.connection', <-- comment out or delete this line + # 'openwisp_controller.subnet_division', <-- comment out or delete this line + "mycontroller.sample_config", + "mycontroller.sample_pki", + "mycontroller.sample_geo", + "mycontroller.sample_connection", + "mycontroller.sample_subnet_division", + "openwisp_users", + # admin + "django.contrib.admin", + # other dependencies + "sortedm2m", + "reversion", + "leaflet", + # rest framework + "rest_framework", + "rest_framework_gis", + # channels + "channels", + # django-import-export + "import_export", + ] + +Substitute ``mycontroller``, ``sample_config``, ``sample_pki``, +``sample_connection``, ``sample_geo`` & ``sample_subnet_division`` with +the name you chose in step 1. + +4. Add ``EXTENDED_APPS`` +------------------------ + +Add the following to your ``settings.py``: + +.. code-block:: python + + EXTENDED_APPS = ( + "django_x509", + "django_loci", + "openwisp_controller.config", + "openwisp_controller.pki", + "openwisp_controller.geo", + "openwisp_controller.connection", + "openwisp_controller.subnet_division", + ) + +5. Add ``openwisp_utils.staticfiles.DependencyFinder`` +------------------------------------------------------ + +Add ``openwisp_utils.staticfiles.DependencyFinder`` to +``STATICFILES_FINDERS`` in your ``settings.py``: + +.. code-block:: python + + STATICFILES_FINDERS = [ + "django.contrib.staticfiles.finders.FileSystemFinder", + "django.contrib.staticfiles.finders.AppDirectoriesFinder", + "openwisp_utils.staticfiles.DependencyFinder", + ] + +6. Add ``openwisp_utils.loaders.DependencyLoader`` +-------------------------------------------------- + +Add ``openwisp_utils.loaders.DependencyLoader`` to ``TEMPLATES`` in your +``settings.py``, but ensure it comes before +``django.template.loaders.app_directories.Loader``: + +.. code-block:: python + + TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "OPTIONS": { + "loaders": [ + "django.template.loaders.filesystem.Loader", + "openwisp_utils.loaders.DependencyLoader", + "django.template.loaders.app_directories.Loader", + ], + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + "openwisp_utils.admin_theme.context_processor.menu_items", + "openwisp_notifications.context_processors.notification_api_settings", + ], + }, + } + ] + +5. Initial Database Setup +------------------------- + +Ensure you are using one of the available geodjango backends, eg: + +.. code-block:: python + + DATABASES = { + "default": { + "ENGINE": "openwisp_utils.db.backends.spatialite", + "NAME": "openwisp-controller.db", + } + } + +For more information about GeoDjango, please refer to the `geodjango +documentation `_. + +6. Django Channels Setup +------------------------ + +Create ``asgi.py`` in your project folder and add following lines in it: + +.. code-block:: python + + from channels.auth import AuthMiddlewareStack + from channels.routing import ProtocolTypeRouter, URLRouter + from channels.security.websocket import AllowedHostsOriginValidator + from django.core.asgi import get_asgi_application + + from openwisp_controller.routing import get_routes + + # You can also add your routes like this + from my_app.routing import my_routes + + application = ProtocolTypeRouter( + { + "http": get_asgi_application(), + "websocket": AllowedHostsOriginValidator( + AuthMiddlewareStack(URLRouter(get_routes() + my_routes)) + ), + } + ) + +7. Other Settings +----------------- + +Add the following settings to ``settings.py``: + +.. code-block:: python + + FORM_RENDERER = "django.forms.renderers.TemplatesSetting" + + ASGI_APPLICATION = "my_project.asgi.application" + CHANNEL_LAYERS = { + "default": {"BACKEND": "channels.layers.InMemoryChannelLayer"}, + } + +For more information about FORM_RENDERER setting, please refer to the +`FORM_RENDERER documentation +`_. For +more information about ASGI_APPLICATION setting, please refer to the +`ASGI_APPLICATION documentation +`_. +For more information about CHANNEL_LAYERS setting, please refer to the +`CHANNEL_LAYERS documentation +`_. + +6. Inherit the AppConfig Class +------------------------------ + +Please refer to the following files in the sample app of the test project: + +- ``sample_config``: + - `sample_config/__init__.py + `_. + - `sample_config/apps.py + `_. +- ``sample_geo``: + - `sample_geo/__init__.py + `_. + - `sample_geo/apps.py + `_. +- ``sample_pki``: + - `sample_pki/__init__.py + `_. + - `sample_pki/apps.py + `_. +- ``sample_connection``: + - `sample_connection/__init__.py + `_. + - `sample_connection/apps.py + `_. +- ``sample_subnet_division``: + - `sample_subnet_division/__init__.py + `_. + - `sample_subnet_division/apps.py + `_. + +You have to replicate and adapt that code in your project. + +For more information regarding the concept of ``AppConfig`` please refer +to the `"Applications" section in the django documentation +`_. + +7. Create Your Custom Models +---------------------------- + +For the purpose of showing an example, we added a simple "details" field +to the models of the sample app in the test project. + +- `sample_config models + `_ +- `sample_geo models + `_ +- `sample_pki models + `_ +- `sample_connection models + `_ +- `sample_subnet_division + `_ + +You can add fields in a similar way in your ``models.py`` file. + +.. note:: + + If you have any doubt regarding how to use, extend or develop models + please refer to the `"Models" section in the django documentation + `_. + +8. Add Swapper Configurations +----------------------------- + +Once you have created the models, add the following to your +``settings.py``: + +.. code-block:: python + + # Setting models for swapper module + CONFIG_DEVICE_MODEL = "sample_config.Device" + CONFIG_DEVICEGROUP_MODEL = "sample_config.DeviceGroup" + CONFIG_CONFIG_MODEL = "sample_config.Config" + CONFIG_TEMPLATETAG_MODEL = "sample_config.TemplateTag" + CONFIG_TAGGEDTEMPLATE_MODEL = "sample_config.TaggedTemplate" + CONFIG_TEMPLATE_MODEL = "sample_config.Template" + CONFIG_VPN_MODEL = "sample_config.Vpn" + CONFIG_VPNCLIENT_MODEL = "sample_config.VpnClient" + CONFIG_ORGANIZATIONCONFIGSETTINGS_MODEL = ( + "sample_config.OrganizationConfigSettings" + ) + CONFIG_ORGANIZATIONLIMITS_MODEL = "sample_config.OrganizationLimits" + DJANGO_X509_CA_MODEL = "sample_pki.Ca" + DJANGO_X509_CERT_MODEL = "sample_pki.Cert" + GEO_LOCATION_MODEL = "sample_geo.Location" + GEO_FLOORPLAN_MODEL = "sample_geo.FloorPlan" + GEO_DEVICELOCATION_MODEL = "sample_geo.DeviceLocation" + CONNECTION_CREDENTIALS_MODEL = "sample_connection.Credentials" + CONNECTION_DEVICECONNECTION_MODEL = "sample_connection.DeviceConnection" + CONNECTION_COMMAND_MODEL = "sample_connection.Command" + SUBNET_DIVISION_SUBNETDIVISIONRULE_MODEL = ( + "sample_subnet_division.SubnetDivisionRule" + ) + SUBNET_DIVISION_SUBNETDIVISIONINDEX_MODEL = ( + "sample_subnet_division.SubnetDivisionIndex" + ) + +Substitute ``sample_config``, ``sample_pki``, ``sample_connection``, +``sample_geo`` & ``sample_subnet_division`` with the name you chose in +step 1. + +9. Create Database Migrations +----------------------------- + +Create database migrations: + +.. code-block:: + + ./manage.py makemigrations + +Now, to use the default ``administrator`` and ``operator`` user groups +like the used in the openwisp_controller module, you'll manually need to +make a migrations file which would look like: + +- `sample_config/migrations/0002_default_groups_permissions.py + `_ +- `sample_geo/migrations/0002_default_group_permissions.py + `_ +- `sample_pki/migrations/0002_default_group_permissions.py + `_ +- `sample_connection/migrations/0002_default_group_permissions.py + `_ +- `sample_subnet_division/migrations/0002_default_group_permissions.py + `_ + +Create database migrations: + +.. code-block:: + + ./manage.py migrate + +For more information, refer to the `"Migrations" section in the django +documentation +`_. + +10. Create the Admin +-------------------- + +Refer to the ``admin.py`` file of the sample app. + +- `sample_config admin.py + `_. +- `sample_geo admin.py + `_. +- `sample_pki admin.py + `_. +- `sample_connection admin.py + `_. +- `sample_subnet_division admin.py + `_. + +To introduce changes to the admin, you can do it in two main ways which +are described below. + +**Note**: for more information regarding how the django admin works, or +how it can be customized, please refer to `"The django admin site" section +in the django documentation +`_. + +1. Monkey Patching +~~~~~~~~~~~~~~~~~~ + +If the changes you need to add are relatively small, you can resort to +monkey patching. + +For example: + +``sample_config`` ++++++++++++++++++ + +.. code-block:: python + + from openwisp_controller.config.admin import ( + DeviceAdmin, + DeviceGroupAdmin, + TemplateAdmin, + VpnAdmin, + ) + + DeviceAdmin.fields += ["example"] # <-- monkey patching example + +``sample_connection`` ++++++++++++++++++++++ + +.. code-block:: python + + from openwisp_controller.connection.admin import CredentialsAdmin + + CredentialsAdmin.fields += ["example"] # <-- monkey patching example + +``sample_geo`` +++++++++++++++ + +.. code-block:: python + + from openwisp_controller.geo.admin import FloorPlanAdmin, LocationAdmin + + FloorPlanAdmin.fields += ["example"] # <-- monkey patching example + +``sample_pki`` +++++++++++++++ + +.. code-block:: python + + from openwisp_controller.pki.admin import CaAdmin, CertAdmin + + CaAdmin.fields += ["example"] # <-- monkey patching example + +``sample_subnet_division`` +++++++++++++++++++++++++++ + +.. code-block:: python + + from openwisp_controller.subnet_division.admin import ( + SubnetDivisionRuleInlineAdmin, + ) + + SubnetDivisionRuleInlineAdmin.fields += [ + "example" + ] # <-- monkey patching example + +2. Inheriting admin classes +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you need to introduce significant changes and/or you don't want to +resort to monkey patching, you can proceed as follows: + +``sample_config`` ++++++++++++++++++ + +.. code-block:: python + + from django.contrib import admin + from openwisp_controller.config.admin import ( + DeviceAdmin as BaseDeviceAdmin, + TemplateAdmin as BaseTemplateAdmin, + VpnAdmin as BaseVpnAdmin, + DeviceGroupAdmin as BaseDeviceGroupAdmin, + ) + from swapper import load_model + + Vpn = load_model("openwisp_controller", "Vpn") + Device = load_model("openwisp_controller", "Device") + DeviceGroup = load_model("openwisp_controller", "DeviceGroup") + Template = load_model("openwisp_controller", "Template") + + admin.site.unregister(Vpn) + admin.site.unregister(Device) + admin.site.unregister(DeviceGroup) + admin.site.unregister(Template) + + + @admin.register(Vpn) + class VpnAdmin(BaseVpnAdmin): + # add your changes here + pass + + + @admin.register(Device) + class DeviceAdmin(BaseDeviceAdmin): + # add your changes here + pass + + + @admin.register(DeviceGroup) + class DeviceGroupAdmin(BaseDeviceGroupAdmin): + # add your changes here + pass + + + @admin.register(Template) + class TemplateAdmin(BaseTemplateAdmin): + # add your changes here + pass + +``sample_connection`` ++++++++++++++++++++++ + +.. code-block:: python + + from openwisp_controller.connection.admin import ( + CredentialsAdmin as BaseCredentialsAdmin, + ) + from django.contrib import admin + from swapper import load_model + + Credentials = load_model("openwisp_controller", "Credentials") + + admin.site.unregister(Credentials) + + + @admin.register(Device) + class CredentialsAdmin(BaseCredentialsAdmin): + pass + # add your changes here + +``sample_geo`` +++++++++++++++ + +.. code-block:: python + + from openwisp_controller.geo.admin import ( + FloorPlanAdmin as BaseFloorPlanAdmin, + LocationAdmin as BaseLocationAdmin, + ) + from django.contrib import admin + from swapper import load_model + + Location = load_model("openwisp_controller", "Location") + FloorPlan = load_model("openwisp_controller", "FloorPlan") + + admin.site.unregister(FloorPlan) + admin.site.unregister(Location) + + + @admin.register(FloorPlan) + class FloorPlanAdmin(BaseFloorPlanAdmin): + pass + # add your changes here + + + @admin.register(Location) + class LocationAdmin(BaseLocationAdmin): + pass + # add your changes here + +``sample_pki`` +++++++++++++++ + +.. code-block:: python + + from openwisp_controller.geo.admin import ( + CaAdmin as BaseCaAdmin, + CertAdmin as BaseCertAdmin, + ) + from django.contrib import admin + from swapper import load_model + + Ca = load_model("openwisp_controller", "Ca") + Cert = load_model("openwisp_controller", "Cert") + + admin.site.unregister(Ca) + admin.site.unregister(Cert) + + + @admin.register(Ca) + class CaAdmin(BaseCaAdmin): + pass + # add your changes here + + + @admin.register(Cert) + class CertAdmin(BaseCertAdmin): + pass + # add your changes here + +``sample_subnet_division`` +++++++++++++++++++++++++++ + +.. code-block:: python + + from openwisp_controller.subnet_division.admin import ( + SubnetAdmin as BaseSubnetAdmin, + IpAddressAdmin as BaseIpAddressAdmin, + SubnetDivisionRuleInlineAdmin as BaseSubnetDivisionRuleInlineAdmin, + ) + from django.contrib import admin + from swapper import load_model + + Subnet = load_model("openwisp_ipam", "Subnet") + IpAddress = load_model("openwisp_ipam", "IpAddress") + SubnetDivisionRule = load_model("subnet_division", "SubnetDivisionRule") + + admin.site.unregister(Subnet) + admin.site.unregister(IpAddress) + admin.site.unregister(SubnetDivisionRule) + + + @admin.register(Subnet) + class SubnetAdmin(BaseSubnetAdmin): + pass + # add your changes here + + + @admin.register(IpAddress) + class IpAddressAdmin(BaseIpAddressAdmin): + pass + # add your changes here + + + @admin.register(SubnetDivisionRule) + class SubnetDivisionRuleInlineAdmin(BaseSubnetDivisionRuleInlineAdmin): + pass + # add your changes here + +11. Create Root URL Configuration +--------------------------------- + +.. code-block:: python + + from django.contrib import admin + from openwisp_controller.config.utils import get_controller_urls + from openwisp_controller.geo.utils import get_geo_urls + + # from .sample_config import views as config_views + # from .sample_geo import views as geo_views + + urlpatterns = [ + # ... other urls in your project ... + # Use only when changing controller API views (discussed below) + # url(r'^controller/', include((get_controller_urls(config_views), 'controller'), namespace='controller')) + # Use only when changing geo API views (discussed below) + # url(r'^geo/', include((get_geo_urls(geo_views), 'geo'), namespace='geo')), + # openwisp-controller urls + url( + r"", + include( + ("openwisp_controller.config.urls", "config"), + namespace="config", + ), + ), + url(r"", include("openwisp_controller.urls")), + ] + +For more information about URL configuration in django, please refer to +the `"URL dispatcher" section in the django documentation +`_. + +12. Import the Automated Tests +------------------------------ + +When developing a custom application based on this module, it's a good +idea to import and run the base tests too, so that you can be sure the +changes you're introducing are not breaking some of the existing features +of *OpenWISP Controller*. + +In case you need to add breaking changes, you can overwrite the tests +defined in the base classes to test your own behavior. + +See the tests in sample_app to find out how to do this. + +- `project common tests.py + `_ +- `sample_config tests.py + `_ +- `sample_geo tests.py + `_ +- `sample_geo pytest.py + `_ +- `sample_pki tests.py + `_ +- `sample_connection tests.py + `_ +- `sample_subnet_division tests.py + `_ + +For running the tests, you need to copy fixtures as well: + +- Change `sample_config` to your config app's name in `sample_config + fixtures + `_ + and paste it in the ``sample_config/fixtures/`` directory. + +You can then run tests with: + +.. code-block:: + + # the --parallel flag is optional + ./manage.py test --parallel mycontroller + +Substitute ``mycontroller`` with the name you chose in step 1. + +For more information about automated tests in django, please refer to +`"Testing in Django" +`_. + +Other Base Classes that Can Be Inherited and Extended +----------------------------------------------------- + +The following steps are not required and are intended for more advanced +customization. + +1. Extending the Controller API Views +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Extending the `sample_config/views.py +`_ +is required only when you want to make changes in the controller API, +Remember to change ``config_views`` location in ``urls.py`` in point 11 +for extending views. + +For more information about django views, please refer to the `views +section in the django documentation +`_. + +2. Extending the Geo API Views +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Extending the `sample_geo/views.py +`_ +is required only when you want to make changes in the geo API, Remember to +change ``geo_views`` location in ``urls.py`` in point 11 for extending +views. + +For more information about django views, please refer to the `views +section in the django documentation +`_. + +.. _custom_subnet_division_rule_types: + +Custom Subnet Division Rule Types +--------------------------------- + +It is possible to create your own :doc:`subnet division rule types +<../user/subnet-division-rules>`. The rule type determines when subnets +and IPs will be provisioned and when they will be destroyed. + +You can create your custom rule types by extending +``openwisp_controller.subnet_division.rule_types.base.BaseSubnetDivisionRuleType``. + +Below is an example to create a subnet division rule type that will +provision subnets and IPs when a new device is created and will delete +them upon deletion for that device. + +.. code-block:: python + + # In mycontroller/sample_subnet_division/rules_types/custom.py + + from django.db.models.signals import post_delete, post_save + from swapper import load_model + + from openwisp_controller.subnet_division.rule_types.base import ( + BaseSubnetDivisionRuleType, + ) + + Device = load_model("config", "Device") + + + class CustomRuleType(BaseSubnetDivisionRuleType): + # The signal on which provisioning should be triggered + provision_signal = post_save + # The sender of the provision_signal + provision_sender = Device + # Dispatch UID for connecting provision_signal to provision_receiver + provision_dispatch_uid = "some_unique_identifier_string" + + # The signal on which deletion should be triggered + destroyer_signal = post_delete + # The sender of the destroyer_signal + destroyer_sender = Device + # Dispatch UID for connecting destroyer_signal to destroyer_receiver + destroyer_dispatch_uid = "another_unique_identifier_string" + + # Attribute path to organization_id + # Example 1: If organization_id is direct attribute of provision_signal + # sender instance, then + # organization_id_path = 'organization_id' + # Example 2: If organization_id is indirect attribute of provision signal + # sender instance, then + # organization_id_path = 'some_attribute.another_intermediate.organization_id' + organization_id_path = "organization_id" + + # Similar to organization_id_path but for the required subnet attribute + subnet_path = "subnet" + + # An intermediate method through which you can specify conditions for provisions + @classmethod + def should_create_subnets_ips(cls, instance, **kwargs): + # Using "post_save" provision_signal, the rule should be only + # triggered when a new object is created. + return kwargs["created"] + + # You can define logic to trigger provisioning for existing objects + # using following classmethod. By default, BaseSubnetDivisionRuleType + # performs no operation for existing objects. + @classmethod + def provision_for_existing_objects(cls, rule_obj): + for device in Device.objects.filter( + organization=rule_obj.organization + ): + cls.provision_receiver(device, created=True) + +After creating a class for your custom rule type, you will need to set +:ref:`OPENWISP_CONTROLLER_SUBNET_DIVISION_TYPES +` setting as follows: + +.. code-block:: python + + OPENWISP_CONTROLLER_SUBNET_DIVISION_TYPES = ( + ( + "openwisp_controller.subnet_division.rule_types.vpn.VpnSubnetDivisionRuleType", + "VPN", + ), + ( + "openwisp_controller.subnet_division.rule_types.device.DeviceSubnetDivisionRuleType", + "Device", + ), + ( + "mycontroller.sample_subnet_division.rules_types.custom.CustomRuleType", + "Custom Rule", + ), + ) + +More Utilities to Extend OpenWISP Controller +-------------------------------------------- + +See :doc:`utils`. diff --git a/docs/developer/index.rst b/docs/developer/index.rst new file mode 100644 index 000000000..99915fc96 --- /dev/null +++ b/docs/developer/index.rst @@ -0,0 +1,16 @@ +Developer Docs +============== + +.. include:: ../partials/developer-docs.rst + +.. toctree:: + :maxdepth: 2 + + ./installation.rst + ./utils.rst + ./extending.rst + +Other useful resources: + + - :doc:`../user/rest-api` + - :doc:`../user/settings` diff --git a/docs/developer/installation.rst b/docs/developer/installation.rst new file mode 100644 index 000000000..45b598cfe --- /dev/null +++ b/docs/developer/installation.rst @@ -0,0 +1,203 @@ +Developer Installation Instructions +=================================== + +.. include:: ../partials/developer-docs.rst + +.. contents:: **Table of Contents**: + :depth: 2 + :local: + +Dependencies +------------ + +- Python >= 3.8 +- OpenSSL + +Installing for Development +-------------------------- + +Install the system dependencies: + +.. code-block:: shell + + sudo apt update + sudo apt install -y sqlite3 libsqlite3-dev openssl libssl-dev + sudo apt install -y gdal-bin libproj-dev libgeos-dev libspatialite-dev libsqlite3-mod-spatialite + sudo apt install -y chromium-browser + +Fork and clone the forked repository: + +.. code-block:: shell + + git clone git://github.com//openwisp-controller + +Navigate into the cloned repository: + +.. code-block:: shell + + cd openwisp-controller/ + +Launch Redis and PostgreSQL: + +.. code-block:: shell + + docker-compose up -d redis postgres + +Setup and activate a virtual-environment (we'll be using `virtualenv +`_): + +.. code-block:: shell + + python -m virtualenv env + source env/bin/activate + +Make sure that your base python packages are up to date before moving to +the next step: + +.. code-block:: shell + + pip install -U pip wheel setuptools + +Install development dependencies: + +.. code-block:: shell + + pip install -e . + pip install -r requirements-test.txt + sudo npm install -g jshint stylelint + +Install WebDriver for Chromium for your browser version from +https://chromedriver.chromium.org/home and Extract ``chromedriver`` to one +of directories from your ``$PATH`` (example: ``~/.local/bin/``). + +Create database: + +.. code-block:: shell + + cd tests/ + ./manage.py migrate + ./manage.py createsuperuser + +Launch celery worker (for background jobs): + +.. code-block:: shell + + celery -A openwisp2 worker -l info + +Launch development server: + +.. code-block:: shell + + ./manage.py runserver 0.0.0.0:8000 + +You can access the admin interface at ``http://127.0.0.1:8000/admin/``. + +Run tests with: + +.. code-block:: shell + + ./runtests.py --parallel + +Some tests, such as the Selenium UI tests, require a PostgreSQL database +to run. If you don't have a PostgreSQL database running on your system, +you can use :ref:`the Docker Compose configuration provided in this +repository `. Once set up, you can run these +specific tests as follows: + +.. code-block:: shell + + # Run database tests against PostgreSQL backend + POSTGRESQL=1 ./runtests.py --parallel + + # Run only specific selenium tests classes + cd tests/ + DJANGO_SETTINGS_MODULE=openwisp2.postgresql_settings ./manage.py test openwisp_controller.config.tests.test_selenium.TestDeviceAdmin + +Run quality assurance tests with: + +.. code-block:: shell + + ./run-qa-checks + +Alternative Sources +------------------- + +Pypi +~~~~ + +To install the latest stable version from pypi: + +.. code-block:: shell + + pip install openwisp-controller + +Github +~~~~~~ + +To install the latest development version tarball via HTTPs: + +.. code-block:: shell + + pip install https://github.com/openwisp/openwisp-controller/tarball/master + +Alternatively you can use the git protocol: + +.. code-block:: shell + + pip install -e git+git://github.com/openwisp/openwisp-controller#egg=openwisp_controller + +.. _controller_dev_docker: + +Install and Run on Docker +------------------------- + +.. warning:: + + This Docker image is for development purposes only. + + For the official OpenWISP Docker images, see: :doc:`docker-openwisp + `. + +Build from the Dockerfile: + +.. code-block:: shell + + docker-compose build + +Run the docker container: + +.. code-block:: shell + + docker-compose up + +Troubleshooting Steps for Common Installation Issues +---------------------------------------------------- + +You may encounter some issues while installing GeoDjango. + +Unable to Load SpatiaLite library Extension? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you are incurring in the following exception: + +.. code-block:: + + django.core.exceptions.ImproperlyConfigured: Unable to load the SpatiaLite library extension + +You need to specify ``SPATIALITE_LIBRARY_PATH`` in your ``settings.py`` as +explained in `django documentation regarding how to install and configure +spatialte +`_. + +Having Issues with Other Geospatial Libraries? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Please refer `troubleshooting issues related to geospatial libraries +`_. + +.. important:: + + If you want to add OpenWISP Controller to an existing Django project, + then you can refer to the `test project in the openwisp-controller + repository + `_. diff --git a/docs/developer/utils.rst b/docs/developer/utils.rst new file mode 100644 index 000000000..53b9d1f9b --- /dev/null +++ b/docs/developer/utils.rst @@ -0,0 +1,332 @@ +Code Utilities +============== + +.. include:: ../partials/developer-docs.rst + +.. contents:: **Table of Contents**: + :depth: 2 + :local: + +.. _registering_unregistering_commands: + +Registering / Unregistering Commands +------------------------------------ + +OpenWISP Controller allows to register new command options or unregister +existing command options through two utility functions: + +- ``openwisp_controller.connection.commands.register_command`` +- ``openwisp_controller.connection.commands.unregister_command`` + +You can use these functions to register new custom commands or unregister +existing commands from your code. + +.. note:: + + These functions are to be used as an alternative to the + :ref:`OPENWISP_CONTROLLER_USER_COMMANDS` setting when :doc:`extending + openwisp-controller ` or when developing custom + applications based on OpenWISP Controller. + +``register_command`` +~~~~~~~~~~~~~~~~~~~~ + +================== ============================================== +Parameter Description +``command_name`` A ``str`` defining identifier for the command. +``command_config`` A ``dict`` like the one shown in :ref:`Command + Configuration: schema `. +================== ============================================== + +**Note:** It will raise ``ImproperlyConfigured`` exception if a command is +already registered with the same name. + +``unregister_command`` +~~~~~~~~~~~~~~~~~~~~~~ + +================ ======================================= +Parameter Description +``command_name`` A ``str`` defining name of the command. +================ ======================================= + +**Note:** It will raise ``ImproperlyConfigured`` exception if such command +does not exists. + +Controller Notifications +------------------------ + +The notification types registered and used by OpenWISP Controller are +listed in the following table. + +===================== =============================================== +Notification Type Use +``config_error`` Fires when the status of a device configuration + changes to ``error``. +``device_registered`` Fires when a new device registers itself. +===================== =============================================== + +Registering Notification Types +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can define your own notification types using +``register_notification_type`` function from OpenWISP Notifications. + +For more information, see the relevant :doc:`documentation section about +registering notification types in the Notifications module +`. + +Once a new notification type is registered, you can use the :doc:`"notify" +signal provided by the Notifications module +` to send notifications with +this new type. + +Signals +------- + +.. include:: /partials/signals-note.rst + +``config_modified`` +~~~~~~~~~~~~~~~~~~~ + +**Path**: ``openwisp_controller.config.signals.config_modified`` + +**Arguments**: + +- ``instance``: instance of ``Config`` which got its ``config`` modified +- ``previous_status``: indicates the status of the config object before + the signal was emitted +- ``action``: action which emitted the signal, can be any of the list + below: - ``config_changed``: the configuration of the config object was + changed - ``related_template_changed``: the configuration of a related + template was changed - ``m2m_templates_changed``: the assigned templates + were changed (either templates were added, removed or their order was + changed) + +This signal is emitted every time the configuration of a device is +modified. + +It does not matter if ``Config.status`` is already modified, this signal +will be emitted anyway because it signals that the device configuration +has changed. + +This signal is used to trigger the update of the configuration on devices, +when the push feature is enabled (requires Device credentials). + +The signal is also emitted when one of the templates used by the device is +modified or if the templates assigned to the device are changed. + +Special cases in which ``config_modified`` is not emitted ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +This signal is not emitted when the device is created for the first time. + +It is also not emitted when templates assigned to a config object are +cleared (``post_clear`` m2m signal), this is necessary because `sortedm2m +`_, the package we use to +implement ordered templates, uses the clear action to reorder templates +(m2m relationships are first cleared and then added back), therefore we +ignore ``post_clear`` to avoid emitting signals twice (one for the clear +action and one for the add action). Please keep this in mind if you plan +on using the clear method of the m2m manager. + +``config_status_changed`` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Path**: ``openwisp_controller.config.signals.config_status_changed`` + +**Arguments**: + +- ``instance``: instance of ``Config`` which got its ``status`` changed + +This signal is emitted only when the configuration status of a device has +changed. + +The signal is emitted also when the m2m template relationships of a config +object are changed, but only on ``post_add`` or ``post_remove`` actions, +``post_clear`` is ignored for the same reason explained in the previous +section. + +.. _config_backend_changed: + +``config_backend_changed`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Path**: ``openwisp_controller.config.signals.config_backend_changed`` +**Arguments**: + +- ``instance``: instance of ``Config`` which got its ``backend`` changed +- ``old_backend``: the old backend of the config object +- ``backend``: the new backend of the config object + +It is not emitted when the device or config is created. + +``checksum_requested`` +~~~~~~~~~~~~~~~~~~~~~~ + +**Path**: ``openwisp_controller.config.signals.checksum_requested`` + +**Arguments**: + +- ``instance``: instance of ``Device`` for which its configuration + checksum has been requested +- ``request``: the HTTP request object + +This signal is emitted when a device requests a checksum via the +controller views. + +The signal is emitted just before a successful response is returned, it is +not sent if the response was not successful. + +``config_download_requested`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Path**: ``openwisp_controller.config.signals.config_download_requested`` + +**Arguments**: + +- ``instance``: instance of ``Device`` for which its configuration has + been requested for download +- ``request``: the HTTP request object + +This signal is emitted when a device requests to download its +configuration via the controller views. + +The signal is emitted just before a successful response is returned, it is +not sent if the response was not successful. + +``is_working_changed`` +~~~~~~~~~~~~~~~~~~~~~~ + +**Path**: ``openwisp_controller.connection.signals.is_working_changed`` + +**Arguments**: + +- ``instance``: instance of ``DeviceConnection`` +- ``is_working``: value of ``DeviceConnection.is_working`` +- ``old_is_working``: previous value of ``DeviceConnection.is_working``, + either ``None`` (for new connections), ``True`` or ``False`` +- ``failure_reason``: error message explaining reason for failure in + establishing connection +- ``old_failure_reason``: previous value of + ``DeviceConnection.failure_reason`` + +This signal is emitted every time ``DeviceConnection.is_working`` changes. + +It is not triggered when the device is created for the first time. + +``management_ip_changed`` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Path**: ``openwisp_controller.config.signals.management_ip_changed`` + +**Arguments**: + +- ``instance``: instance of ``Device`` +- ``management_ip``: value of ``Device.management_ip`` +- ``old_management_ip``: previous value of ``Device.management_ip`` + +This signal is emitted every time ``Device.management_ip`` changes. + +It is not triggered when the device is created for the first time. + +``device_registered`` +~~~~~~~~~~~~~~~~~~~~~ + +**Path**: ``openwisp_controller.config.signals.device_registered`` + +**Arguments**: + +- ``instance``: instance of ``Device`` which got registered. +- ``is_new``: boolean, will be ``True`` when the device is new, ``False`` + when the device already exists (eg: a device which gets a factory reset + will register again) + +This signal is emitted when a device registers automatically through the +controller HTTP API. + +``device_name_changed`` +~~~~~~~~~~~~~~~~~~~~~~~ + +**Path**: ``openwisp_controller.config.signals.device_name_changed`` + +**Arguments**: + +- ``instance``: instance of ``Device``. + +The signal is emitted when the device name changes. + +It is not emitted when the device is created. + +``device_group_changed`` +~~~~~~~~~~~~~~~~~~~~~~~~ + +**Path**: ``openwisp_controller.config.signals.device_group_changed`` + +**Arguments**: + +- ``instance``: instance of ``Device``. +- ``group_id``: primary key of ``DeviceGroup`` of ``Device`` +- ``old_group_id``: primary key of previous ``DeviceGroup`` of ``Device`` + +The signal is emitted when the device group changes. + +It is not emitted when the device is created. + +.. _group_templates_changed: + +``group_templates_changed`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Path**: ``openwisp_controller.config.signals.group_templates_changed`` + +**Arguments**: + +- ``instance``: instance of ``DeviceGroup``. +- ``templates``: list of ``Template`` objects assigned to ``DeviceGroup`` +- ``old_templates``: list of ``Template`` objects assigned earlier to + ``DeviceGroup`` + +The signal is emitted when the device group templates changes. + +It is not emitted when the device is created. + +``subnet_provisioned`` +~~~~~~~~~~~~~~~~~~~~~~ + +**Path**: +``openwisp_controller.subnet_division.signals.subnet_provisioned`` + +**Arguments**: + +- ``instance``: instance of ``VpnClient``. +- ``provisioned``: dictionary of ``Subnet`` and ``IpAddress`` provisioned, + ``None`` if nothing is provisioned + +The signal is emitted when subnets and IP addresses have been provisioned +for a ``VpnClient`` for a VPN server with a subnet with :doc:`subnet +division rule <../user/subnet-division-rules>`. + +``vpn_server_modified`` +~~~~~~~~~~~~~~~~~~~~~~~ + +**Path**: ``openwisp_controller.config.signals.vpn_server_modified`` + +**Arguments**: + +- ``instance``: instance of ``Vpn``. + +The signal is emitted when the VPN server is modified. + +``vpn_peers_changed`` +~~~~~~~~~~~~~~~~~~~~~ + +**Path**: ``openwisp_controller.config.signals.vpn_peers_changed`` + +**Arguments**: + +- ``instance``: instance of ``Vpn``. + +The signal is emitted when the peers of VPN server gets changed. + +It is only emitted for ``Vpn`` object with **WireGuard** or **VXLAN over +WireGuard** backend. diff --git a/docs/images/architecture-v2-openwisp-controller.png b/docs/images/architecture-v2-openwisp-controller.png new file mode 100644 index 0000000000000000000000000000000000000000..c1059c64a81f00ab8ae20304ea59834d2fd8ac83 GIT binary patch literal 397425 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

+ +

+ +Follow the procedure described below to set up `ZeroTier +`_ tunnels on your devices. + +.. include:: ../partials/shared-object.rst + +.. contents:: **Table of Contents**: + :depth: 2 + :local: + +1. Configure Self-Hosted ZeroTier Network Controller +---------------------------------------------------- + +If you haven't already set up a self-hosted ZeroTier network controller on +your server, now is a good time to do so. You can start by simply +installing ZeroTier on your server from the `official website +`_. + +2. Create VPN Server Configuration for ZeroTier +----------------------------------------------- + +1. Visit ``/admin/config/vpn/add/`` to add a new VPN server. +2. We will set **Name** of this VPN server ``ZeroTier`` and **Host** as + ``my-zerotier-server.mydomain.com:9993`` (update this to point to your + ZeroTier VPN server). +3. Select ``ZeroTier`` from the dropdown as **VPN Backend**. +4. When using ZeroTier, OpenWISP takes care of managing IP addresses + (assigning an IP address to each VPN client (ZeroTier network + members)). You can create a new subnet or select an existing one from + the dropdown menu. You can also assign an **Internal IP** to the + ZeroTier controller or leave it empty for OpenWISP to configure. This + IP address will be used to assign it to the ZeroTier controller running + on the server. +5. Set the **Webhook AuthToken**, this will be the ZeroTier authorization + token which you can obtain by running the following command on the + ZeroTier controller: + + .. code-block:: shell + + sudo cat /var/lib/zerotier-one/authtoken.secret + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/vpn-server-1.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/vpn-server-1.png + :alt: ZeroTier VPN server configuration example 1 + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/vpn-server-2.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/vpn-server-2.png + :alt: ZeroTier VPN server configuration example 2 + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/vpn-server-3.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/vpn-server-3.png + :alt: ZeroTier VPN server configuration example 3 + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/vpn-server-4.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/vpn-server-4.png + :alt: ZeroTier VPN server configuration example 4 + +6. After clicking on **Save and continue editing**, OpenWISP automatically + detects the node address of the ZeroTier controller and creates a + ZeroTier network. The **network_id** of this network can be viewed in + the **System Defined Variables** section, where it also provides + internal IP address information. + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/vpn-server-5.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/vpn-server-5.png + :alt: ZeroTier VPN server configuration example 5 + +3. Create VPN Client Template for ZeroTier VPN Server +----------------------------------------------------- + +1. Visit ``/admin/config/template/add/`` to add a new template. +2. Set ``ZeroTier Client`` as **Name** (you can set whatever you want) and + select ``VPN-client`` as **type** from the dropdown list. +3. The **Backend** field refers to the backend of the device this template + can be applied to. For this example, we will leave it to ``OpenWrt``. +4. Select the correct VPN server from the dropdown for the **VPN** field. + Here it is ``ZeroTier``. +5. Ensure that the **Automatic tunnel provisioning** option is checked. + This will enable OpenWISP to automatically provision an IP address and + ZeroTier identity secrets (used for assigning member IDs) for each + ZeroTier VPN client. +6. After clicking on **Save and continue editing** button, you will see + details of *ZeroTier* VPN server in **System Defined Variables**. The + template configuration will be automatically generated which you can + tweak accordingly. We will use the automatically generated VPN client + configuration for this example. + +.. note:: + + OpenWISP uses `zerotier-idtool + `_ + to manage **ZeroTier identity secrets**. Please make sure that you + have `ZeroTier package installed + `_ on the server. + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/template.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/template.png + :alt: ZeroTier VPN client template example + +4. Apply ZeroTier VPN Template to Devices +----------------------------------------- + +.. note:: + + This step assumes that you already have a device registered on + OpenWISP. Register or create a device before proceeding. + +1. Open the **Configuration** tab of the concerned device. +2. Select the *ZeroTier Client* template. +3. Upon clicking the **Save and Continue Editing** button, you will see + entries in the **System Defined Variables** section. These entries will + include **zerotier_member_id**, **identity_secret**, and the internal + **IP address** of the ZeroTier client (network member) on the device, + along with details of the VPN server. + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/device-configuration-1.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/device-configuration-1.png + :alt: ZeroTier VPN device configuration example 1 + +4. Once the configuration is successfully applied to the device, you will + notice a new ZeroTier interface that is up and running. This interface + will have the name ``owzt89f498`` (where ``owzt`` is followed by the + last six hexadecimal characters of the ZeroTier **network ID**). + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/device-configuration-2.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/device-configuration-2.png + :alt: ZeroTier VPN device configuration example 2 + +**Congratulations!** You've successfully configured OpenWISP to manage +ZeroTier tunnels on your devices. + +.. seealso:: + + You may also want to explore other automated VPN tunnel provisioning + options: + + - :doc:`Wireguard ` + - :doc:`Wireguard over VXLAN ` + - :doc:`OpenVPN ` diff --git a/pyproject.toml b/pyproject.toml index eb261c779..c1f5a1ad4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ omit = [ ] [tool.docstrfmt] -extend_exclude = ["**/*.py", "README.rst"] +extend_exclude = ["**/*.py"] [tool.isort] known_third_party = ["django", "django_x509"]

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 A5dZ)H literal 0 HcmV?d00001 diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 000000000..40d4e9301 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,57 @@ +Controller +========== + +.. seealso:: + + **Source code**: `github.com/openwisp/openwisp-controller + `_. + +OpenWISP Controller is responsible of of managing the core resources of +the network and allows automating several aspects like adoption, +provisioning, VPN tunnel configuration, generation of X509 certificates, +subnet and IP address allocation and more. + +For a full introduction please refer to :doc:`user/intro`. + +The following diagram illustrates the role of the Controller module within +the OpenWISP architecture. + +.. figure:: images/architecture-v2-openwisp-controller.png + :target: ../_images/architecture-v2-openwisp-controller.png + :align: center + :alt: OpenWISP Architecture: Controller module + + **OpenWISP Architecture: highlighted controller module** + +.. important:: + + For an enhanced viewing experience, open the image above in a new + browser tab. + + Refer to :doc:`/general/architecture` for more information. + +.. toctree:: + :caption: Controller Module Usage Docs + :maxdepth: 1 + + user/intro.rst + user/templates.rst + user/variables.rst + user/device-groups.rst + user/push-operations.rst + user/shell-commands.rst + user/import-export.rst + user/organization-limits.rst + user/wireguard.rst + user/vxlan-wireguard.rst + user/zerotier.rst + user/openvpn.rst + user/subnet-division-rules.rst + user/rest-api.rst + user/settings.rst + +.. toctree:: + :caption: Controller Module Developer Docs + :maxdepth: 2 + + Developer Docs Index diff --git a/docs/partials/developer-docs.rst b/docs/partials/developer-docs.rst new file mode 100644 index 000000000..9cba48fbb --- /dev/null +++ b/docs/partials/developer-docs.rst @@ -0,0 +1,12 @@ +.. note:: + + This documentation page is aimed at developers who want to customize, + change or extend the code of OpenWISP Controller in order to modify + its behavior (eg: for personal or commercial purposes or to fix a bug, + implement a new feature or contribute to the project in general). + + If you aren't a developer and you are looking for information on how + to use OpenWISP, please refer to: + + - :doc:`General OpenWISP Quickstart ` + - :doc:`OpenWISP Controller User Docs ` diff --git a/docs/partials/shared-object.rst b/docs/partials/shared-object.rst new file mode 100644 index 000000000..ca13c1805 --- /dev/null +++ b/docs/partials/shared-object.rst @@ -0,0 +1,8 @@ +.. note:: + + This guide creates the VPN server and VPN client templates as **Shared + systemwide (no organization)** objects. This allows any device of any + organization to use the automation. + + If needed, you can use any organization as long as the VPN server, the + VPN client template, and devices have the same organization. diff --git a/docs/user/device-groups.rst b/docs/user/device-groups.rst new file mode 100644 index 000000000..0bca0c3cd --- /dev/null +++ b/docs/user/device-groups.rst @@ -0,0 +1,91 @@ +Device Groups +============= + +Device groups allow to group similar devices together, the groups usually +share not only a common characteristic but also some kind of +organizational need: they need to have specific configuration templates, +variables and/or associated metadata which differs from the rest of the +network. + +.. contents:: **Features provided by Device Groups:** + :depth: 2 + :local: + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/1.1/device-groups.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/1.1/device-groups.png + :alt: Device Group example + +.. _device_group_templates: + +Group Templates +--------------- + +Groups allow to define templates which are automatically assigned to +devices belonging to the group. When using this feature, keep in mind the +following important points: + +- Templates of any configuration backend can be selected, when a device is + assigned to a group, only the templates which matches the device + configuration backend are applied to the device. +- The system will not force group templates onto devices, this means that + users can remove the applied group templates from a specific device if + needed. +- If a device group is changed, the system will automatically remove the + group templates of the old group and apply the new templates of the new + group (this operation is implemented by leveraging the + :ref:`group_templates_changed` signal). +- If the group templates are changed, the devices which belong to the + group will be automatically updated to reflect the changes (this + operation is executed in a background task). +- In case the configuration backend of a device is changed, the system + will handle this automatically too and update the group templates + accordingly (this operation is implemented by leveraging the + :ref:`config_backend_changed` signal). +- If a device does not have a configuration defined yet, but it is + assigned to a group which has templates defined, the system will + automatically create a configuration for it using the default backend + specified in the :ref:`OPENWISP_CONTROLLER_DEFAULT_BACKEND` setting. + +**Note:** the list of templates shown in the edit group page do not +contain templates flagged as :ref:`"default" ` or +:ref:`"required" ` to avoid redundancy because those +templates are automatically assigned by the system to new devices. + +This feature works also when editing group templates or the group assigned +to a device via the :ref:`REST API `. + +.. _device_group_variables: + +Group Configuration Variables +----------------------------- + +Groups allow to define configuration variables which are automatically +added to the device's context in the **System Defined Variables**. Check +the :doc:`./variables` section to learn more about precedence of different +configuration variables. + +This feature also works when editing group templates or the group assigned +to a device via the :ref:`REST API `. + +Group Metadata +-------------- + +Groups allow to store additional information regarding a group in the +structured metadata field (which can be accessed via the REST API). + +The metadata field allows custom structure and validation to standardize +information across all groups using the +:ref:`OPENWISP_CONTROLLER_DEVICE_GROUP_SCHEMA` setting. + +Variables vs Metadata +--------------------- + +*Group configuration variables* and *Group metadata* serves different +purposes. + +The group configuration variables should be used when the device +configuration is required to be changed for particular group of devices. + +Group metadata should be used to store additional data for the device +group, this data can be fetched and/or tweaked via the REST API if needed. +Group metadata is not designed to be used for configuration purposes. diff --git a/docs/user/import-export.rst b/docs/user/import-export.rst new file mode 100644 index 000000000..c2d00f03a --- /dev/null +++ b/docs/user/import-export.rst @@ -0,0 +1,35 @@ +Import/Export Device Data +========================= + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/1.1/import-export/device-list.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/1.1/import-export/device-list.png + :alt: Import / Export + +The device list page offers two buttons to export and import device data +in different formats. + +Importing +--------- + +For importing devices into the system, only the required fields are +needed, for example, the following CSV file will import a device named +``TestImport`` with mac address ``00:11:22:09:44:55`` in the organization +with UUID ``3cb5e18c-0312-48ab-8dbd-038b8415bd6f``: + +.. code-block:: + + organization,name,mac_address + 3cb5e18c-0312-48ab-8dbd-038b8415bd6f,TestImport,00:11:22:09:44:55 + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/1.1/import-export/import-page.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/1.1/import-export/import-page.png + :alt: Import / Export + +Exporting +--------- + +The export feature respects any filters selected in the device list. + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/1.1/import-export/export-page.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/1.1/import-export/export-page.png + :alt: Export diff --git a/docs/user/intro.rst b/docs/user/intro.rst new file mode 100644 index 000000000..d9238f71f --- /dev/null +++ b/docs/user/intro.rst @@ -0,0 +1,117 @@ +Controller: Structure & Features +================================ + +OpenWISP Controller is a Python package which ships five Django apps. + +.. contents:: **These Django apps are listed below**: + :depth: 1 + :local: + +Config App +---------- + +The config app is the core of the controller module and implements all the +following features: + +- **Configuration management** for embedded devices supporting: + - `OpenWrt `_ + - `OpenWISP Firmware + `_ + - additional firmware can be added by :ref:`specifying custom + configuration backends ` +- **Configuration editor** based on `JSON-Schema editor + `_ +- **Advanced edit mode**: edit `NetJSON `_ + *DeviceConfiguration* objects for maximum flexibility +- :doc:`templates`: reduce repetition to the minimum, configure default + and required templates +- :doc:`variables`: reference variables in the configuration and templates +- :doc:`device-groups`: define different set of default configuration and + metadata in device groups +- :ref:`Template Tags `: define different sets of default + templates (eg: mesh, WDS, 4G) +- **HTTP resources**: allow devices to automatically check for and + download configuration updates +- **VPN management**: automatically provision VPN tunnel configurations, + including cryptographic keys and IP addresses, eg: :doc:`OpenVPN + `, :doc:`WireGuard ` +- :doc:`import-export` + +It exposes various :doc:`REST API endpoints `. + +PKI App +------- + +The PKI app is based on `django-x509 +`_, allowing you to create, +import, and view x509 CAs and certificates directly from the +administration dashboard. + +It exposes various :doc:`REST API endpoints `. + +Connection App +-------------- + +This app enables OpenWISP Controller to use different protocols to reach +network devices. Currently, the default connection protocols are SSH and +SNMP, but the protocol mechanism is extensible, allowing for +implementation of additional protocols if needed. + +It exposes various :doc:`REST API endpoints `. + +SSH +~~~ + +The SSH connector allows the controller to initialize connections to the +devices in order to perform :doc:`push operations `, +e.g.: + +- Sending configuration updates. +- :doc:`Executing shell commands `. +- Perform firmware upgrades via the additional :doc:`firmware upgrade + module `. + +The default connection protocol implemented is SSH, but other protocol +mechanism is extensible and custom protocols can be implemented as well. + +Access via SSH key is recommended, the SSH key algorithms supported are: + +- RSA +- Ed25519 + +SNMP +~~~~ + +The SNMP connector is useful to collect monitoring information and it's +used in :doc:`OpenWISP Monitoring ` for performing +checks to collect monitoring information. `Read more +`_ +on how to use it. + +Geo App +------- + +The geographic app is based on `django-loci +`_ and allows to define the +geographic coordinates of the devices, as well as their indoor coordinates +on floorplan images. + +It exposes various :doc:`REST API endpoints `. + +Subnet Division App +------------------- + +.. note:: + + This app is optional, if you don't need it you can avoid adding it to + ``settings.INSTALLED_APPS``. + +This app allows to automatically provision subnets and IP addresses which +will be available as :ref:`system defined configuration variables +` that can be used in :doc:`templates`. + +The purpose of this app is to allow users to automatically provision and +configure specific subnets and IP addresses to the devices without the +need of manual intervention. + +Refer to :doc:`subnet-division-rules` for more information. diff --git a/docs/user/openvpn.rst b/docs/user/openvpn.rst new file mode 100644 index 000000000..d644ec807 --- /dev/null +++ b/docs/user/openvpn.rst @@ -0,0 +1,223 @@ +Automating OpenVPN Tunnels +========================== + +In this guide, we will explore how to set up the automatic provisioning +and management of **OpenVPN tunnels**. + +.. contents:: **Table of Contents**: + :backlinks: none + :depth: 3 + +Setting up the OpenVPN Server +----------------------------- + +The first step is to install the OpenVPN server. In this tutorial, to +perform this step we will use Ansible. + +If you already have experience installing an OpenVPN server, feel free to +use any method you prefer. + +.. important:: + + If you have already set up your OpenVPN server or prefer to install + the OpenVPN server using a different method, you can skip forward to + :ref:`import_ca_and_server_cert`. + +For simplicity, **the OpenVPN server must be installed on the same server +where OpenWISP is also installed**. + +While it is possible to install the OpenVPN server on a different server, +it requires additional steps not covered in this tutorial. + +1. Install Ansible and Required Ansible Roles +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Install Ansible **on your local machine** (please ensure that you do not +install it on the server). + +To **install Ansible**, we suggest following the official `Ansible +installation guide +`_. + +After installing Ansible, you need to install Git (example for Linux +Debian/Ubuntu systems): + +.. code-block:: bash + + sudo apt-get install git + +After installing both Ansible and Git, install the required roles: + +.. code-block:: bash + + ansible-galaxy install git+https://github.com/Stouts/Stouts.openvpn,3.0.0 nkakouros.easyrsa + +2. Create Inventory File and Playbook YAML +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Create an Ansible inventory file named ``inventory`` **on your local +machine** (not on the server) with the following contents: + +.. code-block:: + + [openvpn] + your_server_domain_or_ip + +For example, if your server IP is ``192.168.56.2``: + +.. code-block:: + + [openvpn] + 192.168.56.2 + +In the same directory where you created the ``inventory`` file, create a +file named ``playbook.yml`` with the following content: + +.. code-block:: yaml + + - hosts: openvpn + vars: + # EasyRSA + easyrsa_generate_dh: true + easyrsa_servers: + - name: server + easyrsa_clients: [] + easyrsa_pki_dir: /etc/easyrsa/pki + + # OpenVPN + openvpn_keydir: "{{ easyrsa_pki_dir }}" + openvpn_clients: [] + openvpn_use_pam: false + roles: + - role: nkakouros.easyrsa + - role: Stouts.openvpn + +.. hint:: + + You can further customize the configuration using the role variables. + Read more about other options in `EasyRSA + `_ and + `OpenVPN `_. + +3. Run the Playbook +~~~~~~~~~~~~~~~~~~~ + +Run the Ansible playbook: + +.. code-block:: bash + + ansible-playbook -i inventory playbook.yml -b -k -K --become-method=su + +.. _import_ca_and_server_cert: + +Import the CA and the Server Certificate in OpenWISP +---------------------------------------------------- + +.. important:: + + If you chose an alternative installation method for OpenVPN and you + did not create the CA and certificate yet, you can create the + certificates from scratch via the OpenWISP web interface instead of + importing them. + + Follow the instructions below and instead of selecting + :guilabel:`Import Existing` as :guilabel:`Operation Type`, select + :guilabel:`Create new`. + + You also won't need to copy any file from the server as OpenWISP + generates the x509 certificates automatically. + +To import the CA and Server Certificate into OpenWISP, you need to access +your server via ``ssh`` or any other method that suits you. + +Change your directory to ``/etc/easyrsa/pki/``. + +.. note:: + + If you incurr inthe following error: ``-bash: cd: /etc/easyrsa/pki: + Permission denied``, you may need to log in as the root user. + +Import the CA +~~~~~~~~~~~~~ + +In your OpenWISP dashboard, go to ``/admin/pki/ca/add/``. + +In :guilabel:`Operation Type`, choose :guilabel:`Import Existing`. + +Get your CA certificate from the ``ca.crt`` file and the private key from +the ``private/ca.key`` file, then enter them in the respective fields. + +Import the Server Certificate +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In your OpenWISP dashboard, go to ``/admin/pki/cert/add/``. + +In :guilabel:`Operation Type`, choose :guilabel:`Import Existing` and in +**CA**, choose the CA you just created. + +Get your server certificate from the ``issued/server.crt`` file and the +server private key from the ``private/server.key`` file, then enter them +in the respective fields. + +Create the VPN Server in OpenWISP +--------------------------------- + +In the OpenWISP dashboard, go to ``/admin/config/vpn/add/``. + +In the :guilabel:`Host` field, enter your server IP address. In the +:guilabel:`Certification Authority` and :guilabel:`X509 Certificate` +fields, select the CA and certificate you created in the previous step. + +Under :guilabel:`Configuration`, click on :guilabel:`Configuration Menu`, +then change :guilabel:`Server (Bridged)` to :guilabel:`Server (Routed)`. + +Setting up a Bridged Server is similar to setting up a Routed Server but +is not covered in this tutorial. + +Adjust the rest of the VPN configuration to match the settings in +``/etc/openvpn/server.conf``. + +.. tip:: + + You can verify if your VPN configuration matches the ``server.conf`` + file by using the :guilabel:`Preview Configuration` button at the top + right corner of the page. + +Create the VPN-Client Template in OpenWISP +------------------------------------------ + +In your OpenWISP dashboard, go to ``/admin/config/template/add/``. + +Set the :guilabel:`Type` to :guilabel:`VPN-client`. + +Once the :guilabel:`VPN` field appears, select the VPN you created in the +previous step. + +Ensure the :guilabel:`Automatic tunnel provisioning` flag remains enabled. + +If this template is for your management VPN or the default VPN option, we +recommend checking the :guilabel:`Enabled by default` flag. For more +information about this flag, refer to :ref:`default_templates`. + +Now, save the template. + +After saving the template, you can tweak the VPN Client configuration, +which is automatically generated to be compatible with the server +configuration. + +Finally you can add the new template to your devices. + +.. tip:: + + If you need to troubleshoot any issue, increase the verbosity of the + OpenVPN logging, both on the server and the clients, and check both + logs (on the server and on the client). + +.. seealso:: + + You may also want to explore other automated VPN tunnel provisioning + options: + + - :doc:`Wireguard ` + - :doc:`Wireguard over VXLAN ` + - :doc:`Zerotier ` diff --git a/docs/user/organization-limits.rst b/docs/user/organization-limits.rst new file mode 100644 index 000000000..2b3a9d98b --- /dev/null +++ b/docs/user/organization-limits.rst @@ -0,0 +1,17 @@ +Organization Limits +=================== + +You can restrict the number of devices managed by each organization. + +To set these limits: + +1. Navigate to **USERS & ORGANIZATIONS** on the left-hand navigation menu. +2. Go to **Organizations**. +3. Click on the specific organization you want to limit. +4. In the **CONTROLLER LIMIT** section, set the desired limit. + +Refer to the screenshot below for guidance: + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/1.1/organization-limits.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/1.1/organization-limits.png + :alt: Organization limits diff --git a/docs/user/push-operations.rst b/docs/user/push-operations.rst new file mode 100644 index 000000000..54368b37a --- /dev/null +++ b/docs/user/push-operations.rst @@ -0,0 +1,117 @@ +Configuring Push Operations +=========================== + +.. contents:: **Table of Contents**: + :depth: 2 + :local: + +Introduction +------------ + +.. important:: + + If you have installed OpenWISP with the `ansbile-openwisp2 role + `_ you can skip the + following steps, which are handled automatically by the ansible role + during the first installation. + +The Ansible role automatically creates a default template to update +``authorized_keys`` on networking devices using the default access +credentials. + +Follow the procedure described below to enable secure SSH access from +OpenWISP to your devices, this is required to enable push operations +(whenever the configuration is changed, OpenWISP will trigger the update +in the background) and/or :doc:`firmware upgrades (via the additional +module openwisp-firmware-upgrader) `. + +1. Generate SSH Key +------------------- + +First of all, we need to generate the SSH key which will be used by +OpenWISP to access the devices, to do so, you can use the following +command: + +.. code-block:: shell + + echo './sshkey' | ssh-keygen -t ed25519 -C "openwisp" + +This will create two files in the current directory, one called ``sshkey`` +(the private key) and one called ``sshkey.pub`` (the public key). + +Store the content of these files in a secure location. + +**Note:** Support for **ED25519** was added in OpenWrt 21.02 (requires +Dropbear > 2020.79). If you are managing devices with OpenWrt < 21, then +you will need to use RSA keys: + +.. code-block:: shell + + echo './sshkey' | ssh-keygen -t rsa -b 4096 -C "openwisp" + +2. Save SSH Private Key in "Access Credentials" +----------------------------------------------- + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/add-ssh-credentials-private-key.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/add-ssh-credentials-private-key.png + :alt: add SSH private key as access credential in OpenWISP + +From the first page of OpenWISP click on "CONFIGURATIONS" in the left +navigation menu, then "Access credentials", then click on the **"ADD +ACCESS CREDENTIALS"** button in the upper right corner (alternatively, go +to the following URL path: ``/admin/connection/credentials/add/``). + +Select SSH as ``type``, enable the **Auto add** checkbox, then at the +field "Credentials type" select "SSH (private key)", now type "root" in +the ``username`` field, while in the ``key`` field you have to paste the +contents of the private key just created. + +Now hit save. + +The credentials just created will be automatically enabled for all the +devices in the system (both existing devices and devices which will be +added in the future). + +3. Add the Public Key to Your Devices +------------------------------------- + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/add-authorized-ssh-keys-template.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/add-authorized-ssh-keys-template.png + :alt: Add authorized SSH public keys template to OpenWISP (OpenWrt) + +Now we need to instruct your devices to allow OpenWISP accessing via SSH, +in order to do this we need to add the contents of the public key file +created in step 1 (``sshkey.pub``) in the file +``/etc/dropbear/authorized_keys`` on the devices, the recommended way to +do this is to create a configuration template in OpenWISP: from the first +page of OpenWISP, click on "CONFIGURATIONS" in the left navigation menu, +then and click on the **"ADD TEMPLATE"** button in the upper right corner +(alternatively, go to the following URL: ``/admin/config/template/add/``). + +Check **enabled by default**, then scroll down the configuration section, +click on "Configuration Menu", scroll down, click on "Files" then close +the menu by clicking again on "Configuration Menu". Now type +``/etc/dropbear/authorized_keys`` in the ``path`` field of the file, then +paste the contents of ``sshkey.pub`` in ``contents``. + +Now hit save. + +**There's a catch**: you will need to assign the template to any existing +device. + +4. Test It +---------- + +Once you have performed the 3 steps above, you can test it as follows: + +1. Ensure there's at least one device turned on and connected to OpenWISP, + ensure this device has the "SSH Authorized Keys" assigned to it. +2. Ensure the celery worker of OpenWISP Controller is running (eg: ``ps + aux | grep celery``) +3. SSH into the device and wait (maximum 2 minutes) until + ``/etc/dropbear/authorized_keys`` appears as specified in the template. +4. While connected via SSH to the device run the following command in the + console: ``logread -f``, now try changing the device name in OpenWISP +5. Shortly after you change the name in OpenWISP, you should see some + output in the SSH console indicating another SSH access and the + configuration update being performed. diff --git a/docs/user/rest-api.rst b/docs/user/rest-api.rst new file mode 100644 index 000000000..b10dcdfbc --- /dev/null +++ b/docs/user/rest-api.rst @@ -0,0 +1,1152 @@ +REST API Reference +================== + +.. contents:: **Table of contents**: + :depth: 1 + :local: + +.. _controller_live_documentation: + +Live Documentation +------------------ + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/live-docu-api.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/live-docu-api.png + +A general live API documentation (following the OpenAPI specification) at +``/api/v1/docs/``. + +.. _controller_browsable_web_interface: + +Browsable Web Interface +----------------------- + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/browsable-api-ui.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/browsable-api-ui.png + +Additionally, opening any of the endpoints :ref:`listed below +` directly in the browser will show the +`browsable API interface of Django-REST-Framework +`_, which +makes it even easier to find out the details of each endpoint. + +Authentication +-------------- + +See :ref:`authenticating_rest_api`. + +When browsing the API via the :ref:`controller_live_documentation` or the +:ref:`controller_browsable_web_interface`, you can also use the session +authentication by logging in the django admin. + +Pagination +---------- + +All *list* endpoints support the ``page_size`` parameter that allows +paginating the results in conjunction with the ``page`` parameter. + +.. code-block:: text + + GET /api/v1/controller/template/?page_size=10 + GET /api/v1/controller/template/?page_size=10&page=2 + +.. _controller_rest_endpoints: + +List of Endpoints +----------------- + +Since the detailed explanation is contained in the +:ref:`controller_live_documentation` and in the +:ref:`controller_browsable_web_interface` of each point, here we'll +provide just a list of the available endpoints, for further information +please open the URL of the endpoint in your browser. + +List Devices +~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/device/ + +**Available filters** + +You can filter a list of devices based on their configuration status using +the ``status`` (e.g modified, applied, or error). + +.. code-block:: text + + GET /api/v1/controller/device/?config__status={status} + +You can filter a list of devices based on their configuration backend +using the ``backend`` (e.g netjsonconfig.OpenWrt or +netjsonconfig.OpenWisp). + +.. code-block:: text + + GET /api/v1/controller/device/?config__backend={backend} + +You can filter a list of devices based on their organization using the +``organization_id`` or ``organization_slug``. + +.. code-block:: text + + GET /api/v1/controller/device/?organization={organization_id} + +.. code-block:: text + + GET /api/v1/controller/device/?organization_slug={organization_slug} + +You can filter a list of devices based on their configuration templates +using the ``template_id``. + +.. code-block:: text + + GET /api/v1/controller/device/?config__templates={template_id} + +You can filter a list of devices based on their device group using the +``group_id``. + +.. code-block:: text + + GET /api/v1/controller/device/?group={group_id} + +You can filter a list of devices that have a device location object using +the ``with_geo`` (eg. true or false). + +.. code-block:: text + + GET /api/v1/controller/device/?with_geo={with_geo} + +You can filter a list of devices based on their creation time using the +``creation_time``. + +.. code-block:: text + + # Created exact + GET /api/v1/controller/device/?created={creation_time} + + # Created greater than or equal to + GET /api/v1/controller/device/?created__gte={creation_time} + + # Created is less than + GET /api/v1/controller/device/?created__lt={creation_time} + +Create Device +~~~~~~~~~~~~~ + +.. code-block:: text + + POST /api/v1/controller/device/ + +Get Device Detail +~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/device/{id}/ + +Download Device Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/device/{id}/configuration/ + +The above endpoint triggers the download of a ``tar.gz`` file containing +the generated configuration for that specific device. + +Change Details of Device +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + PUT /api/v1/controller/device/{id}/ + +Patch Details of Device +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + PATCH /api/v1/controller/device/{id}/ + +**Note**: To assign, unassign, and change the order of the assigned +templates add, remove, and change the order of the ``{id}`` of the +templates under the ``config`` field in the JSON response respectively. +Moreover, you can also select and unselect templates in the HTML Form of +the Browsable API. + +The required template(s) from the organization(s) of the device will added +automatically to the ``config`` and cannot be removed. + +**Example usage**: For assigning template(s) add the/their {id} to the +config of a device, + +.. code-block:: shell + + curl -X PATCH \ + http://127.0.0.1:8000/api/v1/controller/device/76b7d9cc-4ffd-4a43-b1b0-8f8befd1a7c0/ \ + -H 'authorization: Bearer dc8d497838d4914c9db9aad9b6ec66f6c36ff46b' \ + -H 'content-type: application/json' \ + -d '{ + "config": { + "templates": ["4791fa4c-2cef-4f42-8bb4-c86018d71bd3"] + } + }' + +**Example usage**: For removing assigned templates, simply remove +the/their {id} from the config of a device, + +.. code-block:: shell + + curl -X PATCH \ + http://127.0.0.1:8000/api/v1/controller/device/76b7d9cc-4ffd-4a43-b1b0-8f8befd1a7c0/ \ + -H 'authorization: Bearer dc8d497838d4914c9db9aad9b6ec66f6c36ff46b' \ + -H 'content-type: application/json' \ + -d '{ + "config": { + "templates": [] + } + }' + +**Example usage**: For reordering the templates simply change their order +from the config of a device, + +.. code-block:: shell + + curl -X PATCH \ + http://127.0.0.1:8000/api/v1/controller/device/76b7d9cc-4ffd-4a43-b1b0-8f8befd1a7c0/ \ + -H 'authorization: Bearer dc8d497838d4914c9db9aad9b6ec66f6c36ff46b' \ + -H 'cache-control: no-cache' \ + -H 'content-type: application/json' \ + -H 'postman-token: b3f6a1cc-ff13-5eba-e460-8f394e485801' \ + -d '{ + "config": { + "templates": [ + "c5bbc697-170e-44bc-8eb7-b944b55ee88f", + "4791fa4c-2cef-4f42-8bb4-c86018d71bd3" + ] + } + }' + +Delete Device +~~~~~~~~~~~~~ + +.. code-block:: text + + DELETE /api/v1/controller/device/{id}/ + +List Device Connections +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/device/{id}/connection/ + +Create Device Connection +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + POST /api/v1/controller/device/{id}/connection/ + +Get Device Connection Detail +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/device/{id}/connection/{id}/ + +Change Device Connection Detail +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + PUT /api/v1/controller/device/{id}/connection/{id}/ + +Patch Device Connection Detail +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + PATCH /api/v1/controller/device/{id}/connection/{id}/ + +Delete Device Connection +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + DELETE /api/v1/controller/device/{id}/connection/{id}/ + +List Credentials +~~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/connection/credential/ + +Create Credential +~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + POST /api/v1/connection/credential/ + +Get Credential Detail +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/connection/credential/{id}/ + +Change Credential Detail +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + PUT /api/v1/connection/credential/{id}/ + +Patch Credential Detail +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + PATCH /api/v1/connection/credential/{id}/ + +Delete Credential +~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + DELETE /api/v1/connection/credential/{id}/ + +List Commands of a Device +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/device/{id}/command/ + +Execute a Command a Device +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + POST /api/v1/controller/device/{id}/command/ + +Get Command Details +~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/device/{device_id}/command/{command_id}/ + +List Device Groups +~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/group/ + +**Available filters** + +You can filter a list of device groups based on their organization using +the ``organization_id`` or ``organization_slug``. + +.. code-block:: text + + GET /api/v1/controller/group/?organization={organization_id} + +.. code-block:: text + + GET /api/v1/controller/group/?organization_slug={organization_slug} + +You can filter a list of device groups that have a device object using the +``empty`` (eg. true or false). + +.. code-block:: text + + GET /api/v1/controller/group/?empty={empty} + +Create Device Group +~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + POST /api/v1/controller/group/ + +Get Device Group Detail +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/group/{id}/ + +.. _change_device_group_detail: + +Change Device Group Detail +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + PUT /api/v1/controller/group/{id}/ + +This endpoint allows to change the :ref:`device_group_templates` too. + +Get Device Group from Certificate Common Name +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/cert/{common_name}/group/ + +This endpoint can be used to retrieve group information and metadata by +the common name of a certificate used in a VPN client tunnel, this +endpoint is used in layer 2 tunneling solutions for firewall/captive +portals. + +It is also possible to filter device group by providing organization slug +of certificate's organization as show in the example below: + +.. code-block:: text + + GET /api/v1/controller/cert/{common_name}/group/?org={org1_slug},{org2_slug} + +Get Device Location +~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/device/{id}/location/ + +.. _create_device_location: + +Create Device Location +~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + PUT /api/v1/controller/device/{id}/location/ + +You can create ``DeviceLocation`` object by using primary keys of existing +``Location`` and ``FloorPlan`` objects as shown in the example below. + +.. code-block:: json + + { + "location": "f0cb5762-3711-4791-95b6-c2f6656249fa", + "floorplan": "dfeb6724-aab4-4533-aeab-f7feb6648acd", + "indoor": "-36,264" + } + +**Note:** The ``indoor`` field represents the coordinates of the point +placed on the image from the top left corner. E.g. if you placed the +pointer on the top left corner of the floorplan image, its indoor +coordinates will be ``0,0``. + +.. code-block:: text + + curl -X PUT \ + http://127.0.0.1:8000/api/v1/controller/device/8a85cc23-bad5-4c7e-b9f4-ffe298defb5c/location/ \ + -H 'authorization: Bearer dc8d497838d4914c9db9aad9b6ec66f6c36ff46b' \ + -H 'content-type: application/json' \ + -d '{ + "location": "f0cb5762-3711-4791-95b6-c2f6656249fa", + "floorplan": "dfeb6724-aab4-4533-aeab-f7feb6648acd", + "indoor": "-36,264" + }' + +You can also create related ``Location`` and ``FloorPlan`` objects for the +device directly from this endpoint. + +The following example demonstrates creating related location object in a +single request. + +.. code-block:: json + + { + "location": { + "name": "Via del Corso", + "address": "Via del Corso, Roma, Italia", + "geometry": { + "type": "Point", + "coordinates": [12.512124, 41.898903] + }, + "type": "outdoor", + } + } + +.. code-block:: text + + curl -X PUT \ + http://127.0.0.1:8000/api/v1/controller/device/8a85cc23-bad5-4c7e-b9f4-ffe298defb5c/location/ \ + -H 'authorization: Bearer dc8d497838d4914c9db9aad9b6ec66f6c36ff46b' \ + -H 'content-type: application/json' \ + -d '{ + "location": { + "name": "Via del Corso", + "address": "Via del Corso, Roma, Italia", + "geometry": { + "type": "Point", + "coordinates": [12.512124, 41.898903] + }, + "type": "outdoor" + } + }' + +**Note:** You can also specify the ``geometry`` in **Well-known text +(WKT)** format, like following: + +.. code-block:: json + + { + "location": { + "name": "Via del Corso", + "address": "Via del Corso, Roma, Italia", + "geometry": "POINT (12.512124 41.898903)", + "type": "outdoor", + } + } + +Similarly, you can create ``Floorplan`` object with the same request. But, +note that a ``FloorPlan`` can be added to ``DeviceLocation`` only if the +related ``Location`` object defines an indoor location. The example below +demonstrates creating both ``Location`` and ``FloorPlan`` objects. + +.. code-block:: text + + // This is not a valid JSON object. The JSON format is + // only used for showing available fields. + { + "location.name": "Via del Corso", + "location.address": "Via del Corso, Roma, Italia", + "location.geometry.type": "Point", + "location.geometry.coordinates": [12.512124, 41.898903] + "location.type": "outdoor", + "floorplan.floor": 1, + "floorplan.image": floorplan.png, + } + +.. code-block:: text + + curl -X PUT \ + http://127.0.0.1:8000/api/v1/controller/device/8a85cc23-bad5-4c7e-b9f4-ffe298defb5c/location/ \ + -H 'authorization: Bearer dc8d497838d4914c9db9aad9b6ec66f6c36ff46b' \ + -H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' \ + -F 'location.name=Via del Corso' \ + -F 'location.address=Via del Corso, Roma, Italia' \ + -F location.geometry.type=Point \ + -F 'location.geometry.coordinates=[12.512124, 41.898903]' \ + -F location.type=indoor \ + -F floorplan.floor=1 \ + -F 'floorplan.image=@floorplan.png' + +**Note:** The request in above example uses ``multipart content-type`` for +uploading floorplan image. + +You can also use an existing ``Location`` object and create a new +floorplan for that location using this endpoint. + +.. code-block:: text + + // This is not a valid JSON object. The JSON format is + // only used for showing available fields. + { + "location": "f0cb5762-3711-4791-95b6-c2f6656249fa", + "floorplan.floor": 1, + "floorplan.image": floorplan.png + } + +.. code-block:: text + + curl -X PUT \ + http://127.0.0.1:8000/api/v1/controller/device/8a85cc23-bad5-4c7e-b9f4-ffe298defb5c/location/ \ + -H 'authorization: Bearer dc8d497838d4914c9db9aad9b6ec66f6c36ff46b' \ + -H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' \ + -F location=f0cb5762-3711-4791-95b6-c2f6656249fa \ + -F floorplan.floor=1 \ + -F 'floorplan.image=@floorplan.png' + +Change Details of Device Location +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + PUT /api/v1/controller/device/{id}/location/ + +**Note:** This endpoint can be used to update related ``Location`` and +``Floorplan`` objects. Refer to the :ref:`examples in the "Create device +location" section ` for information on payload +format. + +Delete Device Location +~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + DELETE /api/v1/controller/device/{id}/location/ + +Get Device Coordinates +~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/device/{id}/coordinates/ + +**Note:** This endpoint is intended to be used by devices. + +This endpoint skips multi-tenancy and permission checks if the device +``key`` is passed as ``query_param`` because the system assumes that the +device is updating it's position. + +.. code-block:: text + + curl -X GET \ + 'http://127.0.0.1:8000/api/v1/controller/device/8a85cc23-bad5-4c7e-b9f4-ffe298defb5c/coordinates/?key=10a0cb5a553c71099c0e4ef236435496' + +Update Device Coordinates +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + PUT /api/v1/controller/device/{id}/coordinates/ + +**Note:** This endpoint is intended to be used by devices. + +This endpoint skips multi-tenancy and permission checks if the device +``key`` is passed as ``query_param`` because the system assumes that the +device is updating it's position. + +.. code-block:: json + + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [12.512124, 41.898903] + }, + } + +.. code-block:: text + + curl -X PUT \ + 'http://127.0.0.1:8000/api/v1/controller/device/8a85cc23-bad5-4c7e-b9f4-ffe298defb5c/coordinates/?key=10a0cb5a553c71099c0e4ef236435496' \ + -H 'content-type: application/json' \ + -d '{ + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [12.512124, 41.898903] + }, + }' + +List Locations +~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/location/ + +**Available filters** + +You can filter using ``organization_id`` or ``organization_slug`` to get +list locations that belongs to an organization. + +.. code-block:: text + + GET /api/v1/controller/location/?organization={organization_id} + +.. code-block:: text + + GET /api/v1/controller/location/?organization_slug={organization_slug} + +Create Location +~~~~~~~~~~~~~~~ + +.. code-block:: text + + POST /api/v1/controller/location/ + +If you are creating an ``indoor`` location, you can use this endpoint to +create floorplan for the location. + +The following example demonstrates creating floorplan along with location +in a single request. + +.. code-block:: text + + { + "name": "Via del Corso", + "address": "Via del Corso, Roma, Italia", + "geometry.type": "Point", + "geometry.location": [12.512124, 41.898903], + "type": "indoor", + "is_mobile": "false", + "floorplan.floor": "1", + "floorplan.image": floorplan.png, + "organization": "1f6c5666-1011-4f1d-bce9-fc6fcb4f3a05" + } + +.. code-block:: text + + curl -X POST \ + http://127.0.0.1:8000/api/v1/controller/location/ \ + -H 'authorization: Bearer dc8d497838d4914c9db9aad9b6ec66f6c36ff46b' \ + -H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' \ + -F 'name=Via del Corso' \ + -F 'address=Via del Corso, Roma, Italia' \ + -F geometry.type=Point \ + -F 'geometry.coordinates=[12.512124, 41.898903]' \ + -F type=indoor \ + -F is_mobile=false \ + -F floorplan.floor=1 \ + -F 'floorplan.image=@floorplan.png' \ + -F organization=1f6c5666-1011-4f1d-bce9-fc6fcb4f3a05 + +**Note:** You can also specify the ``geometry`` in **Well-known text +(WKT)** format, like following: + +.. code-block:: text + + { + "name": "Via del Corso", + "address": "Via del Corso, Roma, Italia", + "geometry": "POINT (12.512124 41.898903)", + "type": "indoor", + "is_mobile": "false", + "floorplan.floor": "1", + "floorplan.image": floorplan.png, + "organization": "1f6c5666-1011-4f1d-bce9-fc6fcb4f3a05" + } + +Get Location Details +~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/location/{pk}/ + +Change Location Details +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + PUT /api/v1/controller/location/{pk}/ + +**Note**: Only the first floorplan data present can be edited or changed. +Setting the ``type`` of location to outdoor will remove all the floorplans +associated with it. + +Refer to the :ref:`examples in the "Create device location" section +` for information on payload format. + +Delete Location +~~~~~~~~~~~~~~~ + +.. code-block:: text + + DELETE /api/v1/controller/location/{pk}/ + +List Devices in a Location +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/location/{id}/device/ + +List Locations with Devices Deployed (in GeoJSON Format) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Note**: this endpoint will only list locations that have been assigned +to a device. + +.. code-block:: text + + GET /api/v1/controller/location/geojson/ + +**Available filters** + +You can filter using ``organization_id`` or ``organization_slug`` to get +list location of devices from that organization. + +.. code-block:: text + + GET /api/v1/controller/location/geojson/?organization_id={organization_id} + +.. code-block:: text + + GET /api/v1/controller/location/geojson/?organization_slug={organization_slug} + +List Floorplans +~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/floorplan/ + +**Available filters** + +You can filter using ``organization_id`` or ``organization_slug`` to get +list floorplans that belongs to an organization. + +.. code-block:: text + + GET /api/v1/controller/floorplan/?organization={organization_id} + +.. code-block:: text + + GET /api/v1/controller/floorplan/?organization_slug={organization_slug} + +Create Floorplan +~~~~~~~~~~~~~~~~ + +.. code-block:: text + + POST /api/v1/controller/floorplan/ + +Get Floorplan Details +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/floorplan/{pk}/ + +Change Floorplan Details +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + PUT /api/v1/controller/floorplan/{pk}/ + +Delete floorplan +~~~~~~~~~~~~~~~~ + +.. code-block:: text + + DELETE /api/v1/controller/floorplan/{pk}/ + +List Templates +~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/template/ + +**Available filters** + +You can filter a list of templates based on their organization using the +``organization_id`` or ``organization_slug``. + +.. code-block:: text + + GET /api/v1/controller/template/?organization={organization_id} + +.. code-block:: text + + GET /api/v1/controller/template/?organization_slug={organization_slug} + +You can filter a list of templates based on their backend using the +``backend`` (e.g netjsonconfig.OpenWrt or netjsonconfig.OpenWisp). + +.. code-block:: text + + GET /api/v1/controller/template/?backend={backend} + +You can filter a list of templates based on their type using the ``type`` +(eg. vpn or generic). + +.. code-block:: text + + GET /api/v1/controller/template/?type={type} + +You can filter a list of templates that are enabled by default or not +using the ``default`` (eg. true or false). + +.. code-block:: text + + GET /api/v1/controller/template/?default={default} + +You can filter a list of templates that are required or not using the +``required`` (eg. true or false). + +.. code-block:: text + + GET /api/v1/controller/template/?required={required} + +You can filter a list of templates based on their creation time using the +``creation_time``. + +.. code-block:: text + + # Created exact + + GET /api/v1/controller/template/?created={creation_time} + + # Created greater than or equal to + + GET /api/v1/controller/template/?created__gte={creation_time} + + # Created is less than + + GET /api/v1/controller/template/?created__lt={creation_time} + +Create Template +~~~~~~~~~~~~~~~ + +.. code-block:: text + + POST /api/v1/controller/template/ + +Get Template Detail +~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/template/{id}/ + +Download Template Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/template/{id}/configuration/ + +The above endpoint triggers the download of a ``tar.gz`` file containing +the generated configuration for that specific template. + +Change Details of Template +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + PUT /api/v1/controller/template/{id}/ + +Patch Details of Template +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + PATCH /api/v1/controller/template/{id}/ + +Delete Template +~~~~~~~~~~~~~~~ + +.. code-block:: text + + DELETE /api/v1/controller/template/{id}/ + +List VPNs +~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/vpn/ + +**Available filters** + +You can filter a list of vpns based on their backend using the ``backend`` +(e.g openwisp_controller.vpn_backends.OpenVpn or +openwisp_controller.vpn_backends.Wireguard). + +.. code-block:: text + + GET /api/v1/controller/vpn/?backend={backend} + +You can filter a list of vpns based on their subnet using the +``subnet_id``. + +.. code-block:: text + + GET /api/v1/controller/vpn/?subnet={subnet_id} + +You can filter a list of vpns based on their organization using the +``organization_id`` or ``organization_slug``. + +.. code-block:: text + + GET /api/v1/controller/vpn/?organization={organization_id} + +.. code-block:: text + + GET /api/v1/controller/vpn/?organization_slug={organization_slug} + +Create VPN +~~~~~~~~~~ + +.. code-block:: text + + POST /api/v1/controller/vpn/ + +Get VPN detail +~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/vpn/{id}/ + +Download VPN Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/vpn/{id}/configuration/ + +The above endpoint triggers the download of a ``tar.gz`` file containing +the generated configuration for that specific VPN. + +Change Details of VPN +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + PUT /api/v1/controller/vpn/{id}/ + +Patch Details of VPN +~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + PATCH /api/v1/controller/vpn/{id}/ + +Delete VPN +~~~~~~~~~~ + +.. code-block:: text + + DELETE /api/v1/controller/vpn/{id}/ + +List CA +~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/ca/ + +Create New CA +~~~~~~~~~~~~~ + +.. code-block:: text + + POST /api/v1/controller/ca/ + +Import Existing CA +~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + POST /api/v1/controller/ca/ + +**Note**: To import an existing CA, only ``name``, ``certificate`` and +``private_key`` fields have to be filled in the ``HTML`` form or included +in the ``JSON`` format. + +Get CA Detail +~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/ca/{id}/ + +Change Details of CA +~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + PUT /api/v1/controller/ca/{id}/ + +Patch Details of CA +~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + PATCH /api/v1/controller/ca/{id}/ + +Download CA(crl) +~~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/ca/{id}/crl/ + +The above endpoint triggers the download of ``{id}.crl`` file containing +up to date CRL of that specific CA. + +Delete CA +~~~~~~~~~ + +.. code-block:: text + + DELETE /api/v1/controller/ca/{id}/ + +Renew CA +~~~~~~~~ + +.. code-block:: text + + POST /api/v1/controller/ca/{id}/renew/ + +List Cert +~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/cert/ + +Create New Cert +~~~~~~~~~~~~~~~ + +.. code-block:: text + + POST /api/v1/controller/cert/ + +Import Existing Cert +~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + POST /api/v1/controller/cert/ + +**Note**: To import an existing Cert, only ``name``, ``ca``, +``certificate`` and ``private_key`` fields have to be filled in the +``HTML`` form or included in the ``JSON`` format. + +Get Cert Detail +~~~~~~~~~~~~~~~ + +.. code-block:: text + + GET /api/v1/controller/cert/{id}/ + +Change Details of Cert +~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + PUT /api/v1/controller/cert/{id}/ + +Patch Details of Cert +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: text + + PATCH /api/v1/controller/cert/{id}/ + +Delete Cert +~~~~~~~~~~~ + +.. code-block:: text + + DELETE /api/v1/controller/cert/{id}/ + +Renew Cert +~~~~~~~~~~ + +.. code-block:: text + + POST /api/v1/controller/cert/{id}/renew/ + +Revoke Cert +~~~~~~~~~~~ + +.. code-block:: text + + POST /api/v1/controller/cert/{id}/revoke/ diff --git a/docs/user/settings.rst b/docs/user/settings.rst new file mode 100644 index 000000000..47698239e --- /dev/null +++ b/docs/user/settings.rst @@ -0,0 +1,733 @@ +Settings +======== + +.. include:: /partials/settings-note.rst + +``OPENWISP_SSH_AUTH_TIMEOUT`` +----------------------------- + +============ =========== +**type**: ``int`` +**default**: ``2`` +**unit**: ``seconds`` +============ =========== + +Configure timeout to wait for an authentication response when establishing +a SSH connection. + +``OPENWISP_SSH_BANNER_TIMEOUT`` +------------------------------- + +============ =========== +**type**: ``int`` +**default**: ``60`` +**unit**: ``seconds`` +============ =========== + +Configure timeout to wait for the banner to be presented when establishing +a SSH connection. + +``OPENWISP_SSH_COMMAND_TIMEOUT`` +-------------------------------- + +============ =========== +**type**: ``int`` +**default**: ``30`` +**unit**: ``seconds`` +============ =========== + +Configure timeout on blocking read/write operations when executing a +command in a SSH connection. + +``OPENWISP_SSH_CONNECTION_TIMEOUT`` +----------------------------------- + +============ =========== +**type**: ``int`` +**default**: ``5`` +**unit**: ``seconds`` +============ =========== + +Configure timeout for the TCP connect when establishing a SSH connection. + +``OPENWISP_CONNECTORS`` +----------------------- + +============ ================================================================================= +**type**: ``tuple`` +**default**: .. code-block:: python + + ( + ("openwisp_controller.connection.connectors.ssh.Ssh", "SSH"), + ( + "openwisp_controller.connection.connectors.openwrt.snmp.OpenWRTSnmp", + "OpenWRT SNMP", + ), + ( + "openwisp_controller.connection.connectors.airos.snmp.AirOsSnmp", + "Ubiquiti AirOS SNMP", + ), + ) +============ ================================================================================= + +Available connector classes. Connectors are python classes that specify +ways in which OpenWISP can connect to devices in order to launch commands. + +``OPENWISP_UPDATE_STRATEGIES`` +------------------------------ + +============ ============================================================================ +**type**: ``tuple`` +**default**: .. code-block:: python + + ( + ( + "openwisp_controller.connection.connectors.openwrt.ssh.OpenWrt", + "OpenWRT SSH", + ), + ) +============ ============================================================================ + +Available update strategies. An update strategy is a subclass of a +connector class which defines an ``update_config`` method which is in +charge of updating the configuration of the device. + +This operation is launched in a background worker when the configuration +of a device is changed. + +It's possible to write custom update strategies and add them to this +setting to make them available in OpenWISP. + +``OPENWISP_CONFIG_UPDATE_MAPPING`` +---------------------------------- + +============ ================================================================== +**type**: ``dict`` +**default**: .. code-block:: python + + { + "netjsonconfig.OpenWrt": OPENWISP_UPDATE_STRATEGIES[0][0], + } +============ ================================================================== + +A dictionary that maps configuration backends to update strategies in +order to automatically determine the update strategy of a device +connection if the update strategy field is left blank by the user. + +.. _openwisp_controller_backends: + +``OPENWISP_CONTROLLER_BACKENDS`` +-------------------------------- + +============ =============================================== +**type**: ``tuple`` +**default**: .. code-block:: python + + ( + ("netjsonconfig.OpenWrt", "OpenWRT"), + ("netjsonconfig.OpenWisp", "OpenWISP"), + ) +============ =============================================== + +Available configuration backends. For more information, see `netjsonconfig +backends +`_. + +``OPENWISP_CONTROLLER_VPN_BACKENDS`` +------------------------------------ + +============ ==================================================================== +**type**: ``tuple`` +**default**: .. code-block:: python + + ( + ("openwisp_controller.vpn_backends.OpenVpn", "OpenVPN"), + ("openwisp_controller.vpn_backends.Wireguard", "WireGuard"), + ( + "openwisp_controller.vpn_backends.VxlanWireguard", + "VXLAN over WireGuard", + ), + ("openwisp_controller.vpn_backends.ZeroTier", "ZeroTier"), + ) +============ ==================================================================== + +Available VPN backends for VPN Server objects. For more information, see +`netjsonconfig VPN backends +`_. + +A VPN backend must follow some basic rules in order to be compatible with +*openwisp-controller*: + +- it MUST allow at minimum and at maximum one VPN instance +- the main *NetJSON* property MUST match the lowercase version of the + class name, eg: when using the ``OpenVpn`` backend, the system will look + into ``config['openvpn']`` +- it SHOULD focus on the server capabilities of the VPN software being + used + +.. _openwisp_controller_default_backend: + +``OPENWISP_CONTROLLER_DEFAULT_BACKEND`` +--------------------------------------- + +============ ====================================== +**type**: ``str`` +**default**: ``OPENWISP_CONTROLLER_BACKENDS[0][0]`` +============ ====================================== + +The preferred backend that will be used as initial value when adding new +``Config`` or ``Template`` objects in the admin. + +This setting defaults to the raw value of the first item in the +``OPENWISP_CONTROLLER_BACKENDS`` setting, which is +``netjsonconfig.OpenWrt``. + +Setting it to ``None`` will force the user to choose explicitly. + +``OPENWISP_CONTROLLER_DEFAULT_VPN_BACKEND`` +------------------------------------------- + +============ ========================================== +**type**: ``str`` +**default**: ``OPENWISP_CONTROLLER_VPN_BACKENDS[0][0]`` +============ ========================================== + +The preferred backend that will be used as initial value when adding new +``Vpn`` objects in the admin. + +This setting defaults to the raw value of the first item in the +``OPENWISP_CONTROLLER_VPN_BACKENDS`` setting, which is +``openwisp_controller.vpn_backends.OpenVpn``. + +Setting it to ``None`` will force the user to choose explicitly. + +``OPENWISP_CONTROLLER_REGISTRATION_ENABLED`` +-------------------------------------------- + +============ ======== +**type**: ``bool`` +**default**: ``True`` +============ ======== + +Whether devices can automatically register through the controller or not. + +This feature is enabled by default. + +Auto-registration must be supported on the devices in order to work, see +:doc:`openwisp-config automatic registration +` for more information. + +.. _openwisp_controller_consistent_registration: + +``OPENWISP_CONTROLLER_CONSISTENT_REGISTRATION`` +----------------------------------------------- + +============ ======== +**type**: ``bool`` +**default**: ``True`` +============ ======== + +Whether devices that are already registered are recognized when reflashed +or reset, hence keeping the existing configuration without creating a new +one. + +This feature is enabled by default. + +Auto-registration must be enabled also on the devices in order to work, +see :ref:`openwisp-config consistent key generation +` for more information. + +``OPENWISP_CONTROLLER_REGISTRATION_SELF_CREATION`` +-------------------------------------------------- + +============ ======== +**type**: ``bool`` +**default**: ``True`` +============ ======== + +Whether devices that are not already present in the system are allowed to +register or not. + +Turn this off if you still want to use auto-registration to avoid having +to manually set the device UUID and key in its configuration file but also +want to avoid indiscriminate registration of new devices without explicit +permission. + +.. _context_setting: + +``OPENWISP_CONTROLLER_CONTEXT`` +------------------------------- + +============ ======== +**type**: ``dict`` +**default**: ``{}`` +============ ======== + +Additional context that is passed to the default context of each device +object. + +``OPENWISP_CONTROLLER_CONTEXT`` can be used to define system-wide +configuration variables. + +For more information regarding how to use configuration variables in +OpenWISP, refer to :doc:`variables`. + +For technical information about how variables are handled in the lower +levels of OpenWISP, see `netjsonconfig context: configuration variables +`_. + +``OPENWISP_CONTROLLER_DEFAULT_AUTO_CERT`` +----------------------------------------- + +============ ======== +**type**: ``bool`` +**default**: ``True`` +============ ======== + +The default value of the ``auto_cert`` field for new ``Template`` objects. + +The ``auto_cert`` field is valid only for templates which have ``type`` +set to ``VPN`` and indicates whether configuration regarding the VPN +tunnel is provisioned automatically to each device using the template, eg: + +- when using OpenVPN, new `x509 `_ + certificates will be generated automatically using the same CA assigned + to the related VPN object +- when using WireGuard, new pair of private and public keys (using + `Curve25519 `_) will be generated, as well as + an IP address of the subnet assigned to the related VPN object +- when using `VXLAN `_ tunnels over + Wireguad, in addition to the configuration generated for WireGuard, a + new VID will be generated automatically for each device if the + configuration option "auto VNI" is turned on in the VPN object + +All these auto generated configuration options will be available as +template variables. + +The objects that are automatically created will also be removed when they +are not needed anymore (eg: when the VPN template is removed from a +configuration object). + +``OPENWISP_CONTROLLER_CERT_PATH`` +--------------------------------- + +============ ============= +**type**: ``str`` +**default**: ``/etc/x509`` +============ ============= + +The filesystem path where x509 certificate will be installed when +downloaded on routers when ``auto_cert`` is being used (enabled by +default). + +``OPENWISP_CONTROLLER_COMMON_NAME_FORMAT`` +------------------------------------------ + +============ ======================== +**type**: ``str`` +**default**: ``{mac_address}-{name}`` +============ ======================== + +Defines the format of the ``common_name`` attribute of VPN client +certificates that are automatically created when using VPN templates which +have ``auto_cert`` set to ``True``. A unique slug generated using +`shortuuid `_ is appended to +the common name to introduce uniqueness. Therefore, resulting common names +will have ``{OPENWISP_CONTROLLER_COMMON_NAME_FORMAT}-{unique-slug}`` +format. + +**Note:** If the ``name`` and ``mac address`` of the device are equal, the +``name`` of the device will be omitted from the common name to avoid +redundancy. + +``OPENWISP_CONTROLLER_MANAGEMENT_IP_DEVICE_LIST`` +------------------------------------------------- + +============ ======== +**type**: ``bool`` +**default**: ``True`` +============ ======== + +In the device list page, the column ``IP`` will show the ``management_ip`` +if available, defaulting to ``last_ip`` otherwise. + +If this setting is set to ``False`` the ``management_ip`` won't be shown +in the device list page even if present, it will be shown only in the +device detail page. + +You may set this to ``False`` if for some reason the majority of your user +doesn't care about the management ip address. + +``OPENWISP_CONTROLLER_CONFIG_BACKEND_FIELD_SHOWN`` +-------------------------------------------------- + +============ ======== +**type**: ``bool`` +**default**: ``True`` +============ ======== + +This setting toggles the ``backend`` fields in add/edit pages in Device +and Template configuration, as well as the ``backend`` field/filter in +Device list and Template list. + +If this setting is set to ``False`` these items will be removed from the +UI. + +Note: This setting affects only the configuration backend and NOT the VPN +backend. + +``OPENWISP_CONTROLLER_DEVICE_NAME_UNIQUE`` +------------------------------------------ + +============ ======== +**type**: ``bool`` +**default**: ``True`` +============ ======== + +This setting conditionally enforces unique Device names in an +Organization. The query to enforce this is case-insensitive. + +Note: For this constraint to be optional, it is enforced on an application +level and not on database. + +.. _openwisp_controller_hardware_id_enabled: + +``OPENWISP_CONTROLLER_HARDWARE_ID_ENABLED`` +------------------------------------------- + +============ ========= +**type**: ``bool`` +**default**: ``False`` +============ ========= + +The field ``hardware_id`` can be used to store a unique hardware id, for +example a serial number. + +If this setting is set to ``True`` then this field will be shown first in +the device list page and in the add/edit device page. + +This feature is disabled by default. + +``OPENWISP_CONTROLLER_HARDWARE_ID_OPTIONS`` +------------------------------------------- + +============ ============================================================= +**type**: ``dict`` +**default**: .. code-block:: python + + { + "blank": not OPENWISP_CONTROLLER_HARDWARE_ID_ENABLED, + "null": True, + "max_length": 32, + "unique": True, + "verbose_name": _("Serial number"), + "help_text": _("Serial number of this device"), + } +============ ============================================================= + +Options for the model field ``hardware_id``. + +- ``blank``: wether the field is allowed to be blank +- ``null``: wether an empty value will be stored as ``NULL`` in the + database +- ``max_length``: maximum length of the field +- ``unique``: wether the value of the field must be unique +- ``verbose_name``: text for the human readable label of the field +- ``help_text``: help text to be displayed with the field + +``OPENWISP_CONTROLLER_HARDWARE_ID_AS_NAME`` +------------------------------------------- + +============ ======== +**type**: ``bool`` +**default**: ``True`` +============ ======== + +When the hardware ID feature is enabled, devices will be referenced with +their hardware ID instead of their name. + +If you still want to reference devices by their name, set this to +``False``. + +``OPENWISP_CONTROLLER_DEVICE_VERBOSE_NAME`` +------------------------------------------- + +============ ========================= +**type**: ``tuple`` +**default**: ``('Device', 'Devices')`` +============ ========================= + +Defines the ``verbose_name`` attribute of the ``Device`` model, which is +displayed in the admin site. The first and second element of the tuple +represent the singular and plural forms. + +For example, if we want to change the verbose name to "Hotspot", we could +write: + +.. code-block:: python + + OPENWISP_CONTROLLER_DEVICE_VERBOSE_NAME = ("Hotspot", "Hotspots") + +``OPENWISP_CONTROLLER_HIDE_AUTOMATICALLY_GENERATED_SUBNETS_AND_IPS`` +-------------------------------------------------------------------- + +============ ========= +**type**: ``bool`` +**default**: ``False`` +============ ========= + +Setting this to ``True`` will hide subnets and IP addresses generated by +:doc:`subnet division rules ` from being displayed +in the list of Subnets and IP addresses in the admin dashboard. + +.. _openwisp_controller_subnet_division_types: + +``OPENWISP_CONTROLLER_SUBNET_DIVISION_TYPES`` +--------------------------------------------- + +============ ================================================================================================= +**type**: ``tuple`` +**default**: .. code-block:: python + + ( + ( + "openwisp_controller.subnet_division.rule_types.device.DeviceSubnetDivisionRuleType", + "Device", + ), + ( + "openwisp_controller.subnet_division.rule_types.vpn.VpnSubnetDivisionRuleType", + "VPN", + ), + ) +============ ================================================================================================= + +Available types for :doc:`Subject Division Rule ` +objects. + +For more information on how to write your own types, please refer to: +:ref:`custom_subnet_division_rule_types`. + +``OPENWISP_CONTROLLER_API`` +--------------------------- + +============ ======== +**type**: ``bool`` +**default**: ``True`` +============ ======== + +Indicates whether the API for Openwisp Controller is enabled or not. To +disable the API by default add ``OPENWISP_CONTROLLER_API = False`` in your +project ``settings.py`` file. + +``OPENWISP_CONTROLLER_API_HOST`` +-------------------------------- + +============ ======== +**type**: ``str`` +**default**: ``None`` +============ ======== + +Allows to specify backend URL for API requests, if the frontend is hosted +separately. + +.. _openwisp_controller_user_commands: + +``OPENWISP_CONTROLLER_USER_COMMANDS`` +------------------------------------- + +============ ======== +**type**: ``list`` +**default**: ``[]`` +============ ======== + +Allows to specify a ``list`` of tuples for adding commands as described in +the section: :ref:`defining_new_menu_options`. + +``OPENWISP_CONTROLLER_ORGANIZATION_ENABLED_COMMANDS`` +----------------------------------------------------- + +============ ============================================= +**type**: ``dict`` +**default**: .. code-block:: python + + { + # By default all commands are allowed + "__all__": "*", + } +============ ============================================= + +This setting controls the command types that are enabled on the system By +default, all command types are enabled to all the organizations, but it's +possible to disable a specific command for a specific organization as +shown in the following example: + +.. code-block:: python + + OPENWISP_CONTROLLER_ORGANIZATION_ENABLED_COMMANDS = { + "__all__": "*", + # Organization UUID: # Tuple of enabled commands + "7448a190-6e65-42bf-b8ea-bb6603e593a5": ("reboot", "change_password"), + } + +In the example above, the organization with UUID +``7448a190-6e65-42bf-b8ea-bb6603e593a5`` will allow to send only commands +of type ``reboot`` and ``change_password``, while all the other +organizations will have all command types enabled. + +.. _openwisp_controller_device_group_schema: + +``OPENWISP_CONTROLLER_DEVICE_GROUP_SCHEMA`` +------------------------------------------- + +============ ======================================== +**type**: ``dict`` +**default**: ``{'type': 'object', 'properties': {}}`` +============ ======================================== + +Allows specifying JSONSchema used for validating the meta-data of +:doc:`device-groups`. + +``OPENWISP_CONTROLLER_SHARED_MANAGEMENT_IP_ADDRESS_SPACE`` +---------------------------------------------------------- + +============ ======== +**type**: ``bool`` +**default**: ``True`` +============ ======== + +By default, the system assumes that the address space of the management +tunnel is shared among all the organizations using the system, that is, +the system assumes there's only one management VPN, tunnel or other +networking technology to reach the devices it controls. + +When set to ``True``, any device belonging to any organization will never +have the same ``management_ip`` as another device, the latest device +declaring the management IP will take the IP and any other device who +declared the same IP in the past will have the field reset to empty state +to avoid potential conflicts. + +Set this to ``False`` if every organization has its dedicated management +tunnel with a dedicated address space that is reachable by the OpenWISP +server. + +.. _openwisp_controller_management_ip_only: + +``OPENWISP_CONTROLLER_MANAGEMENT_IP_ONLY`` +------------------------------------------ + +============ ======== +**type**: ``bool`` +**default**: ``True`` +============ ======== + +By default, only the management IP will be used to establish connection +with the devices. + +If the devices are connecting to your OpenWISP instance using a shared +layer2 network, hence the OpenWSP server can reach the devices using the +``last_ip`` field, you can set this to ``False``. + +``OPENWISP_CONTROLLER_DSA_OS_MAPPING`` +-------------------------------------- + +============ ======== +**type**: ``dict`` +**default**: ``{}`` +============ ======== + +OpenWISP Controller can figure out whether it should use the new OpenWrt +syntax for DSA interfaces (Distributed Switch Architecture) introduced in +OpenWrt 21 by reading the ``os`` field of the ``Device`` object. However, +if the firmware you are using has a custom firmware identifier, the system +will not be able to figure out whether it should use the new syntax and it +will default to :ref:`OPENWISP_CONTROLLER_DSA_DEFAULT_FALLBACK +`. + +If you want to make sure the system can parse your custom firmware +identifier properly, you can follow the example below. + +For the sake of the example, the OS identifier ``MyCustomFirmware 2.0`` +corresponds to ``OpenWrt 19.07``, while ``MyCustomFirmware 2.1`` +corresponds to ``OpenWrt 21.02``. Configuring this setting as indicated +below will allow OpenWISP to supply the right syntax automatically. + +Example: + +.. code-block:: python + + OPENWISP_CONTROLLER_DSA_OS_MAPPING = { + "netjsonconfig.OpenWrt": { + # OpenWrt >=21.02 configuration syntax will be used for + # these OS identifiers. + ">=21.02": [r"MyCustomFirmware 2.1(.*)"], + # OpenWrt <=21.02 configuration syntax will be used for + # these OS identifiers. + "<21.02": [r"MyCustomFirmware 2.0(.*)"], + } + } + +**Note**: The OS identifier should be a regular expression as shown in +above example. + +.. _openwisp_controller_dsa_default_fallback: + +``OPENWISP_CONTROLLER_DSA_DEFAULT_FALLBACK`` +-------------------------------------------- + +============ ======== +**type**: ``bool`` +**default**: ``True`` +============ ======== + +The value of this setting decides whether to use DSA syntax (OpenWrt >=21 +configuration syntax) if openwisp-controller fails to make that decision +automatically. + +``OPENWISP_CONTROLLER_GROUP_PIE_CHART`` +--------------------------------------- + +============ ========= +**type**: ``bool`` +**default**: ``False`` +============ ========= + +Allows to show a pie chart like the one in the screenshot. + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/devicegroups-piechart.png + :alt: device groups piechart + +Active groups are groups which have at least one device in them, while +emtpy groups do not have any device assigned. + +``OPENWISP_CONTROLLER_API_TASK_RETRY_OPTIONS`` +---------------------------------------------- + +============ ========= +**type**: ``dict`` +**default**: see below +============ ========= + +.. code-block:: python + + # default value of OPENWISP_CONTROLLER_API_TASK_RETRY_OPTIONS: + + dict( + max_retries=5, # total number of retries + retry_backoff=True, # exponential backoff + retry_backoff_max=600, # 10 minutes + retry_jitter=True, # randomness into exponential backoff + ) + +This setting is utilized by background API tasks executed by +:doc:`ZeroTier VPN servers and ZeroTier VPN clients ` to handle +recoverable HTTP status codes such as 429, 500, 502, 503, and 504. + +These tasks are retried with a maximum of 5 attempts with an exponential +backoff and jitter, with a maximum delay of 10 minutes. + +This feature ensures that ZeroTier Service API calls are resilient to +recoverable failures, improving the reliability of the system. + +For more information on these settings, you can refer to the `the celery +documentation regarding automatic retries for known errors. +`_ diff --git a/docs/user/shell-commands.rst b/docs/user/shell-commands.rst new file mode 100644 index 000000000..6d5165939 --- /dev/null +++ b/docs/user/shell-commands.rst @@ -0,0 +1,177 @@ +Sending Commands to Devices +=========================== + +.. contents:: **Table of Contents**: + :depth: 3 + :local: + +Default Commands +---------------- + +By default, there are three options in the **Send Command** dropdown: + +1. Reboot +2. Change Password +3. Custom Command + +While the first two options are self-explanatory, the **custom command** +option allows you to execute any command on the device as shown in the +example below. + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/commands_demo.gif + :target: https://github.com/openwisp/openwisp-controller/tree/docs/docs/commands_demo.gif + :alt: Executing commands on device example + +**Note**: in order for this feature to work, a device needs to have at +least one **Access Credential** (see :doc:`How to configure push updates +`). + +The **Send Command** button will be hidden until the device has at least +one **Access Credential**. + +If you need to allow your users to quickly send specific commands that are +used often in your network regardless of your users' knowledge of Linux +shell commands, you can add new commands by following instructions in the +:ref:`defining_new_menu_options` section below. + +.. note:: + + If you're an advanced user and want to learn how to register commands + programmatically, refer to the + :ref:`registering_unregistering_commands` section. + +.. _defining_new_menu_options: + +Defining New Options in the Commands Menu +----------------------------------------- + +Let's explore to define new custom commands to help users perform +additional management actions without having to be Linux/Unix experts. + +We can do so by using the ``OPENWISP_CONTROLLER_USER_COMMANDS`` django +setting. + +The following example defines a simple command that can ``ping`` an input +``destination_address`` through a network interface, ``interface_name``. + +.. code-block:: python + + # In yourproject/settings.py + + + def ping_command_callable(destination_address, interface_name=None): + command = f"ping -c 4 {destination_address}" + if interface_name: + command += f" -I {interface_name}" + return command + + + OPENWISP_CONTROLLER_USER_COMMANDS = [ + ( + "ping", + { + "label": "Ping", + "schema": { + "title": "Ping", + "type": "object", + "required": ["destination_address"], + "properties": { + "destination_address": { + "type": "string", + "title": "Destination Address", + }, + "interface_name": { + "type": "string", + "title": "Interface Name", + }, + }, + "message": "Destination Address cannot be empty", + "additionalProperties": False, + }, + "callable": ping_command_callable, + }, + ) + ] + +The above code will add the *Ping* command in the user interface as show +in the GIF below: + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/ping_command_example.gif + :target: https://github.com/openwisp/openwisp-controller/tree/docs/docs/ping_command_example.gif + :alt: Adding a *ping* command + +The ``OPENWISP_CONTROLLER_USER_COMMANDS`` setting takes a ``list`` of +``tuple`` each containing two elements. The first element of the tuple +should contain an identifier for the command and the second element should +contain a ``dict`` defining configuration of the command. + +.. _comand_configuration: + +Command Configuration +~~~~~~~~~~~~~~~~~~~~~ + +The ``dict`` defining configuration for command should contain following +keys: + +1. ``label`` +++++++++++++ + +A ``str`` defining label for the command used internally by Django. + +2. ``schema`` ++++++++++++++ + +A ``dict`` defining `JSONSchema `_ for inputs of +command. You can specify the inputs for your command, add rules for +performing validation and make inputs required or optional. + +Here is a detailed explanation of the schema used in above example: + +.. code-block:: python + + { + # Name of the command displayed in *Send Command* widget + "title": "Ping", + # Use type *object* if the command needs to accept inputs + # Use type *null* if the command does not accepts any input + "type": "object", + # Specify list of inputs that are required + "required": ["destination_address"], + # Define the inputs for the commands along with their properties + "properties": { + "destination_address": { + # type of the input value + "type": "string", + # label used for displaying this input field + "title": "Destination Address", + }, + "interface_name": { + "type": "string", + "title": "Interface Name", + }, + }, + # Error message to be shown if validation fails + "message": "Destination Address cannot be empty", + # Whether specifying addtionaly inputs is allowed from the input form + "additionalProperties": False, + } + +This example uses only handful of properties available in JSONSchema. You +can experiment with other properties of JSONSchema for schema of your +command. + +3. ``callable`` ++++++++++++++++ + +A ``callable`` or ``str`` defining dotted path to a callable. It should +return the command (``str``) to be executed on the device. Inputs of the +command are passed as arguments to this callable. + +The example above includes a callable(``ping_command_callable``) for +``ping`` command. + +How to register or unregister commands +-------------------------------------- + +Refer to :ref:`registering_unregistering_commands` in the developer +documentation. diff --git a/docs/user/subnet-division-rules.rst b/docs/user/subnet-division-rules.rst new file mode 100644 index 000000000..d9e8deef6 --- /dev/null +++ b/docs/user/subnet-division-rules.rst @@ -0,0 +1,188 @@ +Automating Subnet and IP Address Provisioning +============================================= + +This guide helps you automate provisioning subnets and IP addresses for +your network devices. + +.. contents:: **Table of Contents**: + :depth: 2 + :local: + +.. _step1_rule: + +1. Create a Subnet and a Subnet Division Rule +--------------------------------------------- + +Create a master subnet. + +This is the parent subnet from which automatically generated subnets will +be provisioned. + +.. note:: + + Choose a subnet size appropriate for the needs of your network. + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/subnet-division-rule/subnet.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/subnet-division-rule/subnet.png + :alt: Creating a master subnet example + +On the same page, add a **subnet division rule**. This rule defines the +criteria for automatically provisioning subnets under the master subnet. + +The type of subnet division rule determines when subnets and IP addresses +are assigned to devices. + +The currently supported rule types are described below. + +.. note:: + + For information on how to write your own subnet division rule types, + please refer to: :ref:`custom_subnet_division_rule_types`. + +.. _device_rule: + +Device Subnet Division Rule +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This rule triggers when a device configuration (``config.Config`` model) +is created for the organization specified in the rule. + +.. note:: + + If a device object is created without any related configuration + object, it will not trigger this rule. + +Creating a new *"Device"* rule will also automatically provision subnets +and IP addresses for existing devices within the organization. + +.. _vpn_rule: + +VPN Subnet Division Rule +~~~~~~~~~~~~~~~~~~~~~~~~ + +This rule triggers when a template flagged as *VPN-client* is assigned to +a device configuration, but only if the VPN server associated with the +VPN-client template uses the same subnet to which the subnet division rule +is assignated to. + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/subnet-division-rule/subnet-division-rule.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/subnet-division-rule/subnet-division-rule.png + :alt: Creating a subnet division rule example + +In this example, **VPN subnet division rule** is used. + +2. Create a VPN Server +---------------------- + +Now create a VPN Server and choose the previously created **master +subnet** as the subnet for this VPN Server. + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/subnet-division-rule/vpn-server.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/subnet-division-rule/vpn-server.png + :alt: Creating a VPN Server example + +3. Create a VPN Client Template +------------------------------- + +Create a template, setting the **Type** field to **VPN Client** and +**VPN** field to use the previously created VPN Server. + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/subnet-division-rule/vpn-client.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/subnet-division-rule/vpn-client.png + :alt: Creating a VPN Client template example + +**Note**: You can also check the **Enable by default** field if you want +to automatically apply this template to devices that will register in +future. + +4. Apply VPN Client Template to Devices +--------------------------------------- + +With everything in place, you can now apply the VPN Client Template to +devices. + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/subnet-division-rule/apply-template-to-device.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/subnet-division-rule/apply-template-to-device.png + :alt: Adding template to device example + +After saving the device, you should see all provisioned Subnets and IPs +for this device under :ref:`System Defined Variables +`. + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/subnet-division-rule/system-defined-variables.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/subnet-division-rule/system-defined-variables.png + :alt: Provisioned Subnets and IPs available as System Defined Variables example + +You can now use these :doc:`variables` in the configuration of devices of +your network. + +Important notes for using Subnet Division +----------------------------------------- + +- In the example provided, the Subnet, VPN Server, and VPN Client Template + were associated with the **default** organization. You can also utilize + **Systemwide Shared** Subnet, VPN Server, or VPN Client Template; + however, remember that the Subnet Division Rule will always be linked to + an organization. It will only be triggered when a VPN Client Template is + applied to a Device with the same organization as the Subnet Division + Rule. +- Configuration variables can be used for provisioned subnets and IPs in + the Template. Each variable will resolve differently for different + devices. For example, ``OW_subnet1_ip1`` will resolve to ``10.0.0.1`` + for one device and ``10.0.0.55`` for another. Every device receives its + own set of subnets and IPs. Ensure to provide default fallback values in + the *default values* template field (mainly used for validation). +- The Subnet Division Rule automatically creates a reserved subnet, which + can be utilized to provision any IP addresses that need to be created + manually. The remaining address space of the master subnet must not be + interfered with, or the automation implemented in this module will not + function. +- The example provided used the :ref:`VPN subnet division rule + `. Similarly, the :ref:`device subnet division rule + ` can be employed, requiring only :ref:`the creation of a + subnet and a subnet division rule `. + +Limitations of Subnet Division Rules +------------------------------------ + +In the current implementation, it is not possible to change *Size*, +*Number of Subnets* and *Number of IPs* fields of an existing subnet +division rule due to following reasons: + +Size +~~~~ + +Allowing to change size of provisioned subnets of an existing subnet +division rule will require rebuilding of Subnets and IP addresses which +has possibility of breaking existing configurations. + +Number of Subnets +~~~~~~~~~~~~~~~~~ + +Allowing to decrease number of subnets of an existing subnet division rule +can create patches of unused subnets dispersed everywhere in the master +subnet. Allowing to increase number of subnets will break the continuous +allocation of subnets for every device. It can also break configuration of +devices. + +Number of IPs +~~~~~~~~~~~~~ + +**Decreasing the number of IPs** in an existing subnet division rule is +not allowed as it can lead to deletion of IP addresses, potentially +breaking configurations of existing devices. + +**Increasing the number of IPs is allowed**. + +If you need to modify any of these fields (**Size**, **Number of +Subnets**, or **Number of IPs**), we recommend to proceed as follows: + +1. Delete the existing rule. +2. Create a new rule. + +The automation will provision new subnets and addresses according to the +new parameters to any existing devices that are eligible to the subnet +division rule. + +However, be aware that existing devices **will probably be reassigned +different subnets and IP addresses** than the ones used previously. diff --git a/docs/user/templates.rst b/docs/user/templates.rst new file mode 100644 index 000000000..375e708ff --- /dev/null +++ b/docs/user/templates.rst @@ -0,0 +1,212 @@ +Configuration Templates +======================= + +.. contents:: **Table of Contents**: + :depth: 3 + :local: + +What is a Template? +------------------- + +Templates are designed to store configuration that can be reused by some +or all the devices in the system. + +Updating the configuration stored in a template allows to update the +configuration of all the devices that have that template assigned. + +This means that configuration can be defined only once for multiple +devices, and if the need to update a specific piece of configuration +arises, it can be easily achieved by updating the template. + +Template Ordering and Override +------------------------------ + +A device can use multiple templates, **the order in which templates are +assigned to each device matters**: templates assigned last can override +templates assigned earlier, the order can be changed by drag and dropping +the template in the device configuration page as in the animated +screenshot below. + +.. image:: /images/templates/template-ordering.gif + :target: ../../_images/template-ordering.gif + :align: center + :alt: Template ordering: drag and drop to change order + +The device configuration can also override what is defined in templates. + +Overriding means redefining a specific configuration section in a way that +overwrites the template. + +**Overriding involves some form of duplication of information, which is +not great, it should be used as a last resort**. The recommended way to +define parts of the configuration that are specific for each device is to +use :doc:`Configuration variables <./variables>`. + +.. _controller_shared_vs_org: + +Shared Templates vs Organization Specific +----------------------------------------- + +Templates can be *organization specific* or *shared* (no organization +specified). + +.. image:: /images/templates/organization-specific-vs-shared.gif + :target: ../../_images/organization-specific-vs-shared.gif + :align: center + :alt: Shared templates vs organization specific + +**Organization specific templates** will be available and usable only +within the same organization which they are assigned to. + +If no organization is specified when creating a template, a shared +template will be created, **shared templates are available to any +organization in the system**. + +Here are a few typical use cases of shared templates: + +- Management VPN +- Authorized SSH keys belonging to network administrators +- Crontab with generic periodic management operations + +.. _default_templates: + +Default Templates +----------------- + +.. image:: /images/templates/default-templates.gif + :target: ../../_images/default-templates.gif + :align: center + :alt: Templates enabled by default + +When templates are flagged as **"Enabled by default"**, they will be +automatically assigned to new devices. + +This is a very powerful feature: **once default templates are correctly +configured to implement the use case you need, you will only have to +register a device into OpenWISP for it to auto-configure itself**. + +Moreover, you can change the default templates any time you need, which is +the reason this feature has replaced the practice of storing default +configuration in firmware images (which would need to be recompiled and +redistributed): with default templates, the default firmware image only +needs to contain the bare minimum configuration to connect to OpenWISP, +once the device connects to OpenWISP it will download and apply the +default templates without the need of manual intervention from the network +operators. + +An organization specific template flagged as default will be automatically +assigned to any new device which will be created in the same organization. + +A shared default template instead will be automatically assigned to all +the new devices which will be created in the system, regardless of +organization. + +.. _required_templates: + +Required Templates +------------------ + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/required-templates.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/required-templates.png + :alt: Required template example + +Required templates are similar to :ref:`default_templates` but cannot be +unassigned from a device configuration, they can only be overridden. + +They will be always assigned earlier than default templates, so they can +be overridden if needed. + +In the example above, the "SSID" template is flagged as "(required)" and +its checkbox is always checked and disabled. + +Device Group Templates +---------------------- + +:ref:`default_templates` are an incredibly useful tool, but they're +limited: **only one set of default templates can be created** per each +organization. + +With :ref:`device_group_templates` it is possible to specify a set of +default templates for each device group. + +.. _templates_tags: + +Template Tags +------------- + +.. image:: /images/templates/template-tags.gif + :target: ../../_images/template-tags.gif + :align: center + :alt: Template tags + +In some cases, you may have multiple set of default settings to use, let's +explain this with a practical example: you may have 2 different device +types in your network: + +- Mesh routers: they connect to one another, forming a wireless mesh + network +- Dumb access points: they connect to the mesh routers on the LAN port and + offer internet access which is routed via the mesh network by the + routers + +In this example case, the default configuration to use in each device type +can greatly differ. + +In such a setup, default templates would only contain configuration which +is common to both device types, while configuration which is specific for +each type would be stored in specific templates which are then tagged with +specific keywords: + +- ``mesh``: tag to use for mesh configuration +- ``dumb-ap``: tag to use for dumb AP configuration + +The :ref:`openwisp-config ` +configuration of each device type must specify the correct tag before each +device registers in the system. + +Here's the sample ``/etc/config/openwisp`` configuration for mesh devices: + +.. code-block:: + + config controller 'http' + option url 'https://openwisp2.mynetwork.com' + option shared_secret 'mySharedSecret123' + option tags 'mesh' + +Once devices with the above configuration will register into the system, +any template tagged as ``mesh`` (as in the screenshot below) will be +assigned to them. + +.. image:: /images/templates/mesh-template-tag.png + :target: ../../_images/mesh-template-tag.png + :align: center + :alt: Template tags: mesh example + +The sample ``/etc/config/openwisp`` configuration for dumb access points +is the following: + +.. code-block:: + + config controller 'http' + option url 'https://openwisp2.mynetwork.com' + option shared_secret 'mySharedSecret123' + option tags 'dumb-ap' + +Once devices with the above configuration will register into the system, +any template tagged as ``dumb-ap`` (as in the screenshot below) will be +assigned to them. + +.. image:: /images/templates/dumb-ap-template-tag.png + :target: ../../_images/dumb-ap-template-tag.png + :align: center + :alt: Template tags: dumb AP example + +Implementation Details of Templates +----------------------------------- + +Templates are implemented under the hood by the OpenWISP configuration +engine: netjsonconfig. + +For more advanced technical information about templates, consult the +netjsonconfig documentation: `Basic Concepts, Template +`_. diff --git a/docs/user/variables.rst b/docs/user/variables.rst new file mode 100644 index 000000000..ce2043551 --- /dev/null +++ b/docs/user/variables.rst @@ -0,0 +1,171 @@ +Configuration Variables +======================= + +Sometimes the configuration is not exactly equal on all the devices, some +parameters are unique to each device or need to be changed by the user. + +In these cases it is possible to use configuration variables in +conjunction with templates, this feature is also known as *configuration +context*, think of it like a dictionary which is passed to the function +which renders the configuration, so that it can fill variables according +to the passed context. + +Different Types of Variables +---------------------------- + +The different ways in which variables are defined are described below in +the order (high to low) of their precedence. + +.. contents:: + :depth: 2 + :local: + +.. _user_defined_variables: + +1. User Defined Device Variables +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In the device configuration section you can find a section named +"Configuration variables" where it is possible to define the configuration +variables and their values, as shown in the example below: + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/device-context.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/device-context.png + :alt: context + +2. Predefined Device Variables +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Each device gets the following attributes passed as configuration +variables: + +- ``id`` +- ``key`` +- ``name`` +- ``mac_address`` + +3. Group Variables +~~~~~~~~~~~~~~~~~~ + +Variables can also be defined in :doc:`./device-groups`. + +Refer to :ref:`device_group_variables` for more information. + +4. Organization Variables +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Variables can also be defined at the organization level. + +You can set the *organization variables* from the organization change page +``/admin/openwisp_users/organization//change/``, under +the **Configuration Management Settings**. + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/organization-variables.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/organization-variables.png + :alt: organization variables + +5. Global Variables +~~~~~~~~~~~~~~~~~~~ + +Variables can also be defined globally using the :ref:`context_setting` +setting, see also :doc:`How to Edit Django Settings +<../../../../user/django-settings>`. + +6. Template Default Values +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It's possible to specify the default values of variables defined in a +template. + +This allows to achieve 2 goals: + +1. pass schema validation without errors (otherwise it would not be + possible to save the template in the first place) +2. provide good default values that are valid in most cases but can be + overridden in the device if needed + +These default values will be overridden by the :ref:`User defined device +variables `. + +The default values of variables can be manipulated from the section +"configuration variables" in the edit template page: + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/template-default-values.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/template-default-values.png + :alt: default values + +.. _system_defined_variables: + +7. System Defined Variables +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Predefined device variables, global variables and other variables that are +automatically managed by the system (eg: when using templates of type +VPN-client) are displayed in the admin UI as *System Defined Variables* in +read-only mode. + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/system-defined-variables.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/system-defined-variables.png + :alt: system defined variables + +Example Usage of Variables +-------------------------- + +Here's a typical use case, the WiFi SSID and WiFi password. You don't want +to define this for every device, but you may want to allow operators to +easily change the SSID or WiFi password for a specific device without +having to re-define the whole wifi interface to avoid duplicating +information. + +This would be the template: + +.. code-block:: json + + { + "interfaces": [ + { + "type": "wireless", + "name": "wlan0", + "wireless": { + "mode": "access_point", + "radio": "radio0", + "ssid": "{{wlan0_ssid}}", + "encryption": { + "protocol": "wpa2_personal", + "key": "{{wlan0_password}}", + "cipher": "auto" + } + } + } + ] + } + +These would be the default values in the template: + +.. code-block:: json + + { + "wlan0_ssid": "SnakeOil PublicWiFi", + "wlan0_password": "Snakeoil_pwd!321654" + } + +The default values can then be overridden at :ref:`device level +` if needed, eg: + +.. code-block:: json + + { + "wlan0_ssid": "Room 23 ACME Hotel", + "wlan0_password": "room_23pwd!321654" + } + +Implementation Details of Variables +----------------------------------- + +Variables are implemented under the hood by the OpenWISP configuration +engine: netjsonconfig. + +For more advanced technical information about variables, consult the +netjsonconfig documentation: `Basic Concepts, Context (configuration +variables) +`_. diff --git a/docs/user/vxlan-wireguard.rst b/docs/user/vxlan-wireguard.rst new file mode 100644 index 000000000..719813f38 --- /dev/null +++ b/docs/user/vxlan-wireguard.rst @@ -0,0 +1,125 @@ +Automating VXLAN over WireGuard Tunnels +======================================= + +By following these steps, you will be able to setup layer 2 VXLAN tunnels +encapsulated in `WireGuard `_ tunnels which +work on layer 3. + +.. include:: ../partials/shared-object.rst + +.. contents:: **Table of Contents**: + :depth: 2 + :local: + +1. Create VPN Server Configuration for VXLAN Over WireGuard +----------------------------------------------------------- + +1. Visit ``/admin/config/vpn/add/`` to add a new VPN server. +2. We will set **Name** of this VPN server ``Wireguard VXLAN`` and + **Host** as ``wireguard-vxlan-server.mydomain.com`` (update this to + point to your WireGuard VXLAN VPN server). +3. Select ``VXLAN over WireGuard`` from the dropdown as **VPN Backend**. +4. When using VXLAN over WireGuard, OpenWISP takes care of managing IP + addresses (assigning an IP address to each VPN peer). You can create a + new subnet or select an existing one from the dropdown menu. You can + also assign an **Internal IP** to the WireGuard Server or leave it + empty for OpenWISP to configure. This IP address will be used by the + WireGuard interface on server. +5. We have set the **Webhook Endpoint** as + ``https://wireguard-vxlan-server.mydomain.com:8081/trigger-update`` for + this example. You will need to update this according to you VPN + upgrader endpoint. Set **Webhook AuthToken** to any strong passphrase, + this will be used to ensure that configuration upgrades are requested + from trusted sources. + + **Note**: If you are following this tutorial for also setting up + WireGuard VPN server, just substitute ``wireguard-server.mydomain.com`` + with hostname of your VPN server and follow the steps in next section. + +6. Under the configuration section, set the name of WireGuard tunnel 1 + interface. We have used ``wg0`` in this example. + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-vxlan-tutorial/vpn-server-1.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-vxlan-tutorial/vpn-server-1.png + :alt: WireGuard VPN VXLAN server configuration example 1 + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-vxlan-tutorial/vpn-server-2.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-vxlan-tutorial/vpn-server-2.png + :alt: WireGuard VPN VXLAN server configuration example 2 + +7. After clicking on **Save and continue editing**, you will see that + OpenWISP has automatically created public and private key for WireGuard + server in **System Defined Variables** along with internal IP address + information. + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-vxlan-tutorial/vpn-server-3.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-vxlan-tutorial/vpn-server-3.png + :alt: WireGuard VXLAN VPN server configuration example 3 + +2. Deploy Wireguard VXLAN VPN Server +------------------------------------ + +If you haven't already set up WireGuard on your VPN server, this is a good +time to do so. We recommend using the `ansible-wireguard-openwisp +`_ role for +installing WireGuard since it also installs scripts that allow OpenWISP to +manage the WireGuard VPN server along with VXLAN tunnels. + +Pay attention to the VPN server attributes used in your playbook. It +should be the same as the VPN server configuration in OpenWISP. + +3. Create VPN Client Template for WireGuard VXLAN VPN Server +------------------------------------------------------------ + +1. Visit ``/admin/config/template/add/`` to add a new template. +2. Set ``Wireguard VXLAN Client`` as **Name** (you can set whatever you + want) and select ``VPN-client`` as **type** from the dropdown list. +3. The **Backend** field refers to the backend of the device this template + can be applied to. For this example, we will leave it as ``OpenWrt``. +4. Select the correct VPN server from the dropdown for the **VPN** field. + Here it is ``Wireguard VXLAN``. +5. Ensure that **Automatic tunnel provisioning** is checked. This will + make OpenWISP automatically generate public and private keys and + provision IP addresses for each WireGuard VPN client along with the + VXLAN Network Identifier (VNI). +6. After clicking on **Save and continue editing** button, you will see + details of the *Wireguard VXLAN* VPN server in **System Defined + Variables**. The template configuration will be automatically generated + which you can tweak accordingly. We will use the automatically + generated VPN client configuration for this example. + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-vxlan-tutorial/template.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-vxlan-tutorial/template.png + :alt: WireGuard VXLAN VPN client template example + +4. Apply Wireguard VXLAN VPN Template to Devices +------------------------------------------------ + +.. note:: + + This step assumes that you already have a device registered on + OpenWISP. Register or create a device before proceeding. + +1. Open the **Configuration** tab of the concerned device. +2. Select the *WireGuard VXLAN Client* template. +3. Upon clicking on **Save and continue editing** button, you will see + some entries in **System Defined Variables**. It will contain internal + IP address, private and public key for the WireGuard client on the + device and details of WireGuard VPN server along with VXLAN Network + Identifier(VNI) of this device. + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-vxlan-tutorial/device-configuration.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-vxlan-tutorial/device-configuration.png + :alt: WireGuard VXLAN VPN device configuration example + +**Voila!** You have successfully configured OpenWISP to manage VXLAN over +WireGuard tunnels for your devices. + +.. seealso:: + + You may also want to explore other automated VPN tunnel provisioning + options: + + - :doc:`Wireguard ` + - :doc:`Zerotier ` + - :doc:`OpenVPN ` diff --git a/docs/user/wireguard.rst b/docs/user/wireguard.rst new file mode 100644 index 000000000..5e99668d7 --- /dev/null +++ b/docs/user/wireguard.rst @@ -0,0 +1,123 @@ +Automating WireGuard Tunnels +============================ + +This guide will help you to set up the automatic provisioning of +`WireGuard `_ tunnels for your devices. + +.. include:: ../partials/shared-object.rst + +.. contents:: **Table of Contents**: + :depth: 2 + :local: + +1. Create VPN Server Configuration for WireGuard +------------------------------------------------ + +1. Visit ``/admin/config/vpn/add/`` to add a new VPN server. +2. Set the **Name** of this VPN server as ``WireGuard`` and the **Host** + as ``wireguard-server.mydomain.com`` (update this to point to your + WireGuard VPN server). +3. Select ``WireGuard`` from the dropdown as the **VPN Backend**. +4. When using WireGuard, OpenWISP takes care of managing IP addresses, + assigning an IP address to each VPN peer. Create a new subnet or select + an existing one from the dropdown menu. You can also assign an + **Internal IP** to the WireGuard Server or leave it empty for OpenWISP + to configure. This IP address will be used by the WireGuard interface + on the server. +5. Set the **Webhook Endpoint** as + ``https://wireguard-server.mydomain.com:8081/trigger-update`` for this + example. Update this according to your VPN upgrader endpoint. Set + **Webhook AuthToken** to any strong passphrase; this will be used to + ensure that configuration upgrades are requested from trusted sources. + + **Note**: If you are setting up a WireGuard VPN server, substitute + ``wireguard-server.mydomain.com`` with the hostname of your VPN server + and follow the steps in the next section. + +6. Under the configuration section, set the name of the WireGuard tunnel 1 + interface. In this example, we have used ``wg0``. + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-tutorial/vpn-server-1.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-tutorial/vpn-server-1.png + :alt: WireGuard VPN server configuration example 1 + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-tutorial/vpn-server-2.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-tutorial/vpn-server-2.png + :alt: WireGuard VPN server configuration example 2 + +7. After clicking on **Save and continue editing**, you will see that + OpenWISP has automatically created public and private keys for the + WireGuard server in **System Defined Variables**, along with internal + IP address information. + +.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-tutorial/vpn-server-3.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-tutorial/vpn-server-3.png + :alt: WireGuard VPN server configuration example 3 + +2. Deploy WireGuard VPN Server +------------------------------ + +If you haven't already set up WireGuard on your VPN server, this would be +a good time to do so. + +We recommend using the `ansible-wireguard-openwisp +`_ role for +installing WireGuard, as it also installs scripts that allow OpenWISP to +manage the WireGuard VPN server. + +Ensure that the VPN server attributes used in your playbook match the VPN +server configuration in OpenWISP. + +3. Create VPN Client Template for WireGuard VPN Server +------------------------------------------------------ + +1. Visit ``/admin/config/template/add/`` to add a new template. +2. Set ``WireGuard Client`` as **Name** (you can set whatever you want) + and select ``VPN-client`` as **type** from the dropdown list. +3. The **Backend** field refers to the backend of the device this template + can be applied to. For this example, we will leave it to ``OpenWrt``. +4. Select the correct VPN server from the dropdown for the **VPN** field. + Here it is ``WireGuard``. +5. Ensure that **Automatic tunnel provisioning** is checked. This will + make OpenWISP to automatically generate public and private keys and + provision IP address for each WireGuard VPN client. +6. After clicking on **Save and continue editing** button, you will see + details of *WireGuard* VPN server in **System Defined Variables**. The + template configuration will be automatically generated which you can + tweak accordingly. We will use the automatically generated VPN client + configuration for this example. + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-tutorial/template.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-tutorial/template.png + :alt: WireGuard VPN client template example + +4. Apply WireGuard VPN Template to Devices +------------------------------------------ + +.. note:: + + This step assumes that you already have a device registered on + OpenWISP. Register or create a device before proceeding. + +1. Open the **Configuration** tab of the concerned device. +2. Select the *WireGuard Client* template. +3. Upon clicking on **Save and continue editing** button, you will see + some entries in **System Defined Variables**. It will contain internal + IP address, private and public key for the WireGuard client on the + device along with details of WireGuard VPN server. + +.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-tutorial/device-configuration.png + :target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/wireguard-tutorial/device-configuration.png + :alt: WireGuard VPN device configuration example + +**Voila!** You have successfully configured OpenWISP to manage WireGuard +tunnels for your devices. + +.. seealso:: + + You may also want to explore other automated VPN tunnel provisioning + options: + + - :doc:`Wireguard over VXLAN ` + - :doc:`Zerotier ` + - :doc:`OpenVPN ` diff --git a/docs/user/zerotier.rst b/docs/user/zerotier.rst new file mode 100644 index 000000000..54249f0af --- /dev/null +++ b/docs/user/zerotier.rst @@ -0,0 +1,156 @@ +Automating ZeroTier Tunnels +=========================== + +.. raw:: html + +