RESTCONF is described in RESTCONF RFC 8040. Simple said, RESTCONF represents REST API to access datastores and UniConfig operations.


There are two datastores:

  1. Config: Contains data representing intended state - it is possible to read and write it via RESTCONF.

  2. Operational: Contains data representing actual state - it is possible to only read it via RESTCONF.

Note: Each request must start with the URI /rests/. By default, RESTCONF listens on port 8181 for HTTP requests.

REST operations

RESTCONF supports OPTIONS, GET, PUT, POST, PATCH, 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.

  • JSON structures are defined at: JSON-YANG.

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 use Instance Identifier. <identifier> is used in the explanation of the operations and has to keep these rules:

  • Identifier must start with <moduleName>:<nodeName>> where <moduleName> is a name of the YANG module and <nodeName> is the name of a node in the module. If the next node name is placed in the same namespace like the previous one, it is sufficient to just use <nodeName> after the first definition of <moduleName>:<nodeName>. Each <nodeName> has to be separated by /.

  • <nodeName> can represent a data node which is a list node, container, leaf, or leaf-list YANG built-in type. If the data node is a list, there must be defined ordered keys of the list behind the data node name, for example, <nodeName>=<valueOfKey1>,<valueOfKey2>.


The following example shows how reserved characters are percent-encoded within a key value. The value of “key1” contains a comma, single-quote, double-quote, colon, double-quote, space, and forward slash (,’”:” /). Note that double-quote is not a reserved character and does not need to be percent-encoded. The value of “key2” is the empty string, and the value of “key3” is the string “foo”.

Example URL: /rests/data/example-top:top/list1=%2C%27”%3A”%20%2F,,foo

  • The format <moduleName>:<nodeName> has to be used in this case as well. Module A has node A1. Module B augments node A1 by adding node X. Module C augments node A1 by adding node X. For clarity, it has to be known which node is X (for example: C:X).

Mount point

Mount point usually represents an external system. A node can be behind a mount point. In this case, the URI has to be in the format <identifier>/yang-ext:mount/<identifier>. The first <identifier> is the path to a mount point and the second <identifier> is the path to a node behind the mount point. An URI can end in a mount point itself by using <identifier>/yang-ext:mount. More information on how to actually use mountpoints is available at: OpenDaylight Controller:Config:Examples:Netconf.

HTTP methods

OPTIONS /rests

  • Returns the XML description of the resources with the required request and response media types in Web Application Description Language (WADL).

GET /rests/data/<identifier>?content=config

  • Returns a data node from the Config datastore.

  • <identifier> points to a data node which must be retrieved.

GET /rests/data/<identifier>?content=nonconfig

  • Returns the value of the data node from the Operational datastore.

  • <identifier> points to a data node which must be retrieved.

GET /rests/data/<identifier>

  • Returns a data node from both Config and Operational datastores. The outputs from both datastores are merged to one output.

  • <identifier> points to a data node which must be retrieved.

PUT /rests/data/<identifier>

  • Updates or creates data in the Config datastore and returns the state about success.

  • <identifier> points to a data node which must be stored.

  • Content type does not have to be specified in URI - it can only be the Configuration datastore.


PUT http://<uniconfig-ip>:8181/rests/data/module1:foo/bar
Content-Type: applicaton/xml

Example with mount point:

PUT http://<uniconfig-ip>:8181/rests/data/module1:foo1/foo2/yang-ext:mount/module2:foo/bar
Content-Type: applicaton/xml

POST /rests/data/<identifier>

  • Creates the data if it does not exist in the Config datastore, and returns the state about success.

  • <identifier> points to a data node where data must be stored.

  • The root element of data must have the namespace (data is in XML) or module name (data is in JSON).


POST http://<uniconfig-ip>:8181/rests/data/<identifier>
Content-Type: applicaton/xml
<bar xmlns=“module1namespace”></bar>

Example with mount point:

Content-Type: applicaton/xml
<bar xmlns=“module2namespace”></bar>

POST /rests/data

  • Creates the data if it does not exist under data root.

  • In the following example, ‘toaster’ module is root container in YANG (it doesn’t have any parent). This example also makes it clear that URI doesn’t contain ‘toaster’ node in comparison to PUT request that must contain name of the created node in URI.


POST URL: http://localhost:8181/rests/data
content-type: application/json
JSON payload:

     "toaster:toaster" :
       "toaster:toasterManufacturer" : "General Electric",
       "toaster:toasterModelNumber" : "123",
       "toaster:toasterStatus" : "up"

DELETE /rests/data/<identifier>

  • Removes the data node in the Config datastore and returns the state about success.

  • <identifier> points to a data node which must be removed.

PATCH /rests/data/<identifier>

  • The patch request merges the contents of the message-body with the target resource in Configuration datastore (content-type query parameter is not specified).

  • <identifier> points to a data node on which PATCH operations is invoked.

  • This request is implemented by Plain PATCH functionality - see more details on the following page: RFC-8040 documentation - Plain PATCH operation.

  • Plain patch can be used to create or update, but not delete, a child resource within the target resource. Any pre-existing data which is not explicitly overwritten will be preserved. This means that if you store a container, its child entities will also be merge recursively.

The following example shows the PATCH request used for modification of Ethernet interface IP address and two connection settings. Note that other settings under system:system container are left untouched including other leaves under ‘connection’ container and ‘ethernet’ list item.

curl --location --request PATCH 'http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=n1/configuration/system:system' \
--header 'Content-Type: application/json' \
--data-raw '{
    "system:system": {
        "wan": {
            "endpoint": {
                "interfaces": {
                    "ethernet": [
                            "name": "ethernet1",
                            "inet": {
                                "address": ""
        "connection": {
            "syn-flood-check": true,
            "ack-flood-check": true

POST /rests/operations/<moduleName>:<rpcName>

  • Invokes RPC on the specified path.

  • <moduleName>:<rpcName> - <moduleName> is the name of the module and <rpcName> is the name of the RPC in this module.

  • The Root element of the data sent to RPC must have the name “input”.

  • The result have the status code and optionally retrieved data having the root element “output”.


POST http://<uniconfig-ip>:8181/rests/operations/module1:fooRpc
Content-Type: applicaton/xml
Accept: applicaton/xml

The answer from the server could be:


An example using a JSON payload:

POST http://localhost:8181/rests/operations/toaster:make-toast
Content-Type: application/
  "input" :
     "toaster:toasterDoneness" : "10",


GET /rests/operations request can be used to retrieve all available RPCs that are registered in distribution.


More information is available in the RESTCONF RFC 8040.

POST /rests/data/<path-to-operation>

  • Invokes action on the specified path in the data tree.

  • Placeholder <path-to-operation> represents data path to operation definition that is specified under composite data schema node in YANG (only containers and lists may contain action definition).

  • Content query parameter doesn’t have to be specified (it will be ignored) - action is represented equally in Operational and Config datastore.

  • Both RFC-8040 (YANG 1.1) and TAIL-F actions are supported. TAIL-F actions can be placed in both YANG 1.0 and YANG 1.1 schemas. There aren’t any differences in invocation of these types of actions using RESTCONF API.

  • The body of the action invocation request may contain root ‘input’ container. If action definition has no specified input container, it is not required to specify body in the request.

  • The response contains the status code and optionally retrieved data having the root element ‘output’.

  • Currently, FRINX UniConfig only supports invocation of actions under NETCONF mountpoint - <path-to-operation> must contain ‘yang-ext:mount’ container.

  • Structure of ‘input’ and ‘output’ elements are the same like structure of these containers when we invoke YANG RPC.

Assume the following YANG snippet with root container named ‘interfaces’:

container interfaces {
    action compute-stats {
        input {
            leaf month {
                type string;
            leaf year {
                type uint16;
            leaf percentile {
                type uint8;
        output {
            leaf avg-rx-kbps {
                type uint32;
            leaf avg-tx-kbps {
                type uint32;

Invocation of action named ‘compute-stats’ that is placed under ‘interfaces’ container of NETCONF mountpoint:

curl --location --request POST 'http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=dev/yang-ext:mount/interfaces:interfaces/compute-stats' \
--header 'Content-Type: application/json' \
--data-raw '{
    "input": {
        "month": "April",
        "year": 2018,
        "percentile": 50

The response body:

    "interfaces:output": {
        "avg-rx-kbps": 14524,
        "avg-tx-kbps": 47787


Difference between RPCs and actions: Actions are bound to data tree and they can be placed under containers and lists (they cannot be specified as root entities in YANG schema). RPCs are not placed in the data tree and from this reason they can only be specified as root entities in the YANG schema.

Filtering data

For filtering and identifying data is good to use query parameter fields. This parameter has to be used only with GET method. The response body is output filtered by field-expression as value of fields parameter.

The example of using fields parameter:



There are several rules, that needs to be followed:

  1. For filtering more than one field of the same parent, “;” needs to be used. Example : path?fields=field1;field2, where field1 and field2 has the same parent, which is the very last part of path.

  2. For nesting, “/” needs to be used. Example : path?fields=field1;pathField/field2, where field1 and field2 has not the same parent, but pathField is on the same level as field1.

  3. Third character “(” , “)” is used to specify sub-selectors.

    Example: path ? fields = pathField( field1; pathField2( field2; pathField3( field3 ) ) )

    This is different approach to do nesting, however the difference between “(” and “/” is that once we use “/” for specifying some field, we cannot identify another field from upper layers.

    Example: path ? fields = pathField / field1; pathField2 / field2

    This is the case where pathField1 and pathField2 have the same parent, this is not allowed, because once we use “;” it is expected to specify fields on the same layer as field1

Examples - with 2 approaches (nesting, sub-selecting)


Example of filtering whole configuration of all interfaces (name, with whole config)

**Using sub-selectors**

**Using nesting**


Example of filtering all names of interfaces and all names of configs of interfaces

**Using sub-selectors**

**Using nesting**


Example of filtering all names of interfaces with type from config of interfaces

**Using sub-selectors**

**Using nesting**

Device schema filters

By default, all input and output data produced by RESTconf for the selected device is fully compliant with its YANG models. Any violation of the yang schema definitions will result in an error. Some of these restrictions can be addressed by adding the ‘schemaFilters’ configuration parameter for the RESTconf.

Configuration options overview

Following configuration options for ‘schemaFilters’ make RESTconf processing less restrictive:






RESTconf will ignore definitions with any of the selected extensions during writing to the datastore.



RESTconf will not include definition with the selected extensions in the responses to the GET requests.



Any unknown element provided in the body for POST/PUT/PATCH requests will be ignored by RESTconf.



RESTconf will not include definitions marked with ‘status deprecated’ in the responses to the GET requests.

Configuration example

The following example demonstrates how to enable schema filters for selected extensions and make RESTconf ignore unknown definitions and definitions with a ‘deprecated status’ attribute.

"schemaFilters": {
  "ignoredDataOnWriteByExtensions": [
    "tailf:hidden full"
  "hiddenDataOnReadByExtensions": [
    "tailf:hidden deprecated",
    "tailf:hidden debug"
    "ignoreUnsupportedDefinitionsOnWrite": true,
    "hideDeprecatedDefinitionsOnRead": true

Unhide parameter for read operation

RESTconf supports the ‘unhide’ query parameter for the GET requests to include hidden definitions into the response. This parameter value can be populated with a comma-separated list of extensions to unhide or the keyword ‘all’ to include all possible hidden definitions in the response.

Example of using the ‘unhide’ parameter for the GET request.

Using unhide with a list of extensions

http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=device/configuration?unhide=tailf:hidden debug,tailf:hidden deprecated

Using unhide parameter to unhide all hidden definitions


TLS-based authentication

In default UniConfig lighty distribution TLS authentication is disabled. To enable TLS for RESTCONF you must setup two things:

  1. Key-store and trust-store that hold all keys and certificates. If authentication of individual clients is not required, trust-store doesn’t have to be created at all. Key-store must always be initialized.

  2. Enabling of TLS in UniConfig lighty distribution by adjusting of lighty configuration file.

Setting of key-store and trust-store

Steps required for preparation of key-store and trust-store:

  1. Create directory under UniConfig lighty root directory that will contain key-store and optionally trust-store files, for example:

mkdir ./tls
cd ./tls
  1. Create a new key-store. There are two options depending on fact whether you already own certificate that you would like to use for identification of lighty distribution on RESTCONF layer.

  1. Create a new key-store with the generated RSA key-pair (in the example the length of 2048 and validity of 365 days is used). After execution of the following command, the prompt will ask you for information about currently generated certificate that will be pushed into newly generated key-store secured by password (this secret will be used later in configuration file - remember it).

keytool -keystore .keystore -alias jetty -genkey -keyalg RSA -storetype PKCS12 -validity 365 -keysize 2048
  1. Create a new key-store with already generated RSA key-pair (you own certificate that you would like to use for authentication in ODL).

keytool -import -file [your-certificate-file] -alias jetty -keystore .keystore
  1. (Optional step) Create a new trust-store using existing certificate (an empty truststore cannot be created). If you have multiple client certificates, they can be pushed to truststore with the same command executed multiple times (but alias must be unique for each of the imported certificate). Example:

keytool -import -file [client-app-certificate] -alias [unique-name-of-certificate] -keystore .truststore


You can easily convert OPENSSL PEM certificates to DER format that is supported by keytool:

openssl x509 -outform der -in certificate.pem -out certificate.der


If your application needs to own distribution’s certificate, you can export certificate from generated key-pair that we have pushed into the keystore (PKCS12 or OPENSSL format):

keytool -export -keystore .keystore -alias jetty -file odl.cer
penssl pkcs12 -in .keystore -out certificate.pem

Enabling of TLS in UniConfig lighty distribution

Preparation of TLS key-store and trust-store is not enough for enabling of TLS on RESTCONF API. It is also required to point lighty distribution to these created storages and explicitly enable TLS by setting of corresponding flag. The configuration file that must be modified can be found on following path relative to lighty distribution root directory:

vim config/lighty-uniconfig-config.json

Then, you must append the TLS configuration snippet (it must be place under root JSON node) to the configuration file. The following example snippet enables TLS authentication, disables user-based authentication (hence trust-store is not required at all), and points lighty to key-store file that we have created in the previous section.

"tls": {
    "enabledTls": true,
    "enabledClientAuthentication": false,
    "keystorePath": "tls/.keystore",
    "keystorePassword": "key-pass"

If your deployment requires authentication of individual RESTCONF users too, you should also specify the trust-store fields with set ‘enabledClientAuthentication’ field to ‘true’.

"tls": {
    "enabledTls": true,
    "enabledClientAuthentication": true,
    "keystorePath": "tls/.keystore",
    "keystorePassword": "key-pass",
    "truststorePath": "tls/.truststore",
    "truststorePassword": "trust-pass"

You can also specify included or excluded cipher suites and TLS versions that can or cannot be used for establishing of secured tunnel between Jetty server and clients. The following configuration is default and it is based on actual recommendations (you can adjust it):

"tls": {
  "includedProtocols": [
  "excludedProtocols": [
  "includedCipherSuites": [
  "excludedCipherSuites": [


It is enough to specify only included protocols and included cipher suites (all other entries are denied) or excluded protocols and excluded cipher suites (all other entries are permitted). If you specify the same entries under both included and excluded cipher suites or protocols, the excluded entry has higher priority. For example, the final set of usable cipher suites is: setOf(includedCipherSuites) - setOf(excludedCipherSuites).