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