host_image_service.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  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 hostimage
  15. import (
  16. "context"
  17. "fmt"
  18. "net/http"
  19. "os"
  20. "path"
  21. execlient "yunion.io/x/executor/client"
  22. "yunion.io/x/jsonutils"
  23. "yunion.io/x/log"
  24. "yunion.io/x/onecloud/pkg/appsrv"
  25. app_common "yunion.io/x/onecloud/pkg/cloudcommon/app"
  26. "yunion.io/x/onecloud/pkg/cloudcommon/consts"
  27. common_options "yunion.io/x/onecloud/pkg/cloudcommon/options"
  28. "yunion.io/x/onecloud/pkg/httperrors"
  29. "yunion.io/x/onecloud/pkg/mcclient/auth"
  30. "yunion.io/x/onecloud/pkg/util/procutils"
  31. "yunion.io/x/onecloud/pkg/util/qemuimg"
  32. "yunion.io/x/onecloud/pkg/util/seclib2"
  33. )
  34. type SHostImageOptions struct {
  35. common_options.HostCommonOptions
  36. LocalImagePath []string `help:"Local Image Paths"`
  37. LVMVolumeGroups []string `help:"LVM Volume Groups(vgs)"`
  38. SnapshotDirSuffix string `help:"Snapshot dir name equal diskId concat snapshot dir suffix" default:"_snap"`
  39. HostImageNbdPidDir string `help:"Host-image nbd pid files dir " default:"/var/run/onecloud/host-image"`
  40. CommonConfigFile string `help:"common config file for container"`
  41. }
  42. var (
  43. HostImageOptions SHostImageOptions
  44. nbdExportManager *SNbdExportManager
  45. )
  46. func StartService() {
  47. consts.SetServiceType("host-image")
  48. common_options.ParseOptions(&HostImageOptions, os.Args, "host.conf", "host-image")
  49. if len(HostImageOptions.CommonConfigFile) > 0 {
  50. baseOpt := HostImageOptions.BaseOptions.BaseOptions
  51. commonCfg := new(common_options.CommonOptions)
  52. commonCfg.Config = HostImageOptions.CommonConfigFile
  53. common_options.ParseOptions(commonCfg, []string{"host"}, "common.conf", "host")
  54. HostImageOptions.CommonOptions = *commonCfg
  55. HostImageOptions.BaseOptions.BaseOptions = baseOpt
  56. }
  57. log.Infof("exec socket path: %s", HostImageOptions.ExecutorSocketPath)
  58. if HostImageOptions.EnableRemoteExecutor {
  59. execlient.Init(HostImageOptions.ExecutorSocketPath)
  60. execlient.SetTimeoutSeconds(HostImageOptions.ExecutorConnectTimeoutSeconds)
  61. procutils.SetRemoteExecutor()
  62. }
  63. nbdExportManager = NewNbdExportManager()
  64. output, err := procutils.NewCommand("mkdir", "-p", HostImageOptions.HostImageNbdPidDir).Output()
  65. if err != nil {
  66. log.Fatalf("failed to create path %s: %s %s", HostImageOptions.HostImageNbdPidDir, output, err)
  67. }
  68. HostImageOptions.EnableSsl = false
  69. HostImageOptions.Port += 40000
  70. app_common.InitAuth(&HostImageOptions.CommonOptions, func() {
  71. log.Infof("Auth complete!!")
  72. })
  73. app := app_common.InitApp(&HostImageOptions.BaseOptions, false)
  74. initHandlers(app, "")
  75. app_common.ServeForever(app, &HostImageOptions.BaseOptions)
  76. }
  77. func initHandlers(app *appsrv.Application, prefix string) {
  78. app.AddHandler("POST", fmt.Sprintf("%s/disks/<sid>/nbd-export", prefix), auth.Authenticate(imageNbdExport))
  79. app.AddHandler("POST", fmt.Sprintf("%s/snapshots/<diskId>/<sid>/nbd-export", prefix), auth.Authenticate(imageNbdExport))
  80. app.AddHandler("POST", fmt.Sprintf("%s/disks/<sid>/nbd-close", prefix), auth.Authenticate(imageNbdClose))
  81. app.AddHandler("POST", fmt.Sprintf("%s/snapshots/<diskId>/<sid>/nbd-close", prefix), auth.Authenticate(imageNbdClose))
  82. }
  83. func getDiskPath(diskId string) string {
  84. for _, imagePath := range HostImageOptions.LocalImagePath {
  85. diskPath := path.Join(imagePath, diskId)
  86. if _, err := procutils.RemoteStat(diskPath); err == nil {
  87. return diskPath
  88. }
  89. }
  90. for _, vg := range HostImageOptions.LVMVolumeGroups {
  91. diskPath := path.Join("/dev", vg, diskId)
  92. if _, err := procutils.RemoteStat(diskPath); err == nil {
  93. return diskPath
  94. }
  95. }
  96. return ""
  97. }
  98. func getSnapshotPath(diskId, snapshotId string) string {
  99. for _, imagePath := range HostImageOptions.LocalImagePath {
  100. diskPath := path.Join(imagePath, "snapshots",
  101. diskId+HostImageOptions.SnapshotDirSuffix, snapshotId)
  102. if _, err := procutils.RemoteStat(diskPath); err == nil {
  103. return diskPath
  104. }
  105. }
  106. for _, vg := range HostImageOptions.LVMVolumeGroups {
  107. diskPath := path.Join("/dev", vg, "snap_"+snapshotId)
  108. if _, err := procutils.RemoteStat(diskPath); err == nil {
  109. return diskPath
  110. }
  111. }
  112. return ""
  113. }
  114. func inputCheck(ctx context.Context, w http.ResponseWriter, r *http.Request) (string, string, error) {
  115. params, _, body := appsrv.FetchEnv(ctx, w, r)
  116. var sid = params["<sid>"]
  117. var imagePath string
  118. var remoteDiskId string
  119. remoteDiskId, _ = body.GetString("disk_id")
  120. if remoteDiskId == "" {
  121. return "", "", httperrors.NewMissingParameterError("disk_id")
  122. }
  123. if diskId, ok := params["<diskId>"]; ok {
  124. imagePath = getSnapshotPath(diskId, sid)
  125. } else {
  126. imagePath = getDiskPath(sid)
  127. }
  128. if len(imagePath) == 0 {
  129. return "", "", httperrors.NewNotFoundError("Disk not found")
  130. }
  131. return imagePath, remoteDiskId, nil
  132. }
  133. func imageNbdExport(ctx context.Context, w http.ResponseWriter, r *http.Request) {
  134. imagePath, targetDiskId, err := inputCheck(ctx, w, r)
  135. if err != nil {
  136. httperrors.GeneralServerError(ctx, w, err)
  137. return
  138. }
  139. imageInfo := qemuimg.SImageInfo{
  140. Path: imagePath,
  141. }
  142. encryptKey := r.Header.Get("X-Encrypt-Key")
  143. if len(encryptKey) > 0 {
  144. imageInfo.Password = encryptKey
  145. imageInfo.EncryptAlg = seclib2.TSymEncAlg(r.Header.Get("X-Encrypt-Alg"))
  146. }
  147. nbdPort, err := nbdExportManager.QemuNbdStartExport(imageInfo, targetDiskId)
  148. if err != nil {
  149. httperrors.GeneralServerError(ctx, w, err)
  150. return
  151. }
  152. log.Infof("Image %s request nbd export with port %d", targetDiskId, nbdPort)
  153. ret := jsonutils.NewDict()
  154. ret.Set("nbd_port", jsonutils.NewInt(int64(nbdPort)))
  155. appsrv.SendJSON(w, ret)
  156. }
  157. func imageNbdClose(ctx context.Context, w http.ResponseWriter, r *http.Request) {
  158. _, targetDiskId, err := inputCheck(ctx, w, r)
  159. if err != nil {
  160. httperrors.GeneralServerError(ctx, w, err)
  161. return
  162. }
  163. err = nbdExportManager.QemuNbdCloseExport(targetDiskId)
  164. if err != nil {
  165. httperrors.GeneralServerError(ctx, w, err)
  166. return
  167. }
  168. log.Infof("Image %s request nbd close export with port", targetDiskId)
  169. appsrv.SendStruct(w, map[string]string{"result": "ok"})
  170. }