guest_restart_network_task.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  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 guest
  15. import (
  16. "context"
  17. "fmt"
  18. "time"
  19. "yunion.io/x/jsonutils"
  20. "yunion.io/x/log"
  21. "yunion.io/x/pkg/errors"
  22. "yunion.io/x/pkg/tristate"
  23. ansible_api "yunion.io/x/onecloud/pkg/apis/ansible"
  24. api "yunion.io/x/onecloud/pkg/apis/compute"
  25. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  26. "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
  27. "yunion.io/x/onecloud/pkg/compute/models"
  28. "yunion.io/x/onecloud/pkg/mcclient/auth"
  29. ansible_modules "yunion.io/x/onecloud/pkg/mcclient/modules/ansible"
  30. "yunion.io/x/onecloud/pkg/util/ansiblev2"
  31. "yunion.io/x/onecloud/pkg/util/logclient"
  32. )
  33. type GuestRestartNetworkTask struct {
  34. SGuestBaseTask
  35. }
  36. func init() {
  37. taskman.RegisterTask(GuestRestartNetworkTask{})
  38. }
  39. func (self *GuestRestartNetworkTask) taskFailed(ctx context.Context, guest *models.SGuest, clean func() error, err error) {
  40. guest.SetStatus(ctx, self.GetUserCred(), api.VM_RESTART_NETWORK_FAILED, err.Error())
  41. logclient.AddActionLogWithStartable(self, guest, logclient.ACT_RESTART_NETWORK, jsonutils.NewString(err.Error()), self.UserCred, false)
  42. if clean != nil {
  43. err := clean()
  44. if err != nil {
  45. log.Errorf("unable to clean: %s", err.Error())
  46. }
  47. }
  48. self.SetStageFailed(ctx, nil)
  49. }
  50. func (self *GuestRestartNetworkTask) OnCloseIpMacSrcCheckComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
  51. guest := obj.(*models.SGuest)
  52. ip, _ := self.Params.GetString("ip")
  53. session := auth.GetAdminSession(ctx, "")
  54. sshable, clean, err := self.checkSshable(ctx, guest, ip)
  55. log.Infof("start to CheckSshableForYunionCloud")
  56. if err != nil {
  57. self.taskFailed(ctx, guest, clean, err)
  58. return
  59. }
  60. log.Infof("ssable: %s", jsonutils.Marshal(sshable))
  61. if !sshable.Ok {
  62. self.taskFailed(ctx, guest, clean, fmt.Errorf("guest %s is not sshable", guest.GetId()))
  63. return
  64. }
  65. playbook := `- hosts: all
  66. become: true
  67. tasks:
  68. - name: "restart network"
  69. service:
  70. name: network
  71. state: restarted
  72. async: 20
  73. poll: 0`
  74. params := jsonutils.NewDict()
  75. params.Set("playbook", jsonutils.NewString(playbook))
  76. vars := map[string]interface{}{
  77. "ansible_port": fmt.Sprintf("%d", sshable.Port),
  78. "ansible_user": sshable.User,
  79. }
  80. host := ansiblev2.NewHost()
  81. host.Vars = vars
  82. inv := ansiblev2.NewInventory()
  83. inv.SetHost(sshable.Host, host)
  84. params.Set("inventory", jsonutils.NewString(inv.String()))
  85. params.Set("generate_name", jsonutils.NewString(fmt.Sprintf("%s-restart-network", guest.Name)))
  86. apb, err := ansible_modules.AnsiblePlaybooksV2.Create(session, params)
  87. if err != nil {
  88. self.taskFailed(ctx, guest, clean, err)
  89. return
  90. }
  91. id, _ := apb.GetString("id")
  92. defer func() {
  93. _, err := ansible_modules.AnsiblePlaybooksV2.Delete(session, id, nil)
  94. if err != nil {
  95. log.Errorf("unable to delete ansibleplaybook %s: %v", id, err)
  96. }
  97. }()
  98. times, waitTimes := 0, time.Second
  99. Loop:
  100. for times < 10 {
  101. time.Sleep(waitTimes)
  102. times++
  103. waitTimes += time.Second * time.Duration(times)
  104. apd, err := ansible_modules.AnsiblePlaybooksV2.GetSpecific(session, id, "status", nil)
  105. if err != nil {
  106. continue
  107. }
  108. status, _ := apd.GetString("status")
  109. switch status {
  110. case ansible_api.AnsiblePlaybookStatusInit, ansible_api.AnsiblePlaybookStatusRunning:
  111. continue
  112. case ansible_api.AnsiblePlaybookStatusFailed, ansible_api.AnsiblePlaybookStatusCanceled, ansible_api.AnsiblePlaybookStatusUnknown:
  113. apd, err := ansible_modules.AnsiblePlaybooksV2.GetSpecific(session, id, "output", nil)
  114. if err != nil {
  115. self.taskFailed(ctx, guest, nil, errors.Wrapf(err, "ansibleplaybook %s exec failed and can't get its output", id))
  116. return
  117. }
  118. output, _ := apd.GetString("output")
  119. self.taskFailed(ctx, guest, clean, fmt.Errorf("exec ansibleplaybook failed, its output:\n %s", output))
  120. return
  121. case ansible_api.AnsiblePlaybookStatusSucceeded:
  122. break Loop
  123. }
  124. }
  125. if inBlockStream := jsonutils.QueryBoolean(self.Params, "in_block_stream", false); inBlockStream {
  126. guest.SetStatus(ctx, self.GetUserCred(), api.VM_BLOCK_STREAM, "")
  127. } else {
  128. guest.SetStatus(ctx, self.GetUserCred(), api.VM_RUNNING, "")
  129. }
  130. logclient.AddActionLogWithStartable(self, guest, logclient.ACT_RESTART_NETWORK, "", self.UserCred, true)
  131. if clean != nil {
  132. err := clean()
  133. if err != nil {
  134. log.Errorf("unable to clean: %s", err.Error())
  135. }
  136. }
  137. if !self.Params.Contains("src_ip_check") {
  138. self.SetStageComplete(ctx, nil)
  139. return
  140. }
  141. srcIpCheck, _ := self.Params.Bool("src_ip_check")
  142. srcMacCheck, _ := self.Params.Bool("src_mac_check")
  143. _, err = db.Update(guest, func() error {
  144. guest.SrcIpCheck = tristate.NewFromBool(srcIpCheck)
  145. guest.SrcMacCheck = tristate.NewFromBool(srcMacCheck)
  146. return nil
  147. })
  148. if err != nil {
  149. self.taskFailed(ctx, guest, nil, err)
  150. return
  151. }
  152. self.SetStage("OnResumeIpMacSrcCheckComplete", nil)
  153. err = guest.StartSyncTask(ctx, self.GetUserCred(), false, self.Id)
  154. if err != nil {
  155. self.taskFailed(ctx, guest, nil, err)
  156. }
  157. }
  158. func (self *GuestRestartNetworkTask) OnResumeIpMacSrcCheckComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
  159. self.SetStageComplete(ctx, nil)
  160. }
  161. func (self *GuestRestartNetworkTask) OnResumeIpMacSrcCheckCompleteFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
  162. self.SetStageFailed(ctx, data)
  163. }
  164. func (self *GuestRestartNetworkTask) OnCloseIpMacSrcCheckCompleteFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
  165. guest := obj.(*models.SGuest)
  166. guest.SetStatus(ctx, self.GetUserCred(), api.VM_RESTART_NETWORK_FAILED, data.String())
  167. logclient.AddActionLogWithStartable(self, guest, logclient.ACT_RESTART_NETWORK, data, self.UserCred, false)
  168. self.SetStageFailed(ctx, nil)
  169. }
  170. func (self *GuestRestartNetworkTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
  171. guest := obj.(*models.SGuest)
  172. guest.SetStatus(ctx, self.GetUserCred(), api.VM_RESTART_NETWORK, "restart network")
  173. if guest.SrcIpCheck.IsTrue() || guest.SrcMacCheck.IsTrue() {
  174. data := jsonutils.NewDict()
  175. data.Set("src_ip_check", jsonutils.NewBool(guest.SrcIpCheck.Bool()))
  176. data.Set("src_mac_check", jsonutils.NewBool(guest.SrcMacCheck.Bool()))
  177. _, err := db.Update(guest, func() error {
  178. guest.SrcIpCheck = tristate.False
  179. guest.SrcMacCheck = tristate.False
  180. return nil
  181. })
  182. if err != nil {
  183. self.taskFailed(ctx, guest, nil, err)
  184. return
  185. }
  186. self.SetStage("OnCloseIpMacSrcCheckComplete", data)
  187. err = guest.StartSyncTask(ctx, self.GetUserCred(), false, self.Id)
  188. if err != nil {
  189. self.taskFailed(ctx, guest, nil, err)
  190. return
  191. }
  192. } else {
  193. self.OnCloseIpMacSrcCheckComplete(ctx, obj, data)
  194. }
  195. }
  196. type SSHable struct {
  197. Ok bool
  198. Reason string
  199. User string
  200. Host string
  201. Port int
  202. }
  203. func (self *GuestRestartNetworkTask) checkSshable(ctx context.Context, guest *models.SGuest, ip string) (sshable SSHable, cleanFunc func() error, err error) {
  204. vpc, err := guest.GetVpc()
  205. if err != nil {
  206. self.taskFailed(ctx, guest, nil, err)
  207. return
  208. }
  209. vpcId := vpc.GetId()
  210. if vpcId == "" || vpcId == api.DEFAULT_VPC_ID {
  211. sshable = SSHable{
  212. Ok: true,
  213. User: "cloudroot",
  214. Host: ip,
  215. Port: 22,
  216. }
  217. return
  218. }
  219. lfParams := jsonutils.NewDict()
  220. lfParams.Set("proto", jsonutils.NewString("tcp"))
  221. lfParams.Set("port", jsonutils.NewInt(22))
  222. lfParams.Set("addr", jsonutils.NewString(ip))
  223. var forward jsonutils.JSONObject
  224. forward, err = guest.PerformOpenForward(ctx, self.UserCred, nil, lfParams)
  225. if err != nil {
  226. err = errors.Wrapf(err, "unable to Open Forward for server %s", guest.Id)
  227. return
  228. }
  229. cleanFunc = func() error {
  230. proxyAddr := sshable.Host
  231. proxyPort := sshable.Port
  232. params := jsonutils.NewDict()
  233. params.Set("proto", jsonutils.NewString("tcp"))
  234. params.Set("proxy_addr", jsonutils.NewString(proxyAddr))
  235. params.Set("proxy_port", jsonutils.NewInt(int64(proxyPort)))
  236. _, err := guest.PerformCloseForward(ctx, self.UserCred, nil, params)
  237. if err != nil {
  238. return errors.Wrapf(err, "unable to close forward(addr %q, port %d, proto %q) for server %s", proxyAddr, proxyPort, "tcp", guest.Id)
  239. }
  240. return nil
  241. }
  242. proxyAddr, _ := forward.GetString("proxy_addr")
  243. proxyPort, _ := forward.Int("proxy_port")
  244. // register
  245. sshable = SSHable{
  246. Ok: true,
  247. User: "cloudroot",
  248. Host: proxyAddr,
  249. Port: int(proxyPort),
  250. }
  251. return
  252. }