blob: 44725b1f8f0011c4cefb46a08bad21fa419552dd [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 =
177 mapbox::getPtr<const std::string>(findName->second);
178 if (namePtr == nullptr)
179 {
180 BMCWEB_LOG_ERROR << "Pid Name Field illegal";
181 return;
182 }
183
184 std::string name = *namePtr;
185 dbus::utility::escapePathForDbus(name);
186 if (intfPair.first == pidZoneConfigurationIface)
187 {
188 std::string chassis;
189 if (!dbus::utility::getNthStringFromPath(
190 pathPair.first.str, 5, chassis))
191 {
192 chassis = "#IllegalValue";
193 }
194 nlohmann::json& zone = zones[name];
195 zone["Chassis"] = {
196 {"@odata.id", "/redfish/v1/Chassis/" + chassis}};
197 zone["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/"
198 "OpenBmc/Fan/FanZones/" +
199 name;
200 zone["@odata.type"] = "#OemManager.FanZone";
201 zone["@odata.context"] =
202 "/redfish/v1/$metadata#OemManager.FanZone";
203 }
204
205 for (const auto& propertyPair : intfPair.second)
206 {
207 if (propertyPair.first == "Type" ||
208 propertyPair.first == "Class" ||
209 propertyPair.first == "Name")
210 {
211 continue;
212 }
213
214 // zones
215 if (intfPair.first == pidZoneConfigurationIface)
216 {
217 const double* ptr = mapbox::getPtr<const double>(
218 propertyPair.second);
219 if (ptr == nullptr)
220 {
221 BMCWEB_LOG_ERROR << "Field Illegal "
222 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700223 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700224 return;
225 }
226 zones[name][propertyPair.first] = *ptr;
227 }
228
229 // pid and fans are off the same configuration
230 if (intfPair.first == pidConfigurationIface)
231 {
232 const std::string* classPtr = nullptr;
233 auto findClass = intfPair.second.find("Class");
234 if (findClass != intfPair.second.end())
235 {
236 classPtr = mapbox::getPtr<const std::string>(
237 findClass->second);
238 }
239 if (classPtr == nullptr)
240 {
241 BMCWEB_LOG_ERROR << "Pid Class Field illegal";
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800242 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700243 return;
244 }
245 bool isFan = *classPtr == "fan";
246 nlohmann::json& element =
247 isFan ? fans[name] : pids[name];
248 if (isFan)
249 {
250 element["@odata.id"] =
251 "/redfish/v1/Managers/bmc#/Oem/"
252 "OpenBmc/Fan/FanControllers/" +
253 std::string(name);
254 element["@odata.type"] =
255 "#OemManager.FanController";
256
257 element["@odata.context"] =
258 "/redfish/v1/"
259 "$metadata#OemManager.FanController";
260 }
261 else
262 {
263 element["@odata.id"] =
264 "/redfish/v1/Managers/bmc#/Oem/"
265 "OpenBmc/Fan/PidControllers/" +
266 std::string(name);
267 element["@odata.type"] =
268 "#OemManager.PidController";
269 element["@odata.context"] =
270 "/redfish/v1/$metadata"
271 "#OemManager.PidController";
272 }
273
274 if (propertyPair.first == "Zones")
275 {
276 const std::vector<std::string>* inputs =
277 mapbox::getPtr<
278 const std::vector<std::string>>(
279 propertyPair.second);
280
281 if (inputs == nullptr)
282 {
283 BMCWEB_LOG_ERROR
284 << "Zones Pid Field Illegal";
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800285 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700286 return;
287 }
288 auto& data = element[propertyPair.first];
289 data = nlohmann::json::array();
290 for (std::string itemCopy : *inputs)
291 {
292 dbus::utility::escapePathForDbus(itemCopy);
293 data.push_back(
294 {{"@odata.id",
295 "/redfish/v1/Managers/bmc#/Oem/"
296 "OpenBmc/Fan/FanZones/" +
297 itemCopy}});
298 }
299 }
300 // todo(james): may never happen, but this
301 // assumes configuration data referenced in the
302 // PID config is provided by the same daemon, we
303 // could add another loop to cover all cases,
304 // but I'm okay kicking this can down the road a
305 // bit
306
307 else if (propertyPair.first == "Inputs" ||
308 propertyPair.first == "Outputs")
309 {
310 auto& data = element[propertyPair.first];
311 const std::vector<std::string>* inputs =
312 mapbox::getPtr<
313 const std::vector<std::string>>(
314 propertyPair.second);
315
316 if (inputs == nullptr)
317 {
318 BMCWEB_LOG_ERROR << "Field Illegal "
319 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700320 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700321 return;
322 }
323 data = *inputs;
324 } // doubles
325 else if (propertyPair.first ==
326 "FFGainCoefficient" ||
327 propertyPair.first == "FFOffCoefficient" ||
328 propertyPair.first == "ICoefficient" ||
329 propertyPair.first == "ILimitMax" ||
330 propertyPair.first == "ILimitMin" ||
331 propertyPair.first == "OutLimitMax" ||
332 propertyPair.first == "OutLimitMin" ||
333 propertyPair.first == "PCoefficient" ||
334 propertyPair.first == "SlewNeg" ||
335 propertyPair.first == "SlewPos")
336 {
337 const double* ptr =
338 mapbox::getPtr<const double>(
339 propertyPair.second);
340 if (ptr == nullptr)
341 {
342 BMCWEB_LOG_ERROR << "Field Illegal "
343 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700344 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700345 return;
346 }
347 element[propertyPair.first] = *ptr;
348 }
349 }
350 }
351 }
352 }
353 },
354 connection, path, objectManagerIface, "GetManagedObjects");
355}
Jennifer Leeca537922018-08-10 10:07:30 -0700356
James Feist83ff9ab2018-08-31 10:18:24 -0700357enum class CreatePIDRet
358{
359 fail,
360 del,
361 patch
362};
363
364static CreatePIDRet createPidInterface(
365 const std::shared_ptr<AsyncResp>& response, const std::string& type,
366 const nlohmann::json& record, const std::string& path,
367 const dbus::utility::ManagedObjectType& managedObj, bool createNewObject,
368 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>&
369 output,
370 std::string& chassis)
371{
372
373 if (type == "PidControllers" || type == "FanControllers")
374 {
375 if (createNewObject)
376 {
377 output["Class"] = type == "PidControllers" ? std::string("temp")
378 : std::string("fan");
379 output["Type"] = std::string("Pid");
380 }
381 else if (record == nullptr)
382 {
383 // delete interface
384 crow::connections::systemBus->async_method_call(
385 [response,
386 path{std::string(path)}](const boost::system::error_code ec) {
387 if (ec)
388 {
389 BMCWEB_LOG_ERROR << "Error patching " << path << ": "
390 << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700391 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700392 }
393 },
394 "xyz.openbmc_project.EntityManager", path,
395 pidConfigurationIface, "Delete");
396 return CreatePIDRet::del;
397 }
398
399 for (auto& field : record.items())
400 {
401 if (field.key() == "Zones")
402 {
403 if (!field.value().is_array())
404 {
405 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700406 messages::propertyValueFormatError(
407 response->res, field.value(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700408 return CreatePIDRet::fail;
409 }
410 std::vector<std::string> inputs;
411 for (const auto& odata : field.value().items())
412 {
413 for (const auto& value : odata.value().items())
414 {
415 const std::string* path =
416 value.value().get_ptr<const std::string*>();
417 if (path == nullptr)
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().dump(),
422 field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700423 return CreatePIDRet::fail;
424 }
425 std::string input;
426 if (!dbus::utility::getNthStringFromPath(*path, 4,
427 input))
428 {
429 BMCWEB_LOG_ERROR << "Got invalid path " << *path;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700430 messages::propertyValueFormatError(
431 response->res, field.value().dump(),
432 field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700433 return CreatePIDRet::fail;
434 }
435 boost::replace_all(input, "_", " ");
436 inputs.emplace_back(std::move(input));
437 }
438 }
439 output["Zones"] = std::move(inputs);
440 }
441 else if (field.key() == "Inputs" || field.key() == "Outputs")
442 {
443 if (!field.value().is_array())
444 {
445 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700446 messages::propertyValueFormatError(
447 response->res, field.value().dump(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700448 return CreatePIDRet::fail;
449 }
450 std::vector<std::string> inputs;
451 for (const auto& value : field.value().items())
452 {
453 const std::string* sensor =
454 value.value().get_ptr<const std::string*>();
455
456 if (sensor == nullptr)
457 {
458 BMCWEB_LOG_ERROR << "Illegal Type "
459 << field.value().dump();
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
465 std::string input =
466 boost::replace_all_copy(*sensor, "_", " ");
467 inputs.push_back(std::move(input));
468 // try to find the sensor in the
469 // configuration
470 if (chassis.empty())
471 {
472 std::find_if(
473 managedObj.begin(), managedObj.end(),
474 [&chassis, sensor](const auto& obj) {
475 if (boost::algorithm::ends_with(obj.first.str,
476 *sensor))
477 {
478 return dbus::utility::getNthStringFromPath(
479 obj.first.str, 5, chassis);
480 }
481 return false;
482 });
483 }
484 }
485 output[field.key()] = inputs;
486 }
487
488 // doubles
489 else if (field.key() == "FFGainCoefficient" ||
490 field.key() == "FFOffCoefficient" ||
491 field.key() == "ICoefficient" ||
492 field.key() == "ILimitMax" || field.key() == "ILimitMin" ||
493 field.key() == "OutLimitMax" ||
494 field.key() == "OutLimitMin" ||
495 field.key() == "PCoefficient" ||
496 field.key() == "SetPoint" || field.key() == "SlewNeg" ||
497 field.key() == "SlewPos")
498 {
499 const double* ptr = field.value().get_ptr<const double*>();
500 if (ptr == nullptr)
501 {
502 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700503 messages::propertyValueFormatError(
504 response->res, field.value().dump(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700505 return CreatePIDRet::fail;
506 }
507 output[field.key()] = *ptr;
508 }
509
510 else
511 {
512 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700513 messages::propertyUnknown(response->res, field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700514 return CreatePIDRet::fail;
515 }
516 }
517 }
518 else if (type == "FanZones")
519 {
520 if (!createNewObject && record == nullptr)
521 {
522 // delete interface
523 crow::connections::systemBus->async_method_call(
524 [response,
525 path{std::string(path)}](const boost::system::error_code ec) {
526 if (ec)
527 {
528 BMCWEB_LOG_ERROR << "Error patching " << path << ": "
529 << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700530 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700531 }
532 },
533 "xyz.openbmc_project.EntityManager", path,
534 pidZoneConfigurationIface, "Delete");
535 return CreatePIDRet::del;
536 }
537 output["Type"] = std::string("Pid.Zone");
538
539 for (auto& field : record.items())
540 {
541 if (field.key() == "Chassis")
542 {
543 const std::string* chassisId = nullptr;
544 for (const auto& id : field.value().items())
545 {
546 if (id.key() != "@odata.id")
547 {
548 BMCWEB_LOG_ERROR << "Illegal Type " << id.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700549 messages::propertyUnknown(response->res, field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700550 return CreatePIDRet::fail;
551 }
552 chassisId = id.value().get_ptr<const std::string*>();
553 if (chassisId == nullptr)
554 {
Jason M. Bills35a62c72018-10-09 12:45:45 -0700555 messages::createFailedMissingReqProperties(
556 response->res, field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700557 return CreatePIDRet::fail;
558 }
559 }
560
561 // /refish/v1/chassis/chassis_name/
562 if (!dbus::utility::getNthStringFromPath(*chassisId, 3,
563 chassis))
564 {
565 BMCWEB_LOG_ERROR << "Got invalid path " << *chassisId;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700566 messages::invalidObject(response->res, *chassisId);
James Feist83ff9ab2018-08-31 10:18:24 -0700567 return CreatePIDRet::fail;
568 }
569 }
570 else if (field.key() == "FailSafePercent" ||
571 field.key() == "MinThermalRpm")
572 {
573 const double* ptr = field.value().get_ptr<const double*>();
574 if (ptr == nullptr)
575 {
576 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700577 messages::propertyValueFormatError(
578 response->res, field.value().dump(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700579 return CreatePIDRet::fail;
580 }
581 output[field.key()] = *ptr;
582 }
583 else
584 {
585 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700586 messages::propertyUnknown(response->res, field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700587 return CreatePIDRet::fail;
588 }
589 }
590 }
591 else
592 {
593 BMCWEB_LOG_ERROR << "Illegal Type " << type;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700594 messages::propertyUnknown(response->res, type);
James Feist83ff9ab2018-08-31 10:18:24 -0700595 return CreatePIDRet::fail;
596 }
597 return CreatePIDRet::patch;
598}
599
Ed Tanous1abe55e2018-09-05 08:30:59 -0700600class Manager : public Node
601{
602 public:
James Feist5b4aa862018-08-16 14:07:01 -0700603 Manager(CrowApp& app) : Node(app, "/redfish/v1/Managers/bmc/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700604 {
Ed Tanous0f74e642018-11-12 15:17:05 -0800605 uuid = app.template getMiddleware<crow::persistent_data::Middleware>()
606 .systemUuid;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700607 entityPrivileges = {
608 {boost::beast::http::verb::get, {{"Login"}}},
609 {boost::beast::http::verb::head, {{"Login"}}},
610 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
611 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
612 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
613 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100614 }
615
Ed Tanous1abe55e2018-09-05 08:30:59 -0700616 private:
James Feist5b4aa862018-08-16 14:07:01 -0700617 void getPidValues(std::shared_ptr<AsyncResp> asyncResp)
618 {
619 crow::connections::systemBus->async_method_call(
620 [asyncResp](const boost::system::error_code ec,
621 const crow::openbmc_mapper::GetSubTreeType& subtree) {
622 if (ec)
623 {
624 BMCWEB_LOG_ERROR << ec;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700625 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700626 return;
627 }
628
629 // create map of <connection, path to objMgr>>
630 boost::container::flat_map<std::string, std::string>
631 objectMgrPaths;
James Feist6bce33b2018-10-22 12:05:56 -0700632 boost::container::flat_set<std::string> calledConnections;
James Feist5b4aa862018-08-16 14:07:01 -0700633 for (const auto& pathGroup : subtree)
634 {
635 for (const auto& connectionGroup : pathGroup.second)
636 {
James Feist6bce33b2018-10-22 12:05:56 -0700637 auto findConnection =
638 calledConnections.find(connectionGroup.first);
639 if (findConnection != calledConnections.end())
640 {
641 break;
642 }
James Feist5b4aa862018-08-16 14:07:01 -0700643 for (const std::string& interface :
644 connectionGroup.second)
645 {
646 if (interface == objectManagerIface)
647 {
648 objectMgrPaths[connectionGroup.first] =
649 pathGroup.first;
650 }
651 // this list is alphabetical, so we
652 // should have found the objMgr by now
653 if (interface == pidConfigurationIface ||
654 interface == pidZoneConfigurationIface)
655 {
656 auto findObjMgr =
657 objectMgrPaths.find(connectionGroup.first);
658 if (findObjMgr == objectMgrPaths.end())
659 {
660 BMCWEB_LOG_DEBUG << connectionGroup.first
661 << "Has no Object Manager";
662 continue;
663 }
James Feist6bce33b2018-10-22 12:05:56 -0700664
665 calledConnections.insert(connectionGroup.first);
666
James Feist5b4aa862018-08-16 14:07:01 -0700667 asyncPopulatePid(findObjMgr->first,
668 findObjMgr->second, asyncResp);
669 break;
670 }
671 }
672 }
673 }
674 },
675 "xyz.openbmc_project.ObjectMapper",
676 "/xyz/openbmc_project/object_mapper",
677 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
678 std::array<const char*, 3>{pidConfigurationIface,
679 pidZoneConfigurationIface,
680 objectManagerIface});
681 }
682
683 void doGet(crow::Response& res, const crow::Request& req,
684 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700685 {
Ed Tanous0f74e642018-11-12 15:17:05 -0800686 res.jsonValue["@odata.id"] = "/redfish/v1/Managers/bmc";
687 res.jsonValue["@odata.type"] = "#Manager.v1_3_0.Manager";
688 res.jsonValue["@odata.context"] =
689 "/redfish/v1/$metadata#Manager.Manager";
690 res.jsonValue["Id"] = "bmc";
691 res.jsonValue["Name"] = "OpenBmc Manager";
692 res.jsonValue["Description"] = "Baseboard Management Controller";
693 res.jsonValue["PowerState"] = "On";
694 res.jsonValue["ManagerType"] = "BMC";
695 res.jsonValue["UUID"] =
696
697 res.jsonValue["Model"] = "OpenBmc"; // TODO(ed), get model
698
699 res.jsonValue["LogServices"] = {
700 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices"}};
701
702 res.jsonValue["NetworkProtocol"] = {
703 {"@odata.id", "/redfish/v1/Managers/bmc/NetworkProtocol"}};
704
705 res.jsonValue["EthernetInterfaces"] = {
706 {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces"}};
707 // default oem data
708 nlohmann::json& oem = res.jsonValue["Oem"];
709 nlohmann::json& oemOpenbmc = oem["OpenBmc"];
710 oem["@odata.type"] = "#OemManager.Oem";
711 oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem";
712 oem["@odata.context"] = "/redfish/v1/$metadata#OemManager.Oem";
713 oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc";
714 oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc";
715 oemOpenbmc["@odata.context"] =
716 "/redfish/v1/$metadata#OemManager.OpenBmc";
717
Jennifer Leeed5befb2018-08-10 11:29:45 -0700718 // Update Actions object.
Ed Tanous0f74e642018-11-12 15:17:05 -0800719 nlohmann::json& manager_reset =
720 res.jsonValue["Actions"]["#Manager.Reset"];
Jennifer Leeed5befb2018-08-10 11:29:45 -0700721 manager_reset["target"] =
722 "/redfish/v1/Managers/bmc/Actions/Manager.Reset";
723 manager_reset["ResetType@Redfish.AllowableValues"] = {
724 "GracefulRestart"};
Jennifer Leeca537922018-08-10 10:07:30 -0700725
Ed Tanous0f74e642018-11-12 15:17:05 -0800726 res.jsonValue["DateTime"] = getDateTime();
Jennifer Leeed5befb2018-08-10 11:29:45 -0700727 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
James Feist5b4aa862018-08-16 14:07:01 -0700728
Jennifer Leeca537922018-08-10 10:07:30 -0700729 crow::connections::systemBus->async_method_call(
730 [asyncResp](const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -0700731 const dbus::utility::ManagedObjectType& resp) {
Jennifer Leeca537922018-08-10 10:07:30 -0700732 if (ec)
733 {
734 BMCWEB_LOG_ERROR << "Error while getting Software Version";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700735 messages::internalError(asyncResp->res);
Jennifer Leeca537922018-08-10 10:07:30 -0700736 return;
737 }
738
James Feist5b4aa862018-08-16 14:07:01 -0700739 for (auto& objpath : resp)
Jennifer Leeca537922018-08-10 10:07:30 -0700740 {
James Feist5b4aa862018-08-16 14:07:01 -0700741 for (auto& interface : objpath.second)
Jennifer Leeca537922018-08-10 10:07:30 -0700742 {
743 // If interface is xyz.openbmc_project.Software.Version,
744 // this is what we're looking for.
745 if (interface.first ==
746 "xyz.openbmc_project.Software.Version")
747 {
748 // Cut out everyting until last "/", ...
James Feist5b4aa862018-08-16 14:07:01 -0700749 const std::string& iface_id = objpath.first;
750 for (auto& property : interface.second)
Jennifer Leeca537922018-08-10 10:07:30 -0700751 {
752 if (property.first == "Version")
753 {
James Feist5b4aa862018-08-16 14:07:01 -0700754 const std::string* value =
Jennifer Leeca537922018-08-10 10:07:30 -0700755 mapbox::getPtr<const std::string>(
756 property.second);
757 if (value == nullptr)
758 {
759 continue;
760 }
761 asyncResp->res
762 .jsonValue["FirmwareVersion"] = *value;
763 }
764 }
765 }
766 }
767 }
768 },
769 "xyz.openbmc_project.Software.BMC.Updater",
770 "/xyz/openbmc_project/software",
771 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
James Feist5b4aa862018-08-16 14:07:01 -0700772 getPidValues(asyncResp);
773 }
James Feist83ff9ab2018-08-31 10:18:24 -0700774 void setPidValues(std::shared_ptr<AsyncResp> response,
775 const nlohmann::json& data)
776 {
777 // todo(james): might make sense to do a mapper call here if this
778 // interface gets more traction
779 crow::connections::systemBus->async_method_call(
780 [response,
781 data](const boost::system::error_code ec,
782 const dbus::utility::ManagedObjectType& managedObj) {
783 if (ec)
784 {
785 BMCWEB_LOG_ERROR << "Error communicating to Entity Manager";
Jason M. Bills35a62c72018-10-09 12:45:45 -0700786 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700787 return;
788 }
789 for (const auto& type : data.items())
790 {
791 if (!type.value().is_object())
792 {
793 BMCWEB_LOG_ERROR << "Illegal Type " << type.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700794 messages::propertyValueFormatError(
795 response->res, type.value(), type.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700796 return;
797 }
798 for (const auto& record : type.value().items())
799 {
800 const std::string& name = record.key();
801 auto pathItr =
802 std::find_if(managedObj.begin(), managedObj.end(),
803 [&name](const auto& obj) {
804 return boost::algorithm::ends_with(
805 obj.first.str, name);
806 });
807 boost::container::flat_map<
808 std::string, dbus::utility::DbusVariantType>
809 output;
810
811 output.reserve(16); // The pid interface length
812
813 // determines if we're patching entity-manager or
814 // creating a new object
815 bool createNewObject = (pathItr == managedObj.end());
816 if (type.key() == "PidControllers" ||
817 type.key() == "FanControllers")
818 {
819 if (!createNewObject &&
820 pathItr->second.find(pidConfigurationIface) ==
821 pathItr->second.end())
822 {
823 createNewObject = true;
824 }
825 }
826 else if (!createNewObject &&
827 pathItr->second.find(
828 pidZoneConfigurationIface) ==
829 pathItr->second.end())
830 {
831 createNewObject = true;
832 }
833 output["Name"] =
834 boost::replace_all_copy(name, "_", " ");
835
836 std::string chassis;
837 CreatePIDRet ret = createPidInterface(
838 response, type.key(), record.value(),
839 pathItr->first.str, managedObj, createNewObject,
840 output, chassis);
841 if (ret == CreatePIDRet::fail)
842 {
843 return;
844 }
845 else if (ret == CreatePIDRet::del)
846 {
847 continue;
848 }
849
850 if (!createNewObject)
851 {
852 for (const auto& property : output)
853 {
854 const char* iface =
855 type.key() == "FanZones"
856 ? pidZoneConfigurationIface
857 : pidConfigurationIface;
858 crow::connections::systemBus->async_method_call(
859 [response,
860 propertyName{std::string(property.first)}](
861 const boost::system::error_code ec) {
862 if (ec)
863 {
864 BMCWEB_LOG_ERROR
865 << "Error patching "
866 << propertyName << ": " << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700867 messages::internalError(
868 response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700869 }
870 },
871 "xyz.openbmc_project.EntityManager",
872 pathItr->first.str,
873 "org.freedesktop.DBus.Properties", "Set",
874 std::string(iface), property.first,
875 property.second);
876 }
877 }
878 else
879 {
880 if (chassis.empty())
881 {
882 BMCWEB_LOG_ERROR
883 << "Failed to get chassis from config";
Jason M. Bills35a62c72018-10-09 12:45:45 -0700884 messages::invalidObject(response->res, name);
James Feist83ff9ab2018-08-31 10:18:24 -0700885 return;
886 }
887
888 bool foundChassis = false;
889 for (const auto& obj : managedObj)
890 {
891 if (boost::algorithm::ends_with(obj.first.str,
892 chassis))
893 {
894 chassis = obj.first.str;
895 foundChassis = true;
896 break;
897 }
898 }
899 if (!foundChassis)
900 {
901 BMCWEB_LOG_ERROR
902 << "Failed to find chassis on dbus";
Jason M. Bills35a62c72018-10-09 12:45:45 -0700903 messages::resourceMissingAtURI(
904 response->res,
905 "/redfish/v1/Chassis/" + chassis);
James Feist83ff9ab2018-08-31 10:18:24 -0700906 return;
907 }
908
909 crow::connections::systemBus->async_method_call(
910 [response](const boost::system::error_code ec) {
911 if (ec)
912 {
913 BMCWEB_LOG_ERROR
914 << "Error Adding Pid Object " << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700915 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700916 }
917 },
918 "xyz.openbmc_project.EntityManager", chassis,
919 "xyz.openbmc_project.AddObject", "AddObject",
920 output);
921 }
922 }
923 }
924 },
925 "xyz.openbmc_project.EntityManager", "/", objectManagerIface,
926 "GetManagedObjects");
927 }
James Feist5b4aa862018-08-16 14:07:01 -0700928
929 void doPatch(crow::Response& res, const crow::Request& req,
930 const std::vector<std::string>& params) override
931 {
James Feist83ff9ab2018-08-31 10:18:24 -0700932 nlohmann::json patch;
933 if (!json_util::processJsonFromRequest(res, req, patch))
934 {
935 return;
936 }
937 std::shared_ptr<AsyncResp> response = std::make_shared<AsyncResp>(res);
938 for (const auto& topLevel : patch.items())
939 {
940 if (topLevel.key() == "Oem")
941 {
942 if (!topLevel.value().is_object())
943 {
944 BMCWEB_LOG_ERROR << "Bad Patch " << topLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700945 messages::propertyValueFormatError(
946 response->res, topLevel.key(), "OemManager.Oem");
James Feist83ff9ab2018-08-31 10:18:24 -0700947 return;
948 }
949 }
950 else
951 {
952 BMCWEB_LOG_ERROR << "Bad Patch " << topLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700953 messages::propertyUnknown(response->res, topLevel.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700954 return;
955 }
956 for (const auto& oemLevel : topLevel.value().items())
957 {
958 if (oemLevel.key() == "OpenBmc")
959 {
960 if (!oemLevel.value().is_object())
961 {
962 BMCWEB_LOG_ERROR << "Bad Patch " << oemLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700963 messages::propertyValueFormatError(
964 response->res, topLevel.key(),
965 "OemManager.OpenBmc");
James Feist83ff9ab2018-08-31 10:18:24 -0700966 return;
967 }
968 for (const auto& typeLevel : oemLevel.value().items())
969 {
970
971 if (typeLevel.key() == "Fan")
972 {
973 if (!typeLevel.value().is_object())
974 {
975 BMCWEB_LOG_ERROR << "Bad Patch "
976 << typeLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700977 messages::propertyValueFormatError(
978 response->res, typeLevel.value().dump(),
979 typeLevel.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700980 return;
981 }
982 setPidValues(response,
983 std::move(typeLevel.value()));
984 }
985 else
986 {
987 BMCWEB_LOG_ERROR << "Bad Patch " << typeLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700988 messages::propertyUnknown(response->res,
989 typeLevel.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700990 return;
991 }
992 }
993 }
994 else
995 {
996 BMCWEB_LOG_ERROR << "Bad Patch " << oemLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700997 messages::propertyUnknown(response->res, oemLevel.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700998 return;
999 }
1000 }
1001 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001002 }
1003
1004 std::string getDateTime() const
1005 {
1006 std::array<char, 128> dateTime;
1007 std::string redfishDateTime("0000-00-00T00:00:00Z00:00");
1008 std::time_t time = std::time(nullptr);
1009
1010 if (std::strftime(dateTime.begin(), dateTime.size(), "%FT%T%z",
1011 std::localtime(&time)))
1012 {
1013 // insert the colon required by the ISO 8601 standard
1014 redfishDateTime = std::string(dateTime.data());
1015 redfishDateTime.insert(redfishDateTime.end() - 2, ':');
1016 }
1017
1018 return redfishDateTime;
1019 }
Ed Tanous0f74e642018-11-12 15:17:05 -08001020
1021 std::string uuid;
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001022};
1023
Ed Tanous1abe55e2018-09-05 08:30:59 -07001024class ManagerCollection : public Node
1025{
1026 public:
James Feist5b4aa862018-08-16 14:07:01 -07001027 ManagerCollection(CrowApp& app) : Node(app, "/redfish/v1/Managers/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001028 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001029 entityPrivileges = {
1030 {boost::beast::http::verb::get, {{"Login"}}},
1031 {boost::beast::http::verb::head, {{"Login"}}},
1032 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1033 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1034 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1035 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1036 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001037
Ed Tanous1abe55e2018-09-05 08:30:59 -07001038 private:
James Feist5b4aa862018-08-16 14:07:01 -07001039 void doGet(crow::Response& res, const crow::Request& req,
1040 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -07001041 {
James Feist83ff9ab2018-08-31 10:18:24 -07001042 // Collections don't include the static data added by SubRoute
1043 // because it has a duplicate entry for members
Ed Tanous1abe55e2018-09-05 08:30:59 -07001044 res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
1045 res.jsonValue["@odata.type"] = "#ManagerCollection.ManagerCollection";
1046 res.jsonValue["@odata.context"] =
1047 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection";
1048 res.jsonValue["Name"] = "Manager Collection";
1049 res.jsonValue["Members@odata.count"] = 1;
1050 res.jsonValue["Members"] = {
James Feist5b4aa862018-08-16 14:07:01 -07001051 {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001052 res.end();
1053 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001054};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001055} // namespace redfish