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