mirror of
https://github.com/cubefs/cubefs.git
synced 2026-08-02 02:00:56 +00:00
feat(remotecache): add config tool
close:#1000151163
Signed-off-by: chihe <chihe@oppo.com>
(cherry picked from commit 037348725c)
This commit is contained in:
parent
54da526123
commit
58771c8dc2
3
Makefile
3
Makefile
@ -52,6 +52,9 @@ bcache:
|
||||
rctest:
|
||||
@build/build.sh rctest $(GOMOD) --threads=$(threads)
|
||||
|
||||
rcconfig:
|
||||
@build/build.sh rcconfig $(GOMOD) --threads=$(threads)
|
||||
|
||||
phony += clean
|
||||
clean:
|
||||
@$(RM) -rf build/bin
|
||||
|
||||
@ -556,6 +556,13 @@ build_rctest(){
|
||||
popd >/dev/null
|
||||
}
|
||||
|
||||
build_rcconfig(){
|
||||
pushd $SrcPath >/dev/null
|
||||
echo -n "build cfs-remotecache-config "
|
||||
CGO_ENABLED=0 go build ${MODFLAGS} -gcflags=all=-trimpath=${SrcPath} -asmflags=all=-trimpath=${SrcPath} -ldflags="${LDFlags}" -o ${BuildBinPath}/cfs-remotecache-config ${SrcPath}/tool/remotecache-config/*.go && echo "success" || echo "failed"
|
||||
popd >/dev/null
|
||||
}
|
||||
|
||||
clean() {
|
||||
$RM -rf ${BuildBinPath}
|
||||
}
|
||||
@ -613,6 +620,7 @@ case "$cmd" in
|
||||
build_libsdk
|
||||
build_bcache
|
||||
build_rctest
|
||||
build_rcconfig
|
||||
;;
|
||||
"test")
|
||||
run_test
|
||||
@ -668,6 +676,9 @@ case "$cmd" in
|
||||
"rctest")
|
||||
build_rctest
|
||||
;;
|
||||
"rcconfig")
|
||||
build_rcconfig
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
48
tool/remotecache-config/cmd/cluster.go
Normal file
48
tool/remotecache-config/cmd/cluster.go
Normal file
@ -0,0 +1,48 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/cubefs/cubefs/proto"
|
||||
"github.com/cubefs/cubefs/sdk/master"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const (
|
||||
cmdClusterUse = "cluster [COMMAND]"
|
||||
cmdClusterShort = "Manage cluster components"
|
||||
|
||||
cmdClusterInfoShort = "Show cluster summary information"
|
||||
)
|
||||
|
||||
func newClusterCmd(client *master.MasterClient) *cobra.Command {
|
||||
clusterCmd := &cobra.Command{
|
||||
Use: cmdClusterUse,
|
||||
Short: cmdClusterShort,
|
||||
}
|
||||
clusterCmd.AddCommand(
|
||||
newClusterInfoCmd(client),
|
||||
//TODO
|
||||
//newClusterStatCmd(client),
|
||||
//newClusterFreezeCmd(client),
|
||||
//newClusterSetThresholdCmd(client),
|
||||
//newClusterSetParasCmd(client),
|
||||
)
|
||||
return clusterCmd
|
||||
}
|
||||
|
||||
func newClusterInfoCmd(client *master.MasterClient) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: CliOpInfo,
|
||||
Short: cmdClusterInfoShort,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var err error
|
||||
var cv *proto.ClusterView
|
||||
if cv, err = client.AdminAPI().GetCluster(false); err != nil {
|
||||
errout(err)
|
||||
}
|
||||
stdout("[Cluster]\n")
|
||||
stdout("%v", formatClusterView(cv))
|
||||
stdout("\n")
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
156
tool/remotecache-config/cmd/config.go
Normal file
156
tool/remotecache-config/cmd/config.go
Normal file
@ -0,0 +1,156 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const (
|
||||
cmdConfigShort = "Manage global config file"
|
||||
cmdConfigSetShort = "set value of config file"
|
||||
cmdConfigInfoShort = "show info of config file"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
MasterAddr []string `json:"masterAddr"`
|
||||
Timeout uint16 `json:"timeout"`
|
||||
ClientIDKey string `json:"clientIDKey"`
|
||||
}
|
||||
|
||||
var (
|
||||
defaultHomeDir, _ = os.UserHomeDir()
|
||||
defaultConfigName = ".remotecache-config.json"
|
||||
defaultConfigPath = path.Join(defaultHomeDir, defaultConfigName)
|
||||
defaultConfigData = []byte(`
|
||||
{
|
||||
"masterAddr": [
|
||||
"master.cube.io"
|
||||
],
|
||||
"timeout": 60
|
||||
}
|
||||
`)
|
||||
defaultConfigTimeout uint16 = 60
|
||||
)
|
||||
|
||||
func LoadConfig() (*Config, error) {
|
||||
var err error
|
||||
var configData []byte
|
||||
if configData, err = os.ReadFile(defaultConfigPath); err != nil && !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
if err = os.WriteFile(defaultConfigPath, defaultConfigData, 0o600); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
configData = defaultConfigData
|
||||
}
|
||||
config := &Config{}
|
||||
if err = json.Unmarshal(configData, config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if config.Timeout == 0 {
|
||||
config.Timeout = defaultConfigTimeout
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func newConfigCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: CliResourceConfig,
|
||||
Short: cmdConfigShort,
|
||||
}
|
||||
cmd.AddCommand(newConfigSetCmd())
|
||||
cmd.AddCommand(newConfigInfoCmd())
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newConfigSetCmd() *cobra.Command {
|
||||
var optMasterHosts string
|
||||
var optTimeout string
|
||||
cmd := &cobra.Command{
|
||||
Use: CliOpSet,
|
||||
Short: cmdConfigSetShort,
|
||||
Long: `Set the config file`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var err error
|
||||
defer func() {
|
||||
errout(err)
|
||||
}()
|
||||
tmp, _ := strconv.Atoi(optTimeout)
|
||||
if tmp > math.MaxUint16 {
|
||||
stdoutln("Please reset timeout. Input less than math.MaxUint16")
|
||||
return
|
||||
}
|
||||
timeOut := uint16(tmp)
|
||||
if optMasterHosts == "" {
|
||||
stdout("Please set addr. Input 'cfs-cli config set -h' for help.\n")
|
||||
return
|
||||
}
|
||||
|
||||
if timeOut == 0 {
|
||||
stdout("timeOut %v is invalid.\n", timeOut)
|
||||
return
|
||||
}
|
||||
|
||||
if err = setConfig(optMasterHosts, timeOut); err != nil {
|
||||
return
|
||||
}
|
||||
stdout("Config has been set successfully!\n")
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVar(&optMasterHosts, "addr", "",
|
||||
"Specify master address {HOST}:{PORT}[,{HOST}:{PORT}]")
|
||||
cmd.Flags().StringVar(&optTimeout, "timeout", "60", "Specify timeout for requests [Unit: s]")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func setConfig(masterHosts string, timeout uint16) (err error) {
|
||||
var config *Config
|
||||
if config, err = LoadConfig(); err != nil {
|
||||
return
|
||||
}
|
||||
hosts := strings.Split(masterHosts, ",")
|
||||
if masterHosts != "" && len(hosts) > 0 {
|
||||
config.MasterAddr = hosts
|
||||
}
|
||||
if timeout != 0 {
|
||||
config.Timeout = timeout
|
||||
}
|
||||
var configData []byte
|
||||
if configData, err = json.Marshal(config); err != nil {
|
||||
return
|
||||
}
|
||||
if err = os.WriteFile(defaultConfigPath, configData, 0o600); err != nil {
|
||||
return
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newConfigInfoCmd() *cobra.Command {
|
||||
var optFilterStatus string
|
||||
var optFilterWritable string
|
||||
cmd := &cobra.Command{
|
||||
Use: CliOpInfo,
|
||||
Short: cmdConfigInfoShort,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
config, err := LoadConfig()
|
||||
errout(err)
|
||||
printConfigInfo(config)
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVar(&optFilterWritable, "filter-writable", "", "Filter node writable status")
|
||||
cmd.Flags().StringVar(&optFilterStatus, "filter-status", "", "Filter node status [Active, Inactive]")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func printConfigInfo(config *Config) {
|
||||
stdout("Config info:\n")
|
||||
stdout(" Master Address : %v\n", config.MasterAddr)
|
||||
stdout(" Request Timeout [s]: %v\n", config.Timeout)
|
||||
}
|
||||
179
tool/remotecache-config/cmd/const.go
Normal file
179
tool/remotecache-config/cmd/const.go
Normal file
@ -0,0 +1,179 @@
|
||||
package cmd
|
||||
|
||||
const (
|
||||
// List of operation name for cli
|
||||
CliOpGet = "get"
|
||||
CliOpList = "list"
|
||||
CliOpStatus = "stat"
|
||||
CliOpCreate = "create"
|
||||
CliOpDelete = "delete"
|
||||
CliOpRemove = "remove"
|
||||
CliOpInfo = "info"
|
||||
CliOpAdd = "add"
|
||||
CliOpSet = "set"
|
||||
CliOpUpdate = "update"
|
||||
CliOpDecommission = "decommission"
|
||||
CliOpRecommission = "recommission"
|
||||
CliOpQueryProgress = "query-progress"
|
||||
CliOpQueryDiskStat = "query-disk-stat"
|
||||
CliOpQueryNodeStat = "query-node-stat"
|
||||
CliOpAbortDecommission = "abort-decommission"
|
||||
CliOpMigrate = "migrate"
|
||||
CliOpDownloadZip = "load"
|
||||
CliOpMetaCompatibility = "meta"
|
||||
CliOpFreeze = "freeze"
|
||||
CliOpSetThreshold = "threshold"
|
||||
CliOpSetVolDeletionDelayTime = "volDeletionDelayTime"
|
||||
CliOpSetCluster = "set"
|
||||
CliOpCheck = "check"
|
||||
CliOpReset = "reset"
|
||||
CliOpReplicate = "add-replica"
|
||||
CliOpDelReplica = "del-replica"
|
||||
CliOpExpand = "expand"
|
||||
CliOpShrink = "shrink"
|
||||
CliOpGetDiscard = "get-discard"
|
||||
CliOpSetDiscard = "set-discard"
|
||||
CliOpForbidMpDecommission = "forbid-mp-decommission"
|
||||
CliOpQueryDecommissionedDisk = "query-decommissioned-disk"
|
||||
CliOpQueryDecommissionSuccessDisk = "query-decommissionSuccess-disk"
|
||||
CliOpEnableAutoDecommission = "enable-auto-decommission"
|
||||
CliOpQueryDecommissionFailedDisk = "query-decommission-failed-disk"
|
||||
CliOpSetDecommissionDiskLimit = "set-decommission-disk-limit"
|
||||
CliOpResetRestoreStatus = "reset-restore-status"
|
||||
CliOpCancelDecommission = "cancel-decommission"
|
||||
CliOpDiskOp = "diskop"
|
||||
CliOpDpOp = "dpop"
|
||||
CliOpDataNodeOp = "datanodeop"
|
||||
CliOpVolOp = "volop"
|
||||
|
||||
CliOpSetDecommissionLimit = "set-decommission-limit"
|
||||
CliOpQueryDecommissionStatus = "query-decommission-status"
|
||||
|
||||
// Shorthand format of operation name
|
||||
CliOpDecommissionShortHand = "dec"
|
||||
CliOpOffline = "offline"
|
||||
|
||||
// resource name
|
||||
CliResourceDataNode = "datanode [COMMAND]"
|
||||
CliResourceMetaNode = "metanode"
|
||||
CliResourceDataPartition = "datapartition"
|
||||
CliResourceMetaPartition = "metapartition"
|
||||
CliResourceTopology = "topology"
|
||||
CliResourceRaftNode = "raftnode"
|
||||
CliResourceDisk = "disk"
|
||||
CliResourceConfig = "config"
|
||||
|
||||
// Flags
|
||||
CliFlagName = "name"
|
||||
CliFlagOnwer = "user"
|
||||
CliFlagDataPartitionSize = "dp-size"
|
||||
CliFlagDataPartitionCount = "dp-count"
|
||||
CliFlagMetaPartitionCount = "mp-count"
|
||||
CliFlagReplicas = "replicas"
|
||||
CliFlagEnable = "enable"
|
||||
CliFlagEnableFollowerRead = "follower-read"
|
||||
CliFlagAuthenticate = "authenticate"
|
||||
CliFlagCapacity = "capacity"
|
||||
CliFlagBusiness = "description"
|
||||
CliFlagMPCount = "mp-count"
|
||||
CliFlagDPCount = "dp-count"
|
||||
CliFlagReplicaNum = "replica-num"
|
||||
CliFlagSize = "size"
|
||||
CliFlagVolType = "vol-type"
|
||||
CliFlagFollowerRead = "follower-read"
|
||||
CliFlagMetaFollowerRead = "meta-follower-read"
|
||||
CliFlagMaximallyRead = "maximally-read"
|
||||
CliFlagCacheRuleKey = "cache-rule-key"
|
||||
CliFlagEbsBlkSize = "ebs-blk-size"
|
||||
CliFlagCacheThreshold = "cache-threshold"
|
||||
CliFlagCacheRule = "cache-rule"
|
||||
CliFlagThreshold = "threshold"
|
||||
CliFlagAddress = "addr"
|
||||
CliFlagDiskPath = "path"
|
||||
CliFlagAuthKey = "authkey"
|
||||
CliFlagINodeStartID = "inode-start"
|
||||
CliFlagId = "id"
|
||||
CliFlagZoneName = "zone-name"
|
||||
CliFlagFlashZoneName = "zoneName"
|
||||
CliFlagDescription = "description"
|
||||
CliFlagAutoRepairRate = "autoRepairRate"
|
||||
CliFlagDelBatchCount = "batchCount"
|
||||
CliFlagDelWorkerSleepMs = "deleteWorkerSleepMs"
|
||||
CliFlagLoadFactor = "loadFactor"
|
||||
CliFlagMarkDelRate = "markDeleteRate"
|
||||
CliFlagMaxDpCntLimit = "maxDpCntLimit"
|
||||
CliFlagMaxMpCntLimit = "maxMpCntLimit"
|
||||
CliFlagDataNodeSelector = "dataNodeSelector"
|
||||
CliFlagMetaNodeSelector = "metaNodeSelector"
|
||||
CliFlagDataNodesetSelector = "dataNodesetSelector"
|
||||
CliFlagMetaNodesetSelector = "metaNodesetSelector"
|
||||
CliFlagCrossZone = "crossZone"
|
||||
CliNormalZonesFirst = "normalZonesFirst"
|
||||
CliFlagCount = "count"
|
||||
CliDpReadOnlyWhenVolFull = "readonly-when-full"
|
||||
CliTxMask = "transaction-mask"
|
||||
CliTxTimeout = "transaction-timeout"
|
||||
CliTxOpLimit = "transaction-limit"
|
||||
CliTxConflictRetryNum = "tx-conflict-retry-num"
|
||||
CliTxConflictRetryInterval = "tx-conflict-retry-Interval"
|
||||
CliTxForceReset = "transaction-force-reset"
|
||||
CliFlagMaxFiles = "maxFiles"
|
||||
CliFlagMaxBytes = "maxBytes"
|
||||
CliFlagMaxConcurrencyInode = "maxConcurrencyInode"
|
||||
CliFlagForceInode = "forceInode"
|
||||
CliFlagEnableQuota = "enableQuota"
|
||||
CliFlagDeleteLockTime = "delete-lock-time"
|
||||
CliFlagClientIDKey = "clientIDKey"
|
||||
CliFlagMarkDiskBrokenThreshold = "markBrokenDiskThreshold"
|
||||
CliFlagForce = "force"
|
||||
CliFlagEnableCrossZone = "cross-zone"
|
||||
CliFlagAutoDpMetaRepair = "autoDpMetaRepair"
|
||||
CliFlagAutoDpMetaRepairParallelCnt = "autoDpMetaRepairParallelCnt"
|
||||
CliFlagDpRepairTimeout = "dpRepairTimeout"
|
||||
CliFlagDpTimeout = "dpHeartbeatTimeout"
|
||||
CliFlagMpTimeout = "mpHeartbeatTimeout"
|
||||
CliFlagAutoDecommissionDisk = "autoDecommissionDisk"
|
||||
CliFlagAutoDecommissionDiskInterval = "autoDecommissionDiskInterval"
|
||||
CliFlagDpBackupTimeout = "dpBackupTimeout"
|
||||
CliFlagDecommissionDpLimit = "decommissionDpLimit"
|
||||
CliFlagDecommissionDiskLimit = "decommissionDiskLimit"
|
||||
CliFlagTrashInterval = "trashInterval"
|
||||
CliFlagAccessTimeValidInterval = "accessTimeValidInterval"
|
||||
CliFlagEnablePersistAccessTime = "enablePersistAccessTime"
|
||||
CliFlagDecommissionRaftForce = "raftForceDel"
|
||||
CliFLagDecommissionWeight = "decommissionWeight"
|
||||
CliFlagAllowedStorageClass = "allowedStorageClass"
|
||||
CliFlagVolStorageClass = "volStorageClass"
|
||||
CliFlagMediaType = "mediaType"
|
||||
CliForbidWriteOpOfProtoVersion0 = "forbidWriteOpOfProtoVersion0"
|
||||
CliFlagVolQuotaClass = "quotaClass"
|
||||
CliFlagVolQuotaOfClass = "quotaOfStorageClass"
|
||||
|
||||
CliFlagRemoteCacheEnable = "remoteCacheEnable"
|
||||
CliFlagRemoteCachePath = "remoteCachePath"
|
||||
CliFlagRemoteCacheAutoPrepare = "remoteCacheAutoPrepare"
|
||||
CliFlagRemoteCacheTTL = "remoteCacheTTL"
|
||||
CliFlagRemoteCacheReadTimeout = "remoteCacheReadTimeout"
|
||||
CliFlagRemoteCacheMaxFileSizeGB = "remoteCacheMaxFileSizeGB"
|
||||
CliFlagRemoteCacheOnlyForNotSSD = "remoteCacheOnlyForNotSSD"
|
||||
CliFlagRemoteCacheMultiRead = "remoteCacheMultiRead"
|
||||
CliFlagFlashNodeTimeoutCount = "flashNodeTimeoutCount"
|
||||
CliFlagRemoteCacheSameZoneTimeout = "remoteCacheSameZoneTimeout"
|
||||
CliFlagRemoteCacheSameRegionTimeout = "remoteCacheSameRegionTimeout"
|
||||
|
||||
// CliFlagSetDataPartitionCount = "count" use dp-count instead
|
||||
|
||||
// Shorthand format of resource name
|
||||
ResourceDataNodeShortHand = "dn"
|
||||
ResourceMetaNodeShortHand = "mn"
|
||||
ResourceDataPartitionShortHand = "dp"
|
||||
ResourceMetaPartitionShortHand = "mp"
|
||||
|
||||
// Usages
|
||||
CliUsageClientIDKey = "needed if cluster authentication is on"
|
||||
// version op
|
||||
CliFlagVersionCreate = "verCreate"
|
||||
CliFlagVersionList = "verList"
|
||||
CliFlagVersionDel = "verDel"
|
||||
CliFlagVersionSetStrategy = "verSetStrategy"
|
||||
)
|
||||
574
tool/remotecache-config/cmd/flashgroup.go
Normal file
574
tool/remotecache-config/cmd/flashgroup.go
Normal file
@ -0,0 +1,574 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/cubefs/cubefs/proto"
|
||||
"github.com/cubefs/cubefs/sdk/master"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const _flashgroupID = " [FlashGroupID]"
|
||||
|
||||
type slotInfo struct {
|
||||
fgID uint64
|
||||
slot uint32
|
||||
percent float64
|
||||
}
|
||||
|
||||
func newFlashGroupCmd(client *master.MasterClient) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "flashgroup [COMMAND]",
|
||||
Short: "cluster flashgroup management",
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newCmdFlashGroupTurn(client),
|
||||
newCmdFlashGroupCreate(client),
|
||||
newCmdFlashGroupSet(client),
|
||||
newCmdFlashGroupRemove(client),
|
||||
newCmdFlashGroupNodeAdd(client),
|
||||
newCmdFlashGroupNodeRemove(client),
|
||||
newCmdFlashGroupGet(client),
|
||||
newCmdFlashGroupList(client),
|
||||
newCmdFlashGroupClient(client),
|
||||
newCmdFlashGroupSearch(client),
|
||||
newCmdFlashGroupGraph(client),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newCmdFlashGroupTurn(client *master.MasterClient) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "turn [IsEnable]",
|
||||
Short: "turn flash group cache",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
enabled, err := strconv.ParseBool(args[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
result, err := client.AdminAPI().TurnFlashGroup(enabled)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stdoutln(result)
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newCmdFlashGroupCreate(client *master.MasterClient) *cobra.Command {
|
||||
var optSlots string
|
||||
var optWeight int
|
||||
var optGradualFlag bool
|
||||
var optStep uint32
|
||||
cmd := &cobra.Command{
|
||||
Use: CliOpCreate,
|
||||
Short: "create a new flash group",
|
||||
Args: cobra.MinimumNArgs(0),
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
if optSlots != "" {
|
||||
numbers := strings.Split(optSlots, ",")
|
||||
for _, numStr := range numbers {
|
||||
_, err = strconv.Atoi(numStr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
if optWeight <= 0 || optWeight > proto.FlashGroupMaxWeight {
|
||||
err = fmt.Errorf("param weight(%v) must greater than 0 and not greater than %v", optWeight, proto.FlashGroupMaxWeight)
|
||||
return
|
||||
}
|
||||
|
||||
if optGradualFlag {
|
||||
if optStep <= 0 {
|
||||
err = fmt.Errorf("param step(%v) must greater than 0", optStep)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
fgView, err := client.AdminAPI().CreateFlashGroup(optSlots, optWeight, optGradualFlag, optStep)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stdoutln(formatFlashGroupView(&fgView))
|
||||
return
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVar(&optSlots, "slots", "", "set group in which slots, --slots=slot1,slot2,...")
|
||||
cmd.Flags().IntVar(&optWeight, "weight", proto.FlashGroupDefaultWeight, "set group weight(default 1, must 1<=weight<=30), if it was specified slots count equal to 32*weight")
|
||||
cmd.Flags().BoolVar(&optGradualFlag, "gradualFlag", false, "set whether the group's slots are created gradually or not(default false)")
|
||||
cmd.Flags().Uint32Var(&optStep, "step", 1, "set the step size(default 1) for slot gradual creation")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newCmdFlashGroupSet(client *master.MasterClient) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: CliOpSet + _flashgroupID + " [IsActive]",
|
||||
Short: "set flash group active or not",
|
||||
Args: cobra.MinimumNArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
flashGroupID, err := parseFlashGroupID(args[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
isActive, err := strconv.ParseBool(args[1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fgView, err := client.AdminAPI().SetFlashGroup(flashGroupID, isActive)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stdoutln(formatFlashGroupView(&fgView))
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newCmdFlashGroupRemove(client *master.MasterClient) *cobra.Command {
|
||||
var optYes bool
|
||||
var optGradualFlag bool
|
||||
var optStep uint32
|
||||
cmd := &cobra.Command{
|
||||
Use: CliOpRemove + _flashgroupID,
|
||||
Short: "remove flash group by id",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
flashGroupID, err := parseFlashGroupID(args[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// ask user for confirm
|
||||
if !optYes {
|
||||
fmt.Printf("remove flash group by id[%d]\n", flashGroupID)
|
||||
stdout("\nConfirm (yes/no)[no]: ")
|
||||
var userConfirm string
|
||||
_, _ = fmt.Scanln(&userConfirm)
|
||||
if userConfirm != "yes" {
|
||||
err = fmt.Errorf("Abort by user.\n")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if optGradualFlag {
|
||||
if optStep <= 0 {
|
||||
err = fmt.Errorf("param step(%v) must greater than 0", optStep)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
result, err := client.AdminAPI().RemoveFlashGroup(flashGroupID, optGradualFlag, optStep)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stdoutln(result)
|
||||
return
|
||||
},
|
||||
}
|
||||
cmd.Flags().BoolVarP(&optYes, "yes", "y", false, "Answer yes for all questions")
|
||||
cmd.Flags().BoolVar(&optGradualFlag, "gradualFlag", false, "set whether the group's slots are deleted gradually or not(default false)")
|
||||
cmd.Flags().Uint32Var(&optStep, "step", 1, "set the step size(default 1) for slot gradual deletion")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newCmdFlashGroupNodeAdd(client *master.MasterClient) *cobra.Command {
|
||||
var (
|
||||
optAddr string
|
||||
optZoneName string
|
||||
optCount int
|
||||
)
|
||||
cmd := &cobra.Command{
|
||||
Use: "nodeAdd" + _flashgroupID,
|
||||
Short: "add flash node to given flash group",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
flashGroupID, err := parseFlashGroupID(args[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fgView, err := client.AdminAPI().FlashGroupAddFlashNode(flashGroupID, optCount, optZoneName, optAddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stdoutln(formatFlashGroupView(&fgView))
|
||||
return
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVar(&optAddr, CliFlagAddress, "", "add flash node of given addr")
|
||||
cmd.Flags().StringVar(&optZoneName, CliFlagFlashZoneName, "", "add flash node from given zone")
|
||||
cmd.Flags().IntVar(&optCount, CliFlagCount, 0, "add given count flash node from zone")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newCmdFlashGroupNodeRemove(client *master.MasterClient) *cobra.Command {
|
||||
var (
|
||||
optAddr string
|
||||
optZoneName string
|
||||
optCount int
|
||||
optYes bool
|
||||
)
|
||||
cmd := &cobra.Command{
|
||||
Use: "nodeRemove" + _flashgroupID,
|
||||
Short: "remove flash node to given flash group",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
flashGroupID, err := parseFlashGroupID(args[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// ask user for confirm
|
||||
if !optYes {
|
||||
fmt.Printf("remove flash node to given flash groupid[%d]\n", flashGroupID)
|
||||
if optAddr != "" {
|
||||
stdout(" FlashNode Addr : %v\n", optAddr)
|
||||
} else {
|
||||
stdout(" Zone : %v\n", optZoneName)
|
||||
stdout(" Count : %d\n", optCount)
|
||||
}
|
||||
stdout("\nConfirm (yes/no)[no]: ")
|
||||
var userConfirm string
|
||||
_, _ = fmt.Scanln(&userConfirm)
|
||||
if userConfirm != "yes" {
|
||||
err = fmt.Errorf("Abort by user.\n")
|
||||
return
|
||||
}
|
||||
}
|
||||
fgView, err := client.AdminAPI().FlashGroupRemoveFlashNode(flashGroupID, optCount, optZoneName, optAddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stdoutln(formatFlashGroupView(&fgView))
|
||||
return
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVar(&optAddr, CliFlagAddress, "", "remove flash node of given addr")
|
||||
cmd.Flags().StringVar(&optZoneName, CliFlagFlashZoneName, "", "remove flash node from given zone")
|
||||
cmd.Flags().IntVar(&optCount, CliFlagCount, 0, "remove given count flash node from zone")
|
||||
cmd.Flags().BoolVarP(&optYes, "yes", "y", false, "Answer yes for all questions")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newCmdFlashGroupGet(client *master.MasterClient) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: CliOpInfo + _flashgroupID + " [showHitRate ture/false] ",
|
||||
Short: "get flash group by id, default don't show hit rate",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
flashGroupID, err := parseFlashGroupID(args[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fgView, err := client.AdminAPI().GetFlashGroup(flashGroupID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stdoutln(formatFlashGroupView(&fgView))
|
||||
|
||||
showHitRate := false
|
||||
if len(args) > 1 {
|
||||
showHitRate, _ = strconv.ParseBool(args[1])
|
||||
}
|
||||
|
||||
stdoutln("[Flash Nodes]")
|
||||
var tbl table
|
||||
if showHitRate {
|
||||
tbl = table{formatFlashNodeViewTableTitle}
|
||||
} else {
|
||||
tbl = table{formatFlashNodeSimpleViewTableTitle}
|
||||
}
|
||||
for _, flashNodeViewInfos := range fgView.ZoneFlashNodes {
|
||||
tbl = showFlashNodesView(flashNodeViewInfos, showHitRate, nil, tbl)
|
||||
}
|
||||
stdoutln(alignTable(tbl...))
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newCmdFlashGroupList(client *master.MasterClient) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: CliOpList + " [IsActive]",
|
||||
Short: "list active or inactive flash groups",
|
||||
Args: cobra.MinimumNArgs(0),
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
var fgView proto.FlashGroupsAdminView
|
||||
var isActive bool
|
||||
if len(args) > 0 {
|
||||
if isActive, err = strconv.ParseBool(args[0]); err != nil {
|
||||
return
|
||||
}
|
||||
fgView, err = client.AdminAPI().ListFlashGroup(isActive)
|
||||
} else {
|
||||
fgView, err = client.AdminAPI().ListFlashGroups()
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
sort.Slice(fgView.FlashGroups, func(i, j int) bool {
|
||||
return fgView.FlashGroups[i].ID < fgView.FlashGroups[j].ID
|
||||
})
|
||||
|
||||
stdoutln("[Flash Groups]")
|
||||
slots := make([]*slotInfo, 0)
|
||||
reservedSlots := make([]*slotInfo, 0)
|
||||
tbl := table{formatFlashGroupViewTile}
|
||||
for _, group := range fgView.FlashGroups {
|
||||
sort.Slice(group.Slots, func(i, j int) bool {
|
||||
return group.Slots[i] < group.Slots[j]
|
||||
})
|
||||
for _, slot := range group.Slots {
|
||||
slots = append(slots, &slotInfo{
|
||||
fgID: group.ID,
|
||||
slot: slot,
|
||||
})
|
||||
}
|
||||
sort.Slice(group.ReservedSlots, func(i, j int) bool {
|
||||
return group.ReservedSlots[i] < group.ReservedSlots[j]
|
||||
})
|
||||
for _, slot := range group.ReservedSlots {
|
||||
reservedSlots = append(reservedSlots, &slotInfo{
|
||||
fgID: group.ID,
|
||||
slot: slot,
|
||||
})
|
||||
}
|
||||
tbl = tbl.append(arow(group.ID, group.Weight, len(group.Slots), len(group.ReservedSlots), group.Status, group.SlotStatus, len(group.PendingSlots), group.Step, group.FlashNodeCount, group.IsReducingSlots))
|
||||
}
|
||||
stdoutln(alignTable(tbl...))
|
||||
|
||||
sort.Slice(slots, func(i, j int) bool {
|
||||
return slots[i].slot < slots[j].slot
|
||||
})
|
||||
stdoutln("Slots:")
|
||||
for i, info := range slots {
|
||||
if i < len(slots)-1 {
|
||||
info.percent = float64(slots[i+1].slot-info.slot) * 100 / math.MaxUint32
|
||||
} else {
|
||||
info.percent = float64(math.MaxUint32-info.slot) * 100 / math.MaxUint32
|
||||
}
|
||||
stdoutlnf("num:%d slot:%d fg:%d percent:%0.5f%%", i+1, info.slot, info.fgID, info.percent)
|
||||
}
|
||||
stdoutln("ReservedSlots:")
|
||||
for i, info := range reservedSlots {
|
||||
if i < len(reservedSlots)-1 {
|
||||
info.percent = float64(reservedSlots[i+1].slot-info.slot) * 100 / math.MaxUint32
|
||||
} else {
|
||||
info.percent = float64(math.MaxUint32-info.slot) * 100 / math.MaxUint32
|
||||
}
|
||||
stdoutlnf("num:%d slot:%d fg:%d percent:%0.5f%%", i+1, info.slot, info.fgID, info.percent)
|
||||
}
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newCmdFlashGroupClient(client *master.MasterClient) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "client",
|
||||
Short: "show flash group response passed from master to client",
|
||||
Args: cobra.MinimumNArgs(0),
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
fgv, err := client.AdminAPI().ClientFlashGroups()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stdoutln("Flash Group Response:")
|
||||
stdoutln(formatIndent(fgv))
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newCmdFlashGroupSearch(client *master.MasterClient) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "search [volume] [inode] [offset]",
|
||||
Short: "search flash group by volume inode offset",
|
||||
Args: cobra.MinimumNArgs(3),
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
volume := args[0]
|
||||
if volume == "" {
|
||||
err = fmt.Errorf("volume is empty")
|
||||
return
|
||||
}
|
||||
inode, err := strconv.ParseUint(args[1], 10, 64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
offset, err := strconv.ParseUint(args[2], 10, 64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
slotKey := proto.ComputeCacheBlockSlot(volume, inode, offset)
|
||||
|
||||
fgView, err := client.AdminAPI().ListFlashGroups()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
set := make(map[uint32]struct{})
|
||||
slots := make([]slotInfo, 0)
|
||||
for _, fg := range fgView.FlashGroups {
|
||||
if fg.Status != proto.FlashGroupStatus_Active {
|
||||
continue
|
||||
}
|
||||
for _, slot := range fg.Slots {
|
||||
if _, in := set[slot]; in {
|
||||
continue
|
||||
}
|
||||
slots = append(slots, slotInfo{
|
||||
fgID: fg.ID,
|
||||
slot: slot,
|
||||
})
|
||||
}
|
||||
}
|
||||
sort.Slice(slots, func(i, j int) bool {
|
||||
return slots[i].slot < slots[j].slot
|
||||
})
|
||||
|
||||
var whichGroup uint64
|
||||
for _, slot := range slots {
|
||||
if slotKey >= slot.slot {
|
||||
whichGroup = slot.fgID
|
||||
}
|
||||
}
|
||||
for _, fg := range fgView.FlashGroups {
|
||||
if fg.ID == whichGroup {
|
||||
stdoutlnf("Found in FlashGroup:%d", whichGroup)
|
||||
tbl := table{formatFlashNodeSimpleViewTableTitle}
|
||||
for _, fnNodes := range fg.ZoneFlashNodes {
|
||||
tbl = showFlashNodesView(fnNodes, false, nil, tbl)
|
||||
}
|
||||
stdoutln(alignTable(tbl...))
|
||||
return
|
||||
}
|
||||
}
|
||||
stdoutlnf("Not found (%s %d %d) -> %d", volume, inode, offset, slotKey)
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newCmdFlashGroupGraph(client *master.MasterClient) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "graph",
|
||||
Short: "show flash group and node",
|
||||
Args: cobra.MinimumNArgs(0),
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
fgView, err := client.AdminAPI().ListFlashGroups()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
set := make(map[uint32]struct{})
|
||||
groups := make(map[uint64]proto.FlashGroupAdminView)
|
||||
groupn := make(map[uint64]int)
|
||||
groupStatusMap := make(map[uint64]string)
|
||||
slots := make([]slotInfo, 0)
|
||||
reservedSlots := make([]slotInfo, 0)
|
||||
for _, fg := range fgView.FlashGroups {
|
||||
groups[fg.ID] = fg
|
||||
groupn[fg.ID] = 0
|
||||
groupStatusMap[fg.ID] = fg.Status.String()
|
||||
for _, slot := range fg.Slots {
|
||||
if _, in := set[slot]; in {
|
||||
continue
|
||||
}
|
||||
groupn[fg.ID]++
|
||||
slots = append(slots, slotInfo{
|
||||
fgID: fg.ID,
|
||||
slot: slot,
|
||||
})
|
||||
}
|
||||
for _, slot := range fg.ReservedSlots {
|
||||
if _, in := set[slot]; in {
|
||||
continue
|
||||
}
|
||||
groupn[fg.ID]++
|
||||
reservedSlots = append(reservedSlots, slotInfo{
|
||||
fgID: fg.ID,
|
||||
slot: slot,
|
||||
})
|
||||
}
|
||||
}
|
||||
sort.Slice(slots, func(i, j int) bool {
|
||||
return slots[i].slot < slots[j].slot
|
||||
})
|
||||
stdoutln("[Flash Groups]")
|
||||
stdoutln("[Slots]")
|
||||
tbl := table{arow("Slot", "ID", "Status", "Count", "Ref", "Proportion")}
|
||||
for idx, slot := range slots {
|
||||
g := groups[slot.fgID]
|
||||
var p string
|
||||
if idx == len(slots)-1 {
|
||||
p = proportion(slot.slot, math.MaxUint32)
|
||||
} else {
|
||||
p = proportion(slot.slot, slots[idx+1].slot)
|
||||
}
|
||||
tbl = tbl.append(arow(slot.slot, g.ID, g.Status.String(), g.FlashNodeCount, groupn[g.ID], p))
|
||||
}
|
||||
stdoutln(alignTable(tbl...))
|
||||
|
||||
sort.Slice(reservedSlots, func(i, j int) bool {
|
||||
return reservedSlots[i].slot < reservedSlots[j].slot
|
||||
})
|
||||
stdoutln("[ReservedSlots]")
|
||||
tbl1 := table{arow("Slot", "ID", "Status", "Count", "Ref", "Proportion")}
|
||||
for idx, slot := range reservedSlots {
|
||||
g := groups[slot.fgID]
|
||||
var p string
|
||||
if idx == len(reservedSlots)-1 {
|
||||
p = proportion(slot.slot, math.MaxUint32)
|
||||
} else {
|
||||
p = proportion(slot.slot, reservedSlots[idx+1].slot)
|
||||
}
|
||||
tbl1 = tbl1.append(arow(slot.slot, g.ID, g.Status.String(), g.FlashNodeCount, groupn[g.ID], p))
|
||||
}
|
||||
|
||||
stdoutln(alignTable(tbl1...))
|
||||
|
||||
fnView, err := client.NodeAPI().ListFlashNodes(-1)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
busyNodes := make([]*proto.FlashNodeViewInfo, 0)
|
||||
idleNodes := make([]*proto.FlashNodeViewInfo, 0)
|
||||
for _, nodes := range fnView {
|
||||
for _, node := range nodes {
|
||||
if node.FlashGroupID == 0 {
|
||||
idleNodes = append(idleNodes, node)
|
||||
} else {
|
||||
busyNodes = append(busyNodes, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
graphFlashNodeTitle := make([]interface{}, len(formatFlashNodeViewTableTitle)+1)
|
||||
copy(graphFlashNodeTitle, formatFlashNodeViewTableTitle[:6])
|
||||
graphFlashNodeTitle[6] = "GroupStatus"
|
||||
copy(graphFlashNodeTitle[7:], formatFlashNodeViewTableTitle[6:])
|
||||
stdoutln("[FlashNodes Busy]")
|
||||
tbl = showFlashNodesView(busyNodes, true, groupStatusMap, table{graphFlashNodeTitle})
|
||||
stdoutln(alignTable(tbl...))
|
||||
stdoutln("[FlashNodes Idle]")
|
||||
tbl = showFlashNodesView(idleNodes, true, nil, table{formatFlashNodeViewTableTitle})
|
||||
stdoutln(alignTable(tbl...))
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func parseFlashGroupID(id string) (uint64, error) {
|
||||
return strconv.ParseUint(id, 10, 64)
|
||||
}
|
||||
|
||||
const fullDot = ".................................................."
|
||||
|
||||
func proportion(s, e uint32) string {
|
||||
p := "."
|
||||
if n := int(float64(e-s) * float64(len(fullDot)) / float64(math.MaxUint32)); n > 0 {
|
||||
p = fullDot[:n]
|
||||
}
|
||||
return p
|
||||
}
|
||||
345
tool/remotecache-config/cmd/flashnode.go
Normal file
345
tool/remotecache-config/cmd/flashnode.go
Normal file
@ -0,0 +1,345 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/cubefs/cubefs/proto"
|
||||
"github.com/cubefs/cubefs/sdk/httpclient"
|
||||
"github.com/cubefs/cubefs/sdk/master"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const _flashnodeAddr = " [FlashNodeAddr]"
|
||||
|
||||
func newFlashNodeCmd(client *master.MasterClient) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "flashnode [COMMAND]",
|
||||
Short: "cluster flashnode management",
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newCmdFlashNodeSet(client),
|
||||
newCmdFlashNodeRemove(client),
|
||||
newCmdFlashNodeGet(client),
|
||||
newCmdFlashNodeList(client),
|
||||
newCmdFlashNodeRemoveAllInactive(client),
|
||||
|
||||
newCmdFlashNodeHTTPStat(client),
|
||||
newCmdFlashNodeHTTPStatAll(client),
|
||||
newCmdFlashNodeHTTPEvict(client),
|
||||
newCmdFlashNodeHTTPInactiveDisk(client),
|
||||
newCmdFlashNodeHTTPSlotStat(client),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newCmdFlashNodeSet(client *master.MasterClient) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: CliOpSet + _flashnodeAddr + " [IsEnable]",
|
||||
Short: "set flash node enable or not",
|
||||
Args: cobra.MinimumNArgs(2),
|
||||
RunE: func(_ *cobra.Command, args []string) (err error) {
|
||||
addr := args[0]
|
||||
enable, err := strconv.ParseBool(args[1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = client.NodeAPI().SetFlashNode(addr, enable); err != nil {
|
||||
return
|
||||
}
|
||||
stdoutlnf("set flashnode:%s enable:%v success", addr, enable)
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newCmdFlashNodeRemove(client *master.MasterClient) *cobra.Command {
|
||||
var optYes bool
|
||||
cmd := &cobra.Command{
|
||||
Use: CliOpRemove + _flashnodeAddr,
|
||||
Short: "remove flash node by addr",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: func(_ *cobra.Command, args []string) (err error) {
|
||||
if !optYes {
|
||||
fmt.Printf("decommission flashnode:[%v]\n", args[0])
|
||||
stdout("\nConfirm (yes/no)[no]: ")
|
||||
var userConfirm string
|
||||
_, _ = fmt.Scanln(&userConfirm)
|
||||
if userConfirm != "yes" {
|
||||
err = fmt.Errorf("Abort by user.\n")
|
||||
return
|
||||
}
|
||||
}
|
||||
result, err := client.NodeAPI().RemoveFlashNode(args[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stdoutlnf("decommission flashnode:%s %s", args[0], result)
|
||||
return
|
||||
},
|
||||
}
|
||||
cmd.Flags().BoolVarP(&optYes, "yes", "y", false, "Answer yes for all questions")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newCmdFlashNodeRemoveAllInactive(client *master.MasterClient) *cobra.Command {
|
||||
var optYes bool
|
||||
cmd := &cobra.Command{
|
||||
Use: "removeAllInactive",
|
||||
Short: "remove all inactive flash nodes",
|
||||
Args: cobra.MinimumNArgs(0),
|
||||
RunE: func(_ *cobra.Command, args []string) (err error) {
|
||||
if !optYes {
|
||||
fmt.Printf("remove all inactive flash nodes")
|
||||
stdout("\nConfirm (yes/no)[no]: ")
|
||||
var userConfirm string
|
||||
_, _ = fmt.Scanln(&userConfirm)
|
||||
if userConfirm != "yes" {
|
||||
err = fmt.Errorf("Abort by user.\n")
|
||||
return
|
||||
}
|
||||
}
|
||||
var rmNodes []string
|
||||
rmNodes, err = client.NodeAPI().RemoveAllInactiveFlashNodes()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
stdoutlnf("total remove %v flash nodes", len(rmNodes))
|
||||
for _, rmNode := range rmNodes {
|
||||
stdoutlnf("remove flashnode:%s", rmNode)
|
||||
}
|
||||
return
|
||||
},
|
||||
}
|
||||
cmd.Flags().BoolVarP(&optYes, "yes", "y", false, "Answer yes for all questions")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newCmdFlashNodeGet(client *master.MasterClient) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: CliOpInfo + _flashnodeAddr,
|
||||
Short: "get flash node by addr",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
fn, err := client.NodeAPI().GetFlashNode(args[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stdoutln(formatFlashNodeView(&fn))
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newCmdFlashNodeList(client *master.MasterClient) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: CliOpList,
|
||||
Short: "list all flash nodes or [active true/false] flash nodes",
|
||||
Args: cobra.MinimumNArgs(0),
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
var active bool
|
||||
activeFilter := -1
|
||||
filterStr := "all"
|
||||
if len(args) == 1 {
|
||||
if active, err = strconv.ParseBool(args[0]); err != nil {
|
||||
err = fmt.Errorf("Parse bool fail: %v\n", err)
|
||||
return
|
||||
}
|
||||
if active {
|
||||
activeFilter = 1
|
||||
filterStr = "active:true"
|
||||
} else {
|
||||
activeFilter = 0
|
||||
filterStr = "active:false"
|
||||
}
|
||||
}
|
||||
zoneFlashNodes, err := client.NodeAPI().ListFlashNodes(activeFilter)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stdoutln(fmt.Sprintf("[FlashNodes] %s", filterStr))
|
||||
tbl := table{formatFlashNodeViewTableTitle}
|
||||
for _, flashNodeViewInfos := range zoneFlashNodes {
|
||||
tbl = showFlashNodesView(flashNodeViewInfos, true, nil, tbl)
|
||||
}
|
||||
stdoutln(alignTable(tbl...))
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newCmdFlashNodeHTTPStat(client *master.MasterClient) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "httpStat" + _flashnodeAddr,
|
||||
Short: "show flashnode stat",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: func(_ *cobra.Command, args []string) (err error) {
|
||||
// check flashnode whether exist
|
||||
_, err = client.NodeAPI().GetFlashNode(args[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stat, err := httpclient.New().Addr(addr2Prof(args[0])).FlashNode().Stat()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stdoutln(formatIndent(stat))
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newCmdFlashNodeHTTPStatAll(client *master.MasterClient) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "httpStatAll" + _flashnodeAddr,
|
||||
Short: "show flashnode stat all(key with expired time)",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: func(_ *cobra.Command, args []string) (err error) {
|
||||
// check flashnode whether exist
|
||||
_, err = client.NodeAPI().GetFlashNode(args[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stat, err := httpclient.New().Addr(addr2Prof(args[0])).FlashNode().StatAll()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stdoutln(formatIndent(stat))
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newCmdFlashNodeHTTPSlotStat(client *master.MasterClient) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "httpSlotStat" + _flashnodeAddr,
|
||||
Short: "show flashnode slot stat",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: func(_ *cobra.Command, args []string) (err error) {
|
||||
// check flashnode whether exist
|
||||
_, err = client.NodeAPI().GetFlashNode(args[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stat, err := httpclient.New().Addr(addr2Prof(args[0])).FlashNode().SlotStat()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
sort.SliceStable(stat.SlotStat, func(i, j int) bool {
|
||||
return stat.SlotStat[i].SlotId < stat.SlotStat[j].SlotId
|
||||
})
|
||||
stdout("%v\n", formatFlashNodeSlotStat(&stat))
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newCmdFlashNodeHTTPEvict(client *master.MasterClient) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "httpEvict" + _flashnodeAddr + " [volume]",
|
||||
Short: "evict cache in flashnode",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: func(_ *cobra.Command, args []string) (err error) {
|
||||
addr := args[0]
|
||||
// check flashnode whether exist
|
||||
_, err = client.NodeAPI().GetFlashNode(addr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(args) == 1 {
|
||||
if err = httpclient.New().Addr(addr2Prof(addr)).FlashNode().EvictAll(); err == nil {
|
||||
stdoutlnf("%s evicts all [OK]", addr)
|
||||
}
|
||||
return
|
||||
}
|
||||
volume := args[1]
|
||||
if err = httpclient.New().Addr(addr2Prof(addr)).FlashNode().EvictVol(volume); err == nil {
|
||||
stdoutlnf("%s evicts volume(%s) [OK]", addr, volume)
|
||||
}
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newCmdFlashNodeHTTPInactiveDisk(client *master.MasterClient) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "httpInactiveDisk" + _flashnodeAddr + " [dataPath]",
|
||||
Short: "inactive the disk in flashnode",
|
||||
Args: cobra.MinimumNArgs(2),
|
||||
RunE: func(_ *cobra.Command, args []string) (err error) {
|
||||
addr := args[0]
|
||||
// check flashnode whether exist
|
||||
_, err = client.NodeAPI().GetFlashNode(addr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
dataPath := args[1]
|
||||
if err = httpclient.New().Addr(addr2Prof(addr)).FlashNode().InactiveDisk(dataPath); err == nil {
|
||||
stdoutlnf("%s inactives dataPath(%s) [OK]", addr, dataPath)
|
||||
}
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func showFlashNodesView(flashNodeViewInfos []*proto.FlashNodeViewInfo, showStat bool, groupStats map[uint64]string, tbl table) table {
|
||||
sort.Slice(flashNodeViewInfos, func(i, j int) bool {
|
||||
return flashNodeViewInfos[i].ID < flashNodeViewInfos[j].ID
|
||||
})
|
||||
var groupActiveInfo string
|
||||
for _, fn := range flashNodeViewInfos {
|
||||
groupActiveInfo = ""
|
||||
nodeInfo := arow(fn.ZoneName, fn.ID, fn.Addr, formatYesNo(fn.IsActive), formatYesNo(fn.IsEnable),
|
||||
fn.FlashGroupID, formatTimeToString(fn.ReportTime))
|
||||
if groupStats != nil {
|
||||
if v, ok := groupStats[fn.FlashGroupID]; ok {
|
||||
groupActiveInfo = v
|
||||
}
|
||||
nodeInfo = arow(fn.ZoneName, fn.ID, fn.Addr, formatYesNo(fn.IsActive), formatYesNo(fn.IsEnable),
|
||||
fn.FlashGroupID, groupActiveInfo, formatTimeToString(fn.ReportTime))
|
||||
}
|
||||
if !showStat {
|
||||
tbl = tbl.append(nodeInfo)
|
||||
continue
|
||||
}
|
||||
if len(fn.DiskStat) == 0 {
|
||||
nodeInfo = append(nodeInfo, "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A")
|
||||
tbl = tbl.append(nodeInfo)
|
||||
continue
|
||||
}
|
||||
for index, stat := range fn.DiskStat {
|
||||
dataPath, hitRate, evicts, limit, maxAlloc, hasAlloc, num, status := "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"
|
||||
if fn.IsActive && fn.IsEnable {
|
||||
dataPath = stat.DataPath
|
||||
hitRate = fmt.Sprintf("%.2f%%", stat.HitRate*100)
|
||||
evicts = strconv.Itoa(stat.Evicts)
|
||||
limit = strconv.FormatUint(uint64(stat.ReadRps), 10)
|
||||
maxAlloc = strconv.FormatInt(stat.MaxAlloc, 10)
|
||||
hasAlloc = strconv.FormatInt(stat.HasAlloc, 10)
|
||||
num = strconv.Itoa(stat.KeyNum)
|
||||
status = strconv.Itoa(stat.Status)
|
||||
}
|
||||
if index != 0 {
|
||||
if groupStats != nil {
|
||||
nodeInfo = arow("", "", "", "", "", "", "", "")
|
||||
} else {
|
||||
nodeInfo = arow("", "", "", "", "", "", "")
|
||||
}
|
||||
}
|
||||
nodeInfo = append(nodeInfo, dataPath, hitRate, evicts, limit, maxAlloc, hasAlloc, num, status)
|
||||
tbl = tbl.append(nodeInfo)
|
||||
}
|
||||
}
|
||||
return tbl
|
||||
}
|
||||
|
||||
// TODO: mandatory design prof http port is service port+1
|
||||
func addr2Prof(addr string) string {
|
||||
arr := strings.SplitN(addr, ":", 2)
|
||||
p, _ := strconv.ParseUint(arr[1], 10, 64)
|
||||
return fmt.Sprintf("%s:%d", arr[0], p+1)
|
||||
}
|
||||
95
tool/remotecache-config/cmd/fmt.go
Normal file
95
tool/remotecache-config/cmd/fmt.go
Normal file
@ -0,0 +1,95 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cubefs/cubefs/proto"
|
||||
)
|
||||
|
||||
var (
|
||||
formatFlashNodeSimpleViewTableTitle = arow("Zone", "ID", "Address", "Active", "Enable", "FlashGroupID", "ReportTime")
|
||||
formatFlashNodeViewTableTitle = append(formatFlashNodeSimpleViewTableTitle[:], "DataPath", "HitRate", "Evicts", "Limit", "MaxAlloc", "HasAlloc", "Num", "Status")
|
||||
formatFlashGroupViewTile = arow("ID", "Weight", "Slots", "ReservedSlots", "Status", "SlotStatus", "PendingSlots", "Step", "FlashNodeCount", "ReducingSlots")
|
||||
)
|
||||
|
||||
func formatClusterView(cv *proto.ClusterView) string {
|
||||
sb := strings.Builder{}
|
||||
sb.WriteString(fmt.Sprintf(" Cluster name : %v\n", cv.Name))
|
||||
sb.WriteString(fmt.Sprintf(" Master leader : %v\n", cv.LeaderAddr))
|
||||
for _, master := range cv.MasterNodes {
|
||||
sb.WriteString(fmt.Sprintf(" Master-%d : %v\n", master.ID, master.Addr))
|
||||
}
|
||||
sb.WriteString(fmt.Sprintf(" FlashNodeHandleReadTimeout : %v ms\n", cv.FlashNodeHandleReadTimeout))
|
||||
sb.WriteString(fmt.Sprintf(" FlashNodeReadDataNodeTimeout : %v ms\n", cv.FlashNodeReadDataNodeTimeout))
|
||||
sb.WriteString(fmt.Sprintf(" RemoteCacheTTL : %v s\n", cv.RemoteCacheTTL))
|
||||
sb.WriteString(fmt.Sprintf(" RemoteCacheReadTimeout : %v ms\n", cv.RemoteCacheReadTimeout))
|
||||
sb.WriteString(fmt.Sprintf(" RemoteCacheMultiRead : %v\n", cv.RemoteCacheMultiRead))
|
||||
sb.WriteString(fmt.Sprintf(" FlashNodeTimeoutCount : %v\n", cv.FlashNodeTimeoutCount))
|
||||
sb.WriteString(fmt.Sprintf(" RemoteCacheSameZoneTimeout : %v microsecond\n", cv.RemoteCacheSameZoneTimeout))
|
||||
sb.WriteString(fmt.Sprintf(" RemoteCacheSameRegionTimeout : %v millisecond\n", cv.RemoteCacheSameRegionTimeout))
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func formatFlashNodeView(fn *proto.FlashNodeViewInfo) string {
|
||||
return "[FlashNode]\n" + alignColumn(
|
||||
arow(" ID", fn.ID),
|
||||
arow(" Address", fn.Addr),
|
||||
arow(" Version", fn.Version),
|
||||
arow(" ZoneName", fn.ZoneName),
|
||||
arow(" FlashGroupID", fn.FlashGroupID),
|
||||
arow(" ReportTime", formatTimeToString(fn.ReportTime)),
|
||||
arow(" IsActive", fn.IsActive),
|
||||
arow(" IsEnable", fn.IsEnable),
|
||||
)
|
||||
}
|
||||
|
||||
func formatTimeToString(t time.Time) string {
|
||||
return t.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
|
||||
func formatIndent(v interface{}) string {
|
||||
b, _ := json.MarshalIndent(v, "", " ")
|
||||
return string(b)
|
||||
}
|
||||
|
||||
var (
|
||||
flashnodeSlotStatTablePattern = "%-12v %-12v %-12v %-12v %-12v\n"
|
||||
flashnodeSlotStatTableHeader = fmt.Sprintf(flashnodeSlotStatTablePattern,
|
||||
"SlotId", "OwnerSlotId", "HitCount", "HitRate", "RecentTime")
|
||||
)
|
||||
|
||||
func formatFlashNodeSlotStat(stat *proto.FlashNodeSlotStat) string {
|
||||
sb := strings.Builder{}
|
||||
sb.WriteString(flashnodeSlotStatTableHeader)
|
||||
for _, slot := range stat.SlotStat {
|
||||
hitrate := fmt.Sprintf("%.2f%%", slot.HitRate*100)
|
||||
recentTime := formatTimeToString(slot.RecentTime)
|
||||
sb.WriteString(fmt.Sprintf(flashnodeSlotStatTablePattern, slot.SlotId, slot.OwnerSlotId, slot.HitCount, hitrate, recentTime))
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func formatYesNo(b bool) string {
|
||||
if b {
|
||||
return "Yes"
|
||||
}
|
||||
return "No"
|
||||
}
|
||||
|
||||
func formatFlashGroupView(fg *proto.FlashGroupAdminView) string {
|
||||
return "[FlashGroup]\n" +
|
||||
fmt.Sprintf(" ID:%v\n", fg.ID) +
|
||||
fmt.Sprintf(" Weight:%v\n", fg.Weight) +
|
||||
fmt.Sprintf(" Slots:%v\n", fg.Slots) +
|
||||
fmt.Sprintf(" ReservedSlots:%v\n", fg.ReservedSlots) +
|
||||
fmt.Sprintf(" IsReducingSlots:%v\n", fg.IsReducingSlots) +
|
||||
fmt.Sprintf(" Status:%v\n", fg.Status) +
|
||||
fmt.Sprintf(" SlotStatus:%v\n", fg.SlotStatus) +
|
||||
fmt.Sprintf(" PedningSlots:%v\n", fg.PendingSlots) +
|
||||
fmt.Sprintf(" Step:%v\n", fg.Step) +
|
||||
fmt.Sprintf(" FlashNodeCount:%v\n", fg.FlashNodeCount)
|
||||
}
|
||||
81
tool/remotecache-config/cmd/root.go
Normal file
81
tool/remotecache-config/cmd/root.go
Normal file
@ -0,0 +1,81 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/cubefs/cubefs/proto"
|
||||
"github.com/cubefs/cubefs/sdk/master"
|
||||
"github.com/cubefs/cubefs/util/log"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type CubeFSCmd struct {
|
||||
CFSCmd *cobra.Command
|
||||
}
|
||||
|
||||
func NewRootCmd(client *master.MasterClient) *CubeFSCmd {
|
||||
var optShowVersion bool
|
||||
cmd := &CubeFSCmd{
|
||||
CFSCmd: &cobra.Command{
|
||||
Use: path.Base(os.Args[0]),
|
||||
Short: "CubeFS Command Line Interface (CLI)",
|
||||
Args: cobra.MinimumNArgs(0),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if optShowVersion {
|
||||
stdout("%v", proto.DumpVersion("CLI"))
|
||||
return
|
||||
}
|
||||
if len(args) == 0 {
|
||||
cmd.Help()
|
||||
return
|
||||
}
|
||||
suggestionsString := ""
|
||||
if suggestions := cmd.SuggestionsFor(args[0]); len(suggestions) > 0 {
|
||||
suggestionsString += "\nDid you mean this?\n"
|
||||
for _, s := range suggestions {
|
||||
suggestionsString += fmt.Sprintf("\t%v\n", s)
|
||||
}
|
||||
}
|
||||
errout(fmt.Errorf("cfs-cli: unknown command %q\n%s", args[0], suggestionsString))
|
||||
},
|
||||
SilenceErrors: true,
|
||||
SilenceUsage: true,
|
||||
},
|
||||
}
|
||||
cmd.CFSCmd.Flags().BoolVarP(&optShowVersion, "version", "v", false, "Show version information")
|
||||
|
||||
cmd.CFSCmd.AddCommand(
|
||||
newConfigCmd(),
|
||||
newClusterCmd(client),
|
||||
newFlashNodeCmd(client),
|
||||
newFlashGroupCmd(client),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
|
||||
var stdout = stdoutf
|
||||
|
||||
func stdoutf(format string, a ...interface{}) {
|
||||
fmt.Fprintf(os.Stdout, format, a...)
|
||||
}
|
||||
|
||||
func errout(err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "Error:", err)
|
||||
log.LogError("Error:", err)
|
||||
log.LogFlush()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func stdoutln(a ...interface{}) {
|
||||
fmt.Fprintln(os.Stdout, a...)
|
||||
}
|
||||
|
||||
func stdoutlnf(format string, a ...interface{}) {
|
||||
stdoutf(format, a...)
|
||||
fmt.Fprintln(os.Stdout)
|
||||
}
|
||||
71
tool/remotecache-config/cmd/util.go
Normal file
71
tool/remotecache-config/cmd/util.go
Normal file
@ -0,0 +1,71 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cubefs/cubefs/util"
|
||||
)
|
||||
|
||||
type table [][]interface{}
|
||||
|
||||
func (t table) append(rows ...[]interface{}) table {
|
||||
return append(t, rows...)
|
||||
}
|
||||
|
||||
func rowStr(r []interface{}) []string {
|
||||
s := make([]string, len(r))
|
||||
for idx := range s {
|
||||
s[idx] = util.Any2String(r[idx])
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func align(rows [][]interface{}) (pattern []string, table [][]string) {
|
||||
table = make([][]string, len(rows))
|
||||
lens := make([]int, len(rows[0]))
|
||||
for idx := range table {
|
||||
str := rowStr(rows[idx])
|
||||
for ii := range str {
|
||||
if l := len(str[ii]); l > lens[ii] {
|
||||
lens[ii] = l
|
||||
}
|
||||
}
|
||||
table[idx] = rowStr(rows[idx])
|
||||
}
|
||||
pattern = make([]string, len(rows[0]))
|
||||
for idx := range lens {
|
||||
pattern[idx] = fmt.Sprintf("%%-%ds", lens[idx])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func str2Any(strs []string) []interface{} {
|
||||
r := make([]interface{}, len(strs))
|
||||
for idx := range strs {
|
||||
r[idx] = strs[idx]
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func alignTableSep(sep string, rows ...[]interface{}) string {
|
||||
pattern, table := align(rows)
|
||||
pt := strings.Join(pattern, sep) + "\n"
|
||||
sb := strings.Builder{}
|
||||
for idx := range table {
|
||||
sb.WriteString(fmt.Sprintf(pt, str2Any(table[idx])...))
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func alignColumn(rows ...[]interface{}) string {
|
||||
return alignTableSep(" : ", rows...)
|
||||
}
|
||||
|
||||
func arow(r ...interface{}) []interface{} {
|
||||
return r
|
||||
}
|
||||
|
||||
func alignTable(rows ...[]interface{}) string {
|
||||
return alignTableSep(" | ", rows...)
|
||||
}
|
||||
48
tool/remotecache-config/main.go
Normal file
48
tool/remotecache-config/main.go
Normal file
@ -0,0 +1,48 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/cubefs/cubefs/proto"
|
||||
"github.com/cubefs/cubefs/sdk/master"
|
||||
"github.com/cubefs/cubefs/tool/remotecache-config/cmd"
|
||||
"github.com/cubefs/cubefs/util/log"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func runCLI() (err error) {
|
||||
var cfg *cmd.Config
|
||||
if cfg, err = cmd.LoadConfig(); err != nil {
|
||||
return fmt.Errorf("load config %v", err)
|
||||
}
|
||||
cfsCli := setupCommands(cfg)
|
||||
return cfsCli.Execute()
|
||||
}
|
||||
|
||||
func setupCommands(cfg *cmd.Config) *cobra.Command {
|
||||
mc := master.NewMasterClient(cfg.MasterAddr, false)
|
||||
mc.SetTimeout(cfg.Timeout)
|
||||
mc.SetClientIDKey(cfg.ClientIDKey)
|
||||
cfsRootCmd := cmd.NewRootCmd(mc)
|
||||
return cfsRootCmd.CFSCmd
|
||||
}
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
_, err = log.InitLog("/tmp/remotecahce-config", "cli", log.DebugLevel, nil, log.DefaultLogLeftSpaceLimitRatio)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
proto.DumpVersion("cfs-cli")
|
||||
master.CliPrint = true
|
||||
if err = runCLI(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error:", err)
|
||||
log.LogError("Error:", err)
|
||||
log.LogFlush()
|
||||
os.Exit(1)
|
||||
}
|
||||
log.LogFlush()
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user