From: Marcin Siodelski Date: Mon, 11 Jan 2021 09:35:18 +0000 (+0100) Subject: [#1402] Implemented ha-reset command X-Git-Tag: Kea-1.9.4~70 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c58a9aa72f8b1ece5f76499a4dd219cd79c5f603;p=thirdparty%2Fkea.git [#1402] Implemented ha-reset command --- diff --git a/src/hooks/dhcp/high_availability/ha_callouts.cc b/src/hooks/dhcp/high_availability/ha_callouts.cc index 2a9c4568e8..4c3d801dc4 100644 --- a/src/hooks/dhcp/high_availability/ha_callouts.cc +++ b/src/hooks/dhcp/high_availability/ha_callouts.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2017-2020 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2017-2021 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 @@ -270,6 +270,17 @@ int maintenance_cancel_command(CalloutHandle& handle) { return (0); } +/// @brief ha-reset command handler implementation. +int ha_reset_command(CalloutHandle& handle) { + try { + impl->haResetHandler(handle); + + } catch (const std::exception& ex) { + LOG_ERROR(ha_logger, HA_RESET_HANDLER_FAILED) + .arg(ex.what()); + } +} + /// @brief This function is called when the library is loaded. /// /// @param handle library handle @@ -307,6 +318,7 @@ int load(LibraryHandle& handle) { handle.registerCommandCallout("ha-maintenance-notify", maintenance_notify_command); handle.registerCommandCallout("ha-maintenance-start", maintenance_start_command); handle.registerCommandCallout("ha-maintenance-cancel", maintenance_cancel_command); + handle.registerCommandCallout("ha-reset", ha_reset_command); } catch (const std::exception& ex) { LOG_ERROR(ha_logger, HA_CONFIGURATION_FAILED) diff --git a/src/hooks/dhcp/high_availability/ha_impl.cc b/src/hooks/dhcp/high_availability/ha_impl.cc index d7b7c45a8e..84d92eb91e 100644 --- a/src/hooks/dhcp/high_availability/ha_impl.cc +++ b/src/hooks/dhcp/high_availability/ha_impl.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2020 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2021 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 @@ -461,5 +461,11 @@ HAImpl::maintenanceCancelHandler(hooks::CalloutHandle& callout_handle) { callout_handle.setArgument("response", response); } +void +HAImpl::haResetHandler(hooks::CalloutHandle& callout_handle) { + ConstElementPtr response = service_->processHAReset(); + callout_handle.setArgument("response", response); +} + } // end of namespace isc::ha } // end of namespace isc diff --git a/src/hooks/dhcp/high_availability/ha_impl.h b/src/hooks/dhcp/high_availability/ha_impl.h index b4bc346d9d..969de358cc 100644 --- a/src/hooks/dhcp/high_availability/ha_impl.h +++ b/src/hooks/dhcp/high_availability/ha_impl.h @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2020 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2021 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 @@ -159,6 +159,11 @@ public: /// @param callout_handle Callout handle provided to the callout. void maintenanceCancelHandler(hooks::CalloutHandle& callout_handle); + /// @brief Implements handler for the ha-reset command. + /// + /// @param callout_handle Callout handle provided to the callout. + void haResetHandler(hooks::CalloutHandle& callout_handle); + protected: /// @brief Holds parsed configuration. diff --git a/src/hooks/dhcp/high_availability/ha_messages.cc b/src/hooks/dhcp/high_availability/ha_messages.cc index c32a2329f2..8b96d81ed7 100644 --- a/src/hooks/dhcp/high_availability/ha_messages.cc +++ b/src/hooks/dhcp/high_availability/ha_messages.cc @@ -82,6 +82,7 @@ extern const isc::log::MessageID HA_MAINTENANCE_STARTED = "HA_MAINTENANCE_STARTE 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_RESET_HANDLER_FAILED = "HA_RESET_HANDLER_FAILED"; extern const isc::log::MessageID HA_SCOPES_HANDLER_FAILED = "HA_SCOPES_HANDLER_FAILED"; extern const isc::log::MessageID HA_SERVICE_STARTED = "HA_SERVICE_STARTED"; extern const isc::log::MessageID HA_STATE_MACHINE_CONTINUED = "HA_STATE_MACHINE_CONTINUED"; @@ -176,6 +177,7 @@ const char* values[] = { "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_RESET_HANDLER_FAILED", "ha-reset command failed: %1", "HA_SCOPES_HANDLER_FAILED", "ha-scopes command failed: %1", "HA_SERVICE_STARTED", "started high availability service in %1 mode as %2 server", "HA_STATE_MACHINE_CONTINUED", "state machine is un-paused", diff --git a/src/hooks/dhcp/high_availability/ha_messages.h b/src/hooks/dhcp/high_availability/ha_messages.h index 0e72ebb13a..600bc1b453 100644 --- a/src/hooks/dhcp/high_availability/ha_messages.h +++ b/src/hooks/dhcp/high_availability/ha_messages.h @@ -83,6 +83,7 @@ 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_RESET_HANDLER_FAILED; extern const isc::log::MessageID HA_SCOPES_HANDLER_FAILED; extern const isc::log::MessageID HA_SERVICE_STARTED; extern const isc::log::MessageID HA_STATE_MACHINE_CONTINUED; diff --git a/src/hooks/dhcp/high_availability/ha_messages.mes b/src/hooks/dhcp/high_availability/ha_messages.mes index 9b2e3f6d2a..c9e0240b71 100644 --- a/src/hooks/dhcp/high_availability/ha_messages.mes +++ b/src/hooks/dhcp/high_availability/ha_messages.mes @@ -470,6 +470,11 @@ This error message is issued to indicate that the configuration for the High Availability hooks library hasn't been specified. The 'high-availability' parameter must be specified for the hooks library to load properly. +% HA_RESET_HANDLER_FAILED ha-reset command failed: %1 +This error message is issued to indicate that the ha-reset command handler +failed while processing the command. The argument provides the reason for +failure. + % HA_SCOPES_HANDLER_FAILED ha-scopes command failed: %1 This error message is issued to indicate that the ha-scopes command handler failed while processing the command. The argument provides reason for diff --git a/src/hooks/dhcp/high_availability/ha_service.cc b/src/hooks/dhcp/high_availability/ha_service.cc index cd8ca98bbb..c1c87f7e43 100644 --- a/src/hooks/dhcp/high_availability/ha_service.cc +++ b/src/hooks/dhcp/high_availability/ha_service.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2020 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2021 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 @@ -1506,6 +1506,13 @@ HAService::processHeartbeat() { arguments)); } +ConstElementPtr +HAService::processHAReset() { + verboseTransition(HA_WAITING_ST); + runModel(NOP_EVT); + return (createAnswer(CONTROL_RESULT_SUCCESS, "HA state machine reset.")); +} + void HAService::asyncSendHeartbeat() { HAConfig::PeerConfigPtr partner_config = config_->getFailoverPeerConfig(); diff --git a/src/hooks/dhcp/high_availability/ha_service.h b/src/hooks/dhcp/high_availability/ha_service.h index c9aebafc31..9cd1535445 100644 --- a/src/hooks/dhcp/high_availability/ha_service.h +++ b/src/hooks/dhcp/high_availability/ha_service.h @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2020 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2021 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 @@ -638,12 +638,24 @@ public: /// @brief Processes status-get command and returns a response. /// - /// - /// /// @c HAImpl::commandProcessed calls this to add information about the /// HA servers status into the status-get response. data::ConstElementPtr processStatusGet() const; + /// @brief Processes ha-reset command and returns a response. + /// + /// This method processes ha-reset command which instructs the server to + /// transition to the waiting state. A partner may send this command when + /// the communication is re-established between the servers in the + /// communication-recovery state and full lease database synchronization is + /// required. This command may also be sent by an operator if the server's + /// state is invalid and the reset operation may help correct the situation. + /// + /// The ha-reset takes no arguments. + /// + /// @return Pointer to a response to the ha-reset command. + data::ConstElementPtr processHAReset(); + protected: /// @brief Starts asynchronous heartbeat to a peer. diff --git a/src/hooks/dhcp/high_availability/tests/ha_impl_unittest.cc b/src/hooks/dhcp/high_availability/tests/ha_impl_unittest.cc index 38d634af97..900c39df37 100644 --- a/src/hooks/dhcp/high_availability/tests/ha_impl_unittest.cc +++ b/src/hooks/dhcp/high_availability/tests/ha_impl_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2020 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2021 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 @@ -731,4 +731,32 @@ TEST_F(HAImplTest, maintenanceNotify) { checkAnswer(response, CONTROL_RESULT_SUCCESS, "Server is in-maintenance state."); } +// Test ha-reset command handler. +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.startService(io_service_, network_state, + HAServerType::DHCPv4)); + + ConstElementPtr command = Element::fromJSON( + "{" + " \"command\": \"ha-reset\"" + "}" + ); + + 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 reset."); +} + } diff --git a/src/hooks/dhcp/high_availability/tests/ha_service_unittest.cc b/src/hooks/dhcp/high_availability/tests/ha_service_unittest.cc index d5dfaf2223..dbfe177045 100644 --- a/src/hooks/dhcp/high_availability/tests/ha_service_unittest.cc +++ b/src/hooks/dhcp/high_availability/tests/ha_service_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2020 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2021 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 @@ -4371,6 +4371,29 @@ TEST_F(HAServiceTest, processMaintenanceCancelPartnerUnauthorized) { EXPECT_EQ(HA_PARTNER_IN_MAINTENANCE_ST, service.getCurrState()); } +// This test verifies that the ha-reset command is processed successfully. +TEST_F(HAServiceTest, processHAReset) { + HAConfigPtr config_storage = createValidConfiguration(); + TestHAService service(io_service_, network_state_, config_storage); + + // Transion the server to the load-balancing state. + EXPECT_NO_THROW(service.transition(HA_LOAD_BALANCING_ST, HAService::NOP_EVT)); + + // Process ha-reset command that should cause the server to transition + // to the waiting state. + ConstElementPtr rsp; + ASSERT_NO_THROW(rsp = service.processHAReset()); + + // The server should have responded. + ASSERT_TRUE(rsp); + checkAnswer(rsp, CONTROL_RESULT_SUCCESS, "HA state machine reset."); + + // Response should include no arguments. + EXPECT_FALSE(rsp->get("arguments")); + + // The server should be in the waiting state. + EXPECT_EQ(HA_WAITING_ST, service.getCurrState()); +} /// @brief HA partner to the server under test. ///