Skip to content

Commit

Permalink
rbd: add support for block-devices in NodeGetVolumeStats()
Browse files Browse the repository at this point in the history
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 <ndevos@redhat.com>
  • Loading branch information
nixpanic authored and mergify[bot] committed May 25, 2021
1 parent c0ab4c0 commit 25d0a1c
Showing 1 changed file with 55 additions and 0 deletions.
55 changes: 55 additions & 0 deletions internal/rbd/nodeserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

0 comments on commit 25d0a1c

Please sign in to comment.