]> git.apps.os.sepia.ceph.com Git - ceph.git/blob
09bfdaea5f9a78888244f1539f9d273098ae4f32
[ceph.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 { CdTableColumn } from '../../../shared/models/cd-table-column';
13 import { CdTableSelection } from '../../../shared/models/cd-table-selection';
14 import { ExecutingTask } from '../../../shared/models/executing-task';
15 import { FinishedTask } from '../../../shared/models/finished-task';
16 import { Permission } from '../../../shared/models/permissions';
17 import { CdDatePipe } from '../../../shared/pipes/cd-date.pipe';
18 import { DimlessBinaryPipe } from '../../../shared/pipes/dimless-binary.pipe';
19 import { AuthStorageService } from '../../../shared/services/auth-storage.service';
20 import { NotificationService } from '../../../shared/services/notification.service';
21 import { SummaryService } from '../../../shared/services/summary.service';
22 import { TaskListService } from '../../../shared/services/task-list.service';
23 import { TaskManagerService } from '../../../shared/services/task-manager.service';
24 import { RbdSnapshotFormComponent } from '../rbd-snapshot-form/rbd-snapshot-form.component';
25 import { RbdSnapshotModel } from './rbd-snapshot.model';
26
27 @Component({
28   selector: 'cd-rbd-snapshot-list',
29   templateUrl: './rbd-snapshot-list.component.html',
30   styleUrls: ['./rbd-snapshot-list.component.scss'],
31   providers: [TaskListService]
32 })
33 export class RbdSnapshotListComponent implements OnInit, OnChanges {
34   @Input()
35   snapshots: RbdSnapshotModel[] = [];
36   @Input()
37   poolName: string;
38   @Input()
39   rbdName: string;
40   @ViewChild('nameTpl')
41   nameTpl: TemplateRef<any>;
42   @ViewChild('protectTpl')
43   protectTpl: TemplateRef<any>;
44   @ViewChild('rollbackTpl')
45   rollbackTpl: TemplateRef<any>;
46
47   permission: Permission;
48
49   data: RbdSnapshotModel[];
50
51   columns: CdTableColumn[];
52
53   modalRef: BsModalRef;
54
55   selection = new CdTableSelection();
56
57   builders = {
58     'rbd/snap/create': (metadata) => {
59       const model = new RbdSnapshotModel();
60       model.name = metadata['snapshot_name'];
61       return model;
62     }
63   };
64
65   constructor(
66     private authStorageService: AuthStorageService,
67     private modalService: BsModalService,
68     private dimlessBinaryPipe: DimlessBinaryPipe,
69     private cdDatePipe: CdDatePipe,
70     private rbdService: RbdService,
71     private taskManagerService: TaskManagerService,
72     private notificationService: NotificationService,
73     private summaryService: SummaryService,
74     private taskListService: TaskListService
75   ) {
76     this.permission = this.authStorageService.getPermissions().rbdImage;
77   }
78
79   ngOnInit() {
80     this.columns = [
81       {
82         name: 'Name',
83         prop: 'name',
84         cellTransformation: CellTemplate.executing,
85         flexGrow: 2
86       },
87       {
88         name: 'Size',
89         prop: 'size',
90         flexGrow: 1,
91         cellClass: 'text-right',
92         pipe: this.dimlessBinaryPipe
93       },
94       {
95         name: 'Provisioned',
96         prop: 'disk_usage',
97         flexGrow: 1,
98         cellClass: 'text-right',
99         pipe: this.dimlessBinaryPipe
100       },
101       {
102         name: 'State',
103         prop: 'is_protected',
104         flexGrow: 1,
105         cellClass: 'text-center',
106         cellTemplate: this.protectTpl
107       },
108       {
109         name: 'Created',
110         prop: 'timestamp',
111         flexGrow: 1,
112         pipe: this.cdDatePipe
113       }
114     ];
115   }
116
117   ngOnChanges() {
118     const itemFilter = (entry, task) => {
119       return entry.name === task.metadata['snapshot_name'];
120     };
121
122     const taskFilter = (task) => {
123       return (
124         ['rbd/snap/create', 'rbd/snap/delete', 'rbd/snap/edit', 'rbd/snap/rollback'].includes(
125           task.name
126         ) &&
127         this.poolName === task.metadata['pool_name'] &&
128         this.rbdName === task.metadata['image_name']
129       );
130     };
131
132     this.taskListService.init(
133       () => of(this.snapshots),
134       null,
135       (items) => (this.data = items),
136       () => (this.data = this.snapshots),
137       taskFilter,
138       itemFilter,
139       this.builders
140     );
141   }
142
143   private openSnapshotModal(taskName: string, snapName: string = null) {
144     this.modalRef = this.modalService.show(RbdSnapshotFormComponent);
145     this.modalRef.content.poolName = this.poolName;
146     this.modalRef.content.imageName = this.rbdName;
147     if (snapName) {
148       this.modalRef.content.setEditing();
149     } else {
150       // Auto-create a name for the snapshot: <image_name>_<timestamp_ISO_8601>
151       // https://en.wikipedia.org/wiki/ISO_8601
152       snapName = `${this.rbdName}-${moment()
153         .utc()
154         .format('YYYYMMDD[T]HHmmss[Z]')}`;
155     }
156     this.modalRef.content.setSnapName(snapName);
157     this.modalRef.content.onSubmit.subscribe((snapshotName: string) => {
158       const executingTask = new ExecutingTask();
159       executingTask.name = taskName;
160       executingTask.metadata = {
161         image_name: this.rbdName,
162         pool_name: this.poolName,
163         snapshot_name: snapshotName
164       };
165       this.summaryService.addRunningTask(executingTask);
166       this.ngOnChanges();
167     });
168   }
169
170   openCreateSnapshotModal() {
171     this.openSnapshotModal('rbd/snap/create');
172   }
173
174   openEditSnapshotModal() {
175     this.openSnapshotModal('rbd/snap/edit', this.selection.first().name);
176   }
177
178   toggleProtection() {
179     const snapshotName = this.selection.first().name;
180     const isProtected = this.selection.first().is_protected;
181     const finishedTask = new FinishedTask();
182     finishedTask.name = 'rbd/snap/edit';
183     finishedTask.metadata = {
184       pool_name: this.poolName,
185       image_name: this.rbdName,
186       snapshot_name: snapshotName
187     };
188     this.rbdService
189       .protectSnapshot(this.poolName, this.rbdName, snapshotName, !isProtected)
190       .toPromise()
191       .then((resp) => {
192         const executingTask = new ExecutingTask();
193         executingTask.name = finishedTask.name;
194         executingTask.metadata = finishedTask.metadata;
195         this.summaryService.addRunningTask(executingTask);
196         this.ngOnChanges();
197         this.taskManagerService.subscribe(
198           finishedTask.name,
199           finishedTask.metadata,
200           (asyncFinishedTask: FinishedTask) => {
201             this.notificationService.notifyTask(asyncFinishedTask);
202           }
203         );
204       });
205   }
206
207   _asyncTask(task: string, taskName: string, snapshotName: string) {
208     const finishedTask = new FinishedTask();
209     finishedTask.name = taskName;
210     finishedTask.metadata = {
211       pool_name: this.poolName,
212       image_name: this.rbdName,
213       snapshot_name: snapshotName
214     };
215     this.rbdService[task](this.poolName, this.rbdName, snapshotName)
216       .toPromise()
217       .then(() => {
218         const executingTask = new ExecutingTask();
219         executingTask.name = finishedTask.name;
220         executingTask.metadata = finishedTask.metadata;
221         this.summaryService.addRunningTask(executingTask);
222         this.modalRef.hide();
223         this.ngOnChanges();
224         this.taskManagerService.subscribe(
225           executingTask.name,
226           executingTask.metadata,
227           (asyncFinishedTask: FinishedTask) => {
228             this.notificationService.notifyTask(asyncFinishedTask);
229           }
230         );
231       })
232       .catch((resp) => {
233         this.modalRef.content.stopLoadingSpinner();
234       });
235   }
236
237   rollbackModal() {
238     const snapshotName = this.selection.selected[0].name;
239     const initialState = {
240       titleText: 'RBD snapshot rollback',
241       buttonText: 'Rollback',
242       bodyTpl: this.rollbackTpl,
243       bodyData: {
244         snapName: `${this.poolName}/${this.rbdName}@${snapshotName}`
245       },
246       onSubmit: () => {
247         this._asyncTask('rollbackSnapshot', 'rbd/snap/rollback', snapshotName);
248       }
249     };
250
251     this.modalRef = this.modalService.show(ConfirmationModalComponent, { initialState });
252   }
253
254   deleteSnapshotModal() {
255     const snapshotName = this.selection.selected[0].name;
256     this.modalRef = this.modalService.show(DeletionModalComponent);
257     this.modalRef.content.setUp({
258       metaType: 'RBD snapshot',
259       pattern: snapshotName,
260       deletionMethod: () => this._asyncTask('deleteSnapshot', 'rbd/snap/delete', snapshotName),
261       modalRef: this.modalRef
262     });
263   }
264
265   updateSelection(selection: CdTableSelection) {
266     this.selection = selection;
267   }
268 }