]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/blob
58d3a0cc056afbc4a40210baf8819c3d7689354a
[ceph-ci.git] /
1 import {
2   Component,
3   Input,
4   OnChanges,
5   OnDestroy,
6   OnInit,
7   SimpleChanges,
8   ViewChild
9 } from '@angular/core';
10 import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
11 import { BehaviorSubject, Observable, Subscription, of, timer } from 'rxjs';
12 import { finalize, map, shareReplay, switchMap } from 'rxjs/operators';
13 import { CephfsSnapshotScheduleService } from '~/app/shared/api/cephfs-snapshot-schedule.service';
14 import { CdForm } from '~/app/shared/forms/cd-form';
15 import { CdTableAction } from '~/app/shared/models/cd-table-action';
16 import { CdTableColumn } from '~/app/shared/models/cd-table-column';
17 import { CdTableFetchDataContext } from '~/app/shared/models/cd-table-fetch-data-context';
18 import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
19 import { Permissions } from '~/app/shared/models/permissions';
20 import { SnapshotSchedule } from '~/app/shared/models/snapshot-schedule';
21 import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
22 import { ModalService } from '~/app/shared/services/modal.service';
23 import { Icons } from '~/app/shared/enum/icons.enum';
24 import { CellTemplate } from '~/app/shared/enum/cell-template.enum';
25 import { MgrModuleService } from '~/app/shared/api/mgr-module.service';
26 import { NotificationService } from '~/app/shared/services/notification.service';
27 import { BlockUI, NgBlockUI } from 'ng-block-ui';
28 import { NotificationType } from '~/app/shared/enum/notification-type.enum';
29 import { ActionLabelsI18n, URLVerbs } from '~/app/shared/constants/app.constants';
30 import { CephfsSnapshotscheduleFormComponent } from '../cephfs-snapshotschedule-form/cephfs-snapshotschedule-form.component';
31 import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
32 import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
33 import { FinishedTask } from '~/app/shared/models/finished-task';
34
35 @Component({
36   selector: 'cd-cephfs-snapshotschedule-list',
37   templateUrl: './cephfs-snapshotschedule-list.component.html',
38   styleUrls: ['./cephfs-snapshotschedule-list.component.scss']
39 })
40 export class CephfsSnapshotscheduleListComponent
41   extends CdForm
42   implements OnInit, OnChanges, OnDestroy {
43   @Input() fsName!: string;
44   @Input() id!: number;
45
46   @ViewChild('pathTpl', { static: true })
47   pathTpl: any;
48
49   @BlockUI()
50   blockUI: NgBlockUI;
51
52   snapshotSchedules$!: Observable<SnapshotSchedule[]>;
53   subject$ = new BehaviorSubject<SnapshotSchedule[]>([]);
54   snapScheduleModuleStatus$ = new BehaviorSubject<boolean>(false);
55   moduleServiceListSub!: Subscription;
56   columns: CdTableColumn[] = [];
57   tableActions$ = new BehaviorSubject<CdTableAction[]>([]);
58   context!: CdTableFetchDataContext;
59   selection = new CdTableSelection();
60   permissions!: Permissions;
61   modalRef!: NgbModalRef;
62   errorMessage: string = '';
63   selectedName: string = '';
64   icons = Icons;
65   tableActions: CdTableAction[] = [
66     {
67       name: this.actionLabels.CREATE,
68       permission: 'create',
69       icon: Icons.add,
70       click: () => this.openModal(false)
71     },
72     {
73       name: this.actionLabels.EDIT,
74       permission: 'update',
75       icon: Icons.edit,
76       click: () => this.openModal(true)
77     },
78     {
79       name: this.actionLabels.DELETE,
80       permission: 'delete',
81       icon: Icons.trash,
82       click: () => this.deleteSnapshotSchedule()
83     }
84   ];
85
86   MODULE_NAME = 'snap_schedule';
87   ENABLE_MODULE_TIMER = 2 * 1000;
88
89   constructor(
90     private snapshotScheduleService: CephfsSnapshotScheduleService,
91     private authStorageService: AuthStorageService,
92     private modalService: ModalService,
93     private mgrModuleService: MgrModuleService,
94     private notificationService: NotificationService,
95     private actionLabels: ActionLabelsI18n,
96     private taskWrapper: TaskWrapperService
97   ) {
98     super();
99     this.permissions = this.authStorageService.getPermissions();
100   }
101
102   ngOnChanges(changes: SimpleChanges): void {
103     if (changes.fsName) {
104       this.subject$.next([]);
105     }
106   }
107
108   ngOnInit(): void {
109     this.moduleServiceListSub = this.mgrModuleService
110       .list()
111       .pipe(
112         map((modules: any[]) => modules.find((module) => module?.['name'] === this.MODULE_NAME))
113       )
114       .subscribe({
115         next: (module: any) => this.snapScheduleModuleStatus$.next(module?.enabled)
116       });
117
118     this.snapshotSchedules$ = this.subject$.pipe(
119       switchMap(() =>
120         this.snapScheduleModuleStatus$.pipe(
121           switchMap((status) => {
122             if (!status) {
123               return of([]);
124             }
125             return this.snapshotScheduleService.getSnapshotScheduleList('/', this.fsName);
126           }),
127           shareReplay(1)
128         )
129       )
130     );
131
132     this.columns = [
133       { prop: 'path', name: $localize`Path`, flexGrow: 3, cellTemplate: this.pathTpl },
134       { prop: 'subvol', name: $localize`Subvolume` },
135       { prop: 'schedule', name: $localize`Repeat interval` },
136       { prop: 'retention', name: $localize`Retention policy` },
137       { prop: 'created_count', name: $localize`Created Count` },
138       { prop: 'pruned_count', name: $localize`Deleted Count` },
139       { prop: 'start', name: $localize`Start time`, cellTransformation: CellTemplate.timeAgo },
140       { prop: 'created', name: $localize`Created`, cellTransformation: CellTemplate.timeAgo }
141     ];
142
143     this.tableActions$.next(this.tableActions);
144   }
145
146   ngOnDestroy(): void {
147     this.moduleServiceListSub.unsubscribe();
148   }
149
150   fetchData() {
151     this.subject$.next([]);
152   }
153
154   updateSelection(selection: CdTableSelection) {
155     this.selection = selection;
156     if (!this.selection.hasSelection) return;
157     const isActive = this.selection.first()?.active;
158
159     this.tableActions$.next([
160       ...this.tableActions,
161       {
162         name: isActive ? this.actionLabels.DEACTIVATE : this.actionLabels.ACTIVATE,
163         permission: 'update',
164         icon: isActive ? Icons.warning : Icons.success,
165         click: () =>
166           isActive ? this.deactivateSnapshotSchedule() : this.activateSnapshotSchedule()
167       }
168     ]);
169   }
170
171   openModal(edit = false) {
172     this.modalService.show(
173       CephfsSnapshotscheduleFormComponent,
174       {
175         fsName: this.fsName,
176         id: this.id,
177         path: this.selection?.first()?.path,
178         schedule: this.selection?.first()?.schedule,
179         retention: this.selection?.first()?.retention,
180         start: this.selection?.first()?.start,
181         status: this.selection?.first()?.status,
182         isEdit: edit
183       },
184       { size: 'lg' }
185     );
186   }
187
188   enableSnapshotSchedule() {
189     let $obs;
190     const fnWaitUntilReconnected = () => {
191       timer(this.ENABLE_MODULE_TIMER).subscribe(() => {
192         // Trigger an API request to check if the connection is
193         // re-established.
194         this.mgrModuleService.list().subscribe(
195           () => {
196             // Resume showing the notification toasties.
197             this.notificationService.suspendToasties(false);
198             // Unblock the whole UI.
199             this.blockUI.stop();
200             // Reload the data table content.
201             this.notificationService.show(
202               NotificationType.success,
203               $localize`Enabled Snapshot Schedule Module`
204             );
205             // Reload the data table content.
206           },
207           () => {
208             fnWaitUntilReconnected();
209           }
210         );
211       });
212     };
213
214     if (!this.snapScheduleModuleStatus$.value) {
215       $obs = this.mgrModuleService
216         .enable(this.MODULE_NAME)
217         .pipe(finalize(() => this.snapScheduleModuleStatus$.next(true)));
218     }
219     $obs.subscribe(
220       () => undefined,
221       () => {
222         // Suspend showing the notification toasties.
223         this.notificationService.suspendToasties(true);
224         // Block the whole UI to prevent user interactions until
225         // the connection to the backend is reestablished
226         this.blockUI.start($localize`Reconnecting, please wait ...`);
227         fnWaitUntilReconnected();
228       }
229     );
230   }
231
232   deactivateSnapshotSchedule() {
233     const { path, start, fs, schedule } = this.selection.first();
234
235     this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
236       itemDescription: $localize`snapshot schedule`,
237       actionDescription: this.actionLabels.DEACTIVATE,
238       submitActionObservable: () =>
239         this.taskWrapper.wrapTaskAroundCall({
240           task: new FinishedTask('cephfs/snapshot/schedule/deactivate', {
241             path
242           }),
243           call: this.snapshotScheduleService.deactivate({
244             path,
245             schedule,
246             start,
247             fs
248           })
249         })
250     });
251   }
252
253   activateSnapshotSchedule() {
254     const { path, start, fs, schedule } = this.selection.first();
255
256     this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
257       itemDescription: $localize`snapshot schedule`,
258       actionDescription: this.actionLabels.ACTIVATE,
259       submitActionObservable: () =>
260         this.taskWrapper.wrapTaskAroundCall({
261           task: new FinishedTask('cephfs/snapshot/schedule/activate', {
262             path
263           }),
264           call: this.snapshotScheduleService.activate({
265             path,
266             schedule,
267             start,
268             fs
269           })
270         })
271     });
272   }
273
274   deleteSnapshotSchedule() {
275     const { path, start, fs, schedule } = this.selection.first();
276
277     this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
278       itemDescription: $localize`snapshot schedule`,
279       submitActionObservable: () =>
280         this.taskWrapper.wrapTaskAroundCall({
281           task: new FinishedTask('cephfs/snapshot/schedule/' + URLVerbs.DELETE, {
282             path
283           }),
284           call: this.snapshotScheduleService.delete({
285             path,
286             schedule,
287             start,
288             fs
289           })
290         })
291     });
292   }
293 }