mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 02:33:34 +00:00
Merge branch 'master' into master-to-1.0
This commit is contained in:
@ -20,11 +20,11 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"github.com/ceph/ceph-csi/pkg/util"
|
||||
"github.com/golang/glog"
|
||||
"github.com/golang/protobuf/ptypes/timestamp"
|
||||
"github.com/kubernetes-csi/drivers/pkg/csi-common"
|
||||
@ -41,6 +41,28 @@ const (
|
||||
|
||||
type controllerServer struct {
|
||||
*csicommon.DefaultControllerServer
|
||||
MetadataStore util.CachePersister
|
||||
}
|
||||
|
||||
var (
|
||||
rbdVolumes = map[string]*rbdVolume{}
|
||||
rbdSnapshots = map[string]*rbdSnapshot{}
|
||||
)
|
||||
|
||||
func (cs *controllerServer) LoadExDataFromMetadataStore() error {
|
||||
vol := &rbdVolume{}
|
||||
cs.MetadataStore.ForAll("csi-rbd-vol-", vol, func(identifier string) error {
|
||||
rbdVolumes[identifier] = vol
|
||||
return nil
|
||||
})
|
||||
|
||||
snap := &rbdSnapshot{}
|
||||
cs.MetadataStore.ForAll("csi-rbd-(.*)-snap-", snap, func(identifier string) error {
|
||||
rbdSnapshots[identifier] = snap
|
||||
return nil
|
||||
})
|
||||
glog.Infof("Loaded %d volumes and %d snapshots from metadata store", len(rbdVolumes), len(rbdSnapshots))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error) {
|
||||
@ -55,6 +77,14 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
|
||||
if req.VolumeCapabilities == nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "Volume Capabilities cannot be empty")
|
||||
}
|
||||
for _, cap := range req.VolumeCapabilities {
|
||||
if cap.GetBlock() != nil {
|
||||
return nil, status.Error(codes.Unimplemented, "Block Volume not supported")
|
||||
}
|
||||
}
|
||||
|
||||
volumeNameMutex.LockKey(req.GetName())
|
||||
defer volumeNameMutex.UnlockKey(req.GetName())
|
||||
|
||||
volumeNameMutex.LockKey(req.GetName())
|
||||
defer volumeNameMutex.UnlockKey(req.GetName())
|
||||
@ -93,7 +123,7 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
|
||||
volName = rbdVol.Pool + "-dynamic-pvc-" + uniqueID
|
||||
}
|
||||
rbdVol.VolName = volName
|
||||
volumeID := "csi-rbd-" + uniqueID
|
||||
volumeID := "csi-rbd-vol-" + uniqueID
|
||||
rbdVol.VolID = volumeID
|
||||
// Volume Size - Default is 1 GiB
|
||||
volSizeBytes := int64(oneGB)
|
||||
@ -119,7 +149,7 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
|
||||
}
|
||||
|
||||
rbdSnap := &rbdSnapshot{}
|
||||
if err := loadSnapInfo(snapshotID, path.Join(PluginFolder, "controller-snap"), rbdSnap); err != nil {
|
||||
if err := cs.MetadataStore.Get(snapshotID, rbdSnap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -138,11 +168,15 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
|
||||
glog.V(4).Infof("create volume %s", volName)
|
||||
}
|
||||
}
|
||||
|
||||
// Storing volInfo into a persistent file.
|
||||
if err := persistVolInfo(volumeID, path.Join(PluginFolder, "controller"), rbdVol); err != nil {
|
||||
glog.Warningf("rbd: failed to store volInfo with error: %v", err)
|
||||
if err := cs.MetadataStore.Create(volumeID, rbdVol); err != nil {
|
||||
glog.Warningf("failed to store volume metadata with error: %v", err)
|
||||
if err := deleteRBDImage(rbdVol, rbdVol.AdminId, req.GetControllerCreateSecrets()); err != nil {
|
||||
glog.V(3).Infof("failed to delete rbd image: %s/%s with error: %v", rbdVol.Pool, rbdVol.VolName, err)
|
||||
return nil, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rbdVolumes[volumeID] = rbdVol
|
||||
return &csi.CreateVolumeResponse{
|
||||
Volume: &csi.Volume{
|
||||
@ -163,13 +197,13 @@ func (cs *controllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVol
|
||||
volumeIDMutex.LockKey(volumeID)
|
||||
defer volumeIDMutex.UnlockKey(volumeID)
|
||||
rbdVol := &rbdVolume{}
|
||||
if err := loadVolInfo(volumeID, path.Join(PluginFolder, "controller"), rbdVol); err != nil {
|
||||
if err := cs.MetadataStore.Get(volumeID, rbdVol); err != nil {
|
||||
if os.IsNotExist(errors.Cause(err)) {
|
||||
// Must have been deleted already. This is not an error (idempotency!).
|
||||
return &csi.DeleteVolumeResponse{}, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
volName := rbdVol.VolName
|
||||
// Deleting rbd image
|
||||
glog.V(4).Infof("deleting volume %s", volName)
|
||||
@ -178,8 +212,8 @@ func (cs *controllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVol
|
||||
glog.V(3).Infof("failed to delete rbd image: %s/%s with error: %v", rbdVol.Pool, volName, err)
|
||||
return nil, err
|
||||
}
|
||||
// Removing persistent storage file for the unmapped volume
|
||||
if err := deleteVolInfo(volumeID, path.Join(PluginFolder, "controller")); err != nil {
|
||||
|
||||
if err := cs.MetadataStore.Delete(volumeID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -301,24 +335,21 @@ func (cs *controllerServer) CreateSnapshot(ctx context.Context, req *csi.CreateS
|
||||
|
||||
rbdSnap.CreatedAt = time.Now().UnixNano()
|
||||
|
||||
// Storing snapInfo into a persistent file.
|
||||
if err := persistSnapInfo(snapshotID, path.Join(PluginFolder, "controller-snap"), rbdSnap); err != nil {
|
||||
if err := cs.MetadataStore.Create(snapshotID, rbdSnap); err != nil {
|
||||
glog.Warningf("rbd: failed to store snapInfo with error: %v", err)
|
||||
|
||||
// Unprotect snapshot
|
||||
err := unprotectSnapshot(rbdSnap, rbdSnap.AdminId, req.GetSecrets())
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Unknown, fmt.Sprintf("This Snapshot should be removed but failed to unprotect snapshot: %s/%s with error: %v", rbdSnap.Pool, rbdSnap.SnapName, err))
|
||||
}
|
||||
|
||||
// Deleting snapshot
|
||||
glog.V(4).Infof("deleting Snaphot %s", rbdSnap.SnapName)
|
||||
if err := deleteSnapshot(rbdSnap, rbdSnap.AdminId, req.GetSecrets()); err != nil {
|
||||
return nil, status.Error(codes.Unknown, fmt.Sprintf("This Snapshot should be removed but failed to delete snapshot: %s/%s with error: %v", rbdSnap.Pool, rbdSnap.SnapName, err))
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rbdSnapshots[snapshotID] = rbdSnap
|
||||
return &csi.CreateSnapshotResponse{
|
||||
Snapshot: &csi.Snapshot{
|
||||
@ -347,7 +378,7 @@ func (cs *controllerServer) DeleteSnapshot(ctx context.Context, req *csi.DeleteS
|
||||
defer snapshotIDMutex.UnlockKey(snapshotID)
|
||||
|
||||
rbdSnap := &rbdSnapshot{}
|
||||
if err := loadSnapInfo(snapshotID, path.Join(PluginFolder, "controller-snap"), rbdSnap); err != nil {
|
||||
if err := cs.MetadataStore.Get(snapshotID, rbdSnap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -363,8 +394,7 @@ func (cs *controllerServer) DeleteSnapshot(ctx context.Context, req *csi.DeleteS
|
||||
return nil, status.Error(codes.FailedPrecondition, fmt.Sprintf("failed to delete snapshot: %s/%s with error: %v", rbdSnap.Pool, rbdSnap.SnapName, err))
|
||||
}
|
||||
|
||||
// Removing persistent storage file for the unmapped snapshot
|
||||
if err := deleteSnapInfo(snapshotID, path.Join(PluginFolder, "controller-snap")); err != nil {
|
||||
if err := cs.MetadataStore.Delete(snapshotID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
105
pkg/rbd/rbd.go
105
pkg/rbd/rbd.go
@ -17,14 +17,9 @@ limitations under the License.
|
||||
package rbd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"github.com/ceph/ceph-csi/pkg/util"
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
"github.com/kubernetes-csi/drivers/pkg/csi-common"
|
||||
|
||||
@ -56,94 +51,6 @@ var (
|
||||
version = "1.0.0"
|
||||
)
|
||||
|
||||
var rbdVolumes map[string]*rbdVolume
|
||||
var rbdSnapshots map[string]*rbdSnapshot
|
||||
|
||||
// Init checks for the persistent volume file and loads all found volumes
|
||||
// into a memory structure
|
||||
func init() {
|
||||
rbdVolumes = map[string]*rbdVolume{}
|
||||
rbdSnapshots = map[string]*rbdSnapshot{}
|
||||
if _, err := os.Stat(path.Join(PluginFolder, "controller")); os.IsNotExist(err) {
|
||||
glog.Infof("rbd: folder %s not found. Creating... \n", path.Join(PluginFolder, "controller"))
|
||||
if err := os.Mkdir(path.Join(PluginFolder, "controller"), 0755); err != nil {
|
||||
glog.Fatalf("Failed to create a controller's volumes folder with error: %v\n", err)
|
||||
}
|
||||
} else {
|
||||
// Since "controller" folder exists, it means the rbdplugin has already been running, it means
|
||||
// there might be some volumes left, they must be re-inserted into rbdVolumes map
|
||||
loadExVolumes()
|
||||
}
|
||||
if _, err := os.Stat(path.Join(PluginFolder, "controller-snap")); os.IsNotExist(err) {
|
||||
glog.Infof("rbd: folder %s not found. Creating... \n", path.Join(PluginFolder, "controller-snap"))
|
||||
if err := os.Mkdir(path.Join(PluginFolder, "controller-snap"), 0755); err != nil {
|
||||
glog.Fatalf("Failed to create a controller's snapshots folder with error: %v\n", err)
|
||||
}
|
||||
} else {
|
||||
// Since "controller-snap" folder exists, it means the rbdplugin has already been running, it means
|
||||
// there might be some snapshots left, they must be re-inserted into rbdSnapshots map
|
||||
loadExSnapshots()
|
||||
}
|
||||
}
|
||||
|
||||
// loadExSnapshots check for any *.json files in the PluginFolder/controller-snap folder
|
||||
// and loads then into rbdSnapshots map
|
||||
func loadExSnapshots() {
|
||||
rbdSnap := rbdSnapshot{}
|
||||
files, err := ioutil.ReadDir(path.Join(PluginFolder, "controller-snap"))
|
||||
if err != nil {
|
||||
glog.Infof("rbd: failed to read controller's snapshots folder: %s error:%v", path.Join(PluginFolder, "controller-snap"), err)
|
||||
return
|
||||
}
|
||||
for _, f := range files {
|
||||
if !strings.HasSuffix(f.Name(), ".json") {
|
||||
continue
|
||||
}
|
||||
fp, err := os.Open(path.Join(PluginFolder, "controller-snap", f.Name()))
|
||||
if err != nil {
|
||||
glog.Infof("rbd: open file: %s err %v", f.Name(), err)
|
||||
continue
|
||||
}
|
||||
decoder := json.NewDecoder(fp)
|
||||
if err = decoder.Decode(&rbdSnap); err != nil {
|
||||
glog.Infof("rbd: decode file: %s err: %v", f.Name(), err)
|
||||
fp.Close()
|
||||
continue
|
||||
}
|
||||
rbdSnapshots[rbdSnap.SnapID] = &rbdSnap
|
||||
}
|
||||
glog.Infof("rbd: Loaded %d snapshots from %s", len(rbdSnapshots), path.Join(PluginFolder, "controller-snap"))
|
||||
}
|
||||
|
||||
// loadExVolumes check for any *.json files in the PluginFolder/controller folder
|
||||
// and loads then into rbdVolumes map
|
||||
func loadExVolumes() {
|
||||
rbdVol := rbdVolume{}
|
||||
files, err := ioutil.ReadDir(path.Join(PluginFolder, "controller"))
|
||||
if err != nil {
|
||||
glog.Infof("rbd: failed to read controller's volumes folder: %s error:%v", path.Join(PluginFolder, "controller"), err)
|
||||
return
|
||||
}
|
||||
for _, f := range files {
|
||||
if !strings.HasSuffix(f.Name(), ".json") {
|
||||
continue
|
||||
}
|
||||
fp, err := os.Open(path.Join(PluginFolder, "controller", f.Name()))
|
||||
if err != nil {
|
||||
glog.Infof("rbd: open file: %s err %v", f.Name(), err)
|
||||
continue
|
||||
}
|
||||
decoder := json.NewDecoder(fp)
|
||||
if err = decoder.Decode(&rbdVol); err != nil {
|
||||
glog.Infof("rbd: decode file: %s err: %v", f.Name(), err)
|
||||
fp.Close()
|
||||
continue
|
||||
}
|
||||
rbdVolumes[rbdVol.VolID] = &rbdVol
|
||||
}
|
||||
glog.Infof("rbd: Loaded %d volumes from %s", len(rbdVolumes), path.Join(PluginFolder, "controller"))
|
||||
}
|
||||
|
||||
func GetRBDDriver() *rbd {
|
||||
return &rbd{}
|
||||
}
|
||||
@ -154,9 +61,10 @@ func NewIdentityServer(d *csicommon.CSIDriver) *identityServer {
|
||||
}
|
||||
}
|
||||
|
||||
func NewControllerServer(d *csicommon.CSIDriver) *controllerServer {
|
||||
func NewControllerServer(d *csicommon.CSIDriver, cachePersister util.CachePersister) *controllerServer {
|
||||
return &controllerServer{
|
||||
DefaultControllerServer: csicommon.NewDefaultControllerServer(d),
|
||||
MetadataStore: cachePersister,
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,7 +83,7 @@ func NewNodeServer(d *csicommon.CSIDriver, containerized bool) (*nodeServer, err
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (rbd *rbd) Run(driverName, nodeID, endpoint string, containerized bool) {
|
||||
func (rbd *rbd) Run(driverName, nodeID, endpoint string, containerized bool, cachePersister util.CachePersister) {
|
||||
var err error
|
||||
glog.Infof("Driver: %v version: %v", driverName, version)
|
||||
|
||||
@ -198,7 +106,10 @@ func (rbd *rbd) Run(driverName, nodeID, endpoint string, containerized bool) {
|
||||
if err != nil {
|
||||
glog.Fatalf("failed to start node server, err %v\n", err)
|
||||
}
|
||||
rbd.cs = NewControllerServer(rbd.driver)
|
||||
|
||||
rbd.cs = NewControllerServer(rbd.driver, cachePersister)
|
||||
rbd.cs.LoadExDataFromMetadataStore()
|
||||
|
||||
s := csicommon.NewNonBlockingGRPCServer()
|
||||
s.Start(endpoint, rbd.ids, rbd.cs, rbd.ns)
|
||||
s.Wait()
|
||||
|
@ -17,11 +17,8 @@ limitations under the License.
|
||||
package rbd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -76,17 +73,17 @@ type rbdSnapshot struct {
|
||||
|
||||
var (
|
||||
// serializes operations based on "<rbd pool>/<rbd image>" as key
|
||||
attachdetachMutex = keymutex.NewHashed(0)
|
||||
attachdetachMutex = keymutex.NewKeyMutex()
|
||||
// serializes operations based on "volume name" as key
|
||||
volumeNameMutex = keymutex.NewHashed(0)
|
||||
volumeNameMutex = keymutex.NewKeyMutex()
|
||||
// serializes operations based on "volume id" as key
|
||||
volumeIDMutex = keymutex.NewHashed(0)
|
||||
volumeIDMutex = keymutex.NewKeyMutex()
|
||||
// serializes operations based on "snapshot name" as key
|
||||
snapshotNameMutex = keymutex.NewHashed(0)
|
||||
snapshotNameMutex = keymutex.NewKeyMutex()
|
||||
// serializes operations based on "snapshot id" as key
|
||||
snapshotIDMutex = keymutex.NewHashed(0)
|
||||
snapshotIDMutex = keymutex.NewKeyMutex()
|
||||
// serializes operations based on "mount target path" as key
|
||||
targetPathMutex = keymutex.NewHashed(0)
|
||||
targetPathMutex = keymutex.NewKeyMutex()
|
||||
|
||||
supportedFeatures = sets.NewString("layering")
|
||||
)
|
||||
@ -316,95 +313,6 @@ func hasSnapshotFeature(imageFeatures string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func persistVolInfo(image string, persistentStoragePath string, volInfo *rbdVolume) error {
|
||||
file := path.Join(persistentStoragePath, image+".json")
|
||||
fp, err := os.Create(file)
|
||||
if err != nil {
|
||||
glog.Errorf("rbd: failed to create persistent storage file %s with error: %v\n", file, err)
|
||||
return errors.Wrapf(err, "rbd: create error for %s", file)
|
||||
}
|
||||
defer fp.Close()
|
||||
encoder := json.NewEncoder(fp)
|
||||
if err = encoder.Encode(volInfo); err != nil {
|
||||
glog.Errorf("rbd: failed to encode volInfo: %+v for file: %s with error: %v\n", volInfo, file, err)
|
||||
return errors.Wrap(err, "rbd: encode error")
|
||||
}
|
||||
glog.Infof("rbd: successfully saved volInfo: %+v into file: %s\n", volInfo, file)
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadVolInfo(image string, persistentStoragePath string, volInfo *rbdVolume) error {
|
||||
file := path.Join(persistentStoragePath, image+".json")
|
||||
fp, err := os.Open(file)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "rbd: open error for %s", file)
|
||||
}
|
||||
defer fp.Close()
|
||||
|
||||
decoder := json.NewDecoder(fp)
|
||||
if err = decoder.Decode(volInfo); err != nil {
|
||||
return errors.Wrap(err, "rbd: decode error")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteVolInfo(image string, persistentStoragePath string) error {
|
||||
file := path.Join(persistentStoragePath, image+".json")
|
||||
glog.Infof("rbd: Deleting file for Volume: %s at: %s resulting path: %+v\n", image, persistentStoragePath, file)
|
||||
err := os.Remove(file)
|
||||
if err != nil {
|
||||
if err != os.ErrNotExist {
|
||||
return errors.Wrapf(err, "rbd: error removing file %s", file)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func persistSnapInfo(snapshot string, persistentStoragePath string, snapInfo *rbdSnapshot) error {
|
||||
file := path.Join(persistentStoragePath, snapshot+".json")
|
||||
fp, err := os.Create(file)
|
||||
if err != nil {
|
||||
glog.Errorf("rbd: failed to create persistent storage file %s with error: %v\n", file, err)
|
||||
return errors.Wrapf(err, "rbd: create error for %s", file)
|
||||
}
|
||||
defer fp.Close()
|
||||
encoder := json.NewEncoder(fp)
|
||||
if err = encoder.Encode(snapInfo); err != nil {
|
||||
glog.Errorf("rbd: failed to encode snapInfo: %+v for file: %s with error: %v\n", snapInfo, file, err)
|
||||
return errors.Wrap(err, "rbd: encode error")
|
||||
}
|
||||
glog.Infof("rbd: successfully saved snapInfo: %+v into file: %s\n", snapInfo, file)
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadSnapInfo(snapshot string, persistentStoragePath string, snapInfo *rbdSnapshot) error {
|
||||
file := path.Join(persistentStoragePath, snapshot+".json")
|
||||
fp, err := os.Open(file)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "rbd: open error for %s", file)
|
||||
}
|
||||
defer fp.Close()
|
||||
|
||||
decoder := json.NewDecoder(fp)
|
||||
if err = decoder.Decode(snapInfo); err != nil {
|
||||
return errors.Wrap(err, "rbd: decode error")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteSnapInfo(snapshot string, persistentStoragePath string) error {
|
||||
file := path.Join(persistentStoragePath, snapshot+".json")
|
||||
glog.Infof("rbd: Deleting file for Snapshot: %s at: %s resulting path: %+v\n", snapshot, persistentStoragePath, file)
|
||||
err := os.Remove(file)
|
||||
if err != nil {
|
||||
if err != os.ErrNotExist {
|
||||
return errors.Wrapf(err, "rbd: error removing file %s", file)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getRBDVolumeByID(volumeID string) (*rbdVolume, error) {
|
||||
if rbdVol, ok := rbdVolumes[volumeID]; ok {
|
||||
return rbdVol, nil
|
||||
|
Reference in New Issue
Block a user