| #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 |