]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Allow the user to re-use existing r 62906/head
authorAashish Sharma <Aashish.Sharma1@ibm.com>
Wed, 16 Apr 2025 11:19:23 +0000 (16:49 +0530)
committerAashish Sharma <Aashish.Sharma1@ibm.com>
Wed, 21 May 2025 06:39:11 +0000 (12:09 +0530)
ealm/zg/zone and setup replication

1. Currently, we just allow the user to create a new realm/zg/zone and setup replication using the multi-site replication wizard. The ask is to allow the user to select the pre-existing realm/zg/zone and setup replication via automatic export and import of token as well.

2. Enable rgw module automatically in the selected cluster if its not
   enabled

Fixes: https://tracker.ceph.com/issues/70276
Signed-off-by: Aashish Sharma <aasharma@redhat.com>
src/pybind/mgr/dashboard/controllers/rgw.py
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-wizard/multisite-wizard-steps.enum.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-wizard/rgw-multisite-wizard.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-wizard/rgw-multisite-wizard.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-multisite.service.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/models/multi-cluster.ts
src/pybind/mgr/dashboard/services/rgw_client.py
src/pybind/mgr/dashboard/services/service.py

index 10e6d0dbbc678d39411cb01739b625d66b0d035c..757a8b89fcbaa0da519b9c4cd3d5f538a78901ad 100755 (executable)
@@ -122,14 +122,15 @@ class RgwMultisiteStatus(RESTController):
     def setup_multisite_replication(self, daemon_name=None, realm_name=None, zonegroup_name=None,
                                     zonegroup_endpoints=None, zone_name=None, zone_endpoints=None,
                                     username=None, cluster_fsid=None, replication_zone_name=None,
-                                    cluster_details=None):
+                                    cluster_details=None, selectedRealmName=None):
         multisite_instance = RgwMultisiteAutomation()
         result = multisite_instance.setup_multisite_replication(realm_name, zonegroup_name,
                                                                 zonegroup_endpoints, zone_name,
                                                                 zone_endpoints, username,
                                                                 cluster_fsid,
                                                                 replication_zone_name,
-                                                                cluster_details)
+                                                                cluster_details,
+                                                                selectedRealmName)
         return result
 
     @RESTController.Collection(method='PUT', path='/setup-rgw-credentials')
index 299ffee5b7318338773550a29b8b8e8d366b3aa2..d6437a3fa313f677a6984cabe090f9c61490ef23 100644 (file)
@@ -17,3 +17,9 @@ export const STEP_TITLES_SINGLE_CLUSTER = [
   StepTitles.CreateZone,
   StepTitles.Review
 ];
+
+export const STEP_TITLES_EXISTING_REALM = [
+  StepTitles.CreateRealmAndZonegroup,
+  StepTitles.SelectCluster,
+  StepTitles.Review
+];
index d431cd3aa6caf8eaaef805d3c9a60396ae58f852..3658ebf5451aefa2d883be70ae35c5ce4e811002 100644 (file)
                   later to manually import into a desired cluster to establish replication
                   between the clusters.
               </cd-alert-panel>
-              <div class="form-group row">
+              <div class="form-group row"
+                   *ngIf="showConfigType && isMultiClusterConfigured">
                 <label class="cd-col-form-label required"
-                       for="realmName"
-                       i18n>Realm Name</label>
-                <div class="cd-col-form-input">
-                  <input class="form-control"
-                         type="text"
-                         placeholder="Realm name..."
-                         id="realmName"
-                         name="realmName"
-                         formControlName="realmName">
-                  <cd-help-text>
-                    <span i18n>Enter a unique name for the Realm. The Realm is a logical grouping of all your Zonegroups.</span>
-                  </cd-help-text>
-                  <span class="invalid-feedback"
-                        *ngIf="multisiteSetupForm.showError('realmName', formDir, 'required')"
-                        i18n>This field is required.</span>
-                  <span class="invalid-feedback"
-                        *ngIf="multisiteSetupForm.showError('realmName', formDir, 'uniqueName')"
-                        i18n>The chosen realm name is already in use.</span>
+                       for="configType"
+                       i18n>Realm configuration mode</label>
+                <div class="col-md-auto custom-checkbox form-check-inline  ms-3">
+                  <input class="form-check-input"
+                         formControlName="configType"
+                         id="newRealm"
+                         value="newRealm"
+                         (change)="onConfigTypeChange()"
+                         type="radio">
+                  <label class="custom-check-label"
+                         for="newRealm"
+                         i18n>Create new realm/zonegroup/zone</label>
                 </div>
-              </div>
-              <div class="form-group row">
-                <label class="cd-col-form-label required"
-                       for="zonegroupName"
-                       i18n>Zone Group Name</label>
-                <div class="cd-col-form-input">
-                  <input class="form-control"
-                         type="text"
-                         placeholder="Zone group name..."
-                         id="zonegroupName"
-                         name="zonegroupName"
-                         formControlName="zonegroupName">
-                  <cd-help-text>
-                    <span i18n>Enter a name for the Zonegroup. Zonegroup will help you identify and manage the group of zones.</span>
-                  </cd-help-text>
-                  <span class="invalid-feedback"
-                        *ngIf="multisiteSetupForm.showError('zonegroupName', formDir, 'required')"
-                        i18n>This field is required.</span>
-                  <span class="invalid-feedback"
-                        *ngIf="multisiteSetupForm.showError('zonegroupName', formDir, 'uniqueName')"
-                        i18n>The chosen zone group name is already in use.</span>
+                <div class="col-md-auto custom-checkbox form-check-inline">
+                  <input class="form-check-input"
+                         formControlName="configType"
+                         id="existingRealm"
+                         type="radio"
+                         (change)="onConfigTypeChange()"
+                         value="existingRealm">
+                  <label class="custom-check-label"
+                         for="existingRealm"
+                         i18n>Select existing realm</label>
                 </div>
               </div>
-              <div class="form-group row">
-                <label class="cd-col-form-label required"
-                       for="zonegroup_endpoints"
-                       i18n>Zonegroup Endpoints</label>
+              <div class="form-group row"
+                   *ngIf="multisiteSetupForm.get('configType').value === 'existingRealm' && showConfigType && isMultiClusterConfigured">
+                <label class="cd-col-form-label"
+                       for="selectedRealm"
+                       i18n>Select Realm</label>
                 <div class="cd-col-form-input">
-                  <cd-select-badges id="zonegroup_endpoints"
-                                    [data]="rgwEndpoints.value"
-                                    [options]="rgwEndpoints.options"
-                                    [customBadges]="true">
-                  </cd-select-badges>
-                  <cd-help-text>
-                    <span i18n>Select the endpoints for the Zonegroup. Endpoints are the URLs or IP addresses from which the rgw gateways in that zonegroup can be accessed. You can select multiple endpoints in case you have multiple rgw gateways in a zonegroup</span>
-                  </cd-help-text>
+                  <select class="form-select"
+                          id="selectedRealm"
+                          formControlName="selectedRealm">
+                  <option *ngFor="let realm of realmsInfo"
+                          [value]="realm.realm">
+                        {{ realm.realm }}
+                  </option>
+                  </select>
                 </div>
               </div>
-            </div>
-            <div *ngSwitchCase="1"
-                 class="ms-5">
-              <div class="form-group row">
-                <label class="cd-col-form-label required"
-                       for="zonegroupName"
-                       i18n>Zone Name</label>
-                <div class="cd-col-form-input">
-                  <input class="form-control"
-                         type="text"
-                         placeholder="Zone name..."
-                         id="zoneName"
-                         name="zoneName"
-                         formControlName="zoneName">
-                  <cd-help-text>
-                    <span i18n>Enter a unique name for the Zone. A Zone represents a distinct data center or geographical location within a Zonegroup.</span>
-                  </cd-help-text>
-                  <span class="invalid-feedback"
-                        *ngIf="multisiteSetupForm.showError('zoneName', formDir, 'required')"
-                        i18n>This field is required.</span>
-                  <span class="invalid-feedback"
-                        *ngIf="multisiteSetupForm.showError('zoneName', formDir, 'uniqueName')"
-                        i18n>The chosen zone name is already in use.</span>
+              <div *ngIf="multisiteSetupForm.get('configType').value === 'newRealm' || !showConfigType">
+                <div class="form-group row">
+                  <label class="cd-col-form-label required"
+                         for="realmName"
+                         i18n>Realm Name</label>
+                  <div class="cd-col-form-input">
+                    <input class="form-control"
+                           type="text"
+                           id="realmName"
+                           formControlName="realmName">
+                    <cd-help-text>
+                      <span i18n>Enter a unique name for the Realm. The Realm is a logical grouping of all your Zonegroups.</span>
+                    </cd-help-text>
+                    <span class="invalid-feedback"
+                          *ngIf="multisiteSetupForm.showError('realmName', formDir, 'required')"
+                          i18n>This field is required.</span>
+                    <span class="invalid-feedback"
+                          *ngIf="multisiteSetupForm.showError('realmName', formDir, 'uniqueName')"
+                          i18n>This realm name is already in use. Choose a unique name.</span>
+                  </div>
                 </div>
-              </div>
-              <div class="form-group row">
-                <label class="cd-col-form-label required"
-                       for="zone_endpoints"
-                       i18n>Zone Endpoints</label>
-                <div class="cd-col-form-input">
-                  <cd-select-badges id="zone_endpoints"
-                                    [data]="rgwEndpoints.value"
-                                    [options]="rgwEndpoints.options"
-                                    [customBadges]="true">
-                  </cd-select-badges>
-                  <cd-help-text>
-                    <span i18n>Select the endpoints for the Zone. Endpoints are the URLs or IP addresses from which the rgw gateways in that zone can be accessed. You can select multiple endpoints in case you have multiple rgw gateways in a zone</span>
-                  </cd-help-text>
+                <div class="form-group row">
+                  <label class="cd-col-form-label required"
+                         for="zonegroupName"
+                         i18n>Zone Group Name</label>
+                  <div class="cd-col-form-input">
+                    <input class="form-control"
+                           type="text"
+                           id="zonegroupName"
+                           formControlName="zonegroupName">
+                    <cd-help-text>
+                      <span i18n>Enter a name for the Zonegroup. Zonegroup will help you identify and manage the group of zones.</span>
+                    </cd-help-text>
+                    <span class="invalid-feedback"
+                          *ngIf="multisiteSetupForm.showError('zonegroupName', formDir, 'required')"
+                          i18n>This field is required.</span>
+                    <span class="invalid-feedback"
+                          *ngIf="multisiteSetupForm.showError('zonegroupName', formDir, 'uniqueName')"
+                          i18n>This zonegroup name is already in use. Choose a unique name.</span>
+                  </div>
                 </div>
-              </div>
-              <div class="form-group row">
-                <label class="cd-col-form-label required"
-                       for="username"
-                       i18n>Username</label>
-                <div class="cd-col-form-input">
-                  <input class="form-control"
-                         type="text"
-                         placeholder="Username..."
-                         id="username"
-                         name="username"
-                         formControlName="username"
-                         ngbTooltip="White spaces at the beginning and end will be trimmed"
-                         i18n-ngbTooltip
-                         cdTrim>
-                  <cd-help-text>
-                    <span i18n>Specify the username for the system user.</span>
-                  </cd-help-text>
-                  <cd-alert-panel type="info"
-                                  [showTitle]="false">
-                    <span i18n>This user will be created automatically as part of the process, and it will have the necessary permissions to manage and synchronize resources across zones.</span>
-                  </cd-alert-panel>
-                  <span class="invalid-feedback"
-                        *ngIf="multisiteSetupForm.showError('username', formDir, 'required')"
-                        i18n>This field is required.</span>
-                  <span class="invalid-feedback"
-                        *ngIf="multisiteSetupForm.showError('username', formDir, 'notUnique')"
-                        i18n>The username already exists.</span>
+                <div class="form-group row">
+                  <label class="cd-col-form-label required"
+                         for="zonegroup_endpoints"
+                         i18n>Zonegroup Endpoints</label>
+                  <div class="cd-col-form-input">
+                    <cd-select-badges id="zonegroup_endpoints"
+                                      [data]="rgwEndpoints.value"
+                                      [options]="rgwEndpoints.options"
+                                      [customBadges]="true">
+                    </cd-select-badges>
+                    <cd-help-text>
+                      <span i18n>Select the endpoints for the Zonegroup. Endpoints are the URLs or IP addresses from which the rgw gateways in that zonegroup can be accessed. You can select multiple endpoints in case you have multiple rgw gateways in a zonegroup</span>
+                    </cd-help-text>
+                  </div>
                 </div>
               </div>
             </div>
-            <div cass="ms-5"
-                 *ngSwitchCase="2">
-              <div *ngIf="isMultiClusterConfigured; else nonMultiClusterTemplate">
+            <ng-container *ngSwitchCase="1">
+              <div *ngIf="multisiteSetupForm.get('configType').value === 'newRealm'"
+                   class="ms-5">
                 <div class="form-group row">
                   <label class="cd-col-form-label required"
-                         for="cluster"
-                         i18n>Replication Cluster</label>
+                         for="zonegroupName"
+                         i18n>Zone Name</label>
                   <div class="cd-col-form-input">
-                    <select class="form-select"
-                            id="cluster"
-                            [(ngModel)]="selectedCluster"
-                            formControlName="cluster"
-                            name="cluster">
-                      <option *ngFor="let cluster_detail of clusterDetailsArray"
-                              [value]="cluster_detail.name">
-                        {{ cluster_detail.cluster_alias }} - {{ cluster_detail.name }}
-                      </option>
-                    </select>
+                    <input class="form-control"
+                           type="text"
+                           id="zoneName"
+                           formControlName="zoneName">
                     <cd-help-text>
-                      <span i18n>Choose the cluster where you want to apply this multisite configuration. The selected cluster will integrate the defined Realm, Zonegroup, and Zones, enabling data synchronization and management across the multisite setup.</span>
+                      <span i18n>Enter a unique name for the Zone. A Zone represents a distinct data center or geographical location within a Zonegroup.</span>
                     </cd-help-text>
-                    <cd-alert-panel type="info"
-                                    [showTitle]="false">
-                      <span i18n>Before submitting this form, please verify that the selected cluster has an active RGW (Rados Gateway) service running.</span>
-                    </cd-alert-panel>
+                    <span class="invalid-feedback"
+                          *ngIf="multisiteSetupForm.showError('zoneName', formDir, 'required')"
+                          i18n>This field is required.</span>
+                    <span class="invalid-feedback"
+                          *ngIf="multisiteSetupForm.showError('zoneName', formDir, 'uniqueName')"
+                          i18n>This zone name is already in use. Choose a unique name.</span>
                   </div>
                 </div>
                 <div class="form-group row">
                   <label class="cd-col-form-label required"
-                         for="zonegroupName"
-                         i18n>Replication Zone Name</label>
+                         for="zone_endpoints"
+                         i18n>Zone Endpoints</label>
+                  <div class="cd-col-form-input">
+                    <cd-select-badges id="zone_endpoints"
+                                      [data]="rgwEndpoints.value"
+                                      [options]="rgwEndpoints.options"
+                                      [customBadges]="true"></cd-select-badges>
+                    <cd-help-text>
+                      <span i18n>Select the endpoints for the Zone. Endpoints are the URLs or IP addresses from which the rgw gateways in that zone can be accessed. You can select multiple endpoints in case you have multiple rgw gateways in a zone</span>
+                    </cd-help-text>
+                  </div>
+                </div>
+                <div class="form-group row">
+                  <label class="cd-col-form-label required"
+                         for="username"
+                         i18n>Username</label>
                   <div class="cd-col-form-input">
                     <input class="form-control"
                            type="text"
-                           placeholder="Zone name..."
-                           id="replicationZoneName"
-                           name="replicationZoneName"
-                           formControlName="replicationZoneName">
+                           id="username"
+                           formControlName="username"
+                           ngbTooltip="White spaces at the beginning and end will be trimmed"
+                           i18n-ngbTooltip
+                           cdTrim>
                     <cd-help-text>
-                      <span i18n>Replication zone represents the zone to be created in the replication cluster where your data will be replicated.</span>
+                      <span i18n>Specify the username for the system user.</span>
                     </cd-help-text>
+                    <cd-alert-panel type="info"
+                                    [showTitle]="false">
+                      <span i18n>This user will be created automatically as part of the process, and it will have the necessary permissions to manage and synchronize resources across zones.</span>
+                    </cd-alert-panel>
                     <span class="invalid-feedback"
-                          *ngIf="multisiteSetupForm.showError('replicationZoneName', formDir, 'required')"
+                          *ngIf="multisiteSetupForm.showError('username', formDir, 'required')"
                           i18n>This field is required.</span>
+                    <span class="invalid-feedback"
+                          *ngIf="multisiteSetupForm.showError('username', formDir, 'notUnique')"
+                          i18n>This username is already in use. Choose a unique name.</span>
                   </div>
                 </div>
               </div>
-            </div>
+              <div *ngIf="isMultiClusterConfigured && (multisiteSetupForm.get('configType').value === 'existingRealm')"
+                   class="ms-5">
+                <ng-container *ngTemplateOutlet="replicationTemplate"></ng-container>
+              </div>
+            </ng-container>
+            <ng-container *ngSwitchCase="2">
+              <div *ngIf="multisiteSetupForm.get('configType').value === 'newRealm'"
+                   class="ms-5">
+                <ng-container *ngIf="isMultiClusterConfigured; else newRealmReviewTemplate">
+                  <ng-container *ngTemplateOutlet="replicationTemplate"></ng-container>
+                </ng-container>
+              </div>
+              <div *ngIf="multisiteSetupForm.get('configType').value === 'existingRealm' && isMultiClusterConfigured"
+                   class="ms-5">
+                <ng-container *ngIf="!loading; else loadingTemplate">
+                  <ng-container *ngIf="!setupCompleted; else progressCompleteTemplate">
+                    <ng-container *ngTemplateOutlet="reviewTemplate"></ng-container>
+                  </ng-container>
+                </ng-container>
+              </div>
+            </ng-container>
+            <ng-template #newRealmReviewTemplate>
+              <ng-container *ngTemplateOutlet="reviewTemplate"></ng-container>
+            </ng-template>
+            <ng-template #replicationTemplate>
+              <div class="form-group row">
+                <label class="cd-col-form-label required"
+                       for="cluster"
+                       i18n>Replication Cluster</label>
+                <div class="cd-col-form-input">
+                  <select class="form-select"
+                          id="cluster"
+                          [(ngModel)]="selectedCluster"
+                          formControlName="cluster"
+                          name="cluster">
+                    <option *ngFor="let cluster_detail of clusterDetailsArray"
+                            [value]="cluster_detail.name">
+                      {{ cluster_detail.cluster_alias }} - {{ cluster_detail.name }}
+                    </option>
+                  </select>
+                  <cd-help-text>
+                    <span i18n>Choose the cluster where you want to apply this multisite configuration. The selected cluster will integrate the defined Realm, Zonegroup, and Zones, enabling data synchronization and management across the multisite setup.</span>
+                  </cd-help-text>
+                  <cd-alert-panel type="info"
+                                  [showTitle]="false">
+                    <span i18n>Before submitting this form, please verify that the selected cluster has an active RGW (Rados Gateway) service running.</span>
+                  </cd-alert-panel>
+                </div>
+              </div>
+              <div class="form-group row">
+                <label class="cd-col-form-label required"
+                       for="replicationZoneName"
+                       i18n>Replication Zone Name</label>
+                <div class="cd-col-form-input">
+                  <input class="form-control"
+                         type="text"
+                         id="replicationZoneName"
+                         name="replicationZoneName"
+                         formControlName="replicationZoneName">
+                  <cd-help-text>
+                    <span i18n>Replication zone represents the zone to be created in the replication cluster where your data will be replicated.</span>
+                  </cd-help-text>
+                  <span class="invalid-feedback"
+                        *ngIf="multisiteSetupForm.showError('replicationZoneName', formDir, 'required')"
+                        i18n>This field is required.</span>
+                </div>
+              </div>
+            </ng-template>
             <div *ngSwitchCase="3"
                  class="ms-5">
               <div *ngIf="isMultiClusterConfigured">
             name="skip-cluster-selection"
             aria-label="Skip"
             (click)="onSkip()"
-            *ngIf="stepTitles[currentStep.stepIndex]['label'] === 'Select Cluster'"
+            *ngIf="stepTitles[currentStep.stepIndex]['label'] === 'Select Cluster' && multisiteSetupForm.get('configType').value === 'newRealm'"
             i18n>Skip</button>
     <button cdsButton="secondary"
             (click)="onPreviousStep()"
             [attr.aria-label]="showCancelButtonLabel()"
-            [disabled]="loading"
             i18n>{{ showCancelButtonLabel() }}</button>
     <button cdsButton="primary"
             (click)="onNextStep()"
       <cds-loading [isActive]="loading"
                    [overlay]="false"
                    size="sm"
-                   *ngIf="loading"></cds-loading>
+                   *ngIf="loading">
+      </cds-loading>
     </button>
   </cds-modal-footer>
 </cds-modal>
 
+
 <ng-template #nonMultiClusterTemplate>
   <ng-container *ngIf="!loading; else loadingTemplate">
     <ng-container *ngIf="!setupCompleted else exportTokenTemplate">
 </ng-template>
 
 <ng-template #reviewTemplate>
+  <ng-container [ngSwitch]="multisiteSetupForm.get('configType').value">
+    <ng-container *ngSwitchCase="'newRealm'">
+      <ng-container *ngTemplateOutlet="newRealmInfo"></ng-container>
+      <ng-container *ngTemplateOutlet="replicationInfo"></ng-container>
+    </ng-container>
+    <ng-container *ngSwitchCase="'existingRealm'">
+      <ng-container *ngTemplateOutlet="existingRealmInfo"></ng-container>
+      <ng-container *ngTemplateOutlet="replicationInfo"></ng-container>
+    </ng-container>
+  </ng-container>
+</ng-template>
+
+<ng-template #newRealmInfo>
   <div class="form-group row">
     <label class="cd-col-form-label"
            i18n>Realm Name:</label>
-    <div class="cd-col-form-input mt-2 text-muted"
-         id="realmName">
-      <b>{{ multisiteSetupForm.get('realmName').value }}</b>
-    </div>
+    <div class="cd-col-form-input mt-2 text-muted"><b>{{ multisiteSetupForm.get('realmName').value }}</b></div>
   </div>
   <div class="form-group row">
     <label class="cd-col-form-label"
            i18n>Zonegroup Name:</label>
-    <div class="cd-col-form-input mt-2 text-muted"
-         id="zonegroupName">
-      <b>{{ multisiteSetupForm.get('zonegroupName').value }}</b>
-    </div>
+    <div class="cd-col-form-input mt-2 text-muted"><b>{{ multisiteSetupForm.get('zonegroupName').value }}</b></div>
   </div>
   <div class="form-group row">
     <label class="cd-col-form-label"
            i18n>Zonegroup Endpoints:</label>
-    <div class="cd-col-form-input mt-2 text-muted">
-      <b>{{ rgwEndpoints.value.join(', ') }}</b>
-    </div>
+    <div class="cd-col-form-input mt-2 text-muted"><b>{{ rgwEndpoints.value.join(', ') }}</b></div>
   </div>
   <div class="form-group row">
     <label class="cd-col-form-label"
            i18n>Zone Name:</label>
-    <div class="cd-col-form-input mt-2 text-muted"
-         id="zoneName">
-      <b>{{ multisiteSetupForm.get('zoneName').value }}</b>
-    </div>
+    <div class="cd-col-form-input mt-2 text-muted"><b>{{ multisiteSetupForm.get('zoneName').value }}</b></div>
   </div>
   <div class="form-group row">
     <label class="cd-col-form-label"
            i18n>Zone Endpoints:</label>
-    <div class="cd-col-form-input mt-2 text-muted">
-      <b>{{ rgwEndpoints.value.join(', ') }}</b>
-    </div>
+    <div class="cd-col-form-input mt-2 text-muted"><b>{{ rgwEndpoints.value.join(', ') }}</b></div>
   </div>
   <div class="form-group row">
     <label class="cd-col-form-label"
            i18n>Username:</label>
-    <div class="cd-col-form-input mt-2 text-muted">
-      <b>{{ multisiteSetupForm.get('username').value }}</b>
-    </div>
+    <div class="cd-col-form-input mt-2 text-muted"><b>{{ multisiteSetupForm.get('username').value }}</b></div>
   </div>
+</ng-template>
+
+<ng-template #existingRealmInfo>
+  <div class="form-group row">
+    <label class="cd-col-form-label"
+           i18n>Selected Realm:</label>
+    <div class="cd-col-form-input mt-2 text-muted"><b>{{ multisiteSetupForm.get('selectedRealm').value }}</b></div>
+  </div>
+</ng-template>
+
+<ng-template #replicationInfo>
   <div *ngIf="isMultiClusterConfigured && !stepsToSkip['Select Cluster']">
     <div class="form-group row">
       <label class="cd-col-form-label"
              i18n>Selected Replication Cluster:</label>
-      <div class="cd-col-form-input mt-2 text-muted">
-        <b>{{ selectedCluster }}</b>
-      </div>
+      <div class="cd-col-form-input mt-2 text-muted"><b>{{ selectedCluster }}</b></div>
     </div>
     <div class="form-group row">
       <label class="cd-col-form-label"
              i18n>Replication Zone Name:</label>
-      <div class="cd-col-form-input mt-2 text-muted">
-        <b>{{ multisiteSetupForm.get('replicationZoneName').value }}</b>
-      </div>
+      <div class="cd-col-form-input mt-2 text-muted"><b>{{ multisiteSetupForm.get('replicationZoneName').value }}</b></div>
     </div>
   </div>
 </ng-template>
index 2fbe1163ef841d840a102ee8d1c9b4f9d58d20cc..4872ffb3dbda1770c961874ecff7b7fb94baba97 100644 (file)
@@ -1,8 +1,8 @@
-import { Component, OnInit } from '@angular/core';
+import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
 import { Location } from '@angular/common';
 import { UntypedFormControl, Validators } from '@angular/forms';
 import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
-import { Subscription, forkJoin } from 'rxjs';
+import { Observable, Subscription, forkJoin } from 'rxjs';
 import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
 import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
 import { WizardStepModel } from '~/app/shared/models/wizard-steps';
@@ -23,9 +23,34 @@ import { BaseModal, Step } from 'carbon-components-angular';
 import { SummaryService } from '~/app/shared/services/summary.service';
 import { ExecutingTask } from '~/app/shared/models/executing-task';
 import {
+  STEP_TITLES_EXISTING_REALM,
   STEP_TITLES_MULTI_CLUSTER_CONFIGURED,
   STEP_TITLES_SINGLE_CLUSTER
 } from './multisite-wizard-steps.enum';
+import { RgwRealmService } from '~/app/shared/api/rgw-realm.service';
+import { MultiCluster, MultiClusterConfig } from '~/app/shared/models/multi-cluster';
+
+interface DaemonStats {
+  rgw_metadata?: {
+    [key: string]: string;
+  };
+}
+
+interface EndpointInfo {
+  hostname: string;
+  port: number;
+  frontendConfig: string;
+}
+
+enum Protocol {
+  HTTP = 'http',
+  HTTPS = 'https'
+}
+
+enum ConfigType {
+  NewRealm = 'newRealm',
+  ExistingRealm = 'existingRealm'
+}
 
 @Component({
   selector: 'cd-rgw-multisite-wizard',
@@ -43,7 +68,7 @@ export class RgwMultisiteWizardComponent extends BaseModal implements OnInit {
   stepsToSkip: { [steps: string]: boolean } = {};
   daemons: RgwDaemon[] = [];
   selectedCluster = '';
-  clusterDetailsArray: any;
+  clusterDetailsArray: MultiCluster[] = [];
   isMultiClusterConfigured = false;
   exportTokenForm: CdFormGroup;
   realms: any;
@@ -53,6 +78,9 @@ export class RgwMultisiteWizardComponent extends BaseModal implements OnInit {
   rgwEndpoints: { value: any[]; options: any[]; messages: any };
   executingTask: ExecutingTask;
   setupCompleted = false;
+  showConfigType = false;
+  realmList: string[] = [];
+  realmsInfo: { realm: string; token: string }[];
 
   constructor(
     private wizardStepsService: WizardStepsService,
@@ -61,10 +89,12 @@ export class RgwMultisiteWizardComponent extends BaseModal implements OnInit {
     private rgwDaemonService: RgwDaemonService,
     private multiClusterService: MultiClusterService,
     private rgwMultisiteService: RgwMultisiteService,
+    private rgwRealmService: RgwRealmService,
     public notificationService: NotificationService,
     private route: ActivatedRoute,
     private summaryService: SummaryService,
-    private location: Location
+    private location: Location,
+    private cdr: ChangeDetectorRef
   ) {
     super();
     this.pageURL = 'rgw/multisite/configuration';
@@ -87,63 +117,85 @@ export class RgwMultisiteWizardComponent extends BaseModal implements OnInit {
 
   ngOnInit(): void {
     this.open = this.route.outlet === 'modal';
-    this.rgwDaemonService
-      .list()
-      .pipe(
-        switchMap((daemons) => {
-          this.daemons = daemons;
-          const daemonStatsObservables = daemons.map((daemon) =>
-            this.rgwDaemonService.get(daemon.id).pipe(
-              map((daemonStats) => ({
-                hostname: daemon.server_hostname,
-                port: daemon.port,
-                frontendConfig: daemonStats['rgw_metadata']['frontend_config#0']
-              }))
-            )
-          );
-          return forkJoin(daemonStatsObservables);
-        })
-      )
-      .subscribe((daemonStatsArray) => {
-        this.rgwEndpoints.value = daemonStatsArray.map((daemonStats) => {
-          const protocol = daemonStats.frontendConfig.includes('ssl_port') ? 'https' : 'http';
-          return `${protocol}://${daemonStats.hostname}:${daemonStats.port}`;
-        });
-        const options: SelectOption[] = this.rgwEndpoints.value.map(
-          (endpoint: string) => new SelectOption(false, endpoint, '')
-        );
-        this.rgwEndpoints.options = [...options];
-      });
-
-    this.multiClusterService.getCluster().subscribe((clusters) => {
+    this.loadRGWEndpoints();
+    this.multiClusterService.getCluster().subscribe((clusters: MultiClusterConfig) => {
+      const currentUrl = clusters['current_url'];
       this.clusterDetailsArray = Object.values(clusters['config'])
         .flat()
-        .filter((cluster) => cluster['url'] !== clusters['current_url']);
+        .filter((cluster) => cluster['url'] !== currentUrl);
       this.isMultiClusterConfigured = this.clusterDetailsArray.length > 0;
-      if (!this.isMultiClusterConfigured) {
-        this.stepTitles = STEP_TITLES_SINGLE_CLUSTER.map((title) => ({
-          label: title
-        }));
-        this.stepTitles.forEach((steps, index) => {
-          steps.onClick = () => (this.currentStep.stepIndex = index);
-        });
-      } else {
-        this.selectedCluster = this.clusterDetailsArray[0]['name'];
-      }
+      this.stepTitles = (this.isMultiClusterConfigured
+        ? STEP_TITLES_MULTI_CLUSTER_CONFIGURED
+        : STEP_TITLES_SINGLE_CLUSTER
+      ).map((label, index) => ({
+        label,
+        onClick: () => (this.currentStep.stepIndex = index)
+      }));
       this.wizardStepsService.setTotalSteps(this.stepTitles.length);
+      this.selectedCluster = this.isMultiClusterConfigured
+        ? this.clusterDetailsArray[0]['name']
+        : null;
     });
 
     this.summaryService.subscribe((summary) => {
-      this.executingTask = summary.executing_tasks.filter((tasks) =>
-        tasks.name.includes('progress/Multisite-Setup')
-      )[0];
+      this.executingTask = summary.executing_tasks.find((task) =>
+        task.name.includes('progress/Multisite-Setup')
+      );
+    });
+
+    this.stepTitles.forEach((step) => {
+      this.stepsToSkip[step.label] = false;
     });
 
-    this.stepTitles.forEach((stepTitle) => {
-      this.stepsToSkip[stepTitle.label] = false;
+    this.rgwRealmService.getRealmTokens().subscribe((data: { realm: string; token: string }[]) => {
+      const base64Matcher = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$/;
+      this.realmsInfo = data.filter((realmInfo) => base64Matcher.test(realmInfo.token));
+      this.showConfigType = this.realmsInfo.length > 0;
+      if (this.showConfigType) {
+        this.multisiteSetupForm.get('selectedRealm')?.setValue(this.realmsInfo[0].realm);
+        this.cdr.detectChanges();
+      }
     });
   }
 
+  private loadRGWEndpoints(): void {
+    this.rgwDaemonService
+      .list()
+      .pipe(
+        switchMap((daemons: RgwDaemon[]) => {
+          this.daemons = daemons;
+          return this.fetchDaemonStats(daemons);
+        })
+      )
+      .subscribe((daemonStatsArray: EndpointInfo[]) => {
+        this.populateRGWEndpoints(daemonStatsArray);
+      });
+  }
+
+  private fetchDaemonStats(daemons: RgwDaemon[]): Observable<EndpointInfo[]> {
+    const observables = daemons.map((daemon) =>
+      this.rgwDaemonService.get(daemon.id).pipe(
+        map((daemonStats: DaemonStats) => ({
+          hostname: daemon.server_hostname,
+          port: daemon.port,
+          frontendConfig: daemonStats?.rgw_metadata?.['frontend_config#0'] || ''
+        }))
+      )
+    );
+    return forkJoin(observables);
+  }
+
+  private populateRGWEndpoints(statsArray: EndpointInfo[]): void {
+    this.rgwEndpoints.value = statsArray.map((stats: EndpointInfo) => {
+      const protocol = stats.frontendConfig.includes('ssl_port') ? Protocol.HTTPS : Protocol.HTTP;
+      return `${protocol}://${stats.hostname}:${stats.port}`;
+    });
+    this.rgwEndpoints.options = this.rgwEndpoints.value.map(
+      (endpoint) => new SelectOption(false, endpoint, '')
+    );
+    this.cdr.detectChanges();
+  }
+
   createForm() {
     this.multisiteSetupForm = new CdFormGroup({
       realmName: new UntypedFormControl('default_realm', {
@@ -167,7 +219,9 @@ export class RgwMultisiteWizardComponent extends BaseModal implements OnInit {
       }),
       replicationZoneName: new UntypedFormControl('new_replicated_zone', {
         validators: [Validators.required]
-      })
+      }),
+      configType: new UntypedFormControl(ConfigType.NewRealm, {}),
+      selectedRealm: new UntypedFormControl(null, {})
     });
 
     if (!this.isMultiClusterConfigured) {
@@ -244,6 +298,10 @@ export class RgwMultisiteWizardComponent extends BaseModal implements OnInit {
     } else {
       const cluster = values['cluster'];
       const replicationZoneName = values['replicationZoneName'];
+      let selectedRealmName = '';
+      if (this.multisiteSetupForm.get('configType').value === ConfigType.ExistingRealm) {
+        selectedRealmName = this.multisiteSetupForm.get('selectedRealm').value;
+      }
       this.rgwMultisiteService
         .setUpMultisiteReplication(
           realmName,
@@ -254,7 +312,8 @@ export class RgwMultisiteWizardComponent extends BaseModal implements OnInit {
           username,
           cluster,
           replicationZoneName,
-          this.clusterDetailsArray
+          this.clusterDetailsArray,
+          selectedRealmName
         )
         .subscribe(
           () => {
@@ -294,4 +353,25 @@ export class RgwMultisiteWizardComponent extends BaseModal implements OnInit {
   closeModal(): void {
     this.location.back();
   }
+
+  onConfigTypeChange() {
+    const configType = this.multisiteSetupForm.get('configType')?.value;
+    if (configType === ConfigType.ExistingRealm) {
+      this.stepTitles = STEP_TITLES_EXISTING_REALM.map((title) => ({
+        label: title
+      }));
+      this.stepTitles.forEach((steps, index) => {
+        steps.onClick = () => (this.currentStep.stepIndex = index);
+      });
+    } else if (this.isMultiClusterConfigured) {
+      this.stepTitles = STEP_TITLES_MULTI_CLUSTER_CONFIGURED.map((title) => ({
+        label: title
+      }));
+    } else {
+      this.stepTitles = STEP_TITLES_SINGLE_CLUSTER.map((title) => ({
+        label: title
+      }));
+    }
+    this.wizardStepsService.setTotalSteps(this.stepTitles.length);
+  }
 }
index 8a39dc8a284fa2fa85853e756042ad3b648a5031..4063c619e88222d7b985e0ab8598bcadd59ee093 100644 (file)
@@ -86,7 +86,8 @@ export class RgwMultisiteService {
     username: string,
     cluster?: string,
     replicationZoneName?: string,
-    clusterDetailsArray?: any
+    clusterDetailsArray?: any,
+    selectedRealmName?: string
   ) {
     let params = new HttpParams()
       .set('realm_name', realmName)
@@ -108,6 +109,10 @@ export class RgwMultisiteService {
       params = params.set('replication_zone_name', replicationZoneName);
     }
 
+    if (selectedRealmName) {
+      params = params.set('selectedRealmName', selectedRealmName);
+    }
+
     return this.http.post(`${this.uiUrl}/multisite-replications`, null, { params: params });
   }
 
index e41bb12e16d4cd5c2c7988ec441343d081f75939..5661c0d3ebee757aaac55772a2b9104d36602052 100644 (file)
@@ -9,3 +9,12 @@ export interface MultiCluster {
   ssl_certificate: string;
   ttl: number;
 }
+
+export interface MultiClusterConfig {
+  current_url: string;
+  current_user: string;
+  hub_url: string;
+  config: {
+    [clusterId: string]: MultiCluster[];
+  };
+}
index 13c03ec9c95a21ab4eba242b48d6670ba9d7c102..cefad2b045bb7225a5cfa48f55ffbff1136d35dd 100755 (executable)
@@ -1217,196 +1217,221 @@ class RgwMultisiteAutomation:
                                     zone_endpoints: str, username: str,
                                     cluster_fsid: Optional[str] = None,
                                     replication_zone_name: Optional[str] = None,
-                                    cluster_details: Optional[str] = None):
+                                    cluster_details: Optional[str] = None,
+                                    selectedRealmName: Optional[str] = None):
 
-        # Set up multisite replication for Ceph RGW.
         logger.info("Starting multisite replication setup")
-        if cluster_details:
-            cluster_details_dict = json.loads(cluster_details)
+
+        cluster_details_dict = json.loads(cluster_details) if cluster_details else {}
         orch = OrchClient.instance()
-        rgw_multisite_instance = RgwMultisite()
 
         if cluster_fsid:
             self.progress_total = 4
 
-        def get_updated_endpoints(endpoints):
-            # Update endpoint URLs by replacing hostnames with IP addresses.
-            try:
-                hostname_to_ip = {host['hostname']: host['addr'] for host in (h.to_json() for h in orch.hosts.list())}  # noqa E501  # pylint: disable=line-too-long
-                updated_endpoints = [self.replace_hostname(endpoint, hostname_to_ip) for endpoint in endpoints.split(',')]  # noqa E501  # pylint: disable=line-too-long
-                logger.debug("Updated endpoints: %s", updated_endpoints)
-                return updated_endpoints
-            except Exception as e:
-                logger.error("Failed to update endpoints: %s", e)
-                raise
-
-        zonegroup_ip_url = ','.join(get_updated_endpoints(zonegroup_endpoints))
-        zone_ip_url = ','.join(get_updated_endpoints(zone_endpoints))
-        try:
-            # Create the realm and zonegroup
-            self.update_progress(
-                f"Creating realm: {realm_name}, zonegroup: {zonegroup_name} and zone: {zone_name}")
-            logger.info("Creating realm: %s", realm_name)
-            rgw_multisite_instance.create_realm(realm_name=realm_name, default=True)
-            logger.info("Creating zonegroup: %s", zonegroup_name)
-            rgw_multisite_instance.create_zonegroup(realm_name=realm_name,
-                                                    zonegroup_name=zonegroup_name,
-                                                    default=True, master=True,
-                                                    endpoints=zonegroup_ip_url)
+        if selectedRealmName:
+            self.progress_total = 2
+
+        zonegroup_ip_url = ','.join(self.get_updated_endpoints(zonegroup_endpoints, orch))
+        zone_ip_url = ','.join(self.get_updated_endpoints(zone_endpoints, orch))
+
+        if not selectedRealmName:
+            self.create_realm_and_zonegroup(
+                realm_name, zonegroup_name, zone_name, zonegroup_ip_url)
+            self.create_zone_and_user(zone_name, zonegroup_name, username, zone_ip_url)
+            self.restart_daemons()
+
+        return self.export_and_import_realm(
+            realm_name, zonegroup_name, cluster_fsid, replication_zone_name,
+            cluster_details_dict, selectedRealmName, username
+        )
+
+    def get_updated_endpoints(self, endpoints: str, orch: OrchClient) -> list[str]:
+        try:
+            hostname_to_ip = {host['hostname']: host['addr'] for host in
+                              (h.to_json() for h in orch.hosts.list())}
+            return [self.replace_hostname(endpoint, hostname_to_ip) for endpoint
+                    in endpoints.split(',')]
+        except Exception as e:
+            logger.error("Failed to update endpoints: %s", e)
+            raise
+
+    def create_realm_and_zonegroup(self, realm: str, zg: str, zone: str, zg_url: str):
+        try:
+            rgw_multisite_instance = RgwMultisite()
+            self.update_progress(f"Creating realm: {realm}, zonegroup: {zg} and zone: {zone}")
+            rgw_multisite_instance.create_realm(realm_name=realm, default=True)
+            rgw_multisite_instance.create_zonegroup(realm_name=realm, zonegroup_name=zg,
+                                                    default=True, master=True, endpoints=zg_url)
         except Exception as e:
             logger.error("Failed to create realm or zonegroup: %s", e)
             self.update_progress("Failed to create realm or zonegroup", 'fail', str(e))
             raise
+
+    def create_zone_and_user(self, zone: str, zg: str, username: str, zone_url: str):
         try:
-            # Create the zone and system user, then modify the zone with user credentials
-            logger.info("Creating zone: %s", zone_name)
-            if rgw_multisite_instance.create_zone(zone_name=zone_name,
-                                                  zonegroup_name=zonegroup_name,
-                                                  default=True, master=True,
-                                                  endpoints=zone_ip_url,
-                                                  access_key=None,
-                                                  secret_key=None):
+            rgw_multisite_instance = RgwMultisite()
+            if rgw_multisite_instance.create_zone(zone_name=zone, zonegroup_name=zg, default=True,
+                                                  master=True, endpoints=zone_url,
+                                                  access_key=None, secret_key=None):
                 self.progress_done += 1
-                logger.info("Creating system user: %s", username)
-                user_details = rgw_multisite_instance.create_system_user(username, zone_name)
-                if user_details:
-                    keys = user_details['keys'][0]
-                    access_key = keys['access_key']
-                    secret_key = keys['secret_key']
-                    if access_key and secret_key:
-                        rgw_multisite_instance.modify_zone(zone_name=zone_name,
-                                                           zonegroup_name=zonegroup_name,
-                                                           default='true', master='true',
-                                                           endpoints=zone_ip_url,
-                                                           access_key=keys['access_key'],
-                                                           secret_key=keys['secret_key'])
-                    else:
-                        raise ValueError("Access key or secret key is missing")
+                user_details = rgw_multisite_instance.create_system_user(username, zone)
+                keys = user_details.get('keys', [{}])[0]
+                access_key = keys.get('access_key')
+                secret_key = keys.get('secret_key')
+                if access_key and secret_key:
+                    rgw_multisite_instance.modify_zone(
+                        zone_name=zone, zonegroup_name=zg, default='true',
+                        master='true', endpoints=zone_url, access_key=access_key,
+                        secret_key=secret_key)
+                else:
+                    raise DashboardException("Access key or secret key is missing",
+                                             component='rgw', http_status_code=500)
         except Exception as e:
             logger.error("Failed to create zone or system user: %s", e)
-            self.update_progress("Failed to create zone or system user:", 'fail', str(e))
+            self.update_progress("Failed to create zone or system user", 'fail', str(e))
             raise
+
+    def restart_daemons(self):
         try:
-            logger.info("Restarting RGW daemons and setting credentials")
             self.update_progress("Restarting RGW daemons and setting credentials")
-            rgw_service_manager = RgwServiceManager()
-            rgw_service_manager.restart_rgw_daemons_and_set_credentials()
+            RgwServiceManager().restart_rgw_daemons_and_set_credentials()
             self.progress_done += 1
         except Exception as e:
             logger.error("Failed to restart RGW daemon: %s", e)
-            self.update_progress("Failed to restart RGW daemons:", 'fail', str(e))
+            self.update_progress("Failed to restart RGW daemons", 'fail', str(e))
             raise
+
+    def export_and_import_realm(self, realm: str, zg: str,
+                                fsid: Optional[str], rep_zone: Optional[str],
+                                details_dict: dict, selectedRealm: Optional[str],
+                                username: str):
         try:
-            # Get realm tokens and import to another cluster if specified
-            logger.info("Getting realm tokens")
             realm_token_info = CephService.get_realm_tokens()
-            logger.info("Realm tokens: %s", realm_token_info)
-
-            if cluster_fsid and realm_token_info and replication_zone_name and cluster_details_dict:
-                logger.info("Importing realm token to cluster: %s", cluster_fsid)
-                self.update_progress(f"Importing realm token to cluster: {cluster_fsid}")
-                self.import_realm_token_to_cluster(cluster_fsid, realm_name,
-                                                   zonegroup_name, realm_token_info,
-                                                   username, replication_zone_name,
-                                                   cluster_details_dict)
+            if fsid and realm_token_info and rep_zone and details_dict:
+                self.update_progress(f"Importing realm token to cluster: {fsid}")
+                self.import_realm_token_to_cluster(fsid, realm, zg, realm_token_info, username,
+                                                   rep_zone, details_dict, selectedRealm)
             else:
                 self.update_progress("Realm Export Token fetched successfully", 'complete')
+            logger.info("Multisite replication setup completed")
+            return realm_token_info
         except Exception as e:
             logger.error("Failed to get realm tokens or import to cluster: %s", e)
-            self.update_progress("Failed to get realm tokens or import to cluster:", 'fail', str(e))
+            self.update_progress("Failed to get realm tokens or import to cluster",
+                                 'fail', str(e))
             raise
-        logger.info("Multisite replication setup completed")
-        return realm_token_info
 
     def import_realm_token_to_cluster(self, cluster_fsid, realm_name, zonegroup_name,
                                       realm_token_info, username, replication_zone_name,
-                                      cluster_details):
+                                      cluster_details, selectedRealmName):
         try:
-            for realm_token in realm_token_info:
-                if realm_token['realm'] == realm_name:
-                    realm_export_token = realm_token['token']
-                    break
-            else:
-                raise ValueError(f"Realm {realm_name} not found in realm tokens")
-            for cluster in cluster_details:
-                if cluster['name'] == cluster_fsid:
-                    cluster_token = cluster['token']
-                    cluster_url = cluster['url']
-                    break
-            if cluster_token:
-                if not cluster_url.endswith('/'):
-                    cluster_url += '/'
+            if selectedRealmName:
+                rgw_service_manager = RgwServiceManager()
+                username = rgw_service_manager.get_username_from_realm_name(selectedRealmName)
+                realm_name = selectedRealmName
+
+            realm_export_token = self._get_realm_export_token(realm_token_info, realm_name)
+            cluster_url, cluster_token = self._get_cluster_details(cluster_fsid, cluster_details)
+
+            self._configure_selected_cluster(cluster_url, cluster_token, realm_name,
+                                             zonegroup_name, replication_zone_name)
+
+            token_import_response = self._import_realm_token(
+                cluster_url, cluster_token, realm_export_token,
+                replication_zone_name)
+
+            self.progress_done += 1
+            self.update_progress(f"Checking for user {username} in the selected cluster \
+                                 and setting credentials")
+
+            self._verify_user_and_daemons(cluster_url, cluster_token, realm_name,
+                                          replication_zone_name, username)
+
+            return token_import_response
 
-                path = 'api/rgw/realm/import_realm_token'
-                try:
-                    multi_cluster_instance = MultiCluster()
-                    daemon_name = f"{realm_name}.{replication_zone_name}"
-                    # pylint: disable=protected-access
-                    config_payload = {
-                        'realm_name': realm_name,
-                        'zonegroup_name': zonegroup_name,
-                        'zone_name': replication_zone_name,
-                        'daemon_name': daemon_name,
-                    }
-                    config_info = multi_cluster_instance._proxy(method='PUT', base_url=cluster_url,
-                                                                path='api/rgw/daemon/set_multisite_config',  # noqa E501  # pylint: disable=line-too-long
-                                                                payload=config_payload,
-                                                                token=cluster_token)
-                    logger.info("setting config response: %s", config_info)
-                    available_port = multi_cluster_instance._proxy(method='GET',
-                                                                   base_url=cluster_url,
-                                                                   path='ui-api/rgw/multisite/available-ports',  # noqa E501  # pylint: disable=line-too-long
-                                                                   token=cluster_token)
-                    placement_spec: Dict[str, Dict] = {"placement": {}}
-                    payload = {
-                        'realm_token': realm_export_token,
-                        'zone_name': replication_zone_name,
-                        'port': available_port,
-                        'placement_spec': placement_spec,
-                    }
-                    token_import_response = multi_cluster_instance._proxy(method='POST',
-                                                                          base_url=cluster_url,
-                                                                          path=path,
-                                                                          payload=payload,
-                                                                          token=cluster_token)
-                    logger.info("Import realm token response: %s", token_import_response)
-                    self.progress_done += 1
-                    self.update_progress(f"Checking for user {username} in the selected cluster and setting credentials")  # noqa E501  # pylint: disable=line-too-long
-                    service_name = f"rgw.{daemon_name}"
-                    daemons_status = multi_cluster_instance._proxy(method='GET',
-                                                                   base_url=cluster_url,
-                                                                   path=f'ui-api/rgw/multisite/check-daemons-status?service_name={service_name}',  # noqa E501  # pylint: disable=line-too-long
-                                                                   token=cluster_token)
-                    logger.info("Daemons status: %s", daemons_status)
-                    realms_list = multi_cluster_instance._proxy(
-                        method='GET',
-                        base_url=cluster_url,
-                        path='api/rgw/realm',
-                        token=cluster_token
-                    )
-                    logger.debug("Realms info in the selected cluster: %s", realms_list)
-                    system_user_param = "realmName" if realms_list.get('default_info') \
-                        else "zoneName"
-                    if daemons_status is True:
-                        self.check_user_in_second_cluster(cluster_url, cluster_token,
-                                                          username, replication_zone_name,
-                                                          system_user_param, realm_name)
-                    else:
-                        self.update_progress("Failed to set credentials in selected cluster", 'fail', "RGW daemons failed to start")  # noqa E501  # pylint: disable=line-too-long
-                        return token_import_response
-                except requests.RequestException as e:
-                    logger.error("Could not reach %s: %s", cluster_url, e)
-                    raise DashboardException(f"Could not reach {cluster_url}: {e}",
-                                             http_status_code=404, component='dashboard')
-                except json.JSONDecodeError as e:
-                    logger.error("Error parsing Dashboard API response: %s", e.msg)
-                    raise DashboardException(f"Error parsing Dashboard API response: {e.msg}",
-                                             component='dashboard')
         except Exception as e:
             logger.error("Failed to import realm token to cluster: %s", e)
             self.update_progress("Failed to import realm token to cluster:", 'fail', str(e))
             raise
 
+    def _get_realm_export_token(self, realm_token_info, realm_name):
+        for realm_token in realm_token_info:
+            if realm_token['realm'] == realm_name:
+                return realm_token['token']
+        raise DashboardException('Realm token not found',
+                                 http_status_code=500, component='rgw')
+
+    def _get_cluster_details(self, cluster_fsid, cluster_details):
+        for cluster in cluster_details:
+            if cluster['name'] == cluster_fsid:
+                cluster_url = cluster['url'].rstrip('/') + '/'
+                return cluster_url, cluster['token']
+        raise DashboardException("Cluster details not found",
+                                 http_status_code=500, component='rgw')
+
+    def _configure_selected_cluster(self, cluster_url, cluster_token, realm_name,
+                                    zonegroup_name, replication_zone_name):
+        daemon_name = f"{realm_name}.{replication_zone_name}"
+        config_payload = {
+            'realm_name': realm_name,
+            'zonegroup_name': zonegroup_name,
+            'zone_name': replication_zone_name,
+            'daemon_name': daemon_name,
+        }
+        multi_cluster_instance = MultiCluster()
+        # pylint: disable=protected-access
+        config_info = multi_cluster_instance._proxy(method='PUT', base_url=cluster_url,
+                                                    path='api/rgw/daemon/set_multisite_config',
+                                                    payload=config_payload,
+                                                    token=cluster_token)
+        logger.info("setting config response: %s", config_info)
+
+    def _import_realm_token(self, cluster_url, cluster_token, realm_token, zone_name):
+        multi_cluster_instance = MultiCluster()
+        # pylint: disable=protected-access
+        available_port = multi_cluster_instance._proxy(
+            method='GET', base_url=cluster_url, path='ui-api/rgw/multisite/available-ports',
+            token=cluster_token)
+        payload = {
+            'realm_token': realm_token,
+            'zone_name': zone_name,
+            'port': available_port,
+            'placement_spec': {"placement": {}}
+        }
+        # pylint: disable=protected-access
+        token_import_response = multi_cluster_instance._proxy(
+            method='POST', base_url=cluster_url, path='api/rgw/realm/import_realm_token',
+            payload=payload, token=cluster_token)
+        logger.info("Import realm token response: %s", token_import_response)
+        return token_import_response
+
+    def _verify_user_and_daemons(self, cluster_url, cluster_token, realm_name, zone_name,
+                                 username):
+        multi_cluster_instance = MultiCluster()
+        daemon_name = f"{realm_name}.{zone_name}"
+        service_name = f"rgw.{daemon_name}"
+        # pylint: disable=protected-access
+        daemons_status = multi_cluster_instance._proxy(
+            method='GET', base_url=cluster_url,
+            path=f'ui-api/rgw/multisite/check-daemons-status?service_name={service_name}',
+            token=cluster_token)
+        logger.debug("Daemons status: %s", daemons_status)
+        # pylint: disable=protected-access
+        realms_list = multi_cluster_instance._proxy(method='GET',
+                                                    base_url=cluster_url,
+                                                    path='api/rgw/realm',
+                                                    token=cluster_token)
+        logger.debug("Realms info in the selected cluster: %s", realms_list)
+
+        system_user_param = "realmName" if realms_list.get('default_info') else "zoneName"
+
+        if daemons_status is True:
+            self.check_user_in_second_cluster(cluster_url, cluster_token, username, zone_name,
+                                              system_user_param, realm_name)
+        else:
+            self.update_progress("Failed to set credentials in selected cluster", 'fail',
+                                 "RGW daemons failed to start")
+
     def check_user_in_second_cluster(self, cluster_url, cluster_token, username,
                                      replication_zone_name, system_user_param,
                                      realm_name):
@@ -1438,10 +1463,12 @@ class RgwMultisiteAutomation:
                     user_found = True
                     logger.info("User %s found in the second cluster", username)
                     # pylint: disable=protected-access
-                    set_creds_cont = multi_cluster_instance._proxy(method='PUT', base_url=cluster_url,  # noqa E501  # pylint: disable=line-too-long
-                                                                   path='ui-api/rgw/multisite/setup-rgw-credentials',  # noqa E501  # pylint: disable=line-too-long
-                                                                   token=cluster_token)  # noqa E501  # pylint: disable=line-too-long
-                    logger.info("set credentials in selected cluster response: %s", set_creds_cont)  # noqa E501  # pylint: disable=line-too-long  # noqa E501  # pylint: disable=line-too-long
+                    set_creds_cont = multi_cluster_instance._proxy(
+                        method='PUT', base_url=cluster_url,
+                        path='ui-api/rgw/multisite/setup-rgw-credentials',
+                        token=cluster_token)
+                    logger.info("set credentials in selected cluster response: %s",
+                                set_creds_cont)
                     self.progress_done += 1
                     self.update_progress("Multisite replication setup completed",
                                          'complete')
index 057b6f03ef7c62ef45db7acefec77e721afba786..12285b061c0b9962f88f7113e7d1c70fad77f478 100644 (file)
@@ -221,3 +221,48 @@ class RgwServiceManager:
         rgw_hostname_setting = Settings.RGW_HOSTNAME_PER_DAEMON
         rgw_hostname_setting.pop(daemon_name, None)
         Settings.RGW_HOSTNAME_PER_DAEMON = rgw_hostname_setting
+
+    def get_username_from_realm_name(self, realm_name: str) -> str:
+        realm_period_info = {}
+        master_zone_info = {}
+        rgw_realm_period_cmd = ['period', 'get', '--rgw-realm', realm_name]
+        try:
+            exit_code, out, _ = mgr.send_rgwadmin_command(rgw_realm_period_cmd)
+            if exit_code > 0:
+                raise DashboardException('Unable to get realm period info',
+                                         http_status_code=500, component='rgw')
+            realm_period_info = out
+        except SubprocessError as error:
+            raise DashboardException(error, http_status_code=500, component='rgw')
+
+        if realm_period_info:
+            master_zone_id = realm_period_info.get('master_zone')
+            rgw_zone_info_cmd = ['zone', 'get', '--zone-id', master_zone_id]
+            try:
+                exit_code, out, _ = mgr.send_rgwadmin_command(rgw_zone_info_cmd)
+                if exit_code > 0:
+                    raise DashboardException('Unable to get master zone info',
+                                             http_status_code=500, component='rgw')
+                master_zone_info = out
+            except SubprocessError as error:
+                raise DashboardException(error, http_status_code=500, component='rgw')
+
+        if master_zone_info:
+            access_key = master_zone_info['system_key']['access_key']
+            user_info = {}
+            rgw_user_info_cmd = ['user', 'info', '--access-key', access_key]
+            try:
+                exit_code, out, _ = mgr.send_rgwadmin_command(rgw_user_info_cmd)
+                if exit_code > 0:
+                    raise DashboardException('Unable to get user info',
+                                             http_status_code=500, component='rgw')
+                user_info = out
+            except SubprocessError as error:
+                raise DashboardException(error, http_status_code=500, component='rgw')
+            if user_info:
+                user_id = user_info.get('user_id')
+                if user_id:
+                    return user_id
+
+        raise DashboardException('Failed to retrieve user_id for realm',
+                                 http_status_code=500, component='rgw')