blob: 376c7e4e554c9ed0a725b76e8a11ce9b96bc957a [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
Ed Tanousd76323e2018-08-07 14:35:40 -070031constexpr const char* dbusSensorPrefix = "/xyz/openbmc_project/sensors/";
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010032
33using GetSubTreeType = std::vector<
34 std::pair<std::string,
35 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
36
Ed Tanousabf2add2019-01-22 16:40:12 -080037using SensorVariant = std::variant<int64_t, double>;
Ed Tanousaa2e59c2018-04-12 12:17:20 -070038
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010039using ManagedObjectsVectorType = std::vector<std::pair<
Ed Tanousaa2e59c2018-04-12 12:17:20 -070040 sdbusplus::message::object_path,
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010041 boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -070042 std::string, boost::container::flat_map<std::string, SensorVariant>>>>;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010043
44/**
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020045 * SensorsAsyncResp
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010046 * Gathers data needed for response processing after async calls are done
47 */
Ed Tanous1abe55e2018-09-05 08:30:59 -070048class SensorsAsyncResp
49{
50 public:
51 SensorsAsyncResp(crow::Response& response, const std::string& chassisId,
Ed Tanous2474adf2018-09-05 16:31:16 -070052 const std::initializer_list<const char*> types,
53 const std::string& subNode) :
Ed Tanous43b761d2019-02-13 20:10:56 -080054 res(response),
55 chassisId(chassisId), types(types), chassisSubNode(subNode)
Ed Tanous1abe55e2018-09-05 08:30:59 -070056 {
57 res.jsonValue["@odata.id"] =
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +053058 "/redfish/v1/Chassis/" + chassisId + "/" + subNode;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010059 }
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020060
Ed Tanous1abe55e2018-09-05 08:30:59 -070061 ~SensorsAsyncResp()
62 {
63 if (res.result() == boost::beast::http::status::internal_server_error)
64 {
65 // Reset the json object to clear out any data that made it in
66 // before the error happened todo(ed) handle error condition with
67 // proper code
68 res.jsonValue = nlohmann::json::object();
69 }
70 res.end();
71 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010072
Ed Tanous1abe55e2018-09-05 08:30:59 -070073 crow::Response& res;
74 std::string chassisId{};
75 const std::vector<const char*> types;
Ed Tanous2474adf2018-09-05 16:31:16 -070076 std::string chassisSubNode{};
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010077};
78
79/**
Shawn McCarneyde629b62019-03-08 10:42:51 -060080 * @brief Checks whether the specified sensor is one of the requested types.
81 * @param objectPath DBus object path of the sensor.
82 * @param SensorsAsyncResp Pointer to object holding response data and requested
83 * sensor types.
84 * @return true if sensor type is requested, false otherwise.
85 */
86inline bool
87 isRequestedSensorType(const std::string& objectPath,
88 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
89{
90 for (const char* type : SensorsAsyncResp->types)
91 {
92 if (boost::starts_with(objectPath, type))
93 {
94 return true;
95 }
96 }
97 return false;
98}
99
100/**
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530101 * @brief Get objects with connection necessary for sensors
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200102 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100103 * @param sensorNames Sensors retrieved from chassis
104 * @param callback Callback for processing gathered connections
105 */
106template <typename Callback>
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530107void getObjectsWithConnection(
108 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
109 const boost::container::flat_set<std::string>& sensorNames,
110 Callback&& callback)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700111{
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530112 BMCWEB_LOG_DEBUG << "getObjectsWithConnection enter";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700113 const std::string path = "/xyz/openbmc_project/sensors";
114 const std::array<std::string, 1> interfaces = {
115 "xyz.openbmc_project.Sensor.Value"};
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100116
Ed Tanous1abe55e2018-09-05 08:30:59 -0700117 // Response handler for parsing objects subtree
118 auto respHandler = [callback{std::move(callback)}, SensorsAsyncResp,
119 sensorNames](const boost::system::error_code ec,
120 const GetSubTreeType& subtree) {
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530121 BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler enter";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700122 if (ec)
123 {
Ed Tanous5f7d88c2018-11-14 14:08:56 -0800124 messages::internalError(SensorsAsyncResp->res);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530125 BMCWEB_LOG_ERROR
126 << "getObjectsWithConnection resp_handler: Dbus error " << ec;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700127 return;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100128 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100129
Ed Tanous1abe55e2018-09-05 08:30:59 -0700130 BMCWEB_LOG_DEBUG << "Found " << subtree.size() << " subtrees";
131
132 // Make unique list of connections only for requested sensor types and
133 // found in the chassis
134 boost::container::flat_set<std::string> connections;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530135 std::set<std::pair<std::string, std::string>> objectsWithConnection;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700136 // Intrinsic to avoid malloc. Most systems will have < 8 sensor
137 // producers
138 connections.reserve(8);
139
140 BMCWEB_LOG_DEBUG << "sensorNames list count: " << sensorNames.size();
141 for (const std::string& tsensor : sensorNames)
142 {
143 BMCWEB_LOG_DEBUG << "Sensor to find: " << tsensor;
144 }
145
146 for (const std::pair<
147 std::string,
148 std::vector<std::pair<std::string, std::vector<std::string>>>>&
149 object : subtree)
150 {
Shawn McCarneyde629b62019-03-08 10:42:51 -0600151 if (isRequestedSensorType(object.first, SensorsAsyncResp))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700152 {
Shawn McCarneyde629b62019-03-08 10:42:51 -0600153 auto lastPos = object.first.rfind('/');
154 if (lastPos != std::string::npos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700155 {
Shawn McCarneyde629b62019-03-08 10:42:51 -0600156 std::string sensorName = object.first.substr(lastPos + 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700157
Shawn McCarneyde629b62019-03-08 10:42:51 -0600158 if (sensorNames.find(sensorName) != sensorNames.end())
159 {
160 // For each Connection name
161 for (const std::pair<std::string,
162 std::vector<std::string>>&
163 objData : object.second)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700164 {
Shawn McCarneyde629b62019-03-08 10:42:51 -0600165 BMCWEB_LOG_DEBUG << "Adding connection: "
166 << objData.first;
167 connections.insert(objData.first);
168 objectsWithConnection.insert(
169 std::make_pair(object.first, objData.first));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700170 }
171 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700172 }
173 }
174 }
175 BMCWEB_LOG_DEBUG << "Found " << connections.size() << " connections";
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530176 callback(std::move(connections), std::move(objectsWithConnection));
177 BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler exit";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700178 };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700179 // Make call to ObjectMapper to find all sensors objects
180 crow::connections::systemBus->async_method_call(
181 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
182 "/xyz/openbmc_project/object_mapper",
183 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 2, interfaces);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530184 BMCWEB_LOG_DEBUG << "getObjectsWithConnection exit";
185}
186
187/**
188 * @brief Create connections necessary for sensors
189 * @param SensorsAsyncResp Pointer to object holding response data
190 * @param sensorNames Sensors retrieved from chassis
191 * @param callback Callback for processing gathered connections
192 */
193template <typename Callback>
194void getConnections(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
195 const boost::container::flat_set<std::string>& sensorNames,
196 Callback&& callback)
197{
198 auto objectsWithConnectionCb =
199 [callback](const boost::container::flat_set<std::string>& connections,
200 const std::set<std::pair<std::string, std::string>>&
201 objectsWithConnection) {
202 callback(std::move(connections));
203 };
204 getObjectsWithConnection(SensorsAsyncResp, sensorNames,
205 std::move(objectsWithConnectionCb));
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100206}
207
208/**
Shawn McCarneyde629b62019-03-08 10:42:51 -0600209 * @brief Gets all DBus sensor names.
210 *
211 * Finds the sensor names asynchronously. Invokes callback when information has
212 * been obtained.
213 *
214 * The callback must have the following signature:
215 * @code
216 * callback(boost::container::flat_set<std::string>& sensorNames)
217 * @endcode
218 *
219 * @param SensorsAsyncResp Pointer to object holding response data.
220 * @param callback Callback to invoke when sensor names obtained.
221 */
222template <typename Callback>
223void getAllSensors(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
224 Callback&& callback)
225{
226 BMCWEB_LOG_DEBUG << "getAllSensors enter";
227 const std::array<std::string, 1> interfaces = {
228 "xyz.openbmc_project.Sensor.Value"};
229
230 // Response handler for GetSubTreePaths DBus method
231 auto respHandler = [callback{std::move(callback)}, SensorsAsyncResp](
232 const boost::system::error_code ec,
233 const std::vector<std::string>& sensorList) {
234 BMCWEB_LOG_DEBUG << "getAllSensors respHandler enter";
235 if (ec)
236 {
237 messages::internalError(SensorsAsyncResp->res);
238 BMCWEB_LOG_ERROR << "getAllSensors respHandler: DBus error " << ec;
239 return;
240 }
241 boost::container::flat_set<std::string> sensorNames;
242 for (const std::string& sensor : sensorList)
243 {
244 std::size_t lastPos = sensor.rfind("/");
245 if (lastPos == std::string::npos)
246 {
247 BMCWEB_LOG_ERROR << "Failed to find '/' in " << sensor;
248 continue;
249 }
250 std::string sensorName = sensor.substr(lastPos + 1);
251 sensorNames.emplace(sensorName);
252 BMCWEB_LOG_DEBUG << "Added sensor name " << sensorName;
253 }
254 callback(sensorNames);
255 BMCWEB_LOG_DEBUG << "getAllSensors respHandler exit";
256 };
257
258 // Query mapper for all DBus sensor object paths
259 crow::connections::systemBus->async_method_call(
260 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
261 "/xyz/openbmc_project/object_mapper",
262 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
263 "/xyz/openbmc_project/sensors", int32_t(0), interfaces);
264 BMCWEB_LOG_DEBUG << "getAllSensors exit";
265}
266
267/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100268 * @brief Retrieves requested chassis sensors and redundancy data from DBus .
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200269 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100270 * @param callback Callback for next step in gathered sensor processing
271 */
272template <typename Callback>
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200273void getChassis(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700274 Callback&& callback)
275{
276 BMCWEB_LOG_DEBUG << "getChassis enter";
277 // Process response from EntityManager and extract chassis data
278 auto respHandler = [callback{std::move(callback)},
279 SensorsAsyncResp](const boost::system::error_code ec,
280 ManagedObjectsVectorType& resp) {
281 BMCWEB_LOG_DEBUG << "getChassis respHandler enter";
282 if (ec)
283 {
284 BMCWEB_LOG_ERROR << "getChassis respHandler DBUS error: " << ec;
Ed Tanous5f7d88c2018-11-14 14:08:56 -0800285 messages::internalError(SensorsAsyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700286 return;
287 }
288 boost::container::flat_set<std::string> sensorNames;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100289
Ed Tanous1abe55e2018-09-05 08:30:59 -0700290 // SensorsAsyncResp->chassisId
291 bool foundChassis = false;
292 std::vector<std::string> split;
293 // Reserve space for
294 // /xyz/openbmc_project/inventory/<name>/<subname> + 3 subnames
295 split.reserve(8);
Ed Tanousdaf36e22018-04-20 16:01:36 -0700296
Ed Tanous1abe55e2018-09-05 08:30:59 -0700297 for (const auto& objDictEntry : resp)
298 {
299 const std::string& objectPath =
300 static_cast<const std::string&>(objDictEntry.first);
301 boost::algorithm::split(split, objectPath, boost::is_any_of("/"));
302 if (split.size() < 2)
303 {
304 BMCWEB_LOG_ERROR << "Got path that isn't long enough "
305 << objectPath;
306 split.clear();
307 continue;
308 }
309 const std::string& sensorName = split.end()[-1];
310 const std::string& chassisName = split.end()[-2];
Ed Tanousdaf36e22018-04-20 16:01:36 -0700311
Ed Tanous1abe55e2018-09-05 08:30:59 -0700312 if (chassisName != SensorsAsyncResp->chassisId)
313 {
314 split.clear();
315 continue;
316 }
317 BMCWEB_LOG_DEBUG << "New sensor: " << sensorName;
318 foundChassis = true;
319 sensorNames.emplace(sensorName);
320 split.clear();
321 };
322 BMCWEB_LOG_DEBUG << "Found " << sensorNames.size() << " Sensor names";
323
324 if (!foundChassis)
325 {
326 BMCWEB_LOG_INFO << "Unable to find chassis named "
327 << SensorsAsyncResp->chassisId;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700328 messages::resourceNotFound(SensorsAsyncResp->res, "Chassis",
329 SensorsAsyncResp->chassisId);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700330 }
331 else
332 {
333 callback(sensorNames);
334 }
335 BMCWEB_LOG_DEBUG << "getChassis respHandler exit";
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100336 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100337
Ed Tanous1abe55e2018-09-05 08:30:59 -0700338 // Make call to EntityManager to find all chassis objects
339 crow::connections::systemBus->async_method_call(
340 respHandler, "xyz.openbmc_project.EntityManager", "/",
341 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
342 BMCWEB_LOG_DEBUG << "getChassis exit";
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100343}
344
345/**
Shawn McCarneyde629b62019-03-08 10:42:51 -0600346 * @brief Finds all DBus object paths that implement ObjectManager.
347 *
348 * Creates a mapping from the associated connection name to the object path.
349 *
350 * Finds the object paths asynchronously. Invokes callback when information has
351 * been obtained.
352 *
353 * The callback must have the following signature:
354 * @code
355 * callback(const boost::container::flat_map<std::string,
356 * std::string>& objectMgrPaths)
357 * @endcode
358 *
359 * @param SensorsAsyncResp Pointer to object holding response data.
360 * @param callback Callback to invoke when object paths obtained.
361 */
362template <typename Callback>
363void getObjectManagerPaths(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
364 Callback&& callback)
365{
366 BMCWEB_LOG_DEBUG << "getObjectManagerPaths enter";
367 const std::array<std::string, 1> interfaces = {
368 "org.freedesktop.DBus.ObjectManager"};
369
370 // Response handler for GetSubTree DBus method
371 auto respHandler = [callback{std::move(callback)},
372 SensorsAsyncResp](const boost::system::error_code ec,
373 const GetSubTreeType& subtree) {
374 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler enter";
375 if (ec)
376 {
377 messages::internalError(SensorsAsyncResp->res);
378 BMCWEB_LOG_ERROR << "getObjectManagerPaths respHandler: DBus error "
379 << ec;
380 return;
381 }
382
383 // Loop over returned object paths
384 boost::container::flat_map<std::string, std::string> objectMgrPaths;
385 for (const std::pair<
386 std::string,
387 std::vector<std::pair<std::string, std::vector<std::string>>>>&
388 object : subtree)
389 {
390 // Loop over connections for current object path
391 const std::string& objectPath = object.first;
392 for (const std::pair<std::string, std::vector<std::string>>&
393 objData : object.second)
394 {
395 // Add mapping from connection to object path
396 const std::string& connection = objData.first;
397 objectMgrPaths[connection] = objectPath;
398 BMCWEB_LOG_DEBUG << "Added mapping " << connection << " -> "
399 << objectPath;
400 }
401 }
402 callback(std::move(objectMgrPaths));
403 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler exit";
404 };
405
406 // Query mapper for all DBus object paths that implement ObjectManager
407 crow::connections::systemBus->async_method_call(
408 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
409 "/xyz/openbmc_project/object_mapper",
410 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", int32_t(0),
411 interfaces);
412 BMCWEB_LOG_DEBUG << "getObjectManagerPaths exit";
413}
414
415/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100416 * @brief Builds a json sensor representation of a sensor.
417 * @param sensorName The name of the sensor to be built
Gunnar Mills274fad52018-06-13 15:45:36 -0500418 * @param sensorType The type (temperature, fan_tach, etc) of the sensor to
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100419 * build
420 * @param interfacesDict A dictionary of the interfaces and properties of said
421 * interfaces to be built from
422 * @param sensor_json The json object to fill
423 */
424void objectInterfacesToJson(
425 const std::string& sensorName, const std::string& sensorType,
426 const boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700427 std::string, boost::container::flat_map<std::string, SensorVariant>>&
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100428 interfacesDict,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700429 nlohmann::json& sensor_json)
430{
431 // We need a value interface before we can do anything with it
432 auto valueIt = interfacesDict.find("xyz.openbmc_project.Sensor.Value");
433 if (valueIt == interfacesDict.end())
434 {
435 BMCWEB_LOG_ERROR << "Sensor doesn't have a value interface";
436 return;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100437 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100438
Ed Tanous1abe55e2018-09-05 08:30:59 -0700439 // Assume values exist as is (10^0 == 1) if no scale exists
440 int64_t scaleMultiplier = 0;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100441
Ed Tanous1abe55e2018-09-05 08:30:59 -0700442 auto scaleIt = valueIt->second.find("Scale");
443 // If a scale exists, pull value as int64, and use the scaling.
444 if (scaleIt != valueIt->second.end())
445 {
Ed Tanousabf2add2019-01-22 16:40:12 -0800446 const int64_t* int64Value = std::get_if<int64_t>(&scaleIt->second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700447 if (int64Value != nullptr)
448 {
449 scaleMultiplier = *int64Value;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100450 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100451 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700452
453 sensor_json["MemberId"] = sensorName;
454 sensor_json["Name"] = sensorName;
455 sensor_json["Status"]["State"] = "Enabled";
456 sensor_json["Status"]["Health"] = "OK";
457
458 // Parameter to set to override the type we get from dbus, and force it to
459 // int, regardless of what is available. This is used for schemas like fan,
460 // that require integers, not floats.
461 bool forceToInt = false;
462
463 const char* unit = "Reading";
464 if (sensorType == "temperature")
465 {
466 unit = "ReadingCelsius";
467 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Temperature";
468 // TODO(ed) Documentation says that path should be type fan_tach,
469 // implementation seems to implement fan
470 }
471 else if (sensorType == "fan" || sensorType == "fan_tach")
472 {
473 unit = "Reading";
474 sensor_json["ReadingUnits"] = "RPM";
475 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
476 forceToInt = true;
477 }
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700478 else if (sensorType == "fan_pwm")
479 {
480 unit = "Reading";
481 sensor_json["ReadingUnits"] = "Percent";
482 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
483 forceToInt = true;
484 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700485 else if (sensorType == "voltage")
486 {
487 unit = "ReadingVolts";
488 sensor_json["@odata.type"] = "#Power.v1_0_0.Voltage";
489 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700490 else if (sensorType == "power")
491 {
492 unit = "LastPowerOutputWatts";
493 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700494 else
495 {
496 BMCWEB_LOG_ERROR << "Redfish cannot map object type for " << sensorName;
497 return;
498 }
499 // Map of dbus interface name, dbus property name and redfish property_name
500 std::vector<std::tuple<const char*, const char*, const char*>> properties;
501 properties.reserve(7);
502
503 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600504
505 // If sensor type doesn't map to Redfish PowerSupply, add threshold props
506 if ((sensorType != "current") && (sensorType != "power"))
507 {
508 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
509 "WarningHigh", "UpperThresholdNonCritical");
510 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
511 "WarningLow", "LowerThresholdNonCritical");
512 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
513 "CriticalHigh", "UpperThresholdCritical");
514 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
515 "CriticalLow", "LowerThresholdCritical");
516 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700517
Ed Tanous2474adf2018-09-05 16:31:16 -0700518 // TODO Need to get UpperThresholdFatal and LowerThresholdFatal
519
Ed Tanous1abe55e2018-09-05 08:30:59 -0700520 if (sensorType == "temperature")
521 {
522 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
523 "MinReadingRangeTemp");
524 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
525 "MaxReadingRangeTemp");
526 }
Shawn McCarneyde629b62019-03-08 10:42:51 -0600527 else if ((sensorType != "current") && (sensorType != "power"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700528 {
Shawn McCarneyde629b62019-03-08 10:42:51 -0600529 // Sensor type doesn't map to Redfish PowerSupply; add min/max props
Ed Tanous1abe55e2018-09-05 08:30:59 -0700530 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
531 "MinReadingRange");
532 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
533 "MaxReadingRange");
534 }
535
536 for (const std::tuple<const char*, const char*, const char*>& p :
537 properties)
538 {
539 auto interfaceProperties = interfacesDict.find(std::get<0>(p));
540 if (interfaceProperties != interfacesDict.end())
541 {
542 auto valueIt = interfaceProperties->second.find(std::get<1>(p));
543 if (valueIt != interfaceProperties->second.end())
544 {
545 const SensorVariant& valueVariant = valueIt->second;
546 nlohmann::json& valueIt = sensor_json[std::get<2>(p)];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700547 // Attempt to pull the int64 directly
Ed Tanousabf2add2019-01-22 16:40:12 -0800548 const int64_t* int64Value = std::get_if<int64_t>(&valueVariant);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700549
Ed Tanousabf2add2019-01-22 16:40:12 -0800550 const double* doubleValue = std::get_if<double>(&valueVariant);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700551 double temp = 0.0;
552 if (int64Value != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700553 {
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700554 temp = *int64Value;
555 }
556 else if (doubleValue != nullptr)
557 {
558 temp = *doubleValue;
559 }
560 else
561 {
562 BMCWEB_LOG_ERROR
563 << "Got value interface that wasn't int or double";
564 continue;
565 }
566 temp = temp * std::pow(10, scaleMultiplier);
567 if (forceToInt)
568 {
569 valueIt = static_cast<int64_t>(temp);
570 }
571 else
572 {
573 valueIt = temp;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700574 }
575 }
576 }
577 }
578 BMCWEB_LOG_DEBUG << "Added sensor " << sensorName;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100579}
580
581/**
Shawn McCarneyde629b62019-03-08 10:42:51 -0600582 * @brief Gets the values of the specified sensors.
583 *
584 * Stores the results as JSON in the SensorsAsyncResp.
585 *
586 * Gets the sensor values asynchronously. Stores the results later when the
587 * information has been obtained.
588 *
589 * The sensorNames set contains all sensors for the current chassis.
590 * SensorsAsyncResp contains the requested sensor types. Only sensors of a
591 * requested type are included in the JSON output.
592 *
593 * To minimize the number of DBus calls, the DBus method
594 * org.freedesktop.DBus.ObjectManager.GetManagedObjects() is used to get the
595 * values of all sensors provided by a connection (service).
596 *
597 * The connections set contains all the connections that provide sensor values.
598 *
599 * The objectMgrPaths map contains mappings from a connection name to the
600 * corresponding DBus object path that implements ObjectManager.
601 *
602 * @param SensorsAsyncResp Pointer to object holding response data.
603 * @param sensorNames All sensors within the current chassis.
604 * @param connections Connections that provide sensor values.
605 * @param objectMgrPaths Mappings from connection name to DBus object path that
606 * implements ObjectManager.
607 */
608void getSensorData(
609 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
610 const boost::container::flat_set<std::string>& sensorNames,
611 const boost::container::flat_set<std::string>& connections,
612 const boost::container::flat_map<std::string, std::string>& objectMgrPaths)
613{
614 BMCWEB_LOG_DEBUG << "getSensorData enter";
615 // Get managed objects from all services exposing sensors
616 for (const std::string& connection : connections)
617 {
618 // Response handler to process managed objects
619 auto getManagedObjectsCb = [&, SensorsAsyncResp, sensorNames](
620 const boost::system::error_code ec,
621 ManagedObjectsVectorType& resp) {
622 BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter";
623 if (ec)
624 {
625 BMCWEB_LOG_ERROR << "getManagedObjectsCb DBUS error: " << ec;
626 messages::internalError(SensorsAsyncResp->res);
627 return;
628 }
629 // Go through all objects and update response with sensor data
630 for (const auto& objDictEntry : resp)
631 {
632 const std::string& objPath =
633 static_cast<const std::string&>(objDictEntry.first);
634 BMCWEB_LOG_DEBUG << "getManagedObjectsCb parsing object "
635 << objPath;
636
637 // Skip sensor if it is not one of the requested types
638 if (!isRequestedSensorType(objPath, SensorsAsyncResp))
639 {
640 continue;
641 }
642
643 std::vector<std::string> split;
644 // Reserve space for
645 // /xyz/openbmc_project/sensors/<name>/<subname>
646 split.reserve(6);
647 boost::algorithm::split(split, objPath, boost::is_any_of("/"));
648 if (split.size() < 6)
649 {
650 BMCWEB_LOG_ERROR << "Got path that isn't long enough "
651 << objPath;
652 continue;
653 }
654 // These indexes aren't intuitive, as boost::split puts an empty
655 // string at the beginning
656 const std::string& sensorType = split[4];
657 const std::string& sensorName = split[5];
658 BMCWEB_LOG_DEBUG << "sensorName " << sensorName
659 << " sensorType " << sensorType;
660 if (sensorNames.find(sensorName) == sensorNames.end())
661 {
662 BMCWEB_LOG_ERROR << sensorName << " not in sensor list ";
663 continue;
664 }
665
666 const char* fieldName = nullptr;
667 if (sensorType == "temperature")
668 {
669 fieldName = "Temperatures";
670 }
671 else if (sensorType == "fan" || sensorType == "fan_tach" ||
672 sensorType == "fan_pwm")
673 {
674 fieldName = "Fans";
675 }
676 else if (sensorType == "voltage")
677 {
678 fieldName = "Voltages";
679 }
680 else if (sensorType == "current")
681 {
682 fieldName = "PowerSupplies";
683 }
684 else if (sensorType == "power")
685 {
686 fieldName = "PowerSupplies";
687 }
688 else
689 {
690 BMCWEB_LOG_ERROR << "Unsure how to handle sensorType "
691 << sensorType;
692 continue;
693 }
694
695 nlohmann::json& tempArray =
696 SensorsAsyncResp->res.jsonValue[fieldName];
697
698 tempArray.push_back(
699 {{"@odata.id",
700 "/redfish/v1/Chassis/" + SensorsAsyncResp->chassisId +
701 "/" + SensorsAsyncResp->chassisSubNode + "#/" +
702 fieldName + "/" + std::to_string(tempArray.size())}});
703 nlohmann::json& sensorJson = tempArray.back();
704
705 objectInterfacesToJson(sensorName, sensorType,
706 objDictEntry.second, sensorJson);
707 }
708 BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit";
709 };
710
711 // Find DBus object path that implements ObjectManager for the current
712 // connection. If no mapping found, default to "/".
713 auto iter = objectMgrPaths.find(connection);
714 const std::string& objectMgrPath =
715 (iter != objectMgrPaths.end()) ? iter->second : "/";
716 BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
717 << objectMgrPath;
718
719 crow::connections::systemBus->async_method_call(
720 getManagedObjectsCb, connection, objectMgrPath,
721 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
722 };
723 BMCWEB_LOG_DEBUG << "getSensorData exit";
724}
725
726/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100727 * @brief Entry point for retrieving sensors data related to requested
728 * chassis.
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200729 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100730 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700731void getChassisData(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
732{
733 BMCWEB_LOG_DEBUG << "getChassisData enter";
734 auto getChassisCb = [&, SensorsAsyncResp](
735 boost::container::flat_set<std::string>&
736 sensorNames) {
737 BMCWEB_LOG_DEBUG << "getChassisCb enter";
738 auto getConnectionCb =
739 [&, SensorsAsyncResp, sensorNames](
740 const boost::container::flat_set<std::string>& connections) {
741 BMCWEB_LOG_DEBUG << "getConnectionCb enter";
Shawn McCarneyde629b62019-03-08 10:42:51 -0600742 auto getObjectManagerPathsCb =
743 [SensorsAsyncResp, sensorNames,
744 connections](const boost::container::flat_map<
745 std::string, std::string>& objectMgrPaths) {
746 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb enter";
747 // Get sensor data and store results in JSON response
748 getSensorData(SensorsAsyncResp, sensorNames,
749 connections, objectMgrPaths);
750 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb exit";
751 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100752
Shawn McCarneyde629b62019-03-08 10:42:51 -0600753 // Get mapping from connection names to the DBus object paths
754 // that implement the ObjectManager interface
755 getObjectManagerPaths(SensorsAsyncResp,
756 std::move(getObjectManagerPathsCb));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700757 BMCWEB_LOG_DEBUG << "getConnectionCb exit";
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200758 };
Shawn McCarneyde629b62019-03-08 10:42:51 -0600759
760 // Get set of connections that provide sensor values
Ed Tanous1abe55e2018-09-05 08:30:59 -0700761 getConnections(SensorsAsyncResp, sensorNames,
762 std::move(getConnectionCb));
763 BMCWEB_LOG_DEBUG << "getChassisCb exit";
764 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100765
Shawn McCarneyde629b62019-03-08 10:42:51 -0600766#ifdef BMCWEB_ENABLE_REDFISH_ONE_CHASSIS
767 // Get all sensor names
768 getAllSensors(SensorsAsyncResp, std::move(getChassisCb));
769#else
770 // Get sensor names in chassis
Ed Tanous1abe55e2018-09-05 08:30:59 -0700771 getChassis(SensorsAsyncResp, std::move(getChassisCb));
Shawn McCarneyde629b62019-03-08 10:42:51 -0600772#endif
Ed Tanous1abe55e2018-09-05 08:30:59 -0700773 BMCWEB_LOG_DEBUG << "getChassisData exit";
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100774};
775
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530776/**
777 * @brief Entry point for overriding sensor values of given sensor
778 *
779 * @param res response object
780 * @param req request object
781 * @param params parameter passed for CRUD
782 * @param typeList TypeList of sensors for the resource queried
783 * @param chassisSubNode Chassis Node for which the query has to happen
784 */
785void setSensorOverride(crow::Response& res, const crow::Request& req,
786 const std::vector<std::string>& params,
787 const std::initializer_list<const char*> typeList,
788 const std::string& chassisSubNode)
789{
790
791 // TODO: Need to figure out dynamic way to restrict patch (Set Sensor
792 // override) based on another d-bus announcement to be more generic.
793 if (params.size() != 1)
794 {
795 messages::internalError(res);
796 res.end();
797 return;
798 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530799
800 std::unordered_map<std::string, std::vector<nlohmann::json>> allCollections;
801 std::optional<std::vector<nlohmann::json>> temperatureCollections;
802 std::optional<std::vector<nlohmann::json>> fanCollections;
803 std::vector<nlohmann::json> voltageCollections;
804 BMCWEB_LOG_INFO << "setSensorOverride for subNode" << chassisSubNode
805 << "\n";
806
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530807 if (chassisSubNode == "Thermal")
808 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530809 if (!json_util::readJson(req, res, "Temperatures",
810 temperatureCollections, "Fans",
811 fanCollections))
812 {
813 return;
814 }
815 if (!temperatureCollections && !fanCollections)
816 {
817 messages::resourceNotFound(res, "Thermal",
818 "Temperatures / Voltages");
819 res.end();
820 return;
821 }
822 if (temperatureCollections)
823 {
824 allCollections.emplace("Temperatures",
825 *std::move(temperatureCollections));
826 }
827 if (fanCollections)
828 {
829 allCollections.emplace("Fans", *std::move(fanCollections));
830 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530831 }
832 else if (chassisSubNode == "Power")
833 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530834 if (!json_util::readJson(req, res, "Voltages", voltageCollections))
835 {
836 return;
837 }
838 allCollections.emplace("Voltages", std::move(voltageCollections));
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530839 }
840 else
841 {
842 res.result(boost::beast::http::status::not_found);
843 res.end();
844 return;
845 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530846
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530847 const char* propertyValueName;
848 std::unordered_map<std::string, std::pair<double, std::string>> overrideMap;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530849 std::string memberId;
850 double value;
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530851 for (auto& collectionItems : allCollections)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530852 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530853 if (collectionItems.first == "Temperatures")
854 {
855 propertyValueName = "ReadingCelsius";
856 }
857 else if (collectionItems.first == "Fans")
858 {
859 propertyValueName = "Reading";
860 }
861 else
862 {
863 propertyValueName = "ReadingVolts";
864 }
865 for (auto& item : collectionItems.second)
866 {
867 if (!json_util::readJson(item, res, "MemberId", memberId,
868 propertyValueName, value))
869 {
870 return;
871 }
872 overrideMap.emplace(memberId,
873 std::make_pair(value, collectionItems.first));
874 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530875 }
876 const std::string& chassisName = params[0];
877 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
878 res, chassisName, typeList, chassisSubNode);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530879 // first check for valid chassis id & sensor in requested chassis.
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530880 auto getChassisSensorListCb = [sensorAsyncResp, overrideMap](
881 const boost::container::flat_set<
882 std::string>& sensorLists) {
883 boost::container::flat_set<std::string> sensorNames;
884 for (const auto& item : overrideMap)
885 {
886 const auto& sensor = item.first;
887 if (sensorLists.find(item.first) == sensorLists.end())
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530888 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530889 BMCWEB_LOG_INFO << "Unable to find memberId " << item.first;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530890 messages::resourceNotFound(sensorAsyncResp->res,
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530891 item.second.second, item.first);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530892 return;
893 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530894 sensorNames.emplace(sensor);
895 }
896 // Get the connection to which the memberId belongs
897 auto getObjectsWithConnectionCb =
898 [sensorAsyncResp, overrideMap](
899 const boost::container::flat_set<std::string>& connections,
900 const std::set<std::pair<std::string, std::string>>&
901 objectsWithConnection) {
902 if (objectsWithConnection.size() != overrideMap.size())
903 {
904 BMCWEB_LOG_INFO
905 << "Unable to find all objects with proper connection "
906 << objectsWithConnection.size() << " requested "
907 << overrideMap.size() << "\n";
908 messages::resourceNotFound(
909 sensorAsyncResp->res,
910 sensorAsyncResp->chassisSubNode == "Thermal"
911 ? "Temperatures"
912 : "Voltages",
913 "Count");
914 return;
915 }
916 for (const auto& item : objectsWithConnection)
917 {
918
919 auto lastPos = item.first.rfind('/');
920 if (lastPos == std::string::npos)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530921 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530922 messages::internalError(sensorAsyncResp->res);
923 return;
924 }
925 std::string sensorName = item.first.substr(lastPos + 1);
926
927 const auto& iterator = overrideMap.find(sensorName);
928 if (iterator == overrideMap.end())
929 {
930 BMCWEB_LOG_INFO << "Unable to find sensor object"
931 << item.first << "\n";
932 messages::internalError(sensorAsyncResp->res);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530933 return;
934 }
935 crow::connections::systemBus->async_method_call(
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530936 [sensorAsyncResp](const boost::system::error_code ec) {
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530937 if (ec)
938 {
939 BMCWEB_LOG_DEBUG
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530940 << "setOverrideValueStatus DBUS error: "
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530941 << ec;
942 messages::internalError(sensorAsyncResp->res);
943 return;
944 }
945 },
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530946 item.second, item.first,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530947 "org.freedesktop.DBus.Properties", "Set",
948 "xyz.openbmc_project.Sensor.Value", "Value",
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530949 sdbusplus::message::variant<double>(
950 iterator->second.first));
951 }
952 };
953 // Get object with connection for the given sensor name
954 getObjectsWithConnection(sensorAsyncResp, sensorNames,
955 std::move(getObjectsWithConnectionCb));
956 };
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530957 // get full sensor list for the given chassisId and cross verify the sensor.
958 getChassis(sensorAsyncResp, std::move(getChassisSensorListCb));
959}
960
Ed Tanous1abe55e2018-09-05 08:30:59 -0700961} // namespace redfish