added two more options to pingchecker: use random interval per host in given range...
authorChristian Herdtweck <christian.herdtweck@intra2net.com>
Wed, 14 May 2014 08:32:10 +0000 (10:32 +0200)
committerChristian Herdtweck <christian.herdtweck@intra2net.com>
Wed, 14 May 2014 08:32:10 +0000 (10:32 +0200)
src/CMakeLists.txt
src/config/configuration.cpp
src/config/configuration.h
src/config/configurationoptions.cpp
src/config/option/hostpingintervaloption.cpp
src/config/option/ratiorandomhostsoption.cpp [new file with mode: 0644]
src/config/option/ratiorandomhostsoption.h [new file with mode: 0644]
src/main.cpp
test/CMakeLists.test_configurationcommandline.txt
test/CMakeLists.test_configurationfile.txt
test/CMakeLists.test_configurationoptions.txt

index 4ecf48e..8d67551 100644 (file)
@@ -57,6 +57,7 @@ set(SOURCES
     config/option/sourcenetworkinterfaceoption.cpp
     config/option/statusnotifiercmdoption.cpp
     config/option/versionoption.cpp
+    config/option/ratiorandomhostsoption.cpp
     dns/dnsresolver.cpp
     dns/dnsresolverfactory.cpp
     dns/hostaddress.cpp
index fb5a12a..2c29eeb 100644 (file)
@@ -19,10 +19,21 @@ on this file might be covered by the GNU General Public License.
 */
 #include "config/configuration.h"
 
+#include <set>
+#include <ctime>   // for seeding random number generator
 #include <boost/assert.hpp>
+#include <boost/math/special_functions/round.hpp>
+#include <boost/numeric/conversion/cast.hpp>
+#include <boost/foreach.hpp>
+#include <boost/random/uniform_int.hpp>
+#include <boost/random/variate_generator.hpp>
 
 using namespace std;
 using I2n::Logger::LogLevel;
+using I2n::Logger::GlobalLogger;
+
+typedef boost::uniform_int<> rand_dist_type;
+typedef boost::variate_generator<rand_gen_type&, rand_dist_type> rand_var_type;
 
 //-----------------------------------------------------------------------------
 // Configuration
@@ -46,7 +57,9 @@ Configuration::Configuration() :
     LinkDownIntervalInMin( 0 ),
     MinStableLinkIntervalInMin( 0 ),
     MaxStableLinkIntervalInMin( 60 ),
-    Hosts()
+    Hosts(),
+    RatioRandomHosts( 1.0f ),
+    RandomNumberGenerator( boost::numeric_cast<unsigned int>(time(0)) )
 {
 }
 
@@ -186,6 +199,17 @@ void Configuration::set_link_down_interval_in_min(
     LinkDownIntervalInMin = link_down_interval_in_min;
 }
 
+float Configuration::get_ratio_random_hosts() const
+{
+    return RatioRandomHosts;
+}
+
+void Configuration::set_ratio_random_hosts( const float ratio )
+{
+    BOOST_ASSERT( (ratio > 0.0f) && (ratio <= 1.0f) );
+    this->RatioRandomHosts = ratio;
+}
+
 HostList Configuration::get_hosts() const
 {
     return Hosts;
@@ -196,3 +220,61 @@ void Configuration::set_hosts( const HostList &hosts_list )
     this->Hosts = hosts_list;
 }
 
+int Configuration::get_random_number(const int lowest, const int highest)
+{
+    rand_dist_type random_distribution(lowest, highest);
+    rand_var_type random_variate(RandomNumberGenerator, random_distribution);
+    return random_variate();
+}
+
+/**
+ * @brief select RatioRandomHosts of the hosts at random.
+ * changes internal list of hosts
+ * @returns false if could not randomize (e.g. only 1 host
+ *   available or ratio=1)
+ */
+bool Configuration::randomize_hosts()
+{
+    // need this a few times
+    unsigned int n_hosts = Hosts.size();
+    float n_hosts_float = boost::numeric_cast<float>(n_hosts);
+
+    // check if RatioRandomHosts == 1
+    if ((1. - RatioRandomHosts) < 0.5/n_hosts_float)
+        return false;
+
+    // determine number of hosts to keep
+    unsigned int n_wanted = boost::math::iround(n_hosts_float * RatioRandomHosts);
+    if (n_wanted == 0)
+        n_wanted = 1; // want at least 1 host
+    if (n_wanted == n_hosts)   // e.g. always the case if only 1 host given
+        return false;
+
+    GlobalLogger.info() << "randomizing hosts: keeping "
+                << n_wanted << " from overall " << n_hosts;
+
+    BOOST_ASSERT(n_wanted <= n_hosts);   // just to be sure
+
+    // create new set of hosts (more efficient than removal from std::vector Hosts)
+    std::set<HostItem> new_hosts;
+    int take_idx;
+
+    // add hosts at random until size is good
+    rand_dist_type random_distribution(0, n_hosts-1);
+    rand_var_type random_variate(RandomNumberGenerator, random_distribution);
+    while (new_hosts.size() < n_wanted)
+    {
+        take_idx = random_variate();
+        new_hosts.insert( Hosts[take_idx] ); // does nothing if host is already in there
+    }
+
+    // convert from set to list
+    HostList new_list;
+    BOOST_FOREACH(HostItem host, new_hosts)
+        new_list.push_back(host);
+
+    this->Hosts = new_list;
+
+    return true;
+}
+
index bc837c6..a041ce5 100644 (file)
@@ -28,6 +28,7 @@ on this file might be covered by the GNU General Public License.
 #include <logfunc.hpp>
 
 #include <boost/shared_ptr.hpp>
+#include <boost/random/linear_congruential.hpp>
 
 #include "config/host.h"
 #include "host/loglevel.h"
@@ -37,6 +38,8 @@ on this file might be covered by the GNU General Public License.
 // Configuration
 //-----------------------------------------------------------------------------
 
+typedef boost::rand48 rand_gen_type;
+
 /**
  * @brief This class works like a POD (Plain Old Data) for configuration options.
  */
@@ -81,9 +84,16 @@ public:
     int get_link_down_interval_in_min() const;
     void set_link_down_interval_in_min( const int link_down_interval_in_min );
 
+    float get_ratio_random_hosts() const;
+    void set_ratio_random_hosts( const float ratio );
+
     HostList get_hosts() const;
     void set_hosts( const HostList &hosts_list );
 
+    bool randomize_hosts();
+
+    int get_random_number(const int lowest, const int highest);
+
 private:
     bool Daemon;
     I2n::Logger::LogLevel LoggingLevel;
@@ -102,8 +112,9 @@ private:
     int LinkDownIntervalInMin;
     int MinStableLinkIntervalInMin;
     int MaxStableLinkIntervalInMin;
+    float RatioRandomHosts;
     HostList Hosts;
-
+    rand_gen_type RandomNumberGenerator;
 };
 
 //-----------------------------------------------------------------------------
index ee4bccc..4377700 100644 (file)
@@ -44,6 +44,7 @@
 #include "config/option/sourcenetworkinterfaceoption.h"
 #include "config/option/statusnotifiercmdoption.h"
 #include "config/option/versionoption.h"
+#include "config/option/ratiorandomhostsoption.h"
 
 using namespace std;
 using boost::program_options::option_description;
@@ -103,6 +104,9 @@ ConfigurationOptions::ConfigurationOptions() :
     ConfigurationOptionItem status_notifier_cmd( new StatusNotifierCmdOption );
     ConfigOptions.push_back( status_notifier_cmd );
 
+    ConfigurationOptionItem ratio_random_hosts( new RatioRandomHostsOption );
+    ConfigOptions.push_back( ratio_random_hosts );
+
     HostConfigurationOptionItem host_name( new HostNameOption );
     HostOptions.push_back( host_name );
 
index 386726c..b16a79d 100644 (file)
@@ -24,6 +24,9 @@
 
 #include <boost/assert.hpp>
 #include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <stringfunc.hxx>   // I2n::trim
 
 #include <logfunc.hpp>
 
@@ -36,10 +39,13 @@ using I2n::Logger::GlobalLogger;
 // HostPingIntervalOption
 //-----------------------------------------------------------------------------
 
+const std::string range_separator = "...";
+const int failure_interval = 60;
+
 HostPingIntervalOption::HostPingIntervalOption() :
     HostConfigurationOption(
         "host.interval",
-        value< vector<int> >(),//->default_value( 60 ), // 60 seconds
+        value< vector<std::string> >(),
         "Interval between each ping to the host."
     )
 {
@@ -57,6 +63,11 @@ bool HostPingIntervalOption::parse(
     size_t hosts_count = 0;
     size_t hosts_intervals_count = 0;
     bool parsed_success = false;
+    size_t range_separator_pos = 0;
+    std::string interval_part;
+    int host_interval = 0;
+    int host_interval_lower = 0;
+    int host_interval_upper = 0;
 
     // [host] interval
     if ( 1 <= vm.count( get_command_string() ) )
@@ -65,24 +76,64 @@ bool HostPingIntervalOption::parse(
         HostList::iterator hosts_list_iterator = hosts_list.begin();
         hosts_count = hosts_list.size();
 
-        vector<int> hosts_intervals_list = vm[ get_command_string() ].as< vector<int> >();
+        vector<std::string> hosts_intervals_list =
+                            vm[ get_command_string() ].as< vector<std::string> >();
         hosts_intervals_count = hosts_intervals_list.size();
 
         BOOST_ASSERT( hosts_count >= hosts_intervals_count );
 
-        BOOST_FOREACH( int host_interval_in_sec, hosts_intervals_list )
-        {
-            BOOST_ASSERT( 0 < host_interval_in_sec );
+        parsed_success = true;
 
+        BOOST_FOREACH( std::string host_interval_str, hosts_intervals_list )
+        {
             HostItem host_item = *hosts_list_iterator;
-            host_item->set_interval_in_sec( host_interval_in_sec );
-            ++hosts_list_iterator;
 
-            GlobalLogger.info() << get_command_string() << "="
-                    << host_interval_in_sec << endl;
-        }
+            // look for range_separator
+            host_interval_str = I2n::trim(host_interval_str);
+            range_separator_pos = host_interval_str.find(range_separator);
+
+            try
+            {
+                if (range_separator_pos == std::string::npos)
+                {
+                    // no separator --> assume a fixed interval as single number
+                    host_interval = boost::lexical_cast<int>(host_interval_str);
+                    BOOST_ASSERT( 0 < host_interval );
+                    GlobalLogger.info() << get_command_string() << "="
+                            << host_interval << endl;
+                }
+                else
+                {
+                    // found separator --> chose interval value from interval
+                    interval_part = I2n::trim(host_interval_str.substr(0, range_separator_pos));
+                    host_interval_lower = boost::lexical_cast<int>(interval_part);
+                    interval_part = I2n::trim(host_interval_str.substr(range_separator_pos+3));
+                    host_interval_upper = boost::lexical_cast<int>(interval_part);
+
+                    BOOST_ASSERT( (0 < host_interval_lower) &&
+                            (host_interval_lower <= host_interval_upper) );
+
+                    // create random number within this interval
+                    host_interval = configuration->get_random_number(host_interval_lower,
+                                                                     host_interval_upper);
+
+                    GlobalLogger.info() << get_command_string() << "=" << host_interval
+                        << " from range " << host_interval_lower << "..." << host_interval_upper
+                        << endl;
+                }
+                host_item->set_interval_in_sec( host_interval );
+            }
+
+            catch (boost::bad_lexical_cast &exc)
+            {
+                GlobalLogger.error() << "Error interpreting host ping interval: could not interprete \""
+                    << host_interval_str << "\"! Set interval to 60s";
+                host_item->set_interval_in_sec( failure_interval );
+                parsed_success = false;
+            }
 
-        parsed_success = true;
+            ++hosts_list_iterator;
+        } //eo foreach host interval
     }
 
     set_hosts_count( hosts_intervals_count );
diff --git a/src/config/option/ratiorandomhostsoption.cpp b/src/config/option/ratiorandomhostsoption.cpp
new file mode 100644 (file)
index 0000000..2897484
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ The software in this package is distributed under the GNU General
+ Public License version 2 (with a special exception described below).
+
+ A copy of GNU General Public License (GPL) is included in this distribution,
+ in the file COPYING.GPL.
+
+ As a special exception, if other files instantiate templates or use macros
+ or inline functions from this file, or you compile this file and link it
+ with other works to produce a work based on this file, this file
+ does not by itself cause the resulting work to be covered
+ by the GNU General Public License.
+
+ However the source code for this file must still be made available
+ in accordance with section (3) of the GNU General Public License.
+
+ This exception does not invalidate any other reasons why a work based
+ on this file might be covered by the GNU General Public License.
+ */
+
+#include "config/option/ratiorandomhostsoption.h"
+
+#include <logfunc.hpp>
+
+using namespace std;
+using boost::program_options::value;
+using boost::program_options::variables_map;
+using I2n::Logger::GlobalLogger;
+
+//-----------------------------------------------------------------------------
+// RatioRandomHostsOption
+//-----------------------------------------------------------------------------
+
+RatioRandomHostsOption::RatioRandomHostsOption() :
+    ConfigurationOption(
+        "ratio-random-hosts",
+        value<float>()->default_value( 1.0f ),
+        "Ratio in (0,1] of hosts to use, picking them at random from host list"
+    )
+{
+}
+
+RatioRandomHostsOption::~RatioRandomHostsOption()
+{
+}
+
+bool RatioRandomHostsOption::parse(
+        const variables_map& vm,
+        Configuration *configuration
+)
+{
+    // default-source-network-interface
+    if ( 1 <= vm.count( get_command_string() ) )
+    {
+        float ratio_random_hosts = vm[ get_command_string() ].as<float> ();
+        configuration->set_ratio_random_hosts( ratio_random_hosts );
+
+        GlobalLogger.info() << get_command_string() << "="
+                << ratio_random_hosts << endl;
+
+        return true;
+    }
+
+    return false;
+}
diff --git a/src/config/option/ratiorandomhostsoption.h b/src/config/option/ratiorandomhostsoption.h
new file mode 100644 (file)
index 0000000..793e4c8
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ The software in this package is distributed under the GNU General
+ Public License version 2 (with a special exception described below).
+
+ A copy of GNU General Public License (GPL) is included in this distribution,
+ in the file COPYING.GPL.
+
+ As a special exception, if other files instantiate templates or use macros
+ or inline functions from this file, or you compile this file and link it
+ with other works to produce a work based on this file, this file
+ does not by itself cause the resulting work to be covered
+ by the GNU General Public License.
+
+ However the source code for this file must still be made available
+ in accordance with section (3) of the GNU General Public License.
+
+ This exception does not invalidate any other reasons why a work based
+ on this file might be covered by the GNU General Public License.
+ */
+
+#ifndef RATIO_RANDOM_HOSTS_OPTION_H
+#define RATIO_RANDOM_HOSTS_OPTION_H
+
+#include <boost/program_options.hpp>
+
+#include "config/option/configurationoption.h"
+
+//-----------------------------------------------------------------------------
+// RatioRandomHostsOption
+//-----------------------------------------------------------------------------
+
+/**
+ * @brief This class represents the ""Ratio in (0,1] of hosts to use, picking 
+ * them at random from host list" configuration option.
+ */
+class RatioRandomHostsOption : public ConfigurationOption
+{
+public:
+    RatioRandomHostsOption();
+    virtual ~RatioRandomHostsOption();
+
+    virtual bool parse(
+            const boost::program_options::variables_map &vm,
+            Configuration *configuration
+    );
+
+};
+
+#endif // RATIO_RANDOM_HOSTS_OPTION_H
index 3f6b9c7..db8a7ed 100644 (file)
@@ -183,6 +183,9 @@ void init_pingers(
     string nameserver = configuration->get_nameserver();
     int ping_fail_limit = configuration->get_ping_fail_limit();
 
+    // remove some hosts at random
+    configuration->randomize_hosts();
+
     // calculate delays between pingers of same interval
     DelayMap delay_shifts = calc_pinger_delays(configuration->get_hosts());
 
index 4c9460d..a50b1b5 100644 (file)
@@ -25,6 +25,7 @@ add_executable(test_configurationcommandline
     ${CMAKE_SOURCE_DIR}/src/config/option/sourcenetworkinterfaceoption.cpp
     ${CMAKE_SOURCE_DIR}/src/config/option/statusnotifiercmdoption.cpp
     ${CMAKE_SOURCE_DIR}/src/config/option/versionoption.cpp
+    ${CMAKE_SOURCE_DIR}/src/config/option/ratiorandomhostsoption.cpp
     ${CMAKE_SOURCE_DIR}/src/host/loglevel.cpp
     ${CMAKE_SOURCE_DIR}/src/host/logoutput.cpp
     ${CMAKE_SOURCE_DIR}/src/host/pingprotocol.cpp
index bba0354..62f9adc 100644 (file)
@@ -25,6 +25,7 @@ add_executable(test_configurationfile
     ${CMAKE_SOURCE_DIR}/src/config/option/sourcenetworkinterfaceoption.cpp
     ${CMAKE_SOURCE_DIR}/src/config/option/statusnotifiercmdoption.cpp
     ${CMAKE_SOURCE_DIR}/src/config/option/versionoption.cpp
+    ${CMAKE_SOURCE_DIR}/src/config/option/ratiorandomhostsoption.cpp
     ${CMAKE_SOURCE_DIR}/src/host/loglevel.cpp
     ${CMAKE_SOURCE_DIR}/src/host/logoutput.cpp
     ${CMAKE_SOURCE_DIR}/src/host/pingprotocol.cpp
index 0570cbf..c54cfc4 100644 (file)
@@ -23,6 +23,7 @@ add_executable(test_configurationoptions
     ${CMAKE_SOURCE_DIR}/src/config/option/sourcenetworkinterfaceoption.cpp
     ${CMAKE_SOURCE_DIR}/src/config/option/statusnotifiercmdoption.cpp
     ${CMAKE_SOURCE_DIR}/src/config/option/versionoption.cpp
+    ${CMAKE_SOURCE_DIR}/src/config/option/ratiorandomhostsoption.cpp
     ${CMAKE_SOURCE_DIR}/src/host/loglevel.cpp
     ${CMAKE_SOURCE_DIR}/src/host/logoutput.cpp
     ${CMAKE_SOURCE_DIR}/src/host/pingprotocol.cpp