2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
8 As a special exception, if other files instantiate templates or use macros
9 or inline functions from this file, or you compile this file and link it
10 with other works to produce a work based on this file, this file
11 does not by itself cause the resulting work to be covered
12 by the GNU General Public License.
14 However the source code for this file must still be made available
15 in accordance with section (3) of the GNU General Public License.
17 This exception does not invalidate any other reasons why a work based
18 on this file might be covered by the GNU General Public License.
20 #include "host/hoststatus.h"
23 #include <logfunc.hpp>
25 #include "boost_assert_handler.h"
28 using I2n::Logger::GlobalLogger;
30 //-----------------------------------------------------------------------------
32 //-----------------------------------------------------------------------------
35 * @param host_address The address of the host it has to analyze.
36 * @param ping_fail_percentage_limit The percentage threshold of pings that can
38 * @param ping_congestion_limit_percentage The percentage threshold of pings
39 * that can fail due to line congestion
40 * @param ping_duration_congestion_thresh Threshold in micro seconds that marks
41 * the difference between a "normal" and a congested line
42 * @param n_parallel_pings Number of pings that is sent for each IP
43 * @param link_analyzer The object used to notify the status of the host.
45 HostStatus::HostStatus(
46 const string &host_address,
47 const int ping_fail_limit_percentage,
48 const int ping_congestion_limit_percentage,
49 const int ping_duration_congestion_thresh,
50 const int n_parallel_pings,
51 const LinkStatusItem link_analyzer
53 HostAddress( host_address ),
54 LinkAnalyzer( link_analyzer ),
55 PingFailLimitPercentage( ping_fail_limit_percentage ),
56 PingCongestionLimitPercentage( ping_congestion_limit_percentage ),
57 PingDurationCongestionsThresh( ping_duration_congestion_thresh*1000000 ),
59 PingsPerformedCount( 0 ),
60 PingsFailedCount( 0 ),
61 PingCongestionCount( 0 ),
62 ExceededPingFailedLimit( false ),
63 ExceededPingCongestionLimit( false ),
64 NParallelPingers( n_parallel_pings),
67 BOOST_ASSERT( !HostAddress.empty() );
68 BOOST_ASSERT( ( 0 <= PingFailLimitPercentage )
69 && ( PingFailLimitPercentage <= 100 ) );
70 BOOST_ASSERT( ( 0 <= PingCongestionLimitPercentage )
71 && ( PingCongestionLimitPercentage <= 100 ) );
74 HostStatus::~HostStatus()
79 void HostStatus::set_n_parallel_pings(const int n_parallel_pings)
81 if (ExceededPingCongestionLimit)
86 if (NParallelPingers != n_parallel_pings)
88 NParallelPingers = n_parallel_pings;
89 reset_ping_counters();
91 GlobalLogger.debug() << log_prefix() << "#pingers set";
95 std::string HostStatus::log_prefix()
97 std::stringstream temp;
98 temp << "Stat(" << HostAddress << "): "
99 << PingsFailedCount << " fail," << PingCongestionCount << " cong/"
100 << PingsPerformedCount << " pings/" << NParallelPingers << "*"
101 << ResolvedIpCount << " IPs: ";
106 * @param resolved_ip_count The number of IPs resolved for the host.
108 void HostStatus::set_resolved_ip_count( const int resolved_ip_count )
110 BOOST_ASSERT( 0 <= resolved_ip_count );
112 if (resolved_ip_count != ResolvedIpCount)
113 { // assume that the target has changed --> reset counters
114 reset_ping_counters();
116 ResolvedIpCount = resolved_ip_count;
118 GlobalLogger.debug() << log_prefix() << "#IPs set";
122 * @return true if the amount of failed pings given to the host exceeded the
125 bool HostStatus::exceeded_ping_failed_limit() const
127 return ExceededPingFailedLimit;
131 * @return true if the amount of congested pings given to the host exceeded the
134 bool HostStatus::exceeded_ping_congestion_limit() const
136 return ExceededPingCongestionLimit;
140 * Tells the status analyzer how the last ping went
142 * @param result: status of ping specifying success/failure and reason of fail
143 * @param ping_duration_us duration of ping in micro seconds
145 void HostStatus::update_ping_statistics( const PingStatus &result,
146 const long ping_duration_us )
148 float ping_duration_ms = static_cast<float>(ping_duration_us) / 1000.0f;
150 GlobalLogger.debug() << log_prefix() << "add ping with result "
151 << to_string(result) << " which took " << ping_duration_ms << " ms";
153 BOOST_ASSERT( 0 <= ResolvedIpCount );
154 BOOST_ASSERT( 0 <= PingsPerformedCount );
155 BOOST_ASSERT( PingsFailedCount <= PingsPerformedCount );
156 BOOST_ASSERT( PingCongestionCount <= PingsPerformedCount );
158 increase_ping_performed_count();
160 bool failed_because_congested = update_congestion_stats( result,
162 update_fail_stats( result, failed_because_congested );
164 // after we tried all IPs resolved for this host, we can analyze how many
166 if ( tried_all_resolved_ip() )
168 analyze_ping_statistics();
170 reset_ping_counters();
173 BOOST_ASSERT( PingsFailedCount <= PingsPerformedCount );
174 BOOST_ASSERT( PingCongestionCount <= PingsPerformedCount );
178 void HostStatus::update_fail_stats( const PingStatus &result,
179 const bool failed_because_congested )
181 if ( result != PingStatus_SuccessReply
182 && result != PingStatus_SuccessOutdatedIP
183 && !failed_because_congested )
185 increase_ping_failed_count();
188 analyze_ping_failed_count();
192 bool HostStatus::update_congestion_stats( const PingStatus &result,
193 const long ping_duration_us )
195 bool is_congested = false;
196 if (ping_duration_us > PingDurationCongestionsThresh)
198 else if ( result == PingStatus_FailureTimeout )
200 // PingStatus_FailureNoIP, PingStatus_SuccessOutdatedIP could also be caused
201 // by congestion, but also by other reasons (e.g. firewall blocking port 53)
204 increase_ping_congestion_count();
206 analyze_ping_congestion_count();
212 bool HostStatus::tried_all_resolved_ip() const
214 BOOST_ASSERT( 0 < PingsPerformedCount );
216 return ( PingsPerformedCount >= ResolvedIpCount*NParallelPingers );
220 /** @brief called when tried_all_resolved_ip() */
221 void HostStatus::analyze_ping_statistics()
223 BOOST_ASSERT( !HostAddress.empty() );
224 BOOST_ASSERT( PingsPerformedCount >= ResolvedIpCount*NParallelPingers );
226 // timeouts are not counted towards failures, only count as congestions
227 // However, if all pings timed out even in burst mode, then we still declare
229 if (InBurstMode && PingCongestionCount >= PingsPerformedCount)
231 GlobalLogger.notice() << log_prefix() << "All pings timed out despite "
232 << "using more pings per IP --> assume connection is really down";
233 PingsFailedCount += PingCongestionCount;
234 PingCongestionCount = 0;
235 ExceededPingFailedLimit = true;
236 ExceededPingCongestionLimit = false;
239 // notify if the amount of pings that failed exceed the limit
240 if ( exceeded_ping_failed_limit() )
242 GlobalLogger.debug() << log_prefix() << "notify down";
243 LinkAnalyzer->notify_host_down( HostAddress );
245 else if (exceeded_ping_congestion_limit() && !InBurstMode)
246 // only notify up if will not try burst mode next
247 // otherwise will continuously notify up and down if get timeouts
248 GlobalLogger.notice() << log_prefix() << "will not notify up because "
249 << " will go into burst mode next";
252 GlobalLogger.debug() << log_prefix() << "notify up";
253 LinkAnalyzer->notify_host_up( HostAddress );
256 // nothing else to do about congestion here, congestion is not forwarded to
257 // central LinkAnalyzer
260 void HostStatus::reset_ping_counters()
262 PingsPerformedCount = 0;
263 PingsFailedCount = 0;
264 PingCongestionCount = 0;
267 void HostStatus::increase_ping_performed_count()
269 ++PingsPerformedCount;
271 BOOST_ASSERT( 0 < PingsPerformedCount );
274 void HostStatus::increase_ping_failed_count()
278 BOOST_ASSERT( ( 0 <= PingsFailedCount ) && ( PingsFailedCount <= PingsPerformedCount ) );
281 void HostStatus::increase_ping_congestion_count()
283 ++PingCongestionCount;
285 BOOST_ASSERT( ( 0 <= PingCongestionCount )
286 && ( PingCongestionCount <= PingsPerformedCount ) );
289 void HostStatus::analyze_ping_failed_count()
291 BOOST_ASSERT( ( 0 <= PingFailLimitPercentage ) && ( PingFailLimitPercentage <= 100 ) );
292 BOOST_ASSERT( ( 0 <= PingsFailedCount ) && ( PingsFailedCount <= PingsPerformedCount ) );
294 int limit = ( PingsPerformedCount * PingFailLimitPercentage) / 100;
296 // keep a boolean variable because the PingsFailedCount can be reseted
297 if ( PingsFailedCount > limit )
299 ExceededPingFailedLimit = true;
301 GlobalLogger.debug() << log_prefix() << "exceed fail limit=" << limit;
305 ExceededPingFailedLimit = false;
307 GlobalLogger.debug() << log_prefix() << "below fail limit=" << limit;
311 void HostStatus::analyze_ping_congestion_count()
313 BOOST_ASSERT( ( 0 <= PingCongestionLimitPercentage )
314 && ( PingCongestionLimitPercentage <= 100 ) );
315 BOOST_ASSERT( ( 0 <= PingCongestionCount )
316 && ( PingCongestionCount <= PingsPerformedCount ) );
318 int limit = ( PingsPerformedCount * PingCongestionLimitPercentage) / 100;
320 // keep a boolean variable because the PingCongestionCount can be reseted
321 if ( PingCongestionCount > limit )
323 ExceededPingCongestionLimit = true;
325 GlobalLogger.debug() << log_prefix() << "exceed congestion limit="
330 ExceededPingCongestionLimit = false;
332 GlobalLogger.debug() << log_prefix() << "below congestion limit="