mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-05-07 22:24:32 +08:00
259 lines
7.8 KiB
Go
259 lines
7.8 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 specs
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/pkg/errors"
|
|
"yunion.io/x/pkg/util/sets"
|
|
|
|
"yunion.io/x/onecloud/pkg/appctx"
|
|
"yunion.io/x/onecloud/pkg/appsrv"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/policy"
|
|
"yunion.io/x/onecloud/pkg/compute/models"
|
|
"yunion.io/x/onecloud/pkg/httperrors"
|
|
"yunion.io/x/onecloud/pkg/mcclient"
|
|
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
|
)
|
|
|
|
type specHandleFunc func(context context.Context, userCred mcclient.TokenCredential, query *jsonutils.JSONDict) (jsonutils.JSONObject, error)
|
|
|
|
func AddSpecHandler(prefix string, app *appsrv.Application) {
|
|
// get models specs
|
|
for key, handleF := range map[string]specHandleFunc{
|
|
"": models.GetAllModelSpecs,
|
|
"hosts": models.GetHostSpecs,
|
|
"isolated_devices": models.GetIsolatedDeviceSpecs,
|
|
"servers": models.GetServerSpecs,
|
|
} {
|
|
addModelSpecHandler(prefix, key, handleF, app)
|
|
}
|
|
|
|
// get model objects by spec key
|
|
for key, handleF := range map[string]specQueryHandleFunc{
|
|
"hosts": queryHosts,
|
|
"isolated_devices": queryIsolatedDevices,
|
|
} {
|
|
AddQuerySpecModelHandler(prefix, key, handleF, app)
|
|
}
|
|
}
|
|
|
|
func processFilter(handleFunc specHandleFunc) appsrv.FilterHandler {
|
|
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
|
userCred := auth.FetchUserCredential(ctx, policy.FilterPolicyCredential)
|
|
query, err := jsonutils.ParseQueryString(r.URL.RawQuery)
|
|
if err != nil {
|
|
httperrors.GeneralServerError(ctx, w, err)
|
|
return
|
|
}
|
|
params := appctx.AppContextParams(ctx)
|
|
for key, v := range params {
|
|
query.(*jsonutils.JSONDict).Add(jsonutils.NewString(v), key)
|
|
}
|
|
spec, err := handleFunc(ctx, userCred, query.(*jsonutils.JSONDict))
|
|
if err != nil {
|
|
httperrors.GeneralServerError(ctx, w, err)
|
|
return
|
|
}
|
|
ret := jsonutils.NewDict()
|
|
ret.Add(spec, "spec")
|
|
appsrv.SendJSON(w, ret)
|
|
}
|
|
}
|
|
|
|
func addModelSpecHandler(prefix, managerPluralKey string, handleFunc specHandleFunc, app *appsrv.Application) {
|
|
af := auth.Authenticate(processFilter(handleFunc))
|
|
name := "get_spec"
|
|
prefix = fmt.Sprintf("%s/specs", prefix)
|
|
if len(managerPluralKey) != 0 {
|
|
prefix = fmt.Sprintf("%s/%s", prefix, managerPluralKey)
|
|
name = fmt.Sprintf("get_%s_spec", managerPluralKey)
|
|
}
|
|
app.AddHandler2("GET", prefix, af, nil, name, nil)
|
|
}
|
|
|
|
func AddQuerySpecModelHandler(prefix, managerPluralKey string, handleFunc specQueryHandleFunc, app *appsrv.Application) {
|
|
af := auth.Authenticate(processFilter(queryModelHandle(handleFunc)))
|
|
prefix = fmt.Sprintf("%s/specs/%s", prefix, managerPluralKey)
|
|
name := fmt.Sprintf("get_%s_spec_query", managerPluralKey)
|
|
app.AddHandler2("GET", fmt.Sprintf("%s/<spec_key>/resource", prefix), af, nil, name, nil)
|
|
}
|
|
|
|
func parseSpecKey(key string) ([]string, error) {
|
|
unEscapeStr, err := url.PathUnescape(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return strings.Split(unEscapeStr, "/"), nil
|
|
}
|
|
|
|
type specQueryHandleFunc func(context.Context, mcclient.TokenCredential, *jsonutils.JSONDict, []string) (jsonutils.JSONObject, error)
|
|
|
|
func queryModelHandle(queryF specQueryHandleFunc) specHandleFunc {
|
|
return func(ctx context.Context, userCred mcclient.TokenCredential, query *jsonutils.JSONDict) (jsonutils.JSONObject, error) {
|
|
specKey, _ := query.GetString("<spec_key>")
|
|
if specKey == "" {
|
|
return nil, httperrors.NewInputParameterError("Empty spec query key")
|
|
}
|
|
specKeys, err := parseSpecKey(specKey)
|
|
if err != nil {
|
|
return nil, httperrors.NewInputParameterError("Parse spec key %s error: %v", specKey, err)
|
|
}
|
|
return queryF(ctx, userCred, query, specKeys)
|
|
}
|
|
}
|
|
|
|
func queryHosts(
|
|
ctx context.Context,
|
|
userCred mcclient.TokenCredential,
|
|
query *jsonutils.JSONDict,
|
|
specKeys []string,
|
|
) (jsonutils.JSONObject, error) {
|
|
gpuModels := []string{}
|
|
newSpecs := []string{}
|
|
gpuHostIds := []string{}
|
|
for _, specKey := range specKeys {
|
|
if specKey == "gpu_model" {
|
|
gpuModels = append(gpuModels, strings.Split(specKey, ":")[1])
|
|
} else {
|
|
newSpecs = append(newSpecs, specKey)
|
|
}
|
|
}
|
|
|
|
if len(gpuModels) != 0 {
|
|
devs, err := models.IsolatedDeviceManager.FindUnusedByModels(gpuModels)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, dev := range devs {
|
|
gpuHostIds = append(gpuHostIds, dev.HostId)
|
|
}
|
|
}
|
|
|
|
isOk := func(obj models.ISpecModel) bool {
|
|
if len(gpuHostIds) > 0 {
|
|
if !sets.NewString(gpuHostIds...).Has(obj.GetId()) {
|
|
return false
|
|
}
|
|
gpus, _ := models.IsolatedDeviceManager.FindUnusedGpusOnHost(obj.GetId())
|
|
if len(gpus) == 0 {
|
|
return false
|
|
}
|
|
hostGpuModels := []string{}
|
|
for _, gpu := range gpus {
|
|
hostGpuModels = append(hostGpuModels, gpu.Model)
|
|
}
|
|
if !sets.NewString(hostGpuModels...).IsSuperset(sets.NewString(gpuModels...)) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
return handleQueryModel(models.HostManager, ctx, userCred, query, specKeys, isOk)
|
|
}
|
|
|
|
func queryIsolatedDevices(
|
|
ctx context.Context,
|
|
userCred mcclient.TokenCredential,
|
|
query *jsonutils.JSONDict,
|
|
specKeys []string,
|
|
) (jsonutils.JSONObject, error) {
|
|
return handleQueryModel(models.IsolatedDeviceManager, ctx, userCred, query, specKeys, nil)
|
|
}
|
|
|
|
func handleQueryModel(
|
|
manager models.ISpecModelManager,
|
|
ctx context.Context,
|
|
userCred mcclient.TokenCredential,
|
|
query *jsonutils.JSONDict,
|
|
specKeys []string,
|
|
isOkF func(models.ISpecModel) bool,
|
|
) (jsonutils.JSONObject, error) {
|
|
objects, err := models.ListItems(manager, ctx, userCred, query)
|
|
if err != nil {
|
|
return nil, httperrors.NewInternalServerError("Get object error: %v", err)
|
|
}
|
|
if len(objects) == 0 {
|
|
ret := jsonutils.NewArray()
|
|
return ret, nil
|
|
}
|
|
statusCheck, err := manager.GetSpecShouldCheckStatus(query)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
objs := QueryObjects(manager, objects, specKeys, isOkF, statusCheck)
|
|
return QueryObjectsToJson(manager, objs, ctx, userCred, query)
|
|
}
|
|
|
|
func QueryObjects(
|
|
manager models.ISpecModelManager,
|
|
objs []models.ISpecModel,
|
|
specKeys []string,
|
|
isOkF func(models.ISpecModel) bool,
|
|
statusCheck bool,
|
|
) []models.ISpecModel {
|
|
selectedObjs := make([]models.ISpecModel, 0)
|
|
for _, obj := range objs {
|
|
specs := obj.GetSpec(statusCheck)
|
|
if specs == nil {
|
|
continue
|
|
}
|
|
if isOkF != nil && !isOkF(obj) {
|
|
continue
|
|
}
|
|
specIdents := manager.GetSpecIdent(specs)
|
|
if len(specIdents) == 0 {
|
|
continue
|
|
}
|
|
if !sets.NewString(specIdents...).IsSuperset(sets.NewString(specKeys...)) {
|
|
continue
|
|
}
|
|
selectedObjs = append(selectedObjs, obj)
|
|
}
|
|
return selectedObjs
|
|
}
|
|
|
|
func QueryObjectsToJson(manager models.ISpecModelManager, objs []models.ISpecModel, ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
iObjs := make([]interface{}, len(objs))
|
|
for i := range objs {
|
|
iObjs[i] = objs[i]
|
|
}
|
|
details, err := db.FetchCustomizeColumns(manager, ctx, userCred, query, iObjs, nil, false)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "get %s details", manager.KeywordPlural())
|
|
}
|
|
ret := jsonutils.NewArray()
|
|
for idx, obj := range objs {
|
|
jsonData := jsonutils.Marshal(obj)
|
|
jsonDict, ok := jsonData.(*jsonutils.JSONDict)
|
|
if !ok {
|
|
return nil, fmt.Errorf("Invalid model data structure, not a dict")
|
|
}
|
|
extraDict := details[idx]
|
|
if extraDict != nil {
|
|
jsonDict.Update(extraDict)
|
|
}
|
|
ret.Add(jsonDict)
|
|
}
|
|
return ret, nil
|
|
}
|