handler.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  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 handler
  15. import (
  16. "fmt"
  17. "net/http"
  18. "time"
  19. simplejson "github.com/bitly/go-simplejson"
  20. gin "github.com/gin-gonic/gin"
  21. "yunion.io/x/jsonutils"
  22. "yunion.io/x/log"
  23. computeapi "yunion.io/x/onecloud/pkg/apis/compute"
  24. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  25. computemodels "yunion.io/x/onecloud/pkg/compute/models"
  26. "yunion.io/x/onecloud/pkg/scheduler/api"
  27. skuman "yunion.io/x/onecloud/pkg/scheduler/data_manager/sku"
  28. schedman "yunion.io/x/onecloud/pkg/scheduler/manager"
  29. )
  30. // InstallHandler is an interface that registes route and
  31. // handles scheduler's services.
  32. func InstallHandler(r *gin.Engine, enableProfiling bool) {
  33. r.POST("/scheduler", timer(scheduleHandler))
  34. r.POST("/scheduler/:action", timer(schedulerActionHandler))
  35. r.POST("/scheduler/:action/:ident", timer(schedulerActionIdentHandler))
  36. InstallPingHandler(r)
  37. InstallVersionHandler(r)
  38. InstallProfiling(r, enableProfiling)
  39. }
  40. func timer(f gin.HandlerFunc) gin.HandlerFunc {
  41. return func(c *gin.Context) {
  42. startTime := time.Now()
  43. f(c)
  44. path := c.Request.URL.Path
  45. raw := c.Request.URL.RawQuery
  46. if raw != "" {
  47. path = path + "?" + raw
  48. }
  49. log.Infof("Handler %q cost: %v", path, time.Since(startTime))
  50. }
  51. }
  52. func scheduleHandler(c *gin.Context) {
  53. doSyncSchedule(c)
  54. }
  55. func schedulerActionHandler(c *gin.Context) {
  56. act := c.Param("action")
  57. switch act {
  58. case "test":
  59. doSchedulerTest(c)
  60. case "forecast":
  61. doSchedulerForecast(c)
  62. case "candidate-list":
  63. doCandidateList(c)
  64. case "cleanup":
  65. doCleanup(c)
  66. case "history-list":
  67. doHistoryList(c)
  68. case "clean-cache":
  69. doCleanAllHostCache(c)
  70. case "sync-sku":
  71. doSyncSku(c)
  72. //case "reserved-resources":
  73. //doReservedResources(c)
  74. default:
  75. c.AbortWithError(http.StatusBadRequest, fmt.Errorf("action: %s not support", act))
  76. }
  77. }
  78. func schedulerActionIdentHandler(c *gin.Context) {
  79. act := c.Param("action")
  80. id := c.Param("ident")
  81. switch act {
  82. case "clean-cache":
  83. doCleanHostCache(c, id)
  84. case "candidate-detail":
  85. doCandidateDetail(c, id)
  86. case "history-detail":
  87. doHistoryDetail(c, id)
  88. case "completed":
  89. doCompleted(c, id)
  90. default:
  91. c.AbortWithError(http.StatusBadRequest, fmt.Errorf("action: %s not support", act))
  92. }
  93. }
  94. func doSchedulerTest(c *gin.Context) {
  95. if !schedman.IsReady() {
  96. c.AbortWithError(http.StatusBadRequest, fmt.Errorf("Global scheduler not init"))
  97. return
  98. }
  99. schedInfo, err := api.FetchSchedInfo(c.Request)
  100. if err != nil {
  101. c.AbortWithError(http.StatusBadRequest, err)
  102. return
  103. }
  104. schedInfo.IsSuggestion = true
  105. result, err := schedman.Schedule(schedInfo)
  106. if err != nil {
  107. c.AbortWithError(http.StatusBadRequest, err)
  108. return
  109. }
  110. c.JSON(http.StatusOK, result.TestResult)
  111. }
  112. func doSchedulerForecast(c *gin.Context) {
  113. if !schedman.IsReady() {
  114. c.AbortWithError(http.StatusBadRequest, fmt.Errorf("Global scheduler not init"))
  115. return
  116. }
  117. schedInfo, err := api.FetchSchedInfo(c.Request)
  118. if err != nil {
  119. c.AbortWithError(http.StatusBadRequest, err)
  120. return
  121. }
  122. schedInfo.IsSuggestion = true
  123. schedInfo.ShowSuggestionDetails = true
  124. schedInfo.SuggestionAll = true
  125. result, err := schedman.Schedule(schedInfo)
  126. if err != nil {
  127. c.AbortWithError(http.StatusBadRequest, err)
  128. return
  129. }
  130. c.JSON(http.StatusOK, result.ForecastResult)
  131. }
  132. func doCandidateList(c *gin.Context) {
  133. args, err := api.NewCandidateListArgs(c.Request.Body)
  134. if err != nil {
  135. c.AbortWithError(http.StatusBadRequest, err)
  136. return
  137. }
  138. result, err := schedman.GetCandidateList(args)
  139. if err != nil {
  140. c.AbortWithError(http.StatusBadRequest, err)
  141. return
  142. }
  143. c.JSON(http.StatusOK, result)
  144. }
  145. func doCandidateDetail(c *gin.Context, id string) {
  146. userCred, err := api.FetchUserCred(c.Request)
  147. if err != nil {
  148. c.AbortWithError(http.StatusBadRequest, err)
  149. return
  150. }
  151. hs, err := computemodels.HostManager.FetchByIdOrName(c.Request.Context(), userCred, id)
  152. if err != nil {
  153. c.AbortWithError(http.StatusInternalServerError, err)
  154. return
  155. }
  156. if hs == nil {
  157. c.AbortWithError(http.StatusNotFound, fmt.Errorf("Candidate %s not found.", id))
  158. return
  159. }
  160. host := hs.(*computemodels.SHost)
  161. args := new(api.CandidateDetailArgs)
  162. args.ID = host.GetId()
  163. if host.HostType == computeapi.HOST_TYPE_BAREMETAL {
  164. args.Type = api.HostTypeBaremetal
  165. } else {
  166. args.Type = api.HostTypeHost
  167. }
  168. result, err := schedman.GetCandidateDetail(args)
  169. if err != nil {
  170. c.AbortWithError(http.StatusBadRequest, err)
  171. return
  172. }
  173. SendJSON(c, http.StatusOK, result)
  174. }
  175. func doCleanup(c *gin.Context) {
  176. sjson, err := simplejson.NewFromReader(c.Request.Body)
  177. if err != nil {
  178. c.AbortWithError(http.StatusBadRequest, err)
  179. return
  180. }
  181. args, err := api.NewCleanupArgs(sjson)
  182. if err != nil {
  183. c.AbortWithError(http.StatusBadRequest, err)
  184. return
  185. }
  186. result, err := schedman.Cleanup(args)
  187. if err != nil {
  188. c.AbortWithError(http.StatusBadRequest, err)
  189. return
  190. }
  191. c.JSON(http.StatusOK, result)
  192. }
  193. func doHistoryList(c *gin.Context) {
  194. sjson, err := simplejson.NewFromReader(c.Request.Body)
  195. if err != nil {
  196. c.AbortWithError(http.StatusBadRequest, err)
  197. return
  198. }
  199. args, err := api.NewHistoryArgs(sjson)
  200. if err != nil {
  201. c.AbortWithError(http.StatusBadRequest, err)
  202. return
  203. }
  204. result, err := schedman.GetHistoryList(args)
  205. if err != nil {
  206. c.AbortWithError(http.StatusBadRequest, err)
  207. return
  208. }
  209. c.JSON(http.StatusOK, result)
  210. }
  211. func doHistoryDetail(c *gin.Context, id string) {
  212. sjson, err := simplejson.NewFromReader(c.Request.Body)
  213. if err != nil {
  214. c.AbortWithError(http.StatusBadRequest, err)
  215. return
  216. }
  217. args, err := api.NewHistoryDetailArgs(sjson, id)
  218. if err != nil {
  219. c.AbortWithError(http.StatusBadRequest, err)
  220. return
  221. }
  222. result, err := schedman.GetHistoryDetail(args)
  223. if err != nil {
  224. c.AbortWithError(http.StatusBadRequest, err)
  225. return
  226. }
  227. c.JSON(http.StatusOK, result)
  228. }
  229. func doSyncSchedule(c *gin.Context) {
  230. if !schedman.IsReady() {
  231. c.AbortWithError(http.StatusBadRequest, fmt.Errorf("Global scheduler not init"))
  232. return
  233. }
  234. schedInfo, err := api.FetchSchedInfo(c.Request)
  235. if err != nil {
  236. c.AbortWithError(http.StatusBadRequest, err)
  237. return
  238. }
  239. result, err := schedman.Schedule(schedInfo)
  240. if err != nil {
  241. c.AbortWithError(http.StatusBadRequest, err)
  242. return
  243. }
  244. c.JSON(http.StatusOK, result.Result)
  245. }
  246. func regionResponse(v interface{}) interface{} {
  247. return struct {
  248. Result interface{} `json:"scheduler"`
  249. }{Result: v}
  250. }
  251. func newExpireArgsByHostIDs(ids []string, sid string) (*api.ExpireArgs, error) {
  252. hs := []computemodels.SHost{}
  253. q := computemodels.HostManager.Query().In("id", ids)
  254. if err := db.FetchModelObjects(computemodels.HostManager, q, &hs); err != nil {
  255. return nil, fmt.Errorf("Fetch hosts by ids %v: %v", ids, err)
  256. }
  257. expireArgs := &api.ExpireArgs{
  258. DirtyBaremetals: []string{},
  259. DirtyHosts: []string{},
  260. SessionId: sid,
  261. }
  262. for _, host := range hs {
  263. if host.HostType == computeapi.HOST_TYPE_BAREMETAL {
  264. expireArgs.DirtyBaremetals = append(expireArgs.DirtyBaremetals, host.GetId())
  265. } else {
  266. expireArgs.DirtyHosts = append(expireArgs.DirtyHosts, host.GetId())
  267. }
  268. }
  269. return expireArgs, nil
  270. }
  271. func doCleanAllHostCache(c *gin.Context) {
  272. idMap, err := computemodels.HostManager.Query("id").AllStringMap()
  273. if err != nil {
  274. c.AbortWithError(http.StatusInternalServerError, err)
  275. return
  276. }
  277. ids := []string{}
  278. for _, obj := range idMap {
  279. ids = append(ids, obj["id"])
  280. }
  281. sid := getSessionId(c)
  282. args, err := newExpireArgsByHostIDs(ids, sid)
  283. if err != nil {
  284. c.AbortWithError(http.StatusBadRequest, err)
  285. return
  286. }
  287. doCleanHostCacheByArgs(c, args)
  288. }
  289. type SyncSkuArgs struct {
  290. Wait bool `json:"wait"`
  291. }
  292. func doSyncSku(c *gin.Context) {
  293. args := new(SyncSkuArgs)
  294. if err := c.BindJSON(args); err != nil {
  295. c.AbortWithError(http.StatusBadRequest, err)
  296. return
  297. }
  298. if err := skuman.SyncOnce(args.Wait); err != nil {
  299. c.AbortWithError(http.StatusBadRequest, err)
  300. return
  301. }
  302. c.JSON(http.StatusOK, nil)
  303. }
  304. func getSessionId(c *gin.Context) string {
  305. query, err := jsonutils.ParseQueryString(c.Request.URL.RawQuery)
  306. if err != nil {
  307. log.Warningf("not found session id in query")
  308. return ""
  309. }
  310. return jsonutils.GetAnyString(query, []string{"session", "session_id"})
  311. }
  312. func isSyncCleanSchedCache(c *gin.Context) bool {
  313. query, err := jsonutils.ParseQueryString(c.Request.URL.RawQuery)
  314. if err != nil {
  315. log.Warningf("not found sync clean cache in query")
  316. return false
  317. }
  318. return jsonutils.QueryBoolean(query, "sync_clean", false)
  319. }
  320. func doCleanHostCache(c *gin.Context, hostID string) {
  321. sid := getSessionId(c)
  322. args, err := newExpireArgsByHostIDs([]string{hostID}, sid)
  323. if err != nil {
  324. c.AbortWithError(http.StatusBadRequest, err)
  325. return
  326. }
  327. doCleanHostCacheByArgs(c, args)
  328. }
  329. func doCleanHostCacheByArgs(c *gin.Context, args *api.ExpireArgs) {
  330. result, err := schedman.Expire(args, isSyncCleanSchedCache(c))
  331. if err != nil {
  332. c.AbortWithError(http.StatusBadRequest, err)
  333. return
  334. }
  335. c.JSON(http.StatusOK, regionResponse(result))
  336. }
  337. func doCompleted(c *gin.Context, id string) {
  338. sjson, err := simplejson.NewFromReader(c.Request.Body)
  339. if err != nil {
  340. c.AbortWithError(http.StatusBadRequest, err)
  341. return
  342. }
  343. completedNotifyArgs, err := api.NewCompletedNotifyArgs(sjson, id)
  344. if err != nil {
  345. c.AbortWithError(http.StatusBadRequest, err)
  346. return
  347. }
  348. result, err := schedman.CompletedNotify(completedNotifyArgs)
  349. if err != nil {
  350. c.AbortWithError(http.StatusBadRequest, err)
  351. return
  352. }
  353. c.JSON(http.StatusOK, result)
  354. }