From 91aa83f93de4a15800bf9779e62bd93a04e3f4e6 Mon Sep 17 00:00:00 2001 From: Christian Herdtweck Date: Fri, 22 May 2015 18:19:25 +0200 Subject: [PATCH] started parallel pings, not quite done yet since need to delay them --- src/host/hoststatus.cpp | 36 ++++++++---- src/host/hoststatus.h | 3 + src/host/pingscheduler.cpp | 119 +++++++++++++++++++++++++++------------ src/host/pingscheduler.h | 18 +++++- src/icmp/icmppacketfactory.cpp | 2 +- src/main.cpp | 6 ++- test/test_hoststatus.cpp | 9 ++- 7 files changed, 135 insertions(+), 58 deletions(-) diff --git a/src/host/hoststatus.cpp b/src/host/hoststatus.cpp index b5d5bdb..74178a6 100644 --- a/src/host/hoststatus.cpp +++ b/src/host/hoststatus.cpp @@ -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; } } diff --git a/src/host/hoststatus.h b/src/host/hoststatus.h index a167afe..3dba06c 100644 --- a/src/host/hoststatus.h +++ b/src/host/hoststatus.h @@ -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; }; diff --git a/src/host/pingscheduler.cpp b/src/host/pingscheduler.cpp index 0bf1777..706583b 100644 --- a/src/host/pingscheduler.cpp +++ b/src/host/pingscheduler.cpp @@ -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 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 diff --git a/src/icmp/icmppacketfactory.cpp b/src/icmp/icmppacketfactory.cpp index 2eed296..5024789 100644 --- a/src/icmp/icmppacketfactory.cpp +++ b/src/icmp/icmppacketfactory.cpp @@ -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; } diff --git a/src/main.cpp b/src/main.cpp index 6afc047..72157ff 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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 ); diff --git a/test/test_hoststatus.cpp b/test/test_hoststatus.cpp index 752822f..e6bfd5b 100644 --- a/test/test_hoststatus.cpp +++ b/test/test_hoststatus.cpp @@ -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 ); -- 1.7.1