}
ConstElementPtr
-CommandCreator::createHAReset(const HAServerType& server_type) {
- ConstElementPtr command = config::createCommand("ha-reset");
+CommandCreator::createHAReset(const std::string& server_name,
+ const HAServerType& server_type) {
+ auto args = Element::createMap();
+ args->set("server-name", Element::create(server_name));
+ ConstElementPtr command = config::createCommand("ha-reset", args);
insertService(command, server_type);
return (command);
}
ConstElementPtr
-CommandCreator::createHeartbeat(const HAServerType& server_type) {
- ConstElementPtr command = config::createCommand("ha-heartbeat");
+CommandCreator::createHeartbeat(const std::string& server_name,
+ const HAServerType& server_type) {
+ auto args = Element::createMap();
+ args->set("server-name", Element::create(server_name));
+ ConstElementPtr command = config::createCommand("ha-heartbeat", args);
insertService(command, server_type);
return (command);
}
}
ConstElementPtr
-CommandCreator::createSyncCompleteNotify(const HAServerType& server_type) {
- auto command = config::createCommand("ha-sync-complete-notify");
+CommandCreator::createSyncCompleteNotify(const std::string& server_name,
+ const HAServerType& server_type) {
+ auto args = Element::createMap();
+ args->set("server-name", Element::create(server_name));
+ auto command = config::createCommand("ha-sync-complete-notify", args);
insertService(command, server_type);
return (command);
}
/// @brief Creates ha-reset command.
///
+ /// @param server_name name of the server sending the command allowing
+ /// for associating the command with the relationship.
/// @param server_type type of the DHCP server, i.e. v4 or v6.
/// @return Pointer to the JSON representation of the command.
static data::ConstElementPtr
- createHAReset(const HAServerType& server_type);
+ createHAReset(const std::string& server_name,
+ const HAServerType& server_type);
/// @brief Creates ha-heartbeat command for DHCP server.
///
+ /// @param server_name name of the server sending the command allowing
+ /// for associating the command with the relationship.
/// @return Pointer to the JSON representation of the command.
static data::ConstElementPtr
- createHeartbeat(const HAServerType& server_type);
+ createHeartbeat(const std::string& server_name,
+ const HAServerType& server_type);
/// @brief Creates lease4-update command.
///
/// @brief Creates ha-sync-complete-notify command.
///
+ /// @param server_name name of the server sending the command allowing
+ /// for associating the command with the relationship.
/// @param server_type type of the DHCP server, i.e. v4 or v6.
/// @return Pointer to the JSON representation of the command.
static data::ConstElementPtr
- createSyncCompleteNotify(const HAServerType& server_type);
+ createSyncCompleteNotify(const std::string& server_name,
+ const HAServerType& server_type);
/// @brief List of commands used by the High Availability in v4.
static std::unordered_set<std::string> ha_commands4_;
ElementPtr mutable_resp_args =
boost::const_pointer_cast<Element>(resp_args);
- /// @todo Today we support only one HA relationship per Kea server.
- /// In the future there will be more of them. Therefore we enclose
- /// our sole relationship in a list.
+ // Process the status get command for each HA service.
auto ha_relationships = Element::createList();
- auto ha_relationship = Element::createMap();
- ConstElementPtr ha_servers = services_->get()->processStatusGet();
- ha_relationship->set("ha-servers", ha_servers);
- ha_relationship->set("ha-mode", Element::create(HAConfig::HAModeToString(config_->get()->getHAMode())));
- ha_relationships->add(ha_relationship);
- mutable_resp_args->set("high-availability", ha_relationships);
+ for (auto service : services_->getAll()) {
+ auto ha_relationship = Element::createMap();
+ ConstElementPtr ha_servers = service->processStatusGet();
+ ha_relationship->set("ha-servers", ha_servers);
+ ha_relationship->set("ha-mode", Element::create(HAConfig::HAModeToString(config_->get()->getHAMode())));
+ ha_relationships->add(ha_relationship);
+ mutable_resp_args->set("high-availability", ha_relationships);
+ }
}
}
void
HAImpl::heartbeatHandler(CalloutHandle& callout_handle) {
- ConstElementPtr response = services_->get()->processHeartbeat();
+ // Command must always be provided.
+ ConstElementPtr command;
+ callout_handle.getArgument("command", command);
+
+ // Retrieve arguments.
+ ConstElementPtr args;
+ static_cast<void>(parseCommand(args, command));
+
+ HAServicePtr service;
+ try {
+ service = getHAServiceByServerName("ha-heartbeat", args);
+
+ } catch (const std::exception& ex) {
+ // There was an error while parsing command arguments. Return an error status
+ // code to notify the user.
+ ConstElementPtr response = createAnswer(CONTROL_RESULT_ERROR, ex.what());
+ callout_handle.setArgument("response", response);
+ return;
+ }
+
+ // Command parsing was successful, so let's process the command.
+ ConstElementPtr response = service->processHeartbeat();
callout_handle.setArgument("response", response);
}
ConstElementPtr server_name;
unsigned int max_period_value = 0;
+ HAServicePtr service;
try {
// Arguments are required for the ha-sync command.
if (!args) {
max_period_value = static_cast<unsigned int>(max_period->intValue());
}
+ service = getHAServiceByServerName("ha-sync", args);
+
} catch (const std::exception& ex) {
// There was an error while parsing command arguments. Return an error status
// code to notify the user.
}
// Command parsing was successful, so let's process the command.
- ConstElementPtr response = services_->get()->processSynchronize(server_name->stringValue(),
- max_period_value);
+ ConstElementPtr response = service->processSynchronize(server_name->stringValue(),
+ max_period_value);
callout_handle.setArgument("response", response);
}
ConstElementPtr args;
static_cast<void>(parseCommand(args, command));
+ HAServicePtr service;
std::vector<std::string> scopes_vector;
-
try {
// Arguments must be present.
if (!args) {
scopes_vector.push_back(scope->stringValue());
}
+ service = getHAServiceByServerName("ha-sync", args);
+
} catch (const std::exception& ex) {
// There was an error while parsing command arguments. Return an error status
// code to notify the user.
}
// Command parsing was successful, so let's process the command.
- ConstElementPtr response = services_->get()->processScopes(scopes_vector);
+ ConstElementPtr response = service->processScopes(scopes_vector);
callout_handle.setArgument("response", response);
}
void
HAImpl::continueHandler(hooks::CalloutHandle& callout_handle) {
- ConstElementPtr response = services_->get()->processContinue();
+ // Command must always be provided.
+ ConstElementPtr command;
+ callout_handle.getArgument("command", command);
+
+ // Retrieve arguments.
+ ConstElementPtr args;
+ static_cast<void>(parseCommand(args, command));
+
+ HAServicePtr service;
+ try {
+ service = getHAServiceByServerName("ha-continue", args);
+
+ } catch (const std::exception& ex) {
+ // There was an error while parsing command arguments. Return an error status
+ // code to notify the user.
+ ConstElementPtr response = createAnswer(CONTROL_RESULT_ERROR, ex.what());
+ callout_handle.setArgument("response", response);
+ return;
+ }
+ ConstElementPtr response = service->processContinue();
callout_handle.setArgument("response", response);
}
ConstElementPtr command;
callout_handle.getArgument("command", command);
- // Retrieve arguments.
- ConstElementPtr args;
- static_cast<void>(parseCommandWithArgs(args, command));
+ HAServicePtr service;
+ try {
+ // Retrieve arguments.
+ ConstElementPtr args;
+ static_cast<void>(parseCommandWithArgs(args, command));
- ConstElementPtr cancel_op = args->get("cancel");
- if (!cancel_op) {
- isc_throw(BadValue, "'cancel' is mandatory for the 'ha-maintenance-notify' command");
- }
+ ConstElementPtr cancel_op = args->get("cancel");
+ if (!cancel_op) {
+ isc_throw(BadValue, "'cancel' is mandatory for the 'ha-maintenance-notify' command");
+ }
- if (cancel_op->getType() != Element::boolean) {
- isc_throw(BadValue, "'cancel' must be a boolean in the 'ha-maintenance-notify' command");
- }
+ if (cancel_op->getType() != Element::boolean) {
+ isc_throw(BadValue, "'cancel' must be a boolean in the 'ha-maintenance-notify' command");
+ }
- ConstElementPtr response = services_->get()->processMaintenanceNotify(cancel_op->boolValue());
- callout_handle.setArgument("response", response);
+ service = getHAServiceByServerName("ha-maintenance-notify", args);
+
+ ConstElementPtr response = service->processMaintenanceNotify(cancel_op->boolValue());
+ callout_handle.setArgument("response", response);
+
+ } catch (const std::exception& ex) {
+ // There was an error while parsing command arguments. Return an error status
+ // code to notify the user.
+ ConstElementPtr response = createAnswer(CONTROL_RESULT_ERROR, ex.what());
+ callout_handle.setArgument("response", response);
+ }
}
void
HAImpl::maintenanceStartHandler(hooks::CalloutHandle& callout_handle) {
- ConstElementPtr response = services_->get()->processMaintenanceStart();
+ ConstElementPtr response;
+ for (auto service : services_->getAll()) {
+ response = service->processMaintenanceStart();
+ int rcode = CONTROL_RESULT_SUCCESS;
+ static_cast<void>(parseAnswer(rcode, response));
+ if (rcode != CONTROL_RESULT_SUCCESS) {
+ break;
+ }
+
+ }
callout_handle.setArgument("response", response);
}
void
HAImpl::maintenanceCancelHandler(hooks::CalloutHandle& callout_handle) {
- ConstElementPtr response = services_->get()->processMaintenanceCancel();
+ ConstElementPtr response;
+ for (auto service : services_->getAll()) {
+ response = service->processMaintenanceCancel();
+ }
callout_handle.setArgument("response", response);
}
void
HAImpl::haResetHandler(hooks::CalloutHandle& callout_handle) {
+ // Command must always be provided.
+ ConstElementPtr command;
+ callout_handle.getArgument("command", command);
+
+ // Retrieve arguments.
+ ConstElementPtr args;
+ static_cast<void>(parseCommand(args, command));
+
+ HAServicePtr service;
+ try {
+ service = getHAServiceByServerName("ha-reset", args);
+
+ } catch (const std::exception& ex) {
+ // There was an error while parsing command arguments. Return an error status
+ // code to notify the user.
+ ConstElementPtr response = createAnswer(CONTROL_RESULT_ERROR, ex.what());
+ callout_handle.setArgument("response", response);
+ return;
+ }
+
ConstElementPtr response = services_->get()->processHAReset();
callout_handle.setArgument("response", response);
}
void
HAImpl::syncCompleteNotifyHandler(hooks::CalloutHandle& callout_handle) {
+ // Command must always be provided.
+ ConstElementPtr command;
+ callout_handle.getArgument("command", command);
+
+ // Retrieve arguments.
+ ConstElementPtr args;
+ static_cast<void>(parseCommand(args, command));
+
+ HAServicePtr service;
+ try {
+ service = getHAServiceByServerName("ha-sync-complete-notify", args);
+
+ } catch (const std::exception& ex) {
+ // There was an error while parsing command arguments. Return an error status
+ // code to notify the user.
+ ConstElementPtr response = createAnswer(CONTROL_RESULT_ERROR, ex.what());
+ callout_handle.setArgument("response", response);
+ return;
+ }
+
ConstElementPtr response = services_->get()->processSyncCompleteNotify();
callout_handle.setArgument("response", response);
}
+HAServicePtr
+HAImpl::getHAServiceByServerName(const std::string& command_name, ConstElementPtr args) const {
+ HAServicePtr service;
+ if (args) {
+ // Arguments must be a map.
+ if (args->getType() != Element::map) {
+ isc_throw(BadValue, "arguments in the '" << command_name << "' command are not a map");
+ }
+
+ auto server_name = args->get("server-name");
+
+ if (server_name) {
+ if (server_name->getType() != Element::string) {
+ isc_throw(BadValue, "'server-name' must be a string in the '" << command_name << "' command");
+ }
+ service = services_->get(server_name->stringValue());
+ if (!service) {
+ isc_throw(BadValue, server_name->stringValue() << " matches no configured"
+ << " 'server-name'");
+ }
+ }
+ }
+
+ if (!service) {
+ service = services_->get();
+ }
+
+ return (service);
+}
+
} // end of namespace isc::ha
} // end of namespace isc
/// @param callout_handle Callout handle provided to the callout.
void syncCompleteNotifyHandler(hooks::CalloutHandle& callout_handle);
+ /// @brief Attempts to get an @c HAService by server name.
+ ///
+ /// The function expects that the arguments contain the "server-name"
+ /// parameter. If the parameter is not specified, a default @c HAService
+ /// name is returned.
+ ///
+ /// @param command_name command name.
+ /// @param args command arguments or null.
+ /// @return Pointer to an @c HAService instance.
+ /// @throw BadValue if the specified server-name doesn't exist or if the
+ /// server-name wasn't specified and more than one @c HAService exists.
+ HAServicePtr getHAServiceByServerName(const std::string& command_name,
+ data::ConstElementPtr args) const;
+
protected:
/// @brief Holds parsed configuration.
(HttpRequest::Method::HTTP_POST, "/", HttpVersion::HTTP_11(),
HostHttpHeader(partner_config->getUrl().getStrippedHostname()));
partner_config->addBasicAuthHttpHeader(request);
- request->setBodyAsJson(CommandCreator::createHeartbeat(server_type_));
+ request->setBodyAsJson(CommandCreator::createHeartbeat(config_->getThisServerConfig()->getName(),
+ server_type_));
request->finalize();
// Response object should also be created because the HTTP client needs
HAService::asyncSendHAReset(HttpClient& http_client,
const HAConfig::PeerConfigPtr& config,
PostRequestCallback post_request_action) {
- ConstElementPtr command = CommandCreator::createHAReset(server_type_);
+ ConstElementPtr command = CommandCreator::createHAReset(config_->getThisServerConfig()->getName(),
+ server_type_);
// Create HTTP/1.1 request including our command.
PostHttpRequestJsonPtr request = boost::make_shared<PostHttpRequestJson>
HostHttpHeader(remote_config->getUrl().getStrippedHostname()));
remote_config->addBasicAuthHttpHeader(request);
- request->setBodyAsJson(CommandCreator::createSyncCompleteNotify(server_type_));
+ request->setBodyAsJson(CommandCreator::createSyncCompleteNotify(config_->getThisServerConfig()->getName(),
+ server_type_));
request->finalize();
// Response object should also be created because the HTTP client needs
// This test verifies that the ha-reset command sent to DHCPv4 server is correct.
TEST(CommandCreatorTest, createHAReset4) {
- ConstElementPtr command = CommandCreator::createHAReset(HAServerType::DHCPv4);
- ASSERT_NO_FATAL_FAILURE(testCommandBasics(command, "ha-reset", "dhcp4"));
+ ConstElementPtr command = CommandCreator::createHAReset("server1", HAServerType::DHCPv4);
+ ConstElementPtr arguments;
+ ASSERT_NO_FATAL_FAILURE(testCommandBasics(command, "ha-reset", "dhcp4", arguments));
+ auto server_name = arguments->get("server-name");
+ ASSERT_TRUE(server_name);
+ ASSERT_EQ(Element::string, server_name->getType());
+ EXPECT_EQ("server1", server_name->stringValue());
}
// This test verifies that the ha-heartbeat command is correct.
TEST(CommandCreatorTest, createHeartbeat4) {
- ConstElementPtr command = CommandCreator::createHeartbeat(HAServerType::DHCPv4);
- ASSERT_NO_FATAL_FAILURE(testCommandBasics(command, "ha-heartbeat", "dhcp4"));
+ ConstElementPtr command = CommandCreator::createHeartbeat("server1", HAServerType::DHCPv4);
+ ConstElementPtr arguments;
+ ASSERT_NO_FATAL_FAILURE(testCommandBasics(command, "ha-heartbeat", "dhcp4", arguments));
+ auto server_name = arguments->get("server-name");
+ ASSERT_TRUE(server_name);
+ ASSERT_EQ(Element::string, server_name->getType());
+ EXPECT_EQ("server1", server_name->stringValue());
}
// This test verifies that the command generated for the lease update
// This test verifies that the ha-reset command sent to DHCPv6 server is correct.
TEST(CommandCreatorTest, createHAReset6) {
- ConstElementPtr command = CommandCreator::createHAReset(HAServerType::DHCPv6);
- ASSERT_NO_FATAL_FAILURE(testCommandBasics(command, "ha-reset", "dhcp6"));
+ ConstElementPtr arguments;
+ ConstElementPtr command = CommandCreator::createHAReset("server1", HAServerType::DHCPv6);
+ ASSERT_NO_FATAL_FAILURE(testCommandBasics(command, "ha-reset", "dhcp6", arguments));
+ auto server_name = arguments->get("server-name");
+ ASSERT_TRUE(server_name);
+ ASSERT_EQ(Element::string, server_name->getType());
+ EXPECT_EQ("server1", server_name->stringValue());
}
// This test verifies that the command generated for the lease update
// This test verifies that the ha-sync-complete-notify command sent to a
// DHCPv4 server is correct.
TEST(CommandCreatorTest, createSyncCompleteNotify4) {
- ConstElementPtr command = CommandCreator::createSyncCompleteNotify(HAServerType::DHCPv4);
- ASSERT_NO_FATAL_FAILURE(testCommandBasics(command, "ha-sync-complete-notify", "dhcp4"));
+ ConstElementPtr command = CommandCreator::createSyncCompleteNotify("server1", HAServerType::DHCPv4);
+ ConstElementPtr arguments;
+ ASSERT_NO_FATAL_FAILURE(testCommandBasics(command, "ha-sync-complete-notify", "dhcp4", arguments));
+ auto server_name = arguments->get("server-name");
+ ASSERT_TRUE(server_name);
+ ASSERT_EQ(Element::string, server_name->getType());
+ EXPECT_EQ("server1", server_name->stringValue());
}
// This test verifies that the ha-sync-complete-notify command sent to a
// DHCPv4 server is correct.
TEST(CommandCreatorTest, createSyncCompleteNotify6) {
- ConstElementPtr command = CommandCreator::createSyncCompleteNotify(HAServerType::DHCPv6);
+ ConstElementPtr command = CommandCreator::createSyncCompleteNotify("server1", HAServerType::DHCPv6);
ConstElementPtr arguments;
- ASSERT_NO_FATAL_FAILURE(testCommandBasics(command, "ha-sync-complete-notify", "dhcp6"));
+ ASSERT_NO_FATAL_FAILURE(testCommandBasics(command, "ha-sync-complete-notify", "dhcp6", arguments));
+ auto server_name = arguments->get("server-name");
+ ASSERT_TRUE(server_name);
+ ASSERT_EQ(Element::string, server_name->getType());
+ EXPECT_EQ("server1", server_name->stringValue());
}
}
" command");
}
+ {
+ SCOPED_TRACE("Server name must be valid");
+ testSynchronizeHandler("{"
+ " \"command\": \"ha-sync\","
+ " \"arguments\": {"
+ " \"server-name\": \"server5\","
+ " \"max-period\": 20"
+ " }"
+ "}", "server5 matches no configured 'server-name'");
+ }
+
}
-// Tests ha-continue command handler.
+// Tests ha-continue command handler with a specified server name.
TEST_F(HAImplTest, continueHandler) {
HAImpl ha_impl;
ASSERT_NO_THROW(ha_impl.configure(createValidJsonConfiguration()));
+ // Starting the service is required prior to running any callouts.
+ NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
+ ASSERT_NO_THROW(ha_impl.startServices(io_service_, network_state,
+ HAServerType::DHCPv4));
+
+ ConstElementPtr command = Element::fromJSON("{"
+ "\"command\": \"ha-continue\","
+ "\"arguments\": {"
+ " \"server-name\": \"server1\""
+ "}"
+ "}");
+
+ CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
+ callout_handle->setArgument("command", command);
+
+ ASSERT_NO_THROW(ha_impl.continueHandler(*callout_handle));
+
+ ConstElementPtr response;
+ callout_handle->getArgument("response", response);
+ ASSERT_TRUE(response);
+
+ checkAnswer(response, CONTROL_RESULT_SUCCESS, "HA state machine is not paused.");
+}
+
+// Tests ha-continue command handler without a server name.
+TEST_F(HAImplTest, continueHandlerWithNoServerName) {
+ HAImpl ha_impl;
+ ASSERT_NO_THROW(ha_impl.configure(createValidJsonConfiguration()));
+
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(ha_impl.startServices(io_service_, network_state,
checkAnswer(response, CONTROL_RESULT_SUCCESS, "HA state machine is not paused.");
}
+// Tests ha-continue command handler with wrong server name.
+TEST_F(HAImplTest, continueHandlerWithWrongServerName) {
+ HAImpl ha_impl;
+ ASSERT_NO_THROW(ha_impl.configure(createValidJsonConfiguration()));
+
+ // Starting the service is required prior to running any callouts.
+ NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
+ ASSERT_NO_THROW(ha_impl.startServices(io_service_, network_state,
+ HAServerType::DHCPv4));
+
+ ConstElementPtr command = Element::fromJSON("{"
+ "\"command\": \"ha-continue\","
+ "\"arguments\": {"
+ " \"server-name\": \"server5\""
+ "}"
+ "}");
+
+ CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
+ callout_handle->setArgument("command", command);
+
+ ASSERT_NO_THROW(ha_impl.continueHandler(*callout_handle));
+
+ ConstElementPtr response;
+ callout_handle->getArgument("response", response);
+ ASSERT_TRUE(response);
+
+ checkAnswer(response, CONTROL_RESULT_ERROR, "server5 matches no configured 'server-name'");
+}
+
// Tests status-get command processed handler.
TEST_F(HAImplTest, statusGet) {
HAImpl ha_impl;
EXPECT_TRUE(isEquivalent(got, Element::fromJSON(expected)));
}
-// Test ha-maintenance-notify command handler.
+// Test ha-maintenance-notify command handler with server name.
TEST_F(HAImplTest, maintenanceNotify) {
HAImpl ha_impl;
ASSERT_NO_THROW(ha_impl.configure(createValidJsonConfiguration()));
+ // Starting the service is required prior to running any callouts.
+ NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
+ ASSERT_NO_THROW(ha_impl.startServices(io_service_, network_state,
+ HAServerType::DHCPv4));
+
+ ConstElementPtr command = Element::fromJSON(
+ "{"
+ " \"command\": \"ha-maintenance-notify\","
+ " \"arguments\": {"
+ " \"cancel\": false,"
+ " \"server-name\": \"server1\""
+ " }"
+ "}"
+ );
+
+ CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
+ callout_handle->setArgument("command", command);
+
+ ASSERT_NO_THROW(ha_impl.maintenanceNotifyHandler(*callout_handle));
+
+ ConstElementPtr response;
+ callout_handle->getArgument("response", response);
+ ASSERT_TRUE(response);
+
+ checkAnswer(response, CONTROL_RESULT_SUCCESS, "Server is in-maintenance state.");
+}
+
+// Test ha-maintenance-notify command handler without server name.
+TEST_F(HAImplTest, maintenanceNotifyNoServerName) {
+ HAImpl ha_impl;
+ ASSERT_NO_THROW(ha_impl.configure(createValidJsonConfiguration()));
+
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(ha_impl.startServices(io_service_, network_state,
checkAnswer(response, CONTROL_RESULT_SUCCESS, "Server is in-maintenance state.");
}
-// Test ha-reset command handler.
+// Test ha-maintenance-notify command handler without server name.
+TEST_F(HAImplTest, maintenanceNotifyBadServerName) {
+ HAImpl ha_impl;
+ ASSERT_NO_THROW(ha_impl.configure(createValidJsonConfiguration()));
+
+ // Starting the service is required prior to running any callouts.
+ NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
+ ASSERT_NO_THROW(ha_impl.startServices(io_service_, network_state,
+ HAServerType::DHCPv4));
+
+ ConstElementPtr command = Element::fromJSON(
+ "{"
+ " \"command\": \"ha-maintenance-notify\","
+ " \"arguments\": {"
+ " \"cancel\": false,"
+ " \"server-name\": \"server5\""
+ " }"
+ "}"
+ );
+
+ CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
+ callout_handle->setArgument("command", command);
+
+ ASSERT_NO_THROW(ha_impl.maintenanceNotifyHandler(*callout_handle));
+
+ ConstElementPtr response;
+ callout_handle->getArgument("response", response);
+ ASSERT_TRUE(response);
+
+ checkAnswer(response, CONTROL_RESULT_ERROR, "server5 matches no configured 'server-name'");
+}
+
+
+// Test ha-reset command handler with a specified server name.
TEST_F(HAImplTest, haReset) {
HAImpl ha_impl;
ASSERT_NO_THROW(ha_impl.configure(createValidJsonConfiguration()));
+ // Starting the service is required prior to running any callouts.
+ NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
+ ASSERT_NO_THROW(ha_impl.startServices(io_service_, network_state,
+ HAServerType::DHCPv4));
+
+ ConstElementPtr command = Element::fromJSON(
+ "{"
+ " \"command\": \"ha-reset\","
+ " \"arguments\": {"
+ " \"server-name\": \"server1\""
+ " }"
+ "}"
+ );
+
+ CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
+ callout_handle->setArgument("command", command);
+
+ ASSERT_NO_THROW(ha_impl.haResetHandler(*callout_handle));
+
+ ConstElementPtr response;
+ callout_handle->getArgument("response", response);
+ ASSERT_TRUE(response);
+
+ checkAnswer(response, CONTROL_RESULT_SUCCESS, "HA state machine already in WAITING state.");
+}
+
+// Test ha-reset command handler without a specified server name.
+TEST_F(HAImplTest, haResetNoServerName) {
+ HAImpl ha_impl;
+ ASSERT_NO_THROW(ha_impl.configure(createValidJsonConfiguration()));
+
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(ha_impl.startServices(io_service_, network_state,
checkAnswer(response, CONTROL_RESULT_SUCCESS, "HA state machine already in WAITING state.");
}
+// Test ha-reset command handler with a wrong server name.
+TEST_F(HAImplTest, haResetBadServerName) {
+ HAImpl ha_impl;
+ ASSERT_NO_THROW(ha_impl.configure(createValidJsonConfiguration()));
+
+ // Starting the service is required prior to running any callouts.
+ NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
+ ASSERT_NO_THROW(ha_impl.startServices(io_service_, network_state,
+ HAServerType::DHCPv4));
+
+ ConstElementPtr command = Element::fromJSON(
+ "{"
+ " \"command\": \"ha-reset\","
+ " \"arguments\": {"
+ " \"server-name\": \"server5\""
+ " }"
+ "}"
+ );
+
+ CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
+ callout_handle->setArgument("command", command);
+
+ ASSERT_NO_THROW(ha_impl.haResetHandler(*callout_handle));
+
+ ConstElementPtr response;
+ callout_handle->getArgument("response", response);
+ ASSERT_TRUE(response);
+
+ checkAnswer(response, CONTROL_RESULT_ERROR, "server5 matches no configured 'server-name'");
+}
+
+// Test ha-heartbeat command handler with a specified server name.
+TEST_F(HAImplTest, haHeartbeat) {
+ HAImpl ha_impl;
+ ASSERT_NO_THROW(ha_impl.configure(createValidJsonConfiguration()));
+
+ // Starting the service is required prior to running any callouts.
+ NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
+ ASSERT_NO_THROW(ha_impl.startServices(io_service_, network_state,
+ HAServerType::DHCPv4));
+
+ ConstElementPtr command = Element::fromJSON(
+ "{"
+ " \"command\": \"ha-heartbeat\","
+ " \"arguments\": {"
+ " \"server-name\": \"server1\""
+ " }"
+ "}"
+ );
+
+ CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
+ callout_handle->setArgument("command", command);
+
+ ASSERT_NO_THROW(ha_impl.heartbeatHandler(*callout_handle));
+
+ ConstElementPtr response;
+ callout_handle->getArgument("response", response);
+ ASSERT_TRUE(response);
+
+ checkAnswer(response, CONTROL_RESULT_SUCCESS, "HA peer status returned.");
+}
+
+// Test ha-heartbeat command handler without a specified server name.
+TEST_F(HAImplTest, haHeartbeatNoServerName) {
+ HAImpl ha_impl;
+ ASSERT_NO_THROW(ha_impl.configure(createValidJsonConfiguration()));
+
+ // Starting the service is required prior to running any callouts.
+ NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
+ ASSERT_NO_THROW(ha_impl.startServices(io_service_, network_state,
+ HAServerType::DHCPv4));
+
+ ConstElementPtr command = Element::fromJSON(
+ "{"
+ " \"command\": \"ha-heartbeat\""
+ "}"
+ );
+
+ CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
+ callout_handle->setArgument("command", command);
+
+ ASSERT_NO_THROW(ha_impl.heartbeatHandler(*callout_handle));
+
+ ConstElementPtr response;
+ callout_handle->getArgument("response", response);
+ ASSERT_TRUE(response);
+
+ checkAnswer(response, CONTROL_RESULT_SUCCESS, "HA peer status returned.");
+}
+
+// Test ha-heartbeat command handler with a wrong server name.
+TEST_F(HAImplTest, haHeartbeatBadServerName) {
+ HAImpl ha_impl;
+ ASSERT_NO_THROW(ha_impl.configure(createValidJsonConfiguration()));
+
+ // Starting the service is required prior to running any callouts.
+ NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
+ ASSERT_NO_THROW(ha_impl.startServices(io_service_, network_state,
+ HAServerType::DHCPv4));
+
+ ConstElementPtr command = Element::fromJSON(
+ "{"
+ " \"command\": \"ha-heartbeat\","
+ " \"arguments\": {"
+ " \"server-name\": \"server5\""
+ " }"
+ "}"
+ );
+
+ CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
+ callout_handle->setArgument("command", command);
+
+ ASSERT_NO_THROW(ha_impl.heartbeatHandler(*callout_handle));
+
+ ConstElementPtr response;
+ callout_handle->getArgument("response", response);
+ ASSERT_TRUE(response);
+
+ checkAnswer(response, CONTROL_RESULT_ERROR, "server5 matches no configured 'server-name'");
+}
+
+// Test ha-sync-complete-notify command handler with a specified server name.
+TEST_F(HAImplTest, haSyncCompleteNotify) {
+ HAImpl ha_impl;
+ ASSERT_NO_THROW(ha_impl.configure(createValidJsonConfiguration()));
+
+ // Starting the service is required prior to running any callouts.
+ NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
+ ASSERT_NO_THROW(ha_impl.startServices(io_service_, network_state,
+ HAServerType::DHCPv4));
+
+ ConstElementPtr command = Element::fromJSON(
+ "{"
+ " \"command\": \"ha-sync-complete-notify\","
+ " \"arguments\": {"
+ " \"server-name\": \"server1\""
+ " }"
+ "}"
+ );
+
+ CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
+ callout_handle->setArgument("command", command);
+
+ ASSERT_NO_THROW(ha_impl.syncCompleteNotifyHandler(*callout_handle));
+
+ ConstElementPtr response;
+ callout_handle->getArgument("response", response);
+ ASSERT_TRUE(response);
+
+ checkAnswer(response, CONTROL_RESULT_SUCCESS,
+ "Server successfully notified about the synchronization completion.");
+}
+
+// Test ha-sync-complete-notify command handler without a specified server name.
+TEST_F(HAImplTest, haSyncCompleteNotifyNoServerName) {
+ HAImpl ha_impl;
+ ASSERT_NO_THROW(ha_impl.configure(createValidJsonConfiguration()));
+
+ // Starting the service is required prior to running any callouts.
+ NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
+ ASSERT_NO_THROW(ha_impl.startServices(io_service_, network_state,
+ HAServerType::DHCPv4));
+
+ ConstElementPtr command = Element::fromJSON(
+ "{"
+ " \"command\": \"ha-sync-complete-notify\""
+ "}"
+ );
+
+ CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
+ callout_handle->setArgument("command", command);
+
+ ASSERT_NO_THROW(ha_impl.syncCompleteNotifyHandler(*callout_handle));
+
+ ConstElementPtr response;
+ callout_handle->getArgument("response", response);
+ ASSERT_TRUE(response);
+
+ checkAnswer(response, CONTROL_RESULT_SUCCESS,
+ "Server successfully notified about the synchronization completion.");
+}
+
+// Test ha-sync-complete-notify command handler with a wrong server name.
+TEST_F(HAImplTest, haSyncCompleteNotifyBadServerName) {
+ HAImpl ha_impl;
+ ASSERT_NO_THROW(ha_impl.configure(createValidJsonConfiguration()));
+
+ // Starting the service is required prior to running any callouts.
+ NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
+ ASSERT_NO_THROW(ha_impl.startServices(io_service_, network_state,
+ HAServerType::DHCPv4));
+
+ ConstElementPtr command = Element::fromJSON(
+ "{"
+ " \"command\": \"ha-sync-complete-notify\","
+ " \"arguments\": {"
+ " \"server-name\": \"server5\""
+ " }"
+ "}"
+ );
+
+ CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
+ callout_handle->setArgument("command", command);
+
+ ASSERT_NO_THROW(ha_impl.syncCompleteNotifyHandler(*callout_handle));
+
+ ConstElementPtr response;
+ callout_handle->getArgument("response", response);
+ ASSERT_TRUE(response);
+
+ checkAnswer(response, CONTROL_RESULT_ERROR, "server5 matches no configured 'server-name'");
+}
+
+// Test ha-scopes command handler with a specified server name.
+TEST_F(HAImplTest, haScopes) {
+ HAImpl ha_impl;
+ ASSERT_NO_THROW(ha_impl.configure(createValidJsonConfiguration()));
+
+ // Starting the service is required prior to running any callouts.
+ NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
+ ASSERT_NO_THROW(ha_impl.startServices(io_service_, network_state,
+ HAServerType::DHCPv4));
+
+ ConstElementPtr command = Element::fromJSON(
+ "{"
+ " \"command\": \"ha-scopes\","
+ " \"arguments\": {"
+ " \"scopes\": [ \"server1\", \"server2\" ],"
+ " \"server-name\": \"server1\""
+ " }"
+ "}"
+ );
+
+ CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
+ callout_handle->setArgument("command", command);
+
+ ASSERT_NO_THROW(ha_impl.scopesHandler(*callout_handle));
+
+ ConstElementPtr response;
+ callout_handle->getArgument("response", response);
+ ASSERT_TRUE(response);
+
+ checkAnswer(response, CONTROL_RESULT_SUCCESS, "New HA scopes configured.");
+}
+
+// Test ha-scopes command handler without a specified server name.
+TEST_F(HAImplTest, haScopesNoServerName) {
+ HAImpl ha_impl;
+ ASSERT_NO_THROW(ha_impl.configure(createValidJsonConfiguration()));
+
+ // Starting the service is required prior to running any callouts.
+ NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
+ ASSERT_NO_THROW(ha_impl.startServices(io_service_, network_state,
+ HAServerType::DHCPv4));
+
+ ConstElementPtr command = Element::fromJSON(
+ "{"
+ " \"command\": \"ha-scopes\","
+ " \"arguments\": {"
+ " \"scopes\": [ \"server1\", \"server2\" ]"
+ " }"
+ "}"
+ );
+
+ CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
+ callout_handle->setArgument("command", command);
+
+ ASSERT_NO_THROW(ha_impl.scopesHandler(*callout_handle));
+
+ ConstElementPtr response;
+ callout_handle->getArgument("response", response);
+ ASSERT_TRUE(response);
+
+ checkAnswer(response, CONTROL_RESULT_SUCCESS, "New HA scopes configured.");
+}
+
+// Test ha-scopes command handler with a wrong server name.
+TEST_F(HAImplTest, haScopesBadServerName) {
+ HAImpl ha_impl;
+ ASSERT_NO_THROW(ha_impl.configure(createValidJsonConfiguration()));
+
+ // Starting the service is required prior to running any callouts.
+ NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
+ ASSERT_NO_THROW(ha_impl.startServices(io_service_, network_state,
+ HAServerType::DHCPv4));
+
+ ConstElementPtr command = Element::fromJSON(
+ "{"
+ " \"command\": \"ha-scopes\","
+ " \"arguments\": {"
+ " \"scopes\": [ \"server1\", \"server2\" ],"
+ " \"server-name\": \"server5\""
+ " }"
+ "}"
+ );
+
+ CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
+ callout_handle->setArgument("command", command);
+
+ ASSERT_NO_THROW(ha_impl.scopesHandler(*callout_handle));
+
+ ConstElementPtr response;
+ callout_handle->getArgument("response", response);
+ ASSERT_TRUE(response);
+
+ checkAnswer(response, CONTROL_RESULT_ERROR, "server5 matches no configured 'server-name'");
+}
+
+
}