@@ -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
201276func (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