diff options
| author | Ed Tanous <ed.tanous@intel.com> | 2018-02-06 17:08:16 -0800 |
|---|---|---|
| committer | Ed Tanous <ed.tanous@intel.com> | 2018-04-22 18:15:13 +0000 |
| commit | 64530018a8b9c564be992e3166741ad50e8a64b0 (patch) | |
| tree | 285c05d9d349e7bc5b9afbcb644bb4a6303e4b12 /include | |
| parent | 9e6e1b24e27da2d5c728f3666fca6108bd37b360 (diff) | |
| download | bmcweb-64530018a8b9c564be992e3166741ad50e8a64b0.tar.gz bmcweb-64530018a8b9c564be992e3166741ad50e8a64b0.zip | |
Clean up async dbus endpoint
This commit makes the webui more compatible with the dbus backend, and
cleans up the backend to support multiple concurrent transactions, as
well as support for the "enumerate" keyword.
Change-Id: I66144904b5c9b2ce7d1b1147cc80dfd356beb0bc
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
Diffstat (limited to 'include')
| -rw-r--r-- | include/openbmc_dbus_rest.hpp | 324 |
1 files changed, 190 insertions, 134 deletions
diff --git a/include/openbmc_dbus_rest.hpp b/include/openbmc_dbus_rest.hpp index 9e5e540..d42c04b 100644 --- a/include/openbmc_dbus_rest.hpp +++ b/include/openbmc_dbus_rest.hpp @@ -7,42 +7,35 @@ #include <dbus/match.hpp> #include <dbus/message.hpp> #include <dbus_singleton.hpp> +#include <boost/container/flat_set.hpp> namespace crow { namespace openbmc_mapper { -// TODO(ed) having these as scope globals, and as simple as they are limits the -// ability to queue multiple async operations at once. Being able to register -// "done" callbacks to a queue here that also had a count attached would allow -// multiple requests to be running at once -std::atomic<std::size_t> outstanding_async_calls(0); -nlohmann::json object_paths; -bool property_matched=false; void introspect_objects(crow::response &res, std::string process_name, - std::string path) { + std::string path, + std::shared_ptr<nlohmann::json> transaction) { dbus::endpoint introspect_endpoint( process_name, path, "org.freedesktop.DBus.Introspectable", "Introspect"); - outstanding_async_calls++; crow::connections::system_bus->async_method_call( [&, process_name{std::move(process_name)}, object_path{std::move(path)} ]( const boost::system::error_code ec, const std::string &introspect_xml) { - outstanding_async_calls--; if (ec) { - std::cerr << "Introspect call failed with error: " << ec.message() - << " on process: " << process_name - << " path: " << object_path << "\n"; + CROW_LOG_ERROR << "Introspect call failed with error: " + << ec.message() << " on process: " << process_name + << " path: " << object_path << "\n"; } else { - object_paths.push_back({{"path", object_path}}); + transaction->push_back({{"path", object_path}}); tinyxml2::XMLDocument doc; doc.Parse(introspect_xml.c_str()); tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); if (pRoot == nullptr) { - std::cerr << "XML document failed to parse " << process_name << " " - << path << "\n"; + CROW_LOG_ERROR << "XML document failed to parse " << process_name + << " " << path << "\n"; } else { tinyxml2::XMLElement *node = pRoot->FirstChildElement("node"); @@ -53,26 +46,101 @@ void introspect_objects(crow::response &res, std::string process_name, newpath += object_path; } newpath += "/" + child_path; - // intropect the subobjects as well - introspect_objects(res, process_name, newpath); + // introspect the subobjects as well + introspect_objects(res, process_name, newpath, transaction); node = node->NextSiblingElement("node"); } } } // if we're the last outstanding caller, finish the request - if (outstanding_async_calls == 0) { - nlohmann::json j{{"status", "ok"}, - {"bus_name", process_name}, - {"objects", object_paths}}; - - res.write(j.dump()); - object_paths.clear(); + if (transaction.use_count() == 1) { + res.json_value = {{"status", "ok"}, + {"bus_name", process_name}, + {"objects", *transaction}}; res.end(); } }, introspect_endpoint); } +using ManagedObjectType = std::vector<std::pair< + dbus::object_path, boost::container::flat_map< + std::string, boost::container::flat_map< + std::string, dbus::dbus_variant>>>>; + +void get_manged_objects_for_enumerate( + const std::string &object_name, const std::string &connection_name, + crow::response &res, std::shared_ptr<nlohmann::json> transaction) { + crow::connections::system_bus->async_method_call( + [&res, transaction](const boost::system::error_code ec, + const ManagedObjectType &objects) { + if (ec) { + CROW_LOG_ERROR << ec; + } else { + nlohmann::json &data_json = *transaction; + for (auto &object_path : objects) { + nlohmann::json &object_json = data_json[object_path.first.value]; + for (const auto &interface : object_path.second) { + for (const auto &property : interface.second) { + boost::apply_visitor( + [&](auto &&val) { object_json[property.first] = val; }, + property.second); + } + } + } + } + + if (transaction.use_count() == 1) { + res.json_value = {{"message", "200 OK"}, + {"status", "ok"}, + {"data", std::move(*transaction)}}; + res.end(); + } + }, + {connection_name, object_name, "org.freedesktop.DBus.ObjectManager", + "GetManagedObjects"}); +} // namespace openbmc_mapper + +using GetSubTreeType = std::vector< + std::pair<std::string, + std::vector<std::pair<std::string, std::vector<std::string>>>>>; + +void handle_enumerate(crow::response &res, const std::string &object_path) { + crow::connections::system_bus->async_method_call( + [&res, object_path{std::string(object_path)} ]( + const boost::system::error_code ec, + const GetSubTreeType &object_names) { + if (ec) { + res.code = 500; + res.end(); + return; + } + + boost::container::flat_set<std::string> connections; + + for (const auto &object : object_names) { + for (const auto &connection : object.second) { + connections.insert(connection.first); + } + } + + if (connections.size() <= 0) { + res.code = 404; + res.end(); + return; + } + auto transaction = + std::make_shared<nlohmann::json>(nlohmann::json::object()); + for (const std::string &connection : connections) { + get_manged_objects_for_enumerate(object_path, connection, res, + transaction); + } + + }, + {"xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTree"}, + object_path, (int32_t)0, std::array<std::string, 0>()); +} template <typename... Middlewares> void request_routes(Crow<Middlewares...> &app) { @@ -86,21 +154,19 @@ void request_routes(Crow<Middlewares...> &app) { crow::connections::system_bus->async_method_call( [&](const boost::system::error_code ec, std::vector<std::string> &names) { - std::sort(names.begin(), names.end()); + if (ec) { res.code = 500; } else { + std::sort(names.begin(), names.end()); nlohmann::json j{{"status", "ok"}}; auto &objects_sub = j["objects"]; for (auto &name : names) { objects_sub.push_back({{"name", name}}); } - - res.write(j.dump()); + res.json_value = std::move(j); } - res.end(); - }, {"org.freedesktop.DBus", "/", "org.freedesktop.DBus", "ListNames"}); @@ -115,10 +181,9 @@ void request_routes(Crow<Middlewares...> &app) { if (ec) { res.code = 500; } else { - nlohmann::json j{{"status", "ok"}, - {"message", "200 OK"}, - {"data", object_paths}}; - res.body = j.dump(); + res.json_value = {{"status", "ok"}, + {"message", "200 OK"}, + {"data", std::move(object_paths)}}; } res.end(); }, @@ -128,25 +193,21 @@ void request_routes(Crow<Middlewares...> &app) { "", static_cast<int32_t>(99), std::array<std::string, 0>()); }); - CROW_ROUTE(app, "/xyz/<path>") + CROW_ROUTE(app, "/xyz/<path>") .methods("GET"_method, "PUT"_method)([](const crow::request &req, crow::response &res, const std::string &path) { - if (outstanding_async_calls != 0) { - res.code = 500; - res.body = "request in progress"; - res.end(); - return; - } + std::shared_ptr<nlohmann::json> transaction = + std::make_shared<nlohmann::json>(nlohmann::json::object()); using GetObjectType = std::vector<std::pair<std::string, std::vector<std::string>>>; std::string object_path; std::string dest_property; std::string property_set_value; size_t attr_position = path.find("/attr/"); - if (attr_position == path.npos) + if (attr_position == path.npos) { object_path = "/xyz/" + path; - else { + } else { object_path = "/xyz/" + path.substr(0, attr_position); dest_property = path.substr((attr_position + strlen("/attr/")), path.length()); @@ -173,11 +234,17 @@ void request_routes(Crow<Middlewares...> &app) { } } + if (boost::ends_with(object_path, "/enumerate")) { + object_path.erase(object_path.end() - 10, object_path.end()); + handle_enumerate(res, object_path); + return; + } + crow::connections::system_bus->async_method_call( - // object_path intentially captured by value [ - &, object_path, dest_property{std::move(dest_property)}, - property_set_value{std::move(property_set_value)} + &, object_path{std::move(object_path)}, + dest_property{std::move(dest_property)}, + property_set_value{std::move(property_set_value)}, transaction ](const boost::system::error_code ec, const GetObjectType &object_names) { if (ec) { @@ -192,30 +259,27 @@ void request_routes(Crow<Middlewares...> &app) { } if (req.method == "GET"_method) { for (auto &interface : object_names[0].second) { - outstanding_async_calls++; crow::connections::system_bus->async_method_call( [&](const boost::system::error_code ec, const std::vector<std::pair< std::string, dbus::dbus_variant>> &properties) { - outstanding_async_calls--; if (ec) { - std::cerr << "Bad dbus request error: " << ec; + CROW_LOG_ERROR << "Bad dbus request error: " << ec; } else { for (auto &property : properties) { boost::apply_visitor( [&](auto val) { - object_paths[property.first] = val; + (*transaction)[property.first] = val; }, property.second); } } - if (outstanding_async_calls == 0) { - nlohmann::json j{{"status", "ok"}, - {"message", "200 OK"}, - {"data", object_paths}}; - res.body = j.dump(); + if (transaction.use_count() == 1) { + res.json_value = {{"status", "ok"}, + {"message", "200 OK"}, + {"data", *transaction}}; + res.end(); - object_paths.clear(); } }, {object_names[0].first, object_path, @@ -224,69 +288,66 @@ void request_routes(Crow<Middlewares...> &app) { } } else if (req.method == "PUT"_method) { for (auto &interface : object_names[0].second) { - outstanding_async_calls++; crow::connections::system_bus->async_method_call( [ &, interface{std::move(interface)}, object_names{std::move(object_names)}, object_path{std::move(object_path)}, dest_property{std::move(dest_property)}, - property_set_value{std::move(property_set_value)} + property_set_value{std::move(property_set_value)}, + transaction ](const boost::system::error_code ec, - const std::vector<std::pair< - std::string, dbus::dbus_variant>> &properties) { - outstanding_async_calls--; + const boost::container::flat_map< + std::string, dbus::dbus_variant> &properties) { if (ec) { - std::cerr << "Bad dbus request error: " << ec; + CROW_LOG_ERROR << "Bad dbus request error: " << ec; } else { - for (auto &property : properties) { - // search all the properties in the interfaces - if (dest_property.compare(property.first) == 0) { - // find the matched property in the interface - property_matched = true; - dbus::dbus_variant property_value( - property_set_value); // create the dbus - // variant for dbus call - crow::connections::system_bus->async_method_call( - [&](const boost::system::error_code ec) { - // use the method "Set" to set the property - // value - if (ec) { - std::cerr << "Bad dbus request error: " - << ec; - res.code = 500; - res.end(); - } else { - // find the matched property and send the - // response - res.json_value = {{"status", "ok"}, - {"message", "200 OK"}, - {"data", NULL}}; - res.end(); - return; - } - }, - {object_names[0].first, object_path, - "org.freedesktop.DBus.Properties", "Set"}, - interface, dest_property, property_value); - } + auto it = properties.find(dest_property); + if (it != properties.end()) { + // find the matched property in the interface + dbus::dbus_variant property_value( + property_set_value); // create the dbus + // variant for dbus call + crow::connections::system_bus->async_method_call( + [&](const boost::system::error_code ec) { + // use the method "Set" to set the property + // value + if (ec) { + CROW_LOG_ERROR << "Bad dbus request error: " + << ec; + } + // find the matched property and send the + // response + *transaction = {{"status", "ok"}, + {"message", "200 OK"}, + {"data", nullptr}}; + + }, + {object_names[0].first, object_path, + "org.freedesktop.DBus.Properties", "Set"}, + interface, dest_property, property_value); } - if (outstanding_async_calls == 0) { - // all search has been finished - if (property_matched == false) { - nlohmann::json j{{"status", "error"}, - {"message", "403 Forbidden"}, - {"data", - {{"message", - "The specified property " - "cannot be created: " + - dest_property}}}}; - res.json_value = j; - res.end(); - return; - } else - property_matched = false; + } + // if we are the last caller, finish the transaction + if (transaction.use_count() == 1) { + // if nobody filled in the property, all calls either + // errored, or failed + if (transaction == nullptr) { + res.code = 403; + res.json_value = {{"status", "error"}, + {"message", "403 Forbidden"}, + {"data", + {{"message", + "The specified property " + "cannot be created: " + + dest_property}}}}; + + } else { + res.json_value = *transaction; } + + res.end(); + return; } }, {object_names[0].first, object_path, @@ -304,13 +365,8 @@ void request_routes(Crow<Middlewares...> &app) { CROW_ROUTE(app, "/bus/system/<str>/") .methods("GET"_method)([](const crow::request &req, crow::response &res, const std::string &connection) { - if (outstanding_async_calls != 0) { - res.code = 500; - res.body = "request in progress"; - res.end(); - return; - } - introspect_objects(res, connection, "/"); + std::shared_ptr<nlohmann::json> transaction; + introspect_objects(res, connection, "/", transaction); }); CROW_ROUTE(app, "/bus/system/<str>/<path>") @@ -333,9 +389,8 @@ void request_routes(Crow<Middlewares...> &app) { if ((*it).find(".") != std::string::npos) { break; // THis check is neccesary as the trailing slash gets parsed as - // part - // of our <path> specifier above, which causes the normal trailing - // backslash redirector to fail. + // part of our <path> specifier above, which causes the normal + // trailing backslash redirector to fail. } else if (!it->empty()) { object_path += "/" + *it; } @@ -369,7 +424,7 @@ void request_routes(Crow<Middlewares...> &app) { ](const boost::system::error_code ec, const std::string &introspect_xml) { if (ec) { - std::cerr + CROW_LOG_ERROR << "Introspect call failed with error: " << ec.message() << " on process: " << process_name << " path: " << object_path << "\n"; @@ -380,8 +435,9 @@ void request_routes(Crow<Middlewares...> &app) { doc.Parse(introspect_xml.c_str()); tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); if (pRoot == nullptr) { - std::cerr << "XML document failed to parse " << process_name - << " " << object_path << "\n"; + CROW_LOG_ERROR << "XML document failed to parse " + << process_name << " " << object_path + << "\n"; res.write(nlohmann::json{{"status", "XML parse error"}}); res.code = 500; } else { @@ -395,11 +451,10 @@ void request_routes(Crow<Middlewares...> &app) { interface = interface->NextSiblingElement("interface"); } - nlohmann::json j{{"status", "ok"}, - {"bus_name", process_name}, - {"interfaces", interfaces_array}, - {"object_path", object_path}}; - res.write(j.dump()); + res.json_value = {{"status", "ok"}, + {"bus_name", process_name}, + {"interfaces", interfaces_array}, + {"object_path", object_path}}; } } res.end(); @@ -414,7 +469,7 @@ void request_routes(Crow<Middlewares...> &app) { ](const boost::system::error_code ec, const std::string &introspect_xml) { if (ec) { - std::cerr + CROW_LOG_ERROR << "Introspect call failed with error: " << ec.message() << " on process: " << process_name << " path: " << object_path << "\n"; @@ -425,8 +480,9 @@ void request_routes(Crow<Middlewares...> &app) { doc.Parse(introspect_xml.c_str()); tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); if (pRoot == nullptr) { - std::cerr << "XML document failed to parse " << process_name - << " " << object_path << "\n"; + CROW_LOG_ERROR << "XML document failed to parse " + << process_name << " " << object_path + << "\n"; res.code = 500; } else { @@ -458,10 +514,9 @@ void request_routes(Crow<Middlewares...> &app) { } methods_array.push_back( {{"name", methods->Attribute("name")}, - {"uri", - "/bus/system/" + process_name + object_path + - "/" + interface_name + "/" + - methods->Attribute("name")}, + {"uri", "/bus/system/" + process_name + + object_path + "/" + interface_name + + "/" + methods->Attribute("name")}, {"args", args_array}}); methods = methods->NextSiblingElement("method"); } @@ -476,7 +531,8 @@ void request_routes(Crow<Middlewares...> &app) { std::string name = arg->Attribute("name"); std::string type = arg->Attribute("type"); args_array.push_back({ - {"name", name}, {"type", type}, + {"name", name}, + {"type", type}, }); arg = arg->NextSiblingElement("arg"); } |

