From bbd843dadfba3d2af3128427b0dbd4d17e49b3ff Mon Sep 17 00:00:00 2001 From: Eugene Smirnov Date: Tue, 31 Mar 2020 16:39:21 +0200 Subject: [PATCH 1/3] libaktualizr API: support sending custom hardware info Signed-off-by: Eugene Smirnov --- src/libaktualizr/primary/aktualizr.cc | 4 +- src/libaktualizr/primary/aktualizr.h | 3 +- src/libaktualizr/primary/aktualizr_test.cc | 49 ++++++++++++++++++++ src/libaktualizr/primary/sotauptaneclient.cc | 28 ++++++----- src/libaktualizr/primary/sotauptaneclient.h | 4 +- 5 files changed, 72 insertions(+), 16 deletions(-) diff --git a/src/libaktualizr/primary/aktualizr.cc b/src/libaktualizr/primary/aktualizr.cc index 2e38cc291f..271dac0c00 100644 --- a/src/libaktualizr/primary/aktualizr.cc +++ b/src/libaktualizr/primary/aktualizr.cc @@ -136,8 +136,8 @@ std::future Aktualizr::CampaignControl(const std::string &campaign_id, cam return api_queue_.enqueue(task); } -std::future Aktualizr::SendDeviceData() { - std::function task([this] { uptane_client_->sendDeviceData(); }); +std::future Aktualizr::SendDeviceData(const Json::Value &custom_hwinfo) { + std::function task([this, custom_hwinfo] { uptane_client_->sendDeviceData(custom_hwinfo); }); return api_queue_.enqueue(task); } diff --git a/src/libaktualizr/primary/aktualizr.h b/src/libaktualizr/primary/aktualizr.h index 78140ef94f..3100e52b80 100644 --- a/src/libaktualizr/primary/aktualizr.h +++ b/src/libaktualizr/primary/aktualizr.h @@ -71,9 +71,10 @@ class Aktualizr { /** * Send local device data to the server. * This includes network status, installed packages, hardware etc. + * @param custom_hwinfo if not empty will be sent to the backend instead of `lshw` output * @return Empty std::future object */ - std::future SendDeviceData(); + std::future SendDeviceData(const Json::Value& custom_hwinfo = Json::nullValue); /** * Fetch Uptane metadata and check for updates. diff --git a/src/libaktualizr/primary/aktualizr_test.cc b/src/libaktualizr/primary/aktualizr_test.cc index f6a7eda87a..65e9054c07 100644 --- a/src/libaktualizr/primary/aktualizr_test.cc +++ b/src/libaktualizr/primary/aktualizr_test.cc @@ -2280,6 +2280,55 @@ TEST(Aktualizr, PauseResumeQueue) { } } +const std::string custom_hwinfo = + R"([{"description":"ECU1","ECU P/N":"AAA","HW P/N":"BBB","HW Version":"1.234", + "SW P/N":"CCC","SW Version":"4.321","ECU Serial":"AAA-BBB-CCC"}, + {"description":"ECU2","ECU P/N":"ZZZ","HW P/N":"XXX","HW Version":"9.876", + "SW P/N":"YYY","SW Version":"6.789","ECU Serial":"VVV-NNN-MMM"}])"; + +class HttpSystemInfo : public HttpFake { + public: + HttpSystemInfo(const boost::filesystem::path& test_dir_in, const boost::filesystem::path& meta_dir_in) + : HttpFake(test_dir_in, "", meta_dir_in) {} + + HttpResponse put(const std::string& url, const Json::Value& data) override { + if (url.find(hwinfo_ep_) == url.length() - hwinfo_ep_.length()) { + if (info_count_ == 0) { // expect lshw data + EXPECT_TRUE(data.isObject()); + EXPECT_TRUE(data.isMember("description")); + } else if (info_count_ == 1) { // expect custom data + auto hwinfo = Utils::parseJSON(custom_hwinfo); + EXPECT_EQ(hwinfo, data); + } + info_count_++; + return HttpResponse("", 200, CURLE_OK, ""); + } else if (url.find("/manifest") != std::string::npos) { + return HttpResponse("", 200, CURLE_OK, ""); + } + return HttpResponse("", 404, CURLE_HTTP_RETURNED_ERROR, "Not found"); + } + + ~HttpSystemInfo() { EXPECT_EQ(info_count_, 2); } + + int info_count_{0}; + std::string hwinfo_ep_{"/system_info"}; +}; + +TEST(Aktualizr, CustomHwInfo) { + TemporaryDirectory temp_dir; + auto http = std::make_shared(temp_dir.Path(), fake_meta_dir); + Config conf = UptaneTestCommon::makeTestConfig(temp_dir, http->tls_server); + + auto storage = INvStorage::newStorage(conf.storage); + UptaneTestCommon::TestAktualizr aktualizr(conf, storage, http); + aktualizr.Initialize(); + + aktualizr.SendDeviceData().get(); + + auto hwinfo = Utils::parseJSON(custom_hwinfo); + aktualizr.SendDeviceData(hwinfo).get(); +} + #ifndef __NO_MAIN__ int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); diff --git a/src/libaktualizr/primary/sotauptaneclient.cc b/src/libaktualizr/primary/sotauptaneclient.cc index b17aeebf51..34c5c1d00d 100644 --- a/src/libaktualizr/primary/sotauptaneclient.cc +++ b/src/libaktualizr/primary/sotauptaneclient.cc @@ -160,16 +160,22 @@ data::InstallationResult SotaUptaneClient::PackageInstallSetResult(const Uptane: return result; } -void SotaUptaneClient::reportHwInfo() { - Json::Value hw_info = Utils::getHardwareInfo(); - if (!hw_info.empty()) { - if (hw_info != last_hw_info_reported) { - if (http->put(config.tls.server + "/system_info", hw_info).isOk()) { - last_hw_info_reported = hw_info; - } +void SotaUptaneClient::reportHwInfo(const Json::Value &custom_hwinfo) { + Json::Value system_info; + + if (custom_hwinfo.empty()) { + system_info = Utils::getHardwareInfo(); + if (system_info.empty()) { + LOG_WARNING << "Unable to fetch hardware information from host system."; + return; + } + } + + const Json::Value &hw_info = custom_hwinfo.empty() ? system_info : custom_hwinfo; + if (hw_info != last_hw_info_reported) { + if (http->put(config.tls.server + "/system_info", hw_info).isOk()) { + last_hw_info_reported = hw_info; } - } else { - LOG_WARNING << "Unable to fetch hardware information from host system."; } } @@ -747,8 +753,8 @@ bool SotaUptaneClient::uptaneOfflineIteration(std::vector *targe return true; } -void SotaUptaneClient::sendDeviceData() { - reportHwInfo(); +void SotaUptaneClient::sendDeviceData(const Json::Value &custom_hwinfo) { + reportHwInfo(custom_hwinfo); reportInstalledPackages(); reportNetworkInfo(); reportAktualizrConfiguration(); diff --git a/src/libaktualizr/primary/sotauptaneclient.h b/src/libaktualizr/primary/sotauptaneclient.h index 16d6330511..38be19dc9f 100644 --- a/src/libaktualizr/primary/sotauptaneclient.h +++ b/src/libaktualizr/primary/sotauptaneclient.h @@ -60,7 +60,7 @@ class SotaUptaneClient { const api::FlowControlToken *token = nullptr); void reportPause(); void reportResume(); - void sendDeviceData(); + void sendDeviceData(const Json::Value &custom_hwinfo = Json::nullValue); result::UpdateCheck fetchMeta(); bool putManifest(const Json::Value &custom = Json::nullValue); result::UpdateCheck checkUpdates(); @@ -124,7 +124,7 @@ class SotaUptaneClient { const Uptane::EcuSerial &ecu_id); data::InstallationResult PackageInstallSetResult(const Uptane::Target &target); void finalizeAfterReboot(); - void reportHwInfo(); + void reportHwInfo(const Json::Value &custom_hwinfo); void reportInstalledPackages(); void reportNetworkInfo(); void reportAktualizrConfiguration(); From c47c96cbd1065c9a5e836e80f691f57645048279 Mon Sep 17 00:00:00 2001 From: Eugene Smirnov Date: Wed, 15 Apr 2020 18:13:14 +0200 Subject: [PATCH 2/3] aktualizr_primary: add cmdline param for custom hwinfo file Signed-off-by: Eugene Smirnov --- src/aktualizr_primary/main.cc | 19 +++++++++++++++---- src/libaktualizr/primary/aktualizr.cc | 6 +++--- src/libaktualizr/primary/aktualizr.h | 3 ++- src/libaktualizr/primary/sotauptaneclient.cc | 2 +- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/aktualizr_primary/main.cc b/src/aktualizr_primary/main.cc index fb78a0fdc4..436ed459f2 100644 --- a/src/aktualizr_primary/main.cc +++ b/src/aktualizr_primary/main.cc @@ -44,7 +44,8 @@ bpo::variables_map parseOptions(int argc, char *argv[]) { ("primary-ecu-serial", bpo::value(), "serial number of Primary ECU") ("primary-ecu-hardware-id", bpo::value(), "hardware ID of Primary ECU") ("secondary-config-file", bpo::value(), "secondary ECUs configuration file") - ("campaign-id", bpo::value(), "ID of the campaign to act on"); + ("campaign-id", bpo::value(), "ID of the campaign to act on") + ("hwinfo-file", bpo::value(), "custom hardware information JSON file"); // clang-format on // consider the first positional argument as the aktualizr run mode @@ -147,6 +148,16 @@ int main(int argc, char *argv[]) { SigHandler::signal(SIGINT); SigHandler::signal(SIGTERM); + Json::Value hwinfo; + if (commandline_map.count("hwinfo-file") != 0) { + auto file = commandline_map["hwinfo-file"].as(); + hwinfo = Utils::parseJSONFile(file); + if (hwinfo.empty()) { + LOG_ERROR << file << " is not a valid JSON file"; + return EXIT_FAILURE; + } + } + std::string run_mode; if (commandline_map.count("run-mode") != 0) { run_mode = commandline_map["run-mode"].as(); @@ -161,7 +172,7 @@ int main(int argc, char *argv[]) { aktualizr.CampaignControl(commandline_map["campaign-id"].as(), campaign::cmdFromName(run_mode)) .get(); } else if (run_mode == "check") { - aktualizr.SendDeviceData().get(); + aktualizr.SendDeviceData(hwinfo).get(); aktualizr.CheckUpdates().get(); } else if (run_mode == "download") { result::UpdateCheck update_result = aktualizr.CheckUpdates().get(); @@ -170,14 +181,14 @@ int main(int argc, char *argv[]) { result::UpdateCheck update_result = aktualizr.CheckUpdates().get(); aktualizr.Install(update_result.updates).get(); } else if (run_mode == "once") { - aktualizr.SendDeviceData().get(); + aktualizr.SendDeviceData(hwinfo).get(); aktualizr.UptaneCycle(); } else { boost::signals2::connection ac_conn = aktualizr.SetSignalHandler(std::bind(targets_autoclean_cb, std::ref(aktualizr), std::placeholders::_1)); try { - aktualizr.RunForever().get(); + aktualizr.RunForever(hwinfo).get(); } catch (const std::exception &ex) { LOG_ERROR << ex.what(); } diff --git a/src/libaktualizr/primary/aktualizr.cc b/src/libaktualizr/primary/aktualizr.cc index 271dac0c00..6e4ada9bb5 100644 --- a/src/libaktualizr/primary/aktualizr.cc +++ b/src/libaktualizr/primary/aktualizr.cc @@ -69,9 +69,9 @@ bool Aktualizr::UptaneCycle() { return true; } -std::future Aktualizr::RunForever() { - std::future future = std::async(std::launch::async, [&]() { - SendDeviceData().get(); +std::future Aktualizr::RunForever(const Json::Value &custom_hwinfo) { + std::future future = std::async(std::launch::async, [this, custom_hwinfo]() { + SendDeviceData(custom_hwinfo).get(); std::unique_lock l(exit_cond_.m); while (true) { diff --git a/src/libaktualizr/primary/aktualizr.h b/src/libaktualizr/primary/aktualizr.h index 3100e52b80..bd90b59a53 100644 --- a/src/libaktualizr/primary/aktualizr.h +++ b/src/libaktualizr/primary/aktualizr.h @@ -39,9 +39,10 @@ class Aktualizr { /** * Asynchronously run aktualizr indefinitely until Shutdown is called. + * @param custom_hwinfo if not empty will be sent to the backend instead of `lshw` output * @return Empty std::future object */ - std::future RunForever(); + std::future RunForever(const Json::Value& custom_hwinfo = Json::nullValue); /** * Shuts down currently running `RunForever()` method diff --git a/src/libaktualizr/primary/sotauptaneclient.cc b/src/libaktualizr/primary/sotauptaneclient.cc index 34c5c1d00d..906f437416 100644 --- a/src/libaktualizr/primary/sotauptaneclient.cc +++ b/src/libaktualizr/primary/sotauptaneclient.cc @@ -162,7 +162,7 @@ data::InstallationResult SotaUptaneClient::PackageInstallSetResult(const Uptane: void SotaUptaneClient::reportHwInfo(const Json::Value &custom_hwinfo) { Json::Value system_info; - + if (custom_hwinfo.empty()) { system_info = Utils::getHardwareInfo(); if (system_info.empty()) { From 3b5ff5263c187341ad28ea0ff18582b499520c46 Mon Sep 17 00:00:00 2001 From: Eugene Smirnov Date: Fri, 17 Apr 2020 14:52:02 +0200 Subject: [PATCH 3/3] Update changelog Signed-off-by: Eugene Smirnov --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b813bf5739..fb53a3ddb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ Our versioning scheme is `YEAR.N` where `N` is incremented whenever a new releas ## [??? (unreleased)] +### Added + +- libaktualizr API and aktualizr-primary command line parameter to provide custom hardware information in JSON format: [PR](https://github.com/advancedtelematic/aktualizr/pull/1644) + ### Changed - Improved garage-deploy object fetching performance by reusing the curl handle: [PR](https://github.com/advancedtelematic/aktualizr/pull/1643)