Skip to content

Commit

Permalink
add support for vlan counters (#22)
Browse files Browse the repository at this point in the history
Co-authored-by: Sherrie Shen <sherrie@sherries-mbp.lan>
  • Loading branch information
sherrieshen and Sherrie Shen authored Sep 8, 2022
1 parent f39300f commit 110fa41
Show file tree
Hide file tree
Showing 7 changed files with 249 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ information, vlans and interfaces, etc.

* `GetSystemInfo()`: **show version**
* `GetVlans()`: **show vlan**
* `GetVlanCounters()`: **show vlan counters**
* `GetInterfaces()` **show interface**
* `GetSystemResources()` **show system resources** (CPU, Memory)
* `GetSystemEnvironment()` **show environment** (Fans, Power Supplies, Sensors)
Expand Down Expand Up @@ -134,6 +135,7 @@ bin/go-cisco-nx-api-client -cli "show version" -host 10.1.1.1 -user admin -port
bin/go-cisco-nx-api-client -cli "show version" -host 10.1.1.1 -user admin -pass cisco
bin/go-cisco-nx-api-client -cli "show interface" -host 10.1.1.1 -user admin -pass cisco
bin/go-cisco-nx-api-client -cli "show vlan" -host 10.1.1.1 -user admin -pass cisco
bin/go-cisco-nx-api-client -cli "show vlan counters" -host 10.1.1.1 -user admin -pass cisco
bin/go-cisco-nx-api-client -cli "show running-config" -host 10.1.1.1 -user admin -pass cisco
bin/go-cisco-nx-api-client -cli "show startup-config" -host 10.1.1.1 -user admin -pass cisco
bin/go-cisco-nx-api-client -cli "show ip bgp summary" -host 10.1.1.1 -user admin -pass cisco
Expand Down
25 changes: 25 additions & 0 deletions assets/requests/resp.show.vlan.counters.1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"jsonrpc": "2.0",
"result": {
"body": {
"TABLE_vlancounters": {
"ROW_vlancounters": [
{
"vlanshowbr-vlanid": 1,
"l2_ing_ucast_b": 2386,
"l2_ing_ucast_p": 26,
"l2_ing_mcast_b": 218920,
"l2_ing_mcast_p": 2952,
"l2_ing_bcast_b": 135172550,
"l2_ing_bcast_p": 392491,
"l2_egr_ucast_b": 2386,
"l2_egr_ucast_p": 26,
"l3_ucast_rcv_b": 0,
"l3_ucast_rcv_p": 0
}
]
}
}
},
"id": 1
}
38 changes: 38 additions & 0 deletions assets/requests/resp.show.vlan.counters.2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"jsonrpc": "2.0",
"result": {
"body": {
"TABLE_vlancounters": {
"ROW_vlancounters": [
{
"vlanshowbr-vlanid": 1,
"l2_ing_ucast_b": 2386,
"l2_ing_ucast_p": 26,
"l2_ing_mcast_b": 218920,
"l2_ing_mcast_p": 2952,
"l2_ing_bcast_b": 135172550,
"l2_ing_bcast_p": 392491,
"l2_egr_ucast_b": 2386,
"l2_egr_ucast_p": 26,
"l3_ucast_rcv_b": 0,
"l3_ucast_rcv_p": 0
},
{
"vlanshowbr-vlanid": 108,
"l2_ing_ucast_b": 19662301,
"l2_ing_ucast_p": 58354,
"l2_ing_mcast_b": 460220144,
"l2_ing_mcast_p": 3687034,
"l2_ing_bcast_b": 52223390,
"l2_ing_bcast_p": 627387,
"l2_egr_ucast_b": 56424051,
"l2_egr_ucast_p": 214253,
"l3_ucast_rcv_b": 0,
"l3_ucast_rcv_p": 0
}
]
}
}
},
"id": 1
}
10 changes: 10 additions & 0 deletions cmd/client/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,16 @@ func main() {
fmt.Fprintf(os.Stdout, "Vlan ID %s, Name: %s\n", vlan.ID, vlan.Name)
}
log.Debugf("took %s", time.Since(start))
case "show vlan counters":
start := time.Now()
vlanCounters, err := cli.GetVlanCounters()
if err != nil {
log.Fatalf("%s", err)
}
for _, vlanCounter := range vlanCounters {
fmt.Fprintf(os.Stdout, "Vlan ID %d, InUcastBytes: %d, OutUcastBytes: %d\n", vlanCounter.ID, vlanCounter.InputUnicastBytes, vlanCounter.OutputUnicastBytes)
}
log.Debugf("took %s", time.Since(start))
case "show interface":
start := time.Now()
ifaces, err := cli.GetInterfaces()
Expand Down
15 changes: 15 additions & 0 deletions pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,21 @@ func (cli *Client) GetVlans() ([]*Vlan, error) {
return NewVlansFromBytes(resp)
}

// GetVlanCounters returns vlan counter information ("show vlan counters").
func (cli *Client) GetVlanCounters() ([]*VlanCounters, error) {
url := fmt.Sprintf("%s://%s:%d/ins", cli.protocol, cli.host, cli.port)
req := NewJSONRPCRequest([]string{"show vlan counters"})
payload, err := json.Marshal(req)
if err != nil {
return nil, err
}
resp, err := cli.callAPI("jsonrpc", url, payload)
if err != nil {
return nil, err
}
return NewVlanCountersFromBytes(resp)
}

// GetInterfaces returns interface information ("show interface").
func (cli *Client) GetInterfaces() ([]*Interface, error) {
url := fmt.Sprintf("%s://%s:%d/ins", cli.protocol, cli.host, cli.port)
Expand Down
86 changes: 86 additions & 0 deletions pkg/client/vlan_counter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package client

import (
"encoding/json"
"fmt"
)

type vlanCountersResponse struct {
ID uint64 `json:"id" xml:"id"`
Version string `json:"jsonrpc" xml:"jsonrpc"`
Result vlanCountersResponseResult `json:"result" xml:"result"`
}

type vlanCountersResponseResult struct {
Body vlanCountersResponseResultBody `json:"body" xml:"body"`
}

type vlanCountersResponseResultBody struct {
VlanCountersTable vlanCountersResponseResultBodyVlanCountersTable `json:"TABLE_vlancounters" xml:"TABLE_vlancounters"`
}

type vlanCountersResponseResultBodyVlanCountersTable struct {
VlanCountersRow []vlanCountersResponseResultBodyVlanCountersRow `json:"ROW_vlancounters" xml:"ROW_vlancounters"`
}

type vlanCountersResponseResultBodyVlanCountersRow struct {
ID uint64 `json:"vlanshowbr-vlanid" xml:"vlanshowbr-vlanid"`
InputUnicastBytes uint64 `json:"l2_ing_ucast_b" xml:"l2_ing_ucast_b"`
InputUnicastPackets uint64 `json:"l2_ing_ucast_p" xml:"l2_ing_ucast_p"`
InputMulticastBytes uint64 `json:"l2_ing_mcast_b" xml:"l2_ing_mcast_b"`
InputMulticastPackets uint64 `json:"l2_ing_mcast_p" xml:"l2_ing_mcast_p"`
InputBroadcastBytes uint64 `json:"l2_ing_bcast_b" xml:"l2_ing_bcast_b"`
InputBroadcastPackets uint64 `json:"l2_ing_bcast_p" xml:"l2_ing_bcast_p"`
OutputUnicastBytes uint64 `json:"l2_egr_ucast_b" xml:"l2_egr_ucast_b"`
OutputUnicastPackets uint64 `json:"l2_egr_ucast_p" xml:"l2_egr_ucast_p"`
L3InputUnicastBytes uint64 `json:"l3_ucast_rcv_b" xml:"l3_ucast_rcv_b"`
L3InputUnicastPackets uint64 `json:"l3_ucast_rcv_p" xml:"l3_ucast_rcv_p"`
}

type VlanCounters struct {
ID uint64 `json:"id" xml:"id"`
InputUnicastBytes uint64 `json:"input_ucast_bytes" xml:"input_ucast_bytes"`
InputUnicastBytesL3 uint64 `json:"input_ucast_bytes_l3" xml:"input_ucast_bytes_l3"`
InputMulticastBytes uint64 `json:"input_mcast_bytes" xml:"input_mcast_bytes"`
InputBroadcastBytes uint64 `json:"input_bcast_bytes" xml:"input_bcast_bytes"`
InputUnicastPackets uint64 `json:"input_ucast_packets" xml:"input_ucast_packets"`
InputUnicastPacketsL3 uint64 `json:"input_ucast_packets_l3" xml:"input_ucast_packets_l3"`
InputMulticastPackets uint64 `json:"input_mcast_packets" xml:"input_mcast_packets"`
InputBroadcastPackets uint64 `json:"input_bcast_packets" xml:"input_bcast_packets"`
OutputUnicastBytes uint64 `json:"output_ucast_bytes" xml:"output_ucast_bytes"`
OutputUnicastPackets uint64 `json:"output_ucast_packets" xml:"output_ucast_packets"`
}

// NewVlanCountersFromString returns VlanCounter instance from an input string.
func NewVlanCountersFromString(s string) ([]*VlanCounters, error) {
return NewVlanCountersFromBytes([]byte(s))
}

// NewVlanCountersFromBytes returns VlanCounter instance from an input byte array.
func NewVlanCountersFromBytes(s []byte) ([]*VlanCounters, error) {
var vlanCounters []*VlanCounters
vCountersResponse := &vlanCountersResponse{}
err := json.Unmarshal(s, vCountersResponse)
if err != nil {
return nil, fmt.Errorf("parsing error: %s, server response: %s", err, string(s[:]))
}
if len(vCountersResponse.Result.Body.VlanCountersTable.VlanCountersRow) < 1 {
return nil, fmt.Errorf("Error parsing the received response: %s", s)
}
for _, v := range vCountersResponse.Result.Body.VlanCountersTable.VlanCountersRow {
vlanCounter := &VlanCounters{}
vlanCounter.ID = v.ID
vlanCounter.InputUnicastBytes = v.InputUnicastBytes
vlanCounter.InputUnicastBytesL3 = v.L3InputUnicastBytes
vlanCounter.InputMulticastBytes = v.InputMulticastBytes
vlanCounter.InputBroadcastBytes = v.InputBroadcastBytes
vlanCounter.InputUnicastPackets = v.InputUnicastPackets
vlanCounter.InputUnicastPacketsL3 = v.L3InputUnicastPackets
vlanCounter.InputMulticastPackets = v.InputMulticastPackets
vlanCounter.InputBroadcastPackets = v.InputBroadcastPackets
vlanCounter.OutputUnicastBytes = v.OutputUnicastBytes
vlanCounter.OutputUnicastPackets = v.OutputUnicastPackets
vlanCounters = append(vlanCounters, vlanCounter)
}
return vlanCounters, nil
}
73 changes: 73 additions & 0 deletions pkg/client/vlan_counter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package client

import (
"fmt"
"io/ioutil"
"testing"
)

func TestParseShowVlanCountersJsonOutput(t *testing.T) {
testFailed := 0
outputDir := "../../assets/requests"
for i, test := range []struct {
input string
exp *VlanCounters
count int
shouldFail bool
shouldErr bool
}{
{
input: "show.vlan.counters.1",
exp: &VlanCounters{},
count: 1,
shouldFail: false,
shouldErr: false,
},
{
input: "show.vlan.counters.2",
exp: &VlanCounters{},
count: 2,
shouldFail: false,
shouldErr: false,
},
} {
fp := fmt.Sprintf("%s/resp.%s.json", outputDir, test.input)
content, err := ioutil.ReadFile(fp)
if err != nil {
t.Logf("FAIL: Test %d: failed reading '%s', error: %v", i, fp, err)
testFailed++
continue
}
vlanCounters, err := NewVlanCountersFromBytes(content)
if err != nil {
if !test.shouldErr {
t.Logf("FAIL: Test %d: input '%s', expected to pass, but threw error: %v", i, test.input, err)
testFailed++
continue
}
} else {
if test.shouldErr {
t.Logf("FAIL: Test %d: input '%s', expected to throw error, but passed: %v", i, test.input, vlanCounters)
testFailed++
continue
}
}

if vlanCounters != nil {
if (len(vlanCounters) != test.count) && !test.shouldFail {
t.Logf("FAIL: Test %d: input '%s', expected to pass, but failed due to len(vlanCounters) [%d] != %d", i, test.input, len(vlanCounters), test.count)
testFailed++
continue
}
}

if test.shouldFail {
t.Logf("PASS: Test %d: input '%s', expected to fail, failed", i, test.input)
} else {
t.Logf("PASS: Test %d: input '%s', expected to pass, passed", i, test.input)
}
}
if testFailed > 0 {
t.Fatalf("Failed %d tests", testFailed)
}
}

0 comments on commit 110fa41

Please sign in to comment.