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 | */ | |
72e54d1c | 20 | #include "link/linkstatus.h" |
ddf41c89 GMF |
21 | |
22 | #include <iostream> | |
23 | ||
301610ca GMF |
24 | #include <logfunc.hpp> |
25 | ||
e638894d CH |
26 | #include "dns/dnsmaster.h" |
27 | ||
780b0bca CH |
28 | #include "boost_assert_handler.h" |
29 | ||
ddf41c89 | 30 | using namespace std; |
2bf8720f GMF |
31 | using boost::posix_time::microsec_clock; |
32 | using boost::posix_time::ptime; | |
301610ca | 33 | using I2n::Logger::GlobalLogger; |
ddf41c89 GMF |
34 | |
35 | //----------------------------------------------------------------------------- | |
72e54d1c | 36 | // LinkStatus |
ddf41c89 GMF |
37 | //----------------------------------------------------------------------------- |
38 | ||
5a3f6189 GMF |
39 | /** |
40 | * @brief Creates a link status object. | |
41 | * | |
72e54d1c | 42 | * @param hosts_down_limit The maximum amount of different hosts that can be |
5a3f6189 | 43 | * down before the system take any action. |
97837af8 | 44 | * @param link_up_interval_in_sec The amount of time required to the link to |
5a3f6189 | 45 | * stay up before notify. |
97837af8 | 46 | * @param link_down_interval_in_sec The amount of time required to the link to |
5a3f6189 | 47 | * stay down before notify. |
72e54d1c | 48 | * @param status_notifier_cmd The command used to notify about link status |
5a3f6189 GMF |
49 | * changes. |
50 | */ | |
72e54d1c | 51 | LinkStatus::LinkStatus( |
a341119a | 52 | const int hosts_down_limit, |
97837af8 CH |
53 | const int link_up_interval_in_sec, |
54 | const int link_down_interval_in_sec, | |
b279ae09 | 55 | const string &status_notifier_cmd |
c1fff16a | 56 | ) : |
a341119a | 57 | HostsDownLimit( hosts_down_limit ), |
f1bf3249 | 58 | HostsDownList(), |
97837af8 CH |
59 | LinkUpIntervalInSec( link_up_interval_in_sec ), |
60 | LinkDownIntervalInSec( link_down_interval_in_sec ), | |
72e54d1c | 61 | CurrentLinkStatus( Status_Down ), |
f1bf3249 GMF |
62 | CurrentNotificationStatus( NotificationStatus_NotReported ), |
63 | TimeLinkStatusChanged( microsec_clock::universal_time() ), | |
365036be | 64 | StatusNotifierCmd( new StatusNotifierCommand( status_notifier_cmd ) ) |
ddf41c89 | 65 | { |
a341119a | 66 | BOOST_ASSERT( 0 <= hosts_down_limit ); |
97837af8 CH |
67 | BOOST_ASSERT( 0 <= link_up_interval_in_sec ); |
68 | BOOST_ASSERT( 0 <= link_down_interval_in_sec ); | |
ddf41c89 GMF |
69 | } |
70 | ||
72e54d1c | 71 | LinkStatus::~LinkStatus() |
ddf41c89 GMF |
72 | { |
73 | } | |
74 | ||
353fb7f0 CH |
75 | std::string LinkStatus::log_prefix() const |
76 | { | |
77 | stringstream temp; | |
78 | temp << "Status (" << HostsDownList.size() << " down, " | |
79 | << "limit=" << HostsDownLimit << ", "; | |
80 | if ( can_report_link_status() ) | |
81 | { | |
82 | ptime now = microsec_clock::universal_time(); | |
83 | long seconds_missing; | |
84 | if ( CurrentLinkStatus == Status_Down ) | |
85 | { | |
86 | seconds_missing = LinkDownIntervalInSec | |
87 | - (now - TimeLinkStatusChanged).total_seconds(); | |
88 | temp << "notify down "; | |
89 | } | |
90 | else // (assume CurrentLinkStatus == Status_Up) | |
91 | { | |
92 | seconds_missing = LinkUpIntervalInSec | |
93 | - (now - TimeLinkStatusChanged).total_seconds(); | |
94 | temp << "notify up "; | |
95 | } | |
96 | if (seconds_missing < 0) | |
97 | temp << "now"; | |
98 | else | |
99 | temp << "in " << seconds_missing << "s"; | |
100 | } | |
101 | else | |
102 | temp << "no notify"; | |
103 | temp << "): "; | |
104 | return temp.str(); | |
105 | } | |
106 | ||
5a3f6189 GMF |
107 | /** |
108 | * @brief Notify the system that a given host is up. The object takes an | |
109 | * appropriated action to deal with that. | |
110 | * Note: this object does not resolves IPs, thus you have to send the same host | |
111 | * address in order to the object to consider the same host. | |
112 | * | |
113 | * @param host_address the DNS/IP address of the host that is up. | |
114 | */ | |
72e54d1c | 115 | void LinkStatus::notify_host_up( const string &host_address ) |
ddf41c89 | 116 | { |
c1fff16a GMF |
117 | BOOST_ASSERT( !host_address.empty() ); |
118 | ||
f5ffc5df | 119 | bool has_changed = add_host_up( host_address ); |
c1fff16a | 120 | |
a341119a | 121 | if ( !exceeded_host_down_limit() ) |
1266407a | 122 | { |
fb469ffa | 123 | notify_link_up(); |
1266407a GMF |
124 | } |
125 | ||
353fb7f0 CH |
126 | if (has_changed) |
127 | GlobalLogger.notice() << log_prefix() << "now up again is " | |
128 | << DnsMaster::get_cname_chain_str(host_address) << endl; | |
129 | else // less important so log only at info level | |
130 | GlobalLogger.info() << log_prefix() << "still up is " | |
131 | << DnsMaster::get_cname_chain_str(host_address) << endl; | |
132 | ||
b279ae09 | 133 | // removed from the list? |
1266407a | 134 | BOOST_ASSERT( HostsDownList.count( host_address ) == 0 ); |
529e5587 | 135 | } //lint !e1788 |
ddf41c89 | 136 | |
5a3f6189 GMF |
137 | /** |
138 | * @brief Notify the system that a given host is down. The object takes an | |
139 | * appropriated action to deal with that. | |
140 | * Note: this object does not resolves IPs, thus you have to send the same host | |
141 | * address in order to the object to consider the same host. | |
142 | * | |
72e54d1c | 143 | * @param host_address The DNS/IP address of the host that is down. |
5a3f6189 | 144 | */ |
72e54d1c | 145 | void LinkStatus::notify_host_down( const string &host_address ) |
ddf41c89 | 146 | { |
c1fff16a GMF |
147 | BOOST_ASSERT( !host_address.empty() ); |
148 | ||
1266407a | 149 | add_host_down( host_address ); |
c1fff16a | 150 | |
a341119a | 151 | if ( exceeded_host_down_limit() ) |
1266407a | 152 | { |
fb469ffa | 153 | notify_link_down(); |
1266407a | 154 | } |
c1fff16a | 155 | |
353fb7f0 CH |
156 | // report this always at notice level |
157 | GlobalLogger.notice() << log_prefix() << "down is " | |
158 | << DnsMaster::get_cname_chain_str(host_address) << endl; | |
159 | ||
b279ae09 | 160 | // inserted in the list? |
1266407a | 161 | BOOST_ASSERT( HostsDownList.count( host_address ) == 1 ); |
529e5587 | 162 | } //lint !e1788 |
c1fff16a | 163 | |
f5ffc5df CH |
164 | // returns true if this did change something (i.e. host had been down) |
165 | bool LinkStatus::add_host_up( const string &host_address ) | |
1266407a GMF |
166 | { |
167 | if ( HostsDownList.count( host_address ) > 0 ) | |
168 | { | |
a4049623 GMF |
169 | size_t erased_host_count = HostsDownList.erase( host_address ); |
170 | ||
171 | BOOST_ASSERT( erased_host_count == 1 ); | |
f5ffc5df CH |
172 | |
173 | return true; | |
1266407a | 174 | } |
f5ffc5df CH |
175 | else |
176 | return false; | |
177 | ||
1266407a | 178 | } |
c1fff16a | 179 | |
72e54d1c | 180 | void LinkStatus::add_host_down( const string &host_address ) |
1266407a | 181 | { |
a4049623 | 182 | (void) HostsDownList.insert( host_address ); |
1266407a | 183 | } |
c1fff16a | 184 | |
72e54d1c | 185 | bool LinkStatus::exceeded_host_down_limit() const |
1266407a | 186 | { |
cf289c98 | 187 | int host_down_count = static_cast<int>( HostsDownList.size() ); |
6cfbd37c | 188 | |
a341119a | 189 | return ( host_down_count > HostsDownLimit ); |
ddf41c89 | 190 | } |
b279ae09 | 191 | |
72e54d1c | 192 | void LinkStatus::notify_link_up() |
b279ae09 | 193 | { |
72e54d1c | 194 | set_link_status( Status_Up ); |
b279ae09 | 195 | |
1634f2a1 GMF |
196 | // report the link status only if: it is up longer than a configured amount |
197 | // of time, and if we haven't reported the new status yet | |
f1bf3249 GMF |
198 | if ( is_link_up_enough_time() && can_report_link_status() ) |
199 | { | |
72e54d1c | 200 | BOOST_ASSERT( CurrentLinkStatus == Status_Up ); |
f1bf3249 GMF |
201 | BOOST_ASSERT( CurrentNotificationStatus == NotificationStatus_NotReported ); |
202 | ||
709ded82 | 203 | StatusNotifierCmd->set_token_value( |
f1bf3249 GMF |
204 | StatusNotifierCommand::StatusToken, |
205 | "up" | |
2a686bc0 | 206 | ); //lint !e534 |
f1bf3249 | 207 | |
353fb7f0 | 208 | GlobalLogger.notice() << log_prefix() << "report link up" << endl; |
709ded82 | 209 | bool executed = StatusNotifierCmd->execute(); |
f1bf3249 | 210 | |
2a686bc0 | 211 | if ( executed ) |
a4049623 GMF |
212 | { |
213 | CurrentNotificationStatus = NotificationStatus_Reported; | |
353fb7f0 | 214 | LastReportedStatus = CurrentLinkStatus; |
a4049623 | 215 | } |
f1bf3249 | 216 | } |
b279ae09 GMF |
217 | } |
218 | ||
72e54d1c | 219 | void LinkStatus::notify_link_down() |
b279ae09 | 220 | { |
72e54d1c | 221 | set_link_status( Status_Down ); |
f1bf3249 | 222 | |
1634f2a1 GMF |
223 | // report the link status only if: it is down longer than a configured amount |
224 | // of time, and if we haven't reported the new status yet | |
225 | if ( is_link_down_enough_time() && can_report_link_status() ) | |
f1bf3249 | 226 | { |
72e54d1c | 227 | BOOST_ASSERT( CurrentLinkStatus == Status_Down ); |
f1bf3249 | 228 | BOOST_ASSERT( CurrentNotificationStatus == NotificationStatus_NotReported ); |
b279ae09 | 229 | |
353fb7f0 | 230 | GlobalLogger.notice() << log_prefix() << "report link down" << endl; |
709ded82 | 231 | StatusNotifierCmd->set_token_value( |
f1bf3249 GMF |
232 | StatusNotifierCommand::StatusToken, |
233 | "down" | |
2a686bc0 | 234 | ); //lint !e534 |
f1bf3249 | 235 | |
709ded82 | 236 | bool executed = StatusNotifierCmd->execute(); |
f1bf3249 | 237 | |
2a686bc0 | 238 | if ( executed ) |
a4049623 GMF |
239 | { |
240 | CurrentNotificationStatus = NotificationStatus_Reported; | |
353fb7f0 | 241 | LastReportedStatus = CurrentLinkStatus; |
a4049623 | 242 | } |
f1bf3249 GMF |
243 | } |
244 | } | |
245 | ||
72e54d1c | 246 | bool LinkStatus::is_link_up_enough_time() const |
f1bf3249 | 247 | { |
72e54d1c | 248 | if ( CurrentLinkStatus == Status_Up ) |
f1bf3249 GMF |
249 | { |
250 | ptime now = microsec_clock::universal_time(); | |
251 | long amount_time_link_is_up = (now - TimeLinkStatusChanged).total_seconds(); | |
f1bf3249 | 252 | |
97837af8 | 253 | if ( amount_time_link_is_up >= LinkUpIntervalInSec ) |
f1bf3249 GMF |
254 | { |
255 | return true; | |
256 | } | |
257 | } | |
258 | ||
259 | return false; | |
260 | } | |
261 | ||
72e54d1c | 262 | bool LinkStatus::is_link_down_enough_time() const |
1634f2a1 | 263 | { |
72e54d1c | 264 | if ( CurrentLinkStatus == Status_Down ) |
1634f2a1 GMF |
265 | { |
266 | ptime now = microsec_clock::universal_time(); | |
267 | long amount_time_link_is_down = (now - TimeLinkStatusChanged).total_seconds(); | |
1634f2a1 | 268 | |
97837af8 | 269 | if ( amount_time_link_is_down >= LinkDownIntervalInSec ) |
1634f2a1 GMF |
270 | { |
271 | return true; | |
272 | } | |
273 | } | |
274 | ||
275 | return false; | |
276 | } | |
277 | ||
353fb7f0 CH |
278 | /** |
279 | * @brief determine if we should report the current link status | |
280 | * | |
281 | * checks if the current status has already been reported; does not take | |
282 | * LinkUp/DownInterval into account | |
283 | * | |
284 | * @returns true if status should be reported | |
285 | */ | |
72e54d1c | 286 | bool LinkStatus::can_report_link_status() const |
f1bf3249 | 287 | { |
353fb7f0 CH |
288 | return ( CurrentNotificationStatus == NotificationStatus_NotReported |
289 | && LastReportedStatus != CurrentLinkStatus ); | |
f1bf3249 GMF |
290 | } |
291 | ||
72e54d1c GMF |
292 | void LinkStatus::set_link_status( |
293 | const LinkStatus::Status new_link_status | |
f1bf3249 GMF |
294 | ) |
295 | { | |
296 | // only reset the control flags if the link status has changed | |
297 | if ( new_link_status != CurrentLinkStatus ) | |
298 | { | |
299 | CurrentLinkStatus = new_link_status; | |
300 | TimeLinkStatusChanged = microsec_clock::universal_time(); | |
301 | ||
302 | // have to report the link status change | |
303 | CurrentNotificationStatus = NotificationStatus_NotReported; | |
304 | } | |
b279ae09 | 305 | } |