diff --git a/checks.d/dns_check.py b/checks.d/dns_check.py index 0d2dbf8528..6a05efb7ce 100644 --- a/checks.d/dns_check.py +++ b/checks.d/dns_check.py @@ -52,23 +52,30 @@ def query_dns(self, instance, timeout): resolver.nameservers = [nameserver] record_type = instance.get('record_type', 'A') + expected_results = instance.get('addresses', []) status = AgentCheck.CRITICAL start_time = time.time() try: self.log.debug('Querying "%s" record for hostname "%s"...' % (record_type, hostname)) answer = resolver.query(hostname, rdtype=record_type) - assert(answer.rrset.items[0].to_text()) + resolved_results = map(lambda x: x.to_text(), answer.rrset.items) end_time = time.time() + except dns.exception.Timeout: self.log.error('DNS resolution of %s timed out' % hostname) self.service_check(self.SERVICE_CHECK_NAME, status, tags=self._get_tags(instance)) raise - except Exception: - self.log.exception('DNS resolution of %s has failed.' % hostname) - self.service_check(self.SERVICE_CHECK_NAME, status, tags=self._get_tags(instance)) + except Exception as err: + self.log.exception(err) + self.service_check(self.SERVICE_CHECK_NAME, status, tags=self._get_tags(instance), message=err) raise else: + if len(expected_results) > 0: + missing_values = list(set(expected_results, resolved_results)) + if len(missing_values) > 0: + self.log.error('DNS resolution of %s did not contain expected address(es) %s.' % (hostname, ", ".join(missing_values))) + self.service_check(self.SERVICE_CHECK_NAME, status, tags=self._get_tags(instance)) if end_time - start_time > 0: self.gauge('dns.response_time', end_time - start_time, tags=tags) self.service_check(self.SERVICE_CHECK_NAME, AgentCheck.OK, tags=self._get_tags(instance)) diff --git a/conf.d/dns_check.yaml.example b/conf.d/dns_check.yaml.example index 0d05d4e146..615b0c2a4f 100644 --- a/conf.d/dns_check.yaml.example +++ b/conf.d/dns_check.yaml.example @@ -9,3 +9,9 @@ instances: # Specify an (optional) `record_type` to customize the record type # queried by the check (default: "A") # record_type: A + + # Specify expected results if you want to check that the returned IP + # matches. If you supply multiple records then ALL included addresses + # will need to match! + # addresses: + # - 127.0.0.1 diff --git a/tests/checks/mock/test_dns_check.py b/tests/checks/mock/test_dns_check.py index 476edf7d02..1b75de9fa7 100644 --- a/tests/checks/mock/test_dns_check.py +++ b/tests/checks/mock/test_dns_check.py @@ -18,12 +18,12 @@ class MockDNSAnswer: - def __init__(self, address): - self.rrset = MockDNSAnswer.MockRrset(address) + def __init__(self, *args): + self.rrset = MockDNSAnswer.MockRrset(args) class MockRrset: - def __init__(self, address): - self.items = [MockDNSAnswer.MockItem(address)] + def __init__(self, *args): + self.items = map(lambda x: MockDNSAnswer.MockItem(x), args) class MockItem: def __init__(self, address): @@ -39,6 +39,9 @@ def success_query_mock(d_name, rdtype): elif rdtype == 'CNAME': return MockDNSAnswer('alias.example.org') +def multiple_records_query_mock(d_name, rdtype): + return MockDNSAnswer('127.0.0.1','127.0.0.2') + class TestDns(AgentCheckTest): CHECK_NAME = 'dns_check' @@ -55,6 +58,54 @@ def test_success(self, mocked_query): tags=['resolved_hostname:www.example.org', 'nameserver:127.0.0.1']) self.coverage_report() + @mock.patch.object(Resolver, 'query', side_effect=success_query_mock) + def test_success_addresses(self, mocked_query): + config = { + 'instances': [{'hostname': 'www.example.org', 'nameserver': '127.0.0.1', 'addresses': ['127.0.0.1']}] + } + self.run_check(config) + self.assertMetric('dns.response_time', count=1, + tags=['nameserver:127.0.0.1', 'resolved_hostname:www.example.org']) + self.assertServiceCheck(SERVICE_CHECK_NAME, status=AgentCheck.OK, + tags=['resolved_hostname:www.example.org', 'nameserver:127.0.0.1']) + self.coverage_report() + + @mock.patch.object(Resolver, 'query', side_effect=success_query_mock) + def test_success_multiple_addresses(self, multiple_records_query_mock): + config = { + 'instances': [{'hostname': 'www.example.org', 'nameserver': '127.0.0.1', 'addresses': ['127.0.0.1','127.0.0.2']}] + } + self.run_check(config) + self.assertMetric('dns.response_time', count=1, + tags=['nameserver:127.0.0.1', 'resolved_hostname:www.example.org']) + self.assertServiceCheck(SERVICE_CHECK_NAME, status=AgentCheck.OK, + tags=['resolved_hostname:www.example.org', 'nameserver:127.0.0.1']) + self.coverage_report() + + @mock.patch.object(Resolver, 'query', side_effect=success_query_mock) + def test_failure_multiple_addresses(self, multiple_records_query_mock): + config = { + 'instances': [{'hostname': 'www.example.org', 'nameserver': '127.0.0.1', 'addresses': ['127.0.0.1','127.0.0.3']}] + } + self.run_check(config) + self.assertMetric('dns.response_time', count=1, + tags=['nameserver:127.0.0.1', 'resolved_hostname:www.example.org']) + self.assertServiceCheck(SERVICE_CHECK_NAME, status=AgentCheck.CRITICAL, + tags=['resolved_hostname:www.example.org', 'nameserver:127.0.0.1']) + self.coverage_report() + + @mock.patch.object(Resolver, 'query', side_effect=success_query_mock) + def test_failure_addresses(self, mocked_query): + config = { + 'instances': [{'hostname': 'www.example.org', 'nameserver': '127.0.0.1', 'addresses': ['127.0.0.2']}] + } + self.run_check(config) + self.assertMetric('dns.response_time', count=1, + tags=['nameserver:127.0.0.1', 'resolved_hostname:www.example.org']) + self.assertServiceCheck(SERVICE_CHECK_NAME, status=AgentCheck.CRITICAL, + tags=['resolved_hostname:www.example.org', 'nameserver:127.0.0.1']) + self.coverage_report() + @mock.patch.object(Resolver, 'query', side_effect=success_query_mock) def test_success_CNAME(self, mocked_query): config = {