def test_discovery_tags(): """When specifying a tag on discovery, it doesn't make tags leaks between instances.""" instance = common.generate_instance_config(common.SUPPORTED_METRIC_TYPES) instance.pop('ip_address') instance['network_address'] = '192.168.0.0/29' instance['tags'] = ['test:check'] check = SnmpCheck('snmp', {}, [instance]) oids = ['1.3.6.1.4.5', '1.3.6.1.4.5'] def mock_fetch(cfg): if oids: return oids.pop(0) check._running = False raise RuntimeError("Not snmp") check.fetch_sysobject_oid = mock_fetch check.discover_instances(interval=0) config = check._config.discovered_instances['192.168.0.2'] assert set(config.tags) == { 'snmp_device:192.168.0.2', 'test:check', 'snmp_profile:generic-router' }
def test_discovery_devices_monitored_count(read_mock, aggregator): read_mock.return_value = '["192.168.0.1","192.168.0.2"]' host = socket.gethostbyname(common.HOST) network = ipaddress.ip_network(u'{}/29'.format(host), strict=False).with_prefixlen check_tags = [ 'autodiscovery_subnet:{}'.format(to_native_string(network)), ] network_tags = ['network:{}'.format(network)] instance = { 'name': 'snmp_conf', # Make sure the check handles bytes 'network_address': to_native_string(network), 'port': common.PORT, 'community_string': 'public', 'retries': 0, 'discovery_interval': 0, } init_config = { 'profiles': { 'profile1': {'definition': {'metrics': common.SUPPORTED_METRIC_TYPES, 'sysobjectid': '1.3.6.1.4.1.8072.*'}} } } check = SnmpCheck('snmp', init_config, [instance]) check.check(instance) check._running = False aggregator.assert_metric('snmp.discovered_devices_count', tags=network_tags) for device_ip in ['192.168.0.1', '192.168.0.2']: tags = check_tags + ['snmp_device:{}'.format(device_ip)] aggregator.assert_metric('snmp.devices_monitored', metric_type=aggregator.GAUGE, value=1, count=1, tags=tags) common.assert_common_check_run_metrics(aggregator, network_tags) aggregator.assert_all_metrics_covered()
def test_cache_building(write_mock, read_mock): instance = common.generate_instance_config(common.SUPPORTED_METRIC_TYPES) instance.pop('ip_address') read_mock.return_value = '[]' discovered_instance = instance.copy() discovered_instance['ip_address'] = '192.168.0.1' instance['network_address'] = '192.168.0.0/31' check = SnmpCheck('snmp', {}, [instance]) check._config.discovered_instances['192.168.0.1'] = InstanceConfig( discovered_instance) check._start_discovery() try: for _ in range(30): if write_mock.call_count: break time.sleep(0.5) finally: check._running = False write_mock.assert_called_once_with('', '["192.168.0.1"]')
def test_metric_tag_profile_sysoid(aggregator): instance = common.generate_instance_config([]) definition = { 'metric_tags': [{'OID': '1.3.6.1.2.1.1.5.0', 'symbol': 'sysName', 'tag': 'snmp_host'}], 'metrics': common.SUPPORTED_METRIC_TYPES, 'sysobjectid': '1.3.6.1.4.1.8072.3.2.10', } init_config = {'profiles': {'profile1': {'definition': definition}}} check = SnmpCheck('snmp', init_config, [instance]) check.check(instance) tags = list(common.CHECK_TAGS) tags.append('snmp_host:41ba948911b9') tags.append('snmp_profile:profile1') for metric in common.SUPPORTED_METRIC_TYPES: metric_name = "snmp." + metric['name'] aggregator.assert_metric(metric_name, tags=tags, count=1) aggregator.assert_metric('snmp.sysUpTimeInstance', count=1) aggregator.assert_service_check("snmp.can_check", status=SnmpCheck.OK, tags=tags, at_least=1) common.assert_common_metrics(aggregator) aggregator.all_metrics_asserted()
def test_profile_sys_object_prefix(aggregator, most_specific_oid, least_specific_oid): instance = common.generate_instance_config([]) most_specific_profile = {'metrics': common.SUPPORTED_METRIC_TYPES, 'sysobjectid': most_specific_oid} least_specific_profile = {'metrics': common.CAST_METRICS, 'sysobjectid': least_specific_oid} init_config = { 'profiles': {'most': {'definition': most_specific_profile}, 'least': {'definition': least_specific_profile}} } check = SnmpCheck('snmp', init_config, [instance]) check.check(instance) matching_profile_tags = common.CHECK_TAGS + ['snmp_profile:most'] ignored_profile_tags = common.CHECK_TAGS + ['snmp_profile:least'] for metric in most_specific_profile['metrics']: metric_name = "snmp." + metric['name'] aggregator.assert_metric(metric_name, tags=matching_profile_tags, count=1) for metric in least_specific_profile['metrics']: metric_name = "snmp." + metric['name'] aggregator.assert_metric(metric_name, tags=ignored_profile_tags, count=0) aggregator.assert_metric('snmp.sysUpTimeInstance', tags=matching_profile_tags, count=1) common.assert_common_metrics(aggregator) aggregator.assert_all_metrics_covered()
def test_profile_by_file(aggregator): instance = common.generate_instance_config([]) instance['profile'] = 'profile1' with temp_dir() as tmp: profile_file = os.path.join(tmp, 'profile1.yaml') with open(profile_file, 'w') as f: f.write(yaml.safe_dump({'metrics': common.SUPPORTED_METRIC_TYPES})) init_config = { 'profiles': { 'profile1': { 'definition_file': profile_file } } } check = SnmpCheck('snmp', init_config, [instance]) check.check(instance) common_tags = common.CHECK_TAGS + ['snmp_profile:profile1'] for metric in common.SUPPORTED_METRIC_TYPES: metric_name = "snmp." + metric['name'] aggregator.assert_metric(metric_name, tags=common_tags, count=1) aggregator.assert_metric('snmp.sysUpTimeInstance', count=1) common.assert_common_metrics(aggregator) aggregator.assert_all_metrics_covered()
def test_profile_sys_object_prefix(aggregator): instance = common.generate_instance_config([]) init_config = { 'profiles': { 'profile1': { 'definition': { 'metrics': common.SUPPORTED_METRIC_TYPES, 'sysobjectid': '1.3.6.1.4.1.8072.3.2.10' } }, 'profile2': { 'definition': { 'metrics': common.CAST_METRICS, 'sysobjectid': '1.3.6.1.4.*' } }, } } check = SnmpCheck('snmp', init_config, [instance]) check.check(instance) for metric in common.SUPPORTED_METRIC_TYPES: metric_name = "snmp." + metric['name'] aggregator.assert_metric(metric_name, tags=common.CHECK_TAGS, count=1) aggregator.assert_all_metrics_covered()
def test_discovery(aggregator): host = socket.gethostbyname(common.HOST) network = ipaddress.ip_network(u'{}/29'.format(host), strict=False).with_prefixlen check_tags = ['snmp_device:{}'.format(host)] instance = { 'name': 'snmp_conf', # Make sure the check handles bytes 'network_address': network.encode('utf-8'), 'port': common.PORT, 'community_string': 'public', } init_config = { 'profiles': { 'profile1': {'definition': {'metrics': common.SUPPORTED_METRIC_TYPES, 'sysobjectid': '1.3.6.1.4.1.8072.*'}} } } check = SnmpCheck('snmp', init_config, [instance]) try: for _ in range(30): check.check(instance) if len(aggregator.metric_names) > 1: break time.sleep(1) aggregator.reset() finally: check._running = False for metric in common.SUPPORTED_METRIC_TYPES: metric_name = "snmp." + metric['name'] aggregator.assert_metric(metric_name, tags=check_tags, count=1) aggregator.assert_metric('snmp.discovered_devices_count', tags=['network:{}'.format(network)]) aggregator.assert_all_metrics_covered()
def test_profile_error(): instance = common.generate_instance_config([]) instance['profile'] = 'profile1' with pytest.raises(ConfigurationError): SnmpCheck('snmp', {}, [instance]) init_config = { 'profiles': { 'profile1': { 'definition_file': 'doesntexistfile' } } } with pytest.raises(ConfigurationError): SnmpCheck('snmp', init_config, [instance]) with temp_dir() as tmp: profile_file = os.path.join(tmp, 'profile1.yaml') with open(profile_file, 'w') as f: f.write("not yaml: {") init_config = { 'profiles': { 'profile1': { 'definition_file': profile_file } } } with pytest.raises(ConfigurationError): SnmpCheck('snmp', init_config, [instance])
def test_timeout(aggregator, caplog): caplog.set_level(logging.WARNING) instance = common.generate_instance_config([]) instance['community_string'] = 'public_delay' instance['timeout'] = 1 instance['retries'] = 0 check = SnmpCheck('snmp', {}, [instance]) check.check(instance) aggregator.assert_service_check("snmp.can_check", status=SnmpCheck.WARNING, at_least=1) # Some metrics still arrived aggregator.assert_metric('snmp.ifInDiscards', count=4) aggregator.assert_metric('snmp.ifInErrors', count=4) aggregator.assert_metric('snmp.ifOutDiscards', count=4) aggregator.assert_metric('snmp.ifOutErrors', count=4) aggregator.assert_metric('snmp.sysUpTimeInstance', count=1) common.assert_common_metrics(aggregator) aggregator.all_metrics_asserted() for record in caplog.records: if "No SNMP response received before timeout" in record.message: break else: pytest.fail()
def test_f5_router(aggregator): instance = common.generate_instance_config([]) # We need the full path as we're not in installed mode path = os.path.join(os.path.dirname(snmp.__file__), 'data', 'profiles', 'generic-router.yaml') # Use the generic profile against the f5 device instance['community_string'] = 'f5' instance['profile'] = 'router' instance['enforce_mib_constraints'] = False init_config = {'profiles': {'router': {'definition_file': path}}} check = SnmpCheck('snmp', init_config, [instance]) check.check(instance) if_counts = [ 'ifInErrors', 'ifInDiscards', 'ifOutErrors', 'ifOutDiscards', 'ifHCInOctets', 'ifHCInUcastPkts', 'ifHCInMulticastPkts', 'ifHCInBroadcastPkts', 'ifHCOutOctets', 'ifHCOutUcastPkts', 'ifHCOutMulticastPkts', 'ifHCOutBroadcastPkts', ] if_gauges = ['ifAdminStatus', 'ifOperStatus'] # We only get a subset of metrics ip_counts = [ 'ipSystemStatsHCInReceives', 'ipSystemStatsInHdrErrors', 'ipSystemStatsOutFragReqds', 'ipSystemStatsOutFragFails', 'ipSystemStatsHCOutTransmits', 'ipSystemStatsReasmReqds', 'ipSystemStatsHCInMcastPkts', 'ipSystemStatsReasmFails', 'ipSystemStatsHCOutMcastPkts', ] interfaces = ['1.0', 'mgmt', '/Common/internal', '/Common/http-tunnel', '/Common/socks-tunnel'] for interface in interfaces: tags = ['interface:{}'.format(interface)] + common.CHECK_TAGS for metric in if_counts: aggregator.assert_metric( 'snmp.{}'.format(metric), metric_type=aggregator.MONOTONIC_COUNT, tags=tags, count=1 ) for metric in if_gauges: aggregator.assert_metric('snmp.{}'.format(metric), metric_type=aggregator.GAUGE, tags=tags, count=1) for version in ['ipv4', 'ipv6']: tags = ['ipversion:{}'.format(version)] + common.CHECK_TAGS for metric in ip_counts: aggregator.assert_metric( 'snmp.{}'.format(metric), metric_type=aggregator.MONOTONIC_COUNT, tags=tags, count=1 ) aggregator.assert_all_metrics_covered()
def test_cache_corrupted(write_mock, read_mock): instance = common.generate_instance_config(common.SUPPORTED_METRIC_TYPES) instance.pop('ip_address') instance['network_address'] = '192.168.0.0/24' read_mock.return_value = '["192.168.0."]' check = SnmpCheck('snmp', {}, [instance]) check.check(instance) assert not check._config.discovered_instances write_mock.assert_called_once_with('', '[]')
def test_cache_discovered_host(read_mock): instance = common.generate_instance_config(common.SUPPORTED_METRIC_TYPES) instance.pop('ip_address') instance['network_address'] = '192.168.0.0/24' read_mock.return_value = '["192.168.0.1"]' check = SnmpCheck('snmp', {}, [instance]) check.check(instance) assert '192.168.0.1' in check._config.discovered_instances
def test_profile_sys_object_unknown(aggregator): """If the fetched sysObjectID is not referenced by any profiles, check fails.""" instance = common.generate_instance_config([]) init_config = {'profiles': {'profile1': {'definition': common.SUPPORTED_METRIC_TYPES, 'sysobjectid': '1.2.3.4.5'}}} check = SnmpCheck('snmp', init_config, [instance]) check.check(instance) aggregator.assert_service_check("snmp.can_check", status=SnmpCheck.CRITICAL, tags=common.CHECK_TAGS, at_least=1) aggregator.all_metrics_asserted()
def test_profile(aggregator): instance = common.generate_instance_config([]) instance['profile'] = 'profile1' init_config = {'profiles': {'profile1': {'definition': {'metrics': common.SUPPORTED_METRIC_TYPES}}}} check = SnmpCheck('snmp', init_config, [instance]) check.check(instance) for metric in common.SUPPORTED_METRIC_TYPES: metric_name = "snmp." + metric['name'] aggregator.assert_metric(metric_name, tags=common.CHECK_TAGS, count=1) aggregator.assert_all_metrics_covered()
def test_invalid_discovery_interval(): instance = common.generate_instance_config(common.SUPPORTED_METRIC_TYPES) # Trigger autodiscovery. instance.pop('ip_address') instance['network_address'] = '192.168.0.0/24' instance['discovery_interval'] = 'not_parsable_as_a_float' check = SnmpCheck('snmp', {}, [instance]) with pytest.raises(ConfigurationError): check.check(instance)
def test_discovery(aggregator): host = socket.gethostbyname(common.HOST) network = ipaddress.ip_network(u'{}/29'.format(host), strict=False).with_prefixlen check_tags = [ 'snmp_device:{}'.format(host), 'snmp_profile:profile1', 'autodiscovery_subnet:{}'.format(to_native_string(network)), ] instance = { 'name': 'snmp_conf', # Make sure the check handles bytes 'network_address': to_native_string(network), 'port': common.PORT, 'community_string': 'public', 'retries': 0, 'discovery_interval': 0, } init_config = { 'profiles': { 'profile1': { 'definition': { 'metrics': common.SUPPORTED_METRIC_TYPES, 'sysobjectid': '1.3.6.1.4.1.8072.*' } } } } check = SnmpCheck('snmp', init_config, [instance]) try: for _ in range(30): check.check(instance) if len(aggregator.metric_names) > 1: break time.sleep(1) aggregator.reset() finally: check._running = False del check # This is what the Agent would do when unscheduling the check. for metric in common.SUPPORTED_METRIC_TYPES: metric_name = "snmp." + metric['name'] aggregator.assert_metric(metric_name, tags=check_tags, count=1) aggregator.assert_metric('snmp.sysUpTimeInstance') aggregator.assert_metric('snmp.discovered_devices_count', tags=['network:{}'.format(network)]) aggregator.assert_metric('snmp.devices_monitored', metric_type=aggregator.GAUGE, tags=check_tags) aggregator.assert_all_metrics_covered()
def test_try_submit_bandwidth_usage_metric_if_bandwidth_metric(): instance = common.generate_instance_config([]) check = SnmpCheck('snmp', {}, [instance]) index = ('1', '2') tags = ['foo', 'bar'] results = { 'ifHighSpeed': { ('1', '2'): 80, }, 'ifHCInOctets': { ('1', '2'): 5000000, }, 'ifHCOutOctets': { ('1', '2'): 1000000, }, } check.rate = mock.Mock() check.try_submit_bandwidth_usage_metric_if_bandwidth_metric( 'ifHCInOctets', index, results, tags) # ((5000000 * 8) / (80 * 1000000)) * 100 = 50.0 check.rate.assert_called_with('snmp.ifBandwidthInUsage.rate', 50.0, ['foo', 'bar']) check.rate = mock.Mock() check.try_submit_bandwidth_usage_metric_if_bandwidth_metric( 'ifHCOutOctets', index, results, tags) # ((1000000 * 8) / (80 * 1000000)) * 100 = 10.0 check.rate.assert_called_with('snmp.ifBandwidthOutUsage.rate', 10.0, ['foo', 'bar'])
def test_duplicate_sysobjectid_error(): profile1 = {'sysobjectid': '1.3.6.1.4.1.30932.*'} profile2 = copy.copy(profile1) instance = common.generate_instance_config([]) init_config = {'profiles': {'profile1': {'definition': profile1}, 'profile2': {'definition': profile2}}} with pytest.raises(ConfigurationError) as e: SnmpCheck('snmp', init_config, [instance]) assert "has the same sysObjectID" in str(e.value) # no errors are raised init_config['profiles']['profile2']['definition']['sysobjectid'] = '1.3.6.2.4.1.30932.*' SnmpCheck('snmp', init_config, [instance])
def test_custom_mib(aggregator): instance = common.generate_instance_config([oid for oid, _, _ in common.DUMMY_MIB_OID]) instance["community_string"] = "dummy" check = SnmpCheck('snmp', common.MIBS_FOLDER, [instance]) check.check(instance) # Test metrics for metric, metric_type, value in common.DUMMY_MIB_OID: metric_name = "snmp." + (metric.get('name') or metric.get('symbol')) aggregator.assert_metric(metric_name, metric_type=metric_type, count=1, value=value, tags=common.CHECK_TAGS) # Test service check aggregator.assert_service_check("snmp.can_check", status=SnmpCheck.OK, tags=common.CHECK_TAGS, at_least=1)
def test_cache_loading_tags(thread_mock, read_mock): """When loading discovered instances from cache, tags don't leak from one to the others.""" read_mock.return_value = '["192.168.0.1", "192.168.0.2"]' instance = common.generate_instance_config(common.SUPPORTED_METRIC_TYPES) instance.pop('ip_address') instance['network_address'] = '192.168.0.0/29' instance['discovery_interval'] = 0 instance['tags'] = ['test:check'] check = SnmpCheck('snmp', {}, [instance]) check._start_discovery() config = check._config.discovered_instances['192.168.0.2'] assert set(config.tags) == {'snmp_device:192.168.0.2', 'test:check'}
def test_ignore_ip_addresses(): # type: () -> None instance = common.generate_instance_config(common.SUPPORTED_METRIC_TYPES) instance.pop('ip_address') instance['network_address'] = '192.168.1.0/29' instance['ignored_ip_addresses'] = ['192.168.1.2', '192.168.1.3', '192.168.1.5'] check = SnmpCheck('snmp', {}, [instance]) assert list(check._config.network_hosts()) == ['192.168.1.1', '192.168.1.4', '192.168.1.6'] instance = common.generate_instance_config(common.SUPPORTED_METRIC_TYPES) string_not_in_a_list = '192.168.1.0/29' instance['ignored_ip_addresses'] = string_not_in_a_list with pytest.raises(ConfigurationError): SnmpCheck('snmp', {}, [instance])
def test_try_submit_bandwidth_usage_metric_if_bandwidth_metric_errors( results, metric_name, error_messages, caplog): instance = common.generate_instance_config([]) check = SnmpCheck('snmp', {}, [instance]) index = ('1', '2') tags = ['foo', 'bar'] check.rate = mock.Mock() with caplog.at_level(logging.DEBUG): check.try_submit_bandwidth_usage_metric_if_bandwidth_metric( metric_name, index, results, tags) check.rate.assert_not_called() for msg in error_messages: assert msg in caplog.text
def test_command_generator(aggregator): """ Command generator's parameters should match init_config """ check = SnmpCheck('snmp', common.MIBS_FOLDER, {}, {}) snmp_engine, _, _, _, _, _, _, _ = check._load_conf(common.SNMP_CONF) # Test command generator MIB source mib_folders = snmp_engine.getMibBuilder().getMibSources() full_path_mib_folders = map(lambda f: f.fullPath(), mib_folders) assert check.ignore_nonincreasing_oid is False # Default value check = SnmpCheck('snmp', common.IGNORE_NONINCREASING_OID, {}, {}) assert check.ignore_nonincreasing_oid is True assert common.MIBS_FOLDER["mibs_folder"] in full_path_mib_folders
def test_snmp_getnext_call(check): instance = common.generate_instance_config(common.PLAY_WITH_GET_NEXT_METRICS) # Test that we invoke next with the correct keyword arguments that are hard to test otherwise with mock.patch("datadog_checks.snmp.snmp.hlapi.nextCmd") as nextCmd: check.check(instance) _, kwargs = nextCmd.call_args assert ("ignoreNonIncreasingOid", False) in kwargs.items() assert ("lexicographicMode", False) in kwargs.items() check = SnmpCheck('snmp', common.IGNORE_NONINCREASING_OID, {}, {}) check.check(instance) _, kwargs = nextCmd.call_args assert ("ignoreNonIncreasingOid", True) in kwargs.items() assert ("lexicographicMode", False) in kwargs.items()
def test_no_address(): instance = common.generate_instance_config([]) instance.pop('ip_address') with pytest.raises(ConfigurationError) as e: SnmpCheck('snmp', {}, [instance]) assert str( e.value) == 'An IP address or a network address needs to be specified'
def test_failed_to_collect_metrics(): config = InstanceConfig( {"ip_address": "127.0.0.123", "community_string": "public", "metrics": [{"OID": "1.2.3", "name": "foo"}]} ) instance = common.generate_instance_config(common.SUPPORTED_METRIC_TYPES) instance.pop('ip_address') instance['network_address'] = '192.168.0.0/24' check = SnmpCheck('snmp', {}, [instance]) check.fetch_results = mock.Mock(return_value=ValueError("invalid value")) check._check_with_config(config) assert len(check.warnings) == 1 assert 'Failed to collect metrics for 127.0.0.123' in check.warnings[0]
def test_command_generator(): """ Command generator's parameters should match init_config """ instance = common.generate_instance_config(common.CONSTRAINED_OID) check = SnmpCheck('snmp', common.MIBS_FOLDER, [instance]) config = check._config # Test command generator MIB source mib_folders = config._snmp_engine.getMibBuilder().getMibSources() full_path_mib_folders = [f.fullPath() for f in mib_folders] assert check.ignore_nonincreasing_oid is False # Default value check = SnmpCheck('snmp', common.IGNORE_NONINCREASING_OID, [instance]) assert check.ignore_nonincreasing_oid assert common.MIBS_FOLDER["mibs_folder"] in full_path_mib_folders
def test_both_addresses(): instance = common.generate_instance_config(common.SUPPORTED_METRIC_TYPES) instance['network_address'] = '192.168.0.0/24' with pytest.raises(ConfigurationError) as e: SnmpCheck('snmp', {}, [instance]) assert str( e.value ) == 'Only one of IP address and network address must be specified'
def test_extract_value_does_not_match(aggregator, caplog): instance = common.generate_instance_config( [ { "MIB": "DUMMY-MIB", 'symbol': { 'OID': "1.3.6.1.4.1.123456789.4.0", 'name': "aTemperatureValueInferred", 'extract_value': r'(\d+)ZZZ', }, } ] ) instance["community_string"] = "dummy" check = SnmpCheck('snmp', common.MIBS_FOLDER, [instance]) check.check(instance) assert "Unable to submit metric `aTemperatureValueInferred`" in str(caplog.text)