blob: 12ee3b694992aeabdb4833500d48b694fdf9c3b5 [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;
309 response->res.result(
310 boost::beast::http::status::internal_server_error);
311 }
312 },
313 "xyz.openbmc_project.EntityManager", path,
314 pidConfigurationIface, "Delete");
315 return CreatePIDRet::del;
316 }
317
318 for (auto& field : record.items())
319 {
320 if (field.key() == "Zones")
321 {
322 if (!field.value().is_array())
323 {
324 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
325 messages::addMessageToErrorJson(
326 response->res.jsonValue,
327 messages::propertyValueFormatError(field.value(),
328 field.key()));
329 response->res.result(
330 boost::beast::http::status::bad_request);
331 return CreatePIDRet::fail;
332 }
333 std::vector<std::string> inputs;
334 for (const auto& odata : field.value().items())
335 {
336 for (const auto& value : odata.value().items())
337 {
338 const std::string* path =
339 value.value().get_ptr<const std::string*>();
340 if (path == nullptr)
341 {
342 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
343 messages::addMessageToErrorJson(
344 response->res.jsonValue,
345 messages::propertyValueFormatError(
346 field.value().dump(), field.key()));
347 response->res.result(
348 boost::beast::http::status::bad_request);
349 return CreatePIDRet::fail;
350 }
351 std::string input;
352 if (!dbus::utility::getNthStringFromPath(*path, 4,
353 input))
354 {
355 BMCWEB_LOG_ERROR << "Got invalid path " << *path;
356 messages::addMessageToErrorJson(
357 response->res.jsonValue,
358 messages::propertyValueFormatError(
359 field.value().dump(), field.key()));
360 response->res.result(
361 boost::beast::http::status::bad_request);
362 return CreatePIDRet::fail;
363 }
364 boost::replace_all(input, "_", " ");
365 inputs.emplace_back(std::move(input));
366 }
367 }
368 output["Zones"] = std::move(inputs);
369 }
370 else if (field.key() == "Inputs" || field.key() == "Outputs")
371 {
372 if (!field.value().is_array())
373 {
374 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
375 messages::addMessageToErrorJson(
376 response->res.jsonValue,
377 messages::propertyValueFormatError(field.value().dump(),
378 field.key()));
379 response->res.result(
380 boost::beast::http::status::bad_request);
381 return CreatePIDRet::fail;
382 }
383 std::vector<std::string> inputs;
384 for (const auto& value : field.value().items())
385 {
386 const std::string* sensor =
387 value.value().get_ptr<const std::string*>();
388
389 if (sensor == nullptr)
390 {
391 BMCWEB_LOG_ERROR << "Illegal Type "
392 << field.value().dump();
393 messages::addMessageToErrorJson(
394 response->res.jsonValue,
395 messages::propertyValueFormatError(
396 field.value().dump(), field.key()));
397 response->res.result(
398 boost::beast::http::status::bad_request);
399 return CreatePIDRet::fail;
400 }
401
402 std::string input =
403 boost::replace_all_copy(*sensor, "_", " ");
404 inputs.push_back(std::move(input));
405 // try to find the sensor in the
406 // configuration
407 if (chassis.empty())
408 {
409 std::find_if(
410 managedObj.begin(), managedObj.end(),
411 [&chassis, sensor](const auto& obj) {
412 if (boost::algorithm::ends_with(obj.first.str,
413 *sensor))
414 {
415 return dbus::utility::getNthStringFromPath(
416 obj.first.str, 5, chassis);
417 }
418 return false;
419 });
420 }
421 }
422 output[field.key()] = inputs;
423 }
424
425 // doubles
426 else if (field.key() == "FFGainCoefficient" ||
427 field.key() == "FFOffCoefficient" ||
428 field.key() == "ICoefficient" ||
429 field.key() == "ILimitMax" || field.key() == "ILimitMin" ||
430 field.key() == "OutLimitMax" ||
431 field.key() == "OutLimitMin" ||
432 field.key() == "PCoefficient" ||
433 field.key() == "SetPoint" || field.key() == "SlewNeg" ||
434 field.key() == "SlewPos")
435 {
436 const double* ptr = field.value().get_ptr<const double*>();
437 if (ptr == nullptr)
438 {
439 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
440 messages::addMessageToErrorJson(
441 response->res.jsonValue,
442 messages::propertyValueFormatError(field.value().dump(),
443 field.key()));
444 response->res.result(
445 boost::beast::http::status::bad_request);
446 return CreatePIDRet::fail;
447 }
448 output[field.key()] = *ptr;
449 }
450
451 else
452 {
453 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
454 messages::addMessageToErrorJson(
455 response->res.jsonValue,
456 messages::propertyUnknown(field.key()));
457 response->res.result(boost::beast::http::status::bad_request);
458 return CreatePIDRet::fail;
459 }
460 }
461 }
462 else if (type == "FanZones")
463 {
464 if (!createNewObject && record == nullptr)
465 {
466 // delete interface
467 crow::connections::systemBus->async_method_call(
468 [response,
469 path{std::string(path)}](const boost::system::error_code ec) {
470 if (ec)
471 {
472 BMCWEB_LOG_ERROR << "Error patching " << path << ": "
473 << ec;
474 response->res.result(
475 boost::beast::http::status::internal_server_error);
476 }
477 },
478 "xyz.openbmc_project.EntityManager", path,
479 pidZoneConfigurationIface, "Delete");
480 return CreatePIDRet::del;
481 }
482 output["Type"] = std::string("Pid.Zone");
483
484 for (auto& field : record.items())
485 {
486 if (field.key() == "Chassis")
487 {
488 const std::string* chassisId = nullptr;
489 for (const auto& id : field.value().items())
490 {
491 if (id.key() != "@odata.id")
492 {
493 BMCWEB_LOG_ERROR << "Illegal Type " << id.key();
494 messages::addMessageToErrorJson(
495 response->res.jsonValue,
496 messages::propertyUnknown(field.key()));
497 response->res.result(
498 boost::beast::http::status::bad_request);
499 return CreatePIDRet::fail;
500 }
501 chassisId = id.value().get_ptr<const std::string*>();
502 if (chassisId == nullptr)
503 {
504 messages::addMessageToErrorJson(
505 response->res.jsonValue,
506 messages::createFailedMissingReqProperties(
507 field.key()));
508 response->res.result(
509 boost::beast::http::status::bad_request);
510 return CreatePIDRet::fail;
511 }
512 }
513
514 // /refish/v1/chassis/chassis_name/
515 if (!dbus::utility::getNthStringFromPath(*chassisId, 3,
516 chassis))
517 {
518 BMCWEB_LOG_ERROR << "Got invalid path " << *chassisId;
519 response->res.result(
520 boost::beast::http::status::bad_request);
521 return CreatePIDRet::fail;
522 }
523 }
524 else if (field.key() == "FailSafePercent" ||
525 field.key() == "MinThermalRpm")
526 {
527 const double* ptr = field.value().get_ptr<const double*>();
528 if (ptr == nullptr)
529 {
530 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
531 messages::addMessageToErrorJson(
532 response->res.jsonValue,
533 messages::propertyValueFormatError(field.value().dump(),
534 field.key()));
535 response->res.result(
536 boost::beast::http::status::bad_request);
537 return CreatePIDRet::fail;
538 }
539 output[field.key()] = *ptr;
540 }
541 else
542 {
543 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
544 messages::addMessageToErrorJson(
545 response->res.jsonValue,
546 messages::propertyUnknown(field.key()));
547 response->res.result(boost::beast::http::status::bad_request);
548 return CreatePIDRet::fail;
549 }
550 }
551 }
552 else
553 {
554 BMCWEB_LOG_ERROR << "Illegal Type " << type;
555 messages::addMessageToErrorJson(response->res.jsonValue,
556 messages::propertyUnknown(type));
557 response->res.result(boost::beast::http::status::bad_request);
558 return CreatePIDRet::fail;
559 }
560 return CreatePIDRet::patch;
561}
562
Ed Tanous1abe55e2018-09-05 08:30:59 -0700563class Manager : public Node
564{
565 public:
James Feist5b4aa862018-08-16 14:07:01 -0700566 Manager(CrowApp& app) : Node(app, "/redfish/v1/Managers/bmc/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700567 {
James Feist5b4aa862018-08-16 14:07:01 -0700568 Node::json["@odata.id"] = "/redfish/v1/Managers/bmc";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700569 Node::json["@odata.type"] = "#Manager.v1_3_0.Manager";
570 Node::json["@odata.context"] = "/redfish/v1/$metadata#Manager.Manager";
James Feist5b4aa862018-08-16 14:07:01 -0700571 Node::json["Id"] = "bmc";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700572 Node::json["Name"] = "OpenBmc Manager";
573 Node::json["Description"] = "Baseboard Management Controller";
574 Node::json["PowerState"] = "On";
Jennifer Leeca537922018-08-10 10:07:30 -0700575 Node::json["ManagerType"] = "BMC";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700576 Node::json["UUID"] =
577 app.template getMiddleware<crow::persistent_data::Middleware>()
578 .systemUuid;
Jennifer Leeca537922018-08-10 10:07:30 -0700579 Node::json["Model"] = "OpenBmc"; // TODO(ed), get model
580 Node::json["EthernetInterfaces"] = {
James Feist5b4aa862018-08-16 14:07:01 -0700581 {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces"}};
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800582
Ed Tanous1abe55e2018-09-05 08:30:59 -0700583 entityPrivileges = {
584 {boost::beast::http::verb::get, {{"Login"}}},
585 {boost::beast::http::verb::head, {{"Login"}}},
586 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
587 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
588 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
589 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
James Feist5b4aa862018-08-16 14:07:01 -0700590
591 // default oem data
592 nlohmann::json& oem = Node::json["Oem"];
593 nlohmann::json& oemOpenbmc = oem["OpenBmc"];
594 oem["@odata.type"] = "#OemManager.Oem";
595 oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem";
596 oem["@odata.context"] = "/redfish/v1/$metadata#OemManager.Oem";
597 oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc";
598 oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc";
599 oemOpenbmc["@odata.context"] =
600 "/redfish/v1/$metadata#OemManager.OpenBmc";
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100601 }
602
Ed Tanous1abe55e2018-09-05 08:30:59 -0700603 private:
James Feist5b4aa862018-08-16 14:07:01 -0700604 void getPidValues(std::shared_ptr<AsyncResp> asyncResp)
605 {
606 crow::connections::systemBus->async_method_call(
607 [asyncResp](const boost::system::error_code ec,
608 const crow::openbmc_mapper::GetSubTreeType& subtree) {
609 if (ec)
610 {
611 BMCWEB_LOG_ERROR << ec;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700612 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700613 return;
614 }
615
616 // create map of <connection, path to objMgr>>
617 boost::container::flat_map<std::string, std::string>
618 objectMgrPaths;
James Feist6bce33b2018-10-22 12:05:56 -0700619 boost::container::flat_set<std::string> calledConnections;
James Feist5b4aa862018-08-16 14:07:01 -0700620 for (const auto& pathGroup : subtree)
621 {
622 for (const auto& connectionGroup : pathGroup.second)
623 {
James Feist6bce33b2018-10-22 12:05:56 -0700624 auto findConnection =
625 calledConnections.find(connectionGroup.first);
626 if (findConnection != calledConnections.end())
627 {
628 break;
629 }
James Feist5b4aa862018-08-16 14:07:01 -0700630 for (const std::string& interface :
631 connectionGroup.second)
632 {
633 if (interface == objectManagerIface)
634 {
635 objectMgrPaths[connectionGroup.first] =
636 pathGroup.first;
637 }
638 // this list is alphabetical, so we
639 // should have found the objMgr by now
640 if (interface == pidConfigurationIface ||
641 interface == pidZoneConfigurationIface)
642 {
643 auto findObjMgr =
644 objectMgrPaths.find(connectionGroup.first);
645 if (findObjMgr == objectMgrPaths.end())
646 {
647 BMCWEB_LOG_DEBUG << connectionGroup.first
648 << "Has no Object Manager";
649 continue;
650 }
James Feist6bce33b2018-10-22 12:05:56 -0700651
652 calledConnections.insert(connectionGroup.first);
653
James Feist5b4aa862018-08-16 14:07:01 -0700654 asyncPopulatePid(findObjMgr->first,
655 findObjMgr->second, asyncResp);
656 break;
657 }
658 }
659 }
660 }
661 },
662 "xyz.openbmc_project.ObjectMapper",
663 "/xyz/openbmc_project/object_mapper",
664 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
665 std::array<const char*, 3>{pidConfigurationIface,
666 pidZoneConfigurationIface,
667 objectManagerIface});
668 }
669
670 void doGet(crow::Response& res, const crow::Request& req,
671 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700672 {
Jennifer Leeca537922018-08-10 10:07:30 -0700673 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
674 asyncResp->res.jsonValue = Node::json;
675
Ed Tanous1abe55e2018-09-05 08:30:59 -0700676 Node::json["DateTime"] = getDateTime();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700677 res.jsonValue = Node::json;
James Feist5b4aa862018-08-16 14:07:01 -0700678
Jennifer Leeca537922018-08-10 10:07:30 -0700679 crow::connections::systemBus->async_method_call(
680 [asyncResp](const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -0700681 const dbus::utility::ManagedObjectType& resp) {
Jennifer Leeca537922018-08-10 10:07:30 -0700682 if (ec)
683 {
684 BMCWEB_LOG_ERROR << "Error while getting Software Version";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700685 messages::internalError(asyncResp->res);
Jennifer Leeca537922018-08-10 10:07:30 -0700686 return;
687 }
688
James Feist5b4aa862018-08-16 14:07:01 -0700689 for (auto& objpath : resp)
Jennifer Leeca537922018-08-10 10:07:30 -0700690 {
James Feist5b4aa862018-08-16 14:07:01 -0700691 for (auto& interface : objpath.second)
Jennifer Leeca537922018-08-10 10:07:30 -0700692 {
693 // If interface is xyz.openbmc_project.Software.Version,
694 // this is what we're looking for.
695 if (interface.first ==
696 "xyz.openbmc_project.Software.Version")
697 {
698 // Cut out everyting until last "/", ...
James Feist5b4aa862018-08-16 14:07:01 -0700699 const std::string& iface_id = objpath.first;
700 for (auto& property : interface.second)
Jennifer Leeca537922018-08-10 10:07:30 -0700701 {
702 if (property.first == "Version")
703 {
James Feist5b4aa862018-08-16 14:07:01 -0700704 const std::string* value =
Jennifer Leeca537922018-08-10 10:07:30 -0700705 mapbox::getPtr<const std::string>(
706 property.second);
707 if (value == nullptr)
708 {
709 continue;
710 }
711 asyncResp->res
712 .jsonValue["FirmwareVersion"] = *value;
713 }
714 }
715 }
716 }
717 }
718 },
719 "xyz.openbmc_project.Software.BMC.Updater",
720 "/xyz/openbmc_project/software",
721 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
James Feist5b4aa862018-08-16 14:07:01 -0700722 getPidValues(asyncResp);
723 }
James Feist83ff9ab2018-08-31 10:18:24 -0700724 void setPidValues(std::shared_ptr<AsyncResp> response,
725 const nlohmann::json& data)
726 {
727 // todo(james): might make sense to do a mapper call here if this
728 // interface gets more traction
729 crow::connections::systemBus->async_method_call(
730 [response,
731 data](const boost::system::error_code ec,
732 const dbus::utility::ManagedObjectType& managedObj) {
733 if (ec)
734 {
735 BMCWEB_LOG_ERROR << "Error communicating to Entity Manager";
736 response->res.result(
737 boost::beast::http::status::internal_server_error);
738 return;
739 }
740 for (const auto& type : data.items())
741 {
742 if (!type.value().is_object())
743 {
744 BMCWEB_LOG_ERROR << "Illegal Type " << type.key();
745 messages::addMessageToErrorJson(
746 response->res.jsonValue,
747 messages::propertyValueFormatError(type.value(),
748 type.key()));
749 response->res.result(
750 boost::beast::http::status::bad_request);
751 return;
752 }
753 for (const auto& record : type.value().items())
754 {
755 const std::string& name = record.key();
756 auto pathItr =
757 std::find_if(managedObj.begin(), managedObj.end(),
758 [&name](const auto& obj) {
759 return boost::algorithm::ends_with(
760 obj.first.str, name);
761 });
762 boost::container::flat_map<
763 std::string, dbus::utility::DbusVariantType>
764 output;
765
766 output.reserve(16); // The pid interface length
767
768 // determines if we're patching entity-manager or
769 // creating a new object
770 bool createNewObject = (pathItr == managedObj.end());
771 if (type.key() == "PidControllers" ||
772 type.key() == "FanControllers")
773 {
774 if (!createNewObject &&
775 pathItr->second.find(pidConfigurationIface) ==
776 pathItr->second.end())
777 {
778 createNewObject = true;
779 }
780 }
781 else if (!createNewObject &&
782 pathItr->second.find(
783 pidZoneConfigurationIface) ==
784 pathItr->second.end())
785 {
786 createNewObject = true;
787 }
788 output["Name"] =
789 boost::replace_all_copy(name, "_", " ");
790
791 std::string chassis;
792 CreatePIDRet ret = createPidInterface(
793 response, type.key(), record.value(),
794 pathItr->first.str, managedObj, createNewObject,
795 output, chassis);
796 if (ret == CreatePIDRet::fail)
797 {
798 return;
799 }
800 else if (ret == CreatePIDRet::del)
801 {
802 continue;
803 }
804
805 if (!createNewObject)
806 {
807 for (const auto& property : output)
808 {
809 const char* iface =
810 type.key() == "FanZones"
811 ? pidZoneConfigurationIface
812 : pidConfigurationIface;
813 crow::connections::systemBus->async_method_call(
814 [response,
815 propertyName{std::string(property.first)}](
816 const boost::system::error_code ec) {
817 if (ec)
818 {
819 BMCWEB_LOG_ERROR
820 << "Error patching "
821 << propertyName << ": " << ec;
822 response->res.result(
823 boost::beast::http::status::
824 internal_server_error);
825 }
826 },
827 "xyz.openbmc_project.EntityManager",
828 pathItr->first.str,
829 "org.freedesktop.DBus.Properties", "Set",
830 std::string(iface), property.first,
831 property.second);
832 }
833 }
834 else
835 {
836 if (chassis.empty())
837 {
838 BMCWEB_LOG_ERROR
839 << "Failed to get chassis from config";
840 response->res.result(
841 boost::beast::http::status::bad_request);
842 return;
843 }
844
845 bool foundChassis = false;
846 for (const auto& obj : managedObj)
847 {
848 if (boost::algorithm::ends_with(obj.first.str,
849 chassis))
850 {
851 chassis = obj.first.str;
852 foundChassis = true;
853 break;
854 }
855 }
856 if (!foundChassis)
857 {
858 BMCWEB_LOG_ERROR
859 << "Failed to find chassis on dbus";
860 messages::addMessageToErrorJson(
861 response->res.jsonValue,
862 messages::resourceMissingAtURI(
863 "/redfish/v1/Chassis/" + chassis));
864 response->res.result(
865 boost::beast::http::status::
866 internal_server_error);
867 return;
868 }
869
870 crow::connections::systemBus->async_method_call(
871 [response](const boost::system::error_code ec) {
872 if (ec)
873 {
874 BMCWEB_LOG_ERROR
875 << "Error Adding Pid Object " << ec;
876 response->res.result(
877 boost::beast::http::status::
878 internal_server_error);
879 }
880 },
881 "xyz.openbmc_project.EntityManager", chassis,
882 "xyz.openbmc_project.AddObject", "AddObject",
883 output);
884 }
885 }
886 }
887 },
888 "xyz.openbmc_project.EntityManager", "/", objectManagerIface,
889 "GetManagedObjects");
890 }
James Feist5b4aa862018-08-16 14:07:01 -0700891
892 void doPatch(crow::Response& res, const crow::Request& req,
893 const std::vector<std::string>& params) override
894 {
James Feist83ff9ab2018-08-31 10:18:24 -0700895 nlohmann::json patch;
896 if (!json_util::processJsonFromRequest(res, req, patch))
897 {
898 return;
899 }
900 std::shared_ptr<AsyncResp> response = std::make_shared<AsyncResp>(res);
901 for (const auto& topLevel : patch.items())
902 {
903 if (topLevel.key() == "Oem")
904 {
905 if (!topLevel.value().is_object())
906 {
907 BMCWEB_LOG_ERROR << "Bad Patch " << topLevel.key();
908 res.result(boost::beast::http::status::bad_request);
909 return;
910 }
911 }
912 else
913 {
914 BMCWEB_LOG_ERROR << "Bad Patch " << topLevel.key();
915 messages::addMessageToErrorJson(
916 response->res.jsonValue,
917 messages::propertyUnknown(topLevel.key()));
918 res.result(boost::beast::http::status::bad_request);
919 return;
920 }
921 for (const auto& oemLevel : topLevel.value().items())
922 {
923 if (oemLevel.key() == "OpenBmc")
924 {
925 if (!oemLevel.value().is_object())
926 {
927 BMCWEB_LOG_ERROR << "Bad Patch " << oemLevel.key();
928 res.result(boost::beast::http::status::bad_request);
929 return;
930 }
931 for (const auto& typeLevel : oemLevel.value().items())
932 {
933
934 if (typeLevel.key() == "Fan")
935 {
936 if (!typeLevel.value().is_object())
937 {
938 BMCWEB_LOG_ERROR << "Bad Patch "
939 << typeLevel.key();
940 messages::addMessageToErrorJson(
941 response->res.jsonValue,
942 messages::propertyValueFormatError(
943 typeLevel.value().dump(),
944 typeLevel.key()));
945 res.result(
946 boost::beast::http::status::bad_request);
947 return;
948 }
949 setPidValues(response,
950 std::move(typeLevel.value()));
951 }
952 else
953 {
954 BMCWEB_LOG_ERROR << "Bad Patch " << typeLevel.key();
955 messages::addMessageToErrorJson(
956 response->res.jsonValue,
957 messages::propertyUnknown(typeLevel.key()));
958 res.result(boost::beast::http::status::bad_request);
959 return;
960 }
961 }
962 }
963 else
964 {
965 BMCWEB_LOG_ERROR << "Bad Patch " << oemLevel.key();
966 messages::addMessageToErrorJson(
967 response->res.jsonValue,
968 messages::propertyUnknown(oemLevel.key()));
969 res.result(boost::beast::http::status::bad_request);
970 return;
971 }
972 }
973 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700974 }
975
976 std::string getDateTime() const
977 {
978 std::array<char, 128> dateTime;
979 std::string redfishDateTime("0000-00-00T00:00:00Z00:00");
980 std::time_t time = std::time(nullptr);
981
982 if (std::strftime(dateTime.begin(), dateTime.size(), "%FT%T%z",
983 std::localtime(&time)))
984 {
985 // insert the colon required by the ISO 8601 standard
986 redfishDateTime = std::string(dateTime.data());
987 redfishDateTime.insert(redfishDateTime.end() - 2, ':');
988 }
989
990 return redfishDateTime;
991 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100992};
993
Ed Tanous1abe55e2018-09-05 08:30:59 -0700994class ManagerCollection : public Node
995{
996 public:
James Feist5b4aa862018-08-16 14:07:01 -0700997 ManagerCollection(CrowApp& app) : Node(app, "/redfish/v1/Managers/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700998 {
999 Node::json["@odata.id"] = "/redfish/v1/Managers";
1000 Node::json["@odata.type"] = "#ManagerCollection.ManagerCollection";
1001 Node::json["@odata.context"] =
1002 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection";
1003 Node::json["Name"] = "Manager Collection";
1004 Node::json["Members@odata.count"] = 1;
James Feist5b4aa862018-08-16 14:07:01 -07001005 Node::json["Members"] = {{{"@odata.id", "/redfish/v1/Managers/bmc"}}};
Ed Tanous3ebd75f2018-03-05 18:20:01 -08001006
Ed Tanous1abe55e2018-09-05 08:30:59 -07001007 entityPrivileges = {
1008 {boost::beast::http::verb::get, {{"Login"}}},
1009 {boost::beast::http::verb::head, {{"Login"}}},
1010 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1011 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1012 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1013 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1014 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001015
Ed Tanous1abe55e2018-09-05 08:30:59 -07001016 private:
James Feist5b4aa862018-08-16 14:07:01 -07001017 void doGet(crow::Response& res, const crow::Request& req,
1018 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -07001019 {
James Feist83ff9ab2018-08-31 10:18:24 -07001020 // Collections don't include the static data added by SubRoute
1021 // because it has a duplicate entry for members
Ed Tanous1abe55e2018-09-05 08:30:59 -07001022 res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
1023 res.jsonValue["@odata.type"] = "#ManagerCollection.ManagerCollection";
1024 res.jsonValue["@odata.context"] =
1025 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection";
1026 res.jsonValue["Name"] = "Manager Collection";
1027 res.jsonValue["Members@odata.count"] = 1;
1028 res.jsonValue["Members"] = {
James Feist5b4aa862018-08-16 14:07:01 -07001029 {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001030 res.end();
1031 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +01001032};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001033} // namespace redfish