Refactor Fast-Sanity GetAllFrus and support state extraction.
This CL:
1. Update the logic of FRU extraction from hashmap-based to vector-based to improve the scalability, as we can build FRUs during the processing, and avoid out-sourcing GTL.
2. Support enabling state extraction for GetAllFrus RPC.
#tlbmc-fast-sanity
PiperOrigin-RevId: 783114704
Change-Id: I73c535696ba04fd324b0c6df56f76782a4e54207
diff --git a/tlbmc/service/fru_service.cc b/tlbmc/service/fru_service.cc
index 1e7c75d..f80ce21 100644
--- a/tlbmc/service/fru_service.cc
+++ b/tlbmc/service/fru_service.cc
@@ -8,7 +8,6 @@
#include <vector>
#include "absl/base/no_destructor.h"
-#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/log/log.h"
#include "absl/status/status.h"
@@ -43,6 +42,8 @@
using ::milotic::authz::GetValueAsJson;
using ::milotic::authz::GetValueAsString;
+using FruComponentStatus = FruComponent::Status;
+
constexpr absl::string_view kRootChassisUrl = "/redfish/v1/Chassis?$expand=.";
// TODO(b/415893570): Make this config-based.
// Be ware: BB has `"/redfish/v1/Systems/system1/Memory"` as the memory
@@ -129,7 +130,28 @@
return supported_location_types->contains(std::string(location_type_json));
}
-absl::StatusOr<Devpath> ExtractDevpathFromJson(
+FruComponentStatus ExtractFruStatus(const nlohmann::json &fru) {
+ if (!fru.contains("Status") || !fru["Status"].contains("State") ||
+ fru["Status"]["State"] == "Enabled") {
+ return FruComponent::STATUS_OK;
+ }
+ if (fru["Status"]["State"] == "Absent") {
+ return FruComponent::STATUS_NOT_FOUND;
+ }
+ return FruComponent::STATUS_OK;
+}
+
+FruComponent BuildFruComponent(absl::string_view devpath,
+ FruComponentStatus status) {
+ FruComponent fru;
+ fru.mutable_primary_identifier()->set_value(devpath);
+ fru.mutable_primary_identifier()->set_type(
+ UniqueIdentifierType::UNIQUE_IDENTIFIER_TYPE_LOCAL_DEVPATH);
+ fru.set_status(status);
+ return fru;
+}
+
+absl::StatusOr<FruComponent> ExtractFruComponentFromJson(
const nlohmann::json &json, absl::string_view root_chassis_location_code) {
ECCLESIA_RETURN_IF_ERROR(IsLocationInfoValid(json));
@@ -138,19 +160,23 @@
json["Location"]["PartLocation"].find("LocationType");
if (location_type_it == json["Location"]["PartLocation"].end() ||
!IsLocationTypeSupported(*location_type_it)) {
- return Devpath();
+ return absl::InvalidArgumentError(
+ "Invalid location info in the Redfish response.");
}
- // This is the root chassis. Return empty devpath.
+ FruComponentStatus status = ExtractFruStatus(json);
+
const auto &res_member_location = json["Location"];
std::string service_label =
res_member_location["PartLocation"]["ServiceLabel"];
- if (service_label == root_chassis_location_code) {
- return Devpath();
+ // This is the root chassis.
+ if (service_label == root_chassis_location_code ||
+ service_label == kLocalRootChassisLocationCode) {
+ return absl::InternalError("Root chassis FRU will be built separately.");
}
- // PartLocationContext is optional, and the prefix should be removed if it is
- // equal to the root chassis location code.
+ // PartLocationContext is optional, and the prefix should be removed if it
+ // is equal to the root chassis location code.
std::string part_location_context;
const auto part_location_context_it =
res_member_location.find("PartLocationContext");
@@ -173,87 +199,52 @@
kLocalRootChassisLocationCode.length());
}
- return Devpath(part_location_context, service_label);
+ return BuildFruComponent(
+ absl::StrFormat(
+ "/%s/%s%s", kLocalRootChassisLocationCode,
+ (!part_location_context.empty() ? part_location_context + "/" : ""),
+ service_label),
+ status);
}
-void InsertDevpath(const nlohmann::json &json,
- absl::string_view root_chassis_location_code,
- std::shared_ptr<PartLocationContextToServiceLabelSet>
- &part_location_context_to_service_label_set) {
- absl::StatusOr<Devpath> devpath_status =
- ExtractDevpathFromJson(json, root_chassis_location_code);
+void GenerateFruComponentsFromRedfishObject(
+ const nlohmann::json &json, absl::string_view root_chassis_location_code,
+ const std::shared_ptr<FruComponents> &fru_components) {
+ absl::StatusOr<FruComponent> fru_info_status =
+ ExtractFruComponentFromJson(json, root_chassis_location_code);
// TODO(b/422818770): Utilize the error status better.
// E.g., provide logs.
- if (!devpath_status.ok() || devpath_status.value().service_label.empty()) {
- return;
+ if (fru_info_status.ok()) {
+ fru_components->AddFruComponent(std::move(fru_info_status.value()));
}
-
- absl::MutexLock lock(&part_location_context_to_service_label_set->mutex);
- part_location_context_to_service_label_set
- ->part_location_context_to_service_label_set[devpath_status.value()
- .part_location_context]
- .insert(devpath_status.value().service_label);
-}
-
-void GenerateDevpathFromRedfishObject(
- const nlohmann::json &json, absl::string_view root_chassis_location_code,
- std::shared_ptr<PartLocationContextToServiceLabelSet>
- part_location_context_to_service_label_set) {
- InsertDevpath(json, root_chassis_location_code,
- part_location_context_to_service_label_set);
const auto assembly_it = json.find("Assembly");
if (assembly_it != json.end() &&
assembly_it->find("Assemblies") != json["Assembly"].end()) {
for (const auto &fru : (*assembly_it)["Assemblies"]) {
- GenerateDevpathFromRedfishObject(
- fru, root_chassis_location_code,
- part_location_context_to_service_label_set);
+ GenerateFruComponentsFromRedfishObject(fru, root_chassis_location_code,
+ fru_components);
}
}
if (json.contains("Members")) {
for (const auto &fru : json["Members"]) {
- GenerateDevpathFromRedfishObject(
- fru, root_chassis_location_code,
- part_location_context_to_service_label_set);
+ GenerateFruComponentsFromRedfishObject(fru, root_chassis_location_code,
+ fru_components);
}
}
}
void SendGetAllFruInfoResponse(
ServerUnaryReactor &reactor, GetAllFruInfoResponse &response,
- const std::shared_ptr<PartLocationContextToServiceLabelSet>
- &part_location_context_to_service_label_set) {
- // Build the devpath list
- // Insert the default local root chassis devpath first.
- std::vector<std::string> devpaths({"/phys"});
-
- {
- absl::MutexLock lock(&part_location_context_to_service_label_set->mutex);
- for (const auto &[part_location_context, service_label_set] :
- part_location_context_to_service_label_set
- ->part_location_context_to_service_label_set) {
- for (const auto &service_label : service_label_set) {
- devpaths.push_back(absl::StrCat(
- "/", kLocalRootChassisLocationCode, "/",
- (!part_location_context.empty() ? part_location_context + "/" : ""),
- service_label));
- }
- }
- }
-
- if (devpaths.empty()) {
+ const std::shared_ptr<FruComponents> &fru_components) {
+ absl::MutexLock lock(&fru_components->mutex);
+ if (fru_components->fru_components.empty()) {
reactor.Finish(::grpc::Status(::grpc::StatusCode::INTERNAL,
"No matched devpath found."));
}
-
- for (const std::string &devpath : devpaths) {
- if (devpath.empty()) {
+ for (FruComponent &fru : fru_components->fru_components) {
+ if (fru.primary_identifier().value().empty()) {
continue;
}
- FruComponent fru;
- fru.mutable_primary_identifier()->set_value(devpath);
- fru.mutable_primary_identifier()->set_type(
- UniqueIdentifierType::UNIQUE_IDENTIFIER_TYPE_LOCAL_DEVPATH);
response.mutable_fru_components()->Add(std::move(fru));
}
reactor.Finish(::grpc::Status(::grpc::StatusCode::OK, "Devpath list sent."));
@@ -364,9 +355,15 @@
ServerUnaryReactor &reactor, const GetAllFruInfoRequest &request,
GetAllFruInfoResponse &response,
absl::string_view root_chassis_location_code) {
- std::shared_ptr<PartLocationContextToServiceLabelSet>
- part_location_context_to_service_label_set =
- std::make_shared<PartLocationContextToServiceLabelSet>();
+ auto fru_components = std::make_shared<FruComponents>();
+
+ // The root chassis is successfully extracted. Need to explicitly add its
+ // corresponding FRU into the list, as some platforms do not have a root
+ // chassis location code to be identified as a devpath.
+ FruComponent root_fru_component =
+ BuildFruComponent(absl::StrCat("/", kLocalRootChassisLocationCode),
+ FruComponent::STATUS_OK);
+ fru_components->AddFruComponent(std::move(root_fru_component));
std::shared_ptr<OngoingDevpathExtractionRequestCount>
ongoing_devpath_extraction_request_count =
@@ -388,13 +385,12 @@
// Extract devpaths from the rest of the requests.
async_devpath_resp->res.setCompleteRequestHandler(
- [&reactor, &response, url, part_location_context_to_service_label_set,
+ [&reactor, &response, url, fru_components,
root_chassis_location_code = std::string(root_chassis_location_code),
ongoing_devpath_extraction_request_count](crow::Response &res) {
if (res.stringResponse.has_value()) {
- GenerateDevpathFromRedfishObject(
- res.jsonValue, root_chassis_location_code,
- part_location_context_to_service_label_set);
+ GenerateFruComponentsFromRedfishObject(
+ res.jsonValue, root_chassis_location_code, fru_components);
} else {
LOG(WARNING) << "No valid response from redfish for url: `" << url
<< "`";
@@ -407,8 +403,7 @@
&ongoing_devpath_extraction_request_count->mutex);
if (ongoing_devpath_extraction_request_count
->ongoing_devpath_extraction_request_count == 0) {
- SendGetAllFruInfoResponse(
- reactor, response, part_location_context_to_service_label_set);
+ SendGetAllFruInfoResponse(reactor, response, fru_components);
}
});
app_->handle(*devpath_request_status, async_devpath_resp);
diff --git a/tlbmc/service/fru_service.h b/tlbmc/service/fru_service.h
index 91a589e..4a2bdc5 100644
--- a/tlbmc/service/fru_service.h
+++ b/tlbmc/service/fru_service.h
@@ -2,17 +2,17 @@
#define THIRD_PARTY_MILOTIC_EXTERNAL_CC_FAST_SANITY_SERVICE_FRU_SERVICE_H_
#include <cstdint>
-#include <string>
+#include <utility>
+#include <vector>
#include "absl/base/thread_annotations.h"
-#include "absl/container/flat_hash_map.h"
-#include "absl/container/flat_hash_set.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "grpcpp/security/auth_context.h"
#include "grpcpp/server_context.h"
#include "grpcpp/support/server_callback.h"
#include "grpcpp/support/status.h"
+#include "fru_component_model.pb.h"
#include "fru_service.grpc.pb.h"
#include "app.hpp"
#include "dbus_utility.hpp" // NOLINT
@@ -28,22 +28,14 @@
using FruService = ::milotic_fast_sanity::FruService;
#endif
-struct Devpath {
- std::string part_location_context;
- std::string service_label;
-
- Devpath() = default;
-
- Devpath(absl::string_view part_location_context,
- absl::string_view service_label)
- : part_location_context(part_location_context),
- service_label(service_label) {}
-};
-
-struct PartLocationContextToServiceLabelSet {
+struct FruComponents {
absl::Mutex mutex;
- absl::flat_hash_map<std::string, absl::flat_hash_set<std::string>>
- part_location_context_to_service_label_set ABSL_GUARDED_BY(mutex);
+ std::vector<FruComponent> fru_components ABSL_GUARDED_BY(mutex);
+
+ void AddFruComponent(FruComponent&& fru_component) {
+ absl::MutexLock lock(&mutex);
+ fru_components.push_back(std::move(fru_component));
+ }
};
struct FruServiceOptions {