blob: c73a218af912745870c21c5f2a471ae13b70faf8 [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();
Santosh Puranik474bfad2019-04-02 16:00:09 +0530975
976 // Fill in GraphicalConsole and SerialConsole info
977 res.jsonValue["SerialConsole"]["ServiceEnabled"] = true;
978 res.jsonValue["SerialConsole"]["ConnectTypesSupported"] = {"IPMI",
979 "SSH"};
980 // TODO (Santosh) : Uncomment when KVM support is in.
981 // res.jsonValue["GraphicalConsole"]["ServiceEnabled"] = true;
982 // res.jsonValue["GraphicalConsole"]["ConnectTypesSupported"] =
983 // {"KVMIP"};
984
Gunnar Mills603a6642019-01-21 17:03:51 -0600985 res.jsonValue["Links"]["ManagerForServers@odata.count"] = 1;
986 res.jsonValue["Links"]["ManagerForServers"] = {
987 {{"@odata.id", "/redfish/v1/Systems/system"}}};
988#ifdef BMCWEB_ENABLE_REDFISH_ONE_CHASSIS
989 res.jsonValue["Links"]["ManagerForChassis@odata.count"] = 1;
990 res.jsonValue["Links"]["ManagerForChassis"] = {
991 {{"@odata.id", "/redfish/v1/Chassis/chassis"}}};
992#endif
Jennifer Leeed5befb2018-08-10 11:29:45 -0700993 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
James Feist5b4aa862018-08-16 14:07:01 -0700994
Jennifer Leeca537922018-08-10 10:07:30 -0700995 crow::connections::systemBus->async_method_call(
996 [asyncResp](const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -0700997 const dbus::utility::ManagedObjectType& resp) {
Jennifer Leeca537922018-08-10 10:07:30 -0700998 if (ec)
999 {
1000 BMCWEB_LOG_ERROR << "Error while getting Software Version";
Jason M. Billsf12894f2018-10-09 12:45:45 -07001001 messages::internalError(asyncResp->res);
Jennifer Leeca537922018-08-10 10:07:30 -07001002 return;
1003 }
1004
James Feist5b4aa862018-08-16 14:07:01 -07001005 for (auto& objpath : resp)
Jennifer Leeca537922018-08-10 10:07:30 -07001006 {
James Feist5b4aa862018-08-16 14:07:01 -07001007 for (auto& interface : objpath.second)
Jennifer Leeca537922018-08-10 10:07:30 -07001008 {
James Feist5f2caae2018-12-12 14:08:25 -08001009 // If interface is
1010 // xyz.openbmc_project.Software.Version, this is
1011 // what we're looking for.
Jennifer Leeca537922018-08-10 10:07:30 -07001012 if (interface.first ==
1013 "xyz.openbmc_project.Software.Version")
1014 {
1015 // Cut out everyting until last "/", ...
James Feist5b4aa862018-08-16 14:07:01 -07001016 for (auto& property : interface.second)
Jennifer Leeca537922018-08-10 10:07:30 -07001017 {
1018 if (property.first == "Version")
1019 {
James Feist5b4aa862018-08-16 14:07:01 -07001020 const std::string* value =
Ed Tanousabf2add2019-01-22 16:40:12 -08001021 std::get_if<std::string>(
1022 &property.second);
Jennifer Leeca537922018-08-10 10:07:30 -07001023 if (value == nullptr)
1024 {
1025 continue;
1026 }
1027 asyncResp->res
1028 .jsonValue["FirmwareVersion"] = *value;
1029 }
1030 }
1031 }
1032 }
1033 }
1034 },
1035 "xyz.openbmc_project.Software.BMC.Updater",
1036 "/xyz/openbmc_project/software",
1037 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
James Feist5b4aa862018-08-16 14:07:01 -07001038 getPidValues(asyncResp);
1039 }
James Feist5f2caae2018-12-12 14:08:25 -08001040 void setPidValues(std::shared_ptr<AsyncResp> response, nlohmann::json& data)
James Feist83ff9ab2018-08-31 10:18:24 -07001041 {
James Feist5f2caae2018-12-12 14:08:25 -08001042
James Feist83ff9ab2018-08-31 10:18:24 -07001043 // todo(james): might make sense to do a mapper call here if this
1044 // interface gets more traction
1045 crow::connections::systemBus->async_method_call(
1046 [response,
1047 data](const boost::system::error_code ec,
1048 const dbus::utility::ManagedObjectType& managedObj) {
1049 if (ec)
1050 {
1051 BMCWEB_LOG_ERROR << "Error communicating to Entity Manager";
Jason M. Bills35a62c72018-10-09 12:45:45 -07001052 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -07001053 return;
1054 }
James Feist5f2caae2018-12-12 14:08:25 -08001055
1056 // todo(james) mutable doesn't work with asio bindings
1057 nlohmann::json jsonData = data;
1058
1059 std::optional<nlohmann::json> pidControllers;
1060 std::optional<nlohmann::json> fanControllers;
1061 std::optional<nlohmann::json> fanZones;
1062 std::optional<nlohmann::json> stepwiseControllers;
1063 if (!redfish::json_util::readJson(
1064 jsonData, response->res, "PidControllers",
1065 pidControllers, "FanControllers", fanControllers,
1066 "FanZones", fanZones, "StepwiseControllers",
1067 stepwiseControllers))
James Feist83ff9ab2018-08-31 10:18:24 -07001068 {
James Feist5f2caae2018-12-12 14:08:25 -08001069 BMCWEB_LOG_ERROR << "Line:" << __LINE__
1070 << ", Illegal Property "
1071 << jsonData.dump();
1072 return;
1073 }
1074 std::array<
Ed Tanous43b761d2019-02-13 20:10:56 -08001075 std::pair<std::string, std::optional<nlohmann::json>*>, 4>
James Feist5f2caae2018-12-12 14:08:25 -08001076 sections = {
1077 std::make_pair("PidControllers", &pidControllers),
1078 std::make_pair("FanControllers", &fanControllers),
1079 std::make_pair("FanZones", &fanZones),
1080 std::make_pair("StepwiseControllers",
1081 &stepwiseControllers)};
1082
1083 for (auto& containerPair : sections)
1084 {
1085 auto& container = *(containerPair.second);
1086 if (!container)
James Feist83ff9ab2018-08-31 10:18:24 -07001087 {
James Feist5f2caae2018-12-12 14:08:25 -08001088 continue;
James Feist83ff9ab2018-08-31 10:18:24 -07001089 }
Ed Tanous43b761d2019-02-13 20:10:56 -08001090 std::string& type = containerPair.first;
James Feist5f2caae2018-12-12 14:08:25 -08001091
James Feistb6baeaa2019-02-21 10:41:40 -08001092 for (nlohmann::json::iterator it = container->begin();
1093 it != container->end(); it++)
James Feist83ff9ab2018-08-31 10:18:24 -07001094 {
James Feistb6baeaa2019-02-21 10:41:40 -08001095 const auto& name = it.key();
James Feist83ff9ab2018-08-31 10:18:24 -07001096 auto pathItr =
1097 std::find_if(managedObj.begin(), managedObj.end(),
1098 [&name](const auto& obj) {
1099 return boost::algorithm::ends_with(
James Feistb6baeaa2019-02-21 10:41:40 -08001100 obj.first.str, "/" + name);
James Feist83ff9ab2018-08-31 10:18:24 -07001101 });
1102 boost::container::flat_map<
1103 std::string, dbus::utility::DbusVariantType>
1104 output;
1105
1106 output.reserve(16); // The pid interface length
1107
1108 // determines if we're patching entity-manager or
1109 // creating a new object
1110 bool createNewObject = (pathItr == managedObj.end());
James Feist5f2caae2018-12-12 14:08:25 -08001111 std::string iface;
1112 if (type == "PidControllers" ||
1113 type == "FanControllers")
James Feist83ff9ab2018-08-31 10:18:24 -07001114 {
James Feist5f2caae2018-12-12 14:08:25 -08001115 iface = pidConfigurationIface;
James Feist83ff9ab2018-08-31 10:18:24 -07001116 if (!createNewObject &&
1117 pathItr->second.find(pidConfigurationIface) ==
1118 pathItr->second.end())
1119 {
1120 createNewObject = true;
1121 }
1122 }
James Feist5f2caae2018-12-12 14:08:25 -08001123 else if (type == "FanZones")
James Feist83ff9ab2018-08-31 10:18:24 -07001124 {
James Feist5f2caae2018-12-12 14:08:25 -08001125 iface = pidZoneConfigurationIface;
1126 if (!createNewObject &&
1127 pathItr->second.find(
1128 pidZoneConfigurationIface) ==
1129 pathItr->second.end())
1130 {
1131
1132 createNewObject = true;
1133 }
1134 }
1135 else if (type == "StepwiseControllers")
1136 {
1137 iface = stepwiseConfigurationIface;
1138 if (!createNewObject &&
1139 pathItr->second.find(
1140 stepwiseConfigurationIface) ==
1141 pathItr->second.end())
1142 {
1143 createNewObject = true;
1144 }
James Feist83ff9ab2018-08-31 10:18:24 -07001145 }
James Feistb6baeaa2019-02-21 10:41:40 -08001146 BMCWEB_LOG_DEBUG << "Create new = " << createNewObject
1147 << "\n";
James Feist83ff9ab2018-08-31 10:18:24 -07001148 output["Name"] =
1149 boost::replace_all_copy(name, "_", " ");
1150
1151 std::string chassis;
1152 CreatePIDRet ret = createPidInterface(
James Feistb6baeaa2019-02-21 10:41:40 -08001153 response, type, it, pathItr->first.str, managedObj,
1154 createNewObject, output, chassis);
James Feist83ff9ab2018-08-31 10:18:24 -07001155 if (ret == CreatePIDRet::fail)
1156 {
1157 return;
1158 }
1159 else if (ret == CreatePIDRet::del)
1160 {
1161 continue;
1162 }
1163
1164 if (!createNewObject)
1165 {
1166 for (const auto& property : output)
1167 {
James Feist83ff9ab2018-08-31 10:18:24 -07001168 crow::connections::systemBus->async_method_call(
1169 [response,
1170 propertyName{std::string(property.first)}](
1171 const boost::system::error_code ec) {
1172 if (ec)
1173 {
1174 BMCWEB_LOG_ERROR
1175 << "Error patching "
1176 << propertyName << ": " << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -07001177 messages::internalError(
1178 response->res);
James Feistb6baeaa2019-02-21 10:41:40 -08001179 return;
James Feist83ff9ab2018-08-31 10:18:24 -07001180 }
James Feistb6baeaa2019-02-21 10:41:40 -08001181 messages::success(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -07001182 },
1183 "xyz.openbmc_project.EntityManager",
1184 pathItr->first.str,
1185 "org.freedesktop.DBus.Properties", "Set",
James Feist5f2caae2018-12-12 14:08:25 -08001186 iface, property.first, property.second);
James Feist83ff9ab2018-08-31 10:18:24 -07001187 }
1188 }
1189 else
1190 {
1191 if (chassis.empty())
1192 {
1193 BMCWEB_LOG_ERROR
1194 << "Failed to get chassis from config";
Jason M. Bills35a62c72018-10-09 12:45:45 -07001195 messages::invalidObject(response->res, name);
James Feist83ff9ab2018-08-31 10:18:24 -07001196 return;
1197 }
1198
1199 bool foundChassis = false;
1200 for (const auto& obj : managedObj)
1201 {
1202 if (boost::algorithm::ends_with(obj.first.str,
1203 chassis))
1204 {
1205 chassis = obj.first.str;
1206 foundChassis = true;
1207 break;
1208 }
1209 }
1210 if (!foundChassis)
1211 {
1212 BMCWEB_LOG_ERROR
1213 << "Failed to find chassis on dbus";
Jason M. Bills35a62c72018-10-09 12:45:45 -07001214 messages::resourceMissingAtURI(
1215 response->res,
1216 "/redfish/v1/Chassis/" + chassis);
James Feist83ff9ab2018-08-31 10:18:24 -07001217 return;
1218 }
1219
1220 crow::connections::systemBus->async_method_call(
1221 [response](const boost::system::error_code ec) {
1222 if (ec)
1223 {
1224 BMCWEB_LOG_ERROR
1225 << "Error Adding Pid Object " << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -07001226 messages::internalError(response->res);
James Feistb6baeaa2019-02-21 10:41:40 -08001227 return;
James Feist83ff9ab2018-08-31 10:18:24 -07001228 }
James Feistb6baeaa2019-02-21 10:41:40 -08001229 messages::success(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -07001230 },
1231 "xyz.openbmc_project.EntityManager", chassis,
1232 "xyz.openbmc_project.AddObject", "AddObject",
1233 output);
1234 }
1235 }
1236 }
1237 },
1238 "xyz.openbmc_project.EntityManager", "/", objectManagerIface,
1239 "GetManagedObjects");
1240 }
James Feist5b4aa862018-08-16 14:07:01 -07001241
1242 void doPatch(crow::Response& res, const crow::Request& req,
1243 const std::vector<std::string>& params) override
1244 {
Ed Tanous0627a2c2018-11-29 17:09:23 -08001245 std::optional<nlohmann::json> oem;
1246
1247 if (!json_util::readJson(req, res, "Oem", oem))
James Feist83ff9ab2018-08-31 10:18:24 -07001248 {
1249 return;
1250 }
Ed Tanous0627a2c2018-11-29 17:09:23 -08001251
James Feist83ff9ab2018-08-31 10:18:24 -07001252 std::shared_ptr<AsyncResp> response = std::make_shared<AsyncResp>(res);
Ed Tanous0627a2c2018-11-29 17:09:23 -08001253
1254 if (oem)
James Feist83ff9ab2018-08-31 10:18:24 -07001255 {
Ed Tanous43b761d2019-02-13 20:10:56 -08001256 std::optional<nlohmann::json> openbmc;
1257 if (!redfish::json_util::readJson(*oem, res, "OpenBmc", openbmc))
James Feist83ff9ab2018-08-31 10:18:24 -07001258 {
Ed Tanous43b761d2019-02-13 20:10:56 -08001259 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
1260 << oem->dump();
1261 return;
1262 }
1263 if (openbmc)
1264 {
1265 std::optional<nlohmann::json> fan;
1266 if (!redfish::json_util::readJson(*openbmc, res, "Fan", fan))
James Feist83ff9ab2018-08-31 10:18:24 -07001267 {
James Feist5f2caae2018-12-12 14:08:25 -08001268 BMCWEB_LOG_ERROR << "Line:" << __LINE__
Ed Tanous43b761d2019-02-13 20:10:56 -08001269 << ", Illegal Property "
1270 << openbmc->dump();
James Feist5f2caae2018-12-12 14:08:25 -08001271 return;
1272 }
Ed Tanous43b761d2019-02-13 20:10:56 -08001273 if (fan)
James Feist5f2caae2018-12-12 14:08:25 -08001274 {
Ed Tanous43b761d2019-02-13 20:10:56 -08001275 setPidValues(response, *fan);
James Feist83ff9ab2018-08-31 10:18:24 -07001276 }
James Feist83ff9ab2018-08-31 10:18:24 -07001277 }
1278 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001279 }
1280
Ed Tanous0f74e642018-11-12 15:17:05 -08001281 std::string uuid;
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001282};
1283
Ed Tanous1abe55e2018-09-05 08:30:59 -07001284class ManagerCollection : public Node
1285{
1286 public:
James Feist5b4aa862018-08-16 14:07:01 -07001287 ManagerCollection(CrowApp& app) : Node(app, "/redfish/v1/Managers/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001288 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001289 entityPrivileges = {
1290 {boost::beast::http::verb::get, {{"Login"}}},
1291 {boost::beast::http::verb::head, {{"Login"}}},
1292 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1293 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1294 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1295 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1296 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001297
Ed Tanous1abe55e2018-09-05 08:30:59 -07001298 private:
James Feist5b4aa862018-08-16 14:07:01 -07001299 void doGet(crow::Response& res, const crow::Request& req,
1300 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -07001301 {
James Feist83ff9ab2018-08-31 10:18:24 -07001302 // Collections don't include the static data added by SubRoute
1303 // because it has a duplicate entry for members
Ed Tanous1abe55e2018-09-05 08:30:59 -07001304 res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
1305 res.jsonValue["@odata.type"] = "#ManagerCollection.ManagerCollection";
1306 res.jsonValue["@odata.context"] =
1307 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection";
1308 res.jsonValue["Name"] = "Manager Collection";
1309 res.jsonValue["Members@odata.count"] = 1;
1310 res.jsonValue["Members"] = {
James Feist5b4aa862018-08-16 14:07:01 -07001311 {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001312 res.end();
1313 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001314};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001315} // namespace redfish