2018-01-09 18:59:50 +00:00
/ *
2019-04-03 08:46:15 +00:00
Copyright 2018 The Ceph - CSI Authors .
2018-01-09 18:59:50 +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 rbd
import (
2019-08-24 09:14:15 +00:00
"context"
2019-04-22 21:35:39 +00:00
"encoding/json"
2020-06-25 11:30:04 +00:00
"errors"
2018-01-09 18:59:50 +00:00
"fmt"
2019-08-03 22:11:28 +00:00
"io/ioutil"
"os"
2018-01-09 18:59:50 +00:00
"os/exec"
2019-08-03 22:11:28 +00:00
"path/filepath"
2019-12-13 11:41:32 +00:00
"strconv"
2018-01-09 18:59:50 +00:00
"strings"
"time"
2018-03-06 22:33:57 +00:00
2020-04-17 09:23:49 +00:00
"github.com/ceph/ceph-csi/internal/util"
2020-04-15 03:38:16 +00:00
2020-03-16 09:26:43 +00:00
"github.com/ceph/go-ceph/rados"
2020-01-07 13:45:52 +00:00
librbd "github.com/ceph/go-ceph/rbd"
2020-01-24 16:26:56 +00:00
"github.com/container-storage-interface/spec/lib/go/csi"
2020-06-24 07:43:24 +00:00
"github.com/golang/protobuf/ptypes"
2019-04-22 21:35:39 +00:00
"github.com/golang/protobuf/ptypes/timestamp"
2019-05-31 18:09:24 +00:00
"github.com/pborman/uuid"
2018-07-20 08:46:44 +00:00
"k8s.io/apimachinery/pkg/util/sets"
2020-01-17 15:44:06 +00:00
"k8s.io/cloud-provider/volume/helpers"
2019-02-04 13:05:07 +00:00
"k8s.io/klog"
2018-01-09 18:59:50 +00:00
)
const (
imageWatcherStr = "watcher="
// The following three values are used for 30 seconds timeout
// while waiting for RBD Watcher to expire.
rbdImageWatcherInitDelay = 1 * time . Second
rbdImageWatcherFactor = 1.4
rbdImageWatcherSteps = 10
2018-09-18 14:09:12 +00:00
rbdDefaultMounter = "rbd"
2019-08-06 16:59:40 +00:00
// Output strings returned during invocation of "ceph rbd task add remove <imagespec>" when
// command is not supported by ceph manager. Used to check errors and recover when the command
// is unsupported.
2019-10-10 14:30:58 +00:00
rbdTaskRemoveCmdInvalidString1 = "no valid command found"
rbdTaskRemoveCmdInvalidString2 = "Error EINVAL: invalid command"
rbdTaskRemoveCmdAccessDeniedMessage = "Error EACCES:"
2019-12-13 11:41:32 +00:00
// Encryption statuses for RbdImage
rbdImageEncrypted = "encrypted"
rbdImageRequiresEncryption = "requiresEncryption"
2020-01-09 10:31:07 +00:00
// image metadata key for encryption
encryptionMetaKey = ".rbd.csi.ceph.com/encrypted"
2020-06-24 07:43:24 +00:00
// go-ceph will provide rbd.ImageOptionCloneFormat
imageOptionCloneFormat = librbd . RbdImageOption ( 12 )
2018-01-09 18:59:50 +00:00
)
2019-04-22 21:35:39 +00:00
// rbdVolume represents a CSI volume and its RBD image specifics
2018-03-06 22:33:57 +00:00
type rbdVolume struct {
2019-05-31 18:09:24 +00:00
// RbdImageName is the name of the RBD image backing this rbdVolume. This does not have a
// JSON tag as it is not stashed in JSON encoded config maps in v1.0.0
2019-04-22 21:35:39 +00:00
// VolID is the volume ID that is exchanged with CSI drivers, identifying this rbdVol
2019-05-31 18:09:24 +00:00
// RequestName is the CSI generated volume name for the rbdVolume. This does not have a
// JSON tag as it is not stashed in JSON encoded config maps in v1.0.0
// VolName and MonValueFromSecret are retained from older plugin versions (<= 1.0.0)
// for backward compatibility reasons
2020-01-24 16:26:56 +00:00
// JournalPool is the ceph pool in which the CSI Journal is stored
// Pool is where the image journal and image is stored, and could be the same as `JournalPool`
// (retained as Pool instead of renaming to ImagePool or such, as this is referenced in the code extensively)
// DataPool is where the data for images in `Pool` are stored, this is used as the `--data-pool`
// argument when the pool is created, and is not used anywhere else
TopologyPools * [ ] util . TopologyConstrainedPool
TopologyRequirement * csi . TopologyRequirement
Topology map [ string ] string
RbdImageName string
NamePrefix string
VolID string ` json:"volID" `
Monitors string ` json:"monitors" `
JournalPool string
Pool string ` json:"pool" `
DataPool string
2020-06-24 06:56:39 +00:00
ImageID string
2020-06-24 07:43:24 +00:00
ParentName string
2020-06-18 12:07:21 +00:00
imageFeatureSet librbd . FeatureSet
2020-01-24 16:26:56 +00:00
AdminID string ` json:"adminId" `
UserID string ` json:"userId" `
Mounter string ` json:"mounter" `
ClusterID string ` json:"clusterId" `
RequestName string
2020-06-24 07:01:06 +00:00
ReservedID string
2020-01-24 16:26:56 +00:00
VolName string ` json:"volName" `
MonValueFromSecret string ` json:"monValueFromSecret" `
VolSize int64 ` json:"volSize" `
DisableInUseChecks bool ` json:"disableInUseChecks" `
Encrypted bool
2020-04-16 14:47:43 +00:00
readOnly bool
2020-01-24 16:26:56 +00:00
KMS util . EncryptionKMS
2020-06-24 07:43:24 +00:00
CreatedAt * timestamp . Timestamp
2020-03-18 08:30:02 +00:00
// conn is a connection to the Ceph cluster obtained from a ConnPool
conn * util . ClusterConnection
2020-06-03 07:37:44 +00:00
// an opened IOContext, call .openIoctx() before using
ioctx * rados . IOContext
2018-01-09 18:59:50 +00:00
}
2019-04-22 21:35:39 +00:00
// rbdSnapshot represents a CSI snapshot and its RBD snapshot specifics
2018-08-08 05:42:17 +00:00
type rbdSnapshot struct {
2019-04-22 21:35:39 +00:00
// SourceVolumeID is the volume ID of RbdImageName, that is exchanged with CSI drivers
// RbdImageName is the name of the RBD image, that is this rbdSnapshot's source image
// RbdSnapName is the name of the RBD snapshot backing this rbdSnapshot
// SnapID is the snapshot ID that is exchanged with CSI drivers, identifying this rbdSnapshot
// RequestName is the CSI generated snapshot name for the rbdSnapshot
2020-01-24 16:26:56 +00:00
// JournalPool is the ceph pool in which the CSI snapshot Journal is stored
// Pool is where the image snapshot journal and snapshot is stored, and could be the same as `JournalPool`
2019-04-22 21:35:39 +00:00
SourceVolumeID string
RbdImageName string
2020-06-24 07:01:06 +00:00
ReservedID string
2020-02-24 13:19:42 +00:00
NamePrefix string
2019-04-22 21:35:39 +00:00
RbdSnapName string
SnapID string
Monitors string
2020-01-24 16:26:56 +00:00
JournalPool string
2019-04-22 21:35:39 +00:00
Pool string
CreatedAt * timestamp . Timestamp
SizeBytes int64
ClusterID string
RequestName string
2018-08-08 05:42:17 +00:00
}
2018-07-20 08:46:44 +00:00
var (
2020-06-18 12:07:21 +00:00
supportedFeatures = sets . NewString ( librbd . FeatureNameLayering )
2018-07-20 08:46:44 +00:00
)
2018-01-09 18:59:50 +00:00
2020-03-18 08:30:02 +00:00
// Connect an rbdVolume to the Ceph cluster
func ( rv * rbdVolume ) Connect ( cr * util . Credentials ) error {
if rv . conn != nil {
return nil
}
conn := & util . ClusterConnection { }
if err := conn . Connect ( rv . Monitors , cr ) ; err != nil {
return err
}
rv . conn = conn
return nil
}
// Destroy cleans up the rbdVolume and closes the connection to the Ceph
// cluster in case one was setup.
func ( rv * rbdVolume ) Destroy ( ) {
2020-06-03 07:37:44 +00:00
if rv . ioctx != nil {
rv . ioctx . Destroy ( )
}
2020-03-18 08:30:02 +00:00
if rv . conn != nil {
rv . conn . Destroy ( )
}
}
2020-05-28 18:39:44 +00:00
// String returns the image-spec (pool/image) format of the image
func ( rv * rbdVolume ) String ( ) string {
return fmt . Sprintf ( "%s/%s" , rv . Pool , rv . RbdImageName )
}
// String returns the snap-spec (pool/image@snap) format of the snapshot
func ( rs * rbdSnapshot ) String ( ) string {
return fmt . Sprintf ( "%s/%s@%s" , rs . Pool , rs . RbdImageName , rs . RbdSnapName )
}
2019-04-22 21:35:39 +00:00
// createImage creates a new ceph image with provision and volume options.
2020-02-26 09:35:18 +00:00
func createImage ( ctx context . Context , pOpts * rbdVolume , cr * util . Credentials ) error {
volSzMiB := fmt . Sprintf ( "%dM" , util . RoundOffVolSize ( pOpts . VolSize ) )
2020-01-07 13:45:52 +00:00
options := librbd . NewRbdImageOptions ( )
2020-05-28 18:39:44 +00:00
logMsg := "rbd: create %s size %s (features: %s) using mon %s"
2019-09-11 07:08:55 +00:00
if pOpts . DataPool != "" {
2020-05-28 18:39:44 +00:00
logMsg += fmt . Sprintf ( ", data pool %s" , pOpts . DataPool )
2020-01-10 09:09:49 +00:00
err := options . SetString ( librbd . RbdImageOptionDataPool , pOpts . DataPool )
2020-01-07 13:45:52 +00:00
if err != nil {
2020-06-25 11:30:04 +00:00
return fmt . Errorf ( "failed to set data pool: %w" , err )
2020-01-07 13:45:52 +00:00
}
2018-01-09 18:59:50 +00:00
}
2019-09-11 07:08:55 +00:00
klog . V ( 4 ) . Infof ( util . Log ( ctx , logMsg ) ,
2020-06-18 12:07:21 +00:00
pOpts , volSzMiB , pOpts . imageFeatureSet . Names ( ) , pOpts . Monitors )
2019-09-11 07:08:55 +00:00
2020-06-18 12:07:21 +00:00
if pOpts . imageFeatureSet != 0 {
err := options . SetUint64 ( librbd . RbdImageOptionFeatures , uint64 ( pOpts . imageFeatureSet ) )
2020-01-07 13:45:52 +00:00
if err != nil {
2020-06-25 11:30:04 +00:00
return fmt . Errorf ( "failed to set image features: %w" , err )
2020-01-07 13:45:52 +00:00
}
}
2019-09-10 09:56:08 +00:00
2020-03-18 08:30:02 +00:00
err := pOpts . Connect ( cr )
if err != nil {
return err
}
2020-06-03 07:37:44 +00:00
err = pOpts . openIoctx ( )
2020-01-07 13:45:52 +00:00
if err != nil {
2020-06-25 11:30:04 +00:00
return fmt . Errorf ( "failed to get IOContext: %w" , err )
2019-09-10 09:56:08 +00:00
}
2018-01-09 18:59:50 +00:00
2020-06-03 07:37:44 +00:00
err = librbd . CreateImage ( pOpts . ioctx , pOpts . RbdImageName ,
2020-01-17 15:44:06 +00:00
uint64 ( util . RoundOffVolSize ( pOpts . VolSize ) * helpers . MiB ) , options )
2018-01-09 18:59:50 +00:00
if err != nil {
2020-06-25 11:30:04 +00:00
return fmt . Errorf ( "failed to create rbd image: %w" , err )
2018-01-09 18:59:50 +00:00
}
return nil
}
2020-06-03 07:37:44 +00:00
func ( rv * rbdVolume ) openIoctx ( ) error {
if rv . ioctx != nil {
return nil
}
ioctx , err := rv . conn . GetIoctx ( rv . Pool )
if err != nil {
// GetIoctx() can return util.ErrPoolNotFound
return err
}
rv . ioctx = ioctx
return nil
}
2020-06-24 06:46:47 +00:00
// getImageID queries rbd about the given image and stores its id, returns
// ErrImageNotFound if provided image is not found
func ( rv * rbdVolume ) getImageID ( ) error {
if rv . ImageID != "" {
return nil
}
image , err := rv . open ( )
if err != nil {
return err
}
defer image . Close ( )
id , err := image . GetId ( )
if err != nil {
return err
}
rv . ImageID = id
return nil
}
2020-06-24 06:56:39 +00:00
2020-05-08 14:10:18 +00:00
// open the rbdVolume after it has been connected.
// ErrPoolNotFound or ErrImageNotFound are returned in case the pool or image
// can not be found, other errors will contain more details about other issues
// (permission denied, ...) and are expected to relate to configuration issues.
func ( rv * rbdVolume ) open ( ) ( * librbd . Image , error ) {
2020-06-03 07:37:44 +00:00
err := rv . openIoctx ( )
2020-05-08 14:10:18 +00:00
if err != nil {
return nil , err
}
2020-06-03 07:37:44 +00:00
image , err := librbd . OpenImage ( rv . ioctx , rv . RbdImageName , librbd . NoSnapshot )
2020-05-08 14:10:18 +00:00
if err != nil {
2020-07-02 21:43:40 +00:00
if errors . Is ( err , librbd . ErrNotFound ) {
2020-05-08 14:10:18 +00:00
err = ErrImageNotFound { rv . RbdImageName , err }
}
return nil , err
}
return image , nil
}
2018-01-09 18:59:50 +00:00
// rbdStatus checks if there is watcher on the image.
2019-01-28 19:55:10 +00:00
// It returns true if there is a watcher on the image, otherwise returns false.
2019-08-22 16:57:23 +00:00
func rbdStatus ( ctx context . Context , pOpts * rbdVolume , cr * util . Credentials ) ( bool , string , error ) {
2018-01-09 18:59:50 +00:00
var output string
var cmd [ ] byte
2020-05-28 18:39:44 +00:00
klog . V ( 4 ) . Infof ( util . Log ( ctx , "rbd: status %s using mon %s" ) , pOpts , pOpts . Monitors )
args := [ ] string { "status" , pOpts . String ( ) , "-m" , pOpts . Monitors , "--id" , cr . ID , "--keyfile=" + cr . KeyFile }
2019-06-01 21:26:42 +00:00
cmd , err := execCommand ( "rbd" , args )
2018-01-09 18:59:50 +00:00
output = string ( cmd )
2020-06-25 08:23:32 +00:00
var ee * exec . Error
if errors . As ( err , & ee ) {
if errors . Is ( ee , exec . ErrNotFound ) {
2019-08-22 16:57:23 +00:00
klog . Errorf ( util . Log ( ctx , "rbd cmd not found" ) )
2018-01-09 18:59:50 +00:00
// fail fast if command not found
return false , output , err
}
}
// If command never succeed, returns its last error.
if err != nil {
return false , output , err
}
if strings . Contains ( output , imageWatcherStr ) {
2020-05-28 18:39:44 +00:00
klog . V ( 4 ) . Infof ( util . Log ( ctx , "rbd: watchers on %s: %s" ) , pOpts , output )
2018-01-09 18:59:50 +00:00
return true , output , nil
}
2020-05-28 18:39:44 +00:00
klog . Warningf ( util . Log ( ctx , "rbd: no watchers on %s" ) , pOpts )
2019-01-16 13:03:38 +00:00
return false , output , nil
2018-01-09 18:59:50 +00:00
}
2020-06-24 06:56:39 +00:00
// addRbdManagerTask adds a ceph manager task to execute command
// asynchronously. If command is not found returns a bool set to false
// example arg ["trash", "remove","pool/image"]
func addRbdManagerTask ( ctx context . Context , pOpts * rbdVolume , arg [ ] string ) ( bool , error ) {
2019-08-06 16:59:40 +00:00
var output [ ] byte
2020-06-24 06:56:39 +00:00
args := [ ] string { "rbd" , "task" , "add" }
args = append ( args , arg ... )
klog . V ( 4 ) . Infof ( util . Log ( ctx , "executing %v for image (%s) using mon %s, pool %s" ) , args , pOpts . RbdImageName , pOpts . Monitors , pOpts . Pool )
supported := true
2019-08-06 16:59:40 +00:00
output , err := execCommand ( "ceph" , args )
2020-06-24 06:56:39 +00:00
2019-08-06 16:59:40 +00:00
if err != nil {
2020-04-09 15:14:43 +00:00
switch {
case strings . Contains ( string ( output ) , rbdTaskRemoveCmdInvalidString1 ) &&
strings . Contains ( string ( output ) , rbdTaskRemoveCmdInvalidString2 ) :
2020-06-24 06:56:39 +00:00
klog . Warningf ( util . Log ( ctx , "cluster with cluster ID (%s) does not support Ceph manager based rbd commands (minimum ceph version required is v14.2.3)" ) , pOpts . ClusterID )
supported = false
2020-04-09 15:14:43 +00:00
case strings . HasPrefix ( string ( output ) , rbdTaskRemoveCmdAccessDeniedMessage ) :
2020-06-24 06:56:39 +00:00
klog . Warningf ( util . Log ( ctx , "access denied to Ceph MGR-based rbd commands on cluster ID (%s)" ) , pOpts . ClusterID )
supported = false
2020-04-09 15:14:43 +00:00
default :
2020-06-24 06:56:39 +00:00
klog . Warningf ( util . Log ( ctx , "uncaught error while scheduling a task: %s" ) , err )
2019-08-06 16:59:40 +00:00
}
}
2020-06-24 06:56:39 +00:00
return supported , err
2019-08-06 16:59:40 +00:00
}
2019-04-22 21:35:39 +00:00
// deleteImage deletes a ceph image with provision and volume options.
2019-08-22 16:57:23 +00:00
func deleteImage ( ctx context . Context , pOpts * rbdVolume , cr * util . Credentials ) error {
2019-04-22 21:35:39 +00:00
image := pOpts . RbdImageName
2020-06-24 06:56:39 +00:00
// Support deleting the older rbd images whose imageID is not stored in omap
2020-07-02 06:45:47 +00:00
err := pOpts . getImageID ( )
2020-06-24 06:56:39 +00:00
if err != nil {
return err
}
klog . V ( 4 ) . Infof ( util . Log ( ctx , "rbd: delete %s using mon %s, pool %s" ) , image , pOpts . Monitors , pOpts . Pool )
err = pOpts . openIoctx ( )
if err != nil {
return err
2018-01-09 18:59:50 +00:00
}
2019-04-22 21:35:39 +00:00
2020-06-24 06:56:39 +00:00
rbdImage := librbd . GetImage ( pOpts . ioctx , image )
err = rbdImage . Trash ( 0 )
if err != nil {
klog . Errorf ( util . Log ( ctx , "failed to delete rbd image: %s, error: %v" ) , pOpts , err )
return err
}
2019-08-06 16:59:40 +00:00
// attempt to use Ceph manager based deletion support if available
2020-06-24 06:56:39 +00:00
args := [ ] string { "trash" , "remove" ,
pOpts . Pool + "/" + pOpts . ImageID ,
"--id" , cr . ID ,
"--keyfile=" + cr . KeyFile ,
"-m" , pOpts . Monitors ,
}
rbdCephMgrSupported , err := addRbdManagerTask ( ctx , pOpts , args )
2020-03-16 04:59:16 +00:00
if rbdCephMgrSupported && err != nil {
2020-05-28 18:39:44 +00:00
klog . Errorf ( util . Log ( ctx , "failed to add task to delete rbd image: %s, %v" ) , pOpts , err )
2020-03-16 04:59:16 +00:00
return err
}
2019-08-06 16:59:40 +00:00
if ! rbdCephMgrSupported {
2020-06-24 06:56:39 +00:00
err = librbd . TrashRemove ( pOpts . ioctx , pOpts . ImageID , true )
2020-03-16 09:26:43 +00:00
if err != nil {
2020-06-24 06:56:39 +00:00
klog . Errorf ( util . Log ( ctx , "failed to delete rbd image: %s, %v" ) , pOpts , err )
2020-03-16 09:26:43 +00:00
return err
}
2020-06-24 06:56:39 +00:00
}
2020-03-16 09:26:43 +00:00
2020-06-24 06:56:39 +00:00
return nil
}
2020-06-24 07:43:24 +00:00
func ( rv * rbdVolume ) getCloneDepth ( ctx context . Context ) ( uint , error ) {
var depth uint
vol := rbdVolume {
Pool : rv . Pool ,
Monitors : rv . Monitors ,
RbdImageName : rv . RbdImageName ,
conn : rv . conn ,
}
err := vol . openIoctx ( )
2020-06-24 06:56:39 +00:00
if err != nil {
2020-06-24 07:43:24 +00:00
return depth , err
2020-06-24 06:56:39 +00:00
}
2020-06-24 07:43:24 +00:00
defer func ( ) {
vol . ioctx . Destroy ( )
} ( )
for {
if vol . RbdImageName == "" {
return depth , nil
}
err = vol . getImageInfo ( )
if err != nil {
klog . Errorf ( util . Log ( ctx , "failed to check depth on image %s: %s" ) , vol , err )
return depth , err
}
if vol . ParentName != "" {
depth ++
}
vol . RbdImageName = vol . ParentName
}
}
func ( rv * rbdVolume ) flattenRbdImage ( ctx context . Context , cr * util . Credentials , forceFlatten bool ) error {
depth , err := rv . getCloneDepth ( ctx )
2020-06-24 06:56:39 +00:00
if err != nil {
return err
}
2020-06-24 07:43:24 +00:00
klog . Infof ( util . Log ( ctx , "clone depth is (%d), configured softlimit (%d) and hardlimit (%d) for %s" ) , depth , rbdSoftMaxCloneDepth , rbdHardMaxCloneDepth , rv )
if forceFlatten || ( depth >= rbdHardMaxCloneDepth ) || ( depth >= rbdSoftMaxCloneDepth ) {
args := [ ] string { "flatten" , rv . Pool + "/" + rv . RbdImageName , "--id" , cr . ID , "--keyfile=" + cr . KeyFile , "-m" , rv . Monitors }
supported , err := addRbdManagerTask ( ctx , rv , args )
if supported {
if err != nil {
klog . Errorf ( util . Log ( ctx , "failed to add task flatten for %s : %v" ) , rv , err )
return err
}
if forceFlatten || depth >= rbdHardMaxCloneDepth {
return ErrFlattenInProgress { err : fmt . Errorf ( "flatten is in progress for image %s" , rv . RbdImageName ) }
}
}
if ! supported {
klog . Errorf ( util . Log ( ctx , "task manager does not support flatten,image will be flattened once hardlimit is reached: %v" ) , err )
if forceFlatten || depth >= rbdHardMaxCloneDepth {
err = rv . Connect ( cr )
if err != nil {
return err
}
rbdImage , err := rv . open ( )
if err != nil {
return err
}
defer rbdImage . Close ( )
if err = rbdImage . Flatten ( ) ; err != nil {
klog . Errorf ( util . Log ( ctx , "rbd failed to flatten image %s %s: %v" ) , rv . Pool , rv . RbdImageName , err )
return err
}
}
2020-03-16 04:59:16 +00:00
}
2019-04-22 21:35:39 +00:00
}
2020-06-24 06:56:39 +00:00
return nil
2019-04-22 21:35:39 +00:00
}
2020-06-24 07:43:24 +00:00
func ( rv * rbdVolume ) hasFeature ( feature uint64 ) bool {
return ( uint64 ( rv . imageFeatureSet ) & feature ) == feature
}
func ( rv * rbdVolume ) checkImageChainHasFeature ( ctx context . Context , feature uint64 ) ( bool , error ) {
vol := rbdVolume {
Pool : rv . Pool ,
Monitors : rv . Monitors ,
RbdImageName : rv . RbdImageName ,
conn : rv . conn ,
}
err := vol . openIoctx ( )
if err != nil {
return false , err
}
defer vol . ioctx . Destroy ( )
for {
if vol . RbdImageName == "" {
return false , nil
}
err = vol . getImageInfo ( )
if err != nil {
klog . Errorf ( util . Log ( ctx , "failed to get image info for %s: %s" ) , vol , err )
return false , err
}
if f := vol . hasFeature ( feature ) ; f {
return true , nil
}
vol . RbdImageName = vol . ParentName
}
}
2019-04-22 21:35:39 +00:00
// genSnapFromSnapID generates a rbdSnapshot structure from the provided identifier, updating
// the structure with elements from on-disk snapshot metadata as well
2019-08-22 16:57:23 +00:00
func genSnapFromSnapID ( ctx context . Context , rbdSnap * rbdSnapshot , snapshotID string , cr * util . Credentials ) error {
2019-04-22 21:35:39 +00:00
var (
options map [ string ] string
vi util . CSIIdentifier
)
options = make ( map [ string ] string )
rbdSnap . SnapID = snapshotID
err := vi . DecomposeCSIID ( rbdSnap . SnapID )
if err != nil {
2019-08-22 16:57:23 +00:00
klog . Errorf ( util . Log ( ctx , "error decoding snapshot ID (%s) (%s)" ) , err , rbdSnap . SnapID )
2019-04-22 21:35:39 +00:00
return err
}
rbdSnap . ClusterID = vi . ClusterID
options [ "clusterID" ] = rbdSnap . ClusterID
2019-08-22 16:57:23 +00:00
rbdSnap . Monitors , _ , err = getMonsAndClusterID ( ctx , options )
2019-04-22 21:35:39 +00:00
if err != nil {
return err
}
2020-05-14 13:28:14 +00:00
rbdSnap . Pool , err = util . GetPoolName ( rbdSnap . Monitors , cr , vi . LocationID )
2019-04-22 21:35:39 +00:00
if err != nil {
return err
}
2020-01-24 16:26:56 +00:00
rbdSnap . JournalPool = rbdSnap . Pool
2019-04-22 21:35:39 +00:00
2020-05-12 21:05:55 +00:00
j , err := snapJournal . Connect ( rbdSnap . Monitors , cr )
if err != nil {
return err
}
defer j . Destroy ( )
imageAttributes , err := j . GetImageAttributes (
ctx , rbdSnap . Pool , vi . ObjectUUID , true )
2019-04-22 21:35:39 +00:00
if err != nil {
return err
}
2020-01-24 16:26:56 +00:00
rbdSnap . RequestName = imageAttributes . RequestName
rbdSnap . RbdImageName = imageAttributes . SourceName
rbdSnap . RbdSnapName = imageAttributes . ImageName
2020-06-24 07:43:24 +00:00
rbdSnap . ReservedID = vi . ObjectUUID
2020-01-24 16:26:56 +00:00
// convert the journal pool ID to name, for use in DeleteSnapshot cases
if imageAttributes . JournalPoolID != util . InvalidPoolID {
2020-05-14 13:28:14 +00:00
rbdSnap . JournalPool , err = util . GetPoolName ( rbdSnap . Monitors , cr , imageAttributes . JournalPoolID )
2020-01-24 16:26:56 +00:00
if err != nil {
// TODO: If pool is not found we may leak the image (as DeleteSnapshot will return success)
return err
}
}
2019-04-22 21:35:39 +00:00
return err
}
// genVolFromVolID generates a rbdVolume structure from the provided identifier, updating
// the structure with elements from on-disk image metadata as well
2020-03-17 13:39:35 +00:00
func genVolFromVolID ( ctx context . Context , volumeID string , cr * util . Credentials , secrets map [ string ] string ) ( * rbdVolume , error ) {
2019-04-22 21:35:39 +00:00
var (
options map [ string ] string
vi util . CSIIdentifier
2020-03-17 13:39:35 +00:00
rbdVol * rbdVolume
2019-04-22 21:35:39 +00:00
)
options = make ( map [ string ] string )
// rbdVolume fields that are not filled up in this function are:
2020-06-24 07:43:24 +00:00
// Mounter, MultiNodeWritable
2020-03-17 13:39:35 +00:00
rbdVol = & rbdVolume { VolID : volumeID }
2019-04-22 21:35:39 +00:00
err := vi . DecomposeCSIID ( rbdVol . VolID )
if err != nil {
2019-05-31 18:09:24 +00:00
err = fmt . Errorf ( "error decoding volume ID (%s) (%s)" , err , rbdVol . VolID )
2020-06-24 07:43:24 +00:00
return rbdVol , ErrInvalidVolID { err }
2019-04-22 21:35:39 +00:00
}
rbdVol . ClusterID = vi . ClusterID
options [ "clusterID" ] = rbdVol . ClusterID
2019-08-22 16:57:23 +00:00
rbdVol . Monitors , _ , err = getMonsAndClusterID ( ctx , options )
2019-04-22 21:35:39 +00:00
if err != nil {
2020-06-24 07:43:24 +00:00
return rbdVol , err
2019-04-22 21:35:39 +00:00
}
2020-05-14 13:28:14 +00:00
rbdVol . Pool , err = util . GetPoolName ( rbdVol . Monitors , cr , vi . LocationID )
2019-04-22 21:35:39 +00:00
if err != nil {
2020-06-24 07:43:24 +00:00
return rbdVol , err
2020-03-17 13:39:35 +00:00
}
err = rbdVol . Connect ( cr )
if err != nil {
2020-06-24 07:43:24 +00:00
return rbdVol , err
2019-04-22 21:35:39 +00:00
}
2020-01-24 16:26:56 +00:00
rbdVol . JournalPool = rbdVol . Pool
2019-04-22 21:35:39 +00:00
2020-05-12 21:05:55 +00:00
j , err := volJournal . Connect ( rbdVol . Monitors , cr )
if err != nil {
2020-06-24 07:43:24 +00:00
return rbdVol , err
2020-05-12 21:05:55 +00:00
}
defer j . Destroy ( )
imageAttributes , err := j . GetImageAttributes (
ctx , rbdVol . Pool , vi . ObjectUUID , false )
2019-04-22 21:35:39 +00:00
if err != nil {
2020-06-24 07:43:24 +00:00
return rbdVol , err
2019-04-22 21:35:39 +00:00
}
2020-01-24 16:26:56 +00:00
2020-06-24 07:43:24 +00:00
rbdVol . RequestName = imageAttributes . RequestName
rbdVol . RbdImageName = imageAttributes . ImageName
rbdVol . ReservedID = vi . ObjectUUID
2020-01-24 16:26:56 +00:00
if imageAttributes . KmsID != "" {
2020-01-29 11:44:45 +00:00
rbdVol . Encrypted = true
2020-01-24 16:26:56 +00:00
rbdVol . KMS , err = util . GetKMS ( imageAttributes . KmsID , secrets )
if err != nil {
2020-06-24 07:43:24 +00:00
return rbdVol , err
2020-01-24 16:26:56 +00:00
}
}
// convert the journal pool ID to name, for use in DeleteVolume cases
if imageAttributes . JournalPoolID >= 0 {
2020-05-14 13:28:14 +00:00
rbdVol . JournalPool , err = util . GetPoolName ( rbdVol . Monitors , cr , imageAttributes . JournalPoolID )
2020-01-29 11:44:45 +00:00
if err != nil {
2020-01-24 16:26:56 +00:00
// TODO: If pool is not found we may leak the image (as DeleteVolume will return success)
2020-06-24 07:43:24 +00:00
return rbdVol , err
2020-01-29 11:44:45 +00:00
}
}
2019-04-22 21:35:39 +00:00
2020-06-24 07:43:24 +00:00
rbdVol . ImageID , err = j . GetStoredImageID ( ctx , rbdVol . JournalPool , rbdVol . ReservedID , cr )
if _ , ok := err . ( util . ErrKeyNotFound ) ; ok {
err = rbdVol . getImageID ( )
if err != nil {
klog . Errorf ( util . Log ( ctx , "failed to get image id %s: %v" ) , rbdVol , err )
return rbdVol , err
}
err = j . StoreImageID ( ctx , rbdVol . JournalPool , rbdVol . ReservedID , rbdVol . ImageID , cr )
if err != nil {
klog . Errorf ( util . Log ( ctx , "failed to store volume id %s: %v" ) , rbdVol , err )
return rbdVol , err
}
}
if err != nil {
klog . Errorf ( util . Log ( ctx , "failed to get stored image id: %v" ) , err )
return rbdVol , err
}
2019-04-22 21:35:39 +00:00
2020-06-24 07:43:24 +00:00
err = rbdVol . getImageInfo ( )
2020-03-17 13:39:35 +00:00
return rbdVol , err
2018-01-09 18:59:50 +00:00
}
func execCommand ( command string , args [ ] string ) ( [ ] byte , error ) {
2019-01-28 19:55:10 +00:00
// #nosec
2018-01-09 18:59:50 +00:00
cmd := exec . Command ( command , args ... )
return cmd . CombinedOutput ( )
}
2019-08-22 16:57:23 +00:00
func getMonsAndClusterID ( ctx context . Context , options map [ string ] string ) ( monitors , clusterID string , err error ) {
2019-03-02 17:29:52 +00:00
var ok bool
2019-04-22 21:35:39 +00:00
if clusterID , ok = options [ "clusterID" ] ; ! ok {
err = errors . New ( "clusterID must be set" )
return
}
2019-03-07 21:03:33 +00:00
2019-04-22 21:35:39 +00:00
if monitors , err = util . Mons ( csiConfigFile , clusterID ) ; err != nil {
2019-08-22 16:57:23 +00:00
klog . Errorf ( util . Log ( ctx , "failed getting mons (%s)" ) , err )
2020-06-25 11:30:04 +00:00
err = fmt . Errorf ( "failed to fetch monitor list using clusterID (%s): %w" , clusterID , err )
2019-04-22 21:35:39 +00:00
return
2019-03-02 17:29:52 +00:00
}
return
}
2019-05-31 18:09:24 +00:00
// isLegacyVolumeID checks if passed in volume ID string conforms to volume ID naming scheme used
// by the version 1.0.0 (legacy) of the plugin, and returns true if found to be conforming
func isLegacyVolumeID ( volumeID string ) bool {
// Version 1.0.0 volumeID format: "csi-rbd-vol-" + UUID string
// length: 12 ("csi-rbd-vol-") + 36 (UUID string)
// length check
if len ( volumeID ) != 48 {
return false
}
// Header check
if ! strings . HasPrefix ( volumeID , "csi-rbd-vol-" ) {
return false
}
// Trailer UUID format check
if uuid . Parse ( volumeID [ 12 : ] ) == nil {
return false
}
return true
}
// upadateMons function is used to update the rbdVolume.Monitors for volumes that were provisioned
// using the 1.0.0 version (legacy) of the plugin.
func updateMons ( rbdVol * rbdVolume , options , credentials map [ string ] string ) error {
var ok bool
// read monitors and MonValueFromSecret from options, else check passed in rbdVolume for
// MonValueFromSecret key in credentials
monInSecret := ""
if options != nil {
if rbdVol . Monitors , ok = options [ "monitors" ] ; ! ok {
rbdVol . Monitors = ""
}
if monInSecret , ok = options [ "monValueFromSecret" ] ; ! ok {
monInSecret = ""
}
} else {
monInSecret = rbdVol . MonValueFromSecret
}
// if monitors are present in secrets and we have the credentials, use monitors from the
// credentials overriding monitors from other sources
if monInSecret != "" && credentials != nil {
monsFromSecret , ok := credentials [ monInSecret ]
if ok {
rbdVol . Monitors = monsFromSecret
}
}
if rbdVol . Monitors == "" {
return errors . New ( "either monitors or monValueFromSecret must be set" )
}
return nil
}
2019-08-22 16:57:23 +00:00
func genVolFromVolumeOptions ( ctx context . Context , volOptions , credentials map [ string ] string , disableInUseChecks , isLegacyVolume bool ) ( * rbdVolume , error ) {
2019-03-13 13:46:56 +00:00
var (
2020-02-24 13:19:42 +00:00
ok bool
err error
namePrefix string
encrypted string
2019-03-13 13:46:56 +00:00
)
2019-03-02 17:29:52 +00:00
2018-03-06 22:33:57 +00:00
rbdVol := & rbdVolume { }
rbdVol . Pool , ok = volOptions [ "pool" ]
2018-01-09 18:59:50 +00:00
if ! ok {
2019-03-07 12:56:47 +00:00
return nil , errors . New ( "missing required parameter pool" )
2018-01-09 18:59:50 +00:00
}
2019-03-02 17:29:52 +00:00
2019-09-10 09:56:08 +00:00
rbdVol . DataPool = volOptions [ "dataPool" ]
2020-02-24 13:19:42 +00:00
if namePrefix , ok = volOptions [ "volumeNamePrefix" ] ; ok {
rbdVol . NamePrefix = namePrefix
}
2019-09-10 09:56:08 +00:00
2019-05-31 18:09:24 +00:00
if isLegacyVolume {
err = updateMons ( rbdVol , volOptions , credentials )
if err != nil {
return nil , err
}
} else {
2019-08-22 16:57:23 +00:00
rbdVol . Monitors , rbdVol . ClusterID , err = getMonsAndClusterID ( ctx , volOptions )
2019-05-31 18:09:24 +00:00
if err != nil {
return nil , err
}
2018-01-09 18:59:50 +00:00
}
2019-03-02 17:29:52 +00:00
2019-09-11 07:08:55 +00:00
// if no image features is provided, it results in empty string
2020-01-10 09:09:49 +00:00
// which disable all RBD image features as we expected
2019-09-11 07:08:55 +00:00
imageFeatures , found := volOptions [ "imageFeatures" ]
if found {
arr := strings . Split ( imageFeatures , "," )
for _ , f := range arr {
if ! supportedFeatures . Has ( f ) {
return nil , fmt . Errorf ( "invalid feature %q for volume csi-rbdplugin, supported" +
" features are: %v" , f , supportedFeatures )
2018-07-20 08:46:44 +00:00
}
}
2020-06-18 12:07:21 +00:00
rbdVol . imageFeatureSet = librbd . FeatureSetFromNames ( arr )
2018-01-09 18:59:50 +00:00
}
2019-03-14 00:18:04 +00:00
2019-08-22 16:57:23 +00:00
klog . V ( 3 ) . Infof ( util . Log ( ctx , "setting disableInUseChecks on rbd volume to: %v" ) , disableInUseChecks )
2019-03-14 00:18:04 +00:00
rbdVol . DisableInUseChecks = disableInUseChecks
2018-09-18 14:09:12 +00:00
rbdVol . Mounter , ok = volOptions [ "mounter" ]
if ! ok {
rbdVol . Mounter = rbdDefaultMounter
}
2019-06-01 21:26:42 +00:00
2019-12-13 11:41:32 +00:00
rbdVol . Encrypted = false
2020-01-29 11:44:45 +00:00
encrypted , ok = volOptions [ "encrypted" ]
2019-12-13 11:41:32 +00:00
if ok {
rbdVol . Encrypted , err = strconv . ParseBool ( encrypted )
if err != nil {
return nil , fmt . Errorf (
"invalid value set in 'encrypted': %s (should be \"true\" or \"false\")" , encrypted )
}
2020-01-29 11:44:45 +00:00
if rbdVol . Encrypted {
2020-02-06 16:23:14 +00:00
// deliberately ignore if parsing failed as GetKMS will return default
// implementation of kmsID is empty
kmsID := volOptions [ "encryptionKMSID" ]
rbdVol . KMS , err = util . GetKMS ( kmsID , credentials )
2020-01-29 11:44:45 +00:00
if err != nil {
return nil , fmt . Errorf ( "invalid encryption kms configuration: %s" , err )
}
}
2019-12-13 11:41:32 +00:00
}
2019-06-01 21:26:42 +00:00
return rbdVol , nil
2018-01-09 18:59:50 +00:00
}
2019-03-02 17:29:52 +00:00
2019-08-22 16:57:23 +00:00
func genSnapFromOptions ( ctx context . Context , rbdVol * rbdVolume , snapOptions map [ string ] string ) * rbdSnapshot {
2019-05-31 18:43:38 +00:00
var err error
2019-03-02 17:29:52 +00:00
2018-08-08 05:42:17 +00:00
rbdSnap := & rbdSnapshot { }
2019-05-31 18:43:38 +00:00
rbdSnap . Pool = rbdVol . Pool
2020-01-24 16:26:56 +00:00
rbdSnap . JournalPool = rbdVol . JournalPool
2019-03-02 17:29:52 +00:00
2019-08-22 16:57:23 +00:00
rbdSnap . Monitors , rbdSnap . ClusterID , err = getMonsAndClusterID ( ctx , snapOptions )
2019-03-02 17:29:52 +00:00
if err != nil {
2019-04-22 21:35:39 +00:00
rbdSnap . Monitors = rbdVol . Monitors
rbdSnap . ClusterID = rbdVol . ClusterID
2018-08-09 13:07:00 +00:00
}
2018-08-08 05:42:17 +00:00
2020-02-24 13:19:42 +00:00
if namePrefix , ok := snapOptions [ "snapshotNamePrefix" ] ; ok {
rbdSnap . NamePrefix = namePrefix
}
2019-04-22 21:35:39 +00:00
return rbdSnap
2018-08-08 05:42:17 +00:00
}
2020-06-18 12:07:21 +00:00
// hasSnapshotFeature checks if Layering is enabled for this image
2020-06-18 11:33:06 +00:00
func ( rv * rbdVolume ) hasSnapshotFeature ( ) bool {
2020-06-18 12:07:21 +00:00
return ( uint64 ( rv . imageFeatureSet ) & librbd . FeatureLayering ) == librbd . FeatureLayering
2020-01-07 13:45:52 +00:00
}
2020-06-24 07:06:29 +00:00
func ( rv * rbdVolume ) createSnapshot ( ctx context . Context , pOpts * rbdSnapshot ) error {
2020-05-28 18:39:44 +00:00
klog . V ( 4 ) . Infof ( util . Log ( ctx , "rbd: snap create %s using mon %s" ) , pOpts , pOpts . Monitors )
2020-06-24 07:06:29 +00:00
image , err := rv . open ( )
2019-04-22 21:35:39 +00:00
if err != nil {
2020-06-24 07:06:29 +00:00
return err
2019-02-18 08:22:52 +00:00
}
2020-06-24 07:06:29 +00:00
defer image . Close ( )
2019-02-18 08:22:52 +00:00
2020-06-24 07:06:29 +00:00
_ , err = image . CreateSnapshot ( pOpts . RbdSnapName )
return err
2018-08-08 05:42:17 +00:00
}
2020-06-24 07:06:29 +00:00
func ( rv * rbdVolume ) deleteSnapshot ( ctx context . Context , pOpts * rbdSnapshot ) error {
2020-05-28 18:39:44 +00:00
klog . V ( 4 ) . Infof ( util . Log ( ctx , "rbd: snap rm %s using mon %s" ) , pOpts , pOpts . Monitors )
2020-06-24 07:06:29 +00:00
image , err := rv . open ( )
2019-04-22 21:35:39 +00:00
if err != nil {
2020-06-24 07:06:29 +00:00
return err
2019-04-22 21:35:39 +00:00
}
2020-06-24 07:06:29 +00:00
defer image . Close ( )
2019-04-22 21:35:39 +00:00
2020-06-24 07:06:29 +00:00
snap := image . GetSnapshot ( pOpts . RbdSnapName )
if snap == nil {
2020-06-25 11:30:04 +00:00
return fmt . Errorf ( "snapshot value is nil for %s" , pOpts . RbdSnapName )
2019-04-22 21:35:39 +00:00
}
2020-06-24 07:06:29 +00:00
err = snap . Remove ( )
2020-07-02 21:43:40 +00:00
if errors . Is ( err , librbd . ErrNotFound ) {
2020-06-24 07:06:29 +00:00
return ErrSnapNotFound { snapName : pOpts . RbdSnapName , err : err }
}
return err
2019-04-22 21:35:39 +00:00
}
2020-06-24 07:17:21 +00:00
func ( rv * rbdVolume ) cloneRbdImageFromSnapshot ( ctx context . Context , pSnapOpts * rbdSnapshot ) error {
image := rv . RbdImageName
var err error
klog . V ( 4 ) . Infof ( util . Log ( ctx , "rbd: clone %s %s using mon %s" ) , pSnapOpts , image , rv . Monitors )
2019-04-22 21:35:39 +00:00
2020-06-24 07:17:21 +00:00
options := librbd . NewRbdImageOptions ( )
defer options . Destroy ( )
if rv . imageFeatureSet != 0 {
err = options . SetUint64 ( librbd . RbdImageOptionFeatures , uint64 ( rv . imageFeatureSet ) )
if err != nil {
2020-06-25 11:30:04 +00:00
return fmt . Errorf ( "failed to set image features: %w" , err )
2020-06-24 07:17:21 +00:00
}
}
2018-08-08 05:42:17 +00:00
2020-06-24 07:17:21 +00:00
err = options . SetUint64 ( imageOptionCloneFormat , 2 )
if err != nil {
2020-06-25 11:30:04 +00:00
return fmt . Errorf ( "failed to set image features: %w" , err )
2020-06-24 07:17:21 +00:00
}
err = rv . openIoctx ( )
if err != nil {
2020-06-25 11:30:04 +00:00
return fmt . Errorf ( "failed to get IOContext: %w" , err )
2020-06-24 07:17:21 +00:00
}
2018-08-08 05:42:17 +00:00
2020-06-24 07:17:21 +00:00
err = librbd . CloneImage ( rv . ioctx , pSnapOpts . RbdImageName , pSnapOpts . RbdSnapName , rv . ioctx , rv . RbdImageName , options )
2018-08-08 05:42:17 +00:00
if err != nil {
2020-06-25 11:30:04 +00:00
return fmt . Errorf ( "failed to create rbd clone: %w" , err )
2018-08-08 05:42:17 +00:00
}
return nil
}
2020-06-24 07:43:24 +00:00
// imageInfo strongly typed JSON spec for image info
type imageInfo struct {
ObjectUUID string ` json:"name" `
Size int64 ` json:"size" `
Features [ ] string ` json:"features" `
CreatedAt string ` json:"create_timestamp" `
Parent parentInfo ` json:"parent" `
}
// parentInfo spec for parent volume info
type parentInfo struct {
Image string ` json:"image" `
Pool string ` json:"pool" `
Snapshot string ` json:"snapshost" `
}
// updateVolWithImageInfo updates provided rbdVolume with information from on-disk data
// regarding the same
func ( rv * rbdVolume ) updateVolWithImageInfo ( cr * util . Credentials ) error {
// rbd --format=json info [image-spec | snap-spec]
var imgInfo imageInfo
stdout , stderr , err := util . ExecCommand ( "rbd" ,
"-m" , rv . Monitors ,
"--id" , cr . ID ,
"--keyfile=" + cr . KeyFile ,
"-c" , util . CephConfigPath ,
"--format=" + "json" ,
"info" , rv . String ( ) )
if err != nil {
klog . Errorf ( "failed getting information for image (%s): (%s)" , rv , err )
if strings . Contains ( string ( stderr ) , "rbd: error opening image " + rv . RbdImageName +
": (2) No such file or directory" ) {
return ErrImageNotFound { rv . String ( ) , err }
}
return err
}
err = json . Unmarshal ( stdout , & imgInfo )
if err != nil {
klog . Errorf ( "failed to parse JSON output of image info (%s): (%s)" , rv , err )
return fmt . Errorf ( "unmarshal failed: %+v. raw buffer response: %s" , err , string ( stdout ) )
}
rv . VolSize = imgInfo . Size
rv . ParentName = imgInfo . Parent . Image
tm , err := time . Parse ( time . ANSIC , imgInfo . CreatedAt )
if err != nil {
return err
}
rv . CreatedAt , err = ptypes . TimestampProto ( tm )
return err
}
2019-04-22 21:35:39 +00:00
// getImageInfo queries rbd about the given image and returns its metadata, and returns
// ErrImageNotFound if provided image is not found
2020-01-15 13:06:03 +00:00
func ( rv * rbdVolume ) getImageInfo ( ) error {
image , err := rv . open ( )
if err != nil {
return err
}
defer image . Close ( )
2019-04-22 21:35:39 +00:00
2020-01-15 13:06:03 +00:00
imageInfo , err := image . Stat ( )
if err != nil {
return err
2018-09-21 14:38:50 +00:00
}
2020-01-15 13:06:03 +00:00
// TODO: can rv.VolSize not be a uint64? Or initialize it to -1?
rv . VolSize = int64 ( imageInfo . Size )
2018-09-21 14:38:50 +00:00
2020-01-15 13:06:03 +00:00
features , err := image . GetFeatures ( )
2018-08-08 05:42:17 +00:00
if err != nil {
2020-01-15 13:06:03 +00:00
return err
2018-08-08 05:42:17 +00:00
}
2020-06-18 12:07:21 +00:00
rv . imageFeatureSet = librbd . FeatureSet ( features )
2020-06-24 07:43:24 +00:00
err = rv . updateVolWithImageInfo ( rv . conn . Creds )
if err != nil {
return err
}
2018-08-08 05:42:17 +00:00
2020-01-15 13:06:03 +00:00
return nil
2019-04-22 21:35:39 +00:00
}
/ *
getSnapInfo queries rbd about the snapshots of the given image and returns its metadata , and
returns ErrImageNotFound if provided image is not found , and ErrSnapNotFound if provided snap
is not found in the images snapshot list
* /
2020-06-24 07:14:23 +00:00
func ( rv * rbdVolume ) getSnapInfo ( rbdSnap * rbdSnapshot ) ( librbd . SnapInfo , error ) {
invalidSnap := librbd . SnapInfo { }
image , err := rv . open ( )
if err != nil {
return invalidSnap , err
2018-08-08 05:42:17 +00:00
}
2020-06-24 07:14:23 +00:00
defer image . Close ( )
2018-08-08 05:42:17 +00:00
2020-06-24 07:14:23 +00:00
snaps , err := image . GetSnapshotNames ( )
2019-04-22 21:35:39 +00:00
if err != nil {
2020-06-24 07:14:23 +00:00
return invalidSnap , err
2019-04-22 21:35:39 +00:00
}
for _ , snap := range snaps {
2020-06-24 07:14:23 +00:00
if snap . Name == rbdSnap . RbdSnapName {
2019-04-22 21:35:39 +00:00
return snap , nil
}
}
2020-06-24 07:14:23 +00:00
return invalidSnap , ErrSnapNotFound { rbdSnap . RbdSnapName , fmt . Errorf ( "snap %s not found" , rbdSnap . String ( ) ) }
2018-08-08 05:42:17 +00:00
}
2019-08-03 22:11:28 +00:00
// rbdImageMetadataStash strongly typed JSON spec for stashed RBD image metadata
type rbdImageMetadataStash struct {
Version int ` json:"Version" `
Pool string ` json:"pool" `
ImageName string ` json:"image" `
NbdAccess bool ` json:"accessType" `
2020-01-29 11:44:45 +00:00
Encrypted bool ` json:"encrypted" `
2019-08-03 22:11:28 +00:00
}
// file name in which image metadata is stashed
const stashFileName = "image-meta.json"
2020-05-28 18:39:44 +00:00
// spec returns the image-spec (pool/image) format of the image
func ( ri * rbdImageMetadataStash ) String ( ) string {
return fmt . Sprintf ( "%s/%s" , ri . Pool , ri . ImageName )
}
2019-08-03 22:11:28 +00:00
// stashRBDImageMetadata stashes required fields into the stashFileName at the passed in path, in
// JSON format
func stashRBDImageMetadata ( volOptions * rbdVolume , path string ) error {
var imgMeta = rbdImageMetadataStash {
2020-01-29 11:44:45 +00:00
Version : 2 , // there are no checks for this at present
2019-08-03 22:11:28 +00:00
Pool : volOptions . Pool ,
ImageName : volOptions . RbdImageName ,
2020-01-29 11:44:45 +00:00
Encrypted : volOptions . Encrypted ,
2019-08-03 22:11:28 +00:00
}
imgMeta . NbdAccess = false
if volOptions . Mounter == rbdTonbd && hasNBD {
imgMeta . NbdAccess = true
}
encodedBytes , err := json . Marshal ( imgMeta )
if err != nil {
2020-05-28 18:39:44 +00:00
return fmt . Errorf ( "failed to marshall JSON image metadata for image (%s): (%v)" , volOptions , err )
2019-08-03 22:11:28 +00:00
}
fPath := filepath . Join ( path , stashFileName )
err = ioutil . WriteFile ( fPath , encodedBytes , 0600 )
if err != nil {
2020-05-28 18:39:44 +00:00
return fmt . Errorf ( "failed to stash JSON image metadata for image (%s) at path (%s): (%v)" , volOptions , fPath , err )
2019-08-03 22:11:28 +00:00
}
return nil
}
// lookupRBDImageMetadataStash reads and returns stashed image metadata at passed in path
func lookupRBDImageMetadataStash ( path string ) ( rbdImageMetadataStash , error ) {
var imgMeta rbdImageMetadataStash
fPath := filepath . Join ( path , stashFileName )
Address security concerns reported by 'gosec'
gosec reports several issues, none of them looks very critical. With
this change the following concerns have been addressed:
[pkg/cephfs/nodeserver.go:229] - G302: Expect file permissions to be 0600 or less (Confidence: HIGH, Severity: MEDIUM)
> os.Chmod(targetPath, 0777)
[pkg/cephfs/util.go:39] - G204: Subprocess launched with variable (Confidence: HIGH, Severity: MEDIUM)
> exec.Command(program, args...)
[pkg/rbd/nodeserver.go:156] - G302: Expect file permissions to be 0600 or less (Confidence: HIGH, Severity: MEDIUM)
> os.Chmod(stagingTargetPath, 0777)
[pkg/rbd/nodeserver.go:205] - G302: Expect file permissions to be 0600 or less (Confidence: HIGH, Severity: MEDIUM)
> os.OpenFile(mountPath, os.O_CREATE|os.O_RDWR, 0750)
[pkg/rbd/rbd_util.go:797] - G304: Potential file inclusion via variable (Confidence: HIGH, Severity: MEDIUM)
> ioutil.ReadFile(fPath)
[pkg/util/cephcmds.go:35] - G204: Subprocess launched with variable (Confidence: HIGH, Severity: MEDIUM)
> exec.Command(program, args...)
[pkg/util/credentials.go:47] - G104: Errors unhandled. (Confidence: HIGH, Severity: LOW)
> os.Remove(tmpfile.Name())
[pkg/util/credentials.go:92] - G104: Errors unhandled. (Confidence: HIGH, Severity: LOW)
> os.Remove(cr.KeyFile)
[pkg/util/pidlimit.go:74] - G304: Potential file inclusion via variable (Confidence: HIGH, Severity: MEDIUM)
> os.Open(pidsMax)
URL: https://github.com/securego/gosec
Signed-off-by: Niels de Vos <ndevos@redhat.com>
2019-08-30 10:23:10 +00:00
encodedBytes , err := ioutil . ReadFile ( fPath ) // #nosec - intended reading from fPath
2019-08-03 22:11:28 +00:00
if err != nil {
if ! os . IsNotExist ( err ) {
return imgMeta , fmt . Errorf ( "failed to read stashed JSON image metadata from path (%s): (%v)" , fPath , err )
}
return imgMeta , ErrMissingStash { err }
}
err = json . Unmarshal ( encodedBytes , & imgMeta )
if err != nil {
return imgMeta , fmt . Errorf ( "failed to unmarshall stashed JSON image metadata from path (%s): (%v)" , fPath , err )
}
return imgMeta , nil
}
// cleanupRBDImageMetadataStash cleans up any stashed metadata at passed in path
func cleanupRBDImageMetadataStash ( path string ) error {
fPath := filepath . Join ( path , stashFileName )
if err := os . Remove ( fPath ) ; err != nil {
return fmt . Errorf ( "failed to cleanup stashed JSON data (%s): (%v)" , fPath , err )
}
return nil
}
2019-11-27 12:14:31 +00:00
// resizeRBDImage resizes the given volume to new size
2020-02-26 09:35:18 +00:00
func resizeRBDImage ( rbdVol * rbdVolume , cr * util . Credentials ) error {
2019-11-27 12:14:31 +00:00
var output [ ] byte
mon := rbdVol . Monitors
2020-02-26 09:35:18 +00:00
volSzMiB := fmt . Sprintf ( "%dM" , util . RoundOffVolSize ( rbdVol . VolSize ) )
2019-11-27 12:14:31 +00:00
2020-05-28 18:39:44 +00:00
args := [ ] string { "resize" , rbdVol . String ( ) , "--size" , volSzMiB , "--id" , cr . ID , "-m" , mon , "--keyfile=" + cr . KeyFile }
2019-11-27 12:14:31 +00:00
output , err := execCommand ( "rbd" , args )
if err != nil {
2020-06-25 11:30:04 +00:00
return fmt . Errorf ( "failed to resize rbd image (%w), command output: %s" , err , string ( output ) )
2019-11-27 12:14:31 +00:00
}
return nil
}
2019-12-13 11:41:32 +00:00
2020-01-09 10:31:07 +00:00
func ( rv * rbdVolume ) GetMetadata ( key string ) ( string , error ) {
2020-05-12 14:56:08 +00:00
image , err := rv . open ( )
2019-12-13 11:41:32 +00:00
if err != nil {
2020-05-12 14:56:08 +00:00
return "" , err
2019-12-13 11:41:32 +00:00
}
2020-01-09 10:31:07 +00:00
defer image . Close ( )
return image . GetMetadata ( key )
}
2019-12-13 11:41:32 +00:00
2020-01-09 10:31:07 +00:00
func ( rv * rbdVolume ) SetMetadata ( key , value string ) error {
2020-05-12 14:56:08 +00:00
image , err := rv . open ( )
2020-01-09 10:31:07 +00:00
if err != nil {
2020-05-12 14:56:08 +00:00
return err
2020-01-09 10:31:07 +00:00
}
defer image . Close ( )
return image . SetMetadata ( key , value )
}
// checkRbdImageEncrypted verifies if rbd image was encrypted when created
func ( rv * rbdVolume ) checkRbdImageEncrypted ( ctx context . Context ) ( string , error ) {
value , err := rv . GetMetadata ( encryptionMetaKey )
if err != nil {
2020-05-28 18:39:44 +00:00
klog . Errorf ( util . Log ( ctx , "checking image %s encrypted state metadata failed: %s" ) , rv , err )
2020-01-09 10:31:07 +00:00
return "" , err
}
encrypted := strings . TrimSpace ( value )
2020-05-28 18:39:44 +00:00
klog . V ( 4 ) . Infof ( util . Log ( ctx , "image %s encrypted state metadata reports %q" ) , rv , encrypted )
2020-01-09 10:31:07 +00:00
return encrypted , nil
}
2019-12-13 11:41:32 +00:00
2020-01-09 10:31:07 +00:00
func ( rv * rbdVolume ) ensureEncryptionMetadataSet ( status string ) error {
err := rv . SetMetadata ( encryptionMetaKey , status )
2019-12-13 11:41:32 +00:00
if err != nil {
2020-05-28 18:39:44 +00:00
return fmt . Errorf ( "failed to save encryption status for %s: %v" , rv , err )
2019-12-13 11:41:32 +00:00
}
return nil
}
2020-06-25 13:00:31 +00:00
// SnapshotInfo holds snapshots details
type snapshotInfo struct {
ID int ` json:"id" `
Name string ` json:"name" `
Size int64 ` json:"size" `
Protected string ` json:"protected" `
Timestamp string ` json:"timestamp" `
Namespace struct {
Type string ` json:"type" `
OriginalName string ` json:"original_name" `
} ` json:"namespace" `
}
// TODO: use go-ceph once https://github.com/ceph/go-ceph/issues/300 is available in a release.
func ( rv * rbdVolume ) listSnapshots ( ctx context . Context , cr * util . Credentials ) ( [ ] snapshotInfo , error ) {
// rbd snap ls <image> --pool=<pool-name> --all --format=json
var snapInfo [ ] snapshotInfo
stdout , stderr , err := util . ExecCommand ( "rbd" ,
"-m" , rv . Monitors ,
"--id" , cr . ID ,
"--keyfile=" + cr . KeyFile ,
"-c" , util . CephConfigPath ,
"--format=" + "json" ,
"snap" ,
"ls" ,
"--all" , rv . String ( ) )
if err != nil {
klog . Errorf ( util . Log ( ctx , "failed getting information for image (%s): (%s)" ) , rv , err )
if strings . Contains ( string ( stderr ) , "rbd: error opening image " + rv . RbdImageName +
": (2) No such file or directory" ) {
return snapInfo , ErrImageNotFound { rv . String ( ) , err }
}
return snapInfo , err
}
err = json . Unmarshal ( stdout , & snapInfo )
if err != nil {
klog . Errorf ( util . Log ( ctx , "failed to parse JSON output of snapshot info (%s)" ) , err )
return snapInfo , fmt . Errorf ( "unmarshal failed: %w. raw buffer response: %s" , err , string ( stdout ) )
}
return snapInfo , nil
}