blob: a92f490aced48998670e18e1989ba5eb04affda7 [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"
Jennifer Leec5d03ff2019-03-08 15:42:58 -080019#include "redfish_util.hpp"
Borawski.Lukasz9c3106852018-02-09 15:24:22 +010020
John Edward Broadbent7e860f12021-04-08 15:57:16 -070021#include <app.hpp>
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>
Andrew Geisslere90c5052019-06-28 13:52:27 -050025#include <utils/fw_utils.hpp>
Bernard Wong7bffdb72019-03-20 16:17:21 +080026#include <utils/systemd_utils.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050027
Gunnar Mills4bfefa72020-07-30 13:54:29 -050028#include <cstdint>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050029#include <memory>
30#include <sstream>
Ed Tanousabf2add2019-01-22 16:40:12 -080031#include <variant>
James Feist5b4aa862018-08-16 14:07:01 -070032
Ed Tanous1abe55e2018-09-05 08:30:59 -070033namespace redfish
34{
Jennifer Leeed5befb2018-08-10 11:29:45 -070035
36/**
Gunnar Mills2a5c4402020-05-19 09:07:24 -050037 * Function reboots the BMC.
38 *
39 * @param[in] asyncResp - Shared pointer for completing asynchronous calls
Jennifer Leeed5befb2018-08-10 11:29:45 -070040 */
zhanghch058d1b46d2021-04-01 11:18:24 +080041inline void
42 doBMCGracefulRestart(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Gunnar Mills2a5c4402020-05-19 09:07:24 -050043{
44 const char* processName = "xyz.openbmc_project.State.BMC";
45 const char* objectPath = "/xyz/openbmc_project/state/bmc0";
46 const char* interfaceName = "xyz.openbmc_project.State.BMC";
47 const std::string& propertyValue =
48 "xyz.openbmc_project.State.BMC.Transition.Reboot";
49 const char* destProperty = "RequestedBMCTransition";
50
51 // Create the D-Bus variant for D-Bus call.
52 VariantType dbusPropertyValue(propertyValue);
53
54 crow::connections::systemBus->async_method_call(
55 [asyncResp](const boost::system::error_code ec) {
56 // Use "Set" method to set the property value.
57 if (ec)
58 {
59 BMCWEB_LOG_DEBUG << "[Set] Bad D-Bus request error: " << ec;
60 messages::internalError(asyncResp->res);
61 return;
62 }
63
64 messages::success(asyncResp->res);
65 },
66 processName, objectPath, "org.freedesktop.DBus.Properties", "Set",
67 interfaceName, destProperty, dbusPropertyValue);
68}
69
zhanghch058d1b46d2021-04-01 11:18:24 +080070inline void
71 doBMCForceRestart(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Jayaprakash Mutyalaf92af382020-06-16 23:29:41 +000072{
73 const char* processName = "xyz.openbmc_project.State.BMC";
74 const char* objectPath = "/xyz/openbmc_project/state/bmc0";
75 const char* interfaceName = "xyz.openbmc_project.State.BMC";
76 const std::string& propertyValue =
77 "xyz.openbmc_project.State.BMC.Transition.HardReboot";
78 const char* destProperty = "RequestedBMCTransition";
79
80 // Create the D-Bus variant for D-Bus call.
81 VariantType dbusPropertyValue(propertyValue);
82
83 crow::connections::systemBus->async_method_call(
84 [asyncResp](const boost::system::error_code ec) {
85 // Use "Set" method to set the property value.
86 if (ec)
87 {
88 BMCWEB_LOG_DEBUG << "[Set] Bad D-Bus request error: " << ec;
89 messages::internalError(asyncResp->res);
90 return;
91 }
92
93 messages::success(asyncResp->res);
94 },
95 processName, objectPath, "org.freedesktop.DBus.Properties", "Set",
96 interfaceName, destProperty, dbusPropertyValue);
97}
98
Gunnar Mills2a5c4402020-05-19 09:07:24 -050099/**
100 * ManagerResetAction class supports the POST method for the Reset (reboot)
101 * action.
102 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700103inline void requestRoutesManagerResetAction(App& app)
Jennifer Leeed5befb2018-08-10 11:29:45 -0700104{
Jennifer Leeed5befb2018-08-10 11:29:45 -0700105 /**
Jennifer Leeed5befb2018-08-10 11:29:45 -0700106 * Function handles POST method request.
Gunnar Mills2a5c4402020-05-19 09:07:24 -0500107 * Analyzes POST body before sending Reset (Reboot) request data to D-Bus.
Jayaprakash Mutyalaf92af382020-06-16 23:29:41 +0000108 * OpenBMC supports ResetType "GracefulRestart" and "ForceRestart".
Jennifer Leeed5befb2018-08-10 11:29:45 -0700109 */
Jennifer Leeed5befb2018-08-10 11:29:45 -0700110
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700111 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Actions/Manager.Reset/")
112 .privileges({"ConfigureManager"})
113 .methods(boost::beast::http::verb::post)(
114 [](const crow::Request& req,
115 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
116 BMCWEB_LOG_DEBUG << "Post Manager Reset.";
Gunnar Mills2a5c4402020-05-19 09:07:24 -0500117
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700118 std::string resetType;
Jennifer Leeed5befb2018-08-10 11:29:45 -0700119
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700120 if (!json_util::readJson(req, asyncResp->res, "ResetType",
121 resetType))
122 {
123 return;
124 }
Gunnar Mills2a5c4402020-05-19 09:07:24 -0500125
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700126 if (resetType == "GracefulRestart")
127 {
128 BMCWEB_LOG_DEBUG << "Proceeding with " << resetType;
129 doBMCGracefulRestart(asyncResp);
130 return;
131 }
132 if (resetType == "ForceRestart")
133 {
134 BMCWEB_LOG_DEBUG << "Proceeding with " << resetType;
135 doBMCForceRestart(asyncResp);
136 return;
137 }
138 BMCWEB_LOG_DEBUG << "Invalid property value for ResetType: "
139 << resetType;
140 messages::actionParameterNotSupported(asyncResp->res, resetType,
141 "ResetType");
142
143 return;
144 });
145}
Jennifer Leeed5befb2018-08-10 11:29:45 -0700146
Gunnar Mills3e40fc72020-05-19 19:18:17 -0500147/**
148 * ManagerResetToDefaultsAction class supports POST method for factory reset
149 * action.
150 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700151inline void requestRoutesManagerResetToDefaultsAction(App& app)
Gunnar Mills3e40fc72020-05-19 19:18:17 -0500152{
Gunnar Mills3e40fc72020-05-19 19:18:17 -0500153
Gunnar Mills3e40fc72020-05-19 19:18:17 -0500154 /**
155 * Function handles ResetToDefaults POST method request.
156 *
157 * Analyzes POST body message and factory resets BMC by calling
158 * BMC code updater factory reset followed by a BMC reboot.
159 *
160 * BMC code updater factory reset wipes the whole BMC read-write
161 * filesystem which includes things like the network settings.
162 *
163 * OpenBMC only supports ResetToDefaultsType "ResetAll".
164 */
Gunnar Mills3e40fc72020-05-19 19:18:17 -0500165
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700166 BMCWEB_ROUTE(app,
167 "/redfish/v1/Managers/bmc/Actions/Manager.ResetToDefaults/")
168 .privileges({"ConfigureManager"})
169 .methods(boost::beast::http::verb::post)(
170 [](const crow::Request& req,
171 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
172 BMCWEB_LOG_DEBUG << "Post ResetToDefaults.";
Gunnar Mills3e40fc72020-05-19 19:18:17 -0500173
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700174 std::string resetType;
Gunnar Mills3e40fc72020-05-19 19:18:17 -0500175
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700176 if (!json_util::readJson(req, asyncResp->res,
177 "ResetToDefaultsType", resetType))
Gunnar Mills3e40fc72020-05-19 19:18:17 -0500178 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700179 BMCWEB_LOG_DEBUG << "Missing property ResetToDefaultsType.";
180
181 messages::actionParameterMissing(asyncResp->res,
182 "ResetToDefaults",
183 "ResetToDefaultsType");
Gunnar Mills3e40fc72020-05-19 19:18:17 -0500184 return;
185 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700186
187 if (resetType != "ResetAll")
188 {
189 BMCWEB_LOG_DEBUG << "Invalid property value for "
190 "ResetToDefaultsType: "
191 << resetType;
192 messages::actionParameterNotSupported(
193 asyncResp->res, resetType, "ResetToDefaultsType");
194 return;
195 }
196
197 crow::connections::systemBus->async_method_call(
198 [asyncResp](const boost::system::error_code ec) {
199 if (ec)
200 {
201 BMCWEB_LOG_DEBUG << "Failed to ResetToDefaults: "
202 << ec;
203 messages::internalError(asyncResp->res);
204 return;
205 }
206 // Factory Reset doesn't actually happen until a reboot
207 // Can't erase what the BMC is running on
208 doBMCGracefulRestart(asyncResp);
209 },
210 "xyz.openbmc_project.Software.BMC.Updater",
211 "/xyz/openbmc_project/software",
212 "xyz.openbmc_project.Common.FactoryReset", "Reset");
213 });
214}
Gunnar Mills3e40fc72020-05-19 19:18:17 -0500215
AppaRao Puli1cb1a9e2020-07-17 23:38:57 +0530216/**
217 * ManagerResetActionInfo derived class for delivering Manager
218 * ResetType AllowableValues using ResetInfo schema.
219 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700220inline void requestRoutesManagerResetActionInfo(App& app)
AppaRao Puli1cb1a9e2020-07-17 23:38:57 +0530221{
AppaRao Puli1cb1a9e2020-07-17 23:38:57 +0530222 /**
223 * Functions triggers appropriate requests on DBus
224 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700225
226 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/ResetActionInfo/")
227 .privileges({"Login"})
228 .methods(boost::beast::http::verb::get)(
229 [](const crow::Request&,
230 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
231 asyncResp->res.jsonValue = {
232 {"@odata.type", "#ActionInfo.v1_1_2.ActionInfo"},
233 {"@odata.id", "/redfish/v1/Managers/bmc/ResetActionInfo"},
234 {"Name", "Reset Action Info"},
235 {"Id", "ResetActionInfo"},
236 {"Parameters",
237 {{{"Name", "ResetType"},
238 {"Required", true},
239 {"DataType", "String"},
240 {"AllowableValues",
241 {"GracefulRestart", "ForceRestart"}}}}}};
242 });
243}
AppaRao Puli1cb1a9e2020-07-17 23:38:57 +0530244
James Feist5b4aa862018-08-16 14:07:01 -0700245static constexpr const char* objectManagerIface =
246 "org.freedesktop.DBus.ObjectManager";
247static constexpr const char* pidConfigurationIface =
248 "xyz.openbmc_project.Configuration.Pid";
249static constexpr const char* pidZoneConfigurationIface =
250 "xyz.openbmc_project.Configuration.Pid.Zone";
James Feistb7a08d02018-12-11 14:55:37 -0800251static constexpr const char* stepwiseConfigurationIface =
252 "xyz.openbmc_project.Configuration.Stepwise";
James Feist73df0db2019-03-25 15:29:35 -0700253static constexpr const char* thermalModeIface =
254 "xyz.openbmc_project.Control.ThermalMode";
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100255
zhanghch058d1b46d2021-04-01 11:18:24 +0800256inline void
257 asyncPopulatePid(const std::string& connection, const std::string& path,
258 const std::string& currentProfile,
259 const std::vector<std::string>& supportedProfiles,
260 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
James Feist5b4aa862018-08-16 14:07:01 -0700261{
262
263 crow::connections::systemBus->async_method_call(
James Feist73df0db2019-03-25 15:29:35 -0700264 [asyncResp, currentProfile, supportedProfiles](
265 const boost::system::error_code ec,
266 const dbus::utility::ManagedObjectType& managedObj) {
James Feist5b4aa862018-08-16 14:07:01 -0700267 if (ec)
268 {
269 BMCWEB_LOG_ERROR << ec;
James Feist5b4aa862018-08-16 14:07:01 -0700270 asyncResp->res.jsonValue.clear();
Jason M. Billsf12894f2018-10-09 12:45:45 -0700271 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700272 return;
273 }
274 nlohmann::json& configRoot =
275 asyncResp->res.jsonValue["Oem"]["OpenBmc"]["Fan"];
276 nlohmann::json& fans = configRoot["FanControllers"];
277 fans["@odata.type"] = "#OemManager.FanControllers";
James Feist5b4aa862018-08-16 14:07:01 -0700278 fans["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc/"
279 "Fan/FanControllers";
280
281 nlohmann::json& pids = configRoot["PidControllers"];
282 pids["@odata.type"] = "#OemManager.PidControllers";
James Feist5b4aa862018-08-16 14:07:01 -0700283 pids["@odata.id"] =
284 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/PidControllers";
285
James Feistb7a08d02018-12-11 14:55:37 -0800286 nlohmann::json& stepwise = configRoot["StepwiseControllers"];
287 stepwise["@odata.type"] = "#OemManager.StepwiseControllers";
James Feistb7a08d02018-12-11 14:55:37 -0800288 stepwise["@odata.id"] =
289 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/StepwiseControllers";
290
James Feist5b4aa862018-08-16 14:07:01 -0700291 nlohmann::json& zones = configRoot["FanZones"];
292 zones["@odata.id"] =
293 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones";
294 zones["@odata.type"] = "#OemManager.FanZones";
James Feist5b4aa862018-08-16 14:07:01 -0700295 configRoot["@odata.id"] =
296 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan";
297 configRoot["@odata.type"] = "#OemManager.Fan";
James Feist73df0db2019-03-25 15:29:35 -0700298 configRoot["Profile@Redfish.AllowableValues"] = supportedProfiles;
299
300 if (!currentProfile.empty())
301 {
302 configRoot["Profile"] = currentProfile;
303 }
304 BMCWEB_LOG_ERROR << "profile = " << currentProfile << " !";
James Feist5b4aa862018-08-16 14:07:01 -0700305
James Feist5b4aa862018-08-16 14:07:01 -0700306 for (const auto& pathPair : managedObj)
307 {
308 for (const auto& intfPair : pathPair.second)
309 {
310 if (intfPair.first != pidConfigurationIface &&
James Feistb7a08d02018-12-11 14:55:37 -0800311 intfPair.first != pidZoneConfigurationIface &&
312 intfPair.first != stepwiseConfigurationIface)
James Feist5b4aa862018-08-16 14:07:01 -0700313 {
314 continue;
315 }
316 auto findName = intfPair.second.find("Name");
317 if (findName == intfPair.second.end())
318 {
319 BMCWEB_LOG_ERROR << "Pid Field missing Name";
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800320 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700321 return;
322 }
James Feist73df0db2019-03-25 15:29:35 -0700323
James Feist5b4aa862018-08-16 14:07:01 -0700324 const std::string* namePtr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800325 std::get_if<std::string>(&findName->second);
James Feist5b4aa862018-08-16 14:07:01 -0700326 if (namePtr == nullptr)
327 {
328 BMCWEB_LOG_ERROR << "Pid Name Field illegal";
James Feistb7a08d02018-12-11 14:55:37 -0800329 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700330 return;
331 }
James Feist5b4aa862018-08-16 14:07:01 -0700332 std::string name = *namePtr;
333 dbus::utility::escapePathForDbus(name);
James Feist73df0db2019-03-25 15:29:35 -0700334
335 auto findProfiles = intfPair.second.find("Profiles");
336 if (findProfiles != intfPair.second.end())
337 {
338 const std::vector<std::string>* profiles =
339 std::get_if<std::vector<std::string>>(
340 &findProfiles->second);
341 if (profiles == nullptr)
342 {
343 BMCWEB_LOG_ERROR << "Pid Profiles Field illegal";
344 messages::internalError(asyncResp->res);
345 return;
346 }
347 if (std::find(profiles->begin(), profiles->end(),
348 currentProfile) == profiles->end())
349 {
350 BMCWEB_LOG_INFO
351 << name << " not supported in current profile";
352 continue;
353 }
354 }
James Feistb7a08d02018-12-11 14:55:37 -0800355 nlohmann::json* config = nullptr;
James Feistc33a90e2019-03-01 10:17:44 -0800356
357 const std::string* classPtr = nullptr;
358 auto findClass = intfPair.second.find("Class");
359 if (findClass != intfPair.second.end())
360 {
361 classPtr = std::get_if<std::string>(&findClass->second);
362 }
363
James Feist5b4aa862018-08-16 14:07:01 -0700364 if (intfPair.first == pidZoneConfigurationIface)
365 {
366 std::string chassis;
367 if (!dbus::utility::getNthStringFromPath(
368 pathPair.first.str, 5, chassis))
369 {
370 chassis = "#IllegalValue";
371 }
372 nlohmann::json& zone = zones[name];
373 zone["Chassis"] = {
374 {"@odata.id", "/redfish/v1/Chassis/" + chassis}};
375 zone["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/"
376 "OpenBmc/Fan/FanZones/" +
377 name;
378 zone["@odata.type"] = "#OemManager.FanZone";
James Feistb7a08d02018-12-11 14:55:37 -0800379 config = &zone;
James Feist5b4aa862018-08-16 14:07:01 -0700380 }
381
James Feistb7a08d02018-12-11 14:55:37 -0800382 else if (intfPair.first == stepwiseConfigurationIface)
383 {
James Feistc33a90e2019-03-01 10:17:44 -0800384 if (classPtr == nullptr)
385 {
386 BMCWEB_LOG_ERROR << "Pid Class Field illegal";
387 messages::internalError(asyncResp->res);
388 return;
389 }
390
James Feistb7a08d02018-12-11 14:55:37 -0800391 nlohmann::json& controller = stepwise[name];
392 config = &controller;
393
394 controller["@odata.id"] =
395 "/redfish/v1/Managers/bmc#/Oem/"
396 "OpenBmc/Fan/StepwiseControllers/" +
Ed Tanous271584a2019-07-09 16:24:22 -0700397 name;
James Feistb7a08d02018-12-11 14:55:37 -0800398 controller["@odata.type"] =
399 "#OemManager.StepwiseController";
400
James Feistc33a90e2019-03-01 10:17:44 -0800401 controller["Direction"] = *classPtr;
James Feistb7a08d02018-12-11 14:55:37 -0800402 }
403
404 // pid and fans are off the same configuration
405 else if (intfPair.first == pidConfigurationIface)
406 {
James Feistc33a90e2019-03-01 10:17:44 -0800407
James Feistb7a08d02018-12-11 14:55:37 -0800408 if (classPtr == nullptr)
409 {
410 BMCWEB_LOG_ERROR << "Pid Class Field illegal";
411 messages::internalError(asyncResp->res);
412 return;
413 }
414 bool isFan = *classPtr == "fan";
415 nlohmann::json& element =
416 isFan ? fans[name] : pids[name];
417 config = &element;
418 if (isFan)
419 {
420 element["@odata.id"] =
421 "/redfish/v1/Managers/bmc#/Oem/"
422 "OpenBmc/Fan/FanControllers/" +
Ed Tanous271584a2019-07-09 16:24:22 -0700423 name;
James Feistb7a08d02018-12-11 14:55:37 -0800424 element["@odata.type"] =
425 "#OemManager.FanController";
James Feistb7a08d02018-12-11 14:55:37 -0800426 }
427 else
428 {
429 element["@odata.id"] =
430 "/redfish/v1/Managers/bmc#/Oem/"
431 "OpenBmc/Fan/PidControllers/" +
Ed Tanous271584a2019-07-09 16:24:22 -0700432 name;
James Feistb7a08d02018-12-11 14:55:37 -0800433 element["@odata.type"] =
434 "#OemManager.PidController";
James Feistb7a08d02018-12-11 14:55:37 -0800435 }
436 }
437 else
438 {
439 BMCWEB_LOG_ERROR << "Unexpected configuration";
440 messages::internalError(asyncResp->res);
441 return;
442 }
443
444 // used for making maps out of 2 vectors
445 const std::vector<double>* keys = nullptr;
446 const std::vector<double>* values = nullptr;
447
James Feist5b4aa862018-08-16 14:07:01 -0700448 for (const auto& propertyPair : intfPair.second)
449 {
450 if (propertyPair.first == "Type" ||
451 propertyPair.first == "Class" ||
452 propertyPair.first == "Name")
453 {
454 continue;
455 }
456
457 // zones
458 if (intfPair.first == pidZoneConfigurationIface)
459 {
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800460 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;
470 }
471
472 if (intfPair.first == stepwiseConfigurationIface)
473 {
474 if (propertyPair.first == "Reading" ||
475 propertyPair.first == "Output")
476 {
477 const std::vector<double>* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800478 std::get_if<std::vector<double>>(
James Feistb7a08d02018-12-11 14:55:37 -0800479 &propertyPair.second);
480
481 if (ptr == nullptr)
482 {
483 BMCWEB_LOG_ERROR << "Field Illegal "
484 << propertyPair.first;
485 messages::internalError(asyncResp->res);
486 return;
487 }
488
489 if (propertyPair.first == "Reading")
490 {
491 keys = ptr;
492 }
493 else
494 {
495 values = ptr;
496 }
497 if (keys && values)
498 {
499 if (keys->size() != values->size())
500 {
501 BMCWEB_LOG_ERROR
502 << "Reading and Output size don't "
503 "match ";
504 messages::internalError(asyncResp->res);
505 return;
506 }
507 nlohmann::json& steps = (*config)["Steps"];
508 steps = nlohmann::json::array();
509 for (size_t ii = 0; ii < keys->size(); ii++)
510 {
511 steps.push_back(
512 {{"Target", (*keys)[ii]},
513 {"Output", (*values)[ii]}});
514 }
515 }
516 }
517 if (propertyPair.first == "NegativeHysteresis" ||
518 propertyPair.first == "PositiveHysteresis")
519 {
520 const double* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800521 std::get_if<double>(&propertyPair.second);
James Feistb7a08d02018-12-11 14:55:37 -0800522 if (ptr == nullptr)
523 {
524 BMCWEB_LOG_ERROR << "Field Illegal "
525 << propertyPair.first;
526 messages::internalError(asyncResp->res);
527 return;
528 }
529 (*config)[propertyPair.first] = *ptr;
530 }
James Feist5b4aa862018-08-16 14:07:01 -0700531 }
532
533 // pid and fans are off the same configuration
James Feistb7a08d02018-12-11 14:55:37 -0800534 if (intfPair.first == pidConfigurationIface ||
535 intfPair.first == stepwiseConfigurationIface)
James Feist5b4aa862018-08-16 14:07:01 -0700536 {
James Feist5b4aa862018-08-16 14:07:01 -0700537
538 if (propertyPair.first == "Zones")
539 {
540 const std::vector<std::string>* inputs =
Ed Tanousabf2add2019-01-22 16:40:12 -0800541 std::get_if<std::vector<std::string>>(
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800542 &propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700543
544 if (inputs == nullptr)
545 {
546 BMCWEB_LOG_ERROR
547 << "Zones Pid Field Illegal";
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800548 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700549 return;
550 }
James Feistb7a08d02018-12-11 14:55:37 -0800551 auto& data = (*config)[propertyPair.first];
James Feist5b4aa862018-08-16 14:07:01 -0700552 data = nlohmann::json::array();
553 for (std::string itemCopy : *inputs)
554 {
555 dbus::utility::escapePathForDbus(itemCopy);
556 data.push_back(
557 {{"@odata.id",
558 "/redfish/v1/Managers/bmc#/Oem/"
559 "OpenBmc/Fan/FanZones/" +
560 itemCopy}});
561 }
562 }
563 // todo(james): may never happen, but this
564 // assumes configuration data referenced in the
565 // PID config is provided by the same daemon, we
566 // could add another loop to cover all cases,
567 // but I'm okay kicking this can down the road a
568 // bit
569
570 else if (propertyPair.first == "Inputs" ||
571 propertyPair.first == "Outputs")
572 {
James Feistb7a08d02018-12-11 14:55:37 -0800573 auto& data = (*config)[propertyPair.first];
James Feist5b4aa862018-08-16 14:07:01 -0700574 const std::vector<std::string>* inputs =
Ed Tanousabf2add2019-01-22 16:40:12 -0800575 std::get_if<std::vector<std::string>>(
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800576 &propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700577
578 if (inputs == nullptr)
579 {
580 BMCWEB_LOG_ERROR << "Field Illegal "
581 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700582 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700583 return;
584 }
585 data = *inputs;
James Feistb943aae2019-07-11 16:33:56 -0700586 }
587 else if (propertyPair.first == "SetPointOffset")
588 {
589 const std::string* ptr =
590 std::get_if<std::string>(
591 &propertyPair.second);
592
593 if (ptr == nullptr)
594 {
595 BMCWEB_LOG_ERROR << "Field Illegal "
596 << propertyPair.first;
597 messages::internalError(asyncResp->res);
598 return;
599 }
600 // translate from dbus to redfish
601 if (*ptr == "WarningHigh")
602 {
603 (*config)["SetPointOffset"] =
604 "UpperThresholdNonCritical";
605 }
606 else if (*ptr == "WarningLow")
607 {
608 (*config)["SetPointOffset"] =
609 "LowerThresholdNonCritical";
610 }
611 else if (*ptr == "CriticalHigh")
612 {
613 (*config)["SetPointOffset"] =
614 "UpperThresholdCritical";
615 }
616 else if (*ptr == "CriticalLow")
617 {
618 (*config)["SetPointOffset"] =
619 "LowerThresholdCritical";
620 }
621 else
622 {
623 BMCWEB_LOG_ERROR << "Value Illegal "
624 << *ptr;
625 messages::internalError(asyncResp->res);
626 return;
627 }
628 }
629 // doubles
James Feist5b4aa862018-08-16 14:07:01 -0700630 else if (propertyPair.first ==
631 "FFGainCoefficient" ||
632 propertyPair.first == "FFOffCoefficient" ||
633 propertyPair.first == "ICoefficient" ||
634 propertyPair.first == "ILimitMax" ||
635 propertyPair.first == "ILimitMin" ||
James Feistaad1a252019-02-19 10:13:52 -0800636 propertyPair.first ==
637 "PositiveHysteresis" ||
638 propertyPair.first ==
639 "NegativeHysteresis" ||
James Feist5b4aa862018-08-16 14:07:01 -0700640 propertyPair.first == "OutLimitMax" ||
641 propertyPair.first == "OutLimitMin" ||
642 propertyPair.first == "PCoefficient" ||
James Feist7625cb82019-01-23 11:58:21 -0800643 propertyPair.first == "SetPoint" ||
James Feist5b4aa862018-08-16 14:07:01 -0700644 propertyPair.first == "SlewNeg" ||
645 propertyPair.first == "SlewPos")
646 {
647 const double* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800648 std::get_if<double>(&propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700649 if (ptr == nullptr)
650 {
651 BMCWEB_LOG_ERROR << "Field Illegal "
652 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700653 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700654 return;
655 }
James Feistb7a08d02018-12-11 14:55:37 -0800656 (*config)[propertyPair.first] = *ptr;
James Feist5b4aa862018-08-16 14:07:01 -0700657 }
658 }
659 }
660 }
661 }
662 },
663 connection, path, objectManagerIface, "GetManagedObjects");
664}
Jennifer Leeca537922018-08-10 10:07:30 -0700665
James Feist83ff9ab2018-08-31 10:18:24 -0700666enum class CreatePIDRet
667{
668 fail,
669 del,
670 patch
671};
672
zhanghch058d1b46d2021-04-01 11:18:24 +0800673inline bool
674 getZonesFromJsonReq(const std::shared_ptr<bmcweb::AsyncResp>& response,
675 std::vector<nlohmann::json>& config,
676 std::vector<std::string>& zones)
James Feist5f2caae2018-12-12 14:08:25 -0800677{
James Feistb6baeaa2019-02-21 10:41:40 -0800678 if (config.empty())
679 {
680 BMCWEB_LOG_ERROR << "Empty Zones";
681 messages::propertyValueFormatError(response->res,
682 nlohmann::json::array(), "Zones");
683 return false;
684 }
James Feist5f2caae2018-12-12 14:08:25 -0800685 for (auto& odata : config)
686 {
687 std::string path;
688 if (!redfish::json_util::readJson(odata, response->res, "@odata.id",
689 path))
690 {
691 return false;
692 }
693 std::string input;
James Feist61adbda2019-03-25 13:03:51 -0700694
695 // 8 below comes from
696 // /redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones/Left
697 // 0 1 2 3 4 5 6 7 8
698 if (!dbus::utility::getNthStringFromPath(path, 8, input))
James Feist5f2caae2018-12-12 14:08:25 -0800699 {
700 BMCWEB_LOG_ERROR << "Got invalid path " << path;
701 BMCWEB_LOG_ERROR << "Illegal Type Zones";
702 messages::propertyValueFormatError(response->res, odata.dump(),
703 "Zones");
704 return false;
705 }
706 boost::replace_all(input, "_", " ");
707 zones.emplace_back(std::move(input));
708 }
709 return true;
710}
711
Ed Tanous23a21a12020-07-25 04:45:05 +0000712inline const dbus::utility::ManagedItem*
James Feist73df0db2019-03-25 15:29:35 -0700713 findChassis(const dbus::utility::ManagedObjectType& managedObj,
714 const std::string& value, std::string& chassis)
James Feistb6baeaa2019-02-21 10:41:40 -0800715{
716 BMCWEB_LOG_DEBUG << "Find Chassis: " << value << "\n";
717
718 std::string escaped = boost::replace_all_copy(value, " ", "_");
719 escaped = "/" + escaped;
720 auto it = std::find_if(
721 managedObj.begin(), managedObj.end(), [&escaped](const auto& obj) {
722 if (boost::algorithm::ends_with(obj.first.str, escaped))
723 {
724 BMCWEB_LOG_DEBUG << "Matched " << obj.first.str << "\n";
725 return true;
726 }
727 return false;
728 });
729
730 if (it == managedObj.end())
731 {
James Feist73df0db2019-03-25 15:29:35 -0700732 return nullptr;
James Feistb6baeaa2019-02-21 10:41:40 -0800733 }
734 // 5 comes from <chassis-name> being the 5th element
735 // /xyz/openbmc_project/inventory/system/chassis/<chassis-name>
James Feist73df0db2019-03-25 15:29:35 -0700736 if (dbus::utility::getNthStringFromPath(it->first.str, 5, chassis))
737 {
738 return &(*it);
739 }
740
741 return nullptr;
James Feistb6baeaa2019-02-21 10:41:40 -0800742}
743
Ed Tanous23a21a12020-07-25 04:45:05 +0000744inline CreatePIDRet createPidInterface(
zhanghch058d1b46d2021-04-01 11:18:24 +0800745 const std::shared_ptr<bmcweb::AsyncResp>& response, const std::string& type,
Ed Tanousb5a76932020-09-29 16:16:58 -0700746 const nlohmann::json::iterator& it, const std::string& path,
James Feist83ff9ab2018-08-31 10:18:24 -0700747 const dbus::utility::ManagedObjectType& managedObj, bool createNewObject,
748 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>&
749 output,
James Feist73df0db2019-03-25 15:29:35 -0700750 std::string& chassis, const std::string& profile)
James Feist83ff9ab2018-08-31 10:18:24 -0700751{
752
James Feist5f2caae2018-12-12 14:08:25 -0800753 // common deleter
James Feistb6baeaa2019-02-21 10:41:40 -0800754 if (it.value() == nullptr)
James Feist5f2caae2018-12-12 14:08:25 -0800755 {
756 std::string iface;
757 if (type == "PidControllers" || type == "FanControllers")
758 {
759 iface = pidConfigurationIface;
760 }
761 else if (type == "FanZones")
762 {
763 iface = pidZoneConfigurationIface;
764 }
765 else if (type == "StepwiseControllers")
766 {
767 iface = stepwiseConfigurationIface;
768 }
769 else
770 {
Gunnar Millsa0744d32020-11-09 15:40:45 -0600771 BMCWEB_LOG_ERROR << "Illegal Type " << type;
James Feist5f2caae2018-12-12 14:08:25 -0800772 messages::propertyUnknown(response->res, type);
773 return CreatePIDRet::fail;
774 }
James Feist6ee7f772020-02-06 16:25:27 -0800775
776 BMCWEB_LOG_DEBUG << "del " << path << " " << iface << "\n";
James Feist5f2caae2018-12-12 14:08:25 -0800777 // delete interface
778 crow::connections::systemBus->async_method_call(
779 [response, path](const boost::system::error_code ec) {
780 if (ec)
781 {
782 BMCWEB_LOG_ERROR << "Error patching " << path << ": " << ec;
783 messages::internalError(response->res);
James Feistb6baeaa2019-02-21 10:41:40 -0800784 return;
James Feist5f2caae2018-12-12 14:08:25 -0800785 }
James Feistb6baeaa2019-02-21 10:41:40 -0800786 messages::success(response->res);
James Feist5f2caae2018-12-12 14:08:25 -0800787 },
788 "xyz.openbmc_project.EntityManager", path, iface, "Delete");
789 return CreatePIDRet::del;
790 }
791
James Feist73df0db2019-03-25 15:29:35 -0700792 const dbus::utility::ManagedItem* managedItem = nullptr;
James Feistb6baeaa2019-02-21 10:41:40 -0800793 if (!createNewObject)
794 {
795 // if we aren't creating a new object, we should be able to find it on
796 // d-bus
James Feist73df0db2019-03-25 15:29:35 -0700797 managedItem = findChassis(managedObj, it.key(), chassis);
798 if (managedItem == nullptr)
James Feistb6baeaa2019-02-21 10:41:40 -0800799 {
800 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
801 messages::invalidObject(response->res, it.key());
802 return CreatePIDRet::fail;
803 }
804 }
805
James Feist73df0db2019-03-25 15:29:35 -0700806 if (profile.size() &&
807 (type == "PidControllers" || type == "FanControllers" ||
808 type == "StepwiseControllers"))
809 {
810 if (managedItem == nullptr)
811 {
812 output["Profiles"] = std::vector<std::string>{profile};
813 }
814 else
815 {
816 std::string interface;
817 if (type == "StepwiseControllers")
818 {
819 interface = stepwiseConfigurationIface;
820 }
821 else
822 {
823 interface = pidConfigurationIface;
824 }
825 auto findConfig = managedItem->second.find(interface);
826 if (findConfig == managedItem->second.end())
827 {
828 BMCWEB_LOG_ERROR
829 << "Failed to find interface in managed object";
830 messages::internalError(response->res);
831 return CreatePIDRet::fail;
832 }
833 auto findProfiles = findConfig->second.find("Profiles");
834 if (findProfiles != findConfig->second.end())
835 {
836 const std::vector<std::string>* curProfiles =
837 std::get_if<std::vector<std::string>>(
838 &(findProfiles->second));
839 if (curProfiles == nullptr)
840 {
841 BMCWEB_LOG_ERROR << "Illegal profiles in managed object";
842 messages::internalError(response->res);
843 return CreatePIDRet::fail;
844 }
845 if (std::find(curProfiles->begin(), curProfiles->end(),
846 profile) == curProfiles->end())
847 {
848 std::vector<std::string> newProfiles = *curProfiles;
849 newProfiles.push_back(profile);
850 output["Profiles"] = newProfiles;
851 }
852 }
853 }
854 }
855
James Feist83ff9ab2018-08-31 10:18:24 -0700856 if (type == "PidControllers" || type == "FanControllers")
857 {
858 if (createNewObject)
859 {
860 output["Class"] = type == "PidControllers" ? std::string("temp")
861 : std::string("fan");
862 output["Type"] = std::string("Pid");
863 }
James Feist5f2caae2018-12-12 14:08:25 -0800864
865 std::optional<std::vector<nlohmann::json>> zones;
866 std::optional<std::vector<std::string>> inputs;
867 std::optional<std::vector<std::string>> outputs;
868 std::map<std::string, std::optional<double>> doubles;
James Feistb943aae2019-07-11 16:33:56 -0700869 std::optional<std::string> setpointOffset;
James Feist5f2caae2018-12-12 14:08:25 -0800870 if (!redfish::json_util::readJson(
James Feistb6baeaa2019-02-21 10:41:40 -0800871 it.value(), response->res, "Inputs", inputs, "Outputs", outputs,
James Feist5f2caae2018-12-12 14:08:25 -0800872 "Zones", zones, "FFGainCoefficient",
873 doubles["FFGainCoefficient"], "FFOffCoefficient",
874 doubles["FFOffCoefficient"], "ICoefficient",
875 doubles["ICoefficient"], "ILimitMax", doubles["ILimitMax"],
876 "ILimitMin", doubles["ILimitMin"], "OutLimitMax",
877 doubles["OutLimitMax"], "OutLimitMin", doubles["OutLimitMin"],
878 "PCoefficient", doubles["PCoefficient"], "SetPoint",
James Feistb943aae2019-07-11 16:33:56 -0700879 doubles["SetPoint"], "SetPointOffset", setpointOffset,
880 "SlewNeg", doubles["SlewNeg"], "SlewPos", doubles["SlewPos"],
881 "PositiveHysteresis", doubles["PositiveHysteresis"],
882 "NegativeHysteresis", doubles["NegativeHysteresis"]))
James Feist83ff9ab2018-08-31 10:18:24 -0700883 {
Ed Tanous71f52d92021-02-19 08:51:17 -0800884 BMCWEB_LOG_ERROR
885 << "Illegal Property "
886 << it.value().dump(2, ' ', true,
887 nlohmann::json::error_handler_t::replace);
James Feist5f2caae2018-12-12 14:08:25 -0800888 return CreatePIDRet::fail;
James Feist83ff9ab2018-08-31 10:18:24 -0700889 }
James Feist5f2caae2018-12-12 14:08:25 -0800890 if (zones)
James Feist83ff9ab2018-08-31 10:18:24 -0700891 {
James Feist5f2caae2018-12-12 14:08:25 -0800892 std::vector<std::string> zonesStr;
893 if (!getZonesFromJsonReq(response, *zones, zonesStr))
James Feist83ff9ab2018-08-31 10:18:24 -0700894 {
Gunnar Millsa0744d32020-11-09 15:40:45 -0600895 BMCWEB_LOG_ERROR << "Illegal Zones";
James Feist5f2caae2018-12-12 14:08:25 -0800896 return CreatePIDRet::fail;
James Feist83ff9ab2018-08-31 10:18:24 -0700897 }
James Feistb6baeaa2019-02-21 10:41:40 -0800898 if (chassis.empty() &&
899 !findChassis(managedObj, zonesStr[0], chassis))
900 {
901 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
902 messages::invalidObject(response->res, it.key());
903 return CreatePIDRet::fail;
904 }
905
James Feist5f2caae2018-12-12 14:08:25 -0800906 output["Zones"] = std::move(zonesStr);
907 }
908 if (inputs || outputs)
909 {
910 std::array<std::optional<std::vector<std::string>>*, 2> containers =
911 {&inputs, &outputs};
912 size_t index = 0;
913 for (const auto& containerPtr : containers)
James Feist83ff9ab2018-08-31 10:18:24 -0700914 {
James Feist5f2caae2018-12-12 14:08:25 -0800915 std::optional<std::vector<std::string>>& container =
916 *containerPtr;
917 if (!container)
James Feist83ff9ab2018-08-31 10:18:24 -0700918 {
James Feist5f2caae2018-12-12 14:08:25 -0800919 index++;
920 continue;
James Feist83ff9ab2018-08-31 10:18:24 -0700921 }
James Feist5f2caae2018-12-12 14:08:25 -0800922
923 for (std::string& value : *container)
James Feist83ff9ab2018-08-31 10:18:24 -0700924 {
James Feist5f2caae2018-12-12 14:08:25 -0800925 boost::replace_all(value, "_", " ");
James Feist83ff9ab2018-08-31 10:18:24 -0700926 }
James Feist5f2caae2018-12-12 14:08:25 -0800927 std::string key;
928 if (index == 0)
James Feist83ff9ab2018-08-31 10:18:24 -0700929 {
James Feist5f2caae2018-12-12 14:08:25 -0800930 key = "Inputs";
James Feist83ff9ab2018-08-31 10:18:24 -0700931 }
James Feist5f2caae2018-12-12 14:08:25 -0800932 else
933 {
934 key = "Outputs";
935 }
936 output[key] = *container;
937 index++;
James Feist83ff9ab2018-08-31 10:18:24 -0700938 }
James Feist5f2caae2018-12-12 14:08:25 -0800939 }
James Feist83ff9ab2018-08-31 10:18:24 -0700940
James Feistb943aae2019-07-11 16:33:56 -0700941 if (setpointOffset)
942 {
943 // translate between redfish and dbus names
944 if (*setpointOffset == "UpperThresholdNonCritical")
945 {
946 output["SetPointOffset"] = std::string("WarningLow");
947 }
948 else if (*setpointOffset == "LowerThresholdNonCritical")
949 {
950 output["SetPointOffset"] = std::string("WarningHigh");
951 }
952 else if (*setpointOffset == "LowerThresholdCritical")
953 {
954 output["SetPointOffset"] = std::string("CriticalLow");
955 }
956 else if (*setpointOffset == "UpperThresholdCritical")
957 {
958 output["SetPointOffset"] = std::string("CriticalHigh");
959 }
960 else
961 {
962 BMCWEB_LOG_ERROR << "Invalid setpointoffset "
963 << *setpointOffset;
964 messages::invalidObject(response->res, it.key());
965 return CreatePIDRet::fail;
966 }
967 }
968
James Feist5f2caae2018-12-12 14:08:25 -0800969 // doubles
970 for (const auto& pairs : doubles)
971 {
972 if (!pairs.second)
James Feist83ff9ab2018-08-31 10:18:24 -0700973 {
James Feist5f2caae2018-12-12 14:08:25 -0800974 continue;
James Feist83ff9ab2018-08-31 10:18:24 -0700975 }
James Feist5f2caae2018-12-12 14:08:25 -0800976 BMCWEB_LOG_DEBUG << pairs.first << " = " << *pairs.second;
977 output[pairs.first] = *(pairs.second);
James Feist83ff9ab2018-08-31 10:18:24 -0700978 }
979 }
James Feist5f2caae2018-12-12 14:08:25 -0800980
James Feist83ff9ab2018-08-31 10:18:24 -0700981 else if (type == "FanZones")
982 {
James Feist83ff9ab2018-08-31 10:18:24 -0700983 output["Type"] = std::string("Pid.Zone");
984
James Feist5f2caae2018-12-12 14:08:25 -0800985 std::optional<nlohmann::json> chassisContainer;
986 std::optional<double> failSafePercent;
James Feistd3ec07f2019-02-25 14:51:15 -0800987 std::optional<double> minThermalOutput;
James Feistb6baeaa2019-02-21 10:41:40 -0800988 if (!redfish::json_util::readJson(it.value(), response->res, "Chassis",
James Feist5f2caae2018-12-12 14:08:25 -0800989 chassisContainer, "FailSafePercent",
James Feistd3ec07f2019-02-25 14:51:15 -0800990 failSafePercent, "MinThermalOutput",
991 minThermalOutput))
James Feist83ff9ab2018-08-31 10:18:24 -0700992 {
Ed Tanous71f52d92021-02-19 08:51:17 -0800993 BMCWEB_LOG_ERROR
994 << "Illegal Property "
995 << it.value().dump(2, ' ', true,
996 nlohmann::json::error_handler_t::replace);
James Feist5f2caae2018-12-12 14:08:25 -0800997 return CreatePIDRet::fail;
998 }
James Feist83ff9ab2018-08-31 10:18:24 -0700999
James Feist5f2caae2018-12-12 14:08:25 -08001000 if (chassisContainer)
1001 {
1002
1003 std::string chassisId;
1004 if (!redfish::json_util::readJson(*chassisContainer, response->res,
1005 "@odata.id", chassisId))
James Feist83ff9ab2018-08-31 10:18:24 -07001006 {
Ed Tanous71f52d92021-02-19 08:51:17 -08001007 BMCWEB_LOG_ERROR
1008 << "Illegal Property "
1009 << chassisContainer->dump(
1010 2, ' ', true,
1011 nlohmann::json::error_handler_t::replace);
James Feist83ff9ab2018-08-31 10:18:24 -07001012 return CreatePIDRet::fail;
1013 }
James Feist5f2caae2018-12-12 14:08:25 -08001014
AppaRao Puli717794d2019-10-18 22:54:53 +05301015 // /redfish/v1/chassis/chassis_name/
James Feist5f2caae2018-12-12 14:08:25 -08001016 if (!dbus::utility::getNthStringFromPath(chassisId, 3, chassis))
1017 {
1018 BMCWEB_LOG_ERROR << "Got invalid path " << chassisId;
1019 messages::invalidObject(response->res, chassisId);
1020 return CreatePIDRet::fail;
1021 }
1022 }
James Feistd3ec07f2019-02-25 14:51:15 -08001023 if (minThermalOutput)
James Feist5f2caae2018-12-12 14:08:25 -08001024 {
James Feistd3ec07f2019-02-25 14:51:15 -08001025 output["MinThermalOutput"] = *minThermalOutput;
James Feist5f2caae2018-12-12 14:08:25 -08001026 }
1027 if (failSafePercent)
1028 {
1029 output["FailSafePercent"] = *failSafePercent;
1030 }
1031 }
1032 else if (type == "StepwiseControllers")
1033 {
1034 output["Type"] = std::string("Stepwise");
1035
1036 std::optional<std::vector<nlohmann::json>> zones;
1037 std::optional<std::vector<nlohmann::json>> steps;
1038 std::optional<std::vector<std::string>> inputs;
1039 std::optional<double> positiveHysteresis;
1040 std::optional<double> negativeHysteresis;
James Feistc33a90e2019-03-01 10:17:44 -08001041 std::optional<std::string> direction; // upper clipping curve vs lower
James Feist5f2caae2018-12-12 14:08:25 -08001042 if (!redfish::json_util::readJson(
James Feistb6baeaa2019-02-21 10:41:40 -08001043 it.value(), response->res, "Zones", zones, "Steps", steps,
1044 "Inputs", inputs, "PositiveHysteresis", positiveHysteresis,
James Feistc33a90e2019-03-01 10:17:44 -08001045 "NegativeHysteresis", negativeHysteresis, "Direction",
1046 direction))
James Feist5f2caae2018-12-12 14:08:25 -08001047 {
Ed Tanous71f52d92021-02-19 08:51:17 -08001048 BMCWEB_LOG_ERROR
1049 << "Illegal Property "
1050 << it.value().dump(2, ' ', true,
1051 nlohmann::json::error_handler_t::replace);
James Feist5f2caae2018-12-12 14:08:25 -08001052 return CreatePIDRet::fail;
1053 }
1054
1055 if (zones)
1056 {
James Feistb6baeaa2019-02-21 10:41:40 -08001057 std::vector<std::string> zonesStrs;
1058 if (!getZonesFromJsonReq(response, *zones, zonesStrs))
James Feist5f2caae2018-12-12 14:08:25 -08001059 {
Gunnar Millsa0744d32020-11-09 15:40:45 -06001060 BMCWEB_LOG_ERROR << "Illegal Zones";
James Feist5f2caae2018-12-12 14:08:25 -08001061 return CreatePIDRet::fail;
1062 }
James Feistb6baeaa2019-02-21 10:41:40 -08001063 if (chassis.empty() &&
1064 !findChassis(managedObj, zonesStrs[0], chassis))
1065 {
1066 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
1067 messages::invalidObject(response->res, it.key());
1068 return CreatePIDRet::fail;
1069 }
1070 output["Zones"] = std::move(zonesStrs);
James Feist5f2caae2018-12-12 14:08:25 -08001071 }
1072 if (steps)
1073 {
1074 std::vector<double> readings;
1075 std::vector<double> outputs;
1076 for (auto& step : *steps)
1077 {
1078 double target;
Ed Tanous23a21a12020-07-25 04:45:05 +00001079 double out;
James Feist5f2caae2018-12-12 14:08:25 -08001080
1081 if (!redfish::json_util::readJson(step, response->res, "Target",
Ed Tanous23a21a12020-07-25 04:45:05 +00001082 target, "Output", out))
James Feist5f2caae2018-12-12 14:08:25 -08001083 {
Ed Tanous71f52d92021-02-19 08:51:17 -08001084 BMCWEB_LOG_ERROR
1085 << "Illegal Property "
1086 << it.value().dump(
1087 2, ' ', true,
1088 nlohmann::json::error_handler_t::replace);
James Feist5f2caae2018-12-12 14:08:25 -08001089 return CreatePIDRet::fail;
1090 }
1091 readings.emplace_back(target);
Ed Tanous23a21a12020-07-25 04:45:05 +00001092 outputs.emplace_back(out);
James Feist5f2caae2018-12-12 14:08:25 -08001093 }
1094 output["Reading"] = std::move(readings);
1095 output["Output"] = std::move(outputs);
1096 }
1097 if (inputs)
1098 {
1099 for (std::string& value : *inputs)
1100 {
James Feist5f2caae2018-12-12 14:08:25 -08001101 boost::replace_all(value, "_", " ");
1102 }
1103 output["Inputs"] = std::move(*inputs);
1104 }
1105 if (negativeHysteresis)
1106 {
1107 output["NegativeHysteresis"] = *negativeHysteresis;
1108 }
1109 if (positiveHysteresis)
1110 {
1111 output["PositiveHysteresis"] = *positiveHysteresis;
James Feist83ff9ab2018-08-31 10:18:24 -07001112 }
James Feistc33a90e2019-03-01 10:17:44 -08001113 if (direction)
1114 {
1115 constexpr const std::array<const char*, 2> allowedDirections = {
1116 "Ceiling", "Floor"};
1117 if (std::find(allowedDirections.begin(), allowedDirections.end(),
1118 *direction) == allowedDirections.end())
1119 {
1120 messages::propertyValueTypeError(response->res, "Direction",
1121 *direction);
1122 return CreatePIDRet::fail;
1123 }
1124 output["Class"] = *direction;
1125 }
James Feist83ff9ab2018-08-31 10:18:24 -07001126 }
1127 else
1128 {
Gunnar Millsa0744d32020-11-09 15:40:45 -06001129 BMCWEB_LOG_ERROR << "Illegal Type " << type;
Jason M. Bills35a62c72018-10-09 12:45:45 -07001130 messages::propertyUnknown(response->res, type);
James Feist83ff9ab2018-08-31 10:18:24 -07001131 return CreatePIDRet::fail;
1132 }
1133 return CreatePIDRet::patch;
1134}
James Feist73df0db2019-03-25 15:29:35 -07001135struct GetPIDValues : std::enable_shared_from_this<GetPIDValues>
1136{
1137
zhanghch058d1b46d2021-04-01 11:18:24 +08001138 GetPIDValues(const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
Ed Tanous23a21a12020-07-25 04:45:05 +00001139 asyncResp(asyncRespIn)
James Feist73df0db2019-03-25 15:29:35 -07001140
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001141 {}
James Feist73df0db2019-03-25 15:29:35 -07001142
1143 void run()
1144 {
1145 std::shared_ptr<GetPIDValues> self = shared_from_this();
1146
1147 // get all configurations
1148 crow::connections::systemBus->async_method_call(
1149 [self](const boost::system::error_code ec,
Ed Tanous23a21a12020-07-25 04:45:05 +00001150 const crow::openbmc_mapper::GetSubTreeType& subtreeLocal) {
James Feist73df0db2019-03-25 15:29:35 -07001151 if (ec)
1152 {
1153 BMCWEB_LOG_ERROR << ec;
1154 messages::internalError(self->asyncResp->res);
1155 return;
1156 }
Ed Tanous23a21a12020-07-25 04:45:05 +00001157 self->subtree = subtreeLocal;
James Feist73df0db2019-03-25 15:29:35 -07001158 },
1159 "xyz.openbmc_project.ObjectMapper",
1160 "/xyz/openbmc_project/object_mapper",
1161 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
1162 std::array<const char*, 4>{
1163 pidConfigurationIface, pidZoneConfigurationIface,
1164 objectManagerIface, stepwiseConfigurationIface});
1165
1166 // at the same time get the selected profile
1167 crow::connections::systemBus->async_method_call(
1168 [self](const boost::system::error_code ec,
Ed Tanous23a21a12020-07-25 04:45:05 +00001169 const crow::openbmc_mapper::GetSubTreeType& subtreeLocal) {
1170 if (ec || subtreeLocal.empty())
James Feist73df0db2019-03-25 15:29:35 -07001171 {
1172 return;
1173 }
Ed Tanous23a21a12020-07-25 04:45:05 +00001174 if (subtreeLocal[0].second.size() != 1)
James Feist73df0db2019-03-25 15:29:35 -07001175 {
1176 // invalid mapper response, should never happen
1177 BMCWEB_LOG_ERROR << "GetPIDValues: Mapper Error";
1178 messages::internalError(self->asyncResp->res);
1179 return;
1180 }
1181
Ed Tanous23a21a12020-07-25 04:45:05 +00001182 const std::string& path = subtreeLocal[0].first;
1183 const std::string& owner = subtreeLocal[0].second[0].first;
James Feist73df0db2019-03-25 15:29:35 -07001184 crow::connections::systemBus->async_method_call(
1185 [path, owner, self](
Ed Tanous23a21a12020-07-25 04:45:05 +00001186 const boost::system::error_code ec2,
James Feist73df0db2019-03-25 15:29:35 -07001187 const boost::container::flat_map<
1188 std::string, std::variant<std::vector<std::string>,
1189 std::string>>& resp) {
Ed Tanous23a21a12020-07-25 04:45:05 +00001190 if (ec2)
James Feist73df0db2019-03-25 15:29:35 -07001191 {
1192 BMCWEB_LOG_ERROR << "GetPIDValues: Can't get "
1193 "thermalModeIface "
1194 << path;
1195 messages::internalError(self->asyncResp->res);
1196 return;
1197 }
Ed Tanous271584a2019-07-09 16:24:22 -07001198 const std::string* current = nullptr;
1199 const std::vector<std::string>* supported = nullptr;
James Feist73df0db2019-03-25 15:29:35 -07001200 for (auto& [key, value] : resp)
1201 {
1202 if (key == "Current")
1203 {
1204 current = std::get_if<std::string>(&value);
1205 if (current == nullptr)
1206 {
1207 BMCWEB_LOG_ERROR
1208 << "GetPIDValues: thermal mode "
1209 "iface invalid "
1210 << path;
1211 messages::internalError(
1212 self->asyncResp->res);
1213 return;
1214 }
1215 }
1216 if (key == "Supported")
1217 {
1218 supported =
1219 std::get_if<std::vector<std::string>>(
1220 &value);
1221 if (supported == nullptr)
1222 {
1223 BMCWEB_LOG_ERROR
1224 << "GetPIDValues: thermal mode "
1225 "iface invalid"
1226 << path;
1227 messages::internalError(
1228 self->asyncResp->res);
1229 return;
1230 }
1231 }
1232 }
1233 if (current == nullptr || supported == nullptr)
1234 {
1235 BMCWEB_LOG_ERROR << "GetPIDValues: thermal mode "
1236 "iface invalid "
1237 << path;
1238 messages::internalError(self->asyncResp->res);
1239 return;
1240 }
1241 self->currentProfile = *current;
1242 self->supportedProfiles = *supported;
1243 },
1244 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1245 thermalModeIface);
1246 },
1247 "xyz.openbmc_project.ObjectMapper",
1248 "/xyz/openbmc_project/object_mapper",
1249 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
1250 std::array<const char*, 1>{thermalModeIface});
1251 }
1252
1253 ~GetPIDValues()
1254 {
1255 if (asyncResp->res.result() != boost::beast::http::status::ok)
1256 {
1257 return;
1258 }
1259 // create map of <connection, path to objMgr>>
1260 boost::container::flat_map<std::string, std::string> objectMgrPaths;
1261 boost::container::flat_set<std::string> calledConnections;
1262 for (const auto& pathGroup : subtree)
1263 {
1264 for (const auto& connectionGroup : pathGroup.second)
1265 {
1266 auto findConnection =
1267 calledConnections.find(connectionGroup.first);
1268 if (findConnection != calledConnections.end())
1269 {
1270 break;
1271 }
1272 for (const std::string& interface : connectionGroup.second)
1273 {
1274 if (interface == objectManagerIface)
1275 {
1276 objectMgrPaths[connectionGroup.first] = pathGroup.first;
1277 }
1278 // this list is alphabetical, so we
1279 // should have found the objMgr by now
1280 if (interface == pidConfigurationIface ||
1281 interface == pidZoneConfigurationIface ||
1282 interface == stepwiseConfigurationIface)
1283 {
1284 auto findObjMgr =
1285 objectMgrPaths.find(connectionGroup.first);
1286 if (findObjMgr == objectMgrPaths.end())
1287 {
1288 BMCWEB_LOG_DEBUG << connectionGroup.first
1289 << "Has no Object Manager";
1290 continue;
1291 }
1292
1293 calledConnections.insert(connectionGroup.first);
1294
1295 asyncPopulatePid(findObjMgr->first, findObjMgr->second,
1296 currentProfile, supportedProfiles,
1297 asyncResp);
1298 break;
1299 }
1300 }
1301 }
1302 }
1303 }
1304
1305 std::vector<std::string> supportedProfiles;
1306 std::string currentProfile;
1307 crow::openbmc_mapper::GetSubTreeType subtree;
zhanghch058d1b46d2021-04-01 11:18:24 +08001308 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
James Feist73df0db2019-03-25 15:29:35 -07001309};
1310
1311struct SetPIDValues : std::enable_shared_from_this<SetPIDValues>
1312{
1313
zhanghch058d1b46d2021-04-01 11:18:24 +08001314 SetPIDValues(const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn,
James Feist73df0db2019-03-25 15:29:35 -07001315 nlohmann::json& data) :
Ed Tanous271584a2019-07-09 16:24:22 -07001316 asyncResp(asyncRespIn)
James Feist73df0db2019-03-25 15:29:35 -07001317 {
1318
1319 std::optional<nlohmann::json> pidControllers;
1320 std::optional<nlohmann::json> fanControllers;
1321 std::optional<nlohmann::json> fanZones;
1322 std::optional<nlohmann::json> stepwiseControllers;
1323
1324 if (!redfish::json_util::readJson(
1325 data, asyncResp->res, "PidControllers", pidControllers,
1326 "FanControllers", fanControllers, "FanZones", fanZones,
1327 "StepwiseControllers", stepwiseControllers, "Profile", profile))
1328 {
Ed Tanous71f52d92021-02-19 08:51:17 -08001329 BMCWEB_LOG_ERROR
1330 << "Illegal Property "
1331 << data.dump(2, ' ', true,
1332 nlohmann::json::error_handler_t::replace);
James Feist73df0db2019-03-25 15:29:35 -07001333 return;
1334 }
1335 configuration.emplace_back("PidControllers", std::move(pidControllers));
1336 configuration.emplace_back("FanControllers", std::move(fanControllers));
1337 configuration.emplace_back("FanZones", std::move(fanZones));
1338 configuration.emplace_back("StepwiseControllers",
1339 std::move(stepwiseControllers));
1340 }
1341 void run()
1342 {
1343 if (asyncResp->res.result() != boost::beast::http::status::ok)
1344 {
1345 return;
1346 }
1347
1348 std::shared_ptr<SetPIDValues> self = shared_from_this();
1349
1350 // todo(james): might make sense to do a mapper call here if this
1351 // interface gets more traction
1352 crow::connections::systemBus->async_method_call(
1353 [self](const boost::system::error_code ec,
Ed Tanous271584a2019-07-09 16:24:22 -07001354 dbus::utility::ManagedObjectType& mObj) {
James Feist73df0db2019-03-25 15:29:35 -07001355 if (ec)
1356 {
1357 BMCWEB_LOG_ERROR << "Error communicating to Entity Manager";
1358 messages::internalError(self->asyncResp->res);
1359 return;
1360 }
James Feiste69d9de2020-02-07 12:23:27 -08001361 const std::array<const char*, 3> configurations = {
1362 pidConfigurationIface, pidZoneConfigurationIface,
1363 stepwiseConfigurationIface};
1364
James Feist14b0b8d2020-02-12 11:52:07 -08001365 for (const auto& [path, object] : mObj)
James Feiste69d9de2020-02-07 12:23:27 -08001366 {
James Feist14b0b8d2020-02-12 11:52:07 -08001367 for (const auto& [interface, _] : object)
James Feiste69d9de2020-02-07 12:23:27 -08001368 {
1369 if (std::find(configurations.begin(),
1370 configurations.end(),
1371 interface) != configurations.end())
1372 {
James Feist14b0b8d2020-02-12 11:52:07 -08001373 self->objectCount++;
James Feiste69d9de2020-02-07 12:23:27 -08001374 break;
1375 }
1376 }
James Feiste69d9de2020-02-07 12:23:27 -08001377 }
Ed Tanous271584a2019-07-09 16:24:22 -07001378 self->managedObj = std::move(mObj);
James Feist73df0db2019-03-25 15:29:35 -07001379 },
1380 "xyz.openbmc_project.EntityManager", "/", objectManagerIface,
1381 "GetManagedObjects");
1382
1383 // at the same time get the profile information
1384 crow::connections::systemBus->async_method_call(
1385 [self](const boost::system::error_code ec,
1386 const crow::openbmc_mapper::GetSubTreeType& subtree) {
1387 if (ec || subtree.empty())
1388 {
1389 return;
1390 }
1391 if (subtree[0].second.empty())
1392 {
1393 // invalid mapper response, should never happen
1394 BMCWEB_LOG_ERROR << "SetPIDValues: Mapper Error";
1395 messages::internalError(self->asyncResp->res);
1396 return;
1397 }
1398
1399 const std::string& path = subtree[0].first;
1400 const std::string& owner = subtree[0].second[0].first;
1401 crow::connections::systemBus->async_method_call(
1402 [self, path, owner](
Ed Tanouscb13a392020-07-25 19:02:03 +00001403 const boost::system::error_code ec2,
James Feist73df0db2019-03-25 15:29:35 -07001404 const boost::container::flat_map<
1405 std::string, std::variant<std::vector<std::string>,
Ed Tanous271584a2019-07-09 16:24:22 -07001406 std::string>>& r) {
Ed Tanouscb13a392020-07-25 19:02:03 +00001407 if (ec2)
James Feist73df0db2019-03-25 15:29:35 -07001408 {
1409 BMCWEB_LOG_ERROR << "SetPIDValues: Can't get "
1410 "thermalModeIface "
1411 << path;
1412 messages::internalError(self->asyncResp->res);
1413 return;
1414 }
Ed Tanous271584a2019-07-09 16:24:22 -07001415 const std::string* current = nullptr;
1416 const std::vector<std::string>* supported = nullptr;
1417 for (auto& [key, value] : r)
James Feist73df0db2019-03-25 15:29:35 -07001418 {
1419 if (key == "Current")
1420 {
1421 current = std::get_if<std::string>(&value);
1422 if (current == nullptr)
1423 {
1424 BMCWEB_LOG_ERROR
1425 << "SetPIDValues: thermal mode "
1426 "iface invalid "
1427 << path;
1428 messages::internalError(
1429 self->asyncResp->res);
1430 return;
1431 }
1432 }
1433 if (key == "Supported")
1434 {
1435 supported =
1436 std::get_if<std::vector<std::string>>(
1437 &value);
1438 if (supported == nullptr)
1439 {
1440 BMCWEB_LOG_ERROR
1441 << "SetPIDValues: thermal mode "
1442 "iface invalid"
1443 << path;
1444 messages::internalError(
1445 self->asyncResp->res);
1446 return;
1447 }
1448 }
1449 }
1450 if (current == nullptr || supported == nullptr)
1451 {
1452 BMCWEB_LOG_ERROR << "SetPIDValues: thermal mode "
1453 "iface invalid "
1454 << path;
1455 messages::internalError(self->asyncResp->res);
1456 return;
1457 }
1458 self->currentProfile = *current;
1459 self->supportedProfiles = *supported;
1460 self->profileConnection = owner;
1461 self->profilePath = path;
1462 },
1463 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1464 thermalModeIface);
1465 },
1466 "xyz.openbmc_project.ObjectMapper",
1467 "/xyz/openbmc_project/object_mapper",
1468 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
1469 std::array<const char*, 1>{thermalModeIface});
1470 }
1471 ~SetPIDValues()
1472 {
1473 if (asyncResp->res.result() != boost::beast::http::status::ok)
1474 {
1475 return;
1476 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001477 std::shared_ptr<bmcweb::AsyncResp> response = asyncResp;
James Feist73df0db2019-03-25 15:29:35 -07001478 if (profile)
1479 {
1480 if (std::find(supportedProfiles.begin(), supportedProfiles.end(),
1481 *profile) == supportedProfiles.end())
1482 {
1483 messages::actionParameterUnknown(response->res, "Profile",
1484 *profile);
1485 return;
1486 }
1487 currentProfile = *profile;
1488 crow::connections::systemBus->async_method_call(
1489 [response](const boost::system::error_code ec) {
1490 if (ec)
1491 {
1492 BMCWEB_LOG_ERROR << "Error patching profile" << ec;
1493 messages::internalError(response->res);
1494 }
1495 },
1496 profileConnection, profilePath,
1497 "org.freedesktop.DBus.Properties", "Set", thermalModeIface,
1498 "Current", std::variant<std::string>(*profile));
1499 }
1500
1501 for (auto& containerPair : configuration)
1502 {
1503 auto& container = containerPair.second;
1504 if (!container)
1505 {
1506 continue;
1507 }
James Feist6ee7f772020-02-06 16:25:27 -08001508 BMCWEB_LOG_DEBUG << *container;
1509
James Feist73df0db2019-03-25 15:29:35 -07001510 std::string& type = containerPair.first;
1511
1512 for (nlohmann::json::iterator it = container->begin();
Manojkiran Eda17a897d2020-09-12 15:31:58 +05301513 it != container->end(); ++it)
James Feist73df0db2019-03-25 15:29:35 -07001514 {
1515 const auto& name = it.key();
James Feist6ee7f772020-02-06 16:25:27 -08001516 BMCWEB_LOG_DEBUG << "looking for " << name;
1517
James Feist73df0db2019-03-25 15:29:35 -07001518 auto pathItr =
1519 std::find_if(managedObj.begin(), managedObj.end(),
1520 [&name](const auto& obj) {
1521 return boost::algorithm::ends_with(
1522 obj.first.str, "/" + name);
1523 });
1524 boost::container::flat_map<std::string,
1525 dbus::utility::DbusVariantType>
1526 output;
1527
1528 output.reserve(16); // The pid interface length
1529
1530 // determines if we're patching entity-manager or
1531 // creating a new object
1532 bool createNewObject = (pathItr == managedObj.end());
James Feist6ee7f772020-02-06 16:25:27 -08001533 BMCWEB_LOG_DEBUG << "Found = " << !createNewObject;
1534
James Feist73df0db2019-03-25 15:29:35 -07001535 std::string iface;
1536 if (type == "PidControllers" || type == "FanControllers")
1537 {
1538 iface = pidConfigurationIface;
1539 if (!createNewObject &&
1540 pathItr->second.find(pidConfigurationIface) ==
1541 pathItr->second.end())
1542 {
1543 createNewObject = true;
1544 }
1545 }
1546 else if (type == "FanZones")
1547 {
1548 iface = pidZoneConfigurationIface;
1549 if (!createNewObject &&
1550 pathItr->second.find(pidZoneConfigurationIface) ==
1551 pathItr->second.end())
1552 {
1553
1554 createNewObject = true;
1555 }
1556 }
1557 else if (type == "StepwiseControllers")
1558 {
1559 iface = stepwiseConfigurationIface;
1560 if (!createNewObject &&
1561 pathItr->second.find(stepwiseConfigurationIface) ==
1562 pathItr->second.end())
1563 {
1564 createNewObject = true;
1565 }
1566 }
James Feist6ee7f772020-02-06 16:25:27 -08001567
1568 if (createNewObject && it.value() == nullptr)
1569 {
Gunnar Mills4e0453b2020-07-08 14:00:30 -05001570 // can't delete a non-existent object
James Feist6ee7f772020-02-06 16:25:27 -08001571 messages::invalidObject(response->res, name);
1572 continue;
1573 }
1574
1575 std::string path;
1576 if (pathItr != managedObj.end())
1577 {
1578 path = pathItr->first.str;
1579 }
1580
James Feist73df0db2019-03-25 15:29:35 -07001581 BMCWEB_LOG_DEBUG << "Create new = " << createNewObject << "\n";
James Feiste69d9de2020-02-07 12:23:27 -08001582
1583 // arbitrary limit to avoid attacks
1584 constexpr const size_t controllerLimit = 500;
James Feist14b0b8d2020-02-12 11:52:07 -08001585 if (createNewObject && objectCount >= controllerLimit)
James Feiste69d9de2020-02-07 12:23:27 -08001586 {
1587 messages::resourceExhaustion(response->res, type);
1588 continue;
1589 }
1590
James Feist73df0db2019-03-25 15:29:35 -07001591 output["Name"] = boost::replace_all_copy(name, "_", " ");
1592
1593 std::string chassis;
1594 CreatePIDRet ret = createPidInterface(
James Feist6ee7f772020-02-06 16:25:27 -08001595 response, type, it, path, managedObj, createNewObject,
1596 output, chassis, currentProfile);
James Feist73df0db2019-03-25 15:29:35 -07001597 if (ret == CreatePIDRet::fail)
1598 {
1599 return;
1600 }
Ed Tanous3174e4d2020-10-07 11:41:22 -07001601 if (ret == CreatePIDRet::del)
James Feist73df0db2019-03-25 15:29:35 -07001602 {
1603 continue;
1604 }
1605
1606 if (!createNewObject)
1607 {
1608 for (const auto& property : output)
1609 {
1610 crow::connections::systemBus->async_method_call(
1611 [response,
1612 propertyName{std::string(property.first)}](
1613 const boost::system::error_code ec) {
1614 if (ec)
1615 {
1616 BMCWEB_LOG_ERROR << "Error patching "
1617 << propertyName << ": "
1618 << ec;
1619 messages::internalError(response->res);
1620 return;
1621 }
1622 messages::success(response->res);
1623 },
James Feist6ee7f772020-02-06 16:25:27 -08001624 "xyz.openbmc_project.EntityManager", path,
James Feist73df0db2019-03-25 15:29:35 -07001625 "org.freedesktop.DBus.Properties", "Set", iface,
1626 property.first, property.second);
1627 }
1628 }
1629 else
1630 {
1631 if (chassis.empty())
1632 {
1633 BMCWEB_LOG_ERROR << "Failed to get chassis from config";
1634 messages::invalidObject(response->res, name);
1635 return;
1636 }
1637
1638 bool foundChassis = false;
1639 for (const auto& obj : managedObj)
1640 {
1641 if (boost::algorithm::ends_with(obj.first.str, chassis))
1642 {
1643 chassis = obj.first.str;
1644 foundChassis = true;
1645 break;
1646 }
1647 }
1648 if (!foundChassis)
1649 {
1650 BMCWEB_LOG_ERROR << "Failed to find chassis on dbus";
1651 messages::resourceMissingAtURI(
1652 response->res, "/redfish/v1/Chassis/" + chassis);
1653 return;
1654 }
1655
1656 crow::connections::systemBus->async_method_call(
1657 [response](const boost::system::error_code ec) {
1658 if (ec)
1659 {
1660 BMCWEB_LOG_ERROR << "Error Adding Pid Object "
1661 << ec;
1662 messages::internalError(response->res);
1663 return;
1664 }
1665 messages::success(response->res);
1666 },
1667 "xyz.openbmc_project.EntityManager", chassis,
1668 "xyz.openbmc_project.AddObject", "AddObject", output);
1669 }
1670 }
1671 }
1672 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001673 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
James Feist73df0db2019-03-25 15:29:35 -07001674 std::vector<std::pair<std::string, std::optional<nlohmann::json>>>
1675 configuration;
1676 std::optional<std::string> profile;
1677 dbus::utility::ManagedObjectType managedObj;
1678 std::vector<std::string> supportedProfiles;
1679 std::string currentProfile;
1680 std::string profileConnection;
1681 std::string profilePath;
James Feist14b0b8d2020-02-12 11:52:07 -08001682 size_t objectCount = 0;
James Feist73df0db2019-03-25 15:29:35 -07001683};
James Feist83ff9ab2018-08-31 10:18:24 -07001684
SunnySrivastava1984071d8fd2020-10-28 02:20:30 -05001685/**
1686 * @brief Retrieves BMC manager location data over DBus
1687 *
1688 * @param[in] aResp Shared pointer for completing asynchronous calls
1689 * @param[in] connectionName - service name
1690 * @param[in] path - object path
1691 * @return none
1692 */
zhanghch058d1b46d2021-04-01 11:18:24 +08001693inline void getLocation(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
SunnySrivastava1984071d8fd2020-10-28 02:20:30 -05001694 const std::string& connectionName,
1695 const std::string& path)
1696{
1697 BMCWEB_LOG_DEBUG << "Get BMC manager Location data.";
1698
1699 crow::connections::systemBus->async_method_call(
1700 [aResp](const boost::system::error_code ec,
1701 const std::variant<std::string>& property) {
1702 if (ec)
1703 {
1704 BMCWEB_LOG_DEBUG << "DBUS response error for "
1705 "Location";
1706 messages::internalError(aResp->res);
1707 return;
1708 }
1709
1710 const std::string* value = std::get_if<std::string>(&property);
1711
1712 if (value == nullptr)
1713 {
1714 // illegal value
1715 messages::internalError(aResp->res);
1716 return;
1717 }
1718
1719 aResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
1720 *value;
1721 },
1722 connectionName, path, "org.freedesktop.DBus.Properties", "Get",
1723 "xyz.openbmc_project.Inventory.Decorator."
1724 "LocationCode",
1725 "LocationCode");
1726}
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001727// avoid name collision systems.hpp
1728inline void
1729 managerGetLastResetTime(const std::shared_ptr<bmcweb::AsyncResp>& aResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001730{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001731 BMCWEB_LOG_DEBUG << "Getting Manager Last Reset Time";
Ed Tanous52cc1122020-07-18 13:51:21 -07001732
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001733 crow::connections::systemBus->async_method_call(
1734 [aResp](const boost::system::error_code ec,
1735 std::variant<uint64_t>& lastResetTime) {
1736 if (ec)
James Feist83ff9ab2018-08-31 10:18:24 -07001737 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001738 BMCWEB_LOG_DEBUG << "D-BUS response error " << ec;
Ed Tanous43b761d2019-02-13 20:10:56 -08001739 return;
1740 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001741
1742 const uint64_t* lastResetTimePtr =
1743 std::get_if<uint64_t>(&lastResetTime);
1744
1745 if (!lastResetTimePtr)
Ed Tanous43b761d2019-02-13 20:10:56 -08001746 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001747 messages::internalError(aResp->res);
Gunnar Mills4bfefa72020-07-30 13:54:29 -05001748 return;
1749 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001750 // LastRebootTime is epoch time, in milliseconds
1751 // https://github.com/openbmc/phosphor-dbus-interfaces/blob/7f9a128eb9296e926422ddc312c148b625890bb6/xyz/openbmc_project/State/BMC.interface.yaml#L19
1752 time_t lastResetTimeStamp =
1753 static_cast<time_t>(*lastResetTimePtr / 1000);
1754
1755 // Convert to ISO 8601 standard
1756 aResp->res.jsonValue["LastResetTime"] =
1757 crow::utility::getDateTime(lastResetTimeStamp);
1758 },
1759 "xyz.openbmc_project.State.BMC", "/xyz/openbmc_project/state/bmc0",
1760 "org.freedesktop.DBus.Properties", "Get",
1761 "xyz.openbmc_project.State.BMC", "LastRebootTime");
1762}
1763
1764/**
1765 * @brief Set the running firmware image
1766 *
1767 * @param[i,o] aResp - Async response object
1768 * @param[i] runningFirmwareTarget - Image to make the running image
1769 *
1770 * @return void
1771 */
1772inline void
1773 setActiveFirmwareImage(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
1774 const std::string& runningFirmwareTarget)
1775{
1776 // Get the Id from /redfish/v1/UpdateService/FirmwareInventory/<Id>
1777 std::string::size_type idPos = runningFirmwareTarget.rfind('/');
1778 if (idPos == std::string::npos)
1779 {
1780 messages::propertyValueNotInList(aResp->res, runningFirmwareTarget,
1781 "@odata.id");
1782 BMCWEB_LOG_DEBUG << "Can't parse firmware ID!";
1783 return;
1784 }
1785 idPos++;
1786 if (idPos >= runningFirmwareTarget.size())
1787 {
1788 messages::propertyValueNotInList(aResp->res, runningFirmwareTarget,
1789 "@odata.id");
1790 BMCWEB_LOG_DEBUG << "Invalid firmware ID.";
1791 return;
1792 }
1793 std::string firmwareId = runningFirmwareTarget.substr(idPos);
1794
1795 // Make sure the image is valid before setting priority
1796 crow::connections::systemBus->async_method_call(
1797 [aResp, firmwareId, runningFirmwareTarget](
1798 const boost::system::error_code ec, ManagedObjectType& subtree) {
1799 if (ec)
Gunnar Mills4bfefa72020-07-30 13:54:29 -05001800 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001801 BMCWEB_LOG_DEBUG << "D-Bus response error getting objects.";
1802 messages::internalError(aResp->res);
1803 return;
1804 }
1805
1806 if (subtree.size() == 0)
1807 {
1808 BMCWEB_LOG_DEBUG << "Can't find image!";
1809 messages::internalError(aResp->res);
1810 return;
1811 }
1812
1813 bool foundImage = false;
1814 for (auto& object : subtree)
1815 {
1816 const std::string& path =
1817 static_cast<const std::string&>(object.first);
1818 std::size_t idPos2 = path.rfind('/');
1819
1820 if (idPos2 == std::string::npos)
Gunnar Mills4bfefa72020-07-30 13:54:29 -05001821 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001822 continue;
Gunnar Mills4bfefa72020-07-30 13:54:29 -05001823 }
1824
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001825 idPos2++;
1826 if (idPos2 >= path.size())
Gunnar Mills4bfefa72020-07-30 13:54:29 -05001827 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001828 continue;
1829 }
1830
1831 if (path.substr(idPos2) == firmwareId)
1832 {
1833 foundImage = true;
1834 break;
Gunnar Mills4bfefa72020-07-30 13:54:29 -05001835 }
1836 }
Santosh Puranikaf5d60582019-03-20 18:16:36 +05301837
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001838 if (!foundImage)
1839 {
1840 messages::propertyValueNotInList(
1841 aResp->res, runningFirmwareTarget, "@odata.id");
1842 BMCWEB_LOG_DEBUG << "Invalid firmware ID.";
1843 return;
1844 }
Gunnar Mills4bf2b032020-06-23 22:28:31 -05001845
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001846 BMCWEB_LOG_DEBUG
1847 << "Setting firmware version " + firmwareId + " to priority 0.";
Gunnar Mills4bf2b032020-06-23 22:28:31 -05001848
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001849 // Only support Immediate
1850 // An addition could be a Redfish Setting like
1851 // ActiveSoftwareImageApplyTime and support OnReset
Santosh Puranikaf5d60582019-03-20 18:16:36 +05301852 crow::connections::systemBus->async_method_call(
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001853 [aResp](const boost::system::error_code ec) {
Santosh Puranikaf5d60582019-03-20 18:16:36 +05301854 if (ec)
1855 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001856 BMCWEB_LOG_DEBUG << "D-Bus response error setting.";
Santosh Puranikaf5d60582019-03-20 18:16:36 +05301857 messages::internalError(aResp->res);
1858 return;
1859 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001860 doBMCGracefulRestart(aResp);
Santosh Puranikaf5d60582019-03-20 18:16:36 +05301861 },
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001862
1863 "xyz.openbmc_project.Software.BMC.Updater",
1864 "/xyz/openbmc_project/software/" + firmwareId,
Santosh Puranikaf5d60582019-03-20 18:16:36 +05301865 "org.freedesktop.DBus.Properties", "Set",
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001866 "xyz.openbmc_project.Software.RedundancyPriority", "Priority",
1867 std::variant<uint8_t>(static_cast<uint8_t>(0)));
1868 },
1869 "xyz.openbmc_project.Software.BMC.Updater",
1870 "/xyz/openbmc_project/software", "org.freedesktop.DBus.ObjectManager",
1871 "GetManagedObjects");
1872}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001873
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001874inline void setDateTime(std::shared_ptr<bmcweb::AsyncResp> aResp,
1875 std::string datetime)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001876{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001877 BMCWEB_LOG_DEBUG << "Set date time: " << datetime;
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001878
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001879 std::stringstream stream(datetime);
1880 // Convert from ISO 8601 to boost local_time
1881 // (BMC only has time in UTC)
1882 boost::posix_time::ptime posixTime;
1883 boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
1884 // Facet gets deleted with the stringsteam
1885 auto ifc = std::make_unique<boost::local_time::local_time_input_facet>(
1886 "%Y-%m-%d %H:%M:%S%F %ZP");
1887 stream.imbue(std::locale(stream.getloc(), ifc.release()));
1888
1889 boost::local_time::local_date_time ldt(boost::local_time::not_a_date_time);
1890
1891 if (stream >> ldt)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001892 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001893 posixTime = ldt.utc_time();
1894 boost::posix_time::time_duration dur = posixTime - epoch;
1895 uint64_t durMicroSecs = static_cast<uint64_t>(dur.total_microseconds());
1896 crow::connections::systemBus->async_method_call(
1897 [aResp{std::move(aResp)}, datetime{std::move(datetime)}](
1898 const boost::system::error_code ec) {
1899 if (ec)
1900 {
1901 BMCWEB_LOG_DEBUG << "Failed to set elapsed time. "
1902 "DBUS response error "
1903 << ec;
1904 messages::internalError(aResp->res);
1905 return;
1906 }
1907 aResp->res.jsonValue["DateTime"] = datetime;
1908 },
1909 "xyz.openbmc_project.Time.Manager", "/xyz/openbmc_project/time/bmc",
1910 "org.freedesktop.DBus.Properties", "Set",
1911 "xyz.openbmc_project.Time.EpochTime", "Elapsed",
1912 std::variant<uint64_t>(durMicroSecs));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001913 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001914 else
1915 {
1916 messages::propertyValueFormatError(aResp->res, datetime, "DateTime");
1917 return;
1918 }
1919}
1920
1921inline void requestRoutesManager(App& app)
1922{
1923 std::string uuid = persistent_data::getConfig().systemUuid;
1924
1925 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/")
1926 .privileges({"Login"})
1927 .methods(boost::beast::http::verb::get)([uuid](const crow::Request&,
1928 const std::shared_ptr<
1929 bmcweb::AsyncResp>&
1930 asyncResp) {
1931 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Managers/bmc";
1932 asyncResp->res.jsonValue["@odata.type"] =
1933 "#Manager.v1_11_0.Manager";
1934 asyncResp->res.jsonValue["Id"] = "bmc";
1935 asyncResp->res.jsonValue["Name"] = "OpenBmc Manager";
1936 asyncResp->res.jsonValue["Description"] =
1937 "Baseboard Management Controller";
1938 asyncResp->res.jsonValue["PowerState"] = "On";
1939 asyncResp->res.jsonValue["Status"] = {{"State", "Enabled"},
1940 {"Health", "OK"}};
1941 asyncResp->res.jsonValue["ManagerType"] = "BMC";
1942 asyncResp->res.jsonValue["UUID"] = systemd_utils::getUuid();
1943 asyncResp->res.jsonValue["ServiceEntryPointUUID"] = uuid;
1944 asyncResp->res.jsonValue["Model"] =
1945 "OpenBmc"; // TODO(ed), get model
1946
1947 asyncResp->res.jsonValue["LogServices"] = {
1948 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices"}};
1949
1950 asyncResp->res.jsonValue["NetworkProtocol"] = {
1951 {"@odata.id", "/redfish/v1/Managers/bmc/NetworkProtocol"}};
1952
1953 asyncResp->res.jsonValue["EthernetInterfaces"] = {
1954 {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces"}};
1955
1956#ifdef BMCWEB_ENABLE_VM_NBDPROXY
1957 asyncResp->res.jsonValue["VirtualMedia"] = {
1958 {"@odata.id", "/redfish/v1/Managers/bmc/VirtualMedia"}};
1959#endif // BMCWEB_ENABLE_VM_NBDPROXY
1960
1961 // default oem data
1962 nlohmann::json& oem = asyncResp->res.jsonValue["Oem"];
1963 nlohmann::json& oemOpenbmc = oem["OpenBmc"];
1964 oem["@odata.type"] = "#OemManager.Oem";
1965 oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem";
1966 oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc";
1967 oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc";
1968 oemOpenbmc["Certificates"] = {
1969 {"@odata.id",
1970 "/redfish/v1/Managers/bmc/Truststore/Certificates"}};
1971
1972 // Manager.Reset (an action) can be many values, OpenBMC only
1973 // supports BMC reboot.
1974 nlohmann::json& managerReset =
1975 asyncResp->res.jsonValue["Actions"]["#Manager.Reset"];
1976 managerReset["target"] =
1977 "/redfish/v1/Managers/bmc/Actions/Manager.Reset";
1978 managerReset["@Redfish.ActionInfo"] =
1979 "/redfish/v1/Managers/bmc/ResetActionInfo";
1980
1981 // ResetToDefaults (Factory Reset) has values like
1982 // PreserveNetworkAndUsers and PreserveNetwork that aren't supported
1983 // on OpenBMC
1984 nlohmann::json& resetToDefaults =
1985 asyncResp->res.jsonValue["Actions"]["#Manager.ResetToDefaults"];
1986 resetToDefaults["target"] =
1987 "/redfish/v1/Managers/bmc/Actions/Manager.ResetToDefaults";
1988 resetToDefaults["ResetType@Redfish.AllowableValues"] = {"ResetAll"};
1989
1990 asyncResp->res.jsonValue["DateTime"] = crow::utility::dateTimeNow();
1991
1992 // Fill in SerialConsole info
1993 asyncResp->res.jsonValue["SerialConsole"]["ServiceEnabled"] = true;
1994 asyncResp->res.jsonValue["SerialConsole"]["MaxConcurrentSessions"] =
1995 15;
1996 asyncResp->res.jsonValue["SerialConsole"]["ConnectTypesSupported"] =
1997 {"IPMI", "SSH"};
1998#ifdef BMCWEB_ENABLE_KVM
1999 // Fill in GraphicalConsole info
2000 asyncResp->res.jsonValue["GraphicalConsole"]["ServiceEnabled"] =
2001 true;
2002 asyncResp->res
2003 .jsonValue["GraphicalConsole"]["MaxConcurrentSessions"] = 4;
2004 asyncResp->res.jsonValue["GraphicalConsole"]
2005 ["ConnectTypesSupported"] = {"KVMIP"};
2006#endif // BMCWEB_ENABLE_KVM
2007
2008 asyncResp->res.jsonValue["Links"]["ManagerForServers@odata.count"] =
2009 1;
2010 asyncResp->res.jsonValue["Links"]["ManagerForServers"] = {
2011 {{"@odata.id", "/redfish/v1/Systems/system"}}};
2012
2013 auto health = std::make_shared<HealthPopulate>(asyncResp);
2014 health->isManagersHealth = true;
2015 health->populate();
2016
2017 fw_util::populateFirmwareInformation(asyncResp, fw_util::bmcPurpose,
2018 "FirmwareVersion", true);
2019
2020 managerGetLastResetTime(asyncResp);
2021
2022 auto pids = std::make_shared<GetPIDValues>(asyncResp);
2023 pids->run();
2024
2025 getMainChassisId(
2026 asyncResp, [](const std::string& chassisId,
2027 const std::shared_ptr<bmcweb::AsyncResp>& aRsp) {
2028 aRsp->res
2029 .jsonValue["Links"]["ManagerForChassis@odata.count"] =
2030 1;
2031 aRsp->res.jsonValue["Links"]["ManagerForChassis"] = {
2032 {{"@odata.id", "/redfish/v1/Chassis/" + chassisId}}};
2033 aRsp->res.jsonValue["Links"]["ManagerInChassis"] = {
2034 {"@odata.id", "/redfish/v1/Chassis/" + chassisId}};
2035 });
2036
2037 static bool started = false;
2038
2039 if (!started)
2040 {
2041 crow::connections::systemBus->async_method_call(
2042 [asyncResp](const boost::system::error_code ec,
2043 const std::variant<double>& resp) {
2044 if (ec)
2045 {
2046 BMCWEB_LOG_ERROR << "Error while getting progress";
2047 messages::internalError(asyncResp->res);
2048 return;
2049 }
2050 const double* val = std::get_if<double>(&resp);
2051 if (val == nullptr)
2052 {
2053 BMCWEB_LOG_ERROR
2054 << "Invalid response while getting progress";
2055 messages::internalError(asyncResp->res);
2056 return;
2057 }
2058 if (*val < 1.0)
2059 {
2060 asyncResp->res.jsonValue["Status"]["State"] =
2061 "Starting";
2062 started = true;
2063 }
2064 },
2065 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
2066 "org.freedesktop.DBus.Properties", "Get",
2067 "org.freedesktop.systemd1.Manager", "Progress");
2068 }
2069
2070 crow::connections::systemBus->async_method_call(
2071 [asyncResp](
2072 const boost::system::error_code ec,
2073 const std::vector<
2074 std::pair<std::string,
2075 std::vector<std::pair<
2076 std::string, std::vector<std::string>>>>>&
2077 subtree) {
2078 if (ec)
2079 {
2080 BMCWEB_LOG_DEBUG
2081 << "D-Bus response error on GetSubTree " << ec;
2082 return;
2083 }
2084 if (subtree.size() == 0)
2085 {
2086 BMCWEB_LOG_DEBUG << "Can't find bmc D-Bus object!";
2087 return;
2088 }
2089 // Assume only 1 bmc D-Bus object
2090 // Throw an error if there is more than 1
2091 if (subtree.size() > 1)
2092 {
2093 BMCWEB_LOG_DEBUG
2094 << "Found more than 1 bmc D-Bus object!";
2095 messages::internalError(asyncResp->res);
2096 return;
2097 }
2098
2099 if (subtree[0].first.empty() ||
2100 subtree[0].second.size() != 1)
2101 {
2102 BMCWEB_LOG_DEBUG << "Error getting bmc D-Bus object!";
2103 messages::internalError(asyncResp->res);
2104 return;
2105 }
2106
2107 const std::string& path = subtree[0].first;
2108 const std::string& connectionName =
2109 subtree[0].second[0].first;
2110
2111 for (const auto& interfaceName :
2112 subtree[0].second[0].second)
2113 {
2114 if (interfaceName ==
2115 "xyz.openbmc_project.Inventory.Decorator.Asset")
2116 {
2117 crow::connections::systemBus->async_method_call(
2118 [asyncResp](
2119 const boost::system::error_code ec,
2120 const std::vector<
2121 std::pair<std::string,
2122 std::variant<std::string>>>&
2123 propertiesList) {
2124 if (ec)
2125 {
2126 BMCWEB_LOG_DEBUG
2127 << "Can't get bmc asset!";
2128 return;
2129 }
2130 for (const std::pair<
2131 std::string,
2132 std::variant<std::string>>&
2133 property : propertiesList)
2134 {
2135 const std::string& propertyName =
2136 property.first;
2137
2138 if ((propertyName == "PartNumber") ||
2139 (propertyName == "SerialNumber") ||
2140 (propertyName == "Manufacturer") ||
2141 (propertyName == "Model") ||
2142 (propertyName == "SparePartNumber"))
2143 {
2144 const std::string* value =
2145 std::get_if<std::string>(
2146 &property.second);
2147 if (value == nullptr)
2148 {
2149 // illegal property
2150 messages::internalError(
2151 asyncResp->res);
2152 return;
2153 }
2154 asyncResp->res
2155 .jsonValue[propertyName] =
2156 *value;
2157 }
2158 }
2159 },
2160 connectionName, path,
2161 "org.freedesktop.DBus.Properties", "GetAll",
2162 "xyz.openbmc_project.Inventory.Decorator."
2163 "Asset");
2164 }
2165 else if (interfaceName ==
2166 "xyz.openbmc_project.Inventory."
2167 "Decorator.LocationCode")
2168 {
2169 getLocation(asyncResp, connectionName, path);
2170 }
2171 }
2172 },
2173 "xyz.openbmc_project.ObjectMapper",
2174 "/xyz/openbmc_project/object_mapper",
2175 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
2176 "/xyz/openbmc_project/inventory", int32_t(0),
2177 std::array<const char*, 1>{
2178 "xyz.openbmc_project.Inventory.Item.Bmc"});
2179 });
2180
2181 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/")
2182 .privileges({"ConfigureManager"})
2183 .methods(
2184 boost::beast::http::verb::
2185 patch)([](const crow::Request& req,
2186 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2187 std::optional<nlohmann::json> oem;
2188 std::optional<nlohmann::json> links;
2189 std::optional<std::string> datetime;
2190
2191 if (!json_util::readJson(req, asyncResp->res, "Oem", oem,
2192 "DateTime", datetime, "Links", links))
2193 {
2194 return;
2195 }
2196
2197 if (oem)
2198 {
2199 std::optional<nlohmann::json> openbmc;
2200 if (!redfish::json_util::readJson(*oem, asyncResp->res,
2201 "OpenBmc", openbmc))
2202 {
2203 BMCWEB_LOG_ERROR
2204 << "Illegal Property "
2205 << oem->dump(2, ' ', true,
2206 nlohmann::json::error_handler_t::replace);
2207 return;
2208 }
2209 if (openbmc)
2210 {
2211 std::optional<nlohmann::json> fan;
2212 if (!redfish::json_util::readJson(*openbmc, asyncResp->res,
2213 "Fan", fan))
2214 {
2215 BMCWEB_LOG_ERROR
2216 << "Illegal Property "
2217 << openbmc->dump(
2218 2, ' ', true,
2219 nlohmann::json::error_handler_t::replace);
2220 return;
2221 }
2222 if (fan)
2223 {
2224 auto pid =
2225 std::make_shared<SetPIDValues>(asyncResp, *fan);
2226 pid->run();
2227 }
2228 }
2229 }
2230 if (links)
2231 {
2232 std::optional<nlohmann::json> activeSoftwareImage;
2233 if (!redfish::json_util::readJson(*links, asyncResp->res,
2234 "ActiveSoftwareImage",
2235 activeSoftwareImage))
2236 {
2237 return;
2238 }
2239 if (activeSoftwareImage)
2240 {
2241 std::optional<std::string> odataId;
2242 if (!json_util::readJson(*activeSoftwareImage,
2243 asyncResp->res, "@odata.id",
2244 odataId))
2245 {
2246 return;
2247 }
2248
2249 if (odataId)
2250 {
2251 setActiveFirmwareImage(asyncResp, *odataId);
2252 }
2253 }
2254 }
2255 if (datetime)
2256 {
2257 setDateTime(asyncResp, std::move(*datetime));
2258 }
2259 });
2260}
2261
2262inline void requestRoutesManagerCollection(App& app)
2263{
2264 BMCWEB_ROUTE(app, "/redfish/v1/Managers/")
2265 .privileges({"Login"})
2266 .methods(boost::beast::http::verb::get)(
2267 [](const crow::Request&,
2268 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2269 // Collections don't include the static data added by SubRoute
2270 // because it has a duplicate entry for members
2271 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
2272 asyncResp->res.jsonValue["@odata.type"] =
2273 "#ManagerCollection.ManagerCollection";
2274 asyncResp->res.jsonValue["Name"] = "Manager Collection";
2275 asyncResp->res.jsonValue["Members@odata.count"] = 1;
2276 asyncResp->res.jsonValue["Members"] = {
2277 {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
2278 });
2279}
Ed Tanous1abe55e2018-09-05 08:30:59 -07002280} // namespace redfish