Skip to content

Commit

Permalink
Added GetOper functions from @alucarddelta
Browse files Browse the repository at this point in the history
Added GetOper functions
  • Loading branch information
nleiva committed May 15, 2023
2 parents 702b206 + 7c3d4be commit f782286
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 19 deletions.
57 changes: 41 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,37 +25,41 @@ The end goal is to enable use-cases where multiple interactions with devices are
## Table of Contents

- [gRPC library for Cisco IOS XR](#grpc-library-for-cisco-ios-xr)
* [Prerequisite Tools](#prerequisite-tools)
* [Usage](#usage)
+ [Get Config](#get-config)
+ [Show Commands](#show-commands)
- [Table of Contents](#table-of-contents)
- [Prerequisite Tools](#prerequisite-tools)
- [Usage](#usage)
- [Get Config (example/getconfig)](#get-config-examplegetconfig)
- [Get Operation](#get-operation)
- [Show Commands](#show-commands)
- [Clear text](#clear-text)
- [JSON](#json)
+ [Configuring the router](#configuring-the-router)
- [Configuring the router](#configuring-the-router)
- [CLI config (Merge)](#cli-config-merge)
- [JSON (Merge)](#json-merge)
- [JSON (Replace)](#json-replace)
- [Using a YANG config Template (Merge)](#using-a-yang-config-template-merge)
+ [Removing router config](#removing-router-config)
- [Removing router config](#removing-router-config)
- [JSON](#json-1)
+ [CLI config multiple routers simultaneously (Merge)](#cli-config-multiple-routers-simultaneously-merge)
+ [Telemetry](#telemetry)
- [CLI config multiple routers simultaneously (Merge)](#cli-config-multiple-routers-simultaneously-merge)
- [Telemetry](#telemetry)
- [JSON (GPBKV)](#json-gpbkv)
- [JSON (GPBKV): Exploring the fields](#json-gpbkv-exploring-the-fields)
- [JSON (GPBKV): OpenConfig](#json-gpbkv-openconfig)
- [GPB (Protobuf)](#gpb-protobuf)
+ [Config and Validate](#config-and-validate)
+ [Actions](#actions)
- [Config and Validate](#config-and-validate)
- [Actions](#actions)
- [Ping](#ping)
- [Traceroute](#traceroute)
- [Log Generation](#log-generation)
- [Crypto Key Generation](#crypto-key-generation)
+ [Bypass the config file](#bypass-the-config-file)
* [XR gRPC Config](#xr-grpc-config)
+ [Port range](#port-range)
* [Certificate file](#certificate-file)
* [Compiling the proto files](#compiling-the-proto-files)
* [Compiling the Examples](#compiling-the-examples)
- [Bypass the config file](#bypass-the-config-file)
- [XR gRPC Config](#xr-grpc-config)
- [Port range](#port-range)
- [Certificate file](#certificate-file)
- [Self-signed certificate for package testing](#self-signed-certificate-for-package-testing)
- [Generating Go binding from protobuf files](#generating-go-binding-from-protobuf-files)
- [Running the examples](#running-the-examples)
- [Links](#links)

## Prerequisite Tools

Expand Down Expand Up @@ -113,6 +117,27 @@ config from sandbox-iosxr-1.cisco.com:57777
2022/05/11 16:55:36 This process took 715.136564ms
```

### Get Operation

Provides the output of IOS XR operations data (eg. optic light levels) from YANG/JSON formatted request, from the list in [config.json](example/input/config.json). It reads the target from [yangoper.json](example/input/yangoper.json).

- example/getoper
``` console
$ ./getoper

<snip>
"optics-db-info": {
"transport-admin-state": "tas-ui-is",
"controller-state": "optics-state-up"
}
}
]
}
}
}

2023/05/12 10:08:27 This process took 4.465176769s
```
### Show Commands

Provides the output of IOS XR cli commands for one router defined in [config.json](example/input/config.json). Two output format options are available; Unstructured text and JSON encoded:
Expand Down
31 changes: 31 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,37 @@ func GetConfig(ctx context.Context, conn *grpc.ClientConn, js string, id int64)
}
}

// GetOper returns the operation data for specific YANG path elements
// described in 'js'.
func GetOper(ctx context.Context, conn *grpc.ClientConn, js string, id int64) (string, error) {
var s string
// 'c' is the gRPC stub.
c := pb.NewGRPCConfigOperClient(conn)

// 'a' is the object we send to the router via the stub.
a := pb.GetOperArgs{ReqId: id, Yangpathjson: js}

// 'st' is the streamed result that comes back from the target.
st, err := c.GetOper(ctx, &a)
if err != nil {
return s, fmt.Errorf("gRPC GetConfig failed: %w", err)
}

for {
// Loop through the responses in the stream until there is nothing left.
r, err := st.Recv()
if err == io.EOF {
return s, nil
}
if len(r.GetErrors()) != 0 {
return s, fmt.Errorf("error triggered by remote host for ReqId: %v; %s", id, r.GetErrors())
}
if len(r.GetYangjson()) > 0 {
s += r.GetYangjson()
}
}
}

// CLIConfig configs the target with CLI commands described in 'cli'.
func CLIConfig(ctx context.Context, conn *grpc.ClientConn, cli string, id int64) error {
// 'c' is the gRPC stub.
Expand Down
46 changes: 46 additions & 0 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const (
defaultKey = "test/key.pem"
defaultCmd = "show test"
defaultYang = "{\"Cisco-IOS-XR-test:tree\": [null]}"
defaultOperYang = "{\"Cisco-IOS-XR-controller-optics-oper:optics-oper\": [null]}"
defaultSubsID = "TEST"
defaultCommitID uint32 = 100000002
wrongCmd = "show me the money"
Expand Down Expand Up @@ -715,6 +716,51 @@ func TestGetConfig(t *testing.T) {
time.Sleep(200 * time.Millisecond)
}

func TestGetOper(t *testing.T) {
x := xr.CiscoGrpcClient{
User: defaultUser,
Password: defaultPass,
Host: strings.Join([]string{defaultAddr, defaultPort}, ""),
Cert: defaultCert,
Domain: "localhost",
Timeout: defaultTimeout,
}

tt := []struct {
name string
paths string
enc int64
err string
}{
{name: "local connection", paths: defaultOperYang},
{name: "wrong paths", paths: wrongYang, err: wrongYangErr},
}
s := Server(t, "none")
conn, ctx, err := xr.Connect(x)
if err != nil {
t.Fatalf("could not setup a client connection to %v", x.Host)
}
var id int64 = 1
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
output, err := xr.GetOper(ctx, conn, tc.paths, id)
fmt.Println(output)
if err != nil {
if strings.Contains(err.Error(), wrongYangErr) && tc.err == wrongYangErr {
return
}
t.Fatalf("failed to get operation data %v", x.Host)
}
})
id++
}
conn.Close()
s.Stop()
// To avoid tests failing in Travis CI, we sleep for 0.2 seconds, otherwise it
// reports 'bind: address already in use' when trying to run the next function test
time.Sleep(200 * time.Millisecond)
}

func TestMergeConfig(t *testing.T) {
x := xr.CiscoGrpcClient{
User: defaultUser,
Expand Down
1 change: 1 addition & 0 deletions example/getoper/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
getconfig
61 changes: 61 additions & 0 deletions example/getoper/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package main

import (
"flag"
"fmt"
"io/ioutil"
"log"
"math/rand"
"time"

xr "github.com/nleiva/xrgrpc"
)

func timeTrack(start time.Time) {
elapsed := time.Since(start)
log.Printf("This process took %s\n", elapsed)

}

func main() {
// To time this process
defer timeTrack(time.Now())

// YANG path arguments; defaults to "yangoper.json"
ypath := flag.String("ypath", "../input/yangoper.json", "YANG path arguments")
// Config file; defaults to "config.json"
cfg := flag.String("cfg", "../input/config.json", "Configuration file")
flag.Parse()
// Determine the ID for the transaction.
r := rand.New(rand.NewSource(time.Now().UnixNano()))
id := r.Int63n(10000)
var output string

// Define target parameters from the configuration file
targets := xr.NewDevices()
err := xr.DecodeJSONConfig(targets, *cfg)
if err != nil {
log.Fatalf("could not read the config: %v\n", err)
}

// Setup a connection to the target. 'd' is the index of the router
// in the config file
d := 0
conn, ctx, err := xr.Connect(targets.Routers[d])
if err != nil {
log.Fatalf("could not setup a client connection to %s, %v", targets.Routers[d].Host, err)
}
defer conn.Close()

// Get YANG config file
js, err := ioutil.ReadFile(*ypath)
if err != nil {
log.Fatalf("could not read file: %v: %v\n", *ypath, err)
}

output, err = xr.GetOper(ctx, conn, string(js), id)
if err != nil {
log.Fatalf("could not get the operation data from %s, %v", targets.Routers[d].Host, err)
}
fmt.Printf("\noperation data from %s\n %s\n", targets.Routers[d].Host, output)
}
1 change: 1 addition & 0 deletions example/input/getoper.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"Cisco-IOS-XR-controller-optics-oper:optics-oper": [null]}
3 changes: 0 additions & 3 deletions proto/ems/ems_grpc.proto
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,7 @@ service gRPCConfigOper {

rpc ConfigDiscardChanges(DiscardChangesArgs) returns(DiscardChangesReply) {};

// Get only returns oper data
//
rpc GetOper(GetOperArgs) returns(stream GetOperReply) {};
// Do we need "Get" also to give combined oper and config?

// Get Telemetry Data
rpc CreateSubs(CreateSubsArgs) returns(stream CreateSubsReply) {};
Expand Down

0 comments on commit f782286

Please sign in to comment.