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';
36 selector: 'cd-cephfs-snapshotschedule-list',
37 templateUrl: './cephfs-snapshotschedule-list.component.html',
38 styleUrls: ['./cephfs-snapshotschedule-list.component.scss']
40 export class CephfsSnapshotscheduleListComponent
42 implements OnInit, OnChanges, OnDestroy {
43 @Input() fsName!: string;
46 @ViewChild('pathTpl', { static: true })
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 = '';
65 tableActions: CdTableAction[] = [
67 name: this.actionLabels.CREATE,
70 click: () => this.openModal(false)
73 name: this.actionLabels.EDIT,
76 click: () => this.openModal(true)
79 name: this.actionLabels.DELETE,
82 click: () => this.deleteSnapshotSchedule()
86 MODULE_NAME = 'snap_schedule';
87 ENABLE_MODULE_TIMER = 2 * 1000;
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
99 this.permissions = this.authStorageService.getPermissions();
102 ngOnChanges(changes: SimpleChanges): void {
103 if (changes.fsName) {
104 this.subject$.next([]);
109 this.moduleServiceListSub = this.mgrModuleService
112 map((modules: any[]) => modules.find((module) => module?.['name'] === this.MODULE_NAME))
115 next: (module: any) => this.snapScheduleModuleStatus$.next(module?.enabled)
118 this.snapshotSchedules$ = this.subject$.pipe(
120 this.snapScheduleModuleStatus$.pipe(
121 switchMap((status) => {
125 return this.snapshotScheduleService.getSnapshotScheduleList('/', this.fsName);
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 }
143 this.tableActions$.next(this.tableActions);
146 ngOnDestroy(): void {
147 this.moduleServiceListSub.unsubscribe();
151 this.subject$.next([]);
154 updateSelection(selection: CdTableSelection) {
155 this.selection = selection;
156 if (!this.selection.hasSelection) return;
157 const isActive = this.selection.first()?.active;
159 this.tableActions$.next([
160 ...this.tableActions,
162 name: isActive ? this.actionLabels.DEACTIVATE : this.actionLabels.ACTIVATE,
163 permission: 'update',
164 icon: isActive ? Icons.warning : Icons.success,
166 isActive ? this.deactivateSnapshotSchedule() : this.activateSnapshotSchedule()
171 openModal(edit = false) {
172 this.modalService.show(
173 CephfsSnapshotscheduleFormComponent,
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,
188 enableSnapshotSchedule() {
190 const fnWaitUntilReconnected = () => {
191 timer(this.ENABLE_MODULE_TIMER).subscribe(() => {
192 // Trigger an API request to check if the connection is
194 this.mgrModuleService.list().subscribe(
196 // Resume showing the notification toasties.
197 this.notificationService.suspendToasties(false);
198 // Unblock the whole UI.
200 // Reload the data table content.
201 this.notificationService.show(
202 NotificationType.success,
203 $localize`Enabled Snapshot Schedule Module`
205 // Reload the data table content.
208 fnWaitUntilReconnected();
214 if (!this.snapScheduleModuleStatus$.value) {
215 $obs = this.mgrModuleService
216 .enable(this.MODULE_NAME)
217 .pipe(finalize(() => this.snapScheduleModuleStatus$.next(true)));
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();
232 deactivateSnapshotSchedule() {
233 const { path, start, fs, schedule } = this.selection.first();
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', {
243 call: this.snapshotScheduleService.deactivate({
253 activateSnapshotSchedule() {
254 const { path, start, fs, schedule } = this.selection.first();
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', {
264 call: this.snapshotScheduleService.activate({
274 deleteSnapshotSchedule() {
275 const { path, start, fs, schedule } = this.selection.first();
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, {
284 call: this.snapshotScheduleService.delete({