Configuration manager for embedded devices, implemented as a reusable django-app.
Based on the NetJSON format and the netjsonconfig library.
Table of Contents:
- Current features
- Project goals
- Deploy it in production
- Dependencies
- Install stable version from pypi
- Install development version
- Setup (integrate in an existing django project)
- Installing for development
- Settings
NETJSONCONFIG_BACKENDS
NETJSONCONFIG_VPN_BACKENDS
NETJSONCONFIG_DEFAULT_BACKEND
NETJSONCONFIG_DEFAULT_VPN_BACKEND
NETJSONCONFIG_REGISTRATION_ENABLED
NETJSONCONFIG_CONSISTENT_REGISTRATION
NETJSONCONFIG_REGISTRATION_SELF_CREATION
NETJSONCONFIG_SHARED_SECRET
NETJSONCONFIG_CONTEXT
NETJSONCONFIG_DEFAULT_AUTO_CERT
NETJSONCONFIG_CERT_PATH
NETJSONCONFIG_COMMON_NAME_FORMAT
NETJSONCONFIG_MANAGEMENT_IP_DEVICE_LIST
NETJSONCONFIG_BACKEND_DEVICE_LIST
NETJSONCONFIG_HARDWARE_ID_ENABLED
NETJSONCONFIG_HARDWARE_ID_OPTIONS
NETJSONCONFIG_HARDWARE_ID_AS_NAME
- Extending django-netjsonconfig
- Screenshots
- Contributing
- Changelog
- License
- Support
- configuration management for embedded devices supporting different firmwares:
- OpenWRT
- OpenWISP Firmware
- support for additional firmware can be added by specifying custom 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
- 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
- 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)
- provide ways to support more firmwares by adding custom backends
- keep the core as simple as possible
An automated installer is available at ansible-openwisp2.
- Python >=3.6
- OpenSSL
Install from pypi:
pip install django-netjsonconfig
Install tarball:
pip install https://github.com/openwisp/django-netjsonconfig/tarball/master
Alternatively you can install via pip using git:
pip install -e git+git://github.com/openwisp/django-netjsonconfig#egg=django-netjsonconfig
If you want to contribute, install your cloned fork:
git clone git@github.com:<your_fork>/django-netjsonconfig.git
cd django-netjsonconfig
python setup.py develop
Add django_netjsonconfig
, django.contrib.admin
, sortedm2m
and reversion
to
INSTALLED_APPS
in the following order:
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
:
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:
./manage.py migrate
Install sqlite:
sudo apt-get install sqlite3 libsqlite3-dev openssl libssl-dev
Install your forked repo:
git clone git://github.com/<your_fork>/django-netjsonconfig
cd django-netjsonconfig/
python setup.py develop
Install test requirements:
pip install -r requirements-test.txt
Create database:
cd tests/
./manage.py migrate
./manage.py createsuperuser
Launch development server:
./manage.py runserver
You can access the admin interface at http://127.0.0.1:8000/admin/.
Run tests with:
./runtests.py
type: | tuple |
default: | (
('netjsonconfig.OpenWrt', 'OpenWRT'),
('netjsonconfig.OpenWisp', 'OpenWISP'),
) |
Available configuration backends. For more information, see netjsonconfig backends.
type: | tuple |
default: | (
('django_netjsonconfig.vpn_backends.OpenVpn', 'OpenVPN'),
) |
Available VPN backends for VPN Server objects. For more information, see OpenVPN netjsonconfig backend.
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 intoconfig['openvpn']
- it SHOULD focus on the server capabilities of the VPN software being used
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.
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.
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.
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.
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.
type: | str |
default: | "" |
A secret key which must be used by devices to perform automatic registration.
This key MUST be explicitly set in production (if settings.DEBUG is False
), otherwise
an ImproperlyConfigured
exception will be raised on startup.
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.
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).
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).
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
.
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.
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.
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.
type: | dict |
default: | {
'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 blanknull
: wether an empty value will be stored asNULL
in the databasemax_length
: maximum length of the fieldunique
: wether the value of the field must be uniqueverbose_name
: text for the human readable label of the fieldhelp_text
: help text to be displayed with the field
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
.
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.
Add the following to your settings.py
:
EXTENDED_APPS = ('django_netjsonconfig', 'django_x509',)
Add openwisp_utils.staticfiles.DependencyFinder
to
STATICFILES_FINDERS
in your settings.py
:
STATICFILES_FINDERS = [
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
'openwisp_utils.staticfiles.DependencyFinder',
]
Add openwisp_utils.loaders.DependencyLoader
to TEMPLATES
in your settings.py
:
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',
],
},
}
]
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.
# 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
Following the previous Organization
example, you can avoid duplicating the admin
code by importing the base admin classes and registering your models with.
# 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)
If your use case doesn't vary a lot from the base one, you may also want to try to reuse the controller views:
# 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()
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
:
# your_config_app.controller.urls
from django_netjsonconfig.utils import get_controller_urls
from . import views
urlpatterns = get_controller_urls(views)
You may want to reuse the AppConfig
class of django-netjsonconfig too:
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
For full working examples of django proejcts which extend django-netjsonconfig, see:
- Announce your intentions in the OpenWISP Mailing List
- Fork this repo and install it
- Follow PEP8, Style Guide for Python Code
- Write code
- Write tests for your code
- Ensure all tests pass
- Ensure test coverage does not decrease
- Document your changes
- Send pull request
See CHANGES.
See LICENSE.