2 ChangeDetectionStrategy,
10 } from '@angular/core';
12 import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
13 import moment from 'moment';
14 import { of } from 'rxjs';
16 import { RbdService } from '~/app/shared/api/rbd.service';
17 import { CdHelperClass } from '~/app/shared/classes/cd-helper.class';
18 import { ConfirmationModalComponent } from '~/app/shared/components/confirmation-modal/confirmation-modal.component';
19 import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
20 import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
21 import { CellTemplate } from '~/app/shared/enum/cell-template.enum';
22 import { CdTableAction } from '~/app/shared/models/cd-table-action';
23 import { CdTableColumn } from '~/app/shared/models/cd-table-column';
24 import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
25 import { ExecutingTask } from '~/app/shared/models/executing-task';
26 import { FinishedTask } from '~/app/shared/models/finished-task';
27 import { ImageSpec } from '~/app/shared/models/image-spec';
28 import { Permission } from '~/app/shared/models/permissions';
29 import { Task } from '~/app/shared/models/task';
30 import { CdDatePipe } from '~/app/shared/pipes/cd-date.pipe';
31 import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe';
32 import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
33 import { ModalService } from '~/app/shared/services/modal.service';
34 import { NotificationService } from '~/app/shared/services/notification.service';
35 import { SummaryService } from '~/app/shared/services/summary.service';
36 import { TaskListService } from '~/app/shared/services/task-list.service';
37 import { TaskManagerService } from '~/app/shared/services/task-manager.service';
38 import { RbdSnapshotFormModalComponent } from '../rbd-snapshot-form/rbd-snapshot-form-modal.component';
39 import { RbdSnapshotActionsModel } from './rbd-snapshot-actions.model';
40 import { RbdSnapshotModel } from './rbd-snapshot.model';
41 import { DeletionImpact } from '~/app/shared/enum/critical-confirmation-modal-impact.enum';
44 selector: 'cd-rbd-snapshot-list',
45 templateUrl: './rbd-snapshot-list.component.html',
46 styleUrls: ['./rbd-snapshot-list.component.scss'],
47 providers: [TaskListService],
48 changeDetection: ChangeDetectionStrategy.OnPush
50 export class RbdSnapshotListComponent implements OnInit, OnChanges {
52 snapshots: RbdSnapshotModel[] = [];
54 featuresName: string[];
66 nameTpl: TemplateRef<any>;
67 @ViewChild('rollbackTpl', { static: true })
68 rollbackTpl: TemplateRef<any>;
70 permission: Permission;
71 selection = new CdTableSelection();
72 tableActions: CdTableAction[];
73 rbdTableActions: RbdSnapshotActionsModel;
76 data: RbdSnapshotModel[];
78 columns: CdTableColumn[];
80 modalRef: NgbModalRef;
83 'rbd/snap/create': (metadata: any) => {
84 const model = new RbdSnapshotModel();
85 model.name = metadata['snapshot_name'];
91 private authStorageService: AuthStorageService,
92 private modalService: ModalService,
93 private dimlessBinaryPipe: DimlessBinaryPipe,
94 private cdDatePipe: CdDatePipe,
95 private rbdService: RbdService,
96 private taskManagerService: TaskManagerService,
97 private notificationService: NotificationService,
98 private summaryService: SummaryService,
99 private taskListService: TaskListService,
100 private actionLabels: ActionLabelsI18n,
101 private cdr: ChangeDetectorRef
103 this.permission = this.authStorageService.getPermissions().rbdImage;
109 name: $localize`Name`,
111 cellTransformation: CellTemplate.executing,
115 name: $localize`Size`,
118 cellClass: 'text-right',
119 pipe: this.dimlessBinaryPipe
122 name: $localize`Used`,
125 cellClass: 'text-right',
126 pipe: this.dimlessBinaryPipe
129 name: $localize`State`,
130 prop: 'is_protected',
132 cellTransformation: CellTemplate.badge,
133 customTemplateConfig: {
135 true: { value: $localize`PROTECTED`, class: 'badge-success' },
136 false: { value: $localize`UNPROTECTED`, class: 'badge-info' }
141 name: $localize`Created`,
144 pipe: this.cdDatePipe
148 this.imageSpec = new ImageSpec(this.poolName, this.namespace, this.rbdName);
149 this.rbdTableActions = new RbdSnapshotActionsModel(
154 this.rbdTableActions.create.click = () => this.openCreateSnapshotModal();
155 this.rbdTableActions.rename.click = () => this.openEditSnapshotModal();
156 this.rbdTableActions.protect.click = () => this.toggleProtection();
157 this.rbdTableActions.unprotect.click = () => this.toggleProtection();
158 const getImageUri = () =>
159 this.selection.first() &&
160 `${this.imageSpec.toStringEncoded()}/${encodeURIComponent(this.selection.first().name)}`;
161 this.rbdTableActions.clone.routerLink = () => `/block/rbd/clone/${getImageUri()}`;
162 this.rbdTableActions.copy.routerLink = () => `/block/rbd/copy/${getImageUri()}`;
163 this.rbdTableActions.rollback.click = () => this.rollbackModal();
164 this.rbdTableActions.deleteSnap.click = () => this.deleteSnapshotModal();
166 this.tableActions = this.rbdTableActions.ordering;
168 const itemFilter = (entry: any, task: Task) => {
169 return entry.name === task.metadata['snapshot_name'];
172 const taskFilter = (task: Task) => {
174 ['rbd/snap/create', 'rbd/snap/delete', 'rbd/snap/edit', 'rbd/snap/rollback'].includes(
176 ) && this.imageSpec.toString() === task.metadata['image_spec']
180 this.taskListService.init(
181 () => of(this.snapshots),
184 const hasChanges = CdHelperClass.updateChanged(this, {
188 this.cdr.detectChanges();
189 this.data = [...this.data];
193 const hasChanges = CdHelperClass.updateChanged(this, {
197 this.cdr.detectChanges();
198 this.data = [...this.data];
209 this.imageSpec = new ImageSpec(this.poolName, this.namespace, this.rbdName);
210 if (this.rbdTableActions) {
211 this.rbdTableActions.featuresName = this.featuresName;
213 this.taskListService.fetch();
217 private openSnapshotModal(taskName: string, snapName: string = null) {
218 const modalVariables = {
219 mirroring: this.mirroring
221 this.modalRef = this.modalService.show(RbdSnapshotFormModalComponent, modalVariables);
222 this.modalRef.componentInstance.poolName = this.poolName;
223 this.modalRef.componentInstance.imageName = this.rbdName;
224 this.modalRef.componentInstance.namespace = this.namespace;
226 this.modalRef.componentInstance.setEditing();
228 // Auto-create a name for the snapshot: <image_name>_<timestamp_ISO_8601>
229 // https://en.wikipedia.org/wiki/ISO_8601
230 snapName = `${this.rbdName}_${moment().toISOString(true)}`;
232 this.modalRef.componentInstance.setSnapName(snapName);
233 this.modalRef.componentInstance.onSubmit.subscribe((snapshotName: string) => {
234 const executingTask = new ExecutingTask();
235 executingTask.name = taskName;
236 executingTask.metadata = {
237 image_spec: this.imageSpec.toString(),
238 snapshot_name: snapshotName
240 this.summaryService.addRunningTask(executingTask);
244 openCreateSnapshotModal() {
245 this.openSnapshotModal('rbd/snap/create');
248 openEditSnapshotModal() {
249 this.openSnapshotModal('rbd/snap/edit', this.selection.first().name);
253 const snapshotName = this.selection.first().name;
254 const isProtected = this.selection.first().is_protected;
255 const finishedTask = new FinishedTask();
256 finishedTask.name = 'rbd/snap/edit';
257 const imageSpec = new ImageSpec(this.poolName, this.namespace, this.rbdName);
258 finishedTask.metadata = {
259 image_spec: imageSpec.toString(),
260 snapshot_name: snapshotName
263 .protectSnapshot(imageSpec, snapshotName, !isProtected)
266 const executingTask = new ExecutingTask();
267 executingTask.name = finishedTask.name;
268 executingTask.metadata = finishedTask.metadata;
269 this.summaryService.addRunningTask(executingTask);
270 this.taskManagerService.subscribe(
272 finishedTask.metadata,
273 (asyncFinishedTask: FinishedTask) => {
274 this.notificationService.notifyTask(asyncFinishedTask);
280 _asyncTask(task: string, taskName: string, snapshotName: string) {
281 const finishedTask = new FinishedTask();
282 finishedTask.name = taskName;
283 finishedTask.metadata = {
284 image_spec: new ImageSpec(this.poolName, this.namespace, this.rbdName).toString(),
285 snapshot_name: snapshotName
287 const imageSpec = new ImageSpec(this.poolName, this.namespace, this.rbdName);
288 this.rbdService[task](imageSpec, snapshotName)
291 const executingTask = new ExecutingTask();
292 executingTask.name = finishedTask.name;
293 executingTask.metadata = finishedTask.metadata;
294 this.summaryService.addRunningTask(executingTask);
295 this.modalRef.close();
296 this.taskManagerService.subscribe(
298 executingTask.metadata,
299 (asyncFinishedTask: FinishedTask) => {
300 this.notificationService.notifyTask(asyncFinishedTask);
305 this.modalRef.componentInstance.stopLoadingSpinner();
310 const snapshotName = this.selection.selected[0].name;
311 const imageSpec = new ImageSpec(this.poolName, this.namespace, this.rbdName).toString();
312 const initialState = {
313 titleText: $localize`RBD snapshot rollback`,
314 buttonText: $localize`Rollback`,
315 bodyTpl: this.rollbackTpl,
317 snapName: `${imageSpec}@${snapshotName}`
320 this._asyncTask('rollbackSnapshot', 'rbd/snap/rollback', snapshotName);
324 this.modalRef = this.modalService.show(ConfirmationModalComponent, initialState);
327 deleteSnapshotModal() {
328 const snapshotName = this.selection.selected[0].name;
329 this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
330 impact: DeletionImpact.high,
331 itemDescription: $localize`RBD snapshot`,
332 itemNames: [snapshotName],
333 submitAction: () => this._asyncTask('deleteSnapshot', 'rbd/snap/delete', snapshotName)
337 updateSelection(selection: CdTableSelection) {
338 this.selection = selection;