started parallel pings, not quite done yet since need to delay them
authorChristian Herdtweck <christian.herdtweck@intra2net.com>
Fri, 22 May 2015 16:19:25 +0000 (18:19 +0200)
committerChristian Herdtweck <christian.herdtweck@intra2net.com>
Fri, 22 May 2015 16:19:25 +0000 (18:19 +0200)
src/host/hoststatus.cpp
src/host/hoststatus.h
src/host/pingscheduler.cpp
src/host/pingscheduler.h
src/icmp/icmppacketfactory.cpp
src/main.cpp
test/test_hoststatus.cpp

index b5d5bdb..74178a6 100644 (file)
@@ -40,6 +40,7 @@ using I2n::Logger::GlobalLogger;
 HostStatus::HostStatus(
         const string &host_address,
         const int ping_fail_limit_percentage,
+        const int n_parallel_pings,
         const LinkStatusItem link_analyzer
 ) :
     HostAddress( host_address ),
@@ -48,7 +49,8 @@ HostStatus::HostStatus(
     ResolvedIpCount( 0 ),
     PingsPerformedCount( 0 ),
     PingsFailedCount( 0 ),
-    ExceededPingFailedLimit( false )
+    ExceededPingFailedLimit( false ),
+    NParallelPingers( n_parallel_pings)
 {
     BOOST_ASSERT( !HostAddress.empty() );
     BOOST_ASSERT( ( 0 <= PingFailLimitPercentage ) && ( PingFailLimitPercentage <= 100 ) );
@@ -73,7 +75,7 @@ void HostStatus::set_resolved_ip_count( const int resolved_ip_count )
 
     GlobalLogger.debug() << "Stat(" << HostAddress << "): "
         << PingsFailedCount << " fail/" << PingsPerformedCount << " pings/"
-        << ResolvedIpCount << " IPs: #IPs set";
+        << ResolvedIpCount << "*" << NParallelPingers << " IPs: #IPs set";
 }
 
 /**
@@ -98,8 +100,9 @@ void HostStatus::update_ping_statistics( const PingStatus &result,
 
     GlobalLogger.debug() << "Stat(" << HostAddress << "): "
         << PingsFailedCount << " fail/" << PingsPerformedCount << " pings/"
-        << ResolvedIpCount << " IPs: add ping with result "
-        << to_string(result) << " which took " << ping_duration_ms << " ms";
+        << ResolvedIpCount << "*" << NParallelPingers << " IPs: "
+        << "add ping with result " << to_string(result) << " which took "
+        << ping_duration_ms << " ms";
 
     BOOST_ASSERT( 1 <= ResolvedIpCount );
     BOOST_ASSERT( 0 <= PingsPerformedCount );
@@ -129,29 +132,32 @@ void HostStatus::update_ping_statistics( const PingStatus &result,
 
 bool HostStatus::tried_all_resolved_ip() const
 {
-    BOOST_ASSERT( ( 0 < PingsPerformedCount ) && ( PingsPerformedCount <= ResolvedIpCount ) );
+    BOOST_ASSERT( ( 0 < PingsPerformedCount ) &&
+                  ( PingsPerformedCount <= ResolvedIpCount*NParallelPingers ) );
 
-    return ( PingsPerformedCount == ResolvedIpCount );
+    return ( PingsPerformedCount == ResolvedIpCount*NParallelPingers );
 }
 
 void HostStatus::analyze_ping_statistics()
 {
     BOOST_ASSERT( !HostAddress.empty() );
-    BOOST_ASSERT( PingsPerformedCount == ResolvedIpCount );
+    BOOST_ASSERT( PingsPerformedCount == ResolvedIpCount*NParallelPingers );
 
     // notify if the amount of pings that failed exceed the limit
     if ( exceeded_ping_failed_limit() )
     {
         GlobalLogger.debug() << "Stat(" << HostAddress << "): "
             << PingsFailedCount << " fail/" << PingsPerformedCount << " pings/"
-            << ResolvedIpCount << " IPs: notify down";
+            << ResolvedIpCount << "*" << NParallelPingers << " IPs: "
+            << "notify down";
         LinkAnalyzer->notify_host_down( HostAddress );
     }
     else
     {
         GlobalLogger.debug() << "Stat(" << HostAddress << "): "
             << PingsFailedCount << " fail/" << PingsPerformedCount << " pings/"
-            << ResolvedIpCount << " IPs: notify up";
+            << ResolvedIpCount << "*" << NParallelPingers << " IPs: "
+            << "notify up";
         LinkAnalyzer->notify_host_up( HostAddress );
     }
 } //lint !e1762
@@ -166,7 +172,8 @@ void HostStatus::increase_ping_performed_count()
 {
     ++PingsPerformedCount;
 
-    BOOST_ASSERT( ( 0 < PingsPerformedCount ) && ( PingsPerformedCount <= ResolvedIpCount ) );
+    BOOST_ASSERT( ( 0 < PingsPerformedCount ) &&
+                  ( PingsPerformedCount <= ResolvedIpCount*NParallelPingers ) );
 }
 
 void HostStatus::increase_ping_failed_count()
@@ -181,7 +188,8 @@ void HostStatus::analyze_ping_failed_count()
     BOOST_ASSERT( ( 0 <= PingFailLimitPercentage ) && ( PingFailLimitPercentage <= 100 ) );
     BOOST_ASSERT( ( 0 <= PingsFailedCount ) && ( PingsFailedCount <= PingsPerformedCount ) );
 
-    int ping_fail_limit_count = ( ResolvedIpCount * PingFailLimitPercentage ) / 100;
+    int ping_fail_limit_count = ( ResolvedIpCount * PingFailLimitPercentage
+                                                  * NParallelPingers) / 100;
 
     // keep a boolean variable because the PingsFailedCount can be reseted
     if ( PingsFailedCount > ping_fail_limit_count )
@@ -190,7 +198,8 @@ void HostStatus::analyze_ping_failed_count()
 
         GlobalLogger.debug() << "Stat(" << HostAddress << "): "
             << PingsFailedCount << " fail/" << PingsPerformedCount << " pings/"
-            << ResolvedIpCount << " IPs: exceed limit=" << ping_fail_limit_count;
+            << ResolvedIpCount << "*" << NParallelPingers << " IPs: "
+            << "exceed limit=" << ping_fail_limit_count;
     }
     else
     {
@@ -198,6 +207,7 @@ void HostStatus::analyze_ping_failed_count()
 
         GlobalLogger.debug() << "Stat(" << HostAddress << "): "
             << PingsFailedCount << " fail/" << PingsPerformedCount << " pings/"
-            << ResolvedIpCount << " IPs: below limit=" << ping_fail_limit_count;
+            << ResolvedIpCount << "*" << NParallelPingers << " IPs: "
+            << "below limit=" << ping_fail_limit_count;
     }
 }
index a167afe..3dba06c 100644 (file)
@@ -40,6 +40,7 @@ public:
     HostStatus(
             const std::string &host_address,
             const int ping_fail_limit_percentage,
+            const int n_parallel_pings,
             const LinkStatusItem link_analyzer
     );
     ~HostStatus();
@@ -73,6 +74,8 @@ private:
     /// boolean flag that indicate if the last set of failed pings exceed the
     /// limit
     bool ExceededPingFailedLimit;
+    /// number of pingers that ping the same IP in parallel
+    int NParallelPingers;
 
 };
 
index 0bf1777..706583b 100644 (file)
@@ -57,8 +57,10 @@ using I2n::Logger::GlobalLogger;
  * @param ping_protocol_list A list of protocols to use.
  * @param ping_interval_in_sec Amount of time between each ping.
  * @param ping_fail_percentage_limit Maximum amount of pings that can fail.
+ * @param ping_reply_timeout Max amount time to wait for ping to finish
  * @param link_analyzer The object to monitor the link status.
  * @param first_delay Delay in seconds from start_pinging to first ping attempt
+ * @param n_parallel_pings: Number of pingers to ping the same IP in parallel
  */
 PingScheduler::PingScheduler(
         const IoServiceItem io_serv,
@@ -70,8 +72,9 @@ PingScheduler::PingScheduler(
         const int ping_fail_percentage_limit,
         const int ping_reply_timeout,
         LinkStatusItem link_analyzer,
-        const int first_delay
-
+        const int first_delay,
+        const int n_parallel_pings
+        const int parallel_ping_delay
 ) :
     IoService( io_serv ),
     NetworkInterfaceName( network_interface ),
@@ -85,9 +88,12 @@ PingScheduler::PingScheduler(
     TimeSentLastPing( microsec_clock::universal_time() ),
     PingReplyTimeout( ping_reply_timeout ),
     HostAnalyzer( destination_address, ping_fail_percentage_limit,
-                  link_analyzer ),
+                  n_parallel_pings, link_analyzer ),
     Resolver(),
-    Ping(),
+    Pingers(),
+    NPingers( n_parallel_pings ),
+    NPingersDone( 0 ),
+    ParallelPingDelay( parallel_ping_delay ),
     WantToPing( false ),
     LogPrefix(),
     ContinueOnOutdatedIps( false )
@@ -116,7 +122,7 @@ void PingScheduler::stop_pinging()
 {
     // stop pinger and resolver
     GlobalLogger.debug() << LogPrefix << "scheduler: stop pinging";
-    Ping->stop_pinging();
+    clear_pingers();
     cancel_resolve(true);
 
     // now cancel the own timer in case that pinger cancelation called callback
@@ -125,6 +131,23 @@ void PingScheduler::stop_pinging()
 }
 
 /**
+ * @brief stop all pingers and remove them from Pingers variable which will
+ *   proboably cause their destruction
+ *   
+ * Pingers is empty afterwards
+ */
+void PingScheduler::clear_pingers()
+{
+    PingerItem pinger;
+    while ( !Pingers.empty() )
+    {
+        pinger = Pingers.front();
+        pinger->stop_pinging();
+        Pingers.pop_front();
+    }
+}
+
+/**
  * @brief Start into infinite loop of calls to ping
  *
  * Does not start yet but set NextPingTimer (possibly to 0), so action starts
@@ -171,7 +194,7 @@ void PingScheduler::ping_when_ready()
     if ( !WantToPing )
     {
         GlobalLogger.info() << LogPrefix << "waiting for ping request "
-            << "(should take no more than than " << PingIntervalInSec << "s)";
+            << "(should take no more than " << PingIntervalInSec << "s)";
         return;
     }
     else if ( Resolver && Resolver->is_resolving() )
@@ -212,29 +235,35 @@ void PingScheduler::ping_when_ready()
     }
     else
     {
-        uint32_t ttl = ip.get_ttl().get_updated_value();
-        std::string expiry;
-        if (ttl == 0)
-            expiry = "out of date!";
-        else
+        boost::asio::ip::address actual_ip = ip.get_ip();
+        GlobalLogger.info() << LogPrefix << "pinging IP " << actual_ip
+            << " with TTL " << ip.get_ttl().get_updated_value() << "s";
+        int delay_count = 0;
+        BOOST_FOREACH( const PingerItem &pinger, Pingers )
         {
-            boost::posix_time::ptime now =
-                                  boost::posix_time::second_clock::local_time();
-            expiry = boost::posix_time::to_simple_string(now + seconds(ttl));
+            boost::asio::deadline_timer delayed_ping_timer( IoService );
+            delayed_ping_timer.expires_from_now(
+                                  milliseconds(delay_count * ParallelPingDelay);
+            delayed_ping_timer.async_wait( bind( &PingScheduler::delayed_ping,
+                                                                this, pinger) );
+            ++delay_count;
         }
-
-        GlobalLogger.info() << LogPrefix << "pinging IP " << ip.get_ip()
-            << " with TTL " << ttl << "s (" << expiry << ")";
-        Ping->ping( ip.get_ip(),
-                    DestinationPort,
-                    boost::bind(&PingScheduler::ping_done_handler, this, _1) );
         TimeSentLastPing = microsec_clock::universal_time();
+        NPingersDone = 0;
     }
 }
 
+void delayed_ping( const PingerItem &pinger )
+{
+    pinger->ping( actual_ip,
+                  DestinationPort,
+                  boost::bind(&PingScheduler::ping_done_handler,
+                                                        this, _1) );
+}
+
 
 //------------------------------------------------------------------------------
-// Post Processing of Ping result
+// Post Processing of Ping result and Preparation for next ping
 //------------------------------------------------------------------------------
 
 /**
@@ -254,24 +283,41 @@ void PingScheduler::ping_done_handler( const PingStatus &result )
         update_log_prefix();
     }
 
-    GlobalLogger.info() << LogPrefix << "Ping done with result "
-                                     << to_string(edited_result);
+    ++NPingersDone;
+    GlobalLogger.info() << LogPrefix << "Ping " << NPingersDone << " of "
+        << NPingers << " done with result " << to_string(edited_result);
 
     // post-processing
-    // You must call these 3 methods exactly in this order
-    // TODO Fix this method, once it has a semantic dependency with the
-    // update_ping_statistics method, because it depends on the PingAnalyzer
-    // statistics to update the exceeded_ping_failed_limit
+    // can call update_ping_interval only after update_ping_statistics!
     ptime now = microsec_clock::universal_time();
     HostAnalyzer.update_ping_statistics( edited_result,
                                  (now - TimeSentLastPing).total_microseconds());
+
+    // prepare next ping only after all pingers are done
+    if (NPingersDone == NPingers)
+        prepare_next_ping();
+}
+
+
+void PingScheduler::prepare_next_ping()
+{
     update_ping_interval();
 
     // get next protocol, possibly start resolving IPs
     update_ping_protocol();
 
     // schedule next ping
-    (void) NextPingTimer.expires_from_now( seconds( PingIntervalInSec ) );
+    int seconds_since_last_ping = (microsec_clock::universal_time()
+                                            - TimeSentLastPing).total_seconds();
+    if ( seconds_since_last_ping > PingIntervalInSec )
+    {
+        GlobalLogger.info() << "We are late for next ping!";
+        seconds_since_last_ping = PingIntervalInSec;
+        (void) NextPingTimer.expires_from_now( seconds(0) );
+    }
+    else
+        (void) NextPingTimer.expires_from_now( seconds( PingIntervalInSec
+                                                  - seconds_since_last_ping ) );
     NextPingTimer.async_wait( bind( &PingScheduler::ping, this,
                                          boost::asio::placeholders::error ) );
 }
@@ -315,14 +361,12 @@ void PingScheduler::update_ping_protocol()
 
 void PingScheduler::get_next_ping_protocol()
 {
-    if (Ping)
-    {
-        Ping->stop_pinging();
-        Ping.reset();
-    }
-
+    // stop and destruct all pingers
+    clear_pingers();
     GlobalLogger.debug() << LogPrefix
         << "------------------------------------------------------------------";
+
+    // get next protocol
     ++ProtocolIter;
     if (ProtocolIter == Protocols.end())
         ProtocolIter = Protocols.begin();
@@ -330,11 +374,12 @@ void PingScheduler::get_next_ping_protocol()
     // --> ProtocolIter still points to currently used protocol which is
     //     required in dns_resolve_callback
 
-    Ping = PingerFactory::createPinger(ping_protocol, IoService,
-                                       NetworkInterfaceName, PingReplyTimeout);
+    // create new pingers
+    for (int count=0; count<NPingers; ++count)
+        Pingers.push_back( PingerFactory::createPinger(ping_protocol, IoService,
+                                      NetworkInterfaceName, PingReplyTimeout) );
 
     update_dns_resolver( ping_protocol );
-
 }
 
 bool PingScheduler::can_change_ping_protocol() const
index 37d94a1..ad9f63a 100644 (file)
@@ -36,6 +36,8 @@ on this file might be covered by the GNU General Public License.
 #include "host/pingprotocol.h"
 #include "dns/resolverbase.h"
 
+typedef std::list<PingerItem> pinger_list;
+
 //-----------------------------------------------------------------------------
 // PingScheduler
 //-----------------------------------------------------------------------------
@@ -58,7 +60,9 @@ public:
             const int ping_fail_percentage_limit,
             const int ping_reply_timeout,
             LinkStatusItem link_analyzer,
-            const int first_delay
+            const int first_delay,
+            const int n_parallel_pings,
+            const int parallel_ping_delay
     );
     ~PingScheduler();
 
@@ -79,6 +83,7 @@ private:
     void get_next_ping_protocol();
     bool can_change_ping_protocol() const;
 
+    void prepare_next_ping();
     void update_dns_resolver( PingProtocol current_protocol );
 
     void ping_when_ready();
@@ -88,6 +93,7 @@ private:
 
     void update_log_prefix();
     void cancel_resolve(const bool force_cancel);
+    void clear_pingers();
 
     //
     // Attributes
@@ -118,8 +124,14 @@ private:
     HostStatus HostAnalyzer;
     /// The Dns resolver
     ResolverItem Resolver;
-    /// The actual pinger
-    PingerItem Ping;
+    /// vector of pingers
+    pinger_list Pingers;
+    /// number of pingers that work in parallel
+    int NPingers;
+    /// number of results from pingers
+    int NPingersDone;
+    /// delay (in ms) between pings to same IP
+    int ParallelPingDelay;
     /// a flag whether we should ping as soon as dns is ready
     bool WantToPing;
     /// Prefix to log messages for quicker analysis/debugging
index 2eed296..5024789 100644 (file)
@@ -74,7 +74,7 @@ IcmpPacketItem IcmpPacketFactory::create_icmp_packet(
     else
     {
         GlobalLogger.warning() << "ICMP packet creation failed: "
-            << "Unknown protocol, expect ICMP v4 or v6!" << endl;
+            << "Unknown protocol arg, expect ICMP v4 or v6!" << endl;
         icmp_packet.reset();    // --> (!icmp_packet) is true
         return icmp_packet;
     }
index 6afc047..72157ff 100644 (file)
@@ -279,6 +279,8 @@ bool init_pingers(
         // get delay for this scheduler and update assigned delays
         int current_delay = boost::math::iround(delays[ping_interval_in_sec]);
         delays[ping_interval_in_sec] += delay_shifts[ping_interval_in_sec];
+        int n_parallel_pings = 10;
+        int parallel_ping_delay = 100;   // ms
 
         PingSchedulerItem scheduler(
                 new PingScheduler(
@@ -291,7 +293,9 @@ bool init_pingers(
                         ping_fail_limit,
                         ping_reply_timeout,
                         status_notifier,
-                        current_delay
+                        current_delay,
+                        n_parallel_pings,
+                        parallel_ping_delay
                 )
         );
         scheduler_list->push_back( scheduler );
index 752822f..e6bfd5b 100644 (file)
@@ -39,7 +39,8 @@ BOOST_AUTO_TEST_CASE( fail_percentage_10 )
     int resolved_ip_count = 10;
 
     LinkStatusItem link_status( new LinkStatus );
-    HostStatus host_status( "localhost", ping_fail_percentage_limit, link_status );
+    HostStatus host_status( "localhost", ping_fail_percentage_limit, 1,
+                            link_status );
     host_status.set_resolved_ip_count( resolved_ip_count );
 
     host_status.update_ping_statistics( PingStatus_SuccessReply, 1 );
@@ -79,7 +80,8 @@ BOOST_AUTO_TEST_CASE( fail_percentage_50 )
     int resolved_ip_count = 10;
 
     LinkStatusItem link_status( new LinkStatus );
-    HostStatus host_status( "localhost", ping_fail_percentage_limit, link_status );
+    HostStatus host_status( "localhost", ping_fail_percentage_limit, 1,
+                            link_status );
     host_status.set_resolved_ip_count( resolved_ip_count );
 
     host_status.update_ping_statistics( PingStatus_SuccessReply, 1 );
@@ -119,7 +121,8 @@ BOOST_AUTO_TEST_CASE( fail_percentage_80 )
     int resolved_ip_count = 10;
 
     LinkStatusItem link_status( new LinkStatus );
-    HostStatus host_status( "localhost", ping_fail_percentage_limit, link_status );
+    HostStatus host_status( "localhost", ping_fail_percentage_limit, 1,
+                            link_status );
     host_status.set_resolved_ip_count( resolved_ip_count );
 
     host_status.update_ping_statistics( PingStatus_FailureOther, 1 );