blob: a9efb4c79e9d1d2148348219b6e60161b95104c1 [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>
Santosh Puranikaf5d60582019-03-20 18:16:36 +053021#include <boost/date_time.hpp>
James Feist5b4aa862018-08-16 14:07:01 -070022#include <dbus_utility.hpp>
Santosh Puranikaf5d60582019-03-20 18:16:36 +053023#include <sstream>
Ed Tanousabf2add2019-01-22 16:40:12 -080024#include <variant>
James Feist5b4aa862018-08-16 14:07:01 -070025
Ed Tanous1abe55e2018-09-05 08:30:59 -070026namespace redfish
27{
Jennifer Leeed5befb2018-08-10 11:29:45 -070028
29/**
30 * ManagerActionsReset class supports handle POST method for Reset action.
31 * The class retrieves and sends data directly to dbus.
32 */
33class ManagerActionsReset : public Node
34{
35 public:
36 ManagerActionsReset(CrowApp& app) :
37 Node(app, "/redfish/v1/Managers/bmc/Actions/Manager.Reset/")
38 {
39 entityPrivileges = {
40 {boost::beast::http::verb::get, {{"Login"}}},
41 {boost::beast::http::verb::head, {{"Login"}}},
42 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
43 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
44 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
45 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
46 }
47
48 private:
49 /**
Jennifer Leeed5befb2018-08-10 11:29:45 -070050 * Function handles POST method request.
51 * Analyzes POST body message before sends Reset request data to dbus.
52 * OpenBMC allows for ResetType is GracefulRestart only.
53 */
54 void doPost(crow::Response& res, const crow::Request& req,
55 const std::vector<std::string>& params) override
56 {
57 std::string resetType;
58
59 if (!json_util::readJson(req, res, "ResetType", resetType))
60 {
61 return;
62 }
63
64 if (resetType != "GracefulRestart")
65 {
66 res.result(boost::beast::http::status::bad_request);
67 messages::actionParameterNotSupported(res, resetType, "ResetType");
68 BMCWEB_LOG_ERROR << "Request incorrect action parameter: "
69 << resetType;
70 res.end();
71 return;
72 }
73 doBMCGracefulRestart(res, req, params);
74 }
75
76 /**
77 * Function transceives data with dbus directly.
78 * All BMC state properties will be retrieved before sending reset request.
79 */
80 void doBMCGracefulRestart(crow::Response& res, const crow::Request& req,
81 const std::vector<std::string>& params)
82 {
83 const char* processName = "xyz.openbmc_project.State.BMC";
84 const char* objectPath = "/xyz/openbmc_project/state/bmc0";
85 const char* interfaceName = "xyz.openbmc_project.State.BMC";
86 const std::string& propertyValue =
87 "xyz.openbmc_project.State.BMC.Transition.Reboot";
88 const char* destProperty = "RequestedBMCTransition";
89
90 // Create the D-Bus variant for D-Bus call.
91 VariantType dbusPropertyValue(propertyValue);
92
93 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
94
95 crow::connections::systemBus->async_method_call(
96 [asyncResp](const boost::system::error_code ec) {
97 // Use "Set" method to set the property value.
98 if (ec)
99 {
100 BMCWEB_LOG_ERROR << "[Set] Bad D-Bus request error: " << ec;
101 messages::internalError(asyncResp->res);
102 return;
103 }
104
105 messages::success(asyncResp->res);
106 },
107 processName, objectPath, "org.freedesktop.DBus.Properties", "Set",
108 interfaceName, destProperty, dbusPropertyValue);
109 }
110};
111
James Feist5b4aa862018-08-16 14:07:01 -0700112static constexpr const char* objectManagerIface =
113 "org.freedesktop.DBus.ObjectManager";
114static constexpr const char* pidConfigurationIface =
115 "xyz.openbmc_project.Configuration.Pid";
116static constexpr const char* pidZoneConfigurationIface =
117 "xyz.openbmc_project.Configuration.Pid.Zone";
James Feistb7a08d02018-12-11 14:55:37 -0800118static constexpr const char* stepwiseConfigurationIface =
119 "xyz.openbmc_project.Configuration.Stepwise";
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100120
James Feist5b4aa862018-08-16 14:07:01 -0700121static void asyncPopulatePid(const std::string& connection,
122 const std::string& path,
123 std::shared_ptr<AsyncResp> asyncResp)
124{
125
126 crow::connections::systemBus->async_method_call(
127 [asyncResp](const boost::system::error_code ec,
128 const dbus::utility::ManagedObjectType& managedObj) {
129 if (ec)
130 {
131 BMCWEB_LOG_ERROR << ec;
James Feist5b4aa862018-08-16 14:07:01 -0700132 asyncResp->res.jsonValue.clear();
Jason M. Billsf12894f2018-10-09 12:45:45 -0700133 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700134 return;
135 }
136 nlohmann::json& configRoot =
137 asyncResp->res.jsonValue["Oem"]["OpenBmc"]["Fan"];
138 nlohmann::json& fans = configRoot["FanControllers"];
139 fans["@odata.type"] = "#OemManager.FanControllers";
140 fans["@odata.context"] =
141 "/redfish/v1/$metadata#OemManager.FanControllers";
142 fans["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc/"
143 "Fan/FanControllers";
144
145 nlohmann::json& pids = configRoot["PidControllers"];
146 pids["@odata.type"] = "#OemManager.PidControllers";
147 pids["@odata.context"] =
148 "/redfish/v1/$metadata#OemManager.PidControllers";
149 pids["@odata.id"] =
150 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/PidControllers";
151
James Feistb7a08d02018-12-11 14:55:37 -0800152 nlohmann::json& stepwise = configRoot["StepwiseControllers"];
153 stepwise["@odata.type"] = "#OemManager.StepwiseControllers";
154 stepwise["@odata.context"] =
155 "/redfish/v1/$metadata#OemManager.StepwiseControllers";
156 stepwise["@odata.id"] =
157 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/StepwiseControllers";
158
James Feist5b4aa862018-08-16 14:07:01 -0700159 nlohmann::json& zones = configRoot["FanZones"];
160 zones["@odata.id"] =
161 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones";
162 zones["@odata.type"] = "#OemManager.FanZones";
163 zones["@odata.context"] =
164 "/redfish/v1/$metadata#OemManager.FanZones";
165 configRoot["@odata.id"] =
166 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan";
167 configRoot["@odata.type"] = "#OemManager.Fan";
168 configRoot["@odata.context"] =
169 "/redfish/v1/$metadata#OemManager.Fan";
170
James Feist5b4aa862018-08-16 14:07:01 -0700171 for (const auto& pathPair : managedObj)
172 {
173 for (const auto& intfPair : pathPair.second)
174 {
175 if (intfPair.first != pidConfigurationIface &&
James Feistb7a08d02018-12-11 14:55:37 -0800176 intfPair.first != pidZoneConfigurationIface &&
177 intfPair.first != stepwiseConfigurationIface)
James Feist5b4aa862018-08-16 14:07:01 -0700178 {
179 continue;
180 }
181 auto findName = intfPair.second.find("Name");
182 if (findName == intfPair.second.end())
183 {
184 BMCWEB_LOG_ERROR << "Pid Field missing Name";
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800185 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700186 return;
187 }
188 const std::string* namePtr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800189 std::get_if<std::string>(&findName->second);
James Feist5b4aa862018-08-16 14:07:01 -0700190 if (namePtr == nullptr)
191 {
192 BMCWEB_LOG_ERROR << "Pid Name Field illegal";
James Feistb7a08d02018-12-11 14:55:37 -0800193 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700194 return;
195 }
196
197 std::string name = *namePtr;
198 dbus::utility::escapePathForDbus(name);
James Feistb7a08d02018-12-11 14:55:37 -0800199 nlohmann::json* config = nullptr;
James Feistc33a90e2019-03-01 10:17:44 -0800200
201 const std::string* classPtr = nullptr;
202 auto findClass = intfPair.second.find("Class");
203 if (findClass != intfPair.second.end())
204 {
205 classPtr = std::get_if<std::string>(&findClass->second);
206 }
207
James Feist5b4aa862018-08-16 14:07:01 -0700208 if (intfPair.first == pidZoneConfigurationIface)
209 {
210 std::string chassis;
211 if (!dbus::utility::getNthStringFromPath(
212 pathPair.first.str, 5, chassis))
213 {
214 chassis = "#IllegalValue";
215 }
216 nlohmann::json& zone = zones[name];
217 zone["Chassis"] = {
218 {"@odata.id", "/redfish/v1/Chassis/" + chassis}};
219 zone["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/"
220 "OpenBmc/Fan/FanZones/" +
221 name;
222 zone["@odata.type"] = "#OemManager.FanZone";
223 zone["@odata.context"] =
224 "/redfish/v1/$metadata#OemManager.FanZone";
James Feistb7a08d02018-12-11 14:55:37 -0800225 config = &zone;
James Feist5b4aa862018-08-16 14:07:01 -0700226 }
227
James Feistb7a08d02018-12-11 14:55:37 -0800228 else if (intfPair.first == stepwiseConfigurationIface)
229 {
James Feistc33a90e2019-03-01 10:17:44 -0800230 if (classPtr == nullptr)
231 {
232 BMCWEB_LOG_ERROR << "Pid Class Field illegal";
233 messages::internalError(asyncResp->res);
234 return;
235 }
236
James Feistb7a08d02018-12-11 14:55:37 -0800237 nlohmann::json& controller = stepwise[name];
238 config = &controller;
239
240 controller["@odata.id"] =
241 "/redfish/v1/Managers/bmc#/Oem/"
242 "OpenBmc/Fan/StepwiseControllers/" +
243 std::string(name);
244 controller["@odata.type"] =
245 "#OemManager.StepwiseController";
246
247 controller["@odata.context"] =
248 "/redfish/v1/"
249 "$metadata#OemManager.StepwiseController";
James Feistc33a90e2019-03-01 10:17:44 -0800250 controller["Direction"] = *classPtr;
James Feistb7a08d02018-12-11 14:55:37 -0800251 }
252
253 // pid and fans are off the same configuration
254 else if (intfPair.first == pidConfigurationIface)
255 {
James Feistc33a90e2019-03-01 10:17:44 -0800256
James Feistb7a08d02018-12-11 14:55:37 -0800257 if (classPtr == nullptr)
258 {
259 BMCWEB_LOG_ERROR << "Pid Class Field illegal";
260 messages::internalError(asyncResp->res);
261 return;
262 }
263 bool isFan = *classPtr == "fan";
264 nlohmann::json& element =
265 isFan ? fans[name] : pids[name];
266 config = &element;
267 if (isFan)
268 {
269 element["@odata.id"] =
270 "/redfish/v1/Managers/bmc#/Oem/"
271 "OpenBmc/Fan/FanControllers/" +
272 std::string(name);
273 element["@odata.type"] =
274 "#OemManager.FanController";
275
276 element["@odata.context"] =
277 "/redfish/v1/"
278 "$metadata#OemManager.FanController";
279 }
280 else
281 {
282 element["@odata.id"] =
283 "/redfish/v1/Managers/bmc#/Oem/"
284 "OpenBmc/Fan/PidControllers/" +
285 std::string(name);
286 element["@odata.type"] =
287 "#OemManager.PidController";
288 element["@odata.context"] =
289 "/redfish/v1/$metadata"
290 "#OemManager.PidController";
291 }
292 }
293 else
294 {
295 BMCWEB_LOG_ERROR << "Unexpected configuration";
296 messages::internalError(asyncResp->res);
297 return;
298 }
299
300 // used for making maps out of 2 vectors
301 const std::vector<double>* keys = nullptr;
302 const std::vector<double>* values = nullptr;
303
James Feist5b4aa862018-08-16 14:07:01 -0700304 for (const auto& propertyPair : intfPair.second)
305 {
306 if (propertyPair.first == "Type" ||
307 propertyPair.first == "Class" ||
308 propertyPair.first == "Name")
309 {
310 continue;
311 }
312
313 // zones
314 if (intfPair.first == pidZoneConfigurationIface)
315 {
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800316 const double* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800317 std::get_if<double>(&propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700318 if (ptr == nullptr)
319 {
320 BMCWEB_LOG_ERROR << "Field Illegal "
321 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700322 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700323 return;
324 }
James Feistb7a08d02018-12-11 14:55:37 -0800325 (*config)[propertyPair.first] = *ptr;
326 }
327
328 if (intfPair.first == stepwiseConfigurationIface)
329 {
330 if (propertyPair.first == "Reading" ||
331 propertyPair.first == "Output")
332 {
333 const std::vector<double>* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800334 std::get_if<std::vector<double>>(
James Feistb7a08d02018-12-11 14:55:37 -0800335 &propertyPair.second);
336
337 if (ptr == nullptr)
338 {
339 BMCWEB_LOG_ERROR << "Field Illegal "
340 << propertyPair.first;
341 messages::internalError(asyncResp->res);
342 return;
343 }
344
345 if (propertyPair.first == "Reading")
346 {
347 keys = ptr;
348 }
349 else
350 {
351 values = ptr;
352 }
353 if (keys && values)
354 {
355 if (keys->size() != values->size())
356 {
357 BMCWEB_LOG_ERROR
358 << "Reading and Output size don't "
359 "match ";
360 messages::internalError(asyncResp->res);
361 return;
362 }
363 nlohmann::json& steps = (*config)["Steps"];
364 steps = nlohmann::json::array();
365 for (size_t ii = 0; ii < keys->size(); ii++)
366 {
367 steps.push_back(
368 {{"Target", (*keys)[ii]},
369 {"Output", (*values)[ii]}});
370 }
371 }
372 }
373 if (propertyPair.first == "NegativeHysteresis" ||
374 propertyPair.first == "PositiveHysteresis")
375 {
376 const double* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800377 std::get_if<double>(&propertyPair.second);
James Feistb7a08d02018-12-11 14:55:37 -0800378 if (ptr == nullptr)
379 {
380 BMCWEB_LOG_ERROR << "Field Illegal "
381 << propertyPair.first;
382 messages::internalError(asyncResp->res);
383 return;
384 }
385 (*config)[propertyPair.first] = *ptr;
386 }
James Feist5b4aa862018-08-16 14:07:01 -0700387 }
388
389 // pid and fans are off the same configuration
James Feistb7a08d02018-12-11 14:55:37 -0800390 if (intfPair.first == pidConfigurationIface ||
391 intfPair.first == stepwiseConfigurationIface)
James Feist5b4aa862018-08-16 14:07:01 -0700392 {
James Feist5b4aa862018-08-16 14:07:01 -0700393
394 if (propertyPair.first == "Zones")
395 {
396 const std::vector<std::string>* inputs =
Ed Tanousabf2add2019-01-22 16:40:12 -0800397 std::get_if<std::vector<std::string>>(
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800398 &propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700399
400 if (inputs == nullptr)
401 {
402 BMCWEB_LOG_ERROR
403 << "Zones Pid Field Illegal";
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800404 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700405 return;
406 }
James Feistb7a08d02018-12-11 14:55:37 -0800407 auto& data = (*config)[propertyPair.first];
James Feist5b4aa862018-08-16 14:07:01 -0700408 data = nlohmann::json::array();
409 for (std::string itemCopy : *inputs)
410 {
411 dbus::utility::escapePathForDbus(itemCopy);
412 data.push_back(
413 {{"@odata.id",
414 "/redfish/v1/Managers/bmc#/Oem/"
415 "OpenBmc/Fan/FanZones/" +
416 itemCopy}});
417 }
418 }
419 // todo(james): may never happen, but this
420 // assumes configuration data referenced in the
421 // PID config is provided by the same daemon, we
422 // could add another loop to cover all cases,
423 // but I'm okay kicking this can down the road a
424 // bit
425
426 else if (propertyPair.first == "Inputs" ||
427 propertyPair.first == "Outputs")
428 {
James Feistb7a08d02018-12-11 14:55:37 -0800429 auto& data = (*config)[propertyPair.first];
James Feist5b4aa862018-08-16 14:07:01 -0700430 const std::vector<std::string>* inputs =
Ed Tanousabf2add2019-01-22 16:40:12 -0800431 std::get_if<std::vector<std::string>>(
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800432 &propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700433
434 if (inputs == nullptr)
435 {
436 BMCWEB_LOG_ERROR << "Field Illegal "
437 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700438 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700439 return;
440 }
441 data = *inputs;
442 } // doubles
443 else if (propertyPair.first ==
444 "FFGainCoefficient" ||
445 propertyPair.first == "FFOffCoefficient" ||
446 propertyPair.first == "ICoefficient" ||
447 propertyPair.first == "ILimitMax" ||
448 propertyPair.first == "ILimitMin" ||
James Feistaad1a252019-02-19 10:13:52 -0800449 propertyPair.first ==
450 "PositiveHysteresis" ||
451 propertyPair.first ==
452 "NegativeHysteresis" ||
James Feist5b4aa862018-08-16 14:07:01 -0700453 propertyPair.first == "OutLimitMax" ||
454 propertyPair.first == "OutLimitMin" ||
455 propertyPair.first == "PCoefficient" ||
James Feist7625cb82019-01-23 11:58:21 -0800456 propertyPair.first == "SetPoint" ||
James Feist5b4aa862018-08-16 14:07:01 -0700457 propertyPair.first == "SlewNeg" ||
458 propertyPair.first == "SlewPos")
459 {
460 const double* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800461 std::get_if<double>(&propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700462 if (ptr == nullptr)
463 {
464 BMCWEB_LOG_ERROR << "Field Illegal "
465 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700466 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700467 return;
468 }
James Feistb7a08d02018-12-11 14:55:37 -0800469 (*config)[propertyPair.first] = *ptr;
James Feist5b4aa862018-08-16 14:07:01 -0700470 }
471 }
472 }
473 }
474 }
475 },
476 connection, path, objectManagerIface, "GetManagedObjects");
477}
Jennifer Leeca537922018-08-10 10:07:30 -0700478
James Feist83ff9ab2018-08-31 10:18:24 -0700479enum class CreatePIDRet
480{
481 fail,
482 del,
483 patch
484};
485
James Feist5f2caae2018-12-12 14:08:25 -0800486static bool getZonesFromJsonReq(const std::shared_ptr<AsyncResp>& response,
487 std::vector<nlohmann::json>& config,
488 std::vector<std::string>& zones)
489{
James Feistb6baeaa2019-02-21 10:41:40 -0800490 if (config.empty())
491 {
492 BMCWEB_LOG_ERROR << "Empty Zones";
493 messages::propertyValueFormatError(response->res,
494 nlohmann::json::array(), "Zones");
495 return false;
496 }
James Feist5f2caae2018-12-12 14:08:25 -0800497 for (auto& odata : config)
498 {
499 std::string path;
500 if (!redfish::json_util::readJson(odata, response->res, "@odata.id",
501 path))
502 {
503 return false;
504 }
505 std::string input;
James Feist61adbda2019-03-25 13:03:51 -0700506
507 // 8 below comes from
508 // /redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones/Left
509 // 0 1 2 3 4 5 6 7 8
510 if (!dbus::utility::getNthStringFromPath(path, 8, input))
James Feist5f2caae2018-12-12 14:08:25 -0800511 {
512 BMCWEB_LOG_ERROR << "Got invalid path " << path;
513 BMCWEB_LOG_ERROR << "Illegal Type Zones";
514 messages::propertyValueFormatError(response->res, odata.dump(),
515 "Zones");
516 return false;
517 }
518 boost::replace_all(input, "_", " ");
519 zones.emplace_back(std::move(input));
520 }
521 return true;
522}
523
James Feistb6baeaa2019-02-21 10:41:40 -0800524static bool findChassis(const dbus::utility::ManagedObjectType& managedObj,
525 const std::string& value, std::string& chassis)
526{
527 BMCWEB_LOG_DEBUG << "Find Chassis: " << value << "\n";
528
529 std::string escaped = boost::replace_all_copy(value, " ", "_");
530 escaped = "/" + escaped;
531 auto it = std::find_if(
532 managedObj.begin(), managedObj.end(), [&escaped](const auto& obj) {
533 if (boost::algorithm::ends_with(obj.first.str, escaped))
534 {
535 BMCWEB_LOG_DEBUG << "Matched " << obj.first.str << "\n";
536 return true;
537 }
538 return false;
539 });
540
541 if (it == managedObj.end())
542 {
543 return false;
544 }
545 // 5 comes from <chassis-name> being the 5th element
546 // /xyz/openbmc_project/inventory/system/chassis/<chassis-name>
547 return dbus::utility::getNthStringFromPath(it->first.str, 5, chassis);
548}
549
James Feist83ff9ab2018-08-31 10:18:24 -0700550static CreatePIDRet createPidInterface(
551 const std::shared_ptr<AsyncResp>& response, const std::string& type,
James Feistb6baeaa2019-02-21 10:41:40 -0800552 nlohmann::json::iterator it, const std::string& path,
James Feist83ff9ab2018-08-31 10:18:24 -0700553 const dbus::utility::ManagedObjectType& managedObj, bool createNewObject,
554 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>&
555 output,
556 std::string& chassis)
557{
558
James Feist5f2caae2018-12-12 14:08:25 -0800559 // common deleter
James Feistb6baeaa2019-02-21 10:41:40 -0800560 if (it.value() == nullptr)
James Feist5f2caae2018-12-12 14:08:25 -0800561 {
562 std::string iface;
563 if (type == "PidControllers" || type == "FanControllers")
564 {
565 iface = pidConfigurationIface;
566 }
567 else if (type == "FanZones")
568 {
569 iface = pidZoneConfigurationIface;
570 }
571 else if (type == "StepwiseControllers")
572 {
573 iface = stepwiseConfigurationIface;
574 }
575 else
576 {
577 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Type "
578 << type;
579 messages::propertyUnknown(response->res, type);
580 return CreatePIDRet::fail;
581 }
582 // delete interface
583 crow::connections::systemBus->async_method_call(
584 [response, path](const boost::system::error_code ec) {
585 if (ec)
586 {
587 BMCWEB_LOG_ERROR << "Error patching " << path << ": " << ec;
588 messages::internalError(response->res);
James Feistb6baeaa2019-02-21 10:41:40 -0800589 return;
James Feist5f2caae2018-12-12 14:08:25 -0800590 }
James Feistb6baeaa2019-02-21 10:41:40 -0800591 messages::success(response->res);
James Feist5f2caae2018-12-12 14:08:25 -0800592 },
593 "xyz.openbmc_project.EntityManager", path, iface, "Delete");
594 return CreatePIDRet::del;
595 }
596
James Feistb6baeaa2019-02-21 10:41:40 -0800597 if (!createNewObject)
598 {
599 // if we aren't creating a new object, we should be able to find it on
600 // d-bus
601 if (!findChassis(managedObj, it.key(), chassis))
602 {
603 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
604 messages::invalidObject(response->res, it.key());
605 return CreatePIDRet::fail;
606 }
607 }
608
James Feist83ff9ab2018-08-31 10:18:24 -0700609 if (type == "PidControllers" || type == "FanControllers")
610 {
611 if (createNewObject)
612 {
613 output["Class"] = type == "PidControllers" ? std::string("temp")
614 : std::string("fan");
615 output["Type"] = std::string("Pid");
616 }
James Feist5f2caae2018-12-12 14:08:25 -0800617
618 std::optional<std::vector<nlohmann::json>> zones;
619 std::optional<std::vector<std::string>> inputs;
620 std::optional<std::vector<std::string>> outputs;
621 std::map<std::string, std::optional<double>> doubles;
622 if (!redfish::json_util::readJson(
James Feistb6baeaa2019-02-21 10:41:40 -0800623 it.value(), response->res, "Inputs", inputs, "Outputs", outputs,
James Feist5f2caae2018-12-12 14:08:25 -0800624 "Zones", zones, "FFGainCoefficient",
625 doubles["FFGainCoefficient"], "FFOffCoefficient",
626 doubles["FFOffCoefficient"], "ICoefficient",
627 doubles["ICoefficient"], "ILimitMax", doubles["ILimitMax"],
628 "ILimitMin", doubles["ILimitMin"], "OutLimitMax",
629 doubles["OutLimitMax"], "OutLimitMin", doubles["OutLimitMin"],
630 "PCoefficient", doubles["PCoefficient"], "SetPoint",
631 doubles["SetPoint"], "SlewNeg", doubles["SlewNeg"], "SlewPos",
James Feistaad1a252019-02-19 10:13:52 -0800632 doubles["SlewPos"], "PositiveHysteresis",
633 doubles["PositiveHysteresis"], "NegativeHysteresis",
634 doubles["NegativeHysteresis"]))
James Feist83ff9ab2018-08-31 10:18:24 -0700635 {
James Feist5f2caae2018-12-12 14:08:25 -0800636 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
James Feistb6baeaa2019-02-21 10:41:40 -0800637 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800638 return CreatePIDRet::fail;
James Feist83ff9ab2018-08-31 10:18:24 -0700639 }
James Feist5f2caae2018-12-12 14:08:25 -0800640 if (zones)
James Feist83ff9ab2018-08-31 10:18:24 -0700641 {
James Feist5f2caae2018-12-12 14:08:25 -0800642 std::vector<std::string> zonesStr;
643 if (!getZonesFromJsonReq(response, *zones, zonesStr))
James Feist83ff9ab2018-08-31 10:18:24 -0700644 {
James Feist5f2caae2018-12-12 14:08:25 -0800645 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Zones";
646 return CreatePIDRet::fail;
James Feist83ff9ab2018-08-31 10:18:24 -0700647 }
James Feistb6baeaa2019-02-21 10:41:40 -0800648 if (chassis.empty() &&
649 !findChassis(managedObj, zonesStr[0], chassis))
650 {
651 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
652 messages::invalidObject(response->res, it.key());
653 return CreatePIDRet::fail;
654 }
655
James Feist5f2caae2018-12-12 14:08:25 -0800656 output["Zones"] = std::move(zonesStr);
657 }
658 if (inputs || outputs)
659 {
660 std::array<std::optional<std::vector<std::string>>*, 2> containers =
661 {&inputs, &outputs};
662 size_t index = 0;
663 for (const auto& containerPtr : containers)
James Feist83ff9ab2018-08-31 10:18:24 -0700664 {
James Feist5f2caae2018-12-12 14:08:25 -0800665 std::optional<std::vector<std::string>>& container =
666 *containerPtr;
667 if (!container)
James Feist83ff9ab2018-08-31 10:18:24 -0700668 {
James Feist5f2caae2018-12-12 14:08:25 -0800669 index++;
670 continue;
James Feist83ff9ab2018-08-31 10:18:24 -0700671 }
James Feist5f2caae2018-12-12 14:08:25 -0800672
673 for (std::string& value : *container)
James Feist83ff9ab2018-08-31 10:18:24 -0700674 {
James Feist5f2caae2018-12-12 14:08:25 -0800675 boost::replace_all(value, "_", " ");
James Feist83ff9ab2018-08-31 10:18:24 -0700676 }
James Feist5f2caae2018-12-12 14:08:25 -0800677 std::string key;
678 if (index == 0)
James Feist83ff9ab2018-08-31 10:18:24 -0700679 {
James Feist5f2caae2018-12-12 14:08:25 -0800680 key = "Inputs";
James Feist83ff9ab2018-08-31 10:18:24 -0700681 }
James Feist5f2caae2018-12-12 14:08:25 -0800682 else
683 {
684 key = "Outputs";
685 }
686 output[key] = *container;
687 index++;
James Feist83ff9ab2018-08-31 10:18:24 -0700688 }
James Feist5f2caae2018-12-12 14:08:25 -0800689 }
James Feist83ff9ab2018-08-31 10:18:24 -0700690
James Feist5f2caae2018-12-12 14:08:25 -0800691 // doubles
692 for (const auto& pairs : doubles)
693 {
694 if (!pairs.second)
James Feist83ff9ab2018-08-31 10:18:24 -0700695 {
James Feist5f2caae2018-12-12 14:08:25 -0800696 continue;
James Feist83ff9ab2018-08-31 10:18:24 -0700697 }
James Feist5f2caae2018-12-12 14:08:25 -0800698 BMCWEB_LOG_DEBUG << pairs.first << " = " << *pairs.second;
699 output[pairs.first] = *(pairs.second);
James Feist83ff9ab2018-08-31 10:18:24 -0700700 }
701 }
James Feist5f2caae2018-12-12 14:08:25 -0800702
James Feist83ff9ab2018-08-31 10:18:24 -0700703 else if (type == "FanZones")
704 {
James Feist83ff9ab2018-08-31 10:18:24 -0700705 output["Type"] = std::string("Pid.Zone");
706
James Feist5f2caae2018-12-12 14:08:25 -0800707 std::optional<nlohmann::json> chassisContainer;
708 std::optional<double> failSafePercent;
James Feistd3ec07f2019-02-25 14:51:15 -0800709 std::optional<double> minThermalOutput;
James Feistb6baeaa2019-02-21 10:41:40 -0800710 if (!redfish::json_util::readJson(it.value(), response->res, "Chassis",
James Feist5f2caae2018-12-12 14:08:25 -0800711 chassisContainer, "FailSafePercent",
James Feistd3ec07f2019-02-25 14:51:15 -0800712 failSafePercent, "MinThermalOutput",
713 minThermalOutput))
James Feist83ff9ab2018-08-31 10:18:24 -0700714 {
James Feist5f2caae2018-12-12 14:08:25 -0800715 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
James Feistb6baeaa2019-02-21 10:41:40 -0800716 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800717 return CreatePIDRet::fail;
718 }
James Feist83ff9ab2018-08-31 10:18:24 -0700719
James Feist5f2caae2018-12-12 14:08:25 -0800720 if (chassisContainer)
721 {
722
723 std::string chassisId;
724 if (!redfish::json_util::readJson(*chassisContainer, response->res,
725 "@odata.id", chassisId))
James Feist83ff9ab2018-08-31 10:18:24 -0700726 {
James Feist5f2caae2018-12-12 14:08:25 -0800727 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
728 << chassisContainer->dump();
James Feist83ff9ab2018-08-31 10:18:24 -0700729 return CreatePIDRet::fail;
730 }
James Feist5f2caae2018-12-12 14:08:25 -0800731
732 // /refish/v1/chassis/chassis_name/
733 if (!dbus::utility::getNthStringFromPath(chassisId, 3, chassis))
734 {
735 BMCWEB_LOG_ERROR << "Got invalid path " << chassisId;
736 messages::invalidObject(response->res, chassisId);
737 return CreatePIDRet::fail;
738 }
739 }
James Feistd3ec07f2019-02-25 14:51:15 -0800740 if (minThermalOutput)
James Feist5f2caae2018-12-12 14:08:25 -0800741 {
James Feistd3ec07f2019-02-25 14:51:15 -0800742 output["MinThermalOutput"] = *minThermalOutput;
James Feist5f2caae2018-12-12 14:08:25 -0800743 }
744 if (failSafePercent)
745 {
746 output["FailSafePercent"] = *failSafePercent;
747 }
748 }
749 else if (type == "StepwiseControllers")
750 {
751 output["Type"] = std::string("Stepwise");
752
753 std::optional<std::vector<nlohmann::json>> zones;
754 std::optional<std::vector<nlohmann::json>> steps;
755 std::optional<std::vector<std::string>> inputs;
756 std::optional<double> positiveHysteresis;
757 std::optional<double> negativeHysteresis;
James Feistc33a90e2019-03-01 10:17:44 -0800758 std::optional<std::string> direction; // upper clipping curve vs lower
James Feist5f2caae2018-12-12 14:08:25 -0800759 if (!redfish::json_util::readJson(
James Feistb6baeaa2019-02-21 10:41:40 -0800760 it.value(), response->res, "Zones", zones, "Steps", steps,
761 "Inputs", inputs, "PositiveHysteresis", positiveHysteresis,
James Feistc33a90e2019-03-01 10:17:44 -0800762 "NegativeHysteresis", negativeHysteresis, "Direction",
763 direction))
James Feist5f2caae2018-12-12 14:08:25 -0800764 {
765 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
James Feistb6baeaa2019-02-21 10:41:40 -0800766 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800767 return CreatePIDRet::fail;
768 }
769
770 if (zones)
771 {
James Feistb6baeaa2019-02-21 10:41:40 -0800772 std::vector<std::string> zonesStrs;
773 if (!getZonesFromJsonReq(response, *zones, zonesStrs))
James Feist5f2caae2018-12-12 14:08:25 -0800774 {
775 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Zones";
776 return CreatePIDRet::fail;
777 }
James Feistb6baeaa2019-02-21 10:41:40 -0800778 if (chassis.empty() &&
779 !findChassis(managedObj, zonesStrs[0], chassis))
780 {
781 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
782 messages::invalidObject(response->res, it.key());
783 return CreatePIDRet::fail;
784 }
785 output["Zones"] = std::move(zonesStrs);
James Feist5f2caae2018-12-12 14:08:25 -0800786 }
787 if (steps)
788 {
789 std::vector<double> readings;
790 std::vector<double> outputs;
791 for (auto& step : *steps)
792 {
793 double target;
Ed Tanousb01bf292019-03-25 19:25:26 +0000794 double output;
James Feist5f2caae2018-12-12 14:08:25 -0800795
796 if (!redfish::json_util::readJson(step, response->res, "Target",
Ed Tanousb01bf292019-03-25 19:25:26 +0000797 target, "Output", output))
James Feist5f2caae2018-12-12 14:08:25 -0800798 {
799 BMCWEB_LOG_ERROR << "Line:" << __LINE__
James Feistb6baeaa2019-02-21 10:41:40 -0800800 << ", Illegal Property "
801 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800802 return CreatePIDRet::fail;
803 }
804 readings.emplace_back(target);
Ed Tanousb01bf292019-03-25 19:25:26 +0000805 outputs.emplace_back(output);
James Feist5f2caae2018-12-12 14:08:25 -0800806 }
807 output["Reading"] = std::move(readings);
808 output["Output"] = std::move(outputs);
809 }
810 if (inputs)
811 {
812 for (std::string& value : *inputs)
813 {
James Feist5f2caae2018-12-12 14:08:25 -0800814 boost::replace_all(value, "_", " ");
815 }
816 output["Inputs"] = std::move(*inputs);
817 }
818 if (negativeHysteresis)
819 {
820 output["NegativeHysteresis"] = *negativeHysteresis;
821 }
822 if (positiveHysteresis)
823 {
824 output["PositiveHysteresis"] = *positiveHysteresis;
James Feist83ff9ab2018-08-31 10:18:24 -0700825 }
James Feistc33a90e2019-03-01 10:17:44 -0800826 if (direction)
827 {
828 constexpr const std::array<const char*, 2> allowedDirections = {
829 "Ceiling", "Floor"};
830 if (std::find(allowedDirections.begin(), allowedDirections.end(),
831 *direction) == allowedDirections.end())
832 {
833 messages::propertyValueTypeError(response->res, "Direction",
834 *direction);
835 return CreatePIDRet::fail;
836 }
837 output["Class"] = *direction;
838 }
James Feist83ff9ab2018-08-31 10:18:24 -0700839 }
840 else
841 {
James Feist5f2caae2018-12-12 14:08:25 -0800842 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Type " << type;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700843 messages::propertyUnknown(response->res, type);
James Feist83ff9ab2018-08-31 10:18:24 -0700844 return CreatePIDRet::fail;
845 }
846 return CreatePIDRet::patch;
847}
848
Ed Tanous1abe55e2018-09-05 08:30:59 -0700849class Manager : public Node
850{
851 public:
James Feist5b4aa862018-08-16 14:07:01 -0700852 Manager(CrowApp& app) : Node(app, "/redfish/v1/Managers/bmc/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700853 {
Ed Tanous0f74e642018-11-12 15:17:05 -0800854 uuid = app.template getMiddleware<crow::persistent_data::Middleware>()
855 .systemUuid;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700856 entityPrivileges = {
857 {boost::beast::http::verb::get, {{"Login"}}},
858 {boost::beast::http::verb::head, {{"Login"}}},
859 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
860 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
861 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
862 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100863 }
864
Ed Tanous1abe55e2018-09-05 08:30:59 -0700865 private:
James Feist5b4aa862018-08-16 14:07:01 -0700866 void getPidValues(std::shared_ptr<AsyncResp> asyncResp)
867 {
868 crow::connections::systemBus->async_method_call(
869 [asyncResp](const boost::system::error_code ec,
870 const crow::openbmc_mapper::GetSubTreeType& subtree) {
871 if (ec)
872 {
873 BMCWEB_LOG_ERROR << ec;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700874 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700875 return;
876 }
877
878 // create map of <connection, path to objMgr>>
879 boost::container::flat_map<std::string, std::string>
880 objectMgrPaths;
James Feist6bce33b2018-10-22 12:05:56 -0700881 boost::container::flat_set<std::string> calledConnections;
James Feist5b4aa862018-08-16 14:07:01 -0700882 for (const auto& pathGroup : subtree)
883 {
884 for (const auto& connectionGroup : pathGroup.second)
885 {
James Feist6bce33b2018-10-22 12:05:56 -0700886 auto findConnection =
887 calledConnections.find(connectionGroup.first);
888 if (findConnection != calledConnections.end())
889 {
890 break;
891 }
James Feist5b4aa862018-08-16 14:07:01 -0700892 for (const std::string& interface :
893 connectionGroup.second)
894 {
895 if (interface == objectManagerIface)
896 {
897 objectMgrPaths[connectionGroup.first] =
898 pathGroup.first;
899 }
900 // this list is alphabetical, so we
901 // should have found the objMgr by now
902 if (interface == pidConfigurationIface ||
James Feistb7a08d02018-12-11 14:55:37 -0800903 interface == pidZoneConfigurationIface ||
904 interface == stepwiseConfigurationIface)
James Feist5b4aa862018-08-16 14:07:01 -0700905 {
906 auto findObjMgr =
907 objectMgrPaths.find(connectionGroup.first);
908 if (findObjMgr == objectMgrPaths.end())
909 {
910 BMCWEB_LOG_DEBUG << connectionGroup.first
911 << "Has no Object Manager";
912 continue;
913 }
James Feist6bce33b2018-10-22 12:05:56 -0700914
915 calledConnections.insert(connectionGroup.first);
916
James Feist5b4aa862018-08-16 14:07:01 -0700917 asyncPopulatePid(findObjMgr->first,
918 findObjMgr->second, asyncResp);
919 break;
920 }
921 }
922 }
923 }
924 },
925 "xyz.openbmc_project.ObjectMapper",
926 "/xyz/openbmc_project/object_mapper",
927 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
James Feistb7a08d02018-12-11 14:55:37 -0800928 std::array<const char*, 4>{
929 pidConfigurationIface, pidZoneConfigurationIface,
930 objectManagerIface, stepwiseConfigurationIface});
James Feist5b4aa862018-08-16 14:07:01 -0700931 }
932
933 void doGet(crow::Response& res, const crow::Request& req,
934 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700935 {
Ed Tanous0f74e642018-11-12 15:17:05 -0800936 res.jsonValue["@odata.id"] = "/redfish/v1/Managers/bmc";
937 res.jsonValue["@odata.type"] = "#Manager.v1_3_0.Manager";
938 res.jsonValue["@odata.context"] =
939 "/redfish/v1/$metadata#Manager.Manager";
940 res.jsonValue["Id"] = "bmc";
941 res.jsonValue["Name"] = "OpenBmc Manager";
942 res.jsonValue["Description"] = "Baseboard Management Controller";
943 res.jsonValue["PowerState"] = "On";
Ed Tanous029573d2019-02-01 10:57:49 -0800944 res.jsonValue["Status"] = {{"State", "Enabled"}, {"Health", "OK"}};
Ed Tanous0f74e642018-11-12 15:17:05 -0800945 res.jsonValue["ManagerType"] = "BMC";
Ed Tanous75176582018-12-14 08:14:34 -0800946 res.jsonValue["UUID"] = uuid;
947 res.jsonValue["Model"] = "OpenBmc"; // TODO(ed), get model
Ed Tanous0f74e642018-11-12 15:17:05 -0800948
949 res.jsonValue["LogServices"] = {
950 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices"}};
951
952 res.jsonValue["NetworkProtocol"] = {
953 {"@odata.id", "/redfish/v1/Managers/bmc/NetworkProtocol"}};
954
955 res.jsonValue["EthernetInterfaces"] = {
956 {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces"}};
957 // default oem data
958 nlohmann::json& oem = res.jsonValue["Oem"];
959 nlohmann::json& oemOpenbmc = oem["OpenBmc"];
960 oem["@odata.type"] = "#OemManager.Oem";
961 oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem";
962 oem["@odata.context"] = "/redfish/v1/$metadata#OemManager.Oem";
963 oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc";
964 oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc";
965 oemOpenbmc["@odata.context"] =
966 "/redfish/v1/$metadata#OemManager.OpenBmc";
967
Jennifer Leeed5befb2018-08-10 11:29:45 -0700968 // Update Actions object.
Ed Tanous0f74e642018-11-12 15:17:05 -0800969 nlohmann::json& manager_reset =
970 res.jsonValue["Actions"]["#Manager.Reset"];
Jennifer Leeed5befb2018-08-10 11:29:45 -0700971 manager_reset["target"] =
972 "/redfish/v1/Managers/bmc/Actions/Manager.Reset";
973 manager_reset["ResetType@Redfish.AllowableValues"] = {
974 "GracefulRestart"};
Jennifer Leeca537922018-08-10 10:07:30 -0700975
Andrew Geisslercb92c032018-08-17 07:56:14 -0700976 res.jsonValue["DateTime"] = crow::utility::dateTimeNow();
Santosh Puranik474bfad2019-04-02 16:00:09 +0530977
978 // Fill in GraphicalConsole and SerialConsole info
979 res.jsonValue["SerialConsole"]["ServiceEnabled"] = true;
980 res.jsonValue["SerialConsole"]["ConnectTypesSupported"] = {"IPMI",
981 "SSH"};
982 // TODO (Santosh) : Uncomment when KVM support is in.
983 // res.jsonValue["GraphicalConsole"]["ServiceEnabled"] = true;
984 // res.jsonValue["GraphicalConsole"]["ConnectTypesSupported"] =
985 // {"KVMIP"};
986
Gunnar Mills603a6642019-01-21 17:03:51 -0600987 res.jsonValue["Links"]["ManagerForServers@odata.count"] = 1;
988 res.jsonValue["Links"]["ManagerForServers"] = {
989 {{"@odata.id", "/redfish/v1/Systems/system"}}};
990#ifdef BMCWEB_ENABLE_REDFISH_ONE_CHASSIS
991 res.jsonValue["Links"]["ManagerForChassis@odata.count"] = 1;
992 res.jsonValue["Links"]["ManagerForChassis"] = {
993 {{"@odata.id", "/redfish/v1/Chassis/chassis"}}};
994#endif
Jennifer Leeed5befb2018-08-10 11:29:45 -0700995 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
James Feist5b4aa862018-08-16 14:07:01 -0700996
Jennifer Leeca537922018-08-10 10:07:30 -0700997 crow::connections::systemBus->async_method_call(
998 [asyncResp](const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -0700999 const dbus::utility::ManagedObjectType& resp) {
Jennifer Leeca537922018-08-10 10:07:30 -07001000 if (ec)
1001 {
1002 BMCWEB_LOG_ERROR << "Error while getting Software Version";
Jason M. Billsf12894f2018-10-09 12:45:45 -07001003 messages::internalError(asyncResp->res);
Jennifer Leeca537922018-08-10 10:07:30 -07001004 return;
1005 }
1006
James Feist5b4aa862018-08-16 14:07:01 -07001007 for (auto& objpath : resp)
Jennifer Leeca537922018-08-10 10:07:30 -07001008 {
James Feist5b4aa862018-08-16 14:07:01 -07001009 for (auto& interface : objpath.second)
Jennifer Leeca537922018-08-10 10:07:30 -07001010 {
James Feist5f2caae2018-12-12 14:08:25 -08001011 // If interface is
1012 // xyz.openbmc_project.Software.Version, this is
1013 // what we're looking for.
Jennifer Leeca537922018-08-10 10:07:30 -07001014 if (interface.first ==
1015 "xyz.openbmc_project.Software.Version")
1016 {
1017 // Cut out everyting until last "/", ...
James Feist5b4aa862018-08-16 14:07:01 -07001018 for (auto& property : interface.second)
Jennifer Leeca537922018-08-10 10:07:30 -07001019 {
1020 if (property.first == "Version")
1021 {
James Feist5b4aa862018-08-16 14:07:01 -07001022 const std::string* value =
Ed Tanousabf2add2019-01-22 16:40:12 -08001023 std::get_if<std::string>(
1024 &property.second);
Jennifer Leeca537922018-08-10 10:07:30 -07001025 if (value == nullptr)
1026 {
1027 continue;
1028 }
1029 asyncResp->res
1030 .jsonValue["FirmwareVersion"] = *value;
1031 }
1032 }
1033 }
1034 }
1035 }
1036 },
1037 "xyz.openbmc_project.Software.BMC.Updater",
1038 "/xyz/openbmc_project/software",
1039 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
James Feist5b4aa862018-08-16 14:07:01 -07001040 getPidValues(asyncResp);
1041 }
James Feist5f2caae2018-12-12 14:08:25 -08001042 void setPidValues(std::shared_ptr<AsyncResp> response, nlohmann::json& data)
James Feist83ff9ab2018-08-31 10:18:24 -07001043 {
James Feist5f2caae2018-12-12 14:08:25 -08001044
James Feist83ff9ab2018-08-31 10:18:24 -07001045 // todo(james): might make sense to do a mapper call here if this
1046 // interface gets more traction
1047 crow::connections::systemBus->async_method_call(
1048 [response,
1049 data](const boost::system::error_code ec,
1050 const dbus::utility::ManagedObjectType& managedObj) {
1051 if (ec)
1052 {
1053 BMCWEB_LOG_ERROR << "Error communicating to Entity Manager";
Jason M. Bills35a62c72018-10-09 12:45:45 -07001054 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -07001055 return;
1056 }
James Feist5f2caae2018-12-12 14:08:25 -08001057
1058 // todo(james) mutable doesn't work with asio bindings
1059 nlohmann::json jsonData = data;
1060
1061 std::optional<nlohmann::json> pidControllers;
1062 std::optional<nlohmann::json> fanControllers;
1063 std::optional<nlohmann::json> fanZones;
1064 std::optional<nlohmann::json> stepwiseControllers;
1065 if (!redfish::json_util::readJson(
1066 jsonData, response->res, "PidControllers",
1067 pidControllers, "FanControllers", fanControllers,
1068 "FanZones", fanZones, "StepwiseControllers",
1069 stepwiseControllers))
James Feist83ff9ab2018-08-31 10:18:24 -07001070 {
James Feist5f2caae2018-12-12 14:08:25 -08001071 BMCWEB_LOG_ERROR << "Line:" << __LINE__
1072 << ", Illegal Property "
1073 << jsonData.dump();
1074 return;
1075 }
1076 std::array<
Ed Tanous43b761d2019-02-13 20:10:56 -08001077 std::pair<std::string, std::optional<nlohmann::json>*>, 4>
James Feist5f2caae2018-12-12 14:08:25 -08001078 sections = {
1079 std::make_pair("PidControllers", &pidControllers),
1080 std::make_pair("FanControllers", &fanControllers),
1081 std::make_pair("FanZones", &fanZones),
1082 std::make_pair("StepwiseControllers",
1083 &stepwiseControllers)};
1084
1085 for (auto& containerPair : sections)
1086 {
1087 auto& container = *(containerPair.second);
1088 if (!container)
James Feist83ff9ab2018-08-31 10:18:24 -07001089 {
James Feist5f2caae2018-12-12 14:08:25 -08001090 continue;
James Feist83ff9ab2018-08-31 10:18:24 -07001091 }
Ed Tanous43b761d2019-02-13 20:10:56 -08001092 std::string& type = containerPair.first;
James Feist5f2caae2018-12-12 14:08:25 -08001093
James Feistb6baeaa2019-02-21 10:41:40 -08001094 for (nlohmann::json::iterator it = container->begin();
1095 it != container->end(); it++)
James Feist83ff9ab2018-08-31 10:18:24 -07001096 {
James Feistb6baeaa2019-02-21 10:41:40 -08001097 const auto& name = it.key();
James Feist83ff9ab2018-08-31 10:18:24 -07001098 auto pathItr =
1099 std::find_if(managedObj.begin(), managedObj.end(),
1100 [&name](const auto& obj) {
1101 return boost::algorithm::ends_with(
James Feistb6baeaa2019-02-21 10:41:40 -08001102 obj.first.str, "/" + name);
James Feist83ff9ab2018-08-31 10:18:24 -07001103 });
1104 boost::container::flat_map<
1105 std::string, dbus::utility::DbusVariantType>
1106 output;
1107
1108 output.reserve(16); // The pid interface length
1109
1110 // determines if we're patching entity-manager or
1111 // creating a new object
1112 bool createNewObject = (pathItr == managedObj.end());
James Feist5f2caae2018-12-12 14:08:25 -08001113 std::string iface;
1114 if (type == "PidControllers" ||
1115 type == "FanControllers")
James Feist83ff9ab2018-08-31 10:18:24 -07001116 {
James Feist5f2caae2018-12-12 14:08:25 -08001117 iface = pidConfigurationIface;
James Feist83ff9ab2018-08-31 10:18:24 -07001118 if (!createNewObject &&
1119 pathItr->second.find(pidConfigurationIface) ==
1120 pathItr->second.end())
1121 {
1122 createNewObject = true;
1123 }
1124 }
James Feist5f2caae2018-12-12 14:08:25 -08001125 else if (type == "FanZones")
James Feist83ff9ab2018-08-31 10:18:24 -07001126 {
James Feist5f2caae2018-12-12 14:08:25 -08001127 iface = pidZoneConfigurationIface;
1128 if (!createNewObject &&
1129 pathItr->second.find(
1130 pidZoneConfigurationIface) ==
1131 pathItr->second.end())
1132 {
1133
1134 createNewObject = true;
1135 }
1136 }
1137 else if (type == "StepwiseControllers")
1138 {
1139 iface = stepwiseConfigurationIface;
1140 if (!createNewObject &&
1141 pathItr->second.find(
1142 stepwiseConfigurationIface) ==
1143 pathItr->second.end())
1144 {
1145 createNewObject = true;
1146 }
James Feist83ff9ab2018-08-31 10:18:24 -07001147 }
James Feistb6baeaa2019-02-21 10:41:40 -08001148 BMCWEB_LOG_DEBUG << "Create new = " << createNewObject
1149 << "\n";
James Feist83ff9ab2018-08-31 10:18:24 -07001150 output["Name"] =
1151 boost::replace_all_copy(name, "_", " ");
1152
1153 std::string chassis;
1154 CreatePIDRet ret = createPidInterface(
James Feistb6baeaa2019-02-21 10:41:40 -08001155 response, type, it, pathItr->first.str, managedObj,
1156 createNewObject, output, chassis);
James Feist83ff9ab2018-08-31 10:18:24 -07001157 if (ret == CreatePIDRet::fail)
1158 {
1159 return;
1160 }
1161 else if (ret == CreatePIDRet::del)
1162 {
1163 continue;
1164 }
1165
1166 if (!createNewObject)
1167 {
1168 for (const auto& property : output)
1169 {
James Feist83ff9ab2018-08-31 10:18:24 -07001170 crow::connections::systemBus->async_method_call(
1171 [response,
1172 propertyName{std::string(property.first)}](
1173 const boost::system::error_code ec) {
1174 if (ec)
1175 {
1176 BMCWEB_LOG_ERROR
1177 << "Error patching "
1178 << propertyName << ": " << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -07001179 messages::internalError(
1180 response->res);
James Feistb6baeaa2019-02-21 10:41:40 -08001181 return;
James Feist83ff9ab2018-08-31 10:18:24 -07001182 }
James Feistb6baeaa2019-02-21 10:41:40 -08001183 messages::success(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -07001184 },
1185 "xyz.openbmc_project.EntityManager",
1186 pathItr->first.str,
1187 "org.freedesktop.DBus.Properties", "Set",
James Feist5f2caae2018-12-12 14:08:25 -08001188 iface, property.first, property.second);
James Feist83ff9ab2018-08-31 10:18:24 -07001189 }
1190 }
1191 else
1192 {
1193 if (chassis.empty())
1194 {
1195 BMCWEB_LOG_ERROR
1196 << "Failed to get chassis from config";
Jason M. Bills35a62c72018-10-09 12:45:45 -07001197 messages::invalidObject(response->res, name);
James Feist83ff9ab2018-08-31 10:18:24 -07001198 return;
1199 }
1200
1201 bool foundChassis = false;
1202 for (const auto& obj : managedObj)
1203 {
1204 if (boost::algorithm::ends_with(obj.first.str,
1205 chassis))
1206 {
1207 chassis = obj.first.str;
1208 foundChassis = true;
1209 break;
1210 }
1211 }
1212 if (!foundChassis)
1213 {
1214 BMCWEB_LOG_ERROR
1215 << "Failed to find chassis on dbus";
Jason M. Bills35a62c72018-10-09 12:45:45 -07001216 messages::resourceMissingAtURI(
1217 response->res,
1218 "/redfish/v1/Chassis/" + chassis);
James Feist83ff9ab2018-08-31 10:18:24 -07001219 return;
1220 }
1221
1222 crow::connections::systemBus->async_method_call(
1223 [response](const boost::system::error_code ec) {
1224 if (ec)
1225 {
1226 BMCWEB_LOG_ERROR
1227 << "Error Adding Pid Object " << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -07001228 messages::internalError(response->res);
James Feistb6baeaa2019-02-21 10:41:40 -08001229 return;
James Feist83ff9ab2018-08-31 10:18:24 -07001230 }
James Feistb6baeaa2019-02-21 10:41:40 -08001231 messages::success(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -07001232 },
1233 "xyz.openbmc_project.EntityManager", chassis,
1234 "xyz.openbmc_project.AddObject", "AddObject",
1235 output);
1236 }
1237 }
1238 }
1239 },
1240 "xyz.openbmc_project.EntityManager", "/", objectManagerIface,
1241 "GetManagedObjects");
1242 }
James Feist5b4aa862018-08-16 14:07:01 -07001243
1244 void doPatch(crow::Response& res, const crow::Request& req,
1245 const std::vector<std::string>& params) override
1246 {
Ed Tanous0627a2c2018-11-29 17:09:23 -08001247 std::optional<nlohmann::json> oem;
Santosh Puranikaf5d60582019-03-20 18:16:36 +05301248 std::optional<std::string> datetime;
Ed Tanous0627a2c2018-11-29 17:09:23 -08001249
Santosh Puranikaf5d60582019-03-20 18:16:36 +05301250 if (!json_util::readJson(req, res, "Oem", oem, "DateTime", datetime))
James Feist83ff9ab2018-08-31 10:18:24 -07001251 {
1252 return;
1253 }
Ed Tanous0627a2c2018-11-29 17:09:23 -08001254
James Feist83ff9ab2018-08-31 10:18:24 -07001255 std::shared_ptr<AsyncResp> response = std::make_shared<AsyncResp>(res);
Ed Tanous0627a2c2018-11-29 17:09:23 -08001256
1257 if (oem)
James Feist83ff9ab2018-08-31 10:18:24 -07001258 {
Ed Tanous43b761d2019-02-13 20:10:56 -08001259 std::optional<nlohmann::json> openbmc;
1260 if (!redfish::json_util::readJson(*oem, res, "OpenBmc", openbmc))
James Feist83ff9ab2018-08-31 10:18:24 -07001261 {
Ed Tanous43b761d2019-02-13 20:10:56 -08001262 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
1263 << oem->dump();
1264 return;
1265 }
1266 if (openbmc)
1267 {
1268 std::optional<nlohmann::json> fan;
1269 if (!redfish::json_util::readJson(*openbmc, res, "Fan", fan))
James Feist83ff9ab2018-08-31 10:18:24 -07001270 {
James Feist5f2caae2018-12-12 14:08:25 -08001271 BMCWEB_LOG_ERROR << "Line:" << __LINE__
Ed Tanous43b761d2019-02-13 20:10:56 -08001272 << ", Illegal Property "
1273 << openbmc->dump();
James Feist5f2caae2018-12-12 14:08:25 -08001274 return;
1275 }
Ed Tanous43b761d2019-02-13 20:10:56 -08001276 if (fan)
James Feist5f2caae2018-12-12 14:08:25 -08001277 {
Ed Tanous43b761d2019-02-13 20:10:56 -08001278 setPidValues(response, *fan);
James Feist83ff9ab2018-08-31 10:18:24 -07001279 }
James Feist83ff9ab2018-08-31 10:18:24 -07001280 }
1281 }
Santosh Puranikaf5d60582019-03-20 18:16:36 +05301282 if (datetime)
1283 {
1284 setDateTime(response, std::move(*datetime));
1285 }
1286 }
1287
1288 void setDateTime(std::shared_ptr<AsyncResp> aResp,
1289 std::string datetime) const
1290 {
1291 BMCWEB_LOG_DEBUG << "Set date time: " << datetime;
1292
1293 std::stringstream stream(datetime);
1294 // Convert from ISO 8601 to boost local_time
1295 // (BMC only has time in UTC)
1296 boost::posix_time::ptime posixTime;
1297 boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
1298 // Facet gets deleted with the stringsteam
1299 auto ifc = std::make_unique<boost::local_time::local_time_input_facet>(
1300 "%Y-%m-%d %H:%M:%S%F %ZP");
1301 stream.imbue(std::locale(stream.getloc(), ifc.release()));
1302
1303 boost::local_time::local_date_time ldt(
1304 boost::local_time::not_a_date_time);
1305
1306 if (stream >> ldt)
1307 {
1308 posixTime = ldt.utc_time();
1309 boost::posix_time::time_duration dur = posixTime - epoch;
1310 uint64_t durMicroSecs =
1311 static_cast<uint64_t>(dur.total_microseconds());
1312 crow::connections::systemBus->async_method_call(
1313 [aResp{std::move(aResp)}, datetime{std::move(datetime)}](
1314 const boost::system::error_code ec) {
1315 if (ec)
1316 {
1317 BMCWEB_LOG_DEBUG << "Failed to set elapsed time. "
1318 "DBUS response error "
1319 << ec;
1320 messages::internalError(aResp->res);
1321 return;
1322 }
1323 aResp->res.jsonValue["DateTime"] = datetime;
1324 },
1325 "xyz.openbmc_project.Time.Manager",
1326 "/xyz/openbmc_project/time/bmc",
1327 "org.freedesktop.DBus.Properties", "Set",
1328 "xyz.openbmc_project.Time.EpochTime", "Elapsed",
1329 std::variant<uint64_t>(durMicroSecs));
1330 }
1331 else
1332 {
1333 messages::propertyValueFormatError(aResp->res, datetime,
1334 "DateTime");
1335 return;
1336 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001337 }
1338
Ed Tanous0f74e642018-11-12 15:17:05 -08001339 std::string uuid;
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001340};
1341
Ed Tanous1abe55e2018-09-05 08:30:59 -07001342class ManagerCollection : public Node
1343{
1344 public:
James Feist5b4aa862018-08-16 14:07:01 -07001345 ManagerCollection(CrowApp& app) : Node(app, "/redfish/v1/Managers/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001346 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001347 entityPrivileges = {
1348 {boost::beast::http::verb::get, {{"Login"}}},
1349 {boost::beast::http::verb::head, {{"Login"}}},
1350 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1351 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1352 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1353 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1354 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001355
Ed Tanous1abe55e2018-09-05 08:30:59 -07001356 private:
James Feist5b4aa862018-08-16 14:07:01 -07001357 void doGet(crow::Response& res, const crow::Request& req,
1358 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -07001359 {
James Feist83ff9ab2018-08-31 10:18:24 -07001360 // Collections don't include the static data added by SubRoute
1361 // because it has a duplicate entry for members
Ed Tanous1abe55e2018-09-05 08:30:59 -07001362 res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
1363 res.jsonValue["@odata.type"] = "#ManagerCollection.ManagerCollection";
1364 res.jsonValue["@odata.context"] =
1365 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection";
1366 res.jsonValue["Name"] = "Manager Collection";
1367 res.jsonValue["Members@odata.count"] = 1;
1368 res.jsonValue["Members"] = {
James Feist5b4aa862018-08-16 14:07:01 -07001369 {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001370 res.end();
1371 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001372};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001373} // namespace redfish