cubefs/master/flash_group.go
chihe 81643df8cb fix(master): add triggerCondition for dataNode balance
@formatter:off

Signed-off-by: chihe <chihe@oppo.com>
2025-12-24 19:31:51 +08:00

397 lines
12 KiB
Go

// Copyright 2023 The CubeFS Authors.
//
// 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 master
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
"github.com/cubefs/cubefs/cmd/common"
"github.com/cubefs/cubefs/proto"
"github.com/cubefs/cubefs/remotecache/flashgroupmanager"
"github.com/cubefs/cubefs/util/errors"
"github.com/cubefs/cubefs/util/exporter"
"github.com/cubefs/cubefs/util/log"
)
func (c *Cluster) syncAddFlashGroup(flashGroup *flashgroupmanager.FlashGroup) (err error) {
return c.syncPutFlashGroupInfo(opSyncAddFlashGroup, flashGroup)
}
func (c *Cluster) syncDeleteFlashGroup(flashGroup *flashgroupmanager.FlashGroup) (err error) {
return c.syncPutFlashGroupInfo(opSyncDeleteFlashGroup, flashGroup)
}
func (c *Cluster) syncUpdateFlashGroup(flashGroup *flashgroupmanager.FlashGroup) (err error) {
return c.syncPutFlashGroupInfo(opSyncUpdateFlashGroup, flashGroup)
}
func (c *Cluster) syncPutFlashGroupInfo(opType uint32, flashGroup *flashgroupmanager.FlashGroup) (err error) {
metadata := new(RaftCmd)
metadata.Op = opType
metadata.K = flashGroupPrefix + strconv.FormatUint(flashGroup.ID, 10)
metadata.V, err = json.Marshal(flashGroup.FlashGroupValue)
if err != nil {
return errors.New(err.Error())
}
return c.submit(metadata)
}
func (m *Server) turnFlashGroup(w http.ResponseWriter, r *http.Request) {
var err error
metric := exporter.NewTPCnt(apiToMetricsName(proto.AdminFlashGroupTurn))
defer func() {
doStatAndMetric(proto.AdminFlashGroupTurn, metric, 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
topo.TurnFlashGroup(enabled)
sendOkReply(w, r, newSuccessHTTPReply(fmt.Sprintf("turn %v", enabled)))
}
func (m *Server) createFlashGroup(w http.ResponseWriter, r *http.Request) {
var (
err error
setSlots []uint32
setWeight uint32
gradualFlag bool
step uint32
)
metric := exporter.NewTPCnt(apiToMetricsName(proto.AdminFlashGroupCreate))
defer func() {
doStatAndMetric(proto.AdminFlashGroupCreate, metric, err, nil)
}()
if setSlots, err = getSetSlots(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if setWeight, err = getSetWeight(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if gradualFlag, err = getGradualFlag(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if step, err = getStep(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if gradualFlag && step <= 0 {
err = fmt.Errorf("the step size(%v) must be greater than 0 when flashGroup gradually creates the slots", step)
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
flashGroup, err := m.cluster.createFlashGroup(setSlots, setWeight, gradualFlag, step)
if err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
sendOkReply(w, r, newSuccessHTTPReply(flashGroup.GetAdminView()))
}
func (c *Cluster) createFlashGroup(setSlots []uint32, setWeight uint32, gradualFlag bool,
step uint32,
) (fg *flashgroupmanager.FlashGroup, err error) {
defer func() {
if err != nil {
log.LogErrorf("action[addFlashGroup],clusterID[%v] err:%v ", c.Name, err.Error())
}
}()
id, err := c.idAlloc.allocateCommonID()
if err != nil {
return
}
fg, err = c.flashNodeTopo.CreateFlashGroup(id, c.syncUpdateFlashGroup, c.syncAddFlashGroup, setSlots, setWeight, gradualFlag, step)
log.LogInfof("action[addFlashGroup],clusterID[%v] id:%v Weight:%v Slots:%v success", c.Name, fg.ID, fg.Weight, fg.GetSlots())
return
}
func (m *Server) removeFlashGroup(w http.ResponseWriter, r *http.Request) {
var (
err error
gradualFlag bool
step uint32
)
metric := exporter.NewTPCnt(apiToMetricsName(proto.AdminFlashGroupRemove))
defer func() {
doStatAndMetric(proto.AdminFlashGroupRemove, metric, err, nil)
}()
var flashGroupID common.Uint
if err = parseArgs(r, flashGroupID.ID()); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
if gradualFlag, err = getGradualFlag(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if step, err = getStep(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if gradualFlag && step <= 0 {
err = fmt.Errorf("the step size(%v) must be greater than 0 when flashGroup gradually deletes the slots", step)
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
var flashGroup *flashgroupmanager.FlashGroup
if flashGroup, err = m.cluster.flashNodeTopo.RemoveFlashGroup(flashGroupID.V, gradualFlag, step,
m.cluster.syncUpdateFlashGroup, m.cluster.syncUpdateFlashNode, m.cluster.syncDeleteFlashGroup); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
sendOkReply(w, r, newSuccessHTTPReply(fmt.Sprintf("remove flashGroup:%v successfully,Slots:%v nodeCount:%v",
flashGroup.ID, flashGroup.GetSlots(), flashGroup.GetFlashNodesCount())))
}
func (m *Server) setFlashGroup(w http.ResponseWriter, r *http.Request) {
var (
flashGroupID common.Uint
fgStatus proto.FlashGroupStatus
flashGroup *flashgroupmanager.FlashGroup
err error
)
metric := exporter.NewTPCnt(apiToMetricsName(proto.AdminFlashGroupSet))
defer func() {
doStatAndMetric(proto.AdminFlashGroupSet, metric, 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.V); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
err = flashGroup.UpdateStatus(fgStatus, m.cluster.syncUpdateFlashGroup, m.cluster.flashNodeTopo)
if err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
sendOkReply(w, r, newSuccessHTTPReply(flashGroup.GetAdminView()))
}
func (m *Server) getFlashGroup(w http.ResponseWriter, r *http.Request) {
var (
flashGroupID common.Uint
flashGroup *flashgroupmanager.FlashGroup
err error
)
metric := exporter.NewTPCnt(apiToMetricsName(proto.AdminFlashGroupGet))
defer func() {
doStatAndMetric(proto.AdminFlashGroupGet, metric, err, nil)
}()
if err = parseArgs(r, flashGroupID.ID()); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
if flashGroup, err = m.cluster.flashNodeTopo.GetFlashGroup(flashGroupID.V); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
sendOkReply(w, r, newSuccessHTTPReply(flashGroup.GetAdminView()))
}
func (m *Server) flashGroupAddFlashNode(w http.ResponseWriter, r *http.Request) {
var err error
metric := exporter.NewTPCnt(apiToMetricsName(proto.AdminFlashGroupNodeAdd))
defer func() {
doStatAndMetric(proto.AdminFlashGroupNodeAdd, metric, err, nil)
}()
flashGroupID, addr, zoneName, count, err := parseArgsFlashGroupNode(r)
if err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
var flashGroup *flashgroupmanager.FlashGroup
if flashGroup, err = m.cluster.flashNodeTopo.FlashGroupAddFlashNode(flashGroupID,
addr, zoneName, count, m.cluster.syncUpdateFlashNode); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
sendOkReply(w, r, newSuccessHTTPReply(flashGroup.GetAdminView()))
}
func (m *Server) flashGroupRemoveFlashNode(w http.ResponseWriter, r *http.Request) {
var err error
metric := exporter.NewTPCnt(apiToMetricsName(proto.AdminFlashGroupNodeRemove))
defer func() {
doStatAndMetric(proto.AdminFlashGroupNodeRemove, metric, err, nil)
}()
flashGroupID, addr, zoneName, count, err := parseArgsFlashGroupNode(r)
if err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
var flashGroup *flashgroupmanager.FlashGroup
if flashGroup, err = m.cluster.flashNodeTopo.FlashGroupRemoveFlashNode(flashGroupID,
addr, zoneName, count, m.cluster.syncUpdateFlashNode); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
sendOkReply(w, r, newSuccessHTTPReply(flashGroup.GetAdminView()))
}
func (m *Server) listFlashGroups(w http.ResponseWriter, r *http.Request) {
var (
fgStatus proto.FlashGroupStatus
allStatus bool
err error
)
metric := exporter.NewTPCnt(apiToMetricsName(proto.AdminFlashGroupList))
defer func() {
doStatAndMetric(proto.AdminFlashGroupList, metric, err, nil)
}()
var active common.Bool
if err = parseArgs(r, active.Enable().OmitEmpty().
OnEmpty(func() error {
allStatus = true // resp all flash groups
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))
}
func (m *Server) clientFlashGroups(w http.ResponseWriter, r *http.Request) {
var err error
metric := exporter.NewTPCnt(apiToMetricsName(proto.ClientFlashGroups))
defer func() {
doStatAndMetric(proto.ClientFlashGroups, metric, err, nil)
}()
if !m.metaReady {
sendErrReply(w, r, newErrHTTPReply(fmt.Errorf("meta not ready")))
return
}
cache := m.cluster.flashNodeTopo.GetClientResponse()
if len(cache) == 0 {
sendErrReply(w, r, newErrHTTPReply(fmt.Errorf("flash group response cache is empty")))
return
}
send(w, r, cache)
}
func getSetSlots(r *http.Request) (slots []uint32, err error) {
r.ParseForm()
slots = make([]uint32, 0)
slotStr := r.FormValue("slots")
if slotStr != "" {
arr := strings.Split(slotStr, ",")
var slot uint64
for i := 0; i < len(arr); i++ {
slot, err = strconv.ParseUint(arr[i], 10, 32)
if err != nil {
return nil, err
}
if len(slots) >= defaultFlashGroupSlotsCount {
return
}
slots = append(slots, uint32(slot))
}
}
return
}
func getSetWeight(r *http.Request) (weight uint32, err error) {
var value uint64
r.ParseForm()
weightStr := r.FormValue("weight")
if weightStr != "" {
value, err = strconv.ParseUint(weightStr, 10, 32)
weight = uint32(value)
}
return
}
func getGradualFlag(r *http.Request) (gradualCreateFlag bool, err error) {
r.ParseForm()
flagStr := r.FormValue("gradualFlag")
if flagStr != "" {
gradualCreateFlag, err = strconv.ParseBool(flagStr)
}
return
}
func getStep(r *http.Request) (step uint32, err error) {
var value uint64
r.ParseForm()
stepStr := r.FormValue("step")
if stepStr != "" {
value, err = strconv.ParseUint(stepStr, 10, 32)
step = uint32(value)
}
return
}
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 argConvertFlashGroupStatus(active bool) proto.FlashGroupStatus {
if active {
return proto.FlashGroupStatus_Active
}
return proto.FlashGroupStatus_Inactive
}