]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-list/rgw-bucket-list.component.ts
a1f79c312c88924264a4d3d93d0c77e595ea6011
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / rgw / rgw-bucket-list / rgw-bucket-list.component.ts
1 import {
2 ChangeDetectorRef,
3 Component,
4 NgZone,
5 OnInit,
6 TemplateRef,
7 ViewChild
8 } from '@angular/core';
9
10 import { I18n } from '@ngx-translate/i18n-polyfill';
11 import * as _ from 'lodash';
12 import { BsModalService } from 'ngx-bootstrap/modal';
13 import { forkJoin as observableForkJoin, Observable, Subscriber } from 'rxjs';
14
15 import { RgwBucketService } from '../../../shared/api/rgw-bucket.service';
16 import { ListWithDetails } from '../../../shared/classes/list-with-details.class';
17 import { CriticalConfirmationModalComponent } from '../../../shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
18 import { ActionLabelsI18n } from '../../../shared/constants/app.constants';
19 import { TableComponent } from '../../../shared/datatable/table/table.component';
20 import { Icons } from '../../../shared/enum/icons.enum';
21 import { CdTableAction } from '../../../shared/models/cd-table-action';
22 import { CdTableColumn } from '../../../shared/models/cd-table-column';
23 import { CdTableFetchDataContext } from '../../../shared/models/cd-table-fetch-data-context';
24 import { CdTableSelection } from '../../../shared/models/cd-table-selection';
25 import { Permission } from '../../../shared/models/permissions';
26 import { DimlessBinaryPipe } from '../../../shared/pipes/dimless-binary.pipe';
27 import { DimlessPipe } from '../../../shared/pipes/dimless.pipe';
28 import { AuthStorageService } from '../../../shared/services/auth-storage.service';
29 import { URLBuilderService } from '../../../shared/services/url-builder.service';
30
31 const BASE_URL = 'rgw/bucket';
32
33 @Component({
34 selector: 'cd-rgw-bucket-list',
35 templateUrl: './rgw-bucket-list.component.html',
36 styleUrls: ['./rgw-bucket-list.component.scss'],
37 providers: [{ provide: URLBuilderService, useValue: new URLBuilderService(BASE_URL) }]
38 })
39 export class RgwBucketListComponent extends ListWithDetails implements OnInit {
40 @ViewChild(TableComponent, { static: true })
41 table: TableComponent;
42 @ViewChild('bucketSizeTpl', { static: true })
43 bucketSizeTpl: TemplateRef<any>;
44 @ViewChild('bucketObjectTpl', { static: true })
45 bucketObjectTpl: TemplateRef<any>;
46
47 permission: Permission;
48 tableActions: CdTableAction[];
49 columns: CdTableColumn[] = [];
50 buckets: object[] = [];
51 selection: CdTableSelection = new CdTableSelection();
52 isStale = false;
53 staleTimeout: number;
54
55 constructor(
56 private authStorageService: AuthStorageService,
57 private dimlessBinaryPipe: DimlessBinaryPipe,
58 private dimlessPipe: DimlessPipe,
59 private rgwBucketService: RgwBucketService,
60 private bsModalService: BsModalService,
61 private i18n: I18n,
62 private urlBuilder: URLBuilderService,
63 public actionLabels: ActionLabelsI18n,
64 private ngZone: NgZone,
65 private changeDetectorRef: ChangeDetectorRef
66 ) {
67 super();
68 this.permission = this.authStorageService.getPermissions().rgw;
69 const getBucketUri = () =>
70 this.selection.first() && `${encodeURIComponent(this.selection.first().bid)}`;
71 const addAction: CdTableAction = {
72 permission: 'create',
73 icon: Icons.add,
74 routerLink: () => this.urlBuilder.getCreate(),
75 name: this.actionLabels.CREATE,
76 canBePrimary: (selection: CdTableSelection) => !selection.hasSelection
77 };
78 const editAction: CdTableAction = {
79 permission: 'update',
80 icon: Icons.edit,
81 routerLink: () => this.urlBuilder.getEdit(getBucketUri()),
82 name: this.actionLabels.EDIT
83 };
84 const deleteAction: CdTableAction = {
85 permission: 'delete',
86 icon: Icons.destroy,
87 click: () => this.deleteAction(),
88 disable: () => !this.selection.hasSelection,
89 name: this.actionLabels.DELETE,
90 canBePrimary: (selection: CdTableSelection) => selection.hasMultiSelection
91 };
92 this.tableActions = [addAction, editAction, deleteAction];
93 this.timeConditionReached();
94 }
95
96 ngOnInit() {
97 this.columns = [
98 {
99 name: this.i18n('Name'),
100 prop: 'bid',
101 flexGrow: 2
102 },
103 {
104 name: this.i18n('Owner'),
105 prop: 'owner',
106 flexGrow: 3
107 },
108 {
109 name: this.i18n('Used Capacity'),
110 prop: 'bucket_size',
111 flexGrow: 0.5,
112 pipe: this.dimlessBinaryPipe
113 },
114 {
115 name: this.i18n('Capacity Limit %'),
116 prop: 'size_usage',
117 cellTemplate: this.bucketSizeTpl,
118 flexGrow: 1
119 },
120 {
121 name: this.i18n('Objects'),
122 prop: 'num_objects',
123 flexGrow: 0.5,
124 pipe: this.dimlessPipe
125 },
126 {
127 name: this.i18n('Object Limit %'),
128 prop: 'object_usage',
129 cellTemplate: this.bucketObjectTpl,
130 flexGrow: 1
131 }
132 ];
133 }
134
135 transformBucketData() {
136 _.forEach(this.buckets, (bucketKey) => {
137 const usageList = bucketKey['usage'];
138 const maxBucketSize = bucketKey['bucket_quota']['max_size'];
139 const maxBucketObjects = bucketKey['bucket_quota']['max_objects'];
140 let totalBucketSize = 0;
141 let numOfObjects = 0;
142 _.forEach(usageList, (usageKey) => {
143 totalBucketSize = totalBucketSize + usageKey.size_actual;
144 numOfObjects = numOfObjects + usageKey.num_objects;
145 });
146 bucketKey['bucket_size'] = totalBucketSize;
147 bucketKey['num_objects'] = numOfObjects;
148 bucketKey['size_usage'] = maxBucketSize > 0 ? totalBucketSize / maxBucketSize : undefined;
149 bucketKey['object_usage'] =
150 maxBucketObjects > 0 ? numOfObjects / maxBucketObjects : undefined;
151 });
152 }
153
154 timeConditionReached() {
155 clearTimeout(this.staleTimeout);
156 this.ngZone.runOutsideAngular(() => {
157 this.staleTimeout = window.setTimeout(() => {
158 this.ngZone.run(() => {
159 this.isStale = true;
160 });
161 }, 10000);
162 });
163 }
164
165 getBucketList(context: CdTableFetchDataContext) {
166 this.isStale = false;
167 this.timeConditionReached();
168 this.rgwBucketService.list().subscribe(
169 (resp: object[]) => {
170 this.buckets = resp;
171 this.transformBucketData();
172 this.changeDetectorRef.detectChanges();
173 },
174 () => {
175 context.error();
176 }
177 );
178 }
179
180 updateSelection(selection: CdTableSelection) {
181 this.selection = selection;
182 }
183
184 deleteAction() {
185 this.bsModalService.show(CriticalConfirmationModalComponent, {
186 initialState: {
187 itemDescription: this.selection.hasSingleSelection
188 ? this.i18n('bucket')
189 : this.i18n('buckets'),
190 itemNames: this.selection.selected.map((bucket: any) => bucket['bid']),
191 submitActionObservable: () => {
192 return new Observable((observer: Subscriber<any>) => {
193 // Delete all selected data table rows.
194 observableForkJoin(
195 this.selection.selected.map((bucket: any) => {
196 return this.rgwBucketService.delete(bucket.bid);
197 })
198 ).subscribe(
199 null,
200 (error) => {
201 // Forward the error to the observer.
202 observer.error(error);
203 // Reload the data table content because some deletions might
204 // have been executed successfully in the meanwhile.
205 this.table.refreshBtn();
206 },
207 () => {
208 // Notify the observer that we are done.
209 observer.complete();
210 // Reload the data table content.
211 this.table.refreshBtn();
212 }
213 );
214 });
215 }
216 }
217 });
218 }
219 }