The OpenDaylight Controller provides following model-driven subsystems
as a foundation for Java applications:
-- :ref:`config_subsystem` - an activation,
- dependency-injection and configuration framework, which allows
- two-phase commits of configuration and dependency-injection, and
- allows for run-time rewiring.
-
- :ref:`MD-SAL <mdsal_dev_guide>` - messaging and data storage
functionality for data, notifications and RPCs modeled by application
developers. MD-SAL uses YANG as the modeling for both interface and
RESTCONF supports **OPTIONS**, **GET**, **PUT**, **POST**, and
**DELETE** operations. Request and response data can either be in the
XML or JSON format. XML structures according to yang are defined at:
-`XML-YANG <http://tools.ietf.org/html/rfc6020>`__. JSON structures are
+`XML-YANG <https://www.rfc-editor.org/rfc/rfc6020>`__. JSON structures are
defined at:
-`JSON-YANG <http://tools.ietf.org/html/draft-lhotka-netmod-yang-json-02>`__.
+`JSON-YANG <https://datatracker.ietf.org/doc/html/draft-lhotka-netmod-yang-json-02>`__.
Data in the request must have a correctly set **Content-Type** field in
the http header with the allowed value of the media type. The media type
of the requested data has to be set in the **Accept** field. Get the
media types for each resource by calling the OPTIONS operation. Most of
the paths of the pathsRestconf endpoints use `Instance
-Identifier <https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Concepts#Instance_Identifier>`__.
+Identifier <https://wiki-archive.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Concepts#Instance_Identifier>`__.
``<identifier>`` is used in the explanation of the operations.
| **<identifier>**
be known which node is X (for example: C:X). For more details about
encoding, see: `RESTCONF 02 - Encoding YANG Instance Identifiers in
the Request
- URI. <http://tools.ietf.org/html/draft-bierman-netconf-restconf-02#section-5.3.1>`__
+ URI. <https://datatracker.ietf.org/doc/html/draft-bierman-netconf-restconf-02#section-5.3.1>`__
Mount point
~~~~~~~~~~~
point itself by using <identifier>/**yang-ext:mount**.
| More information on how to actually use mountpoints is available at:
`OpenDaylight
- Controller:Config:Examples:Netconf <https://wiki.opendaylight.org/view/OpenDaylight_Controller:Config:Examples:Netconf>`__.
+ Controller:Config:Examples:Netconf <https://wiki-archive.opendaylight.org/view/OpenDaylight_Controller:Config:Examples:Netconf>`__.
HTTP methods
~~~~~~~~~~~~
- <identifier> points to a data node which must be removed.
More information is available in the `RESTCONF
-RFC <http://tools.ietf.org/html/draft-bierman-netconf-restconf-02>`__.
+RFC <https://datatracker.ietf.org/doc/html/draft-bierman-netconf-restconf-02>`__.
How RESTCONF works
~~~~~~~~~~~~~~~~~~
Status: 200 OK
-Websocket change event notification subscription tutorial
----------------------------------------------------------
-
-Subscribing to data change notifications makes it possible to obtain
-notifications about data manipulation (insert, change, delete) which are
-done on any specified **path** of any specified **datastore** with
-specific **scope**. In following examples *{odlAddress}* is address of
-server where ODL is running and *{odlPort}* is port on which
-OpenDaylight is running.
-
-Websocket notifications subscription process
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-In this section we will learn what steps need to be taken in order to
-successfully subscribe to data change event notifications.
-
-Create stream
-^^^^^^^^^^^^^
-
-In order to use event notifications you first need to call RPC that
-creates notification stream that you can later listen to. You need to
-provide three parameters to this RPC:
-
-- **path**: data store path that you plan to listen to. You can
- register listener on containers, lists and leaves.
-
-- **datastore**: data store type. *OPERATIONAL* or *CONFIGURATION*.
-
-- **scope**: Represents scope of data change. Possible options are:
-
- - BASE: only changes directly to the data tree node specified in the
- path will be reported
-
- - ONE: changes to the node and to direct child nodes will be
- reported
-
- - SUBTREE: changes anywhere in the subtree starting at the node will
- be reported
-
-The RPC to create the stream can be invoked via RESTCONF like this:
-
-- URI:
- http://{odlAddress}:{odlPort}/restconf/operations/sal-remote:create-data-change-event-subscription
-
-- HEADER: Content-Type=application/json
-
-- OPERATION: POST
-
-- DATA:
-
- .. code:: json
-
- {
- "input": {
- "path": "/toaster:toaster/toaster:toasterStatus",
- "sal-remote-augment:datastore": "OPERATIONAL",
- "sal-remote-augment:scope": "ONE"
- }
- }
-
-The response should look something like this:
-
-.. code:: json
-
- {
- "output": {
- "stream-name": "data-change-event-subscription/toaster:toaster/toaster:toasterStatus/datastore=CONFIGURATION/scope=SUBTREE"
- }
- }
-
-**stream-name** is important because you will need to use it when you
-subscribe to the stream in the next step.
-
-.. note::
-
- Internally, this will create a new listener for *stream-name* if it
- did not already exist.
-
-Subscribe to stream
-^^^^^^^^^^^^^^^^^^^
-
-In order to subscribe to stream and obtain WebSocket location you need
-to call *GET* on your stream path. The URI should generally be
-http://{odlAddress}:{odlPort}/restconf/streams/stream/{streamName},
-where *{streamName}* is the *stream-name* parameter contained in
-response from *create-data-change-event-subscription* RPC from the
-previous step.
-
-- URI:
- http://{odlAddress}:{odlPort}/restconf/streams/stream/data-change-event-subscription/toaster:toaster/datastore=CONFIGURATION/scope=SUBTREE
-
-- OPERATION: GET
-
-The subscription call may be modified with the following query parameters defined in the RESTCONF RFC:
-
-- `filter <https://tools.ietf.org/html/draft-ietf-netconf-restconf-05#section-4.8.6>`__
-
-- `start-time <https://tools.ietf.org/html/draft-ietf-netconf-restconf-05#section-4.8.7>`__
-
-- `end-time <https://tools.ietf.org/html/draft-ietf-netconf-restconf-05#section-4.8.8>`__
-
-In addition, the following ODL extension query parameter is supported:
-
-:odl-leaf-nodes-only:
- If this parameter is set to "true", create and update notifications will only
- contain the leaf nodes modified instead of the entire subscription subtree.
- This can help in reducing the size of the notifications.
-
-:odl-skip-notification-data:
- If this parameter is set to "true", create and update notifications will only
- contain modified leaf nodes without data.
- This can help in reducing the size of the notifications.
-
-The expected response status is 200 OK and response body should be
-empty. You will get your WebSocket location from **Location** header of
-response. For example in our particular toaster example location header
-would have this value:
-*ws://{odlAddress}:8185/toaster:toaster/datastore=CONFIGURATION/scope=SUBTREE*
-
-.. note::
-
- During this phase there is an internal check for to see if a
- listener for the *stream-name* from the URI exists. If not, new a
- new listener is registered with the DOM data broker.
-
-Receive notifications
-^^^^^^^^^^^^^^^^^^^^^
-
-You should now have a data change notification stream created and have
-location of a WebSocket. You can use this WebSocket to listen to data
-change notifications. To listen to notifications you can use a
-JavaScript client or if you are using chrome browser you can use the
-`Simple WebSocket
-Client <https://chrome.google.com/webstore/detail/simple-websocket-client/pfdhoblngboilpfeibdedpjgfnlcodoo>`__.
-
-Also, for testing purposes, there is simple Java application named
-WebSocketClient. The application is placed in the
-*-sal-rest-connector-classes.class* project. It accepts a WebSocket URI
-as and input parameter. After starting the utility (WebSocketClient
-class directly in Eclipse/InteliJ Idea) received notifications should be
-displayed in console.
-
-Notifications are always in XML format and look like this:
-
-.. code:: xml
-
- <notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">
- <eventTime>2014-09-11T09:58:23+02:00</eventTime>
- <data-changed-notification xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote">
- <data-change-event>
- <path xmlns:meae="http://netconfcentral.org/ns/toaster">/meae:toaster</path>
- <operation>updated</operation>
- <data>
- <!-- updated data -->
- </data>
- </data-change-event>
- </data-changed-notification>
- </notification>
-
-Example use case
-~~~~~~~~~~~~~~~~
-
-The typical use case is listening to data change events to update web
-page data in real-time. In this tutorial we will be using toaster as the
-base.
-
-When you call *make-toast* RPC, it sets *toasterStatus* to "down" to
-reflect that the toaster is busy making toast. When it finishes,
-*toasterStatus* is set to "up" again. We will listen to this toaster
-status changes in data store and will reflect it on our web page in
-real-time thanks to WebSocket data change notification.
-
-Simple javascript client implementation
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-We will create simple JavaScript web application that will listen
-updates on *toasterStatus* leaf and update some element of our web page
-according to new toaster status state.
-
-Create stream
-^^^^^^^^^^^^^
-
-First you need to create stream that you are planing to subscribe to.
-This can be achieved by invoking "create-data-change-event-subscription"
-RPC on RESTCONF via AJAX request. You need to provide data store
-**path** that you plan to listen on, **data store type** and **scope**.
-If the request is successful you can extract the **stream-name** from
-the response and use that to subscribe to the newly created stream. The
-*{username}* and *{password}* fields represent your credentials that you
-use to connect to OpenDaylight via RESTCONF:
-
-.. note::
-
- The default user name and password are "admin".
-
-.. code:: javascript
-
- function createStream() {
- $.ajax(
- {
- url: 'http://{odlAddress}:{odlPort}/restconf/operations/sal-remote:create-data-change-event-subscription',
- type: 'POST',
- headers: {
- 'Authorization': 'Basic ' + btoa('{username}:{password}'),
- 'Content-Type': 'application/json'
- },
- data: JSON.stringify(
- {
- 'input': {
- 'path': '/toaster:toaster/toaster:toasterStatus',
- 'sal-remote-augment:datastore': 'OPERATIONAL',
- 'sal-remote-augment:scope': 'ONE'
- }
- }
- )
- }).done(function (data) {
- // this function will be called when ajax call is executed successfully
- subscribeToStream(data.output['stream-name']);
- }).fail(function (data) {
- // this function will be called when ajax call fails
- console.log("Create stream call unsuccessful");
- })
- }
-
-Subscribe to stream
-^^^^^^^^^^^^^^^^^^^
-
-The Next step is to subscribe to the stream. To subscribe to the stream
-you need to call *GET* on
-*http://{odlAddress}:{odlPort}/restconf/streams/stream/{stream-name}*.
-If the call is successful, you get WebSocket address for this stream in
-**Location** parameter inside response header. You can get response
-header by calling *getResponseHeader(\ *Location*)* on HttpRequest
-object inside *done()* function call:
-
-.. code:: javascript
-
- function subscribeToStream(streamName) {
- $.ajax(
- {
- url: 'http://{odlAddress}:{odlPort}/restconf/streams/stream/' + streamName;
- type: 'GET',
- headers: {
- 'Authorization': 'Basic ' + btoa('{username}:{password}'),
- }
- }
- ).done(function (data, textStatus, httpReq) {
- // we need function that has http request object parameter in order to access response headers.
- listenToNotifications(httpReq.getResponseHeader('Location'));
- }).fail(function (data) {
- console.log("Subscribe to stream call unsuccessful");
- });
- }
-
-Receive notifications
-^^^^^^^^^^^^^^^^^^^^^
-
-Once you got WebSocket server location you can now connect to it and
-start receiving data change events. You need to define functions that
-will handle events on WebSocket. In order to process incoming events
-from OpenDaylight you need to provide a function that will handle
-*onmessage* events. The function must have one parameter that represents
-the received event object. The event data will be stored in
-*event.data*. The data will be in an XML format that you can then easily
-parse using jQuery.
-
-.. code:: javascript
-
- function listenToNotifications(socketLocation) {
- try {
- var notificatinSocket = new WebSocket(socketLocation);
-
- notificatinSocket.onmessage = function (event) {
- // we process our received event here
- console.log('Received toaster data change event.');
- $($.parseXML(event.data)).find('data-change-event').each(
- function (index) {
- var operation = $(this).find('operation').text();
- if (operation == 'updated') {
- // toaster status was updated so we call function that gets the value of toasterStatus leaf
- updateToasterStatus();
- return false;
- }
- }
- );
- }
- notificatinSocket.onerror = function (error) {
- console.log("Socket error: " + error);
- }
- notificatinSocket.onopen = function (event) {
- console.log("Socket connection opened.");
- }
- notificatinSocket.onclose = function (event) {
- console.log("Socket connection closed.");
- }
- // if there is a problem on socket creation we get exception (i.e. when socket address is incorrect)
- } catch(e) {
- alert("Error when creating WebSocket" + e );
- }
- }
-
-The *updateToasterStatus()* function represents function that calls
-*GET* on the path that was modified and sets toaster status in some web
-page element according to received data. After the WebSocket connection
-has been established you can test events by calling make-toast RPC via
-RESTCONF.
-
-.. note::
-
- for more information about WebSockets in JavaScript visit `Writing
- WebSocket client
- applications <https://developer.mozilla.org/en-US/docs/WebSockets/Writing_WebSocket_client_applications>`__
-
-.. _config_subsystem:
-
-Config Subsystem
-----------------
-
-Overview
-~~~~~~~~
-
-The Controller configuration operation has three stages:
-
-- First, a Proposed configuration is created. Its target is to replace
- the old configuration.
-
-- Second, the Proposed configuration is validated, and then committed.
- If it passes validation successfully, the Proposed configuration
- state will be changed to Validated.
-
-- Finally, a Validated configuration can be Committed, and the affected
- modules can be reconfigured.
-
-In fact, each configuration operation is wrapped in a transaction. Once
-a transaction is created, it can be configured, that is to say, a user
-can abort the transaction during this stage. After the transaction
-configuration is done, it is committed to the validation stage. In this
-stage, the validation procedures are invoked. If one or more validations
-fail, the transaction can be reconfigured. Upon success, the second
-phase commit is invoked. If this commit is successful, the transaction
-enters the last stage, committed. After that, the desired modules are
-reconfigured. If the second phase commit fails, it means that the
-transaction is unhealthy - basically, a new configuration instance
-creation failed, and the application can be in an inconsistent state.
-
-.. figure:: ./images/configuration.jpg
- :alt: Configuration states
-
- Configuration states
-
-.. figure:: ./images/Transaction.jpg
- :alt: Transaction states
-
- Transaction states
-
-Validation
-~~~~~~~~~~
-
-To secure the consistency and safety of the new configuration and to
-avoid conflicts, the configuration validation process is necessary.
-Usually, validation checks the input parameters of a new configuration,
-and mostly verifies module-specific relationships. The validation
-procedure results in a decision on whether the proposed configuration is
-healthy.
-
-Dependency resolver
-~~~~~~~~~~~~~~~~~~~
-
-Since there can be dependencies between modules, a change in a module
-configuration can affect the state of other modules. Therefore, we need
-to verify whether dependencies on other modules can be resolved. The
-Dependency Resolver acts in a manner similar to dependency injectors.
-Basically, a dependency tree is built.
-
-APIs and SPIs
-~~~~~~~~~~~~~
-
-This section describes configuration system APIs and SPIs.
-
-SPIs
-^^^^
-
-**Module** org.opendaylight.controller.config.spi. Module is the common
-interface for all modules: every module must implement it. The module is
-designated to hold configuration attributes, validate them, and create
-instances of service based on the attributes. This instance must
-implement the AutoCloseable interface, owing to resources clean up. If
-the module was created from an already running instance, it contains an
-old instance of the module. A module can implement multiple services. If
-the module depends on other modules, setters need to be annotated with
-@RequireInterface.
-
-**Module creation**
-
-1. The module needs to be configured, set with all required attributes.
-
-2. The module is then moved to the commit stage for validation. If the
- validation fails, the module attributes can be reconfigured.
- Otherwise, a new instance is either created, or an old instance is
- reconfigured. A module instance is identified by ModuleIdentifier,
- consisting of the factory name and instance name.
-
-| **ModuleFactory** org.opendaylight.controller.config.spi. The
- ModuleFactory interface must be implemented by each module factory.
-| A module factory can create a new module instance in two ways:
-
-- From an existing module instance
-
-- | An entirely new instance
- | ModuleFactory can also return default modules, useful for
- populating registry with already existing configurations. A module
- factory implementation must have a globally unique name.
-
-APIs
-^^^^
-
-+--------------------------------------+--------------------------------------+
-| ConfigRegistry | Represents functionality provided by |
-| | a configuration transaction (create, |
-| | destroy module, validate, or abort |
-| | transaction). |
-+--------------------------------------+--------------------------------------+
-| ConfigTransactionController | Represents functionality for |
-| | manipulating with configuration |
-| | transactions (begin, commit config). |
-+--------------------------------------+--------------------------------------+
-| RuntimeBeanRegistratorAwareConfiBean | The module implementing this |
-| | interface will receive |
-| | RuntimeBeanRegistrator before |
-| | getInstance is invoked. |
-+--------------------------------------+--------------------------------------+
-
-Runtime APIs
-^^^^^^^^^^^^
-
-+--------------------------------------+--------------------------------------+
-| RuntimeBean | Common interface for all runtime |
-| | beans |
-+--------------------------------------+--------------------------------------+
-| RootRuntimeBeanRegistrator | Represents functionality for root |
-| | runtime bean registration, which |
-| | subsequently allows hierarchical |
-| | registrations |
-+--------------------------------------+--------------------------------------+
-| HierarchicalRuntimeBeanRegistration | Represents functionality for runtime |
-| | bean registration and |
-| | unreregistration from hierarchy |
-+--------------------------------------+--------------------------------------+
-
-JMX APIs
-^^^^^^^^
-
-| JMX API is purposed as a transition between the Client API and the JMX
- platform.
-
-+--------------------------------------+--------------------------------------+
-| ConfigTransactionControllerMXBean | Extends ConfigTransactionController, |
-| | executed by Jolokia clients on |
-| | configuration transaction. |
-+--------------------------------------+--------------------------------------+
-| ConfigRegistryMXBean | Represents entry point of |
-| | configuration management for |
-| | MXBeans. |
-+--------------------------------------+--------------------------------------+
-| Object names | Object Name is the pattern used in |
-| | JMX to locate JMX beans. It consists |
-| | of domain and key properties (at |
-| | least one key-value pair). Domain is |
-| | defined as |
-| | "org.opendaylight.controller". The |
-| | only mandatory property is "type". |
-+--------------------------------------+--------------------------------------+
-
-Use case scenarios
-^^^^^^^^^^^^^^^^^^
-
-| A few samples of successful and unsuccessful transaction scenarios
- follow:
-
-**Successful commit scenario**
-
-1. The user creates a transaction calling creteTransaction() method on
- ConfigRegistry.
-
-2. ConfigRegisty creates a transaction controller, and registers the
- transaction as a new bean.
-
-3. Runtime configurations are copied to the transaction. The user can
- create modules and set their attributes.
-
-4. The configuration transaction is to be committed.
-
-5. The validation process is performed.
-
-6. After successful validation, the second phase commit begins.
-
-7. Modules proposed to be destroyed are destroyed, and their service
- instances are closed.
-
-8. Runtime beans are set to registrator.
-
-9. The transaction controller invokes the method getInstance on each
- module.
-
-10. The transaction is committed, and resources are either closed or
- released.
-
-| **Validation failure scenario**
-| The transaction is the same as the previous case until the validation
- process.
-
-1. If validation fails, (that is to day, illegal input attributes values
- or dependency resolver failure), the validationException is thrown
- and exposed to the user.
-
-2. The user can decide to reconfigure the transaction and commit again,
- or abort the current transaction.
-
-3. On aborted transactions, TransactionController and JMXRegistrator are
- properly closed.
-
-4. Unregistration event is sent to ConfigRegistry.
-
-Default module instances
-^^^^^^^^^^^^^^^^^^^^^^^^
-
-The configuration subsystem provides a way for modules to create default
-instances. A default instance is an instance of a module, that is
-created at the module bundle start-up (module becomes visible for
-configuration subsystem, for example, its bundle is activated in the
-OSGi environment). By default, no default instances are produced.
-
-The default instance does not differ from instances created later in the
-module life-cycle. The only difference is that the configuration for the
-default instance cannot be provided by the configuration subsystem. The
-module has to acquire the configuration for these instances on its own.
-It can be acquired from, for example, environment variables. After the
-creation of a default instance, it acts as a regular instance and fully
-participates in the configuration subsystem (It can be reconfigured or
-deleted in following transactions.).