From 5e3ceaa65dbe48a857b5c0735e48d7ae1e9b377c Mon Sep 17 00:00:00 2001 From: Cory Watson Date: Wed, 31 Aug 2016 07:42:12 -0700 Subject: [PATCH 1/5] Allow expected address to be checked. --- checks.d/dns_check.py | 6 ++++++ conf.d/dns_check.yaml.example | 6 ++++++ tests/checks/mock/test_dns_check.py | 24 ++++++++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/checks.d/dns_check.py b/checks.d/dns_check.py index 0d2dbf8528..45cf3595a1 100644 --- a/checks.d/dns_check.py +++ b/checks.d/dns_check.py @@ -52,14 +52,20 @@ 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) + resolved_value = answer.rrset.items[0].to_text() assert(answer.rrset.items[0].to_text()) end_time = time.time() + if len(expected_results) > 0: + if resolved_value not in expected_results: + raise Exception("Resolution did not match excpected value.") + 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)) diff --git a/conf.d/dns_check.yaml.example b/conf.d/dns_check.yaml.example index 0d05d4e146..9b9173e804 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 a match to any one will + # be considered success + # addressses: + # - 127.0.0.1 diff --git a/tests/checks/mock/test_dns_check.py b/tests/checks/mock/test_dns_check.py index 476edf7d02..1a0002e22b 100644 --- a/tests/checks/mock/test_dns_check.py +++ b/tests/checks/mock/test_dns_check.py @@ -55,6 +55,30 @@ 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_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 = { From 5f6a7816cea4450529b4c29b6310c93003cae765 Mon Sep 17 00:00:00 2001 From: Cory Watson Date: Thu, 1 Sep 2016 08:11:15 -0700 Subject: [PATCH 2/5] Adjust exception message. --- checks.d/dns_check.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/checks.d/dns_check.py b/checks.d/dns_check.py index 45cf3595a1..3ef80fb89f 100644 --- a/checks.d/dns_check.py +++ b/checks.d/dns_check.py @@ -64,15 +64,15 @@ def query_dns(self, instance, timeout): end_time = time.time() if len(expected_results) > 0: if resolved_value not in expected_results: - raise Exception("Resolution did not match excpected value.") + raise Exception('DNS resolution of %s resulted in unexpected address %s.' % (hostname, resolved_value)) 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 end_time - start_time > 0: From aa11e01375486d7b8c3935e7737a8e33cb7d70a7 Mon Sep 17 00:00:00 2001 From: Cory Watson Date: Thu, 22 Sep 2016 08:09:45 -0700 Subject: [PATCH 3/5] Adjustments from PR and a first stab at multiple addresses. --- checks.d/dns_check.py | 11 ++++++----- conf.d/dns_check.yaml.example | 6 +++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/checks.d/dns_check.py b/checks.d/dns_check.py index 3ef80fb89f..6a05efb7ce 100644 --- a/checks.d/dns_check.py +++ b/checks.d/dns_check.py @@ -59,12 +59,8 @@ def query_dns(self, instance, timeout): try: self.log.debug('Querying "%s" record for hostname "%s"...' % (record_type, hostname)) answer = resolver.query(hostname, rdtype=record_type) - resolved_value = answer.rrset.items[0].to_text() - assert(answer.rrset.items[0].to_text()) + resolved_results = map(lambda x: x.to_text(), answer.rrset.items) end_time = time.time() - if len(expected_results) > 0: - if resolved_value not in expected_results: - raise Exception('DNS resolution of %s resulted in unexpected address %s.' % (hostname, resolved_value)) except dns.exception.Timeout: self.log.error('DNS resolution of %s timed out' % hostname) @@ -75,6 +71,11 @@ def query_dns(self, instance, timeout): 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 9b9173e804..615b0c2a4f 100644 --- a/conf.d/dns_check.yaml.example +++ b/conf.d/dns_check.yaml.example @@ -11,7 +11,7 @@ instances: # record_type: A # Specify expected results if you want to check that the returned IP - # matches. If you supply multiple records then a match to any one will - # be considered success - # addressses: + # matches. If you supply multiple records then ALL included addresses + # will need to match! + # addresses: # - 127.0.0.1 From 65787febba5877cf1fba7d7ce4f0f8b93e7f37d2 Mon Sep 17 00:00:00 2001 From: Cory Watson Date: Thu, 22 Sep 2016 08:17:22 -0700 Subject: [PATCH 4/5] Try and test multiple addresses. --- tests/checks/mock/test_dns_check.py | 35 +++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/tests/checks/mock/test_dns_check.py b/tests/checks/mock/test_dns_check.py index 1a0002e22b..9e6ead25bb 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(address), 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' @@ -67,6 +70,30 @@ def test_success_addresses(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_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 = { From 474a1f579561a46cdc10fd982b6f1cacedfb51ab Mon Sep 17 00:00:00 2001 From: Cory Watson Date: Thu, 22 Sep 2016 08:25:56 -0700 Subject: [PATCH 5/5] Fix bad c&p :P --- tests/checks/mock/test_dns_check.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/checks/mock/test_dns_check.py b/tests/checks/mock/test_dns_check.py index 9e6ead25bb..1b75de9fa7 100644 --- a/tests/checks/mock/test_dns_check.py +++ b/tests/checks/mock/test_dns_check.py @@ -23,7 +23,7 @@ def __init__(self, *args): class MockRrset: def __init__(self, *args): - self.items = map(lambda x: MockDNSAnswer.MockItem(address), args) + self.items = map(lambda x: MockDNSAnswer.MockItem(x), args) class MockItem: def __init__(self, address):