Skip to content

Commit 2442ed6

Browse files
authored
exposed pvc metrics to kubelet (#284)
* exposed pvc metrics to kubelet * exposed pvc metrics to kubelet * added description for variable blkGetSize64
1 parent a3435ba commit 2442ed6

File tree

1 file changed

+123
-2
lines changed

1 file changed

+123
-2
lines changed

pkg/spdk/nodeserver.go

Lines changed: 123 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ import (
2424
"strconv"
2525
"time"
2626

27+
"path/filepath"
28+
"unsafe"
29+
30+
"golang.org/x/sys/unix"
31+
2732
"github.com/container-storage-interface/spec/lib/go/csi"
2833
"google.golang.org/grpc"
2934
"google.golang.org/grpc/codes"
@@ -194,8 +199,78 @@ func (ns *nodeServer) buildAccessibleTopology(ctx context.Context) map[string]st
194199
return segments
195200
}
196201

197-
func (ns *nodeServer) NodeGetVolumeStats(_ context.Context, _ *csi.NodeGetVolumeStatsRequest) (*csi.NodeGetVolumeStatsResponse, error) {
198-
return nil, status.Error(codes.Unimplemented, "")
202+
func (ns *nodeServer) NodeGetVolumeStats(ctx context.Context, req *csi.NodeGetVolumeStatsRequest) (*csi.NodeGetVolumeStatsResponse, error) {
203+
volID := req.GetVolumeId()
204+
volumePath := req.GetVolumePath()
205+
206+
if volID == "" {
207+
return nil, status.Error(codes.InvalidArgument, "volume_id is required")
208+
}
209+
if volumePath == "" {
210+
return nil, status.Error(codes.InvalidArgument, "volume_path is required")
211+
}
212+
213+
st, err := os.Stat(volumePath)
214+
if err != nil {
215+
if os.IsNotExist(err) {
216+
return nil, status.Error(codes.NotFound, "volume_path not found")
217+
}
218+
return nil, status.Errorf(codes.Internal, "stat volume_path %q: %v", volumePath, err)
219+
}
220+
221+
if st.IsDir() {
222+
var s unix.Statfs_t
223+
if err := unix.Statfs(volumePath, &s); err != nil {
224+
return nil, status.Errorf(codes.Internal, "statfs %q: %v", volumePath, err)
225+
}
226+
227+
totalBytes := int64(s.Blocks) * int64(s.Bsize)
228+
availBytes := int64(s.Bavail) * int64(s.Bsize)
229+
usedBytes := totalBytes - availBytes
230+
if usedBytes < 0 {
231+
usedBytes = 0
232+
}
233+
234+
totalInodes := int64(s.Files)
235+
availInodes := int64(s.Ffree)
236+
usedInodes := totalInodes - availInodes
237+
if usedInodes < 0 {
238+
usedInodes = 0
239+
}
240+
241+
return &csi.NodeGetVolumeStatsResponse{
242+
Usage: []*csi.VolumeUsage{
243+
{
244+
Unit: csi.VolumeUsage_BYTES,
245+
Total: totalBytes,
246+
Used: usedBytes,
247+
Available: availBytes,
248+
},
249+
{
250+
Unit: csi.VolumeUsage_INODES,
251+
Total: totalInodes,
252+
Used: usedInodes,
253+
Available: availInodes,
254+
},
255+
},
256+
}, nil
257+
}
258+
259+
sizeBytes, err := getBlockSizeBytes(volumePath)
260+
if err != nil {
261+
return nil, status.Errorf(codes.Internal, "get block size for %q: %v", volumePath, err)
262+
}
263+
264+
return &csi.NodeGetVolumeStatsResponse{
265+
Usage: []*csi.VolumeUsage{
266+
{
267+
Unit: csi.VolumeUsage_BYTES,
268+
Total: int64(sizeBytes),
269+
Used: 0,
270+
Available: int64(sizeBytes),
271+
},
272+
},
273+
}, nil
199274
}
200275

201276
func (ns *nodeServer) NodeStageVolume(_ context.Context, req *csi.NodeStageVolumeRequest) (*csi.NodeStageVolumeResponse, error) {
@@ -338,6 +413,13 @@ func (ns *nodeServer) NodeGetCapabilities(_ context.Context, _ *csi.NodeGetCapab
338413
},
339414
},
340415
},
416+
{
417+
Type: &csi.NodeServiceCapability_Rpc{
418+
Rpc: &csi.NodeServiceCapability_RPC{
419+
Type: csi.NodeServiceCapability_RPC_GET_VOLUME_STATS,
420+
},
421+
},
422+
},
341423
},
342424
}, nil
343425
}
@@ -608,3 +690,42 @@ func getStagingTargetPath(req interface{}) string {
608690
}
609691
return ""
610692
}
693+
694+
func getBlockSizeBytes(volumePath string) (uint64, error) {
695+
if size, err := ioctlBlkGetSize64(volumePath); err == nil && size > 0 {
696+
return size, nil
697+
}
698+
699+
rp, err := filepath.EvalSymlinks(volumePath)
700+
if err == nil && rp != "" && rp != volumePath {
701+
if size, err2 := ioctlBlkGetSize64(rp); err2 == nil && size > 0 {
702+
return size, nil
703+
}
704+
}
705+
706+
return 0, fmt.Errorf("BLKGETSIZE64 ioctl failed for %q", volumePath)
707+
}
708+
709+
func ioctlBlkGetSize64(path string) (uint64, error) {
710+
f, err := os.Open(path)
711+
if err != nil {
712+
return 0, err
713+
}
714+
defer f.Close()
715+
716+
// blkGetSize64 is the Linux BLKGETSIZE64 ioctl code.
717+
// It returns the total size (in bytes) of a block device.
718+
var blkGetSize64 = 0x80081272
719+
720+
var size uint64
721+
_, _, errno := unix.Syscall(
722+
unix.SYS_IOCTL,
723+
f.Fd(),
724+
uintptr(blkGetSize64),
725+
uintptr(unsafe.Pointer(&size)),
726+
)
727+
if errno != 0 {
728+
return 0, errno
729+
}
730+
return size, nil
731+
}

0 commit comments

Comments
 (0)