06-27-2017 09:59 PM - edited 03-01-2019 04:20 AM
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 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.
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.
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.
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.
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.
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.
"/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.
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.
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}]
$
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}
$
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}
$
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}
$
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}
$
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>
If we use JSON-RPC user has to login, what will be the advantage over using REST?
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.
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.
Find answers to your questions by entering keywords or phrases in the Search bar above. New here? Use these resources to familiarize yourself with the NSO Developer community: