| #include "tlbmc/redfish/routes/sensor.h" |
| |
| #include <algorithm> |
| #include <cstdint> |
| #include <functional> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/functional/bind_front.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/string_view.h" |
| #include "absl/strings/substitute.h" |
| #include "nlohmann/json.hpp" |
| #include "entity_common_config.pb.h" |
| #include "reading_range_config.pb.h" |
| #include "threshold_config.pb.h" |
| #include "tlbmc/redfish/app.h" |
| #include "tlbmc/redfish/request.h" |
| #include "tlbmc/redfish/response.h" |
| #include "tlbmc/redfish/url.h" |
| #include "resource.pb.h" |
| #include "sensor.pb.h" |
| #include "tlbmc/sensors/sensor.h" |
| #include "tlbmc/store/store.h" |
| |
| namespace milotic_tlbmc::sensor { |
| |
| namespace { |
| |
| void HandleSensorCollection(const RedfishApp& app, const RedfishRequest& req, |
| RedfishResponse& resp, |
| const std::string& chassis_id) { |
| std::string sensor_collection_url = |
| absl::Substitute("/redfish/v1/Chassis/$0/Sensors", chassis_id); |
| resp.SetKeyInJsonBody("/@odata.id", sensor_collection_url); |
| resp.SetKeyInJsonBody("/@odata.type", "#SensorCollection.SensorCollection"); |
| resp.SetKeyInJsonBody("/Name", "Sensors"); |
| resp.SetKeyInJsonBody("/Description", |
| "Collection of Sensors for this Chassis"); |
| |
| const Store& store = *app.GetStore(); |
| |
| std::vector<std::string> sensor_keys = |
| store.GetAllSensorKeysByConfigName(chassis_id); |
| std::sort(sensor_keys.begin(), sensor_keys.end()); |
| nlohmann::json::array_t members; |
| for (const auto& key : sensor_keys) { |
| nlohmann::json::object_t member; |
| member["@odata.id"] = absl::StrCat(sensor_collection_url, "/", key); |
| members.push_back(std::move(member)); |
| } |
| |
| resp.SetKeyInJsonBody("/Members@odata.count", members.size()); |
| resp.SetKeyInJsonBody("/Members", members); |
| } |
| |
| void HandleSensor(const RedfishApp& app, const RedfishRequest& req, |
| RedfishResponse& resp, const std::string& chassis_id, |
| const std::string& sensor_id) { |
| const Store& store = *app.GetStore(); |
| |
| std::shared_ptr<const Sensor> sensor = |
| store.GetSensorByConfigNameAndSensorKey(chassis_id, sensor_id); |
| if (sensor == nullptr) { |
| resp.SetToNotFound(); |
| return; |
| } |
| |
| nlohmann::json::json_pointer sensor_pointer(""); |
| FillResponseWithSensorData(sensor, sensor_pointer, resp); |
| } |
| |
| } // namespace |
| |
| void FillResponseWithSensorData( |
| const std::shared_ptr<const Sensor>& sensor, |
| const nlohmann::json::json_pointer& sensor_pointer, RedfishResponse& resp) { |
| if (sensor->GetSensorAttributesDynamic().state().status() != STATUS_READY) { |
| resp.SetToNotReady( |
| sensor->GetSensorAttributesDynamic().state().status_message()); |
| return; |
| } |
| const SensorAttributesStatic& attributes_static = |
| sensor->GetSensorAttributesStatic(); |
| std::string chassis_id = |
| attributes_static.entity_common_config().board_config_name(); |
| std::string sensor_url = |
| absl::Substitute("/redfish/v1/Chassis/$0/Sensors/$1", chassis_id, |
| attributes_static.attributes().key()); |
| resp.SetKeyInJsonBody(sensor_pointer / "@odata.id", sensor_url); |
| resp.SetKeyInJsonBody(sensor_pointer / "@odata.type", |
| "#Sensor.v1_2_0.Sensor"); |
| std::string sensor_name = |
| GetTrimmedSensorName(attributes_static.attributes().key()); |
| resp.SetKeyInJsonBody(sensor_pointer / "Name", sensor_name); |
| resp.SetKeyInJsonBody(sensor_pointer / "Id", |
| attributes_static.attributes().key()); |
| resp.SetKeyInJsonBody(sensor_pointer / "Description", "Sensor"); |
| resp.SetKeyInJsonBody(sensor_pointer / "Reading", |
| sensor->GetSensorData()->reading()); |
| |
| if (!attributes_static.entity_common_config().related_item().id().empty()) { |
| nlohmann::json::array_t related_items; |
| nlohmann::json::object_t related_item; |
| |
| switch (attributes_static.entity_common_config().related_item().type()) { |
| case RESOURCE_TYPE_FAN: |
| related_item["@odata.id"] = CreateUrl( |
| {"redfish", "v1", "Chassis", chassis_id, "ThermalSubsystem", "Fans", |
| attributes_static.entity_common_config().related_item().id()}); |
| break; |
| case RESOURCE_TYPE_BOARD: |
| related_item["@odata.id"] = CreateUrl( |
| {"redfish", "v1", "Chassis", |
| attributes_static.entity_common_config().related_item().id()}); |
| break; |
| default: |
| break; |
| } |
| |
| related_items.emplace_back(std::move(related_item)); |
| resp.SetKeyInJsonBody(sensor_pointer / "RelatedItem", related_items); |
| } |
| |
| std::string reading_units; |
| std::string reading_type; |
| |
| // Reference: https://redfish.dmtf.org/schemas/v1/Sensor.v1_10_0.json |
| switch (attributes_static.unit()) { |
| case UNIT_UNKNOWN: |
| break; |
| case UNIT_DEGREE_CELSIUS: |
| reading_type = "Temperature"; |
| reading_units = "Cel"; |
| break; |
| case UNIT_VOLT: |
| reading_type = "Voltage"; |
| reading_units = "V"; |
| break; |
| case UNIT_WATT: |
| reading_type = "Power"; |
| reading_units = "W"; |
| break; |
| case UNIT_AMPERE: |
| reading_type = "Current"; |
| reading_units = "A"; |
| break; |
| case UNIT_REVOLUTION_PER_MINUTE: |
| reading_type = "Rotational"; |
| reading_units = "RPM"; |
| break; |
| case UNIT_PERCENT: |
| reading_type = "Percent"; |
| reading_units = "%"; |
| break; |
| default: |
| break; |
| } |
| if (!reading_type.empty()) { |
| resp.SetKeyInJsonBody(sensor_pointer / "ReadingType", reading_type); |
| } |
| if (!reading_units.empty()) { |
| resp.SetKeyInJsonBody(sensor_pointer / "ReadingUnits", reading_units); |
| } |
| resp.SetBodyToJson(); |
| if (attributes_static.has_reading_ranges()) { |
| for (const auto& reading_range : |
| attributes_static.reading_ranges().reading_range_configs()) { |
| if (reading_range.type() == READING_RANGE_TYPE_MAX) { |
| resp.SetKeyInJsonBody(sensor_pointer / "ReadingRangeMax", |
| reading_range.reading()); |
| } else if (reading_range.type() == READING_RANGE_TYPE_MIN) { |
| resp.SetKeyInJsonBody(sensor_pointer / "ReadingRangeMin", |
| reading_range.reading()); |
| } |
| } |
| } |
| |
| if (attributes_static.has_thresholds()) { |
| for (const auto& threshold : |
| attributes_static.thresholds().threshold_configs()) { |
| switch (threshold.type()) { |
| case THRESHOLD_TYPE_LOWER_CRITICAL: |
| resp.SetKeyInJsonBody( |
| sensor_pointer / "Thresholds" / "LowerCritical" / "Reading", |
| threshold.value()); |
| break; |
| case THRESHOLD_TYPE_LOWER_NON_CRITICAL: |
| resp.SetKeyInJsonBody( |
| sensor_pointer / "Thresholds" / "LowerCaution" / "Reading", |
| threshold.value()); |
| break; |
| case THRESHOLD_TYPE_UPPER_NON_CRITICAL: |
| resp.SetKeyInJsonBody( |
| sensor_pointer / "Thresholds" / "UpperCaution" / "Reading", |
| threshold.value()); |
| break; |
| case THRESHOLD_TYPE_UPPER_CRITICAL: |
| resp.SetKeyInJsonBody( |
| sensor_pointer / "Thresholds" / "UpperCritical" / "Reading", |
| threshold.value()); |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| |
| // At this point, sensor is healthy, a problematic sensor will return error |
| // early based on SensorAttributesDynamic state status. |
| resp.SetKeyInJsonBody(sensor_pointer / "Status" / "Health", "OK"); |
| resp.SetKeyInJsonBody(sensor_pointer / "Status" / "State", "Enabled"); |
| } |
| |
| void RegisterRoutes(RedfishApp& app) { |
| TLBMC_ROUTE(app, "/redfish/v1/Chassis/<str>/Sensors/") |
| .methods(boost::beast::http::verb::get)( |
| absl::bind_front(HandleSensorCollection, std::cref(app))); |
| TLBMC_ROUTE(app, "/redfish/v1/Chassis/<str>/Sensors/<str>/") |
| .methods(boost::beast::http::verb::get)( |
| absl::bind_front(HandleSensor, std::cref(app))); |
| } |
| |
| } // namespace milotic_tlbmc::sensor |