// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package driver import ( "context" "github.com/container-storage-interface/spec/lib/go/csi" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "k8s.io/klog/v2" "github.com/aws/amazon-ecs-agent/ecs-agent/daemonimages/csidriver/util" "github.com/aws/amazon-ecs-agent/ecs-agent/daemonimages/csidriver/volume" ) // nodeService represents the node service of CSI driver. type nodeService struct { mounter Mounter // UnimplementedNodeServer implements all interfaces with empty implementation. As one mini version of csi driver, // we only need to override the necessary interfaces. csi.UnimplementedNodeServer } func newNodeService() nodeService { klog.V(4).InfoS("New node service") nodeMounter, err := newNodeMounter() if err != nil { panic(err) } return nodeService{ mounter: nodeMounter, } } func (d *nodeService) NodeGetVolumeStats(ctx context.Context, req *csi.NodeGetVolumeStatsRequest) (*csi.NodeGetVolumeStatsResponse, error) { klog.V(4).InfoS("NodeGetVolumeStats: called", "args", *req) if len(req.VolumeId) == 0 { return nil, status.Error(codes.InvalidArgument, "NodeGetVolumeStats volume ID was empty") } if len(req.VolumePath) == 0 { return nil, status.Error(codes.InvalidArgument, "NodeGetVolumeStats volume path was empty") } exists, err := d.mounter.PathExists(req.VolumePath) if err != nil { return nil, status.Errorf(codes.Internal, "unknown error when stat on %s: %v", req.VolumePath, err) } if !exists { return nil, status.Errorf(codes.NotFound, "path %s does not exist", req.VolumePath) } isBlock, err := util.IsBlockDevice(req.VolumePath) if err != nil { return nil, status.Errorf(codes.Internal, "failed to determine whether %s is block device: %v", req.VolumePath, err) } if isBlock { bcap, blockErr := d.getBlockSizeBytes(req.VolumePath) if blockErr != nil { return nil, status.Errorf(codes.Internal, "failed to get block capacity on path %s: %v", req.VolumePath, err) } return &csi.NodeGetVolumeStatsResponse{ Usage: []*csi.VolumeUsage{ { Unit: csi.VolumeUsage_BYTES, Total: bcap, }, }, }, nil } metricsProvider := volume.NewMetricsStatFS(req.VolumePath) metrics, err := metricsProvider.GetMetrics() if err != nil { return nil, status.Errorf(codes.Internal, "failed to get fs info on path %s: %v", req.VolumePath, err) } return &csi.NodeGetVolumeStatsResponse{ Usage: []*csi.VolumeUsage{ { Unit: csi.VolumeUsage_BYTES, Available: metrics.Available.AsDec().UnscaledBig().Int64(), Total: metrics.Capacity.AsDec().UnscaledBig().Int64(), Used: metrics.Used.AsDec().UnscaledBig().Int64(), }, { Unit: csi.VolumeUsage_INODES, Available: metrics.InodesFree.AsDec().UnscaledBig().Int64(), Total: metrics.Inodes.AsDec().UnscaledBig().Int64(), Used: metrics.InodesUsed.AsDec().UnscaledBig().Int64(), }, }, }, nil }