Skip to content

Commit be95b64

Browse files
authored
Merge pull request ceph#61033 from rhcs-dashboard/rgw-user-accounts-ui
mgr/dashboard: RGW user accounts UI Reviewed-by: Ankush Behl <[email protected]> Reviewed-by: Nizamudeen A <[email protected]>
2 parents edf5f6d + 2159118 commit be95b64

23 files changed

+1641
-48
lines changed

src/pybind/mgr/dashboard/controllers/rgw_iam.py

Lines changed: 89 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,44 +9,121 @@
99
@APIRouter('rgw/accounts', Scope.RGW)
1010
@APIDoc("RGW User Accounts API", "RgwUserAccounts")
1111
class RgwUserAccountsController(RESTController):
12-
12+
@EndpointDoc("Update RGW account info",
13+
parameters={'account_name': (str, 'Account name'),
14+
'email': (str, 'Email'),
15+
'tenant': (str, 'Tenant'),
16+
'max_buckets': (int, 'Max buckets'),
17+
'max_users': (int, 'Max users'),
18+
'max_roles': (int, 'Max roles'),
19+
'max_group': (int, 'Max groups'),
20+
'max_access_keys': (int, 'Max access keys')})
1321
@allow_empty_body
14-
def create(self, account_name: Optional[str] = None,
15-
account_id: Optional[str] = None, email: Optional[str] = None):
16-
return RgwAccounts.create_account(account_name, account_id, email)
22+
def create(self, account_name: str, tenant: Optional[str] = None,
23+
email: Optional[str] = None, max_buckets: Optional[int] = None,
24+
max_users: Optional[int] = None, max_roles: Optional[int] = None,
25+
max_group: Optional[int] = None,
26+
max_access_keys: Optional[int] = None):
27+
"""
28+
Create an account
29+
30+
:param account_name: Account name
31+
:return: Returns account resource.
32+
:rtype: Dict[str, Any]
33+
"""
34+
return RgwAccounts.create_account(account_name, tenant, email,
35+
max_buckets, max_users, max_roles,
36+
max_group, max_access_keys)
1737

1838
def list(self, detailed: bool = False):
39+
"""
40+
List all account ids or all detailed account info based on the 'detailed' query parameter.
41+
42+
- If detailed=True, returns detailed account info.
43+
- If detailed=False, returns only account ids.
44+
"""
1945
detailed = str_to_bool(detailed)
2046
return RgwAccounts.get_accounts(detailed)
2147

2248
@EndpointDoc("Get RGW Account by id",
2349
parameters={'account_id': (str, 'Account id')})
2450
def get(self, account_id: str):
51+
"""
52+
Get an account by account id
53+
"""
2554
return RgwAccounts.get_account(account_id)
2655

2756
@EndpointDoc("Delete RGW Account",
2857
parameters={'account_id': (str, 'Account id')})
2958
def delete(self, account_id):
59+
"""
60+
Removes an account
61+
62+
:param account_id: account identifier
63+
:return: None.
64+
"""
3065
return RgwAccounts.delete_account(account_id)
3166

3267
@EndpointDoc("Update RGW account info",
33-
parameters={'account_id': (str, 'Account id')})
68+
parameters={'account_id': (str, 'Account id'),
69+
'account_name': (str, 'Account name'),
70+
'email': (str, 'Email'),
71+
'tenant': (str, 'Tenant'),
72+
'max_buckets': (int, 'Max buckets'),
73+
'max_users': (int, 'Max users'),
74+
'max_roles': (int, 'Max roles'),
75+
'max_group': (int, 'Max groups'),
76+
'max_access_keys': (int, 'Max access keys')})
3477
@allow_empty_body
35-
def set(self, account_id: str, account_name: Optional[str] = None,
36-
email: Optional[str] = None):
37-
return RgwAccounts.modify_account(account_id, account_name, email)
78+
def set(self, account_id: str, account_name: str,
79+
email: Optional[str] = None, tenant: Optional[str] = None,
80+
max_buckets: Optional[int] = None, max_users: Optional[int] = None,
81+
max_roles: Optional[int] = None, max_group: Optional[int] = None,
82+
max_access_keys: Optional[int] = None):
83+
"""
84+
Modifies an account
85+
86+
:param account_id: Account identifier
87+
:return: Returns modified account resource.
88+
:rtype: Dict[str, Any]
89+
"""
90+
return RgwAccounts.modify_account(account_id, account_name, email, tenant,
91+
max_buckets, max_users, max_roles,
92+
max_group, max_access_keys)
3893

3994
@EndpointDoc("Set RGW Account/Bucket quota",
4095
parameters={'account_id': (str, 'Account id'),
41-
'max_size': (str, 'Max size')})
96+
'quota_type': (str, 'Quota type'),
97+
'max_size': (str, 'Max size'),
98+
'max_objects': (str, 'Max objects')})
4299
@RESTController.Resource(method='PUT', path='/quota')
43100
@allow_empty_body
44-
def set_quota(self, quota_type: str, account_id: str, max_size: str, max_objects: str):
45-
return RgwAccounts.set_quota(quota_type, account_id, max_size, max_objects)
101+
def set_quota(self, quota_type: str, account_id: str, max_size: str, max_objects: str,
102+
enabled: bool):
103+
"""
104+
Modifies quota
105+
106+
:param account_id: Account identifier
107+
:param quota_type: 'account' or 'bucket'
108+
:return: Returns modified quota.
109+
:rtype: Dict[str, Any]
110+
"""
111+
return RgwAccounts.set_quota(quota_type, account_id, max_size, max_objects, enabled)
46112

47113
@EndpointDoc("Enable/Disable RGW Account/Bucket quota",
48-
parameters={'account_id': (str, 'Account id')})
114+
parameters={'account_id': (str, 'Account id'),
115+
'quota_type': (str, 'Quota type'),
116+
'quota_status': (str, 'Quota status')})
49117
@RESTController.Resource(method='PUT', path='/quota/status')
50118
@allow_empty_body
51119
def set_quota_status(self, quota_type: str, account_id: str, quota_status: str):
120+
"""
121+
Enable/Disable quota
122+
123+
:param account_id: Account identifier
124+
:param quota_type: 'account' or 'bucket'
125+
:param quota_status: 'enable' or 'disable'
126+
:return: Returns modified quota.
127+
:rtype: Dict[str, Any]
128+
"""
52129
return RgwAccounts.set_quota_status(quota_type, account_id, quota_status)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
export interface Account {
2+
id: string;
3+
tenant: string;
4+
name: string;
5+
email: string;
6+
quota: Quota;
7+
bucket_quota: Quota;
8+
max_users: number;
9+
max_roles: number;
10+
max_groups: number;
11+
max_buckets: number;
12+
max_access_keys: number;
13+
}
14+
15+
interface Quota {
16+
enabled: boolean;
17+
check_on_raw: boolean;
18+
max_size: number;
19+
max_size_kb: number;
20+
max_objects: number;
21+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!-- Account quota -->
2+
<div *ngIf="selection.quota">
3+
<legend i18n>Account quota</legend>
4+
<cd-table-key-value [data]="quota">
5+
</cd-table-key-value>
6+
</div>
7+
8+
<!-- Bucket quota -->
9+
<div *ngIf="selection.bucket_quota">
10+
<legend i18n>Bucket quota</legend>
11+
<cd-table-key-value [data]="bucket_quota">
12+
</cd-table-key-value>
13+
</div>

src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-accounts-details/rgw-user-accounts-details.component.scss

Whitespace-only changes.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { RgwUserAccountsDetailsComponent } from './rgw-user-accounts-details.component';
4+
import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe';
5+
import { TableKeyValueComponent } from '~/app/shared/datatable/table-key-value/table-key-value.component';
6+
import { CdDatePipe } from '~/app/shared/pipes/cd-date.pipe';
7+
8+
describe('RgwUserAccountsDetailsComponent', () => {
9+
let component: RgwUserAccountsDetailsComponent;
10+
let fixture: ComponentFixture<RgwUserAccountsDetailsComponent>;
11+
12+
beforeEach(async () => {
13+
await TestBed.configureTestingModule({
14+
declarations: [RgwUserAccountsDetailsComponent, TableKeyValueComponent],
15+
providers: [DimlessBinaryPipe, CdDatePipe]
16+
}).compileComponents();
17+
18+
fixture = TestBed.createComponent(RgwUserAccountsDetailsComponent);
19+
component = fixture.componentInstance;
20+
component.selection = { quota: {}, bucket_quota: {} };
21+
fixture.detectChanges();
22+
});
23+
24+
it('should create', () => {
25+
expect(component).toBeTruthy();
26+
});
27+
});
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
2+
import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe';
3+
4+
@Component({
5+
selector: 'cd-rgw-user-accounts-details',
6+
templateUrl: './rgw-user-accounts-details.component.html',
7+
styleUrls: ['./rgw-user-accounts-details.component.scss']
8+
})
9+
export class RgwUserAccountsDetailsComponent implements OnChanges {
10+
@Input()
11+
selection: any;
12+
quota = {};
13+
bucket_quota = {};
14+
15+
constructor(private dimlessBinary: DimlessBinaryPipe) {}
16+
17+
ngOnChanges(changes: SimpleChanges): void {
18+
if (changes.selection && changes.selection.currentValue) {
19+
this.quota = this.createDisplayValues('quota');
20+
this.bucket_quota = this.createDisplayValues('bucket_quota');
21+
}
22+
}
23+
24+
createDisplayValues(quota_type: string) {
25+
return {
26+
Enabled: this.selection[quota_type].enabled ? 'Yes' : 'No',
27+
'Maximum size': this.selection[quota_type].enabled
28+
? this.selection[quota_type].max_size <= -1
29+
? 'Unlimited'
30+
: this.dimlessBinary.transform(this.selection[quota_type].max_size)
31+
: '-',
32+
'Maximum objects': this.selection[quota_type].enabled
33+
? this.selection[quota_type].max_objects <= -1
34+
? 'Unlimited'
35+
: this.selection[quota_type].max_objects
36+
: '-'
37+
};
38+
}
39+
}

0 commit comments

Comments
 (0)