From 561cc26e4c7fa0700bb8bf1ba7812405864baee8 Mon Sep 17 00:00:00 2001 From: Humble Chirammal Date: Thu, 25 Jul 2019 10:45:06 +0530 Subject: [PATCH] Implement metrics for CephFS CSI driver Signed-off-by: Humble Chirammal --- pkg/cephfs/nodeserver.go | 95 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/pkg/cephfs/nodeserver.go b/pkg/cephfs/nodeserver.go index af4779435..e5c7eb6c9 100644 --- a/pkg/cephfs/nodeserver.go +++ b/pkg/cephfs/nodeserver.go @@ -23,11 +23,13 @@ import ( csicommon "github.com/ceph/ceph-csi/pkg/csi-common" "github.com/ceph/ceph-csi/pkg/util" + csipbv1 "github.com/container-storage-interface/spec/lib/go/csi" "github.com/container-storage-interface/spec/lib/go/csi" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "k8s.io/klog" + "k8s.io/kubernetes/pkg/volume" ) // NodeServer struct of ceph CSI driver with supported methods of CSI @@ -308,6 +310,92 @@ func (ns *NodeServer) NodeUnstageVolume(ctx context.Context, req *csi.NodeUnstag return &csi.NodeUnstageVolumeResponse{}, nil } +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()) + } + /* + volID := req.GetVolumeId() + + TODO: Map the volumeID to the targetpath. + + we need secret to connect to the ceph cluster to get the volumeID from volume + Name, however `secret` field/option is not available in NodeGetVolumeStats spec, + Below issue covers this request and once its available, we can do the validation + as per the spec. + https://github.com/container-storage-interface/spec/issues/371 + + */ + + isMnt, err := util.IsMountPoint(targetPath) + + if err != nil { + if os.IsNotExist(err) { + return nil, status.Errorf(codes.InvalidArgument, "targetpath %s doesnot exist", targetPath) + } + return nil, err + } + if !isMnt { + return nil, status.Errorf(codes.InvalidArgument, "targetpath %s is not mounted", targetPath) + } + + cephfsProvider := volume.NewMetricsStatFS(targetPath) + volMetrics, volMetErr := cephfsProvider.GetMetrics() + if volMetErr != nil { + return nil, status.Error(codes.Internal, volMetErr.Error()) + } + + available, ok := (*(volMetrics.Available)).AsInt64() + if !ok { + klog.Errorf("failed to fetch available bytes") + } + capacity, ok := (*(volMetrics.Capacity)).AsInt64() + if !ok { + klog.Errorf("failed to fetch capacity bytes") + return nil, status.Error(codes.Unknown, "failed to fetch capacity bytes") + } + used, ok := (*(volMetrics.Used)).AsInt64() + if !ok { + klog.Errorf("failed to fetch used bytes") + } + inodes, ok := (*(volMetrics.Inodes)).AsInt64() + if !ok { + klog.Errorf("failed to fetch available inodes") + return nil, status.Error(codes.Unknown, "failed to fetch available inodes") + + } + inodesFree, ok := (*(volMetrics.InodesFree)).AsInt64() + if !ok { + klog.Errorf("failed to fetch free inodes") + } + + inodesUsed, ok := (*(volMetrics.InodesUsed)).AsInt64() + if !ok { + klog.Errorf("failed to fetch used inodes") + } + return &csi.NodeGetVolumeStatsResponse{ + Usage: []*csi.VolumeUsage{ + { + Available: available, + Total: capacity, + Used: used, + Unit: csipbv1.VolumeUsage_BYTES, + }, + { + Available: inodesFree, + Total: inodes, + Used: inodesUsed, + Unit: csipbv1.VolumeUsage_INODES, + }, + }, + }, nil + +} + // NodeGetCapabilities returns the supported capabilities of the node server func (ns *NodeServer) NodeGetCapabilities(ctx context.Context, req *csi.NodeGetCapabilitiesRequest) (*csi.NodeGetCapabilitiesResponse, error) { return &csi.NodeGetCapabilitiesResponse{ @@ -319,6 +407,13 @@ func (ns *NodeServer) NodeGetCapabilities(ctx context.Context, req *csi.NodeGetC }, }, }, + { + Type: &csi.NodeServiceCapability_Rpc{ + Rpc: &csi.NodeServiceCapability_RPC{ + Type: csi.NodeServiceCapability_RPC_GET_VOLUME_STATS, + }, + }, + }, }, }, nil }