blob: 0ed47f73a3c0a56331fd0b44d6f03ffc9e6bee56 [file] [log] [blame]
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
18#include <math.h>
Ed Tanous1abe55e2018-09-05 08:30:59 -070019
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010020#include <boost/algorithm/string/predicate.hpp>
21#include <boost/algorithm/string/split.hpp>
22#include <boost/container/flat_map.hpp>
23#include <boost/range/algorithm/replace_copy_if.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070024#include <dbus_singleton.hpp>
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +053025#include <utils/json_utils.hpp>
Ed Tanousabf2add2019-01-22 16:40:12 -080026#include <variant>
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010027
Ed Tanous1abe55e2018-09-05 08:30:59 -070028namespace redfish
29{
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010030
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010031using GetSubTreeType = std::vector<
32 std::pair<std::string,
33 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
34
Eddie James028f7eb2019-05-17 21:24:36 +000035using SensorVariant = std::variant<int64_t, double, uint32_t, bool>;
Ed Tanousaa2e59c2018-04-12 12:17:20 -070036
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010037using ManagedObjectsVectorType = std::vector<std::pair<
Ed Tanousaa2e59c2018-04-12 12:17:20 -070038 sdbusplus::message::object_path,
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010039 boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -070040 std::string, boost::container::flat_map<std::string, SensorVariant>>>>;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010041
42/**
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020043 * SensorsAsyncResp
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010044 * Gathers data needed for response processing after async calls are done
45 */
Ed Tanous1abe55e2018-09-05 08:30:59 -070046class SensorsAsyncResp
47{
48 public:
49 SensorsAsyncResp(crow::Response& response, const std::string& chassisId,
Ed Tanous85e14242019-06-27 15:04:09 -070050 const std::vector<const char*> types,
Ed Tanous2474adf2018-09-05 16:31:16 -070051 const std::string& subNode) :
Ed Tanous43b761d2019-02-13 20:10:56 -080052 res(response),
53 chassisId(chassisId), types(types), chassisSubNode(subNode)
Ed Tanous1abe55e2018-09-05 08:30:59 -070054 {
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010055 }
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020056
Ed Tanous1abe55e2018-09-05 08:30:59 -070057 ~SensorsAsyncResp()
58 {
59 if (res.result() == boost::beast::http::status::internal_server_error)
60 {
61 // Reset the json object to clear out any data that made it in
62 // before the error happened todo(ed) handle error condition with
63 // proper code
64 res.jsonValue = nlohmann::json::object();
65 }
66 res.end();
67 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010068
Ed Tanous1abe55e2018-09-05 08:30:59 -070069 crow::Response& res;
70 std::string chassisId{};
71 const std::vector<const char*> types;
Ed Tanous2474adf2018-09-05 16:31:16 -070072 std::string chassisSubNode{};
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010073};
74
75/**
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +053076 * @brief Get objects with connection necessary for sensors
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020077 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010078 * @param sensorNames Sensors retrieved from chassis
79 * @param callback Callback for processing gathered connections
80 */
81template <typename Callback>
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +053082void getObjectsWithConnection(
83 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -070084 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +053085 Callback&& callback)
Ed Tanous1abe55e2018-09-05 08:30:59 -070086{
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +053087 BMCWEB_LOG_DEBUG << "getObjectsWithConnection enter";
Ed Tanous1abe55e2018-09-05 08:30:59 -070088 const std::string path = "/xyz/openbmc_project/sensors";
89 const std::array<std::string, 1> interfaces = {
90 "xyz.openbmc_project.Sensor.Value"};
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010091
Ed Tanous1abe55e2018-09-05 08:30:59 -070092 // Response handler for parsing objects subtree
93 auto respHandler = [callback{std::move(callback)}, SensorsAsyncResp,
94 sensorNames](const boost::system::error_code ec,
95 const GetSubTreeType& subtree) {
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +053096 BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler enter";
Ed Tanous1abe55e2018-09-05 08:30:59 -070097 if (ec)
98 {
Ed Tanous5f7d88c2018-11-14 14:08:56 -080099 messages::internalError(SensorsAsyncResp->res);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530100 BMCWEB_LOG_ERROR
101 << "getObjectsWithConnection resp_handler: Dbus error " << ec;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700102 return;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100103 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100104
Ed Tanous1abe55e2018-09-05 08:30:59 -0700105 BMCWEB_LOG_DEBUG << "Found " << subtree.size() << " subtrees";
106
107 // Make unique list of connections only for requested sensor types and
108 // found in the chassis
109 boost::container::flat_set<std::string> connections;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530110 std::set<std::pair<std::string, std::string>> objectsWithConnection;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700111 // Intrinsic to avoid malloc. Most systems will have < 8 sensor
112 // producers
113 connections.reserve(8);
114
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700115 BMCWEB_LOG_DEBUG << "sensorNames list count: " << sensorNames->size();
116 for (const std::string& tsensor : *sensorNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700117 {
118 BMCWEB_LOG_DEBUG << "Sensor to find: " << tsensor;
119 }
120
121 for (const std::pair<
122 std::string,
123 std::vector<std::pair<std::string, std::vector<std::string>>>>&
124 object : subtree)
125 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700126 if (sensorNames->find(object.first) != sensorNames->end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700127 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700128 for (const std::pair<std::string, std::vector<std::string>>&
129 objData : object.second)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700130 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700131 BMCWEB_LOG_DEBUG << "Adding connection: " << objData.first;
132 connections.insert(objData.first);
133 objectsWithConnection.insert(
134 std::make_pair(object.first, objData.first));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700135 }
136 }
137 }
138 BMCWEB_LOG_DEBUG << "Found " << connections.size() << " connections";
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530139 callback(std::move(connections), std::move(objectsWithConnection));
140 BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler exit";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700141 };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700142 // Make call to ObjectMapper to find all sensors objects
143 crow::connections::systemBus->async_method_call(
144 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
145 "/xyz/openbmc_project/object_mapper",
146 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 2, interfaces);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530147 BMCWEB_LOG_DEBUG << "getObjectsWithConnection exit";
148}
149
150/**
151 * @brief Create connections necessary for sensors
152 * @param SensorsAsyncResp Pointer to object holding response data
153 * @param sensorNames Sensors retrieved from chassis
154 * @param callback Callback for processing gathered connections
155 */
156template <typename Callback>
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700157void getConnections(
158 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
159 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
160 Callback&& callback)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530161{
162 auto objectsWithConnectionCb =
163 [callback](const boost::container::flat_set<std::string>& connections,
164 const std::set<std::pair<std::string, std::string>>&
165 objectsWithConnection) {
166 callback(std::move(connections));
167 };
168 getObjectsWithConnection(SensorsAsyncResp, sensorNames,
169 std::move(objectsWithConnectionCb));
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100170}
171
172/**
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700173 * @brief Shrinks the list of sensors for processing
174 * @param SensorsAysncResp The class holding the Redfish response
175 * @param allSensors A list of all the sensors associated to the
176 * chassis element (i.e. baseboard, front panel, etc...)
177 * @param activeSensors A list that is a reduction of the incoming
178 * allSensors list. Eliminate Thermal sensors when a Power request is
179 * made, and eliminate Power sensors when a Thermal request is made.
180 */
181void reduceSensorList(
182 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
183 const std::vector<std::string>* allSensors,
184 std::shared_ptr<boost::container::flat_set<std::string>> activeSensors)
185{
186 if (SensorsAsyncResp == nullptr)
187 {
188 return;
189 }
190 if ((allSensors == nullptr) || (activeSensors == nullptr))
191 {
192 messages::resourceNotFound(
193 SensorsAsyncResp->res, SensorsAsyncResp->chassisSubNode,
194 SensorsAsyncResp->chassisSubNode == "Thermal" ? "Temperatures"
195 : "Voltages");
196
197 return;
198 }
199 if (allSensors->empty())
200 {
201 // Nothing to do, the activeSensors object is also empty
202 return;
203 }
204
205 for (const char* type : SensorsAsyncResp->types)
206 {
207 for (const std::string& sensor : *allSensors)
208 {
209 if (boost::starts_with(sensor, type))
210 {
211 activeSensors->emplace(sensor);
212 }
213 }
214 }
215}
216
217/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100218 * @brief Retrieves requested chassis sensors and redundancy data from DBus .
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200219 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100220 * @param callback Callback for next step in gathered sensor processing
221 */
222template <typename Callback>
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700223void getChassis(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700224 Callback&& callback)
225{
226 BMCWEB_LOG_DEBUG << "getChassis enter";
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700227 const std::array<const char*, 3> interfaces = {
228 "xyz.openbmc_project.Inventory.Item.Board",
229 "xyz.openbmc_project.Inventory.Item.Chassis",
230 "xyz.openbmc_project.Inventory.Item.PowerSupply"};
231 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp](
232 const boost::system::error_code ec,
233 const std::vector<std::string>& chassisPaths) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700234 BMCWEB_LOG_DEBUG << "getChassis respHandler enter";
235 if (ec)
236 {
237 BMCWEB_LOG_ERROR << "getChassis respHandler DBUS error: " << ec;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700238 messages::internalError(sensorsAsyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700239 return;
240 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100241
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700242 const std::string* chassisPath = nullptr;
243 std::string chassisName;
244 for (const std::string& chassis : chassisPaths)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700245 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700246 std::size_t lastPos = chassis.rfind("/");
247 if (lastPos == std::string::npos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700248 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700249 BMCWEB_LOG_ERROR << "Failed to find '/' in " << chassis;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700250 continue;
251 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700252 chassisName = chassis.substr(lastPos + 1);
253 if (chassisName == sensorsAsyncResp->chassisId)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700254 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700255 chassisPath = &chassis;
256 break;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700257 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700258 }
259 if (chassisPath == nullptr)
260 {
261 messages::resourceNotFound(sensorsAsyncResp->res, "Chassis",
262 sensorsAsyncResp->chassisId);
263 return;
264 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700265
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700266 const std::string& chassisSubNode = sensorsAsyncResp->chassisSubNode;
267 if (chassisSubNode == "Power")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700268 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700269 sensorsAsyncResp->res.jsonValue["@odata.type"] =
270 "#Power.v1_5_2.Power";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700271 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700272 else if (chassisSubNode == "Thermal")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700273 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700274 sensorsAsyncResp->res.jsonValue["@odata.type"] =
275 "#Thermal.v1_4_0.Thermal";
Jennifer Lee4f9a2132019-03-04 12:45:19 -0800276 sensorsAsyncResp->res.jsonValue["Fans"] = nlohmann::json::array();
277 sensorsAsyncResp->res.jsonValue["Temperatures"] =
278 nlohmann::json::array();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700279 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700280 sensorsAsyncResp->res.jsonValue["@odata.id"] =
281 "/redfish/v1/Chassis/" + sensorsAsyncResp->chassisId + "/" +
282 chassisSubNode;
283
284 sensorsAsyncResp->res.jsonValue["@odata.context"] =
285 "/redfish/v1/$metadata#" + chassisSubNode + "." + chassisSubNode;
286 sensorsAsyncResp->res.jsonValue["Id"] = chassisSubNode;
287 sensorsAsyncResp->res.jsonValue["Name"] = chassisSubNode;
288
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500289 // Get the list of all sensors for this Chassis element
290 std::string sensorPath = *chassisPath + "/all_sensors";
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700291 crow::connections::systemBus->async_method_call(
292 [sensorsAsyncResp, callback{std::move(callback)}](
293 const boost::system::error_code ec,
294 const std::variant<std::vector<std::string>>&
295 variantEndpoints) {
296 if (ec)
297 {
298 if (ec.value() != EBADR)
299 {
300 messages::internalError(sensorsAsyncResp->res);
301 return;
302 }
303 }
304 const std::vector<std::string>* nodeSensorList =
305 std::get_if<std::vector<std::string>>(&(variantEndpoints));
306 if (nodeSensorList == nullptr)
307 {
308 messages::resourceNotFound(
309 sensorsAsyncResp->res, sensorsAsyncResp->chassisSubNode,
310 sensorsAsyncResp->chassisSubNode == "Thermal"
311 ? "Temperatures"
312 : "Voltages");
313 return;
314 }
315 const std::shared_ptr<boost::container::flat_set<std::string>>
316 culledSensorList = std::make_shared<
317 boost::container::flat_set<std::string>>();
318 reduceSensorList(sensorsAsyncResp, nodeSensorList,
319 culledSensorList);
320 callback(culledSensorList);
321 },
322 "xyz.openbmc_project.ObjectMapper", sensorPath,
323 "org.freedesktop.DBus.Properties", "Get",
324 "xyz.openbmc_project.Association", "endpoints");
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100325 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100326
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700327 // Get the Chassis Collection
Ed Tanous1abe55e2018-09-05 08:30:59 -0700328 crow::connections::systemBus->async_method_call(
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700329 respHandler, "xyz.openbmc_project.ObjectMapper",
330 "/xyz/openbmc_project/object_mapper",
331 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
332 "/xyz/openbmc_project/inventory", int32_t(0), interfaces);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700333 BMCWEB_LOG_DEBUG << "getChassis exit";
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100334}
335
336/**
Shawn McCarneyde629b62019-03-08 10:42:51 -0600337 * @brief Finds all DBus object paths that implement ObjectManager.
338 *
339 * Creates a mapping from the associated connection name to the object path.
340 *
341 * Finds the object paths asynchronously. Invokes callback when information has
342 * been obtained.
343 *
344 * The callback must have the following signature:
345 * @code
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500346 * callback(std::shared_ptr<boost::container::flat_map<std::string,
347 * std::string>> objectMgrPaths)
Shawn McCarneyde629b62019-03-08 10:42:51 -0600348 * @endcode
349 *
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700350 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyde629b62019-03-08 10:42:51 -0600351 * @param callback Callback to invoke when object paths obtained.
352 */
353template <typename Callback>
354void getObjectManagerPaths(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
355 Callback&& callback)
356{
357 BMCWEB_LOG_DEBUG << "getObjectManagerPaths enter";
358 const std::array<std::string, 1> interfaces = {
359 "org.freedesktop.DBus.ObjectManager"};
360
361 // Response handler for GetSubTree DBus method
362 auto respHandler = [callback{std::move(callback)},
363 SensorsAsyncResp](const boost::system::error_code ec,
364 const GetSubTreeType& subtree) {
365 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler enter";
366 if (ec)
367 {
368 messages::internalError(SensorsAsyncResp->res);
369 BMCWEB_LOG_ERROR << "getObjectManagerPaths respHandler: DBus error "
370 << ec;
371 return;
372 }
373
374 // Loop over returned object paths
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500375 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
376 objectMgrPaths = std::make_shared<
377 boost::container::flat_map<std::string, std::string>>();
Shawn McCarneyde629b62019-03-08 10:42:51 -0600378 for (const std::pair<
379 std::string,
380 std::vector<std::pair<std::string, std::vector<std::string>>>>&
381 object : subtree)
382 {
383 // Loop over connections for current object path
384 const std::string& objectPath = object.first;
385 for (const std::pair<std::string, std::vector<std::string>>&
386 objData : object.second)
387 {
388 // Add mapping from connection to object path
389 const std::string& connection = objData.first;
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500390 (*objectMgrPaths)[connection] = objectPath;
Shawn McCarneyde629b62019-03-08 10:42:51 -0600391 BMCWEB_LOG_DEBUG << "Added mapping " << connection << " -> "
392 << objectPath;
393 }
394 }
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500395 callback(objectMgrPaths);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600396 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler exit";
397 };
398
399 // Query mapper for all DBus object paths that implement ObjectManager
400 crow::connections::systemBus->async_method_call(
401 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
402 "/xyz/openbmc_project/object_mapper",
403 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", int32_t(0),
404 interfaces);
405 BMCWEB_LOG_DEBUG << "getObjectManagerPaths exit";
406}
407
408/**
James Feist34dd1792019-05-17 14:10:54 -0700409 * @brief Retrieves the health from a sensor .
410 * @param interfacesDict Map of all sensor interfaces
411 */
412
413static std::string getHealth(
414 const boost::container::flat_map<
415 std::string, boost::container::flat_map<std::string, SensorVariant>>&
416 interfacesDict)
417{
418 auto criticalThresholdIt =
419 interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Critical");
420 if (criticalThresholdIt != interfacesDict.end())
421 {
422 auto thresholdHighIt =
423 criticalThresholdIt->second.find("CriticalAlarmHigh");
424 auto thresholdLowIt =
425 criticalThresholdIt->second.find("CriticalAlarmLow");
426 if (thresholdHighIt != criticalThresholdIt->second.end())
427 {
428 const bool* asserted = std::get_if<bool>(&thresholdHighIt->second);
429 if (asserted == nullptr)
430 {
431 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
432 }
433 else if (*asserted)
434 {
435 return "Critical";
436 }
437 }
438 if (thresholdLowIt != criticalThresholdIt->second.end())
439 {
440 const bool* asserted = std::get_if<bool>(&thresholdLowIt->second);
441 if (asserted == nullptr)
442 {
443 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
444 }
445 else if (*asserted)
446 {
447 return "Critical";
448 }
449 }
450 }
451
452 auto warningThresholdIt =
453 interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Warning");
454 if (warningThresholdIt != interfacesDict.end())
455 {
456 auto thresholdHighIt =
457 warningThresholdIt->second.find("WarningAlarmHigh");
458 auto thresholdLowIt =
459 warningThresholdIt->second.find("WarningAlarmLow");
460 if (thresholdHighIt != warningThresholdIt->second.end())
461 {
462 const bool* asserted = std::get_if<bool>(&thresholdHighIt->second);
463 if (asserted == nullptr)
464 {
465 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
466 }
467 else if (*asserted)
468 {
469 return "Warning";
470 }
471 }
472 if (thresholdLowIt != warningThresholdIt->second.end())
473 {
474 const bool* asserted = std::get_if<bool>(&thresholdLowIt->second);
475 if (asserted == nullptr)
476 {
477 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
478 }
479 else if (*asserted)
480 {
481 return "Warning";
482 }
483 }
484 }
485 return "OK";
486}
487
488/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100489 * @brief Builds a json sensor representation of a sensor.
490 * @param sensorName The name of the sensor to be built
Gunnar Mills274fad52018-06-13 15:45:36 -0500491 * @param sensorType The type (temperature, fan_tach, etc) of the sensor to
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100492 * build
493 * @param interfacesDict A dictionary of the interfaces and properties of said
494 * interfaces to be built from
495 * @param sensor_json The json object to fill
496 */
497void objectInterfacesToJson(
498 const std::string& sensorName, const std::string& sensorType,
499 const boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700500 std::string, boost::container::flat_map<std::string, SensorVariant>>&
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100501 interfacesDict,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700502 nlohmann::json& sensor_json)
503{
504 // We need a value interface before we can do anything with it
505 auto valueIt = interfacesDict.find("xyz.openbmc_project.Sensor.Value");
506 if (valueIt == interfacesDict.end())
507 {
508 BMCWEB_LOG_ERROR << "Sensor doesn't have a value interface";
509 return;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100510 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100511
Ed Tanous1abe55e2018-09-05 08:30:59 -0700512 // Assume values exist as is (10^0 == 1) if no scale exists
513 int64_t scaleMultiplier = 0;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100514
Ed Tanous1abe55e2018-09-05 08:30:59 -0700515 auto scaleIt = valueIt->second.find("Scale");
516 // If a scale exists, pull value as int64, and use the scaling.
517 if (scaleIt != valueIt->second.end())
518 {
Ed Tanousabf2add2019-01-22 16:40:12 -0800519 const int64_t* int64Value = std::get_if<int64_t>(&scaleIt->second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700520 if (int64Value != nullptr)
521 {
522 scaleMultiplier = *int64Value;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100523 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100524 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700525
526 sensor_json["MemberId"] = sensorName;
Ed Tanouse742b6c2019-05-03 15:06:53 -0700527 sensor_json["Name"] = boost::replace_all_copy(sensorName, "_", " ");
528
Ed Tanous1abe55e2018-09-05 08:30:59 -0700529 sensor_json["Status"]["State"] = "Enabled";
James Feist34dd1792019-05-17 14:10:54 -0700530 sensor_json["Status"]["Health"] = getHealth(interfacesDict);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700531
532 // Parameter to set to override the type we get from dbus, and force it to
533 // int, regardless of what is available. This is used for schemas like fan,
534 // that require integers, not floats.
535 bool forceToInt = false;
536
537 const char* unit = "Reading";
538 if (sensorType == "temperature")
539 {
540 unit = "ReadingCelsius";
541 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Temperature";
542 // TODO(ed) Documentation says that path should be type fan_tach,
543 // implementation seems to implement fan
544 }
545 else if (sensorType == "fan" || sensorType == "fan_tach")
546 {
547 unit = "Reading";
548 sensor_json["ReadingUnits"] = "RPM";
549 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
550 forceToInt = true;
551 }
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700552 else if (sensorType == "fan_pwm")
553 {
554 unit = "Reading";
555 sensor_json["ReadingUnits"] = "Percent";
556 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
557 forceToInt = true;
558 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700559 else if (sensorType == "voltage")
560 {
561 unit = "ReadingVolts";
562 sensor_json["@odata.type"] = "#Power.v1_0_0.Voltage";
563 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700564 else if (sensorType == "power")
565 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700566 std::string sensorNameLower =
567 boost::algorithm::to_lower_copy(sensorName);
568
Eddie James028f7eb2019-05-17 21:24:36 +0000569 if (!sensorName.compare("total_power"))
570 {
Gunnar Mills7ab06f42019-07-02 13:07:16 -0500571 sensor_json["@odata.type"] = "#Power.v1_0_0.PowerControl";
572 // Put multiple "sensors" into a single PowerControl, so have
573 // generic names for MemberId and Name. Follows Redfish mockup.
574 sensor_json["MemberId"] = "0";
575 sensor_json["Name"] = "Chassis Power Control";
Eddie James028f7eb2019-05-17 21:24:36 +0000576 unit = "PowerConsumedWatts";
577 }
578 else if (sensorNameLower.find("input") != std::string::npos)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700579 {
580 unit = "PowerInputWatts";
581 }
582 else
583 {
584 unit = "PowerOutputWatts";
585 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700586 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700587 else
588 {
589 BMCWEB_LOG_ERROR << "Redfish cannot map object type for " << sensorName;
590 return;
591 }
592 // Map of dbus interface name, dbus property name and redfish property_name
593 std::vector<std::tuple<const char*, const char*, const char*>> properties;
594 properties.reserve(7);
595
596 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600597
598 // If sensor type doesn't map to Redfish PowerSupply, add threshold props
599 if ((sensorType != "current") && (sensorType != "power"))
600 {
601 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
602 "WarningHigh", "UpperThresholdNonCritical");
603 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
604 "WarningLow", "LowerThresholdNonCritical");
605 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
606 "CriticalHigh", "UpperThresholdCritical");
607 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
608 "CriticalLow", "LowerThresholdCritical");
609 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700610
Ed Tanous2474adf2018-09-05 16:31:16 -0700611 // TODO Need to get UpperThresholdFatal and LowerThresholdFatal
612
Ed Tanous1abe55e2018-09-05 08:30:59 -0700613 if (sensorType == "temperature")
614 {
615 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
616 "MinReadingRangeTemp");
617 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
618 "MaxReadingRangeTemp");
619 }
Shawn McCarneyde629b62019-03-08 10:42:51 -0600620 else if ((sensorType != "current") && (sensorType != "power"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700621 {
Shawn McCarneyde629b62019-03-08 10:42:51 -0600622 // Sensor type doesn't map to Redfish PowerSupply; add min/max props
Ed Tanous1abe55e2018-09-05 08:30:59 -0700623 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
624 "MinReadingRange");
625 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
626 "MaxReadingRange");
627 }
628
629 for (const std::tuple<const char*, const char*, const char*>& p :
630 properties)
631 {
632 auto interfaceProperties = interfacesDict.find(std::get<0>(p));
633 if (interfaceProperties != interfacesDict.end())
634 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000635 auto valueIt = interfaceProperties->second.find(std::get<1>(p));
636 if (valueIt != interfaceProperties->second.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700637 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000638 const SensorVariant& valueVariant = valueIt->second;
639 nlohmann::json& valueIt = sensor_json[std::get<2>(p)];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700640 // Attempt to pull the int64 directly
Ed Tanousabf2add2019-01-22 16:40:12 -0800641 const int64_t* int64Value = std::get_if<int64_t>(&valueVariant);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700642
Ed Tanousabf2add2019-01-22 16:40:12 -0800643 const double* doubleValue = std::get_if<double>(&valueVariant);
Eddie James028f7eb2019-05-17 21:24:36 +0000644 const uint32_t* uValue = std::get_if<uint32_t>(&valueVariant);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700645 double temp = 0.0;
646 if (int64Value != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700647 {
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700648 temp = *int64Value;
649 }
650 else if (doubleValue != nullptr)
651 {
652 temp = *doubleValue;
653 }
Eddie James028f7eb2019-05-17 21:24:36 +0000654 else if (uValue != nullptr)
655 {
656 temp = *uValue;
657 }
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700658 else
659 {
660 BMCWEB_LOG_ERROR
661 << "Got value interface that wasn't int or double";
662 continue;
663 }
664 temp = temp * std::pow(10, scaleMultiplier);
665 if (forceToInt)
666 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000667 valueIt = static_cast<int64_t>(temp);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700668 }
669 else
670 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000671 valueIt = temp;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700672 }
673 }
674 }
675 }
676 BMCWEB_LOG_DEBUG << "Added sensor " << sensorName;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100677}
678
James Feist8bd25cc2019-03-15 15:14:00 -0700679static void
680 populateFanRedundancy(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp)
681{
682 crow::connections::systemBus->async_method_call(
683 [sensorsAsyncResp](const boost::system::error_code ec,
684 const GetSubTreeType& resp) {
685 if (ec)
686 {
687 return; // don't have to have this interface
688 }
Ed Tanouse278c182019-03-13 16:23:37 -0700689 for (const std::pair<std::string,
690 std::vector<std::pair<
691 std::string, std::vector<std::string>>>>&
692 pathPair : resp)
James Feist8bd25cc2019-03-15 15:14:00 -0700693 {
Ed Tanouse278c182019-03-13 16:23:37 -0700694 const std::string& path = pathPair.first;
695 const std::vector<
696 std::pair<std::string, std::vector<std::string>>>& objDict =
697 pathPair.second;
James Feist8bd25cc2019-03-15 15:14:00 -0700698 if (objDict.empty())
699 {
700 continue; // this should be impossible
701 }
702
703 const std::string& owner = objDict.begin()->first;
704 crow::connections::systemBus->async_method_call(
705 [path, owner,
706 sensorsAsyncResp](const boost::system::error_code ec,
707 std::variant<std::vector<std::string>>
708 variantEndpoints) {
709 if (ec)
710 {
711 return; // if they don't have an association we
712 // can't tell what chassis is
713 }
714 // verify part of the right chassis
715 auto endpoints = std::get_if<std::vector<std::string>>(
716 &variantEndpoints);
717
718 if (endpoints == nullptr)
719 {
720 BMCWEB_LOG_ERROR << "Invalid association interface";
721 messages::internalError(sensorsAsyncResp->res);
722 return;
723 }
724
725 auto found = std::find_if(
726 endpoints->begin(), endpoints->end(),
727 [sensorsAsyncResp](const std::string& entry) {
728 return entry.find(
729 sensorsAsyncResp->chassisId) !=
730 std::string::npos;
731 });
732
733 if (found == endpoints->end())
734 {
735 return;
736 }
737 crow::connections::systemBus->async_method_call(
738 [path, sensorsAsyncResp](
739 const boost::system::error_code ec,
740 const boost::container::flat_map<
741 std::string,
742 std::variant<uint8_t,
743 std::vector<std::string>,
744 std::string>>& ret) {
745 if (ec)
746 {
747 return; // don't have to have this
748 // interface
749 }
750 auto findFailures = ret.find("AllowedFailures");
751 auto findCollection = ret.find("Collection");
752 auto findStatus = ret.find("Status");
753
754 if (findFailures == ret.end() ||
755 findCollection == ret.end() ||
756 findStatus == ret.end())
757 {
758 BMCWEB_LOG_ERROR
759 << "Invalid redundancy interface";
760 messages::internalError(
761 sensorsAsyncResp->res);
762 return;
763 }
764
765 auto allowedFailures = std::get_if<uint8_t>(
766 &(findFailures->second));
767 auto collection =
768 std::get_if<std::vector<std::string>>(
769 &(findCollection->second));
770 auto status = std::get_if<std::string>(
771 &(findStatus->second));
772
773 if (allowedFailures == nullptr ||
774 collection == nullptr || status == nullptr)
775 {
776
777 BMCWEB_LOG_ERROR
778 << "Invalid redundancy interface "
779 "types";
780 messages::internalError(
781 sensorsAsyncResp->res);
782 return;
783 }
784 size_t lastSlash = path.rfind("/");
785 if (lastSlash == std::string::npos)
786 {
787 // this should be impossible
788 messages::internalError(
789 sensorsAsyncResp->res);
790 return;
791 }
792 std::string name = path.substr(lastSlash + 1);
793 std::replace(name.begin(), name.end(), '_',
794 ' ');
795
796 std::string health;
797
798 if (boost::ends_with(*status, "Full"))
799 {
800 health = "OK";
801 }
802 else if (boost::ends_with(*status, "Degraded"))
803 {
804 health = "Warning";
805 }
806 else
807 {
808 health = "Critical";
809 }
810 std::vector<nlohmann::json> redfishCollection;
811 const auto& fanRedfish =
812 sensorsAsyncResp->res.jsonValue["Fans"];
813 for (const std::string& item : *collection)
814 {
815 lastSlash = item.rfind("/");
816 // make a copy as collection is const
817 std::string itemName =
818 item.substr(lastSlash + 1);
819 /*
820 todo(ed): merge patch that fixes the names
821 std::replace(itemName.begin(),
822 itemName.end(), '_', ' ');*/
823 auto schemaItem = std::find_if(
824 fanRedfish.begin(), fanRedfish.end(),
825 [itemName](const nlohmann::json& fan) {
826 return fan["MemberId"] == itemName;
827 });
828 if (schemaItem != fanRedfish.end())
829 {
830 redfishCollection.push_back(
831 {{"@odata.id",
832 (*schemaItem)["@odata.id"]}});
833 }
834 else
835 {
836 BMCWEB_LOG_ERROR
837 << "failed to find fan in schema";
838 messages::internalError(
839 sensorsAsyncResp->res);
840 return;
841 }
842 }
843
844 auto& resp = sensorsAsyncResp->res
845 .jsonValue["Redundancy"];
846 resp.push_back(
847 {{"@odata.id",
848 "/refish/v1/Chassis/" +
849 sensorsAsyncResp->chassisId + "/" +
850 sensorsAsyncResp->chassisSubNode +
851 "#/Redundancy/" +
852 std::to_string(resp.size())},
853 {"@odata.type",
854 "#Redundancy.v1_3_2.Redundancy"},
855 {"MinNumNeeded",
856 collection->size() - *allowedFailures},
857 {"MemberId", name},
858 {"Mode", "N+m"},
859 {"Name", name},
860 {"RedundancySet", redfishCollection},
861 {"Status",
862 {{"Health", health},
863 {"State", "Enabled"}}}});
864 },
865 owner, path, "org.freedesktop.DBus.Properties",
866 "GetAll",
867 "xyz.openbmc_project.Control.FanRedundancy");
868 },
James Feist02e92e32019-06-26 12:07:05 -0700869 "xyz.openbmc_project.ObjectMapper", path + "/chassis",
James Feist8bd25cc2019-03-15 15:14:00 -0700870 "org.freedesktop.DBus.Properties", "Get",
871 "xyz.openbmc_project.Association", "endpoints");
872 }
873 },
874 "xyz.openbmc_project.ObjectMapper",
875 "/xyz/openbmc_project/object_mapper",
876 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
877 "/xyz/openbmc_project/control", 2,
878 std::array<const char*, 1>{
879 "xyz.openbmc_project.Control.FanRedundancy"});
880}
881
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700882void sortJSONResponse(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
883{
884 nlohmann::json& response = SensorsAsyncResp->res.jsonValue;
885 std::array<std::string, 2> sensorHeaders{"Temperatures", "Fans"};
886 if (SensorsAsyncResp->chassisSubNode == "Power")
887 {
888 sensorHeaders = {"Voltages", "PowerSupplies"};
889 }
890 for (const std::string& sensorGroup : sensorHeaders)
891 {
892 nlohmann::json::iterator entry = response.find(sensorGroup);
893 if (entry != response.end())
894 {
895 std::sort(entry->begin(), entry->end(),
896 [](nlohmann::json& c1, nlohmann::json& c2) {
897 return c1["Name"] < c2["Name"];
898 });
899
900 // add the index counts to the end of each entry
901 size_t count = 0;
902 for (nlohmann::json& sensorJson : *entry)
903 {
904 nlohmann::json::iterator odata = sensorJson.find("@odata.id");
905 if (odata == sensorJson.end())
906 {
907 continue;
908 }
909 std::string* value = odata->get_ptr<std::string*>();
910 if (value != nullptr)
911 {
912 *value += std::to_string(count);
913 count++;
914 }
915 }
916 }
917 }
918}
919
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100920/**
Shawn McCarney8fb49dd2019-06-12 17:47:00 -0500921 * @brief Finds the JSON object for the specified sensor.
922 *
923 * Searches the JSON response in sensorsAsyncResp for an object corresponding to
924 * the specified sensor.
925 *
926 * @param sensorsAsyncResp Pointer to object holding response data.
927 * @param sensorName DBus object path to the sensor.
928 * @return Pointer to JSON object, or nullptr if object not found.
929 */
930static nlohmann::json*
931 findSensorJson(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
932 const std::string& sensorName)
933{
934 // Get base name of sensor
935 std::size_t lastSlash = sensorName.rfind('/');
936 if (lastSlash != std::string::npos)
937 {
938 std::string baseSensorName = sensorName.substr(lastSlash + 1);
939
940 // Loop through JSON sensor groups that could contain sensor
941 nlohmann::json& response = sensorsAsyncResp->res.jsonValue;
942 std::array<std::string, 4> sensorGroups{"Temperatures", "Fans",
943 "Voltages", "PowerSupplies"};
944 for (const std::string& sensorGroup : sensorGroups)
945 {
946 nlohmann::json::iterator groupIt = response.find(sensorGroup);
947 if (groupIt != response.end())
948 {
949 // Loop through sensors in current group
950 for (nlohmann::json& sensorJson : *groupIt)
951 {
952 // Check if this is the sensor we are looking for
953 nlohmann::json::iterator memberIdIt =
954 sensorJson.find("MemberId");
955 if (memberIdIt != sensorJson.end())
956 {
957 std::string* memberId =
958 memberIdIt->get_ptr<std::string*>();
959 if ((memberId != nullptr) &&
960 (*memberId == baseSensorName))
961 {
962 return &sensorJson;
963 }
964 }
965 }
966 }
967 }
968 }
969
970 // Unable to find JSON object for specified sensor
971 return nullptr;
972}
973
974/**
975 * @brief Updates sensor status in JSON response based on inventory item status.
976 *
977 * Updates the status of the specified sensor based on the status of a related
978 * inventory item.
979 *
980 * Modifies the Redfish Status property in the JSON response if the inventory
981 * item indicates the hardware is not present or not functional.
982 *
983 * The D-Bus Present and Functional properties are typically on the inventory
984 * item rather than the sensor.
985 *
986 * @param sensorsAsyncResp Pointer to object holding response data.
987 * @param sensorName DBus object path to the sensor.
988 * @param interfacesDict Map containing the interfaces and properties of the
989 * inventory item associated with this sensor.
990 */
991static void updateSensorStatus(
992 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
993 const std::string& sensorName,
994 const boost::container::flat_map<
995 std::string, boost::container::flat_map<std::string, SensorVariant>>&
996 interfacesDict)
997{
998 // Find the JSON object in the response for this sensor
999 nlohmann::json* sensorJson = findSensorJson(sensorsAsyncResp, sensorName);
1000 if (sensorJson != nullptr)
1001 {
1002 // Get Inventory.Item.Present property of inventory item
1003 auto itemIt = interfacesDict.find("xyz.openbmc_project.Inventory.Item");
1004 if (itemIt != interfacesDict.end())
1005 {
1006 auto presentIt = itemIt->second.find("Present");
1007 if (presentIt != itemIt->second.end())
1008 {
1009 const bool* present = std::get_if<bool>(&presentIt->second);
1010 if ((present != nullptr) && (*present == false))
1011 {
1012 // Inventory item is not present; update sensor State
1013 (*sensorJson)["Status"]["State"] = "Absent";
1014 }
1015 }
1016 }
1017
1018 // Get OperationalStatus.Functional property of inventory item
1019 auto opStatusIt = interfacesDict.find(
1020 "xyz.openbmc_project.State.Decorator.OperationalStatus");
1021 if (opStatusIt != interfacesDict.end())
1022 {
1023 auto functionalIt = opStatusIt->second.find("Functional");
1024 if (functionalIt != opStatusIt->second.end())
1025 {
1026 const bool* functional =
1027 std::get_if<bool>(&functionalIt->second);
1028 if ((functional != nullptr) && (*functional == false))
1029 {
1030 // Inventory item is not functional; update sensor Health
1031 (*sensorJson)["Status"]["Health"] = "Critical";
1032 }
1033 }
1034 }
1035 }
1036}
1037
1038/**
1039 * @brief Gets status of inventory items associated with sensors.
1040 *
1041 * Gets the D-Bus status properties for the inventory items associated with
1042 * sensors.
1043 *
1044 * Updates the Redfish sensors status in the JSON response, if needed, based on
1045 * the inventory items status.
1046 *
1047 * @param sensorsAsyncResp Pointer to object holding response data.
1048 * @param sensorToInvMap Mappings from sensor object path to the associated
1049 * inventory object path.
1050 * @param invConnections Connections that provide the status
1051 * interfaces/properties for the inventory items.
1052 * @param objectMgrPaths Mappings from connection name to DBus object path that
1053 * implements ObjectManager.
1054 */
1055static void getInventoryItemsStatus(
1056 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1057 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1058 sensorToInvMap,
1059 std::shared_ptr<boost::container::flat_set<std::string>> invConnections,
1060 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1061 objectMgrPaths)
1062{
1063 BMCWEB_LOG_DEBUG << "getInventoryItemsStatus enter";
1064
1065 // Loop through all connections providing inventory item status
1066 for (const std::string& invConnection : *invConnections)
1067 {
1068 // Response handler for GetManagedObjects
1069 auto respHandler = [sensorsAsyncResp,
1070 sensorToInvMap](const boost::system::error_code ec,
1071 ManagedObjectsVectorType& resp) {
1072 BMCWEB_LOG_DEBUG << "getInventoryItemsStatus respHandler enter";
1073 if (ec)
1074 {
1075 BMCWEB_LOG_ERROR
1076 << "getInventoryItemsStatus respHandler DBus error " << ec;
1077 messages::internalError(sensorsAsyncResp->res);
1078 return;
1079 }
1080
1081 // Loop through returned object paths
1082 for (const auto& objDictEntry : resp)
1083 {
1084 const std::string& objPath =
1085 static_cast<const std::string&>(objDictEntry.first);
1086
1087 // Find all sensors associated with this inventory item
1088 for (const std::pair<std::string, std::string>& pair :
1089 *sensorToInvMap)
1090 {
1091 if (pair.second == objPath)
1092 {
1093 // Update sensor status based on inventory item status
1094 updateSensorStatus(sensorsAsyncResp, pair.first,
1095 objDictEntry.second);
1096 }
1097 }
1098 }
1099
1100 BMCWEB_LOG_DEBUG << "getInventoryItemsStatus respHandler exit";
1101 };
1102
1103 // Find DBus object path that implements ObjectManager for the current
1104 // connection. If no mapping found, default to "/".
1105 auto iter = objectMgrPaths->find(invConnection);
1106 const std::string& objectMgrPath =
1107 (iter != objectMgrPaths->end()) ? iter->second : "/";
1108 BMCWEB_LOG_DEBUG << "ObjectManager path for " << invConnection << " is "
1109 << objectMgrPath;
1110
1111 // Get all object paths and their interfaces for current connection
1112 crow::connections::systemBus->async_method_call(
1113 std::move(respHandler), invConnection, objectMgrPath,
1114 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1115 }
1116
1117 BMCWEB_LOG_DEBUG << "getInventoryItemsStatus exit";
1118}
1119
1120/**
1121 * @brief Gets connections that provide status information on inventory items.
1122 *
1123 * Gets the D-Bus connections (services) that provide the interfaces and
1124 * properties containing status information for the inventory items.
1125 *
1126 * Finds the connections asynchronously. Invokes callback when information has
1127 * been obtained.
1128 *
1129 * The callback must have the following signature:
1130 * @code
1131 * callback(std::shared_ptr<boost::container::flat_set<std::string>>
1132 * invConnections)
1133 * @endcode
1134 *
1135 * @param sensorsAsyncResp Pointer to object holding response data.
1136 * @param sensorToInvMap Mappings from sensor object path to the associated
1137 * inventory object path.
1138 * @param callback Callback to invoke when connections have been obtained.
1139 */
1140template <typename Callback>
1141static void getInventoryItemsConnections(
1142 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1143 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1144 sensorToInvMap,
1145 Callback&& callback)
1146{
1147 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections enter";
1148
1149 const std::string path = "/xyz/openbmc_project/inventory";
1150 const std::array<std::string, 2> interfaces = {
1151 "xyz.openbmc_project.Inventory.Item",
1152 "xyz.openbmc_project.State.Decorator.OperationalStatus"};
1153
1154 // Response handler for parsing output from GetSubTree
1155 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
1156 sensorToInvMap](const boost::system::error_code ec,
1157 const GetSubTreeType& subtree) {
1158 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections respHandler enter";
1159 if (ec)
1160 {
1161 messages::internalError(sensorsAsyncResp->res);
1162 BMCWEB_LOG_ERROR
1163 << "getInventoryItemsConnections respHandler DBus error " << ec;
1164 return;
1165 }
1166
1167 // Make unique list of connections for desired inventory items
1168 std::shared_ptr<boost::container::flat_set<std::string>>
1169 invConnections =
1170 std::make_shared<boost::container::flat_set<std::string>>();
1171 invConnections->reserve(8);
1172
1173 // Loop through objects from GetSubTree
1174 for (const std::pair<
1175 std::string,
1176 std::vector<std::pair<std::string, std::vector<std::string>>>>&
1177 object : subtree)
1178 {
1179 // Look for inventory item object path in the sensor->inventory map
1180 const std::string& objPath = object.first;
1181 for (const std::pair<std::string, std::string>& pair :
1182 *sensorToInvMap)
1183 {
1184 if (pair.second == objPath)
1185 {
1186 // Store all connections to inventory item
1187 for (const std::pair<std::string, std::vector<std::string>>&
1188 objData : object.second)
1189 {
1190 const std::string& invConnection = objData.first;
1191 invConnections->insert(invConnection);
1192 }
1193 break;
1194 }
1195 }
1196 }
1197 callback(invConnections);
1198 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections respHandler exit";
1199 };
1200
1201 // Make call to ObjectMapper to find all inventory items
1202 crow::connections::systemBus->async_method_call(
1203 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
1204 "/xyz/openbmc_project/object_mapper",
1205 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 0, interfaces);
1206 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections exit";
1207}
1208
1209/**
1210 * @brief Gets inventory items associated with the specified sensors.
1211 *
1212 * Looks for ObjectMapper associations from the specified sensors to related
1213 * inventory items. Builds map where key is sensor object path and value is
1214 * inventory item object path.
1215 *
1216 * Finds the inventory items asynchronously. Invokes callback when information
1217 * has been obtained.
1218 *
1219 * The callback must have the following signature:
1220 * @code
1221 * callback(std::shared_ptr<boost::container::flat_map<
1222 std::string, std::string>> sensorToInvMap)
1223 * @endcode
1224 *
1225 * @param sensorsAsyncResp Pointer to object holding response data.
1226 * @param sensorNames All sensors within the current chassis.
1227 * @param objectMgrPaths Mappings from connection name to DBus object path that
1228 * implements ObjectManager.
1229 * @param callback Callback to invoke when inventory items have been obtained.
1230 */
1231template <typename Callback>
1232static void getInventoryItems(
1233 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1234 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
1235 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1236 objectMgrPaths,
1237 Callback&& callback)
1238{
1239 BMCWEB_LOG_DEBUG << "getInventoryItems enter";
1240
1241 // Response handler for GetManagedObjects
1242 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp,
1243 sensorNames](const boost::system::error_code ec,
1244 dbus::utility::ManagedObjectType& resp) {
1245 BMCWEB_LOG_DEBUG << "getInventoryItems respHandler enter";
1246 if (ec)
1247 {
1248 BMCWEB_LOG_ERROR << "getInventoryItems respHandler DBus error "
1249 << ec;
1250 messages::internalError(sensorsAsyncResp->res);
1251 return;
1252 }
1253
1254 // Loop through returned object paths
1255 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1256 sensorToInvMap = std::make_shared<
1257 boost::container::flat_map<std::string, std::string>>();
1258 std::string sensorAssocPath;
1259 sensorAssocPath.reserve(128); // avoid memory allocations
1260 for (const auto& objDictEntry : resp)
1261 {
1262 const std::string& objPath =
1263 static_cast<const std::string&>(objDictEntry.first);
1264 const boost::container::flat_map<
1265 std::string, boost::container::flat_map<
1266 std::string, dbus::utility::DbusVariantType>>&
1267 interfacesDict = objDictEntry.second;
1268
1269 // If path is inventory association for one of the specified sensors
1270 for (const std::string& sensorName : *sensorNames)
1271 {
1272 sensorAssocPath = sensorName;
1273 sensorAssocPath += "/inventory";
1274 if (objPath == sensorAssocPath)
1275 {
1276 // Get Association interface for object path
1277 auto assocIt =
1278 interfacesDict.find("xyz.openbmc_project.Association");
1279 if (assocIt != interfacesDict.end())
1280 {
1281 // Get inventory item from end point
1282 auto endpointsIt = assocIt->second.find("endpoints");
1283 if (endpointsIt != assocIt->second.end())
1284 {
1285 const std::vector<std::string>* endpoints =
1286 std::get_if<std::vector<std::string>>(
1287 &endpointsIt->second);
1288 if ((endpoints != nullptr) && !endpoints->empty())
1289 {
1290 // Store sensor -> inventory item mapping
1291 const std::string& invItem = endpoints->front();
1292 (*sensorToInvMap)[sensorName] = invItem;
1293 }
1294 }
1295 }
1296 break;
1297 }
1298 }
1299 }
1300
1301 // Call callback if at least one inventory item was found
1302 if (!sensorToInvMap->empty())
1303 {
1304 callback(sensorToInvMap);
1305 }
1306 BMCWEB_LOG_DEBUG << "getInventoryItems respHandler exit";
1307 };
1308
1309 // Find DBus object path that implements ObjectManager for ObjectMapper
1310 std::string connection = "xyz.openbmc_project.ObjectMapper";
1311 auto iter = objectMgrPaths->find(connection);
1312 const std::string& objectMgrPath =
1313 (iter != objectMgrPaths->end()) ? iter->second : "/";
1314 BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
1315 << objectMgrPath;
1316
1317 // Call GetManagedObjects on the ObjectMapper to get all associations
1318 crow::connections::systemBus->async_method_call(
1319 std::move(respHandler), connection, objectMgrPath,
1320 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1321
1322 BMCWEB_LOG_DEBUG << "getInventoryItems exit";
1323}
1324
1325/**
1326 * @brief Checks the status of inventory items associated with sensors.
1327 *
1328 * Finds the inventory items that are associated with the specified sensors.
1329 * Gets the status of those inventory items.
1330 *
1331 * If the inventory items are not present or functional, the sensor status is
1332 * updated in the JSON response.
1333 *
1334 * In D-Bus, the hardware present and functional properties are typically on the
1335 * inventory item rather than the sensor.
1336 *
1337 * @param sensorsAsyncResp Pointer to object holding response data.
1338 * @param sensorNames All sensors within the current chassis.
1339 * @param objectMgrPaths Mappings from connection name to DBus object path that
1340 * implements ObjectManager.
1341 */
1342static void checkInventoryItemsStatus(
1343 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
1344 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
1345 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1346 objectMgrPaths)
1347{
1348 BMCWEB_LOG_DEBUG << "checkInventoryItemsStatus enter";
1349 auto getInventoryItemsCb =
1350 [sensorsAsyncResp,
1351 objectMgrPaths](std::shared_ptr<
1352 boost::container::flat_map<std::string, std::string>>
1353 sensorToInvMap) {
1354 BMCWEB_LOG_DEBUG << "getInventoryItemsCb enter";
1355 auto getInventoryItemsConnectionsCb =
1356 [sensorsAsyncResp, sensorToInvMap, objectMgrPaths](
1357 std::shared_ptr<boost::container::flat_set<std::string>>
1358 invConnections) {
1359 BMCWEB_LOG_DEBUG << "getInventoryItemsConnectionsCb enter";
1360
1361 // Get status of inventory items and update sensors
1362 getInventoryItemsStatus(sensorsAsyncResp, sensorToInvMap,
1363 invConnections, objectMgrPaths);
1364
1365 BMCWEB_LOG_DEBUG << "getInventoryItemsConnectionsCb exit";
1366 };
1367
1368 // Get connections that provide status of inventory items
1369 getInventoryItemsConnections(
1370 sensorsAsyncResp, sensorToInvMap,
1371 std::move(getInventoryItemsConnectionsCb));
1372 BMCWEB_LOG_DEBUG << "getInventoryItemsCb exit";
1373 };
1374
1375 // Get inventory items that are associated with specified sensors
1376 getInventoryItems(sensorsAsyncResp, sensorNames, objectMgrPaths,
1377 std::move(getInventoryItemsCb));
1378 BMCWEB_LOG_DEBUG << "checkInventoryItemsStatus exit";
1379}
1380
1381/**
Shawn McCarneyde629b62019-03-08 10:42:51 -06001382 * @brief Gets the values of the specified sensors.
1383 *
1384 * Stores the results as JSON in the SensorsAsyncResp.
1385 *
1386 * Gets the sensor values asynchronously. Stores the results later when the
1387 * information has been obtained.
1388 *
1389 * The sensorNames set contains all sensors for the current chassis.
1390 * SensorsAsyncResp contains the requested sensor types. Only sensors of a
1391 * requested type are included in the JSON output.
1392 *
1393 * To minimize the number of DBus calls, the DBus method
1394 * org.freedesktop.DBus.ObjectManager.GetManagedObjects() is used to get the
1395 * values of all sensors provided by a connection (service).
1396 *
1397 * The connections set contains all the connections that provide sensor values.
1398 *
1399 * The objectMgrPaths map contains mappings from a connection name to the
1400 * corresponding DBus object path that implements ObjectManager.
1401 *
1402 * @param SensorsAsyncResp Pointer to object holding response data.
1403 * @param sensorNames All sensors within the current chassis.
1404 * @param connections Connections that provide sensor values.
1405 * @param objectMgrPaths Mappings from connection name to DBus object path that
1406 * implements ObjectManager.
1407 */
1408void getSensorData(
1409 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001410 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
Shawn McCarneyde629b62019-03-08 10:42:51 -06001411 const boost::container::flat_set<std::string>& connections,
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001412 std::shared_ptr<boost::container::flat_map<std::string, std::string>>
1413 objectMgrPaths)
Shawn McCarneyde629b62019-03-08 10:42:51 -06001414{
1415 BMCWEB_LOG_DEBUG << "getSensorData enter";
1416 // Get managed objects from all services exposing sensors
1417 for (const std::string& connection : connections)
1418 {
1419 // Response handler to process managed objects
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001420 auto getManagedObjectsCb = [SensorsAsyncResp, sensorNames,
1421 objectMgrPaths](
Shawn McCarneyde629b62019-03-08 10:42:51 -06001422 const boost::system::error_code ec,
1423 ManagedObjectsVectorType& resp) {
1424 BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter";
1425 if (ec)
1426 {
1427 BMCWEB_LOG_ERROR << "getManagedObjectsCb DBUS error: " << ec;
1428 messages::internalError(SensorsAsyncResp->res);
1429 return;
1430 }
1431 // Go through all objects and update response with sensor data
1432 for (const auto& objDictEntry : resp)
1433 {
1434 const std::string& objPath =
1435 static_cast<const std::string&>(objDictEntry.first);
1436 BMCWEB_LOG_DEBUG << "getManagedObjectsCb parsing object "
1437 << objPath;
1438
Shawn McCarneyde629b62019-03-08 10:42:51 -06001439 std::vector<std::string> split;
1440 // Reserve space for
1441 // /xyz/openbmc_project/sensors/<name>/<subname>
1442 split.reserve(6);
1443 boost::algorithm::split(split, objPath, boost::is_any_of("/"));
1444 if (split.size() < 6)
1445 {
1446 BMCWEB_LOG_ERROR << "Got path that isn't long enough "
1447 << objPath;
1448 continue;
1449 }
1450 // These indexes aren't intuitive, as boost::split puts an empty
1451 // string at the beginning
1452 const std::string& sensorType = split[4];
1453 const std::string& sensorName = split[5];
1454 BMCWEB_LOG_DEBUG << "sensorName " << sensorName
1455 << " sensorType " << sensorType;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001456 if (sensorNames->find(objPath) == sensorNames->end())
Shawn McCarneyde629b62019-03-08 10:42:51 -06001457 {
1458 BMCWEB_LOG_ERROR << sensorName << " not in sensor list ";
1459 continue;
1460 }
1461
1462 const char* fieldName = nullptr;
1463 if (sensorType == "temperature")
1464 {
1465 fieldName = "Temperatures";
1466 }
1467 else if (sensorType == "fan" || sensorType == "fan_tach" ||
1468 sensorType == "fan_pwm")
1469 {
1470 fieldName = "Fans";
1471 }
1472 else if (sensorType == "voltage")
1473 {
1474 fieldName = "Voltages";
1475 }
1476 else if (sensorType == "current")
1477 {
1478 fieldName = "PowerSupplies";
1479 }
1480 else if (sensorType == "power")
1481 {
Eddie James028f7eb2019-05-17 21:24:36 +00001482 if (!sensorName.compare("total_power"))
1483 {
1484 fieldName = "PowerControl";
1485 }
1486 else
1487 {
1488 fieldName = "PowerSupplies";
1489 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06001490 }
1491 else
1492 {
1493 BMCWEB_LOG_ERROR << "Unsure how to handle sensorType "
1494 << sensorType;
1495 continue;
1496 }
1497
1498 nlohmann::json& tempArray =
1499 SensorsAsyncResp->res.jsonValue[fieldName];
1500
Gunnar Mills7ab06f42019-07-02 13:07:16 -05001501 if ((fieldName == "PowerSupplies" ||
1502 fieldName == "PowerControl") &&
1503 !tempArray.empty())
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001504 {
Gunnar Mills7ab06f42019-07-02 13:07:16 -05001505 // For power supplies and power control put multiple
1506 // "sensors" into a single power supply or power control
1507 // entry, so only create the first one
1508 }
1509 else if (fieldName == "PowerControl")
1510 {
1511 // Put multiple "sensors" into a single PowerControl.
1512 // Follows MemberId naming and naming in power.hpp.
1513 tempArray.push_back(
1514 {{"@odata.id", "/redfish/v1/Chassis/" +
1515 SensorsAsyncResp->chassisId + "/" +
1516 SensorsAsyncResp->chassisSubNode +
1517 "#/" + fieldName + "/0"}});
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001518 }
1519 else
1520 {
1521 tempArray.push_back(
1522 {{"@odata.id", "/redfish/v1/Chassis/" +
1523 SensorsAsyncResp->chassisId + "/" +
1524 SensorsAsyncResp->chassisSubNode +
1525 "#/" + fieldName + "/"}});
1526 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06001527 nlohmann::json& sensorJson = tempArray.back();
1528
1529 objectInterfacesToJson(sensorName, sensorType,
1530 objDictEntry.second, sensorJson);
1531 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001532 if (SensorsAsyncResp.use_count() == 1)
James Feist8bd25cc2019-03-15 15:14:00 -07001533 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001534 sortJSONResponse(SensorsAsyncResp);
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001535 checkInventoryItemsStatus(SensorsAsyncResp, sensorNames,
1536 objectMgrPaths);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001537 if (SensorsAsyncResp->chassisSubNode == "Thermal")
1538 {
1539 populateFanRedundancy(SensorsAsyncResp);
1540 }
James Feist8bd25cc2019-03-15 15:14:00 -07001541 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06001542 BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit";
1543 };
1544
1545 // Find DBus object path that implements ObjectManager for the current
1546 // connection. If no mapping found, default to "/".
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001547 auto iter = objectMgrPaths->find(connection);
Shawn McCarneyde629b62019-03-08 10:42:51 -06001548 const std::string& objectMgrPath =
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001549 (iter != objectMgrPaths->end()) ? iter->second : "/";
Shawn McCarneyde629b62019-03-08 10:42:51 -06001550 BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
1551 << objectMgrPath;
1552
1553 crow::connections::systemBus->async_method_call(
1554 getManagedObjectsCb, connection, objectMgrPath,
1555 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1556 };
1557 BMCWEB_LOG_DEBUG << "getSensorData exit";
1558}
1559
1560/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001561 * @brief Entry point for retrieving sensors data related to requested
1562 * chassis.
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001563 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001564 */
Ed Tanous1abe55e2018-09-05 08:30:59 -07001565void getChassisData(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
1566{
1567 BMCWEB_LOG_DEBUG << "getChassisData enter";
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001568 auto getChassisCb =
1569 [SensorsAsyncResp](
1570 std::shared_ptr<boost::container::flat_set<std::string>>
1571 sensorNames) {
1572 BMCWEB_LOG_DEBUG << "getChassisCb enter";
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001573 auto getConnectionCb = [SensorsAsyncResp, sensorNames](
1574 const boost::container::flat_set<
1575 std::string>& connections) {
1576 BMCWEB_LOG_DEBUG << "getConnectionCb enter";
1577 auto getObjectManagerPathsCb =
1578 [SensorsAsyncResp, sensorNames, connections](
1579 std::shared_ptr<boost::container::flat_map<std::string,
1580 std::string>>
1581 objectMgrPaths) {
1582 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb enter";
1583 // Get sensor data and store results in JSON
1584 // response
1585 getSensorData(SensorsAsyncResp, sensorNames,
1586 connections, objectMgrPaths);
1587 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb exit";
1588 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001589
Shawn McCarney8fb49dd2019-06-12 17:47:00 -05001590 // Get mapping from connection names to the DBus object
1591 // paths that implement the ObjectManager interface
1592 getObjectManagerPaths(SensorsAsyncResp,
1593 std::move(getObjectManagerPathsCb));
1594 BMCWEB_LOG_DEBUG << "getConnectionCb exit";
1595 };
Shawn McCarneyde629b62019-03-08 10:42:51 -06001596
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001597 // Get set of connections that provide sensor values
1598 getConnections(SensorsAsyncResp, sensorNames,
1599 std::move(getConnectionCb));
1600 BMCWEB_LOG_DEBUG << "getChassisCb exit";
1601 };
Jennifer Lee4f9a2132019-03-04 12:45:19 -08001602 SensorsAsyncResp->res.jsonValue["Redundancy"] = nlohmann::json::array();
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001603
Shawn McCarney26f03892019-05-03 13:20:24 -05001604 // Get set of sensors in chassis
Ed Tanous1abe55e2018-09-05 08:30:59 -07001605 getChassis(SensorsAsyncResp, std::move(getChassisCb));
1606 BMCWEB_LOG_DEBUG << "getChassisData exit";
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001607};
1608
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301609/**
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001610 * @brief Find the requested sensorName in the list of all sensors supplied by
1611 * the chassis node
1612 *
1613 * @param sensorName The sensor name supplied in the PATCH request
1614 * @param sensorsList The list of sensors managed by the chassis node
1615 * @param sensorsModified The list of sensors that were found as a result of
1616 * repeated calls to this function
1617 */
1618bool findSensorNameUsingSensorPath(
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05301619 std::string_view sensorName,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001620 boost::container::flat_set<std::string>& sensorsList,
1621 boost::container::flat_set<std::string>& sensorsModified)
1622{
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05301623 for (std::string_view chassisSensor : sensorsList)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001624 {
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05301625 std::size_t pos = chassisSensor.rfind("/");
1626 if (pos >= (chassisSensor.size() - 1))
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001627 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001628 continue;
1629 }
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05301630 std::string_view thisSensorName = chassisSensor.substr(pos + 1);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001631 if (thisSensorName == sensorName)
1632 {
1633 sensorsModified.emplace(chassisSensor);
1634 return true;
1635 }
1636 }
1637 return false;
1638}
1639
1640/**
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301641 * @brief Entry point for overriding sensor values of given sensor
1642 *
1643 * @param res response object
1644 * @param req request object
1645 * @param params parameter passed for CRUD
1646 * @param typeList TypeList of sensors for the resource queried
1647 * @param chassisSubNode Chassis Node for which the query has to happen
1648 */
1649void setSensorOverride(crow::Response& res, const crow::Request& req,
1650 const std::vector<std::string>& params,
Ed Tanous85e14242019-06-27 15:04:09 -07001651 const std::vector<const char*> typeList,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301652 const std::string& chassisSubNode)
1653{
1654
1655 // TODO: Need to figure out dynamic way to restrict patch (Set Sensor
1656 // override) based on another d-bus announcement to be more generic.
1657 if (params.size() != 1)
1658 {
1659 messages::internalError(res);
1660 res.end();
1661 return;
1662 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301663
1664 std::unordered_map<std::string, std::vector<nlohmann::json>> allCollections;
1665 std::optional<std::vector<nlohmann::json>> temperatureCollections;
1666 std::optional<std::vector<nlohmann::json>> fanCollections;
1667 std::vector<nlohmann::json> voltageCollections;
1668 BMCWEB_LOG_INFO << "setSensorOverride for subNode" << chassisSubNode
1669 << "\n";
1670
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301671 if (chassisSubNode == "Thermal")
1672 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301673 if (!json_util::readJson(req, res, "Temperatures",
1674 temperatureCollections, "Fans",
1675 fanCollections))
1676 {
1677 return;
1678 }
1679 if (!temperatureCollections && !fanCollections)
1680 {
1681 messages::resourceNotFound(res, "Thermal",
1682 "Temperatures / Voltages");
1683 res.end();
1684 return;
1685 }
1686 if (temperatureCollections)
1687 {
1688 allCollections.emplace("Temperatures",
1689 *std::move(temperatureCollections));
1690 }
1691 if (fanCollections)
1692 {
1693 allCollections.emplace("Fans", *std::move(fanCollections));
1694 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301695 }
1696 else if (chassisSubNode == "Power")
1697 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301698 if (!json_util::readJson(req, res, "Voltages", voltageCollections))
1699 {
1700 return;
1701 }
1702 allCollections.emplace("Voltages", std::move(voltageCollections));
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301703 }
1704 else
1705 {
1706 res.result(boost::beast::http::status::not_found);
1707 res.end();
1708 return;
1709 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301710
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301711 const char* propertyValueName;
1712 std::unordered_map<std::string, std::pair<double, std::string>> overrideMap;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301713 std::string memberId;
1714 double value;
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301715 for (auto& collectionItems : allCollections)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301716 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301717 if (collectionItems.first == "Temperatures")
1718 {
1719 propertyValueName = "ReadingCelsius";
1720 }
1721 else if (collectionItems.first == "Fans")
1722 {
1723 propertyValueName = "Reading";
1724 }
1725 else
1726 {
1727 propertyValueName = "ReadingVolts";
1728 }
1729 for (auto& item : collectionItems.second)
1730 {
1731 if (!json_util::readJson(item, res, "MemberId", memberId,
1732 propertyValueName, value))
1733 {
1734 return;
1735 }
1736 overrideMap.emplace(memberId,
1737 std::make_pair(value, collectionItems.first));
1738 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301739 }
1740 const std::string& chassisName = params[0];
1741 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
1742 res, chassisName, typeList, chassisSubNode);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001743 auto getChassisSensorListCb = [sensorAsyncResp,
1744 overrideMap](const std::shared_ptr<
1745 boost::container::flat_set<
1746 std::string>>
1747 sensorsList) {
1748 // Match sensor names in the PATCH request to those managed by the
1749 // chassis node
1750 const std::shared_ptr<boost::container::flat_set<std::string>>
1751 sensorNames =
1752 std::make_shared<boost::container::flat_set<std::string>>();
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301753 for (const auto& item : overrideMap)
1754 {
1755 const auto& sensor = item.first;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001756 if (!findSensorNameUsingSensorPath(sensor, *sensorsList,
1757 *sensorNames))
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301758 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301759 BMCWEB_LOG_INFO << "Unable to find memberId " << item.first;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301760 messages::resourceNotFound(sensorAsyncResp->res,
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301761 item.second.second, item.first);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301762 return;
1763 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301764 }
1765 // Get the connection to which the memberId belongs
1766 auto getObjectsWithConnectionCb =
1767 [sensorAsyncResp, overrideMap](
1768 const boost::container::flat_set<std::string>& connections,
1769 const std::set<std::pair<std::string, std::string>>&
1770 objectsWithConnection) {
1771 if (objectsWithConnection.size() != overrideMap.size())
1772 {
1773 BMCWEB_LOG_INFO
1774 << "Unable to find all objects with proper connection "
1775 << objectsWithConnection.size() << " requested "
1776 << overrideMap.size() << "\n";
1777 messages::resourceNotFound(
1778 sensorAsyncResp->res,
1779 sensorAsyncResp->chassisSubNode == "Thermal"
1780 ? "Temperatures"
1781 : "Voltages",
1782 "Count");
1783 return;
1784 }
1785 for (const auto& item : objectsWithConnection)
1786 {
1787
1788 auto lastPos = item.first.rfind('/');
1789 if (lastPos == std::string::npos)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301790 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301791 messages::internalError(sensorAsyncResp->res);
1792 return;
1793 }
1794 std::string sensorName = item.first.substr(lastPos + 1);
1795
1796 const auto& iterator = overrideMap.find(sensorName);
1797 if (iterator == overrideMap.end())
1798 {
1799 BMCWEB_LOG_INFO << "Unable to find sensor object"
1800 << item.first << "\n";
1801 messages::internalError(sensorAsyncResp->res);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301802 return;
1803 }
1804 crow::connections::systemBus->async_method_call(
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301805 [sensorAsyncResp](const boost::system::error_code ec) {
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301806 if (ec)
1807 {
1808 BMCWEB_LOG_DEBUG
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301809 << "setOverrideValueStatus DBUS error: "
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301810 << ec;
1811 messages::internalError(sensorAsyncResp->res);
1812 return;
1813 }
1814 },
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301815 item.second, item.first,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301816 "org.freedesktop.DBus.Properties", "Set",
1817 "xyz.openbmc_project.Sensor.Value", "Value",
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301818 sdbusplus::message::variant<double>(
1819 iterator->second.first));
1820 }
1821 };
1822 // Get object with connection for the given sensor name
1823 getObjectsWithConnection(sensorAsyncResp, sensorNames,
1824 std::move(getObjectsWithConnectionCb));
1825 };
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301826 // get full sensor list for the given chassisId and cross verify the sensor.
1827 getChassis(sensorAsyncResp, std::move(getChassisSensorListCb));
1828}
1829
Ed Tanous1abe55e2018-09-05 08:30:59 -07001830} // namespace redfish