cloudutil.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. package utils
  2. import (
  3. "context"
  4. "strings"
  5. "time"
  6. "yunion.io/x/jsonutils"
  7. "yunion.io/x/log"
  8. "yunion.io/x/pkg/errors"
  9. "yunion.io/x/pkg/util/httputils"
  10. "yunion.io/x/pkg/utils"
  11. computeapi "yunion.io/x/onecloud/pkg/apis/compute"
  12. "yunion.io/x/onecloud/pkg/httperrors"
  13. "yunion.io/x/onecloud/pkg/mcclient/auth"
  14. "yunion.io/x/onecloud/pkg/mcclient/modulebase"
  15. "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
  16. )
  17. type ResourceInfo struct {
  18. Id string
  19. Name string
  20. Status string
  21. }
  22. func NewResourceInfo(id, name, status string) ResourceInfo {
  23. return ResourceInfo{
  24. Id: id,
  25. Name: name,
  26. Status: status,
  27. }
  28. }
  29. func GetResource[R any](ctx context.Context, man modulebase.Manager, id string) (*R, error) {
  30. if len(id) == 0 {
  31. return nil, errors.Wrapf(httperrors.ErrInvalidStatus, "id is empty")
  32. }
  33. s := auth.GetAdminSession(ctx, "")
  34. resp, err := man.GetById(s, id, jsonutils.Marshal(map[string]interface{}{
  35. "scope": "max",
  36. }))
  37. if err != nil {
  38. if httputils.ErrorCode(err) == 404 {
  39. return nil, errors.Wrapf(errors.ErrNotFound, "GetById %s", id)
  40. }
  41. return nil, errors.Wrapf(err, "Get")
  42. }
  43. res := new(R)
  44. if err := resp.Unmarshal(res); err != nil {
  45. return nil, errors.Wrap(err, "Unmarshal")
  46. }
  47. return res, nil
  48. }
  49. func PerformResourceAction[R any](ctx context.Context, man modulebase.Manager, id string, action string) (*R, error) {
  50. s := auth.GetAdminSession(ctx, "")
  51. resp, err := man.PerformAction(s, id, action, nil)
  52. if err != nil {
  53. return nil, errors.Wrapf(err, "PerformAction %s %s %s", man.GetKeyword(), id, action)
  54. }
  55. res := new(R)
  56. if err := resp.Unmarshal(res); err != nil {
  57. return nil, errors.Wrap(err, "Unmarshal")
  58. }
  59. return res, nil
  60. }
  61. func StopResource[R any](ctx context.Context, man modulebase.Manager, id string) (*R, error) {
  62. return PerformResourceAction[R](ctx, man, id, "stop")
  63. }
  64. func StartResource[R any](ctx context.Context, man modulebase.Manager, id string) (*R, error) {
  65. return PerformResourceAction[R](ctx, man, id, "start")
  66. }
  67. func WaitResourceStatus[R any](
  68. ctx context.Context,
  69. man modulebase.Manager,
  70. id string,
  71. getResInfo func(*R) ResourceInfo,
  72. targetStatus []string,
  73. timeoutSecs int,
  74. intervalSecs int) (*R, error) {
  75. expire := time.Now().Add(time.Second * time.Duration(timeoutSecs))
  76. for time.Now().Before(expire) {
  77. res, err := GetResource[R](ctx, man, id)
  78. if err != nil {
  79. return nil, errors.Wrapf(err, "GetResource")
  80. }
  81. resInfo := getResInfo(res)
  82. log.Debugf("Wait %s status %#v target status %v", man.GetKeyword(), resInfo.Status, targetStatus)
  83. if utils.IsInStringArray(resInfo.Status, targetStatus) {
  84. return res, nil
  85. }
  86. if strings.Contains(resInfo.Status, "fail") {
  87. return nil, errors.Wrapf(errors.ErrInvalidStatus, "resource %s status %s", resInfo.Name, resInfo.Status)
  88. }
  89. time.Sleep(time.Second * time.Duration(intervalSecs))
  90. }
  91. return nil, errors.Wrapf(httperrors.ErrTimeout, "wait %s status %s timeout", man.GetKeyword(), targetStatus)
  92. }
  93. func GetContainer(ctx context.Context, id string) (*computeapi.SContainer, error) {
  94. return GetResource[computeapi.SContainer](ctx, &compute.Containers, id)
  95. }
  96. func GetServer(ctx context.Context, id string) (*computeapi.ServerDetails, error) {
  97. return GetResource[computeapi.ServerDetails](ctx, &compute.Servers, id)
  98. }
  99. func WaitContainerStatus(ctx context.Context, id string, targetStatus []string, timeoutSecs int) (*computeapi.SContainer, error) {
  100. return WaitResourceStatus(ctx, &compute.Containers, id, func(ctr *computeapi.SContainer) ResourceInfo {
  101. return NewResourceInfo(ctr.Id, ctr.Name, ctr.Status)
  102. }, targetStatus, timeoutSecs, 1)
  103. }
  104. func WaitServerStatus(ctx context.Context, id string, targetStatus []string, timeoutSecs int) (*computeapi.ServerDetails, error) {
  105. return WaitResourceStatus(ctx, &compute.Servers, id, func(s *computeapi.ServerDetails) ResourceInfo {
  106. return NewResourceInfo(s.Id, s.Name, s.Status)
  107. }, targetStatus, timeoutSecs, 2)
  108. }
  109. func WaitDelete[R any](ctx context.Context, man modulebase.Manager, id string, timeoutSecs int) error {
  110. expire := time.Now().Add(time.Second * time.Duration(timeoutSecs))
  111. for time.Now().Before(expire) {
  112. _, err := GetResource[R](ctx, man, id)
  113. if err != nil {
  114. if errors.Cause(err) == errors.ErrNotFound {
  115. return nil
  116. }
  117. return errors.Wrapf(err, "Get %s %s", man.GetKeyword(), id)
  118. }
  119. time.Sleep(2 * time.Second)
  120. }
  121. return errors.Wrapf(httperrors.ErrTimeout, "wait %s %s deleted timeout", man.GetKeyword(), id)
  122. }
  123. func UpdateContainer(ctx context.Context, id string, getSpec func(*computeapi.SContainer) *computeapi.ContainerSpec) (*computeapi.SContainer, error) {
  124. s := auth.GetAdminSession(ctx, "")
  125. ctr, err := GetContainer(ctx, id)
  126. if err != nil {
  127. return nil, errors.Wrapf(err, "GetContainer %s", id)
  128. }
  129. curSpecStr := jsonutils.Marshal(ctr.Spec).String()
  130. newSpec := getSpec(ctr)
  131. newSpecStr := jsonutils.Marshal(newSpec).String()
  132. if curSpecStr != newSpecStr {
  133. ctr.Spec = newSpec
  134. resp, err := compute.Containers.Update(s, id, jsonutils.Marshal(ctr))
  135. if err != nil {
  136. return nil, errors.Wrapf(err, "UpdateContainer %s", id)
  137. }
  138. respCtr := new(computeapi.SContainer)
  139. if err := resp.Unmarshal(respCtr); err != nil {
  140. return nil, errors.Wrapf(err, "Unmarshal")
  141. }
  142. return respCtr, nil
  143. } else {
  144. log.Debugf("container spec not changed, skip update container %s spec", ctr.Name)
  145. }
  146. return ctr, nil
  147. }
  148. func ExecSyncContainer(ctx context.Context, containerId string, input *computeapi.ContainerExecSyncInput) (jsonutils.JSONObject, error) {
  149. session := auth.GetAdminSession(ctx, "")
  150. output, err := compute.Containers.PerformAction(session, containerId, "exec-sync", jsonutils.Marshal(input))
  151. if err != nil {
  152. return nil, errors.Wrap(err, "ExecSync")
  153. }
  154. return output, nil
  155. }