[lxc-devel] [lxd/master] Bugfixes

stgraber on Github lxc-bot at linuxcontainers.org
Tue Mar 7 18:16:32 UTC 2017


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/20170307/ead39125/attachment.bin>
-------------- next part --------------
From 670bc354f55bfff51df687a06ee10c737434fcbf Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Tue, 7 Mar 2017 12:44:44 +0100
Subject: [PATCH 1/3] lvm: dumb down functions from methods to functions

This way we can call them from other places as well.

Closes #3026.

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 lxd/storage_lvm.go | 137 ++++++++++++++++++++++++++++++-----------------------
 1 file changed, 79 insertions(+), 58 deletions(-)

diff --git a/lxd/storage_lvm.go b/lxd/storage_lvm.go
index 2f3e364..89a245a 100644
--- a/lxd/storage_lvm.go
+++ b/lxd/storage_lvm.go
@@ -391,8 +391,8 @@ func versionSplit(versionString string) (int, int, int, error) {
 	return maj, min, inc, nil
 }
 
-func (s *storageLvm) lvmVersionIsAtLeast(versionString string) (bool, error) {
-	lvmVersion := strings.Split(s.sTypeVersion, "/")[0]
+func lvmVersionIsAtLeast(sTypeVersion string, versionString string) (bool, error) {
+	lvmVersion := strings.Split(sTypeVersion, "/")[0]
 
 	lvmMaj, lvmMin, lvmInc, err := versionSplit(lvmVersion)
 	if err != nil {
@@ -716,7 +716,12 @@ func (s *storageLvm) StoragePoolVolumeCreate() error {
 		return err
 	}
 
-	err = s.createThinLV(poolName, thinPoolName, s.volume.Name, lvFsType, lvSize, volumeType)
+	err = lvmCreateThinpool(s.d, s.sTypeVersion, poolName, thinPoolName, lvFsType)
+	if err != nil {
+		return err
+	}
+
+	err = lvmCreateThinLV(poolName, thinPoolName, s.volume.Name, lvFsType, lvSize, volumeType)
 	if err != nil {
 		shared.LogErrorf("LVMCreateThinLV: %s.", err)
 		return fmt.Errorf("Error Creating LVM LV for new image: %v", err)
@@ -1028,7 +1033,12 @@ func (s *storageLvm) ContainerCreate(container container) error {
 	}
 
 	poolName := s.getOnDiskPoolName()
-	err = s.createThinLV(poolName, thinPoolName, containerLvmName, lvFsType, lvSize, storagePoolVolumeApiEndpointContainers)
+	err = lvmCreateThinpool(s.d, s.sTypeVersion, poolName, thinPoolName, lvFsType)
+	if err != nil {
+		return err
+	}
+
+	err = lvmCreateThinLV(poolName, thinPoolName, containerLvmName, lvFsType, lvSize, storagePoolVolumeApiEndpointContainers)
 	if err != nil {
 		return err
 	}
@@ -1741,7 +1751,12 @@ func (s *storageLvm) ImageCreate(fingerprint string) error {
 		}
 	}()
 
-	err = s.createThinLV(poolName, thinPoolName, fingerprint, lvFsType, lvSize, storagePoolVolumeApiEndpointImages)
+	err = lvmCreateThinpool(s.d, s.sTypeVersion, poolName, thinPoolName, lvFsType)
+	if err != nil {
+		return err
+	}
+
+	err = lvmCreateThinLV(poolName, thinPoolName, fingerprint, lvFsType, lvSize, storagePoolVolumeApiEndpointImages)
 	if err != nil {
 		shared.LogErrorf("LVMCreateThinLV: %s.", err)
 		return fmt.Errorf("Error Creating LVM LV for new image: %v", err)
@@ -1856,25 +1871,72 @@ func (s *storageLvm) ImageUmount(fingerprint string) (bool, error) {
 	return true, nil
 }
 
-func (s *storageLvm) createThinLV(vgName string, thinPoolName string, lvName string, lvFsType string, lvSize string, volumeType string) error {
-	exists, err := storageLVMThinpoolExists(vgName, thinPoolName)
+func createDefaultThinPool(sTypeVersion string, vgName string, thinPoolName string, lvFsType string) error {
+	isRecent, err := lvmVersionIsAtLeast(sTypeVersion, "2.02.99")
 	if err != nil {
-		return err
+		return fmt.Errorf("Error checking LVM version: %s", err)
 	}
 
-	if !exists {
-		err := s.createDefaultThinPool(vgName, thinPoolName, lvName, lvFsType)
-		if err != nil {
-			return err
-		}
+	// Create the thin pool
+	lvmThinPool := fmt.Sprintf("%s/%s", vgName, thinPoolName)
+	var output string
+	if isRecent {
+		output, err = shared.TryRunCommand(
+			"lvcreate",
+			"--poolmetadatasize", "1G",
+			"-l", "100%FREE",
+			"--thinpool", lvmThinPool)
+	} else {
+		output, err = shared.TryRunCommand(
+			"lvcreate",
+			"--poolmetadatasize", "1G",
+			"-L", "1G",
+			"--thinpool", lvmThinPool)
+	}
+
+	if err != nil {
+		shared.LogErrorf("Could not create thin pool \"%s\": %s.", thinPoolName, string(output))
+		return fmt.Errorf("Could not create LVM thin pool named %s", thinPoolName)
+	}
+
+	if !isRecent {
+		// Grow it to the maximum VG size (two step process required by old LVM)
+		output, err = shared.TryRunCommand("lvextend", "--alloc", "anywhere", "-l", "100%FREE", lvmThinPool)
 
-		err = storageLVMValidateThinPoolName(s.d, vgName, thinPoolName)
 		if err != nil {
-			shared.LogErrorf("Setting thin pool name: %s.", err)
-			return fmt.Errorf("Error setting LVM thin pool config: %v", err)
+			shared.LogErrorf("Could not grow thin pool: \"%s\": %s.", thinPoolName, string(output))
+			return fmt.Errorf("Could not grow LVM thin pool named %s", thinPoolName)
 		}
 	}
 
+	return nil
+}
+
+func lvmCreateThinpool(d *Daemon, sTypeVersion string, vgName string, thinPoolName string, lvFsType string) error {
+	exists, err := storageLVMThinpoolExists(vgName, thinPoolName)
+	if err != nil {
+		return err
+	}
+
+	if exists {
+		return nil
+	}
+
+	err = createDefaultThinPool(sTypeVersion, vgName, thinPoolName, lvFsType)
+	if err != nil {
+		return err
+	}
+
+	err = storageLVMValidateThinPoolName(d, vgName, thinPoolName)
+	if err != nil {
+		shared.LogErrorf("Setting thin pool name: %s.", err)
+		return fmt.Errorf("Error setting LVM thin pool config: %v", err)
+	}
+
+	return nil
+}
+
+func lvmCreateThinLV(vgName string, thinPoolName string, lvName string, lvFsType string, lvSize string, volumeType string) error {
 	lvmThinPoolPath := fmt.Sprintf("%s/%s", vgName, thinPoolName)
 	lvmPoolVolumeName := getPrefixedLvName(volumeType, lvName)
 	output, err := shared.TryRunCommand(
@@ -1907,47 +1969,6 @@ func (s *storageLvm) createThinLV(vgName string, thinPoolName string, lvName str
 	return nil
 }
 
-func (s *storageLvm) createDefaultThinPool(vgName string, thinPoolName string, lvName string, lvFsType string) error {
-	isRecent, err := s.lvmVersionIsAtLeast("2.02.99")
-	if err != nil {
-		return fmt.Errorf("Error checking LVM version: %s", err)
-	}
-
-	// Create the thin pool
-	lvmThinPool := fmt.Sprintf("%s/%s", vgName, thinPoolName)
-	var output string
-	if isRecent {
-		output, err = shared.TryRunCommand(
-			"lvcreate",
-			"--poolmetadatasize", "1G",
-			"-l", "100%FREE",
-			"--thinpool", lvmThinPool)
-	} else {
-		output, err = shared.TryRunCommand(
-			"lvcreate",
-			"--poolmetadatasize", "1G",
-			"-L", "1G",
-			"--thinpool", lvmThinPool)
-	}
-
-	if err != nil {
-		shared.LogErrorf("Could not create thin pool \"%s\": %s.", thinPoolName, output)
-		return fmt.Errorf("Could not create LVM thin pool named %s", thinPoolName)
-	}
-
-	if !isRecent {
-		// Grow it to the maximum VG size (two step process required by old LVM)
-		output, err = shared.TryRunCommand("lvextend", "--alloc", "anywhere", "-l", "100%FREE", lvmThinPool)
-
-		if err != nil {
-			shared.LogErrorf("Could not grow thin pool: \"%s\": %s.", thinPoolName, output)
-			return fmt.Errorf("Could not grow LVM thin pool named %s", thinPoolName)
-		}
-	}
-
-	return nil
-}
-
 func (s *storageLvm) removeLV(vgName string, volumeType string, lvName string) error {
 	lvmVolumePath := getLvmDevPath(vgName, volumeType, lvName)
 	output, err := shared.TryRunCommand("lvremove", "-f", lvmVolumePath)
@@ -1963,7 +1984,7 @@ func (s *storageLvm) removeLV(vgName string, volumeType string, lvName string) e
 func (s *storageLvm) createSnapshotLV(vgName string, origLvName string, origVolumeType string, lvName string, volumeType string, readonly bool) (string, error) {
 	sourceLvmVolumePath := getLvmDevPath(vgName, origVolumeType, origLvName)
 	shared.LogDebugf("in createSnapshotLV: %s.", sourceLvmVolumePath)
-	isRecent, err := s.lvmVersionIsAtLeast("2.02.99")
+	isRecent, err := lvmVersionIsAtLeast(s.sTypeVersion, "2.02.99")
 	if err != nil {
 		return "", fmt.Errorf("Error checking LVM version: %v", err)
 	}

From 9c0e5e8d8270ca8c5b696a6c4113547124adfe82 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Tue, 7 Mar 2017 15:43:59 +0100
Subject: [PATCH 2/3] patches: handle mixed-storage <lvm,dir> upgrade

Handle the case where we have a LVM upgrade that has DIR containers.

Closes #3026.

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 lxd/patches.go     | 230 ++++++++++++++++++++++++++++++++++++++++++-----------
 lxd/storage_lvm.go |   7 ++
 2 files changed, 191 insertions(+), 46 deletions(-)

diff --git a/lxd/patches.go b/lxd/patches.go
index 4017343..83c7acd 100644
--- a/lxd/patches.go
+++ b/lxd/patches.go
@@ -826,7 +826,15 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, defaultPoolName string, d
 		poolConfig["volume.block.mount_options"] = fsMntOpts
 	}
 
+	thinPoolName := "LXDPool"
 	poolConfig["lvm.thinpool_name"] = daemonConfig["storage.lvm_thinpool_name"].Get()
+	if poolConfig["lvm.thinpool_name"] != "" {
+		thinPoolName = poolConfig["lvm.thinpool_name"]
+	} else {
+		// If empty we need to set it to the old default.
+		poolConfig["lvm.thinpool_name"] = thinPoolName
+	}
+
 	poolConfig["lvm.vg_name"] = daemonConfig["storage.lvm_vg_name"].Get()
 
 	poolConfig["volume.size"] = daemonConfig["storage.lvm_volume_size"].Get()
@@ -896,6 +904,7 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, defaultPoolName string, d
 		return err
 	}
 
+	// Create pool mountpoint if it doesn't already exist.
 	poolMntPoint := getStoragePoolMountPoint(defaultPoolName)
 	if !shared.PathExists(poolMntPoint) {
 		err = os.MkdirAll(poolMntPoint, 0711)
@@ -905,6 +914,7 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, defaultPoolName string, d
 	}
 
 	if len(cRegular) > 0 {
+		// Create generic containers folder on the storage pool.
 		newContainersMntPoint := getContainerMountPoint(defaultPoolName, "")
 		if !shared.PathExists(newContainersMntPoint) {
 			err = os.MkdirAll(newContainersMntPoint, 0711)
@@ -954,6 +964,7 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, defaultPoolName string, d
 		if shared.IsMountPoint(oldContainerMntPoint) {
 			err := tryUnmount(oldContainerMntPoint, syscall.MNT_DETACH)
 			if err != nil {
+				shared.LogErrorf("Failed to unmount LVM logical volume \"%s\": %s.", oldContainerMntPoint, err)
 				return err
 			}
 		}
@@ -962,28 +973,91 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, defaultPoolName string, d
 		// new storage api. We do os.Rename() here to preserve
 		// permissions and ownership.
 		newContainerMntPoint := getContainerMountPoint(defaultPoolName, ct)
-		if shared.PathExists(oldContainerMntPoint) && !shared.PathExists(newContainerMntPoint) {
-			err = os.Rename(oldContainerMntPoint, newContainerMntPoint)
-			if err != nil {
-				return err
-			}
-		}
-
-		if shared.PathExists(oldContainerMntPoint + ".lv") {
-			err := os.Remove(oldContainerMntPoint + ".lv")
-			if err != nil {
-				return err
-			}
-		}
-
-		// Rename the logical volume device.
 		ctLvName := containerNameToLVName(ct)
 		newContainerLvName := fmt.Sprintf("%s_%s", storagePoolVolumeApiEndpointContainers, ctLvName)
 		containerLvDevPath := getLvmDevPath(defaultPoolName, storagePoolVolumeApiEndpointContainers, ctLvName)
 		if !shared.PathExists(containerLvDevPath) {
-			_, err := shared.TryRunCommand("lvrename", defaultPoolName, ctLvName, newContainerLvName)
-			if err != nil {
-				return err
+			oldLvDevPath := fmt.Sprintf("/dev/%s/%s", defaultPoolName, ctLvName)
+			// If the old LVM device path for the logical volume
+			// exists we call lvrename. Otherwise this is likely a
+			// mixed-storage LXD instance which we need to deal
+			// with.
+			if shared.PathExists(oldLvDevPath) {
+				// Rename the logical volume mountpoint.
+				if shared.PathExists(oldContainerMntPoint) && !shared.PathExists(newContainerMntPoint) {
+					err = os.Rename(oldContainerMntPoint, newContainerMntPoint)
+					if err != nil {
+						shared.LogErrorf("Failed to rename LVM container mountpoint from %s to %s: %s.", oldContainerMntPoint, newContainerMntPoint, err)
+						return err
+					}
+				}
+
+				// Remove the old container mountpoint.
+				if shared.PathExists(oldContainerMntPoint + ".lv") {
+					err := os.Remove(oldContainerMntPoint + ".lv")
+					if err != nil {
+						shared.LogErrorf("Failed to remove old LVM container mountpoint %s: %s.", oldContainerMntPoint+".lv", err)
+						return err
+					}
+				}
+
+				// Rename the logical volume.
+				msg, err := shared.TryRunCommand("lvrename", defaultPoolName, ctLvName, newContainerLvName)
+				if err != nil {
+					shared.LogErrorf("Failed to rename LVM logical volume from %s to %s: %s.", ctLvName, newContainerLvName, msg)
+					return err
+				}
+			} else if shared.PathExists(oldContainerMntPoint) && shared.IsDir(oldContainerMntPoint) {
+				// This is a directory backed container and it
+				// means that this was a mixed-storage LXD
+				// instance.
+
+				// Initialize storage interface for the new
+				// container.
+				ctStorage, err := storagePoolVolumeContainerLoadInit(d, ct)
+				if err != nil {
+					shared.LogErrorf("Failed to initialize new storage interface for LVM container %s: %s.", ct, err)
+					return err
+				}
+
+				// Load the container from the database.
+				ctStruct, err := containerLoadByName(d, ct)
+				if err != nil {
+					shared.LogErrorf("Failed to load LVM container %s: %s.", ct, err)
+					return err
+				}
+
+				// Create an empty LVM logical volume for the
+				// container.
+				err = ctStorage.ContainerCreate(ctStruct)
+				if err != nil {
+					shared.LogErrorf("Failed to create empty LVM logical volume for container %s: %s.", ct, err)
+					return err
+				}
+
+				// In case the new LVM logical volume for the
+				// container is not mounted mount it.
+				if !shared.IsMountPoint(newContainerMntPoint) {
+					_, err = ctStorage.ContainerMount(ctStruct.Name(), ctStruct.Path())
+					if err != nil {
+						shared.LogErrorf("Failed to mount new empty LVM logical volume for container %s: %s.", ct, err)
+						return err
+					}
+				}
+
+				// Use rsync to fill the empty volume.
+				output, err := storageRsyncCopy(oldContainerMntPoint, newContainerMntPoint)
+				if err != nil {
+					ctStorage.ContainerDelete(ctStruct)
+					return fmt.Errorf("rsync failed: %s", string(output))
+				}
+
+				// Remove the old container.
+				err = os.RemoveAll(oldContainerMntPoint)
+				if err != nil {
+					shared.LogErrorf("Failed to remove old container %s: %s.", oldContainerMntPoint, err)
+					return err
+				}
 			}
 		}
 
@@ -991,6 +1065,7 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, defaultPoolName string, d
 		doesntMatter := false
 		err = createContainerMountpoint(newContainerMntPoint, oldContainerMntPoint, doesntMatter)
 		if err != nil {
+			shared.LogErrorf("Failed to create container mountpoint \"%s\" for LVM logical volume: %s.", newContainerMntPoint, err)
 			return err
 		}
 
@@ -1002,13 +1077,6 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, defaultPoolName string, d
 			mountOptions = "discard"
 		}
 
-		if !shared.IsMountPoint(newContainerMntPoint) {
-			err := tryMount(containerLvDevPath, newContainerMntPoint, lvFsType, 0, mountOptions)
-			if err != nil {
-				return err
-			}
-		}
-
 		// Check if we need to account for snapshots for this container.
 		ctSnapshots, err := dbContainerGetSnapshots(d.db, ct)
 		if err != nil {
@@ -1057,24 +1125,7 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, defaultPoolName string, d
 				}
 			}
 
-			// Unmount the logical volume.
 			oldSnapshotMntPoint := shared.VarPath("snapshots", cs)
-			if shared.IsMountPoint(oldSnapshotMntPoint) {
-				err := tryUnmount(oldSnapshotMntPoint, syscall.MNT_DETACH)
-				if err != nil {
-					return err
-				}
-			}
-
-			// Rename the snapshot mountpoint to preserve acl's and
-			// so on.
-			if shared.PathExists(oldSnapshotMntPoint) && !shared.PathExists(newSnapshotMntPoint) {
-				err := os.Rename(oldSnapshotMntPoint, newSnapshotMntPoint)
-				if err != nil {
-					return err
-				}
-			}
-
 			os.Remove(oldSnapshotMntPoint + ".lv")
 
 			// Make sure we use a valid lv name.
@@ -1082,9 +1133,85 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, defaultPoolName string, d
 			newSnapshotLvName := fmt.Sprintf("%s_%s", storagePoolVolumeApiEndpointContainers, csLvName)
 			snapshotLvDevPath := getLvmDevPath(defaultPoolName, storagePoolVolumeApiEndpointContainers, csLvName)
 			if !shared.PathExists(snapshotLvDevPath) {
-				_, err := shared.TryRunCommand("lvrename", defaultPoolName, csLvName, newSnapshotLvName)
-				if err != nil {
-					return err
+				oldLvDevPath := fmt.Sprintf("/dev/%s/%s", defaultPoolName, csLvName)
+				if shared.PathExists(oldLvDevPath) {
+					// Unmount the logical volume.
+					if shared.IsMountPoint(oldSnapshotMntPoint) {
+						err := tryUnmount(oldSnapshotMntPoint, syscall.MNT_DETACH)
+						if err != nil {
+							shared.LogErrorf("Failed to unmount LVM logical volume \"%s\": %s.", oldSnapshotMntPoint, err)
+							return err
+						}
+					}
+
+					// Rename the snapshot mountpoint to preserve acl's and
+					// so on.
+					if shared.PathExists(oldSnapshotMntPoint) && !shared.PathExists(newSnapshotMntPoint) {
+						err := os.Rename(oldSnapshotMntPoint, newSnapshotMntPoint)
+						if err != nil {
+							shared.LogErrorf("Failed to rename LVM container mountpoint from %s to %s: %s.", oldSnapshotMntPoint, newSnapshotMntPoint, err)
+							return err
+						}
+					}
+
+					// Rename the logical volume.
+					msg, err := shared.TryRunCommand("lvrename", defaultPoolName, csLvName, newSnapshotLvName)
+					if err != nil {
+						shared.LogErrorf("Failed to rename LVM logical volume from %s to %s: %s.", csLvName, newSnapshotLvName, msg)
+						return err
+					}
+				} else if shared.PathExists(oldSnapshotMntPoint) && shared.IsDir(oldSnapshotMntPoint) {
+					// This is a directory backed container
+					// and it means that this was a
+					// mixed-storage LXD instance.
+
+					// Initialize storage interface for the new
+					// snapshot.
+					csStorage, err := storagePoolVolumeContainerLoadInit(d, cs)
+					if err != nil {
+						shared.LogErrorf("Failed to initialize new storage interface for LVM container %s: %s.", cs, err)
+						return err
+					}
+
+					// Load the snapshot from the database.
+					csStruct, err := containerLoadByName(d, cs)
+					if err != nil {
+						shared.LogErrorf("Failed to load LVM container %s: %s.", cs, err)
+						return err
+					}
+
+					// Create an empty LVM logical volume
+					// for the snapshot.
+					err = csStorage.ContainerSnapshotCreateEmpty(csStruct)
+					if err != nil {
+						shared.LogErrorf("Failed to create empty LVM logical volume for container %s: %s.", cs, err)
+						return err
+					}
+
+					// In case the new LVM logical volume
+					// for the snapshot is not mounted mount
+					// it.
+					if !shared.IsMountPoint(newSnapshotMntPoint) {
+						_, err = csStorage.ContainerMount(csStruct.Name(), csStruct.Path())
+						if err != nil {
+							shared.LogErrorf("Failed to mount new empty LVM logical volume for container %s: %s.", cs, err)
+							return err
+						}
+					}
+
+					// Use rsync to fill the empty volume.
+					output, err := storageRsyncCopy(oldSnapshotMntPoint, newSnapshotMntPoint)
+					if err != nil {
+						csStorage.ContainerDelete(csStruct)
+						return fmt.Errorf("rsync failed: %s", string(output))
+					}
+
+					// Remove the old snapshot.
+					err = os.RemoveAll(oldSnapshotMntPoint)
+					if err != nil {
+						shared.LogErrorf("Failed to remove old container %s: %s.", oldSnapshotMntPoint, err)
+						return err
+					}
 				}
 			}
 		}
@@ -1112,6 +1239,13 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, defaultPoolName string, d
 			}
 		}
 
+		if !shared.IsMountPoint(newContainerMntPoint) {
+			err := tryMount(containerLvDevPath, newContainerMntPoint, lvFsType, 0, mountOptions)
+			if err != nil {
+				shared.LogErrorf("Failed to mount LVM logical \"%s\" onto \"%s\" : %s.", containerLvDevPath, newContainerMntPoint, err)
+				return err
+			}
+		}
 	}
 
 	images := append(imgPublic, imgPrivate...)
@@ -1178,7 +1312,11 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, defaultPoolName string, d
 		// Rename the logical volume device.
 		newImageLvName := fmt.Sprintf("%s_%s", storagePoolVolumeApiEndpointImages, img)
 		imageLvDevPath := getLvmDevPath(defaultPoolName, storagePoolVolumeApiEndpointImages, img)
-		if !shared.PathExists(imageLvDevPath) {
+		oldLvDevPath := fmt.Sprintf("/dev/%s/%s", defaultPoolName, img)
+		// Only create logical volumes for images that have a logical
+		// volume on the pre-storage-api LXD instance. If not, we don't
+		// care since LXD will create a logical volume on demand.
+		if !shared.PathExists(imageLvDevPath) && shared.PathExists(oldLvDevPath) {
 			_, err := shared.TryRunCommand("lvrename", defaultPoolName, img, newImageLvName)
 			if err != nil {
 				return err
diff --git a/lxd/storage_lvm.go b/lxd/storage_lvm.go
index 89a245a..60ecaa5 100644
--- a/lxd/storage_lvm.go
+++ b/lxd/storage_lvm.go
@@ -1321,7 +1321,11 @@ func (s *storageLvm) ContainerMount(name string, path string) (bool, error) {
 	poolName := s.getOnDiskPoolName()
 	containerLvmPath := getLvmDevPath(poolName, storagePoolVolumeApiEndpointContainers, containerLvmName)
 	mountOptions := s.getLvmBlockMountOptions()
+
 	containerMntPoint := getContainerMountPoint(s.pool.Name, name)
+	if shared.IsSnapshot(name) {
+		containerMntPoint = getSnapshotMountPoint(s.pool.Name, name)
+	}
 
 	containerMountLockID := getContainerMountLockID(s.pool.Name, name)
 	lxdStorageMapLock.Lock()
@@ -1364,6 +1368,9 @@ func (s *storageLvm) ContainerUmount(name string, path string) (bool, error) {
 	shared.LogDebugf("Unmounting LVM storage volume for container \"%s\" on storage pool \"%s\".", s.volume.Name, s.pool.Name)
 
 	containerMntPoint := getContainerMountPoint(s.pool.Name, name)
+	if shared.IsSnapshot(name) {
+		containerMntPoint = getSnapshotMountPoint(s.pool.Name, name)
+	}
 
 	containerUmountLockID := getContainerUmountLockID(s.pool.Name, name)
 	lxdStorageMapLock.Lock()

From ae7f49e369f37ee66c820dbabfe01a993f2c060a Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Tue, 7 Mar 2017 17:19:10 +0100
Subject: [PATCH 3/3] patches: delete image db entry if lv is missing

When users mixed DIR and LVM on an pre-storage-api LXD LVM instance some images
might exist in the images database but do not actually exist as logical
volumes. In this case the upgrade code will be confused and error out. If we
detect such a case we should delete the image from the storage volumes
database and move on.

Closes #3026.

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 lxd/patches.go | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/lxd/patches.go b/lxd/patches.go
index 83c7acd..3c527ba 100644
--- a/lxd/patches.go
+++ b/lxd/patches.go
@@ -1322,6 +1322,16 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, defaultPoolName string, d
 				return err
 			}
 		}
+
+		if !shared.PathExists(imageLvDevPath) {
+			// This image didn't exist as a logical volume on the
+			// old LXD instance so we need to kick it from the
+			// storage volumes database for this pool.
+			err := dbStoragePoolVolumeDelete(d.db, img, storagePoolVolumeTypeImage, poolID)
+			if err != nil {
+				return err
+			}
+		}
 	}
 
 	return nil


More information about the lxc-devel mailing list