blob: d3c858f480a64497a63e6aa16aa32b3c9f5f8484 [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 }
Ed Tanouse278c182019-03-13 16:23:37 -0700591 for (const std::pair<std::string,
592 std::vector<std::pair<
593 std::string, std::vector<std::string>>>>&
594 pathPair : resp)
James Feist8bd25cc2019-03-15 15:14:00 -0700595 {
Ed Tanouse278c182019-03-13 16:23:37 -0700596 const std::string& path = pathPair.first;
597 const std::vector<
598 std::pair<std::string, std::vector<std::string>>>& objDict =
599 pathPair.second;
James Feist8bd25cc2019-03-15 15:14:00 -0700600 if (objDict.empty())
601 {
602 continue; // this should be impossible
603 }
604
605 const std::string& owner = objDict.begin()->first;
606 crow::connections::systemBus->async_method_call(
607 [path, owner,
608 sensorsAsyncResp](const boost::system::error_code ec,
609 std::variant<std::vector<std::string>>
610 variantEndpoints) {
611 if (ec)
612 {
613 return; // if they don't have an association we
614 // can't tell what chassis is
615 }
616 // verify part of the right chassis
617 auto endpoints = std::get_if<std::vector<std::string>>(
618 &variantEndpoints);
619
620 if (endpoints == nullptr)
621 {
622 BMCWEB_LOG_ERROR << "Invalid association interface";
623 messages::internalError(sensorsAsyncResp->res);
624 return;
625 }
626
627 auto found = std::find_if(
628 endpoints->begin(), endpoints->end(),
629 [sensorsAsyncResp](const std::string& entry) {
630 return entry.find(
631 sensorsAsyncResp->chassisId) !=
632 std::string::npos;
633 });
634
635 if (found == endpoints->end())
636 {
637 return;
638 }
639 crow::connections::systemBus->async_method_call(
640 [path, sensorsAsyncResp](
641 const boost::system::error_code ec,
642 const boost::container::flat_map<
643 std::string,
644 std::variant<uint8_t,
645 std::vector<std::string>,
646 std::string>>& ret) {
647 if (ec)
648 {
649 return; // don't have to have this
650 // interface
651 }
652 auto findFailures = ret.find("AllowedFailures");
653 auto findCollection = ret.find("Collection");
654 auto findStatus = ret.find("Status");
655
656 if (findFailures == ret.end() ||
657 findCollection == ret.end() ||
658 findStatus == ret.end())
659 {
660 BMCWEB_LOG_ERROR
661 << "Invalid redundancy interface";
662 messages::internalError(
663 sensorsAsyncResp->res);
664 return;
665 }
666
667 auto allowedFailures = std::get_if<uint8_t>(
668 &(findFailures->second));
669 auto collection =
670 std::get_if<std::vector<std::string>>(
671 &(findCollection->second));
672 auto status = std::get_if<std::string>(
673 &(findStatus->second));
674
675 if (allowedFailures == nullptr ||
676 collection == nullptr || status == nullptr)
677 {
678
679 BMCWEB_LOG_ERROR
680 << "Invalid redundancy interface "
681 "types";
682 messages::internalError(
683 sensorsAsyncResp->res);
684 return;
685 }
686 size_t lastSlash = path.rfind("/");
687 if (lastSlash == std::string::npos)
688 {
689 // this should be impossible
690 messages::internalError(
691 sensorsAsyncResp->res);
692 return;
693 }
694 std::string name = path.substr(lastSlash + 1);
695 std::replace(name.begin(), name.end(), '_',
696 ' ');
697
698 std::string health;
699
700 if (boost::ends_with(*status, "Full"))
701 {
702 health = "OK";
703 }
704 else if (boost::ends_with(*status, "Degraded"))
705 {
706 health = "Warning";
707 }
708 else
709 {
710 health = "Critical";
711 }
712 std::vector<nlohmann::json> redfishCollection;
713 const auto& fanRedfish =
714 sensorsAsyncResp->res.jsonValue["Fans"];
715 for (const std::string& item : *collection)
716 {
717 lastSlash = item.rfind("/");
718 // make a copy as collection is const
719 std::string itemName =
720 item.substr(lastSlash + 1);
721 /*
722 todo(ed): merge patch that fixes the names
723 std::replace(itemName.begin(),
724 itemName.end(), '_', ' ');*/
725 auto schemaItem = std::find_if(
726 fanRedfish.begin(), fanRedfish.end(),
727 [itemName](const nlohmann::json& fan) {
728 return fan["MemberId"] == itemName;
729 });
730 if (schemaItem != fanRedfish.end())
731 {
732 redfishCollection.push_back(
733 {{"@odata.id",
734 (*schemaItem)["@odata.id"]}});
735 }
736 else
737 {
738 BMCWEB_LOG_ERROR
739 << "failed to find fan in schema";
740 messages::internalError(
741 sensorsAsyncResp->res);
742 return;
743 }
744 }
745
746 auto& resp = sensorsAsyncResp->res
747 .jsonValue["Redundancy"];
748 resp.push_back(
749 {{"@odata.id",
750 "/refish/v1/Chassis/" +
751 sensorsAsyncResp->chassisId + "/" +
752 sensorsAsyncResp->chassisSubNode +
753 "#/Redundancy/" +
754 std::to_string(resp.size())},
755 {"@odata.type",
756 "#Redundancy.v1_3_2.Redundancy"},
757 {"MinNumNeeded",
758 collection->size() - *allowedFailures},
759 {"MemberId", name},
760 {"Mode", "N+m"},
761 {"Name", name},
762 {"RedundancySet", redfishCollection},
763 {"Status",
764 {{"Health", health},
765 {"State", "Enabled"}}}});
766 },
767 owner, path, "org.freedesktop.DBus.Properties",
768 "GetAll",
769 "xyz.openbmc_project.Control.FanRedundancy");
770 },
771 "xyz.openbmc_project.ObjectMapper", path + "/inventory",
772 "org.freedesktop.DBus.Properties", "Get",
773 "xyz.openbmc_project.Association", "endpoints");
774 }
775 },
776 "xyz.openbmc_project.ObjectMapper",
777 "/xyz/openbmc_project/object_mapper",
778 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
779 "/xyz/openbmc_project/control", 2,
780 std::array<const char*, 1>{
781 "xyz.openbmc_project.Control.FanRedundancy"});
782}
783
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100784/**
Shawn McCarneyde629b62019-03-08 10:42:51 -0600785 * @brief Gets the values of the specified sensors.
786 *
787 * Stores the results as JSON in the SensorsAsyncResp.
788 *
789 * Gets the sensor values asynchronously. Stores the results later when the
790 * information has been obtained.
791 *
792 * The sensorNames set contains all sensors for the current chassis.
793 * SensorsAsyncResp contains the requested sensor types. Only sensors of a
794 * requested type are included in the JSON output.
795 *
796 * To minimize the number of DBus calls, the DBus method
797 * org.freedesktop.DBus.ObjectManager.GetManagedObjects() is used to get the
798 * values of all sensors provided by a connection (service).
799 *
800 * The connections set contains all the connections that provide sensor values.
801 *
802 * The objectMgrPaths map contains mappings from a connection name to the
803 * corresponding DBus object path that implements ObjectManager.
804 *
805 * @param SensorsAsyncResp Pointer to object holding response data.
806 * @param sensorNames All sensors within the current chassis.
807 * @param connections Connections that provide sensor values.
808 * @param objectMgrPaths Mappings from connection name to DBus object path that
809 * implements ObjectManager.
810 */
811void getSensorData(
812 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
813 const boost::container::flat_set<std::string>& sensorNames,
814 const boost::container::flat_set<std::string>& connections,
815 const boost::container::flat_map<std::string, std::string>& objectMgrPaths)
816{
817 BMCWEB_LOG_DEBUG << "getSensorData enter";
818 // Get managed objects from all services exposing sensors
819 for (const std::string& connection : connections)
820 {
821 // Response handler to process managed objects
822 auto getManagedObjectsCb = [&, SensorsAsyncResp, sensorNames](
823 const boost::system::error_code ec,
824 ManagedObjectsVectorType& resp) {
825 BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter";
826 if (ec)
827 {
828 BMCWEB_LOG_ERROR << "getManagedObjectsCb DBUS error: " << ec;
829 messages::internalError(SensorsAsyncResp->res);
830 return;
831 }
832 // Go through all objects and update response with sensor data
833 for (const auto& objDictEntry : resp)
834 {
835 const std::string& objPath =
836 static_cast<const std::string&>(objDictEntry.first);
837 BMCWEB_LOG_DEBUG << "getManagedObjectsCb parsing object "
838 << objPath;
839
840 // Skip sensor if it is not one of the requested types
841 if (!isRequestedSensorType(objPath, SensorsAsyncResp))
842 {
843 continue;
844 }
845
846 std::vector<std::string> split;
847 // Reserve space for
848 // /xyz/openbmc_project/sensors/<name>/<subname>
849 split.reserve(6);
850 boost::algorithm::split(split, objPath, boost::is_any_of("/"));
851 if (split.size() < 6)
852 {
853 BMCWEB_LOG_ERROR << "Got path that isn't long enough "
854 << objPath;
855 continue;
856 }
857 // These indexes aren't intuitive, as boost::split puts an empty
858 // string at the beginning
859 const std::string& sensorType = split[4];
860 const std::string& sensorName = split[5];
861 BMCWEB_LOG_DEBUG << "sensorName " << sensorName
862 << " sensorType " << sensorType;
863 if (sensorNames.find(sensorName) == sensorNames.end())
864 {
865 BMCWEB_LOG_ERROR << sensorName << " not in sensor list ";
866 continue;
867 }
868
869 const char* fieldName = nullptr;
870 if (sensorType == "temperature")
871 {
872 fieldName = "Temperatures";
873 }
874 else if (sensorType == "fan" || sensorType == "fan_tach" ||
875 sensorType == "fan_pwm")
876 {
877 fieldName = "Fans";
878 }
879 else if (sensorType == "voltage")
880 {
881 fieldName = "Voltages";
882 }
883 else if (sensorType == "current")
884 {
885 fieldName = "PowerSupplies";
886 }
887 else if (sensorType == "power")
888 {
889 fieldName = "PowerSupplies";
890 }
891 else
892 {
893 BMCWEB_LOG_ERROR << "Unsure how to handle sensorType "
894 << sensorType;
895 continue;
896 }
897
898 nlohmann::json& tempArray =
899 SensorsAsyncResp->res.jsonValue[fieldName];
900
901 tempArray.push_back(
902 {{"@odata.id",
903 "/redfish/v1/Chassis/" + SensorsAsyncResp->chassisId +
904 "/" + SensorsAsyncResp->chassisSubNode + "#/" +
905 fieldName + "/" + std::to_string(tempArray.size())}});
906 nlohmann::json& sensorJson = tempArray.back();
907
908 objectInterfacesToJson(sensorName, sensorType,
909 objDictEntry.second, sensorJson);
910 }
James Feist8bd25cc2019-03-15 15:14:00 -0700911 if (SensorsAsyncResp.use_count() == 1 &&
912 SensorsAsyncResp->chassisSubNode == "Thermal")
913 {
914 populateFanRedundancy(SensorsAsyncResp);
915 }
Shawn McCarneyde629b62019-03-08 10:42:51 -0600916 BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit";
917 };
918
919 // Find DBus object path that implements ObjectManager for the current
920 // connection. If no mapping found, default to "/".
921 auto iter = objectMgrPaths.find(connection);
922 const std::string& objectMgrPath =
923 (iter != objectMgrPaths.end()) ? iter->second : "/";
924 BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
925 << objectMgrPath;
926
927 crow::connections::systemBus->async_method_call(
928 getManagedObjectsCb, connection, objectMgrPath,
929 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
930 };
931 BMCWEB_LOG_DEBUG << "getSensorData exit";
932}
933
934/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100935 * @brief Entry point for retrieving sensors data related to requested
936 * chassis.
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200937 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100938 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700939void getChassisData(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
940{
941 BMCWEB_LOG_DEBUG << "getChassisData enter";
942 auto getChassisCb = [&, SensorsAsyncResp](
943 boost::container::flat_set<std::string>&
944 sensorNames) {
945 BMCWEB_LOG_DEBUG << "getChassisCb enter";
946 auto getConnectionCb =
947 [&, SensorsAsyncResp, sensorNames](
948 const boost::container::flat_set<std::string>& connections) {
949 BMCWEB_LOG_DEBUG << "getConnectionCb enter";
Shawn McCarneyde629b62019-03-08 10:42:51 -0600950 auto getObjectManagerPathsCb =
951 [SensorsAsyncResp, sensorNames,
952 connections](const boost::container::flat_map<
953 std::string, std::string>& objectMgrPaths) {
954 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb enter";
955 // Get sensor data and store results in JSON response
956 getSensorData(SensorsAsyncResp, sensorNames,
957 connections, objectMgrPaths);
958 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb exit";
959 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100960
Shawn McCarneyde629b62019-03-08 10:42:51 -0600961 // Get mapping from connection names to the DBus object paths
962 // that implement the ObjectManager interface
963 getObjectManagerPaths(SensorsAsyncResp,
964 std::move(getObjectManagerPathsCb));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700965 BMCWEB_LOG_DEBUG << "getConnectionCb exit";
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200966 };
Shawn McCarneyde629b62019-03-08 10:42:51 -0600967
968 // Get set of connections that provide sensor values
Ed Tanous1abe55e2018-09-05 08:30:59 -0700969 getConnections(SensorsAsyncResp, sensorNames,
970 std::move(getConnectionCb));
971 BMCWEB_LOG_DEBUG << "getChassisCb exit";
972 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100973
Shawn McCarneyde629b62019-03-08 10:42:51 -0600974#ifdef BMCWEB_ENABLE_REDFISH_ONE_CHASSIS
975 // Get all sensor names
976 getAllSensors(SensorsAsyncResp, std::move(getChassisCb));
977#else
978 // Get sensor names in chassis
Ed Tanous1abe55e2018-09-05 08:30:59 -0700979 getChassis(SensorsAsyncResp, std::move(getChassisCb));
Shawn McCarneyde629b62019-03-08 10:42:51 -0600980#endif
Ed Tanous1abe55e2018-09-05 08:30:59 -0700981 BMCWEB_LOG_DEBUG << "getChassisData exit";
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100982};
983
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530984/**
985 * @brief Entry point for overriding sensor values of given sensor
986 *
987 * @param res response object
988 * @param req request object
989 * @param params parameter passed for CRUD
990 * @param typeList TypeList of sensors for the resource queried
991 * @param chassisSubNode Chassis Node for which the query has to happen
992 */
993void setSensorOverride(crow::Response& res, const crow::Request& req,
994 const std::vector<std::string>& params,
Ed Tanousb01bf292019-03-25 19:25:26 +0000995 const std::initializer_list<const char*> typeList,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530996 const std::string& chassisSubNode)
997{
998
999 // TODO: Need to figure out dynamic way to restrict patch (Set Sensor
1000 // override) based on another d-bus announcement to be more generic.
1001 if (params.size() != 1)
1002 {
1003 messages::internalError(res);
1004 res.end();
1005 return;
1006 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301007
1008 std::unordered_map<std::string, std::vector<nlohmann::json>> allCollections;
1009 std::optional<std::vector<nlohmann::json>> temperatureCollections;
1010 std::optional<std::vector<nlohmann::json>> fanCollections;
1011 std::vector<nlohmann::json> voltageCollections;
1012 BMCWEB_LOG_INFO << "setSensorOverride for subNode" << chassisSubNode
1013 << "\n";
1014
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301015 if (chassisSubNode == "Thermal")
1016 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301017 if (!json_util::readJson(req, res, "Temperatures",
1018 temperatureCollections, "Fans",
1019 fanCollections))
1020 {
1021 return;
1022 }
1023 if (!temperatureCollections && !fanCollections)
1024 {
1025 messages::resourceNotFound(res, "Thermal",
1026 "Temperatures / Voltages");
1027 res.end();
1028 return;
1029 }
1030 if (temperatureCollections)
1031 {
1032 allCollections.emplace("Temperatures",
1033 *std::move(temperatureCollections));
1034 }
1035 if (fanCollections)
1036 {
1037 allCollections.emplace("Fans", *std::move(fanCollections));
1038 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301039 }
1040 else if (chassisSubNode == "Power")
1041 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301042 if (!json_util::readJson(req, res, "Voltages", voltageCollections))
1043 {
1044 return;
1045 }
1046 allCollections.emplace("Voltages", std::move(voltageCollections));
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301047 }
1048 else
1049 {
1050 res.result(boost::beast::http::status::not_found);
1051 res.end();
1052 return;
1053 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301054
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301055 const char* propertyValueName;
1056 std::unordered_map<std::string, std::pair<double, std::string>> overrideMap;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301057 std::string memberId;
1058 double value;
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301059 for (auto& collectionItems : allCollections)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301060 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301061 if (collectionItems.first == "Temperatures")
1062 {
1063 propertyValueName = "ReadingCelsius";
1064 }
1065 else if (collectionItems.first == "Fans")
1066 {
1067 propertyValueName = "Reading";
1068 }
1069 else
1070 {
1071 propertyValueName = "ReadingVolts";
1072 }
1073 for (auto& item : collectionItems.second)
1074 {
1075 if (!json_util::readJson(item, res, "MemberId", memberId,
1076 propertyValueName, value))
1077 {
1078 return;
1079 }
1080 overrideMap.emplace(memberId,
1081 std::make_pair(value, collectionItems.first));
1082 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301083 }
1084 const std::string& chassisName = params[0];
1085 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
1086 res, chassisName, typeList, chassisSubNode);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301087 // first check for valid chassis id & sensor in requested chassis.
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301088 auto getChassisSensorListCb = [sensorAsyncResp, overrideMap](
1089 const boost::container::flat_set<
1090 std::string>& sensorLists) {
1091 boost::container::flat_set<std::string> sensorNames;
1092 for (const auto& item : overrideMap)
1093 {
1094 const auto& sensor = item.first;
1095 if (sensorLists.find(item.first) == sensorLists.end())
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301096 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301097 BMCWEB_LOG_INFO << "Unable to find memberId " << item.first;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301098 messages::resourceNotFound(sensorAsyncResp->res,
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301099 item.second.second, item.first);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301100 return;
1101 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301102 sensorNames.emplace(sensor);
1103 }
1104 // Get the connection to which the memberId belongs
1105 auto getObjectsWithConnectionCb =
1106 [sensorAsyncResp, overrideMap](
1107 const boost::container::flat_set<std::string>& connections,
1108 const std::set<std::pair<std::string, std::string>>&
1109 objectsWithConnection) {
1110 if (objectsWithConnection.size() != overrideMap.size())
1111 {
1112 BMCWEB_LOG_INFO
1113 << "Unable to find all objects with proper connection "
1114 << objectsWithConnection.size() << " requested "
1115 << overrideMap.size() << "\n";
1116 messages::resourceNotFound(
1117 sensorAsyncResp->res,
1118 sensorAsyncResp->chassisSubNode == "Thermal"
1119 ? "Temperatures"
1120 : "Voltages",
1121 "Count");
1122 return;
1123 }
1124 for (const auto& item : objectsWithConnection)
1125 {
1126
1127 auto lastPos = item.first.rfind('/');
1128 if (lastPos == std::string::npos)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301129 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301130 messages::internalError(sensorAsyncResp->res);
1131 return;
1132 }
1133 std::string sensorName = item.first.substr(lastPos + 1);
1134
1135 const auto& iterator = overrideMap.find(sensorName);
1136 if (iterator == overrideMap.end())
1137 {
1138 BMCWEB_LOG_INFO << "Unable to find sensor object"
1139 << item.first << "\n";
1140 messages::internalError(sensorAsyncResp->res);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301141 return;
1142 }
1143 crow::connections::systemBus->async_method_call(
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301144 [sensorAsyncResp](const boost::system::error_code ec) {
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301145 if (ec)
1146 {
1147 BMCWEB_LOG_DEBUG
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301148 << "setOverrideValueStatus DBUS error: "
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301149 << ec;
1150 messages::internalError(sensorAsyncResp->res);
1151 return;
1152 }
1153 },
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301154 item.second, item.first,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301155 "org.freedesktop.DBus.Properties", "Set",
1156 "xyz.openbmc_project.Sensor.Value", "Value",
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301157 sdbusplus::message::variant<double>(
1158 iterator->second.first));
1159 }
1160 };
1161 // Get object with connection for the given sensor name
1162 getObjectsWithConnection(sensorAsyncResp, sensorNames,
1163 std::move(getObjectsWithConnectionCb));
1164 };
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301165 // get full sensor list for the given chassisId and cross verify the sensor.
1166 getChassis(sensorAsyncResp, std::move(getChassisSensorListCb));
1167}
1168
Ed Tanous1abe55e2018-09-05 08:30:59 -07001169} // namespace redfish