1 import { Component, Input, OnChanges, OnInit, TemplateRef, ViewChild } from '@angular/core';
3 import * as _ from 'lodash';
4 import * as moment from 'moment';
5 import { BsModalRef, BsModalService } from 'ngx-bootstrap';
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 { DeletionModalComponent } from '../../../shared/components/deletion-modal/deletion-modal.component';
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 { Permission } from '../../../shared/models/permissions';
18 import { CdDatePipe } from '../../../shared/pipes/cd-date.pipe';
19 import { DimlessBinaryPipe } from '../../../shared/pipes/dimless-binary.pipe';
20 import { AuthStorageService } from '../../../shared/services/auth-storage.service';
21 import { NotificationService } from '../../../shared/services/notification.service';
22 import { SummaryService } from '../../../shared/services/summary.service';
23 import { TaskListService } from '../../../shared/services/task-list.service';
24 import { TaskManagerService } from '../../../shared/services/task-manager.service';
25 import { RbdSnapshotFormComponent } from '../rbd-snapshot-form/rbd-snapshot-form.component';
26 import { RbdSnapshotActionsModel } from './rbd-snapshot-actions.model';
27 import { RbdSnapshotModel } from './rbd-snapshot.model';
30 selector: 'cd-rbd-snapshot-list',
31 templateUrl: './rbd-snapshot-list.component.html',
32 styleUrls: ['./rbd-snapshot-list.component.scss'],
33 providers: [TaskListService]
35 export class RbdSnapshotListComponent implements OnInit, OnChanges {
37 snapshots: RbdSnapshotModel[] = [];
43 nameTpl: TemplateRef<any>;
44 @ViewChild('protectTpl')
45 protectTpl: TemplateRef<any>;
46 @ViewChild('rollbackTpl')
47 rollbackTpl: TemplateRef<any>;
49 permission: Permission;
50 selection = new CdTableSelection();
51 tableActions: CdTableAction[];
53 data: RbdSnapshotModel[];
55 columns: CdTableColumn[];
60 'rbd/snap/create': (metadata) => {
61 const model = new RbdSnapshotModel();
62 model.name = metadata['snapshot_name'];
68 private authStorageService: AuthStorageService,
69 private modalService: BsModalService,
70 private dimlessBinaryPipe: DimlessBinaryPipe,
71 private cdDatePipe: CdDatePipe,
72 private rbdService: RbdService,
73 private taskManagerService: TaskManagerService,
74 private notificationService: NotificationService,
75 private summaryService: SummaryService,
76 private taskListService: TaskListService
78 this.permission = this.authStorageService.getPermissions().rbdImage;
79 const actions = new RbdSnapshotActionsModel();
80 actions.create.click = () => this.openCreateSnapshotModal();
81 actions.rename.click = () => this.openEditSnapshotModal();
82 actions.protect.click = () => this.toggleProtection();
83 actions.unprotect.click = () => this.toggleProtection();
84 const getImageUri = () =>
85 this.selection.first() &&
86 `${encodeURI(this.poolName)}/${encodeURI(this.rbdName)}/${encodeURI(
87 this.selection.first().name
89 actions.clone.routerLink = () => `/block/rbd/clone/${getImageUri()}`;
90 actions.copy.routerLink = () => `/block/rbd/copy/${getImageUri()}`;
91 actions.rollback.click = () => this.rollbackModal();
92 actions.deleteSnap.click = () => this.deleteSnapshotModal();
93 this.tableActions = actions.ordering;
101 cellTransformation: CellTemplate.executing,
108 cellClass: 'text-right',
109 pipe: this.dimlessBinaryPipe
115 cellClass: 'text-right',
116 pipe: this.dimlessBinaryPipe
120 prop: 'is_protected',
122 cellClass: 'text-center',
123 cellTemplate: this.protectTpl
129 pipe: this.cdDatePipe
135 const itemFilter = (entry, task) => {
136 return entry.name === task.metadata['snapshot_name'];
139 const taskFilter = (task) => {
141 ['rbd/snap/create', 'rbd/snap/delete', 'rbd/snap/edit', 'rbd/snap/rollback'].includes(
144 this.poolName === task.metadata['pool_name'] &&
145 this.rbdName === task.metadata['image_name']
149 this.taskListService.init(
150 () => of(this.snapshots),
152 (items) => (this.data = items),
153 () => (this.data = this.snapshots),
160 private openSnapshotModal(taskName: string, snapName: string = null) {
161 this.modalRef = this.modalService.show(RbdSnapshotFormComponent);
162 this.modalRef.content.poolName = this.poolName;
163 this.modalRef.content.imageName = this.rbdName;
165 this.modalRef.content.setEditing();
167 // Auto-create a name for the snapshot: <image_name>_<timestamp_ISO_8601>
168 // https://en.wikipedia.org/wiki/ISO_8601
169 snapName = `${this.rbdName}-${moment()
171 .format('YYYYMMDD[T]HHmmss[Z]')}`;
173 this.modalRef.content.setSnapName(snapName);
174 this.modalRef.content.onSubmit.subscribe((snapshotName: string) => {
175 const executingTask = new ExecutingTask();
176 executingTask.name = taskName;
177 executingTask.metadata = {
178 image_name: this.rbdName,
179 pool_name: this.poolName,
180 snapshot_name: snapshotName
182 this.summaryService.addRunningTask(executingTask);
187 openCreateSnapshotModal() {
188 this.openSnapshotModal('rbd/snap/create');
191 openEditSnapshotModal() {
192 this.openSnapshotModal('rbd/snap/edit', this.selection.first().name);
196 const snapshotName = this.selection.first().name;
197 const isProtected = this.selection.first().is_protected;
198 const finishedTask = new FinishedTask();
199 finishedTask.name = 'rbd/snap/edit';
200 finishedTask.metadata = {
201 pool_name: this.poolName,
202 image_name: this.rbdName,
203 snapshot_name: snapshotName
206 .protectSnapshot(this.poolName, this.rbdName, snapshotName, !isProtected)
209 const executingTask = new ExecutingTask();
210 executingTask.name = finishedTask.name;
211 executingTask.metadata = finishedTask.metadata;
212 this.summaryService.addRunningTask(executingTask);
214 this.taskManagerService.subscribe(
216 finishedTask.metadata,
217 (asyncFinishedTask: FinishedTask) => {
218 this.notificationService.notifyTask(asyncFinishedTask);
224 _asyncTask(task: string, taskName: string, snapshotName: string) {
225 const finishedTask = new FinishedTask();
226 finishedTask.name = taskName;
227 finishedTask.metadata = {
228 pool_name: this.poolName,
229 image_name: this.rbdName,
230 snapshot_name: snapshotName
232 this.rbdService[task](this.poolName, this.rbdName, snapshotName)
235 const executingTask = new ExecutingTask();
236 executingTask.name = finishedTask.name;
237 executingTask.metadata = finishedTask.metadata;
238 this.summaryService.addRunningTask(executingTask);
239 this.modalRef.hide();
241 this.taskManagerService.subscribe(
243 executingTask.metadata,
244 (asyncFinishedTask: FinishedTask) => {
245 this.notificationService.notifyTask(asyncFinishedTask);
250 this.modalRef.content.stopLoadingSpinner();
255 const snapshotName = this.selection.selected[0].name;
256 const initialState = {
257 titleText: 'RBD snapshot rollback',
258 buttonText: 'Rollback',
259 bodyTpl: this.rollbackTpl,
261 snapName: `${this.poolName}/${this.rbdName}@${snapshotName}`
264 this._asyncTask('rollbackSnapshot', 'rbd/snap/rollback', snapshotName);
268 this.modalRef = this.modalService.show(ConfirmationModalComponent, { initialState });
271 deleteSnapshotModal() {
272 const snapshotName = this.selection.selected[0].name;
273 this.modalRef = this.modalService.show(DeletionModalComponent, {
275 itemDescription: 'RBD snapshot',
276 submitAction: () => this._asyncTask('deleteSnapshot', 'rbd/snap/delete', snapshotName)
281 updateSelection(selection: CdTableSelection) {
282 this.selection = selection;