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  }