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