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"
"os/exec"
"strings"
"time"
2018-03-06 22:33:57 +00:00
"github.com/golang/glog"
2018-10-09 10:08:56 +00:00
"github.com/pkg/errors"
2018-07-20 08:46:44 +00:00
"k8s.io/apimachinery/pkg/util/sets"
2018-03-06 22:33:57 +00:00
"k8s.io/kubernetes/pkg/util/keymutex"
2018-01-09 18:59:50 +00:00
)
const (
imageWatcherStr = "watcher="
rbdImageFormat1 = "1"
rbdImageFormat2 = "2"
imageSizeStr = "size "
sizeDivStr = " MB in"
kubeLockMagic = "kubelet_lock_magic_"
// 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"
2018-01-09 18:59:50 +00:00
)
2018-03-06 22:33:57 +00:00
type rbdVolume struct {
2018-09-21 14:38:50 +00:00
VolName string ` json:"volName" `
VolID string ` json:"volID" `
Monitors string ` json:"monitors" `
MonValueFromSecret string ` json:"monValueFromSecret" `
Pool string ` json:"pool" `
ImageFormat string ` json:"imageFormat" `
ImageFeatures string ` json:"imageFeatures" `
VolSize int64 ` json:"volSize" `
AdminId string ` json:"adminId" `
UserId string ` json:"userId" `
Mounter string ` json:"mounter" `
2018-01-09 18:59:50 +00:00
}
2018-08-08 05:42:17 +00:00
type rbdSnapshot struct {
2018-09-21 14:38:50 +00:00
SourceVolumeID string ` json:"sourceVolumeID" `
VolName string ` json:"volName" `
SnapName string ` json:"snapName" `
SnapID string ` json:"sanpID" `
Monitors string ` json:"monitors" `
MonValueFromSecret string ` json:"monValueFromSecret" `
Pool string ` json:"pool" `
CreatedAt int64 ` json:"createdAt" `
SizeBytes int64 ` json:"sizeBytes" `
AdminId string ` json:"adminId" `
UserId string ` json:"userId" `
2018-08-08 05:42:17 +00:00
}
2018-07-20 08:46:44 +00:00
var (
2018-10-17 12:52:45 +00:00
// serializes operations based on "<rbd pool>/<rbd image>" as key
2018-07-20 08:46:44 +00:00
attachdetachMutex = keymutex . NewKeyMutex ( )
2018-10-17 12:52:45 +00:00
// serializes operations based on "volume name" as key
volumeNameMutex = keymutex . NewKeyMutex ( )
// serializes operations based on "volume id" as key
volumeIDMutex = keymutex . NewKeyMutex ( )
// serializes operations based on "snapshot name" as key
snapshotNameMutex = keymutex . NewKeyMutex ( )
// serializes operations based on "snapshot id" as key
snapshotIDMutex = keymutex . NewKeyMutex ( )
// serializes operations based on "mount target path" as key
targetPathMutex = keymutex . NewKeyMutex ( )
2018-07-20 08:46:44 +00:00
supportedFeatures = sets . NewString ( "layering" )
)
2018-01-09 18:59:50 +00:00
2018-03-06 22:33:57 +00:00
func getRBDKey ( id string , credentials map [ string ] string ) ( string , error ) {
if key , ok := credentials [ id ] ; ok {
return key , nil
}
return "" , fmt . Errorf ( "RBD key for ID: %s not found" , id )
}
2018-09-21 14:38:50 +00:00
func getMon ( pOpts * rbdVolume , credentials map [ string ] string ) ( string , error ) {
mon := pOpts . Monitors
if len ( mon ) == 0 {
// if mons are set in secret, retrieve them
if len ( pOpts . MonValueFromSecret ) == 0 {
// yet another sanity check
2019-01-15 18:26:50 +00:00
return "" , errors . New ( "either monitors or monValueFromSecret must be set" )
2018-09-21 14:38:50 +00:00
}
2019-01-15 18:26:50 +00:00
val , ok := credentials [ pOpts . MonValueFromSecret ]
if ! ok {
2018-09-21 14:38:50 +00:00
return "" , fmt . Errorf ( "mon data %s is not set in secret" , pOpts . MonValueFromSecret )
}
2019-01-15 18:26:50 +00:00
mon = val
2018-09-21 14:38:50 +00:00
}
return mon , nil
}
2018-01-09 18:59:50 +00:00
// CreateImage creates a new ceph image with provision and volume options.
2018-08-09 13:07:00 +00:00
func createRBDImage ( pOpts * rbdVolume , volSz int , adminId string , credentials map [ string ] string ) error {
2018-01-09 18:59:50 +00:00
var output [ ] byte
2018-09-21 14:38:50 +00:00
mon , err := getMon ( pOpts , credentials )
if err != nil {
return err
}
2018-01-16 01:52:28 +00:00
image := pOpts . VolName
2018-01-09 18:59:50 +00:00
volSzGB := fmt . Sprintf ( "%dG" , volSz )
2018-08-09 13:07:00 +00:00
key , err := getRBDKey ( adminId , credentials )
2018-03-06 22:33:57 +00:00
if err != nil {
return err
}
2018-01-09 18:59:50 +00:00
if pOpts . ImageFormat == rbdImageFormat2 {
2018-08-09 13:07:00 +00:00
glog . V ( 4 ) . Infof ( "rbd: create %s size %s format %s (features: %s) using mon %s, pool %s id %s key %s" , image , volSzGB , pOpts . ImageFormat , pOpts . ImageFeatures , mon , pOpts . Pool , adminId , key )
2018-01-09 18:59:50 +00:00
} else {
2018-08-09 13:07:00 +00:00
glog . V ( 4 ) . Infof ( "rbd: create %s size %s format %s using mon %s, pool %s id %s key %s" , image , volSzGB , pOpts . ImageFormat , mon , pOpts . Pool , adminId , key )
2018-01-09 18:59:50 +00:00
}
2018-08-09 13:07:00 +00:00
args := [ ] string { "create" , image , "--size" , volSzGB , "--pool" , pOpts . Pool , "--id" , adminId , "-m" , mon , "--key=" + key , "--image-format" , pOpts . ImageFormat }
2018-01-09 18:59:50 +00:00
if pOpts . ImageFormat == rbdImageFormat2 {
2018-07-20 08:46:44 +00:00
args = append ( args , "--image-feature" , pOpts . ImageFeatures )
2018-01-09 18:59:50 +00:00
}
output , err = execCommand ( "rbd" , args )
if err != nil {
2018-10-09 10:08:56 +00:00
return errors . Wrapf ( err , "failed to create rbd image, command output: %s" , string ( output ) )
2018-01-09 18:59:50 +00:00
}
return nil
}
// rbdStatus checks if there is watcher on the image.
// It returns true if there is a watcher onthe image, otherwise returns false.
2018-08-09 13:07:00 +00:00
func rbdStatus ( pOpts * rbdVolume , userId string , credentials map [ string ] string ) ( bool , string , error ) {
2018-01-09 18:59:50 +00:00
var output string
var cmd [ ] byte
2018-03-06 22:33:57 +00:00
image := pOpts . VolName
2018-01-09 18:59:50 +00:00
// If we don't have admin id/secret (e.g. attaching), fallback to user id/secret.
2018-08-09 13:07:00 +00:00
key , err := getRBDKey ( userId , credentials )
2018-03-06 22:33:57 +00:00
if err != nil {
return false , "" , err
2018-01-09 18:59:50 +00:00
}
2018-09-21 14:38:50 +00:00
mon , err := getMon ( pOpts , credentials )
if err != nil {
return false , "" , err
}
glog . V ( 4 ) . Infof ( "rbd: status %s using mon %s, pool %s id %s key %s" , image , mon , pOpts . Pool , userId , key )
args := [ ] string { "status" , image , "--pool" , pOpts . Pool , "-m" , mon , "--id" , userId , "--key=" + key }
2018-01-09 18:59:50 +00:00
cmd , err = execCommand ( "rbd" , args )
output = string ( cmd )
if err , ok := err . ( * exec . Error ) ; ok {
if err . Err == exec . ErrNotFound {
glog . Errorf ( "rbd cmd not found" )
// 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 ) {
glog . V ( 4 ) . Infof ( "rbd: watchers on %s: %s" , image , output )
return true , output , nil
}
2019-01-15 18:26:50 +00:00
glog . Warningf ( "rbd: no watchers on %s" , image )
return false , output , nil
2018-01-09 18:59:50 +00:00
}
// DeleteImage deletes a ceph image with provision and volume options.
2018-08-09 13:07:00 +00:00
func deleteRBDImage ( pOpts * rbdVolume , adminId string , credentials map [ string ] string ) error {
2018-01-09 18:59:50 +00:00
var output [ ] byte
2018-03-06 22:33:57 +00:00
image := pOpts . VolName
2018-08-09 13:07:00 +00:00
found , _ , err := rbdStatus ( pOpts , adminId , credentials )
2018-01-09 18:59:50 +00:00
if err != nil {
return err
}
if found {
glog . Info ( "rbd is still being used " , image )
return fmt . Errorf ( "rbd %s is still being used" , image )
}
2018-08-09 13:07:00 +00:00
key , err := getRBDKey ( adminId , credentials )
2018-03-06 22:33:57 +00:00
if err != nil {
return err
2018-01-09 18:59:50 +00:00
}
2018-09-21 14:38:50 +00:00
mon , err := getMon ( pOpts , credentials )
if err != nil {
return err
}
2018-01-09 18:59:50 +00:00
2018-09-21 14:38:50 +00:00
glog . V ( 4 ) . Infof ( "rbd: rm %s using mon %s, pool %s id %s key %s" , image , mon , pOpts . Pool , adminId , key )
args := [ ] string { "rm" , image , "--pool" , pOpts . Pool , "--id" , adminId , "-m" , mon , "--key=" + key }
2018-01-09 18:59:50 +00:00
output , err = execCommand ( "rbd" , args )
if err == nil {
return nil
}
glog . Errorf ( "failed to delete rbd image: %v, command output: %s" , err , string ( output ) )
return err
}
func execCommand ( command string , args [ ] string ) ( [ ] byte , error ) {
cmd := exec . Command ( command , args ... )
return cmd . CombinedOutput ( )
}
2018-03-06 22:33:57 +00:00
func getRBDVolumeOptions ( volOptions map [ string ] string ) ( * rbdVolume , error ) {
2018-01-09 18:59:50 +00:00
var ok bool
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-01-15 18:26:50 +00:00
return nil , errors . New ( "Missing required parameter pool" )
2018-01-09 18:59:50 +00:00
}
2018-03-06 22:33:57 +00:00
rbdVol . Monitors , ok = volOptions [ "monitors" ]
2018-01-09 18:59:50 +00:00
if ! ok {
2018-09-21 14:38:50 +00:00
// if mons are not set in options, check if they are set in secret
if rbdVol . MonValueFromSecret , ok = volOptions [ "monValueFromSecret" ] ; ! ok {
2019-01-15 18:26:50 +00:00
return nil , errors . New ( "Either monitors or monValueFromSecret must be set" )
2018-09-21 14:38:50 +00:00
}
2018-01-09 18:59:50 +00:00
}
2018-03-06 22:33:57 +00:00
rbdVol . ImageFormat , ok = volOptions [ "imageFormat" ]
2018-01-18 19:13:08 +00:00
if ! ok {
2018-07-20 08:46:44 +00:00
rbdVol . ImageFormat = rbdImageFormat2
}
if rbdVol . ImageFormat == rbdImageFormat2 {
// if no image features is provided, it results in empty string
// which disable all RBD image format 2 features as we expected
imageFeatures , ok := volOptions [ "imageFeatures" ]
if ok {
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 )
}
}
rbdVol . ImageFeatures = imageFeatures
}
2018-01-09 18:59:50 +00:00
}
2018-08-09 13:07:00 +00:00
rbdVol . AdminId , ok = volOptions [ "adminid" ]
if ! ok {
rbdVol . AdminId = rbdDefaultAdminId
}
rbdVol . UserId , ok = volOptions [ "userid" ]
if ! ok {
rbdVol . UserId = rbdDefaultUserId
}
2018-09-18 14:09:12 +00:00
rbdVol . Mounter , ok = volOptions [ "mounter" ]
if ! ok {
rbdVol . Mounter = rbdDefaultMounter
}
2018-03-06 22:33:57 +00:00
return rbdVol , nil
2018-01-09 18:59:50 +00:00
}
2018-08-08 05:42:17 +00:00
func getRBDSnapshotOptions ( snapOptions map [ string ] string ) ( * rbdSnapshot , error ) {
var ok bool
rbdSnap := & rbdSnapshot { }
rbdSnap . Pool , ok = snapOptions [ "pool" ]
if ! ok {
2019-01-15 18:26:50 +00:00
return nil , errors . New ( "Missing required parameter pool" )
2018-08-08 05:42:17 +00:00
}
rbdSnap . Monitors , ok = snapOptions [ "monitors" ]
if ! ok {
2018-09-21 14:38:50 +00:00
// if mons are not set in options, check if they are set in secret
if rbdSnap . MonValueFromSecret , ok = snapOptions [ "monValueFromSecret" ] ; ! ok {
2019-01-15 18:26:50 +00:00
return nil , errors . New ( "Either monitors or monValueFromSecret must be set" )
2018-09-21 14:38:50 +00:00
}
2018-08-08 05:42:17 +00:00
}
2018-08-09 13:07:00 +00:00
rbdSnap . AdminId , ok = snapOptions [ "adminid" ]
if ! ok {
rbdSnap . AdminId = rbdDefaultAdminId
}
rbdSnap . UserId , ok = snapOptions [ "userid" ]
if ! ok {
rbdSnap . UserId = rbdDefaultUserId
}
2018-08-08 05:42:17 +00:00
return rbdSnap , nil
}
2018-08-09 13:07:13 +00:00
func hasSnapshotFeature ( imageFeatures string ) bool {
arr := strings . Split ( imageFeatures , "," )
for _ , f := range arr {
if f == "layering" {
return true
}
}
return false
}
2018-08-09 13:06:51 +00:00
func getRBDVolumeByID ( volumeID string ) ( * rbdVolume , error ) {
2018-03-06 22:33:57 +00:00
if rbdVol , ok := rbdVolumes [ volumeID ] ; ok {
return rbdVol , nil
}
2018-08-09 13:06:51 +00:00
return nil , fmt . Errorf ( "volume id %s does not exit in the volumes list" , volumeID )
2018-03-06 22:33:57 +00:00
}
2018-08-09 13:06:51 +00:00
func getRBDVolumeByName ( volName string ) ( * rbdVolume , error ) {
2018-03-06 22:33:57 +00:00
for _ , rbdVol := range rbdVolumes {
if rbdVol . VolName == volName {
return rbdVol , nil
}
}
2018-08-09 13:06:51 +00:00
return nil , fmt . Errorf ( "volume name %s does not exit in the volumes list" , volName )
2018-03-06 22:33:57 +00:00
}
2018-08-08 05:42:17 +00:00
2018-08-09 13:06:51 +00:00
func getRBDSnapshotByName ( snapName string ) ( * rbdSnapshot , error ) {
2018-08-08 05:42:17 +00:00
for _ , rbdSnap := range rbdSnapshots {
if rbdSnap . SnapName == snapName {
return rbdSnap , nil
}
}
2018-08-09 13:06:51 +00:00
return nil , fmt . Errorf ( "snapshot name %s does not exit in the snapshots list" , snapName )
2018-08-08 05:42:17 +00:00
}
2018-09-21 14:38:50 +00:00
func getSnapMon ( pOpts * rbdSnapshot , credentials map [ string ] string ) ( string , error ) {
mon := pOpts . Monitors
if len ( mon ) == 0 {
// if mons are set in secret, retrieve them
if len ( pOpts . MonValueFromSecret ) == 0 {
// yet another sanity check
2019-01-15 18:26:50 +00:00
return "" , errors . New ( "either monitors or monValueFromSecret must be set" )
2018-09-21 14:38:50 +00:00
}
2019-01-15 18:26:50 +00:00
val , ok := credentials [ pOpts . MonValueFromSecret ]
if ! ok {
2018-09-21 14:38:50 +00:00
return "" , fmt . Errorf ( "mon data %s is not set in secret" , pOpts . MonValueFromSecret )
}
2019-01-15 18:26:50 +00:00
mon = val
2018-09-21 14:38:50 +00:00
}
return mon , nil
}
2018-08-09 13:07:00 +00:00
func protectSnapshot ( pOpts * rbdSnapshot , adminId string , credentials map [ string ] string ) error {
2018-08-08 05:42:17 +00:00
var output [ ] byte
image := pOpts . VolName
snapID := pOpts . SnapID
2018-08-09 13:07:00 +00:00
key , err := getRBDKey ( adminId , credentials )
2018-08-08 05:42:17 +00:00
if err != nil {
return err
}
2018-09-21 14:38:50 +00:00
mon , err := getSnapMon ( pOpts , credentials )
if err != nil {
return err
}
glog . V ( 4 ) . Infof ( "rbd: snap protect %s using mon %s, pool %s id %s key %s" , image , mon , pOpts . Pool , adminId , key )
2018-08-09 13:07:00 +00:00
args := [ ] string { "snap" , "protect" , "--pool" , pOpts . Pool , "--snap" , snapID , image , "--id" , adminId , "-m" , mon , "--key=" + key }
2018-08-08 05:42:17 +00:00
output , err = execCommand ( "rbd" , args )
if err != nil {
2018-10-09 10:08:56 +00:00
return errors . Wrapf ( err , "failed to protect snapshot, command output: %s" , string ( output ) )
2018-08-08 05:42:17 +00:00
}
return nil
}
2018-08-09 13:07:00 +00:00
func createSnapshot ( pOpts * rbdSnapshot , adminId string , credentials map [ string ] string ) error {
2018-08-08 05:42:17 +00:00
var output [ ] byte
2018-09-21 14:38:50 +00:00
mon , err := getSnapMon ( pOpts , credentials )
if err != nil {
return err
}
2018-08-08 05:42:17 +00:00
image := pOpts . VolName
snapID := pOpts . SnapID
2018-08-09 13:07:00 +00:00
key , err := getRBDKey ( adminId , credentials )
2018-08-08 05:42:17 +00:00
if err != nil {
return err
}
2018-09-21 14:38:50 +00:00
glog . V ( 4 ) . Infof ( "rbd: snap create %s using mon %s, pool %s id %s key %s" , image , mon , pOpts . Pool , adminId , key )
2018-08-09 13:07:00 +00:00
args := [ ] string { "snap" , "create" , "--pool" , pOpts . Pool , "--snap" , snapID , image , "--id" , adminId , "-m" , mon , "--key=" + key }
2018-08-08 05:42:17 +00:00
output , err = execCommand ( "rbd" , args )
if err != nil {
2018-10-09 10:08:56 +00:00
return errors . Wrapf ( err , "failed to create snapshot, command output: %s" , string ( output ) )
2018-08-08 05:42:17 +00:00
}
return nil
}
2018-08-09 13:07:00 +00:00
func unprotectSnapshot ( pOpts * rbdSnapshot , adminId string , credentials map [ string ] string ) error {
2018-08-08 05:42:17 +00:00
var output [ ] byte
2018-09-21 14:38:50 +00:00
mon , err := getSnapMon ( pOpts , credentials )
if err != nil {
return err
}
2018-08-08 05:42:17 +00:00
image := pOpts . VolName
snapID := pOpts . SnapID
2018-08-09 13:07:00 +00:00
key , err := getRBDKey ( adminId , credentials )
2018-08-08 05:42:17 +00:00
if err != nil {
return err
}
2018-09-21 14:38:50 +00:00
glog . V ( 4 ) . Infof ( "rbd: snap unprotect %s using mon %s, pool %s id %s key %s" , image , mon , pOpts . Pool , adminId , key )
2018-08-09 13:07:00 +00:00
args := [ ] string { "snap" , "unprotect" , "--pool" , pOpts . Pool , "--snap" , snapID , image , "--id" , adminId , "-m" , mon , "--key=" + key }
2018-08-08 05:42:17 +00:00
output , err = execCommand ( "rbd" , args )
if err != nil {
2018-10-09 10:08:56 +00:00
return errors . Wrapf ( err , "failed to unprotect snapshot, command output: %s" , string ( output ) )
2018-08-08 05:42:17 +00:00
}
return nil
}
2018-08-09 13:07:00 +00:00
func deleteSnapshot ( pOpts * rbdSnapshot , adminId string , credentials map [ string ] string ) error {
2018-08-08 05:42:17 +00:00
var output [ ] byte
2018-09-21 14:38:50 +00:00
mon , err := getSnapMon ( pOpts , credentials )
if err != nil {
return err
}
2018-08-08 05:42:17 +00:00
image := pOpts . VolName
snapID := pOpts . SnapID
2018-08-09 13:07:00 +00:00
key , err := getRBDKey ( adminId , credentials )
2018-08-08 05:42:17 +00:00
if err != nil {
return err
}
2018-09-21 14:38:50 +00:00
glog . V ( 4 ) . Infof ( "rbd: snap rm %s using mon %s, pool %s id %s key %s" , image , mon , pOpts . Pool , adminId , key )
2018-08-09 13:07:00 +00:00
args := [ ] string { "snap" , "rm" , "--pool" , pOpts . Pool , "--snap" , snapID , image , "--id" , adminId , "-m" , mon , "--key=" + key }
2018-08-08 05:42:17 +00:00
output , err = execCommand ( "rbd" , args )
if err != nil {
2018-10-09 10:08:56 +00:00
return errors . Wrapf ( err , "failed to delete snapshot, command output: %s" , string ( output ) )
2018-08-08 05:42:17 +00:00
}
return nil
}
2018-08-09 13:07:00 +00:00
func restoreSnapshot ( pVolOpts * rbdVolume , pSnapOpts * rbdSnapshot , adminId string , credentials map [ string ] string ) error {
2018-08-08 05:42:17 +00:00
var output [ ] byte
2018-09-21 14:38:50 +00:00
mon , err := getMon ( pVolOpts , credentials )
if err != nil {
return err
}
2018-08-08 05:42:17 +00:00
image := pVolOpts . VolName
snapID := pSnapOpts . SnapID
2018-08-09 13:07:00 +00:00
key , err := getRBDKey ( adminId , credentials )
2018-08-08 05:42:17 +00:00
if err != nil {
return err
}
2018-09-21 14:38:50 +00:00
glog . V ( 4 ) . Infof ( "rbd: clone %s using mon %s, pool %s id %s key %s" , image , mon , pVolOpts . Pool , adminId , key )
2018-08-09 13:07:00 +00:00
args := [ ] string { "clone" , pSnapOpts . Pool + "/" + pSnapOpts . VolName + "@" + snapID , pVolOpts . Pool + "/" + image , "--id" , adminId , "-m" , mon , "--key=" + key }
2018-08-08 05:42:17 +00:00
output , err = execCommand ( "rbd" , args )
if err != nil {
2018-10-09 10:08:56 +00:00
return errors . Wrapf ( err , "failed to restore snapshot, command output: %s" , string ( output ) )
2018-08-08 05:42:17 +00:00
}
return nil
}