diff --git a/README.md b/README.md index e1c3dd8..d61e397 100644 --- a/README.md +++ b/README.md @@ -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) @@ -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 diff --git a/assets/requests/resp.show.vlan.counters.1.json b/assets/requests/resp.show.vlan.counters.1.json new file mode 100644 index 0000000..bff9a05 --- /dev/null +++ b/assets/requests/resp.show.vlan.counters.1.json @@ -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 +} diff --git a/assets/requests/resp.show.vlan.counters.2.json b/assets/requests/resp.show.vlan.counters.2.json new file mode 100644 index 0000000..626c67e --- /dev/null +++ b/assets/requests/resp.show.vlan.counters.2.json @@ -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 + } diff --git a/cmd/client/main.go b/cmd/client/main.go index f0375ab..603ce7c 100644 --- a/cmd/client/main.go +++ b/cmd/client/main.go @@ -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() diff --git a/pkg/client/client.go b/pkg/client/client.go index 245645a..3bbb708 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -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) diff --git a/pkg/client/vlan_counter.go b/pkg/client/vlan_counter.go new file mode 100644 index 0000000..5a3821c --- /dev/null +++ b/pkg/client/vlan_counter.go @@ -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 +} diff --git a/pkg/client/vlan_counter_test.go b/pkg/client/vlan_counter_test.go new file mode 100644 index 0000000..d9d40c4 --- /dev/null +++ b/pkg/client/vlan_counter_test.go @@ -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) + } +}