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