def as_metric_with_inferred_type(value): # type: (Any) -> Optional[MetricDefinition] if is_counter(value): return {'type': 'rate', 'value': int(value)} if is_gauge(value): return {'type': 'gauge', 'value': int(value)} if is_opaque(value): # Arbitrary ASN.1 syntax encoded as an octet string. Let's try to decode it as a float. # See: http://snmplabs.com/pysnmp/docs/api-reference.html#opaque-type try: decoded, _ = pyasn1_decode(bytes(value)) value = float(decoded) except Exception: pass else: return {'type': 'gauge', 'value': value} # Fallback for unknown SNMP types. try: number = float(value) except ValueError: return None else: return {'type': 'gauge', 'value': number}
def as_metric_with_inferred_type(value): # type: (Any) -> Optional[MetricDefinition] # Ugly hack but couldn't find a cleaner way. Proper way would be to use the ASN.1 # method `.isSameTypeWith()`, or at least `isinstance()`. # But these wrongfully return `True` in some cases, eg: # ```python # >>> from pysnmp.proto.rfc1902 import Counter64 # >>> from datadog_checks.snmp.pysnmp_types import CounterBasedGauge64 # >>> issubclass(CounterBasedGauge64, Counter64) # True # <-- WRONG! (CounterBasedGauge64 values are gauges, not counters.) # ```` pysnmp_class_name = value.__class__.__name__ if pysnmp_class_name in SNMP_COUNTER_CLASSES: return {'type': 'rate', 'value': int(value)} if pysnmp_class_name in SNMP_GAUGE_CLASSES: return {'type': 'gauge', 'value': int(value)} if pysnmp_class_name == 'Opaque': # Arbitrary ASN.1 syntax encoded as an octet string. Let's try to decode it as a float. # See: http://snmplabs.com/pysnmp/docs/api-reference.html#opaque-type try: decoded, _ = pyasn1_decode(bytes(value)) value = float(decoded) except Exception: pass else: return {'type': 'gauge', 'value': value} # Fallback for unknown SNMP types. try: number = float(value) except ValueError: return None else: return {'type': 'gauge', 'value': number}
def submit_metric(self, name, snmp_value, forced_type, tags): # type: (str, Any, Optional[str], List[str]) -> None """ Convert the values reported as pysnmp-Managed Objects to values and report them to the aggregator. """ if reply_invalid(snmp_value): # Metrics not present in the queried object self.log.warning('No such Mib available: %s', name) return metric_name = self.normalize(name, prefix='snmp') value = 0.0 # type: float if forced_type: forced_type = forced_type.lower() if forced_type == 'gauge': value = int(snmp_value) self.gauge(metric_name, value, tags) elif forced_type == 'percent': value = total_time_to_temporal_percent(int(snmp_value), scale=1) self.rate(metric_name, value, tags) elif forced_type == 'counter': value = int(snmp_value) self.rate(metric_name, value, tags) elif forced_type == 'monotonic_count': value = int(snmp_value) self.monotonic_count(metric_name, value, tags) else: self.warning('Invalid forced-type specified: %s in %s', forced_type, name) raise ConfigurationError( 'Invalid forced-type in config file: {}'.format(name)) return # Ugly hack but couldn't find a cleaner way # Proper way would be to use the ASN1 method isSameTypeWith but it # wrongfully returns True in the case of CounterBasedGauge64 # and Counter64 for example snmp_class = snmp_value.__class__.__name__ if snmp_class in SNMP_COUNTERS: value = int(snmp_value) self.rate(metric_name, value, tags) return if snmp_class in SNMP_GAUGES: value = int(snmp_value) self.gauge(metric_name, value, tags) return if snmp_class == 'Opaque': # Try support for floats try: value = float(pyasn1_decode(bytes(snmp_value))[0]) except Exception: pass else: self.gauge(metric_name, value, tags) return # Falls back to try to cast the value. try: value = float(snmp_value) except ValueError: pass else: self.gauge(metric_name, value, tags) return self.log.warning('Unsupported metric type %s for %s', snmp_class, metric_name)