2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
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.
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.
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.
20 #include "link/linkstatus.h"
24 #include <logfunc.hpp>
26 #include "dns/dnsmaster.h"
28 #include "boost_assert_handler.h"
31 using boost::posix_time::microsec_clock;
32 using boost::posix_time::ptime;
33 using I2n::Logger::GlobalLogger;
35 //-----------------------------------------------------------------------------
37 //-----------------------------------------------------------------------------
40 * @brief Creates a link status object.
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
51 LinkStatus::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
57 HostsDownLimit( hosts_down_limit ),
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 ) )
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 );
71 LinkStatus::~LinkStatus()
75 std::string LinkStatus::log_prefix() const
78 temp << "Status (" << HostsDownList.size() << " down, "
79 << "limit=" << HostsDownLimit << ", ";
80 if ( can_report_link_status() )
82 ptime now = microsec_clock::universal_time();
84 if ( CurrentLinkStatus == Status_Down )
86 seconds_missing = LinkDownIntervalInSec
87 - (now - TimeLinkStatusChanged).total_seconds();
88 temp << "notify down ";
90 else // (assume CurrentLinkStatus == Status_Up)
92 seconds_missing = LinkUpIntervalInSec
93 - (now - TimeLinkStatusChanged).total_seconds();
96 if (seconds_missing < 0)
99 temp << "in " << seconds_missing << "s";
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.
113 * @param host_address the DNS/IP address of the host that is up.
115 void LinkStatus::notify_host_up( const string &host_address )
117 BOOST_ASSERT( !host_address.empty() );
119 bool has_changed = add_host_up( host_address );
121 if ( !exceeded_host_down_limit() )
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;
133 // removed from the list?
134 BOOST_ASSERT( HostsDownList.count( host_address ) == 0 );
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.
143 * @param host_address The DNS/IP address of the host that is down.
145 void LinkStatus::notify_host_down( const string &host_address )
147 BOOST_ASSERT( !host_address.empty() );
149 add_host_down( host_address );
151 if ( exceeded_host_down_limit() )
156 // report this always at notice level
157 GlobalLogger.notice() << log_prefix() << "down is "
158 << DnsMaster::get_cname_chain_str(host_address) << endl;
160 // inserted in the list?
161 BOOST_ASSERT( HostsDownList.count( host_address ) == 1 );
164 // returns true if this did change something (i.e. host had been down)
165 bool LinkStatus::add_host_up( const string &host_address )
167 if ( HostsDownList.count( host_address ) > 0 )
169 size_t erased_host_count = HostsDownList.erase( host_address );
171 BOOST_ASSERT( erased_host_count == 1 );
180 void LinkStatus::add_host_down( const string &host_address )
182 (void) HostsDownList.insert( host_address );
185 bool LinkStatus::exceeded_host_down_limit() const
187 int host_down_count = static_cast<int>( HostsDownList.size() );
189 return ( host_down_count > HostsDownLimit );
192 void LinkStatus::notify_link_up()
194 set_link_status( Status_Up );
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() )
200 BOOST_ASSERT( CurrentLinkStatus == Status_Up );
201 BOOST_ASSERT( CurrentNotificationStatus == NotificationStatus_NotReported );
203 StatusNotifierCmd->set_token_value(
204 StatusNotifierCommand::StatusToken,
208 GlobalLogger.notice() << log_prefix() << "report link up" << endl;
209 bool executed = StatusNotifierCmd->execute();
213 CurrentNotificationStatus = NotificationStatus_Reported;
214 LastReportedStatus = CurrentLinkStatus;
219 void LinkStatus::notify_link_down()
221 set_link_status( Status_Down );
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() )
227 BOOST_ASSERT( CurrentLinkStatus == Status_Down );
228 BOOST_ASSERT( CurrentNotificationStatus == NotificationStatus_NotReported );
230 GlobalLogger.notice() << log_prefix() << "report link down" << endl;
231 StatusNotifierCmd->set_token_value(
232 StatusNotifierCommand::StatusToken,
236 bool executed = StatusNotifierCmd->execute();
240 CurrentNotificationStatus = NotificationStatus_Reported;
241 LastReportedStatus = CurrentLinkStatus;
246 bool LinkStatus::is_link_up_enough_time() const
248 if ( CurrentLinkStatus == Status_Up )
250 ptime now = microsec_clock::universal_time();
251 long amount_time_link_is_up = (now - TimeLinkStatusChanged).total_seconds();
253 if ( amount_time_link_is_up >= LinkUpIntervalInSec )
262 bool LinkStatus::is_link_down_enough_time() const
264 if ( CurrentLinkStatus == Status_Down )
266 ptime now = microsec_clock::universal_time();
267 long amount_time_link_is_down = (now - TimeLinkStatusChanged).total_seconds();
269 if ( amount_time_link_is_down >= LinkDownIntervalInSec )
279 * @brief determine if we should report the current link status
281 * checks if the current status has already been reported; does not take
282 * LinkUp/DownInterval into account
284 * @returns true if status should be reported
286 bool LinkStatus::can_report_link_status() const
288 return ( CurrentNotificationStatus == NotificationStatus_NotReported
289 && LastReportedStatus != CurrentLinkStatus );
292 void LinkStatus::set_link_status(
293 const LinkStatus::Status new_link_status
296 // only reset the control flags if the link status has changed
297 if ( new_link_status != CurrentLinkStatus )
299 CurrentLinkStatus = new_link_status;
300 TimeLinkStatusChanged = microsec_clock::universal_time();
302 // have to report the link status change
303 CurrentNotificationStatus = NotificationStatus_NotReported;