master
go 106 lines 2.13 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package snmputils
4
5 import (
6 "fmt"
7 "strings"
8
9 "github.com/gosnmp/gosnmp"
10 )
11
12 const (
13 OidSnmpEngineTime = "1.3.6.1.6.3.10.2.1.3.0"
14 OidHrSystemUptime = "1.3.6.1.2.1.25.1.1.0"
15 OidSysUpTime = "1.3.6.1.2.1.1.3.0"
16 sysUptimeTimeTicks = 0.01
17 )
18
19 type sysUptimeSource struct {
20 oid string
21 scale float64
22 }
23
24 var sysUptimeSources = []sysUptimeSource{
25 {oid: OidSnmpEngineTime},
26 {oid: OidHrSystemUptime, scale: sysUptimeTimeTicks},
27 {oid: OidSysUpTime, scale: sysUptimeTimeTicks},
28 }
29
30 func GetSysUptime(client gosnmp.Handler) (int64, error) {
31 packet, err := client.Get(sysUptimeOIDs())
32 if err != nil {
33 return 0, err
34 }
35 if packet == nil || len(packet.Variables) == 0 {
36 return 0, nil
37 }
38
39 pdusByOID := make(map[string]gosnmp.SnmpPDU, len(packet.Variables))
40 for _, pdu := range packet.Variables {
41 pdusByOID[strings.TrimPrefix(pdu.Name, ".")] = pdu
42 }
43
44 var lastErr error
45 for _, source := range sysUptimeSources {
46 pdu, ok := pdusByOID[source.oid]
47 if !ok || !isSysUptimePduWithData(pdu) {
48 continue
49 }
50
51 value, err := sysUptimePduValue(pdu)
52 if err != nil {
53 lastErr = fmt.Errorf("OID '%s': %w", source.oid, err)
54 continue
55 }
56 if source.scale != 0 {
57 value = int64(float64(value) * source.scale)
58 }
59 if value > 0 {
60 return value, nil
61 }
62 }
63
64 return 0, lastErr
65 }
66
67 func sysUptimeOIDs() []string {
68 oids := make([]string, 0, len(sysUptimeSources))
69 for _, source := range sysUptimeSources {
70 oids = append(oids, source.oid)
71 }
72 return oids
73 }
74
75 func sysUptimePduValue(pdu gosnmp.SnmpPDU) (int64, error) {
76 if !isSysUptimeNumericPdu(pdu) {
77 return 0, fmt.Errorf("cannot convert %T to numeric uptime", pdu.Value)
78 }
79 return gosnmp.ToBigInt(pdu.Value).Int64(), nil
80 }
81
82 func isSysUptimePduWithData(pdu gosnmp.SnmpPDU) bool {
83 switch pdu.Type {
84 case gosnmp.NoSuchObject,
85 gosnmp.NoSuchInstance,
86 gosnmp.Null,
87 gosnmp.EndOfMibView:
88 return false
89 default:
90 return true
91 }
92 }
93
94 func isSysUptimeNumericPdu(pdu gosnmp.SnmpPDU) bool {
95 switch pdu.Type {
96 case gosnmp.Counter32,
97 gosnmp.Counter64,
98 gosnmp.Integer,
99 gosnmp.Gauge32,
100 gosnmp.Uinteger32,
101 gosnmp.TimeTicks:
102 return true
103 default:
104 return false
105 }
106 }