common/warm_restart.cpp (198 lines of code) (raw):
#include <string>
#include <climits>
#include "logger.h"
#include "schema.h"
#include "warm_restart.h"
namespace swss {
const WarmStart::WarmStartStateNameMap WarmStart::warmStartStateNameMap =
{
{INITIALIZED, "initialized"},
{RESTORED, "restored"},
{REPLAYED, "replayed"},
{RECONCILED, "reconciled"},
{WSDISABLED, "disabled"},
{WSUNKNOWN, "unknown"}
};
const WarmStart::DataCheckStateNameMap WarmStart::dataCheckStateNameMap =
{
{CHECK_IGNORED, "ignored"},
{CHECK_PASSED, "passed"},
{CHECK_FAILED, "failed"}
};
WarmStart &WarmStart::getInstance(void)
{
static WarmStart m_warmStart;
return m_warmStart;
}
/*
* WarmStart's initialization method -- to be invoked once per application.
*/
void WarmStart::initialize(const std::string &app_name,
const std::string &docker_name,
unsigned int db_timeout,
bool isTcpConn)
{
auto& warmStart = getInstance();
if (warmStart.m_initialized)
{
return;
}
/* Use unix socket for db connection by default */
warmStart.m_stateDb =
std::make_shared<swss::DBConnector>("STATE_DB", db_timeout, isTcpConn);
warmStart.m_cfgDb =
std::make_shared<swss::DBConnector>("CONFIG_DB", db_timeout, isTcpConn);
warmStart.m_stateWarmRestartEnableTable =
std::unique_ptr<Table>(new Table(warmStart.m_stateDb.get(), STATE_WARM_RESTART_ENABLE_TABLE_NAME));
warmStart.m_stateWarmRestartTable =
std::unique_ptr<Table>(new Table(warmStart.m_stateDb.get(), STATE_WARM_RESTART_TABLE_NAME));
warmStart.m_cfgWarmRestartTable =
std::unique_ptr<Table>(new Table(warmStart.m_cfgDb.get(), CFG_WARM_RESTART_TABLE_NAME));
warmStart.m_initialized = true;
}
/*
* <1> Upon system reboot, the system enable knob will be checked.
* If enabled, database data will be preserved, if not, database will be flushed.
* No need to check docker level knobs in this case since the whole system is being rebooted .
* <2> Upon docker service start, first to check system knob.
* if enabled, docker warm-start should be performed, otherwise system warm-reboot will be ruined.
* If system knob disabled, while docker knob enabled, this is likely an individual docker
* warm-restart request.
* Within each application which should take care warm start case,
* when the system knob or docker knob enabled, we do further check on the
* actual warm-start state ( restore_count), if no warm-start state data available,
* the database has been flushed, do cold start. Otherwise warm-start.
*/
/*
* Method to verify/obtain the state of Warm-Restart feature for any warm-reboot
* capable component. Typically, this function will be called during initialization of
* SONiC modules; however, method could be invoked at any given point to verify the
* latest state of Warm-Restart functionality and to update the restore_count value.
* A flag of incr_restore_cnt is used to increase the restore cnt or not. Default: true
*/
bool WarmStart::checkWarmStart(const std::string &app_name,
const std::string &docker_name,
const bool incr_restore_cnt)
{
std::string value;
auto& warmStart = getInstance();
// Check system level warm-restart config first
warmStart.m_stateWarmRestartEnableTable->hget("system", "enable", value);
if (value == "true")
{
warmStart.m_enabled = true;
warmStart.m_systemWarmRebootEnabled = true;
}
// Check docker level warm-restart configuration
warmStart.m_stateWarmRestartEnableTable->hget(docker_name, "enable", value);
if (value == "true")
{
warmStart.m_enabled = true;
}
// For cold start, the whole state db will be flushed including warm start table.
// Create the entry for this app here.
if (!warmStart.m_enabled)
{
warmStart.m_stateWarmRestartTable->hset(app_name, "restore_count", "0");
return false;
}
uint32_t restore_count = 0;
warmStart.m_stateWarmRestartTable->hget(app_name, "restore_count", value);
if (value == "")
{
SWSS_LOG_WARN("%s doing warm start, but restore_count not found in stateDB %s table, fall back to cold start",
app_name.c_str(), STATE_WARM_RESTART_TABLE_NAME);
warmStart.m_enabled = false;
warmStart.m_systemWarmRebootEnabled = false;
warmStart.m_stateWarmRestartTable->hset(app_name, "restore_count", "0");
return false;
}
if (incr_restore_cnt)
{
restore_count = (uint32_t)stoul(value);
restore_count++;
warmStart.m_stateWarmRestartTable->hset(app_name, "restore_count",
std::to_string(restore_count));
}
SWSS_LOG_NOTICE("%s doing warm start, restore count %d", app_name.c_str(),
restore_count);
return true;
}
/*
* Obtain the time-interval defined by a warm-restart-capable application
* corresponding to the amount of time required to complete a full-restart cycle.
* This time-duration (warmStartTimer) will be taken into account by the
* warm-restart logic to kick-off the reconciliation process of this application.
* A returned value of '0' implies that no valid interval was found.
*/
uint32_t WarmStart::getWarmStartTimer(const std::string &app_name,
const std::string &docker_name)
{
auto& warmStart = getInstance();
std::string timer_name = app_name + "_timer";
std::string timer_value_str;
warmStart.m_cfgWarmRestartTable->hget(docker_name, timer_name, timer_value_str);
unsigned long int temp_value = strtoul(timer_value_str.c_str(), NULL, 0);
if (temp_value != 0 && temp_value != ULONG_MAX &&
temp_value <= MAXIMUM_WARMRESTART_TIMER_VALUE)
{
SWSS_LOG_NOTICE("Getting warmStartTimer for docker: %s, app: %s, value: %lu",
docker_name.c_str(), app_name.c_str(), temp_value);
return (uint32_t)temp_value;
}
SWSS_LOG_NOTICE("warmStartTimer is not configured or invalid for docker: %s, app: %s",
docker_name.c_str(), app_name.c_str());
return 0;
}
bool WarmStart::isWarmStart(void)
{
auto& warmStart = getInstance();
return warmStart.m_enabled;
}
bool WarmStart::isSystemWarmRebootEnabled(void)
{
auto& warmStart = getInstance();
return warmStart.m_systemWarmRebootEnabled;
}
void WarmStart::getWarmStartState(const std::string &app_name, WarmStartState &state)
{
std::string statestr;
auto& warmStart = getInstance();
state = RECONCILED;
if (!isWarmStart())
{
return;
}
warmStart.m_stateWarmRestartTable->hget(app_name, "state", statestr);
/* If warm-start is enabled, state cannot be assumed as Reconciled
* It should be set to unknown
*/
state = WSUNKNOWN;
for (auto it = warmStartStateNameMap.begin(); it != warmStartStateNameMap.end(); it++)
{
if (it->second == statestr)
{
state = it->first;
break;
}
}
SWSS_LOG_INFO("%s warm start state get %s(%d)",
app_name.c_str(), statestr.c_str(), state);
return;
}
// Set the WarmStart FSM state for a particular application.
void WarmStart::setWarmStartState(const std::string &app_name, WarmStartState state)
{
auto& warmStart = getInstance();
warmStart.m_stateWarmRestartTable->hset(app_name,
"state",
warmStartStateNameMap.at(state).c_str());
SWSS_LOG_NOTICE("%s warm start state changed to %s",
app_name.c_str(),
warmStartStateNameMap.at(state).c_str());
}
// Set the WarmStart data check state for a particular application.
void WarmStart::setDataCheckState(const std::string &app_name, DataCheckStage stage, DataCheckState state)
{
auto& warmStart = getInstance();
std::string stageField = "restore_check";
if (stage == STAGE_SHUTDOWN)
{
stageField = "shutdown_check";
}
warmStart.m_stateWarmRestartTable->hset(app_name,
stageField,
dataCheckStateNameMap.at(state).c_str());
SWSS_LOG_NOTICE("%s %s result %s",
app_name.c_str(),
stageField.c_str(),
dataCheckStateNameMap.at(state).c_str());
}
WarmStart::DataCheckState WarmStart::getDataCheckState(const std::string &app_name, DataCheckStage stage)
{
auto& warmStart = getInstance();
std::string stateStr;
std::string stageField = "restore_check";
if (stage == STAGE_SHUTDOWN)
{
stageField = "shutdown_check";
}
warmStart.m_stateWarmRestartTable->hget(app_name,
stageField,
stateStr);
DataCheckState state = CHECK_IGNORED;
for (auto it = dataCheckStateNameMap.begin(); it != dataCheckStateNameMap.end(); it++)
{
if (it->second == stateStr)
{
state = it->first;
break;
}
}
SWSS_LOG_NOTICE("Getting %s %s result %s",
app_name.c_str(),
stageField.c_str(),
stateStr.c_str());
return state;
}
}