models.go 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  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 quotas
  15. import (
  16. "context"
  17. "database/sql"
  18. "reflect"
  19. "sort"
  20. "yunion.io/x/log"
  21. "yunion.io/x/pkg/errors"
  22. "yunion.io/x/pkg/util/rbacscope"
  23. "yunion.io/x/pkg/util/reflectutils"
  24. "yunion.io/x/sqlchemy"
  25. identityapi "yunion.io/x/onecloud/pkg/apis/identity"
  26. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  27. "yunion.io/x/onecloud/pkg/mcclient"
  28. "yunion.io/x/onecloud/pkg/util/ctx"
  29. )
  30. type SQuotaBaseManager struct {
  31. db.SResourceBaseManager
  32. pendingStore IQuotaStore
  33. usageStore IQuotaStore
  34. nonNegative bool
  35. scope rbacscope.TRbacScope
  36. }
  37. func NewQuotaBaseManager(model interface{}, scope rbacscope.TRbacScope, tableName string, pendingStore IQuotaStore, usageStore IQuotaStore, keyword, keywordPlural string) SQuotaBaseManager {
  38. pendingStore.SetVirtualObject(pendingStore)
  39. usageStore.SetVirtualObject(usageStore)
  40. return SQuotaBaseManager{
  41. SResourceBaseManager: db.NewResourceBaseManager(model, tableName, keyword, keywordPlural),
  42. pendingStore: pendingStore,
  43. usageStore: usageStore,
  44. nonNegative: false,
  45. scope: scope,
  46. }
  47. }
  48. func NewQuotaUsageManager(model interface{}, scope rbacscope.TRbacScope, tableName string, keyword, keywordPlural string) SQuotaBaseManager {
  49. return SQuotaBaseManager{
  50. SResourceBaseManager: db.NewResourceBaseManager(model, tableName, keyword, keywordPlural),
  51. nonNegative: true,
  52. scope: scope,
  53. }
  54. }
  55. type SQuotaBase struct {
  56. db.SResourceBase
  57. }
  58. func (manager *SQuotaBaseManager) GetIQuotaManager() IQuotaManager {
  59. return manager.GetIResourceModelManager().(IQuotaManager)
  60. }
  61. func (manager *SQuotaBaseManager) FetchIdNames(ctx context.Context, idMap map[string]map[string]string) (map[string]map[string]string, error) {
  62. return idMap, nil
  63. }
  64. func (manager *SQuotaBaseManager) getQuotaByKeys(ctx context.Context, keys IQuotaKeys, quota IQuota) error {
  65. q := manager.Query()
  66. fields := keys.Fields()
  67. values := keys.Values()
  68. for i := range fields {
  69. if len(values[i]) == 0 {
  70. q = q.IsNullOrEmpty(fields[i])
  71. } else {
  72. q = q.Equals(fields[i], values[i])
  73. }
  74. }
  75. err := q.First(quota)
  76. if manager.nonNegative {
  77. quota.ResetNegative()
  78. }
  79. if err != nil {
  80. return errors.Wrap(err, "q.Query")
  81. }
  82. return nil
  83. }
  84. func filterParentByKey(q *sqlchemy.SQuery, fieldName string, value string) *sqlchemy.SQuery {
  85. if len(value) > 0 {
  86. q = q.Filter(sqlchemy.OR(
  87. sqlchemy.IsNullOrEmpty(q.Field(fieldName)),
  88. sqlchemy.Equals(q.Field(fieldName), value),
  89. ))
  90. } else {
  91. q = q.Filter(sqlchemy.IsNullOrEmpty(q.Field(fieldName)))
  92. }
  93. return q
  94. }
  95. func filterChildrenByKey(q *sqlchemy.SQuery, fieldName string, value string) *sqlchemy.SQuery {
  96. if len(value) > 0 {
  97. q = q.Equals(fieldName, value)
  98. }
  99. return q
  100. }
  101. func (manager *SQuotaBaseManager) getQuotasInternal(ctx context.Context, keys IQuotaKeys, isParent bool) ([]IQuota, error) {
  102. q := manager.Query()
  103. fields := keys.Fields()
  104. values := keys.Values()
  105. for i := range fields {
  106. if isParent {
  107. q = filterParentByKey(q, fields[i], values[i])
  108. } else {
  109. q = filterChildrenByKey(q, fields[i], values[i])
  110. }
  111. }
  112. rows, err := q.Rows()
  113. if err != nil {
  114. if errors.Cause(err) == sql.ErrNoRows {
  115. return nil, nil
  116. } else {
  117. return nil, errors.Wrap(err, "q.Rows")
  118. }
  119. }
  120. defer rows.Close()
  121. results := make([]IQuota, 0)
  122. for rows.Next() {
  123. r := manager.newQuota()
  124. err := q.Row2Struct(rows, r)
  125. if err != nil {
  126. return nil, errors.Wrap(err, "q.Row2Struct")
  127. }
  128. if manager.nonNegative {
  129. r.ResetNegative()
  130. }
  131. results = append(results, r)
  132. }
  133. sort.Sort(TQuotaList(results))
  134. return results, nil
  135. }
  136. func (manager *SQuotaBaseManager) setQuotaInternal(ctx context.Context, userCred mcclient.TokenCredential, quota IQuota) error {
  137. err := manager.TableSpec().InsertOrUpdate(ctx, quota)
  138. if err != nil {
  139. return errors.Wrap(err, "InsertOrUpdate")
  140. }
  141. if manager.nonNegative {
  142. quota.ResetNegative()
  143. }
  144. return nil
  145. }
  146. func (manager *SQuotaBaseManager) addQuotaInternal(ctx context.Context, userCred mcclient.TokenCredential, diff IQuota) error {
  147. keys := diff.GetKeys()
  148. quota := manager.newQuota()
  149. quota.SetKeys(keys)
  150. err := manager.getQuotaByKeys(ctx, keys, quota)
  151. if err != nil {
  152. if errors.Cause(err) == sql.ErrNoRows {
  153. // insert one
  154. } else {
  155. return errors.Wrap(err, "manager.getQuotaByKeys")
  156. }
  157. }
  158. quota.Add(diff)
  159. return manager.setQuotaInternal(ctx, userCred, quota)
  160. }
  161. func (manager *SQuotaBaseManager) subQuotaInternal(ctx context.Context, userCred mcclient.TokenCredential, diff IQuota) error {
  162. keys := diff.GetKeys()
  163. quota := manager.newQuota()
  164. quota.SetKeys(keys)
  165. err := manager.getQuotaByKeys(ctx, keys, quota)
  166. if err != nil {
  167. if errors.Cause(err) == sql.ErrNoRows {
  168. // insert one
  169. } else {
  170. return errors.Wrap(err, "manager.getQuotaByKeys")
  171. }
  172. }
  173. quota.Sub(diff)
  174. return manager.setQuotaInternal(ctx, userCred, quota)
  175. }
  176. func (manager *SQuotaBaseManager) deleteQuotaByKeys(ctx context.Context, userCred mcclient.TokenCredential, keys IQuotaKeys) error {
  177. quota := manager.newQuota()
  178. quota.SetKeys(keys)
  179. _, err := db.Update(quota.(db.IModel), func() error {
  180. return quota.(db.IModel).MarkDelete()
  181. })
  182. if err != nil {
  183. if errors.Cause(err) != sql.ErrNoRows {
  184. return errors.Wrap(err, "Delete")
  185. }
  186. }
  187. return nil
  188. }
  189. func (manager *SQuotaBaseManager) deleteAllQuotas(ctx context.Context, userCred mcclient.TokenCredential, keys IQuotaKeys) error {
  190. quotas, err := manager.getQuotasInternal(ctx, keys, false)
  191. if err != nil {
  192. if errors.Cause(err) != sql.ErrNoRows {
  193. return errors.Wrap(err, "manager.getQuotasInternal")
  194. } else {
  195. return nil
  196. }
  197. }
  198. for i := range quotas {
  199. err := manager.deleteQuotaByKeys(ctx, userCred, quotas[i].GetKeys())
  200. if err != nil {
  201. if errors.Cause(err) != sql.ErrNoRows {
  202. return errors.Wrapf(err, "manager.deleteQuotaByKeys %s", QuotaKeyString(quotas[i].GetKeys()))
  203. }
  204. }
  205. }
  206. return nil
  207. }
  208. func (manager *SQuotaBaseManager) InitializeData() error {
  209. q := manager.Query()
  210. quotaCnt, err := q.CountWithError()
  211. if err != nil {
  212. return errors.Wrap(err, "SQuotaManager.CountWithError")
  213. }
  214. if quotaCnt > 0 {
  215. // initlaized, quit
  216. return nil
  217. }
  218. log.Debugf("%s", q.String())
  219. metaQuota := newDBQuotaStore()
  220. tenants := make([]db.STenant, 0)
  221. err = db.TenantCacheManager.Query().All(&tenants)
  222. if err != nil && err != sql.ErrNoRows {
  223. return errors.Wrap(err, "Query")
  224. }
  225. for i := range tenants {
  226. obj := tenants[i]
  227. var scope rbacscope.TRbacScope
  228. var ownerId mcclient.IIdentityProvider
  229. if obj.DomainId == identityapi.KeystoneDomainRoot {
  230. // domain
  231. scope = rbacscope.ScopeDomain
  232. ownerId = &db.SOwnerId{
  233. DomainId: tenants[i].Id,
  234. Domain: tenants[i].Name,
  235. }
  236. } else {
  237. // project
  238. scope = rbacscope.ScopeProject
  239. ownerId = &db.SOwnerId{
  240. DomainId: tenants[i].DomainId,
  241. Domain: tenants[i].Domain,
  242. ProjectId: tenants[i].Id,
  243. Project: tenants[i].Name,
  244. }
  245. }
  246. quota := manager.newQuota()
  247. var baseKeys IQuotaKeys
  248. if manager.scope == rbacscope.ScopeDomain {
  249. baseKeys = OwnerIdDomainQuotaKeys(ownerId)
  250. } else {
  251. baseKeys = OwnerIdProjectQuotaKeys(scope, ownerId)
  252. }
  253. if !reflectutils.FillEmbededStructValue(reflect.Indirect(reflect.ValueOf(quota)), reflect.ValueOf(baseKeys)) {
  254. log.Fatalf("invalid quota??? fail to find SBaseQuotaKey")
  255. }
  256. err := metaQuota.GetQuota(ctx.CtxWithTime(), scope, ownerId, quota)
  257. if err != nil && err != sql.ErrNoRows {
  258. log.Errorf("metaQuota.GetQuota error %s for %s", err, ownerId)
  259. continue
  260. }
  261. if quota.IsEmpty() {
  262. quota.FetchSystemQuota()
  263. }
  264. err = manager.TableSpec().Insert(ctx.CtxWithTime(), quota)
  265. if err != nil {
  266. log.Errorf("%s insert error %s", manager.KeywordPlural(), err)
  267. continue
  268. } else {
  269. log.Infof("Insert %s", quota)
  270. }
  271. }
  272. return nil
  273. }