Migrate controller docs to the controller project 71/75871/1
authorThanh Ha <thanh.ha@linuxfoundation.org>
Sun, 9 Sep 2018 00:48:24 +0000 (20:48 -0400)
committerThanh Ha <thanh.ha@linuxfoundation.org>
Sun, 9 Sep 2018 00:48:24 +0000 (20:48 -0400)
Change-Id: Ic6ad9bade1240b08cc112f275ea2d1b4516d5446
Signed-off-by: Thanh Ha <thanh.ha@linuxfoundation.org>
docs/dev-guide.rst [new file with mode: 0644]
docs/index.rst

diff --git a/docs/dev-guide.rst b/docs/dev-guide.rst
new file mode 100644 (file)
index 0000000..58e3b0d
--- /dev/null
@@ -0,0 +1,1907 @@
+.. _controller-dev-guide:
+
+Controller
+==========
+
+Overview
+--------
+
+OpenDaylight Controller is Java-based, model-driven controller using
+YANG as its modeling language for various aspects of the system and
+applications and with its components serves as a base platform for other
+OpenDaylight applications.
+
+The OpenDaylight Controller relies on the following technologies:
+
+-  **OSGI** - This framework is the back-end of OpenDaylight as it
+   allows dynamically loading of bundles and packages JAR files, and
+   binding bundles together for exchanging information.
+
+-  **Karaf** - Application container built on top of OSGI, which
+   simplifies operational aspects of packaging and installing
+   applications.
+
+-  **YANG** - a data modeling language used to model configuration and
+   state data manipulated by the applications, remote procedure calls,
+   and notifications.
+
+The OpenDaylight Controller provides following model-driven subsystems
+as a foundation for Java applications:
+
+-  :ref:`config_subsystem` - an activation,
+   dependency-injection and configuration framework, which allows
+   two-phase commits of configuration and dependency-injection, and
+   allows for run-time rewiring.
+
+-  :ref:`MD-SAL <mdsal_dev_guide>` - messaging and data storage
+   functionality for data, notifications and RPCs modeled by application
+   developers. MD-SAL uses YANG as the modeling for both interface and
+   data definitions, and provides a messaging and data-centric runtime
+   for such services based on YANG modeling.
+
+-  **MD-SAL Clustering** - enables cluster support for core MD-SAL
+   functionality and provides location-transparent accesss to
+   YANG-modeled data.
+
+The OpenDaylight Controller supports external access to applications and
+data using following model-driven protocols:
+
+-  **NETCONF** - XML-based RPC protocol, which provides abilities for
+   client to invoke YANG-modeled RPCs, receive notifications and to
+   read, modify and manipulate YANG modeled data.
+
+-  **RESTCONF** - HTTP-based protocol, which provides REST-like APIs to
+   manipulate YANG modeled data and invoke YANG modeled RPCs, using XML
+   or JSON as payload format.
+
+.. _mdsal_dev_guide:
+
+MD-SAL Overview
+---------------
+
+The Model-Driven Service Adaptation Layer (MD-SAL) is message-bus
+inspired extensible middleware component that provides messaging and
+data storage functionality based on data and interface models defined by
+application developers (i.e. user-defined models).
+
+The MD-SAL:
+
+-  Defines a **common-layer, concepts, data model building blocks and
+   messaging patterns** and provides infrastructure / framework for
+   applications and inter-application communication.
+
+-  Provide common support for user-defined transport and payload
+   formats, including payload serialization and adaptation (e.g. binary,
+   XML or JSON).
+
+The MD-SAL uses **YANG** as the modeling language for both interface and
+data definitions, and provides a messaging and data-centric runtime for
+such services based on YANG modeling.
+
+| The MD-SAL provides two different API types (flavours):
+
+-  **MD-SAL Binding:** MD-SAL APIs which extensively uses APIs and
+   classes generated from YANG models, which provides compile-time
+   safety.
+
+-  **MD-SAL DOM:** (Document Object Model) APIs which uses DOM-like
+   representation of data, which makes them more powerful, but provides
+   less compile-time safety.
+
+.. note::
+
+    Model-driven nature of the MD-SAL and **DOM**-based APIs allows for
+    behind-the-scene API and payload type mediation and transformation
+    to facilitate seamless communication between applications - this
+    enables for other components and applications to provide connectors
+    / expose different set of APIs and derive most of its functionality
+    purely from models, which all existing code can benefit from without
+    modification. For example **RESTCONF Connector** is an application
+    built on top of MD-SAL and exposes YANG-modeled application APIs
+    transparently via HTTP and adds support for XML and JSON payload
+    type.
+
+Basic concepts
+~~~~~~~~~~~~~~
+
+Basic concepts are building blocks which are used by applications, and
+from which MD-SAL uses to define messaging patterns and to provide
+services and behavior based on developer-supplied YANG models.
+
+Data Tree
+    All state-related data are modeled and represented as data tree,
+    with possibility to address any element / subtree
+
+    -  **Operational Data Tree** - Reported state of the system,
+       published by the providers using MD-SAL. Represents a feedback
+       loop for applications to observe state of the network / system.
+
+    -  **Configuration Data Tree** - Intended state of the system or
+       network, populated by consumers, which expresses their intention.
+
+Instance Identifier
+    Unique identifier of node / subtree in data tree, which provides
+    unambiguous information, how to reference and retrieve node /
+    subtree from conceptual data trees.
+
+Notification
+    Asynchronous transient event which may be consumed by subscribers
+    and they may act upon it
+
+RPC
+    asynchronous request-reply message pair, when request is triggered
+    by consumer, send to the provider, which in future replies with
+    reply message.
+
+    .. note::
+
+        In MD-SAL terminology, the term *RPC* is used to define the
+        input and output for a procedure (function) that is to be
+        provided by a provider, and mediated by the MD-SAL, that means
+        it may not result in remote call.
+
+Messaging Patterns
+~~~~~~~~~~~~~~~~~~
+
+MD-SAL provides several messaging patterns using broker derived from
+basic concepts, which are intended to transfer YANG modeled data between
+applications to provide data-centric integration between applications
+instead of API-centric integration.
+
+-  **Unicast communication**
+
+   -  **Remote Procedure Calls** - unicast between consumer and
+      provider, where consumer sends **request** message to provider,
+      which asynchronously responds with **reply** message
+
+-  **Publish / Subscribe**
+
+   -  **Notifications** - multicast transient message which is published
+      by provider and is delivered to subscribers
+
+   -  **Data Change Events** - multicast asynchronous event, which is
+      sent by data broker if there is change in conceptual data tree,
+      and is delivered to subscribers
+
+-  **Transactional access to Data Tree**
+
+   -  Transactional **reads** from conceptual **data tree** - read-only
+      transactions with isolation from other running transactions.
+
+   -  Transactional **modification** to conceptual **data tree** - write
+      transactions with isolation from other running transactions.
+
+   -  **Transaction chaining**
+
+MD-SAL Data Transactions
+------------------------
+
+MD-SAL **Data Broker** provides transactional access to conceptual
+**data trees** representing configuration and operational state.
+
+.. note::
+
+    **Data tree** usually represents state of the modeled data, usually
+    this is state of controller, applications and also external systems
+    (network devices).
+
+**Transactions** provide :ref:`stable and isolated
+view <transaction_isolation>` from other currently running
+transactions. The state of running transaction and underlying data tree
+is not affected by other concurrently running transactions.
+
+Write-Only
+    Transaction provides only modification capabilities, but does not
+    provide read capabilities. Write-only transaction is allocated using
+    ``newWriteOnlyTransaction()``.
+
+    .. note::
+
+        This allows less state tracking for write-only transactions and
+        allows MD-SAL Clustering to optimize internal representation of
+        transaction in cluster.
+
+Read-Write
+    Transaction provides both read and write capabilities. It is
+    allocated using ``newReadWriteTransaction()``.
+
+Read-Only
+    Transaction provides stable read-only view based on current data
+    tree. Read-only view is not affected by any subsequent write
+    transactions. Read-only transaction is allocated using
+    ``newReadOnlyTransaction()``.
+
+    .. note::
+
+        If an application needs to observe changes itself in data tree,
+        it should use **data tree listeners** instead of read-only
+        transactions and polling data tree.
+
+Transactions may be allocated using the **data broker** itself or using
+**transaction chain**. In the case of **transaction chain**, the new
+allocated transaction is not based on current state of data tree, but
+rather on state introduced by previous transaction from the same chain,
+even if the commit for previous transaction has not yet occurred (but
+transaction was submitted).
+
+Write-Only & Read-Write Transaction
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Write-Only and Read-Write transactions provide modification capabilities
+for the conceptual data trees.
+
+1. application allocates new transactions using
+   ``newWriteOnlyTransaction()`` or ``newReadWriteTransaction()``.
+
+2. application `modifies data tree <#_modification_of_data_tree>`__
+   using ``put``, ``merge`` and/or ``delete``.
+
+3. application finishes transaction using
+   ``submit()``, which :ref:`seals transaction
+   and submits <submitting_transaction>` it to be processed.
+
+4. application observes the result of the transaction commit using
+   either blocking or asynchronous calls.
+
+The **initial state** of the write transaction is a **stable snapshot**
+of the current data tree state captured when transaction was created and
+it’s state and underlying data tree are not affected by other
+concurrently running transactions.
+
+Write transactions are **isolated** from other concurrent write
+transactions. All :ref:`writes are local <transaction_local_state>`
+to the transaction and represents only a **proposal of state change**
+for data tree and **are not visible** to any other concurrently running
+transactions (including read-only transactions).
+
+The transaction :ref:`commit may fail <commit_failure_scenarios>` due
+to failing verification of data or concurrent transaction modifying and
+affected data in an incompatible way.
+
+Modification of Data Tree
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Write-only and read-write transaction provides following methods to
+modify data tree:
+
+put
+    .. code:: java
+
+        <T> void put(LogicalDatastoreType store, InstanceIdentifier<T> path, T data);
+
+    Stores a piece of data at a specified path. This acts as an **add /
+    replace** operation, which is to say that whole subtree will be
+    replaced by the specified data.
+
+merge
+    .. code:: java
+
+        <T> void merge(LogicalDatastoreType store, InstanceIdentifier<T> path, T data);
+
+    Merges a piece of data with the existing data at a specified path.
+    Any **pre-existing data** which are not explicitly overwritten
+    **will be preserved**. This means that if you store a container, its
+    child subtrees will be merged.
+
+delete
+    .. code:: java
+
+        void delete(LogicalDatastoreType store, InstanceIdentifier<?> path);
+
+    Removes a whole subtree from a specified path.
+
+.. _submitting_transaction:
+
+Submitting transaction
+^^^^^^^^^^^^^^^^^^^^^^
+
+Transaction is submitted to be processed and committed using following
+method:
+
+.. code:: java
+
+    CheckedFuture<Void,TransactionCommitFailedException> submit();
+
+Applications publish the changes proposed in the transaction by calling
+``submit()`` on the transaction. This **seals the transaction**
+(preventing any further writes using this transaction) and submits it to
+be processed and applied to global conceptual data tree. The
+``submit()`` method does not block, but rather returns
+``ListenableFuture``, which will complete successfully once processing
+of transaction is finished and changes are applied to data tree. If
+**commit** of data failed, the future will fail with
+``TransactionFailedException``.
+
+Application may listen on commit state asynchronously using
+``ListenableFuture``.
+
+.. code:: java
+
+    Futures.addCallback( writeTx.submit(), new FutureCallback<Void>() {
+            public void onSuccess( Void result ) {
+                LOG.debug("Transaction committed successfully.");
+            }
+
+            public void onFailure( Throwable t ) {
+                LOG.error("Commit failed.",e);
+            }
+        });
+
+-  Submits ``writeTx`` and registers application provided
+   ``FutureCallback`` on returned future.
+
+-  Invoked when future completed successfully - transaction ``writeTx``
+   was successfully committed to data tree.
+
+-  Invoked when future failed - commit of transaction ``writeTx``
+   failed. Supplied exception provides additional details and cause of
+   failure.
+
+If application need to block till commit is finished it may use
+``checkedGet()`` to wait till commit is finished.
+
+.. code:: java
+
+    try {
+        writeTx.submit().checkedGet();
+    } catch (TransactionCommitFailedException e) {
+        LOG.error("Commit failed.",e);
+    }
+
+-  Submits ``writeTx`` and blocks till commit of ``writeTx`` is
+   finished. If commit fails ``TransactionCommitFailedException`` will
+   be thrown.
+
+-  Catches ``TransactionCommitFailedException`` and logs it.
+
+.. _transaction_local_state:
+
+Transaction local state
+^^^^^^^^^^^^^^^^^^^^^^^
+
+Read-Write transactions maintain transaction-local state, which renders
+all modifications as if they happened, but this is only local to
+transaction.
+
+Reads from the transaction returns data as if the previous modifications
+in transaction already happened.
+
+Let assume initial state of data tree for ``PATH`` is ``A``.
+
+.. code:: java
+
+    ReadWriteTransaction rwTx = broker.newReadWriteTransaction();
+
+    rwRx.read(OPERATIONAL,PATH).get();
+    rwRx.put(OPERATIONAL,PATH,B);
+    rwRx.read(OPERATIONAL,PATH).get();
+    rwRx.put(OPERATIONAL,PATH,C);
+    rwRx.read(OPERATIONAL,PATH).get();
+
+-  Allocates new ``ReadWriteTransaction``.
+
+-  Read from ``rwTx`` will return value ``A`` for ``PATH``.
+
+-  Writes value ``B`` to ``PATH`` using ``rwTx``.
+
+-  Read will return value ``B`` for ``PATH``, since previous write
+   occurred in same transaction.
+
+-  Writes value ``C`` to ``PATH`` using ``rwTx``.
+
+-  Read will return value ``C`` for ``PATH``, since previous write
+   occurred in same transaction.
+
+.. _transaction_isolation:
+
+Transaction isolation
+~~~~~~~~~~~~~~~~~~~~~
+
+Running (not submitted) transactions are isolated from each other and
+changes done in one transaction are not observable in other currently
+running transaction.
+
+Lets assume initial state of data tree for ``PATH`` is ``A``.
+
+.. code:: java
+
+    ReadOnlyTransaction txRead = broker.newReadOnlyTransaction();
+    ReadWriteTransaction txWrite = broker.newReadWriteTransaction();
+
+    txRead.read(OPERATIONAL,PATH).get();
+    txWrite.put(OPERATIONAL,PATH,B);
+    txWrite.read(OPERATIONAL,PATH).get();
+    txWrite.submit().get();
+    txRead.read(OPERATIONAL,PATH).get();
+    txAfterCommit = broker.newReadOnlyTransaction();
+    txAfterCommit.read(OPERATIONAL,PATH).get();
+
+-  Allocates read only transaction, which is based on data tree which
+   contains value ``A`` for ``PATH``.
+
+-  Allocates read write transaction, which is based on data tree which
+   contains value ``A`` for ``PATH``.
+
+-  Read from read-only transaction returns value ``A`` for ``PATH``.
+
+-  Data tree is updated using read-write transaction, ``PATH`` contains
+   ``B``. Change is not public and only local to transaction.
+
+-  Read from read-write transaction returns value ``B`` for ``PATH``.
+
+-  Submits changes in read-write transaction to be committed to data
+   tree. Once commit will finish, changes will be published and ``PATH``
+   will be updated for value ``B``. Previously allocated transactions
+   are not affected by this change.
+
+-  Read from previously allocated read-only transaction still returns
+   value ``A`` for ``PATH``, since it provides stable and isolated view.
+
+-  Allocates new read-only transaction, which is based on data tree,
+   which contains value ``B`` for ``PATH``.
+
+-  Read from new read-only transaction return value ``B`` for ``PATH``
+   since read-write transaction was committed.
+
+.. note::
+
+    Examples contain blocking calls on future only to illustrate that
+    action happened after other asynchronous action. The use of the
+    blocking call ``ListenableFuture#get()`` is discouraged for most
+    use-cases and you should use
+    ``Futures#addCallback(ListenableFuture, FutureCallback)`` to listen
+    asynchronously for result.
+
+.. _commit_failure_scenarios:
+
+Commit failure scenarios
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+A transaction commit may fail because of following reasons:
+
+Optimistic Lock Failure
+    Another transaction finished earlier and **modified the same node in
+    a non-compatible way**. The commit (and the returned future) will
+    fail with an ``OptimisticLockFailedException``.
+
+    It is the responsibility of the caller to create a new transaction
+    and submit the same modification again in order to update data tree.
+
+    .. warning::
+
+        ``OptimisticLockFailedException`` usually exposes **multiple
+        writers** to the same data subtree, which may conflict on same
+        resources.
+
+        In most cases, retrying may result in a probability of success.
+
+        There are scenarios, albeit unusual, where any number of retries
+        will not succeed. Therefore it is strongly recommended to limit
+        the number of retries (2 or 3) to avoid an endless loop.
+
+Data Validation
+    The data change introduced by this transaction **did not pass
+    validation** by commit handlers or data was incorrectly structured.
+    The returned future will fail with a
+    ``DataValidationFailedException``. User **should not retry** to
+    create new transaction with same data, since it probably will fail
+    again.
+
+Example conflict of two transactions
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This example illustrates two concurrent transactions, which derived from
+same initial state of data tree and proposes conflicting modifications.
+
+.. code:: java
+
+    WriteTransaction txA = broker.newWriteTransaction();
+    WriteTransaction txB = broker.newWriteTransaction();
+
+    txA.put(CONFIGURATION, PATH, A);
+    txB.put(CONFIGURATION, PATH, B);
+
+    CheckedFuture<?,?> futureA = txA.submit();
+    CheckedFuture<?,?> futureB = txB.submit();
+
+-  Updates ``PATH`` to value ``A`` using ``txA``
+
+-  Updates ``PATH`` to value ``B`` using ``txB``
+
+-  Seals & submits ``txA``. The commit will be processed asynchronously
+   and data tree will be updated to contain value ``A`` for ``PATH``.
+   The returned ‘ListenableFuture’ will complete successfully once state
+   is applied to data tree.
+
+-  Seals & submits ``txB``. Commit of ``txB`` will fail, because
+   previous transaction also modified path in a concurrent way. The
+   state introduced by ``txB`` will not be applied. The returned
+   ``ListenableFuture`` will fail with ``OptimisticLockFailedException``
+   exception, which indicates that concurrent transaction prevented the
+   submitted transaction from being applied.
+
+Example asynchronous retry-loop
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code:: java
+
+    private void doWrite( final int tries ) {
+        WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction();
+
+        MyDataObject data = ...;
+        InstanceIdentifier<MyDataObject> path = ...;
+        writeTx.put( LogicalDatastoreType.OPERATIONAL, path, data );
+
+        Futures.addCallback( writeTx.submit(), new FutureCallback<Void>() {
+            public void onSuccess( Void result ) {
+                // succeeded
+            }
+
+            public void onFailure( Throwable t ) {
+                if( t instanceof OptimisticLockFailedException && (( tries - 1 ) > 0)) {
+                    doWrite( tries - 1 );
+                }
+            }
+          });
+    }
+    ...
+    doWrite( 2 );
+
+Concurrent change compatibility
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are several sets of changes which could be considered incompatible
+between two transactions which are derived from same initial state.
+Rules for conflict detection applies recursively for each subtree level.
+
+Following table shows state changes and failures between two concurrent
+transactions, which are based on same initial state, ``tx1`` is
+submitted before ``tx2``.
+
+INFO: Following tables stores numeric values and shows data using
+``toString()`` to simplify examples.
+
++--------------------+--------------------+--------------------+--------------------+
+| Initial state      | tx1                | tx2                | Observable Result  |
++====================+====================+====================+====================+
+| Empty              | ``put(A,1)``       | ``put(A,2)``       | ``tx2`` will fail, |
+|                    |                    |                    | value of ``A`` is  |
+|                    |                    |                    | ``1``              |
++--------------------+--------------------+--------------------+--------------------+
+| Empty              | ``put(A,1)``       | ``merge(A,2)``     | value of ``A`` is  |
+|                    |                    |                    | ``2``              |
++--------------------+--------------------+--------------------+--------------------+
+| Empty              | ``merge(A,1)``     | ``put(A,2)``       | ``tx2`` will fail, |
+|                    |                    |                    | value of ``A`` is  |
+|                    |                    |                    | ``1``              |
++--------------------+--------------------+--------------------+--------------------+
+| Empty              | ``merge(A,1)``     | ``merge(A,2)``     | ``A`` is ``2``     |
++--------------------+--------------------+--------------------+--------------------+
+| A=0                | ``put(A,1)``       | ``put(A,2)``       | ``tx2`` will fail, |
+|                    |                    |                    | ``A`` is ``1``     |
++--------------------+--------------------+--------------------+--------------------+
+| A=0                | ``put(A,1)``       | ``merge(A,2)``     | ``A`` is ``2``     |
++--------------------+--------------------+--------------------+--------------------+
+| A=0                | ``merge(A,1)``     | ``put(A,2)``       | ``tx2`` will fail, |
+|                    |                    |                    | value of ``A`` is  |
+|                    |                    |                    | ``1``              |
++--------------------+--------------------+--------------------+--------------------+
+| A=0                | ``merge(A,1)``     | ``merge(A,2)``     | ``A`` is ``2``     |
++--------------------+--------------------+--------------------+--------------------+
+| A=0                | ``delete(A)``      | ``put(A,2)``       | ``tx2`` will fail, |
+|                    |                    |                    | ``A`` does not     |
+|                    |                    |                    | exists             |
++--------------------+--------------------+--------------------+--------------------+
+| A=0                | ``delete(A)``      | ``merge(A,2)``     | ``A`` is ``2``     |
++--------------------+--------------------+--------------------+--------------------+
+
+Table: Concurrent change resolution for leaves and leaf-list items
+
++--------------------+--------------------+--------------------+--------------------+
+| Initial state      | ``tx1``            | ``tx2``            | Result             |
++====================+====================+====================+====================+
+| Empty              | put(TOP,[])        | put(TOP,[])        | ``tx2`` will fail, |
+|                    |                    |                    | state is TOP=[]    |
++--------------------+--------------------+--------------------+--------------------+
+| Empty              | put(TOP,[])        | merge(TOP,[])      | TOP=[]             |
++--------------------+--------------------+--------------------+--------------------+
+| Empty              | put(TOP,[FOO=1])   | put(TOP,[BAR=1])   | ``tx2`` will fail, |
+|                    |                    |                    | state is           |
+|                    |                    |                    | TOP=[FOO=1]        |
++--------------------+--------------------+--------------------+--------------------+
+| Empty              | put(TOP,[FOO=1])   | merge(TOP,[BAR=1]) | TOP=[FOO=1,BAR=1]  |
++--------------------+--------------------+--------------------+--------------------+
+| Empty              | merge(TOP,[FOO=1]) | put(TOP,[BAR=1])   | ``tx2`` will fail, |
+|                    |                    |                    | state is           |
+|                    |                    |                    | TOP=[FOO=1]        |
++--------------------+--------------------+--------------------+--------------------+
+| Empty              | merge(TOP,[FOO=1]) | merge(TOP,[BAR=1]) | TOP=[FOO=1,BAR=1]  |
++--------------------+--------------------+--------------------+--------------------+
+| TOP=[]             | put(TOP,[FOO=1])   | put(TOP,[BAR=1])   | ``tx2`` will fail, |
+|                    |                    |                    | state is           |
+|                    |                    |                    | TOP=[FOO=1]        |
++--------------------+--------------------+--------------------+--------------------+
+| TOP=[]             | put(TOP,[FOO=1])   | merge(TOP,[BAR=1]) | state is           |
+|                    |                    |                    | TOP=[FOO=1,BAR=1]  |
++--------------------+--------------------+--------------------+--------------------+
+| TOP=[]             | merge(TOP,[FOO=1]) | put(TOP,[BAR=1])   | ``tx2`` will fail, |
+|                    |                    |                    | state is           |
+|                    |                    |                    | TOP=[FOO=1]        |
++--------------------+--------------------+--------------------+--------------------+
+| TOP=[]             | merge(TOP,[FOO=1]) | merge(TOP,[BAR=1]) | state is           |
+|                    |                    |                    | TOP=[FOO=1,BAR=1]  |
++--------------------+--------------------+--------------------+--------------------+
+| TOP=[]             | delete(TOP)        | put(TOP,[BAR=1])   | ``tx2`` will fail, |
+|                    |                    |                    | state is empty     |
+|                    |                    |                    | store              |
++--------------------+--------------------+--------------------+--------------------+
+| TOP=[]             | delete(TOP)        | merge(TOP,[BAR=1]) | state is           |
+|                    |                    |                    | TOP=[BAR=1]        |
++--------------------+--------------------+--------------------+--------------------+
+| TOP=[]             | put(TOP/FOO,1)     | put(TOP/BAR,1])    | state is           |
+|                    |                    |                    | TOP=[FOO=1,BAR=1]  |
++--------------------+--------------------+--------------------+--------------------+
+| TOP=[]             | put(TOP/FOO,1)     | merge(TOP/BAR,1)   | state is           |
+|                    |                    |                    | TOP=[FOO=1,BAR=1]  |
++--------------------+--------------------+--------------------+--------------------+
+| TOP=[]             | merge(TOP/FOO,1)   | put(TOP/BAR,1)     | state is           |
+|                    |                    |                    | TOP=[FOO=1,BAR=1]  |
++--------------------+--------------------+--------------------+--------------------+
+| TOP=[]             | merge(TOP/FOO,1)   | merge(TOP/BAR,1)   | state is           |
+|                    |                    |                    | TOP=[FOO=1,BAR=1]  |
++--------------------+--------------------+--------------------+--------------------+
+| TOP=[]             | delete(TOP)        | put(TOP/BAR,1)     | ``tx2`` will fail, |
+|                    |                    |                    | state is empty     |
+|                    |                    |                    | store              |
++--------------------+--------------------+--------------------+--------------------+
+| TOP=[]             | delete(TOP)        | merge(TOP/BAR,1]   | ``tx2`` will fail, |
+|                    |                    |                    | state is empty     |
+|                    |                    |                    | store              |
++--------------------+--------------------+--------------------+--------------------+
+| TOP=[FOO=1]        | put(TOP/FOO,2)     | put(TOP/BAR,1)     | state is           |
+|                    |                    |                    | TOP=[FOO=2,BAR=1]  |
++--------------------+--------------------+--------------------+--------------------+
+| TOP=[FOO=1]        | put(TOP/FOO,2)     | merge(TOP/BAR,1)   | state is           |
+|                    |                    |                    | TOP=[FOO=2,BAR=1]  |
++--------------------+--------------------+--------------------+--------------------+
+| TOP=[FOO=1]        | merge(TOP/FOO,2)   | put(TOP/BAR,1)     | state is           |
+|                    |                    |                    | TOP=[FOO=2,BAR=1]  |
++--------------------+--------------------+--------------------+--------------------+
+| TOP=[FOO=1]        | merge(TOP/FOO,2)   | merge(TOP/BAR,1)   | state is           |
+|                    |                    |                    | TOP=[FOO=2,BAR=1]  |
++--------------------+--------------------+--------------------+--------------------+
+| TOP=[FOO=1]        | delete(TOP/FOO)    | put(TOP/BAR,1)     | state is           |
+|                    |                    |                    | TOP=[BAR=1]        |
++--------------------+--------------------+--------------------+--------------------+
+| TOP=[FOO=1]        | delete(TOP/FOO)    | merge(TOP/BAR,1]   | state is           |
+|                    |                    |                    | TOP=[BAR=1]        |
++--------------------+--------------------+--------------------+--------------------+
+
+Table: Concurrent change resolution for containers, lists, list items
+
+MD-SAL RPC routing
+------------------
+
+The MD-SAL provides a way to deliver Remote Procedure Calls (RPCs) to a
+particular implementation based on content in the input as it is modeled
+in YANG. This part of the RPC input is referred to as a **context
+reference**.
+
+The MD-SAL does not dictate the name of the leaf which is used for this
+RPC routing, but provides necessary functionality for YANG model author
+to define their **context reference** in their model of RPCs.
+
+MD-SAL routing behavior is modeled using following terminology and its
+application to YANG models:
+
+Context Type
+    Logical type of RPC routing. Context type is modeled as YANG
+    ``identity`` and is referenced in model to provide scoping
+    information.
+
+Context Instance
+    Conceptual location in data tree, which represents context in which
+    RPC could be executed. Context instance usually represent logical
+    point to which RPC execution is attached.
+
+Context Reference
+    Field of RPC input payload which contains Instance Identifier
+    referencing **context instance** in which the RPC should be
+    executed.
+
+Modeling a routed RPC
+~~~~~~~~~~~~~~~~~~~~~
+
+In order to define routed RPCs, the YANG model author needs to declare
+(or reuse) a **context type**, set of possible **context instances** and
+finally RPCs which will contain **context reference** on which they will
+be routed.
+
+Declaring a routing context type
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code::
+
+    identity node-context {
+        description "Identity used to mark node context";
+    }
+
+This declares an identity named ``node-context``, which is used as
+marker for node-based routing and is used in other places to reference
+that routing type.
+
+Declaring possible context instances
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In order to define possible values of **context instances** for routed
+RPCs, we need to model that set accordingly using ``context-instance``
+extension from the ``yang-ext`` model.
+
+.. code::
+
+    import yang-ext { prefix ext; }
+
+    /** Base structure **/
+    container nodes {
+        list node {
+            key "id";
+            ext:context-instance "node-context";
+            // other node-related fields would go here
+        }
+    }
+
+The statement ``ext:context-instance "node-context";`` marks any element
+of the ``list node`` as a possible valid **context instance** in
+``node-context`` based routing.
+
+.. note::
+
+    The existence of a **context instance** node in operational or
+    config data tree is not strongly tied to existence of RPC
+    implementation.
+
+    For most routed RPC models, there is relationship between the data
+    present in operational data tree and RPC implementation
+    availability, but this is not enforced by MD-SAL. This provides some
+    flexibility for YANG model writers to better specify their routing
+    model and requirements for implementations. Details when RPC
+    implementations are available should be documented in YANG model.
+
+    If user invokes RPC with a **context instance** that has no
+    registered implementation, the RPC invocation will fail with the
+    exception ``DOMRpcImplementationNotAvailableException``.
+
+Declaring a routed RPC
+^^^^^^^^^^^^^^^^^^^^^^
+
+To declare RPC to be routed based on ``node-context`` we need to add
+leaf of ``instance-identifier`` type (or type derived from
+``instance-identifier``) to the RPC and mark it as **context
+reference**.
+
+This is achieved using YANG extension ``context-reference`` from
+``yang-ext`` model on leaf, which will be used for RPC routing.
+
+.. code::
+
+    rpc example-routed-rpc  {
+        input {
+            leaf node {
+                ext:context-reference "node-context";
+                type "instance-identifier";
+            }
+            // other input to the RPC would go here
+        }
+    }
+
+The statement ``ext:context-reference "node-context"`` marks
+``leaf node`` as **context reference** of type ``node-context``. The
+value of this leaf, will be used by the MD-SAL to select the particular
+RPC implementation that registered itself as the implementation of the
+RPC for particular **context instance**.
+
+Using routed RPCs
+~~~~~~~~~~~~~~~~~
+
+From a user perspective (e.g. invoking RPCs) there is no difference
+between routed and non-routed RPCs. Routing information is just an
+additional leaf in RPC which must be populated.
+
+Implementing a routed RPC
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Implementation
+
+Registering implementations
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Implementations of a routed RPC (e.g., southbound plugins) will specify
+an instance-identifier for the **context reference** (in this case a
+node) for which they want to provide an implementation during
+registration. Consumers, e.g., those calling the RPC are required to
+specify that instance-identifier (in this case the identifier of a node)
+when invoking RPC.
+
+Simple code which showcases that for add-flow via Binding-Aware APIs
+(`RoutedServiceTest.java <https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blob;f=opendaylight/md-sal/sal-binding-it/src/test/java/org/opendaylight/controller/test/sal/binding/it/RoutedServiceTest.java;h=d49d6f0e25e271e43c8550feb5eef63d96301184;hb=HEAD>`__
+):
+
+.. code:: java
+
+     61  @Override
+     62  public void onSessionInitiated(ProviderContext session) {
+     63      assertNotNull(session);
+     64      firstReg = session.addRoutedRpcImplementation(SalFlowService.class, salFlowService1);
+     65  }
+
+Line 64: We are registering salFlowService1 as implementation of
+SalFlowService RPC
+
+.. code:: java
+
+    107  NodeRef nodeOne = createNodeRef("foo:node:1");
+    109  /**
+    110   * Provider 1 registers path of node 1
+    111   */
+    112  firstReg.registerPath(NodeContext.class, nodeOne);
+
+Line 107: We are creating NodeRef (encapsulation of InstanceIdentifier)
+for "foo:node:1".
+
+Line 112: We register salFlowService1 as implementation for nodeOne.
+
+The salFlowService1 will be executed only for RPCs which contains
+Instance Identifier for foo:node:1.
+
+RPCs and cluster
+^^^^^^^^^^^^^^^^
+
+In case there is is only a single provider of an RPC in the cluster
+the RPC registration is propagated to other nodes via Gossip protocol
+and the RPC calls from other nodes are correctly routed to the
+provider. Since the registrations are not expected to change rapidly
+there is a latency of about 1 second until the registration is reflected
+on the remote nodes.
+
+
+OpenDaylight Controller MD-SAL: RESTCONF
+----------------------------------------
+
+RESTCONF operations overview
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+| RESTCONF allows access to datastores in the controller.
+| There are two datastores:
+
+-  Config: Contains data inserted via controller
+
+-  Operational: Contains other data
+
+.. note::
+
+    | Each request must start with the URI /restconf.
+    | RESTCONF listens on port 8080 for HTTP requests.
+
+RESTCONF supports **OPTIONS**, **GET**, **PUT**, **POST**, and
+**DELETE** operations. Request and response data can either be in the
+XML or JSON format. XML structures according to yang are defined at:
+`XML-YANG <http://tools.ietf.org/html/rfc6020>`__. JSON structures are
+defined at:
+`JSON-YANG <http://tools.ietf.org/html/draft-lhotka-netmod-yang-json-02>`__.
+Data in the request must have a correctly set **Content-Type** field in
+the http header with the allowed value of the media type. The media type
+of the requested data has to be set in the **Accept** field. Get the
+media types for each resource by calling the OPTIONS operation. Most of
+the paths of the pathsRestconf endpoints use `Instance
+Identifier <https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Concepts#Instance_Identifier>`__.
+``<identifier>`` is used in the explanation of the operations.
+
+| **<identifier>**
+
+-  It must start with <moduleName>:<nodeName> where <moduleName> is a
+   name of the module and <nodeName> is the name of a node in the
+   module. It is sufficient to just use <nodeName> after
+   <moduleName>:<nodeName>. Each <nodeName> has to be separated by /.
+
+-  <nodeName> can represent a data node which is a list or container
+   yang built-in type. If the data node is a list, there must be defined
+   keys of the list behind the data node name for example,
+   <nodeName>/<valueOfKey1>/<valueOfKey2>.
+
+-  | The format <moduleName>:<nodeName> has to be used in this case as
+     well:
+   | Module A has node A1. Module B augments node A1 by adding node X.
+     Module C augments node A1 by adding node X. For clarity, it has to
+     be known which node is X (for example: C:X). For more details about
+     encoding, see: `RESTCONF 02 - Encoding YANG Instance Identifiers in
+     the Request
+     URI. <http://tools.ietf.org/html/draft-bierman-netconf-restconf-02#section-5.3.1>`__
+
+Mount point
+~~~~~~~~~~~
+
+| A Node can be behind a mount point. In this case, the URI has to be in
+  format <identifier>/**yang-ext:mount**/<identifier>. The first
+  <identifier> is the path to a mount point and the second <identifier>
+  is the path to a node behind the mount point. A URI can end in a mount
+  point itself by using <identifier>/**yang-ext:mount**.
+| More information on how to actually use mountpoints is available at:
+  `OpenDaylight
+  Controller:Config:Examples:Netconf <https://wiki.opendaylight.org/view/OpenDaylight_Controller:Config:Examples:Netconf>`__.
+
+HTTP methods
+~~~~~~~~~~~~
+
+OPTIONS /restconf
+^^^^^^^^^^^^^^^^^
+
+-  Returns the XML description of the resources with the required
+   request and response media types in Web Application Description
+   Language (WADL)
+
+GET /restconf/config/<identifier>
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+-  Returns a data node from the Config datastore.
+
+-  <identifier> points to a data node which must be retrieved.
+
+GET /restconf/operational/<identifier>
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+-  Returns the value of the data node from the Operational datastore.
+
+-  <identifier> points to a data node which must be retrieved.
+
+PUT /restconf/config/<identifier>
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+-  Updates or creates data in the Config datastore and returns the state
+   about success.
+
+-  <identifier> points to a data node which must be stored.
+
+| **Example:**
+
+::
+
+    PUT http://<controllerIP>:8080/restconf/config/module1:foo/bar
+    Content-Type: applicaton/xml
+    <bar>
+      …
+    </bar>
+
+| **Example with mount point:**
+
+::
+
+    PUT http://<controllerIP>:8080/restconf/config/module1:foo1/foo2/yang-ext:mount/module2:foo/bar
+    Content-Type: applicaton/xml
+    <bar>
+      …
+    </bar>
+
+POST /restconf/config
+^^^^^^^^^^^^^^^^^^^^^
+
+-  Creates the data if it does not exist
+
+| For example:
+
+::
+
+    POST URL: http://localhost:8080/restconf/config/
+    content-type: application/yang.data+json
+    JSON payload:
+
+       {
+         "toaster:toaster" :
+         {
+           "toaster:toasterManufacturer" : "General Electric",
+           "toaster:toasterModelNumber" : "123",
+           "toaster:toasterStatus" : "up"
+         }
+      }
+
+POST /restconf/config/<identifier>
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+-  Creates the data if it does not exist in the Config datastore, and
+   returns the state about success.
+
+-  <identifier> points to a data node where data must be stored.
+
+-  The root element of data must have the namespace (data are in XML) or
+   module name (data are in JSON.)
+
+| **Example:**
+
+::
+
+    POST http://<controllerIP>:8080/restconf/config/module1:foo
+    Content-Type: applicaton/xml/
+    <bar xmlns=“module1namespace”>
+      …
+    </bar>
+
+**Example with mount point:**
+
+::
+
+    http://<controllerIP>:8080/restconf/config/module1:foo1/foo2/yang-ext:mount/module2:foo
+    Content-Type: applicaton/xml
+    <bar xmlns=“module2namespace”>
+      …
+    </bar>
+
+POST /restconf/operations/<moduleName>:<rpcName>
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+-  Invokes RPC.
+
+-  <moduleName>:<rpcName> - <moduleName> is the name of the module and
+   <rpcName> is the name of the RPC in this module.
+
+-  The Root element of the data sent to RPC must have the name “input”.
+
+-  The result can be the status code or the retrieved data having the
+   root element “output”.
+
+| **Example:**
+
+::
+
+    POST http://<controllerIP>:8080/restconf/operations/module1:fooRpc
+    Content-Type: applicaton/xml
+    Accept: applicaton/xml
+    <input>
+      …
+    </input>
+
+    The answer from the server could be:
+    <output>
+      …
+    </output>
+
+| **An example using a JSON payload:**
+
+::
+
+    POST http://localhost:8080/restconf/operations/toaster:make-toast
+    Content-Type: application/yang.data+json
+    {
+      "input" :
+      {
+         "toaster:toasterDoneness" : "10",
+         "toaster:toasterToastType":"wheat-bread"
+      }
+    }
+
+.. note::
+
+    Even though this is a default for the toasterToastType value in the
+    yang, you still need to define it.
+
+DELETE /restconf/config/<identifier>
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+-  Removes the data node in the Config datastore and returns the state
+   about success.
+
+-  <identifier> points to a data node which must be removed.
+
+More information is available in the `RESTCONF
+RFC <http://tools.ietf.org/html/draft-bierman-netconf-restconf-02>`__.
+
+How RESTCONF works
+~~~~~~~~~~~~~~~~~~
+
+| RESTCONF uses these base classes:
+
+InstanceIdentifier
+    Represents the path in the data tree
+
+ConsumerSession
+    Used for invoking RPCs
+
+DataBrokerService
+    Offers manipulation with transactions and reading data from the
+    datastores
+
+SchemaContext
+    Holds information about yang modules
+
+MountService
+    Returns MountInstance based on the InstanceIdentifier pointing to a
+    mount point
+
+MountInstace
+    Contains the SchemaContext behind the mount point
+
+DataSchemaNode
+    Provides information about the schema node
+
+SimpleNode
+    Possesses the same name as the schema node, and contains the value
+    representing the data node value
+
+CompositeNode
+    Can contain CompositeNode-s and SimpleNode-s
+
+GET in action
+~~~~~~~~~~~~~
+
+Figure 1 shows the GET operation with URI restconf/config/M:N where M is
+the module name, and N is the node name.
+
+.. figure:: ./images/Get.png
+   :alt: Get
+
+   Get
+
+1. The requested URI is translated into the InstanceIdentifier which
+   points to the data node. During this translation, the DataSchemaNode
+   that conforms to the data node is obtained. If the data node is
+   behind the mount point, the MountInstance is obtained as well.
+
+2. RESTCONF asks for the value of the data node from DataBrokerService
+   based on InstanceIdentifier.
+
+3. DataBrokerService returns CompositeNode as data.
+
+4. StructuredDataToXmlProvider or StructuredDataToJsonProvider is called
+   based on the **Accept** field from the http request. These two
+   providers can transform CompositeNode regarding DataSchemaNode to an
+   XML or JSON document.
+
+5. XML or JSON is returned as the answer on the request from the client.
+
+PUT in action
+~~~~~~~~~~~~~
+
+Figure 2 shows the PUT operation with the URI restconf/config/M:N where
+M is the module name, and N is the node name. Data is sent in the
+request either in the XML or JSON format.
+
+.. figure:: ./images/Put.png
+   :alt: Put
+
+   Put
+
+1. Input data is sent to JsonToCompositeNodeProvider or
+   XmlToCompositeNodeProvider. The correct provider is selected based on
+   the Content-Type field from the http request. These two providers can
+   transform input data to CompositeNode. However, this CompositeNode
+   does not contain enough information for transactions.
+
+2. The requested URI is translated into InstanceIdentifier which points
+   to the data node. DataSchemaNode conforming to the data node is
+   obtained during this translation. If the data node is behind the
+   mount point, the MountInstance is obtained as well.
+
+3. CompositeNode can be normalized by adding additional information from
+   DataSchemaNode.
+
+4. RESTCONF begins the transaction, and puts CompositeNode with
+   InstanceIdentifier into it. The response on the request from the
+   client is the status code which depends on the result from the
+   transaction.
+
+Something practical
+~~~~~~~~~~~~~~~~~~~
+
+1. Create a new flow on the switch openflow:1 in table 2.
+
+| **HTTP request**
+
+::
+
+    Operation: POST
+    URI: http://192.168.11.1:8080/restconf/config/opendaylight-inventory:nodes/node/openflow:1/table/2
+    Content-Type: application/xml
+
+::
+
+    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
+    <flow
+        xmlns="urn:opendaylight:flow:inventory">
+        <strict>false</strict>
+        <instructions>
+            <instruction>
+                <order>1</order>
+                <apply-actions>
+                    <action>
+                      <order>1</order>
+                        <flood-all-action/>
+                    </action>
+                </apply-actions>
+            </instruction>
+        </instructions>
+        <table_id>2</table_id>
+        <id>111</id>
+        <cookie_mask>10</cookie_mask>
+        <out_port>10</out_port>
+        <installHw>false</installHw>
+        <out_group>2</out_group>
+        <match>
+            <ethernet-match>
+                <ethernet-type>
+                    <type>2048</type>
+                </ethernet-type>
+            </ethernet-match>
+            <ipv4-destination>10.0.0.1/24</ipv4-destination>
+        </match>
+        <hard-timeout>0</hard-timeout>
+        <cookie>10</cookie>
+        <idle-timeout>0</idle-timeout>
+        <flow-name>FooXf22</flow-name>
+        <priority>2</priority>
+        <barrier>false</barrier>
+    </flow>
+
+| **HTTP response**
+
+::
+
+    Status: 204 No Content
+
+1. Change *strict* to *true* in the previous flow.
+
+| **HTTP request**
+
+::
+
+    Operation: PUT
+    URI: http://192.168.11.1:8080/restconf/config/opendaylight-inventory:nodes/node/openflow:1/table/2/flow/111
+    Content-Type: application/xml
+
+::
+
+    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
+    <flow
+        xmlns="urn:opendaylight:flow:inventory">
+        <strict>true</strict>
+        <instructions>
+            <instruction>
+                <order>1</order>
+                <apply-actions>
+                    <action>
+                      <order>1</order>
+                        <flood-all-action/>
+                    </action>
+                </apply-actions>
+            </instruction>
+        </instructions>
+        <table_id>2</table_id>
+        <id>111</id>
+        <cookie_mask>10</cookie_mask>
+        <out_port>10</out_port>
+        <installHw>false</installHw>
+        <out_group>2</out_group>
+        <match>
+            <ethernet-match>
+                <ethernet-type>
+                    <type>2048</type>
+                </ethernet-type>
+            </ethernet-match>
+            <ipv4-destination>10.0.0.1/24</ipv4-destination>
+        </match>
+        <hard-timeout>0</hard-timeout>
+        <cookie>10</cookie>
+        <idle-timeout>0</idle-timeout>
+        <flow-name>FooXf22</flow-name>
+        <priority>2</priority>
+        <barrier>false</barrier>
+    </flow>
+
+| **HTTP response**
+
+::
+
+    Status: 200 OK
+
+1. Show flow: check that *strict* is *true*.
+
+| **HTTP request**
+
+::
+
+    Operation: GET
+    URI: http://192.168.11.1:8080/restconf/config/opendaylight-inventory:nodes/node/openflow:1/table/2/flow/111
+    Accept: application/xml
+
+| **HTTP response**
+
+::
+
+    Status: 200 OK
+
+::
+
+    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
+    <flow
+        xmlns="urn:opendaylight:flow:inventory">
+        <strict>true</strict>
+        <instructions>
+            <instruction>
+                <order>1</order>
+                <apply-actions>
+                    <action>
+                      <order>1</order>
+                        <flood-all-action/>
+                    </action>
+                </apply-actions>
+            </instruction>
+        </instructions>
+        <table_id>2</table_id>
+        <id>111</id>
+        <cookie_mask>10</cookie_mask>
+        <out_port>10</out_port>
+        <installHw>false</installHw>
+        <out_group>2</out_group>
+        <match>
+            <ethernet-match>
+                <ethernet-type>
+                    <type>2048</type>
+                </ethernet-type>
+            </ethernet-match>
+            <ipv4-destination>10.0.0.1/24</ipv4-destination>
+        </match>
+        <hard-timeout>0</hard-timeout>
+        <cookie>10</cookie>
+        <idle-timeout>0</idle-timeout>
+        <flow-name>FooXf22</flow-name>
+        <priority>2</priority>
+        <barrier>false</barrier>
+    </flow>
+
+1. Delete the flow created.
+
+| **HTTP request**
+
+::
+
+    Operation: DELETE
+    URI: http://192.168.11.1:8080/restconf/config/opendaylight-inventory:nodes/node/openflow:1/table/2/flow/111
+
+| **HTTP response**
+
+::
+
+    Status: 200 OK
+
+Websocket change event notification subscription tutorial
+---------------------------------------------------------
+
+Subscribing to data change notifications makes it possible to obtain
+notifications about data manipulation (insert, change, delete) which are
+done on any specified **path** of any specified **datastore** with
+specific **scope**. In following examples *{odlAddress}* is address of
+server where ODL is running and *{odlPort}* is port on which
+OpenDaylight is running.
+
+Websocket notifications subscription process
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In this section we will learn what steps need to be taken in order to
+successfully subscribe to data change event notifications.
+
+Create stream
+^^^^^^^^^^^^^
+
+In order to use event notifications you first need to call RPC that
+creates notification stream that you can later listen to. You need to
+provide three parameters to this RPC:
+
+-  **path**: data store path that you plan to listen to. You can
+   register listener on containers, lists and leaves.
+
+-  **datastore**: data store type. *OPERATIONAL* or *CONFIGURATION*.
+
+-  **scope**: Represents scope of data change. Possible options are:
+
+   -  BASE: only changes directly to the data tree node specified in the
+      path will be reported
+
+   -  ONE: changes to the node and to direct child nodes will be
+      reported
+
+   -  SUBTREE: changes anywhere in the subtree starting at the node will
+      be reported
+
+The RPC to create the stream can be invoked via RESTCONF like this:
+
+-  URI:
+   http://{odlAddress}:{odlPort}/restconf/operations/sal-remote:create-data-change-event-subscription
+
+-  HEADER: Content-Type=application/json
+
+-  OPERATION: POST
+
+-  DATA:
+
+   .. code:: json
+
+       {
+           "input": {
+               "path": "/toaster:toaster/toaster:toasterStatus",
+               "sal-remote-augment:datastore": "OPERATIONAL",
+               "sal-remote-augment:scope": "ONE"
+           }
+       }
+
+The response should look something like this:
+
+.. code:: json
+
+    {
+        "output": {
+            "stream-name": "data-change-event-subscription/toaster:toaster/toaster:toasterStatus/datastore=CONFIGURATION/scope=SUBTREE"
+        }
+    }
+
+**stream-name** is important because you will need to use it when you
+subscribe to the stream in the next step.
+
+.. note::
+
+    Internally, this will create a new listener for *stream-name* if it
+    did not already exist.
+
+Subscribe to stream
+^^^^^^^^^^^^^^^^^^^
+
+In order to subscribe to stream and obtain WebSocket location you need
+to call *GET* on your stream path. The URI should generally be
+http://{odlAddress}:{odlPort}/restconf/streams/stream/{streamName},
+where *{streamName}* is the *stream-name* parameter contained in
+response from *create-data-change-event-subscription* RPC from the
+previous step.
+
+-  URI:
+   http://{odlAddress}:{odlPort}/restconf/streams/stream/data-change-event-subscription/toaster:toaster/datastore=CONFIGURATION/scope=SUBTREE
+
+-  OPERATION: GET
+
+The subscription call may be modified with the following query parameters defined in the RESTCONF RFC:
+
+-  `filter <https://tools.ietf.org/html/draft-ietf-netconf-restconf-05#section-4.8.6>`__
+
+-  `start-time <https://tools.ietf.org/html/draft-ietf-netconf-restconf-05#section-4.8.7>`__
+
+-  `end-time <https://tools.ietf.org/html/draft-ietf-netconf-restconf-05#section-4.8.8>`__
+
+In addition, the following ODL extension query parameter is supported:
+
+:odl-leaf-nodes-only:
+  If this parameter is set to "true", create and update notifications will only
+  contain the leaf nodes modified instead of the entire subscription subtree.
+  This can help in reducing the size of the notifications.
+
+The expected response status is 200 OK and response body should be
+empty. You will get your WebSocket location from **Location** header of
+response. For example in our particular toaster example location header
+would have this value:
+*ws://{odlAddress}:8185/toaster:toaster/datastore=CONFIGURATION/scope=SUBTREE*
+
+.. note::
+
+    During this phase there is an internal check for to see if a
+    listener for the *stream-name* from the URI exists. If not, new a
+    new listener is registered with the DOM data broker.
+
+Receive notifications
+^^^^^^^^^^^^^^^^^^^^^
+
+You should now have a data change notification stream created and have
+location of a WebSocket. You can use this WebSocket to listen to data
+change notifications. To listen to notifications you can use a
+JavaScript client or if you are using chrome browser you can use the
+`Simple WebSocket
+Client <https://chrome.google.com/webstore/detail/simple-websocket-client/pfdhoblngboilpfeibdedpjgfnlcodoo>`__.
+
+Also, for testing purposes, there is simple Java application named
+WebSocketClient. The application is placed in the
+*-sal-rest-connector-classes.class* project. It accepts a WebSocket URI
+as and input parameter. After starting the utility (WebSocketClient
+class directly in Eclipse/InteliJ Idea) received notifications should be
+displayed in console.
+
+Notifications are always in XML format and look like this:
+
+.. code:: xml
+
+    <notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">
+        <eventTime>2014-09-11T09:58:23+02:00</eventTime>
+        <data-changed-notification xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote">
+            <data-change-event>
+                <path xmlns:meae="http://netconfcentral.org/ns/toaster">/meae:toaster</path>
+                <operation>updated</operation>
+                <data>
+                   <!-- updated data -->
+                </data>
+            </data-change-event>
+        </data-changed-notification>
+    </notification>
+
+Example use case
+~~~~~~~~~~~~~~~~
+
+The typical use case is listening to data change events to update web
+page data in real-time. In this tutorial we will be using toaster as the
+base.
+
+When you call *make-toast* RPC, it sets *toasterStatus* to "down" to
+reflect that the toaster is busy making toast. When it finishes,
+*toasterStatus* is set to "up" again. We will listen to this toaster
+status changes in data store and will reflect it on our web page in
+real-time thanks to WebSocket data change notification.
+
+Simple javascript client implementation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+We will create simple JavaScript web application that will listen
+updates on *toasterStatus* leaf and update some element of our web page
+according to new toaster status state.
+
+Create stream
+^^^^^^^^^^^^^
+
+First you need to create stream that you are planing to subscribe to.
+This can be achieved by invoking "create-data-change-event-subscription"
+RPC on RESTCONF via AJAX request. You need to provide data store
+**path** that you plan to listen on, **data store type** and **scope**.
+If the request is successful you can extract the **stream-name** from
+the response and use that to subscribe to the newly created stream. The
+*{username}* and *{password}* fields represent your credentials that you
+use to connect to OpenDaylight via RESTCONF:
+
+.. note::
+
+    The default user name and password are "admin".
+
+.. code:: javascript
+
+    function createStream() {
+        $.ajax(
+            {
+                url: 'http://{odlAddress}:{odlPort}/restconf/operations/sal-remote:create-data-change-event-subscription',
+                type: 'POST',
+                headers: {
+                  'Authorization': 'Basic ' + btoa('{username}:{password}'),
+                  'Content-Type': 'application/json'
+                },
+                data: JSON.stringify(
+                    {
+                        'input': {
+                            'path': '/toaster:toaster/toaster:toasterStatus',
+                            'sal-remote-augment:datastore': 'OPERATIONAL',
+                            'sal-remote-augment:scope': 'ONE'
+                        }
+                    }
+                )
+            }).done(function (data) {
+                // this function will be called when ajax call is executed successfully
+                subscribeToStream(data.output['stream-name']);
+            }).fail(function (data) {
+                // this function will be called when ajax call fails
+                console.log("Create stream call unsuccessful");
+            })
+    }
+
+Subscribe to stream
+^^^^^^^^^^^^^^^^^^^
+
+The Next step is to subscribe to the stream. To subscribe to the stream
+you need to call *GET* on
+*http://{odlAddress}:{odlPort}/restconf/streams/stream/{stream-name}*.
+If the call is successful, you get WebSocket address for this stream in
+**Location** parameter inside response header. You can get response
+header by calling *getResponseHeader(\ *Location*)* on HttpRequest
+object inside *done()* function call:
+
+.. code:: javascript
+
+    function subscribeToStream(streamName) {
+        $.ajax(
+            {
+                url: 'http://{odlAddress}:{odlPort}/restconf/streams/stream/' + streamName;
+                type: 'GET',
+                headers: {
+                  'Authorization': 'Basic ' + btoa('{username}:{password}'),
+                }
+            }
+        ).done(function (data, textStatus, httpReq) {
+            // we need function that has http request object parameter in order to access response headers.
+            listenToNotifications(httpReq.getResponseHeader('Location'));
+        }).fail(function (data) {
+            console.log("Subscribe to stream call unsuccessful");
+        });
+    }
+
+Receive notifications
+^^^^^^^^^^^^^^^^^^^^^
+
+Once you got WebSocket server location you can now connect to it and
+start receiving data change events. You need to define functions that
+will handle events on WebSocket. In order to process incoming events
+from OpenDaylight you need to provide a function that will handle
+*onmessage* events. The function must have one parameter that represents
+the received event object. The event data will be stored in
+*event.data*. The data will be in an XML format that you can then easily
+parse using jQuery.
+
+.. code:: javascript
+
+    function listenToNotifications(socketLocation) {
+        try {
+            var notificatinSocket = new WebSocket(socketLocation);
+
+            notificatinSocket.onmessage = function (event) {
+                // we process our received event here
+                console.log('Received toaster data change event.');
+                $($.parseXML(event.data)).find('data-change-event').each(
+                    function (index) {
+                        var operation = $(this).find('operation').text();
+                        if (operation == 'updated') {
+                            // toaster status was updated so we call function that gets the value of toasterStatus leaf
+                            updateToasterStatus();
+                            return false;
+                        }
+                    }
+                );
+            }
+            notificatinSocket.onerror = function (error) {
+                console.log("Socket error: " + error);
+            }
+            notificatinSocket.onopen = function (event) {
+                console.log("Socket connection opened.");
+            }
+            notificatinSocket.onclose = function (event) {
+                console.log("Socket connection closed.");
+            }
+            // if there is a problem on socket creation we get exception (i.e. when socket address is incorrect)
+        } catch(e) {
+            alert("Error when creating WebSocket" + e );
+        }
+    }
+
+The *updateToasterStatus()* function represents function that calls
+*GET* on the path that was modified and sets toaster status in some web
+page element according to received data. After the WebSocket connection
+has been established you can test events by calling make-toast RPC via
+RESTCONF.
+
+.. note::
+
+    for more information about WebSockets in JavaScript visit `Writing
+    WebSocket client
+    applications <https://developer.mozilla.org/en-US/docs/WebSockets/Writing_WebSocket_client_applications>`__
+
+.. _config_subsystem:
+
+Config Subsystem
+----------------
+
+Overview
+~~~~~~~~
+
+The Controller configuration operation has three stages:
+
+-  First, a Proposed configuration is created. Its target is to replace
+   the old configuration.
+
+-  Second, the Proposed configuration is validated, and then committed.
+   If it passes validation successfully, the Proposed configuration
+   state will be changed to Validated.
+
+-  Finally, a Validated configuration can be Committed, and the affected
+   modules can be reconfigured.
+
+In fact, each configuration operation is wrapped in a transaction. Once
+a transaction is created, it can be configured, that is to say, a user
+can abort the transaction during this stage. After the transaction
+configuration is done, it is committed to the validation stage. In this
+stage, the validation procedures are invoked. If one or more validations
+fail, the transaction can be reconfigured. Upon success, the second
+phase commit is invoked. If this commit is successful, the transaction
+enters the last stage, committed. After that, the desired modules are
+reconfigured. If the second phase commit fails, it means that the
+transaction is unhealthy - basically, a new configuration instance
+creation failed, and the application can be in an inconsistent state.
+
+.. figure:: ./images/configuration.jpg
+   :alt: Configuration states
+
+   Configuration states
+
+.. figure:: ./images/Transaction.jpg
+   :alt: Transaction states
+
+   Transaction states
+
+Validation
+~~~~~~~~~~
+
+To secure the consistency and safety of the new configuration and to
+avoid conflicts, the configuration validation process is necessary.
+Usually, validation checks the input parameters of a new configuration,
+and mostly verifies module-specific relationships. The validation
+procedure results in a decision on whether the proposed configuration is
+healthy.
+
+Dependency resolver
+~~~~~~~~~~~~~~~~~~~
+
+Since there can be dependencies between modules, a change in a module
+configuration can affect the state of other modules. Therefore, we need
+to verify whether dependencies on other modules can be resolved. The
+Dependency Resolver acts in a manner similar to dependency injectors.
+Basically, a dependency tree is built.
+
+APIs and SPIs
+~~~~~~~~~~~~~
+
+This section describes configuration system APIs and SPIs.
+
+SPIs
+^^^^
+
+**Module** org.opendaylight.controller.config.spi. Module is the common
+interface for all modules: every module must implement it. The module is
+designated to hold configuration attributes, validate them, and create
+instances of service based on the attributes. This instance must
+implement the AutoCloseable interface, owing to resources clean up. If
+the module was created from an already running instance, it contains an
+old instance of the module. A module can implement multiple services. If
+the module depends on other modules, setters need to be annotated with
+@RequireInterface.
+
+**Module creation**
+
+1. The module needs to be configured, set with all required attributes.
+
+2. The module is then moved to the commit stage for validation. If the
+   validation fails, the module attributes can be reconfigured.
+   Otherwise, a new instance is either created, or an old instance is
+   reconfigured. A module instance is identified by ModuleIdentifier,
+   consisting of the factory name and instance name.
+
+| **ModuleFactory** org.opendaylight.controller.config.spi. The
+  ModuleFactory interface must be implemented by each module factory.
+| A module factory can create a new module instance in two ways:
+
+-  From an existing module instance
+
+-  | An entirely new instance
+   | ModuleFactory can also return default modules, useful for
+     populating registry with already existing configurations. A module
+     factory implementation must have a globally unique name.
+
+APIs
+^^^^
+
++--------------------------------------+--------------------------------------+
+| ConfigRegistry                       | Represents functionality provided by |
+|                                      | a configuration transaction (create, |
+|                                      | destroy module, validate, or abort   |
+|                                      | transaction).                        |
++--------------------------------------+--------------------------------------+
+| ConfigTransactionController          | Represents functionality for         |
+|                                      | manipulating with configuration      |
+|                                      | transactions (begin, commit config). |
++--------------------------------------+--------------------------------------+
+| RuntimeBeanRegistratorAwareConfiBean | The module implementing this         |
+|                                      | interface will receive               |
+|                                      | RuntimeBeanRegistrator before        |
+|                                      | getInstance is invoked.              |
++--------------------------------------+--------------------------------------+
+
+Runtime APIs
+^^^^^^^^^^^^
+
++--------------------------------------+--------------------------------------+
+| RuntimeBean                          | Common interface for all runtime     |
+|                                      | beans                                |
++--------------------------------------+--------------------------------------+
+| RootRuntimeBeanRegistrator           | Represents functionality for root    |
+|                                      | runtime bean registration, which     |
+|                                      | subsequently allows hierarchical     |
+|                                      | registrations                        |
++--------------------------------------+--------------------------------------+
+| HierarchicalRuntimeBeanRegistration  | Represents functionality for runtime |
+|                                      | bean registration and                |
+|                                      | unreregistration from hierarchy      |
++--------------------------------------+--------------------------------------+
+
+JMX APIs
+^^^^^^^^
+
+| JMX API is purposed as a transition between the Client API and the JMX
+  platform.
+
++--------------------------------------+--------------------------------------+
+| ConfigTransactionControllerMXBean    | Extends ConfigTransactionController, |
+|                                      | executed by Jolokia clients on       |
+|                                      | configuration transaction.           |
++--------------------------------------+--------------------------------------+
+| ConfigRegistryMXBean                 | Represents entry point of            |
+|                                      | configuration management for         |
+|                                      | MXBeans.                             |
++--------------------------------------+--------------------------------------+
+| Object names                         | Object Name is the pattern used in   |
+|                                      | JMX to locate JMX beans. It consists |
+|                                      | of domain and key properties (at     |
+|                                      | least one key-value pair). Domain is |
+|                                      | defined as                           |
+|                                      | "org.opendaylight.controller". The   |
+|                                      | only mandatory property is "type".   |
++--------------------------------------+--------------------------------------+
+
+Use case scenarios
+^^^^^^^^^^^^^^^^^^
+
+| A few samples of successful and unsuccessful transaction scenarios
+  follow:
+
+**Successful commit scenario**
+
+1.  The user creates a transaction calling creteTransaction() method on
+    ConfigRegistry.
+
+2.  ConfigRegisty creates a transaction controller, and registers the
+    transaction as a new bean.
+
+3.  Runtime configurations are copied to the transaction. The user can
+    create modules and set their attributes.
+
+4.  The configuration transaction is to be committed.
+
+5.  The validation process is performed.
+
+6.  After successful validation, the second phase commit begins.
+
+7.  Modules proposed to be destroyed are destroyed, and their service
+    instances are closed.
+
+8.  Runtime beans are set to registrator.
+
+9.  The transaction controller invokes the method getInstance on each
+    module.
+
+10. The transaction is committed, and resources are either closed or
+    released.
+
+| **Validation failure scenario**
+| The transaction is the same as the previous case until the validation
+  process.
+
+1. If validation fails, (that is to day, illegal input attributes values
+   or dependency resolver failure), the validationException is thrown
+   and exposed to the user.
+
+2. The user can decide to reconfigure the transaction and commit again,
+   or abort the current transaction.
+
+3. On aborted transactions, TransactionController and JMXRegistrator are
+   properly closed.
+
+4. Unregistration event is sent to ConfigRegistry.
+
+Default module instances
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+The configuration subsystem provides a way for modules to create default
+instances. A default instance is an instance of a module, that is
+created at the module bundle start-up (module becomes visible for
+configuration subsystem, for example, its bundle is activated in the
+OSGi environment). By default, no default instances are produced.
+
+The default instance does not differ from instances created later in the
+module life-cycle. The only difference is that the configuration for the
+default instance cannot be provided by the configuration subsystem. The
+module has to acquire the configuration for these instances on its own.
+It can be acquired from, for example, environment variables. After the
+creation of a default instance, it acts as a regular instance and fully
+participates in the configuration subsystem (It can be reconfigured or
+deleted in following transactions.).
index ffe79afd6591dd0d5c6121329ec9f649c91ef860..71cfb2b37df0fb0418d57adc0d98dbb84b1d9d9b 100644 (file)
@@ -3,3 +3,8 @@
 ########################
 Controller Documentation
 ########################
+
+.. toctree::
+   :maxdepth: 1
+
+   dev-guide.rst