2018-03-05 11:59:47 +00:00
|
|
|
/*
|
2019-04-03 08:46:15 +00:00
|
|
|
Copyright 2018 The Ceph-CSI Authors.
|
2018-03-05 11:59:47 +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 cephfs
|
|
|
|
|
|
|
|
import (
|
2019-08-22 17:19:06 +00:00
|
|
|
"context"
|
2019-07-10 10:50:04 +00:00
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path"
|
2019-06-08 05:06:03 +00:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2019-01-28 13:59:16 +00:00
|
|
|
|
2019-06-01 21:26:42 +00:00
|
|
|
"github.com/ceph/ceph-csi/pkg/util"
|
|
|
|
|
2019-02-08 08:21:22 +00:00
|
|
|
"k8s.io/klog"
|
2018-03-09 16:05:19 +00:00
|
|
|
)
|
|
|
|
|
2018-03-22 13:01:10 +00:00
|
|
|
const (
|
2019-06-08 05:06:03 +00:00
|
|
|
csiSubvolumeGroup = "csi"
|
2018-03-22 13:01:10 +00:00
|
|
|
)
|
|
|
|
|
2019-06-08 05:06:03 +00:00
|
|
|
var (
|
|
|
|
// cephfsInit is used to create "csi" subvolume group for the first time the csi plugin loads.
|
|
|
|
// Subvolume group create gets called every time the plugin loads, though it doesn't result in error
|
|
|
|
// its unnecessary
|
|
|
|
cephfsInit = false
|
2020-05-06 13:47:46 +00:00
|
|
|
|
|
|
|
// resizeSupportedList stores the mapping for clusterID and resize is
|
|
|
|
// supported in the particular cluster
|
|
|
|
resizeSupportedList = make(map[string]bool)
|
|
|
|
|
|
|
|
inValidCommmand = "no valid command found"
|
2019-06-08 05:06:03 +00:00
|
|
|
)
|
2018-03-22 13:01:10 +00:00
|
|
|
|
2019-07-10 10:50:04 +00:00
|
|
|
func getCephRootVolumePathLocalDeprecated(volID volumeID) string {
|
|
|
|
return path.Join(getCephRootPathLocalDeprecated(volID), "csi-volumes", string(volID))
|
|
|
|
}
|
|
|
|
|
|
|
|
func getVolumeRootPathCephDeprecated(volID volumeID) string {
|
|
|
|
return path.Join("/", "csi-volumes", string(volID))
|
|
|
|
}
|
|
|
|
|
|
|
|
func getCephRootPathLocalDeprecated(volID volumeID) string {
|
|
|
|
return fmt.Sprintf("%s/controller/volumes/root-%s", PluginFolder, string(volID))
|
|
|
|
}
|
|
|
|
|
2020-01-23 02:48:46 +00:00
|
|
|
func getVolumeNotFoundErrorString(volID volumeID) string {
|
|
|
|
return fmt.Sprintf("Error ENOENT: Subvolume '%s' not found", string(volID))
|
|
|
|
}
|
|
|
|
|
2019-08-22 17:19:06 +00:00
|
|
|
func getVolumeRootPathCeph(ctx context.Context, volOptions *volumeOptions, cr *util.Credentials, volID volumeID) (string, error) {
|
2020-01-23 02:48:46 +00:00
|
|
|
stdout, stderr, err := util.ExecCommand(
|
2019-06-08 05:06:03 +00:00
|
|
|
"ceph",
|
|
|
|
"fs",
|
|
|
|
"subvolume",
|
|
|
|
"getpath",
|
|
|
|
volOptions.FsName,
|
|
|
|
string(volID),
|
|
|
|
"--group_name",
|
|
|
|
csiSubvolumeGroup,
|
|
|
|
"-m", volOptions.Monitors,
|
|
|
|
"-c", util.CephConfigPath,
|
|
|
|
"-n", cephEntityClientPrefix+cr.ID,
|
2019-06-25 19:29:17 +00:00
|
|
|
"--keyfile="+cr.KeyFile)
|
2018-03-26 13:00:28 +00:00
|
|
|
|
2019-06-08 05:06:03 +00:00
|
|
|
if err != nil {
|
2019-08-22 17:19:06 +00:00
|
|
|
klog.Errorf(util.Log(ctx, "failed to get the rootpath for the vol %s(%s)"), string(volID), err)
|
2020-01-23 02:48:46 +00:00
|
|
|
|
|
|
|
if strings.Contains(string(stderr), getVolumeNotFoundErrorString(volID)) {
|
|
|
|
return "", ErrVolumeNotFound{err}
|
|
|
|
}
|
|
|
|
|
2019-06-08 05:06:03 +00:00
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return strings.TrimSuffix(string(stdout), "\n"), nil
|
2018-04-13 12:49:49 +00:00
|
|
|
}
|
|
|
|
|
2019-08-22 17:19:06 +00:00
|
|
|
func createVolume(ctx context.Context, volOptions *volumeOptions, cr *util.Credentials, volID volumeID, bytesQuota int64) error {
|
2019-06-08 05:06:03 +00:00
|
|
|
//TODO: When we support multiple fs, need to hande subvolume group create for all fs's
|
|
|
|
if !cephfsInit {
|
|
|
|
err := execCommandErr(
|
2019-08-22 17:19:06 +00:00
|
|
|
ctx,
|
2019-06-08 05:06:03 +00:00
|
|
|
"ceph",
|
|
|
|
"fs",
|
|
|
|
"subvolumegroup",
|
|
|
|
"create",
|
|
|
|
volOptions.FsName,
|
|
|
|
csiSubvolumeGroup,
|
|
|
|
"-m", volOptions.Monitors,
|
|
|
|
"-c", util.CephConfigPath,
|
|
|
|
"-n", cephEntityClientPrefix+cr.ID,
|
2019-06-25 19:29:17 +00:00
|
|
|
"--keyfile="+cr.KeyFile)
|
2019-06-08 05:06:03 +00:00
|
|
|
if err != nil {
|
2019-08-22 17:19:06 +00:00
|
|
|
klog.Errorf(util.Log(ctx, "failed to create subvolume group csi, for the vol %s(%s)"), string(volID), err)
|
2018-12-01 09:39:09 +00:00
|
|
|
return err
|
|
|
|
}
|
2019-08-22 17:19:06 +00:00
|
|
|
klog.V(4).Infof(util.Log(ctx, "cephfs: created subvolume group csi"))
|
2019-06-08 05:06:03 +00:00
|
|
|
cephfsInit = true
|
2018-04-13 12:49:49 +00:00
|
|
|
}
|
2019-08-05 15:40:48 +00:00
|
|
|
|
|
|
|
args := []string{
|
2019-06-08 05:06:03 +00:00
|
|
|
"fs",
|
|
|
|
"subvolume",
|
|
|
|
"create",
|
|
|
|
volOptions.FsName,
|
|
|
|
string(volID),
|
|
|
|
strconv.FormatInt(bytesQuota, 10),
|
|
|
|
"--group_name",
|
|
|
|
csiSubvolumeGroup,
|
2019-07-25 12:39:52 +00:00
|
|
|
"--mode", "777",
|
2019-06-08 05:06:03 +00:00
|
|
|
"-m", volOptions.Monitors,
|
|
|
|
"-c", util.CephConfigPath,
|
2019-08-05 15:40:48 +00:00
|
|
|
"-n", cephEntityClientPrefix + cr.ID,
|
|
|
|
"--keyfile=" + cr.KeyFile,
|
|
|
|
}
|
|
|
|
|
|
|
|
if volOptions.Pool != "" {
|
|
|
|
args = append(args, "--pool_layout", volOptions.Pool)
|
|
|
|
}
|
|
|
|
|
|
|
|
err := execCommandErr(
|
2019-08-22 17:19:06 +00:00
|
|
|
ctx,
|
2019-08-05 15:40:48 +00:00
|
|
|
"ceph",
|
|
|
|
args[:]...)
|
2019-06-08 05:06:03 +00:00
|
|
|
if err != nil {
|
2019-08-22 17:19:06 +00:00
|
|
|
klog.Errorf(util.Log(ctx, "failed to create subvolume %s(%s) in fs %s"), string(volID), err, volOptions.FsName)
|
2019-02-26 10:06:16 +00:00
|
|
|
return err
|
|
|
|
}
|
2018-04-13 12:49:49 +00:00
|
|
|
|
2019-02-26 10:06:16 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-05-06 13:47:46 +00:00
|
|
|
// resizeVolume will try to use ceph fs subvolume resize command to resize the
|
|
|
|
// subvolume. If the command is not available as a fallback it will use
|
|
|
|
// CreateVolume to resize the subvolume.
|
|
|
|
func resizeVolume(ctx context.Context, volOptions *volumeOptions, cr *util.Credentials, volID volumeID, bytesQuota int64) error {
|
|
|
|
supported := false
|
|
|
|
ok := false
|
|
|
|
|
|
|
|
if supported, ok = resizeSupportedList[volOptions.ClusterID]; supported || !ok {
|
|
|
|
args := []string{
|
|
|
|
"fs",
|
|
|
|
"subvolume",
|
|
|
|
"resize",
|
|
|
|
volOptions.FsName,
|
|
|
|
string(volID),
|
|
|
|
strconv.FormatInt(bytesQuota, 10),
|
|
|
|
"--group_name",
|
|
|
|
csiSubvolumeGroup,
|
|
|
|
"-m", volOptions.Monitors,
|
|
|
|
"-c", util.CephConfigPath,
|
|
|
|
"-n", cephEntityClientPrefix + cr.ID,
|
|
|
|
"--keyfile=" + cr.KeyFile,
|
|
|
|
}
|
|
|
|
|
|
|
|
err := execCommandErr(
|
|
|
|
ctx,
|
|
|
|
"ceph",
|
|
|
|
args[:]...)
|
|
|
|
|
|
|
|
if err == nil {
|
|
|
|
resizeSupportedList[volOptions.ClusterID] = true
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
// Incase the error is other than invalid command return error to the caller.
|
|
|
|
if !strings.Contains(err.Error(), inValidCommmand) {
|
|
|
|
klog.Errorf(util.Log(ctx, "failed to resize subvolume %s(%s) in fs %s"), string(volID), err, volOptions.FsName)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
resizeSupportedList[volOptions.ClusterID] = false
|
|
|
|
return createVolume(ctx, volOptions, cr, volID, bytesQuota)
|
|
|
|
}
|
|
|
|
|
2019-08-22 17:19:06 +00:00
|
|
|
func mountCephRoot(ctx context.Context, volID volumeID, volOptions *volumeOptions, adminCr *util.Credentials) error {
|
2019-07-10 10:50:04 +00:00
|
|
|
cephRoot := getCephRootPathLocalDeprecated(volID)
|
|
|
|
|
|
|
|
// Root path is not set for dynamically provisioned volumes
|
|
|
|
// Access to cephfs's / is required
|
|
|
|
volOptions.RootPath = "/"
|
|
|
|
|
2019-07-03 10:02:36 +00:00
|
|
|
if err := util.CreateMountPoint(cephRoot); err != nil {
|
2019-07-10 10:50:04 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
m, err := newMounter(volOptions)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to create mounter: %v", err)
|
|
|
|
}
|
|
|
|
|
2019-08-22 17:19:06 +00:00
|
|
|
if err = m.mount(ctx, cephRoot, adminCr, volOptions); err != nil {
|
2019-07-10 10:50:04 +00:00
|
|
|
return fmt.Errorf("error mounting ceph root: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-08-22 17:19:06 +00:00
|
|
|
func unmountCephRoot(ctx context.Context, volID volumeID) {
|
2019-07-10 10:50:04 +00:00
|
|
|
cephRoot := getCephRootPathLocalDeprecated(volID)
|
|
|
|
|
2019-08-22 17:19:06 +00:00
|
|
|
if err := unmountVolume(ctx, cephRoot); err != nil {
|
|
|
|
klog.Errorf(util.Log(ctx, "failed to unmount %s with error %s"), cephRoot, err)
|
2019-07-10 10:50:04 +00:00
|
|
|
} else {
|
|
|
|
if err := os.Remove(cephRoot); err != nil {
|
2019-08-22 17:19:06 +00:00
|
|
|
klog.Errorf(util.Log(ctx, "failed to remove %s with error %s"), cephRoot, err)
|
2019-07-10 10:50:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-22 17:19:06 +00:00
|
|
|
func purgeVolumeDeprecated(ctx context.Context, volID volumeID, adminCr *util.Credentials, volOptions *volumeOptions) error {
|
|
|
|
if err := mountCephRoot(ctx, volID, volOptions, adminCr); err != nil {
|
2019-07-10 10:50:04 +00:00
|
|
|
return err
|
|
|
|
}
|
2019-08-22 17:19:06 +00:00
|
|
|
defer unmountCephRoot(ctx, volID)
|
2019-07-10 10:50:04 +00:00
|
|
|
|
|
|
|
var (
|
|
|
|
volRoot = getCephRootVolumePathLocalDeprecated(volID)
|
|
|
|
volRootDeleting = volRoot + "-deleting"
|
|
|
|
)
|
|
|
|
|
|
|
|
if pathExists(volRoot) {
|
|
|
|
if err := os.Rename(volRoot, volRootDeleting); err != nil {
|
|
|
|
return fmt.Errorf("couldn't mark volume %s for deletion: %v", volID, err)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if !pathExists(volRootDeleting) {
|
2019-08-22 17:19:06 +00:00
|
|
|
klog.V(4).Infof(util.Log(ctx, "cephfs: volume %s not found, assuming it to be already deleted"), volID)
|
2019-07-10 10:50:04 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := os.RemoveAll(volRootDeleting); err != nil {
|
|
|
|
return fmt.Errorf("failed to delete volume %s: %v", volID, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-08-22 17:19:06 +00:00
|
|
|
func purgeVolume(ctx context.Context, volID volumeID, cr *util.Credentials, volOptions *volumeOptions) error {
|
2019-06-08 05:06:03 +00:00
|
|
|
err := execCommandErr(
|
2019-08-22 17:19:06 +00:00
|
|
|
ctx,
|
2019-06-08 05:06:03 +00:00
|
|
|
"ceph",
|
|
|
|
"fs",
|
|
|
|
"subvolume",
|
|
|
|
"rm",
|
|
|
|
volOptions.FsName,
|
|
|
|
string(volID),
|
|
|
|
"--group_name",
|
|
|
|
csiSubvolumeGroup,
|
|
|
|
"-m", volOptions.Monitors,
|
|
|
|
"-c", util.CephConfigPath,
|
|
|
|
"-n", cephEntityClientPrefix+cr.ID,
|
2019-06-25 19:29:17 +00:00
|
|
|
"--keyfile="+cr.KeyFile)
|
2018-08-14 09:19:41 +00:00
|
|
|
if err != nil {
|
2019-08-22 17:19:06 +00:00
|
|
|
klog.Errorf(util.Log(ctx, "failed to purge subvolume %s(%s) in fs %s"), string(volID), err, volOptions.FsName)
|
2020-01-23 02:48:46 +00:00
|
|
|
|
|
|
|
if strings.Contains(err.Error(), getVolumeNotFoundErrorString(volID)) {
|
|
|
|
return ErrVolumeNotFound{err}
|
|
|
|
}
|
|
|
|
|
2019-06-08 05:06:03 +00:00
|
|
|
return err
|
2018-04-13 12:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2018-03-09 16:05:19 +00:00
|
|
|
}
|