blob: 5a0cf759260a786cdf2a2f4ddbea01d4e3627d5d [file] [log] [blame]
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +01001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
18#include <math.h>
Ed Tanous1abe55e2018-09-05 08:30:59 -070019
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010020#include <boost/algorithm/string/predicate.hpp>
21#include <boost/algorithm/string/split.hpp>
22#include <boost/container/flat_map.hpp>
23#include <boost/range/algorithm/replace_copy_if.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070024#include <dbus_singleton.hpp>
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +053025#include <utils/json_utils.hpp>
Ed Tanousabf2add2019-01-22 16:40:12 -080026#include <variant>
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010027
Ed Tanous1abe55e2018-09-05 08:30:59 -070028namespace redfish
29{
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010030
Ed Tanousd76323e2018-08-07 14:35:40 -070031constexpr const char* dbusSensorPrefix = "/xyz/openbmc_project/sensors/";
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010032
33using GetSubTreeType = std::vector<
34 std::pair<std::string,
35 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
36
Ed Tanousabf2add2019-01-22 16:40:12 -080037using SensorVariant = std::variant<int64_t, double>;
Ed Tanousaa2e59c2018-04-12 12:17:20 -070038
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010039using ManagedObjectsVectorType = std::vector<std::pair<
Ed Tanousaa2e59c2018-04-12 12:17:20 -070040 sdbusplus::message::object_path,
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010041 boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -070042 std::string, boost::container::flat_map<std::string, SensorVariant>>>>;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010043
44/**
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020045 * SensorsAsyncResp
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010046 * Gathers data needed for response processing after async calls are done
47 */
Ed Tanous1abe55e2018-09-05 08:30:59 -070048class SensorsAsyncResp
49{
50 public:
51 SensorsAsyncResp(crow::Response& response, const std::string& chassisId,
Ed Tanous2474adf2018-09-05 16:31:16 -070052 const std::initializer_list<const char*> types,
53 const std::string& subNode) :
Ed Tanous43b761d2019-02-13 20:10:56 -080054 res(response),
55 chassisId(chassisId), types(types), chassisSubNode(subNode)
Ed Tanous1abe55e2018-09-05 08:30:59 -070056 {
57 res.jsonValue["@odata.id"] =
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +053058 "/redfish/v1/Chassis/" + chassisId + "/" + subNode;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010059 }
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020060
Ed Tanous1abe55e2018-09-05 08:30:59 -070061 ~SensorsAsyncResp()
62 {
63 if (res.result() == boost::beast::http::status::internal_server_error)
64 {
65 // Reset the json object to clear out any data that made it in
66 // before the error happened todo(ed) handle error condition with
67 // proper code
68 res.jsonValue = nlohmann::json::object();
69 }
70 res.end();
71 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010072
Ed Tanous1abe55e2018-09-05 08:30:59 -070073 crow::Response& res;
74 std::string chassisId{};
75 const std::vector<const char*> types;
Ed Tanous2474adf2018-09-05 16:31:16 -070076 std::string chassisSubNode{};
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010077};
78
79/**
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +053080 * @brief Get objects with connection necessary for sensors
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020081 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010082 * @param sensorNames Sensors retrieved from chassis
83 * @param callback Callback for processing gathered connections
84 */
85template <typename Callback>
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +053086void getObjectsWithConnection(
87 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
88 const boost::container::flat_set<std::string>& sensorNames,
89 Callback&& callback)
Ed Tanous1abe55e2018-09-05 08:30:59 -070090{
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +053091 BMCWEB_LOG_DEBUG << "getObjectsWithConnection enter";
Ed Tanous1abe55e2018-09-05 08:30:59 -070092 const std::string path = "/xyz/openbmc_project/sensors";
93 const std::array<std::string, 1> interfaces = {
94 "xyz.openbmc_project.Sensor.Value"};
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +010095
Ed Tanous1abe55e2018-09-05 08:30:59 -070096 // Response handler for parsing objects subtree
97 auto respHandler = [callback{std::move(callback)}, SensorsAsyncResp,
98 sensorNames](const boost::system::error_code ec,
99 const GetSubTreeType& subtree) {
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530100 BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler enter";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700101 if (ec)
102 {
Ed Tanous5f7d88c2018-11-14 14:08:56 -0800103 messages::internalError(SensorsAsyncResp->res);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530104 BMCWEB_LOG_ERROR
105 << "getObjectsWithConnection resp_handler: Dbus error " << ec;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700106 return;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100107 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100108
Ed Tanous1abe55e2018-09-05 08:30:59 -0700109 BMCWEB_LOG_DEBUG << "Found " << subtree.size() << " subtrees";
110
111 // Make unique list of connections only for requested sensor types and
112 // found in the chassis
113 boost::container::flat_set<std::string> connections;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530114 std::set<std::pair<std::string, std::string>> objectsWithConnection;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700115 // Intrinsic to avoid malloc. Most systems will have < 8 sensor
116 // producers
117 connections.reserve(8);
118
119 BMCWEB_LOG_DEBUG << "sensorNames list count: " << sensorNames.size();
120 for (const std::string& tsensor : sensorNames)
121 {
122 BMCWEB_LOG_DEBUG << "Sensor to find: " << tsensor;
123 }
124
125 for (const std::pair<
126 std::string,
127 std::vector<std::pair<std::string, std::vector<std::string>>>>&
128 object : subtree)
129 {
130 for (const char* type : SensorsAsyncResp->types)
131 {
132 if (boost::starts_with(object.first, type))
133 {
134 auto lastPos = object.first.rfind('/');
135 if (lastPos != std::string::npos)
136 {
137 std::string sensorName =
138 object.first.substr(lastPos + 1);
139
140 if (sensorNames.find(sensorName) != sensorNames.end())
141 {
142 // For each Connection name
143 for (const std::pair<std::string,
144 std::vector<std::string>>&
145 objData : object.second)
146 {
147 BMCWEB_LOG_DEBUG << "Adding connection: "
148 << objData.first;
149 connections.insert(objData.first);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530150 objectsWithConnection.insert(std::make_pair(
151 object.first, objData.first));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700152 }
153 }
154 }
155 break;
156 }
157 }
158 }
159 BMCWEB_LOG_DEBUG << "Found " << connections.size() << " connections";
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530160 callback(std::move(connections), std::move(objectsWithConnection));
161 BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler exit";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700162 };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700163 // Make call to ObjectMapper to find all sensors objects
164 crow::connections::systemBus->async_method_call(
165 std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
166 "/xyz/openbmc_project/object_mapper",
167 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 2, interfaces);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530168 BMCWEB_LOG_DEBUG << "getObjectsWithConnection exit";
169}
170
171/**
172 * @brief Create connections necessary for sensors
173 * @param SensorsAsyncResp Pointer to object holding response data
174 * @param sensorNames Sensors retrieved from chassis
175 * @param callback Callback for processing gathered connections
176 */
177template <typename Callback>
178void getConnections(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
179 const boost::container::flat_set<std::string>& sensorNames,
180 Callback&& callback)
181{
182 auto objectsWithConnectionCb =
183 [callback](const boost::container::flat_set<std::string>& connections,
184 const std::set<std::pair<std::string, std::string>>&
185 objectsWithConnection) {
186 callback(std::move(connections));
187 };
188 getObjectsWithConnection(SensorsAsyncResp, sensorNames,
189 std::move(objectsWithConnectionCb));
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100190}
191
192/**
193 * @brief Retrieves requested chassis sensors and redundancy data from DBus .
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200194 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100195 * @param callback Callback for next step in gathered sensor processing
196 */
197template <typename Callback>
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200198void getChassis(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700199 Callback&& callback)
200{
201 BMCWEB_LOG_DEBUG << "getChassis enter";
202 // Process response from EntityManager and extract chassis data
203 auto respHandler = [callback{std::move(callback)},
204 SensorsAsyncResp](const boost::system::error_code ec,
205 ManagedObjectsVectorType& resp) {
206 BMCWEB_LOG_DEBUG << "getChassis respHandler enter";
207 if (ec)
208 {
209 BMCWEB_LOG_ERROR << "getChassis respHandler DBUS error: " << ec;
Ed Tanous5f7d88c2018-11-14 14:08:56 -0800210 messages::internalError(SensorsAsyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700211 return;
212 }
213 boost::container::flat_set<std::string> sensorNames;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100214
Ed Tanous1abe55e2018-09-05 08:30:59 -0700215 // SensorsAsyncResp->chassisId
216 bool foundChassis = false;
217 std::vector<std::string> split;
218 // Reserve space for
219 // /xyz/openbmc_project/inventory/<name>/<subname> + 3 subnames
220 split.reserve(8);
Ed Tanousdaf36e22018-04-20 16:01:36 -0700221
Ed Tanous1abe55e2018-09-05 08:30:59 -0700222 for (const auto& objDictEntry : resp)
223 {
224 const std::string& objectPath =
225 static_cast<const std::string&>(objDictEntry.first);
226 boost::algorithm::split(split, objectPath, boost::is_any_of("/"));
227 if (split.size() < 2)
228 {
229 BMCWEB_LOG_ERROR << "Got path that isn't long enough "
230 << objectPath;
231 split.clear();
232 continue;
233 }
234 const std::string& sensorName = split.end()[-1];
235 const std::string& chassisName = split.end()[-2];
Ed Tanousdaf36e22018-04-20 16:01:36 -0700236
Ed Tanous1abe55e2018-09-05 08:30:59 -0700237 if (chassisName != SensorsAsyncResp->chassisId)
238 {
239 split.clear();
240 continue;
241 }
242 BMCWEB_LOG_DEBUG << "New sensor: " << sensorName;
243 foundChassis = true;
244 sensorNames.emplace(sensorName);
245 split.clear();
246 };
247 BMCWEB_LOG_DEBUG << "Found " << sensorNames.size() << " Sensor names";
248
249 if (!foundChassis)
250 {
251 BMCWEB_LOG_INFO << "Unable to find chassis named "
252 << SensorsAsyncResp->chassisId;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700253 messages::resourceNotFound(SensorsAsyncResp->res, "Chassis",
254 SensorsAsyncResp->chassisId);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700255 }
256 else
257 {
258 callback(sensorNames);
259 }
260 BMCWEB_LOG_DEBUG << "getChassis respHandler exit";
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100261 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100262
Ed Tanous1abe55e2018-09-05 08:30:59 -0700263 // Make call to EntityManager to find all chassis objects
264 crow::connections::systemBus->async_method_call(
265 respHandler, "xyz.openbmc_project.EntityManager", "/",
266 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
267 BMCWEB_LOG_DEBUG << "getChassis exit";
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100268}
269
270/**
271 * @brief Builds a json sensor representation of a sensor.
272 * @param sensorName The name of the sensor to be built
Gunnar Mills274fad52018-06-13 15:45:36 -0500273 * @param sensorType The type (temperature, fan_tach, etc) of the sensor to
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100274 * build
275 * @param interfacesDict A dictionary of the interfaces and properties of said
276 * interfaces to be built from
277 * @param sensor_json The json object to fill
278 */
279void objectInterfacesToJson(
280 const std::string& sensorName, const std::string& sensorType,
281 const boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700282 std::string, boost::container::flat_map<std::string, SensorVariant>>&
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100283 interfacesDict,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700284 nlohmann::json& sensor_json)
285{
286 // We need a value interface before we can do anything with it
287 auto valueIt = interfacesDict.find("xyz.openbmc_project.Sensor.Value");
288 if (valueIt == interfacesDict.end())
289 {
290 BMCWEB_LOG_ERROR << "Sensor doesn't have a value interface";
291 return;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100292 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100293
Ed Tanous1abe55e2018-09-05 08:30:59 -0700294 // Assume values exist as is (10^0 == 1) if no scale exists
295 int64_t scaleMultiplier = 0;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100296
Ed Tanous1abe55e2018-09-05 08:30:59 -0700297 auto scaleIt = valueIt->second.find("Scale");
298 // If a scale exists, pull value as int64, and use the scaling.
299 if (scaleIt != valueIt->second.end())
300 {
Ed Tanousabf2add2019-01-22 16:40:12 -0800301 const int64_t* int64Value = std::get_if<int64_t>(&scaleIt->second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700302 if (int64Value != nullptr)
303 {
304 scaleMultiplier = *int64Value;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100305 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100306 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700307
308 sensor_json["MemberId"] = sensorName;
309 sensor_json["Name"] = sensorName;
310 sensor_json["Status"]["State"] = "Enabled";
311 sensor_json["Status"]["Health"] = "OK";
312
313 // Parameter to set to override the type we get from dbus, and force it to
314 // int, regardless of what is available. This is used for schemas like fan,
315 // that require integers, not floats.
316 bool forceToInt = false;
317
318 const char* unit = "Reading";
319 if (sensorType == "temperature")
320 {
321 unit = "ReadingCelsius";
322 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Temperature";
323 // TODO(ed) Documentation says that path should be type fan_tach,
324 // implementation seems to implement fan
325 }
326 else if (sensorType == "fan" || sensorType == "fan_tach")
327 {
328 unit = "Reading";
329 sensor_json["ReadingUnits"] = "RPM";
330 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
331 forceToInt = true;
332 }
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700333 else if (sensorType == "fan_pwm")
334 {
335 unit = "Reading";
336 sensor_json["ReadingUnits"] = "Percent";
337 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan";
338 forceToInt = true;
339 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700340 else if (sensorType == "voltage")
341 {
342 unit = "ReadingVolts";
343 sensor_json["@odata.type"] = "#Power.v1_0_0.Voltage";
344 }
Ed Tanous2474adf2018-09-05 16:31:16 -0700345 else if (sensorType == "power")
346 {
347 unit = "LastPowerOutputWatts";
348 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700349 else
350 {
351 BMCWEB_LOG_ERROR << "Redfish cannot map object type for " << sensorName;
352 return;
353 }
354 // Map of dbus interface name, dbus property name and redfish property_name
355 std::vector<std::tuple<const char*, const char*, const char*>> properties;
356 properties.reserve(7);
357
358 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit);
359 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
360 "WarningHigh", "UpperThresholdNonCritical");
361 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning",
362 "WarningLow", "LowerThresholdNonCritical");
363 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
364 "CriticalHigh", "UpperThresholdCritical");
365 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical",
366 "CriticalLow", "LowerThresholdCritical");
367
Ed Tanous2474adf2018-09-05 16:31:16 -0700368 // TODO Need to get UpperThresholdFatal and LowerThresholdFatal
369
Ed Tanous1abe55e2018-09-05 08:30:59 -0700370 if (sensorType == "temperature")
371 {
372 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
373 "MinReadingRangeTemp");
374 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
375 "MaxReadingRangeTemp");
376 }
377 else
378 {
379 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue",
380 "MinReadingRange");
381 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue",
382 "MaxReadingRange");
383 }
384
385 for (const std::tuple<const char*, const char*, const char*>& p :
386 properties)
387 {
388 auto interfaceProperties = interfacesDict.find(std::get<0>(p));
389 if (interfaceProperties != interfacesDict.end())
390 {
391 auto valueIt = interfaceProperties->second.find(std::get<1>(p));
392 if (valueIt != interfaceProperties->second.end())
393 {
394 const SensorVariant& valueVariant = valueIt->second;
395 nlohmann::json& valueIt = sensor_json[std::get<2>(p)];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700396 // Attempt to pull the int64 directly
Ed Tanousabf2add2019-01-22 16:40:12 -0800397 const int64_t* int64Value = std::get_if<int64_t>(&valueVariant);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700398
Ed Tanousabf2add2019-01-22 16:40:12 -0800399 const double* doubleValue = std::get_if<double>(&valueVariant);
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700400 double temp = 0.0;
401 if (int64Value != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700402 {
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700403 temp = *int64Value;
404 }
405 else if (doubleValue != nullptr)
406 {
407 temp = *doubleValue;
408 }
409 else
410 {
411 BMCWEB_LOG_ERROR
412 << "Got value interface that wasn't int or double";
413 continue;
414 }
415 temp = temp * std::pow(10, scaleMultiplier);
416 if (forceToInt)
417 {
418 valueIt = static_cast<int64_t>(temp);
419 }
420 else
421 {
422 valueIt = temp;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700423 }
424 }
425 }
426 }
427 BMCWEB_LOG_DEBUG << "Added sensor " << sensorName;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100428}
429
430/**
431 * @brief Entry point for retrieving sensors data related to requested
432 * chassis.
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200433 * @param SensorsAsyncResp Pointer to object holding response data
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100434 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700435void getChassisData(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp)
436{
437 BMCWEB_LOG_DEBUG << "getChassisData enter";
438 auto getChassisCb = [&, SensorsAsyncResp](
439 boost::container::flat_set<std::string>&
440 sensorNames) {
441 BMCWEB_LOG_DEBUG << "getChassisCb enter";
442 auto getConnectionCb =
443 [&, SensorsAsyncResp, sensorNames](
444 const boost::container::flat_set<std::string>& connections) {
445 BMCWEB_LOG_DEBUG << "getConnectionCb enter";
446 // Get managed objects from all services exposing sensors
447 for (const std::string& connection : connections)
448 {
449 // Response handler to process managed objects
450 auto getManagedObjectsCb =
451 [&, SensorsAsyncResp,
452 sensorNames](const boost::system::error_code ec,
453 ManagedObjectsVectorType& resp) {
454 BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter";
455 if (ec)
456 {
457 BMCWEB_LOG_ERROR
458 << "getManagedObjectsCb DBUS error: " << ec;
Ed Tanous5f7d88c2018-11-14 14:08:56 -0800459 messages::internalError(SensorsAsyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700460 return;
461 }
462 // Go through all objects and update response with
463 // sensor data
464 for (const auto& objDictEntry : resp)
465 {
466 const std::string& objPath =
467 static_cast<const std::string&>(
468 objDictEntry.first);
469 BMCWEB_LOG_DEBUG
470 << "getManagedObjectsCb parsing object "
471 << objPath;
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100472
Ed Tanous1abe55e2018-09-05 08:30:59 -0700473 std::vector<std::string> split;
474 // Reserve space for
475 // /xyz/openbmc_project/sensors/<name>/<subname>
476 split.reserve(6);
477 boost::algorithm::split(split, objPath,
478 boost::is_any_of("/"));
479 if (split.size() < 6)
480 {
481 BMCWEB_LOG_ERROR
482 << "Got path that isn't long enough "
483 << objPath;
484 continue;
485 }
486 // These indexes aren't intuitive, as
487 // boost::split puts an empty string at the
488 // beggining
489 const std::string& sensorType = split[4];
490 const std::string& sensorName = split[5];
491 BMCWEB_LOG_DEBUG << "sensorName " << sensorName
492 << " sensorType "
493 << sensorType;
494 if (sensorNames.find(sensorName) ==
495 sensorNames.end())
496 {
497 BMCWEB_LOG_ERROR << sensorName
498 << " not in sensor list ";
499 continue;
500 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100501
Ed Tanous1abe55e2018-09-05 08:30:59 -0700502 const char* fieldName = nullptr;
503 if (sensorType == "temperature")
504 {
505 fieldName = "Temperatures";
506 }
507 else if (sensorType == "fan" ||
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700508 sensorType == "fan_tach" ||
509 sensorType == "fan_pwm")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700510 {
511 fieldName = "Fans";
512 }
513 else if (sensorType == "voltage")
514 {
515 fieldName = "Voltages";
516 }
517 else if (sensorType == "current")
518 {
519 fieldName = "PowerSupply";
520 }
521 else if (sensorType == "power")
522 {
523 fieldName = "PowerSupply";
524 }
525 else
526 {
527 BMCWEB_LOG_ERROR
528 << "Unsure how to handle sensorType "
529 << sensorType;
530 continue;
531 }
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100532
Ed Tanous1abe55e2018-09-05 08:30:59 -0700533 nlohmann::json& tempArray =
534 SensorsAsyncResp->res.jsonValue[fieldName];
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100535
Ed Tanous1abe55e2018-09-05 08:30:59 -0700536 tempArray.push_back(
537 {{"@odata.id",
538 "/redfish/v1/Chassis/" +
Ed Tanous2474adf2018-09-05 16:31:16 -0700539 SensorsAsyncResp->chassisId + "/" +
540 SensorsAsyncResp->chassisSubNode +
541 "#/" + fieldName + "/" +
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700542 std::to_string(tempArray.size())}});
Ed Tanous1abe55e2018-09-05 08:30:59 -0700543 nlohmann::json& sensorJson = tempArray.back();
Ed Tanous6f6d0d32018-10-12 11:16:43 -0700544
Ed Tanous1abe55e2018-09-05 08:30:59 -0700545 objectInterfacesToJson(sensorName, sensorType,
546 objDictEntry.second,
547 sensorJson);
548 }
549 BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit";
550 };
551 crow::connections::systemBus->async_method_call(
552 getManagedObjectsCb, connection, "/",
553 "org.freedesktop.DBus.ObjectManager",
554 "GetManagedObjects");
555 };
556 BMCWEB_LOG_DEBUG << "getConnectionCb exit";
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200557 };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700558 // get connections and then pass it to get sensors
559 getConnections(SensorsAsyncResp, sensorNames,
560 std::move(getConnectionCb));
561 BMCWEB_LOG_DEBUG << "getChassisCb exit";
562 };
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100563
Ed Tanous1abe55e2018-09-05 08:30:59 -0700564 // get chassis information related to sensors
565 getChassis(SensorsAsyncResp, std::move(getChassisCb));
566 BMCWEB_LOG_DEBUG << "getChassisData exit";
Lewanczyk, Dawid08777fb2018-03-22 23:33:49 +0100567};
568
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530569/**
570 * @brief Entry point for overriding sensor values of given sensor
571 *
572 * @param res response object
573 * @param req request object
574 * @param params parameter passed for CRUD
575 * @param typeList TypeList of sensors for the resource queried
576 * @param chassisSubNode Chassis Node for which the query has to happen
577 */
578void setSensorOverride(crow::Response& res, const crow::Request& req,
579 const std::vector<std::string>& params,
580 const std::initializer_list<const char*> typeList,
581 const std::string& chassisSubNode)
582{
583
584 // TODO: Need to figure out dynamic way to restrict patch (Set Sensor
585 // override) based on another d-bus announcement to be more generic.
586 if (params.size() != 1)
587 {
588 messages::internalError(res);
589 res.end();
590 return;
591 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530592
593 std::unordered_map<std::string, std::vector<nlohmann::json>> allCollections;
594 std::optional<std::vector<nlohmann::json>> temperatureCollections;
595 std::optional<std::vector<nlohmann::json>> fanCollections;
596 std::vector<nlohmann::json> voltageCollections;
597 BMCWEB_LOG_INFO << "setSensorOverride for subNode" << chassisSubNode
598 << "\n";
599
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530600 if (chassisSubNode == "Thermal")
601 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530602 if (!json_util::readJson(req, res, "Temperatures",
603 temperatureCollections, "Fans",
604 fanCollections))
605 {
606 return;
607 }
608 if (!temperatureCollections && !fanCollections)
609 {
610 messages::resourceNotFound(res, "Thermal",
611 "Temperatures / Voltages");
612 res.end();
613 return;
614 }
615 if (temperatureCollections)
616 {
617 allCollections.emplace("Temperatures",
618 *std::move(temperatureCollections));
619 }
620 if (fanCollections)
621 {
622 allCollections.emplace("Fans", *std::move(fanCollections));
623 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530624 }
625 else if (chassisSubNode == "Power")
626 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530627 if (!json_util::readJson(req, res, "Voltages", voltageCollections))
628 {
629 return;
630 }
631 allCollections.emplace("Voltages", std::move(voltageCollections));
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530632 }
633 else
634 {
635 res.result(boost::beast::http::status::not_found);
636 res.end();
637 return;
638 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530639
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530640 const char* propertyValueName;
641 std::unordered_map<std::string, std::pair<double, std::string>> overrideMap;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530642 std::string memberId;
643 double value;
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530644 for (auto& collectionItems : allCollections)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530645 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530646 if (collectionItems.first == "Temperatures")
647 {
648 propertyValueName = "ReadingCelsius";
649 }
650 else if (collectionItems.first == "Fans")
651 {
652 propertyValueName = "Reading";
653 }
654 else
655 {
656 propertyValueName = "ReadingVolts";
657 }
658 for (auto& item : collectionItems.second)
659 {
660 if (!json_util::readJson(item, res, "MemberId", memberId,
661 propertyValueName, value))
662 {
663 return;
664 }
665 overrideMap.emplace(memberId,
666 std::make_pair(value, collectionItems.first));
667 }
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530668 }
669 const std::string& chassisName = params[0];
670 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
671 res, chassisName, typeList, chassisSubNode);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530672 // first check for valid chassis id & sensor in requested chassis.
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530673 auto getChassisSensorListCb = [sensorAsyncResp, overrideMap](
674 const boost::container::flat_set<
675 std::string>& sensorLists) {
676 boost::container::flat_set<std::string> sensorNames;
677 for (const auto& item : overrideMap)
678 {
679 const auto& sensor = item.first;
680 if (sensorLists.find(item.first) == sensorLists.end())
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530681 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530682 BMCWEB_LOG_INFO << "Unable to find memberId " << item.first;
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530683 messages::resourceNotFound(sensorAsyncResp->res,
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530684 item.second.second, item.first);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530685 return;
686 }
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530687 sensorNames.emplace(sensor);
688 }
689 // Get the connection to which the memberId belongs
690 auto getObjectsWithConnectionCb =
691 [sensorAsyncResp, overrideMap](
692 const boost::container::flat_set<std::string>& connections,
693 const std::set<std::pair<std::string, std::string>>&
694 objectsWithConnection) {
695 if (objectsWithConnection.size() != overrideMap.size())
696 {
697 BMCWEB_LOG_INFO
698 << "Unable to find all objects with proper connection "
699 << objectsWithConnection.size() << " requested "
700 << overrideMap.size() << "\n";
701 messages::resourceNotFound(
702 sensorAsyncResp->res,
703 sensorAsyncResp->chassisSubNode == "Thermal"
704 ? "Temperatures"
705 : "Voltages",
706 "Count");
707 return;
708 }
709 for (const auto& item : objectsWithConnection)
710 {
711
712 auto lastPos = item.first.rfind('/');
713 if (lastPos == std::string::npos)
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530714 {
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530715 messages::internalError(sensorAsyncResp->res);
716 return;
717 }
718 std::string sensorName = item.first.substr(lastPos + 1);
719
720 const auto& iterator = overrideMap.find(sensorName);
721 if (iterator == overrideMap.end())
722 {
723 BMCWEB_LOG_INFO << "Unable to find sensor object"
724 << item.first << "\n";
725 messages::internalError(sensorAsyncResp->res);
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530726 return;
727 }
728 crow::connections::systemBus->async_method_call(
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530729 [sensorAsyncResp](const boost::system::error_code ec) {
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530730 if (ec)
731 {
732 BMCWEB_LOG_DEBUG
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530733 << "setOverrideValueStatus DBUS error: "
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530734 << ec;
735 messages::internalError(sensorAsyncResp->res);
736 return;
737 }
738 },
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530739 item.second, item.first,
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530740 "org.freedesktop.DBus.Properties", "Set",
741 "xyz.openbmc_project.Sensor.Value", "Value",
Richard Marian Thomaiyarf65af9e2019-02-13 23:35:05 +0530742 sdbusplus::message::variant<double>(
743 iterator->second.first));
744 }
745 };
746 // Get object with connection for the given sensor name
747 getObjectsWithConnection(sensorAsyncResp, sensorNames,
748 std::move(getObjectsWithConnectionCb));
749 };
Richard Marian Thomaiyar413961d2019-02-01 00:43:39 +0530750 // get full sensor list for the given chassisId and cross verify the sensor.
751 getChassis(sensorAsyncResp, std::move(getChassisSensorListCb));
752}
753
Ed Tanous1abe55e2018-09-05 08:30:59 -0700754} // namespace redfish