Currently it excludes terminated and syncing states.
const HAConfigPtr& config, const HAServerType& server_type)
: io_service_(io_service), network_state_(network_state), config_(config),
server_type_(server_type), client_(*io_service), communication_state_(),
- query_filter_(config), pending_requests_() {
+ query_filter_(config), state_machine_control_(config), pending_requests_() {
if (server_type == HAServerType::DHCPv4) {
communication_state_.reset(new CommunicationState4(io_service_, config));
if (doOnEntry()) {
query_filter_.serveDefaultScopes();
adjustNetworkState();
+ state_machine_control_.notify(getCurrState());
}
scheduleHeartbeat();
+ if (state_machine_control_.amPaused()) {
+ postNextEvent(NOP_EVT);
+ return;
+ }
+
// Check if the clock skew is still acceptable. If not, transition to
// the terminated state.
if (shouldTerminate()) {
query_filter_.serveDefaultScopes();
}
adjustNetworkState();
+
+ state_machine_control_.notify(getCurrState());
}
scheduleHeartbeat();
+ if (state_machine_control_.amPaused()) {
+ postNextEvent(NOP_EVT);
+ return;
+ }
+
// Check if the clock skew is still acceptable. If not, transition to
// the terminated state.
if (shouldTerminate()) {
if (doOnEntry()) {
query_filter_.serveNoScopes();
adjustNetworkState();
+ state_machine_control_.notify(getCurrState());
}
scheduleHeartbeat();
+ if (state_machine_control_.amPaused()) {
+ postNextEvent(NOP_EVT);
+ return;
+ }
+
// Check if the clock skew is still acceptable. If not, transition to
// the terminated state.
if (shouldTerminate()) {
if (doOnEntry()) {
query_filter_.serveNoScopes();
adjustNetworkState();
+ state_machine_control_.notify(getCurrState());
+ }
+
+ // Only schedule the heartbeat for non-backup servers.
+ if (config_->getThisServerConfig()->getRole() != HAConfig::PeerConfig::BACKUP) {
+ scheduleHeartbeat();
+ }
+
+ if (state_machine_control_.amPaused()) {
+ postNextEvent(NOP_EVT);
+ return;
}
// Backup server must remain in its own state.
return;
}
- scheduleHeartbeat();
-
// Check if the clock skew is still acceptable. If not, transition to
// the terminated state.
if (shouldTerminate()) {
}
}
+void
+HAService::unpause() {
+ state_machine_control_.unpause();
+}
+
void
HAService::serveDefaultScopes() {
query_filter_.serveDefaultScopes();
#include <communication_state.h>
#include <ha_config.h>
#include <ha_server_type.h>
+#include <ha_state_machine_control.h>
#include <query_filter.h>
#include <asiolink/io_service.h>
#include <cc/data.h>
public:
+ /// @brief Un-pauses the state machine.
+ ///
+ /// This is method is invoked when the server receives the 'ha-continue'
+ /// command which instructs the server to unblock the HA state machine
+ /// so as it may transition to a next state.
+ void unpause();
+
/// @brief Instructs the HA service to serve default scopes.
///
/// This method is mostly useful for unit testing. The scopes need to be
/// @brief Selects queries to be processed/dropped.
QueryFilter query_filter_;
+ /// @brief Controls state machine pausing and unpausing.
+ HAStateMachineControl state_machine_control_;
+
/// @brief Map holding a number of scheduled requests for a given packet.
///
/// A single callout may send multiple requests at the same time, e.g.
#define HA_STATE_MACHINE_CONTROL_H
#include <ha_config.h>
+#include <boost/shared_ptr.hpp>
#include <set>
namespace isc {
std::set<int> visited_states_;
};
+/// @brief Shared pointer to the @c HAStateMachineControl.
+typedef boost::shared_ptr<HAStateMachineControl> HAStateMachineControlPtr;
+
} // end of namespace isc::ha
} // end of namespace isc
EXPECT_EQ(HAService::HA_SYNCING_SUCCEEDED_EVT, service_->getLastEvent());
}
+// This test verifies that the HA state machine can be paused in certain states
+// when the server is operating in load balancing mode.
+TEST_F(HAServiceStateMachineTest, stateTransitionsLoadBalancingPause) {
+ partner_->startup();
+
+ HAConfigPtr valid_config = createValidConfiguration();
+ auto state_configs = valid_config->getStateMachineConfig();
+
+ for (auto cfg = state_configs.begin(); cfg != state_configs.end(); ++cfg) {
+ cfg->second->setPausing("always");
+ }
+
+ startService(valid_config);
+
+ {
+ SCOPED_TRACE("LOAD BALANCING state transitions");
+
+ testTransition(MyState(HA_LOAD_BALANCING_ST), PartnerState(HA_TERMINATED_ST),
+ FinalState(HA_LOAD_BALANCING_ST));
+
+ service_->unpause();
+
+ testTransition(MyState(HA_LOAD_BALANCING_ST), PartnerState(HA_TERMINATED_ST),
+ FinalState(HA_TERMINATED_ST));
+ }
+
+ {
+ SCOPED_TRACE("PARTNER DOWN state transitions");
+
+ testTransition(MyState(HA_PARTNER_DOWN_ST), PartnerState(HA_LOAD_BALANCING_ST),
+ FinalState(HA_PARTNER_DOWN_ST));
+
+ service_->unpause();
+
+ testTransition(MyState(HA_PARTNER_DOWN_ST), PartnerState(HA_LOAD_BALANCING_ST),
+ FinalState(HA_WAITING_ST));
+ }
+
+
+ {
+ SCOPED_TRACE("READY state transitions");
+
+ testTransition(MyState(HA_READY_ST), PartnerState(HA_LOAD_BALANCING_ST),
+ FinalState(HA_READY_ST));
+
+ service_->unpause();
+
+ testTransition(MyState(HA_READY_ST), PartnerState(HA_LOAD_BALANCING_ST),
+ FinalState(HA_LOAD_BALANCING_ST));
+ }
+
+
+ {
+ SCOPED_TRACE("WAITING state transitions");
+
+ testTransition(MyState(HA_WAITING_ST), PartnerState(HA_LOAD_BALANCING_ST),
+ FinalState(HA_WAITING_ST));
+
+ service_->unpause();
+
+ testTransition(MyState(HA_WAITING_ST), PartnerState(HA_LOAD_BALANCING_ST),
+ FinalState(HA_SYNCING_ST));
+ }
+}
+
// This test verifies that the server takes ownership of the given scopes
// and whether the DHCP service is disabled or enabled in certain states.
TEST_F(HAServiceStateMachineTest, scopesServingLoadBalancing) {