[lxc-devel] [lxd/master] fix zfs container copy

brauner on Github lxc-bot at linuxcontainers.org
Thu Jun 8 20:33:23 UTC 2017


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 364 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20170608/1b919274/attachment.bin>
-------------- next part --------------
From b1eb7453d3911644a9b6d894de7e6dedd241b17d Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Thu, 8 Jun 2017 22:01:24 +0200
Subject: [PATCH 1/3] zfs: fix container copy

Closes #3395.

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 lxd/storage_zfs.go | 47 +++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 45 insertions(+), 2 deletions(-)

diff --git a/lxd/storage_zfs.go b/lxd/storage_zfs.go
index 37697fe63..dbe421525 100644
--- a/lxd/storage_zfs.go
+++ b/lxd/storage_zfs.go
@@ -958,7 +958,7 @@ func (s *storageZfs) copyWithSnapshots(target container, source container, paren
 
 	zfsSendCmd := exec.Command("zfs", args...)
 	targetSnapshotDataset := fmt.Sprintf("%s/containers/%s at snapshot-%s", poolName, targetParentName, targetSnapOnlyName)
-	zfsRecvCmd := exec.Command("zfs", "receive", targetSnapshotDataset)
+	zfsRecvCmd := exec.Command("zfs", "receive", "-F", targetSnapshotDataset)
 
 	zfsRecvCmd.Stdin, _ = zfsSendCmd.StdoutPipe()
 	zfsRecvCmd.Stdout = os.Stdout
@@ -1019,8 +1019,9 @@ func (s *storageZfs) ContainerCopy(target container, source container, container
 			return err
 		}
 
+		prev := ""
+		prevSnapOnlyName := ""
 		for i, snap := range snapshots {
-			prev := ""
 			if i > 0 {
 				prev = snapshots[i-1].Name()
 			}
@@ -1031,6 +1032,7 @@ func (s *storageZfs) ContainerCopy(target container, source container, container
 			}
 
 			_, snapOnlyName, _ := containerGetParentAndSnapshotName(snap.Name())
+			prevSnapOnlyName = snapOnlyName
 			newSnapName := fmt.Sprintf("%s/%s", target.Name(), snapOnlyName)
 			targetSnapshot, err := containerLoadByName(s.d, newSnapName)
 			if err != nil {
@@ -1043,6 +1045,47 @@ func (s *storageZfs) ContainerCopy(target container, source container, container
 			}
 		}
 
+		// send actual container
+		tmpSnapshotName := fmt.Sprintf("copy-send-%s", uuid.NewRandom().String())
+		err = s.zfsPoolVolumeSnapshotCreate(fmt.Sprintf("containers/%s", source.Name()), tmpSnapshotName)
+		if err != nil {
+			return err
+		}
+
+		poolName := s.getOnDiskPoolName()
+		currentSnapshotDataset := fmt.Sprintf("%s/containers/%s@%s", poolName, source.Name(), tmpSnapshotName)
+		args := []string{"send", currentSnapshotDataset}
+		if prevSnapOnlyName != "" {
+			parentSnapshotDataset := fmt.Sprintf("%s/containers/%s at snapshot-%s", poolName, source.Name(), prevSnapOnlyName)
+			args = append(args, "-i", parentSnapshotDataset)
+		}
+
+		zfsSendCmd := exec.Command("zfs", args...)
+		targetSnapshotDataset := fmt.Sprintf("%s/containers/%s@%s", poolName, target.Name(), tmpSnapshotName)
+		zfsRecvCmd := exec.Command("zfs", "receive", "-F", targetSnapshotDataset)
+
+		zfsRecvCmd.Stdin, _ = zfsSendCmd.StdoutPipe()
+		zfsRecvCmd.Stdout = os.Stdout
+		zfsRecvCmd.Stderr = os.Stderr
+
+		err = zfsRecvCmd.Start()
+		if err != nil {
+			return err
+		}
+
+		err = zfsSendCmd.Run()
+		if err != nil {
+			return err
+		}
+
+		err = zfsRecvCmd.Wait()
+		if err != nil {
+			return err
+		}
+
+		s.zfsPoolVolumeSnapshotDestroy(fmt.Sprintf("containers/%s", source.Name()), tmpSnapshotName)
+		s.zfsPoolVolumeSnapshotDestroy(fmt.Sprintf("containers/%s", target.Name()), tmpSnapshotName)
+
 		fs := fmt.Sprintf("containers/%s", target.Name())
 		err = s.zfsPoolVolumeSet(fs, "mountpoint", targetContainerMountPoint)
 		if err != nil {

From 17de1869158f22d7a77e6e33f46fa51100af9d1d Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Thu, 8 Jun 2017 22:21:55 +0200
Subject: [PATCH 2/3] storage: {copy,move} bugfixes

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 lxd/migrate.go           |  2 +-
 lxd/storage_btrfs.go     | 22 ++++++++++++----------
 lxd/storage_migration.go | 33 ++++++++++++++++++---------------
 lxd/storage_zfs.go       | 27 ++++++++++++++++-----------
 4 files changed, 47 insertions(+), 37 deletions(-)

diff --git a/lxd/migrate.go b/lxd/migrate.go
index 5fa7d1db2..244ee1d73 100644
--- a/lxd/migrate.go
+++ b/lxd/migrate.go
@@ -401,7 +401,7 @@ func (s *migrationSourceWs) Do(migrateOp *operation) error {
 		return err
 	}
 
-	err = driver.SendWhileRunning(s.fsConn, migrateOp, bwlimit)
+	err = driver.SendWhileRunning(s.fsConn, migrateOp, bwlimit, s.containerOnly)
 	if err != nil {
 		return abort(err)
 	}
diff --git a/lxd/storage_btrfs.go b/lxd/storage_btrfs.go
index 89f49ff4f..a403b5e8e 100644
--- a/lxd/storage_btrfs.go
+++ b/lxd/storage_btrfs.go
@@ -1770,7 +1770,7 @@ func (s *btrfsMigrationSourceDriver) send(conn *websocket.Conn, btrfsPath string
 	return err
 }
 
-func (s *btrfsMigrationSourceDriver) SendWhileRunning(conn *websocket.Conn, op *operation, bwlimit string) error {
+func (s *btrfsMigrationSourceDriver) SendWhileRunning(conn *websocket.Conn, op *operation, bwlimit string, containerOnly bool) error {
 	_, containerPool := s.container.Storage().GetContainerPoolInfo()
 	containerName := s.container.Name()
 	containersPath := getContainerMountPoint(containerPool, "")
@@ -1806,16 +1806,18 @@ func (s *btrfsMigrationSourceDriver) SendWhileRunning(conn *websocket.Conn, op *
 		return s.send(conn, migrationSendSnapshot, "", wrapper)
 	}
 
-	for i, snap := range s.snapshots {
-		prev := ""
-		if i > 0 {
-			prev = getSnapshotMountPoint(containerPool, s.snapshots[i-1].Name())
-		}
+	if !containerOnly {
+		for i, snap := range s.snapshots {
+			prev := ""
+			if i > 0 {
+				prev = getSnapshotMountPoint(containerPool, s.snapshots[i-1].Name())
+			}
 
-		snapMntPoint := getSnapshotMountPoint(containerPool, snap.Name())
-		wrapper := StorageProgressReader(op, "fs_progress", snap.Name())
-		if err := s.send(conn, snapMntPoint, prev, wrapper); err != nil {
-			return err
+			snapMntPoint := getSnapshotMountPoint(containerPool, snap.Name())
+			wrapper := StorageProgressReader(op, "fs_progress", snap.Name())
+			if err := s.send(conn, snapMntPoint, prev, wrapper); err != nil {
+				return err
+			}
 		}
 	}
 
diff --git a/lxd/storage_migration.go b/lxd/storage_migration.go
index 4f1e095c5..c49f2b373 100644
--- a/lxd/storage_migration.go
+++ b/lxd/storage_migration.go
@@ -18,7 +18,7 @@ type MigrationStorageSourceDriver interface {
 	/* send any bits of the container/snapshots that are possible while the
 	 * container is still running.
 	 */
-	SendWhileRunning(conn *websocket.Conn, op *operation, bwlimit string) error
+	SendWhileRunning(conn *websocket.Conn, op *operation, bwlimit string, containerOnly bool) error
 
 	/* send the final bits (e.g. a final delta snapshot for zfs, btrfs, or
 	 * do a final rsync) of the fs after the container has been
@@ -42,22 +42,25 @@ func (s rsyncStorageSourceDriver) Snapshots() []container {
 	return s.snapshots
 }
 
-func (s rsyncStorageSourceDriver) SendWhileRunning(conn *websocket.Conn, op *operation, bwlimit string) error {
+func (s rsyncStorageSourceDriver) SendWhileRunning(conn *websocket.Conn, op *operation, bwlimit string, containerOnly bool) error {
 	ctName, _, _ := containerGetParentAndSnapshotName(s.container.Name())
-	for _, send := range s.snapshots {
-		ourStart, err := send.StorageStart()
-		if err != nil {
-			return err
-		}
-		if ourStart {
-			defer send.StorageStop()
-		}
 
-		path := send.Path()
-		wrapper := StorageProgressReader(op, "fs_progress", send.Name())
-		err = RsyncSend(ctName, shared.AddSlash(path), conn, wrapper, bwlimit)
-		if err != nil {
-			return err
+	if !containerOnly {
+		for _, send := range s.snapshots {
+			ourStart, err := send.StorageStart()
+			if err != nil {
+				return err
+			}
+			if ourStart {
+				defer send.StorageStop()
+			}
+
+			path := send.Path()
+			wrapper := StorageProgressReader(op, "fs_progress", send.Name())
+			err = RsyncSend(ctName, shared.AddSlash(path), conn, wrapper, bwlimit)
+			if err != nil {
+				return err
+			}
 		}
 	}
 
diff --git a/lxd/storage_zfs.go b/lxd/storage_zfs.go
index dbe421525..dea7044f5 100644
--- a/lxd/storage_zfs.go
+++ b/lxd/storage_zfs.go
@@ -2510,7 +2510,7 @@ func (s *zfsMigrationSourceDriver) send(conn *websocket.Conn, zfsName string, zf
 	return err
 }
 
-func (s *zfsMigrationSourceDriver) SendWhileRunning(conn *websocket.Conn, op *operation, bwlimit string) error {
+func (s *zfsMigrationSourceDriver) SendWhileRunning(conn *websocket.Conn, op *operation, bwlimit string, containerOnly bool) error {
 	if s.container.IsSnapshot() {
 		_, snapOnlyName, _ := containerGetParentAndSnapshotName(s.container.Name())
 		snapshotName := fmt.Sprintf("snapshot-%s", snapOnlyName)
@@ -2519,18 +2519,19 @@ func (s *zfsMigrationSourceDriver) SendWhileRunning(conn *websocket.Conn, op *op
 	}
 
 	lastSnap := ""
+	if !containerOnly {
+		for i, snap := range s.zfsSnapshotNames {
+			prev := ""
+			if i > 0 {
+				prev = s.zfsSnapshotNames[i-1]
+			}
 
-	for i, snap := range s.zfsSnapshotNames {
-		prev := ""
-		if i > 0 {
-			prev = s.zfsSnapshotNames[i-1]
-		}
-
-		lastSnap = snap
+			lastSnap = snap
 
-		wrapper := StorageProgressReader(op, "fs_progress", snap)
-		if err := s.send(conn, snap, prev, wrapper); err != nil {
-			return err
+			wrapper := StorageProgressReader(op, "fs_progress", snap)
+			if err := s.send(conn, snap, prev, wrapper); err != nil {
+				return err
+			}
 		}
 	}
 
@@ -2593,6 +2594,10 @@ func (s *storageZfs) MigrationSource(ct container, containerOnly bool) (Migratio
 		zfs:              s,
 	}
 
+	if containerOnly {
+		return &driver, nil
+	}
+
 	/* List all the snapshots in order of reverse creation. The idea here
 	* is that we send the oldest to newest snapshot, hopefully saving on
 	* xfer costs. Then, after all that, we send the container itself.

From 4263ec402833cd45b0705e5448a560f610756691 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Thu, 8 Jun 2017 22:32:28 +0200
Subject: [PATCH 3/3] test: add more {copy,migration} tests

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 test/suites/migration.sh | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/test/suites/migration.sh b/test/suites/migration.sh
index 20b75addb..6a75cf4ec 100644
--- a/test/suites/migration.sh
+++ b/test/suites/migration.sh
@@ -128,27 +128,33 @@ migration() {
 
   # Test container only copies
   lxc init testimage cccp
+  echo "before" | lxc file push - cccp/blah
   lxc snapshot cccp
   lxc snapshot cccp
+  echo "after" | lxc file push - cccp/blah
 
   # Local container only copy.
   lxc copy cccp udssr --container-only
   [ "$(lxc info udssr | grep -c snap)" -eq 0 ]
+  [ "$(lxc file pull udssr/blah -)" = "after" ]
   lxc delete udssr
 
   # Local container with snapshots copy.
   lxc copy cccp udssr
   [ "$(lxc info udssr | grep -c snap)" -eq 2 ]
+  [ "$(lxc file pull udssr/blah -)" = "after" ]
   lxc delete udssr
 
   # Remote container only copy.
   lxc_remote copy l1:cccp l2:udssr --container-only
   [ "$(lxc_remote info l2:udssr | grep -c snap)" -eq 0 ]
+  [ "$(lxc_remote file pull l2:udssr/blah -)" = "after" ]
   lxc_remote delete l2:udssr
 
   # Remote container with snapshots copy.
   lxc_remote copy l1:cccp l2:udssr
   [ "$(lxc_remote info l2:udssr | grep -c snap)" -eq 2 ]
+  [ "$(lxc_remote file pull l2:udssr/blah -)" = "after" ]
   lxc_remote delete l2:udssr
 
   # Remote container only move.


More information about the lxc-devel mailing list