mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-31 14:05:33 +00:00
[#1024] Implemented ha-maintenance-start command
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2018-2020 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@@ -175,6 +175,13 @@ CommandCreator::createLease6GetPage(const Lease6Ptr& last_lease6,
|
||||
return (command);
|
||||
}
|
||||
|
||||
ConstElementPtr
|
||||
CommandCreator::createMaintenanceNotify(const HAServerType& server_type) {
|
||||
auto command = config::createCommand("ha-maintenance-notify");
|
||||
insertService(command, server_type);
|
||||
return (command);
|
||||
}
|
||||
|
||||
void
|
||||
CommandCreator::insertLeaseExpireTime(ElementPtr& lease) {
|
||||
if ((lease->getType() != Element::map) ||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2018-2020 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@@ -132,6 +132,12 @@ public:
|
||||
createLease6GetPage(const dhcp::Lease6Ptr& lease6,
|
||||
const uint32_t limit);
|
||||
|
||||
/// @brief Creates ha-maintenance-notify command.
|
||||
///
|
||||
/// @return Pointer to the JSON representation of the command.
|
||||
static data::ConstElementPtr
|
||||
createMaintenanceNotify(const HAServerType& server_typ);
|
||||
|
||||
private:
|
||||
|
||||
/// @brief Replaces "cltt" with "expire" value within the lease.
|
||||
|
@@ -221,6 +221,18 @@ int maintenance_notify_command(CalloutHandle& handle) {
|
||||
return (0);
|
||||
}
|
||||
|
||||
/// @brief ha-maintenance-start command handler implementation.
|
||||
int maintenance_start_command(CalloutHandle& handle) {
|
||||
try {
|
||||
impl->maintenanceStartHandler(handle);
|
||||
|
||||
} catch (const std::exception& ex) {
|
||||
LOG_ERROR(ha_logger, HA_MAINTENANCE_START_HANDLER_FAILED)
|
||||
.arg(ex.what());
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/// @brief This function is called when the library is loaded.
|
||||
///
|
||||
@@ -257,6 +269,7 @@ int load(LibraryHandle& handle) {
|
||||
handle.registerCommandCallout("ha-scopes", scopes_command);
|
||||
handle.registerCommandCallout("ha-continue", continue_command);
|
||||
handle.registerCommandCallout("ha-maintenance-notify", maintenance_notify_command);
|
||||
handle.registerCommandCallout("ha-maintenance-start", maintenance_start_command);
|
||||
|
||||
} catch (const std::exception& ex) {
|
||||
LOG_ERROR(ha_logger, HA_CONFIGURATION_FAILED)
|
||||
|
@@ -419,5 +419,11 @@ HAImpl::maintenanceNotifyHandler(hooks::CalloutHandle& callout_handle) {
|
||||
callout_handle.setArgument("response", response);
|
||||
}
|
||||
|
||||
void
|
||||
HAImpl::maintenanceStartHandler(hooks::CalloutHandle& callout_handle) {
|
||||
ConstElementPtr response = service_->processMaintenanceStart();
|
||||
callout_handle.setArgument("response", response);
|
||||
}
|
||||
|
||||
} // end of namespace isc::ha
|
||||
} // end of namespace isc
|
||||
|
@@ -149,6 +149,11 @@ public:
|
||||
/// @param callout_handle Callout handle provided to the callout.
|
||||
void maintenanceNotifyHandler(hooks::CalloutHandle& callout_handle);
|
||||
|
||||
/// @brief Implements handler for the ha-maintenance-start command.
|
||||
///
|
||||
/// @param callout_handle Callout handle provided to the callout.
|
||||
void maintenanceStartHandler(hooks::CalloutHandle& callout_handle);
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief Holds parsed configuration.
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// File created from ../../../../src/hooks/dhcp/high_availability/ha_messages.mes on Tue Jan 14 2020 12:44
|
||||
// File created from ../../../../src/hooks/dhcp/high_availability/ha_messages.mes on Tue Jan 14 2020 18:59
|
||||
|
||||
#include <cstddef>
|
||||
#include <log/message_types.h>
|
||||
@@ -58,7 +58,13 @@ extern const isc::log::MessageID HA_LOAD_BALANCING_DUID_MISSING = "HA_LOAD_BALAN
|
||||
extern const isc::log::MessageID HA_LOAD_BALANCING_IDENTIFIER_MISSING = "HA_LOAD_BALANCING_IDENTIFIER_MISSING";
|
||||
extern const isc::log::MessageID HA_LOCAL_DHCP_DISABLE = "HA_LOCAL_DHCP_DISABLE";
|
||||
extern const isc::log::MessageID HA_LOCAL_DHCP_ENABLE = "HA_LOCAL_DHCP_ENABLE";
|
||||
extern const isc::log::MessageID HA_MAINTENANCE_NOTIFY_COMMUNICATIONS_FAILED = "HA_MAINTENANCE_NOTIFY_COMMUNICATIONS_FAILED";
|
||||
extern const isc::log::MessageID HA_MAINTENANCE_NOTIFY_FAILED = "HA_MAINTENANCE_NOTIFY_FAILED";
|
||||
extern const isc::log::MessageID HA_MAINTENANCE_NOTIFY_HANDLER_FAILED = "HA_MAINTENANCE_NOTIFY_HANDLER_FAILED";
|
||||
extern const isc::log::MessageID HA_MAINTENANCE_SHUTDOWN_SAFE = "HA_MAINTENANCE_SHUTDOWN_SAFE";
|
||||
extern const isc::log::MessageID HA_MAINTENANCE_STARTED = "HA_MAINTENANCE_STARTED";
|
||||
extern const isc::log::MessageID HA_MAINTENANCE_STARTED_IN_PARTNER_DOWN = "HA_MAINTENANCE_STARTED_IN_PARTNER_DOWN";
|
||||
extern const isc::log::MessageID HA_MAINTENANCE_START_HANDLER_FAILED = "HA_MAINTENANCE_START_HANDLER_FAILED";
|
||||
extern const isc::log::MessageID HA_MISSING_CONFIGURATION = "HA_MISSING_CONFIGURATION";
|
||||
extern const isc::log::MessageID HA_SCOPES_HANDLER_FAILED = "HA_SCOPES_HANDLER_FAILED";
|
||||
extern const isc::log::MessageID HA_SERVICE_STARTED = "HA_SERVICE_STARTED";
|
||||
@@ -128,7 +134,13 @@ const char* values[] = {
|
||||
"HA_LOAD_BALANCING_IDENTIFIER_MISSING", "load balancing failed for the DHCPv4 message (transaction id: %1) because HW address and client identifier are missing",
|
||||
"HA_LOCAL_DHCP_DISABLE", "local DHCP service is disabled while the %1 is in the %2 state",
|
||||
"HA_LOCAL_DHCP_ENABLE", "local DHCP service is enabled while the %1 is in the %2 state",
|
||||
"HA_MAINTENANCE_NOTIFY_COMMUNICATIONS_FAILED", "failed to send ha-maintenance-notify to %1: %2",
|
||||
"HA_MAINTENANCE_NOTIFY_FAILED", "error returned while processing ha-maintenance-notify by %1: %2",
|
||||
"HA_MAINTENANCE_NOTIFY_HANDLER_FAILED", "ha-maintenance-notify command failed: %1",
|
||||
"HA_MAINTENANCE_SHUTDOWN_SAFE", "the server can now be shutdown for maintenance as the partner has taken over the DHCP traffic",
|
||||
"HA_MAINTENANCE_STARTED", "the server is now in the partner-maintained mode and the partner is in the maintained mode",
|
||||
"HA_MAINTENANCE_STARTED_IN_PARTNER_DOWN", "the server is now in the partner-down mode as a result of requested maintenance",
|
||||
"HA_MAINTENANCE_START_HANDLER_FAILED", "ha-maintenance-start command failed: %1",
|
||||
"HA_MISSING_CONFIGURATION", "high-availability parameter not specified for High Availability hooks library",
|
||||
"HA_SCOPES_HANDLER_FAILED", "ha-scopes command failed: %1",
|
||||
"HA_SERVICE_STARTED", "started high availability service in %1 mode as %2 server",
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// File created from ../../../../src/hooks/dhcp/high_availability/ha_messages.mes on Tue Jan 14 2020 12:44
|
||||
// File created from ../../../../src/hooks/dhcp/high_availability/ha_messages.mes on Tue Jan 14 2020 18:59
|
||||
|
||||
#ifndef HA_MESSAGES_H
|
||||
#define HA_MESSAGES_H
|
||||
@@ -59,7 +59,13 @@ extern const isc::log::MessageID HA_LOAD_BALANCING_DUID_MISSING;
|
||||
extern const isc::log::MessageID HA_LOAD_BALANCING_IDENTIFIER_MISSING;
|
||||
extern const isc::log::MessageID HA_LOCAL_DHCP_DISABLE;
|
||||
extern const isc::log::MessageID HA_LOCAL_DHCP_ENABLE;
|
||||
extern const isc::log::MessageID HA_MAINTENANCE_NOTIFY_COMMUNICATIONS_FAILED;
|
||||
extern const isc::log::MessageID HA_MAINTENANCE_NOTIFY_FAILED;
|
||||
extern const isc::log::MessageID HA_MAINTENANCE_NOTIFY_HANDLER_FAILED;
|
||||
extern const isc::log::MessageID HA_MAINTENANCE_SHUTDOWN_SAFE;
|
||||
extern const isc::log::MessageID HA_MAINTENANCE_STARTED;
|
||||
extern const isc::log::MessageID HA_MAINTENANCE_STARTED_IN_PARTNER_DOWN;
|
||||
extern const isc::log::MessageID HA_MAINTENANCE_START_HANDLER_FAILED;
|
||||
extern const isc::log::MessageID HA_MISSING_CONFIGURATION;
|
||||
extern const isc::log::MessageID HA_SCOPES_HANDLER_FAILED;
|
||||
extern const isc::log::MessageID HA_SERVICE_STARTED;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2017-2019 Internet Systems Consortium, Inc. ("ISC")
|
||||
# Copyright (C) 2017-2020 Internet Systems Consortium, Inc. ("ISC")
|
||||
|
||||
$NAMESPACE isc::ha
|
||||
|
||||
@@ -302,11 +302,56 @@ is enabled because the server remains in a state in which it should
|
||||
respond to the DHCP clients. The first argument specifies server name.
|
||||
The second argument specifies server's state.
|
||||
|
||||
% HA_MAINTENANCE_NOTIFY_COMMUNICATIONS_FAILED failed to send ha-maintenance-notify to %1: %2
|
||||
This warning message indicates that there was a problem in communication with a
|
||||
HA peer while sending the ha-maintenance-notify command. The first argument provides the
|
||||
remote server's name. The second argument provides a reason for failure.
|
||||
|
||||
% HA_MAINTENANCE_NOTIFY_FAILED error returned while processing ha-maintenance-notify by %1: %2
|
||||
This warning message indicates that a peer returned an error status code
|
||||
in response to a ha-maintenance-notify command. The first argument provides the
|
||||
remote server's name. The second argument provides a reason for failure.
|
||||
|
||||
% HA_MAINTENANCE_NOTIFY_HANDLER_FAILED ha-maintenance-notify command failed: %1
|
||||
This error message is issued to indicate that the ha-maintenance-notify command
|
||||
handler failed while processing the command. The argument provides the reason for
|
||||
failure.
|
||||
|
||||
% HA_MAINTENANCE_STARTED_IN_PARTNER_DOWN the server is now in the partner-down mode as a result of requested maintenance
|
||||
This informational message is displayed when the server receiving the
|
||||
ha-maintenance-start command transitions to the partner-down state
|
||||
because it was unable to communicate with the partner while receiving
|
||||
the command. It is assumed that in such situation the partner is
|
||||
already offline for the maintenance. Note that in this case the
|
||||
normal failover procedure does not take place. The server does not wait
|
||||
for a heartbeat to fail several times, nor it monitors the DHCP traffic
|
||||
for not responded queries. In the maintenance case the server transitions
|
||||
to the partner-down state when it first encounters a communication
|
||||
problem with the partner.
|
||||
|
||||
% HA_MAINTENANCE_START_HANDLER_FAILED ha-maintenance-start command failed: %1
|
||||
This error message is issued to indicate that the ha-maintenance-start command
|
||||
handler failed while processing the command. The argument provides the reason for
|
||||
failure.
|
||||
|
||||
% HA_MAINTENANCE_STARTED the server is now in the partner-maintained mode and the partner is in the maintained mode
|
||||
This informational message is displayed when the server receiving the
|
||||
ha-maintenance-start command transitions to the partner-maintained
|
||||
state. The server does it after sending the ha-maintenance-notify to
|
||||
its partner to put the partner in the maintained state. From now on,
|
||||
the server in the partner-maintained state will be responding to all
|
||||
queries and the partner will respond to no queries. The partner may be
|
||||
safely shut down for maintenance in which case this server will
|
||||
automatically transition from the partner-maintained state to the
|
||||
partner-down state.
|
||||
|
||||
% HA_MAINTENANCE_SHUTDOWN_SAFE the server can now be shutdown for maintenance as the partner has taken over the DHCP traffic
|
||||
This informational message is displayed after the server transitions to the
|
||||
maintained state. This server no longer responds to any DHCP queries and its
|
||||
partner being in the partner-maintained has taken over the DHCP traffic.
|
||||
When the server being in the maintained state is shut down, the partner
|
||||
will move to the partner-down imediatelly.
|
||||
|
||||
% HA_MISSING_CONFIGURATION high-availability parameter not specified for High Availability hooks library
|
||||
This error message is issued to indicate that the configuration for the
|
||||
High Availability hooks library hasn't been specified. The 'high-availability'
|
||||
|
@@ -42,6 +42,8 @@ const int HAService::HA_HEARTBEAT_COMPLETE_EVT;
|
||||
const int HAService::HA_LEASE_UPDATES_COMPLETE_EVT;
|
||||
const int HAService::HA_SYNCING_FAILED_EVT;
|
||||
const int HAService::HA_SYNCING_SUCCEEDED_EVT;
|
||||
const int HAService::HA_MAINTENANCE_NOTIFY_EVT;
|
||||
const int HAService::HA_MAINTENANCE_START_EVT;
|
||||
|
||||
HAService::HAService(const IOServicePtr& io_service, const NetworkStatePtr& network_state,
|
||||
const HAConfigPtr& config, const HAServerType& server_type)
|
||||
@@ -72,6 +74,7 @@ HAService::defineEvents() {
|
||||
defineEvent(HA_SYNCING_FAILED_EVT, "HA_SYNCING_FAILED_EVT");
|
||||
defineEvent(HA_SYNCING_SUCCEEDED_EVT, "HA_SYNCING_SUCCEEDED_EVT");
|
||||
defineEvent(HA_MAINTENANCE_NOTIFY_EVT, "HA_MAINTENANCE_NOTIFY_EVT");
|
||||
defineEvent(HA_MAINTENANCE_START_EVT, "HA_MAINTENANCE_START_EVT");
|
||||
}
|
||||
|
||||
void
|
||||
@@ -83,6 +86,7 @@ HAService::verifyEvents() {
|
||||
getEvent(HA_SYNCING_FAILED_EVT);
|
||||
getEvent(HA_SYNCING_SUCCEEDED_EVT);
|
||||
getEvent(HA_MAINTENANCE_NOTIFY_EVT);
|
||||
getEvent(HA_MAINTENANCE_START_EVT);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -216,6 +220,8 @@ HAService::maintainedStateHandler() {
|
||||
|
||||
// Log if the state machine is paused.
|
||||
conditionalLogPausedState();
|
||||
|
||||
LOG_INFO(ha_logger, HA_MAINTENANCE_SHUTDOWN_SAFE);
|
||||
}
|
||||
|
||||
scheduleHeartbeat();
|
||||
@@ -232,11 +238,16 @@ HAService::partnerDownStateHandler() {
|
||||
// serving scopes appropriate for the new state. We don't do it if
|
||||
// we remain in this state.
|
||||
if (doOnEntry()) {
|
||||
|
||||
bool maintenance = (getLastEvent() == HA_MAINTENANCE_START_EVT);
|
||||
|
||||
// It may be administratively disabled to handle partner's scope
|
||||
// in case of failure. If this is the case we'll just handle our
|
||||
// default scope (or no scope at all). The user will need to
|
||||
// manually enable this server to handle partner's scope.
|
||||
if (config_->getThisServerConfig()->isAutoFailover()) {
|
||||
// If we're in the maintenance mode we serve all scopes because
|
||||
// it is not a failover situation.
|
||||
if (maintenance || config_->getThisServerConfig()->isAutoFailover()) {
|
||||
query_filter_.serveFailoverScopes();
|
||||
} else {
|
||||
query_filter_.serveDefaultScopes();
|
||||
@@ -245,6 +256,12 @@ HAService::partnerDownStateHandler() {
|
||||
|
||||
// Log if the state machine is paused.
|
||||
conditionalLogPausedState();
|
||||
|
||||
if (maintenance) {
|
||||
// If we ended up in the partner-down state as a result of
|
||||
// receiving the ha-maintenance-start command let's log it.
|
||||
LOG_INFO(ha_logger, HA_MAINTENANCE_STARTED_IN_PARTNER_DOWN);
|
||||
}
|
||||
}
|
||||
|
||||
scheduleHeartbeat();
|
||||
@@ -289,19 +306,14 @@ HAService::partnerMaintainedStateHandler() {
|
||||
// serving scopes appropriate for the new state. We don't do it if
|
||||
// we remain in this state.
|
||||
if (doOnEntry()) {
|
||||
// It may be administratively disabled to handle partner's scope
|
||||
// in case of failure. If this is the case we'll just handle our
|
||||
// default scope (or no scope at all). The user will need to
|
||||
// manually enable this server to handle partner's scope.
|
||||
if (config_->getThisServerConfig()->isAutoFailover()) {
|
||||
query_filter_.serveFailoverScopes();
|
||||
} else {
|
||||
query_filter_.serveDefaultScopes();
|
||||
}
|
||||
query_filter_.serveFailoverScopes();
|
||||
|
||||
adjustNetworkState();
|
||||
|
||||
// Log if the state machine is paused.
|
||||
conditionalLogPausedState();
|
||||
|
||||
LOG_INFO(ha_logger, HA_MAINTENANCE_STARTED);
|
||||
}
|
||||
|
||||
scheduleHeartbeat();
|
||||
@@ -1754,7 +1766,7 @@ HAService::processScopes(const std::vector<std::string>& scopes) {
|
||||
return (createAnswer(CONTROL_RESULT_SUCCESS, "New HA scopes configured."));
|
||||
}
|
||||
|
||||
data::ConstElementPtr
|
||||
ConstElementPtr
|
||||
HAService::processContinue() {
|
||||
if (unpause()) {
|
||||
return (createAnswer(CONTROL_RESULT_SUCCESS, "HA state machine continues."));
|
||||
@@ -1762,7 +1774,7 @@ HAService::processContinue() {
|
||||
return (createAnswer(CONTROL_RESULT_SUCCESS, "HA state machine is not paused."));
|
||||
}
|
||||
|
||||
data::ConstElementPtr
|
||||
ConstElementPtr
|
||||
HAService::processMaintenanceNotify() {
|
||||
switch (getCurrState()) {
|
||||
case HA_BACKUP_ST:
|
||||
@@ -1778,6 +1790,127 @@ HAService::processMaintenanceNotify() {
|
||||
return (createAnswer(CONTROL_RESULT_SUCCESS, "Server is in maintained state."));
|
||||
}
|
||||
|
||||
ConstElementPtr
|
||||
HAService::processMaintenanceStart() {
|
||||
switch (getCurrState()) {
|
||||
case HA_BACKUP_ST:
|
||||
case HA_MAINTAINED_ST:
|
||||
case HA_PARTNER_MAINTAINED_ST:
|
||||
case HA_TERMINATED_ST:
|
||||
return (createAnswer(CONTROL_RESULT_ERROR, "Unable to transition the server from"
|
||||
" the " + stateToString(getCurrState()) + " to"
|
||||
" partner-maintained state."));
|
||||
default:
|
||||
;
|
||||
}
|
||||
|
||||
HAConfig::PeerConfigPtr remote_config = config_->getFailoverPeerConfig();
|
||||
|
||||
// Create HTTP/1.1 request including our command.
|
||||
PostHttpRequestJsonPtr request = boost::make_shared<PostHttpRequestJson>
|
||||
(HttpRequest::Method::HTTP_POST, "/", HttpVersion::HTTP_11(),
|
||||
HostHttpHeader(remote_config->getUrl().getHostname()));
|
||||
request->setBodyAsJson(CommandCreator::createMaintenanceNotify(server_type_));
|
||||
request->finalize();
|
||||
|
||||
// Response object should also be created because the HTTP client needs
|
||||
// to know the type of the expected response.
|
||||
HttpResponseJsonPtr response = boost::make_shared<HttpResponseJson>();
|
||||
|
||||
IOService io_service;
|
||||
HttpClient client(io_service);
|
||||
|
||||
boost::system::error_code captured_ec;
|
||||
std::string captured_error_message;
|
||||
|
||||
// Schedule asynchronous HTTP request.
|
||||
client.asyncSendRequest(remote_config->getUrl(), request, response,
|
||||
[this, remote_config, &io_service, &captured_ec, &captured_error_message]
|
||||
(const boost::system::error_code& ec,
|
||||
const HttpResponsePtr& response,
|
||||
const std::string& error_str) {
|
||||
|
||||
io_service.stop();
|
||||
|
||||
// There are three possible groups of errors. One is the IO error
|
||||
// causing issues in communication with the peer. Another one is
|
||||
// an HTTP parsing error. The last type of error is when non-success
|
||||
// error code is returned in the response carried in the HTTP message
|
||||
// or if the JSON response is otherwise broken.
|
||||
|
||||
std::string error_message;
|
||||
|
||||
// Handle first two groups of errors.
|
||||
if (ec || !error_str.empty()) {
|
||||
error_message = (ec ? ec.message() : error_str);
|
||||
LOG_ERROR(ha_logger, HA_MAINTENANCE_NOTIFY_COMMUNICATIONS_FAILED)
|
||||
.arg(remote_config->getLogLabel())
|
||||
.arg(error_message);
|
||||
|
||||
} else {
|
||||
|
||||
// Handle third group of errors.
|
||||
try {
|
||||
static_cast<void>(verifyAsyncResponse(response));
|
||||
|
||||
} catch (const std::exception& ex) {
|
||||
error_message = ex.what();
|
||||
LOG_ERROR(ha_logger, HA_MAINTENANCE_NOTIFY_FAILED)
|
||||
.arg(remote_config->getLogLabel())
|
||||
.arg(error_message);
|
||||
}
|
||||
}
|
||||
|
||||
// If there was an error communicating with the partner, mark the
|
||||
// partner as unavailable.
|
||||
if (!error_message.empty()) {
|
||||
communication_state_->setPartnerState("unavailable");
|
||||
}
|
||||
|
||||
captured_ec = ec;
|
||||
captured_error_message = error_message;
|
||||
},
|
||||
HttpClient::RequestTimeout(TIMEOUT_DEFAULT_HTTP_CLIENT_REQUEST),
|
||||
boost::bind(&HAService::clientConnectHandler, this, _1, _2),
|
||||
boost::bind(&HAService::clientCloseHandler, this, _1)
|
||||
);
|
||||
|
||||
// Run the IO service until it is stopped by any of the callbacks. This
|
||||
// makes it synchronous.
|
||||
io_service.run();
|
||||
|
||||
// If there was a communication problem with the partner we assume that
|
||||
// the partner is already down while we receive this command.
|
||||
if (captured_ec) {
|
||||
verboseTransition(HA_PARTNER_DOWN_ST);
|
||||
runModel(HA_MAINTENANCE_START_EVT);
|
||||
return (createAnswer(CONTROL_RESULT_SUCCESS,
|
||||
"Server is now in the partner-down state as its"
|
||||
" partner appears to be offline for maintenance."));
|
||||
|
||||
|
||||
} else if (captured_error_message.empty()) {
|
||||
// If the partner responded indicating no error it means that the
|
||||
// partner has been transitioned to the maintained state. In that
|
||||
// case we transition to the partner-maintained state.
|
||||
verboseTransition(HA_PARTNER_MAINTAINED_ST);
|
||||
runModel(HA_MAINTENANCE_START_EVT);
|
||||
|
||||
} else {
|
||||
// Partner server returned an error so this server can't transition to
|
||||
// the partner-maintained mode.
|
||||
return (createAnswer(CONTROL_RESULT_ERROR, "Partner server responded with"
|
||||
" the following error to the ha-maintenance-notify"
|
||||
" commmand: " + captured_error_message + "."));
|
||||
|
||||
}
|
||||
|
||||
return (createAnswer(CONTROL_RESULT_SUCCESS,
|
||||
"Server is now in the partner-maintained state"
|
||||
" and its partner is in the maintained state. The partner"
|
||||
" can be now safely shut down."));
|
||||
}
|
||||
|
||||
ConstElementPtr
|
||||
HAService::verifyAsyncResponse(const HttpResponsePtr& response) {
|
||||
// The response must cast to JSON type.
|
||||
|
@@ -53,6 +53,9 @@ public:
|
||||
/// ha-maintenance-notify command received.
|
||||
static const int HA_MAINTENANCE_NOTIFY_EVT = SM_DERIVED_EVENT_MIN + 5;
|
||||
|
||||
/// ha-maintenance-start command received.
|
||||
static const int HA_MAINTENANCE_START_EVT = SM_DERIVED_EVENT_MIN + 6;
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief Callback invoked when request was sent and a response received
|
||||
@@ -780,6 +783,21 @@ public:
|
||||
/// @return Pointer to the reponse to the ha-maintenance-notify.
|
||||
data::ConstElementPtr processMaintenanceNotify();
|
||||
|
||||
/// @brief Processes ha-maintenance-start command and returns a response.
|
||||
///
|
||||
/// The server receiving this command will try to send the
|
||||
/// ha-maintenance-notify command to the partner to instruct the partner
|
||||
/// to transition to the maintained state. In this state the partner will
|
||||
/// not respond to any DHCP queries. Next, this server will transition to
|
||||
/// the ha-partner-maintained state and therefore will start responding
|
||||
/// to all DHCP queries. If the partner responds to the ha-maintenance-notify
|
||||
/// with an error, this server won't transition to the partner-maintained
|
||||
/// state and signal an error to the caller. If the partner is unavailable,
|
||||
/// this server will directly transition to the partner-down state.
|
||||
///
|
||||
/// @return Pointer to the response to the ha-maintenance-start.
|
||||
data::ConstElementPtr processMaintenanceStart();
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief Checks if the response is valid or contains an error.
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2018-2020 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@@ -392,4 +392,18 @@ TEST(CommandCreatorTest, createLease6GetPageZeroLimit) {
|
||||
EXPECT_THROW(CommandCreator::createLease6GetPage(lease6, 0), BadValue);
|
||||
}
|
||||
|
||||
// This test verifies that the ha-maintenance-notify command is correct
|
||||
// while being sent to the DHCPv4 server.
|
||||
TEST(CommandCreatorTest, createMaintenanceNotify4) {
|
||||
ConstElementPtr command = CommandCreator::createMaintenanceNotify(HAServerType::DHCPv4);
|
||||
ASSERT_NO_FATAL_FAILURE(testCommandBasics(command, "ha-maintenance-notify", "dhcp4"));
|
||||
}
|
||||
|
||||
// This test verifies that the ha-maintenance-notify command is correct
|
||||
// while being sent to the DHCPv6 server.
|
||||
TEST(CommandCreatorTest, createMaintenanceNotify6) {
|
||||
ConstElementPtr command = CommandCreator::createMaintenanceNotify(HAServerType::DHCPv6);
|
||||
ASSERT_NO_FATAL_FAILURE(testCommandBasics(command, "ha-maintenance-notify", "dhcp6"));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -2627,6 +2627,135 @@ TEST_F(HAServiceTest, processMaintenanceNotify) {
|
||||
" partner-maintained to maintained state.");
|
||||
}
|
||||
|
||||
// This test verifies the case when the server receiving the ha-maintenance-start
|
||||
// command successfully transitions to the partner-maintained state and its
|
||||
// partner transitions to the maintained state.
|
||||
TEST_F(HAServiceTest, processMaintenanceStartSuccess) {
|
||||
// Create HA configuration for 3 servers. This server is
|
||||
// server 1.
|
||||
HAConfigPtr config_storage = createValidConfiguration();
|
||||
|
||||
// Start the servers.
|
||||
ASSERT_NO_THROW({
|
||||
listener_->start();
|
||||
listener2_->start();
|
||||
});
|
||||
|
||||
HAService service(io_service_, network_state_, config_storage);
|
||||
|
||||
// The tested function is synchronous, so we need to run server side IO service
|
||||
// in background to not block the main thread.
|
||||
auto thread = runIOServiceInThread();
|
||||
|
||||
// Process ha-maintenance-start command.
|
||||
ConstElementPtr rsp;
|
||||
ASSERT_NO_THROW(rsp = service.processMaintenanceStart());
|
||||
|
||||
// Stop the IO service. This should cause the thread to terminate.
|
||||
io_service_->stop();
|
||||
thread->join();
|
||||
io_service_->get_io_service().reset();
|
||||
io_service_->poll();
|
||||
|
||||
// The partner of our server is online and should have responded with
|
||||
// the success status. Therefore, this server should have transitioned
|
||||
// to the partner-maintained state.
|
||||
ASSERT_TRUE(rsp);
|
||||
checkAnswer(rsp, CONTROL_RESULT_SUCCESS, "Server is now in the partner-maintained state"
|
||||
" and its partner is in the maintained state. The partner can be now safely"
|
||||
" shut down.");
|
||||
|
||||
EXPECT_EQ(HA_PARTNER_MAINTAINED_ST, service.getCurrState());
|
||||
}
|
||||
|
||||
// This test verifies the case that the server transitions to the partner-down
|
||||
// state after receiving the ha-maintenance-start command. This is the case
|
||||
// when the communication with the partner server fails while this command
|
||||
// is received. It is assumed that the partner server is already terminated
|
||||
// for maintenance.
|
||||
TEST_F(HAServiceTest, processMaintenanceStartPartnerDown) {
|
||||
// Create HA configuration for 3 servers. This server is
|
||||
// server 1.
|
||||
HAConfigPtr config_storage = createValidConfiguration();
|
||||
|
||||
// Start the server, but don't start the partner. This simulates
|
||||
// the case that the partner is already down for maintenance.
|
||||
ASSERT_NO_THROW({
|
||||
listener_->start();
|
||||
});
|
||||
|
||||
HAService service(io_service_, network_state_, config_storage);
|
||||
|
||||
// The tested function is synchronous, so we need to run server side IO service
|
||||
// in background to not block the main thread.
|
||||
auto thread = runIOServiceInThread();
|
||||
|
||||
// Process ha-maintenance-start command.
|
||||
ConstElementPtr rsp;
|
||||
ASSERT_NO_THROW(rsp = service.processMaintenanceStart());
|
||||
|
||||
// Stop the IO service. This should cause the thread to terminate.
|
||||
io_service_->stop();
|
||||
thread->join();
|
||||
io_service_->get_io_service().reset();
|
||||
io_service_->poll();
|
||||
|
||||
// The partner of our server is online and should have responded with
|
||||
// the success status. Therefore, this server should have transitioned
|
||||
// to the partner-maintained state.
|
||||
ASSERT_TRUE(rsp);
|
||||
checkAnswer(rsp, CONTROL_RESULT_SUCCESS,
|
||||
"Server is now in the partner-down state as its"
|
||||
" partner appears to be offline for maintenance.");
|
||||
|
||||
EXPECT_EQ(HA_PARTNER_DOWN_ST, service.getCurrState());
|
||||
}
|
||||
|
||||
// This test verifies the case when the server is receiving
|
||||
// ha-maintenance-start command and tries to notify the partner
|
||||
// which returns an error.
|
||||
TEST_F(HAServiceTest, processMaintenanceStartPartnerError) {
|
||||
// Create HA configuration for 3 servers. This server is
|
||||
// server 1.
|
||||
HAConfigPtr config_storage = createValidConfiguration();
|
||||
|
||||
// Simulate an error returned by the partner.
|
||||
factory2_->getResponseCreator()->setControlResult(CONTROL_RESULT_ERROR);
|
||||
|
||||
// Start the servers.
|
||||
ASSERT_NO_THROW({
|
||||
listener_->start();
|
||||
listener2_->start();
|
||||
});
|
||||
|
||||
HAService service(io_service_, network_state_, config_storage);
|
||||
|
||||
// The tested function is synchronous, so we need to run server side IO service
|
||||
// in background to not block the main thread.
|
||||
auto thread = runIOServiceInThread();
|
||||
|
||||
// Process ha-maintenance-start command.
|
||||
ConstElementPtr rsp;
|
||||
ASSERT_NO_THROW(rsp = service.processMaintenanceStart());
|
||||
|
||||
// Stop the IO service. This should cause the thread to terminate.
|
||||
io_service_->stop();
|
||||
thread->join();
|
||||
io_service_->get_io_service().reset();
|
||||
io_service_->poll();
|
||||
|
||||
// The partner of our server is online and should have responded with
|
||||
// the success status. Therefore, this server should have transitioned
|
||||
// to the partner-maintained state.
|
||||
ASSERT_TRUE(rsp);
|
||||
checkAnswer(rsp, CONTROL_RESULT_ERROR, "Partner server responded with"
|
||||
" the following error to the ha-maintenance-notify commmand:"
|
||||
" response returned, error code 1.");
|
||||
|
||||
// The state shouldn't change.
|
||||
EXPECT_EQ(HA_WAITING_ST, service.getCurrState());
|
||||
}
|
||||
|
||||
/// @brief HA partner to the server under test.
|
||||
///
|
||||
/// This is a wrapper class around @c HttpListener which simulates a
|
||||
@@ -3024,8 +3153,9 @@ public:
|
||||
/// state.
|
||||
/// @param dhcp_enabled Indicates whether DHCP service is expected to be enabled
|
||||
/// or disabled in the given state.
|
||||
/// @param event Event to be passed to the tested handler.
|
||||
void expectScopes(const MyState& my_state, const std::vector<std::string>& scopes,
|
||||
const bool dhcp_enabled) {
|
||||
const bool dhcp_enabled, const int event = TestHAService::NOP_EVT) {
|
||||
|
||||
// If expecting no scopes, let's enable some scope to make sure that the
|
||||
// code changes this setting.
|
||||
@@ -3048,6 +3178,7 @@ public:
|
||||
}
|
||||
|
||||
// Transition to the desired state.
|
||||
service_->postNextEvent(event);
|
||||
service_->verboseTransition(my_state.state_);
|
||||
// Run the handler.
|
||||
service_->runModel(TestHAService::NOP_EVT);
|
||||
@@ -4254,10 +4385,15 @@ TEST_F(HAServiceStateMachineTest, scopesServingLoadBalancingNoFailover) {
|
||||
expectScopes(MyState(HA_LOAD_BALANCING_ST), { "server1" }, true);
|
||||
expectScopes(MyState(HA_TERMINATED_ST), { "server1" }, true);
|
||||
|
||||
// PARTNER MAINTAINED & PARTNER DOWN: still serving my own scope
|
||||
// because auto-failover is disabled.
|
||||
// PARTNER DOWN: still serving my own scope because auto-failover is disabled.
|
||||
expectScopes(MyState(HA_PARTNER_DOWN_ST), { "server1" }, true);
|
||||
expectScopes(MyState(HA_PARTNER_MAINTAINED_ST), { "server1" }, true);
|
||||
|
||||
// PARTNER MAINTAINED: always serving all scopes.
|
||||
expectScopes(MyState(HA_PARTNER_MAINTAINED_ST), { "server1", "server2" }, true);
|
||||
|
||||
// Same for the partner-down case during maintenance.
|
||||
expectScopes(MyState(HA_PARTNER_DOWN_ST), { "server1", "server2" }, true,
|
||||
HAService::HA_MAINTENANCE_START_EVT);
|
||||
|
||||
// MAINTAINED, READY & WAITING: serving no scopes.
|
||||
expectScopes(MyState(HA_MAINTAINED_ST), { }, false);
|
||||
@@ -4949,10 +5085,15 @@ TEST_F(HAServiceStateMachineTest, scopesServingHotStandbyStandbyNoFailover) {
|
||||
// TERMINATED: serving no scopes because the primary is active.
|
||||
expectScopes(MyState(HA_TERMINATED_ST), { }, true);
|
||||
|
||||
// PARTNER MAINTAINED & PARTNER DOWN: still serving no scopes because auto-failover is
|
||||
// set to false.
|
||||
// PARTNER DOWN: still serving no scopes because auto-failover is set to false.
|
||||
expectScopes(MyState(HA_PARTNER_DOWN_ST), { }, true);
|
||||
expectScopes(MyState(HA_PARTNER_MAINTAINED_ST), { }, true);
|
||||
|
||||
// PARTNER MAINTAINED: serving partner's scopes.
|
||||
expectScopes(MyState(HA_PARTNER_MAINTAINED_ST), { "server1" }, true);
|
||||
|
||||
// Same for the partner-down case during maintenance.
|
||||
expectScopes(MyState(HA_PARTNER_DOWN_ST), { "server1" }, true,
|
||||
HAService::HA_MAINTENANCE_START_EVT);
|
||||
|
||||
// MAINTAINED, READY & WAITING: serving no scopes.
|
||||
expectScopes(MyState(HA_MAINTAINED_ST), { }, false);
|
||||
|
Reference in New Issue
Block a user