blob: 9e9d150499e5fa12d0e7fe645185a59a2d038ff0 [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
Ed Tanous1abe55e2018-09-05 08:30:59 -0700275class Manager : public Node
276{
277 public:
James Feist5b4aa862018-08-16 14:07:01 -0700278 Manager(CrowApp& app) : Node(app, "/redfish/v1/Managers/bmc/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700279 {
James Feist5b4aa862018-08-16 14:07:01 -0700280 Node::json["@odata.id"] = "/redfish/v1/Managers/bmc";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700281 Node::json["@odata.type"] = "#Manager.v1_3_0.Manager";
282 Node::json["@odata.context"] = "/redfish/v1/$metadata#Manager.Manager";
James Feist5b4aa862018-08-16 14:07:01 -0700283 Node::json["Id"] = "bmc";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700284 Node::json["Name"] = "OpenBmc Manager";
285 Node::json["Description"] = "Baseboard Management Controller";
286 Node::json["PowerState"] = "On";
Jennifer Leeca537922018-08-10 10:07:30 -0700287 Node::json["ManagerType"] = "BMC";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700288 Node::json["UUID"] =
289 app.template getMiddleware<crow::persistent_data::Middleware>()
290 .systemUuid;
Jennifer Leeca537922018-08-10 10:07:30 -0700291 Node::json["Model"] = "OpenBmc"; // TODO(ed), get model
292 Node::json["EthernetInterfaces"] = {
James Feist5b4aa862018-08-16 14:07:01 -0700293 {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces"}};
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800294
Ed Tanous1abe55e2018-09-05 08:30:59 -0700295 entityPrivileges = {
296 {boost::beast::http::verb::get, {{"Login"}}},
297 {boost::beast::http::verb::head, {{"Login"}}},
298 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
299 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
300 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
301 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
James Feist5b4aa862018-08-16 14:07:01 -0700302
303 // default oem data
304 nlohmann::json& oem = Node::json["Oem"];
305 nlohmann::json& oemOpenbmc = oem["OpenBmc"];
306 oem["@odata.type"] = "#OemManager.Oem";
307 oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem";
308 oem["@odata.context"] = "/redfish/v1/$metadata#OemManager.Oem";
309 oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc";
310 oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc";
311 oemOpenbmc["@odata.context"] =
312 "/redfish/v1/$metadata#OemManager.OpenBmc";
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100313 }
314
Ed Tanous1abe55e2018-09-05 08:30:59 -0700315 private:
James Feist5b4aa862018-08-16 14:07:01 -0700316 void getPidValues(std::shared_ptr<AsyncResp> asyncResp)
317 {
318 crow::connections::systemBus->async_method_call(
319 [asyncResp](const boost::system::error_code ec,
320 const crow::openbmc_mapper::GetSubTreeType& subtree) {
321 if (ec)
322 {
323 BMCWEB_LOG_ERROR << ec;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700324 messages::internalError(asyncResp->res);
James Feist5b4aa862018-08-16 14:07:01 -0700325 return;
326 }
327
328 // create map of <connection, path to objMgr>>
329 boost::container::flat_map<std::string, std::string>
330 objectMgrPaths;
James Feist6bce33b2018-10-22 12:05:56 -0700331 boost::container::flat_set<std::string> calledConnections;
James Feist5b4aa862018-08-16 14:07:01 -0700332 for (const auto& pathGroup : subtree)
333 {
334 for (const auto& connectionGroup : pathGroup.second)
335 {
James Feist6bce33b2018-10-22 12:05:56 -0700336 auto findConnection =
337 calledConnections.find(connectionGroup.first);
338 if (findConnection != calledConnections.end())
339 {
340 break;
341 }
James Feist5b4aa862018-08-16 14:07:01 -0700342 for (const std::string& interface :
343 connectionGroup.second)
344 {
345 if (interface == objectManagerIface)
346 {
347 objectMgrPaths[connectionGroup.first] =
348 pathGroup.first;
349 }
350 // this list is alphabetical, so we
351 // should have found the objMgr by now
352 if (interface == pidConfigurationIface ||
353 interface == pidZoneConfigurationIface)
354 {
355 auto findObjMgr =
356 objectMgrPaths.find(connectionGroup.first);
357 if (findObjMgr == objectMgrPaths.end())
358 {
359 BMCWEB_LOG_DEBUG << connectionGroup.first
360 << "Has no Object Manager";
361 continue;
362 }
James Feist6bce33b2018-10-22 12:05:56 -0700363
364 calledConnections.insert(connectionGroup.first);
365
James Feist5b4aa862018-08-16 14:07:01 -0700366 asyncPopulatePid(findObjMgr->first,
367 findObjMgr->second, asyncResp);
368 break;
369 }
370 }
371 }
372 }
373 },
374 "xyz.openbmc_project.ObjectMapper",
375 "/xyz/openbmc_project/object_mapper",
376 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
377 std::array<const char*, 3>{pidConfigurationIface,
378 pidZoneConfigurationIface,
379 objectManagerIface});
380 }
381
382 void doGet(crow::Response& res, const crow::Request& req,
383 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700384 {
Jennifer Leeca537922018-08-10 10:07:30 -0700385 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
386 asyncResp->res.jsonValue = Node::json;
387
Ed Tanous1abe55e2018-09-05 08:30:59 -0700388 Node::json["DateTime"] = getDateTime();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700389 res.jsonValue = Node::json;
James Feist5b4aa862018-08-16 14:07:01 -0700390
Jennifer Leeca537922018-08-10 10:07:30 -0700391 crow::connections::systemBus->async_method_call(
392 [asyncResp](const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -0700393 const dbus::utility::ManagedObjectType& resp) {
Jennifer Leeca537922018-08-10 10:07:30 -0700394 if (ec)
395 {
396 BMCWEB_LOG_ERROR << "Error while getting Software Version";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700397 messages::internalError(asyncResp->res);
Jennifer Leeca537922018-08-10 10:07:30 -0700398 return;
399 }
400
James Feist5b4aa862018-08-16 14:07:01 -0700401 for (auto& objpath : resp)
Jennifer Leeca537922018-08-10 10:07:30 -0700402 {
James Feist5b4aa862018-08-16 14:07:01 -0700403 for (auto& interface : objpath.second)
Jennifer Leeca537922018-08-10 10:07:30 -0700404 {
405 // If interface is xyz.openbmc_project.Software.Version,
406 // this is what we're looking for.
407 if (interface.first ==
408 "xyz.openbmc_project.Software.Version")
409 {
410 // Cut out everyting until last "/", ...
James Feist5b4aa862018-08-16 14:07:01 -0700411 const std::string& iface_id = objpath.first;
412 for (auto& property : interface.second)
Jennifer Leeca537922018-08-10 10:07:30 -0700413 {
414 if (property.first == "Version")
415 {
James Feist5b4aa862018-08-16 14:07:01 -0700416 const std::string* value =
Jennifer Leeca537922018-08-10 10:07:30 -0700417 mapbox::getPtr<const std::string>(
418 property.second);
419 if (value == nullptr)
420 {
421 continue;
422 }
423 asyncResp->res
424 .jsonValue["FirmwareVersion"] = *value;
425 }
426 }
427 }
428 }
429 }
430 },
431 "xyz.openbmc_project.Software.BMC.Updater",
432 "/xyz/openbmc_project/software",
433 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
James Feist5b4aa862018-08-16 14:07:01 -0700434 getPidValues(asyncResp);
435 }
436
437 void doPatch(crow::Response& res, const crow::Request& req,
438 const std::vector<std::string>& params) override
439 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700440 }
441
442 std::string getDateTime() const
443 {
444 std::array<char, 128> dateTime;
445 std::string redfishDateTime("0000-00-00T00:00:00Z00:00");
446 std::time_t time = std::time(nullptr);
447
448 if (std::strftime(dateTime.begin(), dateTime.size(), "%FT%T%z",
449 std::localtime(&time)))
450 {
451 // insert the colon required by the ISO 8601 standard
452 redfishDateTime = std::string(dateTime.data());
453 redfishDateTime.insert(redfishDateTime.end() - 2, ':');
454 }
455
456 return redfishDateTime;
457 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100458};
459
Ed Tanous1abe55e2018-09-05 08:30:59 -0700460class ManagerCollection : public Node
461{
462 public:
James Feist5b4aa862018-08-16 14:07:01 -0700463 ManagerCollection(CrowApp& app) : Node(app, "/redfish/v1/Managers/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700464 {
465 Node::json["@odata.id"] = "/redfish/v1/Managers";
466 Node::json["@odata.type"] = "#ManagerCollection.ManagerCollection";
467 Node::json["@odata.context"] =
468 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection";
469 Node::json["Name"] = "Manager Collection";
470 Node::json["Members@odata.count"] = 1;
James Feist5b4aa862018-08-16 14:07:01 -0700471 Node::json["Members"] = {{{"@odata.id", "/redfish/v1/Managers/bmc"}}};
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800472
Ed Tanous1abe55e2018-09-05 08:30:59 -0700473 entityPrivileges = {
474 {boost::beast::http::verb::get, {{"Login"}}},
475 {boost::beast::http::verb::head, {{"Login"}}},
476 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
477 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
478 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
479 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
480 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100481
Ed Tanous1abe55e2018-09-05 08:30:59 -0700482 private:
James Feist5b4aa862018-08-16 14:07:01 -0700483 void doGet(crow::Response& res, const crow::Request& req,
484 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700485 {
486 // Collections don't include the static data added by SubRoute because
487 // it has a duplicate entry for members
488 res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
489 res.jsonValue["@odata.type"] = "#ManagerCollection.ManagerCollection";
490 res.jsonValue["@odata.context"] =
491 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection";
492 res.jsonValue["Name"] = "Manager Collection";
493 res.jsonValue["Members@odata.count"] = 1;
494 res.jsonValue["Members"] = {
James Feist5b4aa862018-08-16 14:07:01 -0700495 {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700496 res.end();
497 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100498};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700499} // namespace redfish