209 lines
4.9 KiB
Go
209 lines
4.9 KiB
Go
|
package crushmap
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/binary"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
Magic = uint32(0x00010000)
|
||
|
)
|
||
|
|
||
|
type CrushRuleOpType uint32
|
||
|
|
||
|
const (
|
||
|
CrushRuleNoop CrushRuleOpType = iota
|
||
|
CrushRuleTake
|
||
|
CrushRuleChooseFirstN
|
||
|
CrushRuleChooseIndep
|
||
|
CrushRuleEmit
|
||
|
CrushRuleChooseleafFirstN
|
||
|
CrushRuleChooseleafIndep
|
||
|
|
||
|
CrushRuleSetChooseTries
|
||
|
CrushRuleSetChooseleafTries
|
||
|
CrushRuleSetChooseLocalTries
|
||
|
CrushRuleSetChooseLocalFallbackTries
|
||
|
CrushRuleSetChooseleafVaryR
|
||
|
CrushRuleSetChooseleafStable
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
crushRuleOpTypeStringMap = map[CrushRuleOpType]string{
|
||
|
CrushRuleNoop: "noop",
|
||
|
CrushRuleTake: "take",
|
||
|
CrushRuleChooseFirstN: "choose firstn",
|
||
|
CrushRuleChooseIndep: "choose indep",
|
||
|
CrushRuleEmit: "emit",
|
||
|
CrushRuleChooseleafFirstN: "choose_leaf firstn",
|
||
|
CrushRuleChooseleafIndep: "choose_leaf indep",
|
||
|
CrushRuleSetChooseTries: "set_choose_tries",
|
||
|
CrushRuleSetChooseleafTries: "set_chooseleaf_tries",
|
||
|
CrushRuleSetChooseLocalTries: "set_choose_local_tries",
|
||
|
CrushRuleSetChooseLocalFallbackTries: "set_choose_local_fallback_tries",
|
||
|
CrushRuleSetChooseleafVaryR: "set_choose_leaf_vary_r",
|
||
|
CrushRuleSetChooseleafStable: "set_choose_leaf_stable",
|
||
|
}
|
||
|
crushRuleOpStringTypeMap = map[string]CrushRuleOpType{
|
||
|
"noop": CrushRuleNoop,
|
||
|
"take": CrushRuleTake,
|
||
|
"choose firstn": CrushRuleChooseFirstN,
|
||
|
"choose indep": CrushRuleChooseIndep,
|
||
|
"emit": CrushRuleEmit,
|
||
|
"choose_leaf firstn": CrushRuleChooseleafFirstN,
|
||
|
"choose_leaf indep": CrushRuleChooseleafIndep,
|
||
|
"set choose_tries": CrushRuleSetChooseTries,
|
||
|
"set chooseleaf_tries": CrushRuleSetChooseleafTries,
|
||
|
"set choose_local_tries": CrushRuleSetChooseLocalTries,
|
||
|
"set choose_local_fallback_tries": CrushRuleSetChooseLocalFallbackTries,
|
||
|
"set choose_leaf_vary_r": CrushRuleSetChooseleafVaryR,
|
||
|
"set choose_leaf_stable": CrushRuleSetChooseleafStable,
|
||
|
}
|
||
|
)
|
||
|
|
||
|
func (t CrushRuleOpType) String() string {
|
||
|
op, ok := crushRuleOpTypeStringMap[t]
|
||
|
if !ok {
|
||
|
op = "invalid"
|
||
|
}
|
||
|
return op
|
||
|
}
|
||
|
|
||
|
type CrushRuleStep struct {
|
||
|
Op CrushRuleOpType
|
||
|
Arg1 int32
|
||
|
Arg2 int32
|
||
|
}
|
||
|
|
||
|
type binaryParser struct {
|
||
|
r io.Reader
|
||
|
w io.Writer
|
||
|
}
|
||
|
|
||
|
func (cmap *Map) DecodeBinary(data []byte) error {
|
||
|
var err error
|
||
|
|
||
|
var magic uint32
|
||
|
p := &binaryParser{r: bytes.NewBuffer(data)}
|
||
|
|
||
|
err = binary.Read(p.r, binary.LittleEndian, &magic)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
} else if magic != Magic {
|
||
|
return fmt.Errorf("invalid magic: %0x != %0x", magic, Magic)
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
maxBuckets int32
|
||
|
maxRules uint32
|
||
|
maxDevices int32
|
||
|
)
|
||
|
|
||
|
err = binary.Read(p.r, binary.LittleEndian, &maxBuckets)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
err = binary.Read(p.r, binary.LittleEndian, &maxRules)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
err = binary.Read(p.r, binary.LittleEndian, &maxDevices)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
for i := int32(0); i < maxBuckets; i++ {
|
||
|
ibucket, err := p.handleBucket()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if ibucket == nil {
|
||
|
continue
|
||
|
}
|
||
|
cmap.Buckets = append(cmap.Buckets, ibucket)
|
||
|
}
|
||
|
|
||
|
for i := uint32(0); i < maxRules; i++ {
|
||
|
irule, err := p.handleRule()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
cmap.Rules = append(cmap.Rules, irule)
|
||
|
}
|
||
|
|
||
|
itypes, err := p.handleType()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
cmap.Types = itypes
|
||
|
|
||
|
btypes := make(map[int32]string, len(itypes))
|
||
|
for _, t := range itypes {
|
||
|
btypes[t.ID] = t.Name
|
||
|
}
|
||
|
|
||
|
bnames := make(map[int32]string)
|
||
|
itypes, err = p.handleType()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
for _, t := range itypes {
|
||
|
bnames[t.ID] = t.Name
|
||
|
}
|
||
|
|
||
|
rnames := make(map[int32]string)
|
||
|
itypes, err = p.handleType()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
for _, t := range itypes {
|
||
|
rnames[t.ID] = t.Name
|
||
|
}
|
||
|
|
||
|
var ok bool
|
||
|
for _, bucket := range cmap.Buckets {
|
||
|
if bucket != nil {
|
||
|
if bucket.TypeName, ok = btypes[int32(bucket.TypeID)]; !ok {
|
||
|
return fmt.Errorf("unknown type id: %d", bucket.TypeID)
|
||
|
}
|
||
|
for _, item := range bucket.Items {
|
||
|
if item.Name, ok = bnames[int32(bucket.ID)]; !ok {
|
||
|
return fmt.Errorf("unknown type id: %d", bucket.ID)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
itypes, err = p.handleType()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
for _, rule := range cmap.Rules {
|
||
|
if rule.Name, ok = rnames[int32(rule.Ruleset)]; !ok {
|
||
|
return fmt.Errorf("unknown type id: %d", rule.ID)
|
||
|
}
|
||
|
for _, step := range rule.Steps {
|
||
|
switch step.Op {
|
||
|
default:
|
||
|
case CrushRuleChooseFirstN.String(), CrushRuleChooseIndep.String(), CrushRuleChooseleafFirstN.String(), CrushRuleChooseleafIndep.String():
|
||
|
if step.ItemType, ok = btypes[step.ItemTypeID]; !ok {
|
||
|
return fmt.Errorf("unknown type id: %d", step.ItemTypeID)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
itunables, err := p.handleTunable()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
cmap.Tunables = itunables
|
||
|
|
||
|
cmap.rulesSort()
|
||
|
cmap.bucketsSort()
|
||
|
return nil
|
||
|
}
|