diff --git a/checks.d/http_check.py b/checks.d/http_check.py index 446f14b1f0..e3030c878e 100644 --- a/checks.d/http_check.py +++ b/checks.d/http_check.py @@ -45,27 +45,29 @@ def __init__(self, name, init_config, agentConfig, instances): def _load_conf(self, instance): # Fetches the conf tags = instance.get('tags', []) - username = instance.get('username', None) - password = instance.get('password', None) + username = instance.get('username') + password = instance.get('password') + http_response_status_code = instance.get('http_response_status_code', "(1|2|3)\d\d") timeout = int(instance.get('timeout', 10)) config_headers = instance.get('headers', {}) headers = agent_headers(self.agentConfig) headers.update(config_headers) - url = instance.get('url', None) - content_match = instance.get('content_match', None) + url = instance.get('url') + content_match = instance.get('content_match') response_time = _is_affirmative(instance.get('collect_response_time', True)) - if url is None: + if not url: raise Exception("Bad configuration. You must specify a url") include_content = _is_affirmative(instance.get('include_content', False)) ssl = _is_affirmative(instance.get('disable_ssl_validation', True)) ssl_expire = _is_affirmative(instance.get('check_certificate_expiration', True)) - return url, username, password, timeout, include_content, headers, response_time,\ - content_match, tags, ssl, ssl_expire + return url, username, password, http_response_status_code, timeout, include_content,\ + headers, response_time, content_match, tags, ssl, ssl_expire def _check(self, instance): - addr, username, password, timeout, include_content, headers, response_time,\ - content_match, tags, disable_ssl_validation, ssl_expire = self._load_conf(instance) + addr, username, password, http_response_status_code, timeout, include_content, headers,\ + response_time, content_match, tags, disable_ssl_validation,\ + ssl_expire = self._load_conf(instance) start = time.time() service_checks = [] @@ -73,7 +75,8 @@ def _check(self, instance): try: self.log.debug("Connecting to %s" % addr) if disable_ssl_validation and urlparse(addr)[0] == "https": - self.warning("Skipping SSL certificate validation for %s based on configuration" % addr) + self.warning("Skipping SSL certificate validation for %s based on configuration" + % addr) auth = None if username is not None and password is not None: @@ -81,7 +84,6 @@ def _check(self, instance): r = requests.get(addr, auth=auth, timeout=timeout, headers=headers, verify=not disable_ssl_validation) - r.raise_for_status() except socket.timeout, e: length = int((time.time() - start) * 1000) @@ -93,16 +95,6 @@ def _check(self, instance): "%s. Connection failed after %s ms" % (str(e), length) )) - except requests.exceptions.HTTPError, r: - length = int((time.time() - start) * 1000) - self.log.info("%s is DOWN, error code: %s" % (addr, str(r.status_code))) - - content = r.content if include_content else '' - - service_checks.append(( - self.SC_STATUS, Status.DOWN, (r.status_code, r.reason, content or '') - )) - except requests.exceptions.ConnectionError, e: length = int((time.time() - start) * 1000) self.log.info("%s is DOWN, error: %s. Connection failed after %s ms" @@ -138,6 +130,18 @@ def _check(self, instance): tags_list.append('url:%s' % addr) self.gauge('network.http.response_time', running_time, tags=tags_list) + # Check HTTP response status code + if not (service_checks or re.match(http_response_status_code, str(r.status_code))): + self.log.info("Incorrect HTTP return code. Expected %s, got %s" + % (http_response_status_code, str(r.status_code))) + + service_checks.append(( + self.SC_STATUS, + Status.DOWN, + "Incorrect HTTP return code. Expected %s, got %s" + % (http_response_status_code, str(r.status_code)) + )) + if not service_checks: # Host is UP # Check content matching is set @@ -197,7 +201,6 @@ def _create_status_event(self, sc_name, status, msg, instance): else: source_type = "%s.%s" % (NetworkCheck.SOURCE_TYPE_NAME, instance_source_type_name) - # Get the handles you want to notify notify = instance.get('notify', self.init_config.get('notify', [])) notify_message = "" diff --git a/conf.d/http_check.yaml.example b/conf.d/http_check.yaml.example index 632c31efc9..4e3898fba1 100644 --- a/conf.d/http_check.yaml.example +++ b/conf.d/http_check.yaml.example @@ -26,6 +26,12 @@ instances: # username: user # password: pass + # The (optional) http_response_status_code parameter will instruct the check + # to look for a particular HTTP response status code. + # The check will report as DOWN if status code returned differs. + # This defaults to 1xx, 2xx and 3xx HTTP status code: (1|2|3)\d\d. + # http_response_status_code: 401 + # The (optional) window and threshold parameters allow you to trigger # alerts only if the check fails x times within the last y attempts # where x is the threshold and y is the window. diff --git a/tests/test_http_check.py b/tests/test_http_check.py index 9a7058efe8..30e2eddde0 100644 --- a/tests/test_http_check.py +++ b/tests/test_http_check.py @@ -8,24 +8,29 @@ CONFIG = { 'instances': [{ - 'name': 'bad_url', + 'name': 'conn_error', 'url': 'https://thereisnosuchlink.com', 'check_certificate_expiration': False, 'timeout': 1, }, { - 'name': 'content_mismatch', - 'url': 'https://github.com', + 'name': 'http_error_status_code', + 'url': 'http://httpbin.org/404', + 'check_certificate_expiration': False, 'timeout': 1, + }, { + 'name': 'status_code_match', + 'url': 'http://httpbin.org/404', + 'http_response_status_code': '4..', 'check_certificate_expiration': False, - 'content_match': 'thereisnosuchword' + 'timeout': 1, }, { - 'name': 'simple_match', + 'name': 'cnt_mismatch', 'url': 'https://github.com', 'timeout': 1, 'check_certificate_expiration': False, - 'content_match': 'github' + 'content_match': 'thereisnosuchword' }, { - 'name': 'regexp_match', + 'name': 'cnt_match', 'url': 'https://github.com', 'timeout': 1, 'check_certificate_expiration': False, @@ -48,7 +53,7 @@ 'check_certificate_expiration': True, 'days_warning': 9999 }, { - 'name': 'bad_url', + 'name': 'conn_error', 'url': 'https://thereisnosuchlink.com', 'timeout': 1, 'check_certificate_expiration': True, @@ -91,18 +96,29 @@ def wait_for_async_service_checks(self, count): def test_check(self): self.run_check(CONFIG) - # Overrides self.service_checks attribute when values are available - self.service_checks = self.wait_for_async_service_checks(4) + # Overrides self.service_checks attribute when values are available\ + self.service_checks = self.wait_for_async_service_checks(5) - self.assertServiceCheck("http_check.bad_url", status=AgentCheck.CRITICAL, + # HTTP connection error + self.assertServiceCheck("http_check.conn_error", status=AgentCheck.CRITICAL, tags=['url:https://thereisnosuchlink.com']) - self.assertServiceCheck("http_check.content_mismatch", status=AgentCheck.CRITICAL, + + # Wrong HTTP response status code + self.assertServiceCheck("http_check.http_error_status_code", status=AgentCheck.CRITICAL, + tags=['url:http://httpbin.org/404']) + self.assertServiceCheck("http_check.http_error_status_code", status=AgentCheck.OK, + tags=['url:http://httpbin.org/404'], count=0) + + # HTTP response status code match + self.assertServiceCheck("http_check.status_code_match", status=AgentCheck.OK, + tags=['url:http://httpbin.org/404']) + + # Content match & mismatching + self.assertServiceCheck("http_check.cnt_mismatch", status=AgentCheck.CRITICAL, tags=['url:https://github.com']) - self.assertServiceCheck("http_check.content_mismatch", status=AgentCheck.OK, + self.assertServiceCheck("http_check.cnt_mismatch", status=AgentCheck.OK, tags=['url:https://github.com'], count=0) - self.assertServiceCheck("http_check.simple_match", status=AgentCheck.OK, - tags=['url:https://github.com']) - self.assertServiceCheck("http_check.regexp_match", status=AgentCheck.OK, + self.assertServiceCheck("http_check.cnt_match", status=AgentCheck.OK, tags=['url:https://github.com']) def test_check_ssl(self): @@ -113,7 +129,7 @@ def test_check_ssl(self): tags=['url:https://github.com']) self.assertServiceCheck("http_check.ssl_cert.cert_exp_soon", status=AgentCheck.WARNING, tags=['url:https://github.com']) - self.assertServiceCheck("http_check.ssl_cert.bad_url", status=AgentCheck.CRITICAL, + self.assertServiceCheck("http_check.ssl_cert.conn_error", status=AgentCheck.CRITICAL, tags=['url:https://thereisnosuchlink.com']) @mock.patch('ssl.SSLSocket.getpeercert', return_value=FAKE_CERT) diff --git a/tests/test_service_checks.py b/tests/test_service_checks.py index f9ba36cc00..72e15a81b9 100644 --- a/tests/test_service_checks.py +++ b/tests/test_service_checks.py @@ -33,8 +33,9 @@ def testHTTPHeaders(self): } self.init_check(config, 'http_check') - url, username, password, timeout, include_content, headers, response_time, content_match,\ - tags, ssl, ssl_expiration = self.check._load_conf(config['instances'][0]) + url, username, password, timeout, http_response_status_code, include_content, headers, \ + response_time, content_match, tags, ssl, \ + ssl_expiration = self.check._load_conf(config['instances'][0]) self.assertTrue(headers["X-Auth-Token"] == "SOME-AUTH-TOKEN", headers) self.assertTrue(headers.get('User-Agent') == agent_headers(self.agentConfig).get('User-Agent'), headers)