Kea  1.5.0
ha_service.cc
Go to the documentation of this file.
1 // Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
9 #include <command_creator.h>
10 #include <ha_log.h>
11 #include <ha_service.h>
12 #include <ha_service_states.h>
13 #include <cc/command_interpreter.h>
14 #include <cc/data.h>
15 #include <dhcpsrv/lease_mgr.h>
17 #include <http/date_time.h>
18 #include <http/response_json.h>
19 #include <http/post_request_json.h>
20 #include <util/stopwatch.h>
21 #include <boost/pointer_cast.hpp>
22 #include <boost/bind.hpp>
23 #include <boost/make_shared.hpp>
24 #include <boost/weak_ptr.hpp>
25 #include <sstream>
26 
27 using namespace isc::asiolink;
28 using namespace isc::config;
29 using namespace isc::data;
30 using namespace isc::dhcp;
31 using namespace isc::hooks;
32 using namespace isc::http;
33 using namespace isc::log;
34 using namespace isc::util;
35 
36 namespace isc {
37 namespace ha {
38 
39 const int HAService::HA_HEARTBEAT_COMPLETE_EVT;
40 const int HAService::HA_LEASE_UPDATES_COMPLETE_EVT;
41 const int HAService::HA_SYNCING_FAILED_EVT;
42 const int HAService::HA_SYNCING_SUCCEEDED_EVT;
43 
44 HAService::HAService(const IOServicePtr& io_service, const NetworkStatePtr& network_state,
45  const HAConfigPtr& config, const HAServerType& server_type)
46  : io_service_(io_service), network_state_(network_state), config_(config),
47  server_type_(server_type), client_(*io_service), communication_state_(),
48  query_filter_(config), pending_requests_() {
49 
50  if (server_type == HAServerType::DHCPv4) {
52 
53  } else {
55  }
56 
58 
59  LOG_INFO(ha_logger, HA_SERVICE_STARTED)
60  .arg(HAConfig::HAModeToString(config->getHAMode()))
61  .arg(HAConfig::PeerConfig::roleToString(config->getThisServerConfig()->getRole()));
62 }
63 
64 void
66  StateModel::defineEvents();
67 
68  defineEvent(HA_HEARTBEAT_COMPLETE_EVT, "HA_HEARTBEAT_COMPLETE_EVT");
69  defineEvent(HA_LEASE_UPDATES_COMPLETE_EVT, "HA_LEASE_UPDATES_COMPLETE_EVT");
70  defineEvent(HA_SYNCING_FAILED_EVT, "HA_SYNCING_FAILED_EVT");
71  defineEvent(HA_SYNCING_SUCCEEDED_EVT, "HA_SYNCING_SUCCEEDED_EVT");
72 }
73 
74 void
76  StateModel::verifyEvents();
77 
82 }
83 
84 void
86  StateModel::defineStates();
87 
89  boost::bind(&HAService::backupStateHandler, this),
90  config_->getStateMachineConfig()->getStateConfig(HA_BACKUP_ST)->getPausing());
91 
93  boost::bind(&HAService::normalStateHandler, this),
94  config_->getStateMachineConfig()->getStateConfig(HA_HOT_STANDBY_ST)->getPausing());
95 
97  boost::bind(&HAService::normalStateHandler, this),
98  config_->getStateMachineConfig()->getStateConfig(HA_LOAD_BALANCING_ST)->getPausing());
99 
101  boost::bind(&HAService::partnerDownStateHandler, this),
102  config_->getStateMachineConfig()->getStateConfig(HA_PARTNER_DOWN_ST)->getPausing());
103 
105  boost::bind(&HAService::readyStateHandler, this),
106  config_->getStateMachineConfig()->getStateConfig(HA_READY_ST)->getPausing());
107 
109  boost::bind(&HAService::syncingStateHandler, this),
110  config_->getStateMachineConfig()->getStateConfig(HA_SYNCING_ST)->getPausing());
111 
113  boost::bind(&HAService::terminatedStateHandler, this),
114  config_->getStateMachineConfig()->getStateConfig(HA_TERMINATED_ST)->getPausing());
115 
117  boost::bind(&HAService::waitingStateHandler, this),
118  config_->getStateMachineConfig()->getStateConfig(HA_WAITING_ST)->getPausing());
119 }
120 
121 void
123  if (doOnEntry()) {
126 
127  // Log if the state machine is paused.
129  }
130 
131  // There is nothing to do in that state. This server simply receives
132  // lease updates from the partners.
134 }
135 
136 void
138  // If we are transitioning from another state, we have to define new
139  // serving scopes appropriate for the new state. We don't do it if
140  // we remain in this state.
141  if (doOnEntry()) {
144 
145  // Log if the state machine is paused.
147  }
148 
150 
151  if (isModelPaused()) {
153  return;
154  }
155 
156  // Check if the clock skew is still acceptable. If not, transition to
157  // the terminated state.
158  if (shouldTerminate()) {
160  return;
161  }
162 
163  switch (communication_state_->getPartnerState()) {
164  case HA_PARTNER_DOWN_ST:
166  break;
167 
168  case HA_TERMINATED_ST:
170  break;
171 
172  case HA_UNAVAILABLE_ST:
173  if (shouldPartnerDown()) {
175 
176  } else {
178  }
179  break;
180 
181  default:
183  }
184 }
185 
186 void
188  // If we are transitioning from another state, we have to define new
189  // serving scopes appropriate for the new state. We don't do it if
190  // we remain in this state.
191  if (doOnEntry()) {
192  // It may be administratively disabled to handle partner's scope
193  // in case of failure. If this is the case we'll just handle our
194  // default scope (or no scope at all). The user will need to
195  // manually enable this server to handle partner's scope.
196  if (config_->getThisServerConfig()->isAutoFailover()) {
198  } else {
200  }
202 
203  // Log if the state machine is paused.
205  }
206 
208 
209  if (isModelPaused()) {
211  return;
212  }
213 
214  // Check if the clock skew is still acceptable. If not, transition to
215  // the terminated state.
216  if (shouldTerminate()) {
218  return;
219  }
220 
221  switch (communication_state_->getPartnerState()) {
222  case HA_HOT_STANDBY_ST:
224  case HA_PARTNER_DOWN_ST:
226  break;
227 
228  case HA_READY_ST:
231  break;
232 
233  case HA_TERMINATED_ST:
235  break;
236 
237  default:
239  }
240 }
241 
242 void
244  // If we are transitioning from another state, we have to define new
245  // serving scopes appropriate for the new state. We don't do it if
246  // we remain in this state.
247  if (doOnEntry()) {
250 
251  // Log if the state machine is paused.
253  }
254 
256 
257  if (isModelPaused()) {
259  return;
260  }
261 
262  // Check if the clock skew is still acceptable. If not, transition to
263  // the terminated state.
264  if (shouldTerminate()) {
266  return;
267  }
268 
269  switch (communication_state_->getPartnerState()) {
270  case HA_HOT_STANDBY_ST:
272  break;
273 
276  break;
277 
278  case HA_READY_ST:
279  // If both servers are ready, the primary server "wins" and is
280  // transitioned first.
281  if (config_->getThisServerConfig()->getRole() == HAConfig::PeerConfig::PRIMARY) {
284  } else {
286  }
287  break;
288 
289  case HA_TERMINATED_ST:
291  break;
292 
293  case HA_UNAVAILABLE_ST:
294  if (shouldPartnerDown()) {
296 
297  } else {
299  }
300  break;
301 
302  default:
304  }
305 }
306 
307 void
309  // If we are transitioning from another state, we have to define new
310  // serving scopes appropriate for the new state. We don't do it if
311  // we remain in this state.
312  if (doOnEntry()) {
315 
316  // Log if the state machine is paused.
318  }
319 
320  if (isModelPaused()) {
322  return;
323  }
324 
325  // Check if the clock skew is still acceptable. If not, transition to
326  // the terminated state.
327  if (shouldTerminate()) {
329  return;
330  }
331 
332  // We don't want to perform synchronous attempt to synchronize with
333  // a partner until we know that the partner is responding. Therefore,
334  // we wait for the heartbeat to complete successfully before we
335  // initiate the synchronization.
336  switch (communication_state_->getPartnerState()) {
337  case HA_TERMINATED_ST:
339  return;
340 
341  case HA_UNAVAILABLE_ST:
342  // If the partner appears to be offline, let's transition to the partner
343  // down state. Otherwise, we'd be stuck trying to synchronize with a
344  // dead partner.
345  if (shouldPartnerDown()) {
347 
348  } else {
350  }
351  break;
352 
353  default:
354  // We don't want the heartbeat to interfere with the synchronization,
355  // so let's temporarily stop it.
356  communication_state_->stopHeartbeat();
357 
358  // Timeout is configured in milliseconds. Need to convert to seconds.
359  unsigned int dhcp_disable_timeout =
360  static_cast<unsigned int>(config_->getSyncTimeout() / 1000);
361  if (dhcp_disable_timeout == 0) {
362  ++dhcp_disable_timeout;
363  }
364 
365  // Perform synchronous leases update.
366  std::string status_message;
367  int sync_status = synchronize(status_message,
368  config_->getFailoverPeerConfig()->getName(),
369  dhcp_disable_timeout);
370 
371  // If the leases synchronization was successful, let's transition
372  // to the ready state.
373  if (sync_status == CONTROL_RESULT_SUCCESS) {
375 
376  } else {
377  // If the synchronization was unsuccessful we're back to the
378  // situation that the partner is unavailable and therefore
379  // we stay in the syncing state.
381  }
382  }
383 
384  // Make sure that the heartbeat is re-enabled.
386 }
387 
388 void
390  // If we are transitioning from another state, we have to define new
391  // serving scopes appropriate for the new state. We don't do it if
392  // we remain in this state.
393  if (doOnEntry()) {
396 
397  // In the terminated state we don't send heartbeat.
398  communication_state_->stopHeartbeat();
399 
400  // Log if the state machine is paused.
402 
403  LOG_ERROR(ha_logger, HA_TERMINATED);
404  }
405 
407 }
408 
409 void
411  // If we are transitioning from another state, we have to define new
412  // serving scopes appropriate for the new state. We don't do it if
413  // we remain in this state.
414  if (doOnEntry()) {
417 
418  // Log if the state machine is paused.
420  }
421 
422  // Only schedule the heartbeat for non-backup servers.
423  if (config_->getThisServerConfig()->getRole() != HAConfig::PeerConfig::BACKUP) {
425  }
426 
427  if (isModelPaused()) {
429  return;
430  }
431 
432  // Backup server must remain in its own state.
433  if (config_->getThisServerConfig()->getRole() == HAConfig::PeerConfig::BACKUP) {
435  return;
436  }
437 
438  // Check if the clock skew is still acceptable. If not, transition to
439  // the terminated state.
440  if (shouldTerminate()) {
442  return;
443  }
444 
445  switch (communication_state_->getPartnerState()) {
446  case HA_HOT_STANDBY_ST:
448  case HA_PARTNER_DOWN_ST:
449  case HA_READY_ST:
450  // If we're configured to not synchronize lease database, proceed directly
451  // to the "ready" state.
452  verboseTransition(config_->amSyncingLeases() ? HA_SYNCING_ST : HA_READY_ST);
453  break;
454 
455  case HA_SYNCING_ST:
457  break;
458 
459  case HA_TERMINATED_ST:
461  break;
462 
463  case HA_WAITING_ST:
464  // If both servers are waiting, the primary server 'wins' and is
465  // transitioned to the next state first.
466  if (config_->getThisServerConfig()->getRole() == HAConfig::PeerConfig::PRIMARY) {
467  // If we're configured to not synchronize lease database, proceed directly
468  // to the "ready" state.
469  verboseTransition(config_->amSyncingLeases() ? HA_SYNCING_ST : HA_READY_ST);
470 
471  } else {
473  }
474  break;
475 
476  case HA_UNAVAILABLE_ST:
477  if (shouldPartnerDown()) {
479 
480  } else {
482  }
483  break;
484 
485  default:
487  }
488 }
489 
490 void
491 HAService::verboseTransition(const unsigned state) {
492  auto partner_state = communication_state_->getPartnerState();
493 
494  // Get current and new state name.
495  std::string current_state_name = getStateLabel(getCurrState());
496  std::string new_state_name = getStateLabel(state);
497  std::string partner_state_name = getStateLabel(partner_state);
498 
499  // Turn them to upper case so as they are better visible in the logs.
500  boost::to_upper(current_state_name);
501  boost::to_upper(new_state_name);
502  boost::to_upper(partner_state_name);
503 
504  // Log the transition.
505  LOG_INFO(ha_logger, HA_STATE_TRANSITION)
506  .arg(current_state_name)
507  .arg(new_state_name)
508  .arg(partner_state_name);
509 
510  // If we're transitioning directly from the "waiting" to "ready"
511  // state it indicates that the database synchronization is
512  // administratively disabled. Let's remind the user about this
513  // configuration setting.
514  if ((state == HA_READY_ST) && (getCurrState() == HA_WAITING_ST)) {
515  LOG_INFO(ha_logger, HA_CONFIG_LEASE_SYNCING_DISABLED_REMINDER);
516  }
517 
518  // Do the actual transition.
519  transition(state, getNextEvent());
520 
521  // Inform the administrator whether or not lease updates are generated.
522  // Updates are never generated by a backup server so it doesn't make
523  // sense to log anything for the backup server.
524  if (config_->getThisServerConfig()->getRole() != HAConfig::PeerConfig::BACKUP) {
525  if (shouldSendLeaseUpdates(config_->getFailoverPeerConfig())) {
526  LOG_INFO(ha_logger, HA_LEASE_UPDATES_ENABLED)
527  .arg(new_state_name);
528 
529  } else if (!config_->amSendingLeaseUpdates()) {
530  // Lease updates are administratively disabled.
531  LOG_INFO(ha_logger, HA_CONFIG_LEASE_UPDATES_DISABLED_REMINDER)
532  .arg(new_state_name);
533 
534  } else {
535  // Lease updates are not administratively disabled, but they
536  // are not issued because this is the backup server or because
537  // in this state the server should not generate lease updates.
538  LOG_INFO(ha_logger, HA_LEASE_UPDATES_DISABLED)
539  .arg(new_state_name);
540  }
541  }
542 }
543 
544 bool
546  if (isModelPaused()) {
547  LOG_INFO(ha_logger, HA_STATE_MACHINE_CONTINUED);
548  unpauseModel();
549  return (true);
550  }
551  return (false);
552 }
553 
554 void
556  // Inform the administrator if the state machine is paused.
557  if (isModelPaused()) {
558  std::string state_name = stateToString(getCurrState());
559  boost::to_upper(state_name);
560  LOG_INFO(ha_logger, HA_STATE_MACHINE_PAUSED)
561  .arg(state_name);
562  }
563 }
564 
565 void
568 }
569 
570 bool
572  return (inScopeInternal(query4));
573 }
574 
575 bool
577  return (inScopeInternal(query6));
578 }
579 
580 template<typename QueryPtrType>
581 bool
582 HAService::inScopeInternal(QueryPtrType& query) {
583  // Check if the query is in scope (should be processed by this server).
584  std::string scope_class;
585  const bool in_scope = query_filter_.inScope(query, scope_class);
586  // Whether or not the query is going to be processed by this server,
587  // we associate the query with the appropriate class.
588  query->addClass(dhcp::ClientClass(scope_class));
589  // The following is the part of the server failure detection algorithm.
590  // If the query should be processed by the partner we need to check if
591  // the partner responds. If the number of unanswered queries exceeds a
592  // configured threshold, we will consider the partner to be offline.
593  if (!in_scope && communication_state_->isCommunicationInterrupted()) {
594  communication_state_->analyzeMessage(query);
595  }
596  // Indicate if the query is in scope.
597  return (in_scope);
598 }
599 
600 void
602  std::string current_state_name = getStateLabel(getCurrState());
603  boost::to_upper(current_state_name);
604 
605  // DHCP service should be enabled in the following states.
606  const bool should_enable = ((getCurrState() == HA_LOAD_BALANCING_ST) ||
610 
611  if (!should_enable && network_state_->isServiceEnabled()) {
612  std::string current_state_name = getStateLabel(getCurrState());
613  boost::to_upper(current_state_name);
614  LOG_INFO(ha_logger, HA_LOCAL_DHCP_DISABLE)
615  .arg(config_->getThisServerName())
616  .arg(current_state_name);
617  network_state_->disableService();
618 
619  } else if (should_enable && !network_state_->isServiceEnabled()) {
620  std::string current_state_name = getStateLabel(getCurrState());
621  boost::to_upper(current_state_name);
622  LOG_INFO(ha_logger, HA_LOCAL_DHCP_ENABLE)
623  .arg(config_->getThisServerName())
624  .arg(current_state_name);
625  network_state_->enableService();
626  }
627 }
628 
629 bool
631  // Checking whether the communication with the partner is ok is the
632  // first step towards verifying if the server is up.
633  if (communication_state_->isCommunicationInterrupted()) {
634  // If the communication is interrupted, we also have to check
635  // whether the partner answers DHCP requests. The only cases
636  // when we don't (can't) do it are: the hot standby configuration
637  // in which this server is a primary and when the DHCP service is
638  // disabled so we can't analyze incoming traffic. Note that the
639  // primary server can't check delayed responses to the partner
640  // because the partner doesn't respond to any queries in this
641  // configuration.
642  if (network_state_->isServiceEnabled() &&
643  ((config_->getHAMode() == HAConfig::LOAD_BALANCING) ||
644  (config_->getThisServerConfig()->getRole() == HAConfig::PeerConfig::STANDBY))) {
645  return (communication_state_->failureDetected());
646  }
647 
648  // Hot standby / primary case.
649  return (true);
650  }
651 
652  // Shouldn't transition to the partner down state.
653  return (false);
654 }
655 
656 bool
658  if (communication_state_->clockSkewShouldTerminate()) {
659  LOG_ERROR(ha_logger, HA_HIGH_CLOCK_SKEW_CAUSES_TERMINATION)
660  .arg(communication_state_->logFormatClockSkew());
661  return (true);
662 
663  } else if (communication_state_->clockSkewShouldWarn()) {
664  LOG_WARN(ha_logger, HA_HIGH_CLOCK_SKEW)
665  .arg(communication_state_->logFormatClockSkew());
666  }
667 
668  return (false);
669 }
670 
671 size_t
673  const dhcp::Lease4CollectionPtr& leases,
674  const dhcp::Lease4CollectionPtr& deleted_leases,
675  const hooks::ParkingLotHandlePtr& parking_lot) {
676 
677  // Get configurations of the peers. Exclude this instance.
678  HAConfig::PeerConfigMap peers_configs = config_->getOtherServersConfig();
679 
680  size_t sent_num = 0;
681 
682  // Schedule sending lease updates to each peer.
683  for (auto p = peers_configs.begin(); p != peers_configs.end(); ++p) {
684  HAConfig::PeerConfigPtr conf = p->second;
685 
686  // Check if the lease update should be sent to the server. If we're in
687  // the partner-down state we don't send lease updates to the partner.
688  if (!shouldSendLeaseUpdates(conf)) {
689  continue;
690  }
691 
692  // Count contacted servers.
693  ++sent_num;
694 
695  // Lease updates for deleted leases.
696  for (auto l = deleted_leases->begin(); l != deleted_leases->end(); ++l) {
698  parking_lot);
699  }
700 
701  // Lease updates for new allocations and updated leases.
702  for (auto l = leases->begin(); l != leases->end(); ++l) {
704  parking_lot);
705  }
706  }
707 
708  return (sent_num);
709 }
710 
711 size_t
713  const dhcp::Lease6CollectionPtr& leases,
714  const dhcp::Lease6CollectionPtr& deleted_leases,
715  const hooks::ParkingLotHandlePtr& parking_lot) {
716 
717  // Get configurations of the peers. Exclude this instance.
718  HAConfig::PeerConfigMap peers_configs = config_->getOtherServersConfig();
719 
720  size_t sent_num = 0;
721 
722  // Schedule sending lease updates to each peer.
723  for (auto p = peers_configs.begin(); p != peers_configs.end(); ++p) {
724  HAConfig::PeerConfigPtr conf = p->second;
725 
726  // Check if the lease update should be sent to the server. If we're in
727  // the partner-down state we don't send lease updates to the partner.
728  if (!shouldSendLeaseUpdates(conf)) {
729  continue;
730  }
731 
732  // Count contacted servers.
733  ++sent_num;
734 
735  // Lease updates for deleted leases.
736  for (auto l = deleted_leases->begin(); l != deleted_leases->end(); ++l) {
738  parking_lot);
739  }
740 
741  // Lease updates for new allocations and updated leases.
742  for (auto l = leases->begin(); l != leases->end(); ++l) {
744  parking_lot);
745  }
746  }
747 
748  return (sent_num);
749 }
750 
751 template<typename QueryPtrType>
752 void
753 HAService::asyncSendLeaseUpdate(const QueryPtrType& query,
754  const HAConfig::PeerConfigPtr& config,
755  const ConstElementPtr& command,
756  const ParkingLotHandlePtr& parking_lot) {
757  // Create HTTP/1.1 request including our command.
758  PostHttpRequestJsonPtr request = boost::make_shared<PostHttpRequestJson>
759  (HttpRequest::Method::HTTP_POST, "/", HttpVersion::HTTP_11());
760  request->setBodyAsJson(command);
761  request->finalize();
762 
763  // Response object should also be created because the HTTP client needs
764  // to know the type of the expected response.
765  HttpResponseJsonPtr response = boost::make_shared<HttpResponseJson>();
766 
767  // When possible we prefer to pass weak pointers to the queries, rather
768  // than shared pointers, to avoid memory leaks in case cross reference
769  // between the pointers.
770  boost::weak_ptr<typename QueryPtrType::element_type> weak_query(query);
771 
772  // Schedule asynchronous HTTP request.
773  client_.asyncSendRequest(config->getUrl(), request, response,
774  [this, weak_query, parking_lot, config]
775  (const boost::system::error_code& ec,
776  const HttpResponsePtr& response,
777  const std::string& error_str) {
778  // Get the shared pointer of the query. The server should keep the
779  // pointer to the query and then park it. Therefore, we don't really
780  // expect it to be null. If it is null, something is really wrong.
781  QueryPtrType query = weak_query.lock();
782  if (!query) {
783  isc_throw(Unexpected, "query is null while receiving response from"
784  " HA peer. This is programmatic error");
785  }
786 
787  // There are three possible groups of errors during the lease update.
788  // One is the IO error causing issues in communication with the peer.
789  // Another one is an HTTP parsing error. The last type of error is
790  // when non-success error code is returned in the response carried
791  // in the HTTP message or if the JSON response is otherwise broken.
792 
793  bool lease_update_success = true;
794 
795  // Handle first two groups of errors.
796  if (ec || !error_str.empty()) {
797  LOG_WARN(ha_logger, HA_LEASE_UPDATE_COMMUNICATIONS_FAILED)
798  .arg(query->getLabel())
799  .arg(config->getLogLabel())
800  .arg(ec ? ec.message() : error_str);
801 
802  // Communication error, so let's drop parked packet. The DHCP
803  // response will not be sent.
804  lease_update_success = false;
805 
806  } else {
807 
808  // Handle third group of errors.
809  try {
810  verifyAsyncResponse(response);
811 
812  } catch (const std::exception& ex) {
813  LOG_WARN(ha_logger, HA_LEASE_UPDATE_FAILED)
814  .arg(query->getLabel())
815  .arg(config->getLogLabel())
816  .arg(ex.what());
817 
818  // Error while doing an update. The DHCP response will not be sent.
819  lease_update_success = false;
820  }
821  }
822 
823  // We don't care about the result of the lease update to the backup server.
824  // It is a best effort update.
825  if (config->getRole() != HAConfig::PeerConfig::BACKUP) {
826  if (lease_update_success) {
827  // If the lease update was successful and we have sent it to the server
828  // to which we also send heartbeats (primary, secondary or standby) we
829  // can assume that the server is online and we can defer next heartbeat.
830  communication_state_->poke();
831 
832  } else {
833  // Lease update was unsuccessful, so drop the parked DHCP packet.
834  parking_lot->drop(query);
835  communication_state_->setPartnerState("unavailable");
836  }
837  }
838 
839  auto it = pending_requests_.find(query);
840 
841  // If there are no more pending requests for this query, let's unpark
842  // the DHCP packet.
843  if (it == pending_requests_.end() || (--pending_requests_[query] <= 0)) {
844  parking_lot->unpark(query);
845 
846  // If we have unparked the packet we can clear pending requests for
847  // this query.
848  if (it != pending_requests_.end()) {
849  pending_requests_.erase(it);
850  }
851 
852  // If we have finished sending the lease updates we need to run the
853  // state machine until the state machine finds that additional events
854  // are required, such as next heartbeat or a lease update. The runModel()
855  // may transition to another state, schedule asynchronous tasks etc.
856  // Then it returns control to the DHCP server.
857  runModel(HA_LEASE_UPDATES_COMPLETE_EVT);
858  }
859  });
860 
861  // Request scheduled, so update the request counters for the query.
862  if (pending_requests_.count(query) == 0) {
863  pending_requests_[query] = 1;
864 
865  } else {
866  ++pending_requests_[query];
867  }
868 }
869 
870 bool
871 HAService::shouldSendLeaseUpdates(const HAConfig::PeerConfigPtr& peer_config) const {
872  // Never send lease updates if they are administratively disabled.
873  if (!config_->amSendingLeaseUpdates()) {
874  return (false);
875  }
876 
877  // Always send updates to the backup server.
878  if (peer_config->getRole() == HAConfig::PeerConfig::BACKUP) {
879  return (true);
880  }
881 
882  // Never send updates if this is a backup server.
883  if (config_->getThisServerConfig()->getRole() == HAConfig::PeerConfig::BACKUP) {
884  return (false);
885  }
886 
887  // In other case, whether we send lease updates or not depends on our
888  // state.
889  switch (getCurrState()) {
890  case HA_HOT_STANDBY_ST:
892  return (true);
893 
894  default:
895  ;
896  }
897 
898  return (false);
899 }
900 
902 HAService::processHeartbeat() {
903  ElementPtr arguments = Element::createMap();
904  std::string state_label = getState(getCurrState())->getLabel();
905  arguments->set("state", Element::create(state_label));
906 
907  std::string date_time = HttpDateTime().rfc1123Format();
908  arguments->set("date-time", Element::create(date_time));
909 
910  return (createAnswer(CONTROL_RESULT_SUCCESS, "HA peer status returned.",
911  arguments));
912 }
913 
914 void
915 HAService::asyncSendHeartbeat() {
916  HAConfig::PeerConfigPtr partner_config = config_->getFailoverPeerConfig();
917 
918  // Create HTTP/1.1 request including our command.
919  PostHttpRequestJsonPtr request = boost::make_shared<PostHttpRequestJson>
920  (HttpRequest::Method::HTTP_POST, "/", HttpVersion::HTTP_11());
921  request->setBodyAsJson(CommandCreator::createHeartbeat(server_type_));
922  request->finalize();
923 
924  // Response object should also be created because the HTTP client needs
925  // to know the type of the expected response.
926  HttpResponseJsonPtr response = boost::make_shared<HttpResponseJson>();
927 
928  // Schedule asynchronous HTTP request.
929  client_.asyncSendRequest(partner_config->getUrl(), request, response,
930  [this, partner_config]
931  (const boost::system::error_code& ec,
932  const HttpResponsePtr& response,
933  const std::string& error_str) {
934 
935  // There are three possible groups of errors during the heartneat.
936  // One is the IO error causing issues in communication with the peer.
937  // Another one is an HTTP parsing error. The last type of error is
938  // when non-success error code is returned in the response carried
939  // in the HTTP message or if the JSON response is otherwise broken.
940 
941  bool heartbeat_success = true;
942 
943  // Handle first two groups of errors.
944  if (ec || !error_str.empty()) {
945  LOG_WARN(ha_logger, HA_HEARTBEAT_COMMUNICATIONS_FAILED)
946  .arg(partner_config->getLogLabel())
947  .arg(ec ? ec.message() : error_str);
948  heartbeat_success = false;
949 
950  } else {
951 
952  // Handle third group of errors.
953  try {
954  // Response must contain arguments and the arguments must
955  // be a map.
956  ConstElementPtr args = verifyAsyncResponse(response);
957  if (!args || args->getType() != Element::map) {
958  isc_throw(CtrlChannelError, "returned arguments in the response"
959  " must be a map");
960  }
961  // Response must include partner's state.
962  ConstElementPtr state = args->get("state");
963  if (!state || state->getType() != Element::string) {
964  isc_throw(CtrlChannelError, "server state not returned in response"
965  " to a ha-heartbeat command or it is not a string");
966  }
967  // Remember the partner's state. This may throw if the returned
968  // state is invalid.
969  communication_state_->setPartnerState(state->stringValue());
970 
971  ConstElementPtr date_time = args->get("date-time");
972  if (!date_time || date_time->getType() != Element::string) {
973  isc_throw(CtrlChannelError, "date-time not returned in response"
974  " to a ha-heartbeat command or it is not a string");
975  }
976  // Note the time returned by the partner to calculate the clock skew.
977  communication_state_->setPartnerTime(date_time->stringValue());
978 
979  } catch (const std::exception& ex) {
980  LOG_WARN(ha_logger, HA_HEARTBEAT_FAILED)
981  .arg(partner_config->getLogLabel())
982  .arg(ex.what());
983  heartbeat_success = false;
984  }
985  }
986 
987  // If heartbeat was successful, let's mark the connection with the
988  // peer as healthy.
989  if (heartbeat_success) {
990  communication_state_->poke();
991 
992  } else {
993  // We were unable to retrieve partner's state, so let's mark it
994  // as unavailable.
995  communication_state_->setPartnerState("unavailable");
996  }
997 
998  // Whatever the result of the heartbeat was, the state machine needs
999  // to react to this. Let's run the state machine until the state machine
1000  // finds that some new events are required, i.e. next heartbeat or
1001  // lease update. The runModel() may transition to another state, schedule
1002  // asynchronous tasks etc. Then it returns control to the DHCP server.
1003  startHeartbeat();
1004  runModel(HA_HEARTBEAT_COMPLETE_EVT);
1005  });
1006 }
1007 
1008 void
1009 HAService::scheduleHeartbeat() {
1010  if (!communication_state_->isHeartbeatRunning()) {
1011  startHeartbeat();
1012  }
1013 }
1014 
1015 void
1016 HAService::startHeartbeat() {
1017  if (config_->getHeartbeatDelay() > 0) {
1018  communication_state_->startHeartbeat(config_->getHeartbeatDelay(),
1019  boost::bind(&HAService::asyncSendHeartbeat,
1020  this));
1021  }
1022 }
1023 
1024 void
1025 HAService::asyncDisableDHCPService(HttpClient& http_client,
1026  const std::string& server_name,
1027  const unsigned int max_period,
1028  PostRequestCallback post_request_action) {
1029  HAConfig::PeerConfigPtr remote_config = config_->getPeerConfig(server_name);
1030 
1031  // Create HTTP/1.1 request including our command.
1032  PostHttpRequestJsonPtr request = boost::make_shared<PostHttpRequestJson>
1033  (HttpRequest::Method::HTTP_POST, "/", HttpVersion::HTTP_11());
1034 
1035  request->setBodyAsJson(CommandCreator::createDHCPDisable(max_period,
1036  server_type_));
1037  request->finalize();
1038 
1039  // Response object should also be created because the HTTP client needs
1040  // to know the type of the expected response.
1041  HttpResponseJsonPtr response = boost::make_shared<HttpResponseJson>();
1042 
1043  // Schedule asynchronous HTTP request.
1044  http_client.asyncSendRequest(remote_config->getUrl(), request, response,
1045  [this, remote_config, post_request_action]
1046  (const boost::system::error_code& ec,
1047  const HttpResponsePtr& response,
1048  const std::string& error_str) {
1049 
1050  // There are three possible groups of errors during the heartneat.
1051  // One is the IO error causing issues in communication with the peer.
1052  // Another one is an HTTP parsing error. The last type of error is
1053  // when non-success error code is returned in the response carried
1054  // in the HTTP message or if the JSON response is otherwise broken.
1055 
1056  std::string error_message;
1057 
1058  // Handle first two groups of errors.
1059  if (ec || !error_str.empty()) {
1060  error_message = (ec ? ec.message() : error_str);
1061  LOG_ERROR(ha_logger, HA_DHCP_DISABLE_COMMUNICATIONS_FAILED)
1062  .arg(remote_config->getLogLabel())
1063  .arg(error_message);
1064 
1065  } else {
1066 
1067  // Handle third group of errors.
1068  try {
1069  static_cast<void>(verifyAsyncResponse(response));
1070 
1071  } catch (const std::exception& ex) {
1072  error_message = ex.what();
1073  LOG_ERROR(ha_logger, HA_DHCP_DISABLE_FAILED)
1074  .arg(remote_config->getLogLabel())
1075  .arg(error_message);
1076  }
1077  }
1078 
1079  // If there was an error communicating with the partner, mark the
1080  // partner as unavailable.
1081  if (!error_message.empty()) {
1082  communication_state_->setPartnerState("unavailable");
1083  }
1084 
1085  // Invoke post request action if it was specified.
1086  if (post_request_action) {
1087  post_request_action(error_message.empty(),
1088  error_message);
1089  }
1090  });
1091 }
1092 
1093 void
1094 HAService::asyncEnableDHCPService(HttpClient& http_client,
1095  const std::string& server_name,
1096  PostRequestCallback post_request_action) {
1097  HAConfig::PeerConfigPtr remote_config = config_->getPeerConfig(server_name);
1098 
1099  // Create HTTP/1.1 request including our command.
1100  PostHttpRequestJsonPtr request = boost::make_shared<PostHttpRequestJson>
1101  (HttpRequest::Method::HTTP_POST, "/", HttpVersion::HTTP_11());
1102  request->setBodyAsJson(CommandCreator::createDHCPEnable(server_type_));
1103  request->finalize();
1104 
1105  // Response object should also be created because the HTTP client needs
1106  // to know the type of the expected response.
1107  HttpResponseJsonPtr response = boost::make_shared<HttpResponseJson>();
1108 
1109  // Schedule asynchronous HTTP request.
1110  http_client.asyncSendRequest(remote_config->getUrl(), request, response,
1111  [this, remote_config, post_request_action]
1112  (const boost::system::error_code& ec,
1113  const HttpResponsePtr& response,
1114  const std::string& error_str) {
1115 
1116  // There are three possible groups of errors during the heartneat.
1117  // One is the IO error causing issues in communication with the peer.
1118  // Another one is an HTTP parsing error. The last type of error is
1119  // when non-success error code is returned in the response carried
1120  // in the HTTP message or if the JSON response is otherwise broken.
1121 
1122  std::string error_message;
1123 
1124  // Handle first two groups of errors.
1125  if (ec || !error_str.empty()) {
1126  error_message = (ec ? ec.message() : error_str);
1127  LOG_ERROR(ha_logger, HA_DHCP_ENABLE_COMMUNICATIONS_FAILED)
1128  .arg(remote_config->getLogLabel())
1129  .arg(error_message);
1130 
1131  } else {
1132 
1133  // Handle third group of errors.
1134  try {
1135  static_cast<void>(verifyAsyncResponse(response));
1136 
1137  } catch (const std::exception& ex) {
1138  error_message = ex.what();
1139  LOG_ERROR(ha_logger, HA_DHCP_ENABLE_FAILED)
1140  .arg(remote_config->getLogLabel())
1141  .arg(error_message);
1142  }
1143  }
1144 
1145  // If there was an error communicating with the partner, mark the
1146  // partner as unavailable.
1147  if (!error_message.empty()) {
1148  communication_state_->setPartnerState("unavailable");
1149  }
1150 
1151  // Invoke post request action if it was specified.
1152  if (post_request_action) {
1153  post_request_action(error_message.empty(),
1154  error_message);
1155  }
1156  });
1157 }
1158 
1159 void
1160 HAService::localDisableDHCPService() {
1161  network_state_->disableService();
1162 }
1163 
1164 void
1165 HAService::localEnableDHCPService() {
1166  network_state_->enableService();
1167 }
1168 
1169 void
1170 HAService::asyncSyncLeases() {
1171  PostSyncCallback null_action;
1172 
1173  // Timeout is configured in milliseconds. Need to convert to seconds.
1174  unsigned int dhcp_disable_timeout =
1175  static_cast<unsigned int>(config_->getSyncTimeout() / 1000);
1176  if (dhcp_disable_timeout == 0) {
1177  // Ensure that we always use at least 1 second timeout.
1178  dhcp_disable_timeout = 1;
1179  }
1180 
1181  asyncSyncLeases(client_, config_->getFailoverPeerConfig()->getName(),
1182  dhcp_disable_timeout, LeasePtr(), null_action);
1183 }
1184 
1185 void
1186 HAService::asyncSyncLeases(http::HttpClient& http_client,
1187  const std::string& server_name,
1188  const unsigned int max_period,
1189  const dhcp::LeasePtr& last_lease,
1190  PostSyncCallback post_sync_action,
1191  const bool dhcp_disabled) {
1192  // Synchronization starts with a command to disable DHCP service of the
1193  // peer from which we're fetching leases. We don't want the other server
1194  // to allocate new leases while we fetch from it. The DHCP service will
1195  // be disabled for a certain amount of time and will be automatically
1196  // re-enabled if we die during the synchronization.
1197  asyncDisableDHCPService(http_client, server_name, max_period,
1198  [this, &http_client, server_name, max_period, last_lease,
1199  post_sync_action, dhcp_disabled]
1200  (const bool success, const std::string& error_message) {
1201 
1202  // If we have successfully disabled the DHCP service on the peer,
1203  // we can start fetching the leases.
1204  if (success) {
1205  // The last argument indicates that disabling the DHCP
1206  // service on the partner server was successful.
1207  asyncSyncLeasesInternal(http_client, server_name, max_period,
1208  last_lease, post_sync_action, true);
1209 
1210  } else {
1211  post_sync_action(success, error_message, dhcp_disabled);
1212  }
1213  });
1214 }
1215 
1216 void
1217 HAService::asyncSyncLeasesInternal(http::HttpClient& http_client,
1218  const std::string& server_name,
1219  const unsigned int max_period,
1220  const dhcp::LeasePtr& last_lease,
1221  PostSyncCallback post_sync_action,
1222  const bool dhcp_disabled) {
1223 
1224  HAConfig::PeerConfigPtr partner_config = config_->getFailoverPeerConfig();
1225 
1226  // Create HTTP/1.1 request including our command.
1227  PostHttpRequestJsonPtr request = boost::make_shared<PostHttpRequestJson>
1228  (HttpRequest::Method::HTTP_POST, "/", HttpVersion::HTTP_11());
1229  if (server_type_ == HAServerType::DHCPv4) {
1230  request->setBodyAsJson(CommandCreator::createLease4GetPage(
1231  boost::dynamic_pointer_cast<Lease4>(last_lease), config_->getSyncPageLimit()));
1232 
1233  } else {
1234  request->setBodyAsJson(CommandCreator::createLease6GetPage(
1235  boost::dynamic_pointer_cast<Lease6>(last_lease), config_->getSyncPageLimit()));
1236  }
1237  request->finalize();
1238 
1239  // Response object should also be created because the HTTP client needs
1240  // to know the type of the expected response.
1241  HttpResponseJsonPtr response = boost::make_shared<HttpResponseJson>();
1242 
1243  // Schedule asynchronous HTTP request.
1244  http_client.asyncSendRequest(partner_config->getUrl(), request, response,
1245  [this, partner_config, post_sync_action, &http_client, server_name,
1246  max_period, dhcp_disabled]
1247  (const boost::system::error_code& ec,
1248  const HttpResponsePtr& response,
1249  const std::string& error_str) {
1250 
1251  // Holds last lease received on the page of leases. If the last
1252  // page was hit, this value remains null.
1253  LeasePtr last_lease;
1254 
1255  // There are three possible groups of errors during the heartneat.
1256  // One is the IO error causing issues in communication with the peer.
1257  // Another one is an HTTP parsing error. The last type of error is
1258  // when non-success error code is returned in the response carried
1259  // in the HTTP message or if the JSON response is otherwise broken.
1260 
1261  std::string error_message;
1262 
1263  // Handle first two groups of errors.
1264  if (ec || !error_str.empty()) {
1265  error_message = (ec ? ec.message() : error_str);
1266  LOG_ERROR(ha_logger, HA_LEASES_SYNC_COMMUNICATIONS_FAILED)
1267  .arg(partner_config->getLogLabel())
1268  .arg(error_message);
1269 
1270  } else {
1271  // Handle third group of errors.
1272  try {
1273  ConstElementPtr args = verifyAsyncResponse(response);
1274 
1275  // Arguments must be a map.
1276  if (args && (args->getType() != Element::map)) {
1277  isc_throw(CtrlChannelError,
1278  "arguments in the received response must be a map");
1279  }
1280 
1281  ConstElementPtr leases = args->get("leases");
1282  if (!leases || (leases->getType() != Element::list)) {
1283  isc_throw(CtrlChannelError,
1284  "server response does not contain leases argument or this"
1285  " argument is not a list");
1286  }
1287 
1288  // Iterate over the leases and update the database as appropriate.
1289  const auto& leases_element = leases->listValue();
1290 
1291  LOG_INFO(ha_logger, HA_LEASES_SYNC_LEASE_PAGE_RECEIVED)
1292  .arg(leases_element.size())
1293  .arg(server_name);
1294 
1295  for (auto l = leases_element.begin(); l != leases_element.end(); ++l) {
1296  try {
1297 
1298  if (server_type_ == HAServerType::DHCPv4) {
1299  Lease4Ptr lease = Lease4::fromElement(*l);
1300 
1301  // Check if there is such lease in the database already.
1302  Lease4Ptr existing_lease = LeaseMgrFactory::instance().getLease4(lease->addr_);
1303  if (!existing_lease) {
1304  // There is no such lease, so let's add it.
1305  LeaseMgrFactory::instance().addLease(lease);
1306 
1307  } else if (existing_lease->cltt_ < lease->cltt_) {
1308  // If the existing lease is older than the fetched lease, update
1309  // the lease in our local database.
1310  LeaseMgrFactory::instance().updateLease4(lease);
1311 
1312  } else {
1313  LOG_DEBUG(ha_logger, DBGLVL_TRACE_BASIC, HA_LEASE_SYNC_STALE_LEASE4_SKIP)
1314  .arg(lease->addr_.toText())
1315  .arg(lease->subnet_id_);
1316  }
1317 
1318  // If we're not on the last page and we're processing final lease on
1319  // this page, let's record the lease as input to the next
1320  // lease4-get-page command.
1321  if ((leases_element.size() >= config_->getSyncPageLimit()) &&
1322  (l + 1 == leases_element.end())) {
1323  last_lease = boost::dynamic_pointer_cast<Lease>(lease);
1324  }
1325 
1326  } else {
1327  Lease6Ptr lease = Lease6::fromElement(*l);
1328 
1329  // Check if there is such lease in the database already.
1330  Lease6Ptr existing_lease = LeaseMgrFactory::instance().getLease6(lease->type_,
1331  lease->addr_);
1332  if (!existing_lease) {
1333  // There is no such lease, so let's add it.
1334  LeaseMgrFactory::instance().addLease(lease);
1335 
1336  } else if (existing_lease->cltt_ < lease->cltt_) {
1337  // If the existing lease is older than the fetched lease, update
1338  // the lease in our local database.
1339  LeaseMgrFactory::instance().updateLease6(lease);
1340 
1341  } else {
1342  LOG_DEBUG(ha_logger, DBGLVL_TRACE_BASIC, HA_LEASE_SYNC_STALE_LEASE6_SKIP)
1343  .arg(lease->addr_.toText())
1344  .arg(lease->subnet_id_);
1345  }
1346 
1347  // If we're not on the last page and we're processing final lease on
1348  // this page, let's record the lease as input to the next
1349  // lease6-get-page command.
1350  if ((leases_element.size() >= config_->getSyncPageLimit()) &&
1351  (l + 1 == leases_element.end())) {
1352  last_lease = boost::dynamic_pointer_cast<Lease>(lease);
1353  }
1354  }
1355 
1356 
1357  } catch (const std::exception& ex) {
1358  LOG_WARN(ha_logger, HA_LEASE_SYNC_FAILED)
1359  .arg((*l)->str())
1360  .arg(ex.what());
1361  }
1362  }
1363 
1364  } catch (const std::exception& ex) {
1365  error_message = ex.what();
1366  LOG_ERROR(ha_logger, HA_LEASES_SYNC_FAILED)
1367  .arg(partner_config->getLogLabel())
1368  .arg(error_message);
1369  }
1370  }
1371 
1372  // If there was an error communicating with the partner, mark the
1373  // partner as unavailable.
1374  if (!error_message.empty()) {
1375  communication_state_->setPartnerState("unavailable");
1376 
1377  } else if (last_lease) {
1378  // This indicates that there are more leases to be fetched.
1379  // Therefore, we have to send another leaseX-get-page command.
1380  asyncSyncLeases(http_client, server_name, max_period, last_lease,
1381  post_sync_action, dhcp_disabled);
1382  return;
1383  }
1384 
1385  // Invoke post synchronization action if it was specified.
1386  if (post_sync_action) {
1387  post_sync_action(error_message.empty(),
1388  error_message,
1389  dhcp_disabled);
1390  }
1391  }, HttpClient::RequestTimeout(config_->getSyncTimeout()));
1392 }
1393 
1395 HAService::processSynchronize(const std::string& server_name,
1396  const unsigned int max_period) {
1397  std::string answer_message;
1398  int sync_status = synchronize(answer_message, server_name, max_period);
1399  return (createAnswer(sync_status, answer_message));
1400 }
1401 
1402 int
1403 HAService::synchronize(std::string& status_message, const std::string& server_name,
1404  const unsigned int max_period) {
1405  IOService io_service;
1406  HttpClient client(io_service);
1407 
1408  asyncSyncLeases(client, server_name, max_period, Lease4Ptr(),
1409  [&](const bool success, const std::string& error_message,
1410  const bool dhcp_disabled) {
1411  // If there was a fatal error while fetching the leases, let's
1412  // log an error message so as it can be included in the response
1413  // to the controlling client.
1414  if (!success) {
1415  status_message = error_message;
1416  }
1417 
1418  // Whether or not there was an error while fetching the leases,
1419  // we need to re-enable the DHCP service on the peer if the
1420  // DHCP service was disabled in the course of synchronization.
1421  if (dhcp_disabled) {
1422  asyncEnableDHCPService(client, server_name,
1423  [&](const bool success,
1424  const std::string& error_message) {
1425  // It is possible that we have already recorded an error
1426  // message while synchronizing the lease database. Don't
1427  // override the existing error message.
1428  if (!success && status_message.empty()) {
1429  status_message = error_message;
1430  }
1431 
1432  // The synchronization process is completed, so let's break
1433  // the IO service so as we can return the response to the
1434  // controlling client.
1435  io_service.stop();
1436  });
1437 
1438  } else {
1439  // Also stop IO service if there is no need to enable DHCP
1440  // service.
1441  io_service.stop();
1442  }
1443  });
1444 
1445  LOG_INFO(ha_logger, HA_SYNC_START).arg(server_name);
1446 
1447  // Measure duration of the synchronization.
1448  Stopwatch stopwatch;
1449 
1450  // Run the IO service until it is stopped by any of the callbacks. This
1451  // makes it synchronous.
1452  io_service.run();
1453 
1454  // End measuring duration.
1455  stopwatch.stop();
1456 
1457  // If an error message has been recorded, return an error to the controlling
1458  // client.
1459  if (!status_message.empty()) {
1460  postNextEvent(HA_SYNCING_FAILED_EVT);
1461 
1462  LOG_ERROR(ha_logger, HA_SYNC_FAILED)
1463  .arg(server_name)
1464  .arg(status_message);
1465 
1466  return (CONTROL_RESULT_ERROR);
1467 
1468  }
1469 
1470  // Everything was fine, so let's return a success.
1471  status_message = "Lease database synchronization complete.";
1472  postNextEvent(HA_SYNCING_SUCCEEDED_EVT);
1473 
1474  LOG_INFO(ha_logger, HA_SYNC_SUCCESSFUL)
1475  .arg(server_name)
1476  .arg(stopwatch.logFormatLastDuration());
1477 
1478  return (CONTROL_RESULT_SUCCESS);
1479 }
1480 
1481 
1483 HAService::processScopes(const std::vector<std::string>& scopes) {
1484  try {
1485  query_filter_.serveScopes(scopes);
1486  adjustNetworkState();
1487 
1488  } catch (const std::exception& ex) {
1489  return (createAnswer(CONTROL_RESULT_ERROR, ex.what()));
1490  }
1491 
1492  return (createAnswer(CONTROL_RESULT_SUCCESS, "New HA scopes configured."));
1493 }
1494 
1496 HAService::processContinue() {
1497  if (unpause()) {
1498  return (createAnswer(CONTROL_RESULT_SUCCESS, "HA state machine continues."));
1499  }
1500  return (createAnswer(CONTROL_RESULT_SUCCESS, "HA state machine is not paused."));
1501 }
1502 
1504 HAService::verifyAsyncResponse(const HttpResponsePtr& response) {
1505  // The response must cast to JSON type.
1506  HttpResponseJsonPtr json_response =
1507  boost::dynamic_pointer_cast<HttpResponseJson>(response);
1508  if (!json_response) {
1509  isc_throw(CtrlChannelError, "no valid HTTP response found");
1510  }
1511 
1512  // Body holds the response to our command.
1513  ConstElementPtr body = json_response->getBodyAsJson();
1514  if (!body) {
1515  isc_throw(CtrlChannelError, "no body found in the response");
1516  }
1517 
1518  // Body must contain a list of responses form multiple servers.
1519  if (body->getType() != Element::list) {
1520  isc_throw(CtrlChannelError, "body of the response must be a list");
1521  }
1522 
1523  // There must be at least one response.
1524  if (body->empty()) {
1525  isc_throw(CtrlChannelError, "list of responses must not be empty");
1526  }
1527 
1528  // Check if the status code of the first response. We don't support multiple
1529  // at this time, because we always send a request to a single location.
1530  int rcode = 0;
1531  ConstElementPtr args = parseAnswer(rcode, body->get(0));
1532  if ((rcode != CONTROL_RESULT_SUCCESS) &&
1533  (rcode != CONTROL_RESULT_EMPTY)) {
1534  std::ostringstream s;
1535  // Include an error text if available.
1536  if (args && args->getType() == Element::string) {
1537  s << args->stringValue() << ", ";
1538  }
1539  // Include an error code.
1540  s << "error code " << rcode;
1541  isc_throw(CtrlChannelError, s.str());
1542  }
1543 
1544  return (args);
1545 }
1546 
1547 
1548 } // end of namespace isc::ha
1549 } // end of namespace isc
ha_service.h
isc::dhcp::NetworkStatePtr
boost::shared_ptr< NetworkState > NetworkStatePtr
Pointer to the NetworkState object.
Definition: network_state.h:156
isc::ha::HAService::inScope
bool inScope(dhcp::Pkt4Ptr &query4)
Checks if the DHCPv4 query should be processed by this server.
Definition: ha_service.cc:571
isc::ha::HAService::HA_SYNCING_SUCCEEDED_EVT
static const int HA_SYNCING_SUCCEEDED_EVT
Lease database synchroniation succeeded.
Definition: ha_service.h:51
ha_log.h
date_time.h
isc::ha::HAService::HA_SYNCING_FAILED_EVT
static const int HA_SYNCING_FAILED_EVT
Lease database synchronization failed.
Definition: ha_service.h:48
isc::util::StateModel::defineState
void defineState(unsigned int value, const std::string &label, StateHandler handler, const StatePausing &state_pausing=STATE_PAUSE_NEVER)
Adds an state value and associated label to the set of states.
Definition: state_model.cc:194
isc::http::HttpResponseJsonPtr
boost::shared_ptr< HttpResponseJson > HttpResponseJsonPtr
Pointer to the HttpResponseJson object.
Definition: response_json.h:24
isc::http::HttpClient
HTTP client class.
Definition: client.h:66
isc::ha::QueryFilter::serveFailoverScopes
void serveFailoverScopes()
Enable scopes required in failover case.
Definition: query_filter.cc:152
isc::log
Definition: buffer_appender_impl.cc:17
isc::ha::HAService::PostRequestCallback
std::function< void(const bool, const std::string &)> PostRequestCallback
Callback invoked when request was sent and a response received or an error occurred.
Definition: ha_service.h:60
isc::ha::HAConfig::LOAD_BALANCING
@ LOAD_BALANCING
Definition: ha_config.h:38
isc::ha::HA_SYNCING_ST
const int HA_SYNCING_ST
Synchronizing database state.
Definition: ha_service_states.h:32
isc::util::StateModel::unpauseModel
void unpauseModel()
Unpauses state model.
Definition: state_model.cc:267
isc::ha::HAService::defineEvents
virtual void defineEvents()
Defines events used by the HA service.
Definition: ha_service.cc:65
LOG_ERROR
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
isc::ha::HAService::HA_HEARTBEAT_COMPLETE_EVT
static const int HA_HEARTBEAT_COMPLETE_EVT
Finished heartbeat commannd.
Definition: ha_service.h:42
isc::util::StateModel::NOP_EVT
static const int NOP_EVT
Signifies that no event has occurred.
Definition: state_model.h:289
isc::http::HttpDateTime::rfc1123Format
std::string rfc1123Format() const
Returns time value formatted as specified in RFC 1123.
Definition: date_time.cc:30
isc::config::CONTROL_RESULT_SUCCESS
const int CONTROL_RESULT_SUCCESS
Status code indicating a successful operation.
Definition: command_interpreter.h:39
isc::ha::HAService::syncingStateHandler
void syncingStateHandler()
Handler for "syncing" state.
Definition: ha_service.cc:308
isc::ha::HAService::client_
http::HttpClient client_
HTTP client instance used to send lease updates.
Definition: ha_service.h:732
isc::ha::HAService::waitingStateHandler
void waitingStateHandler()
Handler for "waiting" state.
Definition: ha_service.cc:410
isc::ha::HA_LOAD_BALANCING_ST
const int HA_LOAD_BALANCING_ST
Load balancing state.
Definition: ha_service_states.h:23
post_request_json.h
isc::dhcp::Lease4Ptr
boost::shared_ptr< Lease4 > Lease4Ptr
Pointer to a Lease4 structure.
Definition: lease.h:245
isc::ha::HAService::verboseTransition
void verboseTransition(const unsigned state)
Transitions to a desired state and logs it.
Definition: ha_service.cc:491
isc::util::StateModel::getStateLabel
std::string getStateLabel(const int state) const
Fetches the label associated with an state value.
Definition: state_model.cc:380
isc::ha::HAService::readyStateHandler
void readyStateHandler()
Handler for "ready" state.
Definition: ha_service.cc:243
isc::util::StateModel::transition
void transition(unsigned int state, unsigned int event)
Sets up the model to transition into given state with a given event.
Definition: state_model.cc:256
isc::config::createAnswer
ConstElementPtr createAnswer(const int status_code, const std::string &text, const ConstElementPtr &arg)
Definition: command_interpreter.cc:33
isc::util::StateModel::doOnEntry
bool doOnEntry()
Checks if on entry flag is true.
Definition: state_model.cc:317
isc::config
Definition: command_interpreter.cc:23
isc::util::Stopwatch
Utility class to measure code execution times.
Definition: stopwatch.h:35
isc::ha::HA_READY_ST
const int HA_READY_ST
Server ready state, i.e. synchronized database, can enable DHCP service.
Definition: ha_service_states.h:29
isc::ha::HAConfig::HAModeToString
static std::string HAModeToString(const HAMode &ha_mode)
Returns HA mode name.
Definition: ha_config.cc:195
isc::ha::HAService::adjustNetworkState
void adjustNetworkState()
Enables or disables network state depending on the served scopes.
Definition: ha_service.cc:601
isc::ha::HAService::synchronize
int synchronize(std::string &status_message, const std::string &server_name, const unsigned int max_period)
Synchronizes lease database with a partner.
Definition: ha_service.cc:1403
lease_mgr_factory.h
isc::util::StateModel::defineEvent
void defineEvent(unsigned int value, const std::string &label)
Adds an event value and associated label to the set of events.
Definition: state_model.cc:168
isc::ha::HAService::communication_state_
CommunicationStatePtr communication_state_
Holds communication state with a peer.
Definition: ha_service.h:735
isc::config::CONTROL_RESULT_ERROR
const int CONTROL_RESULT_ERROR
Status code indicating a general failure.
Definition: command_interpreter.h:42
isc::data
Definition: cfg_to_element.h:25
isc::config::CONTROL_RESULT_EMPTY
const int CONTROL_RESULT_EMPTY
Status code indicating that the specified command was completed correctly, but failed to produce any ...
Definition: command_interpreter.h:50
isc::ha::HAService::PostSyncCallback
std::function< void(const bool, const std::string &, const bool)> PostSyncCallback
Callback invoked when lease database synchronization is complete.
Definition: ha_service.h:69
isc::http::HttpResponsePtr
boost::shared_ptr< HttpResponse > HttpResponsePtr
Pointer to the HttpResponse object.
Definition: response.h:78
isc::util::Stopwatch::logFormatLastDuration
std::string logFormatLastDuration() const
Returns the last measured duration in the format directly usable in log messages.
Definition: stopwatch.cc:75
lease_mgr.h
An abstract API for lease database.
isc::ha::HAService::shouldPartnerDown
bool shouldPartnerDown() const
Indicates if the server should transition to the partner down state.
Definition: ha_service.cc:630
isc::ha::ha_logger
isc::log::Logger ha_logger("ha-hooks")
Definition: ha_log.h:17
isc::util::StateModel::getEvent
const EventPtr & getEvent(unsigned int value)
Fetches the event referred to by value.
Definition: state_model.cc:184
isc::hooks::ParkingLotHandlePtr
boost::shared_ptr< ParkingLotHandle > ParkingLotHandlePtr
Pointer to the parking lot handle.
Definition: parking_lots.h:292
isc::util::Stopwatch::stop
void stop()
Stops the stopwatch.
Definition: stopwatch.cc:35
isc::util
Definition: edns.h:19
isc::http::HttpClient::RequestTimeout
HTTP request/response timeout value.
Definition: client.h:70
isc
Defines the logger used by the top-level component of kea-dhcp-ddns.
Definition: agent_parser.cc:144
isc::ha::HAServerType::DHCPv4
@ DHCPv4
isc::dhcp::Pkt4Ptr
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition: pkt4.h:546
isc::ha::CommandCreator::createLease4Update
static data::ConstElementPtr createLease4Update(const dhcp::Lease4 &lease4)
Creates lease4-update command.
Definition: command_creator.cc:49
isc::ha::HAService::asyncSendLeaseUpdate
void asyncSendLeaseUpdate(const QueryPtrType &query, const HAConfig::PeerConfigPtr &config, const data::ConstElementPtr &command, const hooks::ParkingLotHandlePtr &parking_lot)
Asynchronously sends lease update to the peer.
Definition: ha_service.cc:753
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition: exceptions/exceptions.h:192
isc::ha::HAService::asyncSendLeaseUpdates
size_t asyncSendLeaseUpdates(const dhcp::Pkt4Ptr &query, const dhcp::Lease4CollectionPtr &leases, const dhcp::Lease4CollectionPtr &deleted_leases, const hooks::ParkingLotHandlePtr &parking_lot)
Schedules asynchronous IPv4 leases updates.
Definition: ha_service.cc:672
isc::ha::HAConfig::PeerConfig::STANDBY
@ STANDBY
Definition: ha_config.h:67
isc::dhcp::Lease6CollectionPtr
boost::shared_ptr< Lease6Collection > Lease6CollectionPtr
A shared pointer to the collection of IPv6 leases.
Definition: lease.h:608
isc::ha::HAService::scheduleHeartbeat
void scheduleHeartbeat()
Schedules asynchronous heartbeat to a peer if it is not scheduled.
Definition: ha_service.cc:1009
command_interpreter.h
isc::util::StateModel::isModelPaused
bool isModelPaused() const
Returns whether or not the model is paused.
Definition: state_model.cc:375
isc::ha::QueryFilter::inScope
bool inScope(const dhcp::Pkt4Ptr &query4, std::string &scope_class) const
Checks if this server should process the DHCPv4 query.
Definition: query_filter.cc:200
isc::ha::stateToString
std::string stateToString(int state)
Returns state name.
Definition: ha_service_states.cc:12
isc::ha::HAService::backupStateHandler
void backupStateHandler()
Handler for the "backup" state.
Definition: ha_service.cc:122
isc::dhcp
Definition: ctrl_dhcp4_srv.cc:75
isc::ha::HA_PARTNER_DOWN_ST
const int HA_PARTNER_DOWN_ST
Partner down state.
Definition: ha_service_states.h:26
isc::ha::HAConfig::PeerConfig::BACKUP
@ BACKUP
Definition: ha_config.h:68
isc::ha::HAService::io_service_
asiolink::IOServicePtr io_service_
Pointer to the IO service object shared between this hooks library and the DHCP server.
Definition: ha_service.h:720
command_creator.h
isc::ha::QueryFilter::serveDefaultScopes
void serveDefaultScopes()
Serve default scopes for the given HA mode.
Definition: query_filter.cc:135
isc::ha::QueryFilter::serveNoScopes
void serveNoScopes()
Disables all scopes.
Definition: query_filter.cc:173
isc::ha::CommandCreator::createLease4Delete
static data::ConstElementPtr createLease4Delete(const dhcp::Lease4 &lease4)
Creates lease4-del command.
Definition: command_creator.cc:59
isc::ha::CommunicationState6
Holds communication state between DHCPv6 servers.
Definition: communication_state.h:389
LOG_WARN
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition: macros.h:26
isc::dhcp::LeasePtr
boost::shared_ptr< Lease > LeasePtr
Pointer to the lease object.
Definition: lease.h:26
isc::ha::HAService::serveDefaultScopes
void serveDefaultScopes()
Instructs the HA service to serve default scopes.
Definition: ha_service.cc:566
isc::ha::HAServerType
HAServerType
Lists possible server types for which HA service is created.
Definition: ha_server_type.h:14
isc::dhcp::Lease4CollectionPtr
boost::shared_ptr< Lease4Collection > Lease4CollectionPtr
A shared pointer to the collection of IPv4 leases.
Definition: lease.h:458
isc::ha::HAService::unpause
bool unpause()
Unpauses the HA state machine with logging.
Definition: ha_service.cc:545
isc::ha::HAService::conditionalLogPausedState
void conditionalLogPausedState() const
Logs if the server is paused in the current state.
Definition: ha_service.cc:555
isc::ha::HAConfig::PeerConfigMap
std::map< std::string, PeerConfigPtr > PeerConfigMap
Map of the servers' configurations.
Definition: ha_config.h:162
isc::ha::HAService::HA_LEASE_UPDATES_COMPLETE_EVT
static const int HA_LEASE_UPDATES_COMPLETE_EVT
Finished lease updates commands.
Definition: ha_service.h:45
isc::http::HttpClient::asyncSendRequest
void asyncSendRequest(const Url &url, const HttpRequestPtr &request, const HttpResponsePtr &response, const RequestHandler &request_callback, const RequestTimeout &request_timeout=RequestTimeout(10000), const ConnectHandler &connect_callback=ConnectHandler())
Queues new asynchronous HTTP request.
Definition: client.cc:768
isc::ha::HAService::query_filter_
QueryFilter query_filter_
Selects queries to be processed/dropped.
Definition: ha_service.h:738
isc::ha::HAService::shouldSendLeaseUpdates
bool shouldSendLeaseUpdates(const HAConfig::PeerConfigPtr &peer_config) const
Checks if the lease updates should be sent as result of leases allocation or release.
Definition: ha_service.cc:871
isc::config::parseAnswer
ConstElementPtr parseAnswer(int &rcode, const ConstElementPtr &msg)
Definition: command_interpreter.cc:68
response_json.h
isc::util::StateModel::getCurrState
unsigned int getCurrState() const
Fetches the model's current state.
Definition: state_model.cc:331
isc::ha::HAConfigPtr
boost::shared_ptr< HAConfig > HAConfigPtr
Pointer to the High Availability configuration structure.
Definition: ha_config.h:509
isc::ha::HAConfig::PeerConfigPtr
boost::shared_ptr< PeerConfig > PeerConfigPtr
Pointer to the server's configuration.
Definition: ha_config.h:159
isc::dhcp::ClientClass
std::string ClientClass
Defines a single class name.
Definition: classify.h:37
isc::ha::CommandCreator::createLease6Delete
static data::ConstElementPtr createLease6Delete(const dhcp::Lease6 &lease6)
Creates lease6-del command.
Definition: command_creator.cc:110
isc::ha::HA_UNAVAILABLE_ST
const int HA_UNAVAILABLE_ST
Special state indicating that this server is unable to communicate with the partner.
Definition: ha_service_states.h:42
data.h
isc::ha::HAService::network_state_
dhcp::NetworkStatePtr network_state_
Pointer to the state of the DHCP service (enabled/disabled).
Definition: ha_service.h:723
isc::util::StateModel::getNextEvent
unsigned int getNextEvent() const
Fetches the model's next event.
Definition: state_model.cc:346
isc::hooks
Definition: callout_handle.cc:21
isc::ha::HAService::verifyEvents
virtual void verifyEvents()
Verifies events used by the HA service.
Definition: ha_service.cc:75
isc::http::PostHttpRequestJsonPtr
boost::shared_ptr< PostHttpRequestJson > PostHttpRequestJsonPtr
Pointer to PostHttpRequestJson.
Definition: post_request_json.h:26
isc::http::HttpDateTime
This class parses and generates time values used in HTTP.
Definition: date_time.h:41
isc::ha::HAConfig::PeerConfig::PRIMARY
@ PRIMARY
Definition: ha_config.h:65
isc::util::StateModel::postNextEvent
void postNextEvent(unsigned int event)
Sets the next event to the given event value.
Definition: state_model.cc:304
isc::data::ElementPtr
boost::shared_ptr< Element > ElementPtr
Definition: data.h:20
isc::http
Definition: client.cc:745
isc::data::ConstElementPtr
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:23
isc::ha::HAService::partnerDownStateHandler
void partnerDownStateHandler()
Handler for "partner-down" state.
Definition: ha_service.cc:187
isc::ha::HA_TERMINATED_ST
const int HA_TERMINATED_ST
HA service terminated state.
Definition: ha_service_states.h:35
isc::ha::HA_WAITING_ST
const int HA_WAITING_ST
Server waiting state, i.e. waiting for another server to be ready.
Definition: ha_service_states.h:38
isc::ha::HAService::shouldTerminate
bool shouldTerminate() const
Indicates if the server should transition to the terminated state as a result of high clock skew.
Definition: ha_service.cc:657
isc::ha::HA_BACKUP_ST
const int HA_BACKUP_ST
Backup state.
Definition: ha_service_states.h:17
isc::config::CtrlChannelError
A standard control channel exception that is thrown if a function is there is a problem with one of t...
Definition: command_interpreter.h:54
if
if(!(yy_init))
Definition: agent_lexer.cc:1692
isc::util::StateModel::startModel
void startModel(const int start_state)
Begins execution of the model.
Definition: state_model.cc:101
isc::ha::HAService::defineStates
virtual void defineStates()
Defines states of the HA service.
Definition: ha_service.cc:85
isc::ha::HAService::normalStateHandler
void normalStateHandler()
Handler for the "hot-standby" and "load-balancing" states.
Definition: ha_service.cc:137
isc::ha::CommandCreator::createLease6Update
static data::ConstElementPtr createLease6Update(const dhcp::Lease6 &lease6)
Creates lease6-update command.
Definition: command_creator.cc:100
ha_service_states.h
isc::dhcp::Pkt6Ptr
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition: pkt6.h:28
isc::ha::HAService::terminatedStateHandler
void terminatedStateHandler()
Handler for "terminated" state.
Definition: ha_service.cc:389
LOG_INFO
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
isc::ha::HA_HOT_STANDBY_ST
const int HA_HOT_STANDBY_ST
Hot standby state.
Definition: ha_service_states.h:20
isc::ha::HAConfig::PeerConfig::roleToString
static std::string roleToString(const HAConfig::PeerConfig::Role &role)
Returns role name.
Definition: ha_config.cc:66
stopwatch.h
isc::ha::CommunicationState4
Holds communication state between DHCPv4 servers.
Definition: communication_state.h:331
isc::ha::HAService::config_
HAConfigPtr config_
Pointer to the HA hooks library configuration.
Definition: ha_service.h:726