blob: 738edc2139b37717958132fd66243d4973e46793 [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 <math.h>
Ed Tanous1abe55e2018-09-05 08:30:59 -070021
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010022#include <boost/algorithm/string/predicate.hpp>
23#include <boost/algorithm/string/split.hpp>
24#include <boost/container/flat_map.hpp>
25#include <boost/range/algorithm/replace_copy_if.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070026#include <dbus_singleton.hpp>
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +053027#include <utils/json_utils.hpp>
Ed Tanousabf2add2019-01-22 16:40:12 -080028#include <variant>
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010029
Ed Tanous1abe55e2018-09-05 08:30:59 -070030namespace redfish
31{
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010032
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010033using GetSubTreeType = std::vector<
34 std::pair<std::string,
35 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
36
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -050037using SensorVariant =
38 std::variant<int64_t, double, uint32_t, bool, std::string>;
Ed Tanousaa2e59c2018-04-12 12:17:20 -070039
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010040using ManagedObjectsVectorType = std::vector<std::pair<
Ed Tanousaa2e59c2018-04-12 12:17:20 -070041 sdbusplus::message::object_path,
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010042 boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -070043 std::string, boost::container::flat_map<std::string, SensorVariant>>>>;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010044
45/**
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020046 * SensorsAsyncResp
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010047 * Gathers data needed for response processing after async calls are done
48 */
Ed Tanous1abe55e2018-09-05 08:30:59 -070049class SensorsAsyncResp
50{
51 public:
Ed Tanous271584a2019-07-09 16:24:22 -070052 SensorsAsyncResp(crow::Response& response, const std::string& chassisIdIn,
53 const std::vector<const char*> typesIn,
Ed Tanous2474adf2018-09-05 16:31:16 -070054 const std::string& subNode) :
Ed Tanous43b761d2019-02-13 20:10:56 -080055 res(response),
Ed Tanous271584a2019-07-09 16:24:22 -070056 chassisId(chassisIdIn), types(typesIn), chassisSubNode(subNode)
Ed Tanous1abe55e2018-09-05 08:30:59 -070057 {
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010058 }
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020059
Ed Tanous1abe55e2018-09-05 08:30:59 -070060 ~SensorsAsyncResp()
61 {
62 if (res.result() == boost::beast::http::status::internal_server_error)
63 {
64 // Reset the json object to clear out any data that made it in
65 // before the error happened todo(ed) handle error condition with
66 // proper code
67 res.jsonValue = nlohmann::json::object();
68 }
69 res.end();
70 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010071
Ed Tanous1abe55e2018-09-05 08:30:59 -070072 crow::Response& res;
73 std::string chassisId{};
74 const std::vector<const char*> types;
Ed Tanous2474adf2018-09-05 16:31:16 -070075 std::string chassisSubNode{};
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010076};
77
78/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -050079 * D-Bus inventory item associated with one or more sensors.
80 */
81class InventoryItem
82{
83 public:
84 InventoryItem(const std::string& objPath) :
85 objectPath(objPath), name(), isPresent(true), isFunctional(true),
86 isPowerSupply(false), manufacturer(), model(), partNumber(),
87 serialNumber(), sensors()
88 {
89 // Set inventory item name to last node of object path
90 auto pos = objectPath.rfind('/');
91 if ((pos != std::string::npos) && ((pos + 1) < objectPath.size()))
92 {
93 name = objectPath.substr(pos + 1);
94 }
95 }
96
97 std::string objectPath;
98 std::string name;
99 bool isPresent;
100 bool isFunctional;
101 bool isPowerSupply;
102 std::string manufacturer;
103 std::string model;
104 std::string partNumber;
105 std::string serialNumber;
106 std::set<std::string> sensors;
107};
108
109/**
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530110 * @brief Get objects with connection necessary for sensors
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200111 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100112 * @param sensorNames Sensors retrieved from chassis
113 * @param callback Callback for processing gathered connections
114 */
115template <typename Callback>
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530116void getObjectsWithConnection(
117 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700118 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530119 Callback&& callback)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700120{
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530121 BMCWEB_LOG_DEBUG << "getObjectsWithConnection enter";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700122 const std::string path = "/xyz/openbmc_project/sensors";
123 const std::array<std::string, 1> interfaces = {
124 "xyz.openbmc_project.Sensor.Value"};
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100125
Ed Tanous1abe55e2018-09-05 08:30:59 -0700126 // Response handler for parsing objects subtree
127 auto respHandler = [callback{std::move(callback)}, SensorsAsyncResp,
128 sensorNames](const boost::system::error_code ec,
129 const GetSubTreeType& subtree) {
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530130 BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler enter";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700131 if (ec)
132 {
Ed Tanous5f7d88c2018-11-14 14:08:56 -0800133 messages::internalError(SensorsAsyncResp->res);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530134 BMCWEB_LOG_ERROR
135 << "getObjectsWithConnection resp_handler: Dbus error " << ec;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700136 return;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100137 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100138
Ed Tanous1abe55e2018-09-05 08:30:59 -0700139 BMCWEB_LOG_DEBUG << "Found " << subtree.size() << " subtrees";
140
141 // Make unique list of connections only for requested sensor types and
142 // found in the chassis
143 boost::container::flat_set<std::string> connections;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530144 std::set<std::pair<std::string, std::string>> objectsWithConnection;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700145 // Intrinsic to avoid malloc. Most systems will have < 8 sensor
146 // producers
147 connections.reserve(8);
148
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700149 BMCWEB_LOG_DEBUG << "sensorNames list count: " << sensorNames->size();
150 for (const std::string& tsensor : *sensorNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700151 {
152 BMCWEB_LOG_DEBUG << "Sensor to find: " << tsensor;
153 }
154
155 for (const std::pair<
156 std::string,
157 std::vector<std::pair<std::string, std::vector<std::string>>>>&
158 object : subtree)
159 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700160 if (sensorNames->find(object.first) != sensorNames->end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700161 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700162 for (const std::pair<std::string, std::vector<std::string>>&
163 objData : object.second)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700164 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700165 BMCWEB_LOG_DEBUG << "Adding connection: " << objData.first;
166 connections.insert(objData.first);
167 objectsWithConnection.insert(
168 std::make_pair(object.first, objData.first));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700169 }
170 }
171 }
172 BMCWEB_LOG_DEBUG << "Found " << connections.size() << " connections";
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530173 callback(std::move(connections), std::move(objectsWithConnection));
174 BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler exit";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700175 };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700176 // Make call to ObjectMapper to find all sensors objects
177 crow::connections::systemBus->async_method_call(
178 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
179 "/xyz/openbmc_project/object_mapper",
180 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 2, interfaces);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530181 BMCWEB_LOG_DEBUG << "getObjectsWithConnection exit";
182}
183
184/**
185 * @brief Create connections necessary for sensors
186 * @param SensorsAsyncResp Pointer to object holding response data
187 * @param sensorNames Sensors retrieved from chassis
188 * @param callback Callback for processing gathered connections
189 */
190template <typename Callback>
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700191void getConnections(
192 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
193 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
194 Callback&& callback)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530195{
196 auto objectsWithConnectionCb =
197 [callback](const boost::container::flat_set<std::string>& connections,
198 const std::set<std::pair<std::string, std::string>>&
199 objectsWithConnection) {
200 callback(std::move(connections));
201 };
202 getObjectsWithConnection(SensorsAsyncResp, sensorNames,
203 std::move(objectsWithConnectionCb));
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100204}
205
206/**
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700207 * @brief Shrinks the list of sensors for processing
208 * @param SensorsAysncResp The class holding the Redfish response
209 * @param allSensors A list of all the sensors associated to the
210 * chassis element (i.e. baseboard, front panel, etc...)
211 * @param activeSensors A list that is a reduction of the incoming
212 * allSensors list. Eliminate Thermal sensors when a Power request is
213 * made, and eliminate Power sensors when a Thermal request is made.
214 */
215void reduceSensorList(
216 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
217 const std::vector<std::string>* allSensors,
218 std::shared_ptr<boost::container::flat_set<std::string>> activeSensors)
219{
220 if (SensorsAsyncResp == nullptr)
221 {
222 return;
223 }
224 if ((allSensors == nullptr) || (activeSensors == nullptr))
225 {
226 messages::resourceNotFound(
227 SensorsAsyncResp->res, SensorsAsyncResp->chassisSubNode,
228 SensorsAsyncResp->chassisSubNode == "Thermal" ? "Temperatures"
229 : "Voltages");
230
231 return;
232 }
233 if (allSensors->empty())
234 {
235 // Nothing to do, the activeSensors object is also empty
236 return;
237 }
238
239 for (const char* type : SensorsAsyncResp->types)
240 {
241 for (const std::string& sensor : *allSensors)
242 {
243 if (boost::starts_with(sensor, type))
244 {
245 activeSensors->emplace(sensor);
246 }
247 }
248 }
249}
250
251/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100252 * @brief Retrieves requested chassis sensors and redundancy data from DBus .
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200253 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100254 * @param callback Callback for next step in gathered sensor processing
255 */
256template <typename Callback>
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700257void getChassis(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700258 Callback&& callback)
259{
260 BMCWEB_LOG_DEBUG << "getChassis enter";
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500261 const std::array<const char*, 2> interfaces = {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700262 "xyz.openbmc_project.Inventory.Item.Board",
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500263 "xyz.openbmc_project.Inventory.Item.Chassis"};
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700264 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp](
265 const boost::system::error_code ec,
266 const std::vector<std::string>& chassisPaths) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700267 BMCWEB_LOG_DEBUG << "getChassis respHandler enter";
268 if (ec)
269 {
270 BMCWEB_LOG_ERROR << "getChassis respHandler DBUS error: " << ec;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700271 messages::internalError(sensorsAsyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700272 return;
273 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100274
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700275 const std::string* chassisPath = nullptr;
276 std::string chassisName;
277 for (const std::string& chassis : chassisPaths)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700278 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700279 std::size_t lastPos = chassis.rfind("/");
280 if (lastPos == std::string::npos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700281 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700282 BMCWEB_LOG_ERROR << "Failed to find '/' in " << chassis;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700283 continue;
284 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700285 chassisName = chassis.substr(lastPos + 1);
286 if (chassisName == sensorsAsyncResp->chassisId)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700287 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700288 chassisPath = &chassis;
289 break;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700290 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700291 }
292 if (chassisPath == nullptr)
293 {
294 messages::resourceNotFound(sensorsAsyncResp->res, "Chassis",
295 sensorsAsyncResp->chassisId);
296 return;
297 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700298
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700299 const std::string& chassisSubNode = sensorsAsyncResp->chassisSubNode;
300 if (chassisSubNode == "Power")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700301 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700302 sensorsAsyncResp->res.jsonValue["@odata.type"] =
303 "#Power.v1_5_2.Power";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700304 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700305 else if (chassisSubNode == "Thermal")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700306 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700307 sensorsAsyncResp->res.jsonValue["@odata.type"] =
308 "#Thermal.v1_4_0.Thermal";
Jennifer Lee4f9a2132019-03-04 12:45:19 -0800309 sensorsAsyncResp->res.jsonValue["Fans"] = nlohmann::json::array();
310 sensorsAsyncResp->res.jsonValue["Temperatures"] =
311 nlohmann::json::array();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700312 }
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500313 else if (chassisSubNode == "Sensors")
314 {
315 sensorsAsyncResp->res.jsonValue["@odata.type"] =
316 "#SensorCollection.SensorCollection";
317 sensorsAsyncResp->res.jsonValue["@odata.context"] =
318 "/redfish/v1/$metadata#SensorCollection.SensorCollection";
319 sensorsAsyncResp->res.jsonValue["Description"] =
320 "Collection of Sensors for this Chassis";
321 sensorsAsyncResp->res.jsonValue["Members"] =
322 nlohmann::json::array();
323 sensorsAsyncResp->res.jsonValue["Members@odata.count"] = 0;
324 }
325
326 if (chassisSubNode != "Sensors")
327 {
328 sensorsAsyncResp->res.jsonValue["Id"] = chassisSubNode;
329 sensorsAsyncResp->res.jsonValue["@odata.context"] =
330 "/redfish/v1/$metadata#" + chassisSubNode + "." +
331 chassisSubNode;
332 }
333
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700334 sensorsAsyncResp->res.jsonValue["@odata.id"] =
335 "/redfish/v1/Chassis/" + sensorsAsyncResp->chassisId + "/" +
336 chassisSubNode;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700337 sensorsAsyncResp->res.jsonValue["Name"] = chassisSubNode;
338
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500339 // Get the list of all sensors for this Chassis element
340 std::string sensorPath = *chassisPath + "/all_sensors";
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700341 crow::connections::systemBus->async_method_call(
342 [sensorsAsyncResp, callback{std::move(callback)}](
Ed Tanous271584a2019-07-09 16:24:22 -0700343 const boost::system::error_code& e,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700344 const std::variant<std::vector<std::string>>&
345 variantEndpoints) {
Ed Tanous271584a2019-07-09 16:24:22 -0700346 if (e)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700347 {
Ed Tanous271584a2019-07-09 16:24:22 -0700348 if (e.value() != EBADR)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700349 {
350 messages::internalError(sensorsAsyncResp->res);
351 return;
352 }
353 }
354 const std::vector<std::string>* nodeSensorList =
355 std::get_if<std::vector<std::string>>(&(variantEndpoints));
356 if (nodeSensorList == nullptr)
357 {
358 messages::resourceNotFound(
359 sensorsAsyncResp->res, sensorsAsyncResp->chassisSubNode,
360 sensorsAsyncResp->chassisSubNode == "Thermal"
361 ? "Temperatures"
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500362 : sensorsAsyncResp->chassisSubNode == "Power"
363 ? "Voltages"
364 : "Sensors");
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700365 return;
366 }
367 const std::shared_ptr<boost::container::flat_set<std::string>>
368 culledSensorList = std::make_shared<
369 boost::container::flat_set<std::string>>();
370 reduceSensorList(sensorsAsyncResp, nodeSensorList,
371 culledSensorList);
372 callback(culledSensorList);
373 },
374 "xyz.openbmc_project.ObjectMapper", sensorPath,
375 "org.freedesktop.DBus.Properties", "Get",
376 "xyz.openbmc_project.Association", "endpoints");
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100377 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100378
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700379 // Get the Chassis Collection
Ed Tanous1abe55e2018-09-05 08:30:59 -0700380 crow::connections::systemBus->async_method_call(
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700381 respHandler, "xyz.openbmc_project.ObjectMapper",
382 "/xyz/openbmc_project/object_mapper",
383 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Ed Tanous271584a2019-07-09 16:24:22 -0700384 "/xyz/openbmc_project/inventory", 0, interfaces);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700385 BMCWEB_LOG_DEBUG << "getChassis exit";
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100386}
387
388/**
Shawn McCarneyde629b62019-03-08 10:42:51 -0600389 * @brief Finds all DBus object paths that implement ObjectManager.
390 *
391 * Creates a mapping from the associated connection name to the object path.
392 *
393 * Finds the object paths asynchronously. Invokes callback when information has
394 * been obtained.
395 *
396 * The callback must have the following signature:
397 * @code
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500398 * callback(std::shared_ptr<boost::container::flat_map<std::string,
399 * std::string>> objectMgrPaths)
Shawn McCarneyde629b62019-03-08 10:42:51 -0600400 * @endcode
401 *
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700402 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyde629b62019-03-08 10:42:51 -0600403 * @param callback Callback to invoke when object paths obtained.
404 */
405template <typename Callback>
406void getObjectManagerPaths(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
407 Callback&& callback)
408{
409 BMCWEB_LOG_DEBUG << "getObjectManagerPaths enter";
410 const std::array<std::string, 1> interfaces = {
411 "org.freedesktop.DBus.ObjectManager"};
412
413 // Response handler for GetSubTree DBus method
414 auto respHandler = [callback{std::move(callback)},
415 SensorsAsyncResp](const boost::system::error_code ec,
416 const GetSubTreeType& subtree) {
417 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler enter";
418 if (ec)
419 {
420 messages::internalError(SensorsAsyncResp->res);
421 BMCWEB_LOG_ERROR << "getObjectManagerPaths respHandler: DBus error "
422 << ec;
423 return;
424 }
425
426 // Loop over returned object paths
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500427 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
428 objectMgrPaths = std::make_shared<
429 boost::container::flat_map<std::string, std::string>>();
Shawn McCarneyde629b62019-03-08 10:42:51 -0600430 for (const std::pair<
431 std::string,
432 std::vector<std::pair<std::string, std::vector<std::string>>>>&
433 object : subtree)
434 {
435 // Loop over connections for current object path
436 const std::string& objectPath = object.first;
437 for (const std::pair<std::string, std::vector<std::string>>&
438 objData : object.second)
439 {
440 // Add mapping from connection to object path
441 const std::string& connection = objData.first;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500442 (*objectMgrPaths)[connection] = objectPath;
Shawn McCarneyde629b62019-03-08 10:42:51 -0600443 BMCWEB_LOG_DEBUG << "Added mapping " << connection << " -> "
444 << objectPath;
445 }
446 }
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500447 callback(objectMgrPaths);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600448 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler exit";
449 };
450
451 // Query mapper for all DBus object paths that implement ObjectManager
452 crow::connections::systemBus->async_method_call(
453 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
454 "/xyz/openbmc_project/object_mapper",
Ed Tanous271584a2019-07-09 16:24:22 -0700455 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0, interfaces);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600456 BMCWEB_LOG_DEBUG << "getObjectManagerPaths exit";
457}
458
459/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500460 * @brief Returns the Redfish State value for the specified inventory item.
461 * @param inventoryItem D-Bus inventory item associated with a sensor.
462 * @return State value for inventory item.
James Feist34dd1792019-05-17 14:10:54 -0700463 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500464static std::string getState(const InventoryItem* inventoryItem)
465{
466 if ((inventoryItem != nullptr) && !(inventoryItem->isPresent))
467 {
468 return "Absent";
469 }
James Feist34dd1792019-05-17 14:10:54 -0700470
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500471 return "Enabled";
472}
473
474/**
475 * @brief Returns the Redfish Health value for the specified sensor.
476 * @param sensorJson Sensor JSON object.
477 * @param interfacesDict Map of all sensor interfaces.
478 * @param inventoryItem D-Bus inventory item associated with the sensor. Will
479 * be nullptr if no associated inventory item was found.
480 * @return Health value for sensor.
481 */
James Feist34dd1792019-05-17 14:10:54 -0700482static std::string getHealth(
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500483 nlohmann::json& sensorJson,
James Feist34dd1792019-05-17 14:10:54 -0700484 const boost::container::flat_map<
485 std::string, boost::container::flat_map<std::string, SensorVariant>>&
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500486 interfacesDict,
487 const InventoryItem* inventoryItem)
James Feist34dd1792019-05-17 14:10:54 -0700488{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500489 // Get current health value (if any) in the sensor JSON object. Some JSON
490 // objects contain multiple sensors (such as PowerSupplies). We want to set
491 // the overall health to be the most severe of any of the sensors.
492 std::string currentHealth;
493 auto statusIt = sensorJson.find("Status");
494 if (statusIt != sensorJson.end())
495 {
496 auto healthIt = statusIt->find("Health");
497 if (healthIt != statusIt->end())
498 {
499 std::string* health = healthIt->get_ptr<std::string*>();
500 if (health != nullptr)
501 {
502 currentHealth = *health;
503 }
504 }
505 }
506
507 // If current health in JSON object is already Critical, return that. This
508 // should override the sensor health, which might be less severe.
509 if (currentHealth == "Critical")
510 {
511 return "Critical";
512 }
513
514 // Check if sensor has critical threshold alarm
James Feist34dd1792019-05-17 14:10:54 -0700515 auto criticalThresholdIt =
516 interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Critical");
517 if (criticalThresholdIt != interfacesDict.end())
518 {
519 auto thresholdHighIt =
520 criticalThresholdIt->second.find("CriticalAlarmHigh");
521 auto thresholdLowIt =
522 criticalThresholdIt->second.find("CriticalAlarmLow");
523 if (thresholdHighIt != criticalThresholdIt->second.end())
524 {
525 const bool* asserted = std::get_if<bool>(&thresholdHighIt->second);
526 if (asserted == nullptr)
527 {
528 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
529 }
530 else if (*asserted)
531 {
532 return "Critical";
533 }
534 }
535 if (thresholdLowIt != criticalThresholdIt->second.end())
536 {
537 const bool* asserted = std::get_if<bool>(&thresholdLowIt->second);
538 if (asserted == nullptr)
539 {
540 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
541 }
542 else if (*asserted)
543 {
544 return "Critical";
545 }
546 }
547 }
548
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500549 // Check if associated inventory item is not functional
550 if ((inventoryItem != nullptr) && !(inventoryItem->isFunctional))
551 {
552 return "Critical";
553 }
554
555 // If current health in JSON object is already Warning, return that. This
556 // should override the sensor status, which might be less severe.
557 if (currentHealth == "Warning")
558 {
559 return "Warning";
560 }
561
562 // Check if sensor has warning threshold alarm
James Feist34dd1792019-05-17 14:10:54 -0700563 auto warningThresholdIt =
564 interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Warning");
565 if (warningThresholdIt != interfacesDict.end())
566 {
567 auto thresholdHighIt =
568 warningThresholdIt->second.find("WarningAlarmHigh");
569 auto thresholdLowIt =
570 warningThresholdIt->second.find("WarningAlarmLow");
571 if (thresholdHighIt != warningThresholdIt->second.end())
572 {
573 const bool* asserted = std::get_if<bool>(&thresholdHighIt->second);
574 if (asserted == nullptr)
575 {
576 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
577 }
578 else if (*asserted)
579 {
580 return "Warning";
581 }
582 }
583 if (thresholdLowIt != warningThresholdIt->second.end())
584 {
585 const bool* asserted = std::get_if<bool>(&thresholdLowIt->second);
586 if (asserted == nullptr)
587 {
588 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
589 }
590 else if (*asserted)
591 {
592 return "Warning";
593 }
594 }
595 }
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500596
James Feist34dd1792019-05-17 14:10:54 -0700597 return "OK";
598}
599
600/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100601 * @brief Builds a json sensor representation of a sensor.
602 * @param sensorName The name of the sensor to be built
Gunnar Mills274fad52018-06-13 15:45:36 -0500603 * @param sensorType The type (temperature, fan_tach, etc) of the sensor to
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100604 * build
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500605 * @param sensorSchema The schema (Power, Thermal, etc) being associated with
606 * the sensor to build
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100607 * @param interfacesDict A dictionary of the interfaces and properties of said
608 * interfaces to be built from
609 * @param sensor_json The json object to fill
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500610 * @param inventoryItem D-Bus inventory item associated with the sensor. Will
611 * be nullptr if no associated inventory item was found.
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100612 */
613void objectInterfacesToJson(
614 const std::string& sensorName, const std::string& sensorType,
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500615 const std::string& sensorSchema,
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100616 const boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700617 std::string, boost::container::flat_map<std::string, SensorVariant>>&
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100618 interfacesDict,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500619 nlohmann::json& sensor_json, InventoryItem* inventoryItem)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700620{
621 // We need a value interface before we can do anything with it
622 auto valueIt = interfacesDict.find("xyz.openbmc_project.Sensor.Value");
623 if (valueIt == interfacesDict.end())
624 {
625 BMCWEB_LOG_ERROR << "Sensor doesn't have a value interface";
626 return;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100627 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100628
Ed Tanous1abe55e2018-09-05 08:30:59 -0700629 // Assume values exist as is (10^0 == 1) if no scale exists
630 int64_t scaleMultiplier = 0;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100631
Ed Tanous1abe55e2018-09-05 08:30:59 -0700632 auto scaleIt = valueIt->second.find("Scale");
633 // If a scale exists, pull value as int64, and use the scaling.
634 if (scaleIt != valueIt->second.end())
635 {
Ed Tanousabf2add2019-01-22 16:40:12 -0800636 const int64_t* int64Value = std::get_if<int64_t>(&scaleIt->second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700637 if (int64Value != nullptr)
638 {
639 scaleMultiplier = *int64Value;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100640 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100641 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700642
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500643 if (sensorSchema == "Sensors")
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500644 {
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500645 // For sensors in SensorCollection we set Id instead of MemberId,
646 // including power sensors.
647 sensor_json["Id"] = sensorName;
648 sensor_json["Name"] = boost::replace_all_copy(sensorName, "_", " ");
649 }
650 else if (sensorType != "power")
651 {
652 // Set MemberId and Name for non-power sensors. For PowerSupplies and
653 // PowerControl, those properties have more general values because
654 // multiple sensors can be stored in the same JSON object.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500655 sensor_json["MemberId"] = sensorName;
656 sensor_json["Name"] = boost::replace_all_copy(sensorName, "_", " ");
657 }
Ed Tanouse742b6c2019-05-03 15:06:53 -0700658
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500659 sensor_json["Status"]["State"] = getState(inventoryItem);
660 sensor_json["Status"]["Health"] =
661 getHealth(sensor_json, interfacesDict, inventoryItem);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700662
663 // Parameter to set to override the type we get from dbus, and force it to
664 // int, regardless of what is available. This is used for schemas like fan,
665 // that require integers, not floats.
666 bool forceToInt = false;
667
Anthony Wilson3929aca2019-07-19 15:42:33 -0500668 nlohmann::json::json_pointer unit("/Reading");
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500669 if (sensorSchema == "Sensors")
670 {
671 sensor_json["@odata.type"] = "#Sensor.v1_0_0.Sensor";
672 sensor_json["@odata.context"] = "/redfish/v1/$metadata#Sensor.Sensor";
673 if (sensorType == "power")
674 {
675 sensor_json["ReadingUnits"] = "Watts";
676 }
677 else if (sensorType == "current")
678 {
679 sensor_json["ReadingUnits"] = "Amperes";
680 }
681 }
682 else if (sensorType == "temperature")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700683 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500684 unit = "/ReadingCelsius"_json_pointer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700685 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Temperature";
686 // TODO(ed) Documentation says that path should be type fan_tach,
687 // implementation seems to implement fan
688 }
689 else if (sensorType == "fan" || sensorType == "fan_tach")
690 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500691 unit = "/Reading"_json_pointer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700692 sensor_json["ReadingUnits"] = "RPM";
693 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
694 forceToInt = true;
695 }
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700696 else if (sensorType == "fan_pwm")
697 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500698 unit = "/Reading"_json_pointer;
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700699 sensor_json["ReadingUnits"] = "Percent";
700 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
701 forceToInt = true;
702 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700703 else if (sensorType == "voltage")
704 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500705 unit = "/ReadingVolts"_json_pointer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700706 sensor_json["@odata.type"] = "#Power.v1_0_0.Voltage";
707 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700708 else if (sensorType == "power")
709 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700710 std::string sensorNameLower =
711 boost::algorithm::to_lower_copy(sensorName);
712
Eddie James028f7eb2019-05-17 21:24:36 +0000713 if (!sensorName.compare("total_power"))
714 {
Gunnar Mills7ab06f42019-07-02 13:07:16 -0500715 sensor_json["@odata.type"] = "#Power.v1_0_0.PowerControl";
716 // Put multiple "sensors" into a single PowerControl, so have
717 // generic names for MemberId and Name. Follows Redfish mockup.
718 sensor_json["MemberId"] = "0";
719 sensor_json["Name"] = "Chassis Power Control";
Anthony Wilson3929aca2019-07-19 15:42:33 -0500720 unit = "/PowerConsumedWatts"_json_pointer;
Eddie James028f7eb2019-05-17 21:24:36 +0000721 }
722 else if (sensorNameLower.find("input") != std::string::npos)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700723 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500724 unit = "/PowerInputWatts"_json_pointer;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700725 }
726 else
727 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500728 unit = "/PowerOutputWatts"_json_pointer;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700729 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700730 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700731 else
732 {
733 BMCWEB_LOG_ERROR << "Redfish cannot map object type for " << sensorName;
734 return;
735 }
736 // Map of dbus interface name, dbus property name and redfish property_name
Anthony Wilson3929aca2019-07-19 15:42:33 -0500737 std::vector<
738 std::tuple<const char*, const char*, nlohmann::json::json_pointer>>
739 properties;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700740 properties.reserve(7);
741
742 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600743
Anthony Wilson3929aca2019-07-19 15:42:33 -0500744 if (sensorSchema == "Sensors")
745 {
746 properties.emplace_back(
747 "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningHigh",
748 "/Thresholds/UpperCaution/Reading"_json_pointer);
749 properties.emplace_back(
750 "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningLow",
751 "/Thresholds/LowerCaution/Reading"_json_pointer);
752 properties.emplace_back(
753 "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalHigh",
754 "/Thresholds/UpperCritical/Reading"_json_pointer);
755 properties.emplace_back(
756 "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalLow",
757 "/Thresholds/LowerCritical/Reading"_json_pointer);
758 }
759 else if (sensorType != "power")
Shawn McCarneyde629b62019-03-08 10:42:51 -0600760 {
761 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500762 "WarningHigh",
763 "/UpperThresholdNonCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600764 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500765 "WarningLow",
766 "/LowerThresholdNonCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600767 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500768 "CriticalHigh",
769 "/UpperThresholdCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600770 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500771 "CriticalLow",
772 "/LowerThresholdCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600773 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700774
Ed Tanous2474adf2018-09-05 16:31:16 -0700775 // TODO Need to get UpperThresholdFatal and LowerThresholdFatal
776
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500777 if (sensorSchema == "Sensors")
778 {
779 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500780 "/ReadingRangeMin"_json_pointer);
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500781 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500782 "/ReadingRangeMax"_json_pointer);
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500783 }
784 else if (sensorType == "temperature")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700785 {
786 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500787 "/MinReadingRangeTemp"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700788 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500789 "/MaxReadingRangeTemp"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700790 }
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500791 else if (sensorType != "power")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700792 {
793 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500794 "/MinReadingRange"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700795 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500796 "/MaxReadingRange"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700797 }
798
Anthony Wilson3929aca2019-07-19 15:42:33 -0500799 for (const std::tuple<const char*, const char*,
800 nlohmann::json::json_pointer>& p : properties)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700801 {
802 auto interfaceProperties = interfacesDict.find(std::get<0>(p));
803 if (interfaceProperties != interfacesDict.end())
804 {
Ed Tanous271584a2019-07-09 16:24:22 -0700805 auto thisValueIt = interfaceProperties->second.find(std::get<1>(p));
806 if (thisValueIt != interfaceProperties->second.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700807 {
Ed Tanous271584a2019-07-09 16:24:22 -0700808 const SensorVariant& valueVariant = thisValueIt->second;
Anthony Wilson3929aca2019-07-19 15:42:33 -0500809
810 // The property we want to set may be nested json, so use
811 // a json_pointer for easy indexing into the json structure.
812 const nlohmann::json::json_pointer& key = std::get<2>(p);
813
Ed Tanous1abe55e2018-09-05 08:30:59 -0700814 // Attempt to pull the int64 directly
Ed Tanousabf2add2019-01-22 16:40:12 -0800815 const int64_t* int64Value = std::get_if<int64_t>(&valueVariant);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700816
Ed Tanousabf2add2019-01-22 16:40:12 -0800817 const double* doubleValue = std::get_if<double>(&valueVariant);
Eddie James028f7eb2019-05-17 21:24:36 +0000818 const uint32_t* uValue = std::get_if<uint32_t>(&valueVariant);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700819 double temp = 0.0;
820 if (int64Value != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700821 {
Ed Tanous271584a2019-07-09 16:24:22 -0700822 temp = static_cast<double>(*int64Value);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700823 }
824 else if (doubleValue != nullptr)
825 {
826 temp = *doubleValue;
827 }
Eddie James028f7eb2019-05-17 21:24:36 +0000828 else if (uValue != nullptr)
829 {
830 temp = *uValue;
831 }
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700832 else
833 {
834 BMCWEB_LOG_ERROR
835 << "Got value interface that wasn't int or double";
836 continue;
837 }
838 temp = temp * std::pow(10, scaleMultiplier);
839 if (forceToInt)
840 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500841 sensor_json[key] = static_cast<int64_t>(temp);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700842 }
843 else
844 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500845 sensor_json[key] = temp;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700846 }
847 }
848 }
849 }
850 BMCWEB_LOG_DEBUG << "Added sensor " << sensorName;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100851}
852
James Feist8bd25cc2019-03-15 15:14:00 -0700853static void
854 populateFanRedundancy(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp)
855{
856 crow::connections::systemBus->async_method_call(
857 [sensorsAsyncResp](const boost::system::error_code ec,
858 const GetSubTreeType& resp) {
859 if (ec)
860 {
861 return; // don't have to have this interface
862 }
Ed Tanouse278c182019-03-13 16:23:37 -0700863 for (const std::pair<std::string,
864 std::vector<std::pair<
865 std::string, std::vector<std::string>>>>&
866 pathPair : resp)
James Feist8bd25cc2019-03-15 15:14:00 -0700867 {
Ed Tanouse278c182019-03-13 16:23:37 -0700868 const std::string& path = pathPair.first;
869 const std::vector<
870 std::pair<std::string, std::vector<std::string>>>& objDict =
871 pathPair.second;
James Feist8bd25cc2019-03-15 15:14:00 -0700872 if (objDict.empty())
873 {
874 continue; // this should be impossible
875 }
876
877 const std::string& owner = objDict.begin()->first;
878 crow::connections::systemBus->async_method_call(
879 [path, owner,
Ed Tanous271584a2019-07-09 16:24:22 -0700880 sensorsAsyncResp](const boost::system::error_code e,
James Feist8bd25cc2019-03-15 15:14:00 -0700881 std::variant<std::vector<std::string>>
882 variantEndpoints) {
Ed Tanous271584a2019-07-09 16:24:22 -0700883 if (e)
James Feist8bd25cc2019-03-15 15:14:00 -0700884 {
885 return; // if they don't have an association we
886 // can't tell what chassis is
887 }
888 // verify part of the right chassis
889 auto endpoints = std::get_if<std::vector<std::string>>(
890 &variantEndpoints);
891
892 if (endpoints == nullptr)
893 {
894 BMCWEB_LOG_ERROR << "Invalid association interface";
895 messages::internalError(sensorsAsyncResp->res);
896 return;
897 }
898
899 auto found = std::find_if(
900 endpoints->begin(), endpoints->end(),
901 [sensorsAsyncResp](const std::string& entry) {
902 return entry.find(
903 sensorsAsyncResp->chassisId) !=
904 std::string::npos;
905 });
906
907 if (found == endpoints->end())
908 {
909 return;
910 }
911 crow::connections::systemBus->async_method_call(
912 [path, sensorsAsyncResp](
Ed Tanous271584a2019-07-09 16:24:22 -0700913 const boost::system::error_code& err,
James Feist8bd25cc2019-03-15 15:14:00 -0700914 const boost::container::flat_map<
915 std::string,
916 std::variant<uint8_t,
917 std::vector<std::string>,
918 std::string>>& ret) {
Ed Tanous271584a2019-07-09 16:24:22 -0700919 if (err)
James Feist8bd25cc2019-03-15 15:14:00 -0700920 {
921 return; // don't have to have this
922 // interface
923 }
924 auto findFailures = ret.find("AllowedFailures");
925 auto findCollection = ret.find("Collection");
926 auto findStatus = ret.find("Status");
927
928 if (findFailures == ret.end() ||
929 findCollection == ret.end() ||
930 findStatus == ret.end())
931 {
932 BMCWEB_LOG_ERROR
933 << "Invalid redundancy interface";
934 messages::internalError(
935 sensorsAsyncResp->res);
936 return;
937 }
938
939 auto allowedFailures = std::get_if<uint8_t>(
940 &(findFailures->second));
941 auto collection =
942 std::get_if<std::vector<std::string>>(
943 &(findCollection->second));
944 auto status = std::get_if<std::string>(
945 &(findStatus->second));
946
947 if (allowedFailures == nullptr ||
948 collection == nullptr || status == nullptr)
949 {
950
951 BMCWEB_LOG_ERROR
952 << "Invalid redundancy interface "
953 "types";
954 messages::internalError(
955 sensorsAsyncResp->res);
956 return;
957 }
958 size_t lastSlash = path.rfind("/");
959 if (lastSlash == std::string::npos)
960 {
961 // this should be impossible
962 messages::internalError(
963 sensorsAsyncResp->res);
964 return;
965 }
966 std::string name = path.substr(lastSlash + 1);
967 std::replace(name.begin(), name.end(), '_',
968 ' ');
969
970 std::string health;
971
972 if (boost::ends_with(*status, "Full"))
973 {
974 health = "OK";
975 }
976 else if (boost::ends_with(*status, "Degraded"))
977 {
978 health = "Warning";
979 }
980 else
981 {
982 health = "Critical";
983 }
984 std::vector<nlohmann::json> redfishCollection;
985 const auto& fanRedfish =
986 sensorsAsyncResp->res.jsonValue["Fans"];
987 for (const std::string& item : *collection)
988 {
989 lastSlash = item.rfind("/");
990 // make a copy as collection is const
991 std::string itemName =
992 item.substr(lastSlash + 1);
993 /*
994 todo(ed): merge patch that fixes the names
995 std::replace(itemName.begin(),
996 itemName.end(), '_', ' ');*/
997 auto schemaItem = std::find_if(
998 fanRedfish.begin(), fanRedfish.end(),
999 [itemName](const nlohmann::json& fan) {
1000 return fan["MemberId"] == itemName;
1001 });
1002 if (schemaItem != fanRedfish.end())
1003 {
1004 redfishCollection.push_back(
1005 {{"@odata.id",
1006 (*schemaItem)["@odata.id"]}});
1007 }
1008 else
1009 {
1010 BMCWEB_LOG_ERROR
1011 << "failed to find fan in schema";
1012 messages::internalError(
1013 sensorsAsyncResp->res);
1014 return;
1015 }
1016 }
1017
Ed Tanous271584a2019-07-09 16:24:22 -07001018 nlohmann::json& jResp =
1019 sensorsAsyncResp->res
1020 .jsonValue["Redundancy"];
1021 jResp.push_back(
James Feist8bd25cc2019-03-15 15:14:00 -07001022 {{"@odata.id",
1023 "/refish/v1/Chassis/" +
1024 sensorsAsyncResp->chassisId + "/" +
1025 sensorsAsyncResp->chassisSubNode +
1026 "#/Redundancy/" +
Ed Tanous271584a2019-07-09 16:24:22 -07001027 std::to_string(jResp.size())},
James Feist8bd25cc2019-03-15 15:14:00 -07001028 {"@odata.type",
1029 "#Redundancy.v1_3_2.Redundancy"},
1030 {"MinNumNeeded",
1031 collection->size() - *allowedFailures},
1032 {"MemberId", name},
1033 {"Mode", "N+m"},
1034 {"Name", name},
1035 {"RedundancySet", redfishCollection},
1036 {"Status",
1037 {{"Health", health},
1038 {"State", "Enabled"}}}});
1039 },
1040 owner, path, "org.freedesktop.DBus.Properties",
1041 "GetAll",
1042 "xyz.openbmc_project.Control.FanRedundancy");
1043 },
James Feist02e92e32019-06-26 12:07:05 -07001044 "xyz.openbmc_project.ObjectMapper", path + "/chassis",
James Feist8bd25cc2019-03-15 15:14:00 -07001045 "org.freedesktop.DBus.Properties", "Get",
1046 "xyz.openbmc_project.Association", "endpoints");
1047 }
1048 },
1049 "xyz.openbmc_project.ObjectMapper",
1050 "/xyz/openbmc_project/object_mapper",
1051 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
1052 "/xyz/openbmc_project/control", 2,
1053 std::array<const char*, 1>{
1054 "xyz.openbmc_project.Control.FanRedundancy"});
1055}
1056
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001057void sortJSONResponse(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
1058{
1059 nlohmann::json& response = SensorsAsyncResp->res.jsonValue;
1060 std::array<std::string, 2> sensorHeaders{"Temperatures", "Fans"};
1061 if (SensorsAsyncResp->chassisSubNode == "Power")
1062 {
1063 sensorHeaders = {"Voltages", "PowerSupplies"};
1064 }
1065 for (const std::string& sensorGroup : sensorHeaders)
1066 {
1067 nlohmann::json::iterator entry = response.find(sensorGroup);
1068 if (entry != response.end())
1069 {
1070 std::sort(entry->begin(), entry->end(),
1071 [](nlohmann::json& c1, nlohmann::json& c2) {
1072 return c1["Name"] < c2["Name"];
1073 });
1074
1075 // add the index counts to the end of each entry
1076 size_t count = 0;
1077 for (nlohmann::json& sensorJson : *entry)
1078 {
1079 nlohmann::json::iterator odata = sensorJson.find("@odata.id");
1080 if (odata == sensorJson.end())
1081 {
1082 continue;
1083 }
1084 std::string* value = odata->get_ptr<std::string*>();
1085 if (value != nullptr)
1086 {
1087 *value += std::to_string(count);
1088 count++;
1089 }
1090 }
1091 }
1092 }
1093}
1094
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001095/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001096 * @brief Finds the inventory item with the specified object path.
1097 * @param inventoryItems D-Bus inventory items associated with sensors.
1098 * @param invItemObjPath D-Bus object path of inventory item.
1099 * @return Inventory item within vector, or nullptr if no match found.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001100 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001101static InventoryItem* findInventoryItem(
1102 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1103 const std::string& invItemObjPath)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001104{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001105 for (InventoryItem& inventoryItem : *inventoryItems)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001106 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001107 if (inventoryItem.objectPath == invItemObjPath)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001108 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001109 return &inventoryItem;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001110 }
1111 }
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001112 return nullptr;
1113}
1114
1115/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001116 * @brief Finds the inventory item associated with the specified sensor.
1117 * @param inventoryItems D-Bus inventory items associated with sensors.
1118 * @param sensorObjPath D-Bus object path of sensor.
1119 * @return Inventory item within vector, or nullptr if no match found.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001120 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001121static InventoryItem* findInventoryItemForSensor(
1122 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1123 const std::string& sensorObjPath)
1124{
1125 for (InventoryItem& inventoryItem : *inventoryItems)
1126 {
1127 if (inventoryItem.sensors.count(sensorObjPath) > 0)
1128 {
1129 return &inventoryItem;
1130 }
1131 }
1132 return nullptr;
1133}
1134
1135/**
1136 * @brief Adds inventory item and associated sensor to specified vector.
1137 *
1138 * Adds a new InventoryItem to the vector if necessary. Searches for an
1139 * existing InventoryItem with the specified object path. If not found, one is
1140 * added to the vector.
1141 *
1142 * Next, the specified sensor is added to the set of sensors associated with the
1143 * InventoryItem.
1144 *
1145 * @param inventoryItems D-Bus inventory items associated with sensors.
1146 * @param invItemObjPath D-Bus object path of inventory item.
1147 * @param sensorObjPath D-Bus object path of sensor
1148 */
1149static void
1150 addInventoryItem(std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1151 const std::string& invItemObjPath,
1152 const std::string& sensorObjPath)
1153{
1154 // Look for inventory item in vector
1155 InventoryItem* inventoryItem =
1156 findInventoryItem(inventoryItems, invItemObjPath);
1157
1158 // If inventory item doesn't exist in vector, add it
1159 if (inventoryItem == nullptr)
1160 {
1161 inventoryItems->emplace_back(invItemObjPath);
1162 inventoryItem = &(inventoryItems->back());
1163 }
1164
1165 // Add sensor to set of sensors associated with inventory item
1166 inventoryItem->sensors.emplace(sensorObjPath);
1167}
1168
1169/**
1170 * @brief Stores D-Bus data in the specified inventory item.
1171 *
1172 * Finds D-Bus data in the specified map of interfaces. Stores the data in the
1173 * specified InventoryItem.
1174 *
1175 * This data is later used to provide sensor property values in the JSON
1176 * response.
1177 *
1178 * @param inventoryItem Inventory item where data will be stored.
1179 * @param interfacesDict Map containing D-Bus interfaces and their properties
1180 * for the specified inventory item.
1181 */
1182static void storeInventoryItemData(
1183 InventoryItem& inventoryItem,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001184 const boost::container::flat_map<
1185 std::string, boost::container::flat_map<std::string, SensorVariant>>&
1186 interfacesDict)
1187{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001188 // Get properties from Inventory.Item interface
1189 auto interfaceIt =
1190 interfacesDict.find("xyz.openbmc_project.Inventory.Item");
1191 if (interfaceIt != interfacesDict.end())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001192 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001193 auto propertyIt = interfaceIt->second.find("Present");
1194 if (propertyIt != interfaceIt->second.end())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001195 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001196 const bool* value = std::get_if<bool>(&propertyIt->second);
1197 if (value != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001198 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001199 inventoryItem.isPresent = *value;
1200 }
1201 }
1202 }
1203
1204 // Check if Inventory.Item.PowerSupply interface is present
1205 interfaceIt =
1206 interfacesDict.find("xyz.openbmc_project.Inventory.Item.PowerSupply");
1207 if (interfaceIt != interfacesDict.end())
1208 {
1209 inventoryItem.isPowerSupply = true;
1210 }
1211
1212 // Get properties from Inventory.Decorator.Asset interface
1213 interfaceIt =
1214 interfacesDict.find("xyz.openbmc_project.Inventory.Decorator.Asset");
1215 if (interfaceIt != interfacesDict.end())
1216 {
1217 auto propertyIt = interfaceIt->second.find("Manufacturer");
1218 if (propertyIt != interfaceIt->second.end())
1219 {
1220 const std::string* value =
1221 std::get_if<std::string>(&propertyIt->second);
1222 if (value != nullptr)
1223 {
1224 inventoryItem.manufacturer = *value;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001225 }
1226 }
1227
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001228 propertyIt = interfaceIt->second.find("Model");
1229 if (propertyIt != interfaceIt->second.end())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001230 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001231 const std::string* value =
1232 std::get_if<std::string>(&propertyIt->second);
1233 if (value != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001234 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001235 inventoryItem.model = *value;
1236 }
1237 }
1238
1239 propertyIt = interfaceIt->second.find("PartNumber");
1240 if (propertyIt != interfaceIt->second.end())
1241 {
1242 const std::string* value =
1243 std::get_if<std::string>(&propertyIt->second);
1244 if (value != nullptr)
1245 {
1246 inventoryItem.partNumber = *value;
1247 }
1248 }
1249
1250 propertyIt = interfaceIt->second.find("SerialNumber");
1251 if (propertyIt != interfaceIt->second.end())
1252 {
1253 const std::string* value =
1254 std::get_if<std::string>(&propertyIt->second);
1255 if (value != nullptr)
1256 {
1257 inventoryItem.serialNumber = *value;
1258 }
1259 }
1260 }
1261
1262 // Get properties from State.Decorator.OperationalStatus interface
1263 interfaceIt = interfacesDict.find(
1264 "xyz.openbmc_project.State.Decorator.OperationalStatus");
1265 if (interfaceIt != interfacesDict.end())
1266 {
1267 auto propertyIt = interfaceIt->second.find("Functional");
1268 if (propertyIt != interfaceIt->second.end())
1269 {
1270 const bool* value = std::get_if<bool>(&propertyIt->second);
1271 if (value != nullptr)
1272 {
1273 inventoryItem.isFunctional = *value;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001274 }
1275 }
1276 }
1277}
1278
1279/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001280 * @brief Gets D-Bus data for inventory items associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001281 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001282 * Uses the specified connections (services) to obtain D-Bus data for inventory
1283 * items associated with sensors. Stores the resulting data in the
1284 * inventoryItems vector.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001285 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001286 * This data is later used to provide sensor property values in the JSON
1287 * response.
1288 *
1289 * Finds the inventory item data asynchronously. Invokes callback when data has
1290 * been obtained.
1291 *
1292 * The callback must have the following signature:
1293 * @code
1294 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
1295 * @endcode
1296 *
1297 * This function is called recursively, obtaining data asynchronously from one
1298 * connection in each call. This ensures the callback is not invoked until the
1299 * last asynchronous function has completed.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001300 *
1301 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001302 * @param inventoryItems D-Bus inventory items associated with sensors.
1303 * @param invConnections Connections that provide data for the inventory items.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001304 * @param objectMgrPaths Mappings from connection name to DBus object path that
1305 * implements ObjectManager.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001306 * @param callback Callback to invoke when inventory data has been obtained.
1307 * @param invConnectionsIndex Current index in invConnections. Only specified
1308 * in recursive calls to this function.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001309 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001310template <typename Callback>
1311static void getInventoryItemsData(
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001312 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001313 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001314 std::shared_ptr<boost::container::flat_set<std::string>> invConnections,
1315 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001316 objectMgrPaths,
Ed Tanous271584a2019-07-09 16:24:22 -07001317 Callback&& callback, size_t invConnectionsIndex = 0)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001318{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001319 BMCWEB_LOG_DEBUG << "getInventoryItemsData enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001320
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001321 // If no more connections left, call callback
1322 if (invConnectionsIndex >= invConnections->size())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001323 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001324 callback(inventoryItems);
1325 BMCWEB_LOG_DEBUG << "getInventoryItemsData exit";
1326 return;
1327 }
1328
1329 // Get inventory item data from current connection
1330 auto it = invConnections->nth(invConnectionsIndex);
1331 if (it != invConnections->end())
1332 {
1333 const std::string& invConnection = *it;
1334
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001335 // Response handler for GetManagedObjects
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001336 auto respHandler = [sensorsAsyncResp, inventoryItems, invConnections,
1337 objectMgrPaths, callback{std::move(callback)},
1338 invConnectionsIndex](
1339 const boost::system::error_code ec,
1340 ManagedObjectsVectorType& resp) {
1341 BMCWEB_LOG_DEBUG << "getInventoryItemsData respHandler enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001342 if (ec)
1343 {
1344 BMCWEB_LOG_ERROR
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001345 << "getInventoryItemsData respHandler DBus error " << ec;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001346 messages::internalError(sensorsAsyncResp->res);
1347 return;
1348 }
1349
1350 // Loop through returned object paths
1351 for (const auto& objDictEntry : resp)
1352 {
1353 const std::string& objPath =
1354 static_cast<const std::string&>(objDictEntry.first);
1355
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001356 // If this object path is one of the specified inventory items
1357 InventoryItem* inventoryItem =
1358 findInventoryItem(inventoryItems, objPath);
1359 if (inventoryItem != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001360 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001361 // Store inventory data in InventoryItem
1362 storeInventoryItemData(*inventoryItem, objDictEntry.second);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001363 }
1364 }
1365
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001366 // Recurse to get inventory item data from next connection
1367 getInventoryItemsData(sensorsAsyncResp, inventoryItems,
1368 invConnections, objectMgrPaths,
1369 std::move(callback), invConnectionsIndex + 1);
1370
1371 BMCWEB_LOG_DEBUG << "getInventoryItemsData respHandler exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001372 };
1373
1374 // Find DBus object path that implements ObjectManager for the current
1375 // connection. If no mapping found, default to "/".
1376 auto iter = objectMgrPaths->find(invConnection);
1377 const std::string& objectMgrPath =
1378 (iter != objectMgrPaths->end()) ? iter->second : "/";
1379 BMCWEB_LOG_DEBUG << "ObjectManager path for " << invConnection << " is "
1380 << objectMgrPath;
1381
1382 // Get all object paths and their interfaces for current connection
1383 crow::connections::systemBus->async_method_call(
1384 std::move(respHandler), invConnection, objectMgrPath,
1385 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1386 }
1387
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001388 BMCWEB_LOG_DEBUG << "getInventoryItemsData exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001389}
1390
1391/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001392 * @brief Gets connections that provide D-Bus data for inventory items.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001393 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001394 * Gets the D-Bus connections (services) that provide data for the inventory
1395 * items that are associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001396 *
1397 * Finds the connections asynchronously. Invokes callback when information has
1398 * been obtained.
1399 *
1400 * The callback must have the following signature:
1401 * @code
1402 * callback(std::shared_ptr<boost::container::flat_set<std::string>>
1403 * invConnections)
1404 * @endcode
1405 *
1406 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001407 * @param inventoryItems D-Bus inventory items associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001408 * @param callback Callback to invoke when connections have been obtained.
1409 */
1410template <typename Callback>
1411static void getInventoryItemsConnections(
1412 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001413 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001414 Callback&& callback)
1415{
1416 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections enter";
1417
1418 const std::string path = "/xyz/openbmc_project/inventory";
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001419 const std::array<std::string, 4> interfaces = {
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001420 "xyz.openbmc_project.Inventory.Item",
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001421 "xyz.openbmc_project.Inventory.Item.PowerSupply",
1422 "xyz.openbmc_project.Inventory.Decorator.Asset",
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001423 "xyz.openbmc_project.State.Decorator.OperationalStatus"};
1424
1425 // Response handler for parsing output from GetSubTree
1426 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001427 inventoryItems](const boost::system::error_code ec,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001428 const GetSubTreeType& subtree) {
1429 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections respHandler enter";
1430 if (ec)
1431 {
1432 messages::internalError(sensorsAsyncResp->res);
1433 BMCWEB_LOG_ERROR
1434 << "getInventoryItemsConnections respHandler DBus error " << ec;
1435 return;
1436 }
1437
1438 // Make unique list of connections for desired inventory items
1439 std::shared_ptr<boost::container::flat_set<std::string>>
1440 invConnections =
1441 std::make_shared<boost::container::flat_set<std::string>>();
1442 invConnections->reserve(8);
1443
1444 // Loop through objects from GetSubTree
1445 for (const std::pair<
1446 std::string,
1447 std::vector<std::pair<std::string, std::vector<std::string>>>>&
1448 object : subtree)
1449 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001450 // Check if object path is one of the specified inventory items
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001451 const std::string& objPath = object.first;
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001452 if (findInventoryItem(inventoryItems, objPath) != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001453 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001454 // Store all connections to inventory item
1455 for (const std::pair<std::string, std::vector<std::string>>&
1456 objData : object.second)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001457 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001458 const std::string& invConnection = objData.first;
1459 invConnections->insert(invConnection);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001460 }
1461 }
1462 }
1463 callback(invConnections);
1464 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections respHandler exit";
1465 };
1466
1467 // Make call to ObjectMapper to find all inventory items
1468 crow::connections::systemBus->async_method_call(
1469 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
1470 "/xyz/openbmc_project/object_mapper",
1471 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 0, interfaces);
1472 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections exit";
1473}
1474
1475/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001476 * @brief Gets associations from sensors to inventory items.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001477 *
1478 * Looks for ObjectMapper associations from the specified sensors to related
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001479 * inventory items.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001480 *
1481 * Finds the inventory items asynchronously. Invokes callback when information
1482 * has been obtained.
1483 *
1484 * The callback must have the following signature:
1485 * @code
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001486 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001487 * @endcode
1488 *
1489 * @param sensorsAsyncResp Pointer to object holding response data.
1490 * @param sensorNames All sensors within the current chassis.
1491 * @param objectMgrPaths Mappings from connection name to DBus object path that
1492 * implements ObjectManager.
1493 * @param callback Callback to invoke when inventory items have been obtained.
1494 */
1495template <typename Callback>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001496static void getInventoryItemAssociations(
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001497 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1498 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
1499 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1500 objectMgrPaths,
1501 Callback&& callback)
1502{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001503 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001504
1505 // Response handler for GetManagedObjects
1506 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
1507 sensorNames](const boost::system::error_code ec,
1508 dbus::utility::ManagedObjectType& resp) {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001509 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations respHandler enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001510 if (ec)
1511 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001512 BMCWEB_LOG_ERROR
1513 << "getInventoryItemAssociations respHandler DBus error " << ec;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001514 messages::internalError(sensorsAsyncResp->res);
1515 return;
1516 }
1517
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001518 // Create vector to hold list of inventory items
1519 std::shared_ptr<std::vector<InventoryItem>> inventoryItems =
1520 std::make_shared<std::vector<InventoryItem>>();
1521
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001522 // Loop through returned object paths
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001523 std::string sensorAssocPath;
1524 sensorAssocPath.reserve(128); // avoid memory allocations
1525 for (const auto& objDictEntry : resp)
1526 {
1527 const std::string& objPath =
1528 static_cast<const std::string&>(objDictEntry.first);
1529 const boost::container::flat_map<
1530 std::string, boost::container::flat_map<
1531 std::string, dbus::utility::DbusVariantType>>&
1532 interfacesDict = objDictEntry.second;
1533
1534 // If path is inventory association for one of the specified sensors
1535 for (const std::string& sensorName : *sensorNames)
1536 {
1537 sensorAssocPath = sensorName;
1538 sensorAssocPath += "/inventory";
1539 if (objPath == sensorAssocPath)
1540 {
1541 // Get Association interface for object path
1542 auto assocIt =
1543 interfacesDict.find("xyz.openbmc_project.Association");
1544 if (assocIt != interfacesDict.end())
1545 {
1546 // Get inventory item from end point
1547 auto endpointsIt = assocIt->second.find("endpoints");
1548 if (endpointsIt != assocIt->second.end())
1549 {
1550 const std::vector<std::string>* endpoints =
1551 std::get_if<std::vector<std::string>>(
1552 &endpointsIt->second);
1553 if ((endpoints != nullptr) && !endpoints->empty())
1554 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001555 // Add inventory item to vector
1556 const std::string& invItemPath =
1557 endpoints->front();
1558 addInventoryItem(inventoryItems, invItemPath,
1559 sensorName);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001560 }
1561 }
1562 }
1563 break;
1564 }
1565 }
1566 }
1567
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001568 callback(inventoryItems);
1569 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations respHandler exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001570 };
1571
1572 // Find DBus object path that implements ObjectManager for ObjectMapper
1573 std::string connection = "xyz.openbmc_project.ObjectMapper";
1574 auto iter = objectMgrPaths->find(connection);
1575 const std::string& objectMgrPath =
1576 (iter != objectMgrPaths->end()) ? iter->second : "/";
1577 BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
1578 << objectMgrPath;
1579
1580 // Call GetManagedObjects on the ObjectMapper to get all associations
1581 crow::connections::systemBus->async_method_call(
1582 std::move(respHandler), connection, objectMgrPath,
1583 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1584
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001585 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001586}
1587
1588/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001589 * @brief Gets inventory items associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001590 *
1591 * Finds the inventory items that are associated with the specified sensors.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001592 * Then gets D-Bus data for the inventory items, such as presence and VPD.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001593 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001594 * This data is later used to provide sensor property values in the JSON
1595 * response.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001596 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001597 * Finds the inventory items asynchronously. Invokes callback when the
1598 * inventory items have been obtained.
1599 *
1600 * The callback must have the following signature:
1601 * @code
1602 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
1603 * @endcode
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001604 *
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.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001609 * @param callback Callback to invoke when inventory items have been obtained.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001610 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001611template <typename Callback>
1612static void getInventoryItems(
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>>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001616 objectMgrPaths,
1617 Callback&& callback)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001618{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001619 BMCWEB_LOG_DEBUG << "getInventoryItems enter";
1620 auto getInventoryItemAssociationsCb =
1621 [sensorsAsyncResp, objectMgrPaths, callback{std::move(callback)}](
1622 std::shared_ptr<std::vector<InventoryItem>> inventoryItems) {
1623 BMCWEB_LOG_DEBUG << "getInventoryItemAssociationsCb enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001624 auto getInventoryItemsConnectionsCb =
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001625 [sensorsAsyncResp, inventoryItems, objectMgrPaths,
1626 callback{std::move(callback)}](
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001627 std::shared_ptr<boost::container::flat_set<std::string>>
1628 invConnections) {
1629 BMCWEB_LOG_DEBUG << "getInventoryItemsConnectionsCb enter";
1630
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001631 // Get inventory item data from connections
1632 getInventoryItemsData(sensorsAsyncResp, inventoryItems,
1633 invConnections, objectMgrPaths,
1634 std::move(callback));
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001635
1636 BMCWEB_LOG_DEBUG << "getInventoryItemsConnectionsCb exit";
1637 };
1638
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001639 // Get connections that provide inventory item data
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001640 getInventoryItemsConnections(
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001641 sensorsAsyncResp, inventoryItems,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001642 std::move(getInventoryItemsConnectionsCb));
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001643 BMCWEB_LOG_DEBUG << "getInventoryItemAssociationsCb exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001644 };
1645
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001646 // Get associations from sensors to inventory items
1647 getInventoryItemAssociations(sensorsAsyncResp, sensorNames, objectMgrPaths,
1648 std::move(getInventoryItemAssociationsCb));
1649 BMCWEB_LOG_DEBUG << "getInventoryItems exit";
1650}
1651
1652/**
1653 * @brief Returns JSON PowerSupply object for the specified inventory item.
1654 *
1655 * Searches for a JSON PowerSupply object that matches the specified inventory
1656 * item. If one is not found, a new PowerSupply object is added to the JSON
1657 * array.
1658 *
1659 * Multiple sensors are often associated with one power supply inventory item.
1660 * As a result, multiple sensor values are stored in one JSON PowerSupply
1661 * object.
1662 *
1663 * @param powerSupplyArray JSON array containing Redfish PowerSupply objects.
1664 * @param inventoryItem Inventory item for the power supply.
1665 * @param chassisId Chassis that contains the power supply.
1666 * @return JSON PowerSupply object for the specified inventory item.
1667 */
1668static nlohmann::json& getPowerSupply(nlohmann::json& powerSupplyArray,
1669 const InventoryItem& inventoryItem,
1670 const std::string& chassisId)
1671{
1672 // Check if matching PowerSupply object already exists in JSON array
1673 for (nlohmann::json& powerSupply : powerSupplyArray)
1674 {
1675 if (powerSupply["MemberId"] == inventoryItem.name)
1676 {
1677 return powerSupply;
1678 }
1679 }
1680
1681 // Add new PowerSupply object to JSON array
1682 powerSupplyArray.push_back({});
1683 nlohmann::json& powerSupply = powerSupplyArray.back();
1684 powerSupply["@odata.id"] =
1685 "/redfish/v1/Chassis/" + chassisId + "/Power#/PowerSupplies/";
1686 powerSupply["MemberId"] = inventoryItem.name;
1687 powerSupply["Name"] = boost::replace_all_copy(inventoryItem.name, "_", " ");
1688 powerSupply["Manufacturer"] = inventoryItem.manufacturer;
1689 powerSupply["Model"] = inventoryItem.model;
1690 powerSupply["PartNumber"] = inventoryItem.partNumber;
1691 powerSupply["SerialNumber"] = inventoryItem.serialNumber;
1692 powerSupply["Status"]["State"] = getState(&inventoryItem);
1693
1694 const char* health = inventoryItem.isFunctional ? "OK" : "Critical";
1695 powerSupply["Status"]["Health"] = health;
1696
1697 return powerSupply;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001698}
1699
1700/**
Shawn McCarneyde629b62019-03-08 10:42:51 -06001701 * @brief Gets the values of the specified sensors.
1702 *
1703 * Stores the results as JSON in the SensorsAsyncResp.
1704 *
1705 * Gets the sensor values asynchronously. Stores the results later when the
1706 * information has been obtained.
1707 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001708 * The sensorNames set contains all requested sensors for the current chassis.
Shawn McCarneyde629b62019-03-08 10:42:51 -06001709 *
1710 * To minimize the number of DBus calls, the DBus method
1711 * org.freedesktop.DBus.ObjectManager.GetManagedObjects() is used to get the
1712 * values of all sensors provided by a connection (service).
1713 *
1714 * The connections set contains all the connections that provide sensor values.
1715 *
1716 * The objectMgrPaths map contains mappings from a connection name to the
1717 * corresponding DBus object path that implements ObjectManager.
1718 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001719 * The InventoryItem vector contains D-Bus inventory items associated with the
1720 * sensors. Inventory item data is needed for some Redfish sensor properties.
1721 *
Shawn McCarneyde629b62019-03-08 10:42:51 -06001722 * @param SensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001723 * @param sensorNames All requested sensors within the current chassis.
Shawn McCarneyde629b62019-03-08 10:42:51 -06001724 * @param connections Connections that provide sensor values.
1725 * @param objectMgrPaths Mappings from connection name to DBus object path that
1726 * implements ObjectManager.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001727 * @param inventoryItems Inventory items associated with the sensors.
Shawn McCarneyde629b62019-03-08 10:42:51 -06001728 */
1729void getSensorData(
1730 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001731 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
Shawn McCarneyde629b62019-03-08 10:42:51 -06001732 const boost::container::flat_set<std::string>& connections,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001733 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001734 objectMgrPaths,
1735 std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
Shawn McCarneyde629b62019-03-08 10:42:51 -06001736{
1737 BMCWEB_LOG_DEBUG << "getSensorData enter";
1738 // Get managed objects from all services exposing sensors
1739 for (const std::string& connection : connections)
1740 {
1741 // Response handler to process managed objects
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001742 auto getManagedObjectsCb = [SensorsAsyncResp, sensorNames,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001743 inventoryItems](
Shawn McCarneyde629b62019-03-08 10:42:51 -06001744 const boost::system::error_code ec,
1745 ManagedObjectsVectorType& resp) {
1746 BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter";
1747 if (ec)
1748 {
1749 BMCWEB_LOG_ERROR << "getManagedObjectsCb DBUS error: " << ec;
1750 messages::internalError(SensorsAsyncResp->res);
1751 return;
1752 }
1753 // Go through all objects and update response with sensor data
1754 for (const auto& objDictEntry : resp)
1755 {
1756 const std::string& objPath =
1757 static_cast<const std::string&>(objDictEntry.first);
1758 BMCWEB_LOG_DEBUG << "getManagedObjectsCb parsing object "
1759 << objPath;
1760
Shawn McCarneyde629b62019-03-08 10:42:51 -06001761 std::vector<std::string> split;
1762 // Reserve space for
1763 // /xyz/openbmc_project/sensors/<name>/<subname>
1764 split.reserve(6);
1765 boost::algorithm::split(split, objPath, boost::is_any_of("/"));
1766 if (split.size() < 6)
1767 {
1768 BMCWEB_LOG_ERROR << "Got path that isn't long enough "
1769 << objPath;
1770 continue;
1771 }
1772 // These indexes aren't intuitive, as boost::split puts an empty
1773 // string at the beginning
1774 const std::string& sensorType = split[4];
1775 const std::string& sensorName = split[5];
1776 BMCWEB_LOG_DEBUG << "sensorName " << sensorName
1777 << " sensorType " << sensorType;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001778 if (sensorNames->find(objPath) == sensorNames->end())
Shawn McCarneyde629b62019-03-08 10:42:51 -06001779 {
1780 BMCWEB_LOG_ERROR << sensorName << " not in sensor list ";
1781 continue;
1782 }
1783
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001784 // Find inventory item (if any) associated with sensor
1785 InventoryItem* inventoryItem =
1786 findInventoryItemForSensor(inventoryItems, objPath);
1787
Anthony Wilson95a3eca2019-06-11 10:44:47 -05001788 const std::string& sensorSchema =
1789 SensorsAsyncResp->chassisSubNode;
1790
1791 nlohmann::json* sensorJson = nullptr;
1792
1793 if (sensorSchema == "Sensors")
Shawn McCarneyde629b62019-03-08 10:42:51 -06001794 {
Anthony Wilson95a3eca2019-06-11 10:44:47 -05001795 SensorsAsyncResp->res.jsonValue["@odata.id"] =
1796 "/redfish/v1/Chassis/" + SensorsAsyncResp->chassisId +
1797 "/" + SensorsAsyncResp->chassisSubNode + "/" +
1798 sensorName;
1799 sensorJson = &(SensorsAsyncResp->res.jsonValue);
Shawn McCarneyde629b62019-03-08 10:42:51 -06001800 }
1801 else
1802 {
Ed Tanous271584a2019-07-09 16:24:22 -07001803 std::string fieldName;
Anthony Wilson95a3eca2019-06-11 10:44:47 -05001804 if (sensorType == "temperature")
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001805 {
Anthony Wilson95a3eca2019-06-11 10:44:47 -05001806 fieldName = "Temperatures";
1807 }
1808 else if (sensorType == "fan" || sensorType == "fan_tach" ||
1809 sensorType == "fan_pwm")
1810 {
1811 fieldName = "Fans";
1812 }
1813 else if (sensorType == "voltage")
1814 {
1815 fieldName = "Voltages";
1816 }
1817 else if (sensorType == "power")
1818 {
1819 if (!sensorName.compare("total_power"))
1820 {
1821 fieldName = "PowerControl";
1822 }
1823 else if ((inventoryItem != nullptr) &&
1824 (inventoryItem->isPowerSupply))
1825 {
1826 fieldName = "PowerSupplies";
1827 }
1828 else
1829 {
1830 // Other power sensors are in SensorCollection
1831 continue;
1832 }
1833 }
1834 else
1835 {
1836 BMCWEB_LOG_ERROR << "Unsure how to handle sensorType "
1837 << sensorType;
1838 continue;
1839 }
1840
1841 nlohmann::json& tempArray =
1842 SensorsAsyncResp->res.jsonValue[fieldName];
1843 if (fieldName == "PowerControl")
1844 {
1845 if (tempArray.empty())
1846 {
1847 // Put multiple "sensors" into a single
1848 // PowerControl. Follows MemberId naming and
1849 // naming in power.hpp.
1850 tempArray.push_back(
1851 {{"@odata.id",
1852 "/redfish/v1/Chassis/" +
1853 SensorsAsyncResp->chassisId + "/" +
1854 SensorsAsyncResp->chassisSubNode + "#/" +
1855 fieldName + "/0"}});
1856 }
1857 sensorJson = &(tempArray.back());
1858 }
1859 else if (fieldName == "PowerSupplies")
1860 {
1861 if (inventoryItem != nullptr)
1862 {
1863 sensorJson =
1864 &(getPowerSupply(tempArray, *inventoryItem,
1865 SensorsAsyncResp->chassisId));
1866 }
1867 }
1868 else
1869 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001870 tempArray.push_back(
1871 {{"@odata.id",
1872 "/redfish/v1/Chassis/" +
1873 SensorsAsyncResp->chassisId + "/" +
1874 SensorsAsyncResp->chassisSubNode + "#/" +
Anthony Wilson95a3eca2019-06-11 10:44:47 -05001875 fieldName + "/"}});
1876 sensorJson = &(tempArray.back());
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001877 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001878 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06001879
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001880 if (sensorJson != nullptr)
1881 {
1882 objectInterfacesToJson(sensorName, sensorType,
Anthony Wilson95a3eca2019-06-11 10:44:47 -05001883 SensorsAsyncResp->chassisSubNode,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001884 objDictEntry.second, *sensorJson,
1885 inventoryItem);
1886 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06001887 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001888 if (SensorsAsyncResp.use_count() == 1)
James Feist8bd25cc2019-03-15 15:14:00 -07001889 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001890 sortJSONResponse(SensorsAsyncResp);
1891 if (SensorsAsyncResp->chassisSubNode == "Thermal")
1892 {
1893 populateFanRedundancy(SensorsAsyncResp);
1894 }
James Feist8bd25cc2019-03-15 15:14:00 -07001895 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06001896 BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit";
1897 };
1898
1899 // Find DBus object path that implements ObjectManager for the current
1900 // connection. If no mapping found, default to "/".
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001901 auto iter = objectMgrPaths->find(connection);
Shawn McCarneyde629b62019-03-08 10:42:51 -06001902 const std::string& objectMgrPath =
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001903 (iter != objectMgrPaths->end()) ? iter->second : "/";
Shawn McCarneyde629b62019-03-08 10:42:51 -06001904 BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
1905 << objectMgrPath;
1906
1907 crow::connections::systemBus->async_method_call(
1908 getManagedObjectsCb, connection, objectMgrPath,
1909 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1910 };
1911 BMCWEB_LOG_DEBUG << "getSensorData exit";
1912}
1913
Anthony Wilson95a3eca2019-06-11 10:44:47 -05001914void processSensorList(
1915 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
1916 std::shared_ptr<boost::container::flat_set<std::string>> sensorNames)
1917{
1918 auto getConnectionCb =
1919 [SensorsAsyncResp, sensorNames](
1920 const boost::container::flat_set<std::string>& connections) {
1921 BMCWEB_LOG_DEBUG << "getConnectionCb enter";
1922 auto getObjectManagerPathsCb =
1923 [SensorsAsyncResp, sensorNames, connections](
1924 std::shared_ptr<
1925 boost::container::flat_map<std::string, std::string>>
1926 objectMgrPaths) {
1927 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb enter";
1928 auto getInventoryItemsCb =
1929 [SensorsAsyncResp, sensorNames, connections,
1930 objectMgrPaths](
1931 std::shared_ptr<std::vector<InventoryItem>>
1932 inventoryItems) {
1933 BMCWEB_LOG_DEBUG << "getInventoryItemsCb enter";
1934 // Get sensor data and store results in JSON
1935 getSensorData(SensorsAsyncResp, sensorNames,
1936 connections, objectMgrPaths,
1937 inventoryItems);
1938 BMCWEB_LOG_DEBUG << "getInventoryItemsCb exit";
1939 };
1940
1941 // Get inventory items associated with sensors
1942 getInventoryItems(SensorsAsyncResp, sensorNames,
1943 objectMgrPaths,
1944 std::move(getInventoryItemsCb));
1945
1946 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb exit";
1947 };
1948
1949 // Get mapping from connection names to the DBus object
1950 // paths that implement the ObjectManager interface
1951 getObjectManagerPaths(SensorsAsyncResp,
1952 std::move(getObjectManagerPathsCb));
1953 BMCWEB_LOG_DEBUG << "getConnectionCb exit";
1954 };
1955
1956 // Get set of connections that provide sensor values
1957 getConnections(SensorsAsyncResp, sensorNames, std::move(getConnectionCb));
1958}
1959
Shawn McCarneyde629b62019-03-08 10:42:51 -06001960/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001961 * @brief Entry point for retrieving sensors data related to requested
1962 * chassis.
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001963 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001964 */
Ed Tanous1abe55e2018-09-05 08:30:59 -07001965void getChassisData(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
1966{
1967 BMCWEB_LOG_DEBUG << "getChassisData enter";
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001968 auto getChassisCb =
1969 [SensorsAsyncResp](
1970 std::shared_ptr<boost::container::flat_set<std::string>>
1971 sensorNames) {
1972 BMCWEB_LOG_DEBUG << "getChassisCb enter";
Anthony Wilson95a3eca2019-06-11 10:44:47 -05001973 processSensorList(SensorsAsyncResp, sensorNames);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001974 BMCWEB_LOG_DEBUG << "getChassisCb exit";
1975 };
Jennifer Lee4f9a2132019-03-04 12:45:19 -08001976 SensorsAsyncResp->res.jsonValue["Redundancy"] = nlohmann::json::array();
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001977
Shawn McCarney26f03892019-05-03 13:20:24 -05001978 // Get set of sensors in chassis
Ed Tanous1abe55e2018-09-05 08:30:59 -07001979 getChassis(SensorsAsyncResp, std::move(getChassisCb));
1980 BMCWEB_LOG_DEBUG << "getChassisData exit";
Ed Tanous271584a2019-07-09 16:24:22 -07001981}
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001982
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301983/**
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001984 * @brief Find the requested sensorName in the list of all sensors supplied by
1985 * the chassis node
1986 *
1987 * @param sensorName The sensor name supplied in the PATCH request
1988 * @param sensorsList The list of sensors managed by the chassis node
1989 * @param sensorsModified The list of sensors that were found as a result of
1990 * repeated calls to this function
1991 */
1992bool findSensorNameUsingSensorPath(
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05301993 std::string_view sensorName,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001994 boost::container::flat_set<std::string>& sensorsList,
1995 boost::container::flat_set<std::string>& sensorsModified)
1996{
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05301997 for (std::string_view chassisSensor : sensorsList)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001998 {
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05301999 std::size_t pos = chassisSensor.rfind("/");
2000 if (pos >= (chassisSensor.size() - 1))
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002001 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002002 continue;
2003 }
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05302004 std::string_view thisSensorName = chassisSensor.substr(pos + 1);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002005 if (thisSensorName == sensorName)
2006 {
2007 sensorsModified.emplace(chassisSensor);
2008 return true;
2009 }
2010 }
2011 return false;
2012}
2013
2014/**
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302015 * @brief Entry point for overriding sensor values of given sensor
2016 *
2017 * @param res response object
2018 * @param req request object
2019 * @param params parameter passed for CRUD
2020 * @param typeList TypeList of sensors for the resource queried
2021 * @param chassisSubNode Chassis Node for which the query has to happen
2022 */
2023void setSensorOverride(crow::Response& res, const crow::Request& req,
2024 const std::vector<std::string>& params,
Ed Tanous85e14242019-06-27 15:04:09 -07002025 const std::vector<const char*> typeList,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302026 const std::string& chassisSubNode)
2027{
2028
2029 // TODO: Need to figure out dynamic way to restrict patch (Set Sensor
2030 // override) based on another d-bus announcement to be more generic.
2031 if (params.size() != 1)
2032 {
2033 messages::internalError(res);
2034 res.end();
2035 return;
2036 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302037
2038 std::unordered_map<std::string, std::vector<nlohmann::json>> allCollections;
2039 std::optional<std::vector<nlohmann::json>> temperatureCollections;
2040 std::optional<std::vector<nlohmann::json>> fanCollections;
2041 std::vector<nlohmann::json> voltageCollections;
2042 BMCWEB_LOG_INFO << "setSensorOverride for subNode" << chassisSubNode
2043 << "\n";
2044
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302045 if (chassisSubNode == "Thermal")
2046 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302047 if (!json_util::readJson(req, res, "Temperatures",
2048 temperatureCollections, "Fans",
2049 fanCollections))
2050 {
2051 return;
2052 }
2053 if (!temperatureCollections && !fanCollections)
2054 {
2055 messages::resourceNotFound(res, "Thermal",
2056 "Temperatures / Voltages");
2057 res.end();
2058 return;
2059 }
2060 if (temperatureCollections)
2061 {
2062 allCollections.emplace("Temperatures",
2063 *std::move(temperatureCollections));
2064 }
2065 if (fanCollections)
2066 {
2067 allCollections.emplace("Fans", *std::move(fanCollections));
2068 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302069 }
2070 else if (chassisSubNode == "Power")
2071 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302072 if (!json_util::readJson(req, res, "Voltages", voltageCollections))
2073 {
2074 return;
2075 }
2076 allCollections.emplace("Voltages", std::move(voltageCollections));
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302077 }
2078 else
2079 {
2080 res.result(boost::beast::http::status::not_found);
2081 res.end();
2082 return;
2083 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302084
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302085 const char* propertyValueName;
2086 std::unordered_map<std::string, std::pair<double, std::string>> overrideMap;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302087 std::string memberId;
2088 double value;
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302089 for (auto& collectionItems : allCollections)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302090 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302091 if (collectionItems.first == "Temperatures")
2092 {
2093 propertyValueName = "ReadingCelsius";
2094 }
2095 else if (collectionItems.first == "Fans")
2096 {
2097 propertyValueName = "Reading";
2098 }
2099 else
2100 {
2101 propertyValueName = "ReadingVolts";
2102 }
2103 for (auto& item : collectionItems.second)
2104 {
2105 if (!json_util::readJson(item, res, "MemberId", memberId,
2106 propertyValueName, value))
2107 {
2108 return;
2109 }
2110 overrideMap.emplace(memberId,
2111 std::make_pair(value, collectionItems.first));
2112 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302113 }
2114 const std::string& chassisName = params[0];
2115 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
2116 res, chassisName, typeList, chassisSubNode);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002117 auto getChassisSensorListCb = [sensorAsyncResp,
2118 overrideMap](const std::shared_ptr<
2119 boost::container::flat_set<
2120 std::string>>
2121 sensorsList) {
2122 // Match sensor names in the PATCH request to those managed by the
2123 // chassis node
2124 const std::shared_ptr<boost::container::flat_set<std::string>>
2125 sensorNames =
2126 std::make_shared<boost::container::flat_set<std::string>>();
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302127 for (const auto& item : overrideMap)
2128 {
2129 const auto& sensor = item.first;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002130 if (!findSensorNameUsingSensorPath(sensor, *sensorsList,
2131 *sensorNames))
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302132 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302133 BMCWEB_LOG_INFO << "Unable to find memberId " << item.first;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302134 messages::resourceNotFound(sensorAsyncResp->res,
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302135 item.second.second, item.first);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302136 return;
2137 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302138 }
2139 // Get the connection to which the memberId belongs
2140 auto getObjectsWithConnectionCb =
2141 [sensorAsyncResp, overrideMap](
2142 const boost::container::flat_set<std::string>& connections,
2143 const std::set<std::pair<std::string, std::string>>&
2144 objectsWithConnection) {
2145 if (objectsWithConnection.size() != overrideMap.size())
2146 {
2147 BMCWEB_LOG_INFO
2148 << "Unable to find all objects with proper connection "
2149 << objectsWithConnection.size() << " requested "
2150 << overrideMap.size() << "\n";
2151 messages::resourceNotFound(
2152 sensorAsyncResp->res,
2153 sensorAsyncResp->chassisSubNode == "Thermal"
2154 ? "Temperatures"
2155 : "Voltages",
2156 "Count");
2157 return;
2158 }
2159 for (const auto& item : objectsWithConnection)
2160 {
2161
2162 auto lastPos = item.first.rfind('/');
2163 if (lastPos == std::string::npos)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302164 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302165 messages::internalError(sensorAsyncResp->res);
2166 return;
2167 }
2168 std::string sensorName = item.first.substr(lastPos + 1);
2169
2170 const auto& iterator = overrideMap.find(sensorName);
2171 if (iterator == overrideMap.end())
2172 {
2173 BMCWEB_LOG_INFO << "Unable to find sensor object"
2174 << item.first << "\n";
2175 messages::internalError(sensorAsyncResp->res);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302176 return;
2177 }
2178 crow::connections::systemBus->async_method_call(
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302179 [sensorAsyncResp](const boost::system::error_code ec) {
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302180 if (ec)
2181 {
2182 BMCWEB_LOG_DEBUG
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302183 << "setOverrideValueStatus DBUS error: "
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302184 << ec;
2185 messages::internalError(sensorAsyncResp->res);
2186 return;
2187 }
2188 },
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302189 item.second, item.first,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302190 "org.freedesktop.DBus.Properties", "Set",
2191 "xyz.openbmc_project.Sensor.Value", "Value",
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302192 sdbusplus::message::variant<double>(
2193 iterator->second.first));
2194 }
2195 };
2196 // Get object with connection for the given sensor name
2197 getObjectsWithConnection(sensorAsyncResp, sensorNames,
2198 std::move(getObjectsWithConnectionCb));
2199 };
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302200 // get full sensor list for the given chassisId and cross verify the sensor.
2201 getChassis(sensorAsyncResp, std::move(getChassisSensorListCb));
2202}
2203
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002204class SensorCollection : public Node
2205{
2206 public:
2207 SensorCollection(CrowApp& app) :
2208 Node(app, "/redfish/v1/Chassis/<str>/Sensors", std::string())
2209 {
2210 entityPrivileges = {
2211 {boost::beast::http::verb::get, {{"Login"}}},
2212 {boost::beast::http::verb::head, {{"Login"}}},
2213 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2214 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2215 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2216 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2217 }
2218
2219 private:
2220 std::vector<const char*> typeList = {
2221 "/xyz/openbmc_project/sensors/power",
2222 "/xyz/openbmc_project/sensors/current"};
2223 void doGet(crow::Response& res, const crow::Request& req,
2224 const std::vector<std::string>& params) override
2225 {
2226 BMCWEB_LOG_DEBUG << "SensorCollection doGet enter";
2227 if (params.size() != 1)
2228 {
2229 BMCWEB_LOG_DEBUG << "SensorCollection doGet param size < 1";
2230 messages::internalError(res);
2231 res.end();
2232 return;
2233 }
2234
2235 const std::string& chassisId = params[0];
2236 std::shared_ptr<SensorsAsyncResp> asyncResp =
2237 std::make_shared<SensorsAsyncResp>(res, chassisId, typeList,
2238 "Sensors");
2239
2240 auto getChassisCb =
2241 [asyncResp](std::shared_ptr<boost::container::flat_set<std::string>>
2242 sensorNames) {
2243 BMCWEB_LOG_DEBUG << "getChassisCb enter";
2244
2245 nlohmann::json& entriesArray =
2246 asyncResp->res.jsonValue["Members"];
2247 for (auto& sensor : *sensorNames)
2248 {
2249 BMCWEB_LOG_DEBUG << "Adding sensor: " << sensor;
2250
2251 std::size_t lastPos = sensor.rfind("/");
2252 if (lastPos == std::string::npos ||
2253 lastPos + 1 >= sensor.size())
2254 {
2255 BMCWEB_LOG_ERROR << "Invalid sensor path: " << sensor;
2256 messages::internalError(asyncResp->res);
2257 return;
2258 }
2259 std::string sensorName = sensor.substr(lastPos + 1);
2260 entriesArray.push_back(
2261 {{"@odata.id",
2262 "/redfish/v1/Chassis/" + asyncResp->chassisId + "/" +
2263 asyncResp->chassisSubNode + "/" + sensorName}});
2264 }
2265
2266 asyncResp->res.jsonValue["Members@odata.count"] =
2267 entriesArray.size();
2268 BMCWEB_LOG_DEBUG << "getChassisCb exit";
2269 };
2270
2271 // Get set of sensors in chassis
2272 getChassis(asyncResp, std::move(getChassisCb));
2273 BMCWEB_LOG_DEBUG << "SensorCollection doGet exit";
2274 }
2275};
2276
2277class Sensor : public Node
2278{
2279 public:
2280 Sensor(CrowApp& app) :
2281 Node(app, "/redfish/v1/Chassis/<str>/Sensors/<str>/", std::string(),
2282 std::string())
2283 {
2284 entityPrivileges = {
2285 {boost::beast::http::verb::get, {{"Login"}}},
2286 {boost::beast::http::verb::head, {{"Login"}}},
2287 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2288 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2289 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2290 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2291 }
2292
2293 private:
2294 void doGet(crow::Response& res, const crow::Request& req,
2295 const std::vector<std::string>& params) override
2296 {
2297 BMCWEB_LOG_DEBUG << "Sensor doGet enter";
2298 if (params.size() != 2)
2299 {
2300 BMCWEB_LOG_DEBUG << "Sensor doGet param size < 2";
2301 messages::internalError(res);
2302 res.end();
2303 return;
2304 }
2305 const std::string& chassisId = params[0];
2306 std::shared_ptr<SensorsAsyncResp> asyncResp =
2307 std::make_shared<SensorsAsyncResp>(
2308 res, chassisId, std::vector<const char*>(), "Sensors");
2309
2310 const std::string& sensorName = params[1];
2311 const std::array<const char*, 1> interfaces = {
2312 "xyz.openbmc_project.Sensor.Value"};
2313
2314 // Get a list of all of the sensors that implement Sensor.Value
2315 // and get the path and service name associated with the sensor
2316 crow::connections::systemBus->async_method_call(
2317 [asyncResp, sensorName](const boost::system::error_code ec,
2318 const GetSubTreeType& subtree) {
2319 BMCWEB_LOG_DEBUG << "respHandler1 enter";
2320 if (ec)
2321 {
2322 messages::internalError(asyncResp->res);
2323 BMCWEB_LOG_ERROR << "Sensor getSensorPaths resp_handler: "
2324 << "Dbus error " << ec;
2325 return;
2326 }
2327
2328 GetSubTreeType::const_iterator it = std::find_if(
2329 subtree.begin(), subtree.end(),
2330 [sensorName](
2331 const std::pair<
2332 std::string,
2333 std::vector<std::pair<std::string,
2334 std::vector<std::string>>>>&
2335 object) {
2336 std::string_view sensor = object.first;
2337 std::size_t lastPos = sensor.rfind("/");
2338 if (lastPos == std::string::npos ||
2339 lastPos + 1 >= sensor.size())
2340 {
2341 BMCWEB_LOG_ERROR << "Invalid sensor path: "
2342 << sensor;
2343 return false;
2344 }
2345 std::string_view name = sensor.substr(lastPos + 1);
2346
2347 return name == sensorName;
2348 });
2349
2350 if (it == subtree.end())
2351 {
2352 BMCWEB_LOG_ERROR << "Could not find path for sensor: "
2353 << sensorName;
2354 messages::resourceNotFound(asyncResp->res, "Sensor",
2355 sensorName);
2356 return;
2357 }
2358 std::string_view sensorPath = (*it).first;
2359 BMCWEB_LOG_DEBUG << "Found sensor path for sensor '"
2360 << sensorName << "': " << sensorPath;
2361
2362 const std::shared_ptr<boost::container::flat_set<std::string>>
2363 sensorList = std::make_shared<
2364 boost::container::flat_set<std::string>>();
2365
2366 sensorList->emplace(sensorPath);
2367 processSensorList(asyncResp, sensorList);
2368 BMCWEB_LOG_DEBUG << "respHandler1 exit";
2369 },
2370 "xyz.openbmc_project.ObjectMapper",
2371 "/xyz/openbmc_project/object_mapper",
2372 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
2373 "/xyz/openbmc_project/sensors", 2, interfaces);
2374 }
2375};
2376
Ed Tanous1abe55e2018-09-05 08:30:59 -07002377} // namespace redfish