Commit 247f42ab authored by Marcin Siodelski's avatar Marcin Siodelski

[5674] Implement state machine control for HA.

parent d228bc2f
......@@ -35,6 +35,7 @@ libha_la_SOURCES += ha_log.cc ha_log.h
libha_la_SOURCES += ha_server_type.h
libha_la_SOURCES += ha_service.cc ha_service.h
libha_la_SOURCES += ha_service_states.cc ha_service_states.h
libha_la_SOURCES += ha_state_machine_control.cc ha_state_machine_control.h
libha_la_SOURCES += query_filter.cc query_filter.h
libha_la_SOURCES += version.cc
......
// Copyright (C) 2018 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
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <ha_state_machine_control.h>
namespace isc {
namespace ha {
HAStateMachineControl::HAStateMachineControl(const HAConfigPtr& config)
: config_(config), paused_(false), visited_states_() {
}
void
HAStateMachineControl::notify(const int state) {
// Always get the configuration to verify that the state identifier is
// recognized.
HAConfig::StateConfigPtr state_config = config_->getStateConfig(state);
// Pause if we should always pause in this state or we should pause once
// and this is the first time we visit this state.
bool first_visit = (visited_states_.count(state) == 0);
// If this is the first time we're in this state, record it.
if (first_visit) {
visited_states_.insert(state);
}
// Only pause the state machine if it is not paused already.
if (!amPaused()) {
if ((state_config->getPausing() == HAConfig::StateConfig::PAUSE_ALWAYS) ||
((state_config->getPausing() == HAConfig::StateConfig::PAUSE_ONCE) &&
first_visit)) {
paused_ = true;
}
}
}
} // end of namespace isc::ha
} // end of namespace isc
// Copyright (C) 2018 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
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef HA_STATE_MACHINE_CONTROL_H
#define HA_STATE_MACHINE_CONTROL_H
#include <ha_config.h>
#include <set>
namespace isc {
namespace ha {
/// @brief Hold runtime information about HA state machine.
///
/// Currently, the only available runtime information is whether the
/// state machine is paused and if it should paused in certain states
/// upon next transition to these states. Note that the server may be
/// configured to only pause the state machine upon the first transition
/// to the certain states.
///
/// The @c HAService calls @c notify upon transition to the next state.
/// This class determines whether the state machine should be paused
/// in this state based on the provided configuration and the history
/// of already visited states. The @c amPaused method should be later
/// called in the state handlers to check if the state machine is
/// paused or running. In the former case, it should not transition to
/// any other state until the state machine is unpaused.
class HAStateMachineControl {
public:
/// @brief Constructor.
///
/// @param config HA hooks library configuration.
explicit HAStateMachineControl(const HAConfigPtr& config);
/// @brief Receives notification from the HA state handlers about
/// the current state.
///
/// This method pauses sets a flag to pause the HA state machine if
/// it should be paused in the given state. It also records that
/// state as "visited", so as it is possible to determine whether
/// the state machine should be paused the next time it transitions
/// to this state.
///
/// @param state current state of the state machine.
/// @throw BadValue if the state is not recognized.
void notify(int state);
/// @brief Informs whether the state machine is paused.
///
/// @return true if the state machine is paused, false otherwise.
bool amPaused() const {
return (paused_);
}
/// @brief Clears flag indicating that the state machine is paused.
void unpause() {
paused_ = false;
}
private:
/// @brief HA configuration
HAConfigPtr config_;
/// @brief Boolean flag indicating if the state machine is paused.
bool paused_;
/// @brief Keeps track of visited states.
std::set<int> visited_states_;
};
} // end of namespace isc::ha
} // end of namespace isc
#endif
......@@ -29,6 +29,7 @@ ha_unittests_SOURCES += communication_state_unittest.cc
ha_unittests_SOURCES += ha_config_unittest.cc
ha_unittests_SOURCES += ha_impl_unittest.cc
ha_unittests_SOURCES += ha_service_unittest.cc
ha_unittests_SOURCES += ha_state_machine_control_unittest.cc
ha_unittests_SOURCES += ha_test.cc ha_test.h
ha_unittests_SOURCES += query_filter_unittest.cc
ha_unittests_SOURCES += run_unittests.cc
......
// Copyright (C) 2018 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
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <ha_state_machine_control.h>
#include <ha_test.h>
#include <gtest/gtest.h>
using namespace isc::ha;
using namespace isc::ha::test;
namespace {
class HAStateMachineControlTest : public HATest {
public:
/// @brief Constructor.
HAStateMachineControlTest()
: HATest() {
}
};
// This test verifies that pausing HA state machine works as expected.
TEST_F(HAStateMachineControlTest, pause) {
HAConfigPtr config = createValidConfiguration();
// Always pause in the waiting state and pause on first transition to
// the ready state. Do not pause for other states.
config->getStateConfig(HA_WAITING_ST)->setPausing("always");
config->getStateConfig(HA_READY_ST)->setPausing("once");
// Initially we shouldn't be paused.
HAStateMachineControl control(config);
EXPECT_FALSE(control.amPaused());
// Should not pause in load-balancing state.
EXPECT_NO_THROW(control.notify(HA_LOAD_BALANCING_ST));
EXPECT_FALSE(control.amPaused());
// Should always pause in waiting state.
EXPECT_NO_THROW(control.notify(HA_WAITING_ST));
EXPECT_TRUE(control.amPaused());
control.unpause();
EXPECT_FALSE(control.amPaused());
// Should pause once in the ready state.
EXPECT_NO_THROW(control.notify(HA_READY_ST));
EXPECT_TRUE(control.amPaused());
control.unpause();
EXPECT_FALSE(control.amPaused());
// Do not pause the second time in the ready state.
EXPECT_NO_THROW(control.notify(HA_READY_ST));
EXPECT_FALSE(control.amPaused());
// Never pause in the load-balancing state.
EXPECT_NO_THROW(control.notify(HA_LOAD_BALANCING_ST));
EXPECT_FALSE(control.amPaused());
// Always pause in the waiting state.
EXPECT_NO_THROW(control.notify(HA_WAITING_ST));
EXPECT_TRUE(control.amPaused());
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment