OpenWISP Controller is a configuration manager that allows to automate several networking tasks like adoption, provisioning, management VPN configuration, X509 certificates automatic generation, revocation of x509 certificates and a lot more features.
OpenWISP is not only an application designed for end users, but can also be used as a framework on which custom network automation solution can be 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-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)
- openwisp-network-topology: provides way to collect and visualize network topology data from dynamic mesh routing deamons 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: it allows to manage the IP address space of networks
Want to help OpenWISP? Find out how to help us grow here.
Table of Contents:
- Deploy it in production
- Dependencies
- Install stable version from pypi
- Install development version
- Project Structure & main features
- Settings
OPENWISP_SSH_AUTH_TIMEOUT
OPENWISP_SSH_BANNER_TIMEOUT
OPENWISP_SSH_COMMAND_TIMEOUT
OPENWISP_SSH_CONNECTION_TIMEOUT
OPENWISP_CONNECTORS
OPENWISP_UPDATE_STRATEGIES
OPENWISP_CONFIG_UPDATE_MAPPING
OPENWISP_CONTROLLER_BACKENDS
OPENWISP_CONTROLLER_VPN_BACKENDS
OPENWISP_CONTROLLER_DEFAULT_BACKEND
OPENWISP_CONTROLLER_DEFAULT_VPN_BACKEND
OPENWISP_CONTROLLER_REGISTRATION_ENABLED
OPENWISP_CONTROLLER_CONSISTENT_REGISTRATION
OPENWISP_CONTROLLER_REGISTRATION_SELF_CREATION
OPENWISP_CONTROLLER_CONTEXT
OPENWISP_CONTROLLER_DEFAULT_AUTO_CERT
OPENWISP_CONTROLLER_CERT_PATH
OPENWISP_CONTROLLER_COMMON_NAME_FORMAT
OPENWISP_CONTROLLER_MANAGEMENT_IP_DEVICE_LIST
OPENWISP_CONTROLLER_BACKEND_DEVICE_LIST
OPENWISP_CONTROLLER_HARDWARE_ID_ENABLED
OPENWISP_CONTROLLER_HARDWARE_ID_OPTIONS
OPENWISP_CONTROLLER_HARDWARE_ID_AS_NAME
- Installing for development
- Install and run on docker
- Troubleshooting Steps
- How to use configuration variables
- Signals
- Setup (Integrate into other Apps)
- Extending openwisp-controller
- 1. Initialize your project & custom apps
- 2. Install
openwisp-controller
- 3. Add your apps in INSTALLED_APPS
- 4. Add
EXTENDED_APPS
- 5. Add
openwisp_utils.staticfiles.DependencyFinder
- 6. Add
openwisp_utils.loaders.DependencyLoader
- 5. Initial Database setup
- 6. Other Settings
- 6. Inherit the AppConfig class
- 7. Create your custom models
- 8. Add swapper configurations
- 9. Create database migrations
- 10. Create the admin
- 11. Create root URL configuration
- 12. Import the automated tests
- Other base classes that can be inherited and extended
- Talks
- Contributing
- Changelog
- License
- Support
An automated installer is available at ansible-openwisp2.
- Python >= 3.6
- OpenSSL
Install from pypi:
pip install openwisp-controller
Install tarball:
pip install https://github.com/openwisp/openwisp-controller/tarball/master
Alternatively you can install via pip using git:
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.
OpenWISP Controller is a python package consisting of four django apps:
- 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 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)
- simple HTTP resources: allow devices to automatically download configuration updates
- VPN management: automatically provision VPN tunnels with unique x509 certificates
The PKI app is based on django-x509, it allows to create, import and view x509 CAs and certificates directly from the administration dashboard.
This app enables the controller to instantiate connections to the devices in order perform push operations (eg: configuration updates or firmware upgrades via the additional firmware upgrade module).
The default connection protocol implemented is SSH, but other protocol mechanism is extensible and custom protocols can be implemented as well.
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.
This module also provides an API through which mobile devices can update their coordinates. See below for further details:
GET /api/v1/device/{id}/location/
PUT /api/v1/device/{id}/location/
You can change the values for the following variables in
settings.py
to configure your instance of openwisp-controller.
type: | int |
default: | 2 |
Configure timeout to wait for an authentication response when establishing a SSH connection.
type: | int |
default: | 60 |
Configure timeout to wait for the banner to be presented when establishing a SSH connection.
type: | int |
default: | 30 |
Configure timeout on blocking read/write operations when executing a command in a SSH connection.
type: | int |
default: | 5 |
Configure timeout for the TCP connect when establishing a SSH connection.
type: | tuple |
default: | (
('openwisp_controller.connection.connectors.ssh.Ssh', 'SSH'),
) |
Available connector classes. Connectors are python classes that specify ways in which OpenWISP can connect to devices in order to launch commands.
type: | tuple |
default: | (
('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 configuratio 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.
type: | dict |
default: | {
'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.
type: | tuple |
default: | (
('netjsonconfig.OpenWrt', 'OpenWRT'),
('netjsonconfig.OpenWisp', 'OpenWISP'),
) |
Available configuration backends. For more information, see netjsonconfig backends.
type: | tuple |
default: | (
('openwisp_controller.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 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 intoconfig['openvpn']
- it SHOULD focus on the server capabilities of the VPN software being used
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.
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.
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: | 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.
For technical information about how variables are handled in the lower levels of OpenWISP, 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 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 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
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
.
Install the system dependencies:
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
Launch Redis:
docker-compose up -d redis
Install your forked repo:
git clone git://github.com/<your_fork>/openwisp-controller
cd openwisp-controller/
python setup.py develop
Install development dependencies:
./install-dev.sh
pip install -r requirements-test.txt
npm install -g jslint
Create database:
cd tests/
./manage.py migrate
./manage.py createsuperuser
Launch celery worker (for background jobs):
celery -A openwisp2 worker -l info
Launch development server:
./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:
pytest -c pytest.ini
./runtests.py
Run quality assurance tests with:
./run-qa-checks
NOTE: This Docker image is for development purposes only. For the official OpenWISP Docker images, see: docker-openwisp.
Build from the Dockerfile:
docker-compose build
Run the docker container:
docker-compose up
You may encounter some issues while installing GeoDjango.
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.
Please refer troubleshooting issues related to geospatial libraries.
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.
Each device gets the following attributes passed as configuration variables:
id
key
name
mac_address
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:
It's possible to specify the default values of variables defined in a template.
This allows to achieve 2 goals:
- pass schema validation without errors (otherwise it would not be possible to save the template in the first place)
- 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.
The default values of variables can be manipulated from the section "configuration variables" in the edit template page:
Variables can also be defined globally using the OPENWISP_CONTROLLER_CONTEXT setting.
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:
{
"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:
{
"wlan0_ssid": "SnakeOil PublicWiFi",
"wlan0_password": "Snakeoil_pwd!321654"
}
The default values can then be overridden at device level if needed, eg:
{
"wlan0_ssid": "Room 23 ACME Hotel",
"wlan0_password": "room_23pwd!321654"
}
Path: openwisp_controller.config.signals.config_modified
Arguments:
instance
: instance ofConfig
which got itsconfig
modified
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.
It is not triggered when the device is created for the first time.
This signal is used to trigger the update of the configuration on devices, when the push feature is enabled (requires Device credentials).
Path: openwisp_controller.config.signals.config_status_changed
Arguments:
instance
: instance ofConfig
which got itsstatus
changed
This signal is emitted only when the configuration status of a device has changed.
Path: openwisp_controller.config.signals.checksum_requested
Arguments:
instance
: instance ofDevice
for which its configuration checksum has been requestedrequest
: 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.
Path: openwisp_controller.config.signals.config_download_requested
Arguments:
instance
: instance ofDevice
for which its configuration has been requested for downloadrequest
: 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.
Path: openwisp_controller.connection.signals.is_working_changed
Arguments:
instance
: instance ofDeviceConnection
is_working
: value ofDeviceConnection.is_working
old_is_working
: previous value ofDeviceConnection.is_working
, eitherNone
(for new connections),True
orFalse
failure_reason
: error message explaining reason for failure in establishing connection
This signal is emitted every time DeviceConnection.is_working
changes.
It is not triggered when the device is created for the first time.
Add openwisp_controller
to INSTALLED_APPS
:
INSTALLED_APPS = [
# other apps
'openwisp_controller',
]
Add the URLs to your main urls.py
:
urlpatterns = [
# ... other urls in your project ...
# openwisp-controller urls
url(r'^', include('openwisp_controller.urls')),
]
Then run:
./manage.py migrate
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.
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.
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
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
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.
Install (and add to the requirement of your project) openwisp-controller:
pip install openwisp-controller
Now you need to add mycontroller.sample_config
,
mycontroller.sample_pki
, mycontroller.sample_connection
& mycontroller.sample_geo
to INSTALLED_APPS
in your settings.py
,
ensuring also that openwisp_controller.config
, openwisp_controller.geo
,
openwisp_controller.pki
, openwisp_controller.connnection
have been removed:
# Remember: Order in INSTALLED_APPS is important.
INSTALLED_APPS = [
# other django installed apps
'openwisp_utils.admin_theme',
# 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
'mycontroller.sample_config',
'mycontroller.sample_pki',
'mycontroller.sample_geo',
'mycontroller.sample_connection',
'openwisp_users',
# admin
'django.contrib.admin',
# other dependencies
'sortedm2m',
'reversion',
'leaflet',
# rest framework
'rest_framework',
'rest_framework_gis',
# channels
'channels',
]
Substitute mycontroller
, sample_config
, sample_pki
, sample_connection
&
sample_geo
with the name you chose in step 1.
Add the following to your settings.py
:
EXTENDED_APPS = (
'django_x509',
'django_loci',
'openwisp_controller.config',
'openwisp_controller.pki',
'openwisp_controller.geo',
'openwisp_controller.connection',
)
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',
],
},
}
]
Ensure you are using one of the available geodjango backends, eg:
DATABASES = {
'default': {
'ENGINE': 'django.contrib.gis.db.backends.spatialite',
'NAME': 'openwisp-controller.db',
}
}
For more information about GeoDjango, please refer to the geodjango documentation.
Add the following settings to settings.py
:
FORM_RENDERER = 'django.forms.renderers.TemplatesSetting'
ASGI_APPLICATION = 'openwisp_controller.geo.channels.routing.channel_routing'
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.
Please refer to the following files in the sample app of the test project:
- sample_config:
- sample_geo:
- sample_pki:
- sample_connection:
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.
For the purpose of showing an example, we added a simple "details" field to the models of the sample app in the test project.
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.
Once you have created the models, add the following to your settings.py
:
# Setting models for swapper module
CONFIG_DEVICE_MODEL = 'sample_config.Device'
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'
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'
Substitute sample_config
, sample_pki
, sample_connection
&
sample_geo
with the name you chose in step 1.
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_groups_permissions.py
- sample_pki/migrations/0002_default_groups_permissions.py
- sample_connection/migrations/0002_default_groups_permissions.py
Create database migrations:
./manage.py migrate
For more information, refer to the "Migrations" section in the django documentation.
Refer to the admin.py
file of the sample app.
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.
If the changes you need to add are relatively small, you can resort to monkey patching.
For example:
from openwisp_controller.config.admin import DeviceAdmin, TemplateAdmin, VpnAdmin
# DeviceAdmin.fields += ['example'] <-- monkey patching example
from openwisp_controller.connection.admin import CredentialsAdmin
# CredentialsAdmin.fields += ['example'] <-- monkey patching example
from openwisp_controller.geo.admin import FloorPlanAdmin, LocationAdmin
# FloorPlanAdmin.fields += ['example'] <-- monkey patching example
from openwisp_controller.geo.admin import CaAdmin, CertAdmin
# CaAdmin.fields += ['example'] <-- monkey patching example
If you need to introduce significant changes and/or you don't want to resort to monkey patching, you can proceed as follows:
from django.contrib import admin
from openwisp_controller.config.admin import (
DeviceAdmin as BaseDeviceAdmin,
TemplateAdmin as BaseTemplateAdmin,
VpnAdmin as BaseVpnAdmin,
from swapper import load_model
Vpn = load_model('openwisp_controller', 'Vpn')
Device = load_model('openwisp_controller', 'Device')
Template = load_model('openwisp_controller', 'Template')
admin.site.unregister(Vpn)
admin.site.unregister(Device)
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(Template)
class TemplateAdmin(BaseTemplateAdmin):
# add your changes here
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
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
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
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.urls')),
]
For more information about URL configuration in django, please refer to the "URL dispatcher" section in the django documentation.
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
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".
The following steps are not required and are intended for more advanced customization.
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.
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.
Please refer to the OpenWISP contributing guidelines.
See CHANGES.
See LICENSE.