removed assertion in HostStatus that PingsPerformedCount <= ResolvedIpCount*NParallel...
[pingcheck] / src / host / pingscheduler.cpp
CommitLineData
91fcc471
TJ
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*/
8f66f529 20#include "host/pingscheduler.h"
9c55ecd3 21
0d46491b 22#include <iostream>
1309d0e4 23#include <limits>
0d46491b 24
9c55ecd3 25#include <boost/bind.hpp>
26b0f687 26#include <boost/foreach.hpp>
0d46491b 27
301610ca
GMF
28#include <logfunc.hpp>
29
780b0bca 30#include "boost_assert_handler.h"
086e2cc0 31#include "host/pingerfactory.h"
26b0f687 32#include "dns/dnsmaster.h"
51cbc790 33#include "icmp/icmppinger.h"
72e54d1c 34#include "link/linkstatus.h"
ced28dc7 35
a7c2eb51 36using namespace std;
2bf8720f
GMF
37using boost::asio::io_service;
38using boost::bind;
101be5ce 39using boost::date_time::time_resolution_traits_adapted64_impl;
2bf8720f
GMF
40using boost::posix_time::microsec_clock;
41using boost::posix_time::ptime;
42using boost::posix_time::seconds;
89f153af 43using boost::posix_time::milliseconds;
e58d7507 44using boost::shared_ptr;
301610ca 45using I2n::Logger::GlobalLogger;
a7c2eb51 46
4c2a5ab5 47//-----------------------------------------------------------------------------
4bb97b45 48// PingScheduler
4c2a5ab5
GMF
49//-----------------------------------------------------------------------------
50
086e2cc0
GMF
51/**
52 * @brief Parameterized constructor.
53 *
ab2cb1ef 54 * @param io_serv The one @c io_serv object that controls async processing
c1abff61 55 * @param network_interface The name of the network interface sending the pings.
086e2cc0
GMF
56 * @param destination_address The remote address to ping.
57 * @param destination_port The remote port to ping.
58 * @param ping_protocol_list A list of protocols to use.
59 * @param ping_interval_in_sec Amount of time between each ping.
60 * @param ping_fail_percentage_limit Maximum amount of pings that can fail.
a7b15639
CH
61 * @param ping_congestion_percentage_limit Amount of pings indication congested
62 * line
63 * @param ping_congestion_duration_thresh Duration in seconds that indicates a
64 * congested line
91aa83f9 65 * @param ping_reply_timeout Max amount time to wait for ping to finish
086e2cc0 66 * @param link_analyzer The object to monitor the link status.
365036be 67 * @param first_delay Delay in seconds from start_pinging to first ping attempt
91aa83f9 68 * @param n_parallel_pings: Number of pingers to ping the same IP in parallel
086e2cc0 69 */
4bb97b45 70PingScheduler::PingScheduler(
ab2cb1ef 71 const IoServiceItem io_serv,
2bf8720f
GMF
72 const string &network_interface,
73 const string &destination_address,
238da857 74 const uint16_t destination_port,
fe6a2f80 75 const PingProtocolList &ping_protocol_list,
c15a722d 76 const long ping_interval_in_sec,
a341119a 77 const int ping_fail_percentage_limit,
a7b15639
CH
78 const int ping_congestion_percentage_limit,
79 const int ping_congestion_duration_thresh,
079d19ab 80 const int ping_reply_timeout,
59733431 81 LinkStatusItem link_analyzer,
91aa83f9 82 const int first_delay,
89f153af 83 const int n_parallel_pings,
91aa83f9 84 const int parallel_ping_delay
e39cc3da 85) :
23f51766
CH
86 IoService( io_serv ),
87 NetworkInterfaceName( network_interface ),
23f51766
CH
88 DestinationAddress( destination_address ),
89 DestinationPort( destination_port ),
26b0f687
CH
90 Protocols( ping_protocol_list ),
91 ProtocolIter(),
92 PingIntervalInSec( ping_interval_in_sec ),
242e5fb3 93 NPingers( n_parallel_pings ),
26b0f687 94 FirstDelay( first_delay ),
ab2cb1ef 95 NextPingTimer( *io_serv ),
e39cc3da 96 TimeSentLastPing( microsec_clock::universal_time() ),
26b0f687 97 PingReplyTimeout( ping_reply_timeout ),
c1abff61 98 HostAnalyzer( destination_address, ping_fail_percentage_limit,
a7b15639
CH
99 ping_congestion_percentage_limit,
100 ping_congestion_duration_thresh, n_parallel_pings,
101 link_analyzer ),
26b0f687 102 Resolver(),
91aa83f9 103 Pingers(),
91aa83f9
CH
104 NPingersDone( 0 ),
105 ParallelPingDelay( parallel_ping_delay ),
89f153af 106 DelayedPingTimer( *io_serv ),
26b0f687
CH
107 WantToPing( false ),
108 LogPrefix(),
8d26221d 109 ContinueOnOutdatedIps( false )
ced28dc7 110{
475ad07c
GMF
111 BOOST_ASSERT( !network_interface.empty() );
112 BOOST_ASSERT( !destination_address.empty() );
23f51766
CH
113 BOOST_ASSERT( ( 0 < destination_port ) &&
114 ( destination_port < numeric_limits<uint16_t>::max() ) );
f71cb7e1 115 BOOST_ASSERT( 0 < ping_interval_in_sec );
23f51766
CH
116 BOOST_ASSERT( (0 <= ping_fail_percentage_limit) &&
117 ( ping_fail_percentage_limit <= 100) );
118
26b0f687 119 update_log_prefix();
23f51766
CH
120
121 init_ping_protocol();
242e5fb3
CH
122
123 // start resolving already so we are prepared to ping
124 update_dns_resolver();
125
2d591235 126}
ced28dc7 127
086e2cc0
GMF
128/**
129 * @brief Destructor.
130 */
4bb97b45 131PingScheduler::~PingScheduler()
2d591235 132{
ced28dc7
GMF
133}
134
c1d776ba 135void PingScheduler::stop_pinging()
ced28dc7 136{
c1abff61
CH
137 // stop pinger and resolver
138 GlobalLogger.debug() << LogPrefix << "scheduler: stop pinging";
91aa83f9 139 clear_pingers();
72be9e7d 140 cancel_resolve(true);
f076f8d4 141
c1abff61
CH
142 // now cancel the own timer in case that pinger cancelation called callback
143 GlobalLogger.debug() << LogPrefix << "scheduler: cancel timer";
f076f8d4 144 NextPingTimer.cancel();
c1d776ba
CH
145}
146
147/**
91aa83f9
CH
148 * @brief stop all pingers and remove them from Pingers variable which will
149 * proboably cause their destruction
150 *
151 * Pingers is empty afterwards
152 */
153void PingScheduler::clear_pingers()
154{
155 PingerItem pinger;
89f153af 156 while(!Pingers.empty())
91aa83f9 157 {
89f153af 158 pinger = Pingers.back();
91aa83f9 159 pinger->stop_pinging();
89f153af 160 Pingers.pop_back();
91aa83f9
CH
161 }
162}
163
164/**
23f51766 165 * @brief Start into infinite loop of calls to ping
cad0b08d
CH
166 *
167 * Does not start yet but set NextPingTimer (possibly to 0), so action starts
168 * when io_service is started
c1d776ba
CH
169 */
170void PingScheduler::start_pinging()
171{
c1d776ba 172 if ( FirstDelay > 0 )
c1abff61
CH
173 GlobalLogger.info() << LogPrefix << "Delaying first ping by "
174 << FirstDelay << "s";
59733431 175 else
c1abff61 176 GlobalLogger.info() << LogPrefix << "Schedule ping as soon as possible";
cad0b08d
CH
177
178 (void) NextPingTimer.expires_from_now( seconds( FirstDelay ) );
23f51766 179 NextPingTimer.async_wait( bind( &PingScheduler::ping, this,
cad0b08d 180 boost::asio::placeholders::error ) );
09de3c4b
GMF
181}
182
4e91c69a 183
c1d776ba 184/**
23f51766 185 * @brief call Ping::ping and schedule a call to ping_done_handler when finished
c1d776ba 186 */
23f51766 187void PingScheduler::ping(const boost::system::error_code &error)
823623d9 188{
d26dce11 189 if ( error )
f076f8d4 190 { // get here, e.g. by NextPingTimer.cancel in stop_pinging
d26dce11 191 if ( error == boost::asio::error::operation_aborted )
c1abff61
CH
192 GlobalLogger.error() << LogPrefix << "Timer for ping was cancelled!"
193 << " --> Stopping";
d26dce11 194 else
c1abff61
CH
195 GlobalLogger.error() << LogPrefix << "Received error " << error
196 << " waiting for ping! Stopping";
d26dce11
CH
197 return;
198 }
199
23f51766
CH
200 // ping as soon as dns is ready
201 WantToPing = true;
8d26221d 202 ping_when_ready();
23f51766
CH
203}
204
c1d776ba 205
8d26221d 206void PingScheduler::ping_when_ready()
23f51766
CH
207{
208 if ( !WantToPing )
823623d9 209 {
2a4dde8b 210 GlobalLogger.info() << LogPrefix << "waiting for ping request "
91aa83f9 211 << "(should take no more than " << PingIntervalInSec << "s)";
23f51766 212 return;
823623d9 213 }
23f51766
CH
214 else if ( Resolver && Resolver->is_resolving() )
215 {
fd62d09f 216 GlobalLogger.info() << LogPrefix << "waiting for DNS to finish";
23f51766
CH
217 return;
218 }
219 else if ( !Resolver )
242e5fb3 220 {
23f51766
CH
221 // should not happen, but check anyway
222 GlobalLogger.warning() << LogPrefix << "Have no resolver!";
242e5fb3
CH
223 return;
224 }
09de3c4b 225
c1abff61 226 GlobalLogger.info() << LogPrefix << "start ping";
23f51766 227 WantToPing = false;
09de3c4b 228
fd62d09f
CH
229 // try to get an up-to-date IP (ContinueOnOutdatedIps may only be set
230 // because a CNAME was out of date -- IPs may still be current)
26b0f687 231 HostAddress ip = Resolver->get_next_ip();
8d26221d 232
fd62d09f
CH
233 if ( !ip.is_valid() )
234 { // this can happen in 2 cases: if ContinueOnOutdatedIps==true
235 // or when ip went out of date between resolve and now
236 // --> try to use outdated IP
26b0f687
CH
237 GlobalLogger.info() << LogPrefix << "Checking for outdated IPs";
238 bool check_up_to_date = false;
239 ip = Resolver->get_next_ip(check_up_to_date);
240 }
fd62d09f 241 if ( !ip.is_valid() )
b44a5f96
CH
242 { // Do not even have an outdated IP!
243 // This happens if have no cached IPs and resolve failed
244 GlobalLogger.info() << LogPrefix << "Not even outdated IP to ping "
245 << "-- treat like a failed ping.";
fd62d09f 246
ffa5cfe2
CH
247 // skip the ping and directly call ping_done_handler the appropriate
248 // number of times
249 for (int count=0; count<NPingers; ++count)
250 ping_done_handler(PingStatus_FailureNoIP, 0);
fd62d09f
CH
251 }
252 else
253 {
242e5fb3
CH
254 // create new pingers
255 for (int count=0; count<NPingers; ++count)
256 Pingers.push_back(
257 PingerFactory::createPinger(*ProtocolIter, IoService,
258 NetworkInterfaceName,
259 PingReplyTimeout) );
260
261 // remember when pinging started
262 TimeSentLastPing = microsec_clock::universal_time();
263
264 // get actual IP
91aa83f9
CH
265 boost::asio::ip::address actual_ip = ip.get_ip();
266 GlobalLogger.info() << LogPrefix << "pinging IP " << actual_ip
267 << " with TTL " << ip.get_ttl().get_updated_value() << "s";
242e5fb3 268 delayed_ping(boost::system::error_code(), actual_ip, 0);
26b0f687 269 }
3f6ba924
CH
270}
271
89f153af 272void PingScheduler::delayed_ping( const boost::system::error_code &error,
242e5fb3 273 const boost::asio::ip::address ip,
89f153af 274 const int pinger_index )
91aa83f9 275{
89f153af
CH
276 if (error)
277 {
278 GlobalLogger.info() << LogPrefix << "delayed ping received an error: "
279 << error;
280 return;
281 }
89f153af
CH
282
283 GlobalLogger.debug() << LogPrefix << "starting delayed ping index "
284 << pinger_index;
285 Pingers[pinger_index]->ping(ip,
286 DestinationPort,
287 boost::bind(&PingScheduler::ping_done_handler,
288 this, _1, _2) );
242e5fb3
CH
289 if (pinger_index >= NPingers-1)
290 GlobalLogger.debug() << LogPrefix << "started all delayed pings";
291 else
292 {
293 DelayedPingTimer.expires_from_now( milliseconds(ParallelPingDelay) );
294 DelayedPingTimer.async_wait( bind( &PingScheduler::delayed_ping,
89f153af 295 this, boost::asio::placeholders::error, ip, pinger_index+1) );
242e5fb3 296 }
91aa83f9
CH
297}
298
166fd9e9 299
23f51766 300//------------------------------------------------------------------------------
91aa83f9 301// Post Processing of Ping result and Preparation for next ping
23f51766 302//------------------------------------------------------------------------------
e58d7507 303
c1d776ba
CH
304/**
305 * @brief called when Ping::ping is done; calls functions to update
306 * statistics, ping interval and elapsed time;
23f51766 307 * schedules a call to ping, thereby closing the loop
c1d776ba 308 */
9c0dcf33
CH
309void PingScheduler::ping_done_handler( const PingStatus &result,
310 const long ping_duration_us )
502b6af0 311{
96c4e7a4
CH
312 PingStatus edited_result = result;
313 if (result == PingStatus_SuccessReply && ContinueOnOutdatedIps)
314 {
315 edited_result = PingStatus_SuccessOutdatedIP;
316
317 // reset ContinueOnOutdatedIps
318 ContinueOnOutdatedIps = false;
319 update_log_prefix();
320 }
321
91aa83f9
CH
322 ++NPingersDone;
323 GlobalLogger.info() << LogPrefix << "Ping " << NPingersDone << " of "
324 << NPingers << " done with result " << to_string(edited_result);
f8918bd5 325
c1d776ba 326 // post-processing
91aa83f9 327 // can call update_ping_interval only after update_ping_statistics!
9c0dcf33 328 HostAnalyzer.update_ping_statistics( edited_result, ping_duration_us );
91aa83f9
CH
329
330 // prepare next ping only after all pingers are done
331 if (NPingersDone == NPingers)
242e5fb3
CH
332 {
333 // stop and destruct all pingers
334 clear_pingers();
335
336 GlobalLogger.debug() << LogPrefix
337 << "--------------------------------------------------------------";
338
339 // update variables for next ping: number of pings, delay, protocol
340 update_ping_protocol();
341 update_ping_interval();
342 update_ping_number();
343
91aa83f9 344 prepare_next_ping();
242e5fb3 345 }
91aa83f9
CH
346}
347
348
349void PingScheduler::prepare_next_ping()
350{
242e5fb3
CH
351 // start DNS resolve if necessary
352 update_dns_resolver();
23f51766 353
ffa5cfe2
CH
354 NPingersDone = 0;
355
c1d776ba 356 // schedule next ping
91aa83f9
CH
357 int seconds_since_last_ping = (microsec_clock::universal_time()
358 - TimeSentLastPing).total_seconds();
359 if ( seconds_since_last_ping > PingIntervalInSec )
360 {
361 GlobalLogger.info() << "We are late for next ping!";
362 seconds_since_last_ping = PingIntervalInSec;
363 (void) NextPingTimer.expires_from_now( seconds(0) );
364 }
365 else
366 (void) NextPingTimer.expires_from_now( seconds( PingIntervalInSec
367 - seconds_since_last_ping ) );
23f51766 368 NextPingTimer.async_wait( bind( &PingScheduler::ping, this,
d26dce11 369 boost::asio::placeholders::error ) );
d8a91bd6
GMF
370}
371
372void PingScheduler::update_ping_interval()
373{
c1d776ba 374 // have to ping more often?
fb469ffa 375 if ( HostAnalyzer.exceeded_ping_failed_limit() )
d8a91bd6
GMF
376 {
377 PingIntervalInSec.speed_up();
378
c1abff61
CH
379 GlobalLogger.debug() << LogPrefix << "- Speeding up ping interval to: "
380 << PingIntervalInSec << "s";
d8a91bd6
GMF
381 }
382 else
383 {
384 PingIntervalInSec.back_to_original();
385
c1abff61
CH
386 GlobalLogger.debug() << LogPrefix << "- Stick to the original ping "
387 << "interval: " << PingIntervalInSec << "s";
d8a91bd6 388 }
ced28dc7
GMF
389}
390
242e5fb3
CH
391/** in case of congested line, increase number of pings
392 *
393 * CAUTION! Only call this after clear_pingers !!!
394 * */
395void PingScheduler::update_ping_number()
396{
397 // make sure we do not loose track of pingers here
398 if ( NPingersDone != NPingers || !Pingers.empty() )
399 {
400 GlobalLogger.warning() << LogPrefix << "Should only change number of "
401 << "pingers when all are finished and deleted! Have " << NPingers
402 << " pingers, " << NPingersDone << " of which are done and "
403 << Pingers.size() << " in listDone! Will not change NPingers.";
404 return;
405 }
406
407 if ( HostAnalyzer.exceeded_ping_congestion_limit() )
408 {
409 NPingers.increase();
410
411 GlobalLogger.debug() << LogPrefix << "- Increasing ping number to: "
412 << NPingers;
413 }
414 else
415 {
416 NPingers.back_to_original();
417
418 GlobalLogger.debug() << LogPrefix << "- Stick to the original ping "
419 << "number: " << NPingers;
420 }
421
422 HostAnalyzer.set_n_parallel_pings(NPingers);
423}
424
23f51766
CH
425//------------------------------------------------------------------------------
426// Ping Protocol Rotation
427//------------------------------------------------------------------------------
428
26b0f687 429void PingScheduler::init_ping_protocol()
23f51766 430{
26b0f687 431 ProtocolIter = Protocols.end();
23f51766
CH
432 get_next_ping_protocol();
433}
434
26b0f687 435void PingScheduler::update_ping_protocol()
23f51766
CH
436{
437 if ( can_change_ping_protocol() )
438 {
439 get_next_ping_protocol();
440 }
441}
442
26b0f687 443void PingScheduler::get_next_ping_protocol()
23f51766 444{
26b0f687
CH
445 ++ProtocolIter;
446 if (ProtocolIter == Protocols.end())
447 ProtocolIter = Protocols.begin();
23f51766
CH
448}
449
26b0f687 450bool PingScheduler::can_change_ping_protocol() const
23f51766 451{
c1abff61
CH
452 // TODO can_change_ping_protocol() and get_next_ping_protocol() may be
453 // implemented in a Algorithm class that can be exchanged in this class to
454 // provide an algorithm neutral class
23f51766
CH
455 return true;
456}
457
458//------------------------------------------------------------------------------
459// DNS host name resolution
460//------------------------------------------------------------------------------
26b0f687
CH
461
462// show "!" after host name if running on outdated IPs
8d26221d 463void PingScheduler::update_log_prefix()
26b0f687
CH
464{
465 std::stringstream temp;
72be9e7d 466 temp << "Sched(" << DestinationAddress;
26b0f687
CH
467 if (ContinueOnOutdatedIps)
468 temp << "!";
469 temp << "): ";
470 LogPrefix = temp.str();
471}
472
242e5fb3 473void PingScheduler::update_dns_resolver()
23f51766
CH
474{
475 if (Resolver && Resolver->is_resolving())
72be9e7d
CH
476 cancel_resolve(false);
477
478 if (ContinueOnOutdatedIps)
23f51766 479 {
72be9e7d
CH
480 ContinueOnOutdatedIps = false;
481 update_log_prefix();
23f51766
CH
482 }
483
484 // DNS master caches created resolvers and resolved IPs, so this will
485 // probably just return an existing resolver with already resolved IPs for
486 // requested protocol ( ICMP/TCP is ignored, only IPv4/v6 is important)
487 Resolver = DnsMaster::get_instance()->get_resolver_for(DestinationAddress,
242e5fb3 488 *ProtocolIter);
fd62d09f
CH
489
490 // get number of up-to-date IPs
2a4dde8b 491 // TODO should check here, if they will be up to date in PingIntervalInSec
fd62d09f
CH
492 bool check_up_to_date = true;
493 int ip_count = Resolver->get_resolved_ip_count(check_up_to_date);
494 if (ip_count > 0)
23f51766 495 {
fd62d09f 496 GlobalLogger.info() << LogPrefix << "Set resolved_ip_count to "
f8918bd5 497 << ip_count << " (IPs may be outdated=" << !check_up_to_date << ")";
fd62d09f
CH
498 HostAnalyzer.set_resolved_ip_count( ip_count );
499
72be9e7d 500 if (Resolver->is_resolving())
c1abff61 501 GlobalLogger.warning() << LogPrefix << "have up to date IPs but "
23f51766
CH
502 << "resolver seems to be resolving all the same... "
503 << "Start pinging anyway!";
8d26221d 504 ping_when_ready();
23f51766
CH
505 }
506 else
2a4dde8b
CH
507 {
508 GlobalLogger.info() << LogPrefix
509 << "No up-to-date IPs --> start resolve";
23f51766 510 start_resolving_ping_address();
2a4dde8b
CH
511 // set resolved_ip_count will be called in resolve callback
512 }
23f51766
CH
513}
514
26b0f687 515void PingScheduler::start_resolving_ping_address()
23f51766 516{
26b0f687 517 Resolver->async_resolve( boost::bind(&PingScheduler::dns_resolve_callback,
23f51766
CH
518 this, _1, _2) );
519}
520
26b0f687 521void PingScheduler::dns_resolve_callback(const bool was_success,
cd71d095 522 const int recursion_count)
23f51766 523{
c1abff61 524 GlobalLogger.info() << LogPrefix << "dns resolution finished "
23f51766 525 << "with success = " << was_success << " "
cd71d095 526 << "after " << recursion_count << " recursions";
23f51766 527
26b0f687
CH
528 if ( was_success )
529 {
fd62d09f
CH
530 // trust that a successfull DNS resolve means we have an IP with TTL>0
531 int ip_count = Resolver->get_resolved_ip_count(!ContinueOnOutdatedIps);
532 if (ip_count == 0)
ffa5cfe2 533 {
fd62d09f
CH
534 GlobalLogger.warning() << LogPrefix
535 << "Should not have reached this case: resolve was "
536 << "successfull but still have no IPs (up-to-date="
537 << !ContinueOnOutdatedIps << ")!";
538 if (DnsMaster::get_instance()->get_resolved_ip_ttl_threshold() > 0)
539 GlobalLogger.warning() << LogPrefix << "This probably happened "
540 << "because you specified a TTL threshold > 0 but resolving"
541 << " had no effect on TTLs since external cache is only "
542 << "updated when TTL=0 is reached.";
543 }
544 else
545 {
546 GlobalLogger.info() << LogPrefix << "Set resolved_ip_count to "
f8918bd5
CH
547 << ip_count << " (IPs may be outdated="
548 << ContinueOnOutdatedIps << ") --> could ping now";
fd62d09f 549 }
ffa5cfe2 550 HostAnalyzer.set_resolved_ip_count( ip_count );
8d26221d 551 ping_when_ready();
26b0f687
CH
552 }
553 else
554 { // host name resolution failed; try again bypassing first outdated CNAME
8d26221d 555 // or using cached IP
26b0f687 556 std::string skip_host = Resolver->get_skip_cname();
23f51766
CH
557
558 if (skip_host.empty())
838e0acf 559 { // try to continue with cached IPs
fd62d09f 560 int ip_count = Resolver->get_resolved_ip_count(false);
838e0acf
CH
561
562 if (ip_count == 0)
563 GlobalLogger.notice() << LogPrefix << "DNS failed "
564 << "and have no cached IPs either --> cannot ping";
565 // ping_when_ready will deal with this case
566 else
567 {
568 ContinueOnOutdatedIps = true;
569 update_log_prefix();
570
571 GlobalLogger.notice() << LogPrefix << "DNS failed, "
572 << "try anyway with cached data";
573 }
574
fd62d09f 575 GlobalLogger.info() << LogPrefix << "Set resolved_ip_count to "
f8918bd5 576 << ip_count << " (IPs may be outdated=" << true << ")";
fd62d09f
CH
577 HostAnalyzer.set_resolved_ip_count( ip_count );
578
8d26221d 579 ping_when_ready();
23f51766
CH
580 }
581 else
8d26221d 582 { // have CNAME to continue
838e0acf
CH
583 ContinueOnOutdatedIps = true;
584 update_log_prefix();
23f51766
CH
585 GlobalLogger.notice() << LogPrefix << "DNS failed, "
586 << "try again skipping a CNAME and resolving "
587 << skip_host << " directly";
72be9e7d
CH
588
589 cancel_resolve(false);
590
591 // now create new resolver
23f51766 592 Resolver = DnsMaster::get_instance()
26b0f687 593 ->get_resolver_for(skip_host, *ProtocolIter);
23f51766
CH
594 start_resolving_ping_address();
595 }
596 }
23f51766 597}
72be9e7d
CH
598
599/**
600 * cancel resolver if force_cancel or if it is not resolving DestinationAddress
601 *
602 * Resolvers have a life on their own: they are cached by DnsMaster so never go
603 * out of scope and even after calling callbacks, there might still be a
604 * longterm timer active to re-try resolving.
605 * We want to cancel that long-term timer only if the Resolver is not for our
606 * real, original DestinationAddress but a CNAME, which can happen when trying
607 * to skip cnames and working on out-dated IPs
608 */
609void PingScheduler::cancel_resolve(const bool force_cancel)
610{
611 if (force_cancel)
612 {
613 GlobalLogger.info() << "Cancelling resolver (forced)";
614 Resolver->cancel_resolve();
615 }
616 else if ( Resolver->get_hostname() == DestinationAddress )
617 GlobalLogger.info() << LogPrefix
618 << "Leave original resolver active in background";
619 else
620 {
621 GlobalLogger.info() << LogPrefix << "Cancel resolver for "
622 << Resolver->get_hostname() << " since is not the original "
623 << DestinationAddress;
624 Resolver->cancel_resolve();
625 }
626}
627