cephfs: refactor cephfs core functions

This commits refactors the cephfs core
functions with interfaces. This helps in
better code structuring and writing the
unit test cases.

update #852

Signed-off-by: Madhu Rajanna <madhupr007@gmail.com>
This commit is contained in:
Madhu Rajanna
2022-02-15 17:41:09 +05:30
committed by mergify[bot]
parent f19ca4a473
commit e9802c4940
12 changed files with 416 additions and 316 deletions

View File

@ -53,20 +53,75 @@ type Subvolume struct {
Features []string
}
// SubVolumeClient is the interface that holds the signature of subvolume methods
// that interacts with CephFS subvolume API's.
type SubVolumeClient interface {
// GetVolumeRootPathCeph returns the root path of the subvolume.
GetVolumeRootPathCeph(ctx context.Context) (string, error)
// CreateVolume creates a subvolume.
CreateVolume(ctx context.Context) error
// GetSubVolumeInfo returns the subvolume information.
GetSubVolumeInfo(ctx context.Context) (*Subvolume, error)
// ExpandVolume expands the volume if the requested size is greater than
// the subvolume size.
ExpandVolume(ctx context.Context, bytesQuota int64) error
// ResizeVolume resizes the volume.
ResizeVolume(ctx context.Context, bytesQuota int64) error
// PurgSubVolume removes the subvolume.
PurgeVolume(ctx context.Context, force bool) error
// CreateCloneFromSubVolume creates a clone from the subvolume.
CreateCloneFromSubvolume(ctx context.Context, parentvolOpt *SubVolume) error
// GetCloneState returns the clone state of the subvolume.
GetCloneState(ctx context.Context) (cephFSCloneState, error)
// CreateCloneFromSnapshot creates a clone from the subvolume snapshot.
CreateCloneFromSnapshot(ctx context.Context, snap Snapshot) error
// CleanupSnapshotFromSubvolume removes the snapshot from the subvolume.
CleanupSnapshotFromSubvolume(ctx context.Context, parentVol *SubVolume) error
}
// subVolumeClient implements SubVolumeClient interface.
type subVolumeClient struct {
*SubVolume // Embedded SubVolume struct.
clusterID string // Cluster ID to check subvolumegroup and resize functionality.
conn *util.ClusterConnection // Cluster connection.
}
// SubVolume holds the information about the subvolume.
type SubVolume struct {
VolID string // subvolume id.
FsName string // filesystem name.
SubvolumeGroup string // subvolume group name where subvolume will be created.
Pool string // pool name where subvolume will be created.
Features []string // subvolume features.
Size int64 // subvolume size.
}
// NewSubVolume returns a new subvolume client.
func NewSubVolume(conn *util.ClusterConnection, vol *SubVolume, clusterID string) SubVolumeClient {
return &subVolumeClient{
SubVolume: vol,
clusterID: clusterID,
conn: conn,
}
}
// GetVolumeRootPathCephDeprecated returns the root path of the subvolume.
func GetVolumeRootPathCephDeprecated(volID fsutil.VolumeID) string {
return path.Join("/", "csi-volumes", string(volID))
}
func (vo *VolumeOptions) GetVolumeRootPathCeph(ctx context.Context, volID fsutil.VolumeID) (string, error) {
fsa, err := vo.conn.GetFSAdmin()
// GetVolumeRootPathCeph returns the root path of the subvolume.
func (s *subVolumeClient) GetVolumeRootPathCeph(ctx context.Context) (string, error) {
fsa, err := s.conn.GetFSAdmin()
if err != nil {
log.ErrorLog(ctx, "could not get FSAdmin err %s", err)
return "", err
}
svPath, err := fsa.SubVolumePath(vo.FsName, vo.SubvolumeGroup, string(volID))
svPath, err := fsa.SubVolumePath(s.FsName, s.SubvolumeGroup, s.VolID)
if err != nil {
log.ErrorLog(ctx, "failed to get the rootpath for the vol %s: %s", string(volID), err)
log.ErrorLog(ctx, "failed to get the rootpath for the vol %s: %s", s.VolID, err)
if errors.Is(err, rados.ErrNotFound) {
return "", util.JoinErrors(cerrors.ErrVolumeNotFound, err)
}
@ -77,17 +132,18 @@ func (vo *VolumeOptions) GetVolumeRootPathCeph(ctx context.Context, volID fsutil
return svPath, nil
}
func (vo *VolumeOptions) GetSubVolumeInfo(ctx context.Context, volID fsutil.VolumeID) (*Subvolume, error) {
fsa, err := vo.conn.GetFSAdmin()
// GetSubVolumeInfo returns the subvolume information.
func (s *subVolumeClient) GetSubVolumeInfo(ctx context.Context) (*Subvolume, error) {
fsa, err := s.conn.GetFSAdmin()
if err != nil {
log.ErrorLog(ctx, "could not get FSAdmin, can not fetch metadata pool for %s:", vo.FsName, err)
log.ErrorLog(ctx, "could not get FSAdmin, can not fetch metadata pool for %s:", s.FsName, err)
return nil, err
}
info, err := fsa.SubVolumeInfo(vo.FsName, vo.SubvolumeGroup, string(volID))
info, err := fsa.SubVolumeInfo(s.FsName, s.SubvolumeGroup, s.VolID)
if err != nil {
log.ErrorLog(ctx, "failed to get subvolume info for the vol %s: %s", string(volID), err)
log.ErrorLog(ctx, "failed to get subvolume info for the vol %s: %s", s.VolID, err)
if errors.Is(err, rados.ErrNotFound) {
return nil, cerrors.ErrVolumeNotFound
}
@ -111,7 +167,7 @@ func (vo *VolumeOptions) GetSubVolumeInfo(ctx context.Context, volID fsutil.Volu
// or nil (in case the subvolume is in snapshot-retained state),
// just continue without returning quota information.
if !(info.BytesQuota == fsAdmin.Infinite || info.State == fsAdmin.StateSnapRetained) {
return nil, fmt.Errorf("subvolume %s has unsupported quota: %v", string(volID), info.BytesQuota)
return nil, fmt.Errorf("subvolume %s has unsupported quota: %v", s.VolID, info.BytesQuota)
}
} else {
subvol.BytesQuota = int64(bc)
@ -140,50 +196,52 @@ type localClusterState struct {
subVolumeGroupCreated bool
}
func CreateVolume(ctx context.Context, volOptions *VolumeOptions, volID fsutil.VolumeID, bytesQuota int64) error {
// verify if corresponding ClusterID key is present in the map,
// CreateVolume creates a subvolume.
func (s *subVolumeClient) CreateVolume(ctx context.Context) error {
// verify if corresponding clusterID key is present in the map,
// and if not, initialize with default values(false).
if _, keyPresent := clusterAdditionalInfo[volOptions.ClusterID]; !keyPresent {
clusterAdditionalInfo[volOptions.ClusterID] = &localClusterState{}
if _, keyPresent := clusterAdditionalInfo[s.clusterID]; !keyPresent {
clusterAdditionalInfo[s.clusterID] = &localClusterState{}
}
ca, err := volOptions.conn.GetFSAdmin()
ca, err := s.conn.GetFSAdmin()
if err != nil {
log.ErrorLog(ctx, "could not get FSAdmin, can not create subvolume %s: %s", string(volID), err)
log.ErrorLog(ctx, "could not get FSAdmin, can not create subvolume %s: %s", s.VolID, err)
return err
}
// create subvolumegroup if not already created for the cluster.
if !clusterAdditionalInfo[volOptions.ClusterID].subVolumeGroupCreated {
if !clusterAdditionalInfo[s.clusterID].subVolumeGroupCreated {
opts := fsAdmin.SubVolumeGroupOptions{}
err = ca.CreateSubVolumeGroup(volOptions.FsName, volOptions.SubvolumeGroup, &opts)
err = ca.CreateSubVolumeGroup(s.FsName, s.SubvolumeGroup, &opts)
if err != nil {
log.ErrorLog(
ctx,
"failed to create subvolume group %s, for the vol %s: %s",
volOptions.SubvolumeGroup,
string(volID),
s.SubvolumeGroup,
s.VolID,
err)
return err
}
log.DebugLog(ctx, "cephfs: created subvolume group %s", volOptions.SubvolumeGroup)
clusterAdditionalInfo[volOptions.ClusterID].subVolumeGroupCreated = true
log.DebugLog(ctx, "cephfs: created subvolume group %s", s.SubvolumeGroup)
clusterAdditionalInfo[s.clusterID].subVolumeGroupCreated = true
}
opts := fsAdmin.SubVolumeOptions{
Size: fsAdmin.ByteCount(bytesQuota),
Size: fsAdmin.ByteCount(s.Size),
Mode: modeAllRWX,
}
if volOptions.Pool != "" {
opts.PoolLayout = volOptions.Pool
if s.Pool != "" {
opts.PoolLayout = s.Pool
}
fmt.Println("this is for debugging ")
// FIXME: check if the right credentials are used ("-n", cephEntityClientPrefix + cr.ID)
err = ca.CreateSubVolume(volOptions.FsName, volOptions.SubvolumeGroup, string(volID), &opts)
err = ca.CreateSubVolume(s.FsName, s.SubvolumeGroup, s.VolID, &opts)
if err != nil {
log.ErrorLog(ctx, "failed to create subvolume %s in fs %s: %s", string(volID), volOptions.FsName, err)
log.ErrorLog(ctx, "failed to create subvolume %s in fs %s: %s", s.VolID, s.FsName, err)
return err
}
@ -193,16 +251,16 @@ func CreateVolume(ctx context.Context, volOptions *VolumeOptions, volID fsutil.V
// ExpandVolume will expand the volume if the requested size is greater than
// the subvolume size.
func (vo *VolumeOptions) ExpandVolume(ctx context.Context, volID fsutil.VolumeID, bytesQuota int64) error {
func (s *subVolumeClient) ExpandVolume(ctx context.Context, bytesQuota int64) error {
// get the subvolume size for comparison with the requested size.
info, err := vo.GetSubVolumeInfo(ctx, volID)
info, err := s.GetSubVolumeInfo(ctx)
if err != nil {
return err
}
// resize if the requested size is greater than the current size.
if vo.Size > info.BytesQuota {
log.DebugLog(ctx, "clone %s size %d is greater than requested size %d", volID, info.BytesQuota, bytesQuota)
err = vo.ResizeVolume(ctx, volID, bytesQuota)
if s.Size > info.BytesQuota {
log.DebugLog(ctx, "clone %s size %d is greater than requested size %d", s.VolID, info.BytesQuota, bytesQuota)
err = s.ResizeVolume(ctx, bytesQuota)
}
return err
@ -211,45 +269,47 @@ func (vo *VolumeOptions) ExpandVolume(ctx context.Context, volID fsutil.VolumeID
// 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 (vo *VolumeOptions) ResizeVolume(ctx context.Context, volID fsutil.VolumeID, bytesQuota int64) error {
func (s *subVolumeClient) ResizeVolume(ctx context.Context, bytesQuota int64) error {
// keyPresent checks whether corresponding clusterID key is present in clusterAdditionalInfo
var keyPresent bool
// verify if corresponding ClusterID key is present in the map,
// verify if corresponding clusterID key is present in the map,
// and if not, initialize with default values(false).
if _, keyPresent = clusterAdditionalInfo[vo.ClusterID]; !keyPresent {
clusterAdditionalInfo[vo.ClusterID] = &localClusterState{}
if _, keyPresent = clusterAdditionalInfo[s.clusterID]; !keyPresent {
clusterAdditionalInfo[s.clusterID] = &localClusterState{}
}
// resize subvolume when either it's supported, or when corresponding
// clusterID key was not present.
if clusterAdditionalInfo[vo.ClusterID].resizeState == unknown ||
clusterAdditionalInfo[vo.ClusterID].resizeState == supported {
fsa, err := vo.conn.GetFSAdmin()
if clusterAdditionalInfo[s.clusterID].resizeState == unknown ||
clusterAdditionalInfo[s.clusterID].resizeState == supported {
fsa, err := s.conn.GetFSAdmin()
if err != nil {
log.ErrorLog(ctx, "could not get FSAdmin, can not resize volume %s:", vo.FsName, err)
log.ErrorLog(ctx, "could not get FSAdmin, can not resize volume %s:", s.FsName, err)
return err
}
_, err = fsa.ResizeSubVolume(vo.FsName, vo.SubvolumeGroup, string(volID), fsAdmin.ByteCount(bytesQuota), true)
_, err = fsa.ResizeSubVolume(s.FsName, s.SubvolumeGroup, s.VolID, fsAdmin.ByteCount(bytesQuota), true)
if err == nil {
clusterAdditionalInfo[vo.ClusterID].resizeState = supported
clusterAdditionalInfo[s.clusterID].resizeState = supported
return nil
}
var invalid fsAdmin.NotImplementedError
// In case the error is other than invalid command return error to the caller.
if !errors.As(err, &invalid) {
log.ErrorLog(ctx, "failed to resize subvolume %s in fs %s: %s", string(volID), vo.FsName, err)
log.ErrorLog(ctx, "failed to resize subvolume %s in fs %s: %s", s.VolID, s.FsName, err)
return err
}
}
clusterAdditionalInfo[vo.ClusterID].resizeState = unsupported
clusterAdditionalInfo[s.clusterID].resizeState = unsupported
s.Size = bytesQuota
return CreateVolume(ctx, vo, volID, bytesQuota)
return s.CreateVolume(ctx)
}
func (vo *VolumeOptions) PurgeVolume(ctx context.Context, volID fsutil.VolumeID, force bool) error {
fsa, err := vo.conn.GetFSAdmin()
// PurgSubVolume removes the subvolume.
func (s *subVolumeClient) PurgeVolume(ctx context.Context, force bool) error {
fsa, err := s.conn.GetFSAdmin()
if err != nil {
log.ErrorLog(ctx, "could not get FSAdmin %s:", err)
@ -259,13 +319,13 @@ func (vo *VolumeOptions) PurgeVolume(ctx context.Context, volID fsutil.VolumeID,
opt := fsAdmin.SubVolRmFlags{}
opt.Force = force
if checkSubvolumeHasFeature("snapshot-retention", vo.Features) {
if checkSubvolumeHasFeature("snapshot-retention", s.Features) {
opt.RetainSnapshots = true
}
err = fsa.RemoveSubVolumeWithFlags(vo.FsName, vo.SubvolumeGroup, string(volID), opt)
err = fsa.RemoveSubVolumeWithFlags(s.FsName, s.SubvolumeGroup, s.VolID, opt)
if err != nil {
log.ErrorLog(ctx, "failed to purge subvolume %s in fs %s: %s", string(volID), vo.FsName, err)
log.ErrorLog(ctx, "failed to purge subvolume %s in fs %s: %s", s.VolID, s.FsName, err)
if strings.Contains(err.Error(), cerrors.VolumeNotEmpty) {
return util.JoinErrors(cerrors.ErrVolumeHasSnapshots, err)
}