1 import { Component, Input, OnChanges, OnInit, TemplateRef, ViewChild } from '@angular/core';
3 import { I18n } from '@ngx-translate/i18n-polyfill';
4 import * as moment from 'moment';
5 import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
6 import { of } from 'rxjs';
8 import { RbdService } from '../../../shared/api/rbd.service';
9 import { ConfirmationModalComponent } from '../../../shared/components/confirmation-modal/confirmation-modal.component';
10 import { CriticalConfirmationModalComponent } from '../../../shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
11 import { ActionLabelsI18n } from '../../../shared/constants/app.constants';
12 import { CellTemplate } from '../../../shared/enum/cell-template.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 { Permission } from '../../../shared/models/permissions';
19 import { CdDatePipe } from '../../../shared/pipes/cd-date.pipe';
20 import { DimlessBinaryPipe } from '../../../shared/pipes/dimless-binary.pipe';
21 import { AuthStorageService } from '../../../shared/services/auth-storage.service';
22 import { NotificationService } from '../../../shared/services/notification.service';
23 import { SummaryService } from '../../../shared/services/summary.service';
24 import { TaskListService } from '../../../shared/services/task-list.service';
25 import { TaskManagerService } from '../../../shared/services/task-manager.service';
26 import { RbdSnapshotFormComponent } from '../rbd-snapshot-form/rbd-snapshot-form.component';
27 import { RbdSnapshotActionsModel } from './rbd-snapshot-actions.model';
28 import { RbdSnapshotModel } from './rbd-snapshot.model';
31 selector: 'cd-rbd-snapshot-list',
32 templateUrl: './rbd-snapshot-list.component.html',
33 styleUrls: ['./rbd-snapshot-list.component.scss'],
34 providers: [TaskListService]
36 export class RbdSnapshotListComponent implements OnInit, OnChanges {
38 snapshots: RbdSnapshotModel[] = [];
40 featuresName: string[];
47 @ViewChild('nameTpl', { static: false })
48 nameTpl: TemplateRef<any>;
49 @ViewChild('protectTpl', { static: true })
50 protectTpl: TemplateRef<any>;
51 @ViewChild('rollbackTpl', { static: true })
52 rollbackTpl: TemplateRef<any>;
54 permission: Permission;
55 selection = new CdTableSelection();
56 tableActions: CdTableAction[];
58 data: RbdSnapshotModel[];
60 columns: CdTableColumn[];
65 'rbd/snap/create': (metadata) => {
66 const model = new RbdSnapshotModel();
67 model.name = metadata['snapshot_name'];
73 private authStorageService: AuthStorageService,
74 private modalService: BsModalService,
75 private dimlessBinaryPipe: DimlessBinaryPipe,
76 private cdDatePipe: CdDatePipe,
77 private rbdService: RbdService,
78 private taskManagerService: TaskManagerService,
79 private notificationService: NotificationService,
80 private summaryService: SummaryService,
81 private taskListService: TaskListService,
83 private actionLabels: ActionLabelsI18n
85 this.permission = this.authStorageService.getPermissions().rbdImage;
91 name: this.i18n('Name'),
93 cellTransformation: CellTemplate.executing,
97 name: this.i18n('Size'),
100 cellClass: 'text-right',
101 pipe: this.dimlessBinaryPipe
104 name: this.i18n('Provisioned'),
107 cellClass: 'text-right',
108 pipe: this.dimlessBinaryPipe
111 name: this.i18n('State'),
112 prop: 'is_protected',
114 cellClass: 'text-center',
115 cellTemplate: this.protectTpl
118 name: this.i18n('Created'),
121 pipe: this.cdDatePipe
127 const actions = new RbdSnapshotActionsModel(this.i18n, this.actionLabels, this.featuresName);
128 actions.create.click = () => this.openCreateSnapshotModal();
129 actions.rename.click = () => this.openEditSnapshotModal();
130 actions.protect.click = () => this.toggleProtection();
131 actions.unprotect.click = () => this.toggleProtection();
132 const getImageUri = () =>
133 this.selection.first() &&
134 `${encodeURIComponent(
135 this.rbdService.getImageSpec(this.poolName, this.namespace, this.rbdName)
136 )}/${encodeURIComponent(this.selection.first().name)}`;
137 actions.clone.routerLink = () => `/block/rbd/clone/${getImageUri()}`;
138 actions.copy.routerLink = () => `/block/rbd/copy/${getImageUri()}`;
139 actions.rollback.click = () => this.rollbackModal();
140 actions.deleteSnap.click = () => this.deleteSnapshotModal();
141 this.tableActions = actions.ordering;
143 const itemFilter = (entry, task) => {
144 return entry.name === task.metadata['snapshot_name'];
147 const taskFilter = (task) => {
149 ['rbd/snap/create', 'rbd/snap/delete', 'rbd/snap/edit', 'rbd/snap/rollback'].includes(
152 this.rbdService.getImageSpec(this.poolName, this.namespace, this.rbdName) ===
153 task.metadata['image_spec']
157 this.taskListService.init(
158 () => of(this.snapshots),
160 (items) => (this.data = items),
161 () => (this.data = this.snapshots),
168 private openSnapshotModal(taskName: string, snapName: string = null) {
169 this.modalRef = this.modalService.show(RbdSnapshotFormComponent);
170 this.modalRef.content.poolName = this.poolName;
171 this.modalRef.content.imageName = this.rbdName;
172 this.modalRef.content.namespace = this.namespace;
174 this.modalRef.content.setEditing();
176 // Auto-create a name for the snapshot: <image_name>_<timestamp_ISO_8601>
177 // https://en.wikipedia.org/wiki/ISO_8601
178 snapName = `${this.rbdName}_${moment().toISOString(true)}`;
180 this.modalRef.content.setSnapName(snapName);
181 this.modalRef.content.onSubmit.subscribe((snapshotName: string) => {
182 const executingTask = new ExecutingTask();
183 executingTask.name = taskName;
184 executingTask.metadata = {
185 image_name: this.rbdName,
186 pool_name: this.poolName,
187 snapshot_name: snapshotName
189 this.summaryService.addRunningTask(executingTask);
194 openCreateSnapshotModal() {
195 this.openSnapshotModal('rbd/snap/create');
198 openEditSnapshotModal() {
199 this.openSnapshotModal('rbd/snap/edit', this.selection.first().name);
203 const snapshotName = this.selection.first().name;
204 const isProtected = this.selection.first().is_protected;
205 const finishedTask = new FinishedTask();
206 finishedTask.name = 'rbd/snap/edit';
207 finishedTask.metadata = {
208 image_spec: this.rbdService.getImageSpec(this.poolName, this.namespace, this.rbdName),
209 snapshot_name: snapshotName
212 .protectSnapshot(this.poolName, this.namespace, this.rbdName, snapshotName, !isProtected)
215 const executingTask = new ExecutingTask();
216 executingTask.name = finishedTask.name;
217 executingTask.metadata = finishedTask.metadata;
218 this.summaryService.addRunningTask(executingTask);
220 this.taskManagerService.subscribe(
222 finishedTask.metadata,
223 (asyncFinishedTask: FinishedTask) => {
224 this.notificationService.notifyTask(asyncFinishedTask);
230 _asyncTask(task: string, taskName: string, snapshotName: string) {
231 const finishedTask = new FinishedTask();
232 finishedTask.name = taskName;
233 finishedTask.metadata = {
234 image_spec: this.rbdService.getImageSpec(this.poolName, this.namespace, this.rbdName),
235 snapshot_name: snapshotName
237 this.rbdService[task](this.poolName, this.namespace, this.rbdName, snapshotName)
240 const executingTask = new ExecutingTask();
241 executingTask.name = finishedTask.name;
242 executingTask.metadata = finishedTask.metadata;
243 this.summaryService.addRunningTask(executingTask);
244 this.modalRef.hide();
246 this.taskManagerService.subscribe(
248 executingTask.metadata,
249 (asyncFinishedTask: FinishedTask) => {
250 this.notificationService.notifyTask(asyncFinishedTask);
255 this.modalRef.content.stopLoadingSpinner();
260 const snapshotName = this.selection.selected[0].name;
261 const imageSpec = this.rbdService.getImageSpec(this.poolName, this.namespace, this.rbdName);
262 const initialState = {
263 titleText: this.i18n('RBD snapshot rollback'),
264 buttonText: this.i18n('Rollback'),
265 bodyTpl: this.rollbackTpl,
267 snapName: `${imageSpec}@${snapshotName}`
270 this._asyncTask('rollbackSnapshot', 'rbd/snap/rollback', snapshotName);
274 this.modalRef = this.modalService.show(ConfirmationModalComponent, { initialState });
277 deleteSnapshotModal() {
278 const snapshotName = this.selection.selected[0].name;
279 this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
281 itemDescription: this.i18n('RBD snapshot'),
282 itemNames: [snapshotName],
283 submitAction: () => this._asyncTask('deleteSnapshot', 'rbd/snap/delete', snapshotName)
288 updateSelection(selection: CdTableSelection) {
289 this.selection = selection;