blob: 4c56cdf2103cb5b97c1beb0d6c832a19c4da97ae [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. Billsf12894f2018-10-09 12:45:45 -0700185 messages::internalError(asyncResp->res, "Name");
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. Billsf12894f2018-10-09 12:45:45 -0700254 messages::internalError(asyncResp->res,
255 "Class");
James Feist5b4aa862018-08-16 14:07:01 -0700256 return;
257 }
258 bool isFan = *classPtr == "fan";
259 nlohmann::json& element =
260 isFan ? fans[name] : pids[name];
261 if (isFan)
262 {
263 element["@odata.id"] =
264 "/redfish/v1/Managers/bmc#/Oem/"
265 "OpenBmc/Fan/FanControllers/" +
266 std::string(name);
267 element["@odata.type"] =
268 "#OemManager.FanController";
269
270 element["@odata.context"] =
271 "/redfish/v1/"
272 "$metadata#OemManager.FanController";
273 }
274 else
275 {
276 element["@odata.id"] =
277 "/redfish/v1/Managers/bmc#/Oem/"
278 "OpenBmc/Fan/PidControllers/" +
279 std::string(name);
280 element["@odata.type"] =
281 "#OemManager.PidController";
282 element["@odata.context"] =
283 "/redfish/v1/$metadata"
284 "#OemManager.PidController";
285 }
286
287 if (propertyPair.first == "Zones")
288 {
289 const std::vector<std::string>* inputs =
290 mapbox::getPtr<
291 const std::vector<std::string>>(
292 propertyPair.second);
293
294 if (inputs == nullptr)
295 {
296 BMCWEB_LOG_ERROR
297 << "Zones Pid Field Illegal";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700298 messages::internalError(asyncResp->res,
299 "Zones");
James Feist5b4aa862018-08-16 14:07:01 -0700300 return;
301 }
302 auto& data = element[propertyPair.first];
303 data = nlohmann::json::array();
304 for (std::string itemCopy : *inputs)
305 {
306 dbus::utility::escapePathForDbus(itemCopy);
307 data.push_back(
308 {{"@odata.id",
309 "/redfish/v1/Managers/bmc#/Oem/"
310 "OpenBmc/Fan/FanZones/" +
311 itemCopy}});
312 }
313 }
314 // todo(james): may never happen, but this
315 // assumes configuration data referenced in the
316 // PID config is provided by the same daemon, we
317 // could add another loop to cover all cases,
318 // but I'm okay kicking this can down the road a
319 // bit
320
321 else if (propertyPair.first == "Inputs" ||
322 propertyPair.first == "Outputs")
323 {
324 auto& data = element[propertyPair.first];
325 const std::vector<std::string>* inputs =
326 mapbox::getPtr<
327 const std::vector<std::string>>(
328 propertyPair.second);
329
330 if (inputs == nullptr)
331 {
332 BMCWEB_LOG_ERROR << "Field Illegal "
333 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700334 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700335 return;
336 }
337 data = *inputs;
338 } // doubles
339 else if (propertyPair.first ==
340 "FFGainCoefficient" ||
341 propertyPair.first == "FFOffCoefficient" ||
342 propertyPair.first == "ICoefficient" ||
343 propertyPair.first == "ILimitMax" ||
344 propertyPair.first == "ILimitMin" ||
345 propertyPair.first == "OutLimitMax" ||
346 propertyPair.first == "OutLimitMin" ||
347 propertyPair.first == "PCoefficient" ||
348 propertyPair.first == "SlewNeg" ||
349 propertyPair.first == "SlewPos")
350 {
351 const double* ptr =
352 mapbox::getPtr<const double>(
353 propertyPair.second);
354 if (ptr == nullptr)
355 {
356 BMCWEB_LOG_ERROR << "Field Illegal "
357 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700358 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700359 return;
360 }
361 element[propertyPair.first] = *ptr;
362 }
363 }
364 }
365 }
366 }
367 },
368 connection, path, objectManagerIface, "GetManagedObjects");
369}
Jennifer Leeca537922018-08-10 10:07:30 -0700370
James Feist83ff9ab2018-08-31 10:18:24 -0700371enum class CreatePIDRet
372{
373 fail,
374 del,
375 patch
376};
377
378static CreatePIDRet createPidInterface(
379 const std::shared_ptr<AsyncResp>& response, const std::string& type,
380 const nlohmann::json& record, const std::string& path,
381 const dbus::utility::ManagedObjectType& managedObj, bool createNewObject,
382 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>&
383 output,
384 std::string& chassis)
385{
386
387 if (type == "PidControllers" || type == "FanControllers")
388 {
389 if (createNewObject)
390 {
391 output["Class"] = type == "PidControllers" ? std::string("temp")
392 : std::string("fan");
393 output["Type"] = std::string("Pid");
394 }
395 else if (record == nullptr)
396 {
397 // delete interface
398 crow::connections::systemBus->async_method_call(
399 [response,
400 path{std::string(path)}](const boost::system::error_code ec) {
401 if (ec)
402 {
403 BMCWEB_LOG_ERROR << "Error patching " << path << ": "
404 << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700405 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700406 }
407 },
408 "xyz.openbmc_project.EntityManager", path,
409 pidConfigurationIface, "Delete");
410 return CreatePIDRet::del;
411 }
412
413 for (auto& field : record.items())
414 {
415 if (field.key() == "Zones")
416 {
417 if (!field.value().is_array())
418 {
419 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700420 messages::propertyValueFormatError(
421 response->res, field.value(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700422 return CreatePIDRet::fail;
423 }
424 std::vector<std::string> inputs;
425 for (const auto& odata : field.value().items())
426 {
427 for (const auto& value : odata.value().items())
428 {
429 const std::string* path =
430 value.value().get_ptr<const std::string*>();
431 if (path == nullptr)
432 {
433 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700434 messages::propertyValueFormatError(
435 response->res, field.value().dump(),
436 field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700437 return CreatePIDRet::fail;
438 }
439 std::string input;
440 if (!dbus::utility::getNthStringFromPath(*path, 4,
441 input))
442 {
443 BMCWEB_LOG_ERROR << "Got invalid path " << *path;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700444 messages::propertyValueFormatError(
445 response->res, field.value().dump(),
446 field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700447 return CreatePIDRet::fail;
448 }
449 boost::replace_all(input, "_", " ");
450 inputs.emplace_back(std::move(input));
451 }
452 }
453 output["Zones"] = std::move(inputs);
454 }
455 else if (field.key() == "Inputs" || field.key() == "Outputs")
456 {
457 if (!field.value().is_array())
458 {
459 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700460 messages::propertyValueFormatError(
461 response->res, field.value().dump(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700462 return CreatePIDRet::fail;
463 }
464 std::vector<std::string> inputs;
465 for (const auto& value : field.value().items())
466 {
467 const std::string* sensor =
468 value.value().get_ptr<const std::string*>();
469
470 if (sensor == nullptr)
471 {
472 BMCWEB_LOG_ERROR << "Illegal Type "
473 << field.value().dump();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700474 messages::propertyValueFormatError(
475 response->res, field.value().dump(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700476 return CreatePIDRet::fail;
477 }
478
479 std::string input =
480 boost::replace_all_copy(*sensor, "_", " ");
481 inputs.push_back(std::move(input));
482 // try to find the sensor in the
483 // configuration
484 if (chassis.empty())
485 {
486 std::find_if(
487 managedObj.begin(), managedObj.end(),
488 [&chassis, sensor](const auto& obj) {
489 if (boost::algorithm::ends_with(obj.first.str,
490 *sensor))
491 {
492 return dbus::utility::getNthStringFromPath(
493 obj.first.str, 5, chassis);
494 }
495 return false;
496 });
497 }
498 }
499 output[field.key()] = inputs;
500 }
501
502 // doubles
503 else if (field.key() == "FFGainCoefficient" ||
504 field.key() == "FFOffCoefficient" ||
505 field.key() == "ICoefficient" ||
506 field.key() == "ILimitMax" || field.key() == "ILimitMin" ||
507 field.key() == "OutLimitMax" ||
508 field.key() == "OutLimitMin" ||
509 field.key() == "PCoefficient" ||
510 field.key() == "SetPoint" || field.key() == "SlewNeg" ||
511 field.key() == "SlewPos")
512 {
513 const double* ptr = field.value().get_ptr<const double*>();
514 if (ptr == nullptr)
515 {
516 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700517 messages::propertyValueFormatError(
518 response->res, field.value().dump(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700519 return CreatePIDRet::fail;
520 }
521 output[field.key()] = *ptr;
522 }
523
524 else
525 {
526 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700527 messages::propertyUnknown(response->res, field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700528 return CreatePIDRet::fail;
529 }
530 }
531 }
532 else if (type == "FanZones")
533 {
534 if (!createNewObject && record == nullptr)
535 {
536 // delete interface
537 crow::connections::systemBus->async_method_call(
538 [response,
539 path{std::string(path)}](const boost::system::error_code ec) {
540 if (ec)
541 {
542 BMCWEB_LOG_ERROR << "Error patching " << path << ": "
543 << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700544 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700545 }
546 },
547 "xyz.openbmc_project.EntityManager", path,
548 pidZoneConfigurationIface, "Delete");
549 return CreatePIDRet::del;
550 }
551 output["Type"] = std::string("Pid.Zone");
552
553 for (auto& field : record.items())
554 {
555 if (field.key() == "Chassis")
556 {
557 const std::string* chassisId = nullptr;
558 for (const auto& id : field.value().items())
559 {
560 if (id.key() != "@odata.id")
561 {
562 BMCWEB_LOG_ERROR << "Illegal Type " << id.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700563 messages::propertyUnknown(response->res, field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700564 return CreatePIDRet::fail;
565 }
566 chassisId = id.value().get_ptr<const std::string*>();
567 if (chassisId == nullptr)
568 {
Jason M. Bills35a62c72018-10-09 12:45:45 -0700569 messages::createFailedMissingReqProperties(
570 response->res, field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700571 return CreatePIDRet::fail;
572 }
573 }
574
575 // /refish/v1/chassis/chassis_name/
576 if (!dbus::utility::getNthStringFromPath(*chassisId, 3,
577 chassis))
578 {
579 BMCWEB_LOG_ERROR << "Got invalid path " << *chassisId;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700580 messages::invalidObject(response->res, *chassisId);
James Feist83ff9ab2018-08-31 10:18:24 -0700581 return CreatePIDRet::fail;
582 }
583 }
584 else if (field.key() == "FailSafePercent" ||
585 field.key() == "MinThermalRpm")
586 {
587 const double* ptr = field.value().get_ptr<const double*>();
588 if (ptr == nullptr)
589 {
590 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700591 messages::propertyValueFormatError(
592 response->res, field.value().dump(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700593 return CreatePIDRet::fail;
594 }
595 output[field.key()] = *ptr;
596 }
597 else
598 {
599 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700600 messages::propertyUnknown(response->res, field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700601 return CreatePIDRet::fail;
602 }
603 }
604 }
605 else
606 {
607 BMCWEB_LOG_ERROR << "Illegal Type " << type;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700608 messages::propertyUnknown(response->res, type);
James Feist83ff9ab2018-08-31 10:18:24 -0700609 return CreatePIDRet::fail;
610 }
611 return CreatePIDRet::patch;
612}
613
Ed Tanous1abe55e2018-09-05 08:30:59 -0700614class Manager : public Node
615{
616 public:
James Feist5b4aa862018-08-16 14:07:01 -0700617 Manager(CrowApp& app) : Node(app, "/redfish/v1/Managers/bmc/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700618 {
James Feist5b4aa862018-08-16 14:07:01 -0700619 Node::json["@odata.id"] = "/redfish/v1/Managers/bmc";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700620 Node::json["@odata.type"] = "#Manager.v1_3_0.Manager";
621 Node::json["@odata.context"] = "/redfish/v1/$metadata#Manager.Manager";
James Feist5b4aa862018-08-16 14:07:01 -0700622 Node::json["Id"] = "bmc";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700623 Node::json["Name"] = "OpenBmc Manager";
624 Node::json["Description"] = "Baseboard Management Controller";
625 Node::json["PowerState"] = "On";
Jennifer Leeca537922018-08-10 10:07:30 -0700626 Node::json["ManagerType"] = "BMC";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700627 Node::json["UUID"] =
628 app.template getMiddleware<crow::persistent_data::Middleware>()
629 .systemUuid;
Jennifer Leeca537922018-08-10 10:07:30 -0700630 Node::json["Model"] = "OpenBmc"; // TODO(ed), get model
631 Node::json["EthernetInterfaces"] = {
James Feist5b4aa862018-08-16 14:07:01 -0700632 {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces"}};
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800633
Ed Tanous1abe55e2018-09-05 08:30:59 -0700634 entityPrivileges = {
635 {boost::beast::http::verb::get, {{"Login"}}},
636 {boost::beast::http::verb::head, {{"Login"}}},
637 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
638 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
639 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
640 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
James Feist5b4aa862018-08-16 14:07:01 -0700641
642 // default oem data
643 nlohmann::json& oem = Node::json["Oem"];
644 nlohmann::json& oemOpenbmc = oem["OpenBmc"];
645 oem["@odata.type"] = "#OemManager.Oem";
646 oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem";
647 oem["@odata.context"] = "/redfish/v1/$metadata#OemManager.Oem";
648 oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc";
649 oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc";
650 oemOpenbmc["@odata.context"] =
651 "/redfish/v1/$metadata#OemManager.OpenBmc";
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100652 }
653
Ed Tanous1abe55e2018-09-05 08:30:59 -0700654 private:
James Feist5b4aa862018-08-16 14:07:01 -0700655 void getPidValues(std::shared_ptr<AsyncResp> asyncResp)
656 {
657 crow::connections::systemBus->async_method_call(
658 [asyncResp](const boost::system::error_code ec,
659 const crow::openbmc_mapper::GetSubTreeType& subtree) {
660 if (ec)
661 {
662 BMCWEB_LOG_ERROR << ec;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700663 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700664 return;
665 }
666
667 // create map of <connection, path to objMgr>>
668 boost::container::flat_map<std::string, std::string>
669 objectMgrPaths;
James Feist6bce33b2018-10-22 12:05:56 -0700670 boost::container::flat_set<std::string> calledConnections;
James Feist5b4aa862018-08-16 14:07:01 -0700671 for (const auto& pathGroup : subtree)
672 {
673 for (const auto& connectionGroup : pathGroup.second)
674 {
James Feist6bce33b2018-10-22 12:05:56 -0700675 auto findConnection =
676 calledConnections.find(connectionGroup.first);
677 if (findConnection != calledConnections.end())
678 {
679 break;
680 }
James Feist5b4aa862018-08-16 14:07:01 -0700681 for (const std::string& interface :
682 connectionGroup.second)
683 {
684 if (interface == objectManagerIface)
685 {
686 objectMgrPaths[connectionGroup.first] =
687 pathGroup.first;
688 }
689 // this list is alphabetical, so we
690 // should have found the objMgr by now
691 if (interface == pidConfigurationIface ||
692 interface == pidZoneConfigurationIface)
693 {
694 auto findObjMgr =
695 objectMgrPaths.find(connectionGroup.first);
696 if (findObjMgr == objectMgrPaths.end())
697 {
698 BMCWEB_LOG_DEBUG << connectionGroup.first
699 << "Has no Object Manager";
700 continue;
701 }
James Feist6bce33b2018-10-22 12:05:56 -0700702
703 calledConnections.insert(connectionGroup.first);
704
James Feist5b4aa862018-08-16 14:07:01 -0700705 asyncPopulatePid(findObjMgr->first,
706 findObjMgr->second, asyncResp);
707 break;
708 }
709 }
710 }
711 }
712 },
713 "xyz.openbmc_project.ObjectMapper",
714 "/xyz/openbmc_project/object_mapper",
715 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
716 std::array<const char*, 3>{pidConfigurationIface,
717 pidZoneConfigurationIface,
718 objectManagerIface});
719 }
720
721 void doGet(crow::Response& res, const crow::Request& req,
722 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700723 {
Jennifer Leeed5befb2018-08-10 11:29:45 -0700724 // Update Actions object.
725 nlohmann::json& manager_reset = Node::json["Actions"]["#Manager.Reset"];
726 manager_reset["target"] =
727 "/redfish/v1/Managers/bmc/Actions/Manager.Reset";
728 manager_reset["ResetType@Redfish.AllowableValues"] = {
729 "GracefulRestart"};
Jennifer Leeca537922018-08-10 10:07:30 -0700730
Ed Tanous1abe55e2018-09-05 08:30:59 -0700731 Node::json["DateTime"] = getDateTime();
Jennifer Leeed5befb2018-08-10 11:29:45 -0700732 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
733 asyncResp->res.jsonValue = Node::json;
James Feist5b4aa862018-08-16 14:07:01 -0700734
Jennifer Leeca537922018-08-10 10:07:30 -0700735 crow::connections::systemBus->async_method_call(
736 [asyncResp](const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -0700737 const dbus::utility::ManagedObjectType& resp) {
Jennifer Leeca537922018-08-10 10:07:30 -0700738 if (ec)
739 {
740 BMCWEB_LOG_ERROR << "Error while getting Software Version";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700741 messages::internalError(asyncResp->res);
Jennifer Leeca537922018-08-10 10:07:30 -0700742 return;
743 }
744
James Feist5b4aa862018-08-16 14:07:01 -0700745 for (auto& objpath : resp)
Jennifer Leeca537922018-08-10 10:07:30 -0700746 {
James Feist5b4aa862018-08-16 14:07:01 -0700747 for (auto& interface : objpath.second)
Jennifer Leeca537922018-08-10 10:07:30 -0700748 {
749 // If interface is xyz.openbmc_project.Software.Version,
750 // this is what we're looking for.
751 if (interface.first ==
752 "xyz.openbmc_project.Software.Version")
753 {
754 // Cut out everyting until last "/", ...
James Feist5b4aa862018-08-16 14:07:01 -0700755 const std::string& iface_id = objpath.first;
756 for (auto& property : interface.second)
Jennifer Leeca537922018-08-10 10:07:30 -0700757 {
758 if (property.first == "Version")
759 {
James Feist5b4aa862018-08-16 14:07:01 -0700760 const std::string* value =
Jennifer Leeca537922018-08-10 10:07:30 -0700761 mapbox::getPtr<const std::string>(
762 property.second);
763 if (value == nullptr)
764 {
765 continue;
766 }
767 asyncResp->res
768 .jsonValue["FirmwareVersion"] = *value;
769 }
770 }
771 }
772 }
773 }
774 },
775 "xyz.openbmc_project.Software.BMC.Updater",
776 "/xyz/openbmc_project/software",
777 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
James Feist5b4aa862018-08-16 14:07:01 -0700778 getPidValues(asyncResp);
779 }
James Feist83ff9ab2018-08-31 10:18:24 -0700780 void setPidValues(std::shared_ptr<AsyncResp> response,
781 const nlohmann::json& data)
782 {
783 // todo(james): might make sense to do a mapper call here if this
784 // interface gets more traction
785 crow::connections::systemBus->async_method_call(
786 [response,
787 data](const boost::system::error_code ec,
788 const dbus::utility::ManagedObjectType& managedObj) {
789 if (ec)
790 {
791 BMCWEB_LOG_ERROR << "Error communicating to Entity Manager";
Jason M. Bills35a62c72018-10-09 12:45:45 -0700792 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700793 return;
794 }
795 for (const auto& type : data.items())
796 {
797 if (!type.value().is_object())
798 {
799 BMCWEB_LOG_ERROR << "Illegal Type " << type.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700800 messages::propertyValueFormatError(
801 response->res, type.value(), type.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700802 return;
803 }
804 for (const auto& record : type.value().items())
805 {
806 const std::string& name = record.key();
807 auto pathItr =
808 std::find_if(managedObj.begin(), managedObj.end(),
809 [&name](const auto& obj) {
810 return boost::algorithm::ends_with(
811 obj.first.str, name);
812 });
813 boost::container::flat_map<
814 std::string, dbus::utility::DbusVariantType>
815 output;
816
817 output.reserve(16); // The pid interface length
818
819 // determines if we're patching entity-manager or
820 // creating a new object
821 bool createNewObject = (pathItr == managedObj.end());
822 if (type.key() == "PidControllers" ||
823 type.key() == "FanControllers")
824 {
825 if (!createNewObject &&
826 pathItr->second.find(pidConfigurationIface) ==
827 pathItr->second.end())
828 {
829 createNewObject = true;
830 }
831 }
832 else if (!createNewObject &&
833 pathItr->second.find(
834 pidZoneConfigurationIface) ==
835 pathItr->second.end())
836 {
837 createNewObject = true;
838 }
839 output["Name"] =
840 boost::replace_all_copy(name, "_", " ");
841
842 std::string chassis;
843 CreatePIDRet ret = createPidInterface(
844 response, type.key(), record.value(),
845 pathItr->first.str, managedObj, createNewObject,
846 output, chassis);
847 if (ret == CreatePIDRet::fail)
848 {
849 return;
850 }
851 else if (ret == CreatePIDRet::del)
852 {
853 continue;
854 }
855
856 if (!createNewObject)
857 {
858 for (const auto& property : output)
859 {
860 const char* iface =
861 type.key() == "FanZones"
862 ? pidZoneConfigurationIface
863 : pidConfigurationIface;
864 crow::connections::systemBus->async_method_call(
865 [response,
866 propertyName{std::string(property.first)}](
867 const boost::system::error_code ec) {
868 if (ec)
869 {
870 BMCWEB_LOG_ERROR
871 << "Error patching "
872 << propertyName << ": " << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700873 messages::internalError(
874 response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700875 }
876 },
877 "xyz.openbmc_project.EntityManager",
878 pathItr->first.str,
879 "org.freedesktop.DBus.Properties", "Set",
880 std::string(iface), property.first,
881 property.second);
882 }
883 }
884 else
885 {
886 if (chassis.empty())
887 {
888 BMCWEB_LOG_ERROR
889 << "Failed to get chassis from config";
Jason M. Bills35a62c72018-10-09 12:45:45 -0700890 messages::invalidObject(response->res, name);
James Feist83ff9ab2018-08-31 10:18:24 -0700891 return;
892 }
893
894 bool foundChassis = false;
895 for (const auto& obj : managedObj)
896 {
897 if (boost::algorithm::ends_with(obj.first.str,
898 chassis))
899 {
900 chassis = obj.first.str;
901 foundChassis = true;
902 break;
903 }
904 }
905 if (!foundChassis)
906 {
907 BMCWEB_LOG_ERROR
908 << "Failed to find chassis on dbus";
Jason M. Bills35a62c72018-10-09 12:45:45 -0700909 messages::resourceMissingAtURI(
910 response->res,
911 "/redfish/v1/Chassis/" + chassis);
James Feist83ff9ab2018-08-31 10:18:24 -0700912 return;
913 }
914
915 crow::connections::systemBus->async_method_call(
916 [response](const boost::system::error_code ec) {
917 if (ec)
918 {
919 BMCWEB_LOG_ERROR
920 << "Error Adding Pid Object " << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700921 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700922 }
923 },
924 "xyz.openbmc_project.EntityManager", chassis,
925 "xyz.openbmc_project.AddObject", "AddObject",
926 output);
927 }
928 }
929 }
930 },
931 "xyz.openbmc_project.EntityManager", "/", objectManagerIface,
932 "GetManagedObjects");
933 }
James Feist5b4aa862018-08-16 14:07:01 -0700934
935 void doPatch(crow::Response& res, const crow::Request& req,
936 const std::vector<std::string>& params) override
937 {
James Feist83ff9ab2018-08-31 10:18:24 -0700938 nlohmann::json patch;
939 if (!json_util::processJsonFromRequest(res, req, patch))
940 {
941 return;
942 }
943 std::shared_ptr<AsyncResp> response = std::make_shared<AsyncResp>(res);
944 for (const auto& topLevel : patch.items())
945 {
946 if (topLevel.key() == "Oem")
947 {
948 if (!topLevel.value().is_object())
949 {
950 BMCWEB_LOG_ERROR << "Bad Patch " << topLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700951 messages::propertyValueFormatError(
952 response->res, topLevel.key(), "OemManager.Oem");
James Feist83ff9ab2018-08-31 10:18:24 -0700953 return;
954 }
955 }
956 else
957 {
958 BMCWEB_LOG_ERROR << "Bad Patch " << topLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700959 messages::propertyUnknown(response->res, topLevel.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700960 return;
961 }
962 for (const auto& oemLevel : topLevel.value().items())
963 {
964 if (oemLevel.key() == "OpenBmc")
965 {
966 if (!oemLevel.value().is_object())
967 {
968 BMCWEB_LOG_ERROR << "Bad Patch " << oemLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700969 messages::propertyValueFormatError(
970 response->res, topLevel.key(),
971 "OemManager.OpenBmc");
James Feist83ff9ab2018-08-31 10:18:24 -0700972 return;
973 }
974 for (const auto& typeLevel : oemLevel.value().items())
975 {
976
977 if (typeLevel.key() == "Fan")
978 {
979 if (!typeLevel.value().is_object())
980 {
981 BMCWEB_LOG_ERROR << "Bad Patch "
982 << typeLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700983 messages::propertyValueFormatError(
984 response->res, typeLevel.value().dump(),
985 typeLevel.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700986 return;
987 }
988 setPidValues(response,
989 std::move(typeLevel.value()));
990 }
991 else
992 {
993 BMCWEB_LOG_ERROR << "Bad Patch " << typeLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700994 messages::propertyUnknown(response->res,
995 typeLevel.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700996 return;
997 }
998 }
999 }
1000 else
1001 {
1002 BMCWEB_LOG_ERROR << "Bad Patch " << oemLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -07001003 messages::propertyUnknown(response->res, oemLevel.key());
James Feist83ff9ab2018-08-31 10:18:24 -07001004 return;
1005 }
1006 }
1007 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001008 }
1009
1010 std::string getDateTime() const
1011 {
1012 std::array<char, 128> dateTime;
1013 std::string redfishDateTime("0000-00-00T00:00:00Z00:00");
1014 std::time_t time = std::time(nullptr);
1015
1016 if (std::strftime(dateTime.begin(), dateTime.size(), "%FT%T%z",
1017 std::localtime(&time)))
1018 {
1019 // insert the colon required by the ISO 8601 standard
1020 redfishDateTime = std::string(dateTime.data());
1021 redfishDateTime.insert(redfishDateTime.end() - 2, ':');
1022 }
1023
1024 return redfishDateTime;
1025 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001026};
1027
Ed Tanous1abe55e2018-09-05 08:30:59 -07001028class ManagerCollection : public Node
1029{
1030 public:
James Feist5b4aa862018-08-16 14:07:01 -07001031 ManagerCollection(CrowApp& app) : Node(app, "/redfish/v1/Managers/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001032 {
1033 Node::json["@odata.id"] = "/redfish/v1/Managers";
1034 Node::json["@odata.type"] = "#ManagerCollection.ManagerCollection";
1035 Node::json["@odata.context"] =
1036 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection";
1037 Node::json["Name"] = "Manager Collection";
1038 Node::json["Members@odata.count"] = 1;
James Feist5b4aa862018-08-16 14:07:01 -07001039 Node::json["Members"] = {{{"@odata.id", "/redfish/v1/Managers/bmc"}}};
Ed Tanous3ebd75f2018-03-05 18:20:01 -08001040
Ed Tanous1abe55e2018-09-05 08:30:59 -07001041 entityPrivileges = {
1042 {boost::beast::http::verb::get, {{"Login"}}},
1043 {boost::beast::http::verb::head, {{"Login"}}},
1044 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1045 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1046 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1047 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1048 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001049
Ed Tanous1abe55e2018-09-05 08:30:59 -07001050 private:
James Feist5b4aa862018-08-16 14:07:01 -07001051 void doGet(crow::Response& res, const crow::Request& req,
1052 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -07001053 {
James Feist83ff9ab2018-08-31 10:18:24 -07001054 // Collections don't include the static data added by SubRoute
1055 // because it has a duplicate entry for members
Ed Tanous1abe55e2018-09-05 08:30:59 -07001056 res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
1057 res.jsonValue["@odata.type"] = "#ManagerCollection.ManagerCollection";
1058 res.jsonValue["@odata.context"] =
1059 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection";
1060 res.jsonValue["Name"] = "Manager Collection";
1061 res.jsonValue["Members@odata.count"] = 1;
1062 res.jsonValue["Members"] = {
James Feist5b4aa862018-08-16 14:07:01 -07001063 {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001064 res.end();
1065 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001066};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001067} // namespace redfish