[lxc-devel] [lxd/master] Storage: VM migration fixes

tomponline on Github lxc-bot at linuxcontainers.org
Wed Apr 8 17:19:51 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 422 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200408/1000456e/attachment-0001.bin>
-------------- next part --------------
From 95193963ff710234baee7553b24d6d43dd550250 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 8 Apr 2020 16:56:54 +0100
Subject: [PATCH 1/4] lxd/storage/drivers/generic/vfs: Log when creating
 snapshots

Helpful when dir driver takes a long time creating snapshot.

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/storage/drivers/generic_vfs.go | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/lxd/storage/drivers/generic_vfs.go b/lxd/storage/drivers/generic_vfs.go
index 56cdd9564b..88d81e8720 100644
--- a/lxd/storage/drivers/generic_vfs.go
+++ b/lxd/storage/drivers/generic_vfs.go
@@ -361,6 +361,7 @@ func genericVFSCreateVolumeFromMigration(d Driver, initVolume func(vol Volume) (
 			}
 
 			// Create the snapshot itself.
+			d.Logger().Debug("Creating snapshot", log.Ctx{"volName": snapVol.Name()})
 			err = d.CreateVolumeSnapshot(snapVol, op)
 			if err != nil {
 				return err
@@ -860,6 +861,7 @@ func genericVFSCopyVolume(d Driver, initVolume func(vol Volume) (func(), error),
 				snapVol := NewVolume(d, d.Name(), vol.volType, vol.contentType, fullSnapName, vol.config, vol.poolConfig)
 
 				// Create the snapshot itself.
+				d.Logger().Debug("Creating snapshot", log.Ctx{"volName": snapVol.Name()})
 				err = d.CreateVolumeSnapshot(snapVol, op)
 				if err != nil {
 					return err

From 81bf97b9220eebca8915a40f6ae7f68d7521a90e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 8 Apr 2020 18:17:16 +0100
Subject: [PATCH 2/4] lxd/storage/drivers/driver/zfs/volumes: Fix migrating VM
 block volumes in MigrateVolume

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/storage/drivers/driver_zfs_volumes.go | 26 +++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/lxd/storage/drivers/driver_zfs_volumes.go b/lxd/storage/drivers/driver_zfs_volumes.go
index e1e11fdddc..c2b7ed1996 100644
--- a/lxd/storage/drivers/driver_zfs_volumes.go
+++ b/lxd/storage/drivers/driver_zfs_volumes.go
@@ -1121,6 +1121,32 @@ func (d *zfs) RenameVolume(vol Volume, newVolName string, op *operations.Operati
 func (d *zfs) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, op *operations.Operation) error {
 	// Handle simple rsync and block_and_rsync through generic.
 	if volSrcArgs.MigrationType.FSType == migration.MigrationFSType_RSYNC || volSrcArgs.MigrationType.FSType == migration.MigrationFSType_BLOCK_AND_RSYNC {
+		// We need to mount the parent volume before calling genericVFSMigrateVolume for two reasons.
+		// 1. In order to get the block device disk path to read from the device must be activated.
+		// 2. If copying snapshots the parent volume must be activated before the snapshot volume's block
+		// device can be made visible.
+		parent, _, _ := shared.InstanceGetParentAndSnapshotName(vol.Name())
+		parentVol := NewVolume(d, d.Name(), vol.volType, vol.contentType, parent, vol.config, vol.poolConfig)
+		ourMount, err := d.MountVolume(parentVol, op)
+		if err != nil {
+			return err
+		}
+		if ourMount {
+			defer d.UnmountVolume(parentVol, op)
+		}
+
+		// In addition to above, if the volume we are sending is a snapshot, we also need to mount that
+		// so that genericVFSMigrateVolume can discover its block device (same reason as 1. above).
+		if vol.IsSnapshot() {
+			ourMount, err = d.MountVolumeSnapshot(vol, op)
+			if err != nil {
+				return err
+			}
+			if ourMount {
+				defer d.UnmountVolumeSnapshot(vol, op)
+			}
+		}
+
 		return genericVFSMigrateVolume(d, d.state, vol, conn, volSrcArgs, op)
 	} else if volSrcArgs.MigrationType.FSType != migration.MigrationFSType_ZFS {
 		return ErrNotSupported

From 4639ebf9fc73522537cf57094de17be5def76105 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 8 Apr 2020 18:17:52 +0100
Subject: [PATCH 3/4] lxd/storage/memorypipe: Adds context support for
 cancellation

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/storage/memorypipe/memory_pipe.go | 44 ++++++++++++++++++++-------
 1 file changed, 33 insertions(+), 11 deletions(-)

diff --git a/lxd/storage/memorypipe/memory_pipe.go b/lxd/storage/memorypipe/memory_pipe.go
index 79a1b03c6e..d8628816b3 100644
--- a/lxd/storage/memorypipe/memory_pipe.go
+++ b/lxd/storage/memorypipe/memory_pipe.go
@@ -1,6 +1,8 @@
 package memorypipe
 
 import (
+	"context"
+	"fmt"
 	"io"
 )
 
@@ -21,17 +23,22 @@ type msg struct {
 // connection to be used for multiple sessions.
 type pipe struct {
 	ch       chan msg
+	ctx      context.Context
 	otherEnd *pipe
 }
 
 // Read reads from the pipe into p. Returns number of bytes read and any errors.
 func (p *pipe) Read(b []byte) (int, error) {
-	msg := <-p.ch
-	if msg.err == io.EOF {
-		return -1, msg.err
+	select {
+	case msg := <-p.ch:
+		if msg.err == io.EOF {
+			return -1, msg.err
+		}
+		n := copy(b, msg.data)
+		return n, msg.err
+	case <-p.ctx.Done():
+		return -1, fmt.Errorf("Context done")
 	}
-	n := copy(b, msg.data)
-	return n, msg.err
 }
 
 // Write writes to the pipe from p. Returns number of bytes written and any errors.
@@ -40,8 +47,13 @@ func (p *pipe) Write(b []byte) (int, error) {
 		data: append(b[:0:0], b...), // Create copy of b in case it is modified externally.
 		err:  nil,
 	}
-	p.otherEnd.ch <- msg // Send msg to the other side's Read function.
-	return len(msg.data), msg.err
+
+	select {
+	case p.otherEnd.ch <- msg: // Sent msg to the other side's Read function.
+		return len(msg.data), msg.err
+	case <-p.ctx.Done():
+		return -1, fmt.Errorf("Context done")
+	}
 }
 
 // Close is unusual in that it doesn't actually close the pipe. Instead it sends an io.EOF error
@@ -49,23 +61,33 @@ func (p *pipe) Write(b []byte) (int, error) {
 // Each call to Close will indicate to the other side that a session has ended, whilst allowing the
 // reuse of a single persistent pipe for multiple sessions.
 func (p *pipe) Close() error {
-	p.otherEnd.ch <- msg{
+	msg := msg{
 		data: nil,
 		err:  io.EOF, // Indicates to the other side's Read function that session has ended.
 	}
+
+	select {
+	case p.otherEnd.ch <- msg: // Sent msg to the other side's Read function.
+		return nil
+	case <-p.ctx.Done():
+		return fmt.Errorf("Context done")
+	}
+
 	return nil
 }
 
 // NewPipePair returns a pair of io.ReadWriterCloser pipes that are connected together such that
 // writes to one will appear as reads on the other and vice versa. Calling Close() on one end will
 // indicate to the other end that the session has ended.
-func NewPipePair() (io.ReadWriteCloser, io.ReadWriteCloser) {
+func NewPipePair(ctx context.Context) (io.ReadWriteCloser, io.ReadWriteCloser) {
 	aEnd := &pipe{
-		ch: make(chan msg, bufferSize),
+		ch:  make(chan msg, bufferSize),
+		ctx: ctx,
 	}
 
 	bEnd := &pipe{
-		ch: make(chan msg, bufferSize),
+		ch:  make(chan msg, bufferSize),
+		ctx: ctx,
 	}
 
 	aEnd.otherEnd = bEnd

From d2a58f17e570e76517a74204cffd45dcb3d73c0e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 8 Apr 2020 18:18:14 +0100
Subject: [PATCH 4/4] lxd/storage/backend/lxd: memorypipe cancellation usage

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/storage/backend_lxd.go | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 4621290a7f..06db8dd65a 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -1,6 +1,7 @@
 package storage
 
 import (
+	"context"
 	"fmt"
 	"io"
 	"os"
@@ -679,8 +680,10 @@ func (b *lxdBackend) CreateInstanceFromCopy(inst instance.Instance, src instance
 			}
 		}
 
+		ctx, cancel := context.WithCancel(context.Background())
+
 		// Use in-memory pipe pair to simulate a connection between the sender and receiver.
-		aEnd, bEnd := memorypipe.NewPipePair()
+		aEnd, bEnd := memorypipe.NewPipePair(ctx)
 
 		// Negotiate the migration type to use.
 		offeredTypes := srcPool.MigrationTypes(contentType, false)
@@ -701,6 +704,7 @@ func (b *lxdBackend) CreateInstanceFromCopy(inst instance.Instance, src instance
 				TrackProgress: true, // Do use a progress tracker on sender.
 			}, op)
 
+			cancel()
 			aEndErrCh <- err
 		}()
 
@@ -712,6 +716,7 @@ func (b *lxdBackend) CreateInstanceFromCopy(inst instance.Instance, src instance
 				TrackProgress: false, // Do not use a progress tracker on receiver.
 			}, op)
 
+			cancel()
 			bEndErrCh <- err
 		}()
 
@@ -820,8 +825,10 @@ func (b *lxdBackend) RefreshInstance(inst instance.Instance, src instance.Instan
 			snapshotNames = append(snapshotNames, snapShotName)
 		}
 
+		ctx, cancel := context.WithCancel(context.Background())
+
 		// Use in-memory pipe pair to simulate a connection between the sender and receiver.
-		aEnd, bEnd := memorypipe.NewPipePair()
+		aEnd, bEnd := memorypipe.NewPipePair(ctx)
 
 		// Negotiate the migration type to use.
 		offeredTypes := srcPool.MigrationTypes(contentType, true)
@@ -842,6 +849,7 @@ func (b *lxdBackend) RefreshInstance(inst instance.Instance, src instance.Instan
 				TrackProgress: true, // Do use a progress tracker on sender.
 			}, op)
 
+			cancel()
 			aEndErrCh <- err
 		}()
 
@@ -854,6 +862,7 @@ func (b *lxdBackend) RefreshInstance(inst instance.Instance, src instance.Instan
 				TrackProgress: false, // Do not use a progress tracker on receiver.
 			}, op)
 
+			cancel()
 			bEndErrCh <- err
 		}()
 
@@ -2261,8 +2270,10 @@ func (b *lxdBackend) CreateCustomVolumeFromCopy(projectName string, volName stri
 	// to negotiate a common transfer method between pool types.
 	logger.Debug("CreateCustomVolumeFromCopy cross-pool mode detected")
 
+	ctx, cancel := context.WithCancel(context.Background())
+
 	// Use in-memory pipe pair to simulate a connection between the sender and receiver.
-	aEnd, bEnd := memorypipe.NewPipePair()
+	aEnd, bEnd := memorypipe.NewPipePair(ctx)
 
 	// Negotiate the migration type to use.
 	offeredTypes := srcPool.MigrationTypes(drivers.ContentTypeFS, false)
@@ -2283,6 +2294,7 @@ func (b *lxdBackend) CreateCustomVolumeFromCopy(projectName string, volName stri
 			TrackProgress: true, // Do use a progress tracker on sender.
 		}, op)
 
+		cancel()
 		aEndErrCh <- err
 	}()
 
@@ -2297,6 +2309,7 @@ func (b *lxdBackend) CreateCustomVolumeFromCopy(projectName string, volName stri
 
 		}, op)
 
+		cancel()
 		bEndErrCh <- err
 	}()
 


More information about the lxc-devel mailing list