github.com/cs3org/reva/v2@v2.27.7/pkg/storage/fs/cephfs/connections.go (about)

     1  // Copyright 2018-2021 CERN
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // In applying this license, CERN does not waive the privileges and immunities
    16  // granted to it by virtue of its status as an Intergovernmental Organization
    17  // or submit itself to any jurisdiction.
    18  
    19  //go:build ceph
    20  // +build ceph
    21  
    22  package cephfs
    23  
    24  import (
    25  	"context"
    26  	"fmt"
    27  	"time"
    28  
    29  	"github.com/ceph/go-ceph/cephfs/admin"
    30  	rados2 "github.com/ceph/go-ceph/rados"
    31  	grouppb "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
    32  	userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    33  	rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
    34  	"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
    35  	"github.com/pkg/errors"
    36  
    37  	cephfs2 "github.com/ceph/go-ceph/cephfs"
    38  	"github.com/dgraph-io/ristretto"
    39  	"golang.org/x/sync/semaphore"
    40  )
    41  
    42  type cacheVal struct {
    43  	perm  *cephfs2.UserPerm
    44  	mount *cephfs2.MountInfo
    45  }
    46  
    47  //TODO: Add to cephfs obj
    48  
    49  type connections struct {
    50  	cache      *ristretto.Cache
    51  	lock       *semaphore.Weighted
    52  	ctx        context.Context
    53  	userCache  *ristretto.Cache
    54  	groupCache *ristretto.Cache
    55  }
    56  
    57  // TODO: make configurable/add to options
    58  var usrLimit int64 = 1e4
    59  
    60  func newCache() (c *connections, err error) {
    61  	cache, err := ristretto.NewCache(&ristretto.Config{
    62  		NumCounters: 1e7,
    63  		MaxCost:     usrLimit,
    64  		BufferItems: 64,
    65  		OnEvict: func(item *ristretto.Item) {
    66  			v := item.Value.(cacheVal)
    67  			v.perm.Destroy()
    68  			_ = v.mount.Unmount()
    69  			_ = v.mount.Release()
    70  		},
    71  	})
    72  	if err != nil {
    73  		return
    74  	}
    75  
    76  	ucache, err := ristretto.NewCache(&ristretto.Config{
    77  		NumCounters: 1e7,
    78  		MaxCost:     10 * usrLimit,
    79  		BufferItems: 64,
    80  	})
    81  	if err != nil {
    82  		return
    83  	}
    84  
    85  	gcache, err := ristretto.NewCache(&ristretto.Config{
    86  		NumCounters: 1e7,
    87  		MaxCost:     10 * usrLimit,
    88  		BufferItems: 64,
    89  	})
    90  	if err != nil {
    91  		return
    92  	}
    93  
    94  	c = &connections{
    95  		cache:      cache,
    96  		lock:       semaphore.NewWeighted(usrLimit),
    97  		ctx:        context.Background(),
    98  		userCache:  ucache,
    99  		groupCache: gcache,
   100  	}
   101  
   102  	return
   103  }
   104  
   105  func (c *connections) clearCache() {
   106  	c.cache.Clear()
   107  	c.cache.Close()
   108  }
   109  
   110  type adminConn struct {
   111  	indexPoolName string
   112  	subvolAdmin   *admin.FSAdmin
   113  	adminMount    Mount
   114  	radosConn     *rados2.Conn
   115  	radosIO       *rados2.IOContext
   116  }
   117  
   118  func newAdminConn(poolName string) *adminConn {
   119  	rados, err := rados2.NewConn()
   120  	if err != nil {
   121  		return nil
   122  	}
   123  	if err = rados.ReadDefaultConfigFile(); err != nil {
   124  		return nil
   125  	}
   126  
   127  	if err = rados.Connect(); err != nil {
   128  		return nil
   129  	}
   130  
   131  	pools, err := rados.ListPools()
   132  	if err != nil {
   133  		rados.Shutdown()
   134  		return nil
   135  	}
   136  
   137  	var radosIO *rados2.IOContext
   138  	if in(poolName, pools) {
   139  		radosIO, err = rados.OpenIOContext(poolName)
   140  		if err != nil {
   141  			rados.Shutdown()
   142  			return nil
   143  		}
   144  	} else {
   145  		err = rados.MakePool(poolName)
   146  		if err != nil {
   147  			rados.Shutdown()
   148  			return nil
   149  		}
   150  		radosIO, err = rados.OpenIOContext(poolName)
   151  		if err != nil {
   152  			rados.Shutdown()
   153  			return nil
   154  		}
   155  	}
   156  
   157  	mount, err := cephfs2.CreateFromRados(rados)
   158  	if err != nil {
   159  		rados.Shutdown()
   160  		return nil
   161  	}
   162  
   163  	if err = mount.Mount(); err != nil {
   164  		rados.Shutdown()
   165  		destroyCephConn(mount, nil)
   166  		return nil
   167  	}
   168  
   169  	return &adminConn{
   170  		poolName,
   171  		admin.NewFromConn(rados),
   172  		mount,
   173  		rados,
   174  		radosIO,
   175  	}
   176  }
   177  
   178  func newConn(user *User) *cacheVal {
   179  	var perm *cephfs2.UserPerm
   180  	mount, err := cephfs2.CreateMount()
   181  	if err != nil {
   182  		return destroyCephConn(mount, perm)
   183  	}
   184  	if err = mount.ReadDefaultConfigFile(); err != nil {
   185  		return destroyCephConn(mount, perm)
   186  	}
   187  	if err = mount.Init(); err != nil {
   188  		return destroyCephConn(mount, perm)
   189  	}
   190  
   191  	if user != nil { //nil creates admin conn
   192  		perm = cephfs2.NewUserPerm(int(user.UidNumber), int(user.GidNumber), []int{})
   193  		if err = mount.SetMountPerms(perm); err != nil {
   194  			return destroyCephConn(mount, perm)
   195  		}
   196  	}
   197  
   198  	if err = mount.MountWithRoot("/"); err != nil {
   199  		return destroyCephConn(mount, perm)
   200  	}
   201  
   202  	if user != nil {
   203  		if err = mount.ChangeDir(user.fs.conf.Root); err != nil {
   204  			return destroyCephConn(mount, perm)
   205  		}
   206  	}
   207  
   208  	return &cacheVal{
   209  		perm:  perm,
   210  		mount: mount,
   211  	}
   212  }
   213  
   214  func (fs *cephfs) getUserByID(ctx context.Context, uid string) (*userpb.User, error) {
   215  	if entity, found := fs.conn.userCache.Get(uid); found {
   216  		return entity.(*userpb.User), nil
   217  	}
   218  
   219  	client, err := pool.GetGatewayServiceClient(fs.conf.GatewaySvc)
   220  	if err != nil {
   221  		return nil, errors.Wrap(err, "cephfs: error getting gateway grpc client")
   222  	}
   223  	getUserResp, err := client.GetUserByClaim(ctx, &userpb.GetUserByClaimRequest{
   224  		Claim: "uid",
   225  		Value: uid,
   226  	})
   227  
   228  	if err != nil {
   229  		return nil, errors.Wrap(err, "cephfs: error getting user")
   230  	}
   231  	if getUserResp.Status.Code != rpc.Code_CODE_OK {
   232  		return nil, errors.Wrap(err, "cephfs: grpc get user failed")
   233  	}
   234  	fs.conn.userCache.SetWithTTL(uid, getUserResp.User, 1, 24*time.Hour)
   235  	fs.conn.userCache.SetWithTTL(getUserResp.User.Id.OpaqueId, getUserResp.User, 1, 24*time.Hour)
   236  
   237  	return getUserResp.User, nil
   238  }
   239  
   240  func (fs *cephfs) getUserByOpaqueID(ctx context.Context, oid string) (*userpb.User, error) {
   241  	if entity, found := fs.conn.userCache.Get(oid); found {
   242  		return entity.(*userpb.User), nil
   243  	}
   244  	client, err := pool.GetGatewayServiceClient(fs.conf.GatewaySvc)
   245  	if err != nil {
   246  		return nil, errors.Wrap(err, "cephfs: error getting gateway grpc client")
   247  	}
   248  	getUserResp, err := client.GetUser(ctx, &userpb.GetUserRequest{
   249  		UserId: &userpb.UserId{
   250  			OpaqueId: oid,
   251  		},
   252  	})
   253  
   254  	if err != nil {
   255  		return nil, errors.Wrap(err, "cephfs: error getting user")
   256  	}
   257  	if getUserResp.Status.Code != rpc.Code_CODE_OK {
   258  		return nil, errors.Wrap(err, "cephfs: grpc get user failed")
   259  	}
   260  	fs.conn.userCache.SetWithTTL(fmt.Sprint(getUserResp.User.UidNumber), getUserResp.User, 1, 24*time.Hour)
   261  	fs.conn.userCache.SetWithTTL(oid, getUserResp.User, 1, 24*time.Hour)
   262  
   263  	return getUserResp.User, nil
   264  }
   265  
   266  func (fs *cephfs) getGroupByID(ctx context.Context, gid string) (*grouppb.Group, error) {
   267  	if entity, found := fs.conn.groupCache.Get(gid); found {
   268  		return entity.(*grouppb.Group), nil
   269  	}
   270  
   271  	client, err := pool.GetGatewayServiceClient(fs.conf.GatewaySvc)
   272  	if err != nil {
   273  		return nil, errors.Wrap(err, "cephfs: error getting gateway grpc client")
   274  	}
   275  	getGroupResp, err := client.GetGroupByClaim(ctx, &grouppb.GetGroupByClaimRequest{
   276  		Claim: "gid",
   277  		Value: gid,
   278  	})
   279  	if err != nil {
   280  		return nil, errors.Wrap(err, "cephfs: error getting group")
   281  	}
   282  	if getGroupResp.Status.Code != rpc.Code_CODE_OK {
   283  		return nil, errors.Wrap(err, "cephfs: grpc get group failed")
   284  	}
   285  	fs.conn.groupCache.SetWithTTL(gid, getGroupResp.Group, 1, 24*time.Hour)
   286  	fs.conn.groupCache.SetWithTTL(getGroupResp.Group.Id.OpaqueId, getGroupResp.Group, 1, 24*time.Hour)
   287  
   288  	return getGroupResp.Group, nil
   289  }
   290  
   291  func (fs *cephfs) getGroupByOpaqueID(ctx context.Context, oid string) (*grouppb.Group, error) {
   292  	if entity, found := fs.conn.groupCache.Get(oid); found {
   293  		return entity.(*grouppb.Group), nil
   294  	}
   295  	client, err := pool.GetGatewayServiceClient(fs.conf.GatewaySvc)
   296  	if err != nil {
   297  		return nil, errors.Wrap(err, "cephfs: error getting gateway grpc client")
   298  	}
   299  	getGroupResp, err := client.GetGroup(ctx, &grouppb.GetGroupRequest{
   300  		GroupId: &grouppb.GroupId{
   301  			OpaqueId: oid,
   302  		},
   303  	})
   304  
   305  	if err != nil {
   306  		return nil, errors.Wrap(err, "cephfs: error getting group")
   307  	}
   308  	if getGroupResp.Status.Code != rpc.Code_CODE_OK {
   309  		return nil, errors.Wrap(err, "cephfs: grpc get group failed")
   310  	}
   311  	fs.conn.userCache.SetWithTTL(fmt.Sprint(getGroupResp.Group.GidNumber), getGroupResp.Group, 1, 24*time.Hour)
   312  	fs.conn.userCache.SetWithTTL(oid, getGroupResp.Group, 1, 24*time.Hour)
   313  
   314  	return getGroupResp.Group, nil
   315  }