]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Cephfs Mirroring Wizard cephfs-mirroring-wizard
authorPedro Gonzalez Gomez <pegonzal@ibm.com>
Thu, 20 Nov 2025 14:09:03 +0000 (15:09 +0100)
committerDnyaneshwari <dnyaneshwari@li-9c9fbecc-2d5c-11b2-a85c-e2a7cc8a424f.ibm.com>
Mon, 22 Dec 2025 07:59:28 +0000 (13:29 +0530)
Fixes: https://tracker.ceph.com/issues/74200
Signed-off-by: Dnyaneshwari Talwekar <dtalweka@redhat.com>
src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/ceph.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs-mirroring/cephfs-mirroring-wizard/cephfs-mirroring-wizard-step.enum.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs-mirroring/cephfs-mirroring-wizard/cephfs-mirroring-wizard.component.html [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs-mirroring/cephfs-mirroring-wizard/cephfs-mirroring-wizard.component.scss [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs-mirroring/cephfs-mirroring-wizard/cephfs-mirroring-wizard.component.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs-mirroring/cephfs-mirroring-wizard/cephfs-mirroring-wizard.component.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mirroring-list/cephfs-mirroring-list.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-mirroring-list/cephfs-mirroring-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs.module.ts

index 70a9550f30cb645d3a0642a64d62a686b3f36b2c..b06fbabf938f50abf3954ae25268bca7ce20e43d 100644 (file)
@@ -59,6 +59,7 @@ import { SmbClusterListComponent } from './ceph/smb/smb-cluster-list/smb-cluster
 import { SmbJoinAuthListComponent } from './ceph/smb/smb-join-auth-list/smb-join-auth-list.component';
 import { SmbUsersgroupsListComponent } from './ceph/smb/smb-usersgroups-list/smb-usersgroups-list.component';
 import { SmbOverviewComponent } from './ceph/smb/smb-overview/smb-overview.component';
+import { CephfsMirroringWizardComponent } from './ceph/cephfs-mirroring/cephfs-mirroring-wizard/cephfs-mirroring-wizard.component';
 import { CephfsMirroringListComponent } from './ceph/cephfs/cephfs-mirroring-list/cephfs-mirroring-list.component';
 
 @Injectable()
@@ -412,6 +413,11 @@ const routes: Routes = [
             component: CephfsMirroringListComponent,
             data: { breadcrumbs: 'File/Mirroring' }
           },
+          {
+            path: `mirroring/${URLVerbs.CREATE}`,
+            component: CephfsMirroringWizardComponent,
+            data: { breadcrumbs: ActionLabels.CREATE }
+          },
           {
             path: 'nfs',
             canActivateChild: [FeatureTogglesGuardService, ModuleStatusGuardService],
index d269b6aa912eee3f2404b2f70a9324008cbb7479..700211e68c45c07f8989094980be96bace396e9c 100644 (file)
@@ -8,6 +8,7 @@ import { DashboardModule } from './dashboard/dashboard.module';
 import { NfsModule } from './nfs/nfs.module';
 import { PerformanceCounterModule } from './performance-counter/performance-counter.module';
 import { SmbModule } from './smb/smb.module';
+import { TilesModule } from 'carbon-components-angular';
 
 @NgModule({
   imports: [
@@ -18,7 +19,8 @@ import { SmbModule } from './smb/smb.module';
     CephfsModule,
     NfsModule,
     SmbModule,
-    SharedModule
+    SharedModule,
+    TilesModule
   ],
   declarations: []
 })
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs-mirroring/cephfs-mirroring-wizard/cephfs-mirroring-wizard-step.enum.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs-mirroring/cephfs-mirroring-wizard/cephfs-mirroring-wizard-step.enum.ts
new file mode 100644 (file)
index 0000000..c213586
--- /dev/null
@@ -0,0 +1,12 @@
+export enum StepTitles {
+  ChooseClusterRole = 'Choose Cluster Role',
+  SelectFilesystem = 'Select Filesystem',
+  ImportBootstrapToken = 'Import Bootstrap Token',
+  Review = 'Review'
+}
+
+export const STEP_TITLES_MIRRORING_CONFIGURED = [
+  StepTitles.ChooseClusterRole,
+  StepTitles.SelectFilesystem,
+  StepTitles.ImportBootstrapToken
+];
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs-mirroring/cephfs-mirroring-wizard/cephfs-mirroring-wizard.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs-mirroring/cephfs-mirroring-wizard/cephfs-mirroring-wizard.component.html
new file mode 100644 (file)
index 0000000..28e988a
--- /dev/null
@@ -0,0 +1,69 @@
+<div>
+  <p>Configure a new mirroring relationship between clusters</p>
+</div>
+<div cdsRow>
+  <div cdsCol
+       [columnNumbers]="{ lg: 2, md: 3, sm: 4 }">
+    <cd-wizard [stepsTitle]="stepTitles"></cd-wizard>
+  </div>
+  <div cdsCol
+       [columnNumbers]="{ lg: 14, md: 13, sm: 12 }"
+       class="mirroring-main">
+    <div class="mirroring-bg">
+      <div class="mirroring-header">
+      <h5>Choose Cluster Role</h5>
+      <p>Select how the cluster will participate in the mirroring setup.</p>
+      </div>
+      <cds-radio-group name="clusterRole"
+                       value="source">
+        <div cdsRow>
+          <div cdsCol
+               [columnNumbers]="{ lg: 6, md: 6, sm: 12 }"
+               class="mirroring-tile-col">
+            <cds-tile class="mirroring-tile">
+              <cds-radio value="source">
+                <div>
+                  <h6>Configure as Local Cluster (Source)</h6>
+                  <div>
+                    This cluster will send snapshots and replicate data to remote clusters for
+                    backup and disaster recovery.
+                  </div>
+
+                  <ul class="mirroring-list">
+                    <li class="mirroring-list-item"
+                        *ngFor="let item of sourceList">
+                      &#8594; {{ item }}
+                    </li>
+                  </ul>
+                </div>
+              </cds-radio>
+            </cds-tile>
+          </div>
+          <div cdsCol
+               [columnNumbers]="{ lg: 6, md: 6, sm: 12 }"
+               class="mirroring-tile-col">
+            <cds-tile class="mirroring-tile">
+              <cds-radio value="target">
+                <div>
+                  <h6>Configure as Remote Cluster (Target)</h6>
+                  <div>
+                    This cluster will receive replication data and generate a token for connection
+                    with source clusters.
+                  </div>
+                  <ul class="mirroring-list">
+                    <li class="mirroring-list-item"
+                        *ngFor="let item of targetList">
+                      &#8594; {{ item }}
+                    </li>
+                  </ul>
+                </div>
+              </cds-radio>
+            </cds-tile>
+          </div>
+        </div>
+      </cds-radio-group>
+    </div>
+  </div>
+</div>
+
+
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs-mirroring/cephfs-mirroring-wizard/cephfs-mirroring-wizard.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs-mirroring/cephfs-mirroring-wizard/cephfs-mirroring-wizard.component.scss
new file mode 100644 (file)
index 0000000..211ff9f
--- /dev/null
@@ -0,0 +1,29 @@
+.mirroring-main {
+  padding-left: 32px;
+}
+
+.mirroring-bg {
+  background-color: var(--cds-ui-02);
+  min-height: 100vh;
+}
+
+.mirroring-header {
+  margin-left: 30px;
+  margin-top: 20px;
+}
+
+.mirroring-tile-col {
+  margin-left: 32px;
+}
+
+.mirroring-tile {
+  margin-bottom: 30px;
+}
+
+.mirroring-list-item {
+  margin-bottom: 16px;
+}
+
+.mirroring-list {
+  margin-top: 16px;
+}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs-mirroring/cephfs-mirroring-wizard/cephfs-mirroring-wizard.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs-mirroring/cephfs-mirroring-wizard/cephfs-mirroring-wizard.component.spec.ts
new file mode 100644 (file)
index 0000000..a277186
--- /dev/null
@@ -0,0 +1,35 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { SharedModule } from '~/app/shared/shared.module';
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+
+import { ToastrModule } from 'ngx-toastr';
+import { CephfsMirroringWizardComponent } from './cephfs-mirroring-wizard.component';
+
+describe('CephfsMirroringWizardComponent', () => {
+  let component: CephfsMirroringWizardComponent;
+  let fixture: ComponentFixture<CephfsMirroringWizardComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [
+        BrowserAnimationsModule,
+        SharedModule,
+        HttpClientTestingModule,
+        ToastrModule.forRoot(),
+        RouterTestingModule
+      ],
+      declarations: [CephfsMirroringWizardComponent]
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(CephfsMirroringWizardComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs-mirroring/cephfs-mirroring-wizard/cephfs-mirroring-wizard.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs-mirroring/cephfs-mirroring-wizard/cephfs-mirroring-wizard.component.ts
new file mode 100644 (file)
index 0000000..fe73aa7
--- /dev/null
@@ -0,0 +1,34 @@
+import { Component, OnInit } from '@angular/core';
+import { Step } from 'carbon-components-angular';
+import { STEP_TITLES_MIRRORING_CONFIGURED } from './cephfs-mirroring-wizard-step.enum';
+import { Icons } from '~/app/shared/enum/icons.enum';
+
+@Component({
+  selector: 'cd-cephfs-mirroring-wizard',
+  templateUrl: './cephfs-mirroring-wizard.component.html',
+  styleUrls: ['./cephfs-mirroring-wizard.component.scss']
+})
+export class CephfsMirroringWizardComponent implements OnInit {
+  stepTitles: Step[] = STEP_TITLES_MIRRORING_CONFIGURED.map((title) => ({
+    label: title
+  }));
+  selectedRole: string = 'source';
+  icons = Icons;
+  sourceList: string[] = [
+    'Sends data to remote clusters',
+    'Requires bootstrap token from target',
+    'Manages snapshot schedules'
+  ];
+
+  targetList: string[] = [
+    'Receives data from source clusters',
+    'Generates bootstrap token',
+    'Stores replicated snapshots'
+  ];
+
+  ngOnInit(): void {}
+
+  selectRole(role: string) {
+    this.selectedRole = role;
+  }
+}
index 848dc1dc57d7d8cd4a05cb2ca98eb6422b206b46..388b40d478451a3f12f17b3a155d8d9431cba02a 100644 (file)
@@ -1,12 +1,18 @@
 <ng-container *ngIf="daemonStatus$ | async as daemonStatus">
+
   <cd-table
+    #table
     [data]="daemonStatus"
     [columns]="columns"
+    columnMode="flex"
     selectionType="single"
-    [hasDetails]="false"
-    (setExpandedRow)="setExpandedRow($event)"
-    (fetchData)="loadDaemonStatus($event)"
+    identifier="name"
     (updateSelection)="updateSelection($event)"
-  >
+    (fetchData)="loadDaemonStatus()">
+  <cd-table-actions class="table-actions"
+                    [permission]="permission"
+                    [selection]="selection"
+                    [tableActions]="tableActions">
+  </cd-table-actions>
   </cd-table>
 </ng-container>
index 1b3422b997a8708578deda8655a2deb64ae49c3e..a9d68e7bc2a6af1587f57fdb7a6921147a59aeef 100644 (file)
@@ -10,6 +10,10 @@ import { CdTableFetchDataContext } from '~/app/shared/models/cd-table-fetch-data
 import { CdTableAction } from '~/app/shared/models/cd-table-action';
 import { URLBuilderService } from '~/app/shared/services/url-builder.service';
 import { Daemon, MirroringRow } from '~/app/shared/models/cephfs.model';
+import { Icons } from '~/app/shared/enum/icons.enum';
+import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
+import { CdForm } from '~/app/shared/forms/cd-form';
+import { Permission } from '~/app/shared/models/permissions';
 
 export const MIRRORING_PATH = 'cephfs/mirroring';
 @Component({
@@ -18,7 +22,7 @@ export const MIRRORING_PATH = 'cephfs/mirroring';
   styleUrls: ['./cephfs-mirroring-list.component.scss'],
   providers: [{ provide: URLBuilderService, useValue: new URLBuilderService(MIRRORING_PATH) }]
 })
-export class CephfsMirroringListComponent implements OnInit {
+export class CephfsMirroringListComponent extends CdForm implements OnInit {
   @ViewChild('table', { static: true }) table: TableComponent;
 
   columns: CdTableColumn[];
@@ -27,8 +31,17 @@ export class CephfsMirroringListComponent implements OnInit {
   daemonStatus$: Observable<MirroringRow[]>;
   context: CdTableFetchDataContext;
   tableActions: CdTableAction[];
+  permission: Permission;
 
-  constructor(public actionLabels: ActionLabelsI18n, private cephfsService: CephfsService) {}
+  constructor(
+    public actionLabels: ActionLabelsI18n,
+    private authStorageService: AuthStorageService,
+    private cephfsService: CephfsService,
+    private urlBuilder: URLBuilderService
+  ) {
+    super();
+    this.permission = this.authStorageService.getPermissions().cephfs;
+  }
 
   ngOnInit() {
     this.columns = [
@@ -42,6 +55,15 @@ export class CephfsMirroringListComponent implements OnInit {
       { name: $localize`Snapshot directories`, prop: 'directory_count', flexGrow: 1 }
     ];
 
+    const createAction: CdTableAction = {
+      permission: 'create',
+      icon: Icons.add,
+      routerLink: () => this.urlBuilder.getCreate(),
+      name: this.actionLabels.CREATE,
+      canBePrimary: (selection: CdTableSelection) => !selection.hasSelection
+    };
+
+    this.tableActions = [createAction];
     this.daemonStatus$ = this.subject$.pipe(
       switchMap(() =>
         this.cephfsService.listDaemonStatus()?.pipe(
@@ -72,7 +94,6 @@ export class CephfsMirroringListComponent implements OnInit {
                 }
               });
             });
-
             return of(result);
           }),
           catchError(() => {
index afe99c867cd4112e777814db99fdc4d13d090954..f999d605a0de0ec63a17c11ce84dd240e0316585 100644 (file)
@@ -49,13 +49,16 @@ import {
   SelectModule,
   TimePickerModule,
   TreeviewModule,
-  TabsModule
+  TabsModule,
+  TilesModule,
+  RadioModule
 } from 'carbon-components-angular';
 
 import AddIcon from '@carbon/icons/es/add/32';
 import LaunchIcon from '@carbon/icons/es/launch/32';
 import Close from '@carbon/icons/es/close/32';
 import Trash from '@carbon/icons/es/trash-can/32';
+import { CephfsMirroringWizardComponent } from '../cephfs-mirroring/cephfs-mirroring-wizard/cephfs-mirroring-wizard.component';
 
 @NgModule({
   imports: [
@@ -87,7 +90,9 @@ import Trash from '@carbon/icons/es/trash-can/32';
     ComboBoxModule,
     IconModule,
     BaseChartDirective,
-    TabsModule
+    TabsModule,
+    TilesModule,
+    RadioModule
   ],
   declarations: [
     CephfsDetailComponent,
@@ -108,7 +113,8 @@ import Trash from '@carbon/icons/es/trash-can/32';
     CephfsSubvolumeSnapshotsFormComponent,
     CephfsMountDetailsComponent,
     CephfsAuthModalComponent,
-    CephfsMirroringListComponent
+    CephfsMirroringListComponent,
+    CephfsMirroringWizardComponent
   ],
   providers: [provideCharts(withDefaultRegisterables())]
 })