started parallel pings, not quite done yet since need to delay them
[pingcheck] / src / host / pingscheduler.cpp
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