fixed bug that caused HostStatus to never leave BurstMode
[pingcheck] / src / host / hoststatus.cpp
CommitLineData
91fcc471
TJ
1/*
2The software in this package is distributed under the GNU General
3Public License version 2 (with a special exception described below).
4
5A copy of GNU General Public License (GPL) is included in this distribution,
6in the file COPYING.GPL.
7
8As a special exception, if other files instantiate templates or use macros
9or inline functions from this file, or you compile this file and link it
10with other works to produce a work based on this file, this file
11does not by itself cause the resulting work to be covered
12by the GNU General Public License.
13
14However the source code for this file must still be made available
15in accordance with section (3) of the GNU General Public License.
16
17This exception does not invalidate any other reasons why a work based
18on this file might be covered by the GNU General Public License.
19*/
6c14bbee 20#include "host/hoststatus.h"
ddf41c89
GMF
21
22#include <iostream>
3f7c921f 23#include <logfunc.hpp>
ddf41c89 24
780b0bca 25#include "boost_assert_handler.h"
ddf41c89
GMF
26
27using namespace std;
3f7c921f 28using I2n::Logger::GlobalLogger;
ddf41c89
GMF
29
30//-----------------------------------------------------------------------------
6c14bbee 31// HostStatus
ddf41c89
GMF
32//-----------------------------------------------------------------------------
33
c01a6023 34/**
6c14bbee
GMF
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
c01a6023 37 * fail.
a7b15639
CH
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
6c14bbee 43 * @param link_analyzer The object used to notify the status of the host.
c01a6023 44 */
6c14bbee 45HostStatus::HostStatus(
ddf41c89 46 const string &host_address,
cd4048df 47 const int ping_fail_limit_percentage,
a7b15639 48 const int ping_congestion_limit_percentage,
941b5e25 49 const int congest_caused_by_fail_limit_percentage,
a7b15639 50 const int ping_duration_congestion_thresh,
91aa83f9 51 const int n_parallel_pings,
c6c54dfb 52 const LinkStatusItem link_analyzer
ddf41c89 53) :
c1fff16a 54 HostAddress( host_address ),
fb469ffa 55 LinkAnalyzer( link_analyzer ),
cd4048df 56 PingFailLimitPercentage( ping_fail_limit_percentage ),
a7b15639 57 PingCongestionLimitPercentage( ping_congestion_limit_percentage ),
941b5e25 58 CongestCausedByFailLimitPercentage(congest_caused_by_fail_limit_percentage),
a7b15639 59 PingDurationCongestionsThresh( ping_duration_congestion_thresh*1000000 ),
ddf41c89
GMF
60 ResolvedIpCount( 0 ),
61 PingsPerformedCount( 0 ),
d8a91bd6 62 PingsFailedCount( 0 ),
a7b15639 63 PingCongestionCount( 0 ),
91aa83f9 64 ExceededPingFailedLimit( false ),
a7b15639 65 ExceededPingCongestionLimit( false ),
4d7db1af
CH
66 NParallelPingers( n_parallel_pings),
67 InBurstMode( false )
ddf41c89 68{
d4793cc9 69 BOOST_ASSERT( !HostAddress.empty() );
a7b15639
CH
70 BOOST_ASSERT( ( 0 <= PingFailLimitPercentage )
71 && ( PingFailLimitPercentage <= 100 ) );
72 BOOST_ASSERT( ( 0 <= PingCongestionLimitPercentage )
73 && ( PingCongestionLimitPercentage <= 100 ) );
ddf41c89
GMF
74}
75
6c14bbee 76HostStatus::~HostStatus()
ddf41c89
GMF
77{
78}
79
a7b15639 80
242e5fb3
CH
81void HostStatus::set_n_parallel_pings(const int n_parallel_pings)
82{
4d7db1af
CH
83 if (ExceededPingCongestionLimit)
84 InBurstMode = true;
85 else
0d4b76ee 86 InBurstMode = false;
4d7db1af 87
242e5fb3
CH
88 if (NParallelPingers != n_parallel_pings)
89 {
90 NParallelPingers = n_parallel_pings;
91 reset_ping_counters();
92 }
93 GlobalLogger.debug() << log_prefix() << "#pingers set";
94}
95
96
a7b15639
CH
97std::string HostStatus::log_prefix()
98{
99 std::stringstream temp;
100 temp << "Stat(" << HostAddress << "): "
101 << PingsFailedCount << " fail," << PingCongestionCount << " cong/"
4d7db1af
CH
102 << PingsPerformedCount << " pings/" << NParallelPingers << "*"
103 << ResolvedIpCount << " IPs: ";
242e5fb3 104 return temp.str();
a7b15639
CH
105}
106
c01a6023 107/**
6c14bbee 108 * @param resolved_ip_count The number of IPs resolved for the host.
c01a6023 109 */
6c14bbee 110void HostStatus::set_resolved_ip_count( const int resolved_ip_count )
ddf41c89 111{
838e0acf 112 BOOST_ASSERT( 0 <= resolved_ip_count );
ddf41c89 113
db625177
CH
114 if (resolved_ip_count != ResolvedIpCount)
115 { // assume that the target has changed --> reset counters
116 reset_ping_counters();
117 }
ddf41c89 118 ResolvedIpCount = resolved_ip_count;
3f7c921f 119
a7b15639 120 GlobalLogger.debug() << log_prefix() << "#IPs set";
ddf41c89
GMF
121}
122
c01a6023
GMF
123/**
124 * @return true if the amount of failed pings given to the host exceeded the
125 * limit.
126 */
6c14bbee 127bool HostStatus::exceeded_ping_failed_limit() const
d8a91bd6 128{
a341119a 129 return ExceededPingFailedLimit;
d8a91bd6
GMF
130}
131
c01a6023 132/**
a7b15639
CH
133 * @return true if the amount of congested pings given to the host exceeded the
134 * limit.
135 */
136bool HostStatus::exceeded_ping_congestion_limit() const
137{
138 return ExceededPingCongestionLimit;
139}
140
141/**
96c4e7a4
CH
142 * Tells the status analyzer how the last ping went
143 *
144 * @param result: status of ping specifying success/failure and reason of fail
145 * @param ping_duration_us duration of ping in micro seconds
c01a6023 146 */
96c4e7a4
CH
147void HostStatus::update_ping_statistics( const PingStatus &result,
148 const long ping_duration_us )
ddf41c89 149{
242e5fb3 150 float ping_duration_ms = static_cast<float>(ping_duration_us) / 1000.0f;
96c4e7a4 151
a7b15639
CH
152 GlobalLogger.debug() << log_prefix() << "add ping with result "
153 << to_string(result) << " which took " << ping_duration_ms << " ms";
3f7c921f 154
ffa5cfe2 155 BOOST_ASSERT( 0 <= ResolvedIpCount );
ddf41c89
GMF
156 BOOST_ASSERT( 0 <= PingsPerformedCount );
157 BOOST_ASSERT( PingsFailedCount <= PingsPerformedCount );
a7b15639 158 BOOST_ASSERT( PingCongestionCount <= PingsPerformedCount );
ddf41c89 159
4d7db1af
CH
160 increase_ping_performed_count();
161
162 bool failed_because_congested = update_congestion_stats( result,
163 ping_duration_us );
164 update_fail_stats( result, failed_because_congested );
2c10f87b 165
c5e4bfa1
GMF
166 // after we tried all IPs resolved for this host, we can analyze how many
167 // failed
168 if ( tried_all_resolved_ip() )
ddf41c89 169 {
d8a91bd6 170 analyze_ping_statistics();
ddf41c89 171
c5e4bfa1 172 reset_ping_counters();
ddf41c89
GMF
173 }
174
175 BOOST_ASSERT( PingsFailedCount <= PingsPerformedCount );
a7b15639
CH
176 BOOST_ASSERT( PingCongestionCount <= PingsPerformedCount );
177}
178
179
4d7db1af
CH
180void HostStatus::update_fail_stats( const PingStatus &result,
181 const bool failed_because_congested )
a7b15639 182{
a7b15639 183 if ( result != PingStatus_SuccessReply
4d7db1af
CH
184 && result != PingStatus_SuccessOutdatedIP
185 && !failed_because_congested )
a7b15639
CH
186 {
187 increase_ping_failed_count();
188 }
189
190 analyze_ping_failed_count();
191}
192
193
4d7db1af 194bool HostStatus::update_congestion_stats( const PingStatus &result,
a7b15639
CH
195 const long ping_duration_us )
196{
4d7db1af 197 bool is_congested = false;
a7b15639 198 if (ping_duration_us > PingDurationCongestionsThresh)
4d7db1af 199 is_congested = true;
a7b15639 200 else if ( result == PingStatus_FailureTimeout )
4d7db1af 201 is_congested = true;
a7b15639
CH
202 // PingStatus_FailureNoIP, PingStatus_SuccessOutdatedIP could also be caused
203 // by congestion, but also by other reasons (e.g. firewall blocking port 53)
204
4d7db1af
CH
205 if (is_congested)
206 increase_ping_congestion_count();
207
a7b15639 208 analyze_ping_congestion_count();
4d7db1af
CH
209
210 return is_congested;
ddf41c89
GMF
211}
212
c1d776ba 213
6c14bbee 214bool HostStatus::tried_all_resolved_ip() const
d8a91bd6 215{
ffa5cfe2 216 BOOST_ASSERT( 0 < PingsPerformedCount );
d4793cc9 217
ffa5cfe2 218 return ( PingsPerformedCount >= ResolvedIpCount*NParallelPingers );
d8a91bd6
GMF
219}
220
4d7db1af
CH
221
222/** @brief called when tried_all_resolved_ip() */
6c14bbee 223void HostStatus::analyze_ping_statistics()
ddf41c89 224{
c1fff16a 225 BOOST_ASSERT( !HostAddress.empty() );
ffa5cfe2 226 BOOST_ASSERT( PingsPerformedCount >= ResolvedIpCount*NParallelPingers );
ddf41c89 227
4d7db1af 228 // timeouts are not counted towards failures, only count as congestions
941b5e25
CH
229 // However, if many pings timed out even in burst mode, then we still
230 // declare the line down
231 float limit = static_cast<float>( PingsPerformedCount
232 * CongestCausedByFailLimitPercentage)/100.f;
233 if (InBurstMode && PingCongestionCount > limit)
87758553 234 {
941b5e25
CH
235 GlobalLogger.info() << log_prefix()
236 << "Assume congestion is actually caused by compromised connection "
237 << "to host because " << PingCongestionCount << " of "
238 << PingsPerformedCount << " burst pings timed out";
81686580
CH
239 PingsFailedCount += PingCongestionCount;
240 PingCongestionCount = 0;
4d7db1af 241 ExceededPingFailedLimit = true;
81686580 242 ExceededPingCongestionLimit = false;
87758553 243 }
4d7db1af 244
c1fff16a 245 // notify if the amount of pings that failed exceed the limit
a341119a 246 if ( exceeded_ping_failed_limit() )
ddf41c89 247 {
a7b15639 248 GlobalLogger.debug() << log_prefix() << "notify down";
fb469ffa 249 LinkAnalyzer->notify_host_down( HostAddress );
ddf41c89 250 }
e5029552
CH
251 else if (exceeded_ping_congestion_limit() && !InBurstMode)
252 // only notify up if will not try burst mode next
253 // otherwise will continuously notify up and down if get timeouts
254 GlobalLogger.notice() << log_prefix() << "will not notify up because "
255 << " will go into burst mode next";
ddf41c89
GMF
256 else
257 {
a7b15639 258 GlobalLogger.debug() << log_prefix() << "notify up";
fb469ffa 259 LinkAnalyzer->notify_host_up( HostAddress );
ddf41c89 260 }
a7b15639 261
4d7db1af 262 // nothing else to do about congestion here, congestion is not forwarded to
a7b15639 263 // central LinkAnalyzer
6fd0993e 264} //lint !e1762
ddf41c89 265
6c14bbee 266void HostStatus::reset_ping_counters()
c1fff16a
GMF
267{
268 PingsPerformedCount = 0;
269 PingsFailedCount = 0;
a7b15639 270 PingCongestionCount = 0;
c1fff16a
GMF
271}
272
6c14bbee 273void HostStatus::increase_ping_performed_count()
c5e4bfa1
GMF
274{
275 ++PingsPerformedCount;
c1fff16a 276
ffa5cfe2 277 BOOST_ASSERT( 0 < PingsPerformedCount );
c5e4bfa1
GMF
278}
279
6c14bbee 280void HostStatus::increase_ping_failed_count()
c5e4bfa1
GMF
281{
282 ++PingsFailedCount;
c1fff16a
GMF
283
284 BOOST_ASSERT( ( 0 <= PingsFailedCount ) && ( PingsFailedCount <= PingsPerformedCount ) );
c5e4bfa1
GMF
285}
286
a7b15639
CH
287void HostStatus::increase_ping_congestion_count()
288{
289 ++PingCongestionCount;
290
291 BOOST_ASSERT( ( 0 <= PingCongestionCount )
292 && ( PingCongestionCount <= PingsPerformedCount ) );
293}
294
6c14bbee 295void HostStatus::analyze_ping_failed_count()
ddf41c89 296{
cd4048df 297 BOOST_ASSERT( ( 0 <= PingFailLimitPercentage ) && ( PingFailLimitPercentage <= 100 ) );
c1fff16a
GMF
298 BOOST_ASSERT( ( 0 <= PingsFailedCount ) && ( PingsFailedCount <= PingsPerformedCount ) );
299
4d7db1af 300 int limit = ( PingsPerformedCount * PingFailLimitPercentage) / 100;
c1fff16a 301
6827496c 302 // keep a boolean variable because the PingsFailedCount can be reseted
4d7db1af 303 if ( PingsFailedCount > limit )
1d7d7cb2
GMF
304 {
305 ExceededPingFailedLimit = true;
3f7c921f 306
4d7db1af 307 GlobalLogger.debug() << log_prefix() << "exceed fail limit=" << limit;
1d7d7cb2
GMF
308 }
309 else
310 {
311 ExceededPingFailedLimit = false;
3f7c921f 312
4d7db1af 313 GlobalLogger.debug() << log_prefix() << "below fail limit=" << limit;
a7b15639
CH
314 }
315}
316
317void HostStatus::analyze_ping_congestion_count()
318{
319 BOOST_ASSERT( ( 0 <= PingCongestionLimitPercentage )
320 && ( PingCongestionLimitPercentage <= 100 ) );
321 BOOST_ASSERT( ( 0 <= PingCongestionCount )
322 && ( PingCongestionCount <= PingsPerformedCount ) );
323
4d7db1af 324 int limit = ( PingsPerformedCount * PingCongestionLimitPercentage) / 100;
a7b15639
CH
325
326 // keep a boolean variable because the PingCongestionCount can be reseted
4d7db1af 327 if ( PingCongestionCount > limit )
a7b15639
CH
328 {
329 ExceededPingCongestionLimit = true;
330
331 GlobalLogger.debug() << log_prefix() << "exceed congestion limit="
4d7db1af 332 << limit;
a7b15639
CH
333 }
334 else
335 {
336 ExceededPingCongestionLimit = false;
337
338 GlobalLogger.debug() << log_prefix() << "below congestion limit="
4d7db1af 339 << limit;
1d7d7cb2 340 }
c5e4bfa1 341}