| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412 |
- // Copyright 2019 Yunion
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package handler
- import (
- "fmt"
- "net/http"
- "time"
- simplejson "github.com/bitly/go-simplejson"
- gin "github.com/gin-gonic/gin"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- computeapi "yunion.io/x/onecloud/pkg/apis/compute"
- "yunion.io/x/onecloud/pkg/cloudcommon/db"
- computemodels "yunion.io/x/onecloud/pkg/compute/models"
- "yunion.io/x/onecloud/pkg/scheduler/api"
- skuman "yunion.io/x/onecloud/pkg/scheduler/data_manager/sku"
- schedman "yunion.io/x/onecloud/pkg/scheduler/manager"
- )
- // InstallHandler is an interface that registes route and
- // handles scheduler's services.
- func InstallHandler(r *gin.Engine, enableProfiling bool) {
- r.POST("/scheduler", timer(scheduleHandler))
- r.POST("/scheduler/:action", timer(schedulerActionHandler))
- r.POST("/scheduler/:action/:ident", timer(schedulerActionIdentHandler))
- InstallPingHandler(r)
- InstallVersionHandler(r)
- InstallProfiling(r, enableProfiling)
- }
- func timer(f gin.HandlerFunc) gin.HandlerFunc {
- return func(c *gin.Context) {
- startTime := time.Now()
- f(c)
- path := c.Request.URL.Path
- raw := c.Request.URL.RawQuery
- if raw != "" {
- path = path + "?" + raw
- }
- log.Infof("Handler %q cost: %v", path, time.Since(startTime))
- }
- }
- func scheduleHandler(c *gin.Context) {
- doSyncSchedule(c)
- }
- func schedulerActionHandler(c *gin.Context) {
- act := c.Param("action")
- switch act {
- case "test":
- doSchedulerTest(c)
- case "forecast":
- doSchedulerForecast(c)
- case "candidate-list":
- doCandidateList(c)
- case "cleanup":
- doCleanup(c)
- case "history-list":
- doHistoryList(c)
- case "clean-cache":
- doCleanAllHostCache(c)
- case "sync-sku":
- doSyncSku(c)
- //case "reserved-resources":
- //doReservedResources(c)
- default:
- c.AbortWithError(http.StatusBadRequest, fmt.Errorf("action: %s not support", act))
- }
- }
- func schedulerActionIdentHandler(c *gin.Context) {
- act := c.Param("action")
- id := c.Param("ident")
- switch act {
- case "clean-cache":
- doCleanHostCache(c, id)
- case "candidate-detail":
- doCandidateDetail(c, id)
- case "history-detail":
- doHistoryDetail(c, id)
- case "completed":
- doCompleted(c, id)
- default:
- c.AbortWithError(http.StatusBadRequest, fmt.Errorf("action: %s not support", act))
- }
- }
- func doSchedulerTest(c *gin.Context) {
- if !schedman.IsReady() {
- c.AbortWithError(http.StatusBadRequest, fmt.Errorf("Global scheduler not init"))
- return
- }
- schedInfo, err := api.FetchSchedInfo(c.Request)
- if err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- schedInfo.IsSuggestion = true
- result, err := schedman.Schedule(schedInfo)
- if err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- c.JSON(http.StatusOK, result.TestResult)
- }
- func doSchedulerForecast(c *gin.Context) {
- if !schedman.IsReady() {
- c.AbortWithError(http.StatusBadRequest, fmt.Errorf("Global scheduler not init"))
- return
- }
- schedInfo, err := api.FetchSchedInfo(c.Request)
- if err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- schedInfo.IsSuggestion = true
- schedInfo.ShowSuggestionDetails = true
- schedInfo.SuggestionAll = true
- result, err := schedman.Schedule(schedInfo)
- if err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- c.JSON(http.StatusOK, result.ForecastResult)
- }
- func doCandidateList(c *gin.Context) {
- args, err := api.NewCandidateListArgs(c.Request.Body)
- if err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- result, err := schedman.GetCandidateList(args)
- if err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- c.JSON(http.StatusOK, result)
- }
- func doCandidateDetail(c *gin.Context, id string) {
- userCred, err := api.FetchUserCred(c.Request)
- if err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- hs, err := computemodels.HostManager.FetchByIdOrName(c.Request.Context(), userCred, id)
- if err != nil {
- c.AbortWithError(http.StatusInternalServerError, err)
- return
- }
- if hs == nil {
- c.AbortWithError(http.StatusNotFound, fmt.Errorf("Candidate %s not found.", id))
- return
- }
- host := hs.(*computemodels.SHost)
- args := new(api.CandidateDetailArgs)
- args.ID = host.GetId()
- if host.HostType == computeapi.HOST_TYPE_BAREMETAL {
- args.Type = api.HostTypeBaremetal
- } else {
- args.Type = api.HostTypeHost
- }
- result, err := schedman.GetCandidateDetail(args)
- if err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- SendJSON(c, http.StatusOK, result)
- }
- func doCleanup(c *gin.Context) {
- sjson, err := simplejson.NewFromReader(c.Request.Body)
- if err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- args, err := api.NewCleanupArgs(sjson)
- if err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- result, err := schedman.Cleanup(args)
- if err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- c.JSON(http.StatusOK, result)
- }
- func doHistoryList(c *gin.Context) {
- sjson, err := simplejson.NewFromReader(c.Request.Body)
- if err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- args, err := api.NewHistoryArgs(sjson)
- if err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- result, err := schedman.GetHistoryList(args)
- if err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- c.JSON(http.StatusOK, result)
- }
- func doHistoryDetail(c *gin.Context, id string) {
- sjson, err := simplejson.NewFromReader(c.Request.Body)
- if err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- args, err := api.NewHistoryDetailArgs(sjson, id)
- if err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- result, err := schedman.GetHistoryDetail(args)
- if err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- c.JSON(http.StatusOK, result)
- }
- func doSyncSchedule(c *gin.Context) {
- if !schedman.IsReady() {
- c.AbortWithError(http.StatusBadRequest, fmt.Errorf("Global scheduler not init"))
- return
- }
- schedInfo, err := api.FetchSchedInfo(c.Request)
- if err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- result, err := schedman.Schedule(schedInfo)
- if err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- c.JSON(http.StatusOK, result.Result)
- }
- func regionResponse(v interface{}) interface{} {
- return struct {
- Result interface{} `json:"scheduler"`
- }{Result: v}
- }
- func newExpireArgsByHostIDs(ids []string, sid string) (*api.ExpireArgs, error) {
- hs := []computemodels.SHost{}
- q := computemodels.HostManager.Query().In("id", ids)
- if err := db.FetchModelObjects(computemodels.HostManager, q, &hs); err != nil {
- return nil, fmt.Errorf("Fetch hosts by ids %v: %v", ids, err)
- }
- expireArgs := &api.ExpireArgs{
- DirtyBaremetals: []string{},
- DirtyHosts: []string{},
- SessionId: sid,
- }
- for _, host := range hs {
- if host.HostType == computeapi.HOST_TYPE_BAREMETAL {
- expireArgs.DirtyBaremetals = append(expireArgs.DirtyBaremetals, host.GetId())
- } else {
- expireArgs.DirtyHosts = append(expireArgs.DirtyHosts, host.GetId())
- }
- }
- return expireArgs, nil
- }
- func doCleanAllHostCache(c *gin.Context) {
- idMap, err := computemodels.HostManager.Query("id").AllStringMap()
- if err != nil {
- c.AbortWithError(http.StatusInternalServerError, err)
- return
- }
- ids := []string{}
- for _, obj := range idMap {
- ids = append(ids, obj["id"])
- }
- sid := getSessionId(c)
- args, err := newExpireArgsByHostIDs(ids, sid)
- if err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- doCleanHostCacheByArgs(c, args)
- }
- type SyncSkuArgs struct {
- Wait bool `json:"wait"`
- }
- func doSyncSku(c *gin.Context) {
- args := new(SyncSkuArgs)
- if err := c.BindJSON(args); err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- if err := skuman.SyncOnce(args.Wait); err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- c.JSON(http.StatusOK, nil)
- }
- func getSessionId(c *gin.Context) string {
- query, err := jsonutils.ParseQueryString(c.Request.URL.RawQuery)
- if err != nil {
- log.Warningf("not found session id in query")
- return ""
- }
- return jsonutils.GetAnyString(query, []string{"session", "session_id"})
- }
- func isSyncCleanSchedCache(c *gin.Context) bool {
- query, err := jsonutils.ParseQueryString(c.Request.URL.RawQuery)
- if err != nil {
- log.Warningf("not found sync clean cache in query")
- return false
- }
- return jsonutils.QueryBoolean(query, "sync_clean", false)
- }
- func doCleanHostCache(c *gin.Context, hostID string) {
- sid := getSessionId(c)
- args, err := newExpireArgsByHostIDs([]string{hostID}, sid)
- if err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- doCleanHostCacheByArgs(c, args)
- }
- func doCleanHostCacheByArgs(c *gin.Context, args *api.ExpireArgs) {
- result, err := schedman.Expire(args, isSyncCleanSchedCache(c))
- if err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- c.JSON(http.StatusOK, regionResponse(result))
- }
- func doCompleted(c *gin.Context, id string) {
- sjson, err := simplejson.NewFromReader(c.Request.Body)
- if err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- completedNotifyArgs, err := api.NewCompletedNotifyArgs(sjson, id)
- if err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- result, err := schedman.CompletedNotify(completedNotifyArgs)
- if err != nil {
- c.AbortWithError(http.StatusBadRequest, err)
- return
- }
- c.JSON(http.StatusOK, result)
- }
|