2019-05-28 19:03:18 +00:00
|
|
|
/*
|
|
|
|
Copyright 2019 The Ceph-CSI Authors.
|
|
|
|
|
|
|
|
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-05-28 19:03:18 +00:00
|
|
|
"github.com/ceph/ceph-csi/pkg/util"
|
|
|
|
|
|
|
|
"k8s.io/klog"
|
|
|
|
)
|
|
|
|
|
|
|
|
// volumeIdentifier structure contains an association between the CSI VolumeID to its subvolume
|
|
|
|
// name on the backing CephFS instance
|
|
|
|
type volumeIdentifier struct {
|
|
|
|
FsSubvolName string
|
|
|
|
VolumeID string
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
checkVolExists checks to determine if passed in RequestName in volOptions exists on the backend.
|
|
|
|
|
|
|
|
**NOTE:** These functions manipulate the rados omaps that hold information regarding
|
|
|
|
volume names as requested by the CSI drivers. Hence, these need to be invoked only when the
|
|
|
|
respective CSI driver generated volume name based locks are held, as otherwise racy
|
|
|
|
access to these omaps may end up leaving them in an inconsistent state.
|
|
|
|
|
|
|
|
These functions also cleanup omap reservations that are stale. I.e when omap entries exist and
|
|
|
|
backing subvolumes are missing, or one of the omaps exist and the next is missing. This is
|
|
|
|
because, the order of omap creation and deletion are inverse of each other, and protected by the
|
|
|
|
request name lock, and hence any stale omaps are leftovers from incomplete transactions and are
|
|
|
|
hence safe to garbage collect.
|
|
|
|
*/
|
2019-08-22 17:19:06 +00:00
|
|
|
func checkVolExists(ctx context.Context, volOptions *volumeOptions, secret map[string]string) (*volumeIdentifier, error) {
|
2020-01-24 16:26:56 +00:00
|
|
|
var vid volumeIdentifier
|
2019-05-28 19:03:18 +00:00
|
|
|
|
2019-06-25 19:29:17 +00:00
|
|
|
cr, err := util.NewAdminCredentials(secret)
|
2019-05-28 19:03:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-06-25 19:29:17 +00:00
|
|
|
defer cr.DeleteCredentials()
|
2019-05-28 19:03:18 +00:00
|
|
|
|
2020-01-24 16:26:56 +00:00
|
|
|
imageData, err := volJournal.CheckReservation(ctx, volOptions.Monitors, cr,
|
2020-02-24 13:19:42 +00:00
|
|
|
volOptions.MetadataPool, volOptions.RequestName, volOptions.NamePrefix, "", "")
|
2019-05-28 19:03:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-01-24 16:26:56 +00:00
|
|
|
if imageData == nil {
|
2019-05-28 19:03:18 +00:00
|
|
|
return nil, nil
|
|
|
|
}
|
2020-01-24 16:26:56 +00:00
|
|
|
imageUUID := imageData.ImageUUID
|
|
|
|
vid.FsSubvolName = imageData.ImageAttributes.ImageName
|
2019-05-28 19:03:18 +00:00
|
|
|
|
2020-02-24 05:29:08 +00:00
|
|
|
_, err = getVolumeRootPathCeph(ctx, volOptions, cr, volumeID(vid.FsSubvolName))
|
|
|
|
if err != nil {
|
|
|
|
if _, ok := err.(ErrVolumeNotFound); ok {
|
2020-01-24 16:26:56 +00:00
|
|
|
err = volJournal.UndoReservation(ctx, volOptions.Monitors, cr, volOptions.MetadataPool,
|
|
|
|
volOptions.MetadataPool, vid.FsSubvolName, volOptions.RequestName)
|
2020-02-24 05:29:08 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-01-24 16:26:56 +00:00
|
|
|
|
|
|
|
// check if topology constraints match what is found
|
|
|
|
// TODO: we need an API to fetch subvolume attributes (size/datapool and others), based
|
|
|
|
// on which we can evaluate which topology this belongs to.
|
|
|
|
// TODO: CephFS topology support is postponed till we get the same
|
2019-05-28 19:03:18 +00:00
|
|
|
// TODO: size checks
|
|
|
|
|
|
|
|
// found a volume already available, process and return it!
|
2020-01-24 16:26:56 +00:00
|
|
|
vid.VolumeID, err = util.GenerateVolID(ctx, volOptions.Monitors, cr, volOptions.FscID,
|
|
|
|
"", volOptions.ClusterID, imageUUID, volIDVersion)
|
2019-05-28 19:03:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-08-22 17:19:06 +00:00
|
|
|
klog.V(4).Infof(util.Log(ctx, "Found existing volume (%s) with subvolume name (%s) for request (%s)"),
|
2019-05-28 19:03:18 +00:00
|
|
|
vid.VolumeID, vid.FsSubvolName, volOptions.RequestName)
|
|
|
|
|
|
|
|
return &vid, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// undoVolReservation is a helper routine to undo a name reservation for a CSI VolumeName
|
2019-08-22 17:19:06 +00:00
|
|
|
func undoVolReservation(ctx context.Context, volOptions *volumeOptions, vid volumeIdentifier, secret map[string]string) error {
|
2019-06-25 19:29:17 +00:00
|
|
|
cr, err := util.NewAdminCredentials(secret)
|
2019-05-28 19:03:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-06-25 19:29:17 +00:00
|
|
|
defer cr.DeleteCredentials()
|
2019-05-28 19:03:18 +00:00
|
|
|
|
2019-08-22 17:19:06 +00:00
|
|
|
err = volJournal.UndoReservation(ctx, volOptions.Monitors, cr, volOptions.MetadataPool,
|
2020-01-24 16:26:56 +00:00
|
|
|
volOptions.MetadataPool, vid.FsSubvolName, volOptions.RequestName)
|
2019-05-28 19:03:18 +00:00
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-01-24 16:26:56 +00:00
|
|
|
func updateTopologyConstraints(volOpts *volumeOptions) error {
|
|
|
|
// update request based on topology constrained parameters (if present)
|
2020-04-06 20:19:13 +00:00
|
|
|
poolName, _, topology, err := util.FindPoolAndTopology(volOpts.TopologyPools, volOpts.TopologyRequirement)
|
2020-01-24 16:26:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if poolName != "" {
|
|
|
|
volOpts.Pool = poolName
|
|
|
|
volOpts.Topology = topology
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-05-28 19:03:18 +00:00
|
|
|
// reserveVol is a helper routine to request a UUID reservation for the CSI VolumeName and,
|
|
|
|
// to generate the volume identifier for the reserved UUID
|
2019-08-22 17:19:06 +00:00
|
|
|
func reserveVol(ctx context.Context, volOptions *volumeOptions, secret map[string]string) (*volumeIdentifier, error) {
|
2019-05-28 19:03:18 +00:00
|
|
|
var (
|
2020-02-24 13:19:42 +00:00
|
|
|
vid volumeIdentifier
|
|
|
|
imageUUID string
|
|
|
|
err error
|
2019-05-28 19:03:18 +00:00
|
|
|
)
|
|
|
|
|
2019-06-25 19:29:17 +00:00
|
|
|
cr, err := util.NewAdminCredentials(secret)
|
2019-05-28 19:03:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-06-25 19:29:17 +00:00
|
|
|
defer cr.DeleteCredentials()
|
2019-05-28 19:03:18 +00:00
|
|
|
|
2020-01-24 16:26:56 +00:00
|
|
|
err = updateTopologyConstraints(volOptions)
|
2019-05-28 19:03:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-01-24 16:26:56 +00:00
|
|
|
imageUUID, vid.FsSubvolName, err = volJournal.ReserveName(ctx, volOptions.Monitors, cr, volOptions.MetadataPool, util.InvalidPoolID,
|
|
|
|
volOptions.MetadataPool, util.InvalidPoolID, volOptions.RequestName, volOptions.NamePrefix, "", "")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2019-05-28 19:03:18 +00:00
|
|
|
}
|
2020-01-24 16:26:56 +00:00
|
|
|
|
|
|
|
// generate the volume ID to return to the CO system
|
|
|
|
vid.VolumeID, err = util.GenerateVolID(ctx, volOptions.Monitors, cr, volOptions.FscID,
|
|
|
|
"", volOptions.ClusterID, imageUUID, volIDVersion)
|
2019-05-28 19:03:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-08-22 17:19:06 +00:00
|
|
|
klog.V(4).Infof(util.Log(ctx, "Generated Volume ID (%s) and subvolume name (%s) for request name (%s)"),
|
2019-05-28 19:03:18 +00:00
|
|
|
vid.VolumeID, vid.FsSubvolName, volOptions.RequestName)
|
|
|
|
|
|
|
|
return &vid, nil
|
|
|
|
}
|