blob: 99a3e8b6313211646f3f9a4d71fe369c4844c669 [file] [log] [blame]
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
18#include "node.hpp"
19
James Feist5b4aa862018-08-16 14:07:01 -070020#include <boost/algorithm/string/replace.hpp>
21#include <dbus_utility.hpp>
22
Ed Tanous1abe55e2018-09-05 08:30:59 -070023namespace redfish
24{
Jennifer Leeed5befb2018-08-10 11:29:45 -070025
26/**
27 * ManagerActionsReset class supports handle POST method for Reset action.
28 * The class retrieves and sends data directly to dbus.
29 */
30class ManagerActionsReset : public Node
31{
32 public:
33 ManagerActionsReset(CrowApp& app) :
34 Node(app, "/redfish/v1/Managers/bmc/Actions/Manager.Reset/")
35 {
36 entityPrivileges = {
37 {boost::beast::http::verb::get, {{"Login"}}},
38 {boost::beast::http::verb::head, {{"Login"}}},
39 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
40 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
41 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
42 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
43 }
44
45 private:
46 /**
Jennifer Leeed5befb2018-08-10 11:29:45 -070047 * Function handles POST method request.
48 * Analyzes POST body message before sends Reset request data to dbus.
49 * OpenBMC allows for ResetType is GracefulRestart only.
50 */
51 void doPost(crow::Response& res, const crow::Request& req,
52 const std::vector<std::string>& params) override
53 {
54 std::string resetType;
55
56 if (!json_util::readJson(req, res, "ResetType", resetType))
57 {
58 return;
59 }
60
61 if (resetType != "GracefulRestart")
62 {
63 res.result(boost::beast::http::status::bad_request);
64 messages::actionParameterNotSupported(res, resetType, "ResetType");
65 BMCWEB_LOG_ERROR << "Request incorrect action parameter: "
66 << resetType;
67 res.end();
68 return;
69 }
70 doBMCGracefulRestart(res, req, params);
71 }
72
73 /**
74 * Function transceives data with dbus directly.
75 * All BMC state properties will be retrieved before sending reset request.
76 */
77 void doBMCGracefulRestart(crow::Response& res, const crow::Request& req,
78 const std::vector<std::string>& params)
79 {
80 const char* processName = "xyz.openbmc_project.State.BMC";
81 const char* objectPath = "/xyz/openbmc_project/state/bmc0";
82 const char* interfaceName = "xyz.openbmc_project.State.BMC";
83 const std::string& propertyValue =
84 "xyz.openbmc_project.State.BMC.Transition.Reboot";
85 const char* destProperty = "RequestedBMCTransition";
86
87 // Create the D-Bus variant for D-Bus call.
88 VariantType dbusPropertyValue(propertyValue);
89
90 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
91
92 crow::connections::systemBus->async_method_call(
93 [asyncResp](const boost::system::error_code ec) {
94 // Use "Set" method to set the property value.
95 if (ec)
96 {
97 BMCWEB_LOG_ERROR << "[Set] Bad D-Bus request error: " << ec;
98 messages::internalError(asyncResp->res);
99 return;
100 }
101
102 messages::success(asyncResp->res);
103 },
104 processName, objectPath, "org.freedesktop.DBus.Properties", "Set",
105 interfaceName, destProperty, dbusPropertyValue);
106 }
107};
108
James Feist5b4aa862018-08-16 14:07:01 -0700109static constexpr const char* objectManagerIface =
110 "org.freedesktop.DBus.ObjectManager";
111static constexpr const char* pidConfigurationIface =
112 "xyz.openbmc_project.Configuration.Pid";
113static constexpr const char* pidZoneConfigurationIface =
114 "xyz.openbmc_project.Configuration.Pid.Zone";
James Feistb7a08d02018-12-11 14:55:37 -0800115static constexpr const char* stepwiseConfigurationIface =
116 "xyz.openbmc_project.Configuration.Stepwise";
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100117
James Feist5b4aa862018-08-16 14:07:01 -0700118static void asyncPopulatePid(const std::string& connection,
119 const std::string& path,
120 std::shared_ptr<AsyncResp> asyncResp)
121{
122
123 crow::connections::systemBus->async_method_call(
124 [asyncResp](const boost::system::error_code ec,
125 const dbus::utility::ManagedObjectType& managedObj) {
126 if (ec)
127 {
128 BMCWEB_LOG_ERROR << ec;
James Feist5b4aa862018-08-16 14:07:01 -0700129 asyncResp->res.jsonValue.clear();
Jason M. Billsf12894f2018-10-09 12:45:45 -0700130 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700131 return;
132 }
133 nlohmann::json& configRoot =
134 asyncResp->res.jsonValue["Oem"]["OpenBmc"]["Fan"];
135 nlohmann::json& fans = configRoot["FanControllers"];
136 fans["@odata.type"] = "#OemManager.FanControllers";
137 fans["@odata.context"] =
138 "/redfish/v1/$metadata#OemManager.FanControllers";
139 fans["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc/"
140 "Fan/FanControllers";
141
142 nlohmann::json& pids = configRoot["PidControllers"];
143 pids["@odata.type"] = "#OemManager.PidControllers";
144 pids["@odata.context"] =
145 "/redfish/v1/$metadata#OemManager.PidControllers";
146 pids["@odata.id"] =
147 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/PidControllers";
148
James Feistb7a08d02018-12-11 14:55:37 -0800149 nlohmann::json& stepwise = configRoot["StepwiseControllers"];
150 stepwise["@odata.type"] = "#OemManager.StepwiseControllers";
151 stepwise["@odata.context"] =
152 "/redfish/v1/$metadata#OemManager.StepwiseControllers";
153 stepwise["@odata.id"] =
154 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/StepwiseControllers";
155
James Feist5b4aa862018-08-16 14:07:01 -0700156 nlohmann::json& zones = configRoot["FanZones"];
157 zones["@odata.id"] =
158 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones";
159 zones["@odata.type"] = "#OemManager.FanZones";
160 zones["@odata.context"] =
161 "/redfish/v1/$metadata#OemManager.FanZones";
162 configRoot["@odata.id"] =
163 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan";
164 configRoot["@odata.type"] = "#OemManager.Fan";
165 configRoot["@odata.context"] =
166 "/redfish/v1/$metadata#OemManager.Fan";
167
168 bool propertyError = false;
169 for (const auto& pathPair : managedObj)
170 {
171 for (const auto& intfPair : pathPair.second)
172 {
173 if (intfPair.first != pidConfigurationIface &&
James Feistb7a08d02018-12-11 14:55:37 -0800174 intfPair.first != pidZoneConfigurationIface &&
175 intfPair.first != stepwiseConfigurationIface)
James Feist5b4aa862018-08-16 14:07:01 -0700176 {
177 continue;
178 }
179 auto findName = intfPair.second.find("Name");
180 if (findName == intfPair.second.end())
181 {
182 BMCWEB_LOG_ERROR << "Pid Field missing Name";
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800183 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700184 return;
185 }
186 const std::string* namePtr =
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800187 sdbusplus::message::variant_ns::get_if<std::string>(
188 &findName->second);
James Feist5b4aa862018-08-16 14:07:01 -0700189 if (namePtr == nullptr)
190 {
191 BMCWEB_LOG_ERROR << "Pid Name Field illegal";
James Feistb7a08d02018-12-11 14:55:37 -0800192 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700193 return;
194 }
195
196 std::string name = *namePtr;
197 dbus::utility::escapePathForDbus(name);
James Feistb7a08d02018-12-11 14:55:37 -0800198 nlohmann::json* config = nullptr;
James Feist5b4aa862018-08-16 14:07:01 -0700199 if (intfPair.first == pidZoneConfigurationIface)
200 {
201 std::string chassis;
202 if (!dbus::utility::getNthStringFromPath(
203 pathPair.first.str, 5, chassis))
204 {
205 chassis = "#IllegalValue";
206 }
207 nlohmann::json& zone = zones[name];
208 zone["Chassis"] = {
209 {"@odata.id", "/redfish/v1/Chassis/" + chassis}};
210 zone["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/"
211 "OpenBmc/Fan/FanZones/" +
212 name;
213 zone["@odata.type"] = "#OemManager.FanZone";
214 zone["@odata.context"] =
215 "/redfish/v1/$metadata#OemManager.FanZone";
James Feistb7a08d02018-12-11 14:55:37 -0800216 config = &zone;
James Feist5b4aa862018-08-16 14:07:01 -0700217 }
218
James Feistb7a08d02018-12-11 14:55:37 -0800219 else if (intfPair.first == stepwiseConfigurationIface)
220 {
221 nlohmann::json& controller = stepwise[name];
222 config = &controller;
223
224 controller["@odata.id"] =
225 "/redfish/v1/Managers/bmc#/Oem/"
226 "OpenBmc/Fan/StepwiseControllers/" +
227 std::string(name);
228 controller["@odata.type"] =
229 "#OemManager.StepwiseController";
230
231 controller["@odata.context"] =
232 "/redfish/v1/"
233 "$metadata#OemManager.StepwiseController";
234 }
235
236 // pid and fans are off the same configuration
237 else if (intfPair.first == pidConfigurationIface)
238 {
239 const std::string* classPtr = nullptr;
240 auto findClass = intfPair.second.find("Class");
241 if (findClass != intfPair.second.end())
242 {
243 classPtr = sdbusplus::message::variant_ns::get_if<
244 std::string>(&findClass->second);
245 }
246 if (classPtr == nullptr)
247 {
248 BMCWEB_LOG_ERROR << "Pid Class Field illegal";
249 messages::internalError(asyncResp->res);
250 return;
251 }
252 bool isFan = *classPtr == "fan";
253 nlohmann::json& element =
254 isFan ? fans[name] : pids[name];
255 config = &element;
256 if (isFan)
257 {
258 element["@odata.id"] =
259 "/redfish/v1/Managers/bmc#/Oem/"
260 "OpenBmc/Fan/FanControllers/" +
261 std::string(name);
262 element["@odata.type"] =
263 "#OemManager.FanController";
264
265 element["@odata.context"] =
266 "/redfish/v1/"
267 "$metadata#OemManager.FanController";
268 }
269 else
270 {
271 element["@odata.id"] =
272 "/redfish/v1/Managers/bmc#/Oem/"
273 "OpenBmc/Fan/PidControllers/" +
274 std::string(name);
275 element["@odata.type"] =
276 "#OemManager.PidController";
277 element["@odata.context"] =
278 "/redfish/v1/$metadata"
279 "#OemManager.PidController";
280 }
281 }
282 else
283 {
284 BMCWEB_LOG_ERROR << "Unexpected configuration";
285 messages::internalError(asyncResp->res);
286 return;
287 }
288
289 // used for making maps out of 2 vectors
290 const std::vector<double>* keys = nullptr;
291 const std::vector<double>* values = nullptr;
292
James Feist5b4aa862018-08-16 14:07:01 -0700293 for (const auto& propertyPair : intfPair.second)
294 {
295 if (propertyPair.first == "Type" ||
296 propertyPair.first == "Class" ||
297 propertyPair.first == "Name")
298 {
299 continue;
300 }
301
302 // zones
303 if (intfPair.first == pidZoneConfigurationIface)
304 {
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800305 const double* ptr =
306 sdbusplus::message::variant_ns::get_if<double>(
307 &propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700308 if (ptr == nullptr)
309 {
310 BMCWEB_LOG_ERROR << "Field Illegal "
311 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700312 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700313 return;
314 }
James Feistb7a08d02018-12-11 14:55:37 -0800315 (*config)[propertyPair.first] = *ptr;
316 }
317
318 if (intfPair.first == stepwiseConfigurationIface)
319 {
320 if (propertyPair.first == "Reading" ||
321 propertyPair.first == "Output")
322 {
323 const std::vector<double>* ptr =
324 sdbusplus::message::variant_ns::get_if<
325 std::vector<double>>(
326 &propertyPair.second);
327
328 if (ptr == nullptr)
329 {
330 BMCWEB_LOG_ERROR << "Field Illegal "
331 << propertyPair.first;
332 messages::internalError(asyncResp->res);
333 return;
334 }
335
336 if (propertyPair.first == "Reading")
337 {
338 keys = ptr;
339 }
340 else
341 {
342 values = ptr;
343 }
344 if (keys && values)
345 {
346 if (keys->size() != values->size())
347 {
348 BMCWEB_LOG_ERROR
349 << "Reading and Output size don't "
350 "match ";
351 messages::internalError(asyncResp->res);
352 return;
353 }
354 nlohmann::json& steps = (*config)["Steps"];
355 steps = nlohmann::json::array();
356 for (size_t ii = 0; ii < keys->size(); ii++)
357 {
358 steps.push_back(
359 {{"Target", (*keys)[ii]},
360 {"Output", (*values)[ii]}});
361 }
362 }
363 }
364 if (propertyPair.first == "NegativeHysteresis" ||
365 propertyPair.first == "PositiveHysteresis")
366 {
367 const double* ptr =
368 sdbusplus::message::variant_ns::get_if<
369 double>(&propertyPair.second);
370 if (ptr == nullptr)
371 {
372 BMCWEB_LOG_ERROR << "Field Illegal "
373 << propertyPair.first;
374 messages::internalError(asyncResp->res);
375 return;
376 }
377 (*config)[propertyPair.first] = *ptr;
378 }
James Feist5b4aa862018-08-16 14:07:01 -0700379 }
380
381 // pid and fans are off the same configuration
James Feistb7a08d02018-12-11 14:55:37 -0800382 if (intfPair.first == pidConfigurationIface ||
383 intfPair.first == stepwiseConfigurationIface)
James Feist5b4aa862018-08-16 14:07:01 -0700384 {
James Feist5b4aa862018-08-16 14:07:01 -0700385
386 if (propertyPair.first == "Zones")
387 {
388 const std::vector<std::string>* inputs =
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800389 sdbusplus::message::variant_ns::get_if<
390 std::vector<std::string>>(
391 &propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700392
393 if (inputs == nullptr)
394 {
395 BMCWEB_LOG_ERROR
396 << "Zones Pid Field Illegal";
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800397 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700398 return;
399 }
James Feistb7a08d02018-12-11 14:55:37 -0800400 auto& data = (*config)[propertyPair.first];
James Feist5b4aa862018-08-16 14:07:01 -0700401 data = nlohmann::json::array();
402 for (std::string itemCopy : *inputs)
403 {
404 dbus::utility::escapePathForDbus(itemCopy);
405 data.push_back(
406 {{"@odata.id",
407 "/redfish/v1/Managers/bmc#/Oem/"
408 "OpenBmc/Fan/FanZones/" +
409 itemCopy}});
410 }
411 }
412 // todo(james): may never happen, but this
413 // assumes configuration data referenced in the
414 // PID config is provided by the same daemon, we
415 // could add another loop to cover all cases,
416 // but I'm okay kicking this can down the road a
417 // bit
418
419 else if (propertyPair.first == "Inputs" ||
420 propertyPair.first == "Outputs")
421 {
James Feistb7a08d02018-12-11 14:55:37 -0800422 auto& data = (*config)[propertyPair.first];
James Feist5b4aa862018-08-16 14:07:01 -0700423 const std::vector<std::string>* inputs =
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800424 sdbusplus::message::variant_ns::get_if<
425 std::vector<std::string>>(
426 &propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700427
428 if (inputs == nullptr)
429 {
430 BMCWEB_LOG_ERROR << "Field Illegal "
431 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700432 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700433 return;
434 }
435 data = *inputs;
436 } // doubles
437 else if (propertyPair.first ==
438 "FFGainCoefficient" ||
439 propertyPair.first == "FFOffCoefficient" ||
440 propertyPair.first == "ICoefficient" ||
441 propertyPair.first == "ILimitMax" ||
442 propertyPair.first == "ILimitMin" ||
443 propertyPair.first == "OutLimitMax" ||
444 propertyPair.first == "OutLimitMin" ||
445 propertyPair.first == "PCoefficient" ||
446 propertyPair.first == "SlewNeg" ||
447 propertyPair.first == "SlewPos")
448 {
449 const double* ptr =
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800450 sdbusplus::message::variant_ns::get_if<
451 double>(&propertyPair.second);
James Feist5b4aa862018-08-16 14:07:01 -0700452 if (ptr == nullptr)
453 {
454 BMCWEB_LOG_ERROR << "Field Illegal "
455 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700456 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700457 return;
458 }
James Feistb7a08d02018-12-11 14:55:37 -0800459 (*config)[propertyPair.first] = *ptr;
James Feist5b4aa862018-08-16 14:07:01 -0700460 }
461 }
462 }
463 }
464 }
465 },
466 connection, path, objectManagerIface, "GetManagedObjects");
467}
Jennifer Leeca537922018-08-10 10:07:30 -0700468
James Feist83ff9ab2018-08-31 10:18:24 -0700469enum class CreatePIDRet
470{
471 fail,
472 del,
473 patch
474};
475
476static CreatePIDRet createPidInterface(
477 const std::shared_ptr<AsyncResp>& response, const std::string& type,
478 const nlohmann::json& record, const std::string& path,
479 const dbus::utility::ManagedObjectType& managedObj, bool createNewObject,
480 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>&
481 output,
482 std::string& chassis)
483{
484
485 if (type == "PidControllers" || type == "FanControllers")
486 {
487 if (createNewObject)
488 {
489 output["Class"] = type == "PidControllers" ? std::string("temp")
490 : std::string("fan");
491 output["Type"] = std::string("Pid");
492 }
493 else if (record == nullptr)
494 {
495 // delete interface
496 crow::connections::systemBus->async_method_call(
497 [response,
498 path{std::string(path)}](const boost::system::error_code ec) {
499 if (ec)
500 {
501 BMCWEB_LOG_ERROR << "Error patching " << path << ": "
502 << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700503 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700504 }
505 },
506 "xyz.openbmc_project.EntityManager", path,
507 pidConfigurationIface, "Delete");
508 return CreatePIDRet::del;
509 }
510
511 for (auto& field : record.items())
512 {
513 if (field.key() == "Zones")
514 {
515 if (!field.value().is_array())
516 {
517 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700518 messages::propertyValueFormatError(
519 response->res, field.value(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700520 return CreatePIDRet::fail;
521 }
522 std::vector<std::string> inputs;
523 for (const auto& odata : field.value().items())
524 {
525 for (const auto& value : odata.value().items())
526 {
527 const std::string* path =
528 value.value().get_ptr<const std::string*>();
529 if (path == nullptr)
530 {
531 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700532 messages::propertyValueFormatError(
533 response->res, field.value().dump(),
534 field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700535 return CreatePIDRet::fail;
536 }
537 std::string input;
538 if (!dbus::utility::getNthStringFromPath(*path, 4,
539 input))
540 {
541 BMCWEB_LOG_ERROR << "Got invalid path " << *path;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700542 messages::propertyValueFormatError(
543 response->res, field.value().dump(),
544 field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700545 return CreatePIDRet::fail;
546 }
547 boost::replace_all(input, "_", " ");
548 inputs.emplace_back(std::move(input));
549 }
550 }
551 output["Zones"] = std::move(inputs);
552 }
553 else if (field.key() == "Inputs" || field.key() == "Outputs")
554 {
555 if (!field.value().is_array())
556 {
557 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700558 messages::propertyValueFormatError(
559 response->res, field.value().dump(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700560 return CreatePIDRet::fail;
561 }
562 std::vector<std::string> inputs;
563 for (const auto& value : field.value().items())
564 {
565 const std::string* sensor =
566 value.value().get_ptr<const std::string*>();
567
568 if (sensor == nullptr)
569 {
570 BMCWEB_LOG_ERROR << "Illegal Type "
571 << field.value().dump();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700572 messages::propertyValueFormatError(
573 response->res, field.value().dump(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700574 return CreatePIDRet::fail;
575 }
576
577 std::string input =
578 boost::replace_all_copy(*sensor, "_", " ");
579 inputs.push_back(std::move(input));
580 // try to find the sensor in the
581 // configuration
582 if (chassis.empty())
583 {
584 std::find_if(
585 managedObj.begin(), managedObj.end(),
586 [&chassis, sensor](const auto& obj) {
587 if (boost::algorithm::ends_with(obj.first.str,
588 *sensor))
589 {
590 return dbus::utility::getNthStringFromPath(
591 obj.first.str, 5, chassis);
592 }
593 return false;
594 });
595 }
596 }
597 output[field.key()] = inputs;
598 }
599
600 // doubles
601 else if (field.key() == "FFGainCoefficient" ||
602 field.key() == "FFOffCoefficient" ||
603 field.key() == "ICoefficient" ||
604 field.key() == "ILimitMax" || field.key() == "ILimitMin" ||
605 field.key() == "OutLimitMax" ||
606 field.key() == "OutLimitMin" ||
607 field.key() == "PCoefficient" ||
608 field.key() == "SetPoint" || field.key() == "SlewNeg" ||
609 field.key() == "SlewPos")
610 {
611 const double* ptr = field.value().get_ptr<const double*>();
612 if (ptr == nullptr)
613 {
614 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700615 messages::propertyValueFormatError(
616 response->res, field.value().dump(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700617 return CreatePIDRet::fail;
618 }
619 output[field.key()] = *ptr;
620 }
621
622 else
623 {
624 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700625 messages::propertyUnknown(response->res, field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700626 return CreatePIDRet::fail;
627 }
628 }
629 }
630 else if (type == "FanZones")
631 {
632 if (!createNewObject && record == nullptr)
633 {
634 // delete interface
635 crow::connections::systemBus->async_method_call(
636 [response,
637 path{std::string(path)}](const boost::system::error_code ec) {
638 if (ec)
639 {
640 BMCWEB_LOG_ERROR << "Error patching " << path << ": "
641 << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700642 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700643 }
644 },
645 "xyz.openbmc_project.EntityManager", path,
646 pidZoneConfigurationIface, "Delete");
647 return CreatePIDRet::del;
648 }
649 output["Type"] = std::string("Pid.Zone");
650
651 for (auto& field : record.items())
652 {
653 if (field.key() == "Chassis")
654 {
655 const std::string* chassisId = nullptr;
656 for (const auto& id : field.value().items())
657 {
658 if (id.key() != "@odata.id")
659 {
660 BMCWEB_LOG_ERROR << "Illegal Type " << id.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700661 messages::propertyUnknown(response->res, field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700662 return CreatePIDRet::fail;
663 }
664 chassisId = id.value().get_ptr<const std::string*>();
665 if (chassisId == nullptr)
666 {
Jason M. Bills35a62c72018-10-09 12:45:45 -0700667 messages::createFailedMissingReqProperties(
668 response->res, field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700669 return CreatePIDRet::fail;
670 }
671 }
672
673 // /refish/v1/chassis/chassis_name/
674 if (!dbus::utility::getNthStringFromPath(*chassisId, 3,
675 chassis))
676 {
677 BMCWEB_LOG_ERROR << "Got invalid path " << *chassisId;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700678 messages::invalidObject(response->res, *chassisId);
James Feist83ff9ab2018-08-31 10:18:24 -0700679 return CreatePIDRet::fail;
680 }
681 }
682 else if (field.key() == "FailSafePercent" ||
683 field.key() == "MinThermalRpm")
684 {
685 const double* ptr = field.value().get_ptr<const double*>();
686 if (ptr == nullptr)
687 {
688 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700689 messages::propertyValueFormatError(
690 response->res, field.value().dump(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700691 return CreatePIDRet::fail;
692 }
693 output[field.key()] = *ptr;
694 }
695 else
696 {
697 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700698 messages::propertyUnknown(response->res, field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700699 return CreatePIDRet::fail;
700 }
701 }
702 }
703 else
704 {
705 BMCWEB_LOG_ERROR << "Illegal Type " << type;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700706 messages::propertyUnknown(response->res, type);
James Feist83ff9ab2018-08-31 10:18:24 -0700707 return CreatePIDRet::fail;
708 }
709 return CreatePIDRet::patch;
710}
711
Ed Tanous1abe55e2018-09-05 08:30:59 -0700712class Manager : public Node
713{
714 public:
James Feist5b4aa862018-08-16 14:07:01 -0700715 Manager(CrowApp& app) : Node(app, "/redfish/v1/Managers/bmc/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700716 {
Ed Tanous0f74e642018-11-12 15:17:05 -0800717 uuid = app.template getMiddleware<crow::persistent_data::Middleware>()
718 .systemUuid;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700719 entityPrivileges = {
720 {boost::beast::http::verb::get, {{"Login"}}},
721 {boost::beast::http::verb::head, {{"Login"}}},
722 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
723 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
724 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
725 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100726 }
727
Ed Tanous1abe55e2018-09-05 08:30:59 -0700728 private:
James Feist5b4aa862018-08-16 14:07:01 -0700729 void getPidValues(std::shared_ptr<AsyncResp> asyncResp)
730 {
731 crow::connections::systemBus->async_method_call(
732 [asyncResp](const boost::system::error_code ec,
733 const crow::openbmc_mapper::GetSubTreeType& subtree) {
734 if (ec)
735 {
736 BMCWEB_LOG_ERROR << ec;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700737 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700738 return;
739 }
740
741 // create map of <connection, path to objMgr>>
742 boost::container::flat_map<std::string, std::string>
743 objectMgrPaths;
James Feist6bce33b2018-10-22 12:05:56 -0700744 boost::container::flat_set<std::string> calledConnections;
James Feist5b4aa862018-08-16 14:07:01 -0700745 for (const auto& pathGroup : subtree)
746 {
747 for (const auto& connectionGroup : pathGroup.second)
748 {
James Feist6bce33b2018-10-22 12:05:56 -0700749 auto findConnection =
750 calledConnections.find(connectionGroup.first);
751 if (findConnection != calledConnections.end())
752 {
753 break;
754 }
James Feist5b4aa862018-08-16 14:07:01 -0700755 for (const std::string& interface :
756 connectionGroup.second)
757 {
758 if (interface == objectManagerIface)
759 {
760 objectMgrPaths[connectionGroup.first] =
761 pathGroup.first;
762 }
763 // this list is alphabetical, so we
764 // should have found the objMgr by now
765 if (interface == pidConfigurationIface ||
James Feistb7a08d02018-12-11 14:55:37 -0800766 interface == pidZoneConfigurationIface ||
767 interface == stepwiseConfigurationIface)
James Feist5b4aa862018-08-16 14:07:01 -0700768 {
769 auto findObjMgr =
770 objectMgrPaths.find(connectionGroup.first);
771 if (findObjMgr == objectMgrPaths.end())
772 {
773 BMCWEB_LOG_DEBUG << connectionGroup.first
774 << "Has no Object Manager";
775 continue;
776 }
James Feist6bce33b2018-10-22 12:05:56 -0700777
778 calledConnections.insert(connectionGroup.first);
779
James Feist5b4aa862018-08-16 14:07:01 -0700780 asyncPopulatePid(findObjMgr->first,
781 findObjMgr->second, asyncResp);
782 break;
783 }
784 }
785 }
786 }
787 },
788 "xyz.openbmc_project.ObjectMapper",
789 "/xyz/openbmc_project/object_mapper",
790 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
James Feistb7a08d02018-12-11 14:55:37 -0800791 std::array<const char*, 4>{
792 pidConfigurationIface, pidZoneConfigurationIface,
793 objectManagerIface, stepwiseConfigurationIface});
James Feist5b4aa862018-08-16 14:07:01 -0700794 }
795
796 void doGet(crow::Response& res, const crow::Request& req,
797 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700798 {
Ed Tanous0f74e642018-11-12 15:17:05 -0800799 res.jsonValue["@odata.id"] = "/redfish/v1/Managers/bmc";
800 res.jsonValue["@odata.type"] = "#Manager.v1_3_0.Manager";
801 res.jsonValue["@odata.context"] =
802 "/redfish/v1/$metadata#Manager.Manager";
803 res.jsonValue["Id"] = "bmc";
804 res.jsonValue["Name"] = "OpenBmc Manager";
805 res.jsonValue["Description"] = "Baseboard Management Controller";
806 res.jsonValue["PowerState"] = "On";
807 res.jsonValue["ManagerType"] = "BMC";
Ed Tanous75176582018-12-14 08:14:34 -0800808 res.jsonValue["UUID"] = uuid;
809 res.jsonValue["Model"] = "OpenBmc"; // TODO(ed), get model
Ed Tanous0f74e642018-11-12 15:17:05 -0800810
811 res.jsonValue["LogServices"] = {
812 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices"}};
813
814 res.jsonValue["NetworkProtocol"] = {
815 {"@odata.id", "/redfish/v1/Managers/bmc/NetworkProtocol"}};
816
817 res.jsonValue["EthernetInterfaces"] = {
818 {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces"}};
819 // default oem data
820 nlohmann::json& oem = res.jsonValue["Oem"];
821 nlohmann::json& oemOpenbmc = oem["OpenBmc"];
822 oem["@odata.type"] = "#OemManager.Oem";
823 oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem";
824 oem["@odata.context"] = "/redfish/v1/$metadata#OemManager.Oem";
825 oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc";
826 oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc";
827 oemOpenbmc["@odata.context"] =
828 "/redfish/v1/$metadata#OemManager.OpenBmc";
829
Jennifer Leeed5befb2018-08-10 11:29:45 -0700830 // Update Actions object.
Ed Tanous0f74e642018-11-12 15:17:05 -0800831 nlohmann::json& manager_reset =
832 res.jsonValue["Actions"]["#Manager.Reset"];
Jennifer Leeed5befb2018-08-10 11:29:45 -0700833 manager_reset["target"] =
834 "/redfish/v1/Managers/bmc/Actions/Manager.Reset";
835 manager_reset["ResetType@Redfish.AllowableValues"] = {
836 "GracefulRestart"};
Jennifer Leeca537922018-08-10 10:07:30 -0700837
Ed Tanous0f74e642018-11-12 15:17:05 -0800838 res.jsonValue["DateTime"] = getDateTime();
Jennifer Leeed5befb2018-08-10 11:29:45 -0700839 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
James Feist5b4aa862018-08-16 14:07:01 -0700840
Jennifer Leeca537922018-08-10 10:07:30 -0700841 crow::connections::systemBus->async_method_call(
842 [asyncResp](const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -0700843 const dbus::utility::ManagedObjectType& resp) {
Jennifer Leeca537922018-08-10 10:07:30 -0700844 if (ec)
845 {
846 BMCWEB_LOG_ERROR << "Error while getting Software Version";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700847 messages::internalError(asyncResp->res);
Jennifer Leeca537922018-08-10 10:07:30 -0700848 return;
849 }
850
James Feist5b4aa862018-08-16 14:07:01 -0700851 for (auto& objpath : resp)
Jennifer Leeca537922018-08-10 10:07:30 -0700852 {
James Feist5b4aa862018-08-16 14:07:01 -0700853 for (auto& interface : objpath.second)
Jennifer Leeca537922018-08-10 10:07:30 -0700854 {
855 // If interface is xyz.openbmc_project.Software.Version,
856 // this is what we're looking for.
857 if (interface.first ==
858 "xyz.openbmc_project.Software.Version")
859 {
860 // Cut out everyting until last "/", ...
James Feist5b4aa862018-08-16 14:07:01 -0700861 const std::string& iface_id = objpath.first;
862 for (auto& property : interface.second)
Jennifer Leeca537922018-08-10 10:07:30 -0700863 {
864 if (property.first == "Version")
865 {
James Feist5b4aa862018-08-16 14:07:01 -0700866 const std::string* value =
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800867 sdbusplus::message::variant_ns::get_if<
868 std::string>(&property.second);
Jennifer Leeca537922018-08-10 10:07:30 -0700869 if (value == nullptr)
870 {
871 continue;
872 }
873 asyncResp->res
874 .jsonValue["FirmwareVersion"] = *value;
875 }
876 }
877 }
878 }
879 }
880 },
881 "xyz.openbmc_project.Software.BMC.Updater",
882 "/xyz/openbmc_project/software",
883 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
James Feist5b4aa862018-08-16 14:07:01 -0700884 getPidValues(asyncResp);
885 }
James Feist83ff9ab2018-08-31 10:18:24 -0700886 void setPidValues(std::shared_ptr<AsyncResp> response,
887 const nlohmann::json& data)
888 {
889 // todo(james): might make sense to do a mapper call here if this
890 // interface gets more traction
891 crow::connections::systemBus->async_method_call(
892 [response,
893 data](const boost::system::error_code ec,
894 const dbus::utility::ManagedObjectType& managedObj) {
895 if (ec)
896 {
897 BMCWEB_LOG_ERROR << "Error communicating to Entity Manager";
Jason M. Bills35a62c72018-10-09 12:45:45 -0700898 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700899 return;
900 }
901 for (const auto& type : data.items())
902 {
903 if (!type.value().is_object())
904 {
905 BMCWEB_LOG_ERROR << "Illegal Type " << type.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700906 messages::propertyValueFormatError(
907 response->res, type.value(), type.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700908 return;
909 }
910 for (const auto& record : type.value().items())
911 {
912 const std::string& name = record.key();
913 auto pathItr =
914 std::find_if(managedObj.begin(), managedObj.end(),
915 [&name](const auto& obj) {
916 return boost::algorithm::ends_with(
917 obj.first.str, name);
918 });
919 boost::container::flat_map<
920 std::string, dbus::utility::DbusVariantType>
921 output;
922
923 output.reserve(16); // The pid interface length
924
925 // determines if we're patching entity-manager or
926 // creating a new object
927 bool createNewObject = (pathItr == managedObj.end());
928 if (type.key() == "PidControllers" ||
929 type.key() == "FanControllers")
930 {
931 if (!createNewObject &&
932 pathItr->second.find(pidConfigurationIface) ==
933 pathItr->second.end())
934 {
935 createNewObject = true;
936 }
937 }
938 else if (!createNewObject &&
939 pathItr->second.find(
940 pidZoneConfigurationIface) ==
941 pathItr->second.end())
942 {
943 createNewObject = true;
944 }
945 output["Name"] =
946 boost::replace_all_copy(name, "_", " ");
947
948 std::string chassis;
949 CreatePIDRet ret = createPidInterface(
950 response, type.key(), record.value(),
951 pathItr->first.str, managedObj, createNewObject,
952 output, chassis);
953 if (ret == CreatePIDRet::fail)
954 {
955 return;
956 }
957 else if (ret == CreatePIDRet::del)
958 {
959 continue;
960 }
961
962 if (!createNewObject)
963 {
964 for (const auto& property : output)
965 {
966 const char* iface =
967 type.key() == "FanZones"
968 ? pidZoneConfigurationIface
969 : pidConfigurationIface;
970 crow::connections::systemBus->async_method_call(
971 [response,
972 propertyName{std::string(property.first)}](
973 const boost::system::error_code ec) {
974 if (ec)
975 {
976 BMCWEB_LOG_ERROR
977 << "Error patching "
978 << propertyName << ": " << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700979 messages::internalError(
980 response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700981 }
982 },
983 "xyz.openbmc_project.EntityManager",
984 pathItr->first.str,
985 "org.freedesktop.DBus.Properties", "Set",
986 std::string(iface), property.first,
987 property.second);
988 }
989 }
990 else
991 {
992 if (chassis.empty())
993 {
994 BMCWEB_LOG_ERROR
995 << "Failed to get chassis from config";
Jason M. Bills35a62c72018-10-09 12:45:45 -0700996 messages::invalidObject(response->res, name);
James Feist83ff9ab2018-08-31 10:18:24 -0700997 return;
998 }
999
1000 bool foundChassis = false;
1001 for (const auto& obj : managedObj)
1002 {
1003 if (boost::algorithm::ends_with(obj.first.str,
1004 chassis))
1005 {
1006 chassis = obj.first.str;
1007 foundChassis = true;
1008 break;
1009 }
1010 }
1011 if (!foundChassis)
1012 {
1013 BMCWEB_LOG_ERROR
1014 << "Failed to find chassis on dbus";
Jason M. Bills35a62c72018-10-09 12:45:45 -07001015 messages::resourceMissingAtURI(
1016 response->res,
1017 "/redfish/v1/Chassis/" + chassis);
James Feist83ff9ab2018-08-31 10:18:24 -07001018 return;
1019 }
1020
1021 crow::connections::systemBus->async_method_call(
1022 [response](const boost::system::error_code ec) {
1023 if (ec)
1024 {
1025 BMCWEB_LOG_ERROR
1026 << "Error Adding Pid Object " << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -07001027 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -07001028 }
1029 },
1030 "xyz.openbmc_project.EntityManager", chassis,
1031 "xyz.openbmc_project.AddObject", "AddObject",
1032 output);
1033 }
1034 }
1035 }
1036 },
1037 "xyz.openbmc_project.EntityManager", "/", objectManagerIface,
1038 "GetManagedObjects");
1039 }
James Feist5b4aa862018-08-16 14:07:01 -07001040
1041 void doPatch(crow::Response& res, const crow::Request& req,
1042 const std::vector<std::string>& params) override
1043 {
Ed Tanous0627a2c2018-11-29 17:09:23 -08001044 std::optional<nlohmann::json> oem;
1045
1046 if (!json_util::readJson(req, res, "Oem", oem))
James Feist83ff9ab2018-08-31 10:18:24 -07001047 {
1048 return;
1049 }
Ed Tanous0627a2c2018-11-29 17:09:23 -08001050
James Feist83ff9ab2018-08-31 10:18:24 -07001051 std::shared_ptr<AsyncResp> response = std::make_shared<AsyncResp>(res);
Ed Tanous0627a2c2018-11-29 17:09:23 -08001052
1053 if (oem)
James Feist83ff9ab2018-08-31 10:18:24 -07001054 {
Ed Tanous0627a2c2018-11-29 17:09:23 -08001055 for (const auto& oemLevel : oem->items())
James Feist83ff9ab2018-08-31 10:18:24 -07001056 {
1057 if (oemLevel.key() == "OpenBmc")
1058 {
1059 if (!oemLevel.value().is_object())
1060 {
1061 BMCWEB_LOG_ERROR << "Bad Patch " << oemLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -07001062 messages::propertyValueFormatError(
Ed Tanous0627a2c2018-11-29 17:09:23 -08001063 response->res, "Oem", "OemManager.OpenBmc");
James Feist83ff9ab2018-08-31 10:18:24 -07001064 return;
1065 }
1066 for (const auto& typeLevel : oemLevel.value().items())
1067 {
1068
1069 if (typeLevel.key() == "Fan")
1070 {
1071 if (!typeLevel.value().is_object())
1072 {
1073 BMCWEB_LOG_ERROR << "Bad Patch "
1074 << typeLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -07001075 messages::propertyValueFormatError(
1076 response->res, typeLevel.value().dump(),
1077 typeLevel.key());
James Feist83ff9ab2018-08-31 10:18:24 -07001078 return;
1079 }
1080 setPidValues(response,
1081 std::move(typeLevel.value()));
1082 }
1083 else
1084 {
1085 BMCWEB_LOG_ERROR << "Bad Patch " << typeLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -07001086 messages::propertyUnknown(response->res,
1087 typeLevel.key());
James Feist83ff9ab2018-08-31 10:18:24 -07001088 return;
1089 }
1090 }
1091 }
1092 else
1093 {
1094 BMCWEB_LOG_ERROR << "Bad Patch " << oemLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -07001095 messages::propertyUnknown(response->res, oemLevel.key());
James Feist83ff9ab2018-08-31 10:18:24 -07001096 return;
1097 }
1098 }
1099 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001100 }
1101
1102 std::string getDateTime() const
1103 {
1104 std::array<char, 128> dateTime;
1105 std::string redfishDateTime("0000-00-00T00:00:00Z00:00");
1106 std::time_t time = std::time(nullptr);
1107
1108 if (std::strftime(dateTime.begin(), dateTime.size(), "%FT%T%z",
1109 std::localtime(&time)))
1110 {
1111 // insert the colon required by the ISO 8601 standard
1112 redfishDateTime = std::string(dateTime.data());
1113 redfishDateTime.insert(redfishDateTime.end() - 2, ':');
1114 }
1115
1116 return redfishDateTime;
1117 }
Ed Tanous0f74e642018-11-12 15:17:05 -08001118
1119 std::string uuid;
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001120};
1121
Ed Tanous1abe55e2018-09-05 08:30:59 -07001122class ManagerCollection : public Node
1123{
1124 public:
James Feist5b4aa862018-08-16 14:07:01 -07001125 ManagerCollection(CrowApp& app) : Node(app, "/redfish/v1/Managers/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001126 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001127 entityPrivileges = {
1128 {boost::beast::http::verb::get, {{"Login"}}},
1129 {boost::beast::http::verb::head, {{"Login"}}},
1130 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1131 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1132 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1133 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1134 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001135
Ed Tanous1abe55e2018-09-05 08:30:59 -07001136 private:
James Feist5b4aa862018-08-16 14:07:01 -07001137 void doGet(crow::Response& res, const crow::Request& req,
1138 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -07001139 {
James Feist83ff9ab2018-08-31 10:18:24 -07001140 // Collections don't include the static data added by SubRoute
1141 // because it has a duplicate entry for members
Ed Tanous1abe55e2018-09-05 08:30:59 -07001142 res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
1143 res.jsonValue["@odata.type"] = "#ManagerCollection.ManagerCollection";
1144 res.jsonValue["@odata.context"] =
1145 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection";
1146 res.jsonValue["Name"] = "Manager Collection";
1147 res.jsonValue["Members@odata.count"] = 1;
1148 res.jsonValue["Members"] = {
James Feist5b4aa862018-08-16 14:07:01 -07001149 {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001150 res.end();
1151 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001152};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001153} // namespace redfish