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