blob: 936d655462c8c1dfff9ba9e041bc13984483a0c3 [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;
504 if (!dbus::utility::getNthStringFromPath(path, 4, input))
505 {
506 BMCWEB_LOG_ERROR << "Got invalid path " << path;
507 BMCWEB_LOG_ERROR << "Illegal Type Zones";
508 messages::propertyValueFormatError(response->res, odata.dump(),
509 "Zones");
510 return false;
511 }
512 boost::replace_all(input, "_", " ");
513 zones.emplace_back(std::move(input));
514 }
515 return true;
516}
517
James Feistb6baeaa2019-02-21 10:41:40 -0800518static bool findChassis(const dbus::utility::ManagedObjectType& managedObj,
519 const std::string& value, std::string& chassis)
520{
521 BMCWEB_LOG_DEBUG << "Find Chassis: " << value << "\n";
522
523 std::string escaped = boost::replace_all_copy(value, " ", "_");
524 escaped = "/" + escaped;
525 auto it = std::find_if(
526 managedObj.begin(), managedObj.end(), [&escaped](const auto& obj) {
527 if (boost::algorithm::ends_with(obj.first.str, escaped))
528 {
529 BMCWEB_LOG_DEBUG << "Matched " << obj.first.str << "\n";
530 return true;
531 }
532 return false;
533 });
534
535 if (it == managedObj.end())
536 {
537 return false;
538 }
539 // 5 comes from <chassis-name> being the 5th element
540 // /xyz/openbmc_project/inventory/system/chassis/<chassis-name>
541 return dbus::utility::getNthStringFromPath(it->first.str, 5, chassis);
542}
543
James Feist83ff9ab2018-08-31 10:18:24 -0700544static CreatePIDRet createPidInterface(
545 const std::shared_ptr<AsyncResp>& response, const std::string& type,
James Feistb6baeaa2019-02-21 10:41:40 -0800546 nlohmann::json::iterator it, const std::string& path,
James Feist83ff9ab2018-08-31 10:18:24 -0700547 const dbus::utility::ManagedObjectType& managedObj, bool createNewObject,
548 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>&
549 output,
550 std::string& chassis)
551{
552
James Feist5f2caae2018-12-12 14:08:25 -0800553 // common deleter
James Feistb6baeaa2019-02-21 10:41:40 -0800554 if (it.value() == nullptr)
James Feist5f2caae2018-12-12 14:08:25 -0800555 {
556 std::string iface;
557 if (type == "PidControllers" || type == "FanControllers")
558 {
559 iface = pidConfigurationIface;
560 }
561 else if (type == "FanZones")
562 {
563 iface = pidZoneConfigurationIface;
564 }
565 else if (type == "StepwiseControllers")
566 {
567 iface = stepwiseConfigurationIface;
568 }
569 else
570 {
571 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Type "
572 << type;
573 messages::propertyUnknown(response->res, type);
574 return CreatePIDRet::fail;
575 }
576 // delete interface
577 crow::connections::systemBus->async_method_call(
578 [response, path](const boost::system::error_code ec) {
579 if (ec)
580 {
581 BMCWEB_LOG_ERROR << "Error patching " << path << ": " << ec;
582 messages::internalError(response->res);
James Feistb6baeaa2019-02-21 10:41:40 -0800583 return;
James Feist5f2caae2018-12-12 14:08:25 -0800584 }
James Feistb6baeaa2019-02-21 10:41:40 -0800585 messages::success(response->res);
James Feist5f2caae2018-12-12 14:08:25 -0800586 },
587 "xyz.openbmc_project.EntityManager", path, iface, "Delete");
588 return CreatePIDRet::del;
589 }
590
James Feistb6baeaa2019-02-21 10:41:40 -0800591 if (!createNewObject)
592 {
593 // if we aren't creating a new object, we should be able to find it on
594 // d-bus
595 if (!findChassis(managedObj, it.key(), chassis))
596 {
597 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
598 messages::invalidObject(response->res, it.key());
599 return CreatePIDRet::fail;
600 }
601 }
602
James Feist83ff9ab2018-08-31 10:18:24 -0700603 if (type == "PidControllers" || type == "FanControllers")
604 {
605 if (createNewObject)
606 {
607 output["Class"] = type == "PidControllers" ? std::string("temp")
608 : std::string("fan");
609 output["Type"] = std::string("Pid");
610 }
James Feist5f2caae2018-12-12 14:08:25 -0800611
612 std::optional<std::vector<nlohmann::json>> zones;
613 std::optional<std::vector<std::string>> inputs;
614 std::optional<std::vector<std::string>> outputs;
615 std::map<std::string, std::optional<double>> doubles;
616 if (!redfish::json_util::readJson(
James Feistb6baeaa2019-02-21 10:41:40 -0800617 it.value(), response->res, "Inputs", inputs, "Outputs", outputs,
James Feist5f2caae2018-12-12 14:08:25 -0800618 "Zones", zones, "FFGainCoefficient",
619 doubles["FFGainCoefficient"], "FFOffCoefficient",
620 doubles["FFOffCoefficient"], "ICoefficient",
621 doubles["ICoefficient"], "ILimitMax", doubles["ILimitMax"],
622 "ILimitMin", doubles["ILimitMin"], "OutLimitMax",
623 doubles["OutLimitMax"], "OutLimitMin", doubles["OutLimitMin"],
624 "PCoefficient", doubles["PCoefficient"], "SetPoint",
625 doubles["SetPoint"], "SlewNeg", doubles["SlewNeg"], "SlewPos",
James Feistaad1a252019-02-19 10:13:52 -0800626 doubles["SlewPos"], "PositiveHysteresis",
627 doubles["PositiveHysteresis"], "NegativeHysteresis",
628 doubles["NegativeHysteresis"]))
James Feist83ff9ab2018-08-31 10:18:24 -0700629 {
James Feist5f2caae2018-12-12 14:08:25 -0800630 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
James Feistb6baeaa2019-02-21 10:41:40 -0800631 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800632 return CreatePIDRet::fail;
James Feist83ff9ab2018-08-31 10:18:24 -0700633 }
James Feist5f2caae2018-12-12 14:08:25 -0800634 if (zones)
James Feist83ff9ab2018-08-31 10:18:24 -0700635 {
James Feist5f2caae2018-12-12 14:08:25 -0800636 std::vector<std::string> zonesStr;
637 if (!getZonesFromJsonReq(response, *zones, zonesStr))
James Feist83ff9ab2018-08-31 10:18:24 -0700638 {
James Feist5f2caae2018-12-12 14:08:25 -0800639 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Zones";
640 return CreatePIDRet::fail;
James Feist83ff9ab2018-08-31 10:18:24 -0700641 }
James Feistb6baeaa2019-02-21 10:41:40 -0800642 if (chassis.empty() &&
643 !findChassis(managedObj, zonesStr[0], chassis))
644 {
645 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
646 messages::invalidObject(response->res, it.key());
647 return CreatePIDRet::fail;
648 }
649
James Feist5f2caae2018-12-12 14:08:25 -0800650 output["Zones"] = std::move(zonesStr);
651 }
652 if (inputs || outputs)
653 {
654 std::array<std::optional<std::vector<std::string>>*, 2> containers =
655 {&inputs, &outputs};
656 size_t index = 0;
657 for (const auto& containerPtr : containers)
James Feist83ff9ab2018-08-31 10:18:24 -0700658 {
James Feist5f2caae2018-12-12 14:08:25 -0800659 std::optional<std::vector<std::string>>& container =
660 *containerPtr;
661 if (!container)
James Feist83ff9ab2018-08-31 10:18:24 -0700662 {
James Feist5f2caae2018-12-12 14:08:25 -0800663 index++;
664 continue;
James Feist83ff9ab2018-08-31 10:18:24 -0700665 }
James Feist5f2caae2018-12-12 14:08:25 -0800666
667 for (std::string& value : *container)
James Feist83ff9ab2018-08-31 10:18:24 -0700668 {
James Feist5f2caae2018-12-12 14:08:25 -0800669 boost::replace_all(value, "_", " ");
James Feist83ff9ab2018-08-31 10:18:24 -0700670 }
James Feist5f2caae2018-12-12 14:08:25 -0800671 std::string key;
672 if (index == 0)
James Feist83ff9ab2018-08-31 10:18:24 -0700673 {
James Feist5f2caae2018-12-12 14:08:25 -0800674 key = "Inputs";
James Feist83ff9ab2018-08-31 10:18:24 -0700675 }
James Feist5f2caae2018-12-12 14:08:25 -0800676 else
677 {
678 key = "Outputs";
679 }
680 output[key] = *container;
681 index++;
James Feist83ff9ab2018-08-31 10:18:24 -0700682 }
James Feist5f2caae2018-12-12 14:08:25 -0800683 }
James Feist83ff9ab2018-08-31 10:18:24 -0700684
James Feist5f2caae2018-12-12 14:08:25 -0800685 // doubles
686 for (const auto& pairs : doubles)
687 {
688 if (!pairs.second)
James Feist83ff9ab2018-08-31 10:18:24 -0700689 {
James Feist5f2caae2018-12-12 14:08:25 -0800690 continue;
James Feist83ff9ab2018-08-31 10:18:24 -0700691 }
James Feist5f2caae2018-12-12 14:08:25 -0800692 BMCWEB_LOG_DEBUG << pairs.first << " = " << *pairs.second;
693 output[pairs.first] = *(pairs.second);
James Feist83ff9ab2018-08-31 10:18:24 -0700694 }
695 }
James Feist5f2caae2018-12-12 14:08:25 -0800696
James Feist83ff9ab2018-08-31 10:18:24 -0700697 else if (type == "FanZones")
698 {
James Feist83ff9ab2018-08-31 10:18:24 -0700699 output["Type"] = std::string("Pid.Zone");
700
James Feist5f2caae2018-12-12 14:08:25 -0800701 std::optional<nlohmann::json> chassisContainer;
702 std::optional<double> failSafePercent;
James Feistd3ec07f2019-02-25 14:51:15 -0800703 std::optional<double> minThermalOutput;
James Feistb6baeaa2019-02-21 10:41:40 -0800704 if (!redfish::json_util::readJson(it.value(), response->res, "Chassis",
James Feist5f2caae2018-12-12 14:08:25 -0800705 chassisContainer, "FailSafePercent",
James Feistd3ec07f2019-02-25 14:51:15 -0800706 failSafePercent, "MinThermalOutput",
707 minThermalOutput))
James Feist83ff9ab2018-08-31 10:18:24 -0700708 {
James Feist5f2caae2018-12-12 14:08:25 -0800709 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
James Feistb6baeaa2019-02-21 10:41:40 -0800710 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800711 return CreatePIDRet::fail;
712 }
James Feist83ff9ab2018-08-31 10:18:24 -0700713
James Feist5f2caae2018-12-12 14:08:25 -0800714 if (chassisContainer)
715 {
716
717 std::string chassisId;
718 if (!redfish::json_util::readJson(*chassisContainer, response->res,
719 "@odata.id", chassisId))
James Feist83ff9ab2018-08-31 10:18:24 -0700720 {
James Feist5f2caae2018-12-12 14:08:25 -0800721 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
722 << chassisContainer->dump();
James Feist83ff9ab2018-08-31 10:18:24 -0700723 return CreatePIDRet::fail;
724 }
James Feist5f2caae2018-12-12 14:08:25 -0800725
726 // /refish/v1/chassis/chassis_name/
727 if (!dbus::utility::getNthStringFromPath(chassisId, 3, chassis))
728 {
729 BMCWEB_LOG_ERROR << "Got invalid path " << chassisId;
730 messages::invalidObject(response->res, chassisId);
731 return CreatePIDRet::fail;
732 }
733 }
James Feistd3ec07f2019-02-25 14:51:15 -0800734 if (minThermalOutput)
James Feist5f2caae2018-12-12 14:08:25 -0800735 {
James Feistd3ec07f2019-02-25 14:51:15 -0800736 output["MinThermalOutput"] = *minThermalOutput;
James Feist5f2caae2018-12-12 14:08:25 -0800737 }
738 if (failSafePercent)
739 {
740 output["FailSafePercent"] = *failSafePercent;
741 }
742 }
743 else if (type == "StepwiseControllers")
744 {
745 output["Type"] = std::string("Stepwise");
746
747 std::optional<std::vector<nlohmann::json>> zones;
748 std::optional<std::vector<nlohmann::json>> steps;
749 std::optional<std::vector<std::string>> inputs;
750 std::optional<double> positiveHysteresis;
751 std::optional<double> negativeHysteresis;
James Feistc33a90e2019-03-01 10:17:44 -0800752 std::optional<std::string> direction; // upper clipping curve vs lower
James Feist5f2caae2018-12-12 14:08:25 -0800753 if (!redfish::json_util::readJson(
James Feistb6baeaa2019-02-21 10:41:40 -0800754 it.value(), response->res, "Zones", zones, "Steps", steps,
755 "Inputs", inputs, "PositiveHysteresis", positiveHysteresis,
James Feistc33a90e2019-03-01 10:17:44 -0800756 "NegativeHysteresis", negativeHysteresis, "Direction",
757 direction))
James Feist5f2caae2018-12-12 14:08:25 -0800758 {
759 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
James Feistb6baeaa2019-02-21 10:41:40 -0800760 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800761 return CreatePIDRet::fail;
762 }
763
764 if (zones)
765 {
James Feistb6baeaa2019-02-21 10:41:40 -0800766 std::vector<std::string> zonesStrs;
767 if (!getZonesFromJsonReq(response, *zones, zonesStrs))
James Feist5f2caae2018-12-12 14:08:25 -0800768 {
769 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Zones";
770 return CreatePIDRet::fail;
771 }
James Feistb6baeaa2019-02-21 10:41:40 -0800772 if (chassis.empty() &&
773 !findChassis(managedObj, zonesStrs[0], chassis))
774 {
775 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
776 messages::invalidObject(response->res, it.key());
777 return CreatePIDRet::fail;
778 }
779 output["Zones"] = std::move(zonesStrs);
James Feist5f2caae2018-12-12 14:08:25 -0800780 }
781 if (steps)
782 {
783 std::vector<double> readings;
784 std::vector<double> outputs;
785 for (auto& step : *steps)
786 {
787 double target;
788 double output;
789
790 if (!redfish::json_util::readJson(step, response->res, "Target",
791 target, "Output", output))
792 {
793 BMCWEB_LOG_ERROR << "Line:" << __LINE__
James Feistb6baeaa2019-02-21 10:41:40 -0800794 << ", Illegal Property "
795 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800796 return CreatePIDRet::fail;
797 }
798 readings.emplace_back(target);
799 outputs.emplace_back(output);
800 }
801 output["Reading"] = std::move(readings);
802 output["Output"] = std::move(outputs);
803 }
804 if (inputs)
805 {
806 for (std::string& value : *inputs)
807 {
James Feist5f2caae2018-12-12 14:08:25 -0800808 boost::replace_all(value, "_", " ");
809 }
810 output["Inputs"] = std::move(*inputs);
811 }
812 if (negativeHysteresis)
813 {
814 output["NegativeHysteresis"] = *negativeHysteresis;
815 }
816 if (positiveHysteresis)
817 {
818 output["PositiveHysteresis"] = *positiveHysteresis;
James Feist83ff9ab2018-08-31 10:18:24 -0700819 }
James Feistc33a90e2019-03-01 10:17:44 -0800820 if (direction)
821 {
822 constexpr const std::array<const char*, 2> allowedDirections = {
823 "Ceiling", "Floor"};
824 if (std::find(allowedDirections.begin(), allowedDirections.end(),
825 *direction) == allowedDirections.end())
826 {
827 messages::propertyValueTypeError(response->res, "Direction",
828 *direction);
829 return CreatePIDRet::fail;
830 }
831 output["Class"] = *direction;
832 }
James Feist83ff9ab2018-08-31 10:18:24 -0700833 }
834 else
835 {
James Feist5f2caae2018-12-12 14:08:25 -0800836 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Type " << type;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700837 messages::propertyUnknown(response->res, type);
James Feist83ff9ab2018-08-31 10:18:24 -0700838 return CreatePIDRet::fail;
839 }
840 return CreatePIDRet::patch;
841}
842
Ed Tanous1abe55e2018-09-05 08:30:59 -0700843class Manager : public Node
844{
845 public:
James Feist5b4aa862018-08-16 14:07:01 -0700846 Manager(CrowApp& app) : Node(app, "/redfish/v1/Managers/bmc/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700847 {
Ed Tanous0f74e642018-11-12 15:17:05 -0800848 uuid = app.template getMiddleware<crow::persistent_data::Middleware>()
849 .systemUuid;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700850 entityPrivileges = {
851 {boost::beast::http::verb::get, {{"Login"}}},
852 {boost::beast::http::verb::head, {{"Login"}}},
853 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
854 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
855 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
856 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100857 }
858
Ed Tanous1abe55e2018-09-05 08:30:59 -0700859 private:
James Feist5b4aa862018-08-16 14:07:01 -0700860 void getPidValues(std::shared_ptr<AsyncResp> asyncResp)
861 {
862 crow::connections::systemBus->async_method_call(
863 [asyncResp](const boost::system::error_code ec,
864 const crow::openbmc_mapper::GetSubTreeType& subtree) {
865 if (ec)
866 {
867 BMCWEB_LOG_ERROR << ec;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700868 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700869 return;
870 }
871
872 // create map of <connection, path to objMgr>>
873 boost::container::flat_map<std::string, std::string>
874 objectMgrPaths;
James Feist6bce33b2018-10-22 12:05:56 -0700875 boost::container::flat_set<std::string> calledConnections;
James Feist5b4aa862018-08-16 14:07:01 -0700876 for (const auto& pathGroup : subtree)
877 {
878 for (const auto& connectionGroup : pathGroup.second)
879 {
James Feist6bce33b2018-10-22 12:05:56 -0700880 auto findConnection =
881 calledConnections.find(connectionGroup.first);
882 if (findConnection != calledConnections.end())
883 {
884 break;
885 }
James Feist5b4aa862018-08-16 14:07:01 -0700886 for (const std::string& interface :
887 connectionGroup.second)
888 {
889 if (interface == objectManagerIface)
890 {
891 objectMgrPaths[connectionGroup.first] =
892 pathGroup.first;
893 }
894 // this list is alphabetical, so we
895 // should have found the objMgr by now
896 if (interface == pidConfigurationIface ||
James Feistb7a08d02018-12-11 14:55:37 -0800897 interface == pidZoneConfigurationIface ||
898 interface == stepwiseConfigurationIface)
James Feist5b4aa862018-08-16 14:07:01 -0700899 {
900 auto findObjMgr =
901 objectMgrPaths.find(connectionGroup.first);
902 if (findObjMgr == objectMgrPaths.end())
903 {
904 BMCWEB_LOG_DEBUG << connectionGroup.first
905 << "Has no Object Manager";
906 continue;
907 }
James Feist6bce33b2018-10-22 12:05:56 -0700908
909 calledConnections.insert(connectionGroup.first);
910
James Feist5b4aa862018-08-16 14:07:01 -0700911 asyncPopulatePid(findObjMgr->first,
912 findObjMgr->second, asyncResp);
913 break;
914 }
915 }
916 }
917 }
918 },
919 "xyz.openbmc_project.ObjectMapper",
920 "/xyz/openbmc_project/object_mapper",
921 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
James Feistb7a08d02018-12-11 14:55:37 -0800922 std::array<const char*, 4>{
923 pidConfigurationIface, pidZoneConfigurationIface,
924 objectManagerIface, stepwiseConfigurationIface});
James Feist5b4aa862018-08-16 14:07:01 -0700925 }
926
927 void doGet(crow::Response& res, const crow::Request& req,
928 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700929 {
Ed Tanous0f74e642018-11-12 15:17:05 -0800930 res.jsonValue["@odata.id"] = "/redfish/v1/Managers/bmc";
931 res.jsonValue["@odata.type"] = "#Manager.v1_3_0.Manager";
932 res.jsonValue["@odata.context"] =
933 "/redfish/v1/$metadata#Manager.Manager";
934 res.jsonValue["Id"] = "bmc";
935 res.jsonValue["Name"] = "OpenBmc Manager";
936 res.jsonValue["Description"] = "Baseboard Management Controller";
937 res.jsonValue["PowerState"] = "On";
Ed Tanous029573d2019-02-01 10:57:49 -0800938 res.jsonValue["Status"] = {{"State", "Enabled"}, {"Health", "OK"}};
Ed Tanous0f74e642018-11-12 15:17:05 -0800939 res.jsonValue["ManagerType"] = "BMC";
Ed Tanous75176582018-12-14 08:14:34 -0800940 res.jsonValue["UUID"] = uuid;
941 res.jsonValue["Model"] = "OpenBmc"; // TODO(ed), get model
Ed Tanous0f74e642018-11-12 15:17:05 -0800942
943 res.jsonValue["LogServices"] = {
944 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices"}};
945
946 res.jsonValue["NetworkProtocol"] = {
947 {"@odata.id", "/redfish/v1/Managers/bmc/NetworkProtocol"}};
948
949 res.jsonValue["EthernetInterfaces"] = {
950 {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces"}};
951 // default oem data
952 nlohmann::json& oem = res.jsonValue["Oem"];
953 nlohmann::json& oemOpenbmc = oem["OpenBmc"];
954 oem["@odata.type"] = "#OemManager.Oem";
955 oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem";
956 oem["@odata.context"] = "/redfish/v1/$metadata#OemManager.Oem";
957 oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc";
958 oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc";
959 oemOpenbmc["@odata.context"] =
960 "/redfish/v1/$metadata#OemManager.OpenBmc";
961
Jennifer Leeed5befb2018-08-10 11:29:45 -0700962 // Update Actions object.
Ed Tanous0f74e642018-11-12 15:17:05 -0800963 nlohmann::json& manager_reset =
964 res.jsonValue["Actions"]["#Manager.Reset"];
Jennifer Leeed5befb2018-08-10 11:29:45 -0700965 manager_reset["target"] =
966 "/redfish/v1/Managers/bmc/Actions/Manager.Reset";
967 manager_reset["ResetType@Redfish.AllowableValues"] = {
968 "GracefulRestart"};
Jennifer Leeca537922018-08-10 10:07:30 -0700969
Ed Tanous0f74e642018-11-12 15:17:05 -0800970 res.jsonValue["DateTime"] = getDateTime();
Ed Tanous029573d2019-02-01 10:57:49 -0800971 res.jsonValue["Links"] = {
972 {"ManagerForServers@odata.count", 1},
973 {"ManagerForServers",
974 {{{"@odata.id", "/redfish/v1/Systems/system"}}}},
975 {"ManagerForServers", nlohmann::json::array()}};
Jennifer Leeed5befb2018-08-10 11:29:45 -0700976 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
James Feist5b4aa862018-08-16 14:07:01 -0700977
Jennifer Leeca537922018-08-10 10:07:30 -0700978 crow::connections::systemBus->async_method_call(
979 [asyncResp](const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -0700980 const dbus::utility::ManagedObjectType& resp) {
Jennifer Leeca537922018-08-10 10:07:30 -0700981 if (ec)
982 {
983 BMCWEB_LOG_ERROR << "Error while getting Software Version";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700984 messages::internalError(asyncResp->res);
Jennifer Leeca537922018-08-10 10:07:30 -0700985 return;
986 }
987
James Feist5b4aa862018-08-16 14:07:01 -0700988 for (auto& objpath : resp)
Jennifer Leeca537922018-08-10 10:07:30 -0700989 {
James Feist5b4aa862018-08-16 14:07:01 -0700990 for (auto& interface : objpath.second)
Jennifer Leeca537922018-08-10 10:07:30 -0700991 {
James Feist5f2caae2018-12-12 14:08:25 -0800992 // If interface is
993 // xyz.openbmc_project.Software.Version, this is
994 // what we're looking for.
Jennifer Leeca537922018-08-10 10:07:30 -0700995 if (interface.first ==
996 "xyz.openbmc_project.Software.Version")
997 {
998 // Cut out everyting until last "/", ...
James Feist5b4aa862018-08-16 14:07:01 -0700999 for (auto& property : interface.second)
Jennifer Leeca537922018-08-10 10:07:30 -07001000 {
1001 if (property.first == "Version")
1002 {
James Feist5b4aa862018-08-16 14:07:01 -07001003 const std::string* value =
Ed Tanousabf2add2019-01-22 16:40:12 -08001004 std::get_if<std::string>(
1005 &property.second);
Jennifer Leeca537922018-08-10 10:07:30 -07001006 if (value == nullptr)
1007 {
1008 continue;
1009 }
1010 asyncResp->res
1011 .jsonValue["FirmwareVersion"] = *value;
1012 }
1013 }
1014 }
1015 }
1016 }
1017 },
1018 "xyz.openbmc_project.Software.BMC.Updater",
1019 "/xyz/openbmc_project/software",
1020 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
James Feist5b4aa862018-08-16 14:07:01 -07001021 getPidValues(asyncResp);
1022 }
James Feist5f2caae2018-12-12 14:08:25 -08001023 void setPidValues(std::shared_ptr<AsyncResp> response, nlohmann::json& data)
James Feist83ff9ab2018-08-31 10:18:24 -07001024 {
James Feist5f2caae2018-12-12 14:08:25 -08001025
James Feist83ff9ab2018-08-31 10:18:24 -07001026 // todo(james): might make sense to do a mapper call here if this
1027 // interface gets more traction
1028 crow::connections::systemBus->async_method_call(
1029 [response,
1030 data](const boost::system::error_code ec,
1031 const dbus::utility::ManagedObjectType& managedObj) {
1032 if (ec)
1033 {
1034 BMCWEB_LOG_ERROR << "Error communicating to Entity Manager";
Jason M. Bills35a62c72018-10-09 12:45:45 -07001035 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -07001036 return;
1037 }
James Feist5f2caae2018-12-12 14:08:25 -08001038
1039 // todo(james) mutable doesn't work with asio bindings
1040 nlohmann::json jsonData = data;
1041
1042 std::optional<nlohmann::json> pidControllers;
1043 std::optional<nlohmann::json> fanControllers;
1044 std::optional<nlohmann::json> fanZones;
1045 std::optional<nlohmann::json> stepwiseControllers;
1046 if (!redfish::json_util::readJson(
1047 jsonData, response->res, "PidControllers",
1048 pidControllers, "FanControllers", fanControllers,
1049 "FanZones", fanZones, "StepwiseControllers",
1050 stepwiseControllers))
James Feist83ff9ab2018-08-31 10:18:24 -07001051 {
James Feist5f2caae2018-12-12 14:08:25 -08001052 BMCWEB_LOG_ERROR << "Line:" << __LINE__
1053 << ", Illegal Property "
1054 << jsonData.dump();
1055 return;
1056 }
1057 std::array<
Ed Tanous43b761d2019-02-13 20:10:56 -08001058 std::pair<std::string, std::optional<nlohmann::json>*>, 4>
James Feist5f2caae2018-12-12 14:08:25 -08001059 sections = {
1060 std::make_pair("PidControllers", &pidControllers),
1061 std::make_pair("FanControllers", &fanControllers),
1062 std::make_pair("FanZones", &fanZones),
1063 std::make_pair("StepwiseControllers",
1064 &stepwiseControllers)};
1065
1066 for (auto& containerPair : sections)
1067 {
1068 auto& container = *(containerPair.second);
1069 if (!container)
James Feist83ff9ab2018-08-31 10:18:24 -07001070 {
James Feist5f2caae2018-12-12 14:08:25 -08001071 continue;
James Feist83ff9ab2018-08-31 10:18:24 -07001072 }
Ed Tanous43b761d2019-02-13 20:10:56 -08001073 std::string& type = containerPair.first;
James Feist5f2caae2018-12-12 14:08:25 -08001074
James Feistb6baeaa2019-02-21 10:41:40 -08001075 for (nlohmann::json::iterator it = container->begin();
1076 it != container->end(); it++)
James Feist83ff9ab2018-08-31 10:18:24 -07001077 {
James Feistb6baeaa2019-02-21 10:41:40 -08001078 const auto& name = it.key();
James Feist83ff9ab2018-08-31 10:18:24 -07001079 auto pathItr =
1080 std::find_if(managedObj.begin(), managedObj.end(),
1081 [&name](const auto& obj) {
1082 return boost::algorithm::ends_with(
James Feistb6baeaa2019-02-21 10:41:40 -08001083 obj.first.str, "/" + name);
James Feist83ff9ab2018-08-31 10:18:24 -07001084 });
1085 boost::container::flat_map<
1086 std::string, dbus::utility::DbusVariantType>
1087 output;
1088
1089 output.reserve(16); // The pid interface length
1090
1091 // determines if we're patching entity-manager or
1092 // creating a new object
1093 bool createNewObject = (pathItr == managedObj.end());
James Feist5f2caae2018-12-12 14:08:25 -08001094 std::string iface;
1095 if (type == "PidControllers" ||
1096 type == "FanControllers")
James Feist83ff9ab2018-08-31 10:18:24 -07001097 {
James Feist5f2caae2018-12-12 14:08:25 -08001098 iface = pidConfigurationIface;
James Feist83ff9ab2018-08-31 10:18:24 -07001099 if (!createNewObject &&
1100 pathItr->second.find(pidConfigurationIface) ==
1101 pathItr->second.end())
1102 {
1103 createNewObject = true;
1104 }
1105 }
James Feist5f2caae2018-12-12 14:08:25 -08001106 else if (type == "FanZones")
James Feist83ff9ab2018-08-31 10:18:24 -07001107 {
James Feist5f2caae2018-12-12 14:08:25 -08001108 iface = pidZoneConfigurationIface;
1109 if (!createNewObject &&
1110 pathItr->second.find(
1111 pidZoneConfigurationIface) ==
1112 pathItr->second.end())
1113 {
1114
1115 createNewObject = true;
1116 }
1117 }
1118 else if (type == "StepwiseControllers")
1119 {
1120 iface = stepwiseConfigurationIface;
1121 if (!createNewObject &&
1122 pathItr->second.find(
1123 stepwiseConfigurationIface) ==
1124 pathItr->second.end())
1125 {
1126 createNewObject = true;
1127 }
James Feist83ff9ab2018-08-31 10:18:24 -07001128 }
James Feistb6baeaa2019-02-21 10:41:40 -08001129 BMCWEB_LOG_DEBUG << "Create new = " << createNewObject
1130 << "\n";
James Feist83ff9ab2018-08-31 10:18:24 -07001131 output["Name"] =
1132 boost::replace_all_copy(name, "_", " ");
1133
1134 std::string chassis;
1135 CreatePIDRet ret = createPidInterface(
James Feistb6baeaa2019-02-21 10:41:40 -08001136 response, type, it, pathItr->first.str, managedObj,
1137 createNewObject, output, chassis);
James Feist83ff9ab2018-08-31 10:18:24 -07001138 if (ret == CreatePIDRet::fail)
1139 {
1140 return;
1141 }
1142 else if (ret == CreatePIDRet::del)
1143 {
1144 continue;
1145 }
1146
1147 if (!createNewObject)
1148 {
1149 for (const auto& property : output)
1150 {
James Feist83ff9ab2018-08-31 10:18:24 -07001151 crow::connections::systemBus->async_method_call(
1152 [response,
1153 propertyName{std::string(property.first)}](
1154 const boost::system::error_code ec) {
1155 if (ec)
1156 {
1157 BMCWEB_LOG_ERROR
1158 << "Error patching "
1159 << propertyName << ": " << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -07001160 messages::internalError(
1161 response->res);
James Feistb6baeaa2019-02-21 10:41:40 -08001162 return;
James Feist83ff9ab2018-08-31 10:18:24 -07001163 }
James Feistb6baeaa2019-02-21 10:41:40 -08001164 messages::success(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -07001165 },
1166 "xyz.openbmc_project.EntityManager",
1167 pathItr->first.str,
1168 "org.freedesktop.DBus.Properties", "Set",
James Feist5f2caae2018-12-12 14:08:25 -08001169 iface, property.first, property.second);
James Feist83ff9ab2018-08-31 10:18:24 -07001170 }
1171 }
1172 else
1173 {
1174 if (chassis.empty())
1175 {
1176 BMCWEB_LOG_ERROR
1177 << "Failed to get chassis from config";
Jason M. Bills35a62c72018-10-09 12:45:45 -07001178 messages::invalidObject(response->res, name);
James Feist83ff9ab2018-08-31 10:18:24 -07001179 return;
1180 }
1181
1182 bool foundChassis = false;
1183 for (const auto& obj : managedObj)
1184 {
1185 if (boost::algorithm::ends_with(obj.first.str,
1186 chassis))
1187 {
1188 chassis = obj.first.str;
1189 foundChassis = true;
1190 break;
1191 }
1192 }
1193 if (!foundChassis)
1194 {
1195 BMCWEB_LOG_ERROR
1196 << "Failed to find chassis on dbus";
Jason M. Bills35a62c72018-10-09 12:45:45 -07001197 messages::resourceMissingAtURI(
1198 response->res,
1199 "/redfish/v1/Chassis/" + chassis);
James Feist83ff9ab2018-08-31 10:18:24 -07001200 return;
1201 }
1202
1203 crow::connections::systemBus->async_method_call(
1204 [response](const boost::system::error_code ec) {
1205 if (ec)
1206 {
1207 BMCWEB_LOG_ERROR
1208 << "Error Adding Pid Object " << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -07001209 messages::internalError(response->res);
James Feistb6baeaa2019-02-21 10:41:40 -08001210 return;
James Feist83ff9ab2018-08-31 10:18:24 -07001211 }
James Feistb6baeaa2019-02-21 10:41:40 -08001212 messages::success(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -07001213 },
1214 "xyz.openbmc_project.EntityManager", chassis,
1215 "xyz.openbmc_project.AddObject", "AddObject",
1216 output);
1217 }
1218 }
1219 }
1220 },
1221 "xyz.openbmc_project.EntityManager", "/", objectManagerIface,
1222 "GetManagedObjects");
1223 }
James Feist5b4aa862018-08-16 14:07:01 -07001224
1225 void doPatch(crow::Response& res, const crow::Request& req,
1226 const std::vector<std::string>& params) override
1227 {
Ed Tanous0627a2c2018-11-29 17:09:23 -08001228 std::optional<nlohmann::json> oem;
1229
1230 if (!json_util::readJson(req, res, "Oem", oem))
James Feist83ff9ab2018-08-31 10:18:24 -07001231 {
1232 return;
1233 }
Ed Tanous0627a2c2018-11-29 17:09:23 -08001234
James Feist83ff9ab2018-08-31 10:18:24 -07001235 std::shared_ptr<AsyncResp> response = std::make_shared<AsyncResp>(res);
Ed Tanous0627a2c2018-11-29 17:09:23 -08001236
1237 if (oem)
James Feist83ff9ab2018-08-31 10:18:24 -07001238 {
Ed Tanous43b761d2019-02-13 20:10:56 -08001239 std::optional<nlohmann::json> openbmc;
1240 if (!redfish::json_util::readJson(*oem, res, "OpenBmc", openbmc))
James Feist83ff9ab2018-08-31 10:18:24 -07001241 {
Ed Tanous43b761d2019-02-13 20:10:56 -08001242 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
1243 << oem->dump();
1244 return;
1245 }
1246 if (openbmc)
1247 {
1248 std::optional<nlohmann::json> fan;
1249 if (!redfish::json_util::readJson(*openbmc, res, "Fan", fan))
James Feist83ff9ab2018-08-31 10:18:24 -07001250 {
James Feist5f2caae2018-12-12 14:08:25 -08001251 BMCWEB_LOG_ERROR << "Line:" << __LINE__
Ed Tanous43b761d2019-02-13 20:10:56 -08001252 << ", Illegal Property "
1253 << openbmc->dump();
James Feist5f2caae2018-12-12 14:08:25 -08001254 return;
1255 }
Ed Tanous43b761d2019-02-13 20:10:56 -08001256 if (fan)
James Feist5f2caae2018-12-12 14:08:25 -08001257 {
Ed Tanous43b761d2019-02-13 20:10:56 -08001258 setPidValues(response, *fan);
James Feist83ff9ab2018-08-31 10:18:24 -07001259 }
James Feist83ff9ab2018-08-31 10:18:24 -07001260 }
1261 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001262 }
1263
1264 std::string getDateTime() const
1265 {
1266 std::array<char, 128> dateTime;
1267 std::string redfishDateTime("0000-00-00T00:00:00Z00:00");
1268 std::time_t time = std::time(nullptr);
1269
1270 if (std::strftime(dateTime.begin(), dateTime.size(), "%FT%T%z",
1271 std::localtime(&time)))
1272 {
1273 // insert the colon required by the ISO 8601 standard
1274 redfishDateTime = std::string(dateTime.data());
1275 redfishDateTime.insert(redfishDateTime.end() - 2, ':');
1276 }
1277
1278 return redfishDateTime;
1279 }
Ed Tanous0f74e642018-11-12 15:17:05 -08001280
1281 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