1 import { Component, Input, OnChanges, OnInit, TemplateRef, ViewChild } from '@angular/core';
3 import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
4 import * as moment from 'moment';
5 import { of } from 'rxjs';
7 import { RbdService } from '../../../shared/api/rbd.service';
8 import { ConfirmationModalComponent } from '../../../shared/components/confirmation-modal/confirmation-modal.component';
9 import { CriticalConfirmationModalComponent } from '../../../shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
10 import { ActionLabelsI18n } from '../../../shared/constants/app.constants';
11 import { CellTemplate } from '../../../shared/enum/cell-template.enum';
12 import { CdTableAction } from '../../../shared/models/cd-table-action';
13 import { CdTableColumn } from '../../../shared/models/cd-table-column';
14 import { CdTableSelection } from '../../../shared/models/cd-table-selection';
15 import { ExecutingTask } from '../../../shared/models/executing-task';
16 import { FinishedTask } from '../../../shared/models/finished-task';
17 import { ImageSpec } from '../../../shared/models/image-spec';
18 import { Permission } from '../../../shared/models/permissions';
19 import { Task } from '../../../shared/models/task';
20 import { CdDatePipe } from '../../../shared/pipes/cd-date.pipe';
21 import { DimlessBinaryPipe } from '../../../shared/pipes/dimless-binary.pipe';
22 import { AuthStorageService } from '../../../shared/services/auth-storage.service';
23 import { ModalService } from '../../../shared/services/modal.service';
24 import { NotificationService } from '../../../shared/services/notification.service';
25 import { SummaryService } from '../../../shared/services/summary.service';
26 import { TaskListService } from '../../../shared/services/task-list.service';
27 import { TaskManagerService } from '../../../shared/services/task-manager.service';
28 import { RbdSnapshotFormModalComponent } from '../rbd-snapshot-form/rbd-snapshot-form-modal.component';
29 import { RbdSnapshotActionsModel } from './rbd-snapshot-actions.model';
30 import { RbdSnapshotModel } from './rbd-snapshot.model';
33 selector: 'cd-rbd-snapshot-list',
34 templateUrl: './rbd-snapshot-list.component.html',
35 styleUrls: ['./rbd-snapshot-list.component.scss'],
36 providers: [TaskListService]
38 export class RbdSnapshotListComponent implements OnInit, OnChanges {
40 snapshots: RbdSnapshotModel[] = [];
42 featuresName: string[];
50 nameTpl: 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[];
62 modalRef: NgbModalRef;
65 'rbd/snap/create': (metadata: any) => {
66 const model = new RbdSnapshotModel();
67 model.name = metadata['snapshot_name'];
73 private authStorageService: AuthStorageService,
74 private modalService: ModalService,
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,
82 private actionLabels: ActionLabelsI18n
84 this.permission = this.authStorageService.getPermissions().rbdImage;
90 name: $localize`Name`,
92 cellTransformation: CellTemplate.executing,
96 name: $localize`Size`,
99 cellClass: 'text-right',
100 pipe: this.dimlessBinaryPipe
103 name: $localize`Provisioned`,
106 cellClass: 'text-right',
107 pipe: this.dimlessBinaryPipe
110 name: $localize`State`,
111 prop: 'is_protected',
113 cellTransformation: CellTemplate.badge,
114 customTemplateConfig: {
116 true: { value: $localize`PROTECTED`, class: 'badge-success' },
117 false: { value: $localize`UNPROTECTED`, class: 'badge-info' }
122 name: $localize`Created`,
125 pipe: this.cdDatePipe
131 const imageSpec = new ImageSpec(this.poolName, this.namespace, this.rbdName);
133 const actions = new RbdSnapshotActionsModel(this.actionLabels, this.featuresName);
134 actions.create.click = () => this.openCreateSnapshotModal();
135 actions.rename.click = () => this.openEditSnapshotModal();
136 actions.protect.click = () => this.toggleProtection();
137 actions.unprotect.click = () => this.toggleProtection();
138 const getImageUri = () =>
139 this.selection.first() &&
140 `${imageSpec.toStringEncoded()}/${encodeURIComponent(this.selection.first().name)}`;
141 actions.clone.routerLink = () => `/block/rbd/clone/${getImageUri()}`;
142 actions.copy.routerLink = () => `/block/rbd/copy/${getImageUri()}`;
143 actions.rollback.click = () => this.rollbackModal();
144 actions.deleteSnap.click = () => this.deleteSnapshotModal();
145 this.tableActions = actions.ordering;
147 const itemFilter = (entry: any, task: Task) => {
148 return entry.name === task.metadata['snapshot_name'];
151 const taskFilter = (task: Task) => {
153 ['rbd/snap/create', 'rbd/snap/delete', 'rbd/snap/edit', 'rbd/snap/rollback'].includes(
155 ) && imageSpec.toString() === task.metadata['image_spec']
159 this.taskListService.init(
160 () => of(this.snapshots),
162 (items) => (this.data = items),
163 () => (this.data = this.snapshots),
170 private openSnapshotModal(taskName: string, snapName: string = null) {
171 this.modalRef = this.modalService.show(RbdSnapshotFormModalComponent);
172 this.modalRef.componentInstance.poolName = this.poolName;
173 this.modalRef.componentInstance.imageName = this.rbdName;
174 this.modalRef.componentInstance.namespace = this.namespace;
176 this.modalRef.componentInstance.setEditing();
178 // Auto-create a name for the snapshot: <image_name>_<timestamp_ISO_8601>
179 // https://en.wikipedia.org/wiki/ISO_8601
180 snapName = `${this.rbdName}_${moment().toISOString(true)}`;
182 this.modalRef.componentInstance.setSnapName(snapName);
183 this.modalRef.componentInstance.onSubmit.subscribe((snapshotName: string) => {
184 const executingTask = new ExecutingTask();
185 executingTask.name = taskName;
186 executingTask.metadata = {
187 image_name: this.rbdName,
188 pool_name: this.poolName,
189 snapshot_name: snapshotName
191 this.summaryService.addRunningTask(executingTask);
196 openCreateSnapshotModal() {
197 this.openSnapshotModal('rbd/snap/create');
200 openEditSnapshotModal() {
201 this.openSnapshotModal('rbd/snap/edit', this.selection.first().name);
205 const snapshotName = this.selection.first().name;
206 const isProtected = this.selection.first().is_protected;
207 const finishedTask = new FinishedTask();
208 finishedTask.name = 'rbd/snap/edit';
209 const imageSpec = new ImageSpec(this.poolName, this.namespace, this.rbdName);
210 finishedTask.metadata = {
211 image_spec: imageSpec.toString(),
212 snapshot_name: snapshotName
215 .protectSnapshot(imageSpec, snapshotName, !isProtected)
218 const executingTask = new ExecutingTask();
219 executingTask.name = finishedTask.name;
220 executingTask.metadata = finishedTask.metadata;
221 this.summaryService.addRunningTask(executingTask);
223 this.taskManagerService.subscribe(
225 finishedTask.metadata,
226 (asyncFinishedTask: FinishedTask) => {
227 this.notificationService.notifyTask(asyncFinishedTask);
233 _asyncTask(task: string, taskName: string, snapshotName: string) {
234 const finishedTask = new FinishedTask();
235 finishedTask.name = taskName;
236 finishedTask.metadata = {
237 image_spec: new ImageSpec(this.poolName, this.namespace, this.rbdName).toString(),
238 snapshot_name: snapshotName
240 const imageSpec = new ImageSpec(this.poolName, this.namespace, this.rbdName);
241 this.rbdService[task](imageSpec, snapshotName)
244 const executingTask = new ExecutingTask();
245 executingTask.name = finishedTask.name;
246 executingTask.metadata = finishedTask.metadata;
247 this.summaryService.addRunningTask(executingTask);
248 this.modalRef.close();
250 this.taskManagerService.subscribe(
252 executingTask.metadata,
253 (asyncFinishedTask: FinishedTask) => {
254 this.notificationService.notifyTask(asyncFinishedTask);
259 this.modalRef.componentInstance.stopLoadingSpinner();
264 const snapshotName = this.selection.selected[0].name;
265 const imageSpec = new ImageSpec(this.poolName, this.namespace, this.rbdName).toString();
266 const initialState = {
267 titleText: $localize`RBD snapshot rollback`,
268 buttonText: $localize`Rollback`,
269 bodyTpl: this.rollbackTpl,
271 snapName: `${imageSpec}@${snapshotName}`
274 this._asyncTask('rollbackSnapshot', 'rbd/snap/rollback', snapshotName);
278 this.modalRef = this.modalService.show(ConfirmationModalComponent, initialState);
281 deleteSnapshotModal() {
282 const snapshotName = this.selection.selected[0].name;
283 this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
284 itemDescription: $localize`RBD snapshot`,
285 itemNames: [snapshotName],
286 submitAction: () => this._asyncTask('deleteSnapshot', 'rbd/snap/delete', snapshotName)
290 updateSelection(selection: CdTableSelection) {
291 this.selection = selection;