fa500a644c9fb5083f23804e860459d59d2618b3
[pingcheck] / src / link / linkstatus.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 "link/linkstatus.h"
21
22 #include <iostream>
23
24 #include <logfunc.hpp>
25
26 #include "boost_assert_handler.h"
27
28 using namespace std;
29 using boost::posix_time::microsec_clock;
30 using boost::posix_time::ptime;
31 using I2n::Logger::GlobalLogger;
32
33 //-----------------------------------------------------------------------------
34 // LinkStatus
35 //-----------------------------------------------------------------------------
36
37 /**
38  * @brief Creates a link status object.
39  *
40  * @param hosts_down_limit The maximum amount of different hosts that can be
41  * down before the system take any action.
42  * @param link_up_interval_in_min The amount of time required to the link to
43  * stay up before notify.
44  * @param link_down_interval_in_min The amount of time required to the link to
45  * stay down before notify.
46  * @param status_notifier_cmd The command used to notify about link status
47  * changes.
48  */
49 LinkStatus::LinkStatus(
50         const int hosts_down_limit,
51         const int link_up_interval_in_min,
52         const int link_down_interval_in_min,
53         const string &status_notifier_cmd
54 ) :
55     HostsDownLimit( hosts_down_limit ),
56     HostsDownList(),
57     LinkUpIntervalInMin( link_up_interval_in_min ),
58     LinkDownIntervalInMin( link_down_interval_in_min ),
59     CurrentLinkStatus( Status_Down ),
60     CurrentNotificationStatus( NotificationStatus_NotReported ),
61     TimeLinkStatusChanged( microsec_clock::universal_time() ),
62     StatusNotifierCmd( new StatusNotifierCommand( status_notifier_cmd ) )
63 {
64     BOOST_ASSERT( 0 <= hosts_down_limit );
65     BOOST_ASSERT( 0 <= link_up_interval_in_min );
66     BOOST_ASSERT( 0 <= link_down_interval_in_min );
67 }
68
69 LinkStatus::~LinkStatus()
70 {
71 }
72
73 /**
74  * @brief Notify the system that a given host is up. The object takes an
75  * appropriated action to deal with that.
76  * Note: this object does not resolves IPs, thus you have to send the same host
77  * address in order to the object to consider the same host.
78  *
79  * @param host_address the DNS/IP address of the host that is up.
80  */
81 void LinkStatus::notify_host_up( const string &host_address )
82 {
83     BOOST_ASSERT( !host_address.empty() );
84
85     GlobalLogger.info() << "- Host up: " << host_address << endl;
86
87     add_host_up( host_address );
88
89     if ( !exceeded_host_down_limit() )
90     {
91         notify_link_up();
92     }
93
94     // removed from the list?
95     BOOST_ASSERT( HostsDownList.count( host_address ) == 0 );
96 } //lint !e1788
97
98 /**
99  * @brief Notify the system that a given host is down. The object takes an
100  * appropriated action to deal with that.
101  * Note: this object does not resolves IPs, thus you have to send the same host
102  * address in order to the object to consider the same host.
103  *
104  * @param host_address The DNS/IP address of the host that is down.
105  */
106 void LinkStatus::notify_host_down( const string &host_address )
107 {
108     BOOST_ASSERT( !host_address.empty() );
109
110     GlobalLogger.info() << "- Host down: " << host_address << endl;
111
112     add_host_down( host_address );
113
114     if ( exceeded_host_down_limit() )
115     {
116         notify_link_down();
117     }
118
119     // inserted in the list?
120     BOOST_ASSERT( HostsDownList.count( host_address ) == 1 );
121 } //lint !e1788
122
123 void LinkStatus::add_host_up( const string &host_address )
124 {
125     if ( HostsDownList.count( host_address ) > 0 )
126     {
127         size_t erased_host_count = HostsDownList.erase( host_address );
128
129         BOOST_ASSERT( erased_host_count == 1 );
130     }
131 }
132
133 void LinkStatus::add_host_down( const string &host_address )
134 {
135     (void) HostsDownList.insert( host_address );
136 }
137
138 bool LinkStatus::exceeded_host_down_limit() const
139 {
140     int host_down_count = static_cast<int>( HostsDownList.size() );
141
142     return ( host_down_count > HostsDownLimit );
143 }
144
145 void LinkStatus::notify_link_up()
146 {
147     set_link_status( Status_Up );
148
149     // report the link status only if: it is up longer than a configured amount
150     // of time, and if we haven't reported the new status yet
151     if ( is_link_up_enough_time() && can_report_link_status() )
152     {
153         BOOST_ASSERT( CurrentLinkStatus == Status_Up );
154         BOOST_ASSERT( CurrentNotificationStatus == NotificationStatus_NotReported );
155
156         StatusNotifierCmd->set_token_value(
157                 StatusNotifierCommand::StatusToken,
158                 "up"
159         );                                                                          //lint !e534
160
161         bool executed = StatusNotifierCmd->execute();
162
163         if ( executed )
164         {
165             CurrentNotificationStatus = NotificationStatus_Reported;
166         }
167     }
168 }
169
170 void LinkStatus::notify_link_down()
171 {
172     set_link_status( Status_Down );
173
174     // report the link status only if: it is down longer than a configured amount
175     // of time, and if we haven't reported the new status yet
176     if ( is_link_down_enough_time() && can_report_link_status() )
177     {
178         BOOST_ASSERT( CurrentLinkStatus == Status_Down );
179         BOOST_ASSERT( CurrentNotificationStatus == NotificationStatus_NotReported );
180
181         StatusNotifierCmd->set_token_value(
182                 StatusNotifierCommand::StatusToken,
183                 "down"
184         );                                                                              //lint !e534
185
186         bool executed = StatusNotifierCmd->execute();
187
188         if ( executed )
189         {
190             CurrentNotificationStatus = NotificationStatus_Reported;
191         }
192     }
193 }
194
195 bool LinkStatus::is_link_up_enough_time() const
196 {
197     if ( CurrentLinkStatus == Status_Up )
198     {
199         ptime now = microsec_clock::universal_time();
200         long amount_time_link_is_up = (now - TimeLinkStatusChanged).total_seconds();
201         long link_up_interval_in_sec = LinkUpIntervalInMin * 60;
202
203         if ( amount_time_link_is_up >= link_up_interval_in_sec )
204         {
205             return true;
206         }
207     }
208
209     return false;
210 }
211
212 bool LinkStatus::is_link_down_enough_time() const
213 {
214     if ( CurrentLinkStatus == Status_Down )
215     {
216         ptime now = microsec_clock::universal_time();
217         long amount_time_link_is_down = (now - TimeLinkStatusChanged).total_seconds();
218         long link_down_interval_in_sec = LinkDownIntervalInMin * 60;
219
220         if ( amount_time_link_is_down >= link_down_interval_in_sec )
221         {
222             return true;
223         }
224     }
225
226     return false;
227 }
228
229 bool LinkStatus::can_report_link_status() const
230 {
231     return ( CurrentNotificationStatus == NotificationStatus_NotReported );
232 }
233
234 void LinkStatus::set_link_status(
235         const LinkStatus::Status new_link_status
236 )
237 {
238     // only reset the control flags if the link status has changed
239     if ( new_link_status != CurrentLinkStatus )
240     {
241         CurrentLinkStatus = new_link_status;
242         TimeLinkStatusChanged = microsec_clock::universal_time();
243
244         // have to report the link status change
245         CurrentNotificationStatus = NotificationStatus_NotReported;
246     }
247 }