Skip to content
This repository was archived by the owner on May 21, 2024. It is now read-only.

Add ability to provide custom hardware information which will be visible in UI #1644

Merged
merged 3 commits into from
Apr 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
19 changes: 15 additions & 4 deletions src/aktualizr_primary/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ bpo::variables_map parseOptions(int argc, char *argv[]) {
("primary-ecu-serial", bpo::value<std::string>(), "serial number of Primary ECU")
("primary-ecu-hardware-id", bpo::value<std::string>(), "hardware ID of Primary ECU")
("secondary-config-file", bpo::value<boost::filesystem::path>(), "secondary ECUs configuration file")
("campaign-id", bpo::value<std::string>(), "ID of the campaign to act on");
("campaign-id", bpo::value<std::string>(), "ID of the campaign to act on")
("hwinfo-file", bpo::value<boost::filesystem::path>(), "custom hardware information JSON file");
// clang-format on

// consider the first positional argument as the aktualizr run mode
Expand Down Expand Up @@ -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<boost::filesystem::path>();
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<std::string>();
Expand All @@ -161,7 +172,7 @@ int main(int argc, char *argv[]) {
aktualizr.CampaignControl(commandline_map["campaign-id"].as<std::string>(), 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();
Expand All @@ -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();
}
Expand Down
10 changes: 5 additions & 5 deletions src/libaktualizr/primary/aktualizr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ bool Aktualizr::UptaneCycle() {
return true;
}

std::future<void> Aktualizr::RunForever() {
std::future<void> future = std::async(std::launch::async, [&]() {
SendDeviceData().get();
std::future<void> Aktualizr::RunForever(const Json::Value &custom_hwinfo) {
std::future<void> future = std::async(std::launch::async, [this, custom_hwinfo]() {
SendDeviceData(custom_hwinfo).get();

std::unique_lock<std::mutex> l(exit_cond_.m);
while (true) {
Expand Down Expand Up @@ -136,8 +136,8 @@ std::future<void> Aktualizr::CampaignControl(const std::string &campaign_id, cam
return api_queue_.enqueue(task);
}

std::future<void> Aktualizr::SendDeviceData() {
std::function<void()> task([this] { uptane_client_->sendDeviceData(); });
std::future<void> Aktualizr::SendDeviceData(const Json::Value &custom_hwinfo) {
std::function<void()> task([this, custom_hwinfo] { uptane_client_->sendDeviceData(custom_hwinfo); });
return api_queue_.enqueue(task);
}

Expand Down
6 changes: 4 additions & 2 deletions src/libaktualizr/primary/aktualizr.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> RunForever();
std::future<void> RunForever(const Json::Value& custom_hwinfo = Json::nullValue);

/**
* Shuts down currently running `RunForever()` method
Expand Down Expand Up @@ -71,9 +72,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<void> SendDeviceData();
std::future<void> SendDeviceData(const Json::Value& custom_hwinfo = Json::nullValue);

/**
* Fetch Uptane metadata and check for updates.
Expand Down
49 changes: 49 additions & 0 deletions src/libaktualizr/primary/aktualizr_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<HttpSystemInfo>(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);
Expand Down
28 changes: 17 additions & 11 deletions src/libaktualizr/primary/sotauptaneclient.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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.";
}
}

Expand Down Expand Up @@ -747,8 +753,8 @@ bool SotaUptaneClient::uptaneOfflineIteration(std::vector<Uptane::Target> *targe
return true;
}

void SotaUptaneClient::sendDeviceData() {
reportHwInfo();
void SotaUptaneClient::sendDeviceData(const Json::Value &custom_hwinfo) {
reportHwInfo(custom_hwinfo);
reportInstalledPackages();
reportNetworkInfo();
reportAktualizrConfiguration();
Expand Down
4 changes: 2 additions & 2 deletions src/libaktualizr/primary/sotauptaneclient.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down