Skip to content

Commit

Permalink
Merge pull request #3265 from canonical/snapshots-todos
Browse files Browse the repository at this point in the history
[snapshots] Address pending TODOs

r=sharder996 a=ricab
  • Loading branch information
sharder996 authored Oct 25, 2023
2 parents 74419af + bb9977b commit ef4a85e
Show file tree
Hide file tree
Showing 23 changed files with 178 additions and 156 deletions.
2 changes: 1 addition & 1 deletion include/multipass/mount_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class MountHandler : private DisabledCopyMove
active = false;
}

const VMMount& get_mount_spec() const
const VMMount& get_mount_spec() const noexcept

Check warning on line 70 in include/multipass/mount_handler.h

View check run for this annotation

Codecov / codecov/patch

include/multipass/mount_handler.h#L70

Added line #L70 was not covered by tests
{
return mount_spec;

Check warning on line 72 in include/multipass/mount_handler.h

View check run for this annotation

Codecov / codecov/patch

include/multipass/mount_handler.h#L72

Added line #L72 was not covered by tests
}
Expand Down
6 changes: 3 additions & 3 deletions include/multipass/snapshot.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ struct VMMount;

class Snapshot : private DisabledCopyMove
{
public: // TODO@snapshots drop any accessors that we end up not needing
public:
virtual ~Snapshot() = default;

virtual int get_index() const = 0;
virtual int get_index() const noexcept = 0;
virtual std::string get_name() const = 0;
virtual std::string get_comment() const = 0;
virtual QDateTime get_creation_timestamp() const = 0;
virtual QDateTime get_creation_timestamp() const noexcept = 0;
virtual int get_num_cores() const noexcept = 0;
virtual MemorySize get_mem_size() const noexcept = 0;
virtual MemorySize get_disk_space() const noexcept = 0;
Expand Down
5 changes: 3 additions & 2 deletions include/multipass/virtual_machine.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class VirtualMachine : private DisabledCopyMove
const VMMount& mount) = 0;

using SnapshotVista = std::vector<std::shared_ptr<const Snapshot>>; // using vista to avoid confusion with C++ views
virtual SnapshotVista view_snapshots() const noexcept = 0;
virtual SnapshotVista view_snapshots() const = 0;
virtual int get_num_snapshots() const noexcept = 0;

virtual std::shared_ptr<const Snapshot> get_snapshot(const std::string& name) const = 0;
Expand All @@ -99,7 +99,8 @@ class VirtualMachine : private DisabledCopyMove
virtual std::shared_ptr<const Snapshot> take_snapshot(const VMSpecs& specs,
const std::string& snapshot_name,
const std::string& comment) = 0;
virtual void rename_snapshot(const std::string& old_name, const std::string& new_name) = 0; // TODO@snapshots remove
virtual void rename_snapshot(const std::string& old_name,
const std::string& new_name) = 0; // only VM can avoid repeated names
virtual void delete_snapshot(const std::string& name) = 0;
virtual void restore_snapshot(const std::string& name, VMSpecs& specs) = 0;
virtual void load_snapshots() = 0;
Expand Down
8 changes: 8 additions & 0 deletions include/multipass/vm_mount.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

#include <multipass/id_mappings.h>

#include <QJsonObject>

#include <string>

namespace multipass
Expand All @@ -32,6 +34,12 @@ struct VMMount
Native = 1
};

VMMount() = default;
VMMount(const QJsonObject& json);
VMMount(const std::string& sourcePath, id_mappings gidMappings, id_mappings uidMappings, MountType mountType);

QJsonObject serialize() const;

std::string source_path;
id_mappings gid_mappings;
id_mappings uid_mappings;
Expand Down
31 changes: 23 additions & 8 deletions src/daemon/vm_specs.h → include/multipass/vm_specs.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@
#ifndef MULTIPASS_VM_SPECS_H
#define MULTIPASS_VM_SPECS_H

#include <multipass/memory_size.h>
#include <multipass/network_interface.h>
#include <multipass/virtual_machine.h>
#include <multipass/vm_mount.h>
#include "memory_size.h"
#include "network_interface.h"
#include "virtual_machine.h"
#include "vm_mount.h"

#include <string>
#include <tuple>
Expand All @@ -48,10 +48,25 @@ struct VMSpecs

inline bool operator==(const VMSpecs& a, const VMSpecs& b)
{
return std::tie(a.num_cores, a.mem_size, a.disk_space, a.default_mac_address, a.extra_interfaces, a.ssh_username,
a.state, a.mounts, a.deleted, a.metadata) ==
std::tie(b.num_cores, b.mem_size, b.disk_space, b.default_mac_address, b.extra_interfaces, b.ssh_username,
b.state, b.mounts, b.deleted, b.metadata);
return std::tie(a.num_cores,

Check warning on line 51 in include/multipass/vm_specs.h

View check run for this annotation

Codecov / codecov/patch

include/multipass/vm_specs.h#L51

Added line #L51 was not covered by tests
a.mem_size,
a.disk_space,
a.default_mac_address,
a.extra_interfaces,
a.ssh_username,
a.state,
a.mounts,
a.deleted,
a.metadata) == std::tie(b.num_cores,
b.mem_size,
b.disk_space,
b.default_mac_address,
b.extra_interfaces,
b.ssh_username,
b.state,
b.mounts,
b.deleted,
b.metadata);
}

inline bool operator!=(const VMSpecs& a, const VMSpecs& b) // TODO drop in C++20

Check warning on line 72 in include/multipass/vm_specs.h

View check run for this annotation

Codecov / codecov/patch

include/multipass/vm_specs.h#L72

Added line #L72 was not covered by tests
Expand Down
3 changes: 2 additions & 1 deletion src/client/cli/formatter/table_formatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ std::string generate_snapshot_details(const mp::DetailedInfoItem& item)
fmt::format_to(std::back_inserter(buf), "{}\n", *child);
}

// TODO@snapshots split and align string if it extends onto several lines
/* TODO split and align string if it extends onto several lines; but actually better implement generic word-wrapping
for all output, taking both terminal width and current indentation level into account */
fmt::format_to(std::back_inserter(buf),
"{:<16}{}\n",
"Comment:",
Expand Down
57 changes: 3 additions & 54 deletions src/daemon/daemon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -325,30 +325,8 @@ std::unordered_map<std::string, mp::VMSpecs> load_db(const mp::Path& data_path,

for (QJsonValueRef entry : record["mounts"].toArray())
{
mp::id_mappings uid_mappings;
mp::id_mappings gid_mappings;

auto target_path = entry.toObject()["target_path"].toString().toStdString();
auto source_path = entry.toObject()["source_path"].toString().toStdString();

for (QJsonValueRef uid_entry : entry.toObject()["uid_mappings"].toArray())
{
uid_mappings.push_back(
{uid_entry.toObject()["host_uid"].toInt(), uid_entry.toObject()["instance_uid"].toInt()});
}

for (QJsonValueRef gid_entry : entry.toObject()["gid_mappings"].toArray())
{
gid_mappings.push_back(
{gid_entry.toObject()["host_gid"].toInt(), gid_entry.toObject()["instance_gid"].toInt()});
}

uid_mappings = mp::unique_id_mappings(uid_mappings);
gid_mappings = mp::unique_id_mappings(gid_mappings);
auto mount_type = mp::VMMount::MountType(entry.toObject()["mount_type"].toInt());

mp::VMMount mount{source_path, gid_mappings, uid_mappings, mount_type};
mounts[target_path] = mount;
const auto& json = entry.toObject();
mounts[json["target_path"].toString().toStdString()] = mp::VMMount{json};
}

reconstructed_records[key] = {num_cores,
Expand Down Expand Up @@ -400,37 +378,8 @@ QJsonObject vm_spec_to_json(const mp::VMSpecs& specs)
QJsonArray json_mounts;
for (const auto& mount : specs.mounts)
{
QJsonObject entry;
entry.insert("source_path", QString::fromStdString(mount.second.source_path));
auto entry = mount.second.serialize();
entry.insert("target_path", QString::fromStdString(mount.first));

QJsonArray uid_mappings;

for (const auto& map : mount.second.uid_mappings)
{
QJsonObject map_entry;
map_entry.insert("host_uid", map.first);
map_entry.insert("instance_uid", map.second);

uid_mappings.append(map_entry);
}

entry.insert("uid_mappings", uid_mappings);

QJsonArray gid_mappings;

for (const auto& map : mount.second.gid_mappings)
{
QJsonObject map_entry;
map_entry.insert("host_gid", map.first);
map_entry.insert("instance_gid", map.second);

gid_mappings.append(map_entry);
}

entry.insert("gid_mappings", gid_mappings);

entry.insert("mount_type", static_cast<int>(mount.second.mount_type));
json_mounts.append(entry);
}

Expand Down
3 changes: 1 addition & 2 deletions src/daemon/daemon.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,12 @@

#include "daemon_config.h"
#include "daemon_rpc.h"
#include "multipass/virtual_machine.h"
#include "vm_specs.h"

#include <multipass/async_periodic_task.h>
#include <multipass/delayed_shutdown_timer.h>
#include <multipass/mount_handler.h>
#include <multipass/virtual_machine.h>
#include <multipass/vm_specs.h>
#include <multipass/vm_status_monitor.h>

#include <chrono>
Expand Down
3 changes: 1 addition & 2 deletions src/daemon/instance_settings_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@
#ifndef MULTIPASS_INSTANCE_SETTINGS_HANDLER_H
#define MULTIPASS_INSTANCE_SETTINGS_HANDLER_H

#include "vm_specs.h"

#include <multipass/exceptions/settings_exceptions.h>
#include <multipass/settings/settings_handler.h>
#include <multipass/virtual_machine.h>
#include <multipass/vm_specs.h>

#include <QString>

Expand Down
2 changes: 1 addition & 1 deletion src/daemon/snapshot_settings_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ mp::SnapshotSettingsException::SnapshotSettingsException(const std::string& deta
mp::SnapshotSettingsHandler::SnapshotSettingsHandler(
std::unordered_map<std::string, VirtualMachine::ShPtr>& operative_instances,
const std::unordered_map<std::string, VirtualMachine::ShPtr>& deleted_instances,
const std::unordered_set<std::string>& preparing_instances)
const std::unordered_set<std::string>& preparing_instances) noexcept
: operative_instances{operative_instances},
deleted_instances{deleted_instances},
preparing_instances{preparing_instances}
Expand Down
2 changes: 1 addition & 1 deletion src/daemon/snapshot_settings_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class SnapshotSettingsHandler : public SettingsHandler
public:
SnapshotSettingsHandler(std::unordered_map<std::string, VirtualMachine::ShPtr>& operative_instances,
const std::unordered_map<std::string, VirtualMachine::ShPtr>& deleted_instances,
const std::unordered_set<std::string>& preparing_instances);
const std::unordered_set<std::string>& preparing_instances) noexcept;

std::set<QString> keys() const override;
QString get(const QString& key) const override;
Expand Down
4 changes: 2 additions & 2 deletions src/platform/backends/libvirt/libvirt_virtual_machine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -552,10 +552,10 @@ auto mp::LibVirtVirtualMachine::make_specific_snapshot(const std::string& /*snap
std::shared_ptr<Snapshot> /*parent*/)
-> std::shared_ptr<Snapshot>
{
throw NotImplementedOnThisBackendException{"Snapshots"}; // TODO@snapshots
throw NotImplementedOnThisBackendException{"Snapshots"};

Check warning on line 555 in src/platform/backends/libvirt/libvirt_virtual_machine.cpp

View check run for this annotation

Codecov / codecov/patch

src/platform/backends/libvirt/libvirt_virtual_machine.cpp#L555

Added line #L555 was not covered by tests
}

auto mp::LibVirtVirtualMachine::make_specific_snapshot(const QString& /*filename*/) -> std::shared_ptr<Snapshot>

Check warning on line 558 in src/platform/backends/libvirt/libvirt_virtual_machine.cpp

View check run for this annotation

Codecov / codecov/patch

src/platform/backends/libvirt/libvirt_virtual_machine.cpp#L558

Added line #L558 was not covered by tests
{
throw NotImplementedOnThisBackendException{"Snapshots"}; // TODO@snapshots
throw NotImplementedOnThisBackendException{"Snapshots"};

Check warning on line 560 in src/platform/backends/libvirt/libvirt_virtual_machine.cpp

View check run for this annotation

Codecov / codecov/patch

src/platform/backends/libvirt/libvirt_virtual_machine.cpp#L560

Added line #L560 was not covered by tests
}
4 changes: 2 additions & 2 deletions src/platform/backends/lxd/lxd_virtual_machine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -492,10 +492,10 @@ auto mp::LXDVirtualMachine::make_specific_snapshot(const std::string& snapshot_n
const VMSpecs& specs,
std::shared_ptr<Snapshot> parent) -> std::shared_ptr<Snapshot>
{
throw NotImplementedOnThisBackendException{"Snapshots"}; // TODO@snapshots
throw NotImplementedOnThisBackendException{"Snapshots"};

Check warning on line 495 in src/platform/backends/lxd/lxd_virtual_machine.cpp

View check run for this annotation

Codecov / codecov/patch

src/platform/backends/lxd/lxd_virtual_machine.cpp#L495

Added line #L495 was not covered by tests
}

std::shared_ptr<mp::Snapshot> mp::LXDVirtualMachine::make_specific_snapshot(const QString& /*filename*/)

Check warning on line 498 in src/platform/backends/lxd/lxd_virtual_machine.cpp

View check run for this annotation

Codecov / codecov/patch

src/platform/backends/lxd/lxd_virtual_machine.cpp#L498

Added line #L498 was not covered by tests
{
throw NotImplementedOnThisBackendException{"Snapshots"}; // TODO@snapshots
throw NotImplementedOnThisBackendException{"Snapshots"};

Check warning on line 500 in src/platform/backends/lxd/lxd_virtual_machine.cpp

View check run for this annotation

Codecov / codecov/patch

src/platform/backends/lxd/lxd_virtual_machine.cpp#L500

Added line #L500 was not covered by tests
}
75 changes: 14 additions & 61 deletions src/platform/backends/shared/base_snapshot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@
*/

#include "base_snapshot.h"
#include "daemon/vm_specs.h" // TODO@snapshots move this
#include "multipass/virtual_machine.h"

#include <multipass/file_ops.h>
#include <multipass/id_mappings.h> // TODO@snapshots may be able to drop after extracting JSON utilities
#include <multipass/json_utils.h>
#include <multipass/vm_mount.h>
#include <multipass/vm_specs.h>

#include <scope_guard.hpp>

#include <QJsonArray> // TODO@snapshots may be able to drop after extracting JSON utilities
#include <QJsonArray>
#include <QString>

#include <QFile>
Expand Down Expand Up @@ -68,35 +68,13 @@ QJsonObject read_snapshot_json(const QString& filename)
return json["snapshot"].toObject();

Check warning on line 68 in src/platform/backends/shared/base_snapshot.cpp

View check run for this annotation

Codecov / codecov/patch

src/platform/backends/shared/base_snapshot.cpp#L68

Added line #L68 was not covered by tests
}

std::unordered_map<std::string, mp::VMMount> load_mounts(const QJsonArray& json)
std::unordered_map<std::string, mp::VMMount> load_mounts(const QJsonArray& mounts_json)

Check warning on line 71 in src/platform/backends/shared/base_snapshot.cpp

View check run for this annotation

Codecov / codecov/patch

src/platform/backends/shared/base_snapshot.cpp#L71

Added line #L71 was not covered by tests
{
std::unordered_map<std::string, mp::VMMount> mounts;
for (const auto& entry : json)
for (const auto& entry : mounts_json)

Check warning on line 74 in src/platform/backends/shared/base_snapshot.cpp

View check run for this annotation

Codecov / codecov/patch

src/platform/backends/shared/base_snapshot.cpp#L73-L74

Added lines #L73 - L74 were not covered by tests
{
mp::id_mappings uid_mappings;
mp::id_mappings gid_mappings;

auto target_path = entry.toObject()["target_path"].toString().toStdString();
auto source_path = entry.toObject()["source_path"].toString().toStdString();

for (const QJsonValueRef uid_entry : entry.toObject()["uid_mappings"].toArray())
{
uid_mappings.push_back(
{uid_entry.toObject()["host_uid"].toInt(), uid_entry.toObject()["instance_uid"].toInt()});
}

for (const QJsonValueRef gid_entry : entry.toObject()["gid_mappings"].toArray())
{
gid_mappings.push_back(
{gid_entry.toObject()["host_gid"].toInt(), gid_entry.toObject()["instance_gid"].toInt()});
}

uid_mappings = mp::unique_id_mappings(uid_mappings);
gid_mappings = mp::unique_id_mappings(gid_mappings);
auto mount_type = mp::VMMount::MountType(entry.toObject()["mount_type"].toInt());

mp::VMMount mount{source_path, gid_mappings, uid_mappings, mount_type};
mounts[target_path] = std::move(mount);
const auto& json = entry.toObject();
mounts[json["target_path"].toString().toStdString()] = mp::VMMount{json};

Check warning on line 77 in src/platform/backends/shared/base_snapshot.cpp

View check run for this annotation

Codecov / codecov/patch

src/platform/backends/shared/base_snapshot.cpp#L76-L77

Added lines #L76 - L77 were not covered by tests
}

return mounts;

Check warning on line 80 in src/platform/backends/shared/base_snapshot.cpp

View check run for this annotation

Codecov / codecov/patch

src/platform/backends/shared/base_snapshot.cpp#L80

Added line #L80 was not covered by tests
Expand Down Expand Up @@ -146,9 +124,13 @@ mp::BaseSnapshot::BaseSnapshot(const std::string& name, // NOLINT(modernize-p
captured{captured}

Check warning on line 124 in src/platform/backends/shared/base_snapshot.cpp

View check run for this annotation

Codecov / codecov/patch

src/platform/backends/shared/base_snapshot.cpp#L111-L124

Added lines #L111 - L124 were not covered by tests
{
assert(index > 0 && "snapshot indices need to start at 1");

Check warning on line 126 in src/platform/backends/shared/base_snapshot.cpp

View check run for this annotation

Codecov / codecov/patch

src/platform/backends/shared/base_snapshot.cpp#L126

Added line #L126 was not covered by tests
using St = VirtualMachine::State;
if (state != St::off && state != St::stopped)
throw std::runtime_error{fmt::format("Unsupported VM state in snapshot: {}", static_cast<int>(state))};
if (index < 1)
throw std::runtime_error{fmt::format("Snapshot index not positive: {}", index)};
if (index > max_snapshots)
throw std::runtime_error{fmt::format("Maximum number of snapshots exceeded: {}", max_snapshots)};

throw std::runtime_error{fmt::format("Maximum number of snapshots exceeded: {}", index)};
if (name.empty())
throw std::runtime_error{"Snapshot names cannot be empty"};
if (num_cores < 1)
Expand Down Expand Up @@ -224,37 +206,8 @@ QJsonObject mp::BaseSnapshot::serialize() const
QJsonArray json_mounts;
for (const auto& mount : mounts)

Check warning on line 207 in src/platform/backends/shared/base_snapshot.cpp

View check run for this annotation

Codecov / codecov/patch

src/platform/backends/shared/base_snapshot.cpp#L206-L207

Added lines #L206 - L207 were not covered by tests
{
QJsonObject entry;
entry.insert("source_path", QString::fromStdString(mount.second.source_path));
auto entry = mount.second.serialize();
entry.insert("target_path", QString::fromStdString(mount.first));

QJsonArray uid_mappings;

for (const auto& map : mount.second.uid_mappings)
{
QJsonObject map_entry;
map_entry.insert("host_uid", map.first);
map_entry.insert("instance_uid", map.second);

uid_mappings.append(map_entry);
}

entry.insert("uid_mappings", uid_mappings);

QJsonArray gid_mappings;

for (const auto& map : mount.second.gid_mappings)
{
QJsonObject map_entry;
map_entry.insert("host_gid", map.first);
map_entry.insert("instance_gid", map.second);

gid_mappings.append(map_entry);
}

entry.insert("gid_mappings", gid_mappings);

entry.insert("mount_type", static_cast<int>(mount.second.mount_type));
json_mounts.append(entry);

Check warning on line 211 in src/platform/backends/shared/base_snapshot.cpp

View check run for this annotation

Codecov / codecov/patch

src/platform/backends/shared/base_snapshot.cpp#L209-L211

Added lines #L209 - L211 were not covered by tests
}

Expand Down
Loading

0 comments on commit ef4a85e

Please sign in to comment.