The ci was using a pretty old version og golangci-lint. This updates to the latest version. Additionally, it silences some gosec integer conversion overflow false positves and fixes some real errors of this category and string format errors found by golangci-lint, while at it. Co-authored-by: Blaine Gardner <b.blaine.gardner@gmail.com> Co-authored-by: Travis Nielsen <tnielsen@redhat.com> Signed-off-by: Michael Adam <obnox@samba.org>
203 lines
4.8 KiB
Go
203 lines
4.8 KiB
Go
/*
|
|
Copyright 2020 The Rook Authors. All rights reserved.
|
|
|
|
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 client
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"github.com/pkg/errors"
|
|
cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1"
|
|
"github.com/rook/rook/pkg/clusterd"
|
|
)
|
|
|
|
const (
|
|
crushReplicatedType = 1
|
|
ruleMinSizeDefault = 1
|
|
ruleMaxSizeDefault = 10
|
|
twoStepCRUSHRuleTemplate = `
|
|
rule %s {
|
|
id %d
|
|
type replicated
|
|
min_size %d
|
|
max_size %d
|
|
step take %s %s
|
|
step choose firstn 0 type %s
|
|
step chooseleaf firstn 2 type %s
|
|
step emit
|
|
}
|
|
`
|
|
twoStepHybridCRUSHRuleTemplate = `
|
|
rule %s {
|
|
id %d
|
|
type replicated
|
|
min_size %d
|
|
max_size %d
|
|
step take %s class %s
|
|
step chooseleaf firstn 1 type %s
|
|
step emit
|
|
step take %s class %s
|
|
step chooseleaf firstn 0 type %s
|
|
step emit
|
|
}
|
|
`
|
|
)
|
|
|
|
var (
|
|
stepEmit = &stepSpec{Operation: "emit"}
|
|
)
|
|
|
|
func buildTwoStepPlainCrushRule(crushMap CrushMap, ruleName string, pool cephv1.PoolSpec) string {
|
|
var crushRuleInsert string
|
|
if pool.DeviceClass != "" {
|
|
crushRuleInsert = fmt.Sprintf("class %s", pool.DeviceClass)
|
|
}
|
|
return fmt.Sprintf(
|
|
twoStepCRUSHRuleTemplate,
|
|
ruleName,
|
|
generateRuleID(crushMap.Rules),
|
|
ruleMinSizeDefault,
|
|
ruleMaxSizeDefault,
|
|
pool.CrushRoot,
|
|
crushRuleInsert,
|
|
pool.FailureDomain,
|
|
pool.Replicated.SubFailureDomain,
|
|
)
|
|
}
|
|
|
|
func buildTwoStepHybridCrushRule(crushMap CrushMap, ruleName string, pool cephv1.PoolSpec) string {
|
|
primaryOSDDeviceClass := pool.Replicated.HybridStorage.PrimaryDeviceClass
|
|
secondaryOSDsDeviceClass := pool.Replicated.HybridStorage.SecondaryDeviceClass
|
|
|
|
return fmt.Sprintf(
|
|
twoStepHybridCRUSHRuleTemplate,
|
|
ruleName,
|
|
generateRuleID(crushMap.Rules),
|
|
ruleMinSizeDefault,
|
|
ruleMaxSizeDefault,
|
|
pool.CrushRoot,
|
|
primaryOSDDeviceClass,
|
|
pool.FailureDomain,
|
|
pool.CrushRoot,
|
|
secondaryOSDsDeviceClass,
|
|
pool.FailureDomain,
|
|
)
|
|
}
|
|
|
|
func buildTwoStepCrushRule(crushMap CrushMap, ruleName string, pool cephv1.PoolSpec) *ruleSpec {
|
|
/*
|
|
The complete CRUSH rule looks like this:
|
|
|
|
rule two_rep_per_dc {
|
|
id 1
|
|
type replicated
|
|
min_size 1
|
|
max_size 10
|
|
step take root
|
|
step choose firstn 0 type datacenter
|
|
step chooseleaf firstn 2 type host
|
|
step emit
|
|
}
|
|
|
|
*/
|
|
|
|
ruleID := generateRuleID(crushMap.Rules)
|
|
return &ruleSpec{
|
|
ID: ruleID,
|
|
Name: ruleName,
|
|
Ruleset: ruleID,
|
|
Type: crushReplicatedType,
|
|
MinSize: ruleMinSizeDefault,
|
|
MaxSize: ruleMaxSizeDefault,
|
|
Steps: buildTwoStepCrushSteps(pool),
|
|
}
|
|
}
|
|
|
|
func buildTwoStepCrushSteps(pool cephv1.PoolSpec) []stepSpec {
|
|
// Create CRUSH rule steps
|
|
steps := []stepSpec{}
|
|
|
|
// Create the default step, which is essentially the entrypoint, the "root" of all requests
|
|
stepTakeDefault := &stepSpec{
|
|
Operation: "take",
|
|
Item: -1,
|
|
ItemName: pool.CrushRoot,
|
|
}
|
|
steps = append(steps, *stepTakeDefault)
|
|
|
|
// Steps two
|
|
stepTakeFailureDomain := &stepSpec{
|
|
Operation: "chooseleaf_firstn",
|
|
Number: 0,
|
|
Type: pool.FailureDomain,
|
|
}
|
|
steps = append(steps, *stepTakeFailureDomain)
|
|
|
|
// Step three
|
|
stepTakeSubFailureDomain := &stepSpec{
|
|
Operation: "chooseleaf_firstn",
|
|
// nolint:gosec // G115 - casting uint to int has no overflow
|
|
Number: int(pool.Replicated.ReplicasPerFailureDomain),
|
|
Type: pool.Replicated.SubFailureDomain,
|
|
}
|
|
steps = append(steps, *stepTakeSubFailureDomain)
|
|
steps = append(steps, *stepEmit)
|
|
|
|
return steps
|
|
}
|
|
|
|
func generateRuleID(rules []ruleSpec) int {
|
|
newRulesID := rules[len(rules)-1].ID + 1
|
|
|
|
for {
|
|
ruleIDExists := checkIfRuleIDExists(rules, newRulesID)
|
|
if !ruleIDExists {
|
|
break
|
|
} else {
|
|
newRulesID++
|
|
}
|
|
}
|
|
|
|
return newRulesID
|
|
}
|
|
|
|
func checkIfRuleIDExists(rules []ruleSpec, ID int) bool {
|
|
for _, rule := range rules {
|
|
if rule.ID == ID {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func getCrushRule(context *clusterd.Context, clusterInfo *ClusterInfo, name string) (ruleSpec, error) {
|
|
var rule ruleSpec
|
|
args := []string{"osd", "crush", "rule", "dump", name}
|
|
buf, err := NewCephCommand(context, clusterInfo, args).Run()
|
|
if err != nil {
|
|
return rule, errors.Wrapf(err, "failed to get crush rule %q. %s", name, string(buf))
|
|
}
|
|
|
|
err = json.Unmarshal(buf, &rule)
|
|
if err != nil {
|
|
return rule, errors.Wrapf(err, "failed to unmarshal crush rule. %s", string(buf))
|
|
}
|
|
|
|
return rule, nil
|
|
}
|