diff options
author | Michael Larson <mike@vyatta.com> | 2011-03-30 12:09:58 -0700 |
---|---|---|
committer | Michael Larson <mike@vyatta.com> | 2011-03-30 12:09:58 -0700 |
commit | 25eef8bfb91c2c57907ab79f4ba6bcd9fe0064cb (patch) | |
tree | 53f6756f29c645fcec455bfec6a1bf8bdb53e50d | |
parent | 55cab2fbbf2ee75d6939a9210ddcaa5c72ed6981 (diff) | |
download | vyatta-wanloadbalance-25eef8bfb91c2c57907ab79f4ba6bcd9fe0064cb.tar.gz vyatta-wanloadbalance-25eef8bfb91c2c57907ab79f4ba6bcd9fe0064cb.zip |
check in for Bug 6959
Add the ability to run a user defined script to determine wan load-balancing interface health
includes sample http test (scripts/http_test.pl). Configuration will look as follows:
wan {
interface-health eth0 {
nexthop 10.3.0.1
test 1 {
test-script /tmp/http_test.pl
type user-defined
}
}
interface-health eth1 {
nexthop 10.3.0.1
test 1 {
test-script /tmp/http_test.pl
type user-defined
}
}
With corresponding output:
vyatta@vyatta# run show wan-load-balance
Interface: eth0
Status: active
Last Status Change: Wed Mar 30 19:03:59 2011
+Test: user Script: /tmp/http_test.pl
Last Interface Success: 0s
Last Interface Failure: n/a
# Interface Failure(s): 0
Interface: eth1
Status: failed
Last Status Change: Wed Mar 30 19:03:59 2011
-Test: user Script: /tmp/http_test.pl
Last Interface Success: n/a
Last Interface Failure: 0s
# Interface Failure(s): 52
Scripts returns 0 for success, non-zero for failure.
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | scripts/http_test.pl | 45 | ||||
-rw-r--r-- | scripts/vyatta-wanloadbalance.pl | 11 | ||||
-rw-r--r-- | src/lbdata.cc | 2 | ||||
-rw-r--r-- | src/lbdatafactory.cc | 21 | ||||
-rw-r--r-- | src/lbdatafactory.hh | 3 | ||||
-rw-r--r-- | src/lboutput.cc | 8 | ||||
-rw-r--r-- | src/lbtest.hh | 7 | ||||
-rw-r--r-- | src/lbtest_icmp.cc | 10 | ||||
-rw-r--r-- | src/lbtest_icmp.hh | 9 | ||||
-rw-r--r-- | src/lbtest_ttl.cc | 15 | ||||
-rw-r--r-- | src/lbtest_ttl.hh | 2 | ||||
-rw-r--r-- | src/lbtest_user.cc | 146 | ||||
-rw-r--r-- | src/lbtest_user.hh | 67 | ||||
-rw-r--r-- | templates/load-balancing/wan/interface-health/node.tag/test/node.tag/test-script/node.def | 3 | ||||
-rw-r--r-- | templates/load-balancing/wan/interface-health/node.tag/test/node.tag/type/node.def | 5 |
16 files changed, 327 insertions, 28 deletions
diff --git a/Makefile.am b/Makefile.am index df689a4..25321b8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -32,6 +32,7 @@ src_wan_lb_SOURCES += src/rl_str_proc.cc src_wan_lb_SOURCES += src/lbpathtest.cc src_wan_lb_SOURCES += src/lbtest_icmp.cc src_wan_lb_SOURCES += src/lbtest_ttl.cc +src_wan_lb_SOURCES += src/lbtest_user.cc src_wan_lb_SOURCES += src/lbdecision.cc src_wan_lb_SOURCES += src/lboutput.cc src_wan_lb_SOURCES += src/lbtest.cc diff --git a/scripts/http_test.pl b/scripts/http_test.pl new file mode 100644 index 0000000..68f8c97 --- /dev/null +++ b/scripts/http_test.pl @@ -0,0 +1,45 @@ +#!/usr/bin/perl +# +# Module: http_test.pl +# This code was originally developed by Vyatta, Inc. +# Portions created by Vyatta are Copyright (C) 2009, 2010 Vyatta, Inc. +# All Rights Reserved. +# Authors: Michael Larson +# Date: 2011 +# Description: wanloadbalance script sample. +# Tests whether google.com responds to simple http GET request. +# + +use strict; +use warnings; +use POSIX; +use JSON; +use Data::Dumper; +use URI::Escape; + + +my $iface = $ENV{WLB_SCRIPT_IFACE}; + +my $code; +my $body; + +#let's see if we can reach google out this interface +my @out = `curl -s -m 3 --interface $iface -i -X GET www.google.com`; +#now process output, for http status code and for response body +foreach my $out (@out) { + if ($out =~ /^HTTP\/[\d.]+\s+(\d+)\s+.*$/) { + $code = $1; + } + elsif ($out =~ /^\r/ || defined $body) { + $body .= $out; + } +} + +#success is if http response code is 200 and body is returned +if (defined($code) && $code == 200 && defined($body)) { + #WLB SUCCESS + exit(0); +} + +#WLB FAILURE +exit(1); diff --git a/scripts/vyatta-wanloadbalance.pl b/scripts/vyatta-wanloadbalance.pl index ef8165f..dde7387 100644 --- a/scripts/vyatta-wanloadbalance.pl +++ b/scripts/vyatta-wanloadbalance.pl @@ -85,6 +85,17 @@ sub write_health { print FILE_LCK "\t\t\t\tttl $ttl_limit\n"; } } + elsif (defined($test_type) && ($test_type eq "user-defined")) { + print FILE_LCK "\t\t\ttype user-defined {\n"; + my $test_script = $config->returnValue("$rule test-script"); + if (defined $test_script) { + print FILE_LCK "\t\t\t\ttest-script $test_script\n"; + } + else { + print "ERROR: script must be defined for test-script\n"; + exit 1; + } + } else { print FILE_LCK "\t\t\ttype ping {\n"; } diff --git a/src/lbdata.cc b/src/lbdata.cc index 1ef1b73..39deaa6 100644 --- a/src/lbdata.cc +++ b/src/lbdata.cc @@ -107,7 +107,7 @@ LBHealth::recv_test() put(-1); return 0; //means stop iteration, no more tests... } - int rtt = _test_iter->second->recv(*this); + int rtt = _test_iter->second->recv(*this); ++_test_iter; if (rtt >= 0) { put(rtt); diff --git a/src/lbdatafactory.cc b/src/lbdatafactory.cc index a9030b3..653e270 100644 --- a/src/lbdatafactory.cc +++ b/src/lbdatafactory.cc @@ -19,6 +19,7 @@ #include "lbdata.hh" #include "lbtest_icmp.hh" #include "lbtest_ttl.hh" +#include "lbtest_user.hh" #include "lbdatafactory.hh" using namespace std; @@ -173,6 +174,9 @@ LBDataFactory::process(const vector<string> &path, int depth, const string &key, else if (depth == 4 && key == "ttl") { process_health_interface_rule_type_udp(l_key,l_value); } + else if (depth == 4 && key == "test-script") { + process_health_interface_rule_type_user(l_key,l_value); + } } else if (path[0] == "rule") { if (depth > 0 && path[1] == "source") { @@ -320,12 +324,19 @@ LBDataFactory::process_health_interface_rule_type_udp(const string &key, const s } void +LBDataFactory::process_health_interface_rule_type_user(const string &key, const string &value) +{ + if (_test_iter != _health_iter->second._test_coll.end()) { + if (key == "test-script") { + ((LBTestUser*)_test_iter->second)->set_script((string&)value); + } + } +} + +void LBDataFactory::process_health_interface_rule_type(const string &key, const string &value) { if (value == "ping") { - if (_debug) { - cout << "LBDataFactory::process_health_interface_rule_type(): setting up icmp test" << endl; - } LBTestICMP *test = new LBTestICMP(_debug); _health_iter->second._test_coll.insert(pair<int,LBTest*>(_current_test_rule_number,test)); } @@ -333,6 +344,10 @@ LBDataFactory::process_health_interface_rule_type(const string &key, const strin LBTestTTL *test = new LBTestTTL(_debug); _health_iter->second._test_coll.insert(pair<int,LBTest*>(_current_test_rule_number,test)); } + else if (value == "user-defined") { + LBTestUser *test = new LBTestUser(_debug); + _health_iter->second._test_coll.insert(pair<int,LBTest*>(_current_test_rule_number,test)); + } _test_iter = _health_iter->second._test_coll.find(_current_test_rule_number); } diff --git a/src/lbdatafactory.hh b/src/lbdatafactory.hh index 358ec8d..cfa62ee 100644 --- a/src/lbdatafactory.hh +++ b/src/lbdatafactory.hh @@ -74,6 +74,9 @@ private: process_health_interface_rule_type_udp(const string &key, const string &value); void + process_health_interface_rule_type_user(const string &key, const string &value); + + void process_rule(const string &key, const string &value); void diff --git a/src/lboutput.cc b/src/lboutput.cc index a3d1c6a..31829e9 100644 --- a/src/lboutput.cc +++ b/src/lboutput.cc @@ -81,15 +81,17 @@ LBOutput::write(const LBData &lbdata) } } + string status; if (titer->second->_state == LBTest::K_NONE) { - line += space + "*Test: " + titer->second->name() + "\t" + string("Target: ") + target + "\n"; + status = "*"; } else if (titer->second->_state == LBTest::K_FAILURE) { - line += space + "-Test: " + titer->second->name() + "\t" + string("Target: ") + target + "\n"; + status = "-"; } else if (titer->second->_state == LBTest::K_SUCCESS) { - line += space + "+Test: " + titer->second->name() + "\t" + string("Target: ") + target + "\n"; + status = "+"; } + line += space + status + "Test: " + titer->second->status() + "\n"; ++titer; } diff --git a/src/lbtest.hh b/src/lbtest.hh index 208adaa..4463315 100644 --- a/src/lbtest.hh +++ b/src/lbtest.hh @@ -59,12 +59,15 @@ public: virtual string dump() = 0; - int + virtual int recv(LBHealth &health); virtual string name() = 0; + virtual string + status() {return _status_line;} + private: int receive(int recv_sock); @@ -75,6 +78,8 @@ public: string _target; int _resp_time; int _state; + string _status_line; + static int _recv_icmp_sock; static int _send_raw_sock; diff --git a/src/lbtest_icmp.cc b/src/lbtest_icmp.cc index 6393b3e..d887d53 100644 --- a/src/lbtest_icmp.cc +++ b/src/lbtest_icmp.cc @@ -49,11 +49,17 @@ LBTestICMP::send(LBHealth &health) } } + //set the status line to be used when the show command is invoked + _status_line = name() + "\t" + string("Target: ") + target; + //don't have target yet... if (target.empty()) { return; } - _packet_id = ++_packet_id % 32767; + + //instead of prefix to avoid this compilier warning: + //warning: operation on ‘LBTest::_packet_id’ may be undefined + _packet_id = int((_packet_id + 1) % int(32767)); if (_debug) { cout << "ICMPEngine::start(): sending ping test for: " << health._interface << " for " << target << " id: " << _packet_id << endl; } @@ -195,3 +201,5 @@ LBTestICMP::dump() sprintf(buf,"%u",_resp_time); return (string("target: ") + _target + ", resp_time: " + buf); } + + diff --git a/src/lbtest_icmp.hh b/src/lbtest_icmp.hh index e662c83..d2148d6 100644 --- a/src/lbtest_icmp.hh +++ b/src/lbtest_icmp.hh @@ -27,7 +27,7 @@ public: ~LBTestICMP() {} void - init() {} + init() {_status_line=name();} void send(LBHealth &health); @@ -38,14 +38,15 @@ public: string name() {return string("ping");} + string + status() {return _status_line;} + private: void send(int sock, const string &iface, const string &target_addr, int packet_id); unsigned short - in_checksum(const unsigned short *buf, int lenght) const; - -private: + in_checksum(const unsigned short *buf, int length) const; }; #endif //__LBTEST_ICMP_HH__ diff --git a/src/lbtest_ttl.cc b/src/lbtest_ttl.cc index 551db07..d3a0d26 100644 --- a/src/lbtest_ttl.cc +++ b/src/lbtest_ttl.cc @@ -37,18 +37,6 @@ using namespace std; * **/ void -LBTestTTL::init() -{ - if (_debug) { - cout << "LBTestTTL::init(): initializing test system" << endl; - } -} - -/** - * - * - **/ -void LBTestTTL::send(LBHealth &health) { if (_debug) { @@ -65,6 +53,9 @@ LBTestTTL::send(LBHealth &health) } } + //set the status line to be used when the show command is invoked + _status_line = name() + "\t" + string("Target: ") + target; + //don't have target yet... if (target.empty()) { return; diff --git a/src/lbtest_ttl.hh b/src/lbtest_ttl.hh index d3abe5e..5d49dc2 100644 --- a/src/lbtest_ttl.hh +++ b/src/lbtest_ttl.hh @@ -40,7 +40,7 @@ public: ~LBTestTTL() {} void - init(); + init() {_status_line=name();} void send(LBHealth &health); diff --git a/src/lbtest_user.cc b/src/lbtest_user.cc new file mode 100644 index 0000000..4dbed74 --- /dev/null +++ b/src/lbtest_user.cc @@ -0,0 +1,146 @@ +/* + * Module: lbtest_user.cc + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ +#include <syslog.h> +#include <stdio.h> +#include <stdlib.h> +#include <iostream> +#include <string> +#include <algorithm> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include "lbdata.hh" +#include "lbtest_user.hh" + +using namespace std; + +/** + * + * + **/ +void +LBTestUser::send(LBHealth &health) +{ + if (_debug) { + cout << "LBTestUser::process()" << endl; + } + + _status_line = name() + "\t" + string("Script: ") + _script; + + + //need to set environment variable for interface + + //execute script + string environment = "export WLB_SCRIPT_IFACE=" + health._interface; + string cmd = "/bin/bash -p -c '" + environment + ";umask 000; " + _script + ";'"; + string out; + if (system_out(cmd,out) == 0) { + _state = LBTest::K_SUCCESS; + } + else { + _state = LBTest::K_FAILURE; + } + return; +} + +int +LBTestUser::system_out(const string &cmd, string &out) +{ + // fprintf(out_stream,"system out\n"); + if (cmd.empty()) { + return -1; + } + + int cp[2]; // Child to parent pipe + if( pipe(cp) < 0) { + return -1; + } + + pid_t pid = fork(); + if (pid == 0) { + //child + close(cp[0]); + close(0); // Close current stdin. + dup2(cp[1],STDOUT_FILENO); // Make stdout go to write end of pipe. + dup2(cp[1],STDERR_FILENO); // Make stderr go to write end of pipe. + // fcntl(cp[1],F_SETFD,fcntl(cp[1],F_GETFD) & (~FD_CLOEXEC)); + close(cp[1]); + int ret = 0; + + + //set user and group here + setregid(getegid(),getegid()); + setreuid(geteuid(),geteuid()); + + // fprintf(out_stream,"executing: %s\n",cmd); + if (execl("/bin/sh","sh","-c",cmd.c_str(),NULL) == -1) { + ret = errno; + } + close(cp[1]); + exit(ret); + } + else { + //parent + char buf[8192]; + memset(buf,'\0',8192); + close(cp[1]); + fd_set rfds; + struct timeval tv; + + int flags = fcntl(cp[0], F_GETFL, 0); + fcntl(cp[0], F_SETFL, flags | O_NONBLOCK); + + FD_ZERO(&rfds); + FD_SET(cp[0], &rfds); + tv.tv_sec = 1; + tv.tv_usec = 0; + while (select(FD_SETSIZE, &rfds, NULL, NULL, &tv) != -1) { + int err = 0; + if ((err = read(cp[0], &buf, 8191)) > 0) { + out += string(buf); + } + else if (err == -1 && errno == EAGAIN) { + //try again + } + else { + break; + } + FD_ZERO(&rfds); + FD_SET(cp[0], &rfds); + tv.tv_sec = 1; + tv.tv_usec = 0; + + fflush(NULL); + } + + //now wait on child to kick the bucket + int status; + wait(&status); + close(cp[0]); + return WEXITSTATUS(status); + } +} + + +/** + * + * + **/ +string +LBTestUser::dump() +{ + char buf[20]; + sprintf(buf,"%u",_resp_time); + string foo = string("script: ") + _script + ", resp_time: " + buf; + return foo; +} diff --git a/src/lbtest_user.hh b/src/lbtest_user.hh new file mode 100644 index 0000000..534d4d0 --- /dev/null +++ b/src/lbtest_user.hh @@ -0,0 +1,67 @@ +/* + * Module: lbtest_user.hh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ +#ifndef __LBTEST_USER_HH__ +#define __LBTEST_USER_HH__ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <iostream> +#include "lbtest.hh" + +using namespace std; + +/** + * + * + **/ +class LBTestUser : public LBTest +{ +public: + LBTestUser(bool debug) : + LBTest(debug) + {} + LBTestUser(bool debug, string &script) : + LBTest(debug), + _script(script) + {} + ~LBTestUser() {} + + void + send(LBHealth &health); + + std::string + get_script() const {return _script;} + + void + set_script(std::string &script) {_script = script;} + + string + dump(); + + string + name() {return string("user");} + + //override, don't need base support for these. + void + init() {_status_line=name();} + void + start() {} + int + recv(LBHealth &health) {return (_state != LBTest::K_SUCCESS) ? -1 : 1;} + + +private: //methods + int + system_out(const string &cmd, string &out); + +private: //variables + string _script; +}; + +#endif //__LBTEST_USER_HH__ diff --git a/templates/load-balancing/wan/interface-health/node.tag/test/node.tag/test-script/node.def b/templates/load-balancing/wan/interface-health/node.tag/test/node.tag/test-script/node.def new file mode 100644 index 0000000..05e68cb --- /dev/null +++ b/templates/load-balancing/wan/interface-health/node.tag/test/node.tag/test-script/node.def @@ -0,0 +1,3 @@ +type: txt +help: Path to user defined script + diff --git a/templates/load-balancing/wan/interface-health/node.tag/test/node.tag/type/node.def b/templates/load-balancing/wan/interface-health/node.tag/test/node.tag/type/node.def index 95dc6ca..5775b6b 100644 --- a/templates/load-balancing/wan/interface-health/node.tag/test/node.tag/type/node.def +++ b/templates/load-balancing/wan/interface-health/node.tag/test/node.tag/type/node.def @@ -2,8 +2,9 @@ type: txt help: WLB test type -syntax:expression: $VAR(@) in "ping", "ttl"; - "action must be either ping or ttl expired test" +syntax:expression: $VAR(@) in "ping", "ttl", "user-defined"; + "action must be either ping, ttl expired or user defined test" val_help: ping; Test with icmp echo response val_help: ttl; Test with udp TTL expired response +val_help: user-defined; User defined test script |