1 .. _controller-dev-guide:
9 OpenDaylight Controller is Java-based, model-driven controller using
10 YANG as its modeling language for various aspects of the system and
11 applications and with its components serves as a base platform for other
12 OpenDaylight applications.
14 The OpenDaylight Controller relies on the following technologies:
16 - **OSGI** - This framework is the back-end of OpenDaylight as it
17 allows dynamically loading of bundles and packages JAR files, and
18 binding bundles together for exchanging information.
20 - **Karaf** - Application container built on top of OSGI, which
21 simplifies operational aspects of packaging and installing
24 - **YANG** - a data modeling language used to model configuration and
25 state data manipulated by the applications, remote procedure calls,
28 The OpenDaylight Controller provides following model-driven subsystems
29 as a foundation for Java applications:
31 - :ref:`MD-SAL <mdsal_dev_guide>` - messaging and data storage
32 functionality for data, notifications and RPCs modeled by application
33 developers. MD-SAL uses YANG as the modeling for both interface and
34 data definitions, and provides a messaging and data-centric runtime
35 for such services based on YANG modeling.
37 - **MD-SAL Clustering** - enables cluster support for core MD-SAL
38 functionality and provides location-transparent accesss to
41 The OpenDaylight Controller supports external access to applications and
42 data using following model-driven protocols:
44 - **NETCONF** - XML-based RPC protocol, which provides abilities for
45 client to invoke YANG-modeled RPCs, receive notifications and to
46 read, modify and manipulate YANG modeled data.
48 - **RESTCONF** - HTTP-based protocol, which provides REST-like APIs to
49 manipulate YANG modeled data and invoke YANG modeled RPCs, using XML
50 or JSON as payload format.
57 The Model-Driven Service Adaptation Layer (MD-SAL) is message-bus
58 inspired extensible middleware component that provides messaging and
59 data storage functionality based on data and interface models defined by
60 application developers (i.e. user-defined models).
64 - Defines a **common-layer, concepts, data model building blocks and
65 messaging patterns** and provides infrastructure / framework for
66 applications and inter-application communication.
68 - Provide common support for user-defined transport and payload
69 formats, including payload serialization and adaptation (e.g. binary,
72 The MD-SAL uses **YANG** as the modeling language for both interface and
73 data definitions, and provides a messaging and data-centric runtime for
74 such services based on YANG modeling.
76 | The MD-SAL provides two different API types (flavours):
78 - **MD-SAL Binding:** MD-SAL APIs which extensively uses APIs and
79 classes generated from YANG models, which provides compile-time
82 - **MD-SAL DOM:** (Document Object Model) APIs which uses DOM-like
83 representation of data, which makes them more powerful, but provides
84 less compile-time safety.
88 Model-driven nature of the MD-SAL and **DOM**-based APIs allows for
89 behind-the-scene API and payload type mediation and transformation
90 to facilitate seamless communication between applications - this
91 enables for other components and applications to provide connectors
92 / expose different set of APIs and derive most of its functionality
93 purely from models, which all existing code can benefit from without
94 modification. For example **RESTCONF Connector** is an application
95 built on top of MD-SAL and exposes YANG-modeled application APIs
96 transparently via HTTP and adds support for XML and JSON payload
102 Basic concepts are building blocks which are used by applications, and
103 from which MD-SAL uses to define messaging patterns and to provide
104 services and behavior based on developer-supplied YANG models.
107 All state-related data are modeled and represented as data tree,
108 with possibility to address any element / subtree
110 - **Operational Data Tree** - Reported state of the system,
111 published by the providers using MD-SAL. Represents a feedback
112 loop for applications to observe state of the network / system.
114 - **Configuration Data Tree** - Intended state of the system or
115 network, populated by consumers, which expresses their intention.
118 Unique identifier of node / subtree in data tree, which provides
119 unambiguous information, how to reference and retrieve node /
120 subtree from conceptual data trees.
123 Asynchronous transient event which may be consumed by subscribers
124 and they may act upon it
127 asynchronous request-reply message pair, when request is triggered
128 by consumer, send to the provider, which in future replies with
133 In MD-SAL terminology, the term *RPC* is used to define the
134 input and output for a procedure (function) that is to be
135 provided by a provider, and mediated by the MD-SAL, that means
136 it may not result in remote call.
141 MD-SAL provides several messaging patterns using broker derived from
142 basic concepts, which are intended to transfer YANG modeled data between
143 applications to provide data-centric integration between applications
144 instead of API-centric integration.
146 - **Unicast communication**
148 - **Remote Procedure Calls** - unicast between consumer and
149 provider, where consumer sends **request** message to provider,
150 which asynchronously responds with **reply** message
152 - **Publish / Subscribe**
154 - **Notifications** - multicast transient message which is published
155 by provider and is delivered to subscribers
157 - **Data Change Events** - multicast asynchronous event, which is
158 sent by data broker if there is change in conceptual data tree,
159 and is delivered to subscribers
161 - **Transactional access to Data Tree**
163 - Transactional **reads** from conceptual **data tree** - read-only
164 transactions with isolation from other running transactions.
166 - Transactional **modification** to conceptual **data tree** - write
167 transactions with isolation from other running transactions.
169 - **Transaction chaining**
171 MD-SAL Data Transactions
172 ------------------------
174 MD-SAL **Data Broker** provides transactional access to conceptual
175 **data trees** representing configuration and operational state.
179 **Data tree** usually represents state of the modeled data, usually
180 this is state of controller, applications and also external systems
183 **Transactions** provide :ref:`stable and isolated
184 view <transaction_isolation>` from other currently running
185 transactions. The state of running transaction and underlying data tree
186 is not affected by other concurrently running transactions.
189 Transaction provides only modification capabilities, but does not
190 provide read capabilities. Write-only transaction is allocated using
191 ``newWriteOnlyTransaction()``.
195 This allows less state tracking for write-only transactions and
196 allows MD-SAL Clustering to optimize internal representation of
197 transaction in cluster.
200 Transaction provides both read and write capabilities. It is
201 allocated using ``newReadWriteTransaction()``.
204 Transaction provides stable read-only view based on current data
205 tree. Read-only view is not affected by any subsequent write
206 transactions. Read-only transaction is allocated using
207 ``newReadOnlyTransaction()``.
211 If an application needs to observe changes itself in data tree,
212 it should use **data tree listeners** instead of read-only
213 transactions and polling data tree.
215 Transactions may be allocated using the **data broker** itself or using
216 **transaction chain**. In the case of **transaction chain**, the new
217 allocated transaction is not based on current state of data tree, but
218 rather on state introduced by previous transaction from the same chain,
219 even if the commit for previous transaction has not yet occurred (but
220 transaction was submitted).
222 Write-Only & Read-Write Transaction
223 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
225 Write-Only and Read-Write transactions provide modification capabilities
226 for the conceptual data trees.
228 1. application allocates new transactions using
229 ``newWriteOnlyTransaction()`` or ``newReadWriteTransaction()``.
231 2. application `modifies data tree <#_modification_of_data_tree>`__
232 using ``put``, ``merge`` and/or ``delete``.
234 3. application finishes transaction using
235 ``submit()``, which :ref:`seals transaction
236 and submits <submitting_transaction>` it to be processed.
238 4. application observes the result of the transaction commit using
239 either blocking or asynchronous calls.
241 The **initial state** of the write transaction is a **stable snapshot**
242 of the current data tree state captured when transaction was created and
243 it’s state and underlying data tree are not affected by other
244 concurrently running transactions.
246 Write transactions are **isolated** from other concurrent write
247 transactions. All :ref:`writes are local <transaction_local_state>`
248 to the transaction and represents only a **proposal of state change**
249 for data tree and **are not visible** to any other concurrently running
250 transactions (including read-only transactions).
252 The transaction :ref:`commit may fail <commit_failure_scenarios>` due
253 to failing verification of data or concurrent transaction modifying and
254 affected data in an incompatible way.
256 Modification of Data Tree
257 ^^^^^^^^^^^^^^^^^^^^^^^^^
259 Write-only and read-write transaction provides following methods to
265 <T> void put(LogicalDatastoreType store, InstanceIdentifier<T> path, T data);
267 Stores a piece of data at a specified path. This acts as an **add /
268 replace** operation, which is to say that whole subtree will be
269 replaced by the specified data.
274 <T> void merge(LogicalDatastoreType store, InstanceIdentifier<T> path, T data);
276 Merges a piece of data with the existing data at a specified path.
277 Any **pre-existing data** which are not explicitly overwritten
278 **will be preserved**. This means that if you store a container, its
279 child subtrees will be merged.
284 void delete(LogicalDatastoreType store, InstanceIdentifier<?> path);
286 Removes a whole subtree from a specified path.
288 .. _submitting_transaction:
290 Submitting transaction
291 ^^^^^^^^^^^^^^^^^^^^^^
293 Transaction is submitted to be processed and committed using following
298 CheckedFuture<Void,TransactionCommitFailedException> submit();
300 Applications publish the changes proposed in the transaction by calling
301 ``submit()`` on the transaction. This **seals the transaction**
302 (preventing any further writes using this transaction) and submits it to
303 be processed and applied to global conceptual data tree. The
304 ``submit()`` method does not block, but rather returns
305 ``ListenableFuture``, which will complete successfully once processing
306 of transaction is finished and changes are applied to data tree. If
307 **commit** of data failed, the future will fail with
308 ``TransactionFailedException``.
310 Application may listen on commit state asynchronously using
311 ``ListenableFuture``.
315 Futures.addCallback( writeTx.submit(), new FutureCallback<Void>() {
316 public void onSuccess( Void result ) {
317 LOG.debug("Transaction committed successfully.");
320 public void onFailure( Throwable t ) {
321 LOG.error("Commit failed.",e);
325 - Submits ``writeTx`` and registers application provided
326 ``FutureCallback`` on returned future.
328 - Invoked when future completed successfully - transaction ``writeTx``
329 was successfully committed to data tree.
331 - Invoked when future failed - commit of transaction ``writeTx``
332 failed. Supplied exception provides additional details and cause of
335 If application need to block till commit is finished it may use
336 ``checkedGet()`` to wait till commit is finished.
341 writeTx.submit().checkedGet();
342 } catch (TransactionCommitFailedException e) {
343 LOG.error("Commit failed.",e);
346 - Submits ``writeTx`` and blocks till commit of ``writeTx`` is
347 finished. If commit fails ``TransactionCommitFailedException`` will
350 - Catches ``TransactionCommitFailedException`` and logs it.
352 .. _transaction_local_state:
354 Transaction local state
355 ^^^^^^^^^^^^^^^^^^^^^^^
357 Read-Write transactions maintain transaction-local state, which renders
358 all modifications as if they happened, but this is only local to
361 Reads from the transaction returns data as if the previous modifications
362 in transaction already happened.
364 Let assume initial state of data tree for ``PATH`` is ``A``.
368 ReadWriteTransaction rwTx = broker.newReadWriteTransaction();
370 rwRx.read(OPERATIONAL,PATH).get();
371 rwRx.put(OPERATIONAL,PATH,B);
372 rwRx.read(OPERATIONAL,PATH).get();
373 rwRx.put(OPERATIONAL,PATH,C);
374 rwRx.read(OPERATIONAL,PATH).get();
376 - Allocates new ``ReadWriteTransaction``.
378 - Read from ``rwTx`` will return value ``A`` for ``PATH``.
380 - Writes value ``B`` to ``PATH`` using ``rwTx``.
382 - Read will return value ``B`` for ``PATH``, since previous write
383 occurred in same transaction.
385 - Writes value ``C`` to ``PATH`` using ``rwTx``.
387 - Read will return value ``C`` for ``PATH``, since previous write
388 occurred in same transaction.
390 .. _transaction_isolation:
392 Transaction isolation
393 ~~~~~~~~~~~~~~~~~~~~~
395 Running (not submitted) transactions are isolated from each other and
396 changes done in one transaction are not observable in other currently
399 Lets assume initial state of data tree for ``PATH`` is ``A``.
403 ReadOnlyTransaction txRead = broker.newReadOnlyTransaction();
404 ReadWriteTransaction txWrite = broker.newReadWriteTransaction();
406 txRead.read(OPERATIONAL,PATH).get();
407 txWrite.put(OPERATIONAL,PATH,B);
408 txWrite.read(OPERATIONAL,PATH).get();
409 txWrite.submit().get();
410 txRead.read(OPERATIONAL,PATH).get();
411 txAfterCommit = broker.newReadOnlyTransaction();
412 txAfterCommit.read(OPERATIONAL,PATH).get();
414 - Allocates read only transaction, which is based on data tree which
415 contains value ``A`` for ``PATH``.
417 - Allocates read write transaction, which is based on data tree which
418 contains value ``A`` for ``PATH``.
420 - Read from read-only transaction returns value ``A`` for ``PATH``.
422 - Data tree is updated using read-write transaction, ``PATH`` contains
423 ``B``. Change is not public and only local to transaction.
425 - Read from read-write transaction returns value ``B`` for ``PATH``.
427 - Submits changes in read-write transaction to be committed to data
428 tree. Once commit will finish, changes will be published and ``PATH``
429 will be updated for value ``B``. Previously allocated transactions
430 are not affected by this change.
432 - Read from previously allocated read-only transaction still returns
433 value ``A`` for ``PATH``, since it provides stable and isolated view.
435 - Allocates new read-only transaction, which is based on data tree,
436 which contains value ``B`` for ``PATH``.
438 - Read from new read-only transaction return value ``B`` for ``PATH``
439 since read-write transaction was committed.
443 Examples contain blocking calls on future only to illustrate that
444 action happened after other asynchronous action. The use of the
445 blocking call ``ListenableFuture#get()`` is discouraged for most
446 use-cases and you should use
447 ``Futures#addCallback(ListenableFuture, FutureCallback)`` to listen
448 asynchronously for result.
450 .. _commit_failure_scenarios:
452 Commit failure scenarios
453 ~~~~~~~~~~~~~~~~~~~~~~~~
455 A transaction commit may fail because of following reasons:
457 Optimistic Lock Failure
458 Another transaction finished earlier and **modified the same node in
459 a non-compatible way**. The commit (and the returned future) will
460 fail with an ``OptimisticLockFailedException``.
462 It is the responsibility of the caller to create a new transaction
463 and submit the same modification again in order to update data tree.
467 ``OptimisticLockFailedException`` usually exposes **multiple
468 writers** to the same data subtree, which may conflict on same
471 In most cases, retrying may result in a probability of success.
473 There are scenarios, albeit unusual, where any number of retries
474 will not succeed. Therefore it is strongly recommended to limit
475 the number of retries (2 or 3) to avoid an endless loop.
478 The data change introduced by this transaction **did not pass
479 validation** by commit handlers or data was incorrectly structured.
480 The returned future will fail with a
481 ``DataValidationFailedException``. User **should not retry** to
482 create new transaction with same data, since it probably will fail
485 Example conflict of two transactions
486 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
488 This example illustrates two concurrent transactions, which derived from
489 same initial state of data tree and proposes conflicting modifications.
493 WriteTransaction txA = broker.newWriteTransaction();
494 WriteTransaction txB = broker.newWriteTransaction();
496 txA.put(CONFIGURATION, PATH, A);
497 txB.put(CONFIGURATION, PATH, B);
499 CheckedFuture<?,?> futureA = txA.submit();
500 CheckedFuture<?,?> futureB = txB.submit();
502 - Updates ``PATH`` to value ``A`` using ``txA``
504 - Updates ``PATH`` to value ``B`` using ``txB``
506 - Seals & submits ``txA``. The commit will be processed asynchronously
507 and data tree will be updated to contain value ``A`` for ``PATH``.
508 The returned ‘ListenableFuture’ will complete successfully once state
509 is applied to data tree.
511 - Seals & submits ``txB``. Commit of ``txB`` will fail, because
512 previous transaction also modified path in a concurrent way. The
513 state introduced by ``txB`` will not be applied. The returned
514 ``ListenableFuture`` will fail with ``OptimisticLockFailedException``
515 exception, which indicates that concurrent transaction prevented the
516 submitted transaction from being applied.
518 Example asynchronous retry-loop
519 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
523 private void doWrite( final int tries ) {
524 WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction();
526 MyDataObject data = ...;
527 InstanceIdentifier<MyDataObject> path = ...;
528 writeTx.put( LogicalDatastoreType.OPERATIONAL, path, data );
530 Futures.addCallback( writeTx.submit(), new FutureCallback<Void>() {
531 public void onSuccess( Void result ) {
535 public void onFailure( Throwable t ) {
536 if( t instanceof OptimisticLockFailedException && (( tries - 1 ) > 0)) {
537 doWrite( tries - 1 );
545 Concurrent change compatibility
546 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
548 There are several sets of changes which could be considered incompatible
549 between two transactions which are derived from same initial state.
550 Rules for conflict detection applies recursively for each subtree level.
552 Following table shows state changes and failures between two concurrent
553 transactions, which are based on same initial state, ``tx1`` is
554 submitted before ``tx2``.
556 INFO: Following tables stores numeric values and shows data using
557 ``toString()`` to simplify examples.
559 +--------------------+--------------------+--------------------+--------------------+
560 | Initial state | tx1 | tx2 | Observable Result |
561 +====================+====================+====================+====================+
562 | Empty | ``put(A,1)`` | ``put(A,2)`` | ``tx2`` will fail, |
563 | | | | value of ``A`` is |
565 +--------------------+--------------------+--------------------+--------------------+
566 | Empty | ``put(A,1)`` | ``merge(A,2)`` | value of ``A`` is |
568 +--------------------+--------------------+--------------------+--------------------+
569 | Empty | ``merge(A,1)`` | ``put(A,2)`` | ``tx2`` will fail, |
570 | | | | value of ``A`` is |
572 +--------------------+--------------------+--------------------+--------------------+
573 | Empty | ``merge(A,1)`` | ``merge(A,2)`` | ``A`` is ``2`` |
574 +--------------------+--------------------+--------------------+--------------------+
575 | A=0 | ``put(A,1)`` | ``put(A,2)`` | ``tx2`` will fail, |
576 | | | | ``A`` is ``1`` |
577 +--------------------+--------------------+--------------------+--------------------+
578 | A=0 | ``put(A,1)`` | ``merge(A,2)`` | ``A`` is ``2`` |
579 +--------------------+--------------------+--------------------+--------------------+
580 | A=0 | ``merge(A,1)`` | ``put(A,2)`` | ``tx2`` will fail, |
581 | | | | value of ``A`` is |
583 +--------------------+--------------------+--------------------+--------------------+
584 | A=0 | ``merge(A,1)`` | ``merge(A,2)`` | ``A`` is ``2`` |
585 +--------------------+--------------------+--------------------+--------------------+
586 | A=0 | ``delete(A)`` | ``put(A,2)`` | ``tx2`` will fail, |
587 | | | | ``A`` does not |
589 +--------------------+--------------------+--------------------+--------------------+
590 | A=0 | ``delete(A)`` | ``merge(A,2)`` | ``A`` is ``2`` |
591 +--------------------+--------------------+--------------------+--------------------+
593 Table: Concurrent change resolution for leaves and leaf-list items
595 +--------------------+--------------------+--------------------+--------------------+
596 | Initial state | ``tx1`` | ``tx2`` | Result |
597 +====================+====================+====================+====================+
598 | Empty | put(TOP,[]) | put(TOP,[]) | ``tx2`` will fail, |
599 | | | | state is TOP=[] |
600 +--------------------+--------------------+--------------------+--------------------+
601 | Empty | put(TOP,[]) | merge(TOP,[]) | TOP=[] |
602 +--------------------+--------------------+--------------------+--------------------+
603 | Empty | put(TOP,[FOO=1]) | put(TOP,[BAR=1]) | ``tx2`` will fail, |
605 | | | | TOP=[FOO=1] |
606 +--------------------+--------------------+--------------------+--------------------+
607 | Empty | put(TOP,[FOO=1]) | merge(TOP,[BAR=1]) | TOP=[FOO=1,BAR=1] |
608 +--------------------+--------------------+--------------------+--------------------+
609 | Empty | merge(TOP,[FOO=1]) | put(TOP,[BAR=1]) | ``tx2`` will fail, |
611 | | | | TOP=[FOO=1] |
612 +--------------------+--------------------+--------------------+--------------------+
613 | Empty | merge(TOP,[FOO=1]) | merge(TOP,[BAR=1]) | TOP=[FOO=1,BAR=1] |
614 +--------------------+--------------------+--------------------+--------------------+
615 | TOP=[] | put(TOP,[FOO=1]) | put(TOP,[BAR=1]) | ``tx2`` will fail, |
617 | | | | TOP=[FOO=1] |
618 +--------------------+--------------------+--------------------+--------------------+
619 | TOP=[] | put(TOP,[FOO=1]) | merge(TOP,[BAR=1]) | state is |
620 | | | | TOP=[FOO=1,BAR=1] |
621 +--------------------+--------------------+--------------------+--------------------+
622 | TOP=[] | merge(TOP,[FOO=1]) | put(TOP,[BAR=1]) | ``tx2`` will fail, |
624 | | | | TOP=[FOO=1] |
625 +--------------------+--------------------+--------------------+--------------------+
626 | TOP=[] | merge(TOP,[FOO=1]) | merge(TOP,[BAR=1]) | state is |
627 | | | | TOP=[FOO=1,BAR=1] |
628 +--------------------+--------------------+--------------------+--------------------+
629 | TOP=[] | delete(TOP) | put(TOP,[BAR=1]) | ``tx2`` will fail, |
630 | | | | state is empty |
632 +--------------------+--------------------+--------------------+--------------------+
633 | TOP=[] | delete(TOP) | merge(TOP,[BAR=1]) | state is |
634 | | | | TOP=[BAR=1] |
635 +--------------------+--------------------+--------------------+--------------------+
636 | TOP=[] | put(TOP/FOO,1) | put(TOP/BAR,1]) | state is |
637 | | | | TOP=[FOO=1,BAR=1] |
638 +--------------------+--------------------+--------------------+--------------------+
639 | TOP=[] | put(TOP/FOO,1) | merge(TOP/BAR,1) | state is |
640 | | | | TOP=[FOO=1,BAR=1] |
641 +--------------------+--------------------+--------------------+--------------------+
642 | TOP=[] | merge(TOP/FOO,1) | put(TOP/BAR,1) | state is |
643 | | | | TOP=[FOO=1,BAR=1] |
644 +--------------------+--------------------+--------------------+--------------------+
645 | TOP=[] | merge(TOP/FOO,1) | merge(TOP/BAR,1) | state is |
646 | | | | TOP=[FOO=1,BAR=1] |
647 +--------------------+--------------------+--------------------+--------------------+
648 | TOP=[] | delete(TOP) | put(TOP/BAR,1) | ``tx2`` will fail, |
649 | | | | state is empty |
651 +--------------------+--------------------+--------------------+--------------------+
652 | TOP=[] | delete(TOP) | merge(TOP/BAR,1] | ``tx2`` will fail, |
653 | | | | state is empty |
655 +--------------------+--------------------+--------------------+--------------------+
656 | TOP=[FOO=1] | put(TOP/FOO,2) | put(TOP/BAR,1) | state is |
657 | | | | TOP=[FOO=2,BAR=1] |
658 +--------------------+--------------------+--------------------+--------------------+
659 | TOP=[FOO=1] | put(TOP/FOO,2) | merge(TOP/BAR,1) | state is |
660 | | | | TOP=[FOO=2,BAR=1] |
661 +--------------------+--------------------+--------------------+--------------------+
662 | TOP=[FOO=1] | merge(TOP/FOO,2) | put(TOP/BAR,1) | state is |
663 | | | | TOP=[FOO=2,BAR=1] |
664 +--------------------+--------------------+--------------------+--------------------+
665 | TOP=[FOO=1] | merge(TOP/FOO,2) | merge(TOP/BAR,1) | state is |
666 | | | | TOP=[FOO=2,BAR=1] |
667 +--------------------+--------------------+--------------------+--------------------+
668 | TOP=[FOO=1] | delete(TOP/FOO) | put(TOP/BAR,1) | state is |
669 | | | | TOP=[BAR=1] |
670 +--------------------+--------------------+--------------------+--------------------+
671 | TOP=[FOO=1] | delete(TOP/FOO) | merge(TOP/BAR,1] | state is |
672 | | | | TOP=[BAR=1] |
673 +--------------------+--------------------+--------------------+--------------------+
675 Table: Concurrent change resolution for containers, lists, list items
680 The MD-SAL provides a way to deliver Remote Procedure Calls (RPCs) to a
681 particular implementation based on content in the input as it is modeled
682 in YANG. This part of the RPC input is referred to as a **context
685 The MD-SAL does not dictate the name of the leaf which is used for this
686 RPC routing, but provides necessary functionality for YANG model author
687 to define their **context reference** in their model of RPCs.
689 MD-SAL routing behavior is modeled using following terminology and its
690 application to YANG models:
693 Logical type of RPC routing. Context type is modeled as YANG
694 ``identity`` and is referenced in model to provide scoping
698 Conceptual location in data tree, which represents context in which
699 RPC could be executed. Context instance usually represent logical
700 point to which RPC execution is attached.
703 Field of RPC input payload which contains Instance Identifier
704 referencing **context instance** in which the RPC should be
707 Modeling a routed RPC
708 ~~~~~~~~~~~~~~~~~~~~~
710 In order to define routed RPCs, the YANG model author needs to declare
711 (or reuse) a **context type**, set of possible **context instances** and
712 finally RPCs which will contain **context reference** on which they will
715 Declaring a routing context type
716 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
720 identity node-context {
721 description "Identity used to mark node context";
724 This declares an identity named ``node-context``, which is used as
725 marker for node-based routing and is used in other places to reference
728 Declaring possible context instances
729 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
731 In order to define possible values of **context instances** for routed
732 RPCs, we need to model that set accordingly using ``context-instance``
733 extension from the ``yang-ext`` model.
737 import yang-ext { prefix ext; }
739 /** Base structure **/
743 ext:context-instance "node-context";
744 // other node-related fields would go here
748 The statement ``ext:context-instance "node-context";`` marks any element
749 of the ``list node`` as a possible valid **context instance** in
750 ``node-context`` based routing.
754 The existence of a **context instance** node in operational or
755 config data tree is not strongly tied to existence of RPC
758 For most routed RPC models, there is relationship between the data
759 present in operational data tree and RPC implementation
760 availability, but this is not enforced by MD-SAL. This provides some
761 flexibility for YANG model writers to better specify their routing
762 model and requirements for implementations. Details when RPC
763 implementations are available should be documented in YANG model.
765 If user invokes RPC with a **context instance** that has no
766 registered implementation, the RPC invocation will fail with the
767 exception ``DOMRpcImplementationNotAvailableException``.
769 Declaring a routed RPC
770 ^^^^^^^^^^^^^^^^^^^^^^
772 To declare RPC to be routed based on ``node-context`` we need to add
773 leaf of ``instance-identifier`` type (or type derived from
774 ``instance-identifier``) to the RPC and mark it as **context
777 This is achieved using YANG extension ``context-reference`` from
778 ``yang-ext`` model on leaf, which will be used for RPC routing.
782 rpc example-routed-rpc {
785 ext:context-reference "node-context";
786 type "instance-identifier";
788 // other input to the RPC would go here
792 The statement ``ext:context-reference "node-context"`` marks
793 ``leaf node`` as **context reference** of type ``node-context``. The
794 value of this leaf, will be used by the MD-SAL to select the particular
795 RPC implementation that registered itself as the implementation of the
796 RPC for particular **context instance**.
801 From a user perspective (e.g. invoking RPCs) there is no difference
802 between routed and non-routed RPCs. Routing information is just an
803 additional leaf in RPC which must be populated.
805 Implementing a routed RPC
806 ~~~~~~~~~~~~~~~~~~~~~~~~~
810 Registering implementations
811 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
813 Implementations of a routed RPC (e.g., southbound plugins) will specify
814 an instance-identifier for the **context reference** (in this case a
815 node) for which they want to provide an implementation during
816 registration. Consumers, e.g., those calling the RPC are required to
817 specify that instance-identifier (in this case the identifier of a node)
820 Simple code which showcases that for add-flow via Binding-Aware APIs
821 (`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>`__
827 62 public void onSessionInitiated(ProviderContext session) {
828 63 assertNotNull(session);
829 64 firstReg = session.addRoutedRpcImplementation(SalFlowService.class, salFlowService1);
832 Line 64: We are registering salFlowService1 as implementation of
837 107 NodeRef nodeOne = createNodeRef("foo:node:1");
839 110 * Provider 1 registers path of node 1
841 112 firstReg.registerPath(NodeContext.class, nodeOne);
843 Line 107: We are creating NodeRef (encapsulation of InstanceIdentifier)
846 Line 112: We register salFlowService1 as implementation for nodeOne.
848 The salFlowService1 will be executed only for RPCs which contains
849 Instance Identifier for foo:node:1.
854 In case there is is only a single provider of an RPC in the cluster
855 the RPC registration is propagated to other nodes via Gossip protocol
856 and the RPC calls from other nodes are correctly routed to the
857 provider. Since the registrations are not expected to change rapidly
858 there is a latency of about 1 second until the registration is reflected
862 OpenDaylight Controller MD-SAL: RESTCONF
863 ----------------------------------------
865 RESTCONF operations overview
866 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
868 | RESTCONF allows access to datastores in the controller.
869 | There are two datastores:
871 - Config: Contains data inserted via controller
873 - Operational: Contains other data
877 | Each request must start with the URI /restconf.
878 | RESTCONF listens on port 8080 for HTTP requests.
880 RESTCONF supports **OPTIONS**, **GET**, **PUT**, **POST**, and
881 **DELETE** operations. Request and response data can either be in the
882 XML or JSON format. XML structures according to yang are defined at:
883 `XML-YANG <https://www.rfc-editor.org/rfc/rfc6020>`__. JSON structures are
885 `JSON-YANG <https://datatracker.ietf.org/doc/html/draft-lhotka-netmod-yang-json-02>`__.
886 Data in the request must have a correctly set **Content-Type** field in
887 the http header with the allowed value of the media type. The media type
888 of the requested data has to be set in the **Accept** field. Get the
889 media types for each resource by calling the OPTIONS operation. Most of
890 the paths of the pathsRestconf endpoints use `Instance
891 Identifier <https://wiki-archive.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Concepts#Instance_Identifier>`__.
892 ``<identifier>`` is used in the explanation of the operations.
896 - It must start with <moduleName>:<nodeName> where <moduleName> is a
897 name of the module and <nodeName> is the name of a node in the
898 module. It is sufficient to just use <nodeName> after
899 <moduleName>:<nodeName>. Each <nodeName> has to be separated by /.
901 - <nodeName> can represent a data node which is a list or container
902 yang built-in type. If the data node is a list, there must be defined
903 keys of the list behind the data node name for example,
904 <nodeName>/<valueOfKey1>/<valueOfKey2>.
906 - | The format <moduleName>:<nodeName> has to be used in this case as
908 | Module A has node A1. Module B augments node A1 by adding node X.
909 Module C augments node A1 by adding node X. For clarity, it has to
910 be known which node is X (for example: C:X). For more details about
911 encoding, see: `RESTCONF 02 - Encoding YANG Instance Identifiers in
913 URI. <https://datatracker.ietf.org/doc/html/draft-bierman-netconf-restconf-02#section-5.3.1>`__
918 | A Node can be behind a mount point. In this case, the URI has to be in
919 format <identifier>/**yang-ext:mount**/<identifier>. The first
920 <identifier> is the path to a mount point and the second <identifier>
921 is the path to a node behind the mount point. A URI can end in a mount
922 point itself by using <identifier>/**yang-ext:mount**.
923 | More information on how to actually use mountpoints is available at:
925 Controller:Config:Examples:Netconf <https://wiki-archive.opendaylight.org/view/OpenDaylight_Controller:Config:Examples:Netconf>`__.
933 - Returns the XML description of the resources with the required
934 request and response media types in Web Application Description
937 GET /restconf/config/<identifier>
938 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
940 - Returns a data node from the Config datastore.
942 - <identifier> points to a data node which must be retrieved.
944 GET /restconf/operational/<identifier>
945 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
947 - Returns the value of the data node from the Operational datastore.
949 - <identifier> points to a data node which must be retrieved.
951 PUT /restconf/config/<identifier>
952 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
954 - Updates or creates data in the Config datastore and returns the state
957 - <identifier> points to a data node which must be stored.
963 PUT http://<controllerIP>:8080/restconf/config/module1:foo/bar
964 Content-Type: applicaton/xml
969 | **Example with mount point:**
973 PUT http://<controllerIP>:8080/restconf/config/module1:foo1/foo2/yang-ext:mount/module2:foo/bar
974 Content-Type: applicaton/xml
979 POST /restconf/config
980 ^^^^^^^^^^^^^^^^^^^^^
982 - Creates the data if it does not exist
988 POST URL: http://localhost:8080/restconf/config/
989 content-type: application/yang.data+json
995 "toaster:toasterManufacturer" : "General Electric",
996 "toaster:toasterModelNumber" : "123",
997 "toaster:toasterStatus" : "up"
1001 POST /restconf/config/<identifier>
1002 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1004 - Creates the data if it does not exist in the Config datastore, and
1005 returns the state about success.
1007 - <identifier> points to a data node where data must be stored.
1009 - The root element of data must have the namespace (data are in XML) or
1010 module name (data are in JSON.)
1016 POST http://<controllerIP>:8080/restconf/config/module1:foo
1017 Content-Type: applicaton/xml/
1018 <bar xmlns=“module1namespace”>
1022 **Example with mount point:**
1026 http://<controllerIP>:8080/restconf/config/module1:foo1/foo2/yang-ext:mount/module2:foo
1027 Content-Type: applicaton/xml
1028 <bar xmlns=“module2namespace”>
1032 POST /restconf/operations/<moduleName>:<rpcName>
1033 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1037 - <moduleName>:<rpcName> - <moduleName> is the name of the module and
1038 <rpcName> is the name of the RPC in this module.
1040 - The Root element of the data sent to RPC must have the name “input”.
1042 - The result can be the status code or the retrieved data having the
1043 root element “output”.
1049 POST http://<controllerIP>:8080/restconf/operations/module1:fooRpc
1050 Content-Type: applicaton/xml
1051 Accept: applicaton/xml
1056 The answer from the server could be:
1061 | **An example using a JSON payload:**
1065 POST http://localhost:8080/restconf/operations/toaster:make-toast
1066 Content-Type: application/yang.data+json
1070 "toaster:toasterDoneness" : "10",
1071 "toaster:toasterToastType":"wheat-bread"
1077 Even though this is a default for the toasterToastType value in the
1078 yang, you still need to define it.
1080 DELETE /restconf/config/<identifier>
1081 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1083 - Removes the data node in the Config datastore and returns the state
1086 - <identifier> points to a data node which must be removed.
1088 More information is available in the `RESTCONF
1089 RFC <https://datatracker.ietf.org/doc/html/draft-bierman-netconf-restconf-02>`__.
1094 | RESTCONF uses these base classes:
1097 Represents the path in the data tree
1100 Used for invoking RPCs
1103 Offers manipulation with transactions and reading data from the
1107 Holds information about yang modules
1110 Returns MountInstance based on the InstanceIdentifier pointing to a
1114 Contains the SchemaContext behind the mount point
1117 Provides information about the schema node
1120 Possesses the same name as the schema node, and contains the value
1121 representing the data node value
1124 Can contain CompositeNode-s and SimpleNode-s
1129 Figure 1 shows the GET operation with URI restconf/config/M:N where M is
1130 the module name, and N is the node name.
1132 .. figure:: ./images/Get.png
1137 1. The requested URI is translated into the InstanceIdentifier which
1138 points to the data node. During this translation, the DataSchemaNode
1139 that conforms to the data node is obtained. If the data node is
1140 behind the mount point, the MountInstance is obtained as well.
1142 2. RESTCONF asks for the value of the data node from DataBrokerService
1143 based on InstanceIdentifier.
1145 3. DataBrokerService returns CompositeNode as data.
1147 4. StructuredDataToXmlProvider or StructuredDataToJsonProvider is called
1148 based on the **Accept** field from the http request. These two
1149 providers can transform CompositeNode regarding DataSchemaNode to an
1150 XML or JSON document.
1152 5. XML or JSON is returned as the answer on the request from the client.
1157 Figure 2 shows the PUT operation with the URI restconf/config/M:N where
1158 M is the module name, and N is the node name. Data is sent in the
1159 request either in the XML or JSON format.
1161 .. figure:: ./images/Put.png
1166 1. Input data is sent to JsonToCompositeNodeProvider or
1167 XmlToCompositeNodeProvider. The correct provider is selected based on
1168 the Content-Type field from the http request. These two providers can
1169 transform input data to CompositeNode. However, this CompositeNode
1170 does not contain enough information for transactions.
1172 2. The requested URI is translated into InstanceIdentifier which points
1173 to the data node. DataSchemaNode conforming to the data node is
1174 obtained during this translation. If the data node is behind the
1175 mount point, the MountInstance is obtained as well.
1177 3. CompositeNode can be normalized by adding additional information from
1180 4. RESTCONF begins the transaction, and puts CompositeNode with
1181 InstanceIdentifier into it. The response on the request from the
1182 client is the status code which depends on the result from the
1188 1. Create a new flow on the switch openflow:1 in table 2.
1195 URI: http://192.168.11.1:8080/restconf/config/opendaylight-inventory:nodes/node/openflow:1/table/2
1196 Content-Type: application/xml
1200 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
1202 xmlns="urn:opendaylight:flow:inventory">
1203 <strict>false</strict>
1215 <table_id>2</table_id>
1217 <cookie_mask>10</cookie_mask>
1218 <out_port>10</out_port>
1219 <installHw>false</installHw>
1220 <out_group>2</out_group>
1227 <ipv4-destination>10.0.0.1/24</ipv4-destination>
1229 <hard-timeout>0</hard-timeout>
1231 <idle-timeout>0</idle-timeout>
1232 <flow-name>FooXf22</flow-name>
1233 <priority>2</priority>
1234 <barrier>false</barrier>
1241 Status: 204 No Content
1243 1. Change *strict* to *true* in the previous flow.
1250 URI: http://192.168.11.1:8080/restconf/config/opendaylight-inventory:nodes/node/openflow:1/table/2/flow/111
1251 Content-Type: application/xml
1255 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
1257 xmlns="urn:opendaylight:flow:inventory">
1258 <strict>true</strict>
1270 <table_id>2</table_id>
1272 <cookie_mask>10</cookie_mask>
1273 <out_port>10</out_port>
1274 <installHw>false</installHw>
1275 <out_group>2</out_group>
1282 <ipv4-destination>10.0.0.1/24</ipv4-destination>
1284 <hard-timeout>0</hard-timeout>
1286 <idle-timeout>0</idle-timeout>
1287 <flow-name>FooXf22</flow-name>
1288 <priority>2</priority>
1289 <barrier>false</barrier>
1298 1. Show flow: check that *strict* is *true*.
1305 URI: http://192.168.11.1:8080/restconf/config/opendaylight-inventory:nodes/node/openflow:1/table/2/flow/111
1306 Accept: application/xml
1316 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
1318 xmlns="urn:opendaylight:flow:inventory">
1319 <strict>true</strict>
1331 <table_id>2</table_id>
1333 <cookie_mask>10</cookie_mask>
1334 <out_port>10</out_port>
1335 <installHw>false</installHw>
1336 <out_group>2</out_group>
1343 <ipv4-destination>10.0.0.1/24</ipv4-destination>
1345 <hard-timeout>0</hard-timeout>
1347 <idle-timeout>0</idle-timeout>
1348 <flow-name>FooXf22</flow-name>
1349 <priority>2</priority>
1350 <barrier>false</barrier>
1353 1. Delete the flow created.
1360 URI: http://192.168.11.1:8080/restconf/config/opendaylight-inventory:nodes/node/openflow:1/table/2/flow/111