* @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,
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 ),
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 )
{
// 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
}
/**
+ * @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
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() )
}
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
//------------------------------------------------------------------------------
/**
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 ) );
}
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();
// --> 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