blob: bb9a6acf846b031dc52b6fcaabf23c20c81acd4d [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
18#include "node.hpp"
Jennifer Leec5d03ff2019-03-08 15:42:58 -080019#include "redfish_util.hpp"
Borawski.Lukasz9c3106852018-02-09 15:24:22 +010020
James Feist5b4aa862018-08-16 14:07:01 -070021#include <boost/algorithm/string/replace.hpp>
Santosh Puranikaf5d60582019-03-20 18:16:36 +053022#include <boost/date_time.hpp>
James Feist5b4aa862018-08-16 14:07:01 -070023#include <dbus_utility.hpp>
James Feist73df0db2019-03-25 15:29:35 -070024#include <memory>
Santosh Puranikaf5d60582019-03-20 18:16:36 +053025#include <sstream>
Bernard Wong7bffdb72019-03-20 16:17:21 +080026#include <utils/systemd_utils.hpp>
Ed Tanousabf2add2019-01-22 16:40:12 -080027#include <variant>
James Feist5b4aa862018-08-16 14:07:01 -070028
Ed Tanous1abe55e2018-09-05 08:30:59 -070029namespace redfish
30{
Jennifer Leeed5befb2018-08-10 11:29:45 -070031
32/**
33 * ManagerActionsReset class supports handle POST method for Reset action.
34 * The class retrieves and sends data directly to dbus.
35 */
36class ManagerActionsReset : public Node
37{
38 public:
39 ManagerActionsReset(CrowApp& app) :
40 Node(app, "/redfish/v1/Managers/bmc/Actions/Manager.Reset/")
41 {
42 entityPrivileges = {
43 {boost::beast::http::verb::get, {{"Login"}}},
44 {boost::beast::http::verb::head, {{"Login"}}},
45 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
46 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
47 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
48 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
49 }
50
51 private:
52 /**
Jennifer Leeed5befb2018-08-10 11:29:45 -070053 * Function handles POST method request.
54 * Analyzes POST body message before sends Reset request data to dbus.
55 * OpenBMC allows for ResetType is GracefulRestart only.
56 */
57 void doPost(crow::Response& res, const crow::Request& req,
58 const std::vector<std::string>& params) override
59 {
60 std::string resetType;
61
62 if (!json_util::readJson(req, res, "ResetType", resetType))
63 {
64 return;
65 }
66
67 if (resetType != "GracefulRestart")
68 {
69 res.result(boost::beast::http::status::bad_request);
70 messages::actionParameterNotSupported(res, resetType, "ResetType");
71 BMCWEB_LOG_ERROR << "Request incorrect action parameter: "
72 << resetType;
73 res.end();
74 return;
75 }
76 doBMCGracefulRestart(res, req, params);
77 }
78
79 /**
80 * Function transceives data with dbus directly.
81 * All BMC state properties will be retrieved before sending reset request.
82 */
83 void doBMCGracefulRestart(crow::Response& res, const crow::Request& req,
84 const std::vector<std::string>& params)
85 {
86 const char* processName = "xyz.openbmc_project.State.BMC";
87 const char* objectPath = "/xyz/openbmc_project/state/bmc0";
88 const char* interfaceName = "xyz.openbmc_project.State.BMC";
89 const std::string& propertyValue =
90 "xyz.openbmc_project.State.BMC.Transition.Reboot";
91 const char* destProperty = "RequestedBMCTransition";
92
93 // Create the D-Bus variant for D-Bus call.
94 VariantType dbusPropertyValue(propertyValue);
95
96 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
97
98 crow::connections::systemBus->async_method_call(
99 [asyncResp](const boost::system::error_code ec) {
100 // Use "Set" method to set the property value.
101 if (ec)
102 {
103 BMCWEB_LOG_ERROR << "[Set] Bad D-Bus request error: " << ec;
104 messages::internalError(asyncResp->res);
105 return;
106 }
107
108 messages::success(asyncResp->res);
109 },
110 processName, objectPath, "org.freedesktop.DBus.Properties", "Set",
111 interfaceName, destProperty, dbusPropertyValue);
112 }
113};
114
James Feist5b4aa862018-08-16 14:07:01 -0700115static constexpr const char* objectManagerIface =
116 "org.freedesktop.DBus.ObjectManager";
117static constexpr const char* pidConfigurationIface =
118 "xyz.openbmc_project.Configuration.Pid";
119static constexpr const char* pidZoneConfigurationIface =
120 "xyz.openbmc_project.Configuration.Pid.Zone";
James Feistb7a08d02018-12-11 14:55:37 -0800121static constexpr const char* stepwiseConfigurationIface =
122 "xyz.openbmc_project.Configuration.Stepwise";
James Feist73df0db2019-03-25 15:29:35 -0700123static constexpr const char* thermalModeIface =
124 "xyz.openbmc_project.Control.ThermalMode";
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100125
James Feist5b4aa862018-08-16 14:07:01 -0700126static void asyncPopulatePid(const std::string& connection,
127 const std::string& path,
James Feist73df0db2019-03-25 15:29:35 -0700128 const std::string& currentProfile,
129 const std::vector<std::string>& supportedProfiles,
James Feist5b4aa862018-08-16 14:07:01 -0700130 std::shared_ptr<AsyncResp> asyncResp)
131{
132
133 crow::connections::systemBus->async_method_call(
James Feist73df0db2019-03-25 15:29:35 -0700134 [asyncResp, currentProfile, supportedProfiles](
135 const boost::system::error_code ec,
136 const dbus::utility::ManagedObjectType& managedObj) {
James Feist5b4aa862018-08-16 14:07:01 -0700137 if (ec)
138 {
139 BMCWEB_LOG_ERROR << ec;
James Feist5b4aa862018-08-16 14:07:01 -0700140 asyncResp->res.jsonValue.clear();
Jason M. Billsf12894f2018-10-09 12:45:45 -0700141 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700142 return;
143 }
144 nlohmann::json& configRoot =
145 asyncResp->res.jsonValue["Oem"]["OpenBmc"]["Fan"];
146 nlohmann::json& fans = configRoot["FanControllers"];
147 fans["@odata.type"] = "#OemManager.FanControllers";
148 fans["@odata.context"] =
149 "/redfish/v1/$metadata#OemManager.FanControllers";
150 fans["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc/"
151 "Fan/FanControllers";
152
153 nlohmann::json& pids = configRoot["PidControllers"];
154 pids["@odata.type"] = "#OemManager.PidControllers";
155 pids["@odata.context"] =
156 "/redfish/v1/$metadata#OemManager.PidControllers";
157 pids["@odata.id"] =
158 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/PidControllers";
159
James Feistb7a08d02018-12-11 14:55:37 -0800160 nlohmann::json& stepwise = configRoot["StepwiseControllers"];
161 stepwise["@odata.type"] = "#OemManager.StepwiseControllers";
162 stepwise["@odata.context"] =
163 "/redfish/v1/$metadata#OemManager.StepwiseControllers";
164 stepwise["@odata.id"] =
165 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/StepwiseControllers";
166
James Feist5b4aa862018-08-16 14:07:01 -0700167 nlohmann::json& zones = configRoot["FanZones"];
168 zones["@odata.id"] =
169 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones";
170 zones["@odata.type"] = "#OemManager.FanZones";
171 zones["@odata.context"] =
172 "/redfish/v1/$metadata#OemManager.FanZones";
173 configRoot["@odata.id"] =
174 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan";
175 configRoot["@odata.type"] = "#OemManager.Fan";
176 configRoot["@odata.context"] =
177 "/redfish/v1/$metadata#OemManager.Fan";
James Feist73df0db2019-03-25 15:29:35 -0700178 configRoot["Profile@Redfish.AllowableValues"] = supportedProfiles;
179
180 if (!currentProfile.empty())
181 {
182 configRoot["Profile"] = currentProfile;
183 }
184 BMCWEB_LOG_ERROR << "profile = " << currentProfile << " !";
James Feist5b4aa862018-08-16 14:07:01 -0700185
James Feist5b4aa862018-08-16 14:07:01 -0700186 for (const auto& pathPair : managedObj)
187 {
188 for (const auto& intfPair : pathPair.second)
189 {
190 if (intfPair.first != pidConfigurationIface &&
James Feistb7a08d02018-12-11 14:55:37 -0800191 intfPair.first != pidZoneConfigurationIface &&
192 intfPair.first != stepwiseConfigurationIface)
James Feist5b4aa862018-08-16 14:07:01 -0700193 {
194 continue;
195 }
196 auto findName = intfPair.second.find("Name");
197 if (findName == intfPair.second.end())
198 {
199 BMCWEB_LOG_ERROR << "Pid Field missing Name";
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800200 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700201 return;
202 }
James Feist73df0db2019-03-25 15:29:35 -0700203
James Feist5b4aa862018-08-16 14:07:01 -0700204 const std::string* namePtr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800205 std::get_if<std::string>(&findName->second);
James Feist5b4aa862018-08-16 14:07:01 -0700206 if (namePtr == nullptr)
207 {
208 BMCWEB_LOG_ERROR << "Pid Name Field illegal";
James Feistb7a08d02018-12-11 14:55:37 -0800209 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700210 return;
211 }
James Feist5b4aa862018-08-16 14:07:01 -0700212 std::string name = *namePtr;
213 dbus::utility::escapePathForDbus(name);
James Feist73df0db2019-03-25 15:29:35 -0700214
215 auto findProfiles = intfPair.second.find("Profiles");
216 if (findProfiles != intfPair.second.end())
217 {
218 const std::vector<std::string>* profiles =
219 std::get_if<std::vector<std::string>>(
220 &findProfiles->second);
221 if (profiles == nullptr)
222 {
223 BMCWEB_LOG_ERROR << "Pid Profiles Field illegal";
224 messages::internalError(asyncResp->res);
225 return;
226 }
227 if (std::find(profiles->begin(), profiles->end(),
228 currentProfile) == profiles->end())
229 {
230 BMCWEB_LOG_INFO
231 << name << " not supported in current profile";
232 continue;
233 }
234 }
James Feistb7a08d02018-12-11 14:55:37 -0800235 nlohmann::json* config = nullptr;
James Feistc33a90e2019-03-01 10:17:44 -0800236
237 const std::string* classPtr = nullptr;
238 auto findClass = intfPair.second.find("Class");
239 if (findClass != intfPair.second.end())
240 {
241 classPtr = std::get_if<std::string>(&findClass->second);
242 }
243
James Feist5b4aa862018-08-16 14:07:01 -0700244 if (intfPair.first == pidZoneConfigurationIface)
245 {
246 std::string chassis;
247 if (!dbus::utility::getNthStringFromPath(
248 pathPair.first.str, 5, chassis))
249 {
250 chassis = "#IllegalValue";
251 }
252 nlohmann::json& zone = zones[name];
253 zone["Chassis"] = {
254 {"@odata.id", "/redfish/v1/Chassis/" + chassis}};
255 zone["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/"
256 "OpenBmc/Fan/FanZones/" +
257 name;
258 zone["@odata.type"] = "#OemManager.FanZone";
259 zone["@odata.context"] =
260 "/redfish/v1/$metadata#OemManager.FanZone";
James Feistb7a08d02018-12-11 14:55:37 -0800261 config = &zone;
James Feist5b4aa862018-08-16 14:07:01 -0700262 }
263
James Feistb7a08d02018-12-11 14:55:37 -0800264 else if (intfPair.first == stepwiseConfigurationIface)
265 {
James Feistc33a90e2019-03-01 10:17:44 -0800266 if (classPtr == nullptr)
267 {
268 BMCWEB_LOG_ERROR << "Pid Class Field illegal";
269 messages::internalError(asyncResp->res);
270 return;
271 }
272
James Feistb7a08d02018-12-11 14:55:37 -0800273 nlohmann::json& controller = stepwise[name];
274 config = &controller;
275
276 controller["@odata.id"] =
277 "/redfish/v1/Managers/bmc#/Oem/"
278 "OpenBmc/Fan/StepwiseControllers/" +
279 std::string(name);
280 controller["@odata.type"] =
281 "#OemManager.StepwiseController";
282
283 controller["@odata.context"] =
284 "/redfish/v1/"
285 "$metadata#OemManager.StepwiseController";
James Feistc33a90e2019-03-01 10:17:44 -0800286 controller["Direction"] = *classPtr;
James Feistb7a08d02018-12-11 14:55:37 -0800287 }
288
289 // pid and fans are off the same configuration
290 else if (intfPair.first == pidConfigurationIface)
291 {
James Feistc33a90e2019-03-01 10:17:44 -0800292
James Feistb7a08d02018-12-11 14:55:37 -0800293 if (classPtr == nullptr)
294 {
295 BMCWEB_LOG_ERROR << "Pid Class Field illegal";
296 messages::internalError(asyncResp->res);
297 return;
298 }
299 bool isFan = *classPtr == "fan";
300 nlohmann::json& element =
301 isFan ? fans[name] : pids[name];
302 config = &element;
303 if (isFan)
304 {
305 element["@odata.id"] =
306 "/redfish/v1/Managers/bmc#/Oem/"
307 "OpenBmc/Fan/FanControllers/" +
308 std::string(name);
309 element["@odata.type"] =
310 "#OemManager.FanController";
311
312 element["@odata.context"] =
313 "/redfish/v1/"
314 "$metadata#OemManager.FanController";
315 }
316 else
317 {
318 element["@odata.id"] =
319 "/redfish/v1/Managers/bmc#/Oem/"
320 "OpenBmc/Fan/PidControllers/" +
321 std::string(name);
322 element["@odata.type"] =
323 "#OemManager.PidController";
324 element["@odata.context"] =
325 "/redfish/v1/$metadata"
326 "#OemManager.PidController";
327 }
328 }
329 else
330 {
331 BMCWEB_LOG_ERROR << "Unexpected configuration";
332 messages::internalError(asyncResp->res);
333 return;
334 }
335
336 // used for making maps out of 2 vectors
337 const std::vector<double>* keys = nullptr;
338 const std::vector<double>* values = nullptr;
339
James Feist5b4aa862018-08-16 14:07:01 -0700340 for (const auto& propertyPair : intfPair.second)
341 {
342 if (propertyPair.first == "Type" ||
343 propertyPair.first == "Class" ||
344 propertyPair.first == "Name")
345 {
346 continue;
347 }
348
349 // zones
350 if (intfPair.first == pidZoneConfigurationIface)
351 {
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800352 const double* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800353 std::get_if<double>(&propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700354 if (ptr == nullptr)
355 {
356 BMCWEB_LOG_ERROR << "Field Illegal "
357 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700358 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700359 return;
360 }
James Feistb7a08d02018-12-11 14:55:37 -0800361 (*config)[propertyPair.first] = *ptr;
362 }
363
364 if (intfPair.first == stepwiseConfigurationIface)
365 {
366 if (propertyPair.first == "Reading" ||
367 propertyPair.first == "Output")
368 {
369 const std::vector<double>* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800370 std::get_if<std::vector<double>>(
James Feistb7a08d02018-12-11 14:55:37 -0800371 &propertyPair.second);
372
373 if (ptr == nullptr)
374 {
375 BMCWEB_LOG_ERROR << "Field Illegal "
376 << propertyPair.first;
377 messages::internalError(asyncResp->res);
378 return;
379 }
380
381 if (propertyPair.first == "Reading")
382 {
383 keys = ptr;
384 }
385 else
386 {
387 values = ptr;
388 }
389 if (keys && values)
390 {
391 if (keys->size() != values->size())
392 {
393 BMCWEB_LOG_ERROR
394 << "Reading and Output size don't "
395 "match ";
396 messages::internalError(asyncResp->res);
397 return;
398 }
399 nlohmann::json& steps = (*config)["Steps"];
400 steps = nlohmann::json::array();
401 for (size_t ii = 0; ii < keys->size(); ii++)
402 {
403 steps.push_back(
404 {{"Target", (*keys)[ii]},
405 {"Output", (*values)[ii]}});
406 }
407 }
408 }
409 if (propertyPair.first == "NegativeHysteresis" ||
410 propertyPair.first == "PositiveHysteresis")
411 {
412 const double* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800413 std::get_if<double>(&propertyPair.second);
James Feistb7a08d02018-12-11 14:55:37 -0800414 if (ptr == nullptr)
415 {
416 BMCWEB_LOG_ERROR << "Field Illegal "
417 << propertyPair.first;
418 messages::internalError(asyncResp->res);
419 return;
420 }
421 (*config)[propertyPair.first] = *ptr;
422 }
James Feist5b4aa862018-08-16 14:07:01 -0700423 }
424
425 // pid and fans are off the same configuration
James Feistb7a08d02018-12-11 14:55:37 -0800426 if (intfPair.first == pidConfigurationIface ||
427 intfPair.first == stepwiseConfigurationIface)
James Feist5b4aa862018-08-16 14:07:01 -0700428 {
James Feist5b4aa862018-08-16 14:07:01 -0700429
430 if (propertyPair.first == "Zones")
431 {
432 const std::vector<std::string>* inputs =
Ed Tanousabf2add2019-01-22 16:40:12 -0800433 std::get_if<std::vector<std::string>>(
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800434 &propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700435
436 if (inputs == nullptr)
437 {
438 BMCWEB_LOG_ERROR
439 << "Zones Pid Field Illegal";
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800440 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700441 return;
442 }
James Feistb7a08d02018-12-11 14:55:37 -0800443 auto& data = (*config)[propertyPair.first];
James Feist5b4aa862018-08-16 14:07:01 -0700444 data = nlohmann::json::array();
445 for (std::string itemCopy : *inputs)
446 {
447 dbus::utility::escapePathForDbus(itemCopy);
448 data.push_back(
449 {{"@odata.id",
450 "/redfish/v1/Managers/bmc#/Oem/"
451 "OpenBmc/Fan/FanZones/" +
452 itemCopy}});
453 }
454 }
455 // todo(james): may never happen, but this
456 // assumes configuration data referenced in the
457 // PID config is provided by the same daemon, we
458 // could add another loop to cover all cases,
459 // but I'm okay kicking this can down the road a
460 // bit
461
462 else if (propertyPair.first == "Inputs" ||
463 propertyPair.first == "Outputs")
464 {
James Feistb7a08d02018-12-11 14:55:37 -0800465 auto& data = (*config)[propertyPair.first];
James Feist5b4aa862018-08-16 14:07:01 -0700466 const std::vector<std::string>* inputs =
Ed Tanousabf2add2019-01-22 16:40:12 -0800467 std::get_if<std::vector<std::string>>(
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800468 &propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700469
470 if (inputs == nullptr)
471 {
472 BMCWEB_LOG_ERROR << "Field Illegal "
473 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700474 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700475 return;
476 }
477 data = *inputs;
478 } // doubles
479 else if (propertyPair.first ==
480 "FFGainCoefficient" ||
481 propertyPair.first == "FFOffCoefficient" ||
482 propertyPair.first == "ICoefficient" ||
483 propertyPair.first == "ILimitMax" ||
484 propertyPair.first == "ILimitMin" ||
James Feistaad1a252019-02-19 10:13:52 -0800485 propertyPair.first ==
486 "PositiveHysteresis" ||
487 propertyPair.first ==
488 "NegativeHysteresis" ||
James Feist5b4aa862018-08-16 14:07:01 -0700489 propertyPair.first == "OutLimitMax" ||
490 propertyPair.first == "OutLimitMin" ||
491 propertyPair.first == "PCoefficient" ||
James Feist7625cb82019-01-23 11:58:21 -0800492 propertyPair.first == "SetPoint" ||
James Feist5b4aa862018-08-16 14:07:01 -0700493 propertyPair.first == "SlewNeg" ||
494 propertyPair.first == "SlewPos")
495 {
496 const double* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800497 std::get_if<double>(&propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700498 if (ptr == nullptr)
499 {
500 BMCWEB_LOG_ERROR << "Field Illegal "
501 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700502 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700503 return;
504 }
James Feistb7a08d02018-12-11 14:55:37 -0800505 (*config)[propertyPair.first] = *ptr;
James Feist5b4aa862018-08-16 14:07:01 -0700506 }
507 }
508 }
509 }
510 }
511 },
512 connection, path, objectManagerIface, "GetManagedObjects");
513}
Jennifer Leeca537922018-08-10 10:07:30 -0700514
James Feist83ff9ab2018-08-31 10:18:24 -0700515enum class CreatePIDRet
516{
517 fail,
518 del,
519 patch
520};
521
James Feist5f2caae2018-12-12 14:08:25 -0800522static bool getZonesFromJsonReq(const std::shared_ptr<AsyncResp>& response,
523 std::vector<nlohmann::json>& config,
524 std::vector<std::string>& zones)
525{
James Feistb6baeaa2019-02-21 10:41:40 -0800526 if (config.empty())
527 {
528 BMCWEB_LOG_ERROR << "Empty Zones";
529 messages::propertyValueFormatError(response->res,
530 nlohmann::json::array(), "Zones");
531 return false;
532 }
James Feist5f2caae2018-12-12 14:08:25 -0800533 for (auto& odata : config)
534 {
535 std::string path;
536 if (!redfish::json_util::readJson(odata, response->res, "@odata.id",
537 path))
538 {
539 return false;
540 }
541 std::string input;
James Feist61adbda2019-03-25 13:03:51 -0700542
543 // 8 below comes from
544 // /redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones/Left
545 // 0 1 2 3 4 5 6 7 8
546 if (!dbus::utility::getNthStringFromPath(path, 8, input))
James Feist5f2caae2018-12-12 14:08:25 -0800547 {
548 BMCWEB_LOG_ERROR << "Got invalid path " << path;
549 BMCWEB_LOG_ERROR << "Illegal Type Zones";
550 messages::propertyValueFormatError(response->res, odata.dump(),
551 "Zones");
552 return false;
553 }
554 boost::replace_all(input, "_", " ");
555 zones.emplace_back(std::move(input));
556 }
557 return true;
558}
559
James Feist73df0db2019-03-25 15:29:35 -0700560static const dbus::utility::ManagedItem*
561 findChassis(const dbus::utility::ManagedObjectType& managedObj,
562 const std::string& value, std::string& chassis)
James Feistb6baeaa2019-02-21 10:41:40 -0800563{
564 BMCWEB_LOG_DEBUG << "Find Chassis: " << value << "\n";
565
566 std::string escaped = boost::replace_all_copy(value, " ", "_");
567 escaped = "/" + escaped;
568 auto it = std::find_if(
569 managedObj.begin(), managedObj.end(), [&escaped](const auto& obj) {
570 if (boost::algorithm::ends_with(obj.first.str, escaped))
571 {
572 BMCWEB_LOG_DEBUG << "Matched " << obj.first.str << "\n";
573 return true;
574 }
575 return false;
576 });
577
578 if (it == managedObj.end())
579 {
James Feist73df0db2019-03-25 15:29:35 -0700580 return nullptr;
James Feistb6baeaa2019-02-21 10:41:40 -0800581 }
582 // 5 comes from <chassis-name> being the 5th element
583 // /xyz/openbmc_project/inventory/system/chassis/<chassis-name>
James Feist73df0db2019-03-25 15:29:35 -0700584 if (dbus::utility::getNthStringFromPath(it->first.str, 5, chassis))
585 {
586 return &(*it);
587 }
588
589 return nullptr;
James Feistb6baeaa2019-02-21 10:41:40 -0800590}
591
James Feist83ff9ab2018-08-31 10:18:24 -0700592static CreatePIDRet createPidInterface(
593 const std::shared_ptr<AsyncResp>& response, const std::string& type,
James Feistb6baeaa2019-02-21 10:41:40 -0800594 nlohmann::json::iterator it, const std::string& path,
James Feist83ff9ab2018-08-31 10:18:24 -0700595 const dbus::utility::ManagedObjectType& managedObj, bool createNewObject,
596 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>&
597 output,
James Feist73df0db2019-03-25 15:29:35 -0700598 std::string& chassis, const std::string& profile)
James Feist83ff9ab2018-08-31 10:18:24 -0700599{
600
James Feist5f2caae2018-12-12 14:08:25 -0800601 // common deleter
James Feistb6baeaa2019-02-21 10:41:40 -0800602 if (it.value() == nullptr)
James Feist5f2caae2018-12-12 14:08:25 -0800603 {
604 std::string iface;
605 if (type == "PidControllers" || type == "FanControllers")
606 {
607 iface = pidConfigurationIface;
608 }
609 else if (type == "FanZones")
610 {
611 iface = pidZoneConfigurationIface;
612 }
613 else if (type == "StepwiseControllers")
614 {
615 iface = stepwiseConfigurationIface;
616 }
617 else
618 {
619 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Type "
620 << type;
621 messages::propertyUnknown(response->res, type);
622 return CreatePIDRet::fail;
623 }
624 // delete interface
625 crow::connections::systemBus->async_method_call(
626 [response, path](const boost::system::error_code ec) {
627 if (ec)
628 {
629 BMCWEB_LOG_ERROR << "Error patching " << path << ": " << ec;
630 messages::internalError(response->res);
James Feistb6baeaa2019-02-21 10:41:40 -0800631 return;
James Feist5f2caae2018-12-12 14:08:25 -0800632 }
James Feistb6baeaa2019-02-21 10:41:40 -0800633 messages::success(response->res);
James Feist5f2caae2018-12-12 14:08:25 -0800634 },
635 "xyz.openbmc_project.EntityManager", path, iface, "Delete");
636 return CreatePIDRet::del;
637 }
638
James Feist73df0db2019-03-25 15:29:35 -0700639 const dbus::utility::ManagedItem* managedItem = nullptr;
James Feistb6baeaa2019-02-21 10:41:40 -0800640 if (!createNewObject)
641 {
642 // if we aren't creating a new object, we should be able to find it on
643 // d-bus
James Feist73df0db2019-03-25 15:29:35 -0700644 managedItem = findChassis(managedObj, it.key(), chassis);
645 if (managedItem == nullptr)
James Feistb6baeaa2019-02-21 10:41:40 -0800646 {
647 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
648 messages::invalidObject(response->res, it.key());
649 return CreatePIDRet::fail;
650 }
651 }
652
James Feist73df0db2019-03-25 15:29:35 -0700653 if (profile.size() &&
654 (type == "PidControllers" || type == "FanControllers" ||
655 type == "StepwiseControllers"))
656 {
657 if (managedItem == nullptr)
658 {
659 output["Profiles"] = std::vector<std::string>{profile};
660 }
661 else
662 {
663 std::string interface;
664 if (type == "StepwiseControllers")
665 {
666 interface = stepwiseConfigurationIface;
667 }
668 else
669 {
670 interface = pidConfigurationIface;
671 }
672 auto findConfig = managedItem->second.find(interface);
673 if (findConfig == managedItem->second.end())
674 {
675 BMCWEB_LOG_ERROR
676 << "Failed to find interface in managed object";
677 messages::internalError(response->res);
678 return CreatePIDRet::fail;
679 }
680 auto findProfiles = findConfig->second.find("Profiles");
681 if (findProfiles != findConfig->second.end())
682 {
683 const std::vector<std::string>* curProfiles =
684 std::get_if<std::vector<std::string>>(
685 &(findProfiles->second));
686 if (curProfiles == nullptr)
687 {
688 BMCWEB_LOG_ERROR << "Illegal profiles in managed object";
689 messages::internalError(response->res);
690 return CreatePIDRet::fail;
691 }
692 if (std::find(curProfiles->begin(), curProfiles->end(),
693 profile) == curProfiles->end())
694 {
695 std::vector<std::string> newProfiles = *curProfiles;
696 newProfiles.push_back(profile);
697 output["Profiles"] = newProfiles;
698 }
699 }
700 }
701 }
702
James Feist83ff9ab2018-08-31 10:18:24 -0700703 if (type == "PidControllers" || type == "FanControllers")
704 {
705 if (createNewObject)
706 {
707 output["Class"] = type == "PidControllers" ? std::string("temp")
708 : std::string("fan");
709 output["Type"] = std::string("Pid");
710 }
James Feist5f2caae2018-12-12 14:08:25 -0800711
712 std::optional<std::vector<nlohmann::json>> zones;
713 std::optional<std::vector<std::string>> inputs;
714 std::optional<std::vector<std::string>> outputs;
715 std::map<std::string, std::optional<double>> doubles;
716 if (!redfish::json_util::readJson(
James Feistb6baeaa2019-02-21 10:41:40 -0800717 it.value(), response->res, "Inputs", inputs, "Outputs", outputs,
James Feist5f2caae2018-12-12 14:08:25 -0800718 "Zones", zones, "FFGainCoefficient",
719 doubles["FFGainCoefficient"], "FFOffCoefficient",
720 doubles["FFOffCoefficient"], "ICoefficient",
721 doubles["ICoefficient"], "ILimitMax", doubles["ILimitMax"],
722 "ILimitMin", doubles["ILimitMin"], "OutLimitMax",
723 doubles["OutLimitMax"], "OutLimitMin", doubles["OutLimitMin"],
724 "PCoefficient", doubles["PCoefficient"], "SetPoint",
725 doubles["SetPoint"], "SlewNeg", doubles["SlewNeg"], "SlewPos",
James Feistaad1a252019-02-19 10:13:52 -0800726 doubles["SlewPos"], "PositiveHysteresis",
727 doubles["PositiveHysteresis"], "NegativeHysteresis",
728 doubles["NegativeHysteresis"]))
James Feist83ff9ab2018-08-31 10:18:24 -0700729 {
James Feist5f2caae2018-12-12 14:08:25 -0800730 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
James Feistb6baeaa2019-02-21 10:41:40 -0800731 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800732 return CreatePIDRet::fail;
James Feist83ff9ab2018-08-31 10:18:24 -0700733 }
James Feist5f2caae2018-12-12 14:08:25 -0800734 if (zones)
James Feist83ff9ab2018-08-31 10:18:24 -0700735 {
James Feist5f2caae2018-12-12 14:08:25 -0800736 std::vector<std::string> zonesStr;
737 if (!getZonesFromJsonReq(response, *zones, zonesStr))
James Feist83ff9ab2018-08-31 10:18:24 -0700738 {
James Feist5f2caae2018-12-12 14:08:25 -0800739 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Zones";
740 return CreatePIDRet::fail;
James Feist83ff9ab2018-08-31 10:18:24 -0700741 }
James Feistb6baeaa2019-02-21 10:41:40 -0800742 if (chassis.empty() &&
743 !findChassis(managedObj, zonesStr[0], chassis))
744 {
745 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
746 messages::invalidObject(response->res, it.key());
747 return CreatePIDRet::fail;
748 }
749
James Feist5f2caae2018-12-12 14:08:25 -0800750 output["Zones"] = std::move(zonesStr);
751 }
752 if (inputs || outputs)
753 {
754 std::array<std::optional<std::vector<std::string>>*, 2> containers =
755 {&inputs, &outputs};
756 size_t index = 0;
757 for (const auto& containerPtr : containers)
James Feist83ff9ab2018-08-31 10:18:24 -0700758 {
James Feist5f2caae2018-12-12 14:08:25 -0800759 std::optional<std::vector<std::string>>& container =
760 *containerPtr;
761 if (!container)
James Feist83ff9ab2018-08-31 10:18:24 -0700762 {
James Feist5f2caae2018-12-12 14:08:25 -0800763 index++;
764 continue;
James Feist83ff9ab2018-08-31 10:18:24 -0700765 }
James Feist5f2caae2018-12-12 14:08:25 -0800766
767 for (std::string& value : *container)
James Feist83ff9ab2018-08-31 10:18:24 -0700768 {
James Feist5f2caae2018-12-12 14:08:25 -0800769 boost::replace_all(value, "_", " ");
James Feist83ff9ab2018-08-31 10:18:24 -0700770 }
James Feist5f2caae2018-12-12 14:08:25 -0800771 std::string key;
772 if (index == 0)
James Feist83ff9ab2018-08-31 10:18:24 -0700773 {
James Feist5f2caae2018-12-12 14:08:25 -0800774 key = "Inputs";
James Feist83ff9ab2018-08-31 10:18:24 -0700775 }
James Feist5f2caae2018-12-12 14:08:25 -0800776 else
777 {
778 key = "Outputs";
779 }
780 output[key] = *container;
781 index++;
James Feist83ff9ab2018-08-31 10:18:24 -0700782 }
James Feist5f2caae2018-12-12 14:08:25 -0800783 }
James Feist83ff9ab2018-08-31 10:18:24 -0700784
James Feist5f2caae2018-12-12 14:08:25 -0800785 // doubles
786 for (const auto& pairs : doubles)
787 {
788 if (!pairs.second)
James Feist83ff9ab2018-08-31 10:18:24 -0700789 {
James Feist5f2caae2018-12-12 14:08:25 -0800790 continue;
James Feist83ff9ab2018-08-31 10:18:24 -0700791 }
James Feist5f2caae2018-12-12 14:08:25 -0800792 BMCWEB_LOG_DEBUG << pairs.first << " = " << *pairs.second;
793 output[pairs.first] = *(pairs.second);
James Feist83ff9ab2018-08-31 10:18:24 -0700794 }
795 }
James Feist5f2caae2018-12-12 14:08:25 -0800796
James Feist83ff9ab2018-08-31 10:18:24 -0700797 else if (type == "FanZones")
798 {
James Feist83ff9ab2018-08-31 10:18:24 -0700799 output["Type"] = std::string("Pid.Zone");
800
James Feist5f2caae2018-12-12 14:08:25 -0800801 std::optional<nlohmann::json> chassisContainer;
802 std::optional<double> failSafePercent;
James Feistd3ec07f2019-02-25 14:51:15 -0800803 std::optional<double> minThermalOutput;
James Feistb6baeaa2019-02-21 10:41:40 -0800804 if (!redfish::json_util::readJson(it.value(), response->res, "Chassis",
James Feist5f2caae2018-12-12 14:08:25 -0800805 chassisContainer, "FailSafePercent",
James Feistd3ec07f2019-02-25 14:51:15 -0800806 failSafePercent, "MinThermalOutput",
807 minThermalOutput))
James Feist83ff9ab2018-08-31 10:18:24 -0700808 {
James Feist5f2caae2018-12-12 14:08:25 -0800809 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
James Feistb6baeaa2019-02-21 10:41:40 -0800810 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800811 return CreatePIDRet::fail;
812 }
James Feist83ff9ab2018-08-31 10:18:24 -0700813
James Feist5f2caae2018-12-12 14:08:25 -0800814 if (chassisContainer)
815 {
816
817 std::string chassisId;
818 if (!redfish::json_util::readJson(*chassisContainer, response->res,
819 "@odata.id", chassisId))
James Feist83ff9ab2018-08-31 10:18:24 -0700820 {
James Feist5f2caae2018-12-12 14:08:25 -0800821 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
822 << chassisContainer->dump();
James Feist83ff9ab2018-08-31 10:18:24 -0700823 return CreatePIDRet::fail;
824 }
James Feist5f2caae2018-12-12 14:08:25 -0800825
826 // /refish/v1/chassis/chassis_name/
827 if (!dbus::utility::getNthStringFromPath(chassisId, 3, chassis))
828 {
829 BMCWEB_LOG_ERROR << "Got invalid path " << chassisId;
830 messages::invalidObject(response->res, chassisId);
831 return CreatePIDRet::fail;
832 }
833 }
James Feistd3ec07f2019-02-25 14:51:15 -0800834 if (minThermalOutput)
James Feist5f2caae2018-12-12 14:08:25 -0800835 {
James Feistd3ec07f2019-02-25 14:51:15 -0800836 output["MinThermalOutput"] = *minThermalOutput;
James Feist5f2caae2018-12-12 14:08:25 -0800837 }
838 if (failSafePercent)
839 {
840 output["FailSafePercent"] = *failSafePercent;
841 }
842 }
843 else if (type == "StepwiseControllers")
844 {
845 output["Type"] = std::string("Stepwise");
846
847 std::optional<std::vector<nlohmann::json>> zones;
848 std::optional<std::vector<nlohmann::json>> steps;
849 std::optional<std::vector<std::string>> inputs;
850 std::optional<double> positiveHysteresis;
851 std::optional<double> negativeHysteresis;
James Feistc33a90e2019-03-01 10:17:44 -0800852 std::optional<std::string> direction; // upper clipping curve vs lower
James Feist5f2caae2018-12-12 14:08:25 -0800853 if (!redfish::json_util::readJson(
James Feistb6baeaa2019-02-21 10:41:40 -0800854 it.value(), response->res, "Zones", zones, "Steps", steps,
855 "Inputs", inputs, "PositiveHysteresis", positiveHysteresis,
James Feistc33a90e2019-03-01 10:17:44 -0800856 "NegativeHysteresis", negativeHysteresis, "Direction",
857 direction))
James Feist5f2caae2018-12-12 14:08:25 -0800858 {
859 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
James Feistb6baeaa2019-02-21 10:41:40 -0800860 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800861 return CreatePIDRet::fail;
862 }
863
864 if (zones)
865 {
James Feistb6baeaa2019-02-21 10:41:40 -0800866 std::vector<std::string> zonesStrs;
867 if (!getZonesFromJsonReq(response, *zones, zonesStrs))
James Feist5f2caae2018-12-12 14:08:25 -0800868 {
869 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Zones";
870 return CreatePIDRet::fail;
871 }
James Feistb6baeaa2019-02-21 10:41:40 -0800872 if (chassis.empty() &&
873 !findChassis(managedObj, zonesStrs[0], chassis))
874 {
875 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
876 messages::invalidObject(response->res, it.key());
877 return CreatePIDRet::fail;
878 }
879 output["Zones"] = std::move(zonesStrs);
James Feist5f2caae2018-12-12 14:08:25 -0800880 }
881 if (steps)
882 {
883 std::vector<double> readings;
884 std::vector<double> outputs;
885 for (auto& step : *steps)
886 {
887 double target;
Ed Tanousb01bf292019-03-25 19:25:26 +0000888 double output;
James Feist5f2caae2018-12-12 14:08:25 -0800889
890 if (!redfish::json_util::readJson(step, response->res, "Target",
Ed Tanousb01bf292019-03-25 19:25:26 +0000891 target, "Output", output))
James Feist5f2caae2018-12-12 14:08:25 -0800892 {
893 BMCWEB_LOG_ERROR << "Line:" << __LINE__
James Feistb6baeaa2019-02-21 10:41:40 -0800894 << ", Illegal Property "
895 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800896 return CreatePIDRet::fail;
897 }
898 readings.emplace_back(target);
Ed Tanousb01bf292019-03-25 19:25:26 +0000899 outputs.emplace_back(output);
James Feist5f2caae2018-12-12 14:08:25 -0800900 }
901 output["Reading"] = std::move(readings);
902 output["Output"] = std::move(outputs);
903 }
904 if (inputs)
905 {
906 for (std::string& value : *inputs)
907 {
James Feist5f2caae2018-12-12 14:08:25 -0800908 boost::replace_all(value, "_", " ");
909 }
910 output["Inputs"] = std::move(*inputs);
911 }
912 if (negativeHysteresis)
913 {
914 output["NegativeHysteresis"] = *negativeHysteresis;
915 }
916 if (positiveHysteresis)
917 {
918 output["PositiveHysteresis"] = *positiveHysteresis;
James Feist83ff9ab2018-08-31 10:18:24 -0700919 }
James Feistc33a90e2019-03-01 10:17:44 -0800920 if (direction)
921 {
922 constexpr const std::array<const char*, 2> allowedDirections = {
923 "Ceiling", "Floor"};
924 if (std::find(allowedDirections.begin(), allowedDirections.end(),
925 *direction) == allowedDirections.end())
926 {
927 messages::propertyValueTypeError(response->res, "Direction",
928 *direction);
929 return CreatePIDRet::fail;
930 }
931 output["Class"] = *direction;
932 }
James Feist83ff9ab2018-08-31 10:18:24 -0700933 }
934 else
935 {
James Feist5f2caae2018-12-12 14:08:25 -0800936 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Type " << type;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700937 messages::propertyUnknown(response->res, type);
James Feist83ff9ab2018-08-31 10:18:24 -0700938 return CreatePIDRet::fail;
939 }
940 return CreatePIDRet::patch;
941}
James Feist73df0db2019-03-25 15:29:35 -0700942struct GetPIDValues : std::enable_shared_from_this<GetPIDValues>
943{
944
945 GetPIDValues(const std::shared_ptr<AsyncResp>& asyncResp) :
946 asyncResp(asyncResp)
947
948 {
949 }
950
951 void run()
952 {
953 std::shared_ptr<GetPIDValues> self = shared_from_this();
954
955 // get all configurations
956 crow::connections::systemBus->async_method_call(
957 [self](const boost::system::error_code ec,
958 const crow::openbmc_mapper::GetSubTreeType& subtree) {
959 if (ec)
960 {
961 BMCWEB_LOG_ERROR << ec;
962 messages::internalError(self->asyncResp->res);
963 return;
964 }
965 self->subtree = subtree;
966 },
967 "xyz.openbmc_project.ObjectMapper",
968 "/xyz/openbmc_project/object_mapper",
969 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
970 std::array<const char*, 4>{
971 pidConfigurationIface, pidZoneConfigurationIface,
972 objectManagerIface, stepwiseConfigurationIface});
973
974 // at the same time get the selected profile
975 crow::connections::systemBus->async_method_call(
976 [self](const boost::system::error_code ec,
977 const crow::openbmc_mapper::GetSubTreeType& subtree) {
978 if (ec || subtree.empty())
979 {
980 return;
981 }
982 if (subtree[0].second.size() != 1)
983 {
984 // invalid mapper response, should never happen
985 BMCWEB_LOG_ERROR << "GetPIDValues: Mapper Error";
986 messages::internalError(self->asyncResp->res);
987 return;
988 }
989
990 const std::string& path = subtree[0].first;
991 const std::string& owner = subtree[0].second[0].first;
992 crow::connections::systemBus->async_method_call(
993 [path, owner, self](
994 const boost::system::error_code ec,
995 const boost::container::flat_map<
996 std::string, std::variant<std::vector<std::string>,
997 std::string>>& resp) {
998 if (ec)
999 {
1000 BMCWEB_LOG_ERROR << "GetPIDValues: Can't get "
1001 "thermalModeIface "
1002 << path;
1003 messages::internalError(self->asyncResp->res);
1004 return;
1005 }
1006 const std::string* current;
1007 const std::vector<std::string>* supported;
1008 for (auto& [key, value] : resp)
1009 {
1010 if (key == "Current")
1011 {
1012 current = std::get_if<std::string>(&value);
1013 if (current == nullptr)
1014 {
1015 BMCWEB_LOG_ERROR
1016 << "GetPIDValues: thermal mode "
1017 "iface invalid "
1018 << path;
1019 messages::internalError(
1020 self->asyncResp->res);
1021 return;
1022 }
1023 }
1024 if (key == "Supported")
1025 {
1026 supported =
1027 std::get_if<std::vector<std::string>>(
1028 &value);
1029 if (supported == nullptr)
1030 {
1031 BMCWEB_LOG_ERROR
1032 << "GetPIDValues: thermal mode "
1033 "iface invalid"
1034 << path;
1035 messages::internalError(
1036 self->asyncResp->res);
1037 return;
1038 }
1039 }
1040 }
1041 if (current == nullptr || supported == nullptr)
1042 {
1043 BMCWEB_LOG_ERROR << "GetPIDValues: thermal mode "
1044 "iface invalid "
1045 << path;
1046 messages::internalError(self->asyncResp->res);
1047 return;
1048 }
1049 self->currentProfile = *current;
1050 self->supportedProfiles = *supported;
1051 },
1052 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1053 thermalModeIface);
1054 },
1055 "xyz.openbmc_project.ObjectMapper",
1056 "/xyz/openbmc_project/object_mapper",
1057 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
1058 std::array<const char*, 1>{thermalModeIface});
1059 }
1060
1061 ~GetPIDValues()
1062 {
1063 if (asyncResp->res.result() != boost::beast::http::status::ok)
1064 {
1065 return;
1066 }
1067 // create map of <connection, path to objMgr>>
1068 boost::container::flat_map<std::string, std::string> objectMgrPaths;
1069 boost::container::flat_set<std::string> calledConnections;
1070 for (const auto& pathGroup : subtree)
1071 {
1072 for (const auto& connectionGroup : pathGroup.second)
1073 {
1074 auto findConnection =
1075 calledConnections.find(connectionGroup.first);
1076 if (findConnection != calledConnections.end())
1077 {
1078 break;
1079 }
1080 for (const std::string& interface : connectionGroup.second)
1081 {
1082 if (interface == objectManagerIface)
1083 {
1084 objectMgrPaths[connectionGroup.first] = pathGroup.first;
1085 }
1086 // this list is alphabetical, so we
1087 // should have found the objMgr by now
1088 if (interface == pidConfigurationIface ||
1089 interface == pidZoneConfigurationIface ||
1090 interface == stepwiseConfigurationIface)
1091 {
1092 auto findObjMgr =
1093 objectMgrPaths.find(connectionGroup.first);
1094 if (findObjMgr == objectMgrPaths.end())
1095 {
1096 BMCWEB_LOG_DEBUG << connectionGroup.first
1097 << "Has no Object Manager";
1098 continue;
1099 }
1100
1101 calledConnections.insert(connectionGroup.first);
1102
1103 asyncPopulatePid(findObjMgr->first, findObjMgr->second,
1104 currentProfile, supportedProfiles,
1105 asyncResp);
1106 break;
1107 }
1108 }
1109 }
1110 }
1111 }
1112
1113 std::vector<std::string> supportedProfiles;
1114 std::string currentProfile;
1115 crow::openbmc_mapper::GetSubTreeType subtree;
1116 std::shared_ptr<AsyncResp> asyncResp;
1117};
1118
1119struct SetPIDValues : std::enable_shared_from_this<SetPIDValues>
1120{
1121
1122 SetPIDValues(const std::shared_ptr<AsyncResp>& asyncResp,
1123 nlohmann::json& data) :
1124 asyncResp(asyncResp)
1125 {
1126
1127 std::optional<nlohmann::json> pidControllers;
1128 std::optional<nlohmann::json> fanControllers;
1129 std::optional<nlohmann::json> fanZones;
1130 std::optional<nlohmann::json> stepwiseControllers;
1131
1132 if (!redfish::json_util::readJson(
1133 data, asyncResp->res, "PidControllers", pidControllers,
1134 "FanControllers", fanControllers, "FanZones", fanZones,
1135 "StepwiseControllers", stepwiseControllers, "Profile", profile))
1136 {
1137 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
1138 << data.dump();
1139 return;
1140 }
1141 configuration.emplace_back("PidControllers", std::move(pidControllers));
1142 configuration.emplace_back("FanControllers", std::move(fanControllers));
1143 configuration.emplace_back("FanZones", std::move(fanZones));
1144 configuration.emplace_back("StepwiseControllers",
1145 std::move(stepwiseControllers));
1146 }
1147 void run()
1148 {
1149 if (asyncResp->res.result() != boost::beast::http::status::ok)
1150 {
1151 return;
1152 }
1153
1154 std::shared_ptr<SetPIDValues> self = shared_from_this();
1155
1156 // todo(james): might make sense to do a mapper call here if this
1157 // interface gets more traction
1158 crow::connections::systemBus->async_method_call(
1159 [self](const boost::system::error_code ec,
1160 dbus::utility::ManagedObjectType& managedObj) {
1161 if (ec)
1162 {
1163 BMCWEB_LOG_ERROR << "Error communicating to Entity Manager";
1164 messages::internalError(self->asyncResp->res);
1165 return;
1166 }
1167 self->managedObj = std::move(managedObj);
1168 },
1169 "xyz.openbmc_project.EntityManager", "/", objectManagerIface,
1170 "GetManagedObjects");
1171
1172 // at the same time get the profile information
1173 crow::connections::systemBus->async_method_call(
1174 [self](const boost::system::error_code ec,
1175 const crow::openbmc_mapper::GetSubTreeType& subtree) {
1176 if (ec || subtree.empty())
1177 {
1178 return;
1179 }
1180 if (subtree[0].second.empty())
1181 {
1182 // invalid mapper response, should never happen
1183 BMCWEB_LOG_ERROR << "SetPIDValues: Mapper Error";
1184 messages::internalError(self->asyncResp->res);
1185 return;
1186 }
1187
1188 const std::string& path = subtree[0].first;
1189 const std::string& owner = subtree[0].second[0].first;
1190 crow::connections::systemBus->async_method_call(
1191 [self, path, owner](
1192 const boost::system::error_code ec,
1193 const boost::container::flat_map<
1194 std::string, std::variant<std::vector<std::string>,
1195 std::string>>& resp) {
1196 if (ec)
1197 {
1198 BMCWEB_LOG_ERROR << "SetPIDValues: Can't get "
1199 "thermalModeIface "
1200 << path;
1201 messages::internalError(self->asyncResp->res);
1202 return;
1203 }
1204 const std::string* current;
1205 const std::vector<std::string>* supported;
1206 for (auto& [key, value] : resp)
1207 {
1208 if (key == "Current")
1209 {
1210 current = std::get_if<std::string>(&value);
1211 if (current == nullptr)
1212 {
1213 BMCWEB_LOG_ERROR
1214 << "SetPIDValues: thermal mode "
1215 "iface invalid "
1216 << path;
1217 messages::internalError(
1218 self->asyncResp->res);
1219 return;
1220 }
1221 }
1222 if (key == "Supported")
1223 {
1224 supported =
1225 std::get_if<std::vector<std::string>>(
1226 &value);
1227 if (supported == nullptr)
1228 {
1229 BMCWEB_LOG_ERROR
1230 << "SetPIDValues: thermal mode "
1231 "iface invalid"
1232 << path;
1233 messages::internalError(
1234 self->asyncResp->res);
1235 return;
1236 }
1237 }
1238 }
1239 if (current == nullptr || supported == nullptr)
1240 {
1241 BMCWEB_LOG_ERROR << "SetPIDValues: thermal mode "
1242 "iface invalid "
1243 << path;
1244 messages::internalError(self->asyncResp->res);
1245 return;
1246 }
1247 self->currentProfile = *current;
1248 self->supportedProfiles = *supported;
1249 self->profileConnection = owner;
1250 self->profilePath = path;
1251 },
1252 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1253 thermalModeIface);
1254 },
1255 "xyz.openbmc_project.ObjectMapper",
1256 "/xyz/openbmc_project/object_mapper",
1257 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
1258 std::array<const char*, 1>{thermalModeIface});
1259 }
1260 ~SetPIDValues()
1261 {
1262 if (asyncResp->res.result() != boost::beast::http::status::ok)
1263 {
1264 return;
1265 }
1266
1267 std::shared_ptr<AsyncResp> response = asyncResp;
1268
1269 if (profile)
1270 {
1271 if (std::find(supportedProfiles.begin(), supportedProfiles.end(),
1272 *profile) == supportedProfiles.end())
1273 {
1274 messages::actionParameterUnknown(response->res, "Profile",
1275 *profile);
1276 return;
1277 }
1278 currentProfile = *profile;
1279 crow::connections::systemBus->async_method_call(
1280 [response](const boost::system::error_code ec) {
1281 if (ec)
1282 {
1283 BMCWEB_LOG_ERROR << "Error patching profile" << ec;
1284 messages::internalError(response->res);
1285 }
1286 },
1287 profileConnection, profilePath,
1288 "org.freedesktop.DBus.Properties", "Set", thermalModeIface,
1289 "Current", std::variant<std::string>(*profile));
1290 }
1291
1292 for (auto& containerPair : configuration)
1293 {
1294 auto& container = containerPair.second;
1295 if (!container)
1296 {
1297 continue;
1298 }
1299 std::string& type = containerPair.first;
1300
1301 for (nlohmann::json::iterator it = container->begin();
1302 it != container->end(); it++)
1303 {
1304 const auto& name = it.key();
1305 auto pathItr =
1306 std::find_if(managedObj.begin(), managedObj.end(),
1307 [&name](const auto& obj) {
1308 return boost::algorithm::ends_with(
1309 obj.first.str, "/" + name);
1310 });
1311 boost::container::flat_map<std::string,
1312 dbus::utility::DbusVariantType>
1313 output;
1314
1315 output.reserve(16); // The pid interface length
1316
1317 // determines if we're patching entity-manager or
1318 // creating a new object
1319 bool createNewObject = (pathItr == managedObj.end());
1320 std::string iface;
1321 if (type == "PidControllers" || type == "FanControllers")
1322 {
1323 iface = pidConfigurationIface;
1324 if (!createNewObject &&
1325 pathItr->second.find(pidConfigurationIface) ==
1326 pathItr->second.end())
1327 {
1328 createNewObject = true;
1329 }
1330 }
1331 else if (type == "FanZones")
1332 {
1333 iface = pidZoneConfigurationIface;
1334 if (!createNewObject &&
1335 pathItr->second.find(pidZoneConfigurationIface) ==
1336 pathItr->second.end())
1337 {
1338
1339 createNewObject = true;
1340 }
1341 }
1342 else if (type == "StepwiseControllers")
1343 {
1344 iface = stepwiseConfigurationIface;
1345 if (!createNewObject &&
1346 pathItr->second.find(stepwiseConfigurationIface) ==
1347 pathItr->second.end())
1348 {
1349 createNewObject = true;
1350 }
1351 }
1352 BMCWEB_LOG_DEBUG << "Create new = " << createNewObject << "\n";
1353 output["Name"] = boost::replace_all_copy(name, "_", " ");
1354
1355 std::string chassis;
1356 CreatePIDRet ret = createPidInterface(
1357 response, type, it, pathItr->first.str, managedObj,
1358 createNewObject, output, chassis, currentProfile);
1359 if (ret == CreatePIDRet::fail)
1360 {
1361 return;
1362 }
1363 else if (ret == CreatePIDRet::del)
1364 {
1365 continue;
1366 }
1367
1368 if (!createNewObject)
1369 {
1370 for (const auto& property : output)
1371 {
1372 crow::connections::systemBus->async_method_call(
1373 [response,
1374 propertyName{std::string(property.first)}](
1375 const boost::system::error_code ec) {
1376 if (ec)
1377 {
1378 BMCWEB_LOG_ERROR << "Error patching "
1379 << propertyName << ": "
1380 << ec;
1381 messages::internalError(response->res);
1382 return;
1383 }
1384 messages::success(response->res);
1385 },
1386 "xyz.openbmc_project.EntityManager",
1387 pathItr->first.str,
1388 "org.freedesktop.DBus.Properties", "Set", iface,
1389 property.first, property.second);
1390 }
1391 }
1392 else
1393 {
1394 if (chassis.empty())
1395 {
1396 BMCWEB_LOG_ERROR << "Failed to get chassis from config";
1397 messages::invalidObject(response->res, name);
1398 return;
1399 }
1400
1401 bool foundChassis = false;
1402 for (const auto& obj : managedObj)
1403 {
1404 if (boost::algorithm::ends_with(obj.first.str, chassis))
1405 {
1406 chassis = obj.first.str;
1407 foundChassis = true;
1408 break;
1409 }
1410 }
1411 if (!foundChassis)
1412 {
1413 BMCWEB_LOG_ERROR << "Failed to find chassis on dbus";
1414 messages::resourceMissingAtURI(
1415 response->res, "/redfish/v1/Chassis/" + chassis);
1416 return;
1417 }
1418
1419 crow::connections::systemBus->async_method_call(
1420 [response](const boost::system::error_code ec) {
1421 if (ec)
1422 {
1423 BMCWEB_LOG_ERROR << "Error Adding Pid Object "
1424 << ec;
1425 messages::internalError(response->res);
1426 return;
1427 }
1428 messages::success(response->res);
1429 },
1430 "xyz.openbmc_project.EntityManager", chassis,
1431 "xyz.openbmc_project.AddObject", "AddObject", output);
1432 }
1433 }
1434 }
1435 }
1436 std::shared_ptr<AsyncResp> asyncResp;
1437 std::vector<std::pair<std::string, std::optional<nlohmann::json>>>
1438 configuration;
1439 std::optional<std::string> profile;
1440 dbus::utility::ManagedObjectType managedObj;
1441 std::vector<std::string> supportedProfiles;
1442 std::string currentProfile;
1443 std::string profileConnection;
1444 std::string profilePath;
1445};
James Feist83ff9ab2018-08-31 10:18:24 -07001446
Ed Tanous1abe55e2018-09-05 08:30:59 -07001447class Manager : public Node
1448{
1449 public:
James Feist5b4aa862018-08-16 14:07:01 -07001450 Manager(CrowApp& app) : Node(app, "/redfish/v1/Managers/bmc/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001451 {
Ed Tanous0f74e642018-11-12 15:17:05 -08001452 uuid = app.template getMiddleware<crow::persistent_data::Middleware>()
1453 .systemUuid;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001454 entityPrivileges = {
1455 {boost::beast::http::verb::get, {{"Login"}}},
1456 {boost::beast::http::verb::head, {{"Login"}}},
1457 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1458 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1459 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1460 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001461 }
1462
Ed Tanous1abe55e2018-09-05 08:30:59 -07001463 private:
James Feist5b4aa862018-08-16 14:07:01 -07001464 void doGet(crow::Response& res, const crow::Request& req,
1465 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -07001466 {
Ed Tanous0f74e642018-11-12 15:17:05 -08001467 res.jsonValue["@odata.id"] = "/redfish/v1/Managers/bmc";
1468 res.jsonValue["@odata.type"] = "#Manager.v1_3_0.Manager";
1469 res.jsonValue["@odata.context"] =
1470 "/redfish/v1/$metadata#Manager.Manager";
1471 res.jsonValue["Id"] = "bmc";
1472 res.jsonValue["Name"] = "OpenBmc Manager";
1473 res.jsonValue["Description"] = "Baseboard Management Controller";
1474 res.jsonValue["PowerState"] = "On";
Ed Tanous029573d2019-02-01 10:57:49 -08001475 res.jsonValue["Status"] = {{"State", "Enabled"}, {"Health", "OK"}};
Ed Tanous0f74e642018-11-12 15:17:05 -08001476 res.jsonValue["ManagerType"] = "BMC";
Ed Tanous3602e232019-05-13 11:11:44 -07001477 res.jsonValue["UUID"] = systemd_utils::getUuid();
1478 res.jsonValue["ServiceEntryPointUUID"] = uuid;
Ed Tanous75176582018-12-14 08:14:34 -08001479 res.jsonValue["Model"] = "OpenBmc"; // TODO(ed), get model
Ed Tanous0f74e642018-11-12 15:17:05 -08001480
1481 res.jsonValue["LogServices"] = {
1482 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices"}};
1483
1484 res.jsonValue["NetworkProtocol"] = {
1485 {"@odata.id", "/redfish/v1/Managers/bmc/NetworkProtocol"}};
1486
1487 res.jsonValue["EthernetInterfaces"] = {
1488 {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces"}};
1489 // default oem data
1490 nlohmann::json& oem = res.jsonValue["Oem"];
1491 nlohmann::json& oemOpenbmc = oem["OpenBmc"];
1492 oem["@odata.type"] = "#OemManager.Oem";
1493 oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem";
1494 oem["@odata.context"] = "/redfish/v1/$metadata#OemManager.Oem";
1495 oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc";
1496 oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc";
1497 oemOpenbmc["@odata.context"] =
1498 "/redfish/v1/$metadata#OemManager.OpenBmc";
1499
Jennifer Leeed5befb2018-08-10 11:29:45 -07001500 // Update Actions object.
Ed Tanous0f74e642018-11-12 15:17:05 -08001501 nlohmann::json& manager_reset =
1502 res.jsonValue["Actions"]["#Manager.Reset"];
Jennifer Leeed5befb2018-08-10 11:29:45 -07001503 manager_reset["target"] =
1504 "/redfish/v1/Managers/bmc/Actions/Manager.Reset";
1505 manager_reset["ResetType@Redfish.AllowableValues"] = {
1506 "GracefulRestart"};
Jennifer Leeca537922018-08-10 10:07:30 -07001507
Andrew Geisslercb92c032018-08-17 07:56:14 -07001508 res.jsonValue["DateTime"] = crow::utility::dateTimeNow();
Santosh Puranik474bfad2019-04-02 16:00:09 +05301509
1510 // Fill in GraphicalConsole and SerialConsole info
1511 res.jsonValue["SerialConsole"]["ServiceEnabled"] = true;
1512 res.jsonValue["SerialConsole"]["ConnectTypesSupported"] = {"IPMI",
1513 "SSH"};
Santosh Puranikef47bb12019-04-30 10:28:52 +05301514#ifdef BMCWEB_ENABLE_KVM
1515 res.jsonValue["GraphicalConsole"]["ServiceEnabled"] = true;
1516 res.jsonValue["GraphicalConsole"]["ConnectTypesSupported"] = {"KVMIP"};
1517#endif // BMCWEB_ENABLE_KVM
Santosh Puranik474bfad2019-04-02 16:00:09 +05301518
Gunnar Mills603a6642019-01-21 17:03:51 -06001519 res.jsonValue["Links"]["ManagerForServers@odata.count"] = 1;
1520 res.jsonValue["Links"]["ManagerForServers"] = {
1521 {{"@odata.id", "/redfish/v1/Systems/system"}}};
Shawn McCarney26f03892019-05-03 13:20:24 -05001522
Jennifer Leeed5befb2018-08-10 11:29:45 -07001523 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
James Feist5b4aa862018-08-16 14:07:01 -07001524
Jennifer Leeca537922018-08-10 10:07:30 -07001525 crow::connections::systemBus->async_method_call(
1526 [asyncResp](const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -07001527 const dbus::utility::ManagedObjectType& resp) {
Jennifer Leeca537922018-08-10 10:07:30 -07001528 if (ec)
1529 {
1530 BMCWEB_LOG_ERROR << "Error while getting Software Version";
Jason M. Billsf12894f2018-10-09 12:45:45 -07001531 messages::internalError(asyncResp->res);
Jennifer Leeca537922018-08-10 10:07:30 -07001532 return;
1533 }
1534
James Feist5b4aa862018-08-16 14:07:01 -07001535 for (auto& objpath : resp)
Jennifer Leeca537922018-08-10 10:07:30 -07001536 {
James Feist5b4aa862018-08-16 14:07:01 -07001537 for (auto& interface : objpath.second)
Jennifer Leeca537922018-08-10 10:07:30 -07001538 {
James Feist5f2caae2018-12-12 14:08:25 -08001539 // If interface is
1540 // xyz.openbmc_project.Software.Version, this is
1541 // what we're looking for.
Jennifer Leeca537922018-08-10 10:07:30 -07001542 if (interface.first ==
1543 "xyz.openbmc_project.Software.Version")
1544 {
1545 // Cut out everyting until last "/", ...
James Feist5b4aa862018-08-16 14:07:01 -07001546 for (auto& property : interface.second)
Jennifer Leeca537922018-08-10 10:07:30 -07001547 {
1548 if (property.first == "Version")
1549 {
James Feist5b4aa862018-08-16 14:07:01 -07001550 const std::string* value =
Ed Tanousabf2add2019-01-22 16:40:12 -08001551 std::get_if<std::string>(
1552 &property.second);
Jennifer Leeca537922018-08-10 10:07:30 -07001553 if (value == nullptr)
1554 {
1555 continue;
1556 }
1557 asyncResp->res
1558 .jsonValue["FirmwareVersion"] = *value;
1559 }
1560 }
1561 }
1562 }
1563 }
1564 },
1565 "xyz.openbmc_project.Software.BMC.Updater",
1566 "/xyz/openbmc_project/software",
1567 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
James Feist0f6b00b2019-06-10 14:15:53 -07001568
James Feist73df0db2019-03-25 15:29:35 -07001569 auto pids = std::make_shared<GetPIDValues>(asyncResp);
1570 pids->run();
Jennifer Leec5d03ff2019-03-08 15:42:58 -08001571
1572 getMainChassisId(asyncResp, [](const std::string& chassisId,
1573 const std::shared_ptr<AsyncResp> aRsp) {
1574 aRsp->res.jsonValue["Links"]["ManagerForChassis@odata.count"] = 1;
1575 aRsp->res.jsonValue["Links"]["ManagerForChassis"] = {
1576 {{"@odata.id", "/redfish/v1/Chassis/" + chassisId}}};
1577 });
James Feist0f6b00b2019-06-10 14:15:53 -07001578
1579 static bool started = false;
1580
1581 if (!started)
1582 {
1583 crow::connections::systemBus->async_method_call(
1584 [asyncResp](const boost::system::error_code ec,
1585 const std::variant<double>& resp) {
1586 if (ec)
1587 {
1588 BMCWEB_LOG_ERROR << "Error while getting progress";
1589 messages::internalError(asyncResp->res);
1590 return;
1591 }
1592 const double* val = std::get_if<double>(&resp);
1593 if (val == nullptr)
1594 {
1595 BMCWEB_LOG_ERROR
1596 << "Invalid response while getting progress";
1597 messages::internalError(asyncResp->res);
1598 return;
1599 }
1600 if (*val < 1.0)
1601 {
1602 asyncResp->res.jsonValue["Status"]["State"] =
1603 "Starting";
1604 started = true;
1605 }
1606 },
1607 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1608 "org.freedesktop.DBus.Properties", "Get",
1609 "org.freedesktop.systemd1.Manager", "Progress");
1610 }
James Feist83ff9ab2018-08-31 10:18:24 -07001611 }
James Feist5b4aa862018-08-16 14:07:01 -07001612
1613 void doPatch(crow::Response& res, const crow::Request& req,
1614 const std::vector<std::string>& params) override
1615 {
Ed Tanous0627a2c2018-11-29 17:09:23 -08001616 std::optional<nlohmann::json> oem;
Santosh Puranikaf5d60582019-03-20 18:16:36 +05301617 std::optional<std::string> datetime;
Ed Tanous0627a2c2018-11-29 17:09:23 -08001618
Santosh Puranikaf5d60582019-03-20 18:16:36 +05301619 if (!json_util::readJson(req, res, "Oem", oem, "DateTime", datetime))
James Feist83ff9ab2018-08-31 10:18:24 -07001620 {
1621 return;
1622 }
Ed Tanous0627a2c2018-11-29 17:09:23 -08001623
James Feist83ff9ab2018-08-31 10:18:24 -07001624 std::shared_ptr<AsyncResp> response = std::make_shared<AsyncResp>(res);
Ed Tanous0627a2c2018-11-29 17:09:23 -08001625
1626 if (oem)
James Feist83ff9ab2018-08-31 10:18:24 -07001627 {
Ed Tanous43b761d2019-02-13 20:10:56 -08001628 std::optional<nlohmann::json> openbmc;
1629 if (!redfish::json_util::readJson(*oem, res, "OpenBmc", openbmc))
James Feist83ff9ab2018-08-31 10:18:24 -07001630 {
Ed Tanous43b761d2019-02-13 20:10:56 -08001631 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
1632 << oem->dump();
1633 return;
1634 }
1635 if (openbmc)
1636 {
1637 std::optional<nlohmann::json> fan;
1638 if (!redfish::json_util::readJson(*openbmc, res, "Fan", fan))
James Feist83ff9ab2018-08-31 10:18:24 -07001639 {
James Feist5f2caae2018-12-12 14:08:25 -08001640 BMCWEB_LOG_ERROR << "Line:" << __LINE__
Ed Tanous43b761d2019-02-13 20:10:56 -08001641 << ", Illegal Property "
1642 << openbmc->dump();
James Feist5f2caae2018-12-12 14:08:25 -08001643 return;
1644 }
Ed Tanous43b761d2019-02-13 20:10:56 -08001645 if (fan)
James Feist5f2caae2018-12-12 14:08:25 -08001646 {
James Feist73df0db2019-03-25 15:29:35 -07001647 auto pid = std::make_shared<SetPIDValues>(response, *fan);
1648 pid->run();
James Feist83ff9ab2018-08-31 10:18:24 -07001649 }
James Feist83ff9ab2018-08-31 10:18:24 -07001650 }
1651 }
Santosh Puranikaf5d60582019-03-20 18:16:36 +05301652 if (datetime)
1653 {
1654 setDateTime(response, std::move(*datetime));
1655 }
1656 }
1657
1658 void setDateTime(std::shared_ptr<AsyncResp> aResp,
1659 std::string datetime) const
1660 {
1661 BMCWEB_LOG_DEBUG << "Set date time: " << datetime;
1662
1663 std::stringstream stream(datetime);
1664 // Convert from ISO 8601 to boost local_time
1665 // (BMC only has time in UTC)
1666 boost::posix_time::ptime posixTime;
1667 boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
1668 // Facet gets deleted with the stringsteam
1669 auto ifc = std::make_unique<boost::local_time::local_time_input_facet>(
1670 "%Y-%m-%d %H:%M:%S%F %ZP");
1671 stream.imbue(std::locale(stream.getloc(), ifc.release()));
1672
1673 boost::local_time::local_date_time ldt(
1674 boost::local_time::not_a_date_time);
1675
1676 if (stream >> ldt)
1677 {
1678 posixTime = ldt.utc_time();
1679 boost::posix_time::time_duration dur = posixTime - epoch;
1680 uint64_t durMicroSecs =
1681 static_cast<uint64_t>(dur.total_microseconds());
1682 crow::connections::systemBus->async_method_call(
1683 [aResp{std::move(aResp)}, datetime{std::move(datetime)}](
1684 const boost::system::error_code ec) {
1685 if (ec)
1686 {
1687 BMCWEB_LOG_DEBUG << "Failed to set elapsed time. "
1688 "DBUS response error "
1689 << ec;
1690 messages::internalError(aResp->res);
1691 return;
1692 }
1693 aResp->res.jsonValue["DateTime"] = datetime;
1694 },
1695 "xyz.openbmc_project.Time.Manager",
1696 "/xyz/openbmc_project/time/bmc",
1697 "org.freedesktop.DBus.Properties", "Set",
1698 "xyz.openbmc_project.Time.EpochTime", "Elapsed",
1699 std::variant<uint64_t>(durMicroSecs));
1700 }
1701 else
1702 {
1703 messages::propertyValueFormatError(aResp->res, datetime,
1704 "DateTime");
1705 return;
1706 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001707 }
1708
Ed Tanous0f74e642018-11-12 15:17:05 -08001709 std::string uuid;
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001710};
1711
Ed Tanous1abe55e2018-09-05 08:30:59 -07001712class ManagerCollection : public Node
1713{
1714 public:
James Feist5b4aa862018-08-16 14:07:01 -07001715 ManagerCollection(CrowApp& app) : Node(app, "/redfish/v1/Managers/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001716 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001717 entityPrivileges = {
1718 {boost::beast::http::verb::get, {{"Login"}}},
1719 {boost::beast::http::verb::head, {{"Login"}}},
1720 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1721 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1722 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1723 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1724 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001725
Ed Tanous1abe55e2018-09-05 08:30:59 -07001726 private:
James Feist5b4aa862018-08-16 14:07:01 -07001727 void doGet(crow::Response& res, const crow::Request& req,
1728 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -07001729 {
James Feist83ff9ab2018-08-31 10:18:24 -07001730 // Collections don't include the static data added by SubRoute
1731 // because it has a duplicate entry for members
Ed Tanous1abe55e2018-09-05 08:30:59 -07001732 res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
1733 res.jsonValue["@odata.type"] = "#ManagerCollection.ManagerCollection";
1734 res.jsonValue["@odata.context"] =
1735 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection";
1736 res.jsonValue["Name"] = "Manager Collection";
1737 res.jsonValue["Members@odata.count"] = 1;
1738 res.jsonValue["Members"] = {
James Feist5b4aa862018-08-16 14:07:01 -07001739 {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001740 res.end();
1741 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001742};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001743} // namespace redfish