blob: a98ff223876706a66ee0b5fc935bac70850de3d9 [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
Sui Chena51fc2d2022-07-14 17:21:53 -070018#include "app.hpp"
19#include "dbus_utility.hpp"
James Feistb49ac872019-05-21 15:12:01 -070020#include "health.hpp"
Sui Chena51fc2d2022-07-14 17:21:53 -070021#include "query.hpp"
Jennifer Leec5d03ff2019-03-08 15:42:58 -080022#include "redfish_util.hpp"
Sui Chena51fc2d2022-07-14 17:21:53 -070023#include "registries/privilege_registry.hpp"
Krzysztof Grobelnyfac6e532022-08-04 12:42:45 +020024#include "utils/dbus_utils.hpp"
Sui Chena51fc2d2022-07-14 17:21:53 -070025#include "utils/sw_utils.hpp"
26#include "utils/systemd_utils.hpp"
Borawski.Lukasz9c3106852018-02-09 15:24:22 +010027
Santosh Puranikaf5d60582019-03-20 18:16:36 +053028#include <boost/date_time.hpp>
Krzysztof Grobelnyfac6e532022-08-04 12:42:45 +020029#include <sdbusplus/asio/property.hpp>
30#include <sdbusplus/unpack_properties.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050031
Ed Tanousa170f272022-06-30 21:53:27 -070032#include <algorithm>
Gunnar Mills4bfefa72020-07-30 13:54:29 -050033#include <cstdint>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050034#include <memory>
35#include <sstream>
Ed Tanousabf2add2019-01-22 16:40:12 -080036#include <variant>
James Feist5b4aa862018-08-16 14:07:01 -070037
Ed Tanous1abe55e2018-09-05 08:30:59 -070038namespace redfish
39{
Jennifer Leeed5befb2018-08-10 11:29:45 -070040
41/**
Gunnar Mills2a5c4402020-05-19 09:07:24 -050042 * Function reboots the BMC.
43 *
44 * @param[in] asyncResp - Shared pointer for completing asynchronous calls
Jennifer Leeed5befb2018-08-10 11:29:45 -070045 */
zhanghch058d1b46d2021-04-01 11:18:24 +080046inline void
47 doBMCGracefulRestart(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Gunnar Mills2a5c4402020-05-19 09:07:24 -050048{
49 const char* processName = "xyz.openbmc_project.State.BMC";
50 const char* objectPath = "/xyz/openbmc_project/state/bmc0";
51 const char* interfaceName = "xyz.openbmc_project.State.BMC";
52 const std::string& propertyValue =
53 "xyz.openbmc_project.State.BMC.Transition.Reboot";
54 const char* destProperty = "RequestedBMCTransition";
55
56 // Create the D-Bus variant for D-Bus call.
Ed Tanous168e20c2021-12-13 14:39:53 -080057 dbus::utility::DbusVariantType dbusPropertyValue(propertyValue);
Gunnar Mills2a5c4402020-05-19 09:07:24 -050058
59 crow::connections::systemBus->async_method_call(
60 [asyncResp](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -070061 // Use "Set" method to set the property value.
62 if (ec)
63 {
64 BMCWEB_LOG_DEBUG << "[Set] Bad D-Bus request error: " << ec;
65 messages::internalError(asyncResp->res);
66 return;
67 }
Gunnar Mills2a5c4402020-05-19 09:07:24 -050068
Ed Tanous002d39b2022-05-31 08:59:27 -070069 messages::success(asyncResp->res);
Gunnar Mills2a5c4402020-05-19 09:07:24 -050070 },
71 processName, objectPath, "org.freedesktop.DBus.Properties", "Set",
72 interfaceName, destProperty, dbusPropertyValue);
73}
74
zhanghch058d1b46d2021-04-01 11:18:24 +080075inline void
76 doBMCForceRestart(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Jayaprakash Mutyalaf92af382020-06-16 23:29:41 +000077{
78 const char* processName = "xyz.openbmc_project.State.BMC";
79 const char* objectPath = "/xyz/openbmc_project/state/bmc0";
80 const char* interfaceName = "xyz.openbmc_project.State.BMC";
81 const std::string& propertyValue =
82 "xyz.openbmc_project.State.BMC.Transition.HardReboot";
83 const char* destProperty = "RequestedBMCTransition";
84
85 // Create the D-Bus variant for D-Bus call.
Ed Tanous168e20c2021-12-13 14:39:53 -080086 dbus::utility::DbusVariantType dbusPropertyValue(propertyValue);
Jayaprakash Mutyalaf92af382020-06-16 23:29:41 +000087
88 crow::connections::systemBus->async_method_call(
89 [asyncResp](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -070090 // Use "Set" method to set the property value.
91 if (ec)
92 {
93 BMCWEB_LOG_DEBUG << "[Set] Bad D-Bus request error: " << ec;
94 messages::internalError(asyncResp->res);
95 return;
96 }
Jayaprakash Mutyalaf92af382020-06-16 23:29:41 +000097
Ed Tanous002d39b2022-05-31 08:59:27 -070098 messages::success(asyncResp->res);
Jayaprakash Mutyalaf92af382020-06-16 23:29:41 +000099 },
100 processName, objectPath, "org.freedesktop.DBus.Properties", "Set",
101 interfaceName, destProperty, dbusPropertyValue);
102}
103
Gunnar Mills2a5c4402020-05-19 09:07:24 -0500104/**
105 * ManagerResetAction class supports the POST method for the Reset (reboot)
106 * action.
107 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700108inline void requestRoutesManagerResetAction(App& app)
Jennifer Leeed5befb2018-08-10 11:29:45 -0700109{
Jennifer Leeed5befb2018-08-10 11:29:45 -0700110 /**
Jennifer Leeed5befb2018-08-10 11:29:45 -0700111 * Function handles POST method request.
Gunnar Mills2a5c4402020-05-19 09:07:24 -0500112 * Analyzes POST body before sending Reset (Reboot) request data to D-Bus.
Jayaprakash Mutyalaf92af382020-06-16 23:29:41 +0000113 * OpenBMC supports ResetType "GracefulRestart" and "ForceRestart".
Jennifer Leeed5befb2018-08-10 11:29:45 -0700114 */
Jennifer Leeed5befb2018-08-10 11:29:45 -0700115
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700116 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Actions/Manager.Reset/")
Ed Tanoused398212021-06-09 17:05:54 -0700117 .privileges(redfish::privileges::postManager)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700118 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700119 [&app](const crow::Request& req,
120 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000121 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700122 {
123 return;
124 }
125 BMCWEB_LOG_DEBUG << "Post Manager Reset.";
Gunnar Mills2a5c4402020-05-19 09:07:24 -0500126
Ed Tanous002d39b2022-05-31 08:59:27 -0700127 std::string resetType;
Jennifer Leeed5befb2018-08-10 11:29:45 -0700128
Ed Tanous002d39b2022-05-31 08:59:27 -0700129 if (!json_util::readJsonAction(req, asyncResp->res, "ResetType",
130 resetType))
131 {
132 return;
133 }
Gunnar Mills2a5c4402020-05-19 09:07:24 -0500134
Ed Tanous002d39b2022-05-31 08:59:27 -0700135 if (resetType == "GracefulRestart")
136 {
137 BMCWEB_LOG_DEBUG << "Proceeding with " << resetType;
138 doBMCGracefulRestart(asyncResp);
139 return;
140 }
141 if (resetType == "ForceRestart")
142 {
143 BMCWEB_LOG_DEBUG << "Proceeding with " << resetType;
144 doBMCForceRestart(asyncResp);
145 return;
146 }
147 BMCWEB_LOG_DEBUG << "Invalid property value for ResetType: "
148 << resetType;
149 messages::actionParameterNotSupported(asyncResp->res, resetType,
150 "ResetType");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700151
Ed Tanous002d39b2022-05-31 08:59:27 -0700152 return;
153 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700154}
Jennifer Leeed5befb2018-08-10 11:29:45 -0700155
Gunnar Mills3e40fc72020-05-19 19:18:17 -0500156/**
157 * ManagerResetToDefaultsAction class supports POST method for factory reset
158 * action.
159 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700160inline void requestRoutesManagerResetToDefaultsAction(App& app)
Gunnar Mills3e40fc72020-05-19 19:18:17 -0500161{
Gunnar Mills3e40fc72020-05-19 19:18:17 -0500162
Gunnar Mills3e40fc72020-05-19 19:18:17 -0500163 /**
164 * Function handles ResetToDefaults POST method request.
165 *
166 * Analyzes POST body message and factory resets BMC by calling
167 * BMC code updater factory reset followed by a BMC reboot.
168 *
169 * BMC code updater factory reset wipes the whole BMC read-write
170 * filesystem which includes things like the network settings.
171 *
172 * OpenBMC only supports ResetToDefaultsType "ResetAll".
173 */
Gunnar Mills3e40fc72020-05-19 19:18:17 -0500174
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700175 BMCWEB_ROUTE(app,
176 "/redfish/v1/Managers/bmc/Actions/Manager.ResetToDefaults/")
Ed Tanoused398212021-06-09 17:05:54 -0700177 .privileges(redfish::privileges::postManager)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700178 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700179 [&app](const crow::Request& req,
180 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000181 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700182 {
183 return;
184 }
185 BMCWEB_LOG_DEBUG << "Post ResetToDefaults.";
Gunnar Mills3e40fc72020-05-19 19:18:17 -0500186
Ed Tanous002d39b2022-05-31 08:59:27 -0700187 std::string resetType;
Gunnar Mills3e40fc72020-05-19 19:18:17 -0500188
Ed Tanous002d39b2022-05-31 08:59:27 -0700189 if (!json_util::readJsonAction(req, asyncResp->res,
190 "ResetToDefaultsType", resetType))
191 {
192 BMCWEB_LOG_DEBUG << "Missing property ResetToDefaultsType.";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700193
Ed Tanous002d39b2022-05-31 08:59:27 -0700194 messages::actionParameterMissing(asyncResp->res, "ResetToDefaults",
195 "ResetToDefaultsType");
196 return;
197 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700198
Ed Tanous002d39b2022-05-31 08:59:27 -0700199 if (resetType != "ResetAll")
200 {
201 BMCWEB_LOG_DEBUG
202 << "Invalid property value for ResetToDefaultsType: "
203 << resetType;
204 messages::actionParameterNotSupported(asyncResp->res, resetType,
205 "ResetToDefaultsType");
206 return;
207 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700208
Ed Tanous002d39b2022-05-31 08:59:27 -0700209 crow::connections::systemBus->async_method_call(
210 [asyncResp](const boost::system::error_code ec) {
211 if (ec)
212 {
213 BMCWEB_LOG_DEBUG << "Failed to ResetToDefaults: " << ec;
214 messages::internalError(asyncResp->res);
215 return;
216 }
217 // Factory Reset doesn't actually happen until a reboot
218 // Can't erase what the BMC is running on
219 doBMCGracefulRestart(asyncResp);
220 },
221 "xyz.openbmc_project.Software.BMC.Updater",
222 "/xyz/openbmc_project/software",
223 "xyz.openbmc_project.Common.FactoryReset", "Reset");
224 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700225}
Gunnar Mills3e40fc72020-05-19 19:18:17 -0500226
AppaRao Puli1cb1a9e2020-07-17 23:38:57 +0530227/**
228 * ManagerResetActionInfo derived class for delivering Manager
229 * ResetType AllowableValues using ResetInfo schema.
230 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700231inline void requestRoutesManagerResetActionInfo(App& app)
AppaRao Puli1cb1a9e2020-07-17 23:38:57 +0530232{
AppaRao Puli1cb1a9e2020-07-17 23:38:57 +0530233 /**
234 * Functions triggers appropriate requests on DBus
235 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700236
237 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/ResetActionInfo/")
Ed Tanoused398212021-06-09 17:05:54 -0700238 .privileges(redfish::privileges::getActionInfo)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700239 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700240 [&app](const crow::Request& req,
241 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000242 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700243 {
244 return;
245 }
Ed Tanous14766872022-03-15 10:44:42 -0700246
Ed Tanous002d39b2022-05-31 08:59:27 -0700247 asyncResp->res.jsonValue["@odata.type"] =
248 "#ActionInfo.v1_1_2.ActionInfo";
249 asyncResp->res.jsonValue["@odata.id"] =
250 "/redfish/v1/Managers/bmc/ResetActionInfo";
251 asyncResp->res.jsonValue["Name"] = "Reset Action Info";
252 asyncResp->res.jsonValue["Id"] = "ResetActionInfo";
253 nlohmann::json::object_t parameter;
254 parameter["Name"] = "ResetType";
255 parameter["Required"] = true;
256 parameter["DataType"] = "String";
Ed Tanous14766872022-03-15 10:44:42 -0700257
Ed Tanous002d39b2022-05-31 08:59:27 -0700258 nlohmann::json::array_t allowableValues;
259 allowableValues.push_back("GracefulRestart");
260 allowableValues.push_back("ForceRestart");
261 parameter["AllowableValues"] = std::move(allowableValues);
Ed Tanous14766872022-03-15 10:44:42 -0700262
Ed Tanous002d39b2022-05-31 08:59:27 -0700263 nlohmann::json::array_t parameters;
264 parameters.push_back(std::move(parameter));
Ed Tanous14766872022-03-15 10:44:42 -0700265
Ed Tanous002d39b2022-05-31 08:59:27 -0700266 asyncResp->res.jsonValue["Parameters"] = std::move(parameters);
267 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700268}
AppaRao Puli1cb1a9e2020-07-17 23:38:57 +0530269
James Feist5b4aa862018-08-16 14:07:01 -0700270static constexpr const char* objectManagerIface =
271 "org.freedesktop.DBus.ObjectManager";
272static constexpr const char* pidConfigurationIface =
273 "xyz.openbmc_project.Configuration.Pid";
274static constexpr const char* pidZoneConfigurationIface =
275 "xyz.openbmc_project.Configuration.Pid.Zone";
James Feistb7a08d02018-12-11 14:55:37 -0800276static constexpr const char* stepwiseConfigurationIface =
277 "xyz.openbmc_project.Configuration.Stepwise";
James Feist73df0db2019-03-25 15:29:35 -0700278static constexpr const char* thermalModeIface =
279 "xyz.openbmc_project.Control.ThermalMode";
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100280
zhanghch058d1b46d2021-04-01 11:18:24 +0800281inline void
282 asyncPopulatePid(const std::string& connection, const std::string& path,
283 const std::string& currentProfile,
284 const std::vector<std::string>& supportedProfiles,
285 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
James Feist5b4aa862018-08-16 14:07:01 -0700286{
287
288 crow::connections::systemBus->async_method_call(
James Feist73df0db2019-03-25 15:29:35 -0700289 [asyncResp, currentProfile, supportedProfiles](
290 const boost::system::error_code ec,
291 const dbus::utility::ManagedObjectType& managedObj) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700292 if (ec)
293 {
294 BMCWEB_LOG_ERROR << ec;
295 asyncResp->res.jsonValue.clear();
296 messages::internalError(asyncResp->res);
297 return;
298 }
299 nlohmann::json& configRoot =
300 asyncResp->res.jsonValue["Oem"]["OpenBmc"]["Fan"];
301 nlohmann::json& fans = configRoot["FanControllers"];
302 fans["@odata.type"] = "#OemManager.FanControllers";
303 fans["@odata.id"] =
304 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanControllers";
305
306 nlohmann::json& pids = configRoot["PidControllers"];
307 pids["@odata.type"] = "#OemManager.PidControllers";
308 pids["@odata.id"] =
309 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/PidControllers";
310
311 nlohmann::json& stepwise = configRoot["StepwiseControllers"];
312 stepwise["@odata.type"] = "#OemManager.StepwiseControllers";
313 stepwise["@odata.id"] =
314 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/StepwiseControllers";
315
316 nlohmann::json& zones = configRoot["FanZones"];
317 zones["@odata.id"] =
318 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones";
319 zones["@odata.type"] = "#OemManager.FanZones";
320 configRoot["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan";
321 configRoot["@odata.type"] = "#OemManager.Fan";
322 configRoot["Profile@Redfish.AllowableValues"] = supportedProfiles;
323
324 if (!currentProfile.empty())
325 {
326 configRoot["Profile"] = currentProfile;
327 }
328 BMCWEB_LOG_ERROR << "profile = " << currentProfile << " !";
329
330 for (const auto& pathPair : managedObj)
331 {
332 for (const auto& intfPair : pathPair.second)
James Feist5b4aa862018-08-16 14:07:01 -0700333 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700334 if (intfPair.first != pidConfigurationIface &&
335 intfPair.first != pidZoneConfigurationIface &&
336 intfPair.first != stepwiseConfigurationIface)
James Feist5b4aa862018-08-16 14:07:01 -0700337 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700338 continue;
339 }
James Feist73df0db2019-03-25 15:29:35 -0700340
Ed Tanous002d39b2022-05-31 08:59:27 -0700341 std::string name;
James Feist73df0db2019-03-25 15:29:35 -0700342
Ed Tanous002d39b2022-05-31 08:59:27 -0700343 for (const std::pair<std::string,
344 dbus::utility::DbusVariantType>& propPair :
345 intfPair.second)
346 {
347 if (propPair.first == "Name")
James Feist73df0db2019-03-25 15:29:35 -0700348 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700349 const std::string* namePtr =
350 std::get_if<std::string>(&propPair.second);
351 if (namePtr == nullptr)
James Feist73df0db2019-03-25 15:29:35 -0700352 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700353 BMCWEB_LOG_ERROR << "Pid Name Field illegal";
James Feistc33a90e2019-03-01 10:17:44 -0800354 messages::internalError(asyncResp->res);
355 return;
356 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700357 name = *namePtr;
358 dbus::utility::escapePathForDbus(name);
James Feistb7a08d02018-12-11 14:55:37 -0800359 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700360 else if (propPair.first == "Profiles")
James Feistb7a08d02018-12-11 14:55:37 -0800361 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700362 const std::vector<std::string>* profiles =
363 std::get_if<std::vector<std::string>>(
364 &propPair.second);
365 if (profiles == nullptr)
James Feistb7a08d02018-12-11 14:55:37 -0800366 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700367 BMCWEB_LOG_ERROR << "Pid Profiles Field illegal";
James Feistb7a08d02018-12-11 14:55:37 -0800368 messages::internalError(asyncResp->res);
369 return;
370 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700371 if (std::find(profiles->begin(), profiles->end(),
372 currentProfile) == profiles->end())
James Feistb7a08d02018-12-11 14:55:37 -0800373 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700374 BMCWEB_LOG_INFO
375 << name << " not supported in current profile";
376 continue;
James Feistb7a08d02018-12-11 14:55:37 -0800377 }
378 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700379 }
380 nlohmann::json* config = nullptr;
381 const std::string* classPtr = nullptr;
382
383 for (const std::pair<std::string,
384 dbus::utility::DbusVariantType>& propPair :
385 intfPair.second)
386 {
387 if (propPair.first == "Class")
James Feistb7a08d02018-12-11 14:55:37 -0800388 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700389 classPtr = std::get_if<std::string>(&propPair.second);
390 }
391 }
392
393 if (intfPair.first == pidZoneConfigurationIface)
394 {
395 std::string chassis;
396 if (!dbus::utility::getNthStringFromPath(pathPair.first.str,
397 5, chassis))
398 {
399 chassis = "#IllegalValue";
400 }
401 nlohmann::json& zone = zones[name];
402 zone["Chassis"] = {
403 {"@odata.id", "/redfish/v1/Chassis/" + chassis}};
404 zone["@odata.id"] =
405 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones/" +
406 name;
407 zone["@odata.type"] = "#OemManager.FanZone";
408 config = &zone;
409 }
410
411 else if (intfPair.first == stepwiseConfigurationIface)
412 {
413 if (classPtr == nullptr)
414 {
415 BMCWEB_LOG_ERROR << "Pid Class Field illegal";
James Feistb7a08d02018-12-11 14:55:37 -0800416 messages::internalError(asyncResp->res);
417 return;
418 }
419
Ed Tanous002d39b2022-05-31 08:59:27 -0700420 nlohmann::json& controller = stepwise[name];
421 config = &controller;
James Feistb7a08d02018-12-11 14:55:37 -0800422
Ed Tanous002d39b2022-05-31 08:59:27 -0700423 controller["@odata.id"] =
424 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/StepwiseControllers/" +
425 name;
426 controller["@odata.type"] =
427 "#OemManager.StepwiseController";
428
429 controller["Direction"] = *classPtr;
430 }
431
432 // pid and fans are off the same configuration
433 else if (intfPair.first == pidConfigurationIface)
434 {
435
436 if (classPtr == nullptr)
James Feist5b4aa862018-08-16 14:07:01 -0700437 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700438 BMCWEB_LOG_ERROR << "Pid Class Field illegal";
439 messages::internalError(asyncResp->res);
440 return;
441 }
442 bool isFan = *classPtr == "fan";
443 nlohmann::json& element = isFan ? fans[name] : pids[name];
444 config = &element;
445 if (isFan)
446 {
447 element["@odata.id"] =
448 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanControllers/" +
449 name;
450 element["@odata.type"] = "#OemManager.FanController";
451 }
452 else
453 {
454 element["@odata.id"] =
455 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/PidControllers/" +
456 name;
457 element["@odata.type"] = "#OemManager.PidController";
458 }
459 }
460 else
461 {
462 BMCWEB_LOG_ERROR << "Unexpected configuration";
463 messages::internalError(asyncResp->res);
464 return;
465 }
James Feist5b4aa862018-08-16 14:07:01 -0700466
Ed Tanous002d39b2022-05-31 08:59:27 -0700467 // used for making maps out of 2 vectors
468 const std::vector<double>* keys = nullptr;
469 const std::vector<double>* values = nullptr;
470
471 for (const auto& propertyPair : intfPair.second)
472 {
473 if (propertyPair.first == "Type" ||
474 propertyPair.first == "Class" ||
475 propertyPair.first == "Name")
476 {
477 continue;
478 }
479
480 // zones
481 if (intfPair.first == pidZoneConfigurationIface)
482 {
483 const double* ptr =
484 std::get_if<double>(&propertyPair.second);
485 if (ptr == nullptr)
486 {
487 BMCWEB_LOG_ERROR << "Field Illegal "
488 << propertyPair.first;
489 messages::internalError(asyncResp->res);
490 return;
491 }
492 (*config)[propertyPair.first] = *ptr;
493 }
494
495 if (intfPair.first == stepwiseConfigurationIface)
496 {
497 if (propertyPair.first == "Reading" ||
498 propertyPair.first == "Output")
499 {
500 const std::vector<double>* ptr =
501 std::get_if<std::vector<double>>(
502 &propertyPair.second);
503
504 if (ptr == nullptr)
505 {
506 BMCWEB_LOG_ERROR << "Field Illegal "
507 << propertyPair.first;
508 messages::internalError(asyncResp->res);
509 return;
510 }
511
512 if (propertyPair.first == "Reading")
513 {
514 keys = ptr;
515 }
516 else
517 {
518 values = ptr;
519 }
520 if (keys != nullptr && values != nullptr)
521 {
522 if (keys->size() != values->size())
523 {
524 BMCWEB_LOG_ERROR
525 << "Reading and Output size don't match ";
526 messages::internalError(asyncResp->res);
527 return;
528 }
529 nlohmann::json& steps = (*config)["Steps"];
530 steps = nlohmann::json::array();
531 for (size_t ii = 0; ii < keys->size(); ii++)
532 {
533 nlohmann::json::object_t step;
534 step["Target"] = (*keys)[ii];
535 step["Output"] = (*values)[ii];
536 steps.push_back(std::move(step));
537 }
538 }
539 }
540 if (propertyPair.first == "NegativeHysteresis" ||
541 propertyPair.first == "PositiveHysteresis")
James Feist5b4aa862018-08-16 14:07:01 -0700542 {
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800543 const double* ptr =
Ed Tanousabf2add2019-01-22 16:40:12 -0800544 std::get_if<double>(&propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700545 if (ptr == nullptr)
546 {
547 BMCWEB_LOG_ERROR << "Field Illegal "
548 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700549 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700550 return;
551 }
James Feistb7a08d02018-12-11 14:55:37 -0800552 (*config)[propertyPair.first] = *ptr;
553 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700554 }
James Feistb7a08d02018-12-11 14:55:37 -0800555
Ed Tanous002d39b2022-05-31 08:59:27 -0700556 // pid and fans are off the same configuration
557 if (intfPair.first == pidConfigurationIface ||
558 intfPair.first == stepwiseConfigurationIface)
559 {
560
561 if (propertyPair.first == "Zones")
James Feistb7a08d02018-12-11 14:55:37 -0800562 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700563 const std::vector<std::string>* inputs =
564 std::get_if<std::vector<std::string>>(
565 &propertyPair.second);
566
567 if (inputs == nullptr)
James Feistb7a08d02018-12-11 14:55:37 -0800568 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700569 BMCWEB_LOG_ERROR << "Zones Pid Field Illegal";
570 messages::internalError(asyncResp->res);
571 return;
James Feistb7a08d02018-12-11 14:55:37 -0800572 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700573 auto& data = (*config)[propertyPair.first];
574 data = nlohmann::json::array();
575 for (std::string itemCopy : *inputs)
James Feistb7a08d02018-12-11 14:55:37 -0800576 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700577 dbus::utility::escapePathForDbus(itemCopy);
578 nlohmann::json::object_t input;
579 input["@odata.id"] =
580 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones/" +
581 itemCopy;
582 data.push_back(std::move(input));
James Feistb7a08d02018-12-11 14:55:37 -0800583 }
James Feist5b4aa862018-08-16 14:07:01 -0700584 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700585 // todo(james): may never happen, but this
586 // assumes configuration data referenced in the
587 // PID config is provided by the same daemon, we
588 // could add another loop to cover all cases,
589 // but I'm okay kicking this can down the road a
590 // bit
James Feist5b4aa862018-08-16 14:07:01 -0700591
Ed Tanous002d39b2022-05-31 08:59:27 -0700592 else if (propertyPair.first == "Inputs" ||
593 propertyPair.first == "Outputs")
James Feist5b4aa862018-08-16 14:07:01 -0700594 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700595 auto& data = (*config)[propertyPair.first];
596 const std::vector<std::string>* inputs =
597 std::get_if<std::vector<std::string>>(
598 &propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700599
Ed Tanous002d39b2022-05-31 08:59:27 -0700600 if (inputs == nullptr)
James Feist5b4aa862018-08-16 14:07:01 -0700601 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700602 BMCWEB_LOG_ERROR << "Field Illegal "
603 << propertyPair.first;
604 messages::internalError(asyncResp->res);
605 return;
James Feist5b4aa862018-08-16 14:07:01 -0700606 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700607 data = *inputs;
608 }
609 else if (propertyPair.first == "SetPointOffset")
610 {
611 const std::string* ptr =
612 std::get_if<std::string>(&propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700613
Ed Tanous002d39b2022-05-31 08:59:27 -0700614 if (ptr == nullptr)
James Feist5b4aa862018-08-16 14:07:01 -0700615 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700616 BMCWEB_LOG_ERROR << "Field Illegal "
617 << propertyPair.first;
618 messages::internalError(asyncResp->res);
619 return;
James Feistb943aae2019-07-11 16:33:56 -0700620 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700621 // translate from dbus to redfish
622 if (*ptr == "WarningHigh")
James Feistb943aae2019-07-11 16:33:56 -0700623 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700624 (*config)["SetPointOffset"] =
625 "UpperThresholdNonCritical";
James Feistb943aae2019-07-11 16:33:56 -0700626 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700627 else if (*ptr == "WarningLow")
James Feist5b4aa862018-08-16 14:07:01 -0700628 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700629 (*config)["SetPointOffset"] =
630 "LowerThresholdNonCritical";
James Feist5b4aa862018-08-16 14:07:01 -0700631 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700632 else if (*ptr == "CriticalHigh")
633 {
634 (*config)["SetPointOffset"] =
635 "UpperThresholdCritical";
636 }
637 else if (*ptr == "CriticalLow")
638 {
639 (*config)["SetPointOffset"] =
640 "LowerThresholdCritical";
641 }
642 else
643 {
644 BMCWEB_LOG_ERROR << "Value Illegal " << *ptr;
645 messages::internalError(asyncResp->res);
646 return;
647 }
648 }
649 // doubles
650 else if (propertyPair.first == "FFGainCoefficient" ||
651 propertyPair.first == "FFOffCoefficient" ||
652 propertyPair.first == "ICoefficient" ||
653 propertyPair.first == "ILimitMax" ||
654 propertyPair.first == "ILimitMin" ||
655 propertyPair.first == "PositiveHysteresis" ||
656 propertyPair.first == "NegativeHysteresis" ||
657 propertyPair.first == "OutLimitMax" ||
658 propertyPair.first == "OutLimitMin" ||
659 propertyPair.first == "PCoefficient" ||
660 propertyPair.first == "SetPoint" ||
661 propertyPair.first == "SlewNeg" ||
662 propertyPair.first == "SlewPos")
663 {
664 const double* ptr =
665 std::get_if<double>(&propertyPair.second);
666 if (ptr == nullptr)
667 {
668 BMCWEB_LOG_ERROR << "Field Illegal "
669 << propertyPair.first;
670 messages::internalError(asyncResp->res);
671 return;
672 }
673 (*config)[propertyPair.first] = *ptr;
James Feist5b4aa862018-08-16 14:07:01 -0700674 }
675 }
676 }
677 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700678 }
James Feist5b4aa862018-08-16 14:07:01 -0700679 },
680 connection, path, objectManagerIface, "GetManagedObjects");
681}
Jennifer Leeca537922018-08-10 10:07:30 -0700682
James Feist83ff9ab2018-08-31 10:18:24 -0700683enum class CreatePIDRet
684{
685 fail,
686 del,
687 patch
688};
689
zhanghch058d1b46d2021-04-01 11:18:24 +0800690inline bool
691 getZonesFromJsonReq(const std::shared_ptr<bmcweb::AsyncResp>& response,
692 std::vector<nlohmann::json>& config,
693 std::vector<std::string>& zones)
James Feist5f2caae2018-12-12 14:08:25 -0800694{
James Feistb6baeaa2019-02-21 10:41:40 -0800695 if (config.empty())
696 {
697 BMCWEB_LOG_ERROR << "Empty Zones";
Ed Tanous1668ce62022-02-07 23:44:31 -0800698 messages::propertyValueFormatError(response->res, "[]", "Zones");
James Feistb6baeaa2019-02-21 10:41:40 -0800699 return false;
700 }
James Feist5f2caae2018-12-12 14:08:25 -0800701 for (auto& odata : config)
702 {
703 std::string path;
704 if (!redfish::json_util::readJson(odata, response->res, "@odata.id",
705 path))
706 {
707 return false;
708 }
709 std::string input;
James Feist61adbda2019-03-25 13:03:51 -0700710
711 // 8 below comes from
712 // /redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones/Left
713 // 0 1 2 3 4 5 6 7 8
714 if (!dbus::utility::getNthStringFromPath(path, 8, input))
James Feist5f2caae2018-12-12 14:08:25 -0800715 {
716 BMCWEB_LOG_ERROR << "Got invalid path " << path;
717 BMCWEB_LOG_ERROR << "Illegal Type Zones";
718 messages::propertyValueFormatError(response->res, odata.dump(),
719 "Zones");
720 return false;
721 }
Ed Tanousa170f272022-06-30 21:53:27 -0700722 std::replace(input.begin(), input.end(), '_', ' ');
James Feist5f2caae2018-12-12 14:08:25 -0800723 zones.emplace_back(std::move(input));
724 }
725 return true;
726}
727
Ed Tanous711ac7a2021-12-20 09:34:41 -0800728inline const dbus::utility::ManagedObjectType::value_type*
James Feist73df0db2019-03-25 15:29:35 -0700729 findChassis(const dbus::utility::ManagedObjectType& managedObj,
730 const std::string& value, std::string& chassis)
James Feistb6baeaa2019-02-21 10:41:40 -0800731{
732 BMCWEB_LOG_DEBUG << "Find Chassis: " << value << "\n";
733
Ed Tanousa170f272022-06-30 21:53:27 -0700734 std::string escaped = value;
735 std::replace(escaped.begin(), escaped.end(), '_', ' ');
James Feistb6baeaa2019-02-21 10:41:40 -0800736 escaped = "/" + escaped;
Ed Tanous002d39b2022-05-31 08:59:27 -0700737 auto it = std::find_if(managedObj.begin(), managedObj.end(),
738 [&escaped](const auto& obj) {
739 if (boost::algorithm::ends_with(obj.first.str, escaped))
740 {
741 BMCWEB_LOG_DEBUG << "Matched " << obj.first.str << "\n";
742 return true;
743 }
744 return false;
745 });
James Feistb6baeaa2019-02-21 10:41:40 -0800746
747 if (it == managedObj.end())
748 {
James Feist73df0db2019-03-25 15:29:35 -0700749 return nullptr;
James Feistb6baeaa2019-02-21 10:41:40 -0800750 }
751 // 5 comes from <chassis-name> being the 5th element
752 // /xyz/openbmc_project/inventory/system/chassis/<chassis-name>
James Feist73df0db2019-03-25 15:29:35 -0700753 if (dbus::utility::getNthStringFromPath(it->first.str, 5, chassis))
754 {
755 return &(*it);
756 }
757
758 return nullptr;
James Feistb6baeaa2019-02-21 10:41:40 -0800759}
760
Ed Tanous23a21a12020-07-25 04:45:05 +0000761inline CreatePIDRet createPidInterface(
zhanghch058d1b46d2021-04-01 11:18:24 +0800762 const std::shared_ptr<bmcweb::AsyncResp>& response, const std::string& type,
Ed Tanousb5a76932020-09-29 16:16:58 -0700763 const nlohmann::json::iterator& it, const std::string& path,
James Feist83ff9ab2018-08-31 10:18:24 -0700764 const dbus::utility::ManagedObjectType& managedObj, bool createNewObject,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800765 dbus::utility::DBusPropertiesMap& output, std::string& chassis,
766 const std::string& profile)
James Feist83ff9ab2018-08-31 10:18:24 -0700767{
768
James Feist5f2caae2018-12-12 14:08:25 -0800769 // common deleter
James Feistb6baeaa2019-02-21 10:41:40 -0800770 if (it.value() == nullptr)
James Feist5f2caae2018-12-12 14:08:25 -0800771 {
772 std::string iface;
773 if (type == "PidControllers" || type == "FanControllers")
774 {
775 iface = pidConfigurationIface;
776 }
777 else if (type == "FanZones")
778 {
779 iface = pidZoneConfigurationIface;
780 }
781 else if (type == "StepwiseControllers")
782 {
783 iface = stepwiseConfigurationIface;
784 }
785 else
786 {
Gunnar Millsa0744d32020-11-09 15:40:45 -0600787 BMCWEB_LOG_ERROR << "Illegal Type " << type;
James Feist5f2caae2018-12-12 14:08:25 -0800788 messages::propertyUnknown(response->res, type);
789 return CreatePIDRet::fail;
790 }
James Feist6ee7f772020-02-06 16:25:27 -0800791
792 BMCWEB_LOG_DEBUG << "del " << path << " " << iface << "\n";
James Feist5f2caae2018-12-12 14:08:25 -0800793 // delete interface
794 crow::connections::systemBus->async_method_call(
795 [response, path](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700796 if (ec)
797 {
798 BMCWEB_LOG_ERROR << "Error patching " << path << ": " << ec;
799 messages::internalError(response->res);
800 return;
801 }
802 messages::success(response->res);
James Feist5f2caae2018-12-12 14:08:25 -0800803 },
804 "xyz.openbmc_project.EntityManager", path, iface, "Delete");
805 return CreatePIDRet::del;
806 }
807
Ed Tanous711ac7a2021-12-20 09:34:41 -0800808 const dbus::utility::ManagedObjectType::value_type* managedItem = nullptr;
James Feistb6baeaa2019-02-21 10:41:40 -0800809 if (!createNewObject)
810 {
811 // if we aren't creating a new object, we should be able to find it on
812 // d-bus
James Feist73df0db2019-03-25 15:29:35 -0700813 managedItem = findChassis(managedObj, it.key(), chassis);
814 if (managedItem == nullptr)
James Feistb6baeaa2019-02-21 10:41:40 -0800815 {
816 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
Ed Tanousace85d62021-10-26 12:45:59 -0700817 messages::invalidObject(response->res,
818 crow::utility::urlFromPieces(
819 "redfish", "v1", "Chassis", chassis));
James Feistb6baeaa2019-02-21 10:41:40 -0800820 return CreatePIDRet::fail;
821 }
822 }
823
Ed Tanous26f69762022-01-25 09:49:11 -0800824 if (!profile.empty() &&
James Feist73df0db2019-03-25 15:29:35 -0700825 (type == "PidControllers" || type == "FanControllers" ||
826 type == "StepwiseControllers"))
827 {
828 if (managedItem == nullptr)
829 {
Ed Tanousb9d36b42022-02-26 21:42:46 -0800830 output.emplace_back("Profiles", std::vector<std::string>{profile});
James Feist73df0db2019-03-25 15:29:35 -0700831 }
832 else
833 {
834 std::string interface;
835 if (type == "StepwiseControllers")
836 {
837 interface = stepwiseConfigurationIface;
838 }
839 else
840 {
841 interface = pidConfigurationIface;
842 }
Ed Tanous711ac7a2021-12-20 09:34:41 -0800843 bool ifaceFound = false;
844 for (const auto& iface : managedItem->second)
845 {
846 if (iface.first == interface)
847 {
848 ifaceFound = true;
849 for (const auto& prop : iface.second)
850 {
851 if (prop.first == "Profiles")
852 {
853 const std::vector<std::string>* curProfiles =
854 std::get_if<std::vector<std::string>>(
855 &(prop.second));
856 if (curProfiles == nullptr)
857 {
858 BMCWEB_LOG_ERROR
859 << "Illegal profiles in managed object";
860 messages::internalError(response->res);
861 return CreatePIDRet::fail;
862 }
863 if (std::find(curProfiles->begin(),
864 curProfiles->end(),
865 profile) == curProfiles->end())
866 {
867 std::vector<std::string> newProfiles =
868 *curProfiles;
869 newProfiles.push_back(profile);
Ed Tanousb9d36b42022-02-26 21:42:46 -0800870 output.emplace_back("Profiles", newProfiles);
Ed Tanous711ac7a2021-12-20 09:34:41 -0800871 }
872 }
873 }
874 }
875 }
876
877 if (!ifaceFound)
James Feist73df0db2019-03-25 15:29:35 -0700878 {
879 BMCWEB_LOG_ERROR
880 << "Failed to find interface in managed object";
881 messages::internalError(response->res);
882 return CreatePIDRet::fail;
883 }
James Feist73df0db2019-03-25 15:29:35 -0700884 }
885 }
886
James Feist83ff9ab2018-08-31 10:18:24 -0700887 if (type == "PidControllers" || type == "FanControllers")
888 {
889 if (createNewObject)
890 {
Ed Tanousb9d36b42022-02-26 21:42:46 -0800891 output.emplace_back("Class",
892 type == "PidControllers" ? "temp" : "fan");
893 output.emplace_back("Type", "Pid");
James Feist83ff9ab2018-08-31 10:18:24 -0700894 }
James Feist5f2caae2018-12-12 14:08:25 -0800895
896 std::optional<std::vector<nlohmann::json>> zones;
897 std::optional<std::vector<std::string>> inputs;
898 std::optional<std::vector<std::string>> outputs;
899 std::map<std::string, std::optional<double>> doubles;
James Feistb943aae2019-07-11 16:33:56 -0700900 std::optional<std::string> setpointOffset;
James Feist5f2caae2018-12-12 14:08:25 -0800901 if (!redfish::json_util::readJson(
James Feistb6baeaa2019-02-21 10:41:40 -0800902 it.value(), response->res, "Inputs", inputs, "Outputs", outputs,
James Feist5f2caae2018-12-12 14:08:25 -0800903 "Zones", zones, "FFGainCoefficient",
904 doubles["FFGainCoefficient"], "FFOffCoefficient",
905 doubles["FFOffCoefficient"], "ICoefficient",
906 doubles["ICoefficient"], "ILimitMax", doubles["ILimitMax"],
907 "ILimitMin", doubles["ILimitMin"], "OutLimitMax",
908 doubles["OutLimitMax"], "OutLimitMin", doubles["OutLimitMin"],
909 "PCoefficient", doubles["PCoefficient"], "SetPoint",
James Feistb943aae2019-07-11 16:33:56 -0700910 doubles["SetPoint"], "SetPointOffset", setpointOffset,
911 "SlewNeg", doubles["SlewNeg"], "SlewPos", doubles["SlewPos"],
912 "PositiveHysteresis", doubles["PositiveHysteresis"],
913 "NegativeHysteresis", doubles["NegativeHysteresis"]))
James Feist83ff9ab2018-08-31 10:18:24 -0700914 {
Ed Tanous71f52d92021-02-19 08:51:17 -0800915 BMCWEB_LOG_ERROR
916 << "Illegal Property "
917 << it.value().dump(2, ' ', true,
918 nlohmann::json::error_handler_t::replace);
James Feist5f2caae2018-12-12 14:08:25 -0800919 return CreatePIDRet::fail;
James Feist83ff9ab2018-08-31 10:18:24 -0700920 }
James Feist5f2caae2018-12-12 14:08:25 -0800921 if (zones)
James Feist83ff9ab2018-08-31 10:18:24 -0700922 {
James Feist5f2caae2018-12-12 14:08:25 -0800923 std::vector<std::string> zonesStr;
924 if (!getZonesFromJsonReq(response, *zones, zonesStr))
James Feist83ff9ab2018-08-31 10:18:24 -0700925 {
Gunnar Millsa0744d32020-11-09 15:40:45 -0600926 BMCWEB_LOG_ERROR << "Illegal Zones";
James Feist5f2caae2018-12-12 14:08:25 -0800927 return CreatePIDRet::fail;
James Feist83ff9ab2018-08-31 10:18:24 -0700928 }
James Feistb6baeaa2019-02-21 10:41:40 -0800929 if (chassis.empty() &&
Ed Tanouse662eae2022-01-25 10:39:19 -0800930 findChassis(managedObj, zonesStr[0], chassis) == nullptr)
James Feistb6baeaa2019-02-21 10:41:40 -0800931 {
932 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
Ed Tanousace85d62021-10-26 12:45:59 -0700933 messages::invalidObject(
934 response->res, crow::utility::urlFromPieces(
935 "redfish", "v1", "Chassis", chassis));
James Feistb6baeaa2019-02-21 10:41:40 -0800936 return CreatePIDRet::fail;
937 }
Ed Tanousb9d36b42022-02-26 21:42:46 -0800938 output.emplace_back("Zones", std::move(zonesStr));
James Feist5f2caae2018-12-12 14:08:25 -0800939 }
940 if (inputs || outputs)
941 {
Ed Tanous02cad962022-06-30 16:50:15 -0700942 std::array<
943 std::reference_wrapper<std::optional<std::vector<std::string>>>,
944 2>
945 containers = {inputs, outputs};
James Feist5f2caae2018-12-12 14:08:25 -0800946 size_t index = 0;
Ed Tanous02cad962022-06-30 16:50:15 -0700947 for (std::optional<std::vector<std::string>>& container :
948 containers)
James Feist83ff9ab2018-08-31 10:18:24 -0700949 {
James Feist5f2caae2018-12-12 14:08:25 -0800950 if (!container)
James Feist83ff9ab2018-08-31 10:18:24 -0700951 {
James Feist5f2caae2018-12-12 14:08:25 -0800952 index++;
953 continue;
James Feist83ff9ab2018-08-31 10:18:24 -0700954 }
James Feist5f2caae2018-12-12 14:08:25 -0800955 for (std::string& value : *container)
James Feist83ff9ab2018-08-31 10:18:24 -0700956 {
Ed Tanousa170f272022-06-30 21:53:27 -0700957 std::replace(value.begin(), value.end(), '_', ' ');
James Feist83ff9ab2018-08-31 10:18:24 -0700958 }
James Feist5f2caae2018-12-12 14:08:25 -0800959 std::string key;
960 if (index == 0)
James Feist83ff9ab2018-08-31 10:18:24 -0700961 {
James Feist5f2caae2018-12-12 14:08:25 -0800962 key = "Inputs";
James Feist83ff9ab2018-08-31 10:18:24 -0700963 }
James Feist5f2caae2018-12-12 14:08:25 -0800964 else
965 {
966 key = "Outputs";
967 }
Ed Tanousb9d36b42022-02-26 21:42:46 -0800968 output.emplace_back(key, *container);
James Feist5f2caae2018-12-12 14:08:25 -0800969 index++;
James Feist83ff9ab2018-08-31 10:18:24 -0700970 }
James Feist5f2caae2018-12-12 14:08:25 -0800971 }
James Feist83ff9ab2018-08-31 10:18:24 -0700972
James Feistb943aae2019-07-11 16:33:56 -0700973 if (setpointOffset)
974 {
975 // translate between redfish and dbus names
976 if (*setpointOffset == "UpperThresholdNonCritical")
977 {
Ed Tanousb9d36b42022-02-26 21:42:46 -0800978 output.emplace_back("SetPointOffset", "WarningLow");
James Feistb943aae2019-07-11 16:33:56 -0700979 }
980 else if (*setpointOffset == "LowerThresholdNonCritical")
981 {
Ed Tanousb9d36b42022-02-26 21:42:46 -0800982 output.emplace_back("SetPointOffset", "WarningHigh");
James Feistb943aae2019-07-11 16:33:56 -0700983 }
984 else if (*setpointOffset == "LowerThresholdCritical")
985 {
Ed Tanousb9d36b42022-02-26 21:42:46 -0800986 output.emplace_back("SetPointOffset", "CriticalLow");
James Feistb943aae2019-07-11 16:33:56 -0700987 }
988 else if (*setpointOffset == "UpperThresholdCritical")
989 {
Ed Tanousb9d36b42022-02-26 21:42:46 -0800990 output.emplace_back("SetPointOffset", "CriticalHigh");
James Feistb943aae2019-07-11 16:33:56 -0700991 }
992 else
993 {
994 BMCWEB_LOG_ERROR << "Invalid setpointoffset "
995 << *setpointOffset;
Ed Tanousace85d62021-10-26 12:45:59 -0700996 messages::propertyValueNotInList(response->res, it.key(),
997 "SetPointOffset");
James Feistb943aae2019-07-11 16:33:56 -0700998 return CreatePIDRet::fail;
999 }
1000 }
1001
James Feist5f2caae2018-12-12 14:08:25 -08001002 // doubles
1003 for (const auto& pairs : doubles)
1004 {
1005 if (!pairs.second)
James Feist83ff9ab2018-08-31 10:18:24 -07001006 {
James Feist5f2caae2018-12-12 14:08:25 -08001007 continue;
James Feist83ff9ab2018-08-31 10:18:24 -07001008 }
James Feist5f2caae2018-12-12 14:08:25 -08001009 BMCWEB_LOG_DEBUG << pairs.first << " = " << *pairs.second;
Ed Tanousb9d36b42022-02-26 21:42:46 -08001010 output.emplace_back(pairs.first, *pairs.second);
James Feist83ff9ab2018-08-31 10:18:24 -07001011 }
1012 }
James Feist5f2caae2018-12-12 14:08:25 -08001013
James Feist83ff9ab2018-08-31 10:18:24 -07001014 else if (type == "FanZones")
1015 {
Ed Tanousb9d36b42022-02-26 21:42:46 -08001016 output.emplace_back("Type", "Pid.Zone");
James Feist83ff9ab2018-08-31 10:18:24 -07001017
James Feist5f2caae2018-12-12 14:08:25 -08001018 std::optional<nlohmann::json> chassisContainer;
1019 std::optional<double> failSafePercent;
James Feistd3ec07f2019-02-25 14:51:15 -08001020 std::optional<double> minThermalOutput;
James Feistb6baeaa2019-02-21 10:41:40 -08001021 if (!redfish::json_util::readJson(it.value(), response->res, "Chassis",
James Feist5f2caae2018-12-12 14:08:25 -08001022 chassisContainer, "FailSafePercent",
James Feistd3ec07f2019-02-25 14:51:15 -08001023 failSafePercent, "MinThermalOutput",
1024 minThermalOutput))
James Feist83ff9ab2018-08-31 10:18:24 -07001025 {
Ed Tanous71f52d92021-02-19 08:51:17 -08001026 BMCWEB_LOG_ERROR
1027 << "Illegal Property "
1028 << it.value().dump(2, ' ', true,
1029 nlohmann::json::error_handler_t::replace);
James Feist5f2caae2018-12-12 14:08:25 -08001030 return CreatePIDRet::fail;
1031 }
James Feist83ff9ab2018-08-31 10:18:24 -07001032
James Feist5f2caae2018-12-12 14:08:25 -08001033 if (chassisContainer)
1034 {
1035
1036 std::string chassisId;
1037 if (!redfish::json_util::readJson(*chassisContainer, response->res,
1038 "@odata.id", chassisId))
James Feist83ff9ab2018-08-31 10:18:24 -07001039 {
Ed Tanous71f52d92021-02-19 08:51:17 -08001040 BMCWEB_LOG_ERROR
1041 << "Illegal Property "
1042 << chassisContainer->dump(
1043 2, ' ', true,
1044 nlohmann::json::error_handler_t::replace);
James Feist83ff9ab2018-08-31 10:18:24 -07001045 return CreatePIDRet::fail;
1046 }
James Feist5f2caae2018-12-12 14:08:25 -08001047
AppaRao Puli717794d2019-10-18 22:54:53 +05301048 // /redfish/v1/chassis/chassis_name/
James Feist5f2caae2018-12-12 14:08:25 -08001049 if (!dbus::utility::getNthStringFromPath(chassisId, 3, chassis))
1050 {
1051 BMCWEB_LOG_ERROR << "Got invalid path " << chassisId;
Ed Tanousace85d62021-10-26 12:45:59 -07001052 messages::invalidObject(
1053 response->res, crow::utility::urlFromPieces(
1054 "redfish", "v1", "Chassis", chassisId));
James Feist5f2caae2018-12-12 14:08:25 -08001055 return CreatePIDRet::fail;
1056 }
1057 }
James Feistd3ec07f2019-02-25 14:51:15 -08001058 if (minThermalOutput)
James Feist5f2caae2018-12-12 14:08:25 -08001059 {
Ed Tanousb9d36b42022-02-26 21:42:46 -08001060 output.emplace_back("MinThermalOutput", *minThermalOutput);
James Feist5f2caae2018-12-12 14:08:25 -08001061 }
1062 if (failSafePercent)
1063 {
Ed Tanousb9d36b42022-02-26 21:42:46 -08001064 output.emplace_back("FailSafePercent", *failSafePercent);
James Feist5f2caae2018-12-12 14:08:25 -08001065 }
1066 }
1067 else if (type == "StepwiseControllers")
1068 {
Ed Tanousb9d36b42022-02-26 21:42:46 -08001069 output.emplace_back("Type", "Stepwise");
James Feist5f2caae2018-12-12 14:08:25 -08001070
1071 std::optional<std::vector<nlohmann::json>> zones;
1072 std::optional<std::vector<nlohmann::json>> steps;
1073 std::optional<std::vector<std::string>> inputs;
1074 std::optional<double> positiveHysteresis;
1075 std::optional<double> negativeHysteresis;
James Feistc33a90e2019-03-01 10:17:44 -08001076 std::optional<std::string> direction; // upper clipping curve vs lower
James Feist5f2caae2018-12-12 14:08:25 -08001077 if (!redfish::json_util::readJson(
James Feistb6baeaa2019-02-21 10:41:40 -08001078 it.value(), response->res, "Zones", zones, "Steps", steps,
1079 "Inputs", inputs, "PositiveHysteresis", positiveHysteresis,
James Feistc33a90e2019-03-01 10:17:44 -08001080 "NegativeHysteresis", negativeHysteresis, "Direction",
1081 direction))
James Feist5f2caae2018-12-12 14:08:25 -08001082 {
Ed Tanous71f52d92021-02-19 08:51:17 -08001083 BMCWEB_LOG_ERROR
1084 << "Illegal Property "
1085 << it.value().dump(2, ' ', true,
1086 nlohmann::json::error_handler_t::replace);
James Feist5f2caae2018-12-12 14:08:25 -08001087 return CreatePIDRet::fail;
1088 }
1089
1090 if (zones)
1091 {
James Feistb6baeaa2019-02-21 10:41:40 -08001092 std::vector<std::string> zonesStrs;
1093 if (!getZonesFromJsonReq(response, *zones, zonesStrs))
James Feist5f2caae2018-12-12 14:08:25 -08001094 {
Gunnar Millsa0744d32020-11-09 15:40:45 -06001095 BMCWEB_LOG_ERROR << "Illegal Zones";
James Feist5f2caae2018-12-12 14:08:25 -08001096 return CreatePIDRet::fail;
1097 }
James Feistb6baeaa2019-02-21 10:41:40 -08001098 if (chassis.empty() &&
Ed Tanouse662eae2022-01-25 10:39:19 -08001099 findChassis(managedObj, zonesStrs[0], chassis) == nullptr)
James Feistb6baeaa2019-02-21 10:41:40 -08001100 {
1101 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
Ed Tanousace85d62021-10-26 12:45:59 -07001102 messages::invalidObject(
1103 response->res, crow::utility::urlFromPieces(
1104 "redfish", "v1", "Chassis", chassis));
James Feistb6baeaa2019-02-21 10:41:40 -08001105 return CreatePIDRet::fail;
1106 }
Ed Tanousb9d36b42022-02-26 21:42:46 -08001107 output.emplace_back("Zones", std::move(zonesStrs));
James Feist5f2caae2018-12-12 14:08:25 -08001108 }
1109 if (steps)
1110 {
1111 std::vector<double> readings;
1112 std::vector<double> outputs;
1113 for (auto& step : *steps)
1114 {
Ed Tanous543f4402022-01-06 13:12:53 -08001115 double target = 0.0;
1116 double out = 0.0;
James Feist5f2caae2018-12-12 14:08:25 -08001117
1118 if (!redfish::json_util::readJson(step, response->res, "Target",
Ed Tanous23a21a12020-07-25 04:45:05 +00001119 target, "Output", out))
James Feist5f2caae2018-12-12 14:08:25 -08001120 {
Ed Tanous71f52d92021-02-19 08:51:17 -08001121 BMCWEB_LOG_ERROR
1122 << "Illegal Property "
1123 << it.value().dump(
1124 2, ' ', true,
1125 nlohmann::json::error_handler_t::replace);
James Feist5f2caae2018-12-12 14:08:25 -08001126 return CreatePIDRet::fail;
1127 }
1128 readings.emplace_back(target);
Ed Tanous23a21a12020-07-25 04:45:05 +00001129 outputs.emplace_back(out);
James Feist5f2caae2018-12-12 14:08:25 -08001130 }
Ed Tanousb9d36b42022-02-26 21:42:46 -08001131 output.emplace_back("Reading", std::move(readings));
1132 output.emplace_back("Output", std::move(outputs));
James Feist5f2caae2018-12-12 14:08:25 -08001133 }
1134 if (inputs)
1135 {
1136 for (std::string& value : *inputs)
1137 {
Ed Tanousa170f272022-06-30 21:53:27 -07001138
1139 std::replace(value.begin(), value.end(), '_', ' ');
James Feist5f2caae2018-12-12 14:08:25 -08001140 }
Ed Tanousb9d36b42022-02-26 21:42:46 -08001141 output.emplace_back("Inputs", std::move(*inputs));
James Feist5f2caae2018-12-12 14:08:25 -08001142 }
1143 if (negativeHysteresis)
1144 {
Ed Tanousb9d36b42022-02-26 21:42:46 -08001145 output.emplace_back("NegativeHysteresis", *negativeHysteresis);
James Feist5f2caae2018-12-12 14:08:25 -08001146 }
1147 if (positiveHysteresis)
1148 {
Ed Tanousb9d36b42022-02-26 21:42:46 -08001149 output.emplace_back("PositiveHysteresis", *positiveHysteresis);
James Feist83ff9ab2018-08-31 10:18:24 -07001150 }
James Feistc33a90e2019-03-01 10:17:44 -08001151 if (direction)
1152 {
1153 constexpr const std::array<const char*, 2> allowedDirections = {
1154 "Ceiling", "Floor"};
1155 if (std::find(allowedDirections.begin(), allowedDirections.end(),
1156 *direction) == allowedDirections.end())
1157 {
1158 messages::propertyValueTypeError(response->res, "Direction",
1159 *direction);
1160 return CreatePIDRet::fail;
1161 }
Ed Tanousb9d36b42022-02-26 21:42:46 -08001162 output.emplace_back("Class", *direction);
James Feistc33a90e2019-03-01 10:17:44 -08001163 }
James Feist83ff9ab2018-08-31 10:18:24 -07001164 }
1165 else
1166 {
Gunnar Millsa0744d32020-11-09 15:40:45 -06001167 BMCWEB_LOG_ERROR << "Illegal Type " << type;
Jason M. Bills35a62c72018-10-09 12:45:45 -07001168 messages::propertyUnknown(response->res, type);
James Feist83ff9ab2018-08-31 10:18:24 -07001169 return CreatePIDRet::fail;
1170 }
1171 return CreatePIDRet::patch;
1172}
James Feist73df0db2019-03-25 15:29:35 -07001173struct GetPIDValues : std::enable_shared_from_this<GetPIDValues>
1174{
1175
Ed Tanous4e23a442022-06-06 09:57:26 -07001176 explicit GetPIDValues(
1177 const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
Ed Tanous23a21a12020-07-25 04:45:05 +00001178 asyncResp(asyncRespIn)
James Feist73df0db2019-03-25 15:29:35 -07001179
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001180 {}
James Feist73df0db2019-03-25 15:29:35 -07001181
1182 void run()
1183 {
1184 std::shared_ptr<GetPIDValues> self = shared_from_this();
1185
1186 // get all configurations
1187 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -08001188 [self](
1189 const boost::system::error_code ec,
1190 const dbus::utility::MapperGetSubTreeResponse& subtreeLocal) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001191 if (ec)
1192 {
1193 BMCWEB_LOG_ERROR << ec;
1194 messages::internalError(self->asyncResp->res);
1195 return;
1196 }
1197 self->subtree = subtreeLocal;
James Feist73df0db2019-03-25 15:29:35 -07001198 },
1199 "xyz.openbmc_project.ObjectMapper",
1200 "/xyz/openbmc_project/object_mapper",
1201 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
1202 std::array<const char*, 4>{
1203 pidConfigurationIface, pidZoneConfigurationIface,
1204 objectManagerIface, stepwiseConfigurationIface});
1205
1206 // at the same time get the selected profile
1207 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -08001208 [self](
1209 const boost::system::error_code ec,
1210 const dbus::utility::MapperGetSubTreeResponse& subtreeLocal) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001211 if (ec || subtreeLocal.empty())
1212 {
1213 return;
1214 }
1215 if (subtreeLocal[0].second.size() != 1)
1216 {
1217 // invalid mapper response, should never happen
1218 BMCWEB_LOG_ERROR << "GetPIDValues: Mapper Error";
1219 messages::internalError(self->asyncResp->res);
1220 return;
1221 }
1222
1223 const std::string& path = subtreeLocal[0].first;
1224 const std::string& owner = subtreeLocal[0].second[0].first;
Krzysztof Grobelnyfac6e532022-08-04 12:42:45 +02001225
1226 sdbusplus::asio::getAllProperties(
1227 *crow::connections::systemBus, owner, path, thermalModeIface,
Ed Tanous002d39b2022-05-31 08:59:27 -07001228 [path, owner,
1229 self](const boost::system::error_code ec2,
1230 const dbus::utility::DBusPropertiesMap& resp) {
1231 if (ec2)
James Feist73df0db2019-03-25 15:29:35 -07001232 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001233 BMCWEB_LOG_ERROR
1234 << "GetPIDValues: Can't get thermalModeIface " << path;
James Feist73df0db2019-03-25 15:29:35 -07001235 messages::internalError(self->asyncResp->res);
1236 return;
1237 }
Krzysztof Grobelnyfac6e532022-08-04 12:42:45 +02001238
Ed Tanous002d39b2022-05-31 08:59:27 -07001239 const std::string* current = nullptr;
1240 const std::vector<std::string>* supported = nullptr;
Krzysztof Grobelnyfac6e532022-08-04 12:42:45 +02001241
1242 const bool success = sdbusplus::unpackPropertiesNoThrow(
1243 dbus_utils::UnpackErrorPrinter(), resp, "Current", current,
1244 "Supported", supported);
1245
1246 if (!success)
Ed Tanous002d39b2022-05-31 08:59:27 -07001247 {
Krzysztof Grobelnyfac6e532022-08-04 12:42:45 +02001248 messages::internalError(self->asyncResp->res);
1249 return;
Ed Tanous002d39b2022-05-31 08:59:27 -07001250 }
Krzysztof Grobelnyfac6e532022-08-04 12:42:45 +02001251
Ed Tanous002d39b2022-05-31 08:59:27 -07001252 if (current == nullptr || supported == nullptr)
1253 {
1254 BMCWEB_LOG_ERROR
1255 << "GetPIDValues: thermal mode iface invalid " << path;
1256 messages::internalError(self->asyncResp->res);
1257 return;
1258 }
1259 self->currentProfile = *current;
1260 self->supportedProfiles = *supported;
Krzysztof Grobelnyfac6e532022-08-04 12:42:45 +02001261 });
James Feist73df0db2019-03-25 15:29:35 -07001262 },
1263 "xyz.openbmc_project.ObjectMapper",
1264 "/xyz/openbmc_project/object_mapper",
1265 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
1266 std::array<const char*, 1>{thermalModeIface});
1267 }
1268
1269 ~GetPIDValues()
1270 {
1271 if (asyncResp->res.result() != boost::beast::http::status::ok)
1272 {
1273 return;
1274 }
1275 // create map of <connection, path to objMgr>>
1276 boost::container::flat_map<std::string, std::string> objectMgrPaths;
1277 boost::container::flat_set<std::string> calledConnections;
1278 for (const auto& pathGroup : subtree)
1279 {
1280 for (const auto& connectionGroup : pathGroup.second)
1281 {
1282 auto findConnection =
1283 calledConnections.find(connectionGroup.first);
1284 if (findConnection != calledConnections.end())
1285 {
1286 break;
1287 }
1288 for (const std::string& interface : connectionGroup.second)
1289 {
1290 if (interface == objectManagerIface)
1291 {
1292 objectMgrPaths[connectionGroup.first] = pathGroup.first;
1293 }
1294 // this list is alphabetical, so we
1295 // should have found the objMgr by now
1296 if (interface == pidConfigurationIface ||
1297 interface == pidZoneConfigurationIface ||
1298 interface == stepwiseConfigurationIface)
1299 {
1300 auto findObjMgr =
1301 objectMgrPaths.find(connectionGroup.first);
1302 if (findObjMgr == objectMgrPaths.end())
1303 {
1304 BMCWEB_LOG_DEBUG << connectionGroup.first
1305 << "Has no Object Manager";
1306 continue;
1307 }
1308
1309 calledConnections.insert(connectionGroup.first);
1310
1311 asyncPopulatePid(findObjMgr->first, findObjMgr->second,
1312 currentProfile, supportedProfiles,
1313 asyncResp);
1314 break;
1315 }
1316 }
1317 }
1318 }
1319 }
1320
Ed Tanousecd6a3a2022-01-07 09:18:40 -08001321 GetPIDValues(const GetPIDValues&) = delete;
1322 GetPIDValues(GetPIDValues&&) = delete;
1323 GetPIDValues& operator=(const GetPIDValues&) = delete;
1324 GetPIDValues& operator=(GetPIDValues&&) = delete;
1325
James Feist73df0db2019-03-25 15:29:35 -07001326 std::vector<std::string> supportedProfiles;
1327 std::string currentProfile;
Ed Tanousb9d36b42022-02-26 21:42:46 -08001328 dbus::utility::MapperGetSubTreeResponse subtree;
zhanghch058d1b46d2021-04-01 11:18:24 +08001329 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
James Feist73df0db2019-03-25 15:29:35 -07001330};
1331
1332struct SetPIDValues : std::enable_shared_from_this<SetPIDValues>
1333{
1334
zhanghch058d1b46d2021-04-01 11:18:24 +08001335 SetPIDValues(const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn,
James Feist73df0db2019-03-25 15:29:35 -07001336 nlohmann::json& data) :
Ed Tanous271584a2019-07-09 16:24:22 -07001337 asyncResp(asyncRespIn)
James Feist73df0db2019-03-25 15:29:35 -07001338 {
1339
1340 std::optional<nlohmann::json> pidControllers;
1341 std::optional<nlohmann::json> fanControllers;
1342 std::optional<nlohmann::json> fanZones;
1343 std::optional<nlohmann::json> stepwiseControllers;
1344
1345 if (!redfish::json_util::readJson(
1346 data, asyncResp->res, "PidControllers", pidControllers,
1347 "FanControllers", fanControllers, "FanZones", fanZones,
1348 "StepwiseControllers", stepwiseControllers, "Profile", profile))
1349 {
Ed Tanous71f52d92021-02-19 08:51:17 -08001350 BMCWEB_LOG_ERROR
1351 << "Illegal Property "
1352 << data.dump(2, ' ', true,
1353 nlohmann::json::error_handler_t::replace);
James Feist73df0db2019-03-25 15:29:35 -07001354 return;
1355 }
1356 configuration.emplace_back("PidControllers", std::move(pidControllers));
1357 configuration.emplace_back("FanControllers", std::move(fanControllers));
1358 configuration.emplace_back("FanZones", std::move(fanZones));
1359 configuration.emplace_back("StepwiseControllers",
1360 std::move(stepwiseControllers));
1361 }
Ed Tanousecd6a3a2022-01-07 09:18:40 -08001362
1363 SetPIDValues(const SetPIDValues&) = delete;
1364 SetPIDValues(SetPIDValues&&) = delete;
1365 SetPIDValues& operator=(const SetPIDValues&) = delete;
1366 SetPIDValues& operator=(SetPIDValues&&) = delete;
1367
James Feist73df0db2019-03-25 15:29:35 -07001368 void run()
1369 {
1370 if (asyncResp->res.result() != boost::beast::http::status::ok)
1371 {
1372 return;
1373 }
1374
1375 std::shared_ptr<SetPIDValues> self = shared_from_this();
1376
1377 // todo(james): might make sense to do a mapper call here if this
1378 // interface gets more traction
1379 crow::connections::systemBus->async_method_call(
1380 [self](const boost::system::error_code ec,
Ed Tanous914e2d52022-01-07 11:38:34 -08001381 const dbus::utility::ManagedObjectType& mObj) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001382 if (ec)
1383 {
1384 BMCWEB_LOG_ERROR << "Error communicating to Entity Manager";
1385 messages::internalError(self->asyncResp->res);
1386 return;
1387 }
1388 const std::array<const char*, 3> configurations = {
1389 pidConfigurationIface, pidZoneConfigurationIface,
1390 stepwiseConfigurationIface};
James Feiste69d9de2020-02-07 12:23:27 -08001391
Ed Tanous002d39b2022-05-31 08:59:27 -07001392 for (const auto& [path, object] : mObj)
1393 {
1394 for (const auto& [interface, _] : object)
James Feiste69d9de2020-02-07 12:23:27 -08001395 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001396 if (std::find(configurations.begin(), configurations.end(),
1397 interface) != configurations.end())
James Feiste69d9de2020-02-07 12:23:27 -08001398 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001399 self->objectCount++;
1400 break;
James Feiste69d9de2020-02-07 12:23:27 -08001401 }
James Feiste69d9de2020-02-07 12:23:27 -08001402 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001403 }
1404 self->managedObj = mObj;
James Feist73df0db2019-03-25 15:29:35 -07001405 },
1406 "xyz.openbmc_project.EntityManager", "/", objectManagerIface,
1407 "GetManagedObjects");
1408
1409 // at the same time get the profile information
1410 crow::connections::systemBus->async_method_call(
1411 [self](const boost::system::error_code ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -08001412 const dbus::utility::MapperGetSubTreeResponse& subtree) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001413 if (ec || subtree.empty())
1414 {
1415 return;
1416 }
1417 if (subtree[0].second.empty())
1418 {
1419 // invalid mapper response, should never happen
1420 BMCWEB_LOG_ERROR << "SetPIDValues: Mapper Error";
1421 messages::internalError(self->asyncResp->res);
1422 return;
1423 }
1424
1425 const std::string& path = subtree[0].first;
1426 const std::string& owner = subtree[0].second[0].first;
Krzysztof Grobelnyfac6e532022-08-04 12:42:45 +02001427 sdbusplus::asio::getAllProperties(
1428 *crow::connections::systemBus, owner, path, thermalModeIface,
Ed Tanous002d39b2022-05-31 08:59:27 -07001429 [self, path, owner](const boost::system::error_code ec2,
1430 const dbus::utility::DBusPropertiesMap& r) {
1431 if (ec2)
James Feist73df0db2019-03-25 15:29:35 -07001432 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001433 BMCWEB_LOG_ERROR
1434 << "SetPIDValues: Can't get thermalModeIface " << path;
James Feist73df0db2019-03-25 15:29:35 -07001435 messages::internalError(self->asyncResp->res);
1436 return;
1437 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001438 const std::string* current = nullptr;
1439 const std::vector<std::string>* supported = nullptr;
Krzysztof Grobelnyfac6e532022-08-04 12:42:45 +02001440
1441 const bool success = sdbusplus::unpackPropertiesNoThrow(
1442 dbus_utils::UnpackErrorPrinter(), r, "Current", current,
1443 "Supported", supported);
1444
1445 if (!success)
Ed Tanous002d39b2022-05-31 08:59:27 -07001446 {
Krzysztof Grobelnyfac6e532022-08-04 12:42:45 +02001447 messages::internalError(self->asyncResp->res);
1448 return;
Ed Tanous002d39b2022-05-31 08:59:27 -07001449 }
Krzysztof Grobelnyfac6e532022-08-04 12:42:45 +02001450
Ed Tanous002d39b2022-05-31 08:59:27 -07001451 if (current == nullptr || supported == nullptr)
1452 {
1453 BMCWEB_LOG_ERROR
1454 << "SetPIDValues: thermal mode iface invalid " << 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;
Krzysztof Grobelnyfac6e532022-08-04 12:42:45 +02001462 });
James Feist73df0db2019-03-25 15:29:35 -07001463 },
1464 "xyz.openbmc_project.ObjectMapper",
1465 "/xyz/openbmc_project/object_mapper",
1466 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
1467 std::array<const char*, 1>{thermalModeIface});
1468 }
Ed Tanous24b2fe82022-01-06 12:45:54 -08001469 void pidSetDone()
James Feist73df0db2019-03-25 15:29:35 -07001470 {
1471 if (asyncResp->res.result() != boost::beast::http::status::ok)
1472 {
1473 return;
1474 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001475 std::shared_ptr<bmcweb::AsyncResp> response = asyncResp;
James Feist73df0db2019-03-25 15:29:35 -07001476 if (profile)
1477 {
1478 if (std::find(supportedProfiles.begin(), supportedProfiles.end(),
1479 *profile) == supportedProfiles.end())
1480 {
1481 messages::actionParameterUnknown(response->res, "Profile",
1482 *profile);
1483 return;
1484 }
1485 currentProfile = *profile;
1486 crow::connections::systemBus->async_method_call(
1487 [response](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001488 if (ec)
1489 {
1490 BMCWEB_LOG_ERROR << "Error patching profile" << ec;
1491 messages::internalError(response->res);
1492 }
James Feist73df0db2019-03-25 15:29:35 -07001493 },
1494 profileConnection, profilePath,
1495 "org.freedesktop.DBus.Properties", "Set", thermalModeIface,
Ed Tanous168e20c2021-12-13 14:39:53 -08001496 "Current", dbus::utility::DbusVariantType(*profile));
James Feist73df0db2019-03-25 15:29:35 -07001497 }
1498
1499 for (auto& containerPair : configuration)
1500 {
1501 auto& container = containerPair.second;
1502 if (!container)
1503 {
1504 continue;
1505 }
James Feist6ee7f772020-02-06 16:25:27 -08001506 BMCWEB_LOG_DEBUG << *container;
1507
Ed Tanous02cad962022-06-30 16:50:15 -07001508 const std::string& type = containerPair.first;
James Feist73df0db2019-03-25 15:29:35 -07001509
1510 for (nlohmann::json::iterator it = container->begin();
Manojkiran Eda17a897d2020-09-12 15:31:58 +05301511 it != container->end(); ++it)
James Feist73df0db2019-03-25 15:29:35 -07001512 {
1513 const auto& name = it.key();
James Feist6ee7f772020-02-06 16:25:27 -08001514 BMCWEB_LOG_DEBUG << "looking for " << name;
1515
James Feist73df0db2019-03-25 15:29:35 -07001516 auto pathItr =
1517 std::find_if(managedObj.begin(), managedObj.end(),
1518 [&name](const auto& obj) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001519 return boost::algorithm::ends_with(obj.first.str,
1520 "/" + name);
1521 });
Ed Tanousb9d36b42022-02-26 21:42:46 -08001522 dbus::utility::DBusPropertiesMap output;
James Feist73df0db2019-03-25 15:29:35 -07001523
1524 output.reserve(16); // The pid interface length
1525
1526 // determines if we're patching entity-manager or
1527 // creating a new object
1528 bool createNewObject = (pathItr == managedObj.end());
James Feist6ee7f772020-02-06 16:25:27 -08001529 BMCWEB_LOG_DEBUG << "Found = " << !createNewObject;
1530
James Feist73df0db2019-03-25 15:29:35 -07001531 std::string iface;
Ed Tanous711ac7a2021-12-20 09:34:41 -08001532 /*
James Feist73df0db2019-03-25 15:29:35 -07001533 if (type == "PidControllers" || type == "FanControllers")
1534 {
1535 iface = pidConfigurationIface;
1536 if (!createNewObject &&
1537 pathItr->second.find(pidConfigurationIface) ==
1538 pathItr->second.end())
1539 {
1540 createNewObject = true;
1541 }
1542 }
1543 else if (type == "FanZones")
1544 {
1545 iface = pidZoneConfigurationIface;
1546 if (!createNewObject &&
1547 pathItr->second.find(pidZoneConfigurationIface) ==
1548 pathItr->second.end())
1549 {
1550
1551 createNewObject = true;
1552 }
1553 }
1554 else if (type == "StepwiseControllers")
1555 {
1556 iface = stepwiseConfigurationIface;
1557 if (!createNewObject &&
1558 pathItr->second.find(stepwiseConfigurationIface) ==
1559 pathItr->second.end())
1560 {
1561 createNewObject = true;
1562 }
Ed Tanous711ac7a2021-12-20 09:34:41 -08001563 }*/
James Feist6ee7f772020-02-06 16:25:27 -08001564
1565 if (createNewObject && it.value() == nullptr)
1566 {
Gunnar Mills4e0453b2020-07-08 14:00:30 -05001567 // can't delete a non-existent object
Ed Tanous1668ce62022-02-07 23:44:31 -08001568 messages::propertyValueNotInList(response->res,
1569 it.value().dump(), name);
James Feist6ee7f772020-02-06 16:25:27 -08001570 continue;
1571 }
1572
1573 std::string path;
1574 if (pathItr != managedObj.end())
1575 {
1576 path = pathItr->first.str;
1577 }
1578
James Feist73df0db2019-03-25 15:29:35 -07001579 BMCWEB_LOG_DEBUG << "Create new = " << createNewObject << "\n";
James Feiste69d9de2020-02-07 12:23:27 -08001580
1581 // arbitrary limit to avoid attacks
1582 constexpr const size_t controllerLimit = 500;
James Feist14b0b8d2020-02-12 11:52:07 -08001583 if (createNewObject && objectCount >= controllerLimit)
James Feiste69d9de2020-02-07 12:23:27 -08001584 {
1585 messages::resourceExhaustion(response->res, type);
1586 continue;
1587 }
Ed Tanousa170f272022-06-30 21:53:27 -07001588 std::string escaped = name;
1589 std::replace(escaped.begin(), escaped.end(), '_', ' ');
1590 output.emplace_back("Name", escaped);
James Feist73df0db2019-03-25 15:29:35 -07001591
1592 std::string chassis;
1593 CreatePIDRet ret = createPidInterface(
James Feist6ee7f772020-02-06 16:25:27 -08001594 response, type, it, path, managedObj, createNewObject,
1595 output, chassis, currentProfile);
James Feist73df0db2019-03-25 15:29:35 -07001596 if (ret == CreatePIDRet::fail)
1597 {
1598 return;
1599 }
Ed Tanous3174e4d2020-10-07 11:41:22 -07001600 if (ret == CreatePIDRet::del)
James Feist73df0db2019-03-25 15:29:35 -07001601 {
1602 continue;
1603 }
1604
1605 if (!createNewObject)
1606 {
1607 for (const auto& property : output)
1608 {
1609 crow::connections::systemBus->async_method_call(
1610 [response,
1611 propertyName{std::string(property.first)}](
1612 const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001613 if (ec)
1614 {
1615 BMCWEB_LOG_ERROR << "Error patching "
1616 << propertyName << ": " << ec;
1617 messages::internalError(response->res);
1618 return;
1619 }
1620 messages::success(response->res);
James Feist73df0db2019-03-25 15:29:35 -07001621 },
James Feist6ee7f772020-02-06 16:25:27 -08001622 "xyz.openbmc_project.EntityManager", path,
James Feist73df0db2019-03-25 15:29:35 -07001623 "org.freedesktop.DBus.Properties", "Set", iface,
1624 property.first, property.second);
1625 }
1626 }
1627 else
1628 {
1629 if (chassis.empty())
1630 {
1631 BMCWEB_LOG_ERROR << "Failed to get chassis from config";
Ed Tanousace85d62021-10-26 12:45:59 -07001632 messages::internalError(response->res);
James Feist73df0db2019-03-25 15:29:35 -07001633 return;
1634 }
1635
1636 bool foundChassis = false;
1637 for (const auto& obj : managedObj)
1638 {
1639 if (boost::algorithm::ends_with(obj.first.str, chassis))
1640 {
1641 chassis = obj.first.str;
1642 foundChassis = true;
1643 break;
1644 }
1645 }
1646 if (!foundChassis)
1647 {
1648 BMCWEB_LOG_ERROR << "Failed to find chassis on dbus";
1649 messages::resourceMissingAtURI(
Ed Tanousace85d62021-10-26 12:45:59 -07001650 response->res,
1651 crow::utility::urlFromPieces("redfish", "v1",
1652 "Chassis", chassis));
James Feist73df0db2019-03-25 15:29:35 -07001653 return;
1654 }
1655
1656 crow::connections::systemBus->async_method_call(
1657 [response](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001658 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);
James Feist73df0db2019-03-25 15:29:35 -07001666 },
1667 "xyz.openbmc_project.EntityManager", chassis,
1668 "xyz.openbmc_project.AddObject", "AddObject", output);
1669 }
1670 }
1671 }
1672 }
Ed Tanous24b2fe82022-01-06 12:45:54 -08001673
1674 ~SetPIDValues()
1675 {
1676 try
1677 {
1678 pidSetDone();
1679 }
1680 catch (...)
1681 {
1682 BMCWEB_LOG_CRITICAL << "pidSetDone threw exception";
1683 }
1684 }
1685
zhanghch058d1b46d2021-04-01 11:18:24 +08001686 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
James Feist73df0db2019-03-25 15:29:35 -07001687 std::vector<std::pair<std::string, std::optional<nlohmann::json>>>
1688 configuration;
1689 std::optional<std::string> profile;
1690 dbus::utility::ManagedObjectType managedObj;
1691 std::vector<std::string> supportedProfiles;
1692 std::string currentProfile;
1693 std::string profileConnection;
1694 std::string profilePath;
James Feist14b0b8d2020-02-12 11:52:07 -08001695 size_t objectCount = 0;
James Feist73df0db2019-03-25 15:29:35 -07001696};
James Feist83ff9ab2018-08-31 10:18:24 -07001697
SunnySrivastava1984071d8fd2020-10-28 02:20:30 -05001698/**
1699 * @brief Retrieves BMC manager location data over DBus
1700 *
1701 * @param[in] aResp Shared pointer for completing asynchronous calls
1702 * @param[in] connectionName - service name
1703 * @param[in] path - object path
1704 * @return none
1705 */
zhanghch058d1b46d2021-04-01 11:18:24 +08001706inline void getLocation(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
SunnySrivastava1984071d8fd2020-10-28 02:20:30 -05001707 const std::string& connectionName,
1708 const std::string& path)
1709{
1710 BMCWEB_LOG_DEBUG << "Get BMC manager Location data.";
1711
Jonathan Doman1e1e5982021-06-11 09:36:17 -07001712 sdbusplus::asio::getProperty<std::string>(
1713 *crow::connections::systemBus, connectionName, path,
1714 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode",
SunnySrivastava1984071d8fd2020-10-28 02:20:30 -05001715 [aResp](const boost::system::error_code ec,
Jonathan Doman1e1e5982021-06-11 09:36:17 -07001716 const std::string& property) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001717 if (ec)
1718 {
1719 BMCWEB_LOG_DEBUG << "DBUS response error for "
1720 "Location";
1721 messages::internalError(aResp->res);
1722 return;
1723 }
SunnySrivastava1984071d8fd2020-10-28 02:20:30 -05001724
Ed Tanous002d39b2022-05-31 08:59:27 -07001725 aResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
1726 property;
Jonathan Doman1e1e5982021-06-11 09:36:17 -07001727 });
SunnySrivastava1984071d8fd2020-10-28 02:20:30 -05001728}
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001729// avoid name collision systems.hpp
1730inline void
1731 managerGetLastResetTime(const std::shared_ptr<bmcweb::AsyncResp>& aResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001732{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001733 BMCWEB_LOG_DEBUG << "Getting Manager Last Reset Time";
Ed Tanous52cc1122020-07-18 13:51:21 -07001734
Jonathan Doman1e1e5982021-06-11 09:36:17 -07001735 sdbusplus::asio::getProperty<uint64_t>(
1736 *crow::connections::systemBus, "xyz.openbmc_project.State.BMC",
1737 "/xyz/openbmc_project/state/bmc0", "xyz.openbmc_project.State.BMC",
1738 "LastRebootTime",
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001739 [aResp](const boost::system::error_code ec,
Jonathan Doman1e1e5982021-06-11 09:36:17 -07001740 const uint64_t lastResetTime) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001741 if (ec)
1742 {
1743 BMCWEB_LOG_DEBUG << "D-BUS response error " << ec;
1744 return;
1745 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001746
Ed Tanous002d39b2022-05-31 08:59:27 -07001747 // LastRebootTime is epoch time, in milliseconds
1748 // https://github.com/openbmc/phosphor-dbus-interfaces/blob/7f9a128eb9296e926422ddc312c148b625890bb6/xyz/openbmc_project/State/BMC.interface.yaml#L19
1749 uint64_t lastResetTimeStamp = lastResetTime / 1000;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001750
Ed Tanous002d39b2022-05-31 08:59:27 -07001751 // Convert to ISO 8601 standard
1752 aResp->res.jsonValue["LastResetTime"] =
1753 crow::utility::getDateTimeUint(lastResetTimeStamp);
Jonathan Doman1e1e5982021-06-11 09:36:17 -07001754 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001755}
1756
1757/**
1758 * @brief Set the running firmware image
1759 *
1760 * @param[i,o] aResp - Async response object
1761 * @param[i] runningFirmwareTarget - Image to make the running image
1762 *
1763 * @return void
1764 */
1765inline void
1766 setActiveFirmwareImage(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
1767 const std::string& runningFirmwareTarget)
1768{
1769 // Get the Id from /redfish/v1/UpdateService/FirmwareInventory/<Id>
1770 std::string::size_type idPos = runningFirmwareTarget.rfind('/');
1771 if (idPos == std::string::npos)
1772 {
1773 messages::propertyValueNotInList(aResp->res, runningFirmwareTarget,
1774 "@odata.id");
1775 BMCWEB_LOG_DEBUG << "Can't parse firmware ID!";
1776 return;
1777 }
1778 idPos++;
1779 if (idPos >= runningFirmwareTarget.size())
1780 {
1781 messages::propertyValueNotInList(aResp->res, runningFirmwareTarget,
1782 "@odata.id");
1783 BMCWEB_LOG_DEBUG << "Invalid firmware ID.";
1784 return;
1785 }
1786 std::string firmwareId = runningFirmwareTarget.substr(idPos);
1787
1788 // Make sure the image is valid before setting priority
1789 crow::connections::systemBus->async_method_call(
Ed Tanous711ac7a2021-12-20 09:34:41 -08001790 [aResp, firmwareId,
1791 runningFirmwareTarget](const boost::system::error_code ec,
1792 dbus::utility::ManagedObjectType& subtree) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001793 if (ec)
1794 {
1795 BMCWEB_LOG_DEBUG << "D-Bus response error getting objects.";
1796 messages::internalError(aResp->res);
1797 return;
1798 }
1799
1800 if (subtree.empty())
1801 {
1802 BMCWEB_LOG_DEBUG << "Can't find image!";
1803 messages::internalError(aResp->res);
1804 return;
1805 }
1806
1807 bool foundImage = false;
Ed Tanous02cad962022-06-30 16:50:15 -07001808 for (const auto& object : subtree)
Ed Tanous002d39b2022-05-31 08:59:27 -07001809 {
1810 const std::string& path =
1811 static_cast<const std::string&>(object.first);
1812 std::size_t idPos2 = path.rfind('/');
1813
1814 if (idPos2 == std::string::npos)
1815 {
1816 continue;
1817 }
1818
1819 idPos2++;
1820 if (idPos2 >= path.size())
1821 {
1822 continue;
1823 }
1824
1825 if (path.substr(idPos2) == firmwareId)
1826 {
1827 foundImage = true;
1828 break;
1829 }
1830 }
1831
1832 if (!foundImage)
1833 {
1834 messages::propertyValueNotInList(aResp->res, runningFirmwareTarget,
1835 "@odata.id");
1836 BMCWEB_LOG_DEBUG << "Invalid firmware ID.";
1837 return;
1838 }
1839
1840 BMCWEB_LOG_DEBUG << "Setting firmware version " << firmwareId
1841 << " to priority 0.";
1842
1843 // Only support Immediate
1844 // An addition could be a Redfish Setting like
1845 // ActiveSoftwareImageApplyTime and support OnReset
1846 crow::connections::systemBus->async_method_call(
Ed Tanous8a592812022-06-04 09:06:59 -07001847 [aResp](const boost::system::error_code ec2) {
1848 if (ec2)
Gunnar Mills4bfefa72020-07-30 13:54:29 -05001849 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001850 BMCWEB_LOG_DEBUG << "D-Bus response error setting.";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001851 messages::internalError(aResp->res);
1852 return;
1853 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001854 doBMCGracefulRestart(aResp);
1855 },
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001856
Ed Tanous002d39b2022-05-31 08:59:27 -07001857 "xyz.openbmc_project.Software.BMC.Updater",
1858 "/xyz/openbmc_project/software/" + firmwareId,
1859 "org.freedesktop.DBus.Properties", "Set",
1860 "xyz.openbmc_project.Software.RedundancyPriority", "Priority",
1861 dbus::utility::DbusVariantType(static_cast<uint8_t>(0)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001862 },
1863 "xyz.openbmc_project.Software.BMC.Updater",
1864 "/xyz/openbmc_project/software", "org.freedesktop.DBus.ObjectManager",
1865 "GetManagedObjects");
1866}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001867
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001868inline void setDateTime(std::shared_ptr<bmcweb::AsyncResp> aResp,
1869 std::string datetime)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001870{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001871 BMCWEB_LOG_DEBUG << "Set date time: " << datetime;
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001872
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001873 std::stringstream stream(datetime);
1874 // Convert from ISO 8601 to boost local_time
1875 // (BMC only has time in UTC)
1876 boost::posix_time::ptime posixTime;
1877 boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
1878 // Facet gets deleted with the stringsteam
1879 auto ifc = std::make_unique<boost::local_time::local_time_input_facet>(
1880 "%Y-%m-%d %H:%M:%S%F %ZP");
1881 stream.imbue(std::locale(stream.getloc(), ifc.release()));
1882
1883 boost::local_time::local_date_time ldt(boost::local_time::not_a_date_time);
1884
1885 if (stream >> ldt)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001886 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001887 posixTime = ldt.utc_time();
1888 boost::posix_time::time_duration dur = posixTime - epoch;
1889 uint64_t durMicroSecs = static_cast<uint64_t>(dur.total_microseconds());
1890 crow::connections::systemBus->async_method_call(
1891 [aResp{std::move(aResp)}, datetime{std::move(datetime)}](
1892 const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001893 if (ec)
1894 {
1895 BMCWEB_LOG_DEBUG << "Failed to set elapsed time. "
1896 "DBUS response error "
1897 << ec;
1898 messages::internalError(aResp->res);
1899 return;
1900 }
1901 aResp->res.jsonValue["DateTime"] = datetime;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001902 },
1903 "xyz.openbmc_project.Time.Manager", "/xyz/openbmc_project/time/bmc",
1904 "org.freedesktop.DBus.Properties", "Set",
1905 "xyz.openbmc_project.Time.EpochTime", "Elapsed",
Ed Tanous168e20c2021-12-13 14:39:53 -08001906 dbus::utility::DbusVariantType(durMicroSecs));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001907 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001908 else
1909 {
1910 messages::propertyValueFormatError(aResp->res, datetime, "DateTime");
1911 return;
1912 }
1913}
1914
1915inline void requestRoutesManager(App& app)
1916{
1917 std::string uuid = persistent_data::getConfig().systemUuid;
1918
1919 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/")
Ed Tanoused398212021-06-09 17:05:54 -07001920 .privileges(redfish::privileges::getManager)
Ed Tanous002d39b2022-05-31 08:59:27 -07001921 .methods(boost::beast::http::verb::get)(
1922 [&app, uuid](const crow::Request& req,
Ed Tanous45ca1b82022-03-25 13:07:27 -07001923 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001924 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001925 {
1926 return;
1927 }
1928 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Managers/bmc";
Sui Chena51fc2d2022-07-14 17:21:53 -07001929 asyncResp->res.jsonValue["@odata.type"] = "#Manager.v1_14_0.Manager";
Ed Tanous002d39b2022-05-31 08:59:27 -07001930 asyncResp->res.jsonValue["Id"] = "bmc";
1931 asyncResp->res.jsonValue["Name"] = "OpenBmc Manager";
1932 asyncResp->res.jsonValue["Description"] =
1933 "Baseboard Management Controller";
1934 asyncResp->res.jsonValue["PowerState"] = "On";
1935 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
1936 asyncResp->res.jsonValue["Status"]["Health"] = "OK";
Ed Tanous14766872022-03-15 10:44:42 -07001937
Ed Tanous002d39b2022-05-31 08:59:27 -07001938 asyncResp->res.jsonValue["ManagerType"] = "BMC";
1939 asyncResp->res.jsonValue["UUID"] = systemd_utils::getUuid();
1940 asyncResp->res.jsonValue["ServiceEntryPointUUID"] = uuid;
1941 asyncResp->res.jsonValue["Model"] = "OpenBmc"; // TODO(ed), get model
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001942
Ed Tanous002d39b2022-05-31 08:59:27 -07001943 asyncResp->res.jsonValue["LogServices"]["@odata.id"] =
1944 "/redfish/v1/Managers/bmc/LogServices";
1945 asyncResp->res.jsonValue["NetworkProtocol"]["@odata.id"] =
1946 "/redfish/v1/Managers/bmc/NetworkProtocol";
1947 asyncResp->res.jsonValue["EthernetInterfaces"]["@odata.id"] =
1948 "/redfish/v1/Managers/bmc/EthernetInterfaces";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001949
1950#ifdef BMCWEB_ENABLE_VM_NBDPROXY
Ed Tanous002d39b2022-05-31 08:59:27 -07001951 asyncResp->res.jsonValue["VirtualMedia"]["@odata.id"] =
1952 "/redfish/v1/Managers/bmc/VirtualMedia";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001953#endif // BMCWEB_ENABLE_VM_NBDPROXY
1954
Ed Tanous002d39b2022-05-31 08:59:27 -07001955 // default oem data
1956 nlohmann::json& oem = asyncResp->res.jsonValue["Oem"];
1957 nlohmann::json& oemOpenbmc = oem["OpenBmc"];
1958 oem["@odata.type"] = "#OemManager.Oem";
1959 oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem";
1960 oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc";
1961 oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc";
Ed Tanous14766872022-03-15 10:44:42 -07001962
Ed Tanous002d39b2022-05-31 08:59:27 -07001963 nlohmann::json::object_t certificates;
1964 certificates["@odata.id"] =
1965 "/redfish/v1/Managers/bmc/Truststore/Certificates";
1966 oemOpenbmc["Certificates"] = std::move(certificates);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001967
Ed Tanous002d39b2022-05-31 08:59:27 -07001968 // Manager.Reset (an action) can be many values, OpenBMC only
1969 // supports BMC reboot.
1970 nlohmann::json& managerReset =
1971 asyncResp->res.jsonValue["Actions"]["#Manager.Reset"];
1972 managerReset["target"] =
1973 "/redfish/v1/Managers/bmc/Actions/Manager.Reset";
1974 managerReset["@Redfish.ActionInfo"] =
1975 "/redfish/v1/Managers/bmc/ResetActionInfo";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001976
Ed Tanous002d39b2022-05-31 08:59:27 -07001977 // ResetToDefaults (Factory Reset) has values like
1978 // PreserveNetworkAndUsers and PreserveNetwork that aren't supported
1979 // on OpenBMC
1980 nlohmann::json& resetToDefaults =
1981 asyncResp->res.jsonValue["Actions"]["#Manager.ResetToDefaults"];
1982 resetToDefaults["target"] =
1983 "/redfish/v1/Managers/bmc/Actions/Manager.ResetToDefaults";
1984 resetToDefaults["ResetType@Redfish.AllowableValues"] = {"ResetAll"};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001985
Ed Tanous002d39b2022-05-31 08:59:27 -07001986 std::pair<std::string, std::string> redfishDateTimeOffset =
1987 crow::utility::getDateTimeOffsetNow();
Tejas Patil7c8c4052021-06-04 17:43:14 +05301988
Ed Tanous002d39b2022-05-31 08:59:27 -07001989 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
1990 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
1991 redfishDateTimeOffset.second;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001992
Ed Tanous002d39b2022-05-31 08:59:27 -07001993 // TODO (Gunnar): Remove these one day since moved to ComputerSystem
1994 // Still used by OCP profiles
1995 // https://github.com/opencomputeproject/OCP-Profiles/issues/23
1996 // Fill in SerialConsole info
1997 asyncResp->res.jsonValue["SerialConsole"]["ServiceEnabled"] = true;
1998 asyncResp->res.jsonValue["SerialConsole"]["MaxConcurrentSessions"] = 15;
1999 asyncResp->res.jsonValue["SerialConsole"]["ConnectTypesSupported"] = {
2000 "IPMI", "SSH"};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002001#ifdef BMCWEB_ENABLE_KVM
Ed Tanous002d39b2022-05-31 08:59:27 -07002002 // Fill in GraphicalConsole info
2003 asyncResp->res.jsonValue["GraphicalConsole"]["ServiceEnabled"] = true;
2004 asyncResp->res.jsonValue["GraphicalConsole"]["MaxConcurrentSessions"] =
2005 4;
2006 asyncResp->res
2007 .jsonValue["GraphicalConsole"]["ConnectTypesSupported"] = {"KVMIP"};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002008#endif // BMCWEB_ENABLE_KVM
2009
Ed Tanous002d39b2022-05-31 08:59:27 -07002010 asyncResp->res.jsonValue["Links"]["ManagerForServers@odata.count"] = 1;
Ed Tanous14766872022-03-15 10:44:42 -07002011
Ed Tanous002d39b2022-05-31 08:59:27 -07002012 nlohmann::json::array_t managerForServers;
2013 nlohmann::json::object_t manager;
2014 manager["@odata.id"] = "/redfish/v1/Systems/system";
2015 managerForServers.push_back(std::move(manager));
2016
2017 asyncResp->res.jsonValue["Links"]["ManagerForServers"] =
2018 std::move(managerForServers);
2019
2020 auto health = std::make_shared<HealthPopulate>(asyncResp);
2021 health->isManagersHealth = true;
2022 health->populate();
2023
Willy Tueee00132022-06-14 14:53:17 -07002024 sw_util::populateSoftwareInformation(asyncResp, sw_util::bmcPurpose,
Ed Tanous002d39b2022-05-31 08:59:27 -07002025 "FirmwareVersion", true);
2026
2027 managerGetLastResetTime(asyncResp);
2028
Sui Chena51fc2d2022-07-14 17:21:53 -07002029 // ManagerDiagnosticData is added for all BMCs.
2030 nlohmann::json& managerDiagnosticData =
2031 asyncResp->res.jsonValue["ManagerDiagnosticData"];
2032 managerDiagnosticData["@odata.id"] =
2033 "/redfish/v1/Managers/bmc/ManagerDiagnosticData";
2034
Gunnar Mills54dce7f2022-08-05 17:01:32 +00002035#ifdef BMCWEB_ENABLE_REDFISH_OEM_MANAGER_FAN_DATA
Ed Tanous002d39b2022-05-31 08:59:27 -07002036 auto pids = std::make_shared<GetPIDValues>(asyncResp);
2037 pids->run();
Gunnar Mills54dce7f2022-08-05 17:01:32 +00002038#endif
Ed Tanous002d39b2022-05-31 08:59:27 -07002039
2040 getMainChassisId(asyncResp,
2041 [](const std::string& chassisId,
2042 const std::shared_ptr<bmcweb::AsyncResp>& aRsp) {
2043 aRsp->res.jsonValue["Links"]["ManagerForChassis@odata.count"] = 1;
2044 nlohmann::json::array_t managerForChassis;
Ed Tanous8a592812022-06-04 09:06:59 -07002045 nlohmann::json::object_t managerObj;
2046 managerObj["@odata.id"] = "/redfish/v1/Chassis/" + chassisId;
2047 managerForChassis.push_back(std::move(managerObj));
Ed Tanous002d39b2022-05-31 08:59:27 -07002048 aRsp->res.jsonValue["Links"]["ManagerForChassis"] =
2049 std::move(managerForChassis);
2050 aRsp->res.jsonValue["Links"]["ManagerInChassis"]["@odata.id"] =
2051 "/redfish/v1/Chassis/" + chassisId;
2052 });
Ed Tanous14766872022-03-15 10:44:42 -07002053
Ed Tanous002d39b2022-05-31 08:59:27 -07002054 static bool started = false;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002055
Ed Tanous002d39b2022-05-31 08:59:27 -07002056 if (!started)
2057 {
2058 sdbusplus::asio::getProperty<double>(
2059 *crow::connections::systemBus, "org.freedesktop.systemd1",
2060 "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager",
2061 "Progress",
2062 [asyncResp](const boost::system::error_code ec,
2063 const double& val) {
2064 if (ec)
2065 {
2066 BMCWEB_LOG_ERROR << "Error while getting progress";
2067 messages::internalError(asyncResp->res);
2068 return;
2069 }
2070 if (val < 1.0)
2071 {
2072 asyncResp->res.jsonValue["Status"]["State"] = "Starting";
2073 started = true;
2074 }
2075 });
2076 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002077
Ed Tanous002d39b2022-05-31 08:59:27 -07002078 crow::connections::systemBus->async_method_call(
2079 [asyncResp](
2080 const boost::system::error_code ec,
2081 const dbus::utility::MapperGetSubTreeResponse& subtree) {
2082 if (ec)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002083 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002084 BMCWEB_LOG_DEBUG << "D-Bus response error on GetSubTree " << ec;
2085 return;
2086 }
2087 if (subtree.empty())
2088 {
2089 BMCWEB_LOG_DEBUG << "Can't find bmc D-Bus object!";
2090 return;
2091 }
2092 // Assume only 1 bmc D-Bus object
2093 // Throw an error if there is more than 1
2094 if (subtree.size() > 1)
2095 {
2096 BMCWEB_LOG_DEBUG << "Found more than 1 bmc D-Bus object!";
2097 messages::internalError(asyncResp->res);
2098 return;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002099 }
2100
Ed Tanous002d39b2022-05-31 08:59:27 -07002101 if (subtree[0].first.empty() || subtree[0].second.size() != 1)
2102 {
2103 BMCWEB_LOG_DEBUG << "Error getting bmc D-Bus object!";
2104 messages::internalError(asyncResp->res);
2105 return;
2106 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002107
Ed Tanous002d39b2022-05-31 08:59:27 -07002108 const std::string& path = subtree[0].first;
2109 const std::string& connectionName = subtree[0].second[0].first;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002110
Ed Tanous002d39b2022-05-31 08:59:27 -07002111 for (const auto& interfaceName : subtree[0].second[0].second)
2112 {
2113 if (interfaceName ==
2114 "xyz.openbmc_project.Inventory.Decorator.Asset")
2115 {
Krzysztof Grobelnyfac6e532022-08-04 12:42:45 +02002116
2117 sdbusplus::asio::getAllProperties(
2118 *crow::connections::systemBus, connectionName, path,
2119 "xyz.openbmc_project.Inventory.Decorator.Asset",
Ed Tanous8a592812022-06-04 09:06:59 -07002120 [asyncResp](const boost::system::error_code ec2,
Ed Tanousb9d36b42022-02-26 21:42:46 -08002121 const dbus::utility::DBusPropertiesMap&
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002122 propertiesList) {
Ed Tanous8a592812022-06-04 09:06:59 -07002123 if (ec2)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002124 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002125 BMCWEB_LOG_DEBUG << "Can't get bmc asset!";
2126 return;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002127 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002128
Krzysztof Grobelnyfac6e532022-08-04 12:42:45 +02002129 const std::string* partNumber = nullptr;
2130 const std::string* serialNumber = nullptr;
2131 const std::string* manufacturer = nullptr;
2132 const std::string* model = nullptr;
2133 const std::string* sparePartNumber = nullptr;
2134
2135 const bool success = sdbusplus::unpackPropertiesNoThrow(
2136 dbus_utils::UnpackErrorPrinter(), propertiesList,
2137 "PartNumber", partNumber, "SerialNumber",
2138 serialNumber, "Manufacturer", manufacturer, "Model",
2139 model, "SparePartNumber", sparePartNumber);
2140
2141 if (!success)
2142 {
2143 messages::internalError(asyncResp->res);
2144 return;
Ed Tanous002d39b2022-05-31 08:59:27 -07002145 }
Krzysztof Grobelnyfac6e532022-08-04 12:42:45 +02002146
2147 if (partNumber != nullptr)
2148 {
2149 asyncResp->res.jsonValue["PartNumber"] =
2150 *partNumber;
2151 }
2152
2153 if (serialNumber != nullptr)
2154 {
2155 asyncResp->res.jsonValue["SerialNumber"] =
2156 *serialNumber;
2157 }
2158
2159 if (manufacturer != nullptr)
2160 {
2161 asyncResp->res.jsonValue["Manufacturer"] =
2162 *manufacturer;
2163 }
2164
2165 if (model != nullptr)
2166 {
2167 asyncResp->res.jsonValue["Model"] = *model;
2168 }
2169
2170 if (sparePartNumber != nullptr)
2171 {
2172 asyncResp->res.jsonValue["SparePartNumber"] =
2173 *sparePartNumber;
2174 }
2175 });
Ed Tanous002d39b2022-05-31 08:59:27 -07002176 }
2177 else if (interfaceName ==
2178 "xyz.openbmc_project.Inventory.Decorator.LocationCode")
2179 {
2180 getLocation(asyncResp, connectionName, path);
2181 }
2182 }
2183 },
2184 "xyz.openbmc_project.ObjectMapper",
2185 "/xyz/openbmc_project/object_mapper",
2186 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
2187 "/xyz/openbmc_project/inventory", int32_t(0),
2188 std::array<const char*, 1>{
2189 "xyz.openbmc_project.Inventory.Item.Bmc"});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002190 });
2191
2192 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/")
Ed Tanoused398212021-06-09 17:05:54 -07002193 .privileges(redfish::privileges::patchManager)
Ed Tanous45ca1b82022-03-25 13:07:27 -07002194 .methods(boost::beast::http::verb::patch)(
2195 [&app](const crow::Request& req,
2196 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002197 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002198 {
2199 return;
2200 }
2201 std::optional<nlohmann::json> oem;
2202 std::optional<nlohmann::json> links;
2203 std::optional<std::string> datetime;
2204
2205 if (!json_util::readJsonPatch(req, asyncResp->res, "Oem", oem,
2206 "DateTime", datetime, "Links", links))
2207 {
2208 return;
2209 }
2210
2211 if (oem)
2212 {
Gunnar Mills54dce7f2022-08-05 17:01:32 +00002213#ifdef BMCWEB_ENABLE_REDFISH_OEM_MANAGER_FAN_DATA
Ed Tanous002d39b2022-05-31 08:59:27 -07002214 std::optional<nlohmann::json> openbmc;
2215 if (!redfish::json_util::readJson(*oem, asyncResp->res, "OpenBmc",
2216 openbmc))
2217 {
2218 BMCWEB_LOG_ERROR
2219 << "Illegal Property "
2220 << oem->dump(2, ' ', true,
2221 nlohmann::json::error_handler_t::replace);
2222 return;
2223 }
2224 if (openbmc)
2225 {
2226 std::optional<nlohmann::json> fan;
2227 if (!redfish::json_util::readJson(*openbmc, asyncResp->res,
2228 "Fan", fan))
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002229 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002230 BMCWEB_LOG_ERROR
2231 << "Illegal Property "
2232 << openbmc->dump(
2233 2, ' ', true,
2234 nlohmann::json::error_handler_t::replace);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002235 return;
2236 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002237 if (fan)
2238 {
2239 auto pid = std::make_shared<SetPIDValues>(asyncResp, *fan);
2240 pid->run();
2241 }
2242 }
Gunnar Mills54dce7f2022-08-05 17:01:32 +00002243#else
2244 messages::propertyUnknown(asyncResp->res, "Oem");
2245 return;
2246#endif
Ed Tanous002d39b2022-05-31 08:59:27 -07002247 }
2248 if (links)
2249 {
2250 std::optional<nlohmann::json> activeSoftwareImage;
2251 if (!redfish::json_util::readJson(*links, asyncResp->res,
2252 "ActiveSoftwareImage",
2253 activeSoftwareImage))
2254 {
2255 return;
2256 }
2257 if (activeSoftwareImage)
2258 {
2259 std::optional<std::string> odataId;
2260 if (!json_util::readJson(*activeSoftwareImage, asyncResp->res,
2261 "@odata.id", odataId))
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002262 {
Ed Tanous45ca1b82022-03-25 13:07:27 -07002263 return;
2264 }
2265
Ed Tanous002d39b2022-05-31 08:59:27 -07002266 if (odataId)
Ed Tanous45ca1b82022-03-25 13:07:27 -07002267 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002268 setActiveFirmwareImage(asyncResp, *odataId);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002269 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002270 }
2271 }
2272 if (datetime)
2273 {
2274 setDateTime(asyncResp, std::move(*datetime));
2275 }
2276 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002277}
2278
2279inline void requestRoutesManagerCollection(App& app)
2280{
2281 BMCWEB_ROUTE(app, "/redfish/v1/Managers/")
Ed Tanoused398212021-06-09 17:05:54 -07002282 .privileges(redfish::privileges::getManagerCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002283 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002284 [&app](const crow::Request& req,
2285 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002286 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002287 {
2288 return;
2289 }
2290 // Collections don't include the static data added by SubRoute
2291 // because it has a duplicate entry for members
2292 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
2293 asyncResp->res.jsonValue["@odata.type"] =
2294 "#ManagerCollection.ManagerCollection";
2295 asyncResp->res.jsonValue["Name"] = "Manager Collection";
2296 asyncResp->res.jsonValue["Members@odata.count"] = 1;
2297 nlohmann::json::array_t members;
2298 nlohmann::json& bmc = members.emplace_back();
2299 bmc["@odata.id"] = "/redfish/v1/Managers/bmc";
2300 asyncResp->res.jsonValue["Members"] = std::move(members);
2301 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002302}
Ed Tanous1abe55e2018-09-05 08:30:59 -07002303} // namespace redfish