Skip to content

Commit e87f506

Browse files
authored
cinder-csi: Adds support for managing backups (kubernetes#2473) (kubernetes#2480)
Signed-off-by: Sebastian-RG <fullmetalliferous@gmail.com>
1 parent 6eda649 commit e87f506

12 files changed

Lines changed: 758 additions & 102 deletions

File tree

docs/cinder-csi-plugin/using-cinder-csi-plugin.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,9 @@ helm install --namespace kube-system --name cinder-csi ./charts/cinder-csi-plugi
267267
| StorageClass `parameters` | `availability` | `nova` | String. Volume Availability Zone |
268268
| StorageClass `parameters` | `type` | Empty String | String. Name/ID of Volume type. Corresponding volume type should exist in cinder |
269269
| VolumeSnapshotClass `parameters` | `force-create` | `false` | Enable to support creating snapshot for a volume in in-use status |
270-
| Inline Volume `volumeAttributes` | `capacity` | `1Gi` | volume size for creating inline volumes|
270+
| 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 |
271+
| 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 |
272+
| Inline Volume `volumeAttributes` | `capacity` | `1Gi` | volume size for creating inline volumes|
271273
| Inline Volume `VolumeAttributes` | `type` | Empty String | Name/ID of Volume type. Corresponding volume type should exist in cinder |
272274

273275
## Local Development

pkg/csi/cinder/controllerserver.go

Lines changed: 273 additions & 54 deletions
Large diffs are not rendered by default.

pkg/csi/cinder/controllerserver_test.go

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ func init() {
4444
func TestCreateVolume(t *testing.T) {
4545
// mock OpenStack
4646
properties := map[string]string{"cinder.csi.openstack.org/cluster": FakeCluster}
47-
// CreateVolume(name string, size int, vtype, availability string, snapshotID string, tags *map[string]string) (string, string, int, error)
48-
osmock.On("CreateVolume", FakeVolName, mock.AnythingOfType("int"), FakeVolType, FakeAvailability, "", "", &properties).Return(&FakeVol, nil)
47+
// CreateVolume(name string, size int, vtype, availability string, snapshotID string, sourceVolID string, sourceBackupID string, tags map[string]string) (string, string, int, error)
48+
osmock.On("CreateVolume", FakeVolName, mock.AnythingOfType("int"), FakeVolType, FakeAvailability, "", "", "", properties).Return(&FakeVol, nil)
4949

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

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

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

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

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

193193
// Init assert
@@ -233,8 +233,8 @@ func TestCreateVolumeFromSnapshot(t *testing.T) {
233233

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

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

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

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

@@ -487,7 +489,7 @@ func TestCreateSnapshotWithExtraMetadata(t *testing.T) {
487489
openstack.SnapshotForceCreate: "true",
488490
}
489491

490-
osmock.On("CreateSnapshot", FakeSnapshotName, FakeVolID, &properties).Return(&FakeSnapshotRes, nil)
492+
osmock.On("CreateSnapshot", FakeSnapshotName, FakeVolID, properties).Return(&FakeSnapshotRes, nil)
491493
osmock.On("ListSnapshots", map[string]string{"Name": FakeSnapshotName}).Return(FakeSnapshotListEmpty, "", nil)
492494
osmock.On("WaitSnapshotReady", FakeSnapshotID).Return(nil)
493495

@@ -522,6 +524,7 @@ func TestCreateSnapshotWithExtraMetadata(t *testing.T) {
522524
func TestDeleteSnapshot(t *testing.T) {
523525
// DeleteSnapshot(volumeID string) error
524526
osmock.On("DeleteSnapshot", FakeSnapshotID).Return(nil)
527+
osmock.On("DeleteBackup", FakeSnapshotID).Return(nil)
525528

526529
// Init assert
527530
assert := assert.New(t)

pkg/csi/cinder/fake.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package cinder
1818

1919
import (
20+
"github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/backups"
2021
"github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots"
2122
"github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes"
2223
"golang.org/x/net/context"
@@ -94,6 +95,7 @@ var FakeSnapshotRes = snapshots.Snapshot{
9495
Name: "fake-snapshot",
9596
VolumeID: FakeVolID,
9697
Size: 1,
98+
Status: "available",
9799
}
98100

99101
var FakeSnapshotsRes = []snapshots.Snapshot{FakeSnapshotRes}
@@ -102,6 +104,7 @@ var FakeVolListMultiple = []volumes.Volume{FakeVol1, FakeVol3}
102104
var FakeVolList = []volumes.Volume{FakeVol1}
103105
var FakeVolListEmpty = []volumes.Volume{}
104106
var FakeSnapshotListEmpty = []snapshots.Snapshot{}
107+
var FakeBackupListEmpty = []backups.Backup{}
105108

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

pkg/csi/cinder/nodeserver.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ func nodePublishEphemeral(req *csi.NodePublishVolumeRequest, ns *nodeServer) (*c
150150
volumeType = ""
151151
}
152152

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

155155
if err != nil {
156156
klog.V(3).Infof("Failed to Create Ephemeral Volume: %v", err)

pkg/csi/cinder/nodeserver_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ func TestNodePublishVolumeEphermeral(t *testing.T) {
129129
fvolName := fmt.Sprintf("ephemeral-%s", FakeVolID)
130130
tState := []string{"available"}
131131

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

134134
omock.On("AttachVolume", FakeNodeID, FakeVolID).Return(FakeVolID, nil)
135135
omock.On("WaitDiskAttached", FakeNodeID, FakeVolID).Return(nil)

pkg/csi/cinder/openstack/openstack.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323

2424
"github.com/gophercloud/gophercloud"
2525
"github.com/gophercloud/gophercloud/openstack"
26+
"github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/backups"
2627
"github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots"
2728
"github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes"
2829
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
@@ -44,7 +45,7 @@ func AddExtraFlags(fs *pflag.FlagSet) {
4445
}
4546

4647
type IOpenStack interface {
47-
CreateVolume(name string, size int, vtype, availability string, snapshotID string, sourcevolID string, tags *map[string]string) (*volumes.Volume, error)
48+
CreateVolume(name string, size int, vtype, availability string, snapshotID string, sourceVolID string, sourceBackupID string, tags map[string]string) (*volumes.Volume, error)
4849
DeleteVolume(volumeID string) error
4950
AttachVolume(instanceID, volumeID string) (string, error)
5051
ListVolumes(limit int, startingToken string) ([]volumes.Volume, string, error)
@@ -55,11 +56,17 @@ type IOpenStack interface {
5556
GetAttachmentDiskPath(instanceID, volumeID string) (string, error)
5657
GetVolume(volumeID string) (*volumes.Volume, error)
5758
GetVolumesByName(name string) ([]volumes.Volume, error)
58-
CreateSnapshot(name, volID string, tags *map[string]string) (*snapshots.Snapshot, error)
59+
CreateSnapshot(name, volID string, tags map[string]string) (*snapshots.Snapshot, error)
5960
ListSnapshots(filters map[string]string) ([]snapshots.Snapshot, string, error)
6061
DeleteSnapshot(snapID string) error
6162
GetSnapshotByID(snapshotID string) (*snapshots.Snapshot, error)
62-
WaitSnapshotReady(snapshotID string) error
63+
WaitSnapshotReady(snapshotID string) (string, error)
64+
CreateBackup(name, volID string, snapshotID string, tags map[string]string) (*backups.Backup, error)
65+
ListBackups(filters map[string]string) ([]backups.Backup, error)
66+
DeleteBackup(backupID string) error
67+
GetBackupByID(backupID string) (*backups.Backup, error)
68+
BackupsAreEnabled() (bool, error)
69+
WaitBackupReady(backupID string, snapshotSize int, backupMaxDurationSecondsPerGB int) (string, error)
6370
GetInstanceByID(instanceID string) (*servers.Server, error)
6471
ExpandVolume(volumeID string, status string, size int) error
6572
GetMaxVolLimit() int64

0 commit comments

Comments
 (0)