cubefs/datanode/server_handler.go
leonrayang 3fdd01cae9 feature: add qos flow limit control process for 2nd cache layer
1) update master inteface of datanode enable qos
2) master clean client info if long time no report
3) enable qos online shutdown and startup
4) master set client alloc preriod and count of girds hit trigger condition
5) datanode qos limit value set by master and add disable conf and http interface
6) client qos init buffer calculate according on real buffer storage
7) create limiter for both hot and cold volume

Signed-off-by: leonrayang <chl696@sina.com>
2022-08-10 22:05:45 +08:00

417 lines
12 KiB
Go

// Copyright 2018 The Chubao 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 datanode
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"sync/atomic"
"github.com/cubefs/cubefs/depends/tiglabs/raft"
"github.com/cubefs/cubefs/proto"
"github.com/cubefs/cubefs/storage"
)
var (
AutoRepairStatus = true
)
func (s *DataNode) getDiskAPI(w http.ResponseWriter, r *http.Request) {
disks := make([]interface{}, 0)
for _, diskItem := range s.space.GetDisks() {
disk := &struct {
Path string `json:"path"`
Total uint64 `json:"total"`
Used uint64 `json:"used"`
Available uint64 `json:"available"`
Unallocated uint64 `json:"unallocated"`
Allocated uint64 `json:"allocated"`
Status int `json:"status"`
RestSize uint64 `json:"restSize"`
DiskRdoSize uint64 `json:"diskRdoSize"`
Partitions int `json:"partitions"`
}{
Path: diskItem.Path,
Total: diskItem.Total,
Used: diskItem.Used,
Available: diskItem.Available,
Unallocated: diskItem.Unallocated,
Allocated: diskItem.Allocated,
Status: diskItem.Status,
RestSize: diskItem.ReservedSpace,
DiskRdoSize: diskItem.DiskRdonlySpace,
Partitions: diskItem.PartitionCount(),
}
disks = append(disks, disk)
}
diskReport := &struct {
Disks []interface{} `json:"disks"`
Zone string `json:"zone"`
}{
Disks: disks,
Zone: s.zoneName,
}
s.buildSuccessResp(w, diskReport)
}
func (s *DataNode) getStatAPI(w http.ResponseWriter, r *http.Request) {
response := &proto.DataNodeHeartbeatResponse{}
s.buildHeartBeatResponse(response)
s.buildSuccessResp(w, response)
}
func (s *DataNode) setAutoRepairStatus(w http.ResponseWriter, r *http.Request) {
const (
paramAutoRepair = "autoRepair"
)
if err := r.ParseForm(); err != nil {
err = fmt.Errorf("parse form fail: %v", err)
s.buildFailureResp(w, http.StatusBadRequest, err.Error())
return
}
autoRepair, err := strconv.ParseBool(r.FormValue(paramAutoRepair))
if err != nil {
err = fmt.Errorf("parse param %v fail: %v", paramAutoRepair, err)
s.buildFailureResp(w, http.StatusBadRequest, err.Error())
return
}
AutoRepairStatus = autoRepair
s.buildSuccessResp(w, autoRepair)
}
func (s *DataNode) getRaftStatus(w http.ResponseWriter, r *http.Request) {
const (
paramRaftID = "raftID"
)
if err := r.ParseForm(); err != nil {
err = fmt.Errorf("parse form fail: %v", err)
s.buildFailureResp(w, http.StatusBadRequest, err.Error())
return
}
raftID, err := strconv.ParseUint(r.FormValue(paramRaftID), 10, 64)
if err != nil {
err = fmt.Errorf("parse param %v fail: %v", paramRaftID, err)
s.buildFailureResp(w, http.StatusBadRequest, err.Error())
return
}
raftStatus := s.raftStore.RaftStatus(raftID)
s.buildSuccessResp(w, raftStatus)
}
func (s *DataNode) getPartitionsAPI(w http.ResponseWriter, r *http.Request) {
partitions := make([]interface{}, 0)
s.space.RangePartitions(func(dp *DataPartition) bool {
partition := &struct {
ID uint64 `json:"id"`
Size int `json:"size"`
Used int `json:"used"`
Status int `json:"status"`
Path string `json:"path"`
Replicas []string `json:"replicas"`
}{
ID: dp.partitionID,
Size: dp.Size(),
Used: dp.Used(),
Status: dp.Status(),
Path: dp.Path(),
Replicas: dp.Replicas(),
}
partitions = append(partitions, partition)
return true
})
result := &struct {
Partitions []interface{} `json:"partitions"`
PartitionCount int `json:"partitionCount"`
}{
Partitions: partitions,
PartitionCount: len(partitions),
}
s.buildSuccessResp(w, result)
}
func (s *DataNode) getPartitionAPI(w http.ResponseWriter, r *http.Request) {
const (
paramPartitionID = "id"
)
var (
partitionID uint64
files []*storage.ExtentInfo
err error
tinyDeleteRecordSize int64
)
if err = r.ParseForm(); err != nil {
err = fmt.Errorf("parse form fail: %v", err)
s.buildFailureResp(w, http.StatusBadRequest, err.Error())
return
}
if partitionID, err = strconv.ParseUint(r.FormValue(paramPartitionID), 10, 64); err != nil {
err = fmt.Errorf("parse param %v fail: %v", paramPartitionID, err)
s.buildFailureResp(w, http.StatusBadRequest, err.Error())
return
}
partition := s.space.Partition(partitionID)
if partition == nil {
s.buildFailureResp(w, http.StatusNotFound, "partition not exist")
return
}
if files, tinyDeleteRecordSize, err = partition.ExtentStore().GetAllWatermarks(nil); err != nil {
err = fmt.Errorf("get watermark fail: %v", err)
s.buildFailureResp(w, http.StatusInternalServerError, err.Error())
return
}
result := &struct {
VolName string `json:"volName"`
ID uint64 `json:"id"`
Size int `json:"size"`
Used int `json:"used"`
Status int `json:"status"`
Path string `json:"path"`
Files []*storage.ExtentInfo `json:"extents"`
FileCount int `json:"fileCount"`
Replicas []string `json:"replicas"`
TinyDeleteRecordSize int64 `json:"tinyDeleteRecordSize"`
RaftStatus *raft.Status `json:"raftStatus"`
}{
VolName: partition.volumeID,
ID: partition.partitionID,
Size: partition.Size(),
Used: partition.Used(),
Status: partition.Status(),
Path: partition.Path(),
Files: files,
FileCount: len(files),
Replicas: partition.Replicas(),
TinyDeleteRecordSize: tinyDeleteRecordSize,
}
if partition.isNormalType() {
result.RaftStatus = partition.raftPartition.Status()
}
s.buildSuccessResp(w, result)
}
func (s *DataNode) getExtentAPI(w http.ResponseWriter, r *http.Request) {
var (
partitionID uint64
extentID int
err error
extentInfo *storage.ExtentInfo
)
if err = r.ParseForm(); err != nil {
s.buildFailureResp(w, http.StatusBadRequest, err.Error())
return
}
if partitionID, err = strconv.ParseUint(r.FormValue("partitionID"), 10, 64); err != nil {
s.buildFailureResp(w, http.StatusBadRequest, err.Error())
return
}
if extentID, err = strconv.Atoi(r.FormValue("extentID")); err != nil {
s.buildFailureResp(w, http.StatusBadRequest, err.Error())
return
}
partition := s.space.Partition(partitionID)
if partition == nil {
s.buildFailureResp(w, http.StatusNotFound, "partition not exist")
return
}
if extentInfo, err = partition.ExtentStore().Watermark(uint64(extentID)); err != nil {
s.buildFailureResp(w, 500, err.Error())
return
}
s.buildSuccessResp(w, extentInfo)
return
}
func (s *DataNode) getBlockCrcAPI(w http.ResponseWriter, r *http.Request) {
var (
partitionID uint64
extentID int
err error
blocks []*storage.BlockCrc
)
if err = r.ParseForm(); err != nil {
s.buildFailureResp(w, http.StatusBadRequest, err.Error())
return
}
if partitionID, err = strconv.ParseUint(r.FormValue("partitionID"), 10, 64); err != nil {
s.buildFailureResp(w, http.StatusBadRequest, err.Error())
return
}
if extentID, err = strconv.Atoi(r.FormValue("extentID")); err != nil {
s.buildFailureResp(w, http.StatusBadRequest, err.Error())
return
}
partition := s.space.Partition(partitionID)
if partition == nil {
s.buildFailureResp(w, http.StatusNotFound, "partition not exist")
return
}
if blocks, err = partition.ExtentStore().ScanBlocks(uint64(extentID)); err != nil {
s.buildFailureResp(w, 500, err.Error())
return
}
s.buildSuccessResp(w, blocks)
return
}
func (s *DataNode) getTinyDeleted(w http.ResponseWriter, r *http.Request) {
var (
partitionID uint64
err error
extentInfo []storage.ExtentDeleted
)
if err = r.ParseForm(); err != nil {
s.buildFailureResp(w, http.StatusBadRequest, err.Error())
return
}
if partitionID, err = strconv.ParseUint(r.FormValue("id"), 10, 64); err != nil {
s.buildFailureResp(w, http.StatusBadRequest, err.Error())
return
}
partition := s.space.Partition(partitionID)
if partition == nil {
s.buildFailureResp(w, http.StatusNotFound, "partition not exist")
return
}
if extentInfo, err = partition.ExtentStore().GetHasDeleteTinyRecords(); err != nil {
s.buildFailureResp(w, 500, err.Error())
return
}
s.buildSuccessResp(w, extentInfo)
return
}
func (s *DataNode) getNormalDeleted(w http.ResponseWriter, r *http.Request) {
var (
partitionID uint64
err error
extentInfo []storage.ExtentDeleted
)
if err = r.ParseForm(); err != nil {
s.buildFailureResp(w, http.StatusBadRequest, err.Error())
return
}
if partitionID, err = strconv.ParseUint(r.FormValue("id"), 10, 64); err != nil {
s.buildFailureResp(w, http.StatusBadRequest, err.Error())
return
}
partition := s.space.Partition(partitionID)
if partition == nil {
s.buildFailureResp(w, http.StatusNotFound, "partition not exist")
return
}
if extentInfo, err = partition.ExtentStore().GetHasDeleteExtent(); err != nil {
s.buildFailureResp(w, 500, err.Error())
return
}
s.buildSuccessResp(w, extentInfo)
return
}
func (s *DataNode) setQosEnable() func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
var (
err error
enable bool
)
if err = r.ParseForm(); err != nil {
s.buildFailureResp(w, http.StatusBadRequest, err.Error())
return
}
if enable, err = strconv.ParseBool(r.FormValue("enable")); err != nil {
s.buildFailureResp(w, http.StatusBadRequest, err.Error())
return
}
s.diskQosEnable = enable
s.buildSuccessResp(w, "success")
}
}
func (s *DataNode) getSmuxPoolStat() func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
if !s.enableSmuxConnPool {
s.buildFailureResp(w, 500, "smux pool not supported")
return
}
if s.smuxConnPool == nil {
s.buildFailureResp(w, 500, "smux pool now is nil")
return
}
stat := s.smuxConnPool.GetStat()
s.buildSuccessResp(w, stat)
return
}
}
func (s *DataNode) setMetricsDegrade(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
w.Write([]byte(err.Error()))
return
}
if level := r.FormValue("level"); level != "" {
val, err := strconv.Atoi(level)
if err != nil {
w.Write([]byte("Set metrics degrade level failed\n"))
} else {
atomic.StoreInt64(&s.metricsDegrade, int64(val))
w.Write([]byte(fmt.Sprintf("Set metrics degrade level to %v successfully\n", val)))
}
}
}
func (s *DataNode) getMetricsDegrade(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(fmt.Sprintf("%v\n", atomic.LoadInt64(&s.metricsDegrade))))
}
func (s *DataNode) buildSuccessResp(w http.ResponseWriter, data interface{}) {
s.buildJSONResp(w, http.StatusOK, data, "")
}
func (s *DataNode) buildFailureResp(w http.ResponseWriter, code int, msg string) {
s.buildJSONResp(w, code, nil, msg)
}
// Create response for the API request.
func (s *DataNode) buildJSONResp(w http.ResponseWriter, code int, data interface{}, msg string) {
var (
jsonBody []byte
err error
)
w.WriteHeader(code)
w.Header().Set("Content-Type", "application/json")
body := struct {
Code int `json:"code"`
Data interface{} `json:"data"`
Msg string `json:"msg"`
}{
Code: code,
Data: data,
Msg: msg,
}
if jsonBody, err = json.Marshal(body); err != nil {
return
}
w.Write(jsonBody)
}