|
27 | 27 | #include <multipass/exceptions/not_implemented_on_this_backend_exception.h>
|
28 | 28 | #include <multipass/exceptions/sshfs_missing_error.h>
|
29 | 29 | #include <multipass/exceptions/start_exception.h>
|
| 30 | +#include <multipass/ip_address.h> |
30 | 31 | #include <multipass/logging/client_logger.h>
|
31 | 32 | #include <multipass/logging/log.h>
|
32 | 33 | #include <multipass/name_generator.h>
|
|
53 | 54 | #include <QJsonDocument>
|
54 | 55 | #include <QJsonObject>
|
55 | 56 | #include <QJsonParseError>
|
| 57 | +#include <QRegularExpression> |
56 | 58 | #include <QString>
|
57 | 59 | #include <QSysInfo>
|
58 | 60 | #include <QtConcurrent/QtConcurrent>
|
@@ -824,6 +826,65 @@ std::string generate_unused_mac_address(std::unordered_set<std::string>& s)
|
824 | 826 | max_tries, s.size())};
|
825 | 827 | }
|
826 | 828 |
|
| 829 | +// Executes a given command on the given session. Returns the output of the command, with spaces and feeds trimmed. |
| 830 | +// Caveat emptor: if the command fails, an empty string is returned. |
| 831 | +std::string run_in_vm(mp::SSHSession& session, const std::string& cmd) |
| 832 | +{ |
| 833 | + auto proc = exec_and_log(session, cmd); |
| 834 | + |
| 835 | + if (proc.exit_code() != 0) |
| 836 | + { |
| 837 | + auto error_msg = proc.read_std_error(); |
| 838 | + mpl::log(mpl::Level::warning, category, |
| 839 | + fmt::format("failed to run '{}', error message: '{}'", cmd, mp::utils::trim_end(error_msg))); |
| 840 | + return std::string{}; |
| 841 | + } |
| 842 | + |
| 843 | + auto output = proc.read_std_output(); |
| 844 | + if (output.empty()) |
| 845 | + { |
| 846 | + mpl::log(mpl::Level::warning, category, fmt::format("no output after running '{}'", cmd)); |
| 847 | + return std::string{}; |
| 848 | + } |
| 849 | + |
| 850 | + return mp::utils::trim_end(output); |
| 851 | +} |
| 852 | + |
| 853 | +bool is_ipv4_valid(const std::string& ipv4) |
| 854 | +{ |
| 855 | + try |
| 856 | + { |
| 857 | + (mp::IPAddress(ipv4)); |
| 858 | + } |
| 859 | + catch (std::invalid_argument&) |
| 860 | + { |
| 861 | + return false; |
| 862 | + } |
| 863 | + |
| 864 | + return true; |
| 865 | +} |
| 866 | + |
| 867 | +std::vector<std::string> get_all_ipv4(mp::SSHSession& session) |
| 868 | +{ |
| 869 | + std::vector<std::string> all_ipv4; |
| 870 | + |
| 871 | + auto ip_a_output = QString::fromStdString(run_in_vm(session, "ip -brief -family inet address show scope global")); |
| 872 | + |
| 873 | + QRegularExpression ipv4_re{QStringLiteral("([\\d\\.]+)\\/\\d+\\s*$"), QRegularExpression::MultilineOption}; |
| 874 | + |
| 875 | + QRegularExpressionMatchIterator ip_it = ipv4_re.globalMatch(ip_a_output); |
| 876 | + |
| 877 | + while (ip_it.hasNext()) |
| 878 | + { |
| 879 | + auto ip_match = ip_it.next(); |
| 880 | + auto ip = ip_match.captured(1).toStdString(); |
| 881 | + |
| 882 | + all_ipv4.push_back(ip); |
| 883 | + } |
| 884 | + |
| 885 | + return all_ipv4; |
| 886 | +} |
| 887 | + |
827 | 888 | } // namespace
|
828 | 889 |
|
829 | 890 | mp::Daemon::Daemon(std::unique_ptr<const DaemonConfig> the_config)
|
@@ -1324,37 +1385,25 @@ try // clang-format on
|
1324 | 1385 | mp::SSHSession session{vm->ssh_hostname(), vm->ssh_port(), vm_specs.ssh_username,
|
1325 | 1386 | *config->ssh_key_provider};
|
1326 | 1387 |
|
1327 |
| - auto run_in_vm = [&session](const std::string& cmd) { |
1328 |
| - auto proc = session.exec(cmd); |
1329 |
| - if (proc.exit_code() != 0) |
1330 |
| - { |
1331 |
| - auto error_msg = proc.read_std_error(); |
1332 |
| - mpl::log( |
1333 |
| - mpl::Level::warning, category, |
1334 |
| - fmt::format("failed to run '{}', error message: '{}'", cmd, mp::utils::trim_end(error_msg))); |
1335 |
| - return std::string{}; |
1336 |
| - } |
| 1388 | + info->set_load(run_in_vm(session, "cat /proc/loadavg | cut -d ' ' -f1-3")); |
| 1389 | + info->set_memory_usage(run_in_vm(session, "free -b | sed '1d;3d' | awk '{printf $3}'")); |
| 1390 | + info->set_memory_total(run_in_vm(session, "free -b | sed '1d;3d' | awk '{printf $2}'")); |
| 1391 | + info->set_disk_usage( |
| 1392 | + run_in_vm(session, "df --output=used `awk '$2 == \"/\" { print $1 }' /proc/mounts` -B1 | sed 1d")); |
| 1393 | + info->set_disk_total( |
| 1394 | + run_in_vm(session, "df --output=size `awk '$2 == \"/\" { print $1 }' /proc/mounts` -B1 | sed 1d")); |
1337 | 1395 |
|
1338 |
| - auto output = proc.read_std_output(); |
1339 |
| - if (output.empty()) |
1340 |
| - { |
1341 |
| - mpl::log(mpl::Level::warning, category, fmt::format("no output after running '{}'", cmd)); |
1342 |
| - return std::string{}; |
1343 |
| - } |
| 1396 | + std::string management_ip = vm->management_ipv4(); |
| 1397 | + auto all_ipv4 = get_all_ipv4(session); |
1344 | 1398 |
|
1345 |
| - return mp::utils::trim_end(output); |
1346 |
| - }; |
| 1399 | + if (is_ipv4_valid(management_ip)) |
| 1400 | + info->add_ipv4(management_ip); |
1347 | 1401 |
|
1348 |
| - info->set_load(run_in_vm("cat /proc/loadavg | cut -d ' ' -f1-3")); |
1349 |
| - info->set_memory_usage(run_in_vm("free -b | sed '1d;3d' | awk '{printf $3}'")); |
1350 |
| - info->set_memory_total(run_in_vm("free -b | sed '1d;3d' | awk '{printf $2}'")); |
1351 |
| - info->set_disk_usage( |
1352 |
| - run_in_vm("df --output=used `awk '$2 == \"/\" { print $1 }' /proc/mounts` -B1 | sed 1d")); |
1353 |
| - info->set_disk_total( |
1354 |
| - run_in_vm("df --output=size `awk '$2 == \"/\" { print $1 }' /proc/mounts` -B1 | sed 1d")); |
1355 |
| - info->set_ipv4(vm->ipv4()); |
| 1402 | + for (const auto& extra_ipv4 : all_ipv4) |
| 1403 | + if (extra_ipv4 != management_ip) |
| 1404 | + info->add_ipv4(extra_ipv4); |
1356 | 1405 |
|
1357 |
| - auto current_release = run_in_vm("lsb_release -ds"); |
| 1406 | + auto current_release = run_in_vm(session, "lsb_release -ds"); |
1358 | 1407 | info->set_current_release(!current_release.empty() ? current_release : original_release);
|
1359 | 1408 | }
|
1360 | 1409 | }
|
@@ -1407,7 +1456,21 @@ try // clang-format on
|
1407 | 1456 | entry->set_current_release(current_release);
|
1408 | 1457 |
|
1409 | 1458 | if (mp::utils::is_running(present_state))
|
1410 |
| - entry->set_ipv4(vm->ipv4()); |
| 1459 | + { |
| 1460 | + auto vm_specs = vm_instance_specs[name]; |
| 1461 | + mp::SSHSession session{vm->ssh_hostname(), vm->ssh_port(), vm_specs.ssh_username, |
| 1462 | + *config->ssh_key_provider}; |
| 1463 | + |
| 1464 | + std::string management_ip = vm->management_ipv4(); |
| 1465 | + auto all_ipv4 = get_all_ipv4(session); |
| 1466 | + |
| 1467 | + if (is_ipv4_valid(management_ip)) |
| 1468 | + entry->add_ipv4(management_ip); |
| 1469 | + |
| 1470 | + for (const auto& extra_ipv4 : all_ipv4) |
| 1471 | + if (extra_ipv4 != management_ip) |
| 1472 | + entry->add_ipv4(extra_ipv4); |
| 1473 | + } |
1411 | 1474 | }
|
1412 | 1475 |
|
1413 | 1476 | for (const auto& instance : deleted_instances)
|
|
0 commit comments