blob: b36bf18c65fe4549fdd3580061e09df80ad8604b [file] [edit]
#include "tlbmc/store/store_hft_adapter.h"
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "absl/log/log.h"
#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "absl/strings/substitute.h"
#include "absl/time/time.h"
#include "g3/macros.h"
#include "tlbmc/adapter/data_source.h"
#include "fru_identifier.pb.h"
#include "fru_payload.pb.h"
#include "identifier.pb.h"
#include "payload.pb.h"
#include "sensor_identifier.pb.h"
#include "sensor_payload.pb.h"
#include "subscription_params.pb.h"
#include "tlbmc/collector/monitoring_change_base.h"
#include "topology_config.pb.h"
#include "fru.pb.h"
#include "memory.pb.h"
#include "processor.pb.h"
#include "resource.pb.h"
#include "sensor.pb.h"
#include "tlbmc/sensors/sensor.h"
#include "tlbmc/store/store.h"
#include "tlbmc/time/time.h"
namespace milotic_tlbmc {
namespace {
constexpr absl::string_view kAllFrusIdentifier = "all_frus";
bool IsGetAllFrus(const milotic_hft::Identifier& identifier) {
return identifier.fru_identifier().barepath() == kAllFrusIdentifier;
}
std::vector<milotic_hft::Identifier> GetSensorIdentifiers(const Store* store) {
std::vector<milotic_hft::Identifier> identifiers;
for (const std::shared_ptr<const Sensor>& sensor : store->GetAllSensors()) {
milotic_hft::Identifier identifier;
identifier.mutable_sensor_identifier()->set_name(sensor->GetKey());
identifiers.push_back(identifier);
}
return identifiers;
}
absl::StatusOr<milotic_hft::Payload> CollectSensorData(
const milotic_hft::SensorIdentifier& sensor_identifier, Store* store,
absl::Time start_time) {
milotic_hft::Payload payload;
milotic_hft::HighFrequencySensorsReadingsBatch* batch =
payload.mutable_high_frequency_sensors_readings_batch();
std::shared_ptr<const Sensor> sensor =
store->GetSensorBySensorKey(sensor_identifier.name());
if (sensor == nullptr) {
return absl::NotFoundError(
absl::Substitute("Sensor $0 not found", sensor_identifier.name()));
}
const SensorAttributesDynamic dynamic_attributes =
sensor->GetSensorAttributesDynamic();
batch->set_configured_sampling_interval_ms(
dynamic_attributes.refresh_interval().nanos() / 1000000);
milotic_hft::HighFrequencySensorsReadings* sensors =
batch->add_high_frequency_sensors();
milotic_hft::SensorIdentifier* sensor_identifier_from_readings =
sensors->mutable_sensor_identifier();
sensors->mutable_state()->set_status(
ConvertToHftStatus(dynamic_attributes.state().status()));
if (dynamic_attributes.state().has_status_message()) {
sensors->mutable_state()->set_status_message(
dynamic_attributes.state().status_message());
}
const SensorAttributesStatic& static_attributes =
sensor->GetSensorAttributesStatic();
// Replace the local root identifier with /phys when exporting the devpath.
ECCLESIA_ASSIGN_OR_RETURN(const std::string& devpath,
store->GetDevpathFromSensor(sensor));
if (!absl::StartsWith(devpath, "/phys")) {
std::vector<std::string> devpath_parts = absl::StrSplit(devpath, '/');
if (devpath_parts.size() < 2) {
return absl::InternalError(
absl::Substitute("Invalid devpath: $0", devpath));
}
size_t device_index = devpath_parts[1].find_first_of(':');
if (device_index != std::string::npos) {
devpath_parts[1] =
absl::StrCat("phys", devpath_parts[1].substr(device_index));
} else {
devpath_parts[1] = "phys";
}
sensor_identifier_from_readings->set_devpath(
absl::StrJoin(devpath_parts, "/"));
} else {
sensor_identifier_from_readings->set_devpath(devpath);
}
std::string sensor_name = GetTrimmedSensorName(sensor->GetKey());
sensor_identifier_from_readings->set_key(sensor->GetKey());
sensor_identifier_from_readings->set_name(sensor_name);
sensor_identifier_from_readings->set_source(milotic_hft::SENSOR_SOURCE_BMC);
sensor_identifier_from_readings->set_units(
ConvertToHftUnit(static_attributes.unit()));
sensor_identifier_from_readings->set_type(
ConvertToHftType(static_attributes.unit()));
for (const auto& sensor_data :
sensor->GetSensorDataHistorySince(start_time)) {
// Drop non-finite readings (NaN/Inf, e.g. from a virtual-sensor expression
// or a malformed parse) so they never enter the HFT stream. A genuinely
// unavailable read carries no reading and is surfaced via sensor status
// instead. This also keeps the UNIT_COUNT cast below well-defined.
if (sensor_data->has_reading() && !std::isfinite(sensor_data->reading())) {
continue;
}
milotic_hft::HighFrequencySensorReading* reading =
sensors->add_timestamped_readings();
if (sensor_data->has_reading()) {
if (static_attributes.unit() == UNIT_COUNT) {
reading->set_count(static_cast<uint64_t>(sensor_data->reading()));
} else {
reading->set_float_reading(static_cast<float>(sensor_data->reading()));
}
} else if (sensor_data->has_count()) {
reading->set_count(sensor_data->count());
}
reading->set_timestamp_ns(
absl::ToUnixNanos(DecodeGoogleApiProto(sensor_data->timestamp())));
if (sensor_data->has_source_timestamp()) {
reading->set_source_timestamp_ns(absl::ToUnixNanos(
DecodeGoogleApiProto(sensor_data->source_timestamp())));
} else {
reading->set_source_timestamp_ns(reading->timestamp_ns());
}
}
return payload;
}
void PopulateFruDetails(const milotic_tlbmc::Fru& fru,
milotic_hft::FruDetails* details) {
details->mutable_fru_identifier()->set_barepath(
fru.attributes().expected_hardware_info().expected_barepath());
details->set_presence_status(fru.attributes().presence_status());
if (fru.data().has_fru_info()) {
*details->mutable_i2c_info() = fru.data().fru_info();
} else if (fru.data().has_memory_info()) {
*details->mutable_memory() = fru.data().memory_info();
} else if (fru.data().has_processor_info()) {
*details->mutable_processor() = fru.data().processor_info();
}
}
absl::StatusOr<milotic_hft::Payload> CollectFruData(Store* store) {
if (store->GetFruCollector() == nullptr) {
return absl::InternalError("FruCollector is null");
}
milotic_hft::Payload payload;
milotic_hft::FruBatch* batch = payload.mutable_fru_batch();
// FRUs from Deterministic Scans
milotic_tlbmc::FruTable determ_frus =
store->GetFruCollector()->GetCopyOfDeterministicFruTable();
for (const auto& [key, fru] : determ_frus.key_to_fru()) {
if (!fru.attributes().expected_hardware_info().has_expected_barepath()) {
// See comment in
// https://source.corp.google.com/piper///depot/google3/third_party/milotic/external/cc/fast_sanity/service/fru_service.cc;l=385
LOG(WARNING) << "FRU key: " << key << " does not have expected barepath.";
continue;
}
PopulateFruDetails(fru, batch->add_frus());
}
// SMBIOS FRUs
milotic_tlbmc::FruTable smbios_frus =
store->GetFruCollector()->GetCopyOfSmbiosFruTable();
for (const auto& [key, fru] : smbios_frus.key_to_fru()) {
PopulateFruDetails(fru, batch->add_frus());
}
return payload;
}
// An implementation of the SensorMutationBatch which uses the
// MonitoringChangeBase interface to apply changes to the store.
// This is used only for Identifier::kSensorIdentifier.
class StoreSensorMutationBatch : public milotic_hft::SensorMutationBatch {
public:
StoreSensorMutationBatch(
milotic_hft::DataSource* data_source,
const std::vector<milotic_hft::Identifier>& identifiers,
std::unique_ptr<MonitoringChangeBase> monitoring_change)
: milotic_hft::SensorMutationBatch(data_source, identifiers),
monitoring_change_(std::move(monitoring_change)) {}
absl::Status SetSamplingInterval(const milotic_hft::Identifier& identifier,
int sampling_interval_ms) &
override {
ECCLESIA_RETURN_IF_ERROR(CheckIdentifier(identifier));
if (identifier.identifier_case() !=
milotic_hft::Identifier::kSensorIdentifier) {
return absl::InvalidArgumentError("Not a sensor identifier");
}
return AddSensor(identifier.sensor_identifier().name(),
{sampling_interval_ms, std::nullopt});
}
absl::Status SetBatchSize(const milotic_hft::Identifier& identifier,
int max_batch_size) &
override {
ECCLESIA_RETURN_IF_ERROR(CheckIdentifier(identifier));
if (identifier.identifier_case() !=
milotic_hft::Identifier::kSensorIdentifier) {
return absl::InvalidArgumentError("Not a sensor identifier");
}
return AddSensor(identifier.sensor_identifier().name(),
{std::nullopt, max_batch_size});
}
absl::Status ResetSensor(const milotic_hft::Identifier& identifier) &
override {
ECCLESIA_RETURN_IF_ERROR(CheckIdentifier(identifier));
if (identifier.identifier_case() !=
milotic_hft::Identifier::kSensorIdentifier) {
return absl::InvalidArgumentError("Not a sensor identifier");
}
return AddSensor(identifier.sensor_identifier().name(),
{kResetMonitoring, 1});
}
absl::Status Apply() && override {
std::move(*monitoring_change_).Apply();
return absl::OkStatus();
}
private:
absl::Status AddSensor(std::string_view key,
::milotic_tlbmc::Mutation&& mutation) {
return monitoring_change_->AddSensor(key, std::move(mutation))
? absl::OkStatus()
: absl::InvalidArgumentError(absl::Substitute(
"Failed to add sensor $0 to monitoring change", key));
}
absl::Status CheckIdentifier(const milotic_hft::Identifier& identifier) {
if (identifier.identifier_case() !=
milotic_hft::Identifier::kSensorIdentifier) {
return absl::InvalidArgumentError("Not a sensor identifier");
}
return milotic_hft::SensorMutationBatch::CheckIdentifier(identifier);
}
std::unique_ptr<MonitoringChangeBase> monitoring_change_;
};
} // namespace
milotic_hft::SensorUnits ConvertToHftUnit(SensorUnit unit) {
switch (unit) {
case UNIT_DEGREE_CELSIUS:
return milotic_hft::SENSOR_UNIT_DEGREES;
case UNIT_WATT:
return milotic_hft::SENSOR_UNIT_WATTS;
case UNIT_AMPERE:
return milotic_hft::SENSOR_UNIT_AMPS;
case UNIT_VOLT:
return milotic_hft::SENSOR_UNIT_VOLTS;
case UNIT_REVOLUTION_PER_MINUTE:
return milotic_hft::SENSOR_UNIT_RPM;
case UNIT_PERCENT:
return milotic_hft::SENSOR_UNIT_PERCENT;
case UNIT_COUNT:
return milotic_hft::SENSOR_UNIT_COUNT;
case UNIT_JOULES:
return milotic_hft::SENSOR_UNIT_JOULES;
case UNIT_PASCAL:
return milotic_hft::SENSOR_UNIT_PASCAL;
default:
return milotic_hft::SENSOR_UNIT_UNSPECIFIED;
}
}
milotic_hft::SensorType ConvertToHftType(SensorUnit unit) {
switch (unit) {
case UNIT_DEGREE_CELSIUS:
return milotic_hft::SENSOR_TYPE_THERMAL;
case UNIT_WATT:
return milotic_hft::SENSOR_TYPE_POWER;
case UNIT_AMPERE:
return milotic_hft::SENSOR_TYPE_CURRENT;
case UNIT_VOLT:
return milotic_hft::SENSOR_TYPE_VOLTAGE;
case UNIT_REVOLUTION_PER_MINUTE:
return milotic_hft::SENSOR_TYPE_FANTACH;
case UNIT_PERCENT:
return milotic_hft::SENSOR_TYPE_DUTYCYCLE;
case UNIT_COUNT:
return milotic_hft::SENSOR_TYPE_COUNTER;
case UNIT_JOULES:
return milotic_hft::SENSOR_TYPE_ENERGY;
case UNIT_PASCAL:
return milotic_hft::SENSOR_TYPE_ALTITUDE;
default:
return milotic_hft::SENSOR_TYPE_UNSPECIFIED;
}
}
milotic_hft::Status ConvertToHftStatus(milotic_tlbmc::Status status) {
// Translate the tlBMC status to the corresponding HFT status.
// This should be updated to handle new HFT statuses as needed.
switch (status) {
case milotic_tlbmc::STATUS_READY:
return milotic_hft::STATUS_OK;
case milotic_tlbmc::STATUS_STALE:
return milotic_hft::STATUS_STALE;
case milotic_tlbmc::STATUS_CREATION_FAILED:
case milotic_tlbmc::STATUS_CREATION_PENDING:
return milotic_hft::STATUS_MISSING;
default:
return milotic_hft::STATUS_UNKNOWN;
}
}
absl::StatusOr<std::unique_ptr<StoreHftAdapter>> StoreHftAdapter::Create(
Store* store) {
return absl::WrapUnique(new StoreHftAdapter(store));
}
// Note: sensor mutation is handled by StoreSensorMutationBatch.
absl::Status StoreHftAdapter::ConfigureSamplingInterval(
const milotic_hft::Identifier& identifier, int sampling_interval_ms) {
LOG(INFO) << "Configure called for identifier: " << identifier;
switch (identifier.identifier_case()) {
case milotic_hft::Identifier::kFruIdentifier:
if (!IsGetAllFrus(identifier)) {
return absl::InvalidArgumentError(absl::Substitute(
"Identifier $0 is not supported. Only "
"CONFIGURATION_TYPE_CONFIGURE_ALL_RESOURCES is supported for FRUs",
identifier.identifier_case()));
}
ECCLESIA_RETURN_IF_ERROR(store_->ConfigureDeterministicCollection(
{.sampling_interval_ms = sampling_interval_ms}));
break;
// Add support for other telemetry types.
default:
return absl::InvalidArgumentError(absl::Substitute(
"Identifier $0 is not supported", identifier.identifier_case()));
}
return absl::OkStatus();
}
absl::Status StoreHftAdapter::ResetSamplingIntervalToDefault(
const milotic_hft::Identifier& identifier) {
switch (identifier.identifier_case()) {
case milotic_hft::Identifier::kFruIdentifier:
if (!IsGetAllFrus(identifier)) {
return absl::InvalidArgumentError(absl::Substitute(
"Identifier $0 is not supported. Only "
"CONFIGURATION_TYPE_CONFIGURE_ALL_RESOURCES is supported for FRUs",
identifier.identifier_case()));
}
return store_->ConfigureDeterministicCollection(
{.sampling_interval_ms = 0});
// Add support for other telemetry types.
default:
return absl::InvalidArgumentError(absl::Substitute(
"Identifier $0 is not supported", identifier.identifier_case()));
}
}
absl::StatusOr<milotic_hft::Payload> StoreHftAdapter::Collect(
const milotic_hft::Identifier& identifier, absl::Time start_time) const {
switch (identifier.identifier_case()) {
case milotic_hft::Identifier::kSensorIdentifier:
return CollectSensorData(identifier.sensor_identifier(), store_,
start_time);
case milotic_hft::Identifier::kFruIdentifier:
if (!IsGetAllFrus(identifier)) {
return absl::InvalidArgumentError(absl::Substitute(
"Identifier $0 is not supported. Only "
"CONFIGURATION_TYPE_CONFIGURE_ALL_RESOURCES is supported for FRUs",
identifier.identifier_case()));
}
return CollectFruData(store_);
// Add support for other telemetry types.
default:
return absl::InvalidArgumentError("Identifier is not supported");
}
}
std::unique_ptr<milotic_hft::SensorMutationBatch>
StoreHftAdapter::CreateSensorMutationBatch(
milotic_hft::SubscriptionPolicy::ResourceType resource_type,
std::vector<milotic_hft::Identifier> identifiers) {
if (resource_type == milotic_hft::SubscriptionPolicy::RESOURCE_TYPE_SENSOR) {
return std::make_unique<StoreSensorMutationBatch>(
this, identifiers, store_->CreateMonitoringChange());
}
return milotic_hft::DataSource::CreateSensorMutationBatch(
resource_type, std::move(identifiers));
}
absl::StatusOr<std::vector<milotic_hft::Identifier>>
StoreHftAdapter::GetIdentifiersForResourceType(
const milotic_hft::SubscriptionPolicy::ResourceType& resource_type) const {
switch (resource_type) {
case milotic_hft::SubscriptionPolicy::RESOURCE_TYPE_SENSOR:
return GetSensorIdentifiers(store_);
case milotic_hft::SubscriptionPolicy::RESOURCE_TYPE_FRU: {
std::vector<milotic_hft::Identifier> identifiers;
milotic_hft::Identifier identifier;
identifier.mutable_fru_identifier()->set_barepath(kAllFrusIdentifier);
identifiers.push_back(identifier);
return identifiers;
}
default:
return absl::InvalidArgumentError("Resource type is not supported");
}
}
absl::Status StoreHftAdapter::ConfigureBatchSize(
const milotic_hft::Identifier& identifier, int max_batch_size) {
switch (identifier.identifier_case()) {
case milotic_hft::Identifier::kFruIdentifier:
return absl::OkStatus();
default:
return absl::InvalidArgumentError(absl::Substitute(
"Identifier $0 is not supported", identifier.identifier_case()));
}
}
absl::Status StoreHftAdapter::ResetBatchSizeToDefault(
const milotic_hft::Identifier& identifier) {
switch (identifier.identifier_case()) {
case milotic_hft::Identifier::kFruIdentifier:
return absl::OkStatus();
default:
return absl::InvalidArgumentError(absl::Substitute(
"Identifier $0 is not supported", identifier.identifier_case()));
}
}
} // namespace milotic_tlbmc