apply_script_task.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  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 tasks
  15. import (
  16. "context"
  17. "yunion.io/x/jsonutils"
  18. "yunion.io/x/log"
  19. "yunion.io/x/pkg/errors"
  20. ansible_api "yunion.io/x/onecloud/pkg/apis/ansible"
  21. devtool_api "yunion.io/x/onecloud/pkg/apis/devtool"
  22. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  23. "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
  24. "yunion.io/x/onecloud/pkg/devtool/models"
  25. "yunion.io/x/onecloud/pkg/devtool/utils"
  26. "yunion.io/x/onecloud/pkg/mcclient"
  27. "yunion.io/x/onecloud/pkg/mcclient/auth"
  28. ansible_modules "yunion.io/x/onecloud/pkg/mcclient/modules/ansible"
  29. "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
  30. )
  31. type ApplyScriptTask struct {
  32. taskman.STask
  33. cleanFunc func()
  34. }
  35. func init() {
  36. taskman.RegisterTask(ApplyScriptTask{})
  37. }
  38. func (self *ApplyScriptTask) registerClean(clean func()) {
  39. self.cleanFunc = clean
  40. }
  41. func (self *ApplyScriptTask) clean() {
  42. if self.cleanFunc == nil {
  43. return
  44. }
  45. self.cleanFunc()
  46. }
  47. func (self *ApplyScriptTask) taskFailed(ctx context.Context, sa *models.SScriptApply, sar *models.SScriptApplyRecord, err error) {
  48. self.clean()
  49. var failCode string
  50. switch errors.Cause(err) {
  51. case utils.ErrServerNotSshable:
  52. failCode = devtool_api.SCRIPT_APPLY_RECORD_FAILCODE_SSHABLE
  53. case utils.ErrCannotReachInfluxbd:
  54. failCode = devtool_api.SCRIPT_APPLY_RECORD_FAILCODE_INFLUXDB
  55. default:
  56. failCode = devtool_api.SCRIPT_APPLY_RECORD_FAILCODE_OTHERS
  57. }
  58. err = sa.StopApply(self.UserCred, sar, false, failCode, err.Error())
  59. if err != nil {
  60. log.Errorf("unable to StopApply script %s to server %s", sa.ScriptId, sa.GuestId)
  61. self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
  62. return
  63. }
  64. if failCode == devtool_api.SCRIPT_APPLY_RECORD_FAILCODE_OTHERS {
  65. // restart
  66. err = sa.StartApply(ctx, self.UserCred)
  67. if err != nil {
  68. log.Errorf("unable to StartApply script %s to server %s", sa.ScriptId, sa.GuestId)
  69. }
  70. }
  71. var errMsg string
  72. if err != nil {
  73. errMsg = err.Error()
  74. }
  75. self.SetStageFailed(ctx, jsonutils.NewString(errMsg))
  76. }
  77. func (self *ApplyScriptTask) taskSuccess(ctx context.Context, sa *models.SScriptApply, sar *models.SScriptApplyRecord) {
  78. self.clean()
  79. err := sa.StopApply(self.UserCred, sar, true, "", "")
  80. if err != nil {
  81. log.Errorf("unable to StopApply script %s to server %s", sa.ScriptId, sa.GuestId)
  82. self.SetStageComplete(ctx, nil)
  83. }
  84. }
  85. func (self *ApplyScriptTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
  86. sa := obj.(*models.SScriptApply)
  87. // create record
  88. sar, err := models.ScriptApplyRecordManager.CreateRecord(ctx, sa.GetId())
  89. if err != nil {
  90. self.taskFailed(ctx, sa, nil, err)
  91. return
  92. }
  93. s, err := sa.Script()
  94. if err != nil {
  95. self.taskFailed(ctx, sa, sar, err)
  96. return
  97. }
  98. session := auth.GetAdminSession(ctx, "")
  99. sshable, cleanFunc, err := utils.CheckSSHable(session, sa.GuestId)
  100. // check sshable
  101. if err != nil {
  102. self.taskFailed(ctx, sa, sar, err)
  103. return
  104. }
  105. if cleanFunc != nil {
  106. self.registerClean(func() {
  107. err := cleanFunc()
  108. if err != nil {
  109. log.Errorf("unable to clean: %v", err)
  110. }
  111. })
  112. }
  113. host := ansible_api.AnsibleHost{
  114. User: sshable.User,
  115. IP: sshable.Host,
  116. Port: sshable.Port,
  117. Name: sshable.ServerName,
  118. Password: sshable.Password,
  119. OsType: sshable.OsType,
  120. }
  121. // genrate args
  122. params := jsonutils.NewDict()
  123. if len(sa.ArgsGenerator) == 0 {
  124. params.Set("args", sa.Args)
  125. } else {
  126. generator, ok := utils.GetArgGenerator(sa.ArgsGenerator)
  127. if !ok {
  128. params.Set("args", sa.Args)
  129. }
  130. arg, err := generator(ctx, sa.GuestId, sshable.ProxyEndpointId, &host)
  131. if err != nil {
  132. self.taskFailed(ctx, sa, sar, err)
  133. return
  134. }
  135. params.Set("args", jsonutils.Marshal(arg))
  136. }
  137. params.Set("host", jsonutils.Marshal(host))
  138. // fetch ansible playbook reference id
  139. updateData := jsonutils.NewDict()
  140. updateData.Set("script_apply_record_id", jsonutils.NewString(sar.GetId()))
  141. self.SetStage("OnAnsiblePlaybookComplete", updateData)
  142. // Inject Task Header
  143. taskHeader := self.GetTaskRequestHeader()
  144. session.Header.Set(mcclient.TASK_NOTIFY_URL, taskHeader.Get(mcclient.TASK_NOTIFY_URL))
  145. session.Header.Set(mcclient.TASK_ID, taskHeader.Get(mcclient.TASK_ID))
  146. _, err = ansible_modules.AnsiblePlaybookReference.PerformAction(session, s.PlaybookReferenceId, "run", params)
  147. if err != nil {
  148. self.taskFailed(ctx, sa, sar, errors.Wrapf(err, "can't run ansible playbook reference %s", s.PlaybookReferenceId))
  149. return
  150. }
  151. }
  152. func mapStringSlice(f func(string) string, a []string) []string {
  153. for i := range a {
  154. a[i] = f(a[i])
  155. }
  156. return a
  157. }
  158. const (
  159. agentInstalledKey = "__monitor_agent"
  160. agentInstalledValue = "true"
  161. )
  162. func (self *ApplyScriptTask) OnAnsiblePlaybookComplete(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
  163. // try to delete local forward
  164. session := auth.GetAdminSession(ctx, "")
  165. sa := obj.(*models.SScriptApply)
  166. // try to set metadata for guest
  167. metadata := jsonutils.NewDict()
  168. metadata.Set(agentInstalledKey, jsonutils.NewString(agentInstalledValue))
  169. _, err := compute.Servers.PerformAction(session, sa.GuestId, "metadata", metadata)
  170. if err != nil {
  171. log.Errorf("set metadata '%s:%s' for guest %s failed: %v", agentInstalledKey, agentInstalledValue, sa.GuestId, err)
  172. }
  173. sarId, _ := self.Params.GetString("script_apply_record_id")
  174. osar, err := models.ScriptApplyRecordManager.FetchById(sarId)
  175. if err != nil {
  176. log.Errorf("unable to fetch script apply record %s: %v", sarId, err)
  177. self.taskSuccess(ctx, sa, nil)
  178. }
  179. self.taskSuccess(ctx, sa, osar.(*models.SScriptApplyRecord))
  180. }
  181. func (self *ApplyScriptTask) OnAnsiblePlaybookCompleteFailed(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
  182. sa := obj.(*models.SScriptApply)
  183. sarId, _ := self.Params.GetString("script_apply_record_id")
  184. osar, err := models.ScriptApplyRecordManager.FetchById(sarId)
  185. if err != nil {
  186. log.Errorf("unable to fetch script apply record %s: %v", sarId, err)
  187. self.taskFailed(ctx, sa, nil, errors.Error(body.String()))
  188. } else {
  189. self.taskFailed(ctx, sa, osar.(*models.SScriptApplyRecord), errors.Error(body.String()))
  190. }
  191. }