lighty.io gNMI RESTCONF

[lighty.io] Open-Source gNMI RESTCONF Application

The lighty.io gNMI RESTCONF app allows for easy manipulation of gNMI devices. PANTHEON.tech has open-sourced the gNMI RESTCONF app for lighty.io, to increase the capabilities of lighty.io for different implementations and solutions.

Imagine CRUD operation on multiple gNMI devices, managed by one application – lighty.io. All requests towards the gNMI devices are executed by RESTCONF operations, while the response is formatted in JSON.

The most important lighty.io components used in the lighty.io gNMI RESTCONF application are:

  • lighty.io Controller – provides core OpenDaylight services (MD-SAL, YANG Tools, Global Schema Context & more) that are required for other services or plugins
  • lighty.io RESTCONF Northbound – provides the RESTCONF interface, used for communication with the application, via the RESTCONF protocol over HTTP
  • lighty.io gNMI Southbound – acts as the gNMI client. Manages connections to gNMI devices and gNMI communication. Currently supported gNMI capabilities are Get & Set

Prerequisites

To build and start the lighty.io gNMI RESTCONF application locally, you need:

  •  Java 11 or later
  •  Maven 3.5.4 or later

Custom Configuration

Before the lighty gNMI RESTCONF app creates a mount point for communicating with the gNMI device, it is necessary to create a schema context. This schema context is created, based on the YANG files which the device implements. These models are obtained via the gNMI Capability response, but only model names and versions are actually returned. Thus, we need some way of providing the content of the YANG model.

The way of providing content for the YANG file, so lighty.io gNMI RESTCONF can correctly create schema context, is to:

  • add a parameter to the RCGNMI app .json configuration
  • use upload-yang-model RPC

Both of these options will load the YANG files into the data-store, from which the ligthy.io gNMI RESTCONF reads the model, based on its name and version, obtained in the gNMI Capability response.

YANG Model Configuration as a Parameter

  1. Open the custom configuration example in src/main/resources/example_config.json
  2. Add custom gNMI configuration in root, next to the controller or RESTCONF configuration
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
```
"gnmi": {
"initialYangsPaths" : [
"INITIAL_FOLDER_PATH"
]
}
```
3. Change `INITIAL_FOLDER_PATH`, from JSON block above, to folder path, which contains YANG models you wish to
load into datastore. These models will be then automatically loaded on startup.
2) Add yang model with RPC request to running app
- 'YANG_MODEL' - Should have included escape characters before each double quotation marks character.
```
curl --request POST 'http://127.0.0.1:8888/restconf/operations/gnmi-yang-storage:upload-yang-model' \
--header 'Content-Type: application/json' \
--data-raw '{
"input": {
"name": "openconfig-interfaces",
"semver": "2.4.3",
"body": "YANG_MODEL"
}
}'
```
``` "gnmi": { "initialYangsPaths" : [ "INITIAL_FOLDER_PATH" ] } ``` 3. Change `INITIAL_FOLDER_PATH`, from JSON block above, to folder path, which contains YANG models you wish to load into datastore. These models will be then automatically loaded on startup. 2) Add yang model with RPC request to running app - 'YANG_MODEL' - Should have included escape characters before each double quotation marks character. ``` curl --request POST 'http://127.0.0.1:8888/restconf/operations/gnmi-yang-storage:upload-yang-model' \ --header 'Content-Type: application/json' \ --data-raw '{ "input": { "name": "openconfig-interfaces", "semver": "2.4.3", "body": "YANG_MODEL" } }' ```
 ```
      "gnmi": {
        "initialYangsPaths" : [
          "INITIAL_FOLDER_PATH"
        ]
      }
    ```
    3. Change `INITIAL_FOLDER_PATH`, from JSON block above, to folder path, which contains YANG models you wish to
    load into datastore. These models will be then automatically loaded on startup.

2) Add yang model with RPC request to running app
- 'YANG_MODEL' - Should have included escape characters before each double quotation marks character.
```
curl --request POST 'http://127.0.0.1:8888/restconf/operations/gnmi-yang-storage:upload-yang-model' \
--header 'Content-Type: application/json' \
--data-raw '{
    "input": {
        "name": "openconfig-interfaces",
        "semver": "2.4.3",
        "body": "YANG_MODEL"
    }
}'
```

Start the gNMI RESTCONF Example App

1. Build the project using:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
mvn clean install
mvn clean install
mvn clean install

2. Go to the target directory:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
cd lighty-rcgnmi-app/target
cd lighty-rcgnmi-app/target
cd lighty-rcgnmi-app/target

3. Unzip example application bundle:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
unzip lighty-rcgnmi-app-14.0.1-SNAPSHOT-bin.zip
unzip lighty-rcgnmi-app-14.0.1-SNAPSHOT-bin.zip
unzip  lighty-rcgnmi-app-14.0.1-SNAPSHOT-bin.zip

4. Go to the unzipped application directory:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
cd lighty-rcgnmi-app-14.0.1-SNAPSHOT
cd lighty-rcgnmi-app-14.0.1-SNAPSHOT
cd lighty-rcgnmi-app-14.0.1-SNAPSHOT

5. To start the application with a custom lighty.io configuration, use arg -c. For a custom initial log4j configuration, use the argument -l:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
start-controller.sh -c /path/to/config-file -l /path/to/log4j-config-file
start-controller.sh -c /path/to/config-file -l /path/to/log4j-config-file
start-controller.sh -c /path/to/config-file -l /path/to/log4j-config-file

Using the gNMI RESTCONF Example App

Register Certificates

Certificates, used for connecting to a device, can be stored inside the lighty-gnmi data store. The certificate key and passphrase are encrypted before they are stored inside the data store.

After registering the certificate key and passphrase, it is not possible to get decrypted data back from the data store.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
curl --request POST 'http://127.0.0.1:8888/restconf/operations/gnmi-certificate-storage:add-keystore-certificate' \
--header 'Content-Type: application/json' \
--data-raw '{
"input": {
"keystore-id": "keystore-id-1",
"ca-certificate": "-----BEGIN CERTIFICATE-----
CA-CERTIFICATE
-----END CERTIFICATE-----",
"client-key": "-----BEGIN RSA PRIVATE KEY-----
CLIENT-KEY
-----END RSA PRIVATE KEY-----",
"passphrase": "key-passphrase",
"client-cert": "-----BEGIN CERTIFICATE-----
CLIENT_CERT
-----END CERTIFICATE-----"
}
}'
curl --request POST 'http://127.0.0.1:8888/restconf/operations/gnmi-certificate-storage:add-keystore-certificate' \ --header 'Content-Type: application/json' \ --data-raw '{ "input": { "keystore-id": "keystore-id-1", "ca-certificate": "-----BEGIN CERTIFICATE----- CA-CERTIFICATE -----END CERTIFICATE-----", "client-key": "-----BEGIN RSA PRIVATE KEY----- CLIENT-KEY -----END RSA PRIVATE KEY-----", "passphrase": "key-passphrase", "client-cert": "-----BEGIN CERTIFICATE----- CLIENT_CERT -----END CERTIFICATE-----" } }'
curl --request POST 'http://127.0.0.1:8888/restconf/operations/gnmi-certificate-storage:add-keystore-certificate' \
--header 'Content-Type: application/json' \
--data-raw '{
    "input": {
        "keystore-id": "keystore-id-1",
        "ca-certificate": "-----BEGIN CERTIFICATE-----
                              CA-CERTIFICATE
                          -----END CERTIFICATE-----",
        "client-key": "-----BEGIN RSA PRIVATE KEY-----
                                CLIENT-KEY
                      -----END RSA PRIVATE KEY-----",
        "passphrase": "key-passphrase",
        "client-cert": "-----BEGIN CERTIFICATE-----
                              CLIENT_CERT
                        -----END CERTIFICATE-----"
    }
}'

Remove Certificates

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
curl --location --request POST 'http://127.0.0.1:8888/restconf/operations/gnmi-certificate-storage:remove-keystore-certificate' \
--header 'Content-Type: application/json' \
--data-raw '{
"input": {
"keystore-id": "keystore-id-1"
}
}'
curl --location --request POST 'http://127.0.0.1:8888/restconf/operations/gnmi-certificate-storage:remove-keystore-certificate' \ --header 'Content-Type: application/json' \ --data-raw '{ "input": { "keystore-id": "keystore-id-1" } }'
curl --location --request POST 'http://127.0.0.1:8888/restconf/operations/gnmi-certificate-storage:remove-keystore-certificate' \
--header 'Content-Type: application/json' \
--data-raw '{
    "input": {
        "keystore-id": "keystore-id-1"
    }
}'

Update Certificates

To update the already existing certificates, use the request for registering a new certificate with the keystore-id you wish to update.

Connecting a gNMI Device

To establish a connection and communication with the gNMI device via RESTCONF, one needs to add a new node to gnmi-topology. This is done by sending the appropriate requests (examples below) with a unique node-id.

The connection parameters are used to specify connection parameters and the client’s (lighty gNMI RESTCONF) way of authenticating.

The property connection-type is enum and can be set to two values:

  • INSECURE: Skip TLS validation with certificates
  • PLAINTEXT:  Disable TLS validation

When the device requires the client to authenticate with registered certificates, remove the connection-type property. Then, add the keystore-id property with the ID of the registered certificates.

If the device requires username/password validation, then fill username and password in the credentials container. This container is optional.

In case the device requires additional parameters in the gNMI request/response, there is a container called extensions-parameters, where a defined set of parameters can be optionally included in the gNMI request and response. Those parameters are:

  • overwrite-data-type is used to overwrite the type field of gNMI GetRequest.
  • use-model-name-prefix is used when the device requires a module prefix in the first element name of gNMI request path
  • path-target is used to specify the context of a particular stream of data and is only set in prefix for a path
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
curl --request PUT 'http://127.0.0.1:8888/restconf/data/network-topology:network-topology/topology=gnmi-topology/node=node-id-1' \
--header 'Content-Type: application/json' \
--data-raw '{
"node": [
{
"node-id": "node-id-1",
"connection-parameters": {
"host": "127.0.0.1",
"port": 9090,
"connection-type": "INSECURE",
"credentials": {
"username": "admin",
"password": "admin"
}
}
}
]
}'
curl --request PUT 'http://127.0.0.1:8888/restconf/data/network-topology:network-topology/topology=gnmi-topology/node=node-id-1' \ --header 'Content-Type: application/json' \ --data-raw '{ "node": [ { "node-id": "node-id-1", "connection-parameters": { "host": "127.0.0.1", "port": 9090, "connection-type": "INSECURE", "credentials": { "username": "admin", "password": "admin" } } } ] }'
curl --request PUT 'http://127.0.0.1:8888/restconf/data/network-topology:network-topology/topology=gnmi-topology/node=node-id-1' \
--header 'Content-Type: application/json' \
--data-raw '{
    "node": [
        {
            "node-id": "node-id-1",
            "connection-parameters": {
                "host": "127.0.0.1",
                "port": 9090,
                "connection-type": "INSECURE",
                "credentials": {
                    "username": "admin",
                    "password": "admin"
                }
            }
        }
    ]
}'

Create a Mountpoint with Custom Certificates

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
curl --request PUT 'http://127.0.0.1:8888/restconf/data/network-topology:network-topology/topology=gnmi-topology/node=node-id-1' \
--header 'Content-Type: application/json' \
--data-raw '{
"node": [
{
"node-id": "node-id-1",
"connection-parameters": {
"host": "127.0.0.1",
"port": 9090,
"keystore-id": "keystore-id-1",
"credentials": {
"username": "admin",
"password": "admin"
}
}
}
]
}'
curl --request PUT 'http://127.0.0.1:8888/restconf/data/network-topology:network-topology/topology=gnmi-topology/node=node-id-1' \ --header 'Content-Type: application/json' \ --data-raw '{ "node": [ { "node-id": "node-id-1", "connection-parameters": { "host": "127.0.0.1", "port": 9090, "keystore-id": "keystore-id-1", "credentials": { "username": "admin", "password": "admin" } } } ] }'
curl --request PUT 'http://127.0.0.1:8888/restconf/data/network-topology:network-topology/topology=gnmi-topology/node=node-id-1' \
--header 'Content-Type: application/json' \
--data-raw '{
    "node": [
        {
            "node-id": "node-id-1",
            "connection-parameters": {
                "host": "127.0.0.1",
                "port": 9090,
                "keystore-id": "keystore-id-1",
                "credentials": {
                    "username": "admin",
                    "password": "admin"
                }
            }
        }
    ]
}'

Get State of Registered gNMI Device

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
curl --request GET 'http://127.0.0.1:8888/restconf/data/network-topology:network-topology/topology=gnmi-topology/node=node-id-1'
curl --request GET 'http://127.0.0.1:8888/restconf/data/network-topology:network-topology/topology=gnmi-topology/node=node-id-1'
curl --request GET 'http://127.0.0.1:8888/restconf/data/network-topology:network-topology/topology=gnmi-topology/node=node-id-1'

[Example] RESTCONF gNMI GetRequest

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
curl --location --request GET 'http://127.0.0.1:8888/restconf/data/network-topology:network-topology/topology=gnmi-topology/node=node-id-1/yang-ext:mount/openconfig-interfaces:interfaces'
curl --location --request GET 'http://127.0.0.1:8888/restconf/data/network-topology:network-topology/topology=gnmi-topology/node=node-id-1/yang-ext:mount/openconfig-interfaces:interfaces'
curl --location --request GET 'http://127.0.0.1:8888/restconf/data/network-topology:network-topology/topology=gnmi-topology/node=node-id-1/yang-ext:mount/openconfig-interfaces:interfaces'

[Example] RESTCONF gNMI SetRequest

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
curl --request PUT 'http://127.0.0.1:8888/restconf/data/network-topology:network-topology/topology=gnmi-topology/node=node-id-1/yang-ext:mount/interfaces/interface=br0/ethernet/config' \
--header 'Content-Type: application/json' \
--data-raw '{
"openconfig-if-ethernet:config": {
"enable-flow-control": false,
"openconfig-if-aggregate:aggregate-id": "admin",
"auto-negotiate": true,
"port-speed": "openconfig-if-ethernet:SPEED_10MB"
}
}'
curl --request PUT 'http://127.0.0.1:8888/restconf/data/network-topology:network-topology/topology=gnmi-topology/node=node-id-1/yang-ext:mount/interfaces/interface=br0/ethernet/config' \ --header 'Content-Type: application/json' \ --data-raw '{ "openconfig-if-ethernet:config": { "enable-flow-control": false, "openconfig-if-aggregate:aggregate-id": "admin", "auto-negotiate": true, "port-speed": "openconfig-if-ethernet:SPEED_10MB" } }'
curl --request PUT 'http://127.0.0.1:8888/restconf/data/network-topology:network-topology/topology=gnmi-topology/node=node-id-1/yang-ext:mount/interfaces/interface=br0/ethernet/config' \
--header 'Content-Type: application/json' \
--data-raw '{
    "openconfig-if-ethernet:config": {
        "enable-flow-control": false,
        "openconfig-if-aggregate:aggregate-id": "admin",
        "auto-negotiate": true,
        "port-speed": "openconfig-if-ethernet:SPEED_10MB"
    }
}'

Disconnect gNMI Device

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
curl --request DELETE 'http://127.0.0.1:8888/restconf/data/network-topology:network-topology/topology=gnmi-topology/node=node-id-1'
curl --request DELETE 'http://127.0.0.1:8888/restconf/data/network-topology:network-topology/topology=gnmi-topology/node=node-id-1'
curl --request DELETE 'http://127.0.0.1:8888/restconf/data/network-topology:network-topology/topology=gnmi-topology/node=node-id-1'

[RESTCONF] gNMI Operations Mapping

The supported HTTP methods are listed below:

  • YANG Node Type GET POST
  • Configuration Data
  • YANG RPC
  • Non-Configuration Data
  • HTTP Method
  • POST, PUT, PATCH, DELETE, GET
  • GET
  • POST

For each REST request, the lighty.io gNMI  RESTCONF app invokes the appropriate gNMI operation GnmiSet/GnmiGet to process the request.

Below is the mapping of HTTP operations to gNMI operations:

  • HTTP Method
  • GET
  • POST
  • PATCH
  • PUT
  • DELETE
  • gNMI Operation
  • GnmiGet
  • GnmiGet
  • GnmiGet
  • GnmiGet
  • GnmiGet
  • Request Data
  • path
  • path, payload
  • path, payload
  • path, payload
  • path, payload
  • Response Data
  • status, payload
  • status
  • status
  • status
  • status

[RESTCONF] GET Method Mapping

In both cases, we will be reading data, but from different data stores.

  • Reading data from the operational datastore invokes readOperationalData() in GnmiGet:
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
src/main/java/io/lighty/gnmi/southbound/mountpoint/ops/GnmiGet.java
src/main/java/io/lighty/gnmi/southbound/mountpoint/ops/GnmiGet.java
src/main/java/io/lighty/gnmi/southbound/mountpoint/ops/GnmiGet.java
  • Reading data from the configuration datastore invokes readConfigurationData() in GnmiGet:
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
src/main/java/io/lighty/gnmi/southbound/mountpoint/ops/GnmiGet.java
src/main/java/io/lighty/gnmi/southbound/mountpoint/ops/GnmiGet.java
src/main/java/io/lighty/gnmi/southbound/mountpoint/ops/GnmiGet.java

[RESTCONF] PUT/POST/PATCH/DELETE Method Mapping

  • Sending data to operational/configuration datastore invokes method set() in GnmiSet:
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
src/main/java/io/lighty/gnmi/southbound/mountpoint/ops/GnmiSet.java
src/main/java/io/lighty/gnmi/southbound/mountpoint/ops/GnmiSet.java
src/main/java/io/lighty/gnmi/southbound/mountpoint/ops/GnmiSet.java

A list of input parameters come from method request in the form of fields of update messages: update, replace, and delete fields.

  • PUT/POST request method sends update messages through two fields: update and replaces fields
  • PATCH request method sends update messages through the update field
  • DELETE request method sends update messages through the delete field

Further Support & Questions

PANTHEON.tech open-sourced lighty.io a while ago, giving the community a unique chance to discover the power of lighty.io in their SDN solution.

If you require enterprise support, integration, or training, make sure to contact us so we can help you catch up with the future of networking.


by Martin Bugáň, Ivan Čaládi, Peter Šuňa & Marek Zaťko