bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/cmd/scollector/collectors/snmp_cisco.go (about) 1 package collectors 2 3 import ( 4 "fmt" 5 "strconv" 6 "strings" 7 "time" 8 9 "bosun.org/cmd/scollector/conf" 10 "bosun.org/metadata" 11 "bosun.org/opentsdb" 12 "bosun.org/slog" 13 ) 14 15 //SNMPCiscoASA registers a SNMP CISCO IOS collector for the given community and host. 16 func SNMPCiscoASA(cfg conf.SNMP) { 17 cpuIntegrator := getTsIntegrator() 18 collectors = append(collectors, 19 &IntervalCollector{ 20 F: func() (opentsdb.MultiDataPoint, error) { 21 // Currently the trees are the same between IOS and NXOS 22 // But registering it this way will make it so future changes 23 // won't require a configuration change 24 return c_cisco_ios(cfg.Host, cfg.Community, cpuIntegrator) 25 }, 26 Interval: time.Second * 30, 27 name: fmt.Sprintf("snmp-cisco-asa-%s", cfg.Host), 28 }, 29 // 30 // Execute ASA-specific checks in c_cisco_asa 31 // 32 &IntervalCollector{ 33 F: func() (opentsdb.MultiDataPoint, error) { 34 return c_cisco_asa(cfg.Host, cfg.Community) 35 }, 36 Interval: time.Second * 30, 37 name: fmt.Sprintf("snmp-cisco-asa-specific-%s", cfg.Host), 38 }, 39 &IntervalCollector{ 40 F: func() (opentsdb.MultiDataPoint, error) { 41 return c_cisco_desc(cfg.Host, cfg.Community) 42 }, 43 Interval: time.Minute * 5, 44 name: fmt.Sprintf("snmp-cisco-desc-%s", cfg.Host), 45 }, 46 ) 47 } 48 49 //SNMPCiscoIOS registers a SNMP CISCO IOS collector for the given community and host. 50 func SNMPCiscoIOS(cfg conf.SNMP) { 51 cpuIntegrator := getTsIntegrator() 52 collectors = append(collectors, 53 &IntervalCollector{ 54 F: func() (opentsdb.MultiDataPoint, error) { 55 return c_cisco_ios(cfg.Host, cfg.Community, cpuIntegrator) 56 }, 57 Interval: time.Second * 30, 58 name: fmt.Sprintf("snmp-cisco-ios-%s", cfg.Host), 59 }, 60 &IntervalCollector{ 61 F: func() (opentsdb.MultiDataPoint, error) { 62 return c_cisco_desc(cfg.Host, cfg.Community) 63 }, 64 Interval: time.Minute * 5, 65 name: fmt.Sprintf("snmp-cisco-desc-%s", cfg.Host), 66 }, 67 ) 68 } 69 70 //SNMPCiscoNXOS registers a SNMP Cisco's NXOS collector (i.e. nexus switches) for the given community and host. 71 func SNMPCiscoNXOS(cfg conf.SNMP) { 72 cpuIntegrator := getTsIntegrator() 73 collectors = append(collectors, 74 &IntervalCollector{ 75 F: func() (opentsdb.MultiDataPoint, error) { 76 return c_cisco_nxos(cfg.Host, cfg.Community, cpuIntegrator) 77 }, 78 Interval: time.Second * 30, 79 name: fmt.Sprintf("snmp-cisco-nxos-%s", cfg.Host), 80 }, 81 &IntervalCollector{ 82 F: func() (opentsdb.MultiDataPoint, error) { 83 return c_cisco_desc(cfg.Host, cfg.Community) 84 }, 85 Interval: time.Minute * 5, 86 name: fmt.Sprintf("snmp-cisco-desc-%s", cfg.Host), 87 }, 88 ) 89 } 90 91 const ( 92 ciscoBaseOID = "1.3.6.1.4.1.9.9" 93 cpmCPUTotal5secRev = ".109.1.1.1.1.6" 94 asaConnInUseCurrent = ".147.1.2.2.2.1.5.40.6" 95 asaConnInUseMax = ".147.1.2.2.2.1.5.40.7" 96 ciscoMemoryPoolTable = ".48.1.1.1" 97 ) 98 99 const ( 100 ciscoMemoryPoolFreeDesc = "The number of bytes from the memory pool that are currently in use by applications on the managed device." 101 ciscoMemoryPoolUsedDesc = "the number of bytes from the memory pool that are currently unused on the managed device." 102 asaConnInUseCurrentDesc = "The number of connections currently registered in the ASA firewall." 103 asaConnInUseMaxDesc = "The maximum number of connections to an ASA firewall since last power cycle." 104 ) 105 106 type ciscoMemoryPoolEntry struct { 107 PoolType string 108 Used int64 109 Free int64 110 } 111 112 func ciscoASAConn(host, community string, ts opentsdb.TagSet, md *opentsdb.MultiDataPoint) error { 113 connCurrent, err := snmp_oid(host, community, ciscoBaseOID+asaConnInUseCurrent) 114 if err != nil { 115 return fmt.Errorf("Error when receiving ASA current connection count.") 116 } 117 118 connMax, err := snmp_oid(host, community, ciscoBaseOID+asaConnInUseMax) 119 if err != nil { 120 return fmt.Errorf("Error when receiving ASA Max connections count.") 121 } 122 123 Add(md, "cisco.asa.conn_current", connCurrent, ts, metadata.Gauge, metadata.Connection, asaConnInUseCurrentDesc) 124 Add(md, "cisco.asa.conn_max", connMax, ts, metadata.Gauge, metadata.Connection, asaConnInUseMaxDesc) 125 return nil 126 127 } 128 129 func ciscoCPU(host, community string, ts opentsdb.TagSet, cpuIntegrator tsIntegrator, md *opentsdb.MultiDataPoint) error { 130 cpuRaw, err := snmp_subtree(host, community, ciscoBaseOID+cpmCPUTotal5secRev) 131 if err != nil { 132 return err 133 } 134 135 cpu := make(map[string]int) 136 for k, v := range cpuRaw { 137 pct, err := strconv.Atoi(fmt.Sprintf("%v", v)) 138 if err != nil { 139 return err 140 } 141 cpu[k] = pct 142 } 143 if len(cpu) > 1 { 144 return fmt.Errorf("expected only one cpu when monitoring cisco cpu via cpmCPUTotal5secRev") 145 } 146 for _, pct := range cpu { 147 Add(md, "cisco.cpu", pct, ts, metadata.Gauge, metadata.Pct, "") 148 Add(md, osCPU, cpuIntegrator(time.Now().Unix(), float64(pct)), ts, metadata.Counter, metadata.Pct, "") 149 } 150 return nil 151 } 152 153 func c_cisco_asa(host, community string) (opentsdb.MultiDataPoint, error) { 154 var md opentsdb.MultiDataPoint 155 ts := opentsdb.TagSet{"host": host} 156 157 // ASA connection counts 158 if err := ciscoASAConn(host, community, ts, &md); err != nil { 159 return md, err 160 } 161 return md, nil 162 } 163 164 func c_cisco_ios(host, community string, cpuIntegrator tsIntegrator) (opentsdb.MultiDataPoint, error) { 165 var md opentsdb.MultiDataPoint 166 ts := opentsdb.TagSet{"host": host} 167 // CPU 168 if err := ciscoCPU(host, community, ts, cpuIntegrator, &md); err != nil { 169 return md, err 170 } 171 // ÎMemory 172 memRaw, err := snmp_subtree(host, community, ciscoBaseOID+ciscoMemoryPoolTable) 173 if err != nil { 174 return md, fmt.Errorf("failed to get ciscoMemoryPoolTable for host %v: %v", host, err) 175 } 176 idToPoolEntry := make(map[string]*ciscoMemoryPoolEntry) 177 for id, value := range memRaw { 178 sp := strings.SplitN(id, ".", 2) 179 if len(sp) != 2 { 180 slog.Errorf("expected length of 2 for snmp sub OID (%v) for ciscoMemoryPoolTable for host %v: got length %v", id, host, len(sp)) 181 } 182 columnID := sp[0] 183 entryID := sp[1] 184 if _, ok := idToPoolEntry[entryID]; !ok { 185 idToPoolEntry[entryID] = &ciscoMemoryPoolEntry{} 186 } 187 switch columnID { 188 case "2": 189 if v, ok := value.([]byte); ok { 190 if m, ok := idToPoolEntry[entryID]; ok { 191 m.PoolType = string(v) 192 } else { 193 slog.Errorf("failed to find cisco memory pool entry for entry id %v on host %v for memory pool type", entryID, host) 194 } 195 } else { 196 slog.Errorf("failed to convert memory pool label %v to []byte for host %v", value, host) 197 } 198 case "5": 199 if v, ok := value.(int64); ok { 200 if m, ok := idToPoolEntry[entryID]; ok { 201 m.Used = v 202 } else { 203 slog.Errorf("failed to find cisco memory pool entry for entry id %v on host %v for used memory", entryID, host) 204 } 205 } else { 206 slog.Errorf("failed to convert used memory value %v to int64 for host %v", value, host) 207 } 208 case "6": 209 if v, ok := value.(int64); ok { 210 if m, ok := idToPoolEntry[entryID]; ok { 211 m.Free = v 212 } else { 213 slog.Errorf("failed to find cisco memory pool entry for entry id %v on host %v for free memory", entryID, host) 214 } 215 } else { 216 slog.Errorf("failed to convert used memory value %v to int64 for host %v", value, host) 217 } 218 } 219 } 220 var totalFreeMem int64 221 var totalUsedMem int64 222 for _, entry := range idToPoolEntry { 223 ts := ts.Copy().Merge(opentsdb.TagSet{"name": entry.PoolType}) 224 Add(&md, "cisco.mem.used", entry.Used, ts, metadata.Gauge, metadata.Bytes, ciscoMemoryPoolUsedDesc) 225 Add(&md, "cisco.mem.free", entry.Free, ts, metadata.Gauge, metadata.Bytes, ciscoMemoryPoolFreeDesc) 226 totalFreeMem += entry.Free 227 totalUsedMem += entry.Used 228 } 229 Add(&md, osMemFree, totalFreeMem, ts, metadata.Gauge, metadata.Bytes, osMemFreeDesc) 230 Add(&md, osMemUsed, totalUsedMem, ts, metadata.Gauge, metadata.Bytes, osMemUsedDesc) 231 totalMem := totalFreeMem + totalUsedMem 232 Add(&md, osMemTotal, totalMem, ts, metadata.Gauge, metadata.Bytes, osMemTotalDesc) 233 Add(&md, osMemPctFree, int64(float64(totalFreeMem)/float64(totalMem)*100), ts, metadata.Gauge, metadata.Pct, osMemPctFreeDesc) 234 return md, nil 235 } 236 237 const ( 238 cpmCPUTotalEntry = ".109.1.1.1.1" 239 ) 240 241 func c_cisco_nxos(host, community string, cpuIntegrator tsIntegrator) (opentsdb.MultiDataPoint, error) { 242 var md opentsdb.MultiDataPoint 243 ts := opentsdb.TagSet{"host": host} 244 // CPU 245 if err := ciscoCPU(host, community, ts, cpuIntegrator, &md); err != nil { 246 return md, err 247 } 248 // Memory 249 memRaw, err := snmp_subtree(host, community, ciscoBaseOID+cpmCPUTotalEntry) 250 if err != nil { 251 return md, fmt.Errorf("failed to get cpmCPUTotalEntry (for memory) for host %v: %v", host, err) 252 } 253 var usedMem, freeMem, totalMem int64 254 var usedOk, freeOk bool 255 for id, value := range memRaw { 256 var v int64 257 switch id { 258 case "12.1": 259 if v, usedOk = value.(int64); usedOk { 260 usedMem = v * 2 << 9 // KiB to Bytes 261 totalMem += usedMem 262 Add(&md, osMemUsed, usedMem, ts, metadata.Gauge, metadata.Bytes, osMemUsedDesc) 263 } else { 264 slog.Errorf("failed to convert used memory %v to int64 for host %v", value, host) 265 } 266 case "13.1": 267 if v, freeOk = value.(int64); freeOk { 268 freeMem = v * 2 << 9 269 totalMem += freeMem 270 Add(&md, osMemFree, freeMem, ts, metadata.Gauge, metadata.Bytes, osMemFreeDesc) 271 } else { 272 slog.Errorf("failed to convert free memory %v to int64 for host %v", value, host) 273 } 274 } 275 } 276 if usedOk && freeOk { 277 Add(&md, osMemTotal, totalMem, ts, metadata.Gauge, metadata.Bytes, osMemTotalDesc) 278 Add(&md, osMemPctFree, int64(float64(freeMem)/float64(totalMem)*100), ts, metadata.Gauge, metadata.Pct, osMemPctFreeDesc) 279 } else { 280 slog.Errorf("failed to get both free and used memory for host %v", host) 281 } 282 return md, nil 283 } 284 285 func c_cisco_desc(host, community string) (opentsdb.MultiDataPoint, error) { 286 var md opentsdb.MultiDataPoint 287 desc, err := getSNMPDesc(host, community) 288 if err != nil { 289 return md, err 290 } 291 if desc == "" { 292 return md, fmt.Errorf("empty description string (used to get OS version) for cisco host %v", host) 293 } 294 metadata.AddMeta("", opentsdb.TagSet{"host": host}, "versionCaption", desc, false) 295 return md, nil 296 }