winterhazel opened a new pull request, #12961:
URL: https://github.com/apache/cloudstack/pull/12961
### Description
Currently, the `quotaBalance` API receives the `startdate` and `enddate`
parameters, and returns what an account's balance was only at the starting and
end dates, alongside with some information about the credits added during that
period. It is not possible to view how an account's balance changed through
multiple days with a single API call, even though balance entries exist in the
database containing this information. Also, returning the credits addition
history is redundant, as `quotaCreditsList` already exists and returns a more
complete response, and inconsistent with the API's purpose (simply viewing an
account's balance).
Hence, this PR proposes changing the behavior of `quotaBalance` to allow
seeing the complete history of an account/project's balance for the specified
period, making it easier to address the 2nd suggestion of #11805. In addition,
the credits addition history was removed from the API's response. UI changes
will be done in a separate PR.
### Types of changes
- [x] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] Enhancement (improves an existing feature and functionality)
- [x] Cleanup (Code refactoring and cleanup, that may add test cases)
### Feature/Enhancement Scale or Bug Severity
#### Feature/Enhancement Scale
- [ ] Major
- [X] Minor
### How Has This Been Tested?
I tested the API by fuzzing its parameters (`account`, `accountid`,
`domainid`, `enddate`, `projectid`, `startdate`) for root admin, domain admin,
and user accounts. In the tests, I validated that the API returned the expected
response while performing correct permission checking.
---
### Environment configuration
My environment had two domains: `ROOT` and `d1`.
```sql
MariaDB [cloud]> select uuid,name,path,parent from domain;
+--------------------------------------+------+------+--------+
| uuid | name | path | parent |
+--------------------------------------+------+------+--------+
| de3f3dfb-f84b-11f0-8ace-32e0826870ba | ROOT | / | NULL |
| dc5064ad-d4a8-4bb3-ae18-a52e4810ac3d | d1 | /d1/ | 1 |
+--------------------------------------+------+------+--------+
```
Each domain had a domain admin (`dr` for `ROOT` and `d1` for `d1`) and a
user account (`ur` for `ROOT` and `u1` for `d1`). `ROOT` also had a project and
the root admin.
```sql
MariaDB [cloud]> select uuid,account_name,domain_id,role_id from account;
+--------------------------------------+--------------------------+-----------+---------+
| uuid | account_name |
domain_id | role_id |
+--------------------------------------+--------------------------+-----------+---------+
| f684aae8-f84b-11f0-8ace-32e0826870ba | system |
1 | 1 |
| f684da1a-f84b-11f0-8ace-32e0826870ba | admin |
1 | 1 |
| 17dfdcce-fd02-46d0-85ae-7de91fda634c | baremetal-system-account |
1 | 4 |
| aaba75a0-a575-41be-80e1-dbea14c10e51 | dr |
1 | 3 |
| feaffae2-7ba4-45dc-b46f-20ae86192685 | d1 |
2 | 3 |
| 2f49212d-d6d5-437b-800d-a7bb4a8def4b | u1 |
2 | 4 |
| b029e8b8-e539-4d1a-906d-3d4f2fb36b82 | ur |
1 | 4 |
| b14100c6-d9f6-450e-87a2-0833bb45e5f8 | PrjAcct-aa-1 |
1 | NULL |
+--------------------------------------+--------------------------+-----------+---------+
```
---
### Test 1 (user accounts)
- No parameters: returns only the user account's last balance statement.
```sh
(u1) 🐱 > quota balance
{
"balance": {
"balances": [
{
"balance": 26.74781192,
"date": "2026-04-09T20:59:59-0300"
}
],
"currency": "$"
}
}
```
- `account`: allows listing their own balance statements, but not the
balance statements of other accounts.
```sh
(u1) 🐱 > quota balance account=u1
domainid=dc5064ad-d4a8-4bb3-ae18-a52e4810ac3d
{
"balance": {
"balances": [
{
"balance": 26.74781192,
"date": "2026-04-09T20:59:59-0300"
}
],
"currency": "$"
}
}
(u1) 🐱 > quota balance account=d1
domainid=dc5064ad-d4a8-4bb3-ae18-a52e4810ac3d
🙈 Error: (HTTP 531, error code 4365) Caller does not have permission to
operate with provided resource.
```
- `accountid`: allows listing their own balance statements, but not the
balance statements of other accounts.
```sh
(u1) 🐱 > quota balance accountid=2f49212d-d6d5-437b-800d-a7bb4a8def4b
{
"balance": {
"balances": [
{
"balance": 26.74781192,
"date": "2026-04-09T20:59:59-0300"
}
],
"currency": "$"
}
}
(u1) 🐱 > quota balance accountid=aaba75a0-a575-41be-80e1-dbea14c10e51
🙈 Error: (HTTP 531, error code 4365) Caller does not have permission to
operate with provided resource.
```
- `domainid`: only lists their own balance statements. Returns an error when
attempting to obtain the balance of another account. Unable to be used alone.
```sh
(u1) 🐱 > quota balance domainid=dc5064ad-d4a8-4bb3-ae18-a52e4810ac3d
account=u1
{
"balance": {
"balances": [
{
"balance": 29.13815933,
"date": "2026-04-11T20:59:59-0300"
}
],
"currency": "$"
}
}
(u1) 🐱 > quota balance domainid=de3f3dfb-f84b-11f0-8ace-32e0826870ba
account=admin
🙈 Error: (HTTP 531, error code 4365) Caller does not have permission to
operate with provided resource.
(u1) 🐱 > quota balance domainid=dc5064ad-d4a8-4bb3-ae18-a52e4810ac3d
🙈 Error: (HTTP 431, error code 9999) Both account and domainid are
needed if using either. Consider using accountid instead.
```
- `startdate` and `enddate`: filters balance statements correctly, only
returning the calling account's own statements.
```sh
(u1) 🐱 > quota balance startdate=2026-03-01 enddate=2026-04-10
{
"balance": {
"balances": [
{
"balance": 24,
"date": "2026-04-03T21:00:00-0300"
},
{
"balance": 33.41447912,
"date": "2026-04-04T20:59:59-0300"
},
{
"balance": 31.41447896,
"date": "2026-04-05T20:59:59-0300"
},
{
"balance": 29.4144788,
"date": "2026-04-06T20:59:59-0300"
},
{
"balance": 28.08114536,
"date": "2026-04-07T20:59:59-0300"
},
{
"balance": 27.41447864,
"date": "2026-04-08T20:59:59-0300"
},
{
"balance": 26.74781192,
"date": "2026-04-09T20:59:59-0300"
}
],
"currency": "$"
}
}
(u1) 🐱 > quota balance startdate="2026-03-01T00:00:00-0300"
enddate="2026-04-08T12:00:00-0300"
{
"balance": {
"balances": [
{
"balance": 24,
"date": "2026-04-03T21:00:00-0300"
},
{
"balance": 33.41447912,
"date": "2026-04-04T20:59:59-0300"
},
{
"balance": 31.41447896,
"date": "2026-04-05T20:59:59-0300"
},
{
"balance": 29.4144788,
"date": "2026-04-06T20:59:59-0300"
},
{
"balance": 28.08114536,
"date": "2026-04-07T20:59:59-0300"
}
],
"currency": "$"
}
}
(u1) 🐱 > quota balance startdate="2026-04-04T00:00:00-0300"
enddate="2026-04-08T12:00:00-0300"
{
"balance": {
"balances": [
{
"balance": 33.41447912,
"date": "2026-04-04T20:59:59-0300"
},
{
"balance": 31.41447896,
"date": "2026-04-05T20:59:59-0300"
},
{
"balance": 29.4144788,
"date": "2026-04-06T20:59:59-0300"
},
{
"balance": 28.08114536,
"date": "2026-04-07T20:59:59-0300"
}
],
"currency": "$"
}
}
```
- `enddate` before `startdate`: returns an error.
```sh
(u1) 🐱 > quota balance startdate=2026-03-02 enddate=2026-03-01
🙈 Error: (HTTP 431, error code 4350) The start date cannot be after the
end date.
```
- `projectid`: user accounts can only obtain balance statements for projects
they belong to.
```sh
(u1) 🐱 > quota balance projectid=c1ce41c4-7b01-4386-8264-479db5ace0bc
🙈 Error: (HTTP 432, error code 9999) The API [quotaBalance] does not
exist or is not available for this account/user in project
[c1ce41c4-7b01-4386-8264-479db5ace0bc].
(ur) 🐱 > quota balance projectid=c1ce41c4-7b01-4386-8264-479db5ace0bc
{
"balance": {
"balances": [
{
"balance": 8.02719503,
"date": "2026-04-11T20:59:59-0300"
}
],
"currency": "$"
}
}
```
- `projectid` combined with `accountid`: returns an error since these
parameters cannot be specified together.
```sh
(ur) 🐱 > quota balance projectid=c1ce41c4-7b01-4386-8264-479db5ace0bc
accountid=b029e8b8-e539-4d1a-906d-3d4f2fb36b82
🙈 Error: (HTTP 431, error code 9999) Project and account can not be
specified together.
```
---
### Test 2 (domain admins)
- No parameters: returns their last balance statement.
```sh
(d1) 🐱 > quota balance
{
"balance": {
"balances": [
{
"balance": 16.13743789,
"date": "2026-04-11T20:59:59-0300"
}
],
"currency": "$"
}
}
```
- `account`: allows listing balance statements of accounts belonging to
their domain, but not accounts from other domains.
```sh
(d1) 🐱 > quota balance account=u1
domainid=dc5064ad-d4a8-4bb3-ae18-a52e4810ac3d
{
"balance": {
"balances": [
{
"balance": 29.13815933,
"date": "2026-04-11T20:59:59-0300"
}
],
"currency": "$"
}
}
(d1) 🐱 > quota balance account=d1
domainid=dc5064ad-d4a8-4bb3-ae18-a52e4810ac3d
{
"balance": {
"balances": [
{
"balance": 16.13743789,
"date": "2026-04-11T20:59:59-0300"
}
],
"currency": "$"
}
}
(d1) 🐱 > quota balance account=admin
domainid=de3f3dfb-f84b-11f0-8ace-32e0826870ba
🙈 Error: (HTTP 531, error code 4365) Caller does not have permission to
operate with provided resource.
```
- `accountid`: allows listing balance statements of accounts belonging to
their domain, but not accounts from other domains.
```sh
(d1) 🐱 > quota balance accountid=2f49212d-d6d5-437b-800d-a7bb4a8def4b
{
"balance": {
"balances": [
{
"balance": 29.13815933,
"date": "2026-04-11T20:59:59-0300"
}
],
"currency": "$"
}
}
(d1) 🐱 > quota balance accountid=feaffae2-7ba4-45dc-b46f-20ae86192685
{
"balance": {
"balances": [
{
"balance": 16.13743789,
"date": "2026-04-11T20:59:59-0300"
}
],
"currency": "$"
}
}
(d1) 🐱 > quota balance accountid=f684da1a-f84b-11f0-8ace-32e0826870ba
🙈 Error: (HTTP 531, error code 4365) Caller does not have permission to
operate with provided resource.
```
- `domainid`: allows listing balance statements of accounts in their domain,
but returns an error for accounts outside it.
```sh
(d1) 🐱 > quota balance account=u1
domainid=dc5064ad-d4a8-4bb3-ae18-a52e4810ac3d
{
"balance": {
"balances": [
{
"balance": 29.13815933,
"date": "2026-04-11T20:59:59-0300"
}
],
"currency": "$"
}
}
(d1) 🐱 > quota balance domainid=de3f3dfb-f84b-11f0-8ace-32e0826870ba
account=admin
🙈 Error: (HTTP 531, error code 4365) Caller does not have permission to
operate with provided resource.
```
- `startdate` and `enddate`: filters balance statements correctly.
```sh
(d1) 🐱 > quota balance startdate=2026-04-10 enddate=2026-04-11
{
"balance": {
"balances": [
{
"balance": 16.80410461,
"date": "2026-04-10T20:59:59-0300"
},
{
"balance": 16.13743789,
"date": "2026-04-11T20:59:59-0300"
}
],
"currency": "$"
}
}
```
- `projectid`: allows listing balance statements for projects belonging to
their domain, but returns an error for projects outside it.
```sh
(d1) 🐱 > quota balance projectid=c1ce41c4-7b01-4386-8264-479db5ace0bc
🙈 Error: (HTTP 531, error code 4365) Account Account
[{"accountName":"d1","id":5,"uuid":"feaffae2-7ba4-45dc-b46f-20ae86192685"}]
does not have permission to operate within domain
id=de3f3dfb-f84b-11f0-8ace-32e0826870ba
(dr) 🐱 > quota balance projectid=c1ce41c4-7b01-4386-8264-479db5ace0bc
{
"balance": {
"balances": [
{
"balance": 8.02719503,
"date": "2026-04-11T20:59:59-0300"
}
],
"currency": "$"
}
}
```
- `projectid` combined with `accountid`: returns an error.
```sh
(dr) 🐱 > quota balance projectid=c1ce41c4-7b01-4386-8264-479db5ace0bc
accountid=aaba75a0-a575-41be-80e1-dbea14c10e51
🙈 Error: (HTTP 431, error code 9999) Project and account can not be
specified together.
```
---
### Test 3 (root admins)
- No parameters: returns their last balance statement.
```sh
(admin) 🐱 > quota balance
{
"balance": {
"balances": [
{
"balance": 77.33786514,
"date": "2026-04-11T20:59:59-0300"
}
],
"currency": "$"
}
}
```
- `account`: allows listing balance statements of all accounts.
```sh
(admin) 🐱 > quota balance account=dr
domainid=de3f3dfb-f84b-11f0-8ace-32e0826870ba
{
"balance": {
"balances": [],
"currency": "$"
}
}
(admin) 🐱 > quota balance account=u1
domainid=dc5064ad-d4a8-4bb3-ae18-a52e4810ac3d
{
"balance": {
"balances": [
{
"balance": 29.13815933,
"date": "2026-04-11T20:59:59-0300"
}
],
"currency": "$"
}
}
(admin) 🐱 > quota balance account=ur
domainid=dc5064ad-d4a8-4bb3-ae18-a52e4810ac3d
🙈 Error: (HTTP 431, error code 4350) Unable to find account by name
[ur] on domain [2].
```
- `accountid`: allows listing balance statements of all accounts.
```sh
(admin) 🐱 > quota balance accountid=f684da1a-f84b-11f0-8ace-32e0826870ba
{
"balance": {
"balances": [
{
"balance": 77.33786514,
"date": "2026-04-11T20:59:59-0300"
}
],
"currency": "$"
}
}
(admin) 🐱 > quota balance accountid=b029e8b8-e539-4d1a-906d-3d4f2fb36b82
{
"balance": {
"balances": [],
"currency": "$"
}
}
(admin) 🐱 > quota balance accountid=feaffae2-7ba4-45dc-b46f-20ae86192685
{
"balance": {
"balances": [
{
"balance": 16.13743789,
"date": "2026-04-11T20:59:59-0300"
}
],
"currency": "$"
}
}
(admin) 🐱 > quota balance accountid=2f49212d-d6d5-437b-800d-a7bb4a8def4b
{
"balance": {
"balances": [
{
"balance": 29.13815933,
"date": "2026-04-11T20:59:59-0300"
}
],
"currency": "$"
}
}
```
- `domainid`: allows listing balance statements for accounts in all domains.
```sh
(admin) 🐱 > quota balance domainid=de3f3dfb-f84b-11f0-8ace-32e0826870ba
account=admin
{
"balance": {
"balances": [
{
"balance": 77.33786514,
"date": "2026-04-11T20:59:59-0300"
}
],
"currency": "$"
}
}
(admin) 🐱 > quota balance domainid=dc5064ad-d4a8-4bb3-ae18-a52e4810ac3d
account=d1
{
"balance": {
"balances": [
{
"balance": 16.13743789,
"date": "2026-04-11T20:59:59-0300"
}
],
"currency": "$"
}
}
(admin) 🐱 > quota balance domainid=dc5064ad-d4a8-4bb3-ae18-a52e4810ac3d
account=admin
🙈 Error: (HTTP 431, error code 4350) Unable to find account by name
[admin] on domain [2].
```
- `startdate` and `enddate`: filters balance statements correctly.
```sh
(admin) 🐱 > quota balance
accountid=f684da1a-f84b-11f0-8ace-32e0826870ba startdate=2026-04-10
enddate=2026-04-11
{
"balance": {
"balances": [
{
"balance": 77.6711985,
"date": "2026-04-10T20:59:59-0300"
},
{
"balance": 77.33786514,
"date": "2026-04-11T20:59:59-0300"
}
],
"currency": "$"
}
}
```
- `projectid`: allows listing balance statements for all projects in the
environment.
```sh
(admin) 🐱 > quota balance projectid=c1ce41c4-7b01-4386-8264-479db5ace0bc
{
"balance": {
"balances": [
{
"balance": 8.02719503,
"date": "2026-04-11T20:59:59-0300"
}
],
"currency": "$"
}
}
(admin) 🐱 > quota balance projectid=082b7869-dd29-46a5-9350-6edc7d94f825
{
"balance": {
"balances": [],
"currency": "$"
}
}
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]