handler.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  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 specs
  15. import (
  16. "context"
  17. "fmt"
  18. "net/http"
  19. "net/url"
  20. "strings"
  21. "yunion.io/x/jsonutils"
  22. "yunion.io/x/pkg/appctx"
  23. "yunion.io/x/pkg/errors"
  24. "yunion.io/x/pkg/util/sets"
  25. "yunion.io/x/onecloud/pkg/appsrv"
  26. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  27. "yunion.io/x/onecloud/pkg/cloudcommon/policy"
  28. "yunion.io/x/onecloud/pkg/compute/models"
  29. "yunion.io/x/onecloud/pkg/httperrors"
  30. "yunion.io/x/onecloud/pkg/mcclient"
  31. "yunion.io/x/onecloud/pkg/mcclient/auth"
  32. )
  33. type specHandleFunc func(context context.Context, userCred mcclient.TokenCredential, query *jsonutils.JSONDict) (jsonutils.JSONObject, error)
  34. func AddSpecHandler(prefix string, app *appsrv.Application) {
  35. // get models specs
  36. for key, handleF := range map[string]specHandleFunc{
  37. "": models.GetAllModelSpecs,
  38. "hosts": models.GetHostSpecs,
  39. "isolated_devices": models.GetIsolatedDeviceSpecs,
  40. "servers": models.GetServerSpecs,
  41. } {
  42. addModelSpecHandler(prefix, key, handleF, app)
  43. }
  44. // get model objects by spec key
  45. for key, handleF := range map[string]specQueryHandleFunc{
  46. "hosts": queryHosts,
  47. "isolated_devices": queryIsolatedDevices,
  48. } {
  49. AddQuerySpecModelHandler(prefix, key, handleF, app)
  50. }
  51. }
  52. func processFilter(handleFunc specHandleFunc) appsrv.FilterHandler {
  53. return func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
  54. userCred := auth.FetchUserCredential(ctx, policy.FilterPolicyCredential)
  55. query, err := jsonutils.ParseQueryString(r.URL.RawQuery)
  56. if err != nil {
  57. httperrors.GeneralServerError(ctx, w, err)
  58. return
  59. }
  60. params := appctx.AppContextParams(ctx)
  61. for key, v := range params {
  62. query.(*jsonutils.JSONDict).Add(jsonutils.NewString(v), key)
  63. }
  64. spec, err := handleFunc(ctx, userCred, query.(*jsonutils.JSONDict))
  65. if err != nil {
  66. httperrors.GeneralServerError(ctx, w, err)
  67. return
  68. }
  69. ret := jsonutils.NewDict()
  70. ret.Add(spec, "spec")
  71. appsrv.SendJSON(w, ret)
  72. }
  73. }
  74. func addModelSpecHandler(prefix, managerPluralKey string, handleFunc specHandleFunc, app *appsrv.Application) {
  75. af := auth.Authenticate(processFilter(handleFunc))
  76. name := "get_spec"
  77. prefix = fmt.Sprintf("%s/specs", prefix)
  78. if len(managerPluralKey) != 0 {
  79. prefix = fmt.Sprintf("%s/%s", prefix, managerPluralKey)
  80. name = fmt.Sprintf("get_%s_spec", managerPluralKey)
  81. }
  82. app.AddHandler2("GET", prefix, af, nil, name, nil)
  83. }
  84. func AddQuerySpecModelHandler(prefix, managerPluralKey string, handleFunc specQueryHandleFunc, app *appsrv.Application) {
  85. af := auth.Authenticate(processFilter(queryModelHandle(handleFunc)))
  86. prefix = fmt.Sprintf("%s/specs/%s", prefix, managerPluralKey)
  87. name := fmt.Sprintf("get_%s_spec_query", managerPluralKey)
  88. app.AddHandler2("GET", fmt.Sprintf("%s/<spec_key>/resource", prefix), af, nil, name, nil)
  89. }
  90. func parseSpecKey(key string) ([]string, error) {
  91. unEscapeStr, err := url.PathUnescape(key)
  92. if err != nil {
  93. return nil, err
  94. }
  95. return strings.Split(unEscapeStr, "/"), nil
  96. }
  97. type specQueryHandleFunc func(context.Context, mcclient.TokenCredential, *jsonutils.JSONDict, []string) (jsonutils.JSONObject, error)
  98. func queryModelHandle(queryF specQueryHandleFunc) specHandleFunc {
  99. return func(ctx context.Context, userCred mcclient.TokenCredential, query *jsonutils.JSONDict) (jsonutils.JSONObject, error) {
  100. specKey, _ := query.GetString("<spec_key>")
  101. if specKey == "" {
  102. return nil, httperrors.NewInputParameterError("Empty spec query key")
  103. }
  104. specKeys, err := parseSpecKey(specKey)
  105. if err != nil {
  106. return nil, httperrors.NewInputParameterError("Parse spec key %s error: %v", specKey, err)
  107. }
  108. return queryF(ctx, userCred, query, specKeys)
  109. }
  110. }
  111. func queryHosts(
  112. ctx context.Context,
  113. userCred mcclient.TokenCredential,
  114. query *jsonutils.JSONDict,
  115. specKeys []string,
  116. ) (jsonutils.JSONObject, error) {
  117. gpuModels := []string{}
  118. newSpecs := []string{}
  119. gpuHostIds := []string{}
  120. for _, specKey := range specKeys {
  121. if specKey == "gpu_model" {
  122. gpuModels = append(gpuModels, strings.Split(specKey, ":")[1])
  123. } else {
  124. newSpecs = append(newSpecs, specKey)
  125. }
  126. }
  127. if len(gpuModels) != 0 {
  128. devs, err := models.IsolatedDeviceManager.FindUnusedByModels(gpuModels)
  129. if err != nil {
  130. return nil, err
  131. }
  132. for _, dev := range devs {
  133. gpuHostIds = append(gpuHostIds, dev.HostId)
  134. }
  135. }
  136. isOk := func(obj models.ISpecModel) bool {
  137. if len(gpuHostIds) > 0 {
  138. if !sets.NewString(gpuHostIds...).Has(obj.GetId()) {
  139. return false
  140. }
  141. gpus, _ := models.IsolatedDeviceManager.FindUnusedGpusOnHost(obj.GetId())
  142. if len(gpus) == 0 {
  143. return false
  144. }
  145. hostGpuModels := []string{}
  146. for _, gpu := range gpus {
  147. hostGpuModels = append(hostGpuModels, gpu.Model)
  148. }
  149. if !sets.NewString(hostGpuModels...).IsSuperset(sets.NewString(gpuModels...)) {
  150. return false
  151. }
  152. }
  153. return true
  154. }
  155. return handleQueryModel(models.HostManager, ctx, userCred, query, specKeys, isOk)
  156. }
  157. func queryIsolatedDevices(
  158. ctx context.Context,
  159. userCred mcclient.TokenCredential,
  160. query *jsonutils.JSONDict,
  161. specKeys []string,
  162. ) (jsonutils.JSONObject, error) {
  163. return handleQueryModel(models.IsolatedDeviceManager, ctx, userCred, query, specKeys, nil)
  164. }
  165. func handleQueryModel(
  166. manager models.ISpecModelManager,
  167. ctx context.Context,
  168. userCred mcclient.TokenCredential,
  169. query *jsonutils.JSONDict,
  170. specKeys []string,
  171. isOkF func(models.ISpecModel) bool,
  172. ) (jsonutils.JSONObject, error) {
  173. objects, err := models.ListItems(manager, ctx, userCred, query)
  174. if err != nil {
  175. return nil, httperrors.NewInternalServerError("Get object error: %v", err)
  176. }
  177. if len(objects) == 0 {
  178. ret := jsonutils.NewArray()
  179. return ret, nil
  180. }
  181. statusCheck, err := manager.GetSpecShouldCheckStatus(query)
  182. if err != nil {
  183. return nil, err
  184. }
  185. objs := QueryObjects(manager, objects, specKeys, isOkF, statusCheck)
  186. return QueryObjectsToJson(manager, objs, ctx, userCred, query)
  187. }
  188. func QueryObjects(
  189. manager models.ISpecModelManager,
  190. objs []models.ISpecModel,
  191. specKeys []string,
  192. isOkF func(models.ISpecModel) bool,
  193. statusCheck bool,
  194. ) []models.ISpecModel {
  195. selectedObjs := make([]models.ISpecModel, 0)
  196. for _, obj := range objs {
  197. specs := obj.GetSpec(statusCheck)
  198. if specs == nil {
  199. continue
  200. }
  201. if isOkF != nil && !isOkF(obj) {
  202. continue
  203. }
  204. specIdents := manager.GetSpecIdent(specs)
  205. if len(specIdents) == 0 {
  206. continue
  207. }
  208. if !sets.NewString(specIdents...).IsSuperset(sets.NewString(specKeys...)) {
  209. continue
  210. }
  211. selectedObjs = append(selectedObjs, obj)
  212. }
  213. return selectedObjs
  214. }
  215. func QueryObjectsToJson(manager models.ISpecModelManager, objs []models.ISpecModel, ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  216. iObjs := make([]interface{}, len(objs))
  217. for i := range objs {
  218. iObjs[i] = objs[i]
  219. }
  220. details, err := db.FetchCustomizeColumns(manager, ctx, userCred, query, iObjs, nil, false)
  221. if err != nil {
  222. return nil, errors.Wrapf(err, "get %s details", manager.KeywordPlural())
  223. }
  224. ret := jsonutils.NewArray()
  225. for idx, obj := range objs {
  226. jsonData := jsonutils.Marshal(obj)
  227. jsonDict, ok := jsonData.(*jsonutils.JSONDict)
  228. if !ok {
  229. return nil, fmt.Errorf("Invalid model data structure, not a dict")
  230. }
  231. extraDict := details[idx]
  232. if extraDict != nil {
  233. jsonDict.Update(extraDict)
  234. }
  235. ret.Add(jsonDict)
  236. }
  237. return ret, nil
  238. }