From 7d8c78692e499780191e35e9b443eb39dea44646 Mon Sep 17 00:00:00 2001 From: Hanqing Wu Date: Fri, 15 Dec 2023 16:36:52 +0800 Subject: [PATCH] [feat]tools-v2: Implement bs list poolset command Signed-off-by: Hanqing Wu --- proto/nameserver2.proto | 3 + src/mds/nameserver2/curvefs.cpp | 21 +- src/mds/nameserver2/curvefs.h | 9 +- src/mds/nameserver2/namespace_service.cpp | 24 +- test/mds/nameserver2/curvefs_test.cpp | 100 ++++++- tools-v2/internal/utils/row.go | 2 + tools-v2/pkg/cli/command/curvebs/list/list.go | 2 + .../curvebs/list/logicalPool/logicalPool.go | 26 +- .../command/curvebs/list/poolset/poolset.go | 248 ++++++++++++++++++ .../cli/command/curvebs/list/space/space.go | 16 +- .../command/curvebs/query/file/filesize.go | 40 ++- 11 files changed, 443 insertions(+), 48 deletions(-) create mode 100644 tools-v2/pkg/cli/command/curvebs/list/poolset/poolset.go diff --git a/proto/nameserver2.proto b/proto/nameserver2.proto index 85947d96ad..be99a532f7 100644 --- a/proto/nameserver2.proto +++ b/proto/nameserver2.proto @@ -539,12 +539,15 @@ message GetAllocatedSizeResponse { message GetFileSizeRequest { required string fileName = 1; + optional bool groupByPoolset = 2 [default = false]; } message GetFileSizeResponse { required StatusCode statusCode = 1; // 文件或目录的file length optional uint64 fileSize = 2; + // key is poolset name and value is total file size on this poolset + map fileSizeMap = 3; } message ClientInfo { diff --git a/src/mds/nameserver2/curvefs.cpp b/src/mds/nameserver2/curvefs.cpp index 5d5af4d75f..ddc728eea9 100644 --- a/src/mds/nameserver2/curvefs.cpp +++ b/src/mds/nameserver2/curvefs.cpp @@ -512,9 +512,9 @@ StatusCode CurveFS::GetDirAllocSize(const std::string& fileName, return StatusCode::kOK; } -StatusCode CurveFS::GetFileSize(const std::string& fileName, uint64_t* size) { - assert(size != nullptr); - *size = 0; +StatusCode CurveFS::GetFileSize(const std::string& fileName, + std::map* fileSizeMap) { + assert(fileSizeMap != nullptr); FileInfo fileInfo; auto ret = GetFileInfo(fileName, &fileInfo); if (ret != StatusCode::kOK) { @@ -527,21 +527,24 @@ StatusCode CurveFS::GetFileSize(const std::string& fileName, uint64_t* size) { << fileInfo.filetype() << ", fileName = " << fileName; return StatusCode::kNotSupported; } - return GetFileSize(fileName, fileInfo, size); + return GetFileSize(fileName, fileInfo, fileSizeMap); } StatusCode CurveFS::GetFileSize(const std::string& fileName, const FileInfo& fileInfo, - uint64_t* fileSize) { + std::map* fileSizeMap) { // return file length if it is a file switch (fileInfo.filetype()) { case FileType::INODE_PAGEFILE: { - *fileSize = fileInfo.length(); + if (fileInfo.has_poolset()) { + (*fileSizeMap)[fileInfo.poolset()] += fileInfo.length(); + } else { + (*fileSizeMap)[kDefaultPoolsetName] += fileInfo.length(); + } return StatusCode::kOK; } case FileType::INODE_SNAPSHOT_PAGEFILE: { // Do not count snapshot file size, set fileSize=0 - *fileSize = 0; return StatusCode::kOK; } case FileType::INODE_DIRECTORY: { @@ -569,14 +572,12 @@ StatusCode CurveFS::GetFileSize(const std::string& fileName, } else { fullPathName = fileName + "/" + file.filename(); } - uint64_t size = 0; - ret = GetFileSize(fullPathName, file, &size); + ret = GetFileSize(fullPathName, file, fileSizeMap); if (ret != StatusCode::kOK) { LOG(ERROR) << "Get file size of " << fullPathName << " fail, error code: " << ret; return ret; } - *fileSize += size; } return StatusCode::kOK; } diff --git a/src/mds/nameserver2/curvefs.h b/src/mds/nameserver2/curvefs.h index 5723168226..4caec98b60 100644 --- a/src/mds/nameserver2/curvefs.h +++ b/src/mds/nameserver2/curvefs.h @@ -187,10 +187,11 @@ class CurveFS { /** * @brief get size of the file or directory * @brief fileName - * @param[out]: size: the fileLength of the file or directory + * @param[out]: fileSizeMap: total file size of corresponding poolset * @return StatusCode::kOK if succeeded */ - StatusCode GetFileSize(const std::string& fileName, uint64_t* size); + StatusCode GetFileSize(const std::string& fileName, + std::map* fileSizeMap); /** * @brief delete file @@ -764,12 +765,12 @@ class CurveFS { * @brief get the size of file or directory * @param: fileName * @param: fileInfo - * @param[out]: fileSize: the size of file or directory + * @param[out]: fileSizeMap: total file size of corresponding poolset * @return StatusCode::kOK if succeeded */ StatusCode GetFileSize(const std::string& fileName, const FileInfo& fileInfo, - uint64_t* fileSize); + std::map* fileSizeMap); /** * @brief check file has rely dest file diff --git a/src/mds/nameserver2/namespace_service.cpp b/src/mds/nameserver2/namespace_service.cpp index fb6f6383fc..19c40120b2 100644 --- a/src/mds/nameserver2/namespace_service.cpp +++ b/src/mds/nameserver2/namespace_service.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include "src/mds/nameserver2/curvefs.h" @@ -2002,13 +2003,12 @@ void NameSpaceService::GetFileSize( brpc::ClosureGuard doneGuard(done); brpc::Controller* cntl = static_cast(controller); - LOG(INFO) << "logid = " << cntl->log_id() << ", GetFileSize request, fileName = " << request->filename(); StatusCode retCode; - uint64_t fileSize = 0; - retCode = kCurveFS.GetFileSize(request->filename(), &fileSize); + std::map fileSizeMap; + retCode = kCurveFS.GetFileSize(request->filename(), &fileSizeMap); if (retCode != StatusCode::kOK) { response->set_statuscode(retCode); LOG(ERROR) << "logid = " << cntl->log_id() @@ -2018,10 +2018,22 @@ void NameSpaceService::GetFileSize( return; } else { response->set_statuscode(StatusCode::kOK); - response->set_filesize(fileSize); + + if (request->groupbypoolset()) { + auto* map = response->mutable_filesizemap(); + for (const auto& p : fileSizeMap) { + (*map)[p.first] = p.second; + } + } else { + uint64_t totalSize = 0; + for (const auto& p : fileSizeMap) { + totalSize += p.second; + } + response->set_filesize(totalSize); + } LOG(INFO) << "logid = " << cntl->log_id() - << ", GetFileSize ok, fileName = " << request->filename() - << ", fileSize = " << response->filesize() / kGB << "GB"; + << ", GetFileSize ok, response: " + << response->ShortDebugString(); } return; } diff --git a/test/mds/nameserver2/curvefs_test.cpp b/test/mds/nameserver2/curvefs_test.cpp index 899b942ee8..79884645f1 100644 --- a/test/mds/nameserver2/curvefs_test.cpp +++ b/test/mds/nameserver2/curvefs_test.cpp @@ -1221,8 +1221,17 @@ TEST_F(CurveFSTest, testGetAllocatedSize) { } } +namespace { +uint64_t TotalSize(const std::map& fileSizeMap) { + uint64_t total = 0; + for (const auto& p : fileSizeMap) { + total += p.second; + } + return total; +} +} // namespace + TEST_F(CurveFSTest, testGetFileSize) { - uint64_t fileSize; FileInfo fileInfo; fileInfo.set_id(0); fileInfo.set_filetype(FileType::INODE_PAGEFILE); @@ -1234,9 +1243,10 @@ TEST_F(CurveFSTest, testGetFileSize) { .Times(1) .WillOnce(DoAll(SetArgPointee<2>(fileInfo), Return(StoreStatus::OK))); + std::map fileSizeMap; ASSERT_EQ(StatusCode::kOK, - curvefs_->GetFileSize("/tests", &fileSize)); - ASSERT_EQ(10 * kGB, fileSize); + curvefs_->GetFileSize("/tests", &fileSizeMap)); + ASSERT_EQ(10 * kGB, TotalSize(fileSizeMap)); } // test directory normal { @@ -1254,17 +1264,19 @@ TEST_F(CurveFSTest, testGetFileSize) { .Times(1) .WillOnce(DoAll(SetArgPointee<2>(files), Return(StoreStatus::OK))); + std::map fileSizeMap; ASSERT_EQ(StatusCode::kOK, - curvefs_->GetFileSize("/tests", &fileSize)); - ASSERT_EQ(30 * kGB, fileSize); + curvefs_->GetFileSize("/tests", &fileSizeMap)); + ASSERT_EQ(30 * kGB, TotalSize(fileSizeMap)); } // test GetFile fail { EXPECT_CALL(*storage_, GetFile(_, _, _)) .Times(1) .WillOnce(Return(StoreStatus::KeyNotExist)); + std::map fileSizeMap; ASSERT_EQ(StatusCode::kFileNotExists, - curvefs_->GetFileSize("/tests", &fileSize)); + curvefs_->GetFileSize("/tests", &fileSizeMap)); } // test file type not supported { @@ -1274,8 +1286,9 @@ TEST_F(CurveFSTest, testGetFileSize) { .Times(1) .WillOnce(DoAll(SetArgPointee<2>(appendFileInfo), Return(StoreStatus::OK))); + std::map fileSizeMap; ASSERT_EQ(StatusCode::kNotSupported, - curvefs_->GetFileSize("/tests", &fileSize)); + curvefs_->GetFileSize("/tests", &fileSizeMap)); } // test list directory fail { @@ -1288,11 +1301,82 @@ TEST_F(CurveFSTest, testGetFileSize) { EXPECT_CALL(*storage_, ListFile(_, _, _)) .Times(1) .WillOnce(Return(StoreStatus::InternalError)); + std::map fileSizeMap; ASSERT_EQ(StatusCode::kStorageError, - curvefs_->GetFileSize("/tests", &fileSize)); + curvefs_->GetFileSize("/tests", &fileSizeMap)); } } +TEST_F(CurveFSTest, testGetFileSizeGroupByPoolset) { + // / + // ├──A (no poolset info) + // └──B + // ├── C (poolset a) + // ├── D (poolset b) + // └── E (poolset a) + FileInfo root; + root.set_id(0); + root.set_filename("/"); + root.set_filetype(FileType::INODE_DIRECTORY); + + FileInfo fileInfoA; + fileInfoA.set_id(1); + fileInfoA.set_filename("A"); + fileInfoA.set_filetype(FileType::INODE_PAGEFILE); + fileInfoA.set_length(10 * kGB); + + FileInfo dirB; + dirB.set_id(2); + dirB.set_filename("B"); + dirB.set_filetype(FileType::INODE_DIRECTORY); + + FileInfo fileInfoC; + fileInfoC.set_id(3); + fileInfoC.set_filename("C"); + fileInfoC.set_filetype(FileType::INODE_PAGEFILE); + fileInfoC.set_length(10 * kGB); + fileInfoC.set_poolset("poolset-a"); + + FileInfo fileInfoD; + fileInfoD.set_id(4); + fileInfoD.set_filename("D"); + fileInfoD.set_filetype(FileType::INODE_PAGEFILE); + fileInfoD.set_length(15 * kGB); + fileInfoD.set_poolset("poolset-b"); + + FileInfo fileInfoE; + fileInfoE.set_id(5); + fileInfoE.set_filename("E"); + fileInfoE.set_filetype(FileType::INODE_PAGEFILE); + fileInfoE.set_length(20 * kGB); + fileInfoE.set_poolset("poolset-a"); + + EXPECT_CALL(*storage_, GetFile(_, _, _)) + .WillOnce(DoAll(SetArgPointee<2>(root), Return(StoreStatus::OK))); + + std::vector listRootResults; + listRootResults.push_back(fileInfoA); + listRootResults.push_back(dirB); + + std::vector listDirBResults; + listDirBResults.push_back(fileInfoC); + listDirBResults.push_back(fileInfoD); + listDirBResults.push_back(fileInfoE); + + EXPECT_CALL(*storage_, ListFile(_, _, _)) + .WillOnce( + DoAll(SetArgPointee<2>(listRootResults), Return(StoreStatus::OK))) + .WillOnce( + DoAll(SetArgPointee<2>(listDirBResults), Return(StoreStatus::OK))); + + std::map fileSizeMap; + ASSERT_EQ(StatusCode::kOK, curvefs_->GetFileSize("/", &fileSizeMap)); + ASSERT_EQ(3, fileSizeMap.size()); + ASSERT_EQ(10 * kGB, fileSizeMap[kDefaultPoolsetName]); + ASSERT_EQ(30 * kGB, fileSizeMap["poolset-a"]); + ASSERT_EQ(15 * kGB, fileSizeMap["poolset-b"]); +} + TEST_F(CurveFSTest, testReadDir) { FileInfo fileInfo; std::vector items; diff --git a/tools-v2/internal/utils/row.go b/tools-v2/internal/utils/row.go index 6369aa3ed5..951ab38787 100644 --- a/tools-v2/internal/utils/row.go +++ b/tools-v2/internal/utils/row.go @@ -89,6 +89,7 @@ const ( ROW_PEER_ID = "peerId" ROW_PEER_NUMBER = "peerNumber" ROW_PHYPOOL = "phyPool" + ROW_PHYPOOLS = "phyPools" ROW_PATH = "path" ROW_POOL = "pool" ROW_POOL_ID = "poolId" @@ -136,6 +137,7 @@ const ( ROW_ISLAZY = "isLazy" ROW_NEXTSTEP = "nextStep" ROW_TIME = "time" + ROW_ALLOC_PERCENT = "alloc percent(%)" ROW_RW_STATUS = "rwStatus" ROW_DISK_STATE = "diskState" diff --git a/tools-v2/pkg/cli/command/curvebs/list/list.go b/tools-v2/pkg/cli/command/curvebs/list/list.go index 44472a01a9..690d31cd3f 100644 --- a/tools-v2/pkg/cli/command/curvebs/list/list.go +++ b/tools-v2/pkg/cli/command/curvebs/list/list.go @@ -30,6 +30,7 @@ import ( "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/list/formatstatus" logicalpool "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/list/logicalPool" may_broken_vol "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/list/may-broken-vol" + "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/list/poolset" "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/list/scanstatus" "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/list/server" "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/list/snapshot" @@ -54,6 +55,7 @@ func (listCmd *ListCommand) AddSubCommands() { scanstatus.NewScanStatusCommand(), may_broken_vol.NewMayBrokenVolCommand(), formatstatus.NewFormatStatusCommand(), + poolset.NewPoolsetCommand(), snapshot.NewSnapShotCommand(), ) } diff --git a/tools-v2/pkg/cli/command/curvebs/list/logicalPool/logicalPool.go b/tools-v2/pkg/cli/command/curvebs/list/logicalPool/logicalPool.go index 699ed9a811..d8e04ed936 100644 --- a/tools-v2/pkg/cli/command/curvebs/list/logicalPool/logicalPool.go +++ b/tools-v2/pkg/cli/command/curvebs/list/logicalPool/logicalPool.go @@ -55,13 +55,13 @@ var _ basecmd.RpcFunc = (*ListLogicalPoolRpc)(nil) // check interface type LogicalPoolCommand struct { basecmd.FinalCurveCmd - Rpc []*ListLogicalPoolRpc - Metric *basecmd.Metric - recycleAllocRes *nameserver2.GetAllocatedSizeResponse - logicalPoolInfo []*topology.ListLogicalPoolResponse - totalCapacity uint64 - allocatedSize uint64 - recycleAllocSize uint64 + Rpc []*ListLogicalPoolRpc + Metric *basecmd.Metric + recycleAllocRes *nameserver2.GetAllocatedSizeResponse + logicalPoolInfo []*topology.ListLogicalPoolResponse + capacity []uint64 // capacity for each logicalpool + allocated []uint64 // allocated for each logicalpool + recyclable []uint64 // recyclable for each logicalpool } var _ basecmd.FinalCurveCmdFunc = (*LogicalPoolCommand)(nil) // check interface @@ -134,7 +134,6 @@ func (lCmd *LogicalPoolCommand) Init(cmd *cobra.Command, args []string) error { return err.ToError() } lCmd.recycleAllocRes = res - lCmd.recycleAllocSize = res.GetAllocatedSize() return nil } @@ -180,7 +179,7 @@ func (lCmd *LogicalPoolCommand) RunCommand(cmd *cobra.Command, args []string) er } row[cobrautil.ROW_TOTAL] = humanize.IBytes(value) total = value - lCmd.totalCapacity += value + lCmd.capacity = append(lCmd.capacity, value) // alloc size metricName = cobrautil.GetPoolLogicalAllocSubUri(loPoolInfo.GetLogicalPoolName()) @@ -190,11 +189,12 @@ func (lCmd *LogicalPoolCommand) RunCommand(cmd *cobra.Command, args []string) er } row[cobrautil.ROW_USED] = humanize.IBytes(value) row[cobrautil.ROW_LEFT] = humanize.IBytes(total - value) - lCmd.allocatedSize += value + lCmd.allocated = append(lCmd.allocated, value) // recycle recycle := lCmd.recycleAllocRes.AllocSizeMap[loPoolInfo.GetLogicalPoolID()] row[cobrautil.ROW_RECYCLE] = humanize.IBytes(recycle) + lCmd.recyclable = append(lCmd.recyclable, recycle) rows = append(rows, row) } } @@ -232,7 +232,7 @@ func (lCmd *LogicalPoolCommand) queryMetric(metricName string) (uint64, *cmderro } } -func ListLogicalPoolInfoAndAllocSize(caller *cobra.Command) ([]*topology.ListLogicalPoolResponse, uint64, uint64, uint64, *cmderror.CmdError) { +func ListLogicalPoolInfoAndAllocSize(caller *cobra.Command) ([]*topology.ListLogicalPoolResponse, []uint64, []uint64, []uint64, *cmderror.CmdError) { listCmd := NewListLogicalPoolCommand() config.AlignFlagsValue(caller, listCmd.Cmd, []string{ config.CURVEBS_MDSADDR, config.RPCRETRYTIMES, config.RPCTIMEOUT, @@ -244,7 +244,7 @@ func ListLogicalPoolInfoAndAllocSize(caller *cobra.Command) ([]*topology.ListLog if err != nil { retErr := cmderror.ErrBsListLogicalPoolInfo() retErr.Format(err.Error()) - return nil, 0, 0, 0, retErr + return nil, nil, nil, nil, retErr } - return listCmd.logicalPoolInfo, listCmd.totalCapacity, listCmd.allocatedSize, listCmd.recycleAllocSize, cmderror.Success() + return listCmd.logicalPoolInfo, listCmd.capacity, listCmd.allocated, listCmd.recyclable, cmderror.Success() } diff --git a/tools-v2/pkg/cli/command/curvebs/list/poolset/poolset.go b/tools-v2/pkg/cli/command/curvebs/list/poolset/poolset.go new file mode 100644 index 0000000000..7ccf971353 --- /dev/null +++ b/tools-v2/pkg/cli/command/curvebs/list/poolset/poolset.go @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2023 NetEase Inc. + * + * 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 poolset + +import ( + "context" + "fmt" + "sort" + "strings" + + "github.com/dustin/go-humanize" + cmderror "github.com/opencurve/curve/tools-v2/internal/error" + cobrautil "github.com/opencurve/curve/tools-v2/internal/utils" + basecmd "github.com/opencurve/curve/tools-v2/pkg/cli/command" + logicalpool "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/list/logicalPool" + physicalpool "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/list/physicalPool" + filesize "github.com/opencurve/curve/tools-v2/pkg/cli/command/curvebs/query/file" + "github.com/opencurve/curve/tools-v2/pkg/config" + "github.com/opencurve/curve/tools-v2/pkg/output" + "github.com/opencurve/curve/tools-v2/proto/proto/topology" + "github.com/spf13/cobra" + "google.golang.org/grpc" +) + +const ( + poolsetsExample = `$ curve bs list poolset` +) + +type ListPoolsetRpc struct { + Info *basecmd.Rpc + Request *topology.ListPoolsetRequest + topologyClient topology.TopologyServiceClient +} + +var _ basecmd.RpcFunc = (*ListPoolsetRpc)(nil) // check interface + +type PoolsetCommand struct { + basecmd.FinalCurveCmd + Rpc *ListPoolsetRpc +} + +type PoolsetUsage struct { + id uint32 + name string + total uint64 + created uint64 + allocated uint64 + recyclable uint64 + physicalPools []uint32 +} + +var _ basecmd.FinalCurveCmdFunc = (*PoolsetCommand)(nil) // check interface + +func (rpc *ListPoolsetRpc) NewRpcClient(cc grpc.ClientConnInterface) { + rpc.topologyClient = topology.NewTopologyServiceClient(cc) +} + +func (rpc *ListPoolsetRpc) Stub_Func(ctx context.Context) (interface{}, error) { + return rpc.topologyClient.ListPoolset(ctx, rpc.Request) +} + +func NewPoolsetCommand() *cobra.Command { + return NewListPoolsetCommand().Cmd +} + +func NewListPoolsetCommand() *PoolsetCommand { + poolsetCmd := &PoolsetCommand{ + FinalCurveCmd: basecmd.FinalCurveCmd{ + Use: "poolset", + Short: "list all poolsets and space usage", + Example: poolsetsExample, + }, + } + + basecmd.NewFinalCurveCli(&poolsetCmd.FinalCurveCmd, poolsetCmd) + return poolsetCmd +} + +func (poolsetCmd *PoolsetCommand) AddFlags() { + config.AddBsMdsFlagOption(poolsetCmd.Cmd) + config.AddRpcRetryTimesFlag(poolsetCmd.Cmd) + config.AddRpcTimeoutFlag(poolsetCmd.Cmd) + config.AddHttpTimeoutFlag(poolsetCmd.Cmd) +} + +func (poolsetCmd *PoolsetCommand) Print(cmd *cobra.Command, args []string) error { + return output.FinalCmdOutput(&poolsetCmd.FinalCurveCmd, poolsetCmd) +} + +func (poolsetCmd *PoolsetCommand) ResultPlainOutput() error { + return output.FinalCmdOutputPlain(&poolsetCmd.FinalCurveCmd) +} + +func (poolsetCmd *PoolsetCommand) Init(cmd *cobra.Command, args []string) error { + mdsAddrs, err := config.GetBsMdsAddrSlice(poolsetCmd.Cmd) + if err.TypeCode() != cmderror.CODE_SUCCESS { + return err.ToError() + } + + timeout := config.GetFlagDuration(poolsetCmd.Cmd, config.RPCTIMEOUT) + retrytimes := config.GetFlagInt32(poolsetCmd.Cmd, config.RPCRETRYTIMES) + poolsetCmd.Rpc = &ListPoolsetRpc{ + Info: basecmd.NewRpc(mdsAddrs, timeout, retrytimes, "ListPoolset"), + Request: &topology.ListPoolsetRequest{}, + } + + header := []string{ + cobrautil.ROW_ID, + cobrautil.ROW_NAME, + cobrautil.ROW_PHYPOOLS, + cobrautil.ROW_TOTAL, + cobrautil.ROW_CREATED, + cobrautil.ROW_ALLOC, + cobrautil.ROW_RECYCLE, + cobrautil.ROW_ALLOC_PERCENT, + } + poolsetCmd.SetHeader(header) + return nil +} + +func buildPhysicalPoolToPoolsetMap(physicalPools []*topology.PhysicalPoolInfo) map[uint32]uint32 { + phyPoolToPoolset := make(map[uint32]uint32) + for _, phyPool := range physicalPools { + phyPoolToPoolset[phyPool.GetPhysicalPoolID()] = phyPool.GetPoolsetId() + } + return phyPoolToPoolset +} + +func buildPoolsetIdToNameMap(resp *topology.ListPoolsetResponse) map[uint32]string { + idToMap := make(map[uint32]string) + + for _, info := range resp.GetPoolsetInfos() { + idToMap[info.GetPoolsetID()] = info.GetPoolsetName() + } + + return idToMap +} + +func stringifyPhysicalPools(pools []uint32) string { + sort.Slice(pools, func(i, j int) bool { + return pools[i] < pools[j] + }) + strs := make([]string, 0) + for _, pool := range pools { + strs = append(strs, fmt.Sprintf("%d", pool)) + } + return strings.Join(strs, ", ") +} + +func getCreatedFileSize(fileSizeMap map[string]uint64, name string) string { + if size, ok := fileSizeMap[name]; ok { + return humanize.IBytes(size) + } + return cobrautil.ROW_VALUE_NO_VALUE +} + +func (poolsetCmd *PoolsetCommand) RunCommand(cmd *cobra.Command, args []string) error { + listPoolsetResp, err := basecmd.GetRpcResponse(poolsetCmd.Rpc.Info, poolsetCmd.Rpc) + if err.TypeCode() != cmderror.CODE_SUCCESS { + return err.ToError() + } + + phyPools, err := physicalpool.GetPhysicalPool(poolsetCmd.Cmd) + if err.TypeCode() != cmderror.CODE_SUCCESS { + return err.ToError() + } + + logPools, capacity, allocated, recyclable, err := logicalpool.ListLogicalPoolInfoAndAllocSize(poolsetCmd.Cmd) + if err.TypeCode() != cmderror.CODE_SUCCESS { + return err.ToError() + } + + logicalPoolInfos := make([]*topology.LogicalPoolInfo, 0) + for _, info := range logPools { + logicalPoolInfos = append(logicalPoolInfos, info.GetLogicalPoolInfos()...) + } + + fileSizeMap, _ := filesize.GetCreatedFileSizeGroupedByPoolset(poolsetCmd.Cmd) + poolsetIdToName := buildPoolsetIdToNameMap(listPoolsetResp.(*topology.ListPoolsetResponse)) + phyPoolToPoolset := buildPhysicalPoolToPoolsetMap(phyPools) + + poolsets := make(map[uint32]PoolsetUsage) + + for idx, logicalPool := range logicalPoolInfos { + phyPoolId := logicalPool.GetPhysicalPoolID() + poolsetId, ok := phyPoolToPoolset[phyPoolId] + if !ok { + err := cmderror.NewInternalCmdError(1, "physical pool not found") + return err.ToError() + } + + if poolset, ok := poolsets[poolsetId]; !ok { + poolsets[poolsetId] = PoolsetUsage{ + id: poolsetId, + name: poolsetIdToName[poolsetId], + total: capacity[idx], + allocated: allocated[idx], + recyclable: recyclable[idx], + physicalPools: []uint32{phyPoolId}, + } + } else { + poolset.total += capacity[idx] + poolset.allocated += allocated[idx] + poolset.recyclable += recyclable[idx] + poolset.physicalPools = append(poolset.physicalPools, phyPoolId) + + poolsets[poolsetId] = poolset + } + } + + rows := make([]map[string]string, 0) + for _, poolset := range poolsets { + row := make(map[string]string) + row[cobrautil.ROW_ID] = fmt.Sprintf("%d", poolset.id) + row[cobrautil.ROW_NAME] = poolset.name + row[cobrautil.ROW_PHYPOOLS] = stringifyPhysicalPools(poolset.physicalPools) + row[cobrautil.ROW_TOTAL] = humanize.IBytes(poolset.total) + row[cobrautil.ROW_CREATED] = getCreatedFileSize(fileSizeMap, poolset.name) + row[cobrautil.ROW_ALLOC] = humanize.IBytes(poolset.allocated) + row[cobrautil.ROW_RECYCLE] = humanize.IBytes(poolset.recyclable) + row[cobrautil.ROW_ALLOC_PERCENT] = fmt.Sprintf("%.2f", float64(poolset.allocated)/float64(poolset.total)*100) + + rows = append(rows, row) + } + + list := cobrautil.ListMap2ListSortByKeys(rows, poolsetCmd.Header, []string{ + cobrautil.ROW_ID, + }) + poolsetCmd.TableNew.AppendBulk(list) + poolsetCmd.Error = nil + poolsetCmd.Result = rows + + return nil +} diff --git a/tools-v2/pkg/cli/command/curvebs/list/space/space.go b/tools-v2/pkg/cli/command/curvebs/list/space/space.go index 715ca22e3c..c0b42ba582 100644 --- a/tools-v2/pkg/cli/command/curvebs/list/space/space.go +++ b/tools-v2/pkg/cli/command/curvebs/list/space/space.go @@ -77,10 +77,18 @@ func (sCmd *SpaceCommand) AddFlags() { config.AddRpcTimeoutFlag(sCmd.Cmd) } +func sum(array []uint64) uint64 { + var result uint64 + for _, v := range array { + result += v + } + return result +} + func (sCmd *SpaceCommand) Init(cmd *cobra.Command, args []string) error { - logicalRes, totalCapacity, allocatedSize, recycleAllocSize, err := logicalpool.ListLogicalPoolInfoAndAllocSize(sCmd.Cmd) - sCmd.TotalCapacity = totalCapacity - sCmd.AllocatedSize = allocatedSize + logicalRes, capacity, allocated, recyclable, err := logicalpool.ListLogicalPoolInfoAndAllocSize(sCmd.Cmd) + sCmd.TotalCapacity = sum(capacity) + sCmd.AllocatedSize = sum(allocated) if err.TypeCode() != cmderror.CODE_SUCCESS { return err.ToError() } @@ -88,7 +96,7 @@ func (sCmd *SpaceCommand) Init(cmd *cobra.Command, args []string) error { for _, info := range logicalRes { logicalPoolInfos = append(logicalPoolInfos, info.GetLogicalPoolInfos()...) } - sCmd.RecycleAllocSize = recycleAllocSize + sCmd.RecycleAllocSize = sum(recyclable) sCmd.TotalChunkSize = 0 timeout := config.GetFlagDuration(sCmd.Cmd, config.HTTPTIMEOUT) diff --git a/tools-v2/pkg/cli/command/curvebs/query/file/filesize.go b/tools-v2/pkg/cli/command/curvebs/query/file/filesize.go index a43f76762b..03291b5a56 100644 --- a/tools-v2/pkg/cli/command/curvebs/query/file/filesize.go +++ b/tools-v2/pkg/cli/command/curvebs/query/file/filesize.go @@ -44,8 +44,9 @@ var _ basecmd.RpcFunc = (*GetFileSizeRpc)(nil) // check interface type GetFileSizeCommand struct { basecmd.FinalCurveCmd - Rpc *GetFileSizeRpc - Response *nameserver2.GetFileSizeResponse + Rpc *GetFileSizeRpc + Response *nameserver2.GetFileSizeResponse + GroupByPoolset bool } var _ basecmd.FinalCurveCmdFunc = (*GetFileSizeCommand)(nil) // check interface @@ -71,6 +72,16 @@ func NewGetFileSizeCommand() *GetFileSizeCommand { return fileCmd } +func NewGetCreatedFileSizeGroupedByPoolsetCommand() *GetFileSizeCommand { + fileCmd := &GetFileSizeCommand{ + FinalCurveCmd: basecmd.FinalCurveCmd{}, + GroupByPoolset: true, + } + + basecmd.NewFinalCurveCli(&fileCmd.FinalCurveCmd, fileCmd) + return fileCmd +} + func (gCmd *GetFileSizeCommand) AddFlags() { config.AddBsMdsFlagOption(gCmd.Cmd) config.AddRpcRetryTimesFlag(gCmd.Cmd) @@ -87,7 +98,8 @@ func (gCmd *GetFileSizeCommand) Init(cmd *cobra.Command, args []string) error { retrytimes := config.GetFlagInt32(gCmd.Cmd, config.RPCRETRYTIMES) filepath := config.GetBsFlagString(gCmd.Cmd, config.CURVEBS_PATH) request := nameserver2.GetFileSizeRequest{ - FileName: &filepath, + FileName: &filepath, + GroupByPoolset: &gCmd.GroupByPoolset, } gCmd.Rpc = &GetFileSizeRpc{ Info: basecmd.NewRpc(mdsAddrs, timeout, retrytimes, "GetFileSize"), @@ -135,3 +147,25 @@ func GetFileSize(caller *cobra.Command) (*nameserver2.GetFileSizeResponse, *cmde } return getCmd.Response, cmderror.Success() } + +func GetCreatedFileSizeGroupedByPoolset(caller *cobra.Command) (map[string]uint64, *cmderror.CmdError) { + getCmd := NewGetCreatedFileSizeGroupedByPoolsetCommand() + config.AlignFlagsValue(caller, getCmd.Cmd, []string{ + config.RPCRETRYTIMES, config.RPCTIMEOUT, config.CURVEBS_MDSADDR, + config.CURVEBS_PATH, + }) + + getCmd.Cmd.Flags().Set(config.CURVEBS_PATH, "/") + getCmd.Cmd.Flag(config.CURVEBS_PATH).Changed = true + getCmd.Cmd.SilenceErrors = true + getCmd.Cmd.SilenceUsage = true + getCmd.Cmd.SetArgs([]string{"--format", config.FORMAT_NOOUT}) + err := getCmd.Cmd.Execute() + if err != nil { + retErr := cmderror.ErrBsGetFileInfo() + retErr.Format(err.Error()) + return nil, retErr + } + + return getCmd.Response.GetFileSizeMap(), cmderror.Success() +}