Skip to content

Commit 84878a6

Browse files
author
Chris Townsend
authored
Merge pull request #3465 from canonical/parallelize-info-intra-vm
Parallelize info intra vm
2 parents 7473d6b + ebb3b0a commit 84878a6

File tree

7 files changed

+173
-42
lines changed

7 files changed

+173
-42
lines changed

include/multipass/utils.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ class Utils : public Singleton<Utils>
229229
virtual std::vector<uint8_t> random_bytes(size_t len);
230230
virtual QString make_uuid(const std::optional<std::string>& seed = std::nullopt) const;
231231
virtual void sleep_for(const std::chrono::milliseconds& ms) const;
232+
virtual bool is_ipv4_valid(const std::string& ipv4) const;
232233
};
233234
} // namespace multipass
234235

src/daemon/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ add_library(daemon STATIC
2424
daemon_rpc.cpp
2525
default_vm_image_vault.cpp
2626
instance_settings_handler.cpp
27+
runtime_instance_info_helper.cpp
2728
snapshot_settings_handler.cpp
2829
ubuntu_image_host.cpp)
2930

src/daemon/daemon.cpp

Lines changed: 7 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "daemon.h"
1919
#include "base_cloud_init_config.h"
2020
#include "instance_settings_handler.h"
21+
#include "runtime_instance_info_helper.h"
2122
#include "snapshot_settings_handler.h"
2223

2324
#include <multipass/alias_definition.h>
@@ -1030,20 +1031,6 @@ std::string generate_unused_mac_address(std::unordered_set<std::string>& s)
10301031
max_tries, s.size())};
10311032
}
10321033

1033-
bool is_ipv4_valid(const std::string& ipv4)
1034-
{
1035-
try
1036-
{
1037-
(mp::IPAddress(ipv4));
1038-
}
1039-
catch (std::invalid_argument&)
1040-
{
1041-
return false;
1042-
}
1043-
1044-
return true;
1045-
}
1046-
10471034
struct SnapshotPick
10481035
{
10491036
std::unordered_set<std::string> pick;
@@ -1802,7 +1789,7 @@ try // clang-format on
18021789
std::string management_ip = vm.management_ipv4();
18031790
auto all_ipv4 = vm.get_all_ipv4();
18041791

1805-
if (is_ipv4_valid(management_ip))
1792+
if (MP_UTILS.is_ipv4_valid(management_ip))
18061793
entry->add_ipv4(management_ip);
18071794
else if (all_ipv4.empty())
18081795
entry->add_ipv4("N/A");
@@ -3522,32 +3509,11 @@ void mp::Daemon::populate_instance_info(VirtualMachine& vm,
35223509
timestamp->set_nanos(created_time.time().msec() * 1'000'000);
35233510

35243511
if (!no_runtime_info && MP_UTILS.is_running(present_state))
3525-
{
3526-
instance_info->set_load(vm.ssh_exec("cat /proc/loadavg | cut -d ' ' -f1-3"));
3527-
instance_info->set_memory_usage(vm.ssh_exec("free -b | grep 'Mem:' | awk '{printf $3}'"));
3528-
info->set_memory_total(vm.ssh_exec("free -b | grep 'Mem:' | awk '{printf $2}'"));
3529-
instance_info->set_disk_usage(vm.ssh_exec("df -t ext4 -t vfat --total -B1 --output=used | tail -n 1"));
3530-
info->set_disk_total(vm.ssh_exec("df -t ext4 -t vfat --total -B1 --output=size | tail -n 1"));
3531-
info->set_cpu_count(vm.ssh_exec("nproc"));
3532-
3533-
std::string management_ip = vm.management_ipv4();
3534-
auto all_ipv4 = vm.get_all_ipv4();
3535-
3536-
if (is_ipv4_valid(management_ip))
3537-
instance_info->add_ipv4(management_ip);
3538-
else if (all_ipv4.empty())
3539-
instance_info->add_ipv4("N/A");
3540-
3541-
for (const auto& extra_ipv4 : all_ipv4)
3542-
if (extra_ipv4 != management_ip)
3543-
instance_info->add_ipv4(extra_ipv4);
3544-
3545-
auto current_release = vm.ssh_exec("cat /etc/os-release | grep 'PRETTY_NAME' | cut -d \\\" -f2");
3546-
instance_info->set_current_release(!current_release.empty() ? current_release : original_release);
3547-
3548-
instance_info->set_cpu_times(vm.ssh_exec("head -n1 /proc/stat"));
3549-
instance_info->set_uptime(vm.ssh_exec("uptime -p | tail -c+4"));
3550-
}
3512+
RuntimeInstanceInfoHelper::populate_runtime_info(vm,
3513+
info,
3514+
instance_info,
3515+
original_release,
3516+
vm_specs.num_cores == 1);
35513517
}
35523518

35533519
bool mp::Daemon::is_bridged(const std::string& instance_name)
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* Copyright (C) Canonical, Ltd.
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; version 3.
7+
*
8+
* This program is distributed in the hope that it will be useful,
9+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
* GNU General Public License for more details.
12+
*
13+
* You should have received a copy of the GNU General Public License
14+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
15+
*
16+
*/
17+
18+
#include "runtime_instance_info_helper.h"
19+
20+
#include <multipass/format.h>
21+
#include <multipass/rpc/multipass.grpc.pb.h>
22+
#include <multipass/utils.h>
23+
#include <multipass/virtual_machine.h>
24+
25+
#include <yaml-cpp/yaml.h>
26+
27+
#include <array>
28+
29+
namespace mp = multipass;
30+
31+
namespace
32+
{
33+
34+
struct Keys
35+
{
36+
public:
37+
static constexpr auto loadavg_key = "loadavg";
38+
static constexpr auto mem_usage_key = "mem_usage";
39+
static constexpr auto mem_total_key = "mem_total";
40+
static constexpr auto disk_usage_key = "disk_usage";
41+
static constexpr auto disk_total_key = "disk_total";
42+
static constexpr auto cpus_key = "cpus";
43+
static constexpr auto current_release_key = "current_release";
44+
};
45+
46+
struct Cmds
47+
{
48+
private:
49+
static constexpr auto key_val_cmd = R"(echo {}: \"$(eval "{}")\")";
50+
static constexpr std::array key_cmds_pairs{
51+
std::pair{Keys::loadavg_key, "cat /proc/loadavg | cut -d ' ' -f1-3"},
52+
std::pair{Keys::mem_usage_key, R"(free -b | grep 'Mem:' | awk '{printf \$3}')"},
53+
std::pair{Keys::mem_total_key, R"(free -b | grep 'Mem:' | awk '{printf \$2}')"},
54+
std::pair{Keys::disk_usage_key, "df -t ext4 -t vfat --total -B1 --output=used | tail -n 1"},
55+
std::pair{Keys::disk_total_key, "df -t ext4 -t vfat --total -B1 --output=size | tail -n 1"},
56+
std::pair{Keys::cpus_key, "nproc"},
57+
std::pair{Keys::current_release_key, R"(cat /etc/os-release | grep 'PRETTY_NAME' | cut -d \\\" -f2)"}};
58+
59+
inline static const std::array cmds = [] {
60+
constexpr auto n = key_cmds_pairs.size();
61+
std::array<std::string, key_cmds_pairs.size()> ret;
62+
for (std::size_t i = 0; i < n; ++i)
63+
{
64+
const auto [key, cmd] = key_cmds_pairs[i];
65+
ret[i] = fmt::format(key_val_cmd, key, cmd);
66+
}
67+
68+
return ret;
69+
}();
70+
71+
public:
72+
inline static const std::string sequential_composite_cmd = fmt::to_string(fmt::join(cmds, "; "));
73+
inline static const std::string parallel_composite_cmd = fmt::format("{} & wait", fmt::join(cmds, "& "));
74+
};
75+
} // namespace
76+
77+
void mp::RuntimeInstanceInfoHelper::populate_runtime_info(mp::VirtualMachine& vm,
78+
mp::DetailedInfoItem* info,
79+
mp::InstanceDetails* instance_info,
80+
const std::string& original_release,
81+
bool parallelize)
82+
{
83+
const auto& cmd = parallelize ? Cmds::parallel_composite_cmd : Cmds::sequential_composite_cmd;
84+
auto results = YAML::Load(vm.ssh_exec(cmd));
85+
86+
instance_info->set_load(results[Keys::loadavg_key].as<std::string>());
87+
instance_info->set_memory_usage(results[Keys::mem_usage_key].as<std::string>());
88+
info->set_memory_total(results[Keys::mem_total_key].as<std::string>());
89+
instance_info->set_disk_usage(results[Keys::disk_usage_key].as<std::string>());
90+
info->set_disk_total(results[Keys::disk_total_key].as<std::string>());
91+
info->set_cpu_count(results[Keys::cpus_key].as<std::string>());
92+
93+
auto current_release = results[Keys::current_release_key].as<std::string>();
94+
instance_info->set_current_release(!current_release.empty() ? current_release : original_release);
95+
96+
std::string management_ip = vm.management_ipv4();
97+
auto all_ipv4 = vm.get_all_ipv4();
98+
99+
if (MP_UTILS.is_ipv4_valid(management_ip))
100+
instance_info->add_ipv4(management_ip);
101+
else if (all_ipv4.empty())
102+
instance_info->add_ipv4("N/A");
103+
104+
for (const auto& extra_ipv4 : all_ipv4)
105+
if (extra_ipv4 != management_ip)
106+
instance_info->add_ipv4(extra_ipv4);
107+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright (C) Canonical, Ltd.
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; version 3.
7+
*
8+
* This program is distributed in the hope that it will be useful,
9+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
* GNU General Public License for more details.
12+
*
13+
* You should have received a copy of the GNU General Public License
14+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
15+
*
16+
*/
17+
18+
#ifndef MULTIPASS_RUNTIME_INSTANCE_INFO_HELPER_H
19+
#define MULTIPASS_RUNTIME_INSTANCE_INFO_HELPER_H
20+
21+
#include <string>
22+
23+
namespace multipass
24+
{
25+
class VirtualMachine;
26+
class DetailedInfoItem;
27+
class InstanceDetails;
28+
29+
// Note: we could extract other code to info/list populating code here, but that is left as a future improvement
30+
struct RuntimeInstanceInfoHelper
31+
{
32+
static void populate_runtime_info(VirtualMachine& vm,
33+
DetailedInfoItem* info,
34+
InstanceDetails* instance_info,
35+
const std::string& original_release,
36+
bool parallelize);
37+
};
38+
39+
} // namespace multipass
40+
41+
#endif // MULTIPASS_RUNTIME_INSTANCE_INFO_HELPER_H

src/utils/utils.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,21 @@ mp::Path mp::Utils::derive_instances_dir(const mp::Path& data_dir,
555555
return QDir(QDir(data_dir).filePath(backend_directory_name)).filePath(instances_subdir);
556556
}
557557

558-
void multipass::Utils::sleep_for(const std::chrono::milliseconds& ms) const
558+
void mp::Utils::sleep_for(const std::chrono::milliseconds& ms) const
559559
{
560560
std::this_thread::sleep_for(ms);
561561
}
562+
563+
bool mp::Utils::is_ipv4_valid(const std::string& ipv4) const
564+
{
565+
try
566+
{
567+
(mp::IPAddress(ipv4));
568+
}
569+
catch (std::invalid_argument&)
570+
{
571+
return false;
572+
}
573+
574+
return true;
575+
}

tests/mock_utils.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ class MockUtils : public Utils
4848
MOCK_METHOD(std::string, run_in_ssh_session, (SSHSession & session, const std::string& cmd), (const, override));
4949
MOCK_METHOD(QString, make_uuid, (const std::optional<std::string>&), (const, override));
5050
MOCK_METHOD(void, sleep_for, (const std::chrono::milliseconds&), (const, override));
51+
MOCK_METHOD(bool, is_ipv4_valid, (const std::string& ipv4), (const, override));
5152

5253
MP_MOCK_SINGLETON_BOILERPLATE(MockUtils, Utils);
5354
};

0 commit comments

Comments
 (0)