Skip to content

Commit 2fd21d6

Browse files
sherrieshenSherrie Shen
andauthored
add support for error counters (#24)
Co-authored-by: Sherrie Shen <sherrie@Sherries-MacBook-Pro.local>
1 parent 10ff16e commit 2fd21d6

File tree

7 files changed

+313
-0
lines changed

7 files changed

+313
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ information, vlans and interfaces, etc.
2222
* `GetVlans()`: **show vlan**
2323
* `GetVlanCounters()`: **show vlan counters**
2424
* `GetInterfaces()` **show interface**
25+
* `GetErrorCounters()` **show error counters**
2526
* `GetSystemResources()` **show system resources** (CPU, Memory)
2627
* `GetSystemEnvironment()` **show environment** (Fans, Power Supplies, Sensors)
2728
* `GetRunningConfiguration()` **show running-config** (running configuration)
@@ -134,6 +135,7 @@ bin/go-cisco-nx-api-client -cli "show version" -host 10.1.1.1 -user admin -port
134135
bin/go-cisco-nx-api-client -cli "show version" -host 10.1.1.1 -user admin -port 80 -proto http -log.level info -pass cisco
135136
bin/go-cisco-nx-api-client -cli "show version" -host 10.1.1.1 -user admin -pass cisco
136137
bin/go-cisco-nx-api-client -cli "show interface" -host 10.1.1.1 -user admin -pass cisco
138+
bin/go-cisco-nx-api-client -cli "show error counters" -host 10.1.1.1 -user admin -pass cisco
137139
bin/go-cisco-nx-api-client -cli "show vlan" -host 10.1.1.1 -user admin -pass cisco
138140
bin/go-cisco-nx-api-client -cli "show vlan counters" -host 10.1.1.1 -user admin -pass cisco
139141
bin/go-cisco-nx-api-client -cli "show running-config" -host 10.1.1.1 -user admin -pass cisco
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"jsonrpc": "2.0",
3+
"result": {
4+
"body": {
5+
"TABLE_interface": {
6+
"ROW_interface": [
7+
{
8+
"interface": "mgmt0",
9+
"eth_align_err": 0,
10+
"eth_fcs_err": 0
11+
},
12+
{
13+
"interface": "mgmt0",
14+
"eth_single_col": 0,
15+
"eth_multi_col": 0,
16+
"eth_late_col": 0,
17+
"eth_excess_col": 0
18+
},
19+
{
20+
"interface": "mgmt0",
21+
"eth_giants": 0,
22+
"eth_sqetest_err": 0,
23+
"eth_deferred_tx": 0,
24+
"eth_inmactx_err": 0,
25+
"eth_inmacrx_err": 0,
26+
"eth_symbol_err": 0
27+
},
28+
{
29+
"interface": "mgmt0"
30+
}
31+
]
32+
}
33+
}
34+
},
35+
"id": 1
36+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{
2+
"jsonrpc": "2.0",
3+
"result": {
4+
"body": {
5+
"TABLE_interface": {
6+
"ROW_interface": [{
7+
"interface": "mgmt0",
8+
"eth_align_err": 0,
9+
"eth_fcs_err": 0
10+
}, {
11+
"interface": "Ethernet1/1",
12+
"eth_align_err": 0,
13+
"eth_fcs_err": 0,
14+
"eth_xmit_err": 29983041,
15+
"eth_rcv_err": 0,
16+
"eth_undersize": 0,
17+
"eth_outdisc": 35393307
18+
}, {
19+
"interface": "mgmt0",
20+
"eth_single_col": 0,
21+
"eth_multi_col": 0,
22+
"eth_late_col": 0,
23+
"eth_excess_col": 0
24+
}, {
25+
"interface": "Ethernet1/1",
26+
"eth_single_col": 0,
27+
"eth_multi_col": 0,
28+
"eth_late_col": 0,
29+
"eth_excess_col": 0,
30+
"eth_carri_sen": 0,
31+
"eth_runts": 0
32+
}, {
33+
"interface": "mgmt0",
34+
"eth_giants": 0,
35+
"eth_sqetest_err": 0,
36+
"eth_deferred_tx": 0,
37+
"eth_inmactx_err": 0,
38+
"eth_inmacrx_err": 0,
39+
"eth_symbol_err": 0
40+
}, {
41+
"interface": "Ethernet1/1",
42+
"eth_giants": 0,
43+
"eth_deferred_tx": 0,
44+
"eth_inmactx_err": 29983041,
45+
"eth_inmacrx_err": 0,
46+
"eth_symbol_err": 0
47+
}, {
48+
"interface": "mgmt0"
49+
}, {
50+
"interface": "Ethernet1/1",
51+
"eth_indisc": 0
52+
}]
53+
}
54+
}
55+
},
56+
"id": 1
57+
}

cmd/client/main.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,16 @@ func main() {
158158
fmt.Fprintf(os.Stdout, "%s\n", out.String())
159159
}
160160
log.Debugf("took %s", time.Since(start))
161+
case "show error counters":
162+
start := time.Now()
163+
errorCounters, err := cli.GetErrorCounters()
164+
if err != nil {
165+
log.Fatalf("%s", err)
166+
}
167+
for _, errorCounter := range errorCounters {
168+
fmt.Fprintf(os.Stdout, "Interface: %s, In Discards: %d, Out Discards: %d\n", errorCounter.Interface, errorCounter.EthInDiscards, errorCounter.EthOutDiscards)
169+
}
170+
log.Debugf("took %s", time.Since(start))
161171
case "show running-config":
162172
config, err := cli.GetRunningConfiguration()
163173
if err != nil {

pkg/client/client.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,21 @@ func (cli *Client) GetInterface(name string) (*Interface, error) {
382382
return intf, err
383383
}
384384

385+
// GetErrorCounters returns ErrorCounters instance ("show interface counters error")
386+
func (cli *Client) GetErrorCounters() ([]*ErrorCounters, error) {
387+
url := fmt.Sprintf("%s://%s:%d/ins", cli.protocol, cli.host, cli.port)
388+
req := NewJSONRPCRequest([]string{"show interface counters error"})
389+
payload, err := json.Marshal(req)
390+
if err != nil {
391+
return nil, err
392+
}
393+
resp, err := cli.callAPI("jsonrpc", url, payload)
394+
if err != nil {
395+
return nil, err
396+
}
397+
return NewErrorCountersFromBytes(resp)
398+
}
399+
385400
// GetSystemResources returns SystemResources instance ("show system resources").
386401
func (cli *Client) GetSystemResources() (*SystemResources, error) {
387402
url := fmt.Sprintf("%s://%s:%d/ins", cli.protocol, cli.host, cli.port)

pkg/client/error_counter.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package client
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
)
7+
8+
type errorCountersResponse struct {
9+
ID uint64 `json:"id" xml:"id"`
10+
Version string `json:"jsonrpc" xml:"jsonrpc"`
11+
Result errorCountersResponseResult `json:"result" xml:"result"`
12+
}
13+
14+
type errorCountersResponseResult struct {
15+
Body errorCountersResponseResultBody `json:"body" xml:"body"`
16+
}
17+
18+
type errorCountersResponseResultBody struct {
19+
ErrorCountersTable errorCountersResponseResultBodyErrorCountersTable `json:"TABLE_interface" xml:"TABLE_interface"`
20+
}
21+
22+
type errorCountersResponseResultBodyErrorCountersTable struct {
23+
ErrorCountersRow []errorCountersResponseResultBodyErrorCountersRow `json:"ROW_interface" xml:"ROW_interface"`
24+
}
25+
26+
type errorCountersResponseResultBodyErrorCountersRow struct {
27+
Interface string `json:"interface" xml:"interface"`
28+
EthAlignErr uint64 `json:"eth_align_err" xml:"eth_align_err"`
29+
EthFCSErr uint64 `json:"eth_fcs_err" xml:"eth_fcs_err"`
30+
EthOutDiscards uint64 `json:"eth_outdisc" xml:"eth_outdisc"`
31+
EthRcvErr uint64 `json:"eth_rcv_err" xml:"eth_rcv_err"`
32+
EthUndersize uint64 `json:"eth_undersize" xml:"eth_undersize"`
33+
EthXmitErr uint64 `json:"eth_xmit_err" xml:"eth_xmit_err"`
34+
EthCarriSen uint64 `json:"eth_carri_sen" xml:"eth_carri_sen"`
35+
EthExcessCol uint64 `json:"eth_excess_col" xml:"eth_excess_col"`
36+
EthLateCol uint64 `json:"eth_late_col" xml:"eth_late_col"`
37+
EthMultiCol uint64 `json:"eth_multi_col" xml:"eth_multi_col"`
38+
EthRunts uint64 `json:"eth_runts" xml:"eth_runts"`
39+
EthSingleCol uint64 `json:"eth_single_col" xml:"eth_single_col"`
40+
EthDeferredTx uint64 `json:"eth_deferred_tx" xml:"eth_deferred_tx"`
41+
EthGiants uint64 `json:"eth_giants" xml:"eth_giants"`
42+
EthInMacRxErr uint64 `json:"eth_inmacrx_err" xml:"eth_inmacrx_err"`
43+
EthInMacTxErr uint64 `json:"eth_inmactx_err" xml:"eth_inmactx_err"`
44+
EthSymbolErr uint64 `json:"eth_symbol_err" xml:"eth_symbol_err"`
45+
EthInDiscards uint64 `json:"eth_indisc" xml:"eth_indisc"`
46+
}
47+
48+
type ErrorCounters struct {
49+
Interface string `json:"interface" xml:"interface"`
50+
EthAlignErr uint64 `json:"eth_align_err" xml:"eth_align_err"`
51+
EthCarriSen uint64 `json:"eth_carri_sen" xml:"eth_carri_sen"`
52+
EthDeferredTx uint64 `json:"eth_deferred_tx" xml:"eth_deferred_tx"`
53+
EthExcessCol uint64 `json:"eth_excess_col" xml:"eth_excess_col"`
54+
EthFCSErr uint64 `json:"eth_fcs_err" xml:"eth_fcs_err"`
55+
EthGiants uint64 `json:"eth_giants" xml:"eth_giants"`
56+
EthInDiscards uint64 `json:"eth_indisc" xml:"eth_indisc"`
57+
EthInMacRxErr uint64 `json:"eth_inmacrx_err" xml:"eth_inmacrx_err"`
58+
EthInMacTxErr uint64 `json:"eth_inmactx_err" xml:"eth_inmactx_err"`
59+
EthLateCol uint64 `json:"eth_late_col" xml:"eth_late_col"`
60+
EthMultiCol uint64 `json:"eth_multi_col" xml:"eth_multi_col"`
61+
EthOutDiscards uint64 `json:"eth_outdisc" xml:"eth_outdisc"`
62+
EthRcvErr uint64 `json:"eth_rcv_err" xml:"eth_rcv_err"`
63+
EthRunts uint64 `json:"eth_runts" xml:"eth_runts"`
64+
EthSingleCol uint64 `json:"eth_single_col" xml:"eth_single_col"`
65+
EthSymbolErr uint64 `json:"eth_symbol_err" xml:"eth_symbol_err"`
66+
EthUndersize uint64 `json:"eth_undersize" xml:"eth_undersize"`
67+
EthXmitErr uint64 `json:"eth_xmit_err" xml:"eth_xmit_err"`
68+
}
69+
70+
// NewErrorCountersFromString returns ErrorCounter instance from an input string.
71+
func NewErrorCountersFromString(s string) ([]*ErrorCounters, error) {
72+
return NewErrorCountersFromBytes([]byte(s))
73+
}
74+
75+
// NewErrorCountersFromBytes returns ErrorCounter instance from an input byte array.
76+
func NewErrorCountersFromBytes(s []byte) ([]*ErrorCounters, error) {
77+
var errorCounters []*ErrorCounters
78+
eCountersResponse := &errorCountersResponse{}
79+
err := json.Unmarshal(s, eCountersResponse)
80+
if err != nil {
81+
return nil, fmt.Errorf("parsing error: %s, server response: %s", err, string(s[:]))
82+
}
83+
if len(eCountersResponse.Result.Body.ErrorCountersTable.ErrorCountersRow) < 1 {
84+
return nil, fmt.Errorf("Error parsing the received response: %s", s)
85+
}
86+
i, size := 0, len(eCountersResponse.Result.Body.ErrorCountersTable.ErrorCountersRow)
87+
for _, e := range eCountersResponse.Result.Body.ErrorCountersTable.ErrorCountersRow {
88+
if i < size/4 {
89+
errorCounter := &ErrorCounters{}
90+
errorCounter.Interface = e.Interface
91+
errorCounter.EthAlignErr = e.EthAlignErr
92+
errorCounter.EthFCSErr = e.EthFCSErr
93+
errorCounter.EthOutDiscards = e.EthOutDiscards
94+
errorCounter.EthRcvErr = e.EthRcvErr
95+
errorCounter.EthUndersize = e.EthUndersize
96+
errorCounter.EthXmitErr = e.EthXmitErr
97+
errorCounters = append(errorCounters, errorCounter)
98+
} else if i < size/2 {
99+
errorCounter := errorCounters[i%(size/4)]
100+
errorCounter.EthCarriSen = e.EthCarriSen
101+
errorCounter.EthExcessCol = e.EthExcessCol
102+
errorCounter.EthLateCol = e.EthLateCol
103+
errorCounter.EthMultiCol = e.EthMultiCol
104+
errorCounter.EthRunts = e.EthRunts
105+
errorCounter.EthSingleCol = e.EthSingleCol
106+
} else if i < 3*size/4 {
107+
errorCounter := errorCounters[i%(size/4)]
108+
errorCounter.EthDeferredTx = e.EthDeferredTx
109+
errorCounter.EthGiants = e.EthGiants
110+
errorCounter.EthInMacRxErr = e.EthInMacRxErr
111+
errorCounter.EthInMacTxErr = e.EthInMacTxErr
112+
errorCounter.EthSymbolErr = e.EthSymbolErr
113+
} else {
114+
errorCounter := errorCounters[i%(size/4)]
115+
errorCounter.EthInDiscards = e.EthInDiscards
116+
}
117+
i += 1
118+
}
119+
return errorCounters, nil
120+
}

pkg/client/error_counter_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package client
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"testing"
7+
)
8+
9+
func TestParseShowErrorCountersJsonOutput(t *testing.T) {
10+
testFailed := 0
11+
outputDir := "../../assets/requests"
12+
for i, test := range []struct {
13+
input string
14+
exp *ErrorCounters
15+
count int
16+
shouldFail bool
17+
shouldErr bool
18+
}{
19+
{
20+
input: "show.error.counters.1",
21+
exp: &ErrorCounters{},
22+
count: 1,
23+
shouldFail: false,
24+
shouldErr: false,
25+
},
26+
{
27+
input: "show.error.counters.2",
28+
exp: &ErrorCounters{},
29+
count: 2,
30+
shouldFail: false,
31+
shouldErr: false,
32+
},
33+
} {
34+
fp := fmt.Sprintf("%s/resp.%s.json", outputDir, test.input)
35+
content, err := ioutil.ReadFile(fp)
36+
if err != nil {
37+
t.Logf("FAIL: Test %d: failed reading '%s', error: %v", i, fp, err)
38+
testFailed++
39+
continue
40+
}
41+
errorCounters, err := NewErrorCountersFromBytes(content)
42+
if err != nil {
43+
if !test.shouldErr {
44+
t.Logf("FAIL: Test %d: input '%s', expected to pass, but threw error: %v", i, test.input, err)
45+
testFailed++
46+
continue
47+
}
48+
} else {
49+
if test.shouldErr {
50+
t.Logf("FAIL: Test %d: input '%s', expected to throw error, but passed: %v", i, test.input, errorCounters)
51+
testFailed++
52+
continue
53+
}
54+
}
55+
56+
if errorCounters != nil {
57+
if (len(errorCounters) != test.count) && !test.shouldFail {
58+
t.Logf("FAIL: Test %d: input '%s', expected to pass, but failed due to len(errorCounters) [%d] != %d", i, test.input, len(errorCounters), test.count)
59+
testFailed++
60+
continue
61+
}
62+
}
63+
64+
if test.shouldFail {
65+
t.Logf("PASS: Test %d: input '%s', expected to fail, failed", i, test.input)
66+
} else {
67+
t.Logf("PASS: Test %d: input '%s', expected to pass, passed", i, test.input)
68+
}
69+
}
70+
if testFailed > 0 {
71+
t.Fatalf("Failed %d tests", testFailed)
72+
}
73+
}

0 commit comments

Comments
 (0)