feat(master): add http args parser in master

. #22200600

Signed-off-by: slasher <mcq.sejust@gmail.com>
This commit is contained in:
slasher 2023-12-26 15:12:09 +08:00 committed by zhumingze1108
parent 1877c03de8
commit 2742e1ddb7
10 changed files with 168 additions and 167 deletions

View File

@ -15,7 +15,6 @@
package cmd
import (
"encoding/json"
"fmt"
"math"
"sort"
@ -315,8 +314,7 @@ func newCmdFlashGroupClient(client *master.MasterClient) *cobra.Command {
return
}
stdoutln("Client Response:")
b, _ := json.MarshalIndent(fgv, "", " ")
stdoutln(string(b))
stdoutln(formatIndent(fgv))
},
}
}

View File

@ -38,22 +38,30 @@ func newFlashNodeCmd(client *master.MasterClient) *cobra.Command {
newCmdFlashNodeRemove(client),
newCmdFlashNodeGet(client),
newCmdFlashNodeList(client),
newCmdFlashNodeHTTPStat(client),
newCmdFlashNodeHTTPEvict(client),
)
return cmd
}
func newCmdFlashNodeSet(client *master.MasterClient) *cobra.Command {
return &cobra.Command{
Use: CliOpSet + _flashnodeAddr + " [state]",
Use: CliOpSet + _flashnodeAddr + " [IsEnable]",
Short: "set flash node enable or not",
Args: cobra.MinimumNArgs(2),
Run: func(_ *cobra.Command, args []string) {
var err error
defer func() { errout(err) }()
if err = client.NodeAPI().SetFlashNode(args[0], args[1]); err != nil {
addr := args[0]
enable, err := strconv.ParseBool(args[1])
if err != nil {
return
}
stdoutlnf("set flashnode:%s state:%s success", args[0], args[1])
if err = client.NodeAPI().SetFlashNode(addr, enable); err != nil {
return
}
stdoutlnf("set flashnode:%s enable:%v success", addr, enable)
},
}
}
@ -117,6 +125,46 @@ func newCmdFlashNodeList(client *master.MasterClient) *cobra.Command {
return cmd
}
func newCmdFlashNodeHTTPStat(_ *master.MasterClient) *cobra.Command {
return &cobra.Command{
Use: "httpStat" + _flashnodeAddr,
Short: "show flashnode stat",
Args: cobra.MinimumNArgs(1),
Run: func(_ *cobra.Command, args []string) {
var err error
defer func() { errout(err) }()
stat, err := httpclient.New().WithAddr(addr2Prof(args[0])).FlashNode().Stat()
if err != nil {
return
}
stdoutln(formatIndent(stat))
},
}
}
func newCmdFlashNodeHTTPEvict(_ *master.MasterClient) *cobra.Command {
return &cobra.Command{
Use: "httpEvict" + _flashnodeAddr + " [volume]",
Short: "evict cache in flashnode",
Args: cobra.MinimumNArgs(1),
Run: func(_ *cobra.Command, args []string) {
var err error
defer func() { errout(err) }()
addr := args[0]
if len(args) == 1 {
if err = httpclient.New().WithAddr(addr2Prof(addr)).FlashNode().EvictAll(); err == nil {
stdoutlnf("%s evicts all [OK]", addr)
}
return
}
volume := args[1]
if err = httpclient.New().WithAddr(addr2Prof(addr)).FlashNode().EvictVol(volume); err == nil {
stdoutlnf("%s evicts volume(%s) [OK]", addr, volume)
}
},
}
}
func showFlashNodesView(flashNodeViewInfos []*proto.FlashNodeViewInfo, showStat bool, tbl table) table {
client := httpclient.New()
sort.Slice(flashNodeViewInfos, func(i, j int) bool {

View File

@ -15,6 +15,7 @@
package cmd
import (
"encoding/json"
"fmt"
"math"
"sort"
@ -36,6 +37,11 @@ func formatAddr(ipAddr string, domainAddr string) (addr string) {
return
}
func formatIndent(v interface{}) string {
b, _ := json.MarshalIndent(v, "", " ")
return string(b)
}
func formatClusterView(cv *proto.ClusterView, cn *proto.ClusterNodeInfo, cp *proto.ClusterIP) string {
sb := strings.Builder{}
sb.WriteString(fmt.Sprintf(" Cluster name : %v\n", cv.Name))

View File

@ -26,6 +26,7 @@ import (
"strings"
"time"
"github.com/cubefs/cubefs/cmd/common"
"github.com/cubefs/cubefs/proto"
"github.com/cubefs/cubefs/util"
"github.com/cubefs/cubefs/util/compressor"
@ -33,6 +34,8 @@ import (
"github.com/cubefs/cubefs/util/log"
)
var parseArgs = common.ParseArguments
// Parse the request that adds/deletes a raft node.
func parseRequestForRaftNode(r *http.Request) (id uint64, host string, err error) {
if err = r.ParseForm(); err != nil {

View File

@ -157,10 +157,6 @@ const (
quotaClass = "quotaClass"
quotaOfClass = "quotaOfStorageClass"
dataMediaTypeKey = "dataMediaType"
stateKey = "state"
versionKey = "version"
fgSlotsKey = "slots"
)
const (

View File

@ -23,6 +23,7 @@ import (
"sync"
"time"
"github.com/cubefs/cubefs/cmd/common"
"github.com/cubefs/cubefs/proto"
"github.com/cubefs/cubefs/util/errors"
"github.com/cubefs/cubefs/util/exporter"
@ -145,13 +146,13 @@ func (m *Server) turnFlashGroup(w http.ResponseWriter, r *http.Request) {
defer func() {
doStatAndMetric(proto.AdminFlashGroupTurn, metric, err, nil)
}()
r.ParseForm()
enabled, err := extractStatus(r)
if err != nil {
var enable common.Bool
if err = parseArgs(r, enable.Enable()); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
topo := m.cluster.flashNodeTopo
enabled := enable.V
if enabled {
topo.clientOff.Store([]byte(nil))
} else {
@ -199,13 +200,13 @@ func (m *Server) removeFlashGroup(w http.ResponseWriter, r *http.Request) {
defer func() {
doStatAndMetric(proto.AdminFlashGroupRemove, metric, err, nil)
}()
var flashGroupID uint64
if flashGroupID, err = extractFlashGroupID(r); err != nil {
var flashGroupID common.Uint
if err = parseArgs(r, flashGroupID.ID()); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
var flashGroup *FlashGroup
if flashGroup, err = m.cluster.flashNodeTopo.getFlashGroup(flashGroupID); err != nil {
if flashGroup, err = m.cluster.flashNodeTopo.getFlashGroup(flashGroupID.V); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
@ -239,7 +240,7 @@ func (c *Cluster) removeFlashGroup(flashGroup *FlashGroup) (err error) {
func (m *Server) setFlashGroup(w http.ResponseWriter, r *http.Request) {
var (
flashGroupID uint64
flashGroupID common.Uint
fgStatus proto.FlashGroupStatus
flashGroup *FlashGroup
err error
@ -248,11 +249,17 @@ func (m *Server) setFlashGroup(w http.ResponseWriter, r *http.Request) {
defer func() {
doStatAndMetric(proto.AdminFlashGroupSet, metric, err, nil)
}()
if flashGroupID, fgStatus, err = parseRequestForSetFlashGroup(r); err != nil {
var active common.Bool
if err = parseArgs(r, flashGroupID.ID(), active.Enable().OnValue(func() error {
fgStatus = argConvertFlashGroupStatus(active.V)
return nil
}),
); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
if flashGroup, err = m.cluster.flashNodeTopo.getFlashGroup(flashGroupID); err != nil {
if flashGroup, err = m.cluster.flashNodeTopo.getFlashGroup(flashGroupID.V); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
@ -276,7 +283,7 @@ func (m *Server) setFlashGroup(w http.ResponseWriter, r *http.Request) {
func (m *Server) getFlashGroup(w http.ResponseWriter, r *http.Request) {
var (
flashGroupID uint64
flashGroupID common.Uint
flashGroup *FlashGroup
err error
)
@ -284,11 +291,11 @@ func (m *Server) getFlashGroup(w http.ResponseWriter, r *http.Request) {
defer func() {
doStatAndMetric(proto.AdminFlashGroupGet, metric, err, nil)
}()
if flashGroupID, err = extractFlashGroupID(r); err != nil {
if err = parseArgs(r, flashGroupID.ID()); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
if flashGroup, err = m.cluster.flashNodeTopo.getFlashGroup(flashGroupID); err != nil {
if flashGroup, err = m.cluster.flashNodeTopo.getFlashGroup(flashGroupID.V); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
@ -301,8 +308,7 @@ func (m *Server) flashGroupAddFlashNode(w http.ResponseWriter, r *http.Request)
defer func() {
doStatAndMetric(proto.AdminFlashGroupNodeAdd, metric, err, nil)
}()
flashGroupID, addr, zoneName, count, err := parseRequestForManageFlashNodeOfFlashGroup(r)
flashGroupID, addr, zoneName, count, err := parseArgsFlashGroupNode(r)
if err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
@ -386,7 +392,7 @@ func (m *Server) flashGroupRemoveFlashNode(w http.ResponseWriter, r *http.Reques
defer func() {
doStatAndMetric(proto.AdminFlashGroupNodeRemove, metric, err, nil)
}()
flashGroupID, addr, zoneName, count, err := parseRequestForManageFlashNodeOfFlashGroup(r)
flashGroupID, addr, zoneName, count, err := parseArgsFlashGroupNode(r)
if err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
@ -468,13 +474,19 @@ func (m *Server) listFlashGroups(w http.ResponseWriter, r *http.Request) {
defer func() {
doStatAndMetric(proto.AdminFlashGroupList, metric, err, nil)
}()
if fgStatus, err = extractFlashGroupStatus(r); err != nil {
if value := r.FormValue(enableKey); value == "" {
var active common.Bool
if err = parseArgs(r, active.Enable().OmitEmpty().
OnEmpty(func() error {
allStatus = true // resp all flash groups
} else {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
return nil
}).
OnValue(func() error {
fgStatus = argConvertFlashGroupStatus(active.V)
return nil
}),
); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
fgv := m.cluster.flashNodeTopo.getFlashGroupsAdminView(fgStatus, allStatus)
sendOkReply(w, r, newSuccessHTTPReply(fgv))
@ -494,65 +506,10 @@ func (m *Server) clientFlashGroups(w http.ResponseWriter, r *http.Request) {
send(w, r, cache)
}
func parseRequestForManageFlashNodeOfFlashGroup(r *http.Request) (flashGroupID uint64, addr, zoneName string, count int, err error) {
if flashGroupID, err = extractFlashGroupID(r); err != nil {
return
}
if addr = r.FormValue(addrKey); addr != "" {
return
}
if zoneName, err = extractZoneName(r); err != nil {
return
}
if count, err = extractCount(r); err != nil {
return
}
if count <= 0 {
err = unmatchedKey(countKey)
}
return
}
func parseRequestForSetFlashGroup(r *http.Request) (flashGroupID uint64, fgStatus proto.FlashGroupStatus, err error) {
if flashGroupID, err = extractFlashGroupID(r); err != nil {
return
}
fgStatus, err = extractFlashGroupStatus(r)
return
}
func extractFlashGroupStatus(r *http.Request) (fgStatus proto.FlashGroupStatus, err error) {
if err = r.ParseForm(); err != nil {
return
}
var status bool
if status, err = extractStatus(r); err != nil {
return
}
if status {
fgStatus = proto.FlashGroupStatus_Active
} else {
fgStatus = proto.FlashGroupStatus_Inactive
}
return
}
func extractFlashGroupID(r *http.Request) (ID uint64, err error) {
if err = r.ParseForm(); err != nil {
return
}
var value string
if value = r.FormValue(idKey); value == "" {
err = keyNotFound(idKey)
return
}
return strconv.ParseUint(value, 10, 64)
}
func getSetSlots(r *http.Request) (slots []uint32) {
r.ParseForm()
slots = make([]uint32, 0)
slotStr := r.FormValue(fgSlotsKey)
slotStr := r.FormValue("slots")
if slotStr != "" {
arr := strings.Split(slotStr, ",")
for i := 0; i < len(arr); i++ {
@ -561,31 +518,38 @@ func getSetSlots(r *http.Request) (slots []uint32) {
continue
}
if len(slots) >= defaultFlashGroupSlotsCount {
continue
return
}
slots = append(slots, uint32(slot))
}
}
return
}
func extractZoneName(r *http.Request) (name string, err error) {
if name = r.FormValue(zoneNameKey); name == "" {
err = keyNotFound(zoneNameKey)
func parseArgsFlashGroupNode(r *http.Request) (id uint64, addr, zoneName string, count int, err error) {
var (
idV common.Uint
addrV common.String
zoneV common.String
countV common.Int
)
if err = parseArgs(r, idV.ID(), addrV.Addr()); err == nil {
id = idV.V
addr = addrV.V
return
}
if err = parseArgs(r, idV.ID(), addrV.Addr().OmitEmpty(), zoneV.ZoneName(), countV.Count()); err == nil {
id = idV.V
addr = addrV.V
zoneName = zoneV.V
count = int(countV.V)
}
return
}
func extractCount(r *http.Request) (count int, err error) {
var value string
if value = r.FormValue(countKey); value == "" {
return
func argConvertFlashGroupStatus(active bool) proto.FlashGroupStatus {
if active {
return proto.FlashGroupStatus_Active
}
if count, err = strconv.Atoi(value); err != nil {
err = unmatchedKey(countKey)
return
}
return
return proto.FlashGroupStatus_Inactive
}

View File

@ -22,7 +22,9 @@ import (
"sync"
"time"
"github.com/cubefs/cubefs/cmd/common"
"github.com/cubefs/cubefs/proto"
"github.com/cubefs/cubefs/util"
"github.com/cubefs/cubefs/util/errors"
"github.com/cubefs/cubefs/util/exporter"
"github.com/cubefs/cubefs/util/log"
@ -156,9 +158,9 @@ func (flashNode *FlashNode) createHeartbeatTask(masterAddr string) (task *proto.
func (m *Server) addFlashNode(w http.ResponseWriter, r *http.Request) {
var (
nodeAddr string
zoneName string
version string
nodeAddr common.String
zoneName common.String
version common.String
id uint64
err error
)
@ -166,11 +168,19 @@ func (m *Server) addFlashNode(w http.ResponseWriter, r *http.Request) {
defer func() {
doStatAndMetric(proto.FlashNodeAdd, metric, err, nil)
}()
if nodeAddr, zoneName, version, err = parseRequestForAddFlashNode(r); err != nil {
if err = parseArgs(r, argParserNodeAddr(&nodeAddr),
zoneName.ZoneName().OmitEmpty().OnValue(func() error {
if zoneName.V == "" {
zoneName.V = DefaultZoneName
}
return nil
}),
version.Key("version").OmitEmpty(),
); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if id, err = m.cluster.addFlashNode(nodeAddr, zoneName, version); err != nil {
if id, err = m.cluster.addFlashNode(nodeAddr.V, zoneName.V, version.V); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
@ -218,10 +228,11 @@ func (m *Server) listFlashNodes(w http.ResponseWriter, r *http.Request) {
doStatAndMetric(proto.FlashNodeList, metric, nil, nil)
}()
zoneFlashNodes := make(map[string][]*proto.FlashNodeViewInfo)
listAll := extractListAll(r)
var listAll common.Bool
parseArgs(r, listAll.All().OmitEmpty().OmitError())
m.cluster.flashNodeTopo.flashNodeMap.Range(func(key, value interface{}) bool {
flashNode := value.(*FlashNode)
if listAll || flashNode.isActiveAndEnable() {
if listAll.V || flashNode.isActiveAndEnable() {
zoneFlashNodes[flashNode.ZoneName] = append(zoneFlashNodes[flashNode.ZoneName], flashNode.getFlashNodeViewInfo())
}
return true
@ -235,13 +246,13 @@ func (m *Server) getFlashNode(w http.ResponseWriter, r *http.Request) {
defer func() {
doStatAndMetric(proto.FlashNodeGet, metric, err, nil)
}()
var nodeAddr string
if nodeAddr, err = parseAndExtractNodeAddr(r); err != nil {
var nodeAddr common.String
if err = parseArgs(r, argParserNodeAddr(&nodeAddr)); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
var flashNode *FlashNode
if flashNode, err = m.cluster.peekFlashNode(nodeAddr); err != nil {
if flashNode, err = m.cluster.peekFlashNode(nodeAddr.V); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
@ -254,13 +265,13 @@ func (m *Server) removeFlashNode(w http.ResponseWriter, r *http.Request) {
defer func() {
doStatAndMetric(proto.FlashNodeRemove, metric, err, nil)
}()
var offLineAddr string
if offLineAddr, err = parseAndExtractNodeAddr(r); err != nil {
var offLineAddr common.String
if err = parseArgs(r, argParserNodeAddr(&offLineAddr)); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
var node *FlashNode
if node, err = m.cluster.peekFlashNode(offLineAddr); err != nil {
if node, err = m.cluster.peekFlashNode(offLineAddr.V); err != nil {
sendErrReply(w, r, newErrHTTPReply(proto.ErrDataNodeNotExists))
return
}
@ -316,8 +327,8 @@ func (c *Cluster) delFlashNodeFromCache(flashNode *FlashNode) {
func (m *Server) setFlashNode(w http.ResponseWriter, r *http.Request) {
var (
nodeAddr string
state bool
nodeAddr common.String
enable common.Bool
flashNode *FlashNode
err error
)
@ -325,27 +336,27 @@ func (m *Server) setFlashNode(w http.ResponseWriter, r *http.Request) {
defer func() {
doStatAndMetric(proto.FlashNodeSet, metric, err, nil)
}()
if nodeAddr, state, err = parseAndExtractFlashNode(r); err != nil {
if err = parseArgs(r, argParserNodeAddr(&nodeAddr), enable.Enable()); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if flashNode, err = m.cluster.peekFlashNode(nodeAddr); err != nil {
if flashNode, err = m.cluster.peekFlashNode(nodeAddr.V); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
if err = m.cluster.updateFlashNode(flashNode, state); err != nil {
if err = m.cluster.updateFlashNode(flashNode, enable.V); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
sendOkReply(w, r, newSuccessHTTPReply("set flashNode success"))
}
func (c *Cluster) updateFlashNode(flashNode *FlashNode, state bool) (err error) {
func (c *Cluster) updateFlashNode(flashNode *FlashNode, enable bool) (err error) {
flashNode.Lock()
defer flashNode.Unlock()
if flashNode.IsEnable != state {
if flashNode.IsEnable != enable {
oldState := flashNode.IsEnable
flashNode.IsEnable = state
flashNode.IsEnable = enable
if err = c.syncUpdateFlashNode(flashNode); err != nil {
flashNode.IsEnable = oldState
return
@ -383,44 +394,19 @@ func (c *Cluster) syncPutFlashNodeInfo(opType uint32, flashNode *FlashNode) (err
func (c *Cluster) peekFlashNode(addr string) (flashNode *FlashNode, err error) {
value, ok := c.flashNodeTopo.flashNodeMap.Load(addr)
if !ok {
err = errors.Trace(flashNodeNotFound(addr), "%v not found", addr)
err = errors.Trace(notFoundMsg(fmt.Sprintf("flashnode[%v]", addr)), "")
return
}
flashNode = value.(*FlashNode)
return
}
func flashNodeNotFound(addr string) (err error) {
return notFoundMsg(fmt.Sprintf("flashnode[%v]", addr))
}
func parseRequestForAddFlashNode(r *http.Request) (nodeAddr, zoneName, version string, err error) {
if err = r.ParseForm(); err != nil {
return
}
if nodeAddr, err = extractNodeAddr(r); err != nil {
return
}
if zoneName = r.FormValue(zoneNameKey); zoneName == "" {
zoneName = DefaultZoneName
}
version = r.FormValue(versionKey)
return
}
func parseAndExtractFlashNode(r *http.Request) (nodeAddr string, state bool, err error) {
if err = r.ParseForm(); err != nil {
return
}
nodeAddr, err = extractNodeAddr(r)
if err != nil {
return
}
state, err = strconv.ParseBool(r.FormValue(stateKey))
return
}
func extractListAll(r *http.Request) (all bool) {
all, _ = strconv.ParseBool(r.FormValue("all"))
return
func argParserNodeAddr(nodeAddr *common.String) *common.Argument {
return nodeAddr.Addr().OnValue(func() error {
if ipAddr, ok := util.ParseAddrToIpAddr(nodeAddr.V); ok {
nodeAddr.V = ipAddr
return nil
}
return unmatchedKey(new(common.String).Addr().Key())
})
}

View File

@ -32,11 +32,11 @@ func testFlashNodeSet(t *testing.T) {
require.NoError(t, err)
require.True(t, fnView.IsEnable)
require.NoError(t, mc.NodeAPI().SetFlashNode(mfs1Addr, "false"))
require.NoError(t, mc.NodeAPI().SetFlashNode(mfs1Addr, false))
fnView, err = mc.NodeAPI().GetFlashNode(mfs1Addr)
require.NoError(t, err)
require.False(t, fnView.IsEnable)
require.NoError(t, mc.NodeAPI().SetFlashNode(mfs1Addr, "true"))
require.NoError(t, mc.NodeAPI().SetFlashNode(mfs1Addr, true))
}
func testFlashNodeRemove(t *testing.T) {
@ -94,9 +94,9 @@ func testFlashNodeList(t *testing.T) {
require.Equal(t, 2, len(zoneNodes[testZone2]))
require.Equal(t, 3, len(zoneNodes[testZone3]))
require.NoError(t, mc.NodeAPI().SetFlashNode(mfs7Addr, "0"))
require.NoError(t, mc.NodeAPI().SetFlashNode(mfs7Addr, false))
defer func() {
require.NoError(t, mc.NodeAPI().SetFlashNode(mfs7Addr, "1"))
require.NoError(t, mc.NodeAPI().SetFlashNode(mfs7Addr, true))
}()
zoneNodes, err = mc.NodeAPI().ListFlashNodes(false)

View File

@ -63,12 +63,12 @@ func (zone *FlashNodeZone) selectFlashNodes(count int, excludeHosts []string) (n
if contains(excludeHosts, flashNode.Addr) {
return true
}
if flashNode.isWriteable() {
newHosts = append(newHosts, flashNode.Addr)
}
if len(newHosts) >= count {
return false
}
if flashNode.isWriteable() {
newHosts = append(newHosts, flashNode.Addr)
}
return true
})
if len(newHosts) != count {

View File

@ -196,9 +196,9 @@ func (api *NodeAPI) AddFlashNode(serverAddr, zoneName, version string) (id uint6
return
}
func (api *NodeAPI) SetFlashNode(addr, state string) (err error) {
return api.mc.request(newRequest(post, proto.FlashNodeSet).Header(api.h).
addParam("addr", addr).addParam("state", state))
func (api *NodeAPI) SetFlashNode(addr string, enable bool) (err error) {
return api.mc.request(newRequest(post, proto.FlashNodeSet).
Header(api.h).Param(anyParam{"addr", addr}, anyParam{"enable", enable}))
}
func (api *NodeAPI) RemoveFlashNode(nodeAddr string) (result string, err error) {