blob: 69ee9831845d142db0f1b4ec877c74b98e6deb4c [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 {
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010057 }
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020058
Ed Tanous1abe55e2018-09-05 08:30:59 -070059 ~SensorsAsyncResp()
60 {
61 if (res.result() == boost::beast::http::status::internal_server_error)
62 {
63 // Reset the json object to clear out any data that made it in
64 // before the error happened todo(ed) handle error condition with
65 // proper code
66 res.jsonValue = nlohmann::json::object();
67 }
68 res.end();
69 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010070
Ed Tanous1abe55e2018-09-05 08:30:59 -070071 crow::Response& res;
72 std::string chassisId{};
73 const std::vector<const char*> types;
Ed Tanous2474adf2018-09-05 16:31:16 -070074 std::string chassisSubNode{};
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010075};
76
77/**
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +053078 * @brief Get objects with connection necessary for sensors
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020079 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010080 * @param sensorNames Sensors retrieved from chassis
81 * @param callback Callback for processing gathered connections
82 */
83template <typename Callback>
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +053084void getObjectsWithConnection(
85 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -070086 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +053087 Callback&& callback)
Ed Tanous1abe55e2018-09-05 08:30:59 -070088{
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +053089 BMCWEB_LOG_DEBUG << "getObjectsWithConnection enter";
Ed Tanous1abe55e2018-09-05 08:30:59 -070090 const std::string path = "/xyz/openbmc_project/sensors";
91 const std::array<std::string, 1> interfaces = {
92 "xyz.openbmc_project.Sensor.Value"};
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010093
Ed Tanous1abe55e2018-09-05 08:30:59 -070094 // Response handler for parsing objects subtree
95 auto respHandler = [callback{std::move(callback)}, SensorsAsyncResp,
96 sensorNames](const boost::system::error_code ec,
97 const GetSubTreeType& subtree) {
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +053098 BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler enter";
Ed Tanous1abe55e2018-09-05 08:30:59 -070099 if (ec)
100 {
Ed Tanous5f7d88c2018-11-14 14:08:56 -0800101 messages::internalError(SensorsAsyncResp->res);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530102 BMCWEB_LOG_ERROR
103 << "getObjectsWithConnection resp_handler: Dbus error " << ec;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700104 return;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100105 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100106
Ed Tanous1abe55e2018-09-05 08:30:59 -0700107 BMCWEB_LOG_DEBUG << "Found " << subtree.size() << " subtrees";
108
109 // Make unique list of connections only for requested sensor types and
110 // found in the chassis
111 boost::container::flat_set<std::string> connections;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530112 std::set<std::pair<std::string, std::string>> objectsWithConnection;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700113 // Intrinsic to avoid malloc. Most systems will have < 8 sensor
114 // producers
115 connections.reserve(8);
116
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700117 BMCWEB_LOG_DEBUG << "sensorNames list count: " << sensorNames->size();
118 for (const std::string& tsensor : *sensorNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700119 {
120 BMCWEB_LOG_DEBUG << "Sensor to find: " << tsensor;
121 }
122
123 for (const std::pair<
124 std::string,
125 std::vector<std::pair<std::string, std::vector<std::string>>>>&
126 object : subtree)
127 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700128 if (sensorNames->find(object.first) != sensorNames->end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700129 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700130 for (const std::pair<std::string, std::vector<std::string>>&
131 objData : object.second)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700132 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700133 BMCWEB_LOG_DEBUG << "Adding connection: " << objData.first;
134 connections.insert(objData.first);
135 objectsWithConnection.insert(
136 std::make_pair(object.first, objData.first));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700137 }
138 }
139 }
140 BMCWEB_LOG_DEBUG << "Found " << connections.size() << " connections";
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530141 callback(std::move(connections), std::move(objectsWithConnection));
142 BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler exit";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700143 };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700144 // Make call to ObjectMapper to find all sensors objects
145 crow::connections::systemBus->async_method_call(
146 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
147 "/xyz/openbmc_project/object_mapper",
148 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 2, interfaces);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530149 BMCWEB_LOG_DEBUG << "getObjectsWithConnection exit";
150}
151
152/**
153 * @brief Create connections necessary for sensors
154 * @param SensorsAsyncResp Pointer to object holding response data
155 * @param sensorNames Sensors retrieved from chassis
156 * @param callback Callback for processing gathered connections
157 */
158template <typename Callback>
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700159void getConnections(
160 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
161 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
162 Callback&& callback)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530163{
164 auto objectsWithConnectionCb =
165 [callback](const boost::container::flat_set<std::string>& connections,
166 const std::set<std::pair<std::string, std::string>>&
167 objectsWithConnection) {
168 callback(std::move(connections));
169 };
170 getObjectsWithConnection(SensorsAsyncResp, sensorNames,
171 std::move(objectsWithConnectionCb));
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100172}
173
174/**
Shawn McCarneyde629b62019-03-08 10:42:51 -0600175 * @brief Gets all DBus sensor names.
176 *
177 * Finds the sensor names asynchronously. Invokes callback when information has
178 * been obtained.
179 *
180 * The callback must have the following signature:
181 * @code
182 * callback(boost::container::flat_set<std::string>& sensorNames)
183 * @endcode
184 *
185 * @param SensorsAsyncResp Pointer to object holding response data.
186 * @param callback Callback to invoke when sensor names obtained.
187 */
188template <typename Callback>
189void getAllSensors(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
190 Callback&& callback)
191{
192 BMCWEB_LOG_DEBUG << "getAllSensors enter";
193 const std::array<std::string, 1> interfaces = {
194 "xyz.openbmc_project.Sensor.Value"};
195
196 // Response handler for GetSubTreePaths DBus method
197 auto respHandler = [callback{std::move(callback)}, SensorsAsyncResp](
198 const boost::system::error_code ec,
199 const std::vector<std::string>& sensorList) {
200 BMCWEB_LOG_DEBUG << "getAllSensors respHandler enter";
201 if (ec)
202 {
203 messages::internalError(SensorsAsyncResp->res);
204 BMCWEB_LOG_ERROR << "getAllSensors respHandler: DBus error " << ec;
205 return;
206 }
207 boost::container::flat_set<std::string> sensorNames;
208 for (const std::string& sensor : sensorList)
209 {
210 std::size_t lastPos = sensor.rfind("/");
211 if (lastPos == std::string::npos)
212 {
213 BMCWEB_LOG_ERROR << "Failed to find '/' in " << sensor;
214 continue;
215 }
216 std::string sensorName = sensor.substr(lastPos + 1);
217 sensorNames.emplace(sensorName);
218 BMCWEB_LOG_DEBUG << "Added sensor name " << sensorName;
219 }
220 callback(sensorNames);
221 BMCWEB_LOG_DEBUG << "getAllSensors respHandler exit";
222 };
223
224 // Query mapper for all DBus sensor object paths
225 crow::connections::systemBus->async_method_call(
226 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
227 "/xyz/openbmc_project/object_mapper",
228 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
229 "/xyz/openbmc_project/sensors", int32_t(0), interfaces);
230 BMCWEB_LOG_DEBUG << "getAllSensors exit";
231}
232
233/**
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700234 * @brief Shrinks the list of sensors for processing
235 * @param SensorsAysncResp The class holding the Redfish response
236 * @param allSensors A list of all the sensors associated to the
237 * chassis element (i.e. baseboard, front panel, etc...)
238 * @param activeSensors A list that is a reduction of the incoming
239 * allSensors list. Eliminate Thermal sensors when a Power request is
240 * made, and eliminate Power sensors when a Thermal request is made.
241 */
242void reduceSensorList(
243 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
244 const std::vector<std::string>* allSensors,
245 std::shared_ptr<boost::container::flat_set<std::string>> activeSensors)
246{
247 if (SensorsAsyncResp == nullptr)
248 {
249 return;
250 }
251 if ((allSensors == nullptr) || (activeSensors == nullptr))
252 {
253 messages::resourceNotFound(
254 SensorsAsyncResp->res, SensorsAsyncResp->chassisSubNode,
255 SensorsAsyncResp->chassisSubNode == "Thermal" ? "Temperatures"
256 : "Voltages");
257
258 return;
259 }
260 if (allSensors->empty())
261 {
262 // Nothing to do, the activeSensors object is also empty
263 return;
264 }
265
266 for (const char* type : SensorsAsyncResp->types)
267 {
268 for (const std::string& sensor : *allSensors)
269 {
270 if (boost::starts_with(sensor, type))
271 {
272 activeSensors->emplace(sensor);
273 }
274 }
275 }
276}
277
278/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100279 * @brief Retrieves requested chassis sensors and redundancy data from DBus .
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200280 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100281 * @param callback Callback for next step in gathered sensor processing
282 */
283template <typename Callback>
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700284void getChassis(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700285 Callback&& callback)
286{
287 BMCWEB_LOG_DEBUG << "getChassis enter";
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700288 const std::array<const char*, 3> interfaces = {
289 "xyz.openbmc_project.Inventory.Item.Board",
290 "xyz.openbmc_project.Inventory.Item.Chassis",
291 "xyz.openbmc_project.Inventory.Item.PowerSupply"};
292 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp](
293 const boost::system::error_code ec,
294 const std::vector<std::string>& chassisPaths) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700295 BMCWEB_LOG_DEBUG << "getChassis respHandler enter";
296 if (ec)
297 {
298 BMCWEB_LOG_ERROR << "getChassis respHandler DBUS error: " << ec;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700299 messages::internalError(sensorsAsyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700300 return;
301 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100302
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700303 const std::string* chassisPath = nullptr;
304 std::string chassisName;
305 for (const std::string& chassis : chassisPaths)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700306 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700307 std::size_t lastPos = chassis.rfind("/");
308 if (lastPos == std::string::npos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700309 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700310 BMCWEB_LOG_ERROR << "Failed to find '/' in " << chassis;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700311 continue;
312 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700313 chassisName = chassis.substr(lastPos + 1);
314 if (chassisName == sensorsAsyncResp->chassisId)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700315 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700316 chassisPath = &chassis;
317 break;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700318 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700319 }
320 if (chassisPath == nullptr)
321 {
322 messages::resourceNotFound(sensorsAsyncResp->res, "Chassis",
323 sensorsAsyncResp->chassisId);
324 return;
325 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700326
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700327 const std::string& chassisSubNode = sensorsAsyncResp->chassisSubNode;
328 if (chassisSubNode == "Power")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700329 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700330 sensorsAsyncResp->res.jsonValue["@odata.type"] =
331 "#Power.v1_5_2.Power";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700332 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700333 else if (chassisSubNode == "Thermal")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700334 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700335 sensorsAsyncResp->res.jsonValue["@odata.type"] =
336 "#Thermal.v1_4_0.Thermal";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700337 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700338 sensorsAsyncResp->res.jsonValue["@odata.id"] =
339 "/redfish/v1/Chassis/" + sensorsAsyncResp->chassisId + "/" +
340 chassisSubNode;
341
342 sensorsAsyncResp->res.jsonValue["@odata.context"] =
343 "/redfish/v1/$metadata#" + chassisSubNode + "." + chassisSubNode;
344 sensorsAsyncResp->res.jsonValue["Id"] = chassisSubNode;
345 sensorsAsyncResp->res.jsonValue["Name"] = chassisSubNode;
346
347 // Get the list of sensors for this Chassis element
348 std::string sensorPath = *chassisPath + "/sensors";
349 crow::connections::systemBus->async_method_call(
350 [sensorsAsyncResp, callback{std::move(callback)}](
351 const boost::system::error_code ec,
352 const std::variant<std::vector<std::string>>&
353 variantEndpoints) {
354 if (ec)
355 {
356 if (ec.value() != EBADR)
357 {
358 messages::internalError(sensorsAsyncResp->res);
359 return;
360 }
361 }
362 const std::vector<std::string>* nodeSensorList =
363 std::get_if<std::vector<std::string>>(&(variantEndpoints));
364 if (nodeSensorList == nullptr)
365 {
366 messages::resourceNotFound(
367 sensorsAsyncResp->res, sensorsAsyncResp->chassisSubNode,
368 sensorsAsyncResp->chassisSubNode == "Thermal"
369 ? "Temperatures"
370 : "Voltages");
371 return;
372 }
373 const std::shared_ptr<boost::container::flat_set<std::string>>
374 culledSensorList = std::make_shared<
375 boost::container::flat_set<std::string>>();
376 reduceSensorList(sensorsAsyncResp, nodeSensorList,
377 culledSensorList);
378 callback(culledSensorList);
379 },
380 "xyz.openbmc_project.ObjectMapper", sensorPath,
381 "org.freedesktop.DBus.Properties", "Get",
382 "xyz.openbmc_project.Association", "endpoints");
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100383 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100384
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700385 // Get the Chassis Collection
Ed Tanous1abe55e2018-09-05 08:30:59 -0700386 crow::connections::systemBus->async_method_call(
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700387 respHandler, "xyz.openbmc_project.ObjectMapper",
388 "/xyz/openbmc_project/object_mapper",
389 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
390 "/xyz/openbmc_project/inventory", int32_t(0), interfaces);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700391 BMCWEB_LOG_DEBUG << "getChassis exit";
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100392}
393
394/**
Shawn McCarneyde629b62019-03-08 10:42:51 -0600395 * @brief Finds all DBus object paths that implement ObjectManager.
396 *
397 * Creates a mapping from the associated connection name to the object path.
398 *
399 * Finds the object paths asynchronously. Invokes callback when information has
400 * been obtained.
401 *
402 * The callback must have the following signature:
403 * @code
404 * callback(const boost::container::flat_map<std::string,
405 * std::string>& objectMgrPaths)
406 * @endcode
407 *
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700408 * @param sensorsAsyncResp Pointer to object holding response data.
Shawn McCarneyde629b62019-03-08 10:42:51 -0600409 * @param callback Callback to invoke when object paths obtained.
410 */
411template <typename Callback>
412void getObjectManagerPaths(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
413 Callback&& callback)
414{
415 BMCWEB_LOG_DEBUG << "getObjectManagerPaths enter";
416 const std::array<std::string, 1> interfaces = {
417 "org.freedesktop.DBus.ObjectManager"};
418
419 // Response handler for GetSubTree DBus method
420 auto respHandler = [callback{std::move(callback)},
421 SensorsAsyncResp](const boost::system::error_code ec,
422 const GetSubTreeType& subtree) {
423 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler enter";
424 if (ec)
425 {
426 messages::internalError(SensorsAsyncResp->res);
427 BMCWEB_LOG_ERROR << "getObjectManagerPaths respHandler: DBus error "
428 << ec;
429 return;
430 }
431
432 // Loop over returned object paths
433 boost::container::flat_map<std::string, std::string> objectMgrPaths;
434 for (const std::pair<
435 std::string,
436 std::vector<std::pair<std::string, std::vector<std::string>>>>&
437 object : subtree)
438 {
439 // Loop over connections for current object path
440 const std::string& objectPath = object.first;
441 for (const std::pair<std::string, std::vector<std::string>>&
442 objData : object.second)
443 {
444 // Add mapping from connection to object path
445 const std::string& connection = objData.first;
446 objectMgrPaths[connection] = objectPath;
447 BMCWEB_LOG_DEBUG << "Added mapping " << connection << " -> "
448 << objectPath;
449 }
450 }
451 callback(std::move(objectMgrPaths));
452 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler exit";
453 };
454
455 // Query mapper for all DBus object paths that implement ObjectManager
456 crow::connections::systemBus->async_method_call(
457 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
458 "/xyz/openbmc_project/object_mapper",
459 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", int32_t(0),
460 interfaces);
461 BMCWEB_LOG_DEBUG << "getObjectManagerPaths exit";
462}
463
464/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100465 * @brief Builds a json sensor representation of a sensor.
466 * @param sensorName The name of the sensor to be built
Gunnar Mills274fad52018-06-13 15:45:36 -0500467 * @param sensorType The type (temperature, fan_tach, etc) of the sensor to
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100468 * build
469 * @param interfacesDict A dictionary of the interfaces and properties of said
470 * interfaces to be built from
471 * @param sensor_json The json object to fill
472 */
473void objectInterfacesToJson(
474 const std::string& sensorName, const std::string& sensorType,
475 const boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700476 std::string, boost::container::flat_map<std::string, SensorVariant>>&
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100477 interfacesDict,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700478 nlohmann::json& sensor_json)
479{
480 // We need a value interface before we can do anything with it
481 auto valueIt = interfacesDict.find("xyz.openbmc_project.Sensor.Value");
482 if (valueIt == interfacesDict.end())
483 {
484 BMCWEB_LOG_ERROR << "Sensor doesn't have a value interface";
485 return;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100486 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100487
Ed Tanous1abe55e2018-09-05 08:30:59 -0700488 // Assume values exist as is (10^0 == 1) if no scale exists
489 int64_t scaleMultiplier = 0;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100490
Ed Tanous1abe55e2018-09-05 08:30:59 -0700491 auto scaleIt = valueIt->second.find("Scale");
492 // If a scale exists, pull value as int64, and use the scaling.
493 if (scaleIt != valueIt->second.end())
494 {
Ed Tanousabf2add2019-01-22 16:40:12 -0800495 const int64_t* int64Value = std::get_if<int64_t>(&scaleIt->second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700496 if (int64Value != nullptr)
497 {
498 scaleMultiplier = *int64Value;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100499 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100500 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700501
502 sensor_json["MemberId"] = sensorName;
503 sensor_json["Name"] = sensorName;
504 sensor_json["Status"]["State"] = "Enabled";
505 sensor_json["Status"]["Health"] = "OK";
506
507 // Parameter to set to override the type we get from dbus, and force it to
508 // int, regardless of what is available. This is used for schemas like fan,
509 // that require integers, not floats.
510 bool forceToInt = false;
511
512 const char* unit = "Reading";
513 if (sensorType == "temperature")
514 {
515 unit = "ReadingCelsius";
516 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Temperature";
517 // TODO(ed) Documentation says that path should be type fan_tach,
518 // implementation seems to implement fan
519 }
520 else if (sensorType == "fan" || sensorType == "fan_tach")
521 {
522 unit = "Reading";
523 sensor_json["ReadingUnits"] = "RPM";
524 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
525 forceToInt = true;
526 }
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700527 else if (sensorType == "fan_pwm")
528 {
529 unit = "Reading";
530 sensor_json["ReadingUnits"] = "Percent";
531 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
532 forceToInt = true;
533 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700534 else if (sensorType == "voltage")
535 {
536 unit = "ReadingVolts";
537 sensor_json["@odata.type"] = "#Power.v1_0_0.Voltage";
538 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700539 else if (sensorType == "power")
540 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700541 std::string sensorNameLower =
542 boost::algorithm::to_lower_copy(sensorName);
543
544 if (sensorNameLower.find("input") != std::string::npos)
545 {
546 unit = "PowerInputWatts";
547 }
548 else
549 {
550 unit = "PowerOutputWatts";
551 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700552 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700553 else
554 {
555 BMCWEB_LOG_ERROR << "Redfish cannot map object type for " << sensorName;
556 return;
557 }
558 // Map of dbus interface name, dbus property name and redfish property_name
559 std::vector<std::tuple<const char*, const char*, const char*>> properties;
560 properties.reserve(7);
561
562 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit);
Shawn McCarneyde629b62019-03-08 10:42:51 -0600563
564 // If sensor type doesn't map to Redfish PowerSupply, add threshold props
565 if ((sensorType != "current") && (sensorType != "power"))
566 {
567 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
568 "WarningHigh", "UpperThresholdNonCritical");
569 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
570 "WarningLow", "LowerThresholdNonCritical");
571 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
572 "CriticalHigh", "UpperThresholdCritical");
573 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
574 "CriticalLow", "LowerThresholdCritical");
575 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700576
Ed Tanous2474adf2018-09-05 16:31:16 -0700577 // TODO Need to get UpperThresholdFatal and LowerThresholdFatal
578
Ed Tanous1abe55e2018-09-05 08:30:59 -0700579 if (sensorType == "temperature")
580 {
581 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
582 "MinReadingRangeTemp");
583 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
584 "MaxReadingRangeTemp");
585 }
Shawn McCarneyde629b62019-03-08 10:42:51 -0600586 else if ((sensorType != "current") && (sensorType != "power"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700587 {
Shawn McCarneyde629b62019-03-08 10:42:51 -0600588 // Sensor type doesn't map to Redfish PowerSupply; add min/max props
Ed Tanous1abe55e2018-09-05 08:30:59 -0700589 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
590 "MinReadingRange");
591 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
592 "MaxReadingRange");
593 }
594
595 for (const std::tuple<const char*, const char*, const char*>& p :
596 properties)
597 {
598 auto interfaceProperties = interfacesDict.find(std::get<0>(p));
599 if (interfaceProperties != interfacesDict.end())
600 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000601 auto valueIt = interfaceProperties->second.find(std::get<1>(p));
602 if (valueIt != interfaceProperties->second.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700603 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000604 const SensorVariant& valueVariant = valueIt->second;
605 nlohmann::json& valueIt = sensor_json[std::get<2>(p)];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700606 // Attempt to pull the int64 directly
Ed Tanousabf2add2019-01-22 16:40:12 -0800607 const int64_t* int64Value = std::get_if<int64_t>(&valueVariant);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700608
Ed Tanousabf2add2019-01-22 16:40:12 -0800609 const double* doubleValue = std::get_if<double>(&valueVariant);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700610 double temp = 0.0;
611 if (int64Value != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700612 {
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700613 temp = *int64Value;
614 }
615 else if (doubleValue != nullptr)
616 {
617 temp = *doubleValue;
618 }
619 else
620 {
621 BMCWEB_LOG_ERROR
622 << "Got value interface that wasn't int or double";
623 continue;
624 }
625 temp = temp * std::pow(10, scaleMultiplier);
626 if (forceToInt)
627 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000628 valueIt = static_cast<int64_t>(temp);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700629 }
630 else
631 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000632 valueIt = temp;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700633 }
634 }
635 }
636 }
637 BMCWEB_LOG_DEBUG << "Added sensor " << sensorName;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100638}
639
James Feist8bd25cc2019-03-15 15:14:00 -0700640static void
641 populateFanRedundancy(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp)
642{
643 crow::connections::systemBus->async_method_call(
644 [sensorsAsyncResp](const boost::system::error_code ec,
645 const GetSubTreeType& resp) {
646 if (ec)
647 {
648 return; // don't have to have this interface
649 }
Ed Tanouse278c182019-03-13 16:23:37 -0700650 for (const std::pair<std::string,
651 std::vector<std::pair<
652 std::string, std::vector<std::string>>>>&
653 pathPair : resp)
James Feist8bd25cc2019-03-15 15:14:00 -0700654 {
Ed Tanouse278c182019-03-13 16:23:37 -0700655 const std::string& path = pathPair.first;
656 const std::vector<
657 std::pair<std::string, std::vector<std::string>>>& objDict =
658 pathPair.second;
James Feist8bd25cc2019-03-15 15:14:00 -0700659 if (objDict.empty())
660 {
661 continue; // this should be impossible
662 }
663
664 const std::string& owner = objDict.begin()->first;
665 crow::connections::systemBus->async_method_call(
666 [path, owner,
667 sensorsAsyncResp](const boost::system::error_code ec,
668 std::variant<std::vector<std::string>>
669 variantEndpoints) {
670 if (ec)
671 {
672 return; // if they don't have an association we
673 // can't tell what chassis is
674 }
675 // verify part of the right chassis
676 auto endpoints = std::get_if<std::vector<std::string>>(
677 &variantEndpoints);
678
679 if (endpoints == nullptr)
680 {
681 BMCWEB_LOG_ERROR << "Invalid association interface";
682 messages::internalError(sensorsAsyncResp->res);
683 return;
684 }
685
686 auto found = std::find_if(
687 endpoints->begin(), endpoints->end(),
688 [sensorsAsyncResp](const std::string& entry) {
689 return entry.find(
690 sensorsAsyncResp->chassisId) !=
691 std::string::npos;
692 });
693
694 if (found == endpoints->end())
695 {
696 return;
697 }
698 crow::connections::systemBus->async_method_call(
699 [path, sensorsAsyncResp](
700 const boost::system::error_code ec,
701 const boost::container::flat_map<
702 std::string,
703 std::variant<uint8_t,
704 std::vector<std::string>,
705 std::string>>& ret) {
706 if (ec)
707 {
708 return; // don't have to have this
709 // interface
710 }
711 auto findFailures = ret.find("AllowedFailures");
712 auto findCollection = ret.find("Collection");
713 auto findStatus = ret.find("Status");
714
715 if (findFailures == ret.end() ||
716 findCollection == ret.end() ||
717 findStatus == ret.end())
718 {
719 BMCWEB_LOG_ERROR
720 << "Invalid redundancy interface";
721 messages::internalError(
722 sensorsAsyncResp->res);
723 return;
724 }
725
726 auto allowedFailures = std::get_if<uint8_t>(
727 &(findFailures->second));
728 auto collection =
729 std::get_if<std::vector<std::string>>(
730 &(findCollection->second));
731 auto status = std::get_if<std::string>(
732 &(findStatus->second));
733
734 if (allowedFailures == nullptr ||
735 collection == nullptr || status == nullptr)
736 {
737
738 BMCWEB_LOG_ERROR
739 << "Invalid redundancy interface "
740 "types";
741 messages::internalError(
742 sensorsAsyncResp->res);
743 return;
744 }
745 size_t lastSlash = path.rfind("/");
746 if (lastSlash == std::string::npos)
747 {
748 // this should be impossible
749 messages::internalError(
750 sensorsAsyncResp->res);
751 return;
752 }
753 std::string name = path.substr(lastSlash + 1);
754 std::replace(name.begin(), name.end(), '_',
755 ' ');
756
757 std::string health;
758
759 if (boost::ends_with(*status, "Full"))
760 {
761 health = "OK";
762 }
763 else if (boost::ends_with(*status, "Degraded"))
764 {
765 health = "Warning";
766 }
767 else
768 {
769 health = "Critical";
770 }
771 std::vector<nlohmann::json> redfishCollection;
772 const auto& fanRedfish =
773 sensorsAsyncResp->res.jsonValue["Fans"];
774 for (const std::string& item : *collection)
775 {
776 lastSlash = item.rfind("/");
777 // make a copy as collection is const
778 std::string itemName =
779 item.substr(lastSlash + 1);
780 /*
781 todo(ed): merge patch that fixes the names
782 std::replace(itemName.begin(),
783 itemName.end(), '_', ' ');*/
784 auto schemaItem = std::find_if(
785 fanRedfish.begin(), fanRedfish.end(),
786 [itemName](const nlohmann::json& fan) {
787 return fan["MemberId"] == itemName;
788 });
789 if (schemaItem != fanRedfish.end())
790 {
791 redfishCollection.push_back(
792 {{"@odata.id",
793 (*schemaItem)["@odata.id"]}});
794 }
795 else
796 {
797 BMCWEB_LOG_ERROR
798 << "failed to find fan in schema";
799 messages::internalError(
800 sensorsAsyncResp->res);
801 return;
802 }
803 }
804
805 auto& resp = sensorsAsyncResp->res
806 .jsonValue["Redundancy"];
807 resp.push_back(
808 {{"@odata.id",
809 "/refish/v1/Chassis/" +
810 sensorsAsyncResp->chassisId + "/" +
811 sensorsAsyncResp->chassisSubNode +
812 "#/Redundancy/" +
813 std::to_string(resp.size())},
814 {"@odata.type",
815 "#Redundancy.v1_3_2.Redundancy"},
816 {"MinNumNeeded",
817 collection->size() - *allowedFailures},
818 {"MemberId", name},
819 {"Mode", "N+m"},
820 {"Name", name},
821 {"RedundancySet", redfishCollection},
822 {"Status",
823 {{"Health", health},
824 {"State", "Enabled"}}}});
825 },
826 owner, path, "org.freedesktop.DBus.Properties",
827 "GetAll",
828 "xyz.openbmc_project.Control.FanRedundancy");
829 },
830 "xyz.openbmc_project.ObjectMapper", path + "/inventory",
831 "org.freedesktop.DBus.Properties", "Get",
832 "xyz.openbmc_project.Association", "endpoints");
833 }
834 },
835 "xyz.openbmc_project.ObjectMapper",
836 "/xyz/openbmc_project/object_mapper",
837 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
838 "/xyz/openbmc_project/control", 2,
839 std::array<const char*, 1>{
840 "xyz.openbmc_project.Control.FanRedundancy"});
841}
842
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700843void sortJSONResponse(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
844{
845 nlohmann::json& response = SensorsAsyncResp->res.jsonValue;
846 std::array<std::string, 2> sensorHeaders{"Temperatures", "Fans"};
847 if (SensorsAsyncResp->chassisSubNode == "Power")
848 {
849 sensorHeaders = {"Voltages", "PowerSupplies"};
850 }
851 for (const std::string& sensorGroup : sensorHeaders)
852 {
853 nlohmann::json::iterator entry = response.find(sensorGroup);
854 if (entry != response.end())
855 {
856 std::sort(entry->begin(), entry->end(),
857 [](nlohmann::json& c1, nlohmann::json& c2) {
858 return c1["Name"] < c2["Name"];
859 });
860
861 // add the index counts to the end of each entry
862 size_t count = 0;
863 for (nlohmann::json& sensorJson : *entry)
864 {
865 nlohmann::json::iterator odata = sensorJson.find("@odata.id");
866 if (odata == sensorJson.end())
867 {
868 continue;
869 }
870 std::string* value = odata->get_ptr<std::string*>();
871 if (value != nullptr)
872 {
873 *value += std::to_string(count);
874 count++;
875 }
876 }
877 }
878 }
879}
880
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100881/**
Shawn McCarneyde629b62019-03-08 10:42:51 -0600882 * @brief Gets the values of the specified sensors.
883 *
884 * Stores the results as JSON in the SensorsAsyncResp.
885 *
886 * Gets the sensor values asynchronously. Stores the results later when the
887 * information has been obtained.
888 *
889 * The sensorNames set contains all sensors for the current chassis.
890 * SensorsAsyncResp contains the requested sensor types. Only sensors of a
891 * requested type are included in the JSON output.
892 *
893 * To minimize the number of DBus calls, the DBus method
894 * org.freedesktop.DBus.ObjectManager.GetManagedObjects() is used to get the
895 * values of all sensors provided by a connection (service).
896 *
897 * The connections set contains all the connections that provide sensor values.
898 *
899 * The objectMgrPaths map contains mappings from a connection name to the
900 * corresponding DBus object path that implements ObjectManager.
901 *
902 * @param SensorsAsyncResp Pointer to object holding response data.
903 * @param sensorNames All sensors within the current chassis.
904 * @param connections Connections that provide sensor values.
905 * @param objectMgrPaths Mappings from connection name to DBus object path that
906 * implements ObjectManager.
907 */
908void getSensorData(
909 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700910 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames,
Shawn McCarneyde629b62019-03-08 10:42:51 -0600911 const boost::container::flat_set<std::string>& connections,
912 const boost::container::flat_map<std::string, std::string>& objectMgrPaths)
913{
914 BMCWEB_LOG_DEBUG << "getSensorData enter";
915 // Get managed objects from all services exposing sensors
916 for (const std::string& connection : connections)
917 {
918 // Response handler to process managed objects
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700919 auto getManagedObjectsCb = [SensorsAsyncResp, sensorNames](
Shawn McCarneyde629b62019-03-08 10:42:51 -0600920 const boost::system::error_code ec,
921 ManagedObjectsVectorType& resp) {
922 BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter";
923 if (ec)
924 {
925 BMCWEB_LOG_ERROR << "getManagedObjectsCb DBUS error: " << ec;
926 messages::internalError(SensorsAsyncResp->res);
927 return;
928 }
929 // Go through all objects and update response with sensor data
930 for (const auto& objDictEntry : resp)
931 {
932 const std::string& objPath =
933 static_cast<const std::string&>(objDictEntry.first);
934 BMCWEB_LOG_DEBUG << "getManagedObjectsCb parsing object "
935 << objPath;
936
Shawn McCarneyde629b62019-03-08 10:42:51 -0600937 std::vector<std::string> split;
938 // Reserve space for
939 // /xyz/openbmc_project/sensors/<name>/<subname>
940 split.reserve(6);
941 boost::algorithm::split(split, objPath, boost::is_any_of("/"));
942 if (split.size() < 6)
943 {
944 BMCWEB_LOG_ERROR << "Got path that isn't long enough "
945 << objPath;
946 continue;
947 }
948 // These indexes aren't intuitive, as boost::split puts an empty
949 // string at the beginning
950 const std::string& sensorType = split[4];
951 const std::string& sensorName = split[5];
952 BMCWEB_LOG_DEBUG << "sensorName " << sensorName
953 << " sensorType " << sensorType;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700954 if (sensorNames->find(objPath) == sensorNames->end())
Shawn McCarneyde629b62019-03-08 10:42:51 -0600955 {
956 BMCWEB_LOG_ERROR << sensorName << " not in sensor list ";
957 continue;
958 }
959
960 const char* fieldName = nullptr;
961 if (sensorType == "temperature")
962 {
963 fieldName = "Temperatures";
964 }
965 else if (sensorType == "fan" || sensorType == "fan_tach" ||
966 sensorType == "fan_pwm")
967 {
968 fieldName = "Fans";
969 }
970 else if (sensorType == "voltage")
971 {
972 fieldName = "Voltages";
973 }
974 else if (sensorType == "current")
975 {
976 fieldName = "PowerSupplies";
977 }
978 else if (sensorType == "power")
979 {
980 fieldName = "PowerSupplies";
981 }
982 else
983 {
984 BMCWEB_LOG_ERROR << "Unsure how to handle sensorType "
985 << sensorType;
986 continue;
987 }
988
989 nlohmann::json& tempArray =
990 SensorsAsyncResp->res.jsonValue[fieldName];
991
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700992 if (fieldName == "PowerSupplies" && !tempArray.empty())
993 {
994 // Power supplies put multiple "sensors" into a single power
995 // supply entry, so only create the first one
996 }
997 else
998 {
999 tempArray.push_back(
1000 {{"@odata.id", "/redfish/v1/Chassis/" +
1001 SensorsAsyncResp->chassisId + "/" +
1002 SensorsAsyncResp->chassisSubNode +
1003 "#/" + fieldName + "/"}});
1004 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06001005 nlohmann::json& sensorJson = tempArray.back();
1006
1007 objectInterfacesToJson(sensorName, sensorType,
1008 objDictEntry.second, sensorJson);
1009 }
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001010 if (SensorsAsyncResp.use_count() == 1)
James Feist8bd25cc2019-03-15 15:14:00 -07001011 {
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001012 sortJSONResponse(SensorsAsyncResp);
1013 if (SensorsAsyncResp->chassisSubNode == "Thermal")
1014 {
1015 populateFanRedundancy(SensorsAsyncResp);
1016 }
James Feist8bd25cc2019-03-15 15:14:00 -07001017 }
Shawn McCarneyde629b62019-03-08 10:42:51 -06001018 BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit";
1019 };
1020
1021 // Find DBus object path that implements ObjectManager for the current
1022 // connection. If no mapping found, default to "/".
1023 auto iter = objectMgrPaths.find(connection);
1024 const std::string& objectMgrPath =
1025 (iter != objectMgrPaths.end()) ? iter->second : "/";
1026 BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is "
1027 << objectMgrPath;
1028
1029 crow::connections::systemBus->async_method_call(
1030 getManagedObjectsCb, connection, objectMgrPath,
1031 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1032 };
1033 BMCWEB_LOG_DEBUG << "getSensorData exit";
1034}
1035
1036/**
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001037 * @brief Entry point for retrieving sensors data related to requested
1038 * chassis.
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001039 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001040 */
Ed Tanous1abe55e2018-09-05 08:30:59 -07001041void getChassisData(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
1042{
1043 BMCWEB_LOG_DEBUG << "getChassisData enter";
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001044 auto getChassisCb =
1045 [SensorsAsyncResp](
1046 std::shared_ptr<boost::container::flat_set<std::string>>
1047 sensorNames) {
1048 BMCWEB_LOG_DEBUG << "getChassisCb enter";
1049 auto getConnectionCb =
1050 [SensorsAsyncResp,
1051 sensorNames](const boost::container::flat_set<std::string>&
1052 connections) {
1053 BMCWEB_LOG_DEBUG << "getConnectionCb enter";
1054 auto getObjectManagerPathsCb =
1055 [SensorsAsyncResp, sensorNames, connections](
1056 const boost::container::flat_map<
1057 std::string, std::string>& objectMgrPaths) {
1058 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb enter";
1059 // Get sensor data and store results in JSON
1060 // response
1061 getSensorData(SensorsAsyncResp, sensorNames,
1062 connections, objectMgrPaths);
1063 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb exit";
1064 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001065
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001066 // Get mapping from connection names to the DBus object
1067 // paths that implement the ObjectManager interface
1068 getObjectManagerPaths(SensorsAsyncResp,
1069 std::move(getObjectManagerPathsCb));
1070 BMCWEB_LOG_DEBUG << "getConnectionCb exit";
1071 };
Shawn McCarneyde629b62019-03-08 10:42:51 -06001072
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001073 // Get set of connections that provide sensor values
1074 getConnections(SensorsAsyncResp, sensorNames,
1075 std::move(getConnectionCb));
1076 BMCWEB_LOG_DEBUG << "getChassisCb exit";
1077 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001078
Shawn McCarneyde629b62019-03-08 10:42:51 -06001079#ifdef BMCWEB_ENABLE_REDFISH_ONE_CHASSIS
1080 // Get all sensor names
1081 getAllSensors(SensorsAsyncResp, std::move(getChassisCb));
1082#else
1083 // Get sensor names in chassis
Ed Tanous1abe55e2018-09-05 08:30:59 -07001084 getChassis(SensorsAsyncResp, std::move(getChassisCb));
Shawn McCarneyde629b62019-03-08 10:42:51 -06001085#endif
Ed Tanous1abe55e2018-09-05 08:30:59 -07001086 BMCWEB_LOG_DEBUG << "getChassisData exit";
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001087};
1088
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301089/**
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001090 * @brief Find the requested sensorName in the list of all sensors supplied by
1091 * the chassis node
1092 *
1093 * @param sensorName The sensor name supplied in the PATCH request
1094 * @param sensorsList The list of sensors managed by the chassis node
1095 * @param sensorsModified The list of sensors that were found as a result of
1096 * repeated calls to this function
1097 */
1098bool findSensorNameUsingSensorPath(
1099 const std::string& sensorName,
1100 boost::container::flat_set<std::string>& sensorsList,
1101 boost::container::flat_set<std::string>& sensorsModified)
1102{
1103 for (const std::string& chassisSensor : sensorsList)
1104 {
1105 std::string thisSensorName;
1106 if (!dbus::utility::getNthStringFromPath(chassisSensor, 5,
1107 thisSensorName))
1108 {
1109 BMCWEB_LOG_ERROR << "Got path that isn't long enough "
1110 << chassisSensor;
1111 continue;
1112 }
1113 if (thisSensorName == sensorName)
1114 {
1115 sensorsModified.emplace(chassisSensor);
1116 return true;
1117 }
1118 }
1119 return false;
1120}
1121
1122/**
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301123 * @brief Entry point for overriding sensor values of given sensor
1124 *
1125 * @param res response object
1126 * @param req request object
1127 * @param params parameter passed for CRUD
1128 * @param typeList TypeList of sensors for the resource queried
1129 * @param chassisSubNode Chassis Node for which the query has to happen
1130 */
1131void setSensorOverride(crow::Response& res, const crow::Request& req,
1132 const std::vector<std::string>& params,
Ed Tanousb01bf292019-03-25 19:25:26 +00001133 const std::initializer_list<const char*> typeList,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301134 const std::string& chassisSubNode)
1135{
1136
1137 // TODO: Need to figure out dynamic way to restrict patch (Set Sensor
1138 // override) based on another d-bus announcement to be more generic.
1139 if (params.size() != 1)
1140 {
1141 messages::internalError(res);
1142 res.end();
1143 return;
1144 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301145
1146 std::unordered_map<std::string, std::vector<nlohmann::json>> allCollections;
1147 std::optional<std::vector<nlohmann::json>> temperatureCollections;
1148 std::optional<std::vector<nlohmann::json>> fanCollections;
1149 std::vector<nlohmann::json> voltageCollections;
1150 BMCWEB_LOG_INFO << "setSensorOverride for subNode" << chassisSubNode
1151 << "\n";
1152
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301153 if (chassisSubNode == "Thermal")
1154 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301155 if (!json_util::readJson(req, res, "Temperatures",
1156 temperatureCollections, "Fans",
1157 fanCollections))
1158 {
1159 return;
1160 }
1161 if (!temperatureCollections && !fanCollections)
1162 {
1163 messages::resourceNotFound(res, "Thermal",
1164 "Temperatures / Voltages");
1165 res.end();
1166 return;
1167 }
1168 if (temperatureCollections)
1169 {
1170 allCollections.emplace("Temperatures",
1171 *std::move(temperatureCollections));
1172 }
1173 if (fanCollections)
1174 {
1175 allCollections.emplace("Fans", *std::move(fanCollections));
1176 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301177 }
1178 else if (chassisSubNode == "Power")
1179 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301180 if (!json_util::readJson(req, res, "Voltages", voltageCollections))
1181 {
1182 return;
1183 }
1184 allCollections.emplace("Voltages", std::move(voltageCollections));
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301185 }
1186 else
1187 {
1188 res.result(boost::beast::http::status::not_found);
1189 res.end();
1190 return;
1191 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301192
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301193 const char* propertyValueName;
1194 std::unordered_map<std::string, std::pair<double, std::string>> overrideMap;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301195 std::string memberId;
1196 double value;
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301197 for (auto& collectionItems : allCollections)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301198 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301199 if (collectionItems.first == "Temperatures")
1200 {
1201 propertyValueName = "ReadingCelsius";
1202 }
1203 else if (collectionItems.first == "Fans")
1204 {
1205 propertyValueName = "Reading";
1206 }
1207 else
1208 {
1209 propertyValueName = "ReadingVolts";
1210 }
1211 for (auto& item : collectionItems.second)
1212 {
1213 if (!json_util::readJson(item, res, "MemberId", memberId,
1214 propertyValueName, value))
1215 {
1216 return;
1217 }
1218 overrideMap.emplace(memberId,
1219 std::make_pair(value, collectionItems.first));
1220 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301221 }
1222 const std::string& chassisName = params[0];
1223 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
1224 res, chassisName, typeList, chassisSubNode);
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001225 auto getChassisSensorListCb = [sensorAsyncResp,
1226 overrideMap](const std::shared_ptr<
1227 boost::container::flat_set<
1228 std::string>>
1229 sensorsList) {
1230 // Match sensor names in the PATCH request to those managed by the
1231 // chassis node
1232 const std::shared_ptr<boost::container::flat_set<std::string>>
1233 sensorNames =
1234 std::make_shared<boost::container::flat_set<std::string>>();
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301235 for (const auto& item : overrideMap)
1236 {
1237 const auto& sensor = item.first;
Johnathan Mantey49c53ac2019-05-02 09:22:38 -07001238 if (!findSensorNameUsingSensorPath(sensor, *sensorsList,
1239 *sensorNames))
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301240 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301241 BMCWEB_LOG_INFO << "Unable to find memberId " << item.first;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301242 messages::resourceNotFound(sensorAsyncResp->res,
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301243 item.second.second, item.first);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301244 return;
1245 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301246 }
1247 // Get the connection to which the memberId belongs
1248 auto getObjectsWithConnectionCb =
1249 [sensorAsyncResp, overrideMap](
1250 const boost::container::flat_set<std::string>& connections,
1251 const std::set<std::pair<std::string, std::string>>&
1252 objectsWithConnection) {
1253 if (objectsWithConnection.size() != overrideMap.size())
1254 {
1255 BMCWEB_LOG_INFO
1256 << "Unable to find all objects with proper connection "
1257 << objectsWithConnection.size() << " requested "
1258 << overrideMap.size() << "\n";
1259 messages::resourceNotFound(
1260 sensorAsyncResp->res,
1261 sensorAsyncResp->chassisSubNode == "Thermal"
1262 ? "Temperatures"
1263 : "Voltages",
1264 "Count");
1265 return;
1266 }
1267 for (const auto& item : objectsWithConnection)
1268 {
1269
1270 auto lastPos = item.first.rfind('/');
1271 if (lastPos == std::string::npos)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301272 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301273 messages::internalError(sensorAsyncResp->res);
1274 return;
1275 }
1276 std::string sensorName = item.first.substr(lastPos + 1);
1277
1278 const auto& iterator = overrideMap.find(sensorName);
1279 if (iterator == overrideMap.end())
1280 {
1281 BMCWEB_LOG_INFO << "Unable to find sensor object"
1282 << item.first << "\n";
1283 messages::internalError(sensorAsyncResp->res);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301284 return;
1285 }
1286 crow::connections::systemBus->async_method_call(
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301287 [sensorAsyncResp](const boost::system::error_code ec) {
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301288 if (ec)
1289 {
1290 BMCWEB_LOG_DEBUG
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301291 << "setOverrideValueStatus DBUS error: "
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301292 << ec;
1293 messages::internalError(sensorAsyncResp->res);
1294 return;
1295 }
1296 },
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301297 item.second, item.first,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301298 "org.freedesktop.DBus.Properties", "Set",
1299 "xyz.openbmc_project.Sensor.Value", "Value",
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +05301300 sdbusplus::message::variant<double>(
1301 iterator->second.first));
1302 }
1303 };
1304 // Get object with connection for the given sensor name
1305 getObjectsWithConnection(sensorAsyncResp, sensorNames,
1306 std::move(getObjectsWithConnectionCb));
1307 };
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +05301308 // get full sensor list for the given chassisId and cross verify the sensor.
1309 getChassis(sensorAsyncResp, std::move(getChassisSensorListCb));
1310}
1311
Ed Tanous1abe55e2018-09-05 08:30:59 -07001312} // namespace redfish