[lxc-devel] [lxd/master] VM snapshot handling
stgraber on Github
lxc-bot at linuxcontainers.org
Sun Jan 19 14:54:39 UTC 2020
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 301 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200119/cf6dd0a8/attachment.bin>
-------------- next part --------------
From 1c3e543c4011f3eeab41fbae671b5306f00656b5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Sun, 19 Jan 2020 14:27:55 +0200
Subject: [PATCH 1/4] lxd/vm: Fix incorrect bootindex
---
lxd/instance/drivers/vm_qemu.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lxd/instance/drivers/vm_qemu.go b/lxd/instance/drivers/vm_qemu.go
index 2b71e0049a..4f14990274 100644
--- a/lxd/instance/drivers/vm_qemu.go
+++ b/lxd/instance/drivers/vm_qemu.go
@@ -1494,7 +1494,7 @@ netdev = "lxd_%s"
mac = "%s"
bus = "qemu_pcie%d"
addr = "0x0"
-bootindex = "%d""
+bootindex = "%d"
`, devName, devName, devTap, 5+nicIndex, 14+nicIndex, 5+nicIndex, 4+nicIndex, devName, devName, devHwaddr, 5+nicIndex, 2+nicIndex))
return
From 6f10f885ea6b1de916c93a95d861dc81c1eec177 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Sun, 19 Jan 2020 16:32:35 +0200
Subject: [PATCH 2/4] lxd/vm: Implement snapshot restore
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
lxd/instance/drivers/vm_qemu.go | 113 +++++++++++++++++++++++++++++++-
1 file changed, 112 insertions(+), 1 deletion(-)
diff --git a/lxd/instance/drivers/vm_qemu.go b/lxd/instance/drivers/vm_qemu.go
index 4f14990274..ee9a218112 100644
--- a/lxd/instance/drivers/vm_qemu.go
+++ b/lxd/instance/drivers/vm_qemu.go
@@ -1611,7 +1611,118 @@ func (vm *qemu) IsPrivileged() bool {
// Restore restores an instance snapshot.
func (vm *qemu) Restore(source instance.Instance, stateful bool) error {
- return fmt.Errorf("Restore Not implemented")
+ if stateful {
+ return fmt.Errorf("Stateful snapshots of VMs aren't supported yet")
+ }
+
+ var ctxMap log.Ctx
+
+ // Load the storage driver
+ pool, err := storagePools.GetPoolByInstance(vm.state, vm)
+ if err != nil {
+ return err
+ }
+
+ // Ensure that storage is mounted for backup.yaml updates.
+ ourStart, err := pool.MountInstance(vm, nil)
+ if err != nil {
+ return err
+ }
+ if ourStart {
+ defer pool.UnmountInstance(vm, nil)
+ }
+
+ // Stop the instance.
+ wasRunning := false
+ if vm.IsRunning() {
+ wasRunning = true
+
+ ephemeral := vm.IsEphemeral()
+ if ephemeral {
+ // Unset ephemeral flag.
+ args := db.InstanceArgs{
+ Architecture: vm.Architecture(),
+ Config: vm.LocalConfig(),
+ Description: vm.Description(),
+ Devices: vm.LocalDevices(),
+ Ephemeral: false,
+ Profiles: vm.Profiles(),
+ Project: vm.Project(),
+ Type: vm.Type(),
+ Snapshot: vm.IsSnapshot(),
+ }
+
+ err := vm.Update(args, false)
+ if err != nil {
+ return err
+ }
+
+ // On function return, set the flag back on.
+ defer func() {
+ args.Ephemeral = ephemeral
+ vm.Update(args, true)
+ }()
+ }
+
+ // This will unmount the instance storage.
+ err := vm.Stop(false)
+ if err != nil {
+ return err
+ }
+ }
+
+ ctxMap = log.Ctx{
+ "project": vm.project,
+ "name": vm.name,
+ "created": vm.creationDate,
+ "ephemeral": vm.ephemeral,
+ "used": vm.lastUsedDate,
+ "source": source.Name()}
+
+ logger.Info("Restoring instance", ctxMap)
+
+ // Restore the rootfs.
+ err = pool.RestoreInstanceSnapshot(vm, source, nil)
+ if err != nil {
+ return err
+ }
+
+ // Restore the configuration.
+ args := db.InstanceArgs{
+ Architecture: source.Architecture(),
+ Config: source.LocalConfig(),
+ Description: source.Description(),
+ Devices: source.LocalDevices(),
+ Ephemeral: source.IsEphemeral(),
+ Profiles: source.Profiles(),
+ Project: source.Project(),
+ Type: source.Type(),
+ Snapshot: source.IsSnapshot(),
+ }
+
+ err = vm.Update(args, false)
+ if err != nil {
+ logger.Error("Failed restoring instance configuration", ctxMap)
+ return err
+ }
+
+ // The old backup file may be out of date (e.g. it doesn't have all the current snapshots of
+ // the instance listed); let's write a new one to be safe.
+ err = vm.UpdateBackupFile()
+ if err != nil {
+ return err
+ }
+
+ vm.state.Events.SendLifecycle(vm.project, "virtual-machine-snapshot-restored", fmt.Sprintf("/1.0/virtual-machines/%s", vm.name), map[string]interface{}{"snapshot_name": vm.name})
+
+ // Restart the insance.
+ if wasRunning {
+ logger.Info("Restored instance", ctxMap)
+ return vm.Start(false)
+ }
+
+ logger.Info("Restored instance", ctxMap)
+ return nil
}
// Snapshots returns a list of snapshots.
From dcbb7d6979512152b0d0fff81d3d13a7e5d2701e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Sun, 19 Jan 2020 16:53:06 +0200
Subject: [PATCH 3/4] lxd/instance: Move LoadAllInternal
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
lxd/container.go | 57 ++--------------------------------
lxd/container_lxc.go | 2 +-
lxd/instance/instance_utils.go | 51 ++++++++++++++++++++++++++++++
3 files changed, 55 insertions(+), 55 deletions(-)
diff --git a/lxd/container.go b/lxd/container.go
index b8281e8114..e86439c2df 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -30,7 +30,6 @@ import (
storageDrivers "github.com/lxc/lxd/lxd/storage/drivers"
"github.com/lxc/lxd/lxd/task"
"github.com/lxc/lxd/shared"
- "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/ioprogress"
log "github.com/lxc/lxd/shared/log15"
"github.com/lxc/lxd/shared/logger"
@@ -963,7 +962,7 @@ func instanceLoadByProject(s *state.State, project string) ([]instance.Instance,
return nil, err
}
- return instanceLoadAllInternal(cts, s)
+ return instance.LoadAllInternal(s, cts)
}
// Load all instances across all projects.
@@ -1013,7 +1012,7 @@ func instanceLoadNodeAll(s *state.State, instanceType instancetype.Type) ([]inst
return nil, err
}
- return instanceLoadAllInternal(insts, s)
+ return instance.LoadAllInternal(s, insts)
}
// Load all instances of this nodes under the given project.
@@ -1033,57 +1032,7 @@ func instanceLoadNodeProjectAll(s *state.State, project string, instanceType ins
return nil, err
}
- return instanceLoadAllInternal(cts, s)
-}
-
-func instanceLoadAllInternal(dbInstances []db.Instance, s *state.State) ([]instance.Instance, error) {
- // Figure out what profiles are in use
- profiles := map[string]map[string]api.Profile{}
- for _, instArgs := range dbInstances {
- projectProfiles, ok := profiles[instArgs.Project]
- if !ok {
- projectProfiles = map[string]api.Profile{}
- profiles[instArgs.Project] = projectProfiles
- }
- for _, profile := range instArgs.Profiles {
- _, ok := projectProfiles[profile]
- if !ok {
- projectProfiles[profile] = api.Profile{}
- }
- }
- }
-
- // Get the profile data
- for project, projectProfiles := range profiles {
- for name := range projectProfiles {
- _, profile, err := s.Cluster.ProfileGet(project, name)
- if err != nil {
- return nil, err
- }
-
- projectProfiles[name] = *profile
- }
- }
-
- // Load the instances structs
- instances := []instance.Instance{}
- for _, dbInstance := range dbInstances {
- // Figure out the instances's profiles
- cProfiles := []api.Profile{}
- for _, name := range dbInstance.Profiles {
- cProfiles = append(cProfiles, profiles[dbInstance.Project][name])
- }
-
- args := db.InstanceToArgs(&dbInstance)
- inst, err := instance.Load(s, args, cProfiles)
- if err != nil {
- return nil, err
- }
-
- instances = append(instances, inst)
- }
-
- return instances, nil
+ return instance.LoadAllInternal(s, cts)
}
func autoCreateContainerSnapshotsTask(d *Daemon) (task.Func, task.Schedule) {
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index ca6e59c495..df12b17c21 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -3168,7 +3168,7 @@ func (c *containerLXC) Snapshots() ([]instance.Instance, error) {
}
// Build the snapshot list
- containers, err := instanceLoadAllInternal(snaps, c.state)
+ containers, err := instance.LoadAllInternal(c.state, snaps)
if err != nil {
return nil, err
}
diff --git a/lxd/instance/instance_utils.go b/lxd/instance/instance_utils.go
index 86a0b3724e..34e62a4c06 100644
--- a/lxd/instance/instance_utils.go
+++ b/lxd/instance/instance_utils.go
@@ -465,6 +465,57 @@ func LoadByProjectAndName(s *state.State, project, name string) (Instance, error
return inst, nil
}
+// LoadAllInternal loads a list of db insances into a list of instances.
+func LoadAllInternal(s *state.State, dbInstances []db.Instance) ([]Instance, error) {
+ // Figure out what profiles are in use
+ profiles := map[string]map[string]api.Profile{}
+ for _, instArgs := range dbInstances {
+ projectProfiles, ok := profiles[instArgs.Project]
+ if !ok {
+ projectProfiles = map[string]api.Profile{}
+ profiles[instArgs.Project] = projectProfiles
+ }
+ for _, profile := range instArgs.Profiles {
+ _, ok := projectProfiles[profile]
+ if !ok {
+ projectProfiles[profile] = api.Profile{}
+ }
+ }
+ }
+
+ // Get the profile data
+ for project, projectProfiles := range profiles {
+ for name := range projectProfiles {
+ _, profile, err := s.Cluster.ProfileGet(project, name)
+ if err != nil {
+ return nil, err
+ }
+
+ projectProfiles[name] = *profile
+ }
+ }
+
+ // Load the instances structs
+ instances := []Instance{}
+ for _, dbInstance := range dbInstances {
+ // Figure out the instances's profiles
+ cProfiles := []api.Profile{}
+ for _, name := range dbInstance.Profiles {
+ cProfiles = append(cProfiles, profiles[dbInstance.Project][name])
+ }
+
+ args := db.InstanceToArgs(&dbInstance)
+ inst, err := Load(s, args, cProfiles)
+ if err != nil {
+ return nil, err
+ }
+
+ instances = append(instances, inst)
+ }
+
+ return instances, nil
+}
+
// WriteBackupFile writes instance's config to a file. Deprecated, use inst.UpdateBackupFile().
func WriteBackupFile(state *state.State, inst Instance) error {
// We only write backup files out for actual instances.
From 69e9e755365c12a6ed061912f65ad6aacd3e2a93 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Sun, 19 Jan 2020 16:53:14 +0200
Subject: [PATCH 4/4] lxd/vm: Implement Snapshots
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
lxd/instance/drivers/vm_qemu.go | 33 ++++++++++++++++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/lxd/instance/drivers/vm_qemu.go b/lxd/instance/drivers/vm_qemu.go
index ee9a218112..a204477198 100644
--- a/lxd/instance/drivers/vm_qemu.go
+++ b/lxd/instance/drivers/vm_qemu.go
@@ -1727,7 +1727,38 @@ func (vm *qemu) Restore(source instance.Instance, stateful bool) error {
// Snapshots returns a list of snapshots.
func (vm *qemu) Snapshots() ([]instance.Instance, error) {
- return []instance.Instance{}, nil
+ var snaps []db.Instance
+
+ if vm.IsSnapshot() {
+ return []instance.Instance{}, nil
+ }
+
+ // Get all the snapshots
+ err := vm.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
+ var err error
+ snaps, err = tx.ContainerGetSnapshotsFull(vm.Project(), vm.name)
+ if err != nil {
+ return err
+ }
+
+ return nil
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ // Build the snapshot list
+ snapshots, err := instance.LoadAllInternal(vm.state, snaps)
+ if err != nil {
+ return nil, err
+ }
+
+ instances := make([]instance.Instance, len(snapshots))
+ for k, v := range snapshots {
+ instances[k] = instance.Instance(v)
+ }
+
+ return instances, nil
}
// Backups returns a list of backups.
More information about the lxc-devel
mailing list