Skip to content

Commit

Permalink
Add Actions support to the library
Browse files Browse the repository at this point in the history
  • Loading branch information
nleiva committed May 29, 2018
1 parent 1146ded commit db3e459
Show file tree
Hide file tree
Showing 16 changed files with 660 additions and 98 deletions.
6 changes: 4 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
language: go

go:
- 1.8.3
go:
- 1.8.3
- 1.8.7
- 1.9.6
- tip # The latest version of Go

script:
Expand Down
197 changes: 197 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ The end goal is to enable use-cases where multiple interactions with devices are
+ [Service Layer API](#service-layer-api)
- [Add an IPv6 route](#add-an-ipv6-route)
- [SLA IOS XR config](#sla-ios-xr-config)
+ [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)
Expand Down Expand Up @@ -693,6 +698,198 @@ grpc
!
```
### Actions
There are multiple actions than can be triggered via gRPC on IOS XR devices running 6.3.1 or later. Below the YANG models supported to the date and some examples using this library.
```console
$ cd ~/yang/vendor/cisco/xr/641
$ ls | grep act.yang
Cisco-IOS-XR-cfgmgr-rollback-act.yang
Cisco-IOS-XR-crypto-act.yang
Cisco-IOS-XR-hwmod-mpa-reload-act.yang
Cisco-IOS-XR-infra-statsd-act.yang
Cisco-IOS-XR-ipv4-bgp-act.yang
Cisco-IOS-XR-ipv4-ospf-act.yang
Cisco-IOS-XR-ipv4-ping-act.yang
Cisco-IOS-XR-ipv4-traceroute-act.yang
Cisco-IOS-XR-ipv6-ospfv3-act.yang
Cisco-IOS-XR-ipv6-ping-act.yang
Cisco-IOS-XR-ipv6-traceroute-act.yang
Cisco-IOS-XR-isis-act.yang
Cisco-IOS-XR-lib-keychain-act.yang
Cisco-IOS-XR-ping-act.yang
Cisco-IOS-XR-snmp-test-trap-act.yang
Cisco-IOS-XR-syslog-act.yang
Cisco-IOS-XR-sysmgr-act.yang
Cisco-IOS-XR-traceroute-act.yang
Cisco-IOS-XR-upgrade-fpd-ng-act.yang
```
#### Ping
- IPv4 Ping (`example/action` with [ping4.json](example/input/action/ping4.json))
```console
$ ./action -act "../input/action/ping4.json"

output from [2001:420:2cff:1204::7816:1]:57344
{
"Cisco-IOS-XR-ping-act:output": {...}
}

2018/05/29 15:03:19 This process took 762.440427ms
```
- IPv6 Ping (`example/action` with [ping6.json](example/input/action/ping6.json))
```console
$ ./action -act "../input/action/ping6.json"

output from [2001:420:2cff:1204::7816:1]:57344
{
"Cisco-IOS-XR-ping-act:output": {...}
}

2018/05/29 15:08:34 This process took 800.050845ms
```
The extended output looks like this:
```json
{
"Cisco-IOS-XR-ping-act:output": {
"ping-response": {
"ipv6": {
"destination": "2001:420:2cff:1204::1",
"repeat-count": "2",
"data-size": "1350",
"timeout": "1",
"pattern": "abcd",
"rotate-pattern": false,
"replies": {
"reply": [
{
"reply-index": "1",
"result": "!"
},
{
"reply-index": "2",
"result": "!"
}
]
},
"hits": "2",
"total": "2",
"success-rate": "100",
"rtt-min": "2",
"rtt-avg": "2",
"rtt-max": "3"
}
}
}
}
```
#### Traceroute
- Dual-Stack traceroute (`example/action` with [traceroute.json](example/input/action/traceroute.json)). Target is an FQDN ([www.cisco.com](www.cisco.com)).
```console
./action -act "../input/action/traceroute.json"

output from [2001:420:2cff:1204::7816:1]:57344
{
"Cisco-IOS-XR-traceroute-act:output": {
"traceroute-response": {
"ipv6": {
"destination": "2001:420:1101:1::a",
"hops": {
"hop": [
{
"hop-index": 1,
"hop-address": "2001:420:2cff:1204::1",
"hop-hostname": "mrstn-vlan44.cisco.com",
"probes": {
"probe": [
{
"probe-index": 0,
"delta-time": 1
},
{
"probe-index": 1,
"delta-time": 1
},
{
"probe-index": 2,
"delta-time": 1
}
]
}
},
...

2018/05/29 15:12:50 This process took 15.241451154s
```
#### Log Generation
- Log event generation (`example/action` with [log.json](example/input/action/log.json)).
```console
$ ./action -act "../input/action/log.json"

output from [2001:420:2cff:1204::7816:1]:57344

2018/05/29 15:23:59 This process took 42.585985ms
```
On the router
```console
#
RP/0/RP0/CPU0:May 29 19:23:59.297 UTC: emsd[1096]: %OS-SYSLOG-1-LOG_ALERT : gRPC Generated: Device will be under maintenance for 2 hrs for planned activities
```
#### Crypto Key Generation
- SSH Crypto key generation (`example/action` with [crypto.json](example/input/action/crypto.json)).
```console
$ ./action -act "../input/action/crypto.json"

output from [2001:420:2cff:1204::7816:1]:57344

2018/05/29 15:26:06 This process took 2.721703931s
```
```console
#
RP/0/RP0/CPU0:May 29 19:26:04.249 UTC: cepki[420]: %SECURITY-CEPKI-6-KEY_INFO : crypto key RSA zeroized, label:test
RP/0/RP0/CPU0:May 29 19:26:06.907 UTC: cepki[420]: %SECURITY-CEPKI-6-KEY_INFO : crypto key RSA generated, label:test, modBits:2048
RP/0/RP0/CPU0:May 29 19:26:06.917 UTC: cepki[420]: %SECURITY-CEPKI-6-INFO : key database updated
```
```console
RP/0/RP0/CPU0:mrstn-7816-1.cisco.com# show crypto key mypubkey rsa
Tue May 29 19:26:49.693 UTC
Key label: test
Type : RSA General purpose
Size : 2048
Created : 19:26:04 UTC Tue May 29 2018
Data :
30820122 300D0609 2A864886 F70D0101 01050003 82010F00 3082010A 02820101
00C30A10 D8807E77 43B1D20A 9896CBC0 43EB8A6C 2C7D8B65 87AFED27 BC1B616C
1477913A C083155B CD4A2372 12CA49B4 48FCBBF3 87DC5C50 844FDF2E A88BBDC4
D32C42D7 5103FFBA 529EE896 7CAE4D64 4569A724 6488F738 1B370C07 654EE086
95A26AF2 314FBFD3 2C7335D1 1BD3DE17 94792028 BB70DF69 221DAF11 D4D31E99
FF438F6B 3809A72A 0EC1368B 5F223E26 3CAFC439 73EFCB50 609036D2 91128505
3769BD7E EDCEAD5E A059E0C3 FF4EC5A2 8EED960D 0933DCEB D0426734 30FFA057
2689900F BDC413EE 0F7640E2 724CC1DF 0701F4FA CFE1389B 53124C3E F217FA95
92A5FF5B C5DBBC3D EC5C62ED FCABC88F 1483CDFB F6EF29DF 3182C729 BA5A81B7
23020301 0001
```
### Bypass the config file
You can manually define the target without the config file [config.json](example/input/config.json), by calling the functional options "WithValue". See the snippet below from [definetarget](example/definetarget/main.go).
Expand Down
56 changes: 42 additions & 14 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ type loginCreds struct {
requireTLS bool
}


// Method of the PerRPCCredentials interface.
func (c *loginCreds) GetRequestMetadata(context.Context, ...string) (map[string]string, error) {
return map[string]string{
Expand All @@ -130,7 +129,6 @@ func (c *loginCreds) RequireTransportSecurity() bool {
return c.requireTLS
}


// Connect will return a grpc.ClienConn to the target. TLS encryption
func Connect(xr CiscoGrpcClient) (*grpc.ClientConn, context.Context, error) {
// opts holds the config options to set up the connection.
Expand All @@ -146,17 +144,17 @@ func Connect(xr CiscoGrpcClient) (*grpc.ClientConn, context.Context, error) {

// WithTimeout returns a DialOption that configures a timeout for dialing a ClientConn initially.
// This is valid if and only if WithBlock() is present
opts = append(opts, grpc.WithTimeout(time.Millisecond * time.Duration(1500)))
opts = append(opts, grpc.WithTimeout(time.Millisecond*time.Duration(1500)))
opts = append(opts, grpc.WithBlock())

// Add gRPC overall timeout to the config options array.
ctx, _ := context.WithTimeout(context.Background(), time.Second * time.Duration(xr.Timeout))
ctx, _ := context.WithTimeout(context.Background(), time.Second*time.Duration(xr.Timeout))

// Add user/password to config options array.
opts = append(opts, grpc.WithPerRPCCredentials(&loginCreds{
Username: xr.User,
Password: xr.Password,
requireTLS: true }))
Username: xr.User,
Password: xr.Password,
requireTLS: true}))

// conn represents a client connection to an RPC server (target).
conn, err := grpc.DialContext(ctx, xr.Host, opts...)
Expand All @@ -166,25 +164,24 @@ func Connect(xr CiscoGrpcClient) (*grpc.ClientConn, context.Context, error) {
return conn, ctx, err
}


// ConnectInsecure will return a grpc.ClienConn to the target. No TLS encryption
func ConnectInsecure(xr CiscoGrpcClient) (*grpc.ClientConn, context.Context, error) {
// opts holds the config options to set up the connection.
var opts []grpc.DialOption

// WithTimeout returns a DialOption that configures a timeout for dialing a ClientConn initially.
// This is valid if and only if WithBlock() is present
opts = append(opts, grpc.WithTimeout(time.Millisecond * time.Duration(1500)))
opts = append(opts, grpc.WithTimeout(time.Millisecond*time.Duration(1500)))
opts = append(opts, grpc.WithBlock())

// Add gRPC overall timeout to the config options array.
ctx, _ := context.WithTimeout(context.Background(), time.Second * time.Duration(xr.Timeout))
ctx, _ := context.WithTimeout(context.Background(), time.Second*time.Duration(xr.Timeout))

// Add user/password to config options array.
opts = append(opts, grpc.WithPerRPCCredentials(&loginCreds{
Username: xr.User,
Password: xr.Password,
requireTLS: false }))
Username: xr.User,
Password: xr.Password,
requireTLS: false}))

// Allow sending the credentials without TSL
opts = append(opts, grpc.WithInsecure())
Expand Down Expand Up @@ -228,8 +225,39 @@ func ShowCmdTextOutput(ctx context.Context, conn *grpc.ClientConn, cli string, i
}
}

// ActionJSON returns the output of an action commands as a JSON structured output.
func ActionJSON(ctx context.Context, conn *grpc.ClientConn, j string, id int64) (string, error) {
var s string
// 'c' is the gRPC stub.
c := pb.NewGRPCExecClient(conn)

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

// 'st' is the streamed result that comes back from the target.
st, err := c.ActionJSON(context.Background(), &a)
if err != nil {
return s, errors.Wrap(err, "gRPC ActionJSON failed")
}

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 {
si := strconv.FormatInt(id, 10)
return s, fmt.Errorf("error triggered by remote host for ReqId: %s; %s", si, r.GetErrors())
}
if len(r.GetYangjson()) > 0 {
s += r.GetYangjson()
}
}
}

// ShowCmdJSONOutput returns the output of a CLI show commands
// as a JSON structure output.
// as a JSON structured output.
func ShowCmdJSONOutput(ctx context.Context, conn *grpc.ClientConn, cli string, id int64) (string, error) {
var s string
// 'c' is the gRPC stub.
Expand Down
Loading

0 comments on commit db3e459

Please sign in to comment.