]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.ts
import ceph nautilus 14.2.2
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / pool / pool-list / pool-list.component.ts
1 import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
2
3 import { I18n } from '@ngx-translate/i18n-polyfill';
4 import * as _ from 'lodash';
5 import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
6
7 import { PoolService } from '../../../shared/api/pool.service';
8 import { CriticalConfirmationModalComponent } from '../../../shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
9 import { ActionLabelsI18n, URLVerbs } from '../../../shared/constants/app.constants';
10 import { TableComponent } from '../../../shared/datatable/table/table.component';
11 import { CellTemplate } from '../../../shared/enum/cell-template.enum';
12 import { ViewCacheStatus } from '../../../shared/enum/view-cache-status.enum';
13 import { CdTableAction } from '../../../shared/models/cd-table-action';
14 import { CdTableColumn } from '../../../shared/models/cd-table-column';
15 import { CdTableSelection } from '../../../shared/models/cd-table-selection';
16 import { ExecutingTask } from '../../../shared/models/executing-task';
17 import { FinishedTask } from '../../../shared/models/finished-task';
18 import { Permissions } from '../../../shared/models/permissions';
19 import { DimlessPipe } from '../../../shared/pipes/dimless.pipe';
20 import { AuthStorageService } from '../../../shared/services/auth-storage.service';
21 import { TaskListService } from '../../../shared/services/task-list.service';
22 import { TaskWrapperService } from '../../../shared/services/task-wrapper.service';
23 import { URLBuilderService } from '../../../shared/services/url-builder.service';
24 import { PgCategoryService } from '../../shared/pg-category.service';
25 import { Pool } from '../pool';
26 import { PoolStats } from '../pool-stat';
27
28 const BASE_URL = 'pool';
29
30 @Component({
31 selector: 'cd-pool-list',
32 templateUrl: './pool-list.component.html',
33 providers: [
34 TaskListService,
35 { provide: URLBuilderService, useValue: new URLBuilderService(BASE_URL) }
36 ],
37 styleUrls: ['./pool-list.component.scss']
38 })
39 export class PoolListComponent implements OnInit {
40 @ViewChild(TableComponent)
41 table: TableComponent;
42 @ViewChild('poolUsageTpl')
43 poolUsageTpl: TemplateRef<any>;
44
45 @ViewChild('poolConfigurationSourceTpl')
46 poolConfigurationSourceTpl: TemplateRef<any>;
47
48 pools: Pool[] = [];
49 columns: CdTableColumn[];
50 selection = new CdTableSelection();
51 modalRef: BsModalRef;
52 executingTasks: ExecutingTask[] = [];
53 permissions: Permissions;
54 tableActions: CdTableAction[];
55 viewCacheStatusList: any[];
56 selectionCacheTiers: any[] = [];
57
58 constructor(
59 private poolService: PoolService,
60 private taskWrapper: TaskWrapperService,
61 private authStorageService: AuthStorageService,
62 private taskListService: TaskListService,
63 private modalService: BsModalService,
64 private i18n: I18n,
65 private pgCategoryService: PgCategoryService,
66 private dimlessPipe: DimlessPipe,
67 private urlBuilder: URLBuilderService,
68 public actionLabels: ActionLabelsI18n
69 ) {
70 this.permissions = this.authStorageService.getPermissions();
71 this.tableActions = [
72 {
73 permission: 'create',
74 icon: 'fa-plus',
75 routerLink: () => this.urlBuilder.getCreate(),
76 name: this.actionLabels.CREATE
77 },
78 {
79 permission: 'update',
80 icon: 'fa-pencil',
81 routerLink: () =>
82 this.urlBuilder.getEdit(encodeURIComponent(this.selection.first().pool_name)),
83 name: this.actionLabels.EDIT
84 },
85 {
86 permission: 'delete',
87 icon: 'fa-trash-o',
88 click: () => this.deletePoolModal(),
89 name: this.actionLabels.DELETE
90 }
91 ];
92 }
93
94 ngOnInit() {
95 this.columns = [
96 {
97 prop: 'pool_name',
98 name: this.i18n('Name'),
99 flexGrow: 4,
100 cellTransformation: CellTemplate.executing
101 },
102 {
103 prop: 'type',
104 name: this.i18n('Type'),
105 flexGrow: 2
106 },
107 {
108 prop: 'application_metadata',
109 name: this.i18n('Applications'),
110 flexGrow: 2
111 },
112 {
113 prop: 'pg_status',
114 name: this.i18n('PG Status'),
115 flexGrow: 3,
116 cellClass: ({ row, column, value }): any => {
117 return this.getPgStatusCellClass(row, column, value);
118 }
119 },
120 {
121 prop: 'size',
122 name: this.i18n('Replica Size'),
123 flexGrow: 1,
124 cellClass: 'text-right'
125 },
126 {
127 prop: 'last_change',
128 name: this.i18n('Last Change'),
129 flexGrow: 1,
130 cellClass: 'text-right'
131 },
132 {
133 prop: 'erasure_code_profile',
134 name: this.i18n('Erasure Coded Profile'),
135 flexGrow: 2
136 },
137 {
138 prop: 'crush_rule',
139 name: this.i18n('Crush Ruleset'),
140 flexGrow: 3
141 },
142 {
143 name: this.i18n('Usage'),
144 prop: 'usage',
145 cellTemplate: this.poolUsageTpl,
146 flexGrow: 3
147 },
148 {
149 prop: 'stats.rd_bytes.series',
150 name: this.i18n('Read bytes'),
151 cellTransformation: CellTemplate.sparkline,
152 flexGrow: 3
153 },
154 {
155 prop: 'stats.wr_bytes.series',
156 name: this.i18n('Write bytes'),
157 cellTransformation: CellTemplate.sparkline,
158 flexGrow: 3
159 },
160 {
161 prop: 'stats.rd.rate',
162 name: this.i18n('Read ops'),
163 flexGrow: 1,
164 pipe: this.dimlessPipe,
165 cellTransformation: CellTemplate.perSecond
166 },
167 {
168 prop: 'stats.wr.rate',
169 name: this.i18n('Write ops'),
170 flexGrow: 1,
171 pipe: this.dimlessPipe,
172 cellTransformation: CellTemplate.perSecond
173 }
174 ];
175
176 this.taskListService.init(
177 () => this.poolService.getList(),
178 undefined,
179 (pools) => (this.pools = this.transformPoolsData(pools)),
180 () => {
181 this.table.reset(); // Disable loading indicator.
182 this.viewCacheStatusList = [{ status: ViewCacheStatus.ValueException }];
183 },
184 (task) => task.name.startsWith(`${BASE_URL}/`),
185 (pool, task) => task.metadata['pool_name'] === pool.pool_name,
186 { default: (task: ExecutingTask) => new Pool(task.metadata['pool_name']) }
187 );
188 }
189
190 updateSelection(selection: CdTableSelection) {
191 this.selection = selection;
192 this.getSelectionTiers();
193 }
194
195 deletePoolModal() {
196 const name = this.selection.first().pool_name;
197 this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
198 initialState: {
199 itemDescription: 'Pool',
200 submitActionObservable: () =>
201 this.taskWrapper.wrapTaskAroundCall({
202 task: new FinishedTask(`${BASE_URL}/${URLVerbs.DELETE}`, { pool_name: name }),
203 call: this.poolService.delete(name)
204 })
205 }
206 });
207 }
208
209 getPgStatusCellClass(_row, _column, value): object {
210 return {
211 'text-right': true,
212 [`pg-${this.pgCategoryService.getTypeByStates(value)}`]: true
213 };
214 }
215
216 transformPoolsData(pools: any) {
217 const requiredStats = ['bytes_used', 'max_avail', 'rd_bytes', 'wr_bytes', 'rd', 'wr'];
218 const emptyStat = { latest: 0, rate: 0, series: [] };
219
220 _.forEach(pools, (pool: Pool) => {
221 pool['pg_status'] = this.transformPgStatus(pool['pg_status']);
222 const stats: PoolStats = {};
223 _.forEach(requiredStats, (stat) => {
224 stats[stat] = pool.stats && pool.stats[stat] ? pool.stats[stat] : emptyStat;
225 });
226 pool['stats'] = stats;
227 const avail = stats.bytes_used.latest + stats.max_avail.latest;
228 pool['usage'] = avail > 0 ? stats.bytes_used.latest / avail : avail;
229
230 ['rd_bytes', 'wr_bytes'].forEach((stat) => {
231 pool.stats[stat].series = pool.stats[stat].series.map((point) => point[1]);
232 });
233 pool.cdIsBinary = true;
234 });
235
236 return pools;
237 }
238
239 transformPgStatus(pgStatus: any): string {
240 const strings = [];
241 _.forEach(pgStatus, (count, state) => {
242 strings.push(`${count} ${state}`);
243 });
244
245 return strings.join(', ');
246 }
247
248 getPoolDetails(pool: object) {
249 return _.omit(pool, ['cdExecuting', 'cdIsBinary']);
250 }
251
252 getSelectionTiers() {
253 const cacheTierIds = this.selection.hasSingleSelection ? this.selection.first()['tiers'] : [];
254 this.selectionCacheTiers = this.pools.filter((pool) => cacheTierIds.includes(pool.pool));
255 }
256 }