2018-01-09 18:59:50 +00:00
/ *
Copyright 2018 The Kubernetes Authors .
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License 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 rbd
import (
"fmt"
2018-10-09 10:08:56 +00:00
"os"
2018-08-08 05:42:17 +00:00
"os/exec"
"syscall"
2018-01-09 18:59:50 +00:00
2018-12-19 14:26:16 +00:00
"github.com/ceph/ceph-csi/pkg/util"
2019-01-15 16:20:41 +00:00
"github.com/container-storage-interface/spec/lib/go/csi"
2018-01-09 18:59:50 +00:00
"github.com/golang/glog"
2019-01-25 13:39:35 +00:00
"github.com/golang/protobuf/ptypes"
2018-11-24 19:18:24 +00:00
"github.com/golang/protobuf/ptypes/timestamp"
2018-03-06 22:33:57 +00:00
"github.com/kubernetes-csi/drivers/pkg/csi-common"
2018-01-09 18:59:50 +00:00
"github.com/pborman/uuid"
2018-10-09 10:08:56 +00:00
"github.com/pkg/errors"
2018-01-09 18:59:50 +00:00
"golang.org/x/net/context"
2018-03-06 22:33:57 +00:00
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
2018-01-09 18:59:50 +00:00
)
const (
oneGB = 1073741824
)
2019-01-28 11:47:06 +00:00
// ControllerServer struct of rbd CSI driver with supported methods of CSI
// controller server spec.
2019-01-17 07:51:06 +00:00
type ControllerServer struct {
2018-01-09 18:59:50 +00:00
* csicommon . DefaultControllerServer
2018-12-19 14:26:16 +00:00
MetadataStore util . CachePersister
}
var (
rbdVolumes = map [ string ] * rbdVolume { }
rbdSnapshots = map [ string ] * rbdSnapshot { }
)
2019-01-28 11:47:06 +00:00
// LoadExDataFromMetadataStore loads the rbd volume and snapshot
// info from metadata store
2019-01-17 07:51:06 +00:00
func ( cs * ControllerServer ) LoadExDataFromMetadataStore ( ) error {
2018-12-19 14:26:16 +00:00
vol := & rbdVolume { }
cs . MetadataStore . ForAll ( "csi-rbd-vol-" , vol , func ( identifier string ) error {
rbdVolumes [ identifier ] = vol
return nil
} )
snap := & rbdSnapshot { }
cs . MetadataStore . ForAll ( "csi-rbd-(.*)-snap-" , snap , func ( identifier string ) error {
rbdSnapshots [ identifier ] = snap
return nil
} )
glog . Infof ( "Loaded %d volumes and %d snapshots from metadata store" , len ( rbdVolumes ) , len ( rbdSnapshots ) )
return nil
2018-01-09 18:59:50 +00:00
}
2019-01-17 07:51:06 +00:00
func ( cs * ControllerServer ) validateVolumeReq ( req * csi . CreateVolumeRequest ) error {
2018-03-06 22:33:57 +00:00
if err := cs . Driver . ValidateControllerServiceRequest ( csi . ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME ) ; err != nil {
2018-01-09 18:59:50 +00:00
glog . V ( 3 ) . Infof ( "invalid create volume req: %v" , req )
2019-01-17 05:27:55 +00:00
return err
2018-01-09 18:59:50 +00:00
}
2018-03-06 22:33:57 +00:00
// Check sanity of request Name, Volume Capabilities
if len ( req . Name ) == 0 {
2019-01-17 05:27:55 +00:00
return status . Error ( codes . InvalidArgument , "Volume Name cannot be empty" )
2018-03-06 22:33:57 +00:00
}
if req . VolumeCapabilities == nil {
2019-01-17 05:27:55 +00:00
return status . Error ( codes . InvalidArgument , "Volume Capabilities cannot be empty" )
2018-03-06 22:33:57 +00:00
}
2019-01-17 05:27:55 +00:00
return nil
}
2018-01-16 01:52:28 +00:00
2019-01-28 11:47:06 +00:00
// CreateVolume creates the volume in backend and store the volume metadata
2019-01-17 07:51:06 +00:00
func ( cs * ControllerServer ) CreateVolume ( ctx context . Context , req * csi . CreateVolumeRequest ) ( * csi . CreateVolumeResponse , error ) {
2018-01-16 01:52:28 +00:00
2019-01-17 05:27:55 +00:00
if err := cs . validateVolumeReq ( req ) ; err != nil {
return nil , err
}
2018-10-17 12:52:45 +00:00
volumeNameMutex . LockKey ( req . GetName ( ) )
defer volumeNameMutex . UnlockKey ( req . GetName ( ) )
2018-01-16 01:52:28 +00:00
2018-03-06 22:33:57 +00:00
// Need to check for already existing volume name, and if found
// check for the requested capacity and already allocated capacity
if exVol , err := getRBDVolumeByName ( req . GetName ( ) ) ; err == nil {
// Since err is nil, it means the volume with the same name already exists
// need to check if the size of exisiting volume is the same as in new
// request
2019-01-17 06:24:10 +00:00
if exVol . VolSize >= req . GetCapacityRange ( ) . GetRequiredBytes ( ) {
2018-03-06 22:33:57 +00:00
// exisiting volume is compatible with new request and should be reused.
// TODO (sbezverk) Do I need to make sure that RBD volume still exists?
return & csi . CreateVolumeResponse {
Volume : & csi . Volume {
2018-11-24 19:18:24 +00:00
VolumeId : exVol . VolID ,
2019-01-17 06:24:10 +00:00
CapacityBytes : exVol . VolSize ,
2018-11-24 19:18:24 +00:00
VolumeContext : req . GetParameters ( ) ,
2018-03-06 22:33:57 +00:00
} ,
} , nil
}
2019-01-16 13:13:38 +00:00
return nil , status . Errorf ( codes . AlreadyExists , "Volume with the same name: %s but with different size already exist" , req . GetName ( ) )
2018-03-06 22:33:57 +00:00
}
// TODO (sbezverk) Last check for not exceeding total storage capacity
rbdVol , err := getRBDVolumeOptions ( req . GetParameters ( ) )
2018-01-16 01:52:28 +00:00
if err != nil {
return nil , err
}
2018-08-08 05:42:17 +00:00
// Generating Volume Name and Volume ID, as according to CSI spec they MUST be different
2018-01-09 18:59:50 +00:00
volName := req . GetName ( )
2018-01-16 01:52:28 +00:00
uniqueID := uuid . NewUUID ( ) . String ( )
2018-01-09 18:59:50 +00:00
if len ( volName ) == 0 {
2018-03-06 22:33:57 +00:00
volName = rbdVol . Pool + "-dynamic-pvc-" + uniqueID
2018-01-09 18:59:50 +00:00
}
2018-03-06 22:33:57 +00:00
rbdVol . VolName = volName
2018-12-19 14:26:16 +00:00
volumeID := "csi-rbd-vol-" + uniqueID
2018-03-06 22:33:57 +00:00
rbdVol . VolID = volumeID
2018-01-09 18:59:50 +00:00
// Volume Size - Default is 1 GiB
volSizeBytes := int64 ( oneGB )
if req . GetCapacityRange ( ) != nil {
2019-01-17 06:24:10 +00:00
volSizeBytes = req . GetCapacityRange ( ) . GetRequiredBytes ( )
2018-01-09 18:59:50 +00:00
}
2018-03-06 22:33:57 +00:00
rbdVol . VolSize = volSizeBytes
2018-01-09 18:59:50 +00:00
volSizeGB := int ( volSizeBytes / 1024 / 1024 / 1024 )
// Check if there is already RBD image with requested name
2019-01-17 05:46:32 +00:00
found , _ , _ := rbdStatus ( rbdVol , rbdVol . UserID , req . GetSecrets ( ) )
2018-01-09 18:59:50 +00:00
if ! found {
2018-08-08 05:42:17 +00:00
// if VolumeContentSource is not nil, this request is for snapshot
if req . VolumeContentSource != nil {
2019-01-17 05:27:55 +00:00
if err = cs . checkSnapshot ( req , rbdVol ) ; err != nil {
2018-08-08 05:42:17 +00:00
return nil , err
}
2019-01-17 05:27:55 +00:00
} else {
2019-01-17 06:49:35 +00:00
err = createRBDImage ( rbdVol , volSizeGB , rbdVol . AdminID , req . GetSecrets ( ) )
2018-01-09 18:59:50 +00:00
if err != nil {
2019-01-17 05:27:55 +00:00
glog . Warningf ( "failed to create volume: %v" , err )
2018-01-09 18:59:50 +00:00
return nil , err
}
2019-01-17 05:27:55 +00:00
2018-08-08 05:42:17 +00:00
glog . V ( 4 ) . Infof ( "create volume %s" , volName )
2018-01-09 18:59:50 +00:00
}
}
2019-01-25 19:09:46 +00:00
if createErr := cs . MetadataStore . Create ( volumeID , rbdVol ) ; createErr != nil {
2018-12-19 14:26:16 +00:00
glog . Warningf ( "failed to store volume metadata with error: %v" , err )
2019-01-17 06:49:35 +00:00
if err = deleteRBDImage ( rbdVol , rbdVol . AdminID , req . GetSecrets ( ) ) ; err != nil {
2018-12-19 14:26:16 +00:00
glog . V ( 3 ) . Infof ( "failed to delete rbd image: %s/%s with error: %v" , rbdVol . Pool , rbdVol . VolName , err )
return nil , err
}
2019-01-25 19:09:46 +00:00
return nil , createErr
2018-01-09 18:59:50 +00:00
}
2018-12-19 14:26:16 +00:00
2018-08-09 13:06:51 +00:00
rbdVolumes [ volumeID ] = rbdVol
2018-01-09 18:59:50 +00:00
return & csi . CreateVolumeResponse {
2018-02-15 13:51:23 +00:00
Volume : & csi . Volume {
2018-11-24 19:18:24 +00:00
VolumeId : volumeID ,
2019-01-17 06:24:10 +00:00
CapacityBytes : volSizeBytes ,
2018-11-24 19:18:24 +00:00
VolumeContext : req . GetParameters ( ) ,
2018-01-09 18:59:50 +00:00
} ,
} , nil
}
2019-01-17 07:51:06 +00:00
func ( cs * ControllerServer ) checkSnapshot ( req * csi . CreateVolumeRequest , rbdVol * rbdVolume ) error {
2019-01-17 05:27:55 +00:00
snapshot := req . VolumeContentSource . GetSnapshot ( )
if snapshot == nil {
return status . Error ( codes . InvalidArgument , "Volume Snapshot cannot be empty" )
}
snapshotID := snapshot . GetSnapshotId ( )
if len ( snapshotID ) == 0 {
return status . Error ( codes . InvalidArgument , "Volume Snapshot ID cannot be empty" )
}
rbdSnap := & rbdSnapshot { }
if err := cs . MetadataStore . Get ( snapshotID , rbdSnap ) ; err != nil {
return err
}
2019-01-17 05:46:32 +00:00
err := restoreSnapshot ( rbdVol , rbdSnap , rbdVol . AdminID , req . GetSecrets ( ) )
2019-01-17 05:27:55 +00:00
if err != nil {
return err
}
glog . V ( 4 ) . Infof ( "create volume %s from snapshot %s" , req . GetName ( ) , rbdSnap . SnapName )
return nil
}
2019-01-17 06:49:35 +00:00
2019-01-28 11:47:06 +00:00
// DeleteVolume deletes the volume in backend and removes the volume metadata
// from store
2019-01-17 07:51:06 +00:00
func ( cs * ControllerServer ) DeleteVolume ( ctx context . Context , req * csi . DeleteVolumeRequest ) ( * csi . DeleteVolumeResponse , error ) {
2018-03-06 22:33:57 +00:00
if err := cs . Driver . ValidateControllerServiceRequest ( csi . ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME ) ; err != nil {
2018-01-09 18:59:50 +00:00
glog . Warningf ( "invalid delete volume req: %v" , req )
return nil , err
}
// For now the image get unconditionally deleted, but here retention policy can be checked
2018-01-16 01:52:28 +00:00
volumeID := req . GetVolumeId ( )
2018-10-17 12:52:45 +00:00
volumeIDMutex . LockKey ( volumeID )
defer volumeIDMutex . UnlockKey ( volumeID )
2018-03-06 22:33:57 +00:00
rbdVol := & rbdVolume { }
2018-12-19 14:26:16 +00:00
if err := cs . MetadataStore . Get ( volumeID , rbdVol ) ; err != nil {
2018-10-09 10:08:56 +00:00
if os . IsNotExist ( errors . Cause ( err ) ) {
return & csi . DeleteVolumeResponse { } , nil
}
2018-01-09 18:59:50 +00:00
return nil , err
}
2018-12-19 14:26:16 +00:00
2018-03-06 22:33:57 +00:00
volName := rbdVol . VolName
2018-01-09 18:59:50 +00:00
// Deleting rbd image
glog . V ( 4 ) . Infof ( "deleting volume %s" , volName )
2019-01-17 05:46:32 +00:00
if err := deleteRBDImage ( rbdVol , rbdVol . AdminID , req . GetSecrets ( ) ) ; err != nil {
2018-10-09 10:08:56 +00:00
// TODO: can we detect "already deleted" situations here and proceed?
2018-03-06 22:33:57 +00:00
glog . V ( 3 ) . Infof ( "failed to delete rbd image: %s/%s with error: %v" , rbdVol . Pool , volName , err )
2018-01-09 18:59:50 +00:00
return nil , err
}
2018-12-19 14:26:16 +00:00
if err := cs . MetadataStore . Delete ( volumeID ) ; err != nil {
2018-01-09 18:59:50 +00:00
return nil , err
}
2018-03-06 22:33:57 +00:00
delete ( rbdVolumes , volumeID )
2018-01-18 19:13:08 +00:00
return & csi . DeleteVolumeResponse { } , nil
}
2019-01-28 11:47:06 +00:00
// ValidateVolumeCapabilities checks whether the volume capabilities requested
// are supported.
2019-01-17 07:51:06 +00:00
func ( cs * ControllerServer ) ValidateVolumeCapabilities ( ctx context . Context , req * csi . ValidateVolumeCapabilitiesRequest ) ( * csi . ValidateVolumeCapabilitiesResponse , error ) {
2018-01-18 19:13:08 +00:00
for _ , cap := range req . VolumeCapabilities {
if cap . GetAccessMode ( ) . GetMode ( ) != csi . VolumeCapability_AccessMode_SINGLE_NODE_WRITER {
2018-11-24 19:18:24 +00:00
return & csi . ValidateVolumeCapabilitiesResponse { Message : "" } , nil
2018-01-18 19:13:08 +00:00
}
}
2018-11-24 19:18:24 +00:00
return & csi . ValidateVolumeCapabilitiesResponse {
Confirmed : & csi . ValidateVolumeCapabilitiesResponse_Confirmed {
VolumeCapabilities : req . VolumeCapabilities ,
} ,
} , nil
2018-01-18 19:13:08 +00:00
}
2019-01-28 11:47:06 +00:00
// ControllerUnpublishVolume returns success response
2019-01-17 07:51:06 +00:00
func ( cs * ControllerServer ) ControllerUnpublishVolume ( ctx context . Context , req * csi . ControllerUnpublishVolumeRequest ) ( * csi . ControllerUnpublishVolumeResponse , error ) {
2018-01-09 18:59:50 +00:00
return & csi . ControllerUnpublishVolumeResponse { } , nil
}
2019-01-28 11:47:06 +00:00
// ControllerPublishVolume returns success response
2019-01-17 07:51:06 +00:00
func ( cs * ControllerServer ) ControllerPublishVolume ( ctx context . Context , req * csi . ControllerPublishVolumeRequest ) ( * csi . ControllerPublishVolumeResponse , error ) {
2018-01-09 18:59:50 +00:00
return & csi . ControllerPublishVolumeResponse { } , nil
}
2018-08-08 05:42:17 +00:00
2019-01-28 11:47:06 +00:00
// CreateSnapshot creates the snapshot in backend and stores metadata
// in store
2019-01-17 07:51:06 +00:00
func ( cs * ControllerServer ) CreateSnapshot ( ctx context . Context , req * csi . CreateSnapshotRequest ) ( * csi . CreateSnapshotResponse , error ) {
2018-08-08 05:42:17 +00:00
if err := cs . Driver . ValidateControllerServiceRequest ( csi . ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT ) ; err != nil {
glog . Warningf ( "invalid create snapshot req: %v" , req )
return nil , err
}
// Check sanity of request Snapshot Name, Source Volume Id
if len ( req . Name ) == 0 {
return nil , status . Error ( codes . InvalidArgument , "Snapshot Name cannot be empty" )
}
if len ( req . SourceVolumeId ) == 0 {
return nil , status . Error ( codes . InvalidArgument , "Source Volume ID cannot be empty" )
}
2018-10-17 12:52:45 +00:00
snapshotNameMutex . LockKey ( req . GetName ( ) )
defer snapshotNameMutex . UnlockKey ( req . GetName ( ) )
2018-08-08 05:42:17 +00:00
// Need to check for already existing snapshot name, and if found
// check for the requested source volume id and already allocated source volume id
if exSnap , err := getRBDSnapshotByName ( req . GetName ( ) ) ; err == nil {
if req . SourceVolumeId == exSnap . SourceVolumeID {
return & csi . CreateSnapshotResponse {
Snapshot : & csi . Snapshot {
SizeBytes : exSnap . SizeBytes ,
2018-11-24 19:18:24 +00:00
SnapshotId : exSnap . SnapID ,
2018-08-08 05:42:17 +00:00
SourceVolumeId : exSnap . SourceVolumeID ,
2018-12-05 02:44:04 +00:00
CreationTime : & timestamp . Timestamp {
2018-11-24 19:18:24 +00:00
Seconds : exSnap . CreatedAt ,
2018-08-08 05:42:17 +00:00
} ,
2018-12-05 02:44:04 +00:00
ReadyToUse : true ,
2018-08-08 05:42:17 +00:00
} ,
} , nil
}
2019-01-16 13:13:38 +00:00
return nil , status . Errorf ( codes . AlreadyExists , "Snapshot with the same name: %s but with different source volume id already exist" , req . GetName ( ) )
2018-08-08 05:42:17 +00:00
}
rbdSnap , err := getRBDSnapshotOptions ( req . GetParameters ( ) )
if err != nil {
return nil , err
}
// Generating Snapshot Name and Snapshot ID, as according to CSI spec they MUST be different
snapName := req . GetName ( )
uniqueID := uuid . NewUUID ( ) . String ( )
rbdVolume , err := getRBDVolumeByID ( req . GetSourceVolumeId ( ) )
if err != nil {
2019-01-16 13:13:38 +00:00
return nil , status . Errorf ( codes . NotFound , "Source Volume ID %s cannot found" , req . GetSourceVolumeId ( ) )
2018-08-08 05:42:17 +00:00
}
2018-08-09 13:07:13 +00:00
if ! hasSnapshotFeature ( rbdVolume . ImageFeatures ) {
return nil , fmt . Errorf ( "Volume(%s) has not snapshot feature(layering)" , req . GetSourceVolumeId ( ) )
}
2018-08-08 05:42:17 +00:00
rbdSnap . VolName = rbdVolume . VolName
rbdSnap . SnapName = snapName
2018-08-09 13:06:51 +00:00
snapshotID := "csi-rbd-" + rbdVolume . VolName + "-snap-" + uniqueID
2018-08-08 05:42:17 +00:00
rbdSnap . SnapID = snapshotID
rbdSnap . SourceVolumeID = req . GetSourceVolumeId ( )
rbdSnap . SizeBytes = rbdVolume . VolSize
2019-01-17 05:46:32 +00:00
err = createSnapshot ( rbdSnap , rbdSnap . AdminID , req . GetSecrets ( ) )
2018-08-08 05:42:17 +00:00
// if we already have the snapshot, return the snapshot
if err != nil {
if exitErr , ok := err . ( * exec . ExitError ) ; ok {
if status , ok := exitErr . Sys ( ) . ( syscall . WaitStatus ) ; ok {
if status . ExitStatus ( ) == int ( syscall . EEXIST ) {
glog . Warningf ( "Snapshot with the same name: %s, we return this." , req . GetName ( ) )
} else {
glog . Warningf ( "failed to create snapshot: %v" , err )
return nil , err
}
} else {
glog . Warningf ( "failed to create snapshot: %v" , err )
return nil , err
}
} else {
glog . Warningf ( "failed to create snapshot: %v" , err )
return nil , err
}
} else {
glog . V ( 4 ) . Infof ( "create snapshot %s" , snapName )
2019-01-17 05:46:32 +00:00
err = protectSnapshot ( rbdSnap , rbdSnap . AdminID , req . GetSecrets ( ) )
2018-08-08 05:42:17 +00:00
if err != nil {
2019-01-17 05:46:32 +00:00
err = deleteSnapshot ( rbdSnap , rbdSnap . AdminID , req . GetSecrets ( ) )
2018-08-08 05:42:17 +00:00
if err != nil {
return nil , fmt . Errorf ( "snapshot is created but failed to protect and delete snapshot: %v" , err )
}
return nil , fmt . Errorf ( "Snapshot is created but failed to protect snapshot" )
}
}
2019-01-25 13:39:35 +00:00
rbdSnap . CreatedAt = ptypes . TimestampNow ( ) . GetSeconds ( )
2018-08-08 05:42:17 +00:00
2019-01-25 19:09:46 +00:00
if createErr := cs . MetadataStore . Create ( snapshotID , rbdSnap ) ; createErr != nil {
2018-08-09 13:07:06 +00:00
glog . Warningf ( "rbd: failed to store snapInfo with error: %v" , err )
// Unprotect snapshot
2019-01-17 06:49:35 +00:00
err = unprotectSnapshot ( rbdSnap , rbdSnap . AdminID , req . GetSecrets ( ) )
2018-08-09 13:07:06 +00:00
if err != nil {
2019-01-16 13:13:38 +00:00
return nil , status . Errorf ( codes . Unknown , "This Snapshot should be removed but failed to unprotect snapshot: %s/%s with error: %v" , rbdSnap . Pool , rbdSnap . SnapName , err )
2018-08-09 13:07:06 +00:00
}
// Deleting snapshot
glog . V ( 4 ) . Infof ( "deleting Snaphot %s" , rbdSnap . SnapName )
2019-01-17 06:49:35 +00:00
if err = deleteSnapshot ( rbdSnap , rbdSnap . AdminID , req . GetSecrets ( ) ) ; err != nil {
2019-01-16 13:13:38 +00:00
return nil , status . Errorf ( codes . Unknown , "This Snapshot should be removed but failed to delete snapshot: %s/%s with error: %v" , rbdSnap . Pool , rbdSnap . SnapName , err )
2018-08-09 13:07:06 +00:00
}
2019-01-25 19:09:46 +00:00
return nil , createErr
2018-08-08 05:42:17 +00:00
}
2018-12-19 14:26:16 +00:00
2018-08-09 13:06:51 +00:00
rbdSnapshots [ snapshotID ] = rbdSnap
2018-08-08 05:42:17 +00:00
return & csi . CreateSnapshotResponse {
Snapshot : & csi . Snapshot {
SizeBytes : rbdSnap . SizeBytes ,
2018-11-24 19:18:24 +00:00
SnapshotId : snapshotID ,
2018-08-08 05:42:17 +00:00
SourceVolumeId : req . GetSourceVolumeId ( ) ,
2018-12-05 02:44:04 +00:00
CreationTime : & timestamp . Timestamp {
2018-11-24 19:18:24 +00:00
Seconds : rbdSnap . CreatedAt ,
2018-08-08 05:42:17 +00:00
} ,
2018-12-05 02:44:04 +00:00
ReadyToUse : true ,
2018-08-08 05:42:17 +00:00
} ,
} , nil
}
2019-01-28 11:47:06 +00:00
// DeleteSnapshot deletes the snapshot in backend and removes the
//snapshot metadata from store
2019-01-17 07:51:06 +00:00
func ( cs * ControllerServer ) DeleteSnapshot ( ctx context . Context , req * csi . DeleteSnapshotRequest ) ( * csi . DeleteSnapshotResponse , error ) {
2018-08-08 05:42:17 +00:00
if err := cs . Driver . ValidateControllerServiceRequest ( csi . ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT ) ; err != nil {
glog . Warningf ( "invalid delete snapshot req: %v" , req )
return nil , err
}
snapshotID := req . GetSnapshotId ( )
2018-08-09 13:06:51 +00:00
if len ( snapshotID ) == 0 {
2018-08-08 05:42:17 +00:00
return nil , status . Error ( codes . InvalidArgument , "Snapshot ID cannot be empty" )
}
2018-10-17 12:52:45 +00:00
snapshotIDMutex . LockKey ( snapshotID )
defer snapshotIDMutex . UnlockKey ( snapshotID )
2018-08-08 05:42:17 +00:00
rbdSnap := & rbdSnapshot { }
2018-12-19 14:26:16 +00:00
if err := cs . MetadataStore . Get ( snapshotID , rbdSnap ) ; err != nil {
2018-08-08 05:42:17 +00:00
return nil , err
}
// Unprotect snapshot
2019-01-17 05:46:32 +00:00
err := unprotectSnapshot ( rbdSnap , rbdSnap . AdminID , req . GetSecrets ( ) )
2018-08-08 05:42:17 +00:00
if err != nil {
2019-01-16 13:13:38 +00:00
return nil , status . Errorf ( codes . FailedPrecondition , "failed to unprotect snapshot: %s/%s with error: %v" , rbdSnap . Pool , rbdSnap . SnapName , err )
2018-08-08 05:42:17 +00:00
}
// Deleting snapshot
glog . V ( 4 ) . Infof ( "deleting Snaphot %s" , rbdSnap . SnapName )
2019-01-17 05:46:32 +00:00
if err := deleteSnapshot ( rbdSnap , rbdSnap . AdminID , req . GetSecrets ( ) ) ; err != nil {
2019-01-16 13:13:38 +00:00
return nil , status . Errorf ( codes . FailedPrecondition , "failed to delete snapshot: %s/%s with error: %v" , rbdSnap . Pool , rbdSnap . SnapName , err )
2018-08-08 05:42:17 +00:00
}
2018-12-19 14:26:16 +00:00
if err := cs . MetadataStore . Delete ( snapshotID ) ; err != nil {
2018-08-08 05:42:17 +00:00
return nil , err
}
delete ( rbdSnapshots , snapshotID )
return & csi . DeleteSnapshotResponse { } , nil
}
2019-01-28 11:47:06 +00:00
// ListSnapshots lists the snapshots in the store
2019-01-17 07:51:06 +00:00
func ( cs * ControllerServer ) ListSnapshots ( ctx context . Context , req * csi . ListSnapshotsRequest ) ( * csi . ListSnapshotsResponse , error ) {
2018-08-08 05:42:17 +00:00
if err := cs . Driver . ValidateControllerServiceRequest ( csi . ControllerServiceCapability_RPC_LIST_SNAPSHOTS ) ; err != nil {
glog . Warningf ( "invalid list snapshot req: %v" , req )
return nil , err
}
2019-01-17 07:34:59 +00:00
sourceVolumeID := req . GetSourceVolumeId ( )
2018-08-08 05:42:17 +00:00
// TODO (sngchlko) list with token
2018-10-17 12:52:45 +00:00
// TODO (#94) protect concurrent access to global data structures
2018-08-08 05:42:17 +00:00
// list only a specific snapshot which has snapshot ID
2018-08-09 13:06:51 +00:00
if snapshotID := req . GetSnapshotId ( ) ; len ( snapshotID ) != 0 {
2018-08-08 05:42:17 +00:00
if rbdSnap , ok := rbdSnapshots [ snapshotID ] ; ok {
// if source volume ID also set, check source volume id on the cache.
2019-01-17 07:34:59 +00:00
if len ( sourceVolumeID ) != 0 && rbdSnap . SourceVolumeID != sourceVolumeID {
return nil , status . Errorf ( codes . Unknown , "Requested Source Volume ID %s is different from %s" , sourceVolumeID , rbdSnap . SourceVolumeID )
2018-08-08 05:42:17 +00:00
}
return & csi . ListSnapshotsResponse {
Entries : [ ] * csi . ListSnapshotsResponse_Entry {
{
Snapshot : & csi . Snapshot {
SizeBytes : rbdSnap . SizeBytes ,
2018-11-24 19:18:24 +00:00
SnapshotId : rbdSnap . SnapID ,
2018-08-08 05:42:17 +00:00
SourceVolumeId : rbdSnap . SourceVolumeID ,
2018-12-05 02:44:04 +00:00
CreationTime : & timestamp . Timestamp {
2018-11-24 19:18:24 +00:00
Seconds : rbdSnap . CreatedAt ,
2018-08-08 05:42:17 +00:00
} ,
2018-12-05 02:44:04 +00:00
ReadyToUse : true ,
2018-08-08 05:42:17 +00:00
} ,
} ,
} ,
} , nil
}
2019-01-16 13:13:38 +00:00
return nil , status . Errorf ( codes . NotFound , "Snapshot ID %s cannot found" , snapshotID )
2019-01-16 13:03:38 +00:00
2018-08-08 05:42:17 +00:00
}
entries := [ ] * csi . ListSnapshotsResponse_Entry { }
for _ , rbdSnap := range rbdSnapshots {
// if source volume ID also set, check source volume id on the cache.
2019-01-17 07:34:59 +00:00
if len ( sourceVolumeID ) != 0 && rbdSnap . SourceVolumeID != sourceVolumeID {
2018-08-08 05:42:17 +00:00
continue
}
entries = append ( entries , & csi . ListSnapshotsResponse_Entry {
Snapshot : & csi . Snapshot {
SizeBytes : rbdSnap . SizeBytes ,
2018-11-24 19:18:24 +00:00
SnapshotId : rbdSnap . SnapID ,
2018-08-08 05:42:17 +00:00
SourceVolumeId : rbdSnap . SourceVolumeID ,
2018-12-05 02:44:04 +00:00
CreationTime : & timestamp . Timestamp {
2018-11-24 19:18:24 +00:00
Seconds : rbdSnap . CreatedAt ,
2018-08-08 05:42:17 +00:00
} ,
2018-12-05 02:44:04 +00:00
ReadyToUse : true ,
2018-08-08 05:42:17 +00:00
} ,
} )
}
return & csi . ListSnapshotsResponse {
Entries : entries ,
} , nil
}