1 import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
3 import { I18n } from '@ngx-translate/i18n-polyfill';
4 import * as _ from 'lodash';
5 import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
7 import { ConfigurationService } from '../../../shared/api/configuration.service';
8 import { PoolService } from '../../../shared/api/pool.service';
9 import { ListWithDetails } from '../../../shared/classes/list-with-details.class';
10 import { CriticalConfirmationModalComponent } from '../../../shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
11 import { ActionLabelsI18n, URLVerbs } from '../../../shared/constants/app.constants';
12 import { TableComponent } from '../../../shared/datatable/table/table.component';
13 import { CellTemplate } from '../../../shared/enum/cell-template.enum';
14 import { Icons } from '../../../shared/enum/icons.enum';
15 import { ViewCacheStatus } from '../../../shared/enum/view-cache-status.enum';
16 import { CdTableAction } from '../../../shared/models/cd-table-action';
17 import { CdTableColumn } from '../../../shared/models/cd-table-column';
18 import { CdTableSelection } from '../../../shared/models/cd-table-selection';
19 import { ExecutingTask } from '../../../shared/models/executing-task';
20 import { FinishedTask } from '../../../shared/models/finished-task';
21 import { Permissions } from '../../../shared/models/permissions';
22 import { DimlessPipe } from '../../../shared/pipes/dimless.pipe';
23 import { AuthStorageService } from '../../../shared/services/auth-storage.service';
24 import { TaskListService } from '../../../shared/services/task-list.service';
25 import { TaskWrapperService } from '../../../shared/services/task-wrapper.service';
26 import { URLBuilderService } from '../../../shared/services/url-builder.service';
27 import { PgCategoryService } from '../../shared/pg-category.service';
28 import { Pool } from '../pool';
29 import { PoolStat, PoolStats } from '../pool-stat';
31 const BASE_URL = 'pool';
34 selector: 'cd-pool-list',
35 templateUrl: './pool-list.component.html',
38 { provide: URLBuilderService, useValue: new URLBuilderService(BASE_URL) }
40 styleUrls: ['./pool-list.component.scss']
42 export class PoolListComponent extends ListWithDetails implements OnInit {
43 @ViewChild(TableComponent, { static: true })
44 table: TableComponent;
45 @ViewChild('poolUsageTpl', { static: true })
46 poolUsageTpl: TemplateRef<any>;
48 @ViewChild('poolConfigurationSourceTpl', { static: false })
49 poolConfigurationSourceTpl: TemplateRef<any>;
52 columns: CdTableColumn[];
53 selection = new CdTableSelection();
55 executingTasks: ExecutingTask[] = [];
56 permissions: Permissions;
57 tableActions: CdTableAction[];
58 viewCacheStatusList: any[];
59 cacheTiers: any[] = [];
60 monAllowPoolDelete = false;
63 private poolService: PoolService,
64 private taskWrapper: TaskWrapperService,
65 private authStorageService: AuthStorageService,
66 private taskListService: TaskListService,
67 private modalService: BsModalService,
69 private pgCategoryService: PgCategoryService,
70 private dimlessPipe: DimlessPipe,
71 private urlBuilder: URLBuilderService,
72 private configurationService: ConfigurationService,
73 public actionLabels: ActionLabelsI18n
76 this.permissions = this.authStorageService.getPermissions();
81 routerLink: () => this.urlBuilder.getCreate(),
82 name: this.actionLabels.CREATE
88 this.urlBuilder.getEdit(encodeURIComponent(this.selection.first().pool_name)),
89 name: this.actionLabels.EDIT
94 click: () => this.deletePoolModal(),
95 name: this.actionLabels.DELETE,
96 disable: () => !this.selection.first() || !this.monAllowPoolDelete,
97 disableDesc: () => this.getDisableDesc()
101 // Note, we need read permissions to get the 'mon_allow_pool_delete'
102 // configuration option.
103 if (this.permissions.configOpt.read) {
104 this.configurationService.get('mon_allow_pool_delete').subscribe((data: any) => {
105 if (_.has(data, 'value')) {
106 const monSection = _.find(data.value, (v) => {
107 return v.section === 'mon';
108 }) || { value: false };
109 this.monAllowPoolDelete = monSection.value === 'true' ? true : false;
116 const compare = (prop: string, pool1: Pool, pool2: Pool) =>
117 _.get(pool1, prop) > _.get(pool2, prop) ? 1 : -1;
121 name: this.i18n('Name'),
123 cellTransformation: CellTemplate.executing
127 name: this.i18n('Type'),
131 prop: 'application_metadata',
132 name: this.i18n('Applications'),
137 name: this.i18n('PG Status'),
139 cellClass: ({ row, column, value }): any => {
140 return this.getPgStatusCellClass(row, column, value);
145 name: this.i18n('Replica Size'),
147 cellClass: 'text-right'
151 name: this.i18n('Last Change'),
153 cellClass: 'text-right'
156 prop: 'erasure_code_profile',
157 name: this.i18n('Erasure Coded Profile'),
162 name: this.i18n('Crush Ruleset'),
166 name: this.i18n('Usage'),
168 cellTemplate: this.poolUsageTpl,
172 prop: 'stats.rd_bytes.rates',
173 name: this.i18n('Read bytes'),
174 comparator: (_valueA: any, _valueB: any, rowA: Pool, rowB: Pool) =>
175 compare('stats.rd_bytes.latest', rowA, rowB),
176 cellTransformation: CellTemplate.sparkline,
180 prop: 'stats.wr_bytes.rates',
181 name: this.i18n('Write bytes'),
182 comparator: (_valueA: any, _valueB: any, rowA: Pool, rowB: Pool) =>
183 compare('stats.wr_bytes.latest', rowA, rowB),
184 cellTransformation: CellTemplate.sparkline,
188 prop: 'stats.rd.rate',
189 name: this.i18n('Read ops'),
191 pipe: this.dimlessPipe,
192 cellTransformation: CellTemplate.perSecond
195 prop: 'stats.wr.rate',
196 name: this.i18n('Write ops'),
198 pipe: this.dimlessPipe,
199 cellTransformation: CellTemplate.perSecond
203 this.taskListService.init(
204 () => this.poolService.getList(),
206 (pools) => (this.pools = this.transformPoolsData(pools)),
208 this.table.reset(); // Disable loading indicator.
209 this.viewCacheStatusList = [{ status: ViewCacheStatus.ValueException }];
211 (task) => task.name.startsWith(`${BASE_URL}/`),
212 (pool, task) => task.metadata['pool_name'] === pool.pool_name,
213 { default: (metadata: any) => new Pool(metadata['pool_name']) }
217 updateSelection(selection: CdTableSelection) {
218 this.selection = selection;
222 const name = this.selection.first().pool_name;
223 this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
225 itemDescription: 'Pool',
227 submitActionObservable: () =>
228 this.taskWrapper.wrapTaskAroundCall({
229 task: new FinishedTask(`${BASE_URL}/${URLVerbs.DELETE}`, { pool_name: name }),
230 call: this.poolService.delete(name)
236 getPgStatusCellClass(_row: any, _column: any, value: string): object {
239 [`pg-${this.pgCategoryService.getTypeByStates(value)}`]: true
243 transformPoolsData(pools: any) {
244 const requiredStats = [
254 const emptyStat: PoolStat = { latest: 0, rate: 0, rates: [] };
256 _.forEach(pools, (pool: Pool) => {
257 pool['pg_status'] = this.transformPgStatus(pool['pg_status']);
258 const stats: PoolStats = {};
259 _.forEach(requiredStats, (stat) => {
260 stats[stat] = pool.stats && pool.stats[stat] ? pool.stats[stat] : emptyStat;
262 pool['stats'] = stats;
263 pool['usage'] = stats.percent_used.latest;
267 pool.pg_num + pool.pg_placement_num !== pool.pg_num_target + pool.pg_placement_num_target
269 pool['cdExecuting'] = 'Updating';
272 ['rd_bytes', 'wr_bytes'].forEach((stat) => {
273 pool.stats[stat].rates = pool.stats[stat].rates.map((point: any) => point[1]);
275 pool.cdIsBinary = true;
281 transformPgStatus(pgStatus: any): string {
282 const strings: string[] = [];
283 _.forEach(pgStatus, (count, state) => {
284 strings.push(`${count} ${state}`);
287 return strings.join(', ');
290 getSelectionTiers() {
291 if (typeof this.expandedRow !== 'undefined') {
292 const cacheTierIds = this.expandedRow['tiers'];
293 this.cacheTiers = this.pools.filter((pool) => cacheTierIds.includes(pool.pool));
297 getDisableDesc(): string | undefined {
298 if (!this.monAllowPoolDelete) {
300 'Pool deletion is disabled by the mon_allow_pool_delete configuration setting.'
307 setExpandedRow(expandedRow: any) {
308 super.setExpandedRow(expandedRow);
309 this.getSelectionTiers();