HostStatus::HostStatus(
const string &host_address,
const int ping_fail_limit_percentage,
+ const int n_parallel_pings,
const LinkStatusItem link_analyzer
) :
HostAddress( host_address ),
ResolvedIpCount( 0 ),
PingsPerformedCount( 0 ),
PingsFailedCount( 0 ),
- ExceededPingFailedLimit( false )
+ ExceededPingFailedLimit( false ),
+ NParallelPingers( n_parallel_pings)
{
BOOST_ASSERT( !HostAddress.empty() );
BOOST_ASSERT( ( 0 <= PingFailLimitPercentage ) && ( PingFailLimitPercentage <= 100 ) );
GlobalLogger.debug() << "Stat(" << HostAddress << "): "
<< PingsFailedCount << " fail/" << PingsPerformedCount << " pings/"
- << ResolvedIpCount << " IPs: #IPs set";
+ << ResolvedIpCount << "*" << NParallelPingers << " IPs: #IPs set";
}
/**
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 );
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
{
++PingsPerformedCount;
- BOOST_ASSERT( ( 0 < PingsPerformedCount ) && ( PingsPerformedCount <= ResolvedIpCount ) );
+ BOOST_ASSERT( ( 0 < PingsPerformedCount ) &&
+ ( PingsPerformedCount <= ResolvedIpCount*NParallelPingers ) );
}
void HostStatus::increase_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 )
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
{
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;
}
}
HostStatus(
const std::string &host_address,
const int ping_fail_limit_percentage,
+ const int n_parallel_pings,
const LinkStatusItem link_analyzer
);
~HostStatus();
/// 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;
};
* @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
#include "host/pingprotocol.h"
#include "dns/resolverbase.h"
+typedef std::list<PingerItem> pinger_list;
+
//-----------------------------------------------------------------------------
// 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
);
~PingScheduler();
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();
void update_log_prefix();
void cancel_resolve(const bool force_cancel);
+ void clear_pingers();
//
// Attributes
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
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;
}
// 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(
ping_fail_limit,
ping_reply_timeout,
status_notifier,
- current_delay
+ current_delay,
+ n_parallel_pings,
+ parallel_ping_delay
)
);
scheduler_list->push_back( scheduler );
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 );
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 );
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 );