blob: c9e40c98882e6acd8647008984ebf39b5722cf43 [file] [log] [blame]
#include "tlbmc/redfish/routes/chassis.h"
#include <algorithm>
#include <functional>
#include <string>
#include <utility>
#include <vector>
#include "absl/functional/bind_front.h"
#include "absl/log/log.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "nlohmann/json.hpp"
#include "nlohmann/json_fwd.hpp"
#include "topology_config.pb.h"
#include "tlbmc/redfish/app.h"
#include "tlbmc/redfish/data/stable_id.h"
#include "stable_id.pb.h"
#include "tlbmc/redfish/request.h"
#include "tlbmc/redfish/response.h"
#include "tlbmc/redfish/url.h"
#include "fru.pb.h"
#include "resource.pb.h"
#include "tlbmc/store/store.h"
#include "google/protobuf/repeated_ptr_field.h"
namespace milotic_tlbmc::chassis {
namespace {
void HandleChassisCollection(const RedfishApp& app, const RedfishRequest& req,
RedfishResponse& resp) {
resp.SetKeyInJsonBody("/@odata.id", "/redfish/v1/Chassis");
resp.SetKeyInJsonBody("/@odata.type", "#ChassisCollection.ChassisCollection");
resp.SetKeyInJsonBody("/Name", "Chassis Collection");
const Store& store = *app.GetStore();
absl::StatusOr<std::vector<std::string>> config_names =
store.GetAllConfigNames();
if (!config_names.ok()) {
resp.SetToAbslStatus(config_names.status());
return;
}
resp.SetKeyInJsonBody("/Members@odata.count", config_names->size());
nlohmann::json::array_t members;
for (const auto& config : *config_names) {
nlohmann::json::object_t member;
member["@odata.id"] = CreateUrl({"redfish", "v1", "Chassis", config});
members.push_back(std::move(member));
}
std::sort(members.begin(), members.end());
resp.SetKeyInJsonBody("/Members", members);
}
void HandleChassis(const RedfishApp& app, const RedfishRequest& req,
RedfishResponse& resp, const std::string& chassis_id) {
const Store& store = *app.GetStore();
nlohmann::json::json_pointer chassis_pointer("");
// Chassis id corresponds to the config name.
absl::StatusOr<std::string> fru_key = store.GetFruKeyByConfigName(chassis_id);
if (!fru_key.ok()) {
resp.SetToAbslStatus(fru_key.status());
return;
}
absl::StatusOr<const Fru*> fru = store.GetFru(*fru_key);
if (!fru.ok()) {
resp.SetToAbslStatus(fru.status());
return;
}
absl::StatusOr<const TopologyConfigNode*> topology_config_node =
store.GetFruTopology(chassis_id);
if (!topology_config_node.ok()) {
resp.SetToAbslStatus(topology_config_node.status());
return;
}
FillResponseWithFruData(chassis_pointer, *fru, *topology_config_node, resp);
}
inline std::string GetSystemId() { return "system"; }
nlohmann::json::array_t CreateChildResourceLinksArray(
const ::google::protobuf::RepeatedPtrField<std::string>& child_resource_ids,
ResourceType resource_type) {
nlohmann::json::array_t child_resources;
for (const auto& id : child_resource_ids) {
nlohmann::json::object_t child_object;
switch (resource_type) {
case RESOURCE_TYPE_BOARD:
child_object["@odata.id"] = CreateUrl({"redfish", "v1", "Chassis", id});
break;
case RESOURCE_TYPE_CABLE:
child_object["@odata.id"] = CreateUrl({"redfish", "v1", "Cables", id});
break;
case RESOURCE_TYPE_PROCESSOR:
child_object["@odata.id"] = CreateUrl(
{"redfish", "v1", "Systems", GetSystemId(), "Processors", id});
break;
case RESOURCE_TYPE_STORAGE:
child_object["@odata.id"] = CreateUrl(
{"redfish", "v1", "Systems", GetSystemId(), "Storage", id});
break;
default:
break;
}
child_resources.emplace_back(child_object);
}
return child_resources;
}
} // namespace
void FillResponseWithFruData(
const nlohmann::json::json_pointer& chassis_pointer, const Fru* fru_ptr,
const TopologyConfigNode* topology_config_node_ptr, RedfishResponse& resp) {
if (fru_ptr->attributes().status() != STATUS_READY) {
resp.SetToNotReady();
return;
}
const std::string& chassis_id = topology_config_node_ptr->name();
resp.SetKeyInJsonBody(chassis_pointer / "@odata.id",
"/redfish/v1/Chassis/" + chassis_id);
resp.SetKeyInJsonBody(chassis_pointer / "@odata.type",
"#Chassis.v1_17_0.Chassis");
resp.SetKeyInJsonBody(
chassis_pointer / "Assembly" / "@odata.id",
CreateUrl({"redfish", "v1", "Chassis", chassis_id, "Assembly"}));
resp.SetKeyInJsonBody(
chassis_pointer / "Certificates" / "@odata.id",
CreateUrl({"redfish", "v1", "Chassis", chassis_id, "Certificates"}));
resp.SetKeyInJsonBody(
chassis_pointer / "TrustedComponents" / "@odata.id",
milotic_tlbmc::CreateUrl(
{"redfish", "v1", "Chassis", chassis_id, "TrustedComponents"}));
absl::string_view chassis_type;
switch (fru_ptr->attributes().chassis_properties().chassis_type()) {
case CHASSIS_TYPE_RACK_MOUNT:
chassis_type = "RackMount";
break;
case CHASSIS_TYPE_MODULE:
chassis_type = "Module";
break;
case CHASSIS_TYPE_STORAGE_ENCLOSURE:
chassis_type = "StorageEnclosure";
break;
case CHASSIS_TYPE_COMPONENT:
chassis_type = "Component";
break;
case CHASSIS_TYPE_STANDALONE:
chassis_type = "StandAlone";
break;
default:
break;
}
resp.SetKeyInJsonBody(chassis_pointer / "ChassisType", chassis_type);
if (fru_ptr->attributes().chassis_properties().bmcnet()) {
resp.SetKeyInJsonBody(
chassis_pointer / "NetworkAdapters" / "@odata.id",
milotic_tlbmc::CreateUrl(
{"redfish", "v1", "Chassis", chassis_id, "NetworkAdapters"}));
}
// Support Chassis Reset Action only for Root Chassis.
if (topology_config_node_ptr->location_context().devpath() == "/phys" ||
topology_config_node_ptr->location_context().devpath() ==
absl::StrCat(
"/", topology_config_node_ptr->root_chassis_location_code())) {
resp.SetKeyInJsonBody(
chassis_pointer / "Actions" / "#Chassis.Reset" / "target",
CreateUrl({"redfish", "v1", "Chassis", chassis_id, "Actions",
"Chassis.Reset"}));
resp.SetKeyInJsonBody(
chassis_pointer / "Actions" / "#Chassis.Reset" / "@Redfish.ActionInfo",
CreateUrl({"redfish", "v1", "Chassis", chassis_id, "ResetActionInfo"}));
}
resp.SetKeyInJsonBody(
chassis_pointer / "Drives" / "@odata.id",
CreateUrl({"redfish", "v1", "Chassis", chassis_id, "Drives"}));
resp.SetKeyInJsonBody(
chassis_pointer / "Memory" / "@odata.id",
CreateUrl({"redfish", "v1", "Systems", GetSystemId(), "Memory"}));
resp.SetKeyInJsonBody(chassis_pointer / "Id", chassis_id);
resp.SetKeyInJsonBody(chassis_pointer / "Name", chassis_id);
resp.SetKeyInJsonBody(
chassis_pointer / "Sensors" / "@odata.id",
CreateUrl({"redfish", "v1", "Chassis", chassis_id, "Sensors"}));
resp.SetKeyInJsonBody(
chassis_pointer / "Thermal" / "@odata.id",
CreateUrl({"redfish", "v1", "Chassis", chassis_id, "Thermal"}));
resp.SetKeyInJsonBody(
chassis_pointer / "Power" / "@odata.id",
CreateUrl({"redfish", "v1", "Chassis", chassis_id, "Power"}));
resp.SetKeyInJsonBody(
chassis_pointer / "ThermalSubsystem" / "@odata.id",
CreateUrl({"redfish", "v1", "Chassis", chassis_id, "ThermalSubsystem"}));
resp.SetKeyInJsonBody(
chassis_pointer / "PowerSubsystem" / "@odata.id",
CreateUrl({"redfish", "v1", "Chassis", chassis_id, "PowerSubsystem"}));
resp.SetKeyInJsonBody(chassis_pointer / "EnvironmentMetrics" / "@odata.id",
CreateUrl({"redfish", "v1", "Chassis", chassis_id,
"EnvironmentMetrics"}));
resp.SetKeyInJsonBody(chassis_pointer / "PowerState", "On");
resp.SetKeyInJsonBody(chassis_pointer / "PCIeDevices" / "@odata.id",
"/redfish/v1/Systems/system/PCIeDevices");
resp.SetKeyInJsonBody(
chassis_pointer / "PCIeSlots" / "@odata.id",
CreateUrl({"redfish", "v1", "Chassis", chassis_id, "PCIeSlots"}));
if (fru_ptr->data().has_asset_info()) {
const AssetInfo& asset_info = fru_ptr->data().asset_info();
if (!asset_info.manufacturer().empty()) {
resp.SetKeyInJsonBody(chassis_pointer / "Manufacturer",
asset_info.manufacturer());
}
if (!asset_info.product_name().empty()) {
resp.SetKeyInJsonBody(chassis_pointer / "Model",
asset_info.product_name());
}
if (!asset_info.serial_number().empty()) {
resp.SetKeyInJsonBody(chassis_pointer / "SerialNumber",
asset_info.serial_number());
}
if (!asset_info.part_number().empty()) {
resp.SetKeyInJsonBody(chassis_pointer / "PartNumber",
asset_info.part_number());
}
}
if (topology_config_node_ptr->has_location_context()) {
StableId stable_id = GetStableId(*topology_config_node_ptr);
if (!stable_id.service_label().empty()) {
resp.SetKeyInJsonBody(
chassis_pointer / "Location" / "PartLocation" / "ServiceLabel",
stable_id.service_label());
if (stable_id.has_location_type()) {
absl::string_view location_type;
switch (stable_id.location_type()) {
case PART_LOCATION_TYPE_BACKPLANE:
location_type = "Backplane";
break;
case PART_LOCATION_TYPE_BAY:
location_type = "Bay";
break;
case PART_LOCATION_TYPE_EMBEDDED:
location_type = "Embedded";
break;
case PART_LOCATION_TYPE_SLOT:
location_type = "Slot";
break;
case PART_LOCATION_TYPE_SOCKET:
location_type = "Socket";
break;
default:
break;
}
if (!location_type.empty()) {
resp.SetKeyInJsonBody(
chassis_pointer / "Location" / "PartLocation" / "LocationType",
location_type);
}
}
}
if (!stable_id.part_location_context().empty() &&
stable_id.part_location_context() != "phys") {
resp.SetKeyInJsonBody(
chassis_pointer / "Location" / "PartLocationContext",
stable_id.part_location_context());
}
if (stable_id.has_embedded_location_context()) {
resp.SetKeyInJsonBody(chassis_pointer / "Location" / "Oem" / "Google" /
"EmbeddedLocationContext",
stable_id.embedded_location_context());
}
// Populate special devpath field for embedded chassis.
if (stable_id.location_type() ==
PART_LOCATION_TYPE_EMBEDDED) {
resp.SetKeyInJsonBody(
chassis_pointer / "Location" / "Oem" / "Google" /
"Devpath",
stable_id.machine_local_devpath());
}
}
nlohmann::json::array_t child_chassis = CreateChildResourceLinksArray(
topology_config_node_ptr->children_chassis_ids(), RESOURCE_TYPE_BOARD);
if (!child_chassis.empty()) {
resp.SetKeyInJsonBody(chassis_pointer / "Links" / "Contains",
child_chassis);
resp.SetKeyInJsonBody(chassis_pointer / "Links" / "Contains@odata.count",
child_chassis.size());
}
nlohmann::json::array_t child_cables = CreateChildResourceLinksArray(
topology_config_node_ptr->children_cable_ids(), RESOURCE_TYPE_CABLE);
if (!child_cables.empty()) {
resp.SetKeyInJsonBody(chassis_pointer / "Links" / "Cables", child_cables);
resp.SetKeyInJsonBody(chassis_pointer / "Links" / "Cables@odata.count",
child_cables.size());
}
nlohmann::json::array_t child_processors = CreateChildResourceLinksArray(
topology_config_node_ptr->children_processor_ids(),
RESOURCE_TYPE_PROCESSOR);
if (!child_processors.empty()) {
resp.SetKeyInJsonBody(chassis_pointer / "Links" / "Processors",
child_processors);
resp.SetKeyInJsonBody(chassis_pointer / "Links" / "Processors@odata.count",
child_processors.size());
}
nlohmann::json::array_t child_storages = CreateChildResourceLinksArray(
topology_config_node_ptr->children_storage_ids(), RESOURCE_TYPE_STORAGE);
if (!child_storages.empty()) {
resp.SetKeyInJsonBody(chassis_pointer / "Links" / "Storage",
child_storages);
resp.SetKeyInJsonBody(chassis_pointer / "Links" / "Storage@odata.count",
child_storages.size());
}
if (topology_config_node_ptr->has_parent_resource_id()) {
resp.SetKeyInJsonBody(
chassis_pointer / "Links" / "ContainedBy" / "@odata.id",
CreateUrl({"redfish", "v1", "Chassis",
topology_config_node_ptr->parent_resource_id()}));
}
nlohmann::json::array_t managed_by;
nlohmann::json::object_t manager;
manager["@odata.id"] = "/redfish/v1/Managers/bmc";
managed_by.emplace_back(manager);
resp.SetKeyInJsonBody(chassis_pointer / "Links" / "ManagedBy", managed_by);
// TODO(davtang): Add support for multihost systems
nlohmann::json::array_t systems;
nlohmann::json::object_t system;
system["@odata.id"] = "/redfish/v1/Systems/system";
systems.emplace_back(system);
resp.SetKeyInJsonBody(chassis_pointer / "Links" / "ComputerSystems", systems);
resp.SetKeyInJsonBody(chassis_pointer / "Status" / "Health", "OK");
resp.SetKeyInJsonBody(chassis_pointer / "Status" / "HealthRollup", "OK");
resp.SetKeyInJsonBody(chassis_pointer / "Status" / "State", "Enabled");
}
void RegisterRoutes(RedfishApp& app) {
TLBMC_ROUTE(app, "/redfish/v1/Chassis/")
.methods(boost::beast::http::verb::get)(
absl::bind_front(HandleChassisCollection, std::cref(app)));
TLBMC_ROUTE(app, "/redfish/v1/Chassis/<str>/")
.methods(boost::beast::http::verb::get)(
absl::bind_front(HandleChassis, std::cref(app)));
}
} // namespace milotic_tlbmc::chassis