blob: 3caaeedec96f29a11a91ff10363b8c8765530b86 [file] [log] [blame]
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
Anthony Wilson95a3eca2019-06-11 10:44:47 -050018#include "node.hpp"
19
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010020#include <boost/algorithm/string/predicate.hpp>
21#include <boost/algorithm/string/split.hpp>
22#include <boost/container/flat_map.hpp>
23#include <boost/range/algorithm/replace_copy_if.hpp>
Ed Tanous9e27a222019-10-24 13:46:39 -070024#include <cmath>
Ed Tanous1abe55e2018-09-05 08:30:59 -070025#include <dbus_singleton.hpp>
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +053026#include <utils/json_utils.hpp>
Ed Tanousabf2add2019-01-22 16:40:12 -080027#include <variant>
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010028
Ed Tanous1abe55e2018-09-05 08:30:59 -070029namespace redfish
30{
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010031
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010032using GetSubTreeType = std::vector<
33 std::pair<std::string,
34 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
35
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -050036using SensorVariant =
37 std::variant<int64_t, double, uint32_t, bool, std::string>;
Ed Tanousaa2e59c2018-04-12 12:17:20 -070038
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010039using ManagedObjectsVectorType = std::vector<std::pair<
Ed Tanousaa2e59c2018-04-12 12:17:20 -070040 sdbusplus::message::object_path,
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010041 boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -070042 std::string, boost::container::flat_map<std::string, SensorVariant>>>>;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010043
44/**
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020045 * SensorsAsyncResp
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010046 * Gathers data needed for response processing after async calls are done
47 */
Ed Tanous1abe55e2018-09-05 08:30:59 -070048class SensorsAsyncResp
49{
50 public:
Ed Tanous271584a2019-07-09 16:24:22 -070051 SensorsAsyncResp(crow::Response& response, const std::string& chassisIdIn,
52 const std::vector<const char*> typesIn,
Ed Tanous2474adf2018-09-05 16:31:16 -070053 const std::string& subNode) :
Ed Tanous43b761d2019-02-13 20:10:56 -080054 res(response),
Ed Tanous271584a2019-07-09 16:24:22 -070055 chassisId(chassisIdIn), types(typesIn), chassisSubNode(subNode)
Ed Tanous1abe55e2018-09-05 08:30:59 -070056 {
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010057 }
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020058
Ed Tanous1abe55e2018-09-05 08:30:59 -070059 ~SensorsAsyncResp()
60 {
61 if (res.result() == boost::beast::http::status::internal_server_error)
62 {
63 // Reset the json object to clear out any data that made it in
64 // before the error happened todo(ed) handle error condition with
65 // proper code
66 res.jsonValue = nlohmann::json::object();
67 }
68 res.end();
69 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010070
Ed Tanous1abe55e2018-09-05 08:30:59 -070071 crow::Response& res;
72 std::string chassisId{};
73 const std::vector<const char*> types;
Ed Tanous2474adf2018-09-05 16:31:16 -070074 std::string chassisSubNode{};
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010075};
76
77/**
Anthony Wilsond5005492019-07-31 16:34:17 -050078 * Possible states for physical inventory leds
79 */
80enum class LedState
81{
82 OFF,
83 ON,
84 BLINK,
85 UNKNOWN
86};
87
88/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -050089 * D-Bus inventory item associated with one or more sensors.
90 */
91class InventoryItem
92{
93 public:
94 InventoryItem(const std::string& objPath) :
95 objectPath(objPath), name(), isPresent(true), isFunctional(true),
Gunnar Mills42cbe532019-08-15 15:26:54 -050096 isPowerSupply(false), powerSupplyEfficiencyPercent(-1), manufacturer(),
97 model(), partNumber(), serialNumber(), sensors(), ledObjectPath(""),
Anthony Wilsond5005492019-07-31 16:34:17 -050098 ledState(LedState::UNKNOWN)
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -050099 {
100 // Set inventory item name to last node of object path
101 auto pos = objectPath.rfind('/');
102 if ((pos != std::string::npos) && ((pos + 1) < objectPath.size()))
103 {
104 name = objectPath.substr(pos + 1);
105 }
106 }
107
108 std::string objectPath;
109 std::string name;
110 bool isPresent;
111 bool isFunctional;
112 bool isPowerSupply;
Gunnar Mills42cbe532019-08-15 15:26:54 -0500113 int powerSupplyEfficiencyPercent;
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500114 std::string manufacturer;
115 std::string model;
116 std::string partNumber;
117 std::string serialNumber;
118 std::set<std::string> sensors;
Anthony Wilsond5005492019-07-31 16:34:17 -0500119 std::string ledObjectPath;
120 LedState ledState;
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500121};
122
123/**
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530124 * @brief Get objects with connection necessary for sensors
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200125 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100126 * @param sensorNames Sensors retrieved from chassis
127 * @param callback Callback for processing gathered connections
128 */
129template <typename Callback>
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530130void getObjectsWithConnection(
131 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700132 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530133 Callback&& callback)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700134{
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530135 BMCWEB_LOG_DEBUG << "getObjectsWithConnection enter";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700136 const std::string path = "/xyz/openbmc_project/sensors";
137 const std::array<std::string, 1> interfaces = {
138 "xyz.openbmc_project.Sensor.Value"};
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100139
Ed Tanous1abe55e2018-09-05 08:30:59 -0700140 // Response handler for parsing objects subtree
141 auto respHandler = [callback{std::move(callback)}, SensorsAsyncResp,
142 sensorNames](const boost::system::error_code ec,
143 const GetSubTreeType& subtree) {
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530144 BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler enter";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700145 if (ec)
146 {
Ed Tanous5f7d88c2018-11-14 14:08:56 -0800147 messages::internalError(SensorsAsyncResp->res);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530148 BMCWEB_LOG_ERROR
149 << "getObjectsWithConnection resp_handler: Dbus error " << ec;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700150 return;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100151 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100152
Ed Tanous1abe55e2018-09-05 08:30:59 -0700153 BMCWEB_LOG_DEBUG << "Found " << subtree.size() << " subtrees";
154
155 // Make unique list of connections only for requested sensor types and
156 // found in the chassis
157 boost::container::flat_set<std::string> connections;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530158 std::set<std::pair<std::string, std::string>> objectsWithConnection;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700159 // Intrinsic to avoid malloc. Most systems will have < 8 sensor
160 // producers
161 connections.reserve(8);
162
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700163 BMCWEB_LOG_DEBUG << "sensorNames list count: " << sensorNames->size();
164 for (const std::string& tsensor : *sensorNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700165 {
166 BMCWEB_LOG_DEBUG << "Sensor to find: " << tsensor;
167 }
168
169 for (const std::pair<
170 std::string,
171 std::vector<std::pair<std::string, std::vector<std::string>>>>&
172 object : subtree)
173 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700174 if (sensorNames->find(object.first) != sensorNames->end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700175 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700176 for (const std::pair<std::string, std::vector<std::string>>&
177 objData : object.second)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700178 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700179 BMCWEB_LOG_DEBUG << "Adding connection: " << objData.first;
180 connections.insert(objData.first);
181 objectsWithConnection.insert(
182 std::make_pair(object.first, objData.first));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700183 }
184 }
185 }
186 BMCWEB_LOG_DEBUG << "Found " << connections.size() << " connections";
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530187 callback(std::move(connections), std::move(objectsWithConnection));
188 BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler exit";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700189 };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700190 // Make call to ObjectMapper to find all sensors objects
191 crow::connections::systemBus->async_method_call(
192 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
193 "/xyz/openbmc_project/object_mapper",
194 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 2, interfaces);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530195 BMCWEB_LOG_DEBUG << "getObjectsWithConnection exit";
196}
197
198/**
199 * @brief Create connections necessary for sensors
200 * @param SensorsAsyncResp Pointer to object holding response data
201 * @param sensorNames Sensors retrieved from chassis
202 * @param callback Callback for processing gathered connections
203 */
204template <typename Callback>
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700205void getConnections(
206 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
207 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
208 Callback&& callback)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530209{
210 auto objectsWithConnectionCb =
211 [callback](const boost::container::flat_set<std::string>& connections,
212 const std::set<std::pair<std::string, std::string>>&
213 objectsWithConnection) {
214 callback(std::move(connections));
215 };
216 getObjectsWithConnection(SensorsAsyncResp, sensorNames,
217 std::move(objectsWithConnectionCb));
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100218}
219
220/**
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700221 * @brief Shrinks the list of sensors for processing
222 * @param SensorsAysncResp The class holding the Redfish response
223 * @param allSensors A list of all the sensors associated to the
224 * chassis element (i.e. baseboard, front panel, etc...)
225 * @param activeSensors A list that is a reduction of the incoming
226 * allSensors list. Eliminate Thermal sensors when a Power request is
227 * made, and eliminate Power sensors when a Thermal request is made.
228 */
229void reduceSensorList(
230 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
231 const std::vector<std::string>* allSensors,
232 std::shared_ptr<boost::container::flat_set<std::string>> activeSensors)
233{
234 if (SensorsAsyncResp == nullptr)
235 {
236 return;
237 }
238 if ((allSensors == nullptr) || (activeSensors == nullptr))
239 {
240 messages::resourceNotFound(
241 SensorsAsyncResp->res, SensorsAsyncResp->chassisSubNode,
242 SensorsAsyncResp->chassisSubNode == "Thermal" ? "Temperatures"
243 : "Voltages");
244
245 return;
246 }
247 if (allSensors->empty())
248 {
249 // Nothing to do, the activeSensors object is also empty
250 return;
251 }
252
253 for (const char* type : SensorsAsyncResp->types)
254 {
255 for (const std::string& sensor : *allSensors)
256 {
257 if (boost::starts_with(sensor, type))
258 {
259 activeSensors->emplace(sensor);
260 }
261 }
262 }
263}
264
265/**
Carol Wang4bb3dc32019-10-17 18:15:02 +0800266 * @brief Retrieves valid chassis path
267 * @param asyncResp Pointer to object holding response data
268 * @param callback Callback for next step to get valid chassis path
269 */
270template <typename Callback>
271void getValidChassisPath(std::shared_ptr<SensorsAsyncResp> asyncResp,
272 Callback&& callback)
273{
274 BMCWEB_LOG_DEBUG << "checkChassisId enter";
275 const std::array<const char*, 2> interfaces = {
276 "xyz.openbmc_project.Inventory.Item.Board",
277 "xyz.openbmc_project.Inventory.Item.Chassis"};
278
279 auto respHandler =
280 [callback{std::move(callback)},
281 asyncResp](const boost::system::error_code ec,
282 const std::vector<std::string>& chassisPaths) mutable {
283 BMCWEB_LOG_DEBUG << "getValidChassisPath respHandler enter";
284 if (ec)
285 {
286 BMCWEB_LOG_ERROR
287 << "getValidChassisPath respHandler DBUS error: " << ec;
288 messages::internalError(asyncResp->res);
289 return;
290 }
291
292 std::optional<std::string> chassisPath;
293 std::string chassisName;
294 for (const std::string& chassis : chassisPaths)
295 {
296 std::size_t lastPos = chassis.rfind("/");
297 if (lastPos == std::string::npos)
298 {
299 BMCWEB_LOG_ERROR << "Failed to find '/' in " << chassis;
300 continue;
301 }
302 chassisName = chassis.substr(lastPos + 1);
303 if (chassisName == asyncResp->chassisId)
304 {
305 chassisPath = chassis;
306 break;
307 }
308 }
309 callback(chassisPath);
310 };
311
312 // Get the Chassis Collection
313 crow::connections::systemBus->async_method_call(
314 respHandler, "xyz.openbmc_project.ObjectMapper",
315 "/xyz/openbmc_project/object_mapper",
316 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
317 "/xyz/openbmc_project/inventory", 0, interfaces);
318 BMCWEB_LOG_DEBUG << "checkChassisId exit";
319}
320
321/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100322 * @brief Retrieves requested chassis sensors and redundancy data from DBus .
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200323 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100324 * @param callback Callback for next step in gathered sensor processing
325 */
326template <typename Callback>
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700327void getChassis(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700328 Callback&& callback)
329{
330 BMCWEB_LOG_DEBUG << "getChassis enter";
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500331 const std::array<const char*, 2> interfaces = {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700332 "xyz.openbmc_project.Inventory.Item.Board",
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500333 "xyz.openbmc_project.Inventory.Item.Chassis"};
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700334 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp](
335 const boost::system::error_code ec,
336 const std::vector<std::string>& chassisPaths) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700337 BMCWEB_LOG_DEBUG << "getChassis respHandler enter";
338 if (ec)
339 {
340 BMCWEB_LOG_ERROR << "getChassis respHandler DBUS error: " << ec;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700341 messages::internalError(sensorsAsyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700342 return;
343 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100344
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700345 const std::string* chassisPath = nullptr;
346 std::string chassisName;
347 for (const std::string& chassis : chassisPaths)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700348 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700349 std::size_t lastPos = chassis.rfind("/");
350 if (lastPos == std::string::npos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700351 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700352 BMCWEB_LOG_ERROR << "Failed to find '/' in " << chassis;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700353 continue;
354 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700355 chassisName = chassis.substr(lastPos + 1);
356 if (chassisName == sensorsAsyncResp->chassisId)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700357 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700358 chassisPath = &chassis;
359 break;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700360 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700361 }
362 if (chassisPath == nullptr)
363 {
364 messages::resourceNotFound(sensorsAsyncResp->res, "Chassis",
365 sensorsAsyncResp->chassisId);
366 return;
367 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700368
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700369 const std::string& chassisSubNode = sensorsAsyncResp->chassisSubNode;
370 if (chassisSubNode == "Power")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700371 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700372 sensorsAsyncResp->res.jsonValue["@odata.type"] =
373 "#Power.v1_5_2.Power";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700374 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700375 else if (chassisSubNode == "Thermal")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700376 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700377 sensorsAsyncResp->res.jsonValue["@odata.type"] =
378 "#Thermal.v1_4_0.Thermal";
Jennifer Lee4f9a2132019-03-04 12:45:19 -0800379 sensorsAsyncResp->res.jsonValue["Fans"] = nlohmann::json::array();
380 sensorsAsyncResp->res.jsonValue["Temperatures"] =
381 nlohmann::json::array();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700382 }
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500383 else if (chassisSubNode == "Sensors")
384 {
385 sensorsAsyncResp->res.jsonValue["@odata.type"] =
386 "#SensorCollection.SensorCollection";
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500387 sensorsAsyncResp->res.jsonValue["Description"] =
388 "Collection of Sensors for this Chassis";
389 sensorsAsyncResp->res.jsonValue["Members"] =
390 nlohmann::json::array();
391 sensorsAsyncResp->res.jsonValue["Members@odata.count"] = 0;
392 }
393
394 if (chassisSubNode != "Sensors")
395 {
396 sensorsAsyncResp->res.jsonValue["Id"] = chassisSubNode;
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500397 }
398
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700399 sensorsAsyncResp->res.jsonValue["@odata.id"] =
400 "/redfish/v1/Chassis/" + sensorsAsyncResp->chassisId + "/" +
401 chassisSubNode;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700402 sensorsAsyncResp->res.jsonValue["Name"] = chassisSubNode;
403
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500404 // Get the list of all sensors for this Chassis element
405 std::string sensorPath = *chassisPath + "/all_sensors";
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700406 crow::connections::systemBus->async_method_call(
407 [sensorsAsyncResp, callback{std::move(callback)}](
Ed Tanous271584a2019-07-09 16:24:22 -0700408 const boost::system::error_code& e,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700409 const std::variant<std::vector<std::string>>&
410 variantEndpoints) {
Ed Tanous271584a2019-07-09 16:24:22 -0700411 if (e)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700412 {
Ed Tanous271584a2019-07-09 16:24:22 -0700413 if (e.value() != EBADR)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700414 {
415 messages::internalError(sensorsAsyncResp->res);
416 return;
417 }
418 }
419 const std::vector<std::string>* nodeSensorList =
420 std::get_if<std::vector<std::string>>(&(variantEndpoints));
421 if (nodeSensorList == nullptr)
422 {
423 messages::resourceNotFound(
424 sensorsAsyncResp->res, sensorsAsyncResp->chassisSubNode,
425 sensorsAsyncResp->chassisSubNode == "Thermal"
426 ? "Temperatures"
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500427 : sensorsAsyncResp->chassisSubNode == "Power"
428 ? "Voltages"
429 : "Sensors");
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700430 return;
431 }
432 const std::shared_ptr<boost::container::flat_set<std::string>>
433 culledSensorList = std::make_shared<
434 boost::container::flat_set<std::string>>();
435 reduceSensorList(sensorsAsyncResp, nodeSensorList,
436 culledSensorList);
437 callback(culledSensorList);
438 },
439 "xyz.openbmc_project.ObjectMapper", sensorPath,
440 "org.freedesktop.DBus.Properties", "Get",
441 "xyz.openbmc_project.Association", "endpoints");
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100442 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100443
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700444 // Get the Chassis Collection
Ed Tanous1abe55e2018-09-05 08:30:59 -0700445 crow::connections::systemBus->async_method_call(
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700446 respHandler, "xyz.openbmc_project.ObjectMapper",
447 "/xyz/openbmc_project/object_mapper",
448 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Ed Tanous271584a2019-07-09 16:24:22 -0700449 "/xyz/openbmc_project/inventory", 0, interfaces);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700450 BMCWEB_LOG_DEBUG << "getChassis exit";
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100451}
452
453/**
Shawn McCarneyde629b62019-03-08 10:42:51 -0600454 * @brief Finds all DBus object paths that implement ObjectManager.
455 *
456 * Creates a mapping from the associated connection name to the object path.
457 *
458 * Finds the object paths asynchronously. Invokes callback when information has
459 * been obtained.
460 *
461 * The callback must have the following signature:
462 * @code
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500463 * callback(std::shared_ptr<boost::container::flat_map<std::string,
464 * std::string>> objectMgrPaths)
Shawn McCarneyde629b62019-03-08 10:42:51 -0600465 * @endcode
466 *
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700467 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyde629b62019-03-08 10:42:51 -0600468 * @param callback Callback to invoke when object paths obtained.
469 */
470template <typename Callback>
471void getObjectManagerPaths(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
472 Callback&& callback)
473{
474 BMCWEB_LOG_DEBUG << "getObjectManagerPaths enter";
475 const std::array<std::string, 1> interfaces = {
476 "org.freedesktop.DBus.ObjectManager"};
477
478 // Response handler for GetSubTree DBus method
479 auto respHandler = [callback{std::move(callback)},
480 SensorsAsyncResp](const boost::system::error_code ec,
481 const GetSubTreeType& subtree) {
482 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler enter";
483 if (ec)
484 {
485 messages::internalError(SensorsAsyncResp->res);
486 BMCWEB_LOG_ERROR << "getObjectManagerPaths respHandler: DBus error "
487 << ec;
488 return;
489 }
490
491 // Loop over returned object paths
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500492 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
493 objectMgrPaths = std::make_shared<
494 boost::container::flat_map<std::string, std::string>>();
Shawn McCarneyde629b62019-03-08 10:42:51 -0600495 for (const std::pair<
496 std::string,
497 std::vector<std::pair<std::string, std::vector<std::string>>>>&
498 object : subtree)
499 {
500 // Loop over connections for current object path
501 const std::string& objectPath = object.first;
502 for (const std::pair<std::string, std::vector<std::string>>&
503 objData : object.second)
504 {
505 // Add mapping from connection to object path
506 const std::string& connection = objData.first;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500507 (*objectMgrPaths)[connection] = objectPath;
Shawn McCarneyde629b62019-03-08 10:42:51 -0600508 BMCWEB_LOG_DEBUG << "Added mapping " << connection << " -> "
509 << objectPath;
510 }
511 }
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500512 callback(objectMgrPaths);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600513 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler exit";
514 };
515
516 // Query mapper for all DBus object paths that implement ObjectManager
517 crow::connections::systemBus->async_method_call(
518 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
519 "/xyz/openbmc_project/object_mapper",
Ed Tanous271584a2019-07-09 16:24:22 -0700520 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0, interfaces);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600521 BMCWEB_LOG_DEBUG << "getObjectManagerPaths exit";
522}
523
524/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500525 * @brief Returns the Redfish State value for the specified inventory item.
526 * @param inventoryItem D-Bus inventory item associated with a sensor.
527 * @return State value for inventory item.
James Feist34dd1792019-05-17 14:10:54 -0700528 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500529static std::string getState(const InventoryItem* inventoryItem)
530{
531 if ((inventoryItem != nullptr) && !(inventoryItem->isPresent))
532 {
533 return "Absent";
534 }
James Feist34dd1792019-05-17 14:10:54 -0700535
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500536 return "Enabled";
537}
538
539/**
540 * @brief Returns the Redfish Health value for the specified sensor.
541 * @param sensorJson Sensor JSON object.
542 * @param interfacesDict Map of all sensor interfaces.
543 * @param inventoryItem D-Bus inventory item associated with the sensor. Will
544 * be nullptr if no associated inventory item was found.
545 * @return Health value for sensor.
546 */
James Feist34dd1792019-05-17 14:10:54 -0700547static std::string getHealth(
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500548 nlohmann::json& sensorJson,
James Feist34dd1792019-05-17 14:10:54 -0700549 const boost::container::flat_map<
550 std::string, boost::container::flat_map<std::string, SensorVariant>>&
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500551 interfacesDict,
552 const InventoryItem* inventoryItem)
James Feist34dd1792019-05-17 14:10:54 -0700553{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500554 // Get current health value (if any) in the sensor JSON object. Some JSON
555 // objects contain multiple sensors (such as PowerSupplies). We want to set
556 // the overall health to be the most severe of any of the sensors.
557 std::string currentHealth;
558 auto statusIt = sensorJson.find("Status");
559 if (statusIt != sensorJson.end())
560 {
561 auto healthIt = statusIt->find("Health");
562 if (healthIt != statusIt->end())
563 {
564 std::string* health = healthIt->get_ptr<std::string*>();
565 if (health != nullptr)
566 {
567 currentHealth = *health;
568 }
569 }
570 }
571
572 // If current health in JSON object is already Critical, return that. This
573 // should override the sensor health, which might be less severe.
574 if (currentHealth == "Critical")
575 {
576 return "Critical";
577 }
578
579 // Check if sensor has critical threshold alarm
James Feist34dd1792019-05-17 14:10:54 -0700580 auto criticalThresholdIt =
581 interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Critical");
582 if (criticalThresholdIt != interfacesDict.end())
583 {
584 auto thresholdHighIt =
585 criticalThresholdIt->second.find("CriticalAlarmHigh");
586 auto thresholdLowIt =
587 criticalThresholdIt->second.find("CriticalAlarmLow");
588 if (thresholdHighIt != criticalThresholdIt->second.end())
589 {
590 const bool* asserted = std::get_if<bool>(&thresholdHighIt->second);
591 if (asserted == nullptr)
592 {
593 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
594 }
595 else if (*asserted)
596 {
597 return "Critical";
598 }
599 }
600 if (thresholdLowIt != criticalThresholdIt->second.end())
601 {
602 const bool* asserted = std::get_if<bool>(&thresholdLowIt->second);
603 if (asserted == nullptr)
604 {
605 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
606 }
607 else if (*asserted)
608 {
609 return "Critical";
610 }
611 }
612 }
613
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500614 // Check if associated inventory item is not functional
615 if ((inventoryItem != nullptr) && !(inventoryItem->isFunctional))
616 {
617 return "Critical";
618 }
619
620 // If current health in JSON object is already Warning, return that. This
621 // should override the sensor status, which might be less severe.
622 if (currentHealth == "Warning")
623 {
624 return "Warning";
625 }
626
627 // Check if sensor has warning threshold alarm
James Feist34dd1792019-05-17 14:10:54 -0700628 auto warningThresholdIt =
629 interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Warning");
630 if (warningThresholdIt != interfacesDict.end())
631 {
632 auto thresholdHighIt =
633 warningThresholdIt->second.find("WarningAlarmHigh");
634 auto thresholdLowIt =
635 warningThresholdIt->second.find("WarningAlarmLow");
636 if (thresholdHighIt != warningThresholdIt->second.end())
637 {
638 const bool* asserted = std::get_if<bool>(&thresholdHighIt->second);
639 if (asserted == nullptr)
640 {
641 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
642 }
643 else if (*asserted)
644 {
645 return "Warning";
646 }
647 }
648 if (thresholdLowIt != warningThresholdIt->second.end())
649 {
650 const bool* asserted = std::get_if<bool>(&thresholdLowIt->second);
651 if (asserted == nullptr)
652 {
653 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
654 }
655 else if (*asserted)
656 {
657 return "Warning";
658 }
659 }
660 }
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500661
James Feist34dd1792019-05-17 14:10:54 -0700662 return "OK";
663}
664
Anthony Wilsond5005492019-07-31 16:34:17 -0500665static void setLedState(nlohmann::json& sensorJson,
666 const InventoryItem* inventoryItem)
667{
668 if (inventoryItem != nullptr && !inventoryItem->ledObjectPath.empty())
669 {
670 switch (inventoryItem->ledState)
671 {
672 case LedState::OFF:
673 sensorJson["IndicatorLED"] = "Off";
674 break;
675 case LedState::ON:
676 sensorJson["IndicatorLED"] = "Lit";
677 break;
678 case LedState::BLINK:
679 sensorJson["IndicatorLED"] = "Blinking";
680 break;
681 default:
682 break;
683 }
684 }
685}
686
James Feist34dd1792019-05-17 14:10:54 -0700687/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100688 * @brief Builds a json sensor representation of a sensor.
689 * @param sensorName The name of the sensor to be built
Gunnar Mills274fad52018-06-13 15:45:36 -0500690 * @param sensorType The type (temperature, fan_tach, etc) of the sensor to
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100691 * build
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500692 * @param sensorSchema The schema (Power, Thermal, etc) being associated with
693 * the sensor to build
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100694 * @param interfacesDict A dictionary of the interfaces and properties of said
695 * interfaces to be built from
696 * @param sensor_json The json object to fill
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500697 * @param inventoryItem D-Bus inventory item associated with the sensor. Will
698 * be nullptr if no associated inventory item was found.
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100699 */
700void objectInterfacesToJson(
701 const std::string& sensorName, const std::string& sensorType,
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500702 const std::string& sensorSchema,
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100703 const boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700704 std::string, boost::container::flat_map<std::string, SensorVariant>>&
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100705 interfacesDict,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500706 nlohmann::json& sensor_json, InventoryItem* inventoryItem)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700707{
708 // We need a value interface before we can do anything with it
709 auto valueIt = interfacesDict.find("xyz.openbmc_project.Sensor.Value");
710 if (valueIt == interfacesDict.end())
711 {
712 BMCWEB_LOG_ERROR << "Sensor doesn't have a value interface";
713 return;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100714 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100715
Ed Tanous1abe55e2018-09-05 08:30:59 -0700716 // Assume values exist as is (10^0 == 1) if no scale exists
717 int64_t scaleMultiplier = 0;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100718
Ed Tanous1abe55e2018-09-05 08:30:59 -0700719 auto scaleIt = valueIt->second.find("Scale");
720 // If a scale exists, pull value as int64, and use the scaling.
721 if (scaleIt != valueIt->second.end())
722 {
Ed Tanousabf2add2019-01-22 16:40:12 -0800723 const int64_t* int64Value = std::get_if<int64_t>(&scaleIt->second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700724 if (int64Value != nullptr)
725 {
726 scaleMultiplier = *int64Value;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100727 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100728 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700729
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500730 if (sensorSchema == "Sensors")
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500731 {
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500732 // For sensors in SensorCollection we set Id instead of MemberId,
733 // including power sensors.
734 sensor_json["Id"] = sensorName;
735 sensor_json["Name"] = boost::replace_all_copy(sensorName, "_", " ");
736 }
737 else if (sensorType != "power")
738 {
739 // Set MemberId and Name for non-power sensors. For PowerSupplies and
740 // PowerControl, those properties have more general values because
741 // multiple sensors can be stored in the same JSON object.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500742 sensor_json["MemberId"] = sensorName;
743 sensor_json["Name"] = boost::replace_all_copy(sensorName, "_", " ");
744 }
Ed Tanouse742b6c2019-05-03 15:06:53 -0700745
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500746 sensor_json["Status"]["State"] = getState(inventoryItem);
747 sensor_json["Status"]["Health"] =
748 getHealth(sensor_json, interfacesDict, inventoryItem);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700749
750 // Parameter to set to override the type we get from dbus, and force it to
751 // int, regardless of what is available. This is used for schemas like fan,
752 // that require integers, not floats.
753 bool forceToInt = false;
754
Anthony Wilson3929aca2019-07-19 15:42:33 -0500755 nlohmann::json::json_pointer unit("/Reading");
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500756 if (sensorSchema == "Sensors")
757 {
758 sensor_json["@odata.type"] = "#Sensor.v1_0_0.Sensor";
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500759 if (sensorType == "power")
760 {
761 sensor_json["ReadingUnits"] = "Watts";
762 }
763 else if (sensorType == "current")
764 {
765 sensor_json["ReadingUnits"] = "Amperes";
766 }
767 }
768 else if (sensorType == "temperature")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700769 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500770 unit = "/ReadingCelsius"_json_pointer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700771 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Temperature";
772 // TODO(ed) Documentation says that path should be type fan_tach,
773 // implementation seems to implement fan
774 }
775 else if (sensorType == "fan" || sensorType == "fan_tach")
776 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500777 unit = "/Reading"_json_pointer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700778 sensor_json["ReadingUnits"] = "RPM";
779 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
Anthony Wilsond5005492019-07-31 16:34:17 -0500780 setLedState(sensor_json, inventoryItem);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700781 forceToInt = true;
782 }
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700783 else if (sensorType == "fan_pwm")
784 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500785 unit = "/Reading"_json_pointer;
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700786 sensor_json["ReadingUnits"] = "Percent";
787 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
Anthony Wilsond5005492019-07-31 16:34:17 -0500788 setLedState(sensor_json, inventoryItem);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700789 forceToInt = true;
790 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700791 else if (sensorType == "voltage")
792 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500793 unit = "/ReadingVolts"_json_pointer;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700794 sensor_json["@odata.type"] = "#Power.v1_0_0.Voltage";
795 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700796 else if (sensorType == "power")
797 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700798 std::string sensorNameLower =
799 boost::algorithm::to_lower_copy(sensorName);
800
Eddie James028f7eb2019-05-17 21:24:36 +0000801 if (!sensorName.compare("total_power"))
802 {
Gunnar Mills7ab06f42019-07-02 13:07:16 -0500803 sensor_json["@odata.type"] = "#Power.v1_0_0.PowerControl";
804 // Put multiple "sensors" into a single PowerControl, so have
805 // generic names for MemberId and Name. Follows Redfish mockup.
806 sensor_json["MemberId"] = "0";
807 sensor_json["Name"] = "Chassis Power Control";
Anthony Wilson3929aca2019-07-19 15:42:33 -0500808 unit = "/PowerConsumedWatts"_json_pointer;
Eddie James028f7eb2019-05-17 21:24:36 +0000809 }
810 else if (sensorNameLower.find("input") != std::string::npos)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700811 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500812 unit = "/PowerInputWatts"_json_pointer;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700813 }
814 else
815 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500816 unit = "/PowerOutputWatts"_json_pointer;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700817 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700818 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700819 else
820 {
821 BMCWEB_LOG_ERROR << "Redfish cannot map object type for " << sensorName;
822 return;
823 }
824 // Map of dbus interface name, dbus property name and redfish property_name
Anthony Wilson3929aca2019-07-19 15:42:33 -0500825 std::vector<
826 std::tuple<const char*, const char*, nlohmann::json::json_pointer>>
827 properties;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700828 properties.reserve(7);
829
830 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600831
Anthony Wilson3929aca2019-07-19 15:42:33 -0500832 if (sensorSchema == "Sensors")
833 {
834 properties.emplace_back(
835 "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningHigh",
836 "/Thresholds/UpperCaution/Reading"_json_pointer);
837 properties.emplace_back(
838 "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningLow",
839 "/Thresholds/LowerCaution/Reading"_json_pointer);
840 properties.emplace_back(
841 "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalHigh",
842 "/Thresholds/UpperCritical/Reading"_json_pointer);
843 properties.emplace_back(
844 "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalLow",
845 "/Thresholds/LowerCritical/Reading"_json_pointer);
846 }
847 else if (sensorType != "power")
Shawn McCarneyde629b62019-03-08 10:42:51 -0600848 {
849 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500850 "WarningHigh",
851 "/UpperThresholdNonCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600852 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500853 "WarningLow",
854 "/LowerThresholdNonCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600855 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500856 "CriticalHigh",
857 "/UpperThresholdCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600858 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500859 "CriticalLow",
860 "/LowerThresholdCritical"_json_pointer);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600861 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700862
Ed Tanous2474adf2018-09-05 16:31:16 -0700863 // TODO Need to get UpperThresholdFatal and LowerThresholdFatal
864
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500865 if (sensorSchema == "Sensors")
866 {
867 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500868 "/ReadingRangeMin"_json_pointer);
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500869 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500870 "/ReadingRangeMax"_json_pointer);
Anthony Wilson95a3eca2019-06-11 10:44:47 -0500871 }
872 else if (sensorType == "temperature")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700873 {
874 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500875 "/MinReadingRangeTemp"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700876 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500877 "/MaxReadingRangeTemp"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700878 }
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -0500879 else if (sensorType != "power")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700880 {
881 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500882 "/MinReadingRange"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700883 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
Anthony Wilson3929aca2019-07-19 15:42:33 -0500884 "/MaxReadingRange"_json_pointer);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700885 }
886
Anthony Wilson3929aca2019-07-19 15:42:33 -0500887 for (const std::tuple<const char*, const char*,
888 nlohmann::json::json_pointer>& p : properties)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700889 {
890 auto interfaceProperties = interfacesDict.find(std::get<0>(p));
891 if (interfaceProperties != interfacesDict.end())
892 {
Ed Tanous271584a2019-07-09 16:24:22 -0700893 auto thisValueIt = interfaceProperties->second.find(std::get<1>(p));
894 if (thisValueIt != interfaceProperties->second.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700895 {
Ed Tanous271584a2019-07-09 16:24:22 -0700896 const SensorVariant& valueVariant = thisValueIt->second;
Anthony Wilson3929aca2019-07-19 15:42:33 -0500897
898 // The property we want to set may be nested json, so use
899 // a json_pointer for easy indexing into the json structure.
900 const nlohmann::json::json_pointer& key = std::get<2>(p);
901
Ed Tanous1abe55e2018-09-05 08:30:59 -0700902 // Attempt to pull the int64 directly
Ed Tanousabf2add2019-01-22 16:40:12 -0800903 const int64_t* int64Value = std::get_if<int64_t>(&valueVariant);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700904
Ed Tanousabf2add2019-01-22 16:40:12 -0800905 const double* doubleValue = std::get_if<double>(&valueVariant);
Eddie James028f7eb2019-05-17 21:24:36 +0000906 const uint32_t* uValue = std::get_if<uint32_t>(&valueVariant);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700907 double temp = 0.0;
908 if (int64Value != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700909 {
Ed Tanous271584a2019-07-09 16:24:22 -0700910 temp = static_cast<double>(*int64Value);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700911 }
912 else if (doubleValue != nullptr)
913 {
914 temp = *doubleValue;
915 }
Eddie James028f7eb2019-05-17 21:24:36 +0000916 else if (uValue != nullptr)
917 {
918 temp = *uValue;
919 }
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700920 else
921 {
922 BMCWEB_LOG_ERROR
923 << "Got value interface that wasn't int or double";
924 continue;
925 }
926 temp = temp * std::pow(10, scaleMultiplier);
927 if (forceToInt)
928 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500929 sensor_json[key] = static_cast<int64_t>(temp);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700930 }
931 else
932 {
Anthony Wilson3929aca2019-07-19 15:42:33 -0500933 sensor_json[key] = temp;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700934 }
935 }
936 }
937 }
938 BMCWEB_LOG_DEBUG << "Added sensor " << sensorName;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100939}
940
James Feist8bd25cc2019-03-15 15:14:00 -0700941static void
942 populateFanRedundancy(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp)
943{
944 crow::connections::systemBus->async_method_call(
945 [sensorsAsyncResp](const boost::system::error_code ec,
946 const GetSubTreeType& resp) {
947 if (ec)
948 {
949 return; // don't have to have this interface
950 }
Ed Tanouse278c182019-03-13 16:23:37 -0700951 for (const std::pair<std::string,
952 std::vector<std::pair<
953 std::string, std::vector<std::string>>>>&
954 pathPair : resp)
James Feist8bd25cc2019-03-15 15:14:00 -0700955 {
Ed Tanouse278c182019-03-13 16:23:37 -0700956 const std::string& path = pathPair.first;
957 const std::vector<
958 std::pair<std::string, std::vector<std::string>>>& objDict =
959 pathPair.second;
James Feist8bd25cc2019-03-15 15:14:00 -0700960 if (objDict.empty())
961 {
962 continue; // this should be impossible
963 }
964
965 const std::string& owner = objDict.begin()->first;
966 crow::connections::systemBus->async_method_call(
967 [path, owner,
Ed Tanous271584a2019-07-09 16:24:22 -0700968 sensorsAsyncResp](const boost::system::error_code e,
James Feist8bd25cc2019-03-15 15:14:00 -0700969 std::variant<std::vector<std::string>>
970 variantEndpoints) {
Ed Tanous271584a2019-07-09 16:24:22 -0700971 if (e)
James Feist8bd25cc2019-03-15 15:14:00 -0700972 {
973 return; // if they don't have an association we
974 // can't tell what chassis is
975 }
976 // verify part of the right chassis
977 auto endpoints = std::get_if<std::vector<std::string>>(
978 &variantEndpoints);
979
980 if (endpoints == nullptr)
981 {
982 BMCWEB_LOG_ERROR << "Invalid association interface";
983 messages::internalError(sensorsAsyncResp->res);
984 return;
985 }
986
987 auto found = std::find_if(
988 endpoints->begin(), endpoints->end(),
989 [sensorsAsyncResp](const std::string& entry) {
990 return entry.find(
991 sensorsAsyncResp->chassisId) !=
992 std::string::npos;
993 });
994
995 if (found == endpoints->end())
996 {
997 return;
998 }
999 crow::connections::systemBus->async_method_call(
1000 [path, sensorsAsyncResp](
Ed Tanous271584a2019-07-09 16:24:22 -07001001 const boost::system::error_code& err,
James Feist8bd25cc2019-03-15 15:14:00 -07001002 const boost::container::flat_map<
1003 std::string,
1004 std::variant<uint8_t,
1005 std::vector<std::string>,
1006 std::string>>& ret) {
Ed Tanous271584a2019-07-09 16:24:22 -07001007 if (err)
James Feist8bd25cc2019-03-15 15:14:00 -07001008 {
1009 return; // don't have to have this
1010 // interface
1011 }
1012 auto findFailures = ret.find("AllowedFailures");
1013 auto findCollection = ret.find("Collection");
1014 auto findStatus = ret.find("Status");
1015
1016 if (findFailures == ret.end() ||
1017 findCollection == ret.end() ||
1018 findStatus == ret.end())
1019 {
1020 BMCWEB_LOG_ERROR
1021 << "Invalid redundancy interface";
1022 messages::internalError(
1023 sensorsAsyncResp->res);
1024 return;
1025 }
1026
1027 auto allowedFailures = std::get_if<uint8_t>(
1028 &(findFailures->second));
1029 auto collection =
1030 std::get_if<std::vector<std::string>>(
1031 &(findCollection->second));
1032 auto status = std::get_if<std::string>(
1033 &(findStatus->second));
1034
1035 if (allowedFailures == nullptr ||
1036 collection == nullptr || status == nullptr)
1037 {
1038
1039 BMCWEB_LOG_ERROR
1040 << "Invalid redundancy interface "
1041 "types";
1042 messages::internalError(
1043 sensorsAsyncResp->res);
1044 return;
1045 }
1046 size_t lastSlash = path.rfind("/");
1047 if (lastSlash == std::string::npos)
1048 {
1049 // this should be impossible
1050 messages::internalError(
1051 sensorsAsyncResp->res);
1052 return;
1053 }
1054 std::string name = path.substr(lastSlash + 1);
1055 std::replace(name.begin(), name.end(), '_',
1056 ' ');
1057
1058 std::string health;
1059
1060 if (boost::ends_with(*status, "Full"))
1061 {
1062 health = "OK";
1063 }
1064 else if (boost::ends_with(*status, "Degraded"))
1065 {
1066 health = "Warning";
1067 }
1068 else
1069 {
1070 health = "Critical";
1071 }
1072 std::vector<nlohmann::json> redfishCollection;
1073 const auto& fanRedfish =
1074 sensorsAsyncResp->res.jsonValue["Fans"];
1075 for (const std::string& item : *collection)
1076 {
1077 lastSlash = item.rfind("/");
1078 // make a copy as collection is const
1079 std::string itemName =
1080 item.substr(lastSlash + 1);
1081 /*
1082 todo(ed): merge patch that fixes the names
1083 std::replace(itemName.begin(),
1084 itemName.end(), '_', ' ');*/
1085 auto schemaItem = std::find_if(
1086 fanRedfish.begin(), fanRedfish.end(),
1087 [itemName](const nlohmann::json& fan) {
1088 return fan["MemberId"] == itemName;
1089 });
1090 if (schemaItem != fanRedfish.end())
1091 {
1092 redfishCollection.push_back(
1093 {{"@odata.id",
1094 (*schemaItem)["@odata.id"]}});
1095 }
1096 else
1097 {
1098 BMCWEB_LOG_ERROR
1099 << "failed to find fan in schema";
1100 messages::internalError(
1101 sensorsAsyncResp->res);
1102 return;
1103 }
1104 }
1105
Ed Tanous271584a2019-07-09 16:24:22 -07001106 nlohmann::json& jResp =
1107 sensorsAsyncResp->res
1108 .jsonValue["Redundancy"];
1109 jResp.push_back(
James Feist8bd25cc2019-03-15 15:14:00 -07001110 {{"@odata.id",
AppaRao Puli717794d2019-10-18 22:54:53 +05301111 "/redfish/v1/Chassis/" +
James Feist8bd25cc2019-03-15 15:14:00 -07001112 sensorsAsyncResp->chassisId + "/" +
1113 sensorsAsyncResp->chassisSubNode +
1114 "#/Redundancy/" +
Ed Tanous271584a2019-07-09 16:24:22 -07001115 std::to_string(jResp.size())},
James Feist8bd25cc2019-03-15 15:14:00 -07001116 {"@odata.type",
1117 "#Redundancy.v1_3_2.Redundancy"},
1118 {"MinNumNeeded",
1119 collection->size() - *allowedFailures},
1120 {"MemberId", name},
1121 {"Mode", "N+m"},
1122 {"Name", name},
1123 {"RedundancySet", redfishCollection},
1124 {"Status",
1125 {{"Health", health},
1126 {"State", "Enabled"}}}});
1127 },
1128 owner, path, "org.freedesktop.DBus.Properties",
1129 "GetAll",
1130 "xyz.openbmc_project.Control.FanRedundancy");
1131 },
James Feist02e92e32019-06-26 12:07:05 -07001132 "xyz.openbmc_project.ObjectMapper", path + "/chassis",
James Feist8bd25cc2019-03-15 15:14:00 -07001133 "org.freedesktop.DBus.Properties", "Get",
1134 "xyz.openbmc_project.Association", "endpoints");
1135 }
1136 },
1137 "xyz.openbmc_project.ObjectMapper",
1138 "/xyz/openbmc_project/object_mapper",
1139 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
1140 "/xyz/openbmc_project/control", 2,
1141 std::array<const char*, 1>{
1142 "xyz.openbmc_project.Control.FanRedundancy"});
1143}
1144
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001145void sortJSONResponse(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
1146{
1147 nlohmann::json& response = SensorsAsyncResp->res.jsonValue;
1148 std::array<std::string, 2> sensorHeaders{"Temperatures", "Fans"};
1149 if (SensorsAsyncResp->chassisSubNode == "Power")
1150 {
1151 sensorHeaders = {"Voltages", "PowerSupplies"};
1152 }
1153 for (const std::string& sensorGroup : sensorHeaders)
1154 {
1155 nlohmann::json::iterator entry = response.find(sensorGroup);
1156 if (entry != response.end())
1157 {
1158 std::sort(entry->begin(), entry->end(),
1159 [](nlohmann::json& c1, nlohmann::json& c2) {
1160 return c1["Name"] < c2["Name"];
1161 });
1162
1163 // add the index counts to the end of each entry
1164 size_t count = 0;
1165 for (nlohmann::json& sensorJson : *entry)
1166 {
1167 nlohmann::json::iterator odata = sensorJson.find("@odata.id");
1168 if (odata == sensorJson.end())
1169 {
1170 continue;
1171 }
1172 std::string* value = odata->get_ptr<std::string*>();
1173 if (value != nullptr)
1174 {
1175 *value += std::to_string(count);
1176 count++;
1177 }
1178 }
1179 }
1180 }
1181}
1182
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001183/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001184 * @brief Finds the inventory item with the specified object path.
1185 * @param inventoryItems D-Bus inventory items associated with sensors.
1186 * @param invItemObjPath D-Bus object path of inventory item.
1187 * @return Inventory item within vector, or nullptr if no match found.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001188 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001189static InventoryItem* findInventoryItem(
1190 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1191 const std::string& invItemObjPath)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001192{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001193 for (InventoryItem& inventoryItem : *inventoryItems)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001194 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001195 if (inventoryItem.objectPath == invItemObjPath)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001196 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001197 return &inventoryItem;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001198 }
1199 }
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001200 return nullptr;
1201}
1202
1203/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001204 * @brief Finds the inventory item associated with the specified sensor.
1205 * @param inventoryItems D-Bus inventory items associated with sensors.
1206 * @param sensorObjPath D-Bus object path of sensor.
1207 * @return Inventory item within vector, or nullptr if no match found.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001208 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001209static InventoryItem* findInventoryItemForSensor(
1210 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1211 const std::string& sensorObjPath)
1212{
1213 for (InventoryItem& inventoryItem : *inventoryItems)
1214 {
1215 if (inventoryItem.sensors.count(sensorObjPath) > 0)
1216 {
1217 return &inventoryItem;
1218 }
1219 }
1220 return nullptr;
1221}
1222
1223/**
Anthony Wilsond5005492019-07-31 16:34:17 -05001224 * @brief Finds the inventory item associated with the specified led path.
1225 * @param inventoryItems D-Bus inventory items associated with sensors.
1226 * @param ledObjPath D-Bus object path of led.
1227 * @return Inventory item within vector, or nullptr if no match found.
1228 */
1229inline InventoryItem*
1230 findInventoryItemForLed(std::vector<InventoryItem>& inventoryItems,
1231 const std::string& ledObjPath)
1232{
1233 for (InventoryItem& inventoryItem : inventoryItems)
1234 {
1235 if (inventoryItem.ledObjectPath == ledObjPath)
1236 {
1237 return &inventoryItem;
1238 }
1239 }
1240 return nullptr;
1241}
1242
1243/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001244 * @brief Adds inventory item and associated sensor to specified vector.
1245 *
1246 * Adds a new InventoryItem to the vector if necessary. Searches for an
1247 * existing InventoryItem with the specified object path. If not found, one is
1248 * added to the vector.
1249 *
1250 * Next, the specified sensor is added to the set of sensors associated with the
1251 * InventoryItem.
1252 *
1253 * @param inventoryItems D-Bus inventory items associated with sensors.
1254 * @param invItemObjPath D-Bus object path of inventory item.
1255 * @param sensorObjPath D-Bus object path of sensor
1256 */
1257static void
1258 addInventoryItem(std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1259 const std::string& invItemObjPath,
1260 const std::string& sensorObjPath)
1261{
1262 // Look for inventory item in vector
1263 InventoryItem* inventoryItem =
1264 findInventoryItem(inventoryItems, invItemObjPath);
1265
1266 // If inventory item doesn't exist in vector, add it
1267 if (inventoryItem == nullptr)
1268 {
1269 inventoryItems->emplace_back(invItemObjPath);
1270 inventoryItem = &(inventoryItems->back());
1271 }
1272
1273 // Add sensor to set of sensors associated with inventory item
1274 inventoryItem->sensors.emplace(sensorObjPath);
1275}
1276
1277/**
1278 * @brief Stores D-Bus data in the specified inventory item.
1279 *
1280 * Finds D-Bus data in the specified map of interfaces. Stores the data in the
1281 * specified InventoryItem.
1282 *
1283 * This data is later used to provide sensor property values in the JSON
1284 * response.
1285 *
1286 * @param inventoryItem Inventory item where data will be stored.
1287 * @param interfacesDict Map containing D-Bus interfaces and their properties
1288 * for the specified inventory item.
1289 */
1290static void storeInventoryItemData(
1291 InventoryItem& inventoryItem,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001292 const boost::container::flat_map<
1293 std::string, boost::container::flat_map<std::string, SensorVariant>>&
1294 interfacesDict)
1295{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001296 // Get properties from Inventory.Item interface
1297 auto interfaceIt =
1298 interfacesDict.find("xyz.openbmc_project.Inventory.Item");
1299 if (interfaceIt != interfacesDict.end())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001300 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001301 auto propertyIt = interfaceIt->second.find("Present");
1302 if (propertyIt != interfaceIt->second.end())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001303 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001304 const bool* value = std::get_if<bool>(&propertyIt->second);
1305 if (value != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001306 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001307 inventoryItem.isPresent = *value;
1308 }
1309 }
1310 }
1311
1312 // Check if Inventory.Item.PowerSupply interface is present
1313 interfaceIt =
1314 interfacesDict.find("xyz.openbmc_project.Inventory.Item.PowerSupply");
1315 if (interfaceIt != interfacesDict.end())
1316 {
1317 inventoryItem.isPowerSupply = true;
1318 }
1319
1320 // Get properties from Inventory.Decorator.Asset interface
1321 interfaceIt =
1322 interfacesDict.find("xyz.openbmc_project.Inventory.Decorator.Asset");
1323 if (interfaceIt != interfacesDict.end())
1324 {
1325 auto propertyIt = interfaceIt->second.find("Manufacturer");
1326 if (propertyIt != interfaceIt->second.end())
1327 {
1328 const std::string* value =
1329 std::get_if<std::string>(&propertyIt->second);
1330 if (value != nullptr)
1331 {
1332 inventoryItem.manufacturer = *value;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001333 }
1334 }
1335
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001336 propertyIt = interfaceIt->second.find("Model");
1337 if (propertyIt != interfaceIt->second.end())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001338 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001339 const std::string* value =
1340 std::get_if<std::string>(&propertyIt->second);
1341 if (value != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001342 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001343 inventoryItem.model = *value;
1344 }
1345 }
1346
1347 propertyIt = interfaceIt->second.find("PartNumber");
1348 if (propertyIt != interfaceIt->second.end())
1349 {
1350 const std::string* value =
1351 std::get_if<std::string>(&propertyIt->second);
1352 if (value != nullptr)
1353 {
1354 inventoryItem.partNumber = *value;
1355 }
1356 }
1357
1358 propertyIt = interfaceIt->second.find("SerialNumber");
1359 if (propertyIt != interfaceIt->second.end())
1360 {
1361 const std::string* value =
1362 std::get_if<std::string>(&propertyIt->second);
1363 if (value != nullptr)
1364 {
1365 inventoryItem.serialNumber = *value;
1366 }
1367 }
1368 }
1369
1370 // Get properties from State.Decorator.OperationalStatus interface
1371 interfaceIt = interfacesDict.find(
1372 "xyz.openbmc_project.State.Decorator.OperationalStatus");
1373 if (interfaceIt != interfacesDict.end())
1374 {
1375 auto propertyIt = interfaceIt->second.find("Functional");
1376 if (propertyIt != interfaceIt->second.end())
1377 {
1378 const bool* value = std::get_if<bool>(&propertyIt->second);
1379 if (value != nullptr)
1380 {
1381 inventoryItem.isFunctional = *value;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001382 }
1383 }
1384 }
1385}
1386
1387/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001388 * @brief Gets D-Bus data for inventory items associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001389 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001390 * Uses the specified connections (services) to obtain D-Bus data for inventory
1391 * items associated with sensors. Stores the resulting data in the
1392 * inventoryItems vector.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001393 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001394 * This data is later used to provide sensor property values in the JSON
1395 * response.
1396 *
1397 * Finds the inventory item data asynchronously. Invokes callback when data has
1398 * been obtained.
1399 *
1400 * The callback must have the following signature:
1401 * @code
Anthony Wilsond5005492019-07-31 16:34:17 -05001402 * callback(void)
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001403 * @endcode
1404 *
1405 * This function is called recursively, obtaining data asynchronously from one
1406 * connection in each call. This ensures the callback is not invoked until the
1407 * last asynchronous function has completed.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001408 *
1409 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001410 * @param inventoryItems D-Bus inventory items associated with sensors.
1411 * @param invConnections Connections that provide data for the inventory items.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001412 * @param objectMgrPaths Mappings from connection name to DBus object path that
1413 * implements ObjectManager.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001414 * @param callback Callback to invoke when inventory data has been obtained.
1415 * @param invConnectionsIndex Current index in invConnections. Only specified
1416 * in recursive calls to this function.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001417 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001418template <typename Callback>
1419static void getInventoryItemsData(
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001420 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001421 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001422 std::shared_ptr<boost::container::flat_set<std::string>> invConnections,
1423 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001424 objectMgrPaths,
Ed Tanous271584a2019-07-09 16:24:22 -07001425 Callback&& callback, size_t invConnectionsIndex = 0)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001426{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001427 BMCWEB_LOG_DEBUG << "getInventoryItemsData enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001428
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001429 // If no more connections left, call callback
1430 if (invConnectionsIndex >= invConnections->size())
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001431 {
Anthony Wilsond5005492019-07-31 16:34:17 -05001432 callback();
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001433 BMCWEB_LOG_DEBUG << "getInventoryItemsData exit";
1434 return;
1435 }
1436
1437 // Get inventory item data from current connection
1438 auto it = invConnections->nth(invConnectionsIndex);
1439 if (it != invConnections->end())
1440 {
1441 const std::string& invConnection = *it;
1442
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001443 // Response handler for GetManagedObjects
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001444 auto respHandler = [sensorsAsyncResp, inventoryItems, invConnections,
1445 objectMgrPaths, callback{std::move(callback)},
1446 invConnectionsIndex](
1447 const boost::system::error_code ec,
1448 ManagedObjectsVectorType& resp) {
1449 BMCWEB_LOG_DEBUG << "getInventoryItemsData respHandler enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001450 if (ec)
1451 {
1452 BMCWEB_LOG_ERROR
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001453 << "getInventoryItemsData respHandler DBus error " << ec;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001454 messages::internalError(sensorsAsyncResp->res);
1455 return;
1456 }
1457
1458 // Loop through returned object paths
1459 for (const auto& objDictEntry : resp)
1460 {
1461 const std::string& objPath =
1462 static_cast<const std::string&>(objDictEntry.first);
1463
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001464 // If this object path is one of the specified inventory items
1465 InventoryItem* inventoryItem =
1466 findInventoryItem(inventoryItems, objPath);
1467 if (inventoryItem != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001468 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001469 // Store inventory data in InventoryItem
1470 storeInventoryItemData(*inventoryItem, objDictEntry.second);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001471 }
1472 }
1473
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001474 // Recurse to get inventory item data from next connection
1475 getInventoryItemsData(sensorsAsyncResp, inventoryItems,
1476 invConnections, objectMgrPaths,
1477 std::move(callback), invConnectionsIndex + 1);
1478
1479 BMCWEB_LOG_DEBUG << "getInventoryItemsData respHandler exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001480 };
1481
1482 // Find DBus object path that implements ObjectManager for the current
1483 // connection. If no mapping found, default to "/".
1484 auto iter = objectMgrPaths->find(invConnection);
1485 const std::string& objectMgrPath =
1486 (iter != objectMgrPaths->end()) ? iter->second : "/";
1487 BMCWEB_LOG_DEBUG << "ObjectManager path for " << invConnection << " is "
1488 << objectMgrPath;
1489
1490 // Get all object paths and their interfaces for current connection
1491 crow::connections::systemBus->async_method_call(
1492 std::move(respHandler), invConnection, objectMgrPath,
1493 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1494 }
1495
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001496 BMCWEB_LOG_DEBUG << "getInventoryItemsData exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001497}
1498
1499/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001500 * @brief Gets connections that provide D-Bus data for inventory items.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001501 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001502 * Gets the D-Bus connections (services) that provide data for the inventory
1503 * items that are associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001504 *
1505 * Finds the connections asynchronously. Invokes callback when information has
1506 * been obtained.
1507 *
1508 * The callback must have the following signature:
1509 * @code
1510 * callback(std::shared_ptr<boost::container::flat_set<std::string>>
1511 * invConnections)
1512 * @endcode
1513 *
1514 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001515 * @param inventoryItems D-Bus inventory items associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001516 * @param callback Callback to invoke when connections have been obtained.
1517 */
1518template <typename Callback>
1519static void getInventoryItemsConnections(
1520 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001521 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001522 Callback&& callback)
1523{
1524 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections enter";
1525
1526 const std::string path = "/xyz/openbmc_project/inventory";
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001527 const std::array<std::string, 4> interfaces = {
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001528 "xyz.openbmc_project.Inventory.Item",
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001529 "xyz.openbmc_project.Inventory.Item.PowerSupply",
1530 "xyz.openbmc_project.Inventory.Decorator.Asset",
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001531 "xyz.openbmc_project.State.Decorator.OperationalStatus"};
1532
1533 // Response handler for parsing output from GetSubTree
1534 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001535 inventoryItems](const boost::system::error_code ec,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001536 const GetSubTreeType& subtree) {
1537 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections respHandler enter";
1538 if (ec)
1539 {
1540 messages::internalError(sensorsAsyncResp->res);
1541 BMCWEB_LOG_ERROR
1542 << "getInventoryItemsConnections respHandler DBus error " << ec;
1543 return;
1544 }
1545
1546 // Make unique list of connections for desired inventory items
1547 std::shared_ptr<boost::container::flat_set<std::string>>
1548 invConnections =
1549 std::make_shared<boost::container::flat_set<std::string>>();
1550 invConnections->reserve(8);
1551
1552 // Loop through objects from GetSubTree
1553 for (const std::pair<
1554 std::string,
1555 std::vector<std::pair<std::string, std::vector<std::string>>>>&
1556 object : subtree)
1557 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001558 // Check if object path is one of the specified inventory items
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001559 const std::string& objPath = object.first;
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001560 if (findInventoryItem(inventoryItems, objPath) != nullptr)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001561 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001562 // Store all connections to inventory item
1563 for (const std::pair<std::string, std::vector<std::string>>&
1564 objData : object.second)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001565 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001566 const std::string& invConnection = objData.first;
1567 invConnections->insert(invConnection);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001568 }
1569 }
1570 }
Anthony Wilsond5005492019-07-31 16:34:17 -05001571
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001572 callback(invConnections);
1573 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections respHandler exit";
1574 };
1575
1576 // Make call to ObjectMapper to find all inventory items
1577 crow::connections::systemBus->async_method_call(
1578 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
1579 "/xyz/openbmc_project/object_mapper",
1580 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 0, interfaces);
1581 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections exit";
1582}
1583
1584/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001585 * @brief Gets associations from sensors to inventory items.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001586 *
1587 * Looks for ObjectMapper associations from the specified sensors to related
Anthony Wilsond5005492019-07-31 16:34:17 -05001588 * inventory items. Then finds the associations from those inventory items to
1589 * their LEDs, if any.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001590 *
1591 * Finds the inventory items asynchronously. Invokes callback when information
1592 * has been obtained.
1593 *
1594 * The callback must have the following signature:
1595 * @code
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001596 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001597 * @endcode
1598 *
1599 * @param sensorsAsyncResp Pointer to object holding response data.
1600 * @param sensorNames All sensors within the current chassis.
1601 * @param objectMgrPaths Mappings from connection name to DBus object path that
1602 * implements ObjectManager.
1603 * @param callback Callback to invoke when inventory items have been obtained.
1604 */
1605template <typename Callback>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001606static void getInventoryItemAssociations(
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001607 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1608 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
1609 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1610 objectMgrPaths,
1611 Callback&& callback)
1612{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001613 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001614
1615 // Response handler for GetManagedObjects
1616 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
1617 sensorNames](const boost::system::error_code ec,
1618 dbus::utility::ManagedObjectType& resp) {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001619 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations respHandler enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001620 if (ec)
1621 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001622 BMCWEB_LOG_ERROR
1623 << "getInventoryItemAssociations respHandler DBus error " << ec;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001624 messages::internalError(sensorsAsyncResp->res);
1625 return;
1626 }
1627
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001628 // Create vector to hold list of inventory items
1629 std::shared_ptr<std::vector<InventoryItem>> inventoryItems =
1630 std::make_shared<std::vector<InventoryItem>>();
1631
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001632 // Loop through returned object paths
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001633 std::string sensorAssocPath;
1634 sensorAssocPath.reserve(128); // avoid memory allocations
1635 for (const auto& objDictEntry : resp)
1636 {
1637 const std::string& objPath =
1638 static_cast<const std::string&>(objDictEntry.first);
1639 const boost::container::flat_map<
1640 std::string, boost::container::flat_map<
1641 std::string, dbus::utility::DbusVariantType>>&
1642 interfacesDict = objDictEntry.second;
1643
1644 // If path is inventory association for one of the specified sensors
1645 for (const std::string& sensorName : *sensorNames)
1646 {
1647 sensorAssocPath = sensorName;
1648 sensorAssocPath += "/inventory";
1649 if (objPath == sensorAssocPath)
1650 {
1651 // Get Association interface for object path
1652 auto assocIt =
1653 interfacesDict.find("xyz.openbmc_project.Association");
1654 if (assocIt != interfacesDict.end())
1655 {
1656 // Get inventory item from end point
1657 auto endpointsIt = assocIt->second.find("endpoints");
1658 if (endpointsIt != assocIt->second.end())
1659 {
1660 const std::vector<std::string>* endpoints =
1661 std::get_if<std::vector<std::string>>(
1662 &endpointsIt->second);
1663 if ((endpoints != nullptr) && !endpoints->empty())
1664 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001665 // Add inventory item to vector
1666 const std::string& invItemPath =
1667 endpoints->front();
1668 addInventoryItem(inventoryItems, invItemPath,
1669 sensorName);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001670 }
1671 }
1672 }
1673 break;
1674 }
1675 }
1676 }
1677
Anthony Wilsond5005492019-07-31 16:34:17 -05001678 // Now loop through the returned object paths again, this time to
1679 // find the leds associated with the inventory items we just found
1680 std::string inventoryAssocPath;
1681 inventoryAssocPath.reserve(128); // avoid memory allocations
1682 for (const auto& objDictEntry : resp)
1683 {
1684 const std::string& objPath =
1685 static_cast<const std::string&>(objDictEntry.first);
1686 const boost::container::flat_map<
1687 std::string, boost::container::flat_map<
1688 std::string, dbus::utility::DbusVariantType>>&
1689 interfacesDict = objDictEntry.second;
1690
1691 for (InventoryItem& inventoryItem : *inventoryItems)
1692 {
1693 inventoryAssocPath = inventoryItem.objectPath;
1694 inventoryAssocPath += "/leds";
1695 if (objPath == inventoryAssocPath)
1696 {
1697 // Get Association interface for object path
1698 auto assocIt =
1699 interfacesDict.find("xyz.openbmc_project.Association");
1700 if (assocIt != interfacesDict.end())
1701 {
1702 // Get inventory item from end point
1703 auto endpointsIt = assocIt->second.find("endpoints");
1704 if (endpointsIt != assocIt->second.end())
1705 {
1706 const std::vector<std::string>* endpoints =
1707 std::get_if<std::vector<std::string>>(
1708 &endpointsIt->second);
1709 if ((endpoints != nullptr) && !endpoints->empty())
1710 {
1711 // Store LED path in inventory item
1712 const std::string& ledPath = endpoints->front();
1713 inventoryItem.ledObjectPath = ledPath;
1714 }
1715 }
1716 }
1717 break;
1718 }
1719 }
1720 }
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001721 callback(inventoryItems);
1722 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations respHandler exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001723 };
1724
1725 // Find DBus object path that implements ObjectManager for ObjectMapper
1726 std::string connection = "xyz.openbmc_project.ObjectMapper";
1727 auto iter = objectMgrPaths->find(connection);
1728 const std::string& objectMgrPath =
1729 (iter != objectMgrPaths->end()) ? iter->second : "/";
1730 BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
1731 << objectMgrPath;
1732
1733 // Call GetManagedObjects on the ObjectMapper to get all associations
1734 crow::connections::systemBus->async_method_call(
1735 std::move(respHandler), connection, objectMgrPath,
1736 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1737
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05001738 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001739}
1740
1741/**
Anthony Wilsond5005492019-07-31 16:34:17 -05001742 * @brief Gets D-Bus data for inventory item leds associated with sensors.
1743 *
1744 * Uses the specified connections (services) to obtain D-Bus data for inventory
1745 * item leds associated with sensors. Stores the resulting data in the
1746 * inventoryItems vector.
1747 *
1748 * This data is later used to provide sensor property values in the JSON
1749 * response.
1750 *
1751 * Finds the inventory item led data asynchronously. Invokes callback when data
1752 * has been obtained.
1753 *
1754 * The callback must have the following signature:
1755 * @code
Gunnar Mills42cbe532019-08-15 15:26:54 -05001756 * callback()
Anthony Wilsond5005492019-07-31 16:34:17 -05001757 * @endcode
1758 *
1759 * This function is called recursively, obtaining data asynchronously from one
1760 * connection in each call. This ensures the callback is not invoked until the
1761 * last asynchronous function has completed.
1762 *
1763 * @param sensorsAsyncResp Pointer to object holding response data.
1764 * @param inventoryItems D-Bus inventory items associated with sensors.
1765 * @param ledConnections Connections that provide data for the inventory leds.
1766 * @param callback Callback to invoke when inventory data has been obtained.
1767 * @param ledConnectionsIndex Current index in ledConnections. Only specified
1768 * in recursive calls to this function.
1769 */
1770template <typename Callback>
1771void getInventoryLedData(
1772 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1773 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1774 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1775 ledConnections,
1776 Callback&& callback, size_t ledConnectionsIndex = 0)
1777{
1778 BMCWEB_LOG_DEBUG << "getInventoryLedData enter";
1779
1780 // If no more connections left, call callback
1781 if (ledConnectionsIndex >= ledConnections->size())
1782 {
Gunnar Mills42cbe532019-08-15 15:26:54 -05001783 callback();
Anthony Wilsond5005492019-07-31 16:34:17 -05001784 BMCWEB_LOG_DEBUG << "getInventoryLedData exit";
1785 return;
1786 }
1787
1788 // Get inventory item data from current connection
1789 auto it = ledConnections->nth(ledConnectionsIndex);
1790 if (it != ledConnections->end())
1791 {
1792 const std::string& ledPath = (*it).first;
1793 const std::string& ledConnection = (*it).second;
1794 // Response handler for Get State property
1795 auto respHandler =
1796 [sensorsAsyncResp, inventoryItems, ledConnections, ledPath,
1797 callback{std::move(callback)},
1798 ledConnectionsIndex](const boost::system::error_code ec,
1799 const std::variant<std::string>& ledState) {
1800 BMCWEB_LOG_DEBUG << "getInventoryLedData respHandler enter";
1801 if (ec)
1802 {
1803 BMCWEB_LOG_ERROR
1804 << "getInventoryLedData respHandler DBus error " << ec;
1805 messages::internalError(sensorsAsyncResp->res);
1806 return;
1807 }
1808
1809 const std::string* state = std::get_if<std::string>(&ledState);
1810 if (state != nullptr)
1811 {
1812 BMCWEB_LOG_DEBUG << "Led state: " << *state;
1813 // Find inventory item with this LED object path
1814 InventoryItem* inventoryItem =
1815 findInventoryItemForLed(*inventoryItems, ledPath);
1816 if (inventoryItem != nullptr)
1817 {
1818 // Store LED state in InventoryItem
1819 if (boost::ends_with(*state, "On"))
1820 {
1821 inventoryItem->ledState = LedState::ON;
1822 }
1823 else if (boost::ends_with(*state, "Blink"))
1824 {
1825 inventoryItem->ledState = LedState::BLINK;
1826 }
1827 else if (boost::ends_with(*state, "Off"))
1828 {
1829 inventoryItem->ledState = LedState::OFF;
1830 }
1831 else
1832 {
1833 inventoryItem->ledState = LedState::UNKNOWN;
1834 }
1835 }
1836 }
1837 else
1838 {
1839 BMCWEB_LOG_DEBUG << "Failed to find State data for LED: "
1840 << ledPath;
1841 }
1842
1843 // Recurse to get LED data from next connection
1844 getInventoryLedData(sensorsAsyncResp, inventoryItems,
1845 ledConnections, std::move(callback),
1846 ledConnectionsIndex + 1);
1847
1848 BMCWEB_LOG_DEBUG << "getInventoryLedData respHandler exit";
1849 };
1850
1851 // Get the State property for the current LED
1852 crow::connections::systemBus->async_method_call(
1853 std::move(respHandler), ledConnection, ledPath,
1854 "org.freedesktop.DBus.Properties", "Get",
1855 "xyz.openbmc_project.Led.Physical", "State");
1856 }
1857
1858 BMCWEB_LOG_DEBUG << "getInventoryLedData exit";
1859}
1860
1861/**
1862 * @brief Gets LED data for LEDs associated with given inventory items.
1863 *
1864 * Gets the D-Bus connections (services) that provide LED data for the LEDs
1865 * associated with the specified inventory items. Then gets the LED data from
1866 * each connection and stores it in the inventory item.
1867 *
1868 * This data is later used to provide sensor property values in the JSON
1869 * response.
1870 *
1871 * Finds the LED data asynchronously. Invokes callback when information has
1872 * been obtained.
1873 *
1874 * The callback must have the following signature:
1875 * @code
Gunnar Mills42cbe532019-08-15 15:26:54 -05001876 * callback()
Anthony Wilsond5005492019-07-31 16:34:17 -05001877 * @endcode
1878 *
1879 * @param sensorsAsyncResp Pointer to object holding response data.
1880 * @param inventoryItems D-Bus inventory items associated with sensors.
1881 * @param callback Callback to invoke when inventory items have been obtained.
1882 */
1883template <typename Callback>
1884void getInventoryLeds(
1885 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1886 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1887 Callback&& callback)
1888{
1889 BMCWEB_LOG_DEBUG << "getInventoryLeds enter";
1890
1891 const std::string path = "/xyz/openbmc_project";
1892 const std::array<std::string, 1> interfaces = {
1893 "xyz.openbmc_project.Led.Physical"};
1894
1895 // Response handler for parsing output from GetSubTree
1896 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
1897 inventoryItems](const boost::system::error_code ec,
1898 const GetSubTreeType& subtree) {
1899 BMCWEB_LOG_DEBUG << "getInventoryLeds respHandler enter";
1900 if (ec)
1901 {
1902 messages::internalError(sensorsAsyncResp->res);
1903 BMCWEB_LOG_ERROR << "getInventoryLeds respHandler DBus error "
1904 << ec;
1905 return;
1906 }
1907
1908 // Build map of LED object paths to connections
1909 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1910 ledConnections = std::make_shared<
1911 boost::container::flat_map<std::string, std::string>>();
1912
1913 // Loop through objects from GetSubTree
1914 for (const std::pair<
1915 std::string,
1916 std::vector<std::pair<std::string, std::vector<std::string>>>>&
1917 object : subtree)
1918 {
1919 // Check if object path is LED for one of the specified inventory
1920 // items
1921 const std::string& ledPath = object.first;
1922 if (findInventoryItemForLed(*inventoryItems, ledPath) != nullptr)
1923 {
1924 // Add mapping from ledPath to connection
1925 const std::string& connection = object.second.begin()->first;
1926 (*ledConnections)[ledPath] = connection;
1927 BMCWEB_LOG_DEBUG << "Added mapping " << ledPath << " -> "
1928 << connection;
1929 }
1930 }
1931
1932 getInventoryLedData(sensorsAsyncResp, inventoryItems, ledConnections,
1933 std::move(callback));
1934 BMCWEB_LOG_DEBUG << "getInventoryLeds respHandler exit";
1935 };
1936 // Make call to ObjectMapper to find all inventory items
1937 crow::connections::systemBus->async_method_call(
1938 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
1939 "/xyz/openbmc_project/object_mapper",
1940 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 0, interfaces);
1941 BMCWEB_LOG_DEBUG << "getInventoryLeds exit";
1942}
1943
1944/**
Gunnar Mills42cbe532019-08-15 15:26:54 -05001945 * @brief Gets D-Bus data for Power Supply Attributes such as EfficiencyPercent
1946 *
1947 * Uses the specified connections (services) (currently assumes just one) to
1948 * obtain D-Bus data for Power Supply Attributes. Stores the resulting data in
1949 * the inventoryItems vector. Only stores data in Power Supply inventoryItems.
1950 *
1951 * This data is later used to provide sensor property values in the JSON
1952 * response.
1953 *
1954 * Finds the Power Supply Attributes data asynchronously. Invokes callback
1955 * when data has been obtained.
1956 *
1957 * The callback must have the following signature:
1958 * @code
1959 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
1960 * @endcode
1961 *
1962 * @param sensorsAsyncResp Pointer to object holding response data.
1963 * @param inventoryItems D-Bus inventory items associated with sensors.
1964 * @param psAttributesConnections Connections that provide data for the Power
1965 * Supply Attributes
1966 * @param callback Callback to invoke when data has been obtained.
1967 */
1968template <typename Callback>
1969void getPowerSupplyAttributesData(
1970 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1971 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
1972 const boost::container::flat_map<std::string, std::string>&
1973 psAttributesConnections,
1974 Callback&& callback)
1975{
1976 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData enter";
1977
1978 if (psAttributesConnections.empty())
1979 {
1980 BMCWEB_LOG_DEBUG << "Can't find PowerSupplyAttributes, no connections!";
1981 callback(inventoryItems);
1982 return;
1983 }
1984
1985 // Assuming just one connection (service) for now
1986 auto it = psAttributesConnections.nth(0);
1987
1988 const std::string& psAttributesPath = (*it).first;
1989 const std::string& psAttributesConnection = (*it).second;
1990
1991 // Response handler for Get DeratingFactor property
1992 auto respHandler = [sensorsAsyncResp, inventoryItems,
1993 callback{std::move(callback)}](
1994 const boost::system::error_code ec,
1995 const std::variant<uint32_t>& deratingFactor) {
1996 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData respHandler enter";
1997 if (ec)
1998 {
1999 BMCWEB_LOG_ERROR
2000 << "getPowerSupplyAttributesData respHandler DBus error " << ec;
2001 messages::internalError(sensorsAsyncResp->res);
2002 return;
2003 }
2004
2005 const uint32_t* value = std::get_if<uint32_t>(&deratingFactor);
2006 if (value != nullptr)
2007 {
2008 BMCWEB_LOG_DEBUG << "PS EfficiencyPercent value: " << *value;
2009 // Store value in Power Supply Inventory Items
2010 for (InventoryItem& inventoryItem : *inventoryItems)
2011 {
2012 if (inventoryItem.isPowerSupply == true)
2013 {
2014 inventoryItem.powerSupplyEfficiencyPercent =
2015 static_cast<int>(*value);
2016 }
2017 }
2018 }
2019 else
2020 {
2021 BMCWEB_LOG_DEBUG
2022 << "Failed to find EfficiencyPercent value for PowerSupplies";
2023 }
2024
2025 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData respHandler exit";
2026 callback(inventoryItems);
2027 };
2028
2029 // Get the DeratingFactor property for the PowerSupplyAttributes
2030 // Currently only property on the interface/only one we care about
2031 crow::connections::systemBus->async_method_call(
2032 std::move(respHandler), psAttributesConnection, psAttributesPath,
2033 "org.freedesktop.DBus.Properties", "Get",
2034 "xyz.openbmc_project.Control.PowerSupplyAttributes", "DeratingFactor");
2035
2036 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData exit";
2037}
2038
2039/**
2040 * @brief Gets the Power Supply Attributes such as EfficiencyPercent
2041 *
2042 * Gets the D-Bus connection (service) that provides Power Supply Attributes
2043 * data. Then gets the Power Supply Attributes data from the connection
2044 * (currently just assumes 1 connection) and stores the data in the inventory
2045 * item.
2046 *
2047 * This data is later used to provide sensor property values in the JSON
2048 * response. DeratingFactor on D-Bus is mapped to EfficiencyPercent on Redfish.
2049 *
2050 * Finds the Power Supply Attributes data asynchronously. Invokes callback
2051 * when information has been obtained.
2052 *
2053 * The callback must have the following signature:
2054 * @code
2055 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
2056 * @endcode
2057 *
2058 * @param sensorsAsyncResp Pointer to object holding response data.
2059 * @param inventoryItems D-Bus inventory items associated with sensors.
2060 * @param callback Callback to invoke when data has been obtained.
2061 */
2062template <typename Callback>
2063void getPowerSupplyAttributes(
2064 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
2065 std::shared_ptr<std::vector<InventoryItem>> inventoryItems,
2066 Callback&& callback)
2067{
2068 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes enter";
2069
2070 // Only need the power supply attributes when the Power Schema
2071 if (sensorsAsyncResp->chassisSubNode != "Power")
2072 {
2073 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes exit since not Power";
2074 callback(inventoryItems);
2075 return;
2076 }
2077
2078 const std::array<std::string, 1> interfaces = {
2079 "xyz.openbmc_project.Control.PowerSupplyAttributes"};
2080
2081 // Response handler for parsing output from GetSubTree
2082 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
2083 inventoryItems](const boost::system::error_code ec,
2084 const GetSubTreeType& subtree) {
2085 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes respHandler enter";
2086 if (ec)
2087 {
2088 messages::internalError(sensorsAsyncResp->res);
2089 BMCWEB_LOG_ERROR
2090 << "getPowerSupplyAttributes respHandler DBus error " << ec;
2091 return;
2092 }
2093 if (subtree.size() == 0)
2094 {
2095 BMCWEB_LOG_DEBUG << "Can't find Power Supply Attributes!";
2096 callback(inventoryItems);
2097 return;
2098 }
2099
2100 // Currently we only support 1 power supply attribute, use this for
2101 // all the power supplies. Build map of object path to connection.
2102 // Assume just 1 connection and 1 path for now.
2103 boost::container::flat_map<std::string, std::string>
2104 psAttributesConnections;
2105
2106 if (subtree[0].first.empty() || subtree[0].second.empty())
2107 {
2108 BMCWEB_LOG_DEBUG << "Power Supply Attributes mapper error!";
2109 callback(inventoryItems);
2110 return;
2111 }
2112
2113 const std::string& psAttributesPath = subtree[0].first;
2114 const std::string& connection = subtree[0].second.begin()->first;
2115
2116 if (connection.empty())
2117 {
2118 BMCWEB_LOG_DEBUG << "Power Supply Attributes mapper error!";
2119 callback(inventoryItems);
2120 return;
2121 }
2122
2123 psAttributesConnections[psAttributesPath] = connection;
2124 BMCWEB_LOG_DEBUG << "Added mapping " << psAttributesPath << " -> "
2125 << connection;
2126
2127 getPowerSupplyAttributesData(sensorsAsyncResp, inventoryItems,
2128 psAttributesConnections,
2129 std::move(callback));
2130 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes respHandler exit";
2131 };
2132 // Make call to ObjectMapper to find the PowerSupplyAttributes service
2133 crow::connections::systemBus->async_method_call(
2134 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
2135 "/xyz/openbmc_project/object_mapper",
2136 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
2137 "/xyz/openbmc_project", 0, interfaces);
2138 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes exit";
2139}
2140
2141/**
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002142 * @brief Gets inventory items associated with sensors.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002143 *
2144 * Finds the inventory items that are associated with the specified sensors.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002145 * Then gets D-Bus data for the inventory items, such as presence and VPD.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002146 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002147 * This data is later used to provide sensor property values in the JSON
2148 * response.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002149 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002150 * Finds the inventory items asynchronously. Invokes callback when the
2151 * inventory items have been obtained.
2152 *
2153 * The callback must have the following signature:
2154 * @code
2155 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
2156 * @endcode
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002157 *
2158 * @param sensorsAsyncResp Pointer to object holding response data.
2159 * @param sensorNames All sensors within the current chassis.
2160 * @param objectMgrPaths Mappings from connection name to DBus object path that
2161 * implements ObjectManager.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002162 * @param callback Callback to invoke when inventory items have been obtained.
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002163 */
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002164template <typename Callback>
2165static void getInventoryItems(
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002166 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
2167 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
2168 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002169 objectMgrPaths,
2170 Callback&& callback)
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002171{
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002172 BMCWEB_LOG_DEBUG << "getInventoryItems enter";
2173 auto getInventoryItemAssociationsCb =
2174 [sensorsAsyncResp, objectMgrPaths, callback{std::move(callback)}](
2175 std::shared_ptr<std::vector<InventoryItem>> inventoryItems) {
2176 BMCWEB_LOG_DEBUG << "getInventoryItemAssociationsCb enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002177 auto getInventoryItemsConnectionsCb =
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002178 [sensorsAsyncResp, inventoryItems, objectMgrPaths,
2179 callback{std::move(callback)}](
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002180 std::shared_ptr<boost::container::flat_set<std::string>>
2181 invConnections) {
2182 BMCWEB_LOG_DEBUG << "getInventoryItemsConnectionsCb enter";
Anthony Wilsond5005492019-07-31 16:34:17 -05002183 auto getInventoryItemsDataCb =
2184 [sensorsAsyncResp, inventoryItems,
2185 callback{std::move(callback)}]() {
2186 BMCWEB_LOG_DEBUG << "getInventoryItemsDataCb enter";
Gunnar Mills42cbe532019-08-15 15:26:54 -05002187
2188 auto getInventoryLedsCb = [sensorsAsyncResp,
2189 inventoryItems,
2190 callback{std::move(
2191 callback)}]() {
2192 BMCWEB_LOG_DEBUG << "getInventoryLedsCb enter";
2193 // Find Power Supply Attributes and get the data
2194 getPowerSupplyAttributes(sensorsAsyncResp,
2195 inventoryItems,
2196 std::move(callback));
2197 BMCWEB_LOG_DEBUG << "getInventoryLedsCb exit";
2198 };
2199
Anthony Wilsond5005492019-07-31 16:34:17 -05002200 // Find led connections and get the data
2201 getInventoryLeds(sensorsAsyncResp, inventoryItems,
Gunnar Mills42cbe532019-08-15 15:26:54 -05002202 std::move(getInventoryLedsCb));
Anthony Wilsond5005492019-07-31 16:34:17 -05002203 BMCWEB_LOG_DEBUG << "getInventoryItemsDataCb exit";
2204 };
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002205
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002206 // Get inventory item data from connections
2207 getInventoryItemsData(sensorsAsyncResp, inventoryItems,
2208 invConnections, objectMgrPaths,
Anthony Wilsond5005492019-07-31 16:34:17 -05002209 std::move(getInventoryItemsDataCb));
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002210 BMCWEB_LOG_DEBUG << "getInventoryItemsConnectionsCb exit";
2211 };
2212
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002213 // Get connections that provide inventory item data
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002214 getInventoryItemsConnections(
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002215 sensorsAsyncResp, inventoryItems,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002216 std::move(getInventoryItemsConnectionsCb));
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002217 BMCWEB_LOG_DEBUG << "getInventoryItemAssociationsCb exit";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002218 };
2219
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002220 // Get associations from sensors to inventory items
2221 getInventoryItemAssociations(sensorsAsyncResp, sensorNames, objectMgrPaths,
2222 std::move(getInventoryItemAssociationsCb));
2223 BMCWEB_LOG_DEBUG << "getInventoryItems exit";
2224}
2225
2226/**
2227 * @brief Returns JSON PowerSupply object for the specified inventory item.
2228 *
2229 * Searches for a JSON PowerSupply object that matches the specified inventory
2230 * item. If one is not found, a new PowerSupply object is added to the JSON
2231 * array.
2232 *
2233 * Multiple sensors are often associated with one power supply inventory item.
2234 * As a result, multiple sensor values are stored in one JSON PowerSupply
2235 * object.
2236 *
2237 * @param powerSupplyArray JSON array containing Redfish PowerSupply objects.
2238 * @param inventoryItem Inventory item for the power supply.
2239 * @param chassisId Chassis that contains the power supply.
2240 * @return JSON PowerSupply object for the specified inventory item.
2241 */
2242static nlohmann::json& getPowerSupply(nlohmann::json& powerSupplyArray,
2243 const InventoryItem& inventoryItem,
2244 const std::string& chassisId)
2245{
2246 // Check if matching PowerSupply object already exists in JSON array
2247 for (nlohmann::json& powerSupply : powerSupplyArray)
2248 {
2249 if (powerSupply["MemberId"] == inventoryItem.name)
2250 {
2251 return powerSupply;
2252 }
2253 }
2254
2255 // Add new PowerSupply object to JSON array
2256 powerSupplyArray.push_back({});
2257 nlohmann::json& powerSupply = powerSupplyArray.back();
2258 powerSupply["@odata.id"] =
2259 "/redfish/v1/Chassis/" + chassisId + "/Power#/PowerSupplies/";
2260 powerSupply["MemberId"] = inventoryItem.name;
2261 powerSupply["Name"] = boost::replace_all_copy(inventoryItem.name, "_", " ");
2262 powerSupply["Manufacturer"] = inventoryItem.manufacturer;
2263 powerSupply["Model"] = inventoryItem.model;
2264 powerSupply["PartNumber"] = inventoryItem.partNumber;
2265 powerSupply["SerialNumber"] = inventoryItem.serialNumber;
Anthony Wilsond5005492019-07-31 16:34:17 -05002266 setLedState(powerSupply, &inventoryItem);
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002267
Gunnar Mills42cbe532019-08-15 15:26:54 -05002268 if (inventoryItem.powerSupplyEfficiencyPercent >= 0)
2269 {
2270 powerSupply["EfficiencyPercent"] =
2271 inventoryItem.powerSupplyEfficiencyPercent;
2272 }
2273
2274 powerSupply["Status"]["State"] = getState(&inventoryItem);
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002275 const char* health = inventoryItem.isFunctional ? "OK" : "Critical";
2276 powerSupply["Status"]["Health"] = health;
2277
2278 return powerSupply;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002279}
2280
2281/**
Shawn McCarneyde629b62019-03-08 10:42:51 -06002282 * @brief Gets the values of the specified sensors.
2283 *
2284 * Stores the results as JSON in the SensorsAsyncResp.
2285 *
2286 * Gets the sensor values asynchronously. Stores the results later when the
2287 * information has been obtained.
2288 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002289 * The sensorNames set contains all requested sensors for the current chassis.
Shawn McCarneyde629b62019-03-08 10:42:51 -06002290 *
2291 * To minimize the number of DBus calls, the DBus method
2292 * org.freedesktop.DBus.ObjectManager.GetManagedObjects() is used to get the
2293 * values of all sensors provided by a connection (service).
2294 *
2295 * The connections set contains all the connections that provide sensor values.
2296 *
2297 * The objectMgrPaths map contains mappings from a connection name to the
2298 * corresponding DBus object path that implements ObjectManager.
2299 *
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002300 * The InventoryItem vector contains D-Bus inventory items associated with the
2301 * sensors. Inventory item data is needed for some Redfish sensor properties.
2302 *
Shawn McCarneyde629b62019-03-08 10:42:51 -06002303 * @param SensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002304 * @param sensorNames All requested sensors within the current chassis.
Shawn McCarneyde629b62019-03-08 10:42:51 -06002305 * @param connections Connections that provide sensor values.
2306 * @param objectMgrPaths Mappings from connection name to DBus object path that
2307 * implements ObjectManager.
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002308 * @param inventoryItems Inventory items associated with the sensors.
Shawn McCarneyde629b62019-03-08 10:42:51 -06002309 */
2310void getSensorData(
2311 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002312 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
Shawn McCarneyde629b62019-03-08 10:42:51 -06002313 const boost::container::flat_set<std::string>& connections,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002314 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002315 objectMgrPaths,
2316 std::shared_ptr<std::vector<InventoryItem>> inventoryItems)
Shawn McCarneyde629b62019-03-08 10:42:51 -06002317{
2318 BMCWEB_LOG_DEBUG << "getSensorData enter";
2319 // Get managed objects from all services exposing sensors
2320 for (const std::string& connection : connections)
2321 {
2322 // Response handler to process managed objects
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002323 auto getManagedObjectsCb = [SensorsAsyncResp, sensorNames,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002324 inventoryItems](
Shawn McCarneyde629b62019-03-08 10:42:51 -06002325 const boost::system::error_code ec,
2326 ManagedObjectsVectorType& resp) {
2327 BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter";
2328 if (ec)
2329 {
2330 BMCWEB_LOG_ERROR << "getManagedObjectsCb DBUS error: " << ec;
2331 messages::internalError(SensorsAsyncResp->res);
2332 return;
2333 }
2334 // Go through all objects and update response with sensor data
2335 for (const auto& objDictEntry : resp)
2336 {
2337 const std::string& objPath =
2338 static_cast<const std::string&>(objDictEntry.first);
2339 BMCWEB_LOG_DEBUG << "getManagedObjectsCb parsing object "
2340 << objPath;
2341
Shawn McCarneyde629b62019-03-08 10:42:51 -06002342 std::vector<std::string> split;
2343 // Reserve space for
2344 // /xyz/openbmc_project/sensors/<name>/<subname>
2345 split.reserve(6);
2346 boost::algorithm::split(split, objPath, boost::is_any_of("/"));
2347 if (split.size() < 6)
2348 {
2349 BMCWEB_LOG_ERROR << "Got path that isn't long enough "
2350 << objPath;
2351 continue;
2352 }
2353 // These indexes aren't intuitive, as boost::split puts an empty
2354 // string at the beginning
2355 const std::string& sensorType = split[4];
2356 const std::string& sensorName = split[5];
2357 BMCWEB_LOG_DEBUG << "sensorName " << sensorName
2358 << " sensorType " << sensorType;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002359 if (sensorNames->find(objPath) == sensorNames->end())
Shawn McCarneyde629b62019-03-08 10:42:51 -06002360 {
2361 BMCWEB_LOG_ERROR << sensorName << " not in sensor list ";
2362 continue;
2363 }
2364
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002365 // Find inventory item (if any) associated with sensor
2366 InventoryItem* inventoryItem =
2367 findInventoryItemForSensor(inventoryItems, objPath);
2368
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002369 const std::string& sensorSchema =
2370 SensorsAsyncResp->chassisSubNode;
2371
2372 nlohmann::json* sensorJson = nullptr;
2373
2374 if (sensorSchema == "Sensors")
Shawn McCarneyde629b62019-03-08 10:42:51 -06002375 {
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002376 SensorsAsyncResp->res.jsonValue["@odata.id"] =
2377 "/redfish/v1/Chassis/" + SensorsAsyncResp->chassisId +
2378 "/" + SensorsAsyncResp->chassisSubNode + "/" +
2379 sensorName;
2380 sensorJson = &(SensorsAsyncResp->res.jsonValue);
Shawn McCarneyde629b62019-03-08 10:42:51 -06002381 }
2382 else
2383 {
Ed Tanous271584a2019-07-09 16:24:22 -07002384 std::string fieldName;
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002385 if (sensorType == "temperature")
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002386 {
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002387 fieldName = "Temperatures";
2388 }
2389 else if (sensorType == "fan" || sensorType == "fan_tach" ||
2390 sensorType == "fan_pwm")
2391 {
2392 fieldName = "Fans";
2393 }
2394 else if (sensorType == "voltage")
2395 {
2396 fieldName = "Voltages";
2397 }
2398 else if (sensorType == "power")
2399 {
2400 if (!sensorName.compare("total_power"))
2401 {
2402 fieldName = "PowerControl";
2403 }
2404 else if ((inventoryItem != nullptr) &&
2405 (inventoryItem->isPowerSupply))
2406 {
2407 fieldName = "PowerSupplies";
2408 }
2409 else
2410 {
2411 // Other power sensors are in SensorCollection
2412 continue;
2413 }
2414 }
2415 else
2416 {
2417 BMCWEB_LOG_ERROR << "Unsure how to handle sensorType "
2418 << sensorType;
2419 continue;
2420 }
2421
2422 nlohmann::json& tempArray =
2423 SensorsAsyncResp->res.jsonValue[fieldName];
2424 if (fieldName == "PowerControl")
2425 {
2426 if (tempArray.empty())
2427 {
2428 // Put multiple "sensors" into a single
2429 // PowerControl. Follows MemberId naming and
2430 // naming in power.hpp.
2431 tempArray.push_back(
2432 {{"@odata.id",
2433 "/redfish/v1/Chassis/" +
2434 SensorsAsyncResp->chassisId + "/" +
2435 SensorsAsyncResp->chassisSubNode + "#/" +
2436 fieldName + "/0"}});
2437 }
2438 sensorJson = &(tempArray.back());
2439 }
2440 else if (fieldName == "PowerSupplies")
2441 {
2442 if (inventoryItem != nullptr)
2443 {
2444 sensorJson =
2445 &(getPowerSupply(tempArray, *inventoryItem,
2446 SensorsAsyncResp->chassisId));
2447 }
2448 }
2449 else
2450 {
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002451 tempArray.push_back(
2452 {{"@odata.id",
2453 "/redfish/v1/Chassis/" +
2454 SensorsAsyncResp->chassisId + "/" +
2455 SensorsAsyncResp->chassisSubNode + "#/" +
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002456 fieldName + "/"}});
2457 sensorJson = &(tempArray.back());
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002458 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002459 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06002460
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002461 if (sensorJson != nullptr)
2462 {
2463 objectInterfacesToJson(sensorName, sensorType,
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002464 SensorsAsyncResp->chassisSubNode,
Shawn McCarneyadc4f0d2019-07-24 09:21:50 -05002465 objDictEntry.second, *sensorJson,
2466 inventoryItem);
2467 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06002468 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002469 if (SensorsAsyncResp.use_count() == 1)
James Feist8bd25cc2019-03-15 15:14:00 -07002470 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002471 sortJSONResponse(SensorsAsyncResp);
2472 if (SensorsAsyncResp->chassisSubNode == "Thermal")
2473 {
2474 populateFanRedundancy(SensorsAsyncResp);
2475 }
James Feist8bd25cc2019-03-15 15:14:00 -07002476 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06002477 BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit";
2478 };
2479
2480 // Find DBus object path that implements ObjectManager for the current
2481 // connection. If no mapping found, default to "/".
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002482 auto iter = objectMgrPaths->find(connection);
Shawn McCarneyde629b62019-03-08 10:42:51 -06002483 const std::string& objectMgrPath =
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05002484 (iter != objectMgrPaths->end()) ? iter->second : "/";
Shawn McCarneyde629b62019-03-08 10:42:51 -06002485 BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
2486 << objectMgrPath;
2487
2488 crow::connections::systemBus->async_method_call(
2489 getManagedObjectsCb, connection, objectMgrPath,
2490 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
2491 };
2492 BMCWEB_LOG_DEBUG << "getSensorData exit";
2493}
2494
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002495void processSensorList(
2496 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
2497 std::shared_ptr<boost::container::flat_set<std::string>> sensorNames)
2498{
2499 auto getConnectionCb =
2500 [SensorsAsyncResp, sensorNames](
2501 const boost::container::flat_set<std::string>& connections) {
2502 BMCWEB_LOG_DEBUG << "getConnectionCb enter";
2503 auto getObjectManagerPathsCb =
2504 [SensorsAsyncResp, sensorNames, connections](
2505 std::shared_ptr<
2506 boost::container::flat_map<std::string, std::string>>
2507 objectMgrPaths) {
2508 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb enter";
2509 auto getInventoryItemsCb =
2510 [SensorsAsyncResp, sensorNames, connections,
2511 objectMgrPaths](
2512 std::shared_ptr<std::vector<InventoryItem>>
2513 inventoryItems) {
2514 BMCWEB_LOG_DEBUG << "getInventoryItemsCb enter";
2515 // Get sensor data and store results in JSON
2516 getSensorData(SensorsAsyncResp, sensorNames,
2517 connections, objectMgrPaths,
2518 inventoryItems);
2519 BMCWEB_LOG_DEBUG << "getInventoryItemsCb exit";
2520 };
2521
2522 // Get inventory items associated with sensors
2523 getInventoryItems(SensorsAsyncResp, sensorNames,
2524 objectMgrPaths,
2525 std::move(getInventoryItemsCb));
2526
2527 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb exit";
2528 };
2529
2530 // Get mapping from connection names to the DBus object
2531 // paths that implement the ObjectManager interface
2532 getObjectManagerPaths(SensorsAsyncResp,
2533 std::move(getObjectManagerPathsCb));
2534 BMCWEB_LOG_DEBUG << "getConnectionCb exit";
2535 };
2536
2537 // Get set of connections that provide sensor values
2538 getConnections(SensorsAsyncResp, sensorNames, std::move(getConnectionCb));
2539}
2540
Shawn McCarneyde629b62019-03-08 10:42:51 -06002541/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01002542 * @brief Entry point for retrieving sensors data related to requested
2543 * chassis.
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02002544 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01002545 */
Ed Tanous1abe55e2018-09-05 08:30:59 -07002546void getChassisData(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
2547{
2548 BMCWEB_LOG_DEBUG << "getChassisData enter";
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002549 auto getChassisCb =
2550 [SensorsAsyncResp](
2551 std::shared_ptr<boost::container::flat_set<std::string>>
2552 sensorNames) {
2553 BMCWEB_LOG_DEBUG << "getChassisCb enter";
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002554 processSensorList(SensorsAsyncResp, sensorNames);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002555 BMCWEB_LOG_DEBUG << "getChassisCb exit";
2556 };
Jennifer Lee4f9a2132019-03-04 12:45:19 -08002557 SensorsAsyncResp->res.jsonValue["Redundancy"] = nlohmann::json::array();
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01002558
Shawn McCarney26f03892019-05-03 13:20:24 -05002559 // Get set of sensors in chassis
Ed Tanous1abe55e2018-09-05 08:30:59 -07002560 getChassis(SensorsAsyncResp, std::move(getChassisCb));
2561 BMCWEB_LOG_DEBUG << "getChassisData exit";
Ed Tanous271584a2019-07-09 16:24:22 -07002562}
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01002563
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302564/**
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002565 * @brief Find the requested sensorName in the list of all sensors supplied by
2566 * the chassis node
2567 *
2568 * @param sensorName The sensor name supplied in the PATCH request
2569 * @param sensorsList The list of sensors managed by the chassis node
2570 * @param sensorsModified The list of sensors that were found as a result of
2571 * repeated calls to this function
2572 */
2573bool findSensorNameUsingSensorPath(
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05302574 std::string_view sensorName,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002575 boost::container::flat_set<std::string>& sensorsList,
2576 boost::container::flat_set<std::string>& sensorsModified)
2577{
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05302578 for (std::string_view chassisSensor : sensorsList)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002579 {
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05302580 std::size_t pos = chassisSensor.rfind("/");
2581 if (pos >= (chassisSensor.size() - 1))
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002582 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002583 continue;
2584 }
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05302585 std::string_view thisSensorName = chassisSensor.substr(pos + 1);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002586 if (thisSensorName == sensorName)
2587 {
2588 sensorsModified.emplace(chassisSensor);
2589 return true;
2590 }
2591 }
2592 return false;
2593}
2594
2595/**
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302596 * @brief Entry point for overriding sensor values of given sensor
2597 *
2598 * @param res response object
Carol Wang4bb3dc32019-10-17 18:15:02 +08002599 * @param allCollections Collections extract from sensors' request patch info
jayaprakash Mutyala70d1d0a2020-01-21 23:41:36 +00002600 * @param chassisSubNode Chassis Node for which the query has to happen
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302601 */
jayaprakash Mutyala70d1d0a2020-01-21 23:41:36 +00002602void setSensorsOverride(
Carol Wang4bb3dc32019-10-17 18:15:02 +08002603 std::shared_ptr<SensorsAsyncResp> sensorAsyncResp,
2604 std::unordered_map<std::string, std::vector<nlohmann::json>>&
jayaprakash Mutyala397fd612020-02-06 23:33:34 +00002605 allCollections)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302606{
jayaprakash Mutyala70d1d0a2020-01-21 23:41:36 +00002607 BMCWEB_LOG_INFO << "setSensorsOverride for subNode"
Carol Wang4bb3dc32019-10-17 18:15:02 +08002608 << sensorAsyncResp->chassisSubNode << "\n";
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302609
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302610 const char* propertyValueName;
2611 std::unordered_map<std::string, std::pair<double, std::string>> overrideMap;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302612 std::string memberId;
2613 double value;
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302614 for (auto& collectionItems : allCollections)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302615 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302616 if (collectionItems.first == "Temperatures")
2617 {
2618 propertyValueName = "ReadingCelsius";
2619 }
2620 else if (collectionItems.first == "Fans")
2621 {
2622 propertyValueName = "Reading";
2623 }
2624 else
2625 {
2626 propertyValueName = "ReadingVolts";
2627 }
2628 for (auto& item : collectionItems.second)
2629 {
Carol Wang4bb3dc32019-10-17 18:15:02 +08002630 if (!json_util::readJson(item, sensorAsyncResp->res, "MemberId",
2631 memberId, propertyValueName, value))
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302632 {
2633 return;
2634 }
2635 overrideMap.emplace(memberId,
2636 std::make_pair(value, collectionItems.first));
2637 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302638 }
Carol Wang4bb3dc32019-10-17 18:15:02 +08002639
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002640 auto getChassisSensorListCb = [sensorAsyncResp,
2641 overrideMap](const std::shared_ptr<
2642 boost::container::flat_set<
2643 std::string>>
2644 sensorsList) {
2645 // Match sensor names in the PATCH request to those managed by the
2646 // chassis node
2647 const std::shared_ptr<boost::container::flat_set<std::string>>
2648 sensorNames =
2649 std::make_shared<boost::container::flat_set<std::string>>();
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302650 for (const auto& item : overrideMap)
2651 {
2652 const auto& sensor = item.first;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07002653 if (!findSensorNameUsingSensorPath(sensor, *sensorsList,
2654 *sensorNames))
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302655 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302656 BMCWEB_LOG_INFO << "Unable to find memberId " << item.first;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302657 messages::resourceNotFound(sensorAsyncResp->res,
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302658 item.second.second, item.first);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302659 return;
2660 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302661 }
2662 // Get the connection to which the memberId belongs
2663 auto getObjectsWithConnectionCb =
2664 [sensorAsyncResp, overrideMap](
2665 const boost::container::flat_set<std::string>& connections,
2666 const std::set<std::pair<std::string, std::string>>&
2667 objectsWithConnection) {
2668 if (objectsWithConnection.size() != overrideMap.size())
2669 {
2670 BMCWEB_LOG_INFO
2671 << "Unable to find all objects with proper connection "
2672 << objectsWithConnection.size() << " requested "
2673 << overrideMap.size() << "\n";
2674 messages::resourceNotFound(
2675 sensorAsyncResp->res,
2676 sensorAsyncResp->chassisSubNode == "Thermal"
2677 ? "Temperatures"
2678 : "Voltages",
2679 "Count");
2680 return;
2681 }
2682 for (const auto& item : objectsWithConnection)
2683 {
2684
2685 auto lastPos = item.first.rfind('/');
2686 if (lastPos == std::string::npos)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302687 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302688 messages::internalError(sensorAsyncResp->res);
2689 return;
2690 }
2691 std::string sensorName = item.first.substr(lastPos + 1);
2692
2693 const auto& iterator = overrideMap.find(sensorName);
2694 if (iterator == overrideMap.end())
2695 {
2696 BMCWEB_LOG_INFO << "Unable to find sensor object"
2697 << item.first << "\n";
2698 messages::internalError(sensorAsyncResp->res);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302699 return;
2700 }
2701 crow::connections::systemBus->async_method_call(
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302702 [sensorAsyncResp](const boost::system::error_code ec) {
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302703 if (ec)
2704 {
2705 BMCWEB_LOG_DEBUG
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302706 << "setOverrideValueStatus DBUS error: "
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302707 << ec;
2708 messages::internalError(sensorAsyncResp->res);
2709 return;
2710 }
2711 },
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302712 item.second, item.first,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302713 "org.freedesktop.DBus.Properties", "Set",
2714 "xyz.openbmc_project.Sensor.Value", "Value",
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05302715 sdbusplus::message::variant<double>(
2716 iterator->second.first));
2717 }
2718 };
2719 // Get object with connection for the given sensor name
2720 getObjectsWithConnection(sensorAsyncResp, sensorNames,
2721 std::move(getObjectsWithConnectionCb));
2722 };
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05302723 // get full sensor list for the given chassisId and cross verify the sensor.
2724 getChassis(sensorAsyncResp, std::move(getChassisSensorListCb));
2725}
2726
jayaprakash Mutyala70d1d0a2020-01-21 23:41:36 +00002727bool isOverridingAllowed(const std::string& manufacturingModeStatus)
2728{
2729 if (manufacturingModeStatus ==
2730 "xyz.openbmc_project.Control.Security.SpecialMode.Modes.Manufacturing")
2731 {
2732 return true;
2733 }
2734
2735#ifdef BMCWEB_ENABLE_VALIDATION_UNSECURE_FEATURE
2736 if (manufacturingModeStatus == "xyz.openbmc_project.Control.Security."
2737 "SpecialMode.Modes.ValidationUnsecure")
2738 {
2739 return true;
2740 }
2741
2742#endif
2743
2744 return false;
2745}
2746
2747/**
2748 * @brief Entry point for Checking the manufacturing mode before doing sensor
2749 * override values of given sensor
2750 *
2751 * @param res response object
2752 * @param allCollections Collections extract from sensors' request patch info
jayaprakash Mutyala397fd612020-02-06 23:33:34 +00002753 * @param chassisSubNode Chassis Node for which the query has to happen
jayaprakash Mutyala70d1d0a2020-01-21 23:41:36 +00002754 */
2755void checkAndDoSensorsOverride(
2756 std::shared_ptr<SensorsAsyncResp> sensorAsyncResp,
jayaprakash Mutyala397fd612020-02-06 23:33:34 +00002757 std::unordered_map<std::string, std::vector<nlohmann::json>>&
2758 allCollections)
jayaprakash Mutyala70d1d0a2020-01-21 23:41:36 +00002759{
2760 BMCWEB_LOG_INFO << "checkAndDoSensorsOverride for subnode"
2761 << sensorAsyncResp->chassisSubNode << "\n";
2762
2763 const std::array<std::string, 1> interfaces = {
2764 "xyz.openbmc_project.Security.SpecialMode"};
2765
2766 crow::connections::systemBus->async_method_call(
jayaprakash Mutyala397fd612020-02-06 23:33:34 +00002767 [sensorAsyncResp, allCollections](const boost::system::error_code ec,
2768 const GetSubTreeType& resp) mutable {
jayaprakash Mutyala70d1d0a2020-01-21 23:41:36 +00002769 if (ec)
2770 {
2771 BMCWEB_LOG_DEBUG
2772 << "Error in querying GetSubTree with Object Mapper. "
2773 << ec;
2774 messages::internalError(sensorAsyncResp->res);
2775 return;
2776 }
2777 if (!resp.size())
2778 {
2779 // Special mode manager doesn't exist, proceed with sensor
2780 // override
jayaprakash Mutyala397fd612020-02-06 23:33:34 +00002781 setSensorsOverride(sensorAsyncResp, allCollections);
jayaprakash Mutyala70d1d0a2020-01-21 23:41:36 +00002782 return;
2783 }
2784
2785 if (resp.size() != 1)
2786 {
2787 BMCWEB_LOG_DEBUG << "Queried object count mismatch. ";
2788 messages::internalError(sensorAsyncResp->res);
2789 return;
2790 }
2791 const std::string& path = resp[0].first;
2792 const std::string& serviceName = resp[0].second.begin()->first;
2793
2794 if (path.empty() || serviceName.empty())
2795 {
2796 BMCWEB_LOG_DEBUG
2797 << "Path or service name is returned as empty. ";
2798 messages::internalError(sensorAsyncResp->res);
2799 return;
2800 }
2801
2802 // Sensor override is allowed only in manufacturing mode or
2803 // validation unsecure mode .
2804 crow::connections::systemBus->async_method_call(
jayaprakash Mutyala397fd612020-02-06 23:33:34 +00002805 [sensorAsyncResp, allCollections,
jayaprakash Mutyala70d1d0a2020-01-21 23:41:36 +00002806 path](const boost::system::error_code ec,
2807 std::variant<std::string>& getManufactMode) mutable {
2808 if (ec)
2809 {
2810 BMCWEB_LOG_DEBUG
2811 << "Error in querying Special mode property " << ec;
2812 messages::internalError(sensorAsyncResp->res);
2813 return;
2814 }
2815
2816 const std::string* manufacturingModeStatus =
2817 std::get_if<std::string>(&getManufactMode);
2818
2819 if (nullptr == manufacturingModeStatus)
2820 {
2821 BMCWEB_LOG_DEBUG << "Sensor override mode is not "
2822 "Enabled. Returning ... ";
2823 messages::internalError(sensorAsyncResp->res);
2824 return;
2825 }
2826
2827 if (isOverridingAllowed(*manufacturingModeStatus))
2828 {
2829 BMCWEB_LOG_INFO << "Manufacturing mode is Enabled. "
2830 "Proceeding further... ";
jayaprakash Mutyala397fd612020-02-06 23:33:34 +00002831 setSensorsOverride(sensorAsyncResp, allCollections);
jayaprakash Mutyala70d1d0a2020-01-21 23:41:36 +00002832 }
2833 else
2834 {
2835 BMCWEB_LOG_WARNING
2836 << "Manufacturing mode is not Enabled...can't "
2837 "Override the sensor value. ";
2838
2839 messages::actionNotSupported(
2840 sensorAsyncResp->res,
2841 "Overriding of Sensor Value for non "
2842 "manufacturing mode");
2843 return;
2844 }
2845 },
2846 serviceName, path, "org.freedesktop.DBus.Properties", "Get",
2847 "xyz.openbmc_project.Security.SpecialMode", "SpecialMode");
2848 },
2849
2850 "xyz.openbmc_project.ObjectMapper",
2851 "/xyz/openbmc_project/object_mapper",
2852 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 5, interfaces);
2853}
2854
Anthony Wilson95a3eca2019-06-11 10:44:47 -05002855class SensorCollection : public Node
2856{
2857 public:
2858 SensorCollection(CrowApp& app) :
2859 Node(app, "/redfish/v1/Chassis/<str>/Sensors", std::string())
2860 {
2861 entityPrivileges = {
2862 {boost::beast::http::verb::get, {{"Login"}}},
2863 {boost::beast::http::verb::head, {{"Login"}}},
2864 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2865 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2866 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2867 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2868 }
2869
2870 private:
2871 std::vector<const char*> typeList = {
2872 "/xyz/openbmc_project/sensors/power",
2873 "/xyz/openbmc_project/sensors/current"};
2874 void doGet(crow::Response& res, const crow::Request& req,
2875 const std::vector<std::string>& params) override
2876 {
2877 BMCWEB_LOG_DEBUG << "SensorCollection doGet enter";
2878 if (params.size() != 1)
2879 {
2880 BMCWEB_LOG_DEBUG << "SensorCollection doGet param size < 1";
2881 messages::internalError(res);
2882 res.end();
2883 return;
2884 }
2885
2886 const std::string& chassisId = params[0];
2887 std::shared_ptr<SensorsAsyncResp> asyncResp =
2888 std::make_shared<SensorsAsyncResp>(res, chassisId, typeList,
2889 "Sensors");
2890
2891 auto getChassisCb =
2892 [asyncResp](std::shared_ptr<boost::container::flat_set<std::string>>
2893 sensorNames) {
2894 BMCWEB_LOG_DEBUG << "getChassisCb enter";
2895
2896 nlohmann::json& entriesArray =
2897 asyncResp->res.jsonValue["Members"];
2898 for (auto& sensor : *sensorNames)
2899 {
2900 BMCWEB_LOG_DEBUG << "Adding sensor: " << sensor;
2901
2902 std::size_t lastPos = sensor.rfind("/");
2903 if (lastPos == std::string::npos ||
2904 lastPos + 1 >= sensor.size())
2905 {
2906 BMCWEB_LOG_ERROR << "Invalid sensor path: " << sensor;
2907 messages::internalError(asyncResp->res);
2908 return;
2909 }
2910 std::string sensorName = sensor.substr(lastPos + 1);
2911 entriesArray.push_back(
2912 {{"@odata.id",
2913 "/redfish/v1/Chassis/" + asyncResp->chassisId + "/" +
2914 asyncResp->chassisSubNode + "/" + sensorName}});
2915 }
2916
2917 asyncResp->res.jsonValue["Members@odata.count"] =
2918 entriesArray.size();
2919 BMCWEB_LOG_DEBUG << "getChassisCb exit";
2920 };
2921
2922 // Get set of sensors in chassis
2923 getChassis(asyncResp, std::move(getChassisCb));
2924 BMCWEB_LOG_DEBUG << "SensorCollection doGet exit";
2925 }
2926};
2927
2928class Sensor : public Node
2929{
2930 public:
2931 Sensor(CrowApp& app) :
2932 Node(app, "/redfish/v1/Chassis/<str>/Sensors/<str>/", std::string(),
2933 std::string())
2934 {
2935 entityPrivileges = {
2936 {boost::beast::http::verb::get, {{"Login"}}},
2937 {boost::beast::http::verb::head, {{"Login"}}},
2938 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2939 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2940 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2941 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2942 }
2943
2944 private:
2945 void doGet(crow::Response& res, const crow::Request& req,
2946 const std::vector<std::string>& params) override
2947 {
2948 BMCWEB_LOG_DEBUG << "Sensor doGet enter";
2949 if (params.size() != 2)
2950 {
2951 BMCWEB_LOG_DEBUG << "Sensor doGet param size < 2";
2952 messages::internalError(res);
2953 res.end();
2954 return;
2955 }
2956 const std::string& chassisId = params[0];
2957 std::shared_ptr<SensorsAsyncResp> asyncResp =
2958 std::make_shared<SensorsAsyncResp>(
2959 res, chassisId, std::vector<const char*>(), "Sensors");
2960
2961 const std::string& sensorName = params[1];
2962 const std::array<const char*, 1> interfaces = {
2963 "xyz.openbmc_project.Sensor.Value"};
2964
2965 // Get a list of all of the sensors that implement Sensor.Value
2966 // and get the path and service name associated with the sensor
2967 crow::connections::systemBus->async_method_call(
2968 [asyncResp, sensorName](const boost::system::error_code ec,
2969 const GetSubTreeType& subtree) {
2970 BMCWEB_LOG_DEBUG << "respHandler1 enter";
2971 if (ec)
2972 {
2973 messages::internalError(asyncResp->res);
2974 BMCWEB_LOG_ERROR << "Sensor getSensorPaths resp_handler: "
2975 << "Dbus error " << ec;
2976 return;
2977 }
2978
2979 GetSubTreeType::const_iterator it = std::find_if(
2980 subtree.begin(), subtree.end(),
2981 [sensorName](
2982 const std::pair<
2983 std::string,
2984 std::vector<std::pair<std::string,
2985 std::vector<std::string>>>>&
2986 object) {
2987 std::string_view sensor = object.first;
2988 std::size_t lastPos = sensor.rfind("/");
2989 if (lastPos == std::string::npos ||
2990 lastPos + 1 >= sensor.size())
2991 {
2992 BMCWEB_LOG_ERROR << "Invalid sensor path: "
2993 << sensor;
2994 return false;
2995 }
2996 std::string_view name = sensor.substr(lastPos + 1);
2997
2998 return name == sensorName;
2999 });
3000
3001 if (it == subtree.end())
3002 {
3003 BMCWEB_LOG_ERROR << "Could not find path for sensor: "
3004 << sensorName;
3005 messages::resourceNotFound(asyncResp->res, "Sensor",
3006 sensorName);
3007 return;
3008 }
3009 std::string_view sensorPath = (*it).first;
3010 BMCWEB_LOG_DEBUG << "Found sensor path for sensor '"
3011 << sensorName << "': " << sensorPath;
3012
3013 const std::shared_ptr<boost::container::flat_set<std::string>>
3014 sensorList = std::make_shared<
3015 boost::container::flat_set<std::string>>();
3016
3017 sensorList->emplace(sensorPath);
3018 processSensorList(asyncResp, sensorList);
3019 BMCWEB_LOG_DEBUG << "respHandler1 exit";
3020 },
3021 "xyz.openbmc_project.ObjectMapper",
3022 "/xyz/openbmc_project/object_mapper",
3023 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
3024 "/xyz/openbmc_project/sensors", 2, interfaces);
3025 }
3026};
3027
Ed Tanous1abe55e2018-09-05 08:30:59 -07003028} // namespace redfish