Commit | Line | Data |
---|---|---|
91fcc471 TJ |
1 | /* |
2 | The software in this package is distributed under the GNU General | |
3 | Public License version 2 (with a special exception described below). | |
4 | ||
5 | A copy of GNU General Public License (GPL) is included in this distribution, | |
6 | in the file COPYING.GPL. | |
7 | ||
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. | |
13 | ||
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. | |
16 | ||
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. | |
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 | |
28 | using namespace std; | |
3f7c921f | 29 | using 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 | 46 | HostStatus::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 | 77 | HostStatus::~HostStatus() |
ddf41c89 GMF |
78 | { |
79 | } | |
80 | ||
a7b15639 | 81 | |
242e5fb3 CH |
82 | void 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 | ||
98 | void 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 |
118 | std::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 | 134 | void 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 | 151 | bool 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 | */ | |
160 | bool 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 |
171 | void 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 |
203 | void 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 | 217 | bool 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 | 237 | bool 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 | 246 | void 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 | 289 | void HostStatus::reset_ping_counters() |
c1fff16a GMF |
290 | { |
291 | PingsPerformedCount = 0; | |
292 | PingsFailedCount = 0; | |
a7b15639 | 293 | PingCongestionCount = 0; |
c1fff16a GMF |
294 | } |
295 | ||
6c14bbee | 296 | void HostStatus::increase_ping_performed_count() |
c5e4bfa1 GMF |
297 | { |
298 | ++PingsPerformedCount; | |
c1fff16a | 299 | |
ffa5cfe2 | 300 | BOOST_ASSERT( 0 < PingsPerformedCount ); |
c5e4bfa1 GMF |
301 | } |
302 | ||
6c14bbee | 303 | void 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 |
310 | void HostStatus::increase_ping_congestion_count() |
311 | { | |
312 | ++PingCongestionCount; | |
313 | ||
314 | BOOST_ASSERT( ( 0 <= PingCongestionCount ) | |
315 | && ( PingCongestionCount <= PingsPerformedCount ) ); | |
316 | } | |
317 | ||
6c14bbee | 318 | void 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 | ||
333 | void 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 |
d10644b9 | 347 | ExceededPingCongestionLimit = false; |
c5e4bfa1 | 348 | } |