blob: 1fb2f69758d2e712f3fff9a7c40e7e672a44b83f [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;
43 asyncResp->res.result(
44 boost::beast::http::status::internal_server_error);
45 asyncResp->res.jsonValue.clear();
46 return;
47 }
48 nlohmann::json& configRoot =
49 asyncResp->res.jsonValue["Oem"]["OpenBmc"]["Fan"];
50 nlohmann::json& fans = configRoot["FanControllers"];
51 fans["@odata.type"] = "#OemManager.FanControllers";
52 fans["@odata.context"] =
53 "/redfish/v1/$metadata#OemManager.FanControllers";
54 fans["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc/"
55 "Fan/FanControllers";
56
57 nlohmann::json& pids = configRoot["PidControllers"];
58 pids["@odata.type"] = "#OemManager.PidControllers";
59 pids["@odata.context"] =
60 "/redfish/v1/$metadata#OemManager.PidControllers";
61 pids["@odata.id"] =
62 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/PidControllers";
63
64 nlohmann::json& zones = configRoot["FanZones"];
65 zones["@odata.id"] =
66 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones";
67 zones["@odata.type"] = "#OemManager.FanZones";
68 zones["@odata.context"] =
69 "/redfish/v1/$metadata#OemManager.FanZones";
70 configRoot["@odata.id"] =
71 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan";
72 configRoot["@odata.type"] = "#OemManager.Fan";
73 configRoot["@odata.context"] =
74 "/redfish/v1/$metadata#OemManager.Fan";
75
76 bool propertyError = false;
77 for (const auto& pathPair : managedObj)
78 {
79 for (const auto& intfPair : pathPair.second)
80 {
81 if (intfPair.first != pidConfigurationIface &&
82 intfPair.first != pidZoneConfigurationIface)
83 {
84 continue;
85 }
86 auto findName = intfPair.second.find("Name");
87 if (findName == intfPair.second.end())
88 {
89 BMCWEB_LOG_ERROR << "Pid Field missing Name";
90 asyncResp->res.result(
91 boost::beast::http::status::internal_server_error);
92 return;
93 }
94 const std::string* namePtr =
95 mapbox::getPtr<const std::string>(findName->second);
96 if (namePtr == nullptr)
97 {
98 BMCWEB_LOG_ERROR << "Pid Name Field illegal";
99 return;
100 }
101
102 std::string name = *namePtr;
103 dbus::utility::escapePathForDbus(name);
104 if (intfPair.first == pidZoneConfigurationIface)
105 {
106 std::string chassis;
107 if (!dbus::utility::getNthStringFromPath(
108 pathPair.first.str, 5, chassis))
109 {
110 chassis = "#IllegalValue";
111 }
112 nlohmann::json& zone = zones[name];
113 zone["Chassis"] = {
114 {"@odata.id", "/redfish/v1/Chassis/" + chassis}};
115 zone["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/"
116 "OpenBmc/Fan/FanZones/" +
117 name;
118 zone["@odata.type"] = "#OemManager.FanZone";
119 zone["@odata.context"] =
120 "/redfish/v1/$metadata#OemManager.FanZone";
121 }
122
123 for (const auto& propertyPair : intfPair.second)
124 {
125 if (propertyPair.first == "Type" ||
126 propertyPair.first == "Class" ||
127 propertyPair.first == "Name")
128 {
129 continue;
130 }
131
132 // zones
133 if (intfPair.first == pidZoneConfigurationIface)
134 {
135 const double* ptr = mapbox::getPtr<const double>(
136 propertyPair.second);
137 if (ptr == nullptr)
138 {
139 BMCWEB_LOG_ERROR << "Field Illegal "
140 << propertyPair.first;
141 asyncResp->res.result(
142 boost::beast::http::status::
143 internal_server_error);
144 return;
145 }
146 zones[name][propertyPair.first] = *ptr;
147 }
148
149 // pid and fans are off the same configuration
150 if (intfPair.first == pidConfigurationIface)
151 {
152 const std::string* classPtr = nullptr;
153 auto findClass = intfPair.second.find("Class");
154 if (findClass != intfPair.second.end())
155 {
156 classPtr = mapbox::getPtr<const std::string>(
157 findClass->second);
158 }
159 if (classPtr == nullptr)
160 {
161 BMCWEB_LOG_ERROR << "Pid Class Field illegal";
162 asyncResp->res.result(
163 boost::beast::http::status::
164 internal_server_error);
165 return;
166 }
167 bool isFan = *classPtr == "fan";
168 nlohmann::json& element =
169 isFan ? fans[name] : pids[name];
170 if (isFan)
171 {
172 element["@odata.id"] =
173 "/redfish/v1/Managers/bmc#/Oem/"
174 "OpenBmc/Fan/FanControllers/" +
175 std::string(name);
176 element["@odata.type"] =
177 "#OemManager.FanController";
178
179 element["@odata.context"] =
180 "/redfish/v1/"
181 "$metadata#OemManager.FanController";
182 }
183 else
184 {
185 element["@odata.id"] =
186 "/redfish/v1/Managers/bmc#/Oem/"
187 "OpenBmc/Fan/PidControllers/" +
188 std::string(name);
189 element["@odata.type"] =
190 "#OemManager.PidController";
191 element["@odata.context"] =
192 "/redfish/v1/$metadata"
193 "#OemManager.PidController";
194 }
195
196 if (propertyPair.first == "Zones")
197 {
198 const std::vector<std::string>* inputs =
199 mapbox::getPtr<
200 const std::vector<std::string>>(
201 propertyPair.second);
202
203 if (inputs == nullptr)
204 {
205 BMCWEB_LOG_ERROR
206 << "Zones Pid Field Illegal";
207 asyncResp->res.result(
208 boost::beast::http::status::
209 internal_server_error);
210 return;
211 }
212 auto& data = element[propertyPair.first];
213 data = nlohmann::json::array();
214 for (std::string itemCopy : *inputs)
215 {
216 dbus::utility::escapePathForDbus(itemCopy);
217 data.push_back(
218 {{"@odata.id",
219 "/redfish/v1/Managers/bmc#/Oem/"
220 "OpenBmc/Fan/FanZones/" +
221 itemCopy}});
222 }
223 }
224 // todo(james): may never happen, but this
225 // assumes configuration data referenced in the
226 // PID config is provided by the same daemon, we
227 // could add another loop to cover all cases,
228 // but I'm okay kicking this can down the road a
229 // bit
230
231 else if (propertyPair.first == "Inputs" ||
232 propertyPair.first == "Outputs")
233 {
234 auto& data = element[propertyPair.first];
235 const std::vector<std::string>* inputs =
236 mapbox::getPtr<
237 const std::vector<std::string>>(
238 propertyPair.second);
239
240 if (inputs == nullptr)
241 {
242 BMCWEB_LOG_ERROR << "Field Illegal "
243 << propertyPair.first;
244 asyncResp->res.result(
245 boost::beast::http::status::
246 internal_server_error);
247 return;
248 }
249 data = *inputs;
250 } // doubles
251 else if (propertyPair.first ==
252 "FFGainCoefficient" ||
253 propertyPair.first == "FFOffCoefficient" ||
254 propertyPair.first == "ICoefficient" ||
255 propertyPair.first == "ILimitMax" ||
256 propertyPair.first == "ILimitMin" ||
257 propertyPair.first == "OutLimitMax" ||
258 propertyPair.first == "OutLimitMin" ||
259 propertyPair.first == "PCoefficient" ||
260 propertyPair.first == "SlewNeg" ||
261 propertyPair.first == "SlewPos")
262 {
263 const double* ptr =
264 mapbox::getPtr<const double>(
265 propertyPair.second);
266 if (ptr == nullptr)
267 {
268 BMCWEB_LOG_ERROR << "Field Illegal "
269 << propertyPair.first;
270 asyncResp->res.result(
271 boost::beast::http::status::
272 internal_server_error);
273 return;
274 }
275 element[propertyPair.first] = *ptr;
276 }
277 }
278 }
279 }
280 }
281 },
282 connection, path, objectManagerIface, "GetManagedObjects");
283}
Jennifer Leeca537922018-08-10 10:07:30 -0700284
Ed Tanous1abe55e2018-09-05 08:30:59 -0700285class Manager : public Node
286{
287 public:
James Feist5b4aa862018-08-16 14:07:01 -0700288 Manager(CrowApp& app) : Node(app, "/redfish/v1/Managers/bmc/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700289 {
James Feist5b4aa862018-08-16 14:07:01 -0700290 Node::json["@odata.id"] = "/redfish/v1/Managers/bmc";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700291 Node::json["@odata.type"] = "#Manager.v1_3_0.Manager";
292 Node::json["@odata.context"] = "/redfish/v1/$metadata#Manager.Manager";
James Feist5b4aa862018-08-16 14:07:01 -0700293 Node::json["Id"] = "bmc";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700294 Node::json["Name"] = "OpenBmc Manager";
295 Node::json["Description"] = "Baseboard Management Controller";
296 Node::json["PowerState"] = "On";
Jennifer Leeca537922018-08-10 10:07:30 -0700297 Node::json["ManagerType"] = "BMC";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700298 Node::json["UUID"] =
299 app.template getMiddleware<crow::persistent_data::Middleware>()
300 .systemUuid;
Jennifer Leeca537922018-08-10 10:07:30 -0700301 Node::json["Model"] = "OpenBmc"; // TODO(ed), get model
302 Node::json["EthernetInterfaces"] = {
James Feist5b4aa862018-08-16 14:07:01 -0700303 {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces"}};
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800304
Ed Tanous1abe55e2018-09-05 08:30:59 -0700305 entityPrivileges = {
306 {boost::beast::http::verb::get, {{"Login"}}},
307 {boost::beast::http::verb::head, {{"Login"}}},
308 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
309 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
310 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
311 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
James Feist5b4aa862018-08-16 14:07:01 -0700312
313 // default oem data
314 nlohmann::json& oem = Node::json["Oem"];
315 nlohmann::json& oemOpenbmc = oem["OpenBmc"];
316 oem["@odata.type"] = "#OemManager.Oem";
317 oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem";
318 oem["@odata.context"] = "/redfish/v1/$metadata#OemManager.Oem";
319 oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc";
320 oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc";
321 oemOpenbmc["@odata.context"] =
322 "/redfish/v1/$metadata#OemManager.OpenBmc";
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100323 }
324
Ed Tanous1abe55e2018-09-05 08:30:59 -0700325 private:
James Feist5b4aa862018-08-16 14:07:01 -0700326 void getPidValues(std::shared_ptr<AsyncResp> asyncResp)
327 {
328 crow::connections::systemBus->async_method_call(
329 [asyncResp](const boost::system::error_code ec,
330 const crow::openbmc_mapper::GetSubTreeType& subtree) {
331 if (ec)
332 {
333 BMCWEB_LOG_ERROR << ec;
334 asyncResp->res.result(
335 boost::beast::http::status::internal_server_error);
336 return;
337 }
338
339 // create map of <connection, path to objMgr>>
340 boost::container::flat_map<std::string, std::string>
341 objectMgrPaths;
342 for (const auto& pathGroup : subtree)
343 {
344 for (const auto& connectionGroup : pathGroup.second)
345 {
346 for (const std::string& interface :
347 connectionGroup.second)
348 {
349 if (interface == objectManagerIface)
350 {
351 objectMgrPaths[connectionGroup.first] =
352 pathGroup.first;
353 }
354 // this list is alphabetical, so we
355 // should have found the objMgr by now
356 if (interface == pidConfigurationIface ||
357 interface == pidZoneConfigurationIface)
358 {
359 auto findObjMgr =
360 objectMgrPaths.find(connectionGroup.first);
361 if (findObjMgr == objectMgrPaths.end())
362 {
363 BMCWEB_LOG_DEBUG << connectionGroup.first
364 << "Has no Object Manager";
365 continue;
366 }
367 asyncPopulatePid(findObjMgr->first,
368 findObjMgr->second, asyncResp);
369 break;
370 }
371 }
372 }
373 }
374 },
375 "xyz.openbmc_project.ObjectMapper",
376 "/xyz/openbmc_project/object_mapper",
377 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
378 std::array<const char*, 3>{pidConfigurationIface,
379 pidZoneConfigurationIface,
380 objectManagerIface});
381 }
382
383 void doGet(crow::Response& res, const crow::Request& req,
384 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700385 {
Jennifer Leeca537922018-08-10 10:07:30 -0700386 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
387 asyncResp->res.jsonValue = Node::json;
388
Ed Tanous1abe55e2018-09-05 08:30:59 -0700389 Node::json["DateTime"] = getDateTime();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700390 res.jsonValue = Node::json;
James Feist5b4aa862018-08-16 14:07:01 -0700391
Jennifer Leeca537922018-08-10 10:07:30 -0700392 crow::connections::systemBus->async_method_call(
393 [asyncResp](const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -0700394 const dbus::utility::ManagedObjectType& resp) {
Jennifer Leeca537922018-08-10 10:07:30 -0700395 if (ec)
396 {
397 BMCWEB_LOG_ERROR << "Error while getting Software Version";
398 asyncResp->res.result(
399 boost::beast::http::status::internal_server_error);
400 return;
401 }
402
James Feist5b4aa862018-08-16 14:07:01 -0700403 for (auto& objpath : resp)
Jennifer Leeca537922018-08-10 10:07:30 -0700404 {
James Feist5b4aa862018-08-16 14:07:01 -0700405 for (auto& interface : objpath.second)
Jennifer Leeca537922018-08-10 10:07:30 -0700406 {
407 // If interface is xyz.openbmc_project.Software.Version,
408 // this is what we're looking for.
409 if (interface.first ==
410 "xyz.openbmc_project.Software.Version")
411 {
412 // Cut out everyting until last "/", ...
James Feist5b4aa862018-08-16 14:07:01 -0700413 const std::string& iface_id = objpath.first;
414 for (auto& property : interface.second)
Jennifer Leeca537922018-08-10 10:07:30 -0700415 {
416 if (property.first == "Version")
417 {
James Feist5b4aa862018-08-16 14:07:01 -0700418 const std::string* value =
Jennifer Leeca537922018-08-10 10:07:30 -0700419 mapbox::getPtr<const std::string>(
420 property.second);
421 if (value == nullptr)
422 {
423 continue;
424 }
425 asyncResp->res
426 .jsonValue["FirmwareVersion"] = *value;
427 }
428 }
429 }
430 }
431 }
432 },
433 "xyz.openbmc_project.Software.BMC.Updater",
434 "/xyz/openbmc_project/software",
435 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
James Feist5b4aa862018-08-16 14:07:01 -0700436 getPidValues(asyncResp);
437 }
438
439 void doPatch(crow::Response& res, const crow::Request& req,
440 const std::vector<std::string>& params) override
441 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700442 }
443
444 std::string getDateTime() const
445 {
446 std::array<char, 128> dateTime;
447 std::string redfishDateTime("0000-00-00T00:00:00Z00:00");
448 std::time_t time = std::time(nullptr);
449
450 if (std::strftime(dateTime.begin(), dateTime.size(), "%FT%T%z",
451 std::localtime(&time)))
452 {
453 // insert the colon required by the ISO 8601 standard
454 redfishDateTime = std::string(dateTime.data());
455 redfishDateTime.insert(redfishDateTime.end() - 2, ':');
456 }
457
458 return redfishDateTime;
459 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100460};
461
Ed Tanous1abe55e2018-09-05 08:30:59 -0700462class ManagerCollection : public Node
463{
464 public:
James Feist5b4aa862018-08-16 14:07:01 -0700465 ManagerCollection(CrowApp& app) : Node(app, "/redfish/v1/Managers/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700466 {
467 Node::json["@odata.id"] = "/redfish/v1/Managers";
468 Node::json["@odata.type"] = "#ManagerCollection.ManagerCollection";
469 Node::json["@odata.context"] =
470 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection";
471 Node::json["Name"] = "Manager Collection";
472 Node::json["Members@odata.count"] = 1;
James Feist5b4aa862018-08-16 14:07:01 -0700473 Node::json["Members"] = {{{"@odata.id", "/redfish/v1/Managers/bmc"}}};
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800474
Ed Tanous1abe55e2018-09-05 08:30:59 -0700475 entityPrivileges = {
476 {boost::beast::http::verb::get, {{"Login"}}},
477 {boost::beast::http::verb::head, {{"Login"}}},
478 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
479 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
480 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
481 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
482 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100483
Ed Tanous1abe55e2018-09-05 08:30:59 -0700484 private:
James Feist5b4aa862018-08-16 14:07:01 -0700485 void doGet(crow::Response& res, const crow::Request& req,
486 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700487 {
488 // Collections don't include the static data added by SubRoute because
489 // it has a duplicate entry for members
490 res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
491 res.jsonValue["@odata.type"] = "#ManagerCollection.ManagerCollection";
492 res.jsonValue["@odata.context"] =
493 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection";
494 res.jsonValue["Name"] = "Manager Collection";
495 res.jsonValue["Members@odata.count"] = 1;
496 res.jsonValue["Members"] = {
James Feist5b4aa862018-08-16 14:07:01 -0700497 {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700498 res.end();
499 }
Borawski.Lukasz9c3106852018-02-09 15:24:22 +0100500};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700501} // namespace redfish