cubefs/master/flash_node.go
2025-08-08 14:59:24 +08:00

926 lines
29 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"
"io"
"net/http"
"strconv"
"strings"
"sync"
"time"
"github.com/cubefs/cubefs/cmd/common"
"github.com/cubefs/cubefs/proto"
"github.com/cubefs/cubefs/sdk/httpclient"
"github.com/cubefs/cubefs/util"
"github.com/cubefs/cubefs/util/auditlog"
"github.com/cubefs/cubefs/util/errors"
"github.com/cubefs/cubefs/util/exporter"
"github.com/cubefs/cubefs/util/log"
"github.com/google/uuid"
)
const (
_defaultNodeTimeoutDuration = defaultNodeTimeOutSec * time.Second
)
type flashNodeValue struct {
// immutable
ID uint64
Addr string
ZoneName string
Version string
// mutable
FlashGroupID uint64 // 0: have not allocated to flash group
IsEnable bool
TaskCountLimit int
}
type FlashNode struct {
TaskManager *AdminTaskManager
sync.RWMutex
flashNodeValue
DiskStat []*proto.FlashNodeDiskCacheStat
ReportTime time.Time
IsActive bool
LimiterStatus *proto.FlashNodeLimiterStatusInfo
WorkRole string
}
func newFlashNode(addr, zoneName, clusterID, version string, isEnable bool) *FlashNode {
node := new(FlashNode)
node.Addr = addr
node.ZoneName = zoneName
node.Version = version
node.IsEnable = isEnable
node.TaskManager = newAdminTaskManager(addr, clusterID)
return node
}
func (flashNode *FlashNode) clean() {
flashNode.TaskManager.exitCh <- struct{}{}
}
func (flashNode *FlashNode) setActive() {
flashNode.Lock()
flashNode.ReportTime = time.Now()
flashNode.IsActive = true
flashNode.Unlock()
}
func (flashNode *FlashNode) isWriteable() (ok bool) {
flashNode.RLock()
if flashNode.FlashGroupID == unusedFlashNodeFlashGroupID &&
time.Since(flashNode.ReportTime) < _defaultNodeTimeoutDuration {
ok = true
}
flashNode.RUnlock()
return
}
func (flashNode *FlashNode) isActiveAndEnable() (ok bool) {
flashNode.RLock()
ok = flashNode.IsActive && flashNode.IsEnable
flashNode.RUnlock()
return
}
func (flashNode *FlashNode) isEnable() (ok bool) {
flashNode.RLock()
ok = flashNode.IsEnable
flashNode.RUnlock()
return
}
func (flashNode *FlashNode) getFlashNodeViewInfo() (info *proto.FlashNodeViewInfo) {
flashNode.RLock()
info = &proto.FlashNodeViewInfo{
ID: flashNode.ID,
Addr: flashNode.Addr,
ReportTime: flashNode.ReportTime,
IsActive: flashNode.IsActive,
Version: flashNode.Version,
ZoneName: flashNode.ZoneName,
FlashGroupID: flashNode.FlashGroupID,
IsEnable: flashNode.IsEnable,
DiskStat: flashNode.DiskStat,
LimiterStatus: flashNode.LimiterStatus,
}
flashNode.RUnlock()
return
}
func (flashNode *FlashNode) updateFlashNodeStatHeartbeat(resp *proto.FlashNodeHeartbeatResponse) {
log.LogInfof("updateFlashNodeStatHeartbeat, flashNode:%v, resp[%v], time:%v", flashNode.Addr, resp, time.Now().Format("2006-01-02 15:04:05"))
flashNode.Lock()
flashNode.DiskStat = resp.Stat
flashNode.LimiterStatus = resp.LimiterStatus
flashNode.TaskCountLimit = resp.FlashNodeTaskCountLimit
flashNode.Unlock()
}
func (c *Cluster) addFlashNodeHeartbeatTasks(tasks []*proto.AdminTask) {
for _, t := range tasks {
if t == nil {
continue
}
node, err := c.peekFlashNode(t.OperatorAddr)
if err != nil {
log.LogWarn(fmt.Sprintf("action[syncFlashNodeHeartbeatTasks],nodeAddr:%v,taskID:%v,err:%v", t.OperatorAddr, t.ID, err.Error()))
continue
}
node.TaskManager.AddTask(t)
}
}
func (c *Cluster) syncFlashNodeSetIOLimitTasks(tasks []*proto.AdminTask) {
for _, t := range tasks {
if t == nil {
continue
}
node, err := c.peekFlashNode(t.OperatorAddr)
if err != nil {
log.LogWarn(fmt.Sprintf("action[syncFlashNodeHeartbeatTasks],nodeAddr:%v,taskID:%v,err:%v", t.OperatorAddr, t.ID, err.Error()))
continue
}
if _, err = node.TaskManager.syncSendAdminTask(t); err != nil {
log.LogWarn(fmt.Sprintf("action[syncFlashNodeHeartbeatTasks],nodeAddr:%v,taskID:%v,err:%v", t.OperatorAddr, t.ID, err.Error()))
continue
}
}
}
func (c *Cluster) handleManualTaskProcessing(flashNode *FlashNode, resp *proto.FlashNodeHeartbeatResponse) {
for _, taskRsp := range resp.ManualScanningTasks {
manualTask, ok := c.flashManMgr.LoadManualTaskById(taskRsp.ID)
if !ok {
continue
}
log.LogDebugf("action[handleFlashNodeHeartbeatResp], get manger rlock for task[%v] and taskRsp[%v]", manualTask, taskRsp)
// avoid updating TaskResults incorrectly when received handleFlashNodeHeartbeatResp first and then handleFlashNodeHeartbeatResp
manualTask.Lock()
if proto.ManualTaskDone(manualTask.Status) {
log.LogInfof("action[handleFlashNodeHeartbeatResp], flashNode[%v] task[%v] already done", flashNode.Addr, taskRsp.ID)
} else {
manualTask.SetResponse(taskRsp)
}
manualTask.Unlock()
log.LogDebugf("action[handleFlashNodeHeartbeatResp], flashNode[%v] taskRsp: %v", flashNode.Addr, taskRsp)
}
c.flashManMgr.flashNodeTaskStatus.mu.Lock()
c.flashManMgr.flashNodeTaskStatus.WorkingCount[flashNode.Addr] = len(resp.ManualScanningTasks)
c.flashManMgr.flashNodeTaskStatus.mu.Unlock()
log.LogInfof("action[handleFlashNodeHeartbeatResp], flashNode[%v], heartbeat success", flashNode.Addr)
}
func (c *Cluster) checkFlashNodeHeartbeat() {
tasks := make([]*proto.AdminTask, 0)
c.flashNodeTopo.flashNodeMap.Range(func(addr, flashNode interface{}) bool {
node := flashNode.(*FlashNode)
node.checkLiveliness()
task := node.createHeartbeatTask(c.masterAddr(), c.cfg.flashNodeHandleReadTimeout, c.cfg.flashNodeReadDataNodeTimeout)
tasks = append(tasks, task)
return true
})
c.addFlashNodeHeartbeatTasks(tasks)
}
func (flashNode *FlashNode) checkLiveliness() {
flashNode.Lock()
if time.Since(flashNode.ReportTime) > _defaultNodeTimeoutDuration {
msg := fmt.Sprintf("flashnode[%v] heartbeat lost, last heartbeat time %v", flashNode.Addr, flashNode.ReportTime)
auditlog.LogMasterOp("checkLiveliness", msg, nil)
flashNode.IsActive = false
}
flashNode.Unlock()
}
func (flashNode *FlashNode) createHeartbeatTask(masterAddr string, flashNodeHandleReadTimeout int, flashNodeReadDataNodeTimeout int) (task *proto.AdminTask) {
request := &proto.HeartBeatRequest{
CurrTime: time.Now().Unix(),
MasterAddr: masterAddr,
}
request.FlashNodeHandleReadTimeout = flashNodeHandleReadTimeout
request.FlashNodeReadDataNodeTimeout = flashNodeReadDataNodeTimeout
task = proto.NewAdminTask(proto.OpFlashNodeHeartbeat, flashNode.Addr, request)
return
}
func (flashNode *FlashNode) createSetIOLimitsTask(flow, iocc, factor int, opCode uint8) (task *proto.AdminTask) {
request := &proto.FlashNodeSetIOLimitsRequest{
Flow: flow,
Iocc: iocc,
Factor: factor,
}
task = proto.NewAdminTask(opCode, flashNode.Addr, request)
return
}
func (flashNode *FlashNode) createFnScanTask(masterAddr string, manualTask *proto.FlashManualTask) (task *proto.AdminTask) {
request := &proto.FlashNodeManualTaskRequest{
MasterAddr: masterAddr,
FnNodeAddr: flashNode.Addr,
Task: manualTask,
}
task = proto.NewAdminTaskEx(proto.OpFlashNodeScan, flashNode.Addr, request, manualTask.Id)
return
}
func (m *Server) addFlashNode(w http.ResponseWriter, r *http.Request) {
var (
nodeAddr common.String
zoneName common.String
version common.String
id uint64
err error
)
metric := exporter.NewTPCnt(apiToMetricsName(proto.FlashNodeAdd))
defer func() {
doStatAndMetric(proto.FlashNodeAdd, metric, 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.V, zoneName.V, version.V); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
sendOkReply(w, r, newSuccessHTTPReply(id))
}
func (c *Cluster) addFlashNode(nodeAddr, zoneName, version string) (id uint64, err error) {
c.flashNodeTopo.mu.Lock()
defer func() {
c.flashNodeTopo.mu.Unlock()
if err != nil {
log.LogErrorf("action[addFlashNode],clusterID[%v] Addr:%v err:%v ", c.Name, nodeAddr, err.Error())
}
}()
var flashNode *FlashNode
flashNode, err = c.peekFlashNode(nodeAddr)
if err == nil {
return flashNode.ID, nil
}
flashNode = newFlashNode(nodeAddr, zoneName, c.Name, version, true)
_, err = c.flashNodeTopo.getZone(zoneName)
if err != nil {
c.flashNodeTopo.putZoneIfAbsent(newFlashNodeZone(zoneName))
}
if id, err = c.idAlloc.allocateCommonID(); err != nil {
return
}
flashNode.ID = id
if err = c.syncAddFlashNode(flashNode); err != nil {
return
}
flashNode.ReportTime = time.Now()
flashNode.IsActive = true
if err = c.flashNodeTopo.putFlashNode(flashNode); err != nil {
return
}
log.LogInfof("action[addFlashNode],clusterID[%v] Addr:%v ZoneName:%v success", c.Name, nodeAddr, zoneName)
return
}
func (m *Server) listFlashNodes(w http.ResponseWriter, r *http.Request) {
metric := exporter.NewTPCnt(apiToMetricsName(proto.FlashNodeList))
defer func() {
doStatAndMetric(proto.FlashNodeList, metric, nil, nil)
}()
zoneFlashNodes := make(map[string][]*proto.FlashNodeViewInfo)
showAll := true
active := false
if err := r.ParseForm(); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if _, exists := r.Form["active"]; exists {
showAll = false
activeReq, _ := strconv.ParseInt(r.FormValue("active"), 10, 64)
if activeReq == -1 {
showAll = true
} else if activeReq == 1 {
active = true
}
}
m.cluster.flashNodeTopo.flashNodeMap.Range(func(key, value interface{}) bool {
flashNode := value.(*FlashNode)
if showAll || flashNode.isActiveAndEnable() == active {
zoneFlashNodes[flashNode.ZoneName] = append(zoneFlashNodes[flashNode.ZoneName], flashNode.getFlashNodeViewInfo())
}
return true
})
sendOkReply(w, r, newSuccessHTTPReply(zoneFlashNodes))
}
func (m *Server) getFlashNode(w http.ResponseWriter, r *http.Request) {
var err error
metric := exporter.NewTPCnt(apiToMetricsName(proto.FlashNodeGet))
defer func() {
doStatAndMetric(proto.FlashNodeGet, metric, 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.V); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
sendOkReply(w, r, newSuccessHTTPReply(flashNode.getFlashNodeViewInfo()))
}
func (m *Server) removeFlashNode(w http.ResponseWriter, r *http.Request) {
var err error
metric := exporter.NewTPCnt(apiToMetricsName(proto.FlashNodeRemove))
defer func() {
doStatAndMetric(proto.FlashNodeRemove, metric, 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.V); err != nil {
sendErrReply(w, r, newErrHTTPReply(proto.ErrDataNodeNotExists))
return
}
if node.FlashGroupID != unusedFlashNodeFlashGroupID {
sendErrReply(w, r, newErrHTTPReply(fmt.Errorf("to delete a flashnode, it needs to be removed from the flashgroup first")))
return
}
if err = m.cluster.removeFlashNode(node); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
sendOkReply(w, r, newSuccessHTTPReply(fmt.Sprintf("delete flash node [%v] successfully", offLineAddr)))
}
func (m *Server) removeAllInactiveFlashNodes(w http.ResponseWriter, r *http.Request) {
var (
err error
removeNodes []*FlashNode
)
removeAddresses := []string{}
m.cluster.flashNodeTopo.flashNodeMap.Range(func(key, value interface{}) bool {
flashNode := value.(*FlashNode)
if !flashNode.isActiveAndEnable() && flashNode.FlashGroupID == unusedFlashNodeFlashGroupID {
removeNodes = append(removeNodes, flashNode)
}
return true
})
for _, node := range removeNodes {
if err = m.cluster.removeFlashNode(node); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
removeAddresses = append(removeAddresses, node.Addr)
}
sendOkReply(w, r, newSuccessHTTPReply(removeAddresses))
}
func (c *Cluster) removeFlashNode(flashNode *FlashNode) (err error) {
log.LogWarnf("action[removeFlashNode], ZoneName[%s] Node[%s] offline", flashNode.ZoneName, flashNode.Addr)
var flashGroupID uint64
if flashGroupID, err = c.deleteFlashNode(flashNode); err != nil {
return
}
if flashGroupID != unusedFlashNodeFlashGroupID {
var flashGroup *FlashGroup
if flashGroup, err = c.flashNodeTopo.getFlashGroup(flashGroupID); err != nil {
return
}
flashGroup.removeFlashNode(flashNode.Addr)
c.flashNodeTopo.updateClientCache()
}
go func() {
time.Sleep(time.Duration(defaultWaitClientUpdateFgTimeSec) * time.Second)
arr := strings.SplitN(flashNode.Addr, ":", 2)
p, _ := strconv.ParseUint(arr[1], 10, 64)
addr := fmt.Sprintf("%s:%d", arr[0], p+1)
if err = httpclient.New().Addr(addr).FlashNode().EvictAll(); err != nil {
log.LogErrorf("flashNode[%v] evict all failed, err:%v", flashNode.Addr, err)
return
}
}()
log.LogInfof("action[removeFlashNode], clusterID[%s] node[%s] flashGroupID[%d] offline success",
c.Name, flashNode.Addr, flashGroupID)
return
}
func (c *Cluster) deleteFlashNode(flashNode *FlashNode) (oldFlashGroupID uint64, err error) {
flashNode.Lock()
defer flashNode.Unlock()
oldFlashGroupID = flashNode.FlashGroupID
flashNode.FlashGroupID = unusedFlashNodeFlashGroupID
if err = c.syncDeleteFlashNode(flashNode); err != nil {
log.LogErrorf("action[deleteFlashNode],clusterID[%v] node[%v] offline failed,err[%v]",
c.Name, flashNode.Addr, err)
flashNode.FlashGroupID = oldFlashGroupID
return
}
c.delFlashNodeFromCache(flashNode)
return
}
func (c *Cluster) delFlashNodeFromCache(flashNode *FlashNode) {
c.flashNodeTopo.deleteFlashNode(flashNode)
go flashNode.clean()
}
func (m *Server) setFlashNode(w http.ResponseWriter, r *http.Request) {
var (
nodeAddr common.String
enable bool
workRole string
flashNode *FlashNode
err error
)
metric := exporter.NewTPCnt(apiToMetricsName(proto.FlashNodeSet))
defer func() {
doStatAndMetric(proto.FlashNodeSet, metric, err, nil)
}()
if err = parseArgs(r, argParserNodeAddr(&nodeAddr)); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if flashNode, err = m.cluster.peekFlashNode(nodeAddr.V); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
if _, exists := r.Form["enable"]; exists {
enable, err = strconv.ParseBool(r.FormValue("enable"))
if err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
}
if err = m.cluster.updateFlashNode(flashNode, enable); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
}
if _, exists := r.Form["workRole"]; exists {
workRole = r.FormValue("workRole")
if err = m.cluster.updateFlashNodeWorkRole(flashNode, workRole); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
}
sendOkReply(w, r, newSuccessHTTPReply("set flashNode success"))
}
func (m *Server) createFlashNodeManualTask(w http.ResponseWriter, r *http.Request) {
var (
bytes []byte
err error
)
metric := exporter.NewTPCnt(apiToMetricsName(proto.CreateFlashNodeManualTask))
defer func() {
doStatAndMetric(proto.CreateFlashNodeManualTask, metric, err, nil)
}()
if bytes, err = io.ReadAll(r.Body); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
req := proto.FlashManualTask{}
if err = json.Unmarshal(bytes, &req); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
var vol *Vol
if vol, err = m.cluster.getVol(req.VolName); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeVolNotExists, Msg: err.Error()})
return
}
if !vol.remoteCacheEnable {
err = fmt.Errorf("distribute cache of vol[%v] unavailable", vol.Name)
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeInvalidCfg, Msg: err.Error()})
return
}
if m.cluster.flashNodeTopo == nil || !m.cluster.flashNodeTopo.checkForActiveNode() {
err = fmt.Errorf("no available distributed cache nodes")
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeInvalidCfg, Msg: err.Error()})
return
}
start := time.Now()
req.StartTime = &start
req.UpdateTime = &start
if req.Id == "" {
req.Id = uuid.New().String()
}
m.cluster.flashManMgr.mu.Lock()
if err = checkManualConfig(&req, vol, m.cluster.flashManMgr); err != nil {
m.cluster.flashManMgr.mu.Unlock()
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeInvalidCfg, Msg: err.Error()})
return
}
err = m.cluster.syncAddFlashManualTask(&req)
if err != nil {
m.cluster.flashManMgr.mu.Unlock()
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeInternalError, Msg: err.Error()})
return
}
m.cluster.flashManMgr.flashManualTasks.Store(req.Id, &req)
m.cluster.flashManMgr.mu.Unlock()
log.LogInfof("action[setFlashNodeManualTask],clusterID[%v] vol:%v", m.cluster.Name, req.VolName)
sendOkReply(w, r, newSuccessHTTPReply("set flashNode manual task success"))
}
func (m *Server) flashManualTask(w http.ResponseWriter, r *http.Request) {
var err error
metric := exporter.NewTPCnt(apiToMetricsName(proto.AdminFlashManualTask))
defer func() {
doStatAndMetric(proto.AdminFlashManualTask, metric, err, nil)
}()
if m.cluster.partition == nil || !m.cluster.partition.IsRaftLeader() {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: "not leader"})
return
}
if err := r.ParseForm(); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
opCode := r.FormValue("op")
tId := r.FormValue("tid")
switch opCode {
case "info":
vol := r.FormValue("vol")
rsp := m.cluster.flashManMgr.findMatchTasks(vol, tId)
sendOkReply(w, r, newSuccessHTTPReply(rsp))
case "set":
limit := r.FormValue("total_limit")
if limit != "" {
if totalLimit, err := strconv.ParseInt(limit, 10, 32); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
} else {
m.cluster.flashManMgr.taskTotalLimit = int(totalLimit)
}
}
sendOkReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeSuccess, Msg: fmt.Sprintf("total limit %v", limit)})
case "stop":
err = m.cluster.flashManMgr.dispatchTaskOp(tId, opCode)
if err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
} else {
sendOkReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeSuccess, Msg: fmt.Sprintf("tid(%v), op(%v), send to flashnode", tId, opCode)})
}
case "pause":
err = m.cluster.flashManMgr.dispatchTaskOp(tId, opCode)
if err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
} else {
manualTask, ok := m.cluster.flashManMgr.LoadManualTaskById(tId)
if ok {
manualTask.Lock()
manualTask.Status = int(proto.Flash_Task_Pause)
if e := m.cluster.syncAddFlashManualTask(manualTask); e != nil {
log.LogWarnf("action[pause] syncAddFlashManualTask %v err(%v)", manualTask, e)
}
manualTask.Unlock()
}
sendOkReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeSuccess, Msg: fmt.Sprintf("tid(%v), op(%v), send to flashnode", tId, opCode)})
}
case "resume":
err = m.cluster.flashManMgr.dispatchTaskOp(tId, opCode)
if err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
} else {
manualTask, ok := m.cluster.flashManMgr.LoadManualTaskById(tId)
if ok {
manualTask.Lock()
manualTask.Status = int(proto.Flash_Task_Running)
if e := m.cluster.syncAddFlashManualTask(manualTask); e != nil {
log.LogWarnf("action[resume] syncAddFlashManualTask %v err(%v)", manualTask, e)
}
manualTask.Unlock()
}
sendOkReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeSuccess, Msg: fmt.Sprintf("tid(%v), op(%v), send to flashnode", tId, opCode)})
}
case "delete":
manualTask, ok := m.cluster.flashManMgr.LoadManualTaskById(tId)
if !ok {
log.LogWarnf("action[delete] %v does not exsit", tId)
} else {
manualTask.Lock()
if proto.ManualTaskIsRunning(manualTask.Status) || manualTask.Status == int(proto.Flash_Task_Pause) {
manualTask.Unlock()
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: fmt.Sprintf("tid(%v), invaild status[%v] can not delete", tId, manualTask.Status)})
return
}
if err = m.cluster.syncDeleteFlashManualTask(manualTask); err != nil {
log.LogWarnf("action[delete] syncDeleteFlashManualTask %v err(%v)", manualTask.Id, err)
}
manualTask.Unlock()
m.cluster.flashManMgr.flashManualTasks.Delete(tId)
}
sendOkReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeSuccess, Msg: fmt.Sprintf("tid(%v), delete success", tId)})
default:
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: "invalid op"})
}
}
func (m *Server) handleFlashNodeTaskResponse(w http.ResponseWriter, r *http.Request) {
var (
tr *proto.AdminTask
err error
)
metric := exporter.NewTPCnt(apiToMetricsName(proto.GetFlashNodeTaskResponse))
defer func() {
doStatAndMetric(proto.GetFlashNodeTaskResponse, metric, err, nil)
}()
tr, err = parseRequestToGetTaskResponse(r)
if err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
sendOkReply(w, r, newSuccessHTTPReply(fmt.Sprintf("%v", http.StatusOK)))
m.cluster.handleFlashNodeTaskResponse(tr.OperatorAddr, tr)
}
func (c *Cluster) handleFlashNodeTaskResponse(nodeAddr string, task *proto.AdminTask) {
if task == nil {
log.LogInfof("flash action[handleFlashNodeTaskResponse] receive addr[%v] task response, but task is nil", nodeAddr)
return
}
log.LogInfof("flash action[handleFlashNodeTaskResponse] receive addr[%v] task: %v", nodeAddr, task.ToString())
var (
err error
flashNode *FlashNode
)
if flashNode, err = c.peekFlashNode(nodeAddr); err != nil {
goto errHandler
}
flashNode.TaskManager.DelTask(task)
if err = unmarshalTaskResponse(task); err != nil {
goto errHandler
}
switch task.OpCode {
case proto.OpFlashNodeScan:
response := task.Response.(*proto.FlashNodeManualTaskResponse)
err = c.handleFlashNodeScanResp(task.OperatorAddr, response)
case proto.OpFlashNodeHeartbeat:
response := task.Response.(*proto.FlashNodeHeartbeatResponse)
err = c.handleFlashNodeHeartbeatResp(task.OperatorAddr, response)
default:
err = fmt.Errorf(fmt.Sprintf("flash unknown operate code %v", task.OpCode))
goto errHandler
}
if err != nil {
goto errHandler
}
return
errHandler:
log.LogWarnf("flash handleFlashNodeTaskResponse failed, task: %v, err: %v", task.ToString(), err)
}
func (c *Cluster) handleFlashNodeHeartbeatResp(nodeAddr string, resp *proto.FlashNodeHeartbeatResponse) (err error) {
if resp.Status != proto.TaskSucceeds {
Warn(c.Name, fmt.Sprintf("action[handleFlashNodeHeartbeatResp] clusterID[%v] flashNode[%v] heartbeat task failed, err[%v]",
c.Name, nodeAddr, resp.Result))
return
}
var node *FlashNode
if node, err = c.peekFlashNode(nodeAddr); err != nil {
log.LogErrorf("action[handleFlashNodeHeartbeatResp], flashNode[%v], heartbeat error: %v", nodeAddr, err.Error())
return
}
node.setActive()
node.updateFlashNodeStatHeartbeat(resp)
c.handleManualTaskProcessing(node, resp)
return
}
func (c *Cluster) updateFlashNode(flashNode *FlashNode, enable bool) (err error) {
flashNode.Lock()
defer flashNode.Unlock()
if flashNode.IsEnable != enable {
oldState := flashNode.IsEnable
flashNode.IsEnable = enable
if err = c.syncUpdateFlashNode(flashNode); err != nil {
flashNode.IsEnable = oldState
return
}
if flashNode.FlashGroupID != unusedFlashNodeFlashGroupID {
c.flashNodeTopo.updateClientCache()
}
}
return
}
func (c *Cluster) updateFlashNodeWorkRole(flashNode *FlashNode, workRole string) error {
flashNode.Lock()
defer flashNode.Unlock()
flashNode.WorkRole = workRole
if err := c.syncUpdateFlashNode(flashNode); err != nil {
return err
}
return nil
}
func (c *Cluster) syncAddFlashNode(flashNode *FlashNode) (err error) {
return c.syncPutFlashNodeInfo(opSyncAddFlashNode, flashNode)
}
func (c *Cluster) syncUpdateFlashNode(flashNode *FlashNode) (err error) {
return c.syncPutFlashNodeInfo(opSyncUpdateFlashNode, flashNode)
}
func (c *Cluster) syncDeleteFlashNode(flashNode *FlashNode) (err error) {
return c.syncPutFlashNodeInfo(opSyncDeleteFlashNode, flashNode)
}
func (c *Cluster) syncPutFlashNodeInfo(opType uint32, flashNode *FlashNode) (err error) {
metadata := new(RaftCmd)
metadata.Op = opType
metadata.K = flashNodePrefix + strconv.FormatUint(flashNode.ID, 10) + keySeparator + flashNode.Addr
metadata.V, err = json.Marshal(flashNode.flashNodeValue)
if err != nil {
return errors.New(err.Error())
}
return c.submit(metadata)
}
func (c *Cluster) syncAddFlashManualTask(flt *proto.FlashManualTask) (err error) {
if flt == nil {
return fmt.Errorf("flashManualTask is nil on syncAddFlashManualTask")
}
return c.syncPutFlashManualTaskInfo(opSyncAddFlashManualTask, flt)
}
func (c *Cluster) syncDeleteFlashManualTask(flt *proto.FlashManualTask) (err error) {
return c.syncPutFlashManualTaskInfo(opSyncDeleteFlashManualTask, flt)
}
func (c *Cluster) syncPutFlashManualTaskInfo(opType uint32, flt *proto.FlashManualTask) (err error) {
metadata := new(RaftCmd)
metadata.Op = opType
metadata.K = flashManualTaskPrefix + flt.Id
metadata.V, err = json.Marshal(flt)
if err != nil {
return errors.New(err.Error())
}
return c.submit(metadata)
}
func (c *Cluster) peekFlashNode(addr string) (flashNode *FlashNode, err error) {
value, ok := c.flashNodeTopo.flashNodeMap.Load(addr)
if !ok {
err = errors.Trace(notFoundMsg(fmt.Sprintf("flashnode[%v]", addr)), "")
return
}
flashNode = value.(*FlashNode)
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())
})
}
func (m *Server) setFlashNodeReadIOLimits(w http.ResponseWriter, r *http.Request) {
var (
flow common.Int
iocc common.Int
factor common.Int
readFlow int64
readIocc int64
readFactor int64
err error
)
if err = parseArgs(r, flow.Flow().OmitEmpty().OnEmpty(func() error {
readFlow = -1
return nil
}).OnValue(func() error {
readFlow = flow.V
return nil
}),
iocc.Iocc().OmitEmpty().OnEmpty(func() error {
readIocc = -1
return nil
}).OnValue(func() error {
readIocc = iocc.V
return nil
}),
factor.Factor().OmitEmpty().OnEmpty(func() error {
readFactor = -1
return nil
}).OnValue(func() error {
readFactor = factor.V
return nil
})); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
log.LogDebugf("action[setFlashNodeReadIOLimits],flow[%v] iocc[%v] factor [%v]",
readFlow, readIocc, readFactor)
tasks := make([]*proto.AdminTask, 0)
m.cluster.flashNodeTopo.flashNodeMap.Range(func(key, value interface{}) bool {
flashNode := value.(*FlashNode)
if flashNode.isActiveAndEnable() {
task := flashNode.createSetIOLimitsTask(int(readFlow), int(readIocc), int(readFactor), proto.OpFlashNodeSetReadIOLimits)
tasks = append(tasks, task)
}
return true
})
go m.cluster.syncFlashNodeSetIOLimitTasks(tasks)
sendOkReply(w, r, newSuccessHTTPReply("set ReadIOLimits for FlashNode is submit,check it later."))
}
func (m *Server) setFlashNodeWriteIOLimits(w http.ResponseWriter, r *http.Request) {
var (
flow common.Int
iocc common.Int
factor common.Int
writeFlow int64
writeIocc int64
writeFactor int64
err error
)
if err = parseArgs(r, flow.Flow().OmitEmpty().OnEmpty(func() error {
writeFlow = -1
return nil
}).OnValue(func() error {
writeFlow = flow.V
return nil
}),
iocc.Iocc().OmitEmpty().OnEmpty(func() error {
writeIocc = -1
return nil
}).OnValue(func() error {
writeIocc = iocc.V
return nil
}),
factor.Factor().OmitEmpty().OnEmpty(func() error {
writeFactor = -1
return nil
}).OnValue(func() error {
writeFactor = factor.V
return nil
})); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
log.LogDebugf("action[setFlashNodeWriteIOLimits],flow[%v] iocc[%v] factor [%v]",
writeFlow, writeIocc, writeFactor)
tasks := make([]*proto.AdminTask, 0)
m.cluster.flashNodeTopo.flashNodeMap.Range(func(key, value interface{}) bool {
flashNode := value.(*FlashNode)
if flashNode.isActiveAndEnable() {
task := flashNode.createSetIOLimitsTask(int(writeFlow), int(writeIocc), int(writeFactor), proto.OpFlashNodeSetWriteIOLimits)
tasks = append(tasks, task)
}
return true
})
go m.cluster.syncFlashNodeSetIOLimitTasks(tasks)
sendOkReply(w, r, newSuccessHTTPReply("set WriteIOLimits for FlashNode is submit,check it later."))
}