[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