crush_rule.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. /*
  2. Copyright 2020 The Rook Authors. All rights reserved.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package client
  14. import (
  15. "encoding/json"
  16. "fmt"
  17. "github.com/pkg/errors"
  18. cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1"
  19. "github.com/rook/rook/pkg/clusterd"
  20. )
  21. const (
  22. crushReplicatedType = 1
  23. ruleMinSizeDefault = 1
  24. ruleMaxSizeDefault = 10
  25. twoStepCRUSHRuleTemplate = `
  26. rule %s {
  27. id %d
  28. type replicated
  29. min_size %d
  30. max_size %d
  31. step take %s %s
  32. step choose firstn 0 type %s
  33. step chooseleaf firstn 2 type %s
  34. step emit
  35. }
  36. `
  37. twoStepHybridCRUSHRuleTemplate = `
  38. rule %s {
  39. id %d
  40. type replicated
  41. min_size %d
  42. max_size %d
  43. step take %s class %s
  44. step chooseleaf firstn 1 type %s
  45. step emit
  46. step take %s class %s
  47. step chooseleaf firstn 0 type %s
  48. step emit
  49. }
  50. `
  51. )
  52. var (
  53. stepEmit = &stepSpec{Operation: "emit"}
  54. )
  55. func buildTwoStepPlainCrushRule(crushMap CrushMap, ruleName string, pool cephv1.PoolSpec) string {
  56. var crushRuleInsert string
  57. if pool.DeviceClass != "" {
  58. crushRuleInsert = fmt.Sprintf("class %s", pool.DeviceClass)
  59. }
  60. return fmt.Sprintf(
  61. twoStepCRUSHRuleTemplate,
  62. ruleName,
  63. generateRuleID(crushMap.Rules),
  64. ruleMinSizeDefault,
  65. ruleMaxSizeDefault,
  66. pool.CrushRoot,
  67. crushRuleInsert,
  68. pool.FailureDomain,
  69. pool.Replicated.SubFailureDomain,
  70. )
  71. }
  72. func buildTwoStepHybridCrushRule(crushMap CrushMap, ruleName string, pool cephv1.PoolSpec) string {
  73. primaryOSDDeviceClass := pool.Replicated.HybridStorage.PrimaryDeviceClass
  74. secondaryOSDsDeviceClass := pool.Replicated.HybridStorage.SecondaryDeviceClass
  75. return fmt.Sprintf(
  76. twoStepHybridCRUSHRuleTemplate,
  77. ruleName,
  78. generateRuleID(crushMap.Rules),
  79. ruleMinSizeDefault,
  80. ruleMaxSizeDefault,
  81. pool.CrushRoot,
  82. primaryOSDDeviceClass,
  83. pool.FailureDomain,
  84. pool.CrushRoot,
  85. secondaryOSDsDeviceClass,
  86. pool.FailureDomain,
  87. )
  88. }
  89. func buildTwoStepCrushRule(crushMap CrushMap, ruleName string, pool cephv1.PoolSpec) *ruleSpec {
  90. /*
  91. The complete CRUSH rule looks like this:
  92. rule two_rep_per_dc {
  93. id 1
  94. type replicated
  95. min_size 1
  96. max_size 10
  97. step take root
  98. step choose firstn 0 type datacenter
  99. step chooseleaf firstn 2 type host
  100. step emit
  101. }
  102. */
  103. ruleID := generateRuleID(crushMap.Rules)
  104. return &ruleSpec{
  105. ID: ruleID,
  106. Name: ruleName,
  107. Ruleset: ruleID,
  108. Type: crushReplicatedType,
  109. MinSize: ruleMinSizeDefault,
  110. MaxSize: ruleMaxSizeDefault,
  111. Steps: buildTwoStepCrushSteps(pool),
  112. }
  113. }
  114. func buildTwoStepCrushSteps(pool cephv1.PoolSpec) []stepSpec {
  115. // Create CRUSH rule steps
  116. steps := []stepSpec{}
  117. // Create the default step, which is essentially the entrypoint, the "root" of all requests
  118. stepTakeDefault := &stepSpec{
  119. Operation: "take",
  120. Item: -1,
  121. ItemName: pool.CrushRoot,
  122. }
  123. steps = append(steps, *stepTakeDefault)
  124. // Steps two
  125. stepTakeFailureDomain := &stepSpec{
  126. Operation: "chooseleaf_firstn",
  127. Number: 0,
  128. Type: pool.FailureDomain,
  129. }
  130. steps = append(steps, *stepTakeFailureDomain)
  131. // Step three
  132. stepTakeSubFailureDomain := &stepSpec{
  133. Operation: "chooseleaf_firstn",
  134. Number: pool.Replicated.ReplicasPerFailureDomain,
  135. Type: pool.Replicated.SubFailureDomain,
  136. }
  137. steps = append(steps, *stepTakeSubFailureDomain)
  138. steps = append(steps, *stepEmit)
  139. return steps
  140. }
  141. func generateRuleID(rules []ruleSpec) int {
  142. newRulesID := rules[len(rules)-1].ID + 1
  143. for {
  144. ruleIDExists := checkIfRuleIDExists(rules, newRulesID)
  145. if !ruleIDExists {
  146. break
  147. } else {
  148. newRulesID++
  149. }
  150. }
  151. return newRulesID
  152. }
  153. func checkIfRuleIDExists(rules []ruleSpec, ID int) bool {
  154. for _, rule := range rules {
  155. if rule.ID == ID {
  156. return true
  157. }
  158. }
  159. return false
  160. }
  161. func getCrushRule(context *clusterd.Context, clusterInfo *ClusterInfo, name string) (ruleSpec, error) {
  162. var rule ruleSpec
  163. args := []string{"osd", "crush", "rule", "dump", name}
  164. buf, err := NewCephCommand(context, clusterInfo, args).Run()
  165. if err != nil {
  166. return rule, errors.Wrapf(err, "failed to get crush rule %q. %s", name, string(buf))
  167. }
  168. err = json.Unmarshal(buf, &rule)
  169. if err != nil {
  170. return rule, errors.Wrapf(err, "failed to unmarshal crush rule. %s", string(buf))
  171. }
  172. return rule, nil
  173. }