blob: e6e80a8a7818707a30952dfe7d2707991b5d34d2 [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"
19
James Feist5b4aa862018-08-16 14:07:01 -070020#include <boost/algorithm/string/replace.hpp>
21#include <dbus_utility.hpp>
Ed Tanousabf2add2019-01-22 16:40:12 -080022#include <variant>
James Feist5b4aa862018-08-16 14:07:01 -070023
Ed Tanous1abe55e2018-09-05 08:30:59 -070024namespace redfish
25{
Jennifer Leeed5befb2018-08-10 11:29:45 -070026
27/**
28 * ManagerActionsReset class supports handle POST method for Reset action.
29 * The class retrieves and sends data directly to dbus.
30 */
31class ManagerActionsReset : public Node
32{
33 public:
34 ManagerActionsReset(CrowApp& app) :
35 Node(app, "/redfish/v1/Managers/bmc/Actions/Manager.Reset/")
36 {
37 entityPrivileges = {
38 {boost::beast::http::verb::get, {{"Login"}}},
39 {boost::beast::http::verb::head, {{"Login"}}},
40 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
41 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
42 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
43 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
44 }
45
46 private:
47 /**
Jennifer Leeed5befb2018-08-10 11:29:45 -070048 * Function handles POST method request.
49 * Analyzes POST body message before sends Reset request data to dbus.
50 * OpenBMC allows for ResetType is GracefulRestart only.
51 */
52 void doPost(crow::Response& res, const crow::Request& req,
53 const std::vector<std::string>& params) override
54 {
55 std::string resetType;
56
57 if (!json_util::readJson(req, res, "ResetType", resetType))
58 {
59 return;
60 }
61
62 if (resetType != "GracefulRestart")
63 {
64 res.result(boost::beast::http::status::bad_request);
65 messages::actionParameterNotSupported(res, resetType, "ResetType");
66 BMCWEB_LOG_ERROR << "Request incorrect action parameter: "
67 << resetType;
68 res.end();
69 return;
70 }
71 doBMCGracefulRestart(res, req, params);
72 }
73
74 /**
75 * Function transceives data with dbus directly.
76 * All BMC state properties will be retrieved before sending reset request.
77 */
78 void doBMCGracefulRestart(crow::Response& res, const crow::Request& req,
79 const std::vector<std::string>& params)
80 {
81 const char* processName = "xyz.openbmc_project.State.BMC";
82 const char* objectPath = "/xyz/openbmc_project/state/bmc0";
83 const char* interfaceName = "xyz.openbmc_project.State.BMC";
84 const std::string& propertyValue =
85 "xyz.openbmc_project.State.BMC.Transition.Reboot";
86 const char* destProperty = "RequestedBMCTransition";
87
88 // Create the D-Bus variant for D-Bus call.
89 VariantType dbusPropertyValue(propertyValue);
90
91 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
92
93 crow::connections::systemBus->async_method_call(
94 [asyncResp](const boost::system::error_code ec) {
95 // Use "Set" method to set the property value.
96 if (ec)
97 {
98 BMCWEB_LOG_ERROR << "[Set] Bad D-Bus request error: " << ec;
99 messages::internalError(asyncResp->res);
100 return;
101 }
102
103 messages::success(asyncResp->res);
104 },
105 processName, objectPath, "org.freedesktop.DBus.Properties", "Set",
106 interfaceName, destProperty, dbusPropertyValue);
107 }
108};
109
James Feist5b4aa862018-08-16 14:07:01 -0700110static constexpr const char* objectManagerIface =
111 "org.freedesktop.DBus.ObjectManager";
112static constexpr const char* pidConfigurationIface =
113 "xyz.openbmc_project.Configuration.Pid";
114static constexpr const char* pidZoneConfigurationIface =
115 "xyz.openbmc_project.Configuration.Pid.Zone";
James Feistb7a08d02018-12-11 14:55:37 -0800116static constexpr const char* stepwiseConfigurationIface =
117 "xyz.openbmc_project.Configuration.Stepwise";
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100118
James Feist5b4aa862018-08-16 14:07:01 -0700119static void asyncPopulatePid(const std::string& connection,
120 const std::string& path,
121 std::shared_ptr<AsyncResp> asyncResp)
122{
123
124 crow::connections::systemBus->async_method_call(
125 [asyncResp](const boost::system::error_code ec,
126 const dbus::utility::ManagedObjectType& managedObj) {
127 if (ec)
128 {
129 BMCWEB_LOG_ERROR << ec;
James Feist5b4aa862018-08-16 14:07:01 -0700130 asyncResp->res.jsonValue.clear();
Jason M. Billsf12894f2018-10-09 12:45:45 -0700131 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700132 return;
133 }
134 nlohmann::json& configRoot =
135 asyncResp->res.jsonValue["Oem"]["OpenBmc"]["Fan"];
136 nlohmann::json& fans = configRoot["FanControllers"];
137 fans["@odata.type"] = "#OemManager.FanControllers";
138 fans["@odata.context"] =
139 "/redfish/v1/$metadata#OemManager.FanControllers";
140 fans["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc/"
141 "Fan/FanControllers";
142
143 nlohmann::json& pids = configRoot["PidControllers"];
144 pids["@odata.type"] = "#OemManager.PidControllers";
145 pids["@odata.context"] =
146 "/redfish/v1/$metadata#OemManager.PidControllers";
147 pids["@odata.id"] =
148 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/PidControllers";
149
James Feistb7a08d02018-12-11 14:55:37 -0800150 nlohmann::json& stepwise = configRoot["StepwiseControllers"];
151 stepwise["@odata.type"] = "#OemManager.StepwiseControllers";
152 stepwise["@odata.context"] =
153 "/redfish/v1/$metadata#OemManager.StepwiseControllers";
154 stepwise["@odata.id"] =
155 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/StepwiseControllers";
156
James Feist5b4aa862018-08-16 14:07:01 -0700157 nlohmann::json& zones = configRoot["FanZones"];
158 zones["@odata.id"] =
159 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones";
160 zones["@odata.type"] = "#OemManager.FanZones";
161 zones["@odata.context"] =
162 "/redfish/v1/$metadata#OemManager.FanZones";
163 configRoot["@odata.id"] =
164 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan";
165 configRoot["@odata.type"] = "#OemManager.Fan";
166 configRoot["@odata.context"] =
167 "/redfish/v1/$metadata#OemManager.Fan";
168
James Feist5b4aa862018-08-16 14:07:01 -0700169 for (const auto& pathPair : managedObj)
170 {
171 for (const auto& intfPair : pathPair.second)
172 {
173 if (intfPair.first != pidConfigurationIface &&
James Feistb7a08d02018-12-11 14:55:37 -0800174 intfPair.first != pidZoneConfigurationIface &&
175 intfPair.first != stepwiseConfigurationIface)
James Feist5b4aa862018-08-16 14:07:01 -0700176 {
177 continue;
178 }
179 auto findName = intfPair.second.find("Name");
180 if (findName == intfPair.second.end())
181 {
182 BMCWEB_LOG_ERROR << "Pid Field missing Name";
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800183 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700184 return;
185 }
186 const std::string* namePtr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800187 std::get_if<std::string>(&findName->second);
James Feist5b4aa862018-08-16 14:07:01 -0700188 if (namePtr == nullptr)
189 {
190 BMCWEB_LOG_ERROR << "Pid Name Field illegal";
James Feistb7a08d02018-12-11 14:55:37 -0800191 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700192 return;
193 }
194
195 std::string name = *namePtr;
196 dbus::utility::escapePathForDbus(name);
James Feistb7a08d02018-12-11 14:55:37 -0800197 nlohmann::json* config = nullptr;
James Feistc33a90e2019-03-01 10:17:44 -0800198
199 const std::string* classPtr = nullptr;
200 auto findClass = intfPair.second.find("Class");
201 if (findClass != intfPair.second.end())
202 {
203 classPtr = std::get_if<std::string>(&findClass->second);
204 }
205
James Feist5b4aa862018-08-16 14:07:01 -0700206 if (intfPair.first == pidZoneConfigurationIface)
207 {
208 std::string chassis;
209 if (!dbus::utility::getNthStringFromPath(
210 pathPair.first.str, 5, chassis))
211 {
212 chassis = "#IllegalValue";
213 }
214 nlohmann::json& zone = zones[name];
215 zone["Chassis"] = {
216 {"@odata.id", "/redfish/v1/Chassis/" + chassis}};
217 zone["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/"
218 "OpenBmc/Fan/FanZones/" +
219 name;
220 zone["@odata.type"] = "#OemManager.FanZone";
221 zone["@odata.context"] =
222 "/redfish/v1/$metadata#OemManager.FanZone";
James Feistb7a08d02018-12-11 14:55:37 -0800223 config = &zone;
James Feist5b4aa862018-08-16 14:07:01 -0700224 }
225
James Feistb7a08d02018-12-11 14:55:37 -0800226 else if (intfPair.first == stepwiseConfigurationIface)
227 {
James Feistc33a90e2019-03-01 10:17:44 -0800228 if (classPtr == nullptr)
229 {
230 BMCWEB_LOG_ERROR << "Pid Class Field illegal";
231 messages::internalError(asyncResp->res);
232 return;
233 }
234
James Feistb7a08d02018-12-11 14:55:37 -0800235 nlohmann::json& controller = stepwise[name];
236 config = &controller;
237
238 controller["@odata.id"] =
239 "/redfish/v1/Managers/bmc#/Oem/"
240 "OpenBmc/Fan/StepwiseControllers/" +
241 std::string(name);
242 controller["@odata.type"] =
243 "#OemManager.StepwiseController";
244
245 controller["@odata.context"] =
246 "/redfish/v1/"
247 "$metadata#OemManager.StepwiseController";
James Feistc33a90e2019-03-01 10:17:44 -0800248 controller["Direction"] = *classPtr;
James Feistb7a08d02018-12-11 14:55:37 -0800249 }
250
251 // pid and fans are off the same configuration
252 else if (intfPair.first == pidConfigurationIface)
253 {
James Feistc33a90e2019-03-01 10:17:44 -0800254
James Feistb7a08d02018-12-11 14:55:37 -0800255 if (classPtr == nullptr)
256 {
257 BMCWEB_LOG_ERROR << "Pid Class Field illegal";
258 messages::internalError(asyncResp->res);
259 return;
260 }
261 bool isFan = *classPtr == "fan";
262 nlohmann::json& element =
263 isFan ? fans[name] : pids[name];
264 config = &element;
265 if (isFan)
266 {
267 element["@odata.id"] =
268 "/redfish/v1/Managers/bmc#/Oem/"
269 "OpenBmc/Fan/FanControllers/" +
270 std::string(name);
271 element["@odata.type"] =
272 "#OemManager.FanController";
273
274 element["@odata.context"] =
275 "/redfish/v1/"
276 "$metadata#OemManager.FanController";
277 }
278 else
279 {
280 element["@odata.id"] =
281 "/redfish/v1/Managers/bmc#/Oem/"
282 "OpenBmc/Fan/PidControllers/" +
283 std::string(name);
284 element["@odata.type"] =
285 "#OemManager.PidController";
286 element["@odata.context"] =
287 "/redfish/v1/$metadata"
288 "#OemManager.PidController";
289 }
290 }
291 else
292 {
293 BMCWEB_LOG_ERROR << "Unexpected configuration";
294 messages::internalError(asyncResp->res);
295 return;
296 }
297
298 // used for making maps out of 2 vectors
299 const std::vector<double>* keys = nullptr;
300 const std::vector<double>* values = nullptr;
301
James Feist5b4aa862018-08-16 14:07:01 -0700302 for (const auto& propertyPair : intfPair.second)
303 {
304 if (propertyPair.first == "Type" ||
305 propertyPair.first == "Class" ||
306 propertyPair.first == "Name")
307 {
308 continue;
309 }
310
311 // zones
312 if (intfPair.first == pidZoneConfigurationIface)
313 {
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800314 const double* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800315 std::get_if<double>(&propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700316 if (ptr == nullptr)
317 {
318 BMCWEB_LOG_ERROR << "Field Illegal "
319 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700320 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700321 return;
322 }
James Feistb7a08d02018-12-11 14:55:37 -0800323 (*config)[propertyPair.first] = *ptr;
324 }
325
326 if (intfPair.first == stepwiseConfigurationIface)
327 {
328 if (propertyPair.first == "Reading" ||
329 propertyPair.first == "Output")
330 {
331 const std::vector<double>* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800332 std::get_if<std::vector<double>>(
James Feistb7a08d02018-12-11 14:55:37 -0800333 &propertyPair.second);
334
335 if (ptr == nullptr)
336 {
337 BMCWEB_LOG_ERROR << "Field Illegal "
338 << propertyPair.first;
339 messages::internalError(asyncResp->res);
340 return;
341 }
342
343 if (propertyPair.first == "Reading")
344 {
345 keys = ptr;
346 }
347 else
348 {
349 values = ptr;
350 }
351 if (keys && values)
352 {
353 if (keys->size() != values->size())
354 {
355 BMCWEB_LOG_ERROR
356 << "Reading and Output size don't "
357 "match ";
358 messages::internalError(asyncResp->res);
359 return;
360 }
361 nlohmann::json& steps = (*config)["Steps"];
362 steps = nlohmann::json::array();
363 for (size_t ii = 0; ii < keys->size(); ii++)
364 {
365 steps.push_back(
366 {{"Target", (*keys)[ii]},
367 {"Output", (*values)[ii]}});
368 }
369 }
370 }
371 if (propertyPair.first == "NegativeHysteresis" ||
372 propertyPair.first == "PositiveHysteresis")
373 {
374 const double* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800375 std::get_if<double>(&propertyPair.second);
James Feistb7a08d02018-12-11 14:55:37 -0800376 if (ptr == nullptr)
377 {
378 BMCWEB_LOG_ERROR << "Field Illegal "
379 << propertyPair.first;
380 messages::internalError(asyncResp->res);
381 return;
382 }
383 (*config)[propertyPair.first] = *ptr;
384 }
James Feist5b4aa862018-08-16 14:07:01 -0700385 }
386
387 // pid and fans are off the same configuration
James Feistb7a08d02018-12-11 14:55:37 -0800388 if (intfPair.first == pidConfigurationIface ||
389 intfPair.first == stepwiseConfigurationIface)
James Feist5b4aa862018-08-16 14:07:01 -0700390 {
James Feist5b4aa862018-08-16 14:07:01 -0700391
392 if (propertyPair.first == "Zones")
393 {
394 const std::vector<std::string>* inputs =
Ed Tanousabf2add2019-01-22 16:40:12 -0800395 std::get_if<std::vector<std::string>>(
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800396 &propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700397
398 if (inputs == nullptr)
399 {
400 BMCWEB_LOG_ERROR
401 << "Zones Pid Field Illegal";
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800402 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700403 return;
404 }
James Feistb7a08d02018-12-11 14:55:37 -0800405 auto& data = (*config)[propertyPair.first];
James Feist5b4aa862018-08-16 14:07:01 -0700406 data = nlohmann::json::array();
407 for (std::string itemCopy : *inputs)
408 {
409 dbus::utility::escapePathForDbus(itemCopy);
410 data.push_back(
411 {{"@odata.id",
412 "/redfish/v1/Managers/bmc#/Oem/"
413 "OpenBmc/Fan/FanZones/" +
414 itemCopy}});
415 }
416 }
417 // todo(james): may never happen, but this
418 // assumes configuration data referenced in the
419 // PID config is provided by the same daemon, we
420 // could add another loop to cover all cases,
421 // but I'm okay kicking this can down the road a
422 // bit
423
424 else if (propertyPair.first == "Inputs" ||
425 propertyPair.first == "Outputs")
426 {
James Feistb7a08d02018-12-11 14:55:37 -0800427 auto& data = (*config)[propertyPair.first];
James Feist5b4aa862018-08-16 14:07:01 -0700428 const std::vector<std::string>* inputs =
Ed Tanousabf2add2019-01-22 16:40:12 -0800429 std::get_if<std::vector<std::string>>(
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800430 &propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700431
432 if (inputs == nullptr)
433 {
434 BMCWEB_LOG_ERROR << "Field Illegal "
435 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700436 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700437 return;
438 }
439 data = *inputs;
440 } // doubles
441 else if (propertyPair.first ==
442 "FFGainCoefficient" ||
443 propertyPair.first == "FFOffCoefficient" ||
444 propertyPair.first == "ICoefficient" ||
445 propertyPair.first == "ILimitMax" ||
446 propertyPair.first == "ILimitMin" ||
James Feistaad1a252019-02-19 10:13:52 -0800447 propertyPair.first ==
448 "PositiveHysteresis" ||
449 propertyPair.first ==
450 "NegativeHysteresis" ||
James Feist5b4aa862018-08-16 14:07:01 -0700451 propertyPair.first == "OutLimitMax" ||
452 propertyPair.first == "OutLimitMin" ||
453 propertyPair.first == "PCoefficient" ||
James Feist7625cb82019-01-23 11:58:21 -0800454 propertyPair.first == "SetPoint" ||
James Feist5b4aa862018-08-16 14:07:01 -0700455 propertyPair.first == "SlewNeg" ||
456 propertyPair.first == "SlewPos")
457 {
458 const double* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800459 std::get_if<double>(&propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700460 if (ptr == nullptr)
461 {
462 BMCWEB_LOG_ERROR << "Field Illegal "
463 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700464 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700465 return;
466 }
James Feistb7a08d02018-12-11 14:55:37 -0800467 (*config)[propertyPair.first] = *ptr;
James Feist5b4aa862018-08-16 14:07:01 -0700468 }
469 }
470 }
471 }
472 }
473 },
474 connection, path, objectManagerIface, "GetManagedObjects");
475}
Jennifer Leeca537922018-08-10 10:07:30 -0700476
James Feist83ff9ab2018-08-31 10:18:24 -0700477enum class CreatePIDRet
478{
479 fail,
480 del,
481 patch
482};
483
James Feist5f2caae2018-12-12 14:08:25 -0800484static bool getZonesFromJsonReq(const std::shared_ptr<AsyncResp>& response,
485 std::vector<nlohmann::json>& config,
486 std::vector<std::string>& zones)
487{
James Feistb6baeaa2019-02-21 10:41:40 -0800488 if (config.empty())
489 {
490 BMCWEB_LOG_ERROR << "Empty Zones";
491 messages::propertyValueFormatError(response->res,
492 nlohmann::json::array(), "Zones");
493 return false;
494 }
James Feist5f2caae2018-12-12 14:08:25 -0800495 for (auto& odata : config)
496 {
497 std::string path;
498 if (!redfish::json_util::readJson(odata, response->res, "@odata.id",
499 path))
500 {
501 return false;
502 }
503 std::string input;
James Feist61adbda2019-03-25 13:03:51 -0700504
505 // 8 below comes from
506 // /redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones/Left
507 // 0 1 2 3 4 5 6 7 8
508 if (!dbus::utility::getNthStringFromPath(path, 8, input))
James Feist5f2caae2018-12-12 14:08:25 -0800509 {
510 BMCWEB_LOG_ERROR << "Got invalid path " << path;
511 BMCWEB_LOG_ERROR << "Illegal Type Zones";
512 messages::propertyValueFormatError(response->res, odata.dump(),
513 "Zones");
514 return false;
515 }
516 boost::replace_all(input, "_", " ");
517 zones.emplace_back(std::move(input));
518 }
519 return true;
520}
521
James Feistb6baeaa2019-02-21 10:41:40 -0800522static bool findChassis(const dbus::utility::ManagedObjectType& managedObj,
523 const std::string& value, std::string& chassis)
524{
525 BMCWEB_LOG_DEBUG << "Find Chassis: " << value << "\n";
526
527 std::string escaped = boost::replace_all_copy(value, " ", "_");
528 escaped = "/" + escaped;
529 auto it = std::find_if(
530 managedObj.begin(), managedObj.end(), [&escaped](const auto& obj) {
531 if (boost::algorithm::ends_with(obj.first.str, escaped))
532 {
533 BMCWEB_LOG_DEBUG << "Matched " << obj.first.str << "\n";
534 return true;
535 }
536 return false;
537 });
538
539 if (it == managedObj.end())
540 {
541 return false;
542 }
543 // 5 comes from <chassis-name> being the 5th element
544 // /xyz/openbmc_project/inventory/system/chassis/<chassis-name>
545 return dbus::utility::getNthStringFromPath(it->first.str, 5, chassis);
546}
547
James Feist83ff9ab2018-08-31 10:18:24 -0700548static CreatePIDRet createPidInterface(
549 const std::shared_ptr<AsyncResp>& response, const std::string& type,
James Feistb6baeaa2019-02-21 10:41:40 -0800550 nlohmann::json::iterator it, const std::string& path,
James Feist83ff9ab2018-08-31 10:18:24 -0700551 const dbus::utility::ManagedObjectType& managedObj, bool createNewObject,
552 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>&
553 output,
554 std::string& chassis)
555{
556
James Feist5f2caae2018-12-12 14:08:25 -0800557 // common deleter
James Feistb6baeaa2019-02-21 10:41:40 -0800558 if (it.value() == nullptr)
James Feist5f2caae2018-12-12 14:08:25 -0800559 {
560 std::string iface;
561 if (type == "PidControllers" || type == "FanControllers")
562 {
563 iface = pidConfigurationIface;
564 }
565 else if (type == "FanZones")
566 {
567 iface = pidZoneConfigurationIface;
568 }
569 else if (type == "StepwiseControllers")
570 {
571 iface = stepwiseConfigurationIface;
572 }
573 else
574 {
575 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Type "
576 << type;
577 messages::propertyUnknown(response->res, type);
578 return CreatePIDRet::fail;
579 }
580 // delete interface
581 crow::connections::systemBus->async_method_call(
582 [response, path](const boost::system::error_code ec) {
583 if (ec)
584 {
585 BMCWEB_LOG_ERROR << "Error patching " << path << ": " << ec;
586 messages::internalError(response->res);
James Feistb6baeaa2019-02-21 10:41:40 -0800587 return;
James Feist5f2caae2018-12-12 14:08:25 -0800588 }
James Feistb6baeaa2019-02-21 10:41:40 -0800589 messages::success(response->res);
James Feist5f2caae2018-12-12 14:08:25 -0800590 },
591 "xyz.openbmc_project.EntityManager", path, iface, "Delete");
592 return CreatePIDRet::del;
593 }
594
James Feistb6baeaa2019-02-21 10:41:40 -0800595 if (!createNewObject)
596 {
597 // if we aren't creating a new object, we should be able to find it on
598 // d-bus
599 if (!findChassis(managedObj, it.key(), chassis))
600 {
601 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
602 messages::invalidObject(response->res, it.key());
603 return CreatePIDRet::fail;
604 }
605 }
606
James Feist83ff9ab2018-08-31 10:18:24 -0700607 if (type == "PidControllers" || type == "FanControllers")
608 {
609 if (createNewObject)
610 {
611 output["Class"] = type == "PidControllers" ? std::string("temp")
612 : std::string("fan");
613 output["Type"] = std::string("Pid");
614 }
James Feist5f2caae2018-12-12 14:08:25 -0800615
616 std::optional<std::vector<nlohmann::json>> zones;
617 std::optional<std::vector<std::string>> inputs;
618 std::optional<std::vector<std::string>> outputs;
619 std::map<std::string, std::optional<double>> doubles;
620 if (!redfish::json_util::readJson(
James Feistb6baeaa2019-02-21 10:41:40 -0800621 it.value(), response->res, "Inputs", inputs, "Outputs", outputs,
James Feist5f2caae2018-12-12 14:08:25 -0800622 "Zones", zones, "FFGainCoefficient",
623 doubles["FFGainCoefficient"], "FFOffCoefficient",
624 doubles["FFOffCoefficient"], "ICoefficient",
625 doubles["ICoefficient"], "ILimitMax", doubles["ILimitMax"],
626 "ILimitMin", doubles["ILimitMin"], "OutLimitMax",
627 doubles["OutLimitMax"], "OutLimitMin", doubles["OutLimitMin"],
628 "PCoefficient", doubles["PCoefficient"], "SetPoint",
629 doubles["SetPoint"], "SlewNeg", doubles["SlewNeg"], "SlewPos",
James Feistaad1a252019-02-19 10:13:52 -0800630 doubles["SlewPos"], "PositiveHysteresis",
631 doubles["PositiveHysteresis"], "NegativeHysteresis",
632 doubles["NegativeHysteresis"]))
James Feist83ff9ab2018-08-31 10:18:24 -0700633 {
James Feist5f2caae2018-12-12 14:08:25 -0800634 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
James Feistb6baeaa2019-02-21 10:41:40 -0800635 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800636 return CreatePIDRet::fail;
James Feist83ff9ab2018-08-31 10:18:24 -0700637 }
James Feist5f2caae2018-12-12 14:08:25 -0800638 if (zones)
James Feist83ff9ab2018-08-31 10:18:24 -0700639 {
James Feist5f2caae2018-12-12 14:08:25 -0800640 std::vector<std::string> zonesStr;
641 if (!getZonesFromJsonReq(response, *zones, zonesStr))
James Feist83ff9ab2018-08-31 10:18:24 -0700642 {
James Feist5f2caae2018-12-12 14:08:25 -0800643 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Zones";
644 return CreatePIDRet::fail;
James Feist83ff9ab2018-08-31 10:18:24 -0700645 }
James Feistb6baeaa2019-02-21 10:41:40 -0800646 if (chassis.empty() &&
647 !findChassis(managedObj, zonesStr[0], chassis))
648 {
649 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
650 messages::invalidObject(response->res, it.key());
651 return CreatePIDRet::fail;
652 }
653
James Feist5f2caae2018-12-12 14:08:25 -0800654 output["Zones"] = std::move(zonesStr);
655 }
656 if (inputs || outputs)
657 {
658 std::array<std::optional<std::vector<std::string>>*, 2> containers =
659 {&inputs, &outputs};
660 size_t index = 0;
661 for (const auto& containerPtr : containers)
James Feist83ff9ab2018-08-31 10:18:24 -0700662 {
James Feist5f2caae2018-12-12 14:08:25 -0800663 std::optional<std::vector<std::string>>& container =
664 *containerPtr;
665 if (!container)
James Feist83ff9ab2018-08-31 10:18:24 -0700666 {
James Feist5f2caae2018-12-12 14:08:25 -0800667 index++;
668 continue;
James Feist83ff9ab2018-08-31 10:18:24 -0700669 }
James Feist5f2caae2018-12-12 14:08:25 -0800670
671 for (std::string& value : *container)
James Feist83ff9ab2018-08-31 10:18:24 -0700672 {
James Feist5f2caae2018-12-12 14:08:25 -0800673 boost::replace_all(value, "_", " ");
James Feist83ff9ab2018-08-31 10:18:24 -0700674 }
James Feist5f2caae2018-12-12 14:08:25 -0800675 std::string key;
676 if (index == 0)
James Feist83ff9ab2018-08-31 10:18:24 -0700677 {
James Feist5f2caae2018-12-12 14:08:25 -0800678 key = "Inputs";
James Feist83ff9ab2018-08-31 10:18:24 -0700679 }
James Feist5f2caae2018-12-12 14:08:25 -0800680 else
681 {
682 key = "Outputs";
683 }
684 output[key] = *container;
685 index++;
James Feist83ff9ab2018-08-31 10:18:24 -0700686 }
James Feist5f2caae2018-12-12 14:08:25 -0800687 }
James Feist83ff9ab2018-08-31 10:18:24 -0700688
James Feist5f2caae2018-12-12 14:08:25 -0800689 // doubles
690 for (const auto& pairs : doubles)
691 {
692 if (!pairs.second)
James Feist83ff9ab2018-08-31 10:18:24 -0700693 {
James Feist5f2caae2018-12-12 14:08:25 -0800694 continue;
James Feist83ff9ab2018-08-31 10:18:24 -0700695 }
James Feist5f2caae2018-12-12 14:08:25 -0800696 BMCWEB_LOG_DEBUG << pairs.first << " = " << *pairs.second;
697 output[pairs.first] = *(pairs.second);
James Feist83ff9ab2018-08-31 10:18:24 -0700698 }
699 }
James Feist5f2caae2018-12-12 14:08:25 -0800700
James Feist83ff9ab2018-08-31 10:18:24 -0700701 else if (type == "FanZones")
702 {
James Feist83ff9ab2018-08-31 10:18:24 -0700703 output["Type"] = std::string("Pid.Zone");
704
James Feist5f2caae2018-12-12 14:08:25 -0800705 std::optional<nlohmann::json> chassisContainer;
706 std::optional<double> failSafePercent;
James Feistd3ec07f2019-02-25 14:51:15 -0800707 std::optional<double> minThermalOutput;
James Feistb6baeaa2019-02-21 10:41:40 -0800708 if (!redfish::json_util::readJson(it.value(), response->res, "Chassis",
James Feist5f2caae2018-12-12 14:08:25 -0800709 chassisContainer, "FailSafePercent",
James Feistd3ec07f2019-02-25 14:51:15 -0800710 failSafePercent, "MinThermalOutput",
711 minThermalOutput))
James Feist83ff9ab2018-08-31 10:18:24 -0700712 {
James Feist5f2caae2018-12-12 14:08:25 -0800713 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
James Feistb6baeaa2019-02-21 10:41:40 -0800714 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800715 return CreatePIDRet::fail;
716 }
James Feist83ff9ab2018-08-31 10:18:24 -0700717
James Feist5f2caae2018-12-12 14:08:25 -0800718 if (chassisContainer)
719 {
720
721 std::string chassisId;
722 if (!redfish::json_util::readJson(*chassisContainer, response->res,
723 "@odata.id", chassisId))
James Feist83ff9ab2018-08-31 10:18:24 -0700724 {
James Feist5f2caae2018-12-12 14:08:25 -0800725 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
726 << chassisContainer->dump();
James Feist83ff9ab2018-08-31 10:18:24 -0700727 return CreatePIDRet::fail;
728 }
James Feist5f2caae2018-12-12 14:08:25 -0800729
730 // /refish/v1/chassis/chassis_name/
731 if (!dbus::utility::getNthStringFromPath(chassisId, 3, chassis))
732 {
733 BMCWEB_LOG_ERROR << "Got invalid path " << chassisId;
734 messages::invalidObject(response->res, chassisId);
735 return CreatePIDRet::fail;
736 }
737 }
James Feistd3ec07f2019-02-25 14:51:15 -0800738 if (minThermalOutput)
James Feist5f2caae2018-12-12 14:08:25 -0800739 {
James Feistd3ec07f2019-02-25 14:51:15 -0800740 output["MinThermalOutput"] = *minThermalOutput;
James Feist5f2caae2018-12-12 14:08:25 -0800741 }
742 if (failSafePercent)
743 {
744 output["FailSafePercent"] = *failSafePercent;
745 }
746 }
747 else if (type == "StepwiseControllers")
748 {
749 output["Type"] = std::string("Stepwise");
750
751 std::optional<std::vector<nlohmann::json>> zones;
752 std::optional<std::vector<nlohmann::json>> steps;
753 std::optional<std::vector<std::string>> inputs;
754 std::optional<double> positiveHysteresis;
755 std::optional<double> negativeHysteresis;
James Feistc33a90e2019-03-01 10:17:44 -0800756 std::optional<std::string> direction; // upper clipping curve vs lower
James Feist5f2caae2018-12-12 14:08:25 -0800757 if (!redfish::json_util::readJson(
James Feistb6baeaa2019-02-21 10:41:40 -0800758 it.value(), response->res, "Zones", zones, "Steps", steps,
759 "Inputs", inputs, "PositiveHysteresis", positiveHysteresis,
James Feistc33a90e2019-03-01 10:17:44 -0800760 "NegativeHysteresis", negativeHysteresis, "Direction",
761 direction))
James Feist5f2caae2018-12-12 14:08:25 -0800762 {
763 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
James Feistb6baeaa2019-02-21 10:41:40 -0800764 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800765 return CreatePIDRet::fail;
766 }
767
768 if (zones)
769 {
James Feistb6baeaa2019-02-21 10:41:40 -0800770 std::vector<std::string> zonesStrs;
771 if (!getZonesFromJsonReq(response, *zones, zonesStrs))
James Feist5f2caae2018-12-12 14:08:25 -0800772 {
773 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Zones";
774 return CreatePIDRet::fail;
775 }
James Feistb6baeaa2019-02-21 10:41:40 -0800776 if (chassis.empty() &&
777 !findChassis(managedObj, zonesStrs[0], chassis))
778 {
779 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
780 messages::invalidObject(response->res, it.key());
781 return CreatePIDRet::fail;
782 }
783 output["Zones"] = std::move(zonesStrs);
James Feist5f2caae2018-12-12 14:08:25 -0800784 }
785 if (steps)
786 {
787 std::vector<double> readings;
788 std::vector<double> outputs;
789 for (auto& step : *steps)
790 {
791 double target;
Ed Tanousb01bf292019-03-25 19:25:26 +0000792 double output;
James Feist5f2caae2018-12-12 14:08:25 -0800793
794 if (!redfish::json_util::readJson(step, response->res, "Target",
Ed Tanousb01bf292019-03-25 19:25:26 +0000795 target, "Output", output))
James Feist5f2caae2018-12-12 14:08:25 -0800796 {
797 BMCWEB_LOG_ERROR << "Line:" << __LINE__
James Feistb6baeaa2019-02-21 10:41:40 -0800798 << ", Illegal Property "
799 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800800 return CreatePIDRet::fail;
801 }
802 readings.emplace_back(target);
Ed Tanousb01bf292019-03-25 19:25:26 +0000803 outputs.emplace_back(output);
James Feist5f2caae2018-12-12 14:08:25 -0800804 }
805 output["Reading"] = std::move(readings);
806 output["Output"] = std::move(outputs);
807 }
808 if (inputs)
809 {
810 for (std::string& value : *inputs)
811 {
James Feist5f2caae2018-12-12 14:08:25 -0800812 boost::replace_all(value, "_", " ");
813 }
814 output["Inputs"] = std::move(*inputs);
815 }
816 if (negativeHysteresis)
817 {
818 output["NegativeHysteresis"] = *negativeHysteresis;
819 }
820 if (positiveHysteresis)
821 {
822 output["PositiveHysteresis"] = *positiveHysteresis;
James Feist83ff9ab2018-08-31 10:18:24 -0700823 }
James Feistc33a90e2019-03-01 10:17:44 -0800824 if (direction)
825 {
826 constexpr const std::array<const char*, 2> allowedDirections = {
827 "Ceiling", "Floor"};
828 if (std::find(allowedDirections.begin(), allowedDirections.end(),
829 *direction) == allowedDirections.end())
830 {
831 messages::propertyValueTypeError(response->res, "Direction",
832 *direction);
833 return CreatePIDRet::fail;
834 }
835 output["Class"] = *direction;
836 }
James Feist83ff9ab2018-08-31 10:18:24 -0700837 }
838 else
839 {
James Feist5f2caae2018-12-12 14:08:25 -0800840 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Type " << type;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700841 messages::propertyUnknown(response->res, type);
James Feist83ff9ab2018-08-31 10:18:24 -0700842 return CreatePIDRet::fail;
843 }
844 return CreatePIDRet::patch;
845}
846
Ed Tanous1abe55e2018-09-05 08:30:59 -0700847class Manager : public Node
848{
849 public:
James Feist5b4aa862018-08-16 14:07:01 -0700850 Manager(CrowApp& app) : Node(app, "/redfish/v1/Managers/bmc/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700851 {
Ed Tanous0f74e642018-11-12 15:17:05 -0800852 uuid = app.template getMiddleware<crow::persistent_data::Middleware>()
853 .systemUuid;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700854 entityPrivileges = {
855 {boost::beast::http::verb::get, {{"Login"}}},
856 {boost::beast::http::verb::head, {{"Login"}}},
857 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
858 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
859 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
860 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100861 }
862
Ed Tanous1abe55e2018-09-05 08:30:59 -0700863 private:
James Feist5b4aa862018-08-16 14:07:01 -0700864 void getPidValues(std::shared_ptr<AsyncResp> asyncResp)
865 {
866 crow::connections::systemBus->async_method_call(
867 [asyncResp](const boost::system::error_code ec,
868 const crow::openbmc_mapper::GetSubTreeType& subtree) {
869 if (ec)
870 {
871 BMCWEB_LOG_ERROR << ec;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700872 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700873 return;
874 }
875
876 // create map of <connection, path to objMgr>>
877 boost::container::flat_map<std::string, std::string>
878 objectMgrPaths;
James Feist6bce33b2018-10-22 12:05:56 -0700879 boost::container::flat_set<std::string> calledConnections;
James Feist5b4aa862018-08-16 14:07:01 -0700880 for (const auto& pathGroup : subtree)
881 {
882 for (const auto& connectionGroup : pathGroup.second)
883 {
James Feist6bce33b2018-10-22 12:05:56 -0700884 auto findConnection =
885 calledConnections.find(connectionGroup.first);
886 if (findConnection != calledConnections.end())
887 {
888 break;
889 }
James Feist5b4aa862018-08-16 14:07:01 -0700890 for (const std::string& interface :
891 connectionGroup.second)
892 {
893 if (interface == objectManagerIface)
894 {
895 objectMgrPaths[connectionGroup.first] =
896 pathGroup.first;
897 }
898 // this list is alphabetical, so we
899 // should have found the objMgr by now
900 if (interface == pidConfigurationIface ||
James Feistb7a08d02018-12-11 14:55:37 -0800901 interface == pidZoneConfigurationIface ||
902 interface == stepwiseConfigurationIface)
James Feist5b4aa862018-08-16 14:07:01 -0700903 {
904 auto findObjMgr =
905 objectMgrPaths.find(connectionGroup.first);
906 if (findObjMgr == objectMgrPaths.end())
907 {
908 BMCWEB_LOG_DEBUG << connectionGroup.first
909 << "Has no Object Manager";
910 continue;
911 }
James Feist6bce33b2018-10-22 12:05:56 -0700912
913 calledConnections.insert(connectionGroup.first);
914
James Feist5b4aa862018-08-16 14:07:01 -0700915 asyncPopulatePid(findObjMgr->first,
916 findObjMgr->second, asyncResp);
917 break;
918 }
919 }
920 }
921 }
922 },
923 "xyz.openbmc_project.ObjectMapper",
924 "/xyz/openbmc_project/object_mapper",
925 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
James Feistb7a08d02018-12-11 14:55:37 -0800926 std::array<const char*, 4>{
927 pidConfigurationIface, pidZoneConfigurationIface,
928 objectManagerIface, stepwiseConfigurationIface});
James Feist5b4aa862018-08-16 14:07:01 -0700929 }
930
931 void doGet(crow::Response& res, const crow::Request& req,
932 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700933 {
Ed Tanous0f74e642018-11-12 15:17:05 -0800934 res.jsonValue["@odata.id"] = "/redfish/v1/Managers/bmc";
935 res.jsonValue["@odata.type"] = "#Manager.v1_3_0.Manager";
936 res.jsonValue["@odata.context"] =
937 "/redfish/v1/$metadata#Manager.Manager";
938 res.jsonValue["Id"] = "bmc";
939 res.jsonValue["Name"] = "OpenBmc Manager";
940 res.jsonValue["Description"] = "Baseboard Management Controller";
941 res.jsonValue["PowerState"] = "On";
Ed Tanous029573d2019-02-01 10:57:49 -0800942 res.jsonValue["Status"] = {{"State", "Enabled"}, {"Health", "OK"}};
Ed Tanous0f74e642018-11-12 15:17:05 -0800943 res.jsonValue["ManagerType"] = "BMC";
Ed Tanous75176582018-12-14 08:14:34 -0800944 res.jsonValue["UUID"] = uuid;
945 res.jsonValue["Model"] = "OpenBmc"; // TODO(ed), get model
Ed Tanous0f74e642018-11-12 15:17:05 -0800946
947 res.jsonValue["LogServices"] = {
948 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices"}};
949
950 res.jsonValue["NetworkProtocol"] = {
951 {"@odata.id", "/redfish/v1/Managers/bmc/NetworkProtocol"}};
952
953 res.jsonValue["EthernetInterfaces"] = {
954 {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces"}};
955 // default oem data
956 nlohmann::json& oem = res.jsonValue["Oem"];
957 nlohmann::json& oemOpenbmc = oem["OpenBmc"];
958 oem["@odata.type"] = "#OemManager.Oem";
959 oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem";
960 oem["@odata.context"] = "/redfish/v1/$metadata#OemManager.Oem";
961 oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc";
962 oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc";
963 oemOpenbmc["@odata.context"] =
964 "/redfish/v1/$metadata#OemManager.OpenBmc";
965
Jennifer Leeed5befb2018-08-10 11:29:45 -0700966 // Update Actions object.
Ed Tanous0f74e642018-11-12 15:17:05 -0800967 nlohmann::json& manager_reset =
968 res.jsonValue["Actions"]["#Manager.Reset"];
Jennifer Leeed5befb2018-08-10 11:29:45 -0700969 manager_reset["target"] =
970 "/redfish/v1/Managers/bmc/Actions/Manager.Reset";
971 manager_reset["ResetType@Redfish.AllowableValues"] = {
972 "GracefulRestart"};
Jennifer Leeca537922018-08-10 10:07:30 -0700973
Andrew Geisslercb92c032018-08-17 07:56:14 -0700974 res.jsonValue["DateTime"] = crow::utility::dateTimeNow();
Gunnar Mills603a6642019-01-21 17:03:51 -0600975 res.jsonValue["Links"]["ManagerForServers@odata.count"] = 1;
976 res.jsonValue["Links"]["ManagerForServers"] = {
977 {{"@odata.id", "/redfish/v1/Systems/system"}}};
978#ifdef BMCWEB_ENABLE_REDFISH_ONE_CHASSIS
979 res.jsonValue["Links"]["ManagerForChassis@odata.count"] = 1;
980 res.jsonValue["Links"]["ManagerForChassis"] = {
981 {{"@odata.id", "/redfish/v1/Chassis/chassis"}}};
982#endif
Jennifer Leeed5befb2018-08-10 11:29:45 -0700983 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
James Feist5b4aa862018-08-16 14:07:01 -0700984
Jennifer Leeca537922018-08-10 10:07:30 -0700985 crow::connections::systemBus->async_method_call(
986 [asyncResp](const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -0700987 const dbus::utility::ManagedObjectType& resp) {
Jennifer Leeca537922018-08-10 10:07:30 -0700988 if (ec)
989 {
990 BMCWEB_LOG_ERROR << "Error while getting Software Version";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700991 messages::internalError(asyncResp->res);
Jennifer Leeca537922018-08-10 10:07:30 -0700992 return;
993 }
994
James Feist5b4aa862018-08-16 14:07:01 -0700995 for (auto& objpath : resp)
Jennifer Leeca537922018-08-10 10:07:30 -0700996 {
James Feist5b4aa862018-08-16 14:07:01 -0700997 for (auto& interface : objpath.second)
Jennifer Leeca537922018-08-10 10:07:30 -0700998 {
James Feist5f2caae2018-12-12 14:08:25 -0800999 // If interface is
1000 // xyz.openbmc_project.Software.Version, this is
1001 // what we're looking for.
Jennifer Leeca537922018-08-10 10:07:30 -07001002 if (interface.first ==
1003 "xyz.openbmc_project.Software.Version")
1004 {
1005 // Cut out everyting until last "/", ...
James Feist5b4aa862018-08-16 14:07:01 -07001006 for (auto& property : interface.second)
Jennifer Leeca537922018-08-10 10:07:30 -07001007 {
1008 if (property.first == "Version")
1009 {
James Feist5b4aa862018-08-16 14:07:01 -07001010 const std::string* value =
Ed Tanousabf2add2019-01-22 16:40:12 -08001011 std::get_if<std::string>(
1012 &property.second);
Jennifer Leeca537922018-08-10 10:07:30 -07001013 if (value == nullptr)
1014 {
1015 continue;
1016 }
1017 asyncResp->res
1018 .jsonValue["FirmwareVersion"] = *value;
1019 }
1020 }
1021 }
1022 }
1023 }
1024 },
1025 "xyz.openbmc_project.Software.BMC.Updater",
1026 "/xyz/openbmc_project/software",
1027 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
James Feist5b4aa862018-08-16 14:07:01 -07001028 getPidValues(asyncResp);
1029 }
James Feist5f2caae2018-12-12 14:08:25 -08001030 void setPidValues(std::shared_ptr<AsyncResp> response, nlohmann::json& data)
James Feist83ff9ab2018-08-31 10:18:24 -07001031 {
James Feist5f2caae2018-12-12 14:08:25 -08001032
James Feist83ff9ab2018-08-31 10:18:24 -07001033 // todo(james): might make sense to do a mapper call here if this
1034 // interface gets more traction
1035 crow::connections::systemBus->async_method_call(
1036 [response,
1037 data](const boost::system::error_code ec,
1038 const dbus::utility::ManagedObjectType& managedObj) {
1039 if (ec)
1040 {
1041 BMCWEB_LOG_ERROR << "Error communicating to Entity Manager";
Jason M. Bills35a62c72018-10-09 12:45:45 -07001042 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -07001043 return;
1044 }
James Feist5f2caae2018-12-12 14:08:25 -08001045
1046 // todo(james) mutable doesn't work with asio bindings
1047 nlohmann::json jsonData = data;
1048
1049 std::optional<nlohmann::json> pidControllers;
1050 std::optional<nlohmann::json> fanControllers;
1051 std::optional<nlohmann::json> fanZones;
1052 std::optional<nlohmann::json> stepwiseControllers;
1053 if (!redfish::json_util::readJson(
1054 jsonData, response->res, "PidControllers",
1055 pidControllers, "FanControllers", fanControllers,
1056 "FanZones", fanZones, "StepwiseControllers",
1057 stepwiseControllers))
James Feist83ff9ab2018-08-31 10:18:24 -07001058 {
James Feist5f2caae2018-12-12 14:08:25 -08001059 BMCWEB_LOG_ERROR << "Line:" << __LINE__
1060 << ", Illegal Property "
1061 << jsonData.dump();
1062 return;
1063 }
1064 std::array<
Ed Tanous43b761d2019-02-13 20:10:56 -08001065 std::pair<std::string, std::optional<nlohmann::json>*>, 4>
James Feist5f2caae2018-12-12 14:08:25 -08001066 sections = {
1067 std::make_pair("PidControllers", &pidControllers),
1068 std::make_pair("FanControllers", &fanControllers),
1069 std::make_pair("FanZones", &fanZones),
1070 std::make_pair("StepwiseControllers",
1071 &stepwiseControllers)};
1072
1073 for (auto& containerPair : sections)
1074 {
1075 auto& container = *(containerPair.second);
1076 if (!container)
James Feist83ff9ab2018-08-31 10:18:24 -07001077 {
James Feist5f2caae2018-12-12 14:08:25 -08001078 continue;
James Feist83ff9ab2018-08-31 10:18:24 -07001079 }
Ed Tanous43b761d2019-02-13 20:10:56 -08001080 std::string& type = containerPair.first;
James Feist5f2caae2018-12-12 14:08:25 -08001081
James Feistb6baeaa2019-02-21 10:41:40 -08001082 for (nlohmann::json::iterator it = container->begin();
1083 it != container->end(); it++)
James Feist83ff9ab2018-08-31 10:18:24 -07001084 {
James Feistb6baeaa2019-02-21 10:41:40 -08001085 const auto& name = it.key();
James Feist83ff9ab2018-08-31 10:18:24 -07001086 auto pathItr =
1087 std::find_if(managedObj.begin(), managedObj.end(),
1088 [&name](const auto& obj) {
1089 return boost::algorithm::ends_with(
James Feistb6baeaa2019-02-21 10:41:40 -08001090 obj.first.str, "/" + name);
James Feist83ff9ab2018-08-31 10:18:24 -07001091 });
1092 boost::container::flat_map<
1093 std::string, dbus::utility::DbusVariantType>
1094 output;
1095
1096 output.reserve(16); // The pid interface length
1097
1098 // determines if we're patching entity-manager or
1099 // creating a new object
1100 bool createNewObject = (pathItr == managedObj.end());
James Feist5f2caae2018-12-12 14:08:25 -08001101 std::string iface;
1102 if (type == "PidControllers" ||
1103 type == "FanControllers")
James Feist83ff9ab2018-08-31 10:18:24 -07001104 {
James Feist5f2caae2018-12-12 14:08:25 -08001105 iface = pidConfigurationIface;
James Feist83ff9ab2018-08-31 10:18:24 -07001106 if (!createNewObject &&
1107 pathItr->second.find(pidConfigurationIface) ==
1108 pathItr->second.end())
1109 {
1110 createNewObject = true;
1111 }
1112 }
James Feist5f2caae2018-12-12 14:08:25 -08001113 else if (type == "FanZones")
James Feist83ff9ab2018-08-31 10:18:24 -07001114 {
James Feist5f2caae2018-12-12 14:08:25 -08001115 iface = pidZoneConfigurationIface;
1116 if (!createNewObject &&
1117 pathItr->second.find(
1118 pidZoneConfigurationIface) ==
1119 pathItr->second.end())
1120 {
1121
1122 createNewObject = true;
1123 }
1124 }
1125 else if (type == "StepwiseControllers")
1126 {
1127 iface = stepwiseConfigurationIface;
1128 if (!createNewObject &&
1129 pathItr->second.find(
1130 stepwiseConfigurationIface) ==
1131 pathItr->second.end())
1132 {
1133 createNewObject = true;
1134 }
James Feist83ff9ab2018-08-31 10:18:24 -07001135 }
James Feistb6baeaa2019-02-21 10:41:40 -08001136 BMCWEB_LOG_DEBUG << "Create new = " << createNewObject
1137 << "\n";
James Feist83ff9ab2018-08-31 10:18:24 -07001138 output["Name"] =
1139 boost::replace_all_copy(name, "_", " ");
1140
1141 std::string chassis;
1142 CreatePIDRet ret = createPidInterface(
James Feistb6baeaa2019-02-21 10:41:40 -08001143 response, type, it, pathItr->first.str, managedObj,
1144 createNewObject, output, chassis);
James Feist83ff9ab2018-08-31 10:18:24 -07001145 if (ret == CreatePIDRet::fail)
1146 {
1147 return;
1148 }
1149 else if (ret == CreatePIDRet::del)
1150 {
1151 continue;
1152 }
1153
1154 if (!createNewObject)
1155 {
1156 for (const auto& property : output)
1157 {
James Feist83ff9ab2018-08-31 10:18:24 -07001158 crow::connections::systemBus->async_method_call(
1159 [response,
1160 propertyName{std::string(property.first)}](
1161 const boost::system::error_code ec) {
1162 if (ec)
1163 {
1164 BMCWEB_LOG_ERROR
1165 << "Error patching "
1166 << propertyName << ": " << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -07001167 messages::internalError(
1168 response->res);
James Feistb6baeaa2019-02-21 10:41:40 -08001169 return;
James Feist83ff9ab2018-08-31 10:18:24 -07001170 }
James Feistb6baeaa2019-02-21 10:41:40 -08001171 messages::success(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -07001172 },
1173 "xyz.openbmc_project.EntityManager",
1174 pathItr->first.str,
1175 "org.freedesktop.DBus.Properties", "Set",
James Feist5f2caae2018-12-12 14:08:25 -08001176 iface, property.first, property.second);
James Feist83ff9ab2018-08-31 10:18:24 -07001177 }
1178 }
1179 else
1180 {
1181 if (chassis.empty())
1182 {
1183 BMCWEB_LOG_ERROR
1184 << "Failed to get chassis from config";
Jason M. Bills35a62c72018-10-09 12:45:45 -07001185 messages::invalidObject(response->res, name);
James Feist83ff9ab2018-08-31 10:18:24 -07001186 return;
1187 }
1188
1189 bool foundChassis = false;
1190 for (const auto& obj : managedObj)
1191 {
1192 if (boost::algorithm::ends_with(obj.first.str,
1193 chassis))
1194 {
1195 chassis = obj.first.str;
1196 foundChassis = true;
1197 break;
1198 }
1199 }
1200 if (!foundChassis)
1201 {
1202 BMCWEB_LOG_ERROR
1203 << "Failed to find chassis on dbus";
Jason M. Bills35a62c72018-10-09 12:45:45 -07001204 messages::resourceMissingAtURI(
1205 response->res,
1206 "/redfish/v1/Chassis/" + chassis);
James Feist83ff9ab2018-08-31 10:18:24 -07001207 return;
1208 }
1209
1210 crow::connections::systemBus->async_method_call(
1211 [response](const boost::system::error_code ec) {
1212 if (ec)
1213 {
1214 BMCWEB_LOG_ERROR
1215 << "Error Adding Pid Object " << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -07001216 messages::internalError(response->res);
James Feistb6baeaa2019-02-21 10:41:40 -08001217 return;
James Feist83ff9ab2018-08-31 10:18:24 -07001218 }
James Feistb6baeaa2019-02-21 10:41:40 -08001219 messages::success(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -07001220 },
1221 "xyz.openbmc_project.EntityManager", chassis,
1222 "xyz.openbmc_project.AddObject", "AddObject",
1223 output);
1224 }
1225 }
1226 }
1227 },
1228 "xyz.openbmc_project.EntityManager", "/", objectManagerIface,
1229 "GetManagedObjects");
1230 }
James Feist5b4aa862018-08-16 14:07:01 -07001231
1232 void doPatch(crow::Response& res, const crow::Request& req,
1233 const std::vector<std::string>& params) override
1234 {
Ed Tanous0627a2c2018-11-29 17:09:23 -08001235 std::optional<nlohmann::json> oem;
1236
1237 if (!json_util::readJson(req, res, "Oem", oem))
James Feist83ff9ab2018-08-31 10:18:24 -07001238 {
1239 return;
1240 }
Ed Tanous0627a2c2018-11-29 17:09:23 -08001241
James Feist83ff9ab2018-08-31 10:18:24 -07001242 std::shared_ptr<AsyncResp> response = std::make_shared<AsyncResp>(res);
Ed Tanous0627a2c2018-11-29 17:09:23 -08001243
1244 if (oem)
James Feist83ff9ab2018-08-31 10:18:24 -07001245 {
Ed Tanous43b761d2019-02-13 20:10:56 -08001246 std::optional<nlohmann::json> openbmc;
1247 if (!redfish::json_util::readJson(*oem, res, "OpenBmc", openbmc))
James Feist83ff9ab2018-08-31 10:18:24 -07001248 {
Ed Tanous43b761d2019-02-13 20:10:56 -08001249 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
1250 << oem->dump();
1251 return;
1252 }
1253 if (openbmc)
1254 {
1255 std::optional<nlohmann::json> fan;
1256 if (!redfish::json_util::readJson(*openbmc, res, "Fan", fan))
James Feist83ff9ab2018-08-31 10:18:24 -07001257 {
James Feist5f2caae2018-12-12 14:08:25 -08001258 BMCWEB_LOG_ERROR << "Line:" << __LINE__
Ed Tanous43b761d2019-02-13 20:10:56 -08001259 << ", Illegal Property "
1260 << openbmc->dump();
James Feist5f2caae2018-12-12 14:08:25 -08001261 return;
1262 }
Ed Tanous43b761d2019-02-13 20:10:56 -08001263 if (fan)
James Feist5f2caae2018-12-12 14:08:25 -08001264 {
Ed Tanous43b761d2019-02-13 20:10:56 -08001265 setPidValues(response, *fan);
James Feist83ff9ab2018-08-31 10:18:24 -07001266 }
James Feist83ff9ab2018-08-31 10:18:24 -07001267 }
1268 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001269 }
1270
Ed Tanous0f74e642018-11-12 15:17:05 -08001271 std::string uuid;
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001272};
1273
Ed Tanous1abe55e2018-09-05 08:30:59 -07001274class ManagerCollection : public Node
1275{
1276 public:
James Feist5b4aa862018-08-16 14:07:01 -07001277 ManagerCollection(CrowApp& app) : Node(app, "/redfish/v1/Managers/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001278 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001279 entityPrivileges = {
1280 {boost::beast::http::verb::get, {{"Login"}}},
1281 {boost::beast::http::verb::head, {{"Login"}}},
1282 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1283 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1284 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1285 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1286 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001287
Ed Tanous1abe55e2018-09-05 08:30:59 -07001288 private:
James Feist5b4aa862018-08-16 14:07:01 -07001289 void doGet(crow::Response& res, const crow::Request& req,
1290 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -07001291 {
James Feist83ff9ab2018-08-31 10:18:24 -07001292 // Collections don't include the static data added by SubRoute
1293 // because it has a duplicate entry for members
Ed Tanous1abe55e2018-09-05 08:30:59 -07001294 res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
1295 res.jsonValue["@odata.type"] = "#ManagerCollection.ManagerCollection";
1296 res.jsonValue["@odata.context"] =
1297 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection";
1298 res.jsonValue["Name"] = "Manager Collection";
1299 res.jsonValue["Members@odata.count"] = 1;
1300 res.jsonValue["Members"] = {
James Feist5b4aa862018-08-16 14:07:01 -07001301 {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001302 res.end();
1303 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001304};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001305} // namespace redfish