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();
784 " HA peer. This is programmatic error");
793 bool lease_update_success =
true;
796 if (ec || !error_str.empty()) {
798 .arg(query->getLabel())
799 .arg(config->getLogLabel())
800 .arg(ec ? ec.message() : error_str);
804 lease_update_success =
false;
812 }
catch (
const std::exception& ex) {
814 .arg(query->getLabel())
815 .arg(config->getLogLabel())
819 lease_update_success =
false;
826 if (lease_update_success) {
834 parking_lot->drop(query);
844 parking_lot->unpark(query);
873 if (!
config_->amSendingLeaseUpdates()) {
905 arguments->set(
"state", Element::create(state_label));
908 arguments->set(
"date-time", Element::create(date_time));
920 (HttpRequest::Method::HTTP_POST,
"/", HttpVersion::HTTP_11());
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()) {
946 .arg(partner_config->getLogLabel())
947 .arg(ec ? ec.message() : error_str);
948 heartbeat_success =
false;
957 if (!args || args->getType() != Element::map) {
963 if (!state || state->getType() != Element::string) {
965 " to a ha-heartbeat command or it is not a string");
972 if (!date_time || date_time->getType() != Element::string) {
974 " to a ha-heartbeat command or it is not a string");
979 }
catch (
const std::exception& ex) {
981 .arg(partner_config->getLogLabel())
983 heartbeat_success =
false;
989 if (heartbeat_success) {
1017 if (
config_->getHeartbeatDelay() > 0) {
1026 const std::string& server_name,
1027 const unsigned int max_period,
1033 (HttpRequest::Method::HTTP_POST,
"/", HttpVersion::HTTP_11());
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);
1062 .arg(remote_config->getLogLabel())
1063 .arg(error_message);
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()) {
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());
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);
1128 .arg(remote_config->getLogLabel())
1129 .arg(error_message);
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()) {
1152 if (post_request_action) {
1153 post_request_action(error_message.empty(),
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;
1182 dhcp_disable_timeout,
LeasePtr(), null_action);
1187 const std::string& server_name,
1188 const unsigned int max_period,
1191 const bool dhcp_disabled) {
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) {
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());
1231 boost::dynamic_pointer_cast<Lease4>(last_lease),
config_->getSyncPageLimit()));
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) {
1261 std::string error_message;
1264 if (ec || !error_str.empty()) {
1265 error_message = (ec ? ec.message() : error_str);
1267 .arg(partner_config->getLogLabel())
1268 .arg(error_message);
1276 if (args && (args->getType() != Element::map)) {
1278 "arguments in the received response must be a map");
1282 if (!leases || (leases->getType() != Element::list)) {
1284 "server response does not contain leases argument or this" 1285 " argument is not a list");
1289 const auto& leases_element = leases->listValue();
1292 .arg(leases_element.size())
1295 for (
auto l = leases_element.begin(); l != leases_element.end(); ++l) {
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);
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);
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) {
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()) {
1377 }
else if (last_lease) {
1381 post_sync_action, dhcp_disabled);
1386 if (post_sync_action) {
1387 post_sync_action(error_message.empty(),
1396 const unsigned int max_period) {
1397 std::string answer_message;
1398 int sync_status =
synchronize(answer_message, server_name, max_period);
1404 const unsigned int max_period) {
1409 [&](
const bool success,
const std::string& error_message,
1410 const bool dhcp_disabled) {
1415 status_message = error_message;
1421 if (dhcp_disabled) {
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()) {
1464 .arg(status_message);
1471 status_message =
"Lease database synchronization complete.";
1488 }
catch (
const std::exception& ex) {
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;
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.
static const int NOP_EVT
Signifies that no event has occurred.
const int HA_TERMINATED_ST
HA service terminated state.
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
virtual void defineStates()
Defines states of the HA service.
void serveDefaultScopes()
Instructs the HA service to serve default scopes.
const int HA_HOT_STANDBY_ST
Hot standby state.
void defineEvent(unsigned int value, const std::string &label)
Adds an event value and associated label to the set of events.
const int DBGLVL_TRACE_BASIC
Trace basic operations.
void readyStateHandler()
Handler for "ready" state.
void serveNoScopes()
Disables all scopes.
void run()
Start the underlying event loop.
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
ConstElementPtr createAnswer(const int status_code, const std::string &text, const ConstElementPtr &arg)
const StatePtr getState(unsigned int value)
Fetches the state referred to by value.
const int CONTROL_RESULT_SUCCESS
Status code indicating a successful operation.
data::ConstElementPtr processScopes(const std::vector< std::string > &scopes)
Processes ha-scopes command and returns a response.
void scheduleHeartbeat()
Schedules asynchronous heartbeat to a peer if it is not scheduled.
std::map< std::string, PeerConfigPtr > PeerConfigMap
Map of the servers' configurations.
void asyncEnableDHCPService(http::HttpClient &http_client, const std::string &server_name, PostRequestCallback post_request_action)
Schedules asynchronous "dhcp-enable" command to the specified server.
boost::shared_ptr< HttpResponseJson > HttpResponseJsonPtr
Pointer to the HttpResponseJson object.
bool unpause()
Unpauses the HA state machine with logging.
const int HA_PARTNER_DOWN_ST
Partner down state.
void asyncSyncLeases()
Asynchronously reads leases from a peer and updates local lease database.
data::ConstElementPtr processHeartbeat()
Processes ha-heartbeat command and returns a response.
QueryFilter query_filter_
Selects queries to be processed/dropped.
An abstract API for lease database.
http::HttpClient client_
HTTP client instance used to send lease updates.
CommunicationStatePtr communication_state_
Holds communication state with a peer.
virtual void verifyEvents()
Verifies events used by the HA service.
HTTP request/response timeout value.
static const int HA_SYNCING_SUCCEEDED_EVT
Lease database synchroniation succeeded.
data::ConstElementPtr processContinue()
Processes ha-continue command and returns a response.
const int HA_LOAD_BALANCING_ST
Load balancing state.
HAServerType server_type_
DHCP server type.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
boost::shared_ptr< IOService > IOServicePtr
Defines a smart pointer to an IOService instance.
const int CONTROL_RESULT_ERROR
Status code indicating a general failure.
boost::shared_ptr< Element > ElementPtr
static data::ConstElementPtr createLease6GetPage(const dhcp::Lease6Ptr &lease6, const uint32_t limit)
Creates lease6-get-page command.
void waitingStateHandler()
Handler for "waiting" state.
HAServerType
Lists possible server types for which HA service is created.
const int HA_BACKUP_ST
Backup state.
bool shouldTerminate() const
Indicates if the server should transition to the terminated state as a result of high clock skew.
boost::shared_ptr< Lease > LeasePtr
Pointer to the lease object.
void serveDefaultScopes()
Serve default scopes for the given HA mode.
dhcp::NetworkStatePtr network_state_
Pointer to the state of the DHCP service (enabled/disabled).
The IOService class is a wrapper for the ASIO io_service class.
virtual void runModel(unsigned int event)
Processes events through the state model.
const int CONTROL_RESULT_EMPTY
Status code indicating that the specified command was completed correctly, but failed to produce any ...
asiolink::IOServicePtr io_service_
Pointer to the IO service object shared between this hooks library and the DHCP server.
const int HA_WAITING_ST
Server waiting state, i.e. waiting for another server to be ready.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
void stop()
Stops the stopwatch.
Holds communication state between DHCPv4 servers.
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
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.
boost::shared_ptr< PostHttpRequestJson > PostHttpRequestJsonPtr
Pointer to PostHttpRequestJson.
const int HA_READY_ST
Server ready state, i.e. synchronized database, can enable DHCP service.
static const int HA_HEARTBEAT_COMPLETE_EVT
Finished heartbeat commannd.
static const int HA_LEASE_UPDATES_COMPLETE_EVT
Finished lease updates commands.
boost::shared_ptr< Lease4 > Lease4Ptr
Pointer to a Lease4 structure.
static data::ConstElementPtr createLease4Update(const dhcp::Lease4 &lease4)
Creates lease4-update command.
void partnerDownStateHandler()
Handler for "partner-down" state.
void syncingStateHandler()
Handler for "syncing" state.
boost::shared_ptr< HttpResponse > HttpResponsePtr
Pointer to the HttpResponse object.
Utility class to measure code execution times.
void asyncSendHeartbeat()
Starts asynchronous heartbeat to a peer.
void serveScopes(const std::vector< std::string > &scopes)
Enables selected scopes.
boost::shared_ptr< Lease4Collection > Lease4CollectionPtr
A shared pointer to the collection of IPv4 leases.
A generic exception that is thrown when an unexpected error condition occurs.
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.
bool doOnEntry()
Checks if on entry flag is true.
std::string stateToString(int state)
Returns state name.
std::string rfc1123Format() const
Returns time value formatted as specified in RFC 1123.
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
void stop()
Stop the underlying event loop.
boost::shared_ptr< const Element > ConstElementPtr
std::string logFormatLastDuration() const
Returns the last measured duration in the format directly usable in log messages.
unsigned int getNextEvent() const
Fetches the model's next event.
bool inScope(dhcp::Pkt4Ptr &query4)
Checks if the DHCPv4 query should be processed by this server.
void terminatedStateHandler()
Handler for "terminated" state.
void serveFailoverScopes()
Enable scopes required in failover case.
void localDisableDHCPService()
Disables local DHCP service.
unsigned int getCurrState() const
Fetches the model's current state.
void adjustNetworkState()
Enables or disables network state depending on the served scopes.
This class parses and generates time values used in HTTP.
data::ConstElementPtr verifyAsyncResponse(const http::HttpResponsePtr &response)
Checks if the response is valid or contains an error.
static data::ConstElementPtr createLease4Delete(const dhcp::Lease4 &lease4)
Creates lease4-del command.
void startModel(const int start_state)
Begins execution of the model.
void unpauseModel()
Unpauses state model.
void asyncSyncLeasesInternal(http::HttpClient &http_client, const std::string &server_name, const unsigned int max_period, const dhcp::LeasePtr &last_lease, PostSyncCallback post_sync_action, const bool dhcp_disabled)
Implements fetching one page of leases during synchronization.
std::function< void(const bool, const std::string &, const bool)> PostSyncCallback
Callback invoked when lease database synchronization is complete.
A standard control channel exception that is thrown if a function is there is a problem with one of t...
ConstElementPtr parseAnswer(int &rcode, const ConstElementPtr &msg)
Defines the logger used by the top-level component of kea-dhcp-ddns.
bool shouldPartnerDown() const
Indicates if the server should transition to the partner down state.
HAConfigPtr config_
Pointer to the HA hooks library configuration.
void startHeartbeat()
Unconditionally starts one heartbeat to a peer.
void transition(unsigned int state, unsigned int event)
Sets up the model to transition into given state with a given event.
void normalStateHandler()
Handler for the "hot-standby" and "load-balancing" states.
This file contains several functions and constants that are used for handling commands and responses ...
static std::string HAModeToString(const HAMode &ha_mode)
Returns HA mode name.
void localEnableDHCPService()
Enables local DHCP service.
static data::ConstElementPtr createLease6Delete(const dhcp::Lease6 &lease6)
Creates lease6-del command.
virtual void defineEvents()
Defines events used by the HA service.
std::string getStateLabel(const int state) const
Fetches the label associated with an state value.
std::function< void(const bool, const std::string &)> PostRequestCallback
Callback invoked when request was sent and a response received or an error occurred.
boost::shared_ptr< Lease6Collection > Lease6CollectionPtr
A shared pointer to the collection of IPv6 leases.
boost::shared_ptr< NetworkState > NetworkStatePtr
Pointer to the NetworkState object.
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
isc::log::Logger ha_logger("ha-hooks")
bool shouldSendLeaseUpdates(const HAConfig::PeerConfigPtr &peer_config) const
Checks if the lease updates should be sent as result of leases allocation or release.
void backupStateHandler()
Handler for the "backup" state.
static const int HA_SYNCING_FAILED_EVT
Lease database synchronization failed.
bool inScope(const dhcp::Pkt4Ptr &query4, std::string &scope_class) const
Checks if this server should process the DHCPv4 query.
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.
boost::shared_ptr< ParkingLotHandle > ParkingLotHandlePtr
Pointer to the parking lot handle.
static std::string roleToString(const HAConfig::PeerConfig::Role &role)
Returns role name.
static data::ConstElementPtr createHeartbeat(const HAServerType &server_type)
Creates ha-heartbeat command for DHCP server.
std::string ClientClass
Defines a single class name.
const EventPtr & getEvent(unsigned int value)
Fetches the event referred to by value.
static data::ConstElementPtr createLease6Update(const dhcp::Lease6 &lease6)
Creates lease6-update command.
void conditionalLogPausedState() const
Logs if the server is paused in the current state.
data::ConstElementPtr processSynchronize(const std::string &server_name, const unsigned int max_period)
Processes ha-sync command and returns a response.
bool isModelPaused() const
Returns whether or not the model is paused.
const int HA_SYNCING_ST
Synchronizing database state.
boost::shared_ptr< Lease6 > Lease6Ptr
Pointer to a Lease6 structure.
static data::ConstElementPtr createDHCPEnable(const HAServerType &server_type)
Creates dhcp-enable command for DHCP server.
void verboseTransition(const unsigned state)
Transitions to a desired state and logs it.
boost::shared_ptr< HAConfig > HAConfigPtr
Pointer to the High Availability configuration structure.
std::map< boost::shared_ptr< dhcp::Pkt >, int > pending_requests_
Map holding a number of scheduled requests for a given packet.
static data::ConstElementPtr createDHCPDisable(const unsigned int max_period, const HAServerType &server_type)
Creates dhcp-disable command for DHCP server.
Holds communication state between DHCPv6 servers.
const int HA_UNAVAILABLE_ST
Special state indicating that this server is unable to communicate with the partner.
void postNextEvent(unsigned int event)
Sets the next event to the given event value.
int synchronize(std::string &status_message, const std::string &server_name, const unsigned int max_period)
Synchronizes lease database with a partner.
boost::shared_ptr< PeerConfig > PeerConfigPtr
Pointer to the server's configuration.
static data::ConstElementPtr createLease4GetPage(const dhcp::Lease4Ptr &lease4, const uint32_t limit)
Creates lease4-get-page command.
void asyncDisableDHCPService(http::HttpClient &http_client, const std::string &server_name, const unsigned int max_period, PostRequestCallback post_request_action)
Schedules asynchronous "dhcp-disable" command to the specified server.