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