diff --git a/connectors/snmp/model.go b/connectors/snmp/model.go index 72e282e0..32791ede 100644 --- a/connectors/snmp/model.go +++ b/connectors/snmp/model.go @@ -43,6 +43,7 @@ type InterfaceMetric struct { Key string Mib string Value int64 + ts int64 } func (state *MonitoringState) Init() { @@ -79,56 +80,77 @@ func (device *DeviceExt) retrieveMonitoredServices(metricDefinitions map[string] } timestamp := transit.NewTimestamp() + ts := timestamp.Unix() for _, iFace := range device.Interfaces { - var bytesInPrev, bytesOutPrev, bytesInX64Prev, bytesOutX64Prev int64 = -1, -1, -1, -1 - if val, ok := previousValueCache.Get(fmt.Sprintf("%s:%s:%s", device.Name, iFace.Name, clients.IfInOctets)); ok { - bytesInPrev = val.(int64) - } - if val, ok := previousValueCache.Get(fmt.Sprintf("%s:%s:%s", device.Name, iFace.Name, clients.IfOutOctets)); ok { - bytesOutPrev = val.(int64) - } - if val, ok := previousValueCache.Get(fmt.Sprintf("%s:%s:%s", device.Name, iFace.Name, clients.IfHCInOctets)); ok { - bytesInX64Prev = val.(int64) - } - if val, ok := previousValueCache.Get(fmt.Sprintf("%s:%s:%s", device.Name, iFace.Name, clients.IfHCOutOctets)); ok { - bytesOutX64Prev = val.(int64) - } - var metricsBuilder []connectors.MetricBuilder - for mib, metric := range iFace.Metrics { - if metricDefinition, has := metricDefinitions[metric.Mib]; has { - unitType := transit.UnitCounter - - metricBuilder := connectors.MetricBuilder{ - Name: metric.Key, - CustomName: metricDefinition.CustomName, - ComputeType: metricDefinition.ComputeType, - Expression: metricDefinition.Expression, - UnitType: unitType, - Warning: metricDefinition.WarningThreshold, - Critical: metricDefinition.CriticalThreshold, - StartTimestamp: timestamp, - EndTimestamp: timestamp, - Graphed: metricDefinition.Graphed, - - Value: nil, - } - ck := fmt.Sprintf("%s:%s:%s", device.Name, iFace.Name, mib) - isDelta, isPreviousPresent, valueToSet := calculateValue(metricDefinition.MetricType, unitType, - ck, metric.Value) + for key := range clients.NonMibMetrics { + if metricDefinition, has := metricDefinitions[key]; has { + metricBuilder := makeMetricBuilder(metricDefinition, key, timestamp) - switch mib { - case clients.IfInOctets, clients.IfOutOctets, clients.IfHCInOctets, clients.IfHCOutOctets: - valueToSet = valueToSet * 8 + switch key { + case clients.BytesPerSecondIn: + if prev, ok := previousValueCache.Get(makeCK(device.Name, iFace.Name, clients.IfInOctets)); ok { + prev := prev.(InterfaceMetric) + v := (iFace.Metrics[clients.IfInOctets].Value - prev.Value) / (ts - prev.ts) + metricBuilder.Value = v + metricsBuilder = append(metricsBuilder, metricBuilder) + metricBuilder.Name = "bitsPerSecondIn" + metricBuilder.Value = v * 8 + metricsBuilder = append(metricsBuilder, metricBuilder) + } + if prev, ok := previousValueCache.Get(makeCK(device.Name, iFace.Name, clients.IfHCInOctets)); ok { + prev := prev.(InterfaceMetric) + v := (iFace.Metrics[clients.IfHCInOctets].Value - prev.Value) / (ts - prev.ts) + metricBuilder.Name = "bytesPerSecondHCIn" + metricBuilder.Value = v + metricsBuilder = append(metricsBuilder, metricBuilder) + metricBuilder.Name = "bitsPerSecondHCIn" + metricBuilder.Value = v * 8 + metricsBuilder = append(metricsBuilder, metricBuilder) + } + + case clients.BytesPerSecondOut: + if prev, ok := previousValueCache.Get(makeCK(device.Name, iFace.Name, clients.IfOutOctets)); ok { + prev := prev.(InterfaceMetric) + v := (iFace.Metrics[clients.IfOutOctets].Value - prev.Value) / (ts - prev.ts) + metricBuilder.Value = v + metricsBuilder = append(metricsBuilder, metricBuilder) + metricBuilder.Name = "bitsPerSecondOut" + metricBuilder.Value = v * 8 + metricsBuilder = append(metricsBuilder, metricBuilder) + } + if prev, ok := previousValueCache.Get(makeCK(device.Name, iFace.Name, clients.IfHCOutOctets)); ok { + prev := prev.(InterfaceMetric) + v := (iFace.Metrics[clients.IfHCOutOctets].Value - prev.Value) / (ts - prev.ts) + metricBuilder.Name = "bytesPerSecondHCOut" + metricBuilder.Value = v + metricsBuilder = append(metricsBuilder, metricBuilder) + metricBuilder.Name = "bitsPerSecondHCOut" + metricBuilder.Value = v * 8 + metricsBuilder = append(metricsBuilder, metricBuilder) + } } + } + } - if !isDelta || (isDelta && isPreviousPresent) { - metricBuilder.Value = valueToSet + for mib, metric := range iFace.Metrics { + if metricDefinition, has := metricDefinitions[metric.Mib]; has { + metricBuilder := makeMetricBuilder(metricDefinition, metric.Key, timestamp) + + ck := makeCK(device.Name, iFace.Name, mib) + if isDelta(metricDefinition.MetricType) { + if prev, ok := previousValueCache.Get(ck); ok { + metricBuilder.Value = metric.Value - prev.(InterfaceMetric).Value + metricsBuilder = append(metricsBuilder, metricBuilder) + } + } else { + metricBuilder.Value = metric.Value metricsBuilder = append(metricsBuilder, metricBuilder) } - previousValueCache.SetDefault(ck, metric.Value) + metric.ts = ts + previousValueCache.SetDefault(ck, metric) // log.Debug(). // Interface("_ck", ck). @@ -140,21 +162,6 @@ func (device *DeviceExt) retrieveMonitoredServices(metricDefinitions map[string] } } - for key := range clients.NonMibMetrics { - if metricDefinition, has := metricDefinitions[key]; has { - switch key { - case clients.BytesPerSecondIn: - metricBuilder := calculateBytesPerSecond(key, metricDefinition, - iFace.Metrics[clients.IfInOctets].Value*8, iFace.Metrics[clients.IfHCInOctets].Value*8, bytesInPrev, bytesInX64Prev, timestamp) - metricsBuilder = append(metricsBuilder, metricBuilder) - case clients.BytesPerSecondOut: - metricBuilder := calculateBytesPerSecond(key, metricDefinition, - iFace.Metrics[clients.IfOutOctets].Value*8, iFace.Metrics[clients.IfHCOutOctets].Value*8, bytesOutPrev, bytesOutX64Prev, timestamp) - metricsBuilder = append(metricsBuilder, metricBuilder) - } - } - } - mService, err := connectors.BuildServiceForMetrics(iFace.Name, device.Name, metricsBuilder) if err != nil { log.Err(err).Msgf("could not create monitored service '%s:%s'", device.Name, iFace.Name) @@ -191,29 +198,15 @@ func calculateHostStatus(lastOk float64) transit.MonitorStatus { return transit.HostUnreachable } -func calculateValue(metricKind transit.MetricKind, unitType transit.UnitType, - ck string, currentValue int64) (bool, bool, int64) { - if strings.EqualFold(string(metricKind), string(transit.Delta)) { - if previousValue, present := previousValueCache.Get(ck); present { - switch unitType { - case transit.UnitCounter: - currentValue = currentValue - previousValue.(int64) - } - return true, true, currentValue - } - return true, false, currentValue - } - return false, false, currentValue +func isDelta(k transit.MetricKind) bool { + return strings.EqualFold(string(k), string(transit.Delta)) } -func calculateBytesPerSecond(metricName string, metricDefinition transit.MetricDefinition, current, currentX64, previous, - previousX64 int64, timestamp *transit.Timestamp) connectors.MetricBuilder { - seconds := int(connectors.CheckInterval.Seconds()) - result := (current - previous) / int64(seconds) - if currentX64 > 0 && previousX64 > 0 { - result = (currentX64 - previousX64) / int64(seconds) - } +func makeCK(deviceName, iFaceName, mib string) string { + return fmt.Sprintf("%s:%s:%s", deviceName, iFaceName, mib) +} +func makeMetricBuilder(metricDefinition transit.MetricDefinition, metricName string, timestamp *transit.Timestamp) connectors.MetricBuilder { return connectors.MetricBuilder{ Name: metricName, CustomName: metricDefinition.CustomName, @@ -225,7 +218,16 @@ func calculateBytesPerSecond(metricName string, metricDefinition transit.MetricD StartTimestamp: timestamp, EndTimestamp: timestamp, Graphed: metricDefinition.Graphed, - - Value: result, } } + +/** +## [Consider SNMP Counters: Frequently Asked Questions](https://www.cisco.com/c/en/us/support/docs/ip/simple-network-management-protocol-snmp/26007-faq-snmpcounter.html) + +Q. When do 64-bit counters be used? + A. [RFC 2233](https://www.ietf.org/rfc/rfc2233.txt) adopted expanded 64-bit counters for high capacity interfaces in which 32-bit counters do not provide enough capacity and wrap too fast. + +> [!Note] +> Cisco IOS Software does not support 64-bit counters for interface speeds of less than 20 Mbps. +> This means that 64-bit counters are not supported on 10 Mb Ethernet ports. Only 100 Mb Fast-Ethernet and other high speed ports support 64-bit counters. +*/ diff --git a/connectors/snmp/snmp_test.go b/connectors/snmp/snmp_test.go index 2869cc50..b57eeecf 100644 --- a/connectors/snmp/snmp_test.go +++ b/connectors/snmp/snmp_test.go @@ -21,7 +21,9 @@ func TestRetrieveMonitoredResources(t *testing.T) { _ = state.retrieveMonitoredResources(metricDefinitions) for k, v := range previousValueCache.Items() { - previousValueCache.SetDefault(k, v.Object.(int64)-10) + prev := v.Object.(InterfaceMetric) + prev.Value = prev.Value - 10 + previousValueCache.SetDefault(k, prev) } // 2nd call for deltas