blob: 33ac72c7d47c2837aaa1adce95f4c8657d59baad [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
169 bool propertyError = false;
170 for (const auto& pathPair : managedObj)
171 {
172 for (const auto& intfPair : pathPair.second)
173 {
174 if (intfPair.first != pidConfigurationIface &&
James Feistb7a08d02018-12-11 14:55:37 -0800175 intfPair.first != pidZoneConfigurationIface &&
176 intfPair.first != stepwiseConfigurationIface)
James Feist5b4aa862018-08-16 14:07:01 -0700177 {
178 continue;
179 }
180 auto findName = intfPair.second.find("Name");
181 if (findName == intfPair.second.end())
182 {
183 BMCWEB_LOG_ERROR << "Pid Field missing Name";
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800184 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700185 return;
186 }
187 const std::string* namePtr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800188 std::get_if<std::string>(&findName->second);
James Feist5b4aa862018-08-16 14:07:01 -0700189 if (namePtr == nullptr)
190 {
191 BMCWEB_LOG_ERROR << "Pid Name Field illegal";
James Feistb7a08d02018-12-11 14:55:37 -0800192 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700193 return;
194 }
195
196 std::string name = *namePtr;
197 dbus::utility::escapePathForDbus(name);
James Feistb7a08d02018-12-11 14:55:37 -0800198 nlohmann::json* config = nullptr;
James Feist5b4aa862018-08-16 14:07:01 -0700199 if (intfPair.first == pidZoneConfigurationIface)
200 {
201 std::string chassis;
202 if (!dbus::utility::getNthStringFromPath(
203 pathPair.first.str, 5, chassis))
204 {
205 chassis = "#IllegalValue";
206 }
207 nlohmann::json& zone = zones[name];
208 zone["Chassis"] = {
209 {"@odata.id", "/redfish/v1/Chassis/" + chassis}};
210 zone["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/"
211 "OpenBmc/Fan/FanZones/" +
212 name;
213 zone["@odata.type"] = "#OemManager.FanZone";
214 zone["@odata.context"] =
215 "/redfish/v1/$metadata#OemManager.FanZone";
James Feistb7a08d02018-12-11 14:55:37 -0800216 config = &zone;
James Feist5b4aa862018-08-16 14:07:01 -0700217 }
218
James Feistb7a08d02018-12-11 14:55:37 -0800219 else if (intfPair.first == stepwiseConfigurationIface)
220 {
221 nlohmann::json& controller = stepwise[name];
222 config = &controller;
223
224 controller["@odata.id"] =
225 "/redfish/v1/Managers/bmc#/Oem/"
226 "OpenBmc/Fan/StepwiseControllers/" +
227 std::string(name);
228 controller["@odata.type"] =
229 "#OemManager.StepwiseController";
230
231 controller["@odata.context"] =
232 "/redfish/v1/"
233 "$metadata#OemManager.StepwiseController";
234 }
235
236 // pid and fans are off the same configuration
237 else if (intfPair.first == pidConfigurationIface)
238 {
239 const std::string* classPtr = nullptr;
240 auto findClass = intfPair.second.find("Class");
241 if (findClass != intfPair.second.end())
242 {
Ed Tanousabf2add2019-01-22 16:40:12 -0800243 classPtr =
244 std::get_if<std::string>(&findClass->second);
James Feistb7a08d02018-12-11 14:55:37 -0800245 }
246 if (classPtr == nullptr)
247 {
248 BMCWEB_LOG_ERROR << "Pid Class Field illegal";
249 messages::internalError(asyncResp->res);
250 return;
251 }
252 bool isFan = *classPtr == "fan";
253 nlohmann::json& element =
254 isFan ? fans[name] : pids[name];
255 config = &element;
256 if (isFan)
257 {
258 element["@odata.id"] =
259 "/redfish/v1/Managers/bmc#/Oem/"
260 "OpenBmc/Fan/FanControllers/" +
261 std::string(name);
262 element["@odata.type"] =
263 "#OemManager.FanController";
264
265 element["@odata.context"] =
266 "/redfish/v1/"
267 "$metadata#OemManager.FanController";
268 }
269 else
270 {
271 element["@odata.id"] =
272 "/redfish/v1/Managers/bmc#/Oem/"
273 "OpenBmc/Fan/PidControllers/" +
274 std::string(name);
275 element["@odata.type"] =
276 "#OemManager.PidController";
277 element["@odata.context"] =
278 "/redfish/v1/$metadata"
279 "#OemManager.PidController";
280 }
281 }
282 else
283 {
284 BMCWEB_LOG_ERROR << "Unexpected configuration";
285 messages::internalError(asyncResp->res);
286 return;
287 }
288
289 // used for making maps out of 2 vectors
290 const std::vector<double>* keys = nullptr;
291 const std::vector<double>* values = nullptr;
292
James Feist5b4aa862018-08-16 14:07:01 -0700293 for (const auto& propertyPair : intfPair.second)
294 {
295 if (propertyPair.first == "Type" ||
296 propertyPair.first == "Class" ||
297 propertyPair.first == "Name")
298 {
299 continue;
300 }
301
302 // zones
303 if (intfPair.first == pidZoneConfigurationIface)
304 {
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800305 const double* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800306 std::get_if<double>(&propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700307 if (ptr == nullptr)
308 {
309 BMCWEB_LOG_ERROR << "Field Illegal "
310 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700311 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700312 return;
313 }
James Feistb7a08d02018-12-11 14:55:37 -0800314 (*config)[propertyPair.first] = *ptr;
315 }
316
317 if (intfPair.first == stepwiseConfigurationIface)
318 {
319 if (propertyPair.first == "Reading" ||
320 propertyPair.first == "Output")
321 {
322 const std::vector<double>* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800323 std::get_if<std::vector<double>>(
James Feistb7a08d02018-12-11 14:55:37 -0800324 &propertyPair.second);
325
326 if (ptr == nullptr)
327 {
328 BMCWEB_LOG_ERROR << "Field Illegal "
329 << propertyPair.first;
330 messages::internalError(asyncResp->res);
331 return;
332 }
333
334 if (propertyPair.first == "Reading")
335 {
336 keys = ptr;
337 }
338 else
339 {
340 values = ptr;
341 }
342 if (keys && values)
343 {
344 if (keys->size() != values->size())
345 {
346 BMCWEB_LOG_ERROR
347 << "Reading and Output size don't "
348 "match ";
349 messages::internalError(asyncResp->res);
350 return;
351 }
352 nlohmann::json& steps = (*config)["Steps"];
353 steps = nlohmann::json::array();
354 for (size_t ii = 0; ii < keys->size(); ii++)
355 {
356 steps.push_back(
357 {{"Target", (*keys)[ii]},
358 {"Output", (*values)[ii]}});
359 }
360 }
361 }
362 if (propertyPair.first == "NegativeHysteresis" ||
363 propertyPair.first == "PositiveHysteresis")
364 {
365 const double* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800366 std::get_if<double>(&propertyPair.second);
James Feistb7a08d02018-12-11 14:55:37 -0800367 if (ptr == nullptr)
368 {
369 BMCWEB_LOG_ERROR << "Field Illegal "
370 << propertyPair.first;
371 messages::internalError(asyncResp->res);
372 return;
373 }
374 (*config)[propertyPair.first] = *ptr;
375 }
James Feist5b4aa862018-08-16 14:07:01 -0700376 }
377
378 // pid and fans are off the same configuration
James Feistb7a08d02018-12-11 14:55:37 -0800379 if (intfPair.first == pidConfigurationIface ||
380 intfPair.first == stepwiseConfigurationIface)
James Feist5b4aa862018-08-16 14:07:01 -0700381 {
James Feist5b4aa862018-08-16 14:07:01 -0700382
383 if (propertyPair.first == "Zones")
384 {
385 const std::vector<std::string>* inputs =
Ed Tanousabf2add2019-01-22 16:40:12 -0800386 std::get_if<std::vector<std::string>>(
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800387 &propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700388
389 if (inputs == nullptr)
390 {
391 BMCWEB_LOG_ERROR
392 << "Zones Pid Field Illegal";
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800393 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700394 return;
395 }
James Feistb7a08d02018-12-11 14:55:37 -0800396 auto& data = (*config)[propertyPair.first];
James Feist5b4aa862018-08-16 14:07:01 -0700397 data = nlohmann::json::array();
398 for (std::string itemCopy : *inputs)
399 {
400 dbus::utility::escapePathForDbus(itemCopy);
401 data.push_back(
402 {{"@odata.id",
403 "/redfish/v1/Managers/bmc#/Oem/"
404 "OpenBmc/Fan/FanZones/" +
405 itemCopy}});
406 }
407 }
408 // todo(james): may never happen, but this
409 // assumes configuration data referenced in the
410 // PID config is provided by the same daemon, we
411 // could add another loop to cover all cases,
412 // but I'm okay kicking this can down the road a
413 // bit
414
415 else if (propertyPair.first == "Inputs" ||
416 propertyPair.first == "Outputs")
417 {
James Feistb7a08d02018-12-11 14:55:37 -0800418 auto& data = (*config)[propertyPair.first];
James Feist5b4aa862018-08-16 14:07:01 -0700419 const std::vector<std::string>* inputs =
Ed Tanousabf2add2019-01-22 16:40:12 -0800420 std::get_if<std::vector<std::string>>(
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800421 &propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700422
423 if (inputs == nullptr)
424 {
425 BMCWEB_LOG_ERROR << "Field Illegal "
426 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700427 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700428 return;
429 }
430 data = *inputs;
431 } // doubles
432 else if (propertyPair.first ==
433 "FFGainCoefficient" ||
434 propertyPair.first == "FFOffCoefficient" ||
435 propertyPair.first == "ICoefficient" ||
436 propertyPair.first == "ILimitMax" ||
437 propertyPair.first == "ILimitMin" ||
438 propertyPair.first == "OutLimitMax" ||
439 propertyPair.first == "OutLimitMin" ||
440 propertyPair.first == "PCoefficient" ||
James Feist7625cb82019-01-23 11:58:21 -0800441 propertyPair.first == "SetPoint" ||
James Feist5b4aa862018-08-16 14:07:01 -0700442 propertyPair.first == "SlewNeg" ||
443 propertyPair.first == "SlewPos")
444 {
445 const double* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800446 std::get_if<double>(&propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700447 if (ptr == nullptr)
448 {
449 BMCWEB_LOG_ERROR << "Field Illegal "
450 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700451 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700452 return;
453 }
James Feistb7a08d02018-12-11 14:55:37 -0800454 (*config)[propertyPair.first] = *ptr;
James Feist5b4aa862018-08-16 14:07:01 -0700455 }
456 }
457 }
458 }
459 }
460 },
461 connection, path, objectManagerIface, "GetManagedObjects");
462}
Jennifer Leeca537922018-08-10 10:07:30 -0700463
James Feist83ff9ab2018-08-31 10:18:24 -0700464enum class CreatePIDRet
465{
466 fail,
467 del,
468 patch
469};
470
James Feist5f2caae2018-12-12 14:08:25 -0800471static bool getZonesFromJsonReq(const std::shared_ptr<AsyncResp>& response,
472 std::vector<nlohmann::json>& config,
473 std::vector<std::string>& zones)
474{
475
476 for (auto& odata : config)
477 {
478 std::string path;
479 if (!redfish::json_util::readJson(odata, response->res, "@odata.id",
480 path))
481 {
482 return false;
483 }
484 std::string input;
485 if (!dbus::utility::getNthStringFromPath(path, 4, input))
486 {
487 BMCWEB_LOG_ERROR << "Got invalid path " << path;
488 BMCWEB_LOG_ERROR << "Illegal Type Zones";
489 messages::propertyValueFormatError(response->res, odata.dump(),
490 "Zones");
491 return false;
492 }
493 boost::replace_all(input, "_", " ");
494 zones.emplace_back(std::move(input));
495 }
496 return true;
497}
498
James Feist83ff9ab2018-08-31 10:18:24 -0700499static CreatePIDRet createPidInterface(
500 const std::shared_ptr<AsyncResp>& response, const std::string& type,
James Feist5f2caae2018-12-12 14:08:25 -0800501 nlohmann::json&& record, const std::string& path,
James Feist83ff9ab2018-08-31 10:18:24 -0700502 const dbus::utility::ManagedObjectType& managedObj, bool createNewObject,
503 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>&
504 output,
505 std::string& chassis)
506{
507
James Feist5f2caae2018-12-12 14:08:25 -0800508 // common deleter
509 if (record == nullptr)
510 {
511 std::string iface;
512 if (type == "PidControllers" || type == "FanControllers")
513 {
514 iface = pidConfigurationIface;
515 }
516 else if (type == "FanZones")
517 {
518 iface = pidZoneConfigurationIface;
519 }
520 else if (type == "StepwiseControllers")
521 {
522 iface = stepwiseConfigurationIface;
523 }
524 else
525 {
526 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Type "
527 << type;
528 messages::propertyUnknown(response->res, type);
529 return CreatePIDRet::fail;
530 }
531 // delete interface
532 crow::connections::systemBus->async_method_call(
533 [response, path](const boost::system::error_code ec) {
534 if (ec)
535 {
536 BMCWEB_LOG_ERROR << "Error patching " << path << ": " << ec;
537 messages::internalError(response->res);
538 }
539 },
540 "xyz.openbmc_project.EntityManager", path, iface, "Delete");
541 return CreatePIDRet::del;
542 }
543
James Feist83ff9ab2018-08-31 10:18:24 -0700544 if (type == "PidControllers" || type == "FanControllers")
545 {
546 if (createNewObject)
547 {
548 output["Class"] = type == "PidControllers" ? std::string("temp")
549 : std::string("fan");
550 output["Type"] = std::string("Pid");
551 }
James Feist5f2caae2018-12-12 14:08:25 -0800552
553 std::optional<std::vector<nlohmann::json>> zones;
554 std::optional<std::vector<std::string>> inputs;
555 std::optional<std::vector<std::string>> outputs;
556 std::map<std::string, std::optional<double>> doubles;
557 if (!redfish::json_util::readJson(
558 record, response->res, "Inputs", inputs, "Outputs", outputs,
559 "Zones", zones, "FFGainCoefficient",
560 doubles["FFGainCoefficient"], "FFOffCoefficient",
561 doubles["FFOffCoefficient"], "ICoefficient",
562 doubles["ICoefficient"], "ILimitMax", doubles["ILimitMax"],
563 "ILimitMin", doubles["ILimitMin"], "OutLimitMax",
564 doubles["OutLimitMax"], "OutLimitMin", doubles["OutLimitMin"],
565 "PCoefficient", doubles["PCoefficient"], "SetPoint",
566 doubles["SetPoint"], "SlewNeg", doubles["SlewNeg"], "SlewPos",
567 doubles["SlewPos"]))
James Feist83ff9ab2018-08-31 10:18:24 -0700568 {
James Feist5f2caae2018-12-12 14:08:25 -0800569 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
570 << record.dump();
571 return CreatePIDRet::fail;
James Feist83ff9ab2018-08-31 10:18:24 -0700572 }
James Feist5f2caae2018-12-12 14:08:25 -0800573 if (zones)
James Feist83ff9ab2018-08-31 10:18:24 -0700574 {
James Feist5f2caae2018-12-12 14:08:25 -0800575 std::vector<std::string> zonesStr;
576 if (!getZonesFromJsonReq(response, *zones, zonesStr))
James Feist83ff9ab2018-08-31 10:18:24 -0700577 {
James Feist5f2caae2018-12-12 14:08:25 -0800578 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Zones";
579 return CreatePIDRet::fail;
James Feist83ff9ab2018-08-31 10:18:24 -0700580 }
James Feist5f2caae2018-12-12 14:08:25 -0800581 output["Zones"] = std::move(zonesStr);
582 }
583 if (inputs || outputs)
584 {
585 std::array<std::optional<std::vector<std::string>>*, 2> containers =
586 {&inputs, &outputs};
587 size_t index = 0;
588 for (const auto& containerPtr : containers)
James Feist83ff9ab2018-08-31 10:18:24 -0700589 {
James Feist5f2caae2018-12-12 14:08:25 -0800590 std::optional<std::vector<std::string>>& container =
591 *containerPtr;
592 if (!container)
James Feist83ff9ab2018-08-31 10:18:24 -0700593 {
James Feist5f2caae2018-12-12 14:08:25 -0800594 index++;
595 continue;
James Feist83ff9ab2018-08-31 10:18:24 -0700596 }
James Feist5f2caae2018-12-12 14:08:25 -0800597
598 for (std::string& value : *container)
James Feist83ff9ab2018-08-31 10:18:24 -0700599 {
James Feist83ff9ab2018-08-31 10:18:24 -0700600
James Feist83ff9ab2018-08-31 10:18:24 -0700601 // try to find the sensor in the
602 // configuration
603 if (chassis.empty())
604 {
James Feist5f2caae2018-12-12 14:08:25 -0800605 std::string escaped =
606 boost::replace_all_copy(value, " ", "_");
James Feist83ff9ab2018-08-31 10:18:24 -0700607 std::find_if(
608 managedObj.begin(), managedObj.end(),
James Feist5f2caae2018-12-12 14:08:25 -0800609 [&chassis, &escaped](const auto& obj) {
James Feist83ff9ab2018-08-31 10:18:24 -0700610 if (boost::algorithm::ends_with(obj.first.str,
James Feist5f2caae2018-12-12 14:08:25 -0800611 escaped))
James Feist83ff9ab2018-08-31 10:18:24 -0700612 {
613 return dbus::utility::getNthStringFromPath(
614 obj.first.str, 5, chassis);
615 }
616 return false;
617 });
618 }
James Feist5f2caae2018-12-12 14:08:25 -0800619 boost::replace_all(value, "_", " ");
James Feist83ff9ab2018-08-31 10:18:24 -0700620 }
James Feist5f2caae2018-12-12 14:08:25 -0800621 std::string key;
622 if (index == 0)
James Feist83ff9ab2018-08-31 10:18:24 -0700623 {
James Feist5f2caae2018-12-12 14:08:25 -0800624 key = "Inputs";
James Feist83ff9ab2018-08-31 10:18:24 -0700625 }
James Feist5f2caae2018-12-12 14:08:25 -0800626 else
627 {
628 key = "Outputs";
629 }
630 output[key] = *container;
631 index++;
James Feist83ff9ab2018-08-31 10:18:24 -0700632 }
James Feist5f2caae2018-12-12 14:08:25 -0800633 }
James Feist83ff9ab2018-08-31 10:18:24 -0700634
James Feist5f2caae2018-12-12 14:08:25 -0800635 // doubles
636 for (const auto& pairs : doubles)
637 {
638 if (!pairs.second)
James Feist83ff9ab2018-08-31 10:18:24 -0700639 {
James Feist5f2caae2018-12-12 14:08:25 -0800640 continue;
James Feist83ff9ab2018-08-31 10:18:24 -0700641 }
James Feist5f2caae2018-12-12 14:08:25 -0800642 BMCWEB_LOG_DEBUG << pairs.first << " = " << *pairs.second;
643 output[pairs.first] = *(pairs.second);
James Feist83ff9ab2018-08-31 10:18:24 -0700644 }
645 }
James Feist5f2caae2018-12-12 14:08:25 -0800646
James Feist83ff9ab2018-08-31 10:18:24 -0700647 else if (type == "FanZones")
648 {
James Feist83ff9ab2018-08-31 10:18:24 -0700649 output["Type"] = std::string("Pid.Zone");
650
James Feist5f2caae2018-12-12 14:08:25 -0800651 std::optional<nlohmann::json> chassisContainer;
652 std::optional<double> failSafePercent;
653 std::optional<double> minThermalRpm;
654 if (!redfish::json_util::readJson(record, response->res, "Chassis",
655 chassisContainer, "FailSafePercent",
656 failSafePercent, "MinThermalRpm",
657 minThermalRpm))
James Feist83ff9ab2018-08-31 10:18:24 -0700658 {
James Feist5f2caae2018-12-12 14:08:25 -0800659 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
660 << record.dump();
661 return CreatePIDRet::fail;
662 }
James Feist83ff9ab2018-08-31 10:18:24 -0700663
James Feist5f2caae2018-12-12 14:08:25 -0800664 if (chassisContainer)
665 {
666
667 std::string chassisId;
668 if (!redfish::json_util::readJson(*chassisContainer, response->res,
669 "@odata.id", chassisId))
James Feist83ff9ab2018-08-31 10:18:24 -0700670 {
James Feist5f2caae2018-12-12 14:08:25 -0800671 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
672 << chassisContainer->dump();
James Feist83ff9ab2018-08-31 10:18:24 -0700673 return CreatePIDRet::fail;
674 }
James Feist5f2caae2018-12-12 14:08:25 -0800675
676 // /refish/v1/chassis/chassis_name/
677 if (!dbus::utility::getNthStringFromPath(chassisId, 3, chassis))
678 {
679 BMCWEB_LOG_ERROR << "Got invalid path " << chassisId;
680 messages::invalidObject(response->res, chassisId);
681 return CreatePIDRet::fail;
682 }
683 }
684 if (minThermalRpm)
685 {
686 output["MinThermalRpm"] = *minThermalRpm;
687 }
688 if (failSafePercent)
689 {
690 output["FailSafePercent"] = *failSafePercent;
691 }
692 }
693 else if (type == "StepwiseControllers")
694 {
695 output["Type"] = std::string("Stepwise");
696
697 std::optional<std::vector<nlohmann::json>> zones;
698 std::optional<std::vector<nlohmann::json>> steps;
699 std::optional<std::vector<std::string>> inputs;
700 std::optional<double> positiveHysteresis;
701 std::optional<double> negativeHysteresis;
702 if (!redfish::json_util::readJson(
703 record, response->res, "Zones", zones, "Steps", steps, "Inputs",
704 inputs, "PositiveHysteresis", positiveHysteresis,
705 "NegativeHysteresis", negativeHysteresis))
706 {
707 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
708 << record.dump();
709 return CreatePIDRet::fail;
710 }
711
712 if (zones)
713 {
714 std::vector<std::string> zoneStrs;
715 if (!getZonesFromJsonReq(response, *zones, zoneStrs))
716 {
717 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Zones";
718 return CreatePIDRet::fail;
719 }
720 output["Zones"] = std::move(zoneStrs);
721 }
722 if (steps)
723 {
724 std::vector<double> readings;
725 std::vector<double> outputs;
726 for (auto& step : *steps)
727 {
728 double target;
729 double output;
730
731 if (!redfish::json_util::readJson(step, response->res, "Target",
732 target, "Output", output))
733 {
734 BMCWEB_LOG_ERROR << "Line:" << __LINE__
735 << ", Illegal Property " << record.dump();
736 return CreatePIDRet::fail;
737 }
738 readings.emplace_back(target);
739 outputs.emplace_back(output);
740 }
741 output["Reading"] = std::move(readings);
742 output["Output"] = std::move(outputs);
743 }
744 if (inputs)
745 {
746 for (std::string& value : *inputs)
747 {
748 if (chassis.empty())
749 {
750 std::string escaped =
751 boost::replace_all_copy(value, " ", "_");
752 std::find_if(
753 managedObj.begin(), managedObj.end(),
754 [&chassis, &escaped](const auto& obj) {
755 if (boost::algorithm::ends_with(obj.first.str,
756 escaped))
757 {
758 return dbus::utility::getNthStringFromPath(
759 obj.first.str, 5, chassis);
760 }
761 return false;
762 });
763 }
764 boost::replace_all(value, "_", " ");
765 }
766 output["Inputs"] = std::move(*inputs);
767 }
768 if (negativeHysteresis)
769 {
770 output["NegativeHysteresis"] = *negativeHysteresis;
771 }
772 if (positiveHysteresis)
773 {
774 output["PositiveHysteresis"] = *positiveHysteresis;
James Feist83ff9ab2018-08-31 10:18:24 -0700775 }
776 }
777 else
778 {
James Feist5f2caae2018-12-12 14:08:25 -0800779 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Type " << type;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700780 messages::propertyUnknown(response->res, type);
James Feist83ff9ab2018-08-31 10:18:24 -0700781 return CreatePIDRet::fail;
782 }
783 return CreatePIDRet::patch;
784}
785
Ed Tanous1abe55e2018-09-05 08:30:59 -0700786class Manager : public Node
787{
788 public:
James Feist5b4aa862018-08-16 14:07:01 -0700789 Manager(CrowApp& app) : Node(app, "/redfish/v1/Managers/bmc/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700790 {
Ed Tanous0f74e642018-11-12 15:17:05 -0800791 uuid = app.template getMiddleware<crow::persistent_data::Middleware>()
792 .systemUuid;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700793 entityPrivileges = {
794 {boost::beast::http::verb::get, {{"Login"}}},
795 {boost::beast::http::verb::head, {{"Login"}}},
796 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
797 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
798 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
799 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100800 }
801
Ed Tanous1abe55e2018-09-05 08:30:59 -0700802 private:
James Feist5b4aa862018-08-16 14:07:01 -0700803 void getPidValues(std::shared_ptr<AsyncResp> asyncResp)
804 {
805 crow::connections::systemBus->async_method_call(
806 [asyncResp](const boost::system::error_code ec,
807 const crow::openbmc_mapper::GetSubTreeType& subtree) {
808 if (ec)
809 {
810 BMCWEB_LOG_ERROR << ec;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700811 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700812 return;
813 }
814
815 // create map of <connection, path to objMgr>>
816 boost::container::flat_map<std::string, std::string>
817 objectMgrPaths;
James Feist6bce33b2018-10-22 12:05:56 -0700818 boost::container::flat_set<std::string> calledConnections;
James Feist5b4aa862018-08-16 14:07:01 -0700819 for (const auto& pathGroup : subtree)
820 {
821 for (const auto& connectionGroup : pathGroup.second)
822 {
James Feist6bce33b2018-10-22 12:05:56 -0700823 auto findConnection =
824 calledConnections.find(connectionGroup.first);
825 if (findConnection != calledConnections.end())
826 {
827 break;
828 }
James Feist5b4aa862018-08-16 14:07:01 -0700829 for (const std::string& interface :
830 connectionGroup.second)
831 {
832 if (interface == objectManagerIface)
833 {
834 objectMgrPaths[connectionGroup.first] =
835 pathGroup.first;
836 }
837 // this list is alphabetical, so we
838 // should have found the objMgr by now
839 if (interface == pidConfigurationIface ||
James Feistb7a08d02018-12-11 14:55:37 -0800840 interface == pidZoneConfigurationIface ||
841 interface == stepwiseConfigurationIface)
James Feist5b4aa862018-08-16 14:07:01 -0700842 {
843 auto findObjMgr =
844 objectMgrPaths.find(connectionGroup.first);
845 if (findObjMgr == objectMgrPaths.end())
846 {
847 BMCWEB_LOG_DEBUG << connectionGroup.first
848 << "Has no Object Manager";
849 continue;
850 }
James Feist6bce33b2018-10-22 12:05:56 -0700851
852 calledConnections.insert(connectionGroup.first);
853
James Feist5b4aa862018-08-16 14:07:01 -0700854 asyncPopulatePid(findObjMgr->first,
855 findObjMgr->second, asyncResp);
856 break;
857 }
858 }
859 }
860 }
861 },
862 "xyz.openbmc_project.ObjectMapper",
863 "/xyz/openbmc_project/object_mapper",
864 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
James Feistb7a08d02018-12-11 14:55:37 -0800865 std::array<const char*, 4>{
866 pidConfigurationIface, pidZoneConfigurationIface,
867 objectManagerIface, stepwiseConfigurationIface});
James Feist5b4aa862018-08-16 14:07:01 -0700868 }
869
870 void doGet(crow::Response& res, const crow::Request& req,
871 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700872 {
Ed Tanous0f74e642018-11-12 15:17:05 -0800873 res.jsonValue["@odata.id"] = "/redfish/v1/Managers/bmc";
874 res.jsonValue["@odata.type"] = "#Manager.v1_3_0.Manager";
875 res.jsonValue["@odata.context"] =
876 "/redfish/v1/$metadata#Manager.Manager";
877 res.jsonValue["Id"] = "bmc";
878 res.jsonValue["Name"] = "OpenBmc Manager";
879 res.jsonValue["Description"] = "Baseboard Management Controller";
880 res.jsonValue["PowerState"] = "On";
881 res.jsonValue["ManagerType"] = "BMC";
Ed Tanous75176582018-12-14 08:14:34 -0800882 res.jsonValue["UUID"] = uuid;
883 res.jsonValue["Model"] = "OpenBmc"; // TODO(ed), get model
Ed Tanous0f74e642018-11-12 15:17:05 -0800884
885 res.jsonValue["LogServices"] = {
886 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices"}};
887
888 res.jsonValue["NetworkProtocol"] = {
889 {"@odata.id", "/redfish/v1/Managers/bmc/NetworkProtocol"}};
890
891 res.jsonValue["EthernetInterfaces"] = {
892 {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces"}};
893 // default oem data
894 nlohmann::json& oem = res.jsonValue["Oem"];
895 nlohmann::json& oemOpenbmc = oem["OpenBmc"];
896 oem["@odata.type"] = "#OemManager.Oem";
897 oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem";
898 oem["@odata.context"] = "/redfish/v1/$metadata#OemManager.Oem";
899 oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc";
900 oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc";
901 oemOpenbmc["@odata.context"] =
902 "/redfish/v1/$metadata#OemManager.OpenBmc";
903
Jennifer Leeed5befb2018-08-10 11:29:45 -0700904 // Update Actions object.
Ed Tanous0f74e642018-11-12 15:17:05 -0800905 nlohmann::json& manager_reset =
906 res.jsonValue["Actions"]["#Manager.Reset"];
Jennifer Leeed5befb2018-08-10 11:29:45 -0700907 manager_reset["target"] =
908 "/redfish/v1/Managers/bmc/Actions/Manager.Reset";
909 manager_reset["ResetType@Redfish.AllowableValues"] = {
910 "GracefulRestart"};
Jennifer Leeca537922018-08-10 10:07:30 -0700911
Ed Tanous0f74e642018-11-12 15:17:05 -0800912 res.jsonValue["DateTime"] = getDateTime();
Jennifer Leeed5befb2018-08-10 11:29:45 -0700913 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
James Feist5b4aa862018-08-16 14:07:01 -0700914
Jennifer Leeca537922018-08-10 10:07:30 -0700915 crow::connections::systemBus->async_method_call(
916 [asyncResp](const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -0700917 const dbus::utility::ManagedObjectType& resp) {
Jennifer Leeca537922018-08-10 10:07:30 -0700918 if (ec)
919 {
920 BMCWEB_LOG_ERROR << "Error while getting Software Version";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700921 messages::internalError(asyncResp->res);
Jennifer Leeca537922018-08-10 10:07:30 -0700922 return;
923 }
924
James Feist5b4aa862018-08-16 14:07:01 -0700925 for (auto& objpath : resp)
Jennifer Leeca537922018-08-10 10:07:30 -0700926 {
James Feist5b4aa862018-08-16 14:07:01 -0700927 for (auto& interface : objpath.second)
Jennifer Leeca537922018-08-10 10:07:30 -0700928 {
James Feist5f2caae2018-12-12 14:08:25 -0800929 // If interface is
930 // xyz.openbmc_project.Software.Version, this is
931 // what we're looking for.
Jennifer Leeca537922018-08-10 10:07:30 -0700932 if (interface.first ==
933 "xyz.openbmc_project.Software.Version")
934 {
935 // Cut out everyting until last "/", ...
James Feist5b4aa862018-08-16 14:07:01 -0700936 const std::string& iface_id = objpath.first;
937 for (auto& property : interface.second)
Jennifer Leeca537922018-08-10 10:07:30 -0700938 {
939 if (property.first == "Version")
940 {
James Feist5b4aa862018-08-16 14:07:01 -0700941 const std::string* value =
Ed Tanousabf2add2019-01-22 16:40:12 -0800942 std::get_if<std::string>(
943 &property.second);
Jennifer Leeca537922018-08-10 10:07:30 -0700944 if (value == nullptr)
945 {
946 continue;
947 }
948 asyncResp->res
949 .jsonValue["FirmwareVersion"] = *value;
950 }
951 }
952 }
953 }
954 }
955 },
956 "xyz.openbmc_project.Software.BMC.Updater",
957 "/xyz/openbmc_project/software",
958 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
James Feist5b4aa862018-08-16 14:07:01 -0700959 getPidValues(asyncResp);
960 }
James Feist5f2caae2018-12-12 14:08:25 -0800961 void setPidValues(std::shared_ptr<AsyncResp> response, nlohmann::json& data)
James Feist83ff9ab2018-08-31 10:18:24 -0700962 {
James Feist5f2caae2018-12-12 14:08:25 -0800963
James Feist83ff9ab2018-08-31 10:18:24 -0700964 // todo(james): might make sense to do a mapper call here if this
965 // interface gets more traction
966 crow::connections::systemBus->async_method_call(
967 [response,
968 data](const boost::system::error_code ec,
969 const dbus::utility::ManagedObjectType& managedObj) {
970 if (ec)
971 {
972 BMCWEB_LOG_ERROR << "Error communicating to Entity Manager";
Jason M. Bills35a62c72018-10-09 12:45:45 -0700973 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700974 return;
975 }
James Feist5f2caae2018-12-12 14:08:25 -0800976
977 // todo(james) mutable doesn't work with asio bindings
978 nlohmann::json jsonData = data;
979
980 std::optional<nlohmann::json> pidControllers;
981 std::optional<nlohmann::json> fanControllers;
982 std::optional<nlohmann::json> fanZones;
983 std::optional<nlohmann::json> stepwiseControllers;
984 if (!redfish::json_util::readJson(
985 jsonData, response->res, "PidControllers",
986 pidControllers, "FanControllers", fanControllers,
987 "FanZones", fanZones, "StepwiseControllers",
988 stepwiseControllers))
James Feist83ff9ab2018-08-31 10:18:24 -0700989 {
James Feist5f2caae2018-12-12 14:08:25 -0800990 BMCWEB_LOG_ERROR << "Line:" << __LINE__
991 << ", Illegal Property "
992 << jsonData.dump();
993 return;
994 }
995 std::array<
996 std::pair<const char*, std::optional<nlohmann::json>*>, 4>
997 sections = {
998 std::make_pair("PidControllers", &pidControllers),
999 std::make_pair("FanControllers", &fanControllers),
1000 std::make_pair("FanZones", &fanZones),
1001 std::make_pair("StepwiseControllers",
1002 &stepwiseControllers)};
1003
1004 for (auto& containerPair : sections)
1005 {
1006 auto& container = *(containerPair.second);
1007 if (!container)
James Feist83ff9ab2018-08-31 10:18:24 -07001008 {
James Feist5f2caae2018-12-12 14:08:25 -08001009 continue;
James Feist83ff9ab2018-08-31 10:18:24 -07001010 }
James Feist5f2caae2018-12-12 14:08:25 -08001011 const char* type = containerPair.first;
1012
1013 for (auto& record : container->items())
James Feist83ff9ab2018-08-31 10:18:24 -07001014 {
James Feist5f2caae2018-12-12 14:08:25 -08001015 const auto& name = record.key();
James Feist83ff9ab2018-08-31 10:18:24 -07001016 auto pathItr =
1017 std::find_if(managedObj.begin(), managedObj.end(),
1018 [&name](const auto& obj) {
1019 return boost::algorithm::ends_with(
1020 obj.first.str, name);
1021 });
1022 boost::container::flat_map<
1023 std::string, dbus::utility::DbusVariantType>
1024 output;
1025
1026 output.reserve(16); // The pid interface length
1027
1028 // determines if we're patching entity-manager or
1029 // creating a new object
1030 bool createNewObject = (pathItr == managedObj.end());
James Feist5f2caae2018-12-12 14:08:25 -08001031 std::string iface;
1032 if (type == "PidControllers" ||
1033 type == "FanControllers")
James Feist83ff9ab2018-08-31 10:18:24 -07001034 {
James Feist5f2caae2018-12-12 14:08:25 -08001035 iface = pidConfigurationIface;
James Feist83ff9ab2018-08-31 10:18:24 -07001036 if (!createNewObject &&
1037 pathItr->second.find(pidConfigurationIface) ==
1038 pathItr->second.end())
1039 {
1040 createNewObject = true;
1041 }
1042 }
James Feist5f2caae2018-12-12 14:08:25 -08001043 else if (type == "FanZones")
James Feist83ff9ab2018-08-31 10:18:24 -07001044 {
James Feist5f2caae2018-12-12 14:08:25 -08001045 iface = pidZoneConfigurationIface;
1046 if (!createNewObject &&
1047 pathItr->second.find(
1048 pidZoneConfigurationIface) ==
1049 pathItr->second.end())
1050 {
1051
1052 createNewObject = true;
1053 }
1054 }
1055 else if (type == "StepwiseControllers")
1056 {
1057 iface = stepwiseConfigurationIface;
1058 if (!createNewObject &&
1059 pathItr->second.find(
1060 stepwiseConfigurationIface) ==
1061 pathItr->second.end())
1062 {
1063 createNewObject = true;
1064 }
James Feist83ff9ab2018-08-31 10:18:24 -07001065 }
1066 output["Name"] =
1067 boost::replace_all_copy(name, "_", " ");
1068
1069 std::string chassis;
1070 CreatePIDRet ret = createPidInterface(
James Feist5f2caae2018-12-12 14:08:25 -08001071 response, type, std::move(record.value()),
James Feist83ff9ab2018-08-31 10:18:24 -07001072 pathItr->first.str, managedObj, createNewObject,
1073 output, chassis);
1074 if (ret == CreatePIDRet::fail)
1075 {
1076 return;
1077 }
1078 else if (ret == CreatePIDRet::del)
1079 {
1080 continue;
1081 }
1082
1083 if (!createNewObject)
1084 {
1085 for (const auto& property : output)
1086 {
James Feist83ff9ab2018-08-31 10:18:24 -07001087 crow::connections::systemBus->async_method_call(
1088 [response,
1089 propertyName{std::string(property.first)}](
1090 const boost::system::error_code ec) {
1091 if (ec)
1092 {
1093 BMCWEB_LOG_ERROR
1094 << "Error patching "
1095 << propertyName << ": " << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -07001096 messages::internalError(
1097 response->res);
James Feist83ff9ab2018-08-31 10:18:24 -07001098 }
1099 },
1100 "xyz.openbmc_project.EntityManager",
1101 pathItr->first.str,
1102 "org.freedesktop.DBus.Properties", "Set",
James Feist5f2caae2018-12-12 14:08:25 -08001103 iface, property.first, property.second);
James Feist83ff9ab2018-08-31 10:18:24 -07001104 }
1105 }
1106 else
1107 {
1108 if (chassis.empty())
1109 {
1110 BMCWEB_LOG_ERROR
1111 << "Failed to get chassis from config";
Jason M. Bills35a62c72018-10-09 12:45:45 -07001112 messages::invalidObject(response->res, name);
James Feist83ff9ab2018-08-31 10:18:24 -07001113 return;
1114 }
1115
1116 bool foundChassis = false;
1117 for (const auto& obj : managedObj)
1118 {
1119 if (boost::algorithm::ends_with(obj.first.str,
1120 chassis))
1121 {
1122 chassis = obj.first.str;
1123 foundChassis = true;
1124 break;
1125 }
1126 }
1127 if (!foundChassis)
1128 {
1129 BMCWEB_LOG_ERROR
1130 << "Failed to find chassis on dbus";
Jason M. Bills35a62c72018-10-09 12:45:45 -07001131 messages::resourceMissingAtURI(
1132 response->res,
1133 "/redfish/v1/Chassis/" + chassis);
James Feist83ff9ab2018-08-31 10:18:24 -07001134 return;
1135 }
1136
1137 crow::connections::systemBus->async_method_call(
1138 [response](const boost::system::error_code ec) {
1139 if (ec)
1140 {
1141 BMCWEB_LOG_ERROR
1142 << "Error Adding Pid Object " << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -07001143 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -07001144 }
1145 },
1146 "xyz.openbmc_project.EntityManager", chassis,
1147 "xyz.openbmc_project.AddObject", "AddObject",
1148 output);
1149 }
1150 }
1151 }
1152 },
1153 "xyz.openbmc_project.EntityManager", "/", objectManagerIface,
1154 "GetManagedObjects");
1155 }
James Feist5b4aa862018-08-16 14:07:01 -07001156
1157 void doPatch(crow::Response& res, const crow::Request& req,
1158 const std::vector<std::string>& params) override
1159 {
Ed Tanous0627a2c2018-11-29 17:09:23 -08001160 std::optional<nlohmann::json> oem;
1161
1162 if (!json_util::readJson(req, res, "Oem", oem))
James Feist83ff9ab2018-08-31 10:18:24 -07001163 {
1164 return;
1165 }
Ed Tanous0627a2c2018-11-29 17:09:23 -08001166
James Feist83ff9ab2018-08-31 10:18:24 -07001167 std::shared_ptr<AsyncResp> response = std::make_shared<AsyncResp>(res);
Ed Tanous0627a2c2018-11-29 17:09:23 -08001168
1169 if (oem)
James Feist83ff9ab2018-08-31 10:18:24 -07001170 {
Ed Tanous0627a2c2018-11-29 17:09:23 -08001171 for (const auto& oemLevel : oem->items())
James Feist83ff9ab2018-08-31 10:18:24 -07001172 {
James Feist5f2caae2018-12-12 14:08:25 -08001173 std::optional<nlohmann::json> openbmc;
1174 if (!redfish::json_util::readJson(*oem, res, "OpenBmc",
1175 openbmc))
James Feist83ff9ab2018-08-31 10:18:24 -07001176 {
James Feist5f2caae2018-12-12 14:08:25 -08001177 BMCWEB_LOG_ERROR << "Line:" << __LINE__
1178 << ", Illegal Property " << oem->dump();
1179 return;
1180 }
1181 if (openbmc)
1182 {
1183 std::optional<nlohmann::json> fan;
1184 if (!redfish::json_util::readJson(*openbmc, res, "Fan",
1185 fan))
James Feist83ff9ab2018-08-31 10:18:24 -07001186 {
James Feist5f2caae2018-12-12 14:08:25 -08001187 BMCWEB_LOG_ERROR << "Line:" << __LINE__
1188 << ", Illegal Property "
1189 << openbmc->dump();
James Feist83ff9ab2018-08-31 10:18:24 -07001190 return;
1191 }
James Feist5f2caae2018-12-12 14:08:25 -08001192 if (fan)
James Feist83ff9ab2018-08-31 10:18:24 -07001193 {
James Feist5f2caae2018-12-12 14:08:25 -08001194 setPidValues(response, *fan);
James Feist83ff9ab2018-08-31 10:18:24 -07001195 }
1196 }
James Feist83ff9ab2018-08-31 10:18:24 -07001197 }
1198 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001199 }
1200
1201 std::string getDateTime() const
1202 {
1203 std::array<char, 128> dateTime;
1204 std::string redfishDateTime("0000-00-00T00:00:00Z00:00");
1205 std::time_t time = std::time(nullptr);
1206
1207 if (std::strftime(dateTime.begin(), dateTime.size(), "%FT%T%z",
1208 std::localtime(&time)))
1209 {
1210 // insert the colon required by the ISO 8601 standard
1211 redfishDateTime = std::string(dateTime.data());
1212 redfishDateTime.insert(redfishDateTime.end() - 2, ':');
1213 }
1214
1215 return redfishDateTime;
1216 }
Ed Tanous0f74e642018-11-12 15:17:05 -08001217
1218 std::string uuid;
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001219};
1220
Ed Tanous1abe55e2018-09-05 08:30:59 -07001221class ManagerCollection : public Node
1222{
1223 public:
James Feist5b4aa862018-08-16 14:07:01 -07001224 ManagerCollection(CrowApp& app) : Node(app, "/redfish/v1/Managers/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001225 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001226 entityPrivileges = {
1227 {boost::beast::http::verb::get, {{"Login"}}},
1228 {boost::beast::http::verb::head, {{"Login"}}},
1229 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1230 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1231 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1232 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1233 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001234
Ed Tanous1abe55e2018-09-05 08:30:59 -07001235 private:
James Feist5b4aa862018-08-16 14:07:01 -07001236 void doGet(crow::Response& res, const crow::Request& req,
1237 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -07001238 {
James Feist83ff9ab2018-08-31 10:18:24 -07001239 // Collections don't include the static data added by SubRoute
1240 // because it has a duplicate entry for members
Ed Tanous1abe55e2018-09-05 08:30:59 -07001241 res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
1242 res.jsonValue["@odata.type"] = "#ManagerCollection.ManagerCollection";
1243 res.jsonValue["@odata.context"] =
1244 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection";
1245 res.jsonValue["Name"] = "Manager Collection";
1246 res.jsonValue["Members@odata.count"] = 1;
1247 res.jsonValue["Members"] = {
James Feist5b4aa862018-08-16 14:07:01 -07001248 {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001249 res.end();
1250 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001251};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001252} // namespace redfish