blob: f5ad839f7d50d6d37c4483161fb66e4560698d5b [file] [log] [blame]
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
Anthony Wilson95a3eca2019-06-11 10:44:47 -050018#include "node.hpp"
19
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010020#include <boost/algorithm/string/predicate.hpp>
21#include <boost/algorithm/string/split.hpp>
22#include <boost/container/flat_map.hpp>
23#include <boost/range/algorithm/replace_copy_if.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070024#include <dbus_singleton.hpp>
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +053025#include <utils/json_utils.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050026
27#include <cmath>
Ed Tanousabf2add2019-01-22 16:40:12 -080028#include <variant>
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010029
Ed Tanous1abe55e2018-09-05 08:30:59 -070030namespace redfish
31{
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010032
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010033using GetSubTreeType = std::vector<
34 std::pair<std::string,
35 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
36
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -050037using SensorVariant =
38 std::variant<int64_t, double, uint32_t, bool, std::string>;
Ed Tanousaa2e59c2018-04-12 12:17:20 -070039
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010040using ManagedObjectsVectorType = std::vector<std::pair<
Ed Tanousaa2e59c2018-04-12 12:17:20 -070041 sdbusplus::message::object_path,
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010042 boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -070043 std::string, boost::container::flat_map<std::string, SensorVariant>>>>;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010044
45/**
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020046 * SensorsAsyncResp
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010047 * Gathers data needed for response processing after async calls are done
48 */
Ed Tanous1abe55e2018-09-05 08:30:59 -070049class SensorsAsyncResp
50{
51 public:
Ed Tanous271584a2019-07-09 16:24:22 -070052 SensorsAsyncResp(crow::Response& response, const std::string& chassisIdIn,
53 const std::vector<const char*> typesIn,
Ed Tanous2474adf2018-09-05 16:31:16 -070054 const std::string& subNode) :
Ed Tanous43b761d2019-02-13 20:10:56 -080055 res(response),
Ed Tanous271584a2019-07-09 16:24:22 -070056 chassisId(chassisIdIn), types(typesIn), chassisSubNode(subNode)
Gunnar Mills1214b7e2020-06-04 10:11:30 -050057 {}
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020058
Ed Tanous1abe55e2018-09-05 08:30:59 -070059 ~SensorsAsyncResp()
60 {
61 if (res.result() == boost::beast::http::status::internal_server_error)
62 {
63 // Reset the json object to clear out any data that made it in
64 // before the error happened todo(ed) handle error condition with
65 // proper code
66 res.jsonValue = nlohmann::json::object();
67 }
68 res.end();
69 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010070
Ed Tanous1abe55e2018-09-05 08:30:59 -070071 crow::Response& res;
72 std::string chassisId{};
73 const std::vector<const char*> types;
Ed Tanous2474adf2018-09-05 16:31:16 -070074 std::string chassisSubNode{};
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010075};
76
77/**
Anthony Wilsond5005492019-07-31 16:34:17 -050078 * Possible states for physical inventory leds
79 */
80enum class LedState
81{
82 OFF,
83 ON,
84 BLINK,
85 UNKNOWN
86};
87
88/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -050089 * D-Bus inventory item associated with one or more sensors.
90 */
91class InventoryItem
92{
93 public:
94 InventoryItem(const std::string& objPath) :
95 objectPath(objPath), name(), isPresent(true), isFunctional(true),
Gunnar Mills42cbe532019-08-15 15:26:54 -050096 isPowerSupply(false), powerSupplyEfficiencyPercent(-1), manufacturer(),
97 model(), partNumber(), serialNumber(), sensors(), ledObjectPath(""),
Anthony Wilsond5005492019-07-31 16:34:17 -050098 ledState(LedState::UNKNOWN)
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -050099 {
100 // Set inventory item name to last node of object path
101 auto pos = objectPath.rfind('/');
102 if ((pos != std::string::npos) && ((pos + 1) < objectPath.size()))
103 {
104 name = objectPath.substr(pos + 1);
105 }
106 }
107
108 std::string objectPath;
109 std::string name;
110 bool isPresent;
111 bool isFunctional;
112 bool isPowerSupply;
Gunnar Mills42cbe532019-08-15 15:26:54 -0500113 int powerSupplyEfficiencyPercent;
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500114 std::string manufacturer;
115 std::string model;
116 std::string partNumber;
117 std::string serialNumber;
118 std::set<std::string> sensors;
Anthony Wilsond5005492019-07-31 16:34:17 -0500119 std::string ledObjectPath;
120 LedState ledState;
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500121};
122
123/**
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530124 * @brief Get objects with connection necessary for sensors
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200125 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100126 * @param sensorNames Sensors retrieved from chassis
127 * @param callback Callback for processing gathered connections
128 */
129template <typename Callback>
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530130void getObjectsWithConnection(
131 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700132 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530133 Callback&& callback)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700134{
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530135 BMCWEB_LOG_DEBUG << "getObjectsWithConnection enter";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700136 const std::string path = "/xyz/openbmc_project/sensors";
137 const std::array<std::string, 1> interfaces = {
138 "xyz.openbmc_project.Sensor.Value"};
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100139
Ed Tanous1abe55e2018-09-05 08:30:59 -0700140 // Response handler for parsing objects subtree
141 auto respHandler = [callback{std::move(callback)}, SensorsAsyncResp,
142 sensorNames](const boost::system::error_code ec,
143 const GetSubTreeType& subtree) {
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530144 BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler enter";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700145 if (ec)
146 {
Ed Tanous5f7d88c2018-11-14 14:08:56 -0800147 messages::internalError(SensorsAsyncResp->res);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530148 BMCWEB_LOG_ERROR
149 << "getObjectsWithConnection resp_handler: Dbus error " << ec;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700150 return;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100151 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100152
Ed Tanous1abe55e2018-09-05 08:30:59 -0700153 BMCWEB_LOG_DEBUG << "Found " << subtree.size() << " subtrees";
154
155 // Make unique list of connections only for requested sensor types and
156 // found in the chassis
157 boost::container::flat_set<std::string> connections;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530158 std::set<std::pair<std::string, std::string>> objectsWithConnection;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700159 // Intrinsic to avoid malloc. Most systems will have < 8 sensor
160 // producers
161 connections.reserve(8);
162
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700163 BMCWEB_LOG_DEBUG << "sensorNames list count: " << sensorNames->size();
164 for (const std::string& tsensor : *sensorNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700165 {
166 BMCWEB_LOG_DEBUG << "Sensor to find: " << tsensor;
167 }
168
169 for (const std::pair<
170 std::string,
171 std::vector<std::pair<std::string, std::vector<std::string>>>>&
172 object : subtree)
173 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700174 if (sensorNames->find(object.first) != sensorNames->end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700175 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700176 for (const std::pair<std::string, std::vector<std::string>>&
177 objData : object.second)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700178 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700179 BMCWEB_LOG_DEBUG << "Adding connection: " << objData.first;
180 connections.insert(objData.first);
181 objectsWithConnection.insert(
182 std::make_pair(object.first, objData.first));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700183 }
184 }
185 }
186 BMCWEB_LOG_DEBUG << "Found " << connections.size() << " connections";
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530187 callback(std::move(connections), std::move(objectsWithConnection));
188 BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler exit";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700189 };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700190 // Make call to ObjectMapper to find all sensors objects
191 crow::connections::systemBus->async_method_call(
192 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
193 "/xyz/openbmc_project/object_mapper",
194 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 2, interfaces);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530195 BMCWEB_LOG_DEBUG << "getObjectsWithConnection exit";
196}
197
198/**
199 * @brief Create connections necessary for sensors
200 * @param SensorsAsyncResp Pointer to object holding response data
201 * @param sensorNames Sensors retrieved from chassis
202 * @param callback Callback for processing gathered connections
203 */
204template <typename Callback>
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700205void getConnections(
206 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
207 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
208 Callback&& callback)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530209{
210 auto objectsWithConnectionCb =
211 [callback](const boost::container::flat_set<std::string>& connections,
212 const std::set<std::pair<std::string, std::string>>&
213 objectsWithConnection) {
214 callback(std::move(connections));
215 };
216 getObjectsWithConnection(SensorsAsyncResp, sensorNames,
217 std::move(objectsWithConnectionCb));
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100218}
219
220/**
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700221 * @brief Shrinks the list of sensors for processing
222 * @param SensorsAysncResp The class holding the Redfish response
223 * @param allSensors A list of all the sensors associated to the
224 * chassis element (i.e. baseboard, front panel, etc...)
225 * @param activeSensors A list that is a reduction of the incoming
226 * allSensors list. Eliminate Thermal sensors when a Power request is
227 * made, and eliminate Power sensors when a Thermal request is made.
228 */
229void reduceSensorList(
230 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
231 const std::vector<std::string>* allSensors,
232 std::shared_ptr<boost::container::flat_set<std::string>> activeSensors)
233{
234 if (SensorsAsyncResp == nullptr)
235 {
236 return;
237 }
238 if ((allSensors == nullptr) || (activeSensors == nullptr))
239 {
240 messages::resourceNotFound(
241 SensorsAsyncResp->res, SensorsAsyncResp->chassisSubNode,
242 SensorsAsyncResp->chassisSubNode == "Thermal" ? "Temperatures"
243 : "Voltages");
244
245 return;
246 }
247 if (allSensors->empty())
248 {
249 // Nothing to do, the activeSensors object is also empty
250 return;
251 }
252
253 for (const char* type : SensorsAsyncResp->types)
254 {
255 for (const std::string& sensor : *allSensors)
256 {
257 if (boost::starts_with(sensor, type))
258 {
259 activeSensors->emplace(sensor);
260 }
261 }
262 }
263}
264
265/**
Carol Wang4bb3dc32019-10-17 18:15:02 +0800266 * @brief Retrieves valid chassis path
267 * @param asyncResp Pointer to object holding response data
268 * @param callback Callback for next step to get valid chassis path
269 */
270template <typename Callback>
271void getValidChassisPath(std::shared_ptr<SensorsAsyncResp> asyncResp,
272 Callback&& callback)
273{
274 BMCWEB_LOG_DEBUG << "checkChassisId enter";
275 const std::array<const char*, 2> interfaces = {
276 "xyz.openbmc_project.Inventory.Item.Board",
277 "xyz.openbmc_project.Inventory.Item.Chassis"};
278
279 auto respHandler =
280 [callback{std::move(callback)},
281 asyncResp](const boost::system::error_code ec,
282 const std::vector<std::string>& chassisPaths) mutable {
283 BMCWEB_LOG_DEBUG << "getValidChassisPath respHandler enter";
284 if (ec)
285 {
286 BMCWEB_LOG_ERROR
287 << "getValidChassisPath respHandler DBUS error: " << ec;
288 messages::internalError(asyncResp->res);
289 return;
290 }
291
292 std::optional<std::string> chassisPath;
293 std::string chassisName;
294 for (const std::string& chassis : chassisPaths)
295 {
296 std::size_t lastPos = chassis.rfind("/");
297 if (lastPos == std::string::npos)
298 {
299 BMCWEB_LOG_ERROR << "Failed to find '/' in " << chassis;
300 continue;
301 }
302 chassisName = chassis.substr(lastPos + 1);
303 if (chassisName == asyncResp->chassisId)
304 {
305 chassisPath = chassis;
306 break;
307 }
308 }
309 callback(chassisPath);
310 };
311
312 // Get the Chassis Collection
313 crow::connections::systemBus->async_method_call(
314 respHandler, "xyz.openbmc_project.ObjectMapper",
315 "/xyz/openbmc_project/object_mapper",
316 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
317 "/xyz/openbmc_project/inventory", 0, interfaces);
318 BMCWEB_LOG_DEBUG << "checkChassisId exit";
319}
320
321/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100322 * @brief Retrieves requested chassis sensors and redundancy data from DBus .
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200323 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100324 * @param callback Callback for next step in gathered sensor processing
325 */
326template <typename Callback>
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700327void getChassis(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700328 Callback&& callback)
329{
330 BMCWEB_LOG_DEBUG << "getChassis enter";
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500331 const std::array<const char*, 2> interfaces = {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700332 "xyz.openbmc_project.Inventory.Item.Board",
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500333 "xyz.openbmc_project.Inventory.Item.Chassis"};
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700334 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp](
335 const boost::system::error_code ec,
336 const std::vector<std::string>& chassisPaths) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700337 BMCWEB_LOG_DEBUG << "getChassis respHandler enter";
338 if (ec)
339 {
340 BMCWEB_LOG_ERROR << "getChassis respHandler DBUS error: " << ec;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700341 messages::internalError(sensorsAsyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700342 return;
343 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100344
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700345 const std::string* chassisPath = nullptr;
346 std::string chassisName;
347 for (const std::string& chassis : chassisPaths)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700348 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700349 std::size_t lastPos = chassis.rfind("/");
350 if (lastPos == std::string::npos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700351 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700352 BMCWEB_LOG_ERROR << "Failed to find '/' in " << chassis;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700353 continue;
354 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700355 chassisName = chassis.substr(lastPos + 1);
356 if (chassisName == sensorsAsyncResp->chassisId)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700357 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700358 chassisPath = &chassis;
359 break;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700360 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700361 }
362 if (chassisPath == nullptr)
363 {
364 messages::resourceNotFound(sensorsAsyncResp->res, "Chassis",
365 sensorsAsyncResp->chassisId);
366 return;
367 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700368
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700369 const std::string& chassisSubNode = sensorsAsyncResp->chassisSubNode;
370 if (chassisSubNode == "Power")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700371 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700372 sensorsAsyncResp->res.jsonValue["@odata.type"] =
373 "#Power.v1_5_2.Power";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700374 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700375 else if (chassisSubNode == "Thermal")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700376 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700377 sensorsAsyncResp->res.jsonValue["@odata.type"] =
378 "#Thermal.v1_4_0.Thermal";
Jennifer Lee4f9a2132019-03-04 12:45:19 -0800379 sensorsAsyncResp->res.jsonValue["Fans"] = nlohmann::json::array();
380 sensorsAsyncResp->res.jsonValue["Temperatures"] =
381 nlohmann::json::array();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700382 }
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500383 else if (chassisSubNode == "Sensors")
384 {
385 sensorsAsyncResp->res.jsonValue["@odata.type"] =
386 "#SensorCollection.SensorCollection";
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500387 sensorsAsyncResp->res.jsonValue["Description"] =
388 "Collection of Sensors for this Chassis";
389 sensorsAsyncResp->res.jsonValue["Members"] =
390 nlohmann::json::array();
391 sensorsAsyncResp->res.jsonValue["Members@odata.count"] = 0;
392 }
393
394 if (chassisSubNode != "Sensors")
395 {
396 sensorsAsyncResp->res.jsonValue["Id"] = chassisSubNode;
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500397 }
398
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700399 sensorsAsyncResp->res.jsonValue["@odata.id"] =
400 "/redfish/v1/Chassis/" + sensorsAsyncResp->chassisId + "/" +
401 chassisSubNode;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700402 sensorsAsyncResp->res.jsonValue["Name"] = chassisSubNode;
403
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500404 // Get the list of all sensors for this Chassis element
405 std::string sensorPath = *chassisPath + "/all_sensors";
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700406 crow::connections::systemBus->async_method_call(
407 [sensorsAsyncResp, callback{std::move(callback)}](
Ed Tanous271584a2019-07-09 16:24:22 -0700408 const boost::system::error_code& e,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700409 const std::variant<std::vector<std::string>>&
410 variantEndpoints) {
Ed Tanous271584a2019-07-09 16:24:22 -0700411 if (e)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700412 {
Ed Tanous271584a2019-07-09 16:24:22 -0700413 if (e.value() != EBADR)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700414 {
415 messages::internalError(sensorsAsyncResp->res);
416 return;
417 }
418 }
419 const std::vector<std::string>* nodeSensorList =
420 std::get_if<std::vector<std::string>>(&(variantEndpoints));
421 if (nodeSensorList == nullptr)
422 {
423 messages::resourceNotFound(
424 sensorsAsyncResp->res, sensorsAsyncResp->chassisSubNode,
425 sensorsAsyncResp->chassisSubNode == "Thermal"
426 ? "Temperatures"
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500427 : sensorsAsyncResp->chassisSubNode == "Power"
428 ? "Voltages"
429 : "Sensors");
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700430 return;
431 }
432 const std::shared_ptr<boost::container::flat_set<std::string>>
433 culledSensorList = std::make_shared<
434 boost::container::flat_set<std::string>>();
435 reduceSensorList(sensorsAsyncResp, nodeSensorList,
436 culledSensorList);
437 callback(culledSensorList);
438 },
439 "xyz.openbmc_project.ObjectMapper", sensorPath,
440 "org.freedesktop.DBus.Properties", "Get",
441 "xyz.openbmc_project.Association", "endpoints");
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100442 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100443
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700444 // Get the Chassis Collection
Ed Tanous1abe55e2018-09-05 08:30:59 -0700445 crow::connections::systemBus->async_method_call(
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700446 respHandler, "xyz.openbmc_project.ObjectMapper",
447 "/xyz/openbmc_project/object_mapper",
448 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Ed Tanous271584a2019-07-09 16:24:22 -0700449 "/xyz/openbmc_project/inventory", 0, interfaces);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700450 BMCWEB_LOG_DEBUG << "getChassis exit";
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100451}
452
453/**
Shawn McCarneyde629b62019-03-08 10:42:51 -0600454 * @brief Finds all DBus object paths that implement ObjectManager.
455 *
456 * Creates a mapping from the associated connection name to the object path.
457 *
458 * Finds the object paths asynchronously. Invokes callback when information has
459 * been obtained.
460 *
461 * The callback must have the following signature:
462 * @code
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500463 * callback(std::shared_ptr<boost::container::flat_map<std::string,
464 * std::string>> objectMgrPaths)
Shawn McCarneyde629b62019-03-08 10:42:51 -0600465 * @endcode
466 *
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700467 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyde629b62019-03-08 10:42:51 -0600468 * @param callback Callback to invoke when object paths obtained.
469 */
470template <typename Callback>
471void getObjectManagerPaths(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
472 Callback&& callback)
473{
474 BMCWEB_LOG_DEBUG << "getObjectManagerPaths enter";
475 const std::array<std::string, 1> interfaces = {
476 "org.freedesktop.DBus.ObjectManager"};
477
478 // Response handler for GetSubTree DBus method
479 auto respHandler = [callback{std::move(callback)},
480 SensorsAsyncResp](const boost::system::error_code ec,
481 const GetSubTreeType& subtree) {
482 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler enter";
483 if (ec)
484 {
485 messages::internalError(SensorsAsyncResp->res);
486 BMCWEB_LOG_ERROR << "getObjectManagerPaths respHandler: DBus error "
487 << ec;
488 return;
489 }
490
491 // Loop over returned object paths
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500492 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
493 objectMgrPaths = std::make_shared<
494 boost::container::flat_map<std::string, std::string>>();
Shawn McCarneyde629b62019-03-08 10:42:51 -0600495 for (const std::pair<
496 std::string,
497 std::vector<std::pair<std::string, std::vector<std::string>>>>&
498 object : subtree)
499 {
500 // Loop over connections for current object path
501 const std::string& objectPath = object.first;
502 for (const std::pair<std::string, std::vector<std::string>>&
503 objData : object.second)
504 {
505 // Add mapping from connection to object path
506 const std::string& connection = objData.first;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500507 (*objectMgrPaths)[connection] = objectPath;
Shawn McCarneyde629b62019-03-08 10:42:51 -0600508 BMCWEB_LOG_DEBUG << "Added mapping " << connection << " -> "
509 << objectPath;
510 }
511 }
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500512 callback(objectMgrPaths);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600513 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler exit";
514 };
515
516 // Query mapper for all DBus object paths that implement ObjectManager
517 crow::connections::systemBus->async_method_call(
518 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
519 "/xyz/openbmc_project/object_mapper",
Ed Tanous271584a2019-07-09 16:24:22 -0700520 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0, interfaces);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600521 BMCWEB_LOG_DEBUG << "getObjectManagerPaths exit";
522}
523
524/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500525 * @brief Returns the Redfish State value for the specified inventory item.
526 * @param inventoryItem D-Bus inventory item associated with a sensor.
527 * @return State value for inventory item.
James Feist34dd1792019-05-17 14:10:54 -0700528 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500529static std::string getState(const InventoryItem* inventoryItem)
530{
531 if ((inventoryItem != nullptr) && !(inventoryItem->isPresent))
532 {
533 return "Absent";
534 }
James Feist34dd1792019-05-17 14:10:54 -0700535
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500536 return "Enabled";
537}
538
539/**
540 * @brief Returns the Redfish Health value for the specified sensor.
541 * @param sensorJson Sensor JSON object.
542 * @param interfacesDict Map of all sensor interfaces.
543 * @param inventoryItem D-Bus inventory item associated with the sensor. Will
544 * be nullptr if no associated inventory item was found.
545 * @return Health value for sensor.
546 */
James Feist34dd1792019-05-17 14:10:54 -0700547static std::string getHealth(
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500548 nlohmann::json& sensorJson,
James Feist34dd1792019-05-17 14:10:54 -0700549 const boost::container::flat_map<
550 std::string, boost::container::flat_map<std::string, SensorVariant>>&
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500551 interfacesDict,
552 const InventoryItem* inventoryItem)
James Feist34dd1792019-05-17 14:10:54 -0700553{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500554 // Get current health value (if any) in the sensor JSON object. Some JSON
555 // objects contain multiple sensors (such as PowerSupplies). We want to set
556 // the overall health to be the most severe of any of the sensors.
557 std::string currentHealth;
558 auto statusIt = sensorJson.find("Status");
559 if (statusIt != sensorJson.end())
560 {
561 auto healthIt = statusIt->find("Health");
562 if (healthIt != statusIt->end())
563 {
564 std::string* health = healthIt->get_ptr<std::string*>();
565 if (health != nullptr)
566 {
567 currentHealth = *health;
568 }
569 }
570 }
571
572 // If current health in JSON object is already Critical, return that. This
573 // should override the sensor health, which might be less severe.
574 if (currentHealth == "Critical")
575 {
576 return "Critical";
577 }
578
579 // Check if sensor has critical threshold alarm
James Feist34dd1792019-05-17 14:10:54 -0700580 auto criticalThresholdIt =
581 interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Critical");
582 if (criticalThresholdIt != interfacesDict.end())
583 {
584 auto thresholdHighIt =
585 criticalThresholdIt->second.find("CriticalAlarmHigh");
586 auto thresholdLowIt =
587 criticalThresholdIt->second.find("CriticalAlarmLow");
588 if (thresholdHighIt != criticalThresholdIt->second.end())
589 {
590 const bool* asserted = std::get_if<bool>(&thresholdHighIt->second);
591 if (asserted == nullptr)
592 {
593 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
594 }
595 else if (*asserted)
596 {
597 return "Critical";
598 }
599 }
600 if (thresholdLowIt != criticalThresholdIt->second.end())
601 {
602 const bool* asserted = std::get_if<bool>(&thresholdLowIt->second);
603 if (asserted == nullptr)
604 {
605 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
606 }
607 else if (*asserted)
608 {
609 return "Critical";
610 }
611 }
612 }
613
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500614 // Check if associated inventory item is not functional
615 if ((inventoryItem != nullptr) && !(inventoryItem->isFunctional))
616 {
617 return "Critical";
618 }
619
620 // If current health in JSON object is already Warning, return that. This
621 // should override the sensor status, which might be less severe.
622 if (currentHealth == "Warning")
623 {
624 return "Warning";
625 }
626
627 // Check if sensor has warning threshold alarm
James Feist34dd1792019-05-17 14:10:54 -0700628 auto warningThresholdIt =
629 interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Warning");
630 if (warningThresholdIt != interfacesDict.end())
631 {
632 auto thresholdHighIt =
633 warningThresholdIt->second.find("WarningAlarmHigh");
634 auto thresholdLowIt =
635 warningThresholdIt->second.find("WarningAlarmLow");
636 if (thresholdHighIt != warningThresholdIt->second.end())
637 {
638 const bool* asserted = std::get_if<bool>(&thresholdHighIt->second);
639 if (asserted == nullptr)
640 {
641 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
642 }
643 else if (*asserted)
644 {
645 return "Warning";
646 }
647 }
648 if (thresholdLowIt != warningThresholdIt->second.end())
649 {
650 const bool* asserted = std::get_if<bool>(&thresholdLowIt->second);
651 if (asserted == nullptr)
652 {
653 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
654 }
655 else if (*asserted)
656 {
657 return "Warning";
658 }
659 }
660 }
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500661
James Feist34dd1792019-05-17 14:10:54 -0700662 return "OK";
663}
664
Anthony Wilsond5005492019-07-31 16:34:17 -0500665static void setLedState(nlohmann::json& sensorJson,
666 const InventoryItem* inventoryItem)
667{
668 if (inventoryItem != nullptr && !inventoryItem->ledObjectPath.empty())
669 {
670 switch (inventoryItem->ledState)
671 {
672 case LedState::OFF:
673 sensorJson["IndicatorLED"] = "Off";
674 break;
675 case LedState::ON:
676 sensorJson["IndicatorLED"] = "Lit";
677 break;
678 case LedState::BLINK:
679 sensorJson["IndicatorLED"] = "Blinking";
680 break;
681 default:
682 break;
683 }
684 }
685}
686
James Feist34dd1792019-05-17 14:10:54 -0700687/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100688 * @brief Builds a json sensor representation of a sensor.
689 * @param sensorName The name of the sensor to be built
Gunnar Mills274fad52018-06-13 15:45:36 -0500690 * @param sensorType The type (temperature, fan_tach, etc) of the sensor to
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100691 * build
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500692 * @param sensorSchema The schema (Power, Thermal, etc) being associated with
693 * the sensor to build
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100694 * @param interfacesDict A dictionary of the interfaces and properties of said
695 * interfaces to be built from
696 * @param sensor_json The json object to fill
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500697 * @param inventoryItem D-Bus inventory item associated with the sensor. Will
698 * be nullptr if no associated inventory item was found.
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100699 */
700void objectInterfacesToJson(
701 const std::string& sensorName, const std::string& sensorType,
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500702 const std::string& sensorSchema,
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100703 const boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700704 std::string, boost::container::flat_map<std::string, SensorVariant>>&
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100705 interfacesDict,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500706 nlohmann::json& sensor_json, InventoryItem* inventoryItem)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700707{
708 // We need a value interface before we can do anything with it
709 auto valueIt = interfacesDict.find("xyz.openbmc_project.Sensor.Value");
710 if (valueIt == interfacesDict.end())
711 {
712 BMCWEB_LOG_ERROR << "Sensor doesn't have a value interface";
713 return;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100714 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100715
Ed Tanous1abe55e2018-09-05 08:30:59 -0700716 // Assume values exist as is (10^0 == 1) if no scale exists
717 int64_t scaleMultiplier = 0;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100718
Ed Tanous1abe55e2018-09-05 08:30:59 -0700719 auto scaleIt = valueIt->second.find("Scale");
720 // If a scale exists, pull value as int64, and use the scaling.
721 if (scaleIt != valueIt->second.end())
722 {
Ed Tanousabf2add2019-01-22 16:40:12 -0800723 const int64_t* int64Value = std::get_if<int64_t>(&scaleIt->second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700724 if (int64Value != nullptr)
725 {
726 scaleMultiplier = *int64Value;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100727 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100728 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700729
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500730 if (sensorSchema == "Sensors")
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500731 {
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500732 // For sensors in SensorCollection we set Id instead of MemberId,
733 // including power sensors.
734 sensor_json["Id"] = sensorName;
735 sensor_json["Name"] = boost::replace_all_copy(sensorName, "_", " ");
736 }
737 else if (sensorType != "power")
738 {
739 // Set MemberId and Name for non-power sensors. For PowerSupplies and
740 // PowerControl, those properties have more general values because
741 // multiple sensors can be stored in the same JSON object.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500742 sensor_json["MemberId"] = sensorName;
743 sensor_json["Name"] = boost::replace_all_copy(sensorName, "_", " ");
744 }
Ed Tanouse742b6c2019-05-03 15:06:53 -0700745
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500746 sensor_json["Status"]["State"] = getState(inventoryItem);
747 sensor_json["Status"]["Health"] =
748 getHealth(sensor_json, interfacesDict, inventoryItem);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700749
750 // Parameter to set to override the type we get from dbus, and force it to
751 // int, regardless of what is available. This is used for schemas like fan,
752 // that require integers, not floats.
753 bool forceToInt = false;
754
Anthony Wilson3929aca2019-07-19 15:42:33 -0500755 nlohmann::json::json_pointer unit("/Reading");
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500756 if (sensorSchema == "Sensors")
757 {
758 sensor_json["@odata.type"] = "#Sensor.v1_0_0.Sensor";
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500759 if (sensorType == "power")
760 {
761 sensor_json["ReadingUnits"] = "Watts";
762 }
763 else if (sensorType == "current")
764 {
765 sensor_json["ReadingUnits"] = "Amperes";
766 }
Adrian Ambrożewiczf8ede152020-06-02 13:26:33 +0200767 else if (sensorType == "utilization")
768 {
769 sensor_json["ReadingUnits"] = "Percent";
770 }
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500771 }
772 else if (sensorType == "temperature")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700773 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500774 unit = "/ReadingCelsius"_json_pointer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700775 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Temperature";
776 // TODO(ed) Documentation says that path should be type fan_tach,
777 // implementation seems to implement fan
778 }
779 else if (sensorType == "fan" || sensorType == "fan_tach")
780 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500781 unit = "/Reading"_json_pointer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700782 sensor_json["ReadingUnits"] = "RPM";
783 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
Anthony Wilsond5005492019-07-31 16:34:17 -0500784 setLedState(sensor_json, inventoryItem);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700785 forceToInt = true;
786 }
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700787 else if (sensorType == "fan_pwm")
788 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500789 unit = "/Reading"_json_pointer;
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700790 sensor_json["ReadingUnits"] = "Percent";
791 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
Anthony Wilsond5005492019-07-31 16:34:17 -0500792 setLedState(sensor_json, inventoryItem);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700793 forceToInt = true;
794 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700795 else if (sensorType == "voltage")
796 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500797 unit = "/ReadingVolts"_json_pointer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700798 sensor_json["@odata.type"] = "#Power.v1_0_0.Voltage";
799 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700800 else if (sensorType == "power")
801 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700802 std::string sensorNameLower =
803 boost::algorithm::to_lower_copy(sensorName);
804
Eddie James028f7eb2019-05-17 21:24:36 +0000805 if (!sensorName.compare("total_power"))
806 {
Gunnar Mills7ab06f42019-07-02 13:07:16 -0500807 sensor_json["@odata.type"] = "#Power.v1_0_0.PowerControl";
808 // Put multiple "sensors" into a single PowerControl, so have
809 // generic names for MemberId and Name. Follows Redfish mockup.
810 sensor_json["MemberId"] = "0";
811 sensor_json["Name"] = "Chassis Power Control";
Anthony Wilson3929aca2019-07-19 15:42:33 -0500812 unit = "/PowerConsumedWatts"_json_pointer;
Eddie James028f7eb2019-05-17 21:24:36 +0000813 }
814 else if (sensorNameLower.find("input") != std::string::npos)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700815 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500816 unit = "/PowerInputWatts"_json_pointer;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700817 }
818 else
819 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500820 unit = "/PowerOutputWatts"_json_pointer;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700821 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700822 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700823 else
824 {
825 BMCWEB_LOG_ERROR << "Redfish cannot map object type for " << sensorName;
826 return;
827 }
828 // Map of dbus interface name, dbus property name and redfish property_name
Anthony Wilson3929aca2019-07-19 15:42:33 -0500829 std::vector<
830 std::tuple<const char*, const char*, nlohmann::json::json_pointer>>
831 properties;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700832 properties.reserve(7);
833
834 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600835
Anthony Wilson3929aca2019-07-19 15:42:33 -0500836 if (sensorSchema == "Sensors")
837 {
838 properties.emplace_back(
839 "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningHigh",
840 "/Thresholds/UpperCaution/Reading"_json_pointer);
841 properties.emplace_back(
842 "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningLow",
843 "/Thresholds/LowerCaution/Reading"_json_pointer);
844 properties.emplace_back(
845 "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalHigh",
846 "/Thresholds/UpperCritical/Reading"_json_pointer);
847 properties.emplace_back(
848 "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalLow",
849 "/Thresholds/LowerCritical/Reading"_json_pointer);
850 }
851 else if (sensorType != "power")
Shawn McCarneyde629b62019-03-08 10:42:51 -0600852 {
853 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500854 "WarningHigh",
855 "/UpperThresholdNonCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600856 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500857 "WarningLow",
858 "/LowerThresholdNonCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600859 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500860 "CriticalHigh",
861 "/UpperThresholdCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600862 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500863 "CriticalLow",
864 "/LowerThresholdCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600865 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700866
Ed Tanous2474adf2018-09-05 16:31:16 -0700867 // TODO Need to get UpperThresholdFatal and LowerThresholdFatal
868
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500869 if (sensorSchema == "Sensors")
870 {
871 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500872 "/ReadingRangeMin"_json_pointer);
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500873 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500874 "/ReadingRangeMax"_json_pointer);
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500875 }
876 else if (sensorType == "temperature")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700877 {
878 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500879 "/MinReadingRangeTemp"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700880 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500881 "/MaxReadingRangeTemp"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700882 }
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500883 else if (sensorType != "power")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700884 {
885 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500886 "/MinReadingRange"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700887 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500888 "/MaxReadingRange"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700889 }
890
Anthony Wilson3929aca2019-07-19 15:42:33 -0500891 for (const std::tuple<const char*, const char*,
892 nlohmann::json::json_pointer>& p : properties)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700893 {
894 auto interfaceProperties = interfacesDict.find(std::get<0>(p));
895 if (interfaceProperties != interfacesDict.end())
896 {
Ed Tanous271584a2019-07-09 16:24:22 -0700897 auto thisValueIt = interfaceProperties->second.find(std::get<1>(p));
898 if (thisValueIt != interfaceProperties->second.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700899 {
Ed Tanous271584a2019-07-09 16:24:22 -0700900 const SensorVariant& valueVariant = thisValueIt->second;
Anthony Wilson3929aca2019-07-19 15:42:33 -0500901
902 // The property we want to set may be nested json, so use
903 // a json_pointer for easy indexing into the json structure.
904 const nlohmann::json::json_pointer& key = std::get<2>(p);
905
Ed Tanous1abe55e2018-09-05 08:30:59 -0700906 // Attempt to pull the int64 directly
Ed Tanousabf2add2019-01-22 16:40:12 -0800907 const int64_t* int64Value = std::get_if<int64_t>(&valueVariant);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700908
Ed Tanousabf2add2019-01-22 16:40:12 -0800909 const double* doubleValue = std::get_if<double>(&valueVariant);
Eddie James028f7eb2019-05-17 21:24:36 +0000910 const uint32_t* uValue = std::get_if<uint32_t>(&valueVariant);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700911 double temp = 0.0;
912 if (int64Value != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700913 {
Ed Tanous271584a2019-07-09 16:24:22 -0700914 temp = static_cast<double>(*int64Value);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700915 }
916 else if (doubleValue != nullptr)
917 {
918 temp = *doubleValue;
919 }
Eddie James028f7eb2019-05-17 21:24:36 +0000920 else if (uValue != nullptr)
921 {
922 temp = *uValue;
923 }
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700924 else
925 {
926 BMCWEB_LOG_ERROR
927 << "Got value interface that wasn't int or double";
928 continue;
929 }
930 temp = temp * std::pow(10, scaleMultiplier);
931 if (forceToInt)
932 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500933 sensor_json[key] = static_cast<int64_t>(temp);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700934 }
935 else
936 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500937 sensor_json[key] = temp;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700938 }
939 }
940 }
941 }
942 BMCWEB_LOG_DEBUG << "Added sensor " << sensorName;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100943}
944
James Feist8bd25cc2019-03-15 15:14:00 -0700945static void
946 populateFanRedundancy(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp)
947{
948 crow::connections::systemBus->async_method_call(
949 [sensorsAsyncResp](const boost::system::error_code ec,
950 const GetSubTreeType& resp) {
951 if (ec)
952 {
953 return; // don't have to have this interface
954 }
Ed Tanouse278c182019-03-13 16:23:37 -0700955 for (const std::pair<std::string,
956 std::vector<std::pair<
957 std::string, std::vector<std::string>>>>&
958 pathPair : resp)
James Feist8bd25cc2019-03-15 15:14:00 -0700959 {
Ed Tanouse278c182019-03-13 16:23:37 -0700960 const std::string& path = pathPair.first;
961 const std::vector<
962 std::pair<std::string, std::vector<std::string>>>& objDict =
963 pathPair.second;
James Feist8bd25cc2019-03-15 15:14:00 -0700964 if (objDict.empty())
965 {
966 continue; // this should be impossible
967 }
968
969 const std::string& owner = objDict.begin()->first;
970 crow::connections::systemBus->async_method_call(
971 [path, owner,
Ed Tanous271584a2019-07-09 16:24:22 -0700972 sensorsAsyncResp](const boost::system::error_code e,
James Feist8bd25cc2019-03-15 15:14:00 -0700973 std::variant<std::vector<std::string>>
974 variantEndpoints) {
Ed Tanous271584a2019-07-09 16:24:22 -0700975 if (e)
James Feist8bd25cc2019-03-15 15:14:00 -0700976 {
977 return; // if they don't have an association we
978 // can't tell what chassis is
979 }
980 // verify part of the right chassis
981 auto endpoints = std::get_if<std::vector<std::string>>(
982 &variantEndpoints);
983
984 if (endpoints == nullptr)
985 {
986 BMCWEB_LOG_ERROR << "Invalid association interface";
987 messages::internalError(sensorsAsyncResp->res);
988 return;
989 }
990
991 auto found = std::find_if(
992 endpoints->begin(), endpoints->end(),
993 [sensorsAsyncResp](const std::string& entry) {
994 return entry.find(
995 sensorsAsyncResp->chassisId) !=
996 std::string::npos;
997 });
998
999 if (found == endpoints->end())
1000 {
1001 return;
1002 }
1003 crow::connections::systemBus->async_method_call(
1004 [path, sensorsAsyncResp](
Ed Tanous271584a2019-07-09 16:24:22 -07001005 const boost::system::error_code& err,
James Feist8bd25cc2019-03-15 15:14:00 -07001006 const boost::container::flat_map<
1007 std::string,
1008 std::variant<uint8_t,
1009 std::vector<std::string>,
1010 std::string>>& ret) {
Ed Tanous271584a2019-07-09 16:24:22 -07001011 if (err)
James Feist8bd25cc2019-03-15 15:14:00 -07001012 {
1013 return; // don't have to have this
1014 // interface
1015 }
1016 auto findFailures = ret.find("AllowedFailures");
1017 auto findCollection = ret.find("Collection");
1018 auto findStatus = ret.find("Status");
1019
1020 if (findFailures == ret.end() ||
1021 findCollection == ret.end() ||
1022 findStatus == ret.end())
1023 {
1024 BMCWEB_LOG_ERROR
1025 << "Invalid redundancy interface";
1026 messages::internalError(
1027 sensorsAsyncResp->res);
1028 return;
1029 }
1030
1031 auto allowedFailures = std::get_if<uint8_t>(
1032 &(findFailures->second));
1033 auto collection =
1034 std::get_if<std::vector<std::string>>(
1035 &(findCollection->second));
1036 auto status = std::get_if<std::string>(
1037 &(findStatus->second));
1038
1039 if (allowedFailures == nullptr ||
1040 collection == nullptr || status == nullptr)
1041 {
1042
1043 BMCWEB_LOG_ERROR
1044 << "Invalid redundancy interface "
1045 "types";
1046 messages::internalError(
1047 sensorsAsyncResp->res);
1048 return;
1049 }
1050 size_t lastSlash = path.rfind("/");
1051 if (lastSlash == std::string::npos)
1052 {
1053 // this should be impossible
1054 messages::internalError(
1055 sensorsAsyncResp->res);
1056 return;
1057 }
1058 std::string name = path.substr(lastSlash + 1);
1059 std::replace(name.begin(), name.end(), '_',
1060 ' ');
1061
1062 std::string health;
1063
1064 if (boost::ends_with(*status, "Full"))
1065 {
1066 health = "OK";
1067 }
1068 else if (boost::ends_with(*status, "Degraded"))
1069 {
1070 health = "Warning";
1071 }
1072 else
1073 {
1074 health = "Critical";
1075 }
1076 std::vector<nlohmann::json> redfishCollection;
1077 const auto& fanRedfish =
1078 sensorsAsyncResp->res.jsonValue["Fans"];
1079 for (const std::string& item : *collection)
1080 {
1081 lastSlash = item.rfind("/");
1082 // make a copy as collection is const
1083 std::string itemName =
1084 item.substr(lastSlash + 1);
1085 /*
1086 todo(ed): merge patch that fixes the names
1087 std::replace(itemName.begin(),
1088 itemName.end(), '_', ' ');*/
1089 auto schemaItem = std::find_if(
1090 fanRedfish.begin(), fanRedfish.end(),
1091 [itemName](const nlohmann::json& fan) {
1092 return fan["MemberId"] == itemName;
1093 });
1094 if (schemaItem != fanRedfish.end())
1095 {
1096 redfishCollection.push_back(
1097 {{"@odata.id",
1098 (*schemaItem)["@odata.id"]}});
1099 }
1100 else
1101 {
1102 BMCWEB_LOG_ERROR
1103 << "failed to find fan in schema";
1104 messages::internalError(
1105 sensorsAsyncResp->res);
1106 return;
1107 }
1108 }
1109
Ed Tanous271584a2019-07-09 16:24:22 -07001110 nlohmann::json& jResp =
1111 sensorsAsyncResp->res
1112 .jsonValue["Redundancy"];
1113 jResp.push_back(
James Feist8bd25cc2019-03-15 15:14:00 -07001114 {{"@odata.id",
AppaRao Puli717794d2019-10-18 22:54:53 +05301115 "/redfish/v1/Chassis/" +
James Feist8bd25cc2019-03-15 15:14:00 -07001116 sensorsAsyncResp->chassisId + "/" +
1117 sensorsAsyncResp->chassisSubNode +
1118 "#/Redundancy/" +
Ed Tanous271584a2019-07-09 16:24:22 -07001119 std::to_string(jResp.size())},
James Feist8bd25cc2019-03-15 15:14:00 -07001120 {"@odata.type",
1121 "#Redundancy.v1_3_2.Redundancy"},
1122 {"MinNumNeeded",
1123 collection->size() - *allowedFailures},
1124 {"MemberId", name},
1125 {"Mode", "N+m"},
1126 {"Name", name},
1127 {"RedundancySet", redfishCollection},
1128 {"Status",
1129 {{"Health", health},
1130 {"State", "Enabled"}}}});
1131 },
1132 owner, path, "org.freedesktop.DBus.Properties",
1133 "GetAll",
1134 "xyz.openbmc_project.Control.FanRedundancy");
1135 },
James Feist02e92e32019-06-26 12:07:05 -07001136 "xyz.openbmc_project.ObjectMapper", path + "/chassis",
James Feist8bd25cc2019-03-15 15:14:00 -07001137 "org.freedesktop.DBus.Properties", "Get",
1138 "xyz.openbmc_project.Association", "endpoints");
1139 }
1140 },
1141 "xyz.openbmc_project.ObjectMapper",
1142 "/xyz/openbmc_project/object_mapper",
1143 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
1144 "/xyz/openbmc_project/control", 2,
1145 std::array<const char*, 1>{
1146 "xyz.openbmc_project.Control.FanRedundancy"});
1147}
1148
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001149void sortJSONResponse(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
1150{
1151 nlohmann::json& response = SensorsAsyncResp->res.jsonValue;
1152 std::array<std::string, 2> sensorHeaders{"Temperatures", "Fans"};
1153 if (SensorsAsyncResp->chassisSubNode == "Power")
1154 {
1155 sensorHeaders = {"Voltages", "PowerSupplies"};
1156 }
1157 for (const std::string& sensorGroup : sensorHeaders)
1158 {
1159 nlohmann::json::iterator entry = response.find(sensorGroup);
1160 if (entry != response.end())
1161 {
1162 std::sort(entry->begin(), entry->end(),
1163 [](nlohmann::json& c1, nlohmann::json& c2) {
1164 return c1["Name"] < c2["Name"];
1165 });
1166
1167 // add the index counts to the end of each entry
1168 size_t count = 0;
1169 for (nlohmann::json& sensorJson : *entry)
1170 {
1171 nlohmann::json::iterator odata = sensorJson.find("@odata.id");
1172 if (odata == sensorJson.end())
1173 {
1174 continue;
1175 }
1176 std::string* value = odata->get_ptr<std::string*>();
1177 if (value != nullptr)
1178 {
1179 *value += std::to_string(count);
1180 count++;
1181 }
1182 }
1183 }
1184 }
1185}
1186
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001187/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001188 * @brief Finds the inventory item with the specified object path.
1189 * @param inventoryItems D-Bus inventory items associated with sensors.
1190 * @param invItemObjPath D-Bus object path of inventory item.
1191 * @return Inventory item within vector, or nullptr if no match found.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001192 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001193static InventoryItem* findInventoryItem(
1194 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1195 const std::string& invItemObjPath)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001196{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001197 for (InventoryItem& inventoryItem : *inventoryItems)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001198 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001199 if (inventoryItem.objectPath == invItemObjPath)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001200 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001201 return &inventoryItem;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001202 }
1203 }
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001204 return nullptr;
1205}
1206
1207/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001208 * @brief Finds the inventory item associated with the specified sensor.
1209 * @param inventoryItems D-Bus inventory items associated with sensors.
1210 * @param sensorObjPath D-Bus object path of sensor.
1211 * @return Inventory item within vector, or nullptr if no match found.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001212 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001213static InventoryItem* findInventoryItemForSensor(
1214 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1215 const std::string& sensorObjPath)
1216{
1217 for (InventoryItem& inventoryItem : *inventoryItems)
1218 {
1219 if (inventoryItem.sensors.count(sensorObjPath) > 0)
1220 {
1221 return &inventoryItem;
1222 }
1223 }
1224 return nullptr;
1225}
1226
1227/**
Anthony Wilsond5005492019-07-31 16:34:17 -05001228 * @brief Finds the inventory item associated with the specified led path.
1229 * @param inventoryItems D-Bus inventory items associated with sensors.
1230 * @param ledObjPath D-Bus object path of led.
1231 * @return Inventory item within vector, or nullptr if no match found.
1232 */
1233inline InventoryItem*
1234 findInventoryItemForLed(std::vector<InventoryItem>& inventoryItems,
1235 const std::string& ledObjPath)
1236{
1237 for (InventoryItem& inventoryItem : inventoryItems)
1238 {
1239 if (inventoryItem.ledObjectPath == ledObjPath)
1240 {
1241 return &inventoryItem;
1242 }
1243 }
1244 return nullptr;
1245}
1246
1247/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001248 * @brief Adds inventory item and associated sensor to specified vector.
1249 *
1250 * Adds a new InventoryItem to the vector if necessary. Searches for an
1251 * existing InventoryItem with the specified object path. If not found, one is
1252 * added to the vector.
1253 *
1254 * Next, the specified sensor is added to the set of sensors associated with the
1255 * InventoryItem.
1256 *
1257 * @param inventoryItems D-Bus inventory items associated with sensors.
1258 * @param invItemObjPath D-Bus object path of inventory item.
1259 * @param sensorObjPath D-Bus object path of sensor
1260 */
1261static void
1262 addInventoryItem(std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1263 const std::string& invItemObjPath,
1264 const std::string& sensorObjPath)
1265{
1266 // Look for inventory item in vector
1267 InventoryItem* inventoryItem =
1268 findInventoryItem(inventoryItems, invItemObjPath);
1269
1270 // If inventory item doesn't exist in vector, add it
1271 if (inventoryItem == nullptr)
1272 {
1273 inventoryItems->emplace_back(invItemObjPath);
1274 inventoryItem = &(inventoryItems->back());
1275 }
1276
1277 // Add sensor to set of sensors associated with inventory item
1278 inventoryItem->sensors.emplace(sensorObjPath);
1279}
1280
1281/**
1282 * @brief Stores D-Bus data in the specified inventory item.
1283 *
1284 * Finds D-Bus data in the specified map of interfaces. Stores the data in the
1285 * specified InventoryItem.
1286 *
1287 * This data is later used to provide sensor property values in the JSON
1288 * response.
1289 *
1290 * @param inventoryItem Inventory item where data will be stored.
1291 * @param interfacesDict Map containing D-Bus interfaces and their properties
1292 * for the specified inventory item.
1293 */
1294static void storeInventoryItemData(
1295 InventoryItem& inventoryItem,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001296 const boost::container::flat_map<
1297 std::string, boost::container::flat_map<std::string, SensorVariant>>&
1298 interfacesDict)
1299{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001300 // Get properties from Inventory.Item interface
1301 auto interfaceIt =
1302 interfacesDict.find("xyz.openbmc_project.Inventory.Item");
1303 if (interfaceIt != interfacesDict.end())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001304 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001305 auto propertyIt = interfaceIt->second.find("Present");
1306 if (propertyIt != interfaceIt->second.end())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001307 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001308 const bool* value = std::get_if<bool>(&propertyIt->second);
1309 if (value != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001310 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001311 inventoryItem.isPresent = *value;
1312 }
1313 }
1314 }
1315
1316 // Check if Inventory.Item.PowerSupply interface is present
1317 interfaceIt =
1318 interfacesDict.find("xyz.openbmc_project.Inventory.Item.PowerSupply");
1319 if (interfaceIt != interfacesDict.end())
1320 {
1321 inventoryItem.isPowerSupply = true;
1322 }
1323
1324 // Get properties from Inventory.Decorator.Asset interface
1325 interfaceIt =
1326 interfacesDict.find("xyz.openbmc_project.Inventory.Decorator.Asset");
1327 if (interfaceIt != interfacesDict.end())
1328 {
1329 auto propertyIt = interfaceIt->second.find("Manufacturer");
1330 if (propertyIt != interfaceIt->second.end())
1331 {
1332 const std::string* value =
1333 std::get_if<std::string>(&propertyIt->second);
1334 if (value != nullptr)
1335 {
1336 inventoryItem.manufacturer = *value;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001337 }
1338 }
1339
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001340 propertyIt = interfaceIt->second.find("Model");
1341 if (propertyIt != interfaceIt->second.end())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001342 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001343 const std::string* value =
1344 std::get_if<std::string>(&propertyIt->second);
1345 if (value != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001346 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001347 inventoryItem.model = *value;
1348 }
1349 }
1350
1351 propertyIt = interfaceIt->second.find("PartNumber");
1352 if (propertyIt != interfaceIt->second.end())
1353 {
1354 const std::string* value =
1355 std::get_if<std::string>(&propertyIt->second);
1356 if (value != nullptr)
1357 {
1358 inventoryItem.partNumber = *value;
1359 }
1360 }
1361
1362 propertyIt = interfaceIt->second.find("SerialNumber");
1363 if (propertyIt != interfaceIt->second.end())
1364 {
1365 const std::string* value =
1366 std::get_if<std::string>(&propertyIt->second);
1367 if (value != nullptr)
1368 {
1369 inventoryItem.serialNumber = *value;
1370 }
1371 }
1372 }
1373
1374 // Get properties from State.Decorator.OperationalStatus interface
1375 interfaceIt = interfacesDict.find(
1376 "xyz.openbmc_project.State.Decorator.OperationalStatus");
1377 if (interfaceIt != interfacesDict.end())
1378 {
1379 auto propertyIt = interfaceIt->second.find("Functional");
1380 if (propertyIt != interfaceIt->second.end())
1381 {
1382 const bool* value = std::get_if<bool>(&propertyIt->second);
1383 if (value != nullptr)
1384 {
1385 inventoryItem.isFunctional = *value;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001386 }
1387 }
1388 }
1389}
1390
1391/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001392 * @brief Gets D-Bus data for inventory items associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001393 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001394 * Uses the specified connections (services) to obtain D-Bus data for inventory
1395 * items associated with sensors. Stores the resulting data in the
1396 * inventoryItems vector.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001397 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001398 * This data is later used to provide sensor property values in the JSON
1399 * response.
1400 *
1401 * Finds the inventory item data asynchronously. Invokes callback when data has
1402 * been obtained.
1403 *
1404 * The callback must have the following signature:
1405 * @code
Anthony Wilsond5005492019-07-31 16:34:17 -05001406 * callback(void)
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001407 * @endcode
1408 *
1409 * This function is called recursively, obtaining data asynchronously from one
1410 * connection in each call. This ensures the callback is not invoked until the
1411 * last asynchronous function has completed.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001412 *
1413 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001414 * @param inventoryItems D-Bus inventory items associated with sensors.
1415 * @param invConnections Connections that provide data for the inventory items.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001416 * @param objectMgrPaths Mappings from connection name to DBus object path that
1417 * implements ObjectManager.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001418 * @param callback Callback to invoke when inventory data has been obtained.
1419 * @param invConnectionsIndex Current index in invConnections. Only specified
1420 * in recursive calls to this function.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001421 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001422template <typename Callback>
1423static void getInventoryItemsData(
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001424 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001425 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001426 std::shared_ptr<boost::container::flat_set<std::string>> invConnections,
1427 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001428 objectMgrPaths,
Ed Tanous271584a2019-07-09 16:24:22 -07001429 Callback&& callback, size_t invConnectionsIndex = 0)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001430{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001431 BMCWEB_LOG_DEBUG << "getInventoryItemsData enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001432
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001433 // If no more connections left, call callback
1434 if (invConnectionsIndex >= invConnections->size())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001435 {
Anthony Wilsond5005492019-07-31 16:34:17 -05001436 callback();
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001437 BMCWEB_LOG_DEBUG << "getInventoryItemsData exit";
1438 return;
1439 }
1440
1441 // Get inventory item data from current connection
1442 auto it = invConnections->nth(invConnectionsIndex);
1443 if (it != invConnections->end())
1444 {
1445 const std::string& invConnection = *it;
1446
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001447 // Response handler for GetManagedObjects
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001448 auto respHandler = [sensorsAsyncResp, inventoryItems, invConnections,
1449 objectMgrPaths, callback{std::move(callback)},
1450 invConnectionsIndex](
1451 const boost::system::error_code ec,
1452 ManagedObjectsVectorType& resp) {
1453 BMCWEB_LOG_DEBUG << "getInventoryItemsData respHandler enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001454 if (ec)
1455 {
1456 BMCWEB_LOG_ERROR
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001457 << "getInventoryItemsData respHandler DBus error " << ec;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001458 messages::internalError(sensorsAsyncResp->res);
1459 return;
1460 }
1461
1462 // Loop through returned object paths
1463 for (const auto& objDictEntry : resp)
1464 {
1465 const std::string& objPath =
1466 static_cast<const std::string&>(objDictEntry.first);
1467
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001468 // If this object path is one of the specified inventory items
1469 InventoryItem* inventoryItem =
1470 findInventoryItem(inventoryItems, objPath);
1471 if (inventoryItem != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001472 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001473 // Store inventory data in InventoryItem
1474 storeInventoryItemData(*inventoryItem, objDictEntry.second);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001475 }
1476 }
1477
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001478 // Recurse to get inventory item data from next connection
1479 getInventoryItemsData(sensorsAsyncResp, inventoryItems,
1480 invConnections, objectMgrPaths,
1481 std::move(callback), invConnectionsIndex + 1);
1482
1483 BMCWEB_LOG_DEBUG << "getInventoryItemsData respHandler exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001484 };
1485
1486 // Find DBus object path that implements ObjectManager for the current
1487 // connection. If no mapping found, default to "/".
1488 auto iter = objectMgrPaths->find(invConnection);
1489 const std::string& objectMgrPath =
1490 (iter != objectMgrPaths->end()) ? iter->second : "/";
1491 BMCWEB_LOG_DEBUG << "ObjectManager path for " << invConnection << " is "
1492 << objectMgrPath;
1493
1494 // Get all object paths and their interfaces for current connection
1495 crow::connections::systemBus->async_method_call(
1496 std::move(respHandler), invConnection, objectMgrPath,
1497 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1498 }
1499
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001500 BMCWEB_LOG_DEBUG << "getInventoryItemsData exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001501}
1502
1503/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001504 * @brief Gets connections that provide D-Bus data for inventory items.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001505 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001506 * Gets the D-Bus connections (services) that provide data for the inventory
1507 * items that are associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001508 *
1509 * Finds the connections asynchronously. Invokes callback when information has
1510 * been obtained.
1511 *
1512 * The callback must have the following signature:
1513 * @code
1514 * callback(std::shared_ptr<boost::container::flat_set<std::string>>
1515 * invConnections)
1516 * @endcode
1517 *
1518 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001519 * @param inventoryItems D-Bus inventory items associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001520 * @param callback Callback to invoke when connections have been obtained.
1521 */
1522template <typename Callback>
1523static void getInventoryItemsConnections(
1524 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001525 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001526 Callback&& callback)
1527{
1528 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections enter";
1529
1530 const std::string path = "/xyz/openbmc_project/inventory";
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001531 const std::array<std::string, 4> interfaces = {
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001532 "xyz.openbmc_project.Inventory.Item",
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001533 "xyz.openbmc_project.Inventory.Item.PowerSupply",
1534 "xyz.openbmc_project.Inventory.Decorator.Asset",
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001535 "xyz.openbmc_project.State.Decorator.OperationalStatus"};
1536
1537 // Response handler for parsing output from GetSubTree
1538 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001539 inventoryItems](const boost::system::error_code ec,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001540 const GetSubTreeType& subtree) {
1541 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections respHandler enter";
1542 if (ec)
1543 {
1544 messages::internalError(sensorsAsyncResp->res);
1545 BMCWEB_LOG_ERROR
1546 << "getInventoryItemsConnections respHandler DBus error " << ec;
1547 return;
1548 }
1549
1550 // Make unique list of connections for desired inventory items
1551 std::shared_ptr<boost::container::flat_set<std::string>>
1552 invConnections =
1553 std::make_shared<boost::container::flat_set<std::string>>();
1554 invConnections->reserve(8);
1555
1556 // Loop through objects from GetSubTree
1557 for (const std::pair<
1558 std::string,
1559 std::vector<std::pair<std::string, std::vector<std::string>>>>&
1560 object : subtree)
1561 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001562 // Check if object path is one of the specified inventory items
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001563 const std::string& objPath = object.first;
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001564 if (findInventoryItem(inventoryItems, objPath) != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001565 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001566 // Store all connections to inventory item
1567 for (const std::pair<std::string, std::vector<std::string>>&
1568 objData : object.second)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001569 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001570 const std::string& invConnection = objData.first;
1571 invConnections->insert(invConnection);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001572 }
1573 }
1574 }
Anthony Wilsond5005492019-07-31 16:34:17 -05001575
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001576 callback(invConnections);
1577 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections respHandler exit";
1578 };
1579
1580 // Make call to ObjectMapper to find all inventory items
1581 crow::connections::systemBus->async_method_call(
1582 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
1583 "/xyz/openbmc_project/object_mapper",
1584 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 0, interfaces);
1585 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections exit";
1586}
1587
1588/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001589 * @brief Gets associations from sensors to inventory items.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001590 *
1591 * Looks for ObjectMapper associations from the specified sensors to related
Anthony Wilsond5005492019-07-31 16:34:17 -05001592 * inventory items. Then finds the associations from those inventory items to
1593 * their LEDs, if any.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001594 *
1595 * Finds the inventory items asynchronously. Invokes callback when information
1596 * has been obtained.
1597 *
1598 * The callback must have the following signature:
1599 * @code
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001600 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001601 * @endcode
1602 *
1603 * @param sensorsAsyncResp Pointer to object holding response data.
1604 * @param sensorNames All sensors within the current chassis.
1605 * @param objectMgrPaths Mappings from connection name to DBus object path that
1606 * implements ObjectManager.
1607 * @param callback Callback to invoke when inventory items have been obtained.
1608 */
1609template <typename Callback>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001610static void getInventoryItemAssociations(
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001611 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1612 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
1613 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1614 objectMgrPaths,
1615 Callback&& callback)
1616{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001617 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001618
1619 // Response handler for GetManagedObjects
1620 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
1621 sensorNames](const boost::system::error_code ec,
1622 dbus::utility::ManagedObjectType& resp) {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001623 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations respHandler enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001624 if (ec)
1625 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001626 BMCWEB_LOG_ERROR
1627 << "getInventoryItemAssociations respHandler DBus error " << ec;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001628 messages::internalError(sensorsAsyncResp->res);
1629 return;
1630 }
1631
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001632 // Create vector to hold list of inventory items
1633 std::shared_ptr<std::vector<InventoryItem>> inventoryItems =
1634 std::make_shared<std::vector<InventoryItem>>();
1635
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001636 // Loop through returned object paths
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001637 std::string sensorAssocPath;
1638 sensorAssocPath.reserve(128); // avoid memory allocations
1639 for (const auto& objDictEntry : resp)
1640 {
1641 const std::string& objPath =
1642 static_cast<const std::string&>(objDictEntry.first);
1643 const boost::container::flat_map<
1644 std::string, boost::container::flat_map<
1645 std::string, dbus::utility::DbusVariantType>>&
1646 interfacesDict = objDictEntry.second;
1647
1648 // If path is inventory association for one of the specified sensors
1649 for (const std::string& sensorName : *sensorNames)
1650 {
1651 sensorAssocPath = sensorName;
1652 sensorAssocPath += "/inventory";
1653 if (objPath == sensorAssocPath)
1654 {
1655 // Get Association interface for object path
1656 auto assocIt =
1657 interfacesDict.find("xyz.openbmc_project.Association");
1658 if (assocIt != interfacesDict.end())
1659 {
1660 // Get inventory item from end point
1661 auto endpointsIt = assocIt->second.find("endpoints");
1662 if (endpointsIt != assocIt->second.end())
1663 {
1664 const std::vector<std::string>* endpoints =
1665 std::get_if<std::vector<std::string>>(
1666 &endpointsIt->second);
1667 if ((endpoints != nullptr) && !endpoints->empty())
1668 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001669 // Add inventory item to vector
1670 const std::string& invItemPath =
1671 endpoints->front();
1672 addInventoryItem(inventoryItems, invItemPath,
1673 sensorName);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001674 }
1675 }
1676 }
1677 break;
1678 }
1679 }
1680 }
1681
Anthony Wilsond5005492019-07-31 16:34:17 -05001682 // Now loop through the returned object paths again, this time to
1683 // find the leds associated with the inventory items we just found
1684 std::string inventoryAssocPath;
1685 inventoryAssocPath.reserve(128); // avoid memory allocations
1686 for (const auto& objDictEntry : resp)
1687 {
1688 const std::string& objPath =
1689 static_cast<const std::string&>(objDictEntry.first);
1690 const boost::container::flat_map<
1691 std::string, boost::container::flat_map<
1692 std::string, dbus::utility::DbusVariantType>>&
1693 interfacesDict = objDictEntry.second;
1694
1695 for (InventoryItem& inventoryItem : *inventoryItems)
1696 {
1697 inventoryAssocPath = inventoryItem.objectPath;
1698 inventoryAssocPath += "/leds";
1699 if (objPath == inventoryAssocPath)
1700 {
1701 // Get Association interface for object path
1702 auto assocIt =
1703 interfacesDict.find("xyz.openbmc_project.Association");
1704 if (assocIt != interfacesDict.end())
1705 {
1706 // Get inventory item from end point
1707 auto endpointsIt = assocIt->second.find("endpoints");
1708 if (endpointsIt != assocIt->second.end())
1709 {
1710 const std::vector<std::string>* endpoints =
1711 std::get_if<std::vector<std::string>>(
1712 &endpointsIt->second);
1713 if ((endpoints != nullptr) && !endpoints->empty())
1714 {
1715 // Store LED path in inventory item
1716 const std::string& ledPath = endpoints->front();
1717 inventoryItem.ledObjectPath = ledPath;
1718 }
1719 }
1720 }
1721 break;
1722 }
1723 }
1724 }
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001725 callback(inventoryItems);
1726 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations respHandler exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001727 };
1728
1729 // Find DBus object path that implements ObjectManager for ObjectMapper
1730 std::string connection = "xyz.openbmc_project.ObjectMapper";
1731 auto iter = objectMgrPaths->find(connection);
1732 const std::string& objectMgrPath =
1733 (iter != objectMgrPaths->end()) ? iter->second : "/";
1734 BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
1735 << objectMgrPath;
1736
1737 // Call GetManagedObjects on the ObjectMapper to get all associations
1738 crow::connections::systemBus->async_method_call(
1739 std::move(respHandler), connection, objectMgrPath,
1740 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1741
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001742 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001743}
1744
1745/**
Anthony Wilsond5005492019-07-31 16:34:17 -05001746 * @brief Gets D-Bus data for inventory item leds associated with sensors.
1747 *
1748 * Uses the specified connections (services) to obtain D-Bus data for inventory
1749 * item leds associated with sensors. Stores the resulting data in the
1750 * inventoryItems vector.
1751 *
1752 * This data is later used to provide sensor property values in the JSON
1753 * response.
1754 *
1755 * Finds the inventory item led data asynchronously. Invokes callback when data
1756 * has been obtained.
1757 *
1758 * The callback must have the following signature:
1759 * @code
Gunnar Mills42cbe532019-08-15 15:26:54 -05001760 * callback()
Anthony Wilsond5005492019-07-31 16:34:17 -05001761 * @endcode
1762 *
1763 * This function is called recursively, obtaining data asynchronously from one
1764 * connection in each call. This ensures the callback is not invoked until the
1765 * last asynchronous function has completed.
1766 *
1767 * @param sensorsAsyncResp Pointer to object holding response data.
1768 * @param inventoryItems D-Bus inventory items associated with sensors.
1769 * @param ledConnections Connections that provide data for the inventory leds.
1770 * @param callback Callback to invoke when inventory data has been obtained.
1771 * @param ledConnectionsIndex Current index in ledConnections. Only specified
1772 * in recursive calls to this function.
1773 */
1774template <typename Callback>
1775void getInventoryLedData(
1776 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1777 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1778 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1779 ledConnections,
1780 Callback&& callback, size_t ledConnectionsIndex = 0)
1781{
1782 BMCWEB_LOG_DEBUG << "getInventoryLedData enter";
1783
1784 // If no more connections left, call callback
1785 if (ledConnectionsIndex >= ledConnections->size())
1786 {
Gunnar Mills42cbe532019-08-15 15:26:54 -05001787 callback();
Anthony Wilsond5005492019-07-31 16:34:17 -05001788 BMCWEB_LOG_DEBUG << "getInventoryLedData exit";
1789 return;
1790 }
1791
1792 // Get inventory item data from current connection
1793 auto it = ledConnections->nth(ledConnectionsIndex);
1794 if (it != ledConnections->end())
1795 {
1796 const std::string& ledPath = (*it).first;
1797 const std::string& ledConnection = (*it).second;
1798 // Response handler for Get State property
1799 auto respHandler =
1800 [sensorsAsyncResp, inventoryItems, ledConnections, ledPath,
1801 callback{std::move(callback)},
1802 ledConnectionsIndex](const boost::system::error_code ec,
1803 const std::variant<std::string>& ledState) {
1804 BMCWEB_LOG_DEBUG << "getInventoryLedData respHandler enter";
1805 if (ec)
1806 {
1807 BMCWEB_LOG_ERROR
1808 << "getInventoryLedData respHandler DBus error " << ec;
1809 messages::internalError(sensorsAsyncResp->res);
1810 return;
1811 }
1812
1813 const std::string* state = std::get_if<std::string>(&ledState);
1814 if (state != nullptr)
1815 {
1816 BMCWEB_LOG_DEBUG << "Led state: " << *state;
1817 // Find inventory item with this LED object path
1818 InventoryItem* inventoryItem =
1819 findInventoryItemForLed(*inventoryItems, ledPath);
1820 if (inventoryItem != nullptr)
1821 {
1822 // Store LED state in InventoryItem
1823 if (boost::ends_with(*state, "On"))
1824 {
1825 inventoryItem->ledState = LedState::ON;
1826 }
1827 else if (boost::ends_with(*state, "Blink"))
1828 {
1829 inventoryItem->ledState = LedState::BLINK;
1830 }
1831 else if (boost::ends_with(*state, "Off"))
1832 {
1833 inventoryItem->ledState = LedState::OFF;
1834 }
1835 else
1836 {
1837 inventoryItem->ledState = LedState::UNKNOWN;
1838 }
1839 }
1840 }
1841 else
1842 {
1843 BMCWEB_LOG_DEBUG << "Failed to find State data for LED: "
1844 << ledPath;
1845 }
1846
1847 // Recurse to get LED data from next connection
1848 getInventoryLedData(sensorsAsyncResp, inventoryItems,
1849 ledConnections, std::move(callback),
1850 ledConnectionsIndex + 1);
1851
1852 BMCWEB_LOG_DEBUG << "getInventoryLedData respHandler exit";
1853 };
1854
1855 // Get the State property for the current LED
1856 crow::connections::systemBus->async_method_call(
1857 std::move(respHandler), ledConnection, ledPath,
1858 "org.freedesktop.DBus.Properties", "Get",
1859 "xyz.openbmc_project.Led.Physical", "State");
1860 }
1861
1862 BMCWEB_LOG_DEBUG << "getInventoryLedData exit";
1863}
1864
1865/**
1866 * @brief Gets LED data for LEDs associated with given inventory items.
1867 *
1868 * Gets the D-Bus connections (services) that provide LED data for the LEDs
1869 * associated with the specified inventory items. Then gets the LED data from
1870 * each connection and stores it in the inventory item.
1871 *
1872 * This data is later used to provide sensor property values in the JSON
1873 * response.
1874 *
1875 * Finds the LED data asynchronously. Invokes callback when information has
1876 * been obtained.
1877 *
1878 * The callback must have the following signature:
1879 * @code
Gunnar Mills42cbe532019-08-15 15:26:54 -05001880 * callback()
Anthony Wilsond5005492019-07-31 16:34:17 -05001881 * @endcode
1882 *
1883 * @param sensorsAsyncResp Pointer to object holding response data.
1884 * @param inventoryItems D-Bus inventory items associated with sensors.
1885 * @param callback Callback to invoke when inventory items have been obtained.
1886 */
1887template <typename Callback>
1888void getInventoryLeds(
1889 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1890 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1891 Callback&& callback)
1892{
1893 BMCWEB_LOG_DEBUG << "getInventoryLeds enter";
1894
1895 const std::string path = "/xyz/openbmc_project";
1896 const std::array<std::string, 1> interfaces = {
1897 "xyz.openbmc_project.Led.Physical"};
1898
1899 // Response handler for parsing output from GetSubTree
1900 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
1901 inventoryItems](const boost::system::error_code ec,
1902 const GetSubTreeType& subtree) {
1903 BMCWEB_LOG_DEBUG << "getInventoryLeds respHandler enter";
1904 if (ec)
1905 {
1906 messages::internalError(sensorsAsyncResp->res);
1907 BMCWEB_LOG_ERROR << "getInventoryLeds respHandler DBus error "
1908 << ec;
1909 return;
1910 }
1911
1912 // Build map of LED object paths to connections
1913 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1914 ledConnections = std::make_shared<
1915 boost::container::flat_map<std::string, std::string>>();
1916
1917 // Loop through objects from GetSubTree
1918 for (const std::pair<
1919 std::string,
1920 std::vector<std::pair<std::string, std::vector<std::string>>>>&
1921 object : subtree)
1922 {
1923 // Check if object path is LED for one of the specified inventory
1924 // items
1925 const std::string& ledPath = object.first;
1926 if (findInventoryItemForLed(*inventoryItems, ledPath) != nullptr)
1927 {
1928 // Add mapping from ledPath to connection
1929 const std::string& connection = object.second.begin()->first;
1930 (*ledConnections)[ledPath] = connection;
1931 BMCWEB_LOG_DEBUG << "Added mapping " << ledPath << " -> "
1932 << connection;
1933 }
1934 }
1935
1936 getInventoryLedData(sensorsAsyncResp, inventoryItems, ledConnections,
1937 std::move(callback));
1938 BMCWEB_LOG_DEBUG << "getInventoryLeds respHandler exit";
1939 };
1940 // Make call to ObjectMapper to find all inventory items
1941 crow::connections::systemBus->async_method_call(
1942 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
1943 "/xyz/openbmc_project/object_mapper",
1944 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 0, interfaces);
1945 BMCWEB_LOG_DEBUG << "getInventoryLeds exit";
1946}
1947
1948/**
Gunnar Mills42cbe532019-08-15 15:26:54 -05001949 * @brief Gets D-Bus data for Power Supply Attributes such as EfficiencyPercent
1950 *
1951 * Uses the specified connections (services) (currently assumes just one) to
1952 * obtain D-Bus data for Power Supply Attributes. Stores the resulting data in
1953 * the inventoryItems vector. Only stores data in Power Supply inventoryItems.
1954 *
1955 * This data is later used to provide sensor property values in the JSON
1956 * response.
1957 *
1958 * Finds the Power Supply Attributes data asynchronously. Invokes callback
1959 * when data has been obtained.
1960 *
1961 * The callback must have the following signature:
1962 * @code
1963 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
1964 * @endcode
1965 *
1966 * @param sensorsAsyncResp Pointer to object holding response data.
1967 * @param inventoryItems D-Bus inventory items associated with sensors.
1968 * @param psAttributesConnections Connections that provide data for the Power
1969 * Supply Attributes
1970 * @param callback Callback to invoke when data has been obtained.
1971 */
1972template <typename Callback>
1973void getPowerSupplyAttributesData(
1974 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1975 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1976 const boost::container::flat_map<std::string, std::string>&
1977 psAttributesConnections,
1978 Callback&& callback)
1979{
1980 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData enter";
1981
1982 if (psAttributesConnections.empty())
1983 {
1984 BMCWEB_LOG_DEBUG << "Can't find PowerSupplyAttributes, no connections!";
1985 callback(inventoryItems);
1986 return;
1987 }
1988
1989 // Assuming just one connection (service) for now
1990 auto it = psAttributesConnections.nth(0);
1991
1992 const std::string& psAttributesPath = (*it).first;
1993 const std::string& psAttributesConnection = (*it).second;
1994
1995 // Response handler for Get DeratingFactor property
1996 auto respHandler = [sensorsAsyncResp, inventoryItems,
1997 callback{std::move(callback)}](
1998 const boost::system::error_code ec,
1999 const std::variant<uint32_t>& deratingFactor) {
2000 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData respHandler enter";
2001 if (ec)
2002 {
2003 BMCWEB_LOG_ERROR
2004 << "getPowerSupplyAttributesData respHandler DBus error " << ec;
2005 messages::internalError(sensorsAsyncResp->res);
2006 return;
2007 }
2008
2009 const uint32_t* value = std::get_if<uint32_t>(&deratingFactor);
2010 if (value != nullptr)
2011 {
2012 BMCWEB_LOG_DEBUG << "PS EfficiencyPercent value: " << *value;
2013 // Store value in Power Supply Inventory Items
2014 for (InventoryItem& inventoryItem : *inventoryItems)
2015 {
2016 if (inventoryItem.isPowerSupply == true)
2017 {
2018 inventoryItem.powerSupplyEfficiencyPercent =
2019 static_cast<int>(*value);
2020 }
2021 }
2022 }
2023 else
2024 {
2025 BMCWEB_LOG_DEBUG
2026 << "Failed to find EfficiencyPercent value for PowerSupplies";
2027 }
2028
2029 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData respHandler exit";
2030 callback(inventoryItems);
2031 };
2032
2033 // Get the DeratingFactor property for the PowerSupplyAttributes
2034 // Currently only property on the interface/only one we care about
2035 crow::connections::systemBus->async_method_call(
2036 std::move(respHandler), psAttributesConnection, psAttributesPath,
2037 "org.freedesktop.DBus.Properties", "Get",
2038 "xyz.openbmc_project.Control.PowerSupplyAttributes", "DeratingFactor");
2039
2040 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData exit";
2041}
2042
2043/**
2044 * @brief Gets the Power Supply Attributes such as EfficiencyPercent
2045 *
2046 * Gets the D-Bus connection (service) that provides Power Supply Attributes
2047 * data. Then gets the Power Supply Attributes data from the connection
2048 * (currently just assumes 1 connection) and stores the data in the inventory
2049 * item.
2050 *
2051 * This data is later used to provide sensor property values in the JSON
2052 * response. DeratingFactor on D-Bus is mapped to EfficiencyPercent on Redfish.
2053 *
2054 * Finds the Power Supply Attributes data asynchronously. Invokes callback
2055 * when information has been obtained.
2056 *
2057 * The callback must have the following signature:
2058 * @code
2059 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
2060 * @endcode
2061 *
2062 * @param sensorsAsyncResp Pointer to object holding response data.
2063 * @param inventoryItems D-Bus inventory items associated with sensors.
2064 * @param callback Callback to invoke when data has been obtained.
2065 */
2066template <typename Callback>
2067void getPowerSupplyAttributes(
2068 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
2069 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
2070 Callback&& callback)
2071{
2072 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes enter";
2073
2074 // Only need the power supply attributes when the Power Schema
2075 if (sensorsAsyncResp->chassisSubNode != "Power")
2076 {
2077 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes exit since not Power";
2078 callback(inventoryItems);
2079 return;
2080 }
2081
2082 const std::array<std::string, 1> interfaces = {
2083 "xyz.openbmc_project.Control.PowerSupplyAttributes"};
2084
2085 // Response handler for parsing output from GetSubTree
2086 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
2087 inventoryItems](const boost::system::error_code ec,
2088 const GetSubTreeType& subtree) {
2089 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes respHandler enter";
2090 if (ec)
2091 {
2092 messages::internalError(sensorsAsyncResp->res);
2093 BMCWEB_LOG_ERROR
2094 << "getPowerSupplyAttributes respHandler DBus error " << ec;
2095 return;
2096 }
2097 if (subtree.size() == 0)
2098 {
2099 BMCWEB_LOG_DEBUG << "Can't find Power Supply Attributes!";
2100 callback(inventoryItems);
2101 return;
2102 }
2103
2104 // Currently we only support 1 power supply attribute, use this for
2105 // all the power supplies. Build map of object path to connection.
2106 // Assume just 1 connection and 1 path for now.
2107 boost::container::flat_map<std::string, std::string>
2108 psAttributesConnections;
2109
2110 if (subtree[0].first.empty() || subtree[0].second.empty())
2111 {
2112 BMCWEB_LOG_DEBUG << "Power Supply Attributes mapper error!";
2113 callback(inventoryItems);
2114 return;
2115 }
2116
2117 const std::string& psAttributesPath = subtree[0].first;
2118 const std::string& connection = subtree[0].second.begin()->first;
2119
2120 if (connection.empty())
2121 {
2122 BMCWEB_LOG_DEBUG << "Power Supply Attributes mapper error!";
2123 callback(inventoryItems);
2124 return;
2125 }
2126
2127 psAttributesConnections[psAttributesPath] = connection;
2128 BMCWEB_LOG_DEBUG << "Added mapping " << psAttributesPath << " -> "
2129 << connection;
2130
2131 getPowerSupplyAttributesData(sensorsAsyncResp, inventoryItems,
2132 psAttributesConnections,
2133 std::move(callback));
2134 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes respHandler exit";
2135 };
2136 // Make call to ObjectMapper to find the PowerSupplyAttributes service
2137 crow::connections::systemBus->async_method_call(
2138 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
2139 "/xyz/openbmc_project/object_mapper",
2140 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
2141 "/xyz/openbmc_project", 0, interfaces);
2142 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes exit";
2143}
2144
2145/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002146 * @brief Gets inventory items associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002147 *
2148 * Finds the inventory items that are associated with the specified sensors.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002149 * Then gets D-Bus data for the inventory items, such as presence and VPD.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002150 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002151 * This data is later used to provide sensor property values in the JSON
2152 * response.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002153 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002154 * Finds the inventory items asynchronously. Invokes callback when the
2155 * inventory items have been obtained.
2156 *
2157 * The callback must have the following signature:
2158 * @code
2159 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
2160 * @endcode
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002161 *
2162 * @param sensorsAsyncResp Pointer to object holding response data.
2163 * @param sensorNames All sensors within the current chassis.
2164 * @param objectMgrPaths Mappings from connection name to DBus object path that
2165 * implements ObjectManager.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002166 * @param callback Callback to invoke when inventory items have been obtained.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002167 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002168template <typename Callback>
2169static void getInventoryItems(
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002170 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
2171 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
2172 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002173 objectMgrPaths,
2174 Callback&& callback)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002175{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002176 BMCWEB_LOG_DEBUG << "getInventoryItems enter";
2177 auto getInventoryItemAssociationsCb =
2178 [sensorsAsyncResp, objectMgrPaths, callback{std::move(callback)}](
2179 std::shared_ptr<std::vector<InventoryItem>> inventoryItems) {
2180 BMCWEB_LOG_DEBUG << "getInventoryItemAssociationsCb enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002181 auto getInventoryItemsConnectionsCb =
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002182 [sensorsAsyncResp, inventoryItems, objectMgrPaths,
2183 callback{std::move(callback)}](
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002184 std::shared_ptr<boost::container::flat_set<std::string>>
2185 invConnections) {
2186 BMCWEB_LOG_DEBUG << "getInventoryItemsConnectionsCb enter";
Anthony Wilsond5005492019-07-31 16:34:17 -05002187 auto getInventoryItemsDataCb =
2188 [sensorsAsyncResp, inventoryItems,
2189 callback{std::move(callback)}]() {
2190 BMCWEB_LOG_DEBUG << "getInventoryItemsDataCb enter";
Gunnar Mills42cbe532019-08-15 15:26:54 -05002191
2192 auto getInventoryLedsCb = [sensorsAsyncResp,
2193 inventoryItems,
2194 callback{std::move(
2195 callback)}]() {
2196 BMCWEB_LOG_DEBUG << "getInventoryLedsCb enter";
2197 // Find Power Supply Attributes and get the data
2198 getPowerSupplyAttributes(sensorsAsyncResp,
2199 inventoryItems,
2200 std::move(callback));
2201 BMCWEB_LOG_DEBUG << "getInventoryLedsCb exit";
2202 };
2203
Anthony Wilsond5005492019-07-31 16:34:17 -05002204 // Find led connections and get the data
2205 getInventoryLeds(sensorsAsyncResp, inventoryItems,
Gunnar Mills42cbe532019-08-15 15:26:54 -05002206 std::move(getInventoryLedsCb));
Anthony Wilsond5005492019-07-31 16:34:17 -05002207 BMCWEB_LOG_DEBUG << "getInventoryItemsDataCb exit";
2208 };
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002209
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002210 // Get inventory item data from connections
2211 getInventoryItemsData(sensorsAsyncResp, inventoryItems,
2212 invConnections, objectMgrPaths,
Anthony Wilsond5005492019-07-31 16:34:17 -05002213 std::move(getInventoryItemsDataCb));
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002214 BMCWEB_LOG_DEBUG << "getInventoryItemsConnectionsCb exit";
2215 };
2216
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002217 // Get connections that provide inventory item data
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002218 getInventoryItemsConnections(
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002219 sensorsAsyncResp, inventoryItems,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002220 std::move(getInventoryItemsConnectionsCb));
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002221 BMCWEB_LOG_DEBUG << "getInventoryItemAssociationsCb exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002222 };
2223
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002224 // Get associations from sensors to inventory items
2225 getInventoryItemAssociations(sensorsAsyncResp, sensorNames, objectMgrPaths,
2226 std::move(getInventoryItemAssociationsCb));
2227 BMCWEB_LOG_DEBUG << "getInventoryItems exit";
2228}
2229
2230/**
2231 * @brief Returns JSON PowerSupply object for the specified inventory item.
2232 *
2233 * Searches for a JSON PowerSupply object that matches the specified inventory
2234 * item. If one is not found, a new PowerSupply object is added to the JSON
2235 * array.
2236 *
2237 * Multiple sensors are often associated with one power supply inventory item.
2238 * As a result, multiple sensor values are stored in one JSON PowerSupply
2239 * object.
2240 *
2241 * @param powerSupplyArray JSON array containing Redfish PowerSupply objects.
2242 * @param inventoryItem Inventory item for the power supply.
2243 * @param chassisId Chassis that contains the power supply.
2244 * @return JSON PowerSupply object for the specified inventory item.
2245 */
2246static nlohmann::json& getPowerSupply(nlohmann::json& powerSupplyArray,
2247 const InventoryItem& inventoryItem,
2248 const std::string& chassisId)
2249{
2250 // Check if matching PowerSupply object already exists in JSON array
2251 for (nlohmann::json& powerSupply : powerSupplyArray)
2252 {
2253 if (powerSupply["MemberId"] == inventoryItem.name)
2254 {
2255 return powerSupply;
2256 }
2257 }
2258
2259 // Add new PowerSupply object to JSON array
2260 powerSupplyArray.push_back({});
2261 nlohmann::json& powerSupply = powerSupplyArray.back();
2262 powerSupply["@odata.id"] =
2263 "/redfish/v1/Chassis/" + chassisId + "/Power#/PowerSupplies/";
2264 powerSupply["MemberId"] = inventoryItem.name;
2265 powerSupply["Name"] = boost::replace_all_copy(inventoryItem.name, "_", " ");
2266 powerSupply["Manufacturer"] = inventoryItem.manufacturer;
2267 powerSupply["Model"] = inventoryItem.model;
2268 powerSupply["PartNumber"] = inventoryItem.partNumber;
2269 powerSupply["SerialNumber"] = inventoryItem.serialNumber;
Anthony Wilsond5005492019-07-31 16:34:17 -05002270 setLedState(powerSupply, &inventoryItem);
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002271
Gunnar Mills42cbe532019-08-15 15:26:54 -05002272 if (inventoryItem.powerSupplyEfficiencyPercent >= 0)
2273 {
2274 powerSupply["EfficiencyPercent"] =
2275 inventoryItem.powerSupplyEfficiencyPercent;
2276 }
2277
2278 powerSupply["Status"]["State"] = getState(&inventoryItem);
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002279 const char* health = inventoryItem.isFunctional ? "OK" : "Critical";
2280 powerSupply["Status"]["Health"] = health;
2281
2282 return powerSupply;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002283}
2284
2285/**
Shawn McCarneyde629b62019-03-08 10:42:51 -06002286 * @brief Gets the values of the specified sensors.
2287 *
2288 * Stores the results as JSON in the SensorsAsyncResp.
2289 *
2290 * Gets the sensor values asynchronously. Stores the results later when the
2291 * information has been obtained.
2292 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002293 * The sensorNames set contains all requested sensors for the current chassis.
Shawn McCarneyde629b62019-03-08 10:42:51 -06002294 *
2295 * To minimize the number of DBus calls, the DBus method
2296 * org.freedesktop.DBus.ObjectManager.GetManagedObjects() is used to get the
2297 * values of all sensors provided by a connection (service).
2298 *
2299 * The connections set contains all the connections that provide sensor values.
2300 *
2301 * The objectMgrPaths map contains mappings from a connection name to the
2302 * corresponding DBus object path that implements ObjectManager.
2303 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002304 * The InventoryItem vector contains D-Bus inventory items associated with the
2305 * sensors. Inventory item data is needed for some Redfish sensor properties.
2306 *
Shawn McCarneyde629b62019-03-08 10:42:51 -06002307 * @param SensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002308 * @param sensorNames All requested sensors within the current chassis.
Shawn McCarneyde629b62019-03-08 10:42:51 -06002309 * @param connections Connections that provide sensor values.
2310 * @param objectMgrPaths Mappings from connection name to DBus object path that
2311 * implements ObjectManager.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002312 * @param inventoryItems Inventory items associated with the sensors.
Shawn McCarneyde629b62019-03-08 10:42:51 -06002313 */
2314void getSensorData(
2315 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002316 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
Shawn McCarneyde629b62019-03-08 10:42:51 -06002317 const boost::container::flat_set<std::string>& connections,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002318 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002319 objectMgrPaths,
2320 std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
Shawn McCarneyde629b62019-03-08 10:42:51 -06002321{
2322 BMCWEB_LOG_DEBUG << "getSensorData enter";
2323 // Get managed objects from all services exposing sensors
2324 for (const std::string& connection : connections)
2325 {
2326 // Response handler to process managed objects
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002327 auto getManagedObjectsCb = [SensorsAsyncResp, sensorNames,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002328 inventoryItems](
Shawn McCarneyde629b62019-03-08 10:42:51 -06002329 const boost::system::error_code ec,
2330 ManagedObjectsVectorType& resp) {
2331 BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter";
2332 if (ec)
2333 {
2334 BMCWEB_LOG_ERROR << "getManagedObjectsCb DBUS error: " << ec;
2335 messages::internalError(SensorsAsyncResp->res);
2336 return;
2337 }
2338 // Go through all objects and update response with sensor data
2339 for (const auto& objDictEntry : resp)
2340 {
2341 const std::string& objPath =
2342 static_cast<const std::string&>(objDictEntry.first);
2343 BMCWEB_LOG_DEBUG << "getManagedObjectsCb parsing object "
2344 << objPath;
2345
Shawn McCarneyde629b62019-03-08 10:42:51 -06002346 std::vector<std::string> split;
2347 // Reserve space for
2348 // /xyz/openbmc_project/sensors/<name>/<subname>
2349 split.reserve(6);
2350 boost::algorithm::split(split, objPath, boost::is_any_of("/"));
2351 if (split.size() < 6)
2352 {
2353 BMCWEB_LOG_ERROR << "Got path that isn't long enough "
2354 << objPath;
2355 continue;
2356 }
2357 // These indexes aren't intuitive, as boost::split puts an empty
2358 // string at the beginning
2359 const std::string& sensorType = split[4];
2360 const std::string& sensorName = split[5];
2361 BMCWEB_LOG_DEBUG << "sensorName " << sensorName
2362 << " sensorType " << sensorType;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002363 if (sensorNames->find(objPath) == sensorNames->end())
Shawn McCarneyde629b62019-03-08 10:42:51 -06002364 {
2365 BMCWEB_LOG_ERROR << sensorName << " not in sensor list ";
2366 continue;
2367 }
2368
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002369 // Find inventory item (if any) associated with sensor
2370 InventoryItem* inventoryItem =
2371 findInventoryItemForSensor(inventoryItems, objPath);
2372
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002373 const std::string& sensorSchema =
2374 SensorsAsyncResp->chassisSubNode;
2375
2376 nlohmann::json* sensorJson = nullptr;
2377
2378 if (sensorSchema == "Sensors")
Shawn McCarneyde629b62019-03-08 10:42:51 -06002379 {
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002380 SensorsAsyncResp->res.jsonValue["@odata.id"] =
2381 "/redfish/v1/Chassis/" + SensorsAsyncResp->chassisId +
2382 "/" + SensorsAsyncResp->chassisSubNode + "/" +
2383 sensorName;
2384 sensorJson = &(SensorsAsyncResp->res.jsonValue);
Shawn McCarneyde629b62019-03-08 10:42:51 -06002385 }
2386 else
2387 {
Ed Tanous271584a2019-07-09 16:24:22 -07002388 std::string fieldName;
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002389 if (sensorType == "temperature")
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002390 {
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002391 fieldName = "Temperatures";
2392 }
2393 else if (sensorType == "fan" || sensorType == "fan_tach" ||
2394 sensorType == "fan_pwm")
2395 {
2396 fieldName = "Fans";
2397 }
2398 else if (sensorType == "voltage")
2399 {
2400 fieldName = "Voltages";
2401 }
2402 else if (sensorType == "power")
2403 {
2404 if (!sensorName.compare("total_power"))
2405 {
2406 fieldName = "PowerControl";
2407 }
2408 else if ((inventoryItem != nullptr) &&
2409 (inventoryItem->isPowerSupply))
2410 {
2411 fieldName = "PowerSupplies";
2412 }
2413 else
2414 {
2415 // Other power sensors are in SensorCollection
2416 continue;
2417 }
2418 }
2419 else
2420 {
2421 BMCWEB_LOG_ERROR << "Unsure how to handle sensorType "
2422 << sensorType;
2423 continue;
2424 }
2425
2426 nlohmann::json& tempArray =
2427 SensorsAsyncResp->res.jsonValue[fieldName];
2428 if (fieldName == "PowerControl")
2429 {
2430 if (tempArray.empty())
2431 {
2432 // Put multiple "sensors" into a single
2433 // PowerControl. Follows MemberId naming and
2434 // naming in power.hpp.
2435 tempArray.push_back(
2436 {{"@odata.id",
2437 "/redfish/v1/Chassis/" +
2438 SensorsAsyncResp->chassisId + "/" +
2439 SensorsAsyncResp->chassisSubNode + "#/" +
2440 fieldName + "/0"}});
2441 }
2442 sensorJson = &(tempArray.back());
2443 }
2444 else if (fieldName == "PowerSupplies")
2445 {
2446 if (inventoryItem != nullptr)
2447 {
2448 sensorJson =
2449 &(getPowerSupply(tempArray, *inventoryItem,
2450 SensorsAsyncResp->chassisId));
2451 }
2452 }
2453 else
2454 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002455 tempArray.push_back(
2456 {{"@odata.id",
2457 "/redfish/v1/Chassis/" +
2458 SensorsAsyncResp->chassisId + "/" +
2459 SensorsAsyncResp->chassisSubNode + "#/" +
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002460 fieldName + "/"}});
2461 sensorJson = &(tempArray.back());
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002462 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002463 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06002464
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002465 if (sensorJson != nullptr)
2466 {
2467 objectInterfacesToJson(sensorName, sensorType,
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002468 SensorsAsyncResp->chassisSubNode,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002469 objDictEntry.second, *sensorJson,
2470 inventoryItem);
2471 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06002472 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002473 if (SensorsAsyncResp.use_count() == 1)
James Feist8bd25cc2019-03-15 15:14:00 -07002474 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002475 sortJSONResponse(SensorsAsyncResp);
2476 if (SensorsAsyncResp->chassisSubNode == "Thermal")
2477 {
2478 populateFanRedundancy(SensorsAsyncResp);
2479 }
James Feist8bd25cc2019-03-15 15:14:00 -07002480 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06002481 BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit";
2482 };
2483
2484 // Find DBus object path that implements ObjectManager for the current
2485 // connection. If no mapping found, default to "/".
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002486 auto iter = objectMgrPaths->find(connection);
Shawn McCarneyde629b62019-03-08 10:42:51 -06002487 const std::string& objectMgrPath =
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002488 (iter != objectMgrPaths->end()) ? iter->second : "/";
Shawn McCarneyde629b62019-03-08 10:42:51 -06002489 BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
2490 << objectMgrPath;
2491
2492 crow::connections::systemBus->async_method_call(
2493 getManagedObjectsCb, connection, objectMgrPath,
2494 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
2495 };
2496 BMCWEB_LOG_DEBUG << "getSensorData exit";
2497}
2498
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002499void processSensorList(
2500 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
2501 std::shared_ptr<boost::container::flat_set<std::string>> sensorNames)
2502{
2503 auto getConnectionCb =
2504 [SensorsAsyncResp, sensorNames](
2505 const boost::container::flat_set<std::string>& connections) {
2506 BMCWEB_LOG_DEBUG << "getConnectionCb enter";
2507 auto getObjectManagerPathsCb =
2508 [SensorsAsyncResp, sensorNames, connections](
2509 std::shared_ptr<
2510 boost::container::flat_map<std::string, std::string>>
2511 objectMgrPaths) {
2512 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb enter";
2513 auto getInventoryItemsCb =
2514 [SensorsAsyncResp, sensorNames, connections,
2515 objectMgrPaths](
2516 std::shared_ptr<std::vector<InventoryItem>>
2517 inventoryItems) {
2518 BMCWEB_LOG_DEBUG << "getInventoryItemsCb enter";
2519 // Get sensor data and store results in JSON
2520 getSensorData(SensorsAsyncResp, sensorNames,
2521 connections, objectMgrPaths,
2522 inventoryItems);
2523 BMCWEB_LOG_DEBUG << "getInventoryItemsCb exit";
2524 };
2525
2526 // Get inventory items associated with sensors
2527 getInventoryItems(SensorsAsyncResp, sensorNames,
2528 objectMgrPaths,
2529 std::move(getInventoryItemsCb));
2530
2531 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb exit";
2532 };
2533
2534 // Get mapping from connection names to the DBus object
2535 // paths that implement the ObjectManager interface
2536 getObjectManagerPaths(SensorsAsyncResp,
2537 std::move(getObjectManagerPathsCb));
2538 BMCWEB_LOG_DEBUG << "getConnectionCb exit";
2539 };
2540
2541 // Get set of connections that provide sensor values
2542 getConnections(SensorsAsyncResp, sensorNames, std::move(getConnectionCb));
2543}
2544
Shawn McCarneyde629b62019-03-08 10:42:51 -06002545/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01002546 * @brief Entry point for retrieving sensors data related to requested
2547 * chassis.
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02002548 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01002549 */
Ed Tanous1abe55e2018-09-05 08:30:59 -07002550void getChassisData(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
2551{
2552 BMCWEB_LOG_DEBUG << "getChassisData enter";
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002553 auto getChassisCb =
2554 [SensorsAsyncResp](
2555 std::shared_ptr<boost::container::flat_set<std::string>>
2556 sensorNames) {
2557 BMCWEB_LOG_DEBUG << "getChassisCb enter";
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002558 processSensorList(SensorsAsyncResp, sensorNames);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002559 BMCWEB_LOG_DEBUG << "getChassisCb exit";
2560 };
Jennifer Lee4f9a2132019-03-04 12:45:19 -08002561 SensorsAsyncResp->res.jsonValue["Redundancy"] = nlohmann::json::array();
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01002562
Shawn McCarney26f03892019-05-03 13:20:24 -05002563 // Get set of sensors in chassis
Ed Tanous1abe55e2018-09-05 08:30:59 -07002564 getChassis(SensorsAsyncResp, std::move(getChassisCb));
2565 BMCWEB_LOG_DEBUG << "getChassisData exit";
Ed Tanous271584a2019-07-09 16:24:22 -07002566}
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01002567
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302568/**
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002569 * @brief Find the requested sensorName in the list of all sensors supplied by
2570 * the chassis node
2571 *
2572 * @param sensorName The sensor name supplied in the PATCH request
2573 * @param sensorsList The list of sensors managed by the chassis node
2574 * @param sensorsModified The list of sensors that were found as a result of
2575 * repeated calls to this function
2576 */
2577bool findSensorNameUsingSensorPath(
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05302578 std::string_view sensorName,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002579 boost::container::flat_set<std::string>& sensorsList,
2580 boost::container::flat_set<std::string>& sensorsModified)
2581{
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05302582 for (std::string_view chassisSensor : sensorsList)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002583 {
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05302584 std::size_t pos = chassisSensor.rfind("/");
2585 if (pos >= (chassisSensor.size() - 1))
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002586 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002587 continue;
2588 }
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05302589 std::string_view thisSensorName = chassisSensor.substr(pos + 1);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002590 if (thisSensorName == sensorName)
2591 {
2592 sensorsModified.emplace(chassisSensor);
2593 return true;
2594 }
2595 }
2596 return false;
2597}
2598
2599/**
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302600 * @brief Entry point for overriding sensor values of given sensor
2601 *
2602 * @param res response object
Carol Wang4bb3dc32019-10-17 18:15:02 +08002603 * @param allCollections Collections extract from sensors' request patch info
jayaprakash Mutyala91e130a2020-03-04 22:26:38 +00002604 * @param chassisSubNode Chassis Node for which the query has to happen
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302605 */
jayaprakash Mutyala70d1d0a2020-01-21 23:41:36 +00002606void setSensorsOverride(
Carol Wang4bb3dc32019-10-17 18:15:02 +08002607 std::shared_ptr<SensorsAsyncResp> sensorAsyncResp,
2608 std::unordered_map<std::string, std::vector<nlohmann::json>>&
jayaprakash Mutyala397fd612020-02-06 23:33:34 +00002609 allCollections)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302610{
jayaprakash Mutyala70d1d0a2020-01-21 23:41:36 +00002611 BMCWEB_LOG_INFO << "setSensorsOverride for subNode"
Carol Wang4bb3dc32019-10-17 18:15:02 +08002612 << sensorAsyncResp->chassisSubNode << "\n";
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302613
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302614 const char* propertyValueName;
2615 std::unordered_map<std::string, std::pair<double, std::string>> overrideMap;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302616 std::string memberId;
2617 double value;
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302618 for (auto& collectionItems : allCollections)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302619 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302620 if (collectionItems.first == "Temperatures")
2621 {
2622 propertyValueName = "ReadingCelsius";
2623 }
2624 else if (collectionItems.first == "Fans")
2625 {
2626 propertyValueName = "Reading";
2627 }
2628 else
2629 {
2630 propertyValueName = "ReadingVolts";
2631 }
2632 for (auto& item : collectionItems.second)
2633 {
Carol Wang4bb3dc32019-10-17 18:15:02 +08002634 if (!json_util::readJson(item, sensorAsyncResp->res, "MemberId",
2635 memberId, propertyValueName, value))
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302636 {
2637 return;
2638 }
2639 overrideMap.emplace(memberId,
2640 std::make_pair(value, collectionItems.first));
2641 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302642 }
Carol Wang4bb3dc32019-10-17 18:15:02 +08002643
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002644 auto getChassisSensorListCb = [sensorAsyncResp,
2645 overrideMap](const std::shared_ptr<
2646 boost::container::flat_set<
2647 std::string>>
2648 sensorsList) {
2649 // Match sensor names in the PATCH request to those managed by the
2650 // chassis node
2651 const std::shared_ptr<boost::container::flat_set<std::string>>
2652 sensorNames =
2653 std::make_shared<boost::container::flat_set<std::string>>();
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302654 for (const auto& item : overrideMap)
2655 {
2656 const auto& sensor = item.first;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002657 if (!findSensorNameUsingSensorPath(sensor, *sensorsList,
2658 *sensorNames))
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302659 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302660 BMCWEB_LOG_INFO << "Unable to find memberId " << item.first;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302661 messages::resourceNotFound(sensorAsyncResp->res,
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302662 item.second.second, item.first);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302663 return;
2664 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302665 }
2666 // Get the connection to which the memberId belongs
2667 auto getObjectsWithConnectionCb =
2668 [sensorAsyncResp, overrideMap](
2669 const boost::container::flat_set<std::string>& connections,
2670 const std::set<std::pair<std::string, std::string>>&
2671 objectsWithConnection) {
2672 if (objectsWithConnection.size() != overrideMap.size())
2673 {
2674 BMCWEB_LOG_INFO
2675 << "Unable to find all objects with proper connection "
2676 << objectsWithConnection.size() << " requested "
2677 << overrideMap.size() << "\n";
2678 messages::resourceNotFound(
2679 sensorAsyncResp->res,
2680 sensorAsyncResp->chassisSubNode == "Thermal"
2681 ? "Temperatures"
2682 : "Voltages",
2683 "Count");
2684 return;
2685 }
2686 for (const auto& item : objectsWithConnection)
2687 {
2688
2689 auto lastPos = item.first.rfind('/');
2690 if (lastPos == std::string::npos)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302691 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302692 messages::internalError(sensorAsyncResp->res);
2693 return;
2694 }
2695 std::string sensorName = item.first.substr(lastPos + 1);
2696
2697 const auto& iterator = overrideMap.find(sensorName);
2698 if (iterator == overrideMap.end())
2699 {
2700 BMCWEB_LOG_INFO << "Unable to find sensor object"
2701 << item.first << "\n";
2702 messages::internalError(sensorAsyncResp->res);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302703 return;
2704 }
2705 crow::connections::systemBus->async_method_call(
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302706 [sensorAsyncResp](const boost::system::error_code ec) {
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302707 if (ec)
2708 {
2709 BMCWEB_LOG_DEBUG
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302710 << "setOverrideValueStatus DBUS error: "
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302711 << ec;
2712 messages::internalError(sensorAsyncResp->res);
2713 return;
2714 }
2715 },
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302716 item.second, item.first,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302717 "org.freedesktop.DBus.Properties", "Set",
2718 "xyz.openbmc_project.Sensor.Value", "Value",
Patrick Williams19bd78d2020-05-13 17:38:24 -05002719 std::variant<double>(iterator->second.first));
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302720 }
2721 };
2722 // Get object with connection for the given sensor name
2723 getObjectsWithConnection(sensorAsyncResp, sensorNames,
2724 std::move(getObjectsWithConnectionCb));
2725 };
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302726 // get full sensor list for the given chassisId and cross verify the sensor.
2727 getChassis(sensorAsyncResp, std::move(getChassisSensorListCb));
2728}
2729
jayaprakash Mutyala70d1d0a2020-01-21 23:41:36 +00002730bool isOverridingAllowed(const std::string& manufacturingModeStatus)
2731{
2732 if (manufacturingModeStatus ==
2733 "xyz.openbmc_project.Control.Security.SpecialMode.Modes.Manufacturing")
2734 {
2735 return true;
2736 }
2737
2738#ifdef BMCWEB_ENABLE_VALIDATION_UNSECURE_FEATURE
2739 if (manufacturingModeStatus == "xyz.openbmc_project.Control.Security."
2740 "SpecialMode.Modes.ValidationUnsecure")
2741 {
2742 return true;
2743 }
2744
2745#endif
2746
2747 return false;
2748}
2749
2750/**
2751 * @brief Entry point for Checking the manufacturing mode before doing sensor
2752 * override values of given sensor
2753 *
2754 * @param res response object
2755 * @param allCollections Collections extract from sensors' request patch info
jayaprakash Mutyala397fd612020-02-06 23:33:34 +00002756 * @param chassisSubNode Chassis Node for which the query has to happen
jayaprakash Mutyala70d1d0a2020-01-21 23:41:36 +00002757 */
2758void checkAndDoSensorsOverride(
2759 std::shared_ptr<SensorsAsyncResp> sensorAsyncResp,
jayaprakash Mutyala397fd612020-02-06 23:33:34 +00002760 std::unordered_map<std::string, std::vector<nlohmann::json>>&
2761 allCollections)
jayaprakash Mutyala70d1d0a2020-01-21 23:41:36 +00002762{
2763 BMCWEB_LOG_INFO << "checkAndDoSensorsOverride for subnode"
2764 << sensorAsyncResp->chassisSubNode << "\n";
2765
2766 const std::array<std::string, 1> interfaces = {
2767 "xyz.openbmc_project.Security.SpecialMode"};
2768
2769 crow::connections::systemBus->async_method_call(
jayaprakash Mutyala397fd612020-02-06 23:33:34 +00002770 [sensorAsyncResp, allCollections](const boost::system::error_code ec,
2771 const GetSubTreeType& resp) mutable {
jayaprakash Mutyala70d1d0a2020-01-21 23:41:36 +00002772 if (ec)
2773 {
2774 BMCWEB_LOG_DEBUG
2775 << "Error in querying GetSubTree with Object Mapper. "
2776 << ec;
2777 messages::internalError(sensorAsyncResp->res);
2778 return;
2779 }
jayaprakash Mutyala91e130a2020-03-04 22:26:38 +00002780#ifdef BMCWEB_INSECURE_UNRESTRICTED_SENSOR_OVERRIDE
2781 // Proceed with sensor override
2782 setSensorsOverride(sensorAsyncResp, allCollections);
2783 return;
2784#endif
jayaprakash Mutyala70d1d0a2020-01-21 23:41:36 +00002785
2786 if (resp.size() != 1)
2787 {
jayaprakash Mutyala91e130a2020-03-04 22:26:38 +00002788 BMCWEB_LOG_WARNING
2789 << "Overriding sensor value is not allowed - Internal "
2790 "error in querying SpecialMode property.";
jayaprakash Mutyala70d1d0a2020-01-21 23:41:36 +00002791 messages::internalError(sensorAsyncResp->res);
2792 return;
2793 }
2794 const std::string& path = resp[0].first;
2795 const std::string& serviceName = resp[0].second.begin()->first;
2796
2797 if (path.empty() || serviceName.empty())
2798 {
2799 BMCWEB_LOG_DEBUG
2800 << "Path or service name is returned as empty. ";
2801 messages::internalError(sensorAsyncResp->res);
2802 return;
2803 }
2804
2805 // Sensor override is allowed only in manufacturing mode or
2806 // validation unsecure mode .
2807 crow::connections::systemBus->async_method_call(
jayaprakash Mutyala397fd612020-02-06 23:33:34 +00002808 [sensorAsyncResp, allCollections,
jayaprakash Mutyala70d1d0a2020-01-21 23:41:36 +00002809 path](const boost::system::error_code ec,
2810 std::variant<std::string>& getManufactMode) mutable {
2811 if (ec)
2812 {
2813 BMCWEB_LOG_DEBUG
2814 << "Error in querying Special mode property " << ec;
2815 messages::internalError(sensorAsyncResp->res);
2816 return;
2817 }
2818
2819 const std::string* manufacturingModeStatus =
2820 std::get_if<std::string>(&getManufactMode);
2821
2822 if (nullptr == manufacturingModeStatus)
2823 {
2824 BMCWEB_LOG_DEBUG << "Sensor override mode is not "
2825 "Enabled. Returning ... ";
2826 messages::internalError(sensorAsyncResp->res);
2827 return;
2828 }
2829
2830 if (isOverridingAllowed(*manufacturingModeStatus))
2831 {
2832 BMCWEB_LOG_INFO << "Manufacturing mode is Enabled. "
2833 "Proceeding further... ";
jayaprakash Mutyala397fd612020-02-06 23:33:34 +00002834 setSensorsOverride(sensorAsyncResp, allCollections);
jayaprakash Mutyala70d1d0a2020-01-21 23:41:36 +00002835 }
2836 else
2837 {
2838 BMCWEB_LOG_WARNING
2839 << "Manufacturing mode is not Enabled...can't "
2840 "Override the sensor value. ";
2841
2842 messages::actionNotSupported(
2843 sensorAsyncResp->res,
2844 "Overriding of Sensor Value for non "
2845 "manufacturing mode");
2846 return;
2847 }
2848 },
2849 serviceName, path, "org.freedesktop.DBus.Properties", "Get",
2850 "xyz.openbmc_project.Security.SpecialMode", "SpecialMode");
2851 },
2852
2853 "xyz.openbmc_project.ObjectMapper",
2854 "/xyz/openbmc_project/object_mapper",
2855 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 5, interfaces);
2856}
2857
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002858class SensorCollection : public Node
2859{
2860 public:
2861 SensorCollection(CrowApp& app) :
Gunnar Millsf99c3792020-04-14 21:54:42 -05002862 Node(app, "/redfish/v1/Chassis/<str>/Sensors/", std::string())
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002863 {
2864 entityPrivileges = {
2865 {boost::beast::http::verb::get, {{"Login"}}},
2866 {boost::beast::http::verb::head, {{"Login"}}},
2867 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2868 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2869 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2870 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2871 }
2872
2873 private:
2874 std::vector<const char*> typeList = {
2875 "/xyz/openbmc_project/sensors/power",
Adrian Ambrożewiczf8ede152020-06-02 13:26:33 +02002876 "/xyz/openbmc_project/sensors/current",
2877 "/xyz/openbmc_project/sensors/utilization"};
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002878 void doGet(crow::Response& res, const crow::Request& req,
2879 const std::vector<std::string>& params) override
2880 {
2881 BMCWEB_LOG_DEBUG << "SensorCollection doGet enter";
2882 if (params.size() != 1)
2883 {
2884 BMCWEB_LOG_DEBUG << "SensorCollection doGet param size < 1";
2885 messages::internalError(res);
2886 res.end();
2887 return;
2888 }
2889
2890 const std::string& chassisId = params[0];
2891 std::shared_ptr<SensorsAsyncResp> asyncResp =
2892 std::make_shared<SensorsAsyncResp>(res, chassisId, typeList,
2893 "Sensors");
2894
2895 auto getChassisCb =
2896 [asyncResp](std::shared_ptr<boost::container::flat_set<std::string>>
2897 sensorNames) {
2898 BMCWEB_LOG_DEBUG << "getChassisCb enter";
2899
2900 nlohmann::json& entriesArray =
2901 asyncResp->res.jsonValue["Members"];
2902 for (auto& sensor : *sensorNames)
2903 {
2904 BMCWEB_LOG_DEBUG << "Adding sensor: " << sensor;
2905
2906 std::size_t lastPos = sensor.rfind("/");
2907 if (lastPos == std::string::npos ||
2908 lastPos + 1 >= sensor.size())
2909 {
2910 BMCWEB_LOG_ERROR << "Invalid sensor path: " << sensor;
2911 messages::internalError(asyncResp->res);
2912 return;
2913 }
2914 std::string sensorName = sensor.substr(lastPos + 1);
2915 entriesArray.push_back(
2916 {{"@odata.id",
2917 "/redfish/v1/Chassis/" + asyncResp->chassisId + "/" +
2918 asyncResp->chassisSubNode + "/" + sensorName}});
2919 }
2920
2921 asyncResp->res.jsonValue["Members@odata.count"] =
2922 entriesArray.size();
2923 BMCWEB_LOG_DEBUG << "getChassisCb exit";
2924 };
2925
2926 // Get set of sensors in chassis
2927 getChassis(asyncResp, std::move(getChassisCb));
2928 BMCWEB_LOG_DEBUG << "SensorCollection doGet exit";
2929 }
2930};
2931
2932class Sensor : public Node
2933{
2934 public:
2935 Sensor(CrowApp& app) :
2936 Node(app, "/redfish/v1/Chassis/<str>/Sensors/<str>/", std::string(),
2937 std::string())
2938 {
2939 entityPrivileges = {
2940 {boost::beast::http::verb::get, {{"Login"}}},
2941 {boost::beast::http::verb::head, {{"Login"}}},
2942 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2943 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2944 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2945 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2946 }
2947
2948 private:
2949 void doGet(crow::Response& res, const crow::Request& req,
2950 const std::vector<std::string>& params) override
2951 {
2952 BMCWEB_LOG_DEBUG << "Sensor doGet enter";
2953 if (params.size() != 2)
2954 {
2955 BMCWEB_LOG_DEBUG << "Sensor doGet param size < 2";
2956 messages::internalError(res);
2957 res.end();
2958 return;
2959 }
2960 const std::string& chassisId = params[0];
2961 std::shared_ptr<SensorsAsyncResp> asyncResp =
2962 std::make_shared<SensorsAsyncResp>(
2963 res, chassisId, std::vector<const char*>(), "Sensors");
2964
2965 const std::string& sensorName = params[1];
2966 const std::array<const char*, 1> interfaces = {
2967 "xyz.openbmc_project.Sensor.Value"};
2968
2969 // Get a list of all of the sensors that implement Sensor.Value
2970 // and get the path and service name associated with the sensor
2971 crow::connections::systemBus->async_method_call(
2972 [asyncResp, sensorName](const boost::system::error_code ec,
2973 const GetSubTreeType& subtree) {
2974 BMCWEB_LOG_DEBUG << "respHandler1 enter";
2975 if (ec)
2976 {
2977 messages::internalError(asyncResp->res);
2978 BMCWEB_LOG_ERROR << "Sensor getSensorPaths resp_handler: "
2979 << "Dbus error " << ec;
2980 return;
2981 }
2982
2983 GetSubTreeType::const_iterator it = std::find_if(
2984 subtree.begin(), subtree.end(),
2985 [sensorName](
2986 const std::pair<
2987 std::string,
2988 std::vector<std::pair<std::string,
2989 std::vector<std::string>>>>&
2990 object) {
2991 std::string_view sensor = object.first;
2992 std::size_t lastPos = sensor.rfind("/");
2993 if (lastPos == std::string::npos ||
2994 lastPos + 1 >= sensor.size())
2995 {
2996 BMCWEB_LOG_ERROR << "Invalid sensor path: "
2997 << sensor;
2998 return false;
2999 }
3000 std::string_view name = sensor.substr(lastPos + 1);
3001
3002 return name == sensorName;
3003 });
3004
3005 if (it == subtree.end())
3006 {
3007 BMCWEB_LOG_ERROR << "Could not find path for sensor: "
3008 << sensorName;
3009 messages::resourceNotFound(asyncResp->res, "Sensor",
3010 sensorName);
3011 return;
3012 }
3013 std::string_view sensorPath = (*it).first;
3014 BMCWEB_LOG_DEBUG << "Found sensor path for sensor '"
3015 << sensorName << "': " << sensorPath;
3016
3017 const std::shared_ptr<boost::container::flat_set<std::string>>
3018 sensorList = std::make_shared<
3019 boost::container::flat_set<std::string>>();
3020
3021 sensorList->emplace(sensorPath);
3022 processSensorList(asyncResp, sensorList);
3023 BMCWEB_LOG_DEBUG << "respHandler1 exit";
3024 },
3025 "xyz.openbmc_project.ObjectMapper",
3026 "/xyz/openbmc_project/object_mapper",
3027 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
3028 "/xyz/openbmc_project/sensors", 2, interfaces);
3029 }
3030};
3031
Ed Tanous1abe55e2018-09-05 08:30:59 -07003032} // namespace redfish