srljrpc

package module
v0.9.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 28, 2023 License: BSD-3-Clause Imports: 20 Imported by: 1

README

SR Linux JSON RPC client

JSON RPC client library implementation for SR Linux aimed to simplify the ways talking to SR Linux devices via JSON RPC.

How To

Introduction

Besides just self explanatory naming it's good to create a sort of the workflow to describe semantics of provided API. Giving some outlook on available methods and options is quite important as well, while working on the first implementation in GO is was not so obvious how provide simple interface at the same time hiding overall complexity via the exposure of well understandable interface elements. This document try to explore available without sophisticated scenarios like service activation, but rather focus on elementary steps and actions to build them.

API workflow

For the sake of demo, we will use Containerlab as brilliant network simulation tool and dedicated clab setup published together with JSON RPC package lab. The same virtual lab is used to perform integration testing as well as for client sample implementation. The setup can be ramped-up by using cd _clab followed by sudo clab deploy command. Our simple program will grow up each and every steps allowing to have necessary grip with the subject API. It does not pretend to be comprehensive, but overall should be easy to learn. Over the next releases we will further extend it based on users feedback.

Client creation

As you can see we have very simple code to start with JSON RPC client. While client is created necessary pre-flight checks are done in background to elicit system hostname and software version. That serving two objectives: verifying immediately and get HTTP client ready, giving minimum information about system in order to take decision about necessary YANG modules to use further if you would like to support ENV with mix of SR Linux versions.

package main

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/azyablov/srljrpc"
	"github.com/azyablov/srljrpc/apierr"
	"github.com/azyablov/srljrpc/formats"
	"github.com/azyablov/srljrpc/yms"
)

var (
	host   = "clab-evpn-leaf1"
	user   = "admin"
	pass   = "NokiaSrl1!"
	port   = 443
	hostOC = "clab-evpn-spine3"
)

func main() {
	// Create a new JSON RPC client with credentials and port (used 443 as default for the sake of demo).
	c, err := srljrpc.NewJSONRPCClient(&host, srljrpc.WithOptCredentials(&user, &pass), srljrpc.WithOptPort(&port))
	if err != nil {
		panic(err)
	}
	fmt.Printf("Target hostname: %s\nTarget system version: %s\n", c.GetHostname(), c.GetSysVer())
}

Finally, our simple program is printing hostname and system version.

[azyablov@ecartman srljrpc_client_example]$ go run client.go 
Target hostname: leaf1
Target system version: v23.3.2-106-g4490a15b16
[azyablov@ecartman srljrpc_client_example]$ 
Options available

Worth to mention that JSON RPC client has a few options available:

  • WithOptPort(port *int)
  • WithOptTimeout(t time.Duration)
  • WithOptCredentials(u, p *string)
  • WithOptTLS(t *TLSAttr)

All of them are quite self-descriptive, but WithOptTLS should be a bit more explained to give 100% confidence. First of all, JSON file to TLSAttr object looks like the following (taken from real lab):

    {
        "tls_attr": {
            "skip_verify": false,
            "cert_file": "/home/azyablov/clab/nokia-evpn-lab/clab-evpn/ca/spine1/spine1.pem",
            "key_file": "/home/azyablov/clab/nokia-evpn-lab/clab-evpn/ca/spine1/spine1-key.pem",
            "ca_file": "/home/azyablov/clab/nokia-evpn-lab/clab-evpn/ca/root/root-ca.pem"
        }
    }

The last could be read from file / string / everything implements Read interface, so basically nothing new:

    var ta TLSAttr
	err := json.Unmarshal([]byte(jsonStr), &ta)
	if err != nil {
		panic(err)
	}
Sending requests
Getting config

Now, let's read something from running configuration by using the next two xpaths:

  • /network-instance[name="MAC-VRF 1"]
  • /system/lldp

As you can see code is quite trivial:

	// GET method example.
	getResp, err := c.Get(`/network-instance[name="MAC-VRF 1"]`, `/system/lldp`)
	if err != nil {
		panic(err)
	}
	rStr, err := json.MarshalIndent(getResp.Result, "", "  ")
	if err != nil {
		panic(err)
	}

	fmt.Printf("Response from GET: %s\n", string(rStr))

As soon as we submitted two xpath, we are getting two elements in the list of getResp.Result:

Target hostname: leaf1
Target system version: v23.3.2-106-g4490a15b16
================================================================================
Get() example:
================================================================================
Response: [
  {
    "type": "srl_nokia-network-instance:mac-vrf",
    "interface": [
      {
        "name": "ethernet-1/1.1"
      }
    ],
    "vxlan-interface": [
      {
        "name": "vxlan0.1"
      }
    ],
    "protocols": {
      "bgp-evpn": {
        "srl_nokia-bgp-evpn:bgp-instance": [
          {
            "id": 1,
            "admin-state": "enable",
            "vxlan-interface": "vxlan0.1",
            "evi": 1,
            "ecmp": 2
          }
        ]
      },
      "srl_nokia-bgp-vpn:bgp-vpn": {
        "bgp-instance": [
          {
            "id": 1,
            "route-distinguisher": {
              "rd": "1:11"
            },
            "route-target": {
              "export-rt": "target:65011:1",
              "import-rt": "target:65011:1"
            }
          }
        ]
      }
    }
  },
  {
    "admin-state": "enable"
  }
]
Getting state

Let's image we have to have some stats/operational state alongside with configuration info, so you need to use STATE datastore in order to get it. Well, one shoot of Stats() methods is resolving it in quite convenient way. In the example below we are using /system/json-rpc-server xpath.

    // Getting stats.
	fmt.Println("State() example:")
	stateResp, err := c.State("/system/json-rpc-server")
	if err != nil {
		panic(err)
	}
	outHelper(stateResp.Result)

In order to read code more readable outHelper() function was introduced, which is unmarshalling and printing results:

func outHelper(v any) {
	rStr, err := json.MarshalIndent(v, "", "  ")
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s\n", string(rStr))
}

So, you should see something more on top of already mentioned output.

State() example:
================================================================================
[
  {
    "admin-state": "enable",
    "commit-confirmed-timeout": 0,
    "network-instance": [
      {
        "name": "mgmt",
        "http": {
          "admin-state": "enable",
          "oper-state": "up",
          "use-authentication": true,
          "session-limit": 10,
          "port": 80,
          "source-address": [
            "::"
          ]
        },
        "https": {
          "admin-state": "enable",
          "oper-state": "up",
          "use-authentication": true,
          "session-limit": 10,
          "port": 443,
          "tls-profile": "clab-profile",
          "source-address": [
            "::"
          ]
        }
      }
    ],
    "unix-socket": {
      "admin-state": "disable",
      "oper-state": "down",
      "use-authentication": true,
      "socket-path": ""
    }
  }
]
================================================================================
Updating/Replacing/Deleting config

Example below is reading values before UPDATE/DELETE/REPLACE operations and executing them respectively. Validate() and Tools() methods are available as well, first is used to validate configuration updates w/o applying it, the second one used to perform /tools operations like /tools interface ethernet-1/1 statistics clear. Confirmation timeout (ct) must be set to 0 to apply changes immediately, or positive int to allow additional verification checks before explicit confirmation OR rolling it back automatically.

	// Updating/Replacing/Deleting config
	fmt.Println(strings.Repeat("=", 80))
	fmt.Println("Update()/Delete()/Replace() example:")
	fmt.Println(strings.Repeat("=", 80))

	pvs := []srljrpc.PV{
		{Path: `/interface[name=ethernet-1/51]/subinterface[index=0]/description`, Value: "UPDATE"},
		{Path: `/system/banner`, Value: "DELETE"},
		{Path: `/interface[name=mgmt0]/description`, Value: "REPLACE"},
	}
	// Getting existing config for the sake of demo.
	for _, pv := range pvs {
		getResp, err := c.Get(pv.Path)
		if err != nil {
			panic(err)
		}
		outHelper(getResp.Result)
	}

	mdmResp, err := c.Update(0, pvs[0]) // setting 0 as confirmation timeout to apply changes immediately.
	if err != nil {
		panic(err)
	}
	outHelper(mdmResp.Result)
	mdmResp, err = c.Delete(0, pvs[1].Path) // setting 0 as confirmation timeout to apply changes immediately.
	if err != nil {
		panic(err)
	}
	outHelper(mdmResp.Result)
	mdmResp, err = c.Replace(0, pvs[2]) // setting 0 as confirmation timeout to apply changes immediately.
	if err != nil {
		panic(err)
	}
	outHelper(mdmResp.Result)

Effectively one operation is just four lines of code:

	mdmResp, err = c.Replace(0, pvs[2]) // setting 0 as confirmation timeout to apply changes immediately.
	if err != nil {
		panic(err)
	}

Console output should be altered by the following contents:

Update()/Delete()/Replace() example:
[
  "to_spine1"
]
[
  {
    "login-banner": "................................................................\n:                  Welcome to Nokia SR Linux!                  :\n:              Open Network OS for the NetOps era.             :\n:                                                              :\n:    This is a freely distributed official container image.    :\n:                      Use it - Share it                       :\n:                                                              :\n: Get started: https://learn.srlinux.dev                       :\n: Container:   https://go.srlinux.dev/container-image          :\n: Docs:        https://doc.srlinux.dev/22-6                    :\n: Rel. notes:  https://doc.srlinux.dev/rn22-6-2                :\n: YANG:        https://yang.srlinux.dev/v22.6.2                :\n: Discord:     https://go.srlinux.dev/discord                  :\n: Contact:     https://go.srlinux.dev/contact-sales            :\n................................................................\n"
  }
]
[
  {}
]
[
  {}
]
[
  {}
]
[
  {}
]

If we would again query it we should get the following output, since commit was applied before (automatically by JSON RPC servers) and running configuration updated:

================================================================================
Update()/Delete()/Replace() example:
================================================================================
[
  "UPDATE"
]
================================================================================
[
  {}
]
================================================================================
[
  "REPLACE"
]
================================================================================
[
  {}
]
================================================================================
[
  {}
]
================================================================================
[
  {}
]

Worth to mention, BulkSet(delete []PV, replace []PV, update []PV, ym yms.EnumYmType, ct int) (*Response, error) method. It allows you to combine delete, replace and update actions into the one SET request, so you can combine operations efficiently. YANG models namespace could be specified as well, where SRL corresponds to native models and OC to OpenConfig models. Confirmation timeout (ct) must be set to 0 to apply changes immediately, or positive int to allow additional verification checks before explicit confirmation OR rolling it back automatically.

Tools

Well, let's imagine you need to clear BGP session or reset counters on interface. An example below provides with how to do it using Tools() method.

  toolsResp, err := c.Tools(srljrpc.PV{
		Path:  "/interface[name=ethernet-1/1]/ethernet/statistics/clear",
		Value: srljrpc.CommandValue("")})
	if err != nil {
		panic(err)
	}
	outHelper(toolsResp.Result)

If request is correct and no mistakes you should not see anything special in output.

c.Tools() example:
================================================================================
[
  {}
]
================================================================================
Diff, OpenConfig yang-models and error handling

Here we will consider number of examples to demonstrate ways to use diff method, OpenConfig models namespace and improved error handling. We will use OpenCOnfig, because by default RPC interface assumes SRL, and as such we got number of examples already. The first example demonstrates typical case of JSON RPC Error.

	// Then for the sake of example we will use DIFF method with Bulk update: TestBulkDiffCandidate.
	// DiffCandidate method is more simple and intended to use in cases you require only one action out of three: UPDATE, DELETE, REPLACE.
	// That's essentially Bulk update with different operations: UPDATE, DELETE, REPLACE, while using yang-models of OpenConfig.
	fmt.Println(strings.Repeat("=", 80))
	fmt.Println("BulkDiff() example with error:")
	fmt.Println(strings.Repeat("=", 80))

	pvs = []srljrpc.PV{
		{Path: `/system/config/login-banner`, Value: "DELETE"},
		{Path: `/interfaces/interface[name=mgmt0]/config/description`, Value: "REPLACE"},
		{Path: `/interfaces/interface[name=ethernet-1/11]/subinterfaces/subinterface[index=0]/config/description`, Value: "UPDATE"},
	}
	bulkDiffResp, err := c.BulkDiff(pvs[0:1], pvs[1:2], pvs[2:], yms.OC)
	if err != nil {
		if cerr, ok := err.(apierr.ClientError); ok {
			fmt.Printf("ClientError error: %s\n", cerr) // ClientError
			if cerr.Code == apierr.ErrClntJSONRPC {     // We expect JSON RPC error here and checking via the message code.
				outHelper(bulkDiffResp)
				// Output supposed to be something like this:
				// {
				// 	"jsonrpc": "2.0",
				// 	"id": 568258505525892051,
				// 	"error": {
				// 	  "id": 0,
				// 	  "message": "Server down or restarting"
				// 	}
				//   }
				// This is an indication OC is not supported on the target system, so we will use another target system spine3.
			}
		} else {
			panic(err) // Unexpected outcome.
		}
	} else {
		outHelper(bulkDiffResp.Result)
	}

In the console you should see something similar to:

================================================================================
c.TestBulkDiffCandidate() example with error:
================================================================================
ClientError error: do: JSON-RPC error
{
  "jsonrpc": "2.0",
  "id": 3198004165524188886,
  "error": {
    "id": 0,
    "message": "Server down or restarting"
  }
}
================================================================================

Out of the error message we can figure out that out target is not serving OpenConfig namespace, so we need to switch target (spine3 in our example). The next case demonstrates how to approach error handling provided by the module. In order to keep necessary abstraction level, but allow diving deep into the root cause and allows extensive error handling automation. Idiomatic golang approach implemented with Unwrap() error method, while decision can be take based on the codes provided by ClientError or MessageError object. apierr package is self-documented and provides quite extensive error codes footprint.

const (
	ErrClntUndefined EnumCltErr = iota
	ErrClntNoHost
	ErrClntTargetVerification
	ErrClntMarshalling
	ErrClntHTTPReqCreation
	ErrClntHTTPSend
	ErrClntHTTPStatus
// <omitted for brevity>
)

Coming to our example...

	fmt.Println(strings.Repeat("=", 80))
	fmt.Println("BulkDiff() example with error:")
	fmt.Println(strings.Repeat("=", 80))

	pvs = []srljrpc.PV{
		{Path: `/system/config/login-banner`, Value: "DELETE"},
		{Path: `/interfaces/interface[name=mgmt0]/config/description`, Value: ""}, // Empty value will cause an error.
		{Path: `/interfaces/interface[name=ethernet-1/11]/subinterfaces/subinterface[index=0]/config/description`, Value: "UPDATE"},
	}
	// Change target hostname to spine3, which supports OC.
	// Create a new JSON RPC client with credentials and port (used 443 as default for the sake of demo).
	cOC, err := srljrpc.NewJSONRPCClient(&hostOC, srljrpc.WithOptCredentials(&user, &pass), srljrpc.WithOptPort(&port))
	if err != nil {
		panic(err)
	}

	bulkDiffResp, err = cOC.BulkDiff(pvs[0:1], pvs[1:2], pvs[2:], yms.OC)
	if err != nil {
		// Unwrapping error to investigate a root cause.
		if cerr, ok := err.(apierr.ClientError); ok {
			fmt.Printf("ClientError error: %s\n", cerr)                   // ClientError
			for uerr := err.(apierr.ClientError).Unwrap(); uerr != nil; { // We expect ClientError here, so we can unwrap it.
				fmt.Printf("Underlaying error: %s\n", uerr.Error())
				if u2err, ok := uerr.(interface{ Unwrap() error }); ok {
					uerr = u2err.Unwrap()
				} else {
					break
				}
			}
		}
		// }

	} else {
		outHelper(bulkDiffResp.Result)
	}

And finally output demonstrates two nested errors...

================================================================================
BulkDiff() example with error:
================================================================================
ClientError error: bulkDiffCandidate: RPC request creation error
Underlaying error: newRequest(): error adding commands in request
Underlaying error: value isn't specified or not found in the path for method diff

After corrections made, we should have our code executed without errors.

	fmt.Println(strings.Repeat("=", 80))
	fmt.Println("BulkDiff() example w/o error:")
	fmt.Println(strings.Repeat("=", 80))
	// Adding changes into PV pairs to fix our artificial error and do things right ))
	pvs = []srljrpc.PV{
		{Path: `/system/config/login-banner`, Value: "DELETE"},
		{Path: `/interfaces/interface[name=mgmt0]/config/description`, Value: "REPLACE"},
		{Path: `/interfaces/interface[name=ethernet-1/11]/subinterfaces/subinterface[index=0]/config/description`, Value: "UPDATE"},
	}

	bulkDiffResp, err = cOC.BulkDiff(pvs[0:1], pvs[1:2], pvs[2:], yms.OC)
	if err != nil {
		outHelper(bulkDiffResp)
		panic(err)
	}
	// Parsing JSON response to get the message.
	var data []interface{}
	err = json.Unmarshal(bulkDiffResp.Result, &data)
	if err != nil {
		fmt.Println("Error parsing JSON:", err)
	}
	message := data[0].(string)
	fmt.Println(message)
================================================================================
BulkDiff() example w/o error:
================================================================================
  {
    "interfaces": {
      "interface": [
        {
          "name": "ethernet-1/11",
          "subinterfaces": {
            "subinterface": [
              {
                "index": 0,
                "config": {
-                 "description": "to_leaf1"
+                 "description": "UPDATE"
                }
              }
            ]
          }
        },
        {
          "name": "mgmt0",
          "config": {
+           "description": "REPLACE"
          }
        }
      ]
    },
    "system": {
      "config": {
-       "login-banner": "................................................................\n:                  Welcome to Nokia SR Linux!                  :\n:              Open Network OS for the NetOps era.             :\n:                                                              :\n:    This is a freely distributed official container image.    :\n:                      Use it - Share it                       :\n:                                                              :\n: Get started: https://learn.srlinux.dev                       :\n: Container:   https://go.srlinux.dev/container-image          :\n: Docs:        https://doc.srlinux.dev/23-3                    :\n: Rel. notes:  https://doc.srlinux.dev/rn23-3-1                :\n: YANG:        https://yang.srlinux.dev/v23.3.1                :\n: Discord:     https://go.srlinux.dev/discord                  :\n: Contact:     https://go.srlinux.dev/contact-sales            :\n................................................................\n"
      }
    }
  }
================================================================================
Confirmation timeout and CallBack functions

As it was mentioned before Update()/Replace()/Delete() function provide ct parameter, which was set to 0 before. Setting it to something >0 must trigger rollback on the switch, if changes aren't confirmed on time via TOOLS datastore /system/configuration/confirmed-accept. Library provides a bit more advanced function BulkSetCallBack() allowing to encapsulate your verification logic inside your function, which must satisfy exposed interface.

// CallBackConfirm type to represent a callback function to confirm a request.
// In case of confirm commit must return true, otherwise false.
type CallBackConfirm func(req *Request, resp *Response) (bool, error)

In the example below BulkSetCallBack() called to apply interface description. The provided call back function just prints our RPC request / response and returns false to allow changes roll-back on SR Linux switch automatically, i.e. not confirming them.

func confirmCallBack(req *srljrpc.Request, resp *srljrpc.Response) (bool, error) {
	// This is a callback function to be called after confirmation timeout is expired.
	// It is supposed to be used to confirm or cancel changes as per logic of the implementation.
	// In this example we will just print out request and response to console and confirm changes - for the sake ot example that's replace sophisticated logic.
	fmt.Println("Request:")
	outHelper(req)
	fmt.Println("Response:")
	outHelper(resp)
	return false, nil
}

Client implementation example with BulkSetCallBack() runs it in separate thread, as such allowing parallel executions against several targets(switches). At the same time CallBack function has all necessary information (request and response) to implement verification logic as part of CI pipeline in order to take necessary decision whether roll back or not roll back changes on the target.

	fmt.Println(strings.Repeat("=", 80))
	fmt.Println("BulkSetCallBack() with cancellation:")
	fmt.Println(strings.Repeat("=", 80))
	empty := []srljrpc.PV{}
	sysInfPath := "/interface[name=system0]/description"
	initVal := []srljrpc.PV{{Path: sysInfPath, Value: srljrpc.CommandValue("INITIAL")}}

	_, err = c.Update(0, initVal[0]) // should be no error and system0 interface description should be set to "INITIAL".
	if err != nil {
		panic(err)
	}

	getResp, err = c.Get(sysInfPath)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Get() against %s before BulkSetCallBack():\n", sysInfPath)
	fmt.Println(strings.Repeat("=", 80))
	outHelper(getResp.Result)

	chResp := make(chan *srljrpc.Response) // Channel for response.
	chErr := make(chan error)              // Channel for error.
	go func() {
		newValueToConfirm := []srljrpc.PV{{Path: sysInfPath, Value: srljrpc.CommandValue("System Loopback")}}
		// setting confirmation timeout to 30 seconds to allow comfortable time to verify changes. Setting 27 seconds as time to exec call back function.
		// confirmCallBack is a function to be called after confirmation timeout is expired to confirm or cancel changes as per logic of the implementation.
		resp, err := c.BulkSetCallBack(empty, empty, newValueToConfirm, yms.SRL, 8, 5, confirmCallBack)

		// sending response and error to channels back to main thread.
		chResp <- resp
		chErr <- err
	}()
	// Meanwhile we can do something else in main thread.
	// For example, we can get current value of the interface.
	time.Sleep(2 * time.Second) // Allow 3 seconds to apply changes.
	getResp, err = c.Get(sysInfPath)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Get() against %s:\n", sysInfPath)
	fmt.Println(strings.Repeat("=", 80))
	outHelper(getResp.Result)

	// Waiting for response and error from channel.
	resp := <-chResp
	err = <-chErr
	if err != nil {
		panic(err)
	}
	// We expect response to be nil, as we set confirmation timeout to 30 seconds and call back function to 27 seconds.
	if resp != nil {
		fmt.Println("Unexpected response. Expected nil.")
		outHelper(resp) // Unexpected outcome.
	}
	time.Sleep(30 * time.Second) // Allow enough time to rollback changes.
	getResp, err = c.Get(sysInfPath)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Get() against %s after confirmation timeout expired:\n", sysInfPath)
	fmt.Println(strings.Repeat("=", 80))
	outHelper(getResp.Result)

Output should look similar to the following one:

================================================================================
BulkSetCallBack() with cancellation:
================================================================================
Get() against /interface[name=system0]/description before BulkSetCallBack():
================================================================================
[
  "INITIAL"
]
================================================================================
Get() against /interface[name=system0]/description:
================================================================================
[
  "System Loopback"
]
================================================================================
Request:
{
  "jsonrpc": "2.0",
  "id": 1560953543165558821,
  "method": "set",
  "params": {
    "commands": [
      {
        "path": "/interface[name=system0]/description",
        "value": "System Loopback",
        "action": "update"
      }
    ],
    "output-format": "json",
    "datastore": "candidate",
    "yang-models": "srl",
    "confirm-timeout": 8
  }
}
================================================================================
Response:
{
  "jsonrpc": "2.0",
  "id": 1560953543165558821,
  "result": [
    {}
  ]
}
================================================================================
Get() against /interface[name=system0]/description after confirmation timeout expired:
================================================================================
[
  "INITIAL"
]
================================================================================
Sending CLI commands

Sending CLI commands is one of the main methods to interact with network devices, even industry is rapidly adopting MDM interfaces. Lab builds, validations, troubleshooting and many other operational tasks would require interaction with CLI. JSON RPC interface of SR Linux is providing very convenient way to automate and use CLI commands, especially we you are using number of CLI plug-ins to elicit essential information, but still giving you full flexibility to utilize structured data outputs.

Executing CLI commands

In CLI example below two commands are executed with output format JSON and one with out format TABLE. For the output format TABLE []string type were used to marshal it correctly into string and print in nice form in STDOUT. As easy to see number of line of code remains +/- stable in terms of getting necessary results. Of course, assuming logic of your application is not counted here.

	// CLI
	fmt.Println("c.CLI() example:")
	cliResp, err := c.CLI([]string{"show version", "show network-instance summary"}, formats.JSON)
	if err != nil {
		panic(err)
	}
	outHelper(cliResp.Result)

	cliResp, err = c.CLI([]string{"show system lldp neighbor"}, formats.TABLE)
	if err != nil {
		panic(err)
	}
	type Table []string
	var t Table
	b, _ := cliResp.Result.MarshalJSON()
	err = json.Unmarshal(b, &t)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s\n", t[0])
================================================================================
c.CLI() example:
================================================================================
[
  {
    "basic system info": {
      "Hostname": "leaf1",
      "Chassis Type": "7220 IXR-D2",
      "Part Number": "Sim Part No.",
      "Serial Number": "Sim Serial No.",
      "System HW MAC Address": "1A:0B:01:FF:00:00",
      "Software Version": "v23.3.1",
      "Build Number": "343-gab924f2e64",
      "Architecture": "x86_64",
      "Last Booted": "2023-05-29T09:07:32.174Z",
      "Total Memory": "23640339 kB",
      "Free Memory": "7898847 kB"
    }
  },
  {
    "Network Instance": [
      {
        "Name": "MAC-VRF 1",
        "Type": "mac-vrf",
        "Admin state": "enable",
        "Oper state": "up",
        "Router id": "N/A"
      },
      {
        "Name": "default",
        "Type": "default",
        "Admin state": "enable",
        "Oper state": "up"
      },
      {
        "Name": "mgmt",
        "Type": "ip-vrf",
        "Admin state": "enable",
        "Oper state": "up",
        "Description": "Management network instance"
      }
    ]
  }
]
================================================================================
+---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+
|           Name            |         Neighbor          |   Neighbor System Name    |    Neighbor Chassis ID    |  Neighbor First Message   |   Neighbor Last Update    |       Neighbor Port       |
+===========================+===========================+===========================+===========================+===========================+===========================+===========================+
| ethernet-1/51             | 1A:35:06:FF:00:00         | spine1                    | 1A:35:06:FF:00:00         | 3 hours ago               | now                       | ethernet-1/11             |
| ethernet-1/52             | 1A:CA:07:FF:00:00         | spine2                    | 1A:CA:07:FF:00:00         | 3 hours ago               | now                       | ethernet-1/11             |
| mgmt0                     | 1A:0F:04:FF:00:00         | leaf3                     | 1A:0F:04:FF:00:00         | 3 hours ago               | now                       | mgmt0                     |
| mgmt0                     | 1A:35:06:FF:00:00         | spine1                    | 1A:35:06:FF:00:00         | 3 hours ago               | now                       | mgmt0                     |
| mgmt0                     | 1A:95:03:FF:00:00         | leaf2                     | 1A:95:03:FF:00:00         | 3 hours ago               | now                       | mgmt0                     |
| mgmt0                     | 1A:B4:05:FF:00:00         | leaf4                     | 1A:B4:05:FF:00:00         | 3 hours ago               | now                       | mgmt0                     |
| mgmt0                     | 1A:CA:07:FF:00:00         | spine2                    | 1A:CA:07:FF:00:00         | 3 hours ago               | now                       | mgmt0                     |
+---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+

All examples provided in this document can be found in repository with SR Linux JSON RPC library samples.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type CLIParams

type CLIParams struct {
	Commands []string `json:"commands"`
	*formats.OutputFormat
}

CLIParams defines a container for CLI commands and optional OutputFormat object.

type CLIRequest

type CLIRequest struct {
	JSONRpcVersion string `json:"jsonrpc"`
	ID             int    `json:"id"`
	*methods.Method
	Params *CLIParams `json:"params"`
}
JSONRpcVersion is mandatory. Version, which must be ‟2.0”. No other JSON RPC versions are currently supported.

ID is mandatory. Client-provided integer. The JSON RPC responds with the same ID, which allows the client to match requests to responses when there are concurrent requests. Implementation uses random numbers and verifies Response ID is the same as Request ID. Embeds Method (set to CLI by NewCLIRequest) and CLIParams.

func NewCLIRequest

func NewCLIRequest(cmds []string, of formats.EnumOutputFormats) (*CLIRequest, error)

Creates a new CLIRequest object using the provided list of commands executed one by one and output format. Each command should have a response under JSON RPC response message - Response under "result" field with respective command index.

func (*CLIRequest) GetID

func (r *CLIRequest) GetID() int

Returns the ID of the request.

func (*CLIRequest) Marshal

func (r *CLIRequest) Marshal() ([]byte, error)

Marshalling CLIRequest into JSON.

func (*CLIRequest) SetOutputFormat

func (r *CLIRequest) SetOutputFormat(of formats.EnumOutputFormats) error

Sets the output format of the request.

type CallBackConfirm added in v0.9.7

type CallBackConfirm func(req *Request, resp *Response) (bool, error)

CallBackConfirm type to represent a callback function to confirm a request. In case of confirm commit must return true, otherwise false.

type ClientOption

type ClientOption func(*JSONRPCClient) error

ClientOption is a function type that applies options to a JSONRPCClient object.

func WithOptCredentials

func WithOptCredentials(u, p *string) ClientOption

ClientOption to specify credentials.

func WithOptPort

func WithOptPort(port *int) ClientOption

ClientOption to update target port.

func WithOptTLS

func WithOptTLS(t *TLSAttr) ClientOption

ClientOption to specify TLS configuration. Setting the TLS configuration will override the default skipVerify option and will enforce the verification of the server certificate. Assumes minimum TLS version 1.2.

func WithOptTimeout

func WithOptTimeout(t time.Duration) ClientOption

ClientOption to set connection timeout.

type Command

type Command struct {
	Path                 string          `json:"path"`
	Value                string          `json:"value,omitempty"`
	PathKeywords         json.RawMessage `json:"path-keywords,omitempty"`
	Recursive            *bool           `json:"recursive,omitempty"`
	IncludeFieldDefaults *bool           `json:"include-field-defaults,omitempty"`
	*actions.Action
	*datastores.Datastore
}

Command is mandatory. List of commands used to execute against the called method. Multiple commands can be executed with a single request. Number of CommandOptions could be used to influence command behavior. Embeds Action and Datastore objects.

func NewCommand

func NewCommand(action actions.EnumActions, path string, value CommandValue, opts ...CommandOption) (*Command, error)

Constructor for a new Command object with mandatory action, path and value fields, and optional command options to influence command behavior.

type CommandOption added in v0.9.2

type CommandOption func(*Command) error

CommandOption type to represent a function that configures a Command.

func WithAddPathKeywords

func WithAddPathKeywords(kw json.RawMessage) CommandOption

CommandOptions to add path keywords to the command to substitute named parameters with the path field.

func WithDatastore

func WithDatastore(d datastores.EnumDatastores) CommandOption

CommandOptions to set datastore for the command.

func WithDefaults

func WithDefaults() CommandOption

CommandOptions to enable inclusion of default values in returned JSON RPC response for the command.

func WithoutRecursion

func WithoutRecursion() CommandOption

Provides CommandOptions to disable recursion for the command.

type CommandValue

type CommandValue string

CommandValue type to represent a value of a command.

type JSONRPCClient

type JSONRPCClient struct {
	// contains filtered or unexported fields
}

JSONRPCClient type to represent a JSON RPC client: HTTP client, NE(target) and related info.

func NewJSONRPCClient

func NewJSONRPCClient(host *string, opts ...ClientOption) (*JSONRPCClient, error)

Creates a new JSON RPC client and applies options in order of appearance.

func (*JSONRPCClient) BulkDiff added in v0.9.7

func (c *JSONRPCClient) BulkDiff(delete []PV, replace []PV, update []PV, ym yms.EnumYmType) (*Response, error)

Bulk CRUD method of JSONRPCClient. Executes a DIFF method with REPLACE/UPDATE/DELETE action request against CANDIDATE datastore. delete/replace/update are path-value pairs. yang model type is mandatory for diff to specify: SRL or OC.

func (*JSONRPCClient) BulkSet added in v0.9.7

func (c *JSONRPCClient) BulkSet(delete []PV, replace []PV, update []PV, ym yms.EnumYmType, ct int) (*Response, error)

Bulk CRUD method of JSONRPCClient. Executes a SET method with REPLACE/UPDATE/DELETE action request against CANDIDATE datastore. ct is the timeout in seconds for the confirm operation, set to 0 to disable. delete/replace/update are path-value pairs. All the PVs are applied immediately in the same order as they are provided. yang model type is mandatory for diff to specify: SRL or OC.

func (*JSONRPCClient) BulkSetCallBack added in v0.9.7

func (c *JSONRPCClient) BulkSetCallBack(delete []PV, replace []PV, update []PV, ym yms.EnumYmType, ct int, cbt int, cbf CallBackConfirm) (*Response, error)

Bulk CRUD method of JSONRPCClient w/ CallBackConfirm callback and mandatory confirm timeout. Executes a SET method with REPLACE/UPDATE/DELETE action request against CANDIDATE datastore. All the PVs are applied immediately in the same order as they are provided. yang model type is mandatory for diff to specify: SRL or OC. JSON RPC Response is not nil if the callback function returns true. if callback function returns false, the both response&error are nil to indicate changes rolled back and NE back to previous state.

func (*JSONRPCClient) CLI

Executes CLI commands against the target device (NE).

func (*JSONRPCClient) Delete

func (c *JSONRPCClient) Delete(ct int, paths ...string) (*Response, error)

SetDelete method of JSONRPCClient. Executes a SET/DELETE action request against CANDIDATE datastore. t is the timeout in seconds for the confirm operation, set to 0 to disable. paths is the list of path to delete. Yang model type is default(SRL).

func (*JSONRPCClient) DiffCandidate added in v0.9.2

func (c *JSONRPCClient) DiffCandidate(action actions.EnumActions, ym yms.EnumYmType, pvs ...PV) (*Response, error)

DiffCandidate method of JSONRPCClient. Executes a DIFF/<action> action request against CANDIDATE datastore. Yang model type is default(SRL). pvs are path-value pairs. The action parameter must be one of DELETE, REPLACE, or UPDATE.

func (*JSONRPCClient) Do

func (c *JSONRPCClient) Do(r Requester) (*Response, error)

Calls the JSON RPC server and returns the response.

func (*JSONRPCClient) Get

func (c *JSONRPCClient) Get(paths ...string) (*Response, error)

Get method of JSONRPCClient. Executes a GET request against RUNNING datastore.

func (*JSONRPCClient) GetHostname

func (c *JSONRPCClient) GetHostname() string

GetHostname returns the hostname of the target after verification.

func (*JSONRPCClient) GetSysVer

func (c *JSONRPCClient) GetSysVer() string

GetSysVer returns the system version of the target after verification.

func (*JSONRPCClient) Replace

func (c *JSONRPCClient) Replace(ct int, pvs ...PV) (*Response, error)

SetReplace method of JSONRPCClient. Executes a SET/REPLACE action request against CANDIDATE datastore. ct is the timeout in seconds for the confirm operation, set to 0 to disable. pvs is the list of path-value pairs. Yang model type is default(SRL).

func (*JSONRPCClient) State

func (c *JSONRPCClient) State(paths ...string) (*Response, error)

Get state method of JSONRPCClient. Executes a GET request against STATE datastore.

func (*JSONRPCClient) Tools

func (c *JSONRPCClient) Tools(pvs ...PV) (*Response, error)

Tools() action of the method SET. Executes a SET/UPDATE action request against TOOLS datastore. Yang model type is default(SRL).

func (*JSONRPCClient) Update

func (c *JSONRPCClient) Update(ct int, pvs ...PV) (*Response, error)

SetUpdate method of JSONRPCClient executing a SET/UPDATE action request against CANDIDATE datastore. ct is the timeout in seconds for the confirm operation, set to 0 to disable. pvs is the list of path-value pairs. Yang model type is default(SRL).

func (*JSONRPCClient) Validate

func (c *JSONRPCClient) Validate(action actions.EnumActions, pvs ...PV) (*Response, error)

Validate() action of the method SET. Executes a SET/VALIDATE specified action request against CANDIDATE datastore. Yang model type is default(SRL).

type JSONRPCTarget

type JSONRPCTarget struct {
	// contains filtered or unexported fields
}

JSONRPCTarget type to represent a JSON RPC target: NE(target), TLS attributes, credentials.

type PV

type PV struct {
	Path  string       `json:"path"`
	Value CommandValue `json:"value"`
}

PV type to represent a path-value pair.

type Params

type Params struct {
	Commands []Command `json:"commands"`
	*formats.OutputFormat
	*datastores.Datastore
	*yms.YmType
	*ctimeout.ConfirmTimeout
}

Params defines a container for Commands and optional OutputFormat and Datastore objects. Params embeds: OutputFormat, Datastore objects and ConfirmTimeout (used for confirmed commits).

type Request

type Request struct {
	JSONRpcVersion string `json:"jsonrpc"`
	ID             int    `json:"id"`
	*methods.Method
	Params *Params `json:"params"`
}

JSON RPC Request for get / set / validate methods.

JSONRpcVersion is mandatory. Version, which must be ‟2.0”. No other JSON RPC versions are currently supported.
ID is mandatory. Client-provided integer. The JSON RPC responds with the same ID, which allows the client to match requests to responses when there are concurrent requests.
Implementation uses random numbers and verifies Response ID is the same as Request ID.
Embeds Method and Params.

func NewDiffRequest added in v0.9.2

func NewDiffRequest(delete []PV, replace []PV, update []PV, ym yms.EnumYmType, of formats.EnumOutputFormats, ds datastores.EnumDatastores) (*Request, error)

NewDiffRequest provides a new Request with the DIFF method and the given commands, which more advanced version of JRPCClient.Diff().

func NewGetRequest added in v0.9.2

func NewGetRequest(paths []string, recursion bool, defaults bool, of formats.EnumOutputFormats, ds datastores.EnumDatastores) (*Request, error)

NewGetRequest provides a new Request with the GET method and the given paths, which more advanced version of JRPCClient.Get().

func NewRequest

func NewRequest(m methods.EnumMethods, cmds []*Command, opts ...RequestOption) (*Request, error)

NewRequest provides a new Request with the given method, commands and options. Sequence of functions is applied to the Request in the order of appearance.

func NewSetRequest added in v0.9.2

func NewSetRequest(delete []PV, replace []PV, update []PV, ym yms.EnumYmType, of formats.EnumOutputFormats, ds datastores.EnumDatastores, ct int) (*Request, error)

NewSetRequest provides a new Request with the SET method and the given commands, which more advanced version of JRPCClient.Set().

func NewValidateRequest added in v0.9.2

func NewValidateRequest(delete []PV, replace []PV, update []PV, ym yms.EnumYmType, of formats.EnumOutputFormats, ds datastores.EnumDatastores) (*Request, error)

NewValidateRequest provides a new Request with the VALIDATE method and the given commands, which more advanced version of JRPCClient.Validate().

func (*Request) GetID

func (r *Request) GetID() int

Get Request ID.

func (*Request) Marshal

func (r *Request) Marshal() ([]byte, error)

Marshaling of the Request into JSON.

func (*Request) SetConfirmTimeout added in v0.9.7

func (r *Request) SetConfirmTimeout(t int) error

Setting confirm timeout for the Request.

func (*Request) SetOutputFormat

func (r *Request) SetOutputFormat(of formats.EnumOutputFormats) error

Set output format for the request via embedded Params.

type RequestOption

type RequestOption func(*Request) error

RequestOption is a function type that applies options to a Request. Each RequestOption has validation logic implemented to check correctness of the option application and return non nil apierr.MessageError if the option is not correct.

func WithConfirmTimeout added in v0.9.7

func WithConfirmTimeout(t int) RequestOption

Defines confirm timeout RequestOption.

func WithOutputFormat

func WithOutputFormat(of formats.EnumOutputFormats) RequestOption

Defines output format RequestOption.

func WithRequestDatastore

func WithRequestDatastore(ds datastores.EnumDatastores) RequestOption

RequestOption that sets the datastore for the request in Params level. Overrides the datastore in Command level! Implemented logic in this option is to check if datastore is valid for the selected method and perform necessary checks on the commands.

func WithYmType added in v0.9.2

func WithYmType(ym yms.EnumYmType) RequestOption

Defines yang models RequestOption.

type Requester

type Requester interface {
	Marshal() ([]byte, error)
	GetMethod() (methods.EnumMethods, error)
	MethodName() string
	GetID() int
	SetOutputFormat(of formats.EnumOutputFormats) error
}

Requester is an interface used by the JSON RPC client to send a request to the server.

type Response

type Response struct {
	JSONRpcVersion string          `json:"jsonrpc"`
	ID             int             `json:"id"`
	Result         json.RawMessage `json:"result,omitempty"`
	Error          *RpcError       `json:"error,omitempty"`
}

JSON RPC response message. When a rpc call is made, the Server MUST reply with a Response, except for in the case of Notifications. The Response is expressed as a single JSON Object. Result and error are mutually exclusive, so only one of them can be expected. Error is represented as a pointer to RpcError, so it can be nil.

JSONRpcVersion is mandatory. Version, which must be ‟2.0”. No other JSON RPC versions are currently supported.
ID is mandatory. Client-provided integer. The JSON RPC responds with the same ID, which allows the client to match requests to responses when there are concurrent requests.
Result is REQUIRED on success (jsonRawMessage). This member MUST NOT exist if there was an error invoking the method. The value of this member is determined by the method invoked on the Server.
Error is REQUIRED on error. This member MUST NOT exist if there was no error triggered during invocation. The value for this member MUST be an RpcError object.

func (*Response) GetID

func (r *Response) GetID() int

Returns ID of the response in order to compare with request ID.

func (*Response) Marshal

func (r *Response) Marshal() ([]byte, error)

Marshalling Response into JSON.

type RpcError

type RpcError struct {
	ID      int    `json:"id"`
	Message string `json:"message"`
	Data    string `json:"data,omitempty"`
}

RpcError is generic JSON RPC error object. When a rpc call is made, the Server MUST reply with a Response, except for in the case of Notifications. The Response is expressed as a single JSON Object.

ID should be set to client provided ID.
Message is a string providing a short description of the error. The message SHOULD be limited to a concise single sentence."
Data is a primitive or structured value that contains additional information about the error. This may be omitted. The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.).

type TLSAttr

type TLSAttr struct {
	CAFile     *string `json:"ca_file,omitempty"`     // CA certificate file in PEM format.
	CertFile   *string `json:"cert_file,omitempty"`   // Client certificate file in PEM format.
	KeyFile    *string `json:"key_file,omitempty"`    // Client private key file.
	SkipVerify *bool   `json:"skip_verify,omitempty"` // Disable certificate validation during TLS session ramp-up.
}

TLSAttr type to represent TLS attributes

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL