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 { Icons } from '~/app/shared/enum/icons.enum';
23 import { CellTemplate } from '~/app/shared/enum/cell-template.enum';
24 import { MgrModuleService } from '~/app/shared/api/mgr-module.service';
25 import { NotificationService } from '~/app/shared/services/notification.service';
26 import { BlockUI, NgBlockUI } from 'ng-block-ui';
27 import { NotificationType } from '~/app/shared/enum/notification-type.enum';
28 import { ActionLabelsI18n, URLVerbs } from '~/app/shared/constants/app.constants';
29 import { CephfsSnapshotscheduleFormComponent } from '../cephfs-snapshotschedule-form/cephfs-snapshotschedule-form.component';
30 import { DeleteConfirmationModalComponent } from '~/app/shared/components/delete-confirmation-modal/delete-confirmation-modal.component';
31 import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
32 import { FinishedTask } from '~/app/shared/models/finished-task';
33 import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
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 })
49 @ViewChild('retentionTpl', { static: true })
52 @ViewChild('subvolTpl', { static: true })
58 snapshotSchedules$!: Observable<SnapshotSchedule[]>;
59 subject$ = new BehaviorSubject<SnapshotSchedule[]>([]);
60 snapScheduleModuleStatus$ = new BehaviorSubject<boolean>(false);
61 moduleServiceListSub!: Subscription;
62 columns: CdTableColumn[] = [];
63 tableActions$ = new BehaviorSubject<CdTableAction[]>([]);
64 context!: CdTableFetchDataContext;
65 selection = new CdTableSelection();
66 permissions!: Permissions;
67 modalRef!: NgbModalRef;
68 errorMessage: string = '';
69 selectedName: string = '';
71 tableActions!: CdTableAction[];
73 MODULE_NAME = 'snap_schedule';
74 ENABLE_MODULE_TIMER = 2 * 1000;
77 private snapshotScheduleService: CephfsSnapshotScheduleService,
78 private authStorageService: AuthStorageService,
79 private modalService: ModalCdsService,
80 private mgrModuleService: MgrModuleService,
81 private notificationService: NotificationService,
82 private actionLabels: ActionLabelsI18n,
83 private taskWrapper: TaskWrapperService
86 this.permissions = this.authStorageService.getPermissions();
89 ngOnChanges(changes: SimpleChanges): void {
91 this.subject$.next([]);
98 name: this.actionLabels.CREATE,
101 click: () => this.openModal(false)
104 name: this.actionLabels.EDIT,
105 permission: 'update',
107 click: () => this.openModal(true)
110 name: this.actionLabels.DELETE,
111 permission: 'delete',
113 click: () => this.deleteSnapshotSchedule()
117 this.moduleServiceListSub = this.mgrModuleService
120 map((modules: any[]) => modules.find((module) => module?.['name'] === this.MODULE_NAME))
123 next: (module: any) => this.snapScheduleModuleStatus$.next(module?.enabled)
126 this.snapshotSchedules$ = this.subject$.pipe(
128 this.snapScheduleModuleStatus$.pipe(
129 switchMap((status) => {
133 return this.snapshotScheduleService
134 .getSnapshotScheduleList('/', this.fsName)
137 list.map((l) => ({ ...l, pathForSelection: `${l.path}@${l.schedule}` }))
147 { prop: 'pathForSelection', name: $localize`Path`, flexGrow: 3, cellTemplate: this.pathTpl },
148 { prop: 'path', isHidden: true, isInvisible: true },
149 { prop: 'subvol', name: $localize`Subvolume`, cellTemplate: this.subvolTpl },
150 { prop: 'scheduleCopy', name: $localize`Repeat interval` },
151 { prop: 'schedule', isHidden: true, isInvisible: true },
152 { prop: 'retentionCopy', name: $localize`Retention policy`, cellTemplate: this.retentionTpl },
153 { prop: 'retention', isHidden: true, isInvisible: true },
154 { prop: 'created_count', name: $localize`Created Count` },
155 { prop: 'pruned_count', name: $localize`Deleted Count` },
156 { prop: 'start', name: $localize`Start time`, cellTransformation: CellTemplate.timeAgo },
157 { prop: 'created', name: $localize`Created`, cellTransformation: CellTemplate.timeAgo }
160 this.tableActions$.next(this.tableActions);
163 ngOnDestroy(): void {
164 this.moduleServiceListSub.unsubscribe();
168 this.subject$.next([]);
171 updateSelection(selection: CdTableSelection) {
172 this.selection = selection;
173 if (!this.selection.hasSelection) return;
174 const isActive = this.selection.first()?.active;
176 this.tableActions$.next([
177 ...this.tableActions,
179 name: isActive ? this.actionLabels.DEACTIVATE : this.actionLabels.ACTIVATE,
180 permission: 'update',
181 icon: isActive ? Icons.warning : Icons.success,
183 isActive ? this.deactivateSnapshotSchedule() : this.activateSnapshotSchedule()
188 openModal(edit = false) {
189 this.modalService.show(CephfsSnapshotscheduleFormComponent, {
192 path: this.selection?.first()?.path,
193 schedule: this.selection?.first()?.schedule,
194 retention: this.selection?.first()?.retention,
195 start: this.selection?.first()?.start,
196 status: this.selection?.first()?.status,
201 enableSnapshotSchedule() {
203 const fnWaitUntilReconnected = () => {
204 timer(this.ENABLE_MODULE_TIMER).subscribe(() => {
205 // Trigger an API request to check if the connection is
207 this.mgrModuleService.list().subscribe(
209 // Resume showing the notification toasties.
210 this.notificationService.suspendToasties(false);
211 // Unblock the whole UI.
213 // Reload the data table content.
214 this.notificationService.show(
215 NotificationType.success,
216 $localize`Enabled Snapshot Schedule Module`
218 // Reload the data table content.
221 fnWaitUntilReconnected();
227 if (!this.snapScheduleModuleStatus$.value) {
228 $obs = this.mgrModuleService
229 .enable(this.MODULE_NAME)
230 .pipe(finalize(() => this.snapScheduleModuleStatus$.next(true)));
235 // Suspend showing the notification toasties.
236 this.notificationService.suspendToasties(true);
237 // Block the whole UI to prevent user interactions until
238 // the connection to the backend is reestablished
239 this.blockUI.start($localize`Reconnecting, please wait ...`);
240 fnWaitUntilReconnected();
245 deactivateSnapshotSchedule() {
246 const { path, start, fs, schedule, subvol, group } = this.selection.first();
248 this.modalRef = this.modalService.show(DeleteConfirmationModalComponent, {
249 itemDescription: $localize`snapshot schedule`,
250 actionDescription: this.actionLabels.DEACTIVATE,
251 submitActionObservable: () =>
252 this.taskWrapper.wrapTaskAroundCall({
253 task: new FinishedTask('cephfs/snapshot/schedule/deactivate', {
256 call: this.snapshotScheduleService.deactivate({
268 activateSnapshotSchedule() {
269 const { path, start, fs, schedule, subvol, group } = this.selection.first();
271 this.modalRef = this.modalService.show(DeleteConfirmationModalComponent, {
272 itemDescription: $localize`snapshot schedule`,
273 actionDescription: this.actionLabels.ACTIVATE,
274 submitActionObservable: () =>
275 this.taskWrapper.wrapTaskAroundCall({
276 task: new FinishedTask('cephfs/snapshot/schedule/activate', {
279 call: this.snapshotScheduleService.activate({
291 deleteSnapshotSchedule() {
292 const { path, start, fs, schedule, subvol, group, retention } = this.selection.first();
293 const retentionPolicy = retention
295 ?.filter((r: string) => !!r)
296 ?.map((r: string) => {
297 const frequency = r.substring(r.length - 1);
298 const interval = r.substring(0, r.length - 1);
299 return `${interval}-${frequency}`;
303 this.modalRef = this.modalService.show(DeleteConfirmationModalComponent, {
304 itemDescription: $localize`snapshot schedule`,
305 submitActionObservable: () =>
306 this.taskWrapper.wrapTaskAroundCall({
307 task: new FinishedTask('cephfs/snapshot/schedule/' + URLVerbs.DELETE, {
310 call: this.snapshotScheduleService.delete({