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