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';
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[];
61 nameTpl: TemplateRef<any>;
62 @ViewChild('rollbackTpl', { static: true })
63 rollbackTpl: TemplateRef<any>;
65 permission: Permission;
66 selection = new CdTableSelection();
67 tableActions: CdTableAction[];
68 rbdTableActions: RbdSnapshotActionsModel;
71 data: RbdSnapshotModel[];
73 columns: CdTableColumn[];
75 modalRef: NgbModalRef;
78 'rbd/snap/create': (metadata: any) => {
79 const model = new RbdSnapshotModel();
80 model.name = metadata['snapshot_name'];
86 private authStorageService: AuthStorageService,
87 private modalService: ModalService,
88 private dimlessBinaryPipe: DimlessBinaryPipe,
89 private cdDatePipe: CdDatePipe,
90 private rbdService: RbdService,
91 private taskManagerService: TaskManagerService,
92 private notificationService: NotificationService,
93 private summaryService: SummaryService,
94 private taskListService: TaskListService,
95 private actionLabels: ActionLabelsI18n,
96 private cdr: ChangeDetectorRef
98 this.permission = this.authStorageService.getPermissions().rbdImage;
104 name: $localize`Name`,
106 cellTransformation: CellTemplate.executing,
110 name: $localize`Size`,
113 cellClass: 'text-right',
114 pipe: this.dimlessBinaryPipe
117 name: $localize`Provisioned`,
120 cellClass: 'text-right',
121 pipe: this.dimlessBinaryPipe
124 name: $localize`State`,
125 prop: 'is_protected',
127 cellTransformation: CellTemplate.badge,
128 customTemplateConfig: {
130 true: { value: $localize`PROTECTED`, class: 'badge-success' },
131 false: { value: $localize`UNPROTECTED`, class: 'badge-info' }
136 name: $localize`Created`,
139 pipe: this.cdDatePipe
143 this.imageSpec = new ImageSpec(this.poolName, this.namespace, this.rbdName);
144 this.rbdTableActions = new RbdSnapshotActionsModel(
149 this.rbdTableActions.create.click = () => this.openCreateSnapshotModal();
150 this.rbdTableActions.rename.click = () => this.openEditSnapshotModal();
151 this.rbdTableActions.protect.click = () => this.toggleProtection();
152 this.rbdTableActions.unprotect.click = () => this.toggleProtection();
153 const getImageUri = () =>
154 this.selection.first() &&
155 `${this.imageSpec.toStringEncoded()}/${encodeURIComponent(this.selection.first().name)}`;
156 this.rbdTableActions.clone.routerLink = () => `/block/rbd/clone/${getImageUri()}`;
157 this.rbdTableActions.copy.routerLink = () => `/block/rbd/copy/${getImageUri()}`;
158 this.rbdTableActions.rollback.click = () => this.rollbackModal();
159 this.rbdTableActions.deleteSnap.click = () => this.deleteSnapshotModal();
161 this.tableActions = this.rbdTableActions.ordering;
163 const itemFilter = (entry: any, task: Task) => {
164 return entry.name === task.metadata['snapshot_name'];
167 const taskFilter = (task: Task) => {
169 ['rbd/snap/create', 'rbd/snap/delete', 'rbd/snap/edit', 'rbd/snap/rollback'].includes(
171 ) && this.imageSpec.toString() === task.metadata['image_spec']
175 this.taskListService.init(
176 () => of(this.snapshots),
179 const hasChanges = CdHelperClass.updateChanged(this, {
183 this.cdr.detectChanges();
184 this.data = [...this.data];
188 const hasChanges = CdHelperClass.updateChanged(this, {
192 this.cdr.detectChanges();
193 this.data = [...this.data];
204 this.imageSpec = new ImageSpec(this.poolName, this.namespace, this.rbdName);
205 if (this.rbdTableActions) {
206 this.rbdTableActions.featuresName = this.featuresName;
208 this.taskListService.fetch();
212 private openSnapshotModal(taskName: string, snapName: string = null) {
213 this.modalRef = this.modalService.show(RbdSnapshotFormModalComponent);
214 this.modalRef.componentInstance.poolName = this.poolName;
215 this.modalRef.componentInstance.imageName = this.rbdName;
216 this.modalRef.componentInstance.namespace = this.namespace;
218 this.modalRef.componentInstance.setEditing();
220 // Auto-create a name for the snapshot: <image_name>_<timestamp_ISO_8601>
221 // https://en.wikipedia.org/wiki/ISO_8601
222 snapName = `${this.rbdName}_${moment().toISOString(true)}`;
224 this.modalRef.componentInstance.setSnapName(snapName);
225 this.modalRef.componentInstance.onSubmit.subscribe((snapshotName: string) => {
226 const executingTask = new ExecutingTask();
227 executingTask.name = taskName;
228 executingTask.metadata = {
229 image_spec: this.imageSpec.toString(),
230 snapshot_name: snapshotName
232 this.summaryService.addRunningTask(executingTask);
236 openCreateSnapshotModal() {
237 this.openSnapshotModal('rbd/snap/create');
240 openEditSnapshotModal() {
241 this.openSnapshotModal('rbd/snap/edit', this.selection.first().name);
245 const snapshotName = this.selection.first().name;
246 const isProtected = this.selection.first().is_protected;
247 const finishedTask = new FinishedTask();
248 finishedTask.name = 'rbd/snap/edit';
249 const imageSpec = new ImageSpec(this.poolName, this.namespace, this.rbdName);
250 finishedTask.metadata = {
251 image_spec: imageSpec.toString(),
252 snapshot_name: snapshotName
255 .protectSnapshot(imageSpec, snapshotName, !isProtected)
258 const executingTask = new ExecutingTask();
259 executingTask.name = finishedTask.name;
260 executingTask.metadata = finishedTask.metadata;
261 this.summaryService.addRunningTask(executingTask);
262 this.taskManagerService.subscribe(
264 finishedTask.metadata,
265 (asyncFinishedTask: FinishedTask) => {
266 this.notificationService.notifyTask(asyncFinishedTask);
272 _asyncTask(task: string, taskName: string, snapshotName: string) {
273 const finishedTask = new FinishedTask();
274 finishedTask.name = taskName;
275 finishedTask.metadata = {
276 image_spec: new ImageSpec(this.poolName, this.namespace, this.rbdName).toString(),
277 snapshot_name: snapshotName
279 const imageSpec = new ImageSpec(this.poolName, this.namespace, this.rbdName);
280 this.rbdService[task](imageSpec, snapshotName)
283 const executingTask = new ExecutingTask();
284 executingTask.name = finishedTask.name;
285 executingTask.metadata = finishedTask.metadata;
286 this.summaryService.addRunningTask(executingTask);
287 this.modalRef.close();
288 this.taskManagerService.subscribe(
290 executingTask.metadata,
291 (asyncFinishedTask: FinishedTask) => {
292 this.notificationService.notifyTask(asyncFinishedTask);
297 this.modalRef.componentInstance.stopLoadingSpinner();
302 const snapshotName = this.selection.selected[0].name;
303 const imageSpec = new ImageSpec(this.poolName, this.namespace, this.rbdName).toString();
304 const initialState = {
305 titleText: $localize`RBD snapshot rollback`,
306 buttonText: $localize`Rollback`,
307 bodyTpl: this.rollbackTpl,
309 snapName: `${imageSpec}@${snapshotName}`
312 this._asyncTask('rollbackSnapshot', 'rbd/snap/rollback', snapshotName);
316 this.modalRef = this.modalService.show(ConfirmationModalComponent, initialState);
319 deleteSnapshotModal() {
320 const snapshotName = this.selection.selected[0].name;
321 this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
322 itemDescription: $localize`RBD snapshot`,
323 itemNames: [snapshotName],
324 submitAction: () => this._asyncTask('deleteSnapshot', 'rbd/snap/delete', snapshotName)
328 updateSelection(selection: CdTableSelection) {
329 this.selection = selection;