From 25d0a1cfc0e6e98b7f04e3657798070d110fade7 Mon Sep 17 00:00:00 2001 From: Niels de Vos Date: Mon, 11 Jan 2021 09:05:06 +0100 Subject: [PATCH] rbd: add support for block-devices in NodeGetVolumeStats() The NodeGetVolumeStats procedure can now be used to fetch the capacity of the RBD block-device. By default this is a thin-provisioned device, which means that the capacity is not reserved in the Ceph cluster. This makes it possible to over-provision the cluster. In order to detect the amount of storage used by the RBD block-device (when thin-provisioned), it is required to connect to the Ceph cluster. Unfortunately, the NodeGetVolumeStats CSI procedure does not provide enough parameters to connect to the Ceph cluster and fetch more details about the RBD image. Signed-off-by: Niels de Vos --- internal/rbd/nodeserver.go | 55 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/internal/rbd/nodeserver.go b/internal/rbd/nodeserver.go index 1582ff78b..1e4975956 100644 --- a/internal/rbd/nodeserver.go +++ b/internal/rbd/nodeserver.go @@ -875,3 +875,58 @@ func (ns *NodeServer) xfsSupportsReflink() bool { xfsHasReflink = xfsReflinkNoSupport return false } + +// NodeGetVolumeStats returns volume stats. +func (ns *NodeServer) NodeGetVolumeStats(ctx context.Context, req *csi.NodeGetVolumeStatsRequest) (*csi.NodeGetVolumeStatsResponse, error) { + var err error + targetPath := req.GetVolumePath() + if targetPath == "" { + err = fmt.Errorf("targetpath %v is empty", targetPath) + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + + stat, err := os.Stat(targetPath) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "failed to get stat for targetpath %q: %v", targetPath, err) + } + + if stat.Mode().IsDir() { + return csicommon.FilesystemNodeGetVolumeStats(ctx, targetPath) + } else if (stat.Mode() & os.ModeDevice) == os.ModeDevice { + return blockNodeGetVolumeStats(ctx, targetPath) + } + + return nil, fmt.Errorf("targetpath %q is not a block device", targetPath) +} + +// blockNodeGetVolumeStats gets the metrics for a `volumeMode: Block` type of +// volume. At the moment, only the size of the block-device can be returned, as +// there are no secrets in the NodeGetVolumeStats request that enables us to +// connect to the Ceph cluster. +// +// TODO: https://github.com/container-storage-interface/spec/issues/371#issuecomment-756834471 +func blockNodeGetVolumeStats(ctx context.Context, targetPath string) (*csi.NodeGetVolumeStatsResponse, error) { + args := []string{"--noheadings", "--bytes", "--output=SIZE", targetPath} + lsblkSize, _, err := util.ExecCommand(ctx, "/bin/lsblk", args...) + if err != nil { + err = fmt.Errorf("lsblk %v returned an error: %w", args, err) + util.ErrorLog(ctx, err.Error()) + return nil, status.Error(codes.Internal, err.Error()) + } + + size, err := strconv.ParseInt(strings.TrimSpace(lsblkSize), 10, 64) + if err != nil { + err = fmt.Errorf("failed to convert %q to bytes: %w", lsblkSize, err) + util.ErrorLog(ctx, err.Error()) + return nil, status.Error(codes.Internal, err.Error()) + } + + return &csi.NodeGetVolumeStatsResponse{ + Usage: []*csi.VolumeUsage{ + { + Total: size, + Unit: csi.VolumeUsage_BYTES, + }, + }, + }, nil +}