2018-03-05 11:59:47 +00:00
/ *
2019-04-03 08:46:15 +00:00
Copyright 2018 The Ceph - CSI Authors .
2018-03-05 11:59:47 +00:00
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 cephfs
import (
2019-08-24 09:14:15 +00:00
"context"
2020-06-25 06:41:35 +00:00
"errors"
2020-08-04 04:25:28 +00:00
"fmt"
2019-08-24 09:14:15 +00:00
2020-04-17 09:23:49 +00:00
csicommon "github.com/ceph/ceph-csi/internal/csi-common"
"github.com/ceph/ceph-csi/internal/util"
2020-04-15 03:38:16 +00:00
2019-02-18 11:30:28 +00:00
"github.com/container-storage-interface/spec/lib/go/csi"
2020-08-04 04:25:28 +00:00
"github.com/golang/protobuf/ptypes/timestamp"
"github.com/kubernetes-csi/csi-lib-utils/protosanitizer"
2018-03-05 11:59:47 +00:00
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
2019-01-28 11:47:06 +00:00
// ControllerServer struct of CEPH CSI driver with supported methods of CSI
// controller server spec.
2019-01-17 07:51:06 +00:00
type ControllerServer struct {
2018-03-05 11:59:47 +00:00
* csicommon . DefaultControllerServer
2019-09-12 04:53:37 +00:00
// A map storing all volumes with ongoing operations so that additional operations
// for that same volume (as defined by VolumeID/volume name) return an Aborted error
VolumeLocks * util . VolumeLocks
2020-08-03 18:32:34 +00:00
// A map storing all snapshots with ongoing operations so that additional operations
// for that same snapshot (as defined by SnapshotID/snapshot name) return an Aborted error
SnapshotLocks * util . VolumeLocks
// A map storing all volumes/snapshots with ongoing operations.
OperationLocks * util . OperationLock
2018-12-19 14:26:16 +00:00
}
2020-07-19 12:21:03 +00:00
// createBackingVolume creates the backing subvolume and on any error cleans up any created entities.
2020-08-03 18:34:28 +00:00
func ( cs * ControllerServer ) createBackingVolume (
ctx context . Context ,
volOptions ,
parentVolOpt * volumeOptions ,
vID ,
pvID * volumeIdentifier ,
sID * snapshotIdentifier ,
cr * util . Credentials ) error {
var err error
if sID != nil {
if err = cs . OperationLocks . GetRestoreLock ( sID . SnapshotID ) ; err != nil {
2020-08-11 11:18:30 +00:00
util . ErrorLog ( ctx , err . Error ( ) )
2020-08-03 18:34:28 +00:00
return status . Error ( codes . Aborted , err . Error ( ) )
}
defer cs . OperationLocks . ReleaseRestoreLock ( sID . SnapshotID )
err = createCloneFromSnapshot ( ctx , parentVolOpt , volOptions , vID , sID , cr )
if err != nil {
2020-08-11 11:18:30 +00:00
util . ErrorLog ( ctx , "failed to create clone from snapshot %s: %v" , sID . FsSnapshotName , err )
2020-08-03 18:34:28 +00:00
return err
}
return err
}
if parentVolOpt != nil {
if err = cs . OperationLocks . GetCloneLock ( pvID . VolumeID ) ; err != nil {
2020-08-11 11:18:30 +00:00
util . ErrorLog ( ctx , err . Error ( ) )
2020-08-03 18:34:28 +00:00
return status . Error ( codes . Aborted , err . Error ( ) )
}
defer cs . OperationLocks . ReleaseCloneLock ( pvID . VolumeID )
err = createCloneFromSubvolume ( ctx , volumeID ( pvID . FsSubvolName ) , volumeID ( vID . FsSubvolName ) , volOptions , parentVolOpt , cr )
if err != nil {
2020-08-11 11:18:30 +00:00
util . ErrorLog ( ctx , "failed to create clone from subvolume %s: %v" , volumeID ( pvID . FsSubvolName ) , err )
2020-08-03 18:34:28 +00:00
return err
}
return nil
2019-05-28 19:03:18 +00:00
}
2019-08-22 17:19:06 +00:00
if err = createVolume ( ctx , volOptions , cr , volumeID ( vID . FsSubvolName ) , volOptions . Size ) ; err != nil {
2020-08-11 11:18:30 +00:00
util . ErrorLog ( ctx , "failed to create volume %s: %v" , volOptions . RequestName , err )
2019-05-28 19:03:18 +00:00
return status . Error ( codes . Internal , err . Error ( ) )
}
return nil
}
2020-08-03 18:37:44 +00:00
func checkContentSource ( ctx context . Context , req * csi . CreateVolumeRequest , cr * util . Credentials ) ( * volumeOptions , * volumeIdentifier , * snapshotIdentifier , error ) {
if req . VolumeContentSource == nil {
return nil , nil , nil , nil
}
volumeSource := req . VolumeContentSource
switch volumeSource . Type . ( type ) {
case * csi . VolumeContentSource_Snapshot :
snapshotID := req . VolumeContentSource . GetSnapshot ( ) . GetSnapshotId ( )
volOpt , _ , sid , err := newSnapshotOptionsFromID ( ctx , snapshotID , cr )
if err != nil {
if errors . Is ( err , ErrSnapNotFound ) {
return nil , nil , nil , status . Error ( codes . NotFound , err . Error ( ) )
}
return nil , nil , nil , status . Error ( codes . Internal , err . Error ( ) )
}
return volOpt , nil , sid , nil
case * csi . VolumeContentSource_Volume :
// Find the volume using the provided VolumeID
volID := req . VolumeContentSource . GetVolume ( ) . GetVolumeId ( )
parentVol , pvID , err := newVolumeOptionsFromVolID ( ctx , volID , nil , req . Secrets )
if err != nil {
if ! errors . Is ( err , ErrVolumeNotFound ) {
return nil , nil , nil , status . Error ( codes . NotFound , err . Error ( ) )
}
return nil , nil , nil , status . Error ( codes . Internal , err . Error ( ) )
}
return parentVol , pvID , nil , nil
}
return nil , nil , nil , status . Errorf ( codes . InvalidArgument , "not a proper volume source %v" , volumeSource )
}
2020-07-19 12:21:03 +00:00
// CreateVolume creates a reservation and the volume in backend, if it is not already present.
2020-08-04 03:56:43 +00:00
// nolint:gocognit:gocyclo // TODO: reduce complexity
2019-01-17 07:51:06 +00:00
func ( cs * ControllerServer ) CreateVolume ( ctx context . Context , req * csi . CreateVolumeRequest ) ( * csi . CreateVolumeResponse , error ) {
2018-03-20 15:15:19 +00:00
if err := cs . validateCreateVolumeRequest ( req ) ; err != nil {
2020-08-11 11:20:31 +00:00
util . ErrorLog ( ctx , "CreateVolumeRequest validation failed: %v" , err )
2018-03-05 11:59:47 +00:00
return nil , err
}
2019-02-13 12:57:16 +00:00
2018-04-13 12:54:40 +00:00
// Configuration
2019-01-21 14:21:03 +00:00
secret := req . GetSecrets ( )
2019-05-28 19:03:18 +00:00
requestName := req . GetName ( )
2019-09-12 04:53:37 +00:00
2020-08-04 03:56:43 +00:00
cr , err := util . NewAdminCredentials ( secret )
if err != nil {
2020-08-11 11:20:31 +00:00
util . ErrorLog ( ctx , "failed to retrieve admin credentials: %v" , err )
2020-08-04 03:56:43 +00:00
return nil , status . Error ( codes . InvalidArgument , err . Error ( ) )
}
defer cr . DeleteCredentials ( )
2019-09-12 04:53:37 +00:00
// Existence and conflict checks
if acquired := cs . VolumeLocks . TryAcquire ( requestName ) ; ! acquired {
2020-08-11 11:36:42 +00:00
util . ErrorLog ( ctx , util . VolumeOperationAlreadyExistsFmt , requestName )
2019-09-12 04:53:37 +00:00
return nil , status . Errorf ( codes . Aborted , util . VolumeOperationAlreadyExistsFmt , requestName )
}
defer cs . VolumeLocks . Release ( requestName )
2020-01-24 16:26:56 +00:00
volOptions , err := newVolumeOptions ( ctx , requestName , req , secret )
2018-03-05 11:59:47 +00:00
if err != nil {
2020-08-11 11:20:31 +00:00
util . ErrorLog ( ctx , "validation and extraction of volume options failed: %v" , err )
2018-03-20 15:15:19 +00:00
return nil , status . Error ( codes . InvalidArgument , err . Error ( ) )
2018-03-05 11:59:47 +00:00
}
2019-09-25 08:35:33 +00:00
if req . GetCapacityRange ( ) != nil {
volOptions . Size = util . RoundOffBytes ( req . GetCapacityRange ( ) . GetRequiredBytes ( ) )
}
// TODO need to add check for 0 volume size
2020-08-04 03:56:43 +00:00
parentVol , pvID , sID , err := checkContentSource ( ctx , req , cr )
2019-05-28 19:03:18 +00:00
if err != nil {
2020-08-04 03:56:43 +00:00
return nil , err
}
vID , err := checkVolExists ( ctx , volOptions , parentVol , pvID , sID , cr )
if err != nil {
if errors . Is ( err , ErrCloneInProgress ) {
return nil , status . Error ( codes . Aborted , err . Error ( ) )
}
2019-05-28 19:03:18 +00:00
return nil , status . Error ( codes . Internal , err . Error ( ) )
}
2019-09-25 08:35:33 +00:00
// TODO return error message if requested vol size greater than found volume return error
2019-05-28 19:03:18 +00:00
if vID != nil {
2020-08-04 03:56:43 +00:00
if sID != nil || pvID != nil {
// while cloning the volume the size is not populated properly to the new volume now.
// it will be fixed in cephfs soon with the parentvolume size. Till then by below
// resize we are making sure we return or satisfy the requested size by setting the size
// explictly
err = resizeVolume ( ctx , volOptions , cr , volumeID ( vID . FsSubvolName ) , volOptions . Size )
if err != nil {
2020-10-19 08:15:57 +00:00
purgeErr := purgeVolume ( ctx , volumeID ( vID . FsSubvolName ) , cr , volOptions , false )
if purgeErr != nil {
2020-08-11 11:20:31 +00:00
util . ErrorLog ( ctx , "failed to delete volume %s: %v" , requestName , purgeErr )
2020-08-04 03:56:43 +00:00
// All errors other than ErrVolumeNotFound should return an error back to the caller
if ! errors . Is ( purgeErr , ErrVolumeNotFound ) {
return nil , status . Error ( codes . Internal , purgeErr . Error ( ) )
2020-10-19 08:15:57 +00:00
2020-08-04 03:56:43 +00:00
}
}
errUndo := undoVolReservation ( ctx , volOptions , * vID , secret )
if errUndo != nil {
2020-08-11 11:36:42 +00:00
util . WarningLog ( ctx , "failed undoing reservation of volume: %s (%s)" ,
2020-08-04 03:56:43 +00:00
requestName , errUndo )
}
2020-08-11 11:20:31 +00:00
util . ErrorLog ( ctx , "failed to expand volume %s: %v" , volumeID ( vID . FsSubvolName ) , err )
2020-08-04 03:56:43 +00:00
return nil , status . Error ( codes . Internal , err . Error ( ) )
}
}
2020-04-18 08:57:05 +00:00
volumeContext := req . GetParameters ( )
volumeContext [ "subvolumeName" ] = vID . FsSubvolName
2020-01-24 16:26:56 +00:00
volume := & csi . Volume {
VolumeId : vID . VolumeID ,
CapacityBytes : volOptions . Size ,
2020-08-04 03:56:43 +00:00
ContentSource : req . GetVolumeContentSource ( ) ,
2020-04-18 08:57:05 +00:00
VolumeContext : volumeContext ,
2020-01-24 16:26:56 +00:00
}
if volOptions . Topology != nil {
volume . AccessibleTopology =
[ ] * csi . Topology {
{
Segments : volOptions . Topology ,
} ,
}
}
return & csi . CreateVolumeResponse { Volume : volume } , nil
2019-05-28 19:03:18 +00:00
}
2018-04-13 12:54:40 +00:00
2019-05-28 19:03:18 +00:00
// Reservation
2019-08-22 17:19:06 +00:00
vID , err = reserveVol ( ctx , volOptions , secret )
2019-05-28 19:03:18 +00:00
if err != nil {
return nil , status . Error ( codes . Internal , err . Error ( ) )
}
2020-08-04 03:56:43 +00:00
2019-05-28 19:03:18 +00:00
defer func ( ) {
2018-04-13 12:54:40 +00:00
if err != nil {
2020-08-04 03:56:43 +00:00
if ! errors . Is ( err , ErrCloneInProgress ) {
errDefer := undoVolReservation ( ctx , volOptions , * vID , secret )
if errDefer != nil {
2020-08-11 11:36:42 +00:00
util . WarningLog ( ctx , "failed undoing reservation of volume: %s (%s)" ,
2020-08-04 03:56:43 +00:00
requestName , errDefer )
}
2019-05-28 19:03:18 +00:00
}
2018-04-13 12:54:40 +00:00
}
2019-05-28 19:03:18 +00:00
} ( )
2018-04-13 12:54:40 +00:00
2019-05-28 19:03:18 +00:00
// Create a volume
2020-08-04 03:56:43 +00:00
err = cs . createBackingVolume ( ctx , volOptions , parentVol , vID , pvID , sID , cr )
2019-05-28 19:03:18 +00:00
if err != nil {
2020-08-04 03:56:43 +00:00
if errors . Is ( err , ErrCloneInProgress ) {
return nil , status . Error ( codes . Aborted , err . Error ( ) )
}
2019-05-28 19:03:18 +00:00
return nil , err
2018-04-13 12:54:40 +00:00
}
2020-07-09 14:48:24 +00:00
util . DebugLog ( ctx , "cephfs: successfully created backing volume named %s for request name %s" ,
2019-05-28 19:03:18 +00:00
vID . FsSubvolName , requestName )
2020-04-18 08:57:05 +00:00
volumeContext := req . GetParameters ( )
volumeContext [ "subvolumeName" ] = vID . FsSubvolName
2020-01-24 16:26:56 +00:00
volume := & csi . Volume {
VolumeId : vID . VolumeID ,
CapacityBytes : volOptions . Size ,
2020-08-04 03:56:43 +00:00
ContentSource : req . GetVolumeContentSource ( ) ,
2020-04-18 08:57:05 +00:00
VolumeContext : volumeContext ,
2020-01-24 16:26:56 +00:00
}
if volOptions . Topology != nil {
volume . AccessibleTopology =
[ ] * csi . Topology {
{
Segments : volOptions . Topology ,
} ,
}
}
return & csi . CreateVolumeResponse { Volume : volume } , nil
2018-03-05 11:59:47 +00:00
}
2020-07-19 12:21:03 +00:00
// DeleteVolume deletes the volume in backend and its reservation.
2019-05-28 19:03:18 +00:00
func ( cs * ControllerServer ) DeleteVolume ( ctx context . Context , req * csi . DeleteVolumeRequest ) ( * csi . DeleteVolumeResponse , error ) {
if err := cs . validateDeleteVolumeRequest ( ) ; err != nil {
2020-08-11 11:22:48 +00:00
util . ErrorLog ( ctx , "DeleteVolumeRequest validation failed: %v" , err )
2019-05-28 19:03:18 +00:00
return nil , err
}
volID := volumeID ( req . GetVolumeId ( ) )
secrets := req . GetSecrets ( )
2019-09-12 04:53:37 +00:00
// lock out parallel delete operations
if acquired := cs . VolumeLocks . TryAcquire ( string ( volID ) ) ; ! acquired {
2020-08-11 11:22:48 +00:00
util . ErrorLog ( ctx , util . VolumeOperationAlreadyExistsFmt , volID )
2019-09-12 04:53:37 +00:00
return nil , status . Errorf ( codes . Aborted , util . VolumeOperationAlreadyExistsFmt , string ( volID ) )
}
defer cs . VolumeLocks . Release ( string ( volID ) )
2020-08-04 03:53:28 +00:00
// lock out volumeID for clone and expand operation
if err := cs . OperationLocks . GetDeleteLock ( req . GetVolumeId ( ) ) ; err != nil {
2020-08-11 11:22:48 +00:00
util . ErrorLog ( ctx , err . Error ( ) )
2020-08-04 03:53:28 +00:00
return nil , status . Error ( codes . Aborted , err . Error ( ) )
}
defer cs . OperationLocks . ReleaseDeleteLock ( req . GetVolumeId ( ) )
2019-05-28 19:03:18 +00:00
// Find the volume using the provided VolumeID
2019-08-22 17:19:06 +00:00
volOptions , vID , err := newVolumeOptionsFromVolID ( ctx , string ( volID ) , nil , secrets )
2019-05-28 19:03:18 +00:00
if err != nil {
2020-01-31 08:49:11 +00:00
// if error is ErrPoolNotFound, the pool is already deleted we dont
// need to worry about deleting subvolume or omap data, return success
2020-07-08 23:00:23 +00:00
if errors . Is ( err , util . ErrPoolNotFound ) {
2020-08-11 11:38:51 +00:00
util . WarningLog ( ctx , "failed to get backend volume for %s: %v" , string ( volID ) , err )
2020-01-31 08:49:11 +00:00
return & csi . DeleteVolumeResponse { } , nil
}
2019-05-28 19:03:18 +00:00
// if error is ErrKeyNotFound, then a previous attempt at deletion was complete
// or partially complete (subvolume and imageOMap are garbage collected already), hence
// return success as deletion is complete
2020-07-08 23:00:23 +00:00
if errors . Is ( err , util . ErrKeyNotFound ) {
2019-05-28 19:03:18 +00:00
return & csi . DeleteVolumeResponse { } , nil
}
2020-08-11 11:22:48 +00:00
util . ErrorLog ( ctx , "Error returned from newVolumeOptionsFromVolID: %v" , err )
2020-08-04 03:53:28 +00:00
2020-01-23 02:48:46 +00:00
// All errors other than ErrVolumeNotFound should return an error back to the caller
2020-07-10 00:14:39 +00:00
if ! errors . Is ( err , ErrVolumeNotFound ) {
2020-01-23 02:48:46 +00:00
return nil , status . Error ( codes . Internal , err . Error ( ) )
}
// If error is ErrImageNotFound then we failed to find the subvolume, but found the imageOMap
// to lead us to the image, hence the imageOMap needs to be garbage collected, by calling
// unreserve for the same
if acquired := cs . VolumeLocks . TryAcquire ( volOptions . RequestName ) ; ! acquired {
return nil , status . Errorf ( codes . Aborted , util . VolumeOperationAlreadyExistsFmt , volOptions . RequestName )
}
defer cs . VolumeLocks . Release ( volOptions . RequestName )
if err = undoVolReservation ( ctx , volOptions , * vID , secrets ) ; err != nil {
return nil , status . Error ( codes . Internal , err . Error ( ) )
}
return & csi . DeleteVolumeResponse { } , nil
2019-05-28 19:03:18 +00:00
}
2019-09-12 04:53:37 +00:00
// lock out parallel delete and create requests against the same volume name as we
// cleanup the subvolume and associated omaps for the same
if acquired := cs . VolumeLocks . TryAcquire ( volOptions . RequestName ) ; ! acquired {
return nil , status . Errorf ( codes . Aborted , util . VolumeOperationAlreadyExistsFmt , volOptions . RequestName )
}
2020-08-18 04:52:36 +00:00
defer cs . VolumeLocks . Release ( volOptions . RequestName )
2019-09-12 04:53:37 +00:00
2019-05-28 19:03:18 +00:00
// Deleting a volume requires admin credentials
2019-06-25 19:29:17 +00:00
cr , err := util . NewAdminCredentials ( secrets )
2019-05-28 19:03:18 +00:00
if err != nil {
2020-08-11 11:22:48 +00:00
util . ErrorLog ( ctx , "failed to retrieve admin credentials: %v" , err )
2019-05-28 19:03:18 +00:00
return nil , status . Error ( codes . InvalidArgument , err . Error ( ) )
}
2019-06-25 19:29:17 +00:00
defer cr . DeleteCredentials ( )
2019-05-28 19:03:18 +00:00
2020-08-04 03:53:28 +00:00
if err = purgeVolume ( ctx , volumeID ( vID . FsSubvolName ) , cr , volOptions , false ) ; err != nil {
2020-08-11 11:22:48 +00:00
util . ErrorLog ( ctx , "failed to delete volume %s: %v" , volID , err )
2020-09-21 05:38:14 +00:00
if errors . Is ( err , ErrVolumeHasSnapshots ) {
return nil , status . Error ( codes . FailedPrecondition , err . Error ( ) )
}
2020-07-10 00:14:39 +00:00
if ! errors . Is ( err , ErrVolumeNotFound ) {
2020-01-23 02:48:46 +00:00
return nil , status . Error ( codes . Internal , err . Error ( ) )
}
2019-05-28 19:03:18 +00:00
}
2019-08-22 17:19:06 +00:00
if err := undoVolReservation ( ctx , volOptions , * vID , secrets ) ; err != nil {
2019-05-28 19:03:18 +00:00
return nil , status . Error ( codes . Internal , err . Error ( ) )
}
2020-07-09 14:48:24 +00:00
util . DebugLog ( ctx , "cephfs: successfully deleted volume %s" , volID )
2019-05-28 19:03:18 +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 (
2018-04-13 12:54:40 +00:00
ctx context . Context ,
req * csi . ValidateVolumeCapabilitiesRequest ) ( * csi . ValidateVolumeCapabilitiesResponse , error ) {
2018-07-10 16:48:55 +00:00
// Cephfs doesn't support Block volume
for _ , cap := range req . VolumeCapabilities {
if cap . GetBlock ( ) != nil {
2018-11-24 18:48:36 +00:00
return & csi . ValidateVolumeCapabilitiesResponse { Message : "" } , nil
2018-07-10 16:48:55 +00:00
}
}
2018-11-24 18:48:36 +00:00
return & csi . ValidateVolumeCapabilitiesResponse {
Confirmed : & csi . ValidateVolumeCapabilitiesResponse_Confirmed {
VolumeCapabilities : req . VolumeCapabilities ,
} ,
} , nil
2018-03-05 11:59:47 +00:00
}
2019-10-07 06:41:33 +00:00
2020-07-19 12:21:03 +00:00
// ControllerExpandVolume expands CephFS Volumes on demand based on resizer request.
2019-10-07 06:41:33 +00:00
func ( cs * ControllerServer ) ControllerExpandVolume ( ctx context . Context , req * csi . ControllerExpandVolumeRequest ) ( * csi . ControllerExpandVolumeResponse , error ) {
if err := cs . validateExpandVolumeRequest ( req ) ; err != nil {
2020-08-11 11:24:15 +00:00
util . ErrorLog ( ctx , "ControllerExpandVolumeRequest validation failed: %v" , err )
2019-10-07 06:41:33 +00:00
return nil , err
}
volID := req . GetVolumeId ( )
secret := req . GetSecrets ( )
2019-11-25 11:09:24 +00:00
// lock out parallel delete operations
if acquired := cs . VolumeLocks . TryAcquire ( volID ) ; ! acquired {
2020-08-11 11:24:15 +00:00
util . ErrorLog ( ctx , util . VolumeOperationAlreadyExistsFmt , volID )
2019-11-25 11:09:24 +00:00
return nil , status . Errorf ( codes . Aborted , util . VolumeOperationAlreadyExistsFmt , volID )
}
defer cs . VolumeLocks . Release ( volID )
2020-08-03 18:28:52 +00:00
// lock out volumeID for clone and delete operation
if err := cs . OperationLocks . GetExpandLock ( volID ) ; err != nil {
2020-08-11 11:24:15 +00:00
util . ErrorLog ( ctx , err . Error ( ) )
2020-08-03 18:28:52 +00:00
return nil , status . Error ( codes . Aborted , err . Error ( ) )
}
defer cs . OperationLocks . ReleaseExpandLock ( volID )
2019-10-07 06:41:33 +00:00
cr , err := util . NewAdminCredentials ( secret )
if err != nil {
return nil , status . Error ( codes . InvalidArgument , err . Error ( ) )
}
defer cr . DeleteCredentials ( )
volOptions , volIdentifier , err := newVolumeOptionsFromVolID ( ctx , volID , nil , secret )
if err != nil {
2020-08-11 11:24:15 +00:00
util . ErrorLog ( ctx , "validation and extraction of volume options failed: %v" , err )
2019-10-07 06:41:33 +00:00
return nil , status . Error ( codes . InvalidArgument , err . Error ( ) )
}
2019-11-26 08:29:28 +00:00
RoundOffSize := util . RoundOffBytes ( req . GetCapacityRange ( ) . GetRequiredBytes ( ) )
2020-05-06 13:47:46 +00:00
if err = resizeVolume ( ctx , volOptions , cr , volumeID ( volIdentifier . FsSubvolName ) , RoundOffSize ) ; err != nil {
2020-08-11 11:24:15 +00:00
util . ErrorLog ( ctx , "failed to expand volume %s: %v" , volumeID ( volIdentifier . FsSubvolName ) , err )
2019-10-07 06:41:33 +00:00
return nil , status . Error ( codes . Internal , err . Error ( ) )
}
return & csi . ControllerExpandVolumeResponse {
2019-11-26 08:29:28 +00:00
CapacityBytes : RoundOffSize ,
2019-10-07 06:41:33 +00:00
NodeExpansionRequired : false ,
} , nil
}
2020-08-04 04:25:28 +00:00
// CreateSnapshot creates the snapshot in backend and stores metadata
// in store
// nolint:gocyclo // golangci-lint did not catch this earlier, needs to get fixed late
func ( cs * ControllerServer ) CreateSnapshot ( ctx context . Context , req * csi . CreateSnapshotRequest ) ( * csi . CreateSnapshotResponse , error ) {
if err := cs . validateSnapshotReq ( ctx , req ) ; err != nil {
return nil , err
}
cr , err := util . NewAdminCredentials ( req . GetSecrets ( ) )
if err != nil {
return nil , err
}
defer cr . DeleteCredentials ( )
clusterData , err := getClusterInformation ( req . GetParameters ( ) )
if err != nil {
return nil , status . Error ( codes . Internal , err . Error ( ) )
}
requestName := req . GetName ( )
sourceVolID := req . GetSourceVolumeId ( )
// Existence and conflict checks
if acquired := cs . SnapshotLocks . TryAcquire ( requestName ) ; ! acquired {
2020-08-11 11:25:36 +00:00
util . ErrorLog ( ctx , util . SnapshotOperationAlreadyExistsFmt , requestName )
2020-08-04 04:25:28 +00:00
return nil , status . Errorf ( codes . Aborted , util . SnapshotOperationAlreadyExistsFmt , requestName )
}
defer cs . SnapshotLocks . Release ( requestName )
if err = cs . OperationLocks . GetSnapshotCreateLock ( sourceVolID ) ; err != nil {
2020-08-11 11:25:36 +00:00
util . ErrorLog ( ctx , err . Error ( ) )
2020-08-04 04:25:28 +00:00
return nil , status . Error ( codes . Aborted , err . Error ( ) )
}
defer cs . OperationLocks . ReleaseSnapshotCreateLock ( sourceVolID )
// Find the volume using the provided VolumeID
parentVolOptions , vid , err := newVolumeOptionsFromVolID ( ctx , sourceVolID , nil , req . GetSecrets ( ) )
if err != nil {
if errors . Is ( err , util . ErrPoolNotFound ) {
2020-08-11 11:40:36 +00:00
util . WarningLog ( ctx , "failed to get backend volume for %s: %v" , sourceVolID , err )
2020-08-04 04:25:28 +00:00
return nil , status . Error ( codes . NotFound , err . Error ( ) )
}
if errors . Is ( err , ErrVolumeNotFound ) {
return nil , status . Error ( codes . NotFound , err . Error ( ) )
}
return nil , status . Error ( codes . Internal , err . Error ( ) )
}
if clusterData . ClusterID != parentVolOptions . ClusterID {
return nil , status . Errorf ( codes . InvalidArgument , "requested cluster id %s not matching subvolume cluster id %s" , clusterData . ClusterID , parentVolOptions . ClusterID )
}
cephfsSnap , genSnapErr := genSnapFromOptions ( ctx , req )
if genSnapErr != nil {
return nil , status . Error ( codes . Internal , genSnapErr . Error ( ) )
}
// lock out parallel snapshot create operations
if acquired := cs . VolumeLocks . TryAcquire ( sourceVolID ) ; ! acquired {
2020-08-11 11:25:36 +00:00
util . ErrorLog ( ctx , util . VolumeOperationAlreadyExistsFmt , sourceVolID )
2020-08-04 04:25:28 +00:00
return nil , status . Errorf ( codes . Aborted , util . VolumeOperationAlreadyExistsFmt , sourceVolID )
}
defer cs . VolumeLocks . Release ( sourceVolID )
snapName := req . GetName ( )
sid , snapInfo , err := checkSnapExists ( ctx , parentVolOptions , vid . FsSubvolName , cephfsSnap , cr )
if err != nil {
return nil , status . Error ( codes . Internal , err . Error ( ) )
}
// check are we able to retrieve the size of parent
// ceph fs subvolume info command got added in 14.2.10 and 15.+
// as we are not able to retrieve the parent size we are rejecting the
// request to create snapshot.
// TODO: For this purpose we could make use of cached clusterAdditionalInfo too.
var info Subvolume
info , err = getSubVolumeInfo ( ctx , parentVolOptions , cr , volumeID ( vid . FsSubvolName ) )
if err != nil {
// Check error code value against ErrInvalidCommand to understand the cluster
// support it or not, its safe to evaluat as the filtering
// is already done from getSubVolumeInfo() and send out the error here.
if errors . Is ( err , ErrInvalidCommand ) {
return nil , status . Error ( codes . FailedPrecondition , "subvolume info command not supported in current ceph cluster" )
}
if sid != nil {
errDefer := undoSnapReservation ( ctx , parentVolOptions , * sid , snapName , cr )
if errDefer != nil {
2020-08-11 11:40:36 +00:00
util . WarningLog ( ctx , "failed undoing reservation of snapshot: %s (%s)" ,
2020-08-04 04:25:28 +00:00
requestName , errDefer )
}
}
return nil , status . Error ( codes . Internal , err . Error ( ) )
}
if sid != nil {
// check snapshot is protected
protected := true
if ! ( snapInfo . Protected == snapshotIsProtected ) {
err = protectSnapshot ( ctx , parentVolOptions , cr , volumeID ( sid . FsSnapshotName ) , volumeID ( vid . FsSubvolName ) )
if err != nil {
protected = false
2020-08-11 11:40:36 +00:00
util . WarningLog ( ctx , "failed to protect snapshot of snapshot: %s (%s)" ,
2020-08-04 04:25:28 +00:00
sid . FsSnapshotName , err )
}
}
return & csi . CreateSnapshotResponse {
Snapshot : & csi . Snapshot {
SizeBytes : int64 ( info . BytesQuota ) ,
SnapshotId : sid . SnapshotID ,
SourceVolumeId : req . GetSourceVolumeId ( ) ,
CreationTime : sid . CreationTime ,
ReadyToUse : protected ,
} ,
} , nil
}
// Reservation
sID , err := reserveSnap ( ctx , parentVolOptions , vid . FsSubvolName , cephfsSnap , cr )
if err != nil {
return nil , status . Error ( codes . Internal , err . Error ( ) )
}
defer func ( ) {
if err != nil {
errDefer := undoSnapReservation ( ctx , parentVolOptions , * sID , snapName , cr )
if errDefer != nil {
2020-08-11 11:40:36 +00:00
util . WarningLog ( ctx , "failed undoing reservation of snapshot: %s (%s)" ,
2020-08-04 04:25:28 +00:00
requestName , errDefer )
}
}
} ( )
snap := snapshotInfo { }
snap , err = doSnapshot ( ctx , parentVolOptions , vid . FsSubvolName , sID . FsSnapshotName , cr )
if err != nil {
return nil , status . Error ( codes . Internal , err . Error ( ) )
}
return & csi . CreateSnapshotResponse {
Snapshot : & csi . Snapshot {
SizeBytes : int64 ( info . BytesQuota ) ,
SnapshotId : sID . SnapshotID ,
SourceVolumeId : req . GetSourceVolumeId ( ) ,
CreationTime : snap . CreationTime ,
ReadyToUse : true ,
} ,
} , nil
}
func doSnapshot ( ctx context . Context , volOpt * volumeOptions , subvolumeName , snapshotName string , cr * util . Credentials ) ( snapshotInfo , error ) {
volID := volumeID ( subvolumeName )
snapID := volumeID ( snapshotName )
snap := snapshotInfo { }
err := createSnapshot ( ctx , volOpt , cr , snapID , volID )
if err != nil {
2020-08-11 11:27:18 +00:00
util . ErrorLog ( ctx , "failed to create snapshot %s %v" , snapID , err )
2020-08-04 04:25:28 +00:00
return snap , err
}
defer func ( ) {
if err != nil {
dErr := deleteSnapshot ( ctx , volOpt , cr , snapID , volID )
if dErr != nil {
2020-08-11 11:27:18 +00:00
util . ErrorLog ( ctx , "failed to delete snapshot %s %v" , snapID , err )
2020-08-04 04:25:28 +00:00
}
}
} ( )
snap , err = getSnapshotInfo ( ctx , volOpt , cr , snapID , volID )
if err != nil {
2020-08-11 11:27:18 +00:00
util . ErrorLog ( ctx , "failed to get snapshot info %s %v" , snapID , err )
2020-08-04 04:25:28 +00:00
return snap , fmt . Errorf ( "failed to get snapshot info for snapshot:%s" , snapID )
}
var t * timestamp . Timestamp
t , err = parseTime ( ctx , snap . CreatedAt )
if err != nil {
return snap , err
}
snap . CreationTime = t
err = protectSnapshot ( ctx , volOpt , cr , snapID , volID )
if err != nil {
2020-08-11 11:27:18 +00:00
util . ErrorLog ( ctx , "failed to protect snapshot %s %v" , snapID , err )
2020-08-04 04:25:28 +00:00
}
return snap , err
}
func ( cs * ControllerServer ) validateSnapshotReq ( ctx context . Context , req * csi . CreateSnapshotRequest ) error {
if err := cs . Driver . ValidateControllerServiceRequest ( csi . ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT ) ; err != nil {
2020-08-11 11:28:14 +00:00
util . ErrorLog ( ctx , "invalid create snapshot req: %v" , protosanitizer . StripSecrets ( req ) )
2020-08-04 04:25:28 +00:00
return err
}
// Check sanity of request Snapshot Name, Source Volume Id
if req . Name == "" {
return status . Error ( codes . NotFound , "snapshot Name cannot be empty" )
}
if req . SourceVolumeId == "" {
return status . Error ( codes . NotFound , "source Volume ID cannot be empty" )
}
return nil
}
// DeleteSnapshot deletes the snapshot in backend and removes the
// snapshot metadata from store.
func ( cs * ControllerServer ) DeleteSnapshot ( ctx context . Context , req * csi . DeleteSnapshotRequest ) ( * csi . DeleteSnapshotResponse , error ) {
if err := cs . Driver . ValidateControllerServiceRequest ( csi . ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT ) ; err != nil {
2020-08-11 11:29:54 +00:00
util . ErrorLog ( ctx , "invalid delete snapshot req: %v" , protosanitizer . StripSecrets ( req ) )
2020-08-04 04:25:28 +00:00
return nil , err
}
cr , err := util . NewAdminCredentials ( req . GetSecrets ( ) )
if err != nil {
return nil , status . Error ( codes . InvalidArgument , err . Error ( ) )
}
defer cr . DeleteCredentials ( )
snapshotID := req . GetSnapshotId ( )
if snapshotID == "" {
return nil , status . Error ( codes . InvalidArgument , "snapshot ID cannot be empty" )
}
if acquired := cs . SnapshotLocks . TryAcquire ( snapshotID ) ; ! acquired {
2020-08-11 11:29:54 +00:00
util . ErrorLog ( ctx , util . SnapshotOperationAlreadyExistsFmt , snapshotID )
2020-08-04 04:25:28 +00:00
return nil , status . Errorf ( codes . Aborted , util . SnapshotOperationAlreadyExistsFmt , snapshotID )
}
defer cs . SnapshotLocks . Release ( snapshotID )
// lock out snapshotID for restore operation
if err = cs . OperationLocks . GetDeleteLock ( snapshotID ) ; err != nil {
2020-08-11 11:29:54 +00:00
util . ErrorLog ( ctx , err . Error ( ) )
2020-08-04 04:25:28 +00:00
return nil , status . Error ( codes . Aborted , err . Error ( ) )
}
defer cs . OperationLocks . ReleaseDeleteLock ( snapshotID )
volOpt , snapInfo , sid , err := newSnapshotOptionsFromID ( ctx , snapshotID , cr )
if err != nil {
// if error is ErrPoolNotFound, the pool is already deleted we dont
// need to worry about deleting snapshot or omap data, return success
if errors . Is ( err , util . ErrPoolNotFound ) {
2020-08-11 11:40:36 +00:00
util . WarningLog ( ctx , "failed to get backend snapshot for %s: %v" , snapshotID , err )
2020-08-04 04:25:28 +00:00
return & csi . DeleteSnapshotResponse { } , nil
}
// if error is ErrKeyNotFound, then a previous attempt at deletion was complete
// or partially complete (snap and snapOMap are garbage collected already), hence return
// success as deletion is complete
if errors . Is ( err , util . ErrKeyNotFound ) {
return & csi . DeleteSnapshotResponse { } , nil
}
if errors . Is ( err , ErrSnapNotFound ) {
err = undoSnapReservation ( ctx , volOpt , * sid , sid . FsSnapshotName , cr )
if err != nil {
2020-08-11 11:29:54 +00:00
util . ErrorLog ( ctx , "failed to remove reservation for snapname (%s) with backing snap (%s) (%s)" ,
2020-08-04 04:25:28 +00:00
sid . FsSubvolName , sid . FsSnapshotName , err )
return nil , status . Error ( codes . Internal , err . Error ( ) )
}
return & csi . DeleteSnapshotResponse { } , nil
}
return nil , status . Error ( codes . Internal , err . Error ( ) )
}
// safeguard against parallel create or delete requests against the same
// name
if acquired := cs . SnapshotLocks . TryAcquire ( sid . RequestName ) ; ! acquired {
2020-08-11 11:29:54 +00:00
util . ErrorLog ( ctx , util . SnapshotOperationAlreadyExistsFmt , sid . RequestName )
2020-08-04 04:25:28 +00:00
return nil , status . Errorf ( codes . Aborted , util . VolumeOperationAlreadyExistsFmt , sid . RequestName )
}
defer cs . SnapshotLocks . Release ( sid . RequestName )
if snapInfo . HasPendingClones == "yes" {
return nil , status . Errorf ( codes . FailedPrecondition , "snapshot %s has pending clones" , snapshotID )
}
if snapInfo . Protected == snapshotIsProtected {
err = unprotectSnapshot ( ctx , volOpt , cr , volumeID ( sid . FsSnapshotName ) , volumeID ( sid . FsSubvolName ) )
if err != nil {
return nil , status . Error ( codes . Internal , err . Error ( ) )
}
}
err = deleteSnapshot ( ctx , volOpt , cr , volumeID ( sid . FsSnapshotName ) , volumeID ( sid . FsSubvolName ) )
if err != nil {
return nil , status . Error ( codes . Internal , err . Error ( ) )
}
err = undoSnapReservation ( ctx , volOpt , * sid , sid . FsSnapshotName , cr )
if err != nil {
2020-08-11 11:29:54 +00:00
util . ErrorLog ( ctx , "failed to remove reservation for snapname (%s) with backing snap (%s) (%s)" ,
2020-08-04 04:25:28 +00:00
sid . RequestName , sid . FsSnapshotName , err )
return nil , status . Error ( codes . Internal , err . Error ( ) )
}
return & csi . DeleteSnapshotResponse { } , nil
}