blob: 09a41063da8376a8053b72197ec5b115c498994f [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{
James Feist5b4aa862018-08-16 14:07:01 -070025static constexpr const char* objectManagerIface =
26 "org.freedesktop.DBus.ObjectManager";
27static constexpr const char* pidConfigurationIface =
28 "xyz.openbmc_project.Configuration.Pid";
29static constexpr const char* pidZoneConfigurationIface =
30 "xyz.openbmc_project.Configuration.Pid.Zone";
Borawski.Lukasz9c3106852018-02-09 15:24:22 +010031
James Feist5b4aa862018-08-16 14:07:01 -070032static void asyncPopulatePid(const std::string& connection,
33 const std::string& path,
34 std::shared_ptr<AsyncResp> asyncResp)
35{
36
37 crow::connections::systemBus->async_method_call(
38 [asyncResp](const boost::system::error_code ec,
39 const dbus::utility::ManagedObjectType& managedObj) {
40 if (ec)
41 {
42 BMCWEB_LOG_ERROR << ec;
James Feist5b4aa862018-08-16 14:07:01 -070043 asyncResp->res.jsonValue.clear();
Jason M. Billsf12894f2018-10-09 12:45:45 -070044 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -070045 return;
46 }
47 nlohmann::json& configRoot =
48 asyncResp->res.jsonValue["Oem"]["OpenBmc"]["Fan"];
49 nlohmann::json& fans = configRoot["FanControllers"];
50 fans["@odata.type"] = "#OemManager.FanControllers";
51 fans["@odata.context"] =
52 "/redfish/v1/$metadata#OemManager.FanControllers";
53 fans["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc/"
54 "Fan/FanControllers";
55
56 nlohmann::json& pids = configRoot["PidControllers"];
57 pids["@odata.type"] = "#OemManager.PidControllers";
58 pids["@odata.context"] =
59 "/redfish/v1/$metadata#OemManager.PidControllers";
60 pids["@odata.id"] =
61 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/PidControllers";
62
63 nlohmann::json& zones = configRoot["FanZones"];
64 zones["@odata.id"] =
65 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones";
66 zones["@odata.type"] = "#OemManager.FanZones";
67 zones["@odata.context"] =
68 "/redfish/v1/$metadata#OemManager.FanZones";
69 configRoot["@odata.id"] =
70 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan";
71 configRoot["@odata.type"] = "#OemManager.Fan";
72 configRoot["@odata.context"] =
73 "/redfish/v1/$metadata#OemManager.Fan";
74
75 bool propertyError = false;
76 for (const auto& pathPair : managedObj)
77 {
78 for (const auto& intfPair : pathPair.second)
79 {
80 if (intfPair.first != pidConfigurationIface &&
81 intfPair.first != pidZoneConfigurationIface)
82 {
83 continue;
84 }
85 auto findName = intfPair.second.find("Name");
86 if (findName == intfPair.second.end())
87 {
88 BMCWEB_LOG_ERROR << "Pid Field missing Name";
Jason M. Billsf12894f2018-10-09 12:45:45 -070089 messages::internalError(asyncResp->res, "Name");
James Feist5b4aa862018-08-16 14:07:01 -070090 return;
91 }
92 const std::string* namePtr =
93 mapbox::getPtr<const std::string>(findName->second);
94 if (namePtr == nullptr)
95 {
96 BMCWEB_LOG_ERROR << "Pid Name Field illegal";
97 return;
98 }
99
100 std::string name = *namePtr;
101 dbus::utility::escapePathForDbus(name);
102 if (intfPair.first == pidZoneConfigurationIface)
103 {
104 std::string chassis;
105 if (!dbus::utility::getNthStringFromPath(
106 pathPair.first.str, 5, chassis))
107 {
108 chassis = "#IllegalValue";
109 }
110 nlohmann::json& zone = zones[name];
111 zone["Chassis"] = {
112 {"@odata.id", "/redfish/v1/Chassis/" + chassis}};
113 zone["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/"
114 "OpenBmc/Fan/FanZones/" +
115 name;
116 zone["@odata.type"] = "#OemManager.FanZone";
117 zone["@odata.context"] =
118 "/redfish/v1/$metadata#OemManager.FanZone";
119 }
120
121 for (const auto& propertyPair : intfPair.second)
122 {
123 if (propertyPair.first == "Type" ||
124 propertyPair.first == "Class" ||
125 propertyPair.first == "Name")
126 {
127 continue;
128 }
129
130 // zones
131 if (intfPair.first == pidZoneConfigurationIface)
132 {
133 const double* ptr = mapbox::getPtr<const double>(
134 propertyPair.second);
135 if (ptr == nullptr)
136 {
137 BMCWEB_LOG_ERROR << "Field Illegal "
138 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700139 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700140 return;
141 }
142 zones[name][propertyPair.first] = *ptr;
143 }
144
145 // pid and fans are off the same configuration
146 if (intfPair.first == pidConfigurationIface)
147 {
148 const std::string* classPtr = nullptr;
149 auto findClass = intfPair.second.find("Class");
150 if (findClass != intfPair.second.end())
151 {
152 classPtr = mapbox::getPtr<const std::string>(
153 findClass->second);
154 }
155 if (classPtr == nullptr)
156 {
157 BMCWEB_LOG_ERROR << "Pid Class Field illegal";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700158 messages::internalError(asyncResp->res,
159 "Class");
James Feist5b4aa862018-08-16 14:07:01 -0700160 return;
161 }
162 bool isFan = *classPtr == "fan";
163 nlohmann::json& element =
164 isFan ? fans[name] : pids[name];
165 if (isFan)
166 {
167 element["@odata.id"] =
168 "/redfish/v1/Managers/bmc#/Oem/"
169 "OpenBmc/Fan/FanControllers/" +
170 std::string(name);
171 element["@odata.type"] =
172 "#OemManager.FanController";
173
174 element["@odata.context"] =
175 "/redfish/v1/"
176 "$metadata#OemManager.FanController";
177 }
178 else
179 {
180 element["@odata.id"] =
181 "/redfish/v1/Managers/bmc#/Oem/"
182 "OpenBmc/Fan/PidControllers/" +
183 std::string(name);
184 element["@odata.type"] =
185 "#OemManager.PidController";
186 element["@odata.context"] =
187 "/redfish/v1/$metadata"
188 "#OemManager.PidController";
189 }
190
191 if (propertyPair.first == "Zones")
192 {
193 const std::vector<std::string>* inputs =
194 mapbox::getPtr<
195 const std::vector<std::string>>(
196 propertyPair.second);
197
198 if (inputs == nullptr)
199 {
200 BMCWEB_LOG_ERROR
201 << "Zones Pid Field Illegal";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700202 messages::internalError(asyncResp->res,
203 "Zones");
James Feist5b4aa862018-08-16 14:07:01 -0700204 return;
205 }
206 auto& data = element[propertyPair.first];
207 data = nlohmann::json::array();
208 for (std::string itemCopy : *inputs)
209 {
210 dbus::utility::escapePathForDbus(itemCopy);
211 data.push_back(
212 {{"@odata.id",
213 "/redfish/v1/Managers/bmc#/Oem/"
214 "OpenBmc/Fan/FanZones/" +
215 itemCopy}});
216 }
217 }
218 // todo(james): may never happen, but this
219 // assumes configuration data referenced in the
220 // PID config is provided by the same daemon, we
221 // could add another loop to cover all cases,
222 // but I'm okay kicking this can down the road a
223 // bit
224
225 else if (propertyPair.first == "Inputs" ||
226 propertyPair.first == "Outputs")
227 {
228 auto& data = element[propertyPair.first];
229 const std::vector<std::string>* inputs =
230 mapbox::getPtr<
231 const std::vector<std::string>>(
232 propertyPair.second);
233
234 if (inputs == nullptr)
235 {
236 BMCWEB_LOG_ERROR << "Field Illegal "
237 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700238 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700239 return;
240 }
241 data = *inputs;
242 } // doubles
243 else if (propertyPair.first ==
244 "FFGainCoefficient" ||
245 propertyPair.first == "FFOffCoefficient" ||
246 propertyPair.first == "ICoefficient" ||
247 propertyPair.first == "ILimitMax" ||
248 propertyPair.first == "ILimitMin" ||
249 propertyPair.first == "OutLimitMax" ||
250 propertyPair.first == "OutLimitMin" ||
251 propertyPair.first == "PCoefficient" ||
252 propertyPair.first == "SlewNeg" ||
253 propertyPair.first == "SlewPos")
254 {
255 const double* ptr =
256 mapbox::getPtr<const double>(
257 propertyPair.second);
258 if (ptr == nullptr)
259 {
260 BMCWEB_LOG_ERROR << "Field Illegal "
261 << propertyPair.first;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700262 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700263 return;
264 }
265 element[propertyPair.first] = *ptr;
266 }
267 }
268 }
269 }
270 }
271 },
272 connection, path, objectManagerIface, "GetManagedObjects");
273}
Jennifer Leeca537922018-08-10 10:07:30 -0700274
James Feist83ff9ab2018-08-31 10:18:24 -0700275enum class CreatePIDRet
276{
277 fail,
278 del,
279 patch
280};
281
282static CreatePIDRet createPidInterface(
283 const std::shared_ptr<AsyncResp>& response, const std::string& type,
284 const nlohmann::json& record, const std::string& path,
285 const dbus::utility::ManagedObjectType& managedObj, bool createNewObject,
286 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>&
287 output,
288 std::string& chassis)
289{
290
291 if (type == "PidControllers" || type == "FanControllers")
292 {
293 if (createNewObject)
294 {
295 output["Class"] = type == "PidControllers" ? std::string("temp")
296 : std::string("fan");
297 output["Type"] = std::string("Pid");
298 }
299 else if (record == nullptr)
300 {
301 // delete interface
302 crow::connections::systemBus->async_method_call(
303 [response,
304 path{std::string(path)}](const boost::system::error_code ec) {
305 if (ec)
306 {
307 BMCWEB_LOG_ERROR << "Error patching " << path << ": "
308 << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700309 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700310 }
311 },
312 "xyz.openbmc_project.EntityManager", path,
313 pidConfigurationIface, "Delete");
314 return CreatePIDRet::del;
315 }
316
317 for (auto& field : record.items())
318 {
319 if (field.key() == "Zones")
320 {
321 if (!field.value().is_array())
322 {
323 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700324 messages::propertyValueFormatError(
325 response->res, field.value(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700326 return CreatePIDRet::fail;
327 }
328 std::vector<std::string> inputs;
329 for (const auto& odata : field.value().items())
330 {
331 for (const auto& value : odata.value().items())
332 {
333 const std::string* path =
334 value.value().get_ptr<const std::string*>();
335 if (path == nullptr)
336 {
337 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700338 messages::propertyValueFormatError(
339 response->res, field.value().dump(),
340 field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700341 return CreatePIDRet::fail;
342 }
343 std::string input;
344 if (!dbus::utility::getNthStringFromPath(*path, 4,
345 input))
346 {
347 BMCWEB_LOG_ERROR << "Got invalid path " << *path;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700348 messages::propertyValueFormatError(
349 response->res, field.value().dump(),
350 field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700351 return CreatePIDRet::fail;
352 }
353 boost::replace_all(input, "_", " ");
354 inputs.emplace_back(std::move(input));
355 }
356 }
357 output["Zones"] = std::move(inputs);
358 }
359 else if (field.key() == "Inputs" || field.key() == "Outputs")
360 {
361 if (!field.value().is_array())
362 {
363 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700364 messages::propertyValueFormatError(
365 response->res, field.value().dump(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700366 return CreatePIDRet::fail;
367 }
368 std::vector<std::string> inputs;
369 for (const auto& value : field.value().items())
370 {
371 const std::string* sensor =
372 value.value().get_ptr<const std::string*>();
373
374 if (sensor == nullptr)
375 {
376 BMCWEB_LOG_ERROR << "Illegal Type "
377 << field.value().dump();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700378 messages::propertyValueFormatError(
379 response->res, field.value().dump(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700380 return CreatePIDRet::fail;
381 }
382
383 std::string input =
384 boost::replace_all_copy(*sensor, "_", " ");
385 inputs.push_back(std::move(input));
386 // try to find the sensor in the
387 // configuration
388 if (chassis.empty())
389 {
390 std::find_if(
391 managedObj.begin(), managedObj.end(),
392 [&chassis, sensor](const auto& obj) {
393 if (boost::algorithm::ends_with(obj.first.str,
394 *sensor))
395 {
396 return dbus::utility::getNthStringFromPath(
397 obj.first.str, 5, chassis);
398 }
399 return false;
400 });
401 }
402 }
403 output[field.key()] = inputs;
404 }
405
406 // doubles
407 else if (field.key() == "FFGainCoefficient" ||
408 field.key() == "FFOffCoefficient" ||
409 field.key() == "ICoefficient" ||
410 field.key() == "ILimitMax" || field.key() == "ILimitMin" ||
411 field.key() == "OutLimitMax" ||
412 field.key() == "OutLimitMin" ||
413 field.key() == "PCoefficient" ||
414 field.key() == "SetPoint" || field.key() == "SlewNeg" ||
415 field.key() == "SlewPos")
416 {
417 const double* ptr = field.value().get_ptr<const double*>();
418 if (ptr == nullptr)
419 {
420 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700421 messages::propertyValueFormatError(
422 response->res, field.value().dump(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700423 return CreatePIDRet::fail;
424 }
425 output[field.key()] = *ptr;
426 }
427
428 else
429 {
430 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700431 messages::propertyUnknown(response->res, field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700432 return CreatePIDRet::fail;
433 }
434 }
435 }
436 else if (type == "FanZones")
437 {
438 if (!createNewObject && record == nullptr)
439 {
440 // delete interface
441 crow::connections::systemBus->async_method_call(
442 [response,
443 path{std::string(path)}](const boost::system::error_code ec) {
444 if (ec)
445 {
446 BMCWEB_LOG_ERROR << "Error patching " << path << ": "
447 << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700448 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700449 }
450 },
451 "xyz.openbmc_project.EntityManager", path,
452 pidZoneConfigurationIface, "Delete");
453 return CreatePIDRet::del;
454 }
455 output["Type"] = std::string("Pid.Zone");
456
457 for (auto& field : record.items())
458 {
459 if (field.key() == "Chassis")
460 {
461 const std::string* chassisId = nullptr;
462 for (const auto& id : field.value().items())
463 {
464 if (id.key() != "@odata.id")
465 {
466 BMCWEB_LOG_ERROR << "Illegal Type " << id.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700467 messages::propertyUnknown(response->res, field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700468 return CreatePIDRet::fail;
469 }
470 chassisId = id.value().get_ptr<const std::string*>();
471 if (chassisId == nullptr)
472 {
Jason M. Bills35a62c72018-10-09 12:45:45 -0700473 messages::createFailedMissingReqProperties(
474 response->res, field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700475 return CreatePIDRet::fail;
476 }
477 }
478
479 // /refish/v1/chassis/chassis_name/
480 if (!dbus::utility::getNthStringFromPath(*chassisId, 3,
481 chassis))
482 {
483 BMCWEB_LOG_ERROR << "Got invalid path " << *chassisId;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700484 messages::invalidObject(response->res, *chassisId);
James Feist83ff9ab2018-08-31 10:18:24 -0700485 return CreatePIDRet::fail;
486 }
487 }
488 else if (field.key() == "FailSafePercent" ||
489 field.key() == "MinThermalRpm")
490 {
491 const double* ptr = field.value().get_ptr<const double*>();
492 if (ptr == nullptr)
493 {
494 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700495 messages::propertyValueFormatError(
496 response->res, field.value().dump(), field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700497 return CreatePIDRet::fail;
498 }
499 output[field.key()] = *ptr;
500 }
501 else
502 {
503 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700504 messages::propertyUnknown(response->res, field.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700505 return CreatePIDRet::fail;
506 }
507 }
508 }
509 else
510 {
511 BMCWEB_LOG_ERROR << "Illegal Type " << type;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700512 messages::propertyUnknown(response->res, type);
James Feist83ff9ab2018-08-31 10:18:24 -0700513 return CreatePIDRet::fail;
514 }
515 return CreatePIDRet::patch;
516}
517
Ed Tanous1abe55e2018-09-05 08:30:59 -0700518class Manager : public Node
519{
520 public:
James Feist5b4aa862018-08-16 14:07:01 -0700521 Manager(CrowApp& app) : Node(app, "/redfish/v1/Managers/bmc/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700522 {
James Feist5b4aa862018-08-16 14:07:01 -0700523 Node::json["@odata.id"] = "/redfish/v1/Managers/bmc";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700524 Node::json["@odata.type"] = "#Manager.v1_3_0.Manager";
525 Node::json["@odata.context"] = "/redfish/v1/$metadata#Manager.Manager";
James Feist5b4aa862018-08-16 14:07:01 -0700526 Node::json["Id"] = "bmc";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700527 Node::json["Name"] = "OpenBmc Manager";
528 Node::json["Description"] = "Baseboard Management Controller";
529 Node::json["PowerState"] = "On";
Jennifer Leeca537922018-08-10 10:07:30 -0700530 Node::json["ManagerType"] = "BMC";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700531 Node::json["UUID"] =
532 app.template getMiddleware<crow::persistent_data::Middleware>()
533 .systemUuid;
Jennifer Leeca537922018-08-10 10:07:30 -0700534 Node::json["Model"] = "OpenBmc"; // TODO(ed), get model
535 Node::json["EthernetInterfaces"] = {
James Feist5b4aa862018-08-16 14:07:01 -0700536 {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces"}};
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800537
Ed Tanous1abe55e2018-09-05 08:30:59 -0700538 entityPrivileges = {
539 {boost::beast::http::verb::get, {{"Login"}}},
540 {boost::beast::http::verb::head, {{"Login"}}},
541 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
542 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
543 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
544 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
James Feist5b4aa862018-08-16 14:07:01 -0700545
546 // default oem data
547 nlohmann::json& oem = Node::json["Oem"];
548 nlohmann::json& oemOpenbmc = oem["OpenBmc"];
549 oem["@odata.type"] = "#OemManager.Oem";
550 oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem";
551 oem["@odata.context"] = "/redfish/v1/$metadata#OemManager.Oem";
552 oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc";
553 oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc";
554 oemOpenbmc["@odata.context"] =
555 "/redfish/v1/$metadata#OemManager.OpenBmc";
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100556 }
557
Ed Tanous1abe55e2018-09-05 08:30:59 -0700558 private:
James Feist5b4aa862018-08-16 14:07:01 -0700559 void getPidValues(std::shared_ptr<AsyncResp> asyncResp)
560 {
561 crow::connections::systemBus->async_method_call(
562 [asyncResp](const boost::system::error_code ec,
563 const crow::openbmc_mapper::GetSubTreeType& subtree) {
564 if (ec)
565 {
566 BMCWEB_LOG_ERROR << ec;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700567 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700568 return;
569 }
570
571 // create map of <connection, path to objMgr>>
572 boost::container::flat_map<std::string, std::string>
573 objectMgrPaths;
James Feist6bce33b2018-10-22 12:05:56 -0700574 boost::container::flat_set<std::string> calledConnections;
James Feist5b4aa862018-08-16 14:07:01 -0700575 for (const auto& pathGroup : subtree)
576 {
577 for (const auto& connectionGroup : pathGroup.second)
578 {
James Feist6bce33b2018-10-22 12:05:56 -0700579 auto findConnection =
580 calledConnections.find(connectionGroup.first);
581 if (findConnection != calledConnections.end())
582 {
583 break;
584 }
James Feist5b4aa862018-08-16 14:07:01 -0700585 for (const std::string& interface :
586 connectionGroup.second)
587 {
588 if (interface == objectManagerIface)
589 {
590 objectMgrPaths[connectionGroup.first] =
591 pathGroup.first;
592 }
593 // this list is alphabetical, so we
594 // should have found the objMgr by now
595 if (interface == pidConfigurationIface ||
596 interface == pidZoneConfigurationIface)
597 {
598 auto findObjMgr =
599 objectMgrPaths.find(connectionGroup.first);
600 if (findObjMgr == objectMgrPaths.end())
601 {
602 BMCWEB_LOG_DEBUG << connectionGroup.first
603 << "Has no Object Manager";
604 continue;
605 }
James Feist6bce33b2018-10-22 12:05:56 -0700606
607 calledConnections.insert(connectionGroup.first);
608
James Feist5b4aa862018-08-16 14:07:01 -0700609 asyncPopulatePid(findObjMgr->first,
610 findObjMgr->second, asyncResp);
611 break;
612 }
613 }
614 }
615 }
616 },
617 "xyz.openbmc_project.ObjectMapper",
618 "/xyz/openbmc_project/object_mapper",
619 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
620 std::array<const char*, 3>{pidConfigurationIface,
621 pidZoneConfigurationIface,
622 objectManagerIface});
623 }
624
625 void doGet(crow::Response& res, const crow::Request& req,
626 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700627 {
Jennifer Leeca537922018-08-10 10:07:30 -0700628 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
629 asyncResp->res.jsonValue = Node::json;
630
Ed Tanous1abe55e2018-09-05 08:30:59 -0700631 Node::json["DateTime"] = getDateTime();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700632 res.jsonValue = Node::json;
James Feist5b4aa862018-08-16 14:07:01 -0700633
Jennifer Leeca537922018-08-10 10:07:30 -0700634 crow::connections::systemBus->async_method_call(
635 [asyncResp](const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -0700636 const dbus::utility::ManagedObjectType& resp) {
Jennifer Leeca537922018-08-10 10:07:30 -0700637 if (ec)
638 {
639 BMCWEB_LOG_ERROR << "Error while getting Software Version";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700640 messages::internalError(asyncResp->res);
Jennifer Leeca537922018-08-10 10:07:30 -0700641 return;
642 }
643
James Feist5b4aa862018-08-16 14:07:01 -0700644 for (auto& objpath : resp)
Jennifer Leeca537922018-08-10 10:07:30 -0700645 {
James Feist5b4aa862018-08-16 14:07:01 -0700646 for (auto& interface : objpath.second)
Jennifer Leeca537922018-08-10 10:07:30 -0700647 {
648 // If interface is xyz.openbmc_project.Software.Version,
649 // this is what we're looking for.
650 if (interface.first ==
651 "xyz.openbmc_project.Software.Version")
652 {
653 // Cut out everyting until last "/", ...
James Feist5b4aa862018-08-16 14:07:01 -0700654 const std::string& iface_id = objpath.first;
655 for (auto& property : interface.second)
Jennifer Leeca537922018-08-10 10:07:30 -0700656 {
657 if (property.first == "Version")
658 {
James Feist5b4aa862018-08-16 14:07:01 -0700659 const std::string* value =
Jennifer Leeca537922018-08-10 10:07:30 -0700660 mapbox::getPtr<const std::string>(
661 property.second);
662 if (value == nullptr)
663 {
664 continue;
665 }
666 asyncResp->res
667 .jsonValue["FirmwareVersion"] = *value;
668 }
669 }
670 }
671 }
672 }
673 },
674 "xyz.openbmc_project.Software.BMC.Updater",
675 "/xyz/openbmc_project/software",
676 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
James Feist5b4aa862018-08-16 14:07:01 -0700677 getPidValues(asyncResp);
678 }
James Feist83ff9ab2018-08-31 10:18:24 -0700679 void setPidValues(std::shared_ptr<AsyncResp> response,
680 const nlohmann::json& data)
681 {
682 // todo(james): might make sense to do a mapper call here if this
683 // interface gets more traction
684 crow::connections::systemBus->async_method_call(
685 [response,
686 data](const boost::system::error_code ec,
687 const dbus::utility::ManagedObjectType& managedObj) {
688 if (ec)
689 {
690 BMCWEB_LOG_ERROR << "Error communicating to Entity Manager";
Jason M. Bills35a62c72018-10-09 12:45:45 -0700691 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700692 return;
693 }
694 for (const auto& type : data.items())
695 {
696 if (!type.value().is_object())
697 {
698 BMCWEB_LOG_ERROR << "Illegal Type " << type.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700699 messages::propertyValueFormatError(
700 response->res, type.value(), type.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700701 return;
702 }
703 for (const auto& record : type.value().items())
704 {
705 const std::string& name = record.key();
706 auto pathItr =
707 std::find_if(managedObj.begin(), managedObj.end(),
708 [&name](const auto& obj) {
709 return boost::algorithm::ends_with(
710 obj.first.str, name);
711 });
712 boost::container::flat_map<
713 std::string, dbus::utility::DbusVariantType>
714 output;
715
716 output.reserve(16); // The pid interface length
717
718 // determines if we're patching entity-manager or
719 // creating a new object
720 bool createNewObject = (pathItr == managedObj.end());
721 if (type.key() == "PidControllers" ||
722 type.key() == "FanControllers")
723 {
724 if (!createNewObject &&
725 pathItr->second.find(pidConfigurationIface) ==
726 pathItr->second.end())
727 {
728 createNewObject = true;
729 }
730 }
731 else if (!createNewObject &&
732 pathItr->second.find(
733 pidZoneConfigurationIface) ==
734 pathItr->second.end())
735 {
736 createNewObject = true;
737 }
738 output["Name"] =
739 boost::replace_all_copy(name, "_", " ");
740
741 std::string chassis;
742 CreatePIDRet ret = createPidInterface(
743 response, type.key(), record.value(),
744 pathItr->first.str, managedObj, createNewObject,
745 output, chassis);
746 if (ret == CreatePIDRet::fail)
747 {
748 return;
749 }
750 else if (ret == CreatePIDRet::del)
751 {
752 continue;
753 }
754
755 if (!createNewObject)
756 {
757 for (const auto& property : output)
758 {
759 const char* iface =
760 type.key() == "FanZones"
761 ? pidZoneConfigurationIface
762 : pidConfigurationIface;
763 crow::connections::systemBus->async_method_call(
764 [response,
765 propertyName{std::string(property.first)}](
766 const boost::system::error_code ec) {
767 if (ec)
768 {
769 BMCWEB_LOG_ERROR
770 << "Error patching "
771 << propertyName << ": " << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700772 messages::internalError(
773 response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700774 }
775 },
776 "xyz.openbmc_project.EntityManager",
777 pathItr->first.str,
778 "org.freedesktop.DBus.Properties", "Set",
779 std::string(iface), property.first,
780 property.second);
781 }
782 }
783 else
784 {
785 if (chassis.empty())
786 {
787 BMCWEB_LOG_ERROR
788 << "Failed to get chassis from config";
Jason M. Bills35a62c72018-10-09 12:45:45 -0700789 messages::invalidObject(response->res, name);
James Feist83ff9ab2018-08-31 10:18:24 -0700790 return;
791 }
792
793 bool foundChassis = false;
794 for (const auto& obj : managedObj)
795 {
796 if (boost::algorithm::ends_with(obj.first.str,
797 chassis))
798 {
799 chassis = obj.first.str;
800 foundChassis = true;
801 break;
802 }
803 }
804 if (!foundChassis)
805 {
806 BMCWEB_LOG_ERROR
807 << "Failed to find chassis on dbus";
Jason M. Bills35a62c72018-10-09 12:45:45 -0700808 messages::resourceMissingAtURI(
809 response->res,
810 "/redfish/v1/Chassis/" + chassis);
James Feist83ff9ab2018-08-31 10:18:24 -0700811 return;
812 }
813
814 crow::connections::systemBus->async_method_call(
815 [response](const boost::system::error_code ec) {
816 if (ec)
817 {
818 BMCWEB_LOG_ERROR
819 << "Error Adding Pid Object " << ec;
Jason M. Bills35a62c72018-10-09 12:45:45 -0700820 messages::internalError(response->res);
James Feist83ff9ab2018-08-31 10:18:24 -0700821 }
822 },
823 "xyz.openbmc_project.EntityManager", chassis,
824 "xyz.openbmc_project.AddObject", "AddObject",
825 output);
826 }
827 }
828 }
829 },
830 "xyz.openbmc_project.EntityManager", "/", objectManagerIface,
831 "GetManagedObjects");
832 }
James Feist5b4aa862018-08-16 14:07:01 -0700833
834 void doPatch(crow::Response& res, const crow::Request& req,
835 const std::vector<std::string>& params) override
836 {
James Feist83ff9ab2018-08-31 10:18:24 -0700837 nlohmann::json patch;
838 if (!json_util::processJsonFromRequest(res, req, patch))
839 {
840 return;
841 }
842 std::shared_ptr<AsyncResp> response = std::make_shared<AsyncResp>(res);
843 for (const auto& topLevel : patch.items())
844 {
845 if (topLevel.key() == "Oem")
846 {
847 if (!topLevel.value().is_object())
848 {
849 BMCWEB_LOG_ERROR << "Bad Patch " << topLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700850 messages::propertyValueFormatError(
851 response->res, topLevel.key(), "OemManager.Oem");
James Feist83ff9ab2018-08-31 10:18:24 -0700852 return;
853 }
854 }
855 else
856 {
857 BMCWEB_LOG_ERROR << "Bad Patch " << topLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700858 messages::propertyUnknown(response->res, topLevel.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700859 return;
860 }
861 for (const auto& oemLevel : topLevel.value().items())
862 {
863 if (oemLevel.key() == "OpenBmc")
864 {
865 if (!oemLevel.value().is_object())
866 {
867 BMCWEB_LOG_ERROR << "Bad Patch " << oemLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700868 messages::propertyValueFormatError(
869 response->res, topLevel.key(),
870 "OemManager.OpenBmc");
James Feist83ff9ab2018-08-31 10:18:24 -0700871 return;
872 }
873 for (const auto& typeLevel : oemLevel.value().items())
874 {
875
876 if (typeLevel.key() == "Fan")
877 {
878 if (!typeLevel.value().is_object())
879 {
880 BMCWEB_LOG_ERROR << "Bad Patch "
881 << typeLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700882 messages::propertyValueFormatError(
883 response->res, typeLevel.value().dump(),
884 typeLevel.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700885 return;
886 }
887 setPidValues(response,
888 std::move(typeLevel.value()));
889 }
890 else
891 {
892 BMCWEB_LOG_ERROR << "Bad Patch " << typeLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700893 messages::propertyUnknown(response->res,
894 typeLevel.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700895 return;
896 }
897 }
898 }
899 else
900 {
901 BMCWEB_LOG_ERROR << "Bad Patch " << oemLevel.key();
Jason M. Bills35a62c72018-10-09 12:45:45 -0700902 messages::propertyUnknown(response->res, oemLevel.key());
James Feist83ff9ab2018-08-31 10:18:24 -0700903 return;
904 }
905 }
906 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700907 }
908
909 std::string getDateTime() const
910 {
911 std::array<char, 128> dateTime;
912 std::string redfishDateTime("0000-00-00T00:00:00Z00:00");
913 std::time_t time = std::time(nullptr);
914
915 if (std::strftime(dateTime.begin(), dateTime.size(), "%FT%T%z",
916 std::localtime(&time)))
917 {
918 // insert the colon required by the ISO 8601 standard
919 redfishDateTime = std::string(dateTime.data());
920 redfishDateTime.insert(redfishDateTime.end() - 2, ':');
921 }
922
923 return redfishDateTime;
924 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100925};
926
Ed Tanous1abe55e2018-09-05 08:30:59 -0700927class ManagerCollection : public Node
928{
929 public:
James Feist5b4aa862018-08-16 14:07:01 -0700930 ManagerCollection(CrowApp& app) : Node(app, "/redfish/v1/Managers/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700931 {
932 Node::json["@odata.id"] = "/redfish/v1/Managers";
933 Node::json["@odata.type"] = "#ManagerCollection.ManagerCollection";
934 Node::json["@odata.context"] =
935 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection";
936 Node::json["Name"] = "Manager Collection";
937 Node::json["Members@odata.count"] = 1;
James Feist5b4aa862018-08-16 14:07:01 -0700938 Node::json["Members"] = {{{"@odata.id", "/redfish/v1/Managers/bmc"}}};
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800939
Ed Tanous1abe55e2018-09-05 08:30:59 -0700940 entityPrivileges = {
941 {boost::beast::http::verb::get, {{"Login"}}},
942 {boost::beast::http::verb::head, {{"Login"}}},
943 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
944 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
945 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
946 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
947 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100948
Ed Tanous1abe55e2018-09-05 08:30:59 -0700949 private:
James Feist5b4aa862018-08-16 14:07:01 -0700950 void doGet(crow::Response& res, const crow::Request& req,
951 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700952 {
James Feist83ff9ab2018-08-31 10:18:24 -0700953 // Collections don't include the static data added by SubRoute
954 // because it has a duplicate entry for members
Ed Tanous1abe55e2018-09-05 08:30:59 -0700955 res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
956 res.jsonValue["@odata.type"] = "#ManagerCollection.ManagerCollection";
957 res.jsonValue["@odata.context"] =
958 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection";
959 res.jsonValue["Name"] = "Manager Collection";
960 res.jsonValue["Members@odata.count"] = 1;
961 res.jsonValue["Members"] = {
James Feist5b4aa862018-08-16 14:07:01 -0700962 {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700963 res.end();
964 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100965};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700966} // namespace redfish