mirror of
https://github.com/cubefs/cubefs.git
synced 2026-08-02 02:00:56 +00:00
405 lines
9.7 KiB
Go
405 lines
9.7 KiB
Go
package master
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"math"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/cubefs/cubefs/proto"
|
|
"github.com/cubefs/cubefs/util/log"
|
|
"github.com/samsarahq/thunder/graphql"
|
|
"github.com/samsarahq/thunder/graphql/schemabuilder"
|
|
)
|
|
|
|
type VolumeService struct {
|
|
user *User
|
|
cluster *Cluster
|
|
}
|
|
|
|
func (s *VolumeService) Schema() *graphql.Schema {
|
|
schema := schemabuilder.NewSchema()
|
|
|
|
s.registerObject(schema)
|
|
s.registerQuery(schema)
|
|
s.registerMutation(schema)
|
|
|
|
return schema.MustBuild()
|
|
}
|
|
|
|
func (s *VolumeService) registerObject(schema *schemabuilder.Schema) {
|
|
object := schema.Object("Vol", Vol{})
|
|
|
|
object.FieldFunc("dpReplicaNum", func(ctx context.Context, v *Vol) (int32, error) {
|
|
if _, _, err := permissions(ctx, USER|ADMIN); err != nil {
|
|
return 0, err
|
|
}
|
|
return int32(v.dpReplicaNum), nil
|
|
})
|
|
|
|
object.FieldFunc("occupied", func(ctx context.Context, v *Vol) (int64, error) {
|
|
if _, _, err := permissions(ctx, USER|ADMIN); err != nil {
|
|
return 0, err
|
|
}
|
|
var used int64
|
|
for _, p := range v.cloneDataPartitionMap() {
|
|
used += int64(p.used)
|
|
}
|
|
return used, nil
|
|
})
|
|
|
|
object.FieldFunc("toSimpleVolView", func(ctx context.Context, vol *Vol) (*proto.SimpleVolView, error) {
|
|
if _, _, err := permissions(ctx, USER|ADMIN); err != nil {
|
|
return nil, err
|
|
}
|
|
return &proto.SimpleVolView{
|
|
ID: vol.ID,
|
|
Name: vol.Name,
|
|
Owner: vol.Owner,
|
|
ZoneName: vol.zoneName,
|
|
DpReplicaNum: vol.dpReplicaNum,
|
|
MpReplicaNum: vol.mpReplicaNum,
|
|
Status: vol.Status,
|
|
Capacity: vol.Capacity,
|
|
FollowerRead: vol.FollowerRead,
|
|
NeedToLowerReplica: vol.NeedToLowerReplica,
|
|
Authenticate: vol.authenticate,
|
|
CrossZone: vol.crossZone,
|
|
RwDpCnt: vol.dataPartitions.readableAndWritableCnt,
|
|
MpCnt: len(vol.MetaPartitions),
|
|
DpCnt: len(vol.dataPartitions.partitionMap),
|
|
CreateTime: time.Unix(vol.createTime, 0).Format(proto.TimeFormat),
|
|
Description: vol.description,
|
|
}, nil
|
|
})
|
|
|
|
object.FieldFunc("createTime", func(ctx context.Context, v *Vol) (int64, error) {
|
|
if _, _, err := permissions(ctx, USER|ADMIN); err != nil {
|
|
return 0, err
|
|
}
|
|
return v.createTime, nil
|
|
})
|
|
}
|
|
|
|
func (s *VolumeService) registerQuery(schema *schemabuilder.Schema) {
|
|
query := schema.Query()
|
|
|
|
query.FieldFunc("getVolume", s.getVolume)
|
|
query.FieldFunc("listVolume", s.listVolume)
|
|
query.FieldFunc("volPermission", s.volPermission)
|
|
}
|
|
|
|
func (s *VolumeService) registerMutation(schema *schemabuilder.Schema) {
|
|
mutation := schema.Mutation()
|
|
|
|
mutation.FieldFunc("createVolume", s.createVolume)
|
|
// mutation.FieldFunc("deleteVolume", s.markDeleteVol)
|
|
mutation.FieldFunc("updateVolume", s.updateVolume)
|
|
}
|
|
|
|
type UserPermission struct {
|
|
UserID string
|
|
Access []string
|
|
Edit bool
|
|
}
|
|
|
|
func (s *VolumeService) volPermission(ctx context.Context, args struct {
|
|
VolName string
|
|
UserID *string
|
|
},
|
|
) ([]*UserPermission, error) {
|
|
uid, perm, err := permissions(ctx, ADMIN|USER)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if perm == USER {
|
|
if args.UserID == nil {
|
|
return nil, fmt.Errorf("user:[%s] need set userID", uid)
|
|
}
|
|
if v, e := s.cluster.getVol(*args.UserID); e != nil {
|
|
return nil, e
|
|
} else {
|
|
if v.Owner != uid {
|
|
return nil, fmt.Errorf("user:[%s] is not volume:[%d] onwer", uid, args.UserID)
|
|
}
|
|
}
|
|
}
|
|
|
|
vol, err := s.cluster.getVol(args.VolName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var volUser *proto.VolUser
|
|
if value, exist := s.user.volUser.Load(args.VolName); exist {
|
|
volUser = value.(*proto.VolUser)
|
|
} else {
|
|
return nil, fmt.Errorf("not found vol user in cluster")
|
|
}
|
|
|
|
userPList := make([]*UserPermission, 0, len(volUser.UserIDs))
|
|
|
|
userMap := make(map[string]bool)
|
|
|
|
for _, u := range volUser.UserIDs {
|
|
v, e := s.user.getUserInfo(u)
|
|
if e != nil {
|
|
log.LogWarnf("get user info by vol has err:[%s]", e.Error())
|
|
continue
|
|
}
|
|
if arr, exist := v.Policy.AuthorizedVols[args.VolName]; exist {
|
|
if userMap[u] {
|
|
continue
|
|
}
|
|
userMap[u] = true
|
|
userPList = append(userPList, &UserPermission{
|
|
UserID: u,
|
|
Access: arr,
|
|
Edit: uid == vol.Owner,
|
|
})
|
|
} else {
|
|
log.LogWarnf("get vol:[%s] author:[%s] by user policy has err ", args.VolName, u)
|
|
}
|
|
}
|
|
|
|
sort.Slice(userPList, func(i, j int) bool {
|
|
return userPList[i].Edit
|
|
})
|
|
|
|
return userPList, nil
|
|
}
|
|
|
|
func (s *VolumeService) createVolume(ctx context.Context, args struct {
|
|
Name, Owner, ZoneName, Description string
|
|
Capacity, DataPartitionSize, MpCount, DpCount, DpReplicaNum uint64
|
|
FollowerRead, Authenticate, CrossZone, DefaultPriority bool
|
|
iopsRLimit, iopsWLimit, flowRlimit, flowWlimit uint64
|
|
},
|
|
) (*Vol, error) {
|
|
uid, per, err := permissions(ctx, ADMIN|USER)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !(args.DpReplicaNum == 2 || args.DpReplicaNum == 3) {
|
|
return nil, fmt.Errorf("replicaNum can only be 2 and 3,received replicaNum is[%v]", args.DpReplicaNum)
|
|
}
|
|
|
|
if per == USER && args.Owner != uid {
|
|
return nil, fmt.Errorf("[%s] not has permission to create volume for [%s]", uid, args.Owner)
|
|
}
|
|
|
|
if args.DpReplicaNum > math.MaxUint8 {
|
|
return nil, fmt.Errorf("invalid arg dpReplicaNum: %v", args.DpReplicaNum)
|
|
}
|
|
|
|
if args.DpCount > maxInitDataPartitionCnt {
|
|
return nil, fmt.Errorf("invalid arg dpCount[%v], exceeds maximum limit[%d]", args.DpCount, maxInitDataPartitionCnt)
|
|
}
|
|
|
|
req := &createVolReq{
|
|
name: args.Name,
|
|
owner: args.Owner,
|
|
dpSize: int(args.DataPartitionSize),
|
|
mpCount: int(args.MpCount),
|
|
dpCount: int(args.DpCount),
|
|
dpReplicaNum: uint8(args.DpReplicaNum),
|
|
capacity: int(args.Capacity),
|
|
followerRead: args.FollowerRead,
|
|
authenticate: args.Authenticate,
|
|
crossZone: args.CrossZone,
|
|
normalZonesFirst: args.DefaultPriority,
|
|
zoneName: args.ZoneName,
|
|
description: args.Description,
|
|
}
|
|
vol, err := s.cluster.createVol(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
userInfo, err := s.user.getUserInfo(args.Owner)
|
|
if err != nil {
|
|
if err != proto.ErrUserNotExists {
|
|
return nil, err
|
|
}
|
|
|
|
param := proto.UserCreateParam{
|
|
ID: args.Owner,
|
|
Password: DefaultUserPassword,
|
|
Type: proto.UserTypeNormal,
|
|
}
|
|
if userInfo, err = s.user.createKey(¶m); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if _, err = s.user.addOwnVol(userInfo.UserID, args.Name); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return vol, nil
|
|
}
|
|
|
|
func (s *VolumeService) markDeleteVol(ctx context.Context, args struct {
|
|
Name, AuthKey string
|
|
},
|
|
) (*proto.GeneralResp, error) {
|
|
uid, perm, err := permissions(ctx, ADMIN|USER)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if perm == USER {
|
|
if v, e := s.cluster.getVol(args.Name); e != nil {
|
|
return nil, e
|
|
} else {
|
|
if v.Owner != uid {
|
|
return nil, fmt.Errorf("user:[%s] is not volume:[%s] onwer", uid, args.Name)
|
|
}
|
|
}
|
|
}
|
|
|
|
if err = s.user.deleteVolPolicy(args.Name); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err = s.cluster.markDeleteVol(args.Name, args.AuthKey, false, true); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
log.LogWarnf("delete vol[%s] successfully,from[%s]", args.Name, uid)
|
|
|
|
return proto.Success("success"), nil
|
|
}
|
|
|
|
func (s *VolumeService) updateVolume(ctx context.Context, args struct {
|
|
Name, AuthKey string
|
|
ZoneName, Description *string
|
|
Capacity, ReplicaNum *uint64
|
|
FollowerRead, Authenticate *bool
|
|
},
|
|
) (*Vol, error) {
|
|
uid, perm, err := permissions(ctx, ADMIN|USER)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if perm == USER {
|
|
if v, e := s.cluster.getVol(args.Name); e != nil {
|
|
return nil, e
|
|
} else {
|
|
if v.Owner != uid {
|
|
return nil, fmt.Errorf("user:[%s] is not volume:[%s] onwer", uid, args.Name)
|
|
}
|
|
}
|
|
}
|
|
|
|
if args.ReplicaNum != nil && !(*args.ReplicaNum == 2 || *args.ReplicaNum == 3) {
|
|
return nil, fmt.Errorf("replicaNum can only be 2 and 3,received replicaNum is[%v]", args.ReplicaNum)
|
|
}
|
|
|
|
vol, err := s.cluster.getVol(args.Name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
newArgs := getVolVarargs(vol)
|
|
|
|
if args.FollowerRead != nil {
|
|
newArgs.followerRead = *args.FollowerRead
|
|
}
|
|
|
|
if args.Authenticate != nil {
|
|
newArgs.authenticate = *args.Authenticate
|
|
}
|
|
|
|
if args.ZoneName != nil {
|
|
newArgs.zoneName = *args.ZoneName
|
|
}
|
|
|
|
if args.Capacity != nil {
|
|
newArgs.capacity = *args.Capacity
|
|
}
|
|
|
|
if args.Description != nil {
|
|
newArgs.description = *args.Description
|
|
}
|
|
|
|
if err = s.cluster.updateVol(args.Name, args.AuthKey, newArgs); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
log.LogInfof("update vol[%v] successfully\n", args.Name)
|
|
|
|
vol, err = s.cluster.getVol(args.Name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return vol, nil
|
|
}
|
|
|
|
func (s *VolumeService) listVolume(ctx context.Context, args struct {
|
|
UserID *string
|
|
Keyword *string
|
|
},
|
|
) ([]*Vol, error) {
|
|
uid, perm, err := permissions(ctx, ADMIN|USER)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if perm == USER {
|
|
args.UserID = &uid
|
|
}
|
|
|
|
var list []*Vol
|
|
for _, vol := range s.cluster.vols {
|
|
if args.UserID != nil && vol.Owner != *args.UserID {
|
|
continue
|
|
}
|
|
|
|
if args.Keyword != nil && *args.Keyword != "" && strings.Contains(vol.Name, *args.Keyword) {
|
|
continue
|
|
}
|
|
|
|
if (vol.Status == proto.VolStatusMarkDelete && !vol.Forbidden) ||
|
|
(vol.Status == proto.VolStatusMarkDelete && vol.Forbidden && time.Until(vol.DeleteExecTime) <= 0) {
|
|
continue
|
|
}
|
|
|
|
list = append(list, vol)
|
|
}
|
|
return list, nil
|
|
}
|
|
|
|
func (s *VolumeService) getVolume(ctx context.Context, args struct {
|
|
Name string
|
|
},
|
|
) (*Vol, error,
|
|
) {
|
|
uid, perm, err := permissions(ctx, ADMIN|USER)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if perm == USER {
|
|
if v, e := s.cluster.getVol(args.Name); e != nil {
|
|
return nil, e
|
|
} else {
|
|
if v.Owner != uid {
|
|
return nil, fmt.Errorf("user:[%s] is not volume:[%s] onwer", uid, args.Name)
|
|
}
|
|
}
|
|
}
|
|
|
|
vol, err := s.cluster.getVol(args.Name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return vol, nil
|
|
}
|