21 #include <boost/pointer_cast.hpp>
22 #include <boost/bind.hpp>
23 #include <boost/make_shared.hpp>
24 #include <boost/weak_ptr.hpp>
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;
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_() {
66 StateModel::defineEvents();
76 StateModel::verifyEvents();
86 StateModel::defineStates();
196 if (
config_->getThisServerConfig()->isAutoFailover()) {
359 unsigned int dhcp_disable_timeout =
360 static_cast<unsigned int>(
config_->getSyncTimeout() / 1000);
361 if (dhcp_disable_timeout == 0) {
362 ++dhcp_disable_timeout;
366 std::string status_message;
368 config_->getFailoverPeerConfig()->getName(),
369 dhcp_disable_timeout);
497 std::string partner_state_name =
getStateLabel(partner_state);
500 boost::to_upper(current_state_name);
501 boost::to_upper(new_state_name);
502 boost::to_upper(partner_state_name);
506 .arg(current_state_name)
508 .arg(partner_state_name);
527 .arg(new_state_name);
529 }
else if (!
config_->amSendingLeaseUpdates()) {
532 .arg(new_state_name);
539 .arg(new_state_name);
559 boost::to_upper(state_name);
572 return (inScopeInternal(query4));
577 return (inScopeInternal(query6));
580 template<
typename QueryPtrType>
582 HAService::inScopeInternal(QueryPtrType& query) {
584 std::string scope_class;
603 boost::to_upper(current_state_name);
613 boost::to_upper(current_state_name);
615 .arg(
config_->getThisServerName())
616 .arg(current_state_name);
619 }
else if (should_enable && !
network_state_->isServiceEnabled()) {
621 boost::to_upper(current_state_name);
623 .arg(
config_->getThisServerName())
624 .arg(current_state_name);
683 for (
auto p = peers_configs.begin(); p != peers_configs.end(); ++p) {
696 for (
auto l = deleted_leases->begin(); l != deleted_leases->end(); ++l) {
702 for (
auto l = leases->begin(); l != leases->end(); ++l) {
723 for (
auto p = peers_configs.begin(); p != peers_configs.end(); ++p) {
736 for (
auto l = deleted_leases->begin(); l != deleted_leases->end(); ++l) {
742 for (
auto l = leases->begin(); l != leases->end(); ++l) {
751 template<
typename QueryPtrType>
759 (HttpRequest::Method::HTTP_POST,
"/", HttpVersion::HTTP_11());
760 request->setBodyAsJson(command);
770 boost::weak_ptr<typename QueryPtrType::element_type> weak_query(query);
774 [
this, weak_query, parking_lot, config]
775 (
const boost::system::error_code& ec,
777 const std::string& error_str) {
781 QueryPtrType query = weak_query.lock();
783 isc_throw(Unexpected,
"query is null while receiving response from"
784 " HA peer. This is programmatic error");
793 bool lease_update_success =
true;
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);
804 lease_update_success = false;
810 verifyAsyncResponse(response);
812 } catch (
const std::exception& ex) {
814 .arg(query->getLabel())
815 .arg(config->getLogLabel())
819 lease_update_success =
false;
826 if (lease_update_success) {
830 communication_state_->poke();
834 parking_lot->drop(query);
835 communication_state_->setPartnerState(
"unavailable");
839 auto it = pending_requests_.find(query);
843 if (it == pending_requests_.end() || (--pending_requests_[query] <= 0)) {
844 parking_lot->unpark(query);
848 if (it != pending_requests_.end()) {
849 pending_requests_.erase(it);
857 runModel(HA_LEASE_UPDATES_COMPLETE_EVT);
862 if (pending_requests_.count(query) == 0) {
863 pending_requests_[query] = 1;
866 ++pending_requests_[query];
873 if (!config_->amSendingLeaseUpdates()) {
878 if (peer_config->getRole() == HAConfig::PeerConfig::BACKUP) {
883 if (config_->getThisServerConfig()->getRole() == HAConfig::PeerConfig::BACKUP) {
889 switch (getCurrState()) {
902 HAService::processHeartbeat() {
904 std::string state_label = getState(getCurrState())->getLabel();
905 arguments->set(
"state", Element::create(state_label));
908 arguments->set(
"date-time", Element::create(date_time));
915 HAService::asyncSendHeartbeat() {
920 (HttpRequest::Method::HTTP_POST,
"/", HttpVersion::HTTP_11());
921 request->setBodyAsJson(CommandCreator::createHeartbeat(server_type_));
929 client_.asyncSendRequest(partner_config->getUrl(), request, response,
930 [
this, partner_config]
931 (
const boost::system::error_code& ec,
933 const std::string& error_str) {
941 bool heartbeat_success = true;
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;
956 ConstElementPtr args = verifyAsyncResponse(response);
957 if (!args || args->getType() != Element::map) {
958 isc_throw(CtrlChannelError,
"returned arguments in the response"
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");
969 communication_state_->setPartnerState(state->stringValue());
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");
977 communication_state_->setPartnerTime(date_time->stringValue());
979 } catch (
const std::exception& ex) {
981 .arg(partner_config->getLogLabel())
983 heartbeat_success =
false;
989 if (heartbeat_success) {
990 communication_state_->poke();
995 communication_state_->setPartnerState(
"unavailable");
1004 runModel(HA_HEARTBEAT_COMPLETE_EVT);
1009 HAService::scheduleHeartbeat() {
1010 if (!communication_state_->isHeartbeatRunning()) {
1016 HAService::startHeartbeat() {
1017 if (config_->getHeartbeatDelay() > 0) {
1018 communication_state_->startHeartbeat(config_->getHeartbeatDelay(),
1019 boost::bind(&HAService::asyncSendHeartbeat,
1026 const std::string& server_name,
1027 const unsigned int max_period,
1033 (HttpRequest::Method::HTTP_POST,
"/", HttpVersion::HTTP_11());
1035 request->setBodyAsJson(CommandCreator::createDHCPDisable(max_period,
1037 request->finalize();
1045 [
this, remote_config, post_request_action]
1046 (
const boost::system::error_code& ec,
1048 const std::string& error_str) {
1056 std::string error_message;
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);
1069 static_cast<void>(verifyAsyncResponse(response));
1071 } catch (
const std::exception& ex) {
1072 error_message = ex.what();
1074 .arg(remote_config->getLogLabel())
1075 .arg(error_message);
1081 if (!error_message.empty()) {
1082 communication_state_->setPartnerState(
"unavailable");
1086 if (post_request_action) {
1087 post_request_action(error_message.empty(),
1095 const std::string& server_name,
1101 (HttpRequest::Method::HTTP_POST,
"/", HttpVersion::HTTP_11());
1102 request->setBodyAsJson(CommandCreator::createDHCPEnable(server_type_));
1103 request->finalize();
1111 [
this, remote_config, post_request_action]
1112 (
const boost::system::error_code& ec,
1114 const std::string& error_str) {
1122 std::string error_message;
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);
1135 static_cast<void>(verifyAsyncResponse(response));
1137 } catch (
const std::exception& ex) {
1138 error_message = ex.what();
1140 .arg(remote_config->getLogLabel())
1141 .arg(error_message);
1147 if (!error_message.empty()) {
1148 communication_state_->setPartnerState(
"unavailable");
1152 if (post_request_action) {
1153 post_request_action(error_message.empty(),
1160 HAService::localDisableDHCPService() {
1161 network_state_->disableService();
1165 HAService::localEnableDHCPService() {
1166 network_state_->enableService();
1170 HAService::asyncSyncLeases() {
1174 unsigned int dhcp_disable_timeout =
1175 static_cast<unsigned int>(config_->getSyncTimeout() / 1000);
1176 if (dhcp_disable_timeout == 0) {
1178 dhcp_disable_timeout = 1;
1181 asyncSyncLeases(client_, config_->getFailoverPeerConfig()->getName(),
1182 dhcp_disable_timeout,
LeasePtr(), null_action);
1187 const std::string& server_name,
1188 const unsigned int max_period,
1191 const bool dhcp_disabled) {
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) {
1207 asyncSyncLeasesInternal(http_client, server_name, max_period,
1208 last_lease, post_sync_action,
true);
1211 post_sync_action(success, error_message, dhcp_disabled);
1218 const std::string& server_name,
1219 const unsigned int max_period,
1222 const bool dhcp_disabled) {
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()));
1234 request->setBodyAsJson(CommandCreator::createLease6GetPage(
1235 boost::dynamic_pointer_cast<Lease6>(last_lease), config_->getSyncPageLimit()));
1237 request->finalize();
1245 [
this, partner_config, post_sync_action, &http_client, server_name,
1246 max_period, dhcp_disabled]
1247 (
const boost::system::error_code& ec,
1249 const std::string& error_str) {
1253 LeasePtr last_lease;
1261 std::string error_message;
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);
1273 ConstElementPtr args = verifyAsyncResponse(response);
1276 if (args && (args->getType() != Element::map)) {
1277 isc_throw(CtrlChannelError,
1278 "arguments in the received response must be a map");
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");
1289 const auto& leases_element = leases->listValue();
1291 LOG_INFO(ha_logger, HA_LEASES_SYNC_LEASE_PAGE_RECEIVED)
1292 .arg(leases_element.size())
1295 for (auto l = leases_element.begin(); l != leases_element.end(); ++l) {
1298 if (server_type_ == HAServerType::DHCPv4) {
1299 Lease4Ptr lease = Lease4::fromElement(*l);
1302 Lease4Ptr existing_lease = LeaseMgrFactory::instance().getLease4(lease->addr_);
1303 if (!existing_lease) {
1305 LeaseMgrFactory::instance().addLease(lease);
1307 } else if (existing_lease->cltt_ < lease->cltt_) {
1310 LeaseMgrFactory::instance().updateLease4(lease);
1313 LOG_DEBUG(ha_logger, DBGLVL_TRACE_BASIC, HA_LEASE_SYNC_STALE_LEASE4_SKIP)
1314 .arg(lease->addr_.toText())
1315 .arg(lease->subnet_id_);
1321 if ((leases_element.size() >= config_->getSyncPageLimit()) &&
1322 (l + 1 == leases_element.end())) {
1323 last_lease = boost::dynamic_pointer_cast<Lease>(lease);
1327 Lease6Ptr lease = Lease6::fromElement(*l);
1330 Lease6Ptr existing_lease = LeaseMgrFactory::instance().getLease6(lease->type_,
1332 if (!existing_lease) {
1334 LeaseMgrFactory::instance().addLease(lease);
1336 } else if (existing_lease->cltt_ < lease->cltt_) {
1339 LeaseMgrFactory::instance().updateLease6(lease);
1342 LOG_DEBUG(ha_logger, DBGLVL_TRACE_BASIC, HA_LEASE_SYNC_STALE_LEASE6_SKIP)
1343 .arg(lease->addr_.toText())
1344 .arg(lease->subnet_id_);
1350 if ((leases_element.size() >= config_->getSyncPageLimit()) &&
1351 (l + 1 == leases_element.end())) {
1352 last_lease = boost::dynamic_pointer_cast<Lease>(lease);
1357 } catch (const std::exception& ex) {
1358 LOG_WARN(ha_logger, HA_LEASE_SYNC_FAILED)
1364 } catch (
const std::exception& ex) {
1365 error_message = ex.what();
1367 .arg(partner_config->getLogLabel())
1368 .arg(error_message);
1374 if (!error_message.empty()) {
1375 communication_state_->setPartnerState(
"unavailable");
1377 }
else if (last_lease) {
1380 asyncSyncLeases(http_client, server_name, max_period, last_lease,
1381 post_sync_action, dhcp_disabled);
1386 if (post_sync_action) {
1387 post_sync_action(error_message.empty(),
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);
1403 HAService::synchronize(std::string& status_message,
const std::string& server_name,
1404 const unsigned int max_period) {
1408 asyncSyncLeases(client, server_name, max_period,
Lease4Ptr(),
1409 [&](
const bool success,
const std::string& error_message,
1410 const bool dhcp_disabled) {
1415 status_message = error_message;
1421 if (dhcp_disabled) {
1422 asyncEnableDHCPService(client, server_name,
1423 [&](
const bool success,
1424 const std::string& error_message) {
1428 if (!success && status_message.empty()) {
1429 status_message = error_message;
1459 if (!status_message.empty()) {
1460 postNextEvent(HA_SYNCING_FAILED_EVT);
1464 .arg(status_message);
1471 status_message =
"Lease database synchronization complete.";
1472 postNextEvent(HA_SYNCING_SUCCEEDED_EVT);
1483 HAService::processScopes(
const std::vector<std::string>& scopes) {
1485 query_filter_.serveScopes(scopes);
1486 adjustNetworkState();
1488 }
catch (
const std::exception& ex) {
1496 HAService::processContinue() {
1507 boost::dynamic_pointer_cast<HttpResponseJson>(response);
1508 if (!json_response) {
1519 if (body->getType() != Element::list) {
1524 if (body->empty()) {
1534 std::ostringstream s;
1536 if (args && args->getType() == Element::string) {
1537 s << args->stringValue() <<
", ";
1540 s <<
"error code " << rcode;