in pkg/csi/manila/controllerserver.go [202:310]
func (cs *controllerServer) CreateSnapshot(ctx context.Context, req *csi.CreateSnapshotRequest) (*csi.CreateSnapshotResponse, error) {
if err := validateCreateSnapshotRequest(req); err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}
// Configuration
osOpts, err := options.NewOpenstackOptions(req.GetSecrets())
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid OpenStack secrets: %v", err)
}
// Check for pending CreateSnapshots for this snapshot name
if _, isPending := pendingSnapshots.LoadOrStore(req.GetName(), true); isPending {
return nil, status.Errorf(codes.Aborted, "snapshot %s is already being processed", req.GetName())
}
defer pendingSnapshots.Delete(req.GetName())
manilaClient, err := cs.d.manilaClientBuilder.New(osOpts)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to create Manila v2 client: %v", err)
}
// Retrieve the source share
sourceShare, err := manilaClient.GetShareByID(req.GetSourceVolumeId())
if err != nil {
if clouderrors.IsNotFound(err) {
return nil, status.Errorf(codes.NotFound, "failed to create snapshot %s for volume %s because the volume doesn't exist: %v", req.GetName(), req.GetSourceVolumeId(), err)
}
return nil, status.Errorf(codes.Internal, "failed to retrieve source volume %s when creating snapshot %s: %v", req.GetSourceVolumeId(), req.GetName(), err)
}
if strings.ToUpper(sourceShare.ShareProto) != cs.d.shareProto {
return nil, status.Errorf(codes.InvalidArgument, "share protocol mismatch: requested snapshot of %s volume %s, but share protocol selector is set to %s",
sourceShare.ShareProto, req.GetSourceVolumeId(), cs.d.shareProto)
}
// In order to satisfy CSI spec requirements around CREATE_DELETE_SNAPSHOT
// and the ability to populate volumes with snapshot contents, parent share
// must advertise snapshot_support and create_share_from_snapshot_support
// capabilities.
if !sourceShare.SnapshotSupport || !sourceShare.CreateShareFromSnapshotSupport {
return nil, status.Errorf(codes.InvalidArgument,
"cannot create snapshot %s for volume %s: parent share must advertise snapshot_support and create_share_from_snapshot_support capabilities",
req.GetName(), req.GetSourceVolumeId())
}
// Retrieve an existing snapshot or create a new one
snapshot, err := getOrCreateSnapshot(req.GetName(), sourceShare.ID, manilaClient)
if err != nil {
if err == wait.ErrWaitTimeout {
return nil, status.Errorf(codes.DeadlineExceeded, "deadline exceeded while waiting for snapshot %s of volume %s to become available", snapshot.ID, req.GetSourceVolumeId())
}
if clouderrors.IsNotFound(err) {
return nil, status.Errorf(codes.NotFound, "failed to create snapshot %s for volume %s because the volume doesn't exist: %v", req.GetName(), req.GetSourceVolumeId(), err)
}
return nil, status.Errorf(codes.Internal, "failed to create snapshot %s of volume %s: %v", req.GetName(), req.GetSourceVolumeId(), err)
}
if err = verifySnapshotCompatibility(snapshot, req); err != nil {
return nil, status.Errorf(codes.AlreadyExists, "snapshot %s already exists, but is incompatible with the request: %v", req.GetName(), err)
}
// Check for snapshot status, determine whether it's ready
var readyToUse bool
switch snapshot.Status {
case snapshotCreating:
readyToUse = false
case snapshotAvailable:
readyToUse = true
case snapshotError:
// An error occurred, try to roll-back the snapshot
tryDeleteSnapshot(snapshot, manilaClient)
manilaErrMsg, err := lastResourceError(snapshot.ID, manilaClient)
if err != nil {
return nil, status.Errorf(codes.Internal, "snapshot %s of volume %s is in error state, error description could not be retrieved: %v", snapshot.ID, req.GetSourceVolumeId(), err.Error())
}
return nil, status.Errorf(manilaErrMsg.errCode.toRpcErrorCode(), "snapshot %s of volume %s is in error state: %s", snapshot.ID, req.GetSourceVolumeId(), manilaErrMsg.message)
default:
return nil, status.Errorf(codes.Internal, "an error occurred while creating snapshot %s of volume %s: snapshot is in an unexpected state: wanted creating/available, got %s",
req.GetName(), req.GetSourceVolumeId(), snapshot.Status)
}
// Parse CreatedAt timestamp
ctime, err := ptypes.TimestampProto(snapshot.CreatedAt)
if err != nil {
klog.Warningf("couldn't parse timestamp %v from snapshot %s: %v", snapshot.CreatedAt, snapshot.ID, err)
}
return &csi.CreateSnapshotResponse{
Snapshot: &csi.Snapshot{
SnapshotId: snapshot.ID,
SourceVolumeId: req.GetSourceVolumeId(),
SizeBytes: int64(sourceShare.Size) * bytesInGiB,
CreationTime: ctime,
ReadyToUse: readyToUse,
},
}, nil
}