blob: 60f88568fc825eb86defcfa107a7ce65ffbe56a4 [file] [log] [blame]
Borawski.Lukasz9c3106852018-02-09 15:24:22 +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
James Feistb49ac872019-05-21 15:12:01 -070018#include "health.hpp"
Borawski.Lukasz9c3106852018-02-09 15:24:22 +010019#include "node.hpp"
Jennifer Leec5d03ff2019-03-08 15:42:58 -080020#include "redfish_util.hpp"
Borawski.Lukasz9c3106852018-02-09 15:24:22 +010021
James Feist5b4aa862018-08-16 14:07:01 -070022#include <boost/algorithm/string/replace.hpp>
Santosh Puranikaf5d60582019-03-20 18:16:36 +053023#include <boost/date_time.hpp>
James Feist5b4aa862018-08-16 14:07:01 -070024#include <dbus_utility.hpp>
James Feist73df0db2019-03-25 15:29:35 -070025#include <memory>
Santosh Puranikaf5d60582019-03-20 18:16:36 +053026#include <sstream>
Bernard Wong7bffdb72019-03-20 16:17:21 +080027#include <utils/systemd_utils.hpp>
Ed Tanousabf2add2019-01-22 16:40:12 -080028#include <variant>
James Feist5b4aa862018-08-16 14:07:01 -070029
Ed Tanous1abe55e2018-09-05 08:30:59 -070030namespace redfish
31{
Jennifer Leeed5befb2018-08-10 11:29:45 -070032
33/**
34 * ManagerActionsReset class supports handle POST method for Reset action.
35 * The class retrieves and sends data directly to dbus.
36 */
37class ManagerActionsReset : public Node
38{
39 public:
40 ManagerActionsReset(CrowApp& app) :
41 Node(app, "/redfish/v1/Managers/bmc/Actions/Manager.Reset/")
42 {
43 entityPrivileges = {
44 {boost::beast::http::verb::get, {{"Login"}}},
45 {boost::beast::http::verb::head, {{"Login"}}},
46 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
47 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
48 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
49 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
50 }
51
52 private:
53 /**
Jennifer Leeed5befb2018-08-10 11:29:45 -070054 * Function handles POST method request.
55 * Analyzes POST body message before sends Reset request data to dbus.
56 * OpenBMC allows for ResetType is GracefulRestart only.
57 */
58 void doPost(crow::Response& res, const crow::Request& req,
59 const std::vector<std::string>& params) override
60 {
61 std::string resetType;
62
63 if (!json_util::readJson(req, res, "ResetType", resetType))
64 {
65 return;
66 }
67
68 if (resetType != "GracefulRestart")
69 {
70 res.result(boost::beast::http::status::bad_request);
71 messages::actionParameterNotSupported(res, resetType, "ResetType");
72 BMCWEB_LOG_ERROR << "Request incorrect action parameter: "
73 << resetType;
74 res.end();
75 return;
76 }
77 doBMCGracefulRestart(res, req, params);
78 }
79
80 /**
81 * Function transceives data with dbus directly.
82 * All BMC state properties will be retrieved before sending reset request.
83 */
84 void doBMCGracefulRestart(crow::Response& res, const crow::Request& req,
85 const std::vector<std::string>& params)
86 {
87 const char* processName = "xyz.openbmc_project.State.BMC";
88 const char* objectPath = "/xyz/openbmc_project/state/bmc0";
89 const char* interfaceName = "xyz.openbmc_project.State.BMC";
90 const std::string& propertyValue =
91 "xyz.openbmc_project.State.BMC.Transition.Reboot";
92 const char* destProperty = "RequestedBMCTransition";
93
94 // Create the D-Bus variant for D-Bus call.
95 VariantType dbusPropertyValue(propertyValue);
96
97 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
98
99 crow::connections::systemBus->async_method_call(
100 [asyncResp](const boost::system::error_code ec) {
101 // Use "Set" method to set the property value.
102 if (ec)
103 {
104 BMCWEB_LOG_ERROR << "[Set] Bad D-Bus request error: " << ec;
105 messages::internalError(asyncResp->res);
106 return;
107 }
108
109 messages::success(asyncResp->res);
110 },
111 processName, objectPath, "org.freedesktop.DBus.Properties", "Set",
112 interfaceName, destProperty, dbusPropertyValue);
113 }
114};
115
James Feist5b4aa862018-08-16 14:07:01 -0700116static constexpr const char* objectManagerIface =
117 "org.freedesktop.DBus.ObjectManager";
118static constexpr const char* pidConfigurationIface =
119 "xyz.openbmc_project.Configuration.Pid";
120static constexpr const char* pidZoneConfigurationIface =
121 "xyz.openbmc_project.Configuration.Pid.Zone";
James Feistb7a08d02018-12-11 14:55:37 -0800122static constexpr const char* stepwiseConfigurationIface =
123 "xyz.openbmc_project.Configuration.Stepwise";
James Feist73df0db2019-03-25 15:29:35 -0700124static constexpr const char* thermalModeIface =
125 "xyz.openbmc_project.Control.ThermalMode";
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100126
James Feist5b4aa862018-08-16 14:07:01 -0700127static void asyncPopulatePid(const std::string& connection,
128 const std::string& path,
James Feist73df0db2019-03-25 15:29:35 -0700129 const std::string& currentProfile,
130 const std::vector<std::string>& supportedProfiles,
James Feist5b4aa862018-08-16 14:07:01 -0700131 std::shared_ptr<AsyncResp> asyncResp)
132{
133
134 crow::connections::systemBus->async_method_call(
James Feist73df0db2019-03-25 15:29:35 -0700135 [asyncResp, currentProfile, supportedProfiles](
136 const boost::system::error_code ec,
137 const dbus::utility::ManagedObjectType& managedObj) {
James Feist5b4aa862018-08-16 14:07:01 -0700138 if (ec)
139 {
140 BMCWEB_LOG_ERROR << ec;
James Feist5b4aa862018-08-16 14:07:01 -0700141 asyncResp->res.jsonValue.clear();
Jason M. Billsf12894f2018-10-09 12:45:45 -0700142 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700143 return;
144 }
145 nlohmann::json& configRoot =
146 asyncResp->res.jsonValue["Oem"]["OpenBmc"]["Fan"];
147 nlohmann::json& fans = configRoot["FanControllers"];
148 fans["@odata.type"] = "#OemManager.FanControllers";
149 fans["@odata.context"] =
150 "/redfish/v1/$metadata#OemManager.FanControllers";
151 fans["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc/"
152 "Fan/FanControllers";
153
154 nlohmann::json& pids = configRoot["PidControllers"];
155 pids["@odata.type"] = "#OemManager.PidControllers";
156 pids["@odata.context"] =
157 "/redfish/v1/$metadata#OemManager.PidControllers";
158 pids["@odata.id"] =
159 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/PidControllers";
160
James Feistb7a08d02018-12-11 14:55:37 -0800161 nlohmann::json& stepwise = configRoot["StepwiseControllers"];
162 stepwise["@odata.type"] = "#OemManager.StepwiseControllers";
163 stepwise["@odata.context"] =
164 "/redfish/v1/$metadata#OemManager.StepwiseControllers";
165 stepwise["@odata.id"] =
166 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/StepwiseControllers";
167
James Feist5b4aa862018-08-16 14:07:01 -0700168 nlohmann::json& zones = configRoot["FanZones"];
169 zones["@odata.id"] =
170 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones";
171 zones["@odata.type"] = "#OemManager.FanZones";
172 zones["@odata.context"] =
173 "/redfish/v1/$metadata#OemManager.FanZones";
174 configRoot["@odata.id"] =
175 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan";
176 configRoot["@odata.type"] = "#OemManager.Fan";
177 configRoot["@odata.context"] =
178 "/redfish/v1/$metadata#OemManager.Fan";
James Feist73df0db2019-03-25 15:29:35 -0700179 configRoot["Profile@Redfish.AllowableValues"] = supportedProfiles;
180
181 if (!currentProfile.empty())
182 {
183 configRoot["Profile"] = currentProfile;
184 }
185 BMCWEB_LOG_ERROR << "profile = " << currentProfile << " !";
James Feist5b4aa862018-08-16 14:07:01 -0700186
James Feist5b4aa862018-08-16 14:07:01 -0700187 for (const auto& pathPair : managedObj)
188 {
189 for (const auto& intfPair : pathPair.second)
190 {
191 if (intfPair.first != pidConfigurationIface &&
James Feistb7a08d02018-12-11 14:55:37 -0800192 intfPair.first != pidZoneConfigurationIface &&
193 intfPair.first != stepwiseConfigurationIface)
James Feist5b4aa862018-08-16 14:07:01 -0700194 {
195 continue;
196 }
197 auto findName = intfPair.second.find("Name");
198 if (findName == intfPair.second.end())
199 {
200 BMCWEB_LOG_ERROR << "Pid Field missing Name";
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800201 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700202 return;
203 }
James Feist73df0db2019-03-25 15:29:35 -0700204
James Feist5b4aa862018-08-16 14:07:01 -0700205 const std::string* namePtr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800206 std::get_if<std::string>(&findName->second);
James Feist5b4aa862018-08-16 14:07:01 -0700207 if (namePtr == nullptr)
208 {
209 BMCWEB_LOG_ERROR << "Pid Name Field illegal";
James Feistb7a08d02018-12-11 14:55:37 -0800210 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700211 return;
212 }
James Feist5b4aa862018-08-16 14:07:01 -0700213 std::string name = *namePtr;
214 dbus::utility::escapePathForDbus(name);
James Feist73df0db2019-03-25 15:29:35 -0700215
216 auto findProfiles = intfPair.second.find("Profiles");
217 if (findProfiles != intfPair.second.end())
218 {
219 const std::vector<std::string>* profiles =
220 std::get_if<std::vector<std::string>>(
221 &findProfiles->second);
222 if (profiles == nullptr)
223 {
224 BMCWEB_LOG_ERROR << "Pid Profiles Field illegal";
225 messages::internalError(asyncResp->res);
226 return;
227 }
228 if (std::find(profiles->begin(), profiles->end(),
229 currentProfile) == profiles->end())
230 {
231 BMCWEB_LOG_INFO
232 << name << " not supported in current profile";
233 continue;
234 }
235 }
James Feistb7a08d02018-12-11 14:55:37 -0800236 nlohmann::json* config = nullptr;
James Feistc33a90e2019-03-01 10:17:44 -0800237
238 const std::string* classPtr = nullptr;
239 auto findClass = intfPair.second.find("Class");
240 if (findClass != intfPair.second.end())
241 {
242 classPtr = std::get_if<std::string>(&findClass->second);
243 }
244
James Feist5b4aa862018-08-16 14:07:01 -0700245 if (intfPair.first == pidZoneConfigurationIface)
246 {
247 std::string chassis;
248 if (!dbus::utility::getNthStringFromPath(
249 pathPair.first.str, 5, chassis))
250 {
251 chassis = "#IllegalValue";
252 }
253 nlohmann::json& zone = zones[name];
254 zone["Chassis"] = {
255 {"@odata.id", "/redfish/v1/Chassis/" + chassis}};
256 zone["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/"
257 "OpenBmc/Fan/FanZones/" +
258 name;
259 zone["@odata.type"] = "#OemManager.FanZone";
260 zone["@odata.context"] =
261 "/redfish/v1/$metadata#OemManager.FanZone";
James Feistb7a08d02018-12-11 14:55:37 -0800262 config = &zone;
James Feist5b4aa862018-08-16 14:07:01 -0700263 }
264
James Feistb7a08d02018-12-11 14:55:37 -0800265 else if (intfPair.first == stepwiseConfigurationIface)
266 {
James Feistc33a90e2019-03-01 10:17:44 -0800267 if (classPtr == nullptr)
268 {
269 BMCWEB_LOG_ERROR << "Pid Class Field illegal";
270 messages::internalError(asyncResp->res);
271 return;
272 }
273
James Feistb7a08d02018-12-11 14:55:37 -0800274 nlohmann::json& controller = stepwise[name];
275 config = &controller;
276
277 controller["@odata.id"] =
278 "/redfish/v1/Managers/bmc#/Oem/"
279 "OpenBmc/Fan/StepwiseControllers/" +
280 std::string(name);
281 controller["@odata.type"] =
282 "#OemManager.StepwiseController";
283
284 controller["@odata.context"] =
285 "/redfish/v1/"
286 "$metadata#OemManager.StepwiseController";
James Feistc33a90e2019-03-01 10:17:44 -0800287 controller["Direction"] = *classPtr;
James Feistb7a08d02018-12-11 14:55:37 -0800288 }
289
290 // pid and fans are off the same configuration
291 else if (intfPair.first == pidConfigurationIface)
292 {
James Feistc33a90e2019-03-01 10:17:44 -0800293
James Feistb7a08d02018-12-11 14:55:37 -0800294 if (classPtr == nullptr)
295 {
296 BMCWEB_LOG_ERROR << "Pid Class Field illegal";
297 messages::internalError(asyncResp->res);
298 return;
299 }
300 bool isFan = *classPtr == "fan";
301 nlohmann::json& element =
302 isFan ? fans[name] : pids[name];
303 config = &element;
304 if (isFan)
305 {
306 element["@odata.id"] =
307 "/redfish/v1/Managers/bmc#/Oem/"
308 "OpenBmc/Fan/FanControllers/" +
309 std::string(name);
310 element["@odata.type"] =
311 "#OemManager.FanController";
312
313 element["@odata.context"] =
314 "/redfish/v1/"
315 "$metadata#OemManager.FanController";
316 }
317 else
318 {
319 element["@odata.id"] =
320 "/redfish/v1/Managers/bmc#/Oem/"
321 "OpenBmc/Fan/PidControllers/" +
322 std::string(name);
323 element["@odata.type"] =
324 "#OemManager.PidController";
325 element["@odata.context"] =
326 "/redfish/v1/$metadata"
327 "#OemManager.PidController";
328 }
329 }
330 else
331 {
332 BMCWEB_LOG_ERROR << "Unexpected configuration";
333 messages::internalError(asyncResp->res);
334 return;
335 }
336
337 // used for making maps out of 2 vectors
338 const std::vector<double>* keys = nullptr;
339 const std::vector<double>* values = nullptr;
340
James Feist5b4aa862018-08-16 14:07:01 -0700341 for (const auto& propertyPair : intfPair.second)
342 {
343 if (propertyPair.first == "Type" ||
344 propertyPair.first == "Class" ||
345 propertyPair.first == "Name")
346 {
347 continue;
348 }
349
350 // zones
351 if (intfPair.first == pidZoneConfigurationIface)
352 {
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800353 const double* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800354 std::get_if<double>(&propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700355 if (ptr == nullptr)
356 {
357 BMCWEB_LOG_ERROR << "Field Illegal "
358 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700359 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700360 return;
361 }
James Feistb7a08d02018-12-11 14:55:37 -0800362 (*config)[propertyPair.first] = *ptr;
363 }
364
365 if (intfPair.first == stepwiseConfigurationIface)
366 {
367 if (propertyPair.first == "Reading" ||
368 propertyPair.first == "Output")
369 {
370 const std::vector<double>* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800371 std::get_if<std::vector<double>>(
James Feistb7a08d02018-12-11 14:55:37 -0800372 &propertyPair.second);
373
374 if (ptr == nullptr)
375 {
376 BMCWEB_LOG_ERROR << "Field Illegal "
377 << propertyPair.first;
378 messages::internalError(asyncResp->res);
379 return;
380 }
381
382 if (propertyPair.first == "Reading")
383 {
384 keys = ptr;
385 }
386 else
387 {
388 values = ptr;
389 }
390 if (keys && values)
391 {
392 if (keys->size() != values->size())
393 {
394 BMCWEB_LOG_ERROR
395 << "Reading and Output size don't "
396 "match ";
397 messages::internalError(asyncResp->res);
398 return;
399 }
400 nlohmann::json& steps = (*config)["Steps"];
401 steps = nlohmann::json::array();
402 for (size_t ii = 0; ii < keys->size(); ii++)
403 {
404 steps.push_back(
405 {{"Target", (*keys)[ii]},
406 {"Output", (*values)[ii]}});
407 }
408 }
409 }
410 if (propertyPair.first == "NegativeHysteresis" ||
411 propertyPair.first == "PositiveHysteresis")
412 {
413 const double* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800414 std::get_if<double>(&propertyPair.second);
James Feistb7a08d02018-12-11 14:55:37 -0800415 if (ptr == nullptr)
416 {
417 BMCWEB_LOG_ERROR << "Field Illegal "
418 << propertyPair.first;
419 messages::internalError(asyncResp->res);
420 return;
421 }
422 (*config)[propertyPair.first] = *ptr;
423 }
James Feist5b4aa862018-08-16 14:07:01 -0700424 }
425
426 // pid and fans are off the same configuration
James Feistb7a08d02018-12-11 14:55:37 -0800427 if (intfPair.first == pidConfigurationIface ||
428 intfPair.first == stepwiseConfigurationIface)
James Feist5b4aa862018-08-16 14:07:01 -0700429 {
James Feist5b4aa862018-08-16 14:07:01 -0700430
431 if (propertyPair.first == "Zones")
432 {
433 const std::vector<std::string>* inputs =
Ed Tanousabf2add2019-01-22 16:40:12 -0800434 std::get_if<std::vector<std::string>>(
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800435 &propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700436
437 if (inputs == nullptr)
438 {
439 BMCWEB_LOG_ERROR
440 << "Zones Pid Field Illegal";
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800441 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700442 return;
443 }
James Feistb7a08d02018-12-11 14:55:37 -0800444 auto& data = (*config)[propertyPair.first];
James Feist5b4aa862018-08-16 14:07:01 -0700445 data = nlohmann::json::array();
446 for (std::string itemCopy : *inputs)
447 {
448 dbus::utility::escapePathForDbus(itemCopy);
449 data.push_back(
450 {{"@odata.id",
451 "/redfish/v1/Managers/bmc#/Oem/"
452 "OpenBmc/Fan/FanZones/" +
453 itemCopy}});
454 }
455 }
456 // todo(james): may never happen, but this
457 // assumes configuration data referenced in the
458 // PID config is provided by the same daemon, we
459 // could add another loop to cover all cases,
460 // but I'm okay kicking this can down the road a
461 // bit
462
463 else if (propertyPair.first == "Inputs" ||
464 propertyPair.first == "Outputs")
465 {
James Feistb7a08d02018-12-11 14:55:37 -0800466 auto& data = (*config)[propertyPair.first];
James Feist5b4aa862018-08-16 14:07:01 -0700467 const std::vector<std::string>* inputs =
Ed Tanousabf2add2019-01-22 16:40:12 -0800468 std::get_if<std::vector<std::string>>(
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800469 &propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700470
471 if (inputs == nullptr)
472 {
473 BMCWEB_LOG_ERROR << "Field Illegal "
474 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700475 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700476 return;
477 }
478 data = *inputs;
479 } // doubles
480 else if (propertyPair.first ==
481 "FFGainCoefficient" ||
482 propertyPair.first == "FFOffCoefficient" ||
483 propertyPair.first == "ICoefficient" ||
484 propertyPair.first == "ILimitMax" ||
485 propertyPair.first == "ILimitMin" ||
James Feistaad1a252019-02-19 10:13:52 -0800486 propertyPair.first ==
487 "PositiveHysteresis" ||
488 propertyPair.first ==
489 "NegativeHysteresis" ||
James Feist5b4aa862018-08-16 14:07:01 -0700490 propertyPair.first == "OutLimitMax" ||
491 propertyPair.first == "OutLimitMin" ||
492 propertyPair.first == "PCoefficient" ||
James Feist7625cb82019-01-23 11:58:21 -0800493 propertyPair.first == "SetPoint" ||
James Feist5b4aa862018-08-16 14:07:01 -0700494 propertyPair.first == "SlewNeg" ||
495 propertyPair.first == "SlewPos")
496 {
497 const double* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800498 std::get_if<double>(&propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700499 if (ptr == nullptr)
500 {
501 BMCWEB_LOG_ERROR << "Field Illegal "
502 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700503 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700504 return;
505 }
James Feistb7a08d02018-12-11 14:55:37 -0800506 (*config)[propertyPair.first] = *ptr;
James Feist5b4aa862018-08-16 14:07:01 -0700507 }
508 }
509 }
510 }
511 }
512 },
513 connection, path, objectManagerIface, "GetManagedObjects");
514}
Jennifer Leeca537922018-08-10 10:07:30 -0700515
James Feist83ff9ab2018-08-31 10:18:24 -0700516enum class CreatePIDRet
517{
518 fail,
519 del,
520 patch
521};
522
James Feist5f2caae2018-12-12 14:08:25 -0800523static bool getZonesFromJsonReq(const std::shared_ptr<AsyncResp>& response,
524 std::vector<nlohmann::json>& config,
525 std::vector<std::string>& zones)
526{
James Feistb6baeaa2019-02-21 10:41:40 -0800527 if (config.empty())
528 {
529 BMCWEB_LOG_ERROR << "Empty Zones";
530 messages::propertyValueFormatError(response->res,
531 nlohmann::json::array(), "Zones");
532 return false;
533 }
James Feist5f2caae2018-12-12 14:08:25 -0800534 for (auto& odata : config)
535 {
536 std::string path;
537 if (!redfish::json_util::readJson(odata, response->res, "@odata.id",
538 path))
539 {
540 return false;
541 }
542 std::string input;
James Feist61adbda2019-03-25 13:03:51 -0700543
544 // 8 below comes from
545 // /redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones/Left
546 // 0 1 2 3 4 5 6 7 8
547 if (!dbus::utility::getNthStringFromPath(path, 8, input))
James Feist5f2caae2018-12-12 14:08:25 -0800548 {
549 BMCWEB_LOG_ERROR << "Got invalid path " << path;
550 BMCWEB_LOG_ERROR << "Illegal Type Zones";
551 messages::propertyValueFormatError(response->res, odata.dump(),
552 "Zones");
553 return false;
554 }
555 boost::replace_all(input, "_", " ");
556 zones.emplace_back(std::move(input));
557 }
558 return true;
559}
560
James Feist73df0db2019-03-25 15:29:35 -0700561static const dbus::utility::ManagedItem*
562 findChassis(const dbus::utility::ManagedObjectType& managedObj,
563 const std::string& value, std::string& chassis)
James Feistb6baeaa2019-02-21 10:41:40 -0800564{
565 BMCWEB_LOG_DEBUG << "Find Chassis: " << value << "\n";
566
567 std::string escaped = boost::replace_all_copy(value, " ", "_");
568 escaped = "/" + escaped;
569 auto it = std::find_if(
570 managedObj.begin(), managedObj.end(), [&escaped](const auto& obj) {
571 if (boost::algorithm::ends_with(obj.first.str, escaped))
572 {
573 BMCWEB_LOG_DEBUG << "Matched " << obj.first.str << "\n";
574 return true;
575 }
576 return false;
577 });
578
579 if (it == managedObj.end())
580 {
James Feist73df0db2019-03-25 15:29:35 -0700581 return nullptr;
James Feistb6baeaa2019-02-21 10:41:40 -0800582 }
583 // 5 comes from <chassis-name> being the 5th element
584 // /xyz/openbmc_project/inventory/system/chassis/<chassis-name>
James Feist73df0db2019-03-25 15:29:35 -0700585 if (dbus::utility::getNthStringFromPath(it->first.str, 5, chassis))
586 {
587 return &(*it);
588 }
589
590 return nullptr;
James Feistb6baeaa2019-02-21 10:41:40 -0800591}
592
James Feist83ff9ab2018-08-31 10:18:24 -0700593static CreatePIDRet createPidInterface(
594 const std::shared_ptr<AsyncResp>& response, const std::string& type,
James Feistb6baeaa2019-02-21 10:41:40 -0800595 nlohmann::json::iterator it, const std::string& path,
James Feist83ff9ab2018-08-31 10:18:24 -0700596 const dbus::utility::ManagedObjectType& managedObj, bool createNewObject,
597 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>&
598 output,
James Feist73df0db2019-03-25 15:29:35 -0700599 std::string& chassis, const std::string& profile)
James Feist83ff9ab2018-08-31 10:18:24 -0700600{
601
James Feist5f2caae2018-12-12 14:08:25 -0800602 // common deleter
James Feistb6baeaa2019-02-21 10:41:40 -0800603 if (it.value() == nullptr)
James Feist5f2caae2018-12-12 14:08:25 -0800604 {
605 std::string iface;
606 if (type == "PidControllers" || type == "FanControllers")
607 {
608 iface = pidConfigurationIface;
609 }
610 else if (type == "FanZones")
611 {
612 iface = pidZoneConfigurationIface;
613 }
614 else if (type == "StepwiseControllers")
615 {
616 iface = stepwiseConfigurationIface;
617 }
618 else
619 {
620 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Type "
621 << type;
622 messages::propertyUnknown(response->res, type);
623 return CreatePIDRet::fail;
624 }
625 // delete interface
626 crow::connections::systemBus->async_method_call(
627 [response, path](const boost::system::error_code ec) {
628 if (ec)
629 {
630 BMCWEB_LOG_ERROR << "Error patching " << path << ": " << ec;
631 messages::internalError(response->res);
James Feistb6baeaa2019-02-21 10:41:40 -0800632 return;
James Feist5f2caae2018-12-12 14:08:25 -0800633 }
James Feistb6baeaa2019-02-21 10:41:40 -0800634 messages::success(response->res);
James Feist5f2caae2018-12-12 14:08:25 -0800635 },
636 "xyz.openbmc_project.EntityManager", path, iface, "Delete");
637 return CreatePIDRet::del;
638 }
639
James Feist73df0db2019-03-25 15:29:35 -0700640 const dbus::utility::ManagedItem* managedItem = nullptr;
James Feistb6baeaa2019-02-21 10:41:40 -0800641 if (!createNewObject)
642 {
643 // if we aren't creating a new object, we should be able to find it on
644 // d-bus
James Feist73df0db2019-03-25 15:29:35 -0700645 managedItem = findChassis(managedObj, it.key(), chassis);
646 if (managedItem == nullptr)
James Feistb6baeaa2019-02-21 10:41:40 -0800647 {
648 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
649 messages::invalidObject(response->res, it.key());
650 return CreatePIDRet::fail;
651 }
652 }
653
James Feist73df0db2019-03-25 15:29:35 -0700654 if (profile.size() &&
655 (type == "PidControllers" || type == "FanControllers" ||
656 type == "StepwiseControllers"))
657 {
658 if (managedItem == nullptr)
659 {
660 output["Profiles"] = std::vector<std::string>{profile};
661 }
662 else
663 {
664 std::string interface;
665 if (type == "StepwiseControllers")
666 {
667 interface = stepwiseConfigurationIface;
668 }
669 else
670 {
671 interface = pidConfigurationIface;
672 }
673 auto findConfig = managedItem->second.find(interface);
674 if (findConfig == managedItem->second.end())
675 {
676 BMCWEB_LOG_ERROR
677 << "Failed to find interface in managed object";
678 messages::internalError(response->res);
679 return CreatePIDRet::fail;
680 }
681 auto findProfiles = findConfig->second.find("Profiles");
682 if (findProfiles != findConfig->second.end())
683 {
684 const std::vector<std::string>* curProfiles =
685 std::get_if<std::vector<std::string>>(
686 &(findProfiles->second));
687 if (curProfiles == nullptr)
688 {
689 BMCWEB_LOG_ERROR << "Illegal profiles in managed object";
690 messages::internalError(response->res);
691 return CreatePIDRet::fail;
692 }
693 if (std::find(curProfiles->begin(), curProfiles->end(),
694 profile) == curProfiles->end())
695 {
696 std::vector<std::string> newProfiles = *curProfiles;
697 newProfiles.push_back(profile);
698 output["Profiles"] = newProfiles;
699 }
700 }
701 }
702 }
703
James Feist83ff9ab2018-08-31 10:18:24 -0700704 if (type == "PidControllers" || type == "FanControllers")
705 {
706 if (createNewObject)
707 {
708 output["Class"] = type == "PidControllers" ? std::string("temp")
709 : std::string("fan");
710 output["Type"] = std::string("Pid");
711 }
James Feist5f2caae2018-12-12 14:08:25 -0800712
713 std::optional<std::vector<nlohmann::json>> zones;
714 std::optional<std::vector<std::string>> inputs;
715 std::optional<std::vector<std::string>> outputs;
716 std::map<std::string, std::optional<double>> doubles;
717 if (!redfish::json_util::readJson(
James Feistb6baeaa2019-02-21 10:41:40 -0800718 it.value(), response->res, "Inputs", inputs, "Outputs", outputs,
James Feist5f2caae2018-12-12 14:08:25 -0800719 "Zones", zones, "FFGainCoefficient",
720 doubles["FFGainCoefficient"], "FFOffCoefficient",
721 doubles["FFOffCoefficient"], "ICoefficient",
722 doubles["ICoefficient"], "ILimitMax", doubles["ILimitMax"],
723 "ILimitMin", doubles["ILimitMin"], "OutLimitMax",
724 doubles["OutLimitMax"], "OutLimitMin", doubles["OutLimitMin"],
725 "PCoefficient", doubles["PCoefficient"], "SetPoint",
726 doubles["SetPoint"], "SlewNeg", doubles["SlewNeg"], "SlewPos",
James Feistaad1a252019-02-19 10:13:52 -0800727 doubles["SlewPos"], "PositiveHysteresis",
728 doubles["PositiveHysteresis"], "NegativeHysteresis",
729 doubles["NegativeHysteresis"]))
James Feist83ff9ab2018-08-31 10:18:24 -0700730 {
James Feist5f2caae2018-12-12 14:08:25 -0800731 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
James Feistb6baeaa2019-02-21 10:41:40 -0800732 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800733 return CreatePIDRet::fail;
James Feist83ff9ab2018-08-31 10:18:24 -0700734 }
James Feist5f2caae2018-12-12 14:08:25 -0800735 if (zones)
James Feist83ff9ab2018-08-31 10:18:24 -0700736 {
James Feist5f2caae2018-12-12 14:08:25 -0800737 std::vector<std::string> zonesStr;
738 if (!getZonesFromJsonReq(response, *zones, zonesStr))
James Feist83ff9ab2018-08-31 10:18:24 -0700739 {
James Feist5f2caae2018-12-12 14:08:25 -0800740 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Zones";
741 return CreatePIDRet::fail;
James Feist83ff9ab2018-08-31 10:18:24 -0700742 }
James Feistb6baeaa2019-02-21 10:41:40 -0800743 if (chassis.empty() &&
744 !findChassis(managedObj, zonesStr[0], chassis))
745 {
746 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
747 messages::invalidObject(response->res, it.key());
748 return CreatePIDRet::fail;
749 }
750
James Feist5f2caae2018-12-12 14:08:25 -0800751 output["Zones"] = std::move(zonesStr);
752 }
753 if (inputs || outputs)
754 {
755 std::array<std::optional<std::vector<std::string>>*, 2> containers =
756 {&inputs, &outputs};
757 size_t index = 0;
758 for (const auto& containerPtr : containers)
James Feist83ff9ab2018-08-31 10:18:24 -0700759 {
James Feist5f2caae2018-12-12 14:08:25 -0800760 std::optional<std::vector<std::string>>& container =
761 *containerPtr;
762 if (!container)
James Feist83ff9ab2018-08-31 10:18:24 -0700763 {
James Feist5f2caae2018-12-12 14:08:25 -0800764 index++;
765 continue;
James Feist83ff9ab2018-08-31 10:18:24 -0700766 }
James Feist5f2caae2018-12-12 14:08:25 -0800767
768 for (std::string& value : *container)
James Feist83ff9ab2018-08-31 10:18:24 -0700769 {
James Feist5f2caae2018-12-12 14:08:25 -0800770 boost::replace_all(value, "_", " ");
James Feist83ff9ab2018-08-31 10:18:24 -0700771 }
James Feist5f2caae2018-12-12 14:08:25 -0800772 std::string key;
773 if (index == 0)
James Feist83ff9ab2018-08-31 10:18:24 -0700774 {
James Feist5f2caae2018-12-12 14:08:25 -0800775 key = "Inputs";
James Feist83ff9ab2018-08-31 10:18:24 -0700776 }
James Feist5f2caae2018-12-12 14:08:25 -0800777 else
778 {
779 key = "Outputs";
780 }
781 output[key] = *container;
782 index++;
James Feist83ff9ab2018-08-31 10:18:24 -0700783 }
James Feist5f2caae2018-12-12 14:08:25 -0800784 }
James Feist83ff9ab2018-08-31 10:18:24 -0700785
James Feist5f2caae2018-12-12 14:08:25 -0800786 // doubles
787 for (const auto& pairs : doubles)
788 {
789 if (!pairs.second)
James Feist83ff9ab2018-08-31 10:18:24 -0700790 {
James Feist5f2caae2018-12-12 14:08:25 -0800791 continue;
James Feist83ff9ab2018-08-31 10:18:24 -0700792 }
James Feist5f2caae2018-12-12 14:08:25 -0800793 BMCWEB_LOG_DEBUG << pairs.first << " = " << *pairs.second;
794 output[pairs.first] = *(pairs.second);
James Feist83ff9ab2018-08-31 10:18:24 -0700795 }
796 }
James Feist5f2caae2018-12-12 14:08:25 -0800797
James Feist83ff9ab2018-08-31 10:18:24 -0700798 else if (type == "FanZones")
799 {
James Feist83ff9ab2018-08-31 10:18:24 -0700800 output["Type"] = std::string("Pid.Zone");
801
James Feist5f2caae2018-12-12 14:08:25 -0800802 std::optional<nlohmann::json> chassisContainer;
803 std::optional<double> failSafePercent;
James Feistd3ec07f2019-02-25 14:51:15 -0800804 std::optional<double> minThermalOutput;
James Feistb6baeaa2019-02-21 10:41:40 -0800805 if (!redfish::json_util::readJson(it.value(), response->res, "Chassis",
James Feist5f2caae2018-12-12 14:08:25 -0800806 chassisContainer, "FailSafePercent",
James Feistd3ec07f2019-02-25 14:51:15 -0800807 failSafePercent, "MinThermalOutput",
808 minThermalOutput))
James Feist83ff9ab2018-08-31 10:18:24 -0700809 {
James Feist5f2caae2018-12-12 14:08:25 -0800810 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
James Feistb6baeaa2019-02-21 10:41:40 -0800811 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800812 return CreatePIDRet::fail;
813 }
James Feist83ff9ab2018-08-31 10:18:24 -0700814
James Feist5f2caae2018-12-12 14:08:25 -0800815 if (chassisContainer)
816 {
817
818 std::string chassisId;
819 if (!redfish::json_util::readJson(*chassisContainer, response->res,
820 "@odata.id", chassisId))
James Feist83ff9ab2018-08-31 10:18:24 -0700821 {
James Feist5f2caae2018-12-12 14:08:25 -0800822 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
823 << chassisContainer->dump();
James Feist83ff9ab2018-08-31 10:18:24 -0700824 return CreatePIDRet::fail;
825 }
James Feist5f2caae2018-12-12 14:08:25 -0800826
827 // /refish/v1/chassis/chassis_name/
828 if (!dbus::utility::getNthStringFromPath(chassisId, 3, chassis))
829 {
830 BMCWEB_LOG_ERROR << "Got invalid path " << chassisId;
831 messages::invalidObject(response->res, chassisId);
832 return CreatePIDRet::fail;
833 }
834 }
James Feistd3ec07f2019-02-25 14:51:15 -0800835 if (minThermalOutput)
James Feist5f2caae2018-12-12 14:08:25 -0800836 {
James Feistd3ec07f2019-02-25 14:51:15 -0800837 output["MinThermalOutput"] = *minThermalOutput;
James Feist5f2caae2018-12-12 14:08:25 -0800838 }
839 if (failSafePercent)
840 {
841 output["FailSafePercent"] = *failSafePercent;
842 }
843 }
844 else if (type == "StepwiseControllers")
845 {
846 output["Type"] = std::string("Stepwise");
847
848 std::optional<std::vector<nlohmann::json>> zones;
849 std::optional<std::vector<nlohmann::json>> steps;
850 std::optional<std::vector<std::string>> inputs;
851 std::optional<double> positiveHysteresis;
852 std::optional<double> negativeHysteresis;
James Feistc33a90e2019-03-01 10:17:44 -0800853 std::optional<std::string> direction; // upper clipping curve vs lower
James Feist5f2caae2018-12-12 14:08:25 -0800854 if (!redfish::json_util::readJson(
James Feistb6baeaa2019-02-21 10:41:40 -0800855 it.value(), response->res, "Zones", zones, "Steps", steps,
856 "Inputs", inputs, "PositiveHysteresis", positiveHysteresis,
James Feistc33a90e2019-03-01 10:17:44 -0800857 "NegativeHysteresis", negativeHysteresis, "Direction",
858 direction))
James Feist5f2caae2018-12-12 14:08:25 -0800859 {
860 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
James Feistb6baeaa2019-02-21 10:41:40 -0800861 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800862 return CreatePIDRet::fail;
863 }
864
865 if (zones)
866 {
James Feistb6baeaa2019-02-21 10:41:40 -0800867 std::vector<std::string> zonesStrs;
868 if (!getZonesFromJsonReq(response, *zones, zonesStrs))
James Feist5f2caae2018-12-12 14:08:25 -0800869 {
870 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Zones";
871 return CreatePIDRet::fail;
872 }
James Feistb6baeaa2019-02-21 10:41:40 -0800873 if (chassis.empty() &&
874 !findChassis(managedObj, zonesStrs[0], chassis))
875 {
876 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
877 messages::invalidObject(response->res, it.key());
878 return CreatePIDRet::fail;
879 }
880 output["Zones"] = std::move(zonesStrs);
James Feist5f2caae2018-12-12 14:08:25 -0800881 }
882 if (steps)
883 {
884 std::vector<double> readings;
885 std::vector<double> outputs;
886 for (auto& step : *steps)
887 {
888 double target;
Ed Tanousb01bf292019-03-25 19:25:26 +0000889 double output;
James Feist5f2caae2018-12-12 14:08:25 -0800890
891 if (!redfish::json_util::readJson(step, response->res, "Target",
Ed Tanousb01bf292019-03-25 19:25:26 +0000892 target, "Output", output))
James Feist5f2caae2018-12-12 14:08:25 -0800893 {
894 BMCWEB_LOG_ERROR << "Line:" << __LINE__
James Feistb6baeaa2019-02-21 10:41:40 -0800895 << ", Illegal Property "
896 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800897 return CreatePIDRet::fail;
898 }
899 readings.emplace_back(target);
Ed Tanousb01bf292019-03-25 19:25:26 +0000900 outputs.emplace_back(output);
James Feist5f2caae2018-12-12 14:08:25 -0800901 }
902 output["Reading"] = std::move(readings);
903 output["Output"] = std::move(outputs);
904 }
905 if (inputs)
906 {
907 for (std::string& value : *inputs)
908 {
James Feist5f2caae2018-12-12 14:08:25 -0800909 boost::replace_all(value, "_", " ");
910 }
911 output["Inputs"] = std::move(*inputs);
912 }
913 if (negativeHysteresis)
914 {
915 output["NegativeHysteresis"] = *negativeHysteresis;
916 }
917 if (positiveHysteresis)
918 {
919 output["PositiveHysteresis"] = *positiveHysteresis;
James Feist83ff9ab2018-08-31 10:18:24 -0700920 }
James Feistc33a90e2019-03-01 10:17:44 -0800921 if (direction)
922 {
923 constexpr const std::array<const char*, 2> allowedDirections = {
924 "Ceiling", "Floor"};
925 if (std::find(allowedDirections.begin(), allowedDirections.end(),
926 *direction) == allowedDirections.end())
927 {
928 messages::propertyValueTypeError(response->res, "Direction",
929 *direction);
930 return CreatePIDRet::fail;
931 }
932 output["Class"] = *direction;
933 }
James Feist83ff9ab2018-08-31 10:18:24 -0700934 }
935 else
936 {
James Feist5f2caae2018-12-12 14:08:25 -0800937 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Type " << type;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700938 messages::propertyUnknown(response->res, type);
James Feist83ff9ab2018-08-31 10:18:24 -0700939 return CreatePIDRet::fail;
940 }
941 return CreatePIDRet::patch;
942}
James Feist73df0db2019-03-25 15:29:35 -0700943struct GetPIDValues : std::enable_shared_from_this<GetPIDValues>
944{
945
946 GetPIDValues(const std::shared_ptr<AsyncResp>& asyncResp) :
947 asyncResp(asyncResp)
948
949 {
950 }
951
952 void run()
953 {
954 std::shared_ptr<GetPIDValues> self = shared_from_this();
955
956 // get all configurations
957 crow::connections::systemBus->async_method_call(
958 [self](const boost::system::error_code ec,
959 const crow::openbmc_mapper::GetSubTreeType& subtree) {
960 if (ec)
961 {
962 BMCWEB_LOG_ERROR << ec;
963 messages::internalError(self->asyncResp->res);
964 return;
965 }
966 self->subtree = subtree;
967 },
968 "xyz.openbmc_project.ObjectMapper",
969 "/xyz/openbmc_project/object_mapper",
970 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
971 std::array<const char*, 4>{
972 pidConfigurationIface, pidZoneConfigurationIface,
973 objectManagerIface, stepwiseConfigurationIface});
974
975 // at the same time get the selected profile
976 crow::connections::systemBus->async_method_call(
977 [self](const boost::system::error_code ec,
978 const crow::openbmc_mapper::GetSubTreeType& subtree) {
979 if (ec || subtree.empty())
980 {
981 return;
982 }
983 if (subtree[0].second.size() != 1)
984 {
985 // invalid mapper response, should never happen
986 BMCWEB_LOG_ERROR << "GetPIDValues: Mapper Error";
987 messages::internalError(self->asyncResp->res);
988 return;
989 }
990
991 const std::string& path = subtree[0].first;
992 const std::string& owner = subtree[0].second[0].first;
993 crow::connections::systemBus->async_method_call(
994 [path, owner, self](
995 const boost::system::error_code ec,
996 const boost::container::flat_map<
997 std::string, std::variant<std::vector<std::string>,
998 std::string>>& resp) {
999 if (ec)
1000 {
1001 BMCWEB_LOG_ERROR << "GetPIDValues: Can't get "
1002 "thermalModeIface "
1003 << path;
1004 messages::internalError(self->asyncResp->res);
1005 return;
1006 }
1007 const std::string* current;
1008 const std::vector<std::string>* supported;
1009 for (auto& [key, value] : resp)
1010 {
1011 if (key == "Current")
1012 {
1013 current = std::get_if<std::string>(&value);
1014 if (current == nullptr)
1015 {
1016 BMCWEB_LOG_ERROR
1017 << "GetPIDValues: thermal mode "
1018 "iface invalid "
1019 << path;
1020 messages::internalError(
1021 self->asyncResp->res);
1022 return;
1023 }
1024 }
1025 if (key == "Supported")
1026 {
1027 supported =
1028 std::get_if<std::vector<std::string>>(
1029 &value);
1030 if (supported == nullptr)
1031 {
1032 BMCWEB_LOG_ERROR
1033 << "GetPIDValues: thermal mode "
1034 "iface invalid"
1035 << path;
1036 messages::internalError(
1037 self->asyncResp->res);
1038 return;
1039 }
1040 }
1041 }
1042 if (current == nullptr || supported == nullptr)
1043 {
1044 BMCWEB_LOG_ERROR << "GetPIDValues: thermal mode "
1045 "iface invalid "
1046 << path;
1047 messages::internalError(self->asyncResp->res);
1048 return;
1049 }
1050 self->currentProfile = *current;
1051 self->supportedProfiles = *supported;
1052 },
1053 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1054 thermalModeIface);
1055 },
1056 "xyz.openbmc_project.ObjectMapper",
1057 "/xyz/openbmc_project/object_mapper",
1058 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
1059 std::array<const char*, 1>{thermalModeIface});
1060 }
1061
1062 ~GetPIDValues()
1063 {
1064 if (asyncResp->res.result() != boost::beast::http::status::ok)
1065 {
1066 return;
1067 }
1068 // create map of <connection, path to objMgr>>
1069 boost::container::flat_map<std::string, std::string> objectMgrPaths;
1070 boost::container::flat_set<std::string> calledConnections;
1071 for (const auto& pathGroup : subtree)
1072 {
1073 for (const auto& connectionGroup : pathGroup.second)
1074 {
1075 auto findConnection =
1076 calledConnections.find(connectionGroup.first);
1077 if (findConnection != calledConnections.end())
1078 {
1079 break;
1080 }
1081 for (const std::string& interface : connectionGroup.second)
1082 {
1083 if (interface == objectManagerIface)
1084 {
1085 objectMgrPaths[connectionGroup.first] = pathGroup.first;
1086 }
1087 // this list is alphabetical, so we
1088 // should have found the objMgr by now
1089 if (interface == pidConfigurationIface ||
1090 interface == pidZoneConfigurationIface ||
1091 interface == stepwiseConfigurationIface)
1092 {
1093 auto findObjMgr =
1094 objectMgrPaths.find(connectionGroup.first);
1095 if (findObjMgr == objectMgrPaths.end())
1096 {
1097 BMCWEB_LOG_DEBUG << connectionGroup.first
1098 << "Has no Object Manager";
1099 continue;
1100 }
1101
1102 calledConnections.insert(connectionGroup.first);
1103
1104 asyncPopulatePid(findObjMgr->first, findObjMgr->second,
1105 currentProfile, supportedProfiles,
1106 asyncResp);
1107 break;
1108 }
1109 }
1110 }
1111 }
1112 }
1113
1114 std::vector<std::string> supportedProfiles;
1115 std::string currentProfile;
1116 crow::openbmc_mapper::GetSubTreeType subtree;
1117 std::shared_ptr<AsyncResp> asyncResp;
1118};
1119
1120struct SetPIDValues : std::enable_shared_from_this<SetPIDValues>
1121{
1122
1123 SetPIDValues(const std::shared_ptr<AsyncResp>& asyncResp,
1124 nlohmann::json& data) :
1125 asyncResp(asyncResp)
1126 {
1127
1128 std::optional<nlohmann::json> pidControllers;
1129 std::optional<nlohmann::json> fanControllers;
1130 std::optional<nlohmann::json> fanZones;
1131 std::optional<nlohmann::json> stepwiseControllers;
1132
1133 if (!redfish::json_util::readJson(
1134 data, asyncResp->res, "PidControllers", pidControllers,
1135 "FanControllers", fanControllers, "FanZones", fanZones,
1136 "StepwiseControllers", stepwiseControllers, "Profile", profile))
1137 {
1138 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
1139 << data.dump();
1140 return;
1141 }
1142 configuration.emplace_back("PidControllers", std::move(pidControllers));
1143 configuration.emplace_back("FanControllers", std::move(fanControllers));
1144 configuration.emplace_back("FanZones", std::move(fanZones));
1145 configuration.emplace_back("StepwiseControllers",
1146 std::move(stepwiseControllers));
1147 }
1148 void run()
1149 {
1150 if (asyncResp->res.result() != boost::beast::http::status::ok)
1151 {
1152 return;
1153 }
1154
1155 std::shared_ptr<SetPIDValues> self = shared_from_this();
1156
1157 // todo(james): might make sense to do a mapper call here if this
1158 // interface gets more traction
1159 crow::connections::systemBus->async_method_call(
1160 [self](const boost::system::error_code ec,
1161 dbus::utility::ManagedObjectType& managedObj) {
1162 if (ec)
1163 {
1164 BMCWEB_LOG_ERROR << "Error communicating to Entity Manager";
1165 messages::internalError(self->asyncResp->res);
1166 return;
1167 }
1168 self->managedObj = std::move(managedObj);
1169 },
1170 "xyz.openbmc_project.EntityManager", "/", objectManagerIface,
1171 "GetManagedObjects");
1172
1173 // at the same time get the profile information
1174 crow::connections::systemBus->async_method_call(
1175 [self](const boost::system::error_code ec,
1176 const crow::openbmc_mapper::GetSubTreeType& subtree) {
1177 if (ec || subtree.empty())
1178 {
1179 return;
1180 }
1181 if (subtree[0].second.empty())
1182 {
1183 // invalid mapper response, should never happen
1184 BMCWEB_LOG_ERROR << "SetPIDValues: Mapper Error";
1185 messages::internalError(self->asyncResp->res);
1186 return;
1187 }
1188
1189 const std::string& path = subtree[0].first;
1190 const std::string& owner = subtree[0].second[0].first;
1191 crow::connections::systemBus->async_method_call(
1192 [self, path, owner](
1193 const boost::system::error_code ec,
1194 const boost::container::flat_map<
1195 std::string, std::variant<std::vector<std::string>,
1196 std::string>>& resp) {
1197 if (ec)
1198 {
1199 BMCWEB_LOG_ERROR << "SetPIDValues: Can't get "
1200 "thermalModeIface "
1201 << path;
1202 messages::internalError(self->asyncResp->res);
1203 return;
1204 }
1205 const std::string* current;
1206 const std::vector<std::string>* supported;
1207 for (auto& [key, value] : resp)
1208 {
1209 if (key == "Current")
1210 {
1211 current = std::get_if<std::string>(&value);
1212 if (current == nullptr)
1213 {
1214 BMCWEB_LOG_ERROR
1215 << "SetPIDValues: thermal mode "
1216 "iface invalid "
1217 << path;
1218 messages::internalError(
1219 self->asyncResp->res);
1220 return;
1221 }
1222 }
1223 if (key == "Supported")
1224 {
1225 supported =
1226 std::get_if<std::vector<std::string>>(
1227 &value);
1228 if (supported == nullptr)
1229 {
1230 BMCWEB_LOG_ERROR
1231 << "SetPIDValues: thermal mode "
1232 "iface invalid"
1233 << path;
1234 messages::internalError(
1235 self->asyncResp->res);
1236 return;
1237 }
1238 }
1239 }
1240 if (current == nullptr || supported == nullptr)
1241 {
1242 BMCWEB_LOG_ERROR << "SetPIDValues: thermal mode "
1243 "iface invalid "
1244 << path;
1245 messages::internalError(self->asyncResp->res);
1246 return;
1247 }
1248 self->currentProfile = *current;
1249 self->supportedProfiles = *supported;
1250 self->profileConnection = owner;
1251 self->profilePath = path;
1252 },
1253 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1254 thermalModeIface);
1255 },
1256 "xyz.openbmc_project.ObjectMapper",
1257 "/xyz/openbmc_project/object_mapper",
1258 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
1259 std::array<const char*, 1>{thermalModeIface});
1260 }
1261 ~SetPIDValues()
1262 {
1263 if (asyncResp->res.result() != boost::beast::http::status::ok)
1264 {
1265 return;
1266 }
1267
1268 std::shared_ptr<AsyncResp> response = asyncResp;
1269
1270 if (profile)
1271 {
1272 if (std::find(supportedProfiles.begin(), supportedProfiles.end(),
1273 *profile) == supportedProfiles.end())
1274 {
1275 messages::actionParameterUnknown(response->res, "Profile",
1276 *profile);
1277 return;
1278 }
1279 currentProfile = *profile;
1280 crow::connections::systemBus->async_method_call(
1281 [response](const boost::system::error_code ec) {
1282 if (ec)
1283 {
1284 BMCWEB_LOG_ERROR << "Error patching profile" << ec;
1285 messages::internalError(response->res);
1286 }
1287 },
1288 profileConnection, profilePath,
1289 "org.freedesktop.DBus.Properties", "Set", thermalModeIface,
1290 "Current", std::variant<std::string>(*profile));
1291 }
1292
1293 for (auto& containerPair : configuration)
1294 {
1295 auto& container = containerPair.second;
1296 if (!container)
1297 {
1298 continue;
1299 }
1300 std::string& type = containerPair.first;
1301
1302 for (nlohmann::json::iterator it = container->begin();
1303 it != container->end(); it++)
1304 {
1305 const auto& name = it.key();
1306 auto pathItr =
1307 std::find_if(managedObj.begin(), managedObj.end(),
1308 [&name](const auto& obj) {
1309 return boost::algorithm::ends_with(
1310 obj.first.str, "/" + name);
1311 });
1312 boost::container::flat_map<std::string,
1313 dbus::utility::DbusVariantType>
1314 output;
1315
1316 output.reserve(16); // The pid interface length
1317
1318 // determines if we're patching entity-manager or
1319 // creating a new object
1320 bool createNewObject = (pathItr == managedObj.end());
1321 std::string iface;
1322 if (type == "PidControllers" || type == "FanControllers")
1323 {
1324 iface = pidConfigurationIface;
1325 if (!createNewObject &&
1326 pathItr->second.find(pidConfigurationIface) ==
1327 pathItr->second.end())
1328 {
1329 createNewObject = true;
1330 }
1331 }
1332 else if (type == "FanZones")
1333 {
1334 iface = pidZoneConfigurationIface;
1335 if (!createNewObject &&
1336 pathItr->second.find(pidZoneConfigurationIface) ==
1337 pathItr->second.end())
1338 {
1339
1340 createNewObject = true;
1341 }
1342 }
1343 else if (type == "StepwiseControllers")
1344 {
1345 iface = stepwiseConfigurationIface;
1346 if (!createNewObject &&
1347 pathItr->second.find(stepwiseConfigurationIface) ==
1348 pathItr->second.end())
1349 {
1350 createNewObject = true;
1351 }
1352 }
1353 BMCWEB_LOG_DEBUG << "Create new = " << createNewObject << "\n";
1354 output["Name"] = boost::replace_all_copy(name, "_", " ");
1355
1356 std::string chassis;
1357 CreatePIDRet ret = createPidInterface(
1358 response, type, it, pathItr->first.str, managedObj,
1359 createNewObject, output, chassis, currentProfile);
1360 if (ret == CreatePIDRet::fail)
1361 {
1362 return;
1363 }
1364 else if (ret == CreatePIDRet::del)
1365 {
1366 continue;
1367 }
1368
1369 if (!createNewObject)
1370 {
1371 for (const auto& property : output)
1372 {
1373 crow::connections::systemBus->async_method_call(
1374 [response,
1375 propertyName{std::string(property.first)}](
1376 const boost::system::error_code ec) {
1377 if (ec)
1378 {
1379 BMCWEB_LOG_ERROR << "Error patching "
1380 << propertyName << ": "
1381 << ec;
1382 messages::internalError(response->res);
1383 return;
1384 }
1385 messages::success(response->res);
1386 },
1387 "xyz.openbmc_project.EntityManager",
1388 pathItr->first.str,
1389 "org.freedesktop.DBus.Properties", "Set", iface,
1390 property.first, property.second);
1391 }
1392 }
1393 else
1394 {
1395 if (chassis.empty())
1396 {
1397 BMCWEB_LOG_ERROR << "Failed to get chassis from config";
1398 messages::invalidObject(response->res, name);
1399 return;
1400 }
1401
1402 bool foundChassis = false;
1403 for (const auto& obj : managedObj)
1404 {
1405 if (boost::algorithm::ends_with(obj.first.str, chassis))
1406 {
1407 chassis = obj.first.str;
1408 foundChassis = true;
1409 break;
1410 }
1411 }
1412 if (!foundChassis)
1413 {
1414 BMCWEB_LOG_ERROR << "Failed to find chassis on dbus";
1415 messages::resourceMissingAtURI(
1416 response->res, "/redfish/v1/Chassis/" + chassis);
1417 return;
1418 }
1419
1420 crow::connections::systemBus->async_method_call(
1421 [response](const boost::system::error_code ec) {
1422 if (ec)
1423 {
1424 BMCWEB_LOG_ERROR << "Error Adding Pid Object "
1425 << ec;
1426 messages::internalError(response->res);
1427 return;
1428 }
1429 messages::success(response->res);
1430 },
1431 "xyz.openbmc_project.EntityManager", chassis,
1432 "xyz.openbmc_project.AddObject", "AddObject", output);
1433 }
1434 }
1435 }
1436 }
1437 std::shared_ptr<AsyncResp> asyncResp;
1438 std::vector<std::pair<std::string, std::optional<nlohmann::json>>>
1439 configuration;
1440 std::optional<std::string> profile;
1441 dbus::utility::ManagedObjectType managedObj;
1442 std::vector<std::string> supportedProfiles;
1443 std::string currentProfile;
1444 std::string profileConnection;
1445 std::string profilePath;
1446};
James Feist83ff9ab2018-08-31 10:18:24 -07001447
Ed Tanous1abe55e2018-09-05 08:30:59 -07001448class Manager : public Node
1449{
1450 public:
James Feist5b4aa862018-08-16 14:07:01 -07001451 Manager(CrowApp& app) : Node(app, "/redfish/v1/Managers/bmc/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001452 {
Ed Tanous0f74e642018-11-12 15:17:05 -08001453 uuid = app.template getMiddleware<crow::persistent_data::Middleware>()
1454 .systemUuid;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001455 entityPrivileges = {
1456 {boost::beast::http::verb::get, {{"Login"}}},
1457 {boost::beast::http::verb::head, {{"Login"}}},
1458 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1459 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1460 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1461 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001462 }
1463
Ed Tanous1abe55e2018-09-05 08:30:59 -07001464 private:
James Feist5b4aa862018-08-16 14:07:01 -07001465 void doGet(crow::Response& res, const crow::Request& req,
1466 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -07001467 {
Ed Tanous0f74e642018-11-12 15:17:05 -08001468 res.jsonValue["@odata.id"] = "/redfish/v1/Managers/bmc";
1469 res.jsonValue["@odata.type"] = "#Manager.v1_3_0.Manager";
1470 res.jsonValue["@odata.context"] =
1471 "/redfish/v1/$metadata#Manager.Manager";
1472 res.jsonValue["Id"] = "bmc";
1473 res.jsonValue["Name"] = "OpenBmc Manager";
1474 res.jsonValue["Description"] = "Baseboard Management Controller";
1475 res.jsonValue["PowerState"] = "On";
Ed Tanous029573d2019-02-01 10:57:49 -08001476 res.jsonValue["Status"] = {{"State", "Enabled"}, {"Health", "OK"}};
Ed Tanous0f74e642018-11-12 15:17:05 -08001477 res.jsonValue["ManagerType"] = "BMC";
Ed Tanous3602e232019-05-13 11:11:44 -07001478 res.jsonValue["UUID"] = systemd_utils::getUuid();
1479 res.jsonValue["ServiceEntryPointUUID"] = uuid;
Ed Tanous75176582018-12-14 08:14:34 -08001480 res.jsonValue["Model"] = "OpenBmc"; // TODO(ed), get model
Ed Tanous0f74e642018-11-12 15:17:05 -08001481
1482 res.jsonValue["LogServices"] = {
1483 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices"}};
1484
1485 res.jsonValue["NetworkProtocol"] = {
1486 {"@odata.id", "/redfish/v1/Managers/bmc/NetworkProtocol"}};
1487
1488 res.jsonValue["EthernetInterfaces"] = {
1489 {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces"}};
1490 // default oem data
1491 nlohmann::json& oem = res.jsonValue["Oem"];
1492 nlohmann::json& oemOpenbmc = oem["OpenBmc"];
1493 oem["@odata.type"] = "#OemManager.Oem";
1494 oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem";
1495 oem["@odata.context"] = "/redfish/v1/$metadata#OemManager.Oem";
1496 oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc";
1497 oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc";
1498 oemOpenbmc["@odata.context"] =
1499 "/redfish/v1/$metadata#OemManager.OpenBmc";
1500
Jennifer Leeed5befb2018-08-10 11:29:45 -07001501 // Update Actions object.
Ed Tanous0f74e642018-11-12 15:17:05 -08001502 nlohmann::json& manager_reset =
1503 res.jsonValue["Actions"]["#Manager.Reset"];
Jennifer Leeed5befb2018-08-10 11:29:45 -07001504 manager_reset["target"] =
1505 "/redfish/v1/Managers/bmc/Actions/Manager.Reset";
1506 manager_reset["ResetType@Redfish.AllowableValues"] = {
1507 "GracefulRestart"};
Jennifer Leeca537922018-08-10 10:07:30 -07001508
Andrew Geisslercb92c032018-08-17 07:56:14 -07001509 res.jsonValue["DateTime"] = crow::utility::dateTimeNow();
Santosh Puranik474bfad2019-04-02 16:00:09 +05301510
1511 // Fill in GraphicalConsole and SerialConsole info
1512 res.jsonValue["SerialConsole"]["ServiceEnabled"] = true;
1513 res.jsonValue["SerialConsole"]["ConnectTypesSupported"] = {"IPMI",
1514 "SSH"};
Santosh Puranikef47bb12019-04-30 10:28:52 +05301515#ifdef BMCWEB_ENABLE_KVM
1516 res.jsonValue["GraphicalConsole"]["ServiceEnabled"] = true;
1517 res.jsonValue["GraphicalConsole"]["ConnectTypesSupported"] = {"KVMIP"};
1518#endif // BMCWEB_ENABLE_KVM
Santosh Puranik474bfad2019-04-02 16:00:09 +05301519
Gunnar Mills603a6642019-01-21 17:03:51 -06001520 res.jsonValue["Links"]["ManagerForServers@odata.count"] = 1;
1521 res.jsonValue["Links"]["ManagerForServers"] = {
1522 {{"@odata.id", "/redfish/v1/Systems/system"}}};
Shawn McCarney26f03892019-05-03 13:20:24 -05001523
Jennifer Leeed5befb2018-08-10 11:29:45 -07001524 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
James Feist5b4aa862018-08-16 14:07:01 -07001525
James Feistb49ac872019-05-21 15:12:01 -07001526 auto health = std::make_shared<HealthPopulate>(asyncResp);
1527 health->isManagersHealth = true;
1528 health->populate();
1529
Jennifer Leeca537922018-08-10 10:07:30 -07001530 crow::connections::systemBus->async_method_call(
1531 [asyncResp](const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -07001532 const dbus::utility::ManagedObjectType& resp) {
Jennifer Leeca537922018-08-10 10:07:30 -07001533 if (ec)
1534 {
1535 BMCWEB_LOG_ERROR << "Error while getting Software Version";
Jason M. Billsf12894f2018-10-09 12:45:45 -07001536 messages::internalError(asyncResp->res);
Jennifer Leeca537922018-08-10 10:07:30 -07001537 return;
1538 }
1539
James Feist5b4aa862018-08-16 14:07:01 -07001540 for (auto& objpath : resp)
Jennifer Leeca537922018-08-10 10:07:30 -07001541 {
James Feist5b4aa862018-08-16 14:07:01 -07001542 for (auto& interface : objpath.second)
Jennifer Leeca537922018-08-10 10:07:30 -07001543 {
James Feist5f2caae2018-12-12 14:08:25 -08001544 // If interface is
1545 // xyz.openbmc_project.Software.Version, this is
1546 // what we're looking for.
Jennifer Leeca537922018-08-10 10:07:30 -07001547 if (interface.first ==
1548 "xyz.openbmc_project.Software.Version")
1549 {
1550 // Cut out everyting until last "/", ...
James Feist5b4aa862018-08-16 14:07:01 -07001551 for (auto& property : interface.second)
Jennifer Leeca537922018-08-10 10:07:30 -07001552 {
1553 if (property.first == "Version")
1554 {
James Feist5b4aa862018-08-16 14:07:01 -07001555 const std::string* value =
Ed Tanousabf2add2019-01-22 16:40:12 -08001556 std::get_if<std::string>(
1557 &property.second);
Jennifer Leeca537922018-08-10 10:07:30 -07001558 if (value == nullptr)
1559 {
1560 continue;
1561 }
1562 asyncResp->res
1563 .jsonValue["FirmwareVersion"] = *value;
1564 }
1565 }
1566 }
1567 }
1568 }
1569 },
1570 "xyz.openbmc_project.Software.BMC.Updater",
1571 "/xyz/openbmc_project/software",
1572 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
James Feist0f6b00b2019-06-10 14:15:53 -07001573
James Feist73df0db2019-03-25 15:29:35 -07001574 auto pids = std::make_shared<GetPIDValues>(asyncResp);
1575 pids->run();
Jennifer Leec5d03ff2019-03-08 15:42:58 -08001576
1577 getMainChassisId(asyncResp, [](const std::string& chassisId,
1578 const std::shared_ptr<AsyncResp> aRsp) {
1579 aRsp->res.jsonValue["Links"]["ManagerForChassis@odata.count"] = 1;
1580 aRsp->res.jsonValue["Links"]["ManagerForChassis"] = {
1581 {{"@odata.id", "/redfish/v1/Chassis/" + chassisId}}};
1582 });
James Feist0f6b00b2019-06-10 14:15:53 -07001583
1584 static bool started = false;
1585
1586 if (!started)
1587 {
1588 crow::connections::systemBus->async_method_call(
1589 [asyncResp](const boost::system::error_code ec,
1590 const std::variant<double>& resp) {
1591 if (ec)
1592 {
1593 BMCWEB_LOG_ERROR << "Error while getting progress";
1594 messages::internalError(asyncResp->res);
1595 return;
1596 }
1597 const double* val = std::get_if<double>(&resp);
1598 if (val == nullptr)
1599 {
1600 BMCWEB_LOG_ERROR
1601 << "Invalid response while getting progress";
1602 messages::internalError(asyncResp->res);
1603 return;
1604 }
1605 if (*val < 1.0)
1606 {
1607 asyncResp->res.jsonValue["Status"]["State"] =
1608 "Starting";
1609 started = true;
1610 }
1611 },
1612 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1613 "org.freedesktop.DBus.Properties", "Get",
1614 "org.freedesktop.systemd1.Manager", "Progress");
1615 }
James Feist83ff9ab2018-08-31 10:18:24 -07001616 }
James Feist5b4aa862018-08-16 14:07:01 -07001617
1618 void doPatch(crow::Response& res, const crow::Request& req,
1619 const std::vector<std::string>& params) override
1620 {
Ed Tanous0627a2c2018-11-29 17:09:23 -08001621 std::optional<nlohmann::json> oem;
Santosh Puranikaf5d60582019-03-20 18:16:36 +05301622 std::optional<std::string> datetime;
Ed Tanous0627a2c2018-11-29 17:09:23 -08001623
Santosh Puranikaf5d60582019-03-20 18:16:36 +05301624 if (!json_util::readJson(req, res, "Oem", oem, "DateTime", datetime))
James Feist83ff9ab2018-08-31 10:18:24 -07001625 {
1626 return;
1627 }
Ed Tanous0627a2c2018-11-29 17:09:23 -08001628
James Feist83ff9ab2018-08-31 10:18:24 -07001629 std::shared_ptr<AsyncResp> response = std::make_shared<AsyncResp>(res);
Ed Tanous0627a2c2018-11-29 17:09:23 -08001630
1631 if (oem)
James Feist83ff9ab2018-08-31 10:18:24 -07001632 {
Ed Tanous43b761d2019-02-13 20:10:56 -08001633 std::optional<nlohmann::json> openbmc;
1634 if (!redfish::json_util::readJson(*oem, res, "OpenBmc", openbmc))
James Feist83ff9ab2018-08-31 10:18:24 -07001635 {
Ed Tanous43b761d2019-02-13 20:10:56 -08001636 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
1637 << oem->dump();
1638 return;
1639 }
1640 if (openbmc)
1641 {
1642 std::optional<nlohmann::json> fan;
1643 if (!redfish::json_util::readJson(*openbmc, res, "Fan", fan))
James Feist83ff9ab2018-08-31 10:18:24 -07001644 {
James Feist5f2caae2018-12-12 14:08:25 -08001645 BMCWEB_LOG_ERROR << "Line:" << __LINE__
Ed Tanous43b761d2019-02-13 20:10:56 -08001646 << ", Illegal Property "
1647 << openbmc->dump();
James Feist5f2caae2018-12-12 14:08:25 -08001648 return;
1649 }
Ed Tanous43b761d2019-02-13 20:10:56 -08001650 if (fan)
James Feist5f2caae2018-12-12 14:08:25 -08001651 {
James Feist73df0db2019-03-25 15:29:35 -07001652 auto pid = std::make_shared<SetPIDValues>(response, *fan);
1653 pid->run();
James Feist83ff9ab2018-08-31 10:18:24 -07001654 }
James Feist83ff9ab2018-08-31 10:18:24 -07001655 }
1656 }
Santosh Puranikaf5d60582019-03-20 18:16:36 +05301657 if (datetime)
1658 {
1659 setDateTime(response, std::move(*datetime));
1660 }
1661 }
1662
1663 void setDateTime(std::shared_ptr<AsyncResp> aResp,
1664 std::string datetime) const
1665 {
1666 BMCWEB_LOG_DEBUG << "Set date time: " << datetime;
1667
1668 std::stringstream stream(datetime);
1669 // Convert from ISO 8601 to boost local_time
1670 // (BMC only has time in UTC)
1671 boost::posix_time::ptime posixTime;
1672 boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
1673 // Facet gets deleted with the stringsteam
1674 auto ifc = std::make_unique<boost::local_time::local_time_input_facet>(
1675 "%Y-%m-%d %H:%M:%S%F %ZP");
1676 stream.imbue(std::locale(stream.getloc(), ifc.release()));
1677
1678 boost::local_time::local_date_time ldt(
1679 boost::local_time::not_a_date_time);
1680
1681 if (stream >> ldt)
1682 {
1683 posixTime = ldt.utc_time();
1684 boost::posix_time::time_duration dur = posixTime - epoch;
1685 uint64_t durMicroSecs =
1686 static_cast<uint64_t>(dur.total_microseconds());
1687 crow::connections::systemBus->async_method_call(
1688 [aResp{std::move(aResp)}, datetime{std::move(datetime)}](
1689 const boost::system::error_code ec) {
1690 if (ec)
1691 {
1692 BMCWEB_LOG_DEBUG << "Failed to set elapsed time. "
1693 "DBUS response error "
1694 << ec;
1695 messages::internalError(aResp->res);
1696 return;
1697 }
1698 aResp->res.jsonValue["DateTime"] = datetime;
1699 },
1700 "xyz.openbmc_project.Time.Manager",
1701 "/xyz/openbmc_project/time/bmc",
1702 "org.freedesktop.DBus.Properties", "Set",
1703 "xyz.openbmc_project.Time.EpochTime", "Elapsed",
1704 std::variant<uint64_t>(durMicroSecs));
1705 }
1706 else
1707 {
1708 messages::propertyValueFormatError(aResp->res, datetime,
1709 "DateTime");
1710 return;
1711 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001712 }
1713
Ed Tanous0f74e642018-11-12 15:17:05 -08001714 std::string uuid;
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001715};
1716
Ed Tanous1abe55e2018-09-05 08:30:59 -07001717class ManagerCollection : public Node
1718{
1719 public:
James Feist5b4aa862018-08-16 14:07:01 -07001720 ManagerCollection(CrowApp& app) : Node(app, "/redfish/v1/Managers/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001721 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001722 entityPrivileges = {
1723 {boost::beast::http::verb::get, {{"Login"}}},
1724 {boost::beast::http::verb::head, {{"Login"}}},
1725 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1726 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1727 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1728 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1729 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001730
Ed Tanous1abe55e2018-09-05 08:30:59 -07001731 private:
James Feist5b4aa862018-08-16 14:07:01 -07001732 void doGet(crow::Response& res, const crow::Request& req,
1733 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -07001734 {
James Feist83ff9ab2018-08-31 10:18:24 -07001735 // Collections don't include the static data added by SubRoute
1736 // because it has a duplicate entry for members
Ed Tanous1abe55e2018-09-05 08:30:59 -07001737 res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
1738 res.jsonValue["@odata.type"] = "#ManagerCollection.ManagerCollection";
1739 res.jsonValue["@odata.context"] =
1740 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection";
1741 res.jsonValue["Name"] = "Manager Collection";
1742 res.jsonValue["Members@odata.count"] = 1;
1743 res.jsonValue["Members"] = {
James Feist5b4aa862018-08-16 14:07:01 -07001744 {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001745 res.end();
1746 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001747};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001748} // namespace redfish