2 ChangeDetectionStrategy,
10 } from '@angular/core';
12 import moment from 'moment';
13 import { of } from 'rxjs';
15 import { RbdService } from '~/app/shared/api/rbd.service';
16 import { CdHelperClass } from '~/app/shared/classes/cd-helper.class';
17 import { ConfirmationModalComponent } from '~/app/shared/components/confirmation-modal/confirmation-modal.component';
18 import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
19 import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
20 import { CellTemplate } from '~/app/shared/enum/cell-template.enum';
21 import { CdTableAction } from '~/app/shared/models/cd-table-action';
22 import { CdTableColumn } from '~/app/shared/models/cd-table-column';
23 import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
24 import { ExecutingTask } from '~/app/shared/models/executing-task';
25 import { FinishedTask } from '~/app/shared/models/finished-task';
26 import { ImageSpec } from '~/app/shared/models/image-spec';
27 import { Permission } from '~/app/shared/models/permissions';
28 import { Task } from '~/app/shared/models/task';
29 import { CdDatePipe } from '~/app/shared/pipes/cd-date.pipe';
30 import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe';
31 import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
32 import { NotificationService } from '~/app/shared/services/notification.service';
33 import { SummaryService } from '~/app/shared/services/summary.service';
34 import { TaskListService } from '~/app/shared/services/task-list.service';
35 import { TaskManagerService } from '~/app/shared/services/task-manager.service';
36 import { RbdSnapshotFormModalComponent } from '../rbd-snapshot-form/rbd-snapshot-form-modal.component';
37 import { RbdSnapshotActionsModel } from './rbd-snapshot-actions.model';
38 import { RbdSnapshotModel } from './rbd-snapshot.model';
39 import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
40 import { DeletionImpact } from '~/app/shared/enum/critical-confirmation-modal-impact.enum';
43 selector: 'cd-rbd-snapshot-list',
44 templateUrl: './rbd-snapshot-list.component.html',
45 styleUrls: ['./rbd-snapshot-list.component.scss'],
46 providers: [TaskListService],
47 changeDetection: ChangeDetectionStrategy.OnPush
49 export class RbdSnapshotListComponent implements OnInit, OnChanges {
51 snapshots: RbdSnapshotModel[] = [];
53 featuresName: string[];
65 nameTpl: TemplateRef<any>;
66 @ViewChild('rollbackTpl', { static: true })
67 rollbackTpl: TemplateRef<any>;
69 permission: Permission;
70 selection = new CdTableSelection();
71 tableActions: CdTableAction[];
72 rbdTableActions: RbdSnapshotActionsModel;
75 data: RbdSnapshotModel[];
77 columns: CdTableColumn[];
82 'rbd/snap/create': (metadata: any) => {
83 const model = new RbdSnapshotModel();
84 model.name = metadata['snapshot_name'];
90 private authStorageService: AuthStorageService,
91 private dimlessBinaryPipe: DimlessBinaryPipe,
92 private cdDatePipe: CdDatePipe,
93 private rbdService: RbdService,
94 private taskManagerService: TaskManagerService,
95 private notificationService: NotificationService,
96 private summaryService: SummaryService,
97 private taskListService: TaskListService,
98 private actionLabels: ActionLabelsI18n,
99 private cdr: ChangeDetectorRef,
100 private cdsModalService: ModalCdsService
102 this.permission = this.authStorageService.getPermissions().rbdImage;
108 name: $localize`Name`,
110 cellTransformation: CellTemplate.executing,
114 name: $localize`Size`,
117 cellClass: 'text-right',
118 pipe: this.dimlessBinaryPipe
121 name: $localize`Used`,
124 cellClass: 'text-right',
125 pipe: this.dimlessBinaryPipe
128 name: $localize`State`,
129 prop: 'is_protected',
131 cellTransformation: CellTemplate.badge,
132 customTemplateConfig: {
134 true: { value: $localize`PROTECTED`, class: 'badge-success' },
135 false: { value: $localize`UNPROTECTED`, class: 'badge-info' }
140 name: $localize`Created`,
143 pipe: this.cdDatePipe
147 this.imageSpec = new ImageSpec(this.poolName, this.namespace, this.rbdName);
148 this.rbdTableActions = new RbdSnapshotActionsModel(
153 this.rbdTableActions.create.click = () => this.openCreateSnapshotModal();
154 this.rbdTableActions.rename.click = () => this.openEditSnapshotModal();
155 this.rbdTableActions.protect.click = () => this.toggleProtection();
156 this.rbdTableActions.unprotect.click = () => this.toggleProtection();
157 const getImageUri = () =>
158 this.selection.first() &&
159 `${this.imageSpec.toStringEncoded()}/${encodeURIComponent(this.selection.first().name)}`;
160 this.rbdTableActions.clone.routerLink = () => `/block/rbd/clone/${getImageUri()}`;
161 this.rbdTableActions.copy.routerLink = () => `/block/rbd/copy/${getImageUri()}`;
162 this.rbdTableActions.rollback.click = () => this.rollbackModal();
163 this.rbdTableActions.deleteSnap.click = () => this.deleteSnapshotModal();
165 this.tableActions = this.rbdTableActions.ordering;
167 const itemFilter = (entry: any, task: Task) => {
168 return entry.name === task.metadata['snapshot_name'];
171 const taskFilter = (task: Task) => {
173 ['rbd/snap/create', 'rbd/snap/delete', 'rbd/snap/edit', 'rbd/snap/rollback'].includes(
175 ) && this.imageSpec.toString() === task.metadata['image_spec']
179 this.taskListService.init(
180 () => of(this.snapshots),
183 const hasChanges = CdHelperClass.updateChanged(this, {
187 this.cdr.detectChanges();
188 this.data = [...this.data];
192 const hasChanges = CdHelperClass.updateChanged(this, {
196 this.cdr.detectChanges();
197 this.data = [...this.data];
208 this.imageSpec = new ImageSpec(this.poolName, this.namespace, this.rbdName);
209 if (this.rbdTableActions) {
210 this.rbdTableActions.featuresName = this.featuresName;
212 this.taskListService.fetch();
216 private openSnapshotModal(taskName: string, snapName: string = null) {
217 const modalVariables = {
218 poolName: this.poolName,
219 imageName: this.rbdName,
220 namespace: this.namespace,
221 mirroring: this.mirroring
223 this.modalRef = this.cdsModalService.show(RbdSnapshotFormModalComponent, modalVariables);
225 this.modalRef.setEditing();
227 // Auto-create a name for the snapshot: <image_name>_<timestamp_ISO_8601>
228 // https://en.wikipedia.org/wiki/ISO_8601
229 snapName = `${this.rbdName}_${moment().toISOString(true)}`;
231 this.modalRef.setSnapName(snapName);
232 this.modalRef.onSubmit.subscribe((snapshotName: string) => {
233 const executingTask = new ExecutingTask();
234 executingTask.name = taskName;
235 executingTask.metadata = {
236 image_spec: this.imageSpec.toString(),
237 snapshot_name: snapshotName
239 this.summaryService.addRunningTask(executingTask);
243 openCreateSnapshotModal() {
244 this.openSnapshotModal('rbd/snap/create');
247 openEditSnapshotModal() {
248 this.openSnapshotModal('rbd/snap/edit', this.selection.first().name);
252 const snapshotName = this.selection.first().name;
253 const isProtected = this.selection.first().is_protected;
254 const finishedTask = new FinishedTask();
255 finishedTask.name = 'rbd/snap/edit';
256 const imageSpec = new ImageSpec(this.poolName, this.namespace, this.rbdName);
257 finishedTask.metadata = {
258 image_spec: imageSpec.toString(),
259 snapshot_name: snapshotName
262 .protectSnapshot(imageSpec, snapshotName, !isProtected)
265 const executingTask = new ExecutingTask();
266 executingTask.name = finishedTask.name;
267 executingTask.metadata = finishedTask.metadata;
268 this.summaryService.addRunningTask(executingTask);
269 this.taskManagerService.subscribe(
271 finishedTask.metadata,
272 (asyncFinishedTask: FinishedTask) => {
273 this.notificationService.notifyTask(asyncFinishedTask);
279 _asyncTask(task: string, taskName: string, snapshotName: string) {
280 const finishedTask = new FinishedTask();
281 finishedTask.name = taskName;
282 finishedTask.metadata = {
283 image_spec: new ImageSpec(this.poolName, this.namespace, this.rbdName).toString(),
284 snapshot_name: snapshotName
286 const imageSpec = new ImageSpec(this.poolName, this.namespace, this.rbdName);
287 this.rbdService[task](imageSpec, snapshotName)
290 const executingTask = new ExecutingTask();
291 executingTask.name = finishedTask.name;
292 executingTask.metadata = finishedTask.metadata;
293 this.summaryService.addRunningTask(executingTask);
294 this.cdsModalService.dismissAll();
295 this.taskManagerService.subscribe(
297 executingTask.metadata,
298 (asyncFinishedTask: FinishedTask) => {
299 this.notificationService.notifyTask(asyncFinishedTask);
304 this.cdsModalService.stopLoadingSpinner(this.modalRef.snapshotForm);
309 const snapshotName = this.selection.selected[0].name;
310 const imageSpec = new ImageSpec(this.poolName, this.namespace, this.rbdName).toString();
311 const initialState = {
312 titleText: $localize`RBD snapshot rollback`,
313 buttonText: $localize`Rollback`,
314 bodyTpl: this.rollbackTpl,
316 snapName: `${imageSpec}@${snapshotName}`
319 this._asyncTask('rollbackSnapshot', 'rbd/snap/rollback', snapshotName);
323 this.modalRef = this.cdsModalService.show(ConfirmationModalComponent, initialState);
326 deleteSnapshotModal() {
327 const snapshotName = this.selection.selected[0].name;
328 this.modalRef = this.cdsModalService.show(CriticalConfirmationModalComponent, {
329 impact: DeletionImpact.high,
330 itemDescription: $localize`RBD snapshot`,
331 itemNames: [snapshotName],
332 submitAction: () => this._asyncTask('deleteSnapshot', 'rbd/snap/delete', snapshotName)
336 updateSelection(selection: CdTableSelection) {
337 this.selection = selection;