blob: 2a8c8702b559b07aed4fe3580216d415d51fdb96 [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 Tanousb01bf292019-03-25 19:25:26 +000050 const std::initializer_list<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
289 // Get the list of sensors for this Chassis element
290 std::string sensorPath = *chassisPath + "/sensors";
291 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
346 * callback(const boost::container::flat_map<std::string,
347 * std::string>& objectMgrPaths)
348 * @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
375 boost::container::flat_map<std::string, std::string> objectMgrPaths;
376 for (const std::pair<
377 std::string,
378 std::vector<std::pair<std::string, std::vector<std::string>>>>&
379 object : subtree)
380 {
381 // Loop over connections for current object path
382 const std::string& objectPath = object.first;
383 for (const std::pair<std::string, std::vector<std::string>>&
384 objData : object.second)
385 {
386 // Add mapping from connection to object path
387 const std::string& connection = objData.first;
388 objectMgrPaths[connection] = objectPath;
389 BMCWEB_LOG_DEBUG << "Added mapping " << connection << " -> "
390 << objectPath;
391 }
392 }
393 callback(std::move(objectMgrPaths));
394 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler exit";
395 };
396
397 // Query mapper for all DBus object paths that implement ObjectManager
398 crow::connections::systemBus->async_method_call(
399 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
400 "/xyz/openbmc_project/object_mapper",
401 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", int32_t(0),
402 interfaces);
403 BMCWEB_LOG_DEBUG << "getObjectManagerPaths exit";
404}
405
406/**
James Feist34dd1792019-05-17 14:10:54 -0700407 * @brief Retrieves the health from a sensor .
408 * @param interfacesDict Map of all sensor interfaces
409 */
410
411static std::string getHealth(
412 const boost::container::flat_map<
413 std::string, boost::container::flat_map<std::string, SensorVariant>>&
414 interfacesDict)
415{
416 auto criticalThresholdIt =
417 interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Critical");
418 if (criticalThresholdIt != interfacesDict.end())
419 {
420 auto thresholdHighIt =
421 criticalThresholdIt->second.find("CriticalAlarmHigh");
422 auto thresholdLowIt =
423 criticalThresholdIt->second.find("CriticalAlarmLow");
424 if (thresholdHighIt != criticalThresholdIt->second.end())
425 {
426 const bool* asserted = std::get_if<bool>(&thresholdHighIt->second);
427 if (asserted == nullptr)
428 {
429 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
430 }
431 else if (*asserted)
432 {
433 return "Critical";
434 }
435 }
436 if (thresholdLowIt != criticalThresholdIt->second.end())
437 {
438 const bool* asserted = std::get_if<bool>(&thresholdLowIt->second);
439 if (asserted == nullptr)
440 {
441 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
442 }
443 else if (*asserted)
444 {
445 return "Critical";
446 }
447 }
448 }
449
450 auto warningThresholdIt =
451 interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Warning");
452 if (warningThresholdIt != interfacesDict.end())
453 {
454 auto thresholdHighIt =
455 warningThresholdIt->second.find("WarningAlarmHigh");
456 auto thresholdLowIt =
457 warningThresholdIt->second.find("WarningAlarmLow");
458 if (thresholdHighIt != warningThresholdIt->second.end())
459 {
460 const bool* asserted = std::get_if<bool>(&thresholdHighIt->second);
461 if (asserted == nullptr)
462 {
463 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
464 }
465 else if (*asserted)
466 {
467 return "Warning";
468 }
469 }
470 if (thresholdLowIt != warningThresholdIt->second.end())
471 {
472 const bool* asserted = std::get_if<bool>(&thresholdLowIt->second);
473 if (asserted == nullptr)
474 {
475 BMCWEB_LOG_ERROR << "Illegal sensor threshold";
476 }
477 else if (*asserted)
478 {
479 return "Warning";
480 }
481 }
482 }
483 return "OK";
484}
485
486/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100487 * @brief Builds a json sensor representation of a sensor.
488 * @param sensorName The name of the sensor to be built
Gunnar Mills274fad52018-06-13 15:45:36 -0500489 * @param sensorType The type (temperature, fan_tach, etc) of the sensor to
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100490 * build
491 * @param interfacesDict A dictionary of the interfaces and properties of said
492 * interfaces to be built from
493 * @param sensor_json The json object to fill
494 */
495void objectInterfacesToJson(
496 const std::string& sensorName, const std::string& sensorType,
497 const boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700498 std::string, boost::container::flat_map<std::string, SensorVariant>>&
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100499 interfacesDict,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700500 nlohmann::json& sensor_json)
501{
502 // We need a value interface before we can do anything with it
503 auto valueIt = interfacesDict.find("xyz.openbmc_project.Sensor.Value");
504 if (valueIt == interfacesDict.end())
505 {
506 BMCWEB_LOG_ERROR << "Sensor doesn't have a value interface";
507 return;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100508 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100509
Ed Tanous1abe55e2018-09-05 08:30:59 -0700510 // Assume values exist as is (10^0 == 1) if no scale exists
511 int64_t scaleMultiplier = 0;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100512
Ed Tanous1abe55e2018-09-05 08:30:59 -0700513 auto scaleIt = valueIt->second.find("Scale");
514 // If a scale exists, pull value as int64, and use the scaling.
515 if (scaleIt != valueIt->second.end())
516 {
Ed Tanousabf2add2019-01-22 16:40:12 -0800517 const int64_t* int64Value = std::get_if<int64_t>(&scaleIt->second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700518 if (int64Value != nullptr)
519 {
520 scaleMultiplier = *int64Value;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100521 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100522 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700523
524 sensor_json["MemberId"] = sensorName;
Ed Tanouse742b6c2019-05-03 15:06:53 -0700525 sensor_json["Name"] = boost::replace_all_copy(sensorName, "_", " ");
526
Ed Tanous1abe55e2018-09-05 08:30:59 -0700527 sensor_json["Status"]["State"] = "Enabled";
James Feist34dd1792019-05-17 14:10:54 -0700528 sensor_json["Status"]["Health"] = getHealth(interfacesDict);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700529
530 // Parameter to set to override the type we get from dbus, and force it to
531 // int, regardless of what is available. This is used for schemas like fan,
532 // that require integers, not floats.
533 bool forceToInt = false;
534
535 const char* unit = "Reading";
536 if (sensorType == "temperature")
537 {
538 unit = "ReadingCelsius";
539 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Temperature";
540 // TODO(ed) Documentation says that path should be type fan_tach,
541 // implementation seems to implement fan
542 }
543 else if (sensorType == "fan" || sensorType == "fan_tach")
544 {
545 unit = "Reading";
546 sensor_json["ReadingUnits"] = "RPM";
547 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
548 forceToInt = true;
549 }
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700550 else if (sensorType == "fan_pwm")
551 {
552 unit = "Reading";
553 sensor_json["ReadingUnits"] = "Percent";
554 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
555 forceToInt = true;
556 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700557 else if (sensorType == "voltage")
558 {
559 unit = "ReadingVolts";
560 sensor_json["@odata.type"] = "#Power.v1_0_0.Voltage";
561 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700562 else if (sensorType == "power")
563 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700564 std::string sensorNameLower =
565 boost::algorithm::to_lower_copy(sensorName);
566
Eddie James028f7eb2019-05-17 21:24:36 +0000567 if (!sensorName.compare("total_power"))
568 {
569 unit = "PowerConsumedWatts";
570 }
571 else if (sensorNameLower.find("input") != std::string::npos)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700572 {
573 unit = "PowerInputWatts";
574 }
575 else
576 {
577 unit = "PowerOutputWatts";
578 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700579 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700580 else
581 {
582 BMCWEB_LOG_ERROR << "Redfish cannot map object type for " << sensorName;
583 return;
584 }
585 // Map of dbus interface name, dbus property name and redfish property_name
586 std::vector<std::tuple<const char*, const char*, const char*>> properties;
587 properties.reserve(7);
588
589 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600590
591 // If sensor type doesn't map to Redfish PowerSupply, add threshold props
592 if ((sensorType != "current") && (sensorType != "power"))
593 {
594 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
595 "WarningHigh", "UpperThresholdNonCritical");
596 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
597 "WarningLow", "LowerThresholdNonCritical");
598 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
599 "CriticalHigh", "UpperThresholdCritical");
600 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
601 "CriticalLow", "LowerThresholdCritical");
602 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700603
Ed Tanous2474adf2018-09-05 16:31:16 -0700604 // TODO Need to get UpperThresholdFatal and LowerThresholdFatal
605
Ed Tanous1abe55e2018-09-05 08:30:59 -0700606 if (sensorType == "temperature")
607 {
608 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
609 "MinReadingRangeTemp");
610 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
611 "MaxReadingRangeTemp");
612 }
Shawn McCarneyde629b62019-03-08 10:42:51 -0600613 else if ((sensorType != "current") && (sensorType != "power"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700614 {
Shawn McCarneyde629b62019-03-08 10:42:51 -0600615 // Sensor type doesn't map to Redfish PowerSupply; add min/max props
Ed Tanous1abe55e2018-09-05 08:30:59 -0700616 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
617 "MinReadingRange");
618 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
619 "MaxReadingRange");
620 }
621
622 for (const std::tuple<const char*, const char*, const char*>& p :
623 properties)
624 {
625 auto interfaceProperties = interfacesDict.find(std::get<0>(p));
626 if (interfaceProperties != interfacesDict.end())
627 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000628 auto valueIt = interfaceProperties->second.find(std::get<1>(p));
629 if (valueIt != interfaceProperties->second.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700630 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000631 const SensorVariant& valueVariant = valueIt->second;
632 nlohmann::json& valueIt = sensor_json[std::get<2>(p)];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700633 // Attempt to pull the int64 directly
Ed Tanousabf2add2019-01-22 16:40:12 -0800634 const int64_t* int64Value = std::get_if<int64_t>(&valueVariant);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700635
Ed Tanousabf2add2019-01-22 16:40:12 -0800636 const double* doubleValue = std::get_if<double>(&valueVariant);
Eddie James028f7eb2019-05-17 21:24:36 +0000637 const uint32_t* uValue = std::get_if<uint32_t>(&valueVariant);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700638 double temp = 0.0;
639 if (int64Value != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700640 {
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700641 temp = *int64Value;
642 }
643 else if (doubleValue != nullptr)
644 {
645 temp = *doubleValue;
646 }
Eddie James028f7eb2019-05-17 21:24:36 +0000647 else if (uValue != nullptr)
648 {
649 temp = *uValue;
650 }
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700651 else
652 {
653 BMCWEB_LOG_ERROR
654 << "Got value interface that wasn't int or double";
655 continue;
656 }
657 temp = temp * std::pow(10, scaleMultiplier);
658 if (forceToInt)
659 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000660 valueIt = static_cast<int64_t>(temp);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700661 }
662 else
663 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000664 valueIt = temp;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700665 }
666 }
667 }
668 }
669 BMCWEB_LOG_DEBUG << "Added sensor " << sensorName;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100670}
671
James Feist8bd25cc2019-03-15 15:14:00 -0700672static void
673 populateFanRedundancy(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp)
674{
675 crow::connections::systemBus->async_method_call(
676 [sensorsAsyncResp](const boost::system::error_code ec,
677 const GetSubTreeType& resp) {
678 if (ec)
679 {
680 return; // don't have to have this interface
681 }
Ed Tanouse278c182019-03-13 16:23:37 -0700682 for (const std::pair<std::string,
683 std::vector<std::pair<
684 std::string, std::vector<std::string>>>>&
685 pathPair : resp)
James Feist8bd25cc2019-03-15 15:14:00 -0700686 {
Ed Tanouse278c182019-03-13 16:23:37 -0700687 const std::string& path = pathPair.first;
688 const std::vector<
689 std::pair<std::string, std::vector<std::string>>>& objDict =
690 pathPair.second;
James Feist8bd25cc2019-03-15 15:14:00 -0700691 if (objDict.empty())
692 {
693 continue; // this should be impossible
694 }
695
696 const std::string& owner = objDict.begin()->first;
697 crow::connections::systemBus->async_method_call(
698 [path, owner,
699 sensorsAsyncResp](const boost::system::error_code ec,
700 std::variant<std::vector<std::string>>
701 variantEndpoints) {
702 if (ec)
703 {
704 return; // if they don't have an association we
705 // can't tell what chassis is
706 }
707 // verify part of the right chassis
708 auto endpoints = std::get_if<std::vector<std::string>>(
709 &variantEndpoints);
710
711 if (endpoints == nullptr)
712 {
713 BMCWEB_LOG_ERROR << "Invalid association interface";
714 messages::internalError(sensorsAsyncResp->res);
715 return;
716 }
717
718 auto found = std::find_if(
719 endpoints->begin(), endpoints->end(),
720 [sensorsAsyncResp](const std::string& entry) {
721 return entry.find(
722 sensorsAsyncResp->chassisId) !=
723 std::string::npos;
724 });
725
726 if (found == endpoints->end())
727 {
728 return;
729 }
730 crow::connections::systemBus->async_method_call(
731 [path, sensorsAsyncResp](
732 const boost::system::error_code ec,
733 const boost::container::flat_map<
734 std::string,
735 std::variant<uint8_t,
736 std::vector<std::string>,
737 std::string>>& ret) {
738 if (ec)
739 {
740 return; // don't have to have this
741 // interface
742 }
743 auto findFailures = ret.find("AllowedFailures");
744 auto findCollection = ret.find("Collection");
745 auto findStatus = ret.find("Status");
746
747 if (findFailures == ret.end() ||
748 findCollection == ret.end() ||
749 findStatus == ret.end())
750 {
751 BMCWEB_LOG_ERROR
752 << "Invalid redundancy interface";
753 messages::internalError(
754 sensorsAsyncResp->res);
755 return;
756 }
757
758 auto allowedFailures = std::get_if<uint8_t>(
759 &(findFailures->second));
760 auto collection =
761 std::get_if<std::vector<std::string>>(
762 &(findCollection->second));
763 auto status = std::get_if<std::string>(
764 &(findStatus->second));
765
766 if (allowedFailures == nullptr ||
767 collection == nullptr || status == nullptr)
768 {
769
770 BMCWEB_LOG_ERROR
771 << "Invalid redundancy interface "
772 "types";
773 messages::internalError(
774 sensorsAsyncResp->res);
775 return;
776 }
777 size_t lastSlash = path.rfind("/");
778 if (lastSlash == std::string::npos)
779 {
780 // this should be impossible
781 messages::internalError(
782 sensorsAsyncResp->res);
783 return;
784 }
785 std::string name = path.substr(lastSlash + 1);
786 std::replace(name.begin(), name.end(), '_',
787 ' ');
788
789 std::string health;
790
791 if (boost::ends_with(*status, "Full"))
792 {
793 health = "OK";
794 }
795 else if (boost::ends_with(*status, "Degraded"))
796 {
797 health = "Warning";
798 }
799 else
800 {
801 health = "Critical";
802 }
803 std::vector<nlohmann::json> redfishCollection;
804 const auto& fanRedfish =
805 sensorsAsyncResp->res.jsonValue["Fans"];
806 for (const std::string& item : *collection)
807 {
808 lastSlash = item.rfind("/");
809 // make a copy as collection is const
810 std::string itemName =
811 item.substr(lastSlash + 1);
812 /*
813 todo(ed): merge patch that fixes the names
814 std::replace(itemName.begin(),
815 itemName.end(), '_', ' ');*/
816 auto schemaItem = std::find_if(
817 fanRedfish.begin(), fanRedfish.end(),
818 [itemName](const nlohmann::json& fan) {
819 return fan["MemberId"] == itemName;
820 });
821 if (schemaItem != fanRedfish.end())
822 {
823 redfishCollection.push_back(
824 {{"@odata.id",
825 (*schemaItem)["@odata.id"]}});
826 }
827 else
828 {
829 BMCWEB_LOG_ERROR
830 << "failed to find fan in schema";
831 messages::internalError(
832 sensorsAsyncResp->res);
833 return;
834 }
835 }
836
837 auto& resp = sensorsAsyncResp->res
838 .jsonValue["Redundancy"];
839 resp.push_back(
840 {{"@odata.id",
841 "/refish/v1/Chassis/" +
842 sensorsAsyncResp->chassisId + "/" +
843 sensorsAsyncResp->chassisSubNode +
844 "#/Redundancy/" +
845 std::to_string(resp.size())},
846 {"@odata.type",
847 "#Redundancy.v1_3_2.Redundancy"},
848 {"MinNumNeeded",
849 collection->size() - *allowedFailures},
850 {"MemberId", name},
851 {"Mode", "N+m"},
852 {"Name", name},
853 {"RedundancySet", redfishCollection},
854 {"Status",
855 {{"Health", health},
856 {"State", "Enabled"}}}});
857 },
858 owner, path, "org.freedesktop.DBus.Properties",
859 "GetAll",
860 "xyz.openbmc_project.Control.FanRedundancy");
861 },
862 "xyz.openbmc_project.ObjectMapper", path + "/inventory",
863 "org.freedesktop.DBus.Properties", "Get",
864 "xyz.openbmc_project.Association", "endpoints");
865 }
866 },
867 "xyz.openbmc_project.ObjectMapper",
868 "/xyz/openbmc_project/object_mapper",
869 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
870 "/xyz/openbmc_project/control", 2,
871 std::array<const char*, 1>{
872 "xyz.openbmc_project.Control.FanRedundancy"});
873}
874
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700875void sortJSONResponse(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
876{
877 nlohmann::json& response = SensorsAsyncResp->res.jsonValue;
878 std::array<std::string, 2> sensorHeaders{"Temperatures", "Fans"};
879 if (SensorsAsyncResp->chassisSubNode == "Power")
880 {
881 sensorHeaders = {"Voltages", "PowerSupplies"};
882 }
883 for (const std::string& sensorGroup : sensorHeaders)
884 {
885 nlohmann::json::iterator entry = response.find(sensorGroup);
886 if (entry != response.end())
887 {
888 std::sort(entry->begin(), entry->end(),
889 [](nlohmann::json& c1, nlohmann::json& c2) {
890 return c1["Name"] < c2["Name"];
891 });
892
893 // add the index counts to the end of each entry
894 size_t count = 0;
895 for (nlohmann::json& sensorJson : *entry)
896 {
897 nlohmann::json::iterator odata = sensorJson.find("@odata.id");
898 if (odata == sensorJson.end())
899 {
900 continue;
901 }
902 std::string* value = odata->get_ptr<std::string*>();
903 if (value != nullptr)
904 {
905 *value += std::to_string(count);
906 count++;
907 }
908 }
909 }
910 }
911}
912
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100913/**
Shawn McCarneyde629b62019-03-08 10:42:51 -0600914 * @brief Gets the values of the specified sensors.
915 *
916 * Stores the results as JSON in the SensorsAsyncResp.
917 *
918 * Gets the sensor values asynchronously. Stores the results later when the
919 * information has been obtained.
920 *
921 * The sensorNames set contains all sensors for the current chassis.
922 * SensorsAsyncResp contains the requested sensor types. Only sensors of a
923 * requested type are included in the JSON output.
924 *
925 * To minimize the number of DBus calls, the DBus method
926 * org.freedesktop.DBus.ObjectManager.GetManagedObjects() is used to get the
927 * values of all sensors provided by a connection (service).
928 *
929 * The connections set contains all the connections that provide sensor values.
930 *
931 * The objectMgrPaths map contains mappings from a connection name to the
932 * corresponding DBus object path that implements ObjectManager.
933 *
934 * @param SensorsAsyncResp Pointer to object holding response data.
935 * @param sensorNames All sensors within the current chassis.
936 * @param connections Connections that provide sensor values.
937 * @param objectMgrPaths Mappings from connection name to DBus object path that
938 * implements ObjectManager.
939 */
940void getSensorData(
941 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700942 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
Shawn McCarneyde629b62019-03-08 10:42:51 -0600943 const boost::container::flat_set<std::string>& connections,
944 const boost::container::flat_map<std::string, std::string>& objectMgrPaths)
945{
946 BMCWEB_LOG_DEBUG << "getSensorData enter";
947 // Get managed objects from all services exposing sensors
948 for (const std::string& connection : connections)
949 {
950 // Response handler to process managed objects
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700951 auto getManagedObjectsCb = [SensorsAsyncResp, sensorNames](
Shawn McCarneyde629b62019-03-08 10:42:51 -0600952 const boost::system::error_code ec,
953 ManagedObjectsVectorType& resp) {
954 BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter";
955 if (ec)
956 {
957 BMCWEB_LOG_ERROR << "getManagedObjectsCb DBUS error: " << ec;
958 messages::internalError(SensorsAsyncResp->res);
959 return;
960 }
961 // Go through all objects and update response with sensor data
962 for (const auto& objDictEntry : resp)
963 {
964 const std::string& objPath =
965 static_cast<const std::string&>(objDictEntry.first);
966 BMCWEB_LOG_DEBUG << "getManagedObjectsCb parsing object "
967 << objPath;
968
Shawn McCarneyde629b62019-03-08 10:42:51 -0600969 std::vector<std::string> split;
970 // Reserve space for
971 // /xyz/openbmc_project/sensors/<name>/<subname>
972 split.reserve(6);
973 boost::algorithm::split(split, objPath, boost::is_any_of("/"));
974 if (split.size() < 6)
975 {
976 BMCWEB_LOG_ERROR << "Got path that isn't long enough "
977 << objPath;
978 continue;
979 }
980 // These indexes aren't intuitive, as boost::split puts an empty
981 // string at the beginning
982 const std::string& sensorType = split[4];
983 const std::string& sensorName = split[5];
984 BMCWEB_LOG_DEBUG << "sensorName " << sensorName
985 << " sensorType " << sensorType;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700986 if (sensorNames->find(objPath) == sensorNames->end())
Shawn McCarneyde629b62019-03-08 10:42:51 -0600987 {
988 BMCWEB_LOG_ERROR << sensorName << " not in sensor list ";
989 continue;
990 }
991
992 const char* fieldName = nullptr;
993 if (sensorType == "temperature")
994 {
995 fieldName = "Temperatures";
996 }
997 else if (sensorType == "fan" || sensorType == "fan_tach" ||
998 sensorType == "fan_pwm")
999 {
1000 fieldName = "Fans";
1001 }
1002 else if (sensorType == "voltage")
1003 {
1004 fieldName = "Voltages";
1005 }
1006 else if (sensorType == "current")
1007 {
1008 fieldName = "PowerSupplies";
1009 }
1010 else if (sensorType == "power")
1011 {
Eddie James028f7eb2019-05-17 21:24:36 +00001012 if (!sensorName.compare("total_power"))
1013 {
1014 fieldName = "PowerControl";
1015 }
1016 else
1017 {
1018 fieldName = "PowerSupplies";
1019 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06001020 }
1021 else
1022 {
1023 BMCWEB_LOG_ERROR << "Unsure how to handle sensorType "
1024 << sensorType;
1025 continue;
1026 }
1027
1028 nlohmann::json& tempArray =
1029 SensorsAsyncResp->res.jsonValue[fieldName];
1030
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001031 if (fieldName == "PowerSupplies" && !tempArray.empty())
1032 {
1033 // Power supplies put multiple "sensors" into a single power
1034 // supply entry, so only create the first one
1035 }
1036 else
1037 {
1038 tempArray.push_back(
1039 {{"@odata.id", "/redfish/v1/Chassis/" +
1040 SensorsAsyncResp->chassisId + "/" +
1041 SensorsAsyncResp->chassisSubNode +
1042 "#/" + fieldName + "/"}});
1043 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06001044 nlohmann::json& sensorJson = tempArray.back();
1045
1046 objectInterfacesToJson(sensorName, sensorType,
1047 objDictEntry.second, sensorJson);
1048 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001049 if (SensorsAsyncResp.use_count() == 1)
James Feist8bd25cc2019-03-15 15:14:00 -07001050 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001051 sortJSONResponse(SensorsAsyncResp);
1052 if (SensorsAsyncResp->chassisSubNode == "Thermal")
1053 {
1054 populateFanRedundancy(SensorsAsyncResp);
1055 }
James Feist8bd25cc2019-03-15 15:14:00 -07001056 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06001057 BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit";
1058 };
1059
1060 // Find DBus object path that implements ObjectManager for the current
1061 // connection. If no mapping found, default to "/".
1062 auto iter = objectMgrPaths.find(connection);
1063 const std::string& objectMgrPath =
1064 (iter != objectMgrPaths.end()) ? iter->second : "/";
1065 BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
1066 << objectMgrPath;
1067
1068 crow::connections::systemBus->async_method_call(
1069 getManagedObjectsCb, connection, objectMgrPath,
1070 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1071 };
1072 BMCWEB_LOG_DEBUG << "getSensorData exit";
1073}
1074
1075/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001076 * @brief Entry point for retrieving sensors data related to requested
1077 * chassis.
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001078 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001079 */
Ed Tanous1abe55e2018-09-05 08:30:59 -07001080void getChassisData(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
1081{
1082 BMCWEB_LOG_DEBUG << "getChassisData enter";
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001083 auto getChassisCb =
1084 [SensorsAsyncResp](
1085 std::shared_ptr<boost::container::flat_set<std::string>>
1086 sensorNames) {
1087 BMCWEB_LOG_DEBUG << "getChassisCb enter";
1088 auto getConnectionCb =
1089 [SensorsAsyncResp,
1090 sensorNames](const boost::container::flat_set<std::string>&
1091 connections) {
1092 BMCWEB_LOG_DEBUG << "getConnectionCb enter";
1093 auto getObjectManagerPathsCb =
1094 [SensorsAsyncResp, sensorNames, connections](
1095 const boost::container::flat_map<
1096 std::string, std::string>& objectMgrPaths) {
1097 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb enter";
1098 // Get sensor data and store results in JSON
1099 // response
1100 getSensorData(SensorsAsyncResp, sensorNames,
1101 connections, objectMgrPaths);
1102 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb exit";
1103 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001104
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001105 // Get mapping from connection names to the DBus object
1106 // paths that implement the ObjectManager interface
1107 getObjectManagerPaths(SensorsAsyncResp,
1108 std::move(getObjectManagerPathsCb));
1109 BMCWEB_LOG_DEBUG << "getConnectionCb exit";
1110 };
Shawn McCarneyde629b62019-03-08 10:42:51 -06001111
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001112 // Get set of connections that provide sensor values
1113 getConnections(SensorsAsyncResp, sensorNames,
1114 std::move(getConnectionCb));
1115 BMCWEB_LOG_DEBUG << "getChassisCb exit";
1116 };
Jennifer Lee4f9a2132019-03-04 12:45:19 -08001117 SensorsAsyncResp->res.jsonValue["Redundancy"] = nlohmann::json::array();
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001118
Shawn McCarney26f03892019-05-03 13:20:24 -05001119 // Get set of sensors in chassis
Ed Tanous1abe55e2018-09-05 08:30:59 -07001120 getChassis(SensorsAsyncResp, std::move(getChassisCb));
1121 BMCWEB_LOG_DEBUG << "getChassisData exit";
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001122};
1123
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301124/**
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001125 * @brief Find the requested sensorName in the list of all sensors supplied by
1126 * the chassis node
1127 *
1128 * @param sensorName The sensor name supplied in the PATCH request
1129 * @param sensorsList The list of sensors managed by the chassis node
1130 * @param sensorsModified The list of sensors that were found as a result of
1131 * repeated calls to this function
1132 */
1133bool findSensorNameUsingSensorPath(
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05301134 std::string_view sensorName,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001135 boost::container::flat_set<std::string>& sensorsList,
1136 boost::container::flat_set<std::string>& sensorsModified)
1137{
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05301138 for (std::string_view chassisSensor : sensorsList)
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001139 {
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05301140 std::size_t pos = chassisSensor.rfind("/");
1141 if (pos >= (chassisSensor.size() - 1))
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001142 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001143 continue;
1144 }
Richard Marian Thomaiyar0a86feb2019-05-27 23:16:40 +05301145 std::string_view thisSensorName = chassisSensor.substr(pos + 1);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001146 if (thisSensorName == sensorName)
1147 {
1148 sensorsModified.emplace(chassisSensor);
1149 return true;
1150 }
1151 }
1152 return false;
1153}
1154
1155/**
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301156 * @brief Entry point for overriding sensor values of given sensor
1157 *
1158 * @param res response object
1159 * @param req request object
1160 * @param params parameter passed for CRUD
1161 * @param typeList TypeList of sensors for the resource queried
1162 * @param chassisSubNode Chassis Node for which the query has to happen
1163 */
1164void setSensorOverride(crow::Response& res, const crow::Request& req,
1165 const std::vector<std::string>& params,
Ed Tanousb01bf292019-03-25 19:25:26 +00001166 const std::initializer_list<const char*> typeList,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301167 const std::string& chassisSubNode)
1168{
1169
1170 // TODO: Need to figure out dynamic way to restrict patch (Set Sensor
1171 // override) based on another d-bus announcement to be more generic.
1172 if (params.size() != 1)
1173 {
1174 messages::internalError(res);
1175 res.end();
1176 return;
1177 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301178
1179 std::unordered_map<std::string, std::vector<nlohmann::json>> allCollections;
1180 std::optional<std::vector<nlohmann::json>> temperatureCollections;
1181 std::optional<std::vector<nlohmann::json>> fanCollections;
1182 std::vector<nlohmann::json> voltageCollections;
1183 BMCWEB_LOG_INFO << "setSensorOverride for subNode" << chassisSubNode
1184 << "\n";
1185
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301186 if (chassisSubNode == "Thermal")
1187 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301188 if (!json_util::readJson(req, res, "Temperatures",
1189 temperatureCollections, "Fans",
1190 fanCollections))
1191 {
1192 return;
1193 }
1194 if (!temperatureCollections && !fanCollections)
1195 {
1196 messages::resourceNotFound(res, "Thermal",
1197 "Temperatures / Voltages");
1198 res.end();
1199 return;
1200 }
1201 if (temperatureCollections)
1202 {
1203 allCollections.emplace("Temperatures",
1204 *std::move(temperatureCollections));
1205 }
1206 if (fanCollections)
1207 {
1208 allCollections.emplace("Fans", *std::move(fanCollections));
1209 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301210 }
1211 else if (chassisSubNode == "Power")
1212 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301213 if (!json_util::readJson(req, res, "Voltages", voltageCollections))
1214 {
1215 return;
1216 }
1217 allCollections.emplace("Voltages", std::move(voltageCollections));
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301218 }
1219 else
1220 {
1221 res.result(boost::beast::http::status::not_found);
1222 res.end();
1223 return;
1224 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301225
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301226 const char* propertyValueName;
1227 std::unordered_map<std::string, std::pair<double, std::string>> overrideMap;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301228 std::string memberId;
1229 double value;
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301230 for (auto& collectionItems : allCollections)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301231 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301232 if (collectionItems.first == "Temperatures")
1233 {
1234 propertyValueName = "ReadingCelsius";
1235 }
1236 else if (collectionItems.first == "Fans")
1237 {
1238 propertyValueName = "Reading";
1239 }
1240 else
1241 {
1242 propertyValueName = "ReadingVolts";
1243 }
1244 for (auto& item : collectionItems.second)
1245 {
1246 if (!json_util::readJson(item, res, "MemberId", memberId,
1247 propertyValueName, value))
1248 {
1249 return;
1250 }
1251 overrideMap.emplace(memberId,
1252 std::make_pair(value, collectionItems.first));
1253 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301254 }
1255 const std::string& chassisName = params[0];
1256 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
1257 res, chassisName, typeList, chassisSubNode);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001258 auto getChassisSensorListCb = [sensorAsyncResp,
1259 overrideMap](const std::shared_ptr<
1260 boost::container::flat_set<
1261 std::string>>
1262 sensorsList) {
1263 // Match sensor names in the PATCH request to those managed by the
1264 // chassis node
1265 const std::shared_ptr<boost::container::flat_set<std::string>>
1266 sensorNames =
1267 std::make_shared<boost::container::flat_set<std::string>>();
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301268 for (const auto& item : overrideMap)
1269 {
1270 const auto& sensor = item.first;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001271 if (!findSensorNameUsingSensorPath(sensor, *sensorsList,
1272 *sensorNames))
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301273 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301274 BMCWEB_LOG_INFO << "Unable to find memberId " << item.first;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301275 messages::resourceNotFound(sensorAsyncResp->res,
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301276 item.second.second, item.first);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301277 return;
1278 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301279 }
1280 // Get the connection to which the memberId belongs
1281 auto getObjectsWithConnectionCb =
1282 [sensorAsyncResp, overrideMap](
1283 const boost::container::flat_set<std::string>& connections,
1284 const std::set<std::pair<std::string, std::string>>&
1285 objectsWithConnection) {
1286 if (objectsWithConnection.size() != overrideMap.size())
1287 {
1288 BMCWEB_LOG_INFO
1289 << "Unable to find all objects with proper connection "
1290 << objectsWithConnection.size() << " requested "
1291 << overrideMap.size() << "\n";
1292 messages::resourceNotFound(
1293 sensorAsyncResp->res,
1294 sensorAsyncResp->chassisSubNode == "Thermal"
1295 ? "Temperatures"
1296 : "Voltages",
1297 "Count");
1298 return;
1299 }
1300 for (const auto& item : objectsWithConnection)
1301 {
1302
1303 auto lastPos = item.first.rfind('/');
1304 if (lastPos == std::string::npos)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301305 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301306 messages::internalError(sensorAsyncResp->res);
1307 return;
1308 }
1309 std::string sensorName = item.first.substr(lastPos + 1);
1310
1311 const auto& iterator = overrideMap.find(sensorName);
1312 if (iterator == overrideMap.end())
1313 {
1314 BMCWEB_LOG_INFO << "Unable to find sensor object"
1315 << item.first << "\n";
1316 messages::internalError(sensorAsyncResp->res);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301317 return;
1318 }
1319 crow::connections::systemBus->async_method_call(
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301320 [sensorAsyncResp](const boost::system::error_code ec) {
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301321 if (ec)
1322 {
1323 BMCWEB_LOG_DEBUG
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301324 << "setOverrideValueStatus DBUS error: "
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301325 << ec;
1326 messages::internalError(sensorAsyncResp->res);
1327 return;
1328 }
1329 },
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301330 item.second, item.first,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301331 "org.freedesktop.DBus.Properties", "Set",
1332 "xyz.openbmc_project.Sensor.Value", "Value",
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301333 sdbusplus::message::variant<double>(
1334 iterator->second.first));
1335 }
1336 };
1337 // Get object with connection for the given sensor name
1338 getObjectsWithConnection(sensorAsyncResp, sensorNames,
1339 std::move(getObjectsWithConnectionCb));
1340 };
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301341 // get full sensor list for the given chassisId and cross verify the sensor.
1342 getChassis(sensorAsyncResp, std::move(getChassisSensorListCb));
1343}
1344
Ed Tanous1abe55e2018-09-05 08:30:59 -07001345} // namespace redfish