blob: 4072645d1091c0b2937c1ac0fddf3f3d74c1517c [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 Feist5b4aa862018-08-16 14:07:01 -0700198 if (intfPair.first == pidZoneConfigurationIface)
199 {
200 std::string chassis;
201 if (!dbus::utility::getNthStringFromPath(
202 pathPair.first.str, 5, chassis))
203 {
204 chassis = "#IllegalValue";
205 }
206 nlohmann::json& zone = zones[name];
207 zone["Chassis"] = {
208 {"@odata.id", "/redfish/v1/Chassis/" + chassis}};
209 zone["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/"
210 "OpenBmc/Fan/FanZones/" +
211 name;
212 zone["@odata.type"] = "#OemManager.FanZone";
213 zone["@odata.context"] =
214 "/redfish/v1/$metadata#OemManager.FanZone";
James Feistb7a08d02018-12-11 14:55:37 -0800215 config = &zone;
James Feist5b4aa862018-08-16 14:07:01 -0700216 }
217
James Feistb7a08d02018-12-11 14:55:37 -0800218 else if (intfPair.first == stepwiseConfigurationIface)
219 {
220 nlohmann::json& controller = stepwise[name];
221 config = &controller;
222
223 controller["@odata.id"] =
224 "/redfish/v1/Managers/bmc#/Oem/"
225 "OpenBmc/Fan/StepwiseControllers/" +
226 std::string(name);
227 controller["@odata.type"] =
228 "#OemManager.StepwiseController";
229
230 controller["@odata.context"] =
231 "/redfish/v1/"
232 "$metadata#OemManager.StepwiseController";
233 }
234
235 // pid and fans are off the same configuration
236 else if (intfPair.first == pidConfigurationIface)
237 {
238 const std::string* classPtr = nullptr;
239 auto findClass = intfPair.second.find("Class");
240 if (findClass != intfPair.second.end())
241 {
Ed Tanousabf2add2019-01-22 16:40:12 -0800242 classPtr =
243 std::get_if<std::string>(&findClass->second);
James Feistb7a08d02018-12-11 14:55:37 -0800244 }
245 if (classPtr == nullptr)
246 {
247 BMCWEB_LOG_ERROR << "Pid Class Field illegal";
248 messages::internalError(asyncResp->res);
249 return;
250 }
251 bool isFan = *classPtr == "fan";
252 nlohmann::json& element =
253 isFan ? fans[name] : pids[name];
254 config = &element;
255 if (isFan)
256 {
257 element["@odata.id"] =
258 "/redfish/v1/Managers/bmc#/Oem/"
259 "OpenBmc/Fan/FanControllers/" +
260 std::string(name);
261 element["@odata.type"] =
262 "#OemManager.FanController";
263
264 element["@odata.context"] =
265 "/redfish/v1/"
266 "$metadata#OemManager.FanController";
267 }
268 else
269 {
270 element["@odata.id"] =
271 "/redfish/v1/Managers/bmc#/Oem/"
272 "OpenBmc/Fan/PidControllers/" +
273 std::string(name);
274 element["@odata.type"] =
275 "#OemManager.PidController";
276 element["@odata.context"] =
277 "/redfish/v1/$metadata"
278 "#OemManager.PidController";
279 }
280 }
281 else
282 {
283 BMCWEB_LOG_ERROR << "Unexpected configuration";
284 messages::internalError(asyncResp->res);
285 return;
286 }
287
288 // used for making maps out of 2 vectors
289 const std::vector<double>* keys = nullptr;
290 const std::vector<double>* values = nullptr;
291
James Feist5b4aa862018-08-16 14:07:01 -0700292 for (const auto& propertyPair : intfPair.second)
293 {
294 if (propertyPair.first == "Type" ||
295 propertyPair.first == "Class" ||
296 propertyPair.first == "Name")
297 {
298 continue;
299 }
300
301 // zones
302 if (intfPair.first == pidZoneConfigurationIface)
303 {
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800304 const double* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800305 std::get_if<double>(&propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700306 if (ptr == nullptr)
307 {
308 BMCWEB_LOG_ERROR << "Field Illegal "
309 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700310 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700311 return;
312 }
James Feistb7a08d02018-12-11 14:55:37 -0800313 (*config)[propertyPair.first] = *ptr;
314 }
315
316 if (intfPair.first == stepwiseConfigurationIface)
317 {
318 if (propertyPair.first == "Reading" ||
319 propertyPair.first == "Output")
320 {
321 const std::vector<double>* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800322 std::get_if<std::vector<double>>(
James Feistb7a08d02018-12-11 14:55:37 -0800323 &propertyPair.second);
324
325 if (ptr == nullptr)
326 {
327 BMCWEB_LOG_ERROR << "Field Illegal "
328 << propertyPair.first;
329 messages::internalError(asyncResp->res);
330 return;
331 }
332
333 if (propertyPair.first == "Reading")
334 {
335 keys = ptr;
336 }
337 else
338 {
339 values = ptr;
340 }
341 if (keys && values)
342 {
343 if (keys->size() != values->size())
344 {
345 BMCWEB_LOG_ERROR
346 << "Reading and Output size don't "
347 "match ";
348 messages::internalError(asyncResp->res);
349 return;
350 }
351 nlohmann::json& steps = (*config)["Steps"];
352 steps = nlohmann::json::array();
353 for (size_t ii = 0; ii < keys->size(); ii++)
354 {
355 steps.push_back(
356 {{"Target", (*keys)[ii]},
357 {"Output", (*values)[ii]}});
358 }
359 }
360 }
361 if (propertyPair.first == "NegativeHysteresis" ||
362 propertyPair.first == "PositiveHysteresis")
363 {
364 const double* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800365 std::get_if<double>(&propertyPair.second);
James Feistb7a08d02018-12-11 14:55:37 -0800366 if (ptr == nullptr)
367 {
368 BMCWEB_LOG_ERROR << "Field Illegal "
369 << propertyPair.first;
370 messages::internalError(asyncResp->res);
371 return;
372 }
373 (*config)[propertyPair.first] = *ptr;
374 }
James Feist5b4aa862018-08-16 14:07:01 -0700375 }
376
377 // pid and fans are off the same configuration
James Feistb7a08d02018-12-11 14:55:37 -0800378 if (intfPair.first == pidConfigurationIface ||
379 intfPair.first == stepwiseConfigurationIface)
James Feist5b4aa862018-08-16 14:07:01 -0700380 {
James Feist5b4aa862018-08-16 14:07:01 -0700381
382 if (propertyPair.first == "Zones")
383 {
384 const std::vector<std::string>* inputs =
Ed Tanousabf2add2019-01-22 16:40:12 -0800385 std::get_if<std::vector<std::string>>(
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800386 &propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700387
388 if (inputs == nullptr)
389 {
390 BMCWEB_LOG_ERROR
391 << "Zones Pid Field Illegal";
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800392 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700393 return;
394 }
James Feistb7a08d02018-12-11 14:55:37 -0800395 auto& data = (*config)[propertyPair.first];
James Feist5b4aa862018-08-16 14:07:01 -0700396 data = nlohmann::json::array();
397 for (std::string itemCopy : *inputs)
398 {
399 dbus::utility::escapePathForDbus(itemCopy);
400 data.push_back(
401 {{"@odata.id",
402 "/redfish/v1/Managers/bmc#/Oem/"
403 "OpenBmc/Fan/FanZones/" +
404 itemCopy}});
405 }
406 }
407 // todo(james): may never happen, but this
408 // assumes configuration data referenced in the
409 // PID config is provided by the same daemon, we
410 // could add another loop to cover all cases,
411 // but I'm okay kicking this can down the road a
412 // bit
413
414 else if (propertyPair.first == "Inputs" ||
415 propertyPair.first == "Outputs")
416 {
James Feistb7a08d02018-12-11 14:55:37 -0800417 auto& data = (*config)[propertyPair.first];
James Feist5b4aa862018-08-16 14:07:01 -0700418 const std::vector<std::string>* inputs =
Ed Tanousabf2add2019-01-22 16:40:12 -0800419 std::get_if<std::vector<std::string>>(
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800420 &propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700421
422 if (inputs == nullptr)
423 {
424 BMCWEB_LOG_ERROR << "Field Illegal "
425 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700426 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700427 return;
428 }
429 data = *inputs;
430 } // doubles
431 else if (propertyPair.first ==
432 "FFGainCoefficient" ||
433 propertyPair.first == "FFOffCoefficient" ||
434 propertyPair.first == "ICoefficient" ||
435 propertyPair.first == "ILimitMax" ||
436 propertyPair.first == "ILimitMin" ||
James Feistaad1a252019-02-19 10:13:52 -0800437 propertyPair.first ==
438 "PositiveHysteresis" ||
439 propertyPair.first ==
440 "NegativeHysteresis" ||
James Feist5b4aa862018-08-16 14:07:01 -0700441 propertyPair.first == "OutLimitMax" ||
442 propertyPair.first == "OutLimitMin" ||
443 propertyPair.first == "PCoefficient" ||
James Feist7625cb82019-01-23 11:58:21 -0800444 propertyPair.first == "SetPoint" ||
James Feist5b4aa862018-08-16 14:07:01 -0700445 propertyPair.first == "SlewNeg" ||
446 propertyPair.first == "SlewPos")
447 {
448 const double* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800449 std::get_if<double>(&propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700450 if (ptr == nullptr)
451 {
452 BMCWEB_LOG_ERROR << "Field Illegal "
453 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700454 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700455 return;
456 }
James Feistb7a08d02018-12-11 14:55:37 -0800457 (*config)[propertyPair.first] = *ptr;
James Feist5b4aa862018-08-16 14:07:01 -0700458 }
459 }
460 }
461 }
462 }
463 },
464 connection, path, objectManagerIface, "GetManagedObjects");
465}
Jennifer Leeca537922018-08-10 10:07:30 -0700466
James Feist83ff9ab2018-08-31 10:18:24 -0700467enum class CreatePIDRet
468{
469 fail,
470 del,
471 patch
472};
473
James Feist5f2caae2018-12-12 14:08:25 -0800474static bool getZonesFromJsonReq(const std::shared_ptr<AsyncResp>& response,
475 std::vector<nlohmann::json>& config,
476 std::vector<std::string>& zones)
477{
James Feistb6baeaa2019-02-21 10:41:40 -0800478 if (config.empty())
479 {
480 BMCWEB_LOG_ERROR << "Empty Zones";
481 messages::propertyValueFormatError(response->res,
482 nlohmann::json::array(), "Zones");
483 return false;
484 }
James Feist5f2caae2018-12-12 14:08:25 -0800485 for (auto& odata : config)
486 {
487 std::string path;
488 if (!redfish::json_util::readJson(odata, response->res, "@odata.id",
489 path))
490 {
491 return false;
492 }
493 std::string input;
494 if (!dbus::utility::getNthStringFromPath(path, 4, input))
495 {
496 BMCWEB_LOG_ERROR << "Got invalid path " << path;
497 BMCWEB_LOG_ERROR << "Illegal Type Zones";
498 messages::propertyValueFormatError(response->res, odata.dump(),
499 "Zones");
500 return false;
501 }
502 boost::replace_all(input, "_", " ");
503 zones.emplace_back(std::move(input));
504 }
505 return true;
506}
507
James Feistb6baeaa2019-02-21 10:41:40 -0800508static bool findChassis(const dbus::utility::ManagedObjectType& managedObj,
509 const std::string& value, std::string& chassis)
510{
511 BMCWEB_LOG_DEBUG << "Find Chassis: " << value << "\n";
512
513 std::string escaped = boost::replace_all_copy(value, " ", "_");
514 escaped = "/" + escaped;
515 auto it = std::find_if(
516 managedObj.begin(), managedObj.end(), [&escaped](const auto& obj) {
517 if (boost::algorithm::ends_with(obj.first.str, escaped))
518 {
519 BMCWEB_LOG_DEBUG << "Matched " << obj.first.str << "\n";
520 return true;
521 }
522 return false;
523 });
524
525 if (it == managedObj.end())
526 {
527 return false;
528 }
529 // 5 comes from <chassis-name> being the 5th element
530 // /xyz/openbmc_project/inventory/system/chassis/<chassis-name>
531 return dbus::utility::getNthStringFromPath(it->first.str, 5, chassis);
532}
533
James Feist83ff9ab2018-08-31 10:18:24 -0700534static CreatePIDRet createPidInterface(
535 const std::shared_ptr<AsyncResp>& response, const std::string& type,
James Feistb6baeaa2019-02-21 10:41:40 -0800536 nlohmann::json::iterator it, const std::string& path,
James Feist83ff9ab2018-08-31 10:18:24 -0700537 const dbus::utility::ManagedObjectType& managedObj, bool createNewObject,
538 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>&
539 output,
540 std::string& chassis)
541{
542
James Feist5f2caae2018-12-12 14:08:25 -0800543 // common deleter
James Feistb6baeaa2019-02-21 10:41:40 -0800544 if (it.value() == nullptr)
James Feist5f2caae2018-12-12 14:08:25 -0800545 {
546 std::string iface;
547 if (type == "PidControllers" || type == "FanControllers")
548 {
549 iface = pidConfigurationIface;
550 }
551 else if (type == "FanZones")
552 {
553 iface = pidZoneConfigurationIface;
554 }
555 else if (type == "StepwiseControllers")
556 {
557 iface = stepwiseConfigurationIface;
558 }
559 else
560 {
561 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Type "
562 << type;
563 messages::propertyUnknown(response->res, type);
564 return CreatePIDRet::fail;
565 }
566 // delete interface
567 crow::connections::systemBus->async_method_call(
568 [response, path](const boost::system::error_code ec) {
569 if (ec)
570 {
571 BMCWEB_LOG_ERROR << "Error patching " << path << ": " << ec;
572 messages::internalError(response->res);
James Feistb6baeaa2019-02-21 10:41:40 -0800573 return;
James Feist5f2caae2018-12-12 14:08:25 -0800574 }
James Feistb6baeaa2019-02-21 10:41:40 -0800575 messages::success(response->res);
James Feist5f2caae2018-12-12 14:08:25 -0800576 },
577 "xyz.openbmc_project.EntityManager", path, iface, "Delete");
578 return CreatePIDRet::del;
579 }
580
James Feistb6baeaa2019-02-21 10:41:40 -0800581 if (!createNewObject)
582 {
583 // if we aren't creating a new object, we should be able to find it on
584 // d-bus
585 if (!findChassis(managedObj, it.key(), chassis))
586 {
587 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
588 messages::invalidObject(response->res, it.key());
589 return CreatePIDRet::fail;
590 }
591 }
592
James Feist83ff9ab2018-08-31 10:18:24 -0700593 if (type == "PidControllers" || type == "FanControllers")
594 {
595 if (createNewObject)
596 {
597 output["Class"] = type == "PidControllers" ? std::string("temp")
598 : std::string("fan");
599 output["Type"] = std::string("Pid");
600 }
James Feist5f2caae2018-12-12 14:08:25 -0800601
602 std::optional<std::vector<nlohmann::json>> zones;
603 std::optional<std::vector<std::string>> inputs;
604 std::optional<std::vector<std::string>> outputs;
605 std::map<std::string, std::optional<double>> doubles;
606 if (!redfish::json_util::readJson(
James Feistb6baeaa2019-02-21 10:41:40 -0800607 it.value(), response->res, "Inputs", inputs, "Outputs", outputs,
James Feist5f2caae2018-12-12 14:08:25 -0800608 "Zones", zones, "FFGainCoefficient",
609 doubles["FFGainCoefficient"], "FFOffCoefficient",
610 doubles["FFOffCoefficient"], "ICoefficient",
611 doubles["ICoefficient"], "ILimitMax", doubles["ILimitMax"],
612 "ILimitMin", doubles["ILimitMin"], "OutLimitMax",
613 doubles["OutLimitMax"], "OutLimitMin", doubles["OutLimitMin"],
614 "PCoefficient", doubles["PCoefficient"], "SetPoint",
615 doubles["SetPoint"], "SlewNeg", doubles["SlewNeg"], "SlewPos",
James Feistaad1a252019-02-19 10:13:52 -0800616 doubles["SlewPos"], "PositiveHysteresis",
617 doubles["PositiveHysteresis"], "NegativeHysteresis",
618 doubles["NegativeHysteresis"]))
James Feist83ff9ab2018-08-31 10:18:24 -0700619 {
James Feist5f2caae2018-12-12 14:08:25 -0800620 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
James Feistb6baeaa2019-02-21 10:41:40 -0800621 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800622 return CreatePIDRet::fail;
James Feist83ff9ab2018-08-31 10:18:24 -0700623 }
James Feist5f2caae2018-12-12 14:08:25 -0800624 if (zones)
James Feist83ff9ab2018-08-31 10:18:24 -0700625 {
James Feist5f2caae2018-12-12 14:08:25 -0800626 std::vector<std::string> zonesStr;
627 if (!getZonesFromJsonReq(response, *zones, zonesStr))
James Feist83ff9ab2018-08-31 10:18:24 -0700628 {
James Feist5f2caae2018-12-12 14:08:25 -0800629 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Zones";
630 return CreatePIDRet::fail;
James Feist83ff9ab2018-08-31 10:18:24 -0700631 }
James Feistb6baeaa2019-02-21 10:41:40 -0800632 if (chassis.empty() &&
633 !findChassis(managedObj, zonesStr[0], chassis))
634 {
635 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
636 messages::invalidObject(response->res, it.key());
637 return CreatePIDRet::fail;
638 }
639
James Feist5f2caae2018-12-12 14:08:25 -0800640 output["Zones"] = std::move(zonesStr);
641 }
642 if (inputs || outputs)
643 {
644 std::array<std::optional<std::vector<std::string>>*, 2> containers =
645 {&inputs, &outputs};
646 size_t index = 0;
647 for (const auto& containerPtr : containers)
James Feist83ff9ab2018-08-31 10:18:24 -0700648 {
James Feist5f2caae2018-12-12 14:08:25 -0800649 std::optional<std::vector<std::string>>& container =
650 *containerPtr;
651 if (!container)
James Feist83ff9ab2018-08-31 10:18:24 -0700652 {
James Feist5f2caae2018-12-12 14:08:25 -0800653 index++;
654 continue;
James Feist83ff9ab2018-08-31 10:18:24 -0700655 }
James Feist5f2caae2018-12-12 14:08:25 -0800656
657 for (std::string& value : *container)
James Feist83ff9ab2018-08-31 10:18:24 -0700658 {
James Feist5f2caae2018-12-12 14:08:25 -0800659 boost::replace_all(value, "_", " ");
James Feist83ff9ab2018-08-31 10:18:24 -0700660 }
James Feist5f2caae2018-12-12 14:08:25 -0800661 std::string key;
662 if (index == 0)
James Feist83ff9ab2018-08-31 10:18:24 -0700663 {
James Feist5f2caae2018-12-12 14:08:25 -0800664 key = "Inputs";
James Feist83ff9ab2018-08-31 10:18:24 -0700665 }
James Feist5f2caae2018-12-12 14:08:25 -0800666 else
667 {
668 key = "Outputs";
669 }
670 output[key] = *container;
671 index++;
James Feist83ff9ab2018-08-31 10:18:24 -0700672 }
James Feist5f2caae2018-12-12 14:08:25 -0800673 }
James Feist83ff9ab2018-08-31 10:18:24 -0700674
James Feist5f2caae2018-12-12 14:08:25 -0800675 // doubles
676 for (const auto& pairs : doubles)
677 {
678 if (!pairs.second)
James Feist83ff9ab2018-08-31 10:18:24 -0700679 {
James Feist5f2caae2018-12-12 14:08:25 -0800680 continue;
James Feist83ff9ab2018-08-31 10:18:24 -0700681 }
James Feist5f2caae2018-12-12 14:08:25 -0800682 BMCWEB_LOG_DEBUG << pairs.first << " = " << *pairs.second;
683 output[pairs.first] = *(pairs.second);
James Feist83ff9ab2018-08-31 10:18:24 -0700684 }
685 }
James Feist5f2caae2018-12-12 14:08:25 -0800686
James Feist83ff9ab2018-08-31 10:18:24 -0700687 else if (type == "FanZones")
688 {
James Feist83ff9ab2018-08-31 10:18:24 -0700689 output["Type"] = std::string("Pid.Zone");
690
James Feist5f2caae2018-12-12 14:08:25 -0800691 std::optional<nlohmann::json> chassisContainer;
692 std::optional<double> failSafePercent;
693 std::optional<double> minThermalRpm;
James Feistb6baeaa2019-02-21 10:41:40 -0800694 if (!redfish::json_util::readJson(it.value(), response->res, "Chassis",
James Feist5f2caae2018-12-12 14:08:25 -0800695 chassisContainer, "FailSafePercent",
696 failSafePercent, "MinThermalRpm",
697 minThermalRpm))
James Feist83ff9ab2018-08-31 10:18:24 -0700698 {
James Feist5f2caae2018-12-12 14:08:25 -0800699 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
James Feistb6baeaa2019-02-21 10:41:40 -0800700 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800701 return CreatePIDRet::fail;
702 }
James Feist83ff9ab2018-08-31 10:18:24 -0700703
James Feist5f2caae2018-12-12 14:08:25 -0800704 if (chassisContainer)
705 {
706
707 std::string chassisId;
708 if (!redfish::json_util::readJson(*chassisContainer, response->res,
709 "@odata.id", chassisId))
James Feist83ff9ab2018-08-31 10:18:24 -0700710 {
James Feist5f2caae2018-12-12 14:08:25 -0800711 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
712 << chassisContainer->dump();
James Feist83ff9ab2018-08-31 10:18:24 -0700713 return CreatePIDRet::fail;
714 }
James Feist5f2caae2018-12-12 14:08:25 -0800715
716 // /refish/v1/chassis/chassis_name/
717 if (!dbus::utility::getNthStringFromPath(chassisId, 3, chassis))
718 {
719 BMCWEB_LOG_ERROR << "Got invalid path " << chassisId;
720 messages::invalidObject(response->res, chassisId);
721 return CreatePIDRet::fail;
722 }
723 }
724 if (minThermalRpm)
725 {
726 output["MinThermalRpm"] = *minThermalRpm;
727 }
728 if (failSafePercent)
729 {
730 output["FailSafePercent"] = *failSafePercent;
731 }
732 }
733 else if (type == "StepwiseControllers")
734 {
735 output["Type"] = std::string("Stepwise");
736
737 std::optional<std::vector<nlohmann::json>> zones;
738 std::optional<std::vector<nlohmann::json>> steps;
739 std::optional<std::vector<std::string>> inputs;
740 std::optional<double> positiveHysteresis;
741 std::optional<double> negativeHysteresis;
742 if (!redfish::json_util::readJson(
James Feistb6baeaa2019-02-21 10:41:40 -0800743 it.value(), response->res, "Zones", zones, "Steps", steps,
744 "Inputs", inputs, "PositiveHysteresis", positiveHysteresis,
James Feist5f2caae2018-12-12 14:08:25 -0800745 "NegativeHysteresis", negativeHysteresis))
746 {
747 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
James Feistb6baeaa2019-02-21 10:41:40 -0800748 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800749 return CreatePIDRet::fail;
750 }
751
752 if (zones)
753 {
James Feistb6baeaa2019-02-21 10:41:40 -0800754 std::vector<std::string> zonesStrs;
755 if (!getZonesFromJsonReq(response, *zones, zonesStrs))
James Feist5f2caae2018-12-12 14:08:25 -0800756 {
757 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Zones";
758 return CreatePIDRet::fail;
759 }
James Feistb6baeaa2019-02-21 10:41:40 -0800760 if (chassis.empty() &&
761 !findChassis(managedObj, zonesStrs[0], chassis))
762 {
763 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
764 messages::invalidObject(response->res, it.key());
765 return CreatePIDRet::fail;
766 }
767 output["Zones"] = std::move(zonesStrs);
James Feist5f2caae2018-12-12 14:08:25 -0800768 }
769 if (steps)
770 {
771 std::vector<double> readings;
772 std::vector<double> outputs;
773 for (auto& step : *steps)
774 {
775 double target;
776 double output;
777
778 if (!redfish::json_util::readJson(step, response->res, "Target",
779 target, "Output", output))
780 {
781 BMCWEB_LOG_ERROR << "Line:" << __LINE__
James Feistb6baeaa2019-02-21 10:41:40 -0800782 << ", Illegal Property "
783 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800784 return CreatePIDRet::fail;
785 }
786 readings.emplace_back(target);
787 outputs.emplace_back(output);
788 }
789 output["Reading"] = std::move(readings);
790 output["Output"] = std::move(outputs);
791 }
792 if (inputs)
793 {
794 for (std::string& value : *inputs)
795 {
James Feist5f2caae2018-12-12 14:08:25 -0800796 boost::replace_all(value, "_", " ");
797 }
798 output["Inputs"] = std::move(*inputs);
799 }
800 if (negativeHysteresis)
801 {
802 output["NegativeHysteresis"] = *negativeHysteresis;
803 }
804 if (positiveHysteresis)
805 {
806 output["PositiveHysteresis"] = *positiveHysteresis;
James Feist83ff9ab2018-08-31 10:18:24 -0700807 }
808 }
809 else
810 {
James Feist5f2caae2018-12-12 14:08:25 -0800811 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Type " << type;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700812 messages::propertyUnknown(response->res, type);
James Feist83ff9ab2018-08-31 10:18:24 -0700813 return CreatePIDRet::fail;
814 }
815 return CreatePIDRet::patch;
816}
817
Ed Tanous1abe55e2018-09-05 08:30:59 -0700818class Manager : public Node
819{
820 public:
James Feist5b4aa862018-08-16 14:07:01 -0700821 Manager(CrowApp& app) : Node(app, "/redfish/v1/Managers/bmc/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700822 {
Ed Tanous0f74e642018-11-12 15:17:05 -0800823 uuid = app.template getMiddleware<crow::persistent_data::Middleware>()
824 .systemUuid;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700825 entityPrivileges = {
826 {boost::beast::http::verb::get, {{"Login"}}},
827 {boost::beast::http::verb::head, {{"Login"}}},
828 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
829 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
830 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
831 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100832 }
833
Ed Tanous1abe55e2018-09-05 08:30:59 -0700834 private:
James Feist5b4aa862018-08-16 14:07:01 -0700835 void getPidValues(std::shared_ptr<AsyncResp> asyncResp)
836 {
837 crow::connections::systemBus->async_method_call(
838 [asyncResp](const boost::system::error_code ec,
839 const crow::openbmc_mapper::GetSubTreeType& subtree) {
840 if (ec)
841 {
842 BMCWEB_LOG_ERROR << ec;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700843 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700844 return;
845 }
846
847 // create map of <connection, path to objMgr>>
848 boost::container::flat_map<std::string, std::string>
849 objectMgrPaths;
James Feist6bce33b2018-10-22 12:05:56 -0700850 boost::container::flat_set<std::string> calledConnections;
James Feist5b4aa862018-08-16 14:07:01 -0700851 for (const auto& pathGroup : subtree)
852 {
853 for (const auto& connectionGroup : pathGroup.second)
854 {
James Feist6bce33b2018-10-22 12:05:56 -0700855 auto findConnection =
856 calledConnections.find(connectionGroup.first);
857 if (findConnection != calledConnections.end())
858 {
859 break;
860 }
James Feist5b4aa862018-08-16 14:07:01 -0700861 for (const std::string& interface :
862 connectionGroup.second)
863 {
864 if (interface == objectManagerIface)
865 {
866 objectMgrPaths[connectionGroup.first] =
867 pathGroup.first;
868 }
869 // this list is alphabetical, so we
870 // should have found the objMgr by now
871 if (interface == pidConfigurationIface ||
James Feistb7a08d02018-12-11 14:55:37 -0800872 interface == pidZoneConfigurationIface ||
873 interface == stepwiseConfigurationIface)
James Feist5b4aa862018-08-16 14:07:01 -0700874 {
875 auto findObjMgr =
876 objectMgrPaths.find(connectionGroup.first);
877 if (findObjMgr == objectMgrPaths.end())
878 {
879 BMCWEB_LOG_DEBUG << connectionGroup.first
880 << "Has no Object Manager";
881 continue;
882 }
James Feist6bce33b2018-10-22 12:05:56 -0700883
884 calledConnections.insert(connectionGroup.first);
885
James Feist5b4aa862018-08-16 14:07:01 -0700886 asyncPopulatePid(findObjMgr->first,
887 findObjMgr->second, asyncResp);
888 break;
889 }
890 }
891 }
892 }
893 },
894 "xyz.openbmc_project.ObjectMapper",
895 "/xyz/openbmc_project/object_mapper",
896 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
James Feistb7a08d02018-12-11 14:55:37 -0800897 std::array<const char*, 4>{
898 pidConfigurationIface, pidZoneConfigurationIface,
899 objectManagerIface, stepwiseConfigurationIface});
James Feist5b4aa862018-08-16 14:07:01 -0700900 }
901
902 void doGet(crow::Response& res, const crow::Request& req,
903 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700904 {
Ed Tanous0f74e642018-11-12 15:17:05 -0800905 res.jsonValue["@odata.id"] = "/redfish/v1/Managers/bmc";
906 res.jsonValue["@odata.type"] = "#Manager.v1_3_0.Manager";
907 res.jsonValue["@odata.context"] =
908 "/redfish/v1/$metadata#Manager.Manager";
909 res.jsonValue["Id"] = "bmc";
910 res.jsonValue["Name"] = "OpenBmc Manager";
911 res.jsonValue["Description"] = "Baseboard Management Controller";
912 res.jsonValue["PowerState"] = "On";
Ed Tanous029573d2019-02-01 10:57:49 -0800913 res.jsonValue["Status"] = {{"State", "Enabled"}, {"Health", "OK"}};
Ed Tanous0f74e642018-11-12 15:17:05 -0800914 res.jsonValue["ManagerType"] = "BMC";
Ed Tanous75176582018-12-14 08:14:34 -0800915 res.jsonValue["UUID"] = uuid;
916 res.jsonValue["Model"] = "OpenBmc"; // TODO(ed), get model
Ed Tanous0f74e642018-11-12 15:17:05 -0800917
918 res.jsonValue["LogServices"] = {
919 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices"}};
920
921 res.jsonValue["NetworkProtocol"] = {
922 {"@odata.id", "/redfish/v1/Managers/bmc/NetworkProtocol"}};
923
924 res.jsonValue["EthernetInterfaces"] = {
925 {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces"}};
926 // default oem data
927 nlohmann::json& oem = res.jsonValue["Oem"];
928 nlohmann::json& oemOpenbmc = oem["OpenBmc"];
929 oem["@odata.type"] = "#OemManager.Oem";
930 oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem";
931 oem["@odata.context"] = "/redfish/v1/$metadata#OemManager.Oem";
932 oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc";
933 oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc";
934 oemOpenbmc["@odata.context"] =
935 "/redfish/v1/$metadata#OemManager.OpenBmc";
936
Jennifer Leeed5befb2018-08-10 11:29:45 -0700937 // Update Actions object.
Ed Tanous0f74e642018-11-12 15:17:05 -0800938 nlohmann::json& manager_reset =
939 res.jsonValue["Actions"]["#Manager.Reset"];
Jennifer Leeed5befb2018-08-10 11:29:45 -0700940 manager_reset["target"] =
941 "/redfish/v1/Managers/bmc/Actions/Manager.Reset";
942 manager_reset["ResetType@Redfish.AllowableValues"] = {
943 "GracefulRestart"};
Jennifer Leeca537922018-08-10 10:07:30 -0700944
Ed Tanous0f74e642018-11-12 15:17:05 -0800945 res.jsonValue["DateTime"] = getDateTime();
Ed Tanous029573d2019-02-01 10:57:49 -0800946 res.jsonValue["Links"] = {
947 {"ManagerForServers@odata.count", 1},
948 {"ManagerForServers",
949 {{{"@odata.id", "/redfish/v1/Systems/system"}}}},
950 {"ManagerForServers", nlohmann::json::array()}};
Jennifer Leeed5befb2018-08-10 11:29:45 -0700951 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
James Feist5b4aa862018-08-16 14:07:01 -0700952
Jennifer Leeca537922018-08-10 10:07:30 -0700953 crow::connections::systemBus->async_method_call(
954 [asyncResp](const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -0700955 const dbus::utility::ManagedObjectType& resp) {
Jennifer Leeca537922018-08-10 10:07:30 -0700956 if (ec)
957 {
958 BMCWEB_LOG_ERROR << "Error while getting Software Version";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700959 messages::internalError(asyncResp->res);
Jennifer Leeca537922018-08-10 10:07:30 -0700960 return;
961 }
962
James Feist5b4aa862018-08-16 14:07:01 -0700963 for (auto& objpath : resp)
Jennifer Leeca537922018-08-10 10:07:30 -0700964 {
James Feist5b4aa862018-08-16 14:07:01 -0700965 for (auto& interface : objpath.second)
Jennifer Leeca537922018-08-10 10:07:30 -0700966 {
James Feist5f2caae2018-12-12 14:08:25 -0800967 // If interface is
968 // xyz.openbmc_project.Software.Version, this is
969 // what we're looking for.
Jennifer Leeca537922018-08-10 10:07:30 -0700970 if (interface.first ==
971 "xyz.openbmc_project.Software.Version")
972 {
973 // Cut out everyting until last "/", ...
James Feist5b4aa862018-08-16 14:07:01 -0700974 for (auto& property : interface.second)
Jennifer Leeca537922018-08-10 10:07:30 -0700975 {
976 if (property.first == "Version")
977 {
James Feist5b4aa862018-08-16 14:07:01 -0700978 const std::string* value =
Ed Tanousabf2add2019-01-22 16:40:12 -0800979 std::get_if<std::string>(
980 &property.second);
Jennifer Leeca537922018-08-10 10:07:30 -0700981 if (value == nullptr)
982 {
983 continue;
984 }
985 asyncResp->res
986 .jsonValue["FirmwareVersion"] = *value;
987 }
988 }
989 }
990 }
991 }
992 },
993 "xyz.openbmc_project.Software.BMC.Updater",
994 "/xyz/openbmc_project/software",
995 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
James Feist5b4aa862018-08-16 14:07:01 -0700996 getPidValues(asyncResp);
997 }
James Feist5f2caae2018-12-12 14:08:25 -0800998 void setPidValues(std::shared_ptr<AsyncResp> response, nlohmann::json& data)
James Feist83ff9ab2018-08-31 10:18:24 -0700999 {
James Feist5f2caae2018-12-12 14:08:25 -08001000
James Feist83ff9ab2018-08-31 10:18:24 -07001001 // todo(james): might make sense to do a mapper call here if this
1002 // interface gets more traction
1003 crow::connections::systemBus->async_method_call(
1004 [response,
1005 data](const boost::system::error_code ec,
1006 const dbus::utility::ManagedObjectType& managedObj) {
1007 if (ec)
1008 {
1009 BMCWEB_LOG_ERROR << "Error communicating to Entity Manager";
Jason M. Bills35a62c72018-10-09 12:45:45 -07001010 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -07001011 return;
1012 }
James Feist5f2caae2018-12-12 14:08:25 -08001013
1014 // todo(james) mutable doesn't work with asio bindings
1015 nlohmann::json jsonData = data;
1016
1017 std::optional<nlohmann::json> pidControllers;
1018 std::optional<nlohmann::json> fanControllers;
1019 std::optional<nlohmann::json> fanZones;
1020 std::optional<nlohmann::json> stepwiseControllers;
1021 if (!redfish::json_util::readJson(
1022 jsonData, response->res, "PidControllers",
1023 pidControllers, "FanControllers", fanControllers,
1024 "FanZones", fanZones, "StepwiseControllers",
1025 stepwiseControllers))
James Feist83ff9ab2018-08-31 10:18:24 -07001026 {
James Feist5f2caae2018-12-12 14:08:25 -08001027 BMCWEB_LOG_ERROR << "Line:" << __LINE__
1028 << ", Illegal Property "
1029 << jsonData.dump();
1030 return;
1031 }
1032 std::array<
Ed Tanous43b761d2019-02-13 20:10:56 -08001033 std::pair<std::string, std::optional<nlohmann::json>*>, 4>
James Feist5f2caae2018-12-12 14:08:25 -08001034 sections = {
1035 std::make_pair("PidControllers", &pidControllers),
1036 std::make_pair("FanControllers", &fanControllers),
1037 std::make_pair("FanZones", &fanZones),
1038 std::make_pair("StepwiseControllers",
1039 &stepwiseControllers)};
1040
1041 for (auto& containerPair : sections)
1042 {
1043 auto& container = *(containerPair.second);
1044 if (!container)
James Feist83ff9ab2018-08-31 10:18:24 -07001045 {
James Feist5f2caae2018-12-12 14:08:25 -08001046 continue;
James Feist83ff9ab2018-08-31 10:18:24 -07001047 }
Ed Tanous43b761d2019-02-13 20:10:56 -08001048 std::string& type = containerPair.first;
James Feist5f2caae2018-12-12 14:08:25 -08001049
James Feistb6baeaa2019-02-21 10:41:40 -08001050 for (nlohmann::json::iterator it = container->begin();
1051 it != container->end(); it++)
James Feist83ff9ab2018-08-31 10:18:24 -07001052 {
James Feistb6baeaa2019-02-21 10:41:40 -08001053 const auto& name = it.key();
James Feist83ff9ab2018-08-31 10:18:24 -07001054 auto pathItr =
1055 std::find_if(managedObj.begin(), managedObj.end(),
1056 [&name](const auto& obj) {
1057 return boost::algorithm::ends_with(
James Feistb6baeaa2019-02-21 10:41:40 -08001058 obj.first.str, "/" + name);
James Feist83ff9ab2018-08-31 10:18:24 -07001059 });
1060 boost::container::flat_map<
1061 std::string, dbus::utility::DbusVariantType>
1062 output;
1063
1064 output.reserve(16); // The pid interface length
1065
1066 // determines if we're patching entity-manager or
1067 // creating a new object
1068 bool createNewObject = (pathItr == managedObj.end());
James Feist5f2caae2018-12-12 14:08:25 -08001069 std::string iface;
1070 if (type == "PidControllers" ||
1071 type == "FanControllers")
James Feist83ff9ab2018-08-31 10:18:24 -07001072 {
James Feist5f2caae2018-12-12 14:08:25 -08001073 iface = pidConfigurationIface;
James Feist83ff9ab2018-08-31 10:18:24 -07001074 if (!createNewObject &&
1075 pathItr->second.find(pidConfigurationIface) ==
1076 pathItr->second.end())
1077 {
1078 createNewObject = true;
1079 }
1080 }
James Feist5f2caae2018-12-12 14:08:25 -08001081 else if (type == "FanZones")
James Feist83ff9ab2018-08-31 10:18:24 -07001082 {
James Feist5f2caae2018-12-12 14:08:25 -08001083 iface = pidZoneConfigurationIface;
1084 if (!createNewObject &&
1085 pathItr->second.find(
1086 pidZoneConfigurationIface) ==
1087 pathItr->second.end())
1088 {
1089
1090 createNewObject = true;
1091 }
1092 }
1093 else if (type == "StepwiseControllers")
1094 {
1095 iface = stepwiseConfigurationIface;
1096 if (!createNewObject &&
1097 pathItr->second.find(
1098 stepwiseConfigurationIface) ==
1099 pathItr->second.end())
1100 {
1101 createNewObject = true;
1102 }
James Feist83ff9ab2018-08-31 10:18:24 -07001103 }
James Feistb6baeaa2019-02-21 10:41:40 -08001104 BMCWEB_LOG_DEBUG << "Create new = " << createNewObject
1105 << "\n";
James Feist83ff9ab2018-08-31 10:18:24 -07001106 output["Name"] =
1107 boost::replace_all_copy(name, "_", " ");
1108
1109 std::string chassis;
1110 CreatePIDRet ret = createPidInterface(
James Feistb6baeaa2019-02-21 10:41:40 -08001111 response, type, it, pathItr->first.str, managedObj,
1112 createNewObject, output, chassis);
James Feist83ff9ab2018-08-31 10:18:24 -07001113 if (ret == CreatePIDRet::fail)
1114 {
1115 return;
1116 }
1117 else if (ret == CreatePIDRet::del)
1118 {
1119 continue;
1120 }
1121
1122 if (!createNewObject)
1123 {
1124 for (const auto& property : output)
1125 {
James Feist83ff9ab2018-08-31 10:18:24 -07001126 crow::connections::systemBus->async_method_call(
1127 [response,
1128 propertyName{std::string(property.first)}](
1129 const boost::system::error_code ec) {
1130 if (ec)
1131 {
1132 BMCWEB_LOG_ERROR
1133 << "Error patching "
1134 << propertyName << ": " << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -07001135 messages::internalError(
1136 response->res);
James Feistb6baeaa2019-02-21 10:41:40 -08001137 return;
James Feist83ff9ab2018-08-31 10:18:24 -07001138 }
James Feistb6baeaa2019-02-21 10:41:40 -08001139 messages::success(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -07001140 },
1141 "xyz.openbmc_project.EntityManager",
1142 pathItr->first.str,
1143 "org.freedesktop.DBus.Properties", "Set",
James Feist5f2caae2018-12-12 14:08:25 -08001144 iface, property.first, property.second);
James Feist83ff9ab2018-08-31 10:18:24 -07001145 }
1146 }
1147 else
1148 {
1149 if (chassis.empty())
1150 {
1151 BMCWEB_LOG_ERROR
1152 << "Failed to get chassis from config";
Jason M. Bills35a62c72018-10-09 12:45:45 -07001153 messages::invalidObject(response->res, name);
James Feist83ff9ab2018-08-31 10:18:24 -07001154 return;
1155 }
1156
1157 bool foundChassis = false;
1158 for (const auto& obj : managedObj)
1159 {
1160 if (boost::algorithm::ends_with(obj.first.str,
1161 chassis))
1162 {
1163 chassis = obj.first.str;
1164 foundChassis = true;
1165 break;
1166 }
1167 }
1168 if (!foundChassis)
1169 {
1170 BMCWEB_LOG_ERROR
1171 << "Failed to find chassis on dbus";
Jason M. Bills35a62c72018-10-09 12:45:45 -07001172 messages::resourceMissingAtURI(
1173 response->res,
1174 "/redfish/v1/Chassis/" + chassis);
James Feist83ff9ab2018-08-31 10:18:24 -07001175 return;
1176 }
1177
1178 crow::connections::systemBus->async_method_call(
1179 [response](const boost::system::error_code ec) {
1180 if (ec)
1181 {
1182 BMCWEB_LOG_ERROR
1183 << "Error Adding Pid Object " << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -07001184 messages::internalError(response->res);
James Feistb6baeaa2019-02-21 10:41:40 -08001185 return;
James Feist83ff9ab2018-08-31 10:18:24 -07001186 }
James Feistb6baeaa2019-02-21 10:41:40 -08001187 messages::success(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -07001188 },
1189 "xyz.openbmc_project.EntityManager", chassis,
1190 "xyz.openbmc_project.AddObject", "AddObject",
1191 output);
1192 }
1193 }
1194 }
1195 },
1196 "xyz.openbmc_project.EntityManager", "/", objectManagerIface,
1197 "GetManagedObjects");
1198 }
James Feist5b4aa862018-08-16 14:07:01 -07001199
1200 void doPatch(crow::Response& res, const crow::Request& req,
1201 const std::vector<std::string>& params) override
1202 {
Ed Tanous0627a2c2018-11-29 17:09:23 -08001203 std::optional<nlohmann::json> oem;
1204
1205 if (!json_util::readJson(req, res, "Oem", oem))
James Feist83ff9ab2018-08-31 10:18:24 -07001206 {
1207 return;
1208 }
Ed Tanous0627a2c2018-11-29 17:09:23 -08001209
James Feist83ff9ab2018-08-31 10:18:24 -07001210 std::shared_ptr<AsyncResp> response = std::make_shared<AsyncResp>(res);
Ed Tanous0627a2c2018-11-29 17:09:23 -08001211
1212 if (oem)
James Feist83ff9ab2018-08-31 10:18:24 -07001213 {
Ed Tanous43b761d2019-02-13 20:10:56 -08001214 std::optional<nlohmann::json> openbmc;
1215 if (!redfish::json_util::readJson(*oem, res, "OpenBmc", openbmc))
James Feist83ff9ab2018-08-31 10:18:24 -07001216 {
Ed Tanous43b761d2019-02-13 20:10:56 -08001217 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
1218 << oem->dump();
1219 return;
1220 }
1221 if (openbmc)
1222 {
1223 std::optional<nlohmann::json> fan;
1224 if (!redfish::json_util::readJson(*openbmc, res, "Fan", fan))
James Feist83ff9ab2018-08-31 10:18:24 -07001225 {
James Feist5f2caae2018-12-12 14:08:25 -08001226 BMCWEB_LOG_ERROR << "Line:" << __LINE__
Ed Tanous43b761d2019-02-13 20:10:56 -08001227 << ", Illegal Property "
1228 << openbmc->dump();
James Feist5f2caae2018-12-12 14:08:25 -08001229 return;
1230 }
Ed Tanous43b761d2019-02-13 20:10:56 -08001231 if (fan)
James Feist5f2caae2018-12-12 14:08:25 -08001232 {
Ed Tanous43b761d2019-02-13 20:10:56 -08001233 setPidValues(response, *fan);
James Feist83ff9ab2018-08-31 10:18:24 -07001234 }
James Feist83ff9ab2018-08-31 10:18:24 -07001235 }
1236 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001237 }
1238
1239 std::string getDateTime() const
1240 {
1241 std::array<char, 128> dateTime;
1242 std::string redfishDateTime("0000-00-00T00:00:00Z00:00");
1243 std::time_t time = std::time(nullptr);
1244
1245 if (std::strftime(dateTime.begin(), dateTime.size(), "%FT%T%z",
1246 std::localtime(&time)))
1247 {
1248 // insert the colon required by the ISO 8601 standard
1249 redfishDateTime = std::string(dateTime.data());
1250 redfishDateTime.insert(redfishDateTime.end() - 2, ':');
1251 }
1252
1253 return redfishDateTime;
1254 }
Ed Tanous0f74e642018-11-12 15:17:05 -08001255
1256 std::string uuid;
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001257};
1258
Ed Tanous1abe55e2018-09-05 08:30:59 -07001259class ManagerCollection : public Node
1260{
1261 public:
James Feist5b4aa862018-08-16 14:07:01 -07001262 ManagerCollection(CrowApp& app) : Node(app, "/redfish/v1/Managers/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001263 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001264 entityPrivileges = {
1265 {boost::beast::http::verb::get, {{"Login"}}},
1266 {boost::beast::http::verb::head, {{"Login"}}},
1267 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1268 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1269 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1270 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1271 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001272
Ed Tanous1abe55e2018-09-05 08:30:59 -07001273 private:
James Feist5b4aa862018-08-16 14:07:01 -07001274 void doGet(crow::Response& res, const crow::Request& req,
1275 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -07001276 {
James Feist83ff9ab2018-08-31 10:18:24 -07001277 // Collections don't include the static data added by SubRoute
1278 // because it has a duplicate entry for members
Ed Tanous1abe55e2018-09-05 08:30:59 -07001279 res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
1280 res.jsonValue["@odata.type"] = "#ManagerCollection.ManagerCollection";
1281 res.jsonValue["@odata.context"] =
1282 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection";
1283 res.jsonValue["Name"] = "Manager Collection";
1284 res.jsonValue["Members@odata.count"] = 1;
1285 res.jsonValue["Members"] = {
James Feist5b4aa862018-08-16 14:07:01 -07001286 {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001287 res.end();
1288 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001289};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001290} // namespace redfish