|  | #include "tlbmc/store/store_impl.h" | 
|  |  | 
|  | #include <cstddef> | 
|  | #include <filesystem> | 
|  | #include <memory> | 
|  | #include <string> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "absl/functional/any_invocable.h" | 
|  | #include "absl/log/log.h" | 
|  | #include "absl/memory/memory.h" | 
|  | #include "absl/status/status.h" | 
|  | #include "absl/status/statusor.h" | 
|  | #include "absl/strings/string_view.h" | 
|  | #include "absl/time/clock.h" | 
|  | #include "absl/time/time.h" | 
|  | #include "g3/macros.h" | 
|  | #include "nlohmann/json.hpp" | 
|  | #include "tlbmc/collector/collector.h" | 
|  | #include "tlbmc/collector/fru_collector.h" | 
|  | #include "tlbmc/collector/sensor_collector.h" | 
|  | #include "ad_hoc_fru_config.pb.h" | 
|  | #include "tlbmc/configs/entity_config.h" | 
|  | #include "topology_config.pb.h" | 
|  | #include "fru.pb.h" | 
|  | #include "sensor.pb.h" | 
|  | #include "tlbmc/scheduler/scheduler.h" | 
|  | #include "tlbmc/sensors/sensor.h" | 
|  | #include "tlbmc/store/store.h" | 
|  | #include "tlbmc/trace/tracer.h" | 
|  | #include "router_interface.h" | 
|  |  | 
|  | namespace milotic_tlbmc { | 
|  |  | 
|  | using ::crow::RouterInterface; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | constexpr int kDefaultPeriodicDumpIntervalMs = 1000 * 60 * 60;  // 1 hour | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | absl::Status StoreImpl::ConfigureCollection(const Collector::Config& config, | 
|  | Collector::Type type) const { | 
|  | switch (type) { | 
|  | case Collector::Type::kSensor: | 
|  | return all_collectors_.sensor_collector->ConfigureCollection(config); | 
|  | default: | 
|  | return absl::InvalidArgumentError("Unsupported collector type"); | 
|  | } | 
|  | } | 
|  |  | 
|  | std::vector<std::shared_ptr<const Sensor>> StoreImpl::GetAllSensors() const { | 
|  | return all_collectors_.sensor_collector->GetAllSensors(); | 
|  | } | 
|  |  | 
|  | std::vector<std::string> StoreImpl::GetAllSensorKeysByConfigName( | 
|  | const std::string& board_config_name) const { | 
|  | return all_collectors_.sensor_collector->GetAllSensorKeysByConfigName( | 
|  | board_config_name); | 
|  | } | 
|  |  | 
|  | std::shared_ptr<const Sensor> StoreImpl::GetSensorByConfigNameAndSensorKey( | 
|  | const std::string& board_config_name, const std::string& sensor_key) const { | 
|  | return all_collectors_.sensor_collector->GetSensorByConfigNameAndSensorKey( | 
|  | board_config_name, sensor_key); | 
|  | } | 
|  |  | 
|  | absl::StatusOr<const Fru*> StoreImpl::GetFru(absl::string_view key) const { | 
|  | return entity_config_->GetFru(key); | 
|  | } | 
|  |  | 
|  | absl::StatusOr<const FruTable*> StoreImpl::GetAllFrus() const { | 
|  | return entity_config_->GetAllFrus(); | 
|  | } | 
|  |  | 
|  | nlohmann::json StoreImpl::ToJson() const { | 
|  | nlohmann::json json; | 
|  | json["Sensor"] = all_collectors_.sensor_collector->ToJson(); | 
|  | json["Fru"] = all_collectors_.fru_collector->ToJson(); | 
|  | json["EntityConfig"] = entity_config_->ToJson(); | 
|  | return json; | 
|  | } | 
|  |  | 
|  | nlohmann::json StoreImpl::GetSchedulerStats() const { | 
|  | nlohmann::json json; | 
|  | json["SensorCollector"] = | 
|  | all_collectors_.sensor_collector->GetSchedulerStats(); | 
|  | json["FruCollector"] = all_collectors_.fru_collector->GetSchedulerStats(); | 
|  | json["Store"] = task_scheduler_->ToJson(); | 
|  | return json; | 
|  | } | 
|  |  | 
|  | Store::Metrics StoreImpl::GetMetrics() const { return metrics_; } | 
|  |  | 
|  | absl::StatusOr<const TopologyConfigNode*> StoreImpl::GetFruTopology( | 
|  | absl::string_view config_name) const { | 
|  | return entity_config_->GetFruTopologyByConfig(config_name); | 
|  | } | 
|  |  | 
|  | absl::StatusOr<const TopologyConfig*> StoreImpl::GetTopologyConfig() const { | 
|  | return entity_config_->GetTopologyConfig(); | 
|  | } | 
|  |  | 
|  | absl::StatusOr<std::vector<std::string>> StoreImpl::GetAllConfigNames() const { | 
|  | return entity_config_->GetAllConfigNames(); | 
|  | } | 
|  |  | 
|  | absl::StatusOr<std::string> StoreImpl::GetConfigNameByFruKey( | 
|  | absl::string_view fru_key) const { | 
|  | return entity_config_->GetConfigNameByFruKey(fru_key); | 
|  | } | 
|  |  | 
|  | absl::StatusOr<std::string> StoreImpl::GetFruKeyByConfigName( | 
|  | absl::string_view config_name) const { | 
|  | return entity_config_->GetFruKeyByConfigName(config_name); | 
|  | } | 
|  |  | 
|  | absl::StatusOr<std::unique_ptr<StoreImpl>> StoreImpl::Create( | 
|  | const Options& options) { | 
|  | Metrics metrics_at_bootup; | 
|  | absl::Time create_start_time = absl::Now(); | 
|  | if (options.fru_scanners.empty()) { | 
|  | return absl::InvalidArgumentError("FruScanners is empty"); | 
|  | } | 
|  | if (options.i2c_sysfs == nullptr) { | 
|  | return absl::InvalidArgumentError("I2cSysfs is null"); | 
|  | } | 
|  |  | 
|  | absl::Time parse_configs_start_time = absl::Now(); | 
|  | Tracer::GetInstance().AddOneOffDatapoint("Tlbmc-Parse-Configs-Begin", | 
|  | parse_configs_start_time); | 
|  |  | 
|  | ECCLESIA_RETURN_IF_ERROR( | 
|  | options.entity_config_reader->LoadEntityConfig(options.config_location)); | 
|  |  | 
|  | std::vector<AdHocFruConfig> ad_hoc_fru_scanning_configs = | 
|  | FruCollector::Options::ParseAdHocFruConfigs( | 
|  | options.entity_config_reader->GetConfig()); | 
|  |  | 
|  | absl::Time parse_configs_end_time = absl::Now(); | 
|  | metrics_at_bootup.config_parse_duration = | 
|  | parse_configs_end_time - parse_configs_start_time; | 
|  | Tracer::GetInstance().AddOneOffDatapoint("Tlbmc-Parse-Configs-End", | 
|  | parse_configs_end_time); | 
|  |  | 
|  | size_t ad_hoc_fru_count = ad_hoc_fru_scanning_configs.size(); | 
|  |  | 
|  | FruCollector::Options fru_collector_options = { | 
|  | .fru_scanners = options.fru_scanners, | 
|  | .ad_hoc_fru_scanning_configs = std::move(ad_hoc_fru_scanning_configs)}; | 
|  |  | 
|  | absl::Time fru_scan_start_time = absl::Now(); | 
|  | Tracer::GetInstance().AddOneOffDatapoint("Tlbmc-Scan-FRUs-Begin", | 
|  | fru_scan_start_time); | 
|  |  | 
|  | ECCLESIA_ASSIGN_OR_RETURN( | 
|  | std::unique_ptr<FruCollector> fru_collector, | 
|  | options.collector_factory->CreateFruCollector(fru_collector_options)); | 
|  |  | 
|  | const RawFruTable fru_table = fru_collector->GetCopyOfCurrentScannedFrus(); | 
|  | absl::Time fru_scan_end_time = absl::Now(); | 
|  | metrics_at_bootup.fru_scan_and_collector_create_duration = | 
|  | fru_scan_end_time - fru_scan_start_time; | 
|  | Tracer::GetInstance().AddOneOffDatapoint("Tlbmc-Scan-FRUs-End", | 
|  | fru_scan_end_time); | 
|  |  | 
|  | // Load Configs | 
|  | absl::Time load_configs_start_time = absl::Now(); | 
|  | Tracer::GetInstance().AddOneOffDatapoint("Tlbmc-Load-Configs-Begin", | 
|  | load_configs_start_time); | 
|  |  | 
|  | absl::StatusOr<std::unique_ptr<EntityConfig>> entity_config = | 
|  | options.entity_config_reader->CreateEntityConfig(fru_table, | 
|  | ad_hoc_fru_count); | 
|  | if (!entity_config.ok()) { | 
|  | LOG(ERROR) << "Failed to create entity config: " << entity_config.status(); | 
|  | LOG(ERROR) << "The cached FRU table will be deleted."; | 
|  | // If Store is bad, then cache may be bad too. Let cache reload on next | 
|  | // boot. | 
|  | std::filesystem::remove(fru_collector_options.cached_fru_table_path); | 
|  | return entity_config.status(); | 
|  | } | 
|  |  | 
|  | absl::Time load_configs_end_time = absl::Now(); | 
|  | metrics_at_bootup.topology_config_load_duration = | 
|  | load_configs_end_time - load_configs_start_time; | 
|  | Tracer::GetInstance().AddOneOffDatapoint("Tlbmc-Load-Configs-End", | 
|  | load_configs_end_time); | 
|  |  | 
|  | // EntityConfig must be shared as it is used by the FruCollector. | 
|  | std::shared_ptr<EntityConfig> entity_config_shared = | 
|  | absl::ShareUniquePtr(std::move(*entity_config)); | 
|  |  | 
|  | fru_collector->SetEntityConfig(entity_config_shared); | 
|  |  | 
|  | // Now create all the collectors one by one. | 
|  | absl::Time sensor_collector_create_start_time = absl::Now(); | 
|  | Tracer::GetInstance().AddOneOffDatapoint("Tlbmc-Create-SensorCollector-Begin", | 
|  | sensor_collector_create_start_time); | 
|  |  | 
|  | absl::StatusOr<std::unique_ptr<SensorCollector>> sensor_collector = | 
|  | options.collector_factory->CreateSensorCollector( | 
|  | {.entity_config = *entity_config_shared, | 
|  | .i2c_sysfs = *options.i2c_sysfs, | 
|  | .override_sensor_sampling_interval_ms = | 
|  | options.override_sensor_sampling_interval_ms}); | 
|  | if (!sensor_collector.ok()) { | 
|  | LOG(ERROR) << "Failed to create sensor collector: " | 
|  | << sensor_collector.status(); | 
|  | LOG(ERROR) << "The cached FRU table will be deleted."; | 
|  | // If Store is bad, then cache may be bad too. Let cache reload on next | 
|  | // boot. | 
|  | std::filesystem::remove(fru_collector_options.cached_fru_table_path); | 
|  | return sensor_collector.status(); | 
|  | } | 
|  |  | 
|  | absl::Time sensor_collector_create_end_time = absl::Now(); | 
|  | metrics_at_bootup.sensor_collector_create_duration = | 
|  | sensor_collector_create_end_time - sensor_collector_create_start_time; | 
|  | Tracer::GetInstance().AddOneOffDatapoint("Tlbmc-Create-SensorCollector-End", | 
|  | sensor_collector_create_end_time); | 
|  |  | 
|  | // TODO(rahulkpr): Add support for other collectors. | 
|  |  | 
|  | AllCollectors all_collectors; | 
|  | all_collectors.sensor_collector = std::move(*sensor_collector); | 
|  | all_collectors.fru_collector = std::move(fru_collector); | 
|  | int periodic_dump_interval_ms = | 
|  | options.override_store_snapshot_interval_ms.value_or( | 
|  | kDefaultPeriodicDumpIntervalMs); | 
|  | metrics_at_bootup.time_to_ready = absl::Now() - create_start_time; | 
|  | return absl::WrapUnique(new StoreImpl( | 
|  | std::move(all_collectors), std::move(entity_config_shared), | 
|  | absl::Milliseconds(periodic_dump_interval_ms), metrics_at_bootup)); | 
|  | } | 
|  |  | 
|  | void StoreImpl::SetSmartRouter(RouterInterface* smart_router) { | 
|  | entity_config_->SetSmartRouter(smart_router); | 
|  | } | 
|  |  | 
|  | StoreImpl::StoreImpl(AllCollectors all_collectors, | 
|  | std::shared_ptr<EntityConfig> entity_config, | 
|  | absl::Duration store_snapshot_interval, | 
|  | Metrics metrics_at_bootup) | 
|  | : all_collectors_(std::move(all_collectors)), | 
|  | entity_config_(std::move(entity_config)), | 
|  | task_scheduler_(std::make_unique<TaskScheduler>()), | 
|  | metrics_(metrics_at_bootup) { | 
|  | task_scheduler_->RunAndScheduleAsync( | 
|  | [this](absl::AnyInvocable<void()> done) { | 
|  | // Dump the store snapshot. | 
|  | LOG(WARNING) << "=== Store snapshot ==="; | 
|  | LOG(WARNING) << ToJson().dump(); | 
|  | LOG(WARNING) << "=== END ==="; | 
|  |  | 
|  | // Dump the collector scheduler stats. | 
|  | LOG(WARNING) << "=== Collector Scheduler stats ==="; | 
|  | LOG(WARNING) << GetSchedulerStats().dump(); | 
|  | LOG(WARNING) << "=== END ==="; | 
|  | done(); | 
|  | }, | 
|  | store_snapshot_interval); | 
|  | } | 
|  |  | 
|  | // Since the task scheduler is a member variable, we need to stop it before | 
|  | // destructing since the store is running an async task that is accessing the | 
|  | // task scheduler. | 
|  | StoreImpl::~StoreImpl() { task_scheduler_->Stop(); } | 
|  |  | 
|  | }  // namespace milotic_tlbmc |