Create your backend

Every backend is based on the common ground of some elements provided by the netjsonconfig library. The BaseBackend, BaseConverter, BaseParser and BaseRenderer are a battle proven set of tools that can be extended when creating you backend.

But the netjsonconfig package is not a playground to experiment, your contributions to a new backend should start elsewhere, a different package, where you are in control and can make errors and experiment more.

Netjsonconfig can now discover packages that provides a custom backend using a feature available in the Python packaging ecosystem which is called entry_points.

To create a new backend start from scratch with a new folder and add this file to your project root directory.

# example_backend/setup.py
from setuptools import setup, find_packages

setup(
    name='example_backend',
    version='0.0.0',
    description='an example to illustrate a netjsonconfig backend as an external module',
    install_requires=['netjsonconfig>=0.6.3'],
    packages=find_packages(),
    entry_points={
        'netjsonconfig.backends': [
            'example=example_backend.__init__:ExampleBackend',
        ]
    }
)

this file can be used to create a package that can be installed using pip or other tools in the python ecosystem. You can find more information about Python packaging at packaging.python.org and at the hitchhikers guide to packaging.

The most important part is to give your package a good name, a well thought description and to add the entry_points keyword argument with the following code

{
        # this is used by netjsonconfig
        # to find your backend
        'netjsonconfig.backends': [
                ...
        ]
}

Now your package will be in the list of backends that netjsonconfig can use!

But we still have to give us a name to be unique! Netjsonconfig already defined the names openwisp, openwrt and openvpn but you can choose whatever you like most.

The name netjsonconfig.backends will be associated with a list of classes from your package that will be presented to netjconfig at runtime. To specify which classes you want to expose write the triple name, path and class_name using the format name=path:class_name as in the example below.

The path part is simply the path to the file that contains the class you want to expose and the class_name is the name of the class.

{
        'netjsonconfig.backends': [
                # name=path:class_name
                'example=example_backend.__init__:ExampleBackend',
        ]
}

The previous example can be used with the following class definition

# example_backend/example_backend/__init__.py
from netjsonconfig.backends.base.backend import BaseBackend
from netjsonconfig.backends.base.renderer import BaseRenderer
from netjsonconfig.backends.base.parser import BaseParser

from netjsonconfig.schema import schema as default_schema

class ExampleBackend(BaseBackend):
    schema = default_schema
    converter = []
    parser = BaseParser
    renderer = BaseRenderer

Once you have your python package configured with the correct entry points you should have a directory tree that looks like this.

$ tree example_backend
example_backend
├── example_backend
│   └── __init__.py
└── setup.py

And now you can install your package using pip install -e ./example_backend or python setup.py install.

As netjsonconfig is a dependency for example_backend you can use your backend directly from the command line, e.g.

$ netjsonconfig
usage: netjsonconfig [-h] [--config CONFIG]
                 [--templates [TEMPLATES [TEMPLATES ...]]]
                 [--native NATIVE] --backend
                 {openwrt,openwisp,openvpn,example} --method
                 {render,generate,write,validate,json}
                 [--args [ARGS [ARGS ...]]] [--verbose] [--version]
netjsonconfig: error: the following arguments are required: --backend/-b, --method/-m

Notice the example in {openwrt,openwisp,openvpn,example}? That’s your backend!

The name exposed is the one chosen in the name, path, class triple from before

# name=path:class
'example=example_backend.__init__:ExampleBackend',