blob: 36aa9bfc2d55a0082bc151a5d396b98856726920 [file]
#ifndef THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_COLLECTOR_FRU_COLLECTOR_H_
#define THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_COLLECTOR_FRU_COLLECTOR_H_
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "gbmc-hal/api/app/inventory/smbios_inventory.h"
#include "gbmc-hal/api/app/inventory/smbios_types.h"
#include "bus_topology.pb.h"
#include "absl/base/thread_annotations.h"
#include "absl/container/flat_hash_map.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/time.h"
#include "time/clock.h"
#include <nlohmann/json.hpp>
#include "json_utils.h"
#include "tlbmc/collector/collector.h"
#include "tlbmc/collector/gpio_collector.h"
#include "tlbmc/collector/resource_state_manager.h"
#include "ad_hoc_fru_config.pb.h"
#include "tlbmc/configs/entity_config.h"
#include "tlbmc/hal/fru_scanner.h"
#include "fru.pb.h"
#include "resource.pb.h"
#include "status.pb.h"
#include "tlbmc/scheduler/scheduler.h"
namespace milotic_tlbmc {
using ::milotic::authz::GetValueAsJson;
constexpr absl::string_view kTlbmcConfigKey = "ProbeV2";
constexpr absl::string_view kAdHocFruConfigKey = "AdHocFruConfig";
// This class is thread-compatible.
// This class contains the currently scanned FRUs. It will pass them off to
// EntityConfig to create a read only topological configuration of the entire
// system
class FruCollector : public Collector {
public:
struct Options {
static std::vector<AdHocFruConfig> ParseAdHocFruConfigs(
const std::vector<nlohmann::json>& config_list);
// FruScanners to be used for ad-hoc FRU scanning. Pointers are not owned.
// This map stores at most one instance of each scanner type
// (e.g., I2C, FRAM). The key is the AdHocScannerType enum, allowing the
// system to look up and use the correct FruScanner implementation based
// on the 'scanner_type' specified in each AdHocFruConfig.
absl::flat_hash_map<AdHocScannerType, FruScanner*> fru_scanners;
std::vector<AdHocFruConfig> ad_hoc_fru_scanning_configs;
std::string cached_fru_table_path = "/run/tlbmc/fru_table.binarypb";
bool enable_deterministic_fru_scanning = false;
// If true, the deterministic FRU scanner will check serial number and part
// number matching to determine the presence status of a FRU. If false, the
// deterministic FRU scanner will mark FRUs as present as long as a valid
// eeprom is parsed on the designated bus.
bool strict_part_info_check = true;
absl::flat_hash_map<
uhmm::BmcMonitoredComponentsResponse::MonitoredComponent::ProbePath,
FruScanner*>
deterministic_fru_scanners;
// If true, the deterministic FRU scanner will scan periodically, with the
// interval specified by the following field.
bool enable_deterministic_periodic_fru_scanning = true;
int periodic_deterministic_fru_scan_interval_ms = 30 * 1000; // 30 seconds
// Configurations for resources whose probing depends on Host Power State.
std::vector<RelatedStateConfig> host_power_related_state_configs;
// Configurations for resources whose probing depends on OS State.
std::vector<RelatedStateConfig> os_state_related_state_configs;
// Populated in FruCollector::Create(). Will be invalidated after
// FruCollector::Create() returns.
absl::flat_hash_map<std::string, RelatedState>
fru_barepath_to_related_state;
std::shared_ptr<platforms::gbmc::hal::SmbiosInventory> smbios_inventory =
nullptr;
// SMBIOS inventory binary file paths
std::string smbios_single_host_file_path = "/var/lib/smbios/smbios2";
std::string smbios_multihost_host_file_path_prefix =
"/var/lib/smbios/smbios-remote-host-";
// SMBIOS status latch file paths
std::string smbios_single_host_status_file_path = "/run/smbios-mdr/status";
std::string smbios_multihost_host_status_file_path_prefix =
"/run/smbios-mdr/status-host-";
std::vector<uhmm::BmcMonitoredComponentsResponse::MonitoredComponent>
expected_smbios_components;
absl::flat_hash_map<std::string, std::vector<std::string>>
host_to_smbios_inventory_parts;
const ecclesia::Clock* clock = ecclesia::Clock::RealClock();
};
// Stores information about the current ongoing scans.
// The PeriodScanConfig is a list of scan intervals and scan counts.
// That means for each Scan Config we do scan_count number of scans at the
// scan_interval.
// We can determine which scan we are on by cur_periodic_scan_config_index
// and cur_scan_count at that index.
// Once the cur_scan_count is exhausted, we move to the next
// PeriodicScanConfig.
struct AdHocFruScanContext {
uint32_t cur_periodic_scan_config_index = 0;
uint32_t cur_periodic_scan_config_scan_count = 0;
bool found = false;
};
// Creates a FRU collector.
static absl::StatusOr<std::unique_ptr<FruCollector>> Create(Options options);
~FruCollector() override {
if (task_scheduler_ != nullptr) {
task_scheduler_->Stop();
}
}
FruCollector(const FruCollector&) = delete;
FruCollector& operator=(const FruCollector&) = delete;
// Scans all FRUs and returns a FruTable. This is to execute the
// deterministic FRU scanning process once. it directly scan the I2C FRUs and
// provide a quick presence check with part number and serial number.
virtual absl::StatusOr<FruTable> ScanAllFrusDeterministically();
// Scans all FRUs on demand and updates the deterministic fru table.
virtual absl::Status ScanDeterministicallyAndUpdateFruTable();
// Detects if there is a cached FRU binary. If so, reads it and returns it.
// Returns nullopt if there is no cached FRU binary or if there is an error.
// Note: at-hoc FRUs are not supported for cached FRUs.
static std::optional<RawFruTable> DetectsAndReadsCachedFru(
const std::string& cached_fru_table_path);
// Writes the given fru_table to the given cached_fru_table_path. Overwrites
// the file if it already exists.
static void WriteCachedFru(const RawFruTable& fru_table,
const std::string& cached_fru_table_path);
// Collects and stores FRU data.
virtual RawFruTable GetCopyOfCurrentScannedFrus() const
ABSL_LOCKS_EXCLUDED(fru_table_mutex_) {
absl::MutexLock lock(fru_table_mutex_);
return fru_table_;
}
// Returns a copy of the SMBIOS FruTable
virtual FruTable GetCopyOfSmbiosFruTable() const
ABSL_LOCKS_EXCLUDED(smbios_fru_table_mutex_) {
absl::MutexLock lock(smbios_fru_table_mutex_);
return smbios_fru_table_;
}
// Returns a copy of the Deterministic FruTable
virtual FruTable GetCopyOfDeterministicFruTable() const
ABSL_LOCKS_EXCLUDED(deterministic_fru_table_mutex_) {
absl::MutexLock lock(deterministic_fru_table_mutex_);
return deterministic_fru_table_;
}
virtual nlohmann::json ToJson() const override;
virtual void SetEntityConfig(
const std::shared_ptr<EntityConfig>& entity_config)
ABSL_LOCKS_EXCLUDED(entity_config_mutex_) {
absl::MutexLock lock(entity_config_mutex_);
entity_config_ = entity_config;
}
virtual std::shared_ptr<EntityConfig> GetEntityConfig() const
ABSL_LOCKS_EXCLUDED(entity_config_mutex_) {
absl::MutexLock lock(entity_config_mutex_);
return entity_config_;
}
virtual absl::Status ConfigureDeterministicCollection(const Config& config);
virtual const absl::flat_hash_map<RelatedState,
std::unique_ptr<ResourceStateManager>>&
GetResourceStateManagers() const {
return resource_state_managers_;
}
// Returns the associated SMBIOS inventory parts for the given host ID.
virtual absl::StatusOr<const std::vector<std::string>&>
GetAssociatedSmbiosInventoryParts(const std::string& host_id) const;
nlohmann::json GetSchedulerStats() const override {
return task_scheduler_->ToJson();
}
virtual std::size_t GetAllUserTaskCount() const {
return task_scheduler_->GetAllUserTaskCount();
}
// This triggers AdHocFruScanning. Since AdHocFru detection may trigger
// responses in other collectors, this function should only be called after
// everything is initialized
void SetUpAdHocFruScanning();
// This triggers periodic deterministic FRU scanning if enabled in options.
void SetUpPeriodicDeterministicFruScanning();
// OS State callbacks to integrate SMBIOS
virtual void HandleHostOsInactiveToStandby(const std::string& host_id,
bool setup_run);
virtual void HandleHostOsStandbyToInactive(const std::string& host_id,
bool setup_run);
// Returns the correct SMBIOS binary file path for a specific host id.
std::string GetSmbiosFilePathForHost(const std::string& host_id) const;
// Returns the correct SMBIOS status latch file path for a specific host id.
std::string GetSmbiosStatusFilePathForHost(const std::string& host_id) const;
// Returns the FRU key for the given FRU bus and address.
static std::string GetFruKey(uint64_t fru_bus, uint64_t fru_address);
protected:
// Protected constructor to allow mocking.
FruCollector() = default;
explicit FruCollector(
Options options, RawFruTable fru_table,
std::shared_ptr<TaskScheduler> task_scheduler,
absl::flat_hash_map<RelatedState, std::unique_ptr<ResourceStateManager>>
resource_state_managers);
static std::optional<AdHocFruScanContext> GetNextScanInfo(
const AdHocFruConfig& ad_hoc_fru_config,
const AdHocFruScanContext& scan_context);
// This function is not thread safe.
// This should only be called at object creation time which happens one a
// single thread.
void ScheduleAdHocFruScan(const AdHocFruConfig& ad_hoc_fru_config);
// Returns a pair of part number and serial number from the given FRU data
// fields. This is to extract the part number and serial number from the
// FRU data fields and use it to create the hardware info from the FRU read.
// Since there is no distinguishment for whether the FRU has board and product
// FRU or both, we will try to extract board's part and serial number first,
// and if it fails, we will extract product part and serial number.
static absl::StatusOr<std::pair<std::string, std::string>>
GetPartAndSerialNumberFromFields(
const absl::flat_hash_map<std::string, std::string>& fru_data_fields);
// Replaces the current FRU table with the given one.
void SetFruTable(RawFruTable&& fru_table)
ABSL_LOCKS_EXCLUDED(fru_table_mutex_) {
absl::MutexLock lock(fru_table_mutex_);
fru_table_ = fru_table;
}
void SetDeterministicFruTable(FruTable&& fru_table)
ABSL_LOCKS_EXCLUDED(deterministic_fru_table_mutex_) {
absl::MutexLock lock(deterministic_fru_table_mutex_);
deterministic_fru_table_ = std::move(fru_table);
}
bool ContainsFru(absl::string_view fru_key) const
ABSL_LOCKS_EXCLUDED(fru_table_mutex_) {
absl::MutexLock lock(fru_table_mutex_);
return fru_table_.key_to_raw_fru().contains(fru_key);
}
void AddFru(const RawFru& fru) ABSL_LOCKS_EXCLUDED(fru_table_mutex_) {
absl::MutexLock lock(fru_table_mutex_);
fru_table_.mutable_key_to_raw_fru()->insert({fru.key(), fru});
}
void AddAdHocFruScanContext(AdHocFruScanContext scan_context)
ABSL_LOCKS_EXCLUDED(ad_hoc_fru_scan_contexts_mutex_) {
absl::MutexLock lock(ad_hoc_fru_scan_contexts_mutex_);
ad_hoc_fru_scan_contexts_.push_back(scan_context);
}
void UpdateAdHocFruScanContext(size_t index, AdHocFruScanContext scan_context)
ABSL_LOCKS_EXCLUDED(ad_hoc_fru_scan_contexts_mutex_) {
absl::MutexLock lock(ad_hoc_fru_scan_contexts_mutex_);
ad_hoc_fru_scan_contexts_[index] = scan_context;
}
AdHocFruScanContext GetAdHocFruScanContext(size_t index)
ABSL_LOCKS_EXCLUDED(ad_hoc_fru_scan_contexts_mutex_) {
absl::MutexLock lock(ad_hoc_fru_scan_contexts_mutex_);
return ad_hoc_fru_scan_contexts_[index];
}
size_t GetAdHocFruScanContextsSize()
ABSL_LOCKS_EXCLUDED(ad_hoc_fru_scan_contexts_mutex_) {
absl::MutexLock lock(ad_hoc_fru_scan_contexts_mutex_);
return ad_hoc_fru_scan_contexts_.size();
}
// This function is not thread safe.
// This should only be called at object creation time which happens one a
// single thread.
void AttemptAdHocFruScan(const AdHocFruConfig& ad_hoc_fru_config,
size_t ad_hoc_fru_scan_context_id);
void RescheduleAdHocFruScan(const AdHocFruConfig& ad_hoc_fru_config,
size_t ad_hoc_fru_scan_context_id,
const AdHocFruScanContext& scan_context);
void ScanNextAdHocIfNeeded(const AdHocFruConfig& ad_hoc_fru_config,
size_t ad_hoc_fru_scan_context_id,
AdHocFruScanContext& scan_context);
absl::StatusOr<bool> IsFruMissingOk(const std::string& fru_barepath);
std::string ReadSmbiosStatus(const std::string& host_id) const;
void WriteSmbiosStatusConsumed(const std::string& host_id) const;
void InitializeSmbiosPollingStates();
void InitializeExpectedSmbiosComponents();
struct HostPollingState {
mutable absl::Mutex host_polling_mutex;
bool is_polling = false;
absl::Time polling_start_time;
int polling_task_id = -1;
};
absl::flat_hash_map<std::string, std::unique_ptr<HostPollingState>>
host_polling_states_;
HostPollingState* GetHostPollingState(const std::string& host_id) const;
std::shared_ptr<TaskScheduler> task_scheduler_;
Options options_;
std::vector<AdHocFruScanContext> ad_hoc_fru_scan_contexts_
ABSL_GUARDED_BY(ad_hoc_fru_scan_contexts_mutex_);
mutable absl::Mutex ad_hoc_fru_scan_contexts_mutex_;
mutable absl::Mutex fru_table_mutex_;
RawFruTable fru_table_ ABSL_GUARDED_BY(fru_table_mutex_);
mutable absl::Mutex entity_config_mutex_;
std::shared_ptr<EntityConfig> entity_config_
ABSL_GUARDED_BY(entity_config_mutex_);
mutable absl::Mutex smbios_fru_table_mutex_;
FruTable smbios_fru_table_ ABSL_GUARDED_BY(smbios_fru_table_mutex_);
mutable absl::Mutex deterministic_fru_table_mutex_;
FruTable deterministic_fru_table_
ABSL_GUARDED_BY(deterministic_fru_table_mutex_);
// This map is used to store the FRU barepath to its related state.
absl::flat_hash_map<std::string, RelatedState> fru_barepath_to_related_state_;
absl::flat_hash_map<std::string, std::vector<std::string>>
host_to_smbios_inventory_parts_;
absl::flat_hash_map<RelatedState, std::unique_ptr<ResourceStateManager>>
resource_state_managers_;
// This mutex is used to ensure that only one FRU scan is in
// progress at a time. Currently, this is only used for deterministic FRU
// scan and ad-hoc FRU scan for IPMI FRUs. Later, this will be extended or
// removed depends on how periodic FRU scanning is implemented with existing
// structure.
mutable absl::Mutex scan_in_progress_mutex_;
private:
void PollSmbiosStatus(const std::string& host_id,
const std::vector<std::string>& associated_parts,
bool setup_run)
ABSL_LOCKS_EXCLUDED(smbios_fru_table_mutex_);
void ExecuteSmbiosUpdate(const std::string& host_id,
const std::vector<std::string>& associated_parts,
bool update_status_file)
ABSL_LOCKS_EXCLUDED(smbios_fru_table_mutex_);
static void SetSmbiosInventoryStatus(
FruTable& table, const std::vector<std::string>& associated_parts,
PresenceStatus target_status);
static void ProcessSmbiosCpu(
const std::vector<std::string>& associated_parts,
const std::vector<platforms::gbmc::hal::SmbiosCpuInfo>& cpus,
FruTable& fru_table);
static void ProcessSmbiosDimm(
const std::vector<std::string>& associated_parts,
const std::vector<platforms::gbmc::hal::SmbiosDimmInfo>& dimms,
FruTable& fru_table);
mutable absl::Mutex deterministic_scan_task_id_mutex_;
std::optional<int> deterministic_scan_task_id_
ABSL_GUARDED_BY(deterministic_scan_task_id_mutex_);
static absl::StatusOr<RawFru> CreateRawFruFromIpmiFruData(
const std::unique_ptr<I2cFruInfo>& i2c_fru);
static absl::StatusOr<RawFru> CreateRawFruFromScanner(
AdHocScannerType scanner_type,
const std::unique_ptr<I2cFruInfo>& i2c_fru_info);
};
// This class is used when the FruCollector module is disabled.
class EmptyFruCollector final : public FruCollector {
public:
EmptyFruCollector() = default;
// Creates a dummy collector.
static std::unique_ptr<FruCollector> Create();
// Collects and stores FRU data.
RawFruTable GetCopyOfCurrentScannedFrus() const
ABSL_LOCKS_EXCLUDED(fru_table_mutex_) override;
FruTable GetCopyOfSmbiosFruTable() const
ABSL_LOCKS_EXCLUDED(smbios_fru_table_mutex_) override;
FruTable GetCopyOfDeterministicFruTable() const
ABSL_LOCKS_EXCLUDED(deterministic_fru_table_mutex_) override;
nlohmann::json ToJson() const override;
void SetEntityConfig(const std::shared_ptr<EntityConfig>& entity_config)
override ABSL_LOCKS_EXCLUDED(entity_config_mutex_);
std::shared_ptr<EntityConfig> GetEntityConfig() const override;
absl::Status ConfigureDeterministicCollection(const Config& config) override;
const absl::flat_hash_map<RelatedState,
std::unique_ptr<ResourceStateManager>>&
GetResourceStateManagers() const override;
nlohmann::json GetSchedulerStats() const override;
std::size_t GetAllUserTaskCount() const override;
absl::StatusOr<FruTable> ScanAllFrusDeterministically() override;
absl::Status ScanDeterministicallyAndUpdateFruTable() override;
};
} // namespace milotic_tlbmc
#endif // THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_COLLECTOR_FRU_COLLECTOR_H_