blob: e32be23261b7b4afcbd4f3034ccb2f1747ce8ae4 [file] [log] [blame]
Ed Tanous911ac312017-08-15 09:37:42 -07001#pragma once
2
Ed Tanous911ac312017-08-15 09:37:42 -07003#include <dbus/connection.hpp>
4#include <dbus/endpoint.hpp>
5#include <dbus/filter.hpp>
6#include <dbus/match.hpp>
7#include <dbus/message.hpp>
Ed Tanousba9f9a62017-10-11 16:40:35 -07008#include <persistent_data_middleware.hpp>
9#include <token_authorization_middleware.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070010#include <fstream>
Ed Tanousba9f9a62017-10-11 16:40:35 -070011#include <streambuf>
12#include <string>
Ed Tanous2a866f82017-10-25 17:46:24 -070013#include <crow/app.h>
14#include <boost/algorithm/string.hpp>
Ed Tanous3dac7492017-08-02 13:46:20 -070015namespace crow {
16namespace redfish {
17
18template <typename... Middlewares>
Ed Tanousba9f9a62017-10-11 16:40:35 -070019void get_redfish_sub_routes(Crow<Middlewares...>& app, const std::string& url,
20 nlohmann::json& j) {
Ed Tanous1c74de82017-10-26 13:58:28 -070021 std::vector<const std::string*> routes = app.get_routes(url);
22 for (auto route : routes) {
Ed Tanousba9f9a62017-10-11 16:40:35 -070023 auto redfish_sub_route =
Ed Tanous1c74de82017-10-26 13:58:28 -070024 route->substr(url.size(), route->size() - url.size() - 1);
Ed Tanousba9f9a62017-10-11 16:40:35 -070025 // check if the route is at this level, and we didn't find and exact match
26 // also, filter out resources that start with $ to remove $metadata
27 if (!redfish_sub_route.empty() && redfish_sub_route[0] != '$' &&
28 redfish_sub_route.find('/') == std::string::npos) {
Ed Tanous1c74de82017-10-26 13:58:28 -070029 j[redfish_sub_route] = nlohmann::json{{"@odata.id", *route}};
Ed Tanous911ac312017-08-15 09:37:42 -070030 }
Ed Tanousba9f9a62017-10-11 16:40:35 -070031 }
32}
Ed Tanous911ac312017-08-15 09:37:42 -070033
Ed Tanous2a866f82017-10-25 17:46:24 -070034std::string execute_process(const char* cmd) {
35 std::array<char, 128> buffer;
36 std::string result;
37 std::shared_ptr<FILE> pipe(popen(cmd, "r"), pclose);
38 if (!pipe) throw std::runtime_error("popen() failed!");
39 while (!feof(pipe.get())) {
40 if (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr)
41 result += buffer.data();
42 }
43 return result;
44}
45
Ed Tanousba9f9a62017-10-11 16:40:35 -070046template <typename... Middlewares>
47void request_routes(Crow<Middlewares...>& app) {
48 CROW_ROUTE(app, "/redfish/")
Ed Tanous911ac312017-08-15 09:37:42 -070049 .methods("GET"_method)([](const crow::request& req, crow::response& res) {
Ed Tanousba9f9a62017-10-11 16:40:35 -070050 res.json_value = {{"v1", "/redfish/v1/"}};
51 res.end();
52 });
53
Ed Tanousba9f9a62017-10-11 16:40:35 -070054 CROW_ROUTE(app, "/redfish/v1/Chassis/")
55 .methods("GET"_method)(
56 [&](const crow::request& req, crow::response& res) {
57 std::vector<std::string> entities;
58 /*std::ifstream f("~/system.json");
59
60 nlohmann::json input = nlohmann::json::parse(f);
61 for (auto it = input.begin(); it != input.end(); it++) {
62 auto value = it.value();
63 if (value["type"] == "Chassis") {
64 std::string str = value["name"];
65 entities.emplace_back(str);
66 }
67 }
68 */
Ed Tanous1c74de82017-10-26 13:58:28 -070069
Ed Tanousba9f9a62017-10-11 16:40:35 -070070 res.json_value = {
71 {"@odata.context",
72 "/redfish/v1/$metadata#ChassisCollection.ChassisCollection"},
73 {"@odata.id", "/redfish/v1/Chassis"},
74 {"@odata.type", "#ChassisCollection.ChassisCollection"},
75 {"Name", "Chassis Collection"},
76 {"Members@odata.count", entities.size()}};
77
78 get_redfish_sub_routes(app, "/redfish/v1/Chassis", res.json_value);
79 res.end();
80 });
81
82 CROW_ROUTE(app, "/redfish/v1/AccountService/")
83 .methods(
84 "GET"_method)([&](const crow::request& req, crow::response& res) {
85 res.json_value = {
86 {"@odata.context",
87 "/redfish/v1/$metadata#AccountService.AccountService"},
88 {"@odata.id", "/redfish/v1/AccountService"},
89 {"@odata.type", "#AccountService.v1_1_0.AccountService"},
90 {"Id", "AccountService"},
91 {"Name", "Account Service"},
92 {"Description", "BMC User Accounts"},
93 {"Status",
94 // TODO(ed) health rollup
95 {{"State", "Enabled"}, {"Health", "OK"}, {"HealthRollup", "OK"}}},
96 {"ServiceEnabled", true},
97 {"MinPasswordLength", 1},
98 {"MaxPasswordLength", 20},
99 };
100 get_redfish_sub_routes(app, "/redfish/v1/AccountService",
101 res.json_value);
102 res.end();
103 });
104
Ed Tanousba9f9a62017-10-11 16:40:35 -0700105 CROW_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
106 .methods(
107 "GET"_method)([&](const crow::request& req, crow::response& res) {
Ed Tanous911ac312017-08-15 09:37:42 -0700108 boost::asio::io_service io;
109 auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session);
110 dbus::endpoint user_list("org.openbmc.UserManager",
111 "/org/openbmc/UserManager/Users",
112 "org.openbmc.Enrol", "UserList");
113 bus->async_method_call(
114 [&](const boost::system::error_code ec,
Ed Tanousba9f9a62017-10-11 16:40:35 -0700115 const std::vector<std::string>& users) {
Ed Tanous911ac312017-08-15 09:37:42 -0700116 if (ec) {
117 res.code = 500;
118 } else {
Ed Tanousba9f9a62017-10-11 16:40:35 -0700119 res.json_value = {
Ed Tanous911ac312017-08-15 09:37:42 -0700120 {"@odata.context",
121 "/redfish/v1/"
122 "$metadata#ManagerAccountCollection."
123 "ManagerAccountCollection"},
124 {"@odata.id", "/redfish/v1/AccountService/Accounts"},
125 {"@odata.type",
126 "#ManagerAccountCollection.ManagerAccountCollection"},
127 {"Name", "Accounts Collection"},
128 {"Description", "BMC User Accounts"},
129 {"Members@odata.count", users.size()}};
Ed Tanousba9f9a62017-10-11 16:40:35 -0700130 nlohmann::json member_array = nlohmann::json::array();
Ed Tanous911ac312017-08-15 09:37:42 -0700131 int user_index = 0;
Ed Tanousc963aa42017-10-27 16:00:19 -0700132 for (int user_index = 0; user_index < users.size();
133 user_index++) {
Ed Tanous911ac312017-08-15 09:37:42 -0700134 member_array.push_back(
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100135 {{"@odata.id", "/redfish/v1/AccountService/Accounts/" +
136 std::to_string(user_index)}});
Ed Tanous911ac312017-08-15 09:37:42 -0700137 }
Ed Tanousba9f9a62017-10-11 16:40:35 -0700138 res.json_value["Members"] = member_array;
Ed Tanous911ac312017-08-15 09:37:42 -0700139 }
140 res.end();
Ed Tanousba9f9a62017-10-11 16:40:35 -0700141 },
142 user_list);
Ed Tanous3dac7492017-08-02 13:46:20 -0700143 });
144
145 CROW_ROUTE(app, "/redfish/v1/AccountService/Accounts/<int>/")
Ed Tanousba9f9a62017-10-11 16:40:35 -0700146 .methods("GET"_method)([](const crow::request& req, crow::response& res,
147 int account_index) {
148 res.json_value = {
Ed Tanous3dac7492017-08-02 13:46:20 -0700149 {"@odata.context",
150 "/redfish/v1/$metadata#ManagerAccount.ManagerAccount"},
151 {"@odata.id", "/redfish/v1/AccountService/Accounts/1"},
152 {"@odata.type", "#ManagerAccount.v1_0_3.ManagerAccount"},
153 {"Id", "1"},
154 {"Name", "User Account"},
155 {"Description", "User Account"},
156 {"Enabled", false},
157 {"Password", nullptr},
158 {"UserName", "anonymous"},
159 {"RoleId", "NoAccess"},
160 {"Links",
161 {{"Role",
162 {{"@odata.id", "/redfish/v1/AccountService/Roles/NoAccess"}}}}}};
Ed Tanousba9f9a62017-10-11 16:40:35 -0700163 res.end();
164 });
165
166 CROW_ROUTE(app, "/redfish/v1/SessionService/")
167 .methods(
168 "GET"_method)([&](const crow::request& req, crow::response& res) {
169 res.json_value = {
170 {"@odata.context",
171 "/redfish/v1/$metadata#SessionService.SessionService"},
172 {"@odata.id", "/redfish/v1/SessionService"},
173 {"@odata.type", "#SessionService.v1_1_1.SessionService"},
174 {"Id", "SessionService"},
175 {"Name", "SessionService"},
176 {"Description", "SessionService"},
177 {"Status",
178 {{"State", "Enabled"}, {"Health", "OK"}, {"HealthRollup", "OK"}}},
179 {"ServiceEnabled", true},
180 // TODO(ed) converge with session timeouts once they exist
181 // Bogus number for now
182 {"SessionTimeout", 1800}};
183 get_redfish_sub_routes(app, "/redfish/v1/AccountService",
184 res.json_value);
185 res.end();
186 });
187
Ed Tanous2a866f82017-10-25 17:46:24 -0700188 CROW_ROUTE(app, "/redfish/v1/Managers/")
189 .methods("GET"_method)(
190 [&](const crow::request& req, crow::response& res) {
191 res.json_value = {
192 {"@odata.context",
193 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection"},
194 {"@odata.id", "/redfish/v1/Managers"},
195 {"@odata.type", "#ManagerCollection.ManagerCollection"},
196 {"Name", "Manager Collection"},
197 {"Members@odata.count", 1},
198 {"Members", {{{"@odata.id", "/redfish/v1/Managers/openbmc"}}}}};
199 res.end();
200 });
201
202 CROW_ROUTE(app, "/redfish/v1/Managers/openbmc/")
203 .methods(
204 "GET"_method)([&](const crow::request& req, crow::response& res) {
205 time_t t = time(NULL);
206 tm* mytime = std::localtime(&t);
207 if (mytime == nullptr) {
208 res.code = 500;
209 res.end();
210 return;
211 }
212 std::array<char, 100> time_buffer;
213 std::size_t len = std::strftime(time_buffer.data(), time_buffer.size(),
214 "%FT%TZ", mytime);
215 if (len == 0) {
216 res.code = 500;
217 res.end();
218 return;
219 }
220 res.json_value = {
221 {"@odata.context", "/redfish/v1/$metadata#Manager.Manager"},
222 {"@odata.id", "/redfish/v1/Managers/openbmc"},
223 {"@odata.type", "#Manager.v1_3_0.Manager"},
224 {"Id", "openbmc"},
225 {"Name", "OpenBmc Manager"},
226 {"Description", "Baseboard Management Controller"},
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100227 {"UUID", app.template get_middleware<PersistentData::Middleware>()
228 .system_uuid},
Ed Tanous2a866f82017-10-25 17:46:24 -0700229 {"Model", "OpenBmc"}, // TODO(ed), get model
230 {"DateTime", time_buffer.data()},
231 {"Status",
232 {{"State", "Enabled"}, {"Health", "OK"}, {"HealthRollup", "OK"}}},
233 {"FirmwareVersion", "1234456789"}, // TODO(ed) get fwversion
234 {"PowerState", "On"}};
235 get_redfish_sub_routes(app, "/redfish/v1/Managers/openbmc/",
236 res.json_value);
237 res.end();
238 });
239
240 CROW_ROUTE(app, "/redfish/v1/Managers/NetworkProtocol/")
241 .methods(
242 "GET"_method)([&](const crow::request& req, crow::response& res) {
243 std::array<char, HOST_NAME_MAX> hostname;
244 if (gethostname(hostname.data(), hostname.size()) != 0) {
245 res.code = 500;
246 res.end();
247 return;
248 }
249 res.json_value = {
250 {"@odata.context",
251 "/redfish/v1/"
252 "$metadata#ManagerNetworkProtocol.ManagerNetworkProtocol"},
253 {"@odata.id", "/redfish/v1/Managers/BMC/NetworkProtocol"},
254 {"@odata.type",
255 "#ManagerNetworkProtocol.v1_1_0.ManagerNetworkProtocol"},
256 {"Id", "NetworkProtocol"},
257 {"Name", "Manager Network Protocol"},
258 {"Description", "Manager Network Service"},
259 {"Status",
260 {{"State", "Enabled"}, {"Health", "OK"}, {"HealthRollup", "OK"}}},
261 {"HostName", hostname.data()}}; // TODO(ed) get hostname
262 std::string netstat_out = execute_process("netstat -tuln");
263
264 std::map<int, const char*> service_types{{22, "SSH"},
265 {443, "HTTPS"},
266 {1900, "SSDP"},
267 {623, "IPMI"},
268 {427, "SLP"}};
269
270 std::vector<std::string> lines;
271 boost::split(lines, netstat_out, boost::is_any_of("\n"));
272 auto lines_it = lines.begin();
273 lines_it++; // skip the netstat header
274 lines_it++;
275 while (lines_it != lines.end()) {
276 std::vector<std::string> columns;
277 boost::split(columns, *lines_it, boost::is_any_of("\t "),
278 boost::token_compress_on);
279 if (columns.size() >= 5) {
280 std::size_t found = columns[3].find_last_of(":");
281 if (found != std::string::npos) {
282 std::string port_str = columns[3].substr(found + 1);
283 int port = std::stoi(port_str.c_str());
284 auto type_it = service_types.find(port);
285 if (type_it != service_types.end()) {
Ed Tanous2a866f82017-10-25 17:46:24 -0700286 res.json_value[type_it->second] = {{"ProtocolEnabled", true},
287 {"Port", port}};
288 }
289 }
290 }
291 lines_it++;
292 }
293
294 get_redfish_sub_routes(app, "/redfish/v1/", res.json_value);
295 res.end();
296 });
Ed Tanous3dac7492017-08-02 13:46:20 -0700297}
Ed Tanous911ac312017-08-15 09:37:42 -0700298} // namespace redfish
299} // namespace crow