| #include "managed_store_http.hpp" | |
| #include <malloc.h> | |
| #include <charconv> | |
| #include <cstddef> | |
| #include <cstdio> | |
| #include <cstring> | |
| #include <memory> | |
| #include <string> | |
| #include <string_view> | |
| #include "absl/strings/match.h" | |
| #include "app.hpp" | |
| #include "http_request.hpp" | |
| #include "logging.hpp" | |
| #include "async_resp.hpp" | |
| #include "query.hpp" | |
| #include <nlohmann/json.hpp> | |
| #include "managed_store.hpp" | |
| #include "managed_store_clock.hpp" | |
| #include "managed_store_types.hpp" | |
| namespace managedStore { | |
| void ManagedObjectStoreHttp::requestRoutesManagedStoreDebug( | |
| const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| // playing with the header: | |
| auto maxAgeSec = 0; | |
| const auto maxAgeSecStr = req.getHeaderValue(bmcweb::BMCWEB_HINT_MAX_AGE_SEC); | |
| if (!maxAgeSecStr.empty()) { | |
| std::from_chars(maxAgeSecStr.data(), | |
| maxAgeSecStr.data() + maxAgeSecStr.size(), maxAgeSec); | |
| } | |
| BMCWEB_LOG_STATEFUL_DEBUG | |
| << "=> /debug/managed_store: target: " << req.target() | |
| << " method: " << req.method() << " body: " << req.body() | |
| << " maxAgeSecStr: " << maxAgeSecStr << " maxAgeSec: " << maxAgeSec; | |
| // ManagedObjectStore instance: | |
| auto* store = managedStore::GetManagedObjectStore(); | |
| if (store == nullptr) { | |
| asyncResp->res.jsonValue["err"] = "managed object store is null"; | |
| BMCWEB_LOG_STATEFUL_ERROR << "=> ERROR: " | |
| << asyncResp->res.jsonValue.dump(); | |
| return; | |
| } | |
| // config: | |
| asyncResp->res.jsonValue["config"] = store->getConfig().toJson(); | |
| // objects: | |
| asyncResp->res.jsonValue["managed_objects_metrics"] = | |
| store->GetManagedObjectsMetrics(managedStore::clockNow()); | |
| // serialized size: | |
| asyncResp->res.jsonValue["snapshot_size_in_bytes"] = | |
| store->GetSnapshotSizeInBytes(); | |
| // Export pq entries: | |
| store->GetPriorityQueueSize([asyncResp](size_t size) { | |
| asyncResp->res.jsonValue["managed_objects_pq_size"] = size; | |
| }); | |
| store->GetPriorityQueueMetrics([asyncResp](const nlohmann::json& metrics) { | |
| asyncResp->res.jsonValue["managed_objects_pq_entries"] = metrics; | |
| }); | |
| // ManagedStore counters from managedStoreTracker | |
| const ManagedStoreTracker& storeTracker = store->getStoreTracker(); | |
| asyncResp->res.jsonValue["tracker"] = storeTracker.toJson(); | |
| // always keep this one as the last item: | |
| asyncResp->res.jsonValue["ok"] = true; | |
| } | |
| void ManagedObjectStoreHttp::requestRoutesManagedStorePostFlush( | |
| const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| BMCWEB_LOG_STATEFUL_DEBUG | |
| << "=> /debug/managed_store: target: " << req.target() | |
| << " method: " << req.method() << " body: " << req.body(); | |
| // ManagedObjectStore instance: | |
| auto* store = managedStore::GetManagedObjectStore(); | |
| if (store == nullptr) { | |
| asyncResp->res.jsonValue["err"] = "managed object store is null"; | |
| BMCWEB_LOG_STATEFUL_ERROR << "=> ERROR: " | |
| << asyncResp->res.jsonValue.dump(); | |
| return; | |
| } | |
| asyncResp->res.jsonValue["config"] = store->getConfig().toJson(); | |
| store->clearAllObjects(); | |
| asyncResp->res.jsonValue["managed_objects_metrics"] = | |
| store->GetManagedObjectsMetrics(managedStore::clockNow()); | |
| store->GetPriorityQueueSize([asyncResp](size_t size) { | |
| asyncResp->res.jsonValue["managed_objects_pq_size"] = size; | |
| }); | |
| // always keep this one as the last item: | |
| asyncResp->res.jsonValue["ok"] = true; | |
| } | |
| void ManagedObjectStoreHttp::requestRoutesSubscriptionsDebug( | |
| const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| BMCWEB_LOG_STATEFUL_DEBUG | |
| << "=> /debug/subscriptions: target: " << req.target() | |
| << " method: " << req.method() << " body: " << req.body(); | |
| // ManagedObjectStore instance: | |
| auto* store = managedStore::GetManagedObjectStore(); | |
| if (store == nullptr) { | |
| asyncResp->res.jsonValue["err"] = "managed object store is null"; | |
| BMCWEB_LOG_STATEFUL_ERROR << "=> ERROR: " | |
| << asyncResp->res.jsonValue.dump(); | |
| return; | |
| } | |
| asyncResp->res.jsonValue["SubscriptionStore"] = | |
| store->GetSubscriptionsToJSON(); | |
| asyncResp->res.jsonValue["DBusMonitors"] = store->GetDBusMonitorsToJSON(); | |
| } | |
| void ManagedObjectStoreHttp::requestRoutesEventStoreDebug( | |
| const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| BMCWEB_LOG_STATEFUL_DEBUG << "=> /debug/event_store: target: " << req.target() | |
| << " method: " << req.method() | |
| << " body: " << req.body(); | |
| // ManagedObjectStore instance: | |
| auto* store = managedStore::GetManagedObjectStore(); | |
| if (store == nullptr) { | |
| asyncResp->res.jsonValue["err"] = "managed object store is null"; | |
| BMCWEB_LOG_STATEFUL_ERROR << "=> ERROR: " | |
| << asyncResp->res.jsonValue.dump(); | |
| return; | |
| } | |
| std::string_view target = req.target(); | |
| auto startIndex = target.find('?'); | |
| if (startIndex != std::string::npos) { | |
| // The query has query params | |
| auto subIdIndex = target.find("subscription_id=", startIndex); | |
| if (subIdIndex == std::string::npos || subIdIndex != startIndex + 1) { | |
| asyncResp->res.jsonValue["Error"] = | |
| "Invalid query param. Only subscription_id param is supported."; | |
| return; | |
| } | |
| auto len = strlen("subscription_id="); | |
| std::string_view subscription_id = target.substr(subIdIndex + len); | |
| if (subscription_id.empty() || absl::StrContains(subscription_id, '?')) { | |
| asyncResp->res.jsonValue["Error"] = | |
| "Invalid query params. Only subscription_id param is supported."; | |
| return; | |
| } | |
| BMCWEB_LOG_STATEFUL_DEBUG << "subscription_id = " << subscription_id; | |
| size_t id = 0; | |
| if (1 == sscanf(subscription_id.data(), "%zu", &id)) // NOLINT | |
| { | |
| asyncResp->res.jsonValue = store->GetEventsBySubscriptionIdToJSON(id); | |
| return; | |
| } | |
| asyncResp->res.jsonValue["Error"] = "Invalid subscription_id."; | |
| return; | |
| } | |
| asyncResp->res.jsonValue = store->GetEventsToJSON(); | |
| } | |
| void ManagedObjectStoreHttp::requestRoutesEventStoreFlush( | |
| const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| BMCWEB_LOG_STATEFUL_DEBUG << "=> /debug/event_store: target: " << req.target() | |
| << " method: " << req.method() | |
| << " body: " << req.body(); | |
| // ManagedObjectStore instance: | |
| auto* store = managedStore::GetManagedObjectStore(); | |
| if (store == nullptr) { | |
| asyncResp->res.jsonValue["err"] = "managed object store is null"; | |
| BMCWEB_LOG_STATEFUL_ERROR << "=> ERROR: " | |
| << asyncResp->res.jsonValue.dump(); | |
| return; | |
| } | |
| store->ClearEventStore(); | |
| asyncResp->res.jsonValue = store->GetEventsToJSON(); | |
| } | |
| void ManagedObjectStoreHttp::requestGetGoogleManagedObjectStoreMetrics( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| asyncResp->res.jsonValue = | |
| managedStore::GetManagedObjectStore()->GetManagedObjectsMetrics( | |
| managedStore::clockNow()); | |
| } | |
| void ManagedObjectStoreHttp::requestGetGoogleTimetrace( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| (void)app; | |
| BMCWEB_LOG_STATEFUL_DEBUG | |
| << "=> GoogleTimetrace:" << " method: " << req.method() | |
| << " target: " << req.target() << " body: " << req.body(); | |
| // ManagedObjectStore instance: | |
| auto* store = managedStore::GetManagedObjectStore(); | |
| if (store == nullptr) { | |
| asyncResp->res.jsonValue["err"] = "managed object store is null"; | |
| BMCWEB_LOG_STATEFUL_ERROR << "=> ERROR: " | |
| << asyncResp->res.jsonValue.dump(); | |
| return; | |
| } | |
| nlohmann::json google; | |
| google["@odata.id"] = | |
| "/redfish/v1/Managers/bmc/ManagerDiagnosticData/Oem/Google/" | |
| "GoogleTimetrace"; | |
| google["@odata.type"] = "#GoogleTimetrace.v1_0_0.GoogleTimetrace"; | |
| google["Actions"]["#GoogleTimetrace.Clear"] = { | |
| {"target", | |
| "/redfish/v1/Managers/bmc/ManagerDiagnosticData/Oem/Google/" | |
| "GoogleTimetrace/Actions/GoogleTimetrace.Clear"}}; | |
| // timeTrace | |
| const auto& timeTrace = store->getTimeTrace(); | |
| google["time_trace_size"] = timeTrace.size(); | |
| google["timetrace"] = timeTrace; | |
| // always keep this one as the last item: | |
| // final object: | |
| asyncResp->res.jsonValue = google; | |
| } | |
| void ManagedObjectStoreHttp::handleGoogleTimetraceClearPost( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| (void)app; | |
| BMCWEB_LOG_STATEFUL_DEBUG | |
| << "=> GoogleTimetraceClear:" << " method: " << req.method() | |
| << " target: " << req.target() << " body: " << req.body(); | |
| // ManagedObjectStore instance: | |
| auto* store = managedStore::GetManagedObjectStore(); | |
| if (store == nullptr) { | |
| asyncResp->res.jsonValue["err"] = "managed object store is null"; | |
| BMCWEB_LOG_STATEFUL_ERROR << "=> ERROR: " | |
| << asyncResp->res.jsonValue.dump(); | |
| return; | |
| } | |
| store->clearTimeTrace(); | |
| } | |
| void ManagedObjectStoreHttp::handleGoogleTakeSnapShot( | |
| [[maybe_unused]] App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| BMCWEB_LOG_STATEFUL_DEBUG | |
| << "=> GoogleTakeSnapShot:" << " method: " << req.method() | |
| << " target: " << req.target() << " body: " << req.body(); | |
| auto* store = managedStore::GetManagedObjectStore(); | |
| if (store == nullptr) { | |
| asyncResp->res.jsonValue["err"] = "managed object store is null"; | |
| BMCWEB_LOG_STATEFUL_ERROR << "=> ERROR: " | |
| << asyncResp->res.jsonValue.dump(); | |
| return; | |
| } | |
| if (store->serialize()) { | |
| asyncResp->res.jsonValue["ok"] = true; | |
| } else { | |
| asyncResp->res.jsonValue["err"] = "Error making snapshot"; | |
| BMCWEB_LOG_STATEFUL_ERROR << "=> ERROR: " | |
| << asyncResp->res.jsonValue.dump(); | |
| } | |
| } | |
| void ManagedObjectStoreHttp::handleGoogleLoadSnapShot( | |
| [[maybe_unused]] App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| BMCWEB_LOG_STATEFUL_DEBUG | |
| << "=> GoogleLoadSnapShot:" << " method: " << req.method() | |
| << " target: " << req.target() << " body: " << req.body(); | |
| auto* store = managedStore::GetManagedObjectStore(); | |
| if (store == nullptr) { | |
| asyncResp->res.jsonValue["err"] = "managed object store is null"; | |
| BMCWEB_LOG_STATEFUL_ERROR << "=> ERROR: " | |
| << asyncResp->res.jsonValue.dump(); | |
| return; | |
| } | |
| if (store->deserialize()) { | |
| asyncResp->res.jsonValue["ok"] = true; | |
| } else { | |
| asyncResp->res.jsonValue["err"] = "Error loading snapshot"; | |
| BMCWEB_LOG_STATEFUL_ERROR << "=> ERROR: " | |
| << asyncResp->res.jsonValue.dump(); | |
| } | |
| } | |
| void requestRoutesManagedStore(App& app) { | |
| // a few exports for debugging: | |
| BMCWEB_ROUTE(app, "/debug/malloc_stats") | |
| .methods(boost::beast::http::verb::get)( | |
| []([[maybe_unused]] const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| asyncResp->res.jsonValue["ok"] = true; | |
| malloc_stats(); | |
| }); | |
| // a few exports for debugging: | |
| BMCWEB_ROUTE(app, "/debug/managed_store") | |
| // .privileges(redfish::privileges::postManager) | |
| .methods(boost::beast::http::verb::get)( | |
| [](const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| ManagedObjectStoreHttp::requestRoutesManagedStoreDebug(req, | |
| asyncResp); | |
| }); | |
| // flush the managed objects: | |
| BMCWEB_ROUTE(app, "/debug/managed_store/clear") | |
| .methods(boost::beast::http::verb::post)( | |
| [](const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| ManagedObjectStoreHttp::requestRoutesManagedStorePostFlush( | |
| req, asyncResp); | |
| }); | |
| // debug subscriptions | |
| BMCWEB_ROUTE(app, "/debug/subscriptions") | |
| .methods(boost::beast::http::verb::get)( | |
| [](const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| ManagedObjectStoreHttp::requestRoutesSubscriptionsDebug(req, | |
| asyncResp); | |
| }); | |
| // debug events | |
| BMCWEB_ROUTE(app, "/debug/event_store") | |
| .methods(boost::beast::http::verb::get)( | |
| [](const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| ManagedObjectStoreHttp::requestRoutesEventStoreDebug(req, | |
| asyncResp); | |
| }); | |
| // flush the event store | |
| BMCWEB_ROUTE(app, "/debug/event_store/clear") | |
| .methods(boost::beast::http::verb::post)( | |
| [](const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| ManagedObjectStoreHttp::requestRoutesEventStoreFlush(req, | |
| asyncResp); | |
| }); | |
| // GoogleManagedObjectStoreMetrics: | |
| BMCWEB_ROUTE(app, | |
| "/redfish/v1/Managers/bmc/ManagerDiagnosticData/Oem/Google/" | |
| "GoogleManagedObjectStoreMetrics") | |
| .methods(boost::beast::http::verb::get)( | |
| [&app](const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| ManagedObjectStoreHttp::requestGetGoogleManagedObjectStoreMetrics( | |
| app, req, asyncResp); | |
| }); | |
| // requests time trace | |
| BMCWEB_ROUTE(app, | |
| "/redfish/v1/Managers/bmc/ManagerDiagnosticData/Oem/Google/" | |
| "GoogleTimetrace") | |
| .methods(boost::beast::http::verb::get)( | |
| [&app](const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| ManagedObjectStoreHttp::requestGetGoogleTimetrace(app, req, | |
| asyncResp); | |
| }); | |
| // Clear timetrace: | |
| BMCWEB_ROUTE(app, | |
| "/redfish/v1/Managers/bmc/ManagerDiagnosticData/Oem/Google/" | |
| "GoogleTimetrace/Actions/GoogleTimetrace.Clear") | |
| .methods(boost::beast::http::verb::post)( | |
| [&app](const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| ManagedObjectStoreHttp::handleGoogleTimetraceClearPost(app, req, | |
| asyncResp); | |
| }); | |
| // take snapshot | |
| BMCWEB_ROUTE(app, | |
| "/redfish/v1/Managers/bmc/ManagerDiagnosticData/Oem/Google/" | |
| "GoogleSnapShot/Actions/GoogleTakeSnapShot") | |
| .methods(boost::beast::http::verb::post)( | |
| [&app](const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| ManagedObjectStoreHttp::handleGoogleTakeSnapShot(app, req, | |
| asyncResp); | |
| }); | |
| // load snapshot | |
| BMCWEB_ROUTE(app, | |
| "/redfish/v1/Managers/bmc/ManagerDiagnosticData/Oem/Google/" | |
| "GoogleSnapShot/Actions/GoogleLoadSnapShot") | |
| .methods(boost::beast::http::verb::post)( | |
| [&app](const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| ManagedObjectStoreHttp::handleGoogleLoadSnapShot(app, req, | |
| asyncResp); | |
| }); | |
| } | |
| } // namespace managedStore |