From 362f8247960d7c30c3df3a5316e44bc0fa0f6133 Mon Sep 17 00:00:00 2001 From: Ricardo Abreu Date: Fri, 5 Apr 2024 12:41:02 +0100 Subject: [PATCH 1/9] [daemon] Extract code to populate runtime vm info --- src/daemon/daemon.cpp | 55 +++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 24c25e1041..4e2bb3f299 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -1274,6 +1274,34 @@ void populate_snapshot_info(mp::VirtualMachine& vm, populate_snapshot_fundamentals(snapshot, fundamentals); } + +void populate_instance_runtime_info(mp::VirtualMachine& vm, + mp::DetailedInfoItem* info, + mp::InstanceDetails* instance_info, + const std::string& original_release) +{ + instance_info->set_load(vm.ssh_exec("cat /proc/loadavg | cut -d ' ' -f1-3")); + instance_info->set_memory_usage(vm.ssh_exec("free -b | grep 'Mem:' | awk '{printf $3}'")); + info->set_memory_total(vm.ssh_exec("free -b | grep 'Mem:' | awk '{printf $2}'")); + instance_info->set_disk_usage(vm.ssh_exec("df -t ext4 -t vfat --total -B1 --output=used | tail -n 1")); + info->set_disk_total(vm.ssh_exec("df -t ext4 -t vfat --total -B1 --output=size | tail -n 1")); + info->set_cpu_count(vm.ssh_exec("nproc")); + + std::string management_ip = vm.management_ipv4(); + auto all_ipv4 = vm.get_all_ipv4(); + + if (is_ipv4_valid(management_ip)) + instance_info->add_ipv4(management_ip); + else if (all_ipv4.empty()) + instance_info->add_ipv4("N/A"); + + for (const auto& extra_ipv4 : all_ipv4) + if (extra_ipv4 != management_ip) + instance_info->add_ipv4(extra_ipv4); + + auto current_release = vm.ssh_exec("cat /etc/os-release | grep 'PRETTY_NAME' | cut -d \\\" -f2"); + instance_info->set_current_release(!current_release.empty() ? current_release : original_release); +} } // namespace mp::Daemon::Daemon(std::unique_ptr the_config) @@ -3522,32 +3550,7 @@ void mp::Daemon::populate_instance_info(VirtualMachine& vm, timestamp->set_nanos(created_time.time().msec() * 1'000'000); if (!no_runtime_info && MP_UTILS.is_running(present_state)) - { - instance_info->set_load(vm.ssh_exec("cat /proc/loadavg | cut -d ' ' -f1-3")); - instance_info->set_memory_usage(vm.ssh_exec("free -b | grep 'Mem:' | awk '{printf $3}'")); - info->set_memory_total(vm.ssh_exec("free -b | grep 'Mem:' | awk '{printf $2}'")); - instance_info->set_disk_usage(vm.ssh_exec("df -t ext4 -t vfat --total -B1 --output=used | tail -n 1")); - info->set_disk_total(vm.ssh_exec("df -t ext4 -t vfat --total -B1 --output=size | tail -n 1")); - info->set_cpu_count(vm.ssh_exec("nproc")); - - std::string management_ip = vm.management_ipv4(); - auto all_ipv4 = vm.get_all_ipv4(); - - if (is_ipv4_valid(management_ip)) - instance_info->add_ipv4(management_ip); - else if (all_ipv4.empty()) - instance_info->add_ipv4("N/A"); - - for (const auto& extra_ipv4 : all_ipv4) - if (extra_ipv4 != management_ip) - instance_info->add_ipv4(extra_ipv4); - - auto current_release = vm.ssh_exec("cat /etc/os-release | grep 'PRETTY_NAME' | cut -d \\\" -f2"); - instance_info->set_current_release(!current_release.empty() ? current_release : original_release); - - instance_info->set_cpu_times(vm.ssh_exec("head -n1 /proc/stat")); - instance_info->set_uptime(vm.ssh_exec("uptime -p | tail -c+4")); - } + populate_instance_runtime_info(vm, info, instance_info, original_release); } bool mp::Daemon::is_bridged(const std::string& instance_name) From d9eea94ab9ec9dc9ec7ba4daf0777b161a928e8a Mon Sep 17 00:00:00 2001 From: Ricardo Abreu Date: Fri, 5 Apr 2024 12:55:20 +0100 Subject: [PATCH 2/9] [daemon] Extract info commands into constants --- src/daemon/daemon.cpp | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 4e2bb3f299..c90fc96358 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -1280,12 +1280,23 @@ void populate_instance_runtime_info(mp::VirtualMachine& vm, mp::InstanceDetails* instance_info, const std::string& original_release) { - instance_info->set_load(vm.ssh_exec("cat /proc/loadavg | cut -d ' ' -f1-3")); - instance_info->set_memory_usage(vm.ssh_exec("free -b | grep 'Mem:' | awk '{printf $3}'")); - info->set_memory_total(vm.ssh_exec("free -b | grep 'Mem:' | awk '{printf $2}'")); - instance_info->set_disk_usage(vm.ssh_exec("df -t ext4 -t vfat --total -B1 --output=used | tail -n 1")); - info->set_disk_total(vm.ssh_exec("df -t ext4 -t vfat --total -B1 --output=size | tail -n 1")); - info->set_cpu_count(vm.ssh_exec("nproc")); + static constexpr auto loadavg_cmd = "cat /proc/loadavg | cut -d ' ' -f1-3"; + static constexpr auto mem_usage_cmd = "free -b | grep 'Mem:' | awk '{printf $3}'"; + static constexpr auto mem_total_cmd = "free -b | grep 'Mem:' | awk '{printf $2}'"; + static constexpr auto disk_usage_cmd = "df -t ext4 -t vfat --total -B1 --output=used | tail -n 1"; + static constexpr auto disk_total_cmd = "df -t ext4 -t vfat --total -B1 --output=size | tail -n 1"; + static constexpr auto cpus_cmd = "nproc"; + static constexpr auto current_release_cmd = "cat /etc/os-release | grep 'PRETTY_NAME' | cut -d \\\" -f2"; + + instance_info->set_load(vm.ssh_exec(loadavg_cmd)); + instance_info->set_memory_usage(vm.ssh_exec(mem_usage_cmd)); + info->set_memory_total(vm.ssh_exec(mem_total_cmd)); + instance_info->set_disk_usage(vm.ssh_exec(disk_usage_cmd)); + info->set_disk_total(vm.ssh_exec(disk_total_cmd)); + info->set_cpu_count(vm.ssh_exec(cpus_cmd)); + + auto current_release = vm.ssh_exec(current_release_cmd); + instance_info->set_current_release(!current_release.empty() ? current_release : original_release); std::string management_ip = vm.management_ipv4(); auto all_ipv4 = vm.get_all_ipv4(); @@ -1298,9 +1309,6 @@ void populate_instance_runtime_info(mp::VirtualMachine& vm, for (const auto& extra_ipv4 : all_ipv4) if (extra_ipv4 != management_ip) instance_info->add_ipv4(extra_ipv4); - - auto current_release = vm.ssh_exec("cat /etc/os-release | grep 'PRETTY_NAME' | cut -d \\\" -f2"); - instance_info->set_current_release(!current_release.empty() ? current_release : original_release); } } // namespace From 580eb31bfc0d191a1ca71b3e54dc3e48e4e49dfb Mon Sep 17 00:00:00 2001 From: Ricardo Abreu Date: Fri, 5 Apr 2024 19:26:20 +0100 Subject: [PATCH 3/9] [daemon] Use single composite cmd for runtime info Use a single, composite, and sequential command to obtain all the runtime info we need from the VM over SSH. Structure the code such that it can be reused for a parallel command. --- src/daemon/daemon.cpp | 56 ++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index c90fc96358..4b40fc333c 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -1280,22 +1280,46 @@ void populate_instance_runtime_info(mp::VirtualMachine& vm, mp::InstanceDetails* instance_info, const std::string& original_release) { - static constexpr auto loadavg_cmd = "cat /proc/loadavg | cut -d ' ' -f1-3"; - static constexpr auto mem_usage_cmd = "free -b | grep 'Mem:' | awk '{printf $3}'"; - static constexpr auto mem_total_cmd = "free -b | grep 'Mem:' | awk '{printf $2}'"; - static constexpr auto disk_usage_cmd = "df -t ext4 -t vfat --total -B1 --output=used | tail -n 1"; - static constexpr auto disk_total_cmd = "df -t ext4 -t vfat --total -B1 --output=size | tail -n 1"; - static constexpr auto cpus_cmd = "nproc"; - static constexpr auto current_release_cmd = "cat /etc/os-release | grep 'PRETTY_NAME' | cut -d \\\" -f2"; - - instance_info->set_load(vm.ssh_exec(loadavg_cmd)); - instance_info->set_memory_usage(vm.ssh_exec(mem_usage_cmd)); - info->set_memory_total(vm.ssh_exec(mem_total_cmd)); - instance_info->set_disk_usage(vm.ssh_exec(disk_usage_cmd)); - info->set_disk_total(vm.ssh_exec(disk_total_cmd)); - info->set_cpu_count(vm.ssh_exec(cpus_cmd)); - - auto current_release = vm.ssh_exec(current_release_cmd); + static constexpr auto loadavg_key = "loadavg"; + static constexpr auto mem_usage_key = "mem_usage"; + static constexpr auto mem_total_key = "mem_total"; + static constexpr auto disk_usage_key = "disk_usage"; + static constexpr auto disk_total_key = "disk_total"; + static constexpr auto cpus_key = "cpus"; + static constexpr auto current_release_key = "current_release"; + + static constexpr std::array key_cmds_pairs{ + std::pair{loadavg_key, "cat /proc/loadavg | cut -d ' ' -f1-3"}, + std::pair{mem_usage_key, R"(free -b | grep 'Mem:' | awk '{printf \$3}')"}, + std::pair{mem_total_key, R"(free -b | grep 'Mem:' | awk '{printf \$2}')"}, + std::pair{disk_usage_key, "df -t ext4 -t vfat --total -B1 --output=used | tail -n 1"}, + std::pair{disk_total_key, "df -t ext4 -t vfat --total -B1 --output=size | tail -n 1"}, + std::pair{cpus_key, "nproc"}, + std::pair{current_release_key, R"(cat /etc/os-release | grep 'PRETTY_NAME' | cut -d \\\" -f2)"}}; + + static const auto cmds = [] { + constexpr auto n = key_cmds_pairs.size(); + std::array ret; + for (std::size_t i = 0; i < n; ++i) + { + const auto [key, cmd] = key_cmds_pairs[i]; + ret[i] = fmt::format(R"(echo {}: \"$(eval "{}")\")", key, cmd); + } + + return ret; + }(); + + static const auto sequential_composite_cmd = fmt::to_string(fmt::join(cmds, "; ")); + auto results = YAML::Load(vm.ssh_exec(sequential_composite_cmd)); + + instance_info->set_load(results[loadavg_key].as()); + instance_info->set_memory_usage(results[mem_usage_key].as()); + info->set_memory_total(results[mem_total_key].as()); + instance_info->set_disk_usage(results[disk_usage_key].as()); + info->set_disk_total(results[disk_total_key].as()); + info->set_cpu_count(results[cpus_key].as()); + + auto current_release = results[current_release_key].as(); instance_info->set_current_release(!current_release.empty() ? current_release : original_release); std::string management_ip = vm.management_ipv4(); From 47b646d30570ebec275600fa530d78ca6f5ca4c6 Mon Sep 17 00:00:00 2001 From: Ricardo Abreu Date: Fri, 5 Apr 2024 19:33:35 +0100 Subject: [PATCH 4/9] [daemon] Use parallel command on multi-core VMs Use a parallel single composite command to obtain runtime information from VMs that have more than one CPU. Continue using the sequential version for VMs with a single CPU. --- src/daemon/daemon.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 4b40fc333c..8622de83be 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -1278,7 +1278,8 @@ void populate_snapshot_info(mp::VirtualMachine& vm, void populate_instance_runtime_info(mp::VirtualMachine& vm, mp::DetailedInfoItem* info, mp::InstanceDetails* instance_info, - const std::string& original_release) + const std::string& original_release, + bool parallelize) { static constexpr auto loadavg_key = "loadavg"; static constexpr auto mem_usage_key = "mem_usage"; @@ -1310,7 +1311,9 @@ void populate_instance_runtime_info(mp::VirtualMachine& vm, }(); static const auto sequential_composite_cmd = fmt::to_string(fmt::join(cmds, "; ")); - auto results = YAML::Load(vm.ssh_exec(sequential_composite_cmd)); + static const auto parallel_composite_cmd = fmt::format("{} & wait", fmt::join(cmds, "& ")); + + auto results = YAML::Load(vm.ssh_exec(parallelize ? parallel_composite_cmd : sequential_composite_cmd)); instance_info->set_load(results[loadavg_key].as()); instance_info->set_memory_usage(results[mem_usage_key].as()); @@ -3582,7 +3585,7 @@ void mp::Daemon::populate_instance_info(VirtualMachine& vm, timestamp->set_nanos(created_time.time().msec() * 1'000'000); if (!no_runtime_info && MP_UTILS.is_running(present_state)) - populate_instance_runtime_info(vm, info, instance_info, original_release); + populate_instance_runtime_info(vm, info, instance_info, original_release, vm_specs.num_cores == 1); } bool mp::Daemon::is_bridged(const std::string& instance_name) From 1cd5d512e8c320deafe6751819ac61aada769832 Mon Sep 17 00:00:00 2001 From: Ricardo Abreu Date: Fri, 5 Apr 2024 22:51:34 +0100 Subject: [PATCH 5/9] [daemon] Organize code to fetch runtime info --- src/daemon/daemon.cpp | 65 ++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 8622de83be..ef667ebb37 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -1275,12 +1275,9 @@ void populate_snapshot_info(mp::VirtualMachine& vm, populate_snapshot_fundamentals(snapshot, fundamentals); } -void populate_instance_runtime_info(mp::VirtualMachine& vm, - mp::DetailedInfoItem* info, - mp::InstanceDetails* instance_info, - const std::string& original_release, - bool parallelize) +struct RuntimeInfoKeys { +public: static constexpr auto loadavg_key = "loadavg"; static constexpr auto mem_usage_key = "mem_usage"; static constexpr auto mem_total_key = "mem_total"; @@ -1288,41 +1285,59 @@ void populate_instance_runtime_info(mp::VirtualMachine& vm, static constexpr auto disk_total_key = "disk_total"; static constexpr auto cpus_key = "cpus"; static constexpr auto current_release_key = "current_release"; +}; +struct RuntimeInfoCmds +{ +private: + using Keys = RuntimeInfoKeys; + static constexpr auto key_val_cmd = R"(echo {}: \"$(eval "{}")\")"; static constexpr std::array key_cmds_pairs{ - std::pair{loadavg_key, "cat /proc/loadavg | cut -d ' ' -f1-3"}, - std::pair{mem_usage_key, R"(free -b | grep 'Mem:' | awk '{printf \$3}')"}, - std::pair{mem_total_key, R"(free -b | grep 'Mem:' | awk '{printf \$2}')"}, - std::pair{disk_usage_key, "df -t ext4 -t vfat --total -B1 --output=used | tail -n 1"}, - std::pair{disk_total_key, "df -t ext4 -t vfat --total -B1 --output=size | tail -n 1"}, - std::pair{cpus_key, "nproc"}, - std::pair{current_release_key, R"(cat /etc/os-release | grep 'PRETTY_NAME' | cut -d \\\" -f2)"}}; - - static const auto cmds = [] { + std::pair{Keys::loadavg_key, "cat /proc/loadavg | cut -d ' ' -f1-3"}, + std::pair{Keys::mem_usage_key, R"(free -b | grep 'Mem:' | awk '{printf \$3}')"}, + std::pair{Keys::mem_total_key, R"(free -b | grep 'Mem:' | awk '{printf \$2}')"}, + std::pair{Keys::disk_usage_key, "df -t ext4 -t vfat --total -B1 --output=used | tail -n 1"}, + std::pair{Keys::disk_total_key, "df -t ext4 -t vfat --total -B1 --output=size | tail -n 1"}, + std::pair{Keys::cpus_key, "nproc"}, + std::pair{Keys::current_release_key, R"(cat /etc/os-release | grep 'PRETTY_NAME' | cut -d \\\" -f2)"}}; + + inline static const std::array cmds = [] { constexpr auto n = key_cmds_pairs.size(); std::array ret; for (std::size_t i = 0; i < n; ++i) { const auto [key, cmd] = key_cmds_pairs[i]; - ret[i] = fmt::format(R"(echo {}: \"$(eval "{}")\")", key, cmd); + ret[i] = fmt::format(key_val_cmd, key, cmd); } return ret; }(); - static const auto sequential_composite_cmd = fmt::to_string(fmt::join(cmds, "; ")); - static const auto parallel_composite_cmd = fmt::format("{} & wait", fmt::join(cmds, "& ")); +public: + inline static const std::string sequential_composite_cmd = fmt::to_string(fmt::join(cmds, "; ")); + inline static const std::string parallel_composite_cmd = fmt::format("{} & wait", fmt::join(cmds, "& ")); +}; + +void populate_instance_runtime_info(mp::VirtualMachine& vm, + mp::DetailedInfoItem* info, + mp::InstanceDetails* instance_info, + const std::string& original_release, + bool parallelize) +{ + using Keys = RuntimeInfoKeys; + using Cmds = RuntimeInfoCmds; - auto results = YAML::Load(vm.ssh_exec(parallelize ? parallel_composite_cmd : sequential_composite_cmd)); + const auto& cmd = parallelize ? Cmds::parallel_composite_cmd : Cmds::sequential_composite_cmd; + auto results = YAML::Load(vm.ssh_exec(cmd)); - instance_info->set_load(results[loadavg_key].as()); - instance_info->set_memory_usage(results[mem_usage_key].as()); - info->set_memory_total(results[mem_total_key].as()); - instance_info->set_disk_usage(results[disk_usage_key].as()); - info->set_disk_total(results[disk_total_key].as()); - info->set_cpu_count(results[cpus_key].as()); + instance_info->set_load(results[Keys::loadavg_key].as()); + instance_info->set_memory_usage(results[Keys::mem_usage_key].as()); + info->set_memory_total(results[Keys::mem_total_key].as()); + instance_info->set_disk_usage(results[Keys::disk_usage_key].as()); + info->set_disk_total(results[Keys::disk_total_key].as()); + info->set_cpu_count(results[Keys::cpus_key].as()); - auto current_release = results[current_release_key].as(); + auto current_release = results[Keys::current_release_key].as(); instance_info->set_current_release(!current_release.empty() ? current_release : original_release); std::string management_ip = vm.management_ipv4(); From 579093399970349372e2bbb123030119ef1ba3e0 Mon Sep 17 00:00:00 2001 From: Ricardo Abreu Date: Fri, 5 Apr 2024 22:58:02 +0100 Subject: [PATCH 6/9] [daemon] Add RuntimeInstanceInfoHelper Add a placeholder helper class meant to house the code to populate runtime instance info, with corresponding header and compilation unit, to avoid polluting the unnamed namespace in the daemon compilation unit any further. --- src/daemon/CMakeLists.txt | 1 + src/daemon/runtime_instance_info_helper.cpp | 18 ++++++++++ src/daemon/runtime_instance_info_helper.h | 40 +++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 src/daemon/runtime_instance_info_helper.cpp create mode 100644 src/daemon/runtime_instance_info_helper.h diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index cd55592620..826c91895e 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -24,6 +24,7 @@ add_library(daemon STATIC daemon_rpc.cpp default_vm_image_vault.cpp instance_settings_handler.cpp + runtime_instance_info_helper.cpp snapshot_settings_handler.cpp ubuntu_image_host.cpp) diff --git a/src/daemon/runtime_instance_info_helper.cpp b/src/daemon/runtime_instance_info_helper.cpp new file mode 100644 index 0000000000..2cd63e078f --- /dev/null +++ b/src/daemon/runtime_instance_info_helper.cpp @@ -0,0 +1,18 @@ +/* + * Copyright (C) Canonical, Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "runtime_instance_info_helper.h" diff --git a/src/daemon/runtime_instance_info_helper.h b/src/daemon/runtime_instance_info_helper.h new file mode 100644 index 0000000000..e81fc56fc6 --- /dev/null +++ b/src/daemon/runtime_instance_info_helper.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) Canonical, Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef MULTIPASS_RUNTIME_INSTANCE_INFO_HELPER_H +#define MULTIPASS_RUNTIME_INSTANCE_INFO_HELPER_H + +#include + +namespace multipass +{ +class VirtualMachine; +class DetailedInfoItem; +class InstanceDetails; + +struct RuntimeInstanceInfoHelper +{ + static void populateRuntimeInstanceInfo(VirtualMachine& vm, + DetailedInfoItem* info, + InstanceDetails* instance_info, + const std::string& original_release, + bool parallelize); +}; + +} // namespace multipass + +#endif // MULTIPASS_RUNTIME_INSTANCE_INFO_HELPER_H From e8a50732a71e86e36174c190bd45d86a159ea92d Mon Sep 17 00:00:00 2001 From: Ricardo Abreu Date: Fri, 5 Apr 2024 23:14:42 +0100 Subject: [PATCH 7/9] [utils] Publish utility to check validity of IPv4 Move utility to verify the validity of IPv4 addresses to the generally accessible Utils singleton, in preparation for its use in the RuntimeInstanceInfoHelper class. --- include/multipass/utils.h | 1 + src/daemon/daemon.cpp | 18 ++---------------- src/utils/utils.cpp | 16 +++++++++++++++- tests/mock_utils.h | 1 + 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/include/multipass/utils.h b/include/multipass/utils.h index f732556b94..2225743acd 100644 --- a/include/multipass/utils.h +++ b/include/multipass/utils.h @@ -229,6 +229,7 @@ class Utils : public Singleton virtual std::vector random_bytes(size_t len); virtual QString make_uuid(const std::optional& seed = std::nullopt) const; virtual void sleep_for(const std::chrono::milliseconds& ms) const; + virtual bool is_ipv4_valid(const std::string& ipv4) const; }; } // namespace multipass diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index ef667ebb37..ce7c362ed4 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -1030,20 +1030,6 @@ std::string generate_unused_mac_address(std::unordered_set& s) max_tries, s.size())}; } -bool is_ipv4_valid(const std::string& ipv4) -{ - try - { - (mp::IPAddress(ipv4)); - } - catch (std::invalid_argument&) - { - return false; - } - - return true; -} - struct SnapshotPick { std::unordered_set pick; @@ -1343,7 +1329,7 @@ void populate_instance_runtime_info(mp::VirtualMachine& vm, std::string management_ip = vm.management_ipv4(); auto all_ipv4 = vm.get_all_ipv4(); - if (is_ipv4_valid(management_ip)) + if (MP_UTILS.is_ipv4_valid(management_ip)) instance_info->add_ipv4(management_ip); else if (all_ipv4.empty()) instance_info->add_ipv4("N/A"); @@ -1880,7 +1866,7 @@ try // clang-format on std::string management_ip = vm.management_ipv4(); auto all_ipv4 = vm.get_all_ipv4(); - if (is_ipv4_valid(management_ip)) + if (MP_UTILS.is_ipv4_valid(management_ip)) entry->add_ipv4(management_ip); else if (all_ipv4.empty()) entry->add_ipv4("N/A"); diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp index cbaa044c95..1193f86f05 100644 --- a/src/utils/utils.cpp +++ b/src/utils/utils.cpp @@ -555,7 +555,21 @@ mp::Path mp::Utils::derive_instances_dir(const mp::Path& data_dir, return QDir(QDir(data_dir).filePath(backend_directory_name)).filePath(instances_subdir); } -void multipass::Utils::sleep_for(const std::chrono::milliseconds& ms) const +void mp::Utils::sleep_for(const std::chrono::milliseconds& ms) const { std::this_thread::sleep_for(ms); } + +bool mp::Utils::is_ipv4_valid(const std::string& ipv4) const +{ + try + { + (mp::IPAddress(ipv4)); + } + catch (std::invalid_argument&) + { + return false; + } + + return true; +} diff --git a/tests/mock_utils.h b/tests/mock_utils.h index 300575b526..14656e188e 100644 --- a/tests/mock_utils.h +++ b/tests/mock_utils.h @@ -48,6 +48,7 @@ class MockUtils : public Utils MOCK_METHOD(std::string, run_in_ssh_session, (SSHSession & session, const std::string& cmd), (const, override)); MOCK_METHOD(QString, make_uuid, (const std::optional&), (const, override)); MOCK_METHOD(void, sleep_for, (const std::chrono::milliseconds&), (const, override)); + MOCK_METHOD(bool, is_ipv4_valid, (const std::string& ipv4), (const, override)); MP_MOCK_SINGLETON_BOILERPLATE(MockUtils, Utils); }; From 88654f2445a42df11cedfec70e3dc0179cb3ef56 Mon Sep 17 00:00:00 2001 From: Ricardo Abreu Date: Fri, 5 Apr 2024 23:24:19 +0100 Subject: [PATCH 8/9] [runtime-info] Move population to helper --- src/daemon/daemon.cpp | 85 ++----------------- src/daemon/runtime_instance_info_helper.cpp | 93 +++++++++++++++++++++ src/daemon/runtime_instance_info_helper.h | 11 +-- 3 files changed, 105 insertions(+), 84 deletions(-) diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index ce7c362ed4..fd85b33684 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -18,6 +18,7 @@ #include "daemon.h" #include "base_cloud_init_config.h" #include "instance_settings_handler.h" +#include "runtime_instance_info_helper.h" #include "snapshot_settings_handler.h" #include @@ -1260,84 +1261,6 @@ void populate_snapshot_info(mp::VirtualMachine& vm, populate_snapshot_fundamentals(snapshot, fundamentals); } - -struct RuntimeInfoKeys -{ -public: - static constexpr auto loadavg_key = "loadavg"; - static constexpr auto mem_usage_key = "mem_usage"; - static constexpr auto mem_total_key = "mem_total"; - static constexpr auto disk_usage_key = "disk_usage"; - static constexpr auto disk_total_key = "disk_total"; - static constexpr auto cpus_key = "cpus"; - static constexpr auto current_release_key = "current_release"; -}; - -struct RuntimeInfoCmds -{ -private: - using Keys = RuntimeInfoKeys; - static constexpr auto key_val_cmd = R"(echo {}: \"$(eval "{}")\")"; - static constexpr std::array key_cmds_pairs{ - std::pair{Keys::loadavg_key, "cat /proc/loadavg | cut -d ' ' -f1-3"}, - std::pair{Keys::mem_usage_key, R"(free -b | grep 'Mem:' | awk '{printf \$3}')"}, - std::pair{Keys::mem_total_key, R"(free -b | grep 'Mem:' | awk '{printf \$2}')"}, - std::pair{Keys::disk_usage_key, "df -t ext4 -t vfat --total -B1 --output=used | tail -n 1"}, - std::pair{Keys::disk_total_key, "df -t ext4 -t vfat --total -B1 --output=size | tail -n 1"}, - std::pair{Keys::cpus_key, "nproc"}, - std::pair{Keys::current_release_key, R"(cat /etc/os-release | grep 'PRETTY_NAME' | cut -d \\\" -f2)"}}; - - inline static const std::array cmds = [] { - constexpr auto n = key_cmds_pairs.size(); - std::array ret; - for (std::size_t i = 0; i < n; ++i) - { - const auto [key, cmd] = key_cmds_pairs[i]; - ret[i] = fmt::format(key_val_cmd, key, cmd); - } - - return ret; - }(); - -public: - inline static const std::string sequential_composite_cmd = fmt::to_string(fmt::join(cmds, "; ")); - inline static const std::string parallel_composite_cmd = fmt::format("{} & wait", fmt::join(cmds, "& ")); -}; - -void populate_instance_runtime_info(mp::VirtualMachine& vm, - mp::DetailedInfoItem* info, - mp::InstanceDetails* instance_info, - const std::string& original_release, - bool parallelize) -{ - using Keys = RuntimeInfoKeys; - using Cmds = RuntimeInfoCmds; - - const auto& cmd = parallelize ? Cmds::parallel_composite_cmd : Cmds::sequential_composite_cmd; - auto results = YAML::Load(vm.ssh_exec(cmd)); - - instance_info->set_load(results[Keys::loadavg_key].as()); - instance_info->set_memory_usage(results[Keys::mem_usage_key].as()); - info->set_memory_total(results[Keys::mem_total_key].as()); - instance_info->set_disk_usage(results[Keys::disk_usage_key].as()); - info->set_disk_total(results[Keys::disk_total_key].as()); - info->set_cpu_count(results[Keys::cpus_key].as()); - - auto current_release = results[Keys::current_release_key].as(); - instance_info->set_current_release(!current_release.empty() ? current_release : original_release); - - std::string management_ip = vm.management_ipv4(); - auto all_ipv4 = vm.get_all_ipv4(); - - if (MP_UTILS.is_ipv4_valid(management_ip)) - instance_info->add_ipv4(management_ip); - else if (all_ipv4.empty()) - instance_info->add_ipv4("N/A"); - - for (const auto& extra_ipv4 : all_ipv4) - if (extra_ipv4 != management_ip) - instance_info->add_ipv4(extra_ipv4); -} } // namespace mp::Daemon::Daemon(std::unique_ptr the_config) @@ -3586,7 +3509,11 @@ void mp::Daemon::populate_instance_info(VirtualMachine& vm, timestamp->set_nanos(created_time.time().msec() * 1'000'000); if (!no_runtime_info && MP_UTILS.is_running(present_state)) - populate_instance_runtime_info(vm, info, instance_info, original_release, vm_specs.num_cores == 1); + RuntimeInstanceInfoHelper::populate_runtime_info(vm, + info, + instance_info, + original_release, + vm_specs.num_cores == 1); } bool mp::Daemon::is_bridged(const std::string& instance_name) diff --git a/src/daemon/runtime_instance_info_helper.cpp b/src/daemon/runtime_instance_info_helper.cpp index 2cd63e078f..e595655c8c 100644 --- a/src/daemon/runtime_instance_info_helper.cpp +++ b/src/daemon/runtime_instance_info_helper.cpp @@ -16,3 +16,96 @@ */ #include "runtime_instance_info_helper.h" + +#include +#include +#include +#include + +#include + +#include + +namespace mp = multipass; + +namespace +{ + +struct RuntimeInfoKeys +{ +public: + static constexpr auto loadavg_key = "loadavg"; + static constexpr auto mem_usage_key = "mem_usage"; + static constexpr auto mem_total_key = "mem_total"; + static constexpr auto disk_usage_key = "disk_usage"; + static constexpr auto disk_total_key = "disk_total"; + static constexpr auto cpus_key = "cpus"; + static constexpr auto current_release_key = "current_release"; +}; + +struct RuntimeInfoCmds +{ +private: + using Keys = RuntimeInfoKeys; + static constexpr auto key_val_cmd = R"(echo {}: \"$(eval "{}")\")"; + static constexpr std::array key_cmds_pairs{ + std::pair{Keys::loadavg_key, "cat /proc/loadavg | cut -d ' ' -f1-3"}, + std::pair{Keys::mem_usage_key, R"(free -b | grep 'Mem:' | awk '{printf \$3}')"}, + std::pair{Keys::mem_total_key, R"(free -b | grep 'Mem:' | awk '{printf \$2}')"}, + std::pair{Keys::disk_usage_key, "df -t ext4 -t vfat --total -B1 --output=used | tail -n 1"}, + std::pair{Keys::disk_total_key, "df -t ext4 -t vfat --total -B1 --output=size | tail -n 1"}, + std::pair{Keys::cpus_key, "nproc"}, + std::pair{Keys::current_release_key, R"(cat /etc/os-release | grep 'PRETTY_NAME' | cut -d \\\" -f2)"}}; + + inline static const std::array cmds = [] { + constexpr auto n = key_cmds_pairs.size(); + std::array ret; + for (std::size_t i = 0; i < n; ++i) + { + const auto [key, cmd] = key_cmds_pairs[i]; + ret[i] = fmt::format(key_val_cmd, key, cmd); + } + + return ret; + }(); + +public: + inline static const std::string sequential_composite_cmd = fmt::to_string(fmt::join(cmds, "; ")); + inline static const std::string parallel_composite_cmd = fmt::format("{} & wait", fmt::join(cmds, "& ")); +}; +} // namespace + +void mp::RuntimeInstanceInfoHelper::populate_runtime_info(mp::VirtualMachine& vm, + mp::DetailedInfoItem* info, + mp::InstanceDetails* instance_info, + const std::string& original_release, + bool parallelize) +{ + using Keys = RuntimeInfoKeys; // TODO@ricab just rename the types now + using Cmds = RuntimeInfoCmds; + + const auto& cmd = parallelize ? Cmds::parallel_composite_cmd : Cmds::sequential_composite_cmd; + auto results = YAML::Load(vm.ssh_exec(cmd)); + + instance_info->set_load(results[Keys::loadavg_key].as()); + instance_info->set_memory_usage(results[Keys::mem_usage_key].as()); + info->set_memory_total(results[Keys::mem_total_key].as()); + instance_info->set_disk_usage(results[Keys::disk_usage_key].as()); + info->set_disk_total(results[Keys::disk_total_key].as()); + info->set_cpu_count(results[Keys::cpus_key].as()); + + auto current_release = results[Keys::current_release_key].as(); + instance_info->set_current_release(!current_release.empty() ? current_release : original_release); + + std::string management_ip = vm.management_ipv4(); + auto all_ipv4 = vm.get_all_ipv4(); + + if (MP_UTILS.is_ipv4_valid(management_ip)) + instance_info->add_ipv4(management_ip); + else if (all_ipv4.empty()) + instance_info->add_ipv4("N/A"); + + for (const auto& extra_ipv4 : all_ipv4) + if (extra_ipv4 != management_ip) + instance_info->add_ipv4(extra_ipv4); +} diff --git a/src/daemon/runtime_instance_info_helper.h b/src/daemon/runtime_instance_info_helper.h index e81fc56fc6..004d0c6b36 100644 --- a/src/daemon/runtime_instance_info_helper.h +++ b/src/daemon/runtime_instance_info_helper.h @@ -26,13 +26,14 @@ class VirtualMachine; class DetailedInfoItem; class InstanceDetails; +// Note: we could extract other code to info/list populating code here, but that is left as a future improvement struct RuntimeInstanceInfoHelper { - static void populateRuntimeInstanceInfo(VirtualMachine& vm, - DetailedInfoItem* info, - InstanceDetails* instance_info, - const std::string& original_release, - bool parallelize); + static void populate_runtime_info(VirtualMachine& vm, + DetailedInfoItem* info, + InstanceDetails* instance_info, + const std::string& original_release, + bool parallelize); }; } // namespace multipass From ebb3b0aadab0affd7a9ba42f477ee23327897ca1 Mon Sep 17 00:00:00 2001 From: Ricardo Abreu Date: Fri, 5 Apr 2024 23:44:21 +0100 Subject: [PATCH 9/9] [runtime-info] Rename implementation classes Shorten the name of implementation-detail classes whose scope is now contained. --- src/daemon/runtime_instance_info_helper.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/daemon/runtime_instance_info_helper.cpp b/src/daemon/runtime_instance_info_helper.cpp index e595655c8c..4d1bb9b6d9 100644 --- a/src/daemon/runtime_instance_info_helper.cpp +++ b/src/daemon/runtime_instance_info_helper.cpp @@ -31,7 +31,7 @@ namespace mp = multipass; namespace { -struct RuntimeInfoKeys +struct Keys { public: static constexpr auto loadavg_key = "loadavg"; @@ -43,10 +43,9 @@ struct RuntimeInfoKeys static constexpr auto current_release_key = "current_release"; }; -struct RuntimeInfoCmds +struct Cmds { private: - using Keys = RuntimeInfoKeys; static constexpr auto key_val_cmd = R"(echo {}: \"$(eval "{}")\")"; static constexpr std::array key_cmds_pairs{ std::pair{Keys::loadavg_key, "cat /proc/loadavg | cut -d ' ' -f1-3"}, @@ -81,9 +80,6 @@ void mp::RuntimeInstanceInfoHelper::populate_runtime_info(mp::VirtualMachine& vm const std::string& original_release, bool parallelize) { - using Keys = RuntimeInfoKeys; // TODO@ricab just rename the types now - using Cmds = RuntimeInfoCmds; - const auto& cmd = parallelize ? Cmds::parallel_composite_cmd : Cmds::sequential_composite_cmd; auto results = YAML::Load(vm.ssh_exec(cmd));