Metadata-Version: 2.1
Name: django-netjsonconfig
Version: 0.11
Summary: Configuration manager for embedded devices, implemented as a reusable django-app
Home-page: http://netjson.org
Author: Federico Capoano
Author-email: f.capoano@cineca.it
License: GPL3
Download-URL: https://github.com/openwisp/django-netjsonconfig/releases
Keywords: django,netjson,openwrt,networking,openwisp
Platform: Platform Independent
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Web Environment
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
Classifier: Operating System :: OS Independent
Classifier: Framework :: Django
Classifier: Topic :: System :: Networking
Classifier: Programming Language :: Python :: 3
Requires-Dist: django (<3.1,>=2.2)
Requires-Dist: django-model-utils (>=4.0)
Requires-Dist: netjsonconfig (<0.8.0,>=0.7.0)
Requires-Dist: django-sortedm2m (<3.1.0,>=3.0.0)
Requires-Dist: django-reversion (<3.1.0,>=3.0.5)
Requires-Dist: django-x509 (<0.7.0,>=0.6.2)
Requires-Dist: django-taggit (<1.3.0,>=0.24.0)
Requires-Dist: openwisp-utils (<0.5.0,>=0.4.4)

django-netjsonconfig
====================

.. image:: https://travis-ci.org/openwisp/django-netjsonconfig.svg
   :target: https://travis-ci.org/openwisp/django-netjsonconfig

.. image:: https://coveralls.io/repos/openwisp/django-netjsonconfig/badge.svg
  :target: https://coveralls.io/r/openwisp/django-netjsonconfig

.. image:: https://requires.io/github/openwisp/django-netjsonconfig/requirements.svg?branch=master
   :target: https://requires.io/github/openwisp/django-netjsonconfig/requirements/?branch=master
   :alt: Requirements Status

.. image:: https://badge.fury.io/py/django-netjsonconfig.svg
   :target: http://badge.fury.io/py/django-netjsonconfig

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

Configuration manager for embedded devices, implemented as a reusable django-app.

Based on the `NetJSON`_ format and the `netjsonconfig`_ library.

.. image:: https://raw.githubusercontent.com/openwisp/django-netjsonconfig/master/docs/images/adhoc-interface.png
   :alt: adhoc interface

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

.. image:: https://raw.githubusercontent.com/openwisp/django-netjsonconfig/master/docs/images/preview.png
   :alt: preview

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

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

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

Current features
----------------

* **configuration management** for embedded devices supporting different firmwares:
    - `OpenWRT <http://openwrt.org>`_
    - `OpenWISP Firmware <https://github.com/openwisp/OpenWISP-Firmware>`_
    - support for additional firmware can be added by `specifying custom backends <#netjsonconfig-backends>`_
* **configuration editor** based on `JSON-Schema editor <https://github.com/jdorn/json-editor>`_
* **advanced edit mode**: edit `NetJSON`_ *DeviceConfiguration* objects for maximum flexibility
* **configuration templates**: reduce repetition to the minimum
* **configuration context**: reference ansible-like variables in the configuration
* **template tags**: tag templates to automate different types of auto-configurations (eg: mesh, WDS, 4G)
* **simple HTTP resources**: allow devices to automatically download configuration updates
* **VPN management**: easily create VPN servers and clients

Project goals
-------------

* automate configuration management for embedded devices
* allow to minimize repetition by using templates
* provide base logic that can be extended by third-party apps (see
  `Extending django-netjsonconfig <#extending-django-netjsonconfig>`_)
* provide ways to support more firmwares by adding custom backends
* keep the core as simple as possible

Deploy it in production
-----------------------

An automated installer is available at `ansible-openwisp2 <https://github.com/openwisp/ansible-openwisp2>`_.

Dependencies
------------

* Python >=3.6
* OpenSSL

Install stable version from pypi
--------------------------------

Install from pypi:

.. code-block:: shell

    pip install django-netjsonconfig

Install development version
---------------------------

Install tarball:

.. code-block:: shell

    pip install https://github.com/openwisp/django-netjsonconfig/tarball/master

Alternatively you can install via pip using git:

.. code-block:: shell

    pip install -e git+git://github.com/openwisp/django-netjsonconfig#egg=django-netjsonconfig

If you want to contribute, install your cloned fork:

.. code-block:: shell

    git clone git@github.com:<your_fork>/django-netjsonconfig.git
    cd django-netjsonconfig
    python setup.py develop

Setup (integrate in an existing django project)
-----------------------------------------------

Add ``django_netjsonconfig``, ``django.contrib.admin``, ``sortedm2m`` and ``reversion`` to
``INSTALLED_APPS`` in the following order:

.. code-block:: python

    INSTALLED_APPS = [
        # other apps
        'openwisp_utils.admin_theme',
        'django_netjsonconfig',
        # ensure the django admin comes after django-netjsonconfig
        'django.contrib.admin',
        'sortedm2m',
        'reversion'  # optional, can be removed if not needed
        # ...
    ]

Add the controller URLs to your main ``urls.py``:

.. code-block:: python

    urlpatterns = [
        # ... other urls in your project ...

        # controller URLs
        # used by devices to download/update their configuration
        # keep the namespace argument unchanged
        url(r'^', include('django_netjsonconfig.controller.urls', namespace='controller')),
        # common URLs
        # shared among django-netjsonconfig components
        # keep the namespace argument unchanged
        url(r'^', include('django_netjsonconfig.urls', namespace='netjsonconfig')),
    ]

Then run:

.. code-block:: shell

    ./manage.py migrate

Installing for development
--------------------------

Install sqlite:

.. code-block:: shell

    sudo apt-get install sqlite3 libsqlite3-dev openssl libssl-dev

Install your forked repo:

.. code-block:: shell

    git clone git://github.com/<your_fork>/django-netjsonconfig
    cd django-netjsonconfig/
    python setup.py develop

Install test requirements:

.. code-block:: shell

    pip install -r requirements-test.txt

Create database:

.. code-block:: shell

    cd tests/
    ./manage.py migrate
    ./manage.py createsuperuser

Launch development server:

.. code-block:: shell

    ./manage.py runserver

You can access the admin interface at http://127.0.0.1:8000/admin/.

Run tests with:

.. code-block:: shell

    ./runtests.py

Settings
--------

``NETJSONCONFIG_BACKENDS``
~~~~~~~~~~~~~~~~~~~~~~~~~~

+--------------+-----------------------------------------------+
| **type**:    | ``tuple``                                     |
+--------------+-----------------------------------------------+
| **default**: | .. code-block:: python                        |
|              |                                               |
|              |   (                                           |
|              |     ('netjsonconfig.OpenWrt', 'OpenWRT'),     |
|              |     ('netjsonconfig.OpenWisp', 'OpenWISP'),   |
|              |   )                                           |
+--------------+-----------------------------------------------+

Available configuration backends. For more information, see `netjsonconfig backends
<http://netjsonconfig.openwisp.org/en/latest/general/basics.html#backend>`_.

``NETJSONCONFIG_VPN_BACKENDS``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+--------------+----------------------------------------------------------------+
| **type**:    | ``tuple``                                                      |
+--------------+----------------------------------------------------------------+
| **default**: | .. code-block:: python                                         |
|              |                                                                |
|              |   (                                                            |
|              |     ('django_netjsonconfig.vpn_backends.OpenVpn', 'OpenVPN'),  |
|              |   )                                                            |
+--------------+----------------------------------------------------------------+

Available VPN backends for VPN Server objects. For more information, see `OpenVPN netjsonconfig backend
<http://netjsonconfig.openwisp.org/en/latest/backends/openvpn.html>`_.

A VPN backend must follow some basic rules in order to be compatible with *django-netjsonconfig*:

* 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

``NETJSONCONFIG_DEFAULT_BACKEND``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+--------------+----------------------------------+
| **type**:    | ``str``                          |
+--------------+----------------------------------+
| **default**: | ``NETJSONCONFIG_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 ``NETJSONCONFIG_BACKENDS`` setting,
which is ``netjsonconfig.OpenWrt``.

Setting it to ``None`` will force the user to choose explicitly.

``NETJSONCONFIG_DEFAULT_VPN_BACKEND``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+--------------+--------------------------------------+
| **type**:    | ``str``                              |
+--------------+--------------------------------------+
| **default**: | ``NETJSONCONFIG_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 ``NETJSONCONFIG_VPN_BACKENDS`` setting,
which is ``django_netjsonconfig.vpn_backends.OpenVpn``.

Setting it to ``None`` will force the user to choose explicitly.

``NETJSONCONFIG_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 <https://github.com/openwisp/openwisp-config#automatic-registration>`_ for more information.

``NETJSONCONFIG_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 <https://github.com/openwisp/openwisp-config#consistent-key-generation>`_
for more information.

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

``NETJSONCONFIG_SHARED_SECRET``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+--------------+------------------+
| **type**:    | ``str``          |
+--------------+------------------+
| **default**: | ``""``           |
+--------------+------------------+

A secret key which must be used by devices to perform `automatic registration
<https://github.com/openwisp/openwisp-config#automatic-registration>`_.

This key MUST be explicitly set in production (if ``settings.DEBUG is False``), otherwise
an ``ImproperlyConfigured`` exception will be raised on startup.

``NETJSONCONFIG_CONTEXT``
~~~~~~~~~~~~~~~~~~~~~~~~~

+--------------+------------------+
| **type**:    | ``dict``         |
+--------------+------------------+
| **default**: | ``{}``           |
+--------------+------------------+

Additional context that is passed to the default context of each ``Config`` object.

Each ``Config`` object gets the following attributes passed as configuration variables:

* ``id``
* ``key``
* ``name``
* ``mac_address``

``NETJSONCONFIG_CONTEXT`` can be used to define system-wide configuration variables.

For more information, see `netjsonconfig context: configuration variables
<http://netjsonconfig.openwisp.org/en/latest/general/basics.html#context-configuration-variables>`_.

``NETJSONCONFIG_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 a new x509 certificate should be created
automatically for each configuration using that template.

The automatically created certificates will also be removed when they are not
needed anymore (eg: when the VPN template is removed from a configuration object).

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

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

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

``NETJSONCONFIG_BACKEND_DEVICE_LIST``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+--------------+------------------------------+
| **type**:    | ``bool``                     |
+--------------+------------------------------+
| **default**: | ``True``                     |
+--------------+------------------------------+

In the device list page, the column ``backend`` and the backend filter are
shown by default.

If this setting is set to ``False`` these items will be removed from the UI.

You may set this to ``False`` if you are using only one configuration backend
and having this UI element doesn't add any value to your users.

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

``NETJSONCONFIG_HARDWARE_ID_OPTIONS``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+--------------+--------------------------------------------------------+
| **type**:    | ``dict``                                               |
+--------------+--------------------------------------------------------+
| **default**: | .. code-block:: python                                 |
|              |                                                        |
|              |    {                                                   |
|              |        'blank': not NETJSONCONFIG_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

``NETJSONCONFIG_HARDWARE_ID_AS_NAME``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+--------------+-------------+
| **type**:    | ``bool``    |
+--------------+-------------+
| **default**: | ``False``   |
+--------------+-------------+

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

Extending django-netjsonconfig
------------------------------

*django-netjsonconfig* provides a set of models, admin classes and generic views which can be imported,
extended and reused by third party apps.

To extend *django-netjsonconfig*, **you MUST NOT** add it to ``settings.INSTALLED_APPS``,
but you must create your own app (which goes into ``settings.INSTALLED_APPS``), import the
base classes from django-netjsonconfig and add your customizations.

In order to help django find the static files and templates of *django-netjsonconfig*,
you need to perform the steps described below.

1. Add ``EXTENDED_APPS``
~~~~~~~~~~~~~~~~~~~~~~~~

Add the following to your ``settings.py``:

.. code-block:: python

    EXTENDED_APPS = ('django_netjsonconfig', 'django_x509',)

2. 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',
    ]

3. Add ``openwisp_utils.loaders.DependencyLoader``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Add ``openwisp_utils.loaders.DependencyLoader`` to ``TEMPLATES`` in your ``settings.py``:

.. code-block:: python

    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'OPTIONS': {
                'loaders': [
                    'django.template.loaders.filesystem.Loader',
                    'django.template.loaders.app_directories.Loader',
                    'openwisp_utils.loaders.DependencyLoader',
                ],
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        }
    ]

Extending models
~~~~~~~~~~~~~~~~

This example provides an example of how to extend the base models of
*django-netjsonconfig* by adding a relation to another django model named `Organization`.

.. code-block:: python

    # models.py of your custom ``config`` app
    from django.db import models
    from sortedm2m.fields import SortedManyToManyField
    from taggit.managers import TaggableManager

    from django_netjsonconfig.base.config import AbstractConfig, TemplatesVpnMixin
    from django_netjsonconfig.base.tag import AbstractTaggedTemplate, AbstractTemplateTag
    from django_netjsonconfig.base.template import AbstractTemplate
    from django_netjsonconfig.base.vpn import AbstractVpn, AbstractVpnClient

    # the model ``organizations.Organization`` is omitted for brevity
    # if you are curious to see a real implementation, check out django-organizations
    # https://github.com/bennylope/django-organizations

    class OrganizationMixin(models.Model):
        organization = models.ForeignKey('organizations.Organization')

        class Meta:
            abstract = True


    class Config(OrganizationMixin, TemplatesVpnMixin, AbstractConfig):
        templates = SortedManyToManyField('config.Template',
                                          related_name='config_relations',
                                          blank=True)
        vpn = models.ManyToManyField('config.Vpn',
                                     through='config.VpnClient',
                                     related_name='vpn_relations',
                                     blank=True)

        def clean(self):
            # your own validation logic here...
            pass

        class Meta(AbstractConfig.Meta):
            abstract = False


    class TemplateTag(AbstractTemplateTag):
        class Meta(AbstractTemplateTag.Meta):
            abstract = False


    class TaggedTemplate(AbstractTaggedTemplate):
        tag = models.ForeignKey('config.TemplateTag',
                                related_name='%(app_label)s_%(class)s_items',
                                on_delete=models.CASCADE)

        class Meta(AbstractTaggedTemplate.Meta):
            abstract = False


    class Template(OrganizationMixin, AbstractTemplate):
        tags = TaggableManager(through='config.TaggedTemplate', blank=True)
        vpn = models.ForeignKey('config.Vpn', blank=True, null=True)

        def clean(self):
            # your own validation logic here...
            pass

        class Meta(AbstractTemplate.Meta):
            abstract = False


    class Vpn(OrganizationMixin, AbstractVpn):
        class Meta(AbstractVpn.Meta):
            abstract = False


    class VpnClient(AbstractVpnClient):
        config = models.ForeignKey('config.Config', on_delete=models.CASCADE)
        vpn = models.ForeignKey('config.Vpn', on_delete=models.CASCADE)
        cert = models.OneToOneField('django_x509.Cert',
                                    on_delete=models.CASCADE,
                                    blank=True,
                                    null=True)

        class Meta(AbstractVpnClient.Meta):
            abstract = False

Extending the admin
~~~~~~~~~~~~~~~~~~~

Following the previous ``Organization`` example, you can avoid duplicating the admin
code by importing the base admin classes and registering your models with.

.. code-block:: python

    # admin.py of your app
    # these are your custom models, they must be imported before the abstract admin classes
    from .models import Config, Template, Vpn

    from django.contrib import admin
    from django_netjsonconfig.base.admin import (AbstractConfigAdmin,
                                                 AbstractConfigForm,
                                                 AbstractTemplateAdmin,
                                                 AbstractVpnAdmin,
                                                 AbstractVpnForm,
                                                 BaseForm)


    class ConfigForm(AbstractConfigForm):
        class Meta(AbstractConfigForm.Meta):
            model = Config


    class ConfigAdmin(AbstractConfigAdmin):
        form = ConfigForm


    class TemplateForm(BaseForm):
        class Meta(BaseForm.Meta):
            model = Template


    class TemplateAdmin(AbstractTemplateAdmin):
        form = TemplateForm


    class VpnForm(AbstractVpnForm):
        class Meta(AbstractVpnForm.Meta):
            model = Vpn


    class VpnAdmin(AbstractVpnAdmin):
        form = VpnForm


    admin.site.register(Config, ConfigAdmin)
    admin.site.register(Template, TemplateAdmin)
    admin.site.register(Vpn, VpnAdmin)

Extending controller views
~~~~~~~~~~~~~~~~~~~~~~~~~~

If your use case doesn't vary a lot from the base one, you may also want
to try to reuse the controller views:

.. code-block:: python

    # your_config_app.controller.views
    from ..models import Device, Vpn
    from django_netjsonconfig.controller.generics import (BaseDeviceChecksumView, BaseDeviceDownloadConfigView,
                                                          BaseDeviceRegisterView, BaseDeviceReportStatusView,
                                                          BaseVpnChecksumView, BaseVpnDownloadConfigView)

    class DeviceChecksumView(BaseDeviceChecksumView):
        model = Device


    class DeviceDownloadConfigView(BaseDeviceDownloadConfigView):
        model = Device


    class DeviceReportStatusView(BaseDeviceReportStatusView):
        model = Device


    class DeviceRegisterView(BaseDeviceRegisterView):
        model = Device


    class VpnChecksumView(BaseVpnChecksumView):
        model = Vpn


    class VpnDownloadConfigView(BaseVpnDownloadConfigView):
        model = Vpn


    device_checksum = DeviceChecksumView.as_view()
    device_download_config = DeviceDownloadConfigView.as_view()
    device_report_status = DeviceReportStatusView.as_view()
    device_register = DeviceRegisterView.as_view()
    vpn_checksum = VpnChecksumView.as_view()
    vpn_download_config = VpnDownloadConfigView.as_view()

Controller URLs
~~~~~~~~~~~~~~~

If you are not making drastic changes to the controller views, you can avoid duplicating the URL
logic by using the ``get_controller_urls`` function. Put this in your controller ``urls.py``:

.. code-block:: python

    # your_config_app.controller.urls
    from django_netjsonconfig.utils import get_controller_urls
    from . import views

    urlpatterns = get_controller_urls(views)

Extending AppConfig
~~~~~~~~~~~~~~~~~~~

You may want to reuse the ``AppConfig`` class of *django-netjsonconfig* too:

.. code-block:: python

    from django_netjsonconfig.apps import DjangoNetjsonconfigApp


    class MyOwnConfig(DjangoNetjsonconfigApp):
        name = 'yourapp.config'
        label = 'config'

        def __setmodels__(self):
            from .models import Config, VpnClient  # these are your custom models
            self.config_model = Config
            self.vpnclient_model = VpnClient

Real world extensions of django-netjsonconfig
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

For full working examples of django proejcts which extend *django-netjsonconfig*, see:

- `openwisp/openwisp-controller <https://github.com/openwisp/openwisp-controller>`_
- `innovationgarage/extendnetjson_project <https://github.com/innovationgarage/extendnetjson_project>`_

Screenshots
-----------

.. image:: https://raw.githubusercontent.com/openwisp/django-netjsonconfig/master/docs/images/configuration-ui.png
   :alt: configuration item

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

.. image:: https://raw.githubusercontent.com/openwisp/django-netjsonconfig/master/docs/images/bridge.png
   :alt: bridge

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

.. image:: https://raw.githubusercontent.com/openwisp/django-netjsonconfig/master/docs/images/radio.png
   :alt: radio

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

.. image:: https://raw.githubusercontent.com/openwisp/django-netjsonconfig/master/docs/images/wpa-enterprise.png
  :alt: wpa enterprise

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

.. image:: https://raw.githubusercontent.com/openwisp/django-netjsonconfig/master/docs/images/preview.png
  :alt: preview

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

.. image:: https://raw.githubusercontent.com/openwisp/django-netjsonconfig/master/docs/images/adhoc-interface.png
   :alt: adhoc interface

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

1. Announce your intentions in the `OpenWISP Mailing List <https://groups.google.com/d/forum/openwisp>`_
2. Fork this repo and install it
3. Follow `PEP8, Style Guide for Python Code`_
4. Write code
5. Write tests for your code
6. Ensure all tests pass
7. Ensure test coverage does not decrease
8. Document your changes
9. Send pull request

.. _PEP8, Style Guide for Python Code: http://www.python.org/dev/peps/pep-0008/
.. _NetJSON: http://netjson.org
.. _netjsonconfig: http://netjsonconfig.openwisp.org

Changelog
---------

See `CHANGES <https://github.com/openwisp/django-netjsonconfig/blob/master/CHANGES.rst>`_.

License
-------

See `LICENSE <https://github.com/openwisp/django-netjsonconfig/blob/master/LICENSE>`_.

Support
-------

See `OpenWISP Support Channels <http://openwisp.org/support.html>`_.


