blob: 12657835d8b2e2835063522dabc1eae1445336a4 [file] [log] [blame]
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
Anthony Wilson95a3eca2019-06-11 10:44:47 -050018#include "node.hpp"
19
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010020#include <boost/algorithm/string/predicate.hpp>
21#include <boost/algorithm/string/split.hpp>
22#include <boost/container/flat_map.hpp>
23#include <boost/range/algorithm/replace_copy_if.hpp>
Ed Tanous9e27a222019-10-24 13:46:39 -070024#include <cmath>
Ed Tanous1abe55e2018-09-05 08:30:59 -070025#include <dbus_singleton.hpp>
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +053026#include <utils/json_utils.hpp>
Ed Tanousabf2add2019-01-22 16:40:12 -080027#include <variant>
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010028
Ed Tanous1abe55e2018-09-05 08:30:59 -070029namespace redfish
30{
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010031
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010032using GetSubTreeType = std::vector<
33 std::pair<std::string,
34 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
35
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -050036using SensorVariant =
37 std::variant<int64_t, double, uint32_t, bool, std::string>;
Ed Tanousaa2e59c2018-04-12 12:17:20 -070038
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010039using ManagedObjectsVectorType = std::vector<std::pair<
Ed Tanousaa2e59c2018-04-12 12:17:20 -070040 sdbusplus::message::object_path,
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010041 boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -070042 std::string, boost::container::flat_map<std::string, SensorVariant>>>>;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010043
44/**
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020045 * SensorsAsyncResp
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010046 * Gathers data needed for response processing after async calls are done
47 */
Ed Tanous1abe55e2018-09-05 08:30:59 -070048class SensorsAsyncResp
49{
50 public:
Ed Tanous271584a2019-07-09 16:24:22 -070051 SensorsAsyncResp(crow::Response& response, const std::string& chassisIdIn,
52 const std::vector<const char*> typesIn,
Ed Tanous2474adf2018-09-05 16:31:16 -070053 const std::string& subNode) :
Ed Tanous43b761d2019-02-13 20:10:56 -080054 res(response),
Ed Tanous271584a2019-07-09 16:24:22 -070055 chassisId(chassisIdIn), types(typesIn), chassisSubNode(subNode)
Ed Tanous1abe55e2018-09-05 08:30:59 -070056 {
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010057 }
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020058
Ed Tanous1abe55e2018-09-05 08:30:59 -070059 ~SensorsAsyncResp()
60 {
61 if (res.result() == boost::beast::http::status::internal_server_error)
62 {
63 // Reset the json object to clear out any data that made it in
64 // before the error happened todo(ed) handle error condition with
65 // proper code
66 res.jsonValue = nlohmann::json::object();
67 }
68 res.end();
69 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010070
Ed Tanous1abe55e2018-09-05 08:30:59 -070071 crow::Response& res;
72 std::string chassisId{};
73 const std::vector<const char*> types;
Ed Tanous2474adf2018-09-05 16:31:16 -070074 std::string chassisSubNode{};
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010075};
76
77/**
Anthony Wilsond5005492019-07-31 16:34:17 -050078 * Possible states for physical inventory leds
79 */
80enum class LedState
81{
82 OFF,
83 ON,
84 BLINK,
85 UNKNOWN
86};
87
88/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -050089 * D-Bus inventory item associated with one or more sensors.
90 */
91class InventoryItem
92{
93 public:
94 InventoryItem(const std::string& objPath) :
95 objectPath(objPath), name(), isPresent(true), isFunctional(true),
96 isPowerSupply(false), manufacturer(), model(), partNumber(),
Anthony Wilsond5005492019-07-31 16:34:17 -050097 serialNumber(), sensors(), ledObjectPath(""),
98 ledState(LedState::UNKNOWN)
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -050099 {
100 // Set inventory item name to last node of object path
101 auto pos = objectPath.rfind('/');
102 if ((pos != std::string::npos) && ((pos + 1) < objectPath.size()))
103 {
104 name = objectPath.substr(pos + 1);
105 }
106 }
107
108 std::string objectPath;
109 std::string name;
110 bool isPresent;
111 bool isFunctional;
112 bool isPowerSupply;
113 std::string manufacturer;
114 std::string model;
115 std::string partNumber;
116 std::string serialNumber;
117 std::set<std::string> sensors;
Anthony Wilsond5005492019-07-31 16:34:17 -0500118 std::string ledObjectPath;
119 LedState ledState;
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500120};
121
122/**
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530123 * @brief Get objects with connection necessary for sensors
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200124 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100125 * @param sensorNames Sensors retrieved from chassis
126 * @param callback Callback for processing gathered connections
127 */
128template <typename Callback>
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530129void getObjectsWithConnection(
130 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700131 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530132 Callback&& callback)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700133{
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530134 BMCWEB_LOG_DEBUG << "getObjectsWithConnection enter";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700135 const std::string path = "/xyz/openbmc_project/sensors";
136 const std::array<std::string, 1> interfaces = {
137 "xyz.openbmc_project.Sensor.Value"};
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100138
Ed Tanous1abe55e2018-09-05 08:30:59 -0700139 // Response handler for parsing objects subtree
140 auto respHandler = [callback{std::move(callback)}, SensorsAsyncResp,
141 sensorNames](const boost::system::error_code ec,
142 const GetSubTreeType& subtree) {
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530143 BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler enter";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700144 if (ec)
145 {
Ed Tanous5f7d88c2018-11-14 14:08:56 -0800146 messages::internalError(SensorsAsyncResp->res);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530147 BMCWEB_LOG_ERROR
148 << "getObjectsWithConnection resp_handler: Dbus error " << ec;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700149 return;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100150 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100151
Ed Tanous1abe55e2018-09-05 08:30:59 -0700152 BMCWEB_LOG_DEBUG << "Found " << subtree.size() << " subtrees";
153
154 // Make unique list of connections only for requested sensor types and
155 // found in the chassis
156 boost::container::flat_set<std::string> connections;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530157 std::set<std::pair<std::string, std::string>> objectsWithConnection;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700158 // Intrinsic to avoid malloc. Most systems will have < 8 sensor
159 // producers
160 connections.reserve(8);
161
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700162 BMCWEB_LOG_DEBUG << "sensorNames list count: " << sensorNames->size();
163 for (const std::string& tsensor : *sensorNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700164 {
165 BMCWEB_LOG_DEBUG << "Sensor to find: " << tsensor;
166 }
167
168 for (const std::pair<
169 std::string,
170 std::vector<std::pair<std::string, std::vector<std::string>>>>&
171 object : subtree)
172 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700173 if (sensorNames->find(object.first) != sensorNames->end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700174 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700175 for (const std::pair<std::string, std::vector<std::string>>&
176 objData : object.second)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700177 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700178 BMCWEB_LOG_DEBUG << "Adding connection: " << objData.first;
179 connections.insert(objData.first);
180 objectsWithConnection.insert(
181 std::make_pair(object.first, objData.first));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700182 }
183 }
184 }
185 BMCWEB_LOG_DEBUG << "Found " << connections.size() << " connections";
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530186 callback(std::move(connections), std::move(objectsWithConnection));
187 BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler exit";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700188 };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700189 // Make call to ObjectMapper to find all sensors objects
190 crow::connections::systemBus->async_method_call(
191 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
192 "/xyz/openbmc_project/object_mapper",
193 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 2, interfaces);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530194 BMCWEB_LOG_DEBUG << "getObjectsWithConnection exit";
195}
196
197/**
198 * @brief Create connections necessary for sensors
199 * @param SensorsAsyncResp Pointer to object holding response data
200 * @param sensorNames Sensors retrieved from chassis
201 * @param callback Callback for processing gathered connections
202 */
203template <typename Callback>
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700204void getConnections(
205 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
206 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
207 Callback&& callback)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530208{
209 auto objectsWithConnectionCb =
210 [callback](const boost::container::flat_set<std::string>& connections,
211 const std::set<std::pair<std::string, std::string>>&
212 objectsWithConnection) {
213 callback(std::move(connections));
214 };
215 getObjectsWithConnection(SensorsAsyncResp, sensorNames,
216 std::move(objectsWithConnectionCb));
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100217}
218
219/**
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700220 * @brief Shrinks the list of sensors for processing
221 * @param SensorsAysncResp The class holding the Redfish response
222 * @param allSensors A list of all the sensors associated to the
223 * chassis element (i.e. baseboard, front panel, etc...)
224 * @param activeSensors A list that is a reduction of the incoming
225 * allSensors list. Eliminate Thermal sensors when a Power request is
226 * made, and eliminate Power sensors when a Thermal request is made.
227 */
228void reduceSensorList(
229 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
230 const std::vector<std::string>* allSensors,
231 std::shared_ptr<boost::container::flat_set<std::string>> activeSensors)
232{
233 if (SensorsAsyncResp == nullptr)
234 {
235 return;
236 }
237 if ((allSensors == nullptr) || (activeSensors == nullptr))
238 {
239 messages::resourceNotFound(
240 SensorsAsyncResp->res, SensorsAsyncResp->chassisSubNode,
241 SensorsAsyncResp->chassisSubNode == "Thermal" ? "Temperatures"
242 : "Voltages");
243
244 return;
245 }
246 if (allSensors->empty())
247 {
248 // Nothing to do, the activeSensors object is also empty
249 return;
250 }
251
252 for (const char* type : SensorsAsyncResp->types)
253 {
254 for (const std::string& sensor : *allSensors)
255 {
256 if (boost::starts_with(sensor, type))
257 {
258 activeSensors->emplace(sensor);
259 }
260 }
261 }
262}
263
264/**
Carol Wang4bb3dc32019-10-17 18:15:02 +0800265 * @brief Retrieves valid chassis path
266 * @param asyncResp Pointer to object holding response data
267 * @param callback Callback for next step to get valid chassis path
268 */
269template <typename Callback>
270void getValidChassisPath(std::shared_ptr<SensorsAsyncResp> asyncResp,
271 Callback&& callback)
272{
273 BMCWEB_LOG_DEBUG << "checkChassisId enter";
274 const std::array<const char*, 2> interfaces = {
275 "xyz.openbmc_project.Inventory.Item.Board",
276 "xyz.openbmc_project.Inventory.Item.Chassis"};
277
278 auto respHandler =
279 [callback{std::move(callback)},
280 asyncResp](const boost::system::error_code ec,
281 const std::vector<std::string>& chassisPaths) mutable {
282 BMCWEB_LOG_DEBUG << "getValidChassisPath respHandler enter";
283 if (ec)
284 {
285 BMCWEB_LOG_ERROR
286 << "getValidChassisPath respHandler DBUS error: " << ec;
287 messages::internalError(asyncResp->res);
288 return;
289 }
290
291 std::optional<std::string> chassisPath;
292 std::string chassisName;
293 for (const std::string& chassis : chassisPaths)
294 {
295 std::size_t lastPos = chassis.rfind("/");
296 if (lastPos == std::string::npos)
297 {
298 BMCWEB_LOG_ERROR << "Failed to find '/' in " << chassis;
299 continue;
300 }
301 chassisName = chassis.substr(lastPos + 1);
302 if (chassisName == asyncResp->chassisId)
303 {
304 chassisPath = chassis;
305 break;
306 }
307 }
308 callback(chassisPath);
309 };
310
311 // Get the Chassis Collection
312 crow::connections::systemBus->async_method_call(
313 respHandler, "xyz.openbmc_project.ObjectMapper",
314 "/xyz/openbmc_project/object_mapper",
315 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
316 "/xyz/openbmc_project/inventory", 0, interfaces);
317 BMCWEB_LOG_DEBUG << "checkChassisId exit";
318}
319
320/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100321 * @brief Retrieves requested chassis sensors and redundancy data from DBus .
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200322 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100323 * @param callback Callback for next step in gathered sensor processing
324 */
325template <typename Callback>
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700326void getChassis(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700327 Callback&& callback)
328{
329 BMCWEB_LOG_DEBUG << "getChassis enter";
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500330 const std::array<const char*, 2> interfaces = {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700331 "xyz.openbmc_project.Inventory.Item.Board",
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500332 "xyz.openbmc_project.Inventory.Item.Chassis"};
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700333 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp](
334 const boost::system::error_code ec,
335 const std::vector<std::string>& chassisPaths) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700336 BMCWEB_LOG_DEBUG << "getChassis respHandler enter";
337 if (ec)
338 {
339 BMCWEB_LOG_ERROR << "getChassis respHandler DBUS error: " << ec;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700340 messages::internalError(sensorsAsyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700341 return;
342 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100343
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700344 const std::string* chassisPath = nullptr;
345 std::string chassisName;
346 for (const std::string& chassis : chassisPaths)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700347 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700348 std::size_t lastPos = chassis.rfind("/");
349 if (lastPos == std::string::npos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700350 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700351 BMCWEB_LOG_ERROR << "Failed to find '/' in " << chassis;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700352 continue;
353 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700354 chassisName = chassis.substr(lastPos + 1);
355 if (chassisName == sensorsAsyncResp->chassisId)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700356 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700357 chassisPath = &chassis;
358 break;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700359 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700360 }
361 if (chassisPath == nullptr)
362 {
363 messages::resourceNotFound(sensorsAsyncResp->res, "Chassis",
364 sensorsAsyncResp->chassisId);
365 return;
366 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700367
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700368 const std::string& chassisSubNode = sensorsAsyncResp->chassisSubNode;
369 if (chassisSubNode == "Power")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700370 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700371 sensorsAsyncResp->res.jsonValue["@odata.type"] =
372 "#Power.v1_5_2.Power";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700373 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700374 else if (chassisSubNode == "Thermal")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700375 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700376 sensorsAsyncResp->res.jsonValue["@odata.type"] =
377 "#Thermal.v1_4_0.Thermal";
Jennifer Lee4f9a2132019-03-04 12:45:19 -0800378 sensorsAsyncResp->res.jsonValue["Fans"] = nlohmann::json::array();
379 sensorsAsyncResp->res.jsonValue["Temperatures"] =
380 nlohmann::json::array();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700381 }
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500382 else if (chassisSubNode == "Sensors")
383 {
384 sensorsAsyncResp->res.jsonValue["@odata.type"] =
385 "#SensorCollection.SensorCollection";
386 sensorsAsyncResp->res.jsonValue["@odata.context"] =
387 "/redfish/v1/$metadata#SensorCollection.SensorCollection";
388 sensorsAsyncResp->res.jsonValue["Description"] =
389 "Collection of Sensors for this Chassis";
390 sensorsAsyncResp->res.jsonValue["Members"] =
391 nlohmann::json::array();
392 sensorsAsyncResp->res.jsonValue["Members@odata.count"] = 0;
393 }
394
395 if (chassisSubNode != "Sensors")
396 {
397 sensorsAsyncResp->res.jsonValue["Id"] = chassisSubNode;
398 sensorsAsyncResp->res.jsonValue["@odata.context"] =
399 "/redfish/v1/$metadata#" + chassisSubNode + "." +
400 chassisSubNode;
401 }
402
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700403 sensorsAsyncResp->res.jsonValue["@odata.id"] =
404 "/redfish/v1/Chassis/" + sensorsAsyncResp->chassisId + "/" +
405 chassisSubNode;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700406 sensorsAsyncResp->res.jsonValue["Name"] = chassisSubNode;
407
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500408 // Get the list of all sensors for this Chassis element
409 std::string sensorPath = *chassisPath + "/all_sensors";
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700410 crow::connections::systemBus->async_method_call(
411 [sensorsAsyncResp, callback{std::move(callback)}](
Ed Tanous271584a2019-07-09 16:24:22 -0700412 const boost::system::error_code& e,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700413 const std::variant<std::vector<std::string>>&
414 variantEndpoints) {
Ed Tanous271584a2019-07-09 16:24:22 -0700415 if (e)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700416 {
Ed Tanous271584a2019-07-09 16:24:22 -0700417 if (e.value() != EBADR)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700418 {
419 messages::internalError(sensorsAsyncResp->res);
420 return;
421 }
422 }
423 const std::vector<std::string>* nodeSensorList =
424 std::get_if<std::vector<std::string>>(&(variantEndpoints));
425 if (nodeSensorList == nullptr)
426 {
427 messages::resourceNotFound(
428 sensorsAsyncResp->res, sensorsAsyncResp->chassisSubNode,
429 sensorsAsyncResp->chassisSubNode == "Thermal"
430 ? "Temperatures"
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500431 : sensorsAsyncResp->chassisSubNode == "Power"
432 ? "Voltages"
433 : "Sensors");
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700434 return;
435 }
436 const std::shared_ptr<boost::container::flat_set<std::string>>
437 culledSensorList = std::make_shared<
438 boost::container::flat_set<std::string>>();
439 reduceSensorList(sensorsAsyncResp, nodeSensorList,
440 culledSensorList);
441 callback(culledSensorList);
442 },
443 "xyz.openbmc_project.ObjectMapper", sensorPath,
444 "org.freedesktop.DBus.Properties", "Get",
445 "xyz.openbmc_project.Association", "endpoints");
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100446 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100447
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700448 // Get the Chassis Collection
Ed Tanous1abe55e2018-09-05 08:30:59 -0700449 crow::connections::systemBus->async_method_call(
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700450 respHandler, "xyz.openbmc_project.ObjectMapper",
451 "/xyz/openbmc_project/object_mapper",
452 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Ed Tanous271584a2019-07-09 16:24:22 -0700453 "/xyz/openbmc_project/inventory", 0, interfaces);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700454 BMCWEB_LOG_DEBUG << "getChassis exit";
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100455}
456
457/**
Shawn McCarneyde629b62019-03-08 10:42:51 -0600458 * @brief Finds all DBus object paths that implement ObjectManager.
459 *
460 * Creates a mapping from the associated connection name to the object path.
461 *
462 * Finds the object paths asynchronously. Invokes callback when information has
463 * been obtained.
464 *
465 * The callback must have the following signature:
466 * @code
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500467 * callback(std::shared_ptr<boost::container::flat_map<std::string,
468 * std::string>> objectMgrPaths)
Shawn McCarneyde629b62019-03-08 10:42:51 -0600469 * @endcode
470 *
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700471 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyde629b62019-03-08 10:42:51 -0600472 * @param callback Callback to invoke when object paths obtained.
473 */
474template <typename Callback>
475void getObjectManagerPaths(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
476 Callback&& callback)
477{
478 BMCWEB_LOG_DEBUG << "getObjectManagerPaths enter";
479 const std::array<std::string, 1> interfaces = {
480 "org.freedesktop.DBus.ObjectManager"};
481
482 // Response handler for GetSubTree DBus method
483 auto respHandler = [callback{std::move(callback)},
484 SensorsAsyncResp](const boost::system::error_code ec,
485 const GetSubTreeType& subtree) {
486 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler enter";
487 if (ec)
488 {
489 messages::internalError(SensorsAsyncResp->res);
490 BMCWEB_LOG_ERROR << "getObjectManagerPaths respHandler: DBus error "
491 << ec;
492 return;
493 }
494
495 // Loop over returned object paths
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500496 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
497 objectMgrPaths = std::make_shared<
498 boost::container::flat_map<std::string, std::string>>();
Shawn McCarneyde629b62019-03-08 10:42:51 -0600499 for (const std::pair<
500 std::string,
501 std::vector<std::pair<std::string, std::vector<std::string>>>>&
502 object : subtree)
503 {
504 // Loop over connections for current object path
505 const std::string& objectPath = object.first;
506 for (const std::pair<std::string, std::vector<std::string>>&
507 objData : object.second)
508 {
509 // Add mapping from connection to object path
510 const std::string& connection = objData.first;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500511 (*objectMgrPaths)[connection] = objectPath;
Shawn McCarneyde629b62019-03-08 10:42:51 -0600512 BMCWEB_LOG_DEBUG << "Added mapping " << connection << " -> "
513 << objectPath;
514 }
515 }
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500516 callback(objectMgrPaths);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600517 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler exit";
518 };
519
520 // Query mapper for all DBus object paths that implement ObjectManager
521 crow::connections::systemBus->async_method_call(
522 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
523 "/xyz/openbmc_project/object_mapper",
Ed Tanous271584a2019-07-09 16:24:22 -0700524 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0, interfaces);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600525 BMCWEB_LOG_DEBUG << "getObjectManagerPaths exit";
526}
527
528/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500529 * @brief Returns the Redfish State value for the specified inventory item.
530 * @param inventoryItem D-Bus inventory item associated with a sensor.
531 * @return State value for inventory item.
James Feist34dd1792019-05-17 14:10:54 -0700532 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500533static std::string getState(const InventoryItem* inventoryItem)
534{
535 if ((inventoryItem != nullptr) && !(inventoryItem->isPresent))
536 {
537 return "Absent";
538 }
James Feist34dd1792019-05-17 14:10:54 -0700539
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500540 return "Enabled";
541}
542
543/**
544 * @brief Returns the Redfish Health value for the specified sensor.
545 * @param sensorJson Sensor JSON object.
546 * @param interfacesDict Map of all sensor interfaces.
547 * @param inventoryItem D-Bus inventory item associated with the sensor. Will
548 * be nullptr if no associated inventory item was found.
549 * @return Health value for sensor.
550 */
James Feist34dd1792019-05-17 14:10:54 -0700551static std::string getHealth(
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500552 nlohmann::json& sensorJson,
James Feist34dd1792019-05-17 14:10:54 -0700553 const boost::container::flat_map<
554 std::string, boost::container::flat_map<std::string, SensorVariant>>&
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500555 interfacesDict,
556 const InventoryItem* inventoryItem)
James Feist34dd1792019-05-17 14:10:54 -0700557{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500558 // Get current health value (if any) in the sensor JSON object. Some JSON
559 // objects contain multiple sensors (such as PowerSupplies). We want to set
560 // the overall health to be the most severe of any of the sensors.
561 std::string currentHealth;
562 auto statusIt = sensorJson.find("Status");
563 if (statusIt != sensorJson.end())
564 {
565 auto healthIt = statusIt->find("Health");
566 if (healthIt != statusIt->end())
567 {
568 std::string* health = healthIt->get_ptr<std::string*>();
569 if (health != nullptr)
570 {
571 currentHealth = *health;
572 }
573 }
574 }
575
576 // If current health in JSON object is already Critical, return that. This
577 // should override the sensor health, which might be less severe.
578 if (currentHealth == "Critical")
579 {
580 return "Critical";
581 }
582
583 // Check if sensor has critical threshold alarm
James Feist34dd1792019-05-17 14:10:54 -0700584 auto criticalThresholdIt =
585 interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Critical");
586 if (criticalThresholdIt != interfacesDict.end())
587 {
588 auto thresholdHighIt =
589 criticalThresholdIt->second.find("CriticalAlarmHigh");
590 auto thresholdLowIt =
591 criticalThresholdIt->second.find("CriticalAlarmLow");
592 if (thresholdHighIt != criticalThresholdIt->second.end())
593 {
594 const bool* asserted = std::get_if<bool>(&thresholdHighIt->second);
595 if (asserted == nullptr)
596 {
597 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
598 }
599 else if (*asserted)
600 {
601 return "Critical";
602 }
603 }
604 if (thresholdLowIt != criticalThresholdIt->second.end())
605 {
606 const bool* asserted = std::get_if<bool>(&thresholdLowIt->second);
607 if (asserted == nullptr)
608 {
609 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
610 }
611 else if (*asserted)
612 {
613 return "Critical";
614 }
615 }
616 }
617
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500618 // Check if associated inventory item is not functional
619 if ((inventoryItem != nullptr) && !(inventoryItem->isFunctional))
620 {
621 return "Critical";
622 }
623
624 // If current health in JSON object is already Warning, return that. This
625 // should override the sensor status, which might be less severe.
626 if (currentHealth == "Warning")
627 {
628 return "Warning";
629 }
630
631 // Check if sensor has warning threshold alarm
James Feist34dd1792019-05-17 14:10:54 -0700632 auto warningThresholdIt =
633 interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Warning");
634 if (warningThresholdIt != interfacesDict.end())
635 {
636 auto thresholdHighIt =
637 warningThresholdIt->second.find("WarningAlarmHigh");
638 auto thresholdLowIt =
639 warningThresholdIt->second.find("WarningAlarmLow");
640 if (thresholdHighIt != warningThresholdIt->second.end())
641 {
642 const bool* asserted = std::get_if<bool>(&thresholdHighIt->second);
643 if (asserted == nullptr)
644 {
645 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
646 }
647 else if (*asserted)
648 {
649 return "Warning";
650 }
651 }
652 if (thresholdLowIt != warningThresholdIt->second.end())
653 {
654 const bool* asserted = std::get_if<bool>(&thresholdLowIt->second);
655 if (asserted == nullptr)
656 {
657 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
658 }
659 else if (*asserted)
660 {
661 return "Warning";
662 }
663 }
664 }
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500665
James Feist34dd1792019-05-17 14:10:54 -0700666 return "OK";
667}
668
Anthony Wilsond5005492019-07-31 16:34:17 -0500669static void setLedState(nlohmann::json& sensorJson,
670 const InventoryItem* inventoryItem)
671{
672 if (inventoryItem != nullptr && !inventoryItem->ledObjectPath.empty())
673 {
674 switch (inventoryItem->ledState)
675 {
676 case LedState::OFF:
677 sensorJson["IndicatorLED"] = "Off";
678 break;
679 case LedState::ON:
680 sensorJson["IndicatorLED"] = "Lit";
681 break;
682 case LedState::BLINK:
683 sensorJson["IndicatorLED"] = "Blinking";
684 break;
685 default:
686 break;
687 }
688 }
689}
690
James Feist34dd1792019-05-17 14:10:54 -0700691/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100692 * @brief Builds a json sensor representation of a sensor.
693 * @param sensorName The name of the sensor to be built
Gunnar Mills274fad52018-06-13 15:45:36 -0500694 * @param sensorType The type (temperature, fan_tach, etc) of the sensor to
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100695 * build
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500696 * @param sensorSchema The schema (Power, Thermal, etc) being associated with
697 * the sensor to build
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100698 * @param interfacesDict A dictionary of the interfaces and properties of said
699 * interfaces to be built from
700 * @param sensor_json The json object to fill
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500701 * @param inventoryItem D-Bus inventory item associated with the sensor. Will
702 * be nullptr if no associated inventory item was found.
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100703 */
704void objectInterfacesToJson(
705 const std::string& sensorName, const std::string& sensorType,
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500706 const std::string& sensorSchema,
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100707 const boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700708 std::string, boost::container::flat_map<std::string, SensorVariant>>&
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100709 interfacesDict,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500710 nlohmann::json& sensor_json, InventoryItem* inventoryItem)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700711{
712 // We need a value interface before we can do anything with it
713 auto valueIt = interfacesDict.find("xyz.openbmc_project.Sensor.Value");
714 if (valueIt == interfacesDict.end())
715 {
716 BMCWEB_LOG_ERROR << "Sensor doesn't have a value interface";
717 return;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100718 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100719
Ed Tanous1abe55e2018-09-05 08:30:59 -0700720 // Assume values exist as is (10^0 == 1) if no scale exists
721 int64_t scaleMultiplier = 0;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100722
Ed Tanous1abe55e2018-09-05 08:30:59 -0700723 auto scaleIt = valueIt->second.find("Scale");
724 // If a scale exists, pull value as int64, and use the scaling.
725 if (scaleIt != valueIt->second.end())
726 {
Ed Tanousabf2add2019-01-22 16:40:12 -0800727 const int64_t* int64Value = std::get_if<int64_t>(&scaleIt->second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700728 if (int64Value != nullptr)
729 {
730 scaleMultiplier = *int64Value;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100731 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100732 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700733
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500734 if (sensorSchema == "Sensors")
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500735 {
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500736 // For sensors in SensorCollection we set Id instead of MemberId,
737 // including power sensors.
738 sensor_json["Id"] = sensorName;
739 sensor_json["Name"] = boost::replace_all_copy(sensorName, "_", " ");
740 }
741 else if (sensorType != "power")
742 {
743 // Set MemberId and Name for non-power sensors. For PowerSupplies and
744 // PowerControl, those properties have more general values because
745 // multiple sensors can be stored in the same JSON object.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500746 sensor_json["MemberId"] = sensorName;
747 sensor_json["Name"] = boost::replace_all_copy(sensorName, "_", " ");
748 }
Ed Tanouse742b6c2019-05-03 15:06:53 -0700749
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500750 sensor_json["Status"]["State"] = getState(inventoryItem);
751 sensor_json["Status"]["Health"] =
752 getHealth(sensor_json, interfacesDict, inventoryItem);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700753
754 // Parameter to set to override the type we get from dbus, and force it to
755 // int, regardless of what is available. This is used for schemas like fan,
756 // that require integers, not floats.
757 bool forceToInt = false;
758
Anthony Wilson3929aca2019-07-19 15:42:33 -0500759 nlohmann::json::json_pointer unit("/Reading");
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500760 if (sensorSchema == "Sensors")
761 {
762 sensor_json["@odata.type"] = "#Sensor.v1_0_0.Sensor";
763 sensor_json["@odata.context"] = "/redfish/v1/$metadata#Sensor.Sensor";
764 if (sensorType == "power")
765 {
766 sensor_json["ReadingUnits"] = "Watts";
767 }
768 else if (sensorType == "current")
769 {
770 sensor_json["ReadingUnits"] = "Amperes";
771 }
772 }
773 else if (sensorType == "temperature")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700774 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500775 unit = "/ReadingCelsius"_json_pointer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700776 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Temperature";
777 // TODO(ed) Documentation says that path should be type fan_tach,
778 // implementation seems to implement fan
779 }
780 else if (sensorType == "fan" || sensorType == "fan_tach")
781 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500782 unit = "/Reading"_json_pointer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700783 sensor_json["ReadingUnits"] = "RPM";
784 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
Anthony Wilsond5005492019-07-31 16:34:17 -0500785 setLedState(sensor_json, inventoryItem);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700786 forceToInt = true;
787 }
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700788 else if (sensorType == "fan_pwm")
789 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500790 unit = "/Reading"_json_pointer;
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700791 sensor_json["ReadingUnits"] = "Percent";
792 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
Anthony Wilsond5005492019-07-31 16:34:17 -0500793 setLedState(sensor_json, inventoryItem);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700794 forceToInt = true;
795 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700796 else if (sensorType == "voltage")
797 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500798 unit = "/ReadingVolts"_json_pointer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700799 sensor_json["@odata.type"] = "#Power.v1_0_0.Voltage";
800 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700801 else if (sensorType == "power")
802 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700803 std::string sensorNameLower =
804 boost::algorithm::to_lower_copy(sensorName);
805
Eddie James028f7eb2019-05-17 21:24:36 +0000806 if (!sensorName.compare("total_power"))
807 {
Gunnar Mills7ab06f42019-07-02 13:07:16 -0500808 sensor_json["@odata.type"] = "#Power.v1_0_0.PowerControl";
809 // Put multiple "sensors" into a single PowerControl, so have
810 // generic names for MemberId and Name. Follows Redfish mockup.
811 sensor_json["MemberId"] = "0";
812 sensor_json["Name"] = "Chassis Power Control";
Anthony Wilson3929aca2019-07-19 15:42:33 -0500813 unit = "/PowerConsumedWatts"_json_pointer;
Eddie James028f7eb2019-05-17 21:24:36 +0000814 }
815 else if (sensorNameLower.find("input") != std::string::npos)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700816 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500817 unit = "/PowerInputWatts"_json_pointer;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700818 }
819 else
820 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500821 unit = "/PowerOutputWatts"_json_pointer;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700822 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700823 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700824 else
825 {
826 BMCWEB_LOG_ERROR << "Redfish cannot map object type for " << sensorName;
827 return;
828 }
829 // Map of dbus interface name, dbus property name and redfish property_name
Anthony Wilson3929aca2019-07-19 15:42:33 -0500830 std::vector<
831 std::tuple<const char*, const char*, nlohmann::json::json_pointer>>
832 properties;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700833 properties.reserve(7);
834
835 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600836
Anthony Wilson3929aca2019-07-19 15:42:33 -0500837 if (sensorSchema == "Sensors")
838 {
839 properties.emplace_back(
840 "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningHigh",
841 "/Thresholds/UpperCaution/Reading"_json_pointer);
842 properties.emplace_back(
843 "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningLow",
844 "/Thresholds/LowerCaution/Reading"_json_pointer);
845 properties.emplace_back(
846 "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalHigh",
847 "/Thresholds/UpperCritical/Reading"_json_pointer);
848 properties.emplace_back(
849 "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalLow",
850 "/Thresholds/LowerCritical/Reading"_json_pointer);
851 }
852 else if (sensorType != "power")
Shawn McCarneyde629b62019-03-08 10:42:51 -0600853 {
854 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500855 "WarningHigh",
856 "/UpperThresholdNonCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600857 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500858 "WarningLow",
859 "/LowerThresholdNonCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600860 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500861 "CriticalHigh",
862 "/UpperThresholdCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600863 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500864 "CriticalLow",
865 "/LowerThresholdCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600866 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700867
Ed Tanous2474adf2018-09-05 16:31:16 -0700868 // TODO Need to get UpperThresholdFatal and LowerThresholdFatal
869
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500870 if (sensorSchema == "Sensors")
871 {
872 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500873 "/ReadingRangeMin"_json_pointer);
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500874 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500875 "/ReadingRangeMax"_json_pointer);
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500876 }
877 else if (sensorType == "temperature")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700878 {
879 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500880 "/MinReadingRangeTemp"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700881 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500882 "/MaxReadingRangeTemp"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700883 }
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500884 else if (sensorType != "power")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700885 {
886 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500887 "/MinReadingRange"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700888 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500889 "/MaxReadingRange"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700890 }
891
Anthony Wilson3929aca2019-07-19 15:42:33 -0500892 for (const std::tuple<const char*, const char*,
893 nlohmann::json::json_pointer>& p : properties)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700894 {
895 auto interfaceProperties = interfacesDict.find(std::get<0>(p));
896 if (interfaceProperties != interfacesDict.end())
897 {
Ed Tanous271584a2019-07-09 16:24:22 -0700898 auto thisValueIt = interfaceProperties->second.find(std::get<1>(p));
899 if (thisValueIt != interfaceProperties->second.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700900 {
Ed Tanous271584a2019-07-09 16:24:22 -0700901 const SensorVariant& valueVariant = thisValueIt->second;
Anthony Wilson3929aca2019-07-19 15:42:33 -0500902
903 // The property we want to set may be nested json, so use
904 // a json_pointer for easy indexing into the json structure.
905 const nlohmann::json::json_pointer& key = std::get<2>(p);
906
Ed Tanous1abe55e2018-09-05 08:30:59 -0700907 // Attempt to pull the int64 directly
Ed Tanousabf2add2019-01-22 16:40:12 -0800908 const int64_t* int64Value = std::get_if<int64_t>(&valueVariant);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700909
Ed Tanousabf2add2019-01-22 16:40:12 -0800910 const double* doubleValue = std::get_if<double>(&valueVariant);
Eddie James028f7eb2019-05-17 21:24:36 +0000911 const uint32_t* uValue = std::get_if<uint32_t>(&valueVariant);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700912 double temp = 0.0;
913 if (int64Value != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700914 {
Ed Tanous271584a2019-07-09 16:24:22 -0700915 temp = static_cast<double>(*int64Value);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700916 }
917 else if (doubleValue != nullptr)
918 {
919 temp = *doubleValue;
920 }
Eddie James028f7eb2019-05-17 21:24:36 +0000921 else if (uValue != nullptr)
922 {
923 temp = *uValue;
924 }
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700925 else
926 {
927 BMCWEB_LOG_ERROR
928 << "Got value interface that wasn't int or double";
929 continue;
930 }
931 temp = temp * std::pow(10, scaleMultiplier);
932 if (forceToInt)
933 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500934 sensor_json[key] = static_cast<int64_t>(temp);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700935 }
936 else
937 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500938 sensor_json[key] = temp;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700939 }
940 }
941 }
942 }
943 BMCWEB_LOG_DEBUG << "Added sensor " << sensorName;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100944}
945
James Feist8bd25cc2019-03-15 15:14:00 -0700946static void
947 populateFanRedundancy(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp)
948{
949 crow::connections::systemBus->async_method_call(
950 [sensorsAsyncResp](const boost::system::error_code ec,
951 const GetSubTreeType& resp) {
952 if (ec)
953 {
954 return; // don't have to have this interface
955 }
Ed Tanouse278c182019-03-13 16:23:37 -0700956 for (const std::pair<std::string,
957 std::vector<std::pair<
958 std::string, std::vector<std::string>>>>&
959 pathPair : resp)
James Feist8bd25cc2019-03-15 15:14:00 -0700960 {
Ed Tanouse278c182019-03-13 16:23:37 -0700961 const std::string& path = pathPair.first;
962 const std::vector<
963 std::pair<std::string, std::vector<std::string>>>& objDict =
964 pathPair.second;
James Feist8bd25cc2019-03-15 15:14:00 -0700965 if (objDict.empty())
966 {
967 continue; // this should be impossible
968 }
969
970 const std::string& owner = objDict.begin()->first;
971 crow::connections::systemBus->async_method_call(
972 [path, owner,
Ed Tanous271584a2019-07-09 16:24:22 -0700973 sensorsAsyncResp](const boost::system::error_code e,
James Feist8bd25cc2019-03-15 15:14:00 -0700974 std::variant<std::vector<std::string>>
975 variantEndpoints) {
Ed Tanous271584a2019-07-09 16:24:22 -0700976 if (e)
James Feist8bd25cc2019-03-15 15:14:00 -0700977 {
978 return; // if they don't have an association we
979 // can't tell what chassis is
980 }
981 // verify part of the right chassis
982 auto endpoints = std::get_if<std::vector<std::string>>(
983 &variantEndpoints);
984
985 if (endpoints == nullptr)
986 {
987 BMCWEB_LOG_ERROR << "Invalid association interface";
988 messages::internalError(sensorsAsyncResp->res);
989 return;
990 }
991
992 auto found = std::find_if(
993 endpoints->begin(), endpoints->end(),
994 [sensorsAsyncResp](const std::string& entry) {
995 return entry.find(
996 sensorsAsyncResp->chassisId) !=
997 std::string::npos;
998 });
999
1000 if (found == endpoints->end())
1001 {
1002 return;
1003 }
1004 crow::connections::systemBus->async_method_call(
1005 [path, sensorsAsyncResp](
Ed Tanous271584a2019-07-09 16:24:22 -07001006 const boost::system::error_code& err,
James Feist8bd25cc2019-03-15 15:14:00 -07001007 const boost::container::flat_map<
1008 std::string,
1009 std::variant<uint8_t,
1010 std::vector<std::string>,
1011 std::string>>& ret) {
Ed Tanous271584a2019-07-09 16:24:22 -07001012 if (err)
James Feist8bd25cc2019-03-15 15:14:00 -07001013 {
1014 return; // don't have to have this
1015 // interface
1016 }
1017 auto findFailures = ret.find("AllowedFailures");
1018 auto findCollection = ret.find("Collection");
1019 auto findStatus = ret.find("Status");
1020
1021 if (findFailures == ret.end() ||
1022 findCollection == ret.end() ||
1023 findStatus == ret.end())
1024 {
1025 BMCWEB_LOG_ERROR
1026 << "Invalid redundancy interface";
1027 messages::internalError(
1028 sensorsAsyncResp->res);
1029 return;
1030 }
1031
1032 auto allowedFailures = std::get_if<uint8_t>(
1033 &(findFailures->second));
1034 auto collection =
1035 std::get_if<std::vector<std::string>>(
1036 &(findCollection->second));
1037 auto status = std::get_if<std::string>(
1038 &(findStatus->second));
1039
1040 if (allowedFailures == nullptr ||
1041 collection == nullptr || status == nullptr)
1042 {
1043
1044 BMCWEB_LOG_ERROR
1045 << "Invalid redundancy interface "
1046 "types";
1047 messages::internalError(
1048 sensorsAsyncResp->res);
1049 return;
1050 }
1051 size_t lastSlash = path.rfind("/");
1052 if (lastSlash == std::string::npos)
1053 {
1054 // this should be impossible
1055 messages::internalError(
1056 sensorsAsyncResp->res);
1057 return;
1058 }
1059 std::string name = path.substr(lastSlash + 1);
1060 std::replace(name.begin(), name.end(), '_',
1061 ' ');
1062
1063 std::string health;
1064
1065 if (boost::ends_with(*status, "Full"))
1066 {
1067 health = "OK";
1068 }
1069 else if (boost::ends_with(*status, "Degraded"))
1070 {
1071 health = "Warning";
1072 }
1073 else
1074 {
1075 health = "Critical";
1076 }
1077 std::vector<nlohmann::json> redfishCollection;
1078 const auto& fanRedfish =
1079 sensorsAsyncResp->res.jsonValue["Fans"];
1080 for (const std::string& item : *collection)
1081 {
1082 lastSlash = item.rfind("/");
1083 // make a copy as collection is const
1084 std::string itemName =
1085 item.substr(lastSlash + 1);
1086 /*
1087 todo(ed): merge patch that fixes the names
1088 std::replace(itemName.begin(),
1089 itemName.end(), '_', ' ');*/
1090 auto schemaItem = std::find_if(
1091 fanRedfish.begin(), fanRedfish.end(),
1092 [itemName](const nlohmann::json& fan) {
1093 return fan["MemberId"] == itemName;
1094 });
1095 if (schemaItem != fanRedfish.end())
1096 {
1097 redfishCollection.push_back(
1098 {{"@odata.id",
1099 (*schemaItem)["@odata.id"]}});
1100 }
1101 else
1102 {
1103 BMCWEB_LOG_ERROR
1104 << "failed to find fan in schema";
1105 messages::internalError(
1106 sensorsAsyncResp->res);
1107 return;
1108 }
1109 }
1110
Ed Tanous271584a2019-07-09 16:24:22 -07001111 nlohmann::json& jResp =
1112 sensorsAsyncResp->res
1113 .jsonValue["Redundancy"];
1114 jResp.push_back(
James Feist8bd25cc2019-03-15 15:14:00 -07001115 {{"@odata.id",
AppaRao Puli717794d2019-10-18 22:54:53 +05301116 "/redfish/v1/Chassis/" +
James Feist8bd25cc2019-03-15 15:14:00 -07001117 sensorsAsyncResp->chassisId + "/" +
1118 sensorsAsyncResp->chassisSubNode +
1119 "#/Redundancy/" +
Ed Tanous271584a2019-07-09 16:24:22 -07001120 std::to_string(jResp.size())},
James Feist8bd25cc2019-03-15 15:14:00 -07001121 {"@odata.type",
1122 "#Redundancy.v1_3_2.Redundancy"},
1123 {"MinNumNeeded",
1124 collection->size() - *allowedFailures},
1125 {"MemberId", name},
1126 {"Mode", "N+m"},
1127 {"Name", name},
1128 {"RedundancySet", redfishCollection},
1129 {"Status",
1130 {{"Health", health},
1131 {"State", "Enabled"}}}});
1132 },
1133 owner, path, "org.freedesktop.DBus.Properties",
1134 "GetAll",
1135 "xyz.openbmc_project.Control.FanRedundancy");
1136 },
James Feist02e92e32019-06-26 12:07:05 -07001137 "xyz.openbmc_project.ObjectMapper", path + "/chassis",
James Feist8bd25cc2019-03-15 15:14:00 -07001138 "org.freedesktop.DBus.Properties", "Get",
1139 "xyz.openbmc_project.Association", "endpoints");
1140 }
1141 },
1142 "xyz.openbmc_project.ObjectMapper",
1143 "/xyz/openbmc_project/object_mapper",
1144 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
1145 "/xyz/openbmc_project/control", 2,
1146 std::array<const char*, 1>{
1147 "xyz.openbmc_project.Control.FanRedundancy"});
1148}
1149
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001150void sortJSONResponse(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
1151{
1152 nlohmann::json& response = SensorsAsyncResp->res.jsonValue;
1153 std::array<std::string, 2> sensorHeaders{"Temperatures", "Fans"};
1154 if (SensorsAsyncResp->chassisSubNode == "Power")
1155 {
1156 sensorHeaders = {"Voltages", "PowerSupplies"};
1157 }
1158 for (const std::string& sensorGroup : sensorHeaders)
1159 {
1160 nlohmann::json::iterator entry = response.find(sensorGroup);
1161 if (entry != response.end())
1162 {
1163 std::sort(entry->begin(), entry->end(),
1164 [](nlohmann::json& c1, nlohmann::json& c2) {
1165 return c1["Name"] < c2["Name"];
1166 });
1167
1168 // add the index counts to the end of each entry
1169 size_t count = 0;
1170 for (nlohmann::json& sensorJson : *entry)
1171 {
1172 nlohmann::json::iterator odata = sensorJson.find("@odata.id");
1173 if (odata == sensorJson.end())
1174 {
1175 continue;
1176 }
1177 std::string* value = odata->get_ptr<std::string*>();
1178 if (value != nullptr)
1179 {
1180 *value += std::to_string(count);
1181 count++;
1182 }
1183 }
1184 }
1185 }
1186}
1187
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001188/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001189 * @brief Finds the inventory item with the specified object path.
1190 * @param inventoryItems D-Bus inventory items associated with sensors.
1191 * @param invItemObjPath D-Bus object path of inventory item.
1192 * @return Inventory item within vector, or nullptr if no match found.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001193 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001194static InventoryItem* findInventoryItem(
1195 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1196 const std::string& invItemObjPath)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001197{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001198 for (InventoryItem& inventoryItem : *inventoryItems)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001199 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001200 if (inventoryItem.objectPath == invItemObjPath)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001201 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001202 return &inventoryItem;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001203 }
1204 }
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001205 return nullptr;
1206}
1207
1208/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001209 * @brief Finds the inventory item associated with the specified sensor.
1210 * @param inventoryItems D-Bus inventory items associated with sensors.
1211 * @param sensorObjPath D-Bus object path of sensor.
1212 * @return Inventory item within vector, or nullptr if no match found.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001213 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001214static InventoryItem* findInventoryItemForSensor(
1215 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1216 const std::string& sensorObjPath)
1217{
1218 for (InventoryItem& inventoryItem : *inventoryItems)
1219 {
1220 if (inventoryItem.sensors.count(sensorObjPath) > 0)
1221 {
1222 return &inventoryItem;
1223 }
1224 }
1225 return nullptr;
1226}
1227
1228/**
Anthony Wilsond5005492019-07-31 16:34:17 -05001229 * @brief Finds the inventory item associated with the specified led path.
1230 * @param inventoryItems D-Bus inventory items associated with sensors.
1231 * @param ledObjPath D-Bus object path of led.
1232 * @return Inventory item within vector, or nullptr if no match found.
1233 */
1234inline InventoryItem*
1235 findInventoryItemForLed(std::vector<InventoryItem>& inventoryItems,
1236 const std::string& ledObjPath)
1237{
1238 for (InventoryItem& inventoryItem : inventoryItems)
1239 {
1240 if (inventoryItem.ledObjectPath == ledObjPath)
1241 {
1242 return &inventoryItem;
1243 }
1244 }
1245 return nullptr;
1246}
1247
1248/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001249 * @brief Adds inventory item and associated sensor to specified vector.
1250 *
1251 * Adds a new InventoryItem to the vector if necessary. Searches for an
1252 * existing InventoryItem with the specified object path. If not found, one is
1253 * added to the vector.
1254 *
1255 * Next, the specified sensor is added to the set of sensors associated with the
1256 * InventoryItem.
1257 *
1258 * @param inventoryItems D-Bus inventory items associated with sensors.
1259 * @param invItemObjPath D-Bus object path of inventory item.
1260 * @param sensorObjPath D-Bus object path of sensor
1261 */
1262static void
1263 addInventoryItem(std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1264 const std::string& invItemObjPath,
1265 const std::string& sensorObjPath)
1266{
1267 // Look for inventory item in vector
1268 InventoryItem* inventoryItem =
1269 findInventoryItem(inventoryItems, invItemObjPath);
1270
1271 // If inventory item doesn't exist in vector, add it
1272 if (inventoryItem == nullptr)
1273 {
1274 inventoryItems->emplace_back(invItemObjPath);
1275 inventoryItem = &(inventoryItems->back());
1276 }
1277
1278 // Add sensor to set of sensors associated with inventory item
1279 inventoryItem->sensors.emplace(sensorObjPath);
1280}
1281
1282/**
1283 * @brief Stores D-Bus data in the specified inventory item.
1284 *
1285 * Finds D-Bus data in the specified map of interfaces. Stores the data in the
1286 * specified InventoryItem.
1287 *
1288 * This data is later used to provide sensor property values in the JSON
1289 * response.
1290 *
1291 * @param inventoryItem Inventory item where data will be stored.
1292 * @param interfacesDict Map containing D-Bus interfaces and their properties
1293 * for the specified inventory item.
1294 */
1295static void storeInventoryItemData(
1296 InventoryItem& inventoryItem,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001297 const boost::container::flat_map<
1298 std::string, boost::container::flat_map<std::string, SensorVariant>>&
1299 interfacesDict)
1300{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001301 // Get properties from Inventory.Item interface
1302 auto interfaceIt =
1303 interfacesDict.find("xyz.openbmc_project.Inventory.Item");
1304 if (interfaceIt != interfacesDict.end())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001305 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001306 auto propertyIt = interfaceIt->second.find("Present");
1307 if (propertyIt != interfaceIt->second.end())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001308 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001309 const bool* value = std::get_if<bool>(&propertyIt->second);
1310 if (value != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001311 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001312 inventoryItem.isPresent = *value;
1313 }
1314 }
1315 }
1316
1317 // Check if Inventory.Item.PowerSupply interface is present
1318 interfaceIt =
1319 interfacesDict.find("xyz.openbmc_project.Inventory.Item.PowerSupply");
1320 if (interfaceIt != interfacesDict.end())
1321 {
1322 inventoryItem.isPowerSupply = true;
1323 }
1324
1325 // Get properties from Inventory.Decorator.Asset interface
1326 interfaceIt =
1327 interfacesDict.find("xyz.openbmc_project.Inventory.Decorator.Asset");
1328 if (interfaceIt != interfacesDict.end())
1329 {
1330 auto propertyIt = interfaceIt->second.find("Manufacturer");
1331 if (propertyIt != interfaceIt->second.end())
1332 {
1333 const std::string* value =
1334 std::get_if<std::string>(&propertyIt->second);
1335 if (value != nullptr)
1336 {
1337 inventoryItem.manufacturer = *value;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001338 }
1339 }
1340
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001341 propertyIt = interfaceIt->second.find("Model");
1342 if (propertyIt != interfaceIt->second.end())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001343 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001344 const std::string* value =
1345 std::get_if<std::string>(&propertyIt->second);
1346 if (value != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001347 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001348 inventoryItem.model = *value;
1349 }
1350 }
1351
1352 propertyIt = interfaceIt->second.find("PartNumber");
1353 if (propertyIt != interfaceIt->second.end())
1354 {
1355 const std::string* value =
1356 std::get_if<std::string>(&propertyIt->second);
1357 if (value != nullptr)
1358 {
1359 inventoryItem.partNumber = *value;
1360 }
1361 }
1362
1363 propertyIt = interfaceIt->second.find("SerialNumber");
1364 if (propertyIt != interfaceIt->second.end())
1365 {
1366 const std::string* value =
1367 std::get_if<std::string>(&propertyIt->second);
1368 if (value != nullptr)
1369 {
1370 inventoryItem.serialNumber = *value;
1371 }
1372 }
1373 }
1374
1375 // Get properties from State.Decorator.OperationalStatus interface
1376 interfaceIt = interfacesDict.find(
1377 "xyz.openbmc_project.State.Decorator.OperationalStatus");
1378 if (interfaceIt != interfacesDict.end())
1379 {
1380 auto propertyIt = interfaceIt->second.find("Functional");
1381 if (propertyIt != interfaceIt->second.end())
1382 {
1383 const bool* value = std::get_if<bool>(&propertyIt->second);
1384 if (value != nullptr)
1385 {
1386 inventoryItem.isFunctional = *value;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001387 }
1388 }
1389 }
1390}
1391
1392/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001393 * @brief Gets D-Bus data for inventory items associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001394 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001395 * Uses the specified connections (services) to obtain D-Bus data for inventory
1396 * items associated with sensors. Stores the resulting data in the
1397 * inventoryItems vector.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001398 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001399 * This data is later used to provide sensor property values in the JSON
1400 * response.
1401 *
1402 * Finds the inventory item data asynchronously. Invokes callback when data has
1403 * been obtained.
1404 *
1405 * The callback must have the following signature:
1406 * @code
Anthony Wilsond5005492019-07-31 16:34:17 -05001407 * callback(void)
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001408 * @endcode
1409 *
1410 * This function is called recursively, obtaining data asynchronously from one
1411 * connection in each call. This ensures the callback is not invoked until the
1412 * last asynchronous function has completed.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001413 *
1414 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001415 * @param inventoryItems D-Bus inventory items associated with sensors.
1416 * @param invConnections Connections that provide data for the inventory items.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001417 * @param objectMgrPaths Mappings from connection name to DBus object path that
1418 * implements ObjectManager.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001419 * @param callback Callback to invoke when inventory data has been obtained.
1420 * @param invConnectionsIndex Current index in invConnections. Only specified
1421 * in recursive calls to this function.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001422 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001423template <typename Callback>
1424static void getInventoryItemsData(
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001425 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001426 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001427 std::shared_ptr<boost::container::flat_set<std::string>> invConnections,
1428 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001429 objectMgrPaths,
Ed Tanous271584a2019-07-09 16:24:22 -07001430 Callback&& callback, size_t invConnectionsIndex = 0)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001431{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001432 BMCWEB_LOG_DEBUG << "getInventoryItemsData enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001433
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001434 // If no more connections left, call callback
1435 if (invConnectionsIndex >= invConnections->size())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001436 {
Anthony Wilsond5005492019-07-31 16:34:17 -05001437 callback();
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001438 BMCWEB_LOG_DEBUG << "getInventoryItemsData exit";
1439 return;
1440 }
1441
1442 // Get inventory item data from current connection
1443 auto it = invConnections->nth(invConnectionsIndex);
1444 if (it != invConnections->end())
1445 {
1446 const std::string& invConnection = *it;
1447
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001448 // Response handler for GetManagedObjects
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001449 auto respHandler = [sensorsAsyncResp, inventoryItems, invConnections,
1450 objectMgrPaths, callback{std::move(callback)},
1451 invConnectionsIndex](
1452 const boost::system::error_code ec,
1453 ManagedObjectsVectorType& resp) {
1454 BMCWEB_LOG_DEBUG << "getInventoryItemsData respHandler enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001455 if (ec)
1456 {
1457 BMCWEB_LOG_ERROR
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001458 << "getInventoryItemsData respHandler DBus error " << ec;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001459 messages::internalError(sensorsAsyncResp->res);
1460 return;
1461 }
1462
1463 // Loop through returned object paths
1464 for (const auto& objDictEntry : resp)
1465 {
1466 const std::string& objPath =
1467 static_cast<const std::string&>(objDictEntry.first);
1468
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001469 // If this object path is one of the specified inventory items
1470 InventoryItem* inventoryItem =
1471 findInventoryItem(inventoryItems, objPath);
1472 if (inventoryItem != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001473 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001474 // Store inventory data in InventoryItem
1475 storeInventoryItemData(*inventoryItem, objDictEntry.second);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001476 }
1477 }
1478
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001479 // Recurse to get inventory item data from next connection
1480 getInventoryItemsData(sensorsAsyncResp, inventoryItems,
1481 invConnections, objectMgrPaths,
1482 std::move(callback), invConnectionsIndex + 1);
1483
1484 BMCWEB_LOG_DEBUG << "getInventoryItemsData respHandler exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001485 };
1486
1487 // Find DBus object path that implements ObjectManager for the current
1488 // connection. If no mapping found, default to "/".
1489 auto iter = objectMgrPaths->find(invConnection);
1490 const std::string& objectMgrPath =
1491 (iter != objectMgrPaths->end()) ? iter->second : "/";
1492 BMCWEB_LOG_DEBUG << "ObjectManager path for " << invConnection << " is "
1493 << objectMgrPath;
1494
1495 // Get all object paths and their interfaces for current connection
1496 crow::connections::systemBus->async_method_call(
1497 std::move(respHandler), invConnection, objectMgrPath,
1498 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1499 }
1500
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001501 BMCWEB_LOG_DEBUG << "getInventoryItemsData exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001502}
1503
1504/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001505 * @brief Gets connections that provide D-Bus data for inventory items.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001506 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001507 * Gets the D-Bus connections (services) that provide data for the inventory
1508 * items that are associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001509 *
1510 * Finds the connections asynchronously. Invokes callback when information has
1511 * been obtained.
1512 *
1513 * The callback must have the following signature:
1514 * @code
1515 * callback(std::shared_ptr<boost::container::flat_set<std::string>>
1516 * invConnections)
1517 * @endcode
1518 *
1519 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001520 * @param inventoryItems D-Bus inventory items associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001521 * @param callback Callback to invoke when connections have been obtained.
1522 */
1523template <typename Callback>
1524static void getInventoryItemsConnections(
1525 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001526 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001527 Callback&& callback)
1528{
1529 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections enter";
1530
1531 const std::string path = "/xyz/openbmc_project/inventory";
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001532 const std::array<std::string, 4> interfaces = {
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001533 "xyz.openbmc_project.Inventory.Item",
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001534 "xyz.openbmc_project.Inventory.Item.PowerSupply",
1535 "xyz.openbmc_project.Inventory.Decorator.Asset",
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001536 "xyz.openbmc_project.State.Decorator.OperationalStatus"};
1537
1538 // Response handler for parsing output from GetSubTree
1539 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001540 inventoryItems](const boost::system::error_code ec,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001541 const GetSubTreeType& subtree) {
1542 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections respHandler enter";
1543 if (ec)
1544 {
1545 messages::internalError(sensorsAsyncResp->res);
1546 BMCWEB_LOG_ERROR
1547 << "getInventoryItemsConnections respHandler DBus error " << ec;
1548 return;
1549 }
1550
1551 // Make unique list of connections for desired inventory items
1552 std::shared_ptr<boost::container::flat_set<std::string>>
1553 invConnections =
1554 std::make_shared<boost::container::flat_set<std::string>>();
1555 invConnections->reserve(8);
1556
1557 // Loop through objects from GetSubTree
1558 for (const std::pair<
1559 std::string,
1560 std::vector<std::pair<std::string, std::vector<std::string>>>>&
1561 object : subtree)
1562 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001563 // Check if object path is one of the specified inventory items
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001564 const std::string& objPath = object.first;
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001565 if (findInventoryItem(inventoryItems, objPath) != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001566 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001567 // Store all connections to inventory item
1568 for (const std::pair<std::string, std::vector<std::string>>&
1569 objData : object.second)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001570 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001571 const std::string& invConnection = objData.first;
1572 invConnections->insert(invConnection);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001573 }
1574 }
1575 }
Anthony Wilsond5005492019-07-31 16:34:17 -05001576
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001577 callback(invConnections);
1578 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections respHandler exit";
1579 };
1580
1581 // Make call to ObjectMapper to find all inventory items
1582 crow::connections::systemBus->async_method_call(
1583 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
1584 "/xyz/openbmc_project/object_mapper",
1585 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 0, interfaces);
1586 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections exit";
1587}
1588
1589/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001590 * @brief Gets associations from sensors to inventory items.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001591 *
1592 * Looks for ObjectMapper associations from the specified sensors to related
Anthony Wilsond5005492019-07-31 16:34:17 -05001593 * inventory items. Then finds the associations from those inventory items to
1594 * their LEDs, if any.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001595 *
1596 * Finds the inventory items asynchronously. Invokes callback when information
1597 * has been obtained.
1598 *
1599 * The callback must have the following signature:
1600 * @code
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001601 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001602 * @endcode
1603 *
1604 * @param sensorsAsyncResp Pointer to object holding response data.
1605 * @param sensorNames All sensors within the current chassis.
1606 * @param objectMgrPaths Mappings from connection name to DBus object path that
1607 * implements ObjectManager.
1608 * @param callback Callback to invoke when inventory items have been obtained.
1609 */
1610template <typename Callback>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001611static void getInventoryItemAssociations(
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001612 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1613 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
1614 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1615 objectMgrPaths,
1616 Callback&& callback)
1617{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001618 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001619
1620 // Response handler for GetManagedObjects
1621 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
1622 sensorNames](const boost::system::error_code ec,
1623 dbus::utility::ManagedObjectType& resp) {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001624 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations respHandler enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001625 if (ec)
1626 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001627 BMCWEB_LOG_ERROR
1628 << "getInventoryItemAssociations respHandler DBus error " << ec;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001629 messages::internalError(sensorsAsyncResp->res);
1630 return;
1631 }
1632
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001633 // Create vector to hold list of inventory items
1634 std::shared_ptr<std::vector<InventoryItem>> inventoryItems =
1635 std::make_shared<std::vector<InventoryItem>>();
1636
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001637 // Loop through returned object paths
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001638 std::string sensorAssocPath;
1639 sensorAssocPath.reserve(128); // avoid memory allocations
1640 for (const auto& objDictEntry : resp)
1641 {
1642 const std::string& objPath =
1643 static_cast<const std::string&>(objDictEntry.first);
1644 const boost::container::flat_map<
1645 std::string, boost::container::flat_map<
1646 std::string, dbus::utility::DbusVariantType>>&
1647 interfacesDict = objDictEntry.second;
1648
1649 // If path is inventory association for one of the specified sensors
1650 for (const std::string& sensorName : *sensorNames)
1651 {
1652 sensorAssocPath = sensorName;
1653 sensorAssocPath += "/inventory";
1654 if (objPath == sensorAssocPath)
1655 {
1656 // Get Association interface for object path
1657 auto assocIt =
1658 interfacesDict.find("xyz.openbmc_project.Association");
1659 if (assocIt != interfacesDict.end())
1660 {
1661 // Get inventory item from end point
1662 auto endpointsIt = assocIt->second.find("endpoints");
1663 if (endpointsIt != assocIt->second.end())
1664 {
1665 const std::vector<std::string>* endpoints =
1666 std::get_if<std::vector<std::string>>(
1667 &endpointsIt->second);
1668 if ((endpoints != nullptr) && !endpoints->empty())
1669 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001670 // Add inventory item to vector
1671 const std::string& invItemPath =
1672 endpoints->front();
1673 addInventoryItem(inventoryItems, invItemPath,
1674 sensorName);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001675 }
1676 }
1677 }
1678 break;
1679 }
1680 }
1681 }
1682
Anthony Wilsond5005492019-07-31 16:34:17 -05001683 // Now loop through the returned object paths again, this time to
1684 // find the leds associated with the inventory items we just found
1685 std::string inventoryAssocPath;
1686 inventoryAssocPath.reserve(128); // avoid memory allocations
1687 for (const auto& objDictEntry : resp)
1688 {
1689 const std::string& objPath =
1690 static_cast<const std::string&>(objDictEntry.first);
1691 const boost::container::flat_map<
1692 std::string, boost::container::flat_map<
1693 std::string, dbus::utility::DbusVariantType>>&
1694 interfacesDict = objDictEntry.second;
1695
1696 for (InventoryItem& inventoryItem : *inventoryItems)
1697 {
1698 inventoryAssocPath = inventoryItem.objectPath;
1699 inventoryAssocPath += "/leds";
1700 if (objPath == inventoryAssocPath)
1701 {
1702 // Get Association interface for object path
1703 auto assocIt =
1704 interfacesDict.find("xyz.openbmc_project.Association");
1705 if (assocIt != interfacesDict.end())
1706 {
1707 // Get inventory item from end point
1708 auto endpointsIt = assocIt->second.find("endpoints");
1709 if (endpointsIt != assocIt->second.end())
1710 {
1711 const std::vector<std::string>* endpoints =
1712 std::get_if<std::vector<std::string>>(
1713 &endpointsIt->second);
1714 if ((endpoints != nullptr) && !endpoints->empty())
1715 {
1716 // Store LED path in inventory item
1717 const std::string& ledPath = endpoints->front();
1718 inventoryItem.ledObjectPath = ledPath;
1719 }
1720 }
1721 }
1722 break;
1723 }
1724 }
1725 }
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001726 callback(inventoryItems);
1727 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations respHandler exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001728 };
1729
1730 // Find DBus object path that implements ObjectManager for ObjectMapper
1731 std::string connection = "xyz.openbmc_project.ObjectMapper";
1732 auto iter = objectMgrPaths->find(connection);
1733 const std::string& objectMgrPath =
1734 (iter != objectMgrPaths->end()) ? iter->second : "/";
1735 BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
1736 << objectMgrPath;
1737
1738 // Call GetManagedObjects on the ObjectMapper to get all associations
1739 crow::connections::systemBus->async_method_call(
1740 std::move(respHandler), connection, objectMgrPath,
1741 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1742
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001743 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001744}
1745
1746/**
Anthony Wilsond5005492019-07-31 16:34:17 -05001747 * @brief Gets D-Bus data for inventory item leds associated with sensors.
1748 *
1749 * Uses the specified connections (services) to obtain D-Bus data for inventory
1750 * item leds associated with sensors. Stores the resulting data in the
1751 * inventoryItems vector.
1752 *
1753 * This data is later used to provide sensor property values in the JSON
1754 * response.
1755 *
1756 * Finds the inventory item led data asynchronously. Invokes callback when data
1757 * has been obtained.
1758 *
1759 * The callback must have the following signature:
1760 * @code
1761 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
1762 * @endcode
1763 *
1764 * This function is called recursively, obtaining data asynchronously from one
1765 * connection in each call. This ensures the callback is not invoked until the
1766 * last asynchronous function has completed.
1767 *
1768 * @param sensorsAsyncResp Pointer to object holding response data.
1769 * @param inventoryItems D-Bus inventory items associated with sensors.
1770 * @param ledConnections Connections that provide data for the inventory leds.
1771 * @param callback Callback to invoke when inventory data has been obtained.
1772 * @param ledConnectionsIndex Current index in ledConnections. Only specified
1773 * in recursive calls to this function.
1774 */
1775template <typename Callback>
1776void getInventoryLedData(
1777 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1778 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1779 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1780 ledConnections,
1781 Callback&& callback, size_t ledConnectionsIndex = 0)
1782{
1783 BMCWEB_LOG_DEBUG << "getInventoryLedData enter";
1784
1785 // If no more connections left, call callback
1786 if (ledConnectionsIndex >= ledConnections->size())
1787 {
1788 callback(inventoryItems);
1789 BMCWEB_LOG_DEBUG << "getInventoryLedData exit";
1790 return;
1791 }
1792
1793 // Get inventory item data from current connection
1794 auto it = ledConnections->nth(ledConnectionsIndex);
1795 if (it != ledConnections->end())
1796 {
1797 const std::string& ledPath = (*it).first;
1798 const std::string& ledConnection = (*it).second;
1799 // Response handler for Get State property
1800 auto respHandler =
1801 [sensorsAsyncResp, inventoryItems, ledConnections, ledPath,
1802 callback{std::move(callback)},
1803 ledConnectionsIndex](const boost::system::error_code ec,
1804 const std::variant<std::string>& ledState) {
1805 BMCWEB_LOG_DEBUG << "getInventoryLedData respHandler enter";
1806 if (ec)
1807 {
1808 BMCWEB_LOG_ERROR
1809 << "getInventoryLedData respHandler DBus error " << ec;
1810 messages::internalError(sensorsAsyncResp->res);
1811 return;
1812 }
1813
1814 const std::string* state = std::get_if<std::string>(&ledState);
1815 if (state != nullptr)
1816 {
1817 BMCWEB_LOG_DEBUG << "Led state: " << *state;
1818 // Find inventory item with this LED object path
1819 InventoryItem* inventoryItem =
1820 findInventoryItemForLed(*inventoryItems, ledPath);
1821 if (inventoryItem != nullptr)
1822 {
1823 // Store LED state in InventoryItem
1824 if (boost::ends_with(*state, "On"))
1825 {
1826 inventoryItem->ledState = LedState::ON;
1827 }
1828 else if (boost::ends_with(*state, "Blink"))
1829 {
1830 inventoryItem->ledState = LedState::BLINK;
1831 }
1832 else if (boost::ends_with(*state, "Off"))
1833 {
1834 inventoryItem->ledState = LedState::OFF;
1835 }
1836 else
1837 {
1838 inventoryItem->ledState = LedState::UNKNOWN;
1839 }
1840 }
1841 }
1842 else
1843 {
1844 BMCWEB_LOG_DEBUG << "Failed to find State data for LED: "
1845 << ledPath;
1846 }
1847
1848 // Recurse to get LED data from next connection
1849 getInventoryLedData(sensorsAsyncResp, inventoryItems,
1850 ledConnections, std::move(callback),
1851 ledConnectionsIndex + 1);
1852
1853 BMCWEB_LOG_DEBUG << "getInventoryLedData respHandler exit";
1854 };
1855
1856 // Get the State property for the current LED
1857 crow::connections::systemBus->async_method_call(
1858 std::move(respHandler), ledConnection, ledPath,
1859 "org.freedesktop.DBus.Properties", "Get",
1860 "xyz.openbmc_project.Led.Physical", "State");
1861 }
1862
1863 BMCWEB_LOG_DEBUG << "getInventoryLedData exit";
1864}
1865
1866/**
1867 * @brief Gets LED data for LEDs associated with given inventory items.
1868 *
1869 * Gets the D-Bus connections (services) that provide LED data for the LEDs
1870 * associated with the specified inventory items. Then gets the LED data from
1871 * each connection and stores it in the inventory item.
1872 *
1873 * This data is later used to provide sensor property values in the JSON
1874 * response.
1875 *
1876 * Finds the LED data asynchronously. Invokes callback when information has
1877 * been obtained.
1878 *
1879 * The callback must have the following signature:
1880 * @code
1881 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
1882 * @endcode
1883 *
1884 * @param sensorsAsyncResp Pointer to object holding response data.
1885 * @param inventoryItems D-Bus inventory items associated with sensors.
1886 * @param callback Callback to invoke when inventory items have been obtained.
1887 */
1888template <typename Callback>
1889void getInventoryLeds(
1890 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1891 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1892 Callback&& callback)
1893{
1894 BMCWEB_LOG_DEBUG << "getInventoryLeds enter";
1895
1896 const std::string path = "/xyz/openbmc_project";
1897 const std::array<std::string, 1> interfaces = {
1898 "xyz.openbmc_project.Led.Physical"};
1899
1900 // Response handler for parsing output from GetSubTree
1901 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
1902 inventoryItems](const boost::system::error_code ec,
1903 const GetSubTreeType& subtree) {
1904 BMCWEB_LOG_DEBUG << "getInventoryLeds respHandler enter";
1905 if (ec)
1906 {
1907 messages::internalError(sensorsAsyncResp->res);
1908 BMCWEB_LOG_ERROR << "getInventoryLeds respHandler DBus error "
1909 << ec;
1910 return;
1911 }
1912
1913 // Build map of LED object paths to connections
1914 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1915 ledConnections = std::make_shared<
1916 boost::container::flat_map<std::string, std::string>>();
1917
1918 // Loop through objects from GetSubTree
1919 for (const std::pair<
1920 std::string,
1921 std::vector<std::pair<std::string, std::vector<std::string>>>>&
1922 object : subtree)
1923 {
1924 // Check if object path is LED for one of the specified inventory
1925 // items
1926 const std::string& ledPath = object.first;
1927 if (findInventoryItemForLed(*inventoryItems, ledPath) != nullptr)
1928 {
1929 // Add mapping from ledPath to connection
1930 const std::string& connection = object.second.begin()->first;
1931 (*ledConnections)[ledPath] = connection;
1932 BMCWEB_LOG_DEBUG << "Added mapping " << ledPath << " -> "
1933 << connection;
1934 }
1935 }
1936
1937 getInventoryLedData(sensorsAsyncResp, inventoryItems, ledConnections,
1938 std::move(callback));
1939 BMCWEB_LOG_DEBUG << "getInventoryLeds respHandler exit";
1940 };
1941 // Make call to ObjectMapper to find all inventory items
1942 crow::connections::systemBus->async_method_call(
1943 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
1944 "/xyz/openbmc_project/object_mapper",
1945 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 0, interfaces);
1946 BMCWEB_LOG_DEBUG << "getInventoryLeds exit";
1947}
1948
1949/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001950 * @brief Gets inventory items associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001951 *
1952 * Finds the inventory items that are associated with the specified sensors.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001953 * Then gets D-Bus data for the inventory items, such as presence and VPD.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001954 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001955 * This data is later used to provide sensor property values in the JSON
1956 * response.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001957 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001958 * Finds the inventory items asynchronously. Invokes callback when the
1959 * inventory items have been obtained.
1960 *
1961 * The callback must have the following signature:
1962 * @code
1963 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
1964 * @endcode
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001965 *
1966 * @param sensorsAsyncResp Pointer to object holding response data.
1967 * @param sensorNames All sensors within the current chassis.
1968 * @param objectMgrPaths Mappings from connection name to DBus object path that
1969 * implements ObjectManager.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001970 * @param callback Callback to invoke when inventory items have been obtained.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001971 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001972template <typename Callback>
1973static void getInventoryItems(
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001974 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1975 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
1976 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001977 objectMgrPaths,
1978 Callback&& callback)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001979{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001980 BMCWEB_LOG_DEBUG << "getInventoryItems enter";
1981 auto getInventoryItemAssociationsCb =
1982 [sensorsAsyncResp, objectMgrPaths, callback{std::move(callback)}](
1983 std::shared_ptr<std::vector<InventoryItem>> inventoryItems) {
1984 BMCWEB_LOG_DEBUG << "getInventoryItemAssociationsCb enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001985 auto getInventoryItemsConnectionsCb =
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001986 [sensorsAsyncResp, inventoryItems, objectMgrPaths,
1987 callback{std::move(callback)}](
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001988 std::shared_ptr<boost::container::flat_set<std::string>>
1989 invConnections) {
1990 BMCWEB_LOG_DEBUG << "getInventoryItemsConnectionsCb enter";
Anthony Wilsond5005492019-07-31 16:34:17 -05001991 auto getInventoryItemsDataCb =
1992 [sensorsAsyncResp, inventoryItems,
1993 callback{std::move(callback)}]() {
1994 BMCWEB_LOG_DEBUG << "getInventoryItemsDataCb enter";
1995 // Find led connections and get the data
1996 getInventoryLeds(sensorsAsyncResp, inventoryItems,
1997 std::move(callback));
1998 BMCWEB_LOG_DEBUG << "getInventoryItemsDataCb exit";
1999 };
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002000
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002001 // Get inventory item data from connections
2002 getInventoryItemsData(sensorsAsyncResp, inventoryItems,
2003 invConnections, objectMgrPaths,
Anthony Wilsond5005492019-07-31 16:34:17 -05002004 std::move(getInventoryItemsDataCb));
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002005 BMCWEB_LOG_DEBUG << "getInventoryItemsConnectionsCb exit";
2006 };
2007
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002008 // Get connections that provide inventory item data
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002009 getInventoryItemsConnections(
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002010 sensorsAsyncResp, inventoryItems,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002011 std::move(getInventoryItemsConnectionsCb));
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002012 BMCWEB_LOG_DEBUG << "getInventoryItemAssociationsCb exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002013 };
2014
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002015 // Get associations from sensors to inventory items
2016 getInventoryItemAssociations(sensorsAsyncResp, sensorNames, objectMgrPaths,
2017 std::move(getInventoryItemAssociationsCb));
2018 BMCWEB_LOG_DEBUG << "getInventoryItems exit";
2019}
2020
2021/**
2022 * @brief Returns JSON PowerSupply object for the specified inventory item.
2023 *
2024 * Searches for a JSON PowerSupply object that matches the specified inventory
2025 * item. If one is not found, a new PowerSupply object is added to the JSON
2026 * array.
2027 *
2028 * Multiple sensors are often associated with one power supply inventory item.
2029 * As a result, multiple sensor values are stored in one JSON PowerSupply
2030 * object.
2031 *
2032 * @param powerSupplyArray JSON array containing Redfish PowerSupply objects.
2033 * @param inventoryItem Inventory item for the power supply.
2034 * @param chassisId Chassis that contains the power supply.
2035 * @return JSON PowerSupply object for the specified inventory item.
2036 */
2037static nlohmann::json& getPowerSupply(nlohmann::json& powerSupplyArray,
2038 const InventoryItem& inventoryItem,
2039 const std::string& chassisId)
2040{
2041 // Check if matching PowerSupply object already exists in JSON array
2042 for (nlohmann::json& powerSupply : powerSupplyArray)
2043 {
2044 if (powerSupply["MemberId"] == inventoryItem.name)
2045 {
2046 return powerSupply;
2047 }
2048 }
2049
2050 // Add new PowerSupply object to JSON array
2051 powerSupplyArray.push_back({});
2052 nlohmann::json& powerSupply = powerSupplyArray.back();
2053 powerSupply["@odata.id"] =
2054 "/redfish/v1/Chassis/" + chassisId + "/Power#/PowerSupplies/";
2055 powerSupply["MemberId"] = inventoryItem.name;
2056 powerSupply["Name"] = boost::replace_all_copy(inventoryItem.name, "_", " ");
2057 powerSupply["Manufacturer"] = inventoryItem.manufacturer;
2058 powerSupply["Model"] = inventoryItem.model;
2059 powerSupply["PartNumber"] = inventoryItem.partNumber;
2060 powerSupply["SerialNumber"] = inventoryItem.serialNumber;
Anthony Wilsond5005492019-07-31 16:34:17 -05002061 setLedState(powerSupply, &inventoryItem);
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002062 powerSupply["Status"]["State"] = getState(&inventoryItem);
2063
2064 const char* health = inventoryItem.isFunctional ? "OK" : "Critical";
2065 powerSupply["Status"]["Health"] = health;
2066
2067 return powerSupply;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002068}
2069
2070/**
Shawn McCarneyde629b62019-03-08 10:42:51 -06002071 * @brief Gets the values of the specified sensors.
2072 *
2073 * Stores the results as JSON in the SensorsAsyncResp.
2074 *
2075 * Gets the sensor values asynchronously. Stores the results later when the
2076 * information has been obtained.
2077 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002078 * The sensorNames set contains all requested sensors for the current chassis.
Shawn McCarneyde629b62019-03-08 10:42:51 -06002079 *
2080 * To minimize the number of DBus calls, the DBus method
2081 * org.freedesktop.DBus.ObjectManager.GetManagedObjects() is used to get the
2082 * values of all sensors provided by a connection (service).
2083 *
2084 * The connections set contains all the connections that provide sensor values.
2085 *
2086 * The objectMgrPaths map contains mappings from a connection name to the
2087 * corresponding DBus object path that implements ObjectManager.
2088 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002089 * The InventoryItem vector contains D-Bus inventory items associated with the
2090 * sensors. Inventory item data is needed for some Redfish sensor properties.
2091 *
Shawn McCarneyde629b62019-03-08 10:42:51 -06002092 * @param SensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002093 * @param sensorNames All requested sensors within the current chassis.
Shawn McCarneyde629b62019-03-08 10:42:51 -06002094 * @param connections Connections that provide sensor values.
2095 * @param objectMgrPaths Mappings from connection name to DBus object path that
2096 * implements ObjectManager.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002097 * @param inventoryItems Inventory items associated with the sensors.
Shawn McCarneyde629b62019-03-08 10:42:51 -06002098 */
2099void getSensorData(
2100 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002101 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
Shawn McCarneyde629b62019-03-08 10:42:51 -06002102 const boost::container::flat_set<std::string>& connections,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002103 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002104 objectMgrPaths,
2105 std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
Shawn McCarneyde629b62019-03-08 10:42:51 -06002106{
2107 BMCWEB_LOG_DEBUG << "getSensorData enter";
2108 // Get managed objects from all services exposing sensors
2109 for (const std::string& connection : connections)
2110 {
2111 // Response handler to process managed objects
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002112 auto getManagedObjectsCb = [SensorsAsyncResp, sensorNames,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002113 inventoryItems](
Shawn McCarneyde629b62019-03-08 10:42:51 -06002114 const boost::system::error_code ec,
2115 ManagedObjectsVectorType& resp) {
2116 BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter";
2117 if (ec)
2118 {
2119 BMCWEB_LOG_ERROR << "getManagedObjectsCb DBUS error: " << ec;
2120 messages::internalError(SensorsAsyncResp->res);
2121 return;
2122 }
2123 // Go through all objects and update response with sensor data
2124 for (const auto& objDictEntry : resp)
2125 {
2126 const std::string& objPath =
2127 static_cast<const std::string&>(objDictEntry.first);
2128 BMCWEB_LOG_DEBUG << "getManagedObjectsCb parsing object "
2129 << objPath;
2130
Shawn McCarneyde629b62019-03-08 10:42:51 -06002131 std::vector<std::string> split;
2132 // Reserve space for
2133 // /xyz/openbmc_project/sensors/<name>/<subname>
2134 split.reserve(6);
2135 boost::algorithm::split(split, objPath, boost::is_any_of("/"));
2136 if (split.size() < 6)
2137 {
2138 BMCWEB_LOG_ERROR << "Got path that isn't long enough "
2139 << objPath;
2140 continue;
2141 }
2142 // These indexes aren't intuitive, as boost::split puts an empty
2143 // string at the beginning
2144 const std::string& sensorType = split[4];
2145 const std::string& sensorName = split[5];
2146 BMCWEB_LOG_DEBUG << "sensorName " << sensorName
2147 << " sensorType " << sensorType;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002148 if (sensorNames->find(objPath) == sensorNames->end())
Shawn McCarneyde629b62019-03-08 10:42:51 -06002149 {
2150 BMCWEB_LOG_ERROR << sensorName << " not in sensor list ";
2151 continue;
2152 }
2153
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002154 // Find inventory item (if any) associated with sensor
2155 InventoryItem* inventoryItem =
2156 findInventoryItemForSensor(inventoryItems, objPath);
2157
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002158 const std::string& sensorSchema =
2159 SensorsAsyncResp->chassisSubNode;
2160
2161 nlohmann::json* sensorJson = nullptr;
2162
2163 if (sensorSchema == "Sensors")
Shawn McCarneyde629b62019-03-08 10:42:51 -06002164 {
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002165 SensorsAsyncResp->res.jsonValue["@odata.id"] =
2166 "/redfish/v1/Chassis/" + SensorsAsyncResp->chassisId +
2167 "/" + SensorsAsyncResp->chassisSubNode + "/" +
2168 sensorName;
2169 sensorJson = &(SensorsAsyncResp->res.jsonValue);
Shawn McCarneyde629b62019-03-08 10:42:51 -06002170 }
2171 else
2172 {
Ed Tanous271584a2019-07-09 16:24:22 -07002173 std::string fieldName;
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002174 if (sensorType == "temperature")
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002175 {
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002176 fieldName = "Temperatures";
2177 }
2178 else if (sensorType == "fan" || sensorType == "fan_tach" ||
2179 sensorType == "fan_pwm")
2180 {
2181 fieldName = "Fans";
2182 }
2183 else if (sensorType == "voltage")
2184 {
2185 fieldName = "Voltages";
2186 }
2187 else if (sensorType == "power")
2188 {
2189 if (!sensorName.compare("total_power"))
2190 {
2191 fieldName = "PowerControl";
2192 }
2193 else if ((inventoryItem != nullptr) &&
2194 (inventoryItem->isPowerSupply))
2195 {
2196 fieldName = "PowerSupplies";
2197 }
2198 else
2199 {
2200 // Other power sensors are in SensorCollection
2201 continue;
2202 }
2203 }
2204 else
2205 {
2206 BMCWEB_LOG_ERROR << "Unsure how to handle sensorType "
2207 << sensorType;
2208 continue;
2209 }
2210
2211 nlohmann::json& tempArray =
2212 SensorsAsyncResp->res.jsonValue[fieldName];
2213 if (fieldName == "PowerControl")
2214 {
2215 if (tempArray.empty())
2216 {
2217 // Put multiple "sensors" into a single
2218 // PowerControl. Follows MemberId naming and
2219 // naming in power.hpp.
2220 tempArray.push_back(
2221 {{"@odata.id",
2222 "/redfish/v1/Chassis/" +
2223 SensorsAsyncResp->chassisId + "/" +
2224 SensorsAsyncResp->chassisSubNode + "#/" +
2225 fieldName + "/0"}});
2226 }
2227 sensorJson = &(tempArray.back());
2228 }
2229 else if (fieldName == "PowerSupplies")
2230 {
2231 if (inventoryItem != nullptr)
2232 {
2233 sensorJson =
2234 &(getPowerSupply(tempArray, *inventoryItem,
2235 SensorsAsyncResp->chassisId));
2236 }
2237 }
2238 else
2239 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002240 tempArray.push_back(
2241 {{"@odata.id",
2242 "/redfish/v1/Chassis/" +
2243 SensorsAsyncResp->chassisId + "/" +
2244 SensorsAsyncResp->chassisSubNode + "#/" +
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002245 fieldName + "/"}});
2246 sensorJson = &(tempArray.back());
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002247 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002248 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06002249
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002250 if (sensorJson != nullptr)
2251 {
2252 objectInterfacesToJson(sensorName, sensorType,
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002253 SensorsAsyncResp->chassisSubNode,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002254 objDictEntry.second, *sensorJson,
2255 inventoryItem);
2256 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06002257 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002258 if (SensorsAsyncResp.use_count() == 1)
James Feist8bd25cc2019-03-15 15:14:00 -07002259 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002260 sortJSONResponse(SensorsAsyncResp);
2261 if (SensorsAsyncResp->chassisSubNode == "Thermal")
2262 {
2263 populateFanRedundancy(SensorsAsyncResp);
2264 }
James Feist8bd25cc2019-03-15 15:14:00 -07002265 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06002266 BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit";
2267 };
2268
2269 // Find DBus object path that implements ObjectManager for the current
2270 // connection. If no mapping found, default to "/".
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002271 auto iter = objectMgrPaths->find(connection);
Shawn McCarneyde629b62019-03-08 10:42:51 -06002272 const std::string& objectMgrPath =
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002273 (iter != objectMgrPaths->end()) ? iter->second : "/";
Shawn McCarneyde629b62019-03-08 10:42:51 -06002274 BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
2275 << objectMgrPath;
2276
2277 crow::connections::systemBus->async_method_call(
2278 getManagedObjectsCb, connection, objectMgrPath,
2279 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
2280 };
2281 BMCWEB_LOG_DEBUG << "getSensorData exit";
2282}
2283
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002284void processSensorList(
2285 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
2286 std::shared_ptr<boost::container::flat_set<std::string>> sensorNames)
2287{
2288 auto getConnectionCb =
2289 [SensorsAsyncResp, sensorNames](
2290 const boost::container::flat_set<std::string>& connections) {
2291 BMCWEB_LOG_DEBUG << "getConnectionCb enter";
2292 auto getObjectManagerPathsCb =
2293 [SensorsAsyncResp, sensorNames, connections](
2294 std::shared_ptr<
2295 boost::container::flat_map<std::string, std::string>>
2296 objectMgrPaths) {
2297 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb enter";
2298 auto getInventoryItemsCb =
2299 [SensorsAsyncResp, sensorNames, connections,
2300 objectMgrPaths](
2301 std::shared_ptr<std::vector<InventoryItem>>
2302 inventoryItems) {
2303 BMCWEB_LOG_DEBUG << "getInventoryItemsCb enter";
2304 // Get sensor data and store results in JSON
2305 getSensorData(SensorsAsyncResp, sensorNames,
2306 connections, objectMgrPaths,
2307 inventoryItems);
2308 BMCWEB_LOG_DEBUG << "getInventoryItemsCb exit";
2309 };
2310
2311 // Get inventory items associated with sensors
2312 getInventoryItems(SensorsAsyncResp, sensorNames,
2313 objectMgrPaths,
2314 std::move(getInventoryItemsCb));
2315
2316 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb exit";
2317 };
2318
2319 // Get mapping from connection names to the DBus object
2320 // paths that implement the ObjectManager interface
2321 getObjectManagerPaths(SensorsAsyncResp,
2322 std::move(getObjectManagerPathsCb));
2323 BMCWEB_LOG_DEBUG << "getConnectionCb exit";
2324 };
2325
2326 // Get set of connections that provide sensor values
2327 getConnections(SensorsAsyncResp, sensorNames, std::move(getConnectionCb));
2328}
2329
Shawn McCarneyde629b62019-03-08 10:42:51 -06002330/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01002331 * @brief Entry point for retrieving sensors data related to requested
2332 * chassis.
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02002333 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01002334 */
Ed Tanous1abe55e2018-09-05 08:30:59 -07002335void getChassisData(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
2336{
2337 BMCWEB_LOG_DEBUG << "getChassisData enter";
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002338 auto getChassisCb =
2339 [SensorsAsyncResp](
2340 std::shared_ptr<boost::container::flat_set<std::string>>
2341 sensorNames) {
2342 BMCWEB_LOG_DEBUG << "getChassisCb enter";
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002343 processSensorList(SensorsAsyncResp, sensorNames);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002344 BMCWEB_LOG_DEBUG << "getChassisCb exit";
2345 };
Jennifer Lee4f9a2132019-03-04 12:45:19 -08002346 SensorsAsyncResp->res.jsonValue["Redundancy"] = nlohmann::json::array();
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01002347
Shawn McCarney26f03892019-05-03 13:20:24 -05002348 // Get set of sensors in chassis
Ed Tanous1abe55e2018-09-05 08:30:59 -07002349 getChassis(SensorsAsyncResp, std::move(getChassisCb));
2350 BMCWEB_LOG_DEBUG << "getChassisData exit";
Ed Tanous271584a2019-07-09 16:24:22 -07002351}
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01002352
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302353/**
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002354 * @brief Find the requested sensorName in the list of all sensors supplied by
2355 * the chassis node
2356 *
2357 * @param sensorName The sensor name supplied in the PATCH request
2358 * @param sensorsList The list of sensors managed by the chassis node
2359 * @param sensorsModified The list of sensors that were found as a result of
2360 * repeated calls to this function
2361 */
2362bool findSensorNameUsingSensorPath(
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05302363 std::string_view sensorName,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002364 boost::container::flat_set<std::string>& sensorsList,
2365 boost::container::flat_set<std::string>& sensorsModified)
2366{
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05302367 for (std::string_view chassisSensor : sensorsList)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002368 {
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05302369 std::size_t pos = chassisSensor.rfind("/");
2370 if (pos >= (chassisSensor.size() - 1))
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002371 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002372 continue;
2373 }
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05302374 std::string_view thisSensorName = chassisSensor.substr(pos + 1);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002375 if (thisSensorName == sensorName)
2376 {
2377 sensorsModified.emplace(chassisSensor);
2378 return true;
2379 }
2380 }
2381 return false;
2382}
2383
2384/**
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302385 * @brief Entry point for overriding sensor values of given sensor
2386 *
2387 * @param res response object
Carol Wang4bb3dc32019-10-17 18:15:02 +08002388 * @param allCollections Collections extract from sensors' request patch info
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302389 * @param typeList TypeList of sensors for the resource queried
2390 * @param chassisSubNode Chassis Node for which the query has to happen
2391 */
Carol Wang4bb3dc32019-10-17 18:15:02 +08002392void setSensorOverride(
2393 std::shared_ptr<SensorsAsyncResp> sensorAsyncResp,
2394 std::unordered_map<std::string, std::vector<nlohmann::json>>&
2395 allCollections,
2396 const std::string& chassisName, const std::vector<const char*> typeList)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302397{
Carol Wang4bb3dc32019-10-17 18:15:02 +08002398 BMCWEB_LOG_INFO << "setSensorOverride for subNode"
2399 << sensorAsyncResp->chassisSubNode << "\n";
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302400
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302401 const char* propertyValueName;
2402 std::unordered_map<std::string, std::pair<double, std::string>> overrideMap;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302403 std::string memberId;
2404 double value;
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302405 for (auto& collectionItems : allCollections)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302406 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302407 if (collectionItems.first == "Temperatures")
2408 {
2409 propertyValueName = "ReadingCelsius";
2410 }
2411 else if (collectionItems.first == "Fans")
2412 {
2413 propertyValueName = "Reading";
2414 }
2415 else
2416 {
2417 propertyValueName = "ReadingVolts";
2418 }
2419 for (auto& item : collectionItems.second)
2420 {
Carol Wang4bb3dc32019-10-17 18:15:02 +08002421 if (!json_util::readJson(item, sensorAsyncResp->res, "MemberId",
2422 memberId, propertyValueName, value))
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302423 {
2424 return;
2425 }
2426 overrideMap.emplace(memberId,
2427 std::make_pair(value, collectionItems.first));
2428 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302429 }
Carol Wang4bb3dc32019-10-17 18:15:02 +08002430
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002431 auto getChassisSensorListCb = [sensorAsyncResp,
2432 overrideMap](const std::shared_ptr<
2433 boost::container::flat_set<
2434 std::string>>
2435 sensorsList) {
2436 // Match sensor names in the PATCH request to those managed by the
2437 // chassis node
2438 const std::shared_ptr<boost::container::flat_set<std::string>>
2439 sensorNames =
2440 std::make_shared<boost::container::flat_set<std::string>>();
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302441 for (const auto& item : overrideMap)
2442 {
2443 const auto& sensor = item.first;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002444 if (!findSensorNameUsingSensorPath(sensor, *sensorsList,
2445 *sensorNames))
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302446 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302447 BMCWEB_LOG_INFO << "Unable to find memberId " << item.first;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302448 messages::resourceNotFound(sensorAsyncResp->res,
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302449 item.second.second, item.first);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302450 return;
2451 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302452 }
2453 // Get the connection to which the memberId belongs
2454 auto getObjectsWithConnectionCb =
2455 [sensorAsyncResp, overrideMap](
2456 const boost::container::flat_set<std::string>& connections,
2457 const std::set<std::pair<std::string, std::string>>&
2458 objectsWithConnection) {
2459 if (objectsWithConnection.size() != overrideMap.size())
2460 {
2461 BMCWEB_LOG_INFO
2462 << "Unable to find all objects with proper connection "
2463 << objectsWithConnection.size() << " requested "
2464 << overrideMap.size() << "\n";
2465 messages::resourceNotFound(
2466 sensorAsyncResp->res,
2467 sensorAsyncResp->chassisSubNode == "Thermal"
2468 ? "Temperatures"
2469 : "Voltages",
2470 "Count");
2471 return;
2472 }
2473 for (const auto& item : objectsWithConnection)
2474 {
2475
2476 auto lastPos = item.first.rfind('/');
2477 if (lastPos == std::string::npos)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302478 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302479 messages::internalError(sensorAsyncResp->res);
2480 return;
2481 }
2482 std::string sensorName = item.first.substr(lastPos + 1);
2483
2484 const auto& iterator = overrideMap.find(sensorName);
2485 if (iterator == overrideMap.end())
2486 {
2487 BMCWEB_LOG_INFO << "Unable to find sensor object"
2488 << item.first << "\n";
2489 messages::internalError(sensorAsyncResp->res);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302490 return;
2491 }
2492 crow::connections::systemBus->async_method_call(
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302493 [sensorAsyncResp](const boost::system::error_code ec) {
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302494 if (ec)
2495 {
2496 BMCWEB_LOG_DEBUG
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302497 << "setOverrideValueStatus DBUS error: "
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302498 << ec;
2499 messages::internalError(sensorAsyncResp->res);
2500 return;
2501 }
2502 },
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302503 item.second, item.first,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302504 "org.freedesktop.DBus.Properties", "Set",
2505 "xyz.openbmc_project.Sensor.Value", "Value",
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302506 sdbusplus::message::variant<double>(
2507 iterator->second.first));
2508 }
2509 };
2510 // Get object with connection for the given sensor name
2511 getObjectsWithConnection(sensorAsyncResp, sensorNames,
2512 std::move(getObjectsWithConnectionCb));
2513 };
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302514 // get full sensor list for the given chassisId and cross verify the sensor.
2515 getChassis(sensorAsyncResp, std::move(getChassisSensorListCb));
2516}
2517
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002518class SensorCollection : public Node
2519{
2520 public:
2521 SensorCollection(CrowApp& app) :
2522 Node(app, "/redfish/v1/Chassis/<str>/Sensors", std::string())
2523 {
2524 entityPrivileges = {
2525 {boost::beast::http::verb::get, {{"Login"}}},
2526 {boost::beast::http::verb::head, {{"Login"}}},
2527 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2528 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2529 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2530 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2531 }
2532
2533 private:
2534 std::vector<const char*> typeList = {
2535 "/xyz/openbmc_project/sensors/power",
2536 "/xyz/openbmc_project/sensors/current"};
2537 void doGet(crow::Response& res, const crow::Request& req,
2538 const std::vector<std::string>& params) override
2539 {
2540 BMCWEB_LOG_DEBUG << "SensorCollection doGet enter";
2541 if (params.size() != 1)
2542 {
2543 BMCWEB_LOG_DEBUG << "SensorCollection doGet param size < 1";
2544 messages::internalError(res);
2545 res.end();
2546 return;
2547 }
2548
2549 const std::string& chassisId = params[0];
2550 std::shared_ptr<SensorsAsyncResp> asyncResp =
2551 std::make_shared<SensorsAsyncResp>(res, chassisId, typeList,
2552 "Sensors");
2553
2554 auto getChassisCb =
2555 [asyncResp](std::shared_ptr<boost::container::flat_set<std::string>>
2556 sensorNames) {
2557 BMCWEB_LOG_DEBUG << "getChassisCb enter";
2558
2559 nlohmann::json& entriesArray =
2560 asyncResp->res.jsonValue["Members"];
2561 for (auto& sensor : *sensorNames)
2562 {
2563 BMCWEB_LOG_DEBUG << "Adding sensor: " << sensor;
2564
2565 std::size_t lastPos = sensor.rfind("/");
2566 if (lastPos == std::string::npos ||
2567 lastPos + 1 >= sensor.size())
2568 {
2569 BMCWEB_LOG_ERROR << "Invalid sensor path: " << sensor;
2570 messages::internalError(asyncResp->res);
2571 return;
2572 }
2573 std::string sensorName = sensor.substr(lastPos + 1);
2574 entriesArray.push_back(
2575 {{"@odata.id",
2576 "/redfish/v1/Chassis/" + asyncResp->chassisId + "/" +
2577 asyncResp->chassisSubNode + "/" + sensorName}});
2578 }
2579
2580 asyncResp->res.jsonValue["Members@odata.count"] =
2581 entriesArray.size();
2582 BMCWEB_LOG_DEBUG << "getChassisCb exit";
2583 };
2584
2585 // Get set of sensors in chassis
2586 getChassis(asyncResp, std::move(getChassisCb));
2587 BMCWEB_LOG_DEBUG << "SensorCollection doGet exit";
2588 }
2589};
2590
2591class Sensor : public Node
2592{
2593 public:
2594 Sensor(CrowApp& app) :
2595 Node(app, "/redfish/v1/Chassis/<str>/Sensors/<str>/", std::string(),
2596 std::string())
2597 {
2598 entityPrivileges = {
2599 {boost::beast::http::verb::get, {{"Login"}}},
2600 {boost::beast::http::verb::head, {{"Login"}}},
2601 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2602 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2603 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2604 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2605 }
2606
2607 private:
2608 void doGet(crow::Response& res, const crow::Request& req,
2609 const std::vector<std::string>& params) override
2610 {
2611 BMCWEB_LOG_DEBUG << "Sensor doGet enter";
2612 if (params.size() != 2)
2613 {
2614 BMCWEB_LOG_DEBUG << "Sensor doGet param size < 2";
2615 messages::internalError(res);
2616 res.end();
2617 return;
2618 }
2619 const std::string& chassisId = params[0];
2620 std::shared_ptr<SensorsAsyncResp> asyncResp =
2621 std::make_shared<SensorsAsyncResp>(
2622 res, chassisId, std::vector<const char*>(), "Sensors");
2623
2624 const std::string& sensorName = params[1];
2625 const std::array<const char*, 1> interfaces = {
2626 "xyz.openbmc_project.Sensor.Value"};
2627
2628 // Get a list of all of the sensors that implement Sensor.Value
2629 // and get the path and service name associated with the sensor
2630 crow::connections::systemBus->async_method_call(
2631 [asyncResp, sensorName](const boost::system::error_code ec,
2632 const GetSubTreeType& subtree) {
2633 BMCWEB_LOG_DEBUG << "respHandler1 enter";
2634 if (ec)
2635 {
2636 messages::internalError(asyncResp->res);
2637 BMCWEB_LOG_ERROR << "Sensor getSensorPaths resp_handler: "
2638 << "Dbus error " << ec;
2639 return;
2640 }
2641
2642 GetSubTreeType::const_iterator it = std::find_if(
2643 subtree.begin(), subtree.end(),
2644 [sensorName](
2645 const std::pair<
2646 std::string,
2647 std::vector<std::pair<std::string,
2648 std::vector<std::string>>>>&
2649 object) {
2650 std::string_view sensor = object.first;
2651 std::size_t lastPos = sensor.rfind("/");
2652 if (lastPos == std::string::npos ||
2653 lastPos + 1 >= sensor.size())
2654 {
2655 BMCWEB_LOG_ERROR << "Invalid sensor path: "
2656 << sensor;
2657 return false;
2658 }
2659 std::string_view name = sensor.substr(lastPos + 1);
2660
2661 return name == sensorName;
2662 });
2663
2664 if (it == subtree.end())
2665 {
2666 BMCWEB_LOG_ERROR << "Could not find path for sensor: "
2667 << sensorName;
2668 messages::resourceNotFound(asyncResp->res, "Sensor",
2669 sensorName);
2670 return;
2671 }
2672 std::string_view sensorPath = (*it).first;
2673 BMCWEB_LOG_DEBUG << "Found sensor path for sensor '"
2674 << sensorName << "': " << sensorPath;
2675
2676 const std::shared_ptr<boost::container::flat_set<std::string>>
2677 sensorList = std::make_shared<
2678 boost::container::flat_set<std::string>>();
2679
2680 sensorList->emplace(sensorPath);
2681 processSensorList(asyncResp, sensorList);
2682 BMCWEB_LOG_DEBUG << "respHandler1 exit";
2683 },
2684 "xyz.openbmc_project.ObjectMapper",
2685 "/xyz/openbmc_project/object_mapper",
2686 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
2687 "/xyz/openbmc_project/sensors", 2, interfaces);
2688 }
2689};
2690
Ed Tanous1abe55e2018-09-05 08:30:59 -07002691} // namespace redfish