blob: ae3201a4448b13cb4d7c2fa5b8243dc4214d93ec [file] [log] [blame]
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +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"
Ed Tanous1abe55e2018-09-05 08:30:59 -070019
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +010020#include <boost/container/flat_map.hpp>
Ed Tanousabf2add2019-01-22 16:40:12 -080021#include <variant>
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +010022
Ed Tanous1abe55e2018-09-05 08:30:59 -070023namespace redfish
24{
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +010025
26/**
Gunnar Millsbeeca0a2019-02-14 16:30:45 -060027 * @brief Retrieves chassis state properties over dbus
28 *
29 * @param[in] aResp - Shared pointer for completing asynchronous calls.
30 *
31 * @return None.
32 */
33void getChassisState(std::shared_ptr<AsyncResp> aResp)
34{
35 crow::connections::systemBus->async_method_call(
36 [aResp{std::move(aResp)}](
37 const boost::system::error_code ec,
38 const std::variant<std::string> &chassisState) {
39 if (ec)
40 {
41 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
42 messages::internalError(aResp->res);
43 return;
44 }
45
46 const std::string *s = std::get_if<std::string>(&chassisState);
47 BMCWEB_LOG_DEBUG << "Chassis state: " << *s;
48 if (s != nullptr)
49 {
50 // Verify Chassis State
51 if (*s == "xyz.openbmc_project.State.Chassis.PowerState.On")
52 {
53 aResp->res.jsonValue["PowerState"] = "On";
54 aResp->res.jsonValue["Status"]["State"] = "Enabled";
55 }
56 else if (*s ==
57 "xyz.openbmc_project.State.Chassis.PowerState.Off")
58 {
59 aResp->res.jsonValue["PowerState"] = "Off";
60 aResp->res.jsonValue["Status"]["State"] = "StandbyOffline";
61 }
62 }
63 },
64 "xyz.openbmc_project.State.Chassis",
65 "/xyz/openbmc_project/state/chassis0",
66 "org.freedesktop.DBus.Properties", "Get",
67 "xyz.openbmc_project.State.Chassis", "CurrentPowerState");
68}
69
70/**
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +010071 * DBus types primitives for several generic DBus interfaces
72 * TODO(Pawel) consider move this to separate file into boost::dbus
73 */
Ed Tanous55c7b7a2018-05-22 15:27:24 -070074// Note, this is not a very useful Variant, but because it isn't used to get
Ed Tanousaa2e59c2018-04-12 12:17:20 -070075// values, it should be as simple as possible
76// TODO(ed) invent a nullvariant type
Ed Tanousabf2add2019-01-22 16:40:12 -080077using VariantType = std::variant<bool, std::string, uint64_t>;
Ed Tanousaa2e59c2018-04-12 12:17:20 -070078using ManagedObjectsType = std::vector<std::pair<
79 sdbusplus::message::object_path,
80 std::vector<std::pair<std::string,
81 std::vector<std::pair<std::string, VariantType>>>>>>;
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +010082
Ed Tanousaa2e59c2018-04-12 12:17:20 -070083using PropertiesType = boost::container::flat_map<std::string, VariantType>;
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +010084
Qiang XUc1819422019-02-27 13:51:32 +080085void getIntrusionByService(std::shared_ptr<AsyncResp> aResp,
86 const std::string &service,
87 const std::string &objPath)
88{
89 BMCWEB_LOG_DEBUG << "Get intrusion status by service \n";
90
91 crow::connections::systemBus->async_method_call(
92 [aResp{std::move(aResp)}](const boost::system::error_code ec,
93 const std::variant<std::string> &value) {
94 if (ec)
95 {
96 // do not add err msg in redfish response, becaues this is not
97 // mandatory property
98 BMCWEB_LOG_ERROR << "DBUS response error " << ec << "\n";
99 return;
100 }
101
102 const std::string *status = std::get_if<std::string>(&value);
103
104 if (status == nullptr)
105 {
106 BMCWEB_LOG_ERROR << "intrusion status read error \n";
107 return;
108 }
109
110 aResp->res.jsonValue["PhysicalSecurity"] = {
111 {"IntrusionSensorNumber", 1}, {"IntrusionSensor", *status}};
112 },
113 service, objPath, "org.freedesktop.DBus.Properties", "Get",
114 "xyz.openbmc_project.Chassis.Intrusion", "Status");
115}
116
117/**
118 * Retrieves physical security properties over dbus
119 */
120void getPhysicalSecurityData(std::shared_ptr<AsyncResp> aResp)
121{
122 crow::connections::systemBus->async_method_call(
123 [aResp{std::move(aResp)}](
124 const boost::system::error_code ec,
125 const std::vector<std::pair<
126 std::string,
127 std::vector<std::pair<std::string, std::vector<std::string>>>>>
128 &subtree) {
129 if (ec)
130 {
131 // do not add err msg in redfish response, becaues this is not
132 // mandatory property
133 BMCWEB_LOG_ERROR << "DBUS error: no matched iface " << ec
134 << "\n";
135 return;
136 }
137 // Iterate over all retrieved ObjectPaths.
138 for (const auto &object : subtree)
139 {
140 for (const auto &service : object.second)
141 {
142 getIntrusionByService(aResp, service.first, object.first);
143 return;
144 }
145 }
146 },
147 "xyz.openbmc_project.ObjectMapper",
148 "/xyz/openbmc_project/object_mapper",
149 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
150 "/xyz/openbmc_project/Intrusion", int32_t(1),
151 std::array<const char *, 1>{"xyz.openbmc_project.Chassis.Intrusion"});
152}
153
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100154/**
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100155 * ChassisCollection derived class for delivering Chassis Collection Schema
156 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700157class ChassisCollection : public Node
158{
159 public:
160 ChassisCollection(CrowApp &app) : Node(app, "/redfish/v1/Chassis/")
161 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700162 entityPrivileges = {
163 {boost::beast::http::verb::get, {{"Login"}}},
164 {boost::beast::http::verb::head, {{"Login"}}},
165 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
166 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
167 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
168 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
169 }
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100170
Ed Tanous1abe55e2018-09-05 08:30:59 -0700171 private:
172 /**
173 * Functions triggers appropriate requests on DBus
174 */
175 void doGet(crow::Response &res, const crow::Request &req,
176 const std::vector<std::string> &params) override
177 {
Ed Tanous0f74e642018-11-12 15:17:05 -0800178 res.jsonValue["@odata.type"] = "#ChassisCollection.ChassisCollection";
179 res.jsonValue["@odata.id"] = "/redfish/v1/Chassis";
180 res.jsonValue["@odata.context"] =
181 "/redfish/v1/$metadata#ChassisCollection.ChassisCollection";
182 res.jsonValue["Name"] = "Chassis Collection";
183
Gunnar Mills603a6642019-01-21 17:03:51 -0600184 const std::array<const char *, 3> interfaces = {
185 "xyz.openbmc_project.Inventory.Item.Board",
186 "xyz.openbmc_project.Inventory.Item.Chassis",
187 "xyz.openbmc_project.Inventory.Item.PowerSupply"};
188
Ed Tanous62d5e2e2018-09-05 16:17:25 -0700189 auto asyncResp = std::make_shared<AsyncResp>(res);
190 crow::connections::systemBus->async_method_call(
191 [asyncResp](const boost::system::error_code ec,
192 const std::vector<std::string> &chassisList) {
193 if (ec)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700194 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700195 messages::internalError(asyncResp->res);
Ed Tanous62d5e2e2018-09-05 16:17:25 -0700196 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700197 }
Ed Tanous62d5e2e2018-09-05 16:17:25 -0700198 nlohmann::json &chassisArray =
199 asyncResp->res.jsonValue["Members"];
200 chassisArray = nlohmann::json::array();
201 for (const std::string &objpath : chassisList)
202 {
203 std::size_t lastPos = objpath.rfind("/");
204 if (lastPos == std::string::npos)
205 {
206 BMCWEB_LOG_ERROR << "Failed to find '/' in " << objpath;
207 continue;
208 }
209 chassisArray.push_back(
210 {{"@odata.id", "/redfish/v1/Chassis/" +
211 objpath.substr(lastPos + 1)}});
212 }
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100213
Ed Tanous62d5e2e2018-09-05 16:17:25 -0700214 asyncResp->res.jsonValue["Members@odata.count"] =
215 chassisArray.size();
216 },
217 "xyz.openbmc_project.ObjectMapper",
218 "/xyz/openbmc_project/object_mapper",
219 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Gunnar Mills734bfe92019-01-21 16:33:50 -0600220 "/xyz/openbmc_project/inventory", int32_t(0), interfaces);
Ed Tanous62d5e2e2018-09-05 16:17:25 -0700221 }
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100222};
223
224/**
225 * Chassis override class for delivering Chassis Schema
226 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700227class Chassis : public Node
228{
229 public:
230 Chassis(CrowApp &app) :
231 Node(app, "/redfish/v1/Chassis/<str>/", std::string())
232 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700233 entityPrivileges = {
234 {boost::beast::http::verb::get, {{"Login"}}},
235 {boost::beast::http::verb::head, {{"Login"}}},
236 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
237 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
238 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
239 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100240 }
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100241
Ed Tanous1abe55e2018-09-05 08:30:59 -0700242 private:
243 /**
244 * Functions triggers appropriate requests on DBus
245 */
246 void doGet(crow::Response &res, const crow::Request &req,
247 const std::vector<std::string> &params) override
248 {
Gunnar Mills734bfe92019-01-21 16:33:50 -0600249 const std::array<const char *, 3> interfaces = {
250 "xyz.openbmc_project.Inventory.Item.Board",
251 "xyz.openbmc_project.Inventory.Item.Chassis",
252 "xyz.openbmc_project.Inventory.Item.PowerSupply"};
253
Ed Tanous1abe55e2018-09-05 08:30:59 -0700254 // Check if there is required param, truly entering this shall be
255 // impossible.
256 if (params.size() != 1)
257 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700258 messages::internalError(res);
Ed Tanousdaf36e22018-04-20 16:01:36 -0700259 res.end();
260 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700261 }
Shawn McCarney99cffd72019-03-01 10:46:20 -0600262 const std::string &chassisId = params[0];
Ed Tanouse0d918b2018-03-27 17:41:04 -0700263
Ed Tanous62d5e2e2018-09-05 16:17:25 -0700264 auto asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700265 crow::connections::systemBus->async_method_call(
Ed Tanous62d5e2e2018-09-05 16:17:25 -0700266 [asyncResp, chassisId(std::string(chassisId))](
267 const boost::system::error_code ec,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700268 const std::vector<std::pair<
269 std::string, std::vector<std::pair<
270 std::string, std::vector<std::string>>>>>
271 &subtree) {
Ed Tanous62d5e2e2018-09-05 16:17:25 -0700272 if (ec)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700273 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700274 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700275 return;
276 }
277 // Iterate over all retrieved ObjectPaths.
278 for (const std::pair<
279 std::string,
280 std::vector<
281 std::pair<std::string, std::vector<std::string>>>>
282 &object : subtree)
283 {
284 const std::string &path = object.first;
285 const std::vector<
286 std::pair<std::string, std::vector<std::string>>>
287 &connectionNames = object.second;
Ed Tanouse0d918b2018-03-27 17:41:04 -0700288
Ed Tanous1abe55e2018-09-05 08:30:59 -0700289 if (!boost::ends_with(path, chassisId))
290 {
291 continue;
Ed Tanousdaf36e22018-04-20 16:01:36 -0700292 }
Shawn McCarney26f03892019-05-03 13:20:24 -0500293
Ed Tanous1abe55e2018-09-05 08:30:59 -0700294 if (connectionNames.size() < 1)
295 {
296 BMCWEB_LOG_ERROR << "Only got "
297 << connectionNames.size()
298 << " Connection names";
299 continue;
300 }
Ed Tanouse0d918b2018-03-27 17:41:04 -0700301
Johnathan Mantey49c53ac2019-05-02 09:22:38 -0700302 asyncResp->res.jsonValue["@odata.type"] =
303 "#Chassis.v1_4_0.Chassis";
304 asyncResp->res.jsonValue["@odata.id"] =
305 "/redfish/v1/Chassis/" + chassisId;
306 asyncResp->res.jsonValue["@odata.context"] =
307 "/redfish/v1/$metadata#Chassis.Chassis";
308 asyncResp->res.jsonValue["Name"] = "Chassis Collection";
309 asyncResp->res.jsonValue["ChassisType"] = "RackMount";
310
311 const std::string &connectionName =
312 connectionNames[0].first;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700313 crow::connections::systemBus->async_method_call(
Ed Tanous62d5e2e2018-09-05 16:17:25 -0700314 [asyncResp, chassisId(std::string(chassisId))](
315 const boost::system::error_code ec,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700316 const std::vector<std::pair<
317 std::string, VariantType>> &propertiesList) {
318 for (const std::pair<std::string, VariantType>
319 &property : propertiesList)
320 {
Shawn McCarney99cffd72019-03-01 10:46:20 -0600321 // Store DBus properties that are also Redfish
322 // properties with same name and a string value
323 const std::string &propertyName =
324 property.first;
325 if ((propertyName == "PartNumber") ||
326 (propertyName == "SerialNumber") ||
327 (propertyName == "Manufacturer") ||
328 (propertyName == "Model"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700329 {
Shawn McCarney99cffd72019-03-01 10:46:20 -0600330 const std::string *value =
331 std::get_if<std::string>(
332 &property.second);
333 if (value != nullptr)
334 {
335 asyncResp->res.jsonValue[propertyName] =
336 *value;
337 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700338 }
339 }
Ed Tanous62d5e2e2018-09-05 16:17:25 -0700340 asyncResp->res.jsonValue["Name"] = chassisId;
341 asyncResp->res.jsonValue["Id"] = chassisId;
342 asyncResp->res.jsonValue["Thermal"] = {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700343 {"@odata.id", "/redfish/v1/Chassis/" +
344 chassisId + "/Thermal"}};
Ed Tanous2474adf2018-09-05 16:31:16 -0700345 // Power object
346 asyncResp->res.jsonValue["Power"] = {
347 {"@odata.id", "/redfish/v1/Chassis/" +
348 chassisId + "/Power"}};
Ed Tanous029573d2019-02-01 10:57:49 -0800349 asyncResp->res.jsonValue["Status"] = {
350 {"Health", "OK"},
351 {"State", "Enabled"},
352 };
Ed Tanous2474adf2018-09-05 16:31:16 -0700353
Ed Tanous029573d2019-02-01 10:57:49 -0800354 asyncResp->res
355 .jsonValue["Links"]["ComputerSystems"] = {
356 {{"@odata.id", "/redfish/v1/Systems/system"}}};
357 asyncResp->res.jsonValue["Links"]["ManagedBy"] = {
358 {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
Gunnar Millsbeeca0a2019-02-14 16:30:45 -0600359 getChassisState(asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700360 },
361 connectionName, path, "org.freedesktop.DBus.Properties",
362 "GetAll",
363 "xyz.openbmc_project.Inventory.Decorator.Asset");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700364 return;
365 }
Ed Tanouse0d918b2018-03-27 17:41:04 -0700366
Ed Tanous1abe55e2018-09-05 08:30:59 -0700367 // Couldn't find an object with that name. return an error
Jason M. Billsf12894f2018-10-09 12:45:45 -0700368 messages::resourceNotFound(
369 asyncResp->res, "#Chassis.v1_4_0.Chassis", chassisId);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700370 },
371 "xyz.openbmc_project.ObjectMapper",
372 "/xyz/openbmc_project/object_mapper",
373 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
Gunnar Mills734bfe92019-01-21 16:33:50 -0600374 "/xyz/openbmc_project/inventory", int32_t(0), interfaces);
Qiang XUc1819422019-02-27 13:51:32 +0800375
376 getPhysicalSecurityData(asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700377 }
Ed Tanous62d5e2e2018-09-05 16:17:25 -0700378};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700379} // namespace redfish