The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/3034
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) ===
From 671166d83b3567b310084dd43314dff7b65cfa95 Mon Sep 17 00:00:00 2001 From: Christian Brauner <christian.brau...@ubuntu.com> Date: Tue, 7 Mar 2017 12:44:44 +0100 Subject: [PATCH 1/2] 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.brau...@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 75b767c..25a44dd 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 []byte + if isRecent { + output, err = tryExec( + "lvcreate", + "--poolmetadatasize", "1G", + "-l", "100%FREE", + "--thinpool", lvmThinPool) + } else { + output, err = tryExec( + "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 = tryExec("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 := tryExec( @@ -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 []byte - if isRecent { - output, err = tryExec( - "lvcreate", - "--poolmetadatasize", "1G", - "-l", "100%FREE", - "--thinpool", lvmThinPool) - } else { - output, err = tryExec( - "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 = tryExec("lvextend", "--alloc", "anywhere", "-l", "100%FREE", lvmThinPool) - - if err != nil { - 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 (s *storageLvm) removeLV(vgName string, volumeType string, lvName string) error { lvmVolumePath := getLvmDevPath(vgName, volumeType, lvName) output, err := tryExec("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 aa2fe123504b0e432897dc768aea06769822b5d9 Mon Sep 17 00:00:00 2001 From: Christian Brauner <christian.brau...@ubuntu.com> Date: Tue, 7 Mar 2017 15:43:59 +0100 Subject: [PATCH 2/2] 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.brau...@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 369860b..d522fc6 100644 --- a/lxd/patches.go +++ b/lxd/patches.go @@ -827,7 +827,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() @@ -897,6 +905,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) @@ -906,6 +915,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) @@ -955,6 +965,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 } } @@ -963,28 +974,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 := tryExec("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 := tryExec("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 + } } } @@ -992,6 +1066,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 } @@ -1003,13 +1078,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 { @@ -1058,24 +1126,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. @@ -1083,9 +1134,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 := tryExec("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 := tryExec("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 + } } } } @@ -1113,6 +1240,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...) @@ -1179,7 +1313,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 := tryExec("lvrename", defaultPoolName, img, newImageLvName) if err != nil { return err diff --git a/lxd/storage_lvm.go b/lxd/storage_lvm.go index 25a44dd..388deb3 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()
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel