blob: bbed0479a1e954c9711aa30931ea1f16ef8263ef [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 Tanousb01bf292019-03-25 19:25:26 +000052 const std::initializer_list<const char*> types,
Ed Tanous2474adf2018-09-05 16:31:16 -070053 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 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000542 auto valueIt = interfaceProperties->second.find(std::get<1>(p));
543 if (valueIt != interfaceProperties->second.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700544 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000545 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 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000569 valueIt = static_cast<int64_t>(temp);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700570 }
571 else
572 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000573 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
James Feist8bd25cc2019-03-15 15:14:00 -0700581static void
582 populateFanRedundancy(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp)
583{
584 crow::connections::systemBus->async_method_call(
585 [sensorsAsyncResp](const boost::system::error_code ec,
586 const GetSubTreeType& resp) {
587 if (ec)
588 {
589 return; // don't have to have this interface
590 }
591 for (const auto& [path, objDict] : resp)
592 {
593 if (objDict.empty())
594 {
595 continue; // this should be impossible
596 }
597
598 const std::string& owner = objDict.begin()->first;
599 crow::connections::systemBus->async_method_call(
600 [path, owner,
601 sensorsAsyncResp](const boost::system::error_code ec,
602 std::variant<std::vector<std::string>>
603 variantEndpoints) {
604 if (ec)
605 {
606 return; // if they don't have an association we
607 // can't tell what chassis is
608 }
609 // verify part of the right chassis
610 auto endpoints = std::get_if<std::vector<std::string>>(
611 &variantEndpoints);
612
613 if (endpoints == nullptr)
614 {
615 BMCWEB_LOG_ERROR << "Invalid association interface";
616 messages::internalError(sensorsAsyncResp->res);
617 return;
618 }
619
620 auto found = std::find_if(
621 endpoints->begin(), endpoints->end(),
622 [sensorsAsyncResp](const std::string& entry) {
623 return entry.find(
624 sensorsAsyncResp->chassisId) !=
625 std::string::npos;
626 });
627
628 if (found == endpoints->end())
629 {
630 return;
631 }
632 crow::connections::systemBus->async_method_call(
633 [path, sensorsAsyncResp](
634 const boost::system::error_code ec,
635 const boost::container::flat_map<
636 std::string,
637 std::variant<uint8_t,
638 std::vector<std::string>,
639 std::string>>& ret) {
640 if (ec)
641 {
642 return; // don't have to have this
643 // interface
644 }
645 auto findFailures = ret.find("AllowedFailures");
646 auto findCollection = ret.find("Collection");
647 auto findStatus = ret.find("Status");
648
649 if (findFailures == ret.end() ||
650 findCollection == ret.end() ||
651 findStatus == ret.end())
652 {
653 BMCWEB_LOG_ERROR
654 << "Invalid redundancy interface";
655 messages::internalError(
656 sensorsAsyncResp->res);
657 return;
658 }
659
660 auto allowedFailures = std::get_if<uint8_t>(
661 &(findFailures->second));
662 auto collection =
663 std::get_if<std::vector<std::string>>(
664 &(findCollection->second));
665 auto status = std::get_if<std::string>(
666 &(findStatus->second));
667
668 if (allowedFailures == nullptr ||
669 collection == nullptr || status == nullptr)
670 {
671
672 BMCWEB_LOG_ERROR
673 << "Invalid redundancy interface "
674 "types";
675 messages::internalError(
676 sensorsAsyncResp->res);
677 return;
678 }
679 size_t lastSlash = path.rfind("/");
680 if (lastSlash == std::string::npos)
681 {
682 // this should be impossible
683 messages::internalError(
684 sensorsAsyncResp->res);
685 return;
686 }
687 std::string name = path.substr(lastSlash + 1);
688 std::replace(name.begin(), name.end(), '_',
689 ' ');
690
691 std::string health;
692
693 if (boost::ends_with(*status, "Full"))
694 {
695 health = "OK";
696 }
697 else if (boost::ends_with(*status, "Degraded"))
698 {
699 health = "Warning";
700 }
701 else
702 {
703 health = "Critical";
704 }
705 std::vector<nlohmann::json> redfishCollection;
706 const auto& fanRedfish =
707 sensorsAsyncResp->res.jsonValue["Fans"];
708 for (const std::string& item : *collection)
709 {
710 lastSlash = item.rfind("/");
711 // make a copy as collection is const
712 std::string itemName =
713 item.substr(lastSlash + 1);
714 /*
715 todo(ed): merge patch that fixes the names
716 std::replace(itemName.begin(),
717 itemName.end(), '_', ' ');*/
718 auto schemaItem = std::find_if(
719 fanRedfish.begin(), fanRedfish.end(),
720 [itemName](const nlohmann::json& fan) {
721 return fan["MemberId"] == itemName;
722 });
723 if (schemaItem != fanRedfish.end())
724 {
725 redfishCollection.push_back(
726 {{"@odata.id",
727 (*schemaItem)["@odata.id"]}});
728 }
729 else
730 {
731 BMCWEB_LOG_ERROR
732 << "failed to find fan in schema";
733 messages::internalError(
734 sensorsAsyncResp->res);
735 return;
736 }
737 }
738
739 auto& resp = sensorsAsyncResp->res
740 .jsonValue["Redundancy"];
741 resp.push_back(
742 {{"@odata.id",
743 "/refish/v1/Chassis/" +
744 sensorsAsyncResp->chassisId + "/" +
745 sensorsAsyncResp->chassisSubNode +
746 "#/Redundancy/" +
747 std::to_string(resp.size())},
748 {"@odata.type",
749 "#Redundancy.v1_3_2.Redundancy"},
750 {"MinNumNeeded",
751 collection->size() - *allowedFailures},
752 {"MemberId", name},
753 {"Mode", "N+m"},
754 {"Name", name},
755 {"RedundancySet", redfishCollection},
756 {"Status",
757 {{"Health", health},
758 {"State", "Enabled"}}}});
759 },
760 owner, path, "org.freedesktop.DBus.Properties",
761 "GetAll",
762 "xyz.openbmc_project.Control.FanRedundancy");
763 },
764 "xyz.openbmc_project.ObjectMapper", path + "/inventory",
765 "org.freedesktop.DBus.Properties", "Get",
766 "xyz.openbmc_project.Association", "endpoints");
767 }
768 },
769 "xyz.openbmc_project.ObjectMapper",
770 "/xyz/openbmc_project/object_mapper",
771 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
772 "/xyz/openbmc_project/control", 2,
773 std::array<const char*, 1>{
774 "xyz.openbmc_project.Control.FanRedundancy"});
775}
776
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100777/**
Shawn McCarneyde629b62019-03-08 10:42:51 -0600778 * @brief Gets the values of the specified sensors.
779 *
780 * Stores the results as JSON in the SensorsAsyncResp.
781 *
782 * Gets the sensor values asynchronously. Stores the results later when the
783 * information has been obtained.
784 *
785 * The sensorNames set contains all sensors for the current chassis.
786 * SensorsAsyncResp contains the requested sensor types. Only sensors of a
787 * requested type are included in the JSON output.
788 *
789 * To minimize the number of DBus calls, the DBus method
790 * org.freedesktop.DBus.ObjectManager.GetManagedObjects() is used to get the
791 * values of all sensors provided by a connection (service).
792 *
793 * The connections set contains all the connections that provide sensor values.
794 *
795 * The objectMgrPaths map contains mappings from a connection name to the
796 * corresponding DBus object path that implements ObjectManager.
797 *
798 * @param SensorsAsyncResp Pointer to object holding response data.
799 * @param sensorNames All sensors within the current chassis.
800 * @param connections Connections that provide sensor values.
801 * @param objectMgrPaths Mappings from connection name to DBus object path that
802 * implements ObjectManager.
803 */
804void getSensorData(
805 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
806 const boost::container::flat_set<std::string>& sensorNames,
807 const boost::container::flat_set<std::string>& connections,
808 const boost::container::flat_map<std::string, std::string>& objectMgrPaths)
809{
810 BMCWEB_LOG_DEBUG << "getSensorData enter";
811 // Get managed objects from all services exposing sensors
812 for (const std::string& connection : connections)
813 {
814 // Response handler to process managed objects
815 auto getManagedObjectsCb = [&, SensorsAsyncResp, sensorNames](
816 const boost::system::error_code ec,
817 ManagedObjectsVectorType& resp) {
818 BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter";
819 if (ec)
820 {
821 BMCWEB_LOG_ERROR << "getManagedObjectsCb DBUS error: " << ec;
822 messages::internalError(SensorsAsyncResp->res);
823 return;
824 }
825 // Go through all objects and update response with sensor data
826 for (const auto& objDictEntry : resp)
827 {
828 const std::string& objPath =
829 static_cast<const std::string&>(objDictEntry.first);
830 BMCWEB_LOG_DEBUG << "getManagedObjectsCb parsing object "
831 << objPath;
832
833 // Skip sensor if it is not one of the requested types
834 if (!isRequestedSensorType(objPath, SensorsAsyncResp))
835 {
836 continue;
837 }
838
839 std::vector<std::string> split;
840 // Reserve space for
841 // /xyz/openbmc_project/sensors/<name>/<subname>
842 split.reserve(6);
843 boost::algorithm::split(split, objPath, boost::is_any_of("/"));
844 if (split.size() < 6)
845 {
846 BMCWEB_LOG_ERROR << "Got path that isn't long enough "
847 << objPath;
848 continue;
849 }
850 // These indexes aren't intuitive, as boost::split puts an empty
851 // string at the beginning
852 const std::string& sensorType = split[4];
853 const std::string& sensorName = split[5];
854 BMCWEB_LOG_DEBUG << "sensorName " << sensorName
855 << " sensorType " << sensorType;
856 if (sensorNames.find(sensorName) == sensorNames.end())
857 {
858 BMCWEB_LOG_ERROR << sensorName << " not in sensor list ";
859 continue;
860 }
861
862 const char* fieldName = nullptr;
863 if (sensorType == "temperature")
864 {
865 fieldName = "Temperatures";
866 }
867 else if (sensorType == "fan" || sensorType == "fan_tach" ||
868 sensorType == "fan_pwm")
869 {
870 fieldName = "Fans";
871 }
872 else if (sensorType == "voltage")
873 {
874 fieldName = "Voltages";
875 }
876 else if (sensorType == "current")
877 {
878 fieldName = "PowerSupplies";
879 }
880 else if (sensorType == "power")
881 {
882 fieldName = "PowerSupplies";
883 }
884 else
885 {
886 BMCWEB_LOG_ERROR << "Unsure how to handle sensorType "
887 << sensorType;
888 continue;
889 }
890
891 nlohmann::json& tempArray =
892 SensorsAsyncResp->res.jsonValue[fieldName];
893
894 tempArray.push_back(
895 {{"@odata.id",
896 "/redfish/v1/Chassis/" + SensorsAsyncResp->chassisId +
897 "/" + SensorsAsyncResp->chassisSubNode + "#/" +
898 fieldName + "/" + std::to_string(tempArray.size())}});
899 nlohmann::json& sensorJson = tempArray.back();
900
901 objectInterfacesToJson(sensorName, sensorType,
902 objDictEntry.second, sensorJson);
903 }
James Feist8bd25cc2019-03-15 15:14:00 -0700904 if (SensorsAsyncResp.use_count() == 1 &&
905 SensorsAsyncResp->chassisSubNode == "Thermal")
906 {
907 populateFanRedundancy(SensorsAsyncResp);
908 }
Shawn McCarneyde629b62019-03-08 10:42:51 -0600909 BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit";
910 };
911
912 // Find DBus object path that implements ObjectManager for the current
913 // connection. If no mapping found, default to "/".
914 auto iter = objectMgrPaths.find(connection);
915 const std::string& objectMgrPath =
916 (iter != objectMgrPaths.end()) ? iter->second : "/";
917 BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
918 << objectMgrPath;
919
920 crow::connections::systemBus->async_method_call(
921 getManagedObjectsCb, connection, objectMgrPath,
922 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
923 };
924 BMCWEB_LOG_DEBUG << "getSensorData exit";
925}
926
927/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100928 * @brief Entry point for retrieving sensors data related to requested
929 * chassis.
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200930 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100931 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700932void getChassisData(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
933{
934 BMCWEB_LOG_DEBUG << "getChassisData enter";
935 auto getChassisCb = [&, SensorsAsyncResp](
936 boost::container::flat_set<std::string>&
937 sensorNames) {
938 BMCWEB_LOG_DEBUG << "getChassisCb enter";
939 auto getConnectionCb =
940 [&, SensorsAsyncResp, sensorNames](
941 const boost::container::flat_set<std::string>& connections) {
942 BMCWEB_LOG_DEBUG << "getConnectionCb enter";
Shawn McCarneyde629b62019-03-08 10:42:51 -0600943 auto getObjectManagerPathsCb =
944 [SensorsAsyncResp, sensorNames,
945 connections](const boost::container::flat_map<
946 std::string, std::string>& objectMgrPaths) {
947 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb enter";
948 // Get sensor data and store results in JSON response
949 getSensorData(SensorsAsyncResp, sensorNames,
950 connections, objectMgrPaths);
951 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb exit";
952 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100953
Shawn McCarneyde629b62019-03-08 10:42:51 -0600954 // Get mapping from connection names to the DBus object paths
955 // that implement the ObjectManager interface
956 getObjectManagerPaths(SensorsAsyncResp,
957 std::move(getObjectManagerPathsCb));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700958 BMCWEB_LOG_DEBUG << "getConnectionCb exit";
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200959 };
Shawn McCarneyde629b62019-03-08 10:42:51 -0600960
961 // Get set of connections that provide sensor values
Ed Tanous1abe55e2018-09-05 08:30:59 -0700962 getConnections(SensorsAsyncResp, sensorNames,
963 std::move(getConnectionCb));
964 BMCWEB_LOG_DEBUG << "getChassisCb exit";
965 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100966
Shawn McCarneyde629b62019-03-08 10:42:51 -0600967#ifdef BMCWEB_ENABLE_REDFISH_ONE_CHASSIS
968 // Get all sensor names
969 getAllSensors(SensorsAsyncResp, std::move(getChassisCb));
970#else
971 // Get sensor names in chassis
Ed Tanous1abe55e2018-09-05 08:30:59 -0700972 getChassis(SensorsAsyncResp, std::move(getChassisCb));
Shawn McCarneyde629b62019-03-08 10:42:51 -0600973#endif
Ed Tanous1abe55e2018-09-05 08:30:59 -0700974 BMCWEB_LOG_DEBUG << "getChassisData exit";
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100975};
976
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530977/**
978 * @brief Entry point for overriding sensor values of given sensor
979 *
980 * @param res response object
981 * @param req request object
982 * @param params parameter passed for CRUD
983 * @param typeList TypeList of sensors for the resource queried
984 * @param chassisSubNode Chassis Node for which the query has to happen
985 */
986void setSensorOverride(crow::Response& res, const crow::Request& req,
987 const std::vector<std::string>& params,
Ed Tanousb01bf292019-03-25 19:25:26 +0000988 const std::initializer_list<const char*> typeList,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530989 const std::string& chassisSubNode)
990{
991
992 // TODO: Need to figure out dynamic way to restrict patch (Set Sensor
993 // override) based on another d-bus announcement to be more generic.
994 if (params.size() != 1)
995 {
996 messages::internalError(res);
997 res.end();
998 return;
999 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301000
1001 std::unordered_map<std::string, std::vector<nlohmann::json>> allCollections;
1002 std::optional<std::vector<nlohmann::json>> temperatureCollections;
1003 std::optional<std::vector<nlohmann::json>> fanCollections;
1004 std::vector<nlohmann::json> voltageCollections;
1005 BMCWEB_LOG_INFO << "setSensorOverride for subNode" << chassisSubNode
1006 << "\n";
1007
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301008 if (chassisSubNode == "Thermal")
1009 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301010 if (!json_util::readJson(req, res, "Temperatures",
1011 temperatureCollections, "Fans",
1012 fanCollections))
1013 {
1014 return;
1015 }
1016 if (!temperatureCollections && !fanCollections)
1017 {
1018 messages::resourceNotFound(res, "Thermal",
1019 "Temperatures / Voltages");
1020 res.end();
1021 return;
1022 }
1023 if (temperatureCollections)
1024 {
1025 allCollections.emplace("Temperatures",
1026 *std::move(temperatureCollections));
1027 }
1028 if (fanCollections)
1029 {
1030 allCollections.emplace("Fans", *std::move(fanCollections));
1031 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301032 }
1033 else if (chassisSubNode == "Power")
1034 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301035 if (!json_util::readJson(req, res, "Voltages", voltageCollections))
1036 {
1037 return;
1038 }
1039 allCollections.emplace("Voltages", std::move(voltageCollections));
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301040 }
1041 else
1042 {
1043 res.result(boost::beast::http::status::not_found);
1044 res.end();
1045 return;
1046 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301047
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301048 const char* propertyValueName;
1049 std::unordered_map<std::string, std::pair<double, std::string>> overrideMap;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301050 std::string memberId;
1051 double value;
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301052 for (auto& collectionItems : allCollections)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301053 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301054 if (collectionItems.first == "Temperatures")
1055 {
1056 propertyValueName = "ReadingCelsius";
1057 }
1058 else if (collectionItems.first == "Fans")
1059 {
1060 propertyValueName = "Reading";
1061 }
1062 else
1063 {
1064 propertyValueName = "ReadingVolts";
1065 }
1066 for (auto& item : collectionItems.second)
1067 {
1068 if (!json_util::readJson(item, res, "MemberId", memberId,
1069 propertyValueName, value))
1070 {
1071 return;
1072 }
1073 overrideMap.emplace(memberId,
1074 std::make_pair(value, collectionItems.first));
1075 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301076 }
1077 const std::string& chassisName = params[0];
1078 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
1079 res, chassisName, typeList, chassisSubNode);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301080 // first check for valid chassis id & sensor in requested chassis.
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301081 auto getChassisSensorListCb = [sensorAsyncResp, overrideMap](
1082 const boost::container::flat_set<
1083 std::string>& sensorLists) {
1084 boost::container::flat_set<std::string> sensorNames;
1085 for (const auto& item : overrideMap)
1086 {
1087 const auto& sensor = item.first;
1088 if (sensorLists.find(item.first) == sensorLists.end())
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301089 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301090 BMCWEB_LOG_INFO << "Unable to find memberId " << item.first;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301091 messages::resourceNotFound(sensorAsyncResp->res,
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301092 item.second.second, item.first);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301093 return;
1094 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301095 sensorNames.emplace(sensor);
1096 }
1097 // Get the connection to which the memberId belongs
1098 auto getObjectsWithConnectionCb =
1099 [sensorAsyncResp, overrideMap](
1100 const boost::container::flat_set<std::string>& connections,
1101 const std::set<std::pair<std::string, std::string>>&
1102 objectsWithConnection) {
1103 if (objectsWithConnection.size() != overrideMap.size())
1104 {
1105 BMCWEB_LOG_INFO
1106 << "Unable to find all objects with proper connection "
1107 << objectsWithConnection.size() << " requested "
1108 << overrideMap.size() << "\n";
1109 messages::resourceNotFound(
1110 sensorAsyncResp->res,
1111 sensorAsyncResp->chassisSubNode == "Thermal"
1112 ? "Temperatures"
1113 : "Voltages",
1114 "Count");
1115 return;
1116 }
1117 for (const auto& item : objectsWithConnection)
1118 {
1119
1120 auto lastPos = item.first.rfind('/');
1121 if (lastPos == std::string::npos)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301122 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301123 messages::internalError(sensorAsyncResp->res);
1124 return;
1125 }
1126 std::string sensorName = item.first.substr(lastPos + 1);
1127
1128 const auto& iterator = overrideMap.find(sensorName);
1129 if (iterator == overrideMap.end())
1130 {
1131 BMCWEB_LOG_INFO << "Unable to find sensor object"
1132 << item.first << "\n";
1133 messages::internalError(sensorAsyncResp->res);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301134 return;
1135 }
1136 crow::connections::systemBus->async_method_call(
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301137 [sensorAsyncResp](const boost::system::error_code ec) {
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301138 if (ec)
1139 {
1140 BMCWEB_LOG_DEBUG
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301141 << "setOverrideValueStatus DBUS error: "
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301142 << ec;
1143 messages::internalError(sensorAsyncResp->res);
1144 return;
1145 }
1146 },
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301147 item.second, item.first,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301148 "org.freedesktop.DBus.Properties", "Set",
1149 "xyz.openbmc_project.Sensor.Value", "Value",
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301150 sdbusplus::message::variant<double>(
1151 iterator->second.first));
1152 }
1153 };
1154 // Get object with connection for the given sensor name
1155 getObjectsWithConnection(sensorAsyncResp, sensorNames,
1156 std::move(getObjectsWithConnectionCb));
1157 };
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301158 // get full sensor list for the given chassisId and cross verify the sensor.
1159 getChassis(sensorAsyncResp, std::move(getChassisSensorListCb));
1160}
1161
Ed Tanous1abe55e2018-09-05 08:30:59 -07001162} // namespace redfish