blob: c900863f136d492ce760c513bb7a272010e979fa [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/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100265 * @brief Retrieves requested chassis sensors and redundancy data from DBus .
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200266 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100267 * @param callback Callback for next step in gathered sensor processing
268 */
269template <typename Callback>
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700270void getChassis(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700271 Callback&& callback)
272{
273 BMCWEB_LOG_DEBUG << "getChassis enter";
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500274 const std::array<const char*, 2> interfaces = {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700275 "xyz.openbmc_project.Inventory.Item.Board",
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500276 "xyz.openbmc_project.Inventory.Item.Chassis"};
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700277 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp](
278 const boost::system::error_code ec,
279 const std::vector<std::string>& chassisPaths) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700280 BMCWEB_LOG_DEBUG << "getChassis respHandler enter";
281 if (ec)
282 {
283 BMCWEB_LOG_ERROR << "getChassis respHandler DBUS error: " << ec;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700284 messages::internalError(sensorsAsyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700285 return;
286 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100287
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700288 const std::string* chassisPath = nullptr;
289 std::string chassisName;
290 for (const std::string& chassis : chassisPaths)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700291 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700292 std::size_t lastPos = chassis.rfind("/");
293 if (lastPos == std::string::npos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700294 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700295 BMCWEB_LOG_ERROR << "Failed to find '/' in " << chassis;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700296 continue;
297 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700298 chassisName = chassis.substr(lastPos + 1);
299 if (chassisName == sensorsAsyncResp->chassisId)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700300 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700301 chassisPath = &chassis;
302 break;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700303 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700304 }
305 if (chassisPath == nullptr)
306 {
307 messages::resourceNotFound(sensorsAsyncResp->res, "Chassis",
308 sensorsAsyncResp->chassisId);
309 return;
310 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700311
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700312 const std::string& chassisSubNode = sensorsAsyncResp->chassisSubNode;
313 if (chassisSubNode == "Power")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700314 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700315 sensorsAsyncResp->res.jsonValue["@odata.type"] =
316 "#Power.v1_5_2.Power";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700317 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700318 else if (chassisSubNode == "Thermal")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700319 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700320 sensorsAsyncResp->res.jsonValue["@odata.type"] =
321 "#Thermal.v1_4_0.Thermal";
Jennifer Lee4f9a2132019-03-04 12:45:19 -0800322 sensorsAsyncResp->res.jsonValue["Fans"] = nlohmann::json::array();
323 sensorsAsyncResp->res.jsonValue["Temperatures"] =
324 nlohmann::json::array();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700325 }
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500326 else if (chassisSubNode == "Sensors")
327 {
328 sensorsAsyncResp->res.jsonValue["@odata.type"] =
329 "#SensorCollection.SensorCollection";
330 sensorsAsyncResp->res.jsonValue["@odata.context"] =
331 "/redfish/v1/$metadata#SensorCollection.SensorCollection";
332 sensorsAsyncResp->res.jsonValue["Description"] =
333 "Collection of Sensors for this Chassis";
334 sensorsAsyncResp->res.jsonValue["Members"] =
335 nlohmann::json::array();
336 sensorsAsyncResp->res.jsonValue["Members@odata.count"] = 0;
337 }
338
339 if (chassisSubNode != "Sensors")
340 {
341 sensorsAsyncResp->res.jsonValue["Id"] = chassisSubNode;
342 sensorsAsyncResp->res.jsonValue["@odata.context"] =
343 "/redfish/v1/$metadata#" + chassisSubNode + "." +
344 chassisSubNode;
345 }
346
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700347 sensorsAsyncResp->res.jsonValue["@odata.id"] =
348 "/redfish/v1/Chassis/" + sensorsAsyncResp->chassisId + "/" +
349 chassisSubNode;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700350 sensorsAsyncResp->res.jsonValue["Name"] = chassisSubNode;
351
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500352 // Get the list of all sensors for this Chassis element
353 std::string sensorPath = *chassisPath + "/all_sensors";
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700354 crow::connections::systemBus->async_method_call(
355 [sensorsAsyncResp, callback{std::move(callback)}](
Ed Tanous271584a2019-07-09 16:24:22 -0700356 const boost::system::error_code& e,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700357 const std::variant<std::vector<std::string>>&
358 variantEndpoints) {
Ed Tanous271584a2019-07-09 16:24:22 -0700359 if (e)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700360 {
Ed Tanous271584a2019-07-09 16:24:22 -0700361 if (e.value() != EBADR)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700362 {
363 messages::internalError(sensorsAsyncResp->res);
364 return;
365 }
366 }
367 const std::vector<std::string>* nodeSensorList =
368 std::get_if<std::vector<std::string>>(&(variantEndpoints));
369 if (nodeSensorList == nullptr)
370 {
371 messages::resourceNotFound(
372 sensorsAsyncResp->res, sensorsAsyncResp->chassisSubNode,
373 sensorsAsyncResp->chassisSubNode == "Thermal"
374 ? "Temperatures"
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500375 : sensorsAsyncResp->chassisSubNode == "Power"
376 ? "Voltages"
377 : "Sensors");
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700378 return;
379 }
380 const std::shared_ptr<boost::container::flat_set<std::string>>
381 culledSensorList = std::make_shared<
382 boost::container::flat_set<std::string>>();
383 reduceSensorList(sensorsAsyncResp, nodeSensorList,
384 culledSensorList);
385 callback(culledSensorList);
386 },
387 "xyz.openbmc_project.ObjectMapper", sensorPath,
388 "org.freedesktop.DBus.Properties", "Get",
389 "xyz.openbmc_project.Association", "endpoints");
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100390 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100391
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700392 // Get the Chassis Collection
Ed Tanous1abe55e2018-09-05 08:30:59 -0700393 crow::connections::systemBus->async_method_call(
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700394 respHandler, "xyz.openbmc_project.ObjectMapper",
395 "/xyz/openbmc_project/object_mapper",
396 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Ed Tanous271584a2019-07-09 16:24:22 -0700397 "/xyz/openbmc_project/inventory", 0, interfaces);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700398 BMCWEB_LOG_DEBUG << "getChassis exit";
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100399}
400
401/**
Shawn McCarneyde629b62019-03-08 10:42:51 -0600402 * @brief Finds all DBus object paths that implement ObjectManager.
403 *
404 * Creates a mapping from the associated connection name to the object path.
405 *
406 * Finds the object paths asynchronously. Invokes callback when information has
407 * been obtained.
408 *
409 * The callback must have the following signature:
410 * @code
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500411 * callback(std::shared_ptr<boost::container::flat_map<std::string,
412 * std::string>> objectMgrPaths)
Shawn McCarneyde629b62019-03-08 10:42:51 -0600413 * @endcode
414 *
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700415 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyde629b62019-03-08 10:42:51 -0600416 * @param callback Callback to invoke when object paths obtained.
417 */
418template <typename Callback>
419void getObjectManagerPaths(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
420 Callback&& callback)
421{
422 BMCWEB_LOG_DEBUG << "getObjectManagerPaths enter";
423 const std::array<std::string, 1> interfaces = {
424 "org.freedesktop.DBus.ObjectManager"};
425
426 // Response handler for GetSubTree DBus method
427 auto respHandler = [callback{std::move(callback)},
428 SensorsAsyncResp](const boost::system::error_code ec,
429 const GetSubTreeType& subtree) {
430 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler enter";
431 if (ec)
432 {
433 messages::internalError(SensorsAsyncResp->res);
434 BMCWEB_LOG_ERROR << "getObjectManagerPaths respHandler: DBus error "
435 << ec;
436 return;
437 }
438
439 // Loop over returned object paths
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500440 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
441 objectMgrPaths = std::make_shared<
442 boost::container::flat_map<std::string, std::string>>();
Shawn McCarneyde629b62019-03-08 10:42:51 -0600443 for (const std::pair<
444 std::string,
445 std::vector<std::pair<std::string, std::vector<std::string>>>>&
446 object : subtree)
447 {
448 // Loop over connections for current object path
449 const std::string& objectPath = object.first;
450 for (const std::pair<std::string, std::vector<std::string>>&
451 objData : object.second)
452 {
453 // Add mapping from connection to object path
454 const std::string& connection = objData.first;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500455 (*objectMgrPaths)[connection] = objectPath;
Shawn McCarneyde629b62019-03-08 10:42:51 -0600456 BMCWEB_LOG_DEBUG << "Added mapping " << connection << " -> "
457 << objectPath;
458 }
459 }
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500460 callback(objectMgrPaths);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600461 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler exit";
462 };
463
464 // Query mapper for all DBus object paths that implement ObjectManager
465 crow::connections::systemBus->async_method_call(
466 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
467 "/xyz/openbmc_project/object_mapper",
Ed Tanous271584a2019-07-09 16:24:22 -0700468 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0, interfaces);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600469 BMCWEB_LOG_DEBUG << "getObjectManagerPaths exit";
470}
471
472/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500473 * @brief Returns the Redfish State value for the specified inventory item.
474 * @param inventoryItem D-Bus inventory item associated with a sensor.
475 * @return State value for inventory item.
James Feist34dd1792019-05-17 14:10:54 -0700476 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500477static std::string getState(const InventoryItem* inventoryItem)
478{
479 if ((inventoryItem != nullptr) && !(inventoryItem->isPresent))
480 {
481 return "Absent";
482 }
James Feist34dd1792019-05-17 14:10:54 -0700483
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500484 return "Enabled";
485}
486
487/**
488 * @brief Returns the Redfish Health value for the specified sensor.
489 * @param sensorJson Sensor JSON object.
490 * @param interfacesDict Map of all sensor interfaces.
491 * @param inventoryItem D-Bus inventory item associated with the sensor. Will
492 * be nullptr if no associated inventory item was found.
493 * @return Health value for sensor.
494 */
James Feist34dd1792019-05-17 14:10:54 -0700495static std::string getHealth(
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500496 nlohmann::json& sensorJson,
James Feist34dd1792019-05-17 14:10:54 -0700497 const boost::container::flat_map<
498 std::string, boost::container::flat_map<std::string, SensorVariant>>&
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500499 interfacesDict,
500 const InventoryItem* inventoryItem)
James Feist34dd1792019-05-17 14:10:54 -0700501{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500502 // Get current health value (if any) in the sensor JSON object. Some JSON
503 // objects contain multiple sensors (such as PowerSupplies). We want to set
504 // the overall health to be the most severe of any of the sensors.
505 std::string currentHealth;
506 auto statusIt = sensorJson.find("Status");
507 if (statusIt != sensorJson.end())
508 {
509 auto healthIt = statusIt->find("Health");
510 if (healthIt != statusIt->end())
511 {
512 std::string* health = healthIt->get_ptr<std::string*>();
513 if (health != nullptr)
514 {
515 currentHealth = *health;
516 }
517 }
518 }
519
520 // If current health in JSON object is already Critical, return that. This
521 // should override the sensor health, which might be less severe.
522 if (currentHealth == "Critical")
523 {
524 return "Critical";
525 }
526
527 // Check if sensor has critical threshold alarm
James Feist34dd1792019-05-17 14:10:54 -0700528 auto criticalThresholdIt =
529 interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Critical");
530 if (criticalThresholdIt != interfacesDict.end())
531 {
532 auto thresholdHighIt =
533 criticalThresholdIt->second.find("CriticalAlarmHigh");
534 auto thresholdLowIt =
535 criticalThresholdIt->second.find("CriticalAlarmLow");
536 if (thresholdHighIt != criticalThresholdIt->second.end())
537 {
538 const bool* asserted = std::get_if<bool>(&thresholdHighIt->second);
539 if (asserted == nullptr)
540 {
541 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
542 }
543 else if (*asserted)
544 {
545 return "Critical";
546 }
547 }
548 if (thresholdLowIt != criticalThresholdIt->second.end())
549 {
550 const bool* asserted = std::get_if<bool>(&thresholdLowIt->second);
551 if (asserted == nullptr)
552 {
553 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
554 }
555 else if (*asserted)
556 {
557 return "Critical";
558 }
559 }
560 }
561
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500562 // Check if associated inventory item is not functional
563 if ((inventoryItem != nullptr) && !(inventoryItem->isFunctional))
564 {
565 return "Critical";
566 }
567
568 // If current health in JSON object is already Warning, return that. This
569 // should override the sensor status, which might be less severe.
570 if (currentHealth == "Warning")
571 {
572 return "Warning";
573 }
574
575 // Check if sensor has warning threshold alarm
James Feist34dd1792019-05-17 14:10:54 -0700576 auto warningThresholdIt =
577 interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Warning");
578 if (warningThresholdIt != interfacesDict.end())
579 {
580 auto thresholdHighIt =
581 warningThresholdIt->second.find("WarningAlarmHigh");
582 auto thresholdLowIt =
583 warningThresholdIt->second.find("WarningAlarmLow");
584 if (thresholdHighIt != warningThresholdIt->second.end())
585 {
586 const bool* asserted = std::get_if<bool>(&thresholdHighIt->second);
587 if (asserted == nullptr)
588 {
589 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
590 }
591 else if (*asserted)
592 {
593 return "Warning";
594 }
595 }
596 if (thresholdLowIt != warningThresholdIt->second.end())
597 {
598 const bool* asserted = std::get_if<bool>(&thresholdLowIt->second);
599 if (asserted == nullptr)
600 {
601 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
602 }
603 else if (*asserted)
604 {
605 return "Warning";
606 }
607 }
608 }
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500609
James Feist34dd1792019-05-17 14:10:54 -0700610 return "OK";
611}
612
Anthony Wilsond5005492019-07-31 16:34:17 -0500613static void setLedState(nlohmann::json& sensorJson,
614 const InventoryItem* inventoryItem)
615{
616 if (inventoryItem != nullptr && !inventoryItem->ledObjectPath.empty())
617 {
618 switch (inventoryItem->ledState)
619 {
620 case LedState::OFF:
621 sensorJson["IndicatorLED"] = "Off";
622 break;
623 case LedState::ON:
624 sensorJson["IndicatorLED"] = "Lit";
625 break;
626 case LedState::BLINK:
627 sensorJson["IndicatorLED"] = "Blinking";
628 break;
629 default:
630 break;
631 }
632 }
633}
634
James Feist34dd1792019-05-17 14:10:54 -0700635/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100636 * @brief Builds a json sensor representation of a sensor.
637 * @param sensorName The name of the sensor to be built
Gunnar Mills274fad52018-06-13 15:45:36 -0500638 * @param sensorType The type (temperature, fan_tach, etc) of the sensor to
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100639 * build
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500640 * @param sensorSchema The schema (Power, Thermal, etc) being associated with
641 * the sensor to build
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100642 * @param interfacesDict A dictionary of the interfaces and properties of said
643 * interfaces to be built from
644 * @param sensor_json The json object to fill
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500645 * @param inventoryItem D-Bus inventory item associated with the sensor. Will
646 * be nullptr if no associated inventory item was found.
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100647 */
648void objectInterfacesToJson(
649 const std::string& sensorName, const std::string& sensorType,
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500650 const std::string& sensorSchema,
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100651 const boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700652 std::string, boost::container::flat_map<std::string, SensorVariant>>&
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100653 interfacesDict,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500654 nlohmann::json& sensor_json, InventoryItem* inventoryItem)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700655{
656 // We need a value interface before we can do anything with it
657 auto valueIt = interfacesDict.find("xyz.openbmc_project.Sensor.Value");
658 if (valueIt == interfacesDict.end())
659 {
660 BMCWEB_LOG_ERROR << "Sensor doesn't have a value interface";
661 return;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100662 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100663
Ed Tanous1abe55e2018-09-05 08:30:59 -0700664 // Assume values exist as is (10^0 == 1) if no scale exists
665 int64_t scaleMultiplier = 0;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100666
Ed Tanous1abe55e2018-09-05 08:30:59 -0700667 auto scaleIt = valueIt->second.find("Scale");
668 // If a scale exists, pull value as int64, and use the scaling.
669 if (scaleIt != valueIt->second.end())
670 {
Ed Tanousabf2add2019-01-22 16:40:12 -0800671 const int64_t* int64Value = std::get_if<int64_t>(&scaleIt->second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700672 if (int64Value != nullptr)
673 {
674 scaleMultiplier = *int64Value;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100675 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100676 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700677
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500678 if (sensorSchema == "Sensors")
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500679 {
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500680 // For sensors in SensorCollection we set Id instead of MemberId,
681 // including power sensors.
682 sensor_json["Id"] = sensorName;
683 sensor_json["Name"] = boost::replace_all_copy(sensorName, "_", " ");
684 }
685 else if (sensorType != "power")
686 {
687 // Set MemberId and Name for non-power sensors. For PowerSupplies and
688 // PowerControl, those properties have more general values because
689 // multiple sensors can be stored in the same JSON object.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500690 sensor_json["MemberId"] = sensorName;
691 sensor_json["Name"] = boost::replace_all_copy(sensorName, "_", " ");
692 }
Ed Tanouse742b6c2019-05-03 15:06:53 -0700693
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500694 sensor_json["Status"]["State"] = getState(inventoryItem);
695 sensor_json["Status"]["Health"] =
696 getHealth(sensor_json, interfacesDict, inventoryItem);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700697
698 // Parameter to set to override the type we get from dbus, and force it to
699 // int, regardless of what is available. This is used for schemas like fan,
700 // that require integers, not floats.
701 bool forceToInt = false;
702
Anthony Wilson3929aca2019-07-19 15:42:33 -0500703 nlohmann::json::json_pointer unit("/Reading");
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500704 if (sensorSchema == "Sensors")
705 {
706 sensor_json["@odata.type"] = "#Sensor.v1_0_0.Sensor";
707 sensor_json["@odata.context"] = "/redfish/v1/$metadata#Sensor.Sensor";
708 if (sensorType == "power")
709 {
710 sensor_json["ReadingUnits"] = "Watts";
711 }
712 else if (sensorType == "current")
713 {
714 sensor_json["ReadingUnits"] = "Amperes";
715 }
716 }
717 else if (sensorType == "temperature")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700718 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500719 unit = "/ReadingCelsius"_json_pointer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700720 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Temperature";
721 // TODO(ed) Documentation says that path should be type fan_tach,
722 // implementation seems to implement fan
723 }
724 else if (sensorType == "fan" || sensorType == "fan_tach")
725 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500726 unit = "/Reading"_json_pointer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700727 sensor_json["ReadingUnits"] = "RPM";
728 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
Anthony Wilsond5005492019-07-31 16:34:17 -0500729 setLedState(sensor_json, inventoryItem);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700730 forceToInt = true;
731 }
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700732 else if (sensorType == "fan_pwm")
733 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500734 unit = "/Reading"_json_pointer;
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700735 sensor_json["ReadingUnits"] = "Percent";
736 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
Anthony Wilsond5005492019-07-31 16:34:17 -0500737 setLedState(sensor_json, inventoryItem);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700738 forceToInt = true;
739 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700740 else if (sensorType == "voltage")
741 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500742 unit = "/ReadingVolts"_json_pointer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700743 sensor_json["@odata.type"] = "#Power.v1_0_0.Voltage";
744 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700745 else if (sensorType == "power")
746 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700747 std::string sensorNameLower =
748 boost::algorithm::to_lower_copy(sensorName);
749
Eddie James028f7eb2019-05-17 21:24:36 +0000750 if (!sensorName.compare("total_power"))
751 {
Gunnar Mills7ab06f42019-07-02 13:07:16 -0500752 sensor_json["@odata.type"] = "#Power.v1_0_0.PowerControl";
753 // Put multiple "sensors" into a single PowerControl, so have
754 // generic names for MemberId and Name. Follows Redfish mockup.
755 sensor_json["MemberId"] = "0";
756 sensor_json["Name"] = "Chassis Power Control";
Anthony Wilson3929aca2019-07-19 15:42:33 -0500757 unit = "/PowerConsumedWatts"_json_pointer;
Eddie James028f7eb2019-05-17 21:24:36 +0000758 }
759 else if (sensorNameLower.find("input") != std::string::npos)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700760 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500761 unit = "/PowerInputWatts"_json_pointer;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700762 }
763 else
764 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500765 unit = "/PowerOutputWatts"_json_pointer;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700766 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700767 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700768 else
769 {
770 BMCWEB_LOG_ERROR << "Redfish cannot map object type for " << sensorName;
771 return;
772 }
773 // Map of dbus interface name, dbus property name and redfish property_name
Anthony Wilson3929aca2019-07-19 15:42:33 -0500774 std::vector<
775 std::tuple<const char*, const char*, nlohmann::json::json_pointer>>
776 properties;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700777 properties.reserve(7);
778
779 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600780
Anthony Wilson3929aca2019-07-19 15:42:33 -0500781 if (sensorSchema == "Sensors")
782 {
783 properties.emplace_back(
784 "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningHigh",
785 "/Thresholds/UpperCaution/Reading"_json_pointer);
786 properties.emplace_back(
787 "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningLow",
788 "/Thresholds/LowerCaution/Reading"_json_pointer);
789 properties.emplace_back(
790 "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalHigh",
791 "/Thresholds/UpperCritical/Reading"_json_pointer);
792 properties.emplace_back(
793 "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalLow",
794 "/Thresholds/LowerCritical/Reading"_json_pointer);
795 }
796 else if (sensorType != "power")
Shawn McCarneyde629b62019-03-08 10:42:51 -0600797 {
798 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500799 "WarningHigh",
800 "/UpperThresholdNonCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600801 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500802 "WarningLow",
803 "/LowerThresholdNonCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600804 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500805 "CriticalHigh",
806 "/UpperThresholdCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600807 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500808 "CriticalLow",
809 "/LowerThresholdCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600810 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700811
Ed Tanous2474adf2018-09-05 16:31:16 -0700812 // TODO Need to get UpperThresholdFatal and LowerThresholdFatal
813
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500814 if (sensorSchema == "Sensors")
815 {
816 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500817 "/ReadingRangeMin"_json_pointer);
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500818 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500819 "/ReadingRangeMax"_json_pointer);
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500820 }
821 else if (sensorType == "temperature")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700822 {
823 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500824 "/MinReadingRangeTemp"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700825 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500826 "/MaxReadingRangeTemp"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700827 }
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500828 else if (sensorType != "power")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700829 {
830 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500831 "/MinReadingRange"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700832 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500833 "/MaxReadingRange"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700834 }
835
Anthony Wilson3929aca2019-07-19 15:42:33 -0500836 for (const std::tuple<const char*, const char*,
837 nlohmann::json::json_pointer>& p : properties)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700838 {
839 auto interfaceProperties = interfacesDict.find(std::get<0>(p));
840 if (interfaceProperties != interfacesDict.end())
841 {
Ed Tanous271584a2019-07-09 16:24:22 -0700842 auto thisValueIt = interfaceProperties->second.find(std::get<1>(p));
843 if (thisValueIt != interfaceProperties->second.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700844 {
Ed Tanous271584a2019-07-09 16:24:22 -0700845 const SensorVariant& valueVariant = thisValueIt->second;
Anthony Wilson3929aca2019-07-19 15:42:33 -0500846
847 // The property we want to set may be nested json, so use
848 // a json_pointer for easy indexing into the json structure.
849 const nlohmann::json::json_pointer& key = std::get<2>(p);
850
Ed Tanous1abe55e2018-09-05 08:30:59 -0700851 // Attempt to pull the int64 directly
Ed Tanousabf2add2019-01-22 16:40:12 -0800852 const int64_t* int64Value = std::get_if<int64_t>(&valueVariant);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700853
Ed Tanousabf2add2019-01-22 16:40:12 -0800854 const double* doubleValue = std::get_if<double>(&valueVariant);
Eddie James028f7eb2019-05-17 21:24:36 +0000855 const uint32_t* uValue = std::get_if<uint32_t>(&valueVariant);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700856 double temp = 0.0;
857 if (int64Value != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700858 {
Ed Tanous271584a2019-07-09 16:24:22 -0700859 temp = static_cast<double>(*int64Value);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700860 }
861 else if (doubleValue != nullptr)
862 {
863 temp = *doubleValue;
864 }
Eddie James028f7eb2019-05-17 21:24:36 +0000865 else if (uValue != nullptr)
866 {
867 temp = *uValue;
868 }
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700869 else
870 {
871 BMCWEB_LOG_ERROR
872 << "Got value interface that wasn't int or double";
873 continue;
874 }
875 temp = temp * std::pow(10, scaleMultiplier);
876 if (forceToInt)
877 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500878 sensor_json[key] = static_cast<int64_t>(temp);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700879 }
880 else
881 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500882 sensor_json[key] = temp;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700883 }
884 }
885 }
886 }
887 BMCWEB_LOG_DEBUG << "Added sensor " << sensorName;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100888}
889
James Feist8bd25cc2019-03-15 15:14:00 -0700890static void
891 populateFanRedundancy(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp)
892{
893 crow::connections::systemBus->async_method_call(
894 [sensorsAsyncResp](const boost::system::error_code ec,
895 const GetSubTreeType& resp) {
896 if (ec)
897 {
898 return; // don't have to have this interface
899 }
Ed Tanouse278c182019-03-13 16:23:37 -0700900 for (const std::pair<std::string,
901 std::vector<std::pair<
902 std::string, std::vector<std::string>>>>&
903 pathPair : resp)
James Feist8bd25cc2019-03-15 15:14:00 -0700904 {
Ed Tanouse278c182019-03-13 16:23:37 -0700905 const std::string& path = pathPair.first;
906 const std::vector<
907 std::pair<std::string, std::vector<std::string>>>& objDict =
908 pathPair.second;
James Feist8bd25cc2019-03-15 15:14:00 -0700909 if (objDict.empty())
910 {
911 continue; // this should be impossible
912 }
913
914 const std::string& owner = objDict.begin()->first;
915 crow::connections::systemBus->async_method_call(
916 [path, owner,
Ed Tanous271584a2019-07-09 16:24:22 -0700917 sensorsAsyncResp](const boost::system::error_code e,
James Feist8bd25cc2019-03-15 15:14:00 -0700918 std::variant<std::vector<std::string>>
919 variantEndpoints) {
Ed Tanous271584a2019-07-09 16:24:22 -0700920 if (e)
James Feist8bd25cc2019-03-15 15:14:00 -0700921 {
922 return; // if they don't have an association we
923 // can't tell what chassis is
924 }
925 // verify part of the right chassis
926 auto endpoints = std::get_if<std::vector<std::string>>(
927 &variantEndpoints);
928
929 if (endpoints == nullptr)
930 {
931 BMCWEB_LOG_ERROR << "Invalid association interface";
932 messages::internalError(sensorsAsyncResp->res);
933 return;
934 }
935
936 auto found = std::find_if(
937 endpoints->begin(), endpoints->end(),
938 [sensorsAsyncResp](const std::string& entry) {
939 return entry.find(
940 sensorsAsyncResp->chassisId) !=
941 std::string::npos;
942 });
943
944 if (found == endpoints->end())
945 {
946 return;
947 }
948 crow::connections::systemBus->async_method_call(
949 [path, sensorsAsyncResp](
Ed Tanous271584a2019-07-09 16:24:22 -0700950 const boost::system::error_code& err,
James Feist8bd25cc2019-03-15 15:14:00 -0700951 const boost::container::flat_map<
952 std::string,
953 std::variant<uint8_t,
954 std::vector<std::string>,
955 std::string>>& ret) {
Ed Tanous271584a2019-07-09 16:24:22 -0700956 if (err)
James Feist8bd25cc2019-03-15 15:14:00 -0700957 {
958 return; // don't have to have this
959 // interface
960 }
961 auto findFailures = ret.find("AllowedFailures");
962 auto findCollection = ret.find("Collection");
963 auto findStatus = ret.find("Status");
964
965 if (findFailures == ret.end() ||
966 findCollection == ret.end() ||
967 findStatus == ret.end())
968 {
969 BMCWEB_LOG_ERROR
970 << "Invalid redundancy interface";
971 messages::internalError(
972 sensorsAsyncResp->res);
973 return;
974 }
975
976 auto allowedFailures = std::get_if<uint8_t>(
977 &(findFailures->second));
978 auto collection =
979 std::get_if<std::vector<std::string>>(
980 &(findCollection->second));
981 auto status = std::get_if<std::string>(
982 &(findStatus->second));
983
984 if (allowedFailures == nullptr ||
985 collection == nullptr || status == nullptr)
986 {
987
988 BMCWEB_LOG_ERROR
989 << "Invalid redundancy interface "
990 "types";
991 messages::internalError(
992 sensorsAsyncResp->res);
993 return;
994 }
995 size_t lastSlash = path.rfind("/");
996 if (lastSlash == std::string::npos)
997 {
998 // this should be impossible
999 messages::internalError(
1000 sensorsAsyncResp->res);
1001 return;
1002 }
1003 std::string name = path.substr(lastSlash + 1);
1004 std::replace(name.begin(), name.end(), '_',
1005 ' ');
1006
1007 std::string health;
1008
1009 if (boost::ends_with(*status, "Full"))
1010 {
1011 health = "OK";
1012 }
1013 else if (boost::ends_with(*status, "Degraded"))
1014 {
1015 health = "Warning";
1016 }
1017 else
1018 {
1019 health = "Critical";
1020 }
1021 std::vector<nlohmann::json> redfishCollection;
1022 const auto& fanRedfish =
1023 sensorsAsyncResp->res.jsonValue["Fans"];
1024 for (const std::string& item : *collection)
1025 {
1026 lastSlash = item.rfind("/");
1027 // make a copy as collection is const
1028 std::string itemName =
1029 item.substr(lastSlash + 1);
1030 /*
1031 todo(ed): merge patch that fixes the names
1032 std::replace(itemName.begin(),
1033 itemName.end(), '_', ' ');*/
1034 auto schemaItem = std::find_if(
1035 fanRedfish.begin(), fanRedfish.end(),
1036 [itemName](const nlohmann::json& fan) {
1037 return fan["MemberId"] == itemName;
1038 });
1039 if (schemaItem != fanRedfish.end())
1040 {
1041 redfishCollection.push_back(
1042 {{"@odata.id",
1043 (*schemaItem)["@odata.id"]}});
1044 }
1045 else
1046 {
1047 BMCWEB_LOG_ERROR
1048 << "failed to find fan in schema";
1049 messages::internalError(
1050 sensorsAsyncResp->res);
1051 return;
1052 }
1053 }
1054
Ed Tanous271584a2019-07-09 16:24:22 -07001055 nlohmann::json& jResp =
1056 sensorsAsyncResp->res
1057 .jsonValue["Redundancy"];
1058 jResp.push_back(
James Feist8bd25cc2019-03-15 15:14:00 -07001059 {{"@odata.id",
AppaRao Puli717794d2019-10-18 22:54:53 +05301060 "/redfish/v1/Chassis/" +
James Feist8bd25cc2019-03-15 15:14:00 -07001061 sensorsAsyncResp->chassisId + "/" +
1062 sensorsAsyncResp->chassisSubNode +
1063 "#/Redundancy/" +
Ed Tanous271584a2019-07-09 16:24:22 -07001064 std::to_string(jResp.size())},
James Feist8bd25cc2019-03-15 15:14:00 -07001065 {"@odata.type",
1066 "#Redundancy.v1_3_2.Redundancy"},
1067 {"MinNumNeeded",
1068 collection->size() - *allowedFailures},
1069 {"MemberId", name},
1070 {"Mode", "N+m"},
1071 {"Name", name},
1072 {"RedundancySet", redfishCollection},
1073 {"Status",
1074 {{"Health", health},
1075 {"State", "Enabled"}}}});
1076 },
1077 owner, path, "org.freedesktop.DBus.Properties",
1078 "GetAll",
1079 "xyz.openbmc_project.Control.FanRedundancy");
1080 },
James Feist02e92e32019-06-26 12:07:05 -07001081 "xyz.openbmc_project.ObjectMapper", path + "/chassis",
James Feist8bd25cc2019-03-15 15:14:00 -07001082 "org.freedesktop.DBus.Properties", "Get",
1083 "xyz.openbmc_project.Association", "endpoints");
1084 }
1085 },
1086 "xyz.openbmc_project.ObjectMapper",
1087 "/xyz/openbmc_project/object_mapper",
1088 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
1089 "/xyz/openbmc_project/control", 2,
1090 std::array<const char*, 1>{
1091 "xyz.openbmc_project.Control.FanRedundancy"});
1092}
1093
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001094void sortJSONResponse(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
1095{
1096 nlohmann::json& response = SensorsAsyncResp->res.jsonValue;
1097 std::array<std::string, 2> sensorHeaders{"Temperatures", "Fans"};
1098 if (SensorsAsyncResp->chassisSubNode == "Power")
1099 {
1100 sensorHeaders = {"Voltages", "PowerSupplies"};
1101 }
1102 for (const std::string& sensorGroup : sensorHeaders)
1103 {
1104 nlohmann::json::iterator entry = response.find(sensorGroup);
1105 if (entry != response.end())
1106 {
1107 std::sort(entry->begin(), entry->end(),
1108 [](nlohmann::json& c1, nlohmann::json& c2) {
1109 return c1["Name"] < c2["Name"];
1110 });
1111
1112 // add the index counts to the end of each entry
1113 size_t count = 0;
1114 for (nlohmann::json& sensorJson : *entry)
1115 {
1116 nlohmann::json::iterator odata = sensorJson.find("@odata.id");
1117 if (odata == sensorJson.end())
1118 {
1119 continue;
1120 }
1121 std::string* value = odata->get_ptr<std::string*>();
1122 if (value != nullptr)
1123 {
1124 *value += std::to_string(count);
1125 count++;
1126 }
1127 }
1128 }
1129 }
1130}
1131
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001132/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001133 * @brief Finds the inventory item with the specified object path.
1134 * @param inventoryItems D-Bus inventory items associated with sensors.
1135 * @param invItemObjPath D-Bus object path of inventory item.
1136 * @return Inventory item within vector, or nullptr if no match found.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001137 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001138static InventoryItem* findInventoryItem(
1139 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1140 const std::string& invItemObjPath)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001141{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001142 for (InventoryItem& inventoryItem : *inventoryItems)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001143 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001144 if (inventoryItem.objectPath == invItemObjPath)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001145 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001146 return &inventoryItem;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001147 }
1148 }
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001149 return nullptr;
1150}
1151
1152/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001153 * @brief Finds the inventory item associated with the specified sensor.
1154 * @param inventoryItems D-Bus inventory items associated with sensors.
1155 * @param sensorObjPath D-Bus object path of sensor.
1156 * @return Inventory item within vector, or nullptr if no match found.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001157 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001158static InventoryItem* findInventoryItemForSensor(
1159 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1160 const std::string& sensorObjPath)
1161{
1162 for (InventoryItem& inventoryItem : *inventoryItems)
1163 {
1164 if (inventoryItem.sensors.count(sensorObjPath) > 0)
1165 {
1166 return &inventoryItem;
1167 }
1168 }
1169 return nullptr;
1170}
1171
1172/**
Anthony Wilsond5005492019-07-31 16:34:17 -05001173 * @brief Finds the inventory item associated with the specified led path.
1174 * @param inventoryItems D-Bus inventory items associated with sensors.
1175 * @param ledObjPath D-Bus object path of led.
1176 * @return Inventory item within vector, or nullptr if no match found.
1177 */
1178inline InventoryItem*
1179 findInventoryItemForLed(std::vector<InventoryItem>& inventoryItems,
1180 const std::string& ledObjPath)
1181{
1182 for (InventoryItem& inventoryItem : inventoryItems)
1183 {
1184 if (inventoryItem.ledObjectPath == ledObjPath)
1185 {
1186 return &inventoryItem;
1187 }
1188 }
1189 return nullptr;
1190}
1191
1192/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001193 * @brief Adds inventory item and associated sensor to specified vector.
1194 *
1195 * Adds a new InventoryItem to the vector if necessary. Searches for an
1196 * existing InventoryItem with the specified object path. If not found, one is
1197 * added to the vector.
1198 *
1199 * Next, the specified sensor is added to the set of sensors associated with the
1200 * InventoryItem.
1201 *
1202 * @param inventoryItems D-Bus inventory items associated with sensors.
1203 * @param invItemObjPath D-Bus object path of inventory item.
1204 * @param sensorObjPath D-Bus object path of sensor
1205 */
1206static void
1207 addInventoryItem(std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1208 const std::string& invItemObjPath,
1209 const std::string& sensorObjPath)
1210{
1211 // Look for inventory item in vector
1212 InventoryItem* inventoryItem =
1213 findInventoryItem(inventoryItems, invItemObjPath);
1214
1215 // If inventory item doesn't exist in vector, add it
1216 if (inventoryItem == nullptr)
1217 {
1218 inventoryItems->emplace_back(invItemObjPath);
1219 inventoryItem = &(inventoryItems->back());
1220 }
1221
1222 // Add sensor to set of sensors associated with inventory item
1223 inventoryItem->sensors.emplace(sensorObjPath);
1224}
1225
1226/**
1227 * @brief Stores D-Bus data in the specified inventory item.
1228 *
1229 * Finds D-Bus data in the specified map of interfaces. Stores the data in the
1230 * specified InventoryItem.
1231 *
1232 * This data is later used to provide sensor property values in the JSON
1233 * response.
1234 *
1235 * @param inventoryItem Inventory item where data will be stored.
1236 * @param interfacesDict Map containing D-Bus interfaces and their properties
1237 * for the specified inventory item.
1238 */
1239static void storeInventoryItemData(
1240 InventoryItem& inventoryItem,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001241 const boost::container::flat_map<
1242 std::string, boost::container::flat_map<std::string, SensorVariant>>&
1243 interfacesDict)
1244{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001245 // Get properties from Inventory.Item interface
1246 auto interfaceIt =
1247 interfacesDict.find("xyz.openbmc_project.Inventory.Item");
1248 if (interfaceIt != interfacesDict.end())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001249 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001250 auto propertyIt = interfaceIt->second.find("Present");
1251 if (propertyIt != interfaceIt->second.end())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001252 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001253 const bool* value = std::get_if<bool>(&propertyIt->second);
1254 if (value != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001255 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001256 inventoryItem.isPresent = *value;
1257 }
1258 }
1259 }
1260
1261 // Check if Inventory.Item.PowerSupply interface is present
1262 interfaceIt =
1263 interfacesDict.find("xyz.openbmc_project.Inventory.Item.PowerSupply");
1264 if (interfaceIt != interfacesDict.end())
1265 {
1266 inventoryItem.isPowerSupply = true;
1267 }
1268
1269 // Get properties from Inventory.Decorator.Asset interface
1270 interfaceIt =
1271 interfacesDict.find("xyz.openbmc_project.Inventory.Decorator.Asset");
1272 if (interfaceIt != interfacesDict.end())
1273 {
1274 auto propertyIt = interfaceIt->second.find("Manufacturer");
1275 if (propertyIt != interfaceIt->second.end())
1276 {
1277 const std::string* value =
1278 std::get_if<std::string>(&propertyIt->second);
1279 if (value != nullptr)
1280 {
1281 inventoryItem.manufacturer = *value;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001282 }
1283 }
1284
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001285 propertyIt = interfaceIt->second.find("Model");
1286 if (propertyIt != interfaceIt->second.end())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001287 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001288 const std::string* value =
1289 std::get_if<std::string>(&propertyIt->second);
1290 if (value != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001291 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001292 inventoryItem.model = *value;
1293 }
1294 }
1295
1296 propertyIt = interfaceIt->second.find("PartNumber");
1297 if (propertyIt != interfaceIt->second.end())
1298 {
1299 const std::string* value =
1300 std::get_if<std::string>(&propertyIt->second);
1301 if (value != nullptr)
1302 {
1303 inventoryItem.partNumber = *value;
1304 }
1305 }
1306
1307 propertyIt = interfaceIt->second.find("SerialNumber");
1308 if (propertyIt != interfaceIt->second.end())
1309 {
1310 const std::string* value =
1311 std::get_if<std::string>(&propertyIt->second);
1312 if (value != nullptr)
1313 {
1314 inventoryItem.serialNumber = *value;
1315 }
1316 }
1317 }
1318
1319 // Get properties from State.Decorator.OperationalStatus interface
1320 interfaceIt = interfacesDict.find(
1321 "xyz.openbmc_project.State.Decorator.OperationalStatus");
1322 if (interfaceIt != interfacesDict.end())
1323 {
1324 auto propertyIt = interfaceIt->second.find("Functional");
1325 if (propertyIt != interfaceIt->second.end())
1326 {
1327 const bool* value = std::get_if<bool>(&propertyIt->second);
1328 if (value != nullptr)
1329 {
1330 inventoryItem.isFunctional = *value;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001331 }
1332 }
1333 }
1334}
1335
1336/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001337 * @brief Gets D-Bus data for inventory items associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001338 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001339 * Uses the specified connections (services) to obtain D-Bus data for inventory
1340 * items associated with sensors. Stores the resulting data in the
1341 * inventoryItems vector.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001342 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001343 * This data is later used to provide sensor property values in the JSON
1344 * response.
1345 *
1346 * Finds the inventory item data asynchronously. Invokes callback when data has
1347 * been obtained.
1348 *
1349 * The callback must have the following signature:
1350 * @code
Anthony Wilsond5005492019-07-31 16:34:17 -05001351 * callback(void)
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001352 * @endcode
1353 *
1354 * This function is called recursively, obtaining data asynchronously from one
1355 * connection in each call. This ensures the callback is not invoked until the
1356 * last asynchronous function has completed.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001357 *
1358 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001359 * @param inventoryItems D-Bus inventory items associated with sensors.
1360 * @param invConnections Connections that provide data for the inventory items.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001361 * @param objectMgrPaths Mappings from connection name to DBus object path that
1362 * implements ObjectManager.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001363 * @param callback Callback to invoke when inventory data has been obtained.
1364 * @param invConnectionsIndex Current index in invConnections. Only specified
1365 * in recursive calls to this function.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001366 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001367template <typename Callback>
1368static void getInventoryItemsData(
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001369 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001370 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001371 std::shared_ptr<boost::container::flat_set<std::string>> invConnections,
1372 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001373 objectMgrPaths,
Ed Tanous271584a2019-07-09 16:24:22 -07001374 Callback&& callback, size_t invConnectionsIndex = 0)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001375{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001376 BMCWEB_LOG_DEBUG << "getInventoryItemsData enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001377
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001378 // If no more connections left, call callback
1379 if (invConnectionsIndex >= invConnections->size())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001380 {
Anthony Wilsond5005492019-07-31 16:34:17 -05001381 callback();
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001382 BMCWEB_LOG_DEBUG << "getInventoryItemsData exit";
1383 return;
1384 }
1385
1386 // Get inventory item data from current connection
1387 auto it = invConnections->nth(invConnectionsIndex);
1388 if (it != invConnections->end())
1389 {
1390 const std::string& invConnection = *it;
1391
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001392 // Response handler for GetManagedObjects
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001393 auto respHandler = [sensorsAsyncResp, inventoryItems, invConnections,
1394 objectMgrPaths, callback{std::move(callback)},
1395 invConnectionsIndex](
1396 const boost::system::error_code ec,
1397 ManagedObjectsVectorType& resp) {
1398 BMCWEB_LOG_DEBUG << "getInventoryItemsData respHandler enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001399 if (ec)
1400 {
1401 BMCWEB_LOG_ERROR
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001402 << "getInventoryItemsData respHandler DBus error " << ec;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001403 messages::internalError(sensorsAsyncResp->res);
1404 return;
1405 }
1406
1407 // Loop through returned object paths
1408 for (const auto& objDictEntry : resp)
1409 {
1410 const std::string& objPath =
1411 static_cast<const std::string&>(objDictEntry.first);
1412
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001413 // If this object path is one of the specified inventory items
1414 InventoryItem* inventoryItem =
1415 findInventoryItem(inventoryItems, objPath);
1416 if (inventoryItem != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001417 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001418 // Store inventory data in InventoryItem
1419 storeInventoryItemData(*inventoryItem, objDictEntry.second);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001420 }
1421 }
1422
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001423 // Recurse to get inventory item data from next connection
1424 getInventoryItemsData(sensorsAsyncResp, inventoryItems,
1425 invConnections, objectMgrPaths,
1426 std::move(callback), invConnectionsIndex + 1);
1427
1428 BMCWEB_LOG_DEBUG << "getInventoryItemsData respHandler exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001429 };
1430
1431 // Find DBus object path that implements ObjectManager for the current
1432 // connection. If no mapping found, default to "/".
1433 auto iter = objectMgrPaths->find(invConnection);
1434 const std::string& objectMgrPath =
1435 (iter != objectMgrPaths->end()) ? iter->second : "/";
1436 BMCWEB_LOG_DEBUG << "ObjectManager path for " << invConnection << " is "
1437 << objectMgrPath;
1438
1439 // Get all object paths and their interfaces for current connection
1440 crow::connections::systemBus->async_method_call(
1441 std::move(respHandler), invConnection, objectMgrPath,
1442 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1443 }
1444
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001445 BMCWEB_LOG_DEBUG << "getInventoryItemsData exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001446}
1447
1448/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001449 * @brief Gets connections that provide D-Bus data for inventory items.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001450 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001451 * Gets the D-Bus connections (services) that provide data for the inventory
1452 * items that are associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001453 *
1454 * Finds the connections asynchronously. Invokes callback when information has
1455 * been obtained.
1456 *
1457 * The callback must have the following signature:
1458 * @code
1459 * callback(std::shared_ptr<boost::container::flat_set<std::string>>
1460 * invConnections)
1461 * @endcode
1462 *
1463 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001464 * @param inventoryItems D-Bus inventory items associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001465 * @param callback Callback to invoke when connections have been obtained.
1466 */
1467template <typename Callback>
1468static void getInventoryItemsConnections(
1469 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001470 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001471 Callback&& callback)
1472{
1473 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections enter";
1474
1475 const std::string path = "/xyz/openbmc_project/inventory";
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001476 const std::array<std::string, 4> interfaces = {
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001477 "xyz.openbmc_project.Inventory.Item",
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001478 "xyz.openbmc_project.Inventory.Item.PowerSupply",
1479 "xyz.openbmc_project.Inventory.Decorator.Asset",
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001480 "xyz.openbmc_project.State.Decorator.OperationalStatus"};
1481
1482 // Response handler for parsing output from GetSubTree
1483 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001484 inventoryItems](const boost::system::error_code ec,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001485 const GetSubTreeType& subtree) {
1486 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections respHandler enter";
1487 if (ec)
1488 {
1489 messages::internalError(sensorsAsyncResp->res);
1490 BMCWEB_LOG_ERROR
1491 << "getInventoryItemsConnections respHandler DBus error " << ec;
1492 return;
1493 }
1494
1495 // Make unique list of connections for desired inventory items
1496 std::shared_ptr<boost::container::flat_set<std::string>>
1497 invConnections =
1498 std::make_shared<boost::container::flat_set<std::string>>();
1499 invConnections->reserve(8);
1500
1501 // Loop through objects from GetSubTree
1502 for (const std::pair<
1503 std::string,
1504 std::vector<std::pair<std::string, std::vector<std::string>>>>&
1505 object : subtree)
1506 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001507 // Check if object path is one of the specified inventory items
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001508 const std::string& objPath = object.first;
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001509 if (findInventoryItem(inventoryItems, objPath) != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001510 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001511 // Store all connections to inventory item
1512 for (const std::pair<std::string, std::vector<std::string>>&
1513 objData : object.second)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001514 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001515 const std::string& invConnection = objData.first;
1516 invConnections->insert(invConnection);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001517 }
1518 }
1519 }
Anthony Wilsond5005492019-07-31 16:34:17 -05001520
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001521 callback(invConnections);
1522 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections respHandler exit";
1523 };
1524
1525 // Make call to ObjectMapper to find all inventory items
1526 crow::connections::systemBus->async_method_call(
1527 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
1528 "/xyz/openbmc_project/object_mapper",
1529 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 0, interfaces);
1530 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections exit";
1531}
1532
1533/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001534 * @brief Gets associations from sensors to inventory items.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001535 *
1536 * Looks for ObjectMapper associations from the specified sensors to related
Anthony Wilsond5005492019-07-31 16:34:17 -05001537 * inventory items. Then finds the associations from those inventory items to
1538 * their LEDs, if any.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001539 *
1540 * Finds the inventory items asynchronously. Invokes callback when information
1541 * has been obtained.
1542 *
1543 * The callback must have the following signature:
1544 * @code
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001545 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001546 * @endcode
1547 *
1548 * @param sensorsAsyncResp Pointer to object holding response data.
1549 * @param sensorNames All sensors within the current chassis.
1550 * @param objectMgrPaths Mappings from connection name to DBus object path that
1551 * implements ObjectManager.
1552 * @param callback Callback to invoke when inventory items have been obtained.
1553 */
1554template <typename Callback>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001555static void getInventoryItemAssociations(
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001556 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1557 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
1558 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1559 objectMgrPaths,
1560 Callback&& callback)
1561{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001562 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001563
1564 // Response handler for GetManagedObjects
1565 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
1566 sensorNames](const boost::system::error_code ec,
1567 dbus::utility::ManagedObjectType& resp) {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001568 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations respHandler enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001569 if (ec)
1570 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001571 BMCWEB_LOG_ERROR
1572 << "getInventoryItemAssociations respHandler DBus error " << ec;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001573 messages::internalError(sensorsAsyncResp->res);
1574 return;
1575 }
1576
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001577 // Create vector to hold list of inventory items
1578 std::shared_ptr<std::vector<InventoryItem>> inventoryItems =
1579 std::make_shared<std::vector<InventoryItem>>();
1580
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001581 // Loop through returned object paths
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001582 std::string sensorAssocPath;
1583 sensorAssocPath.reserve(128); // avoid memory allocations
1584 for (const auto& objDictEntry : resp)
1585 {
1586 const std::string& objPath =
1587 static_cast<const std::string&>(objDictEntry.first);
1588 const boost::container::flat_map<
1589 std::string, boost::container::flat_map<
1590 std::string, dbus::utility::DbusVariantType>>&
1591 interfacesDict = objDictEntry.second;
1592
1593 // If path is inventory association for one of the specified sensors
1594 for (const std::string& sensorName : *sensorNames)
1595 {
1596 sensorAssocPath = sensorName;
1597 sensorAssocPath += "/inventory";
1598 if (objPath == sensorAssocPath)
1599 {
1600 // Get Association interface for object path
1601 auto assocIt =
1602 interfacesDict.find("xyz.openbmc_project.Association");
1603 if (assocIt != interfacesDict.end())
1604 {
1605 // Get inventory item from end point
1606 auto endpointsIt = assocIt->second.find("endpoints");
1607 if (endpointsIt != assocIt->second.end())
1608 {
1609 const std::vector<std::string>* endpoints =
1610 std::get_if<std::vector<std::string>>(
1611 &endpointsIt->second);
1612 if ((endpoints != nullptr) && !endpoints->empty())
1613 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001614 // Add inventory item to vector
1615 const std::string& invItemPath =
1616 endpoints->front();
1617 addInventoryItem(inventoryItems, invItemPath,
1618 sensorName);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001619 }
1620 }
1621 }
1622 break;
1623 }
1624 }
1625 }
1626
Anthony Wilsond5005492019-07-31 16:34:17 -05001627 // Now loop through the returned object paths again, this time to
1628 // find the leds associated with the inventory items we just found
1629 std::string inventoryAssocPath;
1630 inventoryAssocPath.reserve(128); // avoid memory allocations
1631 for (const auto& objDictEntry : resp)
1632 {
1633 const std::string& objPath =
1634 static_cast<const std::string&>(objDictEntry.first);
1635 const boost::container::flat_map<
1636 std::string, boost::container::flat_map<
1637 std::string, dbus::utility::DbusVariantType>>&
1638 interfacesDict = objDictEntry.second;
1639
1640 for (InventoryItem& inventoryItem : *inventoryItems)
1641 {
1642 inventoryAssocPath = inventoryItem.objectPath;
1643 inventoryAssocPath += "/leds";
1644 if (objPath == inventoryAssocPath)
1645 {
1646 // Get Association interface for object path
1647 auto assocIt =
1648 interfacesDict.find("xyz.openbmc_project.Association");
1649 if (assocIt != interfacesDict.end())
1650 {
1651 // Get inventory item from end point
1652 auto endpointsIt = assocIt->second.find("endpoints");
1653 if (endpointsIt != assocIt->second.end())
1654 {
1655 const std::vector<std::string>* endpoints =
1656 std::get_if<std::vector<std::string>>(
1657 &endpointsIt->second);
1658 if ((endpoints != nullptr) && !endpoints->empty())
1659 {
1660 // Store LED path in inventory item
1661 const std::string& ledPath = endpoints->front();
1662 inventoryItem.ledObjectPath = ledPath;
1663 }
1664 }
1665 }
1666 break;
1667 }
1668 }
1669 }
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001670 callback(inventoryItems);
1671 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations respHandler exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001672 };
1673
1674 // Find DBus object path that implements ObjectManager for ObjectMapper
1675 std::string connection = "xyz.openbmc_project.ObjectMapper";
1676 auto iter = objectMgrPaths->find(connection);
1677 const std::string& objectMgrPath =
1678 (iter != objectMgrPaths->end()) ? iter->second : "/";
1679 BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
1680 << objectMgrPath;
1681
1682 // Call GetManagedObjects on the ObjectMapper to get all associations
1683 crow::connections::systemBus->async_method_call(
1684 std::move(respHandler), connection, objectMgrPath,
1685 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1686
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001687 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001688}
1689
1690/**
Anthony Wilsond5005492019-07-31 16:34:17 -05001691 * @brief Gets D-Bus data for inventory item leds associated with sensors.
1692 *
1693 * Uses the specified connections (services) to obtain D-Bus data for inventory
1694 * item leds associated with sensors. Stores the resulting data in the
1695 * inventoryItems vector.
1696 *
1697 * This data is later used to provide sensor property values in the JSON
1698 * response.
1699 *
1700 * Finds the inventory item led data asynchronously. Invokes callback when data
1701 * has been obtained.
1702 *
1703 * The callback must have the following signature:
1704 * @code
1705 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
1706 * @endcode
1707 *
1708 * This function is called recursively, obtaining data asynchronously from one
1709 * connection in each call. This ensures the callback is not invoked until the
1710 * last asynchronous function has completed.
1711 *
1712 * @param sensorsAsyncResp Pointer to object holding response data.
1713 * @param inventoryItems D-Bus inventory items associated with sensors.
1714 * @param ledConnections Connections that provide data for the inventory leds.
1715 * @param callback Callback to invoke when inventory data has been obtained.
1716 * @param ledConnectionsIndex Current index in ledConnections. Only specified
1717 * in recursive calls to this function.
1718 */
1719template <typename Callback>
1720void getInventoryLedData(
1721 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1722 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1723 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1724 ledConnections,
1725 Callback&& callback, size_t ledConnectionsIndex = 0)
1726{
1727 BMCWEB_LOG_DEBUG << "getInventoryLedData enter";
1728
1729 // If no more connections left, call callback
1730 if (ledConnectionsIndex >= ledConnections->size())
1731 {
1732 callback(inventoryItems);
1733 BMCWEB_LOG_DEBUG << "getInventoryLedData exit";
1734 return;
1735 }
1736
1737 // Get inventory item data from current connection
1738 auto it = ledConnections->nth(ledConnectionsIndex);
1739 if (it != ledConnections->end())
1740 {
1741 const std::string& ledPath = (*it).first;
1742 const std::string& ledConnection = (*it).second;
1743 // Response handler for Get State property
1744 auto respHandler =
1745 [sensorsAsyncResp, inventoryItems, ledConnections, ledPath,
1746 callback{std::move(callback)},
1747 ledConnectionsIndex](const boost::system::error_code ec,
1748 const std::variant<std::string>& ledState) {
1749 BMCWEB_LOG_DEBUG << "getInventoryLedData respHandler enter";
1750 if (ec)
1751 {
1752 BMCWEB_LOG_ERROR
1753 << "getInventoryLedData respHandler DBus error " << ec;
1754 messages::internalError(sensorsAsyncResp->res);
1755 return;
1756 }
1757
1758 const std::string* state = std::get_if<std::string>(&ledState);
1759 if (state != nullptr)
1760 {
1761 BMCWEB_LOG_DEBUG << "Led state: " << *state;
1762 // Find inventory item with this LED object path
1763 InventoryItem* inventoryItem =
1764 findInventoryItemForLed(*inventoryItems, ledPath);
1765 if (inventoryItem != nullptr)
1766 {
1767 // Store LED state in InventoryItem
1768 if (boost::ends_with(*state, "On"))
1769 {
1770 inventoryItem->ledState = LedState::ON;
1771 }
1772 else if (boost::ends_with(*state, "Blink"))
1773 {
1774 inventoryItem->ledState = LedState::BLINK;
1775 }
1776 else if (boost::ends_with(*state, "Off"))
1777 {
1778 inventoryItem->ledState = LedState::OFF;
1779 }
1780 else
1781 {
1782 inventoryItem->ledState = LedState::UNKNOWN;
1783 }
1784 }
1785 }
1786 else
1787 {
1788 BMCWEB_LOG_DEBUG << "Failed to find State data for LED: "
1789 << ledPath;
1790 }
1791
1792 // Recurse to get LED data from next connection
1793 getInventoryLedData(sensorsAsyncResp, inventoryItems,
1794 ledConnections, std::move(callback),
1795 ledConnectionsIndex + 1);
1796
1797 BMCWEB_LOG_DEBUG << "getInventoryLedData respHandler exit";
1798 };
1799
1800 // Get the State property for the current LED
1801 crow::connections::systemBus->async_method_call(
1802 std::move(respHandler), ledConnection, ledPath,
1803 "org.freedesktop.DBus.Properties", "Get",
1804 "xyz.openbmc_project.Led.Physical", "State");
1805 }
1806
1807 BMCWEB_LOG_DEBUG << "getInventoryLedData exit";
1808}
1809
1810/**
1811 * @brief Gets LED data for LEDs associated with given inventory items.
1812 *
1813 * Gets the D-Bus connections (services) that provide LED data for the LEDs
1814 * associated with the specified inventory items. Then gets the LED data from
1815 * each connection and stores it in the inventory item.
1816 *
1817 * This data is later used to provide sensor property values in the JSON
1818 * response.
1819 *
1820 * Finds the LED data asynchronously. Invokes callback when information has
1821 * been obtained.
1822 *
1823 * The callback must have the following signature:
1824 * @code
1825 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
1826 * @endcode
1827 *
1828 * @param sensorsAsyncResp Pointer to object holding response data.
1829 * @param inventoryItems D-Bus inventory items associated with sensors.
1830 * @param callback Callback to invoke when inventory items have been obtained.
1831 */
1832template <typename Callback>
1833void getInventoryLeds(
1834 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1835 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1836 Callback&& callback)
1837{
1838 BMCWEB_LOG_DEBUG << "getInventoryLeds enter";
1839
1840 const std::string path = "/xyz/openbmc_project";
1841 const std::array<std::string, 1> interfaces = {
1842 "xyz.openbmc_project.Led.Physical"};
1843
1844 // Response handler for parsing output from GetSubTree
1845 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
1846 inventoryItems](const boost::system::error_code ec,
1847 const GetSubTreeType& subtree) {
1848 BMCWEB_LOG_DEBUG << "getInventoryLeds respHandler enter";
1849 if (ec)
1850 {
1851 messages::internalError(sensorsAsyncResp->res);
1852 BMCWEB_LOG_ERROR << "getInventoryLeds respHandler DBus error "
1853 << ec;
1854 return;
1855 }
1856
1857 // Build map of LED object paths to connections
1858 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1859 ledConnections = std::make_shared<
1860 boost::container::flat_map<std::string, std::string>>();
1861
1862 // Loop through objects from GetSubTree
1863 for (const std::pair<
1864 std::string,
1865 std::vector<std::pair<std::string, std::vector<std::string>>>>&
1866 object : subtree)
1867 {
1868 // Check if object path is LED for one of the specified inventory
1869 // items
1870 const std::string& ledPath = object.first;
1871 if (findInventoryItemForLed(*inventoryItems, ledPath) != nullptr)
1872 {
1873 // Add mapping from ledPath to connection
1874 const std::string& connection = object.second.begin()->first;
1875 (*ledConnections)[ledPath] = connection;
1876 BMCWEB_LOG_DEBUG << "Added mapping " << ledPath << " -> "
1877 << connection;
1878 }
1879 }
1880
1881 getInventoryLedData(sensorsAsyncResp, inventoryItems, ledConnections,
1882 std::move(callback));
1883 BMCWEB_LOG_DEBUG << "getInventoryLeds respHandler exit";
1884 };
1885 // Make call to ObjectMapper to find all inventory items
1886 crow::connections::systemBus->async_method_call(
1887 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
1888 "/xyz/openbmc_project/object_mapper",
1889 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 0, interfaces);
1890 BMCWEB_LOG_DEBUG << "getInventoryLeds exit";
1891}
1892
1893/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001894 * @brief Gets inventory items associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001895 *
1896 * Finds the inventory items that are associated with the specified sensors.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001897 * Then gets D-Bus data for the inventory items, such as presence and VPD.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001898 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001899 * This data is later used to provide sensor property values in the JSON
1900 * response.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001901 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001902 * Finds the inventory items asynchronously. Invokes callback when the
1903 * inventory items have been obtained.
1904 *
1905 * The callback must have the following signature:
1906 * @code
1907 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
1908 * @endcode
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001909 *
1910 * @param sensorsAsyncResp Pointer to object holding response data.
1911 * @param sensorNames All sensors within the current chassis.
1912 * @param objectMgrPaths Mappings from connection name to DBus object path that
1913 * implements ObjectManager.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001914 * @param callback Callback to invoke when inventory items have been obtained.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001915 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001916template <typename Callback>
1917static void getInventoryItems(
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001918 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1919 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
1920 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001921 objectMgrPaths,
1922 Callback&& callback)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001923{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001924 BMCWEB_LOG_DEBUG << "getInventoryItems enter";
1925 auto getInventoryItemAssociationsCb =
1926 [sensorsAsyncResp, objectMgrPaths, callback{std::move(callback)}](
1927 std::shared_ptr<std::vector<InventoryItem>> inventoryItems) {
1928 BMCWEB_LOG_DEBUG << "getInventoryItemAssociationsCb enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001929 auto getInventoryItemsConnectionsCb =
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001930 [sensorsAsyncResp, inventoryItems, objectMgrPaths,
1931 callback{std::move(callback)}](
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001932 std::shared_ptr<boost::container::flat_set<std::string>>
1933 invConnections) {
1934 BMCWEB_LOG_DEBUG << "getInventoryItemsConnectionsCb enter";
Anthony Wilsond5005492019-07-31 16:34:17 -05001935 auto getInventoryItemsDataCb =
1936 [sensorsAsyncResp, inventoryItems,
1937 callback{std::move(callback)}]() {
1938 BMCWEB_LOG_DEBUG << "getInventoryItemsDataCb enter";
1939 // Find led connections and get the data
1940 getInventoryLeds(sensorsAsyncResp, inventoryItems,
1941 std::move(callback));
1942 BMCWEB_LOG_DEBUG << "getInventoryItemsDataCb exit";
1943 };
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001944
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001945 // Get inventory item data from connections
1946 getInventoryItemsData(sensorsAsyncResp, inventoryItems,
1947 invConnections, objectMgrPaths,
Anthony Wilsond5005492019-07-31 16:34:17 -05001948 std::move(getInventoryItemsDataCb));
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001949 BMCWEB_LOG_DEBUG << "getInventoryItemsConnectionsCb exit";
1950 };
1951
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001952 // Get connections that provide inventory item data
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001953 getInventoryItemsConnections(
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001954 sensorsAsyncResp, inventoryItems,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001955 std::move(getInventoryItemsConnectionsCb));
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001956 BMCWEB_LOG_DEBUG << "getInventoryItemAssociationsCb exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001957 };
1958
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001959 // Get associations from sensors to inventory items
1960 getInventoryItemAssociations(sensorsAsyncResp, sensorNames, objectMgrPaths,
1961 std::move(getInventoryItemAssociationsCb));
1962 BMCWEB_LOG_DEBUG << "getInventoryItems exit";
1963}
1964
1965/**
1966 * @brief Returns JSON PowerSupply object for the specified inventory item.
1967 *
1968 * Searches for a JSON PowerSupply object that matches the specified inventory
1969 * item. If one is not found, a new PowerSupply object is added to the JSON
1970 * array.
1971 *
1972 * Multiple sensors are often associated with one power supply inventory item.
1973 * As a result, multiple sensor values are stored in one JSON PowerSupply
1974 * object.
1975 *
1976 * @param powerSupplyArray JSON array containing Redfish PowerSupply objects.
1977 * @param inventoryItem Inventory item for the power supply.
1978 * @param chassisId Chassis that contains the power supply.
1979 * @return JSON PowerSupply object for the specified inventory item.
1980 */
1981static nlohmann::json& getPowerSupply(nlohmann::json& powerSupplyArray,
1982 const InventoryItem& inventoryItem,
1983 const std::string& chassisId)
1984{
1985 // Check if matching PowerSupply object already exists in JSON array
1986 for (nlohmann::json& powerSupply : powerSupplyArray)
1987 {
1988 if (powerSupply["MemberId"] == inventoryItem.name)
1989 {
1990 return powerSupply;
1991 }
1992 }
1993
1994 // Add new PowerSupply object to JSON array
1995 powerSupplyArray.push_back({});
1996 nlohmann::json& powerSupply = powerSupplyArray.back();
1997 powerSupply["@odata.id"] =
1998 "/redfish/v1/Chassis/" + chassisId + "/Power#/PowerSupplies/";
1999 powerSupply["MemberId"] = inventoryItem.name;
2000 powerSupply["Name"] = boost::replace_all_copy(inventoryItem.name, "_", " ");
2001 powerSupply["Manufacturer"] = inventoryItem.manufacturer;
2002 powerSupply["Model"] = inventoryItem.model;
2003 powerSupply["PartNumber"] = inventoryItem.partNumber;
2004 powerSupply["SerialNumber"] = inventoryItem.serialNumber;
Anthony Wilsond5005492019-07-31 16:34:17 -05002005 setLedState(powerSupply, &inventoryItem);
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002006 powerSupply["Status"]["State"] = getState(&inventoryItem);
2007
2008 const char* health = inventoryItem.isFunctional ? "OK" : "Critical";
2009 powerSupply["Status"]["Health"] = health;
2010
2011 return powerSupply;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002012}
2013
2014/**
Shawn McCarneyde629b62019-03-08 10:42:51 -06002015 * @brief Gets the values of the specified sensors.
2016 *
2017 * Stores the results as JSON in the SensorsAsyncResp.
2018 *
2019 * Gets the sensor values asynchronously. Stores the results later when the
2020 * information has been obtained.
2021 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002022 * The sensorNames set contains all requested sensors for the current chassis.
Shawn McCarneyde629b62019-03-08 10:42:51 -06002023 *
2024 * To minimize the number of DBus calls, the DBus method
2025 * org.freedesktop.DBus.ObjectManager.GetManagedObjects() is used to get the
2026 * values of all sensors provided by a connection (service).
2027 *
2028 * The connections set contains all the connections that provide sensor values.
2029 *
2030 * The objectMgrPaths map contains mappings from a connection name to the
2031 * corresponding DBus object path that implements ObjectManager.
2032 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002033 * The InventoryItem vector contains D-Bus inventory items associated with the
2034 * sensors. Inventory item data is needed for some Redfish sensor properties.
2035 *
Shawn McCarneyde629b62019-03-08 10:42:51 -06002036 * @param SensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002037 * @param sensorNames All requested sensors within the current chassis.
Shawn McCarneyde629b62019-03-08 10:42:51 -06002038 * @param connections Connections that provide sensor values.
2039 * @param objectMgrPaths Mappings from connection name to DBus object path that
2040 * implements ObjectManager.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002041 * @param inventoryItems Inventory items associated with the sensors.
Shawn McCarneyde629b62019-03-08 10:42:51 -06002042 */
2043void getSensorData(
2044 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002045 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
Shawn McCarneyde629b62019-03-08 10:42:51 -06002046 const boost::container::flat_set<std::string>& connections,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002047 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002048 objectMgrPaths,
2049 std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
Shawn McCarneyde629b62019-03-08 10:42:51 -06002050{
2051 BMCWEB_LOG_DEBUG << "getSensorData enter";
2052 // Get managed objects from all services exposing sensors
2053 for (const std::string& connection : connections)
2054 {
2055 // Response handler to process managed objects
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002056 auto getManagedObjectsCb = [SensorsAsyncResp, sensorNames,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002057 inventoryItems](
Shawn McCarneyde629b62019-03-08 10:42:51 -06002058 const boost::system::error_code ec,
2059 ManagedObjectsVectorType& resp) {
2060 BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter";
2061 if (ec)
2062 {
2063 BMCWEB_LOG_ERROR << "getManagedObjectsCb DBUS error: " << ec;
2064 messages::internalError(SensorsAsyncResp->res);
2065 return;
2066 }
2067 // Go through all objects and update response with sensor data
2068 for (const auto& objDictEntry : resp)
2069 {
2070 const std::string& objPath =
2071 static_cast<const std::string&>(objDictEntry.first);
2072 BMCWEB_LOG_DEBUG << "getManagedObjectsCb parsing object "
2073 << objPath;
2074
Shawn McCarneyde629b62019-03-08 10:42:51 -06002075 std::vector<std::string> split;
2076 // Reserve space for
2077 // /xyz/openbmc_project/sensors/<name>/<subname>
2078 split.reserve(6);
2079 boost::algorithm::split(split, objPath, boost::is_any_of("/"));
2080 if (split.size() < 6)
2081 {
2082 BMCWEB_LOG_ERROR << "Got path that isn't long enough "
2083 << objPath;
2084 continue;
2085 }
2086 // These indexes aren't intuitive, as boost::split puts an empty
2087 // string at the beginning
2088 const std::string& sensorType = split[4];
2089 const std::string& sensorName = split[5];
2090 BMCWEB_LOG_DEBUG << "sensorName " << sensorName
2091 << " sensorType " << sensorType;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002092 if (sensorNames->find(objPath) == sensorNames->end())
Shawn McCarneyde629b62019-03-08 10:42:51 -06002093 {
2094 BMCWEB_LOG_ERROR << sensorName << " not in sensor list ";
2095 continue;
2096 }
2097
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002098 // Find inventory item (if any) associated with sensor
2099 InventoryItem* inventoryItem =
2100 findInventoryItemForSensor(inventoryItems, objPath);
2101
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002102 const std::string& sensorSchema =
2103 SensorsAsyncResp->chassisSubNode;
2104
2105 nlohmann::json* sensorJson = nullptr;
2106
2107 if (sensorSchema == "Sensors")
Shawn McCarneyde629b62019-03-08 10:42:51 -06002108 {
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002109 SensorsAsyncResp->res.jsonValue["@odata.id"] =
2110 "/redfish/v1/Chassis/" + SensorsAsyncResp->chassisId +
2111 "/" + SensorsAsyncResp->chassisSubNode + "/" +
2112 sensorName;
2113 sensorJson = &(SensorsAsyncResp->res.jsonValue);
Shawn McCarneyde629b62019-03-08 10:42:51 -06002114 }
2115 else
2116 {
Ed Tanous271584a2019-07-09 16:24:22 -07002117 std::string fieldName;
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002118 if (sensorType == "temperature")
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002119 {
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002120 fieldName = "Temperatures";
2121 }
2122 else if (sensorType == "fan" || sensorType == "fan_tach" ||
2123 sensorType == "fan_pwm")
2124 {
2125 fieldName = "Fans";
2126 }
2127 else if (sensorType == "voltage")
2128 {
2129 fieldName = "Voltages";
2130 }
2131 else if (sensorType == "power")
2132 {
2133 if (!sensorName.compare("total_power"))
2134 {
2135 fieldName = "PowerControl";
2136 }
2137 else if ((inventoryItem != nullptr) &&
2138 (inventoryItem->isPowerSupply))
2139 {
2140 fieldName = "PowerSupplies";
2141 }
2142 else
2143 {
2144 // Other power sensors are in SensorCollection
2145 continue;
2146 }
2147 }
2148 else
2149 {
2150 BMCWEB_LOG_ERROR << "Unsure how to handle sensorType "
2151 << sensorType;
2152 continue;
2153 }
2154
2155 nlohmann::json& tempArray =
2156 SensorsAsyncResp->res.jsonValue[fieldName];
2157 if (fieldName == "PowerControl")
2158 {
2159 if (tempArray.empty())
2160 {
2161 // Put multiple "sensors" into a single
2162 // PowerControl. Follows MemberId naming and
2163 // naming in power.hpp.
2164 tempArray.push_back(
2165 {{"@odata.id",
2166 "/redfish/v1/Chassis/" +
2167 SensorsAsyncResp->chassisId + "/" +
2168 SensorsAsyncResp->chassisSubNode + "#/" +
2169 fieldName + "/0"}});
2170 }
2171 sensorJson = &(tempArray.back());
2172 }
2173 else if (fieldName == "PowerSupplies")
2174 {
2175 if (inventoryItem != nullptr)
2176 {
2177 sensorJson =
2178 &(getPowerSupply(tempArray, *inventoryItem,
2179 SensorsAsyncResp->chassisId));
2180 }
2181 }
2182 else
2183 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002184 tempArray.push_back(
2185 {{"@odata.id",
2186 "/redfish/v1/Chassis/" +
2187 SensorsAsyncResp->chassisId + "/" +
2188 SensorsAsyncResp->chassisSubNode + "#/" +
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002189 fieldName + "/"}});
2190 sensorJson = &(tempArray.back());
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002191 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002192 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06002193
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002194 if (sensorJson != nullptr)
2195 {
2196 objectInterfacesToJson(sensorName, sensorType,
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002197 SensorsAsyncResp->chassisSubNode,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002198 objDictEntry.second, *sensorJson,
2199 inventoryItem);
2200 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06002201 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002202 if (SensorsAsyncResp.use_count() == 1)
James Feist8bd25cc2019-03-15 15:14:00 -07002203 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002204 sortJSONResponse(SensorsAsyncResp);
2205 if (SensorsAsyncResp->chassisSubNode == "Thermal")
2206 {
2207 populateFanRedundancy(SensorsAsyncResp);
2208 }
James Feist8bd25cc2019-03-15 15:14:00 -07002209 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06002210 BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit";
2211 };
2212
2213 // Find DBus object path that implements ObjectManager for the current
2214 // connection. If no mapping found, default to "/".
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002215 auto iter = objectMgrPaths->find(connection);
Shawn McCarneyde629b62019-03-08 10:42:51 -06002216 const std::string& objectMgrPath =
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002217 (iter != objectMgrPaths->end()) ? iter->second : "/";
Shawn McCarneyde629b62019-03-08 10:42:51 -06002218 BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
2219 << objectMgrPath;
2220
2221 crow::connections::systemBus->async_method_call(
2222 getManagedObjectsCb, connection, objectMgrPath,
2223 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
2224 };
2225 BMCWEB_LOG_DEBUG << "getSensorData exit";
2226}
2227
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002228void processSensorList(
2229 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
2230 std::shared_ptr<boost::container::flat_set<std::string>> sensorNames)
2231{
2232 auto getConnectionCb =
2233 [SensorsAsyncResp, sensorNames](
2234 const boost::container::flat_set<std::string>& connections) {
2235 BMCWEB_LOG_DEBUG << "getConnectionCb enter";
2236 auto getObjectManagerPathsCb =
2237 [SensorsAsyncResp, sensorNames, connections](
2238 std::shared_ptr<
2239 boost::container::flat_map<std::string, std::string>>
2240 objectMgrPaths) {
2241 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb enter";
2242 auto getInventoryItemsCb =
2243 [SensorsAsyncResp, sensorNames, connections,
2244 objectMgrPaths](
2245 std::shared_ptr<std::vector<InventoryItem>>
2246 inventoryItems) {
2247 BMCWEB_LOG_DEBUG << "getInventoryItemsCb enter";
2248 // Get sensor data and store results in JSON
2249 getSensorData(SensorsAsyncResp, sensorNames,
2250 connections, objectMgrPaths,
2251 inventoryItems);
2252 BMCWEB_LOG_DEBUG << "getInventoryItemsCb exit";
2253 };
2254
2255 // Get inventory items associated with sensors
2256 getInventoryItems(SensorsAsyncResp, sensorNames,
2257 objectMgrPaths,
2258 std::move(getInventoryItemsCb));
2259
2260 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb exit";
2261 };
2262
2263 // Get mapping from connection names to the DBus object
2264 // paths that implement the ObjectManager interface
2265 getObjectManagerPaths(SensorsAsyncResp,
2266 std::move(getObjectManagerPathsCb));
2267 BMCWEB_LOG_DEBUG << "getConnectionCb exit";
2268 };
2269
2270 // Get set of connections that provide sensor values
2271 getConnections(SensorsAsyncResp, sensorNames, std::move(getConnectionCb));
2272}
2273
Shawn McCarneyde629b62019-03-08 10:42:51 -06002274/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01002275 * @brief Entry point for retrieving sensors data related to requested
2276 * chassis.
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02002277 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01002278 */
Ed Tanous1abe55e2018-09-05 08:30:59 -07002279void getChassisData(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
2280{
2281 BMCWEB_LOG_DEBUG << "getChassisData enter";
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002282 auto getChassisCb =
2283 [SensorsAsyncResp](
2284 std::shared_ptr<boost::container::flat_set<std::string>>
2285 sensorNames) {
2286 BMCWEB_LOG_DEBUG << "getChassisCb enter";
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002287 processSensorList(SensorsAsyncResp, sensorNames);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002288 BMCWEB_LOG_DEBUG << "getChassisCb exit";
2289 };
Jennifer Lee4f9a2132019-03-04 12:45:19 -08002290 SensorsAsyncResp->res.jsonValue["Redundancy"] = nlohmann::json::array();
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01002291
Shawn McCarney26f03892019-05-03 13:20:24 -05002292 // Get set of sensors in chassis
Ed Tanous1abe55e2018-09-05 08:30:59 -07002293 getChassis(SensorsAsyncResp, std::move(getChassisCb));
2294 BMCWEB_LOG_DEBUG << "getChassisData exit";
Ed Tanous271584a2019-07-09 16:24:22 -07002295}
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01002296
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302297/**
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002298 * @brief Find the requested sensorName in the list of all sensors supplied by
2299 * the chassis node
2300 *
2301 * @param sensorName The sensor name supplied in the PATCH request
2302 * @param sensorsList The list of sensors managed by the chassis node
2303 * @param sensorsModified The list of sensors that were found as a result of
2304 * repeated calls to this function
2305 */
2306bool findSensorNameUsingSensorPath(
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05302307 std::string_view sensorName,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002308 boost::container::flat_set<std::string>& sensorsList,
2309 boost::container::flat_set<std::string>& sensorsModified)
2310{
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05302311 for (std::string_view chassisSensor : sensorsList)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002312 {
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05302313 std::size_t pos = chassisSensor.rfind("/");
2314 if (pos >= (chassisSensor.size() - 1))
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002315 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002316 continue;
2317 }
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05302318 std::string_view thisSensorName = chassisSensor.substr(pos + 1);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002319 if (thisSensorName == sensorName)
2320 {
2321 sensorsModified.emplace(chassisSensor);
2322 return true;
2323 }
2324 }
2325 return false;
2326}
2327
2328/**
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302329 * @brief Entry point for overriding sensor values of given sensor
2330 *
2331 * @param res response object
2332 * @param req request object
2333 * @param params parameter passed for CRUD
2334 * @param typeList TypeList of sensors for the resource queried
2335 * @param chassisSubNode Chassis Node for which the query has to happen
2336 */
2337void setSensorOverride(crow::Response& res, const crow::Request& req,
2338 const std::vector<std::string>& params,
Ed Tanous85e14242019-06-27 15:04:09 -07002339 const std::vector<const char*> typeList,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302340 const std::string& chassisSubNode)
2341{
2342
2343 // TODO: Need to figure out dynamic way to restrict patch (Set Sensor
2344 // override) based on another d-bus announcement to be more generic.
2345 if (params.size() != 1)
2346 {
2347 messages::internalError(res);
2348 res.end();
2349 return;
2350 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302351
2352 std::unordered_map<std::string, std::vector<nlohmann::json>> allCollections;
2353 std::optional<std::vector<nlohmann::json>> temperatureCollections;
2354 std::optional<std::vector<nlohmann::json>> fanCollections;
2355 std::vector<nlohmann::json> voltageCollections;
2356 BMCWEB_LOG_INFO << "setSensorOverride for subNode" << chassisSubNode
2357 << "\n";
2358
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302359 if (chassisSubNode == "Thermal")
2360 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302361 if (!json_util::readJson(req, res, "Temperatures",
2362 temperatureCollections, "Fans",
2363 fanCollections))
2364 {
2365 return;
2366 }
2367 if (!temperatureCollections && !fanCollections)
2368 {
2369 messages::resourceNotFound(res, "Thermal",
2370 "Temperatures / Voltages");
2371 res.end();
2372 return;
2373 }
2374 if (temperatureCollections)
2375 {
2376 allCollections.emplace("Temperatures",
2377 *std::move(temperatureCollections));
2378 }
2379 if (fanCollections)
2380 {
2381 allCollections.emplace("Fans", *std::move(fanCollections));
2382 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302383 }
2384 else if (chassisSubNode == "Power")
2385 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302386 if (!json_util::readJson(req, res, "Voltages", voltageCollections))
2387 {
2388 return;
2389 }
2390 allCollections.emplace("Voltages", std::move(voltageCollections));
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302391 }
2392 else
2393 {
2394 res.result(boost::beast::http::status::not_found);
2395 res.end();
2396 return;
2397 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302398
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302399 const char* propertyValueName;
2400 std::unordered_map<std::string, std::pair<double, std::string>> overrideMap;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302401 std::string memberId;
2402 double value;
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302403 for (auto& collectionItems : allCollections)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302404 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302405 if (collectionItems.first == "Temperatures")
2406 {
2407 propertyValueName = "ReadingCelsius";
2408 }
2409 else if (collectionItems.first == "Fans")
2410 {
2411 propertyValueName = "Reading";
2412 }
2413 else
2414 {
2415 propertyValueName = "ReadingVolts";
2416 }
2417 for (auto& item : collectionItems.second)
2418 {
2419 if (!json_util::readJson(item, res, "MemberId", memberId,
2420 propertyValueName, value))
2421 {
2422 return;
2423 }
2424 overrideMap.emplace(memberId,
2425 std::make_pair(value, collectionItems.first));
2426 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302427 }
2428 const std::string& chassisName = params[0];
2429 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
2430 res, chassisName, typeList, chassisSubNode);
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