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
"errors"
"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"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/util/mount"
"k8s.io/kubernetes/pkg/util/node"
"k8s.io/kubernetes/pkg/volume"
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
}
type RBDUtil struct { }
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-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
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
used , statusErr := util . rbdStatus ( & b )
if statusErr == nil && ! 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 {
2017-08-22 13:49:21 +00:00
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 ... )
2017-02-16 20:55:55 +00:00
glog . Infof ( "remove orphaned locker %s from client %s: err %v, output: %s" , lockInfo [ 1 ] , lockInfo [ 0 ] , err , string ( cmd ) )
}
}
}
}
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 ... )
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
}
}
// remove a lock: rbd lock remove
2017-08-22 13:49:21 +00:00
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 ... )
2015-06-29 19:07:52 +00:00
}
if err == nil {
//lock is acquired
break
}
}
return err
}
2016-03-23 05:12:21 +00:00
func ( util * RBDUtil ) persistRBD ( rbd rbdMounter , mnt string ) error {
2015-06-29 19:07:52 +00:00
file := path . Join ( mnt , "rbd.json" )
fp , err := os . Create ( file )
if err != nil {
return fmt . Errorf ( "rbd: create err %s/%s" , file , err )
}
defer fp . Close ( )
encoder := json . NewEncoder ( fp )
if err = encoder . Encode ( rbd ) ; err != nil {
return fmt . Errorf ( "rbd: encode err: %v." , err )
}
return nil
}
2016-03-23 05:12:21 +00:00
func ( util * RBDUtil ) loadRBD ( mounter * rbdMounter , mnt string ) error {
2015-06-29 19:07:52 +00:00
file := path . Join ( mnt , "rbd.json" )
fp , err := os . Open ( file )
if err != nil {
return fmt . Errorf ( "rbd: open err %s/%s" , file , err )
}
defer fp . Close ( )
decoder := json . NewDecoder ( fp )
2016-03-23 05:12:21 +00:00
if err = decoder . Decode ( mounter ) ; err != nil {
2015-06-29 19:07:52 +00:00
return fmt . Errorf ( "rbd: decode err: %v." , err )
}
2015-08-10 22:57:05 +00:00
2015-06-29 19:07:52 +00:00
return nil
}
2016-03-23 05:12:21 +00:00
func ( util * RBDUtil ) fencing ( b rbdMounter ) error {
2015-06-29 19:07:52 +00:00
// no need to fence readOnly
2015-10-30 20:25:36 +00:00
if ( & b ) . GetAttributes ( ) . ReadOnly {
2015-06-29 19:07:52 +00:00
return nil
}
2015-07-24 09:20:42 +00:00
return util . rbdLock ( b , true )
2015-06-29 19:07:52 +00:00
}
2016-03-23 05:12:21 +00:00
func ( util * RBDUtil ) defencing ( c rbdUnmounter ) error {
2015-06-29 19:07:52 +00:00
// no need to fence readOnly
2015-07-24 09:20:42 +00:00
if c . ReadOnly {
2015-06-29 19:07:52 +00:00
return nil
}
2016-03-23 05:12:21 +00:00
return util . rbdLock ( * c . rbdMounter , false )
2015-04-07 17:22:23 +00:00
}
2016-03-23 05:12:21 +00:00
func ( util * RBDUtil ) AttachDisk ( b rbdMounter ) 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-02-16 20:55:55 +00:00
// create mount point
globalPDPath := b . manager . MakeGlobalPDName ( * b . rbd )
notMnt , err := b . mounter . IsLikelyNotMountPoint ( globalPDPath )
// in the first time, the path shouldn't exist and IsLikelyNotMountPoint is expected to get NotExist
if err != nil && ! os . IsNotExist ( err ) {
return fmt . Errorf ( "rbd: %s failed to check mountpoint" , globalPDPath )
}
if ! notMnt {
return nil
}
if err = os . MkdirAll ( globalPDPath , 0750 ) ; err != nil {
return fmt . Errorf ( "rbd: failed to mkdir %s, error" , globalPDPath )
}
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
// fence off other mappers
if err = util . fencing ( b ) ; err != nil {
2017-06-28 02:22:13 +00:00
return rbdErrors ( err , fmt . Errorf ( "rbd: failed to lock image %s (maybe locked by other nodes), error %v" , b . Image , err ) )
2017-02-16 20:55:55 +00:00
}
// rbd lock remove needs ceph and image config
// but kubelet doesn't get them from apiserver during teardown
// so persit rbd config so upon disk detach, rbd lock can be removed
// since rbd json is persisted in the same local directory that is used as rbd mountpoint later,
// the json file remains invisible during rbd mount and thus won't be removed accidentally.
util . persistRBD ( b , globalPDPath )
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
}
2016-01-13 20:57:36 +00:00
glog . V ( 1 ) . Infof ( "rbd: map error %v %s" , err , string ( output ) )
2015-04-07 17:22:23 +00:00
}
2015-11-13 00:32:10 +00:00
if err != nil {
2016-01-13 20:57:36 +00:00
return fmt . Errorf ( "rbd: map failed %v %s" , err , string ( output ) )
2015-11-13 00:32:10 +00:00
}
devicePath , found = waitForPath ( b . Pool , b . Image , 10 )
if ! found {
return errors . New ( "Could not map image: Timeout after 10s" )
}
2017-08-22 07:10:01 +00:00
glog . V ( 3 ) . Infof ( "rbd: successfully map image %s/%s to %s" , b . Pool , b . Image , devicePath )
2015-04-07 17:22:23 +00:00
}
2015-06-29 19:07:52 +00:00
2017-02-16 20:55:55 +00:00
// mount it
2015-11-05 21:49:40 +00:00
if err = b . mounter . FormatAndMount ( devicePath , globalPDPath , b . fsType , nil ) ; err != nil {
2015-07-24 09:20:42 +00:00
err = fmt . Errorf ( "rbd: failed to mount rbd volume %s [%s] to %s, error %v" , devicePath , b . fsType , globalPDPath , err )
2015-04-07 17:22:23 +00:00
}
2017-08-22 07:10:01 +00:00
glog . V ( 3 ) . Infof ( "rbd: successfully mount image %s/%s at %s" , b . Pool , b . Image , globalPDPath )
2015-04-07 17:22:23 +00:00
return err
}
2016-03-23 05:12:21 +00:00
func ( util * RBDUtil ) DetachDisk ( c rbdUnmounter , mntPath string ) error {
2015-07-24 09:20:42 +00:00
device , cnt , err := mount . GetDeviceNameFromMount ( c . mounter , mntPath )
2015-04-07 17:22:23 +00:00
if err != nil {
return fmt . Errorf ( "rbd detach disk: failed to get device from mnt: %s\nError: %v" , mntPath , err )
}
2015-07-24 09:20:42 +00:00
if err = c . mounter . Unmount ( mntPath ) ; err != nil {
2015-04-07 17:22:23 +00:00
return fmt . Errorf ( "rbd detach disk: failed to umount: %s\nError: %v" , mntPath , err )
}
2017-08-22 07:10:01 +00:00
glog . V ( 3 ) . Infof ( "rbd: successfully umount mountpoint %s" , mntPath )
2015-04-07 17:22:23 +00:00
// if device is no longer used, see if can unmap
if cnt <= 1 {
// rbd unmap
2017-08-22 13:49:21 +00:00
_ , err = c . exec . Run ( "rbd" , "unmap" , device )
2015-04-07 17:22:23 +00:00
if err != nil {
2017-06-28 02:22:13 +00:00
return rbdErrors ( err , fmt . Errorf ( "rbd: failed to unmap device %s:Error: %v" , device , err ) )
2015-04-07 17:22:23 +00:00
}
2015-06-29 19:07:52 +00:00
// load ceph and image/pool info to remove fencing
2016-03-23 05:12:21 +00:00
if err := util . loadRBD ( c . rbdMounter , mntPath ) ; err == nil {
2015-06-29 19:07:52 +00:00
// remove rbd lock
2015-07-24 09:20:42 +00:00
util . defencing ( c )
2015-06-29 19:07:52 +00:00
}
2017-08-22 07:10:01 +00:00
glog . V ( 3 ) . Infof ( "rbd: successfully unmap device %s" , device )
2015-04-07 17:22:23 +00:00
}
return nil
}
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
2016-08-23 17:22:35 +00:00
found , err := util . rbdStatus ( p . rbdMounter )
if err != nil {
return err
}
if found {
glog . Info ( "rbd is still being used " , p . rbdMounter . Image )
return fmt . Errorf ( "rbd %s is still being used" , p . rbdMounter . Image )
}
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
}
}
return err
}
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.
2016-08-23 17:22:35 +00:00
func ( util * RBDUtil ) rbdStatus ( b * rbdMounter ) ( bool , error ) {
var err error
var output string
var cmd [ ] byte
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
//
2016-08-23 17:22:35 +00:00
glog . V ( 4 ) . Infof ( "rbd: status %s using mon %s, pool %s id %s key %s" , b . Image , mon , b . Pool , b . adminId , b . adminSecret )
2017-08-22 13:49:21 +00:00
cmd , err = b . exec . Run ( "rbd" ,
"status" , b . Image , "--pool" , b . Pool , "-m" , mon , "--id" , b . adminId , "--key=" + b . adminSecret )
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
return false , 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 {
return false , err
}
if strings . Contains ( output , imageWatcherStr ) {
glog . V ( 4 ) . Infof ( "rbd: watchers on %s: %s" , b . Image , output )
return true , nil
} else {
glog . Warningf ( "rbd: no watchers on %s" , b . Image )
return false , nil
}
2016-08-23 17:22:35 +00:00
}