diff options
Diffstat (limited to 'src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-trash-list/rbd-trash-list.component.ts')
-rw-r--r-- | src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-trash-list/rbd-trash-list.component.ts | 225 |
1 files changed, 225 insertions, 0 deletions
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-trash-list/rbd-trash-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-trash-list/rbd-trash-list.component.ts new file mode 100644 index 000000000..43fe42b99 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-trash-list/rbd-trash-list.component.ts @@ -0,0 +1,225 @@ +import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core'; + +import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import _ from 'lodash'; +import moment from 'moment'; + +import { RbdService } from '~/app/shared/api/rbd.service'; +import { TableStatusViewCache } from '~/app/shared/classes/table-status-view-cache'; +import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component'; +import { ActionLabelsI18n } from '~/app/shared/constants/app.constants'; +import { TableComponent } from '~/app/shared/datatable/table/table.component'; +import { CellTemplate } from '~/app/shared/enum/cell-template.enum'; +import { Icons } from '~/app/shared/enum/icons.enum'; +import { ViewCacheStatus } from '~/app/shared/enum/view-cache-status.enum'; +import { CdTableAction } from '~/app/shared/models/cd-table-action'; +import { CdTableColumn } from '~/app/shared/models/cd-table-column'; +import { CdTableSelection } from '~/app/shared/models/cd-table-selection'; +import { ExecutingTask } from '~/app/shared/models/executing-task'; +import { FinishedTask } from '~/app/shared/models/finished-task'; +import { ImageSpec } from '~/app/shared/models/image-spec'; +import { Permission } from '~/app/shared/models/permissions'; +import { Task } from '~/app/shared/models/task'; +import { CdDatePipe } from '~/app/shared/pipes/cd-date.pipe'; +import { AuthStorageService } from '~/app/shared/services/auth-storage.service'; +import { ModalService } from '~/app/shared/services/modal.service'; +import { TaskListService } from '~/app/shared/services/task-list.service'; +import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service'; +import { RbdTrashPurgeModalComponent } from '../rbd-trash-purge-modal/rbd-trash-purge-modal.component'; +import { RbdTrashRestoreModalComponent } from '../rbd-trash-restore-modal/rbd-trash-restore-modal.component'; + +@Component({ + selector: 'cd-rbd-trash-list', + templateUrl: './rbd-trash-list.component.html', + styleUrls: ['./rbd-trash-list.component.scss'], + providers: [TaskListService] +}) +export class RbdTrashListComponent implements OnInit { + @ViewChild(TableComponent, { static: true }) + table: TableComponent; + @ViewChild('expiresTpl', { static: true }) + expiresTpl: TemplateRef<any>; + @ViewChild('deleteTpl', { static: true }) + deleteTpl: TemplateRef<any>; + + icons = Icons; + + columns: CdTableColumn[]; + executingTasks: ExecutingTask[] = []; + images: any; + modalRef: NgbModalRef; + permission: Permission; + retries: number; + selection = new CdTableSelection(); + tableActions: CdTableAction[]; + tableStatus = new TableStatusViewCache(); + disablePurgeBtn = true; + + constructor( + private authStorageService: AuthStorageService, + private rbdService: RbdService, + private modalService: ModalService, + private cdDatePipe: CdDatePipe, + public taskListService: TaskListService, + private taskWrapper: TaskWrapperService, + public actionLabels: ActionLabelsI18n + ) { + this.permission = this.authStorageService.getPermissions().rbdImage; + const restoreAction: CdTableAction = { + permission: 'update', + icon: Icons.undo, + click: () => this.restoreModal(), + name: this.actionLabels.RESTORE + }; + const deleteAction: CdTableAction = { + permission: 'delete', + icon: Icons.destroy, + click: () => this.deleteModal(), + name: this.actionLabels.DELETE + }; + this.tableActions = [restoreAction, deleteAction]; + } + + ngOnInit() { + this.columns = [ + { + name: $localize`ID`, + prop: 'id', + flexGrow: 1, + cellTransformation: CellTemplate.executing + }, + { + name: $localize`Name`, + prop: 'name', + flexGrow: 1 + }, + { + name: $localize`Pool`, + prop: 'pool_name', + flexGrow: 1 + }, + { + name: $localize`Namespace`, + prop: 'namespace', + flexGrow: 1 + }, + { + name: $localize`Status`, + prop: 'deferment_end_time', + flexGrow: 1, + cellTemplate: this.expiresTpl + }, + { + name: $localize`Deleted At`, + prop: 'deletion_time', + flexGrow: 1, + pipe: this.cdDatePipe + } + ]; + + const itemFilter = (entry: any, task: Task) => { + const imageSpec = new ImageSpec(entry.pool_name, entry.namespace, entry.id); + return imageSpec.toString() === task.metadata['image_id_spec']; + }; + + const taskFilter = (task: Task) => { + return ['rbd/trash/remove', 'rbd/trash/restore'].includes(task.name); + }; + + this.taskListService.init( + () => this.rbdService.listTrash(), + (resp) => this.prepareResponse(resp), + (images) => (this.images = images), + () => this.onFetchError(), + taskFilter, + itemFilter, + undefined + ); + } + + prepareResponse(resp: any[]): any[] { + let images: any[] = []; + const viewCacheStatusMap = {}; + + resp.forEach((pool: Record<string, any>) => { + if (_.isUndefined(viewCacheStatusMap[pool.status])) { + viewCacheStatusMap[pool.status] = []; + } + viewCacheStatusMap[pool.status].push(pool.pool_name); + images = images.concat(pool.value); + this.disablePurgeBtn = !images.length; + }); + + let status: number; + if (viewCacheStatusMap[3]) { + status = 3; + } else if (viewCacheStatusMap[1]) { + status = 1; + } else if (viewCacheStatusMap[2]) { + status = 2; + } + + if (status) { + const statusFor = + (viewCacheStatusMap[status].length > 1 ? 'pools ' : 'pool ') + + viewCacheStatusMap[status].join(); + + this.tableStatus = new TableStatusViewCache(status, statusFor); + } else { + this.tableStatus = new TableStatusViewCache(); + } + + images.forEach((image) => { + image.cdIsExpired = moment().isAfter(image.deferment_end_time); + }); + + return images; + } + + onFetchError() { + this.table.reset(); // Disable loading indicator. + this.tableStatus = new TableStatusViewCache(ViewCacheStatus.ValueException); + } + + updateSelection(selection: CdTableSelection) { + this.selection = selection; + } + + restoreModal() { + const initialState = { + poolName: this.selection.first().pool_name, + namespace: this.selection.first().namespace, + imageName: this.selection.first().name, + imageId: this.selection.first().id + }; + + this.modalRef = this.modalService.show(RbdTrashRestoreModalComponent, initialState); + } + + deleteModal() { + const poolName = this.selection.first().pool_name; + const namespace = this.selection.first().namespace; + const imageId = this.selection.first().id; + const expiresAt = this.selection.first().deferment_end_time; + const isExpired = moment().isAfter(expiresAt); + const imageIdSpec = new ImageSpec(poolName, namespace, imageId); + + this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, { + itemDescription: 'RBD', + itemNames: [imageIdSpec], + bodyTemplate: this.deleteTpl, + bodyContext: { expiresAt, isExpired }, + submitActionObservable: () => + this.taskWrapper.wrapTaskAroundCall({ + task: new FinishedTask('rbd/trash/remove', { + image_id_spec: imageIdSpec.toString() + }), + call: this.rbdService.removeTrash(imageIdSpec, true) + }) + }); + } + + purgeModal() { + this.modalService.show(RbdTrashPurgeModalComponent); + } +} |