From 9f9d294201dd12d79ca671eeab2d72ef05b97678 Mon Sep 17 00:00:00 2001 From: James Date: Thu, 14 Nov 2024 00:19:15 +0700 Subject: [PATCH 1/2] feat: add load/unload engine cli --- engine/cli/command_line_parser.cc | 75 ++++++++++ engine/cli/command_line_parser.h | 8 +- engine/cli/commands/config_upd_cmd.cc | 2 +- engine/cli/commands/engine_load_cmd.cc | 36 +++++ engine/cli/commands/engine_load_cmd.h | 13 ++ engine/cli/commands/engine_uninstall_cmd.cc | 2 +- engine/cli/commands/engine_unload_cmd.cc | 35 +++++ engine/cli/commands/engine_unload_cmd.h | 13 ++ engine/common/engine_servicei.h | 10 +- engine/controllers/engines.cc | 6 +- engine/utils/curl_utils.h | 158 ++++++-------------- 11 files changed, 235 insertions(+), 123 deletions(-) create mode 100644 engine/cli/commands/engine_load_cmd.cc create mode 100644 engine/cli/commands/engine_load_cmd.h create mode 100644 engine/cli/commands/engine_unload_cmd.cc create mode 100644 engine/cli/commands/engine_unload_cmd.h diff --git a/engine/cli/command_line_parser.cc b/engine/cli/command_line_parser.cc index d4c1ef793..136aebdbc 100644 --- a/engine/cli/command_line_parser.cc +++ b/engine/cli/command_line_parser.cc @@ -9,7 +9,9 @@ #include "commands/engine_get_cmd.h" #include "commands/engine_install_cmd.h" #include "commands/engine_list_cmd.h" +#include "commands/engine_load_cmd.h" #include "commands/engine_uninstall_cmd.h" +#include "commands/engine_unload_cmd.h" #include "commands/engine_update_cmd.h" #include "commands/engine_use_cmd.h" #include "commands/hardware_activate_cmd.h" @@ -474,6 +476,41 @@ void CommandLineParser::SetupEngineCommands() { EngineUse(engine_use_cmd, engine_name); } + auto engine_load_cmd = engines_cmd->add_subcommand("load", "Load engine"); + engine_load_cmd->usage("Usage:\n" + commands::GetCortexBinary() + + " engines load [engine_name]"); + engine_load_cmd->callback([this, engine_load_cmd] { + if (std::exchange(executed_, true)) + return; + if (engine_load_cmd->get_subcommands().empty()) { + CLI_LOG("[engine_name] is required\n"); + CLI_LOG(engine_load_cmd->help()); + } + }); + engine_load_cmd->group(kSubcommands); + for (auto& engine : engine_service_.kSupportEngines) { + std::string engine_name{engine}; + EngineLoad(engine_load_cmd, engine_name); + } + + auto engine_unload_cmd = + engines_cmd->add_subcommand("unload", "Unload engine"); + engine_unload_cmd->usage("Usage:\n" + commands::GetCortexBinary() + + " engines unload [engine_name]"); + engine_unload_cmd->callback([this, engine_unload_cmd] { + if (std::exchange(executed_, true)) + return; + if (engine_unload_cmd->get_subcommands().empty()) { + CLI_LOG("[engine_name] is required\n"); + CLI_LOG(engine_unload_cmd->help()); + } + }); + engine_unload_cmd->group(kSubcommands); + for (auto& engine : engine_service_.kSupportEngines) { + std::string engine_name{engine}; + EngineUnload(engine_unload_cmd, engine_name); + } + EngineGet(engines_cmd); } @@ -691,6 +728,44 @@ void CommandLineParser::EngineUpdate(CLI::App* parent, }); } +void CommandLineParser::EngineUnload(CLI::App* parent, + const std::string& engine_name) { + auto sub_cmd = parent->add_subcommand(engine_name, ""); + sub_cmd->usage("Usage:\n" + commands::GetCortexBinary() + " engines unload " + + engine_name); + sub_cmd->group(kEngineGroup); + + sub_cmd->callback([this, engine_name] { + if (std::exchange(executed_, true)) + return; + auto result = commands::EngineUnloadCmd().Exec( + cml_data_.config.apiServerHost, + std::stoi(cml_data_.config.apiServerPort), engine_name); + if (result.has_error()) { + CTL_ERR(result.error()); + } + }); +} + +void CommandLineParser::EngineLoad(CLI::App* parent, + const std::string& engine_name) { + auto sub_cmd = parent->add_subcommand(engine_name, ""); + sub_cmd->usage("Usage:\n" + commands::GetCortexBinary() + " engines load " + + engine_name); + sub_cmd->group(kEngineGroup); + + sub_cmd->callback([this, engine_name] { + if (std::exchange(executed_, true)) + return; + auto result = commands::EngineLoadCmd().Exec( + cml_data_.config.apiServerHost, + std::stoi(cml_data_.config.apiServerPort), engine_name); + if (result.has_error()) { + CTL_ERR(result.error()); + } + }); +} + void CommandLineParser::EngineUse(CLI::App* parent, const std::string& engine_name) { auto engine_use_cmd = parent->add_subcommand(engine_name, ""); diff --git a/engine/cli/command_line_parser.h b/engine/cli/command_line_parser.h index a6c8bcd62..bce83222a 100644 --- a/engine/cli/command_line_parser.h +++ b/engine/cli/command_line_parser.h @@ -3,11 +3,10 @@ #include #include #include "CLI/CLI.hpp" +#include "commands/hardware_list_cmd.h" #include "services/engine_service.h" #include "services/model_service.h" #include "utils/config_yaml_utils.h" -#include "commands/hardware_list_cmd.h" -#include "common/hardware_config.h" class CommandLineParser { public: @@ -40,6 +39,10 @@ class CommandLineParser { void EngineUse(CLI::App* parent, const std::string& engine_name); + void EngineLoad(CLI::App* parent, const std::string& engine_name); + + void EngineUnload(CLI::App* parent, const std::string& engine_name); + void ModelUpdate(CLI::App* parent); CLI::App app_; @@ -66,7 +69,6 @@ class CommandLineParser { bool show_menu = false; - int port; config_yaml_utils::CortexConfig config; std::unordered_map model_update_options; diff --git a/engine/cli/commands/config_upd_cmd.cc b/engine/cli/commands/config_upd_cmd.cc index ebf86fcea..ad1211f36 100644 --- a/engine/cli/commands/config_upd_cmd.cc +++ b/engine/cli/commands/config_upd_cmd.cc @@ -63,7 +63,7 @@ void commands::ConfigUpdCmd::Exec( } auto update_cnf_result = - curl_utils::SimplePatch(url.ToFullPath(), json.toStyledString()); + curl_utils::SimplePatchJson(url.ToFullPath(), json.toStyledString()); if (update_cnf_result.has_error()) { CLI_LOG_ERROR(update_cnf_result.error()); return; diff --git a/engine/cli/commands/engine_load_cmd.cc b/engine/cli/commands/engine_load_cmd.cc new file mode 100644 index 000000000..329d1b7e2 --- /dev/null +++ b/engine/cli/commands/engine_load_cmd.cc @@ -0,0 +1,36 @@ +#include "engine_load_cmd.h" +#include "server_start_cmd.h" +#include "utils/cli_selection_utils.h" +#include "utils/curl_utils.h" +#include "utils/logging_utils.h" +#include "utils/url_parser.h" + +namespace commands { +cpp::result EngineLoadCmd::Exec(const std::string& host, + int port, + const std::string& engine) { + // Start server if server is not started yet + if (!commands::IsServerAlive(host, port)) { + CLI_LOG("Starting server ..."); + commands::ServerStartCmd ssc; + if (!ssc.Exec(host, port)) { + return cpp::fail("Failed to start server"); + } + } + + auto load_engine_url = url_parser::Url{ + .protocol = "http", + .host = host + ":" + std::to_string(port), + .pathParams = {"v1", "engines", engine, "load"}, + }; + auto load_engine_result = + curl_utils::SimplePostJson(load_engine_url.ToFullPath()); + if (load_engine_result.has_error()) { + CTL_ERR(load_engine_result.error()); + return cpp::fail("Failed to load engine: " + load_engine_result.error()); + } + + CLI_LOG("Engine loaded successfully"); + return {}; +} +}; // namespace commands diff --git a/engine/cli/commands/engine_load_cmd.h b/engine/cli/commands/engine_load_cmd.h new file mode 100644 index 000000000..1b288036d --- /dev/null +++ b/engine/cli/commands/engine_load_cmd.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include "utils/result.hpp" + +namespace commands { + +class EngineLoadCmd { + public: + cpp::result Exec(const std::string& host, int port, + const std::string& engine); +}; +} // namespace commands diff --git a/engine/cli/commands/engine_uninstall_cmd.cc b/engine/cli/commands/engine_uninstall_cmd.cc index d915b1dc5..1ef5580a8 100644 --- a/engine/cli/commands/engine_uninstall_cmd.cc +++ b/engine/cli/commands/engine_uninstall_cmd.cc @@ -21,7 +21,7 @@ void EngineUninstallCmd::Exec(const std::string& host, int port, .host = host + ":" + std::to_string(port), .pathParams = {"v1", "engines", engine}}; - auto result = curl_utils::SimpleDelete(url.ToFullPath()); + auto result = curl_utils::SimpleDeleteJson(url.ToFullPath()); if (result.has_error()) { CTL_ERR(result.error()); return; diff --git a/engine/cli/commands/engine_unload_cmd.cc b/engine/cli/commands/engine_unload_cmd.cc new file mode 100644 index 000000000..e36a64f3b --- /dev/null +++ b/engine/cli/commands/engine_unload_cmd.cc @@ -0,0 +1,35 @@ +#include "engine_unload_cmd.h" +#include "server_start_cmd.h" +#include "utils/cli_selection_utils.h" +#include "utils/curl_utils.h" +#include "utils/logging_utils.h" +#include "utils/url_parser.h" + +namespace commands { +cpp::result EngineUnloadCmd::Exec( + const std::string& host, int port, const std::string& engine) { + // Start server if server is not started yet + if (!commands::IsServerAlive(host, port)) { + CLI_LOG("Starting server ..."); + commands::ServerStartCmd ssc; + if (!ssc.Exec(host, port)) { + return cpp::fail("Failed to start server"); + } + } + + auto load_engine_url = url_parser::Url{ + .protocol = "http", + .host = host + ":" + std::to_string(port), + .pathParams = {"v1", "engines", engine, "load"}, + }; + auto load_engine_result = + curl_utils::SimpleDeleteJson(load_engine_url.ToFullPath()); + if (load_engine_result.has_error()) { + CTL_ERR(load_engine_result.error()); + return cpp::fail("Failed to unload engine: " + load_engine_result.error()); + } + + CLI_LOG("Engine unloaded successfully"); + return {}; +} +}; // namespace commands diff --git a/engine/cli/commands/engine_unload_cmd.h b/engine/cli/commands/engine_unload_cmd.h new file mode 100644 index 000000000..25977b197 --- /dev/null +++ b/engine/cli/commands/engine_unload_cmd.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include "utils/result.hpp" + +namespace commands { + +class EngineUnloadCmd { + public: + cpp::result Exec(const std::string& host, int port, + const std::string& engine); +}; +} // namespace commands diff --git a/engine/common/engine_servicei.h b/engine/common/engine_servicei.h index fb81839fc..bd4f099ab 100644 --- a/engine/common/engine_servicei.h +++ b/engine/common/engine_servicei.h @@ -1,7 +1,8 @@ #pragma once + +#include #include #include -#include "json/json.h" #include "utils/result.hpp" // TODO: namh think of the other name @@ -37,12 +38,12 @@ struct EngineVariantResponse { class EngineServiceI { public: virtual ~EngineServiceI() {} - + virtual cpp::result SetDefaultEngineVariant(const std::string& engine, const std::string& version, const std::string& variant) = 0; -virtual cpp::result + virtual cpp::result GetDefaultEngineVariant(const std::string& engine) = 0; virtual cpp::result, std::string> @@ -53,5 +54,4 @@ virtual cpp::result virtual cpp::result UnloadEngine( const std::string& engine_name) = 0; - -}; \ No newline at end of file +}; diff --git a/engine/controllers/engines.cc b/engine/controllers/engines.cc index 63f86ed38..a75bd1f9b 100644 --- a/engine/controllers/engines.cc +++ b/engine/controllers/engines.cc @@ -229,8 +229,10 @@ void Engines::SetDefaultEngineVariant( resp->setStatusCode(k400BadRequest); callback(resp); } else { - auto resp = - cortex_utils::CreateCortexHttpJsonResponse(result.value().ToJson()); + Json::Value res; + res["message"] = "Engine " + result.value().variant + " " + + result.value().version + " set as default"; + auto resp = cortex_utils::CreateCortexHttpJsonResponse(res); resp->setStatusCode(k200OK); callback(resp); } diff --git a/engine/utils/curl_utils.h b/engine/utils/curl_utils.h index 739565a69..b9c4abb80 100644 --- a/engine/utils/curl_utils.h +++ b/engine/utils/curl_utils.h @@ -13,6 +13,9 @@ #include "utils/url_parser.h" namespace curl_utils { + +enum class RequestType { GET, PATCH, POST, DELETE }; + namespace { size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* output) { @@ -75,8 +78,9 @@ inline cpp::result SimpleGet(const std::string& url, return readBuffer; } -inline cpp::result SimplePatch( - const std::string& url, const std::string& body = "") { +inline cpp::result SimpleRequest( + const std::string& url, const RequestType& request_type, + const std::string& body = "") { auto curl = curl_easy_init(); if (!curl) { @@ -87,6 +91,7 @@ inline cpp::result SimplePatch( curl_slist* curl_headers = nullptr; curl_headers = curl_slist_append(curl_headers, "Content-Type: application/json"); + curl_headers = curl_slist_append(curl_headers, "Expect:"); if (headers.has_value()) { for (const auto& [key, value] : headers.value()) { @@ -94,85 +99,43 @@ inline cpp::result SimplePatch( curl_headers = curl_slist_append(curl_headers, header.c_str()); } } - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_headers); - std::string readBuffer; + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_headers); curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH"); + if (request_type == RequestType::PATCH) { + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH"); + } else if (request_type == RequestType::POST) { + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POST, 1L); + } else if (request_type == RequestType::DELETE) { + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); + } + // enable below line for debugging + // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); - // Set content length if body is not empty - if (!body.empty()) { - curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, body.length()); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str()); - } + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, body.length()); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str()); // Perform the request auto res = curl_easy_perform(curl); - curl_slist_free_all(curl_headers); - curl_easy_cleanup(curl); - if (res != CURLE_OK) { - CTL_ERR("CURL request failed: " + std::string(curl_easy_strerror(res))); - return cpp::fail("CURL request failed: " + - static_cast(curl_easy_strerror(res))); - } - auto http_code = 0; + auto http_code = 0L; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); - if (http_code >= 400) { - CTL_ERR("HTTP request failed with status code: " + - std::to_string(http_code)); - return cpp::fail(readBuffer); - } - - return readBuffer; -} - -inline cpp::result SimplePost( - const std::string& url, const std::string& body = "") { - curl_global_init(CURL_GLOBAL_DEFAULT); - auto curl = curl_easy_init(); - - if (!curl) { - return cpp::fail("Failed to init CURL"); - } - - auto headers = GetHeaders(url); - curl_slist* curl_headers = nullptr; - if (headers.has_value()) { - - for (const auto& [key, value] : headers.value()) { - auto header = key + ": " + value; - curl_headers = curl_slist_append(curl_headers, header.c_str()); - } - - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_headers); - } - - std::string readBuffer; - - curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl, CURLOPT_POST, 1L); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str()); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); - - // Perform the request - auto res = curl_easy_perform(curl); + // Clean up curl_slist_free_all(curl_headers); curl_easy_cleanup(curl); + if (res != CURLE_OK) { CTL_ERR("CURL request failed: " + std::string(curl_easy_strerror(res))); return cpp::fail("CURL request failed: " + static_cast(curl_easy_strerror(res))); } - auto http_code = 0; - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); + if (http_code >= 400) { CTL_ERR("HTTP request failed with status code: " + std::to_string(http_code)); @@ -182,54 +145,6 @@ inline cpp::result SimplePost( return readBuffer; } -inline cpp::result SimpleDelete( - const std::string& url) { - - std::string readBuffer; - auto curl = curl_easy_init(); - - if (!curl) { - throw std::runtime_error("Failed to initialize CURL"); - } - - auto headers = GetHeaders(url); - curl_slist* curl_headers = nullptr; - if (headers.has_value()) { - for (const auto& [key, value] : headers.value()) { - auto header = key + ": " + value; - curl_headers = curl_slist_append(curl_headers, header.c_str()); - } - - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_headers); - } - - curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); - - // Perform the request - auto res = curl_easy_perform(curl); - - long responseCode; - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode); - - curl_slist_free_all(curl_headers); - curl_easy_cleanup(curl); - - if (res != CURLE_OK) { - throw std::runtime_error(std::string("Delete request failed: ") + - curl_easy_strerror(res)); - } - - if (responseCode >= 400) { - throw std::runtime_error("HTTP error: " + std::to_string(responseCode) + - "\nResponse: " + readBuffer); - } - - return readBuffer; -} - inline cpp::result ReadRemoteYaml( const std::string& url) { auto result = SimpleGet(url); @@ -271,7 +186,28 @@ inline cpp::result SimpleGetJson( inline cpp::result SimplePostJson( const std::string& url, const std::string& body = "") { - auto result = SimplePost(url, body); + auto result = SimpleRequest(url, RequestType::POST, body); + if (result.has_error()) { + CTL_INF("url: " + url); + CTL_INF("body: " + body); + CTL_ERR("Failed to get JSON from " + url + ": " + result.error()); + return cpp::fail(result.error()); + } + + CTL_INF("Response: " + result.value()); + Json::Value root; + Json::Reader reader; + if (!reader.parse(result.value(), root)) { + return cpp::fail("JSON from " + url + + " parsing error: " + reader.getFormattedErrorMessages()); + } + + return root; +} + +inline cpp::result SimpleDeleteJson( + const std::string& url, const std::string& body = "") { + auto result = SimpleRequest(url, RequestType::DELETE, body); if (result.has_error()) { CTL_ERR("Failed to get JSON from " + url + ": " + result.error()); return cpp::fail(result.error()); @@ -290,7 +226,7 @@ inline cpp::result SimplePostJson( inline cpp::result SimplePatchJson( const std::string& url, const std::string& body = "") { - auto result = SimplePatch(url, body); + auto result = SimpleRequest(url, RequestType::PATCH, body); if (result.has_error()) { CTL_ERR("Failed to get JSON from " + url + ": " + result.error()); return cpp::fail(result.error()); From 4dbca80ede261976234ebec0cfd898d64394d961 Mon Sep 17 00:00:00 2001 From: James Date: Thu, 14 Nov 2024 01:10:51 +0700 Subject: [PATCH 2/2] fix build windows --- engine/utils/curl_utils.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/engine/utils/curl_utils.h b/engine/utils/curl_utils.h index b9c4abb80..55a824c6c 100644 --- a/engine/utils/curl_utils.h +++ b/engine/utils/curl_utils.h @@ -12,9 +12,9 @@ #include "utils/result.hpp" #include "utils/url_parser.h" -namespace curl_utils { +enum class RequestType { GET, PATCH, POST, DEL }; -enum class RequestType { GET, PATCH, POST, DELETE }; +namespace curl_utils { namespace { size_t WriteCallback(void* contents, size_t size, size_t nmemb, @@ -108,7 +108,7 @@ inline cpp::result SimpleRequest( } else if (request_type == RequestType::POST) { curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); curl_easy_setopt(curl, CURLOPT_POST, 1L); - } else if (request_type == RequestType::DELETE) { + } else if (request_type == RequestType::DEL) { curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); } // enable below line for debugging @@ -207,7 +207,7 @@ inline cpp::result SimplePostJson( inline cpp::result SimpleDeleteJson( const std::string& url, const std::string& body = "") { - auto result = SimpleRequest(url, RequestType::DELETE, body); + auto result = SimpleRequest(url, RequestType::DEL, body); if (result.has_error()) { CTL_ERR("Failed to get JSON from " + url + ": " + result.error()); return cpp::fail(result.error());