blob: f3c3ec32c77811f72f939d861d13d560f13bd277 [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";
698 res.jsonValue["UUID"] =
699
700 res.jsonValue["Model"] = "OpenBmc"; // TODO(ed), get model
701
702 res.jsonValue["LogServices"] = {
703 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices"}};
704
705 res.jsonValue["NetworkProtocol"] = {
706 {"@odata.id", "/redfish/v1/Managers/bmc/NetworkProtocol"}};
707
708 res.jsonValue["EthernetInterfaces"] = {
709 {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces"}};
710 // default oem data
711 nlohmann::json& oem = res.jsonValue["Oem"];
712 nlohmann::json& oemOpenbmc = oem["OpenBmc"];
713 oem["@odata.type"] = "#OemManager.Oem";
714 oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem";
715 oem["@odata.context"] = "/redfish/v1/$metadata#OemManager.Oem";
716 oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc";
717 oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc";
718 oemOpenbmc["@odata.context"] =
719 "/redfish/v1/$metadata#OemManager.OpenBmc";
720
Jennifer Leeed5befb2018-08-10 11:29:45 -0700721 // Update Actions object.
Ed Tanous0f74e642018-11-12 15:17:05 -0800722 nlohmann::json& manager_reset =
723 res.jsonValue["Actions"]["#Manager.Reset"];
Jennifer Leeed5befb2018-08-10 11:29:45 -0700724 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 Tanous0f74e642018-11-12 15:17:05 -0800729 res.jsonValue["DateTime"] = getDateTime();
Jennifer Leeed5befb2018-08-10 11:29:45 -0700730 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
James Feist5b4aa862018-08-16 14:07:01 -0700731
Jennifer Leeca537922018-08-10 10:07:30 -0700732 crow::connections::systemBus->async_method_call(
733 [asyncResp](const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -0700734 const dbus::utility::ManagedObjectType& resp) {
Jennifer Leeca537922018-08-10 10:07:30 -0700735 if (ec)
736 {
737 BMCWEB_LOG_ERROR << "Error while getting Software Version";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700738 messages::internalError(asyncResp->res);
Jennifer Leeca537922018-08-10 10:07:30 -0700739 return;
740 }
741
James Feist5b4aa862018-08-16 14:07:01 -0700742 for (auto& objpath : resp)
Jennifer Leeca537922018-08-10 10:07:30 -0700743 {
James Feist5b4aa862018-08-16 14:07:01 -0700744 for (auto& interface : objpath.second)
Jennifer Leeca537922018-08-10 10:07:30 -0700745 {
746 // If interface is xyz.openbmc_project.Software.Version,
747 // this is what we're looking for.
748 if (interface.first ==
749 "xyz.openbmc_project.Software.Version")
750 {
751 // Cut out everyting until last "/", ...
James Feist5b4aa862018-08-16 14:07:01 -0700752 const std::string& iface_id = objpath.first;
753 for (auto& property : interface.second)
Jennifer Leeca537922018-08-10 10:07:30 -0700754 {
755 if (property.first == "Version")
756 {
James Feist5b4aa862018-08-16 14:07:01 -0700757 const std::string* value =
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800758 sdbusplus::message::variant_ns::get_if<
759 std::string>(&property.second);
Jennifer Leeca537922018-08-10 10:07:30 -0700760 if (value == nullptr)
761 {
762 continue;
763 }
764 asyncResp->res
765 .jsonValue["FirmwareVersion"] = *value;
766 }
767 }
768 }
769 }
770 }
771 },
772 "xyz.openbmc_project.Software.BMC.Updater",
773 "/xyz/openbmc_project/software",
774 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
James Feist5b4aa862018-08-16 14:07:01 -0700775 getPidValues(asyncResp);
776 }
James Feist83ff9ab2018-08-31 10:18:24 -0700777 void setPidValues(std::shared_ptr<AsyncResp> response,
778 const nlohmann::json& data)
779 {
780 // todo(james): might make sense to do a mapper call here if this
781 // interface gets more traction
782 crow::connections::systemBus->async_method_call(
783 [response,
784 data](const boost::system::error_code ec,
785 const dbus::utility::ManagedObjectType& managedObj) {
786 if (ec)
787 {
788 BMCWEB_LOG_ERROR << "Error communicating to Entity Manager";
Jason M. Bills35a62c72018-10-09 12:45:45 -0700789 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700790 return;
791 }
792 for (const auto& type : data.items())
793 {
794 if (!type.value().is_object())
795 {
796 BMCWEB_LOG_ERROR << "Illegal Type " << type.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700797 messages::propertyValueFormatError(
798 response->res, type.value(), type.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700799 return;
800 }
801 for (const auto& record : type.value().items())
802 {
803 const std::string& name = record.key();
804 auto pathItr =
805 std::find_if(managedObj.begin(), managedObj.end(),
806 [&name](const auto& obj) {
807 return boost::algorithm::ends_with(
808 obj.first.str, name);
809 });
810 boost::container::flat_map<
811 std::string, dbus::utility::DbusVariantType>
812 output;
813
814 output.reserve(16); // The pid interface length
815
816 // determines if we're patching entity-manager or
817 // creating a new object
818 bool createNewObject = (pathItr == managedObj.end());
819 if (type.key() == "PidControllers" ||
820 type.key() == "FanControllers")
821 {
822 if (!createNewObject &&
823 pathItr->second.find(pidConfigurationIface) ==
824 pathItr->second.end())
825 {
826 createNewObject = true;
827 }
828 }
829 else if (!createNewObject &&
830 pathItr->second.find(
831 pidZoneConfigurationIface) ==
832 pathItr->second.end())
833 {
834 createNewObject = true;
835 }
836 output["Name"] =
837 boost::replace_all_copy(name, "_", " ");
838
839 std::string chassis;
840 CreatePIDRet ret = createPidInterface(
841 response, type.key(), record.value(),
842 pathItr->first.str, managedObj, createNewObject,
843 output, chassis);
844 if (ret == CreatePIDRet::fail)
845 {
846 return;
847 }
848 else if (ret == CreatePIDRet::del)
849 {
850 continue;
851 }
852
853 if (!createNewObject)
854 {
855 for (const auto& property : output)
856 {
857 const char* iface =
858 type.key() == "FanZones"
859 ? pidZoneConfigurationIface
860 : pidConfigurationIface;
861 crow::connections::systemBus->async_method_call(
862 [response,
863 propertyName{std::string(property.first)}](
864 const boost::system::error_code ec) {
865 if (ec)
866 {
867 BMCWEB_LOG_ERROR
868 << "Error patching "
869 << propertyName << ": " << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700870 messages::internalError(
871 response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700872 }
873 },
874 "xyz.openbmc_project.EntityManager",
875 pathItr->first.str,
876 "org.freedesktop.DBus.Properties", "Set",
877 std::string(iface), property.first,
878 property.second);
879 }
880 }
881 else
882 {
883 if (chassis.empty())
884 {
885 BMCWEB_LOG_ERROR
886 << "Failed to get chassis from config";
Jason M. Bills35a62c72018-10-09 12:45:45 -0700887 messages::invalidObject(response->res, name);
James Feist83ff9ab2018-08-31 10:18:24 -0700888 return;
889 }
890
891 bool foundChassis = false;
892 for (const auto& obj : managedObj)
893 {
894 if (boost::algorithm::ends_with(obj.first.str,
895 chassis))
896 {
897 chassis = obj.first.str;
898 foundChassis = true;
899 break;
900 }
901 }
902 if (!foundChassis)
903 {
904 BMCWEB_LOG_ERROR
905 << "Failed to find chassis on dbus";
Jason M. Bills35a62c72018-10-09 12:45:45 -0700906 messages::resourceMissingAtURI(
907 response->res,
908 "/redfish/v1/Chassis/" + chassis);
James Feist83ff9ab2018-08-31 10:18:24 -0700909 return;
910 }
911
912 crow::connections::systemBus->async_method_call(
913 [response](const boost::system::error_code ec) {
914 if (ec)
915 {
916 BMCWEB_LOG_ERROR
917 << "Error Adding Pid Object " << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700918 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700919 }
920 },
921 "xyz.openbmc_project.EntityManager", chassis,
922 "xyz.openbmc_project.AddObject", "AddObject",
923 output);
924 }
925 }
926 }
927 },
928 "xyz.openbmc_project.EntityManager", "/", objectManagerIface,
929 "GetManagedObjects");
930 }
James Feist5b4aa862018-08-16 14:07:01 -0700931
932 void doPatch(crow::Response& res, const crow::Request& req,
933 const std::vector<std::string>& params) override
934 {
James Feist83ff9ab2018-08-31 10:18:24 -0700935 nlohmann::json patch;
936 if (!json_util::processJsonFromRequest(res, req, patch))
937 {
938 return;
939 }
940 std::shared_ptr<AsyncResp> response = std::make_shared<AsyncResp>(res);
941 for (const auto& topLevel : patch.items())
942 {
943 if (topLevel.key() == "Oem")
944 {
945 if (!topLevel.value().is_object())
946 {
947 BMCWEB_LOG_ERROR << "Bad Patch " << topLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700948 messages::propertyValueFormatError(
949 response->res, topLevel.key(), "OemManager.Oem");
James Feist83ff9ab2018-08-31 10:18:24 -0700950 return;
951 }
952 }
953 else
954 {
955 BMCWEB_LOG_ERROR << "Bad Patch " << topLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700956 messages::propertyUnknown(response->res, topLevel.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700957 return;
958 }
959 for (const auto& oemLevel : topLevel.value().items())
960 {
961 if (oemLevel.key() == "OpenBmc")
962 {
963 if (!oemLevel.value().is_object())
964 {
965 BMCWEB_LOG_ERROR << "Bad Patch " << oemLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700966 messages::propertyValueFormatError(
967 response->res, topLevel.key(),
968 "OemManager.OpenBmc");
James Feist83ff9ab2018-08-31 10:18:24 -0700969 return;
970 }
971 for (const auto& typeLevel : oemLevel.value().items())
972 {
973
974 if (typeLevel.key() == "Fan")
975 {
976 if (!typeLevel.value().is_object())
977 {
978 BMCWEB_LOG_ERROR << "Bad Patch "
979 << typeLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700980 messages::propertyValueFormatError(
981 response->res, typeLevel.value().dump(),
982 typeLevel.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700983 return;
984 }
985 setPidValues(response,
986 std::move(typeLevel.value()));
987 }
988 else
989 {
990 BMCWEB_LOG_ERROR << "Bad Patch " << typeLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700991 messages::propertyUnknown(response->res,
992 typeLevel.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700993 return;
994 }
995 }
996 }
997 else
998 {
999 BMCWEB_LOG_ERROR << "Bad Patch " << oemLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -07001000 messages::propertyUnknown(response->res, oemLevel.key());
James Feist83ff9ab2018-08-31 10:18:24 -07001001 return;
1002 }
1003 }
1004 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001005 }
1006
1007 std::string getDateTime() const
1008 {
1009 std::array<char, 128> dateTime;
1010 std::string redfishDateTime("0000-00-00T00:00:00Z00:00");
1011 std::time_t time = std::time(nullptr);
1012
1013 if (std::strftime(dateTime.begin(), dateTime.size(), "%FT%T%z",
1014 std::localtime(&time)))
1015 {
1016 // insert the colon required by the ISO 8601 standard
1017 redfishDateTime = std::string(dateTime.data());
1018 redfishDateTime.insert(redfishDateTime.end() - 2, ':');
1019 }
1020
1021 return redfishDateTime;
1022 }
Ed Tanous0f74e642018-11-12 15:17:05 -08001023
1024 std::string uuid;
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001025};
1026
Ed Tanous1abe55e2018-09-05 08:30:59 -07001027class ManagerCollection : public Node
1028{
1029 public:
James Feist5b4aa862018-08-16 14:07:01 -07001030 ManagerCollection(CrowApp& app) : Node(app, "/redfish/v1/Managers/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001031 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001032 entityPrivileges = {
1033 {boost::beast::http::verb::get, {{"Login"}}},
1034 {boost::beast::http::verb::head, {{"Login"}}},
1035 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1036 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1037 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1038 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1039 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001040
Ed Tanous1abe55e2018-09-05 08:30:59 -07001041 private:
James Feist5b4aa862018-08-16 14:07:01 -07001042 void doGet(crow::Response& res, const crow::Request& req,
1043 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -07001044 {
James Feist83ff9ab2018-08-31 10:18:24 -07001045 // Collections don't include the static data added by SubRoute
1046 // because it has a duplicate entry for members
Ed Tanous1abe55e2018-09-05 08:30:59 -07001047 res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
1048 res.jsonValue["@odata.type"] = "#ManagerCollection.ManagerCollection";
1049 res.jsonValue["@odata.context"] =
1050 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection";
1051 res.jsonValue["Name"] = "Manager Collection";
1052 res.jsonValue["Members@odata.count"] = 1;
1053 res.jsonValue["Members"] = {
James Feist5b4aa862018-08-16 14:07:01 -07001054 {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001055 res.end();
1056 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001057};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001058} // namespace redfish