use line digestion recognition in PingScheduler;
[pingcheck] / src / host / hoststatus.cpp
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 */
20 #include "host/hoststatus.h"
21
22 #include <iostream>
23 #include <logfunc.hpp>
24
25 #include "boost_assert_handler.h"
26
27 using namespace std;
28 using I2n::Logger::GlobalLogger;
29
30 //-----------------------------------------------------------------------------
31 // HostStatus
32 //-----------------------------------------------------------------------------
33
34 /**
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
37  * fail.
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.
44  */
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
52 ) :
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 ),
58     ResolvedIpCount( 0 ),
59     PingsPerformedCount( 0 ),
60     PingsFailedCount( 0 ),
61     PingCongestionCount( 0 ),
62     ExceededPingFailedLimit( false ),
63     ExceededPingCongestionLimit( false ),
64     NParallelPingers( n_parallel_pings)
65 {
66     BOOST_ASSERT( !HostAddress.empty() );
67     BOOST_ASSERT( ( 0 <= PingFailLimitPercentage )
68                     && ( PingFailLimitPercentage <= 100 ) );
69     BOOST_ASSERT( ( 0 <= PingCongestionLimitPercentage )
70                     && ( PingCongestionLimitPercentage <= 100 ) );
71 }
72
73 HostStatus::~HostStatus()
74 {
75 }
76
77
78 void HostStatus::set_n_parallel_pings(const int n_parallel_pings)
79 {
80     if (NParallelPingers != n_parallel_pings)
81     {
82         NParallelPingers = n_parallel_pings;
83         reset_ping_counters();
84     }
85     GlobalLogger.debug() << log_prefix() << "#pingers set";
86 }
87
88
89 std::string HostStatus::log_prefix()
90 {
91     std::stringstream temp;
92     temp << "Stat(" << HostAddress << "): "
93         << PingsFailedCount << " fail," << PingCongestionCount << " cong/"
94         << PingsPerformedCount << " pings/" << ResolvedIpCount << "*"
95         << NParallelPingers << " IPs: ";
96     return temp.str();
97 }
98
99 /**
100  * @param resolved_ip_count The number of IPs resolved for the host.
101  */
102 void HostStatus::set_resolved_ip_count( const int resolved_ip_count )
103 {
104     BOOST_ASSERT( 0 <= resolved_ip_count );
105
106     if (resolved_ip_count != ResolvedIpCount)
107     {   // assume that the target has changed --> reset counters
108         reset_ping_counters();
109     }
110     ResolvedIpCount = resolved_ip_count;
111
112     GlobalLogger.debug() << log_prefix() << "#IPs set";
113 }
114
115 /**
116  * @return true if the amount of failed pings given to the host exceeded the
117  * limit.
118  */
119 bool HostStatus::exceeded_ping_failed_limit() const
120 {
121     return ExceededPingFailedLimit;
122 }
123
124 /**
125  * @return true if the amount of congested pings given to the host exceeded the
126  * limit.
127  */
128 bool HostStatus::exceeded_ping_congestion_limit() const
129 {
130     return ExceededPingCongestionLimit;
131 }
132
133 /**
134  * Tells the status analyzer how the last ping went
135  *
136  * @param result: status of ping specifying success/failure and reason of fail
137  * @param ping_duration_us duration of ping in micro seconds
138  */
139 void HostStatus::update_ping_statistics( const PingStatus &result,
140                                          const long ping_duration_us )
141 {
142     float ping_duration_ms = static_cast<float>(ping_duration_us) / 1000.0f;
143
144     GlobalLogger.debug() << log_prefix() << "add ping with result "
145         << to_string(result) << " which took " << ping_duration_ms << " ms";
146
147     BOOST_ASSERT( 1 <= ResolvedIpCount );
148     BOOST_ASSERT( 0 <= PingsPerformedCount );
149     BOOST_ASSERT( PingsFailedCount <= PingsPerformedCount );
150     BOOST_ASSERT( PingCongestionCount <= PingsPerformedCount );
151
152     update_fail_stats( result );
153     update_congestion_stats( result, ping_duration_us );
154
155     // after we tried all IPs resolved for this host, we can analyze how many
156     // failed
157     if ( tried_all_resolved_ip() )
158     {
159         analyze_ping_statistics();
160
161         reset_ping_counters();
162     }
163
164     BOOST_ASSERT( PingsFailedCount <= PingsPerformedCount );
165     BOOST_ASSERT( PingCongestionCount <= PingsPerformedCount );
166 }
167
168
169 void HostStatus::update_fail_stats( const PingStatus &result)
170 {
171     increase_ping_performed_count();
172
173     if ( result != PingStatus_SuccessReply
174       && result != PingStatus_SuccessOutdatedIP)
175     {
176         increase_ping_failed_count();
177     }
178
179     analyze_ping_failed_count();
180 }
181
182
183 void HostStatus::update_congestion_stats( const PingStatus &result,
184                                           const long ping_duration_us )
185 {
186     if (ping_duration_us > PingDurationCongestionsThresh)
187         increase_ping_congestion_count();
188     else if ( result == PingStatus_FailureTimeout )
189         increase_ping_congestion_count();
190     // PingStatus_FailureNoIP, PingStatus_SuccessOutdatedIP could also be caused
191     // by congestion, but also by other reasons (e.g. firewall blocking port 53)
192
193     analyze_ping_congestion_count();
194 }
195
196
197 bool HostStatus::tried_all_resolved_ip() const
198 {
199     BOOST_ASSERT( ( 0 < PingsPerformedCount ) &&
200                   ( PingsPerformedCount <= ResolvedIpCount*NParallelPingers ) );
201
202     return ( PingsPerformedCount == ResolvedIpCount*NParallelPingers );
203 }
204
205 void HostStatus::analyze_ping_statistics()
206 {
207     BOOST_ASSERT( !HostAddress.empty() );
208     BOOST_ASSERT( PingsPerformedCount == ResolvedIpCount*NParallelPingers );
209
210     // notify if the amount of pings that failed exceed the limit
211     if ( exceeded_ping_failed_limit() )
212     {
213         GlobalLogger.debug() << log_prefix() << "notify down";
214         LinkAnalyzer->notify_host_down( HostAddress );
215     }
216     else
217     {
218         GlobalLogger.debug() << log_prefix() << "notify up";
219         LinkAnalyzer->notify_host_up( HostAddress );
220     }
221
222     // nothing to do about congestion here, congestion is not forwarded to
223     // central LinkAnalyzer
224 } //lint !e1762
225
226 void HostStatus::reset_ping_counters()
227 {
228     PingsPerformedCount = 0;
229     PingsFailedCount = 0;
230     PingCongestionCount = 0;
231 }
232
233 void HostStatus::increase_ping_performed_count()
234 {
235     ++PingsPerformedCount;
236
237     BOOST_ASSERT( ( 0 < PingsPerformedCount ) &&
238                   ( PingsPerformedCount <= ResolvedIpCount*NParallelPingers ) );
239 }
240
241 void HostStatus::increase_ping_failed_count()
242 {
243     ++PingsFailedCount;
244
245     BOOST_ASSERT( ( 0 <= PingsFailedCount ) && ( PingsFailedCount <= PingsPerformedCount ) );
246 }
247
248 void HostStatus::increase_ping_congestion_count()
249 {
250     ++PingCongestionCount;
251
252     BOOST_ASSERT( ( 0 <= PingCongestionCount )
253                     && ( PingCongestionCount <= PingsPerformedCount ) );
254 }
255
256 void HostStatus::analyze_ping_failed_count()
257 {
258     BOOST_ASSERT( ( 0 <= PingFailLimitPercentage ) && ( PingFailLimitPercentage <= 100 ) );
259     BOOST_ASSERT( ( 0 <= PingsFailedCount ) && ( PingsFailedCount <= PingsPerformedCount ) );
260
261     int ping_fail_limit_count = ( ResolvedIpCount * PingFailLimitPercentage
262                                                   * NParallelPingers) / 100;
263
264     // keep a boolean variable because the PingsFailedCount can be reseted
265     if ( PingsFailedCount > ping_fail_limit_count )
266     {
267         ExceededPingFailedLimit = true;
268
269         GlobalLogger.debug() << log_prefix() << "exceed fail limit="
270                              << ping_fail_limit_count;
271     }
272     else
273     {
274         ExceededPingFailedLimit = false;
275
276         GlobalLogger.debug() << log_prefix() << "below fail limit="
277                              << ping_fail_limit_count;
278     }
279 }
280
281 void HostStatus::analyze_ping_congestion_count()
282 {
283     BOOST_ASSERT( ( 0 <= PingCongestionLimitPercentage )
284                     && ( PingCongestionLimitPercentage <= 100 ) );
285     BOOST_ASSERT( ( 0 <= PingCongestionCount )
286                     && ( PingCongestionCount <= PingsPerformedCount ) );
287
288     int ping_congestion_limit_count = ( ResolvedIpCount * NParallelPingers
289                                         * PingCongestionLimitPercentage ) / 100;
290
291     // keep a boolean variable because the PingCongestionCount can be reseted
292     if ( PingCongestionCount > ping_congestion_limit_count )
293     {
294         ExceededPingCongestionLimit = true;
295
296         GlobalLogger.debug() << log_prefix() << "exceed congestion limit="
297                              << ping_congestion_limit_count;
298     }
299     else
300     {
301         ExceededPingCongestionLimit = false;
302
303         GlobalLogger.debug() << log_prefix() << "below congestion limit="
304                              << ping_congestion_limit_count;
305     }
306 }