[lxc-devel] [lxd/master] Storage: ceph snapshot usage

tomponline on Github lxc-bot at linuxcontainers.org
Tue Apr 7 16:38:54 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 659 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200407/60f1b994/attachment.bin>
-------------- next part --------------
From 1da997a575fbff3e35385eda3a702b564eb71502 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 7 Apr 2020 17:33:56 +0100
Subject: [PATCH 1/2] lxd/storagr/drivers/driver/ceph/volumes: Adds support for
 snapshot usage reporting

Also removes block level usage reporting for filesystem volumes (to bring inline with how LVM driver does it).
Also adds block volume reporting total usage.

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/storage/drivers/driver_ceph_volumes.go | 91 +++++++++++++---------
 1 file changed, 55 insertions(+), 36 deletions(-)

diff --git a/lxd/storage/drivers/driver_ceph_volumes.go b/lxd/storage/drivers/driver_ceph_volumes.go
index 56eefdcdb4..9c799793bf 100644
--- a/lxd/storage/drivers/driver_ceph_volumes.go
+++ b/lxd/storage/drivers/driver_ceph_volumes.go
@@ -668,7 +668,13 @@ func (d *ceph) UpdateVolume(vol Volume, changedConfig map[string]string) error {
 
 // GetVolumeUsage returns the disk space used by the volume.
 func (d *ceph) GetVolumeUsage(vol Volume) (int64, error) {
-	if vol.contentType == ContentTypeFS && shared.IsMountPoint(vol.MountPath()) {
+	isSnap := vol.IsSnapshot()
+
+	// For non-snapshot filesystem volumes, we only return usage when the volume is mounted.
+	// This is because to get an accurate value we cannot use blocks allocated, as the filesystem will likely
+	// consume blocks and not free them when files are deleted in the volume. This avoids returning different
+	// values depending on whether the volume is mounted or not.
+	if !isSnap && vol.contentType == ContentTypeFS && shared.IsMountPoint(vol.MountPath()) {
 		var stat unix.Statfs_t
 
 		err := unix.Statfs(vol.MountPath(), &stat)
@@ -677,49 +683,62 @@ func (d *ceph) GetVolumeUsage(vol Volume) (int64, error) {
 		}
 
 		return int64(stat.Blocks-stat.Bfree) * int64(stat.Bsize), nil
-	}
-
-	type cephDuLine struct {
-		Name            string `json:"name"`
-		Snapshot        string `json:"snapshot"`
-		ProvisionedSize int64  `json:"provisioned_size"`
-		UsedSize        int64  `json:"used_size"`
-	}
+	} else if vol.contentType == ContentTypeBlock || isSnap {
+		type cephDuLine struct {
+			Name            string `json:"name"`
+			Snapshot        string `json:"snapshot"`
+			ProvisionedSize int64  `json:"provisioned_size"`
+			UsedSize        int64  `json:"used_size"`
+		}
 
-	type cephDuInfo struct {
-		Images []cephDuLine `json:"images"`
-	}
+		type cephDuInfo struct {
+			Images []cephDuLine `json:"images"`
+		}
 
-	jsonInfo, err := shared.TryRunCommand(
-		"rbd",
-		"du",
-		"--format", "json",
-		"--id", d.config["ceph.user.name"],
-		"--cluster", d.config["ceph.cluster_name"],
-		"--pool", d.config["ceph.osd.pool_name"],
-		d.getRBDVolumeName(vol, "", false, false))
+		jsonInfo, err := shared.TryRunCommand(
+			"rbd",
+			"du",
+			"--format", "json",
+			"--id", d.config["ceph.user.name"],
+			"--cluster", d.config["ceph.cluster_name"],
+			"--pool", d.config["ceph.osd.pool_name"],
+			d.getRBDVolumeName(vol, "", false, false),
+		)
+		if err != nil {
+			return -1, err
+		}
 
-	if err != nil {
-		return -1, err
-	}
+		var usedSize int64
+		var result cephDuInfo
 
-	var usedSize int64
-	var result cephDuInfo
+		err = json.Unmarshal([]byte(jsonInfo), &result)
+		if err != nil {
+			return -1, err
+		}
 
-	err = json.Unmarshal([]byte(jsonInfo), &result)
-	if err != nil {
-		return -1, err
-	}
+		_, snapName, _ := shared.InstanceGetParentAndSnapshotName(vol.Name())
+		snapName = fmt.Sprintf("snapshot_%s", snapName)
 
-	// rbd du gives the output of all related rbd images, snapshots included
-	// to get the total size of the image we use the result that does not include
-	// a snapshot name, this is the total image size.
-	for _, image := range result.Images {
-		if image.Snapshot == "" {
-			usedSize = image.UsedSize
+		// rbd du gives the output of all related rbd images, snapshots included.
+		for _, image := range result.Images {
+			if isSnap {
+				// For snapshot volumes we only want to get the specific image used so we can
+				// indicate how much CoW usage that snapshot has.
+				if image.Snapshot == snapName {
+					usedSize = image.UsedSize
+					break
+				}
+			} else {
+				// For non-snapshot volumes, to get the total size of the volume we need to add up
+				// all of the image's usage.
+				usedSize += image.UsedSize
+			}
 		}
+
+		return usedSize, nil
 	}
-	return usedSize, nil
+
+	return -1, ErrNotSupported
 }
 
 // SetVolumeQuota applies a size limit on volume.

From ff7affd99fbfafd97eaa7f3045840bce1a450fec Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 7 Apr 2020 17:36:38 +0100
Subject: [PATCH 2/2] lxd/storage/drivers/driver/lvm/volumes: Clarifies
 comments on LVM volume usage reporting

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/storage/drivers/driver_lvm_volumes.go | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/lxd/storage/drivers/driver_lvm_volumes.go b/lxd/storage/drivers/driver_lvm_volumes.go
index ba41b10023..0c2257fa72 100644
--- a/lxd/storage/drivers/driver_lvm_volumes.go
+++ b/lxd/storage/drivers/driver_lvm_volumes.go
@@ -288,7 +288,10 @@ func (d *lvm) GetVolumeUsage(vol Volume) (int64, error) {
 		return -1, ErrNotSupported
 	}
 
-	// If volume has a filesystem and is mounted we can ask the filesystem for usage.
+	// For non-snapshot filesystem volumes, we only return usage when the volume is mounted.
+	// This is because to get an accurate value we cannot use blocks allocated, as the filesystem will likely
+	// consume blocks and not free them when files are deleted in the volume. This avoids returning different
+	// values depending on whether the volume is mounted or not.
 	if vol.contentType == ContentTypeFS && shared.IsMountPoint(vol.MountPath()) {
 		var stat unix.Statfs_t
 		err := unix.Statfs(vol.MountPath(), &stat)
@@ -298,8 +301,8 @@ func (d *lvm) GetVolumeUsage(vol Volume) (int64, error) {
 
 		return int64(stat.Blocks-stat.Bfree) * int64(stat.Bsize), nil
 	} else if vol.contentType == ContentTypeBlock && d.usesThinpool() {
-		// For thin pool block volumes we can calculate an approximate usage using the space allocated to
-		// the volume from the thin pool.
+		// For non-snapshot thin pool block volumes we can calculate an approximate usage using the space
+		// allocated to the volume from the thin pool.
 		volDevPath := d.lvmDevPath(d.config["lvm.vg_name"], vol.volType, vol.contentType, vol.name)
 		_, usedSize, err := d.thinPoolVolumeUsage(volDevPath)
 		if err != nil {


More information about the lxc-devel mailing list