RESTCONF

Overview

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

Datastores

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, 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>.

Note

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.

Example:

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

Example with mount point:

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

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).

Example:

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

Example with mount point:

http://<uniconfig-ip>:8181/rests/data/module1:foo1/foo2/yang-ext:mount/module2:foo
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.

Example:

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.

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”.

Example:

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

The answer from the server could be:

<output></output>

An example using a JSON payload:

POST http://localhost:8181/rests/operations/toaster:make-toast
Content-Type: application/yang.data+json
{
  "input" :
  {
     "toaster:toasterDoneness" : "10",
     "toaster:toasterToastType":"wheat-bread"
  }
}

Note

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

Note

More information is available in the RESTCONF RFC 8040.

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:

Note

path?fields=field_expression

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)

Note

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

**Using sub-selectors**
http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=IOSXR/configuration?fields=frinx-openconfig-interfaces:interfaces(interface)&content=nonconfig

**Using nesting**
http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=IOSXR/configuration?fields=frinx-openconfig-interfaces:interfaces/interface/name;config&content=nonconfig

Note

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

**Using sub-selectors**
http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=IOSXR/configuration?fields=frinx-openconfig-interfaces:interfaces(interface(name;config(name)))&content=nonconfig

**Using nesting**
http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=IOSXR/configuration?fields=frinx-openconfig-interfaces:interfaces/interface/name;config/name&content=nonconfig

Note

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

**Using sub-selectors**
http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=IOSXR/configuration?fields=frinx-openconfig-interfaces:interfaces(interface(name;config(type)))&content=nonconfig

**Using nesting**
http://localhost:8181/rests/data/network-topology:network-topology/topology=uniconfig/node=IOSXR/configuration?fields=frinx-openconfig-interfaces:interfaces/interface/name;config/type&content=nonconfig

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

Note

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

Note

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": [
      "TLSv1.2",
      "TLSv1.3"
  ],
  "excludedProtocols": [
      "TLSv1",
      "TLSv1.1",
      "SSL",
      "SSLv2",
      "SSLv2Hello",
      "SSLv3"
  ],
  "includedCipherSuites": [
      "TLS_ECDHE.*",
      "TLS_DHE_RSA.*"
  ],
  "excludedCipherSuites": [
      ".*MD5.*",
      ".*RC4.*",
      ".*DSS.*",
      ".*NULL.*",
      ".*DES.*"
  ]
}

Note

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).