Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cinder-csi: Adds support for managing backups (kubernetes#2473) #2480

Merged
merged 1 commit into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/cinder-csi-plugin/using-cinder-csi-plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,9 @@ helm install --namespace kube-system --name cinder-csi ./charts/cinder-csi-plugi
| StorageClass `parameters` | `availability` | `nova` | String. Volume Availability Zone |
| StorageClass `parameters` | `type` | Empty String | String. Name/ID of Volume type. Corresponding volume type should exist in cinder |
| VolumeSnapshotClass `parameters` | `force-create` | `false` | Enable to support creating snapshot for a volume in in-use status |
| Inline Volume `volumeAttributes` | `capacity` | `1Gi` | volume size for creating inline volumes|
| VolumeSnapshotClass `parameters` | `type` | Empty String | `snapshot` creates a VolumeSnapshot object linked to a Cinder volume snapshot. `backup` creates a VolumeSnapshot object linked to a cinder volume backup. Defaults to `snapshot` if not defined |
| VolumeSnapshotClass `parameters` | `backup-max-duration-seconds-per-gb` | `20` | Defines the amount of time to wait for a backup to complete in seconds per GB of volume size |
| Inline Volume `volumeAttributes` | `capacity` | `1Gi` | volume size for creating inline volumes|
| Inline Volume `VolumeAttributes` | `type` | Empty String | Name/ID of Volume type. Corresponding volume type should exist in cinder |

## Local Development
Expand Down
327 changes: 273 additions & 54 deletions pkg/csi/cinder/controllerserver.go

Large diffs are not rendered by default.

31 changes: 17 additions & 14 deletions pkg/csi/cinder/controllerserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ func init() {
func TestCreateVolume(t *testing.T) {
// mock OpenStack
properties := map[string]string{"cinder.csi.openstack.org/cluster": FakeCluster}
// CreateVolume(name string, size int, vtype, availability string, snapshotID string, tags *map[string]string) (string, string, int, error)
osmock.On("CreateVolume", FakeVolName, mock.AnythingOfType("int"), FakeVolType, FakeAvailability, "", "", &properties).Return(&FakeVol, nil)
// CreateVolume(name string, size int, vtype, availability string, snapshotID string, sourceVolID string, sourceBackupID string, tags map[string]string) (string, string, int, error)
osmock.On("CreateVolume", FakeVolName, mock.AnythingOfType("int"), FakeVolType, FakeAvailability, "", "", "", properties).Return(&FakeVol, nil)

osmock.On("GetVolumesByName", FakeVolName).Return(FakeVolListEmpty, nil)
// Init assert
Expand Down Expand Up @@ -90,9 +90,9 @@ func TestCreateVolume(t *testing.T) {
func TestCreateVolumeWithParam(t *testing.T) {
// mock OpenStack
properties := map[string]string{"cinder.csi.openstack.org/cluster": FakeCluster}
// CreateVolume(name string, size int, vtype, availability string, snapshotID string, tags *map[string]string) (string, string, int, error)
// CreateVolume(name string, size int, vtype, availability string, snapshotID string, sourceVolID string, sourceBackupID string, tags map[string]string) (string, string, int, error)
// Vol type and availability comes from CreateVolumeRequest.Parameters
osmock.On("CreateVolume", FakeVolName, mock.AnythingOfType("int"), "dummyVolType", "cinder", "", "", &properties).Return(&FakeVol, nil)
osmock.On("CreateVolume", FakeVolName, mock.AnythingOfType("int"), "dummyVolType", "cinder", "", "", "", properties).Return(&FakeVol, nil)

osmock.On("GetVolumesByName", FakeVolName).Return(FakeVolListEmpty, nil)
// Init assert
Expand Down Expand Up @@ -146,8 +146,8 @@ func TestCreateVolumeWithExtraMetadata(t *testing.T) {
"csi.storage.k8s.io/pvc/name": FakePVCName,
"csi.storage.k8s.io/pvc/namespace": FakePVCNamespace,
}
// CreateVolume(name string, size int, vtype, availability string, snapshotID string, tags *map[string]string) (string, string, int, error)
osmock.On("CreateVolume", FakeVolName, mock.AnythingOfType("int"), FakeVolType, FakeAvailability, "", "", &properties).Return(&FakeVol, nil)
// CreateVolume(name string, size int, vtype, availability string, snapshotID string, sourceVolID string, sourceBackupID string, tags map[string]string) (string, string, int, error)
osmock.On("CreateVolume", FakeVolName, mock.AnythingOfType("int"), FakeVolType, FakeAvailability, "", "", "", properties).Return(&FakeVol, nil)

osmock.On("GetVolumesByName", FakeVolName).Return(FakeVolListEmpty, nil)

Expand Down Expand Up @@ -186,8 +186,8 @@ func TestCreateVolumeWithExtraMetadata(t *testing.T) {

func TestCreateVolumeFromSnapshot(t *testing.T) {
properties := map[string]string{"cinder.csi.openstack.org/cluster": FakeCluster}
// CreateVolume(name string, size int, vtype, availability string, snapshotID string, tags *map[string]string) (string, string, int, error)
osmock.On("CreateVolume", FakeVolName, mock.AnythingOfType("int"), FakeVolType, "", FakeSnapshotID, "", &properties).Return(&FakeVolFromSnapshot, nil)
// CreateVolume(name string, size int, vtype, availability string, snapshotID string, sourceVolID string, sourceBackupID string, tags map[string]string) (string, string, int, error)
osmock.On("CreateVolume", FakeVolName, mock.AnythingOfType("int"), FakeVolType, "", FakeSnapshotID, "", "", properties).Return(&FakeVolFromSnapshot, nil)
osmock.On("GetVolumesByName", FakeVolName).Return(FakeVolListEmpty, nil)

// Init assert
Expand Down Expand Up @@ -233,8 +233,8 @@ func TestCreateVolumeFromSnapshot(t *testing.T) {

func TestCreateVolumeFromSourceVolume(t *testing.T) {
properties := map[string]string{"cinder.csi.openstack.org/cluster": FakeCluster}
// CreateVolume(name string, size int, vtype, availability string, snapshotID string, tags *map[string]string) (string, string, int, error)
osmock.On("CreateVolume", FakeVolName, mock.AnythingOfType("int"), FakeVolType, "", "", FakeVolID, &properties).Return(&FakeVolFromSourceVolume, nil)
// CreateVolume(name string, size int, vtype, availability string, snapshotID string, sourceVolID string, sourceBackupID string, tags map[string]string) (string, string, int, error)
osmock.On("CreateVolume", FakeVolName, mock.AnythingOfType("int"), FakeVolType, "", "", FakeVolID, "", properties).Return(&FakeVolFromSourceVolume, nil)
osmock.On("GetVolumesByName", FakeVolName).Return(FakeVolListEmpty, nil)

// Init assert
Expand Down Expand Up @@ -451,10 +451,12 @@ func TestListVolumes(t *testing.T) {

// Test CreateSnapshot
func TestCreateSnapshot(t *testing.T) {
osmock.On("CreateSnapshot", FakeSnapshotName, FakeVolID, &map[string]string{cinderCSIClusterIDKey: "cluster"}).Return(&FakeSnapshotRes, nil)
osmock.On("ListSnapshots", map[string]string{"Name": FakeSnapshotName}).Return(FakeSnapshotListEmpty, "", nil)
osmock.On("WaitSnapshotReady", FakeSnapshotID).Return(nil)

osmock.On("CreateSnapshot", FakeSnapshotName, FakeVolID, map[string]string{cinderCSIClusterIDKey: "cluster"}).Return(&FakeSnapshotRes, nil)
osmock.On("ListSnapshots", map[string]string{"Name": FakeSnapshotName}).Return(FakeSnapshotListEmpty, "", nil)
osmock.On("WaitSnapshotReady", FakeSnapshotID).Return(FakeSnapshotRes.Status, nil)
osmock.On("ListBackups", map[string]string{"Name": FakeSnapshotName}).Return(FakeBackupListEmpty, nil)
osmock.On("GetSnapshotByID", FakeVolID).Return(&FakeSnapshotRes, nil)
// Init assert
assert := assert.New(t)

Expand Down Expand Up @@ -487,7 +489,7 @@ func TestCreateSnapshotWithExtraMetadata(t *testing.T) {
openstack.SnapshotForceCreate: "true",
}

osmock.On("CreateSnapshot", FakeSnapshotName, FakeVolID, &properties).Return(&FakeSnapshotRes, nil)
osmock.On("CreateSnapshot", FakeSnapshotName, FakeVolID, properties).Return(&FakeSnapshotRes, nil)
osmock.On("ListSnapshots", map[string]string{"Name": FakeSnapshotName}).Return(FakeSnapshotListEmpty, "", nil)
osmock.On("WaitSnapshotReady", FakeSnapshotID).Return(nil)

Expand Down Expand Up @@ -522,6 +524,7 @@ func TestCreateSnapshotWithExtraMetadata(t *testing.T) {
func TestDeleteSnapshot(t *testing.T) {
// DeleteSnapshot(volumeID string) error
osmock.On("DeleteSnapshot", FakeSnapshotID).Return(nil)
osmock.On("DeleteBackup", FakeSnapshotID).Return(nil)

// Init assert
assert := assert.New(t)
Expand Down
3 changes: 3 additions & 0 deletions pkg/csi/cinder/fake.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package cinder

import (
"github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/backups"
"github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots"
"github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes"
"golang.org/x/net/context"
Expand Down Expand Up @@ -94,6 +95,7 @@ var FakeSnapshotRes = snapshots.Snapshot{
Name: "fake-snapshot",
VolumeID: FakeVolID,
Size: 1,
Status: "available",
}

var FakeSnapshotsRes = []snapshots.Snapshot{FakeSnapshotRes}
Expand All @@ -102,6 +104,7 @@ var FakeVolListMultiple = []volumes.Volume{FakeVol1, FakeVol3}
var FakeVolList = []volumes.Volume{FakeVol1}
var FakeVolListEmpty = []volumes.Volume{}
var FakeSnapshotListEmpty = []snapshots.Snapshot{}
var FakeBackupListEmpty = []backups.Backup{}

var FakeInstanceID = "321a8b81-3660-43e5-bab8-6470b65ee4e8"

Expand Down
2 changes: 1 addition & 1 deletion pkg/csi/cinder/nodeserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ func nodePublishEphemeral(req *csi.NodePublishVolumeRequest, ns *nodeServer) (*c
volumeType = ""
}

evol, err := ns.Cloud.CreateVolume(volName, size, volumeType, volAvailability, "", "", &properties)
evol, err := ns.Cloud.CreateVolume(volName, size, volumeType, volAvailability, "", "", "", properties)

if err != nil {
klog.V(3).Infof("Failed to Create Ephemeral Volume: %v", err)
Expand Down
2 changes: 1 addition & 1 deletion pkg/csi/cinder/nodeserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func TestNodePublishVolumeEphermeral(t *testing.T) {
fvolName := fmt.Sprintf("ephemeral-%s", FakeVolID)
tState := []string{"available"}

omock.On("CreateVolume", fvolName, 2, "test", "nova", "", "", &properties).Return(&FakeVol, nil)
omock.On("CreateVolume", fvolName, 2, "test", "nova", "", "", "", properties).Return(&FakeVol, nil)

omock.On("AttachVolume", FakeNodeID, FakeVolID).Return(FakeVolID, nil)
omock.On("WaitDiskAttached", FakeNodeID, FakeVolID).Return(nil)
Expand Down
13 changes: 10 additions & 3 deletions pkg/csi/cinder/openstack/openstack.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/openstack"
"github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/backups"
"github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots"
"github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
Expand All @@ -44,7 +45,7 @@ func AddExtraFlags(fs *pflag.FlagSet) {
}

type IOpenStack interface {
CreateVolume(name string, size int, vtype, availability string, snapshotID string, sourcevolID string, tags *map[string]string) (*volumes.Volume, error)
CreateVolume(name string, size int, vtype, availability string, snapshotID string, sourceVolID string, sourceBackupID string, tags map[string]string) (*volumes.Volume, error)
DeleteVolume(volumeID string) error
AttachVolume(instanceID, volumeID string) (string, error)
ListVolumes(limit int, startingToken string) ([]volumes.Volume, string, error)
Expand All @@ -55,11 +56,17 @@ type IOpenStack interface {
GetAttachmentDiskPath(instanceID, volumeID string) (string, error)
GetVolume(volumeID string) (*volumes.Volume, error)
GetVolumesByName(name string) ([]volumes.Volume, error)
CreateSnapshot(name, volID string, tags *map[string]string) (*snapshots.Snapshot, error)
CreateSnapshot(name, volID string, tags map[string]string) (*snapshots.Snapshot, error)
ListSnapshots(filters map[string]string) ([]snapshots.Snapshot, string, error)
DeleteSnapshot(snapID string) error
GetSnapshotByID(snapshotID string) (*snapshots.Snapshot, error)
WaitSnapshotReady(snapshotID string) error
WaitSnapshotReady(snapshotID string) (string, error)
CreateBackup(name, volID string, snapshotID string, tags map[string]string) (*backups.Backup, error)
ListBackups(filters map[string]string) ([]backups.Backup, error)
DeleteBackup(backupID string) error
GetBackupByID(backupID string) (*backups.Backup, error)
BackupsAreEnabled() (bool, error)
WaitBackupReady(backupID string, snapshotSize int, backupMaxDurationSecondsPerGB int) (string, error)
GetInstanceByID(instanceID string) (*servers.Server, error)
ExpandVolume(volumeID string, status string, size int) error
GetMaxVolLimit() int64
Expand Down
Loading