-
Notifications
You must be signed in to change notification settings - Fork 0
/
timos.go
183 lines (159 loc) · 5.7 KB
/
timos.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
package codecommunicator
import (
"context"
"github.com/RincewindsHat/thola/internal/device"
"github.com/RincewindsHat/thola/internal/deviceclass/groupproperty"
"github.com/RincewindsHat/thola/internal/network"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
"strconv"
"strings"
)
type timosCommunicator struct {
codeCommunicator
}
// GetInterfaces returns the interfaces of Nokia devices.
func (c *timosCommunicator) GetInterfaces(ctx context.Context, filter ...groupproperty.Filter) ([]device.Interface, error) {
interfaces, err := c.parent.GetInterfaces(ctx)
if err != nil {
return nil, err
}
con, ok := network.DeviceConnectionFromContext(ctx)
if !ok || con.SNMP == nil {
return nil, errors.New("no device connection available")
}
// get mapping from every ifIndex to a description
indexDescriptions, err := getPhysPortDescriptions(ctx)
if err != nil {
return nil, errors.Wrap(err, "failed to read phys port descriptions")
}
// apply description mapping to the default interfaces
interfaces = normalizeTimosInterfaces(interfaces, indexDescriptions)
// get all sap interfaces
sapDescriptionsOID := network.OID(".1.3.6.1.4.1.6527.3.1.2.4.3.2.1.5")
sapDescriptions, err := con.SNMP.SnmpClient.SNMPWalk(ctx, sapDescriptionsOID)
if err != nil {
log.Ctx(ctx).Debug().Err(err).Msg("sap interfaces are not available on this device")
return interfaces, nil
}
for _, response := range sapDescriptions {
special, err := response.GetValue()
if err != nil {
return nil, errors.Wrap(err, "couldn't get string value")
}
// construct description
suffix := strings.Split(strings.TrimPrefix(response.GetOID().String(), sapDescriptionsOID.String()), ".")
physIndex := suffix[2]
subID := suffix[3]
description, ok := indexDescriptions[physIndex]
if !ok {
return nil, errors.New("invalid physical index")
}
description += ":" + subID
if !special.IsEmpty() {
description += " " + special.String()
}
// construct index
subIndex, err := strconv.ParseUint(physIndex+subID, 0, 64)
if err != nil {
return nil, errors.Wrap(err, "couldn't get index from strings")
}
// retrieve admin status
admin, err := getStatusFromSnmpGet(ctx, network.OID(".1.3.6.1.4.1.6527.3.1.2.4.3.2.1.6.").AddIndex(suffix[1]+"."+physIndex+"."+subID))
if err != nil {
return nil, errors.Wrap(err, "failed to retrieve admin status")
}
// retrieve oper status
oper, err := getStatusFromSnmpGet(ctx, network.OID(".1.3.6.1.4.1.6527.3.1.2.4.3.2.1.7.").AddIndex(suffix[1]+"."+physIndex+"."+subID))
if err != nil {
return nil, errors.Wrap(err, "failed to retrieve oper status")
}
// build logical interface
interfaces = append(interfaces, device.Interface{
IfIndex: &subIndex,
IfDescr: &description,
IfAdminStatus: &admin,
IfOperStatus: &oper,
})
}
return filterInterfaces(ctx, interfaces, filter)
}
// getPhysPortDescriptions returns a mapping from every ifIndex to a description.
// This description is different and shorter than the ifDescription.
func getPhysPortDescriptions(ctx context.Context) (map[string]string, error) {
con, ok := network.DeviceConnectionFromContext(ctx)
if !ok || con.SNMP == nil {
return nil, errors.New("no device connection available")
}
physPortsOID := network.OID(".1.3.6.1.4.1.6527.3.1.2.2.4.2.1.6.1")
physPorts, err := con.SNMP.SnmpClient.SNMPWalk(ctx, physPortsOID)
if err != nil {
return nil, errors.Wrap(err, "snmpwalk failed")
}
indexDescriptions := make(map[string]string)
for _, response := range physPorts {
description, err := response.GetValue()
if err != nil {
return nil, errors.Wrap(err, "couldn't get string value")
}
index := strings.TrimPrefix(response.GetOID().String(), physPortsOID.AddIndex(".").String())
indexDescriptions[index] = description.String()
}
return indexDescriptions, nil
}
// getCounterFromSnmpGet returns the snmpget value at the given oid as uint64 counter.
func getCounterFromSnmpGet(ctx context.Context, oid network.OID) (uint64, error) {
con, ok := network.DeviceConnectionFromContext(ctx)
if !ok || con.SNMP == nil {
return 0, errors.New("no device connection available")
}
res, err := con.SNMP.SnmpClient.SNMPGet(ctx, oid)
if err != nil || len(res) != 1 {
return 0, errors.Wrap(err, "snmpget failed")
}
resValue, err := res[0].GetValue()
if err != nil {
return 0, errors.Wrap(err, "couldn't parse snmp response")
}
resCounter, err := resValue.UInt64()
if err != nil {
return 0, errors.Wrap(err, "couldn't parse snmp response")
}
return resCounter, nil
}
// getStatusFromSnmpGet returns the snmpget value at the given oid as device.Status.
func getStatusFromSnmpGet(ctx context.Context, oid network.OID) (device.Status, error) {
con, ok := network.DeviceConnectionFromContext(ctx)
if !ok || con.SNMP == nil {
return "", errors.New("no device connection available")
}
res, err := con.SNMP.SnmpClient.SNMPGet(ctx, oid)
if err != nil || len(res) != 1 {
return "", errors.Wrap(err, "snmpget failed")
}
resValue, err := res[0].GetValue()
if err != nil {
return "", errors.Wrap(err, "couldn't parse snmp response")
}
resInt, err := resValue.Int()
if err != nil {
return "", errors.Wrap(err, "couldn't parse snmp response")
}
resStatus, err := device.GetStatus(resInt)
if err != nil {
return "", errors.Wrap(err, "couldn't get status from snmp response")
}
return resStatus, nil
}
// normalizeTimosInterfaces applies the description mapping to the given interfaces.
func normalizeTimosInterfaces(interfaces []device.Interface, descriptions map[string]string) []device.Interface {
for _, interf := range interfaces {
descr, ok := descriptions[strconv.FormatUint(*interf.IfIndex, 10)]
if !ok {
continue
}
*interf.IfDescr = descr
*interf.IfName = descr
}
return interfaces
}