Skip to content

Commit

Permalink
feat: csi-cinder storage capacity
Browse files Browse the repository at this point in the history
Available capacity of disk storage

Signed-off-by: Serge Logvinov <[email protected]>
  • Loading branch information
sergelogvinov committed Oct 15, 2024
1 parent a294df6 commit e932508
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 5 deletions.
21 changes: 17 additions & 4 deletions docs/cinder-csi-plugin/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- [Plugin Features](#plugin-features)
- [Dynamic Provisioning](#dynamic-provisioning)
- [Topology](#topology)
- [Storage capacity](#storage-capacity)
- [Block Volume](#block-volume)
- [Volume Expansion](#volume-expansion)
- [Rescan on in-use volume resize](#rescan-on-in-use-volume-resize)
Expand All @@ -24,7 +25,7 @@

Dynamic Provisioning uses persistence volume claim (PVC) to request the Kubernetes to create the Cinder volume on behalf of user and consumes the volume from inside container.

For usage, refer [sample app](./examples.md#dynamic-volume-provisioning)
For usage, refer [sample app](./examples.md#dynamic-volume-provisioning)

## Topology

Expand All @@ -35,10 +36,22 @@ This feature enables driver to consider the topology constraints while creating
`topology.cinder.csi.openstack.org/zone` : Availability by Zone
* `allowedTopologies` can be specified in storage class to restrict the topology of provisioned volumes to specific zones and should be used as replacement of `availability` parameter.
* To disable: set `--feature-gates=Topology=false` in external-provisioner (container `csi-provisioner` of `csi-cinder-controllerplugin`).
* If using Helm, it can be disabled by setting `Values.csi.provisioner.topology: "false"`
* If using Helm, it can be disabled by setting `Values.csi.provisioner.topology: "false"`

For usage, refer [sample app](./examples.md#use-topology)

## Storage Capacity

This feature enables driver to consider the storage capacity constraints while creating the volume. For more info, refer [Capacity Support](https://github.com/kubernetes-csi/external-provisioner/blob/master/README.md#capacity-support)

The driver will expose the storage capacity limits of the openstack project.
The capacity is calculated based on the quota, which is a sum of all the volumes and snapshots created by the csi project account.

* Disabled by default
* To enable: set `--enable-capacity` and `--capacity-ownerref-level=2` in external-provisioner (container `csi-provisioner` of `csi-cinder-controllerplugin`), you need to have `StorageCapacity: true` in CSIDriver object.
* If using Helm, it can be enabled by setting `Values.csi.provisioner.capacity: "true"`
* To change frequency of capacity check, set `--capacity-check-interval` in external-provisioner

## Block Volume

Cinder volumes to be exposed inside containers as a block device instead of as a mounted file system. The corresponding CSI feature (CSIBlockVolume) is GA since Kubernetes 1.18.
Expand All @@ -51,7 +64,7 @@ For usage, refer [sample app](./examples.md#using-block-volume)

## Volume Expansion

Driver supports both `Offline` and `Online` resize of cinder volumes. Cinder online resize support is available since cinder 3.42 microversion.
Driver supports both `Offline` and `Online` resize of cinder volumes. Cinder online resize support is available since cinder 3.42 microversion.
The same should be supported by underlying OpenStack Cloud to avail the feature.

* As of kubernetes v1.16, Volume Expansion is a beta feature and enabled by default.
Expand Down Expand Up @@ -81,7 +94,7 @@ Two different Kubernetes features allow volumes to follow the Pod's lifecycle: C

This feature allows CSI volumes to be directly embedded in the Pod specification instead of a PersistentVolume. Volumes specified in this way are ephemeral and do not persist across Pod restarts.

* As of Kubernetes v1.16 this feature is beta so enabled by default.
* As of Kubernetes v1.16 this feature is beta so enabled by default.
* To enable this feature for CSI Driver, `volumeLifecycleModes` needs to be specified in [CSIDriver](../../manifests/cinder-csi-plugin/csi-cinder-driver.yaml) object. The driver can run in `Persistent` mode, `Ephemeral` or in both modes.
* `podInfoOnMount` must be `true` to use this feature.
* For usage, refer [sample app](./examples.md#deploy-app-using-inline-volumes)
Expand Down
1 change: 1 addition & 0 deletions docs/cinder-csi-plugin/using-cinder-csi-plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ helm install --namespace kube-system --name cinder-csi ./charts/cinder-csi-plugi

* [Dynamic Provisioning](./features.md#dynamic-provisioning)
* [Topology](./features.md#topology)
* [Storage capacity](./features.md#storage-capacity)
* [Raw Block Volume](./features.md#block-volume)
* [Volume Expansion](./features.md#volume-expansion)
* [Volume Cloning](./features.md#volume-cloning)
Expand Down
2 changes: 2 additions & 0 deletions manifests/cinder-csi-plugin/cinder-csi-controllerplugin.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ spec:
- "--timeout=3m"
- "--default-fstype=ext4"
- "--feature-gates=Topology=true"
- "--enable-capacity"
- "--capacity-ownerref-level=2"
- "--extra-create-metadata"
- "--leader-election=true"
env:
Expand Down
1 change: 1 addition & 0 deletions manifests/cinder-csi-plugin/csi-cinder-driver.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ metadata:
spec:
attachRequired: true
podInfoOnMount: true
storageCapacity: true
volumeLifecycleModes:
- Persistent
- Ephemeral
24 changes: 23 additions & 1 deletion pkg/csi/cinder/controllerserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -949,7 +950,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 {
return &csi.GetCapacityResponse{}, 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
}

func (cs *controllerServer) ControllerGetVolume(ctx context.Context, req *csi.ControllerGetVolumeRequest) (*csi.ControllerGetVolumeResponse, error) {
Expand Down
1 change: 1 addition & 0 deletions pkg/csi/cinder/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,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,
Expand Down
1 change: 1 addition & 0 deletions pkg/csi/cinder/openstack/openstack.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/csi/cinder/openstack/openstack_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
17 changes: 17 additions & 0 deletions pkg/csi/cinder/openstack/openstack_volumes.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions tests/sanity/cinder/fakecloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down

0 comments on commit e932508

Please sign in to comment.