]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/blob
0c44327bfb7fe723bc24d6718a87bb996fc20c39
[ceph-ci.git] /
1 import { Component, Input, OnChanges, OnInit, TemplateRef, ViewChild } from '@angular/core';
2
3 import * as _ from 'lodash';
4 import * as moment from 'moment';
5 import { BsModalRef, BsModalService } from 'ngx-bootstrap';
6 import { of } from 'rxjs';
7
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';
28
29 @Component({
30   selector: 'cd-rbd-snapshot-list',
31   templateUrl: './rbd-snapshot-list.component.html',
32   styleUrls: ['./rbd-snapshot-list.component.scss'],
33   providers: [TaskListService]
34 })
35 export class RbdSnapshotListComponent implements OnInit, OnChanges {
36   @Input()
37   snapshots: RbdSnapshotModel[] = [];
38   @Input()
39   poolName: string;
40   @Input()
41   rbdName: string;
42   @ViewChild('nameTpl')
43   nameTpl: TemplateRef<any>;
44   @ViewChild('protectTpl')
45   protectTpl: TemplateRef<any>;
46   @ViewChild('rollbackTpl')
47   rollbackTpl: TemplateRef<any>;
48
49   permission: Permission;
50   selection = new CdTableSelection();
51   tableActions: CdTableAction[];
52
53   data: RbdSnapshotModel[];
54
55   columns: CdTableColumn[];
56
57   modalRef: BsModalRef;
58
59   builders = {
60     'rbd/snap/create': (metadata) => {
61       const model = new RbdSnapshotModel();
62       model.name = metadata['snapshot_name'];
63       return model;
64     }
65   };
66
67   constructor(
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
77   ) {
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
88       )}`;
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;
94   }
95
96   ngOnInit() {
97     this.columns = [
98       {
99         name: 'Name',
100         prop: 'name',
101         cellTransformation: CellTemplate.executing,
102         flexGrow: 2
103       },
104       {
105         name: 'Size',
106         prop: 'size',
107         flexGrow: 1,
108         cellClass: 'text-right',
109         pipe: this.dimlessBinaryPipe
110       },
111       {
112         name: 'Provisioned',
113         prop: 'disk_usage',
114         flexGrow: 1,
115         cellClass: 'text-right',
116         pipe: this.dimlessBinaryPipe
117       },
118       {
119         name: 'State',
120         prop: 'is_protected',
121         flexGrow: 1,
122         cellClass: 'text-center',
123         cellTemplate: this.protectTpl
124       },
125       {
126         name: 'Created',
127         prop: 'timestamp',
128         flexGrow: 1,
129         pipe: this.cdDatePipe
130       }
131     ];
132   }
133
134   ngOnChanges() {
135     const itemFilter = (entry, task) => {
136       return entry.name === task.metadata['snapshot_name'];
137     };
138
139     const taskFilter = (task) => {
140       return (
141         ['rbd/snap/create', 'rbd/snap/delete', 'rbd/snap/edit', 'rbd/snap/rollback'].includes(
142           task.name
143         ) &&
144         this.poolName === task.metadata['pool_name'] &&
145         this.rbdName === task.metadata['image_name']
146       );
147     };
148
149     this.taskListService.init(
150       () => of(this.snapshots),
151       null,
152       (items) => (this.data = items),
153       () => (this.data = this.snapshots),
154       taskFilter,
155       itemFilter,
156       this.builders
157     );
158   }
159
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;
164     if (snapName) {
165       this.modalRef.content.setEditing();
166     } else {
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()
170         .utc()
171         .format('YYYYMMDD[T]HHmmss[Z]')}`;
172     }
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
181       };
182       this.summaryService.addRunningTask(executingTask);
183       this.ngOnChanges();
184     });
185   }
186
187   openCreateSnapshotModal() {
188     this.openSnapshotModal('rbd/snap/create');
189   }
190
191   openEditSnapshotModal() {
192     this.openSnapshotModal('rbd/snap/edit', this.selection.first().name);
193   }
194
195   toggleProtection() {
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
204     };
205     this.rbdService
206       .protectSnapshot(this.poolName, this.rbdName, snapshotName, !isProtected)
207       .toPromise()
208       .then((resp) => {
209         const executingTask = new ExecutingTask();
210         executingTask.name = finishedTask.name;
211         executingTask.metadata = finishedTask.metadata;
212         this.summaryService.addRunningTask(executingTask);
213         this.ngOnChanges();
214         this.taskManagerService.subscribe(
215           finishedTask.name,
216           finishedTask.metadata,
217           (asyncFinishedTask: FinishedTask) => {
218             this.notificationService.notifyTask(asyncFinishedTask);
219           }
220         );
221       });
222   }
223
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
231     };
232     this.rbdService[task](this.poolName, this.rbdName, snapshotName)
233       .toPromise()
234       .then(() => {
235         const executingTask = new ExecutingTask();
236         executingTask.name = finishedTask.name;
237         executingTask.metadata = finishedTask.metadata;
238         this.summaryService.addRunningTask(executingTask);
239         this.modalRef.hide();
240         this.ngOnChanges();
241         this.taskManagerService.subscribe(
242           executingTask.name,
243           executingTask.metadata,
244           (asyncFinishedTask: FinishedTask) => {
245             this.notificationService.notifyTask(asyncFinishedTask);
246           }
247         );
248       })
249       .catch((resp) => {
250         this.modalRef.content.stopLoadingSpinner();
251       });
252   }
253
254   rollbackModal() {
255     const snapshotName = this.selection.selected[0].name;
256     const initialState = {
257       titleText: 'RBD snapshot rollback',
258       buttonText: 'Rollback',
259       bodyTpl: this.rollbackTpl,
260       bodyData: {
261         snapName: `${this.poolName}/${this.rbdName}@${snapshotName}`
262       },
263       onSubmit: () => {
264         this._asyncTask('rollbackSnapshot', 'rbd/snap/rollback', snapshotName);
265       }
266     };
267
268     this.modalRef = this.modalService.show(ConfirmationModalComponent, { initialState });
269   }
270
271   deleteSnapshotModal() {
272     const snapshotName = this.selection.selected[0].name;
273     this.modalRef = this.modalService.show(DeletionModalComponent, {
274       initialState: {
275         itemDescription: 'RBD snapshot',
276         submitAction: () => this._asyncTask('deleteSnapshot', 'rbd/snap/delete', snapshotName)
277       }
278     });
279   }
280
281   updateSelection(selection: CdTableSelection) {
282     this.selection = selection;
283   }
284 }