<commit_before>==============
Basic concepts
==============

.. raw:: html

    <p>
        <iframe src="https://nodeshot.org/github-btn.html?user=openwisp&amp;repo=netjsonconfig&amp;type=watch&amp;count=true&amp;size=large" frameborder="0" scrolling="0" width="120" height="33"></iframe>
        <iframe src="https://nodeshot.org/github-btn.html?user=openwisp&amp;repo=netjsonconfig&amp;type=fork&amp;count=true&amp;size=large" frameborder="0" scrolling="0" width="120" height="33"></iframe>
    </p>

Before starting, let's quickly introduce the main concepts used in netjsonconfig:

* :ref:`configuration_dictionary`: python dictionary representing the configuration of a router
* :ref:`backend`: python class used to process the configuration and generate the final
  router configuration
* :ref:`schema`: each backend has a `JSON-Schema <http://json-schema.org>`_ which
  defines the useful configuration options that the backend is able to process
* :ref:`validation`: the configuration is validated against its JSON-Schema before
  being processed by the backend
* :ref:`template`: common configuration options shared among routers (eg: VPNs, SSID)
  which can be passed to backends
* :ref:`multiple_templates`: possibility inherit common configuration options from more than
  one template
* :ref:`context`: variables that can be referenced from the *configuration dictionary*

.. _configuration_dictionary:

Configuration dictionary
------------------------

*netjsonconfig* is an implementation of the `NetJSON <http://netjson.org>`_ format,
more specifically the ``DeviceConfiguration`` object, therefore to understand the
configuration format that the library uses to generate the final router configurations
it is essential to read at least the relevant `DeviceConfiguration section in the
NetJSON RFC <http://netjson.org/rfc.html#rfc.section.5>`_.

Here it is a simple NetJSON DeviceConfiguration object:

.. code-block:: python

    {
        "type": "DeviceConfiguration",
        "general": {
            "hostname": "RouterA"
        },
        "interfaces": [
            {
                "name": "eth0",
                "type": "ethernet",
                "addresses": [
                    {
                        "address": "192.168.1.1",
                        "mask": 24,
                        "proto": "static",
                        "family": "ipv4"
                    }
                ]
            }
        ]
    }

The previous example describes a device named ``RouterA`` which has a single
network interface named ``eth0`` with a statically assigned ip address ``192.168.1.1/24``
(CIDR notation).

Because netjsonconfig deals only with ``DeviceConfiguration`` objects, the ``type`` attribute can be omitted.

The previous configuration object therefore can be shortened to:

.. code-block:: python

    {
        "general": {
            "hostname": "RouterA"
        },
        "interfaces": [
            {
                "name": "eth0",
                "type": "ethernet",
                "addresses": [
                    {
                        "address": "192.168.1.1",
                        "mask": 24,
                        "proto": "static",
                        "family": "ipv4"
                    }
                ]
            }
        ]
    }

From now on we will use the term *configuration dictionary* to refer to
*NetJSON DeviceConfiguration* objects.

.. _backend:

Backend
-------

A backend is a python class used to process the *configuration dictionary* and
generate the final router configuration, each supported firmware or opearting system
will have its own backend and third parties can write their own custom backends.

The current implemented backends are:

 * :doc:`OpenWrt </backends/openwrt>`
 * :doc:`OpenWisp </backends/openwisp>` (based on the ``OpenWrt`` backend)

Example initialization of ``OpenWrt`` backend:

.. code-block:: python

    from netjsonconfig import OpenWrt

    ipv6_router = OpenWrt({
        "interfaces": [
            {
                "name": "eth0.1",
                "type": "ethernet",
                "addresses": [
                    {
                        "address": "fd87::1",
                        "mask": 128,
                        "proto": "static",
                        "family": "ipv6"
                    }
                ]
            }
        ]
    })

.. _schema:

Schema
------

Each backend has a JSON-Schema, all the backends have a schema which is derived
from the same parent schema, defined in ``netjsonconfig.backends.schema``
(`view source <https://github.com/openwisp/netjsonconfig/blob/master/netjsonconfig/schema.py>`_).

Since different backends may support different features each backend may extend its
schema by adding custom definitions.

.. _validation:

Validation
----------

All the backends have a ``validate`` method which is called automatically before
trying to process the configuration.

If the passed configuration violates the schema the ``validate`` method will raise
a ``ValidationError``.

An instance of validation error has two public attributes:

* ``message``: a human readable message explaining the error
* ``details``: a reference to the instance of ``jsonschema.exceptions.ValidationError``
  which contains more details about what has gone wrong;
  for a complete reference see the `python-jsonschema documentation <https://python-jsonschema.readthedocs.org/en/latest/errors/#handling-validation-errors>`_

You may call the ``validate`` method in your application arbitrarily, eg: before
trying to save the *configuration dictionary* into a database.

.. _template:

Template
--------

If you have devices with very similar *configuration dictionaries* you can store the shared
blocks in one or more reusable templates which will be used as a base to build
the final configuration.
When using multiple templates, their order is important.
Templates that come afterwards overrides the templates that come before it.

Let's illustrate this with a practical example, we have two devices:

 * Router1
 * Router2

Both devices have an ``eth0`` interface in DHCP mode; *Router2* additionally has<commit_msg>Added a note under Templates<commit_after>==============
Basic concepts
==============

.. raw:: html

    <p>
        <iframe src="https://nodeshot.org/github-btn.html?user=openwisp&amp;repo=netjsonconfig&amp;type=watch&amp;count=true&amp;size=large" frameborder="0" scrolling="0" width="120" height="33"></iframe>
        <iframe src="https://nodeshot.org/github-btn.html?user=openwisp&amp;repo=netjsonconfig&amp;type=fork&amp;count=true&amp;size=large" frameborder="0" scrolling="0" width="120" height="33"></iframe>
    </p>

Before starting, let's quickly introduce the main concepts used in netjsonconfig:

* :ref:`configuration_dictionary`: python dictionary representing the configuration of a router
* :ref:`backend`: python class used to process the configuration and generate the final
  router configuration
* :ref:`schema`: each backend has a `JSON-Schema <http://json-schema.org>`_ which
  defines the useful configuration options that the backend is able to process
* :ref:`validation`: the configuration is validated against its JSON-Schema before
  being processed by the backend
* :ref:`template`: common configuration options shared among routers (eg: VPNs, SSID)
  which can be passed to backends
* :ref:`multiple_templates`: possibility inherit common configuration options from more than
  one template
* :ref:`context`: variables that can be referenced from the *configuration dictionary*

.. _configuration_dictionary:

Configuration dictionary
------------------------

*netjsonconfig* is an implementation of the `NetJSON <http://netjson.org>`_ format,
more specifically the ``DeviceConfiguration`` object, therefore to understand the
configuration format that the library uses to generate the final router configurations
it is essential to read at least the relevant `DeviceConfiguration section in the
NetJSON RFC <http://netjson.org/rfc.html#rfc.section.5>`_.

Here it is a simple NetJSON DeviceConfiguration object:

.. code-block:: python

    {
        "type": "DeviceConfiguration",
        "general": {
            "hostname": "RouterA"
        },
        "interfaces": [
            {
                "name": "eth0",
                "type": "ethernet",
                "addresses": [
                    {
                        "address": "192.168.1.1",
                        "mask": 24,
                        "proto": "static",
                        "family": "ipv4"
                    }
                ]
            }
        ]
    }

The previous example describes a device named ``RouterA`` which has a single
network interface named ``eth0`` with a statically assigned ip address ``192.168.1.1/24``
(CIDR notation).

Because netjsonconfig deals only with ``DeviceConfiguration`` objects, the ``type`` attribute can be omitted.

The previous configuration object therefore can be shortened to:

.. code-block:: python

    {
        "general": {
            "hostname": "RouterA"
        },
        "interfaces": [
            {
                "name": "eth0",
                "type": "ethernet",
                "addresses": [
                    {
                        "address": "192.168.1.1",
                        "mask": 24,
                        "proto": "static",
                        "family": "ipv4"
                    }
                ]
            }
        ]
    }

From now on we will use the term *configuration dictionary* to refer to
*NetJSON DeviceConfiguration* objects.

.. _backend:

Backend
-------

A backend is a python class used to process the *configuration dictionary* and
generate the final router configuration, each supported firmware or opearting system
will have its own backend and third parties can write their own custom backends.

The current implemented backends are:

 * :doc:`OpenWrt </backends/openwrt>`
 * :doc:`OpenWisp </backends/openwisp>` (based on the ``OpenWrt`` backend)

Example initialization of ``OpenWrt`` backend:

.. code-block:: python

    from netjsonconfig import OpenWrt

    ipv6_router = OpenWrt({
        "interfaces": [
            {
                "name": "eth0.1",
                "type": "ethernet",
                "addresses": [
                    {
                        "address": "fd87::1",
                        "mask": 128,
                        "proto": "static",
                        "family": "ipv6"
                    }
                ]
            }
        ]
    })

.. _schema:

Schema
------

Each backend has a JSON-Schema, all the backends have a schema which is derived
from the same parent schema, defined in ``netjsonconfig.backends.schema``
(`view source <https://github.com/openwisp/netjsonconfig/blob/master/netjsonconfig/schema.py>`_).

Since different backends may support different features each backend may extend its
schema by adding custom definitions.

.. _validation:

Validation
----------

All the backends have a ``validate`` method which is called automatically before
trying to process the configuration.

If the passed configuration violates the schema the ``validate`` method will raise
a ``ValidationError``.

An instance of validation error has two public attributes:

* ``message``: a human readable message explaining the error
* ``details``: a reference to the instance of ``jsonschema.exceptions.ValidationError``
  which contains more details about what has gone wrong;
  for a complete reference see the `python-jsonschema documentation <https://python-jsonschema.readthedocs.org/en/latest/errors/#handling-validation-errors>`_

You may call the ``validate`` method in your application arbitrarily, eg: before
trying to save the *configuration dictionary* into a database.

.. _template:

Template
--------

If you have devices with very similar *configuration dictionaries* you can store the shared
blocks in one or more reusable templates which will be used as a base to build
the final configuration.

.. note::
    When using multiple templates, their order is important.
    Templates that come afterwards overrides the templates that come before it.

Let's illustrate this with a practical example, we have two devices:

 * Router1
 * Router2

Both devices have an ``eth0`` interface in DHCP mode; *Router2* additionally has
{'fraction_non_alphanumeric': 0.10725806451612903, 'fraction_numerical': 0.01032258064516129, 'mean_word_length': 2.619673088149445, 'pattern_counts': {'":': 58, '<': 29, '<?xml version=': 0, '>': 29, 'https://': 8, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 6, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1338265', 'n_tokens_mistral': 3265, 'n_tokens_neox': 3068, 'n_words': 1174}