blob: 65de109e3a37a1e7fe342cba97982dca44d86c1b [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
James Feistb49ac872019-05-21 15:12:01 -070018#include "health.hpp"
Borawski.Lukasz9c3106852018-02-09 15:24:22 +010019#include "node.hpp"
Jennifer Leec5d03ff2019-03-08 15:42:58 -080020#include "redfish_util.hpp"
Borawski.Lukasz9c3106852018-02-09 15:24:22 +010021
James Feist5b4aa862018-08-16 14:07:01 -070022#include <boost/algorithm/string/replace.hpp>
Santosh Puranikaf5d60582019-03-20 18:16:36 +053023#include <boost/date_time.hpp>
James Feist5b4aa862018-08-16 14:07:01 -070024#include <dbus_utility.hpp>
James Feist73df0db2019-03-25 15:29:35 -070025#include <memory>
Santosh Puranikaf5d60582019-03-20 18:16:36 +053026#include <sstream>
Andrew Geisslere90c5052019-06-28 13:52:27 -050027#include <utils/fw_utils.hpp>
Bernard Wong7bffdb72019-03-20 16:17:21 +080028#include <utils/systemd_utils.hpp>
Ed Tanousabf2add2019-01-22 16:40:12 -080029#include <variant>
James Feist5b4aa862018-08-16 14:07:01 -070030
Ed Tanous1abe55e2018-09-05 08:30:59 -070031namespace redfish
32{
Jennifer Leeed5befb2018-08-10 11:29:45 -070033
34/**
Gunnar Mills2a5c4402020-05-19 09:07:24 -050035 * Function reboots the BMC.
36 *
37 * @param[in] asyncResp - Shared pointer for completing asynchronous calls
Jennifer Leeed5befb2018-08-10 11:29:45 -070038 */
Gunnar Mills2a5c4402020-05-19 09:07:24 -050039void doBMCGracefulRestart(std::shared_ptr<AsyncResp> asyncResp)
40{
41 const char* processName = "xyz.openbmc_project.State.BMC";
42 const char* objectPath = "/xyz/openbmc_project/state/bmc0";
43 const char* interfaceName = "xyz.openbmc_project.State.BMC";
44 const std::string& propertyValue =
45 "xyz.openbmc_project.State.BMC.Transition.Reboot";
46 const char* destProperty = "RequestedBMCTransition";
47
48 // Create the D-Bus variant for D-Bus call.
49 VariantType dbusPropertyValue(propertyValue);
50
51 crow::connections::systemBus->async_method_call(
52 [asyncResp](const boost::system::error_code ec) {
53 // Use "Set" method to set the property value.
54 if (ec)
55 {
56 BMCWEB_LOG_DEBUG << "[Set] Bad D-Bus request error: " << ec;
57 messages::internalError(asyncResp->res);
58 return;
59 }
60
61 messages::success(asyncResp->res);
62 },
63 processName, objectPath, "org.freedesktop.DBus.Properties", "Set",
64 interfaceName, destProperty, dbusPropertyValue);
65}
66
67/**
68 * ManagerResetAction class supports the POST method for the Reset (reboot)
69 * action.
70 */
71class ManagerResetAction : public Node
Jennifer Leeed5befb2018-08-10 11:29:45 -070072{
73 public:
Gunnar Mills2a5c4402020-05-19 09:07:24 -050074 ManagerResetAction(CrowApp& app) :
Jennifer Leeed5befb2018-08-10 11:29:45 -070075 Node(app, "/redfish/v1/Managers/bmc/Actions/Manager.Reset/")
76 {
77 entityPrivileges = {
Jennifer Leeed5befb2018-08-10 11:29:45 -070078 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
79 }
80
81 private:
82 /**
Jennifer Leeed5befb2018-08-10 11:29:45 -070083 * Function handles POST method request.
Gunnar Mills2a5c4402020-05-19 09:07:24 -050084 * Analyzes POST body before sending Reset (Reboot) request data to D-Bus.
85 * OpenBMC only supports ResetType "GracefulRestart".
Jennifer Leeed5befb2018-08-10 11:29:45 -070086 */
87 void doPost(crow::Response& res, const crow::Request& req,
88 const std::vector<std::string>& params) override
89 {
Gunnar Mills2a5c4402020-05-19 09:07:24 -050090 BMCWEB_LOG_DEBUG << "Post Manager Reset.";
Jennifer Leeed5befb2018-08-10 11:29:45 -070091
Gunnar Mills2a5c4402020-05-19 09:07:24 -050092 std::string resetType;
93 auto asyncResp = std::make_shared<AsyncResp>(res);
94
95 if (!json_util::readJson(req, asyncResp->res, "ResetType", resetType))
Jennifer Leeed5befb2018-08-10 11:29:45 -070096 {
97 return;
98 }
99
100 if (resetType != "GracefulRestart")
101 {
Gunnar Mills2a5c4402020-05-19 09:07:24 -0500102 BMCWEB_LOG_DEBUG << "Invalid property value for ResetType: "
Jennifer Leeed5befb2018-08-10 11:29:45 -0700103 << resetType;
Gunnar Mills2a5c4402020-05-19 09:07:24 -0500104 messages::actionParameterNotSupported(asyncResp->res, resetType,
105 "ResetType");
106
Jennifer Leeed5befb2018-08-10 11:29:45 -0700107 return;
108 }
Gunnar Mills2a5c4402020-05-19 09:07:24 -0500109 doBMCGracefulRestart(asyncResp);
Jennifer Leeed5befb2018-08-10 11:29:45 -0700110 }
111};
112
James Feist5b4aa862018-08-16 14:07:01 -0700113static constexpr const char* objectManagerIface =
114 "org.freedesktop.DBus.ObjectManager";
115static constexpr const char* pidConfigurationIface =
116 "xyz.openbmc_project.Configuration.Pid";
117static constexpr const char* pidZoneConfigurationIface =
118 "xyz.openbmc_project.Configuration.Pid.Zone";
James Feistb7a08d02018-12-11 14:55:37 -0800119static constexpr const char* stepwiseConfigurationIface =
120 "xyz.openbmc_project.Configuration.Stepwise";
James Feist73df0db2019-03-25 15:29:35 -0700121static constexpr const char* thermalModeIface =
122 "xyz.openbmc_project.Control.ThermalMode";
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100123
James Feist5b4aa862018-08-16 14:07:01 -0700124static void asyncPopulatePid(const std::string& connection,
125 const std::string& path,
James Feist73df0db2019-03-25 15:29:35 -0700126 const std::string& currentProfile,
127 const std::vector<std::string>& supportedProfiles,
James Feist5b4aa862018-08-16 14:07:01 -0700128 std::shared_ptr<AsyncResp> asyncResp)
129{
130
131 crow::connections::systemBus->async_method_call(
James Feist73df0db2019-03-25 15:29:35 -0700132 [asyncResp, currentProfile, supportedProfiles](
133 const boost::system::error_code ec,
134 const dbus::utility::ManagedObjectType& managedObj) {
James Feist5b4aa862018-08-16 14:07:01 -0700135 if (ec)
136 {
137 BMCWEB_LOG_ERROR << ec;
James Feist5b4aa862018-08-16 14:07:01 -0700138 asyncResp->res.jsonValue.clear();
Jason M. Billsf12894f2018-10-09 12:45:45 -0700139 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700140 return;
141 }
142 nlohmann::json& configRoot =
143 asyncResp->res.jsonValue["Oem"]["OpenBmc"]["Fan"];
144 nlohmann::json& fans = configRoot["FanControllers"];
145 fans["@odata.type"] = "#OemManager.FanControllers";
James Feist5b4aa862018-08-16 14:07:01 -0700146 fans["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc/"
147 "Fan/FanControllers";
148
149 nlohmann::json& pids = configRoot["PidControllers"];
150 pids["@odata.type"] = "#OemManager.PidControllers";
James Feist5b4aa862018-08-16 14:07:01 -0700151 pids["@odata.id"] =
152 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/PidControllers";
153
James Feistb7a08d02018-12-11 14:55:37 -0800154 nlohmann::json& stepwise = configRoot["StepwiseControllers"];
155 stepwise["@odata.type"] = "#OemManager.StepwiseControllers";
James Feistb7a08d02018-12-11 14:55:37 -0800156 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";
James Feist5b4aa862018-08-16 14:07:01 -0700163 configRoot["@odata.id"] =
164 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan";
165 configRoot["@odata.type"] = "#OemManager.Fan";
James Feist73df0db2019-03-25 15:29:35 -0700166 configRoot["Profile@Redfish.AllowableValues"] = supportedProfiles;
167
168 if (!currentProfile.empty())
169 {
170 configRoot["Profile"] = currentProfile;
171 }
172 BMCWEB_LOG_ERROR << "profile = " << currentProfile << " !";
James Feist5b4aa862018-08-16 14:07:01 -0700173
James Feist5b4aa862018-08-16 14:07:01 -0700174 for (const auto& pathPair : managedObj)
175 {
176 for (const auto& intfPair : pathPair.second)
177 {
178 if (intfPair.first != pidConfigurationIface &&
James Feistb7a08d02018-12-11 14:55:37 -0800179 intfPair.first != pidZoneConfigurationIface &&
180 intfPair.first != stepwiseConfigurationIface)
James Feist5b4aa862018-08-16 14:07:01 -0700181 {
182 continue;
183 }
184 auto findName = intfPair.second.find("Name");
185 if (findName == intfPair.second.end())
186 {
187 BMCWEB_LOG_ERROR << "Pid Field missing Name";
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800188 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700189 return;
190 }
James Feist73df0db2019-03-25 15:29:35 -0700191
James Feist5b4aa862018-08-16 14:07:01 -0700192 const std::string* namePtr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800193 std::get_if<std::string>(&findName->second);
James Feist5b4aa862018-08-16 14:07:01 -0700194 if (namePtr == nullptr)
195 {
196 BMCWEB_LOG_ERROR << "Pid Name Field illegal";
James Feistb7a08d02018-12-11 14:55:37 -0800197 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700198 return;
199 }
James Feist5b4aa862018-08-16 14:07:01 -0700200 std::string name = *namePtr;
201 dbus::utility::escapePathForDbus(name);
James Feist73df0db2019-03-25 15:29:35 -0700202
203 auto findProfiles = intfPair.second.find("Profiles");
204 if (findProfiles != intfPair.second.end())
205 {
206 const std::vector<std::string>* profiles =
207 std::get_if<std::vector<std::string>>(
208 &findProfiles->second);
209 if (profiles == nullptr)
210 {
211 BMCWEB_LOG_ERROR << "Pid Profiles Field illegal";
212 messages::internalError(asyncResp->res);
213 return;
214 }
215 if (std::find(profiles->begin(), profiles->end(),
216 currentProfile) == profiles->end())
217 {
218 BMCWEB_LOG_INFO
219 << name << " not supported in current profile";
220 continue;
221 }
222 }
James Feistb7a08d02018-12-11 14:55:37 -0800223 nlohmann::json* config = nullptr;
James Feistc33a90e2019-03-01 10:17:44 -0800224
225 const std::string* classPtr = nullptr;
226 auto findClass = intfPair.second.find("Class");
227 if (findClass != intfPair.second.end())
228 {
229 classPtr = std::get_if<std::string>(&findClass->second);
230 }
231
James Feist5b4aa862018-08-16 14:07:01 -0700232 if (intfPair.first == pidZoneConfigurationIface)
233 {
234 std::string chassis;
235 if (!dbus::utility::getNthStringFromPath(
236 pathPair.first.str, 5, chassis))
237 {
238 chassis = "#IllegalValue";
239 }
240 nlohmann::json& zone = zones[name];
241 zone["Chassis"] = {
242 {"@odata.id", "/redfish/v1/Chassis/" + chassis}};
243 zone["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/"
244 "OpenBmc/Fan/FanZones/" +
245 name;
246 zone["@odata.type"] = "#OemManager.FanZone";
James Feistb7a08d02018-12-11 14:55:37 -0800247 config = &zone;
James Feist5b4aa862018-08-16 14:07:01 -0700248 }
249
James Feistb7a08d02018-12-11 14:55:37 -0800250 else if (intfPair.first == stepwiseConfigurationIface)
251 {
James Feistc33a90e2019-03-01 10:17:44 -0800252 if (classPtr == nullptr)
253 {
254 BMCWEB_LOG_ERROR << "Pid Class Field illegal";
255 messages::internalError(asyncResp->res);
256 return;
257 }
258
James Feistb7a08d02018-12-11 14:55:37 -0800259 nlohmann::json& controller = stepwise[name];
260 config = &controller;
261
262 controller["@odata.id"] =
263 "/redfish/v1/Managers/bmc#/Oem/"
264 "OpenBmc/Fan/StepwiseControllers/" +
Ed Tanous271584a2019-07-09 16:24:22 -0700265 name;
James Feistb7a08d02018-12-11 14:55:37 -0800266 controller["@odata.type"] =
267 "#OemManager.StepwiseController";
268
James Feistc33a90e2019-03-01 10:17:44 -0800269 controller["Direction"] = *classPtr;
James Feistb7a08d02018-12-11 14:55:37 -0800270 }
271
272 // pid and fans are off the same configuration
273 else if (intfPair.first == pidConfigurationIface)
274 {
James Feistc33a90e2019-03-01 10:17:44 -0800275
James Feistb7a08d02018-12-11 14:55:37 -0800276 if (classPtr == nullptr)
277 {
278 BMCWEB_LOG_ERROR << "Pid Class Field illegal";
279 messages::internalError(asyncResp->res);
280 return;
281 }
282 bool isFan = *classPtr == "fan";
283 nlohmann::json& element =
284 isFan ? fans[name] : pids[name];
285 config = &element;
286 if (isFan)
287 {
288 element["@odata.id"] =
289 "/redfish/v1/Managers/bmc#/Oem/"
290 "OpenBmc/Fan/FanControllers/" +
Ed Tanous271584a2019-07-09 16:24:22 -0700291 name;
James Feistb7a08d02018-12-11 14:55:37 -0800292 element["@odata.type"] =
293 "#OemManager.FanController";
James Feistb7a08d02018-12-11 14:55:37 -0800294 }
295 else
296 {
297 element["@odata.id"] =
298 "/redfish/v1/Managers/bmc#/Oem/"
299 "OpenBmc/Fan/PidControllers/" +
Ed Tanous271584a2019-07-09 16:24:22 -0700300 name;
James Feistb7a08d02018-12-11 14:55:37 -0800301 element["@odata.type"] =
302 "#OemManager.PidController";
James Feistb7a08d02018-12-11 14:55:37 -0800303 }
304 }
305 else
306 {
307 BMCWEB_LOG_ERROR << "Unexpected configuration";
308 messages::internalError(asyncResp->res);
309 return;
310 }
311
312 // used for making maps out of 2 vectors
313 const std::vector<double>* keys = nullptr;
314 const std::vector<double>* values = nullptr;
315
James Feist5b4aa862018-08-16 14:07:01 -0700316 for (const auto& propertyPair : intfPair.second)
317 {
318 if (propertyPair.first == "Type" ||
319 propertyPair.first == "Class" ||
320 propertyPair.first == "Name")
321 {
322 continue;
323 }
324
325 // zones
326 if (intfPair.first == pidZoneConfigurationIface)
327 {
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800328 const double* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800329 std::get_if<double>(&propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700330 if (ptr == nullptr)
331 {
332 BMCWEB_LOG_ERROR << "Field Illegal "
333 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700334 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700335 return;
336 }
James Feistb7a08d02018-12-11 14:55:37 -0800337 (*config)[propertyPair.first] = *ptr;
338 }
339
340 if (intfPair.first == stepwiseConfigurationIface)
341 {
342 if (propertyPair.first == "Reading" ||
343 propertyPair.first == "Output")
344 {
345 const std::vector<double>* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800346 std::get_if<std::vector<double>>(
James Feistb7a08d02018-12-11 14:55:37 -0800347 &propertyPair.second);
348
349 if (ptr == nullptr)
350 {
351 BMCWEB_LOG_ERROR << "Field Illegal "
352 << propertyPair.first;
353 messages::internalError(asyncResp->res);
354 return;
355 }
356
357 if (propertyPair.first == "Reading")
358 {
359 keys = ptr;
360 }
361 else
362 {
363 values = ptr;
364 }
365 if (keys && values)
366 {
367 if (keys->size() != values->size())
368 {
369 BMCWEB_LOG_ERROR
370 << "Reading and Output size don't "
371 "match ";
372 messages::internalError(asyncResp->res);
373 return;
374 }
375 nlohmann::json& steps = (*config)["Steps"];
376 steps = nlohmann::json::array();
377 for (size_t ii = 0; ii < keys->size(); ii++)
378 {
379 steps.push_back(
380 {{"Target", (*keys)[ii]},
381 {"Output", (*values)[ii]}});
382 }
383 }
384 }
385 if (propertyPair.first == "NegativeHysteresis" ||
386 propertyPair.first == "PositiveHysteresis")
387 {
388 const double* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800389 std::get_if<double>(&propertyPair.second);
James Feistb7a08d02018-12-11 14:55:37 -0800390 if (ptr == nullptr)
391 {
392 BMCWEB_LOG_ERROR << "Field Illegal "
393 << propertyPair.first;
394 messages::internalError(asyncResp->res);
395 return;
396 }
397 (*config)[propertyPair.first] = *ptr;
398 }
James Feist5b4aa862018-08-16 14:07:01 -0700399 }
400
401 // pid and fans are off the same configuration
James Feistb7a08d02018-12-11 14:55:37 -0800402 if (intfPair.first == pidConfigurationIface ||
403 intfPair.first == stepwiseConfigurationIface)
James Feist5b4aa862018-08-16 14:07:01 -0700404 {
James Feist5b4aa862018-08-16 14:07:01 -0700405
406 if (propertyPair.first == "Zones")
407 {
408 const std::vector<std::string>* inputs =
Ed Tanousabf2add2019-01-22 16:40:12 -0800409 std::get_if<std::vector<std::string>>(
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800410 &propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700411
412 if (inputs == nullptr)
413 {
414 BMCWEB_LOG_ERROR
415 << "Zones Pid Field Illegal";
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800416 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700417 return;
418 }
James Feistb7a08d02018-12-11 14:55:37 -0800419 auto& data = (*config)[propertyPair.first];
James Feist5b4aa862018-08-16 14:07:01 -0700420 data = nlohmann::json::array();
421 for (std::string itemCopy : *inputs)
422 {
423 dbus::utility::escapePathForDbus(itemCopy);
424 data.push_back(
425 {{"@odata.id",
426 "/redfish/v1/Managers/bmc#/Oem/"
427 "OpenBmc/Fan/FanZones/" +
428 itemCopy}});
429 }
430 }
431 // todo(james): may never happen, but this
432 // assumes configuration data referenced in the
433 // PID config is provided by the same daemon, we
434 // could add another loop to cover all cases,
435 // but I'm okay kicking this can down the road a
436 // bit
437
438 else if (propertyPair.first == "Inputs" ||
439 propertyPair.first == "Outputs")
440 {
James Feistb7a08d02018-12-11 14:55:37 -0800441 auto& data = (*config)[propertyPair.first];
James Feist5b4aa862018-08-16 14:07:01 -0700442 const std::vector<std::string>* inputs =
Ed Tanousabf2add2019-01-22 16:40:12 -0800443 std::get_if<std::vector<std::string>>(
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800444 &propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700445
446 if (inputs == nullptr)
447 {
448 BMCWEB_LOG_ERROR << "Field Illegal "
449 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700450 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700451 return;
452 }
453 data = *inputs;
James Feistb943aae2019-07-11 16:33:56 -0700454 }
455 else if (propertyPair.first == "SetPointOffset")
456 {
457 const std::string* ptr =
458 std::get_if<std::string>(
459 &propertyPair.second);
460
461 if (ptr == nullptr)
462 {
463 BMCWEB_LOG_ERROR << "Field Illegal "
464 << propertyPair.first;
465 messages::internalError(asyncResp->res);
466 return;
467 }
468 // translate from dbus to redfish
469 if (*ptr == "WarningHigh")
470 {
471 (*config)["SetPointOffset"] =
472 "UpperThresholdNonCritical";
473 }
474 else if (*ptr == "WarningLow")
475 {
476 (*config)["SetPointOffset"] =
477 "LowerThresholdNonCritical";
478 }
479 else if (*ptr == "CriticalHigh")
480 {
481 (*config)["SetPointOffset"] =
482 "UpperThresholdCritical";
483 }
484 else if (*ptr == "CriticalLow")
485 {
486 (*config)["SetPointOffset"] =
487 "LowerThresholdCritical";
488 }
489 else
490 {
491 BMCWEB_LOG_ERROR << "Value Illegal "
492 << *ptr;
493 messages::internalError(asyncResp->res);
494 return;
495 }
496 }
497 // doubles
James Feist5b4aa862018-08-16 14:07:01 -0700498 else if (propertyPair.first ==
499 "FFGainCoefficient" ||
500 propertyPair.first == "FFOffCoefficient" ||
501 propertyPair.first == "ICoefficient" ||
502 propertyPair.first == "ILimitMax" ||
503 propertyPair.first == "ILimitMin" ||
James Feistaad1a252019-02-19 10:13:52 -0800504 propertyPair.first ==
505 "PositiveHysteresis" ||
506 propertyPair.first ==
507 "NegativeHysteresis" ||
James Feist5b4aa862018-08-16 14:07:01 -0700508 propertyPair.first == "OutLimitMax" ||
509 propertyPair.first == "OutLimitMin" ||
510 propertyPair.first == "PCoefficient" ||
James Feist7625cb82019-01-23 11:58:21 -0800511 propertyPair.first == "SetPoint" ||
James Feist5b4aa862018-08-16 14:07:01 -0700512 propertyPair.first == "SlewNeg" ||
513 propertyPair.first == "SlewPos")
514 {
515 const double* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800516 std::get_if<double>(&propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700517 if (ptr == nullptr)
518 {
519 BMCWEB_LOG_ERROR << "Field Illegal "
520 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700521 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700522 return;
523 }
James Feistb7a08d02018-12-11 14:55:37 -0800524 (*config)[propertyPair.first] = *ptr;
James Feist5b4aa862018-08-16 14:07:01 -0700525 }
526 }
527 }
528 }
529 }
530 },
531 connection, path, objectManagerIface, "GetManagedObjects");
532}
Jennifer Leeca537922018-08-10 10:07:30 -0700533
James Feist83ff9ab2018-08-31 10:18:24 -0700534enum class CreatePIDRet
535{
536 fail,
537 del,
538 patch
539};
540
James Feist5f2caae2018-12-12 14:08:25 -0800541static bool getZonesFromJsonReq(const std::shared_ptr<AsyncResp>& response,
542 std::vector<nlohmann::json>& config,
543 std::vector<std::string>& zones)
544{
James Feistb6baeaa2019-02-21 10:41:40 -0800545 if (config.empty())
546 {
547 BMCWEB_LOG_ERROR << "Empty Zones";
548 messages::propertyValueFormatError(response->res,
549 nlohmann::json::array(), "Zones");
550 return false;
551 }
James Feist5f2caae2018-12-12 14:08:25 -0800552 for (auto& odata : config)
553 {
554 std::string path;
555 if (!redfish::json_util::readJson(odata, response->res, "@odata.id",
556 path))
557 {
558 return false;
559 }
560 std::string input;
James Feist61adbda2019-03-25 13:03:51 -0700561
562 // 8 below comes from
563 // /redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones/Left
564 // 0 1 2 3 4 5 6 7 8
565 if (!dbus::utility::getNthStringFromPath(path, 8, input))
James Feist5f2caae2018-12-12 14:08:25 -0800566 {
567 BMCWEB_LOG_ERROR << "Got invalid path " << path;
568 BMCWEB_LOG_ERROR << "Illegal Type Zones";
569 messages::propertyValueFormatError(response->res, odata.dump(),
570 "Zones");
571 return false;
572 }
573 boost::replace_all(input, "_", " ");
574 zones.emplace_back(std::move(input));
575 }
576 return true;
577}
578
James Feist73df0db2019-03-25 15:29:35 -0700579static const dbus::utility::ManagedItem*
580 findChassis(const dbus::utility::ManagedObjectType& managedObj,
581 const std::string& value, std::string& chassis)
James Feistb6baeaa2019-02-21 10:41:40 -0800582{
583 BMCWEB_LOG_DEBUG << "Find Chassis: " << value << "\n";
584
585 std::string escaped = boost::replace_all_copy(value, " ", "_");
586 escaped = "/" + escaped;
587 auto it = std::find_if(
588 managedObj.begin(), managedObj.end(), [&escaped](const auto& obj) {
589 if (boost::algorithm::ends_with(obj.first.str, escaped))
590 {
591 BMCWEB_LOG_DEBUG << "Matched " << obj.first.str << "\n";
592 return true;
593 }
594 return false;
595 });
596
597 if (it == managedObj.end())
598 {
James Feist73df0db2019-03-25 15:29:35 -0700599 return nullptr;
James Feistb6baeaa2019-02-21 10:41:40 -0800600 }
601 // 5 comes from <chassis-name> being the 5th element
602 // /xyz/openbmc_project/inventory/system/chassis/<chassis-name>
James Feist73df0db2019-03-25 15:29:35 -0700603 if (dbus::utility::getNthStringFromPath(it->first.str, 5, chassis))
604 {
605 return &(*it);
606 }
607
608 return nullptr;
James Feistb6baeaa2019-02-21 10:41:40 -0800609}
610
James Feist83ff9ab2018-08-31 10:18:24 -0700611static CreatePIDRet createPidInterface(
612 const std::shared_ptr<AsyncResp>& response, const std::string& type,
James Feistb6baeaa2019-02-21 10:41:40 -0800613 nlohmann::json::iterator it, const std::string& path,
James Feist83ff9ab2018-08-31 10:18:24 -0700614 const dbus::utility::ManagedObjectType& managedObj, bool createNewObject,
615 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>&
616 output,
James Feist73df0db2019-03-25 15:29:35 -0700617 std::string& chassis, const std::string& profile)
James Feist83ff9ab2018-08-31 10:18:24 -0700618{
619
James Feist5f2caae2018-12-12 14:08:25 -0800620 // common deleter
James Feistb6baeaa2019-02-21 10:41:40 -0800621 if (it.value() == nullptr)
James Feist5f2caae2018-12-12 14:08:25 -0800622 {
623 std::string iface;
624 if (type == "PidControllers" || type == "FanControllers")
625 {
626 iface = pidConfigurationIface;
627 }
628 else if (type == "FanZones")
629 {
630 iface = pidZoneConfigurationIface;
631 }
632 else if (type == "StepwiseControllers")
633 {
634 iface = stepwiseConfigurationIface;
635 }
636 else
637 {
638 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Type "
639 << type;
640 messages::propertyUnknown(response->res, type);
641 return CreatePIDRet::fail;
642 }
James Feist6ee7f772020-02-06 16:25:27 -0800643
644 BMCWEB_LOG_DEBUG << "del " << path << " " << iface << "\n";
James Feist5f2caae2018-12-12 14:08:25 -0800645 // delete interface
646 crow::connections::systemBus->async_method_call(
647 [response, path](const boost::system::error_code ec) {
648 if (ec)
649 {
650 BMCWEB_LOG_ERROR << "Error patching " << path << ": " << ec;
651 messages::internalError(response->res);
James Feistb6baeaa2019-02-21 10:41:40 -0800652 return;
James Feist5f2caae2018-12-12 14:08:25 -0800653 }
James Feistb6baeaa2019-02-21 10:41:40 -0800654 messages::success(response->res);
James Feist5f2caae2018-12-12 14:08:25 -0800655 },
656 "xyz.openbmc_project.EntityManager", path, iface, "Delete");
657 return CreatePIDRet::del;
658 }
659
James Feist73df0db2019-03-25 15:29:35 -0700660 const dbus::utility::ManagedItem* managedItem = nullptr;
James Feistb6baeaa2019-02-21 10:41:40 -0800661 if (!createNewObject)
662 {
663 // if we aren't creating a new object, we should be able to find it on
664 // d-bus
James Feist73df0db2019-03-25 15:29:35 -0700665 managedItem = findChassis(managedObj, it.key(), chassis);
666 if (managedItem == nullptr)
James Feistb6baeaa2019-02-21 10:41:40 -0800667 {
668 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
669 messages::invalidObject(response->res, it.key());
670 return CreatePIDRet::fail;
671 }
672 }
673
James Feist73df0db2019-03-25 15:29:35 -0700674 if (profile.size() &&
675 (type == "PidControllers" || type == "FanControllers" ||
676 type == "StepwiseControllers"))
677 {
678 if (managedItem == nullptr)
679 {
680 output["Profiles"] = std::vector<std::string>{profile};
681 }
682 else
683 {
684 std::string interface;
685 if (type == "StepwiseControllers")
686 {
687 interface = stepwiseConfigurationIface;
688 }
689 else
690 {
691 interface = pidConfigurationIface;
692 }
693 auto findConfig = managedItem->second.find(interface);
694 if (findConfig == managedItem->second.end())
695 {
696 BMCWEB_LOG_ERROR
697 << "Failed to find interface in managed object";
698 messages::internalError(response->res);
699 return CreatePIDRet::fail;
700 }
701 auto findProfiles = findConfig->second.find("Profiles");
702 if (findProfiles != findConfig->second.end())
703 {
704 const std::vector<std::string>* curProfiles =
705 std::get_if<std::vector<std::string>>(
706 &(findProfiles->second));
707 if (curProfiles == nullptr)
708 {
709 BMCWEB_LOG_ERROR << "Illegal profiles in managed object";
710 messages::internalError(response->res);
711 return CreatePIDRet::fail;
712 }
713 if (std::find(curProfiles->begin(), curProfiles->end(),
714 profile) == curProfiles->end())
715 {
716 std::vector<std::string> newProfiles = *curProfiles;
717 newProfiles.push_back(profile);
718 output["Profiles"] = newProfiles;
719 }
720 }
721 }
722 }
723
James Feist83ff9ab2018-08-31 10:18:24 -0700724 if (type == "PidControllers" || type == "FanControllers")
725 {
726 if (createNewObject)
727 {
728 output["Class"] = type == "PidControllers" ? std::string("temp")
729 : std::string("fan");
730 output["Type"] = std::string("Pid");
731 }
James Feist5f2caae2018-12-12 14:08:25 -0800732
733 std::optional<std::vector<nlohmann::json>> zones;
734 std::optional<std::vector<std::string>> inputs;
735 std::optional<std::vector<std::string>> outputs;
736 std::map<std::string, std::optional<double>> doubles;
James Feistb943aae2019-07-11 16:33:56 -0700737 std::optional<std::string> setpointOffset;
James Feist5f2caae2018-12-12 14:08:25 -0800738 if (!redfish::json_util::readJson(
James Feistb6baeaa2019-02-21 10:41:40 -0800739 it.value(), response->res, "Inputs", inputs, "Outputs", outputs,
James Feist5f2caae2018-12-12 14:08:25 -0800740 "Zones", zones, "FFGainCoefficient",
741 doubles["FFGainCoefficient"], "FFOffCoefficient",
742 doubles["FFOffCoefficient"], "ICoefficient",
743 doubles["ICoefficient"], "ILimitMax", doubles["ILimitMax"],
744 "ILimitMin", doubles["ILimitMin"], "OutLimitMax",
745 doubles["OutLimitMax"], "OutLimitMin", doubles["OutLimitMin"],
746 "PCoefficient", doubles["PCoefficient"], "SetPoint",
James Feistb943aae2019-07-11 16:33:56 -0700747 doubles["SetPoint"], "SetPointOffset", setpointOffset,
748 "SlewNeg", doubles["SlewNeg"], "SlewPos", doubles["SlewPos"],
749 "PositiveHysteresis", doubles["PositiveHysteresis"],
750 "NegativeHysteresis", doubles["NegativeHysteresis"]))
James Feist83ff9ab2018-08-31 10:18:24 -0700751 {
James Feist5f2caae2018-12-12 14:08:25 -0800752 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
James Feistb6baeaa2019-02-21 10:41:40 -0800753 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800754 return CreatePIDRet::fail;
James Feist83ff9ab2018-08-31 10:18:24 -0700755 }
James Feist5f2caae2018-12-12 14:08:25 -0800756 if (zones)
James Feist83ff9ab2018-08-31 10:18:24 -0700757 {
James Feist5f2caae2018-12-12 14:08:25 -0800758 std::vector<std::string> zonesStr;
759 if (!getZonesFromJsonReq(response, *zones, zonesStr))
James Feist83ff9ab2018-08-31 10:18:24 -0700760 {
James Feist5f2caae2018-12-12 14:08:25 -0800761 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Zones";
762 return CreatePIDRet::fail;
James Feist83ff9ab2018-08-31 10:18:24 -0700763 }
James Feistb6baeaa2019-02-21 10:41:40 -0800764 if (chassis.empty() &&
765 !findChassis(managedObj, zonesStr[0], chassis))
766 {
767 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
768 messages::invalidObject(response->res, it.key());
769 return CreatePIDRet::fail;
770 }
771
James Feist5f2caae2018-12-12 14:08:25 -0800772 output["Zones"] = std::move(zonesStr);
773 }
774 if (inputs || outputs)
775 {
776 std::array<std::optional<std::vector<std::string>>*, 2> containers =
777 {&inputs, &outputs};
778 size_t index = 0;
779 for (const auto& containerPtr : containers)
James Feist83ff9ab2018-08-31 10:18:24 -0700780 {
James Feist5f2caae2018-12-12 14:08:25 -0800781 std::optional<std::vector<std::string>>& container =
782 *containerPtr;
783 if (!container)
James Feist83ff9ab2018-08-31 10:18:24 -0700784 {
James Feist5f2caae2018-12-12 14:08:25 -0800785 index++;
786 continue;
James Feist83ff9ab2018-08-31 10:18:24 -0700787 }
James Feist5f2caae2018-12-12 14:08:25 -0800788
789 for (std::string& value : *container)
James Feist83ff9ab2018-08-31 10:18:24 -0700790 {
James Feist5f2caae2018-12-12 14:08:25 -0800791 boost::replace_all(value, "_", " ");
James Feist83ff9ab2018-08-31 10:18:24 -0700792 }
James Feist5f2caae2018-12-12 14:08:25 -0800793 std::string key;
794 if (index == 0)
James Feist83ff9ab2018-08-31 10:18:24 -0700795 {
James Feist5f2caae2018-12-12 14:08:25 -0800796 key = "Inputs";
James Feist83ff9ab2018-08-31 10:18:24 -0700797 }
James Feist5f2caae2018-12-12 14:08:25 -0800798 else
799 {
800 key = "Outputs";
801 }
802 output[key] = *container;
803 index++;
James Feist83ff9ab2018-08-31 10:18:24 -0700804 }
James Feist5f2caae2018-12-12 14:08:25 -0800805 }
James Feist83ff9ab2018-08-31 10:18:24 -0700806
James Feistb943aae2019-07-11 16:33:56 -0700807 if (setpointOffset)
808 {
809 // translate between redfish and dbus names
810 if (*setpointOffset == "UpperThresholdNonCritical")
811 {
812 output["SetPointOffset"] = std::string("WarningLow");
813 }
814 else if (*setpointOffset == "LowerThresholdNonCritical")
815 {
816 output["SetPointOffset"] = std::string("WarningHigh");
817 }
818 else if (*setpointOffset == "LowerThresholdCritical")
819 {
820 output["SetPointOffset"] = std::string("CriticalLow");
821 }
822 else if (*setpointOffset == "UpperThresholdCritical")
823 {
824 output["SetPointOffset"] = std::string("CriticalHigh");
825 }
826 else
827 {
828 BMCWEB_LOG_ERROR << "Invalid setpointoffset "
829 << *setpointOffset;
830 messages::invalidObject(response->res, it.key());
831 return CreatePIDRet::fail;
832 }
833 }
834
James Feist5f2caae2018-12-12 14:08:25 -0800835 // doubles
836 for (const auto& pairs : doubles)
837 {
838 if (!pairs.second)
James Feist83ff9ab2018-08-31 10:18:24 -0700839 {
James Feist5f2caae2018-12-12 14:08:25 -0800840 continue;
James Feist83ff9ab2018-08-31 10:18:24 -0700841 }
James Feist5f2caae2018-12-12 14:08:25 -0800842 BMCWEB_LOG_DEBUG << pairs.first << " = " << *pairs.second;
843 output[pairs.first] = *(pairs.second);
James Feist83ff9ab2018-08-31 10:18:24 -0700844 }
845 }
James Feist5f2caae2018-12-12 14:08:25 -0800846
James Feist83ff9ab2018-08-31 10:18:24 -0700847 else if (type == "FanZones")
848 {
James Feist83ff9ab2018-08-31 10:18:24 -0700849 output["Type"] = std::string("Pid.Zone");
850
James Feist5f2caae2018-12-12 14:08:25 -0800851 std::optional<nlohmann::json> chassisContainer;
852 std::optional<double> failSafePercent;
James Feistd3ec07f2019-02-25 14:51:15 -0800853 std::optional<double> minThermalOutput;
James Feistb6baeaa2019-02-21 10:41:40 -0800854 if (!redfish::json_util::readJson(it.value(), response->res, "Chassis",
James Feist5f2caae2018-12-12 14:08:25 -0800855 chassisContainer, "FailSafePercent",
James Feistd3ec07f2019-02-25 14:51:15 -0800856 failSafePercent, "MinThermalOutput",
857 minThermalOutput))
James Feist83ff9ab2018-08-31 10:18:24 -0700858 {
James Feist5f2caae2018-12-12 14:08:25 -0800859 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
James Feistb6baeaa2019-02-21 10:41:40 -0800860 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800861 return CreatePIDRet::fail;
862 }
James Feist83ff9ab2018-08-31 10:18:24 -0700863
James Feist5f2caae2018-12-12 14:08:25 -0800864 if (chassisContainer)
865 {
866
867 std::string chassisId;
868 if (!redfish::json_util::readJson(*chassisContainer, response->res,
869 "@odata.id", chassisId))
James Feist83ff9ab2018-08-31 10:18:24 -0700870 {
James Feist5f2caae2018-12-12 14:08:25 -0800871 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
872 << chassisContainer->dump();
James Feist83ff9ab2018-08-31 10:18:24 -0700873 return CreatePIDRet::fail;
874 }
James Feist5f2caae2018-12-12 14:08:25 -0800875
AppaRao Puli717794d2019-10-18 22:54:53 +0530876 // /redfish/v1/chassis/chassis_name/
James Feist5f2caae2018-12-12 14:08:25 -0800877 if (!dbus::utility::getNthStringFromPath(chassisId, 3, chassis))
878 {
879 BMCWEB_LOG_ERROR << "Got invalid path " << chassisId;
880 messages::invalidObject(response->res, chassisId);
881 return CreatePIDRet::fail;
882 }
883 }
James Feistd3ec07f2019-02-25 14:51:15 -0800884 if (minThermalOutput)
James Feist5f2caae2018-12-12 14:08:25 -0800885 {
James Feistd3ec07f2019-02-25 14:51:15 -0800886 output["MinThermalOutput"] = *minThermalOutput;
James Feist5f2caae2018-12-12 14:08:25 -0800887 }
888 if (failSafePercent)
889 {
890 output["FailSafePercent"] = *failSafePercent;
891 }
892 }
893 else if (type == "StepwiseControllers")
894 {
895 output["Type"] = std::string("Stepwise");
896
897 std::optional<std::vector<nlohmann::json>> zones;
898 std::optional<std::vector<nlohmann::json>> steps;
899 std::optional<std::vector<std::string>> inputs;
900 std::optional<double> positiveHysteresis;
901 std::optional<double> negativeHysteresis;
James Feistc33a90e2019-03-01 10:17:44 -0800902 std::optional<std::string> direction; // upper clipping curve vs lower
James Feist5f2caae2018-12-12 14:08:25 -0800903 if (!redfish::json_util::readJson(
James Feistb6baeaa2019-02-21 10:41:40 -0800904 it.value(), response->res, "Zones", zones, "Steps", steps,
905 "Inputs", inputs, "PositiveHysteresis", positiveHysteresis,
James Feistc33a90e2019-03-01 10:17:44 -0800906 "NegativeHysteresis", negativeHysteresis, "Direction",
907 direction))
James Feist5f2caae2018-12-12 14:08:25 -0800908 {
909 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
James Feistb6baeaa2019-02-21 10:41:40 -0800910 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800911 return CreatePIDRet::fail;
912 }
913
914 if (zones)
915 {
James Feistb6baeaa2019-02-21 10:41:40 -0800916 std::vector<std::string> zonesStrs;
917 if (!getZonesFromJsonReq(response, *zones, zonesStrs))
James Feist5f2caae2018-12-12 14:08:25 -0800918 {
919 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Zones";
920 return CreatePIDRet::fail;
921 }
James Feistb6baeaa2019-02-21 10:41:40 -0800922 if (chassis.empty() &&
923 !findChassis(managedObj, zonesStrs[0], chassis))
924 {
925 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
926 messages::invalidObject(response->res, it.key());
927 return CreatePIDRet::fail;
928 }
929 output["Zones"] = std::move(zonesStrs);
James Feist5f2caae2018-12-12 14:08:25 -0800930 }
931 if (steps)
932 {
933 std::vector<double> readings;
934 std::vector<double> outputs;
935 for (auto& step : *steps)
936 {
937 double target;
Ed Tanousb01bf292019-03-25 19:25:26 +0000938 double output;
James Feist5f2caae2018-12-12 14:08:25 -0800939
940 if (!redfish::json_util::readJson(step, response->res, "Target",
Ed Tanousb01bf292019-03-25 19:25:26 +0000941 target, "Output", output))
James Feist5f2caae2018-12-12 14:08:25 -0800942 {
943 BMCWEB_LOG_ERROR << "Line:" << __LINE__
James Feistb6baeaa2019-02-21 10:41:40 -0800944 << ", Illegal Property "
945 << it.value().dump();
James Feist5f2caae2018-12-12 14:08:25 -0800946 return CreatePIDRet::fail;
947 }
948 readings.emplace_back(target);
Ed Tanousb01bf292019-03-25 19:25:26 +0000949 outputs.emplace_back(output);
James Feist5f2caae2018-12-12 14:08:25 -0800950 }
951 output["Reading"] = std::move(readings);
952 output["Output"] = std::move(outputs);
953 }
954 if (inputs)
955 {
956 for (std::string& value : *inputs)
957 {
James Feist5f2caae2018-12-12 14:08:25 -0800958 boost::replace_all(value, "_", " ");
959 }
960 output["Inputs"] = std::move(*inputs);
961 }
962 if (negativeHysteresis)
963 {
964 output["NegativeHysteresis"] = *negativeHysteresis;
965 }
966 if (positiveHysteresis)
967 {
968 output["PositiveHysteresis"] = *positiveHysteresis;
James Feist83ff9ab2018-08-31 10:18:24 -0700969 }
James Feistc33a90e2019-03-01 10:17:44 -0800970 if (direction)
971 {
972 constexpr const std::array<const char*, 2> allowedDirections = {
973 "Ceiling", "Floor"};
974 if (std::find(allowedDirections.begin(), allowedDirections.end(),
975 *direction) == allowedDirections.end())
976 {
977 messages::propertyValueTypeError(response->res, "Direction",
978 *direction);
979 return CreatePIDRet::fail;
980 }
981 output["Class"] = *direction;
982 }
James Feist83ff9ab2018-08-31 10:18:24 -0700983 }
984 else
985 {
James Feist5f2caae2018-12-12 14:08:25 -0800986 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Type " << type;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700987 messages::propertyUnknown(response->res, type);
James Feist83ff9ab2018-08-31 10:18:24 -0700988 return CreatePIDRet::fail;
989 }
990 return CreatePIDRet::patch;
991}
James Feist73df0db2019-03-25 15:29:35 -0700992struct GetPIDValues : std::enable_shared_from_this<GetPIDValues>
993{
994
995 GetPIDValues(const std::shared_ptr<AsyncResp>& asyncResp) :
996 asyncResp(asyncResp)
997
998 {
999 }
1000
1001 void run()
1002 {
1003 std::shared_ptr<GetPIDValues> self = shared_from_this();
1004
1005 // get all configurations
1006 crow::connections::systemBus->async_method_call(
1007 [self](const boost::system::error_code ec,
1008 const crow::openbmc_mapper::GetSubTreeType& subtree) {
1009 if (ec)
1010 {
1011 BMCWEB_LOG_ERROR << ec;
1012 messages::internalError(self->asyncResp->res);
1013 return;
1014 }
1015 self->subtree = subtree;
1016 },
1017 "xyz.openbmc_project.ObjectMapper",
1018 "/xyz/openbmc_project/object_mapper",
1019 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
1020 std::array<const char*, 4>{
1021 pidConfigurationIface, pidZoneConfigurationIface,
1022 objectManagerIface, stepwiseConfigurationIface});
1023
1024 // at the same time get the selected profile
1025 crow::connections::systemBus->async_method_call(
1026 [self](const boost::system::error_code ec,
1027 const crow::openbmc_mapper::GetSubTreeType& subtree) {
1028 if (ec || subtree.empty())
1029 {
1030 return;
1031 }
1032 if (subtree[0].second.size() != 1)
1033 {
1034 // invalid mapper response, should never happen
1035 BMCWEB_LOG_ERROR << "GetPIDValues: Mapper Error";
1036 messages::internalError(self->asyncResp->res);
1037 return;
1038 }
1039
1040 const std::string& path = subtree[0].first;
1041 const std::string& owner = subtree[0].second[0].first;
1042 crow::connections::systemBus->async_method_call(
1043 [path, owner, self](
1044 const boost::system::error_code ec,
1045 const boost::container::flat_map<
1046 std::string, std::variant<std::vector<std::string>,
1047 std::string>>& resp) {
1048 if (ec)
1049 {
1050 BMCWEB_LOG_ERROR << "GetPIDValues: Can't get "
1051 "thermalModeIface "
1052 << path;
1053 messages::internalError(self->asyncResp->res);
1054 return;
1055 }
Ed Tanous271584a2019-07-09 16:24:22 -07001056 const std::string* current = nullptr;
1057 const std::vector<std::string>* supported = nullptr;
James Feist73df0db2019-03-25 15:29:35 -07001058 for (auto& [key, value] : resp)
1059 {
1060 if (key == "Current")
1061 {
1062 current = std::get_if<std::string>(&value);
1063 if (current == nullptr)
1064 {
1065 BMCWEB_LOG_ERROR
1066 << "GetPIDValues: thermal mode "
1067 "iface invalid "
1068 << path;
1069 messages::internalError(
1070 self->asyncResp->res);
1071 return;
1072 }
1073 }
1074 if (key == "Supported")
1075 {
1076 supported =
1077 std::get_if<std::vector<std::string>>(
1078 &value);
1079 if (supported == nullptr)
1080 {
1081 BMCWEB_LOG_ERROR
1082 << "GetPIDValues: thermal mode "
1083 "iface invalid"
1084 << path;
1085 messages::internalError(
1086 self->asyncResp->res);
1087 return;
1088 }
1089 }
1090 }
1091 if (current == nullptr || supported == nullptr)
1092 {
1093 BMCWEB_LOG_ERROR << "GetPIDValues: thermal mode "
1094 "iface invalid "
1095 << path;
1096 messages::internalError(self->asyncResp->res);
1097 return;
1098 }
1099 self->currentProfile = *current;
1100 self->supportedProfiles = *supported;
1101 },
1102 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1103 thermalModeIface);
1104 },
1105 "xyz.openbmc_project.ObjectMapper",
1106 "/xyz/openbmc_project/object_mapper",
1107 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
1108 std::array<const char*, 1>{thermalModeIface});
1109 }
1110
1111 ~GetPIDValues()
1112 {
1113 if (asyncResp->res.result() != boost::beast::http::status::ok)
1114 {
1115 return;
1116 }
1117 // create map of <connection, path to objMgr>>
1118 boost::container::flat_map<std::string, std::string> objectMgrPaths;
1119 boost::container::flat_set<std::string> calledConnections;
1120 for (const auto& pathGroup : subtree)
1121 {
1122 for (const auto& connectionGroup : pathGroup.second)
1123 {
1124 auto findConnection =
1125 calledConnections.find(connectionGroup.first);
1126 if (findConnection != calledConnections.end())
1127 {
1128 break;
1129 }
1130 for (const std::string& interface : connectionGroup.second)
1131 {
1132 if (interface == objectManagerIface)
1133 {
1134 objectMgrPaths[connectionGroup.first] = pathGroup.first;
1135 }
1136 // this list is alphabetical, so we
1137 // should have found the objMgr by now
1138 if (interface == pidConfigurationIface ||
1139 interface == pidZoneConfigurationIface ||
1140 interface == stepwiseConfigurationIface)
1141 {
1142 auto findObjMgr =
1143 objectMgrPaths.find(connectionGroup.first);
1144 if (findObjMgr == objectMgrPaths.end())
1145 {
1146 BMCWEB_LOG_DEBUG << connectionGroup.first
1147 << "Has no Object Manager";
1148 continue;
1149 }
1150
1151 calledConnections.insert(connectionGroup.first);
1152
1153 asyncPopulatePid(findObjMgr->first, findObjMgr->second,
1154 currentProfile, supportedProfiles,
1155 asyncResp);
1156 break;
1157 }
1158 }
1159 }
1160 }
1161 }
1162
1163 std::vector<std::string> supportedProfiles;
1164 std::string currentProfile;
1165 crow::openbmc_mapper::GetSubTreeType subtree;
1166 std::shared_ptr<AsyncResp> asyncResp;
1167};
1168
1169struct SetPIDValues : std::enable_shared_from_this<SetPIDValues>
1170{
1171
Ed Tanous271584a2019-07-09 16:24:22 -07001172 SetPIDValues(const std::shared_ptr<AsyncResp>& asyncRespIn,
James Feist73df0db2019-03-25 15:29:35 -07001173 nlohmann::json& data) :
Ed Tanous271584a2019-07-09 16:24:22 -07001174 asyncResp(asyncRespIn)
James Feist73df0db2019-03-25 15:29:35 -07001175 {
1176
1177 std::optional<nlohmann::json> pidControllers;
1178 std::optional<nlohmann::json> fanControllers;
1179 std::optional<nlohmann::json> fanZones;
1180 std::optional<nlohmann::json> stepwiseControllers;
1181
1182 if (!redfish::json_util::readJson(
1183 data, asyncResp->res, "PidControllers", pidControllers,
1184 "FanControllers", fanControllers, "FanZones", fanZones,
1185 "StepwiseControllers", stepwiseControllers, "Profile", profile))
1186 {
1187 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
1188 << data.dump();
1189 return;
1190 }
1191 configuration.emplace_back("PidControllers", std::move(pidControllers));
1192 configuration.emplace_back("FanControllers", std::move(fanControllers));
1193 configuration.emplace_back("FanZones", std::move(fanZones));
1194 configuration.emplace_back("StepwiseControllers",
1195 std::move(stepwiseControllers));
1196 }
1197 void run()
1198 {
1199 if (asyncResp->res.result() != boost::beast::http::status::ok)
1200 {
1201 return;
1202 }
1203
1204 std::shared_ptr<SetPIDValues> self = shared_from_this();
1205
1206 // todo(james): might make sense to do a mapper call here if this
1207 // interface gets more traction
1208 crow::connections::systemBus->async_method_call(
1209 [self](const boost::system::error_code ec,
Ed Tanous271584a2019-07-09 16:24:22 -07001210 dbus::utility::ManagedObjectType& mObj) {
James Feist73df0db2019-03-25 15:29:35 -07001211 if (ec)
1212 {
1213 BMCWEB_LOG_ERROR << "Error communicating to Entity Manager";
1214 messages::internalError(self->asyncResp->res);
1215 return;
1216 }
James Feiste69d9de2020-02-07 12:23:27 -08001217 const std::array<const char*, 3> configurations = {
1218 pidConfigurationIface, pidZoneConfigurationIface,
1219 stepwiseConfigurationIface};
1220
James Feist14b0b8d2020-02-12 11:52:07 -08001221 for (const auto& [path, object] : mObj)
James Feiste69d9de2020-02-07 12:23:27 -08001222 {
James Feist14b0b8d2020-02-12 11:52:07 -08001223 for (const auto& [interface, _] : object)
James Feiste69d9de2020-02-07 12:23:27 -08001224 {
1225 if (std::find(configurations.begin(),
1226 configurations.end(),
1227 interface) != configurations.end())
1228 {
James Feist14b0b8d2020-02-12 11:52:07 -08001229 self->objectCount++;
James Feiste69d9de2020-02-07 12:23:27 -08001230 break;
1231 }
1232 }
James Feiste69d9de2020-02-07 12:23:27 -08001233 }
Ed Tanous271584a2019-07-09 16:24:22 -07001234 self->managedObj = std::move(mObj);
James Feist73df0db2019-03-25 15:29:35 -07001235 },
1236 "xyz.openbmc_project.EntityManager", "/", objectManagerIface,
1237 "GetManagedObjects");
1238
1239 // at the same time get the profile information
1240 crow::connections::systemBus->async_method_call(
1241 [self](const boost::system::error_code ec,
1242 const crow::openbmc_mapper::GetSubTreeType& subtree) {
1243 if (ec || subtree.empty())
1244 {
1245 return;
1246 }
1247 if (subtree[0].second.empty())
1248 {
1249 // invalid mapper response, should never happen
1250 BMCWEB_LOG_ERROR << "SetPIDValues: Mapper Error";
1251 messages::internalError(self->asyncResp->res);
1252 return;
1253 }
1254
1255 const std::string& path = subtree[0].first;
1256 const std::string& owner = subtree[0].second[0].first;
1257 crow::connections::systemBus->async_method_call(
1258 [self, path, owner](
1259 const boost::system::error_code ec,
1260 const boost::container::flat_map<
1261 std::string, std::variant<std::vector<std::string>,
Ed Tanous271584a2019-07-09 16:24:22 -07001262 std::string>>& r) {
James Feist73df0db2019-03-25 15:29:35 -07001263 if (ec)
1264 {
1265 BMCWEB_LOG_ERROR << "SetPIDValues: Can't get "
1266 "thermalModeIface "
1267 << path;
1268 messages::internalError(self->asyncResp->res);
1269 return;
1270 }
Ed Tanous271584a2019-07-09 16:24:22 -07001271 const std::string* current = nullptr;
1272 const std::vector<std::string>* supported = nullptr;
1273 for (auto& [key, value] : r)
James Feist73df0db2019-03-25 15:29:35 -07001274 {
1275 if (key == "Current")
1276 {
1277 current = std::get_if<std::string>(&value);
1278 if (current == nullptr)
1279 {
1280 BMCWEB_LOG_ERROR
1281 << "SetPIDValues: thermal mode "
1282 "iface invalid "
1283 << path;
1284 messages::internalError(
1285 self->asyncResp->res);
1286 return;
1287 }
1288 }
1289 if (key == "Supported")
1290 {
1291 supported =
1292 std::get_if<std::vector<std::string>>(
1293 &value);
1294 if (supported == nullptr)
1295 {
1296 BMCWEB_LOG_ERROR
1297 << "SetPIDValues: thermal mode "
1298 "iface invalid"
1299 << path;
1300 messages::internalError(
1301 self->asyncResp->res);
1302 return;
1303 }
1304 }
1305 }
1306 if (current == nullptr || supported == nullptr)
1307 {
1308 BMCWEB_LOG_ERROR << "SetPIDValues: thermal mode "
1309 "iface invalid "
1310 << path;
1311 messages::internalError(self->asyncResp->res);
1312 return;
1313 }
1314 self->currentProfile = *current;
1315 self->supportedProfiles = *supported;
1316 self->profileConnection = owner;
1317 self->profilePath = path;
1318 },
1319 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1320 thermalModeIface);
1321 },
1322 "xyz.openbmc_project.ObjectMapper",
1323 "/xyz/openbmc_project/object_mapper",
1324 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
1325 std::array<const char*, 1>{thermalModeIface});
1326 }
1327 ~SetPIDValues()
1328 {
1329 if (asyncResp->res.result() != boost::beast::http::status::ok)
1330 {
1331 return;
1332 }
1333
1334 std::shared_ptr<AsyncResp> response = asyncResp;
1335
1336 if (profile)
1337 {
1338 if (std::find(supportedProfiles.begin(), supportedProfiles.end(),
1339 *profile) == supportedProfiles.end())
1340 {
1341 messages::actionParameterUnknown(response->res, "Profile",
1342 *profile);
1343 return;
1344 }
1345 currentProfile = *profile;
1346 crow::connections::systemBus->async_method_call(
1347 [response](const boost::system::error_code ec) {
1348 if (ec)
1349 {
1350 BMCWEB_LOG_ERROR << "Error patching profile" << ec;
1351 messages::internalError(response->res);
1352 }
1353 },
1354 profileConnection, profilePath,
1355 "org.freedesktop.DBus.Properties", "Set", thermalModeIface,
1356 "Current", std::variant<std::string>(*profile));
1357 }
1358
1359 for (auto& containerPair : configuration)
1360 {
1361 auto& container = containerPair.second;
1362 if (!container)
1363 {
1364 continue;
1365 }
James Feist6ee7f772020-02-06 16:25:27 -08001366 BMCWEB_LOG_DEBUG << *container;
1367
James Feist73df0db2019-03-25 15:29:35 -07001368 std::string& type = containerPair.first;
1369
1370 for (nlohmann::json::iterator it = container->begin();
1371 it != container->end(); it++)
1372 {
1373 const auto& name = it.key();
James Feist6ee7f772020-02-06 16:25:27 -08001374 BMCWEB_LOG_DEBUG << "looking for " << name;
1375
James Feist73df0db2019-03-25 15:29:35 -07001376 auto pathItr =
1377 std::find_if(managedObj.begin(), managedObj.end(),
1378 [&name](const auto& obj) {
1379 return boost::algorithm::ends_with(
1380 obj.first.str, "/" + name);
1381 });
1382 boost::container::flat_map<std::string,
1383 dbus::utility::DbusVariantType>
1384 output;
1385
1386 output.reserve(16); // The pid interface length
1387
1388 // determines if we're patching entity-manager or
1389 // creating a new object
1390 bool createNewObject = (pathItr == managedObj.end());
James Feist6ee7f772020-02-06 16:25:27 -08001391 BMCWEB_LOG_DEBUG << "Found = " << !createNewObject;
1392
James Feist73df0db2019-03-25 15:29:35 -07001393 std::string iface;
1394 if (type == "PidControllers" || type == "FanControllers")
1395 {
1396 iface = pidConfigurationIface;
1397 if (!createNewObject &&
1398 pathItr->second.find(pidConfigurationIface) ==
1399 pathItr->second.end())
1400 {
1401 createNewObject = true;
1402 }
1403 }
1404 else if (type == "FanZones")
1405 {
1406 iface = pidZoneConfigurationIface;
1407 if (!createNewObject &&
1408 pathItr->second.find(pidZoneConfigurationIface) ==
1409 pathItr->second.end())
1410 {
1411
1412 createNewObject = true;
1413 }
1414 }
1415 else if (type == "StepwiseControllers")
1416 {
1417 iface = stepwiseConfigurationIface;
1418 if (!createNewObject &&
1419 pathItr->second.find(stepwiseConfigurationIface) ==
1420 pathItr->second.end())
1421 {
1422 createNewObject = true;
1423 }
1424 }
James Feist6ee7f772020-02-06 16:25:27 -08001425
1426 if (createNewObject && it.value() == nullptr)
1427 {
1428 // can't delete a non-existant object
1429 messages::invalidObject(response->res, name);
1430 continue;
1431 }
1432
1433 std::string path;
1434 if (pathItr != managedObj.end())
1435 {
1436 path = pathItr->first.str;
1437 }
1438
James Feist73df0db2019-03-25 15:29:35 -07001439 BMCWEB_LOG_DEBUG << "Create new = " << createNewObject << "\n";
James Feiste69d9de2020-02-07 12:23:27 -08001440
1441 // arbitrary limit to avoid attacks
1442 constexpr const size_t controllerLimit = 500;
James Feist14b0b8d2020-02-12 11:52:07 -08001443 if (createNewObject && objectCount >= controllerLimit)
James Feiste69d9de2020-02-07 12:23:27 -08001444 {
1445 messages::resourceExhaustion(response->res, type);
1446 continue;
1447 }
1448
James Feist73df0db2019-03-25 15:29:35 -07001449 output["Name"] = boost::replace_all_copy(name, "_", " ");
1450
1451 std::string chassis;
1452 CreatePIDRet ret = createPidInterface(
James Feist6ee7f772020-02-06 16:25:27 -08001453 response, type, it, path, managedObj, createNewObject,
1454 output, chassis, currentProfile);
James Feist73df0db2019-03-25 15:29:35 -07001455 if (ret == CreatePIDRet::fail)
1456 {
1457 return;
1458 }
1459 else if (ret == CreatePIDRet::del)
1460 {
1461 continue;
1462 }
1463
1464 if (!createNewObject)
1465 {
1466 for (const auto& property : output)
1467 {
1468 crow::connections::systemBus->async_method_call(
1469 [response,
1470 propertyName{std::string(property.first)}](
1471 const boost::system::error_code ec) {
1472 if (ec)
1473 {
1474 BMCWEB_LOG_ERROR << "Error patching "
1475 << propertyName << ": "
1476 << ec;
1477 messages::internalError(response->res);
1478 return;
1479 }
1480 messages::success(response->res);
1481 },
James Feist6ee7f772020-02-06 16:25:27 -08001482 "xyz.openbmc_project.EntityManager", path,
James Feist73df0db2019-03-25 15:29:35 -07001483 "org.freedesktop.DBus.Properties", "Set", iface,
1484 property.first, property.second);
1485 }
1486 }
1487 else
1488 {
1489 if (chassis.empty())
1490 {
1491 BMCWEB_LOG_ERROR << "Failed to get chassis from config";
1492 messages::invalidObject(response->res, name);
1493 return;
1494 }
1495
1496 bool foundChassis = false;
1497 for (const auto& obj : managedObj)
1498 {
1499 if (boost::algorithm::ends_with(obj.first.str, chassis))
1500 {
1501 chassis = obj.first.str;
1502 foundChassis = true;
1503 break;
1504 }
1505 }
1506 if (!foundChassis)
1507 {
1508 BMCWEB_LOG_ERROR << "Failed to find chassis on dbus";
1509 messages::resourceMissingAtURI(
1510 response->res, "/redfish/v1/Chassis/" + chassis);
1511 return;
1512 }
1513
1514 crow::connections::systemBus->async_method_call(
1515 [response](const boost::system::error_code ec) {
1516 if (ec)
1517 {
1518 BMCWEB_LOG_ERROR << "Error Adding Pid Object "
1519 << ec;
1520 messages::internalError(response->res);
1521 return;
1522 }
1523 messages::success(response->res);
1524 },
1525 "xyz.openbmc_project.EntityManager", chassis,
1526 "xyz.openbmc_project.AddObject", "AddObject", output);
1527 }
1528 }
1529 }
1530 }
1531 std::shared_ptr<AsyncResp> asyncResp;
1532 std::vector<std::pair<std::string, std::optional<nlohmann::json>>>
1533 configuration;
1534 std::optional<std::string> profile;
1535 dbus::utility::ManagedObjectType managedObj;
1536 std::vector<std::string> supportedProfiles;
1537 std::string currentProfile;
1538 std::string profileConnection;
1539 std::string profilePath;
James Feist14b0b8d2020-02-12 11:52:07 -08001540 size_t objectCount = 0;
James Feist73df0db2019-03-25 15:29:35 -07001541};
James Feist83ff9ab2018-08-31 10:18:24 -07001542
Ed Tanous1abe55e2018-09-05 08:30:59 -07001543class Manager : public Node
1544{
1545 public:
James Feist5b4aa862018-08-16 14:07:01 -07001546 Manager(CrowApp& app) : Node(app, "/redfish/v1/Managers/bmc/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001547 {
Ed Tanous0f74e642018-11-12 15:17:05 -08001548 uuid = app.template getMiddleware<crow::persistent_data::Middleware>()
1549 .systemUuid;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001550 entityPrivileges = {
1551 {boost::beast::http::verb::get, {{"Login"}}},
1552 {boost::beast::http::verb::head, {{"Login"}}},
1553 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1554 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1555 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1556 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001557 }
1558
Ed Tanous1abe55e2018-09-05 08:30:59 -07001559 private:
James Feist5b4aa862018-08-16 14:07:01 -07001560 void doGet(crow::Response& res, const crow::Request& req,
1561 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -07001562 {
Ed Tanous0f74e642018-11-12 15:17:05 -08001563 res.jsonValue["@odata.id"] = "/redfish/v1/Managers/bmc";
1564 res.jsonValue["@odata.type"] = "#Manager.v1_3_0.Manager";
Ed Tanous0f74e642018-11-12 15:17:05 -08001565 res.jsonValue["Id"] = "bmc";
1566 res.jsonValue["Name"] = "OpenBmc Manager";
1567 res.jsonValue["Description"] = "Baseboard Management Controller";
1568 res.jsonValue["PowerState"] = "On";
Ed Tanous029573d2019-02-01 10:57:49 -08001569 res.jsonValue["Status"] = {{"State", "Enabled"}, {"Health", "OK"}};
Ed Tanous0f74e642018-11-12 15:17:05 -08001570 res.jsonValue["ManagerType"] = "BMC";
Ed Tanous3602e232019-05-13 11:11:44 -07001571 res.jsonValue["UUID"] = systemd_utils::getUuid();
1572 res.jsonValue["ServiceEntryPointUUID"] = uuid;
Ed Tanous75176582018-12-14 08:14:34 -08001573 res.jsonValue["Model"] = "OpenBmc"; // TODO(ed), get model
Ed Tanous0f74e642018-11-12 15:17:05 -08001574
1575 res.jsonValue["LogServices"] = {
1576 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices"}};
1577
1578 res.jsonValue["NetworkProtocol"] = {
1579 {"@odata.id", "/redfish/v1/Managers/bmc/NetworkProtocol"}};
1580
1581 res.jsonValue["EthernetInterfaces"] = {
1582 {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces"}};
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001583
1584#ifdef BMCWEB_ENABLE_VM_NBDPROXY
1585 res.jsonValue["VirtualMedia"] = {
1586 {"@odata.id", "/redfish/v1/Managers/bmc/VirtualMedia"}};
1587#endif // BMCWEB_ENABLE_VM_NBDPROXY
1588
Ed Tanous0f74e642018-11-12 15:17:05 -08001589 // default oem data
1590 nlohmann::json& oem = res.jsonValue["Oem"];
1591 nlohmann::json& oemOpenbmc = oem["OpenBmc"];
1592 oem["@odata.type"] = "#OemManager.Oem";
1593 oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem";
Ed Tanous0f74e642018-11-12 15:17:05 -08001594 oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc";
1595 oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc";
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001596 oemOpenbmc["Certificates"] = {
1597 {"@odata.id", "/redfish/v1/Managers/bmc/Truststore/Certificates"}};
Ed Tanous0f74e642018-11-12 15:17:05 -08001598
Gunnar Mills2a5c4402020-05-19 09:07:24 -05001599 // Manager.Reset (an action) can be many values, OpenBMC only supports
1600 // BMC reboot.
1601 nlohmann::json& managerReset =
Ed Tanous0f74e642018-11-12 15:17:05 -08001602 res.jsonValue["Actions"]["#Manager.Reset"];
Gunnar Mills2a5c4402020-05-19 09:07:24 -05001603 managerReset["target"] =
Jennifer Leeed5befb2018-08-10 11:29:45 -07001604 "/redfish/v1/Managers/bmc/Actions/Manager.Reset";
Gunnar Mills2a5c4402020-05-19 09:07:24 -05001605 managerReset["ResetType@Redfish.AllowableValues"] = {"GracefulRestart"};
Jennifer Leeca537922018-08-10 10:07:30 -07001606
Andrew Geisslercb92c032018-08-17 07:56:14 -07001607 res.jsonValue["DateTime"] = crow::utility::dateTimeNow();
Santosh Puranik474bfad2019-04-02 16:00:09 +05301608
Kuiying Wangf8c3e6f2019-08-22 13:35:56 +08001609 // Fill in SerialConsole info
Santosh Puranik474bfad2019-04-02 16:00:09 +05301610 res.jsonValue["SerialConsole"]["ServiceEnabled"] = true;
Kuiying Wangf8c3e6f2019-08-22 13:35:56 +08001611 res.jsonValue["SerialConsole"]["MaxConcurrentSessions"] = 15;
Santosh Puranik474bfad2019-04-02 16:00:09 +05301612 res.jsonValue["SerialConsole"]["ConnectTypesSupported"] = {"IPMI",
1613 "SSH"};
Santosh Puranikef47bb12019-04-30 10:28:52 +05301614#ifdef BMCWEB_ENABLE_KVM
Kuiying Wangf8c3e6f2019-08-22 13:35:56 +08001615 // Fill in GraphicalConsole info
Santosh Puranikef47bb12019-04-30 10:28:52 +05301616 res.jsonValue["GraphicalConsole"]["ServiceEnabled"] = true;
Jae Hyun Yoo704fae62019-10-02 13:01:27 -07001617 res.jsonValue["GraphicalConsole"]["MaxConcurrentSessions"] = 4;
Santosh Puranikef47bb12019-04-30 10:28:52 +05301618 res.jsonValue["GraphicalConsole"]["ConnectTypesSupported"] = {"KVMIP"};
1619#endif // BMCWEB_ENABLE_KVM
Santosh Puranik474bfad2019-04-02 16:00:09 +05301620
Gunnar Mills603a6642019-01-21 17:03:51 -06001621 res.jsonValue["Links"]["ManagerForServers@odata.count"] = 1;
1622 res.jsonValue["Links"]["ManagerForServers"] = {
1623 {{"@odata.id", "/redfish/v1/Systems/system"}}};
Shawn McCarney26f03892019-05-03 13:20:24 -05001624
Jennifer Leeed5befb2018-08-10 11:29:45 -07001625 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
James Feist5b4aa862018-08-16 14:07:01 -07001626
James Feistb49ac872019-05-21 15:12:01 -07001627 auto health = std::make_shared<HealthPopulate>(asyncResp);
1628 health->isManagersHealth = true;
1629 health->populate();
1630
Andrew Geisslere90c5052019-06-28 13:52:27 -05001631 fw_util::getActiveFwVersion(asyncResp, fw_util::bmcPurpose,
1632 "FirmwareVersion");
James Feist0f6b00b2019-06-10 14:15:53 -07001633
James Feist73df0db2019-03-25 15:29:35 -07001634 auto pids = std::make_shared<GetPIDValues>(asyncResp);
1635 pids->run();
Jennifer Leec5d03ff2019-03-08 15:42:58 -08001636
1637 getMainChassisId(asyncResp, [](const std::string& chassisId,
1638 const std::shared_ptr<AsyncResp> aRsp) {
1639 aRsp->res.jsonValue["Links"]["ManagerForChassis@odata.count"] = 1;
1640 aRsp->res.jsonValue["Links"]["ManagerForChassis"] = {
1641 {{"@odata.id", "/redfish/v1/Chassis/" + chassisId}}};
Jason M. Bills2c0feb02019-07-26 16:35:20 -07001642 aRsp->res.jsonValue["Links"]["ManagerInChassis"] = {
1643 {"@odata.id", "/redfish/v1/Chassis/" + chassisId}};
Jennifer Leec5d03ff2019-03-08 15:42:58 -08001644 });
James Feist0f6b00b2019-06-10 14:15:53 -07001645
1646 static bool started = false;
1647
1648 if (!started)
1649 {
1650 crow::connections::systemBus->async_method_call(
1651 [asyncResp](const boost::system::error_code ec,
1652 const std::variant<double>& resp) {
1653 if (ec)
1654 {
1655 BMCWEB_LOG_ERROR << "Error while getting progress";
1656 messages::internalError(asyncResp->res);
1657 return;
1658 }
1659 const double* val = std::get_if<double>(&resp);
1660 if (val == nullptr)
1661 {
1662 BMCWEB_LOG_ERROR
1663 << "Invalid response while getting progress";
1664 messages::internalError(asyncResp->res);
1665 return;
1666 }
1667 if (*val < 1.0)
1668 {
1669 asyncResp->res.jsonValue["Status"]["State"] =
1670 "Starting";
1671 started = true;
1672 }
1673 },
1674 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1675 "org.freedesktop.DBus.Properties", "Get",
1676 "org.freedesktop.systemd1.Manager", "Progress");
1677 }
James Feist83ff9ab2018-08-31 10:18:24 -07001678 }
James Feist5b4aa862018-08-16 14:07:01 -07001679
1680 void doPatch(crow::Response& res, const crow::Request& req,
1681 const std::vector<std::string>& params) override
1682 {
Ed Tanous0627a2c2018-11-29 17:09:23 -08001683 std::optional<nlohmann::json> oem;
Santosh Puranikaf5d60582019-03-20 18:16:36 +05301684 std::optional<std::string> datetime;
Santosh Puranik41352c22019-07-03 05:35:49 -05001685 std::shared_ptr<AsyncResp> response = std::make_shared<AsyncResp>(res);
Ed Tanous0627a2c2018-11-29 17:09:23 -08001686
Santosh Puranik41352c22019-07-03 05:35:49 -05001687 if (!json_util::readJson(req, response->res, "Oem", oem, "DateTime",
1688 datetime))
James Feist83ff9ab2018-08-31 10:18:24 -07001689 {
1690 return;
1691 }
Ed Tanous0627a2c2018-11-29 17:09:23 -08001692
Ed Tanous0627a2c2018-11-29 17:09:23 -08001693 if (oem)
James Feist83ff9ab2018-08-31 10:18:24 -07001694 {
Ed Tanous43b761d2019-02-13 20:10:56 -08001695 std::optional<nlohmann::json> openbmc;
1696 if (!redfish::json_util::readJson(*oem, res, "OpenBmc", openbmc))
James Feist83ff9ab2018-08-31 10:18:24 -07001697 {
Ed Tanous43b761d2019-02-13 20:10:56 -08001698 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
1699 << oem->dump();
1700 return;
1701 }
1702 if (openbmc)
1703 {
1704 std::optional<nlohmann::json> fan;
1705 if (!redfish::json_util::readJson(*openbmc, res, "Fan", fan))
James Feist83ff9ab2018-08-31 10:18:24 -07001706 {
James Feist5f2caae2018-12-12 14:08:25 -08001707 BMCWEB_LOG_ERROR << "Line:" << __LINE__
Ed Tanous43b761d2019-02-13 20:10:56 -08001708 << ", Illegal Property "
1709 << openbmc->dump();
James Feist5f2caae2018-12-12 14:08:25 -08001710 return;
1711 }
Ed Tanous43b761d2019-02-13 20:10:56 -08001712 if (fan)
James Feist5f2caae2018-12-12 14:08:25 -08001713 {
James Feist73df0db2019-03-25 15:29:35 -07001714 auto pid = std::make_shared<SetPIDValues>(response, *fan);
1715 pid->run();
James Feist83ff9ab2018-08-31 10:18:24 -07001716 }
James Feist83ff9ab2018-08-31 10:18:24 -07001717 }
1718 }
Santosh Puranikaf5d60582019-03-20 18:16:36 +05301719 if (datetime)
1720 {
1721 setDateTime(response, std::move(*datetime));
1722 }
1723 }
1724
1725 void setDateTime(std::shared_ptr<AsyncResp> aResp,
1726 std::string datetime) const
1727 {
1728 BMCWEB_LOG_DEBUG << "Set date time: " << datetime;
1729
1730 std::stringstream stream(datetime);
1731 // Convert from ISO 8601 to boost local_time
1732 // (BMC only has time in UTC)
1733 boost::posix_time::ptime posixTime;
1734 boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
1735 // Facet gets deleted with the stringsteam
1736 auto ifc = std::make_unique<boost::local_time::local_time_input_facet>(
1737 "%Y-%m-%d %H:%M:%S%F %ZP");
1738 stream.imbue(std::locale(stream.getloc(), ifc.release()));
1739
1740 boost::local_time::local_date_time ldt(
1741 boost::local_time::not_a_date_time);
1742
1743 if (stream >> ldt)
1744 {
1745 posixTime = ldt.utc_time();
1746 boost::posix_time::time_duration dur = posixTime - epoch;
1747 uint64_t durMicroSecs =
1748 static_cast<uint64_t>(dur.total_microseconds());
1749 crow::connections::systemBus->async_method_call(
1750 [aResp{std::move(aResp)}, datetime{std::move(datetime)}](
1751 const boost::system::error_code ec) {
1752 if (ec)
1753 {
1754 BMCWEB_LOG_DEBUG << "Failed to set elapsed time. "
1755 "DBUS response error "
1756 << ec;
1757 messages::internalError(aResp->res);
1758 return;
1759 }
1760 aResp->res.jsonValue["DateTime"] = datetime;
1761 },
1762 "xyz.openbmc_project.Time.Manager",
1763 "/xyz/openbmc_project/time/bmc",
1764 "org.freedesktop.DBus.Properties", "Set",
1765 "xyz.openbmc_project.Time.EpochTime", "Elapsed",
1766 std::variant<uint64_t>(durMicroSecs));
1767 }
1768 else
1769 {
1770 messages::propertyValueFormatError(aResp->res, datetime,
1771 "DateTime");
1772 return;
1773 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001774 }
1775
Ed Tanous0f74e642018-11-12 15:17:05 -08001776 std::string uuid;
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001777};
1778
Ed Tanous1abe55e2018-09-05 08:30:59 -07001779class ManagerCollection : public Node
1780{
1781 public:
James Feist5b4aa862018-08-16 14:07:01 -07001782 ManagerCollection(CrowApp& app) : Node(app, "/redfish/v1/Managers/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001783 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001784 entityPrivileges = {
1785 {boost::beast::http::verb::get, {{"Login"}}},
1786 {boost::beast::http::verb::head, {{"Login"}}},
1787 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1788 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1789 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1790 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1791 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001792
Ed Tanous1abe55e2018-09-05 08:30:59 -07001793 private:
James Feist5b4aa862018-08-16 14:07:01 -07001794 void doGet(crow::Response& res, const crow::Request& req,
1795 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -07001796 {
James Feist83ff9ab2018-08-31 10:18:24 -07001797 // Collections don't include the static data added by SubRoute
1798 // because it has a duplicate entry for members
Ed Tanous1abe55e2018-09-05 08:30:59 -07001799 res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
1800 res.jsonValue["@odata.type"] = "#ManagerCollection.ManagerCollection";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001801 res.jsonValue["Name"] = "Manager Collection";
1802 res.jsonValue["Members@odata.count"] = 1;
1803 res.jsonValue["Members"] = {
James Feist5b4aa862018-08-16 14:07:01 -07001804 {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001805 res.end();
1806 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001807};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001808} // namespace redfish