2015-04-07 17:22:23 +00:00
/ *
2016-06-03 00:25:58 +00:00
Copyright 2014 The Kubernetes Authors .
2015-04-07 17:22:23 +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 .
* /
//
// utility functions to setup rbd volume
// mainly implement diskManager interface
//
package rbd
import (
2015-06-29 19:07:52 +00:00
"encoding/json"
2015-04-07 17:22:23 +00:00
"fmt"
2015-09-16 12:52:22 +00:00
"io/ioutil"
2015-04-07 17:22:23 +00:00
"math/rand"
"os"
"path"
2015-09-16 12:52:22 +00:00
"regexp"
2015-04-07 17:22:23 +00:00
"strings"
"time"
2015-08-05 22:05:17 +00:00
"github.com/golang/glog"
2017-06-22 17:25:57 +00:00
"k8s.io/api/core/v1"
2017-10-24 04:16:12 +00:00
fileutil "k8s.io/kubernetes/pkg/util/file"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/util/node"
"k8s.io/kubernetes/pkg/volume"
2017-08-30 16:52:11 +00:00
volutil "k8s.io/kubernetes/pkg/volume/util"
2015-04-07 17:22:23 +00:00
)
2016-08-23 17:22:35 +00:00
const (
imageWatcherStr = "watcher="
2017-02-16 20:55:55 +00:00
kubeLockMagic = "kubelet_lock_magic_"
2017-06-28 02:22:13 +00:00
rbdCmdErr = "executable file not found in $PATH"
2016-08-23 17:22:35 +00:00
)
2015-09-16 12:52:22 +00:00
// search /sys/bus for rbd device that matches given pool and image
func getDevFromImageAndPool ( pool , image string ) ( string , bool ) {
// /sys/bus/rbd/devices/X/name and /sys/bus/rbd/devices/X/pool
sys_path := "/sys/bus/rbd/devices"
if dirs , err := ioutil . ReadDir ( sys_path ) ; err == nil {
for _ , f := range dirs {
// pool and name format:
// see rbd_pool_show() and rbd_name_show() at
// https://github.com/torvalds/linux/blob/master/drivers/block/rbd.c
name := f . Name ( )
// first match pool, then match name
2017-08-22 13:49:21 +00:00
poolFile := path . Join ( sys_path , name , "pool" )
poolBytes , err := ioutil . ReadFile ( poolFile )
2015-09-16 12:52:22 +00:00
if err != nil {
2017-08-22 13:49:21 +00:00
glog . V ( 4 ) . Infof ( "Error reading %s: %v" , poolFile , err )
2015-09-16 12:52:22 +00:00
continue
}
2017-08-22 13:49:21 +00:00
if strings . TrimSpace ( string ( poolBytes ) ) != pool {
glog . V ( 4 ) . Infof ( "Device %s is not %q: %q" , name , pool , string ( poolBytes ) )
continue
}
imgFile := path . Join ( sys_path , name , "name" )
imgBytes , err := ioutil . ReadFile ( imgFile )
if err != nil {
glog . V ( 4 ) . Infof ( "Error reading %s: %v" , imgFile , err )
continue
}
if strings . TrimSpace ( string ( imgBytes ) ) != image {
glog . V ( 4 ) . Infof ( "Device %s is not %q: %q" , name , image , string ( imgBytes ) )
2015-09-16 12:52:22 +00:00
continue
}
// found a match, check if device exists
devicePath := "/dev/rbd" + name
if _ , err := os . Lstat ( devicePath ) ; err == nil {
return devicePath , true
}
}
}
return "" , false
}
2015-04-07 17:22:23 +00:00
// stat a path, if not exists, retry maxRetries times
2015-09-16 12:52:22 +00:00
func waitForPath ( pool , image string , maxRetries int ) ( string , bool ) {
2015-04-07 17:22:23 +00:00
for i := 0 ; i < maxRetries ; i ++ {
2015-09-16 12:52:22 +00:00
devicePath , found := getDevFromImageAndPool ( pool , image )
if found {
return devicePath , true
2015-04-07 17:22:23 +00:00
}
2016-12-21 09:52:01 +00:00
if i == maxRetries - 1 {
break
}
2015-04-07 17:22:23 +00:00
time . Sleep ( time . Second )
}
2015-09-16 12:52:22 +00:00
return "" , false
2015-04-07 17:22:23 +00:00
}
// make a directory like /var/lib/kubelet/plugins/kubernetes.io/pod/rbd/pool-image-image
func makePDNameInternal ( host volume . VolumeHost , pool string , image string ) string {
2015-06-29 17:07:22 +00:00
return path . Join ( host . GetPluginDir ( rbdPluginName ) , "rbd" , pool + "-image-" + image )
2015-04-07 17:22:23 +00:00
}
2017-08-30 16:52:11 +00:00
// RBDUtil implements diskManager interface.
2015-04-07 17:22:23 +00:00
type RBDUtil struct { }
2017-08-30 16:52:11 +00:00
var _ diskManager = & RBDUtil { }
2015-04-07 17:22:23 +00:00
func ( util * RBDUtil ) MakeGlobalPDName ( rbd rbd ) string {
2015-06-29 19:07:52 +00:00
return makePDNameInternal ( rbd . plugin . host , rbd . Pool , rbd . Image )
}
2017-08-30 16:52:11 +00:00
2017-06-28 02:22:13 +00:00
func rbdErrors ( runErr , resultErr error ) error {
if runErr . Error ( ) == rbdCmdErr {
return fmt . Errorf ( "rbd: rbd cmd not found" )
}
return resultErr
}
2015-06-29 19:07:52 +00:00
2017-08-30 16:52:11 +00:00
// rbdLock acquires a lock on image if lock is true, otherwise releases if a
// lock is found on image.
2016-03-23 05:12:21 +00:00
func ( util * RBDUtil ) rbdLock ( b rbdMounter , lock bool ) error {
2015-06-29 19:07:52 +00:00
var err error
var output , locker string
var cmd [ ] byte
var secret_opt [ ] string
2015-07-24 09:20:42 +00:00
if b . Secret != "" {
secret_opt = [ ] string { "--key=" + b . Secret }
2015-06-29 19:07:52 +00:00
} else {
2015-07-24 09:20:42 +00:00
secret_opt = [ ] string { "-k" , b . Keyring }
2015-06-29 19:07:52 +00:00
}
2017-02-16 20:55:55 +00:00
if len ( b . adminId ) == 0 {
b . adminId = b . Id
}
if len ( b . adminSecret ) == 0 {
b . adminSecret = b . Secret
}
2015-06-29 19:07:52 +00:00
// construct lock id using host name and a magic prefix
2017-02-16 20:55:55 +00:00
lock_id := kubeLockMagic + node . GetHostname ( "" )
2015-06-29 19:07:52 +00:00
2015-07-24 09:20:42 +00:00
l := len ( b . Mon )
2015-06-29 19:07:52 +00:00
// avoid mount storm, pick a host randomly
start := rand . Int ( ) % l
// iterate all hosts until mount succeeds.
for i := start ; i < start + l ; i ++ {
2015-07-24 09:20:42 +00:00
mon := b . Mon [ i % l ]
2015-06-29 19:07:52 +00:00
// cmd "rbd lock list" serves two purposes:
// for fencing, check if lock already held for this host
// this edge case happens if host crashes in the middle of acquiring lock and mounting rbd
// for defencing, get the locker name, something like "client.1234"
2017-08-22 13:49:21 +00:00
args := [ ] string { "lock" , "list" , b . Image , "--pool" , b . Pool , "--id" , b . Id , "-m" , mon }
args = append ( args , secret_opt ... )
cmd , err = b . exec . Run ( "rbd" , args ... )
2015-06-29 19:07:52 +00:00
output = string ( cmd )
2017-02-16 20:55:55 +00:00
glog . Infof ( "lock list output %q" , output )
2015-06-29 19:07:52 +00:00
if err != nil {
continue
}
if lock {
// check if lock is already held for this host by matching lock_id and rbd lock id
if strings . Contains ( output , lock_id ) {
// this host already holds the lock, exit
glog . V ( 1 ) . Infof ( "rbd: lock already held for %s" , lock_id )
return nil
}
2017-02-16 20:55:55 +00:00
// clean up orphaned lock if no watcher on the image
2017-10-29 08:35:28 +00:00
used , rbdOutput , statusErr := util . rbdStatus ( & b )
if statusErr != nil {
return fmt . Errorf ( "rbdStatus failed error %v, rbd output: %v" , statusErr , rbdOutput )
}
if used {
// this image is already used by a node other than this node
return fmt . Errorf ( "rbd image: %s/%s is already used by a node other than this node, rbd output: %v" , b . Image , b . Pool , output )
}
// best effort clean up orphaned locked if not used
re := regexp . MustCompile ( "client.* " + kubeLockMagic + ".*" )
locks := re . FindAllStringSubmatch ( output , - 1 )
for _ , v := range locks {
if len ( v ) > 0 {
lockInfo := strings . Split ( v [ 0 ] , " " )
if len ( lockInfo ) > 2 {
args := [ ] string { "lock" , "remove" , b . Image , lockInfo [ 1 ] , lockInfo [ 0 ] , "--pool" , b . Pool , "--id" , b . Id , "-m" , mon }
args = append ( args , secret_opt ... )
cmd , err = b . exec . Run ( "rbd" , args ... )
glog . Infof ( "remove orphaned locker %s from client %s: err %v, rbd output: %s" , lockInfo [ 1 ] , lockInfo [ 0 ] , err , string ( cmd ) )
2017-02-16 20:55:55 +00:00
}
}
}
2015-06-29 19:07:52 +00:00
// hold a lock: rbd lock add
2017-08-22 13:49:21 +00:00
args := [ ] string { "lock" , "add" , b . Image , lock_id , "--pool" , b . Pool , "--id" , b . Id , "-m" , mon }
args = append ( args , secret_opt ... )
cmd , err = b . exec . Run ( "rbd" , args ... )
2017-08-30 16:52:11 +00:00
if err == nil {
glog . V ( 4 ) . Infof ( "rbd: successfully add lock (locker_id: %s) on image: %s/%s with id %s mon %s" , lock_id , b . Pool , b . Image , b . Id , mon )
}
2015-06-29 19:07:52 +00:00
} else {
// defencing, find locker name
ind := strings . LastIndex ( output , lock_id ) - 1
for i := ind ; i >= 0 ; i -- {
if output [ i ] == '\n' {
locker = output [ ( i + 1 ) : ind ]
break
}
}
2017-08-30 16:52:11 +00:00
// remove a lock if found: rbd lock remove
if len ( locker ) > 0 {
args := [ ] string { "lock" , "remove" , b . Image , lock_id , locker , "--pool" , b . Pool , "--id" , b . Id , "-m" , mon }
args = append ( args , secret_opt ... )
cmd , err = b . exec . Run ( "rbd" , args ... )
if err == nil {
glog . V ( 4 ) . Infof ( "rbd: successfully remove lock (locker_id: %s) on image: %s/%s with id %s mon %s" , lock_id , b . Pool , b . Image , b . Id , mon )
}
}
2015-06-29 19:07:52 +00:00
}
if err == nil {
2017-08-30 16:52:11 +00:00
// break if operation succeeds
2015-06-29 19:07:52 +00:00
break
}
}
return err
}
2017-08-30 16:52:11 +00:00
// AttachDisk attaches the disk on the node.
// If Volume is not read-only, acquire a lock on image first.
func ( util * RBDUtil ) AttachDisk ( b rbdMounter ) ( string , error ) {
2015-04-07 17:22:23 +00:00
var err error
2016-01-13 20:57:36 +00:00
var output [ ] byte
2015-09-16 12:52:22 +00:00
2017-08-30 16:52:11 +00:00
globalPDPath := util . MakeGlobalPDName ( * b . rbd )
if pathExists , pathErr := volutil . PathExists ( globalPDPath ) ; pathErr != nil {
return "" , fmt . Errorf ( "Error checking if path exists: %v" , pathErr )
} else if ! pathExists {
if err := os . MkdirAll ( globalPDPath , 0750 ) ; err != nil {
return "" , err
}
2017-02-16 20:55:55 +00:00
}
2015-09-16 12:52:22 +00:00
devicePath , found := waitForPath ( b . Pool , b . Image , 1 )
if ! found {
2017-08-22 13:49:21 +00:00
_ , err = b . exec . Run ( "modprobe" , "rbd" )
2015-04-07 17:22:23 +00:00
if err != nil {
2017-05-18 05:46:21 +00:00
glog . Warningf ( "rbd: failed to load rbd kernel module:%v" , err )
2015-04-07 17:22:23 +00:00
}
2017-02-16 20:55:55 +00:00
2017-10-24 04:16:12 +00:00
// Currently, we don't acquire advisory lock on image, but for backward
// compatibility, we need to check if the image is being used by nodes running old kubelet.
2017-10-29 08:35:28 +00:00
found , rbdOutput , err := util . rbdStatus ( & b )
2017-10-24 04:16:12 +00:00
if err != nil {
2017-10-29 08:35:28 +00:00
return "" , fmt . Errorf ( "error: %v, rbd output: %v" , err , rbdOutput )
2017-10-24 04:16:12 +00:00
}
if found {
2017-10-29 08:35:28 +00:00
glog . Infof ( "rbd image %s/%s is still being used " , b . Pool , b . Image )
return "" , fmt . Errorf ( "rbd image %s/%s is still being used. rbd output: %s" , b . Pool , b . Image , rbdOutput )
2017-02-16 20:55:55 +00:00
}
2015-04-07 17:22:23 +00:00
// rbd map
2015-07-24 09:20:42 +00:00
l := len ( b . Mon )
2015-04-07 17:22:23 +00:00
// avoid mount storm, pick a host randomly
start := rand . Int ( ) % l
// iterate all hosts until mount succeeds.
for i := start ; i < start + l ; i ++ {
2015-07-24 09:20:42 +00:00
mon := b . Mon [ i % l ]
2015-04-07 17:22:23 +00:00
glog . V ( 1 ) . Infof ( "rbd: map mon %s" , mon )
2015-07-24 09:20:42 +00:00
if b . Secret != "" {
2017-08-22 13:49:21 +00:00
output , err = b . exec . Run ( "rbd" ,
"map" , b . Image , "--pool" , b . Pool , "--id" , b . Id , "-m" , mon , "--key=" + b . Secret )
2015-04-07 17:22:23 +00:00
} else {
2017-08-22 13:49:21 +00:00
output , err = b . exec . Run ( "rbd" ,
"map" , b . Image , "--pool" , b . Pool , "--id" , b . Id , "-m" , mon , "-k" , b . Keyring )
2015-04-07 17:22:23 +00:00
}
if err == nil {
break
}
2017-10-29 08:35:28 +00:00
glog . V ( 1 ) . Infof ( "rbd: map error %v, rbd output: %s" , err , string ( output ) )
2015-04-07 17:22:23 +00:00
}
2015-11-13 00:32:10 +00:00
if err != nil {
2017-10-29 08:35:28 +00:00
return "" , fmt . Errorf ( "rbd: map failed %v, rbd output: %s" , err , string ( output ) )
2015-11-13 00:32:10 +00:00
}
devicePath , found = waitForPath ( b . Pool , b . Image , 10 )
if ! found {
2017-10-29 08:35:28 +00:00
return "" , fmt . Errorf ( "Could not map image %s/%s, Timeout after 10s" , b . Pool , b . Image )
2015-11-13 00:32:10 +00:00
}
2015-04-07 17:22:23 +00:00
}
2017-10-29 08:35:28 +00:00
return devicePath , nil
2015-04-07 17:22:23 +00:00
}
2017-08-30 16:52:11 +00:00
// DetachDisk detaches the disk from the node.
// It detaches device from the node if device is provided, and removes the lock
// if there is persisted RBD info under deviceMountPath.
func ( util * RBDUtil ) DetachDisk ( plugin * rbdPlugin , deviceMountPath string , device string ) error {
2017-10-29 08:35:28 +00:00
if len ( device ) == 0 {
return fmt . Errorf ( "DetachDisk failed , device is empty" )
}
// rbd unmap
exec := plugin . host . GetExec ( plugin . GetPluginName ( ) )
output , err := exec . Run ( "rbd" , "unmap" , device )
if err != nil {
return rbdErrors ( err , fmt . Errorf ( "rbd: failed to unmap device %s, error %v, rbd output: %v" , device , err , output ) )
2015-04-07 17:22:23 +00:00
}
2017-10-29 08:35:28 +00:00
glog . V ( 3 ) . Infof ( "rbd: successfully unmap device %s" , device )
2017-10-24 04:16:12 +00:00
// Currently, we don't persist rbd info on the disk, but for backward
// compatbility, we need to clean it if found.
rbdFile := path . Join ( deviceMountPath , "rbd.json" )
exists , err := fileutil . FileExists ( rbdFile )
if err != nil {
return err
}
if exists {
glog . V ( 3 ) . Infof ( "rbd: old rbd.json is found under %s, cleaning it" , deviceMountPath )
err = util . cleanOldRBDFile ( plugin , rbdFile )
if err != nil {
glog . Errorf ( "rbd: failed to clean %s" , rbdFile )
return err
}
glog . V ( 3 ) . Infof ( "rbd: successfully remove %s" , rbdFile )
}
return nil
}
// cleanOldRBDFile read rbd info from rbd.json file and removes lock if found.
// At last, it removes rbd.json file.
func ( util * RBDUtil ) cleanOldRBDFile ( plugin * rbdPlugin , rbdFile string ) error {
2017-08-30 16:52:11 +00:00
mounter := & rbdMounter {
// util.rbdLock needs it to run command.
rbd : newRBD ( "" , "" , "" , "" , false , plugin , util ) ,
}
2017-10-24 04:16:12 +00:00
fp , err := os . Open ( rbdFile )
2017-08-30 16:52:11 +00:00
if err != nil {
2017-10-24 04:16:12 +00:00
return fmt . Errorf ( "rbd: open err %s/%s" , rbdFile , err )
}
defer fp . Close ( )
decoder := json . NewDecoder ( fp )
if err = decoder . Decode ( mounter ) ; err != nil {
return fmt . Errorf ( "rbd: decode err: %v." , err )
}
if err != nil {
glog . Errorf ( "failed to load rbd info from %s: %v" , rbdFile , err )
2017-08-30 16:52:11 +00:00
return err
}
// remove rbd lock if found
// the disk is not attached to this node anymore, so the lock on image
// for this node can be removed safely
2017-10-24 04:16:12 +00:00
err = util . rbdLock ( * mounter , false )
if err == nil {
os . Remove ( rbdFile )
}
return err
2015-04-07 17:22:23 +00:00
}
2016-08-18 20:50:59 +00:00
2017-10-23 20:59:34 +00:00
func ( util * RBDUtil ) CreateImage ( p * rbdVolumeProvisioner ) ( r * v1 . RBDPersistentVolumeSource , size int , err error ) {
2016-11-28 11:08:09 +00:00
var output [ ] byte
2016-11-18 20:58:56 +00:00
capacity := p . options . PVC . Spec . Resources . Requests [ v1 . ResourceName ( v1 . ResourceStorage ) ]
2016-10-12 10:22:01 +00:00
volSizeBytes := capacity . Value ( )
2016-08-18 20:50:59 +00:00
// convert to MB that rbd defaults on
2016-08-23 17:22:35 +00:00
sz := int ( volume . RoundUpSize ( volSizeBytes , 1024 * 1024 ) )
2016-08-18 20:50:59 +00:00
volSz := fmt . Sprintf ( "%d" , sz )
// rbd create
l := len ( p . rbdMounter . Mon )
// pick a mon randomly
start := rand . Int ( ) % l
// iterate all monitors until create succeeds.
for i := start ; i < start + l ; i ++ {
mon := p . Mon [ i % l ]
2017-06-16 12:11:35 +00:00
if p . rbdMounter . imageFormat == rbdImageFormat2 {
glog . V ( 4 ) . Infof ( "rbd: create %s size %s format %s (features: %s) using mon %s, pool %s id %s key %s" , p . rbdMounter . Image , volSz , p . rbdMounter . imageFormat , p . rbdMounter . imageFeatures , mon , p . rbdMounter . Pool , p . rbdMounter . adminId , p . rbdMounter . adminSecret )
} else {
glog . V ( 4 ) . Infof ( "rbd: create %s size %s format %s using mon %s, pool %s id %s key %s" , p . rbdMounter . Image , volSz , p . rbdMounter . imageFormat , mon , p . rbdMounter . Pool , p . rbdMounter . adminId , p . rbdMounter . adminSecret )
}
args := [ ] string { "create" , p . rbdMounter . Image , "--size" , volSz , "--pool" , p . rbdMounter . Pool , "--id" , p . rbdMounter . adminId , "-m" , mon , "--key=" + p . rbdMounter . adminSecret , "--image-format" , p . rbdMounter . imageFormat }
2017-06-16 16:29:58 +00:00
if p . rbdMounter . imageFormat == rbdImageFormat2 {
// if no image features is provided, it results in empty string
// which disable all RBD image format 2 features as we expected
features := strings . Join ( p . rbdMounter . imageFeatures , "," )
args = append ( args , "--image-feature" , features )
2017-06-16 12:11:35 +00:00
}
2017-08-22 13:49:21 +00:00
output , err = p . exec . Run ( "rbd" , args ... )
2016-08-18 20:50:59 +00:00
if err == nil {
break
} else {
2016-08-23 17:22:35 +00:00
glog . Warningf ( "failed to create rbd image, output %v" , string ( output ) )
2016-08-18 20:50:59 +00:00
}
}
if err != nil {
2016-11-28 11:08:09 +00:00
return nil , 0 , fmt . Errorf ( "failed to create rbd image: %v, command output: %s" , err , string ( output ) )
2016-08-18 20:50:59 +00:00
}
2017-10-23 20:59:34 +00:00
return & v1 . RBDPersistentVolumeSource {
2016-08-18 20:50:59 +00:00
CephMonitors : p . rbdMounter . Mon ,
RBDImage : p . rbdMounter . Image ,
RBDPool : p . rbdMounter . Pool ,
} , sz , nil
}
func ( util * RBDUtil ) DeleteImage ( p * rbdVolumeDeleter ) error {
var output [ ] byte
2017-10-29 08:35:28 +00:00
found , rbdOutput , err := util . rbdStatus ( p . rbdMounter )
2016-08-23 17:22:35 +00:00
if err != nil {
2017-10-29 08:35:28 +00:00
return fmt . Errorf ( "error %v, rbd output: %v" , err , rbdOutput )
2016-08-23 17:22:35 +00:00
}
if found {
glog . Info ( "rbd is still being used " , p . rbdMounter . Image )
2017-10-29 08:35:28 +00:00
return fmt . Errorf ( "rbd image %s/%s is still being used, rbd output: %v" , p . rbdMounter . Pool , p . rbdMounter . Image , rbdOutput )
2016-08-23 17:22:35 +00:00
}
2016-08-18 20:50:59 +00:00
// rbd rm
l := len ( p . rbdMounter . Mon )
// pick a mon randomly
start := rand . Int ( ) % l
// iterate all monitors until rm succeeds.
for i := start ; i < start + l ; i ++ {
mon := p . rbdMounter . Mon [ i % l ]
2016-08-23 17:22:35 +00:00
glog . V ( 4 ) . Infof ( "rbd: rm %s using mon %s, pool %s id %s key %s" , p . rbdMounter . Image , mon , p . rbdMounter . Pool , p . rbdMounter . adminId , p . rbdMounter . adminSecret )
2017-08-22 13:49:21 +00:00
output , err = p . exec . Run ( "rbd" ,
"rm" , p . rbdMounter . Image , "--pool" , p . rbdMounter . Pool , "--id" , p . rbdMounter . adminId , "-m" , mon , "--key=" + p . rbdMounter . adminSecret )
2016-08-18 20:50:59 +00:00
if err == nil {
return nil
} else {
2016-11-28 11:08:09 +00:00
glog . Errorf ( "failed to delete rbd image: %v, command output: %s" , err , string ( output ) )
2016-08-18 20:50:59 +00:00
}
}
2017-10-29 08:35:28 +00:00
return fmt . Errorf ( "error %v, rbd output: %v" , err , string ( output ) )
2016-08-18 20:50:59 +00:00
}
2016-08-23 17:22:35 +00:00
2017-10-24 04:47:37 +00:00
// rbdStatus runs `rbd status` command to check if there is watcher on the image.
2017-10-29 08:35:28 +00:00
func ( util * RBDUtil ) rbdStatus ( b * rbdMounter ) ( bool , string , error ) {
2016-08-23 17:22:35 +00:00
var err error
var output string
var cmd [ ] byte
2017-10-24 04:16:12 +00:00
// If we don't have admin id/secret (e.g. attaching), fallback to user id/secret.
id := b . adminId
secret := b . adminSecret
if id == "" {
id = b . Id
secret = b . Secret
}
2016-08-23 17:22:35 +00:00
l := len ( b . Mon )
start := rand . Int ( ) % l
2017-10-24 04:47:37 +00:00
// iterate all hosts until rbd command succeeds.
2016-08-23 17:22:35 +00:00
for i := start ; i < start + l ; i ++ {
mon := b . Mon [ i % l ]
// cmd "rbd status" list the rbd client watch with the following output:
2017-10-24 04:47:37 +00:00
//
// # there is a watcher (exit=0)
2016-08-23 17:22:35 +00:00
// Watchers:
// watcher=10.16.153.105:0/710245699 client.14163 cookie=1
2017-10-24 04:47:37 +00:00
//
// # there is no watcher (exit=0)
// Watchers: none
//
// Otherwise, exit is non-zero, for example:
//
// # image does not exist (exit=2)
// rbd: error opening image kubernetes-dynamic-pvc-<UUID>: (2) No such file or directory
//
2017-10-24 04:16:12 +00:00
glog . V ( 4 ) . Infof ( "rbd: status %s using mon %s, pool %s id %s key %s" , b . Image , mon , b . Pool , id , secret )
2017-08-22 13:49:21 +00:00
cmd , err = b . exec . Run ( "rbd" ,
2017-10-24 04:16:12 +00:00
"status" , b . Image , "--pool" , b . Pool , "-m" , mon , "--id" , id , "--key=" + secret )
2016-08-23 17:22:35 +00:00
output = string ( cmd )
2017-10-24 04:47:37 +00:00
// break if command succeeds
if err == nil {
break
2016-08-23 17:22:35 +00:00
}
2017-10-24 04:47:37 +00:00
if err . Error ( ) == rbdCmdErr {
glog . Errorf ( "rbd cmd not found" )
// fail fast if command not found
2017-10-29 08:35:28 +00:00
return false , output , err
2016-08-23 17:22:35 +00:00
}
}
2017-10-24 04:47:37 +00:00
// If command never succeed, returns its last error.
if err != nil {
2017-10-29 08:35:28 +00:00
return false , output , err
2017-10-24 04:47:37 +00:00
}
if strings . Contains ( output , imageWatcherStr ) {
glog . V ( 4 ) . Infof ( "rbd: watchers on %s: %s" , b . Image , output )
2017-10-29 08:35:28 +00:00
return true , output , nil
2017-10-24 04:47:37 +00:00
} else {
glog . Warningf ( "rbd: no watchers on %s" , b . Image )
2017-10-29 08:35:28 +00:00
return false , output , nil
2017-10-24 04:47:37 +00:00
}
2016-08-23 17:22:35 +00:00
}