Update pingcheck to work with cmake 3.28
[pingcheck] / src / link / linkstatus.cpp
... / ...
CommitLineData
1/*
2The software in this package is distributed under the GNU General
3Public License version 2 (with a special exception described below).
4
5A copy of GNU General Public License (GPL) is included in this distribution,
6in the file COPYING.GPL.
7
8As a special exception, if other files instantiate templates or use macros
9or inline functions from this file, or you compile this file and link it
10with other works to produce a work based on this file, this file
11does not by itself cause the resulting work to be covered
12by the GNU General Public License.
13
14However the source code for this file must still be made available
15in accordance with section (3) of the GNU General Public License.
16
17This exception does not invalidate any other reasons why a work based
18on 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 "dns/dnsmaster.h"
27
28#include "boost_assert_handler.h"
29
30using namespace std;
31using boost::posix_time::microsec_clock;
32using boost::posix_time::ptime;
33using I2n::Logger::GlobalLogger;
34
35//-----------------------------------------------------------------------------
36// LinkStatus
37//-----------------------------------------------------------------------------
38
39/**
40 * @brief Creates a link status object.
41 *
42 * @param hosts_down_limit The maximum amount of different hosts that can be
43 * down before the system take any action.
44 * @param link_up_interval_in_sec The amount of time required to the link to
45 * stay up before notify.
46 * @param link_down_interval_in_sec The amount of time required to the link to
47 * stay down before notify.
48 * @param status_notifier_cmd The command used to notify about link status
49 * changes.
50 */
51LinkStatus::LinkStatus(
52 const int hosts_down_limit,
53 const int link_up_interval_in_sec,
54 const int link_down_interval_in_sec,
55 const string &status_notifier_cmd
56) :
57 HostsDownLimit( hosts_down_limit ),
58 HostsDownList(),
59 LinkUpIntervalInSec( link_up_interval_in_sec ),
60 LinkDownIntervalInSec( link_down_interval_in_sec ),
61 CurrentLinkStatus( Status_Down ),
62 CurrentNotificationStatus( NotificationStatus_NotReported ),
63 TimeLinkStatusChanged( microsec_clock::universal_time() ),
64 StatusNotifierCmd( new StatusNotifierCommand( status_notifier_cmd ) )
65{
66 BOOST_ASSERT( 0 <= hosts_down_limit );
67 BOOST_ASSERT( 0 <= link_up_interval_in_sec );
68 BOOST_ASSERT( 0 <= link_down_interval_in_sec );
69}
70
71LinkStatus::~LinkStatus()
72{
73}
74
75std::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
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 */
115void LinkStatus::notify_host_up( const string &host_address )
116{
117 BOOST_ASSERT( !host_address.empty() );
118
119 bool has_changed = add_host_up( host_address );
120
121 if ( !exceeded_host_down_limit() )
122 {
123 notify_link_up();
124 }
125
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
133 // removed from the list?
134 BOOST_ASSERT( HostsDownList.count( host_address ) == 0 );
135} //lint !e1788
136
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 *
143 * @param host_address The DNS/IP address of the host that is down.
144 */
145void LinkStatus::notify_host_down( const string &host_address )
146{
147 BOOST_ASSERT( !host_address.empty() );
148
149 add_host_down( host_address );
150
151 if ( exceeded_host_down_limit() )
152 {
153 notify_link_down();
154 }
155
156 // report this always at notice level
157 GlobalLogger.notice() << log_prefix() << "down is "
158 << DnsMaster::get_cname_chain_str(host_address) << endl;
159
160 // inserted in the list?
161 BOOST_ASSERT( HostsDownList.count( host_address ) == 1 );
162} //lint !e1788
163
164// returns true if this did change something (i.e. host had been down)
165bool LinkStatus::add_host_up( const string &host_address )
166{
167 if ( HostsDownList.count( host_address ) > 0 )
168 {
169 size_t erased_host_count = HostsDownList.erase( host_address );
170
171 BOOST_ASSERT( erased_host_count == 1 );
172
173 return true;
174 }
175 else
176 return false;
177
178}
179
180void LinkStatus::add_host_down( const string &host_address )
181{
182 (void) HostsDownList.insert( host_address );
183}
184
185bool LinkStatus::exceeded_host_down_limit() const
186{
187 int host_down_count = static_cast<int>( HostsDownList.size() );
188
189 return ( host_down_count > HostsDownLimit );
190}
191
192void LinkStatus::notify_link_up()
193{
194 set_link_status( Status_Up );
195
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
198 if ( is_link_up_enough_time() && can_report_link_status() )
199 {
200 BOOST_ASSERT( CurrentLinkStatus == Status_Up );
201 BOOST_ASSERT( CurrentNotificationStatus == NotificationStatus_NotReported );
202
203 StatusNotifierCmd->set_token_value(
204 StatusNotifierCommand::StatusToken,
205 "up"
206 ); //lint !e534
207
208 GlobalLogger.notice() << log_prefix() << "report link up" << endl;
209 bool executed = StatusNotifierCmd->execute();
210
211 if ( executed )
212 {
213 CurrentNotificationStatus = NotificationStatus_Reported;
214 LastReportedStatus = CurrentLinkStatus;
215 }
216 }
217}
218
219void LinkStatus::notify_link_down()
220{
221 set_link_status( Status_Down );
222
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() )
226 {
227 BOOST_ASSERT( CurrentLinkStatus == Status_Down );
228 BOOST_ASSERT( CurrentNotificationStatus == NotificationStatus_NotReported );
229
230 GlobalLogger.notice() << log_prefix() << "report link down" << endl;
231 StatusNotifierCmd->set_token_value(
232 StatusNotifierCommand::StatusToken,
233 "down"
234 ); //lint !e534
235
236 bool executed = StatusNotifierCmd->execute();
237
238 if ( executed )
239 {
240 CurrentNotificationStatus = NotificationStatus_Reported;
241 LastReportedStatus = CurrentLinkStatus;
242 }
243 }
244}
245
246bool LinkStatus::is_link_up_enough_time() const
247{
248 if ( CurrentLinkStatus == Status_Up )
249 {
250 ptime now = microsec_clock::universal_time();
251 long amount_time_link_is_up = (now - TimeLinkStatusChanged).total_seconds();
252
253 if ( amount_time_link_is_up >= LinkUpIntervalInSec )
254 {
255 return true;
256 }
257 }
258
259 return false;
260}
261
262bool LinkStatus::is_link_down_enough_time() const
263{
264 if ( CurrentLinkStatus == Status_Down )
265 {
266 ptime now = microsec_clock::universal_time();
267 long amount_time_link_is_down = (now - TimeLinkStatusChanged).total_seconds();
268
269 if ( amount_time_link_is_down >= LinkDownIntervalInSec )
270 {
271 return true;
272 }
273 }
274
275 return false;
276}
277
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 */
286bool LinkStatus::can_report_link_status() const
287{
288 return ( CurrentNotificationStatus == NotificationStatus_NotReported
289 && LastReportedStatus != CurrentLinkStatus );
290}
291
292void LinkStatus::set_link_status(
293 const LinkStatus::Status new_link_status
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 }
305}