project_resources.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  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 cronjobs
  15. import (
  16. "context"
  17. "fmt"
  18. "net/http"
  19. "time"
  20. "yunion.io/x/jsonutils"
  21. "yunion.io/x/log"
  22. "yunion.io/x/pkg/errors"
  23. "yunion.io/x/pkg/gotypes"
  24. "yunion.io/x/pkg/util/httputils"
  25. "yunion.io/x/pkg/utils"
  26. "yunion.io/x/onecloud/pkg/apis"
  27. api "yunion.io/x/onecloud/pkg/apis/identity"
  28. "yunion.io/x/onecloud/pkg/appsrv"
  29. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  30. "yunion.io/x/onecloud/pkg/httperrors"
  31. "yunion.io/x/onecloud/pkg/keystone/models"
  32. "yunion.io/x/onecloud/pkg/keystone/tokens"
  33. "yunion.io/x/onecloud/pkg/mcclient"
  34. "yunion.io/x/onecloud/pkg/mcclient/auth"
  35. )
  36. var (
  37. serviceBlackList map[string]time.Time
  38. )
  39. func init() {
  40. serviceBlackList = make(map[string]time.Time)
  41. }
  42. type sServiceEndpoints struct {
  43. regionId string
  44. serviceId string
  45. internal string
  46. external string
  47. }
  48. func FetchScopeResourceCount(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
  49. err := refreshScopeResourceCount(ctx)
  50. if err != nil {
  51. log.Errorf("refreshScopeResourceCount error: %v", err)
  52. }
  53. }
  54. func refreshScopeResourceCount(ctx context.Context) error {
  55. log.Debugf("FetchScopeResourceCount")
  56. eps, err := models.EndpointManager.FetchAll()
  57. if err != nil {
  58. return errors.Wrapf(err, "EndpointManager.FetchAll")
  59. }
  60. serviceTbl := make(map[string]*sServiceEndpoints)
  61. for _, ep := range eps {
  62. if utils.IsInStringArray(ep.ServiceType, apis.NO_RESOURCE_SERVICES) {
  63. // skip self and offline cloudmeta
  64. continue
  65. }
  66. key := fmt.Sprintf("%s-%s", ep.RegionId, ep.ServiceId)
  67. if _, ok := serviceTbl[key]; !ok {
  68. serviceTbl[key] = &sServiceEndpoints{
  69. regionId: ep.RegionId,
  70. serviceId: ep.ServiceId,
  71. }
  72. }
  73. switch ep.Interface {
  74. case api.EndpointInterfacePublic:
  75. serviceTbl[key].external = ep.Url
  76. case api.EndpointInterfaceInternal:
  77. serviceTbl[key].internal = ep.Url
  78. }
  79. }
  80. for srvId, ep := range serviceTbl {
  81. if to, ok := serviceBlackList[srvId]; ok {
  82. if to.IsZero() || to.After(time.Now()) {
  83. continue
  84. }
  85. }
  86. url := ep.internal
  87. if url == "" {
  88. url = ep.external
  89. }
  90. url = httputils.JoinPath(url, "scope-resources")
  91. tk := tokens.GetDefaultToken()
  92. hdr := http.Header{}
  93. hdr.Add("X-Auth-Token", tk)
  94. _, ret, err := httputils.JSONRequest(
  95. httputils.GetTimeoutClient(time.Minute),
  96. ctx, "GET",
  97. url,
  98. hdr,
  99. nil, false)
  100. if err != nil {
  101. // ignore errors
  102. // log.Errorf("fetch from %s fail: %s", url, err)
  103. errCode := httputils.ErrorCode(err)
  104. if errCode == 404 {
  105. serviceBlackList[srvId] = time.Time{}
  106. } else {
  107. serviceBlackList[srvId] = time.Now().Add(time.Hour)
  108. }
  109. continue
  110. }
  111. if gotypes.IsNil(ret) {
  112. continue
  113. }
  114. if _, ok := serviceBlackList[srvId]; ok {
  115. delete(serviceBlackList, srvId)
  116. }
  117. projectResCounts := make(map[string][]db.SScopeResourceCount)
  118. err = ret.Unmarshal(&projectResCounts)
  119. if err != nil {
  120. continue
  121. }
  122. syncScopeResourceCount(ctx, ep.regionId, ep.serviceId, projectResCounts)
  123. }
  124. return nil
  125. }
  126. func syncScopeResourceCount(ctx context.Context, regionId string, serviceId string, projResCnt map[string][]db.SScopeResourceCount) {
  127. for res, resCnts := range projResCnt {
  128. projList := make([]string, 0)
  129. domainList := []string{}
  130. ownerList := []string{}
  131. for i := range resCnts {
  132. if len(resCnts[i].TenantId) == 0 && len(resCnts[i].DomainId) == 0 && len(resCnts[i].OwnerId) == 0 {
  133. continue
  134. }
  135. scopeRes := models.SScopeResource{
  136. DomainId: resCnts[i].DomainId,
  137. ProjectId: resCnts[i].TenantId,
  138. OwnerId: resCnts[i].OwnerId,
  139. }
  140. scopeRes.RegionId = regionId
  141. scopeRes.ServiceId = serviceId
  142. scopeRes.Resource = res
  143. scopeRes.Count = resCnts[i].ResCount
  144. if len(scopeRes.ProjectId) > 0 {
  145. projList = append(projList, scopeRes.ProjectId)
  146. }
  147. if len(scopeRes.DomainId) > 0 {
  148. domainList = append(domainList, scopeRes.DomainId)
  149. }
  150. if len(scopeRes.OwnerId) > 0 {
  151. ownerList = append(ownerList, scopeRes.OwnerId)
  152. }
  153. scopeRes.SetModelManager(models.ScopeResourceManager, &scopeRes)
  154. err := models.ScopeResourceManager.TableSpec().InsertOrUpdate(ctx, &scopeRes)
  155. if err != nil {
  156. log.Errorf("table insert error %s", err)
  157. }
  158. }
  159. q := models.ScopeResourceManager.Query()
  160. if len(projList) > 0 {
  161. q = q.NotIn("project_id", projList)
  162. }
  163. if len(domainList) > 0 {
  164. q = q.NotIn("domain_id", domainList)
  165. }
  166. if len(ownerList) > 0 {
  167. q = q.NotIn("owner_id", ownerList)
  168. }
  169. q = q.Equals("region_id", regionId)
  170. q = q.Equals("service_id", serviceId)
  171. q = q.Equals("resource", res)
  172. q = q.NotEquals("count", 0)
  173. emptySets := make([]models.SScopeResource, 0)
  174. err := db.FetchModelObjects(models.ScopeResourceManager, q, &emptySets)
  175. if err != nil {
  176. log.Errorf("db.FetchModelObjects %s", err)
  177. }
  178. for i := range emptySets {
  179. _, err := db.Update(&emptySets[i], func() error {
  180. emptySets[i].Count = 0
  181. return nil
  182. })
  183. if err != nil {
  184. log.Errorf("db.Update %s", err)
  185. }
  186. }
  187. }
  188. }
  189. func AddRefreshHandler(prefix string, app *appsrv.Application) {
  190. app.AddHandler2("POST", fmt.Sprintf("%s/scope-resource/refresh", prefix), auth.Authenticate(refreshHandler), nil, "scope_resource_refresh", nil)
  191. }
  192. func refreshHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
  193. userCred := auth.FetchUserCredential(ctx, nil)
  194. if userCred == nil || db.IsDomainAllowList(userCred, models.DomainManager).Result.IsDeny() {
  195. httperrors.ForbiddenError(ctx, w, "not enough privilege")
  196. return
  197. }
  198. err := refreshScopeResourceCount(ctx)
  199. if err != nil {
  200. httperrors.GeneralServerError(ctx, w, err)
  201. return
  202. }
  203. ret := map[string]interface{}{
  204. "scope-resource": map[string]string{
  205. "status": "ok",
  206. },
  207. }
  208. fmt.Fprintf(w, "%s", jsonutils.Marshal(ret).String())
  209. }