Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

query backends from installed packages #106

Merged
merged 1 commit into from Jun 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 5 additions & 8 deletions bin/netjsonconfig
@@ -1,9 +1,11 @@
#!/usr/bin/env python

import argparse
import os
import sys

import six
import argparse

import netjsonconfig

description = """
Expand Down Expand Up @@ -56,7 +58,7 @@ output = parser.add_argument_group('output')

output.add_argument('--backend', '-b',
required=True,
choices=['openwrt', 'openwisp', 'openvpn'],
choices=netjsonconfig.get_backends().keys(),
action='store',
type=str,
help='Configuration backend')
Expand Down Expand Up @@ -166,13 +168,8 @@ context = dict(os.environ)
method = args.method
method_arguments = parse_method_arguments(args.args)

backends = {
'openwrt': netjsonconfig.OpenWrt,
'openwisp': netjsonconfig.OpenWisp,
'openvpn': netjsonconfig.OpenVpn
}

backend_class = backends[args.backend]
backend_class = netjsonconfig.get_backends()[args.backend]
try:
options = dict(templates=templates, context=context)
if args.config:
Expand Down
96 changes: 96 additions & 0 deletions docs/source/backends/create_your_backend.rst
@@ -0,0 +1,96 @@

===================
Create your backend
===================

.. include:: ../_github.rst

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.

.. code-block:: python

# 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',
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 <https://packaging.python.org/>`_
and `at the hitchhikers guide to packaging <https://the-hitchhikers-guide-to-packaging.readthedocs.io/en/latest/>`_.

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

.. code-block:: python

{
# 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.

.. code-block:: python

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

The previous example can be used with the following class definition

.. code-block:: python

# 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
1 change: 1 addition & 0 deletions docs/source/index.rst
Expand Up @@ -55,6 +55,7 @@ Contents:
/backends/openwrt
/backends/openwisp
/backends/openvpn
/backends/create_your_backend
/general/commandline_utility
/general/running_tests
/general/contributing
Expand Down
19 changes: 19 additions & 0 deletions netjsonconfig/__init__.py
@@ -1,5 +1,24 @@
from pkg_resources import iter_entry_points
import logging

from .version import VERSION, __version__, get_version # noqa

from .backends.openwrt.openwrt import OpenWrt # noqa
from .backends.openwisp.openwisp import OpenWisp # noqa
from .backends.openvpn.openvpn import OpenVpn # noqa


def get_backends():
default = {
'openwrt': OpenWrt,
'openwisp': OpenWisp,
'openvpn': OpenVpn,
}
logger = logging.getLogger(__name__)

for entry_point in iter_entry_points('netjsonconfig.backends'):
try:
default.update({entry_point.name.lower(): entry_point.load()})
except ImportError as e: # noqa
logger.error("Error loading backend {}".format(entry_point.name.lower()))
return default
1 change: 0 additions & 1 deletion netjsonconfig/backends/openwrt/schema.py
Expand Up @@ -6,7 +6,6 @@
from ..openvpn.schema import base_openvpn_schema
from .timezones import timezones


default_radio_driver = "mac80211"


Expand Down