blob: daa441d579595bb7514dc363460c736cfa4f868f [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 /**
Jennifer Leeed5befb2018-08-10 11:29:45 -070047 * Function handles POST method request.
48 * Analyzes POST body message before sends Reset request data to dbus.
49 * OpenBMC allows for ResetType is GracefulRestart only.
50 */
51 void doPost(crow::Response& res, const crow::Request& req,
52 const std::vector<std::string>& params) override
53 {
54 std::string resetType;
55
56 if (!json_util::readJson(req, res, "ResetType", resetType))
57 {
58 return;
59 }
60
61 if (resetType != "GracefulRestart")
62 {
63 res.result(boost::beast::http::status::bad_request);
64 messages::actionParameterNotSupported(res, resetType, "ResetType");
65 BMCWEB_LOG_ERROR << "Request incorrect action parameter: "
66 << resetType;
67 res.end();
68 return;
69 }
70 doBMCGracefulRestart(res, req, params);
71 }
72
73 /**
74 * Function transceives data with dbus directly.
75 * All BMC state properties will be retrieved before sending reset request.
76 */
77 void doBMCGracefulRestart(crow::Response& res, const crow::Request& req,
78 const std::vector<std::string>& params)
79 {
80 const char* processName = "xyz.openbmc_project.State.BMC";
81 const char* objectPath = "/xyz/openbmc_project/state/bmc0";
82 const char* interfaceName = "xyz.openbmc_project.State.BMC";
83 const std::string& propertyValue =
84 "xyz.openbmc_project.State.BMC.Transition.Reboot";
85 const char* destProperty = "RequestedBMCTransition";
86
87 // Create the D-Bus variant for D-Bus call.
88 VariantType dbusPropertyValue(propertyValue);
89
90 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
91
92 crow::connections::systemBus->async_method_call(
93 [asyncResp](const boost::system::error_code ec) {
94 // Use "Set" method to set the property value.
95 if (ec)
96 {
97 BMCWEB_LOG_ERROR << "[Set] Bad D-Bus request error: " << ec;
98 messages::internalError(asyncResp->res);
99 return;
100 }
101
102 messages::success(asyncResp->res);
103 },
104 processName, objectPath, "org.freedesktop.DBus.Properties", "Set",
105 interfaceName, destProperty, dbusPropertyValue);
106 }
107};
108
James Feist5b4aa862018-08-16 14:07:01 -0700109static constexpr const char* objectManagerIface =
110 "org.freedesktop.DBus.ObjectManager";
111static constexpr const char* pidConfigurationIface =
112 "xyz.openbmc_project.Configuration.Pid";
113static constexpr const char* pidZoneConfigurationIface =
114 "xyz.openbmc_project.Configuration.Pid.Zone";
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100115
James Feist5b4aa862018-08-16 14:07:01 -0700116static void asyncPopulatePid(const std::string& connection,
117 const std::string& path,
118 std::shared_ptr<AsyncResp> asyncResp)
119{
120
121 crow::connections::systemBus->async_method_call(
122 [asyncResp](const boost::system::error_code ec,
123 const dbus::utility::ManagedObjectType& managedObj) {
124 if (ec)
125 {
126 BMCWEB_LOG_ERROR << ec;
James Feist5b4aa862018-08-16 14:07:01 -0700127 asyncResp->res.jsonValue.clear();
Jason M. Billsf12894f2018-10-09 12:45:45 -0700128 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700129 return;
130 }
131 nlohmann::json& configRoot =
132 asyncResp->res.jsonValue["Oem"]["OpenBmc"]["Fan"];
133 nlohmann::json& fans = configRoot["FanControllers"];
134 fans["@odata.type"] = "#OemManager.FanControllers";
135 fans["@odata.context"] =
136 "/redfish/v1/$metadata#OemManager.FanControllers";
137 fans["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc/"
138 "Fan/FanControllers";
139
140 nlohmann::json& pids = configRoot["PidControllers"];
141 pids["@odata.type"] = "#OemManager.PidControllers";
142 pids["@odata.context"] =
143 "/redfish/v1/$metadata#OemManager.PidControllers";
144 pids["@odata.id"] =
145 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/PidControllers";
146
147 nlohmann::json& zones = configRoot["FanZones"];
148 zones["@odata.id"] =
149 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones";
150 zones["@odata.type"] = "#OemManager.FanZones";
151 zones["@odata.context"] =
152 "/redfish/v1/$metadata#OemManager.FanZones";
153 configRoot["@odata.id"] =
154 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan";
155 configRoot["@odata.type"] = "#OemManager.Fan";
156 configRoot["@odata.context"] =
157 "/redfish/v1/$metadata#OemManager.Fan";
158
159 bool propertyError = false;
160 for (const auto& pathPair : managedObj)
161 {
162 for (const auto& intfPair : pathPair.second)
163 {
164 if (intfPair.first != pidConfigurationIface &&
165 intfPair.first != pidZoneConfigurationIface)
166 {
167 continue;
168 }
169 auto findName = intfPair.second.find("Name");
170 if (findName == intfPair.second.end())
171 {
172 BMCWEB_LOG_ERROR << "Pid Field missing Name";
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800173 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700174 return;
175 }
176 const std::string* namePtr =
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800177 sdbusplus::message::variant_ns::get_if<std::string>(
178 &findName->second);
James Feist5b4aa862018-08-16 14:07:01 -0700179 if (namePtr == nullptr)
180 {
181 BMCWEB_LOG_ERROR << "Pid Name Field illegal";
182 return;
183 }
184
185 std::string name = *namePtr;
186 dbus::utility::escapePathForDbus(name);
187 if (intfPair.first == pidZoneConfigurationIface)
188 {
189 std::string chassis;
190 if (!dbus::utility::getNthStringFromPath(
191 pathPair.first.str, 5, chassis))
192 {
193 chassis = "#IllegalValue";
194 }
195 nlohmann::json& zone = zones[name];
196 zone["Chassis"] = {
197 {"@odata.id", "/redfish/v1/Chassis/" + chassis}};
198 zone["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/"
199 "OpenBmc/Fan/FanZones/" +
200 name;
201 zone["@odata.type"] = "#OemManager.FanZone";
202 zone["@odata.context"] =
203 "/redfish/v1/$metadata#OemManager.FanZone";
204 }
205
206 for (const auto& propertyPair : intfPair.second)
207 {
208 if (propertyPair.first == "Type" ||
209 propertyPair.first == "Class" ||
210 propertyPair.first == "Name")
211 {
212 continue;
213 }
214
215 // zones
216 if (intfPair.first == pidZoneConfigurationIface)
217 {
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800218 const double* ptr =
219 sdbusplus::message::variant_ns::get_if<double>(
220 &propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700221 if (ptr == nullptr)
222 {
223 BMCWEB_LOG_ERROR << "Field Illegal "
224 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700225 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700226 return;
227 }
228 zones[name][propertyPair.first] = *ptr;
229 }
230
231 // pid and fans are off the same configuration
232 if (intfPair.first == pidConfigurationIface)
233 {
234 const std::string* classPtr = nullptr;
235 auto findClass = intfPair.second.find("Class");
236 if (findClass != intfPair.second.end())
237 {
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800238 classPtr =
239 sdbusplus::message::variant_ns::get_if<
240 std::string>(&findClass->second);
James Feist5b4aa862018-08-16 14:07:01 -0700241 }
242 if (classPtr == nullptr)
243 {
244 BMCWEB_LOG_ERROR << "Pid Class Field illegal";
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800245 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700246 return;
247 }
248 bool isFan = *classPtr == "fan";
249 nlohmann::json& element =
250 isFan ? fans[name] : pids[name];
251 if (isFan)
252 {
253 element["@odata.id"] =
254 "/redfish/v1/Managers/bmc#/Oem/"
255 "OpenBmc/Fan/FanControllers/" +
256 std::string(name);
257 element["@odata.type"] =
258 "#OemManager.FanController";
259
260 element["@odata.context"] =
261 "/redfish/v1/"
262 "$metadata#OemManager.FanController";
263 }
264 else
265 {
266 element["@odata.id"] =
267 "/redfish/v1/Managers/bmc#/Oem/"
268 "OpenBmc/Fan/PidControllers/" +
269 std::string(name);
270 element["@odata.type"] =
271 "#OemManager.PidController";
272 element["@odata.context"] =
273 "/redfish/v1/$metadata"
274 "#OemManager.PidController";
275 }
276
277 if (propertyPair.first == "Zones")
278 {
279 const std::vector<std::string>* inputs =
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800280 sdbusplus::message::variant_ns::get_if<
281 std::vector<std::string>>(
282 &propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700283
284 if (inputs == nullptr)
285 {
286 BMCWEB_LOG_ERROR
287 << "Zones Pid Field Illegal";
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800288 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700289 return;
290 }
291 auto& data = element[propertyPair.first];
292 data = nlohmann::json::array();
293 for (std::string itemCopy : *inputs)
294 {
295 dbus::utility::escapePathForDbus(itemCopy);
296 data.push_back(
297 {{"@odata.id",
298 "/redfish/v1/Managers/bmc#/Oem/"
299 "OpenBmc/Fan/FanZones/" +
300 itemCopy}});
301 }
302 }
303 // todo(james): may never happen, but this
304 // assumes configuration data referenced in the
305 // PID config is provided by the same daemon, we
306 // could add another loop to cover all cases,
307 // but I'm okay kicking this can down the road a
308 // bit
309
310 else if (propertyPair.first == "Inputs" ||
311 propertyPair.first == "Outputs")
312 {
313 auto& data = element[propertyPair.first];
314 const std::vector<std::string>* inputs =
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800315 sdbusplus::message::variant_ns::get_if<
316 std::vector<std::string>>(
317 &propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700318
319 if (inputs == nullptr)
320 {
321 BMCWEB_LOG_ERROR << "Field Illegal "
322 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700323 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700324 return;
325 }
326 data = *inputs;
327 } // doubles
328 else if (propertyPair.first ==
329 "FFGainCoefficient" ||
330 propertyPair.first == "FFOffCoefficient" ||
331 propertyPair.first == "ICoefficient" ||
332 propertyPair.first == "ILimitMax" ||
333 propertyPair.first == "ILimitMin" ||
334 propertyPair.first == "OutLimitMax" ||
335 propertyPair.first == "OutLimitMin" ||
336 propertyPair.first == "PCoefficient" ||
337 propertyPair.first == "SlewNeg" ||
338 propertyPair.first == "SlewPos")
339 {
340 const double* ptr =
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800341 sdbusplus::message::variant_ns::get_if<
342 double>(&propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700343 if (ptr == nullptr)
344 {
345 BMCWEB_LOG_ERROR << "Field Illegal "
346 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700347 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700348 return;
349 }
350 element[propertyPair.first] = *ptr;
351 }
352 }
353 }
354 }
355 }
356 },
357 connection, path, objectManagerIface, "GetManagedObjects");
358}
Jennifer Leeca537922018-08-10 10:07:30 -0700359
James Feist83ff9ab2018-08-31 10:18:24 -0700360enum class CreatePIDRet
361{
362 fail,
363 del,
364 patch
365};
366
367static CreatePIDRet createPidInterface(
368 const std::shared_ptr<AsyncResp>& response, const std::string& type,
369 const nlohmann::json& record, const std::string& path,
370 const dbus::utility::ManagedObjectType& managedObj, bool createNewObject,
371 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>&
372 output,
373 std::string& chassis)
374{
375
376 if (type == "PidControllers" || type == "FanControllers")
377 {
378 if (createNewObject)
379 {
380 output["Class"] = type == "PidControllers" ? std::string("temp")
381 : std::string("fan");
382 output["Type"] = std::string("Pid");
383 }
384 else if (record == nullptr)
385 {
386 // delete interface
387 crow::connections::systemBus->async_method_call(
388 [response,
389 path{std::string(path)}](const boost::system::error_code ec) {
390 if (ec)
391 {
392 BMCWEB_LOG_ERROR << "Error patching " << path << ": "
393 << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700394 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700395 }
396 },
397 "xyz.openbmc_project.EntityManager", path,
398 pidConfigurationIface, "Delete");
399 return CreatePIDRet::del;
400 }
401
402 for (auto& field : record.items())
403 {
404 if (field.key() == "Zones")
405 {
406 if (!field.value().is_array())
407 {
408 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700409 messages::propertyValueFormatError(
410 response->res, field.value(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700411 return CreatePIDRet::fail;
412 }
413 std::vector<std::string> inputs;
414 for (const auto& odata : field.value().items())
415 {
416 for (const auto& value : odata.value().items())
417 {
418 const std::string* path =
419 value.value().get_ptr<const std::string*>();
420 if (path == nullptr)
421 {
422 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700423 messages::propertyValueFormatError(
424 response->res, field.value().dump(),
425 field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700426 return CreatePIDRet::fail;
427 }
428 std::string input;
429 if (!dbus::utility::getNthStringFromPath(*path, 4,
430 input))
431 {
432 BMCWEB_LOG_ERROR << "Got invalid path " << *path;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700433 messages::propertyValueFormatError(
434 response->res, field.value().dump(),
435 field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700436 return CreatePIDRet::fail;
437 }
438 boost::replace_all(input, "_", " ");
439 inputs.emplace_back(std::move(input));
440 }
441 }
442 output["Zones"] = std::move(inputs);
443 }
444 else if (field.key() == "Inputs" || field.key() == "Outputs")
445 {
446 if (!field.value().is_array())
447 {
448 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700449 messages::propertyValueFormatError(
450 response->res, field.value().dump(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700451 return CreatePIDRet::fail;
452 }
453 std::vector<std::string> inputs;
454 for (const auto& value : field.value().items())
455 {
456 const std::string* sensor =
457 value.value().get_ptr<const std::string*>();
458
459 if (sensor == nullptr)
460 {
461 BMCWEB_LOG_ERROR << "Illegal Type "
462 << field.value().dump();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700463 messages::propertyValueFormatError(
464 response->res, field.value().dump(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700465 return CreatePIDRet::fail;
466 }
467
468 std::string input =
469 boost::replace_all_copy(*sensor, "_", " ");
470 inputs.push_back(std::move(input));
471 // try to find the sensor in the
472 // configuration
473 if (chassis.empty())
474 {
475 std::find_if(
476 managedObj.begin(), managedObj.end(),
477 [&chassis, sensor](const auto& obj) {
478 if (boost::algorithm::ends_with(obj.first.str,
479 *sensor))
480 {
481 return dbus::utility::getNthStringFromPath(
482 obj.first.str, 5, chassis);
483 }
484 return false;
485 });
486 }
487 }
488 output[field.key()] = inputs;
489 }
490
491 // doubles
492 else if (field.key() == "FFGainCoefficient" ||
493 field.key() == "FFOffCoefficient" ||
494 field.key() == "ICoefficient" ||
495 field.key() == "ILimitMax" || field.key() == "ILimitMin" ||
496 field.key() == "OutLimitMax" ||
497 field.key() == "OutLimitMin" ||
498 field.key() == "PCoefficient" ||
499 field.key() == "SetPoint" || field.key() == "SlewNeg" ||
500 field.key() == "SlewPos")
501 {
502 const double* ptr = field.value().get_ptr<const double*>();
503 if (ptr == nullptr)
504 {
505 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700506 messages::propertyValueFormatError(
507 response->res, field.value().dump(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700508 return CreatePIDRet::fail;
509 }
510 output[field.key()] = *ptr;
511 }
512
513 else
514 {
515 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700516 messages::propertyUnknown(response->res, field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700517 return CreatePIDRet::fail;
518 }
519 }
520 }
521 else if (type == "FanZones")
522 {
523 if (!createNewObject && record == nullptr)
524 {
525 // delete interface
526 crow::connections::systemBus->async_method_call(
527 [response,
528 path{std::string(path)}](const boost::system::error_code ec) {
529 if (ec)
530 {
531 BMCWEB_LOG_ERROR << "Error patching " << path << ": "
532 << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700533 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700534 }
535 },
536 "xyz.openbmc_project.EntityManager", path,
537 pidZoneConfigurationIface, "Delete");
538 return CreatePIDRet::del;
539 }
540 output["Type"] = std::string("Pid.Zone");
541
542 for (auto& field : record.items())
543 {
544 if (field.key() == "Chassis")
545 {
546 const std::string* chassisId = nullptr;
547 for (const auto& id : field.value().items())
548 {
549 if (id.key() != "@odata.id")
550 {
551 BMCWEB_LOG_ERROR << "Illegal Type " << id.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700552 messages::propertyUnknown(response->res, field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700553 return CreatePIDRet::fail;
554 }
555 chassisId = id.value().get_ptr<const std::string*>();
556 if (chassisId == nullptr)
557 {
Jason M. Bills35a62c72018-10-09 12:45:45 -0700558 messages::createFailedMissingReqProperties(
559 response->res, field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700560 return CreatePIDRet::fail;
561 }
562 }
563
564 // /refish/v1/chassis/chassis_name/
565 if (!dbus::utility::getNthStringFromPath(*chassisId, 3,
566 chassis))
567 {
568 BMCWEB_LOG_ERROR << "Got invalid path " << *chassisId;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700569 messages::invalidObject(response->res, *chassisId);
James Feist83ff9ab2018-08-31 10:18:24 -0700570 return CreatePIDRet::fail;
571 }
572 }
573 else if (field.key() == "FailSafePercent" ||
574 field.key() == "MinThermalRpm")
575 {
576 const double* ptr = field.value().get_ptr<const double*>();
577 if (ptr == nullptr)
578 {
579 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700580 messages::propertyValueFormatError(
581 response->res, field.value().dump(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700582 return CreatePIDRet::fail;
583 }
584 output[field.key()] = *ptr;
585 }
586 else
587 {
588 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700589 messages::propertyUnknown(response->res, field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700590 return CreatePIDRet::fail;
591 }
592 }
593 }
594 else
595 {
596 BMCWEB_LOG_ERROR << "Illegal Type " << type;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700597 messages::propertyUnknown(response->res, type);
James Feist83ff9ab2018-08-31 10:18:24 -0700598 return CreatePIDRet::fail;
599 }
600 return CreatePIDRet::patch;
601}
602
Ed Tanous1abe55e2018-09-05 08:30:59 -0700603class Manager : public Node
604{
605 public:
James Feist5b4aa862018-08-16 14:07:01 -0700606 Manager(CrowApp& app) : Node(app, "/redfish/v1/Managers/bmc/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700607 {
Ed Tanous0f74e642018-11-12 15:17:05 -0800608 uuid = app.template getMiddleware<crow::persistent_data::Middleware>()
609 .systemUuid;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700610 entityPrivileges = {
611 {boost::beast::http::verb::get, {{"Login"}}},
612 {boost::beast::http::verb::head, {{"Login"}}},
613 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
614 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
615 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
616 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100617 }
618
Ed Tanous1abe55e2018-09-05 08:30:59 -0700619 private:
James Feist5b4aa862018-08-16 14:07:01 -0700620 void getPidValues(std::shared_ptr<AsyncResp> asyncResp)
621 {
622 crow::connections::systemBus->async_method_call(
623 [asyncResp](const boost::system::error_code ec,
624 const crow::openbmc_mapper::GetSubTreeType& subtree) {
625 if (ec)
626 {
627 BMCWEB_LOG_ERROR << ec;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700628 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700629 return;
630 }
631
632 // create map of <connection, path to objMgr>>
633 boost::container::flat_map<std::string, std::string>
634 objectMgrPaths;
James Feist6bce33b2018-10-22 12:05:56 -0700635 boost::container::flat_set<std::string> calledConnections;
James Feist5b4aa862018-08-16 14:07:01 -0700636 for (const auto& pathGroup : subtree)
637 {
638 for (const auto& connectionGroup : pathGroup.second)
639 {
James Feist6bce33b2018-10-22 12:05:56 -0700640 auto findConnection =
641 calledConnections.find(connectionGroup.first);
642 if (findConnection != calledConnections.end())
643 {
644 break;
645 }
James Feist5b4aa862018-08-16 14:07:01 -0700646 for (const std::string& interface :
647 connectionGroup.second)
648 {
649 if (interface == objectManagerIface)
650 {
651 objectMgrPaths[connectionGroup.first] =
652 pathGroup.first;
653 }
654 // this list is alphabetical, so we
655 // should have found the objMgr by now
656 if (interface == pidConfigurationIface ||
657 interface == pidZoneConfigurationIface)
658 {
659 auto findObjMgr =
660 objectMgrPaths.find(connectionGroup.first);
661 if (findObjMgr == objectMgrPaths.end())
662 {
663 BMCWEB_LOG_DEBUG << connectionGroup.first
664 << "Has no Object Manager";
665 continue;
666 }
James Feist6bce33b2018-10-22 12:05:56 -0700667
668 calledConnections.insert(connectionGroup.first);
669
James Feist5b4aa862018-08-16 14:07:01 -0700670 asyncPopulatePid(findObjMgr->first,
671 findObjMgr->second, asyncResp);
672 break;
673 }
674 }
675 }
676 }
677 },
678 "xyz.openbmc_project.ObjectMapper",
679 "/xyz/openbmc_project/object_mapper",
680 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
681 std::array<const char*, 3>{pidConfigurationIface,
682 pidZoneConfigurationIface,
683 objectManagerIface});
684 }
685
686 void doGet(crow::Response& res, const crow::Request& req,
687 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700688 {
Ed Tanous0f74e642018-11-12 15:17:05 -0800689 res.jsonValue["@odata.id"] = "/redfish/v1/Managers/bmc";
690 res.jsonValue["@odata.type"] = "#Manager.v1_3_0.Manager";
691 res.jsonValue["@odata.context"] =
692 "/redfish/v1/$metadata#Manager.Manager";
693 res.jsonValue["Id"] = "bmc";
694 res.jsonValue["Name"] = "OpenBmc Manager";
695 res.jsonValue["Description"] = "Baseboard Management Controller";
696 res.jsonValue["PowerState"] = "On";
697 res.jsonValue["ManagerType"] = "BMC";
Ed Tanous75176582018-12-14 08:14:34 -0800698 res.jsonValue["UUID"] = uuid;
699 res.jsonValue["Model"] = "OpenBmc"; // TODO(ed), get model
Ed Tanous0f74e642018-11-12 15:17:05 -0800700
701 res.jsonValue["LogServices"] = {
702 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices"}};
703
704 res.jsonValue["NetworkProtocol"] = {
705 {"@odata.id", "/redfish/v1/Managers/bmc/NetworkProtocol"}};
706
707 res.jsonValue["EthernetInterfaces"] = {
708 {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces"}};
709 // default oem data
710 nlohmann::json& oem = res.jsonValue["Oem"];
711 nlohmann::json& oemOpenbmc = oem["OpenBmc"];
712 oem["@odata.type"] = "#OemManager.Oem";
713 oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem";
714 oem["@odata.context"] = "/redfish/v1/$metadata#OemManager.Oem";
715 oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc";
716 oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc";
717 oemOpenbmc["@odata.context"] =
718 "/redfish/v1/$metadata#OemManager.OpenBmc";
719
Jennifer Leeed5befb2018-08-10 11:29:45 -0700720 // Update Actions object.
Ed Tanous0f74e642018-11-12 15:17:05 -0800721 nlohmann::json& manager_reset =
722 res.jsonValue["Actions"]["#Manager.Reset"];
Jennifer Leeed5befb2018-08-10 11:29:45 -0700723 manager_reset["target"] =
724 "/redfish/v1/Managers/bmc/Actions/Manager.Reset";
725 manager_reset["ResetType@Redfish.AllowableValues"] = {
726 "GracefulRestart"};
Jennifer Leeca537922018-08-10 10:07:30 -0700727
Ed Tanous0f74e642018-11-12 15:17:05 -0800728 res.jsonValue["DateTime"] = getDateTime();
Jennifer Leeed5befb2018-08-10 11:29:45 -0700729 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
James Feist5b4aa862018-08-16 14:07:01 -0700730
Jennifer Leeca537922018-08-10 10:07:30 -0700731 crow::connections::systemBus->async_method_call(
732 [asyncResp](const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -0700733 const dbus::utility::ManagedObjectType& resp) {
Jennifer Leeca537922018-08-10 10:07:30 -0700734 if (ec)
735 {
736 BMCWEB_LOG_ERROR << "Error while getting Software Version";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700737 messages::internalError(asyncResp->res);
Jennifer Leeca537922018-08-10 10:07:30 -0700738 return;
739 }
740
James Feist5b4aa862018-08-16 14:07:01 -0700741 for (auto& objpath : resp)
Jennifer Leeca537922018-08-10 10:07:30 -0700742 {
James Feist5b4aa862018-08-16 14:07:01 -0700743 for (auto& interface : objpath.second)
Jennifer Leeca537922018-08-10 10:07:30 -0700744 {
745 // If interface is xyz.openbmc_project.Software.Version,
746 // this is what we're looking for.
747 if (interface.first ==
748 "xyz.openbmc_project.Software.Version")
749 {
750 // Cut out everyting until last "/", ...
James Feist5b4aa862018-08-16 14:07:01 -0700751 const std::string& iface_id = objpath.first;
752 for (auto& property : interface.second)
Jennifer Leeca537922018-08-10 10:07:30 -0700753 {
754 if (property.first == "Version")
755 {
James Feist5b4aa862018-08-16 14:07:01 -0700756 const std::string* value =
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800757 sdbusplus::message::variant_ns::get_if<
758 std::string>(&property.second);
Jennifer Leeca537922018-08-10 10:07:30 -0700759 if (value == nullptr)
760 {
761 continue;
762 }
763 asyncResp->res
764 .jsonValue["FirmwareVersion"] = *value;
765 }
766 }
767 }
768 }
769 }
770 },
771 "xyz.openbmc_project.Software.BMC.Updater",
772 "/xyz/openbmc_project/software",
773 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
James Feist5b4aa862018-08-16 14:07:01 -0700774 getPidValues(asyncResp);
775 }
James Feist83ff9ab2018-08-31 10:18:24 -0700776 void setPidValues(std::shared_ptr<AsyncResp> response,
777 const nlohmann::json& data)
778 {
779 // todo(james): might make sense to do a mapper call here if this
780 // interface gets more traction
781 crow::connections::systemBus->async_method_call(
782 [response,
783 data](const boost::system::error_code ec,
784 const dbus::utility::ManagedObjectType& managedObj) {
785 if (ec)
786 {
787 BMCWEB_LOG_ERROR << "Error communicating to Entity Manager";
Jason M. Bills35a62c72018-10-09 12:45:45 -0700788 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700789 return;
790 }
791 for (const auto& type : data.items())
792 {
793 if (!type.value().is_object())
794 {
795 BMCWEB_LOG_ERROR << "Illegal Type " << type.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700796 messages::propertyValueFormatError(
797 response->res, type.value(), type.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700798 return;
799 }
800 for (const auto& record : type.value().items())
801 {
802 const std::string& name = record.key();
803 auto pathItr =
804 std::find_if(managedObj.begin(), managedObj.end(),
805 [&name](const auto& obj) {
806 return boost::algorithm::ends_with(
807 obj.first.str, name);
808 });
809 boost::container::flat_map<
810 std::string, dbus::utility::DbusVariantType>
811 output;
812
813 output.reserve(16); // The pid interface length
814
815 // determines if we're patching entity-manager or
816 // creating a new object
817 bool createNewObject = (pathItr == managedObj.end());
818 if (type.key() == "PidControllers" ||
819 type.key() == "FanControllers")
820 {
821 if (!createNewObject &&
822 pathItr->second.find(pidConfigurationIface) ==
823 pathItr->second.end())
824 {
825 createNewObject = true;
826 }
827 }
828 else if (!createNewObject &&
829 pathItr->second.find(
830 pidZoneConfigurationIface) ==
831 pathItr->second.end())
832 {
833 createNewObject = true;
834 }
835 output["Name"] =
836 boost::replace_all_copy(name, "_", " ");
837
838 std::string chassis;
839 CreatePIDRet ret = createPidInterface(
840 response, type.key(), record.value(),
841 pathItr->first.str, managedObj, createNewObject,
842 output, chassis);
843 if (ret == CreatePIDRet::fail)
844 {
845 return;
846 }
847 else if (ret == CreatePIDRet::del)
848 {
849 continue;
850 }
851
852 if (!createNewObject)
853 {
854 for (const auto& property : output)
855 {
856 const char* iface =
857 type.key() == "FanZones"
858 ? pidZoneConfigurationIface
859 : pidConfigurationIface;
860 crow::connections::systemBus->async_method_call(
861 [response,
862 propertyName{std::string(property.first)}](
863 const boost::system::error_code ec) {
864 if (ec)
865 {
866 BMCWEB_LOG_ERROR
867 << "Error patching "
868 << propertyName << ": " << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700869 messages::internalError(
870 response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700871 }
872 },
873 "xyz.openbmc_project.EntityManager",
874 pathItr->first.str,
875 "org.freedesktop.DBus.Properties", "Set",
876 std::string(iface), property.first,
877 property.second);
878 }
879 }
880 else
881 {
882 if (chassis.empty())
883 {
884 BMCWEB_LOG_ERROR
885 << "Failed to get chassis from config";
Jason M. Bills35a62c72018-10-09 12:45:45 -0700886 messages::invalidObject(response->res, name);
James Feist83ff9ab2018-08-31 10:18:24 -0700887 return;
888 }
889
890 bool foundChassis = false;
891 for (const auto& obj : managedObj)
892 {
893 if (boost::algorithm::ends_with(obj.first.str,
894 chassis))
895 {
896 chassis = obj.first.str;
897 foundChassis = true;
898 break;
899 }
900 }
901 if (!foundChassis)
902 {
903 BMCWEB_LOG_ERROR
904 << "Failed to find chassis on dbus";
Jason M. Bills35a62c72018-10-09 12:45:45 -0700905 messages::resourceMissingAtURI(
906 response->res,
907 "/redfish/v1/Chassis/" + chassis);
James Feist83ff9ab2018-08-31 10:18:24 -0700908 return;
909 }
910
911 crow::connections::systemBus->async_method_call(
912 [response](const boost::system::error_code ec) {
913 if (ec)
914 {
915 BMCWEB_LOG_ERROR
916 << "Error Adding Pid Object " << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700917 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700918 }
919 },
920 "xyz.openbmc_project.EntityManager", chassis,
921 "xyz.openbmc_project.AddObject", "AddObject",
922 output);
923 }
924 }
925 }
926 },
927 "xyz.openbmc_project.EntityManager", "/", objectManagerIface,
928 "GetManagedObjects");
929 }
James Feist5b4aa862018-08-16 14:07:01 -0700930
931 void doPatch(crow::Response& res, const crow::Request& req,
932 const std::vector<std::string>& params) override
933 {
Ed Tanous0627a2c2018-11-29 17:09:23 -0800934 std::optional<nlohmann::json> oem;
935
936 if (!json_util::readJson(req, res, "Oem", oem))
James Feist83ff9ab2018-08-31 10:18:24 -0700937 {
938 return;
939 }
Ed Tanous0627a2c2018-11-29 17:09:23 -0800940
James Feist83ff9ab2018-08-31 10:18:24 -0700941 std::shared_ptr<AsyncResp> response = std::make_shared<AsyncResp>(res);
Ed Tanous0627a2c2018-11-29 17:09:23 -0800942
943 if (oem)
James Feist83ff9ab2018-08-31 10:18:24 -0700944 {
Ed Tanous0627a2c2018-11-29 17:09:23 -0800945 for (const auto& oemLevel : oem->items())
James Feist83ff9ab2018-08-31 10:18:24 -0700946 {
947 if (oemLevel.key() == "OpenBmc")
948 {
949 if (!oemLevel.value().is_object())
950 {
951 BMCWEB_LOG_ERROR << "Bad Patch " << oemLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700952 messages::propertyValueFormatError(
Ed Tanous0627a2c2018-11-29 17:09:23 -0800953 response->res, "Oem", "OemManager.OpenBmc");
James Feist83ff9ab2018-08-31 10:18:24 -0700954 return;
955 }
956 for (const auto& typeLevel : oemLevel.value().items())
957 {
958
959 if (typeLevel.key() == "Fan")
960 {
961 if (!typeLevel.value().is_object())
962 {
963 BMCWEB_LOG_ERROR << "Bad Patch "
964 << typeLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700965 messages::propertyValueFormatError(
966 response->res, typeLevel.value().dump(),
967 typeLevel.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700968 return;
969 }
970 setPidValues(response,
971 std::move(typeLevel.value()));
972 }
973 else
974 {
975 BMCWEB_LOG_ERROR << "Bad Patch " << typeLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700976 messages::propertyUnknown(response->res,
977 typeLevel.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700978 return;
979 }
980 }
981 }
982 else
983 {
984 BMCWEB_LOG_ERROR << "Bad Patch " << oemLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700985 messages::propertyUnknown(response->res, oemLevel.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700986 return;
987 }
988 }
989 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700990 }
991
992 std::string getDateTime() const
993 {
994 std::array<char, 128> dateTime;
995 std::string redfishDateTime("0000-00-00T00:00:00Z00:00");
996 std::time_t time = std::time(nullptr);
997
998 if (std::strftime(dateTime.begin(), dateTime.size(), "%FT%T%z",
999 std::localtime(&time)))
1000 {
1001 // insert the colon required by the ISO 8601 standard
1002 redfishDateTime = std::string(dateTime.data());
1003 redfishDateTime.insert(redfishDateTime.end() - 2, ':');
1004 }
1005
1006 return redfishDateTime;
1007 }
Ed Tanous0f74e642018-11-12 15:17:05 -08001008
1009 std::string uuid;
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001010};
1011
Ed Tanous1abe55e2018-09-05 08:30:59 -07001012class ManagerCollection : public Node
1013{
1014 public:
James Feist5b4aa862018-08-16 14:07:01 -07001015 ManagerCollection(CrowApp& app) : Node(app, "/redfish/v1/Managers/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001016 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001017 entityPrivileges = {
1018 {boost::beast::http::verb::get, {{"Login"}}},
1019 {boost::beast::http::verb::head, {{"Login"}}},
1020 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1021 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1022 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1023 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1024 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001025
Ed Tanous1abe55e2018-09-05 08:30:59 -07001026 private:
James Feist5b4aa862018-08-16 14:07:01 -07001027 void doGet(crow::Response& res, const crow::Request& req,
1028 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -07001029 {
James Feist83ff9ab2018-08-31 10:18:24 -07001030 // Collections don't include the static data added by SubRoute
1031 // because it has a duplicate entry for members
Ed Tanous1abe55e2018-09-05 08:30:59 -07001032 res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
1033 res.jsonValue["@odata.type"] = "#ManagerCollection.ManagerCollection";
1034 res.jsonValue["@odata.context"] =
1035 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection";
1036 res.jsonValue["Name"] = "Manager Collection";
1037 res.jsonValue["Members@odata.count"] = 1;
1038 res.jsonValue["Members"] = {
James Feist5b4aa862018-08-16 14:07:01 -07001039 {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001040 res.end();
1041 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001042};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001043} // namespace redfish