blob: 58f33f0e392597f0a9a43a016322373780bb6d1e [file]
#include "tlbmc/collector/resource_state_manager.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/log/log.h"
#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/time.h"
#include "boost/asio/io_context.hpp" // NOLINT
#include "resource.pb.h"
#include "tlbmc/time/time.h"
namespace milotic_tlbmc {
absl::StatusOr<std::unique_ptr<ResourceStateManager>>
ResourceStateManager::Create(ResourceStateManagerOptions options) {
for (const auto& config : options.related_state_configs) {
if (config.related_state() != options.target_state) {
return absl::InvalidArgumentError(
absl::StrCat("Target state ", RelatedState_Name(options.target_state),
" does not match config state ",
RelatedState_Name(config.related_state())));
}
if (config.host_id().empty()) {
return absl::InvalidArgumentError(
absl::StrCat("Host id is empty for config: ", config));
}
if (config.resource_identifiers().empty()) {
return absl::InvalidArgumentError(
absl::StrCat("Resource identifiers are empty for config: ", config));
}
for (const auto& resource_identifier : config.resource_identifiers()) {
if (resource_identifier.empty()) {
return absl::InvalidArgumentError(absl::StrCat(
"Some resource identifier is empty for config: ", config));
}
}
if (config.has_expected_uptime() &&
DecodeGoogleApiProto(config.expected_uptime()) < absl::ZeroDuration()) {
return absl::InvalidArgumentError(
absl::StrCat("Expected uptime is not positive for config: ", config));
}
}
return absl::WrapUnique(new ResourceStateManager(
options.target_state, options.related_state_configs));
}
ResourceStateManager::ResourceStateManager(
RelatedState target_state,
const std::vector<milotic_tlbmc::RelatedStateConfig>& related_state_configs)
: target_state_(target_state),
related_state_configs_(related_state_configs),
work_guard_(boost::asio::make_work_guard(io_context_)),
io_thread_([this]() { io_context_.run(); }) {
for (const auto& config : related_state_configs_) {
for (const std::string& resource_identifier :
config.resource_identifiers()) {
resource_states_[resource_identifier].associated_host_id =
config.host_id();
resource_states_[resource_identifier].expected_uptime =
DecodeGoogleApiProto(config.expected_uptime());
resource_states_[resource_identifier].timer =
std::make_unique<boost::asio::steady_timer>(io_context_);
}
}
}
ResourceStateManager::~ResourceStateManager() {
{
absl::MutexLock lock(mutex_);
for (auto& [name, state] : resource_states_) {
state.timer->cancel();
}
}
work_guard_.reset();
io_context_.stop();
if (io_thread_.joinable()) {
io_thread_.join();
}
}
void ResourceStateManager::HandleStateOnToOff(std::string host_id) {
absl::MutexLock lock(mutex_);
DLOG(INFO) << RelatedState_Name(target_state_) << ": On to off event: "
<< " setting missing_ok to true for all resources.";
for (auto& [name, state] : resource_states_) {
if (state.associated_host_id != host_id) {
LOG(WARNING) << "ResourceStateManager::HandleStateOnToOff: Resource "
<< name << " is not associated with host " << host_id;
continue;
}
state.missing_ok = true;
state.timer->cancel();
LOG(WARNING) << "ResourceStateManager::HandleStateOnToOff: Resource "
<< name << " is associated with host " << host_id
<< ", setting missing_ok to true and cancelling timer.";
}
}
void ResourceStateManager::HandleStateOffToOn(std::string host_id) {
absl::MutexLock lock(mutex_);
DLOG(INFO) << RelatedState_Name(target_state_)
<< ": off to on event, starting timers...";
for (auto& [name, state] : resource_states_) {
if (state.associated_host_id != host_id) {
LOG(WARNING) << "ResourceStateManager::HandleStateOffToOn: Resource "
<< name << " is not associated with host " << host_id;
continue;
}
state.missing_ok = true;
absl::Duration timeout = state.expected_uptime;
LOG(WARNING)
<< "ResourceStateManager::HandleStateOffToOn: Starting timer for "
<< name << " with timeout " << timeout;
state.timer->expires_after(absl::ToChronoNanoseconds(timeout));
state.timer->async_wait([this, name](const boost::system::error_code& ec) {
if (ec == boost::asio::error::operation_aborted) {
LOG(WARNING) << "ResourceStateManager::HandleStateOffToOn: Timer "
"cancelled for "
<< name;
return; // Timer cancelled.
}
absl::MutexLock lock(mutex_);
resource_states_[name].missing_ok = false;
LOG(WARNING) << "ResourceStateManager timer expired for " << name
<< ", setting missing_ok to false.";
});
}
}
bool ResourceStateManager::IsStateRelatedResourceMissingOk(
const std::string& resource_name) const {
absl::MutexLock lock(mutex_);
auto it = resource_states_.find(resource_name);
if (it == resource_states_.end()) {
LOG(WARNING) << "Resource " << resource_name
<< " not found in resource_states_ map. Please check if the "
"resource has a proper related state config.";
return false;
}
return it->second.missing_ok;
}
} // namespace milotic_tlbmc