From 4fc81e359a19ab7d07aabffed3b0364530f57f67 Mon Sep 17 00:00:00 2001 From: Serge Logvinov Date: Thu, 16 May 2024 08:17:21 +0300 Subject: [PATCH] feat: csi-cinder storage capacity Available capacity of disk storage Signed-off-by: Serge Logvinov --- pkg/csi/cinder/controllerserver.go | 25 +++++++++++++++++-- pkg/csi/cinder/driver.go | 1 + pkg/csi/cinder/openstack/openstack.go | 1 + pkg/csi/cinder/openstack/openstack_mock.go | 4 +++ pkg/csi/cinder/openstack/openstack_volumes.go | 17 +++++++++++++ .../blockdevice/blockdevice_unsupported.go | 4 +++ tests/sanity/cinder/fakecloud.go | 4 +++ 7 files changed, 54 insertions(+), 2 deletions(-) diff --git a/pkg/csi/cinder/controllerserver.go b/pkg/csi/cinder/controllerserver.go index 595b34fb7d..b700a5dd57 100644 --- a/pkg/csi/cinder/controllerserver.go +++ b/pkg/csi/cinder/controllerserver.go @@ -33,6 +33,7 @@ import ( "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/timestamppb" + corev1 "k8s.io/api/core/v1" "k8s.io/cloud-provider-openstack/pkg/csi/cinder/openstack" "k8s.io/cloud-provider-openstack/pkg/util" cpoerrors "k8s.io/cloud-provider-openstack/pkg/util/errors" @@ -944,7 +945,28 @@ func (cs *controllerServer) ValidateVolumeCapabilities(ctx context.Context, req } func (cs *controllerServer) GetCapacity(ctx context.Context, req *csi.GetCapacityRequest) (*csi.GetCapacityResponse, error) { - return nil, status.Error(codes.Unimplemented, "GetCapacity is not yet implemented") + klog.V(4).Infof("GetCapacity: called with args %+v", protosanitizer.StripSecrets(*req)) + + topology := req.GetAccessibleTopology() + if topology != nil { + region := topology.GetSegments()[corev1.LabelTopologyRegion] + + cloud, cloudExist := cs.Clouds[region] + if !cloudExist { + return nil, status.Error(codes.InvalidArgument, "[GetCapacity] specified cloud undefined") + } + + availableCapacity, err := cloud.GetFreeQuotaStorageSpace() + if err != nil { + return nil, status.Errorf(codes.Internal, "[GetCapacity]: failed with error %v", err) + } + + return &csi.GetCapacityResponse{ + AvailableCapacity: int64(availableCapacity * 1024 * 1024 * 1024), + }, nil + } + + return &csi.GetCapacityResponse{}, nil } func (cs *controllerServer) ControllerGetVolume(ctx context.Context, req *csi.ControllerGetVolumeRequest) (*csi.ControllerGetVolumeResponse, error) { @@ -1112,5 +1134,4 @@ func getCreateVolumeResponse(vol *volumes.Volume, ignoreVolumeAZ bool, accessibl } return resp - } diff --git a/pkg/csi/cinder/driver.go b/pkg/csi/cinder/driver.go index 773b98a8cc..e355cc1728 100644 --- a/pkg/csi/cinder/driver.go +++ b/pkg/csi/cinder/driver.go @@ -93,6 +93,7 @@ func NewDriver(o *DriverOpts) *Driver { csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME, csi.ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT, csi.ControllerServiceCapability_RPC_LIST_SNAPSHOTS, + csi.ControllerServiceCapability_RPC_GET_CAPACITY, csi.ControllerServiceCapability_RPC_EXPAND_VOLUME, csi.ControllerServiceCapability_RPC_CLONE_VOLUME, csi.ControllerServiceCapability_RPC_LIST_VOLUMES_PUBLISHED_NODES, diff --git a/pkg/csi/cinder/openstack/openstack.go b/pkg/csi/cinder/openstack/openstack.go index dbf10dd11d..ca1d1f5cfe 100644 --- a/pkg/csi/cinder/openstack/openstack.go +++ b/pkg/csi/cinder/openstack/openstack.go @@ -70,6 +70,7 @@ type IOpenStack interface { GetInstanceByID(instanceID string) (*servers.Server, error) ExpandVolume(volumeID string, status string, size int) error GetMaxVolLimit() int64 + GetFreeQuotaStorageSpace() (int, error) GetMetadataOpts() metadata.Opts GetBlockStorageOpts() BlockStorageOpts } diff --git a/pkg/csi/cinder/openstack/openstack_mock.go b/pkg/csi/cinder/openstack/openstack_mock.go index 53263e1f8e..fa7e18c221 100644 --- a/pkg/csi/cinder/openstack/openstack_mock.go +++ b/pkg/csi/cinder/openstack/openstack_mock.go @@ -460,6 +460,10 @@ func (_m *OpenStackMock) GetMaxVolLimit() int64 { return 256 } +func (_m *OpenStackMock) GetFreeQuotaStorageSpace() (int, error) { + return 100, nil +} + func (_m *OpenStackMock) BackupsAreEnabled() (bool, error) { return true, nil } diff --git a/pkg/csi/cinder/openstack/openstack_volumes.go b/pkg/csi/cinder/openstack/openstack_volumes.go index 116f35c467..08ecf9c64b 100644 --- a/pkg/csi/cinder/openstack/openstack_volumes.go +++ b/pkg/csi/cinder/openstack/openstack_volumes.go @@ -23,6 +23,7 @@ import ( "time" "github.com/gophercloud/gophercloud/v2/openstack" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/limits" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach" "github.com/gophercloud/gophercloud/v2/pagination" @@ -396,6 +397,22 @@ func (os *OpenStack) GetMaxVolLimit() int64 { return defaultMaxVolAttachLimit } +// GetFreeQuotaStorageSpace returns the tenant quota capacity of the block storage, in GB +func (os *OpenStack) GetFreeQuotaStorageSpace() (int, error) { + mc := metrics.NewMetricContext("limits", "get") + + res, err := limits.Get(context.TODO(), os.blockstorage).Extract() + if mc.ObserveRequest(err) != nil { + return 0, err + } + + capacity := res.Absolute.MaxTotalVolumeGigabytes - res.Absolute.TotalGigabytesUsed + if capacity < 0 { + capacity = 0 + } + return capacity, nil +} + // diskIsAttached queries if a volume is attached to a compute instance func (os *OpenStack) diskIsAttached(instanceID, volumeID string) (bool, error) { volume, err := os.GetVolume(volumeID) diff --git a/pkg/util/blockdevice/blockdevice_unsupported.go b/pkg/util/blockdevice/blockdevice_unsupported.go index a75c69be7b..b9adad44d3 100644 --- a/pkg/util/blockdevice/blockdevice_unsupported.go +++ b/pkg/util/blockdevice/blockdevice_unsupported.go @@ -34,3 +34,7 @@ func GetBlockDeviceSize(path string) (int64, error) { func RescanBlockDeviceGeometry(devicePath string, deviceMountPath string, newSize int64) error { return errors.New("RescanBlockDeviceGeometry is not implemented for this OS") } + +func RescanDevice(devicePath string) error { + return errors.New("RescanDevice is not implemented for this OS") +} diff --git a/tests/sanity/cinder/fakecloud.go b/tests/sanity/cinder/fakecloud.go index 53c0dcba4f..f3e87d34fe 100644 --- a/tests/sanity/cinder/fakecloud.go +++ b/tests/sanity/cinder/fakecloud.go @@ -332,6 +332,10 @@ func (cloud *cloud) GetMaxVolLimit() int64 { return 256 } +func (cloud *cloud) GetFreeQuotaStorageSpace() (int, error) { + return 100, nil +} + func (cloud *cloud) GetMetadataOpts() metadata.Opts { var m metadata.Opts m.SearchOrder = fmt.Sprintf("%s,%s", "configDrive", "metadataService")