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>
14#include <cc/data.h>
15#include <dhcpsrv/lease_mgr.h>
17#include <http/date_time.h>
18#include <http/response_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
27using namespace isc::asiolink;
28using namespace isc::config;
29using namespace isc::data;
30using namespace isc::dhcp;
31using namespace isc::hooks;
32using namespace isc::http;
33using namespace isc::log;
34using namespace isc::util;
35
36namespace isc {
37namespace ha {
38
43
44HAService::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
64void
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
74void
77
82}
83
84void
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
121void
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
136void
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()) {
166 break;
167
168 case HA_TERMINATED_ST:
170 break;
171
173 if (shouldPartnerDown()) {
175
176 } else {
178 }
179 break;
180
181 default:
183 }
184}
185
186void
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()) {
226 break;
227
228 case HA_READY_ST:
231 break;
232
233 case HA_TERMINATED_ST:
235 break;
236
237 default:
239 }
240}
241
242void
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()) {
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
294 if (shouldPartnerDown()) {
296
297 } else {
299 }
300 break;
301
302 default:
304 }
305}
306
307void
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
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
388void
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
409void
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()) {
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
477 if (shouldPartnerDown()) {
479
480 } else {
482 }
483 break;
484
485 default:
487 }
488}
489
490void
491HAService::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
544bool
546 if (isModelPaused()) {
547 LOG_INFO(ha_logger, HA_STATE_MACHINE_CONTINUED);
548 unpauseModel();
549 return (true);
550 }
551 return (false);
552}
553
554void
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
565void
568}
569
570bool
572 return (inScopeInternal(query4));
573}
574
575bool
577 return (inScopeInternal(query6));
578}
579
580template<typename QueryPtrType>
581bool
582HAService::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
600void
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
629bool
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
656bool
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
671size_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
711size_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
751template<typename QueryPtrType>
752void
753HAService::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
870bool
871HAService::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()) {
892 return (true);
893
894 default:
895 ;
896 }
897
898 return (false);
899}
900
902HAService::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
914void
915HAService::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
1008void
1009HAService::scheduleHeartbeat() {
1010 if (!communication_state_->isHeartbeatRunning()) {
1011 startHeartbeat();
1012 }
1013}
1014
1015void
1016HAService::startHeartbeat() {
1017 if (config_->getHeartbeatDelay() > 0) {
1018 communication_state_->startHeartbeat(config_->getHeartbeatDelay(),
1019 boost::bind(&HAService::asyncSendHeartbeat,
1020 this));
1021 }
1022}
1023
1024void
1025HAService::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
1093void
1094HAService::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
1159void
1160HAService::localDisableDHCPService() {
1161 network_state_->disableService();
1162}
1163
1164void
1165HAService::localEnableDHCPService() {
1166 network_state_->enableService();
1167}
1168
1169void
1170HAService::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
1185void
1186HAService::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
1216void
1217HAService::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
1395HAService::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
1402int
1403HAService::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
1483HAService::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
1496HAService::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
1504HAService::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
if(!(yy_init))
A standard control channel exception that is thrown if a function is there is a problem with one of t...
static ElementPtr create(const Position &pos=ZERO_POSITION())
Definition: data.cc:223
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition: data.cc:268
static data::ConstElementPtr createLease4Delete(const dhcp::Lease4 &lease4)
Creates lease4-del command.
static data::ConstElementPtr createLease4Update(const dhcp::Lease4 &lease4)
Creates lease4-update command.
static data::ConstElementPtr createLease6Update(const dhcp::Lease6 &lease6)
Creates lease6-update command.
static data::ConstElementPtr createLease6Delete(const dhcp::Lease6 &lease6)
Creates lease6-del command.
Holds communication state between DHCPv4 servers.
Holds communication state between DHCPv6 servers.
static std::string roleToString(const HAConfig::PeerConfig::Role &role)
Returns role name.
Definition: ha_config.cc:66
std::map< std::string, PeerConfigPtr > PeerConfigMap
Map of the servers' configurations.
Definition: ha_config.h:162
static std::string HAModeToString(const HAMode &ha_mode)
Returns HA mode name.
Definition: ha_config.cc:195
boost::shared_ptr< PeerConfig > PeerConfigPtr
Pointer to the server's configuration.
Definition: ha_config.h:159
bool inScope(dhcp::Pkt4Ptr &query4)
Checks if the DHCPv4 query should be processed by this server.
Definition: ha_service.cc:571
void adjustNetworkState()
Enables or disables network state depending on the served scopes.
Definition: ha_service.cc:601
static const int HA_HEARTBEAT_COMPLETE_EVT
Finished heartbeat commannd.
Definition: ha_service.h:42
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
void verboseTransition(const unsigned state)
Transitions to a desired state and logs it.
Definition: ha_service.cc:491
http::HttpClient client_
HTTP client instance used to send lease updates.
Definition: ha_service.h:732
HAConfigPtr config_
Pointer to the HA hooks library configuration.
Definition: ha_service.h:726
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
void terminatedStateHandler()
Handler for "terminated" state.
Definition: ha_service.cc:389
dhcp::NetworkStatePtr network_state_
Pointer to the state of the DHCP service (enabled/disabled).
Definition: ha_service.h:723
HAService(const asiolink::IOServicePtr &io_service, const dhcp::NetworkStatePtr &network_state, const HAConfigPtr &config, const HAServerType &server_type=HAServerType::DHCPv4)
Constructor.
Definition: ha_service.cc:44
void scheduleHeartbeat()
Schedules asynchronous heartbeat to a peer if it is not scheduled.
Definition: ha_service.cc:1009
QueryFilter query_filter_
Selects queries to be processed/dropped.
Definition: ha_service.h:738
virtual void verifyEvents()
Verifies events used by the HA service.
Definition: ha_service.cc:75
void conditionalLogPausedState() const
Logs if the server is paused in the current state.
Definition: ha_service.cc:555
bool unpause()
Unpauses the HA state machine with logging.
Definition: ha_service.cc:545
void serveDefaultScopes()
Instructs the HA service to serve default scopes.
Definition: ha_service.cc:566
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
static const int HA_SYNCING_SUCCEEDED_EVT
Lease database synchroniation succeeded.
Definition: ha_service.h:51
virtual void defineEvents()
Defines events used by the HA service.
Definition: ha_service.cc:65
asiolink::IOServicePtr io_service_
Pointer to the IO service object shared between this hooks library and the DHCP server.
Definition: ha_service.h:720
CommunicationStatePtr communication_state_
Holds communication state with a peer.
Definition: ha_service.h:735
static const int HA_SYNCING_FAILED_EVT
Lease database synchronization failed.
Definition: ha_service.h:48
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
void readyStateHandler()
Handler for "ready" state.
Definition: ha_service.cc:243
virtual void defineStates()
Defines states of the HA service.
Definition: ha_service.cc:85
void backupStateHandler()
Handler for the "backup" state.
Definition: ha_service.cc:122
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
void normalStateHandler()
Handler for the "hot-standby" and "load-balancing" states.
Definition: ha_service.cc:137
void waitingStateHandler()
Handler for "waiting" state.
Definition: ha_service.cc:410
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
static const int HA_LEASE_UPDATES_COMPLETE_EVT
Finished lease updates commands.
Definition: ha_service.h:45
void partnerDownStateHandler()
Handler for "partner-down" state.
Definition: ha_service.cc:187
bool shouldPartnerDown() const
Indicates if the server should transition to the partner down state.
Definition: ha_service.cc:630
std::function< void(const bool, const std::string &, const bool)> PostSyncCallback
Callback invoked when lease database synchronization is complete.
Definition: ha_service.h:69
void syncingStateHandler()
Handler for "syncing" state.
Definition: ha_service.cc:308
bool inScope(const dhcp::Pkt4Ptr &query4, std::string &scope_class) const
Checks if this server should process the DHCPv4 query.
void serveFailoverScopes()
Enable scopes required in failover case.
void serveDefaultScopes()
Serve default scopes for the given HA mode.
void serveNoScopes()
Disables all scopes.
HTTP client class.
Definition: client.h:66
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
This class parses and generates time values used in HTTP.
Definition: date_time.h:41
std::string rfc1123Format() const
Returns time value formatted as specified in RFC 1123.
Definition: date_time.cc:30
const EventPtr & getEvent(unsigned int value)
Fetches the event referred to by value.
Definition: state_model.cc:184
std::string getStateLabel(const int state) const
Fetches the label associated with an state value.
Definition: state_model.cc:380
void unpauseModel()
Unpauses state model.
Definition: state_model.cc:267
bool isModelPaused() const
Returns whether or not the model is paused.
Definition: state_model.cc:375
virtual void defineEvents()
Populates the set of events.
Definition: state_model.cc:221
void postNextEvent(unsigned int event)
Sets the next event to the given event value.
Definition: state_model.cc:304
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
unsigned int getNextEvent() const
Fetches the model's next event.
Definition: state_model.cc:346
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
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
virtual void verifyEvents()
Validates the contents of the set of events.
Definition: state_model.cc:229
bool doOnEntry()
Checks if on entry flag is true.
Definition: state_model.cc:317
static const int NOP_EVT
Signifies that no event has occurred.
Definition: state_model.h:289
void startModel(const int start_state)
Begins execution of the model.
Definition: state_model.cc:101
virtual void defineStates()
Populates the set of states.
Definition: state_model.cc:237
unsigned int getCurrState() const
Fetches the model's current state.
Definition: state_model.cc:331
Utility class to measure code execution times.
Definition: stopwatch.h:35
void stop()
Stops the stopwatch.
Definition: stopwatch.cc:35
std::string logFormatLastDuration() const
Returns the last measured duration in the format directly usable in log messages.
Definition: stopwatch.cc:75
This file contains several functions and constants that are used for handling commands and responses ...
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
An abstract API for lease database.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition: macros.h:26
const int CONTROL_RESULT_EMPTY
Status code indicating that the specified command was completed correctly, but failed to produce any ...
const int CONTROL_RESULT_ERROR
Status code indicating a general failure.
ConstElementPtr createAnswer()
Creates a standard config/command level success answer message (i.e.
ConstElementPtr parseAnswer(int &rcode, const ConstElementPtr &msg)
const int CONTROL_RESULT_SUCCESS
Status code indicating a successful operation.
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:23
boost::shared_ptr< Element > ElementPtr
Definition: data.h:22
std::string ClientClass
Defines a single class name.
Definition: classify.h:37
boost::shared_ptr< Lease4Collection > Lease4CollectionPtr
A shared pointer to the collection of IPv4 leases.
Definition: lease.h:458
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition: pkt4.h:546
boost::shared_ptr< Lease > LeasePtr
Pointer to the lease object.
Definition: lease.h:29
boost::shared_ptr< NetworkState > NetworkStatePtr
Pointer to the NetworkState object.
boost::shared_ptr< Lease6Collection > Lease6CollectionPtr
A shared pointer to the collection of IPv6 leases.
Definition: lease.h:608
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition: pkt6.h:31
boost::shared_ptr< Lease4 > Lease4Ptr
Pointer to a Lease4 structure.
Definition: lease.h:248
const int HA_HOT_STANDBY_ST
Hot standby state.
isc::log::Logger ha_logger("ha-hooks")
Definition: ha_log.h:17
const int HA_UNAVAILABLE_ST
Special state indicating that this server is unable to communicate with the partner.
const int HA_TERMINATED_ST
HA service terminated state.
const int HA_LOAD_BALANCING_ST
Load balancing state.
const int HA_PARTNER_DOWN_ST
Partner down state.
boost::shared_ptr< HAConfig > HAConfigPtr
Pointer to the High Availability configuration structure.
Definition: ha_config.h:509
const int HA_WAITING_ST
Server waiting state, i.e. waiting for another server to be ready.
HAServerType
Lists possible server types for which HA service is created.
const int HA_BACKUP_ST
Backup state.
const int HA_SYNCING_ST
Synchronizing database state.
std::string stateToString(int state)
Returns state name.
const int HA_READY_ST
Server ready state, i.e. synchronized database, can enable DHCP service.
boost::shared_ptr< ParkingLotHandle > ParkingLotHandlePtr
Pointer to the parking lot handle.
Definition: parking_lots.h:292
boost::shared_ptr< PostHttpRequestJson > PostHttpRequestJsonPtr
Pointer to PostHttpRequestJson.
boost::shared_ptr< HttpResponseJson > HttpResponseJsonPtr
Pointer to the HttpResponseJson object.
Definition: response_json.h:27
boost::shared_ptr< HttpResponse > HttpResponsePtr
Pointer to the HttpResponse object.
Definition: response.h:81
Definition: edns.h:19
Defines the logger used by the top-level component of kea-dhcp-ddns.
HTTP request/response timeout value.
Definition: client.h:70
static const HttpVersion & HTTP_11()
HTTP version 1.1.
Definition: http_types.h:59