rbd.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. // Copyright 2019 Yunion
  2. //
  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. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package rbd
  15. import (
  16. "fmt"
  17. "path/filepath"
  18. "strings"
  19. "yunion.io/x/jsonutils"
  20. "yunion.io/x/log"
  21. "yunion.io/x/pkg/errors"
  22. "yunion.io/x/onecloud/pkg/hostman/container/storage"
  23. "yunion.io/x/onecloud/pkg/util/fileutils2"
  24. "yunion.io/x/onecloud/pkg/util/procutils"
  25. )
  26. func init() {
  27. storage.RegisterDriver(newRbd())
  28. }
  29. type rbd struct{}
  30. func newRbd() *rbd {
  31. return &rbd{}
  32. }
  33. func (r rbd) GetType() storage.StorageType {
  34. return storage.STORAGE_TYPE_RBD
  35. }
  36. func parseRbdPath(diskPath string) (pool, image, confPath, keyringPath string, err error) {
  37. if !strings.HasPrefix(diskPath, "rbd:") {
  38. return "", "", "", "", errors.Errorf("invalid rbd path: %s", diskPath)
  39. }
  40. diskPath = strings.TrimPrefix(diskPath, "rbd:")
  41. parts := strings.SplitN(diskPath, ":", 2)
  42. if len(parts) < 1 || parts[0] == "" {
  43. return "", "", "", "", errors.Errorf("invalid rbd path: missing pool/image")
  44. }
  45. poolImage := parts[0]
  46. slash := strings.Index(poolImage, "/")
  47. if slash <= 0 {
  48. return "", "", "", "", errors.Errorf("invalid rbd path: missing pool/image in %s", poolImage)
  49. }
  50. pool = poolImage[:slash]
  51. image = poolImage[slash+1:]
  52. if pool == "" || image == "" {
  53. return "", "", "", "", errors.Errorf("invalid rbd path: empty pool or image")
  54. }
  55. confPath = ""
  56. if len(parts) == 2 && parts[1] != "" {
  57. confPrefix := "conf="
  58. if strings.HasPrefix(parts[1], confPrefix) {
  59. confPath = strings.TrimPrefix(parts[1], confPrefix)
  60. }
  61. }
  62. if confPath == "" {
  63. return "", "", "", "", errors.Errorf("invalid rbd path: missing conf= in %s", diskPath)
  64. }
  65. keyringPath = filepath.Join(filepath.Dir(confPath), "ceph.keyring")
  66. return pool, image, confPath, keyringPath, nil
  67. }
  68. func imageSpec(pool, image string) string {
  69. return fmt.Sprintf("%s/%s", pool, image)
  70. }
  71. type rbdDeviceInfo struct {
  72. Id int `json:"id"`
  73. Pool string `json:"pool"`
  74. Namespace string `json:"namespace"`
  75. Image string `json:"image"`
  76. Name string `json:"name"`
  77. Snap string `json:"snap"`
  78. Device string `json:"device"`
  79. }
  80. func (r rbd) listMappedDevices(confPath, keyringPath string) (map[string]string, error) {
  81. args := []string{"device", "list", "--format", "json"}
  82. if confPath != "" {
  83. args = append(args, "--conf", confPath)
  84. }
  85. if keyringPath != "" && fileutils2.Exists(keyringPath) {
  86. args = append(args, "--keyring", keyringPath)
  87. }
  88. out, err := procutils.NewRemoteCommandAsFarAsPossible("rbd", args...).Output()
  89. if err != nil {
  90. return nil, errors.Wrapf(err, "rbd device list: %s", string(out))
  91. }
  92. jsonObj, err := jsonutils.Parse(out)
  93. if err != nil {
  94. return nil, errors.Wrapf(err, "parse rbd device list json output: %s", string(out))
  95. }
  96. devices, err := jsonObj.GetArray()
  97. if err != nil {
  98. return nil, errors.Wrapf(err, "get devices array from json: %s", string(out))
  99. }
  100. result := make(map[string]string)
  101. for _, devObj := range devices {
  102. devInfo := rbdDeviceInfo{}
  103. if err := devObj.Unmarshal(&devInfo); err != nil {
  104. log.Warningf("failed to unmarshal device info: %v, skip", err)
  105. continue
  106. }
  107. image := devInfo.Image
  108. if image == "" {
  109. image = devInfo.Name
  110. }
  111. if devInfo.Pool == "" || image == "" || devInfo.Device == "" {
  112. continue
  113. }
  114. spec := imageSpec(devInfo.Pool, image)
  115. result[spec] = devInfo.Device
  116. }
  117. return result, nil
  118. }
  119. func (r rbd) CheckConnect(diskPath string) (string, bool, error) {
  120. pool, image, confPath, keyringPath, err := parseRbdPath(diskPath)
  121. if err != nil {
  122. return "", false, err
  123. }
  124. spec := imageSpec(pool, image)
  125. mapped, err := r.listMappedDevices(confPath, keyringPath)
  126. if err != nil {
  127. return "", false, err
  128. }
  129. dev, ok := mapped[spec]
  130. if !ok {
  131. return "", false, nil
  132. }
  133. devPath := r.checkPartition(dev)
  134. return devPath, true, nil
  135. }
  136. func (r rbd) checkPartition(devName string) string {
  137. // /dev/rbd0 -> /dev/rbd0p1
  138. partPath := devName + "p1"
  139. if fileutils2.Exists(partPath) {
  140. return partPath
  141. }
  142. return devName
  143. }
  144. func (r rbd) ConnectDisk(diskPath string) (string, error) {
  145. pool, image, confPath, keyringPath, err := parseRbdPath(diskPath)
  146. if err != nil {
  147. return "", err
  148. }
  149. spec := imageSpec(pool, image)
  150. args := []string{"device", "map", spec}
  151. if confPath != "" {
  152. args = append(args, "--conf", confPath)
  153. }
  154. if keyringPath != "" && fileutils2.Exists(keyringPath) {
  155. args = append(args, "--keyring", keyringPath)
  156. }
  157. out, err := procutils.NewRemoteCommandAsFarAsPossible("rbd", args...).Output()
  158. if err != nil {
  159. return "", errors.Wrapf(err, "rbd device map %s: %s", spec, string(out))
  160. }
  161. devStr := strings.TrimSpace(string(out))
  162. if devStr == "" {
  163. devPath, _, err := r.CheckConnect(diskPath)
  164. if err != nil || devPath == "" {
  165. return "", errors.Wrapf(err, "rbd map succeeded but device not found for %s", spec)
  166. }
  167. return r.checkPartition(devPath), nil
  168. }
  169. if !strings.HasPrefix(devStr, "/dev/") {
  170. devStr = "/dev/" + devStr
  171. }
  172. devStr = strings.TrimSpace(devStr)
  173. if idx := strings.Index(devStr, "\n"); idx > 0 {
  174. devStr = devStr[:idx]
  175. }
  176. return r.checkPartition(devStr), nil
  177. }
  178. func (r rbd) DisconnectDisk(diskPath string, mountPoint string) error {
  179. pool, image, confPath, keyringPath, err := parseRbdPath(diskPath)
  180. if err != nil {
  181. return err
  182. }
  183. spec := imageSpec(pool, image)
  184. mapped, err := r.listMappedDevices(confPath, keyringPath)
  185. if err != nil {
  186. log.Warningf("rbd device list before unmap: %v", err)
  187. return r.unmapBySpec(spec, confPath, keyringPath)
  188. }
  189. dev, ok := mapped[spec]
  190. if !ok {
  191. log.Infof("rbd image %s not mapped, skip unmap", spec)
  192. return nil
  193. }
  194. args := []string{"device", "unmap", dev}
  195. if confPath != "" {
  196. args = append(args, "--conf", confPath)
  197. }
  198. out, err := procutils.NewRemoteCommandAsFarAsPossible("rbd", args...).Output()
  199. if err != nil {
  200. if strings.Contains(string(out), "not mapped") || strings.Contains(string(out), "No such device") {
  201. return nil
  202. }
  203. return r.unmapBySpec(spec, confPath, keyringPath)
  204. }
  205. log.Infof("rbd device unmap %s (image %s) ok", dev, spec)
  206. return nil
  207. }
  208. func (r rbd) unmapBySpec(spec, confPath, keyringPath string) error {
  209. args := []string{"device", "unmap", spec}
  210. if confPath != "" {
  211. args = append(args, "--conf", confPath)
  212. }
  213. if keyringPath != "" && fileutils2.Exists(keyringPath) {
  214. args = append(args, "--keyring", keyringPath)
  215. }
  216. out, err := procutils.NewRemoteCommandAsFarAsPossible("rbd", args...).Output()
  217. if err != nil {
  218. return errors.Wrapf(err, "rbd device unmap %s: %s", spec, string(out))
  219. }
  220. return nil
  221. }