mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-05-07 22:24:32 +08:00
412 lines
9.7 KiB
Go
412 lines
9.7 KiB
Go
// 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) {
|
|
r.POST("/scheduler", timer(scheduleHandler))
|
|
r.POST("/scheduler/:action", timer(schedulerActionHandler))
|
|
r.POST("/scheduler/:action/:ident", timer(schedulerActionIdentHandler))
|
|
InstallPingHandler(r)
|
|
InstallVersionHandler(r)
|
|
}
|
|
|
|
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(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)
|
|
}
|