quotas.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  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 models
  15. import (
  16. "context"
  17. "fmt"
  18. "yunion.io/x/jsonutils"
  19. "yunion.io/x/pkg/errors"
  20. "yunion.io/x/pkg/tristate"
  21. "yunion.io/x/pkg/util/rbacscope"
  22. "yunion.io/x/onecloud/pkg/apis"
  23. api "yunion.io/x/onecloud/pkg/apis/compute"
  24. identityapi "yunion.io/x/onecloud/pkg/apis/identity"
  25. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  26. "yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
  27. commonOptions "yunion.io/x/onecloud/pkg/cloudcommon/options"
  28. "yunion.io/x/onecloud/pkg/compute/options"
  29. "yunion.io/x/onecloud/pkg/mcclient"
  30. "yunion.io/x/onecloud/pkg/mcclient/auth"
  31. "yunion.io/x/onecloud/pkg/mcclient/utils"
  32. "yunion.io/x/onecloud/pkg/util/rbacutils"
  33. )
  34. type SQuotaManager struct {
  35. quotas.SQuotaBaseManager
  36. }
  37. var (
  38. Quota SQuota
  39. QuotaManager *SQuotaManager
  40. QuotaUsageManager *SQuotaManager
  41. QuotaPendingUsageManager *SQuotaManager
  42. )
  43. func init() {
  44. Quota = SQuota{}
  45. QuotaUsageManager = &SQuotaManager{
  46. SQuotaBaseManager: quotas.NewQuotaUsageManager(Quota,
  47. rbacscope.ScopeProject,
  48. "quota_usage_tbl",
  49. "quota_usage",
  50. "quota_usages",
  51. ),
  52. }
  53. QuotaPendingUsageManager = &SQuotaManager{
  54. SQuotaBaseManager: quotas.NewQuotaUsageManager(Quota,
  55. rbacscope.ScopeProject,
  56. "quota_pending_usage_tbl",
  57. "quota_pending_usage",
  58. "quota_pending_usages",
  59. ),
  60. }
  61. QuotaManager = &SQuotaManager{
  62. SQuotaBaseManager: quotas.NewQuotaBaseManager(Quota,
  63. rbacscope.ScopeProject,
  64. "quota_tbl",
  65. QuotaPendingUsageManager,
  66. QuotaUsageManager,
  67. "quota",
  68. "quotas",
  69. ),
  70. }
  71. quotas.Register(QuotaManager)
  72. }
  73. type SQuota struct {
  74. quotas.SQuotaBase
  75. SComputeResourceKeys
  76. // 主机数量配额
  77. Count int `default:"-1" allow_zero:"true" json:"count"`
  78. // 主机CPU核数量配额
  79. Cpu int `default:"-1" allow_zero:"true" json:"cpu"`
  80. // 主机内存容量配额
  81. Memory int `default:"-1" allow_zero:"true" json:"memory"`
  82. // 主机存储容量配额
  83. Storage int `default:"-1" allow_zero:"true" json:"storage"`
  84. // 主机组配额
  85. Group int `default:"-1" allow_zero:"true" json:"group"`
  86. // 直通设备(GPU)配额
  87. IsolatedDevice int `default:"-1" allow_zero:"true" json:"isolated_device"`
  88. }
  89. func (self *SQuota) GetKeys() quotas.IQuotaKeys {
  90. return self.SComputeResourceKeys
  91. }
  92. func (self *SQuota) SetKeys(keys quotas.IQuotaKeys) {
  93. self.SComputeResourceKeys = keys.(SComputeResourceKeys)
  94. }
  95. func (self *SQuota) FetchSystemQuota() {
  96. keys := self.SComputeResourceKeys
  97. base := 0
  98. switch options.Options.DefaultQuotaValue {
  99. case commonOptions.DefaultQuotaUnlimit:
  100. base = -1
  101. case commonOptions.DefaultQuotaZero:
  102. base = 0
  103. if keys.Scope() == rbacscope.ScopeDomain { // domain level quota
  104. base = 10
  105. } else if keys.DomainId == identityapi.DEFAULT_DOMAIN_ID && keys.ProjectId == auth.AdminCredential().GetProjectId() {
  106. base = 1
  107. }
  108. case commonOptions.DefaultQuotaDefault:
  109. base = 1
  110. if keys.Scope() == rbacscope.ScopeDomain {
  111. base = 10
  112. }
  113. }
  114. defaultValue := func(def int) int {
  115. if base < 0 {
  116. return -1
  117. } else {
  118. return def * base
  119. }
  120. }
  121. self.Count = defaultValue(options.Options.DefaultServerQuota)
  122. self.Cpu = defaultValue(options.Options.DefaultCpuQuota)
  123. self.Memory = defaultValue(options.Options.DefaultMemoryQuota)
  124. self.Storage = defaultValue(options.Options.DefaultStorageQuota)
  125. self.Group = defaultValue(options.Options.DefaultGroupQuota)
  126. self.IsolatedDevice = defaultValue(options.Options.DefaultIsolatedDeviceQuota)
  127. }
  128. func (self *SQuota) FetchUsage(ctx context.Context) error {
  129. keys := self.SComputeResourceKeys
  130. scope := keys.Scope()
  131. ownerId := keys.OwnerId()
  132. rangeObjs := make([]db.IStandaloneModel, 0)
  133. if len(keys.ManagerId) > 0 {
  134. obj, err := CloudproviderManager.FetchById(keys.ManagerId)
  135. if err != nil {
  136. return errors.Wrap(err, "CloudproviderManager.FetchById")
  137. }
  138. rangeObjs = append(rangeObjs, obj.(db.IStandaloneModel))
  139. } else if len(keys.AccountId) > 0 {
  140. obj, err := CloudaccountManager.FetchById(keys.AccountId)
  141. if err != nil {
  142. return errors.Wrap(err, "CloudaccountManager.FetchById")
  143. }
  144. rangeObjs = append(rangeObjs, obj.(db.IStandaloneModel))
  145. }
  146. if len(keys.ZoneId) > 0 {
  147. obj, err := ZoneManager.FetchById(keys.ZoneId)
  148. if err != nil {
  149. return errors.Wrap(err, "ZoneManager.FetchById")
  150. }
  151. rangeObjs = append(rangeObjs, obj.(db.IStandaloneModel))
  152. } else if len(keys.RegionId) > 0 {
  153. obj, err := CloudregionManager.FetchById(keys.RegionId)
  154. if err != nil {
  155. return errors.Wrap(err, "CloudregionManager.FetchById")
  156. }
  157. rangeObjs = append(rangeObjs, obj.(db.IStandaloneModel))
  158. }
  159. var hypervisors []string
  160. if len(keys.Hypervisor) > 0 {
  161. hypervisors = []string{keys.Hypervisor}
  162. }
  163. var providers []string
  164. if len(keys.Provider) > 0 {
  165. providers = []string{keys.Provider}
  166. }
  167. var brands []string
  168. if len(keys.Brand) > 0 {
  169. brands = []string{keys.Brand}
  170. }
  171. diskSize := totalDiskSize(scope, ownerId, tristate.None, tristate.None, false, false, rangeObjs, providers, brands, keys.CloudEnv, hypervisors)
  172. guest := usageTotalGuestResourceCountByArch(ctx, scope, ownerId, rangeObjs, nil, hypervisors, false, false, nil, nil, providers, brands, keys.CloudEnv, nil, rbacutils.SPolicyResult{}, apis.OS_ARCH_ALL)
  173. self.Count = guest.TotalGuestCount
  174. self.Cpu = guest.TotalCpuCount
  175. self.Memory = guest.TotalMemSize
  176. self.Storage = diskSize
  177. self.Group = 0
  178. self.IsolatedDevice = guest.TotalIsolatedCount
  179. return nil
  180. }
  181. func (self *SQuota) ResetNegative() {
  182. if self.Count < 0 {
  183. self.Count = 0
  184. }
  185. if self.Cpu < 0 {
  186. self.Cpu = 0
  187. }
  188. if self.Memory < 0 {
  189. self.Memory = 0
  190. }
  191. if self.Storage < 0 {
  192. self.Storage = 0
  193. }
  194. if self.Group < 0 {
  195. self.Group = 0
  196. }
  197. if self.IsolatedDevice < 0 {
  198. self.IsolatedDevice = 0
  199. }
  200. }
  201. func (self *SQuota) IsEmpty() bool {
  202. if self.Count > 0 {
  203. return false
  204. }
  205. if self.Cpu > 0 {
  206. return false
  207. }
  208. if self.Memory > 0 {
  209. return false
  210. }
  211. if self.Storage > 0 {
  212. return false
  213. }
  214. if self.Group > 0 {
  215. return false
  216. }
  217. if self.IsolatedDevice > 0 {
  218. return false
  219. }
  220. return true
  221. }
  222. func (self *SQuota) Add(quota quotas.IQuota) {
  223. squota := quota.(*SQuota)
  224. self.Count = self.Count + quotas.NonNegative(squota.Count)
  225. self.Cpu = self.Cpu + quotas.NonNegative(squota.Cpu)
  226. self.Memory = self.Memory + quotas.NonNegative(squota.Memory)
  227. self.Storage = self.Storage + quotas.NonNegative(squota.Storage)
  228. self.Group = self.Group + quotas.NonNegative(squota.Group)
  229. self.IsolatedDevice = self.IsolatedDevice + quotas.NonNegative(squota.IsolatedDevice)
  230. }
  231. func nonNegative(val int) int {
  232. return quotas.NonNegative(val)
  233. }
  234. func (self *SQuota) Sub(quota quotas.IQuota) {
  235. squota := quota.(*SQuota)
  236. self.Count = nonNegative(self.Count - squota.Count)
  237. self.Cpu = nonNegative(self.Cpu - squota.Cpu)
  238. self.Memory = nonNegative(self.Memory - squota.Memory)
  239. self.Storage = nonNegative(self.Storage - squota.Storage)
  240. self.Group = nonNegative(self.Group - squota.Group)
  241. self.IsolatedDevice = nonNegative(self.IsolatedDevice - squota.IsolatedDevice)
  242. }
  243. func (self *SQuota) Allocable(request quotas.IQuota) int {
  244. squota := request.(*SQuota)
  245. cnt := -1
  246. if self.Count >= 0 && squota.Count > 0 && (cnt < 0 || cnt > self.Count/squota.Count) {
  247. cnt = self.Count / squota.Count
  248. }
  249. if self.Cpu >= 0 && squota.Cpu > 0 && (cnt < 0 || cnt > self.Cpu/squota.Cpu) {
  250. cnt = self.Cpu / squota.Cpu
  251. }
  252. if self.Memory >= 0 && squota.Memory > 0 && (cnt < 0 || cnt > self.Memory/squota.Memory) {
  253. cnt = self.Memory / squota.Memory
  254. }
  255. if self.Storage >= 0 && squota.Storage > 0 && (cnt < 0 || cnt > self.Storage/squota.Storage) {
  256. cnt = self.Storage / squota.Storage
  257. }
  258. if self.Group >= 0 && squota.Group > 0 && (cnt < 0 || cnt > self.Group/squota.Group) {
  259. cnt = self.Group / squota.Group
  260. }
  261. if self.IsolatedDevice >= 0 && squota.IsolatedDevice > 0 && (cnt < 0 || cnt > self.IsolatedDevice/squota.IsolatedDevice) {
  262. cnt = self.IsolatedDevice / squota.IsolatedDevice
  263. }
  264. return cnt
  265. }
  266. func (self *SQuota) Update(quota quotas.IQuota) {
  267. squota := quota.(*SQuota)
  268. if squota.Count > 0 {
  269. self.Count = squota.Count
  270. }
  271. if squota.Cpu > 0 {
  272. self.Cpu = squota.Cpu
  273. }
  274. if squota.Memory > 0 {
  275. self.Memory = squota.Memory
  276. }
  277. if squota.Storage > 0 {
  278. self.Storage = squota.Storage
  279. }
  280. if squota.Group > 0 {
  281. self.Group = squota.Group
  282. }
  283. if squota.IsolatedDevice > 0 {
  284. self.IsolatedDevice = squota.IsolatedDevice
  285. }
  286. }
  287. func (used *SQuota) Exceed(request quotas.IQuota, quota quotas.IQuota) error {
  288. err := quotas.NewOutOfQuotaError()
  289. sreq := request.(*SQuota)
  290. squota := quota.(*SQuota)
  291. if quotas.Exceed(used.Count, sreq.Count, squota.Count) {
  292. err.Add(used, "count", squota.Count, used.Count, sreq.Count)
  293. }
  294. if quotas.Exceed(used.Cpu, sreq.Cpu, squota.Cpu) {
  295. err.Add(used, "cpu", squota.Cpu, used.Cpu, sreq.Cpu)
  296. }
  297. if quotas.Exceed(used.Memory, sreq.Memory, squota.Memory) {
  298. err.Add(used, "memory", squota.Memory, used.Memory, sreq.Memory)
  299. }
  300. if quotas.Exceed(used.Storage, sreq.Storage, squota.Storage) {
  301. err.Add(used, "storage", squota.Storage, used.Storage, sreq.Storage)
  302. }
  303. if quotas.Exceed(used.Group, sreq.Group, squota.Group) {
  304. err.Add(used, "group", squota.Group, used.Group, sreq.Group)
  305. }
  306. if quotas.Exceed(used.IsolatedDevice, sreq.IsolatedDevice, squota.IsolatedDevice) {
  307. err.Add(used, "isolated_device", squota.IsolatedDevice, used.IsolatedDevice, sreq.IsolatedDevice)
  308. }
  309. if err.IsError() {
  310. return err
  311. } else {
  312. return nil
  313. }
  314. }
  315. func keyName(prefix, name string) string {
  316. if len(prefix) > 0 {
  317. return fmt.Sprintf("%s.%s", prefix, name)
  318. } else {
  319. return name
  320. }
  321. }
  322. func (self *SQuota) ToJSON(prefix string) jsonutils.JSONObject {
  323. ret := jsonutils.NewDict()
  324. ret.Add(jsonutils.NewInt(int64(self.Count)), keyName(prefix, "count"))
  325. ret.Add(jsonutils.NewInt(int64(self.Cpu)), keyName(prefix, "cpu"))
  326. ret.Add(jsonutils.NewInt(int64(self.Memory)), keyName(prefix, "memory"))
  327. ret.Add(jsonutils.NewInt(int64(self.Storage)), keyName(prefix, "storage"))
  328. ret.Add(jsonutils.NewInt(int64(self.Group)), keyName(prefix, "group"))
  329. ret.Add(jsonutils.NewInt(int64(self.IsolatedDevice)), keyName(prefix, "isolated_device"))
  330. return ret
  331. }
  332. func (manager *SQuotaManager) FetchIdNames(ctx context.Context, idMap map[string]map[string]string) (map[string]map[string]string, error) {
  333. for field := range idMap {
  334. switch field {
  335. case "domain_id":
  336. fieldIdMap, err := utils.FetchDomainNames(ctx, idMap[field])
  337. if err != nil {
  338. return nil, errors.Wrap(err, "utils.FetchDomainNames")
  339. }
  340. idMap[field] = fieldIdMap
  341. case "tenant_id":
  342. fieldIdMap, err := utils.FetchTenantNames(ctx, idMap[field])
  343. if err != nil {
  344. return nil, errors.Wrap(err, "utils.FetchTenantNames")
  345. }
  346. idMap[field] = fieldIdMap
  347. case "region_id":
  348. fieldIdMap, err := fetchRegionNames(idMap[field])
  349. if err != nil {
  350. return nil, errors.Wrap(err, "fetchRegionNames")
  351. }
  352. idMap[field] = fieldIdMap
  353. case "zone_id":
  354. fieldIdMap, err := fetchZoneNames(idMap[field])
  355. if err != nil {
  356. return nil, errors.Wrap(err, "fetchZoneNames")
  357. }
  358. idMap[field] = fieldIdMap
  359. case "account_id":
  360. fieldIdMap, err := fetchAccountNames(idMap[field])
  361. if err != nil {
  362. return nil, errors.Wrap(err, "fetchAccountNames")
  363. }
  364. idMap[field] = fieldIdMap
  365. case "manager_id":
  366. fieldIdMap, err := fetchManagerNames(idMap[field])
  367. if err != nil {
  368. return nil, errors.Wrap(err, "fetchManagerNames")
  369. }
  370. idMap[field] = fieldIdMap
  371. }
  372. }
  373. return idMap, nil
  374. }
  375. func fetchRegionNames(idMap map[string]string) (map[string]string, error) {
  376. return db.FetchIdNameMap(CloudregionManager, idMap)
  377. }
  378. func fetchZoneNames(idMap map[string]string) (map[string]string, error) {
  379. return db.FetchIdNameMap(ZoneManager, idMap)
  380. }
  381. func fetchAccountNames(idMap map[string]string) (map[string]string, error) {
  382. return db.FetchIdNameMap(CloudaccountManager, idMap)
  383. }
  384. func fetchManagerNames(idMap map[string]string) (map[string]string, error) {
  385. return db.FetchIdNameMap(CloudproviderManager, idMap)
  386. }
  387. type SComputeResourceKeys struct {
  388. quotas.SZonalCloudResourceKeys
  389. // 主机配额适用的主机类型,参考主机List的Hypervisor列表
  390. Hypervisor string `width:"16" charset:"ascii" nullable:"false" primary:"true" list:"user"`
  391. }
  392. func (k SComputeResourceKeys) Fields() []string {
  393. return append(k.SZonalCloudResourceKeys.Fields(), "hypervisor")
  394. }
  395. func (k SComputeResourceKeys) Values() []string {
  396. return append(k.SZonalCloudResourceKeys.Values(), k.Hypervisor)
  397. }
  398. func (k1 SComputeResourceKeys) Compare(ik quotas.IQuotaKeys) int {
  399. k2 := ik.(SComputeResourceKeys)
  400. r := k1.SZonalCloudResourceKeys.Compare(k2.SZonalCloudResourceKeys)
  401. if r != 0 {
  402. return r
  403. }
  404. if k1.Hypervisor < k2.Hypervisor {
  405. return -1
  406. } else if k1.Hypervisor > k2.Hypervisor {
  407. return 1
  408. }
  409. return 0
  410. }
  411. func fetchCloudQuotaKeys(scope rbacscope.TRbacScope, ownerId mcclient.IIdentityProvider, manager *SCloudprovider) quotas.SCloudResourceKeys {
  412. keys := quotas.SCloudResourceKeys{}
  413. keys.SBaseProjectQuotaKeys = quotas.OwnerIdProjectQuotaKeys(scope, ownerId)
  414. if manager != nil {
  415. keys.ManagerId = manager.Id
  416. account, _ := manager.GetCloudaccount()
  417. if account != nil {
  418. keys.Provider = account.Provider
  419. keys.Brand = account.Brand
  420. keys.CloudEnv = account.GetCloudEnv()
  421. keys.AccountId = account.Id
  422. }
  423. } else {
  424. keys.Provider = api.CLOUD_PROVIDER_ONECLOUD
  425. keys.Brand = api.ONECLOUD_BRAND_ONECLOUD
  426. keys.CloudEnv = api.CLOUD_ENV_ON_PREMISE
  427. }
  428. return keys
  429. }
  430. func fetchRegionalQuotaKeys(scope rbacscope.TRbacScope, ownerId mcclient.IIdentityProvider, region *SCloudregion, manager *SCloudprovider) quotas.SRegionalCloudResourceKeys {
  431. keys := quotas.SRegionalCloudResourceKeys{}
  432. keys.SCloudResourceKeys = fetchCloudQuotaKeys(scope, ownerId, manager)
  433. if region != nil {
  434. keys.RegionId = region.Id
  435. }
  436. return keys
  437. }
  438. func fetchZonalQuotaKeys(scope rbacscope.TRbacScope, ownerId mcclient.IIdentityProvider, zone *SZone, manager *SCloudprovider) quotas.SZonalCloudResourceKeys {
  439. keys := quotas.SZonalCloudResourceKeys{}
  440. keys.SCloudResourceKeys = fetchCloudQuotaKeys(scope, ownerId, manager)
  441. if zone != nil {
  442. keys.RegionId = zone.CloudregionId
  443. keys.ZoneId = zone.Id
  444. }
  445. return keys
  446. }
  447. func fetchComputeQuotaKeys(scope rbacscope.TRbacScope, ownerId mcclient.IIdentityProvider, zone *SZone, manager *SCloudprovider, hypervisor string) SComputeResourceKeys {
  448. keys := SComputeResourceKeys{}
  449. keys.SZonalCloudResourceKeys = fetchZonalQuotaKeys(scope, ownerId, zone, manager)
  450. keys.Hypervisor = hypervisor
  451. return keys
  452. }