cubefs/master/api_service.go
leonrayang 2970e2c3d1 update. go import path change from chubaofs to cubefs
Signed-off-by: leonrayang <chl696@sina.com>
2022-01-25 18:17:02 +08:00

3067 lines
90 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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 master
import (
"encoding/json"
"fmt"
"net/http"
"regexp"
"strconv"
"sync/atomic"
"time"
"bytes"
"io/ioutil"
"strings"
"github.com/cubefs/cubefs/proto"
"github.com/cubefs/cubefs/util"
"github.com/cubefs/cubefs/util/cryptoutil"
"github.com/cubefs/cubefs/util/errors"
"github.com/cubefs/cubefs/util/log"
)
// NodeView provides the view of the data or meta node.
type NodeView struct {
Addr string
Status bool
ID uint64
IsWritable bool
}
// NodeView provides the view of the data or meta node.
type InvalidNodeView struct {
Addr string
ID uint64
OldID uint64
NodeType string
}
// TopologyView provides the view of the topology view of the cluster
type TopologyView struct {
Zones []*ZoneView
}
type NodeSetView struct {
DataNodeLen int
MetaNodeLen int
MetaNodes []proto.NodeView
DataNodes []proto.NodeView
}
func newNodeSetView(dataNodeLen, metaNodeLen int) *NodeSetView {
return &NodeSetView{DataNodes: make([]proto.NodeView, 0), MetaNodes: make([]proto.NodeView, 0), DataNodeLen: dataNodeLen, MetaNodeLen: metaNodeLen}
}
//ZoneView define the view of zone
type ZoneView struct {
Name string
Status string
NodeSet map[uint64]*NodeSetView
}
func newZoneView(name string) *ZoneView {
return &ZoneView{NodeSet: make(map[uint64]*NodeSetView, 0), Name: name}
}
type badPartitionView = proto.BadPartitionView
// Set the threshold of the memory usage on each meta node.
// If the memory usage reaches this threshold, then all the mata partition will be marked as readOnly.
func (m *Server) setMetaNodeThreshold(w http.ResponseWriter, r *http.Request) {
var (
threshold float64
err error
)
if threshold, err = parseAndExtractThreshold(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if err = m.cluster.setMetaNodeThreshold(float32(threshold)); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
sendOkReply(w, r, newSuccessHTTPReply(fmt.Sprintf("set threshold to %v successfully", threshold)))
}
// Turn on or off the automatic allocation of the data partitions.
// If DisableAutoAllocate == off, then we WILL NOT automatically allocate new data partitions for the volume when:
// 1. the used space is below the max capacity,
// 2. and the number of r&w data partition is less than 20.
//
// If DisableAutoAllocate == on, then we WILL automatically allocate new data partitions for the volume when:
// 1. the used space is below the max capacity,
// 2. and the number of r&w data partition is less than 20.
func (m *Server) setupAutoAllocation(w http.ResponseWriter, r *http.Request) {
var (
status bool
err error
)
if status, err = parseAndExtractStatus(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if err = m.cluster.setDisableAutoAllocate(status); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
sendOkReply(w, r, newSuccessHTTPReply(fmt.Sprintf("set DisableAutoAllocate to %v successfully", status)))
}
// View the topology of the cluster.
func (m *Server) getTopology(w http.ResponseWriter, r *http.Request) {
tv := &TopologyView{
Zones: make([]*ZoneView, 0),
}
zones := m.cluster.t.getAllZones()
for _, zone := range zones {
cv := newZoneView(zone.name)
cv.Status = zone.getStatusToString()
tv.Zones = append(tv.Zones, cv)
nsc := zone.getAllNodeSet()
for _, ns := range nsc {
nsView := newNodeSetView(ns.dataNodeLen(), ns.metaNodeLen())
cv.NodeSet[ns.ID] = nsView
ns.dataNodes.Range(func(key, value interface{}) bool {
dataNode := value.(*DataNode)
nsView.DataNodes = append(nsView.DataNodes, proto.NodeView{ID: dataNode.ID, Addr: dataNode.Addr, Status: dataNode.isActive, IsWritable: dataNode.isWriteAble()})
return true
})
ns.metaNodes.Range(func(key, value interface{}) bool {
metaNode := value.(*MetaNode)
nsView.MetaNodes = append(nsView.MetaNodes, proto.NodeView{ID: metaNode.ID, Addr: metaNode.Addr, Status: metaNode.IsActive, IsWritable: metaNode.isWritable()})
return true
})
}
}
sendOkReply(w, r, newSuccessHTTPReply(tv))
}
func (m *Server) updateZone(w http.ResponseWriter, r *http.Request) {
var (
name string
err error
)
if name = r.FormValue(nameKey); name == "" {
err = keyNotFound(nameKey)
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
status, err := extractStatus(r)
if err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
zone, err := m.cluster.t.getZone(name)
if err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeZoneNotExists, Msg: err.Error()})
return
}
if status {
zone.setStatus(normalZone)
} else {
zone.setStatus(unavailableZone)
}
sendOkReply(w, r, newSuccessHTTPReply(fmt.Sprintf("update zone status to [%v] successfully", status)))
}
func (m *Server) listZone(w http.ResponseWriter, r *http.Request) {
zones := m.cluster.t.getAllZones()
zoneViews := make([]*ZoneView, 0)
for _, zone := range zones {
cv := newZoneView(zone.name)
cv.Status = zone.getStatusToString()
zoneViews = append(zoneViews, cv)
}
sendOkReply(w, r, newSuccessHTTPReply(zoneViews))
}
func (m *Server) clusterStat(w http.ResponseWriter, r *http.Request) {
cs := &proto.ClusterStatInfo{
DataNodeStatInfo: m.cluster.dataNodeStatInfo,
MetaNodeStatInfo: m.cluster.metaNodeStatInfo,
ZoneStatInfo: make(map[string]*proto.ZoneStat, 0),
}
for zoneName, zoneStat := range m.cluster.zoneStatInfos {
cs.ZoneStatInfo[zoneName] = zoneStat
}
sendOkReply(w, r, newSuccessHTTPReply(cs))
}
func (m *Server) getCluster(w http.ResponseWriter, r *http.Request) {
cv := &proto.ClusterView{
Name: m.cluster.Name,
LeaderAddr: m.leaderInfo.addr,
DisableAutoAlloc: m.cluster.DisableAutoAllocate,
MetaNodeThreshold: m.cluster.cfg.MetaNodeThreshold,
Applied: m.fsm.applied,
MaxDataPartitionID: m.cluster.idAlloc.dataPartitionID,
MaxMetaNodeID: m.cluster.idAlloc.commonID,
MaxMetaPartitionID: m.cluster.idAlloc.metaPartitionID,
MetaNodes: make([]proto.NodeView, 0),
DataNodes: make([]proto.NodeView, 0),
VolStatInfo: make([]*proto.VolStatInfo, 0),
BadPartitionIDs: make([]proto.BadPartitionView, 0),
BadMetaPartitionIDs: make([]proto.BadPartitionView, 0),
}
vols := m.cluster.allVolNames()
cv.MetaNodes = m.cluster.allMetaNodes()
cv.DataNodes = m.cluster.allDataNodes()
cv.DataNodeStatInfo = m.cluster.dataNodeStatInfo
cv.MetaNodeStatInfo = m.cluster.metaNodeStatInfo
for _, name := range vols {
stat, ok := m.cluster.volStatInfo.Load(name)
if !ok {
cv.VolStatInfo = append(cv.VolStatInfo, newVolStatInfo(name, 0, 0, "0.0001"))
continue
}
cv.VolStatInfo = append(cv.VolStatInfo, stat.(*volStatInfo))
}
cv.BadPartitionIDs = m.cluster.getBadDataPartitionsView()
cv.BadMetaPartitionIDs = m.cluster.getBadMetaPartitionsView()
sendOkReply(w, r, newSuccessHTTPReply(cv))
}
func (m *Server) getIPAddr(w http.ResponseWriter, r *http.Request) {
m.cluster.loadClusterValue()
batchCount := atomic.LoadUint64(&m.cluster.cfg.MetaNodeDeleteBatchCount)
limitRate := atomic.LoadUint64(&m.cluster.cfg.DataNodeDeleteLimitRate)
deleteSleepMs := atomic.LoadUint64(&m.cluster.cfg.MetaNodeDeleteWorkerSleepMs)
autoRepairRate := atomic.LoadUint64(&m.cluster.cfg.DataNodeAutoRepairLimitRate)
cInfo := &proto.ClusterInfo{
Cluster: m.cluster.Name,
MetaNodeDeleteBatchCount: batchCount,
MetaNodeDeleteWorkerSleepMs: deleteSleepMs,
DataNodeDeleteLimitRate: limitRate,
DataNodeAutoRepairLimitRate: autoRepairRate,
Ip: strings.Split(r.RemoteAddr, ":")[0],
}
sendOkReply(w, r, newSuccessHTTPReply(cInfo))
}
func (m *Server) createMetaPartition(w http.ResponseWriter, r *http.Request) {
var (
volName string
start uint64
err error
)
if volName, start, err = validateRequestToCreateMetaPartition(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if err = m.cluster.updateInodeIDRange(volName, start); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
sendOkReply(w, r, newSuccessHTTPReply(fmt.Sprint("create meta partition successfully")))
}
func (m *Server) createDataPartition(w http.ResponseWriter, r *http.Request) {
var (
rstMsg string
volName string
vol *Vol
reqCreateCount int
lastTotalDataPartitions int
clusterTotalDataPartitions int
err error
)
if reqCreateCount, volName, err = parseRequestToCreateDataPartition(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if vol, err = m.cluster.getVol(volName); err != nil {
sendErrReply(w, r, newErrHTTPReply(proto.ErrVolNotExists))
return
}
lastTotalDataPartitions = len(vol.dataPartitions.partitions)
clusterTotalDataPartitions = m.cluster.getDataPartitionCount()
err = m.cluster.batchCreateDataPartition(vol, reqCreateCount)
rstMsg = fmt.Sprintf(" createDataPartition succeeeds. "+
"clusterLastTotalDataPartitions[%v],vol[%v] has %v data partitions previously and %v data partitions now",
clusterTotalDataPartitions, volName, lastTotalDataPartitions, len(vol.dataPartitions.partitions))
if err != nil {
log.LogErrorf("create data partition fail: volume(%v) err(%v)", volName, err)
sendErrReply(w, r, newErrHTTPReply(err))
return
}
_ = sendOkReply(w, r, newSuccessHTTPReply(rstMsg))
}
func (m *Server) getDataPartition(w http.ResponseWriter, r *http.Request) {
var (
dp *DataPartition
partitionID uint64
volName string
vol *Vol
err error
)
if partitionID, volName, err = parseRequestToGetDataPartition(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if volName != "" {
if vol, err = m.cluster.getVol(volName); err != nil {
sendErrReply(w, r, newErrHTTPReply(proto.ErrDataPartitionNotExists))
return
}
if dp, err = vol.getDataPartitionByID(partitionID); err != nil {
sendErrReply(w, r, newErrHTTPReply(proto.ErrDataPartitionNotExists))
return
}
} else {
if dp, err = m.cluster.getDataPartitionByID(partitionID); err != nil {
sendErrReply(w, r, newErrHTTPReply(proto.ErrDataPartitionNotExists))
return
}
}
sendOkReply(w, r, newSuccessHTTPReply(dp.ToProto(m.cluster)))
}
// Load the data partition.
func (m *Server) loadDataPartition(w http.ResponseWriter, r *http.Request) {
var (
msg string
dp *DataPartition
partitionID uint64
err error
)
if partitionID, err = parseRequestToLoadDataPartition(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if dp, err = m.cluster.getDataPartitionByID(partitionID); err != nil {
sendErrReply(w, r, newErrHTTPReply(proto.ErrDataPartitionNotExists))
return
}
m.cluster.loadDataPartition(dp)
msg = fmt.Sprintf(proto.AdminLoadDataPartition+"partitionID :%v load data partition successfully", partitionID)
sendOkReply(w, r, newSuccessHTTPReply(msg))
}
func (m *Server) addDataReplica(w http.ResponseWriter, r *http.Request) {
var (
msg string
addr string
dp *DataPartition
partitionID uint64
err error
)
if partitionID, addr, err = parseRequestToAddDataReplica(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if dp, err = m.cluster.getDataPartitionByID(partitionID); err != nil {
sendErrReply(w, r, newErrHTTPReply(proto.ErrDataPartitionNotExists))
return
}
if err = m.cluster.addDataReplica(dp, addr); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
dp.Status = proto.ReadOnly
dp.isRecover = true
m.cluster.putBadDataPartitionIDs(nil, addr, dp.PartitionID)
msg = fmt.Sprintf("data partitionID :%v add replica [%v] successfully", partitionID, addr)
sendOkReply(w, r, newSuccessHTTPReply(msg))
}
func (m *Server) deleteDataReplica(w http.ResponseWriter, r *http.Request) {
var (
msg string
addr string
dp *DataPartition
partitionID uint64
err error
)
if partitionID, addr, err = parseRequestToRemoveDataReplica(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if dp, err = m.cluster.getDataPartitionByID(partitionID); err != nil {
sendErrReply(w, r, newErrHTTPReply(proto.ErrDataPartitionNotExists))
return
}
if err = m.cluster.removeDataReplica(dp, addr, true); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
msg = fmt.Sprintf("data partitionID :%v delete replica [%v] successfully", partitionID, addr)
sendOkReply(w, r, newSuccessHTTPReply(msg))
}
func (m *Server) addMetaReplica(w http.ResponseWriter, r *http.Request) {
var (
msg string
addr string
mp *MetaPartition
partitionID uint64
err error
)
if partitionID, addr, err = parseRequestToAddMetaReplica(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if mp, err = m.cluster.getMetaPartitionByID(partitionID); err != nil {
sendErrReply(w, r, newErrHTTPReply(proto.ErrMetaPartitionNotExists))
return
}
if err = m.cluster.addMetaReplica(mp, addr); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
mp.IsRecover = true
m.cluster.putBadMetaPartitions(addr, mp.PartitionID)
msg = fmt.Sprintf("meta partitionID :%v add replica [%v] successfully", partitionID, addr)
sendOkReply(w, r, newSuccessHTTPReply(msg))
}
func (m *Server) deleteMetaReplica(w http.ResponseWriter, r *http.Request) {
var (
msg string
addr string
mp *MetaPartition
partitionID uint64
err error
force bool
)
if partitionID, addr, err = parseRequestToRemoveMetaReplica(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if mp, err = m.cluster.getMetaPartitionByID(partitionID); err != nil {
sendErrReply(w, r, newErrHTTPReply(proto.ErrMetaPartitionNotExists))
return
}
var value string
if value = r.FormValue(forceKey); value != "" {
force, _ = strconv.ParseBool(value)
}
if err = m.cluster.deleteMetaReplica(mp, addr, true, force); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
msg = fmt.Sprintf("meta partitionID :%v delete replica [%v] successfully", partitionID, addr)
sendOkReply(w, r, newSuccessHTTPReply(msg))
}
// Decommission a data partition. This usually happens when disk error has been reported.
// This function needs to be called manually by the admin.
func (m *Server) decommissionDataPartition(w http.ResponseWriter, r *http.Request) {
var (
rstMsg string
dp *DataPartition
addr string
partitionID uint64
err error
)
if partitionID, addr, err = parseRequestToDecommissionDataPartition(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if dp, err = m.cluster.getDataPartitionByID(partitionID); err != nil {
sendErrReply(w, r, newErrHTTPReply(proto.ErrDataPartitionNotExists))
return
}
if err = m.cluster.decommissionDataPartition(addr, dp, handleDataPartitionOfflineErr); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
rstMsg = fmt.Sprintf(proto.AdminDecommissionDataPartition+" dataPartitionID :%v on node:%v successfully", partitionID, addr)
sendOkReply(w, r, newSuccessHTTPReply(rstMsg))
}
func (m *Server) diagnoseDataPartition(w http.ResponseWriter, r *http.Request) {
var (
err error
rstMsg *proto.DataPartitionDiagnosis
inactiveNodes []string
corruptDps []*DataPartition
lackReplicaDps []*DataPartition
corruptDpIDs []uint64
lackReplicaDpIDs []uint64
badDataPartitions []badPartitionView
)
corruptDpIDs = make([]uint64, 0)
lackReplicaDpIDs = make([]uint64, 0)
if inactiveNodes, corruptDps, err = m.cluster.checkCorruptDataPartitions(); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
if lackReplicaDps, err = m.cluster.checkLackReplicaDataPartitions(); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
for _, dp := range corruptDps {
corruptDpIDs = append(corruptDpIDs, dp.PartitionID)
}
for _, dp := range lackReplicaDps {
lackReplicaDpIDs = append(lackReplicaDpIDs, dp.PartitionID)
}
badDataPartitions = m.cluster.getBadDataPartitionsView()
rstMsg = &proto.DataPartitionDiagnosis{
InactiveDataNodes: inactiveNodes,
CorruptDataPartitionIDs: corruptDpIDs,
LackReplicaDataPartitionIDs: lackReplicaDpIDs,
BadDataPartitionIDs: badDataPartitions,
}
log.LogInfof("diagnose dataPartition[%v] inactiveNodes:[%v], corruptDpIDs:[%v], lackReplicaDpIDs:[%v]", m.cluster.Name, inactiveNodes, corruptDpIDs, lackReplicaDpIDs)
sendOkReply(w, r, newSuccessHTTPReply(rstMsg))
}
// Mark the volume as deleted, which will then be deleted later.
func (m *Server) markDeleteVol(w http.ResponseWriter, r *http.Request) {
var (
name string
authKey string
err error
msg string
)
if name, authKey, err = parseRequestToDeleteVol(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if err = m.cluster.markDeleteVol(name, authKey); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
if err = m.user.deleteVolPolicy(name); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
msg = fmt.Sprintf("delete vol[%v] successfully,from[%v]", name, r.RemoteAddr)
log.LogWarn(msg)
sendOkReply(w, r, newSuccessHTTPReply(msg))
}
func (m *Server) updateVol(w http.ResponseWriter, r *http.Request) {
var (
name string
authKey string
err error
msg string
capacity uint64
replicaNum int
followerRead bool
authenticate bool
zoneName string
description string
dpSelectorName string
dpSelectorParm string
vol *Vol
)
if name, authKey, description, err = parseRequestToUpdateVol(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if vol, err = m.cluster.getVol(name); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeVolNotExists, Msg: err.Error()})
return
}
if zoneName, capacity, replicaNum, dpSelectorName, dpSelectorParm, err =
parseDefaultInfoToUpdateVol(r, vol); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if replicaNum != 0 && !(replicaNum == 2 || replicaNum == 3) {
err = fmt.Errorf("replicaNum can only be 2 and 3,received replicaNum is[%v]", replicaNum)
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if followerRead, authenticate, err = parseBoolFieldToUpdateVol(r, vol); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
newArgs := getVolVarargs(vol)
newArgs.zoneName = zoneName
newArgs.description = description
newArgs.capacity = capacity
newArgs.followerRead = followerRead
newArgs.authenticate = authenticate
newArgs.dpSelectorName = dpSelectorName
newArgs.dpSelectorParm = dpSelectorParm
if err = m.cluster.updateVol(name, authKey, newArgs); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
msg = fmt.Sprintf("update vol[%v] successfully\n", name)
sendOkReply(w, r, newSuccessHTTPReply(msg))
}
func (m *Server) volExpand(w http.ResponseWriter, r *http.Request) {
var (
name string
authKey string
err error
msg string
capacity int
vol *Vol
)
if name, authKey, capacity, err = parseRequestToSetVolCapacity(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if vol, err = m.cluster.getVol(name); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeVolNotExists, Msg: err.Error()})
return
}
if uint64(capacity) <= vol.Capacity {
err = fmt.Errorf("expand capacity[%v] should be larger than the old capacity[%v]", capacity, vol.Capacity)
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
newArgs := getVolVarargs(vol)
newArgs.capacity = uint64(capacity)
if err = m.cluster.updateVol(name, authKey, newArgs); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
msg = fmt.Sprintf("update vol[%v] successfully\n", name)
sendOkReply(w, r, newSuccessHTTPReply(msg))
}
func (m *Server) volShrink(w http.ResponseWriter, r *http.Request) {
var (
name string
authKey string
err error
msg string
capacity int
vol *Vol
)
if name, authKey, capacity, err = parseRequestToSetVolCapacity(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if vol, err = m.cluster.getVol(name); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeVolNotExists, Msg: err.Error()})
return
}
if uint64(capacity) >= vol.Capacity {
err = fmt.Errorf("shrink capacity[%v] should be less than the old capacity[%v]", capacity, vol.Capacity)
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
newArgs := getVolVarargs(vol)
newArgs.capacity = uint64(capacity)
if err = m.cluster.updateVol(name, authKey, newArgs); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
msg = fmt.Sprintf("update vol[%v] successfully\n", name)
sendOkReply(w, r, newSuccessHTTPReply(msg))
}
func (m *Server) createVol(w http.ResponseWriter, r *http.Request) {
var (
name string
owner string
err error
msg string
size int
mpCount int
dpReplicaNum int
capacity int
vol *Vol
followerRead bool
authenticate bool
crossZone bool
defaultPriority bool
zoneName string
description string
)
if name, owner, zoneName, description,
mpCount, dpReplicaNum, size,
capacity, followerRead,
authenticate, crossZone, defaultPriority,
err = parseRequestToCreateVol(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if !(dpReplicaNum == 2 || dpReplicaNum == 3) {
err = fmt.Errorf("replicaNum can only be 2 and 3,received replicaNum is[%v]", dpReplicaNum)
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if vol, err = m.cluster.createVol(name, owner, zoneName, description,
mpCount, dpReplicaNum, size, capacity,
followerRead, authenticate, crossZone,
defaultPriority); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
if err = m.associateVolWithUser(owner, name); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
msg = fmt.Sprintf("create vol[%v] successfully, has allocate [%v] data partitions", name, len(vol.dataPartitions.partitions))
sendOkReply(w, r, newSuccessHTTPReply(msg))
}
func (m *Server) getVolSimpleInfo(w http.ResponseWriter, r *http.Request) {
var (
err error
name string
vol *Vol
volView *proto.SimpleVolView
)
if name, err = parseAndExtractName(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if vol, err = m.cluster.getVol(name); err != nil {
sendErrReply(w, r, newErrHTTPReply(proto.ErrVolNotExists))
return
}
volView = newSimpleView(vol)
sendOkReply(w, r, newSuccessHTTPReply(volView))
}
func newSimpleView(vol *Vol) *proto.SimpleVolView {
var (
volInodeCount uint64
volDentryCount uint64
)
for _, mp := range vol.MetaPartitions {
volDentryCount = volDentryCount + mp.DentryCount
volInodeCount = volInodeCount + mp.InodeCount
}
maxPartitionID := vol.maxPartitionID()
return &proto.SimpleVolView{
ID: vol.ID,
Name: vol.Name,
Owner: vol.Owner,
ZoneName: vol.zoneName,
DpReplicaNum: vol.dpReplicaNum,
MpReplicaNum: vol.mpReplicaNum,
InodeCount: volInodeCount,
DentryCount: volDentryCount,
MaxMetaPartitionID: maxPartitionID,
Status: vol.Status,
Capacity: vol.Capacity,
FollowerRead: vol.FollowerRead,
NeedToLowerReplica: vol.NeedToLowerReplica,
Authenticate: vol.authenticate,
CrossZone: vol.crossZone,
DefaultPriority: vol.defaultPriority,
DomainOn: vol.domainOn,
RwDpCnt: vol.dataPartitions.readableAndWritableCnt,
MpCnt: len(vol.MetaPartitions),
DpCnt: len(vol.dataPartitions.partitionMap),
CreateTime: time.Unix(vol.createTime, 0).Format(proto.TimeFormat),
Description: vol.description,
DpSelectorName: vol.dpSelectorName,
DpSelectorParm: vol.dpSelectorParm,
DefaultZonePrior: vol.defaultPriority,
}
}
func checkIp(addr string) bool {
var arr []string
if arr = strings.Split(addr, ":"); len(arr) < 2 {
return false
}
if id, err := strconv.ParseUint(arr[1], 10, 64); err != nil || id > 65535 || id < 1024 {
return false
}
ip := strings.Trim(addr, " ")
regStr := `^(([1-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.)(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){2}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])`
if match, _ := regexp.MatchString(regStr, ip); match {
return true
}
return false
}
func (m *Server) addDataNode(w http.ResponseWriter, r *http.Request) {
var (
nodeAddr string
zoneName string
id uint64
err error
nodesetId uint64
)
if nodeAddr, zoneName, err = parseRequestForAddNode(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if !checkIp(nodeAddr) {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: fmt.Errorf("addr not legal").Error()})
return
}
var value string
if value = r.FormValue(idKey); value == "" {
nodesetId = 0
} else {
if nodesetId, err = strconv.ParseUint(value, 10, 64); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
}
}
if id, err = m.cluster.addDataNode(nodeAddr, zoneName, nodesetId); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
sendOkReply(w, r, newSuccessHTTPReply(id))
}
func (m *Server) getDataNode(w http.ResponseWriter, r *http.Request) {
var (
nodeAddr string
dataNode *DataNode
dataNodeInfo *proto.DataNodeInfo
err error
)
if nodeAddr, err = parseAndExtractNodeAddr(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if dataNode, err = m.cluster.dataNode(nodeAddr); err != nil {
sendErrReply(w, r, newErrHTTPReply(proto.ErrDataNodeNotExists))
return
}
dataNode.PersistenceDataPartitions = m.cluster.getAllDataPartitionIDByDatanode(nodeAddr)
dataNodeInfo = &proto.DataNodeInfo{
Total: dataNode.Total,
Used: dataNode.Used,
AvailableSpace: dataNode.AvailableSpace,
ID: dataNode.ID,
ZoneName: dataNode.ZoneName,
Addr: dataNode.Addr,
ReportTime: dataNode.ReportTime,
IsActive: dataNode.isActive,
IsWriteAble: dataNode.isWriteAble(),
UsageRatio: dataNode.UsageRatio,
SelectedTimes: dataNode.SelectedTimes,
Carry: dataNode.Carry,
DataPartitionReports: dataNode.DataPartitionReports,
DataPartitionCount: dataNode.DataPartitionCount,
NodeSetID: dataNode.NodeSetID,
PersistenceDataPartitions: dataNode.PersistenceDataPartitions,
BadDisks: dataNode.BadDisks,
RdOnly: dataNode.RdOnly,
}
sendOkReply(w, r, newSuccessHTTPReply(dataNodeInfo))
}
// Decommission a data node. This will decommission all the data partition on that node.
func (m *Server) decommissionDataNode(w http.ResponseWriter, r *http.Request) {
var (
rstMsg string
offLineAddr string
limit int
err error
)
if offLineAddr, limit, err = parseDecomNodeReq(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if _, err = m.cluster.dataNode(offLineAddr); err != nil {
sendErrReply(w, r, newErrHTTPReply(proto.ErrDataNodeNotExists))
return
}
if err = m.cluster.migrateDataNode(offLineAddr, "", limit); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
rstMsg = fmt.Sprintf("decommission data node [%v] limit %d successfully", offLineAddr, limit)
sendOkReply(w, r, newSuccessHTTPReply(rstMsg))
}
func (m *Server) migrateDataNodeHandler(w http.ResponseWriter, r *http.Request) {
srcAddr, targetAddr, limit, err := parseMigrateNodeParam(r)
if err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if limit > defaultMigrateDpCnt {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError,
Msg: fmt.Sprintf("limit %d can't be bigger than %d", limit, defaultMigrateDpCnt)})
return
}
srcNode, err := m.cluster.dataNode(srcAddr)
if err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeDataNodeNotExists, Msg: err.Error()})
return
}
targetNode, err := m.cluster.dataNode(targetAddr)
if err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeDataNodeNotExists, Msg: err.Error()})
return
}
if srcNode.NodeSetID != targetNode.NodeSetID {
err = fmt.Errorf("src %s and target %s must exist in the same nodeSet when migrate", srcAddr, targetAddr)
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if !targetNode.isWriteAble() {
err = fmt.Errorf("[%s] is not writable, can't used as target addr for migrate", targetAddr)
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if err = m.cluster.migrateDataNode(srcAddr, targetAddr, limit); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
rstMsg := fmt.Sprintf("migrateDataNodeHandler from src [%v] to target[%v] has migrate successfully", srcAddr, targetAddr)
sendOkReply(w, r, newSuccessHTTPReply(rstMsg))
}
func (m *Server) setNodeInfoHandler(w http.ResponseWriter, r *http.Request) {
var (
params map[string]interface{}
err error
)
if params, err = parseAndExtractSetNodeInfoParams(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if batchCount, ok := params[nodeDeleteBatchCountKey]; ok {
if bc, ok := batchCount.(uint64); ok {
if err = m.cluster.setMetaNodeDeleteBatchCount(bc); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
}
}
if val, ok := params[nodeMarkDeleteRateKey]; ok {
if v, ok := val.(uint64); ok {
if err = m.cluster.setDataNodeDeleteLimitRate(v); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
}
}
if val, ok := params[nodeAutoRepairRateKey]; ok {
if v, ok := val.(uint64); ok {
if err = m.cluster.setDataNodeAutoRepairLimitRate(v); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
}
}
if val, ok := params[nodeDeleteWorkerSleepMs]; ok {
if v, ok := val.(uint64); ok {
if err = m.cluster.setMetaNodeDeleteWorkerSleepMs(v); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
}
}
sendOkReply(w, r, newSuccessHTTPReply(fmt.Sprintf("set nodeinfo params %v successfully", params)))
}
func (m *Server) updateDataUseRatio(ratio float64) (err error) {
m.cluster.nodeSetGrpManager.Lock()
defer m.cluster.nodeSetGrpManager.Unlock()
m.cluster.nodeSetGrpManager.dataRatioLimit = ratio
err = m.cluster.putZoneDomain(false)
return
}
func (m *Server) updateExcludeZoneUseRatio(ratio float64) (err error) {
m.cluster.nodeSetGrpManager.Lock()
defer m.cluster.nodeSetGrpManager.Unlock()
m.cluster.nodeSetGrpManager.excludeZoneUseRatio = ratio
err = m.cluster.putZoneDomain(false)
return
}
func (m *Server) updateNodesetId(zoneName string, destNodesetId uint64, nodeType uint64, addr string) (err error) {
var (
nsId uint64
dstNs *nodeSet
srcNs *nodeSet
ok bool
value interface{}
metaNode *MetaNode
dataNode *DataNode
)
defer func() {
log.LogInfof("action[updateNodesetId] step out")
}()
log.LogWarnf("action[updateNodesetId] zonename[%v] destNodesetId[%v] nodeType[%v] addr[%v]",
zoneName, destNodesetId, nodeType, addr)
if value, ok = m.cluster.t.zoneMap.Load(zoneName); !ok {
return fmt.Errorf("zonename [%v] not found", zoneName)
}
zone := value.(*Zone)
if dstNs, ok = zone.nodeSetMap[destNodesetId]; !ok {
return fmt.Errorf("%v destNodesetId not found", destNodesetId)
}
if uint32(nodeType) == TypeDataPartion {
value, ok = zone.dataNodes.Load(addr)
if !ok {
return fmt.Errorf("addr %v not found", addr)
}
nsId = value.(*DataNode).NodeSetID
} else if uint32(nodeType) == TypeMetaPartion {
value, ok = zone.metaNodes.Load(addr)
if !ok {
return fmt.Errorf("addr %v not found", addr)
}
nsId = value.(*MetaNode).NodeSetID
} else {
return fmt.Errorf("%v wrong type", nodeType)
}
log.LogInfof("action[updateNodesetId] zonename[%v] destNodesetId[%v] nodeType[%v] addr[%v] get destid[%v]",
zoneName, destNodesetId, nodeType, addr, dstNs.ID)
srcNs = zone.nodeSetMap[nsId]
if srcNs.ID == dstNs.ID {
return fmt.Errorf("addr belong to same nodeset")
} else if srcNs.ID < dstNs.ID {
// take parallel call updatenodeid and defer unlock order into consider
srcNs.Lock()
dstNs.Lock()
defer srcNs.Unlock()
defer dstNs.Unlock()
} else {
// take parallel call updatenodeid and defer unlock order into consider
dstNs.Lock()
srcNs.Lock()
defer dstNs.Unlock()
defer srcNs.Unlock()
}
// the nodeset capcity not enlarged if node be added,capacity can be adjust by
// AdminUpdateNodeSetCapcity
if uint32(nodeType) == TypeDataPartion {
if value, ok = srcNs.dataNodes.Load(addr); !ok {
return fmt.Errorf("addr not found in srcNs.dataNodes")
}
dataNode = value.(*DataNode)
dataNode.NodeSetID = dstNs.ID
dstNs.putDataNode(dataNode)
srcNs.deleteDataNode(dataNode)
if err = m.cluster.syncUpdateDataNode(dataNode); err != nil {
dataNode.NodeSetID = srcNs.ID
return
}
} else {
if value, ok = srcNs.metaNodes.Load(addr); !ok {
return fmt.Errorf("ddr not found in srcNs.metaNodes")
}
metaNode = value.(*MetaNode)
metaNode.NodeSetID = dstNs.ID
dstNs.putMetaNode(metaNode)
srcNs.deleteMetaNode(metaNode)
if err = m.cluster.syncUpdateMetaNode(metaNode); err != nil {
dataNode.NodeSetID = srcNs.ID
return
}
}
if err = m.cluster.syncUpdateNodeSet(dstNs); err != nil {
return fmt.Errorf("warn:syncUpdateNodeSet dst srcNs [%v] failed", dstNs.ID)
}
if err = m.cluster.syncUpdateNodeSet(srcNs); err != nil {
return fmt.Errorf("warn:syncUpdateNodeSet src srcNs [%v] failed", srcNs.ID)
}
return
}
func (m *Server) setNodeRdOnly(addr string, nodeType uint32, rdOnly bool) (err error) {
if nodeType == TypeDataPartion {
value, ok := m.cluster.dataNodes.Load(addr)
if !ok {
return fmt.Errorf("[setNodeRdOnly] data node %s is not exist", addr)
}
dataNode := value.(*DataNode)
oldRdOnly := dataNode.RdOnly
dataNode.RdOnly = rdOnly
if err = m.cluster.syncUpdateDataNode(dataNode); err != nil {
dataNode.RdOnly = oldRdOnly
return fmt.Errorf("[setNodeRdOnly] syncUpdateDataNode err(%s)", err.Error())
}
return
}
value, ok := m.cluster.metaNodes.Load(addr)
if !ok {
return fmt.Errorf("[setNodeRdOnly] meta node %s is not exist", addr)
}
metaNode := value.(*MetaNode)
oldRdOnly := metaNode.RdOnly
metaNode.RdOnly = rdOnly
if err = m.cluster.syncUpdateMetaNode(metaNode); err != nil {
metaNode.RdOnly = oldRdOnly
return fmt.Errorf("[setNodeRdOnly] syncUpdateMetaNode err(%s)", err.Error())
}
return
}
func (m *Server) updateNodesetCapcity(zoneName string, nodesetId uint64, capcity int) (err error) {
var ns *nodeSet
var ok bool
var value interface{}
if capcity < defaultReplicaNum || capcity > 100 {
err = fmt.Errorf("capcity [%v] value out of scope", capcity)
return
}
if value, ok = m.cluster.t.zoneMap.Load(zoneName); !ok {
err = fmt.Errorf("zonename [%v] not found", zoneName)
return
}
zone := value.(*Zone)
if ns, ok = zone.nodeSetMap[nodesetId]; !ok {
err = fmt.Errorf("nodesetId [%v] not found", nodesetId)
return
}
ns.Lock()
defer ns.Unlock()
ns.Capacity = capcity
m.cluster.syncUpdateNodeSet(ns)
log.LogInfof("updateNodesetCapcity update nodeSet[%d] cap(%d) success", nodesetId, capcity)
return
}
func (m *Server) buildNodeSetGrpInfoByID(id uint64) (*proto.SimpleNodeSetGrpInfo, error) {
nsgm := m.cluster.nodeSetGrpManager
var index int
for index = 0; index < len(nsgm.nodeSetGrpMap); index++ {
if nsgm.nodeSetGrpMap[index].ID == id {
break
}
if nsgm.nodeSetGrpMap[index].ID > id {
return nil, fmt.Errorf("id not found")
}
}
if index == len(nsgm.nodeSetGrpMap) {
return nil, fmt.Errorf("id not found")
}
return m.buildNodeSetGrpInfo(index), nil
}
func (m *Server) buildNodeSetGrpInfo(index int) *proto.SimpleNodeSetGrpInfo {
nsgm := m.cluster.nodeSetGrpManager
nsg := nsgm.nodeSetGrpMap[index]
nsgStat := new(proto.SimpleNodeSetGrpInfo)
nsgStat.ID = nsg.ID
nsgStat.Status = nsg.status
for i := 0; i < len(nsg.nodeSets); i++ {
var nsStat proto.NodeSetInfo
nsStat.ID = nsg.nodeSets[i].ID
nsStat.Capacity = nsg.nodeSets[i].Capacity
nsStat.ZoneName = nsg.nodeSets[i].zoneName
nsg.nodeSets[i].dataNodes.Range(func(key, value interface{}) bool {
node := value.(*DataNode)
nsStat.DataTotal += node.Total
if node.isWriteAble() {
nsStat.DataUsed += node.Used
} else {
nsStat.DataUsed += node.Total
}
log.LogInfof("nodeset index[%v], datanode nodeset id[%v],zonename[%v], addr[%v] inner nodesetid[%v]",
i, nsStat.ID, node.ZoneName, node.Addr, node.NodeSetID)
dataNodeInfo := &proto.DataNodeInfo{
Total: node.Total,
Used: node.Used,
AvailableSpace: node.AvailableSpace,
ID: node.ID,
ZoneName: node.ZoneName,
Addr: node.Addr,
ReportTime: node.ReportTime,
IsActive: node.isActive,
IsWriteAble: node.isWriteAble(),
UsageRatio: node.UsageRatio,
SelectedTimes: node.SelectedTimes,
Carry: node.Carry,
DataPartitionCount: node.DataPartitionCount,
NodeSetID: node.NodeSetID,
RdOnly: node.RdOnly,
}
nsStat.DataNodes = append(nsStat.DataNodes, dataNodeInfo)
return true
})
nsStat.DataUseRatio, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", float64(nsStat.DataUsed)/float64(nsStat.DataTotal)), 64)
nsg.nodeSets[i].metaNodes.Range(func(key, value interface{}) bool {
node := value.(*MetaNode)
nsStat.MetaTotal += node.Total
nsStat.MetaUsed += node.Used
log.LogInfof("nodeset index[%v], metanode nodeset id[%v],zonename[%v], addr[%v] inner nodesetid[%v]",
i, nsStat.ID, node.ZoneName, node.Addr, node.NodeSetID)
metaNodeInfo := &proto.MetaNodeInfo{
ID: node.ID,
Addr: node.Addr,
IsActive: node.IsActive,
IsWriteAble: node.isWritable(),
ZoneName: node.ZoneName,
MaxMemAvailWeight: node.MaxMemAvailWeight,
Total: node.Total,
Used: node.Used,
Ratio: node.Ratio,
SelectCount: node.SelectCount,
Carry: node.Carry,
Threshold: node.Threshold,
ReportTime: node.ReportTime,
MetaPartitionCount: node.MetaPartitionCount,
NodeSetID: node.NodeSetID,
}
nsStat.MetaNodes = append(nsStat.MetaNodes, metaNodeInfo)
return true
})
nsStat.MetaUseRatio, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", float64(nsStat.MetaUsed)/float64(nsStat.MetaTotal)), 64)
nsgStat.NodeSetInfo = append(nsgStat.NodeSetInfo, nsStat)
log.LogInfof("nodeset index[%v], nodeset id[%v],capacity[%v], datatotal[%v] dataused[%v] metatotal[%v] metaused[%v], metanode[%v], datanodes[%v]",
i, nsStat.ID, nsStat.Capacity, nsStat.DataTotal, nsStat.DataUsed, nsStat.MetaTotal, nsStat.MetaUsed, nsStat.MetaNodes, nsStat.DataNodes)
}
return nsgStat
}
func parseSetNodeRdOnlyParam(r *http.Request) (addr string, nodeType int, rdOnly bool, err error) {
if err = r.ParseForm(); err != nil {
return
}
if addr = r.FormValue(addrKey); addr == "" {
err = fmt.Errorf("parseSetNodeRdOnlyParam %s is empty", addrKey)
return
}
if nodeType, err = parseNodeType(r); err != nil {
return
}
val := r.FormValue(rdOnlyKey)
if val == "" {
err = fmt.Errorf("parseSetNodeRdOnlyParam %s is empty", rdOnlyKey)
return
}
if rdOnly, err = strconv.ParseBool(val); err != nil {
err = fmt.Errorf("parseSetNodeRdOnlyParam %s is not bool value %s", rdOnlyKey, val)
return
}
return
}
func parseNodeType(r *http.Request) (nodeType int, err error) {
var val string
if val = r.FormValue(nodeTypeKey); val == "" {
err = fmt.Errorf("parseSetNodeRdOnlyParam %s is empty", nodeTypeKey)
return
}
if nodeType, err = strconv.Atoi(val); err != nil {
err = fmt.Errorf("parseSetNodeRdOnlyParam %s is not number, err %s", nodeTypeKey, err.Error())
return
}
if nodeType != int(TypeDataPartion) && nodeType != int(TypeMetaPartion) {
err = fmt.Errorf("parseSetNodeRdOnlyParam %s is not legal, must be %d or %d", nodeTypeKey, TypeDataPartion, TypeMetaPartion)
return
}
return
}
func (m *Server) setNodeRdOnlyHandler(w http.ResponseWriter, r *http.Request) {
addr, nodeType, rdOnly, err := parseSetNodeRdOnlyParam(r)
if err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
log.LogInfof("[setNodeRdOnlyHandler] set node %s to rdOnly(%v)", addr, rdOnly)
err = m.setNodeRdOnly(addr, uint32(nodeType), rdOnly)
if err != nil {
log.LogErrorf("[setNodeRdOnlyHandler] set node %s to rdOnly %v, err (%s)", addr, rdOnly, err.Error())
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
sendOkReply(w, r, newSuccessHTTPReply(fmt.Sprintf("[setNodeRdOnlyHandler] set node %s to rdOnly(%v) success", addr, rdOnly)))
return
}
func (m *Server) updateNodeSetCapacityHandler(w http.ResponseWriter, r *http.Request) {
cnt, id, zoneName, err := parseSetNodeSetCapParams(r)
if err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if err := m.updateNodesetCapcity(zoneName, uint64(id), cnt); err != nil {
log.LogErrorf("updateNodeSetCapacityHandler update node set fail, zone(%s) set(%d) cnt(%d), err(%s)",
zoneName, id, cnt, err.Error())
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
sendOkReply(w, r, newSuccessHTTPReply(fmt.Sprintf("set nodesetinfo zone(%s) nodeSet(%d) cnt(%d) successfully",
zoneName, id, cnt)))
}
func (m *Server) updateDataUseRatioHandler(w http.ResponseWriter, r *http.Request) {
var (
params map[string]interface{}
err error
)
var value string
if value = r.FormValue(ratio); value == "" {
err = keyNotFound(ratio)
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
var ratioVal float64
if ratioVal, err = strconv.ParseFloat(value, 64); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if ratioVal == 0 || ratioVal > 1 {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if err = m.updateDataUseRatio(ratioVal); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
sendOkReply(w, r, newSuccessHTTPReply(fmt.Sprintf("set nodesetinfo params %v successfully", params)))
}
func (m *Server) updateZoneExcludeRatioHandler(w http.ResponseWriter, r *http.Request) {
var (
params map[string]interface{}
err error
)
var value string
if value = r.FormValue(ratio); value == "" {
err = keyNotFound(ratio)
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
var ratioVal float64
if ratioVal, err = strconv.ParseFloat(value, 64); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if err = m.updateExcludeZoneUseRatio(ratioVal); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
sendOkReply(w, r, newSuccessHTTPReply(fmt.Sprintf("set nodesetinfo params %v successfully", params)))
}
func (m *Server) updateNodeSetIdHandler(w http.ResponseWriter, r *http.Request) {
var (
nodeAddr string
id uint64
zoneName string
err error
nodeType uint64
value string
)
defer func() {
if err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
}
}()
if zoneName = r.FormValue(zoneNameKey); zoneName == "" {
zoneName = DefaultZoneName
}
if err = r.ParseForm(); err != nil {
return
}
if nodeAddr, err = extractNodeAddr(r); err != nil {
return
}
if id, err = extractNodeID(r); err != nil {
return
}
if value = r.FormValue(nodeTypeKey); value == "" {
err = fmt.Errorf("need param nodeType")
return
}
if nodeType, err = strconv.ParseUint(value, 10, 64); err != nil {
return
}
if err = m.updateNodesetId(zoneName, id, nodeType, nodeAddr); err != nil {
return
}
sendOkReply(w, r, newSuccessHTTPReply(fmt.Sprintf("update node setid successfully")))
}
// get metanode some interval params
func (m *Server) getNodeSetGrpInfoHandler(w http.ResponseWriter, r *http.Request) {
var err error
if err = r.ParseForm(); err != nil {
sendOkReply(w, r, newErrHTTPReply(err))
return
}
var value string
var id uint64
if value = r.FormValue(idKey); value != "" {
id, err = strconv.ParseUint(value, 10, 64)
if err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
}
}
log.LogInfof("action[getNodeSetGrpInfoHandler] id [%v]", id)
var info *proto.SimpleNodeSetGrpInfo
if info, err = m.buildNodeSetGrpInfoByID(id); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
}
sendOkReply(w, r, newSuccessHTTPReply(info))
}
func (m *Server) getIsDomainOn(w http.ResponseWriter, r *http.Request) {
type SimpleDomainInfo struct {
DomainOn bool
}
nsglStat := new(SimpleDomainInfo)
nsglStat.DomainOn = m.cluster.FaultDomain
sendOkReply(w, r, newSuccessHTTPReply(nsglStat))
}
// get metanode some interval params
func (m *Server) getAllNodeSetGrpInfoHandler(w http.ResponseWriter, r *http.Request) {
var i int
nsgm := m.cluster.nodeSetGrpManager
nsglStat := new(proto.SimpleNodeSetGrpInfoList)
nsglStat.DomainOn = m.cluster.FaultDomain
nsglStat.NeedDomain = m.cluster.needFaultDomain
nsglStat.DataRatioLimit = nsgm.dataRatioLimit
nsglStat.ZoneExcludeRatioLimit = nsgm.excludeZoneUseRatio
nsglStat.Status = nsgm.status
nsglStat.ExcludeZones = nsgm.c.t.domainExcludeZones
for i = 0; i < len(nsgm.nodeSetGrpMap); i++ {
log.LogInfof("action[getAllNodeSetGrpInfoHandler] index [%v],id [%v],Print inner nodeset now!", i, nsgm.nodeSetGrpMap[i].ID)
nsglStat.SimpleNodeSetGrpInfo = append(nsglStat.SimpleNodeSetGrpInfo, m.buildNodeSetGrpInfo(i))
}
sendOkReply(w, r, newSuccessHTTPReply(nsglStat))
}
// get metanode some interval params
func (m *Server) getNodeInfoHandler(w http.ResponseWriter, r *http.Request) {
resp := make(map[string]string)
resp[nodeDeleteBatchCountKey] = fmt.Sprintf("%v", m.cluster.cfg.MetaNodeDeleteBatchCount)
resp[nodeMarkDeleteRateKey] = fmt.Sprintf("%v", m.cluster.cfg.DataNodeDeleteLimitRate)
resp[nodeDeleteWorkerSleepMs] = fmt.Sprintf("%v", m.cluster.cfg.MetaNodeDeleteWorkerSleepMs)
resp[nodeAutoRepairRateKey] = fmt.Sprintf("%v", m.cluster.cfg.DataNodeAutoRepairLimitRate)
sendOkReply(w, r, newSuccessHTTPReply(resp))
}
func (m *Server) diagnoseMetaPartition(w http.ResponseWriter, r *http.Request) {
var (
err error
rstMsg *proto.MetaPartitionDiagnosis
inactiveNodes []string
corruptMps []*MetaPartition
lackReplicaMps []*MetaPartition
corruptMpIDs []uint64
lackReplicaMpIDs []uint64
badMetaPartitions []badPartitionView
)
corruptMpIDs = make([]uint64, 0)
lackReplicaMpIDs = make([]uint64, 0)
if inactiveNodes, corruptMps, err = m.cluster.checkCorruptMetaPartitions(); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
}
if lackReplicaMps, err = m.cluster.checkLackReplicaMetaPartitions(); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
}
for _, mp := range corruptMps {
corruptMpIDs = append(corruptMpIDs, mp.PartitionID)
}
for _, mp := range lackReplicaMps {
lackReplicaMpIDs = append(lackReplicaMpIDs, mp.PartitionID)
}
badMetaPartitions = m.cluster.getBadMetaPartitionsView()
rstMsg = &proto.MetaPartitionDiagnosis{
InactiveMetaNodes: inactiveNodes,
CorruptMetaPartitionIDs: corruptMpIDs,
LackReplicaMetaPartitionIDs: lackReplicaMpIDs,
BadMetaPartitionIDs: badMetaPartitions,
}
log.LogInfof("diagnose metaPartition[%v] inactiveNodes:[%v], corruptMpIDs:[%v], lackReplicaMpIDs:[%v]", m.cluster.Name, inactiveNodes, corruptMpIDs, lackReplicaMpIDs)
sendOkReply(w, r, newSuccessHTTPReply(rstMsg))
}
// Decommission a disk. This will decommission all the data partitions on this disk.
func (m *Server) decommissionDisk(w http.ResponseWriter, r *http.Request) {
var (
node *DataNode
rstMsg string
offLineAddr, diskPath string
err error
limit int
badPartitionIds []uint64
badPartitions []*DataPartition
)
if offLineAddr, diskPath, limit, err = parseReqToDecoDisk(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if node, err = m.cluster.dataNode(offLineAddr); err != nil {
sendErrReply(w, r, newErrHTTPReply(proto.ErrDataNodeNotExists))
return
}
badPartitions = node.badPartitions(diskPath, m.cluster)
if len(badPartitions) == 0 {
rstMsg = fmt.Sprintf("receive decommissionDisk node[%v] no any partitions on disk[%v],offline successfully",
node.Addr, diskPath)
sendOkReply(w, r, newSuccessHTTPReply(rstMsg))
return
}
for _, bdp := range badPartitions {
badPartitionIds = append(badPartitionIds, bdp.PartitionID)
}
if limit > 0 && limit < len(badPartitionIds) {
badPartitionIds = badPartitionIds[:limit]
badPartitions = badPartitions[:limit]
}
rstMsg = fmt.Sprintf("receive decommissionDisk node[%v] disk[%v] limit [%d], badPartitionIds[%v] has offline successfully",
node.Addr, diskPath, limit, badPartitionIds)
if err = m.cluster.decommissionDisk(node, diskPath, badPartitions); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
Warn(m.clusterName, rstMsg)
sendOkReply(w, r, newSuccessHTTPReply(rstMsg))
}
// handle tasks such as heartbeat๏ผŒloadDataPartition๏ผŒdeleteDataPartition, etc.
func (m *Server) handleDataNodeTaskResponse(w http.ResponseWriter, r *http.Request) {
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)))
go m.cluster.handleDataNodeTaskResponse(tr.OperatorAddr, tr)
}
func (m *Server) addMetaNode(w http.ResponseWriter, r *http.Request) {
var (
nodeAddr string
zoneName string
id uint64
err error
nodesetId uint64
)
if nodeAddr, zoneName, err = parseRequestForAddNode(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if !checkIp(nodeAddr) {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: fmt.Errorf("addr not legal").Error()})
return
}
var value string
if value = r.FormValue(idKey); value == "" {
nodesetId = 0
} else {
if nodesetId, err = strconv.ParseUint(value, 10, 64); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
}
}
if id, err = m.cluster.addMetaNode(nodeAddr, zoneName, nodesetId); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
sendOkReply(w, r, newSuccessHTTPReply(id))
}
func (m *Server) checkInvalidIDNodes(w http.ResponseWriter, r *http.Request) {
nodes := m.cluster.getInvalidIDNodes()
sendOkReply(w, r, newSuccessHTTPReply(nodes))
}
func (m *Server) updateDataNode(w http.ResponseWriter, r *http.Request) {
var (
nodeAddr string
id uint64
err error
)
if nodeAddr, id, err = parseRequestForUpdateMetaNode(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if err = m.cluster.updateDataNodeBaseInfo(nodeAddr, id); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
sendOkReply(w, r, newSuccessHTTPReply(id))
}
func (m *Server) updateMetaNode(w http.ResponseWriter, r *http.Request) {
var (
nodeAddr string
id uint64
err error
)
if nodeAddr, id, err = parseRequestForUpdateMetaNode(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if err = m.cluster.updateMetaNodeBaseInfo(nodeAddr, id); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
sendOkReply(w, r, newSuccessHTTPReply(id))
}
func (m *Server) getMetaNode(w http.ResponseWriter, r *http.Request) {
var (
nodeAddr string
metaNode *MetaNode
metaNodeInfo *proto.MetaNodeInfo
err error
)
if nodeAddr, err = parseAndExtractNodeAddr(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if metaNode, err = m.cluster.metaNode(nodeAddr); err != nil {
sendErrReply(w, r, newErrHTTPReply(proto.ErrMetaNodeNotExists))
return
}
metaNode.PersistenceMetaPartitions = m.cluster.getAllMetaPartitionIDByMetaNode(nodeAddr)
metaNodeInfo = &proto.MetaNodeInfo{
ID: metaNode.ID,
Addr: metaNode.Addr,
IsActive: metaNode.IsActive,
IsWriteAble: metaNode.isWritable(),
ZoneName: metaNode.ZoneName,
MaxMemAvailWeight: metaNode.MaxMemAvailWeight,
Total: metaNode.Total,
Used: metaNode.Used,
Ratio: metaNode.Ratio,
SelectCount: metaNode.SelectCount,
Carry: metaNode.Carry,
Threshold: metaNode.Threshold,
ReportTime: metaNode.ReportTime,
MetaPartitionCount: metaNode.MetaPartitionCount,
NodeSetID: metaNode.NodeSetID,
PersistenceMetaPartitions: metaNode.PersistenceMetaPartitions,
RdOnly: metaNode.RdOnly,
}
sendOkReply(w, r, newSuccessHTTPReply(metaNodeInfo))
}
func (m *Server) decommissionMetaPartition(w http.ResponseWriter, r *http.Request) {
var (
partitionID uint64
nodeAddr string
mp *MetaPartition
msg string
err error
)
if partitionID, nodeAddr, err = parseRequestToDecommissionMetaPartition(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if mp, err = m.cluster.getMetaPartitionByID(partitionID); err != nil {
sendErrReply(w, r, newErrHTTPReply(proto.ErrMetaPartitionNotExists))
return
}
if err = m.cluster.decommissionMetaPartition(nodeAddr, mp); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
msg = fmt.Sprintf(proto.AdminDecommissionMetaPartition+" partitionID :%v decommissionMetaPartition successfully", partitionID)
sendOkReply(w, r, newSuccessHTTPReply(msg))
}
func (m *Server) loadMetaPartition(w http.ResponseWriter, r *http.Request) {
var (
msg string
mp *MetaPartition
partitionID uint64
err error
)
if partitionID, err = parseRequestToLoadMetaPartition(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if mp, err = m.cluster.getMetaPartitionByID(partitionID); err != nil {
sendErrReply(w, r, newErrHTTPReply(proto.ErrMetaPartitionNotExists))
return
}
m.cluster.loadMetaPartitionAndCheckResponse(mp)
msg = fmt.Sprintf(proto.AdminLoadMetaPartition+" partitionID :%v Load successfully", partitionID)
sendOkReply(w, r, newSuccessHTTPReply(msg))
}
func parseMigrateNodeParam(r *http.Request) (srcAddr, targetAddr string, limit int, err error) {
if err = r.ParseForm(); err != nil {
return
}
srcAddr = r.FormValue(srcAddrKey)
if srcAddr == "" {
err = fmt.Errorf("parseMigrateNodeParam %s can't be empty", srcAddrKey)
return
}
targetAddr = r.FormValue(targetAddrKey)
if targetAddr == "" {
err = fmt.Errorf("parseMigrateNodeParam %s can't be empty when migrate", targetAddrKey)
return
}
if srcAddr == targetAddr {
err = fmt.Errorf("parseMigrateNodeParam srcAddr %s can't be equal to targetAddr %s", srcAddr, targetAddr)
return
}
limit, err = parseUintParam(r, countKey)
if err != nil {
return
}
return
}
func parseUintParam(r *http.Request, key string) (num int, err error) {
val := r.FormValue(key)
if val == "" {
num = 0
return
}
numVal, err := strconv.ParseUint(val, 10, 64)
if err != nil {
err = fmt.Errorf("parseUintParam %s-%s is not legal, err %s", key, val, err.Error())
return
}
num = int(numVal)
return
}
func (m *Server) migrateMetaNodeHandler(w http.ResponseWriter, r *http.Request) {
srcAddr, targetAddr, limit, err := parseMigrateNodeParam(r)
if err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if limit > defaultMigrateMpCnt {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError,
Msg: fmt.Sprintf("limit %d can't be bigger than %d", limit, defaultMigrateMpCnt)})
return
}
srcNode, err := m.cluster.metaNode(srcAddr)
if err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeMetaNodeNotExists, Msg: err.Error()})
return
}
targetNode, err := m.cluster.metaNode(targetAddr)
if err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeMetaNodeNotExists, Msg: err.Error()})
return
}
if srcNode.NodeSetID != targetNode.NodeSetID {
err = fmt.Errorf("src %s and target %s must exist in the same nodeSet when migrate", srcAddr, targetAddr)
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeMetaNodeNotExists, Msg: err.Error()})
return
}
if !targetNode.isWritable() {
err = fmt.Errorf("[%s] is not writable, can't used as target addr for migrate", targetAddr)
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if err = m.cluster.migrateMetaNode(srcAddr, targetAddr, limit); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
rstMsg := fmt.Sprintf("migrateMetaNodeHandler from src [%v] to targaet[%s] has migrate successfully", srcAddr, targetAddr)
sendOkReply(w, r, newSuccessHTTPReply(rstMsg))
}
func (m *Server) decommissionMetaNode(w http.ResponseWriter, r *http.Request) {
var (
rstMsg string
offLineAddr string
limit int
err error
)
if offLineAddr, limit, err = parseDecomNodeReq(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if _, err = m.cluster.metaNode(offLineAddr); err != nil {
sendErrReply(w, r, newErrHTTPReply(proto.ErrMetaNodeNotExists))
return
}
if err = m.cluster.migrateMetaNode(offLineAddr, "", limit); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
rstMsg = fmt.Sprintf("decommissionMetaNode metaNode [%v] limit %d has offline successfully", offLineAddr, limit)
sendOkReply(w, r, newSuccessHTTPReply(rstMsg))
}
func (m *Server) handleMetaNodeTaskResponse(w http.ResponseWriter, r *http.Request) {
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)))
go m.cluster.handleMetaNodeTaskResponse(tr.OperatorAddr, tr)
}
// Dynamically add a raft node (replica) for the master.
// By using this function, there is no need to stop all the master services. Adding a new raft node is performed online.
func (m *Server) addRaftNode(w http.ResponseWriter, r *http.Request) {
var msg string
id, addr, err := parseRequestForRaftNode(r)
if err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if err = m.cluster.addRaftNode(id, addr); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
msg = fmt.Sprintf("add raft node id :%v, addr:%v successfully \n", id, addr)
sendOkReply(w, r, newSuccessHTTPReply(msg))
}
// Dynamically remove a master node. Similar to addRaftNode, this operation is performed online.
func (m *Server) removeRaftNode(w http.ResponseWriter, r *http.Request) {
var msg string
id, addr, err := parseRequestForRaftNode(r)
if err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
err = m.cluster.removeRaftNode(id, addr)
if err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
msg = fmt.Sprintf("remove raft node id :%v,adr:%v successfully\n", id, addr)
sendOkReply(w, r, newSuccessHTTPReply(msg))
}
// 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 {
return
}
var idStr string
if idStr = r.FormValue(idKey); idStr == "" {
err = keyNotFound(idKey)
return
}
if id, err = strconv.ParseUint(idStr, 10, 64); err != nil {
return
}
if host = r.FormValue(addrKey); host == "" {
err = keyNotFound(addrKey)
return
}
if arr := strings.Split(host, colonSplit); len(arr) < 2 {
err = unmatchedKey(addrKey)
return
}
return
}
func parseRequestForUpdateMetaNode(r *http.Request) (nodeAddr string, id uint64, err error) {
if err = r.ParseForm(); err != nil {
return
}
if nodeAddr, err = extractNodeAddr(r); err != nil {
return
}
if id, err = extractNodeID(r); err != nil {
return
}
return
}
func parseRequestForAddNode(r *http.Request) (nodeAddr, zoneName 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
}
return
}
func parseDecomNodeReq(r *http.Request) (nodeAddr string, limit int, err error) {
nodeAddr, err = parseAndExtractNodeAddr(r)
if err != nil {
return
}
limit, err = parseUintParam(r, countKey)
if err != nil {
return
}
return
}
func parseAndExtractNodeAddr(r *http.Request) (nodeAddr string, err error) {
if err = r.ParseForm(); err != nil {
return
}
return extractNodeAddr(r)
}
func parseReqToDecoDisk(r *http.Request) (nodeAddr, diskPath string, limit int, err error) {
if err = r.ParseForm(); err != nil {
return
}
nodeAddr, err = extractNodeAddr(r)
if err != nil {
return
}
diskPath, err = extractDiskPath(r)
if err != nil {
return
}
limit, err = parseUintParam(r, countKey)
return
}
func parseRequestToGetTaskResponse(r *http.Request) (tr *proto.AdminTask, err error) {
var body []byte
if err = r.ParseForm(); err != nil {
return
}
if body, err = ioutil.ReadAll(r.Body); err != nil {
return
}
tr = &proto.AdminTask{}
decoder := json.NewDecoder(bytes.NewBuffer([]byte(body)))
decoder.UseNumber()
err = decoder.Decode(tr)
return
}
func parseVolName(r *http.Request) (name string, err error) {
if err = r.ParseForm(); err != nil {
return
}
if name, err = extractName(r); err != nil {
return
}
return
}
type getVolParameter struct {
name string
authKey string
skipOwnerValidation bool
}
func parseGetVolParameter(r *http.Request) (p *getVolParameter, err error) {
p = &getVolParameter{}
skipOwnerValidationVal := r.Header.Get(proto.SkipOwnerValidation)
if len(skipOwnerValidationVal) > 0 {
if p.skipOwnerValidation, err = strconv.ParseBool(skipOwnerValidationVal); err != nil {
return
}
}
if p.name = r.FormValue(nameKey); p.name == "" {
err = keyNotFound(nameKey)
return
}
if !volNameRegexp.MatchString(p.name) {
err = errors.New("name can only be number and letters")
return
}
if p.authKey = r.FormValue(volAuthKey); !p.skipOwnerValidation && len(p.authKey) == 0 {
err = keyNotFound(volAuthKey)
return
}
return
}
func parseVolNameAndAuthKey(r *http.Request) (name, authKey string, err error) {
if err = r.ParseForm(); err != nil {
return
}
if name, err = extractName(r); err != nil {
return
}
if authKey, err = extractAuthKey(r); err != nil {
return
}
return
}
func parseRequestToDeleteVol(r *http.Request) (name, authKey string, err error) {
return parseVolNameAndAuthKey(r)
}
func parseRequestToUpdateVol(r *http.Request) (name, authKey, description string, err error) {
if err = r.ParseForm(); err != nil {
return
}
if name, err = extractName(r); err != nil {
return
}
if authKey, err = extractAuthKey(r); err != nil {
return
}
description = r.FormValue(descriptionKey)
return
}
func parseDefaultInfoToUpdateVol(r *http.Request, vol *Vol) (zoneName string, capacity uint64, replicaNum int,
dpSelectorName string, dpSelectorParm string, err error) {
if err = r.ParseForm(); err != nil {
return
}
if zoneName = r.FormValue(zoneNameKey); zoneName == "" {
zoneName = vol.zoneName
}
if capacityStr := r.FormValue(volCapacityKey); capacityStr != "" {
var capacityInt int
if capacityInt, err = strconv.Atoi(capacityStr); err != nil {
err = unmatchedKey(volCapacityKey)
return
}
capacity = uint64(capacityInt)
} else {
capacity = vol.Capacity
}
if replicaNumStr := r.FormValue(replicaNumKey); replicaNumStr != "" {
if replicaNum, err = strconv.Atoi(replicaNumStr); err != nil {
err = unmatchedKey(replicaNumKey)
return
}
} else {
replicaNum = int(vol.dpReplicaNum)
}
dpSelectorName = r.FormValue(dpSelectorNameKey)
dpSelectorParm = r.FormValue(dpSelectorParmKey)
if (dpSelectorName == "") || (dpSelectorParm == "") {
if (dpSelectorName != "") || (dpSelectorParm != "") {
err = keyNotFound(dpSelectorNameKey + " or " + dpSelectorParmKey)
return
}
dpSelectorName = vol.dpSelectorName
dpSelectorParm = vol.dpSelectorParm
}
return
}
func parseBoolFieldToUpdateVol(r *http.Request, vol *Vol) (followerRead, authenticate bool, err error) {
if followerReadStr := r.FormValue(followerReadKey); followerReadStr != "" {
if followerRead, err = strconv.ParseBool(followerReadStr); err != nil {
err = unmatchedKey(followerReadKey)
return
}
} else {
followerRead = vol.FollowerRead
}
if authenticateStr := r.FormValue(authenticateKey); authenticateStr != "" {
if authenticate, err = strconv.ParseBool(authenticateStr); err != nil {
err = unmatchedKey(authenticateKey)
return
}
} else {
authenticate = vol.authenticate
}
return
}
func parseRequestToSetVolCapacity(r *http.Request) (name, authKey string, capacity int, err error) {
if err = r.ParseForm(); err != nil {
return
}
if name, err = extractName(r); err != nil {
return
}
if authKey, err = extractAuthKey(r); err != nil {
return
}
if capacity, err = extractCapacity(r); err != nil {
return
}
return
}
func parseRequestToCreateVol(r *http.Request) (name, owner, zoneName, description string,
mpCount, dpReplicaNum, size,
capacity int, followerRead,
authenticate, crossZone, defaultPriority bool,
err error) {
if err = r.ParseForm(); err != nil {
return
}
if name, err = extractName(r); err != nil {
return
}
if owner, err = extractOwner(r); err != nil {
return
}
if mpCountStr := r.FormValue(metaPartitionCountKey); mpCountStr != "" {
if mpCount, err = strconv.Atoi(mpCountStr); err != nil {
mpCount = defaultInitMetaPartitionCount
}
}
if replicaStr := r.FormValue(replicaNumKey); replicaStr == "" {
dpReplicaNum = defaultReplicaNum
} else if dpReplicaNum, err = strconv.Atoi(replicaStr); err != nil {
err = unmatchedKey(replicaNumKey)
return
}
if sizeStr := r.FormValue(dataPartitionSizeKey); sizeStr != "" {
if size, err = strconv.Atoi(sizeStr); err != nil {
err = unmatchedKey(dataPartitionSizeKey)
return
}
}
if capacity, err = extractCapacity(r); err != nil {
return
}
if followerRead, err = extractFollowerRead(r); err != nil {
return
}
if authenticate, err = extractAuthenticate(r); err != nil {
return
}
if crossZone, err = extractCrossZone(r); err != nil {
return
}
if defaultPriority, err = extractDefaulPriority(r); err != nil {
return
}
zoneName = r.FormValue(zoneNameKey)
description = r.FormValue(descriptionKey)
return
}
func parseRequestToCreateDataPartition(r *http.Request) (count int, name string, err error) {
if err = r.ParseForm(); err != nil {
return
}
if countStr := r.FormValue(countKey); countStr == "" {
err = keyNotFound(countKey)
return
} else if count, err = strconv.Atoi(countStr); err != nil || count == 0 {
err = unmatchedKey(countKey)
return
}
if name, err = extractName(r); err != nil {
return
}
return
}
func parseRequestToGetDataPartition(r *http.Request) (ID uint64, volName string, err error) {
if err = r.ParseForm(); err != nil {
return
}
if ID, err = extractDataPartitionID(r); err != nil {
return
}
volName = r.FormValue(nameKey)
return
}
func parseRequestToLoadDataPartition(r *http.Request) (ID uint64, err error) {
if err = r.ParseForm(); err != nil {
return
}
if ID, err = extractDataPartitionID(r); err != nil {
return
}
return
}
func parseRequestToAddMetaReplica(r *http.Request) (ID uint64, addr string, err error) {
return extractMetaPartitionIDAndAddr(r)
}
func parseRequestToRemoveMetaReplica(r *http.Request) (ID uint64, addr string, err error) {
return extractMetaPartitionIDAndAddr(r)
}
func extractMetaPartitionIDAndAddr(r *http.Request) (ID uint64, addr string, err error) {
if err = r.ParseForm(); err != nil {
return
}
if ID, err = extractMetaPartitionID(r); err != nil {
return
}
if addr, err = extractNodeAddr(r); err != nil {
return
}
return
}
func parseRequestToAddDataReplica(r *http.Request) (ID uint64, addr string, err error) {
return extractDataPartitionIDAndAddr(r)
}
func parseRequestToRemoveDataReplica(r *http.Request) (ID uint64, addr string, err error) {
return extractDataPartitionIDAndAddr(r)
}
func extractDataPartitionIDAndAddr(r *http.Request) (ID uint64, addr string, err error) {
if err = r.ParseForm(); err != nil {
return
}
if ID, err = extractDataPartitionID(r); err != nil {
return
}
if addr, err = extractNodeAddr(r); err != nil {
return
}
return
}
func extractDataPartitionID(r *http.Request) (ID uint64, err error) {
var value string
if value = r.FormValue(idKey); value == "" {
err = keyNotFound(idKey)
return
}
return strconv.ParseUint(value, 10, 64)
}
func parseRequestToDecommissionDataPartition(r *http.Request) (ID uint64, nodeAddr string, err error) {
return extractDataPartitionIDAndAddr(r)
}
func extractNodeAddr(r *http.Request) (nodeAddr string, err error) {
if nodeAddr = r.FormValue(addrKey); nodeAddr == "" {
err = keyNotFound(addrKey)
return
}
return
}
func extractNodeID(r *http.Request) (ID uint64, err error) {
var value string
if value = r.FormValue(idKey); value == "" {
err = keyNotFound(idKey)
return
}
return strconv.ParseUint(value, 10, 64)
}
func extractDiskPath(r *http.Request) (diskPath string, err error) {
if diskPath = r.FormValue(diskPathKey); diskPath == "" {
err = keyNotFound(diskPathKey)
return
}
return
}
func parseRequestToLoadMetaPartition(r *http.Request) (partitionID uint64, err error) {
if err = r.ParseForm(); err != nil {
return
}
if partitionID, err = extractMetaPartitionID(r); err != nil {
return
}
return
}
func parseRequestToDecommissionMetaPartition(r *http.Request) (partitionID uint64, nodeAddr string, err error) {
return extractMetaPartitionIDAndAddr(r)
}
func parseAndExtractStatus(r *http.Request) (status bool, err error) {
if err = r.ParseForm(); err != nil {
return
}
return extractStatus(r)
}
func extractStatus(r *http.Request) (status bool, err error) {
var value string
if value = r.FormValue(enableKey); value == "" {
err = keyNotFound(enableKey)
return
}
if status, err = strconv.ParseBool(value); err != nil {
return
}
return
}
func extractFollowerRead(r *http.Request) (followerRead bool, err error) {
var value string
if value = r.FormValue(followerReadKey); value == "" {
followerRead = false
return
}
if followerRead, err = strconv.ParseBool(value); err != nil {
return
}
return
}
func extractAuthenticate(r *http.Request) (authenticate bool, err error) {
var value string
if value = r.FormValue(authenticateKey); value == "" {
authenticate = false
return
}
if authenticate, err = strconv.ParseBool(value); err != nil {
return
}
return
}
func extractCrossZone(r *http.Request) (crossZone bool, err error) {
var value string
if value = r.FormValue(crossZoneKey); value == "" {
crossZone = false
return
}
if crossZone, err = strconv.ParseBool(value); err != nil {
return
}
return
}
func extractDefaulPriority(r *http.Request) (defaultPrior bool, err error) {
var value string
if value = r.FormValue(defaultPriority); value == "" {
defaultPrior = false
return
}
if defaultPrior, err = strconv.ParseBool(value); err != nil {
return
}
return
}
func parseAndExtractThreshold(r *http.Request) (threshold float64, err error) {
if err = r.ParseForm(); err != nil {
return
}
var value string
if value = r.FormValue(thresholdKey); value == "" {
err = keyNotFound(thresholdKey)
return
}
if threshold, err = strconv.ParseFloat(value, 64); err != nil {
return
}
return
}
func parseSetNodeSetCapParams(r *http.Request) (count, id int, zoneName string, err error) {
if err = r.ParseForm(); err != nil {
return
}
count, err = parseUintParam(r, countKey)
if err != nil {
return
}
if zoneName = r.FormValue(zoneNameKey); zoneName == "" {
zoneName = DefaultZoneName
}
id, err = parseUintParam(r, idKey)
if err != nil {
return
}
return
}
func parseAndExtractSetNodeInfoParams(r *http.Request) (params map[string]interface{}, err error) {
if err = r.ParseForm(); err != nil {
return
}
var value string
noParams := true
params = make(map[string]interface{})
if value = r.FormValue(nodeDeleteBatchCountKey); value != "" {
noParams = false
var batchCount = uint64(0)
batchCount, err = strconv.ParseUint(value, 10, 64)
if err != nil {
err = unmatchedKey(nodeDeleteBatchCountKey)
return
}
params[nodeDeleteBatchCountKey] = batchCount
}
if value = r.FormValue(nodeMarkDeleteRateKey); value != "" {
noParams = false
var val = uint64(0)
val, err = strconv.ParseUint(value, 10, 64)
if err != nil {
err = unmatchedKey(nodeMarkDeleteRateKey)
return
}
params[nodeMarkDeleteRateKey] = val
}
if value = r.FormValue(nodeAutoRepairRateKey); value != "" {
noParams = false
var val = uint64(0)
val, err = strconv.ParseUint(value, 10, 64)
if err != nil {
err = unmatchedKey(nodeAutoRepairRateKey)
return
}
params[nodeAutoRepairRateKey] = val
}
if value = r.FormValue(nodeDeleteWorkerSleepMs); value != "" {
noParams = false
var val = uint64(0)
val, err = strconv.ParseUint(value, 10, 64)
if err != nil {
err = unmatchedKey(nodeMarkDeleteRateKey)
return
}
params[nodeDeleteWorkerSleepMs] = val
}
if noParams {
err = keyNotFound(nodeDeleteBatchCountKey)
return
}
return
}
func validateRequestToCreateMetaPartition(r *http.Request) (volName string, start uint64, err error) {
if volName, err = extractName(r); err != nil {
return
}
var value string
if value = r.FormValue(startKey); value == "" {
err = keyNotFound(startKey)
return
}
start, err = strconv.ParseUint(value, 10, 64)
return
}
func newSuccessHTTPReply(data interface{}) *proto.HTTPReply {
return &proto.HTTPReply{Code: proto.ErrCodeSuccess, Msg: proto.ErrSuc.Error(), Data: data}
}
func newErrHTTPReply(err error) *proto.HTTPReply {
if err == nil {
return newSuccessHTTPReply("")
}
code, ok := proto.Err2CodeMap[err]
if ok {
return &proto.HTTPReply{Code: code, Msg: err.Error()}
}
return &proto.HTTPReply{Code: proto.ErrCodeInternalError, Msg: err.Error()}
}
func sendOkReply(w http.ResponseWriter, r *http.Request, httpReply *proto.HTTPReply) (err error) {
switch httpReply.Data.(type) {
case *DataPartition:
dp := httpReply.Data.(*DataPartition)
dp.RLock()
defer dp.RUnlock()
case *MetaPartition:
mp := httpReply.Data.(*MetaPartition)
mp.RLock()
defer mp.RUnlock()
case *MetaNode:
mn := httpReply.Data.(*MetaNode)
mn.RLock()
defer mn.RUnlock()
case *DataNode:
dn := httpReply.Data.(*DataNode)
dn.RLock()
defer dn.RUnlock()
}
reply, err := json.Marshal(httpReply)
if err != nil {
log.LogErrorf("fail to marshal http reply[%v]. URL[%v],remoteAddr[%v] err:[%v]", httpReply, r.URL, r.RemoteAddr, err)
http.Error(w, "fail to marshal http reply", http.StatusBadRequest)
return
}
send(w, r, reply)
return
}
func send(w http.ResponseWriter, r *http.Request, reply []byte) {
w.Header().Set("content-type", "application/json")
w.Header().Set("Content-Length", strconv.Itoa(len(reply)))
if _, err := w.Write(reply); err != nil {
log.LogErrorf("fail to write http reply[%s] len[%d].URL[%v],remoteAddr[%v] err:[%v]", string(reply), len(reply), r.URL, r.RemoteAddr, err)
return
}
log.LogInfof("URL[%v],remoteAddr[%v],response ok", r.URL, r.RemoteAddr)
return
}
func sendErrReply(w http.ResponseWriter, r *http.Request, httpReply *proto.HTTPReply) {
log.LogInfof("URL[%v],remoteAddr[%v],response err[%v]", r.URL, r.RemoteAddr, httpReply)
reply, err := json.Marshal(httpReply)
if err != nil {
log.LogErrorf("fail to marshal http reply[%v]. URL[%v],remoteAddr[%v] err:[%v]", httpReply, r.URL, r.RemoteAddr, err)
http.Error(w, "fail to marshal http reply", http.StatusBadRequest)
return
}
w.Header().Set("content-type", "application/json")
w.Header().Set("Content-Length", strconv.Itoa(len(reply)))
if _, err = w.Write(reply); err != nil {
log.LogErrorf("fail to write http reply[%s] len[%d].URL[%v],remoteAddr[%v] err:[%v]", string(reply), len(reply), r.URL, r.RemoteAddr, err)
}
return
}
func (m *Server) getMetaPartitions(w http.ResponseWriter, r *http.Request) {
var (
name string
vol *Vol
err error
)
if name, err = parseAndExtractName(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if vol, err = m.cluster.getVol(name); err != nil {
sendErrReply(w, r, newErrHTTPReply(proto.ErrVolNotExists))
return
}
mpsCache := vol.getMpsCache()
if len(mpsCache) == 0 {
vol.updateViewCache(m.cluster)
mpsCache = vol.getMpsCache()
}
send(w, r, mpsCache)
return
}
// Obtain all the data partitions in a volume.
func (m *Server) getDataPartitions(w http.ResponseWriter, r *http.Request) {
var (
body []byte
name string
vol *Vol
err error
)
if name, err = parseAndExtractName(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
log.LogInfof("action[getDataPartitions] tmp is leader[%v]", m.cluster.partition.IsRaftLeader())
if !m.cluster.partition.IsRaftLeader() {
var ok bool
m.cluster.followerReadManager.rwMutex.RLock()
if body, ok = m.cluster.followerReadManager.volDataPartitionsView[name]; !ok {
m.cluster.followerReadManager.rwMutex.RUnlock()
log.LogErrorf("action[getDataPartitions] volume [%v] not get partitions info", name)
sendErrReply(w, r, newErrHTTPReply(fmt.Errorf("follower volume info not found")))
return
}
m.cluster.followerReadManager.rwMutex.RUnlock()
send(w, r, body)
return
}
if vol, err = m.cluster.getVol(name); err != nil {
sendErrReply(w, r, newErrHTTPReply(proto.ErrVolNotExists))
return
}
if body, err = vol.getDataPartitionsView(); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
send(w, r, body)
}
func (m *Server) getVol(w http.ResponseWriter, r *http.Request) {
var (
err error
vol *Vol
message string
jobj proto.APIAccessReq
ticket cryptoutil.Ticket
ts int64
param *getVolParameter
)
if param, err = parseGetVolParameter(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if vol, err = m.cluster.getVol(param.name); err != nil {
sendErrReply(w, r, newErrHTTPReply(proto.ErrVolNotExists))
return
}
if !param.skipOwnerValidation && !matchKey(vol.Owner, param.authKey) {
sendErrReply(w, r, newErrHTTPReply(proto.ErrVolAuthKeyNotMatch))
return
}
viewCache := vol.getViewCache()
if len(viewCache) == 0 {
vol.updateViewCache(m.cluster)
viewCache = vol.getViewCache()
}
if !param.skipOwnerValidation && vol.authenticate {
if jobj, ticket, ts, err = parseAndCheckTicket(r, m.cluster.MasterSecretKey, param.name); err != nil {
if err == proto.ErrExpiredTicket {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeInvalidTicket, Msg: err.Error()})
return
}
if message, err = genRespMessage(viewCache, &jobj, ts, ticket.SessionKey.Key); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeMasterAPIGenRespError, Msg: err.Error()})
return
}
sendOkReply(w, r, newSuccessHTTPReply(message))
} else {
send(w, r, viewCache)
}
}
// Obtain the volume information such as total capacity and used space, etc.
func (m *Server) getVolStatInfo(w http.ResponseWriter, r *http.Request) {
var (
err error
name string
vol *Vol
)
if name, err = parseAndExtractName(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if vol, err = m.cluster.getVol(name); err != nil {
sendErrReply(w, r, newErrHTTPReply(proto.ErrVolNotExists))
return
}
sendOkReply(w, r, newSuccessHTTPReply(volStat(vol)))
}
func volStat(vol *Vol) (stat *proto.VolStatInfo) {
stat = new(proto.VolStatInfo)
stat.Name = vol.Name
stat.TotalSize = vol.Capacity * util.GB
stat.UsedSize = vol.totalUsedSpace()
if stat.UsedSize > stat.TotalSize {
stat.UsedSize = stat.TotalSize
}
stat.UsedRatio = strconv.FormatFloat(float64(stat.UsedSize)/float64(stat.TotalSize), 'f', 2, 32)
for _, mp := range vol.MetaPartitions {
stat.InodeCount += mp.InodeCount
}
log.LogDebugf("total[%v],usedSize[%v]", stat.TotalSize, stat.UsedSize)
return
}
func getMetaPartitionView(mp *MetaPartition) (mpView *proto.MetaPartitionView) {
mpView = proto.NewMetaPartitionView(mp.PartitionID, mp.Start, mp.End, mp.Status)
mp.Lock()
defer mp.Unlock()
for _, host := range mp.Hosts {
mpView.Members = append(mpView.Members, host)
}
mr, err := mp.getMetaReplicaLeader()
if err != nil {
return
}
mpView.LeaderAddr = mr.Addr
mpView.MaxInodeID = mp.MaxInodeID
mpView.InodeCount = mp.InodeCount
mpView.DentryCount = mp.DentryCount
mpView.IsRecover = mp.IsRecover
return
}
func (m *Server) getMetaPartition(w http.ResponseWriter, r *http.Request) {
var (
err error
partitionID uint64
mp *MetaPartition
)
if partitionID, err = parseAndExtractPartitionInfo(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
if mp, err = m.cluster.getMetaPartitionByID(partitionID); err != nil {
sendErrReply(w, r, newErrHTTPReply(proto.ErrMetaPartitionNotExists))
return
}
var toInfo = func(mp *MetaPartition) *proto.MetaPartitionInfo {
mp.RLock()
defer mp.RUnlock()
var replicas = make([]*proto.MetaReplicaInfo, len(mp.Replicas))
zones := make([]string, len(mp.Hosts))
for idx, host := range mp.Hosts {
metaNode, err := m.cluster.metaNode(host)
if err == nil {
zones[idx] = metaNode.ZoneName
}
}
for i := 0; i < len(replicas); i++ {
replicas[i] = &proto.MetaReplicaInfo{
Addr: mp.Replicas[i].Addr,
ReportTime: mp.Replicas[i].ReportTime,
Status: mp.Replicas[i].Status,
IsLeader: mp.Replicas[i].IsLeader,
}
}
var mpInfo = &proto.MetaPartitionInfo{
PartitionID: mp.PartitionID,
Start: mp.Start,
End: mp.End,
VolName: mp.volName,
MaxInodeID: mp.MaxInodeID,
InodeCount: mp.InodeCount,
DentryCount: mp.DentryCount,
Replicas: replicas,
ReplicaNum: mp.ReplicaNum,
Status: mp.Status,
IsRecover: mp.IsRecover,
Hosts: mp.Hosts,
Peers: mp.Peers,
Zones: zones,
MissNodes: mp.MissNodes,
OfflinePeerID: mp.OfflinePeerID,
LoadResponse: mp.LoadResponse,
}
return mpInfo
}
sendOkReply(w, r, newSuccessHTTPReply(toInfo(mp)))
}
func (m *Server) listVols(w http.ResponseWriter, r *http.Request) {
var (
err error
keywords string
vol *Vol
volsInfo []*proto.VolInfo
)
if keywords, err = parseKeywords(r); err != nil {
sendErrReply(w, r, &proto.HTTPReply{Code: proto.ErrCodeParamError, Msg: err.Error()})
return
}
volsInfo = make([]*proto.VolInfo, 0)
for _, name := range m.cluster.allVolNames() {
if strings.Contains(name, keywords) {
if vol, err = m.cluster.getVol(name); err != nil {
sendErrReply(w, r, newErrHTTPReply(proto.ErrVolNotExists))
return
}
stat := volStat(vol)
volInfo := proto.NewVolInfo(vol.Name, vol.Owner, vol.createTime, vol.status(), stat.TotalSize, stat.UsedSize)
volsInfo = append(volsInfo, volInfo)
}
}
sendOkReply(w, r, newSuccessHTTPReply(volsInfo))
}
func parseAndExtractPartitionInfo(r *http.Request) (partitionID uint64, err error) {
if err = r.ParseForm(); err != nil {
return
}
if partitionID, err = extractMetaPartitionID(r); err != nil {
return
}
return
}
func extractMetaPartitionID(r *http.Request) (partitionID uint64, err error) {
var value string
if value = r.FormValue(idKey); value == "" {
err = keyNotFound(idKey)
return
}
return strconv.ParseUint(value, 10, 64)
}
func extractCapacity(r *http.Request) (capacity int, err error) {
var capacityStr string
if capacityStr = r.FormValue(volCapacityKey); capacityStr == "" {
err = keyNotFound(volCapacityKey)
return
}
if capacity, err = strconv.Atoi(capacityStr); err != nil {
err = unmatchedKey(volCapacityKey)
}
return
}
func extractAuthKey(r *http.Request) (authKey string, err error) {
if authKey = r.FormValue(volAuthKey); authKey == "" {
err = keyNotFound(volAuthKey)
return
}
return
}
func parseAndExtractName(r *http.Request) (name string, err error) {
if err = r.ParseForm(); err != nil {
return
}
return extractName(r)
}
func extractName(r *http.Request) (name string, err error) {
if name = r.FormValue(nameKey); name == "" {
err = keyNotFound(nameKey)
return
}
if !volNameRegexp.MatchString(name) {
return "", errors.New("name can only be number and letters")
}
return
}
func extractOwner(r *http.Request) (owner string, err error) {
if owner = r.FormValue(volOwnerKey); owner == "" {
err = keyNotFound(volOwnerKey)
return
}
if !ownerRegexp.MatchString(owner) {
return "", errors.New("owner can only be number and letters")
}
return
}
func parseAndCheckTicket(r *http.Request, key []byte, volName string) (jobj proto.APIAccessReq, ticket cryptoutil.Ticket, ts int64, err error) {
var (
plaintext []byte
)
if err = r.ParseForm(); err != nil {
return
}
if plaintext, err = extractClientReqInfo(r); err != nil {
return
}
if err = json.Unmarshal([]byte(plaintext), &jobj); err != nil {
return
}
if err = proto.VerifyAPIAccessReqIDs(&jobj); err != nil {
return
}
ticket, ts, err = extractTicketMess(&jobj, key, volName)
return
}
func extractClientReqInfo(r *http.Request) (plaintext []byte, err error) {
var (
message string
)
if err = r.ParseForm(); err != nil {
return
}
if message = r.FormValue(proto.ClientMessage); message == "" {
err = keyNotFound(proto.ClientMessage)
return
}
if plaintext, err = cryptoutil.Base64Decode(message); err != nil {
return
}
return
}
func extractTicketMess(req *proto.APIAccessReq, key []byte, volName string) (ticket cryptoutil.Ticket, ts int64, err error) {
if ticket, err = proto.ExtractTicket(req.Ticket, key); err != nil {
err = fmt.Errorf("extractTicket failed: %s", err.Error())
return
}
if time.Now().Unix() >= ticket.Exp {
err = proto.ErrExpiredTicket
return
}
if ts, err = proto.ParseVerifier(req.Verifier, ticket.SessionKey.Key); err != nil {
err = fmt.Errorf("parseVerifier failed: %s", err.Error())
return
}
if err = proto.CheckAPIAccessCaps(&ticket, proto.APIRsc, req.Type, proto.APIAccess); err != nil {
err = fmt.Errorf("CheckAPIAccessCaps failed: %s", err.Error())
return
}
if err = proto.CheckVOLAccessCaps(&ticket, volName, proto.VOLAccess, proto.MasterNode); err != nil {
err = fmt.Errorf("CheckVOLAccessCaps failed: %s", err.Error())
return
}
return
}
func genRespMessage(data []byte, req *proto.APIAccessReq, ts int64, key []byte) (message string, err error) {
var (
jresp []byte
resp proto.MasterAPIAccessResp
)
resp.Data = data
resp.APIResp.Type = req.Type + 1
resp.APIResp.ClientID = req.ClientID
resp.APIResp.ServiceID = req.ServiceID
resp.APIResp.Verifier = ts + 1 // increase ts by one for client verify server
if jresp, err = json.Marshal(resp); err != nil {
err = fmt.Errorf("json marshal for response failed %s", err.Error())
return
}
if message, err = cryptoutil.EncodeMessage(jresp, key); err != nil {
err = fmt.Errorf("encdoe message for response failed %s", err.Error())
return
}
return
}
func (m *Server) associateVolWithUser(userID, volName string) error {
var err error
var userInfo *proto.UserInfo
if userInfo, err = m.user.getUserInfo(userID); err != nil && err != proto.ErrUserNotExists {
return err
}
if err == proto.ErrUserNotExists {
var param = proto.UserCreateParam{
ID: userID,
Password: DefaultUserPassword,
Type: proto.UserTypeNormal,
}
if userInfo, err = m.user.createKey(&param); err != nil {
return err
}
}
if _, err = m.user.addOwnVol(userInfo.UserID, volName); err != nil {
return err
}
return nil
}