cancel
Showing results for 
Search instead for 
Did you mean: 
cancel

JSON-RPC Basics

1761
Views
34
Helpful
3
Comments

What is JSON-RPC


JSON-RPC is one of Northbound APIs supported by NSO, and the client can operate NSO using HTTP(S). Unlike the REST API, it is possible to execute multiple commands within one session (or transaction).

Many methods have historically been used as Remote Procedure Call (RPC). XML-RPC began to be used in the late 1990's, SOAP exists as an extension of it. JSON-RPC began to be used in mid 2000, and the way of thinking is the same as these other RPCs.

The latest version of JSON-RPC is currently 2.0. For its specifications and details, please check the following.

JSON-RPC 2.0 Specification

JSON-RPC in NSO

JSON - RPC does not specify the specification of the Transport layer. In JSON-RPC used by NSO, payload is transmitted and received over HTTP (S) as used in many implementations.

The contents of Payload looks like the following as like general JSON-RPC.

Request:

{

"Jsonrpc" : "2.0" ,

"Method" : "XXXXX" ,

"Params" : {

     "Param 1" : XXXXX ,

     "Param2" : XXXXX

},

"Id" : XXXXX

}

Response (successful):

{

"Jsonrpc": "2.0",

"Result": XXXXX,

"Id": XXXXX

}

Response (error):

{

"Jsonrpc": "2.0",

"Error": {

     "Code": XXXXX,

     "Message": XXXXX,

     "Data": XXXXX,

     "Type": XXXXX

},

"Id": XXXXX

}

The type field is added when application error (code = -32000, NSO specific) is occurring, and it contains the error details.

WebUI

NSO has implemented WebUI for GUI operations. It is using JSON-RPC internally, indeed, this is a great example of JSON-RPC implementation. Users can see how it is working using development feature on each browser software.

Example of JSON-RPC

For any JSON-RPC calls, the request needs to be authenticated with an exception of login method. This method doesn't require to be pre-authenticated, of course naturally. With this method, we can get a HTTP Cookie, and following RPC invocation has to be with this cookie.

In this example, we will add a user and delete the user. Also this explains how to run actions.

Session Creation (login)

We need to execute login method to login first time. HTTP Response includes the Cookie in the Set-Cookie header, and data in sessionid_(port number) variable needs to be included in the following invocation. We can confirm those information using "-i" option in curl command.

$ curl -i -c cookie -X POST http://localhost:8080/jsonrpc -H 'Content-Type: application/json' -d '

{

"jsonrpc":"2.0",

"id":1,

"method":"login",

"params":{

   "user":"admin",

   "passwd":"admin"

}

}'

HTTP/1.1 200 OK

Server:

Date: Sat, 29 Apr 2017 03:56:28 GMT

Expires: Thu, 01 Jan 1970 00:00:00 GMT

Cache-Control: private, no-cache, no-store, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0

Content-Length: 36

Content-Type: application/json

Set-Cookie: sessionid_8080=sessjZtwG0ekhzDUZXwvrkW2rw==; path=/; HttpOnly

Vary: Accept-Encoding

{"jsonrpc":"2.0","result":{},"id":1}

curl command can save the cookie information using "-c" option, and it can be used with "-b" option later.

Start new transaction (new_trans)

Obtaining/Modification data on CDB is through a transaction. We need to create one for manipulating CDB.

$ curl -b cookie -X POST http://localhost:8080/jsonrpc -H 'Content-Type: application/json' -d '

{

"jsonrpc":"2.0",

"id":1,

"method":"new_trans",

"params":{

   "db": "running",

   "mode": "read"

}

}'

{"jsonrpc":"2.0","result":{"th":1},"id":1}

$

You can specify the mode of the transaction, either "read" or "read_write". In this example, new read only transaction has been started. Please notice transaction handle (th) 1 is assigned for it from the response data.

Just to confirm, let's try get_trans method to see.

$ curl -b cookie -X POST http://localhost:8080/jsonrpc -H 'Content-Type: application/json' -d '

{

"jsonrpc":"2.0",

"id":1,

"method":"get_trans",

"params":{}

}'

{"jsonrpc":"2.0","result":{"trans":[{"th":1,"db":"running","mode":"read"}]},"id":1}

$

We see one transaction with th=1 now.

Retrieving config (show_config)

show_config method is equivalent to "show running-config" on NCS CLI.  In this example, "/aaa" is obtained, and it is same with "show running-config aaa".

$ curl -b cookie -X POST http://localhost:8080/jsonrpc -H 'Content-Type: application/json' -d '

{

"jsonrpc":"2.0",

"id":1,

"method":"show_config",

"params":{

   "th":1,

   "path":"/aaa"

}

}'

{"jsonrpc":"2.0","result":{"config":"aaa {\n    authentication {\n        users {\n            user admin {\n                uid        65534;\n                gid        65534;\n                password   $6$EOlKr.zRujz34uPj$hHyfbMtbvcQWPwvwsmJ8VRaLn1mu/fdZAYKDRkHaL7UEm.WOxPafGr807a2hulfeJwrDSdr4YqLo/Kvg8KOrG0;\n                ssh_keydir /var/ncs/homes/admin/.ssh;\n                homedir    /var/ncs/homes/admin;\n            }\n            user oper {\n                uid        65534;\n                gid        65534;\n                password   $6$7kfOoClUgxfsrRyl$DBpyWaMkBzPQ2njtxfj9ysaSA7WeabDWaW9EfaaokpNGsSiGuu.Ek6VH12EzEiJghl7aCEFy9Sj24eAPxOcBP/;\n                ssh_keydir /var/ncs/homes/oper/.ssh;\n                homedir    /var/ncs/homes/oper;\n            }\n            user private {\n                uid        65534;\n                gid        65534;\n                password   $6$;\n                ssh_keydir /var/ncs/homes/private/.ssh;\n                homedir    /var/ncs/homes/private;\n            }\n            user public {\n                uid        65534;\n                gid        65534;\n                password   $6$;\n                ssh_keydir /var/ncs/homes/public/.ssh;\n                homedir    /var/ncs/homes/public;\n            }\n        }\n    }\n}\n"},"id":1}

$

The output of "show running-config aaa | display curly-braces" is returned in text format. From an application point of view, parsing this data is not effective, and let's get the data in list in the next example.

Getting list keys (get_list_keys)

"/aaa/authentication/users/user" is a list. Using get_list_keys method, let's get keys of this list.

$ curl -b cookie -X POST http://localhost:8080/jsonrpc -H 'Content-Type: application/json' -d '

{

"jsonrpc":"2.0",

"id":1,

"method":"get_list_keys",

"params":{

   "th":1,

   "path":"/aaa/authentication/users/user"

}

}'

{"jsonrpc":"2.0","result":{"keys":[["admin"],["oper"],["private"],["public"]],"total_count":4,"lh":-1},"id":1}

$

4 users of admin, oper, private, public are found in this NSO instance.

Creating a new list instance (create)

Let's add a user "test" in the below key path through JSON-RPC.

/aaa authentication/users/user{test}

The transaction we used till this point was read only transaction. To write data, we need to create a read-write transaction.

$ curl -b cookie -X POST http://localhost:8080/jsonrpc -H 'Content-Type: application/json' -d '

{

"jsonrpc":"2.0",

"id":1,

"method":"new_trans",

"params":{

   "db": "running",

   "mode": "read_write"

}

}'

{"jsonrpc":"2.0","result":{"th":2},"id":1}

$

A transaction (th=2) is created. We will use this for adding a user.

To add an instance in a list, create method needs to be called.

$ curl -b cookie -X POST http://localhost:8080/jsonrpc -H 'Content-Type: application/json' -d '

{

"jsonrpc":"2.0",

"id":1,

"method":"create",

"params":{

   "th": 2,

   "path": "/aaa/authentication/users/user{test}"

}

}'

{"jsonrpc":"2.0","result":{},"id":1}

$

The above is equivalent to the below commands in CLI.

admin@ncs# conf

admin@ncs(config)# aaa authentication users user test

To add an instance on /aaa/authentication/users/user list, uid/gid/password/ssh_keydir/homedir need to be also set together.

Set value on a leaf (set_value / set_values)

In this example, values are set onto leaves such as "/aaa/authentication/users/user{test}/uid". We can set one each by each HTTP requests (or set_value method), let's try the batch feature to configure in one HTTP request.

Please note that uid/gid leaf type is integer, however we use string value in JSON-RPC because set_value method accepts string values.

If null is set, the leaf is deleted.

$ curl -b cookie -X POST http://localhost:8080/jsonrpc -H 'Content-Type: application/json' -d '

[{

"jsonrpc":"2.0",

"id":1,

"method":"set_value",

"params":{

   "th": 2,

   "path": "/aaa/authentication/users/user{test}/uid",

   "value": "0"

}

},

{

"jsonrpc":"2.0",

"id":1,

"method":"set_value",

"params":{

   "th": 2,

   "path": "/aaa/authentication/users/user{test}/gid",

   "value": "0"

}

},

{

"jsonrpc":"2.0",

"id":1,

"method":"set_value",

"params":{

   "th": 2,

   "path": "/aaa/authentication/users/user{test}/password",

   "value": "cisco"

}

},

{

"jsonrpc":"2.0",

"id":1,

"method":"set_value",

"params":{

   "th": 2,

   "path": "/aaa/authentication/users/user{test}/ssh_keydir",

   "value": "/home/test/.ssh"

}

},

{

"jsonrpc":"2.0",

"id":1,

"method":"set_value",

"params":{

   "th": 2,

   "path": "/aaa/authentication/users/user{test}/homedir",

   "value": "/home/test"

}

}]'

[{"jsonrpc":"2.0","result":{},"id":1},{"jsonrpc":"2.0","result":{},"id":1},{"jsonrpc":"2.0","result":{},"id":1},{"jsonrpc":"2.0","result":{},"id":1},{"jsonrpc":"2.0","result":{},"id":1}]

$

Validate and Commit (validate_commit / commit)

The all required values are now in a transaction. Now we need to validate and commit them to put them in CDB.

Validation is done automatically in NCS CLI. In JSON-RPC (and NETCONF, RESTCONF), data validation needs to be done manually in a transaction. Only validated config can be committed.

Validate the config entered in the transaction

$ curl -b cookie -X POST http://localhost:8080/jsonrpc -H 'Content-Type: application/json' -d '

{

"jsonrpc":"2.0",

"id":1,

"method":"validate_commit",

"params":{

   "th": 2

}

}'

{"jsonrpc":"2.0","result":{},"id":1}

$

The below is an example when the validation is failed. The list user needs homedir to be set together, however if it is missing, the below error is shown.

$ curl -b cookie -X POST http://localhost:8080/jsonrpc -H 'Content-Type: application/json' -d '

{

"jsonrpc":"2.0",

"id":1,

"method":"validate_commit",

"params":{

   "th": 2

}

}'

{"jsonrpc":"2.0","error":{"type":"trans.validation_failed","code":-32000,"message":"Validation failed","data":{"errors":[{"reason":"is not configured","paths":["/aaa:aaa/authentication/users/user{test}/homedir"],"path":"/aaa:aaa/authentication/users/user{test}/homedir"}]}},"id":1}

$

Now let's commit.

We can add needed flags to change the behavior.

Example of Flags:

flag = ["dry-run=native"]

flag = ["no-networking"]

$ curl -b cookie -X POST http://localhost:8080/jsonrpc -H 'Content-Type: application/json' -d '

{

"jsonrpc":"2.0",

"id":1,

"method":"commit",

"params":{

   "th": 2,

   "flags" : []

}

}'

{"jsonrpc":"2.0","result":{},"id":1}

$

Getting values from leaves (get_value / get_values)

Now the commit was successful. Let's check if the values are now in CDB.

First, get_list_keys on the list should return the new user.

$ curl -b cookie -X POST http://localhost:8080/jsonrpc -H 'Content-Type: application/json' -d '

{

"jsonrpc":"2.0",

"id":1,

"method":"get_list_keys",

"params":{

  "th":1,

  "path":"/aaa/authentication/users/user"

}

}'

{"jsonrpc":"2.0","result":{"keys":[["admin"],["oper"],["private"],["public"],["test"]],"total_count":5,"lh":-1},"id":1}

$

A user "test" is now there. What is set for the user test's ssh_keydir?

$ curl -b cookie -X POST http://localhost:8080/jsonrpc -H 'Content-Type: application/json' -d '

{

"jsonrpc":"2.0",

"id":1,

"method":"get_value",

"params":{

  "th": 1,

  "path": "/aaa/authentication/users/user{test}/ssh_keydir"

}

}'

{"jsonrpc":"2.0","result":{"value":"/home/test/.ssh"},"id":1}

$

We now see "/home/test/.ssh" is set.

With get_values method, we can get the all in one shot.

$ curl -b cookie -X POST http://localhost:8080/jsonrpc -H 'Content-Type: application/json' -d '

{

"jsonrpc":"2.0",

"id":1,

"method":"get_values",

"params":{

"th": 1,

  "path": "/aaa/authentication/users/user{test}",

  "leafs": ["uid", "gid", "password", "ssh_keydir", "homedir"]

}

}'

{"jsonrpc":"2.0","result":{"values":[{"value":"0","access":{"read":true,"write":true}},{"value":"0","access":{"read":true,"write":true}},{"value":"$6$hXtiNxdt6ngFWZCO$MdOQfIe4cHEnzsZnVMkMIOH4eJ7ZGZEmNSUCYPO68SEHs2L/V9tZ1KbCIFFUfV52kl88JKbWmFCn2vrZNVuxA.","access":{"read":true,"write":true}},{"value":"/home/test/.ssh","access":{"read":true,"write":true}},{"value":"/home/test","access":{"read":true,"write":true}}]},"id":1}

$

Deletion of an instance in a list (delete)

Next, we will delete the user test using delete method.

$ curl -b cookie -X POST http://localhost:8080/jsonrpc -H 'Content-Type: application/json' -d '

{

"jsonrpc":"2.0",

"id":1,

"method":"delete",

"params":{

  "th": 2,

  "path": "/aaa/authentication/users/user{test}"

}

}'

{"jsonrpc":"2.0","result":{},"id":1}

$

Invoke actions (run_action)

Actions are RPCs defined in Yang model. Executing the node, NSO starts any logics programmed.

run_action method can be used to invoke the actions in JSON-RPC.

The below is an example of executing sync-from. Two devices, c0 and c1, are registered, and this action does sync-from on both devices.

This is equivalent to "devices sync-from" on NCS CLI.

$ curl -b cookie -X POST http://localhost:8080/jsonrpc -H 'Content-Type: application/json' -d '

{

"jsonrpc":"2.0",

"id":1,

"method":"run_action",

"params":{

  "th": 1,

  "path": "/devices/sync-from"

}

}'

{"jsonrpc":"2.0","result":[{"name":"sync-result/device","value":"c0"},{"name":"sync-result/result","value":"true"},{"name":"sync-result/device","value":"c1"},{"name":"sync-result/result","value":"true"}],"id":1}

$

Obtaining the schema of the models (get_schema)

An assumption that we know the models was there in the all examples up to here.

Because we know the model /aaa/authentication/users, we could do get_value/set_value, and we knew pid/gid were required. When the model is unknown, we can't do in the same way.

When we are creating services, the model should be known to developer, so that situation should not occur. In applications that need to walk on the tree, like the NSO WebUI, can't expect what the model is, so this can be a problem.

JSON-RPC has get_schema method for this use case. Using this method, client can know the schema and clients can work dynamically.

$ curl -b cookie -X POST http://localhost:8080/jsonrpc -H 'Content-Type: application/json' -d '

{

"jsonrpc":"2.0",

"id":1,

"method":"get_schema",

"params":{

  "th": 1,

  "path": "/aaa/authentication/users/user"

}

}'

{"jsonrpc":"2.0","result":{"meta":{"namespace":"http://tail-f.com/ns/aaa/1.1","keypath":"/aaa:aaa/authentication/users/user","prefix":"aaa","types":{"http://tail-f.com/ns/aaa/1.1:passwdStr":[{"name":"http://tail-f.com/ns/aaa/1.1:passwdStr"},

<omitted>

Comments
Cisco Employee

If we use JSON-RPC user has to login, what will be the advantage over using REST?

Cisco Employee

REST doesn't support "transactions" for multiple REST queries. Each HTTP query are committed every time.

With JSON-RPC, we can perform the two-phase-commit like NETCONF.

Cisco Employee

And the JSON-RPC has (significantly) more functionality in other areas as well. But it's a proprietary protocol, not a standard like RESTCONF. If you want maximum functionality within the standards realm, then go with NETCONF.

Try to think ahead what your future needs will be, because as some projects have found, it's somewhat boring to face the situation that the application you built on one protocol needs to be redesigned to use another at a late stage.