[lxc-devel] [lxd/master] ceph: implement resizing

brauner on Github lxc-bot at linuxcontainers.org
Sun Sep 3 19:53:57 UTC 2017


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 381 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20170903/b5497805/attachment.bin>
-------------- next part --------------
From c544742bb8c4f86c74a6555fc02a5f248dc58c8a Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Sun, 3 Sep 2017 21:50:04 +0200
Subject: [PATCH] ceph: implement resizing

Closes #3760.

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 lxd/storage_ceph.go       | 125 +++++++++++++++++++++++++++++++++++++++++++++-
 lxd/storage_ceph_utils.go | 122 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 245 insertions(+), 2 deletions(-)

diff --git a/lxd/storage_ceph.go b/lxd/storage_ceph.go
index c0410a385..975452c15 100644
--- a/lxd/storage_ceph.go
+++ b/lxd/storage_ceph.go
@@ -454,6 +454,19 @@ func (s *storageCeph) StoragePoolVolumeCreate() error {
 		}
 	}()
 
+	// apply quota
+	if s.volume.Config["size"] != "" {
+		size, err := shared.ParseByteSizeString(s.volume.Config["size"])
+		if err != nil {
+			return err
+		}
+
+		err = s.StorageEntitySetQuota(storagePoolVolumeTypeCustom, size, nil)
+		if err != nil {
+			return err
+		}
+	}
+
 	logger.Debugf(`Created RBD storage volume "%s" on storage pool "%s"`,
 		s.volume.Name, s.pool.Name)
 
@@ -647,7 +660,38 @@ func (s *storageCeph) StoragePoolVolumeUmount() (bool, error) {
 }
 
 func (s *storageCeph) StoragePoolVolumeUpdate(writable *api.StorageVolumePut, changedConfig []string) error {
-	return fmt.Errorf("RBD storage volume properties cannot be changed")
+	logger.Infof(`Updating RBD storage volume "%s" on storage pool "%s"`,
+		s.volume.Name, s.pool.Name)
+
+	if !(shared.StringInSlice("block.mount_options", changedConfig) &&
+		len(changedConfig) == 1) &&
+		!(shared.StringInSlice("block.mount_options", changedConfig) &&
+			len(changedConfig) == 2 &&
+			shared.StringInSlice("size", changedConfig)) &&
+		!(shared.StringInSlice("size", changedConfig) &&
+			len(changedConfig) == 1) {
+		return fmt.Errorf("The properties \"%v\" cannot be changed",
+			changedConfig)
+	}
+
+	if shared.StringInSlice("size", changedConfig) {
+		// apply quota
+		if s.volume.Config["size"] != writable.Config["size"] {
+			size, err := shared.ParseByteSizeString(writable.Config["size"])
+			if err != nil {
+				return err
+			}
+
+			err = s.StorageEntitySetQuota(storagePoolVolumeTypeCustom, size, nil)
+			if err != nil {
+				return err
+			}
+		}
+	}
+
+	logger.Infof(`Updated RBD storage volume "%s" on storage pool "%s"`,
+		s.volume.Name, s.pool.Name)
+	return nil
 }
 
 func (s *storageCeph) StoragePoolUpdate(writable *api.StoragePoolPut, changedConfig []string) error {
@@ -2500,5 +2544,82 @@ func (s *storageCeph) ImageUmount(fingerprint string) (bool, error) {
 }
 
 func (s *storageCeph) StorageEntitySetQuota(volumeType int, size int64, data interface{}) error {
-	return fmt.Errorf("RBD storage volume quota are not supported")
+	logger.Debugf(`Setting RBD quota for "%s"`, s.volume.Name)
+
+	if !shared.IntInSlice(volumeType, supportedVolumeTypes) {
+		return fmt.Errorf("Invalid storage type")
+	}
+
+	var ret int
+	var c container
+	fsType := s.getRBDFilesystem()
+	mountpoint := ""
+	RBDDevPath := ""
+	volumeName := ""
+	switch volumeType {
+	case storagePoolVolumeTypeContainer:
+		c = data.(container)
+		ctName := c.Name()
+		if c.IsRunning() {
+			msg := fmt.Sprintf(`Cannot resize RBD storage volume `+
+				`for container \"%s\" when it is running`,
+				ctName)
+			logger.Errorf(msg)
+			return fmt.Errorf(msg)
+		}
+
+		RBDDevPath, ret = getRBDMappedDevPath(s.ClusterName,
+			s.OSDPoolName, storagePoolVolumeTypeNameContainer,
+			s.volume.Name, true, s.UserName)
+		mountpoint = getContainerMountPoint(s.pool.Name, ctName)
+		volumeName = ctName
+	default:
+		RBDDevPath, ret = getRBDMappedDevPath(s.ClusterName,
+			s.OSDPoolName, storagePoolVolumeTypeNameCustom,
+			s.volume.Name, true, s.UserName)
+		mountpoint = getStoragePoolVolumeMountPoint(s.pool.Name,
+			s.volume.Name)
+		volumeName = s.volume.Name
+	}
+	if ret < 0 {
+		return fmt.Errorf("Failed to get mapped RBD path")
+	}
+
+	oldSize, err := shared.ParseByteSizeString(s.volume.Config["size"])
+	if err != nil {
+		return err
+	}
+
+	// The right disjunct just means that someone unset the size property in
+	// the container's config. We obviously cannot resize to 0.
+	if oldSize == size || size == 0 {
+		return nil
+	}
+
+	if size < oldSize {
+		err = s.rbdShrink(RBDDevPath, size, fsType, mountpoint,
+			volumeType, volumeName, data)
+	} else if size > oldSize {
+		err = s.rbdGrow(RBDDevPath, size, fsType, mountpoint,
+			volumeType, volumeName, data)
+	}
+	if err != nil {
+		return err
+	}
+
+	// Update the database
+	s.volume.Config["size"] = shared.GetByteSizeString(size, 0)
+	err = db.StoragePoolVolumeUpdate(
+		s.s.DB,
+		s.volume.Name,
+		volumeType,
+		s.poolID,
+		s.volume.Description,
+		s.volume.Config)
+	if err != nil {
+		return err
+	}
+
+	logger.Debugf(`Set RBD quota for "%s"`, s.volume.Name)
+	return nil
 }
diff --git a/lxd/storage_ceph_utils.go b/lxd/storage_ceph_utils.go
index 1f4ec0e2e..e27969366 100644
--- a/lxd/storage_ceph_utils.go
+++ b/lxd/storage_ceph_utils.go
@@ -1492,3 +1492,125 @@ mapImage:
 
 	return strings.TrimSpace(devPath), 2
 }
+
+func (s *storageCeph) rbdShrink(path string, size int64, fsType string,
+	fsMntPoint string, volumeType int, volumeName string,
+	data interface{}) error {
+	var err error
+	var msg string
+
+	sizeString := strconv.FormatInt(size, 10)
+	switch fsType {
+	case "xfs":
+		logger.Errorf("xfs filesystems cannot be shrunk: dump, mkfs, and restore are required")
+		return fmt.Errorf("xfs filesystems cannot be shrunk: dump, mkfs, and restore are required")
+	default:
+		// default = ext4
+		switch volumeType {
+		case storagePoolVolumeTypeContainer:
+			c := data.(container)
+			ourMount, err := c.StorageStop()
+			if err != nil {
+				return err
+			}
+			if !ourMount {
+				defer c.StorageStart()
+			}
+		case storagePoolVolumeTypeCustom:
+			ourMount, err := s.StoragePoolVolumeUmount()
+			if err != nil {
+				return err
+			}
+			if !ourMount {
+				defer s.StoragePoolVolumeMount()
+			}
+		}
+
+		msg, err = shared.TryRunCommand("e2fsck", "-f", "-y", path)
+		if err != nil {
+			return err
+		}
+
+		// don't assume resize2fs semantics are sane (because they
+		// aren't)
+		kbSize := size / 1024
+		ext4SizeString := strconv.FormatInt(kbSize, 10)
+		ext4SizeString += "K"
+		msg, err = shared.TryRunCommand("resize2fs", path, ext4SizeString)
+		if err != nil {
+			logger.Errorf("could not reduce underlying %s filesystem for LV \"%s\": %s", fsType, path, msg)
+			return fmt.Errorf("could not reduce underlying %s filesystem for LV \"%s\": %s", fsType, path, msg)
+		}
+	}
+
+	sizeString = shared.GetByteSizeString(size, 0)
+	msg, err = shared.TryRunCommand(
+		"rbd",
+		"resize",
+		"--allow-shrink",
+		"--id", s.UserName,
+		"--cluster", s.ClusterName,
+		"--pool", s.OSDPoolName,
+		"--size", sizeString,
+		fmt.Sprintf("%s_%s", volumeType, volumeName))
+	if err != nil {
+		logger.Errorf("could not extend LV \"%s\": %s", path, msg)
+		return fmt.Errorf("could not extend LV \"%s\": %s", path, msg)
+	}
+
+	logger.Debugf("reduce underlying %s filesystem for LV \"%s\"", fsType, path)
+	return nil
+}
+
+func (s *storageCeph) rbdGrow(path string, size int64, fsType string,
+	fsMntPoint string, volumeType int, volumeName string,
+	data interface{}) error {
+	sizeString := shared.GetByteSizeString(size, 0)
+	msg, err := shared.TryRunCommand(
+		"rbd",
+		"resize",
+		"--id", s.UserName,
+		"--cluster", s.ClusterName,
+		"--pool", s.OSDPoolName,
+		"--size", sizeString,
+		fmt.Sprintf("%s_%s", volumeType, volumeName))
+	if err != nil {
+		logger.Errorf("could not extend LV \"%s\": %s", path, msg)
+		return fmt.Errorf("could not extend LV \"%s\": %s", path, msg)
+	}
+
+	switch volumeType {
+	case storagePoolVolumeTypeContainer:
+		c := data.(container)
+		ourMount, err := c.StorageStart()
+		if err != nil {
+			return err
+		}
+		if ourMount {
+			defer c.StorageStop()
+		}
+	case storagePoolVolumeTypeCustom:
+		ourMount, err := s.StoragePoolVolumeMount()
+		if err != nil {
+			return err
+		}
+		if ourMount {
+			defer s.StoragePoolVolumeUmount()
+		}
+	}
+
+	switch fsType {
+	case "xfs":
+		msg, err = shared.TryRunCommand("xfs_growfs", fsMntPoint)
+	default:
+		// default = ext4
+		msg, err = shared.TryRunCommand("resize2fs", path)
+	}
+	if err != nil {
+		logger.Errorf("could not extend underlying %s filesystem for LV \"%s\": %s", fsType, path, msg)
+		return fmt.Errorf("could not extend underlying %s filesystem for LV \"%s\": %s", fsType, path, msg)
+	}
+
+	logger.Debugf("extended underlying %s filesystem for LV \"%s\"", fsType, path)
+	return nil
+}


More information about the lxc-devel mailing list