blob: 3fd86d6218cb89bf3d571377aa3abbe7d17a29bf [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
18#include <math.h>
Ed Tanous1abe55e2018-09-05 08:30:59 -070019
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010020#include <boost/algorithm/string/predicate.hpp>
21#include <boost/algorithm/string/split.hpp>
22#include <boost/container/flat_map.hpp>
23#include <boost/range/algorithm/replace_copy_if.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070024#include <dbus_singleton.hpp>
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +053025#include <utils/json_utils.hpp>
Ed Tanousabf2add2019-01-22 16:40:12 -080026#include <variant>
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010027
Ed Tanous1abe55e2018-09-05 08:30:59 -070028namespace redfish
29{
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010030
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010031using GetSubTreeType = std::vector<
32 std::pair<std::string,
33 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
34
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -050035using SensorVariant =
36 std::variant<int64_t, double, uint32_t, bool, std::string>;
Ed Tanousaa2e59c2018-04-12 12:17:20 -070037
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010038using ManagedObjectsVectorType = std::vector<std::pair<
Ed Tanousaa2e59c2018-04-12 12:17:20 -070039 sdbusplus::message::object_path,
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010040 boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -070041 std::string, boost::container::flat_map<std::string, SensorVariant>>>>;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010042
43/**
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020044 * SensorsAsyncResp
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010045 * Gathers data needed for response processing after async calls are done
46 */
Ed Tanous1abe55e2018-09-05 08:30:59 -070047class SensorsAsyncResp
48{
49 public:
50 SensorsAsyncResp(crow::Response& response, const std::string& chassisId,
Ed Tanous85e14242019-06-27 15:04:09 -070051 const std::vector<const char*> types,
Ed Tanous2474adf2018-09-05 16:31:16 -070052 const std::string& subNode) :
Ed Tanous43b761d2019-02-13 20:10:56 -080053 res(response),
54 chassisId(chassisId), types(types), chassisSubNode(subNode)
Ed Tanous1abe55e2018-09-05 08:30:59 -070055 {
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010056 }
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020057
Ed Tanous1abe55e2018-09-05 08:30:59 -070058 ~SensorsAsyncResp()
59 {
60 if (res.result() == boost::beast::http::status::internal_server_error)
61 {
62 // Reset the json object to clear out any data that made it in
63 // before the error happened todo(ed) handle error condition with
64 // proper code
65 res.jsonValue = nlohmann::json::object();
66 }
67 res.end();
68 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010069
Ed Tanous1abe55e2018-09-05 08:30:59 -070070 crow::Response& res;
71 std::string chassisId{};
72 const std::vector<const char*> types;
Ed Tanous2474adf2018-09-05 16:31:16 -070073 std::string chassisSubNode{};
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010074};
75
76/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -050077 * D-Bus inventory item associated with one or more sensors.
78 */
79class InventoryItem
80{
81 public:
82 InventoryItem(const std::string& objPath) :
83 objectPath(objPath), name(), isPresent(true), isFunctional(true),
84 isPowerSupply(false), manufacturer(), model(), partNumber(),
85 serialNumber(), sensors()
86 {
87 // Set inventory item name to last node of object path
88 auto pos = objectPath.rfind('/');
89 if ((pos != std::string::npos) && ((pos + 1) < objectPath.size()))
90 {
91 name = objectPath.substr(pos + 1);
92 }
93 }
94
95 std::string objectPath;
96 std::string name;
97 bool isPresent;
98 bool isFunctional;
99 bool isPowerSupply;
100 std::string manufacturer;
101 std::string model;
102 std::string partNumber;
103 std::string serialNumber;
104 std::set<std::string> sensors;
105};
106
107/**
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530108 * @brief Get objects with connection necessary for sensors
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200109 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100110 * @param sensorNames Sensors retrieved from chassis
111 * @param callback Callback for processing gathered connections
112 */
113template <typename Callback>
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530114void getObjectsWithConnection(
115 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700116 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530117 Callback&& callback)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700118{
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530119 BMCWEB_LOG_DEBUG << "getObjectsWithConnection enter";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700120 const std::string path = "/xyz/openbmc_project/sensors";
121 const std::array<std::string, 1> interfaces = {
122 "xyz.openbmc_project.Sensor.Value"};
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100123
Ed Tanous1abe55e2018-09-05 08:30:59 -0700124 // Response handler for parsing objects subtree
125 auto respHandler = [callback{std::move(callback)}, SensorsAsyncResp,
126 sensorNames](const boost::system::error_code ec,
127 const GetSubTreeType& subtree) {
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530128 BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler enter";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700129 if (ec)
130 {
Ed Tanous5f7d88c2018-11-14 14:08:56 -0800131 messages::internalError(SensorsAsyncResp->res);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530132 BMCWEB_LOG_ERROR
133 << "getObjectsWithConnection resp_handler: Dbus error " << ec;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700134 return;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100135 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100136
Ed Tanous1abe55e2018-09-05 08:30:59 -0700137 BMCWEB_LOG_DEBUG << "Found " << subtree.size() << " subtrees";
138
139 // Make unique list of connections only for requested sensor types and
140 // found in the chassis
141 boost::container::flat_set<std::string> connections;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530142 std::set<std::pair<std::string, std::string>> objectsWithConnection;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700143 // Intrinsic to avoid malloc. Most systems will have < 8 sensor
144 // producers
145 connections.reserve(8);
146
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700147 BMCWEB_LOG_DEBUG << "sensorNames list count: " << sensorNames->size();
148 for (const std::string& tsensor : *sensorNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700149 {
150 BMCWEB_LOG_DEBUG << "Sensor to find: " << tsensor;
151 }
152
153 for (const std::pair<
154 std::string,
155 std::vector<std::pair<std::string, std::vector<std::string>>>>&
156 object : subtree)
157 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700158 if (sensorNames->find(object.first) != sensorNames->end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700159 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700160 for (const std::pair<std::string, std::vector<std::string>>&
161 objData : object.second)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700162 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700163 BMCWEB_LOG_DEBUG << "Adding connection: " << objData.first;
164 connections.insert(objData.first);
165 objectsWithConnection.insert(
166 std::make_pair(object.first, objData.first));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700167 }
168 }
169 }
170 BMCWEB_LOG_DEBUG << "Found " << connections.size() << " connections";
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530171 callback(std::move(connections), std::move(objectsWithConnection));
172 BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler exit";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700173 };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700174 // Make call to ObjectMapper to find all sensors objects
175 crow::connections::systemBus->async_method_call(
176 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
177 "/xyz/openbmc_project/object_mapper",
178 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 2, interfaces);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530179 BMCWEB_LOG_DEBUG << "getObjectsWithConnection exit";
180}
181
182/**
183 * @brief Create connections necessary for sensors
184 * @param SensorsAsyncResp Pointer to object holding response data
185 * @param sensorNames Sensors retrieved from chassis
186 * @param callback Callback for processing gathered connections
187 */
188template <typename Callback>
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700189void getConnections(
190 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
191 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
192 Callback&& callback)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530193{
194 auto objectsWithConnectionCb =
195 [callback](const boost::container::flat_set<std::string>& connections,
196 const std::set<std::pair<std::string, std::string>>&
197 objectsWithConnection) {
198 callback(std::move(connections));
199 };
200 getObjectsWithConnection(SensorsAsyncResp, sensorNames,
201 std::move(objectsWithConnectionCb));
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100202}
203
204/**
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700205 * @brief Shrinks the list of sensors for processing
206 * @param SensorsAysncResp The class holding the Redfish response
207 * @param allSensors A list of all the sensors associated to the
208 * chassis element (i.e. baseboard, front panel, etc...)
209 * @param activeSensors A list that is a reduction of the incoming
210 * allSensors list. Eliminate Thermal sensors when a Power request is
211 * made, and eliminate Power sensors when a Thermal request is made.
212 */
213void reduceSensorList(
214 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
215 const std::vector<std::string>* allSensors,
216 std::shared_ptr<boost::container::flat_set<std::string>> activeSensors)
217{
218 if (SensorsAsyncResp == nullptr)
219 {
220 return;
221 }
222 if ((allSensors == nullptr) || (activeSensors == nullptr))
223 {
224 messages::resourceNotFound(
225 SensorsAsyncResp->res, SensorsAsyncResp->chassisSubNode,
226 SensorsAsyncResp->chassisSubNode == "Thermal" ? "Temperatures"
227 : "Voltages");
228
229 return;
230 }
231 if (allSensors->empty())
232 {
233 // Nothing to do, the activeSensors object is also empty
234 return;
235 }
236
237 for (const char* type : SensorsAsyncResp->types)
238 {
239 for (const std::string& sensor : *allSensors)
240 {
241 if (boost::starts_with(sensor, type))
242 {
243 activeSensors->emplace(sensor);
244 }
245 }
246 }
247}
248
249/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100250 * @brief Retrieves requested chassis sensors and redundancy data from DBus .
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200251 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100252 * @param callback Callback for next step in gathered sensor processing
253 */
254template <typename Callback>
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700255void getChassis(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700256 Callback&& callback)
257{
258 BMCWEB_LOG_DEBUG << "getChassis enter";
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500259 const std::array<const char*, 2> interfaces = {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700260 "xyz.openbmc_project.Inventory.Item.Board",
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500261 "xyz.openbmc_project.Inventory.Item.Chassis"};
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700262 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp](
263 const boost::system::error_code ec,
264 const std::vector<std::string>& chassisPaths) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700265 BMCWEB_LOG_DEBUG << "getChassis respHandler enter";
266 if (ec)
267 {
268 BMCWEB_LOG_ERROR << "getChassis respHandler DBUS error: " << ec;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700269 messages::internalError(sensorsAsyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700270 return;
271 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100272
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700273 const std::string* chassisPath = nullptr;
274 std::string chassisName;
275 for (const std::string& chassis : chassisPaths)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700276 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700277 std::size_t lastPos = chassis.rfind("/");
278 if (lastPos == std::string::npos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700279 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700280 BMCWEB_LOG_ERROR << "Failed to find '/' in " << chassis;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700281 continue;
282 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700283 chassisName = chassis.substr(lastPos + 1);
284 if (chassisName == sensorsAsyncResp->chassisId)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700285 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700286 chassisPath = &chassis;
287 break;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700288 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700289 }
290 if (chassisPath == nullptr)
291 {
292 messages::resourceNotFound(sensorsAsyncResp->res, "Chassis",
293 sensorsAsyncResp->chassisId);
294 return;
295 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700296
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700297 const std::string& chassisSubNode = sensorsAsyncResp->chassisSubNode;
298 if (chassisSubNode == "Power")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700299 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700300 sensorsAsyncResp->res.jsonValue["@odata.type"] =
301 "#Power.v1_5_2.Power";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700302 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700303 else if (chassisSubNode == "Thermal")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700304 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700305 sensorsAsyncResp->res.jsonValue["@odata.type"] =
306 "#Thermal.v1_4_0.Thermal";
Jennifer Lee4f9a2132019-03-04 12:45:19 -0800307 sensorsAsyncResp->res.jsonValue["Fans"] = nlohmann::json::array();
308 sensorsAsyncResp->res.jsonValue["Temperatures"] =
309 nlohmann::json::array();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700310 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700311 sensorsAsyncResp->res.jsonValue["@odata.id"] =
312 "/redfish/v1/Chassis/" + sensorsAsyncResp->chassisId + "/" +
313 chassisSubNode;
314
315 sensorsAsyncResp->res.jsonValue["@odata.context"] =
316 "/redfish/v1/$metadata#" + chassisSubNode + "." + chassisSubNode;
317 sensorsAsyncResp->res.jsonValue["Id"] = chassisSubNode;
318 sensorsAsyncResp->res.jsonValue["Name"] = chassisSubNode;
319
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500320 // Get the list of all sensors for this Chassis element
321 std::string sensorPath = *chassisPath + "/all_sensors";
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700322 crow::connections::systemBus->async_method_call(
323 [sensorsAsyncResp, callback{std::move(callback)}](
324 const boost::system::error_code ec,
325 const std::variant<std::vector<std::string>>&
326 variantEndpoints) {
327 if (ec)
328 {
329 if (ec.value() != EBADR)
330 {
331 messages::internalError(sensorsAsyncResp->res);
332 return;
333 }
334 }
335 const std::vector<std::string>* nodeSensorList =
336 std::get_if<std::vector<std::string>>(&(variantEndpoints));
337 if (nodeSensorList == nullptr)
338 {
339 messages::resourceNotFound(
340 sensorsAsyncResp->res, sensorsAsyncResp->chassisSubNode,
341 sensorsAsyncResp->chassisSubNode == "Thermal"
342 ? "Temperatures"
343 : "Voltages");
344 return;
345 }
346 const std::shared_ptr<boost::container::flat_set<std::string>>
347 culledSensorList = std::make_shared<
348 boost::container::flat_set<std::string>>();
349 reduceSensorList(sensorsAsyncResp, nodeSensorList,
350 culledSensorList);
351 callback(culledSensorList);
352 },
353 "xyz.openbmc_project.ObjectMapper", sensorPath,
354 "org.freedesktop.DBus.Properties", "Get",
355 "xyz.openbmc_project.Association", "endpoints");
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100356 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100357
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700358 // Get the Chassis Collection
Ed Tanous1abe55e2018-09-05 08:30:59 -0700359 crow::connections::systemBus->async_method_call(
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700360 respHandler, "xyz.openbmc_project.ObjectMapper",
361 "/xyz/openbmc_project/object_mapper",
362 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
363 "/xyz/openbmc_project/inventory", int32_t(0), interfaces);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700364 BMCWEB_LOG_DEBUG << "getChassis exit";
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100365}
366
367/**
Shawn McCarneyde629b62019-03-08 10:42:51 -0600368 * @brief Finds all DBus object paths that implement ObjectManager.
369 *
370 * Creates a mapping from the associated connection name to the object path.
371 *
372 * Finds the object paths asynchronously. Invokes callback when information has
373 * been obtained.
374 *
375 * The callback must have the following signature:
376 * @code
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500377 * callback(std::shared_ptr<boost::container::flat_map<std::string,
378 * std::string>> objectMgrPaths)
Shawn McCarneyde629b62019-03-08 10:42:51 -0600379 * @endcode
380 *
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700381 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyde629b62019-03-08 10:42:51 -0600382 * @param callback Callback to invoke when object paths obtained.
383 */
384template <typename Callback>
385void getObjectManagerPaths(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
386 Callback&& callback)
387{
388 BMCWEB_LOG_DEBUG << "getObjectManagerPaths enter";
389 const std::array<std::string, 1> interfaces = {
390 "org.freedesktop.DBus.ObjectManager"};
391
392 // Response handler for GetSubTree DBus method
393 auto respHandler = [callback{std::move(callback)},
394 SensorsAsyncResp](const boost::system::error_code ec,
395 const GetSubTreeType& subtree) {
396 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler enter";
397 if (ec)
398 {
399 messages::internalError(SensorsAsyncResp->res);
400 BMCWEB_LOG_ERROR << "getObjectManagerPaths respHandler: DBus error "
401 << ec;
402 return;
403 }
404
405 // Loop over returned object paths
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500406 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
407 objectMgrPaths = std::make_shared<
408 boost::container::flat_map<std::string, std::string>>();
Shawn McCarneyde629b62019-03-08 10:42:51 -0600409 for (const std::pair<
410 std::string,
411 std::vector<std::pair<std::string, std::vector<std::string>>>>&
412 object : subtree)
413 {
414 // Loop over connections for current object path
415 const std::string& objectPath = object.first;
416 for (const std::pair<std::string, std::vector<std::string>>&
417 objData : object.second)
418 {
419 // Add mapping from connection to object path
420 const std::string& connection = objData.first;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500421 (*objectMgrPaths)[connection] = objectPath;
Shawn McCarneyde629b62019-03-08 10:42:51 -0600422 BMCWEB_LOG_DEBUG << "Added mapping " << connection << " -> "
423 << objectPath;
424 }
425 }
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500426 callback(objectMgrPaths);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600427 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler exit";
428 };
429
430 // Query mapper for all DBus object paths that implement ObjectManager
431 crow::connections::systemBus->async_method_call(
432 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
433 "/xyz/openbmc_project/object_mapper",
434 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", int32_t(0),
435 interfaces);
436 BMCWEB_LOG_DEBUG << "getObjectManagerPaths exit";
437}
438
439/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500440 * @brief Returns the Redfish State value for the specified inventory item.
441 * @param inventoryItem D-Bus inventory item associated with a sensor.
442 * @return State value for inventory item.
James Feist34dd1792019-05-17 14:10:54 -0700443 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500444static std::string getState(const InventoryItem* inventoryItem)
445{
446 if ((inventoryItem != nullptr) && !(inventoryItem->isPresent))
447 {
448 return "Absent";
449 }
James Feist34dd1792019-05-17 14:10:54 -0700450
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500451 return "Enabled";
452}
453
454/**
455 * @brief Returns the Redfish Health value for the specified sensor.
456 * @param sensorJson Sensor JSON object.
457 * @param interfacesDict Map of all sensor interfaces.
458 * @param inventoryItem D-Bus inventory item associated with the sensor. Will
459 * be nullptr if no associated inventory item was found.
460 * @return Health value for sensor.
461 */
James Feist34dd1792019-05-17 14:10:54 -0700462static std::string getHealth(
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500463 nlohmann::json& sensorJson,
James Feist34dd1792019-05-17 14:10:54 -0700464 const boost::container::flat_map<
465 std::string, boost::container::flat_map<std::string, SensorVariant>>&
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500466 interfacesDict,
467 const InventoryItem* inventoryItem)
James Feist34dd1792019-05-17 14:10:54 -0700468{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500469 // Get current health value (if any) in the sensor JSON object. Some JSON
470 // objects contain multiple sensors (such as PowerSupplies). We want to set
471 // the overall health to be the most severe of any of the sensors.
472 std::string currentHealth;
473 auto statusIt = sensorJson.find("Status");
474 if (statusIt != sensorJson.end())
475 {
476 auto healthIt = statusIt->find("Health");
477 if (healthIt != statusIt->end())
478 {
479 std::string* health = healthIt->get_ptr<std::string*>();
480 if (health != nullptr)
481 {
482 currentHealth = *health;
483 }
484 }
485 }
486
487 // If current health in JSON object is already Critical, return that. This
488 // should override the sensor health, which might be less severe.
489 if (currentHealth == "Critical")
490 {
491 return "Critical";
492 }
493
494 // Check if sensor has critical threshold alarm
James Feist34dd1792019-05-17 14:10:54 -0700495 auto criticalThresholdIt =
496 interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Critical");
497 if (criticalThresholdIt != interfacesDict.end())
498 {
499 auto thresholdHighIt =
500 criticalThresholdIt->second.find("CriticalAlarmHigh");
501 auto thresholdLowIt =
502 criticalThresholdIt->second.find("CriticalAlarmLow");
503 if (thresholdHighIt != criticalThresholdIt->second.end())
504 {
505 const bool* asserted = std::get_if<bool>(&thresholdHighIt->second);
506 if (asserted == nullptr)
507 {
508 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
509 }
510 else if (*asserted)
511 {
512 return "Critical";
513 }
514 }
515 if (thresholdLowIt != criticalThresholdIt->second.end())
516 {
517 const bool* asserted = std::get_if<bool>(&thresholdLowIt->second);
518 if (asserted == nullptr)
519 {
520 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
521 }
522 else if (*asserted)
523 {
524 return "Critical";
525 }
526 }
527 }
528
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500529 // Check if associated inventory item is not functional
530 if ((inventoryItem != nullptr) && !(inventoryItem->isFunctional))
531 {
532 return "Critical";
533 }
534
535 // If current health in JSON object is already Warning, return that. This
536 // should override the sensor status, which might be less severe.
537 if (currentHealth == "Warning")
538 {
539 return "Warning";
540 }
541
542 // Check if sensor has warning threshold alarm
James Feist34dd1792019-05-17 14:10:54 -0700543 auto warningThresholdIt =
544 interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Warning");
545 if (warningThresholdIt != interfacesDict.end())
546 {
547 auto thresholdHighIt =
548 warningThresholdIt->second.find("WarningAlarmHigh");
549 auto thresholdLowIt =
550 warningThresholdIt->second.find("WarningAlarmLow");
551 if (thresholdHighIt != warningThresholdIt->second.end())
552 {
553 const bool* asserted = std::get_if<bool>(&thresholdHighIt->second);
554 if (asserted == nullptr)
555 {
556 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
557 }
558 else if (*asserted)
559 {
560 return "Warning";
561 }
562 }
563 if (thresholdLowIt != warningThresholdIt->second.end())
564 {
565 const bool* asserted = std::get_if<bool>(&thresholdLowIt->second);
566 if (asserted == nullptr)
567 {
568 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
569 }
570 else if (*asserted)
571 {
572 return "Warning";
573 }
574 }
575 }
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500576
James Feist34dd1792019-05-17 14:10:54 -0700577 return "OK";
578}
579
580/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100581 * @brief Builds a json sensor representation of a sensor.
582 * @param sensorName The name of the sensor to be built
Gunnar Mills274fad52018-06-13 15:45:36 -0500583 * @param sensorType The type (temperature, fan_tach, etc) of the sensor to
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100584 * build
585 * @param interfacesDict A dictionary of the interfaces and properties of said
586 * interfaces to be built from
587 * @param sensor_json The json object to fill
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500588 * @param inventoryItem D-Bus inventory item associated with the sensor. Will
589 * be nullptr if no associated inventory item was found.
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100590 */
591void objectInterfacesToJson(
592 const std::string& sensorName, const std::string& sensorType,
593 const boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700594 std::string, boost::container::flat_map<std::string, SensorVariant>>&
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100595 interfacesDict,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500596 nlohmann::json& sensor_json, InventoryItem* inventoryItem)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700597{
598 // We need a value interface before we can do anything with it
599 auto valueIt = interfacesDict.find("xyz.openbmc_project.Sensor.Value");
600 if (valueIt == interfacesDict.end())
601 {
602 BMCWEB_LOG_ERROR << "Sensor doesn't have a value interface";
603 return;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100604 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100605
Ed Tanous1abe55e2018-09-05 08:30:59 -0700606 // Assume values exist as is (10^0 == 1) if no scale exists
607 int64_t scaleMultiplier = 0;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100608
Ed Tanous1abe55e2018-09-05 08:30:59 -0700609 auto scaleIt = valueIt->second.find("Scale");
610 // If a scale exists, pull value as int64, and use the scaling.
611 if (scaleIt != valueIt->second.end())
612 {
Ed Tanousabf2add2019-01-22 16:40:12 -0800613 const int64_t* int64Value = std::get_if<int64_t>(&scaleIt->second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700614 if (int64Value != nullptr)
615 {
616 scaleMultiplier = *int64Value;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100617 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100618 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700619
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500620 // Set MemberId and Name for non-power sensors. For PowerSupplies and
621 // PowerControl, those properties have more general values because multiple
622 // sensors can be stored in the same JSON object.
623 if (sensorType != "power")
624 {
625 sensor_json["MemberId"] = sensorName;
626 sensor_json["Name"] = boost::replace_all_copy(sensorName, "_", " ");
627 }
Ed Tanouse742b6c2019-05-03 15:06:53 -0700628
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500629 sensor_json["Status"]["State"] = getState(inventoryItem);
630 sensor_json["Status"]["Health"] =
631 getHealth(sensor_json, interfacesDict, inventoryItem);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700632
633 // Parameter to set to override the type we get from dbus, and force it to
634 // int, regardless of what is available. This is used for schemas like fan,
635 // that require integers, not floats.
636 bool forceToInt = false;
637
638 const char* unit = "Reading";
639 if (sensorType == "temperature")
640 {
641 unit = "ReadingCelsius";
642 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Temperature";
643 // TODO(ed) Documentation says that path should be type fan_tach,
644 // implementation seems to implement fan
645 }
646 else if (sensorType == "fan" || sensorType == "fan_tach")
647 {
648 unit = "Reading";
649 sensor_json["ReadingUnits"] = "RPM";
650 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
651 forceToInt = true;
652 }
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700653 else if (sensorType == "fan_pwm")
654 {
655 unit = "Reading";
656 sensor_json["ReadingUnits"] = "Percent";
657 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
658 forceToInt = true;
659 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700660 else if (sensorType == "voltage")
661 {
662 unit = "ReadingVolts";
663 sensor_json["@odata.type"] = "#Power.v1_0_0.Voltage";
664 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700665 else if (sensorType == "power")
666 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700667 std::string sensorNameLower =
668 boost::algorithm::to_lower_copy(sensorName);
669
Eddie James028f7eb2019-05-17 21:24:36 +0000670 if (!sensorName.compare("total_power"))
671 {
Gunnar Mills7ab06f42019-07-02 13:07:16 -0500672 sensor_json["@odata.type"] = "#Power.v1_0_0.PowerControl";
673 // Put multiple "sensors" into a single PowerControl, so have
674 // generic names for MemberId and Name. Follows Redfish mockup.
675 sensor_json["MemberId"] = "0";
676 sensor_json["Name"] = "Chassis Power Control";
Eddie James028f7eb2019-05-17 21:24:36 +0000677 unit = "PowerConsumedWatts";
678 }
679 else if (sensorNameLower.find("input") != std::string::npos)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700680 {
681 unit = "PowerInputWatts";
682 }
683 else
684 {
685 unit = "PowerOutputWatts";
686 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700687 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700688 else
689 {
690 BMCWEB_LOG_ERROR << "Redfish cannot map object type for " << sensorName;
691 return;
692 }
693 // Map of dbus interface name, dbus property name and redfish property_name
694 std::vector<std::tuple<const char*, const char*, const char*>> properties;
695 properties.reserve(7);
696
697 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600698
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500699 if (sensorType != "power")
Shawn McCarneyde629b62019-03-08 10:42:51 -0600700 {
701 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
702 "WarningHigh", "UpperThresholdNonCritical");
703 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
704 "WarningLow", "LowerThresholdNonCritical");
705 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
706 "CriticalHigh", "UpperThresholdCritical");
707 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
708 "CriticalLow", "LowerThresholdCritical");
709 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700710
Ed Tanous2474adf2018-09-05 16:31:16 -0700711 // TODO Need to get UpperThresholdFatal and LowerThresholdFatal
712
Ed Tanous1abe55e2018-09-05 08:30:59 -0700713 if (sensorType == "temperature")
714 {
715 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
716 "MinReadingRangeTemp");
717 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
718 "MaxReadingRangeTemp");
719 }
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500720 else if (sensorType != "power")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700721 {
722 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
723 "MinReadingRange");
724 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
725 "MaxReadingRange");
726 }
727
728 for (const std::tuple<const char*, const char*, const char*>& p :
729 properties)
730 {
731 auto interfaceProperties = interfacesDict.find(std::get<0>(p));
732 if (interfaceProperties != interfacesDict.end())
733 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000734 auto valueIt = interfaceProperties->second.find(std::get<1>(p));
735 if (valueIt != interfaceProperties->second.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700736 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000737 const SensorVariant& valueVariant = valueIt->second;
738 nlohmann::json& valueIt = sensor_json[std::get<2>(p)];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700739 // Attempt to pull the int64 directly
Ed Tanousabf2add2019-01-22 16:40:12 -0800740 const int64_t* int64Value = std::get_if<int64_t>(&valueVariant);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700741
Ed Tanousabf2add2019-01-22 16:40:12 -0800742 const double* doubleValue = std::get_if<double>(&valueVariant);
Eddie James028f7eb2019-05-17 21:24:36 +0000743 const uint32_t* uValue = std::get_if<uint32_t>(&valueVariant);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700744 double temp = 0.0;
745 if (int64Value != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700746 {
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700747 temp = *int64Value;
748 }
749 else if (doubleValue != nullptr)
750 {
751 temp = *doubleValue;
752 }
Eddie James028f7eb2019-05-17 21:24:36 +0000753 else if (uValue != nullptr)
754 {
755 temp = *uValue;
756 }
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700757 else
758 {
759 BMCWEB_LOG_ERROR
760 << "Got value interface that wasn't int or double";
761 continue;
762 }
763 temp = temp * std::pow(10, scaleMultiplier);
764 if (forceToInt)
765 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000766 valueIt = static_cast<int64_t>(temp);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700767 }
768 else
769 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000770 valueIt = temp;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700771 }
772 }
773 }
774 }
775 BMCWEB_LOG_DEBUG << "Added sensor " << sensorName;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100776}
777
James Feist8bd25cc2019-03-15 15:14:00 -0700778static void
779 populateFanRedundancy(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp)
780{
781 crow::connections::systemBus->async_method_call(
782 [sensorsAsyncResp](const boost::system::error_code ec,
783 const GetSubTreeType& resp) {
784 if (ec)
785 {
786 return; // don't have to have this interface
787 }
Ed Tanouse278c182019-03-13 16:23:37 -0700788 for (const std::pair<std::string,
789 std::vector<std::pair<
790 std::string, std::vector<std::string>>>>&
791 pathPair : resp)
James Feist8bd25cc2019-03-15 15:14:00 -0700792 {
Ed Tanouse278c182019-03-13 16:23:37 -0700793 const std::string& path = pathPair.first;
794 const std::vector<
795 std::pair<std::string, std::vector<std::string>>>& objDict =
796 pathPair.second;
James Feist8bd25cc2019-03-15 15:14:00 -0700797 if (objDict.empty())
798 {
799 continue; // this should be impossible
800 }
801
802 const std::string& owner = objDict.begin()->first;
803 crow::connections::systemBus->async_method_call(
804 [path, owner,
805 sensorsAsyncResp](const boost::system::error_code ec,
806 std::variant<std::vector<std::string>>
807 variantEndpoints) {
808 if (ec)
809 {
810 return; // if they don't have an association we
811 // can't tell what chassis is
812 }
813 // verify part of the right chassis
814 auto endpoints = std::get_if<std::vector<std::string>>(
815 &variantEndpoints);
816
817 if (endpoints == nullptr)
818 {
819 BMCWEB_LOG_ERROR << "Invalid association interface";
820 messages::internalError(sensorsAsyncResp->res);
821 return;
822 }
823
824 auto found = std::find_if(
825 endpoints->begin(), endpoints->end(),
826 [sensorsAsyncResp](const std::string& entry) {
827 return entry.find(
828 sensorsAsyncResp->chassisId) !=
829 std::string::npos;
830 });
831
832 if (found == endpoints->end())
833 {
834 return;
835 }
836 crow::connections::systemBus->async_method_call(
837 [path, sensorsAsyncResp](
838 const boost::system::error_code ec,
839 const boost::container::flat_map<
840 std::string,
841 std::variant<uint8_t,
842 std::vector<std::string>,
843 std::string>>& ret) {
844 if (ec)
845 {
846 return; // don't have to have this
847 // interface
848 }
849 auto findFailures = ret.find("AllowedFailures");
850 auto findCollection = ret.find("Collection");
851 auto findStatus = ret.find("Status");
852
853 if (findFailures == ret.end() ||
854 findCollection == ret.end() ||
855 findStatus == ret.end())
856 {
857 BMCWEB_LOG_ERROR
858 << "Invalid redundancy interface";
859 messages::internalError(
860 sensorsAsyncResp->res);
861 return;
862 }
863
864 auto allowedFailures = std::get_if<uint8_t>(
865 &(findFailures->second));
866 auto collection =
867 std::get_if<std::vector<std::string>>(
868 &(findCollection->second));
869 auto status = std::get_if<std::string>(
870 &(findStatus->second));
871
872 if (allowedFailures == nullptr ||
873 collection == nullptr || status == nullptr)
874 {
875
876 BMCWEB_LOG_ERROR
877 << "Invalid redundancy interface "
878 "types";
879 messages::internalError(
880 sensorsAsyncResp->res);
881 return;
882 }
883 size_t lastSlash = path.rfind("/");
884 if (lastSlash == std::string::npos)
885 {
886 // this should be impossible
887 messages::internalError(
888 sensorsAsyncResp->res);
889 return;
890 }
891 std::string name = path.substr(lastSlash + 1);
892 std::replace(name.begin(), name.end(), '_',
893 ' ');
894
895 std::string health;
896
897 if (boost::ends_with(*status, "Full"))
898 {
899 health = "OK";
900 }
901 else if (boost::ends_with(*status, "Degraded"))
902 {
903 health = "Warning";
904 }
905 else
906 {
907 health = "Critical";
908 }
909 std::vector<nlohmann::json> redfishCollection;
910 const auto& fanRedfish =
911 sensorsAsyncResp->res.jsonValue["Fans"];
912 for (const std::string& item : *collection)
913 {
914 lastSlash = item.rfind("/");
915 // make a copy as collection is const
916 std::string itemName =
917 item.substr(lastSlash + 1);
918 /*
919 todo(ed): merge patch that fixes the names
920 std::replace(itemName.begin(),
921 itemName.end(), '_', ' ');*/
922 auto schemaItem = std::find_if(
923 fanRedfish.begin(), fanRedfish.end(),
924 [itemName](const nlohmann::json& fan) {
925 return fan["MemberId"] == itemName;
926 });
927 if (schemaItem != fanRedfish.end())
928 {
929 redfishCollection.push_back(
930 {{"@odata.id",
931 (*schemaItem)["@odata.id"]}});
932 }
933 else
934 {
935 BMCWEB_LOG_ERROR
936 << "failed to find fan in schema";
937 messages::internalError(
938 sensorsAsyncResp->res);
939 return;
940 }
941 }
942
943 auto& resp = sensorsAsyncResp->res
944 .jsonValue["Redundancy"];
945 resp.push_back(
946 {{"@odata.id",
947 "/refish/v1/Chassis/" +
948 sensorsAsyncResp->chassisId + "/" +
949 sensorsAsyncResp->chassisSubNode +
950 "#/Redundancy/" +
951 std::to_string(resp.size())},
952 {"@odata.type",
953 "#Redundancy.v1_3_2.Redundancy"},
954 {"MinNumNeeded",
955 collection->size() - *allowedFailures},
956 {"MemberId", name},
957 {"Mode", "N+m"},
958 {"Name", name},
959 {"RedundancySet", redfishCollection},
960 {"Status",
961 {{"Health", health},
962 {"State", "Enabled"}}}});
963 },
964 owner, path, "org.freedesktop.DBus.Properties",
965 "GetAll",
966 "xyz.openbmc_project.Control.FanRedundancy");
967 },
James Feist02e92e32019-06-26 12:07:05 -0700968 "xyz.openbmc_project.ObjectMapper", path + "/chassis",
James Feist8bd25cc2019-03-15 15:14:00 -0700969 "org.freedesktop.DBus.Properties", "Get",
970 "xyz.openbmc_project.Association", "endpoints");
971 }
972 },
973 "xyz.openbmc_project.ObjectMapper",
974 "/xyz/openbmc_project/object_mapper",
975 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
976 "/xyz/openbmc_project/control", 2,
977 std::array<const char*, 1>{
978 "xyz.openbmc_project.Control.FanRedundancy"});
979}
980
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700981void sortJSONResponse(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
982{
983 nlohmann::json& response = SensorsAsyncResp->res.jsonValue;
984 std::array<std::string, 2> sensorHeaders{"Temperatures", "Fans"};
985 if (SensorsAsyncResp->chassisSubNode == "Power")
986 {
987 sensorHeaders = {"Voltages", "PowerSupplies"};
988 }
989 for (const std::string& sensorGroup : sensorHeaders)
990 {
991 nlohmann::json::iterator entry = response.find(sensorGroup);
992 if (entry != response.end())
993 {
994 std::sort(entry->begin(), entry->end(),
995 [](nlohmann::json& c1, nlohmann::json& c2) {
996 return c1["Name"] < c2["Name"];
997 });
998
999 // add the index counts to the end of each entry
1000 size_t count = 0;
1001 for (nlohmann::json& sensorJson : *entry)
1002 {
1003 nlohmann::json::iterator odata = sensorJson.find("@odata.id");
1004 if (odata == sensorJson.end())
1005 {
1006 continue;
1007 }
1008 std::string* value = odata->get_ptr<std::string*>();
1009 if (value != nullptr)
1010 {
1011 *value += std::to_string(count);
1012 count++;
1013 }
1014 }
1015 }
1016 }
1017}
1018
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001019/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001020 * @brief Finds the inventory item with the specified object path.
1021 * @param inventoryItems D-Bus inventory items associated with sensors.
1022 * @param invItemObjPath D-Bus object path of inventory item.
1023 * @return Inventory item within vector, or nullptr if no match found.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001024 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001025static InventoryItem* findInventoryItem(
1026 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1027 const std::string& invItemObjPath)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001028{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001029 for (InventoryItem& inventoryItem : *inventoryItems)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001030 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001031 if (inventoryItem.objectPath == invItemObjPath)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001032 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001033 return &inventoryItem;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001034 }
1035 }
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001036 return nullptr;
1037}
1038
1039/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001040 * @brief Finds the inventory item associated with the specified sensor.
1041 * @param inventoryItems D-Bus inventory items associated with sensors.
1042 * @param sensorObjPath D-Bus object path of sensor.
1043 * @return Inventory item within vector, or nullptr if no match found.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001044 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001045static InventoryItem* findInventoryItemForSensor(
1046 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1047 const std::string& sensorObjPath)
1048{
1049 for (InventoryItem& inventoryItem : *inventoryItems)
1050 {
1051 if (inventoryItem.sensors.count(sensorObjPath) > 0)
1052 {
1053 return &inventoryItem;
1054 }
1055 }
1056 return nullptr;
1057}
1058
1059/**
1060 * @brief Adds inventory item and associated sensor to specified vector.
1061 *
1062 * Adds a new InventoryItem to the vector if necessary. Searches for an
1063 * existing InventoryItem with the specified object path. If not found, one is
1064 * added to the vector.
1065 *
1066 * Next, the specified sensor is added to the set of sensors associated with the
1067 * InventoryItem.
1068 *
1069 * @param inventoryItems D-Bus inventory items associated with sensors.
1070 * @param invItemObjPath D-Bus object path of inventory item.
1071 * @param sensorObjPath D-Bus object path of sensor
1072 */
1073static void
1074 addInventoryItem(std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1075 const std::string& invItemObjPath,
1076 const std::string& sensorObjPath)
1077{
1078 // Look for inventory item in vector
1079 InventoryItem* inventoryItem =
1080 findInventoryItem(inventoryItems, invItemObjPath);
1081
1082 // If inventory item doesn't exist in vector, add it
1083 if (inventoryItem == nullptr)
1084 {
1085 inventoryItems->emplace_back(invItemObjPath);
1086 inventoryItem = &(inventoryItems->back());
1087 }
1088
1089 // Add sensor to set of sensors associated with inventory item
1090 inventoryItem->sensors.emplace(sensorObjPath);
1091}
1092
1093/**
1094 * @brief Stores D-Bus data in the specified inventory item.
1095 *
1096 * Finds D-Bus data in the specified map of interfaces. Stores the data in the
1097 * specified InventoryItem.
1098 *
1099 * This data is later used to provide sensor property values in the JSON
1100 * response.
1101 *
1102 * @param inventoryItem Inventory item where data will be stored.
1103 * @param interfacesDict Map containing D-Bus interfaces and their properties
1104 * for the specified inventory item.
1105 */
1106static void storeInventoryItemData(
1107 InventoryItem& inventoryItem,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001108 const boost::container::flat_map<
1109 std::string, boost::container::flat_map<std::string, SensorVariant>>&
1110 interfacesDict)
1111{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001112 // Get properties from Inventory.Item interface
1113 auto interfaceIt =
1114 interfacesDict.find("xyz.openbmc_project.Inventory.Item");
1115 if (interfaceIt != interfacesDict.end())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001116 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001117 auto propertyIt = interfaceIt->second.find("Present");
1118 if (propertyIt != interfaceIt->second.end())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001119 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001120 const bool* value = std::get_if<bool>(&propertyIt->second);
1121 if (value != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001122 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001123 inventoryItem.isPresent = *value;
1124 }
1125 }
1126 }
1127
1128 // Check if Inventory.Item.PowerSupply interface is present
1129 interfaceIt =
1130 interfacesDict.find("xyz.openbmc_project.Inventory.Item.PowerSupply");
1131 if (interfaceIt != interfacesDict.end())
1132 {
1133 inventoryItem.isPowerSupply = true;
1134 }
1135
1136 // Get properties from Inventory.Decorator.Asset interface
1137 interfaceIt =
1138 interfacesDict.find("xyz.openbmc_project.Inventory.Decorator.Asset");
1139 if (interfaceIt != interfacesDict.end())
1140 {
1141 auto propertyIt = interfaceIt->second.find("Manufacturer");
1142 if (propertyIt != interfaceIt->second.end())
1143 {
1144 const std::string* value =
1145 std::get_if<std::string>(&propertyIt->second);
1146 if (value != nullptr)
1147 {
1148 inventoryItem.manufacturer = *value;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001149 }
1150 }
1151
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001152 propertyIt = interfaceIt->second.find("Model");
1153 if (propertyIt != interfaceIt->second.end())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001154 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001155 const std::string* value =
1156 std::get_if<std::string>(&propertyIt->second);
1157 if (value != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001158 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001159 inventoryItem.model = *value;
1160 }
1161 }
1162
1163 propertyIt = interfaceIt->second.find("PartNumber");
1164 if (propertyIt != interfaceIt->second.end())
1165 {
1166 const std::string* value =
1167 std::get_if<std::string>(&propertyIt->second);
1168 if (value != nullptr)
1169 {
1170 inventoryItem.partNumber = *value;
1171 }
1172 }
1173
1174 propertyIt = interfaceIt->second.find("SerialNumber");
1175 if (propertyIt != interfaceIt->second.end())
1176 {
1177 const std::string* value =
1178 std::get_if<std::string>(&propertyIt->second);
1179 if (value != nullptr)
1180 {
1181 inventoryItem.serialNumber = *value;
1182 }
1183 }
1184 }
1185
1186 // Get properties from State.Decorator.OperationalStatus interface
1187 interfaceIt = interfacesDict.find(
1188 "xyz.openbmc_project.State.Decorator.OperationalStatus");
1189 if (interfaceIt != interfacesDict.end())
1190 {
1191 auto propertyIt = interfaceIt->second.find("Functional");
1192 if (propertyIt != interfaceIt->second.end())
1193 {
1194 const bool* value = std::get_if<bool>(&propertyIt->second);
1195 if (value != nullptr)
1196 {
1197 inventoryItem.isFunctional = *value;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001198 }
1199 }
1200 }
1201}
1202
1203/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001204 * @brief Gets D-Bus data for inventory items associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001205 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001206 * Uses the specified connections (services) to obtain D-Bus data for inventory
1207 * items associated with sensors. Stores the resulting data in the
1208 * inventoryItems vector.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001209 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001210 * This data is later used to provide sensor property values in the JSON
1211 * response.
1212 *
1213 * Finds the inventory item data asynchronously. Invokes callback when data has
1214 * been obtained.
1215 *
1216 * The callback must have the following signature:
1217 * @code
1218 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
1219 * @endcode
1220 *
1221 * This function is called recursively, obtaining data asynchronously from one
1222 * connection in each call. This ensures the callback is not invoked until the
1223 * last asynchronous function has completed.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001224 *
1225 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001226 * @param inventoryItems D-Bus inventory items associated with sensors.
1227 * @param invConnections Connections that provide data for the inventory items.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001228 * @param objectMgrPaths Mappings from connection name to DBus object path that
1229 * implements ObjectManager.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001230 * @param callback Callback to invoke when inventory data has been obtained.
1231 * @param invConnectionsIndex Current index in invConnections. Only specified
1232 * in recursive calls to this function.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001233 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001234template <typename Callback>
1235static void getInventoryItemsData(
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001236 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001237 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001238 std::shared_ptr<boost::container::flat_set<std::string>> invConnections,
1239 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001240 objectMgrPaths,
1241 Callback&& callback, int invConnectionsIndex = 0)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001242{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001243 BMCWEB_LOG_DEBUG << "getInventoryItemsData enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001244
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001245 // If no more connections left, call callback
1246 if (invConnectionsIndex >= invConnections->size())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001247 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001248 callback(inventoryItems);
1249 BMCWEB_LOG_DEBUG << "getInventoryItemsData exit";
1250 return;
1251 }
1252
1253 // Get inventory item data from current connection
1254 auto it = invConnections->nth(invConnectionsIndex);
1255 if (it != invConnections->end())
1256 {
1257 const std::string& invConnection = *it;
1258
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001259 // Response handler for GetManagedObjects
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001260 auto respHandler = [sensorsAsyncResp, inventoryItems, invConnections,
1261 objectMgrPaths, callback{std::move(callback)},
1262 invConnectionsIndex](
1263 const boost::system::error_code ec,
1264 ManagedObjectsVectorType& resp) {
1265 BMCWEB_LOG_DEBUG << "getInventoryItemsData respHandler enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001266 if (ec)
1267 {
1268 BMCWEB_LOG_ERROR
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001269 << "getInventoryItemsData respHandler DBus error " << ec;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001270 messages::internalError(sensorsAsyncResp->res);
1271 return;
1272 }
1273
1274 // Loop through returned object paths
1275 for (const auto& objDictEntry : resp)
1276 {
1277 const std::string& objPath =
1278 static_cast<const std::string&>(objDictEntry.first);
1279
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001280 // If this object path is one of the specified inventory items
1281 InventoryItem* inventoryItem =
1282 findInventoryItem(inventoryItems, objPath);
1283 if (inventoryItem != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001284 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001285 // Store inventory data in InventoryItem
1286 storeInventoryItemData(*inventoryItem, objDictEntry.second);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001287 }
1288 }
1289
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001290 // Recurse to get inventory item data from next connection
1291 getInventoryItemsData(sensorsAsyncResp, inventoryItems,
1292 invConnections, objectMgrPaths,
1293 std::move(callback), invConnectionsIndex + 1);
1294
1295 BMCWEB_LOG_DEBUG << "getInventoryItemsData respHandler exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001296 };
1297
1298 // Find DBus object path that implements ObjectManager for the current
1299 // connection. If no mapping found, default to "/".
1300 auto iter = objectMgrPaths->find(invConnection);
1301 const std::string& objectMgrPath =
1302 (iter != objectMgrPaths->end()) ? iter->second : "/";
1303 BMCWEB_LOG_DEBUG << "ObjectManager path for " << invConnection << " is "
1304 << objectMgrPath;
1305
1306 // Get all object paths and their interfaces for current connection
1307 crow::connections::systemBus->async_method_call(
1308 std::move(respHandler), invConnection, objectMgrPath,
1309 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1310 }
1311
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001312 BMCWEB_LOG_DEBUG << "getInventoryItemsData exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001313}
1314
1315/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001316 * @brief Gets connections that provide D-Bus data for inventory items.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001317 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001318 * Gets the D-Bus connections (services) that provide data for the inventory
1319 * items that are associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001320 *
1321 * Finds the connections asynchronously. Invokes callback when information has
1322 * been obtained.
1323 *
1324 * The callback must have the following signature:
1325 * @code
1326 * callback(std::shared_ptr<boost::container::flat_set<std::string>>
1327 * invConnections)
1328 * @endcode
1329 *
1330 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001331 * @param inventoryItems D-Bus inventory items associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001332 * @param callback Callback to invoke when connections have been obtained.
1333 */
1334template <typename Callback>
1335static void getInventoryItemsConnections(
1336 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001337 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001338 Callback&& callback)
1339{
1340 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections enter";
1341
1342 const std::string path = "/xyz/openbmc_project/inventory";
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001343 const std::array<std::string, 4> interfaces = {
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001344 "xyz.openbmc_project.Inventory.Item",
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001345 "xyz.openbmc_project.Inventory.Item.PowerSupply",
1346 "xyz.openbmc_project.Inventory.Decorator.Asset",
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001347 "xyz.openbmc_project.State.Decorator.OperationalStatus"};
1348
1349 // Response handler for parsing output from GetSubTree
1350 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001351 inventoryItems](const boost::system::error_code ec,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001352 const GetSubTreeType& subtree) {
1353 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections respHandler enter";
1354 if (ec)
1355 {
1356 messages::internalError(sensorsAsyncResp->res);
1357 BMCWEB_LOG_ERROR
1358 << "getInventoryItemsConnections respHandler DBus error " << ec;
1359 return;
1360 }
1361
1362 // Make unique list of connections for desired inventory items
1363 std::shared_ptr<boost::container::flat_set<std::string>>
1364 invConnections =
1365 std::make_shared<boost::container::flat_set<std::string>>();
1366 invConnections->reserve(8);
1367
1368 // Loop through objects from GetSubTree
1369 for (const std::pair<
1370 std::string,
1371 std::vector<std::pair<std::string, std::vector<std::string>>>>&
1372 object : subtree)
1373 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001374 // Check if object path is one of the specified inventory items
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001375 const std::string& objPath = object.first;
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001376 if (findInventoryItem(inventoryItems, objPath) != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001377 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001378 // Store all connections to inventory item
1379 for (const std::pair<std::string, std::vector<std::string>>&
1380 objData : object.second)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001381 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001382 const std::string& invConnection = objData.first;
1383 invConnections->insert(invConnection);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001384 }
1385 }
1386 }
1387 callback(invConnections);
1388 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections respHandler exit";
1389 };
1390
1391 // Make call to ObjectMapper to find all inventory items
1392 crow::connections::systemBus->async_method_call(
1393 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
1394 "/xyz/openbmc_project/object_mapper",
1395 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 0, interfaces);
1396 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections exit";
1397}
1398
1399/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001400 * @brief Gets associations from sensors to inventory items.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001401 *
1402 * Looks for ObjectMapper associations from the specified sensors to related
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001403 * inventory items.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001404 *
1405 * Finds the inventory items asynchronously. Invokes callback when information
1406 * has been obtained.
1407 *
1408 * The callback must have the following signature:
1409 * @code
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001410 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001411 * @endcode
1412 *
1413 * @param sensorsAsyncResp Pointer to object holding response data.
1414 * @param sensorNames All sensors within the current chassis.
1415 * @param objectMgrPaths Mappings from connection name to DBus object path that
1416 * implements ObjectManager.
1417 * @param callback Callback to invoke when inventory items have been obtained.
1418 */
1419template <typename Callback>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001420static void getInventoryItemAssociations(
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001421 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1422 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
1423 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1424 objectMgrPaths,
1425 Callback&& callback)
1426{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001427 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001428
1429 // Response handler for GetManagedObjects
1430 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
1431 sensorNames](const boost::system::error_code ec,
1432 dbus::utility::ManagedObjectType& resp) {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001433 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations respHandler enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001434 if (ec)
1435 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001436 BMCWEB_LOG_ERROR
1437 << "getInventoryItemAssociations respHandler DBus error " << ec;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001438 messages::internalError(sensorsAsyncResp->res);
1439 return;
1440 }
1441
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001442 // Create vector to hold list of inventory items
1443 std::shared_ptr<std::vector<InventoryItem>> inventoryItems =
1444 std::make_shared<std::vector<InventoryItem>>();
1445
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001446 // Loop through returned object paths
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001447 std::string sensorAssocPath;
1448 sensorAssocPath.reserve(128); // avoid memory allocations
1449 for (const auto& objDictEntry : resp)
1450 {
1451 const std::string& objPath =
1452 static_cast<const std::string&>(objDictEntry.first);
1453 const boost::container::flat_map<
1454 std::string, boost::container::flat_map<
1455 std::string, dbus::utility::DbusVariantType>>&
1456 interfacesDict = objDictEntry.second;
1457
1458 // If path is inventory association for one of the specified sensors
1459 for (const std::string& sensorName : *sensorNames)
1460 {
1461 sensorAssocPath = sensorName;
1462 sensorAssocPath += "/inventory";
1463 if (objPath == sensorAssocPath)
1464 {
1465 // Get Association interface for object path
1466 auto assocIt =
1467 interfacesDict.find("xyz.openbmc_project.Association");
1468 if (assocIt != interfacesDict.end())
1469 {
1470 // Get inventory item from end point
1471 auto endpointsIt = assocIt->second.find("endpoints");
1472 if (endpointsIt != assocIt->second.end())
1473 {
1474 const std::vector<std::string>* endpoints =
1475 std::get_if<std::vector<std::string>>(
1476 &endpointsIt->second);
1477 if ((endpoints != nullptr) && !endpoints->empty())
1478 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001479 // Add inventory item to vector
1480 const std::string& invItemPath =
1481 endpoints->front();
1482 addInventoryItem(inventoryItems, invItemPath,
1483 sensorName);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001484 }
1485 }
1486 }
1487 break;
1488 }
1489 }
1490 }
1491
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001492 callback(inventoryItems);
1493 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations respHandler exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001494 };
1495
1496 // Find DBus object path that implements ObjectManager for ObjectMapper
1497 std::string connection = "xyz.openbmc_project.ObjectMapper";
1498 auto iter = objectMgrPaths->find(connection);
1499 const std::string& objectMgrPath =
1500 (iter != objectMgrPaths->end()) ? iter->second : "/";
1501 BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
1502 << objectMgrPath;
1503
1504 // Call GetManagedObjects on the ObjectMapper to get all associations
1505 crow::connections::systemBus->async_method_call(
1506 std::move(respHandler), connection, objectMgrPath,
1507 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1508
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001509 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001510}
1511
1512/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001513 * @brief Gets inventory items associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001514 *
1515 * Finds the inventory items that are associated with the specified sensors.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001516 * Then gets D-Bus data for the inventory items, such as presence and VPD.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001517 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001518 * This data is later used to provide sensor property values in the JSON
1519 * response.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001520 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001521 * Finds the inventory items asynchronously. Invokes callback when the
1522 * inventory items have been obtained.
1523 *
1524 * The callback must have the following signature:
1525 * @code
1526 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
1527 * @endcode
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001528 *
1529 * @param sensorsAsyncResp Pointer to object holding response data.
1530 * @param sensorNames All sensors within the current chassis.
1531 * @param objectMgrPaths Mappings from connection name to DBus object path that
1532 * implements ObjectManager.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001533 * @param callback Callback to invoke when inventory items have been obtained.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001534 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001535template <typename Callback>
1536static void getInventoryItems(
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001537 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1538 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
1539 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001540 objectMgrPaths,
1541 Callback&& callback)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001542{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001543 BMCWEB_LOG_DEBUG << "getInventoryItems enter";
1544 auto getInventoryItemAssociationsCb =
1545 [sensorsAsyncResp, objectMgrPaths, callback{std::move(callback)}](
1546 std::shared_ptr<std::vector<InventoryItem>> inventoryItems) {
1547 BMCWEB_LOG_DEBUG << "getInventoryItemAssociationsCb enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001548 auto getInventoryItemsConnectionsCb =
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001549 [sensorsAsyncResp, inventoryItems, objectMgrPaths,
1550 callback{std::move(callback)}](
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001551 std::shared_ptr<boost::container::flat_set<std::string>>
1552 invConnections) {
1553 BMCWEB_LOG_DEBUG << "getInventoryItemsConnectionsCb enter";
1554
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001555 // Get inventory item data from connections
1556 getInventoryItemsData(sensorsAsyncResp, inventoryItems,
1557 invConnections, objectMgrPaths,
1558 std::move(callback));
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001559
1560 BMCWEB_LOG_DEBUG << "getInventoryItemsConnectionsCb exit";
1561 };
1562
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001563 // Get connections that provide inventory item data
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001564 getInventoryItemsConnections(
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001565 sensorsAsyncResp, inventoryItems,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001566 std::move(getInventoryItemsConnectionsCb));
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001567 BMCWEB_LOG_DEBUG << "getInventoryItemAssociationsCb exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001568 };
1569
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001570 // Get associations from sensors to inventory items
1571 getInventoryItemAssociations(sensorsAsyncResp, sensorNames, objectMgrPaths,
1572 std::move(getInventoryItemAssociationsCb));
1573 BMCWEB_LOG_DEBUG << "getInventoryItems exit";
1574}
1575
1576/**
1577 * @brief Returns JSON PowerSupply object for the specified inventory item.
1578 *
1579 * Searches for a JSON PowerSupply object that matches the specified inventory
1580 * item. If one is not found, a new PowerSupply object is added to the JSON
1581 * array.
1582 *
1583 * Multiple sensors are often associated with one power supply inventory item.
1584 * As a result, multiple sensor values are stored in one JSON PowerSupply
1585 * object.
1586 *
1587 * @param powerSupplyArray JSON array containing Redfish PowerSupply objects.
1588 * @param inventoryItem Inventory item for the power supply.
1589 * @param chassisId Chassis that contains the power supply.
1590 * @return JSON PowerSupply object for the specified inventory item.
1591 */
1592static nlohmann::json& getPowerSupply(nlohmann::json& powerSupplyArray,
1593 const InventoryItem& inventoryItem,
1594 const std::string& chassisId)
1595{
1596 // Check if matching PowerSupply object already exists in JSON array
1597 for (nlohmann::json& powerSupply : powerSupplyArray)
1598 {
1599 if (powerSupply["MemberId"] == inventoryItem.name)
1600 {
1601 return powerSupply;
1602 }
1603 }
1604
1605 // Add new PowerSupply object to JSON array
1606 powerSupplyArray.push_back({});
1607 nlohmann::json& powerSupply = powerSupplyArray.back();
1608 powerSupply["@odata.id"] =
1609 "/redfish/v1/Chassis/" + chassisId + "/Power#/PowerSupplies/";
1610 powerSupply["MemberId"] = inventoryItem.name;
1611 powerSupply["Name"] = boost::replace_all_copy(inventoryItem.name, "_", " ");
1612 powerSupply["Manufacturer"] = inventoryItem.manufacturer;
1613 powerSupply["Model"] = inventoryItem.model;
1614 powerSupply["PartNumber"] = inventoryItem.partNumber;
1615 powerSupply["SerialNumber"] = inventoryItem.serialNumber;
1616 powerSupply["Status"]["State"] = getState(&inventoryItem);
1617
1618 const char* health = inventoryItem.isFunctional ? "OK" : "Critical";
1619 powerSupply["Status"]["Health"] = health;
1620
1621 return powerSupply;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001622}
1623
1624/**
Shawn McCarneyde629b62019-03-08 10:42:51 -06001625 * @brief Gets the values of the specified sensors.
1626 *
1627 * Stores the results as JSON in the SensorsAsyncResp.
1628 *
1629 * Gets the sensor values asynchronously. Stores the results later when the
1630 * information has been obtained.
1631 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001632 * The sensorNames set contains all requested sensors for the current chassis.
Shawn McCarneyde629b62019-03-08 10:42:51 -06001633 *
1634 * To minimize the number of DBus calls, the DBus method
1635 * org.freedesktop.DBus.ObjectManager.GetManagedObjects() is used to get the
1636 * values of all sensors provided by a connection (service).
1637 *
1638 * The connections set contains all the connections that provide sensor values.
1639 *
1640 * The objectMgrPaths map contains mappings from a connection name to the
1641 * corresponding DBus object path that implements ObjectManager.
1642 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001643 * The InventoryItem vector contains D-Bus inventory items associated with the
1644 * sensors. Inventory item data is needed for some Redfish sensor properties.
1645 *
Shawn McCarneyde629b62019-03-08 10:42:51 -06001646 * @param SensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001647 * @param sensorNames All requested sensors within the current chassis.
Shawn McCarneyde629b62019-03-08 10:42:51 -06001648 * @param connections Connections that provide sensor values.
1649 * @param objectMgrPaths Mappings from connection name to DBus object path that
1650 * implements ObjectManager.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001651 * @param inventoryItems Inventory items associated with the sensors.
Shawn McCarneyde629b62019-03-08 10:42:51 -06001652 */
1653void getSensorData(
1654 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001655 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
Shawn McCarneyde629b62019-03-08 10:42:51 -06001656 const boost::container::flat_set<std::string>& connections,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001657 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001658 objectMgrPaths,
1659 std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
Shawn McCarneyde629b62019-03-08 10:42:51 -06001660{
1661 BMCWEB_LOG_DEBUG << "getSensorData enter";
1662 // Get managed objects from all services exposing sensors
1663 for (const std::string& connection : connections)
1664 {
1665 // Response handler to process managed objects
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001666 auto getManagedObjectsCb = [SensorsAsyncResp, sensorNames,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001667 inventoryItems](
Shawn McCarneyde629b62019-03-08 10:42:51 -06001668 const boost::system::error_code ec,
1669 ManagedObjectsVectorType& resp) {
1670 BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter";
1671 if (ec)
1672 {
1673 BMCWEB_LOG_ERROR << "getManagedObjectsCb DBUS error: " << ec;
1674 messages::internalError(SensorsAsyncResp->res);
1675 return;
1676 }
1677 // Go through all objects and update response with sensor data
1678 for (const auto& objDictEntry : resp)
1679 {
1680 const std::string& objPath =
1681 static_cast<const std::string&>(objDictEntry.first);
1682 BMCWEB_LOG_DEBUG << "getManagedObjectsCb parsing object "
1683 << objPath;
1684
Shawn McCarneyde629b62019-03-08 10:42:51 -06001685 std::vector<std::string> split;
1686 // Reserve space for
1687 // /xyz/openbmc_project/sensors/<name>/<subname>
1688 split.reserve(6);
1689 boost::algorithm::split(split, objPath, boost::is_any_of("/"));
1690 if (split.size() < 6)
1691 {
1692 BMCWEB_LOG_ERROR << "Got path that isn't long enough "
1693 << objPath;
1694 continue;
1695 }
1696 // These indexes aren't intuitive, as boost::split puts an empty
1697 // string at the beginning
1698 const std::string& sensorType = split[4];
1699 const std::string& sensorName = split[5];
1700 BMCWEB_LOG_DEBUG << "sensorName " << sensorName
1701 << " sensorType " << sensorType;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001702 if (sensorNames->find(objPath) == sensorNames->end())
Shawn McCarneyde629b62019-03-08 10:42:51 -06001703 {
1704 BMCWEB_LOG_ERROR << sensorName << " not in sensor list ";
1705 continue;
1706 }
1707
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001708 // Find inventory item (if any) associated with sensor
1709 InventoryItem* inventoryItem =
1710 findInventoryItemForSensor(inventoryItems, objPath);
1711
Shawn McCarneyde629b62019-03-08 10:42:51 -06001712 const char* fieldName = nullptr;
1713 if (sensorType == "temperature")
1714 {
1715 fieldName = "Temperatures";
1716 }
1717 else if (sensorType == "fan" || sensorType == "fan_tach" ||
1718 sensorType == "fan_pwm")
1719 {
1720 fieldName = "Fans";
1721 }
1722 else if (sensorType == "voltage")
1723 {
1724 fieldName = "Voltages";
1725 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06001726 else if (sensorType == "power")
1727 {
Eddie James028f7eb2019-05-17 21:24:36 +00001728 if (!sensorName.compare("total_power"))
1729 {
1730 fieldName = "PowerControl";
1731 }
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001732 else if ((inventoryItem != nullptr) &&
1733 (inventoryItem->isPowerSupply))
Eddie James028f7eb2019-05-17 21:24:36 +00001734 {
1735 fieldName = "PowerSupplies";
1736 }
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001737 else
1738 {
1739 // Other power sensors are in SensorCollection
1740 continue;
1741 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06001742 }
1743 else
1744 {
1745 BMCWEB_LOG_ERROR << "Unsure how to handle sensorType "
1746 << sensorType;
1747 continue;
1748 }
1749
1750 nlohmann::json& tempArray =
1751 SensorsAsyncResp->res.jsonValue[fieldName];
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001752 nlohmann::json* sensorJson = nullptr;
Shawn McCarneyde629b62019-03-08 10:42:51 -06001753
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001754 if (fieldName == "PowerControl")
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001755 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001756 if (tempArray.empty())
1757 {
1758 // Put multiple "sensors" into a single PowerControl.
1759 // Follows MemberId naming and naming in power.hpp.
1760 tempArray.push_back(
1761 {{"@odata.id",
1762 "/redfish/v1/Chassis/" +
1763 SensorsAsyncResp->chassisId + "/" +
1764 SensorsAsyncResp->chassisSubNode + "#/" +
1765 fieldName + "/0"}});
1766 }
1767 sensorJson = &(tempArray.back());
Gunnar Mills7ab06f42019-07-02 13:07:16 -05001768 }
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001769 else if (fieldName == "PowerSupplies")
Gunnar Mills7ab06f42019-07-02 13:07:16 -05001770 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001771 if (inventoryItem != nullptr)
1772 {
1773 sensorJson =
1774 &(getPowerSupply(tempArray, *inventoryItem,
1775 SensorsAsyncResp->chassisId));
1776 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001777 }
1778 else
1779 {
1780 tempArray.push_back(
1781 {{"@odata.id", "/redfish/v1/Chassis/" +
1782 SensorsAsyncResp->chassisId + "/" +
1783 SensorsAsyncResp->chassisSubNode +
1784 "#/" + fieldName + "/"}});
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001785 sensorJson = &(tempArray.back());
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001786 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06001787
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001788 if (sensorJson != nullptr)
1789 {
1790 objectInterfacesToJson(sensorName, sensorType,
1791 objDictEntry.second, *sensorJson,
1792 inventoryItem);
1793 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06001794 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001795 if (SensorsAsyncResp.use_count() == 1)
James Feist8bd25cc2019-03-15 15:14:00 -07001796 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001797 sortJSONResponse(SensorsAsyncResp);
1798 if (SensorsAsyncResp->chassisSubNode == "Thermal")
1799 {
1800 populateFanRedundancy(SensorsAsyncResp);
1801 }
James Feist8bd25cc2019-03-15 15:14:00 -07001802 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06001803 BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit";
1804 };
1805
1806 // Find DBus object path that implements ObjectManager for the current
1807 // connection. If no mapping found, default to "/".
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001808 auto iter = objectMgrPaths->find(connection);
Shawn McCarneyde629b62019-03-08 10:42:51 -06001809 const std::string& objectMgrPath =
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001810 (iter != objectMgrPaths->end()) ? iter->second : "/";
Shawn McCarneyde629b62019-03-08 10:42:51 -06001811 BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
1812 << objectMgrPath;
1813
1814 crow::connections::systemBus->async_method_call(
1815 getManagedObjectsCb, connection, objectMgrPath,
1816 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1817 };
1818 BMCWEB_LOG_DEBUG << "getSensorData exit";
1819}
1820
1821/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001822 * @brief Entry point for retrieving sensors data related to requested
1823 * chassis.
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001824 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001825 */
Ed Tanous1abe55e2018-09-05 08:30:59 -07001826void getChassisData(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
1827{
1828 BMCWEB_LOG_DEBUG << "getChassisData enter";
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001829 auto getChassisCb =
1830 [SensorsAsyncResp](
1831 std::shared_ptr<boost::container::flat_set<std::string>>
1832 sensorNames) {
1833 BMCWEB_LOG_DEBUG << "getChassisCb enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001834 auto getConnectionCb = [SensorsAsyncResp, sensorNames](
1835 const boost::container::flat_set<
1836 std::string>& connections) {
1837 BMCWEB_LOG_DEBUG << "getConnectionCb enter";
1838 auto getObjectManagerPathsCb =
1839 [SensorsAsyncResp, sensorNames, connections](
1840 std::shared_ptr<boost::container::flat_map<std::string,
1841 std::string>>
1842 objectMgrPaths) {
1843 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb enter";
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001844 auto getInventoryItemsCb =
1845 [SensorsAsyncResp, sensorNames, connections,
1846 objectMgrPaths](
1847 std::shared_ptr<std::vector<InventoryItem>>
1848 inventoryItems) {
1849 BMCWEB_LOG_DEBUG << "getInventoryItemsCb enter";
1850 // Get sensor data and store results in JSON
1851 getSensorData(SensorsAsyncResp, sensorNames,
1852 connections, objectMgrPaths,
1853 inventoryItems);
1854 BMCWEB_LOG_DEBUG << "getInventoryItemsCb exit";
1855 };
1856
1857 // Get inventory items associated with sensors
1858 getInventoryItems(SensorsAsyncResp, sensorNames,
1859 objectMgrPaths,
1860 std::move(getInventoryItemsCb));
1861
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001862 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb exit";
1863 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001864
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001865 // Get mapping from connection names to the DBus object
1866 // paths that implement the ObjectManager interface
1867 getObjectManagerPaths(SensorsAsyncResp,
1868 std::move(getObjectManagerPathsCb));
1869 BMCWEB_LOG_DEBUG << "getConnectionCb exit";
1870 };
Shawn McCarneyde629b62019-03-08 10:42:51 -06001871
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001872 // Get set of connections that provide sensor values
1873 getConnections(SensorsAsyncResp, sensorNames,
1874 std::move(getConnectionCb));
1875 BMCWEB_LOG_DEBUG << "getChassisCb exit";
1876 };
Jennifer Lee4f9a2132019-03-04 12:45:19 -08001877 SensorsAsyncResp->res.jsonValue["Redundancy"] = nlohmann::json::array();
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001878
Shawn McCarney26f03892019-05-03 13:20:24 -05001879 // Get set of sensors in chassis
Ed Tanous1abe55e2018-09-05 08:30:59 -07001880 getChassis(SensorsAsyncResp, std::move(getChassisCb));
1881 BMCWEB_LOG_DEBUG << "getChassisData exit";
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001882};
1883
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301884/**
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001885 * @brief Find the requested sensorName in the list of all sensors supplied by
1886 * the chassis node
1887 *
1888 * @param sensorName The sensor name supplied in the PATCH request
1889 * @param sensorsList The list of sensors managed by the chassis node
1890 * @param sensorsModified The list of sensors that were found as a result of
1891 * repeated calls to this function
1892 */
1893bool findSensorNameUsingSensorPath(
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05301894 std::string_view sensorName,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001895 boost::container::flat_set<std::string>& sensorsList,
1896 boost::container::flat_set<std::string>& sensorsModified)
1897{
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05301898 for (std::string_view chassisSensor : sensorsList)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001899 {
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05301900 std::size_t pos = chassisSensor.rfind("/");
1901 if (pos >= (chassisSensor.size() - 1))
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001902 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001903 continue;
1904 }
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05301905 std::string_view thisSensorName = chassisSensor.substr(pos + 1);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001906 if (thisSensorName == sensorName)
1907 {
1908 sensorsModified.emplace(chassisSensor);
1909 return true;
1910 }
1911 }
1912 return false;
1913}
1914
1915/**
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301916 * @brief Entry point for overriding sensor values of given sensor
1917 *
1918 * @param res response object
1919 * @param req request object
1920 * @param params parameter passed for CRUD
1921 * @param typeList TypeList of sensors for the resource queried
1922 * @param chassisSubNode Chassis Node for which the query has to happen
1923 */
1924void setSensorOverride(crow::Response& res, const crow::Request& req,
1925 const std::vector<std::string>& params,
Ed Tanous85e14242019-06-27 15:04:09 -07001926 const std::vector<const char*> typeList,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301927 const std::string& chassisSubNode)
1928{
1929
1930 // TODO: Need to figure out dynamic way to restrict patch (Set Sensor
1931 // override) based on another d-bus announcement to be more generic.
1932 if (params.size() != 1)
1933 {
1934 messages::internalError(res);
1935 res.end();
1936 return;
1937 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301938
1939 std::unordered_map<std::string, std::vector<nlohmann::json>> allCollections;
1940 std::optional<std::vector<nlohmann::json>> temperatureCollections;
1941 std::optional<std::vector<nlohmann::json>> fanCollections;
1942 std::vector<nlohmann::json> voltageCollections;
1943 BMCWEB_LOG_INFO << "setSensorOverride for subNode" << chassisSubNode
1944 << "\n";
1945
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301946 if (chassisSubNode == "Thermal")
1947 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301948 if (!json_util::readJson(req, res, "Temperatures",
1949 temperatureCollections, "Fans",
1950 fanCollections))
1951 {
1952 return;
1953 }
1954 if (!temperatureCollections && !fanCollections)
1955 {
1956 messages::resourceNotFound(res, "Thermal",
1957 "Temperatures / Voltages");
1958 res.end();
1959 return;
1960 }
1961 if (temperatureCollections)
1962 {
1963 allCollections.emplace("Temperatures",
1964 *std::move(temperatureCollections));
1965 }
1966 if (fanCollections)
1967 {
1968 allCollections.emplace("Fans", *std::move(fanCollections));
1969 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301970 }
1971 else if (chassisSubNode == "Power")
1972 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301973 if (!json_util::readJson(req, res, "Voltages", voltageCollections))
1974 {
1975 return;
1976 }
1977 allCollections.emplace("Voltages", std::move(voltageCollections));
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301978 }
1979 else
1980 {
1981 res.result(boost::beast::http::status::not_found);
1982 res.end();
1983 return;
1984 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301985
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301986 const char* propertyValueName;
1987 std::unordered_map<std::string, std::pair<double, std::string>> overrideMap;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301988 std::string memberId;
1989 double value;
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301990 for (auto& collectionItems : allCollections)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301991 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301992 if (collectionItems.first == "Temperatures")
1993 {
1994 propertyValueName = "ReadingCelsius";
1995 }
1996 else if (collectionItems.first == "Fans")
1997 {
1998 propertyValueName = "Reading";
1999 }
2000 else
2001 {
2002 propertyValueName = "ReadingVolts";
2003 }
2004 for (auto& item : collectionItems.second)
2005 {
2006 if (!json_util::readJson(item, res, "MemberId", memberId,
2007 propertyValueName, value))
2008 {
2009 return;
2010 }
2011 overrideMap.emplace(memberId,
2012 std::make_pair(value, collectionItems.first));
2013 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302014 }
2015 const std::string& chassisName = params[0];
2016 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
2017 res, chassisName, typeList, chassisSubNode);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002018 auto getChassisSensorListCb = [sensorAsyncResp,
2019 overrideMap](const std::shared_ptr<
2020 boost::container::flat_set<
2021 std::string>>
2022 sensorsList) {
2023 // Match sensor names in the PATCH request to those managed by the
2024 // chassis node
2025 const std::shared_ptr<boost::container::flat_set<std::string>>
2026 sensorNames =
2027 std::make_shared<boost::container::flat_set<std::string>>();
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302028 for (const auto& item : overrideMap)
2029 {
2030 const auto& sensor = item.first;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002031 if (!findSensorNameUsingSensorPath(sensor, *sensorsList,
2032 *sensorNames))
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302033 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302034 BMCWEB_LOG_INFO << "Unable to find memberId " << item.first;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302035 messages::resourceNotFound(sensorAsyncResp->res,
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302036 item.second.second, item.first);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302037 return;
2038 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302039 }
2040 // Get the connection to which the memberId belongs
2041 auto getObjectsWithConnectionCb =
2042 [sensorAsyncResp, overrideMap](
2043 const boost::container::flat_set<std::string>& connections,
2044 const std::set<std::pair<std::string, std::string>>&
2045 objectsWithConnection) {
2046 if (objectsWithConnection.size() != overrideMap.size())
2047 {
2048 BMCWEB_LOG_INFO
2049 << "Unable to find all objects with proper connection "
2050 << objectsWithConnection.size() << " requested "
2051 << overrideMap.size() << "\n";
2052 messages::resourceNotFound(
2053 sensorAsyncResp->res,
2054 sensorAsyncResp->chassisSubNode == "Thermal"
2055 ? "Temperatures"
2056 : "Voltages",
2057 "Count");
2058 return;
2059 }
2060 for (const auto& item : objectsWithConnection)
2061 {
2062
2063 auto lastPos = item.first.rfind('/');
2064 if (lastPos == std::string::npos)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302065 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302066 messages::internalError(sensorAsyncResp->res);
2067 return;
2068 }
2069 std::string sensorName = item.first.substr(lastPos + 1);
2070
2071 const auto& iterator = overrideMap.find(sensorName);
2072 if (iterator == overrideMap.end())
2073 {
2074 BMCWEB_LOG_INFO << "Unable to find sensor object"
2075 << item.first << "\n";
2076 messages::internalError(sensorAsyncResp->res);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302077 return;
2078 }
2079 crow::connections::systemBus->async_method_call(
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302080 [sensorAsyncResp](const boost::system::error_code ec) {
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302081 if (ec)
2082 {
2083 BMCWEB_LOG_DEBUG
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302084 << "setOverrideValueStatus DBUS error: "
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302085 << ec;
2086 messages::internalError(sensorAsyncResp->res);
2087 return;
2088 }
2089 },
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302090 item.second, item.first,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302091 "org.freedesktop.DBus.Properties", "Set",
2092 "xyz.openbmc_project.Sensor.Value", "Value",
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302093 sdbusplus::message::variant<double>(
2094 iterator->second.first));
2095 }
2096 };
2097 // Get object with connection for the given sensor name
2098 getObjectsWithConnection(sensorAsyncResp, sensorNames,
2099 std::move(getObjectsWithConnectionCb));
2100 };
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302101 // get full sensor list for the given chassisId and cross verify the sensor.
2102 getChassis(sensorAsyncResp, std::move(getChassisSensorListCb));
2103}
2104
Ed Tanous1abe55e2018-09-05 08:30:59 -07002105} // namespace redfish