blob: 628e6ff6b6c4c638b9430064ebc73179e4c5b256 [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 Tanous9e27a222019-10-24 13:46:39 -070024#include <cmath>
Ed Tanous1abe55e2018-09-05 08:30:59 -070025#include <dbus_singleton.hpp>
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +053026#include <utils/json_utils.hpp>
Ed Tanousabf2add2019-01-22 16:40:12 -080027#include <variant>
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010028
Ed Tanous1abe55e2018-09-05 08:30:59 -070029namespace redfish
30{
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010031
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010032using GetSubTreeType = std::vector<
33 std::pair<std::string,
34 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
35
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -050036using SensorVariant =
37 std::variant<int64_t, double, uint32_t, bool, std::string>;
Ed Tanousaa2e59c2018-04-12 12:17:20 -070038
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010039using ManagedObjectsVectorType = std::vector<std::pair<
Ed Tanousaa2e59c2018-04-12 12:17:20 -070040 sdbusplus::message::object_path,
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010041 boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -070042 std::string, boost::container::flat_map<std::string, SensorVariant>>>>;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010043
44/**
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020045 * SensorsAsyncResp
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010046 * Gathers data needed for response processing after async calls are done
47 */
Ed Tanous1abe55e2018-09-05 08:30:59 -070048class SensorsAsyncResp
49{
50 public:
Ed Tanous271584a2019-07-09 16:24:22 -070051 SensorsAsyncResp(crow::Response& response, const std::string& chassisIdIn,
52 const std::vector<const char*> typesIn,
Ed Tanous2474adf2018-09-05 16:31:16 -070053 const std::string& subNode) :
Ed Tanous43b761d2019-02-13 20:10:56 -080054 res(response),
Ed Tanous271584a2019-07-09 16:24:22 -070055 chassisId(chassisIdIn), types(typesIn), chassisSubNode(subNode)
Ed Tanous1abe55e2018-09-05 08:30:59 -070056 {
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010057 }
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";
387 sensorsAsyncResp->res.jsonValue["@odata.context"] =
388 "/redfish/v1/$metadata#SensorCollection.SensorCollection";
389 sensorsAsyncResp->res.jsonValue["Description"] =
390 "Collection of Sensors for this Chassis";
391 sensorsAsyncResp->res.jsonValue["Members"] =
392 nlohmann::json::array();
393 sensorsAsyncResp->res.jsonValue["Members@odata.count"] = 0;
394 }
395
396 if (chassisSubNode != "Sensors")
397 {
398 sensorsAsyncResp->res.jsonValue["Id"] = chassisSubNode;
399 sensorsAsyncResp->res.jsonValue["@odata.context"] =
400 "/redfish/v1/$metadata#" + chassisSubNode + "." +
401 chassisSubNode;
402 }
403
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700404 sensorsAsyncResp->res.jsonValue["@odata.id"] =
405 "/redfish/v1/Chassis/" + sensorsAsyncResp->chassisId + "/" +
406 chassisSubNode;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700407 sensorsAsyncResp->res.jsonValue["Name"] = chassisSubNode;
408
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500409 // Get the list of all sensors for this Chassis element
410 std::string sensorPath = *chassisPath + "/all_sensors";
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700411 crow::connections::systemBus->async_method_call(
412 [sensorsAsyncResp, callback{std::move(callback)}](
Ed Tanous271584a2019-07-09 16:24:22 -0700413 const boost::system::error_code& e,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700414 const std::variant<std::vector<std::string>>&
415 variantEndpoints) {
Ed Tanous271584a2019-07-09 16:24:22 -0700416 if (e)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700417 {
Ed Tanous271584a2019-07-09 16:24:22 -0700418 if (e.value() != EBADR)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700419 {
420 messages::internalError(sensorsAsyncResp->res);
421 return;
422 }
423 }
424 const std::vector<std::string>* nodeSensorList =
425 std::get_if<std::vector<std::string>>(&(variantEndpoints));
426 if (nodeSensorList == nullptr)
427 {
428 messages::resourceNotFound(
429 sensorsAsyncResp->res, sensorsAsyncResp->chassisSubNode,
430 sensorsAsyncResp->chassisSubNode == "Thermal"
431 ? "Temperatures"
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500432 : sensorsAsyncResp->chassisSubNode == "Power"
433 ? "Voltages"
434 : "Sensors");
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700435 return;
436 }
437 const std::shared_ptr<boost::container::flat_set<std::string>>
438 culledSensorList = std::make_shared<
439 boost::container::flat_set<std::string>>();
440 reduceSensorList(sensorsAsyncResp, nodeSensorList,
441 culledSensorList);
442 callback(culledSensorList);
443 },
444 "xyz.openbmc_project.ObjectMapper", sensorPath,
445 "org.freedesktop.DBus.Properties", "Get",
446 "xyz.openbmc_project.Association", "endpoints");
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100447 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100448
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700449 // Get the Chassis Collection
Ed Tanous1abe55e2018-09-05 08:30:59 -0700450 crow::connections::systemBus->async_method_call(
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700451 respHandler, "xyz.openbmc_project.ObjectMapper",
452 "/xyz/openbmc_project/object_mapper",
453 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Ed Tanous271584a2019-07-09 16:24:22 -0700454 "/xyz/openbmc_project/inventory", 0, interfaces);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700455 BMCWEB_LOG_DEBUG << "getChassis exit";
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100456}
457
458/**
Shawn McCarneyde629b62019-03-08 10:42:51 -0600459 * @brief Finds all DBus object paths that implement ObjectManager.
460 *
461 * Creates a mapping from the associated connection name to the object path.
462 *
463 * Finds the object paths asynchronously. Invokes callback when information has
464 * been obtained.
465 *
466 * The callback must have the following signature:
467 * @code
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500468 * callback(std::shared_ptr<boost::container::flat_map<std::string,
469 * std::string>> objectMgrPaths)
Shawn McCarneyde629b62019-03-08 10:42:51 -0600470 * @endcode
471 *
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700472 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyde629b62019-03-08 10:42:51 -0600473 * @param callback Callback to invoke when object paths obtained.
474 */
475template <typename Callback>
476void getObjectManagerPaths(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
477 Callback&& callback)
478{
479 BMCWEB_LOG_DEBUG << "getObjectManagerPaths enter";
480 const std::array<std::string, 1> interfaces = {
481 "org.freedesktop.DBus.ObjectManager"};
482
483 // Response handler for GetSubTree DBus method
484 auto respHandler = [callback{std::move(callback)},
485 SensorsAsyncResp](const boost::system::error_code ec,
486 const GetSubTreeType& subtree) {
487 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler enter";
488 if (ec)
489 {
490 messages::internalError(SensorsAsyncResp->res);
491 BMCWEB_LOG_ERROR << "getObjectManagerPaths respHandler: DBus error "
492 << ec;
493 return;
494 }
495
496 // Loop over returned object paths
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500497 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
498 objectMgrPaths = std::make_shared<
499 boost::container::flat_map<std::string, std::string>>();
Shawn McCarneyde629b62019-03-08 10:42:51 -0600500 for (const std::pair<
501 std::string,
502 std::vector<std::pair<std::string, std::vector<std::string>>>>&
503 object : subtree)
504 {
505 // Loop over connections for current object path
506 const std::string& objectPath = object.first;
507 for (const std::pair<std::string, std::vector<std::string>>&
508 objData : object.second)
509 {
510 // Add mapping from connection to object path
511 const std::string& connection = objData.first;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500512 (*objectMgrPaths)[connection] = objectPath;
Shawn McCarneyde629b62019-03-08 10:42:51 -0600513 BMCWEB_LOG_DEBUG << "Added mapping " << connection << " -> "
514 << objectPath;
515 }
516 }
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500517 callback(objectMgrPaths);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600518 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler exit";
519 };
520
521 // Query mapper for all DBus object paths that implement ObjectManager
522 crow::connections::systemBus->async_method_call(
523 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
524 "/xyz/openbmc_project/object_mapper",
Ed Tanous271584a2019-07-09 16:24:22 -0700525 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0, interfaces);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600526 BMCWEB_LOG_DEBUG << "getObjectManagerPaths exit";
527}
528
529/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500530 * @brief Returns the Redfish State value for the specified inventory item.
531 * @param inventoryItem D-Bus inventory item associated with a sensor.
532 * @return State value for inventory item.
James Feist34dd1792019-05-17 14:10:54 -0700533 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500534static std::string getState(const InventoryItem* inventoryItem)
535{
536 if ((inventoryItem != nullptr) && !(inventoryItem->isPresent))
537 {
538 return "Absent";
539 }
James Feist34dd1792019-05-17 14:10:54 -0700540
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500541 return "Enabled";
542}
543
544/**
545 * @brief Returns the Redfish Health value for the specified sensor.
546 * @param sensorJson Sensor JSON object.
547 * @param interfacesDict Map of all sensor interfaces.
548 * @param inventoryItem D-Bus inventory item associated with the sensor. Will
549 * be nullptr if no associated inventory item was found.
550 * @return Health value for sensor.
551 */
James Feist34dd1792019-05-17 14:10:54 -0700552static std::string getHealth(
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500553 nlohmann::json& sensorJson,
James Feist34dd1792019-05-17 14:10:54 -0700554 const boost::container::flat_map<
555 std::string, boost::container::flat_map<std::string, SensorVariant>>&
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500556 interfacesDict,
557 const InventoryItem* inventoryItem)
James Feist34dd1792019-05-17 14:10:54 -0700558{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500559 // Get current health value (if any) in the sensor JSON object. Some JSON
560 // objects contain multiple sensors (such as PowerSupplies). We want to set
561 // the overall health to be the most severe of any of the sensors.
562 std::string currentHealth;
563 auto statusIt = sensorJson.find("Status");
564 if (statusIt != sensorJson.end())
565 {
566 auto healthIt = statusIt->find("Health");
567 if (healthIt != statusIt->end())
568 {
569 std::string* health = healthIt->get_ptr<std::string*>();
570 if (health != nullptr)
571 {
572 currentHealth = *health;
573 }
574 }
575 }
576
577 // If current health in JSON object is already Critical, return that. This
578 // should override the sensor health, which might be less severe.
579 if (currentHealth == "Critical")
580 {
581 return "Critical";
582 }
583
584 // Check if sensor has critical threshold alarm
James Feist34dd1792019-05-17 14:10:54 -0700585 auto criticalThresholdIt =
586 interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Critical");
587 if (criticalThresholdIt != interfacesDict.end())
588 {
589 auto thresholdHighIt =
590 criticalThresholdIt->second.find("CriticalAlarmHigh");
591 auto thresholdLowIt =
592 criticalThresholdIt->second.find("CriticalAlarmLow");
593 if (thresholdHighIt != criticalThresholdIt->second.end())
594 {
595 const bool* asserted = std::get_if<bool>(&thresholdHighIt->second);
596 if (asserted == nullptr)
597 {
598 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
599 }
600 else if (*asserted)
601 {
602 return "Critical";
603 }
604 }
605 if (thresholdLowIt != criticalThresholdIt->second.end())
606 {
607 const bool* asserted = std::get_if<bool>(&thresholdLowIt->second);
608 if (asserted == nullptr)
609 {
610 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
611 }
612 else if (*asserted)
613 {
614 return "Critical";
615 }
616 }
617 }
618
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500619 // Check if associated inventory item is not functional
620 if ((inventoryItem != nullptr) && !(inventoryItem->isFunctional))
621 {
622 return "Critical";
623 }
624
625 // If current health in JSON object is already Warning, return that. This
626 // should override the sensor status, which might be less severe.
627 if (currentHealth == "Warning")
628 {
629 return "Warning";
630 }
631
632 // Check if sensor has warning threshold alarm
James Feist34dd1792019-05-17 14:10:54 -0700633 auto warningThresholdIt =
634 interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Warning");
635 if (warningThresholdIt != interfacesDict.end())
636 {
637 auto thresholdHighIt =
638 warningThresholdIt->second.find("WarningAlarmHigh");
639 auto thresholdLowIt =
640 warningThresholdIt->second.find("WarningAlarmLow");
641 if (thresholdHighIt != warningThresholdIt->second.end())
642 {
643 const bool* asserted = std::get_if<bool>(&thresholdHighIt->second);
644 if (asserted == nullptr)
645 {
646 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
647 }
648 else if (*asserted)
649 {
650 return "Warning";
651 }
652 }
653 if (thresholdLowIt != warningThresholdIt->second.end())
654 {
655 const bool* asserted = std::get_if<bool>(&thresholdLowIt->second);
656 if (asserted == nullptr)
657 {
658 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
659 }
660 else if (*asserted)
661 {
662 return "Warning";
663 }
664 }
665 }
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500666
James Feist34dd1792019-05-17 14:10:54 -0700667 return "OK";
668}
669
Anthony Wilsond5005492019-07-31 16:34:17 -0500670static void setLedState(nlohmann::json& sensorJson,
671 const InventoryItem* inventoryItem)
672{
673 if (inventoryItem != nullptr && !inventoryItem->ledObjectPath.empty())
674 {
675 switch (inventoryItem->ledState)
676 {
677 case LedState::OFF:
678 sensorJson["IndicatorLED"] = "Off";
679 break;
680 case LedState::ON:
681 sensorJson["IndicatorLED"] = "Lit";
682 break;
683 case LedState::BLINK:
684 sensorJson["IndicatorLED"] = "Blinking";
685 break;
686 default:
687 break;
688 }
689 }
690}
691
James Feist34dd1792019-05-17 14:10:54 -0700692/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100693 * @brief Builds a json sensor representation of a sensor.
694 * @param sensorName The name of the sensor to be built
Gunnar Mills274fad52018-06-13 15:45:36 -0500695 * @param sensorType The type (temperature, fan_tach, etc) of the sensor to
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100696 * build
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500697 * @param sensorSchema The schema (Power, Thermal, etc) being associated with
698 * the sensor to build
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100699 * @param interfacesDict A dictionary of the interfaces and properties of said
700 * interfaces to be built from
701 * @param sensor_json The json object to fill
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500702 * @param inventoryItem D-Bus inventory item associated with the sensor. Will
703 * be nullptr if no associated inventory item was found.
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100704 */
705void objectInterfacesToJson(
706 const std::string& sensorName, const std::string& sensorType,
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500707 const std::string& sensorSchema,
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100708 const boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700709 std::string, boost::container::flat_map<std::string, SensorVariant>>&
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100710 interfacesDict,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500711 nlohmann::json& sensor_json, InventoryItem* inventoryItem)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700712{
713 // We need a value interface before we can do anything with it
714 auto valueIt = interfacesDict.find("xyz.openbmc_project.Sensor.Value");
715 if (valueIt == interfacesDict.end())
716 {
717 BMCWEB_LOG_ERROR << "Sensor doesn't have a value interface";
718 return;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100719 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100720
Ed Tanous1abe55e2018-09-05 08:30:59 -0700721 // Assume values exist as is (10^0 == 1) if no scale exists
722 int64_t scaleMultiplier = 0;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100723
Ed Tanous1abe55e2018-09-05 08:30:59 -0700724 auto scaleIt = valueIt->second.find("Scale");
725 // If a scale exists, pull value as int64, and use the scaling.
726 if (scaleIt != valueIt->second.end())
727 {
Ed Tanousabf2add2019-01-22 16:40:12 -0800728 const int64_t* int64Value = std::get_if<int64_t>(&scaleIt->second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700729 if (int64Value != nullptr)
730 {
731 scaleMultiplier = *int64Value;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100732 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100733 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700734
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500735 if (sensorSchema == "Sensors")
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500736 {
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500737 // For sensors in SensorCollection we set Id instead of MemberId,
738 // including power sensors.
739 sensor_json["Id"] = sensorName;
740 sensor_json["Name"] = boost::replace_all_copy(sensorName, "_", " ");
741 }
742 else if (sensorType != "power")
743 {
744 // Set MemberId and Name for non-power sensors. For PowerSupplies and
745 // PowerControl, those properties have more general values because
746 // multiple sensors can be stored in the same JSON object.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500747 sensor_json["MemberId"] = sensorName;
748 sensor_json["Name"] = boost::replace_all_copy(sensorName, "_", " ");
749 }
Ed Tanouse742b6c2019-05-03 15:06:53 -0700750
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500751 sensor_json["Status"]["State"] = getState(inventoryItem);
752 sensor_json["Status"]["Health"] =
753 getHealth(sensor_json, interfacesDict, inventoryItem);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700754
755 // Parameter to set to override the type we get from dbus, and force it to
756 // int, regardless of what is available. This is used for schemas like fan,
757 // that require integers, not floats.
758 bool forceToInt = false;
759
Anthony Wilson3929aca2019-07-19 15:42:33 -0500760 nlohmann::json::json_pointer unit("/Reading");
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500761 if (sensorSchema == "Sensors")
762 {
763 sensor_json["@odata.type"] = "#Sensor.v1_0_0.Sensor";
764 sensor_json["@odata.context"] = "/redfish/v1/$metadata#Sensor.Sensor";
765 if (sensorType == "power")
766 {
767 sensor_json["ReadingUnits"] = "Watts";
768 }
769 else if (sensorType == "current")
770 {
771 sensor_json["ReadingUnits"] = "Amperes";
772 }
773 }
774 else if (sensorType == "temperature")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700775 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500776 unit = "/ReadingCelsius"_json_pointer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700777 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Temperature";
778 // TODO(ed) Documentation says that path should be type fan_tach,
779 // implementation seems to implement fan
780 }
781 else if (sensorType == "fan" || sensorType == "fan_tach")
782 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500783 unit = "/Reading"_json_pointer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700784 sensor_json["ReadingUnits"] = "RPM";
785 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
Anthony Wilsond5005492019-07-31 16:34:17 -0500786 setLedState(sensor_json, inventoryItem);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700787 forceToInt = true;
788 }
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700789 else if (sensorType == "fan_pwm")
790 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500791 unit = "/Reading"_json_pointer;
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700792 sensor_json["ReadingUnits"] = "Percent";
793 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
Anthony Wilsond5005492019-07-31 16:34:17 -0500794 setLedState(sensor_json, inventoryItem);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700795 forceToInt = true;
796 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700797 else if (sensorType == "voltage")
798 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500799 unit = "/ReadingVolts"_json_pointer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700800 sensor_json["@odata.type"] = "#Power.v1_0_0.Voltage";
801 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700802 else if (sensorType == "power")
803 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700804 std::string sensorNameLower =
805 boost::algorithm::to_lower_copy(sensorName);
806
Eddie James028f7eb2019-05-17 21:24:36 +0000807 if (!sensorName.compare("total_power"))
808 {
Gunnar Mills7ab06f42019-07-02 13:07:16 -0500809 sensor_json["@odata.type"] = "#Power.v1_0_0.PowerControl";
810 // Put multiple "sensors" into a single PowerControl, so have
811 // generic names for MemberId and Name. Follows Redfish mockup.
812 sensor_json["MemberId"] = "0";
813 sensor_json["Name"] = "Chassis Power Control";
Anthony Wilson3929aca2019-07-19 15:42:33 -0500814 unit = "/PowerConsumedWatts"_json_pointer;
Eddie James028f7eb2019-05-17 21:24:36 +0000815 }
816 else if (sensorNameLower.find("input") != std::string::npos)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700817 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500818 unit = "/PowerInputWatts"_json_pointer;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700819 }
820 else
821 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500822 unit = "/PowerOutputWatts"_json_pointer;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700823 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700824 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700825 else
826 {
827 BMCWEB_LOG_ERROR << "Redfish cannot map object type for " << sensorName;
828 return;
829 }
830 // Map of dbus interface name, dbus property name and redfish property_name
Anthony Wilson3929aca2019-07-19 15:42:33 -0500831 std::vector<
832 std::tuple<const char*, const char*, nlohmann::json::json_pointer>>
833 properties;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700834 properties.reserve(7);
835
836 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600837
Anthony Wilson3929aca2019-07-19 15:42:33 -0500838 if (sensorSchema == "Sensors")
839 {
840 properties.emplace_back(
841 "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningHigh",
842 "/Thresholds/UpperCaution/Reading"_json_pointer);
843 properties.emplace_back(
844 "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningLow",
845 "/Thresholds/LowerCaution/Reading"_json_pointer);
846 properties.emplace_back(
847 "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalHigh",
848 "/Thresholds/UpperCritical/Reading"_json_pointer);
849 properties.emplace_back(
850 "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalLow",
851 "/Thresholds/LowerCritical/Reading"_json_pointer);
852 }
853 else if (sensorType != "power")
Shawn McCarneyde629b62019-03-08 10:42:51 -0600854 {
855 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500856 "WarningHigh",
857 "/UpperThresholdNonCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600858 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500859 "WarningLow",
860 "/LowerThresholdNonCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600861 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500862 "CriticalHigh",
863 "/UpperThresholdCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600864 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500865 "CriticalLow",
866 "/LowerThresholdCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600867 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700868
Ed Tanous2474adf2018-09-05 16:31:16 -0700869 // TODO Need to get UpperThresholdFatal and LowerThresholdFatal
870
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500871 if (sensorSchema == "Sensors")
872 {
873 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500874 "/ReadingRangeMin"_json_pointer);
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500875 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500876 "/ReadingRangeMax"_json_pointer);
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500877 }
878 else if (sensorType == "temperature")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700879 {
880 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500881 "/MinReadingRangeTemp"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700882 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500883 "/MaxReadingRangeTemp"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700884 }
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500885 else if (sensorType != "power")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700886 {
887 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500888 "/MinReadingRange"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700889 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500890 "/MaxReadingRange"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700891 }
892
Anthony Wilson3929aca2019-07-19 15:42:33 -0500893 for (const std::tuple<const char*, const char*,
894 nlohmann::json::json_pointer>& p : properties)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700895 {
896 auto interfaceProperties = interfacesDict.find(std::get<0>(p));
897 if (interfaceProperties != interfacesDict.end())
898 {
Ed Tanous271584a2019-07-09 16:24:22 -0700899 auto thisValueIt = interfaceProperties->second.find(std::get<1>(p));
900 if (thisValueIt != interfaceProperties->second.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700901 {
Ed Tanous271584a2019-07-09 16:24:22 -0700902 const SensorVariant& valueVariant = thisValueIt->second;
Anthony Wilson3929aca2019-07-19 15:42:33 -0500903
904 // The property we want to set may be nested json, so use
905 // a json_pointer for easy indexing into the json structure.
906 const nlohmann::json::json_pointer& key = std::get<2>(p);
907
Ed Tanous1abe55e2018-09-05 08:30:59 -0700908 // Attempt to pull the int64 directly
Ed Tanousabf2add2019-01-22 16:40:12 -0800909 const int64_t* int64Value = std::get_if<int64_t>(&valueVariant);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700910
Ed Tanousabf2add2019-01-22 16:40:12 -0800911 const double* doubleValue = std::get_if<double>(&valueVariant);
Eddie James028f7eb2019-05-17 21:24:36 +0000912 const uint32_t* uValue = std::get_if<uint32_t>(&valueVariant);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700913 double temp = 0.0;
914 if (int64Value != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700915 {
Ed Tanous271584a2019-07-09 16:24:22 -0700916 temp = static_cast<double>(*int64Value);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700917 }
918 else if (doubleValue != nullptr)
919 {
920 temp = *doubleValue;
921 }
Eddie James028f7eb2019-05-17 21:24:36 +0000922 else if (uValue != nullptr)
923 {
924 temp = *uValue;
925 }
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700926 else
927 {
928 BMCWEB_LOG_ERROR
929 << "Got value interface that wasn't int or double";
930 continue;
931 }
932 temp = temp * std::pow(10, scaleMultiplier);
933 if (forceToInt)
934 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500935 sensor_json[key] = static_cast<int64_t>(temp);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700936 }
937 else
938 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500939 sensor_json[key] = temp;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700940 }
941 }
942 }
943 }
944 BMCWEB_LOG_DEBUG << "Added sensor " << sensorName;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100945}
946
James Feist8bd25cc2019-03-15 15:14:00 -0700947static void
948 populateFanRedundancy(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp)
949{
950 crow::connections::systemBus->async_method_call(
951 [sensorsAsyncResp](const boost::system::error_code ec,
952 const GetSubTreeType& resp) {
953 if (ec)
954 {
955 return; // don't have to have this interface
956 }
Ed Tanouse278c182019-03-13 16:23:37 -0700957 for (const std::pair<std::string,
958 std::vector<std::pair<
959 std::string, std::vector<std::string>>>>&
960 pathPair : resp)
James Feist8bd25cc2019-03-15 15:14:00 -0700961 {
Ed Tanouse278c182019-03-13 16:23:37 -0700962 const std::string& path = pathPair.first;
963 const std::vector<
964 std::pair<std::string, std::vector<std::string>>>& objDict =
965 pathPair.second;
James Feist8bd25cc2019-03-15 15:14:00 -0700966 if (objDict.empty())
967 {
968 continue; // this should be impossible
969 }
970
971 const std::string& owner = objDict.begin()->first;
972 crow::connections::systemBus->async_method_call(
973 [path, owner,
Ed Tanous271584a2019-07-09 16:24:22 -0700974 sensorsAsyncResp](const boost::system::error_code e,
James Feist8bd25cc2019-03-15 15:14:00 -0700975 std::variant<std::vector<std::string>>
976 variantEndpoints) {
Ed Tanous271584a2019-07-09 16:24:22 -0700977 if (e)
James Feist8bd25cc2019-03-15 15:14:00 -0700978 {
979 return; // if they don't have an association we
980 // can't tell what chassis is
981 }
982 // verify part of the right chassis
983 auto endpoints = std::get_if<std::vector<std::string>>(
984 &variantEndpoints);
985
986 if (endpoints == nullptr)
987 {
988 BMCWEB_LOG_ERROR << "Invalid association interface";
989 messages::internalError(sensorsAsyncResp->res);
990 return;
991 }
992
993 auto found = std::find_if(
994 endpoints->begin(), endpoints->end(),
995 [sensorsAsyncResp](const std::string& entry) {
996 return entry.find(
997 sensorsAsyncResp->chassisId) !=
998 std::string::npos;
999 });
1000
1001 if (found == endpoints->end())
1002 {
1003 return;
1004 }
1005 crow::connections::systemBus->async_method_call(
1006 [path, sensorsAsyncResp](
Ed Tanous271584a2019-07-09 16:24:22 -07001007 const boost::system::error_code& err,
James Feist8bd25cc2019-03-15 15:14:00 -07001008 const boost::container::flat_map<
1009 std::string,
1010 std::variant<uint8_t,
1011 std::vector<std::string>,
1012 std::string>>& ret) {
Ed Tanous271584a2019-07-09 16:24:22 -07001013 if (err)
James Feist8bd25cc2019-03-15 15:14:00 -07001014 {
1015 return; // don't have to have this
1016 // interface
1017 }
1018 auto findFailures = ret.find("AllowedFailures");
1019 auto findCollection = ret.find("Collection");
1020 auto findStatus = ret.find("Status");
1021
1022 if (findFailures == ret.end() ||
1023 findCollection == ret.end() ||
1024 findStatus == ret.end())
1025 {
1026 BMCWEB_LOG_ERROR
1027 << "Invalid redundancy interface";
1028 messages::internalError(
1029 sensorsAsyncResp->res);
1030 return;
1031 }
1032
1033 auto allowedFailures = std::get_if<uint8_t>(
1034 &(findFailures->second));
1035 auto collection =
1036 std::get_if<std::vector<std::string>>(
1037 &(findCollection->second));
1038 auto status = std::get_if<std::string>(
1039 &(findStatus->second));
1040
1041 if (allowedFailures == nullptr ||
1042 collection == nullptr || status == nullptr)
1043 {
1044
1045 BMCWEB_LOG_ERROR
1046 << "Invalid redundancy interface "
1047 "types";
1048 messages::internalError(
1049 sensorsAsyncResp->res);
1050 return;
1051 }
1052 size_t lastSlash = path.rfind("/");
1053 if (lastSlash == std::string::npos)
1054 {
1055 // this should be impossible
1056 messages::internalError(
1057 sensorsAsyncResp->res);
1058 return;
1059 }
1060 std::string name = path.substr(lastSlash + 1);
1061 std::replace(name.begin(), name.end(), '_',
1062 ' ');
1063
1064 std::string health;
1065
1066 if (boost::ends_with(*status, "Full"))
1067 {
1068 health = "OK";
1069 }
1070 else if (boost::ends_with(*status, "Degraded"))
1071 {
1072 health = "Warning";
1073 }
1074 else
1075 {
1076 health = "Critical";
1077 }
1078 std::vector<nlohmann::json> redfishCollection;
1079 const auto& fanRedfish =
1080 sensorsAsyncResp->res.jsonValue["Fans"];
1081 for (const std::string& item : *collection)
1082 {
1083 lastSlash = item.rfind("/");
1084 // make a copy as collection is const
1085 std::string itemName =
1086 item.substr(lastSlash + 1);
1087 /*
1088 todo(ed): merge patch that fixes the names
1089 std::replace(itemName.begin(),
1090 itemName.end(), '_', ' ');*/
1091 auto schemaItem = std::find_if(
1092 fanRedfish.begin(), fanRedfish.end(),
1093 [itemName](const nlohmann::json& fan) {
1094 return fan["MemberId"] == itemName;
1095 });
1096 if (schemaItem != fanRedfish.end())
1097 {
1098 redfishCollection.push_back(
1099 {{"@odata.id",
1100 (*schemaItem)["@odata.id"]}});
1101 }
1102 else
1103 {
1104 BMCWEB_LOG_ERROR
1105 << "failed to find fan in schema";
1106 messages::internalError(
1107 sensorsAsyncResp->res);
1108 return;
1109 }
1110 }
1111
Ed Tanous271584a2019-07-09 16:24:22 -07001112 nlohmann::json& jResp =
1113 sensorsAsyncResp->res
1114 .jsonValue["Redundancy"];
1115 jResp.push_back(
James Feist8bd25cc2019-03-15 15:14:00 -07001116 {{"@odata.id",
AppaRao Puli717794d2019-10-18 22:54:53 +05301117 "/redfish/v1/Chassis/" +
James Feist8bd25cc2019-03-15 15:14:00 -07001118 sensorsAsyncResp->chassisId + "/" +
1119 sensorsAsyncResp->chassisSubNode +
1120 "#/Redundancy/" +
Ed Tanous271584a2019-07-09 16:24:22 -07001121 std::to_string(jResp.size())},
James Feist8bd25cc2019-03-15 15:14:00 -07001122 {"@odata.type",
1123 "#Redundancy.v1_3_2.Redundancy"},
1124 {"MinNumNeeded",
1125 collection->size() - *allowedFailures},
1126 {"MemberId", name},
1127 {"Mode", "N+m"},
1128 {"Name", name},
1129 {"RedundancySet", redfishCollection},
1130 {"Status",
1131 {{"Health", health},
1132 {"State", "Enabled"}}}});
1133 },
1134 owner, path, "org.freedesktop.DBus.Properties",
1135 "GetAll",
1136 "xyz.openbmc_project.Control.FanRedundancy");
1137 },
James Feist02e92e32019-06-26 12:07:05 -07001138 "xyz.openbmc_project.ObjectMapper", path + "/chassis",
James Feist8bd25cc2019-03-15 15:14:00 -07001139 "org.freedesktop.DBus.Properties", "Get",
1140 "xyz.openbmc_project.Association", "endpoints");
1141 }
1142 },
1143 "xyz.openbmc_project.ObjectMapper",
1144 "/xyz/openbmc_project/object_mapper",
1145 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
1146 "/xyz/openbmc_project/control", 2,
1147 std::array<const char*, 1>{
1148 "xyz.openbmc_project.Control.FanRedundancy"});
1149}
1150
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001151void sortJSONResponse(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
1152{
1153 nlohmann::json& response = SensorsAsyncResp->res.jsonValue;
1154 std::array<std::string, 2> sensorHeaders{"Temperatures", "Fans"};
1155 if (SensorsAsyncResp->chassisSubNode == "Power")
1156 {
1157 sensorHeaders = {"Voltages", "PowerSupplies"};
1158 }
1159 for (const std::string& sensorGroup : sensorHeaders)
1160 {
1161 nlohmann::json::iterator entry = response.find(sensorGroup);
1162 if (entry != response.end())
1163 {
1164 std::sort(entry->begin(), entry->end(),
1165 [](nlohmann::json& c1, nlohmann::json& c2) {
1166 return c1["Name"] < c2["Name"];
1167 });
1168
1169 // add the index counts to the end of each entry
1170 size_t count = 0;
1171 for (nlohmann::json& sensorJson : *entry)
1172 {
1173 nlohmann::json::iterator odata = sensorJson.find("@odata.id");
1174 if (odata == sensorJson.end())
1175 {
1176 continue;
1177 }
1178 std::string* value = odata->get_ptr<std::string*>();
1179 if (value != nullptr)
1180 {
1181 *value += std::to_string(count);
1182 count++;
1183 }
1184 }
1185 }
1186 }
1187}
1188
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001189/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001190 * @brief Finds the inventory item with the specified object path.
1191 * @param inventoryItems D-Bus inventory items associated with sensors.
1192 * @param invItemObjPath D-Bus object path of inventory item.
1193 * @return Inventory item within vector, or nullptr if no match found.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001194 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001195static InventoryItem* findInventoryItem(
1196 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1197 const std::string& invItemObjPath)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001198{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001199 for (InventoryItem& inventoryItem : *inventoryItems)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001200 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001201 if (inventoryItem.objectPath == invItemObjPath)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001202 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001203 return &inventoryItem;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001204 }
1205 }
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001206 return nullptr;
1207}
1208
1209/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001210 * @brief Finds the inventory item associated with the specified sensor.
1211 * @param inventoryItems D-Bus inventory items associated with sensors.
1212 * @param sensorObjPath D-Bus object path of sensor.
1213 * @return Inventory item within vector, or nullptr if no match found.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001214 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001215static InventoryItem* findInventoryItemForSensor(
1216 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1217 const std::string& sensorObjPath)
1218{
1219 for (InventoryItem& inventoryItem : *inventoryItems)
1220 {
1221 if (inventoryItem.sensors.count(sensorObjPath) > 0)
1222 {
1223 return &inventoryItem;
1224 }
1225 }
1226 return nullptr;
1227}
1228
1229/**
Anthony Wilsond5005492019-07-31 16:34:17 -05001230 * @brief Finds the inventory item associated with the specified led path.
1231 * @param inventoryItems D-Bus inventory items associated with sensors.
1232 * @param ledObjPath D-Bus object path of led.
1233 * @return Inventory item within vector, or nullptr if no match found.
1234 */
1235inline InventoryItem*
1236 findInventoryItemForLed(std::vector<InventoryItem>& inventoryItems,
1237 const std::string& ledObjPath)
1238{
1239 for (InventoryItem& inventoryItem : inventoryItems)
1240 {
1241 if (inventoryItem.ledObjectPath == ledObjPath)
1242 {
1243 return &inventoryItem;
1244 }
1245 }
1246 return nullptr;
1247}
1248
1249/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001250 * @brief Adds inventory item and associated sensor to specified vector.
1251 *
1252 * Adds a new InventoryItem to the vector if necessary. Searches for an
1253 * existing InventoryItem with the specified object path. If not found, one is
1254 * added to the vector.
1255 *
1256 * Next, the specified sensor is added to the set of sensors associated with the
1257 * InventoryItem.
1258 *
1259 * @param inventoryItems D-Bus inventory items associated with sensors.
1260 * @param invItemObjPath D-Bus object path of inventory item.
1261 * @param sensorObjPath D-Bus object path of sensor
1262 */
1263static void
1264 addInventoryItem(std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1265 const std::string& invItemObjPath,
1266 const std::string& sensorObjPath)
1267{
1268 // Look for inventory item in vector
1269 InventoryItem* inventoryItem =
1270 findInventoryItem(inventoryItems, invItemObjPath);
1271
1272 // If inventory item doesn't exist in vector, add it
1273 if (inventoryItem == nullptr)
1274 {
1275 inventoryItems->emplace_back(invItemObjPath);
1276 inventoryItem = &(inventoryItems->back());
1277 }
1278
1279 // Add sensor to set of sensors associated with inventory item
1280 inventoryItem->sensors.emplace(sensorObjPath);
1281}
1282
1283/**
1284 * @brief Stores D-Bus data in the specified inventory item.
1285 *
1286 * Finds D-Bus data in the specified map of interfaces. Stores the data in the
1287 * specified InventoryItem.
1288 *
1289 * This data is later used to provide sensor property values in the JSON
1290 * response.
1291 *
1292 * @param inventoryItem Inventory item where data will be stored.
1293 * @param interfacesDict Map containing D-Bus interfaces and their properties
1294 * for the specified inventory item.
1295 */
1296static void storeInventoryItemData(
1297 InventoryItem& inventoryItem,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001298 const boost::container::flat_map<
1299 std::string, boost::container::flat_map<std::string, SensorVariant>>&
1300 interfacesDict)
1301{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001302 // Get properties from Inventory.Item interface
1303 auto interfaceIt =
1304 interfacesDict.find("xyz.openbmc_project.Inventory.Item");
1305 if (interfaceIt != interfacesDict.end())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001306 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001307 auto propertyIt = interfaceIt->second.find("Present");
1308 if (propertyIt != interfaceIt->second.end())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001309 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001310 const bool* value = std::get_if<bool>(&propertyIt->second);
1311 if (value != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001312 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001313 inventoryItem.isPresent = *value;
1314 }
1315 }
1316 }
1317
1318 // Check if Inventory.Item.PowerSupply interface is present
1319 interfaceIt =
1320 interfacesDict.find("xyz.openbmc_project.Inventory.Item.PowerSupply");
1321 if (interfaceIt != interfacesDict.end())
1322 {
1323 inventoryItem.isPowerSupply = true;
1324 }
1325
1326 // Get properties from Inventory.Decorator.Asset interface
1327 interfaceIt =
1328 interfacesDict.find("xyz.openbmc_project.Inventory.Decorator.Asset");
1329 if (interfaceIt != interfacesDict.end())
1330 {
1331 auto propertyIt = interfaceIt->second.find("Manufacturer");
1332 if (propertyIt != interfaceIt->second.end())
1333 {
1334 const std::string* value =
1335 std::get_if<std::string>(&propertyIt->second);
1336 if (value != nullptr)
1337 {
1338 inventoryItem.manufacturer = *value;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001339 }
1340 }
1341
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001342 propertyIt = interfaceIt->second.find("Model");
1343 if (propertyIt != interfaceIt->second.end())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001344 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001345 const std::string* value =
1346 std::get_if<std::string>(&propertyIt->second);
1347 if (value != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001348 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001349 inventoryItem.model = *value;
1350 }
1351 }
1352
1353 propertyIt = interfaceIt->second.find("PartNumber");
1354 if (propertyIt != interfaceIt->second.end())
1355 {
1356 const std::string* value =
1357 std::get_if<std::string>(&propertyIt->second);
1358 if (value != nullptr)
1359 {
1360 inventoryItem.partNumber = *value;
1361 }
1362 }
1363
1364 propertyIt = interfaceIt->second.find("SerialNumber");
1365 if (propertyIt != interfaceIt->second.end())
1366 {
1367 const std::string* value =
1368 std::get_if<std::string>(&propertyIt->second);
1369 if (value != nullptr)
1370 {
1371 inventoryItem.serialNumber = *value;
1372 }
1373 }
1374 }
1375
1376 // Get properties from State.Decorator.OperationalStatus interface
1377 interfaceIt = interfacesDict.find(
1378 "xyz.openbmc_project.State.Decorator.OperationalStatus");
1379 if (interfaceIt != interfacesDict.end())
1380 {
1381 auto propertyIt = interfaceIt->second.find("Functional");
1382 if (propertyIt != interfaceIt->second.end())
1383 {
1384 const bool* value = std::get_if<bool>(&propertyIt->second);
1385 if (value != nullptr)
1386 {
1387 inventoryItem.isFunctional = *value;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001388 }
1389 }
1390 }
1391}
1392
1393/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001394 * @brief Gets D-Bus data for inventory items associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001395 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001396 * Uses the specified connections (services) to obtain D-Bus data for inventory
1397 * items associated with sensors. Stores the resulting data in the
1398 * inventoryItems vector.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001399 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001400 * This data is later used to provide sensor property values in the JSON
1401 * response.
1402 *
1403 * Finds the inventory item data asynchronously. Invokes callback when data has
1404 * been obtained.
1405 *
1406 * The callback must have the following signature:
1407 * @code
Anthony Wilsond5005492019-07-31 16:34:17 -05001408 * callback(void)
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001409 * @endcode
1410 *
1411 * This function is called recursively, obtaining data asynchronously from one
1412 * connection in each call. This ensures the callback is not invoked until the
1413 * last asynchronous function has completed.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001414 *
1415 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001416 * @param inventoryItems D-Bus inventory items associated with sensors.
1417 * @param invConnections Connections that provide data for the inventory items.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001418 * @param objectMgrPaths Mappings from connection name to DBus object path that
1419 * implements ObjectManager.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001420 * @param callback Callback to invoke when inventory data has been obtained.
1421 * @param invConnectionsIndex Current index in invConnections. Only specified
1422 * in recursive calls to this function.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001423 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001424template <typename Callback>
1425static void getInventoryItemsData(
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001426 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001427 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001428 std::shared_ptr<boost::container::flat_set<std::string>> invConnections,
1429 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001430 objectMgrPaths,
Ed Tanous271584a2019-07-09 16:24:22 -07001431 Callback&& callback, size_t invConnectionsIndex = 0)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001432{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001433 BMCWEB_LOG_DEBUG << "getInventoryItemsData enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001434
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001435 // If no more connections left, call callback
1436 if (invConnectionsIndex >= invConnections->size())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001437 {
Anthony Wilsond5005492019-07-31 16:34:17 -05001438 callback();
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001439 BMCWEB_LOG_DEBUG << "getInventoryItemsData exit";
1440 return;
1441 }
1442
1443 // Get inventory item data from current connection
1444 auto it = invConnections->nth(invConnectionsIndex);
1445 if (it != invConnections->end())
1446 {
1447 const std::string& invConnection = *it;
1448
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001449 // Response handler for GetManagedObjects
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001450 auto respHandler = [sensorsAsyncResp, inventoryItems, invConnections,
1451 objectMgrPaths, callback{std::move(callback)},
1452 invConnectionsIndex](
1453 const boost::system::error_code ec,
1454 ManagedObjectsVectorType& resp) {
1455 BMCWEB_LOG_DEBUG << "getInventoryItemsData respHandler enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001456 if (ec)
1457 {
1458 BMCWEB_LOG_ERROR
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001459 << "getInventoryItemsData respHandler DBus error " << ec;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001460 messages::internalError(sensorsAsyncResp->res);
1461 return;
1462 }
1463
1464 // Loop through returned object paths
1465 for (const auto& objDictEntry : resp)
1466 {
1467 const std::string& objPath =
1468 static_cast<const std::string&>(objDictEntry.first);
1469
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001470 // If this object path is one of the specified inventory items
1471 InventoryItem* inventoryItem =
1472 findInventoryItem(inventoryItems, objPath);
1473 if (inventoryItem != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001474 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001475 // Store inventory data in InventoryItem
1476 storeInventoryItemData(*inventoryItem, objDictEntry.second);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001477 }
1478 }
1479
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001480 // Recurse to get inventory item data from next connection
1481 getInventoryItemsData(sensorsAsyncResp, inventoryItems,
1482 invConnections, objectMgrPaths,
1483 std::move(callback), invConnectionsIndex + 1);
1484
1485 BMCWEB_LOG_DEBUG << "getInventoryItemsData respHandler exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001486 };
1487
1488 // Find DBus object path that implements ObjectManager for the current
1489 // connection. If no mapping found, default to "/".
1490 auto iter = objectMgrPaths->find(invConnection);
1491 const std::string& objectMgrPath =
1492 (iter != objectMgrPaths->end()) ? iter->second : "/";
1493 BMCWEB_LOG_DEBUG << "ObjectManager path for " << invConnection << " is "
1494 << objectMgrPath;
1495
1496 // Get all object paths and their interfaces for current connection
1497 crow::connections::systemBus->async_method_call(
1498 std::move(respHandler), invConnection, objectMgrPath,
1499 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1500 }
1501
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001502 BMCWEB_LOG_DEBUG << "getInventoryItemsData exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001503}
1504
1505/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001506 * @brief Gets connections that provide D-Bus data for inventory items.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001507 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001508 * Gets the D-Bus connections (services) that provide data for the inventory
1509 * items that are associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001510 *
1511 * Finds the connections asynchronously. Invokes callback when information has
1512 * been obtained.
1513 *
1514 * The callback must have the following signature:
1515 * @code
1516 * callback(std::shared_ptr<boost::container::flat_set<std::string>>
1517 * invConnections)
1518 * @endcode
1519 *
1520 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001521 * @param inventoryItems D-Bus inventory items associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001522 * @param callback Callback to invoke when connections have been obtained.
1523 */
1524template <typename Callback>
1525static void getInventoryItemsConnections(
1526 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001527 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001528 Callback&& callback)
1529{
1530 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections enter";
1531
1532 const std::string path = "/xyz/openbmc_project/inventory";
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001533 const std::array<std::string, 4> interfaces = {
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001534 "xyz.openbmc_project.Inventory.Item",
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001535 "xyz.openbmc_project.Inventory.Item.PowerSupply",
1536 "xyz.openbmc_project.Inventory.Decorator.Asset",
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001537 "xyz.openbmc_project.State.Decorator.OperationalStatus"};
1538
1539 // Response handler for parsing output from GetSubTree
1540 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001541 inventoryItems](const boost::system::error_code ec,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001542 const GetSubTreeType& subtree) {
1543 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections respHandler enter";
1544 if (ec)
1545 {
1546 messages::internalError(sensorsAsyncResp->res);
1547 BMCWEB_LOG_ERROR
1548 << "getInventoryItemsConnections respHandler DBus error " << ec;
1549 return;
1550 }
1551
1552 // Make unique list of connections for desired inventory items
1553 std::shared_ptr<boost::container::flat_set<std::string>>
1554 invConnections =
1555 std::make_shared<boost::container::flat_set<std::string>>();
1556 invConnections->reserve(8);
1557
1558 // Loop through objects from GetSubTree
1559 for (const std::pair<
1560 std::string,
1561 std::vector<std::pair<std::string, std::vector<std::string>>>>&
1562 object : subtree)
1563 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001564 // Check if object path is one of the specified inventory items
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001565 const std::string& objPath = object.first;
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001566 if (findInventoryItem(inventoryItems, objPath) != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001567 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001568 // Store all connections to inventory item
1569 for (const std::pair<std::string, std::vector<std::string>>&
1570 objData : object.second)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001571 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001572 const std::string& invConnection = objData.first;
1573 invConnections->insert(invConnection);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001574 }
1575 }
1576 }
Anthony Wilsond5005492019-07-31 16:34:17 -05001577
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001578 callback(invConnections);
1579 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections respHandler exit";
1580 };
1581
1582 // Make call to ObjectMapper to find all inventory items
1583 crow::connections::systemBus->async_method_call(
1584 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
1585 "/xyz/openbmc_project/object_mapper",
1586 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 0, interfaces);
1587 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections exit";
1588}
1589
1590/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001591 * @brief Gets associations from sensors to inventory items.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001592 *
1593 * Looks for ObjectMapper associations from the specified sensors to related
Anthony Wilsond5005492019-07-31 16:34:17 -05001594 * inventory items. Then finds the associations from those inventory items to
1595 * their LEDs, if any.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001596 *
1597 * Finds the inventory items asynchronously. Invokes callback when information
1598 * has been obtained.
1599 *
1600 * The callback must have the following signature:
1601 * @code
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001602 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001603 * @endcode
1604 *
1605 * @param sensorsAsyncResp Pointer to object holding response data.
1606 * @param sensorNames All sensors within the current chassis.
1607 * @param objectMgrPaths Mappings from connection name to DBus object path that
1608 * implements ObjectManager.
1609 * @param callback Callback to invoke when inventory items have been obtained.
1610 */
1611template <typename Callback>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001612static void getInventoryItemAssociations(
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001613 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1614 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
1615 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1616 objectMgrPaths,
1617 Callback&& callback)
1618{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001619 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001620
1621 // Response handler for GetManagedObjects
1622 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
1623 sensorNames](const boost::system::error_code ec,
1624 dbus::utility::ManagedObjectType& resp) {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001625 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations respHandler enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001626 if (ec)
1627 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001628 BMCWEB_LOG_ERROR
1629 << "getInventoryItemAssociations respHandler DBus error " << ec;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001630 messages::internalError(sensorsAsyncResp->res);
1631 return;
1632 }
1633
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001634 // Create vector to hold list of inventory items
1635 std::shared_ptr<std::vector<InventoryItem>> inventoryItems =
1636 std::make_shared<std::vector<InventoryItem>>();
1637
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001638 // Loop through returned object paths
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001639 std::string sensorAssocPath;
1640 sensorAssocPath.reserve(128); // avoid memory allocations
1641 for (const auto& objDictEntry : resp)
1642 {
1643 const std::string& objPath =
1644 static_cast<const std::string&>(objDictEntry.first);
1645 const boost::container::flat_map<
1646 std::string, boost::container::flat_map<
1647 std::string, dbus::utility::DbusVariantType>>&
1648 interfacesDict = objDictEntry.second;
1649
1650 // If path is inventory association for one of the specified sensors
1651 for (const std::string& sensorName : *sensorNames)
1652 {
1653 sensorAssocPath = sensorName;
1654 sensorAssocPath += "/inventory";
1655 if (objPath == sensorAssocPath)
1656 {
1657 // Get Association interface for object path
1658 auto assocIt =
1659 interfacesDict.find("xyz.openbmc_project.Association");
1660 if (assocIt != interfacesDict.end())
1661 {
1662 // Get inventory item from end point
1663 auto endpointsIt = assocIt->second.find("endpoints");
1664 if (endpointsIt != assocIt->second.end())
1665 {
1666 const std::vector<std::string>* endpoints =
1667 std::get_if<std::vector<std::string>>(
1668 &endpointsIt->second);
1669 if ((endpoints != nullptr) && !endpoints->empty())
1670 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001671 // Add inventory item to vector
1672 const std::string& invItemPath =
1673 endpoints->front();
1674 addInventoryItem(inventoryItems, invItemPath,
1675 sensorName);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001676 }
1677 }
1678 }
1679 break;
1680 }
1681 }
1682 }
1683
Anthony Wilsond5005492019-07-31 16:34:17 -05001684 // Now loop through the returned object paths again, this time to
1685 // find the leds associated with the inventory items we just found
1686 std::string inventoryAssocPath;
1687 inventoryAssocPath.reserve(128); // avoid memory allocations
1688 for (const auto& objDictEntry : resp)
1689 {
1690 const std::string& objPath =
1691 static_cast<const std::string&>(objDictEntry.first);
1692 const boost::container::flat_map<
1693 std::string, boost::container::flat_map<
1694 std::string, dbus::utility::DbusVariantType>>&
1695 interfacesDict = objDictEntry.second;
1696
1697 for (InventoryItem& inventoryItem : *inventoryItems)
1698 {
1699 inventoryAssocPath = inventoryItem.objectPath;
1700 inventoryAssocPath += "/leds";
1701 if (objPath == inventoryAssocPath)
1702 {
1703 // Get Association interface for object path
1704 auto assocIt =
1705 interfacesDict.find("xyz.openbmc_project.Association");
1706 if (assocIt != interfacesDict.end())
1707 {
1708 // Get inventory item from end point
1709 auto endpointsIt = assocIt->second.find("endpoints");
1710 if (endpointsIt != assocIt->second.end())
1711 {
1712 const std::vector<std::string>* endpoints =
1713 std::get_if<std::vector<std::string>>(
1714 &endpointsIt->second);
1715 if ((endpoints != nullptr) && !endpoints->empty())
1716 {
1717 // Store LED path in inventory item
1718 const std::string& ledPath = endpoints->front();
1719 inventoryItem.ledObjectPath = ledPath;
1720 }
1721 }
1722 }
1723 break;
1724 }
1725 }
1726 }
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001727 callback(inventoryItems);
1728 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations respHandler exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001729 };
1730
1731 // Find DBus object path that implements ObjectManager for ObjectMapper
1732 std::string connection = "xyz.openbmc_project.ObjectMapper";
1733 auto iter = objectMgrPaths->find(connection);
1734 const std::string& objectMgrPath =
1735 (iter != objectMgrPaths->end()) ? iter->second : "/";
1736 BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
1737 << objectMgrPath;
1738
1739 // Call GetManagedObjects on the ObjectMapper to get all associations
1740 crow::connections::systemBus->async_method_call(
1741 std::move(respHandler), connection, objectMgrPath,
1742 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1743
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001744 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001745}
1746
1747/**
Anthony Wilsond5005492019-07-31 16:34:17 -05001748 * @brief Gets D-Bus data for inventory item leds associated with sensors.
1749 *
1750 * Uses the specified connections (services) to obtain D-Bus data for inventory
1751 * item leds associated with sensors. Stores the resulting data in the
1752 * inventoryItems vector.
1753 *
1754 * This data is later used to provide sensor property values in the JSON
1755 * response.
1756 *
1757 * Finds the inventory item led data asynchronously. Invokes callback when data
1758 * has been obtained.
1759 *
1760 * The callback must have the following signature:
1761 * @code
Gunnar Mills42cbe532019-08-15 15:26:54 -05001762 * callback()
Anthony Wilsond5005492019-07-31 16:34:17 -05001763 * @endcode
1764 *
1765 * This function is called recursively, obtaining data asynchronously from one
1766 * connection in each call. This ensures the callback is not invoked until the
1767 * last asynchronous function has completed.
1768 *
1769 * @param sensorsAsyncResp Pointer to object holding response data.
1770 * @param inventoryItems D-Bus inventory items associated with sensors.
1771 * @param ledConnections Connections that provide data for the inventory leds.
1772 * @param callback Callback to invoke when inventory data has been obtained.
1773 * @param ledConnectionsIndex Current index in ledConnections. Only specified
1774 * in recursive calls to this function.
1775 */
1776template <typename Callback>
1777void getInventoryLedData(
1778 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1779 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1780 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1781 ledConnections,
1782 Callback&& callback, size_t ledConnectionsIndex = 0)
1783{
1784 BMCWEB_LOG_DEBUG << "getInventoryLedData enter";
1785
1786 // If no more connections left, call callback
1787 if (ledConnectionsIndex >= ledConnections->size())
1788 {
Gunnar Mills42cbe532019-08-15 15:26:54 -05001789 callback();
Anthony Wilsond5005492019-07-31 16:34:17 -05001790 BMCWEB_LOG_DEBUG << "getInventoryLedData exit";
1791 return;
1792 }
1793
1794 // Get inventory item data from current connection
1795 auto it = ledConnections->nth(ledConnectionsIndex);
1796 if (it != ledConnections->end())
1797 {
1798 const std::string& ledPath = (*it).first;
1799 const std::string& ledConnection = (*it).second;
1800 // Response handler for Get State property
1801 auto respHandler =
1802 [sensorsAsyncResp, inventoryItems, ledConnections, ledPath,
1803 callback{std::move(callback)},
1804 ledConnectionsIndex](const boost::system::error_code ec,
1805 const std::variant<std::string>& ledState) {
1806 BMCWEB_LOG_DEBUG << "getInventoryLedData respHandler enter";
1807 if (ec)
1808 {
1809 BMCWEB_LOG_ERROR
1810 << "getInventoryLedData respHandler DBus error " << ec;
1811 messages::internalError(sensorsAsyncResp->res);
1812 return;
1813 }
1814
1815 const std::string* state = std::get_if<std::string>(&ledState);
1816 if (state != nullptr)
1817 {
1818 BMCWEB_LOG_DEBUG << "Led state: " << *state;
1819 // Find inventory item with this LED object path
1820 InventoryItem* inventoryItem =
1821 findInventoryItemForLed(*inventoryItems, ledPath);
1822 if (inventoryItem != nullptr)
1823 {
1824 // Store LED state in InventoryItem
1825 if (boost::ends_with(*state, "On"))
1826 {
1827 inventoryItem->ledState = LedState::ON;
1828 }
1829 else if (boost::ends_with(*state, "Blink"))
1830 {
1831 inventoryItem->ledState = LedState::BLINK;
1832 }
1833 else if (boost::ends_with(*state, "Off"))
1834 {
1835 inventoryItem->ledState = LedState::OFF;
1836 }
1837 else
1838 {
1839 inventoryItem->ledState = LedState::UNKNOWN;
1840 }
1841 }
1842 }
1843 else
1844 {
1845 BMCWEB_LOG_DEBUG << "Failed to find State data for LED: "
1846 << ledPath;
1847 }
1848
1849 // Recurse to get LED data from next connection
1850 getInventoryLedData(sensorsAsyncResp, inventoryItems,
1851 ledConnections, std::move(callback),
1852 ledConnectionsIndex + 1);
1853
1854 BMCWEB_LOG_DEBUG << "getInventoryLedData respHandler exit";
1855 };
1856
1857 // Get the State property for the current LED
1858 crow::connections::systemBus->async_method_call(
1859 std::move(respHandler), ledConnection, ledPath,
1860 "org.freedesktop.DBus.Properties", "Get",
1861 "xyz.openbmc_project.Led.Physical", "State");
1862 }
1863
1864 BMCWEB_LOG_DEBUG << "getInventoryLedData exit";
1865}
1866
1867/**
1868 * @brief Gets LED data for LEDs associated with given inventory items.
1869 *
1870 * Gets the D-Bus connections (services) that provide LED data for the LEDs
1871 * associated with the specified inventory items. Then gets the LED data from
1872 * each connection and stores it in the inventory item.
1873 *
1874 * This data is later used to provide sensor property values in the JSON
1875 * response.
1876 *
1877 * Finds the LED data asynchronously. Invokes callback when information has
1878 * been obtained.
1879 *
1880 * The callback must have the following signature:
1881 * @code
Gunnar Mills42cbe532019-08-15 15:26:54 -05001882 * callback()
Anthony Wilsond5005492019-07-31 16:34:17 -05001883 * @endcode
1884 *
1885 * @param sensorsAsyncResp Pointer to object holding response data.
1886 * @param inventoryItems D-Bus inventory items associated with sensors.
1887 * @param callback Callback to invoke when inventory items have been obtained.
1888 */
1889template <typename Callback>
1890void getInventoryLeds(
1891 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1892 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1893 Callback&& callback)
1894{
1895 BMCWEB_LOG_DEBUG << "getInventoryLeds enter";
1896
1897 const std::string path = "/xyz/openbmc_project";
1898 const std::array<std::string, 1> interfaces = {
1899 "xyz.openbmc_project.Led.Physical"};
1900
1901 // Response handler for parsing output from GetSubTree
1902 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
1903 inventoryItems](const boost::system::error_code ec,
1904 const GetSubTreeType& subtree) {
1905 BMCWEB_LOG_DEBUG << "getInventoryLeds respHandler enter";
1906 if (ec)
1907 {
1908 messages::internalError(sensorsAsyncResp->res);
1909 BMCWEB_LOG_ERROR << "getInventoryLeds respHandler DBus error "
1910 << ec;
1911 return;
1912 }
1913
1914 // Build map of LED object paths to connections
1915 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1916 ledConnections = std::make_shared<
1917 boost::container::flat_map<std::string, std::string>>();
1918
1919 // Loop through objects from GetSubTree
1920 for (const std::pair<
1921 std::string,
1922 std::vector<std::pair<std::string, std::vector<std::string>>>>&
1923 object : subtree)
1924 {
1925 // Check if object path is LED for one of the specified inventory
1926 // items
1927 const std::string& ledPath = object.first;
1928 if (findInventoryItemForLed(*inventoryItems, ledPath) != nullptr)
1929 {
1930 // Add mapping from ledPath to connection
1931 const std::string& connection = object.second.begin()->first;
1932 (*ledConnections)[ledPath] = connection;
1933 BMCWEB_LOG_DEBUG << "Added mapping " << ledPath << " -> "
1934 << connection;
1935 }
1936 }
1937
1938 getInventoryLedData(sensorsAsyncResp, inventoryItems, ledConnections,
1939 std::move(callback));
1940 BMCWEB_LOG_DEBUG << "getInventoryLeds respHandler exit";
1941 };
1942 // Make call to ObjectMapper to find all inventory items
1943 crow::connections::systemBus->async_method_call(
1944 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
1945 "/xyz/openbmc_project/object_mapper",
1946 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 0, interfaces);
1947 BMCWEB_LOG_DEBUG << "getInventoryLeds exit";
1948}
1949
1950/**
Gunnar Mills42cbe532019-08-15 15:26:54 -05001951 * @brief Gets D-Bus data for Power Supply Attributes such as EfficiencyPercent
1952 *
1953 * Uses the specified connections (services) (currently assumes just one) to
1954 * obtain D-Bus data for Power Supply Attributes. Stores the resulting data in
1955 * the inventoryItems vector. Only stores data in Power Supply inventoryItems.
1956 *
1957 * This data is later used to provide sensor property values in the JSON
1958 * response.
1959 *
1960 * Finds the Power Supply Attributes data asynchronously. Invokes callback
1961 * when data has been obtained.
1962 *
1963 * The callback must have the following signature:
1964 * @code
1965 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
1966 * @endcode
1967 *
1968 * @param sensorsAsyncResp Pointer to object holding response data.
1969 * @param inventoryItems D-Bus inventory items associated with sensors.
1970 * @param psAttributesConnections Connections that provide data for the Power
1971 * Supply Attributes
1972 * @param callback Callback to invoke when data has been obtained.
1973 */
1974template <typename Callback>
1975void getPowerSupplyAttributesData(
1976 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1977 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1978 const boost::container::flat_map<std::string, std::string>&
1979 psAttributesConnections,
1980 Callback&& callback)
1981{
1982 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData enter";
1983
1984 if (psAttributesConnections.empty())
1985 {
1986 BMCWEB_LOG_DEBUG << "Can't find PowerSupplyAttributes, no connections!";
1987 callback(inventoryItems);
1988 return;
1989 }
1990
1991 // Assuming just one connection (service) for now
1992 auto it = psAttributesConnections.nth(0);
1993
1994 const std::string& psAttributesPath = (*it).first;
1995 const std::string& psAttributesConnection = (*it).second;
1996
1997 // Response handler for Get DeratingFactor property
1998 auto respHandler = [sensorsAsyncResp, inventoryItems,
1999 callback{std::move(callback)}](
2000 const boost::system::error_code ec,
2001 const std::variant<uint32_t>& deratingFactor) {
2002 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData respHandler enter";
2003 if (ec)
2004 {
2005 BMCWEB_LOG_ERROR
2006 << "getPowerSupplyAttributesData respHandler DBus error " << ec;
2007 messages::internalError(sensorsAsyncResp->res);
2008 return;
2009 }
2010
2011 const uint32_t* value = std::get_if<uint32_t>(&deratingFactor);
2012 if (value != nullptr)
2013 {
2014 BMCWEB_LOG_DEBUG << "PS EfficiencyPercent value: " << *value;
2015 // Store value in Power Supply Inventory Items
2016 for (InventoryItem& inventoryItem : *inventoryItems)
2017 {
2018 if (inventoryItem.isPowerSupply == true)
2019 {
2020 inventoryItem.powerSupplyEfficiencyPercent =
2021 static_cast<int>(*value);
2022 }
2023 }
2024 }
2025 else
2026 {
2027 BMCWEB_LOG_DEBUG
2028 << "Failed to find EfficiencyPercent value for PowerSupplies";
2029 }
2030
2031 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData respHandler exit";
2032 callback(inventoryItems);
2033 };
2034
2035 // Get the DeratingFactor property for the PowerSupplyAttributes
2036 // Currently only property on the interface/only one we care about
2037 crow::connections::systemBus->async_method_call(
2038 std::move(respHandler), psAttributesConnection, psAttributesPath,
2039 "org.freedesktop.DBus.Properties", "Get",
2040 "xyz.openbmc_project.Control.PowerSupplyAttributes", "DeratingFactor");
2041
2042 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData exit";
2043}
2044
2045/**
2046 * @brief Gets the Power Supply Attributes such as EfficiencyPercent
2047 *
2048 * Gets the D-Bus connection (service) that provides Power Supply Attributes
2049 * data. Then gets the Power Supply Attributes data from the connection
2050 * (currently just assumes 1 connection) and stores the data in the inventory
2051 * item.
2052 *
2053 * This data is later used to provide sensor property values in the JSON
2054 * response. DeratingFactor on D-Bus is mapped to EfficiencyPercent on Redfish.
2055 *
2056 * Finds the Power Supply Attributes data asynchronously. Invokes callback
2057 * when information has been obtained.
2058 *
2059 * The callback must have the following signature:
2060 * @code
2061 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
2062 * @endcode
2063 *
2064 * @param sensorsAsyncResp Pointer to object holding response data.
2065 * @param inventoryItems D-Bus inventory items associated with sensors.
2066 * @param callback Callback to invoke when data has been obtained.
2067 */
2068template <typename Callback>
2069void getPowerSupplyAttributes(
2070 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
2071 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
2072 Callback&& callback)
2073{
2074 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes enter";
2075
2076 // Only need the power supply attributes when the Power Schema
2077 if (sensorsAsyncResp->chassisSubNode != "Power")
2078 {
2079 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes exit since not Power";
2080 callback(inventoryItems);
2081 return;
2082 }
2083
2084 const std::array<std::string, 1> interfaces = {
2085 "xyz.openbmc_project.Control.PowerSupplyAttributes"};
2086
2087 // Response handler for parsing output from GetSubTree
2088 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
2089 inventoryItems](const boost::system::error_code ec,
2090 const GetSubTreeType& subtree) {
2091 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes respHandler enter";
2092 if (ec)
2093 {
2094 messages::internalError(sensorsAsyncResp->res);
2095 BMCWEB_LOG_ERROR
2096 << "getPowerSupplyAttributes respHandler DBus error " << ec;
2097 return;
2098 }
2099 if (subtree.size() == 0)
2100 {
2101 BMCWEB_LOG_DEBUG << "Can't find Power Supply Attributes!";
2102 callback(inventoryItems);
2103 return;
2104 }
2105
2106 // Currently we only support 1 power supply attribute, use this for
2107 // all the power supplies. Build map of object path to connection.
2108 // Assume just 1 connection and 1 path for now.
2109 boost::container::flat_map<std::string, std::string>
2110 psAttributesConnections;
2111
2112 if (subtree[0].first.empty() || subtree[0].second.empty())
2113 {
2114 BMCWEB_LOG_DEBUG << "Power Supply Attributes mapper error!";
2115 callback(inventoryItems);
2116 return;
2117 }
2118
2119 const std::string& psAttributesPath = subtree[0].first;
2120 const std::string& connection = subtree[0].second.begin()->first;
2121
2122 if (connection.empty())
2123 {
2124 BMCWEB_LOG_DEBUG << "Power Supply Attributes mapper error!";
2125 callback(inventoryItems);
2126 return;
2127 }
2128
2129 psAttributesConnections[psAttributesPath] = connection;
2130 BMCWEB_LOG_DEBUG << "Added mapping " << psAttributesPath << " -> "
2131 << connection;
2132
2133 getPowerSupplyAttributesData(sensorsAsyncResp, inventoryItems,
2134 psAttributesConnections,
2135 std::move(callback));
2136 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes respHandler exit";
2137 };
2138 // Make call to ObjectMapper to find the PowerSupplyAttributes service
2139 crow::connections::systemBus->async_method_call(
2140 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
2141 "/xyz/openbmc_project/object_mapper",
2142 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
2143 "/xyz/openbmc_project", 0, interfaces);
2144 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes exit";
2145}
2146
2147/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002148 * @brief Gets inventory items associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002149 *
2150 * Finds the inventory items that are associated with the specified sensors.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002151 * Then gets D-Bus data for the inventory items, such as presence and VPD.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002152 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002153 * This data is later used to provide sensor property values in the JSON
2154 * response.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002155 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002156 * Finds the inventory items asynchronously. Invokes callback when the
2157 * inventory items have been obtained.
2158 *
2159 * The callback must have the following signature:
2160 * @code
2161 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
2162 * @endcode
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002163 *
2164 * @param sensorsAsyncResp Pointer to object holding response data.
2165 * @param sensorNames All sensors within the current chassis.
2166 * @param objectMgrPaths Mappings from connection name to DBus object path that
2167 * implements ObjectManager.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002168 * @param callback Callback to invoke when inventory items have been obtained.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002169 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002170template <typename Callback>
2171static void getInventoryItems(
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002172 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
2173 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
2174 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002175 objectMgrPaths,
2176 Callback&& callback)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002177{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002178 BMCWEB_LOG_DEBUG << "getInventoryItems enter";
2179 auto getInventoryItemAssociationsCb =
2180 [sensorsAsyncResp, objectMgrPaths, callback{std::move(callback)}](
2181 std::shared_ptr<std::vector<InventoryItem>> inventoryItems) {
2182 BMCWEB_LOG_DEBUG << "getInventoryItemAssociationsCb enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002183 auto getInventoryItemsConnectionsCb =
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002184 [sensorsAsyncResp, inventoryItems, objectMgrPaths,
2185 callback{std::move(callback)}](
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002186 std::shared_ptr<boost::container::flat_set<std::string>>
2187 invConnections) {
2188 BMCWEB_LOG_DEBUG << "getInventoryItemsConnectionsCb enter";
Anthony Wilsond5005492019-07-31 16:34:17 -05002189 auto getInventoryItemsDataCb =
2190 [sensorsAsyncResp, inventoryItems,
2191 callback{std::move(callback)}]() {
2192 BMCWEB_LOG_DEBUG << "getInventoryItemsDataCb enter";
Gunnar Mills42cbe532019-08-15 15:26:54 -05002193
2194 auto getInventoryLedsCb = [sensorsAsyncResp,
2195 inventoryItems,
2196 callback{std::move(
2197 callback)}]() {
2198 BMCWEB_LOG_DEBUG << "getInventoryLedsCb enter";
2199 // Find Power Supply Attributes and get the data
2200 getPowerSupplyAttributes(sensorsAsyncResp,
2201 inventoryItems,
2202 std::move(callback));
2203 BMCWEB_LOG_DEBUG << "getInventoryLedsCb exit";
2204 };
2205
Anthony Wilsond5005492019-07-31 16:34:17 -05002206 // Find led connections and get the data
2207 getInventoryLeds(sensorsAsyncResp, inventoryItems,
Gunnar Mills42cbe532019-08-15 15:26:54 -05002208 std::move(getInventoryLedsCb));
Anthony Wilsond5005492019-07-31 16:34:17 -05002209 BMCWEB_LOG_DEBUG << "getInventoryItemsDataCb exit";
2210 };
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002211
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002212 // Get inventory item data from connections
2213 getInventoryItemsData(sensorsAsyncResp, inventoryItems,
2214 invConnections, objectMgrPaths,
Anthony Wilsond5005492019-07-31 16:34:17 -05002215 std::move(getInventoryItemsDataCb));
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002216 BMCWEB_LOG_DEBUG << "getInventoryItemsConnectionsCb exit";
2217 };
2218
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002219 // Get connections that provide inventory item data
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002220 getInventoryItemsConnections(
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002221 sensorsAsyncResp, inventoryItems,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002222 std::move(getInventoryItemsConnectionsCb));
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002223 BMCWEB_LOG_DEBUG << "getInventoryItemAssociationsCb exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002224 };
2225
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002226 // Get associations from sensors to inventory items
2227 getInventoryItemAssociations(sensorsAsyncResp, sensorNames, objectMgrPaths,
2228 std::move(getInventoryItemAssociationsCb));
2229 BMCWEB_LOG_DEBUG << "getInventoryItems exit";
2230}
2231
2232/**
2233 * @brief Returns JSON PowerSupply object for the specified inventory item.
2234 *
2235 * Searches for a JSON PowerSupply object that matches the specified inventory
2236 * item. If one is not found, a new PowerSupply object is added to the JSON
2237 * array.
2238 *
2239 * Multiple sensors are often associated with one power supply inventory item.
2240 * As a result, multiple sensor values are stored in one JSON PowerSupply
2241 * object.
2242 *
2243 * @param powerSupplyArray JSON array containing Redfish PowerSupply objects.
2244 * @param inventoryItem Inventory item for the power supply.
2245 * @param chassisId Chassis that contains the power supply.
2246 * @return JSON PowerSupply object for the specified inventory item.
2247 */
2248static nlohmann::json& getPowerSupply(nlohmann::json& powerSupplyArray,
2249 const InventoryItem& inventoryItem,
2250 const std::string& chassisId)
2251{
2252 // Check if matching PowerSupply object already exists in JSON array
2253 for (nlohmann::json& powerSupply : powerSupplyArray)
2254 {
2255 if (powerSupply["MemberId"] == inventoryItem.name)
2256 {
2257 return powerSupply;
2258 }
2259 }
2260
2261 // Add new PowerSupply object to JSON array
2262 powerSupplyArray.push_back({});
2263 nlohmann::json& powerSupply = powerSupplyArray.back();
2264 powerSupply["@odata.id"] =
2265 "/redfish/v1/Chassis/" + chassisId + "/Power#/PowerSupplies/";
2266 powerSupply["MemberId"] = inventoryItem.name;
2267 powerSupply["Name"] = boost::replace_all_copy(inventoryItem.name, "_", " ");
2268 powerSupply["Manufacturer"] = inventoryItem.manufacturer;
2269 powerSupply["Model"] = inventoryItem.model;
2270 powerSupply["PartNumber"] = inventoryItem.partNumber;
2271 powerSupply["SerialNumber"] = inventoryItem.serialNumber;
Anthony Wilsond5005492019-07-31 16:34:17 -05002272 setLedState(powerSupply, &inventoryItem);
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002273
Gunnar Mills42cbe532019-08-15 15:26:54 -05002274 if (inventoryItem.powerSupplyEfficiencyPercent >= 0)
2275 {
2276 powerSupply["EfficiencyPercent"] =
2277 inventoryItem.powerSupplyEfficiencyPercent;
2278 }
2279
2280 powerSupply["Status"]["State"] = getState(&inventoryItem);
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002281 const char* health = inventoryItem.isFunctional ? "OK" : "Critical";
2282 powerSupply["Status"]["Health"] = health;
2283
2284 return powerSupply;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002285}
2286
2287/**
Shawn McCarneyde629b62019-03-08 10:42:51 -06002288 * @brief Gets the values of the specified sensors.
2289 *
2290 * Stores the results as JSON in the SensorsAsyncResp.
2291 *
2292 * Gets the sensor values asynchronously. Stores the results later when the
2293 * information has been obtained.
2294 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002295 * The sensorNames set contains all requested sensors for the current chassis.
Shawn McCarneyde629b62019-03-08 10:42:51 -06002296 *
2297 * To minimize the number of DBus calls, the DBus method
2298 * org.freedesktop.DBus.ObjectManager.GetManagedObjects() is used to get the
2299 * values of all sensors provided by a connection (service).
2300 *
2301 * The connections set contains all the connections that provide sensor values.
2302 *
2303 * The objectMgrPaths map contains mappings from a connection name to the
2304 * corresponding DBus object path that implements ObjectManager.
2305 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002306 * The InventoryItem vector contains D-Bus inventory items associated with the
2307 * sensors. Inventory item data is needed for some Redfish sensor properties.
2308 *
Shawn McCarneyde629b62019-03-08 10:42:51 -06002309 * @param SensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002310 * @param sensorNames All requested sensors within the current chassis.
Shawn McCarneyde629b62019-03-08 10:42:51 -06002311 * @param connections Connections that provide sensor values.
2312 * @param objectMgrPaths Mappings from connection name to DBus object path that
2313 * implements ObjectManager.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002314 * @param inventoryItems Inventory items associated with the sensors.
Shawn McCarneyde629b62019-03-08 10:42:51 -06002315 */
2316void getSensorData(
2317 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002318 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
Shawn McCarneyde629b62019-03-08 10:42:51 -06002319 const boost::container::flat_set<std::string>& connections,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002320 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002321 objectMgrPaths,
2322 std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
Shawn McCarneyde629b62019-03-08 10:42:51 -06002323{
2324 BMCWEB_LOG_DEBUG << "getSensorData enter";
2325 // Get managed objects from all services exposing sensors
2326 for (const std::string& connection : connections)
2327 {
2328 // Response handler to process managed objects
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002329 auto getManagedObjectsCb = [SensorsAsyncResp, sensorNames,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002330 inventoryItems](
Shawn McCarneyde629b62019-03-08 10:42:51 -06002331 const boost::system::error_code ec,
2332 ManagedObjectsVectorType& resp) {
2333 BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter";
2334 if (ec)
2335 {
2336 BMCWEB_LOG_ERROR << "getManagedObjectsCb DBUS error: " << ec;
2337 messages::internalError(SensorsAsyncResp->res);
2338 return;
2339 }
2340 // Go through all objects and update response with sensor data
2341 for (const auto& objDictEntry : resp)
2342 {
2343 const std::string& objPath =
2344 static_cast<const std::string&>(objDictEntry.first);
2345 BMCWEB_LOG_DEBUG << "getManagedObjectsCb parsing object "
2346 << objPath;
2347
Shawn McCarneyde629b62019-03-08 10:42:51 -06002348 std::vector<std::string> split;
2349 // Reserve space for
2350 // /xyz/openbmc_project/sensors/<name>/<subname>
2351 split.reserve(6);
2352 boost::algorithm::split(split, objPath, boost::is_any_of("/"));
2353 if (split.size() < 6)
2354 {
2355 BMCWEB_LOG_ERROR << "Got path that isn't long enough "
2356 << objPath;
2357 continue;
2358 }
2359 // These indexes aren't intuitive, as boost::split puts an empty
2360 // string at the beginning
2361 const std::string& sensorType = split[4];
2362 const std::string& sensorName = split[5];
2363 BMCWEB_LOG_DEBUG << "sensorName " << sensorName
2364 << " sensorType " << sensorType;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002365 if (sensorNames->find(objPath) == sensorNames->end())
Shawn McCarneyde629b62019-03-08 10:42:51 -06002366 {
2367 BMCWEB_LOG_ERROR << sensorName << " not in sensor list ";
2368 continue;
2369 }
2370
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002371 // Find inventory item (if any) associated with sensor
2372 InventoryItem* inventoryItem =
2373 findInventoryItemForSensor(inventoryItems, objPath);
2374
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002375 const std::string& sensorSchema =
2376 SensorsAsyncResp->chassisSubNode;
2377
2378 nlohmann::json* sensorJson = nullptr;
2379
2380 if (sensorSchema == "Sensors")
Shawn McCarneyde629b62019-03-08 10:42:51 -06002381 {
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002382 SensorsAsyncResp->res.jsonValue["@odata.id"] =
2383 "/redfish/v1/Chassis/" + SensorsAsyncResp->chassisId +
2384 "/" + SensorsAsyncResp->chassisSubNode + "/" +
2385 sensorName;
2386 sensorJson = &(SensorsAsyncResp->res.jsonValue);
Shawn McCarneyde629b62019-03-08 10:42:51 -06002387 }
2388 else
2389 {
Ed Tanous271584a2019-07-09 16:24:22 -07002390 std::string fieldName;
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002391 if (sensorType == "temperature")
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002392 {
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002393 fieldName = "Temperatures";
2394 }
2395 else if (sensorType == "fan" || sensorType == "fan_tach" ||
2396 sensorType == "fan_pwm")
2397 {
2398 fieldName = "Fans";
2399 }
2400 else if (sensorType == "voltage")
2401 {
2402 fieldName = "Voltages";
2403 }
2404 else if (sensorType == "power")
2405 {
2406 if (!sensorName.compare("total_power"))
2407 {
2408 fieldName = "PowerControl";
2409 }
2410 else if ((inventoryItem != nullptr) &&
2411 (inventoryItem->isPowerSupply))
2412 {
2413 fieldName = "PowerSupplies";
2414 }
2415 else
2416 {
2417 // Other power sensors are in SensorCollection
2418 continue;
2419 }
2420 }
2421 else
2422 {
2423 BMCWEB_LOG_ERROR << "Unsure how to handle sensorType "
2424 << sensorType;
2425 continue;
2426 }
2427
2428 nlohmann::json& tempArray =
2429 SensorsAsyncResp->res.jsonValue[fieldName];
2430 if (fieldName == "PowerControl")
2431 {
2432 if (tempArray.empty())
2433 {
2434 // Put multiple "sensors" into a single
2435 // PowerControl. Follows MemberId naming and
2436 // naming in power.hpp.
2437 tempArray.push_back(
2438 {{"@odata.id",
2439 "/redfish/v1/Chassis/" +
2440 SensorsAsyncResp->chassisId + "/" +
2441 SensorsAsyncResp->chassisSubNode + "#/" +
2442 fieldName + "/0"}});
2443 }
2444 sensorJson = &(tempArray.back());
2445 }
2446 else if (fieldName == "PowerSupplies")
2447 {
2448 if (inventoryItem != nullptr)
2449 {
2450 sensorJson =
2451 &(getPowerSupply(tempArray, *inventoryItem,
2452 SensorsAsyncResp->chassisId));
2453 }
2454 }
2455 else
2456 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002457 tempArray.push_back(
2458 {{"@odata.id",
2459 "/redfish/v1/Chassis/" +
2460 SensorsAsyncResp->chassisId + "/" +
2461 SensorsAsyncResp->chassisSubNode + "#/" +
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002462 fieldName + "/"}});
2463 sensorJson = &(tempArray.back());
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002464 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002465 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06002466
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002467 if (sensorJson != nullptr)
2468 {
2469 objectInterfacesToJson(sensorName, sensorType,
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002470 SensorsAsyncResp->chassisSubNode,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002471 objDictEntry.second, *sensorJson,
2472 inventoryItem);
2473 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06002474 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002475 if (SensorsAsyncResp.use_count() == 1)
James Feist8bd25cc2019-03-15 15:14:00 -07002476 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002477 sortJSONResponse(SensorsAsyncResp);
2478 if (SensorsAsyncResp->chassisSubNode == "Thermal")
2479 {
2480 populateFanRedundancy(SensorsAsyncResp);
2481 }
James Feist8bd25cc2019-03-15 15:14:00 -07002482 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06002483 BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit";
2484 };
2485
2486 // Find DBus object path that implements ObjectManager for the current
2487 // connection. If no mapping found, default to "/".
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002488 auto iter = objectMgrPaths->find(connection);
Shawn McCarneyde629b62019-03-08 10:42:51 -06002489 const std::string& objectMgrPath =
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002490 (iter != objectMgrPaths->end()) ? iter->second : "/";
Shawn McCarneyde629b62019-03-08 10:42:51 -06002491 BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
2492 << objectMgrPath;
2493
2494 crow::connections::systemBus->async_method_call(
2495 getManagedObjectsCb, connection, objectMgrPath,
2496 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
2497 };
2498 BMCWEB_LOG_DEBUG << "getSensorData exit";
2499}
2500
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002501void processSensorList(
2502 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
2503 std::shared_ptr<boost::container::flat_set<std::string>> sensorNames)
2504{
2505 auto getConnectionCb =
2506 [SensorsAsyncResp, sensorNames](
2507 const boost::container::flat_set<std::string>& connections) {
2508 BMCWEB_LOG_DEBUG << "getConnectionCb enter";
2509 auto getObjectManagerPathsCb =
2510 [SensorsAsyncResp, sensorNames, connections](
2511 std::shared_ptr<
2512 boost::container::flat_map<std::string, std::string>>
2513 objectMgrPaths) {
2514 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb enter";
2515 auto getInventoryItemsCb =
2516 [SensorsAsyncResp, sensorNames, connections,
2517 objectMgrPaths](
2518 std::shared_ptr<std::vector<InventoryItem>>
2519 inventoryItems) {
2520 BMCWEB_LOG_DEBUG << "getInventoryItemsCb enter";
2521 // Get sensor data and store results in JSON
2522 getSensorData(SensorsAsyncResp, sensorNames,
2523 connections, objectMgrPaths,
2524 inventoryItems);
2525 BMCWEB_LOG_DEBUG << "getInventoryItemsCb exit";
2526 };
2527
2528 // Get inventory items associated with sensors
2529 getInventoryItems(SensorsAsyncResp, sensorNames,
2530 objectMgrPaths,
2531 std::move(getInventoryItemsCb));
2532
2533 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb exit";
2534 };
2535
2536 // Get mapping from connection names to the DBus object
2537 // paths that implement the ObjectManager interface
2538 getObjectManagerPaths(SensorsAsyncResp,
2539 std::move(getObjectManagerPathsCb));
2540 BMCWEB_LOG_DEBUG << "getConnectionCb exit";
2541 };
2542
2543 // Get set of connections that provide sensor values
2544 getConnections(SensorsAsyncResp, sensorNames, std::move(getConnectionCb));
2545}
2546
Shawn McCarneyde629b62019-03-08 10:42:51 -06002547/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01002548 * @brief Entry point for retrieving sensors data related to requested
2549 * chassis.
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02002550 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01002551 */
Ed Tanous1abe55e2018-09-05 08:30:59 -07002552void getChassisData(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
2553{
2554 BMCWEB_LOG_DEBUG << "getChassisData enter";
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002555 auto getChassisCb =
2556 [SensorsAsyncResp](
2557 std::shared_ptr<boost::container::flat_set<std::string>>
2558 sensorNames) {
2559 BMCWEB_LOG_DEBUG << "getChassisCb enter";
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002560 processSensorList(SensorsAsyncResp, sensorNames);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002561 BMCWEB_LOG_DEBUG << "getChassisCb exit";
2562 };
Jennifer Lee4f9a2132019-03-04 12:45:19 -08002563 SensorsAsyncResp->res.jsonValue["Redundancy"] = nlohmann::json::array();
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01002564
Shawn McCarney26f03892019-05-03 13:20:24 -05002565 // Get set of sensors in chassis
Ed Tanous1abe55e2018-09-05 08:30:59 -07002566 getChassis(SensorsAsyncResp, std::move(getChassisCb));
2567 BMCWEB_LOG_DEBUG << "getChassisData exit";
Ed Tanous271584a2019-07-09 16:24:22 -07002568}
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01002569
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302570/**
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002571 * @brief Find the requested sensorName in the list of all sensors supplied by
2572 * the chassis node
2573 *
2574 * @param sensorName The sensor name supplied in the PATCH request
2575 * @param sensorsList The list of sensors managed by the chassis node
2576 * @param sensorsModified The list of sensors that were found as a result of
2577 * repeated calls to this function
2578 */
2579bool findSensorNameUsingSensorPath(
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05302580 std::string_view sensorName,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002581 boost::container::flat_set<std::string>& sensorsList,
2582 boost::container::flat_set<std::string>& sensorsModified)
2583{
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05302584 for (std::string_view chassisSensor : sensorsList)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002585 {
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05302586 std::size_t pos = chassisSensor.rfind("/");
2587 if (pos >= (chassisSensor.size() - 1))
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002588 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002589 continue;
2590 }
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05302591 std::string_view thisSensorName = chassisSensor.substr(pos + 1);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002592 if (thisSensorName == sensorName)
2593 {
2594 sensorsModified.emplace(chassisSensor);
2595 return true;
2596 }
2597 }
2598 return false;
2599}
2600
2601/**
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302602 * @brief Entry point for overriding sensor values of given sensor
2603 *
2604 * @param res response object
Carol Wang4bb3dc32019-10-17 18:15:02 +08002605 * @param allCollections Collections extract from sensors' request patch info
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302606 * @param typeList TypeList of sensors for the resource queried
2607 * @param chassisSubNode Chassis Node for which the query has to happen
2608 */
Carol Wang4bb3dc32019-10-17 18:15:02 +08002609void setSensorOverride(
2610 std::shared_ptr<SensorsAsyncResp> sensorAsyncResp,
2611 std::unordered_map<std::string, std::vector<nlohmann::json>>&
2612 allCollections,
2613 const std::string& chassisName, const std::vector<const char*> typeList)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302614{
Carol Wang4bb3dc32019-10-17 18:15:02 +08002615 BMCWEB_LOG_INFO << "setSensorOverride for subNode"
2616 << sensorAsyncResp->chassisSubNode << "\n";
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302617
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302618 const char* propertyValueName;
2619 std::unordered_map<std::string, std::pair<double, std::string>> overrideMap;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302620 std::string memberId;
2621 double value;
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302622 for (auto& collectionItems : allCollections)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302623 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302624 if (collectionItems.first == "Temperatures")
2625 {
2626 propertyValueName = "ReadingCelsius";
2627 }
2628 else if (collectionItems.first == "Fans")
2629 {
2630 propertyValueName = "Reading";
2631 }
2632 else
2633 {
2634 propertyValueName = "ReadingVolts";
2635 }
2636 for (auto& item : collectionItems.second)
2637 {
Carol Wang4bb3dc32019-10-17 18:15:02 +08002638 if (!json_util::readJson(item, sensorAsyncResp->res, "MemberId",
2639 memberId, propertyValueName, value))
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302640 {
2641 return;
2642 }
2643 overrideMap.emplace(memberId,
2644 std::make_pair(value, collectionItems.first));
2645 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302646 }
Carol Wang4bb3dc32019-10-17 18:15:02 +08002647
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002648 auto getChassisSensorListCb = [sensorAsyncResp,
2649 overrideMap](const std::shared_ptr<
2650 boost::container::flat_set<
2651 std::string>>
2652 sensorsList) {
2653 // Match sensor names in the PATCH request to those managed by the
2654 // chassis node
2655 const std::shared_ptr<boost::container::flat_set<std::string>>
2656 sensorNames =
2657 std::make_shared<boost::container::flat_set<std::string>>();
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302658 for (const auto& item : overrideMap)
2659 {
2660 const auto& sensor = item.first;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002661 if (!findSensorNameUsingSensorPath(sensor, *sensorsList,
2662 *sensorNames))
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302663 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302664 BMCWEB_LOG_INFO << "Unable to find memberId " << item.first;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302665 messages::resourceNotFound(sensorAsyncResp->res,
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302666 item.second.second, item.first);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302667 return;
2668 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302669 }
2670 // Get the connection to which the memberId belongs
2671 auto getObjectsWithConnectionCb =
2672 [sensorAsyncResp, overrideMap](
2673 const boost::container::flat_set<std::string>& connections,
2674 const std::set<std::pair<std::string, std::string>>&
2675 objectsWithConnection) {
2676 if (objectsWithConnection.size() != overrideMap.size())
2677 {
2678 BMCWEB_LOG_INFO
2679 << "Unable to find all objects with proper connection "
2680 << objectsWithConnection.size() << " requested "
2681 << overrideMap.size() << "\n";
2682 messages::resourceNotFound(
2683 sensorAsyncResp->res,
2684 sensorAsyncResp->chassisSubNode == "Thermal"
2685 ? "Temperatures"
2686 : "Voltages",
2687 "Count");
2688 return;
2689 }
2690 for (const auto& item : objectsWithConnection)
2691 {
2692
2693 auto lastPos = item.first.rfind('/');
2694 if (lastPos == std::string::npos)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302695 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302696 messages::internalError(sensorAsyncResp->res);
2697 return;
2698 }
2699 std::string sensorName = item.first.substr(lastPos + 1);
2700
2701 const auto& iterator = overrideMap.find(sensorName);
2702 if (iterator == overrideMap.end())
2703 {
2704 BMCWEB_LOG_INFO << "Unable to find sensor object"
2705 << item.first << "\n";
2706 messages::internalError(sensorAsyncResp->res);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302707 return;
2708 }
2709 crow::connections::systemBus->async_method_call(
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302710 [sensorAsyncResp](const boost::system::error_code ec) {
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302711 if (ec)
2712 {
2713 BMCWEB_LOG_DEBUG
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302714 << "setOverrideValueStatus DBUS error: "
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302715 << ec;
2716 messages::internalError(sensorAsyncResp->res);
2717 return;
2718 }
2719 },
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302720 item.second, item.first,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302721 "org.freedesktop.DBus.Properties", "Set",
2722 "xyz.openbmc_project.Sensor.Value", "Value",
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302723 sdbusplus::message::variant<double>(
2724 iterator->second.first));
2725 }
2726 };
2727 // Get object with connection for the given sensor name
2728 getObjectsWithConnection(sensorAsyncResp, sensorNames,
2729 std::move(getObjectsWithConnectionCb));
2730 };
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302731 // get full sensor list for the given chassisId and cross verify the sensor.
2732 getChassis(sensorAsyncResp, std::move(getChassisSensorListCb));
2733}
2734
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002735class SensorCollection : public Node
2736{
2737 public:
2738 SensorCollection(CrowApp& app) :
2739 Node(app, "/redfish/v1/Chassis/<str>/Sensors", std::string())
2740 {
2741 entityPrivileges = {
2742 {boost::beast::http::verb::get, {{"Login"}}},
2743 {boost::beast::http::verb::head, {{"Login"}}},
2744 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2745 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2746 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2747 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2748 }
2749
2750 private:
2751 std::vector<const char*> typeList = {
2752 "/xyz/openbmc_project/sensors/power",
2753 "/xyz/openbmc_project/sensors/current"};
2754 void doGet(crow::Response& res, const crow::Request& req,
2755 const std::vector<std::string>& params) override
2756 {
2757 BMCWEB_LOG_DEBUG << "SensorCollection doGet enter";
2758 if (params.size() != 1)
2759 {
2760 BMCWEB_LOG_DEBUG << "SensorCollection doGet param size < 1";
2761 messages::internalError(res);
2762 res.end();
2763 return;
2764 }
2765
2766 const std::string& chassisId = params[0];
2767 std::shared_ptr<SensorsAsyncResp> asyncResp =
2768 std::make_shared<SensorsAsyncResp>(res, chassisId, typeList,
2769 "Sensors");
2770
2771 auto getChassisCb =
2772 [asyncResp](std::shared_ptr<boost::container::flat_set<std::string>>
2773 sensorNames) {
2774 BMCWEB_LOG_DEBUG << "getChassisCb enter";
2775
2776 nlohmann::json& entriesArray =
2777 asyncResp->res.jsonValue["Members"];
2778 for (auto& sensor : *sensorNames)
2779 {
2780 BMCWEB_LOG_DEBUG << "Adding sensor: " << sensor;
2781
2782 std::size_t lastPos = sensor.rfind("/");
2783 if (lastPos == std::string::npos ||
2784 lastPos + 1 >= sensor.size())
2785 {
2786 BMCWEB_LOG_ERROR << "Invalid sensor path: " << sensor;
2787 messages::internalError(asyncResp->res);
2788 return;
2789 }
2790 std::string sensorName = sensor.substr(lastPos + 1);
2791 entriesArray.push_back(
2792 {{"@odata.id",
2793 "/redfish/v1/Chassis/" + asyncResp->chassisId + "/" +
2794 asyncResp->chassisSubNode + "/" + sensorName}});
2795 }
2796
2797 asyncResp->res.jsonValue["Members@odata.count"] =
2798 entriesArray.size();
2799 BMCWEB_LOG_DEBUG << "getChassisCb exit";
2800 };
2801
2802 // Get set of sensors in chassis
2803 getChassis(asyncResp, std::move(getChassisCb));
2804 BMCWEB_LOG_DEBUG << "SensorCollection doGet exit";
2805 }
2806};
2807
2808class Sensor : public Node
2809{
2810 public:
2811 Sensor(CrowApp& app) :
2812 Node(app, "/redfish/v1/Chassis/<str>/Sensors/<str>/", std::string(),
2813 std::string())
2814 {
2815 entityPrivileges = {
2816 {boost::beast::http::verb::get, {{"Login"}}},
2817 {boost::beast::http::verb::head, {{"Login"}}},
2818 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2819 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2820 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2821 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2822 }
2823
2824 private:
2825 void doGet(crow::Response& res, const crow::Request& req,
2826 const std::vector<std::string>& params) override
2827 {
2828 BMCWEB_LOG_DEBUG << "Sensor doGet enter";
2829 if (params.size() != 2)
2830 {
2831 BMCWEB_LOG_DEBUG << "Sensor doGet param size < 2";
2832 messages::internalError(res);
2833 res.end();
2834 return;
2835 }
2836 const std::string& chassisId = params[0];
2837 std::shared_ptr<SensorsAsyncResp> asyncResp =
2838 std::make_shared<SensorsAsyncResp>(
2839 res, chassisId, std::vector<const char*>(), "Sensors");
2840
2841 const std::string& sensorName = params[1];
2842 const std::array<const char*, 1> interfaces = {
2843 "xyz.openbmc_project.Sensor.Value"};
2844
2845 // Get a list of all of the sensors that implement Sensor.Value
2846 // and get the path and service name associated with the sensor
2847 crow::connections::systemBus->async_method_call(
2848 [asyncResp, sensorName](const boost::system::error_code ec,
2849 const GetSubTreeType& subtree) {
2850 BMCWEB_LOG_DEBUG << "respHandler1 enter";
2851 if (ec)
2852 {
2853 messages::internalError(asyncResp->res);
2854 BMCWEB_LOG_ERROR << "Sensor getSensorPaths resp_handler: "
2855 << "Dbus error " << ec;
2856 return;
2857 }
2858
2859 GetSubTreeType::const_iterator it = std::find_if(
2860 subtree.begin(), subtree.end(),
2861 [sensorName](
2862 const std::pair<
2863 std::string,
2864 std::vector<std::pair<std::string,
2865 std::vector<std::string>>>>&
2866 object) {
2867 std::string_view sensor = object.first;
2868 std::size_t lastPos = sensor.rfind("/");
2869 if (lastPos == std::string::npos ||
2870 lastPos + 1 >= sensor.size())
2871 {
2872 BMCWEB_LOG_ERROR << "Invalid sensor path: "
2873 << sensor;
2874 return false;
2875 }
2876 std::string_view name = sensor.substr(lastPos + 1);
2877
2878 return name == sensorName;
2879 });
2880
2881 if (it == subtree.end())
2882 {
2883 BMCWEB_LOG_ERROR << "Could not find path for sensor: "
2884 << sensorName;
2885 messages::resourceNotFound(asyncResp->res, "Sensor",
2886 sensorName);
2887 return;
2888 }
2889 std::string_view sensorPath = (*it).first;
2890 BMCWEB_LOG_DEBUG << "Found sensor path for sensor '"
2891 << sensorName << "': " << sensorPath;
2892
2893 const std::shared_ptr<boost::container::flat_set<std::string>>
2894 sensorList = std::make_shared<
2895 boost::container::flat_set<std::string>>();
2896
2897 sensorList->emplace(sensorPath);
2898 processSensorList(asyncResp, sensorList);
2899 BMCWEB_LOG_DEBUG << "respHandler1 exit";
2900 },
2901 "xyz.openbmc_project.ObjectMapper",
2902 "/xyz/openbmc_project/object_mapper",
2903 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
2904 "/xyz/openbmc_project/sensors", 2, interfaces);
2905 }
2906};
2907
Ed Tanous1abe55e2018-09-05 08:30:59 -07002908} // namespace redfish