Skip to content

Commit ef4a85e

Browse files
authored
Merge pull request #3265 from canonical/snapshots-todos
[snapshots] Address pending TODOs r=sharder996 a=ricab
2 parents 74419af + bb9977b commit ef4a85e

23 files changed

+178
-156
lines changed

include/multipass/mount_handler.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class MountHandler : private DisabledCopyMove
6767
active = false;
6868
}
6969

70-
const VMMount& get_mount_spec() const
70+
const VMMount& get_mount_spec() const noexcept
7171
{
7272
return mount_spec;
7373
}

include/multipass/snapshot.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,13 @@ struct VMMount;
3535

3636
class Snapshot : private DisabledCopyMove
3737
{
38-
public: // TODO@snapshots drop any accessors that we end up not needing
38+
public:
3939
virtual ~Snapshot() = default;
4040

41-
virtual int get_index() const = 0;
41+
virtual int get_index() const noexcept = 0;
4242
virtual std::string get_name() const = 0;
4343
virtual std::string get_comment() const = 0;
44-
virtual QDateTime get_creation_timestamp() const = 0;
44+
virtual QDateTime get_creation_timestamp() const noexcept = 0;
4545
virtual int get_num_cores() const noexcept = 0;
4646
virtual MemorySize get_mem_size() const noexcept = 0;
4747
virtual MemorySize get_disk_space() const noexcept = 0;

include/multipass/virtual_machine.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ class VirtualMachine : private DisabledCopyMove
8888
const VMMount& mount) = 0;
8989

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

9494
virtual std::shared_ptr<const Snapshot> get_snapshot(const std::string& name) const = 0;
@@ -99,7 +99,8 @@ class VirtualMachine : private DisabledCopyMove
9999
virtual std::shared_ptr<const Snapshot> take_snapshot(const VMSpecs& specs,
100100
const std::string& snapshot_name,
101101
const std::string& comment) = 0;
102-
virtual void rename_snapshot(const std::string& old_name, const std::string& new_name) = 0; // TODO@snapshots remove
102+
virtual void rename_snapshot(const std::string& old_name,
103+
const std::string& new_name) = 0; // only VM can avoid repeated names
103104
virtual void delete_snapshot(const std::string& name) = 0;
104105
virtual void restore_snapshot(const std::string& name, VMSpecs& specs) = 0;
105106
virtual void load_snapshots() = 0;

include/multipass/vm_mount.h

+8
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
#include <multipass/id_mappings.h>
2222

23+
#include <QJsonObject>
24+
2325
#include <string>
2426

2527
namespace multipass
@@ -32,6 +34,12 @@ struct VMMount
3234
Native = 1
3335
};
3436

37+
VMMount() = default;
38+
VMMount(const QJsonObject& json);
39+
VMMount(const std::string& sourcePath, id_mappings gidMappings, id_mappings uidMappings, MountType mountType);
40+
41+
QJsonObject serialize() const;
42+
3543
std::string source_path;
3644
id_mappings gid_mappings;
3745
id_mappings uid_mappings;

src/daemon/vm_specs.h include/multipass/vm_specs.h

+23-8
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@
1818
#ifndef MULTIPASS_VM_SPECS_H
1919
#define MULTIPASS_VM_SPECS_H
2020

21-
#include <multipass/memory_size.h>
22-
#include <multipass/network_interface.h>
23-
#include <multipass/virtual_machine.h>
24-
#include <multipass/vm_mount.h>
21+
#include "memory_size.h"
22+
#include "network_interface.h"
23+
#include "virtual_machine.h"
24+
#include "vm_mount.h"
2525

2626
#include <string>
2727
#include <tuple>
@@ -48,10 +48,25 @@ struct VMSpecs
4848

4949
inline bool operator==(const VMSpecs& a, const VMSpecs& b)
5050
{
51-
return std::tie(a.num_cores, a.mem_size, a.disk_space, a.default_mac_address, a.extra_interfaces, a.ssh_username,
52-
a.state, a.mounts, a.deleted, a.metadata) ==
53-
std::tie(b.num_cores, b.mem_size, b.disk_space, b.default_mac_address, b.extra_interfaces, b.ssh_username,
54-
b.state, b.mounts, b.deleted, b.metadata);
51+
return std::tie(a.num_cores,
52+
a.mem_size,
53+
a.disk_space,
54+
a.default_mac_address,
55+
a.extra_interfaces,
56+
a.ssh_username,
57+
a.state,
58+
a.mounts,
59+
a.deleted,
60+
a.metadata) == std::tie(b.num_cores,
61+
b.mem_size,
62+
b.disk_space,
63+
b.default_mac_address,
64+
b.extra_interfaces,
65+
b.ssh_username,
66+
b.state,
67+
b.mounts,
68+
b.deleted,
69+
b.metadata);
5570
}
5671

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

src/client/cli/formatter/table_formatter.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@ std::string generate_snapshot_details(const mp::DetailedInfoItem& item)
103103
fmt::format_to(std::back_inserter(buf), "{}\n", *child);
104104
}
105105

106-
// TODO@snapshots split and align string if it extends onto several lines
106+
/* TODO split and align string if it extends onto several lines; but actually better implement generic word-wrapping
107+
for all output, taking both terminal width and current indentation level into account */
107108
fmt::format_to(std::back_inserter(buf),
108109
"{:<16}{}\n",
109110
"Comment:",

src/daemon/daemon.cpp

+3-54
Original file line numberDiff line numberDiff line change
@@ -325,30 +325,8 @@ std::unordered_map<std::string, mp::VMSpecs> load_db(const mp::Path& data_path,
325325

326326
for (QJsonValueRef entry : record["mounts"].toArray())
327327
{
328-
mp::id_mappings uid_mappings;
329-
mp::id_mappings gid_mappings;
330-
331-
auto target_path = entry.toObject()["target_path"].toString().toStdString();
332-
auto source_path = entry.toObject()["source_path"].toString().toStdString();
333-
334-
for (QJsonValueRef uid_entry : entry.toObject()["uid_mappings"].toArray())
335-
{
336-
uid_mappings.push_back(
337-
{uid_entry.toObject()["host_uid"].toInt(), uid_entry.toObject()["instance_uid"].toInt()});
338-
}
339-
340-
for (QJsonValueRef gid_entry : entry.toObject()["gid_mappings"].toArray())
341-
{
342-
gid_mappings.push_back(
343-
{gid_entry.toObject()["host_gid"].toInt(), gid_entry.toObject()["instance_gid"].toInt()});
344-
}
345-
346-
uid_mappings = mp::unique_id_mappings(uid_mappings);
347-
gid_mappings = mp::unique_id_mappings(gid_mappings);
348-
auto mount_type = mp::VMMount::MountType(entry.toObject()["mount_type"].toInt());
349-
350-
mp::VMMount mount{source_path, gid_mappings, uid_mappings, mount_type};
351-
mounts[target_path] = mount;
328+
const auto& json = entry.toObject();
329+
mounts[json["target_path"].toString().toStdString()] = mp::VMMount{json};
352330
}
353331

354332
reconstructed_records[key] = {num_cores,
@@ -400,37 +378,8 @@ QJsonObject vm_spec_to_json(const mp::VMSpecs& specs)
400378
QJsonArray json_mounts;
401379
for (const auto& mount : specs.mounts)
402380
{
403-
QJsonObject entry;
404-
entry.insert("source_path", QString::fromStdString(mount.second.source_path));
381+
auto entry = mount.second.serialize();
405382
entry.insert("target_path", QString::fromStdString(mount.first));
406-
407-
QJsonArray uid_mappings;
408-
409-
for (const auto& map : mount.second.uid_mappings)
410-
{
411-
QJsonObject map_entry;
412-
map_entry.insert("host_uid", map.first);
413-
map_entry.insert("instance_uid", map.second);
414-
415-
uid_mappings.append(map_entry);
416-
}
417-
418-
entry.insert("uid_mappings", uid_mappings);
419-
420-
QJsonArray gid_mappings;
421-
422-
for (const auto& map : mount.second.gid_mappings)
423-
{
424-
QJsonObject map_entry;
425-
map_entry.insert("host_gid", map.first);
426-
map_entry.insert("instance_gid", map.second);
427-
428-
gid_mappings.append(map_entry);
429-
}
430-
431-
entry.insert("gid_mappings", gid_mappings);
432-
433-
entry.insert("mount_type", static_cast<int>(mount.second.mount_type));
434383
json_mounts.append(entry);
435384
}
436385

src/daemon/daemon.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,12 @@
2020

2121
#include "daemon_config.h"
2222
#include "daemon_rpc.h"
23-
#include "multipass/virtual_machine.h"
24-
#include "vm_specs.h"
2523

2624
#include <multipass/async_periodic_task.h>
2725
#include <multipass/delayed_shutdown_timer.h>
2826
#include <multipass/mount_handler.h>
2927
#include <multipass/virtual_machine.h>
28+
#include <multipass/vm_specs.h>
3029
#include <multipass/vm_status_monitor.h>
3130

3231
#include <chrono>

src/daemon/instance_settings_handler.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,10 @@
1818
#ifndef MULTIPASS_INSTANCE_SETTINGS_HANDLER_H
1919
#define MULTIPASS_INSTANCE_SETTINGS_HANDLER_H
2020

21-
#include "vm_specs.h"
22-
2321
#include <multipass/exceptions/settings_exceptions.h>
2422
#include <multipass/settings/settings_handler.h>
2523
#include <multipass/virtual_machine.h>
24+
#include <multipass/vm_specs.h>
2625

2726
#include <QString>
2827

src/daemon/snapshot_settings_handler.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ mp::SnapshotSettingsException::SnapshotSettingsException(const std::string& deta
8080
mp::SnapshotSettingsHandler::SnapshotSettingsHandler(
8181
std::unordered_map<std::string, VirtualMachine::ShPtr>& operative_instances,
8282
const std::unordered_map<std::string, VirtualMachine::ShPtr>& deleted_instances,
83-
const std::unordered_set<std::string>& preparing_instances)
83+
const std::unordered_set<std::string>& preparing_instances) noexcept
8484
: operative_instances{operative_instances},
8585
deleted_instances{deleted_instances},
8686
preparing_instances{preparing_instances}

src/daemon/snapshot_settings_handler.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class SnapshotSettingsHandler : public SettingsHandler
3535
public:
3636
SnapshotSettingsHandler(std::unordered_map<std::string, VirtualMachine::ShPtr>& operative_instances,
3737
const std::unordered_map<std::string, VirtualMachine::ShPtr>& deleted_instances,
38-
const std::unordered_set<std::string>& preparing_instances);
38+
const std::unordered_set<std::string>& preparing_instances) noexcept;
3939

4040
std::set<QString> keys() const override;
4141
QString get(const QString& key) const override;

src/platform/backends/libvirt/libvirt_virtual_machine.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -552,10 +552,10 @@ auto mp::LibVirtVirtualMachine::make_specific_snapshot(const std::string& /*snap
552552
std::shared_ptr<Snapshot> /*parent*/)
553553
-> std::shared_ptr<Snapshot>
554554
{
555-
throw NotImplementedOnThisBackendException{"Snapshots"}; // TODO@snapshots
555+
throw NotImplementedOnThisBackendException{"Snapshots"};
556556
}
557557

558558
auto mp::LibVirtVirtualMachine::make_specific_snapshot(const QString& /*filename*/) -> std::shared_ptr<Snapshot>
559559
{
560-
throw NotImplementedOnThisBackendException{"Snapshots"}; // TODO@snapshots
560+
throw NotImplementedOnThisBackendException{"Snapshots"};
561561
}

src/platform/backends/lxd/lxd_virtual_machine.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -492,10 +492,10 @@ auto mp::LXDVirtualMachine::make_specific_snapshot(const std::string& snapshot_n
492492
const VMSpecs& specs,
493493
std::shared_ptr<Snapshot> parent) -> std::shared_ptr<Snapshot>
494494
{
495-
throw NotImplementedOnThisBackendException{"Snapshots"}; // TODO@snapshots
495+
throw NotImplementedOnThisBackendException{"Snapshots"};
496496
}
497497

498498
std::shared_ptr<mp::Snapshot> mp::LXDVirtualMachine::make_specific_snapshot(const QString& /*filename*/)
499499
{
500-
throw NotImplementedOnThisBackendException{"Snapshots"}; // TODO@snapshots
500+
throw NotImplementedOnThisBackendException{"Snapshots"};
501501
}

src/platform/backends/shared/base_snapshot.cpp

+14-61
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@
1616
*/
1717

1818
#include "base_snapshot.h"
19-
#include "daemon/vm_specs.h" // TODO@snapshots move this
19+
#include "multipass/virtual_machine.h"
2020

2121
#include <multipass/file_ops.h>
22-
#include <multipass/id_mappings.h> // TODO@snapshots may be able to drop after extracting JSON utilities
2322
#include <multipass/json_utils.h>
2423
#include <multipass/vm_mount.h>
24+
#include <multipass/vm_specs.h>
2525

2626
#include <scope_guard.hpp>
2727

28-
#include <QJsonArray> // TODO@snapshots may be able to drop after extracting JSON utilities
28+
#include <QJsonArray>
2929
#include <QString>
3030

3131
#include <QFile>
@@ -68,35 +68,13 @@ QJsonObject read_snapshot_json(const QString& filename)
6868
return json["snapshot"].toObject();
6969
}
7070

71-
std::unordered_map<std::string, mp::VMMount> load_mounts(const QJsonArray& json)
71+
std::unordered_map<std::string, mp::VMMount> load_mounts(const QJsonArray& mounts_json)
7272
{
7373
std::unordered_map<std::string, mp::VMMount> mounts;
74-
for (const auto& entry : json)
74+
for (const auto& entry : mounts_json)
7575
{
76-
mp::id_mappings uid_mappings;
77-
mp::id_mappings gid_mappings;
78-
79-
auto target_path = entry.toObject()["target_path"].toString().toStdString();
80-
auto source_path = entry.toObject()["source_path"].toString().toStdString();
81-
82-
for (const QJsonValueRef uid_entry : entry.toObject()["uid_mappings"].toArray())
83-
{
84-
uid_mappings.push_back(
85-
{uid_entry.toObject()["host_uid"].toInt(), uid_entry.toObject()["instance_uid"].toInt()});
86-
}
87-
88-
for (const QJsonValueRef gid_entry : entry.toObject()["gid_mappings"].toArray())
89-
{
90-
gid_mappings.push_back(
91-
{gid_entry.toObject()["host_gid"].toInt(), gid_entry.toObject()["instance_gid"].toInt()});
92-
}
93-
94-
uid_mappings = mp::unique_id_mappings(uid_mappings);
95-
gid_mappings = mp::unique_id_mappings(gid_mappings);
96-
auto mount_type = mp::VMMount::MountType(entry.toObject()["mount_type"].toInt());
97-
98-
mp::VMMount mount{source_path, gid_mappings, uid_mappings, mount_type};
99-
mounts[target_path] = std::move(mount);
76+
const auto& json = entry.toObject();
77+
mounts[json["target_path"].toString().toStdString()] = mp::VMMount{json};
10078
}
10179

10280
return mounts;
@@ -146,9 +124,13 @@ mp::BaseSnapshot::BaseSnapshot(const std::string& name, // NOLINT(modernize-p
146124
captured{captured}
147125
{
148126
assert(index > 0 && "snapshot indices need to start at 1");
127+
using St = VirtualMachine::State;
128+
if (state != St::off && state != St::stopped)
129+
throw std::runtime_error{fmt::format("Unsupported VM state in snapshot: {}", static_cast<int>(state))};
130+
if (index < 1)
131+
throw std::runtime_error{fmt::format("Snapshot index not positive: {}", index)};
149132
if (index > max_snapshots)
150-
throw std::runtime_error{fmt::format("Maximum number of snapshots exceeded: {}", max_snapshots)};
151-
133+
throw std::runtime_error{fmt::format("Maximum number of snapshots exceeded: {}", index)};
152134
if (name.empty())
153135
throw std::runtime_error{"Snapshot names cannot be empty"};
154136
if (num_cores < 1)
@@ -224,37 +206,8 @@ QJsonObject mp::BaseSnapshot::serialize() const
224206
QJsonArray json_mounts;
225207
for (const auto& mount : mounts)
226208
{
227-
QJsonObject entry;
228-
entry.insert("source_path", QString::fromStdString(mount.second.source_path));
209+
auto entry = mount.second.serialize();
229210
entry.insert("target_path", QString::fromStdString(mount.first));
230-
231-
QJsonArray uid_mappings;
232-
233-
for (const auto& map : mount.second.uid_mappings)
234-
{
235-
QJsonObject map_entry;
236-
map_entry.insert("host_uid", map.first);
237-
map_entry.insert("instance_uid", map.second);
238-
239-
uid_mappings.append(map_entry);
240-
}
241-
242-
entry.insert("uid_mappings", uid_mappings);
243-
244-
QJsonArray gid_mappings;
245-
246-
for (const auto& map : mount.second.gid_mappings)
247-
{
248-
QJsonObject map_entry;
249-
map_entry.insert("host_gid", map.first);
250-
map_entry.insert("instance_gid", map.second);
251-
252-
gid_mappings.append(map_entry);
253-
}
254-
255-
entry.insert("gid_mappings", gid_mappings);
256-
257-
entry.insert("mount_type", static_cast<int>(mount.second.mount_type));
258211
json_mounts.append(entry);
259212
}
260213

0 commit comments

Comments
 (0)