[prev in list] [next in list] [prev in thread] [next in thread] 

List:       keepalived-devel
Subject:    [Keepalived-devel] [PATCH] Make http(s) healthchecker check for regular expression
From:       Anders Henke <anders.henke () 1und1 ! de>
Date:       2012-02-09 15:04:25
Message-ID: 20120209150425.GH9364 () 1und1 ! de
[Download RAW message or body]

Hi,

we've been investigating a few minor issues with LVS and keepalived; a
freelance developer at my team also prepared a couple of smaller patches
to resolve those issues we'd like to contribute to the general
LVS/keepalived public.

The patches are based upon commit
91c61a85dccffd00dd6e887d24302443e412433d
from http://master.formilux.org/git/people/alex/keepalived.git, but
of course to apply to newer releases as well.

=== Regex content check for http(s) healthcheckers

Usually, checking for the digest of a static (testing-only) website is
fine to check wether some service is available; in some setups, we do
need some way to also check more "dynamic" content and not only the
testing code by checking the content of a http(s) website.

Over the last few years, we did so using a check_misc command, but chose 
to implement an option to the keepalived-internal healtchecker as well.

The attached patch uses the regex functions of ICU/libicuuc to check 
for a regular expression within the first 4000 byte of returned content.

Best,

Anders
-- 
1&1 Internet AG              Expert Systems Architect (IT Operations)
Brauerstrasse 48             v://49.721.91374.0
D-76135 Karlsruhe            f://49.721.91374.225

Amtsgericht Montabaur HRB 6484
Vorstände: Henning Ahlert, Ralph Dommermuth, Matthias Ehrlich, Robert Hoffmann,
Markus Huhn, Hans-Henning Kettler, Dr. Oliver Mauss, Jan Oetjen

Aufsichtsratsvorsitzender: Michael Scheeren


["0001-Implement-http-s-check-for-regular-expression.patch" (text/x-diff)]

> From 2e2a1a4aab20ea5f164ac9116767c5006bc61ec8 Mon Sep 17 00:00:00 2001
From: Alexander Holler <alexander.holler@1und1.de>
Date: Wed, 23 Nov 2011 09:28:38 +0100
Subject: [PATCH 1/3] Implement http(s) check for regular expression.

---
 configure.in                    |    4 ++
 keepalived/check/check_http.c   |   87 +++++++++++++++++++++++++++++++++++++++
 keepalived/include/check_http.h |    5 ++
 3 files changed, 96 insertions(+), 0 deletions(-)

diff --git a/configure.in b/configure.in
index fd41a9d..9c3624c 100644
--- a/configure.in
+++ b/configure.in
@@ -44,12 +44,16 @@ dnl [do we really need this ?] AC_CHECK_HEADERS(linux/netlink.h \
linux/rtnetlink.  AC_CHECK_HEADERS(openssl/ssl.h openssl/md5.h \
                openssl/err.h,,AC_MSG_ERROR([
   !!! OpenSSL is not properly installed on your system. !!!
   !!! Can not include OpenSSL headers files.            !!!]))
+AC_CHECK_HEADERS(unicode/uregex.h unicode/ustring.h,,AC_MSG_ERROR([
+  !!! ICU is not properly installed on your system. !!!
+  !!! Can not include ICU header files.             !!!]))
 AC_CHECK_DECL([ETHERTYPE_IPV6],[],[CFLAGS="$CFLAGS -DETHERTYPE_IPV6=0x86dd"],
   [[@%:@include <net/ethernet.h>]])
 
 dnl ----[ Checks for libraries ]----
 AC_CHECK_LIB(crypto, MD5_Init,,AC_MSG_ERROR([OpenSSL libraries are required]))
 AC_CHECK_LIB(ssl, SSL_CTX_new,,AC_MSG_ERROR([OpenSSL libraries are required]))
+AC_CHECK_LIB(icui18n, uregex_openC_44,,AC_MSG_ERROR([ICU libraries are required]))
 AC_CHECK_LIB(popt, poptGetContext,,AC_MSG_ERROR([Popt libraries is required]))
 AC_CHECK_LIB(nl, nl_handle_alloc,
   [
diff --git a/keepalived/check/check_http.c b/keepalived/check/check_http.c
index 0d1a12c..063919b 100644
--- a/keepalived/check/check_http.c
+++ b/keepalived/check/check_http.c
@@ -22,6 +22,8 @@
  */
 
 #include <openssl/err.h>
+#include <unicode/uregex.h>
+#include <unicode/ustring.h>
 #include "check_http.h"
 #include "check_ssl.h"
 #include "check_api.h"
@@ -40,6 +42,9 @@ free_url(void *data)
 	url_t *url = data;
 	FREE(url->path);
 	FREE(url->digest);
+	FREE(url->regex_pattern);
+	if(url->regexp)
+		uregex_close(url->regexp);
 	FREE(url);
 }
 
@@ -54,6 +59,9 @@ dump_url(void *data)
 	if (url->status_code)
 		log_message(LOG_INFO, "           HTTP Status Code = %d",
 		       url->status_code);
+	if (url->regex_pattern)
+		log_message(LOG_INFO, "           regex = %s",
+		       url->regex_pattern);
 }
 
 void
@@ -180,6 +188,21 @@ digest_handler(vector strvec)
 }
 
 void
+regex_handler(vector strvec)
+{
+	http_checker_t *http_get_chk = CHECKER_GET();
+	url_t *url = LIST_TAIL_DATA(http_get_chk->url);
+
+	url->regex_pattern = CHECKER_VALUE_STRING(strvec);
+	if (!url->regexp) {
+		UErrorCode status = U_ZERO_ERROR;
+		url->regexp = uregex_openC(url->regex_pattern, 0, NULL, &status);
+		if (U_FAILURE(status))
+			log_message(LOG_INFO, "Failed setting up regular expression pattern (%s).", \
u_errorName(status)); +	}
+}
+
+void
 status_code_handler(vector strvec)
 {
 	http_checker_t *http_get_chk = CHECKER_GET();
@@ -203,6 +226,7 @@ install_http_check_keyword(void)
 	install_keyword("path", &path_handler);
 	install_keyword("digest", &digest_handler);
 	install_keyword("status_code", &status_code_handler);
+	install_keyword("regex", &regex_handler);
 	install_sublevel_end();
 	install_sublevel_end();
 }
@@ -223,6 +247,7 @@ install_ssl_check_keyword(void)
 	install_keyword("path", &path_handler);
 	install_keyword("digest", &digest_handler);
 	install_keyword("status_code", &status_code_handler);
+	install_keyword("regex", &regex_handler);
 	install_sublevel_end();
 	install_sublevel_end();
 }
@@ -323,6 +348,8 @@ epilog(thread_t * thread, int method, int t, int c)
 			SSL_free(req->ssl);
 		if (req->buffer)
 			FREE(req->buffer);
+		if (req->buffer_regex)
+			FREE(req->buffer_regex);
 		FREE(req);
 		http_arg->req = NULL;
 		close(thread->u.fd);
@@ -478,6 +505,56 @@ http_handle_response(thread_t * thread, unsigned char digest[16]
 		}
 	}
 
+	/* Continue with REGEXP */
+	if (fetched_url->regexp) {
+		/* Search the regular expression */
+		if( req->buffer_regex_len) {
+			UErrorCode status = U_ZERO_ERROR;
+			UChar *text = MALLOC(req->buffer_regex_len*2+1);
+			req->buffer_regex[req->buffer_regex_len] = 0;
+			u_uastrncpy(text, req->buffer_regex, req->buffer_regex_len);
+			uregex_setText(fetched_url->regexp, text, req->buffer_regex_len, &status);
+			uregex_setTimeLimit(fetched_url->regexp, 500, &status);
+			r = (uregex_find(fetched_url->regexp, 0, &status) == FALSE);
+			FREE(text);
+			if (U_FAILURE(status))
+				log_message(LOG_INFO, "Failed setting up regular expression (%s).", \
u_errorName(status)); +		}
+		else
+			r = 1; /* No HTML code received to check teh pattern against */
+		if (r) {
+			/* check if server is currently alive */
+			if (svr_checker_up(checker->id, checker->rs)) {
+				log_message(LOG_INFO,
+				       "Regex error to [%s]:%d url[%s].",
+				       inet_sockaddrtos(&http_get_check->dst),
+				       ntohs(inet_sockaddrport(&http_get_check->dst)),
+				       fetched_url->path);
+				smtp_alert(checker->rs, NULL, NULL,
+					   "DOWN",
+					   "=> CHECK failed on service"
+					   " : HTTP regex not found <=");
+				update_svr_checker_state(DOWN, checker->id
+							     , checker->vs
+							     , checker->rs);
+			} else {
+				/*
+				 * We set retry iterator to max value to not retry
+				 * when service is already know as die.
+				 */
+				http_arg->retry_it = http_get_check->nb_get_retry;
+			}
+			return epilog(thread, 2, 0, 1);
+		} else {
+			if (!svr_checker_up(checker->id, checker->rs))
+				log_message(LOG_INFO, "Regex success to [%s]:%d url(%d)."
+				       , inet_sockaddrtos(&http_get_check->dst)
+				       , ntohs(inet_sockaddrport(&http_get_check->dst))
+				       , http_arg->url_it + 1);
+			return epilog(thread, 1, 1, 0) + 1;
+		}
+	}
+
 	return epilog(thread, 1, 0, 0) + 1;
 }
 
@@ -492,6 +569,9 @@ http_process_response(request_t *req, int r)
 			req->status_code = extract_status_code(req->buffer, req->len);
 			r = req->len - (req->extracted - req->buffer);
 			if (r) {
+				req->buffer_regex = (char *) MALLOC(MAX_BUFFER_LENGTH+1);
+				memmove(req->buffer_regex, req->extracted, r);
+				req->buffer_regex_len = r;
 				memmove(req->buffer, req->extracted, r);
 				MD5_Update(&req->context, req->buffer, r);
 				r = 0;
@@ -499,6 +579,11 @@ http_process_response(request_t *req, int r)
 			req->len = r;
 		}
 	} else if (req->len) {
+		if (req->buffer_regex_len < MAX_BUFFER_LENGTH) {
+			size_t to_copy = (req->buffer_regex_len + req->len < MAX_BUFFER_LENGTH ) ? \
req->len : MAX_BUFFER_LENGTH - req->buffer_regex_len; +			memmove(req->buffer_regex + \
req->buffer_regex_len, req->buffer, to_copy); +			req->buffer_regex_len += to_copy;
+		}
 		MD5_Update(&req->context, req->buffer,
 			   req->len);
 		req->len = 0;
@@ -607,6 +692,8 @@ http_response_thread(thread_t * thread)
 
 	/* Allocate & clean the get buffer */
 	req->buffer = (char *) MALLOC(MAX_BUFFER_LENGTH);
+	req->buffer_regex = NULL;
+	req->buffer_regex_len = 0;
 	req->extracted = NULL;
 	req->len = 0;
 	req->error = 0;
diff --git a/keepalived/include/check_http.h b/keepalived/include/check_http.h
index 221bb91..92a95e4 100644
--- a/keepalived/include/check_http.h
+++ b/keepalived/include/check_http.h
@@ -28,6 +28,7 @@
 #include <stdio.h>
 #include <openssl/md5.h>
 #include <openssl/ssl.h>
+#include <unicode/uregex.h>
 
 /* local includes */
 #include "check_data.h"
@@ -40,6 +41,8 @@
 /* ssl specific thread arguments defs */
 typedef struct _request {
 	char *buffer;
+	char *buffer_regex;
+	size_t buffer_regex_len;
 	char *extracted;
 	int error;
 	int status_code;
@@ -59,6 +62,8 @@ typedef struct _http_arg {
 typedef struct _url {
 	char *path;
 	char *digest;
+	char *regex_pattern;
+	URegularExpression *regexp;
 	int status_code;
 } url_t;
 
-- 
1.7.6.5


["0002-Document-new-option-regex.patch" (text/x-diff)]

>From b3402f53beb6733f484509137fe6e0689ef805ed Mon Sep 17 00:00:00 2001
From: Alexander Holler <alexander.holler@1und1.de>
Date: Mon, 12 Dec 2011 10:27:50 +0100
Subject: [PATCH 2/3] Document new option regex.

---
 doc/keepalived.conf.SYNOPSIS   |    3 +++
 doc/man/man5/keepalived.conf.5 |    5 ++++-
 2 files changed, 7 insertions(+), 1 deletions(-)

diff --git a/doc/keepalived.conf.SYNOPSIS b/doc/keepalived.conf.SYNOPSIS
index ddbc1c1..18d5966 100644
--- a/doc/keepalived.conf.SYNOPSIS
+++ b/doc/keepalived.conf.SYNOPSIS
@@ -332,11 +332,14 @@ virtual_server group <STRING>      {	# VS group declaration
             url {			# A set of url to test
               path <STRING>		# Path
               digest <STRING>		# Digest computed with genhash
+              regex <STRING>            # Regular expression searched for in
+                                        # the first 4000 bytes of the response
               status_code <INTEGER>	# status code returned into the HTTP
             }                           #   header.
             url {
               path <STRING>
               digest <STRING>
+              regex <STRING>
               status_code <INTEGER>
             }
             ...
diff --git a/doc/man/man5/keepalived.conf.5 b/doc/man/man5/keepalived.conf.5
index b8db9fe..f1d727e 100644
--- a/doc/man/man5/keepalived.conf.5
+++ b/doc/man/man5/keepalived.conf.5
@@ -396,10 +396,13 @@ A virtual_server can be a declaration of one of
                  #eg path / , or path /mrtg2/

                  path <STRING> 

                  # healthcheck needs status_code

-                 # or status_code and digest

+                 # or status_code and digest or regex

                  # Digest computed with genhash

                  # eg digest 9b3a0c85a887a256d6939da88aabd8cd

                  digest <STRING>

+                 # Regular expression which is searched in the first

+                 # 4000 bytes of the response

+                 regex <STRING>

                  # status code returned in the HTTP header

                  # eg status_code 200

                  status_code <INT>     

-- 
1.7.6.5


["0003-Add-libicuuc-as-requirement.patch" (text/x-diff)]

>From 6b800da9ccbc57e313b978f9f530134200af7e43 Mon Sep 17 00:00:00 2001
From: Alexander Holler <alexander.holler@1und1.de>
Date: Wed, 4 Jan 2012 13:57:59 +0100
Subject: [PATCH 3/3] Add libicuuc as requirement

---
 configure.in |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/configure.in b/configure.in
index 9c3624c..27badfe 100644
--- a/configure.in
+++ b/configure.in
@@ -54,6 +54,7 @@ dnl ----[ Checks for libraries ]----
 AC_CHECK_LIB(crypto, MD5_Init,,AC_MSG_ERROR([OpenSSL libraries are required]))
 AC_CHECK_LIB(ssl, SSL_CTX_new,,AC_MSG_ERROR([OpenSSL libraries are required]))
 AC_CHECK_LIB(icui18n, uregex_openC_44,,AC_MSG_ERROR([ICU libraries are required]))
+AC_CHECK_LIB(icuuc, u_uastrncpy_44,,AC_MSG_ERROR([ICU libraries are required]))
 AC_CHECK_LIB(popt, poptGetContext,,AC_MSG_ERROR([Popt libraries is required]))
 AC_CHECK_LIB(nl, nl_handle_alloc,
   [
-- 
1.7.6.5



------------------------------------------------------------------------------
Virtualization & Cloud Management Using Capacity Planning
Cloud computing makes use of virtualization - but cloud computing 
also focuses on allowing computing to be delivered as a service.
http://www.accelacomm.com/jaw/sfnl/114/51521223/

_______________________________________________
Keepalived-devel mailing list
Keepalived-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/keepalived-devel


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic