osd.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. /*
  2. Copyright 2021 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 fake
  14. import (
  15. "fmt"
  16. "strconv"
  17. "strings"
  18. )
  19. // OsdLsOutput returns JSON output from 'ceph osd ls' that can be used for unit tests. It
  20. // returns output for a Ceph cluster with the number of OSDs given as input starting with ID 0.
  21. // example: numOSDs = 5 => return: "[0,1,2,3,4]"
  22. func OsdLsOutput(numOSDs int) string {
  23. stringIDs := make([]string, 0, numOSDs)
  24. for id := 0; id < numOSDs; id++ {
  25. stringIDs = append(stringIDs, strconv.Itoa(id))
  26. }
  27. return fmt.Sprintf("[%s]", strings.Join(stringIDs, ","))
  28. }
  29. // OsdTreeOutput returns JSON output from 'ceph osd tree' that can be used for unit tests.
  30. // It returns output for a Ceph cluster with the given number of nodes and the given number of OSDs
  31. // per node with no complex configuration. This should work even for 0 nodes.
  32. //
  33. // example: OsdTreeOutput(3, 3) // returns JSON output for the Ceph cluster below
  34. // node0: node1: node2:
  35. // - osd0 - osd1 - osd2
  36. // - osd3 - osd4 - osd5
  37. // - osd6 - osd7 - osd8
  38. func OsdTreeOutput(numNodes, numOSDsPerNode int) string {
  39. // JSON output
  40. rootFormat := ` {
  41. "id": -1,
  42. "name": "default",
  43. "type": "root",
  44. "type_id": 11,
  45. "children": [%s]
  46. }` // format: negative node IDs as comma-delimited string (e.g., "-3,-4,-5")
  47. nodeFormat := ` {
  48. "id": %d,
  49. "name": "%s",
  50. "type": "host",
  51. "type_id": 1,
  52. "pool_weights": {},
  53. "children": [%s]
  54. }` // format: negative node ID, node name, OSD IDs as comma-delimited string (e.g., "0,3,6")
  55. osdFormat := ` {
  56. "id": %d,
  57. "device_class": "hdd",
  58. "name": "osd.%d",
  59. "type": "osd",
  60. "type_id": 0,
  61. "crush_weight": 0.009796142578125,
  62. "depth": 2,
  63. "pool_weights": {},
  64. "exists": 1,
  65. "status": "up",
  66. "reweight": 1,
  67. "primary_affinity": 1
  68. }` // format: OSD ID, OSD ID
  69. wrapperFormat := `{
  70. "nodes": [
  71. %s
  72. ],
  73. "stray": []
  74. }` // format: <rendered root JSON, rendered nodes, rendered osds - with commas in between>
  75. nodesJSON := []string{}
  76. osdsJSON := []string{}
  77. nodes := []string{}
  78. for n := 0; n < numNodes; n++ {
  79. osds := []string{}
  80. nodeName := fmt.Sprintf("node%d", n)
  81. nodeID := -3 - n
  82. nodes = append(nodes, strconv.Itoa(nodeID))
  83. for i := 0; i < numOSDsPerNode; i++ {
  84. osdID := n + 3*i
  85. osds = append(osds, strconv.Itoa(osdID))
  86. osdsJSON = append(osdsJSON, fmt.Sprintf(osdFormat, osdID, osdID))
  87. }
  88. nodesJSON = append(nodesJSON, fmt.Sprintf(nodeFormat, nodeID, nodeName, strings.Join(osds, ",")))
  89. }
  90. rootJSON := fmt.Sprintf(rootFormat, strings.Join(nodes, ","))
  91. fullJSON := append(append([]string{rootJSON}, nodesJSON...), osdsJSON...)
  92. rendered := fmt.Sprintf(wrapperFormat, strings.Join(fullJSON, ",\n"))
  93. return rendered
  94. }
  95. // OsdOkToStopOutput returns JSON output from 'ceph osd ok-to-stop' that can be used for unit tests.
  96. // queriedID should be given as the ID sent to the 'osd ok-to-stop <id> [--max=N]' command. It will
  97. // be returned with relevant NOT ok-to-stop results.
  98. // If returnOsdIds is empty, this returns a NOT ok-to-stop result. Otherwise, it returns an
  99. // ok-to-stop result. returnOsdIds should include queriedID if the result should be successful.
  100. func OsdOkToStopOutput(queriedID int, returnOsdIds []int) string {
  101. okTemplate := `{"ok_to_stop":true,"osds":[%s],"num_ok_pgs":132,"num_not_ok_pgs":0,"ok_become_degraded":["1.0","1.2","1.3"]}`
  102. notOkTemplate := `{"ok_to_stop":false,"osds":[%d],"num_ok_pgs":161,"num_not_ok_pgs":50,"bad_become_inactive":["1.0","1.3","1.a"],"ok_become_degraded":["1.2","1.4","1.5"]}`
  103. // NOT ok-to-stop
  104. if len(returnOsdIds) == 0 {
  105. return fmt.Sprintf(notOkTemplate, queriedID)
  106. }
  107. // ok-to-stop
  108. osdIdsStr := make([]string, len(returnOsdIds))
  109. for i := 0; i < len(returnOsdIds); i++ {
  110. osdIdsStr[i] = strconv.Itoa(returnOsdIds[i])
  111. }
  112. return fmt.Sprintf(okTemplate, strings.Join(osdIdsStr, ","))
  113. }
  114. // OSDDeviceClassOutput returns JSON output from 'ceph osd crush get-device-class' that can be used for unit tests.
  115. // osdId is a osd ID to get from crush map. If ID is empty raise a fake error.
  116. func OSDDeviceClassOutput(osdId string) string {
  117. if osdId == "" {
  118. return "ERR: fake error from ceph cli"
  119. }
  120. okTemplate := `[{"osd":%s,"device_class":"hdd"}]`
  121. return fmt.Sprintf(okTemplate, osdId)
  122. }