blob: 07361490881df9a3059fd7e538f300a08883fb9c [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
Ed Tanousabf2add2019-01-22 16:40:12 -080035using SensorVariant = std::variant<int64_t, double>;
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";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700276 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700277 sensorsAsyncResp->res.jsonValue["@odata.id"] =
278 "/redfish/v1/Chassis/" + sensorsAsyncResp->chassisId + "/" +
279 chassisSubNode;
280
281 sensorsAsyncResp->res.jsonValue["@odata.context"] =
282 "/redfish/v1/$metadata#" + chassisSubNode + "." + chassisSubNode;
283 sensorsAsyncResp->res.jsonValue["Id"] = chassisSubNode;
284 sensorsAsyncResp->res.jsonValue["Name"] = chassisSubNode;
285
286 // Get the list of sensors for this Chassis element
287 std::string sensorPath = *chassisPath + "/sensors";
288 crow::connections::systemBus->async_method_call(
289 [sensorsAsyncResp, callback{std::move(callback)}](
290 const boost::system::error_code ec,
291 const std::variant<std::vector<std::string>>&
292 variantEndpoints) {
293 if (ec)
294 {
295 if (ec.value() != EBADR)
296 {
297 messages::internalError(sensorsAsyncResp->res);
298 return;
299 }
300 }
301 const std::vector<std::string>* nodeSensorList =
302 std::get_if<std::vector<std::string>>(&(variantEndpoints));
303 if (nodeSensorList == nullptr)
304 {
305 messages::resourceNotFound(
306 sensorsAsyncResp->res, sensorsAsyncResp->chassisSubNode,
307 sensorsAsyncResp->chassisSubNode == "Thermal"
308 ? "Temperatures"
309 : "Voltages");
310 return;
311 }
312 const std::shared_ptr<boost::container::flat_set<std::string>>
313 culledSensorList = std::make_shared<
314 boost::container::flat_set<std::string>>();
315 reduceSensorList(sensorsAsyncResp, nodeSensorList,
316 culledSensorList);
317 callback(culledSensorList);
318 },
319 "xyz.openbmc_project.ObjectMapper", sensorPath,
320 "org.freedesktop.DBus.Properties", "Get",
321 "xyz.openbmc_project.Association", "endpoints");
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100322 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100323
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700324 // Get the Chassis Collection
Ed Tanous1abe55e2018-09-05 08:30:59 -0700325 crow::connections::systemBus->async_method_call(
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700326 respHandler, "xyz.openbmc_project.ObjectMapper",
327 "/xyz/openbmc_project/object_mapper",
328 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
329 "/xyz/openbmc_project/inventory", int32_t(0), interfaces);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700330 BMCWEB_LOG_DEBUG << "getChassis exit";
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100331}
332
333/**
Shawn McCarneyde629b62019-03-08 10:42:51 -0600334 * @brief Finds all DBus object paths that implement ObjectManager.
335 *
336 * Creates a mapping from the associated connection name to the object path.
337 *
338 * Finds the object paths asynchronously. Invokes callback when information has
339 * been obtained.
340 *
341 * The callback must have the following signature:
342 * @code
343 * callback(const boost::container::flat_map<std::string,
344 * std::string>& objectMgrPaths)
345 * @endcode
346 *
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700347 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyde629b62019-03-08 10:42:51 -0600348 * @param callback Callback to invoke when object paths obtained.
349 */
350template <typename Callback>
351void getObjectManagerPaths(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
352 Callback&& callback)
353{
354 BMCWEB_LOG_DEBUG << "getObjectManagerPaths enter";
355 const std::array<std::string, 1> interfaces = {
356 "org.freedesktop.DBus.ObjectManager"};
357
358 // Response handler for GetSubTree DBus method
359 auto respHandler = [callback{std::move(callback)},
360 SensorsAsyncResp](const boost::system::error_code ec,
361 const GetSubTreeType& subtree) {
362 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler enter";
363 if (ec)
364 {
365 messages::internalError(SensorsAsyncResp->res);
366 BMCWEB_LOG_ERROR << "getObjectManagerPaths respHandler: DBus error "
367 << ec;
368 return;
369 }
370
371 // Loop over returned object paths
372 boost::container::flat_map<std::string, std::string> objectMgrPaths;
373 for (const std::pair<
374 std::string,
375 std::vector<std::pair<std::string, std::vector<std::string>>>>&
376 object : subtree)
377 {
378 // Loop over connections for current object path
379 const std::string& objectPath = object.first;
380 for (const std::pair<std::string, std::vector<std::string>>&
381 objData : object.second)
382 {
383 // Add mapping from connection to object path
384 const std::string& connection = objData.first;
385 objectMgrPaths[connection] = objectPath;
386 BMCWEB_LOG_DEBUG << "Added mapping " << connection << " -> "
387 << objectPath;
388 }
389 }
390 callback(std::move(objectMgrPaths));
391 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler exit";
392 };
393
394 // Query mapper for all DBus object paths that implement ObjectManager
395 crow::connections::systemBus->async_method_call(
396 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
397 "/xyz/openbmc_project/object_mapper",
398 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", int32_t(0),
399 interfaces);
400 BMCWEB_LOG_DEBUG << "getObjectManagerPaths exit";
401}
402
403/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100404 * @brief Builds a json sensor representation of a sensor.
405 * @param sensorName The name of the sensor to be built
Gunnar Mills274fad52018-06-13 15:45:36 -0500406 * @param sensorType The type (temperature, fan_tach, etc) of the sensor to
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100407 * build
408 * @param interfacesDict A dictionary of the interfaces and properties of said
409 * interfaces to be built from
410 * @param sensor_json The json object to fill
411 */
412void objectInterfacesToJson(
413 const std::string& sensorName, const std::string& sensorType,
414 const boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700415 std::string, boost::container::flat_map<std::string, SensorVariant>>&
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100416 interfacesDict,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700417 nlohmann::json& sensor_json)
418{
419 // We need a value interface before we can do anything with it
420 auto valueIt = interfacesDict.find("xyz.openbmc_project.Sensor.Value");
421 if (valueIt == interfacesDict.end())
422 {
423 BMCWEB_LOG_ERROR << "Sensor doesn't have a value interface";
424 return;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100425 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100426
Ed Tanous1abe55e2018-09-05 08:30:59 -0700427 // Assume values exist as is (10^0 == 1) if no scale exists
428 int64_t scaleMultiplier = 0;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100429
Ed Tanous1abe55e2018-09-05 08:30:59 -0700430 auto scaleIt = valueIt->second.find("Scale");
431 // If a scale exists, pull value as int64, and use the scaling.
432 if (scaleIt != valueIt->second.end())
433 {
Ed Tanousabf2add2019-01-22 16:40:12 -0800434 const int64_t* int64Value = std::get_if<int64_t>(&scaleIt->second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700435 if (int64Value != nullptr)
436 {
437 scaleMultiplier = *int64Value;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100438 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100439 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700440
441 sensor_json["MemberId"] = sensorName;
Ed Tanouse742b6c2019-05-03 15:06:53 -0700442 sensor_json["Name"] = boost::replace_all_copy(sensorName, "_", " ");
443
Ed Tanous1abe55e2018-09-05 08:30:59 -0700444 sensor_json["Status"]["State"] = "Enabled";
445 sensor_json["Status"]["Health"] = "OK";
446
447 // Parameter to set to override the type we get from dbus, and force it to
448 // int, regardless of what is available. This is used for schemas like fan,
449 // that require integers, not floats.
450 bool forceToInt = false;
451
452 const char* unit = "Reading";
453 if (sensorType == "temperature")
454 {
455 unit = "ReadingCelsius";
456 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Temperature";
457 // TODO(ed) Documentation says that path should be type fan_tach,
458 // implementation seems to implement fan
459 }
460 else if (sensorType == "fan" || sensorType == "fan_tach")
461 {
462 unit = "Reading";
463 sensor_json["ReadingUnits"] = "RPM";
464 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
465 forceToInt = true;
466 }
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700467 else if (sensorType == "fan_pwm")
468 {
469 unit = "Reading";
470 sensor_json["ReadingUnits"] = "Percent";
471 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
472 forceToInt = true;
473 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700474 else if (sensorType == "voltage")
475 {
476 unit = "ReadingVolts";
477 sensor_json["@odata.type"] = "#Power.v1_0_0.Voltage";
478 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700479 else if (sensorType == "power")
480 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700481 std::string sensorNameLower =
482 boost::algorithm::to_lower_copy(sensorName);
483
484 if (sensorNameLower.find("input") != std::string::npos)
485 {
486 unit = "PowerInputWatts";
487 }
488 else
489 {
490 unit = "PowerOutputWatts";
491 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700492 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700493 else
494 {
495 BMCWEB_LOG_ERROR << "Redfish cannot map object type for " << sensorName;
496 return;
497 }
498 // Map of dbus interface name, dbus property name and redfish property_name
499 std::vector<std::tuple<const char*, const char*, const char*>> properties;
500 properties.reserve(7);
501
502 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600503
504 // If sensor type doesn't map to Redfish PowerSupply, add threshold props
505 if ((sensorType != "current") && (sensorType != "power"))
506 {
507 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
508 "WarningHigh", "UpperThresholdNonCritical");
509 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
510 "WarningLow", "LowerThresholdNonCritical");
511 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
512 "CriticalHigh", "UpperThresholdCritical");
513 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
514 "CriticalLow", "LowerThresholdCritical");
515 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700516
Ed Tanous2474adf2018-09-05 16:31:16 -0700517 // TODO Need to get UpperThresholdFatal and LowerThresholdFatal
518
Ed Tanous1abe55e2018-09-05 08:30:59 -0700519 if (sensorType == "temperature")
520 {
521 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
522 "MinReadingRangeTemp");
523 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
524 "MaxReadingRangeTemp");
525 }
Shawn McCarneyde629b62019-03-08 10:42:51 -0600526 else if ((sensorType != "current") && (sensorType != "power"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700527 {
Shawn McCarneyde629b62019-03-08 10:42:51 -0600528 // Sensor type doesn't map to Redfish PowerSupply; add min/max props
Ed Tanous1abe55e2018-09-05 08:30:59 -0700529 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
530 "MinReadingRange");
531 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
532 "MaxReadingRange");
533 }
534
535 for (const std::tuple<const char*, const char*, const char*>& p :
536 properties)
537 {
538 auto interfaceProperties = interfacesDict.find(std::get<0>(p));
539 if (interfaceProperties != interfacesDict.end())
540 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000541 auto valueIt = interfaceProperties->second.find(std::get<1>(p));
542 if (valueIt != interfaceProperties->second.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700543 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000544 const SensorVariant& valueVariant = valueIt->second;
545 nlohmann::json& valueIt = sensor_json[std::get<2>(p)];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700546 // Attempt to pull the int64 directly
Ed Tanousabf2add2019-01-22 16:40:12 -0800547 const int64_t* int64Value = std::get_if<int64_t>(&valueVariant);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700548
Ed Tanousabf2add2019-01-22 16:40:12 -0800549 const double* doubleValue = std::get_if<double>(&valueVariant);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700550 double temp = 0.0;
551 if (int64Value != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700552 {
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700553 temp = *int64Value;
554 }
555 else if (doubleValue != nullptr)
556 {
557 temp = *doubleValue;
558 }
559 else
560 {
561 BMCWEB_LOG_ERROR
562 << "Got value interface that wasn't int or double";
563 continue;
564 }
565 temp = temp * std::pow(10, scaleMultiplier);
566 if (forceToInt)
567 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000568 valueIt = static_cast<int64_t>(temp);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700569 }
570 else
571 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000572 valueIt = temp;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700573 }
574 }
575 }
576 }
577 BMCWEB_LOG_DEBUG << "Added sensor " << sensorName;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100578}
579
James Feist8bd25cc2019-03-15 15:14:00 -0700580static void
581 populateFanRedundancy(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp)
582{
583 crow::connections::systemBus->async_method_call(
584 [sensorsAsyncResp](const boost::system::error_code ec,
585 const GetSubTreeType& resp) {
586 if (ec)
587 {
588 return; // don't have to have this interface
589 }
Ed Tanouse278c182019-03-13 16:23:37 -0700590 for (const std::pair<std::string,
591 std::vector<std::pair<
592 std::string, std::vector<std::string>>>>&
593 pathPair : resp)
James Feist8bd25cc2019-03-15 15:14:00 -0700594 {
Ed Tanouse278c182019-03-13 16:23:37 -0700595 const std::string& path = pathPair.first;
596 const std::vector<
597 std::pair<std::string, std::vector<std::string>>>& objDict =
598 pathPair.second;
James Feist8bd25cc2019-03-15 15:14:00 -0700599 if (objDict.empty())
600 {
601 continue; // this should be impossible
602 }
603
604 const std::string& owner = objDict.begin()->first;
605 crow::connections::systemBus->async_method_call(
606 [path, owner,
607 sensorsAsyncResp](const boost::system::error_code ec,
608 std::variant<std::vector<std::string>>
609 variantEndpoints) {
610 if (ec)
611 {
612 return; // if they don't have an association we
613 // can't tell what chassis is
614 }
615 // verify part of the right chassis
616 auto endpoints = std::get_if<std::vector<std::string>>(
617 &variantEndpoints);
618
619 if (endpoints == nullptr)
620 {
621 BMCWEB_LOG_ERROR << "Invalid association interface";
622 messages::internalError(sensorsAsyncResp->res);
623 return;
624 }
625
626 auto found = std::find_if(
627 endpoints->begin(), endpoints->end(),
628 [sensorsAsyncResp](const std::string& entry) {
629 return entry.find(
630 sensorsAsyncResp->chassisId) !=
631 std::string::npos;
632 });
633
634 if (found == endpoints->end())
635 {
636 return;
637 }
638 crow::connections::systemBus->async_method_call(
639 [path, sensorsAsyncResp](
640 const boost::system::error_code ec,
641 const boost::container::flat_map<
642 std::string,
643 std::variant<uint8_t,
644 std::vector<std::string>,
645 std::string>>& ret) {
646 if (ec)
647 {
648 return; // don't have to have this
649 // interface
650 }
651 auto findFailures = ret.find("AllowedFailures");
652 auto findCollection = ret.find("Collection");
653 auto findStatus = ret.find("Status");
654
655 if (findFailures == ret.end() ||
656 findCollection == ret.end() ||
657 findStatus == ret.end())
658 {
659 BMCWEB_LOG_ERROR
660 << "Invalid redundancy interface";
661 messages::internalError(
662 sensorsAsyncResp->res);
663 return;
664 }
665
666 auto allowedFailures = std::get_if<uint8_t>(
667 &(findFailures->second));
668 auto collection =
669 std::get_if<std::vector<std::string>>(
670 &(findCollection->second));
671 auto status = std::get_if<std::string>(
672 &(findStatus->second));
673
674 if (allowedFailures == nullptr ||
675 collection == nullptr || status == nullptr)
676 {
677
678 BMCWEB_LOG_ERROR
679 << "Invalid redundancy interface "
680 "types";
681 messages::internalError(
682 sensorsAsyncResp->res);
683 return;
684 }
685 size_t lastSlash = path.rfind("/");
686 if (lastSlash == std::string::npos)
687 {
688 // this should be impossible
689 messages::internalError(
690 sensorsAsyncResp->res);
691 return;
692 }
693 std::string name = path.substr(lastSlash + 1);
694 std::replace(name.begin(), name.end(), '_',
695 ' ');
696
697 std::string health;
698
699 if (boost::ends_with(*status, "Full"))
700 {
701 health = "OK";
702 }
703 else if (boost::ends_with(*status, "Degraded"))
704 {
705 health = "Warning";
706 }
707 else
708 {
709 health = "Critical";
710 }
711 std::vector<nlohmann::json> redfishCollection;
712 const auto& fanRedfish =
713 sensorsAsyncResp->res.jsonValue["Fans"];
714 for (const std::string& item : *collection)
715 {
716 lastSlash = item.rfind("/");
717 // make a copy as collection is const
718 std::string itemName =
719 item.substr(lastSlash + 1);
720 /*
721 todo(ed): merge patch that fixes the names
722 std::replace(itemName.begin(),
723 itemName.end(), '_', ' ');*/
724 auto schemaItem = std::find_if(
725 fanRedfish.begin(), fanRedfish.end(),
726 [itemName](const nlohmann::json& fan) {
727 return fan["MemberId"] == itemName;
728 });
729 if (schemaItem != fanRedfish.end())
730 {
731 redfishCollection.push_back(
732 {{"@odata.id",
733 (*schemaItem)["@odata.id"]}});
734 }
735 else
736 {
737 BMCWEB_LOG_ERROR
738 << "failed to find fan in schema";
739 messages::internalError(
740 sensorsAsyncResp->res);
741 return;
742 }
743 }
744
745 auto& resp = sensorsAsyncResp->res
746 .jsonValue["Redundancy"];
747 resp.push_back(
748 {{"@odata.id",
749 "/refish/v1/Chassis/" +
750 sensorsAsyncResp->chassisId + "/" +
751 sensorsAsyncResp->chassisSubNode +
752 "#/Redundancy/" +
753 std::to_string(resp.size())},
754 {"@odata.type",
755 "#Redundancy.v1_3_2.Redundancy"},
756 {"MinNumNeeded",
757 collection->size() - *allowedFailures},
758 {"MemberId", name},
759 {"Mode", "N+m"},
760 {"Name", name},
761 {"RedundancySet", redfishCollection},
762 {"Status",
763 {{"Health", health},
764 {"State", "Enabled"}}}});
765 },
766 owner, path, "org.freedesktop.DBus.Properties",
767 "GetAll",
768 "xyz.openbmc_project.Control.FanRedundancy");
769 },
770 "xyz.openbmc_project.ObjectMapper", path + "/inventory",
771 "org.freedesktop.DBus.Properties", "Get",
772 "xyz.openbmc_project.Association", "endpoints");
773 }
774 },
775 "xyz.openbmc_project.ObjectMapper",
776 "/xyz/openbmc_project/object_mapper",
777 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
778 "/xyz/openbmc_project/control", 2,
779 std::array<const char*, 1>{
780 "xyz.openbmc_project.Control.FanRedundancy"});
781}
782
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700783void sortJSONResponse(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
784{
785 nlohmann::json& response = SensorsAsyncResp->res.jsonValue;
786 std::array<std::string, 2> sensorHeaders{"Temperatures", "Fans"};
787 if (SensorsAsyncResp->chassisSubNode == "Power")
788 {
789 sensorHeaders = {"Voltages", "PowerSupplies"};
790 }
791 for (const std::string& sensorGroup : sensorHeaders)
792 {
793 nlohmann::json::iterator entry = response.find(sensorGroup);
794 if (entry != response.end())
795 {
796 std::sort(entry->begin(), entry->end(),
797 [](nlohmann::json& c1, nlohmann::json& c2) {
798 return c1["Name"] < c2["Name"];
799 });
800
801 // add the index counts to the end of each entry
802 size_t count = 0;
803 for (nlohmann::json& sensorJson : *entry)
804 {
805 nlohmann::json::iterator odata = sensorJson.find("@odata.id");
806 if (odata == sensorJson.end())
807 {
808 continue;
809 }
810 std::string* value = odata->get_ptr<std::string*>();
811 if (value != nullptr)
812 {
813 *value += std::to_string(count);
814 count++;
815 }
816 }
817 }
818 }
819}
820
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100821/**
Shawn McCarneyde629b62019-03-08 10:42:51 -0600822 * @brief Gets the values of the specified sensors.
823 *
824 * Stores the results as JSON in the SensorsAsyncResp.
825 *
826 * Gets the sensor values asynchronously. Stores the results later when the
827 * information has been obtained.
828 *
829 * The sensorNames set contains all sensors for the current chassis.
830 * SensorsAsyncResp contains the requested sensor types. Only sensors of a
831 * requested type are included in the JSON output.
832 *
833 * To minimize the number of DBus calls, the DBus method
834 * org.freedesktop.DBus.ObjectManager.GetManagedObjects() is used to get the
835 * values of all sensors provided by a connection (service).
836 *
837 * The connections set contains all the connections that provide sensor values.
838 *
839 * The objectMgrPaths map contains mappings from a connection name to the
840 * corresponding DBus object path that implements ObjectManager.
841 *
842 * @param SensorsAsyncResp Pointer to object holding response data.
843 * @param sensorNames All sensors within the current chassis.
844 * @param connections Connections that provide sensor values.
845 * @param objectMgrPaths Mappings from connection name to DBus object path that
846 * implements ObjectManager.
847 */
848void getSensorData(
849 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700850 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
Shawn McCarneyde629b62019-03-08 10:42:51 -0600851 const boost::container::flat_set<std::string>& connections,
852 const boost::container::flat_map<std::string, std::string>& objectMgrPaths)
853{
854 BMCWEB_LOG_DEBUG << "getSensorData enter";
855 // Get managed objects from all services exposing sensors
856 for (const std::string& connection : connections)
857 {
858 // Response handler to process managed objects
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700859 auto getManagedObjectsCb = [SensorsAsyncResp, sensorNames](
Shawn McCarneyde629b62019-03-08 10:42:51 -0600860 const boost::system::error_code ec,
861 ManagedObjectsVectorType& resp) {
862 BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter";
863 if (ec)
864 {
865 BMCWEB_LOG_ERROR << "getManagedObjectsCb DBUS error: " << ec;
866 messages::internalError(SensorsAsyncResp->res);
867 return;
868 }
869 // Go through all objects and update response with sensor data
870 for (const auto& objDictEntry : resp)
871 {
872 const std::string& objPath =
873 static_cast<const std::string&>(objDictEntry.first);
874 BMCWEB_LOG_DEBUG << "getManagedObjectsCb parsing object "
875 << objPath;
876
Shawn McCarneyde629b62019-03-08 10:42:51 -0600877 std::vector<std::string> split;
878 // Reserve space for
879 // /xyz/openbmc_project/sensors/<name>/<subname>
880 split.reserve(6);
881 boost::algorithm::split(split, objPath, boost::is_any_of("/"));
882 if (split.size() < 6)
883 {
884 BMCWEB_LOG_ERROR << "Got path that isn't long enough "
885 << objPath;
886 continue;
887 }
888 // These indexes aren't intuitive, as boost::split puts an empty
889 // string at the beginning
890 const std::string& sensorType = split[4];
891 const std::string& sensorName = split[5];
892 BMCWEB_LOG_DEBUG << "sensorName " << sensorName
893 << " sensorType " << sensorType;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700894 if (sensorNames->find(objPath) == sensorNames->end())
Shawn McCarneyde629b62019-03-08 10:42:51 -0600895 {
896 BMCWEB_LOG_ERROR << sensorName << " not in sensor list ";
897 continue;
898 }
899
900 const char* fieldName = nullptr;
901 if (sensorType == "temperature")
902 {
903 fieldName = "Temperatures";
904 }
905 else if (sensorType == "fan" || sensorType == "fan_tach" ||
906 sensorType == "fan_pwm")
907 {
908 fieldName = "Fans";
909 }
910 else if (sensorType == "voltage")
911 {
912 fieldName = "Voltages";
913 }
914 else if (sensorType == "current")
915 {
916 fieldName = "PowerSupplies";
917 }
918 else if (sensorType == "power")
919 {
920 fieldName = "PowerSupplies";
921 }
922 else
923 {
924 BMCWEB_LOG_ERROR << "Unsure how to handle sensorType "
925 << sensorType;
926 continue;
927 }
928
929 nlohmann::json& tempArray =
930 SensorsAsyncResp->res.jsonValue[fieldName];
931
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700932 if (fieldName == "PowerSupplies" && !tempArray.empty())
933 {
934 // Power supplies put multiple "sensors" into a single power
935 // supply entry, so only create the first one
936 }
937 else
938 {
939 tempArray.push_back(
940 {{"@odata.id", "/redfish/v1/Chassis/" +
941 SensorsAsyncResp->chassisId + "/" +
942 SensorsAsyncResp->chassisSubNode +
943 "#/" + fieldName + "/"}});
944 }
Shawn McCarneyde629b62019-03-08 10:42:51 -0600945 nlohmann::json& sensorJson = tempArray.back();
946
947 objectInterfacesToJson(sensorName, sensorType,
948 objDictEntry.second, sensorJson);
949 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700950 if (SensorsAsyncResp.use_count() == 1)
James Feist8bd25cc2019-03-15 15:14:00 -0700951 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700952 sortJSONResponse(SensorsAsyncResp);
953 if (SensorsAsyncResp->chassisSubNode == "Thermal")
954 {
955 populateFanRedundancy(SensorsAsyncResp);
956 }
James Feist8bd25cc2019-03-15 15:14:00 -0700957 }
Shawn McCarneyde629b62019-03-08 10:42:51 -0600958 BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit";
959 };
960
961 // Find DBus object path that implements ObjectManager for the current
962 // connection. If no mapping found, default to "/".
963 auto iter = objectMgrPaths.find(connection);
964 const std::string& objectMgrPath =
965 (iter != objectMgrPaths.end()) ? iter->second : "/";
966 BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
967 << objectMgrPath;
968
969 crow::connections::systemBus->async_method_call(
970 getManagedObjectsCb, connection, objectMgrPath,
971 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
972 };
973 BMCWEB_LOG_DEBUG << "getSensorData exit";
974}
975
976/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100977 * @brief Entry point for retrieving sensors data related to requested
978 * chassis.
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200979 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100980 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700981void getChassisData(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
982{
983 BMCWEB_LOG_DEBUG << "getChassisData enter";
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700984 auto getChassisCb =
985 [SensorsAsyncResp](
986 std::shared_ptr<boost::container::flat_set<std::string>>
987 sensorNames) {
988 BMCWEB_LOG_DEBUG << "getChassisCb enter";
989 auto getConnectionCb =
990 [SensorsAsyncResp,
991 sensorNames](const boost::container::flat_set<std::string>&
992 connections) {
993 BMCWEB_LOG_DEBUG << "getConnectionCb enter";
994 auto getObjectManagerPathsCb =
995 [SensorsAsyncResp, sensorNames, connections](
996 const boost::container::flat_map<
997 std::string, std::string>& objectMgrPaths) {
998 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb enter";
999 // Get sensor data and store results in JSON
1000 // response
1001 getSensorData(SensorsAsyncResp, sensorNames,
1002 connections, objectMgrPaths);
1003 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb exit";
1004 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001005
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001006 // Get mapping from connection names to the DBus object
1007 // paths that implement the ObjectManager interface
1008 getObjectManagerPaths(SensorsAsyncResp,
1009 std::move(getObjectManagerPathsCb));
1010 BMCWEB_LOG_DEBUG << "getConnectionCb exit";
1011 };
Shawn McCarneyde629b62019-03-08 10:42:51 -06001012
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001013 // Get set of connections that provide sensor values
1014 getConnections(SensorsAsyncResp, sensorNames,
1015 std::move(getConnectionCb));
1016 BMCWEB_LOG_DEBUG << "getChassisCb exit";
1017 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001018
Shawn McCarney26f03892019-05-03 13:20:24 -05001019 // Get set of sensors in chassis
Ed Tanous1abe55e2018-09-05 08:30:59 -07001020 getChassis(SensorsAsyncResp, std::move(getChassisCb));
1021 BMCWEB_LOG_DEBUG << "getChassisData exit";
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001022};
1023
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301024/**
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001025 * @brief Find the requested sensorName in the list of all sensors supplied by
1026 * the chassis node
1027 *
1028 * @param sensorName The sensor name supplied in the PATCH request
1029 * @param sensorsList The list of sensors managed by the chassis node
1030 * @param sensorsModified The list of sensors that were found as a result of
1031 * repeated calls to this function
1032 */
1033bool findSensorNameUsingSensorPath(
1034 const std::string& sensorName,
1035 boost::container::flat_set<std::string>& sensorsList,
1036 boost::container::flat_set<std::string>& sensorsModified)
1037{
1038 for (const std::string& chassisSensor : sensorsList)
1039 {
1040 std::string thisSensorName;
1041 if (!dbus::utility::getNthStringFromPath(chassisSensor, 5,
1042 thisSensorName))
1043 {
1044 BMCWEB_LOG_ERROR << "Got path that isn't long enough "
1045 << chassisSensor;
1046 continue;
1047 }
1048 if (thisSensorName == sensorName)
1049 {
1050 sensorsModified.emplace(chassisSensor);
1051 return true;
1052 }
1053 }
1054 return false;
1055}
1056
1057/**
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301058 * @brief Entry point for overriding sensor values of given sensor
1059 *
1060 * @param res response object
1061 * @param req request object
1062 * @param params parameter passed for CRUD
1063 * @param typeList TypeList of sensors for the resource queried
1064 * @param chassisSubNode Chassis Node for which the query has to happen
1065 */
1066void setSensorOverride(crow::Response& res, const crow::Request& req,
1067 const std::vector<std::string>& params,
Ed Tanousb01bf292019-03-25 19:25:26 +00001068 const std::initializer_list<const char*> typeList,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301069 const std::string& chassisSubNode)
1070{
1071
1072 // TODO: Need to figure out dynamic way to restrict patch (Set Sensor
1073 // override) based on another d-bus announcement to be more generic.
1074 if (params.size() != 1)
1075 {
1076 messages::internalError(res);
1077 res.end();
1078 return;
1079 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301080
1081 std::unordered_map<std::string, std::vector<nlohmann::json>> allCollections;
1082 std::optional<std::vector<nlohmann::json>> temperatureCollections;
1083 std::optional<std::vector<nlohmann::json>> fanCollections;
1084 std::vector<nlohmann::json> voltageCollections;
1085 BMCWEB_LOG_INFO << "setSensorOverride for subNode" << chassisSubNode
1086 << "\n";
1087
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301088 if (chassisSubNode == "Thermal")
1089 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301090 if (!json_util::readJson(req, res, "Temperatures",
1091 temperatureCollections, "Fans",
1092 fanCollections))
1093 {
1094 return;
1095 }
1096 if (!temperatureCollections && !fanCollections)
1097 {
1098 messages::resourceNotFound(res, "Thermal",
1099 "Temperatures / Voltages");
1100 res.end();
1101 return;
1102 }
1103 if (temperatureCollections)
1104 {
1105 allCollections.emplace("Temperatures",
1106 *std::move(temperatureCollections));
1107 }
1108 if (fanCollections)
1109 {
1110 allCollections.emplace("Fans", *std::move(fanCollections));
1111 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301112 }
1113 else if (chassisSubNode == "Power")
1114 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301115 if (!json_util::readJson(req, res, "Voltages", voltageCollections))
1116 {
1117 return;
1118 }
1119 allCollections.emplace("Voltages", std::move(voltageCollections));
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301120 }
1121 else
1122 {
1123 res.result(boost::beast::http::status::not_found);
1124 res.end();
1125 return;
1126 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301127
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301128 const char* propertyValueName;
1129 std::unordered_map<std::string, std::pair<double, std::string>> overrideMap;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301130 std::string memberId;
1131 double value;
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301132 for (auto& collectionItems : allCollections)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301133 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301134 if (collectionItems.first == "Temperatures")
1135 {
1136 propertyValueName = "ReadingCelsius";
1137 }
1138 else if (collectionItems.first == "Fans")
1139 {
1140 propertyValueName = "Reading";
1141 }
1142 else
1143 {
1144 propertyValueName = "ReadingVolts";
1145 }
1146 for (auto& item : collectionItems.second)
1147 {
1148 if (!json_util::readJson(item, res, "MemberId", memberId,
1149 propertyValueName, value))
1150 {
1151 return;
1152 }
1153 overrideMap.emplace(memberId,
1154 std::make_pair(value, collectionItems.first));
1155 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301156 }
1157 const std::string& chassisName = params[0];
1158 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
1159 res, chassisName, typeList, chassisSubNode);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001160 auto getChassisSensorListCb = [sensorAsyncResp,
1161 overrideMap](const std::shared_ptr<
1162 boost::container::flat_set<
1163 std::string>>
1164 sensorsList) {
1165 // Match sensor names in the PATCH request to those managed by the
1166 // chassis node
1167 const std::shared_ptr<boost::container::flat_set<std::string>>
1168 sensorNames =
1169 std::make_shared<boost::container::flat_set<std::string>>();
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301170 for (const auto& item : overrideMap)
1171 {
1172 const auto& sensor = item.first;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001173 if (!findSensorNameUsingSensorPath(sensor, *sensorsList,
1174 *sensorNames))
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301175 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301176 BMCWEB_LOG_INFO << "Unable to find memberId " << item.first;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301177 messages::resourceNotFound(sensorAsyncResp->res,
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301178 item.second.second, item.first);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301179 return;
1180 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301181 }
1182 // Get the connection to which the memberId belongs
1183 auto getObjectsWithConnectionCb =
1184 [sensorAsyncResp, overrideMap](
1185 const boost::container::flat_set<std::string>& connections,
1186 const std::set<std::pair<std::string, std::string>>&
1187 objectsWithConnection) {
1188 if (objectsWithConnection.size() != overrideMap.size())
1189 {
1190 BMCWEB_LOG_INFO
1191 << "Unable to find all objects with proper connection "
1192 << objectsWithConnection.size() << " requested "
1193 << overrideMap.size() << "\n";
1194 messages::resourceNotFound(
1195 sensorAsyncResp->res,
1196 sensorAsyncResp->chassisSubNode == "Thermal"
1197 ? "Temperatures"
1198 : "Voltages",
1199 "Count");
1200 return;
1201 }
1202 for (const auto& item : objectsWithConnection)
1203 {
1204
1205 auto lastPos = item.first.rfind('/');
1206 if (lastPos == std::string::npos)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301207 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301208 messages::internalError(sensorAsyncResp->res);
1209 return;
1210 }
1211 std::string sensorName = item.first.substr(lastPos + 1);
1212
1213 const auto& iterator = overrideMap.find(sensorName);
1214 if (iterator == overrideMap.end())
1215 {
1216 BMCWEB_LOG_INFO << "Unable to find sensor object"
1217 << item.first << "\n";
1218 messages::internalError(sensorAsyncResp->res);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301219 return;
1220 }
1221 crow::connections::systemBus->async_method_call(
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301222 [sensorAsyncResp](const boost::system::error_code ec) {
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301223 if (ec)
1224 {
1225 BMCWEB_LOG_DEBUG
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301226 << "setOverrideValueStatus DBUS error: "
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301227 << ec;
1228 messages::internalError(sensorAsyncResp->res);
1229 return;
1230 }
1231 },
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301232 item.second, item.first,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301233 "org.freedesktop.DBus.Properties", "Set",
1234 "xyz.openbmc_project.Sensor.Value", "Value",
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301235 sdbusplus::message::variant<double>(
1236 iterator->second.first));
1237 }
1238 };
1239 // Get object with connection for the given sensor name
1240 getObjectsWithConnection(sensorAsyncResp, sensorNames,
1241 std::move(getObjectsWithConnectionCb));
1242 };
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301243 // get full sensor list for the given chassisId and cross verify the sensor.
1244 getChassis(sensorAsyncResp, std::move(getChassisSensorListCb));
1245}
1246
Ed Tanous1abe55e2018-09-05 08:30:59 -07001247} // namespace redfish