]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: snap schedule module detect/eneable
authorIvo Almeida <ialmeida@redhat.com>
Sun, 10 Dec 2023 17:44:33 +0000 (17:44 +0000)
committerIvo Almeida <ialmeida@redhat.com>
Wed, 14 Feb 2024 13:38:56 +0000 (13:38 +0000)
Fixes: https://tracker.ceph.com/issues/63768
Signed-off-by: Ivo Almeida <ialmeida@redhat.com>
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-snapshotschedule-list/cephfs-snapshotschedule-list.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-snapshotschedule-list/cephfs-snapshotschedule-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.html

index 2e270057d579f462dfc3cb2337847c9f49f03350..0346de67f9419911779115695ddde0bd7ae2e651 100644 (file)
@@ -1,44 +1,66 @@
-<ng-container *ngIf="isLoading$ | async">
-  <cd-loading-panel>
-    <span i18n>Loading snapshot schedules...</span>
-  </cd-loading-panel>
-</ng-container>
+<cd-alert-panel
+  *ngIf="(snapScheduleModuleStatus$ | async) === false"
+  type="info"
+  spacingClass="mb-3"
+  i18n
+  class="align-items-center"
+>
+  In order to access the snapshot scheduler feature, the snap_scheduler module must be enabled
+  <button
+    class="btn btn-light mx-2"
+    type="button"
+    (click)="enableSnapshotSchedule()">
+    Enable
+  </button>
+</cd-alert-panel>
 
-<ng-template #pathTpl
-             let-row="row">
+<ng-template
+  #pathTpl
+  let-row="row">
   <span
     class="fw-bold"
     [ngbTooltip]="fullpathTpl"
-    triggers="click:blur">{{row.path | path}}</span>
+    triggers="click:blur">
+    {{ row.path | path }}
+  </span>
 
-  <span *ngIf="row.active; else inactiveStatusTpl">
-    <i [ngClass]="[icons.success, icons.large]"
-       ngbTooltip="{{row.path}} is active"
-       class="text-success"></i>
+  <span
+  *ngIf="row.active; else inactiveStatusTpl">
+    <i
+      [ngClass]="[icons.success, icons.large]"
+      ngbTooltip="{{ row.path }} is active"
+      class="text-success"
+    ></i>
   </span>
 
   <ng-template #inactiveStatusTpl>
-    <i [ngClass]="[icons.warning, icons.large]"
-       class="text-warning"
-       ngbTooltip="{{row.path}} has been deactivated"></i>
+    <i
+      [ngClass]="[icons.warning, icons.large]"
+      class="text-warning"
+      ngbTooltip="{{ row.path }} has been deactivated"
+    ></i>
   </ng-template>
 
   <ng-template #fullpathTpl>
-  <span data-toggle="tooltip"
-        [title]="row.path"
-        class="font-monospace">{{ row.path }}
-    <cd-copy-2-clipboard-button *ngIf="row.path"
-                                [source]="row.path"
-                                [byId]="false"
-                                [showIconOnly]="true">
-    </cd-copy-2-clipboard-button>
-  </span>
-</ng-template>
-
+    <span
+      data-toggle="tooltip"
+      [title]="row.path"
+      class="font-monospace"
+      >{{ row.path }}
+      <cd-copy-2-clipboard-button
+        *ngIf="row.path"
+        [source]="row.path"
+        [byId]="false"
+        [showIconOnly]="true"
+      >
+      </cd-copy-2-clipboard-button>
+    </span>
+  </ng-template>
 </ng-template>
 
 <cd-table
   [data]="snapshotSchedules$ | async"
+  *ngIf="snapScheduleModuleStatus$ | async"
   columnMode="flex"
   [columns]="columns"
   selectionType="single"
index d5f24e897284928b93c8cfd6b4de4aec04bf6411..14752b7e2a8f39eb094a822184393b5f943293c3 100644 (file)
@@ -1,7 +1,15 @@
-import { Component, Input, OnChanges, OnInit, SimpleChanges, ViewChild } from '@angular/core';
+import {
+  Component,
+  Input,
+  OnChanges,
+  OnDestroy,
+  OnInit,
+  SimpleChanges,
+  ViewChild
+} from '@angular/core';
 import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
-import { BehaviorSubject, Observable } from 'rxjs';
-import { finalize, shareReplay, switchMap } from 'rxjs/operators';
+import { BehaviorSubject, Observable, Subscription, of, timer } from 'rxjs';
+import { finalize, map, shareReplay, switchMap } from 'rxjs/operators';
 import { CephfsSnapshotScheduleService } from '~/app/shared/api/cephfs-snapshot-schedule.service';
 import { CdForm } from '~/app/shared/forms/cd-form';
 import { CdTableAction } from '~/app/shared/models/cd-table-action';
@@ -14,21 +22,31 @@ import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
 import { ModalService } from '~/app/shared/services/modal.service';
 import { Icons } from '~/app/shared/enum/icons.enum';
 import { CellTemplate } from '~/app/shared/enum/cell-template.enum';
+import { MgrModuleService } from '~/app/shared/api/mgr-module.service';
+import { NotificationService } from '~/app/shared/services/notification.service';
+import { BlockUI, NgBlockUI } from 'ng-block-ui';
+import { NotificationType } from '~/app/shared/enum/notification-type.enum';
 
 @Component({
   selector: 'cd-cephfs-snapshotschedule-list',
   templateUrl: './cephfs-snapshotschedule-list.component.html',
   styleUrls: ['./cephfs-snapshotschedule-list.component.scss']
 })
-export class CephfsSnapshotscheduleListComponent extends CdForm implements OnInit, OnChanges {
+export class CephfsSnapshotscheduleListComponent
+  extends CdForm
+  implements OnInit, OnChanges, OnDestroy {
   @Input() fsName!: string;
 
   @ViewChild('pathTpl', { static: true })
   pathTpl: any;
 
+  @BlockUI()
+  blockUI: NgBlockUI;
+
   snapshotSchedules$!: Observable<SnapshotSchedule[]>;
   subject$ = new BehaviorSubject<SnapshotSchedule[]>([]);
-  isLoading$ = new BehaviorSubject<boolean>(true);
+  snapScheduleModuleStatus$ = new BehaviorSubject<boolean>(false);
+  moduleServiceListSub!: Subscription;
   columns: CdTableColumn[] = [];
   tableActions: CdTableAction[] = [];
   context!: CdTableFetchDataContext;
@@ -39,10 +57,15 @@ export class CephfsSnapshotscheduleListComponent extends CdForm implements OnIni
   selectedName: string = '';
   icons = Icons;
 
+  MODULE_NAME = 'snap_schedule';
+  ENABLE_MODULE_TIMER = 2 * 1000;
+
   constructor(
     private snapshotScheduleService: CephfsSnapshotScheduleService,
     private authStorageService: AuthStorageService,
-    private modalService: ModalService
+    private modalService: ModalService,
+    private mgrModuleService: MgrModuleService,
+    private notificationService: NotificationService
   ) {
     super();
     this.permissions = this.authStorageService.getPermissions();
@@ -55,13 +78,27 @@ export class CephfsSnapshotscheduleListComponent extends CdForm implements OnIni
   }
 
   ngOnInit(): void {
+    this.moduleServiceListSub = this.mgrModuleService
+      .list()
+      .pipe(
+        map((modules: any[]) => modules.find((module) => module?.['name'] === this.MODULE_NAME))
+      )
+      .subscribe({
+        next: (module: any) => this.snapScheduleModuleStatus$.next(module?.enabled)
+      });
+
     this.snapshotSchedules$ = this.subject$.pipe(
       switchMap(() =>
-        this.snapshotScheduleService
-          .getSnapshotScheduleList('/', this.fsName)
-          .pipe(finalize(() => this.isLoading$.next(false)))
-      ),
-      shareReplay(1)
+        this.snapScheduleModuleStatus$.pipe(
+          switchMap((status) => {
+            if (!status) {
+              return of([]);
+            }
+            return this.snapshotScheduleService.getSnapshotScheduleList('/', this.fsName);
+          }),
+          shareReplay(1)
+        )
+      )
     );
 
     this.columns = [
@@ -78,6 +115,10 @@ export class CephfsSnapshotscheduleListComponent extends CdForm implements OnIni
     this.tableActions = [];
   }
 
+  ngOnDestroy(): void {
+    this.moduleServiceListSub.unsubscribe();
+  }
+
   fetchData() {
     this.subject$.next([]);
   }
@@ -96,4 +137,48 @@ export class CephfsSnapshotscheduleListComponent extends CdForm implements OnIni
       { size: 'lg' }
     );
   }
+
+  enableSnapshotSchedule() {
+    let $obs;
+    const fnWaitUntilReconnected = () => {
+      timer(this.ENABLE_MODULE_TIMER).subscribe(() => {
+        // Trigger an API request to check if the connection is
+        // re-established.
+        this.mgrModuleService.list().subscribe(
+          () => {
+            // Resume showing the notification toasties.
+            this.notificationService.suspendToasties(false);
+            // Unblock the whole UI.
+            this.blockUI.stop();
+            // Reload the data table content.
+            this.notificationService.show(
+              NotificationType.success,
+              $localize`Enabled Snapshot Schedule Module`
+            );
+            // Reload the data table content.
+          },
+          () => {
+            fnWaitUntilReconnected();
+          }
+        );
+      });
+    };
+
+    if (!this.snapScheduleModuleStatus$.value) {
+      $obs = this.mgrModuleService
+        .enable(this.MODULE_NAME)
+        .pipe(finalize(() => this.snapScheduleModuleStatus$.next(true)));
+    }
+    $obs.subscribe(
+      () => undefined,
+      () => {
+        // Suspend showing the notification toasties.
+        this.notificationService.suspendToasties(true);
+        // Block the whole UI to prevent user interactions until
+        // the connection to the backend is reestablished
+        this.blockUI.start($localize`Reconnecting, please wait ...`);
+        fnWaitUntilReconnected();
+      }
+    );
+  }
 }
index 5274cf73a5569481324148773c557c2fce07b986..291013a5ce2f13badd72f46f2b88635bfc7c84b1 100644 (file)
@@ -4,10 +4,12 @@
       <cd-alert-panel   *ngIf="!rgwModuleStatus"
                         type="info"
                         spacingClass="mb-3"
+                        class="d-flex align-items-center"
                         i18n>In order to access the import/export feature, the rgw module must be enabled
-        <a class="text-decoration-underline"
-           (click)="enableRgwModule()">
-           Enable the Object Gateway Module</a>
+
+        <button class="btn btn-light mx-2"
+                type="button"
+                (click)="enableRgwModule()">Enable</button>
       </cd-alert-panel>
       <cd-alert-panel   *ngIf="restartGatewayMessage"
                         type="warning"