7 OpenDaylight Controller is Java-based, model-driven controller using
8 YANG as its modeling language for various aspects of the system and
9 applications and with its components serves as a base platform for other
10 OpenDaylight applications.
12 The OpenDaylight Controller relies on the following technologies:
14 - **OSGI** - This framework is the back-end of OpenDaylight as it
15 allows dynamically loading of bundles and packages JAR files, and
16 binding bundles together for exchanging information.
18 - **Karaf** - Application container built on top of OSGI, which
19 simplifies operational aspects of packaging and installing
22 - **YANG** - a data modeling language used to model configuration and
23 state data manipulated by the applications, remote procedure calls,
26 The OpenDaylight Controller provides following model-driven subsystems
27 as a foundation for Java applications:
29 - **`Config Subsystem <#_config_subsystem>`__** - an activation,
30 dependency-injection and configuration framework, which allows
31 two-phase commits of configuration and dependency-injection, and
32 allows for run-time rewiring.
34 - **`MD-SAL <#_md_sal_overview>`__** - messaging and data storage
35 functionality for data, notifications and RPCs modeled by application
36 developers. MD-SAL uses YANG as the modeling for both interface and
37 data definitions, and provides a messaging and data-centric runtime
38 for such services based on YANG modeling.
40 - **MD-SAL Clustering** - enables cluster support for core MD-SAL
41 functionality and provides location-transparent accesss to
44 The OpenDaylight Controller supports external access to applications and
45 data using following model-driven protocols:
47 - **NETCONF** - XML-based RPC protocol, which provides abilities for
48 client to invoke YANG-modeled RPCs, receive notifications and to
49 read, modify and manipulate YANG modeled data.
51 - **RESTCONF** - HTTP-based protocol, which provides REST-like APIs to
52 manipulate YANG modeled data and invoke YANG modeled RPCs, using XML
53 or JSON as payload format.
60 The Model-Driven Service Adaptation Layer (MD-SAL) is message-bus
61 inspired extensible middleware component that provides messaging and
62 data storage functionality based on data and interface models defined by
63 application developers (i.e. user-defined models).
67 - Defines a **common-layer, concepts, data model building blocks and
68 messaging patterns** and provides infrastructure / framework for
69 applications and inter-application communication.
71 - Provide common support for user-defined transport and payload
72 formats, including payload serialization and adaptation (e.g. binary,
75 The MD-SAL uses **YANG** as the modeling language for both interface and
76 data definitions, and provides a messaging and data-centric runtime for
77 such services based on YANG modeling.
79 | The MD-SAL provides two different API types (flavours):
81 - **MD-SAL Binding:** MD-SAL APIs which extensively uses APIs and
82 classes generated from YANG models, which provides compile-time
85 - **MD-SAL DOM:** (Document Object Model) APIs which uses DOM-like
86 representation of data, which makes them more powerful, but provides
87 less compile-time safety.
91 Model-driven nature of the MD-SAL and **DOM**-based APIs allows for
92 behind-the-scene API and payload type mediation and transformation
93 to facilitate seamless communication between applications - this
94 enables for other components and applications to provide connectors
95 / expose different set of APIs and derive most of its functionality
96 purely from models, which all existing code can benefit from without
97 modification. For example **RESTCONF Connector** is an application
98 built on top of MD-SAL and exposes YANG-modeled application APIs
99 transparently via HTTP and adds support for XML and JSON payload
105 Basic concepts are building blocks which are used by applications, and
106 from which MD-SAL uses to define messaging patterns and to provide
107 services and behavior based on developer-supplied YANG models.
110 All state-related data are modeled and represented as data tree,
111 with possibility to address any element / subtree
113 - **Operational Data Tree** - Reported state of the system,
114 published by the providers using MD-SAL. Represents a feedback
115 loop for applications to observe state of the network / system.
117 - **Configuration Data Tree** - Intended state of the system or
118 network, populated by consumers, which expresses their intention.
121 Unique identifier of node / subtree in data tree, which provides
122 unambiguous information, how to reference and retrieve node /
123 subtree from conceptual data trees.
126 Asynchronous transient event which may be consumed by subscribers
127 and they may act upon it
130 asynchronous request-reply message pair, when request is triggered
131 by consumer, send to the provider, which in future replies with
136 In MD-SAL terminology, the term *RPC* is used to define the
137 input and output for a procedure (function) that is to be
138 provided by a provider, and mediated by the MD-SAL, that means
139 it may not result in remote call.
144 MD-SAL provides several messaging patterns using broker derived from
145 basic concepts, which are intended to transfer YANG modeled data between
146 applications to provide data-centric integration between applications
147 instead of API-centric integration.
149 - **Unicast communication**
151 - **Remote Procedure Calls** - unicast between consumer and
152 provider, where consumer sends **request** message to provider,
153 which asynchronously responds with **reply** message
155 - **Publish / Subscribe**
157 - **Notifications** - multicast transient message which is published
158 by provider and is delivered to subscribers
160 - **Data Change Events** - multicast asynchronous event, which is
161 sent by data broker if there is change in conceptual data tree,
162 and is delivered to subscribers
164 - **Transactional access to Data Tree**
166 - Transactional **reads** from conceptual **data tree** - read-only
167 transactions with isolation from other running transactions.
169 - Transactional **modification** to conceptual **data tree** - write
170 transactions with isolation from other running transactions.
172 - **Transaction chaining**
174 MD-SAL Data Transactions
175 ------------------------
177 MD-SAL **Data Broker** provides transactional access to conceptual
178 **data trees** representing configuration and operational state.
182 **Data tree** usually represents state of the modeled data, usually
183 this is state of controller, applications and also external systems
186 **Transactions** provide **`stable and isolated
187 view <#_transaction_isolation>`__** from other currently running
188 transactions. The state of running transaction and underlying data tree
189 is not affected by other concurrently running transactions.
192 Transaction provides only modification capabilities, but does not
193 provide read capabilities. Write-only transaction is allocated using
194 ``newWriteOnlyTransaction()``.
198 This allows less state tracking for write-only transactions and
199 allows MD-SAL Clustering to optimize internal representation of
200 transaction in cluster.
203 Transaction provides both read and write capabilities. It is
204 allocated using ``newReadWriteTransaction()``.
207 Transaction provides stable read-only view based on current data
208 tree. Read-only view is not affected by any subsequent write
209 transactions. Read-only transaction is allocated using
210 ``newReadOnlyTransaction()``.
214 If an application needs to observe changes itself in data tree,
215 it should use **data tree listeners** instead of read-only
216 transactions and polling data tree.
218 Transactions may be allocated using the **data broker** itself or using
219 **transaction chain**. In the case of **transaction chain**, the new
220 allocated transaction is not based on current state of data tree, but
221 rather on state introduced by previous transaction from the same chain,
222 even if the commit for previous transaction has not yet occurred (but
223 transaction was submitted).
225 Write-Only & Read-Write Transaction
226 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
228 Write-Only and Read-Write transactions provide modification capabilities
229 for the conceptual data trees.
231 1. application allocates new transactions using
232 ``newWriteOnlyTransaction()`` or ``newReadWriteTransaction()``.
234 2. application `modifies data tree <#_modification_of_data_tree>`__
235 using ``put``, ``merge`` and/or ``delete``.
237 3. application finishes transaction using
238 ```submit()`` <#_submitting_transaction>`__, which seals transaction
239 and submits it to be processed.
241 4. application observes the result of the transaction commit using
242 either blocking or asynchronous calls.
244 The **initial state** of the write transaction is a **stable snapshot**
245 of the current data tree state captured when transaction was created and
246 it’s state and underlying data tree are not affected by other
247 concurrently running transactions.
249 Write transactions are **isolated** from other concurrent write
250 transactions. All **`writes are local <#_transaction_local_state>`__**
251 to the transaction and represents only a **proposal of state change**
252 for data tree and **are not visible** to any other concurrently running
253 transactions (including read-only transactions).
255 The transaction **`commit may fail <#_commit_failure_scenarios>`__** due
256 to failing verification of data or concurrent transaction modifying and
257 affected data in an incompatible way.
259 Modification of Data Tree
260 ^^^^^^^^^^^^^^^^^^^^^^^^^
262 Write-only and read-write transaction provides following methods to
268 <T> void put(LogicalDatastoreType store, InstanceIdentifier<T> path, T data);
270 Stores a piece of data at a specified path. This acts as an **add /
271 replace** operation, which is to say that whole subtree will be
272 replaced by the specified data.
277 <T> void merge(LogicalDatastoreType store, InstanceIdentifier<T> path, T data);
279 Merges a piece of data with the existing data at a specified path.
280 Any **pre-existing data** which are not explicitly overwritten
281 **will be preserved**. This means that if you store a container, its
282 child subtrees will be merged.
287 void delete(LogicalDatastoreType store, InstanceIdentifier<?> path);
289 Removes a whole subtree from a specified path.
291 Submitting transaction
292 ^^^^^^^^^^^^^^^^^^^^^^
294 Transaction is submitted to be processed and committed using following
299 CheckedFuture<Void,TransactionCommitFailedException> submit();
301 Applications publish the changes proposed in the transaction by calling
302 ``submit()`` on the transaction. This **seals the transaction**
303 (preventing any further writes using this transaction) and submits it to
304 be processed and applied to global conceptual data tree. The
305 ``submit()`` method does not block, but rather returns
306 ``ListenableFuture``, which will complete successfully once processing
307 of transaction is finished and changes are applied to data tree. If
308 **commit** of data failed, the future will fail with
309 ``TransactionFailedException``.
311 Application may listen on commit state asynchronously using
312 ``ListenableFuture``.
316 Futures.addCallback( writeTx.submit(), new FutureCallback<Void>() {
317 public void onSuccess( Void result ) {
318 LOG.debug("Transaction committed successfully.");
321 public void onFailure( Throwable t ) {
322 LOG.error("Commit failed.",e);
326 - Submits ``writeTx`` and registers application provided
327 ``FutureCallback`` on returned future.
329 - Invoked when future completed successfully - transaction ``writeTx``
330 was successfully committed to data tree.
332 - Invoked when future failed - commit of transaction ``writeTx``
333 failed. Supplied exception provides additional details and cause of
336 If application need to block till commit is finished it may use
337 ``checkedGet()`` to wait till commit is finished.
342 writeTx.submit().checkedGet();
343 } catch (TransactionCommitFailedException e) {
344 LOG.error("Commit failed.",e);
347 - Submits ``writeTx`` and blocks till commit of ``writeTx`` is
348 finished. If commit fails ``TransactionCommitFailedException`` will
351 - Catches ``TransactionCommitFailedException`` and logs it.
353 Transaction local state
354 ^^^^^^^^^^^^^^^^^^^^^^^
356 Read-Write transactions maintain transaction-local state, which renders
357 all modifications as if they happened, but this is only local to
360 Reads from the transaction returns data as if the previous modifications
361 in transaction already happened.
363 Let assume initial state of data tree for ``PATH`` is ``A``.
367 ReadWriteTransaction rwTx = broker.newReadWriteTransaction();
369 rwRx.read(OPERATIONAL,PATH).get();
370 rwRx.put(OPERATIONAL,PATH,B);
371 rwRx.read(OPERATIONAL,PATH).get();
372 rwRx.put(OPERATIONAL,PATH,C);
373 rwRx.read(OPERATIONAL,PATH).get();
375 - Allocates new ``ReadWriteTransaction``.
377 - Read from ``rwTx`` will return value ``A`` for ``PATH``.
379 - Writes value ``B`` to ``PATH`` using ``rwTx``.
381 - Read will return value ``B`` for ``PATH``, since previous write
382 occurred in same transaction.
384 - Writes value ``C`` to ``PATH`` using ``rwTx``.
386 - Read will return value ``C`` for ``PATH``, since previous write
387 occurred in same transaction.
389 Transaction isolation
390 ~~~~~~~~~~~~~~~~~~~~~
392 Running (not submitted) transactions are isolated from each other and
393 changes done in one transaction are not observable in other currently
396 Lets assume initial state of data tree for ``PATH`` is ``A``.
400 ReadOnlyTransaction txRead = broker.newReadOnlyTransaction();
401 ReadWriteTransaction txWrite = broker.newReadWriteTransaction();
403 txRead.read(OPERATIONAL,PATH).get();
404 txWrite.put(OPERATIONAL,PATH,B);
405 txWrite.read(OPERATIONAL,PATH).get();
406 txWrite.submit().get();
407 txRead.read(OPERATIONAL,PATH).get();
408 txAfterCommit = broker.newReadOnlyTransaction();
409 txAfterCommit.read(OPERATIONAL,PATH).get();
411 - Allocates read only transaction, which is based on data tree which
412 contains value ``A`` for ``PATH``.
414 - Allocates read write transaction, which is based on data tree which
415 contains value ``A`` for ``PATH``.
417 - Read from read-only transaction returns value ``A`` for ``PATH``.
419 - Data tree is updated using read-write transaction, ``PATH`` contains
420 ``B``. Change is not public and only local to transaction.
422 - Read from read-write transaction returns value ``B`` for ``PATH``.
424 - Submits changes in read-write transaction to be committed to data
425 tree. Once commit will finish, changes will be published and ``PATH``
426 will be updated for value ``B``. Previously allocated transactions
427 are not affected by this change.
429 - Read from previously allocated read-only transaction still returns
430 value ``A`` for ``PATH``, since it provides stable and isolated view.
432 - Allocates new read-only transaction, which is based on data tree,
433 which contains value ``B`` for ``PATH``.
435 - Read from new read-only transaction return value ``B`` for ``PATH``
436 since read-write transaction was committed.
440 Examples contain blocking calls on future only to illustrate that
441 action happened after other asynchronous action. The use of the
442 blocking call ``ListenableFuture#get()`` is discouraged for most
443 use-cases and you should use
444 ``Futures#addCallback(ListenableFuture, FutureCallback)`` to listen
445 asynchronously for result.
447 Commit failure scenarios
448 ~~~~~~~~~~~~~~~~~~~~~~~~
450 A transaction commit may fail because of following reasons:
452 Optimistic Lock Failure
453 Another transaction finished earlier and **modified the same node in
454 a non-compatible way**. The commit (and the returned future) will
455 fail with an ``OptimisticLockFailedException``.
457 It is the responsibility of the caller to create a new transaction
458 and submit the same modification again in order to update data tree.
462 ``OptimisticLockFailedException`` usually exposes **multiple
463 writers** to the same data subtree, which may conflict on same
466 In most cases, retrying may result in a probability of success.
468 There are scenarios, albeit unusual, where any number of retries
469 will not succeed. Therefore it is strongly recommended to limit
470 the number of retries (2 or 3) to avoid an endless loop.
473 The data change introduced by this transaction **did not pass
474 validation** by commit handlers or data was incorrectly structured.
475 The returned future will fail with a
476 ``DataValidationFailedException``. User **should not retry** to
477 create new transaction with same data, since it probably will fail
480 Example conflict of two transactions
481 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
483 This example illustrates two concurrent transactions, which derived from
484 same initial state of data tree and proposes conflicting modifications.
488 WriteTransaction txA = broker.newWriteTransaction();
489 WriteTransaction txB = broker.newWriteTransaction();
491 txA.put(CONFIGURATION, PATH, A);
492 txB.put(CONFIGURATION, PATH, B);
494 CheckedFuture<?,?> futureA = txA.submit();
495 CheckedFuture<?,?> futureB = txB.submit();
497 - Updates ``PATH`` to value ``A`` using ``txA``
499 - Updates ``PATH`` to value ``B`` using ``txB``
501 - Seals & submits ``txA``. The commit will be processed asynchronously
502 and data tree will be updated to contain value ``A`` for ``PATH``.
503 The returned ‘ListenableFuture’ will complete successfully once state
504 is applied to data tree.
506 - Seals & submits ``txB``. Commit of ``txB`` will fail, because
507 previous transaction also modified path in a concurrent way. The
508 state introduced by ``txB`` will not be applied. The returned
509 ``ListenableFuture`` will fail with ``OptimisticLockFailedException``
510 exception, which indicates that concurrent transaction prevented the
511 submitted transaction from being applied.
513 Example asynchronous retry-loop
514 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
518 private void doWrite( final int tries ) {
519 WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction();
521 MyDataObject data = ...;
522 InstanceIdentifier<MyDataObject> path = ...;
523 writeTx.put( LogicalDatastoreType.OPERATIONAL, path, data );
525 Futures.addCallback( writeTx.submit(), new FutureCallback<Void>() {
526 public void onSuccess( Void result ) {
530 public void onFailure( Throwable t ) {
531 if( t instanceof OptimisticLockFailedException && (( tries - 1 ) > 0)) {
532 doWrite( tries - 1 );
540 Concurrent change compatibility
541 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
543 There are several sets of changes which could be considered incompatible
544 between two transactions which are derived from same initial state.
545 Rules for conflict detection applies recursively for each subtree level.
547 Following table shows state changes and failures between two concurrent
548 transactions, which are based on same initial state, ``tx1`` is
549 submitted before ``tx2``.
551 INFO: Following tables stores numeric values and shows data using
552 ``toString()`` to simplify examples.
554 +--------------------+--------------------+--------------------+--------------------+
555 | Initial state | tx1 | tx2 | Observable Result |
556 +====================+====================+====================+====================+
557 | Empty | ``put(A,1)`` | ``put(A,2)`` | ``tx2`` will fail, |
558 | | | | value of ``A`` is |
560 +--------------------+--------------------+--------------------+--------------------+
561 | Empty | ``put(A,1)`` | ``merge(A,2)`` | value of ``A`` is |
563 +--------------------+--------------------+--------------------+--------------------+
564 | Empty | ``merge(A,1)`` | ``put(A,2)`` | ``tx2`` will fail, |
565 | | | | value of ``A`` is |
567 +--------------------+--------------------+--------------------+--------------------+
568 | Empty | ``merge(A,1)`` | ``merge(A,2)`` | ``A`` is ``2`` |
569 +--------------------+--------------------+--------------------+--------------------+
570 | A=0 | ``put(A,1)`` | ``put(A,2)`` | ``tx2`` will fail, |
571 | | | | ``A`` is ``1`` |
572 +--------------------+--------------------+--------------------+--------------------+
573 | A=0 | ``put(A,1)`` | ``merge(A,2)`` | ``A`` is ``2`` |
574 +--------------------+--------------------+--------------------+--------------------+
575 | A=0 | ``merge(A,1)`` | ``put(A,2)`` | ``tx2`` will fail, |
576 | | | | value of ``A`` is |
578 +--------------------+--------------------+--------------------+--------------------+
579 | A=0 | ``merge(A,1)`` | ``merge(A,2)`` | ``A`` is ``2`` |
580 +--------------------+--------------------+--------------------+--------------------+
581 | A=0 | ``delete(A)`` | ``put(A,2)`` | ``tx2`` will fail, |
582 | | | | ``A`` does not |
584 +--------------------+--------------------+--------------------+--------------------+
585 | A=0 | ``delete(A)`` | ``merge(A,2)`` | ``A`` is ``2`` |
586 +--------------------+--------------------+--------------------+--------------------+
588 Table: Concurrent change resolution for leaves and leaf-list items
590 +--------------------+--------------------+--------------------+--------------------+
591 | Initial state | ``tx1`` | ``tx2`` | Result |
592 +====================+====================+====================+====================+
593 | Empty | put(TOP,[]) | put(TOP,[]) | ``tx2`` will fail, |
594 | | | | state is TOP=[] |
595 +--------------------+--------------------+--------------------+--------------------+
596 | Empty | put(TOP,[]) | merge(TOP,[]) | TOP=[] |
597 +--------------------+--------------------+--------------------+--------------------+
598 | Empty | put(TOP,[FOO=1]) | put(TOP,[BAR=1]) | ``tx2`` will fail, |
600 | | | | TOP=[FOO=1] |
601 +--------------------+--------------------+--------------------+--------------------+
602 | Empty | put(TOP,[FOO=1]) | merge(TOP,[BAR=1]) | TOP=[FOO=1,BAR=1] |
603 +--------------------+--------------------+--------------------+--------------------+
604 | Empty | merge(TOP,[FOO=1]) | put(TOP,[BAR=1]) | ``tx2`` will fail, |
606 | | | | TOP=[FOO=1] |
607 +--------------------+--------------------+--------------------+--------------------+
608 | Empty | merge(TOP,[FOO=1]) | merge(TOP,[BAR=1]) | TOP=[FOO=1,BAR=1] |
609 +--------------------+--------------------+--------------------+--------------------+
610 | TOP=[] | put(TOP,[FOO=1]) | put(TOP,[BAR=1]) | ``tx2`` will fail, |
612 | | | | TOP=[FOO=1] |
613 +--------------------+--------------------+--------------------+--------------------+
614 | TOP=[] | put(TOP,[FOO=1]) | merge(TOP,[BAR=1]) | state is |
615 | | | | TOP=[FOO=1,BAR=1] |
616 +--------------------+--------------------+--------------------+--------------------+
617 | TOP=[] | merge(TOP,[FOO=1]) | put(TOP,[BAR=1]) | ``tx2`` will fail, |
619 | | | | TOP=[FOO=1] |
620 +--------------------+--------------------+--------------------+--------------------+
621 | TOP=[] | merge(TOP,[FOO=1]) | merge(TOP,[BAR=1]) | state is |
622 | | | | TOP=[FOO=1,BAR=1] |
623 +--------------------+--------------------+--------------------+--------------------+
624 | TOP=[] | delete(TOP) | put(TOP,[BAR=1]) | ``tx2`` will fail, |
625 | | | | state is empty |
627 +--------------------+--------------------+--------------------+--------------------+
628 | TOP=[] | delete(TOP) | merge(TOP,[BAR=1]) | state is |
629 | | | | TOP=[BAR=1] |
630 +--------------------+--------------------+--------------------+--------------------+
631 | TOP=[] | put(TOP/FOO,1) | put(TOP/BAR,1]) | state is |
632 | | | | TOP=[FOO=1,BAR=1] |
633 +--------------------+--------------------+--------------------+--------------------+
634 | TOP=[] | put(TOP/FOO,1) | merge(TOP/BAR,1) | state is |
635 | | | | TOP=[FOO=1,BAR=1] |
636 +--------------------+--------------------+--------------------+--------------------+
637 | TOP=[] | merge(TOP/FOO,1) | put(TOP/BAR,1) | state is |
638 | | | | TOP=[FOO=1,BAR=1] |
639 +--------------------+--------------------+--------------------+--------------------+
640 | TOP=[] | merge(TOP/FOO,1) | merge(TOP/BAR,1) | state is |
641 | | | | TOP=[FOO=1,BAR=1] |
642 +--------------------+--------------------+--------------------+--------------------+
643 | TOP=[] | delete(TOP) | put(TOP/BAR,1) | ``tx2`` will fail, |
644 | | | | state is empty |
646 +--------------------+--------------------+--------------------+--------------------+
647 | TOP=[] | delete(TOP) | merge(TOP/BAR,1] | ``tx2`` will fail, |
648 | | | | state is empty |
650 +--------------------+--------------------+--------------------+--------------------+
651 | TOP=[FOO=1] | put(TOP/FOO,2) | put(TOP/BAR,1) | state is |
652 | | | | TOP=[FOO=2,BAR=1] |
653 +--------------------+--------------------+--------------------+--------------------+
654 | TOP=[FOO=1] | put(TOP/FOO,2) | merge(TOP/BAR,1) | state is |
655 | | | | TOP=[FOO=2,BAR=1] |
656 +--------------------+--------------------+--------------------+--------------------+
657 | TOP=[FOO=1] | merge(TOP/FOO,2) | put(TOP/BAR,1) | state is |
658 | | | | TOP=[FOO=2,BAR=1] |
659 +--------------------+--------------------+--------------------+--------------------+
660 | TOP=[FOO=1] | merge(TOP/FOO,2) | merge(TOP/BAR,1) | state is |
661 | | | | TOP=[FOO=2,BAR=1] |
662 +--------------------+--------------------+--------------------+--------------------+
663 | TOP=[FOO=1] | delete(TOP/FOO) | put(TOP/BAR,1) | state is |
664 | | | | TOP=[BAR=1] |
665 +--------------------+--------------------+--------------------+--------------------+
666 | TOP=[FOO=1] | delete(TOP/FOO) | merge(TOP/BAR,1] | state is |
667 | | | | TOP=[BAR=1] |
668 +--------------------+--------------------+--------------------+--------------------+
670 Table: Concurrent change resolution for containers, lists, list items
675 The MD-SAL provides a way to deliver Remote Procedure Calls (RPCs) to a
676 particular implementation based on content in the input as it is modeled
677 in YANG. This part of the the RPC input is referred to as a **context
680 The MD-SAL does not dictate the name of the leaf which is used for this
681 RPC routing, but provides necessary functionality for YANG model author
682 to define their **context reference** in their model of RPCs.
684 MD-SAL routing behavior is modeled using following terminology and its
685 application to YANG models:
688 Logical type of RPC routing. Context type is modeled as YANG
689 ``identity`` and is referenced in model to provide scoping
693 Conceptual location in data tree, which represents context in which
694 RPC could be executed. Context instance usually represent logical
695 point to which RPC execution is attached.
698 Field of RPC input payload which contains Instance Identifier
699 referencing **context instance** in which the RPC should be
702 Modeling a routed RPC
703 ~~~~~~~~~~~~~~~~~~~~~
705 In order to define routed RPCs, the YANG model author needs to declare
706 (or reuse) a **context type**, set of possible **context instances** and
707 finally RPCs which will contain **context reference** on which they will
710 Declaring a routing context type
711 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
715 identity node-context {
716 description "Identity used to mark node context";
719 This declares an identity named ``node-context``, which is used as
720 marker for node-based routing and is used in other places to reference
723 Declaring possible context instances
724 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
726 In order to define possible values of **context instances** for routed
727 RPCs, we need to model that set accordingly using ``context-instance``
728 extension from the ``yang-ext`` model.
732 import yang-ext { prefix ext; }
734 /** Base structure **/
738 ext:context-instance "node-context";
739 // other node-related fields would go here
743 The statement ``ext:context-instance "node-context";`` marks any element
744 of the ``list node`` as a possible valid **context instance** in
745 ``node-context`` based routing.
749 The existence of a **context instance** node in operational or
750 config data tree is not strongly tied to existence of RPC
753 For most routed RPC models, there is relationship between the data
754 present in operational data tree and RPC implementation
755 availability, but this is not enforced by MD-SAL. This provides some
756 flexibility for YANG model writers to better specify their routing
757 model and requirements for implementations. Details when RPC
758 implementations are available should be documented in YANG model.
760 If user invokes RPC with a **context instance** that has no
761 registered implementation, the RPC invocation will fail with the
762 exception ``DOMRpcImplementationNotAvailableException``.
764 Declaring a routed RPC
765 ^^^^^^^^^^^^^^^^^^^^^^
767 To declare RPC to be routed based on ``node-context`` we need to add
768 leaf of ``instance-identifier`` type (or type derived from
769 ``instance-identifier``) to the RPC and mark it as **context
772 This is achieved using YANG extension ``context-reference`` from
773 ``yang-ext`` model on leaf, which will be used for RPC routing.
777 rpc example-routed-rpc {
780 ext:context-reference "node-context";
781 type "instance-identifier";
783 // other input to the RPC would go here
787 The statement ``ext:context-reference "node-context"`` marks
788 ``leaf node`` as **context reference** of type ``node-context``. The
789 value of this leaf, will be used by the MD-SAL to select the particular
790 RPC implementation that registered itself as the implementation of the
791 RPC for particular **context instance**.
796 From a user perspective (e.g. invoking RPCs) there is no difference
797 between routed and non-routed RPCs. Routing information is just an
798 additional leaf in RPC which must be populated.
800 Implementing a routed RPC
801 ~~~~~~~~~~~~~~~~~~~~~~~~~
805 Registering implementations
806 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
808 Implementations of a routed RPC (e.g., southbound plugins) will specify
809 an instance-identifier for the **context reference** (in this case a
810 node) for which they want to provide an implementation during
811 registration. Consumers, e.g., those calling the RPC are required to
812 specify that instance-identifier (in this case the identifier of a node)
815 Simple code which showcases that for add-flow via Binding-Aware APIs
816 (`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>`__
822 62 public void onSessionInitiated(ProviderContext session) {
823 63 assertNotNull(session);
824 64 firstReg = session.addRoutedRpcImplementation(SalFlowService.class, salFlowService1);
827 Line 64: We are registering salFlowService1 as implementation of
832 107 NodeRef nodeOne = createNodeRef("foo:node:1");
834 110 * Provider 1 registers path of node 1
836 112 firstReg.registerPath(NodeContext.class, nodeOne);
838 Line 107: We are creating NodeRef (encapsulation of InstanceIdentifier)
841 Line 112: We register salFlowService1 as implementation for nodeOne.
843 The salFlowService1 will be executed only for RPCs which contains
844 Instance Identifier for foo:node:1.
846 OpenDaylight Controller MD-SAL: RESTCONF
847 ----------------------------------------
849 RESCONF operations overview
850 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
852 | RESTCONF allows access to datastores in the controller.
853 | There are two datastores:
855 - Config: Contains data inserted via controller
857 - Operational: Contains other data
861 | Each request must start with the URI /restconf.
862 | RESTCONF listens on port 8080 for HTTP requests.
864 RESTCONF supports **OPTIONS**, **GET**, **PUT**, **POST**, and
865 **DELETE** operations. Request and response data can either be in the
866 XML or JSON format. XML structures according to yang are defined at:
867 `XML-YANG <http://tools.ietf.org/html/rfc6020>`__. JSON structures are
869 `JSON-YANG <http://tools.ietf.org/html/draft-lhotka-netmod-yang-json-02>`__.
870 Data in the request must have a correctly set **Content-Type** field in
871 the http header with the allowed value of the media type. The media type
872 of the requested data has to be set in the **Accept** field. Get the
873 media types for each resource by calling the OPTIONS operation. Most of
874 the paths of the pathsRestconf endpoints use `Instance
875 Identifier <https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Concepts#Instance_Identifier>`__.
876 ``<identifier>`` is used in the explanation of the operations.
880 - It must start with <moduleName>:<nodeName> where <moduleName> is a
881 name of the module and <nodeName> is the name of a node in the
882 module. It is sufficient to just use <nodeName> after
883 <moduleName>:<nodeName>. Each <nodeName> has to be separated by /.
885 - <nodeName> can represent a data node which is a list or container
886 yang built-in type. If the data node is a list, there must be defined
887 keys of the list behind the data node name for example,
888 <nodeName>/<valueOfKey1>/<valueOfKey2>.
890 - | The format <moduleName>:<nodeName> has to be used in this case as
892 | Module A has node A1. Module B augments node A1 by adding node X.
893 Module C augments node A1 by adding node X. For clarity, it has to
894 be known which node is X (for example: C:X). For more details about
895 encoding, see: `RESTCONF 02 - Encoding YANG Instance Identifiers in
897 URI. <http://tools.ietf.org/html/draft-bierman-netconf-restconf-02#section-5.3.1>`__
902 | A Node can be behind a mount point. In this case, the URI has to be in
903 format <identifier>/**yang-ext:mount**/<identifier>. The first
904 <identifier> is the path to a mount point and the second <identifier>
905 is the path to a node behind the mount point. A URI can end in a mount
906 point itself by using <identifier>/**yang-ext:mount**.
907 | More information on how to actually use mountpoints is available at:
909 Controller:Config:Examples:Netconf <https://wiki.opendaylight.org/view/OpenDaylight_Controller:Config:Examples:Netconf>`__.
917 - Returns the XML description of the resources with the required
918 request and response media types in Web Application Description
921 GET /restconf/config/<identifier>
922 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
924 - Returns a data node from the Config datastore.
926 - <identifier> points to a data node which must be retrieved.
928 GET /restconf/operational/<identifier>
929 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
931 - Returns the value of the data node from the Operational datastore.
933 - <identifier> points to a data node which must be retrieved.
935 PUT /restconf/config/<identifier>
936 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
938 - Updates or creates data in the Config datastore and returns the state
941 - <identifier> points to a data node which must be stored.
947 PUT http://<controllerIP>:8080/restconf/config/module1:foo/bar
948 Content-Type: applicaton/xml
953 | **Example with mount point:**
957 PUT http://<controllerIP>:8080/restconf/config/module1:foo1/foo2/yang-ext:mount/module2:foo/bar
958 Content-Type: applicaton/xml
963 POST /restconf/config
964 ^^^^^^^^^^^^^^^^^^^^^
966 - Creates the data if it does not exist
972 POST URL: http://localhost:8080/restconf/config/
973 content-type: application/yang.data+json
979 "toaster:toasterManufacturer" : "General Electric",
980 "toaster:toasterModelNumber" : "123",
981 "toaster:toasterStatus" : "up"
985 POST /restconf/config/<identifier>
986 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
988 - Creates the data if it does not exist in the Config datastore, and
989 returns the state about success.
991 - <identifier> points to a data node where data must be stored.
993 - The root element of data must have the namespace (data are in XML) or
994 module name (data are in JSON.)
1000 POST http://<controllerIP>:8080/restconf/config/module1:foo
1001 Content-Type: applicaton/xml/
1002 <bar xmlns=“module1namespace”>
1006 **Example with mount point:**
1010 http://<controllerIP>:8080/restconf/config/module1:foo1/foo2/yang-ext:mount/module2:foo
1011 Content-Type: applicaton/xml
1012 <bar xmlns=“module2namespace”>
1016 POST /restconf/operations/<moduleName>:<rpcName>
1017 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1021 - <moduleName>:<rpcName> - <moduleName> is the name of the module and
1022 <rpcName> is the name of the RPC in this module.
1024 - The Root element of the data sent to RPC must have the name “input”.
1026 - The result can be the status code or the retrieved data having the
1027 root element “output”.
1033 POST http://<controllerIP>:8080/restconf/operations/module1:fooRpc
1034 Content-Type: applicaton/xml
1035 Accept: applicaton/xml
1040 The answer from the server could be:
1045 | **An example using a JSON payload:**
1049 POST http://localhost:8080/restconf/operations/toaster:make-toast
1050 Content-Type: application/yang.data+json
1054 "toaster:toasterDoneness" : "10",
1055 "toaster:toasterToastType":"wheat-bread"
1061 Even though this is a default for the toasterToastType value in the
1062 yang, you still need to define it.
1064 DELETE /restconf/config/<identifier>
1065 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1067 - Removes the data node in the Config datastore and returns the state
1070 - <identifier> points to a data node which must be removed.
1072 More information is available in the `RESTCONF
1073 RFC <http://tools.ietf.org/html/draft-bierman-netconf-restconf-02>`__.
1078 | RESTCONF uses these base classes:
1081 Represents the path in the data tree
1084 Used for invoking RPCs
1087 Offers manipulation with transactions and reading data from the
1091 Holds information about yang modules
1094 Returns MountInstance based on the InstanceIdentifier pointing to a
1098 Contains the SchemaContext behind the mount point
1101 Provides information about the schema node
1104 Possesses the same name as the schema node, and contains the value
1105 representing the data node value
1108 Can contain CompositeNode-s and SimpleNode-s
1113 Figure 1 shows the GET operation with URI restconf/config/M:N where M is
1114 the module name, and N is the node name.
1116 .. figure:: ./images/Get.png
1121 1. The requested URI is translated into the InstanceIdentifier which
1122 points to the data node. During this translation, the DataSchemaNode
1123 that conforms to the data node is obtained. If the data node is
1124 behind the mount point, the MountInstance is obtained as well.
1126 2. RESTCONF asks for the value of the data node from DataBrokerService
1127 based on InstanceIdentifier.
1129 3. DataBrokerService returns CompositeNode as data.
1131 4. StructuredDataToXmlProvider or StructuredDataToJsonProvider is called
1132 based on the **Accept** field from the http request. These two
1133 providers can transform CompositeNode regarding DataSchemaNode to an
1134 XML or JSON document.
1136 5. XML or JSON is returned as the answer on the request from the client.
1141 Figure 2 shows the PUT operation with the URI restconf/config/M:N where
1142 M is the module name, and N is the node name. Data is sent in the
1143 request either in the XML or JSON format.
1145 .. figure:: ./images/Put.png
1150 1. Input data is sent to JsonToCompositeNodeProvider or
1151 XmlToCompositeNodeProvider. The correct provider is selected based on
1152 the Content-Type field from the http request. These two providers can
1153 transform input data to CompositeNode. However, this CompositeNode
1154 does not contain enough information for transactions.
1156 2. The requested URI is translated into InstanceIdentifier which points
1157 to the data node. DataSchemaNode conforming to the data node is
1158 obtained during this translation. If the data node is behind the
1159 mount point, the MountInstance is obtained as well.
1161 3. CompositeNode can be normalized by adding additional information from
1164 4. RESTCONF begins the transaction, and puts CompositeNode with
1165 InstanceIdentifier into it. The response on the request from the
1166 client is the status code which depends on the result from the
1172 1. Create a new flow on the switch openflow:1 in table 2.
1179 URI: http://192.168.11.1:8080/restconf/config/opendaylight-inventory:nodes/node/openflow:1/table/2
1180 Content-Type: application/xml
1184 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
1186 xmlns="urn:opendaylight:flow:inventory">
1187 <strict>false</strict>
1199 <table_id>2</table_id>
1201 <cookie_mask>10</cookie_mask>
1202 <out_port>10</out_port>
1203 <installHw>false</installHw>
1204 <out_group>2</out_group>
1211 <ipv4-destination>10.0.0.1/24</ipv4-destination>
1213 <hard-timeout>0</hard-timeout>
1215 <idle-timeout>0</idle-timeout>
1216 <flow-name>FooXf22</flow-name>
1217 <priority>2</priority>
1218 <barrier>false</barrier>
1225 Status: 204 No Content
1227 1. Change *strict* to *true* in the previous flow.
1234 URI: http://192.168.11.1:8080/restconf/config/opendaylight-inventory:nodes/node/openflow:1/table/2/flow/111
1235 Content-Type: application/xml
1239 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
1241 xmlns="urn:opendaylight:flow:inventory">
1242 <strict>true</strict>
1254 <table_id>2</table_id>
1256 <cookie_mask>10</cookie_mask>
1257 <out_port>10</out_port>
1258 <installHw>false</installHw>
1259 <out_group>2</out_group>
1266 <ipv4-destination>10.0.0.1/24</ipv4-destination>
1268 <hard-timeout>0</hard-timeout>
1270 <idle-timeout>0</idle-timeout>
1271 <flow-name>FooXf22</flow-name>
1272 <priority>2</priority>
1273 <barrier>false</barrier>
1282 1. Show flow: check that *strict* is *true*.
1289 URI: http://192.168.11.1:8080/restconf/config/opendaylight-inventory:nodes/node/openflow:1/table/2/flow/111
1290 Accept: application/xml
1300 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
1302 xmlns="urn:opendaylight:flow:inventory">
1303 <strict>true</strict>
1315 <table_id>2</table_id>
1317 <cookie_mask>10</cookie_mask>
1318 <out_port>10</out_port>
1319 <installHw>false</installHw>
1320 <out_group>2</out_group>
1327 <ipv4-destination>10.0.0.1/24</ipv4-destination>
1329 <hard-timeout>0</hard-timeout>
1331 <idle-timeout>0</idle-timeout>
1332 <flow-name>FooXf22</flow-name>
1333 <priority>2</priority>
1334 <barrier>false</barrier>
1337 1. Delete the flow created.
1344 URI: http://192.168.11.1:8080/restconf/config/opendaylight-inventory:nodes/node/openflow:1/table/2/flow/111
1352 Websocket change event notification subscription tutorial
1353 ---------------------------------------------------------
1355 Subscribing to data change notifications makes it possible to obtain
1356 notifications about data manipulation (insert, change, delete) which are
1357 done on any specified **path** of any specified **datastore** with
1358 specific **scope**. In following examples *{odlAddress}* is address of
1359 server where ODL is running and *{odlPort}* is port on which
1360 OpenDaylight is running.
1362 Websocket notifications subscription process
1363 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1365 In this section we will learn what steps need to be taken in order to
1366 successfully subscribe to data change event notifications.
1371 In order to use event notifications you first need to call RPC that
1372 creates notification stream that you can later listen to. You need to
1373 provide three parameters to this RPC:
1375 - **path**: data store path that you plan to listen to. You can
1376 register listener on containers, lists and leaves.
1378 - **datastore**: data store type. *OPERATIONAL* or *CONFIGURATION*.
1380 - **scope**: Represents scope of data change. Possible options are:
1382 - BASE: only changes directly to the data tree node specified in the
1383 path will be reported
1385 - ONE: changes to the node and to direct child nodes will be
1388 - SUBTREE: changes anywhere in the subtree starting at the node will
1391 The RPC to create the stream can be invoked via RESCONF like this:
1394 http://{odlAddress}:{odlPort}/restconf/operations/sal-remote:create-data-change-event-subscription
1396 - HEADER: Content-Type=application/json
1406 "path": "/toaster:toaster/toaster:toasterStatus",
1407 "sal-remote-augment:datastore": "OPERATIONAL",
1408 "sal-remote-augment:scope": "ONE"
1412 The response should look something like this:
1418 "stream-name": "data-change-event-subscription/toaster:toaster/toaster:toasterStatus/datastore=CONFIGURATION/scope=SUBTREE"
1422 **stream-name** is important because you will need to use it when you
1423 subscribe to the stream in the next step.
1427 Internally, this will create a new listener for *stream-name* if it
1428 did not already exist.
1433 In order to subscribe to stream and obtain WebSocket location you need
1434 to call *GET* on your stream path. The URI should generally be
1435 http://{odlAddress}:{odlPort}/restconf/streams/stream/{streamName},
1436 where *{streamName}* is the *stream-name* parameter contained in
1437 response from *create-data-change-event-subscription* RPC from the
1441 http://{odlAddress}:{odlPort}/restconf/streams/stream/data-change-event-subscription/toaster:toaster/datastore=CONFIGURATION/scope=SUBTREE
1445 The subscription call may be modified with the following query parameters defined in the RESTCONF RFC:
1447 - `filter <https://tools.ietf.org/html/draft-ietf-netconf-restconf-05#section-4.8.6>`__
1449 - `start-time <https://tools.ietf.org/html/draft-ietf-netconf-restconf-05#section-4.8.7>`__
1451 - `end-time <https://tools.ietf.org/html/draft-ietf-netconf-restconf-05#section-4.8.8>`__
1453 In addition, the following ODL extension query parameter is supported:
1455 :odl-leaf-nodes-only:
1456 If this parameter is set to "true", create and update notifications will only
1457 contain the leaf nodes modified instead of the entire subscription subtree.
1458 This can help in reducing the size of the notifications.
1460 The expected response status is 200 OK and response body should be
1461 empty. You will get your WebSocket location from **Location** header of
1462 response. For example in our particular toaster example location header
1463 would have this value:
1464 *ws://{odlAddress}:8185/toaster:toaster/datastore=CONFIGURATION/scope=SUBTREE*
1468 During this phase there is an internal check for to see if a
1469 listener for the *stream-name* from the URI exists. If not, new a
1470 new listener is registered with the DOM data broker.
1472 Receive notifications
1473 ^^^^^^^^^^^^^^^^^^^^^
1475 You should now have a data change notification stream created and have
1476 location of a WebSocket. You can use this WebSocket to listen to data
1477 change notifications. To listen to notifications you can use a
1478 JavaScript client or if you are using chrome browser you can use the
1480 Client <https://chrome.google.com/webstore/detail/simple-websocket-client/pfdhoblngboilpfeibdedpjgfnlcodoo>`__.
1482 Also, for testing purposes, there is simple Java application named
1483 WebSocketClient. The application is placed in the
1484 *-sal-rest-connector-classes.class* project. It accepts a WebSocket URI
1485 as and input parameter. After starting the utility (WebSocketClient
1486 class directly in Eclipse/InteliJ Idea) received notifications should be
1487 displayed in console.
1489 Notifications are always in XML format and look like this:
1493 <notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">
1494 <eventTime>2014-09-11T09:58:23+02:00</eventTime>
1495 <data-changed-notification xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote">
1497 <path xmlns:meae="http://netconfcentral.org/ns/toaster">/meae:toaster</path>
1498 <operation>updated</operation>
1500 <!-- updated data -->
1502 </data-change-event>
1503 </data-changed-notification>
1509 The typical use case is listening to data change events to update web
1510 page data in real-time. In this tutorial we will be using toaster as the
1513 When you call *make-toast* RPC, it sets *toasterStatus* to "down" to
1514 reflect that the toaster is busy making toast. When it finishes,
1515 *toasterStatus* is set to "up" again. We will listen to this toaster
1516 status changes in data store and will reflect it on our web page in
1517 real-time thanks to WebSocket data change notification.
1519 Simple javascript client implementation
1520 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1522 We will create simple JavaScript web application that will listen
1523 updates on *toasterStatus* leaf and update some element of our web page
1524 according to new toaster status state.
1529 First you need to create stream that you are planing to subscribe to.
1530 This can be achieved by invoking "create-data-change-event-subscription"
1531 RPC on RESTCONF via AJAX request. You need to provide data store
1532 **path** that you plan to listen on, **data store type** and **scope**.
1533 If the request is successful you can extract the **stream-name** from
1534 the response and use that to subscribe to the newly created stream. The
1535 *{username}* and *{password}* fields represent your credentials that you
1536 use to connect to OpenDaylight via RESTCONF:
1540 The default user name and password are "admin".
1542 .. code:: javascript
1544 function createStream() {
1547 url: 'http://{odlAddress}:{odlPort}/restconf/operations/sal-remote:create-data-change-event-subscription',
1550 'Authorization': 'Basic ' + btoa('{username}:{password}'),
1551 'Content-Type': 'application/json'
1553 data: JSON.stringify(
1556 'path': '/toaster:toaster/toaster:toasterStatus',
1557 'sal-remote-augment:datastore': 'OPERATIONAL',
1558 'sal-remote-augment:scope': 'ONE'
1562 }).done(function (data) {
1563 // this function will be called when ajax call is executed successfully
1564 subscribeToStream(data.output['stream-name']);
1565 }).fail(function (data) {
1566 // this function will be called when ajax call fails
1567 console.log("Create stream call unsuccessful");
1574 The Next step is to subscribe to the stream. To subscribe to the stream
1575 you need to call *GET* on
1576 *http://{odlAddress}:{odlPort}/restconf/streams/stream/{stream-name}*.
1577 If the call is successful, you get WebSocket address for this stream in
1578 **Location** parameter inside response header. You can get response
1579 header by calling *getResponseHeader(\ *Location*)* on HttpRequest
1580 object inside *done()* function call:
1582 .. code:: javascript
1584 function subscribeToStream(streamName) {
1587 url: 'http://{odlAddress}:{odlPort}/restconf/streams/stream/' + streamName;
1590 'Authorization': 'Basic ' + btoa('{username}:{password}'),
1593 ).done(function (data, textStatus, httpReq) {
1594 // we need function that has http request object parameter in order to access response headers.
1595 listenToNotifications(httpReq.getResponseHeader('Location'));
1596 }).fail(function (data) {
1597 console.log("Subscribe to stream call unsuccessful");
1601 Receive notifications
1602 ^^^^^^^^^^^^^^^^^^^^^
1604 Once you got WebSocket server location you can now connect to it and
1605 start receiving data change events. You need to define functions that
1606 will handle events on WebSocket. In order to process incoming events
1607 from OpenDaylight you need to provide a function that will handle
1608 *onmessage* events. The function must have one parameter that represents
1609 the received event object. The event data will be stored in
1610 *event.data*. The data will be in an XML format that you can then easily
1613 .. code:: javascript
1615 function listenToNotifications(socketLocation) {
1617 var notificatinSocket = new WebSocket(socketLocation);
1619 notificatinSocket.onmessage = function (event) {
1620 // we process our received event here
1621 console.log('Received toaster data change event.');
1622 $($.parseXML(event.data)).find('data-change-event').each(
1624 var operation = $(this).find('operation').text();
1625 if (operation == 'updated') {
1626 // toaster status was updated so we call function that gets the value of toasterStatus leaf
1627 updateToasterStatus();
1633 notificatinSocket.onerror = function (error) {
1634 console.log("Socket error: " + error);
1636 notificatinSocket.onopen = function (event) {
1637 console.log("Socket connection opened.");
1639 notificatinSocket.onclose = function (event) {
1640 console.log("Socket connection closed.");
1642 // if there is a problem on socket creation we get exception (i.e. when socket address is incorrect)
1644 alert("Error when creating WebSocket" + e );
1648 The *updateToasterStatus()* function represents function that calls
1649 *GET* on the path that was modified and sets toaster status in some web
1650 page element according to received data. After the WebSocket connection
1651 has been established you can test events by calling make-toast RPC via
1656 for more information about WebSockets in JavaScript visit `Writing
1658 applications <https://developer.mozilla.org/en-US/docs/WebSockets/Writing_WebSocket_client_applications>`__
1666 The Controller configuration operation has three stages:
1668 - First, a Proposed configuration is created. Its target is to replace
1669 the old configuration.
1671 - Second, the Proposed configuration is validated, and then committed.
1672 If it passes validation successfully, the Proposed configuration
1673 state will be changed to Validated.
1675 - Finally, a Validated configuration can be Committed, and the affected
1676 modules can be reconfigured.
1678 In fact, each configuration operation is wrapped in a transaction. Once
1679 a transaction is created, it can be configured, that is to say, a user
1680 can abort the transaction during this stage. After the transaction
1681 configuration is done, it is committed to the validation stage. In this
1682 stage, the validation procedures are invoked. If one or more validations
1683 fail, the transaction can be reconfigured. Upon success, the second
1684 phase commit is invoked. If this commit is successful, the transaction
1685 enters the last stage, committed. After that, the desired modules are
1686 reconfigured. If the second phase commit fails, it means that the
1687 transaction is unhealthy - basically, a new configuration instance
1688 creation failed, and the application can be in an inconsistent state.
1690 .. figure:: ./images/configuration.jpg
1691 :alt: Configuration states
1693 Configuration states
1695 .. figure:: ./images/Transaction.jpg
1696 :alt: Transaction states
1703 To secure the consistency and safety of the new configuration and to
1704 avoid conflicts, the configuration validation process is necessary.
1705 Usually, validation checks the input parameters of a new configuration,
1706 and mostly verifies module-specific relationships. The validation
1707 procedure results in a decision on whether the proposed configuration is
1713 Since there can be dependencies between modules, a change in a module
1714 configuration can affect the state of other modules. Therefore, we need
1715 to verify whether dependencies on other modules can be resolved. The
1716 Dependency Resolver acts in a manner similar to dependency injectors.
1717 Basically, a dependency tree is built.
1722 This section describes configuration system APIs and SPIs.
1727 **Module** org.opendaylight.controller.config.spi. Module is the common
1728 interface for all modules: every module must implement it. The module is
1729 designated to hold configuration attributes, validate them, and create
1730 instances of service based on the attributes. This instance must
1731 implement the AutoCloseable interface, owing to resources clean up. If
1732 the module was created from an already running instance, it contains an
1733 old instance of the module. A module can implement multiple services. If
1734 the module depends on other modules, setters need to be annotated with
1739 1. The module needs to be configured, set with all required attributes.
1741 2. The module is then moved to the commit stage for validation. If the
1742 validation fails, the module attributes can be reconfigured.
1743 Otherwise, a new instance is either created, or an old instance is
1744 reconfigured. A module instance is identified by ModuleIdentifier,
1745 consisting of the factory name and instance name.
1747 | **ModuleFactory** org.opendaylight.controller.config.spi. The
1748 ModuleFactory interface must be implemented by each module factory.
1749 | A module factory can create a new module instance in two ways:
1751 - From an existing module instance
1753 - | An entirely new instance
1754 | ModuleFactory can also return default modules, useful for
1755 populating registry with already existing configurations. A module
1756 factory implementation must have a globally unique name.
1761 +--------------------------------------+--------------------------------------+
1762 | ConfigRegistry | Represents functionality provided by |
1763 | | a configuration transaction (create, |
1764 | | destroy module, validate, or abort |
1766 +--------------------------------------+--------------------------------------+
1767 | ConfigTransactionController | Represents functionality for |
1768 | | manipulating with configuration |
1769 | | transactions (begin, commit config). |
1770 +--------------------------------------+--------------------------------------+
1771 | RuntimeBeanRegistratorAwareConfiBean | The module implementing this |
1772 | | interface will receive |
1773 | | RuntimeBeanRegistrator before |
1774 | | getInstance is invoked. |
1775 +--------------------------------------+--------------------------------------+
1780 +--------------------------------------+--------------------------------------+
1781 | RuntimeBean | Common interface for all runtime |
1783 +--------------------------------------+--------------------------------------+
1784 | RootRuntimeBeanRegistrator | Represents functionality for root |
1785 | | runtime bean registration, which |
1786 | | subsequently allows hierarchical |
1788 +--------------------------------------+--------------------------------------+
1789 | HierarchicalRuntimeBeanRegistration | Represents functionality for runtime |
1790 | | bean registration and |
1791 | | unreregistration from hierarchy |
1792 +--------------------------------------+--------------------------------------+
1797 | JMX API is purposed as a transition between the Client API and the JMX
1800 +--------------------------------------+--------------------------------------+
1801 | ConfigTransactionControllerMXBean | Extends ConfigTransactionController, |
1802 | | executed by Jolokia clients on |
1803 | | configuration transaction. |
1804 +--------------------------------------+--------------------------------------+
1805 | ConfigRegistryMXBean | Represents entry point of |
1806 | | configuration management for |
1808 +--------------------------------------+--------------------------------------+
1809 | Object names | Object Name is the pattern used in |
1810 | | JMX to locate JMX beans. It consists |
1811 | | of domain and key properties (at |
1812 | | least one key-value pair). Domain is |
1814 | | "org.opendaylight.controller". The |
1815 | | only mandatory property is "type". |
1816 +--------------------------------------+--------------------------------------+
1821 | A few samples of successful and unsuccessful transaction scenarios
1824 **Successful commit scenario**
1826 1. The user creates a transaction calling creteTransaction() method on
1829 2. ConfigRegisty creates a transaction controller, and registers the
1830 transaction as a new bean.
1832 3. Runtime configurations are copied to the transaction. The user can
1833 create modules and set their attributes.
1835 4. The configuration transaction is to be committed.
1837 5. The validation process is performed.
1839 6. After successful validation, the second phase commit begins.
1841 7. Modules proposed to be destroyed are destroyed, and their service
1842 instances are closed.
1844 8. Runtime beans are set to registrator.
1846 9. The transaction controller invokes the method getInstance on each
1849 10. The transaction is committed, and resources are either closed or
1852 | **Validation failure scenario**
1853 | The transaction is the same as the previous case until the validation
1856 1. If validation fails, (that is to day, illegal input attributes values
1857 or dependency resolver failure), the validationException is thrown
1858 and exposed to the user.
1860 2. The user can decide to reconfigure the transaction and commit again,
1861 or abort the current transaction.
1863 3. On aborted transactions, TransactionController and JMXRegistrator are
1866 4. Unregistration event is sent to ConfigRegistry.
1868 Default module instances
1869 ^^^^^^^^^^^^^^^^^^^^^^^^
1871 The configuration subsystem provides a way for modules to create default
1872 instances. A default instance is an instance of a module, that is
1873 created at the module bundle start-up (module becomes visible for
1874 configuration subsystem, for example, its bundle is activated in the
1875 OSGi environment). By default, no default instances are produced.
1877 The default instance does not differ from instances created later in the
1878 module life-cycle. The only difference is that the configuration for the
1879 default instance cannot be provided by the configuration subsystem. The
1880 module has to acquire the configuration for these instances on its own.
1881 It can be acquired from, for example, environment variables. After the
1882 creation of a default instance, it acts as a regular instance and fully
1883 participates in the configuration subsystem (It can be reconfigured or
1884 deleted in following transactions.).