mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 02:33:34 +00:00
vendor updates
This commit is contained in:
352
vendor/k8s.io/kubernetes/pkg/volume/rbd/rbd.go
generated
vendored
352
vendor/k8s.io/kubernetes/pkg/volume/rbd/rbd.go
generated
vendored
@ -18,6 +18,9 @@ package rbd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
dstrings "strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
@ -32,7 +35,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/util/strings"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
volutil "k8s.io/kubernetes/pkg/volume/util"
|
||||
"k8s.io/kubernetes/pkg/volume/util/volumehelper"
|
||||
"k8s.io/kubernetes/pkg/volume/util/volumepathhandler"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -55,6 +58,7 @@ var _ volume.DeletableVolumePlugin = &rbdPlugin{}
|
||||
var _ volume.ProvisionableVolumePlugin = &rbdPlugin{}
|
||||
var _ volume.AttachableVolumePlugin = &rbdPlugin{}
|
||||
var _ volume.ExpandableVolumePlugin = &rbdPlugin{}
|
||||
var _ volume.BlockVolumePlugin = &rbdPlugin{}
|
||||
|
||||
const (
|
||||
rbdPluginName = "kubernetes.io/rbd"
|
||||
@ -81,7 +85,7 @@ func (plugin *rbdPlugin) GetPluginName() string {
|
||||
}
|
||||
|
||||
func (plugin *rbdPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
|
||||
mon, err := getVolumeSourceMonitors(spec)
|
||||
pool, err := getVolumeSourcePool(spec)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -92,16 +96,12 @@ func (plugin *rbdPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
|
||||
|
||||
return fmt.Sprintf(
|
||||
"%v:%v",
|
||||
mon,
|
||||
pool,
|
||||
img), nil
|
||||
}
|
||||
|
||||
func (plugin *rbdPlugin) CanSupport(spec *volume.Spec) bool {
|
||||
if (spec.Volume != nil && spec.Volume.RBD == nil) || (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.RBD == nil) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
return (spec.Volume != nil && spec.Volume.RBD != nil) || (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.RBD != nil)
|
||||
}
|
||||
|
||||
func (plugin *rbdPlugin) RequiresRemount() bool {
|
||||
@ -244,7 +244,7 @@ func (plugin *rbdPlugin) createMounterFromVolumeSpecAndPod(spec *volume.Spec, po
|
||||
if kubeClient == nil {
|
||||
return nil, fmt.Errorf("Cannot get kube client")
|
||||
}
|
||||
secrets, err := kubeClient.Core().Secrets(secretNs).Get(secretName, metav1.GetOptions{})
|
||||
secrets, err := kubeClient.CoreV1().Secrets(secretNs).Get(secretName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Couldn't get secret %v/%v err: %v", secretNs, secretName, err)
|
||||
return nil, err
|
||||
@ -327,7 +327,7 @@ func (plugin *rbdPlugin) newMounterInternal(spec *volume.Spec, podUID types.UID,
|
||||
Keyring: keyring,
|
||||
Secret: secret,
|
||||
fsType: fstype,
|
||||
mountOptions: volume.MountOptionFromSpec(spec),
|
||||
mountOptions: volutil.MountOptionFromSpec(spec),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -346,17 +346,184 @@ func (plugin *rbdPlugin) newUnmounterInternal(volName string, podUID types.UID,
|
||||
}
|
||||
|
||||
func (plugin *rbdPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
|
||||
mounter := plugin.host.GetMounter(plugin.GetPluginName())
|
||||
pluginDir := plugin.host.GetPluginDir(plugin.GetPluginName())
|
||||
sourceName, err := mounter.GetDeviceNameFromMount(mountPath, pluginDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := dstrings.Split(sourceName, "-image-")
|
||||
if len(s) != 2 {
|
||||
// The mountPath parameter is the volume mount path for a specific pod, its format
|
||||
// is /var/lib/kubelet/pods/{podUID}/volumes/{volumePluginName}/{volumeName}.
|
||||
// mounter.GetDeviceNameFromMount will find the device path(such as /dev/rbd0) by
|
||||
// mountPath first, and then try to find the global device mount path from the mounted
|
||||
// path list of this device. sourceName is extracted from this global device mount path.
|
||||
// mounter.GetDeviceNameFromMount expects the global device mount path conforms to canonical
|
||||
// format: /var/lib/kubelet/plugins/kubernetes.io/rbd/mounts/{pool}-image-{image}.
|
||||
// If this assertion failed, it means that the global device mount path is created by
|
||||
// the deprecated format: /var/lib/kubelet/plugins/kubernetes.io/rbd/rbd/{pool}-image-{image}.
|
||||
// So we will try to check whether this old style global device mount path exist or not.
|
||||
// If existed, extract the sourceName from this old style path, otherwise return an error.
|
||||
glog.V(3).Infof("SourceName %s wrong, fallback to old format", sourceName)
|
||||
sourceName, err = plugin.getDeviceNameFromOldMountPath(mounter, mountPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s = dstrings.Split(sourceName, "-image-")
|
||||
if len(s) != 2 {
|
||||
return nil, fmt.Errorf("sourceName %s wrong, should be pool+\"-image-\"+imageName", sourceName)
|
||||
}
|
||||
}
|
||||
rbdVolume := &v1.Volume{
|
||||
Name: volumeName,
|
||||
VolumeSource: v1.VolumeSource{
|
||||
RBD: &v1.RBDVolumeSource{
|
||||
CephMonitors: []string{},
|
||||
RBDPool: s[0],
|
||||
RBDImage: s[1],
|
||||
},
|
||||
},
|
||||
}
|
||||
return volume.NewSpecFromVolume(rbdVolume), nil
|
||||
}
|
||||
|
||||
func (plugin *rbdPlugin) ConstructBlockVolumeSpec(podUID types.UID, volumeName, mapPath string) (*volume.Spec, error) {
|
||||
pluginDir := plugin.host.GetVolumeDevicePluginDir(rbdPluginName)
|
||||
blkutil := volumepathhandler.NewBlockVolumePathHandler()
|
||||
|
||||
globalMapPathUUID, err := blkutil.FindGlobalMapPathUUIDFromPod(pluginDir, mapPath, podUID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
glog.V(5).Infof("globalMapPathUUID: %v, err: %v", globalMapPathUUID, err)
|
||||
globalMapPath := filepath.Dir(globalMapPathUUID)
|
||||
if len(globalMapPath) == 1 {
|
||||
return nil, fmt.Errorf("failed to retrieve volume plugin information from globalMapPathUUID: %v", globalMapPathUUID)
|
||||
}
|
||||
return getVolumeSpecFromGlobalMapPath(globalMapPath)
|
||||
}
|
||||
|
||||
func getVolumeSpecFromGlobalMapPath(globalMapPath string) (*volume.Spec, error) {
|
||||
// Retrieve volume spec information from globalMapPath
|
||||
// globalMapPath example:
|
||||
// plugins/kubernetes.io/{PluginName}/{DefaultKubeletVolumeDevicesDirName}/{volumePluginDependentPath}
|
||||
pool, image, err := getPoolAndImageFromMapPath(globalMapPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
block := v1.PersistentVolumeBlock
|
||||
rbdVolume := &v1.PersistentVolume{
|
||||
Spec: v1.PersistentVolumeSpec{
|
||||
PersistentVolumeSource: v1.PersistentVolumeSource{
|
||||
RBD: &v1.RBDPersistentVolumeSource{
|
||||
RBDImage: image,
|
||||
RBDPool: pool,
|
||||
},
|
||||
},
|
||||
VolumeMode: &block,
|
||||
},
|
||||
}
|
||||
|
||||
return volume.NewSpecFromPersistentVolume(rbdVolume, true), nil
|
||||
}
|
||||
|
||||
func (plugin *rbdPlugin) NewBlockVolumeMapper(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.BlockVolumeMapper, error) {
|
||||
|
||||
var uid types.UID
|
||||
if pod != nil {
|
||||
uid = pod.UID
|
||||
}
|
||||
secret := ""
|
||||
// var err error
|
||||
if pod != nil {
|
||||
secretName, secretNs, err := getSecretNameAndNamespace(spec, pod.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(secretName) > 0 && len(secretNs) > 0 {
|
||||
// if secret is provideded, retrieve it
|
||||
kubeClient := plugin.host.GetKubeClient()
|
||||
if kubeClient == nil {
|
||||
return nil, fmt.Errorf("Cannot get kube client")
|
||||
}
|
||||
secrets, err := kubeClient.Core().Secrets(secretNs).Get(secretName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Couldn't get secret %v/%v err: %v", secretNs, secretName, err)
|
||||
return nil, err
|
||||
}
|
||||
for _, data := range secrets.Data {
|
||||
secret = string(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return plugin.newBlockVolumeMapperInternal(spec, uid, &RBDUtil{}, secret, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName()))
|
||||
}
|
||||
|
||||
func (plugin *rbdPlugin) newBlockVolumeMapperInternal(spec *volume.Spec, podUID types.UID, manager diskManager, secret string, mounter mount.Interface, exec mount.Exec) (volume.BlockVolumeMapper, error) {
|
||||
mon, err := getVolumeSourceMonitors(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
img, err := getVolumeSourceImage(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pool, err := getVolumeSourcePool(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
id, err := getVolumeSourceUser(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyring, err := getVolumeSourceKeyRing(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ro, err := getVolumeSourceReadOnly(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &rbdDiskMapper{
|
||||
rbd: newRBD(podUID, spec.Name(), img, pool, ro, plugin, manager),
|
||||
mon: mon,
|
||||
id: id,
|
||||
keyring: keyring,
|
||||
secret: secret,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (plugin *rbdPlugin) NewBlockVolumeUnmapper(volName string, podUID types.UID) (volume.BlockVolumeUnmapper, error) {
|
||||
return plugin.newUnmapperInternal(volName, podUID, &RBDUtil{})
|
||||
}
|
||||
|
||||
func (plugin *rbdPlugin) newUnmapperInternal(volName string, podUID types.UID, manager diskManager) (volume.BlockVolumeUnmapper, error) {
|
||||
return &rbdDiskUnmapper{
|
||||
rbdDiskMapper: &rbdDiskMapper{
|
||||
rbd: newRBD(podUID, volName, "", "", false, plugin, manager),
|
||||
mon: make([]string, 0),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (plugin *rbdPlugin) getDeviceNameFromOldMountPath(mounter mount.Interface, mountPath string) (string, error) {
|
||||
refs, err := mount.GetMountRefsByDev(mounter, mountPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// baseMountPath is the prefix of deprecated device global mounted path,
|
||||
// such as: /var/lib/kubelet/plugins/kubernetes.io/rbd/rbd
|
||||
baseMountPath := filepath.Join(plugin.host.GetPluginDir(rbdPluginName), "rbd")
|
||||
for _, ref := range refs {
|
||||
if dstrings.HasPrefix(ref, baseMountPath) {
|
||||
return filepath.Rel(baseMountPath, ref)
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("can't find source name from mounted path: %s", mountPath)
|
||||
}
|
||||
|
||||
func (plugin *rbdPlugin) NewDeleter(spec *volume.Spec) (volume.Deleter, error) {
|
||||
if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.RBD == nil {
|
||||
return nil, fmt.Errorf("spec.PersistentVolumeSource.Spec.RBD is nil")
|
||||
@ -402,7 +569,7 @@ type rbdVolumeProvisioner struct {
|
||||
var _ volume.Provisioner = &rbdVolumeProvisioner{}
|
||||
|
||||
func (r *rbdVolumeProvisioner) Provision() (*v1.PersistentVolume, error) {
|
||||
if !volume.AccessModesContainedInAll(r.plugin.GetAccessModes(), r.options.PVC.Spec.AccessModes) {
|
||||
if !volutil.AccessModesContainedInAll(r.plugin.GetAccessModes(), r.options.PVC.Spec.AccessModes) {
|
||||
return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", r.options.PVC.Spec.AccessModes, r.plugin.GetAccessModes())
|
||||
}
|
||||
|
||||
@ -415,6 +582,7 @@ func (r *rbdVolumeProvisioner) Provision() (*v1.PersistentVolume, error) {
|
||||
secret := ""
|
||||
secretName := ""
|
||||
secretNamespace := ""
|
||||
keyring := ""
|
||||
imageFormat := rbdImageFormat2
|
||||
fstype := ""
|
||||
|
||||
@ -439,6 +607,8 @@ func (r *rbdVolumeProvisioner) Provision() (*v1.PersistentVolume, error) {
|
||||
secretName = v
|
||||
case "usersecretnamespace":
|
||||
secretNamespace = v
|
||||
case "keyring":
|
||||
keyring = v
|
||||
case "imageformat":
|
||||
imageFormat = v
|
||||
case "imagefeatures":
|
||||
@ -472,8 +642,8 @@ func (r *rbdVolumeProvisioner) Provision() (*v1.PersistentVolume, error) {
|
||||
if len(r.Mon) < 1 {
|
||||
return nil, fmt.Errorf("missing Ceph monitors")
|
||||
}
|
||||
if secretName == "" {
|
||||
return nil, fmt.Errorf("missing user secret name")
|
||||
if secretName == "" && keyring == "" {
|
||||
return nil, fmt.Errorf("must specify either keyring or user secret name")
|
||||
}
|
||||
if r.adminId == "" {
|
||||
r.adminId = rbdDefaultAdminId
|
||||
@ -495,10 +665,20 @@ func (r *rbdVolumeProvisioner) Provision() (*v1.PersistentVolume, error) {
|
||||
}
|
||||
glog.Infof("successfully created rbd image %q", image)
|
||||
pv := new(v1.PersistentVolume)
|
||||
metav1.SetMetaDataAnnotation(&pv.ObjectMeta, volumehelper.VolumeDynamicallyCreatedByKey, "rbd-dynamic-provisioner")
|
||||
rbd.SecretRef = new(v1.SecretReference)
|
||||
rbd.SecretRef.Name = secretName
|
||||
rbd.SecretRef.Namespace = secretNamespace
|
||||
metav1.SetMetaDataAnnotation(&pv.ObjectMeta, volutil.VolumeDynamicallyCreatedByKey, "rbd-dynamic-provisioner")
|
||||
|
||||
if secretName != "" {
|
||||
rbd.SecretRef = new(v1.SecretReference)
|
||||
rbd.SecretRef.Name = secretName
|
||||
rbd.SecretRef.Namespace = secretNamespace
|
||||
} else {
|
||||
var filePathRegex = regexp.MustCompile(`^(?:/[^/!;` + "`" + ` ]+)+$`)
|
||||
if keyring != "" && !filePathRegex.MatchString(keyring) {
|
||||
return nil, fmt.Errorf("keyring field must contain a path to a file")
|
||||
}
|
||||
rbd.Keyring = keyring
|
||||
}
|
||||
|
||||
rbd.RadosUser = r.Id
|
||||
rbd.FSType = fstype
|
||||
pv.Spec.PersistentVolumeSource.RBD = rbd
|
||||
@ -561,7 +741,7 @@ func newRBD(podUID types.UID, volName string, image string, pool string, readOnl
|
||||
Pool: pool,
|
||||
ReadOnly: readOnly,
|
||||
plugin: plugin,
|
||||
mounter: volumehelper.NewSafeFormatAndMountFromHost(plugin.GetPluginName(), plugin.host),
|
||||
mounter: volutil.NewSafeFormatAndMountFromHost(plugin.GetPluginName(), plugin.host),
|
||||
exec: plugin.host.GetExec(plugin.GetPluginName()),
|
||||
manager: manager,
|
||||
MetricsProvider: volume.NewMetricsStatFS(getPath(podUID, volName, plugin.host)),
|
||||
@ -650,6 +830,140 @@ func (c *rbdUnmounter) TearDownAt(dir string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ volume.BlockVolumeMapper = &rbdDiskMapper{}
|
||||
|
||||
type rbdDiskMapper struct {
|
||||
*rbd
|
||||
mon []string
|
||||
id string
|
||||
keyring string
|
||||
secret string
|
||||
adminSecret string
|
||||
adminId string
|
||||
imageFormat string
|
||||
imageFeatures []string
|
||||
}
|
||||
|
||||
var _ volume.BlockVolumeUnmapper = &rbdDiskUnmapper{}
|
||||
|
||||
// GetGlobalMapPath returns global map path and error
|
||||
// path: plugins/kubernetes.io/{PluginName}/volumeDevices/{rbd pool}-image-{rbd image-name}/{podUid}
|
||||
func (rbd *rbd) GetGlobalMapPath(spec *volume.Spec) (string, error) {
|
||||
return rbd.rbdGlobalMapPath(spec)
|
||||
}
|
||||
|
||||
// GetPodDeviceMapPath returns pod device map path and volume name
|
||||
// path: pods/{podUid}/volumeDevices/kubernetes.io~rbd
|
||||
// volumeName: pv0001
|
||||
func (rbd *rbd) GetPodDeviceMapPath() (string, string) {
|
||||
return rbd.rbdPodDeviceMapPath()
|
||||
}
|
||||
|
||||
func (rbd *rbdDiskMapper) SetUpDevice() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (rbd *rbd) rbdGlobalMapPath(spec *volume.Spec) (string, error) {
|
||||
var err error
|
||||
mon, err := getVolumeSourceMonitors(spec)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
img, err := getVolumeSourceImage(spec)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
pool, err := getVolumeSourcePool(spec)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ro, err := getVolumeSourceReadOnly(spec)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
mounter := &rbdMounter{
|
||||
rbd: newRBD("", spec.Name(), img, pool, ro, rbd.plugin, &RBDUtil{}),
|
||||
Mon: mon,
|
||||
}
|
||||
return rbd.manager.MakeGlobalVDPDName(*mounter.rbd), nil
|
||||
}
|
||||
|
||||
func (rbd *rbd) rbdPodDeviceMapPath() (string, string) {
|
||||
name := rbdPluginName
|
||||
return rbd.plugin.host.GetPodVolumeDeviceDir(rbd.podUID, strings.EscapeQualifiedNameForDisk(name)), rbd.volName
|
||||
}
|
||||
|
||||
type rbdDiskUnmapper struct {
|
||||
*rbdDiskMapper
|
||||
}
|
||||
|
||||
func getPoolAndImageFromMapPath(mapPath string) (string, string, error) {
|
||||
|
||||
pathParts := dstrings.Split(mapPath, "/")
|
||||
if len(pathParts) < 2 {
|
||||
return "", "", fmt.Errorf("corrupted mapPath")
|
||||
}
|
||||
rbdParts := dstrings.Split(pathParts[len(pathParts)-1], "-image-")
|
||||
|
||||
if len(rbdParts) < 2 {
|
||||
return "", "", fmt.Errorf("corrupted mapPath")
|
||||
}
|
||||
return string(rbdParts[0]), string(rbdParts[1]), nil
|
||||
}
|
||||
|
||||
func getBlockVolumeDevice(mapPath string) (string, error) {
|
||||
pool, image, err := getPoolAndImageFromMapPath(mapPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Getting full device path
|
||||
device, found := getDevFromImageAndPool(pool, image)
|
||||
if !found {
|
||||
return "", err
|
||||
}
|
||||
return device, nil
|
||||
}
|
||||
|
||||
func (rbd *rbdDiskUnmapper) TearDownDevice(mapPath, _ string) error {
|
||||
|
||||
device, err := getBlockVolumeDevice(mapPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("rbd: failed to get loopback for device: %v, err: %v", device, err)
|
||||
}
|
||||
// Get loopback device which takes fd lock for device beofore detaching a volume from node.
|
||||
// TODO: This is a workaround for issue #54108
|
||||
// Currently local attach plugins such as FC, iSCSI, RBD can't obtain devicePath during
|
||||
// GenerateUnmapDeviceFunc() in operation_generator. As a result, these plugins fail to get
|
||||
// and remove loopback device then it will be remained on kubelet node. To avoid the problem,
|
||||
// local attach plugins needs to remove loopback device during TearDownDevice().
|
||||
blkUtil := volumepathhandler.NewBlockVolumePathHandler()
|
||||
loop, err := volumepathhandler.BlockVolumePathHandler.GetLoopDevice(blkUtil, device)
|
||||
if err != nil {
|
||||
return fmt.Errorf("rbd: failed to get loopback for device: %v, err: %v", device, err)
|
||||
}
|
||||
// Remove loop device before detaching volume since volume detach operation gets busy if volume is opened by loopback.
|
||||
err = volumepathhandler.BlockVolumePathHandler.RemoveLoopDevice(blkUtil, loop)
|
||||
if err != nil {
|
||||
return fmt.Errorf("rbd: failed to remove loopback :%v, err: %v", loop, err)
|
||||
}
|
||||
glog.V(4).Infof("rbd: successfully removed loop device: %s", loop)
|
||||
|
||||
err = rbd.manager.DetachBlockDisk(*rbd, mapPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("rbd: failed to detach disk: %s\nError: %v", mapPath, err)
|
||||
}
|
||||
glog.V(4).Infof("rbd: %q is unmapped, deleting the directory", mapPath)
|
||||
|
||||
err = os.RemoveAll(mapPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("rbd: failed to delete the directory: %s\nError: %v", mapPath, err)
|
||||
}
|
||||
glog.V(4).Infof("rbd: successfully detached disk: %s", mapPath)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getVolumeSourceMonitors(spec *volume.Spec) ([]string, error) {
|
||||
if spec.Volume != nil && spec.Volume.RBD != nil {
|
||||
return spec.Volume.RBD.CephMonitors, nil
|
||||
|
Reference in New Issue
Block a user