blob: d4e734404ff9b11f864cd09c6f3e309a238c3faf [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>
22
Ed Tanous1abe55e2018-09-05 08:30:59 -070023namespace redfish
24{
Jennifer Leeed5befb2018-08-10 11:29:45 -070025
26/**
27 * ManagerActionsReset class supports handle POST method for Reset action.
28 * The class retrieves and sends data directly to dbus.
29 */
30class ManagerActionsReset : public Node
31{
32 public:
33 ManagerActionsReset(CrowApp& app) :
34 Node(app, "/redfish/v1/Managers/bmc/Actions/Manager.Reset/")
35 {
36 entityPrivileges = {
37 {boost::beast::http::verb::get, {{"Login"}}},
38 {boost::beast::http::verb::head, {{"Login"}}},
39 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
40 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
41 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
42 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
43 }
44
45 private:
46 /**
47 * Function handles GET method request.
48 * ManagerActionReset supports for POST method,
49 * it is not required to retrieve more information in GET.
50 */
51 void doGet(crow::Response& res, const crow::Request& req,
52 const std::vector<std::string>& params) override
53 {
54 res.jsonValue = Node::json;
55 res.end();
56 }
57
58 /**
59 * Function handles POST method request.
60 * Analyzes POST body message before sends Reset request data to dbus.
61 * OpenBMC allows for ResetType is GracefulRestart only.
62 */
63 void doPost(crow::Response& res, const crow::Request& req,
64 const std::vector<std::string>& params) override
65 {
66 std::string resetType;
67
68 if (!json_util::readJson(req, res, "ResetType", resetType))
69 {
70 return;
71 }
72
73 if (resetType != "GracefulRestart")
74 {
75 res.result(boost::beast::http::status::bad_request);
76 messages::actionParameterNotSupported(res, resetType, "ResetType");
77 BMCWEB_LOG_ERROR << "Request incorrect action parameter: "
78 << resetType;
79 res.end();
80 return;
81 }
82 doBMCGracefulRestart(res, req, params);
83 }
84
85 /**
86 * Function transceives data with dbus directly.
87 * All BMC state properties will be retrieved before sending reset request.
88 */
89 void doBMCGracefulRestart(crow::Response& res, const crow::Request& req,
90 const std::vector<std::string>& params)
91 {
92 const char* processName = "xyz.openbmc_project.State.BMC";
93 const char* objectPath = "/xyz/openbmc_project/state/bmc0";
94 const char* interfaceName = "xyz.openbmc_project.State.BMC";
95 const std::string& propertyValue =
96 "xyz.openbmc_project.State.BMC.Transition.Reboot";
97 const char* destProperty = "RequestedBMCTransition";
98
99 // Create the D-Bus variant for D-Bus call.
100 VariantType dbusPropertyValue(propertyValue);
101
102 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
103
104 crow::connections::systemBus->async_method_call(
105 [asyncResp](const boost::system::error_code ec) {
106 // Use "Set" method to set the property value.
107 if (ec)
108 {
109 BMCWEB_LOG_ERROR << "[Set] Bad D-Bus request error: " << ec;
110 messages::internalError(asyncResp->res);
111 return;
112 }
113
114 messages::success(asyncResp->res);
115 },
116 processName, objectPath, "org.freedesktop.DBus.Properties", "Set",
117 interfaceName, destProperty, dbusPropertyValue);
118 }
119};
120
James Feist5b4aa862018-08-16 14:07:01 -0700121static constexpr const char* objectManagerIface =
122 "org.freedesktop.DBus.ObjectManager";
123static constexpr const char* pidConfigurationIface =
124 "xyz.openbmc_project.Configuration.Pid";
125static constexpr const char* pidZoneConfigurationIface =
126 "xyz.openbmc_project.Configuration.Pid.Zone";
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100127
James Feist5b4aa862018-08-16 14:07:01 -0700128static void asyncPopulatePid(const std::string& connection,
129 const std::string& path,
130 std::shared_ptr<AsyncResp> asyncResp)
131{
132
133 crow::connections::systemBus->async_method_call(
134 [asyncResp](const boost::system::error_code ec,
135 const dbus::utility::ManagedObjectType& managedObj) {
136 if (ec)
137 {
138 BMCWEB_LOG_ERROR << ec;
James Feist5b4aa862018-08-16 14:07:01 -0700139 asyncResp->res.jsonValue.clear();
Jason M. Billsf12894f2018-10-09 12:45:45 -0700140 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700141 return;
142 }
143 nlohmann::json& configRoot =
144 asyncResp->res.jsonValue["Oem"]["OpenBmc"]["Fan"];
145 nlohmann::json& fans = configRoot["FanControllers"];
146 fans["@odata.type"] = "#OemManager.FanControllers";
147 fans["@odata.context"] =
148 "/redfish/v1/$metadata#OemManager.FanControllers";
149 fans["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc/"
150 "Fan/FanControllers";
151
152 nlohmann::json& pids = configRoot["PidControllers"];
153 pids["@odata.type"] = "#OemManager.PidControllers";
154 pids["@odata.context"] =
155 "/redfish/v1/$metadata#OemManager.PidControllers";
156 pids["@odata.id"] =
157 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/PidControllers";
158
159 nlohmann::json& zones = configRoot["FanZones"];
160 zones["@odata.id"] =
161 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones";
162 zones["@odata.type"] = "#OemManager.FanZones";
163 zones["@odata.context"] =
164 "/redfish/v1/$metadata#OemManager.FanZones";
165 configRoot["@odata.id"] =
166 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan";
167 configRoot["@odata.type"] = "#OemManager.Fan";
168 configRoot["@odata.context"] =
169 "/redfish/v1/$metadata#OemManager.Fan";
170
171 bool propertyError = false;
172 for (const auto& pathPair : managedObj)
173 {
174 for (const auto& intfPair : pathPair.second)
175 {
176 if (intfPair.first != pidConfigurationIface &&
177 intfPair.first != pidZoneConfigurationIface)
178 {
179 continue;
180 }
181 auto findName = intfPair.second.find("Name");
182 if (findName == intfPair.second.end())
183 {
184 BMCWEB_LOG_ERROR << "Pid Field missing Name";
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800185 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700186 return;
187 }
188 const std::string* namePtr =
189 mapbox::getPtr<const std::string>(findName->second);
190 if (namePtr == nullptr)
191 {
192 BMCWEB_LOG_ERROR << "Pid Name Field illegal";
193 return;
194 }
195
196 std::string name = *namePtr;
197 dbus::utility::escapePathForDbus(name);
198 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";
215 }
216
217 for (const auto& propertyPair : intfPair.second)
218 {
219 if (propertyPair.first == "Type" ||
220 propertyPair.first == "Class" ||
221 propertyPair.first == "Name")
222 {
223 continue;
224 }
225
226 // zones
227 if (intfPair.first == pidZoneConfigurationIface)
228 {
229 const double* ptr = mapbox::getPtr<const double>(
230 propertyPair.second);
231 if (ptr == nullptr)
232 {
233 BMCWEB_LOG_ERROR << "Field Illegal "
234 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700235 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700236 return;
237 }
238 zones[name][propertyPair.first] = *ptr;
239 }
240
241 // pid and fans are off the same configuration
242 if (intfPair.first == pidConfigurationIface)
243 {
244 const std::string* classPtr = nullptr;
245 auto findClass = intfPair.second.find("Class");
246 if (findClass != intfPair.second.end())
247 {
248 classPtr = mapbox::getPtr<const std::string>(
249 findClass->second);
250 }
251 if (classPtr == nullptr)
252 {
253 BMCWEB_LOG_ERROR << "Pid Class Field illegal";
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800254 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700255 return;
256 }
257 bool isFan = *classPtr == "fan";
258 nlohmann::json& element =
259 isFan ? fans[name] : pids[name];
260 if (isFan)
261 {
262 element["@odata.id"] =
263 "/redfish/v1/Managers/bmc#/Oem/"
264 "OpenBmc/Fan/FanControllers/" +
265 std::string(name);
266 element["@odata.type"] =
267 "#OemManager.FanController";
268
269 element["@odata.context"] =
270 "/redfish/v1/"
271 "$metadata#OemManager.FanController";
272 }
273 else
274 {
275 element["@odata.id"] =
276 "/redfish/v1/Managers/bmc#/Oem/"
277 "OpenBmc/Fan/PidControllers/" +
278 std::string(name);
279 element["@odata.type"] =
280 "#OemManager.PidController";
281 element["@odata.context"] =
282 "/redfish/v1/$metadata"
283 "#OemManager.PidController";
284 }
285
286 if (propertyPair.first == "Zones")
287 {
288 const std::vector<std::string>* inputs =
289 mapbox::getPtr<
290 const std::vector<std::string>>(
291 propertyPair.second);
292
293 if (inputs == nullptr)
294 {
295 BMCWEB_LOG_ERROR
296 << "Zones Pid Field Illegal";
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800297 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700298 return;
299 }
300 auto& data = element[propertyPair.first];
301 data = nlohmann::json::array();
302 for (std::string itemCopy : *inputs)
303 {
304 dbus::utility::escapePathForDbus(itemCopy);
305 data.push_back(
306 {{"@odata.id",
307 "/redfish/v1/Managers/bmc#/Oem/"
308 "OpenBmc/Fan/FanZones/" +
309 itemCopy}});
310 }
311 }
312 // todo(james): may never happen, but this
313 // assumes configuration data referenced in the
314 // PID config is provided by the same daemon, we
315 // could add another loop to cover all cases,
316 // but I'm okay kicking this can down the road a
317 // bit
318
319 else if (propertyPair.first == "Inputs" ||
320 propertyPair.first == "Outputs")
321 {
322 auto& data = element[propertyPair.first];
323 const std::vector<std::string>* inputs =
324 mapbox::getPtr<
325 const std::vector<std::string>>(
326 propertyPair.second);
327
328 if (inputs == nullptr)
329 {
330 BMCWEB_LOG_ERROR << "Field Illegal "
331 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700332 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700333 return;
334 }
335 data = *inputs;
336 } // doubles
337 else if (propertyPair.first ==
338 "FFGainCoefficient" ||
339 propertyPair.first == "FFOffCoefficient" ||
340 propertyPair.first == "ICoefficient" ||
341 propertyPair.first == "ILimitMax" ||
342 propertyPair.first == "ILimitMin" ||
343 propertyPair.first == "OutLimitMax" ||
344 propertyPair.first == "OutLimitMin" ||
345 propertyPair.first == "PCoefficient" ||
346 propertyPair.first == "SlewNeg" ||
347 propertyPair.first == "SlewPos")
348 {
349 const double* ptr =
350 mapbox::getPtr<const double>(
351 propertyPair.second);
352 if (ptr == nullptr)
353 {
354 BMCWEB_LOG_ERROR << "Field Illegal "
355 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700356 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700357 return;
358 }
359 element[propertyPair.first] = *ptr;
360 }
361 }
362 }
363 }
364 }
365 },
366 connection, path, objectManagerIface, "GetManagedObjects");
367}
Jennifer Leeca537922018-08-10 10:07:30 -0700368
James Feist83ff9ab2018-08-31 10:18:24 -0700369enum class CreatePIDRet
370{
371 fail,
372 del,
373 patch
374};
375
376static CreatePIDRet createPidInterface(
377 const std::shared_ptr<AsyncResp>& response, const std::string& type,
378 const nlohmann::json& record, const std::string& path,
379 const dbus::utility::ManagedObjectType& managedObj, bool createNewObject,
380 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>&
381 output,
382 std::string& chassis)
383{
384
385 if (type == "PidControllers" || type == "FanControllers")
386 {
387 if (createNewObject)
388 {
389 output["Class"] = type == "PidControllers" ? std::string("temp")
390 : std::string("fan");
391 output["Type"] = std::string("Pid");
392 }
393 else if (record == nullptr)
394 {
395 // delete interface
396 crow::connections::systemBus->async_method_call(
397 [response,
398 path{std::string(path)}](const boost::system::error_code ec) {
399 if (ec)
400 {
401 BMCWEB_LOG_ERROR << "Error patching " << path << ": "
402 << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700403 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700404 }
405 },
406 "xyz.openbmc_project.EntityManager", path,
407 pidConfigurationIface, "Delete");
408 return CreatePIDRet::del;
409 }
410
411 for (auto& field : record.items())
412 {
413 if (field.key() == "Zones")
414 {
415 if (!field.value().is_array())
416 {
417 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700418 messages::propertyValueFormatError(
419 response->res, field.value(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700420 return CreatePIDRet::fail;
421 }
422 std::vector<std::string> inputs;
423 for (const auto& odata : field.value().items())
424 {
425 for (const auto& value : odata.value().items())
426 {
427 const std::string* path =
428 value.value().get_ptr<const std::string*>();
429 if (path == nullptr)
430 {
431 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700432 messages::propertyValueFormatError(
433 response->res, field.value().dump(),
434 field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700435 return CreatePIDRet::fail;
436 }
437 std::string input;
438 if (!dbus::utility::getNthStringFromPath(*path, 4,
439 input))
440 {
441 BMCWEB_LOG_ERROR << "Got invalid path " << *path;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700442 messages::propertyValueFormatError(
443 response->res, field.value().dump(),
444 field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700445 return CreatePIDRet::fail;
446 }
447 boost::replace_all(input, "_", " ");
448 inputs.emplace_back(std::move(input));
449 }
450 }
451 output["Zones"] = std::move(inputs);
452 }
453 else if (field.key() == "Inputs" || field.key() == "Outputs")
454 {
455 if (!field.value().is_array())
456 {
457 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700458 messages::propertyValueFormatError(
459 response->res, field.value().dump(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700460 return CreatePIDRet::fail;
461 }
462 std::vector<std::string> inputs;
463 for (const auto& value : field.value().items())
464 {
465 const std::string* sensor =
466 value.value().get_ptr<const std::string*>();
467
468 if (sensor == nullptr)
469 {
470 BMCWEB_LOG_ERROR << "Illegal Type "
471 << field.value().dump();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700472 messages::propertyValueFormatError(
473 response->res, field.value().dump(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700474 return CreatePIDRet::fail;
475 }
476
477 std::string input =
478 boost::replace_all_copy(*sensor, "_", " ");
479 inputs.push_back(std::move(input));
480 // try to find the sensor in the
481 // configuration
482 if (chassis.empty())
483 {
484 std::find_if(
485 managedObj.begin(), managedObj.end(),
486 [&chassis, sensor](const auto& obj) {
487 if (boost::algorithm::ends_with(obj.first.str,
488 *sensor))
489 {
490 return dbus::utility::getNthStringFromPath(
491 obj.first.str, 5, chassis);
492 }
493 return false;
494 });
495 }
496 }
497 output[field.key()] = inputs;
498 }
499
500 // doubles
501 else if (field.key() == "FFGainCoefficient" ||
502 field.key() == "FFOffCoefficient" ||
503 field.key() == "ICoefficient" ||
504 field.key() == "ILimitMax" || field.key() == "ILimitMin" ||
505 field.key() == "OutLimitMax" ||
506 field.key() == "OutLimitMin" ||
507 field.key() == "PCoefficient" ||
508 field.key() == "SetPoint" || field.key() == "SlewNeg" ||
509 field.key() == "SlewPos")
510 {
511 const double* ptr = field.value().get_ptr<const double*>();
512 if (ptr == nullptr)
513 {
514 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700515 messages::propertyValueFormatError(
516 response->res, field.value().dump(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700517 return CreatePIDRet::fail;
518 }
519 output[field.key()] = *ptr;
520 }
521
522 else
523 {
524 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700525 messages::propertyUnknown(response->res, field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700526 return CreatePIDRet::fail;
527 }
528 }
529 }
530 else if (type == "FanZones")
531 {
532 if (!createNewObject && record == nullptr)
533 {
534 // delete interface
535 crow::connections::systemBus->async_method_call(
536 [response,
537 path{std::string(path)}](const boost::system::error_code ec) {
538 if (ec)
539 {
540 BMCWEB_LOG_ERROR << "Error patching " << path << ": "
541 << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700542 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700543 }
544 },
545 "xyz.openbmc_project.EntityManager", path,
546 pidZoneConfigurationIface, "Delete");
547 return CreatePIDRet::del;
548 }
549 output["Type"] = std::string("Pid.Zone");
550
551 for (auto& field : record.items())
552 {
553 if (field.key() == "Chassis")
554 {
555 const std::string* chassisId = nullptr;
556 for (const auto& id : field.value().items())
557 {
558 if (id.key() != "@odata.id")
559 {
560 BMCWEB_LOG_ERROR << "Illegal Type " << id.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700561 messages::propertyUnknown(response->res, field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700562 return CreatePIDRet::fail;
563 }
564 chassisId = id.value().get_ptr<const std::string*>();
565 if (chassisId == nullptr)
566 {
Jason M. Bills35a62c72018-10-09 12:45:45 -0700567 messages::createFailedMissingReqProperties(
568 response->res, field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700569 return CreatePIDRet::fail;
570 }
571 }
572
573 // /refish/v1/chassis/chassis_name/
574 if (!dbus::utility::getNthStringFromPath(*chassisId, 3,
575 chassis))
576 {
577 BMCWEB_LOG_ERROR << "Got invalid path " << *chassisId;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700578 messages::invalidObject(response->res, *chassisId);
James Feist83ff9ab2018-08-31 10:18:24 -0700579 return CreatePIDRet::fail;
580 }
581 }
582 else if (field.key() == "FailSafePercent" ||
583 field.key() == "MinThermalRpm")
584 {
585 const double* ptr = field.value().get_ptr<const double*>();
586 if (ptr == nullptr)
587 {
588 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700589 messages::propertyValueFormatError(
590 response->res, field.value().dump(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700591 return CreatePIDRet::fail;
592 }
593 output[field.key()] = *ptr;
594 }
595 else
596 {
597 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700598 messages::propertyUnknown(response->res, field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700599 return CreatePIDRet::fail;
600 }
601 }
602 }
603 else
604 {
605 BMCWEB_LOG_ERROR << "Illegal Type " << type;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700606 messages::propertyUnknown(response->res, type);
James Feist83ff9ab2018-08-31 10:18:24 -0700607 return CreatePIDRet::fail;
608 }
609 return CreatePIDRet::patch;
610}
611
Ed Tanous1abe55e2018-09-05 08:30:59 -0700612class Manager : public Node
613{
614 public:
James Feist5b4aa862018-08-16 14:07:01 -0700615 Manager(CrowApp& app) : Node(app, "/redfish/v1/Managers/bmc/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700616 {
James Feist5b4aa862018-08-16 14:07:01 -0700617 Node::json["@odata.id"] = "/redfish/v1/Managers/bmc";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700618 Node::json["@odata.type"] = "#Manager.v1_3_0.Manager";
619 Node::json["@odata.context"] = "/redfish/v1/$metadata#Manager.Manager";
James Feist5b4aa862018-08-16 14:07:01 -0700620 Node::json["Id"] = "bmc";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700621 Node::json["Name"] = "OpenBmc Manager";
622 Node::json["Description"] = "Baseboard Management Controller";
623 Node::json["PowerState"] = "On";
Jennifer Leeca537922018-08-10 10:07:30 -0700624 Node::json["ManagerType"] = "BMC";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700625 Node::json["UUID"] =
626 app.template getMiddleware<crow::persistent_data::Middleware>()
627 .systemUuid;
Jennifer Leeca537922018-08-10 10:07:30 -0700628 Node::json["Model"] = "OpenBmc"; // TODO(ed), get model
629 Node::json["EthernetInterfaces"] = {
James Feist5b4aa862018-08-16 14:07:01 -0700630 {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces"}};
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800631
Ed Tanous1abe55e2018-09-05 08:30:59 -0700632 entityPrivileges = {
633 {boost::beast::http::verb::get, {{"Login"}}},
634 {boost::beast::http::verb::head, {{"Login"}}},
635 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
636 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
637 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
638 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
James Feist5b4aa862018-08-16 14:07:01 -0700639
640 // default oem data
641 nlohmann::json& oem = Node::json["Oem"];
642 nlohmann::json& oemOpenbmc = oem["OpenBmc"];
643 oem["@odata.type"] = "#OemManager.Oem";
644 oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem";
645 oem["@odata.context"] = "/redfish/v1/$metadata#OemManager.Oem";
646 oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc";
647 oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc";
648 oemOpenbmc["@odata.context"] =
649 "/redfish/v1/$metadata#OemManager.OpenBmc";
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100650 }
651
Ed Tanous1abe55e2018-09-05 08:30:59 -0700652 private:
James Feist5b4aa862018-08-16 14:07:01 -0700653 void getPidValues(std::shared_ptr<AsyncResp> asyncResp)
654 {
655 crow::connections::systemBus->async_method_call(
656 [asyncResp](const boost::system::error_code ec,
657 const crow::openbmc_mapper::GetSubTreeType& subtree) {
658 if (ec)
659 {
660 BMCWEB_LOG_ERROR << ec;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700661 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700662 return;
663 }
664
665 // create map of <connection, path to objMgr>>
666 boost::container::flat_map<std::string, std::string>
667 objectMgrPaths;
James Feist6bce33b2018-10-22 12:05:56 -0700668 boost::container::flat_set<std::string> calledConnections;
James Feist5b4aa862018-08-16 14:07:01 -0700669 for (const auto& pathGroup : subtree)
670 {
671 for (const auto& connectionGroup : pathGroup.second)
672 {
James Feist6bce33b2018-10-22 12:05:56 -0700673 auto findConnection =
674 calledConnections.find(connectionGroup.first);
675 if (findConnection != calledConnections.end())
676 {
677 break;
678 }
James Feist5b4aa862018-08-16 14:07:01 -0700679 for (const std::string& interface :
680 connectionGroup.second)
681 {
682 if (interface == objectManagerIface)
683 {
684 objectMgrPaths[connectionGroup.first] =
685 pathGroup.first;
686 }
687 // this list is alphabetical, so we
688 // should have found the objMgr by now
689 if (interface == pidConfigurationIface ||
690 interface == pidZoneConfigurationIface)
691 {
692 auto findObjMgr =
693 objectMgrPaths.find(connectionGroup.first);
694 if (findObjMgr == objectMgrPaths.end())
695 {
696 BMCWEB_LOG_DEBUG << connectionGroup.first
697 << "Has no Object Manager";
698 continue;
699 }
James Feist6bce33b2018-10-22 12:05:56 -0700700
701 calledConnections.insert(connectionGroup.first);
702
James Feist5b4aa862018-08-16 14:07:01 -0700703 asyncPopulatePid(findObjMgr->first,
704 findObjMgr->second, asyncResp);
705 break;
706 }
707 }
708 }
709 }
710 },
711 "xyz.openbmc_project.ObjectMapper",
712 "/xyz/openbmc_project/object_mapper",
713 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
714 std::array<const char*, 3>{pidConfigurationIface,
715 pidZoneConfigurationIface,
716 objectManagerIface});
717 }
718
719 void doGet(crow::Response& res, const crow::Request& req,
720 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700721 {
Jennifer Leeed5befb2018-08-10 11:29:45 -0700722 // Update Actions object.
723 nlohmann::json& manager_reset = Node::json["Actions"]["#Manager.Reset"];
724 manager_reset["target"] =
725 "/redfish/v1/Managers/bmc/Actions/Manager.Reset";
726 manager_reset["ResetType@Redfish.AllowableValues"] = {
727 "GracefulRestart"};
Jennifer Leeca537922018-08-10 10:07:30 -0700728
Ed Tanous1abe55e2018-09-05 08:30:59 -0700729 Node::json["DateTime"] = getDateTime();
Jennifer Leeed5befb2018-08-10 11:29:45 -0700730 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
731 asyncResp->res.jsonValue = Node::json;
James Feist5b4aa862018-08-16 14:07:01 -0700732
Jennifer Leeca537922018-08-10 10:07:30 -0700733 crow::connections::systemBus->async_method_call(
734 [asyncResp](const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -0700735 const dbus::utility::ManagedObjectType& resp) {
Jennifer Leeca537922018-08-10 10:07:30 -0700736 if (ec)
737 {
738 BMCWEB_LOG_ERROR << "Error while getting Software Version";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700739 messages::internalError(asyncResp->res);
Jennifer Leeca537922018-08-10 10:07:30 -0700740 return;
741 }
742
James Feist5b4aa862018-08-16 14:07:01 -0700743 for (auto& objpath : resp)
Jennifer Leeca537922018-08-10 10:07:30 -0700744 {
James Feist5b4aa862018-08-16 14:07:01 -0700745 for (auto& interface : objpath.second)
Jennifer Leeca537922018-08-10 10:07:30 -0700746 {
747 // If interface is xyz.openbmc_project.Software.Version,
748 // this is what we're looking for.
749 if (interface.first ==
750 "xyz.openbmc_project.Software.Version")
751 {
752 // Cut out everyting until last "/", ...
James Feist5b4aa862018-08-16 14:07:01 -0700753 const std::string& iface_id = objpath.first;
754 for (auto& property : interface.second)
Jennifer Leeca537922018-08-10 10:07:30 -0700755 {
756 if (property.first == "Version")
757 {
James Feist5b4aa862018-08-16 14:07:01 -0700758 const std::string* value =
Jennifer Leeca537922018-08-10 10:07:30 -0700759 mapbox::getPtr<const std::string>(
760 property.second);
761 if (value == nullptr)
762 {
763 continue;
764 }
765 asyncResp->res
766 .jsonValue["FirmwareVersion"] = *value;
767 }
768 }
769 }
770 }
771 }
772 },
773 "xyz.openbmc_project.Software.BMC.Updater",
774 "/xyz/openbmc_project/software",
775 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
James Feist5b4aa862018-08-16 14:07:01 -0700776 getPidValues(asyncResp);
777 }
James Feist83ff9ab2018-08-31 10:18:24 -0700778 void setPidValues(std::shared_ptr<AsyncResp> response,
779 const nlohmann::json& data)
780 {
781 // todo(james): might make sense to do a mapper call here if this
782 // interface gets more traction
783 crow::connections::systemBus->async_method_call(
784 [response,
785 data](const boost::system::error_code ec,
786 const dbus::utility::ManagedObjectType& managedObj) {
787 if (ec)
788 {
789 BMCWEB_LOG_ERROR << "Error communicating to Entity Manager";
Jason M. Bills35a62c72018-10-09 12:45:45 -0700790 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700791 return;
792 }
793 for (const auto& type : data.items())
794 {
795 if (!type.value().is_object())
796 {
797 BMCWEB_LOG_ERROR << "Illegal Type " << type.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700798 messages::propertyValueFormatError(
799 response->res, type.value(), type.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700800 return;
801 }
802 for (const auto& record : type.value().items())
803 {
804 const std::string& name = record.key();
805 auto pathItr =
806 std::find_if(managedObj.begin(), managedObj.end(),
807 [&name](const auto& obj) {
808 return boost::algorithm::ends_with(
809 obj.first.str, name);
810 });
811 boost::container::flat_map<
812 std::string, dbus::utility::DbusVariantType>
813 output;
814
815 output.reserve(16); // The pid interface length
816
817 // determines if we're patching entity-manager or
818 // creating a new object
819 bool createNewObject = (pathItr == managedObj.end());
820 if (type.key() == "PidControllers" ||
821 type.key() == "FanControllers")
822 {
823 if (!createNewObject &&
824 pathItr->second.find(pidConfigurationIface) ==
825 pathItr->second.end())
826 {
827 createNewObject = true;
828 }
829 }
830 else if (!createNewObject &&
831 pathItr->second.find(
832 pidZoneConfigurationIface) ==
833 pathItr->second.end())
834 {
835 createNewObject = true;
836 }
837 output["Name"] =
838 boost::replace_all_copy(name, "_", " ");
839
840 std::string chassis;
841 CreatePIDRet ret = createPidInterface(
842 response, type.key(), record.value(),
843 pathItr->first.str, managedObj, createNewObject,
844 output, chassis);
845 if (ret == CreatePIDRet::fail)
846 {
847 return;
848 }
849 else if (ret == CreatePIDRet::del)
850 {
851 continue;
852 }
853
854 if (!createNewObject)
855 {
856 for (const auto& property : output)
857 {
858 const char* iface =
859 type.key() == "FanZones"
860 ? pidZoneConfigurationIface
861 : pidConfigurationIface;
862 crow::connections::systemBus->async_method_call(
863 [response,
864 propertyName{std::string(property.first)}](
865 const boost::system::error_code ec) {
866 if (ec)
867 {
868 BMCWEB_LOG_ERROR
869 << "Error patching "
870 << propertyName << ": " << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700871 messages::internalError(
872 response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700873 }
874 },
875 "xyz.openbmc_project.EntityManager",
876 pathItr->first.str,
877 "org.freedesktop.DBus.Properties", "Set",
878 std::string(iface), property.first,
879 property.second);
880 }
881 }
882 else
883 {
884 if (chassis.empty())
885 {
886 BMCWEB_LOG_ERROR
887 << "Failed to get chassis from config";
Jason M. Bills35a62c72018-10-09 12:45:45 -0700888 messages::invalidObject(response->res, name);
James Feist83ff9ab2018-08-31 10:18:24 -0700889 return;
890 }
891
892 bool foundChassis = false;
893 for (const auto& obj : managedObj)
894 {
895 if (boost::algorithm::ends_with(obj.first.str,
896 chassis))
897 {
898 chassis = obj.first.str;
899 foundChassis = true;
900 break;
901 }
902 }
903 if (!foundChassis)
904 {
905 BMCWEB_LOG_ERROR
906 << "Failed to find chassis on dbus";
Jason M. Bills35a62c72018-10-09 12:45:45 -0700907 messages::resourceMissingAtURI(
908 response->res,
909 "/redfish/v1/Chassis/" + chassis);
James Feist83ff9ab2018-08-31 10:18:24 -0700910 return;
911 }
912
913 crow::connections::systemBus->async_method_call(
914 [response](const boost::system::error_code ec) {
915 if (ec)
916 {
917 BMCWEB_LOG_ERROR
918 << "Error Adding Pid Object " << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700919 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700920 }
921 },
922 "xyz.openbmc_project.EntityManager", chassis,
923 "xyz.openbmc_project.AddObject", "AddObject",
924 output);
925 }
926 }
927 }
928 },
929 "xyz.openbmc_project.EntityManager", "/", objectManagerIface,
930 "GetManagedObjects");
931 }
James Feist5b4aa862018-08-16 14:07:01 -0700932
933 void doPatch(crow::Response& res, const crow::Request& req,
934 const std::vector<std::string>& params) override
935 {
James Feist83ff9ab2018-08-31 10:18:24 -0700936 nlohmann::json patch;
937 if (!json_util::processJsonFromRequest(res, req, patch))
938 {
939 return;
940 }
941 std::shared_ptr<AsyncResp> response = std::make_shared<AsyncResp>(res);
942 for (const auto& topLevel : patch.items())
943 {
944 if (topLevel.key() == "Oem")
945 {
946 if (!topLevel.value().is_object())
947 {
948 BMCWEB_LOG_ERROR << "Bad Patch " << topLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700949 messages::propertyValueFormatError(
950 response->res, topLevel.key(), "OemManager.Oem");
James Feist83ff9ab2018-08-31 10:18:24 -0700951 return;
952 }
953 }
954 else
955 {
956 BMCWEB_LOG_ERROR << "Bad Patch " << topLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700957 messages::propertyUnknown(response->res, topLevel.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700958 return;
959 }
960 for (const auto& oemLevel : topLevel.value().items())
961 {
962 if (oemLevel.key() == "OpenBmc")
963 {
964 if (!oemLevel.value().is_object())
965 {
966 BMCWEB_LOG_ERROR << "Bad Patch " << oemLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700967 messages::propertyValueFormatError(
968 response->res, topLevel.key(),
969 "OemManager.OpenBmc");
James Feist83ff9ab2018-08-31 10:18:24 -0700970 return;
971 }
972 for (const auto& typeLevel : oemLevel.value().items())
973 {
974
975 if (typeLevel.key() == "Fan")
976 {
977 if (!typeLevel.value().is_object())
978 {
979 BMCWEB_LOG_ERROR << "Bad Patch "
980 << typeLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700981 messages::propertyValueFormatError(
982 response->res, typeLevel.value().dump(),
983 typeLevel.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700984 return;
985 }
986 setPidValues(response,
987 std::move(typeLevel.value()));
988 }
989 else
990 {
991 BMCWEB_LOG_ERROR << "Bad Patch " << typeLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700992 messages::propertyUnknown(response->res,
993 typeLevel.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700994 return;
995 }
996 }
997 }
998 else
999 {
1000 BMCWEB_LOG_ERROR << "Bad Patch " << oemLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -07001001 messages::propertyUnknown(response->res, oemLevel.key());
James Feist83ff9ab2018-08-31 10:18:24 -07001002 return;
1003 }
1004 }
1005 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001006 }
1007
1008 std::string getDateTime() const
1009 {
1010 std::array<char, 128> dateTime;
1011 std::string redfishDateTime("0000-00-00T00:00:00Z00:00");
1012 std::time_t time = std::time(nullptr);
1013
1014 if (std::strftime(dateTime.begin(), dateTime.size(), "%FT%T%z",
1015 std::localtime(&time)))
1016 {
1017 // insert the colon required by the ISO 8601 standard
1018 redfishDateTime = std::string(dateTime.data());
1019 redfishDateTime.insert(redfishDateTime.end() - 2, ':');
1020 }
1021
1022 return redfishDateTime;
1023 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001024};
1025
Ed Tanous1abe55e2018-09-05 08:30:59 -07001026class ManagerCollection : public Node
1027{
1028 public:
James Feist5b4aa862018-08-16 14:07:01 -07001029 ManagerCollection(CrowApp& app) : Node(app, "/redfish/v1/Managers/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001030 {
1031 Node::json["@odata.id"] = "/redfish/v1/Managers";
1032 Node::json["@odata.type"] = "#ManagerCollection.ManagerCollection";
1033 Node::json["@odata.context"] =
1034 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection";
1035 Node::json["Name"] = "Manager Collection";
1036 Node::json["Members@odata.count"] = 1;
James Feist5b4aa862018-08-16 14:07:01 -07001037 Node::json["Members"] = {{{"@odata.id", "/redfish/v1/Managers/bmc"}}};
Ed Tanous3ebd75f2018-03-05 18:20:01 -08001038
Ed Tanous1abe55e2018-09-05 08:30:59 -07001039 entityPrivileges = {
1040 {boost::beast::http::verb::get, {{"Login"}}},
1041 {boost::beast::http::verb::head, {{"Login"}}},
1042 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1043 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1044 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1045 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1046 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001047
Ed Tanous1abe55e2018-09-05 08:30:59 -07001048 private:
James Feist5b4aa862018-08-16 14:07:01 -07001049 void doGet(crow::Response& res, const crow::Request& req,
1050 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -07001051 {
James Feist83ff9ab2018-08-31 10:18:24 -07001052 // Collections don't include the static data added by SubRoute
1053 // because it has a duplicate entry for members
Ed Tanous1abe55e2018-09-05 08:30:59 -07001054 res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
1055 res.jsonValue["@odata.type"] = "#ManagerCollection.ManagerCollection";
1056 res.jsonValue["@odata.context"] =
1057 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection";
1058 res.jsonValue["Name"] = "Manager Collection";
1059 res.jsonValue["Members@odata.count"] = 1;
1060 res.jsonValue["Members"] = {
James Feist5b4aa862018-08-16 14:07:01 -07001061 {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001062 res.end();
1063 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001064};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001065} // namespace redfish